处理ASProtect的Advanced Import Protect的一些想法和过程(1)
7C8099C0 > 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
7C8099C6 8B40 20 MOV EAX,DWORD PTR DS:[EAX+20]
7C8099C9 C3 RETN
这个从各个寄存器里看到这是对GetCurrentProcessId函数的调用,不是我想要的。
第二次停下:
7C801D7B > 8BFF MOV EDI,EDI
7C801D7D 55 PUSH EBP
从堆栈和寄存器看到是调用LoadLibraryA得到kernel32的基址,对于正常的程序一般不会执行这个函数的,也不是我想要的。用ALT+F9返回到调用的地方。
第三次停下:
00AFCAD2 8B78 20 MOV EDI,DWORD PTR DS:[EAX+20]
00AFCAD5 03FB ADD EDI,EBX
可以看到这是VM的领空,我在这里从寄存器和堆栈看到GetCommandLineA函数字符串,感觉这个像是正常程序调用的函数,然后单步走。
没几步来到一个循环,通过单步走我发现这个循环的作用:从刚才调用LoadLibraryA得到kernel32的基址,到现在用到这个基址,以及各个函数 的字符串出现,这个循环是通过kernel32的导出表来遍历每个函数的字符串并且和“GetCommandLineA”这个字符串进行比较,一直到相同才会走出循环;
这是比较字符串的代码:
00ACC2E1 F3:A6 REPE CMPS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
ESI指向“GetCommandLineA”字符串,EDI指向kernel32中的其他遍历的字符串,在这句上下条件日志断点SHIFT+F4 条件设置为STRING[EDI]=="GetCommandLineA",然后F9运行停在这里,这时就会比较结果相同了,到此,函数是知道了,但是这个函数是从哪个call 01ba0000处真正对应的呢?正是我们进入VM时的那个call 01ba0000吗?对于这个我感觉应该是吧,如果不放心的话,可以在从call 01ba0000进入到VM后对程序的CODE段下断点,看壳代码会不会偷偷执行完一个函数调用又从另一个call 01ba0000进入VM从而欺骗我。。。
从两个字符串比较相同后再单步跟踪一下,不知道怎么工拐八拐就到得到的GetCommandLineA的地址7C 81 2F BD,但是我始终找不到ret call jmp 直接或者间接到达这个地方,于是进入看一下什么代码:
7C812FBD > A1 F455887C MOV EAX,DWORD PTR DS:[7C8855F4]
7C812FC2 C3 RETN 从这里就能返回到VM或者CODE段,我猜想这么少的代码,被偷取到VM中也不是什么难事啊!于是对7C812FBD下读取硬断,来到这里:
00AE426B 8A01 MOV AL,BYTE PTR DS:[ECX]
00AE426D 41 INC ECX ; kernel32.GetCommandLineA 这里的读取没有看到保存
下面也会读取:
00AE441A 8A06 MOV AL,BYTE PTR DS:[ESI] 此时的ESI和EDI都是kernel32.GetCommandLineA的开始处,
00AE441C 8D7E 01 LEA EDI,DWORD PTR DS:[ESI+1]
00AE441F 8A1F MOV BL,BYTE PTR DS:[EDI]
00AE4421 881C24 MOV BYTE PTR SS:[ESP],BL
这是明显的保存了,只是不在VM中,而是在堆栈中,得到第一个BYTE的数据会与EB,E9比较,在代码中找到一个EB和E9看一下,是JMP的16进制码,再往下走。。。来到这里也是取第一个BYTE:
00AE43FC 8A08 MOV CL,BYTE PTR DS:[EAX]
00AE43FE 80F9 E8 CMP CL,0E8
和E8比较,E8是call的16进制码。。。再走来到:
00AF4E96 66:813F FF25 CMP WORD PTR DS:[EDI],25FF
前两个BYTE和FF25比较,FF25是call [*****]的16进制,到这里算是明白了,都是在比较是不是满足条件的。。。。。
最后算保存了一个DD和一个BYTE:
00AC266B F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]保存在B44340
00AC2672 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
最后总算是比较完了,于是到这:
00AEBE0C A1 B02BB000 MOV EAX,DWORD PTR DS:[B02BB0]
00AEBE11 8B40 2C MOV EAX,DWORD PTR DS:[EAX+2C]
00AEBE14 FFD0 CALL EAX 调用VirtualAlloc申请一块内存,保存从kernel32.GetCommandLineA函数处抽取的代码。。。。
接下来的流程是壳代码(VM中)修改原来的call 01ba0000成为call 【刚才VirtualAlloc】得到的内存,再从VM执行到CODE的call 【】处,从call 【】处走到申请的内存中,这时申请的内存中的很少的数据就是偷的GetCommandLineA的代码,执行完又回到CODE接着往下执行。。。。。。。。。。。其他的函数可能也是这样偷的代码。。。
现在说一下第二个被抽取的函数GetStartupInfoA,这个偷的和第一个不同,这一个偷到第一个call ***********处,在保存到VM中时又用了一点代码变形,把call 这句修改为
push &&&&&&&&(call 执行伴随压栈下一句代码地址,&&&&&&&&就是call这句的下一句) push ********** ret 总之偷的不多。。。。
再面临一个问题:壳会不会偷取call 01ba0000的下几行代码呢?现在假如偷取了,过程会这样,肯定会是先从VM进入系统DLL的领空,对于回来嘛,如果偷取了的话,被偷取的代码会在VM里面,那么会从系统DLL中回到VM中,如果没有偷取的话,会从系统DLL的领空直接回到CODE段执行,为人验证到底是哪种情况,我对CODE段和VM中下内存断点,实施一下看。。。
00AE426B 8A01 MOV AL,BYTE PTR DS:[ECX]
00AE426D 41 INC ECX
在这里时,ECX是保存函数的地址,有点像是对CC断点检查的呢?