脱壳基础知识入门
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
脱壳基础知识入门
现在加解密发展己形成2个分支了,一个就是传统的算法,另一个就是加密壳。
越来越多的软件采用了密码学相关算法,现在要做出一个软件注册机己不象前几年那么容易,这就要求解密者必须要有一定的数学功底和密码学知识,而这些在短时间内是不容易掌握的。
除了密码学的应用,越来越多的软件加壳了,因此要求解密者必须掌握一些脱壳技术,这就使得壳成了解密必须迈过的一个门槛。
壳发展到今天,强度越来越高了,将许多人挡在门外,使得大家望壳兴叹。
另外,论坛现在两极分化比较严重,高手讨论的脱壳技术新手看不懂,很多人想学脱壳,但看到壳这么难,只好放弃了,造成新手与高手间一个断档,为了鼓励更多新人加入脱壳的行列,很有必要将壳有关知识总结一下。
主页提供的教学确实有点过时了,己到非更新不可了。
相对于密码学算法,脱壳并不难,只要肯花时间,短期内还是比较容易取得成绩的。
第一课PE格式
要想学脱壳,第一步就得掌握PE格式,PE是Portable Executable File Format(可移植的执行体)简写,它是目前Windows平台上的主流可执行文件格式。
Microsoft Visual C++提供的WINNT.H里有PE数据结构的完整定义。
推荐文档:
ah007翻译的“PE文件格式”1.9版
qduwg翻译的PE文件格式
Iczelion's的PE文件格式
PE结构各字段偏移参考
学习PE格式的方法是自己先准备一个十六进制工具,如HexWorkshop,WinHex,用这些工具打开一个EXE文件对照着学。
强烈推荐你用Stud_PE v.2.2.0.5这款工具辅助学习PE格式。
PE格式学习的重点是在输入表(Import Table)这块。
Stud_PE工具界面:
PE结构图:
第二课SEH技术
结构化异常处理(Structured Exception Handling,SEH)是Windows操作系统处理程序错误或异常的技术。
SEH是Windows操作系统的一种系统机制,与特定的程序设计语言无关。
外壳程序里大量地使用了SEH,如果不了解SEH,将会使你跟踪十分困难。
SEH in ASM研究(一)by hume
SEH in ASM研究(二)by hume
Structured Exception Handling
加密与解密二版菜鸟学习笔记(2)-SEH结构化异常处理by ytcswb
由于Ollydbg对SEH处理异常灵活,因此脱壳用Ollydbg会大大提高效率。
附CONTEXT结构环境:
代码:
typedef struct_CONTEXT{
/*000*/DWORD ContextFlags;
/*004*/DWORD Dr0;
/*008*/DWORD Dr1;
/*00C*/DWORD Dr2;
/*010*/DWORD Dr3;
/*014*/DWORD Dr6;
/*018*/DWORD Dr7;
/*01C*/FLOATING_SAVE_AREA FloatSave;
/*08C*/DWORD SegGs;
/*090*/DWORD SegFs;
/*094*/DWORD SegEs;
/*098*/DWORD SegDs;
/*09C*/DWORD Edi;
/*0A0*/DWORD Esi;
/*0A4*/DWORD Ebx;
/*0A8*/DWORD Edx;
/*0AC*/DWORD Ecx;
/*0B0*/DWORD Eax;
/*0B4*/DWORD Ebp;
/*0B8*/DWORD Eip;
/*0BC*/DWORD SegCs;
/*0C0*/DWORD EFlags;
/*0C4*/DWORD Esp;
/*0C8*/DWORD SegSs;
/*0CC*/BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
/*2CC*/}CONTEXT;
第三课认识壳
1.什么是壳?
在一些计算机软件里也有一段专门负责保护软件不被非法修改或反编译的程序。
它们一般都是先于程序运行,拿到控制权,然后完成它们保护软件的任务。
由于这段程序和自然界的壳在功能上有很多相同的地方,基于命名的规则,就把这样的程序称为“壳”了。
推荐文档:
一切从“壳”开始
描述壳的示意图:
2.壳的加载过程
这里谈的加壳工具不是WinZIP、WinRAR等数据压缩工具,而是谈压缩可执行文件EXE或DLL的工具。
加壳过的EXE文件是可执行文件,它可以同正常的EXE文件一样执行。
用户执行的实际上是外壳程序,这个外壳程序负责把用户原来的程序在内存中解压缩,并把控制权交还给解开后的真正程序,这一切工作都是在内存中运行的,整个过程对用户是透明的。
壳和病毒在某些方面比较类似,都需要比原程序代码更早的获得控制权。
壳修改了原程序的执行文件的组织结构,从而能够比原程序的代码提前获得控制权,并且不会影响原程序的正常运行。
这里简单说说一般壳的装载过程。
(参考了Ljtt以前写过的一篇文章)
1)获取壳自己所需要使用的API地址
如果用PE编辑工具查看加壳后的文件,会发现未加壳的文件和加壳后的文件的输入表不一样,加壳后的输入表一般所引入的DLL和API函数很少,甚至只有Kernel32.dll以及GetProcAddress这个API函数。
壳实际上还需要其他的API函数来完成它的工作,为了隐藏这些API,它一般只在壳的代码中用显式链接方式动态加载这些API函数:3个脱壳相关的重要函数介绍
2)解密原程序的各个区块(Section)的数据
壳出于保护原程序代码和数据的目的,一般都会加密原程序文件的各个区块。
在程序执行时外壳将会对这些区块数据解密,以让程序能正常运行。
壳一般按区块加密的,那么在解密时也按区块解密,并且把解密的区块数据按照区块的定义放在合适的内存位置。
如果加壳时用到了压缩技术,那么在解密之前还有一道工序,当然是解压缩。
这也是一些壳的特色之一,比如说原来的程序文件未加壳时1~2M大小,加壳后反而只有几百K。
3)重定位
文件执行时将被映像到指定内存地址中,这个初始内存地址称为基地址(ImageBase)。
当然这只是程序文件中声明的,程序运行时能够保证系统一定满足其要求吗?
对于EXE的程序文件来说,Windows系统会尽量满足。
例如某EXE文件的基地址为0x400000,而运行时Windows 系统提供给程序的基地址也同样是0x400000。
在这种情况下就不需要进行地址“重定位”了。
由于不需要对EXE
文件进行“重定位”,所以加壳软件把原程序文件中用于保存重定位信息的区块干脆也删除了,这样使得加壳后
的文件更加小巧。
有些工具提供“Wipe Reloc”的功能,其实就是这个作用。
不过对于DLL的动态链接库文件来说,Windows系统没有办法保证每一次DLL运行时提供相同的基地址。
这样“重定位”就很重要了,此时壳中也需要提供进行“重定位”的代码,否则原程序中的代码是无法正常运行起来的。
从这点来说,加壳的DLL比加壳的EXE更难修正。
4)HOOK-API
程序文件中的输入表的作用是让Windows系统在程序运行时提供API的实际地址给程序使用。
在程序的第一行代码执行之前,Windows系统就完成了这个工作。
壳一般都修改了原程序文件的输入表,然后自己模仿Windows系统的工作来填充输入表中相关的数据。
在填充过程中,外壳就可填充HOOK-API的代码的地址,这样就可间接地获得程序的控制权。
5)跳转到程序原入口点(OEP)
从这个时候起壳就把控制权交还给原程序了,一般的壳在这里会有明显的一个“分界线”。
但现在的猛壳己没这界限了,壳里有肉,肉里有壳。
3.压缩引擎
各类加壳软件,其压缩算法一般不是自己实现的,大多是调用其他的压缩引擎。
目前压缩引擎种类比较多,不同的压缩引擎有不同特点,如一些对图像压缩效果好,一些对数据压缩效果好。
而加壳软件选择压缩引擎有一个特点,在保证压缩比的条件下,压缩速度慢些关系不是太大,但解压速度一定要快,这样加了壳的EXE文件运行起来速度才不会受太大的影响。
例如下面几个压缩引擎就能满足这要求:
1.aPLib压缩引擎/,这个库对于低于64K的文件压缩效果较好,速度较快。
2.JCALG1压缩引擎,相对于aPlib,JCALG1对于大文件效果好些。
3.LZMA压缩引擎/zh-cn/sdk.html,LZMA是7-Zip程序中7z格式的默认压缩算法,压缩率很高。
第四课常见压缩壳与加密壳
加壳软件按照其加壳目的和作用,可分为两类:一是压缩(Packers),二是保护(Protectors)。
压缩这类壳主要目的是减小程序体积,如ASPacK、UPX和PECompact等。
另一类是保护程序,用上了各种反跟踪技术保护程序不被调试、脱壳等,其加壳后的体积大小不是其考虑的主要因素,如ASProtect、Armadillo、EXECryptor 等。
随着加壳技术的发展,这两类软件之间的界线越来越模糊,很多加壳软件除具有较强的压缩性能,同时也有了较强的保护性能。
1.ASPacK
主页:/
ASPack是款Win32可执行文件压缩软件,可压缩Windows32位可执行文件(.exe)以及库文件(.dll、.ocx),文件压缩比率高达40%~70%。
2.UPX
主页:/
UPX是一个以命令行方式操作的可执行文件经典免费压缩程序,压缩算法自己实现,速度极快,压缩比极高。
(开源)
3.PECompact
主页:/
PECompact同样也是一款能压缩可执行文件的工具(支持EXE、DLL、SCR、OCX等文件)。
相比同类软件,PECompact 提供了多种压缩项目的选择,用户可以根据需要确定哪些内部资源需要压缩处理。
同时,该软件还提供了加解密的插件接口功能。
4.ASProtect
主页:/
ASProtect是一款非常强大的Windows32位保护工具,这4、5年来,其一直在更新进步。
其开发者是俄国人Alexey Solodovnikov。
它拥有压缩、加密、反跟踪代码、反-反汇编代码、CRC校验和花指令等保护措施。
它使用Blowfish、Twofish、TEA等强劲的加密算法,还用RSA1024作为注册密钥生成器。
它还通过API钩子(API hooks,包括Import hooks(GPA hook)和Export hooks)与加壳的程序进行通信。
甚至用到了多态变形引擎(Polymorphic Engine)。
反Apihook代码(Anti-Apihook Code)和BPE32的多态变形引擎(BPE32的Polymorphic Engine)。
并且ASProtect为软件开发人员提供SDK,实现加密程序内外结合。
更多与壳有关的描述参考:
纵横间谁能相抗—论壳与加壳技术作者:forgot
常见加密壳官方站点
ASProtect /
ACProtect /
Armadillo /srt-news.shtml
EXECryptor /cgi-bin/anon-www.cgi//
Obsidium http://www.obsidium.de/
PESpin http://pespin.w.interia.pl/
VMProtect http://www.polytech.ural.ru/
Xtreme-Protector /xprotector/
Themida/WinLicense /downloads.php
第五课文件类型分析
拿到一个壳,第一步就是用相关工具分析一下是什么壳,然后就可心中有数地跟踪分析。
文件分析工具有PEID,FileInfo等。
1.PEiD
PEiD的GUI界面操作非常方便直观。
它的原理是利用查特征串搜索来完成识别工作的。
各种开发语言都有固定的启动代码部分,利用这点就可识别出是何种语言编编译的。
同样,不同的壳也有其特征码,利用这点就可识别是被何种壳所加密。
PEiD提供了一个扩展接口文件userdb.txt,用户可以自定义一些特征码,这样就可识别出新的文件类型。
有些外壳程序为了欺骗PEiD等文件识别软件,会伪造启动代码部分,例如将入口代码改成与Visual C++6.0所编程程序入口处类似代码,即可达到欺骗目的。
所以,文件识别工具所给出的结果只是个参考,文件是否被加壳处理过,还得跟踪分析程序代码才可得知。
可参考这个文档了解如何伪装:让侦测工具把壳识别为VC++7.0的源代码。
目前Hying的壳PE-Armor伪装能力是最强的:
PEiD分析不出类型的文件就报告是“Nothing found*”,如出现这情况一般都是未知壳或新版的壳。
下面PEiD识别出这个软件是用Asprotect1.2x加的壳。
2.FileInfo
FileInfo(简称Fi)另一款不错的文件检测工具。
Fi运行时是DOS界面,在DOS窗口中运行程序相当不便,建议采用下面的技巧:
1.用鼠标将文件拖到Fi主文件上。
2.将Fi快捷方放进Windows的SendTo文件夹里.以后要分析某文件,只需右击“发送到”功能就可打开Fi。
FileInfo升级慢,其识别库不能自定义。
而PEiD升级比较频繁,用户也可自定义特征码,因此PEiD用的比较普遍。
有时,FileInfo和PEID会报“PE Win GUI”,Win GUI就是Windows图形用户界面程序统称,表明程序可能没加壳。
但不排除也有加壳的可能性,下图是一个ExeCryptor2.2x的壳,FileInfo报“*PE Win GUI*section*??”,其不能识别出来。
识别信息中带了个问号,表明FI对给出的结果不是太肯定。
第六课寻找OEP
一般的压缩壳,如Aspack等都有专用的脱壳机。
而加密壳(如ASProtect,Armadillo)一般很少有脱壳机,必须手工脱壳。
手工脱壳一般情况是分三步:一是查找程序的真正入口点(OEP);二是抓取内存映像文件;三是输入表重建。
(当然现在的加密壳复杂些,要考虑更多的东西)
OEP是Original Entry Point缩写,即程序加壳前的真正的入口点。
外壳初始化的现场环境(各寄存器值)与原程序的现场环境是相同的。
加壳程序初始化时保存各寄存器的值,
外壳执行完毕,会恢复各寄存器内容。
其代码形式一般如下:
PUSHFD;将标志寄存器入栈保存
PUSHAD;push eax,ecx,edx,ebx,esp,ebp,esi,edi
……;外壳代码部分
POPAD;pop edi,esi,ebp,esp,ebx,edx,ecx,eax
POPFD;恢复标志寄存器
JMP OEP;
OEP:……;解压后的程序原代码
为了讲述方便,本节用UPX加壳的Win98记事本来演示。
首先用PEid查看加壳前的记事本:
PEid显示Notepad.exe程序是用Microsoft Visual C++6.0编译的,接下来用UPX来加壳,方法是开个DOS窗口,用命令upx notepad.exe。
如下图所示:
这时再用PEid查看加壳的文件,PEid会给出如下信息:UPX0.89.6-1.02/1.05-1.24->Markus&Laszlo UPX的壳可以用UPX.exe自身来脱,命令是:upx-d文件名。
一些变种的UPX壳用UPX.EXE自身脱不了,这时可以试试UPX ShellEx这款工具。
本节实例下载:notepad.upx.rar
脱壳前建议用PE工具LordPE打开目标文件查看一下区块,以尽可能地多了解一些信息,对脱壳有帮助,如下图:
1.根据跨段指令寻找OEP
推荐用Ollydbg来调试脱壳,比SoftICE和TRW2000方便多了。
运行Ollydbg,点击菜单“选项/调试设置”,将第一次暂停设在WinMain函数上。
再用Ollydbg打开实例notepad.upx.exe就可中断在外壳的入口点处了:
上图相关代码如下:
0040E8C0>60pushad//一开始Ollydbg就会中断这行,这个就是外壳的入口点,注意这个pushad指令绝大多数加壳程序在被加密的程序中加上一个或多个段,所以依据跨段的转移指令(JMP)就可找到真正的入
口点,此时就会有POPAD/POPFD指令出现。
UPX用了一次跨段的转移指令(JMP),在跳到OEP处会看到虚拟地址的值有一个突变,此时就能确定OEP了。
UPX壳比较简单,大家不必要跟踪去找这个跨段的转移指令,中断WinMain后,只需要在Ollydbg里往下翻屏,就会发现这个跨段转移指令:
上图相关代码如下:
0040EA0E61popad//注意这里的popad指令,和开始的pushad对应
0040EA0F-E9B826FFFF jmp004010CC//这里跳到OEP,将光标移到这,按F4执行到这行
这一句0040EA0F jmp004010CC就是跳到OEP的指令,执行到这,UPX外壳己将程序解压完毕,并模拟Windows 加载器的将原始程序加载到内存,004010CC就是映射到内存目标程序的入口点,此时就可抓取内存映像文件了。
2.根据堆栈平衡原理找OEP
这个堆栈平衡原理其找OEP原理这篇文档描述的比较详细:寻找真正的入口(OEP)--广义ESP定律作者:Lenus 操作方法:多数壳在运行到OEP的时候ESP=0012FFC4,这就是说程序的第一句是对0012FFC0进行写入操作,只要在0012FFC0下硬件写入断点(命令行里键入HW12FFC0),我们就能停在OEP的第二句处。
用OllyDBG重新加载实例程序notepad.upx.exe,在命令行下硬件写断点:
按F9执行程序,就会中断在OEP第二行:
此时如果将光标向上移,会发现第一句代码变乱了:
004010C7000D0A000055add[5500000A],cl
004010CD8BEC mov ebp,esp
这是因为Ollydbg将数据当汇编代码来分析了,你可以按Ctrl+ALT+向上光标键将当前显示的代码向上滚动一个字节就可看到正确的汇编代码了:
004010CC55push ebp
004010CD8BEC mov ebp,esp//中断在这行
004010CF83EC44sub esp,44
004010D256push esi
004010D3FF15E4634000call[4063E4];kernel32.GetCommandLineA
中断后,别忘点击菜单“调试/硬件断点/”打开硬件断点面板,将刚才的硬件断点删除。
小知识:硬件断点的原理作者:Lenus
3.根据编译语言特点找OEP
各类语言编译的文件入口点都有一些规律,可以这利用这点来寻找入口点。
1)Delphi程序
执行程序,用LordPE(或Prodump)选dump(full)脱壳,存为dump.exe。
接着用Hex Workshop打开dump.exe,搜索文本“runtime”,搜到后,向前查找离“runtime”最近的十六进制数字“558B EC”,数字所在的地址就是程序的OEP。
2)Visual C程序
可以利用Visual C启动部分的几个函数GetCommandLineA(W)、GetVersion、GetModuleHandleA(W)、GetStartupInfoA(W)等来定位程序的OEP。
常见的各类编译语言的入口汇编代码都要熟悉,因为一些加密强壳会偷OEP处的代码到壳里,一般情况各编译语言入口代码都相同,到时只需要直接引用相关程序的入口代码,这给我们恢复代码带来方便。
4.用内存断点找OEP
Author:Lenus
From:/&/
E-mail:Lenus_M@
--------------------------------------------------
1.前言
发现论坛中很多兄弟在询问:什么是二次内存断点,三次内存断点。
还有很多人对内存断点的原理不是很明
白。
其实只要懂得壳是如何解压代码的,那么就完全可以按自己的喜欢来下断。
本文要解决的问题是:
1.什么是内存断点?
2.如何在寻找OEP时使用内存断点。
3.内存断点的局限性。
2.内存断点寻找OEP的原理
i.首先,在OD中内存断点,硬件断点和普通断点(F2下断)是有本质区别的。
硬件断点等效与SoftICE命令bpm,他的中断要用到DR0-DR7的调试寄存器,也就是说OD通过这些DR0-DR7的调试寄存器来判断是否断下。
普通断点(F2下断)等效于bpx,他是在所执行的的代码的当前地址的一个字节修改为CC(int3)。
当程序运行到int3的时候就会产生一个异常,而这个异常将交给OD处理,把这个异常的regEIP-1以后就正好停在了需要的中断的地方(这个根据系统不同会不一样),同时OD在把上面的int3修改回原来的代码。
而内存断点基本上使用的是对代码使用的保护属性来实现中断。
内存断点分为:内存访问断点,内存写入断点。
我们知道,在程序运行的时候会有3种基本的状态产生:读取,写入,执行。
004AE242A100104000mov eax,dword ptr ds:[004AE24C]//004AE24C处的内存读取
004AE247A300104000mov dword ptr ds:[004AE24C],eax//004AE24C处的内存写入
004AE24C83C001add eax,1//004AE24C处的内存执行
那么我们应该如何中断在上面的几行呢?
1.当我们对004AE24C下内存访问断点的时候,可以中断在004AE242也可以中断在004AE247。
2.当我们对004AE24C下内存写入断点的时候,只能中断在004AE247。
3.当我们对004AE24C下内存访问断点的时候,能中断在004AE24C。
到这里你可能不明白了,为什么内存访问断点能中断在004AE247这一句对004AE24C的写入,而且还能中断在004AE24C的执行呢?
其实很简单,我们只要仔细体会一下“内存访问”这四个字的含义遍可以知道,当我们对004AE24C进行读取的时候需要“访问”他吧,当我对004AE24C进行写入的时候也需要“访问”他吧!!当然我们要执行内存地址004AE24C的代码的时候也是还是要“访问”他的!
所以我们不难得出下面的结论:
1.内存写入中断的地方,一定是也可以用内存访问中断。
2.内存执行的地方,也可以用内存访问中断。
如果这时你认为,那么内存写入岂不是没用了。
呵呵~那我要告诉你当然不是,如果你想快速的准确的定位到004AE247这一行的时候,那么他就大有作用了!
总结一下:内存断点不修改改原代码,不会像普通断点那样因为修改代码被程序校验而导致中断失败;对于区段的访问只是区域大了一点,其原理和上面分析的三行代码是一样的。
ii.如何使用内存断点来寻找OEP呢?
要回答这个问题首先要回答这一个问题:壳是如何解压代码的?
正如我们知道的,壳如果要把原来加密或压缩的代码运行起来就必须要解压和解密原来的代码。
而这一个过程我们难道不能将他看做是对代码段(code段)的写入吗?好了,解压完毕了。
我们要从壳代码的区段JMP到原来的代码段的时候,难道不正是对代码段(code段)的执行吗?
理清了上面的关系就好办了,那么如果载入OD后,我们直接对code段下内存访问断点的时候,一定会中断在壳对code段的写入的代码的上面,就像上面的004AE247的这一行。
而如果当他把code段的代码全部解压解密完毕了以后,JMP到OEP的时候,我们是不是还可以停在OEP的代码上面呢?而且每按下F9都会中断,因为这时code 段在执行中哦!
相信很多人到这里已经明白了,为什么在教程中到达了某一个时候,某一行的时候。
牛人们就叫我们对code
段下内存访问断点了吧。
而如果你还要继续问我为什么一定要到那个地方才可以下断呢?我难道不可以一开始就下断吗?
正入我上面所说的,如果你在前面下断很可能壳对code段还没解压完毕呢,这时如果你不停的按F9,你将会看到OD的下方不断的在提示你“对401000写入中断”“对401002写入中断”“对401004写入中断”.......如果你不介意按F9到他把正个code段写完的话,我除了同情你的“F9”以外,没什么其他的意见!
那么我们就没有别更快一点的办法了吗?
有的!那就是我们呼之欲出的两次内存断点办法。
怎么理解两次内存断点呢?
让我来做一个假设吧,假设我是一个壳的作者。
一个EXE文件的有code段,data段,rsrc段.....依次排列在你的内存空间中,那么我会怎么解码呢?呵呵~我比较笨一点,我会先将code段解码,然后再将data段解压,接着是rsrc段......那么聪明的你不难发现,只要你在data断或者rsrc段下内存访问断点,那么中断的时候code段就已经解压完毕了。
这时我们再对code段下内存反问断点,不就可以到达OEP了吗?
这里注意上面虽然下了两次内存访问断点,但是本质是不一样的,目的也是不一样的。
1.对data段下内存访问断点而中断是因为内存写入中断,目的是断在对对data段的解压时,这时壳要对data 段写数据,但是code段已经解压完毕。
2.对code段下内存访问断点而中断是因为内存执行中断,目的当然就是寻找OEP了。
总结一下:如果我们知道壳在什么地方对code段解压完毕我们就可以使用内存断点,找到OEP。
如果不知道,那么我们就依靠2次内存断点去找,如果还不行就用多次内存断点。
总之明白了原理在多次的内存断点其实都一样。
从这个过程中我们了解的是壳在对区段解码的顺序!
iii.实战
说了这么多,我想大家都越越欲试了吧。
好吧,来弄一个猛壳怎么样:
注:本节实例有些难度,不适合新手练习,新手可以跳过这个实例的学习,等找到合适的实例会补充上来的本节实例下载:unpackme.rar
这个壳是一个hying的旧版,我们用他来实验一下我们内存断点法。
OD载入以后来到这里
0040D000u>56push esi//这里
0040D00152push edx
0040D00251push ecx
0040D00353push ebx
0040D00455push ebp
0040D005E815010000call unpackme.0040D11F
根据跟过一次的经验我们将先设置,除int3异常以外忽略其他异常,SHIFT+F9
003725B164:89250000000>mov fs:[0],esp
003725B8CC int3
003725B990nop//到这里
003725BA8BCD mov ecx,ebp
然后再设置除“除零”异常外,忽略其他异常。
SHIFT+F9
00372660F7F3div ebx//到这里
0037266290nop
下面是很多的单步异常,太麻烦我们不管他,现在开始用内存断点的方法(记得将所有异常忽略)。
对code段下内存访问断点,希望他已经解压完毕。
方法是按ALT+M键打开内存窗口,在.code段按F2设断:SHIFT+F9执行:
0040D19D A4movs byte ptr es:[edi],byte ptr ds:[esi]//还没解完呢
0040D19E B302mov bl,2
对data段下内存“写入”断点,试试看他是不是要写data段。
00372712F3:A4rep movs byte ptr es:[edi],byte ptr ds:[esi]//断到这里
003727145E pop esi
下面再对code段下内存访问断点。
F9
003728558907mov dword ptr ds:[edi],eax;SHELL32.DragFinish//这里是对IAT加密
的地方了!!!
003728575A pop edx
003728580FB642FF movzx eax,byte ptr ds:[edx-1]
0037285C03D0add edx,eax
0037285E42inc edx
0037285F83C704add edi,4
0037286259pop ecx
00372863^E2A9loopd short0037280E
00372865^E963FFFFFF jmp003727CD
0037286A8BB593060000mov esi,dword ptr ss:[ebp+693]//到这里下断F2
现在如果再对data下访问断点已经是没用了。
这时应该格外的小心。
我们现在就想既然这一段是对code解码的,那么我们就绕过他吧!
到0037286A下断F2,然后清除内存断点!!!!
F9以后停在这里,继续对code下内存访问断点。
看看左下角还在解码,哎~真是麻烦!
003728E1/EB1D jmp short00372900
003728E3|25FFFFFF7F and eax,7FFFFFFF
003728E8|038583060000add eax,dword ptr ss:[ebp+683]
003728EE|2B858F060000sub eax,dword ptr ss:[ebp+68F]
003728F4|8BDE mov ebx,esi
003728F6|2BD8sub ebx,eax
003728F8|8958FC mov dword ptr ds:[eax-4],ebx//停在这里
003728FB|83C708add edi,8
003728FE^|EB DB jmp short003728DB
00372900\64:FF3530000000push dword ptr fs:[30]//清除内存断点以后到这里下断,F9
又是一段解码的代码,再次使用上面的办法手动跳出去。
现在继续对code段下内存访问断点!!F9以后到达这里。
004010CC FFD7call edi;unpackme.004010CE//OEP哦
004010CE58pop eax
004010CF83EC44sub esp,44
004010D256push esi
004010D390nop
004010D4E8B518F7FF call0037298E
004010D98BF0mov esi,eax
呵呵~虽然不是我们熟悉的OEP,但是地址是没错了,况且根据我们的步骤,我可以很肯定的说这是code段的第一次“执行”中断!
所以这就是OEP了。
总结一下:当我们在寻找OEP的时候,要多次对code下断“赌”一“赌”他解压完毕,如果不是就对别的段试试~如果程序跑飞了,那就没办法了,重来呗~其实说起来要赌的是:当data段,idata段,rsrc段摆在你的面前,你会好好“珍惜”那个段,不过还好上天还会给我们从来一次的机会(ctrl+F2^_^),那么我们会对那个不会跑飞的段说3个字----“先断你”如果非要在上面加一个次数,我希望是“一次内存断点就好了”
vi.下面来讨论一下内存断点的局限性问题。
是不是什么壳都可以用内存中断啊?
不是每个都可以的,一些像UPX和ASPACK就不行。
为什么?
呵呵~follew me!
情况1.
我们来看看UPX的壳
首先,他的壳代码在UPX1段。
这里是他要跳到OEP的地方
0040ED4F/7711ja short NOTEPAD_.0040ED62
0040ED51|01C3add ebx,eax
0040ED53|8B03mov eax,dword ptr ds:[ebx]
0040ED55|86C4xchg ah,al
0040ED57|C1C010rol eax,10//在解码
0040ED5A|86C4xchg ah,al
0040ED5C|01F0add eax,esi
0040ED5E|8903mov dword ptr ds:[ebx],eax
0040ED60^|EB E2jmp short NOTEPAD_.0040ED44
0040ED62\240F and al,0F
0040ED64C1E010shl eax,10
0040ED6766:8B07mov ax,word ptr ds:[edi]
0040ED6A83C702add edi,2
0040ED6D^EB E2jmp short NOTEPAD_.0040ED51//回跳解码
0040ED6F61popad。