OllyDbg 反调试之检测进程名
使用工具
- OllyDbg 1.10原版,简称
OD
; OD
汉化
和插件
均来自互联网;- CrackMe来自互联网,仅供学习使用;
- 文中特殊数字均是
HEX
,为了书写方便采用DEC
;
分析思路
本次内容学习 Windows API 才是重点;
打开
CrackMe
看看:既然是学习反调试,那么注册码当然不是重点,重点是它对
OD
的影响,打开OD
,发现闪了一下然后被关闭了,嗯,反调试了;还有一个现象就是:给
OD
改个名后,就不会被反调试了,即使是OD
载入程序并运行,也不会被反调试;倒入
OD
开始分析:首先,
Ctrl + N
查看 API 列表,API 很多,搜索GetProcAddress
设置 CC 断点;了解一下 Windows API
GetProcAddress
:1
2
3
4
5
6
7
8
9
10
11GetProcAddress
作用
检索指定的动态链接库中的输出库函数地址;
(获取函数列表中不显示且被调用的隐藏函数的地址;)
(捕捉间接加载或调用的其他隐藏函数;)
参数
hModule:包含此函数的 DLL 模块的句柄;
(程序的句柄)
IpProcName:包含函数名的以 NULL 结尾的字符串;
(程序的名字)
接着,
F9
运行程序,程序会在设置断点的 API 上中断多次,每中断一次,都代表获取了一个隐藏的函数;在大约中断了 143 次后,对接下来获取到的 3 个重要的 API 设置 CC 断点:
EnumProcesses
、EnumProcessesModules
、GetModuleBaseNameA
;国际惯例,分别了解一下这几个 API :
1
2
3
4
5
6EnumProcesses
作用
检索进程列表中每一个进程的标识符;
(枚举进程的 PID;)
(获取进程列表所有进程的 PID;)1
2
3
4
5EnumProcessesModules
作用
检索指定进程中每个模块的句柄;
(获取进程的基址;)1
2
3
4
5GetModuleBaseNameA
作用
检索指定模块的基本名称;
(获取进程名;)设置断点的方法:
程序中断后,在
堆栈窗口
确认是需要的 API 后,在函数名称所在行右键菜单选择复制到剪贴板,接着
Ctrl + F9
执行到返回,然后
bp EAX
设置断点,在断点窗口双击断点进入汇编窗口,
在注释栏双击并粘贴复制的函数名,也就是给断点设置备注,
断点设置完成;
至于为什么要设置备注,如果有 10 个没有设置备注的断点,那就傻傻分不清了;
这里不得不提一下 PID,它的全称是
Process ID
,通俗易懂嘛,进程 ID
;至于怎么查看 PID,打开任务管理器,选择
进程
列,就显示了当前所有程序的诸如程序名、PID、用户名等等信息:而我们使用的
OD
也赫然在列;当然,如果你的进程列表中没有 PID 这一列,不要慌,你的电脑没问题,点击菜单栏中的
查看
按钮并选择选择列
选项:然后勾选
PID
复选框并确定,你的进程列表中就有 PID 这一列了:
了解并知道如何查看 PID 之后,就要进入正题了:
以我的机器为例,
OD
的 PID 是1380
,这是一个 10 进制数,要在OD
里使用它,当然要转换为 16 进制:进制转换的方法很多,比如使用网页提供的进制转换器,使用系统内置的计算器,使用
OD
自带的进制转换,这里以OD
为例:随便双击一个寄存器,然后修改它的
无符号
值,十六进制
栏就会显示对应的 16 进制数,这里OD
的 PID 对应的 16 进制数就是564
;当然,修改寄存器的值只是为了进制转换,一定不要点击确定哦;设置好 3 个函数的断点后,继续运行程序,程序会再次中断,不过,不再是中断在
GetProcAddress
,而是中断在新设置的第一个断点,也就是EnumProcesses
:我们已经了解了
EnumProcesses
的功能:获取进程列表所有进程的 PID;既然
EnumProcesses
不需要参数,那么堆栈窗口ESP + 4
的位置,存储的就是它的返回值,数据窗口中跟随一下:接着
Ctrl + F9
执行到返回,发现以基址为起始位置的部分地址的内容被覆盖了,而OD
的 PID 也在其中:
既然是要了解反调试对
OD
的影响,当然要跟随OD
的数据了,在OD
的 PID 上设置内存访问断点
:继续运行程序,程序再次中断,一眼就看到了
OD
的 PID 被当作参数传递给了函数OpenProcess
:OpenProcess
这个函数的作用是什么呢,了解一下:1
2
3
4OpenProcess
作用
通过已知 PID 获取程序的句柄;OD
危险了,因为这个CrackMe
拿到了它的句柄,也就是它的生杀大权;F8
单步步过执行程序,看看是否如是:因为函数的返回值一般都在
EAX
,所以我们大胆猜测,EAX
中应该就是OD
的句柄,如何证明呢?去句柄窗口
看看:句柄窗口
中有一个数值相同的句柄,而这个函数是用来获取句柄的,又把OD
的 PID 当作了参数,那么它返回的应该就是OD
的句柄,所以可以确定,EAX
中就是OD
的句柄也就是000000C4
;
继续运行程序,程序再次中断,来到了我们设置的三个函数断点中的其二,也就是
EnumProcessesModules
:获取指定进程的基址在数据窗口中跟随存放函数执行结果的地址,然后
Ctrl + F9
执行到返回后,可以发现OD
的基址是00400000
:继续
F9
运行程序,程序再次中断,这次是GetModuleBaseNameA
:执行到返回发现,它通过进程和基址获取到了
OD
的名称:接下来就不能直接
F9
运行程序了,因为再运行下去程序可能就结束了,所以使用F8
单步执行,看看它获取了进程名之后要做什么:咦,又一个把
OD
句柄当作参数传递的函数,了解一下:1
2
3
4CloseHandle
作用
通过已知对象的句柄关闭句柄;继续执行后,去
句柄窗口
查看,已经找不到OD
的句柄了,说明它被关闭了;接着向下运行,发现一个把
OD
的名称当作参数的函数:使用
F7
步入跟进后,发现它是将OD
的名称转换为 UpperCase,紧跟着下一个函数把转换后的名称和给定字符进行了比较:上一步执行完毕后,由于比较的结果相同,跳转失败,程序又重新通过
OpenProcess
使用OD
的 PID 获取了OD
的句柄:接着,把
OD
的句柄作为参数传递给TerminateProcess
:1
2
3
4TerminateProcess
作用
通过进程句柄终止指定进程及其所有线程;了解了这个 API 的用途后,继续运行程序,结果不言而喻:灵光一闪,程序关闭!