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

参考文章: 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;
        }
    }
}
时间: 2024-11-03 21:42:33

Android中的so挂钩(hook)之替换Got表的相关文章

Android中使用Sqlite数据库 (一) 建表

一.实现一个类,继承SQLiteOpenHelper类,并实现构造函数,onCreate()  onUpgrade() import android.content.Context; import android.database.DatabaseErrorHandler; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; impo

Android下的挂钩(hook)和代码注入(inject)

Android是基于linux内核的操作系统,根据语言环境可以简单的划分为java层.native C层.linux内核层.java层通过jni与native层交互,使用linux提供的底层函数功能. 因此,类似linux系统,我们可以在Android下实现对另一个进程的挂钩和代码注入.在这简单介绍下挂钩和代码注入的方法和两个库,以及针对<刀塔传奇>实现的代码注入. 利用libinject实现so注入和API Hook 一. so注入 Linux上有一个强大的系统调用ptrace,它提供了父进

Android下so注入和hook

总结一下这两天学习的Android注入so文件,通过遍历got表hook函数调用 1.注入so文件 2.so文件中遍历got表hook函数 一.注入so文件 分为以下几个步骤 1.每个进程都在/proc目录下,以进程id为文件夹名,所以可以通过/proc/id/cmdline文件中中读取进程名称,和我们需要注入的进程名称比较,获得进程id 2.以root身份运行注入程序,通过ptrace函数,传入PTRACE_ATIACH附加到目标进程,PTRACE_SETREGS设置进程寄存器,PTRACE_

Android中带你开发一款自动爆破签名校验工具kstools

一.技术回顾 为了安全起见,一些应用会利用自身的签名信息对应用做一层防护,为了防止应用被二次打包操作,在之前已经介绍了很多关于应用签名校验爆破的方法,一条基本原则不能忘:全局搜索"signature"字符串,这里可以在Jadx打开apk搜索,也可以在IDA中打开so搜索都可以.找到这信息之后可以手动的修改校验逻辑,但是这个法则有个问题,就是如果一个应用在代码中很多地方都做了签名校验,比如以前介绍的一篇爆破游戏文章:Android中爆破应用签名信息案例分析,那时候就会发现,应用在很多地方

Android 中带你开发一款自动爆破签名校验工具 kstools

"-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> Android中带你开发一款自动爆破签名校验工具kstools - 生死看淡,不服就干! - 博客频道 - CSDN.NET 生死看淡,不服就干! http://www.wjdiankong.cn 目录视图 摘要视图 订阅 [活动]2017 CSDN博客专栏评选 &n

Android中各个权限详解

在Android的设计中,资源的访问或者网络连接,要得到这些服务都需要声明其访问权限,否则将无法正常工作.在Android中这样的权限有很多种,这里将各类访问权限一一罗列出来,供大家使用时参考之用. android.permission.EXPAND_STATUS_BAR允许一个程序扩展收缩在状态栏,android开发网提示应该是一个类似Windows Mobile中的托盘程序 android.permission.FACTORY_TEST作为一个工厂测试程序,运行在root用户 android

编程中老有人问hook是什么

HOOK就是传说中的钩子,用于劫持消息,在windows中是这样的,因为win32程序是以消息机制为基础的,比如你点击鼠标,会给窗口传递一个消息,移动鼠标,会给窗口一个消息,用钩子可以比你的窗口先检测到这个消息,从而得到这个消息进行处理,你的窗口可能就处理不到这个消息了,要看你的钩子处理程序是否把这个消息传给窗口,具体看WIN32应用程序开发吧. 随着Android设备上的隐私安全问题越来越被公众重视,恶意软件对用户隐私,尤其是对电话.短信等私密信息的威胁日益突出,各大主流安全软件均推出了自己的

Android so注入(inject)和Hook技术学习(三)——Got表hook之导出表hook

前文介绍了导入表hook,现在来说下导出表的hook.导出表的hook的流程如下.1.获取动态库基值 1 void* get_module_base(pid_t pid, const char* module_name){ 2 FILE* fp; 3 long addr = 0; 4 char* pch; 5 char filename[32]; 6 char line[1024]; 7 8 // 格式化字符串得到 "/proc/pid/maps" 9 if(pid < 0){

Android中的各种访问权限Permission含义

android.permission.EXPAND_STATUS_BAR 允许一个程序扩展收缩在状态栏,android开发网提示应该是一个类似Windows Mobile中的托盘程序 android.permission.FACTORY_TEST 作为一个工厂测试程序,运行在root用户 android.permission.FLASHLIGHT 访问闪光灯,android开发网提示HTC Dream不包含闪光灯 android.permission.FORCE_BACK 允许程序强行一个后退操