Linux内核私闯过程地址空间并修改过程内存的办法

发布时间: 2019-10-08 12:30:18 来源: 互联网 栏目: LINUX 点击:

这篇文章重要简介了Linux内核私闯过程地址空间并修改过程内存的办法,文中经过过程示例代码简介的异常详细,对大年夜家的进修或许任务具有必定的参考进修价值,须要的同伙们下面随着小编来一路进修进修吧

过程地址空间的隔离 是现代操作体系的一个明显特点。这也是差别于 “现代”操作体系 的明显特点。

过程地址空间隔离意味着过程P1没法以随便的方法拜访过程P2的内存,除非这块内存被声明是共享的。

这异常轻易懂得,我举个例子。

我们知道,在原始野人社会,是没有家庭的不雅念的,一切的资本都是部落内共享的,一切的野人都可以以随便任性的方法在随便任性时间和任何其他野人交互。类似Dos如许的操作体系就是如许的,内存地址空间并没有隔离。过程可以随便拜访其它过程的内存。

后来有了家庭的不雅念,家庭的资本被隔离,人们便不克不及私闯平易近宅了,人们没法以随便的方法进入他人的家用他人的器械,除非这是主人许可的。操作体系进入现代形式后,过程也有了类似家庭的概念。

但家庭的概念是虚拟的,人们只是遵守商定而不去破坏他人的家庭。房子作为一个物理基本举措措施,保护着家庭。在操作体系中,家庭类似于虚拟地址空间,而房子就是页表。

邻居不克不及闯入你的房子,但警察可以,当局公事人员以公道的来由也能够。所谓的特权管理机构只需来由充分,便可以进入浅显人家的房子,touch这家人的器械。关于操作体系而言,这就是内核可以做的事,内核可以拜访随便任性过程的地址空间。

固然了,内核其实不会无故私闯平易近宅,就像警察不会随便闯入他人家里一样。

然则,你可让内核成心这么做,做点无赖的任务。

我们来试一下,先看一个法式榜样:

// test.c
// gcc test.c -o test
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>

int main()
{
  char* addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  strcpy(addr, "Zhejiang wenzhou pixie shi");

  printf("addr: %lu  pid:%d\n", addr, getpid());

  printf("before:%s \n", addr);

 getchar();

  printf("after:%s\n", addr);

  return 0;
}

这个法式榜样的输入异常简单,before和after都邑输入 “Zhejiang wenzhou pixie shi”,然则我们想把这句话给改了,怎样办呢?明显,test过程假设本身不改它,那就没辙…然则可让内核强迫改啊,让内核私闯平易近宅就是了。

接上去我写一个内核模块:

// test.c
// make -C /lib/modules/`uname -r`/build SUBDIRS=`pwd` modules
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/module.h>

static int pid = 1;
module_param(pid, int, 0644);

static unsigned long addr = 0;
module_param(addr, long, 0644);

// 根据一个过程的虚拟地址找到它的页表,相当于找到这家人的房子地址,然后闯入!
static pte_t* get_pte(struct task_struct *task, unsigned long address)
{
 pgd_t* pgd;
 pud_t* pud;
 pmd_t* pmd;
 pte_t* pte;
 struct mm_struct *mm = task->mm;

 pgd = pgd_offset(mm, address);
 if(pgd_none(*pgd) || pgd_bad(*pgd))
 return NULL;

 pud = pud_offset(pgd, address);
 if(pud_none(*pud) || pud_bad(*pud))
 return NULL;

 pmd = pmd_offset(pud, address);
 if(pmd_none(*pmd) || pmd_bad(*pmd))
 return NULL;

 pte = pte_offset_kernel(pmd, address);
 if(pte_none(*pte))
 return NULL;

 return pte;
}

static int test_init(void)
{
 struct task_struct *task;
 pte_t* pte;
 struct page* page;

 // 找到这家人
 task = pid_task(find_pid_ns(pid, &init_pid_ns), PIDTYPE_PID);
 // 找到这家人住在哪里
 if(!(pte = get_pte(task, addr)))
 return -1;

 page = pte_page(*pte);
 // 强行闯入
 addr = page_address(page);
 // sdajgdoiewhgikwnsviwgvwgvw
 strcpy(addr, (char *)"rain flooding water will not get fat!");
 // 事了拂袖去,深藏功与名
 return 0;
}

static void test_exit(void)
{
}

module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");

来来来,我们来试一下:

[root@10 page_replace]# ./test
addr: 140338535763968  pid:9912
before:Zhejiang wenzhou pixie shi

此时,我们加载内核模块test.ko

[root@10 test]# insmod test.ko pid=9912 addr=140338535763968
[root@10 test]#

在test过程拍入回车:

[root@10 page_replace]# ./test
addr: 140338535763968  pid:9912
before:Zhejiang wenzhou pixie shi

after:rain flooding water will not get fat!
[root@10 page_replace]#

明显,“浙江温州皮鞋湿”被改成了“下雨进水不会胖”。

细心看下面那个内核模块的 get_pte 函数,这个函数要想写对,你必须对你想践踏的过程地点的机械的MMU有必定的懂得,比如是32位体系照样64位体系,是3级页表照样4级页表或许5级?这…

Linux的可玩性在于你可以本身着手,又可让人代劳。比如,获得一个过程的虚拟地址的页表项指导的物理页面,便可以直接取得。

有如许的API吗?有啊,别忘了一切皆文件,正好在proc文件体系中,就有这么一个文件:

/proc/$pid/pagemap

读取这个文件,取得的就是过程虚拟地址的页表项,下图截自内核Doc:

Documentation/vm/pagemap.txt

 

虚拟地址空间是每过程的,而物理地址空间则是一切过程共享的。换句话说,物理地址是全局的。

如今,根据Documentation/vm/pagemap.txt的解释,写一个法式榜样,获得随便任性过程随便任性虚拟地址的全局物理地址:

// getphys.c
// gcc getphys -o getphys
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
 int fd;
 int pid;
 unsigned long pte;
 unsigned long addr;
 unsigned long phy_addr;
 char procbuf[64] = {0};

 pid = atoi(argv[1]);
 addr = atol(argv[2]);

 sprintf(procbuf, "/proc/%d/pagemap", pid);

 fd = open(procbuf, O_RDONLY);
 size_t offset = (addr/4096) * sizeof(unsigned long);
 lseek(fd, offset, SEEK_SET);

 read(fd, &pte, sizeof(unsigned long));

 phy_addr = (pte & ((((unsigned long)1) << 55) - 1))*4096 + addr%4096;
 printf("phy addr:%lu\n", phy_addr);

 return 0;
}

随后,我们修改内核模块:

#include <linux/module.h>

static unsigned long addr = 0;
module_param(addr, long, 0644);

static int test_init(void)
{
 strcpy(phys_to_virt(addr), (char *)"rain flooding water will not get fat!");
 return 0;
}

static void test_exit(void)
{
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

先运转test,然后根据test的输入作为getphys的输入,再根据getphys的输入作为内核模块test.ko的输入,就成了。还记得吗?这不就是管道连接多个法式榜样的风格吗?

输入一个物理地址,然后把它改了,仅此罢了。经过过程虚拟地址获得页表的操作曾经过用户态的pagemap文件的读取并解析代劳了。

以上就是本文的全部内容,欲望对大年夜家的进修有所赞助,也欲望大年夜家多多支撑我们。

本文标题: Linux内核私闯过程地址空间并修改过程内存的办法
本文地址: http://yourctp.com/os/linux/277845.html

假设认为本文对您有所赞助请赞助本站

付出宝扫一扫赞助微信扫一扫赞助

  • 付出宝扫一扫赞助
  • 微信扫一扫赞助
  • 付出宝先领红包再赞助
    声明:凡注明"本站原创"的一切文字图片等材料,版权均属编程客栈一切,迎接转载,但务请注明出处。
    应用PXE主动装置CentOS7.6的教程详解CentOS8 网卡设备文件
    Top