环境准备 安装Frida框架 Frida官网:https://www.frida.re/
Frida源码:https://github.com/frida
Fride是一个轻量的hook框架,有着访问进程内存等能力。
我们需要在windows安装frida客户端和在安卓安装frida服务端。
Windows需要有Python3环境,而安卓必须有Root权限,本机如无Root权限可以在虚拟机上进行。
Windows下的frida CLI 首先需要有Python3的环境,然后直接进行安装
然后安装frida-tools
查看版本号,即说明安装成功
Android端的frida-server 可以在Github 下载最新的frida-server。上面有着非常多的平台和架构可以选择,选择你的Android手机的架构就可以下载了。
Tips:如果不清楚手机cpu架构是什么,可以进行下面操作:
连接手机ADB
进入adb shell
查看cpu架构
1 getprop ro.product.cpu.abi
然后选择对应的frida-server进行下载
下载后进行解压,将解压的frida-server通过adb或者其他方式传入手机,这里我们用adb进行传输
1 adb push frida-server-15.1.8-android-arm64 /data/local/tmp
对端口进行转发,默认使用27042端口与frida-server通信
1 adb forward tcp:27042 tcp:27042
对frida-server加权限
1 2 3 4 5 6 adb shell equuleus:/ $ su 2|equuleus:/ # cd /data/local/tmp equuleus:/data/local/tmp # ls frida-server-15.1.8-android-arm64 equuleus:/data/local/tmp # chmod 755 frida-server-15.1.8-android-arm64
运行frida-server
1 ./frida-server-15.1.8-android-arm64
新建一个控制台窗口,检查frida-server是否正常运行,查询手机进程
控制台将手机进程信息进行输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 PID Name ----- ------------------------------------------------------------------------------------------------- 9020 .dataservices 9059 .qtidataservices 21109 ADB WiFi 1276 ATFWD-daemon 20772 Google Play 商店 21191 Magisk 9430 Pharos Pro 9136 USIM卡应用 24100 adbd 1239 adsprpcd 601 android.hardware.atrace@1.0-service 8325 android.hardware.audio@2.0-service 1322 android.hardware.biometrics.fingerprint@2.1-service 834 android.hardware.bluetooth@1.0-service-qti 835 android.hardware.camera.provider@2.4-service 836 android.hardware.cas@1.1-service 837 android.hardware.configstore@1.1-service 838 android.hardware.drm@1.0-service 839 android.hardware.drm@1.2-service.clearkey
证明frida-server是正常运行的
APK去壳 获取目标应用的dex 我们通过“3秒去壳”神器:FRIDA-DEXDump(https://github.com/hluwa/FRIDA-DEXDump)获取应用dex
直接进行安装
1 pip3 install frida-dexdump
显示帮助信息
显示帮助页面信息,则证明安装成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ------------------------------------------------------------------------------------------------------------------------ ____________ ___________ ___ ______ _______ _______ | ___| ___ \_ _| _ \/ _ \ | _ \ ___\ \ / / _ \ | |_ | |_/ / | | | | | / /_\ \______| | | | |__ \ V /| | | |_ _ _ __ ___ _ __ | _| | / | | | | | | _ |______| | | | __| / \| | | | | | | '_ ` _ \| '_ \ | | | |\ \ _| |_| |/ /| | | | | |/ /| |___/ /^\ \ |/ /| |_| | | | | | | |_) | \_| \_| \_|\___/|___/ \_| |_/ |___/ \____/\/ \/___/ \__,_|_| |_| |_| .__/ | | |_| https://github.com/hluwa/FRIDA-DEXDump ------------------------------------------------------------------------------------------------------------------------ Usage: frida-dexdump -n <process> -p <pid> -f[enable spawn mode] -s <delay seconds> -d[enable deep search] -P <prepend script path> -A <append script path> -n: [Optional] Specify target process name, when spawn mode, it requires an application package name. If not specified, use frontmost application. -p: [Optional] Specify pid when multiprocess. If not specified, dump all. -f: [Optional] Use spawn mode, default is disable. -s: [Optional] When spawn mode, start dump work after sleep few seconds. default is 10s. -d: [Optional] Enable deep search maybe detected more dex, but speed will be slower. -P: [Optional] Prepend a Frida script to run before dexdump does. -A: [Optional] Append a Frida script to run after dexdump done. -E: [Optional] Changes 'su -c cmd' to 'su 0 cmd' for emulators. -h: show help.
我们在手机上运行目标应用,然后使用上面的frida-ps -R
命令获取进程的PID(即下面代码演示中的18912),然后尝试获取dex,因为普通搜索可能搜索到的dex不全,这里我们使用深度搜索:
1 frida-dexdump -p 18912 -d
在搜索过程中,会有一些dex被发现,并进行保存,如下所示
1 2 3 4 5 6 7 8 9 10 11 12 [DEXDump]: DexSize=0x34c, DexMd5=3adc8638108be839ded93b461116144c, SavePath=D:\Person_Program\Frida\dex/0xe62d5000.dex [DEXDump]: DexSize=0x1000, DexMd5=c0d7b0658d67591a8426ffec23e7aac0, SavePath=D:\Person_Program\Frida\dex/0xe62d5000.dex [DEXDump]: DexSize=0x2ac, DexMd5=c44309f71fdb137742dcea736be5f92a, SavePath=D:\Person_Program\Frida\dex/0xe62d7000.dex [DEXDump]: DexSize=0x1000, DexMd5=0205ddcc1829e68e7191c363d8ba4df3, SavePath=D:\Person_Program\Frida\dex/0xe62d7000.dex [DEXDump]: DexSize=0x2b0, DexMd5=9c25cb9a5adb25ed0673f594cf87571f, SavePath=D:\Person_Program\Frida\dex/0xe62d9000.dex [DEXDump]: DexSize=0x1000, DexMd5=d915d67068efbd38c54f7c653d4518cf, SavePath=D:\Person_Program\Frida\dex/0xe62d9000.dex [DEXDump]: DexSize=0xecfc, DexMd5=2dd14f384bfe4741e5a9463e12c79c89, SavePath=D:\Person_Program\Frida\dex/0xe661c02c.dex [DEXDump]: DexSize=0xefd4, DexMd5=18445cc5ff022b10460403ea87d232d4, SavePath=D:\Person_Program\Frida\dex/0xe661c02c.dex [DEXDump]: DexSize=0x1d0, DexMd5=8c261b848588b1a4345c570ef6fe63d8, SavePath=D:\Person_Program\Frida\dex/0xe74ef000.dex [DEXDump]: DexSize=0x1000, DexMd5=0434eb92bd66c427682c7423c9934336, SavePath=D:\Person_Program\Frida\dex/0xe74ef000.dex [DEXDump]: DexSize=0x6391c, DexMd5=82d43d81513d858f3e6e89cdf9f27a15, SavePath=D:\Person_Program\Frida\dex/0xe750f02c.dex [DEXDump]: DexSize=0x63fd4, DexMd5=b96219f7a71a763d089d4120fc065cfd, SavePath=D:\Person_Program\Frida\dex/0xe750f02c.dex
结束之后,所有找到的dex会保存在命令行所在的目录中。
查看应用代码 我们使用jadx(https://github.com/skylot/jadx )对dex文件进行反编译,1.2.0版本支持对多个dex文件的查看,正好符合我们的需求。
将所有的dex文件添加至jadx中,如果正常的话,会展现出所有的源代码。但大部分情况,由于部分dex文件损坏,jadx会提示文件无法打开,此时我们需要逐一将有问题的dex文件删除掉(暂时没有找到更好的办法,如果有好的办法可以踢我一下:)
将损坏的dex文件剔除后,我们可以看到所有的源代码
以某APP为例 需求 APP更新了某签到功能的提交签到接口,参数部分做了加密,我们的目标就是找出相应的加密逻辑
定位 APP接口更新了加密的参数
1 { "appVersion" : "xxx" , "systemName" : "android" , "bodyString" : "8tbnsTxxxxxxxx==" , "sign" : "41b42bddffe257599f7xxxxxxx" , "model" : "M2006J10C" , "lon" : 120.2 , "calVersion" : "firstv" , "systemVersion" : "11" , "deviceId" : "e6adf99332c3ecebf9933" , "userId" : "123456" , "version" : "first_v2" , "lat" : 30.1 }
通过jadx搜索字符串去快速定位到相应代码
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 private void doHttpAction (IUIViewPlugin.HttpAction httpAction, JSONObject jSONObject, Map<String, String> map, CallbackContext callbackContext, Map<String, Object> map2) { try { String optString = jSONObject.optString("url" ); String str = "utf-8" ; String str2 = null ; Map<String, String> map3 = null ; if (httpAction == IUIViewPlugin.HttpAction.sendGetRequest) { String optString2 = jSONObject.optString(a.p); if (!TextUtils.isEmpty(optString2)) { map3 = (Map) JSON.parseObject(optString2, Map.class); } if (map.containsKey("Except-Encoding" )) { str = map.get("Except-Encoding" ); } map.put("Except-Encoding" , str); callbackContext.success(new JSONObject (JSON.toJSONString(new OkHTTPHelper ().get(optString, map, map3)))); } else if (httpAction == IUIViewPlugin.HttpAction.sendPostRequest) { String optString3 = jSONObject.optString("bodyString" ); String c = tq.c(optString3, CommonUtils.q(), CpUiUtils.f()); if (map2 != null ) { map2.put("bodyString" , c); map2.put("sign" , CommonUtils.A(map2)); map2.put("version" , tq.h(CpUiUtils.f())); map2.put("calVersion" , "firstv" ); optString3 = new JSONObject (map2).toString(); } if (map != null && (str2 = map.get("Content-Type" )) == null ) { str2 = map.get("content-type" ); } if (str2 == null ) { str2 = "application/json; charset=utf-8" ; } if (map != null && map.containsKey("Except-Encoding" )) { str = map.get("Except-Encoding" ); } OkHttpClient build = OkHTTPHelper.getOkHttpBuilder().build(); Headers.Builder builder = new Headers .Builder(); builder.add("tenantId" , com.wisedu.campushoy.common.utils.b.h("tenant_id" )).add("User-Agent" , NetWorkUtil.getDefaultConfigUserAgent()).add("CpdailyStandAlone" , ModuleCommImpl.getInstance().getBuildPackageType()); if (map != null ) { for (String str3 : map.keySet()) { builder.add(str3, map.get(str3)); } } Response execute = build.newCall(new Request .Builder().url(optString).headers(builder.build()).post(RequestBody.create(MediaType.parse(str2), optString3)).build()).execute(); HttpResult httpResult = new HttpResult (); httpResult.code = execute.code(); String contentType = getContentType(execute.header("Content-Type" )); if (TextUtils.isEmpty(contentType) || !contentType.startsWith("image/" )) { contentType = getContentType(execute.header("content-type" )); } if (TextUtils.isEmpty(contentType) || !contentType.startsWith("image/" )) { httpResult.data = new String (execute.body().bytes(), str); } else { httpResult.data = "data:" + contentType + ";base64," + Base64.encodeToString(execute.body().bytes(), 2 ); } httpResult.url = execute.request().url().toString(); httpResult.headers = new HashMap <String, String>(httpResult) { public final HttpResult val$result; { this .val$result = r2; put("url" , r2.url); } }; callbackContext.success(new JSONObject (JSON.toJSONString(httpResult))); } } catch (Exception e) { e.printStackTrace(); } }
通过IDA x查看引用
F5 显示代码
未完待续。。