您的位置:首页 > 移动开发 > Android开发

Android中的so挂钩(hook)之替换Got表

2016-01-12 13:42 483 查看
参考文章: http://blog.csdn.net/jinzhuojun/article/details/9900105


逻辑大概如下:

使用之前的注入代码注入自己的so并执行so中函数 -> so中函数解析ELF文件获取GOT表的位置和大小 -> 获取需要挂钩的原函数地址,以及自定义用于挂钩的函数地址 -> 遍历GOT表的每一项判断是否有与需要挂钩的原函数地址相同的项 -> 找到后把该项的地址替换为自定义用于挂钩的函数地址 -> 最后在自定义用于挂钩的函数中调用原函数

这种挂钩存在很多弊端: 如果只能挂钩函数的头部,无法达到对整个进程都有效等。


实现效果如下:



代码写的很戳,只是为了了解实现原理,实际操作中还是要用一些比较成熟的HOOK库。

//Demo

#include <stdio.h>

int count = 0;

void sevenWeapons(int number)
{
char* str = "Hello,LiBieGou!";
printf("%s %d\n",str,number);
}

int main(int argc, char* argv[])
{
while(1)
{
sevenWeapons(count);
count++;
sleep(1);
}

return 0;
}


//Inject代码

#include <jni.h>
#include <stdio.h>
#include <dlfcn.h>
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include <android/log.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/mman.h>

#define LOG_TAG "INJECT"
#define LOGD(fmt, args...)  __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)
#define DEBUG_PRINT(format, args...) LOGD(format, ##args)

#define LIBC_PATH   "/system/lib/libc.so"
#define LINKER_PATH "/system/bin/linker"

#define CPSR_T_MASK     ( 1u << 5 )

/*--------------------------------------------------
*   功能:   通过进程的名称获取对应的进程Pid
*
*   返回值: 未找到返回-1
*--------------------------------------------------*/
int FindProIdByProName(const char *lpszProName)
{
DIR* lpDirp = NULL;
struct dirent* lpDirentp = NULL;
int nPid = 0;
FILE *fp = NULL;
char szLines[1024] = {0};
char szCmdlinePath[256] = {0};
int nFind = 0;

lpDirp = opendir("/proc");
if(lpDirp == NULL)
{
DEBUG_PRINT("[-]FindProIdByProName::opendir error\r\n");
return -1;
}

while ((lpDirentp = readdir(lpDirp)) != NULL)
{
nPid = atoi(lpDirentp->d_name);
memset(szCmdlinePath, sizeof(szCmdlinePath), 0);
snprintf(szCmdlinePath, sizeof(szCmdlinePath), "/proc/%d/cmdline", nPid);
fp = fopen(szCmdlinePath, "r");
if (fp != NULL)
{
fgets(szLines, sizeof(szLines), fp);
if (strcmp(szLines, lpszProName) == 0)
{
nFind = 1;
DEBUG_PRINT("[+]Find ProId = %d\r\n", nPid);
fclose(fp);
fp = NULL;
break;
}

fclose(fp);
fp = NULL;
}
}

if (nFind == 0)
{
DEBUG_PRINT("[-]No Find ProId\r\n");
return -1;
}

return nPid;
}

/*--------------------------------------------------
*   功能:   附加进程
*
*   返回值: 失败返回-1
*--------------------------------------------------*/
int PtraceAttach(int nPid)
{
//被跟踪进程将成为当前进程的子进程,并进入中止状态。
if (ptrace(PTRACE_ATTACH, nPid, NULL, NULL) == -1)
{
return -1;
}

int nStatus = 0;

//如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。
//父进程退出时, 不影响子进程
waitpid(nPid, &nStatus , WUNTRACED);

return 0;
}

/*--------------------------------------------------
*   功能:   获取指定进程的寄存器信息
*
*   返回值: 失败返回-1
*--------------------------------------------------*/
int PtraceGetRegs(int nPid, struct pt_regs *lpRegs)
{
if (ptrace(PTRACE_GETREGS, nPid, NULL, lpRegs) == -1)
{
return -1;
}

return 0;
}

/*--------------------------------------------------
*   功能:   取消附加
*
*   返回值: 失败返回-1
*--------------------------------------------------*/
int PtraceDetach(int nPid)
{
if (ptrace(PTRACE_DETACH, nPid, NULL, NULL) == -1)
{
return -1;
}

return 0;
}

/*--------------------------------------------------
*   功能:   获取进程中指定模块的首地址
*
*   参数:
*           nPid            需要注入的进程Pid, 如果为0则获取自身进程
*           lpLibraryPath   需要获取模块路径
*
*   返回值: 失败返回NULL, 成功返回Addr
*--------------------------------------------------*/
void* GetModuleBase(int nPid, const char *lpLibraryPath)
{
char szPath[256] = {0};
char szLines[1024] = {0};
char *lpCh = NULL;
void *lpBaseAddr = NULL;

if (nPid == 0)
{
snprintf(szPath, sizeof(szPath), "/proc/self/maps");
}
else
{
snprintf(szPath, sizeof(szPath), "/proc/%d/maps", nPid);
}

FILE *fp = fopen(szPath, "r");
if (fp != NULL)
{
while (fgets(szLines, sizeof(szLines), fp))
{
if (strstr(szLines, lpLibraryPath))
{
lpCh = strtok(szLines, "-");
lpBaseAddr = strtoul(lpCh, NULL, 16);
fclose(fp);
break;
}
}

fclose(fp);
fp = NULL;
}

return lpBaseAddr;
}

/*--------------------------------------------------
*   功能:   获取目标进程中函数指针
*
*   参数:
*           nPid            需要注入的进程Pid
*           lpLibraryPath   需要获取的函数所在的lib库路径
*           lpFunctionAddr  需要获取的函数所在当前进程内存中的地址
*
*           目标进程中函数指针 = 目标进程模块基址 - 自身进程模块基址 + 内存中的地址
*
*   返回值: 失败返回NULL, 成功返回Addr
*--------------------------------------------------*/
void* GetRemoteFunctionAddr(int nPid, const char *lpLibraryPath, void *lpFunctionAddr)
{
//获取目标进程模块基址
void *lpRemoteBaseAddr = GetModuleBase(nPid, lpLibraryPath);
void *lpLocalBaseAddr = GetModuleBase(0, lpLibraryPath);
void *lpRemoteFunctionAddr = NULL;

if ((lpRemoteBaseAddr == NULL) || (lpLocalBaseAddr == NULL))
{
return lpRemoteFunctionAddr;
}

DEBUG_PRINT("[+] GetRemoteFunctionAddr: local[%p], remote[%p]\n", lpLocalBaseAddr, lpRemoteBaseAddr);

lpRemoteFunctionAddr = (void *)((uint32_t)lpRemoteBaseAddr - (uint32_t)lpLocalBaseAddr + (uint32_t)lpFunctionAddr);

return lpRemoteFunctionAddr;
}

/*--------------------------------------------------
*   功能:   向目标进程指定的地址中写入数据
*
*   参数:
*           nPid        需要注入的进程Pid
*           lpAddr      需要写入的目标进程地址
*           lpData      需要写入的数据缓冲区
*           nLength     需要写入的数据长度
*
*   返回值: -1
*--------------------------------------------------*/
int PtraceWriteProcessMemory(int nPid, void *lpAddr, const uint8_t *lpData, uint32_t nLength)
{
uint32_t i, j, remain;
uint8_t *lpDataBuff = NULL;

union u {
long val;
char chars[sizeof(long)];
} d;

j = nLength / 4;
remain = nLength % 4;

lpDataBuff = lpData;

//先4字节拷贝
for (i = 0; i < j; i++)
{
memcpy(d.chars, lpDataBuff, 4);
ptrace(PTRACE_POKETEXT, nPid, lpAddr, d.val);

lpAddr  += 4;
lpDataBuff += 4;
}

//最后不足4字节的,单字节拷贝
if (remain > 0)
{
d.val = ptrace(PTRACE_PEEKTEXT, nPid, lpAddr, 0);
for (i = 0; i < remain; i ++) {
d.chars[i] = *lpDataBuff ++;
}

ptrace(PTRACE_POKETEXT, nPid, lpAddr, d.val);
}

return 0;
}

/*--------------------------------------------------
*   功能:   修改目标进程寄存器的值
*
*   参数:
*           nPid        需要注入的进程Pid
*           lpRegs      需要修改的新寄存器信息
*
*   返回值: -1
*--------------------------------------------------*/
int PtraceSetRegs(int nPid, struct pt_regs *lpRegs)
{
if (ptrace(PTRACE_SETREGS, nPid, NULL, lpRegs) == -1)
{
return -1;
}

return 0;
}

/*--------------------------------------------------
*   功能:   恢复程序运行
*
*   参数:
*           nPid        需要注入的进程Pid
*
*   返回值: -1
*--------------------------------------------------*/
int PtraceContinue(int nPid)
{
if (ptrace(PTRACE_CONT, nPid, NULL, NULL) == -1)
{
return -1;
}

return 0;
}

/*--------------------------------------------------
*   功能:   调用远程函数指针
*
*   参数:
*           nPid            需要注入的进程Pid
*           pfnFunctionAddr 调用的函数指针地址
*           lpParamArg      调用的参数
*           nParamCount     调用的参数个数
*           lpRegs          远程进程寄存器信息(ARM前4个参数由r0 ~ r3传递)
*
*   返回值: 失败返回-1
*--------------------------------------------------*/
int PtraceCallRemoteFunction(int nPid, void *pfnFunctionAddr, long *lpParamArg, int nParamCount, struct pt_regs *lpRegs)
{
uint32_t i = 0;
int nStatus = 0;

//首先将前4个参数赋值给r0~r3
for (; (i < nParamCount) && (i < 4); i++)
{
lpRegs->uregs[i] = lpParamArg[i];
}

//如果有超过4个的参数, 则将剩余参数拷贝到目标栈上
if (i < nParamCount)
{
//抬高栈顶sub esp, xxx
lpRegs->ARM_sp -= (nParamCount - i) * sizeof(long);
if (PtraceWriteProcessMemory(nPid, (void *)lpRegs->ARM_sp, (const uint8_t *)&lpParamArg[i], (uint32_t)((nParamCount - i) * sizeof(long))) == -1)
{
DEBUG_PRINT("[-]PtraceCallRemoteFunction::PtraceWriteProcessMemory Error\r\n");
return -1;
}
}

//将PC的值设置为函数地址
lpRegs->ARM_pc = pfnFunctionAddr;

//设置ARM_cpsr寄存器的值
if (lpRegs->ARM_pc & 1)
{
/* thumb */
lpRegs->ARM_pc &= (~1u);
lpRegs->ARM_cpsr |= CPSR_T_MASK;
}
else
{
/* arm */
lpRegs->ARM_cpsr &= ~CPSR_T_MASK;
}

//设置返回地址为0的原因见下面注释
lpRegs->ARM_lr = 0;

//修改目标进程寄存器的值
if (PtraceSetRegs(nPid, lpRegs) == -1)
{
DEBUG_PRINT("[-]PtraceCallRemoteFunction::PtraceSetRegs Error\r\n");
return -1;
}

/*
WUNTRACED告诉waitpid,如果子进程进入暂停状态,那么就立即返回。
如果是被ptrace的子进程,那么即使不提供WUNTRACED参数,也会在子进程进入暂停状态的时候立即返回。
对于使用ptrace_cont运行的子进程,它会在3种情况下进入暂停状态:
①下一次系统调用;
②子进程退出;
③子进程的执行发生错误。
这里的0xb7f就表示子进程进入了暂停状态,且发送的错误信号为11(SIGSEGV),它表示试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据。
那么什么时候会发生这种错误呢?
显然,当子进程执行完注入的函数后,由于我们在前面设置了regs->ARM_lr = 0,它就会返回到0地址处继续执行,这样就会产生SIGSEGV了!
*/
do
{
//恢复程序运行, 由于之前Attach被挂起了
if (PtraceContinue(nPid) == -1)
{
DEBUG_PRINT("[-]PtraceCallRemoteFunction::PtraceContinue Error\r\n");
return -1;
}

waitpid(nPid, &nStatus, WUNTRACED);

} while(nStatus != 0xb7f);

return 0;
}

/*--------------------------------------------------
*   功能:   调用远程函数指针
*
*   参数:
*           nPid            需要注入的进程Pid
*           lpFunctionName  调用的函数名称, 此参数仅作Debug输出用
*           pfnFunctionAddr 调用的函数指针地址
*           lpParamArg      调用的参数
*           nParamCount     调用的参数个数
*           lpRegs          远程进程寄存器信息(ARM前4个参数由r0 ~ r3传递)
*
*   返回值: 失败返回-1
*--------------------------------------------------*/
int CallRemoteFunction(int nPid, const char *lpFunctionName, void *pfnFunctionAddr, long *lpParamArg, int nParamCount, struct pt_regs *lpRegs)
{
DEBUG_PRINT("[+] Calling %s in target process.\n", lpFunctionName);

//call
if (PtraceCallRemoteFunction(nPid, pfnFunctionAddr, lpParamArg, nParamCount, lpRegs) == -1)
{
return -1;
}

//获取返回值
if (PtraceGetRegs(nPid, lpRegs) == -1)
{
DEBUG_PRINT("[-]CallRemoteFunction::PtraceGetRegs Error\r\n");
return -1;
}

DEBUG_PRINT("[+] Target process returned from %s, return value=%p, pc=%p\r\n",
lpFunctionName, lpRegs->ARM_r0, lpRegs->ARM_pc);
return 0;
}

/*--------------------------------------------------
*   功能:   远程注入
*
*   参数:
*           nPid            需要注入的进程Pid
*           lpLibraryPath   需要注入的.so路径
*           lpFunctionName  .so中导出的函数名
*           lpFunctionParam 函数的参数
*
*   返回值: 注入失败返回-1
*--------------------------------------------------*/
int InjectRemoteProcess(int nPid, const char *lpLibraryPath, const char *lpFunctionName, const char *lpFunctionParam)
{
int nRet = 0;
void *pfnmmap = NULL;
void *pfndlopen = NULL;
void *pfndlsym = NULL;
void *pfndlclose = NULL;
void *lpMmapBase = NULL;
struct pt_regs Regs = {0};
struct pt_regs OldRegs = {0};
long ParamArg[10] = {0};
void *hSo = NULL;
void *pfnRemoteFunction = NULL;
void *pfnsleep = NULL;

DEBUG_PRINT("[+] Injecting process: %d\n", nPid);

//附加目标进程
if (PtraceAttach(nPid) == -1)
{
DEBUG_PRINT("[-]PtraceAttach Error\r\n");
return -1;
}

//获取保存寄存器信息, 恢复时用
if (PtraceGetRegs(nPid, &Regs) == -1)
{
DEBUG_PRINT("[-]PtraceGetRegs Error\r\n");
nRet = -1;
goto SAFE_END;
}

//保存
memcpy(&OldRegs, &Regs, sizeof(Regs));

pfnmmap = GetRemoteFunctionAddr(nPid, LIBC_PATH, (void *)mmap);
if (pfnmmap == NULL)
{
DEBUG_PRINT("[-]pfnmmap == NULL\r\n");
nRet = -1;
goto SAFE_END;
}

DEBUG_PRINT("[+] pfnmmap Addr: %p\r\n", pfnmmap);

//申请远程空间
//构造参数void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
ParamArg[0] = 0;
ParamArg[1] = 0x4000;
ParamArg[2] = PROT_READ | PROT_WRITE | PROT_EXEC;
ParamArg[3] = MAP_ANONYMOUS | MAP_PRIVATE;
ParamArg[4] = 0;
ParamArg[5] = 0;

//调用远程函数指针
if (CallRemoteFunction(nPid, "mmap", pfnmmap, ParamArg, 6, &Regs) == -1)
{
nRet = -1;
goto SAFE_END;
}

//远程申请的Buffer首地址
lpMmapBase = Regs.ARM_r0;

pfndlopen = GetRemoteFunctionAddr(nPid, LINKER_PATH, (void *)dlopen);
pfndlsym = GetRemoteFunctionAddr(nPid, LINKER_PATH, (void *)dlsym);
pfndlclose = GetRemoteFunctionAddr(nPid, LINKER_PATH, (void *)dlclose);
pfnsleep = GetRemoteFunctionAddr(nPid, LIBC_PATH, (void *)sleep);
if ((pfndlopen == NULL) || (pfndlsym == NULL) || (pfndlclose == NULL) || (pfnsleep == NULL))
{
DEBUG_PRINT("[-]pfndlopen | pfndlsym | pfndlclose | pfnsleep == NULL\r\n");
nRet = -1;
goto SAFE_END;
}

DEBUG_PRINT("[+] Get imports: dlopen: %p, dlsym: %p, dlclose: %p\r\n",
pfndlopen, pfndlsym, pfndlclose);

printf("lpLibraryPath Length: %d\r\n", strlen(lpLibraryPath) + 1);

//远程申请的Buffer首地址写入需要注入的so路径
if (PtraceWriteProcessMemory(nPid, lpMmapBase, lpLibraryPath, strlen(lpLibraryPath) + 1) == -1)
{
DEBUG_PRINT("[-]InjectRemoteProcess::PtraceWriteProcessMemory Error\r\n");
nRet = -1;
goto SAFE_END;
}

//传递参数, 准备调用dlopen  void * dlopen(const char * pathname, int mode);
ParamArg[0] = lpMmapBase;
ParamArg[1] = RTLD_NOW| RTLD_GLOBAL;

if (CallRemoteFunction(nPid, "dlopen", pfndlopen, ParamArg, 2, &Regs) == -1)
{
nRet = -1;
goto SAFE_END;
}

hSo = Regs.ARM_r0;

//传递参数, 准备调用pfndlsym  void* dlsym( void* handle, const char* name );
ParamArg[0] = hSo;

#define FUNCTION_NAME_OFFSET    0x100
//lpFunctionName需要写入远程的buffer中才能使用
if (PtraceWriteProcessMemory(nPid, lpMmapBase + FUNCTION_NAME_OFFSET, lpFunctionName, strlen(lpFunctionName) + 1) == -1)
{
DEBUG_PRINT("[-]InjectRemoteProcess::PtraceWriteProcessMemory Error\r\n");
nRet = -1;
goto SAFE_END;
}

ParamArg[1] = lpMmapBase + FUNCTION_NAME_OFFSET;

if (CallRemoteFunction(nPid, "dlsym", pfndlsym, ParamArg, 2, &Regs) == -1)
{
nRet = -1;
goto SAFE_END;
}

pfnRemoteFunction = Regs.ARM_r0;
DEBUG_PRINT("hook_entry_addr = %p\r\n", pfnRemoteFunction);

//传递参数, 准备调用注入模块中的函数  void MyHook(void)
if (CallRemoteFunction(nPid, lpFunctionName, pfnRemoteFunction, ParamArg, 0, &Regs) == -1)
{
nRet = -1;
goto SAFE_END;
}

/*
printf("Press enter to dlclose and detach\r\n");
getchar();

//传递参数, 准备调用dlclose
ParamArg[0] = hSo;

if (CallRemoteFunction(nPid, "dlclose", pfndlclose, ParamArg, 1, &Regs) == -1)
{
nRet = -1;
}
*/

SAFE_END:
//恢复原始寄存器信息
if (PtraceSetRegs(nPid, &OldRegs) == -1)
{
DEBUG_PRINT("[-]PtraceSetRegs Error\r\n");
}

//取消附加
if (PtraceDetach(nPid) == -1)
{
DEBUG_PRINT("[-]PtraceDetach Error\r\n");
}

return nRet;
}

int main(int argc, char* argv[])
{
int nProId = FindProIdByProName("./hello");
if (nProId == -1)
{
return -1;
}

if (InjectRemoteProcess(nProId, "/data/local/tmp/MyTest.so", "MyHook", NULL) == -1)
{
DEBUG_PRINT("[+]InjectRemoteProcess Failed\r\n");
}
else
{
DEBUG_PRINT("[+]InjectRemoteProcess Success\r\n");
}

return 0;
}


//Hook代码

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>

typedef unsigned int (*PFNSLEEP)(unsigned int);

typedef unsigned short Elf32_Half;
typedef unsigned long Elf32_Word;
typedef unsigned long Elf32_Addr;
typedef unsigned long Elf32_Off;

#define EI_NIDENT (16)
#define SHT_PROGBITS 1

typedef struct
{
unsigned char    e_ident[EI_NIDENT];    /* Magic number and other info */
Elf32_Half    e_type;            /* Object file type */
Elf32_Half    e_machine;        /* Architecture */
Elf32_Word    e_version;        /* Object file version */
Elf32_Addr    e_entry;        /* Entry point virtual address */
Elf32_Off    e_phoff;        /* Program header table file offset */
Elf32_Off    e_shoff;        /* Section header table file offset */
Elf32_Word    e_flags;        /* Processor-specific flags */
Elf32_Half    e_ehsize;        /* ELF header size in bytes */
Elf32_Half    e_phentsize;        /* Program header table entry size */
Elf32_Half    e_phnum;        /* Program header table entry count */
Elf32_Half    e_shentsize;        /* Section header table entry size */
Elf32_Half    e_shnum;        /* Section header table entry count */
Elf32_Half    e_shstrndx;        /* Section header string table index */
} Elf32_Ehdr;

typedef struct
{
Elf32_Word    sh_name;        /* Section name (string tbl index) */
Elf32_Word    sh_type;        /* Section type */
Elf32_Word    sh_flags;        /* Section flags */
Elf32_Addr    sh_addr;        /* Section virtual addr at execution */
Elf32_Off    sh_offset;        /* Section file offset */
Elf32_Word    sh_size;        /* Section size in bytes */
Elf32_Word    sh_link;        /* Link to another section */
Elf32_Word    sh_info;        /* Additional section information */
Elf32_Word    sh_addralign;        /* Section alignment */
Elf32_Word    sh_entsize;        /* Entry size if section holds table */
} Elf32_Shdr;

//新函数
unsigned int NewSleep(unsigned int seconds)
{
printf("Hello NewSleep\r\n");

//调用原始函数,也可以定义一个函数指针来调
//return (*pfnSleep)(seconds);
return sleep(seconds);
}

//解析Got表的内存偏移和表的大小
int GetGotTableInfo(int *lpnVirtualAddr, int *lpnSize)
{
int nRet = -1;
FILE *fp = fopen("/data/local/tmp/hello", "r");
if (fp == NULL)
{
return -1;
}

Elf32_Ehdr Elf32Header;
Elf32_Shdr Elf32SectionHeader;

//不做返回值检查了
fread(&Elf32Header, sizeof(Elf32_Ehdr), 1, fp);

fseek(fp, Elf32Header.e_shstrndx * Elf32Header.e_shentsize + Elf32Header.e_shoff, SEEK_SET);
fread(&Elf32SectionHeader, Elf32Header.e_shentsize, 1, fp);

char *lpStringTable = (char *)malloc(Elf32SectionHeader.sh_size);
if (lpStringTable == NULL)
{
goto SAFE_END;
}

fseek(fp, Elf32SectionHeader.sh_offset, SEEK_SET);
fread(lpStringTable, Elf32SectionHeader.sh_size, 1, fp);
fseek(fp, Elf32Header.e_shoff, SEEK_SET);

int nNameIndex = 0;
int i = 0;

for (i = 0; i < Elf32Header.e_shnum; i++)
{
fread(&Elf32SectionHeader, Elf32Header.e_shentsize, 1, fp);
if (Elf32SectionHeader.sh_type == SHT_PROGBITS)
{
nNameIndex = Elf32SectionHeader.sh_name;
if ((strcmp(&(lpStringTable[nNameIndex]), ".got.plt") == 0) ||
(strcmp(&(lpStringTable[nNameIndex]), ".got") == 0))
{
//不是.so就不用修正地址了, so的话需要加上模块基地址修正
*lpnVirtualAddr = Elf32SectionHeader.sh_addr;
*lpnSize = Elf32SectionHeader.sh_size;
nRet = 0;
break;
}
}
}

SAFE_END:
if (fp != NULL)
{
fclose(fp);
}

if (lpStringTable != NULL)
{
free(lpStringTable);
}

return nRet;
}

void MyHook()
{
int nVirtualAddr = 0;
int nSize = 0;
int i = 0;

//获取GotTable的VirtualAddr, Size
if (GetGotTableInfo(&nVirtualAddr, &nSize) == -1)
{
return;
}

//遍历Got表中的每一项
for (i = 0; i < nSize; i += 4)
{
if ((int)sleep == (*(int *)(nVirtualAddr + i)))
{
//修改内存保护属性为可写
mprotect((void *)0x9000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC);
*(int *)(nVirtualAddr + i) = (int)NewSleep;
break;
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: