版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、逆向的第一步是什么?这要问你学习C 语言的第一步是什么, 很自然的, 逆向的第一步当然也是大名鼎鼎“ HelloWorld! ”了。但是也不要因此就误认为这一节会很简单,如果你是第一 次接触逆向的话,那么这一节还是有些难度的。好的,让我们先写一个世界上最出名的程序:int _tmain(int argc, _TCHAR* argv)printf("Hello World!rn"); return 0;不错!很好的开始!然后用 VS2008以Debug方式编译下,再用 OllyDbg打开看看: 00411078 >JMP Test_0.004117B00041107D
2、JMP Test_0.00412CC000411082 JMP <JMP.&MSVCR90D._lock>00411087 JMP <JMP.&KERNEL32.GetProcAddress>0041108C JMP Test_0.0041144000411091 JMP Test_0.0041331000411096 JMP <JMP.&MSVCR90D.?terminateYAXXZ>0041109B JMP <JMP.&MSVCR90D._exit>004110A0 JMP <JMP.&KERN
3、EL32.GetCurrentThreadId>004110A5 JMP <JMP.&MSVCR90D._initterm>看看我们的程序停在了什么鬼地方, 如果各位初学读者试图从这里就开始分析的话那真的很 恐怖,相信 30 分钟内你的自信心将被打击到零 , 我们都知道其实编译器在编译我们的程序前会做很多准备工作,而这些准备工作由于涉及的东西较多且每个由此编译器生成的程序都一样, 因此我们不必深究, 只需快速且准确的找到 main 函数即可。但是这对于初学逆向的朋友来说也是最难的,下面我就教各位读者怎样突破这个障碍。想要找到main函数,那么我们就要从 C语言本身讲起
4、,在刚刚开始学习 C语言的时候我们 就被不幸的告知,我们的程序中必须要包含一个名字叫做main 的函数,不管你多讨厌它都必须如此,后来便成了习惯 ,后来查查 C99标准,发现"int main(int argc, char *argv) ”与"int main(void) ”都是被接受的, 然后又查查 MSDN,可以清晰看到一句话"The main and main functions can take the following three optional arguments ”,也就是告诉了我们 main 函数其实是有 3 个参数的,其后面的例 子更是证明了
5、这句话确实是微软写上去的:main( int argc, char *argv , char *envp )嗯,他们又在标准上较劲了,但是考虑到我们大部分程序都是用vs 编译的(而且 Borland的C+勺参数也是如此),因此我们还是做墙头草,随大流吧”到这里有的读者可能会感到疑惑,如果我们使用的是符合 C99 标准的 main 函数呢?例如我 们源码的 main 函数不就是两个参数吗。但是在这里我要很负责的告诉大家,不管我们代码 中实际使用了几个参数,在程序被编译时其 main 函数肯定是三个参数的,因为这取决于 Windows 系统的机制。因此现在已经为我们识别 main 函数提供了很好的
6、特征,既有三个参数,且前两个参数为地 址量的 call 就应该是我们的 main 函数了。除此之外,我们通过 MSDN 可知应用程序会随着 main 函数结束而退出, 这又给了我们第二个有力的特征, 既 main 函数很定是在程序退出代 码附近的(而且目前的主流调试、反汇编工具都可以正确识别出退出函数exit )。有了这些特征,我们再想找到 main 函数就不难了,目前我为大家提供三种方法:1.1.1、字符串搜索法安装完各个版本的 C+编译器后,逐个写 Hello World,然后用OllyDbg的搜索字符串功能搜 索这个字符串,最后逐步回溯即可,下面我为大家演示一下我做的步骤。用OllyDb
7、g打开目标文件后,先记住程序默认停在哪里,然后在CPU窗格点击右键,依次选择【超级字符串参考】【查找ASCII字符】,选择我们的“ Hello World ”后双击即可到 main函数中,代码如下:004113A0PUSH EBP; 函数入口004113A1MOV EBP, ESP004113A3SUB ES,P0C0004113A9PUSH EBX004113AAPUSH ESI004113ABPUSH EDI004113ACLEA EDI, DWORD PTR SS:EBP-C0004113B2MOV ECX, 30004113B7MOV EAX, CCCCCCCC004113BCREP
8、STOSD004113BEMOV ESI, ESP004113C0PUSH Test_0.0041573C; /Hello World!rn004113C5CALL DWORD PTR DS:<&MSVCR90D.printf> ; printf004113CBADD ESP,4004113CECMP ESI, ESP004113D0CALL Test_0.00411145004113D5XOR EAX, EAX004113D7POP EDI004113D8POP ESI004113D9POP EBX004113DAADD ESP,0C0004113E0CMP EBP,ES
9、P004113E2CALL Test_0.00411145004113E7 MOV ESP, EBP004113E9 POP EBP004113EA RETN我们单击选择函数入口后,可以看到CPU窗格下面的信息窗格中显示如下信息:跳转来自 0041100F我们单击选择此信息后,点击鼠标右键,并选择【转到 JMP来自0041100F】后即可来到上层调用函数(以后我们将之称为“返回到调用” ):; 我们停到这里0041100A JMP <JMP.&KERNEL32.DebugBreak>0041100F JMP Test_0.004113A000411014 JMP Test_
10、0.004124E0遇到这种情况直接在返回到调用,此时来到真正调用main 函数的地方:0041195F MOV EAX, DWORD PTR DS:41714800411964 PUSH EAX00411965 MOV ECX, DWORD PTR DS:41714C0041196B PUSH ECX0041196C MOV EDX, DWORD PTR DS:41714400411972 PUSH EDX00411973 CALL Test_0.0041100F00411978 ADD ESP,0C0041197B MOV DWORD PTR DS:41715C, EAX00411980
11、CMP DWORD PTR DS:417150, 000411987 JNZ SHORT Test_0.0041199500411989 MOV EAX, DWORD PTR DS:41715C 0041198E PUSH EAX; 我们停到这里; /status => 00041198F CALL DWORD PTR DS:<&MSVCR90D.exit> exit通过上面的代码我们便看到了main函数的典型特征,临近exit,且有三个参数。接下来我们要做的就是不断地重复上面的步骤,一直到找到程序入口点为止。最后你要做的就是针对不同的版本不同城上的编译器重复上面的步
12、骤,直到收集到你认为足够丰富的信息后结束,从此你就再也不用怕为找不到 main 函数而苦恼了。1.1.2 、栈回溯法栈回溯的方法是先找到main函数中的那个"HelloWorld ”下断点并按【F9】键运行后查看堆栈情况,我这里的堆栈情况如下:0012FE9C7C930208 ntdll.7C930208我们停在这里0012FEA0FFFFFFFF0012FEA47FFDE0000012FEA8CCCCCCCC0012FF64CCCCCCCC0012FF68/0012FFB80012FF6C|00411978 返回到 Test_0.00411978 来自 Test_0.0041100
13、F0012FF70|000000010012FF74|003D2C600012FF78|003D2D400012FF7C|0A641DBC0012FF80|7C930208ntdll.7C9302080012FF84|FFFFFFFF0012FF88|7FFDE0000012FF8C|00369E990012FF90|000000000012FF94|000000000012FF98|00130000 ASCII "Actx "0012FF9C|000000000012FFA0|0012FF7C0012FFA4|000000200012FFA8|0012FFE0 指向下一个
14、 SEH 记录的指针0012FFACI0041107D SE处理程序0012FFB0|0A3788D40012FFB4|000000000012FFB80012FFC00012FFBC|004117BF 返回到 Test_0.004117BF 来自 Test_0.004117D00012FFC00012FFF00012FFC47C817077 返回到 kernel32.7C817077对于这些信息我们只需要关注注释前面有“返回到”三个字的,离我们最近是:0012FF6C |00411978 返回到 Test_0.00411978 来自 Test_0.0041100F鼠标单击选择该项后,按【En
15、ter】键即可来到返回地址00411978处:0041195F.A1 48714100MOV EAX, DWORD PTR DS:41714800411964.50PUSH EAX00411965.8B0D 4C714100 MOV ECX, DWORD PTR DS:41714C0041196B.51PUSH ECX0041196C. 8B15 44714100 MOV EDX, DWORD PTR DS:41714400411972.52PUSH EDX00411973.E8 97F6FFFFCALL Test_0.0041100F00411978.83C40CADD ESP, 0C;我们
16、停在这里0041197B . A3 5C714100 MOV DWORD PTR DS:41715C, EAX00411980 . 833D 50714100>CMP DWORD PTR DS:417150, 000411987 . 75 0C JNZ SHORT Test_0.0041199500411989 . A1 5C714100 MOV EAX, DWORD PTR DS:41715C0041198E . 50 PUSH EAX ; /status => 0 0041198F . FF15 80824100 CALL DWORD PTR DS:<&MSVCR
17、90D.exit> ; exit此时我们又来到了这个熟悉的地方, 接下来的事情就要各位读者自己发挥了 (重复上面的步 骤)。1.1.3、逐步分析法以上讲的两种方法都是在学习与知识储备时用的, 不可能收到什么实战效果。 假如我们现在 碰到了一个现在就需要我们分析的软件, 而且它的编译环境我们以前没碰到过, 这就要求我 们纯手工分析并找到 main 函数了。之所以将之称为逐步分析法, 是因为我们不需要阅读它代码的具体含义, 而是只需要以 JMP 与CALL为单位逐个跟进,从而根据main函数的特征判定 main函数的所在位置。其实这种方法有点类似于文件搜索, 先搜索根目录、 在逐层加深搜索其
18、子目录, 直到找到我 们需要的东西。那我们的程序为例,我们的OEP处就是一个JMP,因此其“根目录”也就是第一层代码里是不可能有我们的 main函数了,当我们跟进这个 JMP后会发现如下代码:004117B0 > 8BFFMOV EDI, EDI004117B2 /. 55PUSH EBP004117B3 |.004117B5 |.8BECMOV EBP, ESPE8 96F8FFFF CALL Test_0.00411050004117BA |. E8 11000000CALL Test_0.004117D0004117BF |. 5DPOP EBP004117C0 . C3RETN我
19、们发现第二层代码里也没有我们的main函数,但是有两个CALL因此我们跟进第一个 CALL中,为了节省篇幅,我在这里就不贴出代码了,我在这里并没有发现main 函数,但是发现了数个JMP与CALL不过需要注意的是,我们一定要注意采用逐层搜索的思想,因此这里的 CALL 与 JMP 就不要再继续跟下去了,我们现在要住的是返回上一层,看看第二个CALL里是什么:004117D0 MOV EDI, EDI004117D2 PUSH EBP004117D3 MOV EBP, ESP004117D5 PUSH -200411813 CALL Test_0.004110FF00411830CALL DWO
20、RDPTR DS:<&KERNEL32.Interlocke>kernel32.InterlockedCompareExchange 0041184E JMP SHORT Test_0.0041185D00411850 PUSH 3E8; /Timeout = 1000. ms00411855 CALL DWORD PTR DS:<&KERNEL32.Sleep> ; Sleep0041185B JMP SHORT Test_0.00411825 004118EB PUSH Test_0.004157C8 ; _004118F0 PUSH 0004118
21、F2 PUSH 1F4004118F7 PUSH Test_0.00415750 ; f004118FC PUSH 2004118FE CALL DWORD PTR DS:<&MSVCR90D._CrtDbgRep>MSVCR90D._CrtDbgReportW 00411904 ADD ESP, 14; /NewValue = 0; |pTarget = Test_0.0041756C00411913 PUSH 0 00411915 PUSH Test_0.0041756C 0041191A CALL DWORD PTR DS:<&KERNEL32.Interlocke> InterlockedExchange 00411929 PUSH Test_0.004175900041192E CALL Test_0.0041117200411933 ADD ESP,4 0041193A PUSH 00041193C PUSH 20041193E PUSH 0;注意这里,虽然这个CALL也有; 这并不是 main 函数,因为; 不符合要求。其次他也并非是00411940 CALL DWORD PTR DS:417590 三个参数,但是仔细分
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年嘉兴南洋职业技术学院单招职业技能测试题库及答案详解(网校专用)
- 2026年吉林交通职业技术学院单招职业适应性测试题库带答案详解(巩固)
- 2026年合肥经济技术职业学院单招职业适应性考试题库带答案详解(a卷)
- 2026年吕梁师范高等专科学校单招职业技能考试题库及完整答案详解1套
- Linux系统安全加固最佳实践
- 基础护理操作中的无菌技术
- 人工气道并发症预防与处理
- 中毒急诊护士的角色与职责
- 2026贵州遵义习水县二里镇卫生院招聘见习人员2人笔试参考题库及答案解析
- 2026江西萍乡市各县区中学引进高层次教师102人笔试模拟试题及答案解析
- 2025年华电集团应聘笔试题目及答案
- 2025年高考英语新课标Ⅱ卷点评及2026备考方向 课件
- 有限空间及作业场所隐患图
- JJG 688-2025汽车排放气体测试仪检定规程
- 长沙学法减分题库及答案
- 《酒店职业英语》课件-unit 1 Room Reservation
- T/CTRA 01-2020废轮胎/橡胶再生油
- 2019抽水蓄能电站工程施工工艺标准手册:土建分册
- 医院培训课件:《中医病历书写基本规范及要点》
- 中考道德与法治一轮专题复习课件专题四 生命的思考(含答案)
- 《粤港澳大湾区发展规划纲要》(双语全文)
评论
0/150
提交评论