当前位置: 首页 > 工具软件 > xHook > 使用案例 >

android命令抹机原理,[原创]利用xhook安卓系统底层抹机原理

阎功
2023-12-01

大概原理是:先读取/proc/self/maps文件内容

正则匹配找到so文件路径和加载基址,

解析elf格式找到要hook的函数的地址替换成自己指定的函数地址//xh_core.c

static void xh_core_refresh_impl()

{

char                     line[512];

FILE                    *fp;

uintptr_t                base_addr;

char                     perm[5];

unsigned long            offset;

int                      pathname_pos;

char                    *pathname;

size_t                   pathname_len;

xh_core_map_info_t      *mi, *mi_tmp;

xh_core_map_info_t       mi_key;

xh_core_hook_info_t     *hi;

xh_core_ignore_info_t   *ii;

int                      match;

xh_core_map_info_tree_t  map_info_refreshed = RB_INITIALIZER(&map_info_refreshed);

if(NULL == (fp = fopen("/proc/self/maps", "r")))

{

XH_LOG_ERROR("fopen /proc/self/maps failed");

return;

}

while(fgets(line, sizeof(line), fp))

{

if(sscanf(line, "%"PRIxPTR"-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue;

//check permission

if(perm[0] != 'r') continue;

if(perm[3] != 'p') continue; //do not touch the shared memory

//check offset

//

//We are trying to find ELF header in memory.

//It can only be found at the beginning of a mapped memory regions

//whose offset is 0.

if(0 != offset) continue;

//get pathname

while(isspace(line[pathname_pos]) && pathname_pos 

pathname_pos += 1;

if(pathname_pos >= (int)(sizeof(line) - 1)) continue;

pathname = line + pathname_pos;

pathname_len = strlen(pathname);

if(0 == pathname_len) continue;

if(pathname[pathname_len - 1] == '\n')

{

pathname[pathname_len - 1] = '\0';

pathname_len -= 1;

}

if(0 == pathname_len) continue;

if('[' == pathname[0]) continue;

//check pathname

//if we need to hook this elf?

match = 0;

TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info

{

if(0 == regexec(&(hi->pathname_regex), pathname, 0, NULL, 0))

{

TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info

{

if(0 == regexec(&(ii->pathname_regex), pathname, 0, NULL, 0))

{

if(NULL == ii->symbol)

goto check_finished;

if(0 == strcmp(ii->symbol, hi->symbol))

goto check_continue;

}

}

match = 1;

check_continue:

break;

}

}

check_finished:

if(0 == match) continue;

//check elf header format

//We are trying to do ELF header checking as late as possible.

if(0 != xh_core_check_elf_header(base_addr, pathname)) continue;

//check existed map item

mi_key.pathname = pathname;

if(NULL != (mi = RB_FIND(xh_core_map_info_tree, &xh_core_map_info, &mi_key)))

{

//exist

RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi);

//repeated?

//We only keep the first one, that is the real base address

if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi))

{

#if XH_CORE_DEBUG

XH_LOG_DEBUG("repeated map info when update: %s", line);

#endif

free(mi->pathname);

free(mi);

continue;

}

//re-hook if base_addr changed

if(mi->base_addr != base_addr)

{

mi->base_addr = base_addr;

xh_core_hook(mi);

}

}

else

{

//not exist, create a new map info

if(NULL == (mi = (xh_core_map_info_t *)malloc(sizeof(xh_core_map_info_t)))) continue;

if(NULL == (mi->pathname = strdup(pathname)))

{

free(mi);

continue;

}

mi->base_addr = base_addr;

//repeated?

//We only keep the first one, that is the real base address

if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi))

{

#if XH_CORE_DEBUG

XH_LOG_DEBUG("repeated map info when create: %s", line);

#endif

free(mi->pathname);

free(mi);

continue;

}

//hook

xh_core_hook(mi); //hook

}

}

fclose(fp);

//free all missing map item, maybe dlclosed?

RB_FOREACH_SAFE(mi, xh_core_map_info_tree, &xh_core_map_info, mi_tmp)

{

#if XH_CORE_DEBUG

XH_LOG_DEBUG("remove missing map info: %s", mi->pathname);

#endif

RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi);

if(mi->pathname) free(mi->pathname);

free(mi);

}

//save the new refreshed map info tree

xh_core_map_info = map_info_refreshed;

XH_LOG_INFO("map refreshed");

#if XH_CORE_DEBUG

RB_FOREACH(mi, xh_core_map_info_tree, &xh_core_map_info)

XH_LOG_DEBUG("  %"PRIxPTR" %s\n", mi->base_addr, mi->pathname);

#endif

}//xh_elf.c

int xh_elf_hook(xh_elf_t *self, const char *symbol, void *new_func, void **old_func)

{

uint32_t                        symidx;

void                           *rel_common;

xh_elf_plain_reloc_iterator_t   plain_iter;

xh_elf_packed_reloc_iterator_t  packed_iter;

int                             found;

int                             r;

if(NULL == self->pathname)

{

XH_LOG_ERROR("not inited\n");

return XH_ERRNO_ELFINIT; //not inited?

}

if(NULL == symbol || NULL == new_func) return XH_ERRNO_INVAL;

XH_LOG_INFO("hooking %s in %s\n", symbol, self->pathname);

//find symbol index by symbol name

if(0 != (r = xh_elf_find_symidx_by_name(self, symbol, &symidx))) return 0;

//replace for .rel(a).plt

if(0 != self->relplt)

{

xh_elf_plain_reloc_iterator_init(&plain_iter, self->relplt, self->relplt_sz, self->is_use_rela);

while(NULL != (rel_common = xh_elf_plain_reloc_iterator_next(&plain_iter)))

{

if(0 != (r = xh_elf_find_and_replace_func(self,

(self->is_use_rela ? ".rela.plt" : ".rel.plt"), 1,

symbol, new_func, old_func,

symidx, rel_common, &found))) return r;

if(found) break;

}

}

//replace for .rel(a).dyn

if(0 != self->reldyn)

{

xh_elf_plain_reloc_iterator_init(&plain_iter, self->reldyn, self->reldyn_sz, self->is_use_rela);

while(NULL != (rel_common = xh_elf_plain_reloc_iterator_next(&plain_iter)))

{

if(0 != (r = xh_elf_find_and_replace_func(self,

(self->is_use_rela ? ".rela.dyn" : ".rel.dyn"), 0,

symbol, new_func, old_func,

symidx, rel_common, NULL))) return r;

}

}

//replace for .rel(a).android

if(0 != self->relandroid)

{

xh_elf_packed_reloc_iterator_init(&packed_iter, self->relandroid, self->relandroid_sz, self->is_use_rela);

while(NULL != (rel_common = xh_elf_packed_reloc_iterator_next(&packed_iter)))

{

if(0 != (r = xh_elf_find_and_replace_func(self,

(self->is_use_rela ? ".rela.android" : ".rel.android"), 0,

symbol, new_func, old_func,

symidx, rel_common, NULL))) return r;

}

}

return 0;

}

抹机软件通过hook一些底层函数达到抹机的目的,其中经常用到的函数如下:int access(const char *pathname, int mode);//判断文件能否访问,一般判断是否存在root文件

int open(const char *pathname, int flags);//打开一些设备配置文件

FILE* fopen(const char *filename, const char *mode);//打开一些设备配置文件

prop_info* __system_property_find(const char* name);//判断build.prop等prop文件的key值存在否

int __system_property_read(const prop_info *pi, char *name, char *value);//同等android.os.SystemProperties.get("xxxx")

int __system_property_get(const char *name, char *value);//同等android.os.SystemProperties.get("xxxx")

FILE* popen( const char *command , const char *type );//执行shell命令并读取结果返回一个文件指针

实例代码如下:#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include "xhook/src/xhook.h"

int (*oldaccess)(const char *pathname, int mode);

int (*oldopen)(const char *pathname, int flags);

FILE* (*oldfopen)(const char *filename, const char *mode);

FILE* (*oldpopen)( const char *command , const char *type );

prop_info* (*old_system_property_find)(const char* name);

int (*old_system_property_get)(const char *name, char *value);

int (*old_system_property_read)(const prop_info *pi, char *name, char *value);

static int new_access(const char * pathname, int mode){

__android_log_print(3, "Nativehook", "Nativehook_access unhook %s %d", pathname, mode);

return access(pathname,mode);

}

static int new_open(const char * pathname, int flags,...){

__android_log_print(3, "Nativehook", "Nativehook_open unhook %s %d", pathname, flags);

return oldopen(pathname,flags);

}

static FILE *new_fopen(const char *filename, const char *mode){

__android_log_print(3, "Nativehook", "Nativehook_fopen unhook %s %s", filename, mode);

return oldfopen(filename,mode);

}

static int new_system_property_get(const char *name, char *value){

__android_log_print(3, "Nativehook", "Nativehook_system_property_get unhook %s %s",name,value);

return old_system_property_get(name,value);

}

static int new_system_property_read(const prop_info *pi, char *name, char *value) {

__android_log_print(3, "Nativehook", "Nativehook_system_property_read unhook %s %s",name,value);

return old_system_property_read(pi,name,value);

}

const prop_info* new_system_property_find(const char* name){

__android_log_print(3, "Nativehook", "Nativehook_system_property_find unhook %s",name);

return old_system_property_find(name);

}

static FILE * newpopen(const char *command ,const char *type ){

__android_log_print(3, "Nativehook", "Nativehook_popen unhook %s %s",command,type);

return oldpopen(command,type);

}

void Java_com_ayona333_fix_party_NativeHook_start(JNIEnv* env, jobject obj)

{

(void)env;

(void)obj;

xhook_ignore(".*/libnativehook.so$", NULL);//屏蔽hook自己

xhook_register("/data/.*\\.so$", "access",new_access ,(void**)(&oldaccess));

xhook_register("/data/.*\\.so$", "open", new_open,(void**)(&oldopen));

xhook_register("/data/data.*\\.so$", "fopen",new_fopen,(void**)(&oldfopen));

xhook_register("/data/.*\\.so$", "__system_property_find",new_system_property_find,(void**)(&old_system_property_find));

xhook_register("/data/.*\\.so$", "__system_property_get",new_system_property_get,(void**)(&old_system_property_get));

xhook_register("/data/.*\\.so$", "__system_property_read",new_system_property_read,(void**)(&old_system_property_read));

xhook_register("/data/.*\\.so", "popen",newpopen,(void**)(&oldpopen));

xhook_refresh(1);

}

jint Java_com_ayona333_fix_party_NativeHook_refresh(JNIEnv* env, jobject obj,jboolean flag)

{

return xhook_refresh(flag);

}

void Java_com_ayona333_fix_party_NativeHook_clear(JNIEnv* env, jobject obj)

{

xhook_clear();

}

void Java_com_ayona333_fix_party_NativeHook_enableDebug(JNIEnv* env, jobject obj,jboolean flag)

{

xhook_enable_debug(flag);

}

void Java_com_ayona333_fix_party_NativeHook_enableSigSegvProtection(JNIEnv* env, jobject obj,jboolean flag)

{

xhook_enable_sigsegv_protection(flag);

}

大概原理明白了以后运用起来比较简单,实际场景一般跟xposed等hook框架配合,拦截需要抹机的APP进程后读取该APP的/proc/self/maps文件、然后进行native层的hook。

或抽出so文件,然后嵌入到自己写APP里,配合xhook进行要抹机的so文件,绕过native层的设备指纹检测或者其他操作。

如果配合其他inlineHook框架的话能达到全hook的目的。

最后于 2021-1-16 11:19

被AyonA333编辑

,原因:

 类似资料: