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;