一道自写VM 的CTF题(护网杯RE)

IDA来到main函数:

一道自写VM 的CTF题(护网杯RE)_第1张图片

发现调用了FuncTable的+68,+6c,+70的函数,并且初始化了1,2,3,5,6,8等偏移的成员。每个FuncTable中的函数都传递了this指针,那么可以确定这是一个类的调用,然后分析+0x70处:

一道自写VM 的CTF题(护网杯RE)_第2张图片

很明显的虚拟机特征,OD去掉ASLR也很观察出,0x401644就是handler的表,但是这里handler表只是起一个代理作用,真正的vm_handler在类的第一项的函数数组(FuncTable)里面:

一道自写VM 的CTF题(护网杯RE)_第3张图片

handler也很简单,没有花指令等等,直接IDA就能看到伪代码,经过分析一些handler以及初始化部分,IDA创建一个类的结构体:

一道自写VM 的CTF题(护网杯RE)_第4张图片

这样就很轻松的看程序结构啦~~~~,现在handler都已经识别了,主要的就是记录下每个handler的执行顺序以及vm_reg的值还有一些比如立即数的值等等,这里选择注入一个dll来hook掉vm_dispatcher输出Log文件。

              

Hook点就选择的这里,因为这个时候esi刚好保存的是VM_STRUCT的this指针,下一句刚好是vm_disaptcher,handler的索引都可以知道。Hook代码:

_declspec(naked) void HookMain()
{
	__asm
	{
		pushad;
		pushfd;

		push esi;
		push eax;
		call HookLog;

		popfd;
		popad;
		cmp eax, 0x16;
		ja _above;
		mov ecx, 0x0401545;
		jmp ecx;
	_above:
		mov ecx, 0x0401633;
		jmp ecx;
	}
}

其中HookLog就是记录信息的call,传递this和handler索引作为参数。

先来一波 ”0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF“ 的Input。

第一次的LOG得到1200多行的handler记录:

第一部分应该是一个while循环,只放出循环的相同部分,这里就是依次在判断输入的字符串是否为'0' - '9' 或者'A' - 'F',如果不是就会直接进行vm_retn:

vPushImm32                      Imm32 = 47
vEip_Unknown                    reg4 = 0
vPopReg32                       reg3 , vEsp = 47            //
vMovReg32_Input8                reg0=8 , InputByte = 48     //r0 = Input[0] 48'0'
vMovReg32_XorRegHighLow         reg2=0 XOR reg2=0           //r2 = 0
vCheckReg                       reg0=48 Check reg2=0        //check( Input[0] == 0)    
vAddEip_D0                      reg5 = 1
vIncInputAndvEip                
vPushImm32                      Imm32 = 70
vPopReg32                       reg1 , vEsp = 70
vCheckReg                       reg0=48 Check reg1=70       //check( Input[0] > 70'F' )    
vAddEip_Z1                      reg5 = -1
vPushImm32                      Imm32 = 48
vPopReg32                       reg1 , vEsp = 48
vCheckReg                       reg0=48 Check reg1=48       //check( Input[0] < 48'0' )    
vAddEip_F1                      reg5 = 0
vPushImm32                      Imm32 = 57
vPopReg32                       reg1 , vEsp = 57
vCheckReg                       reg0=48 Check reg1=57       //check( Input[0] > 57'9' )    
vAddEip_F1                      reg5 = -1
vMovReg32_XorRegHighLow         reg0=48 XOR reg0=48
vCheckReg                       reg0=0 Check reg0=0
vAddEip_D0                      reg5 = 0
vEip_Unknown                    reg4 = 47

第二部分就是计算工作,从最后一个Input开始倒着来:

//第二部分check
vDecInput_IncvEip                                               //Input --
vMovReg32_Input8                reg0=0 , InputByte = 70         //r0 = 最后一个Input
vPushImm32                      Imm32 = 48
vPopReg32                       reg2 , vEsp = 48
vMovReg32_SubRegHighLow         reg0=70 SUB reg2=48             //r0 = 最后一个Input - 48
vPushImm32                      Imm32 = 10
vPopReg32                       reg2 , vEsp = 10
vCheckReg                       reg0=22 Check reg2=10           //如果是输入是字母'A' - 'F'
vAddEip_F1                      reg5 = 1
vPushImm32                      Imm32 = 7
vPopReg32                       reg2 , vEsp = 7
vMovReg32_SubRegHighLow         reg0=22 SUB reg2=7              //r0 = r0 - 7
vPushImm32                      Imm32 = 16
vPopReg32                       reg2 , vEsp = 16
vMovReg32_MulRegHighLow         reg1=0 MUL reg2=16              //r1 = r1 * 16
vMovReg32_AddRegHighLow         reg1=0 ADD reg0=15              //r1 = r1 + r0
vEip_Unknown                    reg4 = 7
vMovReg32_Input8                reg0=10 , InputByte = 57
vPushImm32                      Imm32 = 48
vPopReg32                       reg2 , vEsp = 48
vMovReg32_SubRegHighLow         reg0=57 SUB reg2=48                 //r0 = input[i] - 48;
vPushImm32                      Imm32 = 10
vPopReg32                       reg2 , vEsp = 10
vCheckReg                       reg0=9 Check reg2=10                //是数字了
vAddEip_F1                      reg5 = -1
vPushImm32                      Imm32 = 16
vPopReg32                       reg2 , vEsp = 16                    
vMovReg32_MulRegHighLow         reg1=16702650 MUL reg2=16           //r1 = r1 * 16
vMovReg32_AddRegHighLow         reg1=267242400 ADD reg0=9           //r1 = r1 + r0
vEip_Unknown                    reg4 = 1
计算的Check部分
vPushImm32                      Imm32 = -1573385282 //A2380BBE              //Push A2380BBE
vPopReg32                       reg2 , vEsp = 2721582014            
vCheckReg                       reg1=4275878552 Check reg2=2721582014       //if(r1 == r2)  这里判断前面计算的是否等于这个,不等于就退出虚拟机

可以很清晰的看到计算的方法和结构,进行6轮运算,每次计算8个输入,最后结果与一个立即数进行一个比较:

伪代码:
r0 = input[i] - 48;
if (r0 > 10)	//是字母
	r0 = r0 - 7;
                //是数字就不减

r1 = r1 * 16;
r1 = r1 + r0;	

//8次运算后
if(r1 == 内存中的立即数)

其实算法这里就是将输入的十六进制转换为对应的数字,比如输入0123ABCD,那么输出就是DCBA3210

知道算法后可以进行多次的Log记录,这里因为一次对比失败后直接会退出,所以,这里我每次只取出正确的一组,记录6次就能够得到全部的正确hash。

	//1			:A2380BBE
	//2			:86CCC775
	//3			:14BCEB64
	//4			:768F2C49
	//5			:7F87C53F
	//6			:3E9D1FAE
	//EAF1D9E3F35C78F794C2F86746BECB41577CCC68EBB0832A

启动参数带上EAF1D9E3F35C78F794C2F86746BECB41577CCC68EBB0832A,最后:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(逆向)