【 标题 】 堀北压缩 0.28 beta脱壳
【 作者 】 linxer
【 Q Q 】 3568599
【破解平台】 盗版xp sp2
【脱壳工具】 OllyICE + OllyDump插件
【待脱软件】 mouserate.exe 见附件(用堀北压缩 0.28 beta加壳)
【 声明 】 初学脱壳,只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
这款壳,脱法很简单,不过,在网上搜了下,貌似还没人写这个脱文,这里就粗略写一篇出来吧
OD加载程序,程序停留在入口
00401000 >/$ 68 37A34000 push 0040A337
00401005 |. E8 01000000 call 0040100B ;调用一个空函数
0040100A \. C3 retn ;F7后 到0x0040A337处执行
0040100B $ C3 retn
0040A337 B8 49A30000 mov eax, 0A349 ;随着指令的执行,等下这个指令的立即数会改变,目前不管,F7
0040A33C BA 00004000 mov edx, 00400000
0040A341 03C2 add eax, edx
0040A343 FFE0 jmp eax ;跳到0x0040A349执行
0040A345 B1 15 mov cl, 15 ;它和下一条指令,其实是代码中夹杂的一个4字节数据
0040A347 0000 add byte ptr [eax], al
0040A349 60 pushad
0040A34A E8 00000000 call 0040A34F ;这个地方应该在重定位程序上有用吧
0040A34F 5E pop esi ; mouserat.0040A34F
0040A350 83EE 0A sub esi, 0A
0040A353 8B06 mov eax, dword ptr [esi]
0040A355 03C2 add eax, edx
0040A357 8B08 mov ecx, dword ptr [eax]
0040A359 894E F3 mov dword ptr [esi-D], ecx
0040A35C 83EE 0F sub esi, 0F
0040A35F 56 push esi
0040A360 52 push edx
0040A361 8BF0 mov esi, eax
0040A363 AD lods dword ptr [esi]
0040A364 AD lods dword ptr [esi]
0040A365 03C2 add eax, edx
0040A367 8BD8 mov ebx, eax
0040A369 6A 04 push 4
0040A36B BF 00100000 mov edi, 1000
0040A370 57 push edi
0040A371 57 push edi
0040A372 6A 00 push 0
0040A374 FF53 08 call dword ptr [ebx+8] ;VirtualAlloc调用
0040A377 5A pop edx
0040A378 59 pop ecx
0040A379 BD 00800000 mov ebp, 8000
0040A37E 55 push ebp
0040A37F 6A 00 push 0
0040A381 50 push eax
0040A382 51 push ecx
0040A383 52 push edx
0040A384 50 push eax
0040A385 8906 mov dword ptr [esi], eax
0040A387 AD lods dword ptr [esi]
0040A388 AD lods dword ptr [esi]
0040A389 03C2 add eax, edx
0040A38B 50 push eax
0040A38C AD lods dword ptr [esi]
0040A38D 03C2 add eax, edx
0040A38F FFD0 call eax ;F8步过(这里解密出来一部分代码到刚分配内存中)
0040A391 6A 04 push 4
0040A393 57 push edi
0040A394 AD lods dword ptr [esi]
0040A395 50 push eax
0040A396 6A 00 push 0
0040A398 FF53 08 call dword ptr [ebx+8] ;VirtualAlloc调用
0040A39B 5A pop edx
0040A39C 55 push ebp
0040A39D 6A 00 push 0
0040A39F 50 push eax ;压入VirtualFree的参数
0040A3A0 FF73 0C push dword ptr [ebx+C] ;压入VirtualFree地址,在处理完导入表后,用ret指令触发这个函数释放内存
0040A3A3 52 push edx
0040A3A4 6A 04 push 4
0040A3A6 57 push edi
0040A3A7 8BF8 mov edi, eax
0040A3A9 55 push ebp
0040A3AA 6A 00 push 0
0040A3AC FF53 08 call dword ptr [ebx+8] ;VirtualAlloc调用
0040A3AF 5A pop edx
0040A3B0 55 push ebp
0040A3B1 6A 00 push 0
0040A3B3 50 push eax ;压入VirtualFree的参数
0040A3B4 FF73 0C push dword ptr [ebx+C] ;压入VirtualFree地址,在处理完导入表后,用ret指令触发这个函数释放内存
0040A3B7 FF3424 push dword ptr [esp] ;再次压入VirtualFree地址
0040A3BA 8BC8 mov ecx, eax
0040A3BC 50 push eax
0040A3BD 54 push esp
0040A3BE FF76 FC push dword ptr [esi-4]
0040A3C1 57 push edi
0040A3C2 AD lods dword ptr [esi]
0040A3C3 50 push eax
0040A3C4 AD lods dword ptr [esi]
0040A3C5 03C2 add eax, edx
0040A3C7 50 push eax
0040A3C8 55 push ebp
0040A3C9 51 push ecx
0040A3CA 8BEA mov ebp, edx
0040A3CC FF56 E8 call dword ptr [esi-18] ;F8步过(这里跳到第一次分的内存中执行,里面貌似在解压代码到第二次分配内存)
0040A3CF 58 pop eax
0040A3D0 012E add dword ptr [esi], ebp
0040A3D2 8B3E mov edi, dword ptr [esi]
0040A3D4 AD lods dword ptr [esi]
0040A3D5 50 push eax
0040A3D6 AD lods dword ptr [esi]
0040A3D7 50 push eax
0040A3D8 AD lods dword ptr [esi]
0040A3D9 50 push eax
0040A3DA AD lods dword ptr [esi]
0040A3DB 50 push eax
0040A3DC 33C0 xor eax, eax
0040A3DE B9 80010000 mov ecx, 180
0040A3E3 F3:AB rep stos dword ptr es:[edi]
0040A3E5 8B7424 28 mov esi, dword ptr [esp+28]
0040A3E9 AC lods byte ptr [esi]
0040A3EA 8BD0 mov edx, eax
0040A3EC AD lods dword ptr [esi]
0040A3ED 8BF8 mov edi, eax
0040A3EF 03FD add edi, ebp
0040A3F1 AD lods dword ptr [esi]
0040A3F2 8BC8 mov ecx, eax
0040A3F4 F3:A4 rep movs byte ptr es:[edi], byte ptr [esi] ;拷解压代码到程序中
0040A3F6 4A dec edx
0040A3F7 ^ 75 F3 jnz short 0040A3EC ;这里一个小循环,在它下一指令F4
0040A3F9 59 pop ecx ;F4
0040A3FA 5A pop edx
0040A3FB 58 pop eax
0040A3FC 5F pop edi
0040A3FD 85C0 test eax, eax ;又要出现循环了,到0x0040A41D处F4
0040A3FF 74 1C je short 0040A41D
0040A401 57 push edi
0040A402 8A07 mov al, byte ptr [edi]
0040A404 47 inc edi
0040A405 2C E8 sub al, 0E8
0040A407 3C 01 cmp al, 1
0040A409 ^ 77 F7 ja short 0040A402
0040A40B 8B07 mov eax, dword ptr [edi]
0040A40D 3AC2 cmp al, dl
0040A40F ^ 75 F1 jnz short 0040A402
0040A411 32C0 xor al, al
0040A413 0FC8 bswap eax
0040A415 030424 add eax, dword ptr [esp]
0040A418 2BC7 sub eax, edi
0040A41A AB stos dword ptr es:[edi]
0040A41B ^ E2 E5 loopd short 0040A402 ;循环里的代码在搞啥东东,俺不知道
0040A41D 55 push ebp ;F4
0040A41E AD lods dword ptr [esi]
0040A41F 85C0 test eax, eax
//下面代码处理导入表,dll名称,函数序号,函数名称在esi指定的地方,每个dll信息导入信息排列如下:
1.最开始是dll名称,然后紧接是其导出的函数序号/函数名,最后是一个长整型的0
0040A421 74 37 je short 0040A45A ;这个循环里,出口是0x0040A45A,可以到这个地方F4
0040A423 8BF8 mov edi, eax
0040A425 033C24 add edi, dword ptr [esp]
0040A428 56 push esi
0040A429 FF13 call dword ptr [ebx] ;LoadLibraryA调用
0040A42B 8BE8 mov ebp, eax
0040A42D AC lods byte ptr [esi]
0040A42E 84C0 test al, al
0040A430 ^ 75 FB jnz short 0040A42D ;这个小循环在寻找dll名称结束地址
0040A432 AD lods dword ptr [esi]
0040A433 85C0 test eax, eax ;看是否处理完这个dll
0040A435 ^ 74 E7 je short 0040A41E ;处理完则跳,处理下一个dll
0040A437 83EE 04 sub esi, 4
0040A43A AD lods dword ptr [esi] ;装函数序号或函数名前4字节到eax
0040A43B A9 00000080 test eax, 80000000 ;判定是否序号导出,如是函数名也能正常工作,因为ascii最高位为0
0040A440 75 0B jnz short 0040A44D
0040A442 83EE 04 sub esi, 4 ;函数名导出情况
0040A445 56 push esi
0040A446 55 push ebp
0040A447 FF53 04 call dword ptr [ebx+4] ;GetProcAddress调用
0040A44A AB stos dword ptr es:[edi]
0040A44B ^ EB E0 jmp short 0040A42D
0040A44D 25 FFFFFF7F and eax, 7FFFFFFF ;序号导出情况
0040A452 50 push eax
0040A453 55 push ebp
0040A454 FF53 04 call dword ptr [ebx+4] ;GetProcAddress调用
0040A457 AB stos dword ptr es:[edi]
0040A458 ^ EB D8 jmp short 0040A432
0040A45A 5D pop ebp ;在这个地方F4,可以不详细跟导入表处理
0040A45B 5F pop edi
0040A45C C3 retn ;运行到这个地方,看下堆栈吧
堆栈如下:
0012FF70 7C809AE4 kernel32.VirtualFree ;retn指令触发这个VirtualFree调用,这个调用的返回地址还是VirtualFree,参数是
0012FF74 7C809AE4 kernel32.VirtualFree ;第一次VirtualFree调用时返回地址
0012FF78 00390000 ;第一次VirtualFree调用参数
0012FF7C 00000000 ;第一次VirtualFree调用参数
0012FF80 00008000 ;第一次VirtualFree调用参数
0012FF84 7C809AE4 kernel32.VirtualFree
0012FF88 00380000
0012FF8C 00000000
0012FF90 00008000
0012FF94 0040A336 返回到 mouserat.0040A336
0012FF98 00370000
0012FF9C 00000000
0012FFA0 00008000
将上面堆栈产生效果分解下:
第一个VirtualFree调用完后,马上会执行第二个VirtualFree,这个时候的堆栈为:
0012FF84 7C809AE4 kernel32.VirtualFree ;第二次VirtualFree调用时返回地址,可见又是一个VirtualFree
0012FF88 00380000 ;第二次VirtualFree调用参数
0012FF8C 00000000 ;第二次VirtualFree调用参数
0012FF90 00008000 ;第二次VirtualFree调用参数
0012FF94 0040A336 返回到 mouserat.0040A336
0012FF98 00370000
0012FF9C 00000000
0012FFA0 00008000
第二个VirtualFree调用完后,马上会执行第三个VirtualFree,这个时候的堆栈为:
0012FF94 0040A336 返回到 mouserat.0040A336 ;这个是返回地址,终于要返回我们想要的地方了
0012FF98 00370000 ;VirtualFree的三个参数
0012FF9C 00000000
0012FFA0 00008000
从上面分析知,程序在完成三个VirtualFree调用后,将返回0040A336这个地方执行,在这个地方下断点,bp 0x0040a336,然后F9,到如下地方:
0040A336 61 popad
0040A337 B8 F01B0000 mov eax, 1BF0
0040A33C BA 00004000 mov edx, 00400000
0040A341 03C2 add eax, edx
0040A343 FFE0 jmp eax ;终于要到OEP了,F7进入到下面代码
00401BF0 . 55 push ebp ;在这个地方用ollydump插件dump吧
00401BF1 . 8BEC mov ebp, esp
00401BF3 . 6A FF push -1
00401BF5 . 68 90254000 push 00402590
00401BFA . 68 001E4000 push 00401E00 ; jmp 到 msvcrt._except_handler3; SE 处理程序安装
00401BFF . 64:A1 0000000>mov eax, dword ptr fs:[0]
dump出来程序可以运行,优化就不做了,因为我不太会,还有待学习呢
这里总结下这个壳的脱法:
1.bp VirtualFree,在第三次执行这个函数时,在返回地址在下断点,运行到这个断点处
2.在单步执行5条指令到OEP
当然这个壳也可以用ESP定律秒杀
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法