日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android hook 实例,代码实例分析android中inline hook

發布時間:2025/3/15 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android hook 实例,代码实例分析android中inline hook 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

以下內容通過1、實現目標注入程序,2、實現主程序,3、實現注入函數,4、thumb指令集實現等4個方面詳細分析了android中inline hook的用法,以下是全部內容:

最近終于沉下心來對著書把hook跟注入方面的代碼敲了一遍,打算寫幾個博客把它們記錄下來。

第一次介紹一下我感覺難度最大的inline hook,實現代碼參考了騰訊GAD的游戲安全入門。

inline hook的大致流程如下:

首先將目標指令替換為跳轉指令,跳轉地址為一段我們自己編寫的匯編代碼,這段匯編代碼先是執行用戶指定的代碼,如修改寄存器的值,然后執行被替換掉的原指令2,最后再跳轉回原指令3處,恢復程序的正常運行。

為了避開注入過程,我們通過hook自己進程加載的動態連接庫進行演示。

1、實現目標注入程序

我們將這個程序編譯為動態連接庫,然后在主程序中加載,作為hook的目標。

target.h

#ifndef TARGET_H_INCLUDED

#define TARGET_H_INCLUDED

void target_foo();

#endif // TARGET_H_INCLUDED

target.c

#include "target.h"

#include

#include

#include

void target_foo()

{

int a = 3;

int b = 2;

while(a--) {

sleep(2);

b = a * b;

printf("[INFO] b is %d\n", b);

}

b = b + 2;

b = b - 1;

printf("[INFO] finally, b is %d\n", b);

}

Android.mk

include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm

LOCAL_MODULE := target

LOCAL_CFLAGS += -pie -fPIE -std=c11

LOCAL_LDFLAGS += -pie -fPIE -shared -llog

APP_ABI := armeabi-v7a

LOCAL_SRC_FILES := target.c

include $(BUILD_SHARED_LIBRARY)

注意Android.mk中LOCAL_ARM_MODE := arm代表編譯時使用4字節的arm指令集,而不是2字節的thumb指令集。

2、實現主程序

在主程序中我們首先加載之前編寫的動態鏈接庫,進行hook之后再對其中的函數target_foo進行調用。

main.c

#include

#include

#include

#include

#include

#include "hook_inline.h"

typedef void (*target_foo)(void);

void my_func(struct hook_reg *reg)

{

puts("here we go!");

}

void main()

{

void *handler = dlopen("/data/local/tmp/libtarget.so", RTLD_NOW);

target_foo foo = (target_foo)dlsym(handler, "target_foo");

hook_inline_make("/data/local/tmp/libtarget.so", 0xde2, my_func, true);

foo();

}

hook_inline.h

#ifndef HOOK_INLINE_H_INCLUDED

#define HOOK_INLINE_H_INCLUDED

#include

struct hook_reg {

long ARM_r0; long ARM_r1; long ARM_r2; long ARM_r3;

long ARM_r4; long ARM_r5; long ARM_r6; long ARM_r7;

long ARM_r8; long ARM_r9; long ARM_r10;long ARM_r11;

long ARM_r12;long ARM_sp; long ARM_lr; long ARM_cpsr;

};

typedef void (*hook_func)(struct hook_reg *reg);

bool hook_inline_make(const char *library, long address, hook_func func, bool isArm);

#endif // HOOK_INLINE_H_INCLUDED

這里我們hook功能的實現函數為hook_inline_make,4個參數分別為動態庫路徑,目標地址,用戶函數,目標地址處指令集。

當程序執行到目標地址處時會回調我們傳入的用戶函數,可通過參數hook_reg來更改寄存器的值(不包括寄存器pc)。因為之前在動態鏈接庫的Android.mk文件指定了使用arm指令集進行編譯,所以此處指定最后一個參數為true。

3、實現注入函數

現在到了最為關鍵的地方,為了實現這個功能還需要了解幾個知識。

(1)、獲取內存中動態鏈接庫的基址

Linux系統中各個進程的內存加載信息可以在/proc/pid/maps文件中到,通過它我們可以獲取到動態鏈接庫在內存中的加載基址。

long get_module_addr(pid_t pid, const char *module_name)

{

char file_path[256];

char file_line[512];

if (pid < 0) {

snprintf(file_path, sizeof(file_path), "/proc/self/maps");

} else {

snprintf(file_path, sizeof(file_path), "/proc/%d/maps", pid);

}

FILE *fp = fopen(file_path, "r");

if (fp == NULL) {

return -1;

}

long addr_start = -1, addr_end = 0;

while (fgets(file_line, sizeof(file_line), fp)) {

if (strstr(file_line, module_name)) {

if (2 == sscanf(file_line, "%8lx-%8lx", &addr_start, &addr_end)) {

break;

}

}

}

fclose(fp);

printf("library :%s %lx-%lx, pid : %d\n", module_name, addr_start, addr_end, pid);

return addr_start;

}

(2)、更改內存中的二進制代碼

現在的計算機系統中一般對內存進行分段式管理,不同的段有不同的讀、寫、執行的屬性。一般來講代碼段只有讀和執行的屬性,不允許對代碼段進行寫操作。Linux系統中通過函數mprotect對內存的屬性進行更改,需要注意的一點是需要以內存頁的大小進行對齊。

bool change_addr_writable(long address, bool writable) {

long page_size = sysconf(_SC_PAGESIZE);

//align address by page size

long page_start = (address) & (~(page_size - 1));

//change memory attribute

if (writable == true) {

return mprotect((void*)page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC) != -1;

} else {

return mprotect((void*)page_start, page_size, PROT_READ | PROT_EXEC) != -1;

}

}

接下來就可以著手實現功能了,inline hook跟指令集密切相關,此處我們先演示arm指令集的情況,之后對thumb指令集進行討論。這里實現的功能是用戶可在自己注冊的回調函數中對hook點寄存器的值進行修改。

為了實現32位地址空間的長跳轉,我們需要兩條指令的長度(8個字節)來實現。一般手機上的arm處理器為3級流水,所以pc寄存器的值總是指向當前執行指令后的第二條指令,因而使用ldr pc, [pc, #-4]來加載該指令之后的跳轉地址。當程序跳轉到shellcode后,首先對寄存器組進行備份,然后調用用戶注冊的回調函數,用戶可在回調函數中修改備份中各個寄存器(pc寄存器除外)的值,然后從備份中恢復寄存器組再跳轉到stubcode,stubcode的功能是執行被hook點的跳轉指令替換掉的兩條指令,最后跳回原程序。

ellcode.S 1 .global _shellcode_start_s

.global _shellcode_end_s

.global _hook_func_addr_s

.global _stub_func_addr_s

.data

_shellcode_start_s:

@ 備份各個寄存器

push? {r0, r1, r2, r3}

mrs?? r0, cpsr

str?? r0, [sp, #0xc]

str?? r14, [sp, #0x8]

add?? r14, sp, #0x10

str?? r14, [sp, #0x4]

pop?? {r0}

push? {r0-r12}

@ 此時寄存器被備份在棧中,將棧頂地址作為回調函數的參數(struct hook_reg)

mov?? r0, sp

ldr?? r3, _hook_func_addr_s

blx?? r3

@ 恢復寄存器值

ldr?? r0, [sp, #0x3c]

msr?? cpsr, r0

ldmfd sp!, {r0-r12}

ldr?? r14, [sp, #0x4]

ldr?? sp, [r13]

ldr?? pc, _stub_func_addr_s

_hook_func_addr_s:

.word 0x0

_stub_func_addr_s:

.word 0x0

_shellcode_end_s:

.end

shellcode使用匯編實現,在使用時需要對里邊的兩個地址進行修復,用戶回調函數地址(_hook_func_addr_s)跟stubcode地址(_stub_func_addr_s)。

接下來我們可以看一下函數hook_inline_make的具體實現了

void hook_inline_make(const char *library, long address, hook_func func)

{

//獲取hook點在內存中的地址

long base_addr = get_module_addr(-1, library);

long hook_addr = base_addr + address;

//獲取shellcode中的符號地址

extern long _shellcode_start_s;

extern long _shellcode_end_s;

extern long _hook_func_addr_s;

extern long _stub_func_addr_s;

void *p_shellcode_start = &_shellcode_start_s;

void *p_shellcdoe_end = &_shellcode_end_s;

void *p_hook_func = &_hook_func_addr_s;

void *p_stub_func = &_stub_func_addr_s;

//計算shellcode大小

int shellcode_size = (int)(p_shellcdoe_end - p_shellcode_start);

//新建shellcode

void *shellcode = malloc(shellcode_size);

memcpy(shellcode, p_shellcode_start, shellcode_size);

//添加執行屬性

change_addr_writable((long)shellcode, true);

//在32bit的arm指令集中,stubcode中的4條指令占用16個字節的空間

//前兩條指令為hook點被替換的兩條指令

//后兩條指令跳轉回原程序

void *stubcode = malloc(16);

memcpy(stubcode, (void*)hook_addr, 8);

//ldr pc, [pc, #-4]

//[address]

//手動填充stubcode

char jump_ins[8] = {0x04, 0xF0, 0x1F, 0xE5};

uint32_t jmp_address = hook_addr + 8;

memcpy(jump_ins + 4, &jmp_address, 4);

memcpy(stubcode + 8, jump_ins, 8);

//添加執行屬性

change_addr_writable((long)stubcode, true);

//修復shellcode中的兩個地址值

uint32_t *shell_hook = shellcode + (p_hook_func - p_shellcode_start);

*shell_hook = (uint32_t)func;

uint32_t *shell_stub = shellcode + (p_stub_func - p_shellcode_start);

*shell_stub = (uint32_t)stubcode;

//為hook點添加寫屬性

change_addr_writable(hook_addr, true);

//替換hook點指令為跳轉指令,跳轉至shellcode

jmp_address = (uint32_t)shellcode;

memcpy(jump_ins + 4, &jmp_address, 4);

memcpy((void*)hook_addr, jump_ins, 8);

change_addr_writable(hook_addr, false);

//刷新cache

cacheflush(hook_addr, 8, 0);

}

注意這里的change_addr_writable函數無論傳入false還是true對應地址都會添加上執行屬性。由于處理器采用流水線跟多級緩存,在更改代碼后我們需要手動刷新cache,即函數cacheflush(第三個參數無意義)。

4、thumb指令集實現

由于thumb指令集的功能受到限制,雖然思路上跟arm指令集一致,但在實現上需要用更多條指令,下面是我自己想的一種實現方式,歡迎交流。

需要注意的是由于每條thumb指令為16bit,所以32位的跳轉地址需要占用兩條指令的空間,而且跳轉時會污染r0寄存器所以要對其進行保護。我在實現程序時將shellcode編譯為了arm指令集,所以在原程序、shellcode、stubcode之間相互跳轉時需要使用bx指令進行處理器狀態切換(需要跳轉的地址代碼為thumb指令集時,需要將地址的第1個bit位置位)。

總結

以上是生活随笔為你收集整理的android hook 实例,代码实例分析android中inline hook的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。