当前位置:   article > 正文

_黑客大神秘籍:Linux下Hook方式汇总

linux下微信hook注入

导语

Linux下的Hook方式,从ring3到ring0,姿势很多,但其实是互通的,这里总结自己实现的几个。

/****** Ring3 ******/
LD_PRELOAD劫持.so
ptrace API调试技术Hook
PLT劫持
/******* Ring 0 *******/
针对系统调用的hook
--首先获得sys_call_table
利用sys函数的嵌套实现hook调用的子函数
修改系统调用的前几个字节为jmp之类的指令(内联

• 网上很多教程是针对Linux2.6左右的,很多方法需要自己重新摸索,记录踩坑。

– 注: 以下所有代码在Linux 5.0.3. x86_64内核调试通过。

LD_PRELOAD劫持.so

• LDPRELOAD是一个Linux下的动态链接的程序的环境变量,几乎我们用到的函数实现来自于glibc,.so文件是glibc编译得到的库,类似于Win下的DLL。而LDPRELOAD变量优先于相关配置指定链接到哪个.so文件。

• 一旦我们可以控制该变量,也就可以决定程序调用函数时会做什么。

• 实例展示

– 目标文件target.c

#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
if( strcmp(argv[1], "password") )
{
printf("Incorrect passwordn");
}
else
{
printf("Correct passwordn");
}
return 0;
}
//gcc target.c -o target

– 伪造的.so文件preload.c

#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
typedef int(*Strcmp)(const char*, const char*);
int strcmp(const char* s1, const char* s2)
{
static void* handle = NULL;
static Strcmp org_strcmp = NULL;
if(!handle)
{ //解析得到真实的strcmp函数
handle = dlopen("libc.so.6", RTLD_LAZY);
org_strcmp = (Strcmp)dlsym(handle, "strcmp");
}
//做我们想做的
printf("Hacked by way of ld_preloadnnn");
//完成真实的功能
return org_strcmp(s1, s2);
}
//gcc -fPIC preload.c -shared -o preload.so -ldl

• 这种方式比较简单,前提是程序不是静态链接的。(不会再解析.so,程序已经包含了库函数的实现)。

• 防护方案

– 关闭LD_PRELOAD

ptrace API调试技术Hook

• ptrace是很多Linux平台下调试器实现的基础,包括syscall跟踪程序strace。

• ptrace可以实现调试程序、跟踪;但是一个进程只能被一个进程跟踪。所以无法在gdb或者其他程序调试的时候去ptrace一个程序,同样也无法在ptrace一个进程的时候,再去gdb调试。后者经常作为一个简单的反调试手段。

• 而且Linux下的攻防中,ptrace也由于自己的特殊性,常常是必争之地。

• 总体思路

ptrace attach目标进程
保存rip
控制跳转到mmap分配一段rwx内存
将一段机器码copy进去
控制跳转到机器码(可以以bin文件的形式)
恢复执行。

• 简单示例代码

– 首先需要知道一些函数在目标进程的地址,下面是已知pid获取libc基地址(读取/proc/pid/maps),和函数地址(dlsym)

size_t getLibcbase(int pid)
{
size_t libcAddr;
char* buf;
char* end;
char* mapfile[0x18];
sprintf(mapfile, Mapfile, pid);
FILE* fd = fopen(mapfile, "r");
if(!fd)
{
printf("open maps error!");
exit(1);
}
//search the libc-.....
buf = (char*) malloc(0x100);
do{
fgets(buf, 0x100, fd);
} while(!strstr(buf, "libc-"));
end = strchr(buf, '-');
libcAddr = strtol(buf, &end, 16);
printf("The process %d's libcbase is: 0x%lxn", pid, libcAddr);
fclose(fd);
return libcAddr;
}
size_t getFuncAddr(int pid, char* funcName)
{
size_t funcAddr;
char* buf;
char* end;
char* mapfile[0x18];
sprintf(mapfile, Mapfile, pid);
//get function offset from self process, the shared libc.so
funcAddr = (size_t)dlsym(0, funcName);
funcAddr -= getLibcbase(getpid());
funcAddr += libc_addr;
printf("function %s address is: 0x%lxn", funcName, funcAddr);
return funcAddr;
}

– main代码

• 为了得到存放shellcode的地址,我们需要先执行mmap,而执行mmap也需要一段可执行地址。这里其实我们可以直接使用libcbase。在libcbase处写入下面的opcode,其中int 0x3是为了发出信号,让我们知道该opcode执行完成。

call rax
int 0x3
;"xffxd0xcdx03"

• 实现mmap调用

– 备份数据,写入opcode,设置mmap参数

//save a bak of regs
ptrace(PTRACE_GETREGS, traced, 0, ®s_bak);
memcpy(®s, ®s_bak, sizeof(struct user_regs_struct));
//use libc_base to write our short hook code
buf.val = ptrace(PTRACE_PEEKTEXT, traced, libc_addr, 0);
hook_bak.val = buf.val;
memcpy(buf.chars, Call, 4);
ptrace(PTRACE_POKETEXT, traced, libc_addr, buf.val);
fd = open(argv[2], O_RDONLY);
fstat(fd, &sb);
if(fd < 0)
{
printf("open shellcode error!n");
exit(1);
}
shellcode = malloc(sb.st_size + 1);
read(fd, shellcode, sb.st_size);
regs.rax = mmap_addr;
//prepare for the mmap args
regs.rdi = 0;
regs.rsi = sb.st_size;
regs.rdx = 0x7;
regs.rcx = MAP_PRIVATE | MAP_ANONYMOUS;
regs.r8 = -1;
regs.r9 = 0;
regs.rip = libc_addr; //jmp to call rax
ptrace(PTRACE_SETREGS, traced, 0, ®s);
ptrace(PTRACE_CONT, traced, 0, 0);
//wait mmap is executed
waitpid(traced, &status, WUNTRACED);
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP){
printf("SIGTRAP receivednn");
}

• 这里有几个坑

– mmap的fd参数,必须是open打开的返回值,而不能是fopen这种。

– x64传参,顺序是rdi、rsi、rdx、rcx、r8、r9;有些地方可能会说第四个参数是r10,发现有误导。

void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);

– 由于我们open是在注入进程得到的fd,而mmap是在被hook进程执行的,所以这个fd是不可以被当作参数的。我们必须使用flag MAPPRIVATE | MAPANONYMOUS

• 往mmap返回的地址里写入shellcode,并执行。

ptrace(PTRACE_GETREGS, traced, 0, ®s);
tmp_addr = regs.rax;/
printf("We get address from mmap: 0x%lxn", tmp_addr);
copy_code(tmp_addr, shellcode, sb.st_size);
tmp_val = ptrace(PTRACE_PEEKTEXT, traced, tmp_addr, 0);
printf("the mapped val: 0x%lxn", tmp_val);
//jmp to shellcode from file
regs.rip = tmp_addr;
ptrace(PTRACE_SETREGS, traced, 0, ®s);
ptrace(PTRACE_CONT, traced, 0, 0);
//wait shellcode is executed
waitpid(traced, &status, WUNTRACED);
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP){
printf("SIGTRAP receivednn");
}
ptrace(PTRACE_SETREGS, traced, 0, ®s_bak);
ptrace(PTRACE_CONT, traced, 0, 0);
close(fd);

– 其中copy_code的实现通过POKETEXT,每次可以向目标进程任意地址写入long大小的值

void copy_code(size_t addr, char* shellcode, int len)
{
int i=0;
long word;
for(i=0; i<len; i+=sizeof(long)){
memcpy(&word, shellcode+i, sizeof(long));
if(ptrace(PTRACE_POKETEXT, traced, addr+i, word) == -1){
printerror();
exit(1);
}
}
}

• 我的shellcode,就是write(1, 'cba', 8),目标进程就是当前终端/bin/bash看下效果。

a4b40ec1-ea08-eb11-8da9-e4434bdf6706.png

PLT重定向劫持Hook

• 这个主要是利用ELF文件的,GOT和PLT的方式解决地址无关的链接.so文件的机制。

• 在第一次调用前,Got里是PLT的地址;一般在调用之后Got里会写入库函数的真实地址。

• PLT在text段,一般不可写;(所以迷,为啥有这一技术)

– 是因为ptrace可以无视'rwx',这也是为什么gdb可以修改text,下断点。

– 所以这个来说,算是plt和ptrace结合的一种方式。

• 一个简单的在PLT处下断,执行完操作后恢复的样例。这里主要解决的是代码重定位的问题,我们从/proc/pid/maps下读出Codebase即可。

#include <sys/reg.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#define Mapfile "/proc/%d/maps"
//用于获取plt内容
union pltval{
size_t val;
unsigned char chars[sizeof(size_t)];
};
void usage(char** argv){
printf("USAGE: n --- %s pid plt_offset n", argv[0]);
}
void printerror(){
printf("Status: %sn", strerror(errno));
}
void mod_handle(pid_t tracee, void* addr1, void* addr2)
{
union pltval buf;
buf.val = ptrace(PTRACE_PEEKDATA, tracee, addr1, 0);
printf("mod_handle: ");
printerror();
memcpy(buf.chars, "hooked", 6);
buf.chars[6] = 0;
ptrace(PTRACE_POKEDATA, tracee, addr1, buf.val);
printf("hook: ");
printerror();
buf.val = ptrace(PTRACE_PEEKDATA, tracee, addr2, 0);
printf("mod_handle: ");
printerror();
memcpy(buf.chars, "/hooked", 7);
buf.chars[7] = 0;
ptrace(PTRACE_POKEDATA, tracee, addr2, buf.val);
printf("/hooked: ");
printerror();
}
size_t getCodebase(pid_t pid)
{
size_t addr;
char buf[2 * sizeof(size_t)];
char* end;
char* mapfile[0x18];
sprintf(mapfile, Mapfile, pid);
int fd = open(mapfile, O_RDONLY);
if(fd == -1)
{
printf("open maps error!");
exit(1);
}
read(fd, buf, 2 * sizeof(size_t));
end = strchr(buf, '-');
addr = strtol(buf, &end, 16);
printf("The codebase is: 0x%lxn", addr);
close(fd);
return addr;
}
int main(int argc, char* argv[]){
pid_t tracee;
union pltval plt;
struct user_regs_struct regs;
siginfo_t si;
int status;
size_t plt_offset, plt_addr, bak;
if(argc < 2){
usage(argv);
exit(1);
}
tracee = atoi(argv[1]);
plt_offset = atoi(argv[2]);
//获取codebase
plt_addr = plt_offset + getCodebase(tracee);
printf("plt_addr ==> %lxn", plt_addr);
//attach the process
ptrace(PTRACE_ATTACH, tracee, 0, 0);
printf("Attach: ");
printerror();
wait(&status);
//获取目标的plt值,保存,修改,写入, 继续运行
plt.val = ptrace(PTRACE_PEEKDATA, tracee, plt_addr, 0);
bak = plt.val;
plt.chars[0] = 0xcc; //breakpoint
ptrace(PTRACE_POKEDATA, tracee, plt_addr, plt.val);
ptrace(PTRACE_CONT, tracee, 0, 0);
//监视有没有触发断点
while(1){
printf("Wait....n");
wait(&status);
printf("Done!n");
if(WIFEXITED(status)) break;
//获取regs和sig信息,判断是否到达plt
ptrace(PTRACE_GETSIGINFO, tracee, 0, &si);
ptrace(PTRACE_GETREGS, tracee, 0, ®s);
if((si.si_signo != SIGTRAP) || (regs.rip != (size_t)plt_addr + 1)){
ptrace(PTRACE_GETREGS, tracee, 0, ®s);
ptrace(PTRACE_CONT, tracee, 0, 0);
continue;
}
//hook & modify
mod_handle(tracee, (void*)argv[0], (void*)argv[1]);
//修改回原值
plt.val = bak;
ptrace(PTRACE_POKEDATA, tracee, plt_addr, plt.val);
//返回0xcc前
regs.rip -= 1;
ptrace(PTRACE_SETREGS, tracee, 0, ®s);
ptrace(PTRACE_SINGLESTEP, tracee, 0, 0);
wait(0);
ptrace(PTRACE_GETREGS, tracee, 0, ®s);
plt.chars[0] = 0xcc;
ptrace(PTRACE_POKEDATA, tracee, plt_addr, plt.val);
ptrace(PTRACE_CONT, tracee, 0, 0);
}
return 0;
}

Ring0级别的Hook

前置知识

• linux内核的编译

– 最好选择一个和自己虚拟机内核版本一致的源码,网上也很多教程。

– 再编译一个busybox的文件系统,为了方便添加文件。

• 模块编译

– makefile的基本格式(用于本机加载的模块)

obj-m += inter.o
CURRENT_PATH := $(shell pwd)
LINUX_KERNEL := $(shell uname -r)
LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL)
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean

– 交叉编译(本机和编译的内核不同)

obj-m += mod1.o
CURRENT_PATH := $(shell pwd)
LINUX_KERNEL := $(shell uname -r)
LINUX_KERNEL_PATH := /home/tree/kernel/linux-5.0.3
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean

• syscalltable

– 都知道linux通过int 0x80或者syscall都可以进入kernel完成系统调用,而我们需要把对应的系统调用号传给rax。两者最终还是用了system_call。

– int 0x80工作原理

1、系统维护一个叫做“向量中断表的”,每个int xx都会去对应的向量表的xx处
2、0x80对应系统调用的服务例程,记录了syscall的地址。
3、而eax的值,对应具体的系统调用号。

– syscall的具体实现,可以看到这里有syscalltable符号。

.globl system_call, buserr, trap, resume
.globl sys_call_table
................
................
ENTRY(system_call)
SAVE_ALL_SYS //保存
GET_CURRENT(%d1)
movel %d1,%a1
| save top of frame
movel %sp,%curptr@(TASK_THREAD+THREAD_ESP0)
| syscall trace? //有没有被ptrace跟踪
tstb %a1@(TINFO_FLAGS+2)
jmi do_trace_entry
cmpl #NR_syscalls,%d0
jcc badsys
syscall: //真实的执行系统调用
jbsr @(sys_call_table,%d0:l:4)@(0)
movel %d0,%sp@(PT_OFF_D0) | save the return value
ret_from_syscall:
|oriw #0x0700,%sr
movel %curptr@(TASK_STACK),%a1
movew %a1@(TINFO_FLAGS+2),%d0
jne syscall_exit_work
1: RESTORE_ALL

• 获得syscalltable地址的方式

– 由于syscall实现处有syscalltable的符号,我们可以从这里拿到地址。

1. 获取中断描述符表(IDT)的地址(使用C ASM汇编)
2. 从中查找0x80中断(系统调用中断)的服务例程(8*0x80偏移)
3. 搜索该例程的内存空间
4. 从其中获取sys_call_table(保存所有系统调用例程的入口地址)的地址

– 使用kallsymslookupname读取。该函数本身也是一个符号,如果没有导出就不能使用。

sys_call_table_addr = kallsyms_lookup_name("sys_call_table")

– 读取/proc/kallsyms文件。我的理解就是和/proc/pid/maps差不多特殊的一个文件,由内核动态生成,需要root权限,普通用户读到的全是0(但是加载模块也是需要root权限的,所以不是问题)

sudo cat /proc/kallsyms | grep sys_call_table

– 修改内核,添加EXPORTSYMBOL(syscalltable)或EXPORTSYMBOLGPL(syscall_table)。
这种方法适用于可以修改内核的情形。在可以修改内核的情况下,这是最简单的方式。

• 实战——hook系统调用 mkdir,我这里使用kallsymslookupname

//This kernel module locates the sys_call_table by kallsyms_lookup_name
#include<linux/init.h>
#include<linux/module.h>
#include<linux/moduleparam.h>
#include<linux/unistd.h>
#include<linux/sched.h>
#include<linux/syscalls.h>
#include<linux/string.h>
#include<linux/fs.h>
#include<linux/fdtable.h>
#include<linux/uaccess.h>
#include <linux/kallsyms.h>
#include<linux/rtc.h>
#include<linux/vmalloc.h>
#include <linux/slab.h>
//module macros
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("hook sys_mkdir");
//module constructor/destructor
typedef unsigned long (*sys_call_ptr_t)(void);
sys_call_ptr_t *_sys_call_table = NULL;
typedef asmlinkage long (*old_mkdir_t)(const char __user *pathname, umode_t mode);
old_mkdir_t old_mkdir = NULL;
// hooked mkdir function
asmlinkage long hooked_mkdir(const char __user *pathname, umode_t mode) {
printk("hooked sys_mkdir(), mkdir name: ");
printk(pathname);
old_mkdir(pathname, mode);
}
// memory protection shinanigans
unsigned int level;
pte_t *pte;
//obtain sys_call_table
static int get_sys_call_table(void){
unsigned long tmp_sys_call_table = 0;
int ans = 0;
tmp_sys_call_table = kallsyms_lookup_name("sys_call_table");
if(tmp_sys_call_table != 0)
{
ans = 1;
_sys_call_table = tmp_sys_call_table;
printk("[+] find sys_call_table: 0x%lxn", tmp_sys_call_table);
}
return ans;
}
// initialize the module
static int hooked_init(void) {
printk("+ Loading hook_mkdir modulen");
if(!get_sys_call_table()){
return 0;
}
// now we can hook syscalls ...such as uname
// first, save the old gate (fptr)
old_mkdir = (old_mkdir_t) _sys_call_table[__NR_mkdir];
// unprotect sys_call_table memory page
pte = lookup_address((unsigned long) _sys_call_table, &level);
// change PTE to allow writing
set_pte_atomic(pte, pte_mkwrite(*pte));
printk("+ unprotected kernel memory page containing sys_call_tablen");
// now overwrite the __NR_uname entry with address to our uname
_sys_call_table[__NR_mkdir] = (sys_call_ptr_t) hooked_mkdir;
printk("+ sys_mkdir hooked!n");
return 0;
}
static void hooked_exit(void) {
if(old_mkdir != NULL) {
// restore sys_call_table to original state
_sys_call_table[__NR_mkdir] = (sys_call_ptr_t) old_mkdir;
// reprotect page
set_pte_atomic(pte, pte_clear_flags(*pte, _PAGE_RW));
}
printk("+ Unloading hook_mkdir modulen");
}
/*entry/exit macros*/
module_init(hooked_init);
module_exit(hooked_exit);

– 效果(不知道为啥pathname参数不能输出)

b7b40ec1-ea08-eb11-8da9-e4434bdf6706.png

• systemcall函数内存内搜索syscall_table,实现execve的hook

– 这里注意这种方式在x86和x64上的区别。

1、x86或者x64的兼容模式:使用int 0x80,MSR寄存器地址为0xc0000083,宏MSR_CSTAR来代表. 使用sidt获取system_call地址
2、x64的long模式:使用syscall,MSR寄存器地址为0xc0000082,宏MSR_LSTAR来代表. 使用rdmsrl指令获取system_call地址
3、x86 sys_call_table的特征码 xffx14x85
4、x86_64下 sys_call_table的特征码 xffx14xc5

– 网上有一大堆x86的获取syscalltable的方法,我本来打算用下面的思路实现一个x64的。

1、在x64下,通过rdmsrl(MSR_LSTAR, xxxx)可以拿到entry_SYSCALL_64的地址。entry_SYSCALL_64的实现里会有sys_call_table的机器码。
2、所以有两种hook的思路
a、修改entry_SYSCALL_64起始的几个字节(内联hook)
b、找到sys_call_table,修改对应的系统调用。

– 但是在linux内核5.x里,entrySYSCALL64的实现改了,不再试图用过call syscalltable[index]的方式进行系统调用,而是引入了一个dosyscall64的符号。具体的看下面截取的源码

ENTRY(entry_SYSCALL_64)
UNWIND_HINT_EMPTY
.......
.......
/* IRQs are off. */
movq %rax, %rdi
movq %rsp, %rsi
call do_syscall_64 /* returns with IRQs disabled */

– 于是我找到了dosyscall64的实现,幸运的是在这里找到了对syscalltable的直接引用。

#ifdef CONFIG_X86_64
__visible void do_syscall_64(unsigned long nr, struct pt_regs *regs)
{
struct thread_info *ti;
enter_from_user_mode();
local_irq_enable();
ti = current_thread_info();
if (READ_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY)
nr = syscall_trace_enter(regs);
/*
* NB: Native and x32 syscalls are dispatched from the same
* table. The only functional difference is the x32 bit in
* regs->orig_ax, which changes the behavior of some syscalls.
*/
nr &= __SYSCALL_MASK;
if (likely(nr < NR_syscalls)) {
nr = array_index_nospec(nr, NR_syscalls);
regs->ax = sys_call_table[nr](regs); //这里sys_call_table的直接引用
}
syscall_return_slowpath(regs);
}
#endif

– 所以,显然我们就需要多一次的搜索,特征码可以在gdb里查或者看它的汇编指令。

1、在entry_SYSCALL_64里搜索do_syscall_64; 特征码'x48x89xe6xe8' <= mov rsi, rsp; call ....
2、在do_syscall_64里搜索sys_call_table; 特征码 'x48x8bx04xfd' <= mov rax []

– 通过entrySYSCALL64的call dosyscall64找到call dosyscall64地址

bdb40ec1-ea08-eb11-8da9-e4434bdf6706.png

– 通过dosyscall64对syscalltable的引用找到syscalltable

cbb40ec1-ea08-eb11-8da9-e4434bdf6706.png

– 代码实现

static void*
get_lstar_dosys_addr(void){
unsigned long lstar;
// temp variables for scan
unsigned int i;
unsigned char *off;
rdmsrl(MSR_LSTAR, lstar);
// print out int 0x80 handler
printk("[+] entry_SYSCALL_64 is at 0x%lxn", lstar);
// scan for known pattern(0xff14c5xx)
// pattern is just before sys_call_table address
for(i = 0; i <= PAGE_SIZE; i++) {
off = (char*)lstar + i;
if(*(off) == 0x48 && *(off+1) == 0x89 && *(off+2) == 0xe6) {
return (off + 3); //call do_syscall_64
}
}
return NULL;
}
static void*
get_lstar_dosys(void)
{
unsigned long* lstar_dosys_addr = get_lstar_dosys_addr();
if(lstar_dosys_addr != NULL) {
printk("[+] call_do_syscall_64 at: 0x%lxn", lstar_dosys_addr);
unsigned int offset = *(unsigned int*)((char*)lstar_dosys_addr + 1);
printk("[+] offset is: 0x%08xn", offset);
unsigned long base = 0xffffffff00000000;
return (void*)(base | ((unsigned long)lstar_dosys_addr + 5 + offset));
}
return NULL;
}
static void*
get_sys_sct_addr(unsigned long* do_syscall_64_addr)
{
unsigned char* off;
int i;
for(i = 0; i <= PAGE_SIZE; i++) {
off = (char*)do_syscall_64_addr + i;
if(*(off) == 0x48 && *(off+1) == 0x8b && *(off+2) == 0x04 && *(off+3) == 0xfd) {
return (off+4);
}
}
return NULL;
}
static void*
get_sys_sct(unsigned long* do_syscall_64_addr)
{
unsigned long* sct_addr = get_sys_sct_addr(do_syscall_64_addr);
if(!sct_addr){
return NULL;
}
unsigned int offset = *(unsigned int*)(sct_addr);
unsigned long base = 0xffffffff00000000;
return (void*)(base | offset);
}
//hooked execve
static int hook_execve_init(void){
printk("[+] Finding sys_call_tablen");
unsigned long* do_syscall_64_addr = 0;
do_syscall_64_addr = get_lstar_dosys();
if(!do_syscall_64_addr){
printk("[x] Failed to find do_syscall_64_addrn");
return 0;
}
printk("[+] Found do_syscall_64_addr at: 0x%lxn", do_syscall_64_addr);
_sys_call_table = get_sys_sct(do_syscall_64_addr);
if(!_sys_call_table) {
printk("[x] Failed to find sys_call_tablen");
return 0;
}
printk("[+] Found sys_call_table at: 0x%lxn", _sys_call_table);
return 0;
}

– 注意处理一些细节问题(比如unsigned long 还是 unsinged int),最终可以达到想要的效果。

d8b40ec1-ea08-eb11-8da9-e4434bdf6706.png
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/160358
推荐阅读
相关标签
  

闽ICP备14008679号