主要用的知识点:ptrace,关于ptrace不太了解的同学可以先看看这两篇文章:
玩转ptrace1:https://www.cnblogs.com/catch/p/3476280.html
玩转ptrace2:http://blog.csdn.net/code09/article/details/6093363
首先运行我们的调试器,调试器作为父进程,fork一个子进程,在子进程中先trace_me,然后调用exec函数去执行被调试的进程。在父进程中运行一个大循环,去监听我们输入的调试指令。
我们来看看main函数
//main.cpp
#include
#include
#include
#include "debugger.h"
int main(int argc,char *argv[]){
using namespace std;
if(argc<2)
{
cerr<<"Program name not specified"<auto prog=argv[1];//要调试的程序的名字
pid_t pid=fork();
if(pid==0)
{
cout<<"子进程中"<0, nullptr, nullptr);
execl(prog,prog, nullptr);//执行要调试的程序,程序启动完毕会给父进程发送一个sigtrap信号
}else if(pid>=1)
{
cout<<"父进程中"<//在这里我们一直运行我们的调试器,去监听子进程的状态的改变
}
}
当子进程的状态改变的时候,父进程就能收到这种信号。
我们继续看父进程中的run函数:
void debugger::run()
{
int wait_status;
auto options=0;
waitpid(m_pid,&wait_status,options);//父进程阻塞在这里等待子进程启动完毕
char* line= nullptr;
while((line=linenoise("minidbg>"))!= nullptr)//调试器在这里输入
{
handle_command(line);//主要对命令处理函数
linenoiseHistoryAdd(line);
linenoiseFree(line);
}
}
我们继续看handle_command函数:
void debugger::handle_command(const std::string&line)
{
auto args=split(line,' ');
auto command=args[0];
if(is_prefix(command,"continue"))
{
continue_execution();
}else if(is_prefix(command,"break"))
{
std::string addr{args[1],2};//从0x以后开始初始地址,认为用户已经加上了0x
set_breakpoint_at_address(std::stol(addr,0,16));//设置断点
}else if(is_prefix(command,"register"))
{
if(is_prefix(args[1],"dump"))
{
dump_register();
}else if(is_prefix(args[1],"read"))
{
std::cout<2]))<<std::endl;
}else if(is_prefix(args[1],"write"))
{
std::string val{args[3],2};
set_register_value(m_pid,get_register_from_name(args[2]),std::stol(val,0,16));
}
}else if(is_prefix(command,"memory"))
{
std::string addr{args[2],2};
if(is_prefix(args[1],"read"))
{
std::cout<<std::hex<std::stol(addr,0,16))<<std::endl;
}
if(is_prefix(args[1],"write"))
{
std::string val{args[3],2};
write_memory(std::stol(addr,0,16),std::stol(val,0,16));
}
}
else
{
std::cerr<<"Unknow command\n";
}
}
这里我们先实现了几个常用的命令,后边还会对这些命令有扩充。
接下来的几篇,我们将会分析每个命令的实现。