本文为看雪论坛精华文章
看雪论坛作者ID:卓桐
https://github.com/GToad/Android_Inline_Hook_ARM64
有32和64的实现,但是是分离的,要用的话还要自己把两份代码合并在一起。
缺点:
1、不支持函数替换(即hook后不执行原函数),现在只能修改参数寄存器,无法修改返回值。
2、不支持定义同类型的hook函数来接受处理参数,只能通过修改寄存器的方式修改参数。
多余4个/或者占两个字节的参数,那么参数还要自己从栈上捞取。所以issues中说的把mov r0,sp去掉用来接收参数也是有问题的,就是参数在栈上的情况,传过来的时候sp不是原来的sp了。
因为以上的两个缺点,想来没太多人用也是情理之中了,因为自己解析参数、不能修改返回值、不能不执行原函数,局限太大了。看来要想用起来,还得自己修改代码。
https://github.com/asLody/whale
移植好像比较简单,记得好像移植过,但是hook了某个系统函数回调原函数就崩溃了。所以用之前可能要先帮他找一遍bug、修复。
后记:art hook的之前看过,应该说是xposed/frida的另一份代码,frida也是用的xposed的方式,只不过不修改系统文件、通过动态调用系统函数实现。frida是js的代码,这个是c/c++的实现。
内联hook大概看了下其实也是一样的套路,32位采用ldr pc的方式跳到hook函数,64位使用x17寄存器跳到hook函数。剩下的修复原函数也是一样的。只是没时间完整看一遍定位bug了。
https://github.com/jmpews/HookZz
对于安卓程序员来说不太友好,编译需要cmake。
mkdir build_for_android_arm64 && cd build_for_android_arm64
set ANDROID_NDK=D:\android\NDK\android-ndk-r16b
C:\Users\EDZ\AppData\Local\Android\Sdk\cmake\3.10.2.4988404\bin\cmake .. -DCMAKE_TOOLCHAIN_FILE=%ANDROID_NDK%/build/cmake/android.toolchain.cmake -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI="arm64-v8a" -DANDROID_NATIVE_API_LEVEL=android-21 -DSHARED=OFF -DHOOKZZ_DEBUG=OFF -G "Unix Makefiles" -D"CMAKE_MAKE_PROGRAM:PATH=D:\app\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin\mingw32-make.exe"
需要指定-G "Unix Makefiles",默认的NMake Makefiles编译不过,指定make,因为未设置环境变量,-D"CMAKE_MAKE_PROGRAM:PATH="
编译动态库,-DSHARED=ON;编译静态库,-DSHARED=OFF。
接下来为了方便还是移植到Android工程中吧。
呃。基本不可用!Android:arm/arm64均crach,arm64可以修复,https://www.gitmemory.com/foundkey,在OneLib\stdcxx\LiteIterator.cc中函数initWithCollection添加inCollection->initIterator(innerIterator);
但是arm还是crash:
#00 pc 00025ef0 [anon:libc_malloc]
12-12 15:12:53.685 181-181/? I/DEBUG: #01 pc 000081f1 /data/app-lib/com.zhuotong.myhkzz-1/libhookzz.so (LiteCollectionIterator::getNextObject()+28)
12-12 15:12:53.685 181-181/? I/DEBUG: #02 pc 00007531 /data/app-lib/com.zhuotong.myhkzz-1/libhookzz.so (gen_thumb_relocate_code(void*, int*, unsigned int, unsigned int)+312)
12-12 15:12:53.685 181-181/? I/DEBUG: #03 pc 00007ac1 /data/app-lib/com.zhuotong.myhkzz-1/libhookzz.so (InterceptRouting::Prepare()+56)
12-12 15:12:53.685 181-181/? I/DEBUG: #04 pc 00007cc1 /data/app-lib/com.zhuotong.myhkzz-1/libhookzz.so (FunctionInlineReplaceRouting::Dispatch()+12)
12-12 15:12:53.685 181-181/? I/DEBUG: #05 pc 00007d4d /data/app-lib/com.zhuotong.myhkzz-1/libhookzz.so (ZzReplace+120)
且通过arm64测试来看,open、fopen可以hook成功。
__system_property_get函数,第二个参数既是入参也做返回参数的情况无法正确hook,可以hook到,但是回调原函数,无论是使用第二个参数还是new参数均无法得到值,所以肯定哪里存在bug。
一个函数只能被hook一次,再次hook调用原函数(备份的第一个hook函数)崩溃,所以两次hook不能再调用原函数。
基于以上种种情况,可能还是自己实现Android_Inline_Hook比较好,毕竟Android_Inline_Hook代码易懂,hookZz太多无关代码,没时间看架构了。
后记:后来大概看了一下,32位也是ldr pc实现,好像也做了保存寄存器等操作,和Android_Inline_Hook基本是一样的,64位好像也是使用x17寄存器。其他也都是大同小异。所以也是没时间完整看一遍定位bug了。
因为以上的问题,目前/或者之前用过的一些hook框架或多或少都有些较大的bug(hookzz之前的某个版本应该是可以的),而对其进行修复成本较高,还不如自己写一个。
首先统一一些概念。把覆盖被hook函数的指令称为跳板0,因为这部分指令越短越好,所以其大多数情况下只是一个跳板,跳到真正的用于hook指令处,这部分指令称为shellcode(shellcode不是必须的,比如跳板0直接跳到hook函数去执行,那么就不需要shellcode)。
这里假设都有一些arm指令的基础了,或者对hook已经有些理解了。后来我想了下我这篇更偏向于怎么写一个稳定可用hook框架,更偏向设计、编程,所以适合已经有些基础的,不是从0讲述hook到实现,虽然接下来的部分也会有很细节的部分,但是是针对一些特定的点。
建议可以先看下其他人,比如ele7enxxh、GToad写的一些文章。
inline hook这种东西,我是感觉当你掌握汇编、自己有需求的情况下,不经过学习也是可以从0写出一个hook框架的,确实是原理很简单的。
最容易想到的一种实现方式,使用跳板0覆盖一个函数的指令,当执行到这个函数的时候其实就是执行跳板0,跳板0在不修改寄存器的情况跳到执行hook函数。如果在hook函数内不需要执行原函数是最简单的,到这hook就是一个完整的hook。
如果需要执行原函数,那么在跳板0覆盖指令之前先备份指令,执行原函数之前把备份的指令再覆盖回去,执行之后再覆盖回跳板0。确实是最简单的方法、也确实可以,但是也有一个很大的问题,就是多线程的问题,在把备份的指令覆盖回去之后,其他线程执行到这里不就hook不到了,甚至crash。
因为无法加锁(真正有效的锁),而暂停所有线程的太重了,所以基本上只有自己确定某个函数不存在多线程问题或者无需调用原函数才可用。写个demo,熟悉下hook还行,真的实际使用是不行的。
也是因为这个问题,基本上目前的inline hook都会避免再次覆盖指令。不能覆盖回去,那么就直接执行备份的指令,执行后再跳到跳板0之后再继续执行。
也确实是可行的,大部分指令是可以这么做的,但是也有例外。比如备份的指令中有b/bl指令跳到一个偏移地址,跳转到的地址等于当前地址+偏移。
而备份后的指令取得当前地址肯定是不等于原来的当前地址的,所以就跳到错误的地址去执行了。
好在我们可以进行修复,计算出原来要跳到的绝对地址,把这条b指令替换成ldr pc指令。其他指令的一些修复也是类似的道理。
Android_Inline_Hook就是这样的实现,只能接收读写寄存器和堆栈,原函数还运行。
那么其实这种方式主要作用就是读写参数寄存器、栈,不能读写函数返回值,如果是不受参数控制流程的函数就无能为力了(当然是可以逆向分析,在已经得到返回值的指令处hook,但是应用场景太小了)。
那么实现的核心就是,跳板0->dumpshellcode。dumpshellcode把寄存器以数组的形式存放(栈就是天然的数组),把这个数组传递给dump函数,dump函数接收处理寄存器(未生效)。
dump函数返回shellcode,恢复数组数据到寄存器,完成恢复或者修改。执行backupshellcode(备份的原函数的开头部分,修复pc,跳回原函数之后的部分)完成原函数的执行流程。
最常用、最符合使用习惯的的方式。
实现的核心,跳板0->funshellcode。funshellcode可以在上面的dumpshellcode基础上实现也可以全新实现。
在dumpshellcode基础上可以取巧一些,前面的保存dump寄存器保留,把后面的执行backupshellcode换成执行hook函数。
全新实现就是不保存寄存器,那么就可以把hook函数放在跳板shellcode中,直接跳到hook函数;也可以跳板0->funshellcode,funshellcode中再跳转到hook函数。
之后就进入hook函数,如果不调用原函数,直接返回一个返回值或者void函数什么都不做即可(如果是参数也做返回值的情况就修改参数)。如果调用原函数,就通过一个结构体/map等查询被hook函数地址对应的backupshellcode,把backupshellcode转成函数指针,传参调用,即可完成原函数的调用、或者返回值。
在dump的基础上,执行backupshellcode(备份的原函数的开头部分,修复pc,跳回原函数之后的部分)完成原函数的执行流程之后返回到dumpshellcode,调用另一个dump函数,读写返回值(r0/x0,r1/x1寄存器)。
不同于dump的地方在于如果要返回到dumpshellcode,那么在调用backupshellcode之前应该备份原来的lr寄存器。考虑到多线程的问题,肯定是不能用一个固定的变量/地址去存储lr寄存器的,可能被覆盖,同一个线程哪怕是递归调用函数也是有顺序的,所以每个线程的被hook函数使用一个顺序的容器保存lr,后进先出。
在dump的基础上,不执行backupshellcode(备份的原函数的开头部分,修复pc,跳回原函数之后的部分),直接修改r0/x0、r1/x1寄存器,返回。和dump函数很多地方是一致的,应用于只需要返回值,并不需要原函数执行的情况。
以上四种场景应该是足够满足hook的需要了。
因为无法直接操作pc,那么实现跳转(通用情况)需要占用一个寄存器。要么使用一个不会被使用的寄存器(哪有绝对不会被使用的寄存器),要么先保存这个寄存器,通过栈保存(之前就是忽略了这个问题在固定地址保存寄存器,那么多线程情况下就可能被覆盖),跳过去之后先恢复这个寄存器。例如:
stp X1, X0, [SP, #-0x10];//保存寄存器
...
ldr x0, [sp, #-0x8];//恢复x0寄存器
对应shellcode我们很容易实现,但是如果是c/c++函数(我们的hook函数)就麻烦了,直接在函数开头插代码肯定是不行的。在源码中函数第一行内嵌汇编恢复寄存器?很可惜,这种只在无参、无返回值(空实现)、只有内嵌汇编的情况会成功,其他情况下源码中的第一行并不是汇编中的第一行。
所以似乎陷入了无解的状态,在llvm中函数开头插指令?似乎太重了。所以回到原点了,就要考虑真的没有不使用寄存器跳转的可能吗?其实还是有的,b或者bl到偏移。
ARM64:
B:0x17向前跳转,0x14向后跳转
BL:0x97向前跳转 0x94向后跳转
偏移地址计算过程:
(目标地址 - 指令地址)/ 4 = 偏移
// 减8,指令流水造成。
// 除4,因为指令定长,存储指令个数差,而不是地址差。
完整指令:
.text:000000000008CC84 8D B3 FF 97 BL je_arena_malloc_hard
.text:0000000000079AB8 je_arena_malloc_hard
计算偏移:
(79AB8-8CC84) / 4 = FFFFFFFFFFFFB38D
FFB38D | 0x97000000 = 97FFB38D
所以理论上,如果被hook的函数和hook函数/跳板/shellcode的距离在正负67108860/64m的范围内,64m=67108864,还有0,比如BL 0=00000094。那么这样就可以省一个寄存器了,针对arm64不能直接操作pc的问题,这应该是一个解决方案。
那么单指令hook除了异常的方式是不是就是指的这种方式呢?只需要覆盖一条指令,关键是怎么确保被hook函数和hook函数地址在正负64m内呢。怎么能申请到这块地址的内存呢。
也许可以查看proc/pid/maps,在要hook的so附近寻找未使用的空间,然后使用mmap申请,不确定是否可行。
暂时可用的一些方式如下(最终未采用):
实际上到内存中还是被和代码段放在一起都是可读可执行,没有写的权限。
.section ".my_sec", "awx"
.global _myopen_start
//.text
_myopen_start:
ldr x0, [sp, #-0x8];
b my_open;
.end
//用于手动生成蹦床代码,因为hook代码和这个蹦床一起编译的,所以基本上不会超出加减64m,那么就可以使用b跳转
//到偏移,就不用占用一个寄存器了。需要每增加一个hook函数,就加一个蹦床,相应的生成shellcode中跳转到hook
//函数的地方都要改成这个蹦床的地址。难就难在这个不好通过内嵌汇编实现,因为b跟的是个偏移值,在汇编中可以使用
//函数名称,编译器替换,但是内嵌汇编中不行。而自己计算
//用于演示,因为不是最终方案,不写完整代码了。
//修改trampoline指向蹦床即可
.data
replace_start:
ldr x0, trampoline; //如果每一个函数都在源码中新建一个shellcode的话,而不是动态复制生成,那么这个shellcode和蹦床可以合为一个。
br x0; //不能改变lr寄存器
trampoline: //蹦床的地址
.double 0xffffffffffffffff
replace_end:
77c5545000-77c557c000 r-xp 00000000 103:37 533353 /data/app/com.zhuotong.myinhk-TRvqt0ReHRjQeeAnpXDnFQ==/lib/arm64/libInlineHook.so
77c558b000-77c558e000 r--p 00036000 103:37 533353 /data/app/com.zhuotong.myinhk-TRvqt0ReHRjQeeAnpXDnFQ==/lib/arm64/libInlineHook.so
77c558e000-77c558f000 rw-p 00039000 103:37 533353 /data/app/com.zhuotong.myinhk-TRvqt0ReHRjQeeAnpXDnFQ==/lib/arm64/libInlineHook.so
unsigned int hkArry[256*2] __attribute__ ((section(".my_sec")));
//下面为伪代码,通过这样也可以实现不修改/保存寄存器、不修改hook函数,动态生成蹦床完成hook。
//优点是不用保存寄存器,缺点是因为正负64m的限制,hook函数应该和hook库是一起编译成动态库/可执行文件的。不能单独使用hook库。
//256个蹦床,实际使用可能要考虑这个hkArry内存对齐的问题(如果编译器未做内存对齐)。
//unsigned int hkArry[256 * 2] __attribute__ ((section(".my_sec")));
//void test_trampoline(){
//仅用于演示,应该先设置内存为可读写/执行,
// hkArry[0] = 0xf85f83e0;//或者memcpy, ldr x0, [sp, #-0x8];
//例如这样计算偏移,组合b指令。
// unsigned long offset = (unsigned long) my_open - ((unsigned long) hkArry + 1 * 4);
// hkArry[1] = 0x14000000;//计算偏移,生成指令,b 0;
//}
STP X1, X0, [SP, #-0x10];//因为要使用一个寄存器,所以先保存,当然不一定是x0
LDR X0, 8; //把shellcode/hook函数地址存到x0
BR X0; //执行shellcode/hook函数
ADDR(64)
LDR X0, [SP, -0x8];//因为不能操作pc,所以跳回来的时候免不了也要使用一个寄存器
LDR X17, 8;
BR x17;
ADDR(64)
.include "../../asm/base.s"
//.extern _dump_start
//.extern _dump_end
//.extern _hk_info
//.set _dump_start, r_dump_start
//.set _dump_end, r__dump_end
//.set _hk_info, r__hk_info
//.global _dump_start
//.global _dump_end
//.global _hk_info
//.hidden _dump_start
//.hidden _dump_end
//.hidden _hk_info
...完整代码请阅读原文
//hook信息
typedef struct STR_HK_INFO{
void (*onPreCallBack)(struct my_pt_regs *, struct STR_HK_INFO *pInfo); //回调函数,在执行原函数之前获取参数/寄存器的函数
void * pOriFuncAddr; //存放备份/修复后原函数的地址
void (*pre_callback)(struct my_pt_regs *, struct STR_HK_INFO *pInfo); //pre_callback,内部做保存lr的操作,之后回调onPreCallBack,不能被用户操作
void (*onCallBack)(struct my_pt_regs *, struct STR_HK_INFO *pInfo); //回调函数,执行原函数之后获取返回值/寄存器的函数
void (*aft_callback)(struct my_pt_regs *, struct STR_HK_INFO *pInfo); //aft_callback,内部做恢复lr的操作,之后回调onCallBack,不能被用户操作
void *pHkFunAddr; //hook函数,即自定义按照被hook的函数原型构造,处理参数/返回值的函数
//以上为在shellcode中通过偏移直接或间接用到的,所以如果有变动,相应的shellcode也要跟着变动
bl get_lr_pc; //lr为下条指令
add lr, lr, #8; //lr为blr x3的地址
str lr, [sp, #0x108]; //lr当作pc,覆盖栈上的x0
blr x3
...
get_lr_pc:
ret; //仅用于获取LR/PC
/**
* 用户自定义的stub函数,嵌入在hook点中,可直接操作寄存器等
* @param regs 寄存器结构,保存寄存器当前hook点的寄存器信息
* @param pInfo 保存了被hook函数、hook函数等的结构体
*/
void onPreCallBack(my_pt_regs *regs, HK_INFO *pInfo) //参数regs就是指向栈上的一个数据结构,由第二部分的mov r0, sp所传递。
{
const char* name = "null";
if (pInfo) {
if (pInfo->methodName) {
name = pInfo->methodName;
} else {
char buf[20];
sprintf(buf, "%p", pInfo->pBeHookAddr);
name = buf;
}
}
// LE("tid=%d onPreCallBack:%s", gettid(), name);.....
//.include "../../asm/base.s"
.global r_dump_start
.global r_dump_end
.global r_hk_info
.hidden r_dump_start
.hidden r_dump_end
.hidden r_hk_info
.data......完整代码请阅读原文查看
//头文件
#include <map>
#include <pthread.h>
#include <vector>
typedef std::vector<unsigned long> LRS;
//static LRS lrs;
struct STR_LR {
};
typedef std::map<const void*, LRS*> LR_MAP;
//typedef std::map<pid_t, LR_MAP*> TID_MAP;
typedef std::map<pid_t, LR_MAP*> TID_MAP;
static TID_MAP * getTid_map();
static void saveLR(void* key_fun, unsigned long lr);
static unsigned long getLR(void* key_fun);
void test_dump_with_ret(){
LE("open=%p, callback=%p", open, onPreCallBack);
if (dump((void *)(open), onPreCallBack, onCallBack, "open") != success) {
LE("hook open error");
}
int fd = open("/system/lib/libc.so", O_RDONLY);
LE("open /system/lib/libc.so, fd=%d", fd);
}
void test_dump_ret(){
LE("open=%p, callback=%p", open, onPreCallBack);
if (dump((void *)(open), NULL, onCallBack, "open") != success) {
LE("hook open error");
}
int fd = open("/system/lib/libc.so", O_RDONLY);
LE("open /system/lib/libc.so, fd=%d", fd);
}
.global j_dump_start
.global j_dump_end
.global j_hk_info
.hidden j_dump_start
.hidden j_dump_end
.hidden j_hk_info
.data
j_dump_start:
......完整代码请阅读原文查看
void test_dump_just_ret(){
LE("open=%p, callback=%p", open, onPreCallBack);
if (dumpRet((void *)(open), onCallBack, "open") != success) {
LE("hook open error");
}
int fd = open("/system/lib/libc.so", O_RDONLY);
LE("open /system/lib/libc.so, fd=%d", fd);
}
.global replace_start
.global replace_end
.global p_hk_info
.hidden replace_start
.hidden replace_end
.hidden p_hk_info
.data
//这种方式尽量用于标准的c/c++函数,因为通过hook函数再调用原函数,只能保证参数寄存器和lr寄存器是一致的,其他寄存器可能被修改。
......完整代码请阅读原文查看
typedef int (*old_open)(const char* pathname,int flags,/*mode_t mode*/...);
typedef int (*old__open)(const char* pathname,int flags,int mode);
typedef int (*old__openat)(int fd, const char *pathname, int flags, int mode);
typedef FILE* (*old_fopen)(const char* __path, const char* __mode);
old_open *ori_open;
old__open *ori__open;
//可变参数的函数,需要自己按照被hook函数的逻辑,解析出参数再传递给原函数。
//因为并不清楚参数个数/类型,如果不改变参数的情况下还有方法不解析参数调用原函数。
//但是如果改变了参数,比如printf中的fmt,那么理论上后面的参数类型个数也应该改变,这种情况下应该是使用者
//已经清楚共用多少参数和类型,应该自己调用,而如果只改fmt,应该会出bug的。
//所以如果只是想打印明确类型的参数,不改变参数直接调用原函数的情况,应该实现下参数的解析重组/传递,待实现
......完整代码请阅读原文查看
#ifdef __cplusplus
#include <map>
#include <pthread.h>
#include <vector>
typedef std::vector<HK_INFO*> INFOS;
//typedef std::map<const void*, HK_INFO*> LR_MAP;
#endif
enum hk_status{
success, hooked, error
};
struct RetInfo {
enum hk_status status;
HK_INFO *info;
};
......完整代码请阅读原文查看
.code 16
//.thumb //和上面效果一致
//.force_thumb //强制thumb
shell_code:
push {r0, r1, r2, r3} //r0=r0, r1=sp(push之前的sp), r2=r14/lr, r3=cpsr
mrs r0, cpsr
str r0, [sp, #0xC]
str r14, [sp, #8]
Error: lo register required -- `str r14,[sp,#8]'
Error: unexpected character `w' in type specifier
Error: bad instruction `str.w r14,[sp,#8]'
.syntax unified
.text
.align 4
.globl helloThumb
.thumb
.thumb_func
helloThumb:
add.w r0, r0, r1, lsl #2
bx lr
.syntax unified
.force_thumb
LDR PC, [PC, #-4];流水线,pc为addr下面的指令,所以-4
addr:0x12345678
LDR PC, [PC, #0];流水线,pc为addr指令
addr:0x12345678
.global _dump_start
.global _dump_end
.global _hk_info
.global _oriFuc
.hidden _dump_start
.hidden _dump_end
.hidden _hk_info
.hidden _oriFuc
//可用于标准的c/c++函数、非标准函数、函数的一部分(用于读写寄存器),前提都是字节长度足够
//非标准函数即非c/c++编译的函数,那么手写汇编可能存在并不遵守约定的情况,比如我们使用了sp寄存器,并在未使用的栈上保存寄存器
//但是可能不是满递减而是反过来满递增,或者不遵守栈平衡,往栈上写数据,但是并不改变sp寄存器。当然应该是很少见的。
......完整代码请阅读原文查看
.global r_dump_start
.global r_dump_end
.global r_hk_info
.hidden r_dump_start
.hidden r_dump_end
.hidden r_hk_info
.data
r_dump_start: //用于读写寄存器/栈,需要自己解析参数,不能读写返回值,不能阻止原函数(被hook函数)的执行
//从行为上来我觉得更偏向dump,所以起名为dump。
......完整代码请阅读原文查看
.global j_dump_start
.global j_dump_end
.global j_hk_info
.hidden j_dump_start
.hidden j_dump_end
.hidden j_hk_info
.data
j_dump_start:
push {r0-r4} //r0=r0,中转用 r1=sp(push之前的sp), r2=r14/lr, r3=pc, r4=cpsr
mrs r0, cpsr
str r0, [sp, #0x10] //r4的位置存放cpsr
str r14, [sp, #8] //r2的位置存放lr
add r14, sp, #0x14
str r14, [sp, #4] //r1的位置存放真实sp
pop {r0} //恢复r0
push {r0-r12} //保存r-r12,之后是r13-r16/cpsr
......完整代码请阅读原文查看
.global replace_start
.global replace_end
.global p_hk_info
.hidden replace_start
.hidden replace_end
.hidden p_hk_info
.data
//这种方式尽量用于标准的c/c++函数,因为通过hook函数再调用原函数,只能保证参数寄存器和lr寄存器是一致的,其他寄存器可能被修改。
replace_start: //如果只是替换/跳到hook函数,其实是不用保存寄存器的,只是重新写比较麻烦,所以在之前的基础上
push {r0-r4} //r0=r0,中转用 r1=sp(push之前的sp), r2=r14/lr, r3=pc, r4=cpsr
mrs r0, cpsr
void test_args_11(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11){
LE("a0=%d, a1=%d, a2=%d, a3=%d, a4=%d, a5=%d,"
" a6=%d, a7=%d, a8=%d, a9=%d, a10=%d. a11=%d",
a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
}
void nullCallBack(my_pt_regs *regs, HK_INFO *pInfo){
}
void test_args_for_cache(){
//测试多参数传递情况
dump((void *)(test_args_11), nullCallBack, /*onCallBack*/NULL);
// dumpRet((void *) (test_args_11), onPreCallBack, "test_args_11");
test_args_11(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
HK_INFO *pInfo = isHooked((void*)test_args_11);
// unHook(pInfo);
}......完整代码请阅读原文查看
int (*ori__system_property_get)(const char *name, char *value);
//注意操作参数之前最好都检查参数是否为null,如果原函数也是crash处理还行,如果不是就改变了函数逻辑、进程中断等
//所以细节都要注意,参数和返回值也有对应关系
int my__system_property_get(const char *name, char *value){
LE("hk: __system_property_get(%s, %p)", name ? name : "is null", value);
ori__system_property_get = (int (*)(const char *, char *))(getOriFunByHkFun(
(void *) (my__system_property_get)));
if (!ori__system_property_get) {
ori__system_property_get = __system_property_get;
}
int ret = ori__system_property_get(name, value);
LE("ori: __system_property_get(%s, %s)=%d", name ? name : "is null", ret > 0 ? value : "is null", ret);
if (name && !strncmp("ro.serialno", name, strlen("ro.serialno"))) {
strcpy(value, "12345678");
return (int)(strlen("12345678"));
}
return ret;
}......完整代码请阅读原文查看
.text:0000000000000EF8 dlopen ; DATA XREF: LOAD:0000000000000508↑o
.text:0000000000000EF8
.text:0000000000000EF8 var_s0 = 0
.text:0000000000000EF8
.text:0000000000000EF8 ; __unwind {
.text:0000000000000EF8 STP X29, X30, [SP,#-0x10+var_s0]!
.text:0000000000000EFC MOV X29, SP
.text:0000000000000F00 MOV X2, X30
.text:0000000000000F04 BL .__loader_dlopen
.text:0000000000000F08 LDP X29, X30, [SP+var_s0],#0x10
.text:0000000000000F0C RET
.text:0000000000000F0C ; } // starts at EF8
.text:0000000000000F0C ; End of function dlopen
STP X1, X0, [SP, #-0x10]
LDR X0, 8
BR X0
ADDR(64)
LDR X0, [SP, -0x8]
if (result != NULL) {
if (backup[0] == A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8)) {
*result = reinterpret_cast<void *>(backup[1]);
return;
}
f (
(align == 0 || area[0] == T$nop) &&
thumb[0] == T$bx(A$pc) &&
thumb[1] == T$nop &&
arm[0] == A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8)
) {
if (result != NULL)
*result = reinterpret_cast<void *>(arm[1]);
SubstrateHookMemory code(process, arm + 1, sizeof(uint32_t) * 1);
arm[1] = reinterpret_cast<uint32_t>(replace);
return sizeof(arm[0]);
}
long pagesize = sysconf(_SC_PAGE_SIZE);
void *pNewShellCode = NULL;// = malloc(sShellCodeLength);
int code = posix_memalign(&pNewShellCode,pagesize, pagesize);
mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
//通过测试:cacheflush无法刷新缓存,不过不确定是不是第三个参数的问题,ICACHE, DCACHE, or BCACHE,抽时间再看吧,
//不过gtoad的把地址转成无符号指针再取值肯定是不正确的。
// cacheflush(((uint32_t)(info->pBeHookAddr)), info->backUpLength, 0);//测试也无法刷新缓存
// cacheflush(PAGE_START((uintptr_t)info->pBeHookAddr), PAGE_SIZE, 0);//测试也无法刷新缓存
// cacheflush(info->pBeHookAddr, info->backUpLength, 0);//测试也无法刷新缓存
// if(TEST_BIT0((uint32_t)info->pBeHookAddr)){
// builtin_clear_cache((char)info->pBeHookAddr - 1, (char)info->pBeHookAddr - 1 + info->backUpLength);
// } else {
// builtin_clear_cache((char ) info->pBeHookAddr,
// (char ) info->pBeHookAddr + info->backUpLength);
// }
// builtin_clear_cache(PAGE_START((uintptr_t)info->pBeHookAddr), PAGE_END((uintptr_t)info->pBeHookAddr + info->backUpLength));
/if(TEST_BIT0((uint32_t)info->pBeHookAddr)){
cacheflush((char)info->pBeHookAddr - 1, (char)info->pBeHookAddr - 1 + info->backUpLength, 0);
} else {
cacheflush((char ) info->pBeHookAddr,
(char ) info->pBeHookAddr + info->backUpLength, 0);
}/
看雪ID:卓桐
https://bbs.pediy.com/user-670707.htm
推荐文章++++
好书推荐