20210418(__start)

20210418(__start)

入口点理解

借一个main目标文件的地址信息

从代码段寄存器分析

readelf -h main

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V6acRDsm-1618732210744)(C:\Users\11066\AppData\Roaming\Typora\typora-user-images\1618727241953.png)]

可以看到程序入口点地址为0x530
objdump -d main
列出汇编信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uSEsdjFs-1618732210747)(C:\Users\11066\AppData\Roaming\Typora\typora-user-images\1618727480093.png)]

然后在汇编信息(主要看代码段)
看到地址入口点为_start
然后查看代码段寄存器(cs)的值
file /sbin/init查看机器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dj12mwGq-1618732210749)(C:\Users\11066\AppData\Roaming\Typora\typora-user-images\1618727680130.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ezaYiTQ7-1618732210751)(C:\Users\11066\AppData\Roaming\Typora\typora-user-images\1618728775419.png)]

但是这里不得不再点一下,那就是我们对段的支持是在  CPU  上体现的,而不是在内存中实现了段,

所以事实上我们使用的段其实是一个逻辑概念,即是我们自己定义的,

再说白了,我定义一个段,我说它是数据段那它就是数据段,我说它是代码段那么它就是代码段,

它们其实都是一块连续的内存而已,至于为什么要区分为数据段和代码段,

很明显,是用来给我们编程提供方便的,即我们在自己的思想上或者说是编码习惯上规定,

数据放数据段中,代码放代码段中 。而我们在使用数据段的时候,为了方便或者说是代码的编写方便起见,

我们一般把数据段的段地址放在  DS  寄存器中,当然,如果你硬要觉得  DS  不顺眼,那你可以换个  ES  也是一样的,
————————————————
版权声明:本文为CSDN博主「qq_35212671」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35212671/article/details/52770730
可以武断的得到入口点的物理地址
Inaddr=51<<4+0x530

examine的用法

查看当前程序栈的内容:x/10x $sp-->打印stack的前10个元素
2、查看程序栈的信息:info frame
3、查看当前程序栈的参数:info args
4、info locals
5、查看当前寄存器的值info registers
6、查看当前栈帧中的异常处理器:info catch(exception handlers)

直接查看物理地址的方式

Linux内核里提供的/dev/mem驱动,为我们读写内存物理地址,提供了一个渠道。下面讲述2种利用mem设备文件进行物理地址读写的方法,一种是设备驱动的方法,另一种是系统调用的方法。

首先我们看下mem这个设备文件,/dev/mem是linux下的一个字符设备,源文件是~/drivers/char/mem.c,这个设备文件是专门用来读写物理地址用的。里面的内容是所有物理内存的地址以及内容信息。通常只有root用户对其有读写权限。
#include <stdio.h>
#include <fcntl.h>
int main(void)
{
       int fd;
       char *rdbuf;
       char *wrbuf = "butterfly";
       int i;
       fd = open("/dev/mem",O_RDWR);
       if(fd < 0)
       {
         printf("open /dev/mem failed.");
       }
       read(fd,rdbuf,10);

       for(i = 0;i < 10;i++)
       {
         printf("old mem[%d]:%c\n",i,*(rdbuf + i));
       }
       lseek(fd,5,0);
       write(fd,wrbuf,10);
       lseek(fd,0,0);//move f_ops to the front
       read(fd,rdbuf,10);
       for(i = 0;i < 10;i++)
       {
         printf("new mem[%d]:%c\n",i,*(rdbuf + i));
       }

       return 0;
}

2\

hexedit /dev/mem
Linux下/dev/mem和/dev/kmem的区别:
/dev/mem: 物理内存的全镜像。可以用来访问物理内存。
/dev/kmem: kernel看到的虚拟内存的全镜像。可以用来访问kernel的内容。
作用:
 /dev/mem用来访问物理IO设备,比如X用来访问显卡的物理内存,或嵌入式中访问GPIO。用法一般就是open,然后mmap,接着可以使用map之后的地址来访问物理内存。这   其实就是实现用户空间驱动的一种方法。
/dev/kmem后者一般可以用来查看kernel的变量,或者用作rootkit之类的。参考1和2描述了用来查看kernel变量这个问题。



/dev/mem用法举例:
比如驱动(内核空间)中
vir_addr = kmalloc( size, GFP_KERNEL | GFP_DMA )
 phy_addr = __pa( vir_addr );
申请了一段内存,然后得到__pa()得到它的物理地址,然后将该物理地址传到用户空间
然后在应用程序(用户空间)中
 int map_fd = open("/dev/mem", O_RDWR);
 map_addr = mmap(0,  size,  PROT_READ|PROT_WRITE, MAP_SHARED,  map_fd,  phy_addr);

这样就可以得到 在应用程序中 访问驱动程序中申请的内存的指针map_addr,可以实现应用程序和驱动程序访问同段内存,节省开销,实现内存共享

mmap

mmap的函数原型为:void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offset),该函数定义在/usr/include/sys/mman.h中,使用时要包含:#include<sys /mman.h>,mmap()用来将某个文件中的内容映射到进程的地址空间,对该空间的存取即是对该文件内容的读写。参数说明如下:

start:指向欲映射到的地址空间的起始地址,通常设为null或者0.表示让系统融自动选定地址,映射成功后该地址会返回。

length:表示映射的文件内容的大小,以字节为单位。

prot:表示映射区域的保护方式,有如下四种组合:
--PROT_EXEC 映射区域可执行 ,
--PROT_READ 映射区域可读 ,
--PROT_WRITE 映射区域可写,
--PROT_NONE 映射区域不能被访问

flags:映射区域的一些特性,主要有:
--MAP_FIXED 如果映射不成功则出错返回,
--MAP_SHARED 对映射区域的写入数据会写回到原来的文件
--MAP_PRIVATE 对映射区域的写入数据不会写回原来的文件
--MAP_ANONYMOUS
--MAP_DENYWRITE 只允许对映射区域的写入操作,其他对文件直接写入的操作将被拒绝
--MAP_LOCKED 锁定映射区域

在调用mmap()时,必须要指定MAP_SHARED或MAP_PRIVATE。
fd:open()返回的文件描述符。
offset:为被映射文件的偏移量,表示从文件的哪个地方开始映射,一般设置为0,表示从文件的最开始位置开始映射。offset必须是分页大小(4096字节)的整数倍。

/dev/mem的用法举例

比如驱动(内核空间)中
vir_addr = kmalloc( size, GFP_KERNEL | GFP_DMA )
 phy_addr = __pa( vir_addr );
申请了一段内存,然后得到__pa()得到它的物理地址,然后将该物理地址传到用户空间
然后在应用程序(用户空间)中
 int map_fd = open("/dev/mem", O_RDWR);
 map_addr = mmap(0,  size,  PROT_READ|PROT_WRITE, MAP_SHARED,  map_fd,  phy_addr);

这样就可以得到 在应用程序中 访问驱动程序中申请的内存的指针map_addr,可以实现应用程序和驱动程序访问同段内存,节省开销,实现内存共享
这是一种方法,把内核空间的内存映射到用户空间,内核空间内存-->物理地址(PA)-->用户空间  通过/dev/mem 和系统调用mmap

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <sys/mman.h>

int page_size;
#define PAGE_SIZE page_size
#define PAGE_MASK (~(PAGE_SIZE-1))

void get_var (unsigned long addr) {
	off_t ptr = addr & ~(PAGE_MASK);
	off_t offset = addr & PAGE_MASK;
	int i = 0;
	char *map;
	static int kfd = -1;

	kfd = open("/dev/kmem",O_RDONLY);
	if (kfd < 0) {
		perror("open");
		exit(0);
	}

	map = mmap(NULL,PAGE_SIZE,PROT_READ,MAP_SHARED,kfd,offset);
	if (map == MAP_FAILED) {
		perror("mmap");
		exit(-1);
	}
	printf("%s\n",map+ptr);

	return;
}

int main(int argc, char **argv)
{
	FILE *fp;
	char addr_str[11]="0x";
	char var[51];
	unsigned long addr;
	char ch;
	int r;
	
	if (argc != 2) {
		fprintf(stderr,"usage: %s System.map\n",argv[0]);
		exit(-1);
	}

	if ((fp = fopen(argv[1],"r")) == NULL) {
		perror("fopen");
		exit(-1);
	}

	do {
		r = fscanf(fp,"%8s %c %50s\n",&addr_str[2],&ch,var);
		if (strcmp(var,"modprobe_path")==0)
			break;
	} while(r > 0);
	if (r < 0) {
		printf("could not find modprobe_path\n");
		exit(-1);
	}
	page_size = getpagesize();
	addr = strtoul(addr_str,NULL,16);
	printf("found modprobe_path at (%s) %08lx\n",addr_str,addr);
	get_var(addr);
}

分析一段代码


if((fdMem = open("/dev/mem", O_RDWR | O_SYNC)) == -1)
      {
	fprintf(stderr, "Unable to open the /dev/mem interface !\n");
	return;
      }
 
 
    map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fdMem, GPIO_ADDR & ~MAP_MASK);
    if (map_base == (void *) -1)
      {
	fprintf(stderr, "Unable to map 0x%08x address\n", GPIO_ADDR);
	close (fdMem);
	return;
      }
 
    // Add offset for init (VD0) => PORTC8
    virt_addr = map_base + 0x20;
 
    // Configure GPIO
    read_result  = *((unsigned long *) virt_addr);
    read_result &= (~(0x03 << 16));
    read_result |= 0x01 << 16;
    *((unsigned long *) virt_addr) = read_result;
 
    // Add offset for VD0
    virt_addr = map_base + 0x24;

map_base + 0x20;

// Configure GPIO
read_result  = *((unsigned long *) virt_addr);
read_result &= (~(0x03 << 16));
read_result |= 0x01 << 16;
*((unsigned long *) virt_addr) = read_result;

// Add offset for VD0
virt_addr = map_base + 0x24;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值