<< QQ四国军旗外挂 Category: 黑客技术文章 PEiD <= 0.92溢出分析 >>
PECompact 2.x -> Jeremy Collake    [ 2009-04-09 12:22:24 AM | Author: kyo327 | From: Original ]
闲着也是闲着,无聊时打发时间吧.
也玩玩壳aspack upx upack0.39final Nspack3.7 Petite 2.2这些都是简单的压缩壳,直接ESP定律到OEP再DUMP就OK了.我就不记录了.


PECompact 2.x -> Jeremy Collake

一、查找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上设个硬件执行断点,方便下次到达。 he 47b9ba

二、解开输入表
到OEP后
CALL cpuz.0048703A 这个call F7进去,
看到这个
/*48706F*/ CALL DWORD PTR DS:[496184]
dd 496184 看看输入表情况

00496100 77EFEF1C gdi32.GetTextExtentPoint32A
00496104 77EFB74C gdi32.GetPixel
00496108 77EF6F79 gdi32.BitBlt
0049610C 77EF5FE0 gdi32.createCompatibleDC
00496110 77EF700A gdi32.createCompatibleBitmap
00496114 00000000
00496118 00AF0000 //这里开始加密
0049611C 00AF0007
00496120 00AF000E
00496124 00AF0015

可以看到gdi32.createCompatibleBitmap下面的IAT都被加密了。

记住加密输入表的开始位置00496118,重新载入程序,在数据窗口中转到地址

00496118,设内存写入断点,F9几次,在代码窗口中看到要写入 00AF0000 时开始

分析:

/*AE1B70*/ MOV DWORD PTR DS:[ESI],EAX ; 断在这里
/*AE1B72*/ MOV DWORD PTR DS:[EDX],EAX
/*AE1B74*/ ADD EDX,4
/*AE1B77*/ ADD ESI,4
/*AE1B7A*/ JMP SHORT 00AE1B28
/*AE1B7C*/ XOR EAX,EAX
/*AE1B7E*/ POP ESI
/*AE1B7F*/ POP EDI
/*AE1B80*/ POP EBX
/*AE1B81*/ LEAVE
/*AE1B82*/ RET 10

------------------------------------------------------------------------
看看信息窗口:

EAX=00AF0000
DS:[00496118]=00000000
------------------------------------------------------------------------
现在按F8来分析,看看这个00BE0000之类的东西是怎么来的:

/*AE1B4F*/ MOV ECX,DWORD PTR SS:[EBP+8]
/*AE1B52*/ ADD EAX,DWORD PTR DS:[ECX+8]
/*AE1B55*/ XOR ECX,ECX
/*AE1B57*/ MOV CX,WORD PTR DS:[EAX]
/*AE1B5A*/ PUSH ECX
/*AE1B5B*/ INC EAX
/*AE1B5C*/ INC EAX
/*AE1B5D*/ PUSH EAX
/*AE1B5E*/ PUSH DWORD PTR SS:[EBP-4]
/*AE1B61*/ CALL DWORD PTR DS:[EBX+10001F4D] //这个CALL是关键,F7进去
/*AE1B67*/ POP EDX
/*AE1B68*/ TEST EAX,EAX
/*AE1B6A*/ JE 00AE1ADF
/*AE1B70*/ MOV DWORD PTR DS:[ESI],EAX
/*AE1B72*/ MOV DWORD PTR DS:[EDX],EAX
/*AE1B74*/ ADD EDX,4
/*AE1B77*/ ADD ESI,4
/*AE1B7A*/ JMP SHORT 00AE1B28
/*AE1B7C*/ XOR EAX,EAX


跟进/*AE1B61*/处的那个CALL:

/*AE0738*/ PUSH EBP
/*AE0739*/ MOV EBP,ESP
/*AE073B*/ ADD ESP,-4
/*AE073E*/ PUSH EBX
/*AE073F*/ PUSH EDI
/*AE0740*/ PUSH ESI
/*AE0741*/ CALL 00AE0746
/*AE0746*/ POP EBX
/*AE0747*/ SUB EBX,0A810FE
/*AE074D*/ PUSH DWORD PTR SS:[EBP+10]
/*AE0750*/ PUSH DWORD PTR SS:[EBP+C]
/*AE0753*/ PUSH DWORD PTR SS:[EBP+8]
/*AE0756*/ CALL DWORD PTR DS:[EBX+A8102F]
; 过这里就看到函数了,应该是个GetProcAddress
/*AE075C*/ MOV DWORD PTR SS:[EBP-4],EAX
/*AE075F*/ MOV ECX,DWORD PTR DS:[EBX+A81061]
/*AE0765*/ CMP ECX,DWORD PTR SS:[EBP+8]
/*AE0768*/ JNZ SHORT 00AE07CD ; 这里必须要跳,改成JMP就可避开输入表加密

/*AE076A*/ XOR EAX,EAX
/*AE076C*/ ADD EAX,DWORD PTR DS:[EBX+A81043]
/*AE0772*/ JE SHORT 00AE0781
/*AE0774*/ ADD EAX,7
/*AE0779*/ CMP EAX,DWORD PTR DS:[EBX+A81047]
/*AE077F*/ JB SHORT 00AE07A6
/*AE0781*/ PUSH 40
/*AE0783*/ PUSH 1000
/*AE0788*/ PUSH 1000
/*AE078D*/ PUSH 0
/*AE078F*/ CALL DWORD PTR DS:[EBX+A8103F]
/*AE0795*/ MOV DWORD PTR DS:[EBX+A81043],EAX
------------------------------------------------------------------------
现在在地址 AE0768 上设个硬件执行断点,重新载入程序,F9,断在刚才设的硬件

断点上。删掉这个硬件断点,现在把地址 AE0768 处的 JNZ 改成 JMP,F9,会断在

我们在 OEP 设的硬件断点上。这时我们打开 ImportREC,选 目标进程,OEP 填

0007B9BA,点"自动查找 IAT",会获得输入表。现在看看还有没有无效的,嗯,

还有一个:

004962D0 7C80B4CF kernel32.GetModuleFileNameA
004962D4 00BD05C4 ;这个无效
004962D8 7C8310F2 kernel32.GlobalMemoryStatus

通过上面的三个函数,可以知道这个无效的函数应该是 kernel32.dll 中的。这样

我们就在无效函数的前一个地址设内存写入断点,看看壳是怎么处理这个加密函数

的。

在地址 AE0768 上设个硬件执行断点,现在我们 CTR+F2 重来。F9运行,硬件断点

断下后删掉 AE0768 的硬件断点,把地址 AE0768 处的 JNZ 改成 JMP。

数据窗口中在地址 004962D0 上设内存写入断点,在代码窗口中看到要往 004962D0

写内容时开始分析,发现还是断在老地方:


/*AE1B70*/ MOV DWORD PTR DS:[ESI],EAX ; 断在这里
/*AE1B72*/ MOV DWORD PTR DS:[EDX],EAX
/*AE1B74*/ ADD EDX,4
/*AE1B77*/ ADD ESI,4
/*AE1B7A*/ JMP SHORT 00AE1B28
/*AE1B7C*/ XOR EAX,EAX
/*AE1B7E*/ POP ESI
/*AE1B7F*/ POP EDI
/*AE1B80*/ POP EBX
/*AE1B81*/ LEAVE
/*AE1B82*/ RET 10

前面我们分析时地址 AE0756 处的那个 CALL 我们没分析,只是猜测应该是个

GetProcAddress 的功能。这次我们进去看看:

/*AE0756*/ CALL DWORD PTR DS:[EBX+A8102F]

进去后是call都F7进去看看

在这 /*AE05A9*/ CALL DWORD PTR DS:[EBX+A81029] F7

00AE0416 75 06 JNZ SHORT 00BD041E

; 这里改成 JMP 就可以跳过加密

两个要改的位置都知道了,现在就可以获取完整的输入表了。从上面的分析内容,

应该可以总结一下对于加密的输入表,在相应的加密输入表地址处设内存写入断点

,能较快的找到加密的位置。
------------------------------------------------------------------------

脱完壳修复优化后加载会出现一个实时错误,原因是第二个区段 .rdata 的特征值

应为 40000040。
出现R6002~浮点未加载 也可以用upx203压缩下就OK了.

其实这个不是为了脱壳而脱壳.
只是说明一种避开加密输入表的方法,学习一种方法比学习脱一个壳作用大的多.
这个完全是学习CCDebuger的思路.





[ Edited by kyo327 at 2009-04-10 2:54:41 PM ]

Comments RSS Feed http://www.kyospace.com/feedcomm.asp?logID=312

Quote kyo327 Posted at 2009-04-10 12:12:57 AM
其实脱壳也挺有意思的.


手脱原版的Poison Ivy 2.3.2.exe
听说国外这个马功能很强大。想玩玩试试,随手用PEID查看发现加的有壳。
不过没查出来是什么壳,只看到最后一个区段名是GPPE。
不管了,给它脱了!!
--------------------------邪恶的分割线----------------------------
1.查找OEP
可以用老Y写的GUnPacker0.50来找

当然这个也可以直接用esp定律。F9两次就可以到
/*561A9C*/ PUSH EBP
/*561A9D*/ MOV EBP,ESP
/*561A9F*/ MOV ECX,4
/*561AA4*/ PUSH 0
/*561AA6*/ PUSH 0
/*561AA8*/ DEC ECX
/*561AA9*/ DB 75
/*561AAA*/ DB F9

0x00561A9C就是OEP了。

--------------------------华丽的分割线----------------------------
2.去校验。
找到OEP后可以先he 561A9C作备用。
这时DUMP后再ImportREC修复IAT时发现有好多无效的。
可能有些IAT被加密了。
顺手找一个CALL
/*561D11*/ CALL Poison_I.0040720C
enter敲进去看下
/*40720C*/ JMP DWORD PTR DS:[5684CC]
然后在命令窗口dd 5684CC

00568310 7C812FC9 kernel32.GetStdHandle
00568314 7C810B07 kernel32.GetFileSize
00568318 7C810EE1 kernel32.GetFileType
0056831C 7C801A28 kernel32.createFileA
00568320 7C809BD7 kernel32.CloseHandle
00568324 00000000
00568328 001534E0 ;加密了
0056832C 001534E5
00568330 001534EA
00568334 001534EF
00568338 00000000
0056833C 001534F4
00568340 001534F9
00568344 001534FE

发现确实有一些IAT被加密了

重新来过吧
在 00568328下内存写入断点。

当发现 变为00568328 77D311DB user32.GetKeyboardType时

注意看数据窗口
ESI=001534E0
DS:[00568328]=77D311DB (user32.GetKeyboardType)
然后F8走一下看看
发现
/*61161D*/ CMP EBX,77FFFFFF
/*611623*/ JBE SHORT Poison_I.00611633
在611623处跳转实现了就会加密。所以这里干脆把它NOP掉。

但是NOP掉后再F9执行,程序直接退出了,看来还是有校验啊。


那我在hr 611623再F9 看看到底那里校验我的。

来到这了
/*611319*/ MOV AL,BYTE PTR DS:[EDI]
/*61131B*/ MUL EDX
/*61131D*/ ADD EBX,EAX
/*61131F*/ INC EDX
/*611320*/ INC EDI
/*611321*/ LOOPD SHORT Poison_I.00611319
/*611323*/ XCHG EAX,EBX

MOV AL,BYTE PTR DS:[EDI]就是取那里的代码然后放到了eax里。
把这个循环走出来再看下,
/*611695*/ JMP SHORT Poison_I.00611698
/*611697*/ ???
/*611698*/ MOV EBX,DWORD PTR SS:[EBP+40425E]
/*61169E*/ XOR EAX,EBX ;这里是比较是否相等的,关键。
/*6116A0*/ JE SHORT Poison_I.006116AA

比完以后没跳转,那我把它改成jmp 006116AA应该就OK了。
把其他断点全删除,只留到OEP那个,再F9后就飞向光明之颠。

避开了IAT加密,这次再DUMP,修复IAT就没问题了。不过在DUMP时要先修正下镜象大小。注意OEP=561A9C-40000=161a9c。


Quote kyo327 Posted at 2009-04-10 2:46:23 PM
FSG 2.0 -> bart/xt

也算是压缩壳吧,不过比其他的稍微麻烦点.

入口点是这样的

/*400154*/ XCHG DWORD PTR DS:[505AE0],ESP
/*40015A*/ POPAD
/*40015B*/ XCHG EAX,ESP
/*40015C*/ PUSH EBP
/*40015D*/ MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
/*40015E*/ MOV DH,80
/*400160*/ CALL DWORD PTR DS:[EBX]
/*400162*/ JNB SHORT KeyGen.0040015D
/*400164*/ XOR ECX,ECX
/*400166*/ CALL DWORD PTR DS:[EBX]
/*400168*/ JNB SHORT KeyGen.00400180
/*40016A*/ XOR EAX,EAX
/*40016C*/ CALL DWORD PTR DS:[EBX]
/*40016E*/ JNB SHORT KeyGen.0040018F
/*400170*/ MOV DH,80
/*400172*/ INC ECX
/*400173*/ MOV AL,10
/*400175*/ CALL DWORD PTR DS:[EBX]
/*400177*/ ADC AL,AL
/*400179*/ JNB SHORT KeyGen.00400175
/*40017B*/ JNZ SHORT KeyGen.004001B7
/*40017D*/ STOS BYTE PTR ES:[EDI]
/*40017E*/ JMP SHORT KeyGen.00400160
/*400180*/ CALL DWORD PTR DS:[EBX+8]
/*400183*/ ADD DH,DH
/*400185*/ SBB ECX,1
/*400188*/ JNZ SHORT KeyGen.00400198
/*40018A*/ CALL DWORD PTR DS:[EBX+4]
/*40018D*/ JMP SHORT KeyGen.004001B3
/*40018F*/ LODS BYTE PTR DS:[ESI]
/*400190*/ SHR EAX,1
/*400192*/ JE SHORT KeyGen.004001C1
/*400194*/ ADC ECX,ECX
/*400196*/ JMP SHORT KeyGen.004001B0
/*400198*/ XCHG EAX,ECX
/*400199*/ DEC EAX
/*40019A*/ SHL EAX,8
/*40019D*/ LODS BYTE PTR DS:[ESI]
/*40019E*/ CALL DWORD PTR DS:[EBX+4]
/*4001A1*/ CMP EAX,DWORD PTR DS:[EBX-8]
/*4001A4*/ JNB SHORT KeyGen.004001B0
/*4001A6*/ CMP AH,5
/*4001A9*/ JNB SHORT KeyGen.004001B1
/*4001AB*/ CMP EAX,7F
/*4001AE*/ JA SHORT KeyGen.004001B2
/*4001B0*/ INC ECX
/*4001B1*/ INC ECX
/*4001B2*/ XCHG EAX,EBP
/*4001B3*/ MOV EAX,EBP
/*4001B5*/ MOV DH,0
/*4001B7*/ PUSH ESI
/*4001B8*/ MOV ESI,EDI
/*4001BA*/ SUB ESI,EAX
/*4001BC*/ REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
/*4001BE*/ POP ESI
/*4001BF*/ JMP SHORT KeyGen.00400160
/*4001C1*/ POP ESI
/*4001C2*/ LODS DWORD PTR DS:[ESI]
/*4001C3*/ XCHG EAX,EDI
/*4001C4*/ LODS DWORD PTR DS:[ESI]
/*4001C5*/ PUSH EAX
/*4001C6*/ CALL DWORD PTR DS:[EBX+10]
/*4001C9*/ XCHG EAX,EBP
/*4001CA*/ MOV EAX,DWORD PTR DS:[EDI]
/*4001CC*/ INC EAX
/*4001CD*/ JS SHORT KeyGen.004001C2
/*4001CF*/ JNZ SHORT KeyGen.004001D4
/*4001D1*/ JMP DWORD PTR DS:[EBX+C]
/*4001D4*/ PUSH EAX
/*4001D5*/ PUSH EBP
/*4001D6*/ CALL DWORD PTR DS:[EBX+14]
/*4001D9*/ STOS DWORD PTR ES:[EDI]
/*4001DA*/ JMP SHORT KeyGen.004001CA
/*4001DC*/ XOR ECX,ECX
/*4001DE*/ INC ECX
/*4001DF*/ CALL DWORD PTR DS:[EBX]
/*4001E1*/ ADC ECX,ECX
/*4001E3*/ CALL DWORD PTR DS:[EBX]
/*4001E5*/ JB SHORT KeyGen.004001DF
/*4001E7*/ RET
/*4001E8*/ ADD DL,DL
/*4001EA*/ JNZ SHORT KeyGen.004001F1
/*4001EC*/ MOV DL,BYTE PTR DS:[ESI]
/*4001EE*/ INC ESI
/*4001EF*/ ADC DL,DL
/*4001F1*/ RET

这个用esp定律好象不行.
不过比较短,可以单步慢慢跟.
跟过两遍后发现直接f4到这
/*4001D1*/ JMP DWORD PTR DS:[EBX+C]
再单步一下就到oep了.
这时看下iat

0040C09C 7C80CD27 kernel32.SetHandleCount
0040C0A0 7C812FC9 kernel32.GetStdHandle
0040C0A4 7C810EE1 kernel32.GetFileType
0040C0A8 7C94ABA5 ntdll.RtlUnwind
0040C0AC 7C810E17 kernel32.WriteFile
0040C0B0 7C80AE30 kernel32.GetProcAddress
0040C0B4 7C812A99 kernel32.RaiseException
0040C0B8 7C812F06 kernel32.GetCPInfo
0040C0BC 7C8099A5 kernel32.GetACP
0040C0C0 7FFFFFFF
0040C0C4 77D24A4E user32.EndDialog
0040C0C8 77D507EA user32.MessageBoxA
0040C0CC 77D2E8F6 user32.LoadIconA
0040C0D0 77D2F3C2 user32.SendMessageA
0040C0D4 77D3B144 user32.DialogBoxParamA
0040C0D8 77D6B05E user32.GetDlgItemTextA
0040C0DC 7FFFFFFF
0040C0E0 76B15A4A winmm.waveOutWrite
0040C0E4 76B159D9 winmm.waveOutPrepareHeader
0040C0E8 76B15201 winmm.waveOutOpen
0040C0EC 76B15726 winmm.waveOutClose

每组iat中间间隔是7fffffff而不是00000000
这个好说 右键--->二进制填充为0就ok了
这时再dump和修复iat就能运行了.

Quote kyo327 Posted at 2009-04-12 9:55:59 PM
脱一个古董的壳。

国产的 好象名字是 仙剑1.003
用PEID查 有个区段是XJ

OEP好找。 2次内存断点即可。
我这里是40169d。he 40169d
DUMP后修复有问题。
找了个无效指针4050ac记录下。

重新载入
dd 4050ac 下内存写入断点。
发现在/*40E955*/ MOV DWORD PTR DS:[EDX],ESI
给覆盖了。
往上看 发现这个地方跳转实现的话 就覆盖。
/*40E93A*/ JBE SHORT CrackM.0040E94A

NOP掉吧
再重新来过,
he 40e93a

在到达OEP后会退出,看来有校验。

hr 40e93a

这时发现校验在这里
/*40E9D6*/ JE SHORT CrackM.0040E9E0
把它jmp就过了校验。
DUMP时记得修正一下境象大小。
修复完IAT 再打开程序 就能运行了。看来这个壳还算简单。

我感觉他的加密IAT和校验有点象yoda's Protector里面的代码啊。


Post Comment
Topic Locked or You don't have the Permission. No Comment Allowed.
Here is kyo's blog © 2004-2005 
Processed in 0.093750 second(s)