在这里,依旧是我们的利器ptrace
//reg.h
#ifndef DEBUGGER_REG_H
#define DEBUGGER_REG_H
#include <string>
#include <boost/mpl/size_t.hpp>
#include <array>
#include <algorithm>
enum class reg
{
rax,rbx,rcx,rdx,
rdi,rsi,rbp,rsp,
r8,r9,r10,r11,
r12,r13,r14,r15,
rip,eflags,cs,orig_rax,
fs_base,gs_base,fs,gs,ss,ds,es
};
static constexpr std::size_t n_registers=27;
struct reg_descriptor
{
reg r;//rax---es寄存器
int dwarf_r;
std::string name;
};
//这里要看自己的平台下的reg.h或者user.h,我的是:
/usr/include/x86_64-linux-gnu/sys/reg.h,一定要对应起来,贴一个我的环境:ubuntu16.04下reg.h:
define R15 0
define R14 1
define R13 2
define R12 3
define RBP 4
define RBX 5
define R11 6
define R10 7
define R9 8
define R8 9
define RAX 10
define RCX 11
define RDX 12
define RSI 13
define RDI 14
define ORIG_RAX 15
define RIP 16
define CS 17
define EFLAGS 18
define RSP 19
define SS 20
define FS_BASE 21
define GS_BASE 22
define DS 23
define ES 24
define FS 25
define GS 26
//为了方便获取寄存器的值,名字等后边的使用
const
std::array<reg_descriptor,n_registers>g_register_descriptors
{{
{reg::r15,0,"r15"},
{reg::r14,1,"r14"},
{reg::r13,2,"r13"},
{reg::r12,3,"r12"},
{reg::rbp,4,"rbp"},
{reg::rbx,5,"rbx"},
{reg::r11,6,"r11"},
{reg::r10,7,"r10"},
{reg::r9,8,"r9"},
{reg::r8,9,"r8"},
{reg::rax,10,"rax"},
{reg::rcx,11,"rcx"},
{reg::rdx,12,"rdx"},
{reg::rsi,13,"rsi"},
{reg::rdi,14,"rdi"},
{reg::orig_rax,15,"orig_rax"},
{reg::rip,16,"rip"},
{reg::cs,17,"cs"},
{reg::eflags,18,"eflags"},
{reg::rsp,19,"rsp"},
{reg::ss,20,"ss"},
{reg::fs_base,21,"fs_base"},
{reg::gs_base,22,"gs_base"},
{reg::ds,23,"ds"},
{reg::es,24,"es"},
{reg::fs,25,"fs"},
{reg::gs,19,"26"},
}};
uint64_t get_register_value(pid_t pid,reg r);
void set_register_value(pid_t,reg r,uint64_t value);
uint64_t get_register_value_from_dwarf_register(pid_t pid, unsigned regnum);
std::string get_register_name(reg r);
reg get_register_from_name(const std::string& name);
#endif //DEBUGGER_REG_
我们先来看看get_register_value(pid_t pid,reg r)的实现
uint64_t get_register_value(pid_t pid,reg r)
{
user_regs_struct regs;
//这里父进程将子进程所有寄存器的值都放在了regs结构体里面
ptrace(PTRACE_GETREGS,pid,nullptr,®s);
//获取我们传进去的寄存器在regs中的偏移
auto it=std::find_if(begin(g_register_descriptors),end(g_register_descriptors),[r](reg_descriptor rd){
return rd.r==r;
});
/**
* 用存放了子进程reg结构体内容的regs获取我们制定的寄存器的值,下面这样代码首先将regs的地址强转成
* 一个指向uint64_t的指针,这个指针加1,就相当于偏移了sizeof(uint64_t),这里偏移了it-begin(g_register_descriptors),
* 也就是我们需要查看的寄存器
*/
return *(reinterpret_cast<uint64_t*>(®s)+(it-begin(g_register_descriptors)));
好像这个函数没啥说的,直接看注释就好。
void set_register_value(pid_t pid,reg r,uint64_t value)
{
user_regs_struct regs;
ptrace(PTRACE_GETREGS,pid, nullptr,®s);//同样先将子进程的当前寄存器数据保存到regs这个结构体中
auto it=std::find_if(begin(g_register_descriptors),end(g_register_descriptors),[r](reg_descriptor rd){
return rd.r==r;//在reg_descriptor这个数组中先找到我们需要的寄存器在整个regs中的偏移
});
*(reinterpret_cast<uint64_t*>(®s)+(it-begin(g_register_descriptors)))=value;//给结构体中相应的寄存器赋值
ptrace(PTRACE_SETREGS,pid, nullptr,®s);//将修改后的regs结构体整个赋值给子进程当前的寄存器,这里我们只修改了我们传进去的寄存器的值
}
上边两个函数理解了,下边这三个就不用详细解释了,直接看代码:
uint64_t get_register_value_from_dwarf_register(pid_t pid, unsigned regnum)
{
user_regs_struct regs;
auto it=std::find_if(begin(g_register_descriptors),end(g_register_descriptors),[regnum](reg_descriptor rd){
return rd.dwarf_r==regnum;
});
if(it==end(g_register_descriptors))
{
throw std::out_of_range{"Unknow dwarf register"};
}
ptrace(PTRACE_GETREGS,pid, nullptr,®s);
return *(reinterpret_cast<uint64_t*>(®s)+(it-begin(g_register_descriptors)));
}
std::string get_register_name(reg r)
{
auto it=std::find_if(begin(g_register_descriptors),end(g_register_descriptors),[r](reg_descriptor rd){
return rd.r==r;
});
return it->name;
}
reg get_register_from_name(const std::string&name)
{
auto it=std::find_if(begin(g_register_descriptors),end(g_register_descriptors),[name](reg_descriptor rd){
return rd.name==name;
});
return it->r;
}
到这里我们的有关寄存器操作的函数就实现了,下一篇我们将介绍断点的实现。