【文章标题】: 耐心的体验——手脱SVK1.43加壳记事本(两种方法)
【文章作者】: zyqex
【软件名称】: 加壳NotePad98
【下载地址】: 附件
【加壳方式】: SVK1.43
【使用工具】: ollyice
【操作平台】: xp
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
【前语】
唔~又是记事本!!呵呵,没办法,水平菜,最近忙着找工作,好歹脱了四五天,又晾了一个周, 还是拿出来给部分人
分享一下(高手就飘过吧)。我将用两种方法演示:1.常规方法 2.以壳解壳
【Begin】
方法一:
一.先大致介绍一下壳用到的技术:
1.加密iat
2.stolen code (oep 处)
3.replace code (mov 类的)
4.还有一个替换api的,不知该归为什么(我首次接触stolen code等概念,失误之处敬请诸位赐教)
二.
1.peid查看,SVKP 1.3x -> Pavol Cerven(其实是1.43,我用论坛里的那个svk1.43的版本)
od 载入,开始的时候提示打开文件错误,不用管点确定就是,Alt+M 看看,只看得到一个区段
(至少在我的机器上是这样)。
地址=00400000
大小=0002D000 (184320.)
属主=NOTEPAD_ 00400000 (自身)
区段=
包含=PE 文件头
类型=Imag 01001002
访问=R
初始访问=RWE
2.设置一下od:
olly advance1.26 beta 12插件里勾选IsDebuggerPresent及Anti-RdTSC下的Enable
异常设置,只勾选“忽略在kernel32中的内存访问异常”(忽略所有异常,按下面的做法我的od会死,所以就麻烦一点吧)
下面的跟chasgone《某外挂 SVKP 1.4x -> Pavol Cerven脱壳》的部分类似,我就直接引用一下了(呵呵,应该不会犯错吧)
【引用】
搞定iat //避过IAT加密的处理,这样修复时就容易多了
od载入 下断点bp GetModuleHandleA+5 //到达处理IAT的地方,随意跟踪过壳的不会不知道
shift+f9断下,取消断点,ctrl+f9返回主程序。
ctrl+f搜索特征码
cmp dword ptr ds:[ebx],251097CC
把这下面的所有的比较和跳转全部nop掉
然后在搜索特征码
mov dword ptr ds:[edi],eax //此处处理甚妙,改过后iat里写入的直接就是调用的函数的地址了
popad
改成:
popad
mov dword ptr ds:[edi],eax
【引用结束】
接着上面
0B755B4F 8907 mov dword ptr [edi], eax //即上面要修改的地方
0B755B51 61 popad
0B755B52 8385 43010200 0>add dword ptr [ebp+20143], 4
0B755B59 ^ E9 ADFBFFFF jmp 0B75570B
0B755B5E 83C6 14 add esi, 14
0B755B61 8B95 871C0300 mov edx, dword ptr [ebp+31C87]
0B755B67 C685 32550200 0>mov byte ptr [ebp+25532], 0
0B755B6E C685 33550200 0>mov byte ptr [ebp+25533], 0
0B755B75 ^ E9 9DFAFFFF jmp 0B755617
0B755B7A 33C0 xor eax, eax
0B755B7C 64:8F00 pop dword ptr fs:[eax]
0B755B7F 83C4 04 add esp, 4
0B755B82 C3 retn //在这里下断(这时iat就处理完了,下断后F9运行)
断下后,取消0B755B82处断点。
接着Alt+M在那个主程序唯一的区段上F2下断,F9运行
断在:
0013FF17 8B89 78010000 mov ecx, dword ptr [ecx+178]
0013FF1D 8BAD 42210000 mov ebp, dword ptr [ebp+2142]
f8步过0013FF17处后(不步过待会下断运行后会原地停留),Alt+M在那个主程序唯一的区段上F2下断,F9运行
断在:
0B7DF5F4 FF15 E4634000 call dword ptr [4063E4] //其实这两句是被偷的完整的两句代码
0B7DF5FA 89C6 mov esi, eax
F7步进一下(F8或F9就跑飞),Alt+M在那个主程序唯一的区段上F2下断,F9运行
断在:
004010DD 3C 22 cmp al, 22 //foep 处
004010DF 75 1B jnz short 004010FC
004010E1 56 push esi
004010E2 FF15 AC644000 call dword ptr [4064AC]
很明显的一个大跨段的跳跃了^_^
向上翻看:
004010C7 000D 0A000090 add byte ptr [9000000A], cl
004010CD 90 nop
004010CE 90 nop
004010CF 90 nop
004010D0 90 nop
004010D1 90 nop
004010D2 90 nop
004010D3 90 nop
004010D4 90 nop
004010D5 90 nop
004010D6 90 nop
004010D7 90 nop
004010D8 90 nop
004010D9 90 nop
004010DA 90 nop
004010DB 90 nop
004010DC 90 nop
004010CC--004010DC 被偷了。
拿起importrec 填oep=10dd,自动搜索iat 全部有效 保存为树文件 后面有用
好了,现在我们来搞定stolen code
对于sloen code 的处理有(又一篇文章里的引用^_^)
【引用】
找回抽掉的代码的方法一般有:
1、TC跟踪得到
2、tDasm老兄介绍的还原stolen code的分析法
3、FLY老兄介绍的cdi猜测法,这个我还是未明白
4、blowfish老兄介绍的建立stack法,这个方法非常好。
【引用结束】
关于2:看雪精华里搜tDasm相关文章吧,没搜到合适的
关于3:很遗憾我没搜索到(发了个求助帖,好像没人知道,知道的补充一下吧。)
关于4:看雪精华6里搜《重建stolen bytes的一般方法》(建议去好好的看看,非常不错的一篇文章)
----------------------------------------------------------------------------------------------
下面我大致介绍一下我自己找的过程中用的方法(可能有些愚笨,见谅!),上面的1、2、4的混用。
先到foep 后记下寄存器的值(我们可以注意到foep开始以后的代码里用到了寄存器eax,esi,
所以在跟踪stolen code 时要注意这里个寄存器的变化。),以及堆栈内ebp 到esp的内容。
先看看代码的入口特征,vc的,大致的跟踪一遍,看程序在什么地方跳向foep处,这个地方以上的一些代码一般就是
模拟被偷代码的功能部分了,在跟踪过程中我们注意什么时候ebp变为到foep后ebp 的值,注意寄存器的变化,我自己
琢磨的tc 跟踪法应该是这样使用:重载程序,按二的步骤到达地址0B755B82处,然后在foep(4010dd)处下断,Ctrl+F11
(步进跟踪),停下后看记录。
引用blowfish一句话:“重建stolen bytes的目的,是为了让局部变量、全局变量、寄存器的初值在执行到fake OEP时
和未加壳前的保持一致,并非要精确还原这些stolen的指令,只要功能相同即可。 ” 加壳程序要恢复被偷处的功能也
是同样的道理。(不清处见谅,水平太菜了!亲自跟上几遍也许就明白了^_^)
依照这样一个思路,跟踪到下面的代码(只列出了关键部分):
0B7E0095 主 sub esp, 44 FL=A, ESP=0013FF7C //被偷代码
0B7E0098 主 push eax ESP=0013FF78 //被偷代码(注意到esp的值)
0B7E0099 主 add esi, 8B8A3F66 FL=S, ESI=8B9DFB7A
0B7E009F 主 jmp 0B7DE2B3
……
0B7DF5ED 主 sub dword ptr [esp], eax FL=P
0B7DF5F0 主 pop esi ESP=0013FF74, ESI=0013BC14
0B7DF5F1 主 mov edi, edi
0B7DF5F3 主 pop eax EAX=0B7DE159, ESP=0013FF78
0B7DF5F4 主 call dword ptr [4063E4] ESP=0013FF74 //被偷代码
0B7DF5FA 主 mov esi, eax ESI=00152398 //被偷代码(注意esi值)
0B7DF5FC 主 mov al, byte ptr [eax] EAX=00152322 //被偷代码
0B7DF5FE 主 push 74B5D177 ESP=0013FF74
0B7DF603 主 add eax, 8B8A3F66 FL=PS, EAX=8B9F6288
0B7DF608 主 push eax ESP=0013FF70
0B7DF609 主 mov eax, 8B8A3F66 EAX=8B8A3F66
0B7DF60E 主 sub dword ptr [esp], eax FL=P
0B7DF611 主 jmp 0B7DE83A
0B7DE83A 主 mov eax, dword ptr [esp] EAX=00152322
0B7DE83D 主 mov eax, 8B8A3F66 EAX=8B8A3F66
0B7DE842 主 add dword ptr [esp+4], eax FL=CP
0B7DE846 主 pop eax EAX=00152322, ESP=0013FF74
0B7DE847 主 add esp, 4 FL=P, ESP=0013FF78
0B7DE84A 主 jmp dword ptr [esp-4] //这里跳向foep
c程序的入口为push ebp mov ebp,esp 这两句在上面的代码里被变形了,罗列一下stolen code:
push ebp
mov ebp,esp
sub esp,44
push eax
call dword ptr ds:[4063E4]
mov esi,eax
mov al,byte ptr ds:[eax]
补到00401010cc处并保存到可执行文件,然后重新用od打开,运行到oep处后,用LoadPe dump 程序,改
入口为004010CC。
当然使用blowfish介绍的建立stack法会容易一些,有兴趣的可以自己试试。
***************************************************************************************
三.修复被替换的api
上面的步骤完成后,如果使用步骤二保存的树文件修复iat,运行程序没反应。用od 加入看看,很多调用MessageBox
和ExitProcess的地方。
现在我们知道了了foep--004010dd,od 载入“加壳记事本”, bp GetModuleHandleA+5 断下后直接在foep处下断。
断下后,我们看到程序里很多call dword ptr [4064AC]和call dword ptr [4063A0]。
随便找一个调用的地方f7进去看看(可以在下面的语句设断,然后Ctr+F11),查看记录:
会发现如下的关键地方:
*******对于call dword ptr [4064AC]********
00F2A078 9C pushfd //004064ac
00F2A079 55 push ebp
00F2A07A E8 00000000 call 00F2A07F
--------------------------------------------------------------------
0013FE64 833C31 00 cmp dword ptr [ecx+esi], 0 //关键循环1
0013FE68 74 64 je short 0013FECE
0013FE6A 50 push eax
0013FE6B 807D 36 01 cmp byte ptr [ebp+36], 1
0013FE6F 75 0C jnz short 0013FE7D
0013FE71 2B85 3E010000 sub eax, dword ptr [ebp+13E]
0013FE77 0385 42010000 add eax, dword ptr [ebp+142]
0013FE7D 390431 cmp dword ptr [ecx+esi], eax ; NOTEPAD.004010E4
0013FE80 58 pop eax
0013FE81 74 07 je short 0013FE8A
0013FE83 83E9 09 sub ecx, 9
0013FE86 74 46 je short 0013FECE
0013FE88 ^ EB DA jmp short 0013FE64
eax=004010E4 (NOTEPAD.004010E4)
ds:[00F2CB14]=0040353F (NOTEPAD.0040353F)
------------------------------------------------------------------------
0013FED9 833C31 00 cmp dword ptr [ecx+esi], 0 ; 关键循环2
0013FEDD 74 48 je short 0013FF27
0013FEDF 50 push eax
0013FEE0 807D 36 01 cmp byte ptr [ebp+36], 1
0013FEE4 75 0C jnz short 0013FEF2
0013FEE6 2B85 3E010000 sub eax, dword ptr [ebp+13E]
0013FEEC 0385 42010000 add eax, dword ptr [ebp+142]
0013FEF2 390431 cmp dword ptr [ecx+esi], eax //eax存储的是需要被修改的地方的地址
0013FEF5 58 pop eax
0013FEF6 74 07 je short 0013FEFF
0013FEF8 83E9 08 sub ecx, 8
0013FEFB 74 2A je short 0013FF27
0013FEFD ^ EB DA jmp short 0013FED9
--------------------------------------
0013FF24 9D popfd
0013FF25 - FF20 jmp dword ptr [eax] 跳向正确函数
其实上面代码中两个循环的 ecx+esi 指向两张表(跟踪时可到数据窗口看看,不清楚的建议看看
《某外挂 SVKP 1.4x -> Pavol Cerven脱壳》chasgone 的文章,就不细说了,大同小异,处理方法稍有区别)。
表1:
0B7DCAFD 00 00 00 00 00 00 00 00 00 35 DC 64 40 00 (70 37 .........5躣@.p7
0B7DCB0D 40 00) 35 DC 64 40 00 (3F 35 40 00) 3D E4 64 40 00 @.5躣@.?5@.=鋎@.
0B7DCB1D (5D 2E 40 00) 00 00 00 00 00 43 3A 5C 44 6F 63 75 ].@......C:\Docu
()里的表示壳会修改的地址,其前面的四个字节就是修改后的值
比如说:E4 64 40 00 (5D 2E 40 00)
这里()里的地址00402e5d处的内容将会被修改为 004064e4(这个值指向被调用的正确的函数地址)
即:待修复的地址为[0B7DCB1D-9*n],修改为的值是[0B7DCB1D-9*n-4],n=0,1,2,3……
表2:
0B7DB72E F4 64 40 00 鬱@.
0B7DB73E 8F 4F 40 00 F4 64 40 00 1A 4F 40 00 F4 64 40 00 廜@.鬱@.O@.鬱@.
0B7DB74E 3C 4E 40 00 F4 64 40 00 A7 4D 40 00 F4 64 40 00 <N@.鬱@.@.鬱@.
0B7DB75E E1 4C 40 00 F4 64 40 00 56 4C 40 00 DC 64 40 00 酟@.鬱@.VL@.躣@.
0B7DB76E 24 4C 40 00 DC 64 40 00 0D 4C 40 00 DC 64 40 00 $L@.躣@..L@.躣@.
0B7DB77E FB 4B 40 00 DC 64 40 00 E8 4B 40 00 DC 64 40 00 鸎@.躣@.鐺@.躣@.
……
0B7DBCEE 41 12 40 00 EC 64 40 00 27 12 40 00 F4 64 40 00 A@.靌@.'@.鬱@.
0B7DBCFE 1B 11 40 00 F4 64 40 00 03 11 40 00 F4 64 40 00 @.鬱@.@.鬱@.
0B7DBD0E E4 10 40 00 ?@..
表2和表1类似,比如说
0B7DBCFE 1B 11 40 00 F4 64 40 00 03 11 40 00 F4 64 40 00 @.鬱@.@.鬱@.
0B7DBD0E E4 10 40 00
0B7DBD0E 地址处是要修复的地址,0B7DBD0A地址处是修改为的值,接下来0B7DBD06处的值又是待修复的地址……
即:待修复的地址为[0B7DBD0E-4*2n],修改为的值是[0B7DBD0E-4*(2n+1)],n=0,1,2,3……
另外对于call dword ptr [4063A0]的处理也是一样的,不多说了,直接给出另两个表
表3:
0B7BA122 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0B7BA132 3D BC 63 40 00 (3A 34 40 00) 1D A4 63 40 00 (F0 2F =糲@.:4@.@.?
0B7BA142 40 00) 35 D8 63 40 00 (E0 2F 40 00) 35 D0 63 40 00 @.5豤@.?@.5衏@.
0B7BA152 (5A 19 40 00) 00 00 00 00 9C 55 E8 00 00 00 00 5D Z@.....淯?...]
表4
0B7B8EE3 00 00 00 00 00 00 00 00 00 00 00 00 A4 63 40 00 ............@.
0B7B8EF3 62 4F 40 00 A4 63 40 00 49 4F 40 00 A4 63 40 00 bO@.@.IO@.@.
0B7B8F03 F0 4E 40 00 A4 63 40 00 77 4E 40 00 A4 63 40 00 餘@.@.wN@.@.
0B7B8F13 5D 4E 40 00 A4 63 40 00 12 4E 40 00 94 63 40 00 ]N@.@.N@.攃@.
……
0B7B9303 4C 16 40 00 D0 63 40 00 A7 14 40 00 D0 63 40 00 L@.衏@.?@.衏@.
0B7B9313 66 14 40 00 D8 63 40 00 0C 14 40 00 DC 63 40 00 f@.豤@..@.躢@.
0B7B9323 BA 11 40 00 D4 63 40 00 8E 11 40 00 D4 63 40 00 ?@.詂@.?@.詂@.
0B7B9333 79 11 40 00 9C 63 40 00 4E 11 40 00 98 63 40 00 y@.渃@.N@.榗@.
0B7B9343 33 11 40 00
**********************************************************************
这样很容易就可以写出手动的修复代码来:
0B800000 9C pushfd //表2
0B800001 60 pushad
0B800002 BE 3EB77D0B mov esi, 0B7DB73E
0B800007 8B06 mov eax, dword ptr [esi]
0B800009 8B5E FC mov ebx, dword ptr [esi-4]
0B80000C 8918 mov dword ptr [eax], ebx
0B80000E 83C6 08 add esi, 8
0B800011 81FE 0EBD7D0B cmp esi, 0B7DBD0E
0B800017 ^ 7E EE jle short 0B800007 //此处跳转地址以分配的为准
----------------------------------------------------------
0B800019 BE F38E7B0B mov esi, 0B7B8EF3 //表1
0B80001E 8B06 mov eax, dword ptr [esi]
0B800020 8B5E FC mov ebx, dword ptr [esi-4]
0B800023 8918 mov dword ptr [eax], ebx
0B800025 83C6 08 add esi, 8
0B800028 81FE 43937B0B cmp esi, 0B7B9343
0B80002E ^ 7E EE jle short 0B80001E //此处跳转地址以分配的为准
-----------------------------------------------------------
0B800030 BE 0BCB7D0B mov esi, 0B7DCB0B
0B800035 8B06 mov eax, dword ptr [esi]
0B800037 8B5E FC mov ebx, dword ptr [esi-4]
0B80003A 8918 mov dword ptr [eax], ebx
0B80003C 83C6 09 add esi, 9
0B80003F 81FE 1DCB7D0B cmp esi, 0B7DCB1D
0B800045 ^ 7E EE jle short 0B800035 //此处跳转地址以分配的为准
-----------------------------------------------------------
0B800047 BE 37A17B0B mov esi, 0B7BA137
0B80004C 8B06 mov eax, dword ptr [esi]
0B80004E 8B5E FC mov ebx, dword ptr [esi-4]
0B800051 8918 mov dword ptr [eax], ebx
0B800053 83C6 09 add esi, 9
0B800056 81FE 52A17B0B cmp esi, 0B7BA152
0B80005C - 0F8E EAFF2FFD jle 0B80004C //此处跳转地址以分配的为准
0B800062 61 popad
0B800063 9D popfd
上面的代码就是修复代码,修复的时候用插件分配一块内存,然后写入以上代码,我的是0B800000开始。
打开步骤二dump 的程序,分配一块内存,写入上面的代码,然后新建在代码开始处eip,执行完后让它跳回oep.
接着再dump 一下程序,运行dump 的程序,用 import rec 选择进程(注意是dump后程序的进程),然后载入
步骤二保存的的树文件,填入 oep 4010cc,fix dump.
四.修复replace code (mov 类)
到了这里程序还是不能运行,其实是因为表1里地址处的mov 指令被替换成了call 指令。发生的地点就是表1、3
()里需要修复的地址-2的地方。罗列一下,即地址:
0040343a-2,00402ff0-2,00402fe0-2,0040195a-2,00402E5D-2,0040353F-2,00403770-2
修复方法:
比如说:
0040343a-2处,od载入步骤三得到的程序,然后Ctrl+G输入0040343a-2到:
00403438 FF15 BC634000 call dword ptr ds:[<&kernel32.lstrcat>] ; kernel32.lstrcatA
0040343E |. FFD7 call edi
在00403438处按空格键(Space),出现call dword ptr ds:[4063BC],将其改为mov edi,dword ptr ds:[4063BC]
其他地方的处理方法类似,mov 后面的寄存器为上面Ctrl+G后地址下面的call 所用的寄存器,就像上面的
call dword ptr ds:[<&kernel32.lstrcat>]下面的call edi。
就7个地方,手动修改不是很麻烦。
修改完后保存到可执行文件。
运行程序,这样就OK了。^_^ ^_^
最后优化一下,我优化的结果是和原记事本一样大,这个就不说了,请参看其他文章或者《加密与解密3》有关章节。
-------------------------------------------------------------------------------------------------------
方法二:(待续,写了快一下午,休息一下^_^)
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2009年04月13日 19:05:46
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)