《逆向工程核心原理》学习笔记1
《逆向工程核心原理》学习笔记1
逆向工程核心原理第二章
准备阶段
-
软件:Ollydbg,visual studio community
-
使用visual studio创建一个c++控制台项目,粘入以下代码,然后运行生成**x86(Win32)**的HelloWorld.exe文件
1
2
3
4
5
6
7 -
《逆向工程核心原理》作者提供的资源下载github仓库:https://github.com/reversecore/book.git;书中用到的代码和可执行文件都能在里面获取
调试定位main函数
-
简单使用 F7(step into)和F8(step over)查找main函数位置
-
使用ollydbg打开HelloWorld.exe后,可以看到入口点(EntryPoint)跟《逆向工程核心原理》书中不太一样,应该是vs版本不一致导致的(我在vs2017编译出来的程序是wmain名字,汇编是 004011E2 . E8 19FEFFFF call HelloWor.wmainerrree_localeunction<int (__cdecl*)(void)>ator()uninitialize<void (__cdecl&)(char *...),但不影响后续的分析,这里直接F7跳至下一语句
-
跟踪跳转后可以看到002B1F63的地方调用了一个HelloWor*函数,我们继续F7跟踪跳转
-
F7跟踪进入后,发现函数内部又调用了两个函数,这里为了可以改用F8来筛选出这两个哪个函数是包含我们main函数的代码,因为我们已知程序是会弹出一个HelloWorld的窗,所以F8执行时,只要看执行这两个函数时,哪个会弹窗,那么那个函数就是需要我们进一步分析的
-
按照上面的思路,最终定位到第二个HelloWor.__scrt_comon_main这个函数是我们要找的(vs2017里没有,注意),找到后,我们重新调试Ctrl + F2,然后F7步入HelloWor.__scrt_comon_main这个函数进行进一步分析
-
F7步入后可以发现,这个函数内部相比前面的函数要复杂的多,调用其他函数的地方也比较多,这里也一样用上面一样的思路来使用F8单步跳过来查找main函数,每执行完一个函数,查看下程序是否出现弹窗,如果出现了那么说明那个函数就是包含我们main函数代码的函数
-
最终先定位到HelloWor.invoke_main_slow_path*这个函数(vs2017也没有),其实从函数名字也大致能看出来(invoke main => 执行main),这个函数就是调用我们main函数的地方,我们F7步入跟踪查看
-
可以看到,该函数内部也调用了4个函数,这里还是上面的老办法,继续排查,最终终于找到我们的main函数HelloWor.002B114F,F7步入查看
-
可以看到我们代码中定义的字符串"blog.iz4.cc"和"HelloWorld",以及调用MessageBoxW,可以确定这就是我们的main函数,至此终于找到了main函数
-
小结:我们想定位程序某段代码所在位置时,可以利用F7和F8来完成,根据程序执行到某个位置时,程序状态是否发生改变来定位相应的位置,但这个方法只适用于程序比较简单的情况下,如果程序比较复杂,那么这个方法则非常的麻烦。
使用搜索字符串来定位main函数
-
重新开始调试,右键选择 中文搜索引擎 => 智能搜索
-
这里可以看到排在最前面的两个字符串就是我们要找的字符串,如果不能一眼看出所找字符串在哪,那么可以Ctrl + F进行搜索查找,这里我们双击字符串"HelloWrold"就能进入我们的main函数当中,比上面的运行调试的代码更为快捷。
根据API调用查找main函数位置
-
原理:根据程序运行时的行为来推断其可能用到了哪些API,然后查看程序所有调用该API的地方,再判断是否为目标函数
-
重新开始调试,右键选择 查找 => 所有模块间的调用
-
点击窗口内容,输入MessageBoxW,则自动定位到相应API调用的地方,双击user32.MessageBoxW,定位到程序调用该API的位置,也就找到了我们的main函数所在的位置
在API代码中打断点查找main函数
-
原理:根据程序运行时的行为来推断其可能用到了哪些API,在相应API的代码中设下断点,然后让程序运行至断点处,再执行"运行到返回"来找到程序相应的调用该API的代码位置
-
重新开始调试,右键选择 查找 => 所有模块名称,然后点击数据窗口,输入MessageBoxW进行筛选,然后双击筛选结果MessageBoxW进入MessageBoxW函数所在位置
-
按下F2(设置/取消)断点,管理所有断点可以点击图中BreakPoint下方的b按钮打开断点管理窗口,设置断点后,可以按F9使之运行到断点处(需要只有一个断点,多个断点则停在靠前的断点处),或着按F4运行运行至光标处
-
然后Ctrl + F9运行到返回,这里实际上并不会直接返回程序调用MessageBoxW的地方,需要我们先点击程序的"确定",点击完后再按Ctrl + F9,则能返回到main函数
-
如下图,可以看到最终返回的位置是程序调用MessageBoxW之后的下一句语句,这样也找到了main函数所在位置
修改字符串"HelloWorld"为"Hi World!"
-
找到main函数后,我们可以知道"HelloWorld"这个字符串所在的地址,如下图地址为"002B7B4C"
-
接下来,我们点击左下角的数据窗口,Ctrl + G输入"002B7B4C",跳转至地址"002B7B4C",可以看到该数据内容就是字符串"HelloWorld"
-
光标选择HelloWorld字符串,然后Ctrl + E进行编辑,这里一般来说修改字符串后的字符串长度应该小于等于原字符串长度,否则可能引起程序出错,这里选择修改Unicode的"HelloWorld"为"Hi World!",然后选择HEX将末尾的64 00 改为 00 00,即用NULL填充,从c++的角度就是用'\0'标记该处为字符串终点,此外这里是两个字节的原因是编码为Unicode,所以才两个字节一个字符
-
保存后,运行程序,可以看到程序的弹窗内容已被修改为"Hi World!",但是这样的修改不是永久的
-
如果需要永久保存我们的修改,需要右键选择左下角的数据窗口,然后选择复制到可执行文件
-
然后进入如下的数据窗口,右键选择保存文件即可
修改字符串"HelloWorld"为长字符串"HelloReversingWorld!!!"
-
前面提到的方法虽然也能修改字符串内容,但当我们需要修改的字符串比原字符串长的多时,该方法就不适用了,下面介绍的方法原理很简单,在程序中非常多"00"的地方也就是比较"空闲"的地方里面放我们需要加的字符串,然后将程序在引用"HelloWorld"的地方将相应的地址替换为我们的字符串地址即可,但有点需要注意的是,不是所有非常多"00"的地方都适合,我们需要在程序存放字符串常量的区域去找大片的空白区
-
我们先跟之前方法一样跳转至"HelloWrold"字符串所在位置,然后向下拖动,找到一大片"00"数据区域,然后选择一个位置开始写入我们的字符串即可
-
如下图,选择编辑002B861C处的数据,取消勾选保持大小,这样可以输入我们想要的字符串长度,输入HelloReversingWorld!!!
-
保存完后,我们回到程序引用"HelloWorld"字符串的地方,修改该处的汇编指令,将指向"HelloWorld"的地址替换为"HelloReversingWorld!!!"的地址
-
如下图,按"空格"修改光标所在的汇编代码,将地址改为"HelloReversingWorld!!!"的地址即"0x002B861C",保存然后运行查看结果
-
最终效果如下,可以看到同样达到修改字符串的目的
-
Restart Ctrl+F2 重新开始调试
-
Step Into F7 执行语句会进入函数内部
-
Step Over F8 执行语句不会进入函数内部
-
Execute till Return Ctrl+F9 一直在函数代码内部运行,直到遇到retn跳出
(跳出该命令函数)
OD中右边注释中的红字部分是 代码调用的API函数名称 是VS为了保证程序正常运行自动添加的,源码中并没有,是VS的启动函数,可以暂时忽略。
RETN指令,它用于返回函数调用者的下一条命令,一般是被调用的函数的最后一句
●找到Helloworld!的main()主函数 未完成 没找到
在菜单栏选择View-Breakpoints选项(快捷键ALT+B)打开Breakpoints窗口,该窗口列出代码中设置的断点。双击会跳到相应位置。
右键菜单Search for User-defined comment。可以看见所有添加的注释。双击会跳到相应位置。
|
1、 从头到尾地执行 直到消息窗口弹出 弹出消息窗口的就是主函数 F8
2、 第二遍 一直执行到弹出消息窗口的函数前 F7 进入 函数内部
Tips
VC++中,static字符串会被默认保存为Unicode码形式,static字符串是指在程序内部被硬编码的字符串。
代码与数据所在的区域是彼此分开的。
Helloworld!.exe这个程序会出弹窗
弹窗调用了user32.MessageBoxW()API
所以我们直接 右键 查找所有模块间的调用(All intermodular calls)该窗口会列出程序中调用的所有API
找 MessageBoxW()
上法的缺点: OD不能为所有.exe都列出API函数调用列表。使用压缩器/保护器工具对软件进行压缩或者保护后,文件结构机会改变。因此OD就不能列出所有API函数列表了。
Tips
压缩器:压缩软件的代码、数据、资源等,压缩后仍是.exe
保护器:不仅具有压缩功能还能反调试、反模拟、反储存..能够有效保护进程
DLL代码库被加载到进程内存后,可直接向DLL代码库添加断点。看不懂
API是操作系统对用户应用程序提供的一系列函数,实现于系统文件夹中的*.dll文件内部。
我们编写的应用程序执行某种操作时,必须使用OS提供的API向OS提出请求,然后与被调用API对应的系统DLL文件就会被加载到应用程序的进程内存。
该过程可以通过,OD菜单中的View-Memory(Alt+M),打开内存映射窗口,观察到。
若HelloWorld.exe应用程序中调用了MessageBoxW()API,则调用时程序运行到该处就会停止。
1、 OD中的Name in all modules(所有模块名称) 命令可以列出被加载的DLL文件中提供的所有API。名称排序。找到MessageBoxW。
2、 下断点 F2
3、 继续执行 F9 直到遇见MessageBoxW()代码的断点 自动停
4、 此时的ESP值对应一个返回地址 HelloWorld.exe的main()函数调用完MessageBoxW()之后,程序会回到该地址处。Ctrl+F9可以直接让MessageBoxW()到RETN处。F7也可以。
|
打补丁 可以修BUG 还可以加新功能
右下 要选HEX/ASCII
直接智能搜索到 相应的字符串处 数据窗口跟随—>立即数
在ASCII区域选中 Ctrl+E 直接更改 保存到可执行文件 保存文件
此法的缺点在于不能比原字符串长,最好别
找一块空地儿 Ctrl+E 修改
找到原字符串的地方修改汇编指令 空格键修改
把地址改成新地方的地址
保存
术语和说明