虚假的调试-ptrace,真正的调试-proc/pid/mem
安装原理来说调试需要用到ptrace,父进程是调试进程,子进程是被调试进程,用ptrace实现父进程对子进程的调试。
每一个进程都会在/proc/ 下创建自己的文件夹,里边的status储存了进程的基本状况,maps存储了进程使用的内存的映射位置,mem就是进程的内存虚拟文件,通过访问这个虚拟内存文件,就可以实现对该进程的内存访问。
下面的little_game是用来查看的一个进程
使用read_mem来实现对little_game进行内存访问。
其流程大致是,首先通过进程名字,来遍历/proc文件夹,在每个进程文件夹中的status这个文件中的第一行获得该进程的名字,文件夹的名字就是这个进程的pid,从而确定这个进程的pid,然后little_game输出了他的字符串的地址,我们要使用 root 权限运行read_mem,这个内存文件的读取需要 root 权限,需要 root 权限, 需要 root 权限。然后从文件的开始,找到那个字符串的地址,从那个位置读取字符串,打印出来,证明了我们的确访问到了 little_game的内存,然后,再次重定位到这个字符串的位置,写入我们希望的内容。
little_game.cpp:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
char buff[]="hello there is 1.cpp";
printf("%s\n", buff);
printf("%p\n", buff);
printf("%d\n", getpid());
getchar();
printf("%s\n", buff);
return 0;
}
read_mem.cpp
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;
string process_name = "little_game";
bool is_a_number(char *s)
{
bool flag=true;
for(int i=0;i!=strlen(s);i++)
{
if(s[i]<'0'||s[i]>'9')
{
flag = false;
break;
}
}
return flag;
}
string get_processName_by_pid(int pid)
{
string name="";
string process_status_file_path = "/proc/"+ to_string(pid)+"/status";
FILE *fp = fopen(process_status_file_path.c_str(), "r");
if(fp!=NULL)
{
char c;
while((c=getc(fp))!=':');
while((c=getc(fp))==' ');
while((c=getc(fp))!='\n')name+=c;
}
fclose(fp);
return name;
}
int get_process_pid_by_name(string processName)
{
int pid=-1;
DIR *dirp;
struct dirent *dirEntry;
string proc_path = "/proc/";
dirp = opendir(proc_path.c_str());
if(dirp==NULL)
{
printf("%s is not a folder!\n", proc_path.c_str());
}
else
{
while((dirEntry = readdir(dirp)) !=NULL)
{
// 当这个目标是文件夹,并且这个文件夹的名字是数字,那么才是一个进程的文件夹
if(dirEntry->d_type==4 && is_a_number(dirEntry->d_name))
{
// 文件夹的名字就是pid,不需要去从status文件读取pid了
pid=atoi(dirEntry->d_name);
// 获取进程的名字,但是进程的名字好像是只存放了前16个字符
string tName = get_processName_by_pid(pid);
// 检查是不是我们找的进程的名字
bool flag=true;
if(tName.size()<=processName.size())
{
for(int i=0;i!=tName.size();i++)
{
if(tName[i]!=processName[i])
{
flag = false;
break;
}
}
}
else flag = false;
if(flag)break;
else pid=-1;
}
}
}
closedir(dirp);
return pid;
}
int main(int argc, char **argv)
{
int pid = get_process_pid_by_name(process_name);
printf("%d\n", pid);
off64_t addr=0;
scanf("%p", &addr);
printf("%p\n", addr);
string process_mem_file_path = "/proc/"+to_string(pid)+"/mem";
printf("%s\n", process_mem_file_path.c_str());
int fd=open(process_mem_file_path.c_str(), O_RDWR);
lseek64(fd,addr,SEEK_SET);
char buff[200];
memset(buff, 0, sizeof(buff));
ssize_t size=read(fd, &buff,30);
printf("%d\n", size);
if(size>0)printf("%s\n", buff);
else printf("something is wrong!\n");
memset(buff, 0, sizeof(buff));
scanf("%s", buff);
lseek64(fd,addr,SEEK_SET);
write(fd,buff,strlen(buff));
close(fd);
return 0;
}
makefie:
all:little_game read_mem
little_game:little_game.cpp
g++ little_game.cpp -o little_game
read_mem:read_mem.cpp
g++ read_mem.cpp -o read_mem
clean:
del little_game read_mem