一段被破解程序中的shellcode分析
本帖最后由 Crack_Qs 于 2015-6-14 17:57 编辑只是感兴趣,大牛路过.
默笙妹子发来一个被用SMC方式破解的一个软件,于是研究其SMC中的ShellCode.
//////////////////////////////////////////////////////////////////////////////////////////////////
执行流程:
1.首先得到kernel32的ImageBase为起始解析PE,得到导出表.
2.然后取出GetProcAddress函数地址,获取指定的API
3.Api hook破解该软件
分析代码:
00400380 60 pushad ; 保存寄存器环境
00400381 8B4424 44 mov eax, dword ptr
00400385 25 0000FFFF and eax, 0xFFFF0000 ; 不为序号导出
0040038A 66:8138 4D5A cmp word ptr , 0x5A4D ; 判断MZ
0040038F 74 07 je short 00400398 ; 该判断成立即意味着已经得到kernel32的imageBase
00400391 2D 00100000 sub eax, 0x1000 ; 内存中4K对齐,挪动到kernel32 dll的头部
00400396 ^ EB F2 jmp short 0040038A ; 循环得到kernel32 imagebase
获得一个存放kernel32_ImageBase的指针:
00400398 50 push eax ; eax == kernel32_ImageBase
00400399 E8 C2000000 call 00400460 ; push 0x0040039E
0040039E 59 pop ecx
0040039F 58 pop eax
00400460:
00400460 E8 F5FFFFFF call 0040045A
00400465 0000 add byte ptr , al
0040045A:
0040045A 58 pop eax
0040045B 870424 xchg dword ptr , eax
0040045E 50 push eax
0040045F C3 retn
进函数时栈布局
$ ==> > 00400465__CALL_RET_EIP -> EAX
$+4 > 0040039E_CALL_RET_EIP->
$+8 > 7C800000kernel32_ImageBase
出函数时栈布局
$ ==> > 0040039E_CALL_RET_EIP
$+4 > 00400465__CALL_RET_EIP
$+8 > 7C800000kernel32.7C800000
得出的结论即是,mov ecx,00400465(kernel32_ImageBase),其他忽略不谈
通过PE结构的固定偏移算出kernel32.dll的导出表,为后面顺利取出WINAPI做准备
004003A0 8BD8 mov ebx, eax ; ebx == kernel32_ImageBase(SAVE)
004003A2 8B48 3C mov ecx, dword ptr ; ecx == e_lfanew(offset)
004003A5 03C8 add ecx, eax ; ecx == kernel32_Image_NT_Header
004003A7 8B51 78 mov edx, dword ptr ; edx == Kernel32_Export_Table(offset)
004003AA 03D0 add edx, eax ; EDX == Kernel32_ExportTable_VA
同上冲定位函数,直接给出结果 mov ecx,00400465
004003AC 50 push eax
004003AD E8 AE000000 call 00400460
004003B2 59 pop ecx
004003B3 58 pop eax
004003B4 83C1 08 add ecx, 0x8 ; ecx == pNewEipAddr
004003B7 50 push eax ; kernel32_ImageBase
004003B8 51 push ecx ; ecx == pNewEipAddr
004003B9 E8 9C000000 call 0040045A
0040045A:
0040045A 58 pop eax
0040045B 870424 xchg dword ptr , eax
0040045E 50 push eax
0040045F C3 retn
有意思的在于下面的一句,之前没注意看,此时才发现&pKernel32 + 8的位置是接下来运算的中的shellcode的一部分
004003B4 83C1 08 add ecx, 0x8 ; ecx == 0040046D(pKernel32)
004003B7 50 push eax ; kernel32_ImageBase
004003B8 51 push ecx ; ecx == pUnKnowAddr
004003B9 E8 9C000000 call 0040045A
0040046D 59 pop ecx
0040046E 58 pop eax ; eax == Kernel32_ImageBase
0040046F 8B4A 20 mov ecx, dword ptr ; ecx == Kernel32_ExprotTable_AddrOfNames(offset)
00400472 52 push edx ; edx == Kernel32_ExportTable_VA
00400473 03C1 add eax, ecx ; eax == Kernel32_ExprotTable_AddrOfNames
00400475 53 push ebx ; ebx == Kernel32_ImageBase(SAVE)
00400476 33DB xor ebx, ebx
00400478 EB 04 jmp short 0040047E
0040047A 83C0 04 add eax, 0x4
0040047D 43 inc ebx
0040047E 8B0C24 mov ecx, dword ptr ; ecx == Kernel32_ImageBase
00400481 8B10 mov edx, dword ptr ; api offset
00400483 03D1 add edx, ecx ; edx == API Addr
00400485 8BFA mov edi, edx ; Sava Api Addr
00400487 33C9 xor ecx, ecx
00400489 50 push eax ; Kernel32_ExprotTable_AddrOfNames(Save)
0040048A 33C0 xor eax, eax
0040048C 41 inc ecx ; 计数器(API Name Len)
0040048D AE scas byte ptr es:
0040048E^ 75 FC jnz short 0040048C
00400490 49 dec ecx
00400491 E8 28FFFFFF call 004003BE
004003BE E8 97000000 call 0040045A
004003C3 47 inc edi ;看不懂正常,因为是字符串
004003C4 65:74 50 je short 00400417
004003C7 72 6F jb short 00400438
004003C9 6341 64 arpl word ptr , ax
004003CC 64:72 65 jb short 00400434
004003CF 73 73 jnb short 00400444
004003D1 0000 add byte ptr , al
004003D3 56 push esi
004003D4 6972 74 75616C5>imul esi, dword ptr , 0x506>
004003DB 72 6F jb short 0040044C
004003DD 74 65 je short 00400444
004003DF 637400 00 arpl word ptr , si
004003E3 4D dec ebp
004003E4 61 popad
004003E5 70 56 jo short 0040043D
004003E7 6965 77 4F66466>imul esp, dword ptr , 0x694>
004003EE 6C ins byte ptr es:, dx
004003EF 65:0000 add byte ptr gs:, al
004003F2 0043 72 add byte ptr , al
004003F5 65:61 popad
004003F7 74 65 je short 0040045E
004003F9 46 inc esi
004003FA 696C65 41 00000>imul ebp, dword ptr , 0x0
00400402 0056 69 add byte ptr , dl
00400405 72 74 jb short 0040047B
00400407 75 61 jnz short 0040046A
00400409 6C ins byte ptr es:, dx
0040040A 41 inc ecx
0040040B 6C ins byte ptr es:, dx
0040040C 6C ins byte ptr es:, dx
0040040D 6F outs dx, dword ptr es:
0040040E 6300 arpl word ptr , ax
004003C347 65 74 50 72 6F 63 41 64 64 72 65 73 73 00 00GetProcAddress..
004003D356 69 72 74 75 61 6C 50 72 6F 74 65 63 74 00 00VirtualProtect..
004003E34D 61 70 56 69 65 77 4F 66 46 69 6C 65 00 00 00MapViewOfFile...
004003F343 72 65 61 74 65 46 69 6C 65 41 00 00 00 00 00CreateFileA.....
0040040356 69 72 74 75 61 6C 41 6C 6C 6F 63 00 00 00 00VirtualAlloc....
当前在获取GetProcAddress
0040047A 83C0 04 add eax, 0x4 ; 挪动指针,方便取新API
0040047D 43 inc ebx ; 计数器
0040047E 8B0C24 mov ecx, dword ptr ; ecx == Kernel32_ImageBase
00400481 8B10 mov edx, dword ptr ; api offset
00400483 03D1 add edx, ecx ; edx == API Addr
00400485 8BFA mov edi, edx ; Sava Api Addr
00400487 33C9 xor ecx, ecx
00400489 50 push eax ; Kernel32_ExprotTable_AddrOfNames(Save)
0040048A 33C0 xor eax, eax
0040048C 41 inc ecx ; 计数器(API Name Len)
0040048D AE scas byte ptr es:
0040048E^ 75 FC jnz short 0040048C
00400490 49 dec ecx
00400491 E8 28FFFFFF call 004003BE
00400496 5E pop esi ; esi == "GetProcAddress"
00400497 58 pop eax ; eax == Kernel32_ExprotTable_AddrOfNames
00400498 8BFA mov edi, edx ; 当前遍历出的API
0040049A F3:A6 repe cmps byte ptr es:, byte ptr>; 比较是否一致
0040049C^ 75 DC jnz short 0040047A ; 不一致开始下一轮
0040049E 58 pop eax ; eax == Kernel32_ImageBase
0040049F 870424 xchg dword ptr , eax ; 与Kernel32_Export_Table_VA交换
004004A2 90 nop
004004A3 83C0 1C add eax, 0x1C ; eax == Kernel32_ExportTable_AddrOfFunc(offset)
004004A6 8B00 mov eax, dword ptr ; eax == Kernel32_ExportTable_AddrOfFunc
004004A8 50 push eax
004004A9 8B4C24 04 mov ecx, dword ptr ; ecx == Kernel32_ImageBase
004004AD 8BFF mov edi, edi
004004AF 58 pop eax
004004B0 03C1 add eax, ecx ; eax == Kernel32_ExportTable_AddrOfFunc(VA)
004004B2 8B0498 mov eax, dword ptr ; 利用计数器查表得到API offset
004004B5 03C1 add eax, ecx ; 得到API
004004B7 50 push eax ; push api addr
004004B8 E8 A3FFFFFF call 00400460 ; 执行
004004C8 59 pop ecx
004004C9 58 pop eax
004004CA 83C1 10 add ecx, 0x10 ; 取自定义API名字表中的下一项(VirtualProtect)
004004CD 51 push ecx
004004CE 8BD0 mov edx, eax
004004D0 E8 8BFFFFFF call 00400460
004004D5 59 pop ecx
004004D6 58 pop eax
004004D7 50 push eax ; push lpProcName
004004D8 FF31 push dword ptr ; hModule
004004DA FFD2 call edx ; edx == GetProcAddress
004004DC 50 push eax ; api(VirtualProtect) addr
004004DD E8 4EFEFFFF call 00400330
00400330 8BF8 mov edi, eax
00400332 50 push eax
00400333 54 push esp
00400334 6A 40 push 0x40 ; VirtualProtect 参数 PAGE_EXECUTE_READWRITE
00400336 68 00010000 push 0x100 ; VirtualProtect 参数 Size
0040033B E8 F3000000 call 00400433
00400436 0000 add byte ptr , al
00400438 D4 1A aam 0x1A ; VirtualProtectEx addr
0040043A 807C95 B9 80 cmp byte ptr , 0x80; MapViewOfFile addr
0040043F 7C 00 jl short 00400441
00400350 8B4424 08 mov eax, dword ptr
00400354 8901 mov dword ptr , eax ; 保存 kernel32_imagebase
00400356 8959 04 mov dword ptr , ebx ; 保存GetProcAddress
00400359 E8 D5000000 call 00400433
0040035E 59 pop ecx
0040035F C3 retn
00400580 59 pop ecx
00400581 58 pop eax
00400582 50 push eax ; push lpProcName(MapViewOffile)
00400583 FF31 push dword ptr ; hModule
00400585 FF51 04 call dword ptr ; kernel32.GetProcAddress
保存了前五字节,应是要进入HOOK:
004005B8 59 pop ecx
004005B9 58 pop eax
004005BA 83C1 14 add ecx, 0x14
004005BD 8B10 mov edx, dword ptr
004005BF 66:8B58 04 mov bx, word ptr
004005C3 8911 mov dword ptr , edx
004005C5 66:8959 04 mov word ptr , bx
004005C9 C3 retn
HOOK操作:
00400606 59 pop ecx
00400607 58 pop eax
00400608 C600 68 mov byte ptr , 0x68
0040060B 8948 01 mov dword ptr , ecx
0040060E C640 05 C3 mov byte ptr , 0xC3
00400612 C3 retn
恢复环境:
00400598 61 popad
00400599 830424 07 add dword ptr , 0x7
0040059D C3 retn
CreateFile挂后:
7C801A28 >68 8F074000 push 0x40078F ; ASCII "PQ?"
7C801A2D C3 retn
MapViewOffile挂后:
7C80B995 >68 38064000 push 0x400638
7C80B99A C3 retn
如何构造的shellcode已经分析完成,下面看是如何破解的:
hook:
004007CC 50 push eax
004007CD 83C0 75 add eax, 0x75
004007D0 B9 16924300 mov ecx, 00439216
004007D5 2BC1 sub eax, ecx
004007D7 C601 E8 mov byte ptr , 0xE8
004007DA 8941 01 mov dword ptr , eax
004007DD C741 05 8BCEEBA>mov dword ptr , 0xACEBCE8B
004007E4 83E9 4D sub ecx, 0x4D
004007E7 66:C701 EB4B mov word ptr , 0x4BEB
004007EC 830424 01 add dword ptr , 0x1
004007F0 81C1 95220000 add ecx, 0x2295
004007F6 C701 33C04083 mov dword ptr , 0x8340C033
004007FC 81C1 D2830000 add ecx, 0x83D2
00400802 66:C701 C390 mov word ptr , 0x90C3
00400807 C3 retn
Patch Code(只是简单跟了下,估计不全只简单跟了下hook createfile的部分,没测试过):
1.
004391c9:
jmp PatchAddr
PatchAddr:
call PatchFun(00400810)
恢复原流程:
mov ecx, esi
jmp 004391CB
2.
0043B45E|.8B07 mov eax, dword ptr
0043B460|.48 dec eax ;Switch (cases 1..C)
0043B461|.83F8 0B cmp eax, 0xB
0043B464|.0F87 95020000 ja 0043B6FF
0043B45E|.33C0 xor eax, eax
0043B460|.40 inc eax ;Switch (cases 1..C)
0043B461|.83F8 0B cmp eax, 0xB
0043B464|.0F87 95020000 ja 0043B6FF
3.
00443830 .6A FF push -0x1
00443830 .C3 retn
00443831 ?90 nop
屌炸天,值得学习! 碉堡了! QS,你看下400899哪里开始的后面的KEY是怎么处理的 {:soso_e100:}学习一下! vipcrack 发表于 2015-6-14 18:36
QS,你看下400899哪里开始的后面的KEY是怎么处理的
你又一眼看到了重点,鄙视你的智商太高了。什么时候带带我 不错,你排版的真不错,好舒服,一下就看懂了
碉堡了!+1 不错的技术分析
页:
[1]