Get Sysent Address On FreeBSD

Published on 2007 - 06 - 18

FreeBSD的sysent表的地址在内核中可以直接使用,是个全局变量.可以直接hook.
但是如果是ring3下Patch on fly呢?和linux一样,都是读取/dev/kmem或者/dev/mem
开始走了点弯路,以为和linux一样需要到内核函数代码中去查找,于是做了如下分析( 可见不google自以为是的坏处:( )


FreeBSD# uname -a
FreeBSD FreeBSD.0x1057 6.0-RELEASE FreeBSD 6.0-RELEASE #0: Thu Nov 3 09:36:13 UTC 2005 //系统是6.0的FREEBSD root@x64.samsco.home:/usr/obj/usr/src/sys/GENERIC i386
FreeBSD# gdb -q /boot/kernel/kernel
(no debugging symbols found)...(gdb) p &sysent
$1 = (<data variable, no debug info> ) 0xc08bdf60 //sysent地址
(gdb) q
FreeBSD# objdump -d /boot/kernel/kernel | grep 0xc08bdf60 //查找sysent地址在内核中出现的位置
c063ec4a: 8b 90 60 df 8b c0 mov 0xc08bdf60(%eax),%edx
c063ec6e: 89 90 60 df 8b c0 mov %edx,0xc08bdf60(%eax)
c063ecac: 89 90 60 df 8b c0 mov %edx,0xc08bdf60(%eax)
FreeBSD# gdb -q /boot/kernel/kernel
(no debugging symbols found)...(gdb) disass 0xc063ec40 //反汇编此地址
Dump of assembler code for function syscall_register: //在函数体syscall_register内
0xc063ebc4 <syscall_register+0>: push %ebp
0xc063ebc5 <syscall_register+1>: mov %esp,%ebp
0xc063ebc7 <syscall_register+3>: push %edi
0xc063ebc8 <syscall_register+4>: push %esi
.....
.....
0xc063ec36 <syscall_register+114>: cmpl $0xc063ebb4,0xc08bdf64(%eax)
0xc063ec40 <syscall_register+124>: jne 0xc063ec8b <syscall_register+199>
0xc063ec42 <syscall_register+126>: mov (%ebx),%eax
0xc063ec44 <syscall_register+128>: lea (%eax,%eax,2),%eax
0xc063ec47 <syscall_register+131>: shl $0x2,%eax
0xc063ec4a <syscall_register+134>: mov 0xc08bdf60(%eax),%edx //找到
0xc063ec50 <syscall_register+140>: mov %edx,(%esi)
---Type <return> to continue, or q <return> to quit---q
Quit
(gdb) x/xw (syscall_register+134)
0xc063ec4a <syscall_register+134>: 0xdf60908b //字节码是这样的阿
(gdb) q
FreeBSD#


于是速度动手,写了个第一版本获取sysent地址的代码:


#include <fcntl.h>
#
include <kvm.h>
#
include <limits.h>
#
include <nlist.h>
#
include <stdio.h>
#
include <sys/types.h>

#define SIZE 0x100 // 搜索0x100个字节

int
main(int argc
, char
argv[])
{
char errbuf[_POSIX2_LINE_MAX]
, p;
kvm_t
kd;
struct nlist nl[]
= { {NULL}, {NULL}, };
unsigned char syscall_register_code[SIZE];
// 保存原始函数字节码
unsigned sct;

kd
= kvm_openfiles(NULL, NULL, NULL, O_RDWR, errbuf);// 打开/dev/mem
if (kd == NULL) {
fprintf(stderr, "ERROR: %s\n", errbuf);
exit(-1);
}

nl[
0].n_name = "syscall_register";

if (kvm_nlist(kd, nl) < 0) { // 查找syscall_register
fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd));
exit(-1);
}

if (!nl[0].n_value) {
fprintf(stderr, "ERROR: Symbol %s not found\n", nl[0].n_name);
exit(-1);
}

if (kvm_read(kd, nl[0].n_value, syscall_register_code, SIZE) < 0) { // 保存字节码
fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd));
exit(-1);
}

p
= (char ) memmem(syscall_register_code, SIZE, "\x8b\x90", 2); // 查找 mov 0xc08bdf60(%eax),%edx
sct =
(unsigned)(p+2);

printf ("sysent at 0x%x\n", sct);

if (kvm_close(kd) < 0) {
fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd));
exit(-1);
}

exit(0);
}


结果也如人所愿:


FreeBSD# gcc -o getsysent getsysent.c -lkvm
FreeBSD# ./getsysent
sysent at 0xc08bdf60


到这里突然发现一个问题,如果能直接从/dev/mem获取syscall_register符号的地址,那么也就能直接获取sysent


#include <fcntl.h>
#
include <kvm.h>
#
include <limits.h>
#
include <nlist.h>
#
include <stdio.h>
#
include <sys/types.h>

int
main(int argc
, char
argv[])
{
char errbuf[_POSIX2_LINE_MAX];
kvm_t
*kd;
struct nlist nl[]
= { {NULL}, {NULL}, };

if(argc != 2) return 0;

kd
= kvm_openfiles(NULL, NULL, NULL, O_RDWR, errbuf);// 打开/dev/mem
if (kd == NULL) {
fprintf(stderr, "ERROR: %s\n", errbuf);
exit(-1);
}

nl[
0].n_name = argv[1];

if (kvm_nlist(kd, nl) < 0) {
fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd));
exit(-1);
}

if (!nl[0].n_value) {
fprintf(stderr, "ERROR: Symbol %s not found\n", nl[0].n_name);
exit(-1);
}

printf ("%s at 0x%x\n", nl[0].n_name, nl[0].n_value);

if (kvm_close(kd) < 0) {
fprintf(stderr, "ERROR: %s\n", kvm_geterr(kd));
exit(-1);
}

exit(0);
}


结果:


FreeBSD# gcc -o getsysent2 getsysent2.c -lkvm
FreeBSD# ./getsysent2 sysent
sysent at 0xc08bdf60
FreeBSD# ./getsysent2 syscall
syscall at 0xc0807c90



也成功了,看来在FreeBSD里使用kvm库对mem等操作果然方便很多:)


Comments
Write a Comment