最近好迷茫,所以又斥巨资买了一本充满力量的书,那就是看雪的《加密与解密》,也来彻底地玩一玩逆向吧。
<!--more-->
曾经的我,认为 Linux
天下第一好用,(虽然现在我也那么认为)但是 windows
作为受众很广的操作系统,很多 windows
的程序也是有必要去学习一下的。
当今大部分 windows
程序都是 GUI
,GUI
是通过一系列 API
来完成的,具体帮助手册自行下载。
我们只需要知道一部分的,并且用到什么就学什么好了,比如常见的从控件中获取输入值,用接口 GetDlgItemTextA
,弹窗反馈 MessageBoxA
,当然这是比较常见的,具体调试的时候还得具体情况具体分析。
windows
程序运行比较依赖动态链接库(dll),可以理解为 Linux
下的 .so
文件。windows
比较内核的三个动态链接库为
kernel(kernel32.dll)
:提供操作系统的核心服务。
user(user32.dll)
:提供用户输入和输出的接口。
GDI(GDI32.dll)
:提供图形设备接口。
这里需要注意一点,就是很多接口的后缀 A
或者 W
表示它处理字符的一个字符集, A
表示 ASCII
码,w
表示 unicode
码。
这本书介绍了很多的动态调试器,这里我还是比较喜欢 x32dbg
,所以其它的我也不一一解读了,但是相同的特性也能拿来讲一讲记一记。
打开调试器,在文件 ->
打开 中选择自己要调试的文件,然后载入。
打开调试器,在文件 ->
附加 中选择自己要附加调试的进程,然后载入。
感觉附加调试会好一点吧,毕竟能先让程序运行到自己想调试的点然后再去调试,这样省了前面入口点的一些操作,但是我们要调试关键信息,一定是要下断点(break point)的。
这里我们直接用 chap02\OllyDbg调试器\2.1.4 基本操作\bin\ASCII版\TraceMe.exe
来试试手。
调试器整体呈这样,最上面一行肯定是标签栏,工具栏,选项卡。剩余窗口大致分为四块。CPU
选项卡中的窗口表现出来就是代码窗口,最右边的窗口时寄存器窗口,左下角的窗口是内存窗口,右下角的窗口是栈窗口,最下面一行的文本框可以用于打命令使用。
大概分了五列,如下图所示。
对于第一列,更多的是标识跳转的作用,以及设置断点。
第二列标识了当前内存地址的地址,双击可以显示改行的相对地址偏移,再次双击恢复。
第三列标识了该地址的内存字节,双击可以下断点。
第四列标识了该字节码的反汇编代码,选中使用 空格 键可以修改汇编代码。
第五列提供了一些内存地址或者是寄存器的值,也可以用 ;
键去添加注释。
断点大致分为以下几种类型
INT 3
断点
硬件断点
内存断点
消息断点
条件断点
INT 3
是一条汇编指令,其机器码是 0xCC
所以也叫 CC
指令。执行这个指令的时候,会抛出一个 break point exception
异常,这个异常会被调试器捕获到,因此能达到断点的目的。在打断点的时候,会把这个地址设置为 0xCC
,也就是 INT 3
的机器码。优点是可以设置很多个断点,因为我们只要想,可以在任意地址把值改成 0xCC
以此达到断点的目的。但是带来的缺点就是会修改程序的内存,改变了原机器码,可能会被程序检测到。
比如这样的一个 MFC
程序,附件下载
source:
void CTrackMeDlg::OnBnClickedButton1(){
// TODO: 在此添加控件通知处理程序代码
HWND hWnd = AfxGetMainWnd()->m_hWnd;
QWORD Uaddr;
BYTE Mark = 0;
Uaddr = (QWORD)MessageBoxA;
Mark = *(BYTE*)Uaddr;
if (Mark == 0xCC) {
MessageBoxA(hWnd, "be tracked", "MessageBox", 0);
return;
}
MessageBoxA(hWnd,"Very OK","MessageBox",0);
}
正常运行结果如图所示
如果放到调试器,对 MessageBoxA
这个函数下断点的话,那么就会导致出现另一个不同的运行结果。
MessageBoxA
上,而程序判断了 MessageBoxA
调用地址是否为 INT 3
指令(0xCC
),有的话就直接输出 be tracked
说明检测到这里下了 INT 3
断点。INT 3
断点。DRx
寄存器实现的,DR0~DR3
分别用于保存硬件断点的地址,那我们也可以看出来它最多能同时存在四个硬件断点。DR4-DR5
未公开具体作用, DR6
用于保存寄存器组状态, DR7
用于保存寄存器组控制。硬件断点不会改变程序字节码,因此它更难被检测,在断点选项中可以设置硬件执行断点,同样,对于一般内存来说,我们可以设置硬件访问断点。DR
寄存器,我们可以从前四个寄存器中找到我们下的硬件断点的位置。硬件断点的优势劣势与前面的 INT 3
断点相对,硬件断点不能同时大量设置,但是它不会更改进程代码段的内存。F9
继续执行可以发现程序停在了我们下的断点位置。x32dbg
应该是这里的技术细节没有实现,所以导致它只能在一个内存页设置,不能保证在指定位置断住,因此在一个内存页中可能多次被这个点断住,因为它可能没有比较访存位置与内存断点位置。shift+F2
设置条件断点,比如我想在一个 10000
次的循环当中,看第 5000
次的执行结果,那么我们正常操作就是给循环体一个断点,然后 F9
5000
次,显然这么做会很麻烦,那么我们可以设置一个条件,让它在指定条件才断住,加入循环变量存储在 rcx
当中,那么我们可以设置 rcx==5000
。#include<bits/stdc++.h>
int main(){
int sum=0;
system("pause");
for(int i=1;i<=10000;i++){
sum+=i;
printf("%d\n",sum);
}
}
ebx
寄存器中,我们在循环体中下一个条件断点,观察程序的运行。