南京邮电大学网络攻防训练平台逆向第四题WxyVM

1、IDA静态分析

总结:

1、1:长度必须是0x18

1、2:v4必须是==1

1、3:循环比较InputBuff[X]==dword_601060[X]

2、分析sub_4005B6函数(VStartVM)

2、1:F5伪代码

总结:

这是一个典型的VM框架

扩展知识(参考加密与解密3):

虚拟机保护技术就是基于x86汇编系统的可执行代码转换为字节码指令系统的代码,以达到保护原有指令不被轻易逆向和篡改,这种指令执行系统和Intel的X86指令系统并不在同一个层次上。比如说80x86汇编指令是在CPU里执行的,而字节码指令系统是通过解释指令来执行的,并且这里谈到的字节码指令执行系统是建立在X86指令系统上的。

   字节码其实就是指令执行系统定义的一套指令和数据组成的一串数据流。Java的JVM、.Net或则其他动态语言的虚拟机都是靠解释字节码来执行的,但它们的字节码之间并不通用,因为每一个系统设计的字节码都是为自己使用的,并不兼容其他系统的。

 

3、编写出解密代码

1、注意的地方

if ( *(&InputBuff + i) != dword_601060[i] ) // 注意这里是比较低位,是char而不是dword

2、将原来加密算法的+-*变成-+/,^不变(因为再异或一次就变回去了)

// WxyVM1.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "Data.h"
//用来逆推解密的
__int64 DecVStartVM(char* InputBuff)
{
	unsigned int VM_Eip; // ST04_4@3 操作码
	__int64 result; // rax@3 偏移量
	signed int i; // [sp+0h] [bp-10h]@1 下一组要解析的内容
	char v3; // [sp+8h] [bp-8h]@3 操作数

	for (i = 0x3A95; i >= 0; i -= 3)            // 注意这里必须写成0x3A95才是最后一组
												// 0:操作码(判断执行不同的处理函数)
												// 1:要对第几个字节运算
												// 2:操作数
	{
		VM_Eip = VM_opcode[(signed __int64)i];
		v3 = VM_opcode[(signed __int64)(i + 2)];
		result = VM_Eip;
		switch (VM_Eip)                           // 查找VM_eip指向的正在解释的字节码对应的处理函数
		{
		case 1u:
			result = VM_opcode[(signed __int64)(i + 1)];
			InputBuff[result] -= v3;
			break;
		case 2u:
			result = VM_opcode[(signed __int64)(i + 1)];
			InputBuff[result] += v3;
			break;
		case 3u:
			result = VM_opcode[(signed __int64)(i + 1)];
			InputBuff[result] ^= v3;
			break;
		case 4u:
			result = VM_opcode[(signed __int64)(i + 1)];
			InputBuff[result] /= v3;
			break;
		case 5u:
			result = VM_opcode[(signed __int64)(i + 1)];
			InputBuff[result] ^= InputBuff[VM_opcode[(signed __int64)(i + 2)]];
			break;
		default:
			continue;
		}
	}
	return result;
}

//用来正向测试加密的
__int64 EncVStartVM(char* InputBuff)
{
	unsigned int VM_Eip; // ST04_4@3 操作码
	__int64 result; // rax@3 偏移量
	signed int i; // [sp+0h] [bp-10h]@1 下一组要解析的内容
	char v3; // [sp+8h] [bp-8h]@3 操作数

	for (i = 0; i <= 0x3A97; i += 3)            // 3个字节为1组,一共需要解析0x1387次
												// 0:操作码(判断执行不同的处理函数)
												// 1:要对第几个字节运算
												// 2:操作数
	{
		VM_Eip = VM_opcode[(signed __int64)i];
		v3 = VM_opcode[(signed __int64)(i + 2)];
		result = VM_Eip;
		switch (VM_Eip)                           // 查找VM_eip指向的正在解释的字节码对应的处理函数
		{
		case 1u:
			result = VM_opcode[(signed __int64)(i + 1)];
			InputBuff[result] += v3;
			break;
		case 2u:
			result = VM_opcode[(signed __int64)(i + 1)];
			InputBuff[result] -= v3;
			break;
		case 3u:
			result = VM_opcode[(signed __int64)(i + 1)];
			InputBuff[result] ^= v3;
			break;
		case 4u:
			result = VM_opcode[(signed __int64)(i + 1)];
			InputBuff[result] *= v3;
			break;
		case 5u:
			result = VM_opcode[(signed __int64)(i + 1)];
			InputBuff[result] ^= InputBuff[VM_opcode[(signed __int64)(i + 2)]];
			break;
		default:
			continue;
		}
	}
	return result;
}
int main()
{
	//正确密码长度是0x18个字节
	char CorrectFlag[0x100] = {
		0xC4,
		0x34,
		0x22,
		0xB1,
		0xD3,
		0x11,
		0x97,
		0x07,
		0xDB,
		0x37,
		0xC4,
		0x06,
		0x1D,
		0xFC,
		0X5B,
		0XED,
		0X98,
		0XDF,
		0X94,
		0XD8,
		0XB3,
		0X84,
		0XCC,
		0X08
	};
	//我用来测试的
	//char EncBuff[0x100] = {
	//	"HelloWorldHelloWorldHell"
	//};
	//printf("原始的字符串:%s\n", EncBuff);
	//EncVStartVM(EncBuff);
	//printf("加密后字符串:%s\n", CorrectFlag);
	DecVStartVM(CorrectFlag);
	printf("解密后字符串:%s\n", CorrectFlag);
    return 0;
}

解密后字符串:nctf{Embr4ce_Vm_j0in_R3}

猜你喜欢

转载自blog.csdn.net/u014738665/article/details/84575693