debugger(二):读、写内存以及寄存器
〇、前言
上一节,可以通过 break 0xADDR 的方式打断点,这种打断点的原理很简单,就是修改指令的第一个字节为 int3(0xcc)。本文将会读写内存单元、读写寄存器。
一、读写内存
启动 mini debugger,并打一个断点:
1 | |
一下为进程的maps 部分信息:
1 | |
可以看到,打了一个断点之后,0x5555555551d5 的数据被改写了,其中 0x48 被修改为了 0xcc。我们要做的就是读写内存,并且利用写数据的方法打一个断点来检验成果。
事实上,读写内存很简单,直接可以用 ptrace() 系统调用来实现:
1 | |
接着尝试使用 memory write 的方式进行打断点:
1 | |
可以看到非常成功,利用 memory write 成功打了一个“断点”。事实上,这个断点只是暂时的,它并没有被记录在 debug 信息系统中,不过这不重要,这只是在验证 memory write 的功能。
二、读写寄存器
这个比 memory 读写能稍微复杂一点,因为要做一些辅助性工作。首先要创建一些枚举、结构体以及一些辅助函数。
这里会用到一个库 <sys/user.h>,这个库中提供了 ptrace()进行修改读写的数据结构,比如 struct user_regs_struct:
1 | |
我们可以类似于这样:
1 | |
对被 debug 的进程进行访问。要对寄存器进行有效快速地访问,首先得定义一些函数,这些函数分别是:从寄存器本身、寄存器编号、寄存器名称进行访问。函数原型如下:
1 | |
要实现这些算法,首先得定义一些必要的数据结构:
1 | |
以上包括一个寄存器描述符,这是进行以上访问的基本结构。接着初始化一个描述符数组,这个数组的类型为寄存器描述符,大小为 27:
1 | |
然后就是访问的基本实现了:
1 | |
这样就可以验证了:
1 | |
上述,首先打印出了所有的寄存器,然后给 rax 中写了 0x1,接着 dump,可以看到 rax 成功地被修改为了 1。