前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何读取Linux进程中的代码段和数据段

如何读取Linux进程中的代码段和数据段

作者头像
yifei_
发布2022-11-14 14:46:16
3.8K0
发布2022-11-14 14:46:16
举报
文章被收录于专栏:yifei的专栏

Linux下的程序的文件格式是ELF,里面分了各种段,有代码段、数据段、等。当运行这个程序时,系统也会给这个进程创建虚拟内存,然后把ELF中的数据分别加载到内存中的对应位置。本文整理了用cpp程序读取内存中的代码段和rodata数据段的方法。

Ptrace

Ptrace是一个Linux系统提供的一个功能强大的API接口,可以让一个进程跟踪或控制另一个进程,调试程序GDB就是在这个系统调用的基础上开发的。

代码语言:javascript
复制
long ptrace(enum  ptrace_request request,pid_t pid,void addr, void *data);

参数request 控制ptrace函数的行为,定义在sys/ptrace.h中。 参数pid 指定trace的进程号。 以上两个参数是必须的,之后两个参数分别为地址和数据,其含义由参数request控制。

/proc/pid/mem

mem是内核创建的虚拟文件,是Linux的”一切皆文件”在进程上的体现,但是这个文件无法直接进行读取,需要先利用ptrace进行绑定操作。 用ptrace绑定之后就可以用read来读取这个“文件”了,但是要注意输入读取的地址不对,也读不出数据来。

/proc/pid/maps

下图是Linux的进程内存布局,这是系统给进程虚拟出的一个内存空间,并不是实际的物理内存,maps文件中就记录了虚拟内存的的每段地址分别对应什么数据。

maps文件的内容可以通过cat命令直接查看:

代码语言:javascript
复制
root@yifei:~/blog_backup/source/_notes# cat /proc/32435/maps
55ad31b9f000-55ad31ba0000 r-xp 00000000 08:08 2755760    /root/cppSpace/test/while
55ad31d9f000-55ad31da0000 r--p 00000000 08:08 2755760    /root/cppSpace/test/while
55ad31da0000-55ad31da1000 rw-p 00001000 08:08 2755760    /root/cppSpace/test/while
55ad31da1000-55ad31e02000 rw-p 00000000 00:00 0 
55ad327c7000-55ad327e8000 rw-p 00000000 00:00 0          [heap]
7fe825cc6000-7fe825cdd000 r-xp 00000000 08:08 8919115    /lib/x86_64-linux-gnu/libgcc_s.so.1
7fe825cdd000-7fe825edc000 ---p 00017000 08:08 8919115    /lib/x86_64-linux-gnu/libgcc_s.so.1
7fe825edc000-7fe825edd000 r--p 00016000 08:08 8919115    /lib/x86_64-linux-gnu/libgcc_s.so.1
7fe825edd000-7fe825ede000 rw-p 00017000 08:08 8919115    /lib/x86_64-linux-gnu/libgcc_s.so.1
7fe825ede000-7fe82607b000 r-xp 00000000 08:08 8917704    /lib/x86_64-linux-gnu/libm-2.27.so
7fe82607b000-7fe82627a000 ---p 0019d000 08:08 8917704    /lib/x86_64-linux-gnu/libm-2.27.so
7fe82627a000-7fe82627b000 r--p 0019c000 08:08 8917704    /lib/x86_64-linux-gnu/libm-2.27.so
7fe82627b000-7fe82627c000 rw-p 0019d000 08:08 8917704    /lib/x86_64-linux-gnu/libm-2.27.so
7fe82627c000-7fe826463000 r-xp 00000000 08:08 8917641    /lib/x86_64-linux-gnu/libc-2.27.so
7fe826463000-7fe826663000 ---p 001e7000 08:08 8917641    /lib/x86_64-linux-gnu/libc-2.27.so
7fe826663000-7fe826667000 r--p 001e7000 08:08 8917641    /lib/x86_64-linux-gnu/libc-2.27.so
7fe826667000-7fe826669000 rw-p 001eb000 08:08 8917641    /lib/x86_64-linux-gnu/libc-2.27.so
7fe826669000-7fe82666d000 rw-p 00000000 00:00 0 
7fe82666d000-7fe8267e6000 r-xp 00000000 08:08 20316746   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7fe8267e6000-7fe8269e6000 ---p 00179000 08:08 20316746   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7fe8269e6000-7fe8269f0000 r--p 00179000 08:08 20316746   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7fe8269f0000-7fe8269f2000 rw-p 00183000 08:08 20316746   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7fe8269f2000-7fe8269f6000 rw-p 00000000 00:00 0 
7fe8269f6000-7fe826a1d000 r-xp 00000000 08:08 8917613    /lib/x86_64-linux-gnu/ld-2.27.so
7fe826bfc000-7fe826c02000 rw-p 00000000 00:00 0 
7fe826c1d000-7fe826c1e000 r--p 00027000 08:08 8917613    /lib/x86_64-linux-gnu/ld-2.27.so
7fe826c1e000-7fe826c1f000 rw-p 00028000 08:08 8917613    /lib/x86_64-linux-gnu/ld-2.27.so
7fe826c1f000-7fe826c20000 rw-p 00000000 00:00 0 
7ffed1e23000-7ffed1e44000 rw-p 00000000 00:00 0          [stack]
7ffed1fdc000-7ffed1fdf000 r--p 00000000 00:00 0          [vvar]
7ffed1fdf000-7ffed1fe1000 r-xp 00000000 00:00 0          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0  [vsyscall]

其中第一列是虚拟内存的地址区间。第二列是对这段内存的权限,类似查看文件时的权限。最后一列是这段虚拟内存存储的对应数据。 这个文件的前三列分别是代码段、rodata数据段、和普通数据段,可以看到代码段的权限是读和执行,rodata数据段是只读,普通数据段可读写。

用程序读取内存的代码段和rodata数据段

以tcpdump程序为例,用程序读取代码段和radata的过程如下: 1.查看tcpdump的进程ID。 2.运行自己写的程序,分别输入进程PID和代码段的地址。

code

代码语言:javascript
复制
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <errno.h>
using namespace std;

int main(int argc, char *argv[]){

	/*get argvs*/
	off_t start_addr;
	int len;
	pid_t pid;
    string s1,s2,s3,s4;
    cout<<"input pid and start_addr:  "<<endl;
    cin>>s1>>s2;
    s3="0x001";
    s4="data.txt";

	pid = atoi(s1.c_str());
	sscanf(s2.c_str(), "%x", &start_addr);
	sscanf(s3.c_str(), "%x", &len);

	/*attach the memory of pid*/
    //绑定到pid进程上
	int ptrace_ret;
	ptrace_ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
	if (ptrace_ret == -1) {
		fprintf(stderr, "ptrace attach failed.\n");
		perror("ptrace");
		return -1;
	}else{
        cout<<"attach sucess"<<endl;
    }

	if (waitpid(pid, NULL, 0) == -1) {
		fprintf(stderr, "waitpid failed.\n");
		perror("waitpid");
		ptrace(PTRACE_DETACH, pid, NULL, NULL);
		return -1;
	}

	/*open /proc/<pid>/mem to attach the memory*/
	int fd;
	char path[256] = {0};
	sprintf(path, "/proc/%d/mem", pid);
	fd = open(path, O_RDWR);
	if (fd == -1) {
		fprintf(stderr, "open file failed.\n");
		perror("open");
		ptrace(PTRACE_DETACH, pid, NULL, NULL);
		return -1;
	}else{
        cout<<"open /proc/"<<pid<<"/mem sucess"<<endl;
    }

	/*seek the file pointer*/
	off_t off;
	off = lseek(fd, start_addr, SEEK_SET);
	if (off == (off_t)-1) {
		fprintf(stderr, "lseek failed.\n");
		perror("lseek");
		ptrace(PTRACE_DETACH, pid, NULL, NULL);
		close(fd);
		return -1;
	}else{
        cout<<"lseek sucess"<<endl;
    }

	unsigned char *buf = (unsigned char *)malloc(100000);
    int rd_sz;
    while(rd_sz=read(fd,buf,10)){
	if(rd_sz<10){
		cout<<rd_sz<<endl;
		perror("read");
		break;
	}

    for(int i=0;i<10;i++){
		printf("%x",buf[i]);
    }
	ptrace(PTRACE_DETACH, pid, NULL, NULL);
	free(buf);
	close(fd);
	return 0;
}

参考

欢迎与我分享你的看法。 转载请注明出处:http://taowusheng.cn/

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-09-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Ptrace
  • /proc/pid/mem
  • /proc/pid/maps
  • 用程序读取内存的代码段和rodata数据段
  • code
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档