debugger(二):读、写内存以及寄存器
〇、前言
上一节,可以通过 break 0xADDR
的方式打断点,这种打断点的原理很简单,就是修改指令的第一个字节为 int3
(0xc
c)。本文将会读写内存单元、读写寄存器。
一、读写内存
启动 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
。