脱upx壳踩的一些坑
练习upx脱壳,该app其实可以直接使用upx.exe -d解压,但是还是想试试看手动脱壳的感觉。踩了两个大坑,留个记录,想必对后来者会有一些帮助。
重建IAT问题
查找oep还算比较顺利的,在重建IAT时遇到一些问题:
如图所示,像这种iat很明显是有问题的,而且观察加载的dll,还有apphelp.dll,这个dll应该是处于兼容性目的存在的,目的是转发api。除了大量的重复的dll之外,还有就是一些错误的数据,比如:
这些api应该是来自usp10.dll的,不知道为何放到gdi32.dll上去了。如此种种。后来经过一些研究发现,这可能是兼容性问题造成的。我最初是在Windows11下面脱壳的,所以有这些问题。后来在Windows XP sp3 32bit和Windows 7 64bit下都试过都没有这些烦恼。Windows 10尚未尝试过。
运行时Runtime Error R6002 - Floating point not loaded after unpacking错误
这个问题看起来像是msvc的实现上的问题,在初始化crt时会检测它所需要的一些数据所在的section是否是只读的,如果不是的话,那么它可能会跳过一些初始化步骤,最终会导致在执行一些crt函数时报上面这个错,比如sprintf之类的。
这个检查是在内部函数__IsNonwritableInCurrentImage中实现的,通过ida的函数列表并不能找到这个函数,但是我们可以先找到__cinit函数,然后在它的头部可以看到这个函数,双击即可跳转过去了:
; =============== S U B R O U T I N E ======================================= UPX0:005E4910 UPX0:005E4910 ; Attributes: library function bp-based frame UPX0:005E4910 UPX0:005E4910 __IsNonwritableInCurrentImage proc near ; CODE XREF: __cinit+E↑p UPX0:005E4910 ; __cinit+79↑p UPX0:005E4910 ; SEH_5E4910+FF↑p UPX0:005E4910 ; __endthreadex+E↑p UPX0:005E4910 ; _threadstartex(x)+6A↑p UPX0:005E4910 UPX0:005E4910 ms_exc= CPPEH_RECORD ptr -18h UPX0:005E4910 arg_0= dword ptr 8 UPX0:005E4910 UPX0:005E4910 ; __unwind { // __except_handler4 UPX0:005E4910 55 push ebp UPX0:005E4911 8B EC mov ebp, esp UPX0:005E4913 6A FE push 0FFFFFFFEh UPX0:005E4915 68 90 79 67 00 push offset stru_677990 UPX0:005E491A 68 80 F6 5D 00 push offset __except_handler4 UPX0:005E491F 64 A1 00 00 00 00 mov eax, large fs:0 UPX0:005E4925 50 push eax UPX0:005E4926 83 EC 08 sub esp, 8 UPX0:005E4929 53 push ebx UPX0:005E492A 56 push esi UPX0:005E492B 57 push edi UPX0:005E492C A1 80 58 68 00 mov eax, ___security_cookie UPX0:005E4931 31 45 F8 xor [ebp+ms_exc.registration.ScopeTable], eax UPX0:005E4934 33 C5 xor eax, ebp UPX0:005E4936 50 push eax UPX0:005E4937 8D 45 F0 lea eax, [ebp+ms_exc.registration] UPX0:005E493A 64 A3 00 00 00 00 mov large fs:0, eax UPX0:005E4940 89 65 E8 mov [ebp+ms_exc.old_esp], esp UPX0:005E4940 UPX0:005E4943 ; __try { // __except at loc_5E49AD UPX0:005E4943 C7 45 FC 00 00 00 00 mov [ebp+ms_exc.registration.TryLevel], 0 UPX0:005E494A 68 00 00 40 00 push offset __ImageBase UPX0:005E494F E8 3C FF FF FF call __ValidateImageBase UPX0:005E494F UPX0:005E4954 83 C4 04 add esp, 4 UPX0:005E4957 85 C0 test eax, eax UPX0:005E4959 74 55 jz short loc_5E49B0 UPX0:005E4959 UPX0:005E495B 8B 45 08 mov eax, [ebp+arg_0] UPX0:005E495E 2D 00 00 40 00 sub eax, offset __ImageBase UPX0:005E4963 50 push eax UPX0:005E4964 68 00 00 40 00 push offset __ImageBase UPX0:005E4969 E8 52 FF FF FF call __FindPESection UPX0:005E4969 UPX0:005E496E 83 C4 08 add esp, 8 UPX0:005E4971 85 C0 test eax, eax UPX0:005E4973 74 3B jz short loc_5E49B0 UPX0:005E4973 UPX0:005E4975 8B 40 24 mov eax, [eax+24h] UPX0:005E4978 C1 E8 1F shr eax, 1Fh UPX0:005E497B F7 D0 not eax UPX0:005E497D 83 E0 01 and eax, 1 UPX0:005E497D ; } // starts at 5E4943 UPX0:005E4980 C7 45 FC FE FF FF FF mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh UPX0:005E4987 8B 4D F0 mov ecx, [ebp+ms_exc.registration.Next] UPX0:005E498A 64 89 0D 00 00 00 00 mov large fs:0, ecx UPX0:005E4991 59 pop ecx UPX0:005E4992 5F pop edi UPX0:005E4993 5E pop esi UPX0:005E4994 5B pop ebx UPX0:005E4995 8B E5 mov esp, ebp UPX0:005E4997 5D pop ebp UPX0:005E4998 C3 retn UPX0:005E4998 UPX0:005E4999 ; --------------------------------------------------------------------------- UPX0:005E4999 UPX0:005E4999 loc_5E4999: ; DATA XREF: UPX0:stru_677990↓o UPX0:005E4999 ; __except filter // owned by 5E4943 UPX0:005E4999 8B 45 EC mov eax, [ebp+ms_exc.exc_ptr] UPX0:005E499C 8B 08 mov ecx, [eax] UPX0:005E499E 8B 01 mov eax, [ecx] UPX0:005E49A0 33 D2 xor edx, edx UPX0:005E49A2 3D 05 00 00 C0 cmp eax, 0C0000005h UPX0:005E49A7 0F 94 C2 setz dl UPX0:005E49AA 8B C2 mov eax, edx UPX0:005E49AC C3 retn UPX0:005E49AC UPX0:005E49AD ; --------------------------------------------------------------------------- UPX0:005E49AD UPX0:005E49AD loc_5E49AD: ; DATA XREF: UPX0:stru_677990↓o UPX0:005E49AD ; __except(loc_5E4999) // owned by 5E4943 UPX0:005E49AD 8B 65 E8 mov esp, [ebp+ms_exc.old_esp] UPX0:005E49AD UPX0:005E49B0 UPX0:005E49B0 loc_5E49B0: ; CODE XREF: __IsNonwritableInCurrentImage+49↑j UPX0:005E49B0 ; __IsNonwritableInCurrentImage+63↑j UPX0:005E49B0 C7 45 FC FE FF FF FF mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh UPX0:005E49B7 33 C0 xor eax, eax UPX0:005E49B9 8B 4D F0 mov ecx, [ebp+ms_exc.registration.Next] UPX0:005E49BC 64 89 0D 00 00 00 00 mov large fs:0, ecx UPX0:005E49C3 59 pop ecx UPX0:005E49C4 5F pop edi UPX0:005E49C5 5E pop esi UPX0:005E49C6 5B pop ebx UPX0:005E49C7 8B E5 mov esp, ebp UPX0:005E49C9 5D pop ebp UPX0:005E49CA C3 retn UPX0:005E49CA ; } // starts at 5E4910 UPX0:005E49CA UPX0:005E49CA __IsNonwritableInCurrentImage endp
理论上来说有两种办法可以修复这个错误,一是修改section的权限,让本来应该只读的section变成只读的,但是这有一个问题,因为upx解密后,原来的section可能混在一起了,那么你单纯使用工具去修改pe头中的section权限会导致其它问题。
我选择第二种方法,直接修改__IsNonwritableInCurrentImage函数,让它总是返回1就可以了,修改前:
UPX0:005E497B F7 D0 not eax UPX0:005E497D 83 E0 01 and eax, 1
修改后:
UPX0:005E497B B8 01 00 00 00 mov eax, 1
保存,问题解决。