前言
二
编译原理
1.编译过程

预处理阶段:预处理器(cpp)根据以字符#开头的命令修给原始的C程序,结果得到另一个C程序,通常以.i作为文件扩展名。主要是进行文本替换、宏展开、删除注释这类简单工作。
命令行:gcc -E hello.c hello.i
编译阶段:将文本文件hello.i翻译成hello.s,包含相应的汇编语言程序。
汇编阶段:将.S文件翻译成机器指令,然后把这些指令打包成一种可重定位目标程序的格式,并把结果保存在目标文件.o中(汇编——>机器)。
命令行:gcc -c hello.c hello.o
链接阶段:hello程序调用了printf函数,链接器(Id)就把printf.o文件并入hello.o文件中,得到hello可执行文件,然后加载到存储器中由系统执行。
函数库包括静态库和动态库
静态库:编译链接时,把库文件代码全部加入可执行文件中,运行时不需要库文件,后缀为.a。
动态库:编译链接时,不加入,在程序执行时,由运行时链接文件加载库,这样节省开销,后缀为.so。(gcc编译时默认使用动态库)
再经过汇编器和连接器的作用后输出一个目标文件,这个目标文件为可执行文件。
(1)链接方式

(2)链接库
命名规范为libXXX.a库函数会被连接进可执行程序,可执行文件体积较大可执行文件运行时,不需要从磁盘载入库函数,执行效率较高库函数更新后,需要重新编译可执行程序
命名规范为libXXX.so库函数不被连接进可执行程序,可执行文件体积较小可执行文件运行时,库函数动态载入使用灵活,库函数更新后,不需要重新编译可执行程序
2.可执行文件(ELF)
(1)ELF文件结构




(2)GOT和PLT
(1)如果是在其他中间文件中已经定义了的函数,链接阶段可以直接重定位到函数地址,比如我们从头文件访问另一个函数。
(2)如果是在动态库中定义了的函数,链接阶段无法直接重定位到函数地址,只能生成额外的小片段代码,也就是PLT表,然后重定位到该代码片段。




三
NDK基础知识
1.Android so文件的类型

adb shellcat /proc/cpuinfo

2.so文件加载
//加载的是libnative-lib.so,注意的是这边只需要传入"native-lib"System.loadLibrary("native-lib");//传入的是so文件完整的绝对路径System.load("/data/data/应用包名/lib/libnative-lib.so")
public static void load(String pathName) {Runtime.getRuntime().load(pathName, VMStack.getCallingClassLoader());}public static void loadLibrary(String libName) {Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());}
void load(String absolutePath, ClassLoader loader) {if (absolutePath == null) {throw new NullPointerException("absolutePath == null");}String error = doLoad(absolutePath, loader);if (error != null) {throw new UnsatisfiedLinkError(error);}}public void loadLibrary(String nickname) {loadLibrary(nickname, VMStack.getCallingClassLoader());}void loadLibrary(String libraryName, ClassLoader loader) {if (loader != null) {String filename = loader.findLibrary(libraryName);if (filename == null) {...
public static void load(String filename) {Runtime.getRuntime().load0(VMStack.getStackClass1(), filename);}public static void loadLibrary(String libname) {Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);}
synchronized void load0(Class fromClass, String filename) {if (!(new File(filename).isAbsolute())) {throw new UnsatisfiedLinkError("Expecting an absolute path of the library: " + filename); }if (filename == null) {throw new NullPointerException("filename == null");}String error = doLoad(filename, fromClass.getClassLoader());if (error != null) {throw new UnsatisfiedLinkError(error);}}public void loadLibrary(String libname, ClassLoader classLoader) {java.lang.System.logE("java.lang.Runtime#loadLibrary(String, ClassLoader)" +" is private and will be removed in a future Android release");loadLibrary0(classLoader, libname);}
四 各类hook技术原理分析
1.Xposed hook技术


2.Frida hook技术

accessflags = nativeentry_point_fromjni = 自定义代码的入口entry_point_from_quick_compiledcode = art_quick_generic_jni_trampoline函数的地址entry_point_frominterpreter = artInterpreterToCompiledCodeBridge函数地址
3.inlinehook 技术
(1)基本原理
(2)inlineHook组成
(3)inlineHook实现


(4)Android-Inline-Hook和SandHook 技术
4.PLT/GOT hook技术
5.Unicorn hook技术
五 各类hook技术实操
1.Xposed hook实操
(1)环境安装
(1) 4.4以下Android版本安装比较简单,只需要两步即可1.对需要安装Xposed的手机进行root2.下载并安装xposedInstaller,之后授权其root权限,进入app点击安装即可但是由于官网不在维护,导致无法直接通过xposedinstaller下载补丁包(2)Android 5.0-8.0 由于5.0后出现ART,所以安装步骤分成两个部分:xposed.zip 和XposedInstaller.apk,zip文件是框架主体,需要进入Recovery后刷入,apk文件用于Xposed管理1.完成对手机的root,并刷入reconvery(比如twrp),使用Superroot2.下载你对应的zip补丁包,并进入recovery刷入3.重启手机,安装xposedInstaller并授予root权限即可官网地址:https://dl-xda.xposed.info/framework/(3)由于Android 8.0后,Xposed官方作者没有再对其更新,我们一般就使用国内大佬riyu的Edxposed框架Magisk + riyu + Edxposed
asop镜像:https://developers.google.com/android/ota#hammerheadtwrp: https://twrp.me/xposed: https://dl-xda.xposed.info/framework/xposed installer https://repo.xposed.info/module/de.robv.android.xposed.installer
fastboot flash recovery twrp-3.4.0-0-hammerhead.img




(2)Xposed插件编写

android:name="xposedmodule" //是否配置为Xposed插件,设置为true android:value="true"/> android:name="xposeddescription" //模块名称 android:value="模块描述"/> android:name="xposedminversion" //最低版本号 android:value="54"/>进入app目录下的build.gradle文件, compile fileTree(includes:['*.jar'],dir:'libs') 替换成 provided fileTree(includes:['*.jar'],dir:'libs')现在provided变为 compileOnly如果使用compile,可以正常编译生成插件apk,但是当安装到手机上后,xposed会报错,无法正常工作public class Xposed01 implements IXposedHookLoadPackage { public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { if(loadPackageParam.packageName.equals("com.example.xposedlesson2")){ //判断目标包名 XposedBridge.log("XLZH"+loadPackageParam.packageName); //打出包名的信息 Log.i("Xposed01",loadPackageParam.packageName); } }}


2.frida hook实操
(1)环境安装
pip install frida==12.8.0pip install frida-tools==5.3.0pip install objection==1.8.4
frida --versionobjection --help

(2)frida使用
常见的hook命令:objection -g com.android.settings explore //注入设置应用android hooking list activities //查看Activity,service相同android intent launch_activity com.android.settings.DisplaySettings //实现Activity跳转android heap search instances com.android.settings.DisplaySettings //搜索类的实例android heap execute 0x2526 getPreferenceScreenResId //主动调用实例android hooking list classes //列出内存中所有类android hooking search methods display //列出内存中所有的方法android hooking watch class android.bluetooth.BluetoothDevice //hook相关类的所有方法android hooking watch class_method android.bluetooth.BluetoothDevice.getName --dump-args --dump-return --dump-backtrace //打印具体方法的参数、返回值、堆栈信息

attach方式 frida -U com.example.test -l hook.jsspwan启动 frida -U -f com.example.test -l demo1.js --no-pause
3.inlinehook实操
(1)Android-lnine-Hook
<1>编写目标函数so文件

<2>导入文件


<3>修改配置文件

<4>编写hook代码






源码解析:(1)dlopen:该函数将打开一个新库,并把它装入内存void *dlopen(const char *filename, int flag);参数1:文件名就是一个动态库so文件,标志位:RTLD_NOW 的话,则立刻计算;设置的是 RTLD_LAZY,则在需要的时候才计算libc.so是一个共享库======================参数中的 libname 一般是库的全路径,这样 dlopen 会直接装载该文件;如果只是指定了库名称,在 dlopen 会按照下面的机制去搜寻:根据环境变量 LD_LIBRARY_PATH 查找根据 /etc/ld.so.cache 查找查找依次在 /lib 和 /usr/lib 目录查找。flag 参数表示处理未定义函数的方式,可以使用 RTLD_LAZY 或 RTLD_NOW 。RTLD_LAZY 表示暂时不去处理未定义函数,先把库装载到内存,等用到没定义的函数再说;RTLD_NOW 表示马上检查是否存在未定义的函数,若存在,则 dlopen 以失败告终。参考链接:https://blog.nowcoder.ne t/n/5b2c04bbcccf431e9f1ab34aa02717fe=======================(2)dlsym:在 dlopen 之后,库被装载到内存。dlsym 可以获得指定函数( symbol )在内存中的位置(指针)。void *dlsym(void *handle,const char *symbol);参数1:文件句柄 参数2:函数名
我们对一个目标so文件hook步骤如下:(1)我们获取so的handler,使用dlopen函数void* libhandler = dlopen("libc.so",RTLD_NOW);(2)我们获取hook目标函数的地址,使用dlsym函数void* strstr_addr = dlsym(libhandler,函数名);(3)声明原来的函数void* (*oldmethod)(char*,char*); //这个格式需要参考hook的函数声明现在的函数void* newmethod(char* a,char* b){return (void *)oldmethod(a,b);< code style="white-space: pre-wrap; outline: 0px; max-width: 1000%; text-align: left; display: flex; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 32.5px; box-sizing: border-box !important; overflow-wrap: break-word !important;"> }(3)使用registerInlinehook进行重定向,将hook函数地址重定向我们编写的新函数上(registerInlineHook((uint32_t) strstr_addr, (uint32_t) new_strstr, (uint32_t **) &old_strstr) != ELE7EN_OK//参数一:hook函数的地址 参数二:替换函数的地址 参数3:用来保存原来函数的地址(5)我们判断我们的hook操作是否成功,并且再次调用实现hook(inlineHook((uint32_t) strstr_addr) == ELE7EN_OK)
(2)SandHook实操
<1>导入文件

<2>配置环境



cmake {arguments '-DBUILD_TESTING=OFF'cppFlags "-frtti -fexceptions -Wpointer-arith"abiFilters 'armeabi-v7a', 'arm64-v8a'}
<3>编写hook代码



(1)导包,将SandHook中cpp文件夹下的包全部导入到项目中,并修改CMakeLists.txt中添加native.cpp, 修改java层导入so库为sandHook-native(2)配置相关的环境在配置文件build.gradle中配置externalNativeBuild {cmake {arguments '-DBUILD_TESTING=OFF'cppFlags "-frtti -fexceptions -Wpointer-arith"abiFilters 'armeabi-v7a', 'arm64-v8a'}}(3)编译可以成功通过(4)使用const char * libc = "/system/lib64/libc.so";old_fopen = reinterpret_cast<void *(*)(char *, char *)>(SandInlineHookSym(libc, "fopen",reinterpret_cast<void *>(new_fopen)));参数2:hook的函数 参数3:新的函数添加原理hook旧函数的声明void* (*old_fopen)(char*,char*);实现新的函数功能void* new_fopen(char* a,char* b){__android_log_print(6,"windaa","I am from new open %s",a);re turn old_fopen(a,b);}(5)运行测试是否成功启动
4.PLT/GOT hook实操






<1>获得so模块的加载地址
char line[1024]; int *start; int *end; int n=1; //1.拿到so的起始地址// 749e5d7000-749e5db000 r--p 000f4000 103:09 441 /system/bin/linker64// 749e5db000-749e5dc000 rw-p 000f8000 103:09 441 /system/bin/linker64 FILE *fd = fopen("/proc/self/maps","r"); while (fgets(line,sizeof(line),fd)){ if(strstr(line,"libnative-lib.so")){
__android_log_print(6,"windaa","%s",line); if(n==1){ start = reinterpret_cast<int *>(strtoul(strtok(line, "-"),NULL,16)); end = reinterpret_cast<int *>(strtoul(strtok(NULL, " "),NULL,16)); } else{ strtok(line,"-"); end = reinterpret_cast<int *>(strtoul(strtok(NULL, " "),NULL,16)); } n++; } }
<2>找到got表的位置



<3>定位到节表的地址
//读取elf文件 Elf64_Ehdr ehd; int fp =open("/data/local/tmp/libnative-lib.so", O_RDONLY); if(fp == -1){ __android_log_print(4,"windaa","%s","error"); } //读取elf文件的文件头 read(fp,&ehd,sizeof(Elf64_Ehdr)); //读取节表的地址 unsigned long shof = ehd.e_shoff; //读取节表的数量 int shnum = ehd.e_shnum; //读取每个节表的大小 int shsize = ehd.e_ehsize; //记录一下str表的偏移,主要是获取后面got的字符串值 int shstr = ehd.e_shstrndx;
<4>定位到got表的位置和函数位置
//2.拿到字符串表Elf64_Shdr shdr;//定位字符串,节表地址加字符串表偏移×节表个数lseek(fp,shof+shstr*shsize,SEEK_SET);//此时节表就定位到字符串表开头read(fp,&shdr,shsize);//分配一个字符串表大小char* strtable = (char *)malloc(shdr.sh_size);__android_log_print(6,"windaa","shdrsize %p",shdr.sh_offset);//将字符串片指针移动到0x34104上lseek(fp,shdr.sh_offset,SEEK_SET);read(fp,strtable,shdr.sh_size);//将指针移动到节表开头lseek(fp,shof,SEEK_SET);//遍历查找到gotfor(int i=0;i//从节表开头开始读取字符串,每次读取一个节表read(fp,&shdr,shsize);//通过节表的索引找到字符串表中对应的值if(strcmp(&strtable[shdr.sh_name], ".got")==0){//定位到got表的地址int* saddr = start+shdr.sh_addr/4;//整个got表的大小int size = shdr.sh_size;//遍历got表中的函数for(int j=0;j8){uint64_t value = *(uint64_t *)(saddr + j / 4);//找到mywind的地址if(reinterpret_cast<uint64_t>(mywin0) == value){__android_log_print(6,"windaa","value %p",value);//替换mywind地址// 获取当前内存分页的大小uint64_t page_size = getpagesize();// 获取内存分页的起始地址(需要内存对齐)//page要保护的是函数的绝对地址,而不是相对地址uint64_t entry_page_start = (uint64_t)(saddr+j/4) & (~(page_size - 1));// 修改内存属性为可读可写可执行if(mprotect((uint64_t*)entry_page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC) == -1){__android_log_print(6,"windaa","%s","mprotect failed");}value = (uint64_t)mywin1;//将mywind0函数的地址换成mywind1函数的地址memcpy((saddr+j/4),&value,16);}}}}

5.Unicorn hook使用


六
实验总结
参考文献
参考书目:《程序员的自我修养——链接、装载与库》
https://www.geek-share.com/detail/2774116640.htmlhttps://www.jianshu.com/p/0ac63c3744ddhttps://www.zhihu.com/question/21249496https://www.codeleading.com/article/37234101170/
https://zhuanlan.zhihu.com/p/389889716https://mabin004.github.io/2018/07/31/Mac%E4%B8%8A%E7%BC%96%E8%AF%91Frida/https://zhuanlan.zhihu.com/p/269441842https://blog.csdn.net/sdoyuxuan/article/details/78481239https://www.cnblogs.com/codingmengmeng/p/6046481.htmlhttps://blog.csdn.net/sssssuuuuu666/article/details/78788369https://www.malwaretech.com/2015/01/inline-hooking-for-programmers-part-1.htmlhttps://juejin.cn/post/6844903993668272141
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/14923,转载请注明出处。


评论0