NDK汇编(开发)
基础配置
https://www.jianshu.com/p/c546783ad284
idea安装cmake和NDK
1、ubuntu自动加进环境变量
2、windows要自己添加
source ~/.bashrc
1
| ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk
|
IDA远程调试:
1 2
| 1、把 IDA dbgsrv目录下的文件在手机执行 android_server 2、用32位IDA Debugger调试程序
|
elf文件
arm 模式:定长指令集,四个字节
thumb 模式:不定长
T标志位
clang 开发
https://developer.android.com/ndk/guides/other_build_systems?hl=zh-cn
https://www.cnblogs.com/burner/p/clang-fen-si-bu-bian-yimainc.html
clang路径在SDK/NDK下
1 2
| C:\Users\admin\AppData\Local\Android\Sdk\ndk\28.0.12674087\toolchains\llvm\prebuilt\windows-x86_64\bin C:\Users\admin\AppData\Local\Android\Sdk\ndk\28.0.12674087\toolchains\llvm\prebuilt\linux-x86_64\bin
|
GDB
安装GEF
GDB multiarch 远程调试,原理同IDA远程调试,需上传服务端文件
1 2 3 4 5 6 7 8
| Sdk/ndk/23.1.7779620/prebuilt/android-arm/gdbserver ./gdbserver :1234 hello.o
gdb-multiarch target remote ip:port
gdb-multiarch gef-remote -p 3929 ip:port #雷电需要用x86_64版本,提示权限不足
|
ARM 案例
案例一
在 .o文件里用汇编代替原有函数(print、getchar)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #printf #只有return 0的汇编 main: .fnstart @ %bb.0: .pad #4 sub sp, sp, #4 movw r0, #0 str r0, [sp] movw r0, #0 add sp, sp, #4 bx lr
main: .fnstart push {lr}
ldr r0,[r1,#4] bl printf
movw r0, #0 pop {lr} bx lr
|
案例二
根据汇编还原为C代码
1、IDA打开v7a的 SO文件
2、找到导出 check
3、修改第一个默认参数为 JNIEnv,这样就能关联到内置的函数
4、算法还原
这些R0 、R1指代第一第二个参数吗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| TestDec ; CODE XREF: j_TestDec+8↑j .text:00001062 ; DATA XREF: LOAD:00000250↑o ... .text:00001062 ; __unwind { .text:00001062 PUSH {R4,R5,R7,LR} .text:00001064 ADD R7, SP, #8 .text:00001066 MOV R4, R0 .text:00001068 BLX strlen .text:0000106C CMP R0, #2 .text:0000106E BCC loc_108A .text:00001070 MOVS R5, #0 .text:00001072 .text:00001072 loc_1072 ; CODE XREF: TestDec+26↓j .text:00001072 ADDS R1, R4, R5 .text:00001074 LDRB R0, [R4,R5] .text:00001076 LDRB R2, [R1,#0x10] .text:00001078 STRB R2, [R4,R5] .text:0000107A ADDS R5, #1 .text:0000107C STRB R0, [R1,#0x10] .text:0000107E MOV R0, R4 ; s .text:00001080 BLX strlen .text:00001084 CMP.W R5, R0,LSR#1 .text:00001088 BCC loc_1072 .text:0000108A .text:0000108A loc_108A ; CODE XREF: TestDec+C↑j .text:0000108A LDRB R0, [R4] .text:0000108C CBZ R0, locret_10B8 .text:0000108E LDRB R1, [R4,#1] .text:00001090 STRB R1, [R4] .text:00001092 STRB R0, [R4,#1] .text:00001094 MOV R0, R4 ; s .text:00001096 BLX strlen .text:0000109A CMP R0, #3 .text:0000109C BCC locret_10B8 .text:0000109E MOVS R5, #0 .text:000010A0 .text:000010A0 loc_10A0 ; CODE XREF: TestDec+54↓j .text:000010A0 ADDS R0, R4, R5 .text:000010A2 LDRB R1, [R0,#2] .text:000010A4 LDRB R2, [R0,#3] .text:000010A6 STRB R2, [R0,#2] .text:000010A8 STRB R1, [R0,#3] .text:000010AA MOV R0, R4 ; s .text:000010AC BLX strlen .text:000010B0 ADDS R1, R5, #4 .text:000010B2 ADDS R5, #2 .text:000010B4 CMP R1, R0 .text:000010B6 BCC loc_10A0 .text:000010B8 .text:000010B8 locret_10B8 ; CODE XREF: TestDec+2A↑j .text:000010B8 ; TestDec+3A↑j .text:000010B8 POP {R4,R5,R7,PC} .text:000010B8 ; End of function TestDec
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include <stdio.h> #include <string.h> #include <stdlib.h> int TestDec(char *str){ char *r4 = str; int lenstr = strlen(str); if(lenstr>=2){ for(int r5=0;r5< lenstr>>1;r5++){ char * r1 = r4+r5; char r0 = *(r4+r5); char r2 = *(r1+16); *(r4+r5) = r2; *(r1+16) = r0; } } char r0 = *(r4); if(r0){ char r1 = *(r4+1); *(r4) = r1; *(r4+1) = r0; if(lenstr >= 3){ for(int r5=0; r5+4 < lenstr;r5+=2){ char *rr0 = r4+r5; char rr1 = *(rr0+2); char rr2 = *(rr0+3); *(rr0+2) = rr2; *(rr0+3) = rr1; } } } printf("%s\n",r4); return 0; }
int main(){ char *s = "9007b55be5bf95adf72c5a365694182a"; int lenstr = strlen(s); char *arg = (char*)malloc(lenstr+1); memset(arg,0,lenstr+1); memcpy(arg,s,lenstr); TestDec(arg); return 0; }
|
可引入IDA 提供的 defs.h 后直接运行伪代码
https://www.52pojie.cn/forum.php?mod=viewthread&tid=1584115&highlight=ida
IDA快捷键&常用方法
- 汇编 & 伪代码的跳转(空格、Tab)
- 强制当做代码/数据(C、D)
- 数据 convert
- 重命名
- 查找调用链,Xref graph to
- 插件使用
https://blog.csdn.net/qq_41028985/article/details/119407917
熟悉算法
用于识别魔改算法或分析算法
输入信息/输出信息要hex编码
md5算法
SHA1算法
SHA2/256/512等后续一堆
HMAC(salt)
https://space.bilibili.com/253413704
1 2 3
| message:加密消息 key:加密密钥(需要补充) opad 和 ipad 都是固定
|
DES
1 2 3 4 5
| https://www.bilibili.com/video/BV1134y1Y71j https://www.bilibili.com/video/BV1QW411B7A4 https://www.bilibili.com/video/BV1KQ4y127AT https://juejin.cn/post/7084242293816983589 https://cloud.tencent.com/developer/article/1497864
|
https://www.cnblogs.com/jikexianfeng/p/10192024.html
CLion
分组加密
https://blog.csdn.net/a745233700/article/details/102311776
https://segmentfault.com/a/1190000040964999
https://www.bilibili.com/video/BV1U8411f74f/
AES
https://www.bilibili.com/video/BV1824y1y7gc
12345678,8个字节,64个位
APK未抹去符号表,MD5算法是否魔改分析
1、提取 so,用IDA分析
2、根据原MD5的几个关键步骤,分析汇编。搜索固定的表内容,没被魔改可以搜到
3、Hook SO地址
MD5常见魔改点:
1、明文加盐或填充
2、初始魔数的修改
3、常量表K
4、左移次数
5、F,G,H,I四个非线性变换函数等的逻辑
unidbg
IDA快捷键:H、Y、Tab
IDA 动态调试之反调试
遗留问题:虚拟机debuggable=1
1、getprop ro.debuggable = 0 的话 IDA 动态调试看不到进程名,部分功能被屏蔽
真机用:agiskHide Props Config 修改成功
虚拟机暂未成功
2、查看线程名要用新版的IDA,实测7.0的看不到,7.7的就看得到
1 2 3 4 5 6
| 1.Attach模式 附加 so动态调试。过掉反调试的方法 2.Spwan模式 过掉反调试的方法:这里讲原理
1. nop掉 2. 挂起线程 3. frida hook
|
IDA调试tips:
1 2 3 4 5 6
| 1、真机无网络通过USB调试,需配置端口转发才能IDA远程调试 adb forward tcp:23946 tcp:23946 本机->设备 adb reverse tcp:23946 tcp:23946 设备->本机 adb forward --remove tcp:<port>
2、静态调试和动态调试,IDA自动化分析的注释不同
|
步骤:
1 2 3 4
| 1、进入IDA动态调试,点击运行崩溃 2、在Threads窗口,找和包名一样的线程,Suspended 挂起运行正常 3、在Modules窗口,搜索包名/运行关键词/check/security,找到检测/校验模块,双击进入对应代码,下断点 4、点击触发,Tab切换伪代码
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var ByPassTracerPid = function () { var fgetsPtr = Module.findExportByName("libc.so", "fgets"); var fgets = new NativeFunction(fgetsPtr, 'pointer', ['pointer', 'int', 'pointer']); Interceptor.replace(fgetsPtr, new NativeCallback(function (buffer, size, fp) { var retval = fgets(buffer, size, fp); var bufstr = Memory.readUtf8String(buffer); if (bufstr.indexOf("TracerPid:") > -1) { Memory.writeUtf8String(buffer, "TracerPid:\t0"); console.log("tracerpid replaced: " + Memory.readUtf8String(buffer)); } return retval; }, 'pointer', ['pointer', 'int', 'pointer'])); }; setImmediate(ByPassTracerPid);
|
参考文章:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Android修改ro.debuggable 的四种方法 https://blog.csdn.net/jinmie0193/article/details/111355867
IDA动态调试破解AliCrackme与反调试对抗 https://blog.csdn.net/weixin_39190897/article/details/120808079
[原创]2015年AliCrackMe第二题的分析之人肉过反调试 https://bbs.pediy.com/thread-258595.htm
常用命令: adb shell am start -D -n com.yaotong.crackme/.MainActivity adb shell ps | findstr crackme jdwp协议端口转发:adb forward tcp:8700 jdwp:2494 jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700
|
APP过ROOT检测
安装面具:
https://www.bilibili.com/video/BV1UN4y137Z5/?vd_source=43c2c404de6d798650d44c856ee1e992
https://bbs.kanxue.com/thread-263203.htm
修改固有面具(修改源码)
https://blog.csdn.net/qq_41155858/article/details/127885048
root检测:
1.在代码中检测 -> hook/修改重打包(代码固有API检测是否root?)
2.检测系统属性(检测app安装的系统环境参数)
3.查找字符串(查找root关键词,比如su)
对抗方法:
1.hook
2.最新版magisk(配置排除列表、随机文件名) + shamiko(可有可无)+随机magisk
3.需要重新编译面具 改su名称(这个有点麻烦,最新版gsyh过不去就这个)
1 2 3 4
| 小米6重装最新版Magisk就可以了,直接过三个(新版的gs只开还是过不了) adb reboot bootloader https://magiskcn.com/ https://blog.csdn.net/qq_42663692/article/details/135704736
|
绕过的具体操作
1、最新版面具
2、修改面具名称
3、用自带的隐藏root模块
4、修改su名称
Tips:
1、面具安装在真机,需要提取真机boot,通过面具修复后生成.img文件,再重新刷回手机
2、面具安装在模拟器,要安装修改的magisk-delta版本,有直接刷在系统选项(https://magisk-delta.com/)
自编译面具
环境:win10
1 2 3 4 5 6 7 8
| 1、用AS自带的 java11,设置为环境变量 2、全程挂梯子 3、windows 用旧版 python,如python3.8
git clone --recurse-submodules https://github.com/topjohnwu/Magisk.git python build.py ndk python build.py clean python build.py -v all
|
需要修改成自定义指令的地方
1 2
| val isActive = versionCode > 0 追踪逻辑 "su","--mount-master"
|
Apktoolbox 重打包
windwos坑太多,用ubuntu试试,用ubuntu挂梯子
参考链接:
1 2
| https://blog.csdn.net/qq_41155858/article/details/127885048?spm=1001.2014.3001.5506 https://zhuanlan.zhihu.com/p/385255256
|
frida 反调试
1 2 3 4 5 6 7 8 9 10 11 12 13
| 参考文章: [翻译]多种特征检测 Frida ->https://bbs.pediy.com/thread-217482.htm
从inlinehook角度检测frida->https://blog.csdn.net/u010559109/article/details/120846740 9 Android逆向——过frida检测+so层算法逆向https://blog.csdn.net/weixin_43889136/article/details/127713563
原理:当被调试器附加之后 不为0:https://wiki.hackjie.com/2728.html
ptrace占坑: https://bbs.pediy.com/thread-268155.htm#msg_header_h2_16
adb shell dumpsys window w |grep \/ |grep name=
|
fridacheck app
Process terminated douban strstr
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| frida -U -f xxx %resume
ps -e | grep xxx adb shell dumpsys window w | grep \/ | grep name=
./frida-server-pass -l 0.0.0.0:1234 adb forward tcp:1234 tcp:1234 frida -H 手机ip:1234 -F
hook_RegisterNatives.js https://github.com/zhkl0228/unidbg
修改第一个参数为 JNIEnv*
通过IDA分析所用检测的方法,根据检测逻辑去hook fgets、fopen,为什么直接hook整个检测函数
function hook_readlink() {} function main(){ hook_readlink() } setImmediate(main)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| // code.js function hook_strstr() { // char *strstr(const char *haystack, const char *needle) 在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 var strstr = Module.findExportByName(null, "strstr"); if (null !== strstr) { Interceptor.attach(strstr, { onEnter: function (args) { this.frida = Boolean(0); this.haystack = args[0]; this.needle = args[1]; if (this.haystack.readCString() !== null && this.needle.readCString() !== null) { if (this.haystack.readCString().indexOf("frida") !== -1 || this.needle.readCString().indexOf("frida") !== -1 || this.haystack.readCString().indexOf("gum-js-loop") !== -1 || this.needle.readCString().indexOf("gum-js-loop") !== -1 || this.haystack.readCString().indexOf("gmain") !== -1 || this.needle.readCString().indexOf("gmain") !== -1 || this.haystack.readCString().indexOf("linjector") !== -1 || this.needle.readCString().indexOf("linjector") !== -1) { this.frida = Boolean(1); } console.log("this.haystack.readCString():",this.haystack.readCString()) console.log("this.needle.readCString():",this.needle.readCString()) } }, onLeave: function (retval) { if (this.frida) { retval.replace(ptr("0x0")); }
} }) console.log("anti anti-frida"); }
console.log("hook strstr over") }
function main(){ hook_strstr(); }
setImmediate(main);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| function bypass_fgets() { var fgetsPtr = Module.findExportByName("libc.so", "fgets"); var fgets = new NativeFunction(fgetsPtr, 'pointer', ['pointer', 'int', 'pointer']); console.log(fgetsPtr,fgets); Interceptor.replace(fgetsPtr, new NativeCallback(function (buffer, size, fp) { var retval = fgets(buffer, size, fp); var bufstr = buffer.readCString(); if (bufstr.indexOf("TracerPid:") > -1) { Memory.writeUtf8String(buffer, "TracerPid:\t0"); }
if (bufstr.indexOf("Name:\tgmain") > -1 || bufstr.indexOf("Name:\tgdbus") > -1 || bufstr.indexOf("Name:\tpool-frida") > -1 || bufstr.indexOf("Name:\tgum-js-loop") > -1 || bufstr.indexOf("SigBlk:\tffffffe0fffbfaff") > -1) { Memory.writeUtf8String(buffer, "TName:\t1"); } return retval; }, 'pointer', ['pointer', 'int', 'pointer'])) }
function hook_fopen() { var open_addr = Module.findExportByName("libc.so", "fopen") var io_map = Memory.allocUtf8String("/proc/13585/maps"); Interceptor.attach(open_addr, { onEnter: function (args) { if (args[0].readCString().indexOf("/maps") != -1) { args[0] = io_map } this.pathname = args[0] this.mode = args[1] }, onLeave: function (retval) { console.log("fopen pathname=" + this.pathname.readCString() + "---mode=" + this.mode) } }) }
function hook_readlink() { var aaa, bbb, ccc; Interceptor.attach(Module.findExportByName(null, "readlink"), { onEnter: function (args) { aaa = args[0]; bbb = args[1]; ccc = args[2]; }, onLeave: function (retval) { if (bbb.readCString().indexOf("frida") !== -1 || bbb.readCString().indexOf("gum-js-loop") !== -1 || bbb.readCString().indexOf("gmain") !== -1 || bbb.readCString().indexOf("tmp") !== -1 || bbb.readCString().indexOf("linjector") !== -1) { console.log('\nreadlink(' + 's1="' + aaa.readCString() + '"' + ', s2="' + bbb.readCString() + '"' + ', s3="' + ccc + '"' + ')'); bbb.writeUtf8String("/system/framework/boot.art") console.log("replce with: "+bbb.readCString()) retval.replace(0x1A) } } }); }
function main(){ bypass_fgets() hook_fopen() hook_readlink() }
setImmediate(main)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| // hook_anti-thread.js function hook_pthread_create() { var pthread_create_addr = Module.findExportByName(null, "pthread_create"); const __android_log_print_ptr = Module.findExportByName(null, '__android_log_print') const cm = new CModule(` extern void onMessageStr (const gchar * message); extern void onMessagePtr (void * message); extern void onMessageInt (int a); extern int __android_log_print(int prio, const char* tag, const char* fmt, ...); void hello(){ } void onEnter (GumInvocationContext * ic) { // void* arg2,arg3; if((((int)gum_invocation_context_get_nth_argument(ic, 2))&0xfff)==0x599){ onMessageStr("replace success"); gum_invocation_context_replace_nth_argument(ic,2,(gpointer)hello); } // arg2 = gum_invocation_context_get_nth_argument (ic, 2); onMessagePtr(gum_invocation_context_get_nth_argument (ic, 2)); // arg0 = (int)gum_invocation_context_get_nth_argument (ic, 2); // gum_invocation_context_replace_nth_argument(ic,2,(gpointer)100); // arg1 = (int)gum_invocation_context_get_nth_argument (ic, 3); // log ("function add arg0=%d arg1=%d ", arg0,arg1); // __android_log_print(3,"isDebug","arg0=%d,arg1=%d",arg0,arg1); } void onLeave (GumInvocationContext * ic) { int result; result = (int) gum_invocation_context_get_return_value (ic); onMessageInt(result); // gum_invocation_context_replace_return_value (ic,(gpointer)100); }`, { __android_log_print: __android_log_print_ptr, onMessageStr: new NativeCallback(messagePtr => { const message = messagePtr.readUtf8String(); console.log('onMessageStr:', message); }, 'void', ['pointer']), onMessagePtr: new NativeCallback(messagePtr => { console.log('onMessagePtr:', messagePtr ,hexdump(messagePtr)); }, 'void', ['pointer']), onMessageInt: new NativeCallback(messageInt => { console.log('onMessageInt:', messageInt); }, 'void', ['int']), }); Interceptor.attach(pthread_create_addr, cm); }
function replace_thread() { var pthread_create_addr = Module.findExportByName(null, "pthread_create"); var pthread_create = new NativeFunction(pthread_create_addr, "int", ["pointer", "pointer", "pointer", "pointer"]); Interceptor.replace(pthread_create_addr, new NativeCallback((parg0, parg1, parg2, parg3) => { var so_name = Process.findModuleByAddress(parg2).name; var so_base = Module.getBaseAddress(so_name); var offset = (parg2 - so_base); var PC = 0; console.log("normal find thread func offset", so_name, parg2,offset, offset.toString(16)); // i加密 guilvyouping 梆梆 budayuepao 360 leisu if( (so_name.indexOf("libexec.so")>-1 && offset===197069)|| (so_name.indexOf("libexec.so")>-1 && offset===196137) ){ }else if((so_name.indexOf("libDexHelper.so")>-1 && offset===684452)|| (so_name.indexOf("libDexHelper.so")>-1 && offset===724380)){
} else{ PC = pthread_create(parg0, parg1, parg2, parg3); } return PC; }, "int", ["pointer", "pointer", "pointer", "pointer"])); }
setImmediate(replace_thread)
// frida -U -f com.darvin.security -l hook_anti-thread.js --no-pause -o out.log // frida -UF -l hook_anti-thread.js
//frida -U -f com.gzlex.hui.guoziwei.travel --no-pause -l hook_anti-thread.js //frida -U -f tv.danmaku.bili -l hook_anti-thread.js --no-pause
|
1、hook app调用的各关键内置函数的调用 so和偏移值
2、针对每次调用进行功能hook,多个进行hook,筛选关键功能
打快照为什么要关机
自编译Frida
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| 安装全新ubuntu sudo apt-get install proxychains sudo gedit /etc/proxychains.conf socks5 ip port sudo apt-get install curl proxychains curl www.httpbin.org/ip // 验证代理IP sudo apt install git sudo dpkg -i vscode.deb code . proxychains sudo apt-get install build-essential curl git lib32stdc++-9-dev libc6-dev-i386 nodejs npm python3-dev python3-pip proxychains git clone -b 16.2.1 --recurse-submodules https://github.com/frida/frida cd frida sudo apt install m4
proxychains make -f Makefile.toolchain.mk
cd releng cat deps.mk | head -n 10
sudo wget https://build.frida.re/deps/20240123/toolchain-linux-x86_64.tar.bz2 sudo wget https://build.frida.re/deps/20240123/sdk-linux-x86_64.tar.bz2 sudo wget https://build.frida.re/deps/20240123/sdk-android-arm64.tar.bz2
./releng/setup-env.sh
cd releng cat setup-env.sh | grep ndk
proxychains wget https://dl.google.com/android/repository/android-ndk-r22b-linux-x86_64.zip unzip xxx
sudo gedit ~/.bashrc export ANDROID_NDK_ROOT=/XXX/XXX/android-ndk-r24 export PATH=$ANDROID_NDK_ROOT:$PATH ndk-build -v
https://blog.csdn.net/John_Lenon/article/details/136155942?spm=1001.2014.3001.5506
make core-android-arm64
make clean rm -rf build/ frida/build/tmp-android-arm64/frida-core/server
注意注意:执行make clean时候会把自建的python文件删除
|
魔改Frida
1 2 3 4
| https://github.com/hluwa/Patchs/tree/master/strongR-frida/frida-core
根据gmain修改gdbus 替换所有/data/local/tmp
|
ubuntu使用 v2
1 2 3 4 5 6 7 8 9
| sudo apt install libfuse2 wget https://github.com/v2ray/v2ray-core/releases/download/v4.27.0/v2ray-linux-64.zip wget https://github.com/Qv2ray/Qv2ray/releases/download/v2.6.3/Qv2ray.v2.6.3.linux-x64.AppImage unzip v2ray-linux-64.zip chmod +x Qv2ray.v2.6.3.linux-x64.AppImage ./Qv2ray.v2.6.3.linux-x64.AppImage # 修改内核设置/可执行文件路径/资源目录
hiddify
|
unidbg 模拟执行 SO
用起来啊,不然学个der
- 框架本身开启打印日志,就能打印出RegisterNatives的方法,找到动态注册的方法,通过IDA跳转过去,hook_RegisterNative.js
- jadx中tab键切换smail代码
- java项目,直接导入idea
- frida代码.$className 可查看类对象
1 2 3 4 5 6 7 8
| 1、AndroidEmulatorBuilder 指定包名初始化、指定SDK sdk19 -> Android 4(32位) sdk23 -> Android 6(64位) 2、创建虚拟机,指定APK 3、设定日志打印 4、指定loadLibrary,不然要加载的so文件名字为libhookdemo.so,只需要写hookdemo 5、根据包路径实例化类、初始化类 6、方法调用,指定方法声明&参数
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| androidEmulator = AndroidEmulatorBuilder .for64Bit() .addBackendFactory(new Unicorn2Factory(true)) .setProcessName("com.example.xx") .setRootDir(new File("target/rootfs")) .build();
memory = androidEmulator.getMemory(); memory.setLibraryResolver(new AndroidResolver(23)); dalvikVM= androidEmulator.createDalvikVM(new File("unidbg-android/src/test/java/fuck/xx.apk")); dalvikVM.setJni(this); dalvikVM.setVerbose(true);
dalvikModule = dalvikVM.loadLibrary("test", true) module = dalvikModule.getModule();
System.out.println(module.findSymbolByName("Java_com_example_hookdemo_MainActivity_tesucanshu_1jni").getAddress()); dalvikModule.callJNI_OnLoad(androidEmulator);
|
JNI参数类型
https://www.jianshu.com/p/54a0b7ccc35e
主动调用&特殊参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public void calltesucanshu_dizhi(){ DvmClass dvmClass = dalvikVM.resolveClass("com.example.xx.MainActivity"); DvmObject<?> dvmObject = dvmClass.newObject(null);
ArrayList<String> list3 = new ArrayList<>(); list3.add("test1"); list3.add("test2");
ArrayListObject arrayListObject = ArrayListObject.newStringList(dalvikVM,"test1","test2");
HashMap<Integer, String> Sites3 = new HashMap<>(); Sites3.put(1, "Google"); Sites3.put(2, "Runoob"); DvmObject<?> object_Sites3 = ProxyDvmObject.createObject(dalvikVM, Sites3);
List<Object> list = new ArrayList<>(); list.add(dalvikVM.getJNIEnv()); list.add(0); list.add(1); list.add(dalvikVM.addGlobalObject(new StringObject(dalvikVM,"11111"))); list.add(dalvikVM.addGlobalObject(ProxyDvmObject.createObject(dalvikVM,new int[]{3, 1, 2, 6, 4, 2}))); list.add(dalvikVM.addGlobalObject(dalvikVM.resolveClass("com.example.xx.Person").newObject(null))); list.add(dalvikVM.addGlobalObject(arrayListObject)); list.add(dalvikVM.addGlobalObject(object_Sites3)); list.add(dalvikVM.addGlobalObject(ProxyDvmObject.createObject(dalvikVM,new String[]{"nihao", "shijie"})));
int number =module.callFunction(androidEmulator,0x63BBC,list.toArray()).intValue(); System.out.println("111->"+number); System.out.println(dalvikVM.getObject(number)); }
|
JNI补环境
日志打印模式,src/test/resources/log4j.properties 修改test文件下debug
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
| public class testJNI extends AbstractJni { private final AndroidEmulator androidEmulator; private final Memory memory; private final VM dalvikVM; private final DalvikModule dalvikModule; private final Module module; public testJNI() { androidEmulator = AndroidEmulatorBuilder .for64Bit() .addBackendFactory(new Unicorn2Factory(true)) .setProcessName("com.example.hookdemo") .setRootDir(new File("target/rootfs")) .build(); memory = androidEmulator.getMemory(); memory.setLibraryResolver(new AndroidResolver(23)); dalvikVM = androidEmulator.createDalvikVM(new File("unidbg-android/src/test/java/lesson/testJNI/app-debug.apk")); dalvikVM.setJni(this); dalvikVM.setVerbose(true); dalvikModule = dalvikVM.loadLibrary("hookdemo", true); module = dalvikModule.getModule(); dalvikModule.callJNI_OnLoad(androidEmulator); }
public static void main(String[] args) { testJNI testJNI = new testJNI(); testJNI.callgetHash(); } @Override public DvmObject<?> allocObject(BaseVM vm, DvmClass dvmClass, String signature) { switch (signature){ case "com/example/hookdemo/Person->allocObject":{ return dvmClass.newObject(signature); } } return super.allocObject(vm, dvmClass, signature); }
private void callgetHash() { DvmClass dvmClass = dalvikVM.resolveClass("com.example.hookdemo.MainActivity"); String apk_path = "/data/app/com.example.hookdemo-qmoUcGSJuvbQaByYAxdvOg==/base.apk"; DvmObject dvmObject = dvmClass.newObject(null); DvmObject dvmObject1 = dvmObject.callJniMethodObject(androidEmulator, "getHash(Ljava/lang/String;)Ljava/lang/String;", apk_path); System.out.println(dvmObject1.getValue()); }
@Override public DvmObject<?> newObjectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) { switch (signature){ case "java/util/zip/ZipFile-><init>(Ljava/lang/String;)V":{ String path = (String) vaList.getObjectArg(0).getValue(); if(path.equals("/data/app/com.example.hookdemo-qmoUcGSJuvbQaByYAxdvOg==/base.apk")){ try { ZipFile zipFile = new ZipFile("unidbg-android/src/test/java/lesson/testJNI/app-debug.apk"); return vm.resolveClass("java/util/zip/ZipFile").newObject(zipFile); } catch (IOException e) { throw new RuntimeException(e); } } } } return super.newObjectV(vm, dvmClass, signature, vaList); }
@Override public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) { switch (signature){ case "java/util/zip/ZipFile->entries()Ljava/util/Enumeration;":{ ZipFile zipFile = (ZipFile) dvmObject.getValue(); Enumeration<? extends ZipEntry> entries = zipFile.entries(); DvmClass ZipEntryClass = vm.resolveClass("java/util/zip/ZipEntry");
List<DvmObject<?>> objs = new ArrayList<>(); while (entries.hasMoreElements()){ ZipEntry zipEntry = entries.nextElement(); objs.add(ZipEntryClass.newObject(zipEntry)); } return new com.github.unidbg.linux.android.dvm.Enumeration(dalvikVM,objs); } case "java/util/zip/ZipEntry->getName()Ljava/lang/String;":{ ZipEntry zipEntry = (ZipEntry) dvmObject.getValue(); String name = zipEntry.getName(); return new StringObject(dalvikVM,name); } case "java/util/zip/ZipFile->getInputStream(Ljava/util/zip/ZipEntry;)Ljava/io/InputStream;": { ZipFile zipFile = (ZipFile) dvmObject.getValue(); ZipEntry zipEntry = (ZipEntry) vaList.getObjectArg(0).getValue();
try { InputStream inputStream = zipFile.getInputStream(zipEntry); return vm.resolveClass("java/io/InputStream").newObject(inputStream); } catch (IOException e) { throw new RuntimeException(e); } } case "java/security/MessageDigest->digest()[B":{ MessageDigest messageDigest = (MessageDigest) dvmObject.getValue(); byte[] digest = messageDigest.digest(); return new ByteArray(dalvikVM,digest); }
} return super.callObjectMethodV(vm, dvmObject, signature, vaList); }
@Override public int callIntMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) { switch (signature){ case "java/io/InputStream->read([B)I":{ InputStream inputStream = (InputStream) dvmObject.getValue(); try { int read = inputStream.read((byte[]) vaList.getObjectArg(0).getValue()); return read; } catch (IOException e) { throw new RuntimeException(e); } } } return super.callIntMethodV(vm, dvmObject, signature, vaList); }
@Override public void callVoidMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) { switch (signature){ case "java/security/MessageDigest->update([B)V":{ MessageDigest messageDigest = (MessageDigest) dvmObject.getValue(); messageDigest.update((byte[]) vaList.getObjectArg(0).getValue()); return ; } case "java/io/InputStream->close()V" :{ InputStream inputStream = (InputStream) dvmObject.getValue(); try { inputStream.close(); } catch (IOException e) { throw new RuntimeException(e); } return ; } } super.callVoidMethodV(vm, dvmObject, signature, vaList); } }
|
IDA 编译名转换
http://demangler.com/
Dobby&Unicorn
需要在onload之前hook
1 2 3 4 5
| module = dalvikModule.getModule(); hookDobby(); hookUnicorn();
dalvikModule.callJNI_OnLoad(androidEmulator);
|
1 2 3 4 5 6 7 8 9 10 11 12
| private void hookUnicorn() { androidEmulator.attach().addBreakPoint(module.findSymbolByName("AES_ECB_PKCS7_Encrypt").getAddress(), new BreakPointCallback() { @Override public boolean onHit(Emulator<?> emulator, long address) { System.out.println(emulator.getContext().getPointerArg(0).getString(0)); emulator.getBackend().reg_write(Arm64Const.UC_ARM64_REG_PC,emulator.getContext().getLRPointer().peer); emulator.getBackend().reg_write(Arm64Const.UC_ARM64_REG_X0,0); return true; } }); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| public void hookDobby(){ Dobby dobby = Dobby.getInstance(androidEmulator); dobby.replace(module.findSymbolByName("AES_ECB_PKCS7_Encrypt").getAddress(), new ReplaceCallback() { @Override public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) {
String fakeargs1 = "Hello world"; int length = fakeargs1.length(); MemoryBlock fakeargs1malloc = emulator.getMemory().malloc(length, true); fakeargs1malloc.getPointer().write(fakeargs1.getBytes(StandardCharsets.UTF_8)); emulator.getBackend().reg_write(Arm64_const.ARM64_REG_X0,fakeargs1malloc.getPointer().peer);
return HookStatus.RET(emulator,context.getLR()); }
@Override public void postCall(Emulator<?> emulator, HookContext context) {
super.postCall(emulator, context); } },true); }
|
path_so
https://armconverter.com/
这个path的功能能否用hook实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public void patch1() { UnidbgPointer pointer = UnidbgPointer.pointer(androidEmulator, module.base + 0xE444); byte[] code = new byte[]{0x40, 0x01, 0x00, 0x34}; pointer.write(code);
}
private void patch2() { UnidbgPointer pointer = UnidbgPointer.pointer(androidEmulator, module.base + 0xE444); Keystone keystone = new Keystone(KeystoneArchitecture.Arm64, KeystoneMode.LittleEndian); String huibian = "cbz w0, #0x28"; byte[] machineCode = keystone.assemble(huibian).getMachineCode(); pointer.write(machineCode); }
|
文件访问 IO 流补环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| public class testIO extends AbstractJni implements IOResolver { private final AndroidEmulator androidEmulator; private final Memory memory; private final VM dalvikVM; private final DalvikModule dalvikModule; private final Module module; public testIO() { androidEmulator = AndroidEmulatorBuilder .for64Bit() .addBackendFactory(new Unicorn2Factory(true)) .setProcessName("com.example.hookdemo") .setRootDir(new File("target/rootfs")) .build(); memory = androidEmulator.getMemory(); memory.setLibraryResolver(new AndroidResolver(23)); dalvikVM = androidEmulator.createDalvikVM(new File("unidbg-android/src/test/java/lesson/testIO/app-debug.apk")); dalvikVM.setJni(this); dalvikVM.setVerbose(true); dalvikModule = dalvikVM.loadLibrary("hookdemo", true); androidEmulator.getSyscallHandler().addIOResolver(this); module = dalvikModule.getModule(); dalvikModule.callJNI_OnLoad(androidEmulator); } public static void main(String[] args) { testIO testIO = new testIO(); testIO.jiance_xp_frida(); } private void jiance_xp_frida() { DvmClass dvmClass = dalvikVM.resolveClass("com.example.hookdemo.MainActivity"); DvmObject<?> dvmObject = dvmClass.newObject(null); DvmObject<?> dvmObject1 = dvmObject.callJniMethodObject(androidEmulator, "jiance_xp_frida()Ljava/lang/String;"); System.out.println(dvmObject1.getValue()); } @Override public FileResult resolve(Emulator emulator, String pathname, int oflags) { switch (pathname){ case "/sys/class/thermal/":{ return FileResult.<AndroidFileIO>success(new DirectoryFileIO(oflags,pathname,new File("target/rootfs/sys/class/thermal"))); } case "/data":{ return FileResult.failed(UnixEmulator.EACCES); } case "/proc/self/cmdline":{ return FileResult.<AndroidFileIO>success(new ByteArrayFileIO(oflags,pathname,"com.example.hookdemo\0".getBytes())); } case "/proc/self/maps":{ return FileResult.<AndroidFileIO>success(new SimpleFileIO(oflags,new File("unidbg-android/src/test/java/lesson/testIO/maps"),pathname )); } } System.out.println("file open-> "+pathname); return null; } }
|
系统补环境
https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md
判断是JNI调用还是系统调用
- svcNumber=0x0的是系统调用,NR调用号,ARM64调用号
- 补环境要补哪个方法,根据NR调用号决定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| JNI [17:19:05 115] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:399) - handleInterrupt intno=2, NR=-128672, svcNumber=0x18d, PC=unidbg@0xfffe0964, LR=RX@0x40012b88[libhookdemo.so]0x12b88, syscall=null java.lang.UnsupportedOperationException: android/os/Build->MODEL:Ljava/lang/String; at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:103) at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:53)
调用到系统API: [17:22:56 521] WARN [com.github.unidbg.linux.ARM64SyscallHandler] (ARM64SyscallHandler:399) - handleInterrupt intno=2, NR=165, svcNumber=0x0, PC=RX@0x401ca3d4[libc.so]0x6a3d4, LR=RX@0x40011f28[libhookdemo.so]0x11f28, syscall=null
|
补系统的环境:
1、硬编码返回
hook下某个系统API的返回值,按一定格式返回
NR |
syscall name |
references |
%rax |
arg0 (%rdi) |
arg1 (%rsi) |
arg2 (%rdx) |
arg3 (%r10) |
165 |
getrusage |
man/ cs/ |
0xa5 |
int who |
struct rusage *ru |
|
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Interceptor.attach(Module.findExportByName("libc.so", "getrusage"), { onEnter: function (args) { this.rusage = args[1] }, onLeave: function (retval) { console.log(hexdump(this.rusage,{length: 144})) } });
7ff4b68f58 01 00 00 00 00 00 00 00 70 f3 05 00 00 00 00 00 ........p....... 7ff4b68f68 00 00 00 00 00 00 00 00 30 57 05 00 00 00 00 00 ........0W...... 7ff4b68f78 10 64 01 00 00 00 00 00 00 00 00 00 00 00 00 00 .d.............. 7ff4b68f88 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 7ff4b68f98 ff a5 00 00 00 00 00 00 98 01 00 00 00 00 00 00 ................ 7ff4b68fa8 00 00 00 00 00 00 00 00 f0 7e 00 00 00 00 00 00 .........~...... 7ff4b68fb8 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 7ff4b68fc8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 7ff4b68fd8 b8 04 00 00 00 00 00 00 5c 01 00 00 00 00 00 00 ........\.......
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| public class HookDemoArm64SysCallHandler extends ARM64SyscallHandler { public HookDemoArm64SysCallHandler(SvcMemory svcMemory) { super(svcMemory); }
@Override protected boolean handleUnknownSyscall(Emulator<?> emulator, int NR) { switch (NR){ case 165: getrusage(emulator); return true; } return super.handleUnknownSyscall(emulator, NR); }
private void getrusage(Emulator<?> emulator) { UnidbgPointer register = UnidbgPointer.register(emulator, Arm64Const.UC_ARM64_REG_X1); byte[] bytes = hexStringToByteArray("010000000000000020300500000000000000000000000000e022020000000000149a0100000000000000000000000000000000000000000000000000000000008663000000000000ac190000000000000000000000000000382f000000000000000000000000000000000000000000000000000000000000000000000000000075010000000000009800000000000000"); for (int i=0;i<bytes.length;i++){ register.setByte(i,bytes[i]); } }
public static byte[] hexStringToByteArray(String hexString) { int len = hexString.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16)); } return data; } @Override protected int clock_gettime(Emulator<?> emulator) { RegisterContext context = emulator.getContext(); int clk_id = context.getIntArg(0) & 0x7; Pointer tp = context.getPointerArg(1); long offset = clk_id == 0 ? currentTimeMillis() * 1000000L : System.nanoTime() - System.nanoTime(); long tv_sec = offset / 1000000000L; long tv_nsec = offset % 1000000000L;
switch (clk_id) { case 3: tp.setLong(0, tv_sec); tp.setLong(8, tv_nsec); return 0; } return super.clock_gettime(emulator); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
| public class lesson11 extends AbstractJni implements IOResolver { public final AndroidEmulator androidEmulatorBuilder; public final VM vm; public final DalvikModule dalvikModule; public final Memory memory; public final Module module;
public lesson11() { AndroidEmulatorBuilder builder = new AndroidEmulatorBuilder(true) { @Override public AndroidEmulator build() { return new AndroidARM64Emulator(processName, rootDir, backendFactories) { @Override protected UnixSyscallHandler<AndroidFileIO> createSyscallHandler(SvcMemory svcMemory) { return new HookDemoArm64SysCallHandler(svcMemory); } }; } };
androidEmulatorBuilder = builder .addBackendFactory(new Unicorn2Factory(true)) .setRootDir(new File("target/rootfs")) .setProcessName("com.example.hookdemo") .build();
vm = androidEmulatorBuilder.createDalvikVM(new File("unidbg-android/src/test/java/lesson/lesson11/app-debug.apk")); vm.setJni(this); vm.setVerbose(true); androidEmulatorBuilder.getSyscallHandler().addIOResolver(this); memory = androidEmulatorBuilder.getMemory(); memory.setLibraryResolver(new AndroidResolver(23)); SystemPropertyHook systemPropertyHook = new SystemPropertyHook(androidEmulatorBuilder); systemPropertyHook.setPropertyProvider(new SystemPropertyProvider() { @Override public String getProperty(String key) { System.out.println("getProperty systemkey:" + key); switch (key) { case "net.hostname": { return "MIX2S-zhongeryayadeM"; } case "ro.serialno": { return "f8a995f5"; } case "ro.boot.serialno": { return "f8a995f5"; } case "ro.product.brand": { return "Xiaomi"; } case "ro.product.manufacturer": { return "Xiaomi"; } case "ro.product.model": { return "MIX 2S"; } case "ro.product.cpu.abi": { return "arm64-v8a"; } case "ro.product.cpu.abilist": { return "arm64-v8a,armeabi-v7a,armeabi"; } case "ro.boot.vbmeta.digest": { return null; } case "init.svc.droid4x": { return null; } case "ro.build.id":{ return "PKQ1.190118.001"; } } return ""; } }); memory.addHookListener(systemPropertyHook); dalvikModule = vm.loadLibrary("hookdemo", true); module = dalvikModule.getModule(); dalvikModule.callJNI_OnLoad(androidEmulatorBuilder); }
public static void main(String[] args) { lesson11 lesson11 = new lesson11(); lesson11.setEnv(); lesson11.hookgetEnv(); lesson11.xitongdiaoyong(); }
private void xitongdiaoyong() { DvmClass dvmClass = vm.resolveClass("com.example.hookdemo.MainActivity"); DvmObject<?> dvmObject = dvmClass.newObject(null); dvmObject.callJniMethod(androidEmulatorBuilder, "xitongdiaoyong()V"); }
@Override public FileResult resolve(Emulator emulator, String pathname, int oflags) { System.out.println("file open:" + pathname); return null; }
@Override public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) { switch (signature) { case "android/os/Build->MODEL:Ljava/lang/String;": { return new StringObject(vm, "MI 6"); } } return super.getStaticObjectField(vm, dvmClass, signature); } @Override public FileResult resolve(Emulator emulator, String pathname, int oflags) { System.out.println("file open:" + pathname); switch (pathname){ case "stat /data":{ return FileResult.success(new ByteArrayFileIO(oflags,pathname,(" File: `/data'\n" + " Size: 4096\t Blocks: 16\t IO Blocks: 512\tdirectory\n" + "Device: 10301h/66305d\t Inode: 2\t Links: 46\n" + "Access: (771/drwxrwx--x)\tUid: ( 1000/ system)\tGid: ( 1000/ system)\n" + "Access: 2019-08-22 00:32:54.000000000\n" + "Modify: 2023-05-29 08:37:38.813999995\n" + "Change: 1970-08-12 09:44:20.749999999\n").getBytes())); } } return null; }
@Override public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) { switch (signature) { case "android/os/Build->MODEL:Ljava/lang/String;": { return new StringObject(vm, "MI 6"); } } return super.getStaticObjectField(vm, dvmClass, signature); }
public void setEnv(){ Symbol setenv = module.findSymbolByName("setenv", true); setenv.call(androidEmulatorBuilder, "muyang", "6666"); }; public void hookgetEnv() { Dobby dobby = Dobby.getInstance(androidEmulatorBuilder); dobby.replace(module.findSymbolByName("getenv"), new ReplaceCallback() { @Override public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) { emulator.set("envkey", context.getPointerArg(0).getString(0)); return super.onCall(emulator, context, originFunction); } @Override public void postCall(Emulator<?> emulator, HookContext context) { String key = emulator.get("envkey"); switch (key) { case "muyang": String fakeInput = "6666"; int length = fakeInput.length(); MemoryBlock fakeInputBlock = emulator.getMemory().malloc(length, true); fakeInputBlock.getPointer().write(fakeInput.getBytes(StandardCharsets.UTF_8)); emulator.getBackend().reg_write(Arm64Const.UC_ARM64_REG_X0, fakeInputBlock.getPointer().peer); } super.postCall(emulator, context); } }, true); } public void hookPopen() { Dobby dobby = Dobby.getInstance(androidEmulatorBuilder); UnidbgPointer stat_data_pointer = UnidbgPointer.pointer(androidEmulatorBuilder, module.findSymbolByName("fopen").call(androidEmulatorBuilder, "stat /data", "r"));
dobby.replace(module.findSymbolByName("popen"), new ReplaceCallback() { @Override public HookStatus onCall(Emulator<?> emulator, long originFunction) { RegisterContext registerContext = emulator.getContext(); String command = registerContext.getPointerArg(0).getString(0); System.out.println("command:" + command);
emulator.getBackend().reg_write(Arm64Const.UC_ARM64_REG_PC,registerContext.getLRPointer().peer); switch (command){ case "stat /data":{ emulator.getBackend().reg_write(Arm64Const.UC_ARM64_REG_X0,stat_data_pointer.peer); return super.onCall(emulator, originFunction); } }
return super.onCall(emulator, originFunction); } }); } }
|
打开调试模式可查看更详细报错
2、仿写框架
根据unidbg提供的补其他系统API的例子写
访写 src/main/java/com/github/unidbg/ios/ARM64SyscallHandler.java
1 2 3 4 5 6 7 8 9
| private void getrusage(Emulator<?> emulator) { RegisterContext context = emulator.getContext(); int who = context.getIntArg(0); Pointer r_usage = context.getPointerArg(1); RUsage64 usage64 = new RUsage64(r_usage); usage64.unpack(); usage64.fillDefault(); usage64.pack(); }
|
https://bbs.kanxue.com/thread-272751.html
https://buaq.net/go-129256.html
https://github.com/gl953236368
函数初始化
trace工具
Java层trace工具:https://github.com/r0ysue/r0tracer
Jnitrace:https://github.com/chame1eon/jnitrace
所以unidbg就是用来不运行app的情况下,调用so方法,补环境属于其中的前置环境
dump apk上下文环境
https://blog.seeflower.dev/search/dump/2/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| https://www.jianshu.com/p/c546783ad284 https://developer.android.com/ndk/guides/other_build_systems?hl=zh-cn https://www.cnblogs.com/burner/p/clang-fen-si-bu-bian-yimainc.html 编译博客也不错 https://blog.csdn.net/qq_31865983/article/details/89402100 这个更加完整一点 https://github.com/hugsy/gef https://www.likecs.com/show-204245381.html#sc=400 Cortex-A7 常用汇编指令资料: https://www.cnblogs.com/iron2222/p/15640269.html
gdb调试器快速上手教程 https://blog.csdn.net/qq_45953886/article/details/127678876
ARM官方汇编指令 https://blog.csdn.net/oqqHuTu12345678/article/details/125683244
ARM汇编基础 https://www.cnblogs.com/hilfloser/p/10516610.html
ARM关于标志位影响详解 https://blog.csdn.net/tabactivity/article/details/90407858
ARM的九种寻址方式 https://blog.csdn.net/qq_43743762/article/details/105056991
### 5.编写第一个汇编程序
> https://www.w3cschool.cn/c/c-file-io.html
参考资料:https://softool.cn/read/arm_assembly_basic_aye/21010203.html
https://blog.csdn.net/qq_41028985/article/details/119407917
### 6.逆向C程序-数据类型
> 计算器: > https://www.bangnishouji.com/tools/224457.html
##### 1.32位浮点数表示方法:
> 二进制数怎么转化为浮点数?阶码又是怎么算出来的? > https://zhidao.baidu.com/question/750285508423217012.html
算法篇:ios和安卓通用
参考连接: https://www.cnblogs.com/xiaxveliang/p/15004954.html https://blog.csdn.net/sinat_27933301/article/details/79538169 https://www.cnblogs.com/DeeLMind/p/7581423.html https://www.bilibili.com/video/BV1aP411M7ug:强推
https://www.bilibili.com/video/BV1V14y1T7LM https://blog.csdn.net/ZCShouCSDN/article/details/84675235
|