虚假的调试-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
文章目录