一、查找OEP
00401000 > B8 002A5400 MOV EAX,cpuz.00542A00 ; EP
00401005 50 PUSH EAX ; F8到这时hr esp,几次F9到OEP
00401006 64:FF35 0000000>PUSH DWORD PTR FS:[0]
--------------------------------------------------------------------------------------------------
0047B9BA E8 7BB60000 CALL cpuz.0048703A ; OEP
0047B9BF ^ E9 16FEFFFF JMP cpuz.0047B7DA
0047B9C4 6A 10 PUSH 10
0047B9C6 68 782F4B00 PUSH cpuz.004B2F78
0047B9CB E8 40070000 CALL cpuz.0047C110
在OEP上设个硬件执行断点,方便下次到达。
二、解开输入表
到OEP后看看输入表的情况:
0049610C 77EF5FF0 GDI32.CreateCompatibleDC
00496110 77EF701A GDI32.CreateCompatibleBitmap
00496114 00000000
00496118 00BE0000 ;这里都是加密过的
0049611C 00BE0007
00496120 00BE000E
记住加密输入表的开始位置00496118,OD中CTR+F2重新载入程序,在数据窗口中转到地址 00496118,设内存写入断点,F9几次,在代码窗口中看到要写入 00BE0000 时开始分析:
00BD1B70 8906 MOV DWORD PTR DS:[ESI],EAX ; 断在这里
00BD1B72 8902 MOV DWORD PTR DS:[EDX],EAX
00BD1B74 83C2 04 ADD EDX,4
00BD1B77 83C6 04 ADD ESI,4
00BD1B7A ^ EB AC JMP SHORT 00BD1B28
00BD1B7C 33C0 XOR EAX,EAX
00BD1B7E 5E POP ESI
00BD1B7F 5F POP EDI
00BD1B80 5B POP EBX
00BD1B81 C9 LEAVE
00BD1B82 C2 1000 RETN 10
--------------------------------------------------------------------------------------------------
看看信息窗口:
EAX=00BE0000
DS:[00496118]=00000000
--------------------------------------------------------------------------------------------------
现在按F8来分析,看看这个00BE0000之类的东西是怎么来的:
00BD1B28 C783 C21A0010 00000000 MOV DWORD PTR DS:[EBX+10001AC2],0
00BD1B32 8B02 MOV EAX,DWORD PTR DS:[EDX]
00BD1B34 85C0 TEST EAX,EAX
00BD1B36 74 44 JE SHORT 00BD1B7C
00BD1B38 52 PUSH EDX
00BD1B39 8983 C21A0010 MOV DWORD PTR DS:[EBX+10001AC2],EAX
00BD1B3F A9 00000080 TEST EAX,80000000
00BD1B44 74 09 JE SHORT 00BD1B4F
00BD1B46 25 FFFFFF7F AND EAX,7FFFFFFF
00BD1B4B 6A 00 PUSH 0
00BD1B4D EB 0E JMP SHORT 00BD1B5D
00BD1B4F 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
00BD1B52 0341 08 ADD EAX,DWORD PTR DS:[ECX+8]
00BD1B55 33C9 XOR ECX,ECX
00BD1B57 66:8B08 MOV CX,WORD PTR DS:[EAX]
00BD1B5A 51 PUSH ECX
00BD1B5B 40 INC EAX
00BD1B5C 40 INC EAX
00BD1B5D 50 PUSH EAX
00BD1B5E FF75 FC PUSH DWORD PTR SS:[EBP-4]
00BD1B61 FF93 4D1F0010 CALL DWORD PTR DS:[EBX+10001F4D] ; 这里是关键,跟进去
00BD1B67 5A POP EDX
00BD1B68 85C0 TEST EAX,EAX
00BD1B6A ^ 0F84 6FFFFFFF JE 00BD1ADF
00BD1B70 8906 MOV DWORD PTR DS:[ESI],EAX ; 断在这里
00BD1B72 8902 MOV DWORD PTR DS:[EDX],EAX
00BD1B74 83C2 04 ADD EDX,4
00BD1B77 83C6 04 ADD ESI,4
00BD1B7A ^ EB AC JMP SHORT 00BD1B28
00BD1B7C 33C0 XOR EAX,EAX
00BD1B7E 5E POP ESI
00BD1B7F 5F POP EDI
00BD1B80 5B POP EBX
00BD1B81 C9 LEAVE
00BD1B82 C2 1000 RETN 10
--------------------------------------------------------------------------------------------------
跟进 00BD1B61 处的那个CALL:
00BD0738 55 PUSH EBP
00BD0739 8BEC MOV EBP,ESP
00BD073B 83C4 FC ADD ESP,-4
00BD073E 53 PUSH EBX
00BD073F 57 PUSH EDI
00BD0740 56 PUSH ESI
00BD0741 E8 00000000 CALL 00BD0746
00BD0746 5B POP EBX
00BD0747 81EB FE10A800 SUB EBX,0A810FE
00BD074D FF75 10 PUSH DWORD PTR SS:[EBP+10]
00BD0750 FF75 0C PUSH DWORD PTR SS:[EBP+C]
00BD0753 FF75 08 PUSH DWORD PTR SS:[EBP+8]
00BD0756 FF93 2F10A800 CALL DWORD PTR DS:[EBX+A8102F] ; 过这里就看到函数了,应该是个GetProcAddress
00BD075C 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
00BD075F 8B8B 6110A800 MOV ECX,DWORD PTR DS:[EBX+A81061]
00BD0765 3B4D 08 CMP ECX,DWORD PTR SS:[EBP+8]
00BD0768 75 63 JNZ SHORT 00BD07CD ; 这里必须要跳,改成JMP就可避开输入表加密
00BD076A 33C0 XOR EAX,EAX
00BD076C 0383 4310A800 ADD EAX,DWORD PTR DS:[EBX+A81043]
00BD0772 74 0D JE SHORT 00BD0781
00BD0774 05 07000000 ADD EAX,7
00BD0779 3B83 4710A800 CMP EAX,DWORD PTR DS:[EBX+A81047]
00BD077F 72 25 JB SHORT 00BD07A6
00BD0781 6A 40 PUSH 40
00BD0783 68 00100000 PUSH 1000
00BD0788 68 00100000 PUSH 1000
00BD078D 6A 00 PUSH 0
00BD078F FF93 3F10A800 CALL DWORD PTR DS:[EBX+A8103F]
00BD0795 8983 4310A800 MOV DWORD PTR DS:[EBX+A81043],EAX
00BD079B 05 00100000 ADD EAX,1000
00BD07A0 8983 4710A800 MOV DWORD PTR DS:[EBX+A81047],EAX
00BD07A6 8DBB E910A800 LEA EDI,DWORD PTR DS:[EBX+A810E9]
00BD07AC 8BF7 MOV ESI,EDI
00BD07AE 81C7 01000000 ADD EDI,1
00BD07B4 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]
00BD07B7 AB STOS DWORD PTR ES:[EDI]
00BD07B8 8BBB 4310A800 MOV EDI,DWORD PTR DS:[EBX+A81043]
00BD07BE 8BC7 MOV EAX,EDI
00BD07C0 B9 07000000 MOV ECX,7
00BD07C5 018B 4310A800 ADD DWORD PTR DS:[EBX+A81043],ECX
00BD07CB F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
00BD07CD 5E POP ESI
00BD07CE 5F POP EDI
00BD07CF 5B POP EBX
00BD07D0 C9 LEAVE
00BD07D1 C2 0C00 RETN 0C
--------------------------------------------------------------------------------------------------
现在在地址 00BD0765 上设个硬件执行断点,CTR+F2 重新载入程序,F9,断在刚才设的硬件断点上。删掉这个硬件断点,现在把地址 00BD0768 处的 JNZ 改成 JMP,F9,会断在我们在 OEP 设的硬件断点上。这时我们打开 ImportREC,选 CPU-Z 进程,OEP 填 0007B9BA,点“自动查找 IAT”,会获得输入表。现在看看还有没有无效的,嗯,还有一个:
004962D0 7C80B4CF kernel32.GetModuleFileNameA
004962D4 00BD05C4 ;这个无效
004962D8 7C8310F2 kernel32.GlobalMemoryStatus
通过上面的三个函数,可以知道这个无效的函数应该是 kernel32.dll 中的。这样我们就在无效函数的前一个地址设内存写入断点,看看壳是怎么处理这个加密函数的。在地址 00BD0765 上设个硬件执行断点,现在我们 CTR+F2 重来。F9运行,硬件断点断下后删掉 00BD0765 的硬件断点,把地址 00BD0768 处的 JNZ 改成 JMP。数据窗口中在地址 004962D0 上设内存写入断点,在代码窗口中看到要往 004962D0 写内容时开始分析,发现还是断在老地方:
00BD1B70 8906 MOV DWORD PTR DS:[ESI],EAX ; 断在这里
00BD1B72 8902 MOV DWORD PTR DS:[EDX],EAX
00BD1B74 83C2 04 ADD EDX,4
00BD1B77 83C6 04 ADD ESI,4
00BD1B7A ^ EB AC JMP SHORT 00BD1B28
前面我们分析时地址 00BD0756 处的那个 CALL 我们没分析,只是猜测应该是个 GetProcAddress 的功能。这次我们进去看看:
00BD056B 55 PUSH EBP
00BD056C 8BEC MOV EBP,ESP
00BD056E 83C4 FC ADD ESP,-4
00BD0571 53 PUSH EBX
00BD0572 57 PUSH EDI
00BD0573 56 PUSH ESI
00BD0574 E8 00000000 CALL 00BD0579 ; 跟进去
00BD0579 5B POP EBX ; 跟进去后就到这里
00BD057A 81EB 0C11A800 SUB EBX,0A8110C
00BD0580 8B75 0C MOV ESI,DWORD PTR SS:[EBP+C]
00BD0583 81FE FFFF0000 CMP ESI,0FFFF
00BD0589 7F 15 JG SHORT 00BD05A0
00BD058B 81FE 83030000 CMP ESI,383
00BD0591 75 0D JNZ SHORT 00BD05A0
00BD0593 8D83 A211A800 LEA EAX,DWORD PTR DS:[EBX+A811A2]
00BD0599 5E POP ESI
00BD059A 5F POP EDI
00BD059B 5B POP EBX
00BD059C C9 LEAVE
00BD059D C2 0C00 RETN 0C
00BD05A0 FF75 10 PUSH DWORD PTR SS:[EBP+10]
00BD05A3 FF75 0C PUSH DWORD PTR SS:[EBP+C]
00BD05A6 FF75 08 PUSH DWORD PTR SS:[EBP+8]
00BD05A9 FF93 2910A800 CALL DWORD PTR DS:[EBX+A81029] ; 跟进去
00BD05AF 3B83 4110A800 CMP EAX,DWORD PTR DS:[EBX+A81041]
00BD05B5 75 06 JNZ SHORT 00BD05BD
00BD05B7 8D83 5711A800 LEA EAX,DWORD PTR DS:[EBX+A81157]
00BD05BD 5E POP ESI
00BD05BE 5F POP EDI
00BD05BF 5B POP EBX
00BD05C0 C9 LEAVE
00BD05C1 C2 0C00 RETN 0C
--------------------------------------------------------------------------------------------------
F7跟进 00BD0574 处的CALL,发现就是执行到地址 00BD0579。一路 F8,到 00BD05A9 时F7跟进:
00BD03CE 55 PUSH EBP
00BD03CF 8BEC MOV EBP,ESP
00BD03D1 83C4 FC ADD ESP,-4
00BD03D4 53 PUSH EBX
00BD03D5 57 PUSH EDI
00BD03D6 56 PUSH ESI
00BD03D7 E8 00000000 CALL 00BD03DC
00BD03DC 5B POP EBX
00BD03DD 81EB 0C11A800 SUB EBX,0A8110C
00BD03E3 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
00BD03E6 40 INC EAX
00BD03E7 75 18 JNZ SHORT 00BD0401
00BD03E9 8B75 0C MOV ESI,DWORD PTR SS:[EBP+C]
00BD03EC 81FE FFFF0000 CMP ESI,0FFFF
00BD03F2 76 0D JBE SHORT 00BD0401
00BD03F4 8D83 A011A800 LEA EAX,DWORD PTR DS:[EBX+A811A0]
00BD03FA 5E POP ESI
00BD03FB 5F POP EDI
00BD03FC 5B POP EBX
00BD03FD C9 LEAVE
00BD03FE C2 0C00 RETN 0C
00BD0401 FF75 10 PUSH DWORD PTR SS:[EBP+10]
00BD0404 FF75 0C PUSH DWORD PTR SS:[EBP+C]
00BD0407 FF75 08 PUSH DWORD PTR SS:[EBP+8]
00BD040A FF93 2910A800 CALL DWORD PTR DS:[EBX+A81029]
00BD0410 3B83 4110A800 CMP EAX,DWORD PTR DS:[EBX+A81041]
00BD0416 75 06 JNZ SHORT 00BD041E ; 这里改成 JMP 就可以跳过加密
00BD0418 8D83 5511A800 LEA EAX,DWORD PTR DS:[EBX+A81155]
00BD041E 5E POP ESI
00BD041F 5F POP EDI
00BD0420 5B POP EBX
00BD0421 C9 LEAVE
00BD0422 C2 0C00 RETN 0C
--------------------------------------------------------------------------------------------------
两个要改的位置都知道了,现在就可以获取完整的输入表了。从上面的分析内容,应该可以总结一下对于加密的输入表,在相应的加密输入表地址处设内存写入断点,能较快的找到加密的位置。
--------------------------------------------------------------------------------------------------
脱完壳修复优化后加载会出现一个实时错误,原因是第二个区段 .rdata 的特征值应为 40000040。
别问我是怎么知道的,我只是去 CPU-Z 的官方网站下了个原版对比了一下区段的特征值。
附上脱壳优化过的程序: