├── .gitignore ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── agent ├── dist │ ├── index.js │ └── test.js └── src │ ├── android_api.ts │ ├── index.ts │ └── search.ts ├── dist ├── cli.js ├── cli.js.map ├── dex_dumper.js ├── dex_dumper.js.map ├── dumper.js ├── dumper.js.map ├── index.js ├── index.js.map ├── test copy.js ├── test.js └── test.js.map ├── doc └── useage.gif ├── package.json ├── src ├── cli.ts ├── dumper.ts ├── index.ts └── test.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.lock 3 | **/*.dex 4 | **/*.log -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "ts-node-esm-U", 9 | "type": "node", 10 | "request": "launch", 11 | "args": [ 12 | "src/cli.ts", 13 | "-UF", 14 | "-o", 15 | "./output" 16 | ], 17 | "runtimeArgs": [ 18 | "--loader", 19 | "ts-node/esm" 20 | ], 21 | "cwd": "${workspaceRoot}", 22 | "protocol": "inspector", 23 | "internalConsoleOptions": "openOnSessionStart", 24 | "console": "externalTerminal" 25 | } , 26 | { 27 | "name": "ts-node-esm-H", 28 | "type": "node", 29 | "request": "launch", 30 | "args": [ 31 | "src/cli.ts", 32 | "-H", 33 | "127.0.0.1:27042", 34 | "-o", 35 | "./output" 36 | ], 37 | "runtimeArgs": [ 38 | "--loader", 39 | "ts-node/esm" 40 | ], 41 | "cwd": "${workspaceRoot}", 42 | "protocol": "inspector", 43 | "internalConsoleOptions": "openOnSessionStart", 44 | "console": "externalTerminal" 45 | } , 46 | { 47 | "name": "ts-node", 48 | "type": "node", 49 | "request": "launch", 50 | "args": [ 51 | "${relativeFile}", 52 | "-U", 53 | "-o", 54 | "/output", 55 | "-d", 56 | "com.sample.app" 57 | ], 58 | "runtimeArgs": [ 59 | "-r", 60 | "ts-node/register" 61 | ], 62 | "cwd": "${workspaceRoot}", 63 | "protocol": "inspector", 64 | "internalConsoleOptions": "openOnSessionStart" 65 | } 66 | ] 67 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 evlon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FRIDA-JS-DEXDump 2 | 3 | `frida-js-dexdump` is a copy of frida-dexdump writed by ts. 4 | It is a frida tool to find and dump dex in memory to support security engineers in analyzing malware. 5 | 6 | 7 | 8 | 9 | ## Features 10 | 11 | 1. Support fuzzy search broken header dex(deep search mode). 12 | 2. Compatible with all android version(frida supported). 13 | 3. One click installation, without modifying the system, easy to deploy and use. 14 | 15 | ## Require 16 | 17 | 1. Node.js Version > 14.16 , my dev node is 16.13.2 18 | ``` 19 | $ node -v 20 | v16.13.2 21 | ``` 22 | 23 | 2. Python3 3.10.7 24 | 25 | ``` 26 | $ python -V 27 | Python 3.10.7 28 | 29 | ``` 30 | 31 | ## Installation 32 | 33 | 34 | 35 | ``` 36 | pip3 install frida frida-tools 37 | npm install -g frida-fs-dexdump 38 | ``` 39 | 40 | ## Usage 41 | 42 | CLI arguments base on [frida-tools](https://github.com/frida/frida-tools), you can quickly dump the foreground application like this: 43 | 44 | ``` 45 | frida-js-dexdump -FU 46 | ``` 47 | 48 | Or use select to choice app like this: 49 | 50 | ``` 51 | frida-js-dexdump -U 52 | 53 | ? What app? (Use arrow keys) 54 | ❯ 2328:bin.mt.plus-MT管理器 55 | 2492:com.android.flysilkworm-雷电游戏中心 56 | 4171:com.xiaojianbang.app-HookTestDemo 57 | 12477:com.android.settings-设置 58 | 14633:com.android.documentsui-文件 59 | ``` 60 | 61 | Or specify and spawn app like this: 62 | 63 | ``` 64 | frida-js-dexdump -U -f com.app.pkgname 65 | ``` 66 | 67 | Or select install app and spawn app like this: 68 | 69 | ``` 70 | frida-js-dexdump -U -f 71 | 72 | 73 | ? What app? (Use arrow keys) 74 | ❯ bin.mt.plus(MT管理器) 75 | com.v2ray.ang(v2rayNG) 76 | com.xiaojianbang.app(HookTestDemo) 77 | com.yssenlin.app(影视森林) 78 | lnes.ef(一起设置) 79 | magisk.term(Magisk Terminal Emulator) 80 | player.normal.np(NP管理器) 81 | ``` 82 | 83 | 84 | Additionally, you can see in `-h` that the new options provided by frida-dexdump are: 85 | 86 | ``` 87 | -o OUTPUT, --output OUTPUT Output folder path, default is './/'. 88 | -d, --deep-search Enable deep search mode. 89 | --sleep SLEEP Waiting times for start, spawn mode default is 5s. 90 | ``` 91 | 92 | When using, I suggest using the `-d, --deep-search` option, which may take more time, but the results will be more complete. 93 | 94 | ## Build and develop 95 | 96 | ``` 97 | yarn install 98 | yarn run watch-agent 99 | yarn run watch 100 | ``` 101 | 102 | ## 截图 103 | ![screenshot](doc/useage.gif) 104 | 105 | ### 参考和致谢 106 | 107 | See [hluwa](https://github.com/hluwa/FRIDA-DEXDump/) 108 | [《深入 FRIDA-DEXDump 中的矛与盾》](https://mp.weixin.qq.com/s/n2XHGhshTmvt2FhxyFfoMA) 109 | 110 | 111 | -------------------------------------------------------------------------------- /agent/dist/index.js: -------------------------------------------------------------------------------- 1 | 📦 2 | 1198 /agent/src/index.js.map 3 | 1103 /agent/src/index.js 4 | 9614 /agent/src/search.js.map 5 | 14969 /agent/src/search.js 6 | ✄ 7 | {"version":3,"file":"index.js","sourceRoot":"E:/Repos/frida/frida-js-dexdump/","sources":["agent/src/index.ts"],"names":[],"mappings":"AAAA;;;;IAII;AAEJ,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAEtC,SAAS,iBAAiB,CAAC,IAAmB,EAAE,IAAY;IACxD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,UAAU,KAAK;QAClD,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC5C,IAAI,KAAK,CAAC,IAAI,GAAG,IAAI,IAAI,SAAS,GAAG,GAAG,EAAE;YACtC,OAAM;SACT;QACD,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE;YACnC,OAAO,CAAC,GAAG,CAAC,wCAAwC,GAAG,IAAI,GAAG,GAAG,GAAG,SAAS,CAAC,CAAA;YAC9E,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;SAC9E;IAEL,CAAC,CAAC,CAAA;AACN,CAAC;AAGD,GAAG,CAAC,OAAO,GAAG;IACV,UAAU,EAAE,UAAU,OAAO,EAAE,IAAI;QAC/B,MAAM,GAAG,GAAG,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC;QACvC,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC7B,OAAO,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IACD,SAAS,EAAE,UAAU,gBAAyB;QAC1C,OAAO,SAAS,CAAC,gBAAgB,CAAC,CAAC;IACvC,CAAC;IACD,WAAW,EAAE;QACT,OAAO,CAAC,gBAAgB,EAAE,CAAC,OAAO,CAAC,UAAU,MAAM;QACnD,CAAC,CAAC,CAAA;IACN,CAAC;CAEJ,CAAC"} 8 | ✄ 9 | /* 10 | * Author: hluwa 11 | * HomePage: https://github.com/hluwa 12 | * CreateTime: 2021/6/2 13 | * */ 14 | import { searchDex } from "./search.js"; 15 | function setReadPermission(base, size) { 16 | const end = base.add(size); 17 | Process.enumerateRanges("---").forEach(function (range) { 18 | const range_end = range.base.add(range.size); 19 | if (range.base < base || range_end > end) { 20 | return; 21 | } 22 | if (!range.protection.startsWith("r")) { 23 | console.log("Set read permission for memory range: " + base + "-" + range_end); 24 | Memory.protect(range.base, range.size, "r" + range.protection.substr(1, 2)); 25 | } 26 | }); 27 | } 28 | rpc.exports = { 29 | memorydump: function (address, size) { 30 | const ptr = new NativePointer(address); 31 | setReadPermission(ptr, size); 32 | return ptr.readByteArray(size); 33 | }, 34 | searchdex: function (enableDeepSearch) { 35 | return searchDex(enableDeepSearch); 36 | }, 37 | stopthreads: function () { 38 | Process.enumerateThreads().forEach(function (thread) { 39 | }); 40 | }, 41 | }; 42 | ✄ 43 | {"version":3,"file":"search.js","sourceRoot":"E:/Repos/frida/frida-js-dexdump/","sources":["agent/src/search.ts"],"names":[],"mappings":"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"} 44 | ✄ 45 | /* 46 | * Author: hluwa 47 | * HomePage: https://github.com/hluwa 48 | * CreateTime: 2021/6/3 49 | * */ 50 | /* 51 | map_list 52 | 名称 格式 说明 53 | size uint 列表的大小(以条目数表示) 54 | list map_item[size] 列表的元素 55 | 56 | 57 | 58 | map_item 格式 59 | 名称 格式 说明 60 | type ushort 项的类型;见下表 61 | unused ushort (未使用) 62 | size uint 在指定偏移量处找到的项数量 63 | offset uint 从文件开头到相关项的偏移量 64 | */ 65 | /* 66 | 类型代码 67 | 项类型 常量 值 项大小(以字节为单位) 68 | header_item TYPE_HEADER_ITEM 0x0000 0x70 69 | string_id_item TYPE_STRING_ID_ITEM 0x0001 0x04 70 | type_id_item TYPE_TYPE_ID_ITEM 0x0002 0x04 71 | proto_id_item TYPE_PROTO_ID_ITEM 0x0003 0x0c 72 | field_id_item TYPE_FIELD_ID_ITEM 0x0004 0x08 73 | method_id_item TYPE_METHOD_ID_ITEM 0x0005 0x08 74 | class_def_item TYPE_CLASS_DEF_ITEM 0x0006 0x20 75 | call_site_id_item TYPE_CALL_SITE_ID_ITEM 0x0007 0x04 76 | method_handle_item TYPE_METHOD_HANDLE_ITEM 0x0008 0x08 77 | map_list TYPE_MAP_LIST 0x1000 4 + (item.size * 12) 78 | type_list TYPE_TYPE_LIST 0x1001 4 + (item.size * 2) 79 | annotation_set_ref_list TYPE_ANNOTATION_SET_REF_LIST 0x1002 4 + (item.size * 4) 80 | annotation_set_item TYPE_ANNOTATION_SET_ITEM 0x1003 4 + (item.size * 4) 81 | class_data_item TYPE_CLASS_DATA_ITEM 0x2000 隐式;必须解析 82 | code_item TYPE_CODE_ITEM 0x2001 隐式;必须解析 83 | string_data_item TYPE_STRING_DATA_ITEM 0x2002 隐式;必须解析 84 | debug_info_item TYPE_DEBUG_INFO_ITEM 0x2003 隐式;必须解析 85 | annotation_item TYPE_ANNOTATION_ITEM 0x2004 隐式;必须解析 86 | encoded_array_item TYPE_ENCODED_ARRAY_ITEM 0x2005 隐式;必须解析 87 | annotations_directory_item TYPE_ANNOTATIONS_DIRECTORY_ITEM 0x2006 隐式;必须解析 88 | hiddenapi_class_data_item TYPE_HIDDENAPI_CLASS_DATA_ITEM 0xF000 隐式;必须解析 89 | */ 90 | // 91 | function verify_by_maps_old(dexptr, mapsptr, range_base, range_end) { 92 | const maps_offset = dexptr.add(0x34).readUInt(); 93 | const maps_size = mapsptr.readUInt(); 94 | for (let i = 0; i < maps_size; i++) { 95 | const item_type = mapsptr.add(4 + i * 0xC).readU16(); 96 | if (item_type === 4096) { 97 | const map_offset = mapsptr.add(4 + i * 0xC + 8).readUInt(); 98 | if (maps_offset === map_offset) { 99 | return true; 100 | } 101 | } 102 | } 103 | return false; 104 | } 105 | function verify_by_maps(dexptr, mapsptr, range_base, range_end) { 106 | const maps_size = mapsptr.readUInt(); 107 | //看看每一个map_item type 是不是正确 108 | let rightTypeList = [ 109 | 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 110 | 0x1000, 0x1001, 0x1002, 0x1003, 111 | 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 112 | 0xF000 113 | ]; 114 | let processed_type = new Map(); 115 | let unknown_type = new Map(); 116 | let mustOk = false; 117 | for (let i = 0; i < maps_size; i++) { 118 | // ptr is map_item[] begin addr 119 | let ptr = mapsptr.add(4 + i * 0xC); 120 | const item_type = ptr.readU16(); 121 | if (rightTypeList.indexOf(item_type) == -1) { 122 | unknown_type.set(item_type, 1); 123 | //警告一下 124 | send(`[warn] map item type error: idx:${i} type:0x${item_type.toString(16).padStart(4, '0')} dexptr:${dexptr}`); 125 | } 126 | else { 127 | let val = processed_type.get(item_type) || 0; 128 | val++; 129 | processed_type.set(item_type, val); 130 | if (val > 1) { 131 | send(`[warn] map item type repeet: times:${val} idx:${i} type:0x${item_type.toString(16).padStart(4, '0')} dexptr:${dexptr}`); 132 | } 133 | } 134 | let map_item_size = ptr.add(4).readU32(); 135 | let map_item_offset = ptr.add(8).readU32(); 136 | const map_item_offset_addr = dexptr.add(map_item_offset); 137 | //特殊情况,size == 0 offset must == 0 138 | if (map_item_size === 0 && map_item_offset !== 0) { 139 | return false; 140 | } 141 | //地址超出也不行 142 | if (map_item_offset_addr < range_base || map_item_offset_addr > range_end) { 143 | send(`[bad dex skip] map item type error: idx:${i} addr out of range:0x${map_item_offset.toString(16).padStart(4, '0')}`); 144 | return false; 145 | } 146 | if (item_type === 0x1000) { 147 | //map_off uint 从文件开头到映射项的偏移量。该偏移量(必须为非零值)应该是到 data 区段的偏移量,而数据应采用下文中“map_list”指定的格式。 148 | const map_offset = map_item_offset; 149 | const header_map_offset = dexptr.add(0x34).readUInt(); 150 | if (header_map_offset !== map_offset) { 151 | send(`[bad dex skip] map item header_map_offset(${header_map_offset}) !== map_offset(${map_offset}): idx:${i} `); 152 | return false; 153 | } 154 | else { 155 | mustOk = true; 156 | } 157 | } 158 | else if (item_type === 0x0001) { 159 | //type_ids 从文件开头到类型标识符列表的偏移量;如果 type_ids_size == 0(不可否认是一种奇怪的极端情况),则该值为 0。该偏移量(如果为非零值)应该是到 type_ids 区段开头的偏移量。 160 | const header_string_id_size = dexptr.add(0x38).readUInt(); 161 | const header_string_id_offset = dexptr.add(0x38 + 4).readUInt(); 162 | if (header_string_id_size !== map_item_size) { 163 | send(`[bad dex skip] map item header_string_id_size(${header_string_id_size}) !== map_item_size(${map_item_size}): idx:${i} `); 164 | return false; 165 | } 166 | if (header_string_id_offset !== map_item_offset) { 167 | send(`[bad dex skip] map item header_string_id_offset(${header_string_id_offset}) !== map_item_offset(${map_item_offset}): idx:${i} `); 168 | return false; 169 | } 170 | } 171 | else if (item_type === 0x0002) { 172 | //type_ids 从文件开头到类型标识符列表的偏移量;如果 type_ids_size == 0(不可否认是一种奇怪的极端情况),则该值为 0。该偏移量(如果为非零值)应该是到 type_ids 区段开头的偏移量。 173 | const header_type_ids_size = dexptr.add(0x40).readUInt(); 174 | const header_type_ids_offset = dexptr.add(0x40 + 4).readUInt(); 175 | //类型标识符列表中的元素数量,最多为 65535 176 | if (map_item_size > 65535) { 177 | return false; 178 | } 179 | if (header_type_ids_size !== map_item_size) { 180 | send(`[bad dex skip] map item header_type_ids_size(${header_type_ids_size}) !== map_item_size(${map_item_size}): idx:${i} `); 181 | return false; 182 | } 183 | if (header_type_ids_offset !== map_item_offset) { 184 | send(`[bad dex skip] map item header_type_ids_offset(${header_type_ids_offset}) !== map_item_offset(${map_item_offset}): idx:${i} `); 185 | return false; 186 | } 187 | } 188 | else { 189 | // nop 190 | continue; 191 | } 192 | } 193 | // 已知的,怎么也的多于未知的吧。 194 | return mustOk && processed_type.keys.length > unknown_type.keys.length; 195 | } 196 | function get_dex_real_size(dexptr, range_base, range_end) { 197 | const dex_size = dexptr.add(0x20).readUInt(); 198 | const maps_address = get_maps_address(dexptr, range_base, range_end); 199 | if (!maps_address) { 200 | return dex_size; 201 | } 202 | const maps_end = get_maps_end(maps_address, range_base, range_end); 203 | if (!maps_end) { 204 | return dex_size; 205 | } 206 | return maps_end.sub(dexptr).toInt32(); 207 | } 208 | function get_maps_address(dexptr, range_base, range_end) { 209 | //+0x34 是 map_off ,也就是 map 段的偏移位置,一般情况下 map 段都是在 DEX 文件的最末尾,与 file_size 同理 210 | const maps_offset = dexptr.add(0x34).readUInt(); 211 | if (maps_offset === 0) { 212 | return null; 213 | } 214 | const maps_address = dexptr.add(maps_offset); 215 | //必须再Range的内存范围内,不在就不是 216 | if (maps_address < range_base || maps_address > range_end) { 217 | return null; 218 | } 219 | return maps_address; 220 | } 221 | function get_maps_end(maps, range_base, range_end) { 222 | const maps_size = maps.readUInt(); 223 | // send(`get_maps_end maps_size:${maps_size}`) 224 | if (maps_size < 2 || maps_size > 50) { 225 | return null; 226 | } 227 | const maps_end = maps.add(maps_size * 0xC + 4); 228 | if (maps_end < range_base || maps_end > range_end) { 229 | return null; 230 | } 231 | return maps_end; 232 | } 233 | function verify(dexptr, range, enable_verify_maps) { 234 | if (range != null) { 235 | var range_end = range.base.add(range.size); 236 | // verify header_size header 结构放不下,跳过 237 | if (dexptr.add(0x70) > range_end) { 238 | return false; 239 | } 240 | var maps_address = get_maps_address(dexptr, range.base, range_end); 241 | if (!maps_address) { 242 | return false; 243 | } 244 | var maps_end = get_maps_end(maps_address, range.base, range_end); 245 | if (!maps_end) { 246 | return false; 247 | } 248 | if (!verify_ids_off(dexptr, range.size)) { 249 | return false; 250 | } 251 | if (enable_verify_maps) { 252 | return verify_by_maps(dexptr, maps_address, range.base, range_end); 253 | } 254 | else { 255 | // TODO: 读取头的大小,如果这里读出来的都是正确的,后面为啥还修复? 256 | return dexptr.add(0x3C).readUInt() === 0x70; 257 | } 258 | } 259 | return false; 260 | } 261 | function verify_ids_off(dexptr, dex_size) { 262 | const string_ids_off = dexptr.add(0x3C).readUInt(); 263 | const type_ids_off = dexptr.add(0x44).readUInt(); 264 | const proto_ids_off = dexptr.add(0x4C).readUInt(); 265 | const field_ids_off = dexptr.add(0x54).readUInt(); 266 | const method_ids_off = dexptr.add(0x5C).readUInt(); 267 | //考虑特殊情况,添加一下 268 | // xxx_size == 0(不可否认是一种奇怪的极端情况),则该值为 0 269 | return string_ids_off < dex_size && (string_ids_off >= 0x70 || string_ids_off === 0) 270 | && type_ids_off < dex_size && (type_ids_off >= 0x70 || type_ids_off === 0) 271 | && proto_ids_off < dex_size && (proto_ids_off >= 0x70 || proto_ids_off === 0) 272 | && field_ids_off < dex_size && (field_ids_off >= 0x70 || field_ids_off === 0) 273 | && method_ids_off < dex_size && (method_ids_off >= 0x70 || method_ids_off === 0); 274 | } 275 | export function searchDex(deepSearch) { 276 | const result = []; 277 | //Process.enumerateRanges('r--') 这是用于遍历当前进程中所有可以读的内存段,毕竟不能读的内存区域是不能被 VM 执行的 (Native 可以)。想必不难理解。 278 | Process.enumerateRanges('r--').forEach(function (range) { 279 | try { 280 | //DEX\n3??\0 搜索 64 65 78 0a 30 ?? ?? 00 281 | Memory.scanSync(range.base, range.size, "64 65 78 0a 30 ?? ?? 00").forEach(function (match) { 282 | //跳过系统的 283 | if (range.file && range.file.path 284 | && (range.file.path.startsWith("/data/dalvik-cache/") || 285 | range.file.path.startsWith("/system/"))) { 286 | return; 287 | } 288 | // send("search normal dex\\n begin header...") 289 | // 正常的,一般也不用检查别的了, 但是如果对方内存里面放点这个东西呢?那就乱了。 290 | // send(`-----------begin range ${match.address} --------------`) 291 | if (verify(match.address, range, false)) { 292 | const dex_size = get_dex_real_size(match.address, range.base, range.base.add(range.size)); 293 | send(`found normal dex\\n begin header, addr:${match.address}, size:${dex_size}`); 294 | result.push({ 295 | "addr": match.address, 296 | "size": dex_size 297 | }); 298 | const max_size = range.size - match.address.sub(range.base).toInt32(); 299 | //如果这里面,Range没有占满,可能考虑有重分布,全部弄出来保险点? 300 | if (deepSearch && max_size != dex_size) { 301 | result.push({ 302 | "addr": match.address, 303 | "size": max_size 304 | }); 305 | } 306 | } 307 | }); 308 | if (deepSearch) { 309 | //先查找具有 headersize 的内存,然后减去 0x3c 得到DEX头的位置。 应对加固的时候,DEX不在开始的情况 310 | Memory.scanSync(range.base, range.size, "70 00 00 00 78 56 34 12") 311 | .concat(Memory.scanSync(range.base, range.size, "70 00 00 00 12 34 56 78")).forEach(function (match) { 312 | const dex_base = match.address.sub(0x3C); 313 | if (dex_base < range.base) { 314 | //前面没有 0x3c 的大小,不是 315 | return; 316 | } 317 | //如果有 uchar[8] magic ; 并且正确 318 | // TODO: 如果这里也没有呢?被修改了呢?程序还可以运行吗? 319 | if (dex_base.readCString(4) != "dex\n" && verify(dex_base, range, true)) { 320 | //计算真实大小 321 | const real_dex_size = get_dex_real_size(dex_base, range.base, range.base.add(range.size)); 322 | if (!verify_ids_off(dex_base, real_dex_size)) { 323 | return; 324 | } 325 | result.push({ 326 | "addr": dex_base, 327 | "size": real_dex_size 328 | }); 329 | const max_size = range.size - dex_base.sub(range.base).toInt32(); 330 | send(`DeepSearch found 'not dex\\n begin' header. , addr:${range.base}, size:${real_dex_size}`); 331 | if (max_size != real_dex_size) { 332 | result.push({ 333 | "addr": dex_base, 334 | "size": max_size 335 | }); 336 | } 337 | } 338 | }); 339 | } 340 | else { 341 | if (range.base.readCString(4) != "dex\n" && verify(range.base, range, true)) { 342 | const real_dex_size = get_dex_real_size(range.base, range.base, range.base.add(range.size)); 343 | send(`found 'not dex\\n begin' header. , addr:${range.base}, size:${real_dex_size}`); 344 | result.push({ 345 | "addr": range.base, 346 | "size": real_dex_size 347 | }); 348 | } 349 | } 350 | } 351 | catch (e) { 352 | console.log(e); 353 | } 354 | }); 355 | return result; 356 | } -------------------------------------------------------------------------------- /agent/dist/test.js: -------------------------------------------------------------------------------- 1 | console.log(`------------------${new Date()}----------------------`) -------------------------------------------------------------------------------- /agent/src/android_api.ts: -------------------------------------------------------------------------------- 1 | export class AndroidApi{ 2 | 3 | 4 | private static func_Sleep: AnyFunction; 5 | static usleep(dwMilliseconds: number): void { 6 | if (this.func_Sleep == null) { 7 | let address = Module.findExportByName("libandroid_runtime.so", "usleep"); 8 | this.func_Sleep = new NativeFunction(address!, "void", ["int"]); 9 | } 10 | return this.func_Sleep(dwMilliseconds); 11 | } 12 | 13 | 14 | // private static func_EVP_md5: AnyFunction; 15 | // static md5(dwMilliseconds: number): void { 16 | // if (this.func_EVP_md5 == null) { 17 | // let address = Module.findExportByName("libcrypto.so", "EVP_md5"); 18 | // this.func_EVP_md5 = new NativeFunction(address!, "void", ["int"]); 19 | // } 20 | // return this.func_EVP_md5(dwMilliseconds); 21 | // } 22 | } 23 | // let libcrypto = Module.load("libcrypto.so"); 24 | // console.log(libcrypto); -------------------------------------------------------------------------------- /agent/src/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: hluwa 3 | * HomePage: https://github.com/hluwa 4 | * CreateTime: 2021/6/2 5 | * */ 6 | 7 | import {searchDex} from "./search.js"; 8 | 9 | function setReadPermission(base: NativePointer, size: number) { 10 | const end = base.add(size); 11 | Process.enumerateRanges("---").forEach(function (range) { 12 | const range_end = range.base.add(range.size) 13 | if (range.base < base || range_end > end) { 14 | return 15 | } 16 | if (!range.protection.startsWith("r")) { 17 | console.log("Set read permission for memory range: " + base + "-" + range_end) 18 | Memory.protect(range.base, range.size, "r" + range.protection.substr(1, 2)) 19 | } 20 | 21 | }) 22 | } 23 | 24 | 25 | rpc.exports = { 26 | memorydump: function (address, size) { 27 | const ptr = new NativePointer(address); 28 | setReadPermission(ptr, size); 29 | return ptr.readByteArray(size); 30 | }, 31 | searchdex: function (enableDeepSearch: boolean) { 32 | return searchDex(enableDeepSearch); 33 | }, 34 | stopthreads: function () { 35 | Process.enumerateThreads().forEach(function (thread) { 36 | }) 37 | }, 38 | 39 | }; 40 | 41 | -------------------------------------------------------------------------------- /agent/src/search.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: hluwa 3 | * HomePage: https://github.com/hluwa 4 | * CreateTime: 2021/6/3 5 | * */ 6 | 7 | 8 | 9 | /* 10 | map_list 11 | 名称 格式 说明 12 | size uint 列表的大小(以条目数表示) 13 | list map_item[size] 列表的元素 14 | 15 | 16 | 17 | map_item 格式 18 | 名称 格式 说明 19 | type ushort 项的类型;见下表 20 | unused ushort (未使用) 21 | size uint 在指定偏移量处找到的项数量 22 | offset uint 从文件开头到相关项的偏移量 23 | */ 24 | 25 | /* 26 | 类型代码 27 | 项类型 常量 值 项大小(以字节为单位) 28 | header_item TYPE_HEADER_ITEM 0x0000 0x70 29 | string_id_item TYPE_STRING_ID_ITEM 0x0001 0x04 30 | type_id_item TYPE_TYPE_ID_ITEM 0x0002 0x04 31 | proto_id_item TYPE_PROTO_ID_ITEM 0x0003 0x0c 32 | field_id_item TYPE_FIELD_ID_ITEM 0x0004 0x08 33 | method_id_item TYPE_METHOD_ID_ITEM 0x0005 0x08 34 | class_def_item TYPE_CLASS_DEF_ITEM 0x0006 0x20 35 | call_site_id_item TYPE_CALL_SITE_ID_ITEM 0x0007 0x04 36 | method_handle_item TYPE_METHOD_HANDLE_ITEM 0x0008 0x08 37 | map_list TYPE_MAP_LIST 0x1000 4 + (item.size * 12) 38 | type_list TYPE_TYPE_LIST 0x1001 4 + (item.size * 2) 39 | annotation_set_ref_list TYPE_ANNOTATION_SET_REF_LIST 0x1002 4 + (item.size * 4) 40 | annotation_set_item TYPE_ANNOTATION_SET_ITEM 0x1003 4 + (item.size * 4) 41 | class_data_item TYPE_CLASS_DATA_ITEM 0x2000 隐式;必须解析 42 | code_item TYPE_CODE_ITEM 0x2001 隐式;必须解析 43 | string_data_item TYPE_STRING_DATA_ITEM 0x2002 隐式;必须解析 44 | debug_info_item TYPE_DEBUG_INFO_ITEM 0x2003 隐式;必须解析 45 | annotation_item TYPE_ANNOTATION_ITEM 0x2004 隐式;必须解析 46 | encoded_array_item TYPE_ENCODED_ARRAY_ITEM 0x2005 隐式;必须解析 47 | annotations_directory_item TYPE_ANNOTATIONS_DIRECTORY_ITEM 0x2006 隐式;必须解析 48 | hiddenapi_class_data_item TYPE_HIDDENAPI_CLASS_DATA_ITEM 0xF000 隐式;必须解析 49 | */ 50 | // 51 | 52 | function verify_by_maps_old(dexptr: NativePointer, mapsptr: NativePointer, range_base: NativePointer, range_end: NativePointer): boolean { 53 | const maps_offset = dexptr.add(0x34).readUInt(); 54 | const maps_size = mapsptr.readUInt(); 55 | for (let i = 0; i < maps_size; i++) { 56 | const item_type = mapsptr.add(4 + i * 0xC).readU16(); 57 | if (item_type === 4096) { 58 | const map_offset = mapsptr.add(4 + i * 0xC + 8).readUInt(); 59 | if (maps_offset === map_offset) { 60 | return true; 61 | } 62 | } 63 | } 64 | return false; 65 | } 66 | 67 | function verify_by_maps(dexptr: NativePointer, mapsptr: NativePointer, range_base: NativePointer, range_end: NativePointer): boolean { 68 | 69 | const maps_size = mapsptr.readUInt(); 70 | //看看每一个map_item type 是不是正确 71 | let rightTypeList = [ 72 | 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008, 73 | 0x1000,0x1001,0x1002,0x1003, 74 | 0x2000,0x2001,0x2002,0x2003,0x2004,0x2005,0x2006, 75 | 0xF000 76 | ]; 77 | 78 | let processed_type = new Map(); 79 | let unknown_type = new Map(); 80 | let mustOk = false; 81 | for (let i = 0; i < maps_size; i++) { 82 | 83 | // ptr is map_item[] begin addr 84 | let ptr = mapsptr.add(4 + i * 0xC); 85 | const item_type = ptr.readU16(); 86 | 87 | if (rightTypeList.indexOf(item_type) == -1) { 88 | unknown_type.set(item_type,1); 89 | //警告一下 90 | send(`[warn] map item type error: idx:${i} type:0x${item_type.toString(16).padStart(4, '0')} dexptr:${dexptr}`); 91 | } 92 | else{ 93 | let val = processed_type.get(item_type) || 0; 94 | val ++; 95 | processed_type.set(item_type,val) 96 | if(val > 1){ 97 | send(`[warn] map item type repeet: times:${val} idx:${i} type:0x${item_type.toString(16).padStart(4, '0')} dexptr:${dexptr}`); 98 | } 99 | } 100 | let map_item_size = ptr.add(4).readU32(); 101 | let map_item_offset = ptr.add(8).readU32(); 102 | const map_item_offset_addr = dexptr.add(map_item_offset) 103 | 104 | //特殊情况,size == 0 offset must == 0 105 | if(map_item_size === 0 && map_item_offset !==0){ 106 | return false; 107 | } 108 | //地址超出也不行 109 | if (map_item_offset_addr < range_base || map_item_offset_addr > range_end) { 110 | send(`[bad dex skip] map item type error: idx:${i} addr out of range:0x${map_item_offset.toString(16).padStart(4,'0')}`) 111 | return false; 112 | } 113 | 114 | 115 | if (item_type === 0x1000) { 116 | //map_off uint 从文件开头到映射项的偏移量。该偏移量(必须为非零值)应该是到 data 区段的偏移量,而数据应采用下文中“map_list”指定的格式。 117 | const map_offset =map_item_offset; 118 | const header_map_offset = dexptr.add(0x34).readUInt(); 119 | if (header_map_offset !== map_offset) { 120 | send(`[bad dex skip] map item header_map_offset(${header_map_offset}) !== map_offset(${map_offset}): idx:${i} `) 121 | return false; 122 | } 123 | else{ 124 | mustOk = true; 125 | } 126 | } 127 | else if(item_type === 0x0001){ 128 | //type_ids 从文件开头到类型标识符列表的偏移量;如果 type_ids_size == 0(不可否认是一种奇怪的极端情况),则该值为 0。该偏移量(如果为非零值)应该是到 type_ids 区段开头的偏移量。 129 | const header_string_id_size = dexptr.add(0x38).readUInt(); 130 | const header_string_id_offset = dexptr.add(0x38 + 4).readUInt(); 131 | 132 | 133 | if(header_string_id_size !== map_item_size){ 134 | send(`[bad dex skip] map item header_string_id_size(${header_string_id_size}) !== map_item_size(${map_item_size}): idx:${i} `) 135 | return false; 136 | } 137 | 138 | 139 | if (header_string_id_offset !== map_item_offset) { 140 | send(`[bad dex skip] map item header_string_id_offset(${header_string_id_offset}) !== map_item_offset(${map_item_offset}): idx:${i} `) 141 | return false; 142 | } 143 | 144 | } 145 | else if(item_type === 0x0002){ 146 | //type_ids 从文件开头到类型标识符列表的偏移量;如果 type_ids_size == 0(不可否认是一种奇怪的极端情况),则该值为 0。该偏移量(如果为非零值)应该是到 type_ids 区段开头的偏移量。 147 | const header_type_ids_size = dexptr.add(0x40).readUInt(); 148 | const header_type_ids_offset = dexptr.add(0x40 + 4).readUInt(); 149 | 150 | //类型标识符列表中的元素数量,最多为 65535 151 | if(map_item_size > 65535){ 152 | return false; 153 | } 154 | 155 | if (header_type_ids_size !== map_item_size) { 156 | send(`[bad dex skip] map item header_type_ids_size(${header_type_ids_size}) !== map_item_size(${map_item_size}): idx:${i} `) 157 | return false; 158 | } 159 | if (header_type_ids_offset !== map_item_offset) { 160 | send(`[bad dex skip] map item header_type_ids_offset(${header_type_ids_offset}) !== map_item_offset(${map_item_offset}): idx:${i} `) 161 | return false; 162 | } 163 | 164 | } 165 | else{ 166 | // nop 167 | continue; 168 | 169 | } 170 | } 171 | 172 | 173 | // 已知的,怎么也的多于未知的吧。 174 | return mustOk && processed_type.keys.length > unknown_type.keys.length; 175 | } 176 | 177 | 178 | function get_dex_real_size(dexptr: NativePointer, range_base: NativePointer, range_end: NativePointer): Number { 179 | const dex_size = dexptr.add(0x20).readUInt(); 180 | 181 | const maps_address = get_maps_address(dexptr, range_base, range_end); 182 | if (!maps_address) { 183 | return dex_size; 184 | } 185 | 186 | const maps_end = get_maps_end(maps_address, range_base, range_end); 187 | if (!maps_end) { 188 | return dex_size; 189 | } 190 | 191 | return maps_end.sub(dexptr).toInt32(); 192 | } 193 | 194 | function get_maps_address(dexptr: NativePointer, range_base: NativePointer, range_end: NativePointer): NativePointer | null { 195 | //+0x34 是 map_off ,也就是 map 段的偏移位置,一般情况下 map 段都是在 DEX 文件的最末尾,与 file_size 同理 196 | const maps_offset = dexptr.add(0x34).readUInt(); 197 | if (maps_offset === 0) { 198 | return null; 199 | } 200 | 201 | const maps_address = dexptr.add(maps_offset); 202 | 203 | //必须再Range的内存范围内,不在就不是 204 | if (maps_address < range_base || maps_address > range_end) { 205 | return null; 206 | } 207 | 208 | return maps_address; 209 | } 210 | 211 | function get_maps_end(maps: NativePointer, range_base: NativePointer, range_end: NativePointer): NativePointer | null { 212 | const maps_size = maps.readUInt(); 213 | // send(`get_maps_end maps_size:${maps_size}`) 214 | if (maps_size < 2 || maps_size > 50) { 215 | return null; 216 | } 217 | 218 | 219 | const maps_end = maps.add(maps_size * 0xC + 4); 220 | if (maps_end < range_base || maps_end > range_end) { 221 | return null; 222 | } 223 | 224 | return maps_end; 225 | } 226 | 227 | 228 | function verify(dexptr: NativePointer, range: RangeDetails, enable_verify_maps: boolean): boolean { 229 | 230 | if (range != null) { 231 | var range_end = range.base.add(range.size); 232 | // verify header_size header 结构放不下,跳过 233 | if (dexptr.add(0x70) > range_end) { 234 | return false; 235 | } 236 | 237 | var maps_address = get_maps_address(dexptr, range.base, range_end); 238 | if (!maps_address) { 239 | return false; 240 | } 241 | 242 | var maps_end = get_maps_end(maps_address, range.base, range_end); 243 | if (!maps_end) { 244 | return false; 245 | } 246 | 247 | if (!verify_ids_off(dexptr, range.size)) { 248 | return false; 249 | } 250 | 251 | if (enable_verify_maps) { 252 | return verify_by_maps(dexptr, maps_address,range.base, range_end) 253 | } else { 254 | 255 | // TODO: 读取头的大小,如果这里读出来的都是正确的,后面为啥还修复? 256 | return dexptr.add(0x3C).readUInt() === 0x70; 257 | } 258 | } 259 | 260 | return false; 261 | 262 | } 263 | 264 | function verify_ids_off(dexptr: NativePointer, dex_size: Number) { 265 | 266 | const string_ids_off = dexptr.add(0x3C).readUInt(); 267 | const type_ids_off = dexptr.add(0x44).readUInt(); 268 | const proto_ids_off = dexptr.add(0x4C).readUInt(); 269 | const field_ids_off = dexptr.add(0x54).readUInt(); 270 | const method_ids_off = dexptr.add(0x5C).readUInt(); 271 | 272 | //考虑特殊情况,添加一下 273 | // xxx_size == 0(不可否认是一种奇怪的极端情况),则该值为 0 274 | 275 | return string_ids_off < dex_size && (string_ids_off >= 0x70 || string_ids_off === 0) 276 | && type_ids_off < dex_size && (type_ids_off >= 0x70|| type_ids_off === 0) 277 | && proto_ids_off < dex_size && (proto_ids_off >= 0x70|| proto_ids_off === 0) 278 | && field_ids_off < dex_size && (field_ids_off >= 0x70|| field_ids_off === 0) 279 | && method_ids_off < dex_size && (method_ids_off >= 0x70|| method_ids_off === 0); 280 | 281 | } 282 | 283 | export function searchDex(deepSearch: boolean) { 284 | const result: any = []; 285 | 286 | //Process.enumerateRanges('r--') 这是用于遍历当前进程中所有可以读的内存段,毕竟不能读的内存区域是不能被 VM 执行的 (Native 可以)。想必不难理解。 287 | Process.enumerateRanges('r--').forEach(function (range: RangeDetails) { 288 | try { 289 | 290 | //DEX\n3??\0 搜索 64 65 78 0a 30 ?? ?? 00 291 | Memory.scanSync(range.base, range.size, "64 65 78 0a 30 ?? ?? 00").forEach(function (match) { 292 | 293 | //跳过系统的 294 | if (range.file && range.file.path 295 | && (range.file.path.startsWith("/data/dalvik-cache/") || 296 | range.file.path.startsWith("/system/"))) { 297 | return; 298 | } 299 | 300 | 301 | // send("search normal dex\\n begin header...") 302 | // 正常的,一般也不用检查别的了, 但是如果对方内存里面放点这个东西呢?那就乱了。 303 | // send(`-----------begin range ${match.address} --------------`) 304 | if (verify(match.address, range, false)) { 305 | const dex_size = get_dex_real_size(match.address, range.base, range.base.add(range.size)); 306 | send(`found normal dex\\n begin header, addr:${match.address}, size:${dex_size}`) 307 | result.push({ 308 | "addr": match.address, 309 | "size": dex_size 310 | }); 311 | 312 | const max_size = range.size - match.address.sub(range.base).toInt32(); 313 | 314 | //如果这里面,Range没有占满,可能考虑有重分布,全部弄出来保险点? 315 | if (deepSearch && max_size != dex_size) { 316 | result.push({ 317 | "addr": match.address, 318 | "size": max_size 319 | }); 320 | } 321 | } 322 | }); 323 | 324 | if (deepSearch) { 325 | //先查找具有 headersize 的内存,然后减去 0x3c 得到DEX头的位置。 应对加固的时候,DEX不在开始的情况 326 | Memory.scanSync(range.base, range.size, "70 00 00 00 78 56 34 12") 327 | .concat( 328 | Memory.scanSync(range.base, range.size, "70 00 00 00 12 34 56 78") 329 | ).forEach(function (match) { 330 | const dex_base = match.address.sub(0x3C); 331 | if (dex_base < range.base) { 332 | //前面没有 0x3c 的大小,不是 333 | return; 334 | } 335 | 336 | //如果有 uchar[8] magic ; 并且正确 337 | // TODO: 如果这里也没有呢?被修改了呢?程序还可以运行吗? 338 | if (dex_base.readCString(4) != "dex\n" && verify(dex_base, range, true)) { 339 | 340 | //计算真实大小 341 | const real_dex_size = get_dex_real_size(dex_base, range.base, range.base.add(range.size)); 342 | if (!verify_ids_off(dex_base, real_dex_size)) { 343 | return; 344 | } 345 | result.push({ 346 | "addr": dex_base, 347 | "size": real_dex_size 348 | }); 349 | const max_size = range.size - dex_base.sub(range.base).toInt32(); 350 | send(`DeepSearch found 'not dex\\n begin' header. , addr:${range.base}, size:${real_dex_size}`) 351 | if (max_size != real_dex_size) { 352 | result.push({ 353 | "addr": dex_base, 354 | "size": max_size 355 | }); 356 | } 357 | } 358 | }) 359 | } else { 360 | 361 | if (range.base.readCString(4) != "dex\n" && verify(range.base, range, true)) { 362 | const real_dex_size = get_dex_real_size(range.base, range.base, range.base.add(range.size)); 363 | send(`found 'not dex\\n begin' header. , addr:${range.base}, size:${real_dex_size}`) 364 | result.push({ 365 | "addr": range.base, 366 | "size": real_dex_size 367 | }); 368 | } 369 | } 370 | 371 | } catch (e) { 372 | console.log(e); 373 | } 374 | }); 375 | 376 | return result; 377 | } -------------------------------------------------------------------------------- /dist/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { main } from './index.js'; 3 | !async function () { 4 | try { 5 | process.exitCode = await main(); 6 | } 7 | catch (e) { 8 | console.error(e); 9 | process.exitCode = 1; 10 | } 11 | }(); 12 | //# sourceMappingURL=cli.js.map -------------------------------------------------------------------------------- /dist/cli.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAC,IAAI,EAAC,MAAM,YAAY,CAAA;AAE/B,CAAC,KAAK;IACN,IAAI;QACA,OAAO,CAAC,QAAQ,GAAG,MAAM,IAAI,EAAE,CAAC;KACnC;IACD,OAAO,CAAC,EAAE;QACN,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;KACxB;AACD,CAAC,EAAE,CAAC"} -------------------------------------------------------------------------------- /dist/dex_dumper.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | //# sourceMappingURL=dex_dumper.js.map -------------------------------------------------------------------------------- /dist/dex_dumper.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"dex_dumper.js","sourceRoot":"","sources":["../src/dex_dumper.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /dist/dumper.js: -------------------------------------------------------------------------------- 1 | import crypt from 'crypto'; 2 | import path from 'path'; 3 | import fs from 'fs'; 4 | function getErrorMessage(e) { 5 | let msg = e; 6 | if (typeof e === "string") { 7 | msg = e.toUpperCase(); // works, `e` narrowed to string 8 | } 9 | else if (e instanceof Error) { 10 | msg = e.message; // works, `e` narrowed to Error 11 | } 12 | return { msg, err: e }; 13 | } 14 | export class Dumper { 15 | rpc; 16 | constructor(rpc) { 17 | this.rpc = rpc; 18 | } 19 | async dump(outputDir, deepSearch = false) { 20 | // let options = this._options; 21 | console.log("[+] Searching..."); 22 | let startTime = new Date().valueOf(); 23 | //获取所有的ranges (后面的!告诉编译器忽略错误) 24 | let ranges = await this.rpc.searchdex(deepSearch); 25 | let endTime = new Date().valueOf(); 26 | console.log(`[*] Successful found ${ranges.length} dex, used ${(endTime - startTime) / 1000} time.`); 27 | console.log(`[+] Starting dump to '${outputDir}'...`); 28 | let dexMd5Map = new Map(); 29 | let classIndex = 1; 30 | for (const range of ranges) { 31 | try { 32 | let dexBytes = await this.rpc.memorydump(range.addr, range.size.valueOf()); 33 | let dexBytesBuffer = Buffer.from(dexBytes); 34 | //去掉重复的 35 | let md5sign = crypt.createHash('md5').update(dexBytesBuffer).digest('hex'); 36 | if (dexMd5Map.has(md5sign)) { 37 | continue; 38 | } 39 | dexMd5Map.set(md5sign, true); 40 | //修复 dex header 41 | dexBytesBuffer = this.fixDexHeader(dexBytesBuffer); 42 | let outputDexPath = path.join(outputDir, `classes${classIndex != 1 ? classIndex : ''}.dex`); 43 | fs.writeFileSync(outputDexPath, dexBytesBuffer); 44 | console.log(`[+] DexMd5=${md5sign}, SavePath=${outputDexPath}, DexSize=${range.size.valueOf().toString(16)}`); 45 | classIndex += 1; 46 | } 47 | catch (e) { 48 | console.warn(getErrorMessage(e).msg); 49 | continue; 50 | } 51 | } 52 | console.log("[*] All done..."); 53 | } 54 | /* 55 | struct header_item { 56 | uchar[8] magic ; 57 | uint checksum ; 58 | uchar[20] signature ; 59 | uint file_size ; 60 | uint header_size ; 61 | uint endian_tag ; 62 | uint link_size ; 63 | uint link_off ; 64 | uint map_off ; 65 | uint string_ids_size ; 66 | uint string_ids_off ; 67 | uint type_ids_size ; 68 | uint type_ids_off ; 69 | uint proto_ids_size ; 70 | uint proto_ids_off ; 71 | uint field_ids_size ; 72 | uint field_ids_off ; 73 | uint method_ids_size ; 74 | uint method_ids_off ; 75 | uint class_defs_size ; 76 | uint class_defs_off ; 77 | uint data_size ; 78 | uint data_off ; 79 | }; 80 | 81 | https://source.android.google.cn/docs/core/dalvik/dex-format#header-item 82 | 83 | */ 84 | fixDexHeader(buffer) { 85 | let dex_size = buffer.byteLength; 86 | // if dex_bytes[:4] != b"dex\n": 87 | // dex_bytes = b"dex\n035\x00" + dex_bytes[8:] 88 | if (buffer.toString('ascii', 0, 4) !== "dex\n") { 89 | buffer.write("dex\n", 'ascii'); 90 | // dex_bytes = b"dex\n035\x00" + dex_bytes[8:] 91 | } 92 | // if dex_size >= 0x24: 93 | // dex_bytes = dex_bytes[:0x20] + struct.Struct("= 0x24) { 95 | let val = buffer.readUint32LE(0x20); 96 | if (val == buffer.length) { 97 | console.log(`[*] fix header: skip fix header.file_size. (${val})`); 98 | } 99 | else { 100 | console.log(`[*] fix header: fix header.file_size. (${val} => ${buffer.length})`); 101 | buffer.writeUInt32LE(buffer.length, 0x20); 102 | } 103 | } 104 | if (buffer.length >= 0x28) { 105 | let val = buffer.readUint32LE(0x24); 106 | if (val == 0x70) { 107 | console.log(`[*] fix header: skip fix header.header_size. (0x${val.toString(16)})`); 108 | } 109 | else { 110 | console.log(`[*] fix header: fix header.header_size. (${val.toString(16)} => 0x70)`); 111 | buffer.writeUInt32LE(0x70, 0x24); 112 | } 113 | } 114 | if (buffer.length >= 0x2C && (["\x78\x56\x34\x12", "\x12\x34\x56\x78"].indexOf(buffer.toString('ascii', 0x28, 0x2c)) == -1)) { 115 | buffer.write("\x78\x56\x34\x12", 0x28, "ascii"); 116 | } 117 | return buffer; 118 | } 119 | } 120 | //# sourceMappingURL=dumper.js.map -------------------------------------------------------------------------------- /dist/dumper.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"dumper.js","sourceRoot":"","sources":["../src/dumper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,QAAQ,CAAC;AAC3B,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,MAAM,IAAI,CAAA;AAInB,SAAS,eAAe,CAAC,CAAK;IAC1B,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE;QACvB,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,CAAA,CAAC,gCAAgC;KACzD;SAAM,IAAI,CAAC,YAAY,KAAK,EAAE;QAC3B,GAAG,GAAG,CAAC,CAAC,OAAO,CAAA,CAAC,+BAA+B;KAClD;IAED,OAAO,EAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAC,CAAC;AACzB,CAAC;AAYD,MAAM,OAAO,MAAM;IACT,GAAG,CAAW;IACtB,YAAY,GAAa;QACvB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,SAAiB,EAAE,aAAsB,KAAK;QACvD,+BAA+B;QAC/B,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAChC,IAAI,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;QAErC,6BAA6B;QAC7B,IAAI,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAElD,IAAI,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;QACnC,OAAO,CAAC,GAAG,CACT,wBAAwB,MAAM,CAAC,MAAM,cACnC,CAAC,OAAO,GAAG,SAAS,CAAC,GAAG,IAC1B,QAAQ,CACT,CAAC;QAEF,OAAO,CAAC,GAAG,CAAC,yBAAyB,SAAS,MAAM,CAAC,CAAC;QAEtD,IAAI,SAAS,GAAG,IAAI,GAAG,EAAE,CAAC;QAC1B,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;YAC1B,IAAI;gBACF,IAAI,QAAQ,GAAI,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC5E,IAAI,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC3C,OAAO;gBACP,IAAI,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC3E,IAAG,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,EAAC;oBACtB,SAAS;iBACZ;gBAED,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;gBAE7B,eAAe;gBACf,cAAc,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC;gBAEnD,IAAI,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;gBAC5F,EAAE,CAAC,aAAa,CAAC,aAAa,EAAC,cAAc,CAAC,CAAC;gBAC/C,OAAO,CAAC,GAAG,CAAC,cAAc,OAAO,cAAc,aAAa,aAAa,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;gBAC9G,UAAU,IAAI,CAAC,CAAC;aAEjB;YAAC,OAAO,CAAC,EAAE;gBACV,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;gBACrC,SAAS;aACV;SACF;QAED,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;IAChC,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MA6BE;IACF,YAAY,CAAC,MAAe;QAE1B,IAAK,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC;QAClC,gCAAgC;QAChC,8CAA8C;QAG9C,IAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAC,CAAC,EAAE,CAAC,CAAC,KAAK,OAAO,EAAE;YAC1C,MAAM,CAAC,KAAK,CAAC,OAAO,EAAC,OAAO,CAAC,CAAC;YAC9B,8CAA8C;SACjD;QAED,uBAAuB;QACvB,uFAAuF;QACvF,IAAG,MAAM,CAAC,MAAM,IAAI,IAAI,EAAC;YACrB,IAAI,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YACpC,IAAG,GAAG,IAAI,MAAM,CAAC,MAAM,EAAC;gBACpB,OAAO,CAAC,GAAG,CAAC,+CAA+C,GAAG,GAAG,CAAC,CAAC;aACtE;iBACG;gBACA,OAAO,CAAC,GAAG,CAAC,0CAA0C,GAAG,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;gBAClF,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,MAAM,EAAC,IAAI,CAAC,CAAC;aAC5C;SAEJ;QAED,IAAG,MAAM,CAAC,MAAM,IAAI,IAAI,EAAC;YAErB,IAAI,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YACpC,IAAG,GAAG,IAAI,IAAI,EAAC;gBACX,OAAO,CAAC,GAAG,CAAC,mDAAmD,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;aACvF;iBACG;gBACA,OAAO,CAAC,GAAG,CAAC,4CAA4C,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC;gBACrF,MAAM,CAAC,aAAa,CAAC,IAAI,EAAC,IAAI,CAAC,CAAC;aACnC;SACJ;QAED,IAAG,MAAM,CAAC,MAAM,IAAI,IAAI,IAAI,CAAC,CAAC,kBAAkB,EAAC,kBAAkB,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAC;YACrH,MAAM,CAAC,KAAK,CAAC,kBAAkB,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;SACnD;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"} -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | import { program } from "commander"; 2 | import inquirer from "inquirer"; 3 | import * as frida from "frida"; 4 | import path from "path"; 5 | import fs from "fs"; 6 | import { Dumper } from "./dumper.js"; 7 | import inquirerPrompt from "inquirer-autocomplete-prompt"; 8 | inquirer.registerPrompt("autocomplete", inquirerPrompt); 9 | function getErrorMessage(e) { 10 | let msg = e; 11 | if (typeof e === "string") { 12 | msg = e.toUpperCase(); // works, `e` narrowed to string 13 | } 14 | else if (e instanceof Error) { 15 | msg = e.message; // works, `e` narrowed to Error 16 | } 17 | return { msg, err: e }; 18 | } 19 | export async function main() { 20 | program 21 | .usage("[options] [package-name]") 22 | .option("-U, --usb-device", "use usb device", false) 23 | .option("-H, --usb-host-ip ", "use host:ip device, frida server defaut is 127.0.0.1:27042, use adb forward tcp:27042 tcp:27042 first") 24 | .option("-f, --spawn", "use spawn restart app") 25 | .option("-F, --front-app", "dump front app") 26 | .option("-o, --output-dex-path ", "output dir of the dex file to save, default is .//") 27 | .option("-d, --deep-search", "use deep search to dump dex", false) 28 | .option("-s, --sleep-befor_dump", "wait some seconds to dump dex", "5") 29 | .option("--include-system", "when --spawn enable, use this to get system apps"); 30 | program.parse(); 31 | const opts = program.opts(); 32 | let packageName = program.args[0]; 33 | //console.log(opts, packageNameOrPid); 34 | if ("usbHostIp" in opts) { 35 | opts.usbHostIp = opts.usbHostIp || "127.0.0.1:27042"; 36 | } 37 | if (!opts.usbDevice && !opts.usbHostIp) { 38 | program.outputHelp(); 39 | return 3; 40 | } 41 | try { 42 | let targetDevice = null; 43 | try { 44 | let deviceManager = frida.getDeviceManager(); 45 | if (opts.usbDevice) { 46 | //所有设备 47 | let deviceList = await deviceManager.enumerateDevices(); 48 | //仅保留手机等远程设备 49 | deviceList = deviceList.filter((d) => d.type === "remote"); 50 | //多个设备,用户选择一下吧 51 | if (deviceList.length > 1) { 52 | // console.log(inquirer) 53 | let choiceDeviceName = await inquirer.prompt({ 54 | type: "list", 55 | name: "name", 56 | message: "What device?", 57 | choices: deviceList.map((d) => d.name), 58 | }); 59 | targetDevice = deviceList.find((d) => d.name == choiceDeviceName.name); 60 | } 61 | else { 62 | //只有一个,就它吧 63 | targetDevice = deviceList[0]; 64 | } 65 | // targetDevice = await frida.getUsbDevice(); 66 | } 67 | else { 68 | // 直接指定了,就它吧 69 | targetDevice = await deviceManager.addRemoteDevice(opts.usbHostIp); 70 | } 71 | } 72 | catch (e) { 73 | console.log(getErrorMessage(e).msg); 74 | return 3; 75 | } 76 | let session = null; 77 | let script = null; 78 | let targetPID = 0; 79 | if (opts.spawn) { 80 | if (!packageName) { 81 | // 通过安装好的应用中选择 82 | let appsInstaled = await targetDevice.enumerateApplications({ 83 | scope: frida.Scope.Full, 84 | }); 85 | if (!opts.includeSystem) { 86 | appsInstaled = appsInstaled.filter((d) => d.parameters.sources && 87 | d.parameters.sources.length > 0 && 88 | !d.parameters.sources[0].startsWith("/system/")); 89 | } 90 | let aiSelect = await inquirer.prompt({ 91 | type: "list", 92 | name: "sel", 93 | message: "What app?", 94 | choices: appsInstaled.map((d) => `${d.identifier}(${d.name})`), 95 | }); 96 | packageName = aiSelect.sel.match(/^(.+?)\(/)[1]; 97 | } 98 | targetPID = await targetDevice.spawn(packageName); 99 | try { 100 | session = await targetDevice.attach(targetPID); 101 | //继续运行 102 | await session.resume(); 103 | } 104 | catch (e) { 105 | console.log(getErrorMessage(e).msg); 106 | return 3; 107 | } 108 | } 109 | else { 110 | //从运行的App中找一个 111 | let processList = await targetDevice.enumerateProcesses({ 112 | scope: frida.Scope.Full, 113 | }); 114 | let appProcessList = processList.filter((p) => p.parameters.applications && 115 | p.parameters.applications.length > 0 && 116 | p.parameters.icons); 117 | if (opts.frontApp) { 118 | let frontAppProcessList = appProcessList.filter(p => p.parameters.frontmost); 119 | if (frontAppProcessList.length === 1) { 120 | let p = frontAppProcessList[0]; 121 | targetPID = p.pid; 122 | // packageName = p.parameters.applications? p.parameters.applications[0] : 'com.nothis.app'; 123 | } 124 | else { 125 | console.log(`error, ${frontAppProcessList.length} front app found. `); 126 | return 1; 127 | } 128 | } 129 | else if (packageName) { 130 | let foundPkgList = appProcessList.filter((d) => (d.parameters.applications ? d.parameters.applications[0] : "") === 131 | packageName); 132 | if (foundPkgList.length === 1) { 133 | targetPID = foundPkgList[0].pid; 134 | } 135 | else { 136 | console.log(`error, found ${foundPkgList.length} app instance. `); 137 | return 1; 138 | } 139 | } 140 | //如果没有找到,继续找 141 | if (targetPID === 0) { 142 | // 通过安装好的应用中选择 143 | //let appsInstaled = await targetDevice.enumerateApplications(); 144 | let cpn = await inquirer.prompt({ 145 | type: "list", 146 | name: "sel", 147 | message: "What app?", 148 | choices: appProcessList.map((d) => `${d.pid}:${d.parameters.applications ? d.parameters.applications[0] : "nop"}-${d.name}`), 149 | }); 150 | let pid = parseInt(cpn.sel); 151 | // packageName = 152 | targetPID = pid; 153 | } 154 | // console.log(JSON.stringify(appProcessList,null,4)); 155 | console.log("attach to app with pid:", targetPID); 156 | try { 157 | session = await targetDevice.attach(targetPID); 158 | } 159 | catch (e) { 160 | console.log(getErrorMessage(e).msg); 161 | return 3; 162 | } 163 | } 164 | if (session === null) { 165 | console.log(`error, session is null `); 166 | return 2; 167 | } 168 | try { 169 | // script = await session.createScript("console.log('hello world')"); 170 | let agentJsFilePath = path.join(process.cwd(), "agent", "dist", "index.js"); 171 | if (!fs.existsSync(agentJsFilePath)) { 172 | console.log("agent file not found in path:", agentJsFilePath); 173 | } 174 | script = await session.createScript(fs.readFileSync(agentJsFilePath, { encoding: "utf8" })); 175 | script.message.connect((msg, data) => { 176 | if (msg.type == "send") { 177 | console.log(`[*] ${msg.payload}`); 178 | } 179 | else { 180 | console.log(msg); 181 | } 182 | }); 183 | await script.load(); 184 | let agentRpc = script.exports; 185 | if (!agentRpc) { 186 | console.log("inject agent script error!"); 187 | return 3; 188 | } 189 | if (opts.sleep > 0) { 190 | Thread.sleep(opts.sleep); 191 | } 192 | let getAppName = async () => { 193 | let currentProcess = await targetDevice.enumerateProcesses({ pids: [targetPID], scope: frida.Scope.Full }); 194 | let appName = currentProcess[0].parameters.applications ? currentProcess[0].parameters.applications[0] : 'no.this.app'; 195 | return appName; 196 | }; 197 | //设置保存的路径 198 | let outputDexPath = opts.outputDexPath || path.join(process.cwd(), await getAppName()); 199 | if (!fs.existsSync(outputDexPath)) { 200 | fs.mkdirSync(outputDexPath, { recursive: true }); 201 | } 202 | // begin to dump 203 | let dumper = new Dumper(agentRpc); 204 | await dumper.dump(outputDexPath, opts.deepSearch); 205 | } 206 | catch (e) { 207 | console.log(getErrorMessage(e).msg); 208 | return 3; 209 | } 210 | finally { 211 | if (script) { 212 | await script.unload(); 213 | script = null; 214 | } 215 | if (session) { 216 | await session.detach(); 217 | console.log("detched from device."); 218 | session = null; 219 | } 220 | } 221 | } 222 | catch (e) { 223 | console.log(e); 224 | return 3; 225 | } 226 | return 0; 227 | } 228 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /dist/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/test copy.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // import { TextEncoder } from "util"; 3 | function so_test1() { 4 | let modules = Process.enumerateModules(); 5 | for (const m of modules) { 6 | let exports = m.enumerateExports(); 7 | for (const exp of exports) { 8 | //console.log(JSON.stringify(exp)); 9 | if (exp.name.indexOf('onCreate') != -1) { 10 | console.log('so onCreate found :', JSON.stringify(exp)); 11 | } 12 | } 13 | } 14 | } 15 | function testHook1() { 16 | let loadedCls = Java.enumerateLoadedClasses({ 17 | onMatch: function (name, handle) { 18 | if (name.indexOf('com.yssenlin') != -1) { 19 | console.log(name, handle); 20 | } 21 | }, 22 | onComplete: function () { 23 | console.log('------- all class found! ----------'); 24 | } 25 | }); 26 | let mainActivity = Java.use('com.yssenlin.app.MainActivity'); 27 | let methods = mainActivity.class.getDeclaredMethods(); 28 | for (const iterator of methods) { 29 | console.log(iterator); 30 | } 31 | //console.log(JSON.stringify(mainActivity.$ownMembers,null,4)); 32 | //send(mainActivity.onCreate) 33 | } 34 | function hookTest2() { 35 | let shufferMap = Java.use("com.xiaojianbang.app.ShufferMap"); 36 | shufferMap.show.implementation = function (m) { 37 | console.log(m); 38 | for (let k of m.keySet().toArray()) { 39 | console.log(k, '=', m.get(k)); 40 | } 41 | // // console.log(m, m.get("user"), JSON.stringify(m.class.getDeclaredMethods().map(d=>d.toString()),null,4)); 42 | // console.log("--------------------------" + new Date() + "--------------------------"); 43 | // console.log(JSON.stringify(m.keySet().class.getDeclaredMethods().map(d=>d.toString()),null,4)); 44 | m.put("user", "user_string"); 45 | console.log(m); 46 | return this.show(m); 47 | }; 48 | } 49 | function hookTest3() { 50 | let rsa = Java.use("com.xiaojianbang.app.RSA"); 51 | let data = "hello world"; 52 | let dataBytes = new TextEncoder().encode(data); 53 | console.log(dataBytes); 54 | let encDataBytes = rsa.encrypt(dataBytes); 55 | console.log(encDataBytes); 56 | } 57 | function main() { 58 | console.log("--------------------------" + new Date() + "--------------------------"); 59 | Java.perform(() => { 60 | // testHook1(); 61 | hookTest2(); 62 | hookTest3(); 63 | }); 64 | } 65 | setImmediate(main); 66 | //# sourceMappingURL=test%20copy.js.map -------------------------------------------------------------------------------- /dist/test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // import { TextEncoder } from "util"; 3 | function so_test1() { 4 | let modules = Process.enumerateModules(); 5 | for (const m of modules) { 6 | let exports = m.enumerateExports(); 7 | for (const exp of exports) { 8 | //console.log(JSON.stringify(exp)); 9 | if (exp.name.indexOf('onCreate') != -1) { 10 | console.log('so onCreate found :', JSON.stringify(exp)); 11 | } 12 | } 13 | } 14 | } 15 | function testHook1() { 16 | let loadedCls = Java.enumerateLoadedClasses({ 17 | onMatch: function (name, handle) { 18 | if (name.indexOf('com.yssenlin') != -1) { 19 | console.log(name, handle); 20 | } 21 | }, 22 | onComplete: function () { 23 | console.log('------- all class found! ----------'); 24 | } 25 | }); 26 | let mainActivity = Java.use('com.yssenlin.app.MainActivity'); 27 | let methods = mainActivity.class.getDeclaredMethods(); 28 | for (const iterator of methods) { 29 | console.log(iterator); 30 | } 31 | //console.log(JSON.stringify(mainActivity.$ownMembers,null,4)); 32 | //send(mainActivity.onCreate) 33 | } 34 | function hookTest2() { 35 | let shufferMap = Java.use("com.xiaojianbang.app.ShufferMap"); 36 | shufferMap.show.implementation = function (m) { 37 | console.log(m); 38 | for (let k of m.keySet().toArray()) { 39 | console.log(k, '=', m.get(k)); 40 | } 41 | // // console.log(m, m.get("user"), JSON.stringify(m.class.getDeclaredMethods().map(d=>d.toString()),null,4)); 42 | // console.log("--------------------------" + new Date() + "--------------------------"); 43 | // console.log(JSON.stringify(m.keySet().class.getDeclaredMethods().map(d=>d.toString()),null,4)); 44 | m.put("user", "user_string"); 45 | console.log(m); 46 | return this.show(m); 47 | }; 48 | } 49 | function hookTest3() { 50 | let rsa = Java.use("com.xiaojianbang.app.RSA"); 51 | let data = "hello world"; 52 | let dataBytes = new TextEncoder().encode(data); 53 | console.log(dataBytes); 54 | let encDataBytes = rsa.encrypt(dataBytes); 55 | console.log(encDataBytes); 56 | } 57 | function main() { 58 | console.log("--------------------------" + new Date() + "--------------------------"); 59 | Java.perform(() => { 60 | // testHook1(); 61 | hookTest2(); 62 | hookTest3(); 63 | }); 64 | } 65 | setImmediate(main); 66 | //# sourceMappingURL=test.js.map -------------------------------------------------------------------------------- /dist/test.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"test.js","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":";AAAA,sCAAsC;AAEtC,SAAS,QAAQ;IACb,IAAI,OAAO,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE;QACrB,IAAI,OAAO,GAAG,CAAC,CAAC,gBAAgB,EAAE,CAAC;QACnC,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE;YACvB,mCAAmC;YACnC,IAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,EAAC;gBAClC,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;aAC5D;SACJ;KACJ;AACL,CAAC;AAED,SAAS,SAAS;IACd,IAAI,SAAS,GAAG,IAAI,CAAC,sBAAsB,CAAC;QACxC,OAAO,EAAC,UAAS,IAAI,EAAC,MAAM;YACxB,IAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,EAAC;gBAClC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;aAC7B;QACL,CAAC;QACD,UAAU,EAAC;YACP,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;QACvD,CAAC;KACJ,CAAC,CAAC;IAGH,IAAI,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;IAC7D,IAAI,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC;IACtD,KAAK,MAAM,QAAQ,IAAI,OAAO,EAAE;QAC5B,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;KACxB;IACD,+DAA+D;IAE/D,6BAA6B;AACjC,CAAC;AAED,SAAS,SAAS;IAEd,IAAI,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;IAC7D,UAAU,CAAC,IAAI,CAAC,cAAc,GAAG,UAAS,CAAK;QAC3C,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACf,KAAI,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,EAAC;YAC9B,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;SACjC;QACD,8GAA8G;QAC9G,yFAAyF;QACzF,kGAAkG;QAClG,CAAC,CAAC,GAAG,CAAC,MAAM,EAAC,aAAa,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACf,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxB,CAAC,CAAA;AAEL,CAAC;AAED,SAAS,SAAS;IACd,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;IAC/C,IAAI,IAAI,GAAI,aAAa,CAAC;IAE1B,IAAI,SAAS,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;IAEtB,IAAI,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,CAAE,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;AAC7B,CAAC;AAID,SAAS,IAAI;IACT,OAAO,CAAC,GAAG,CAAC,4BAA4B,GAAG,IAAI,IAAI,EAAE,GAAG,4BAA4B,CAAC,CAAC;IACtF,IAAI,CAAC,OAAO,CAAC,GAAE,EAAE;QACd,eAAe;QACf,SAAS,EAAE,CAAC;QACZ,SAAS,EAAE,CAAC;IACf,CAAC,CAAC,CAAA;AACN,CAAC;AAGD,YAAY,CAAC,IAAI,CAAC,CAAC"} -------------------------------------------------------------------------------- /doc/useage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evlon/frida-js-dexdump/040d18144eba59cc2a92cbde841b0a928123185c/doc/useage.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frida-js-dexdump", 3 | "author": { 4 | "name": "evlon", 5 | "email": "evlion@qq.com", 6 | "url": "https://github.com/evlon/" 7 | }, 8 | "homepage": "https://github.com/evlon/frida-js-dexdump", 9 | "version": "0.3.0", 10 | "main": "cli.js", 11 | "license": "MIT", 12 | "type": "module", 13 | "bin": "dist/cli.js", 14 | "scripts": { 15 | "build-agent": "frida-compile ./agent/src/index.ts -o ./agent/dist/index.js -c", 16 | "watch-agent": "frida-compile ./agent/src/index.ts -o ./agent/dist/index.js -w", 17 | "build": "tsc.cmd -p ./tsconfig.json", 18 | "watch": "tsc.cmd -w -p ./tsconfig.json" 19 | }, 20 | "dependencies": { 21 | "@types/frida-gum": "^18.1.0", 22 | "@types/node": "^18.7.23", 23 | "crypto-js": "^4.1.1", 24 | "frida": "^15.2.2", 25 | "frida-compile": "^16.1.2", 26 | "inquirer": "^9.1.2", 27 | "inquirer-autocomplete-prompt": "^3.0.0", 28 | "ts-node": "^10.9.1", 29 | "typescript": "^4.8.4" 30 | }, 31 | "devDependencies": { 32 | "@types/crypto-js": "^4.1.1", 33 | "@types/inquirer": "^9.0.2", 34 | "@types/inquirer-autocomplete-prompt": "^1.3.5" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import {main} from './index.js' 3 | 4 | !async function(){ 5 | try { 6 | process.exitCode = await main(); 7 | } 8 | catch (e) { 9 | console.error(e); 10 | process.exitCode = 1; 11 | } 12 | }(); -------------------------------------------------------------------------------- /src/dumper.ts: -------------------------------------------------------------------------------- 1 | import * as frida from "frida"; 2 | import crypt from 'crypto'; 3 | import path from 'path' 4 | import fs from 'fs' 5 | 6 | 7 | 8 | function getErrorMessage(e:any){ 9 | let msg = e; 10 | if (typeof e === "string") { 11 | msg = e.toUpperCase() // works, `e` narrowed to string 12 | } else if (e instanceof Error) { 13 | msg = e.message // works, `e` narrowed to Error 14 | } 15 | 16 | return {msg, err :e}; 17 | } 18 | 19 | 20 | export interface AgentRpc { 21 | searchdex(deepSearch: boolean): Promise; 22 | memorydump( 23 | address: number | NativePointerValue, 24 | size: number 25 | ): Promise; 26 | stopthreads(): Promise; 27 | } 28 | 29 | export class Dumper { 30 | private rpc: AgentRpc; 31 | constructor(rpc: AgentRpc) { 32 | this.rpc = rpc; 33 | } 34 | 35 | async dump(outputDir: string, deepSearch: boolean = false) { 36 | // let options = this._options; 37 | console.log("[+] Searching..."); 38 | let startTime = new Date().valueOf(); 39 | 40 | //获取所有的ranges (后面的!告诉编译器忽略错误) 41 | let ranges = await this.rpc.searchdex(deepSearch); 42 | 43 | let endTime = new Date().valueOf(); 44 | console.log( 45 | `[*] Successful found ${ranges.length} dex, used ${ 46 | (endTime - startTime) / 1000 47 | } time.` 48 | ); 49 | 50 | console.log(`[+] Starting dump to '${outputDir}'...`); 51 | 52 | let dexMd5Map = new Map(); 53 | let classIndex = 1; 54 | for (const range of ranges) { 55 | try { 56 | let dexBytes = await this.rpc.memorydump(range.addr, range.size.valueOf()); 57 | let dexBytesBuffer = Buffer.from(dexBytes); 58 | //去掉重复的 59 | let md5sign = crypt.createHash('md5').update(dexBytesBuffer).digest('hex'); 60 | if(dexMd5Map.has(md5sign)){ 61 | continue; 62 | } 63 | 64 | dexMd5Map.set(md5sign, true); 65 | 66 | //修复 dex header 67 | dexBytesBuffer = this.fixDexHeader(dexBytesBuffer); 68 | 69 | let outputDexPath = path.join(outputDir, `classes${classIndex != 1 ? classIndex : ''}.dex`); 70 | fs.writeFileSync(outputDexPath,dexBytesBuffer); 71 | console.log(`[+] DexMd5=${md5sign}, SavePath=${outputDexPath}, DexSize=${range.size.valueOf().toString(16)}`); 72 | classIndex += 1; 73 | 74 | } catch (e) { 75 | console.warn(getErrorMessage(e).msg); 76 | continue; 77 | } 78 | } 79 | 80 | console.log("[*] All done...") 81 | } 82 | 83 | /* 84 | struct header_item { 85 | uchar[8] magic ; 86 | uint checksum ; 87 | uchar[20] signature ; 88 | uint file_size ; 89 | uint header_size ; 90 | uint endian_tag ; 91 | uint link_size ; 92 | uint link_off ; 93 | uint map_off ; 94 | uint string_ids_size ; 95 | uint string_ids_off ; 96 | uint type_ids_size ; 97 | uint type_ids_off ; 98 | uint proto_ids_size ; 99 | uint proto_ids_off ; 100 | uint field_ids_size ; 101 | uint field_ids_off ; 102 | uint method_ids_size ; 103 | uint method_ids_off ; 104 | uint class_defs_size ; 105 | uint class_defs_off ; 106 | uint data_size ; 107 | uint data_off ; 108 | }; 109 | 110 | https://source.android.google.cn/docs/core/dalvik/dex-format#header-item 111 | 112 | */ 113 | fixDexHeader(buffer : Buffer){ 114 | 115 | let dex_size = buffer.byteLength; 116 | // if dex_bytes[:4] != b"dex\n": 117 | // dex_bytes = b"dex\n035\x00" + dex_bytes[8:] 118 | 119 | 120 | if(buffer.toString('ascii',0, 4) !== "dex\n" ){ 121 | buffer.write("dex\n",'ascii'); 122 | // dex_bytes = b"dex\n035\x00" + dex_bytes[8:] 123 | } 124 | 125 | // if dex_size >= 0x24: 126 | // dex_bytes = dex_bytes[:0x20] + struct.Struct("= 0x24){ 128 | let val = buffer.readUint32LE(0x20); 129 | if(val == buffer.length){ 130 | console.log(`[*] fix header: skip fix header.file_size. (${val})`); 131 | } 132 | else{ 133 | console.log(`[*] fix header: fix header.file_size. (${val} => ${buffer.length})`); 134 | buffer.writeUInt32LE(buffer.length,0x20); 135 | } 136 | 137 | } 138 | 139 | if(buffer.length >= 0x28){ 140 | 141 | let val = buffer.readUint32LE(0x24); 142 | if(val == 0x70){ 143 | console.log(`[*] fix header: skip fix header.header_size. (0x${val.toString(16)})`); 144 | } 145 | else{ 146 | console.log(`[*] fix header: fix header.header_size. (${val.toString(16)} => 0x70)`); 147 | buffer.writeUInt32LE(0x70,0x24); 148 | } 149 | } 150 | 151 | if(buffer.length >= 0x2C && (["\x78\x56\x34\x12","\x12\x34\x56\x78"].indexOf(buffer.toString('ascii', 0x28,0x2c)) == -1)){ 152 | buffer.write("\x78\x56\x34\x12", 0x28, "ascii"); 153 | } 154 | 155 | return buffer; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { program } from "commander"; 2 | import inquirer from "inquirer"; 3 | import * as frida from "frida"; 4 | import path from "path"; 5 | import fs from "fs"; 6 | import { Dumper, AgentRpc } from "./dumper.js"; 7 | 8 | import inquirerPrompt from "inquirer-autocomplete-prompt"; 9 | inquirer.registerPrompt("autocomplete", inquirerPrompt); 10 | 11 | function getErrorMessage(e: any) { 12 | let msg = e; 13 | if (typeof e === "string") { 14 | msg = e.toUpperCase(); // works, `e` narrowed to string 15 | } else if (e instanceof Error) { 16 | msg = e.message; // works, `e` narrowed to Error 17 | } 18 | 19 | return { msg, err: e }; 20 | } 21 | 22 | export async function main() { 23 | program 24 | .usage("[options] [package-name]") 25 | .option("-U, --usb-device", "use usb device", false) 26 | .option( 27 | "-H, --usb-host-ip ", 28 | "use host:ip device, frida server defaut is 127.0.0.1:27042, use adb forward tcp:27042 tcp:27042 first" 29 | ) 30 | .option("-f, --spawn", "use spawn restart app") 31 | .option("-F, --front-app","dump front app") 32 | .option( 33 | "-o, --output-dex-path ", 34 | "output dir of the dex file to save, default is .//" 35 | ) 36 | .option("-d, --deep-search", "use deep search to dump dex", false) 37 | .option("-s, --sleep-befor_dump", "wait some seconds to dump dex", "5") 38 | .option( 39 | "--include-system", 40 | "when --spawn enable, use this to get system apps" 41 | ); 42 | 43 | program.parse(); 44 | const opts = program.opts(); 45 | 46 | let packageName: any = program.args[0]; 47 | 48 | //console.log(opts, packageNameOrPid); 49 | 50 | if ("usbHostIp" in opts) { 51 | opts.usbHostIp = opts.usbHostIp || "127.0.0.1:27042"; 52 | } 53 | 54 | if (!opts.usbDevice && !opts.usbHostIp) { 55 | program.outputHelp(); 56 | return 3; 57 | } 58 | try { 59 | let targetDevice : frida.Device | null = null; 60 | try { 61 | let deviceManager = frida.getDeviceManager(); 62 | if (opts.usbDevice) { 63 | //所有设备 64 | let deviceList = await deviceManager.enumerateDevices(); 65 | 66 | //仅保留手机等远程设备 67 | deviceList = deviceList.filter((d) => d.type === "remote"); 68 | 69 | //多个设备,用户选择一下吧 70 | if (deviceList.length > 1) { 71 | // console.log(inquirer) 72 | let choiceDeviceName = await inquirer.prompt({ 73 | type: "list", 74 | name: "name", 75 | message: "What device?", 76 | choices: deviceList.map((d) => d.name), 77 | }); 78 | 79 | targetDevice = deviceList.find( 80 | (d) => d.name == choiceDeviceName.name 81 | )!; 82 | } else { 83 | //只有一个,就它吧 84 | targetDevice = deviceList[0]; 85 | } 86 | // targetDevice = await frida.getUsbDevice(); 87 | } else { 88 | // 直接指定了,就它吧 89 | targetDevice = await deviceManager.addRemoteDevice(opts.usbHostIp); 90 | } 91 | } catch (e) { 92 | console.log(getErrorMessage(e).msg); 93 | return 3; 94 | } 95 | 96 | let session: frida.Session | null = null; 97 | let script: frida.Script | null = null; 98 | 99 | let targetPID = 0; 100 | if (opts.spawn) { 101 | if (!packageName) { 102 | // 通过安装好的应用中选择 103 | let appsInstaled = await targetDevice.enumerateApplications({ 104 | scope: frida.Scope.Full, 105 | }); 106 | 107 | if (!opts.includeSystem) { 108 | appsInstaled = appsInstaled.filter( 109 | (d) => 110 | d.parameters.sources && 111 | d.parameters.sources.length > 0 && 112 | !d.parameters.sources[0].startsWith("/system/") 113 | ); 114 | } 115 | 116 | let aiSelect = await inquirer.prompt({ 117 | type: "list", 118 | name: "sel", 119 | message: "What app?", 120 | choices: appsInstaled.map((d) => `${d.identifier}(${d.name})`), 121 | }); 122 | 123 | packageName = aiSelect.sel.match(/^(.+?)\(/)[1]; 124 | 125 | } 126 | 127 | targetPID = await targetDevice.spawn(packageName); 128 | 129 | try { 130 | session = await targetDevice.attach(targetPID); 131 | //继续运行 132 | await session.resume(); 133 | } catch (e) { 134 | console.log(getErrorMessage(e).msg); 135 | return 3; 136 | } 137 | } else { 138 | //从运行的App中找一个 139 | let processList = await targetDevice.enumerateProcesses({ 140 | scope: frida.Scope.Full, 141 | }); 142 | 143 | let appProcessList = processList.filter( 144 | (p) => 145 | p.parameters.applications && 146 | p.parameters.applications.length > 0 && 147 | p.parameters.icons 148 | ); 149 | 150 | if(opts.frontApp){ 151 | let frontAppProcessList = appProcessList.filter(p=>p.parameters.frontmost); 152 | if (frontAppProcessList.length === 1) { 153 | let p = frontAppProcessList[0]; 154 | targetPID = p.pid; 155 | // packageName = p.parameters.applications? p.parameters.applications[0] : 'com.nothis.app'; 156 | } else { 157 | console.log(`error, ${frontAppProcessList.length} front app found. `); 158 | return 1; 159 | } 160 | } 161 | else if (packageName) { 162 | let foundPkgList = appProcessList.filter( 163 | (d) => 164 | (d.parameters.applications ? d.parameters.applications[0] : "") === 165 | packageName 166 | ); 167 | if (foundPkgList.length === 1) { 168 | targetPID = foundPkgList[0].pid; 169 | } else { 170 | console.log(`error, found ${foundPkgList.length} app instance. `); 171 | return 1; 172 | } 173 | } 174 | 175 | //如果没有找到,继续找 176 | if (targetPID === 0) { 177 | // 通过安装好的应用中选择 178 | //let appsInstaled = await targetDevice.enumerateApplications(); 179 | 180 | let cpn = await inquirer.prompt({ 181 | type: "list", 182 | name: "sel", 183 | message: "What app?", 184 | choices: appProcessList.map( 185 | (d) => 186 | `${d.pid}:${ 187 | d.parameters.applications ? d.parameters.applications[0] : "nop" 188 | }-${d.name}` 189 | ), 190 | }); 191 | 192 | let pid = parseInt(cpn.sel); 193 | // packageName = 194 | targetPID = pid; 195 | } 196 | 197 | // console.log(JSON.stringify(appProcessList,null,4)); 198 | 199 | console.log("attach to app with pid:", targetPID); 200 | 201 | try { 202 | session = await targetDevice.attach(targetPID); 203 | } catch (e) { 204 | console.log(getErrorMessage(e).msg); 205 | return 3; 206 | } 207 | } 208 | 209 | if (session === null) { 210 | console.log(`error, session is null `); 211 | return 2; 212 | } 213 | 214 | try { 215 | // script = await session.createScript("console.log('hello world')"); 216 | let agentJsFilePath = path.join( 217 | process.cwd(), 218 | "agent", 219 | "dist", 220 | "index.js" 221 | ); 222 | if (!fs.existsSync(agentJsFilePath)) { 223 | console.log("agent file not found in path:", agentJsFilePath); 224 | } 225 | script = await session.createScript( 226 | fs.readFileSync(agentJsFilePath, { encoding: "utf8" }) 227 | ); 228 | script.message.connect((msg, data) => { 229 | if (msg.type == "send") { 230 | console.log(`[*] ${msg.payload}`); 231 | } else { 232 | console.log(msg); 233 | } 234 | }); 235 | 236 | await script.load(); 237 | 238 | let agentRpc = script.exports as unknown as AgentRpc; 239 | 240 | if (!agentRpc) { 241 | console.log("inject agent script error!"); 242 | return 3; 243 | } 244 | 245 | if (opts.sleep > 0) { 246 | Thread.sleep(opts.sleep); 247 | } 248 | 249 | 250 | let getAppName = async ()=>{ 251 | let currentProcess = await targetDevice!.enumerateProcesses({pids:[targetPID],scope: frida.Scope.Full}); 252 | let appName = currentProcess[0].parameters.applications ? currentProcess[0].parameters.applications[0] : 'no.this.app'; 253 | return appName; 254 | } 255 | 256 | //设置保存的路径 257 | let outputDexPath = opts.outputDexPath || path.join(process.cwd(), await getAppName()); 258 | if (!fs.existsSync(outputDexPath)) { 259 | fs.mkdirSync(outputDexPath, { recursive: true }); 260 | } 261 | 262 | // begin to dump 263 | let dumper = new Dumper(agentRpc); 264 | await dumper.dump(outputDexPath, opts.deepSearch); 265 | } catch (e) { 266 | console.log(getErrorMessage(e).msg); 267 | return 3; 268 | } finally { 269 | if (script) { 270 | await script.unload(); 271 | script = null; 272 | } 273 | 274 | if (session) { 275 | await session.detach(); 276 | console.log("detched from device."); 277 | session = null; 278 | } 279 | } 280 | } catch (e) { 281 | console.log(e); 282 | return 3; 283 | } 284 | 285 | return 0; 286 | } 287 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // import { TextEncoder } from "util"; 2 | 3 | function so_test1(){ 4 | let modules = Process.enumerateModules(); 5 | for (const m of modules) { 6 | let exports = m.enumerateExports(); 7 | for (const exp of exports) { 8 | //console.log(JSON.stringify(exp)); 9 | if(exp.name.indexOf('onCreate') != -1){ 10 | console.log('so onCreate found :', JSON.stringify(exp)); 11 | } 12 | } 13 | } 14 | } 15 | 16 | function testHook1(){ 17 | let loadedCls = Java.enumerateLoadedClasses({ 18 | onMatch:function(name,handle){ 19 | if(name.indexOf('com.yssenlin') != -1){ 20 | console.log(name, handle); 21 | } 22 | }, 23 | onComplete:function(){ 24 | console.log('------- all class found! ----------'); 25 | } 26 | }); 27 | 28 | 29 | let mainActivity = Java.use('com.yssenlin.app.MainActivity'); 30 | let methods = mainActivity.class.getDeclaredMethods(); 31 | for (const iterator of methods) { 32 | console.log(iterator) 33 | } 34 | //console.log(JSON.stringify(mainActivity.$ownMembers,null,4)); 35 | 36 | //send(mainActivity.onCreate) 37 | } 38 | 39 | function hookTest2(){ 40 | 41 | let shufferMap = Java.use("com.xiaojianbang.app.ShufferMap"); 42 | shufferMap.show.implementation = function(m:any){ 43 | console.log(m); 44 | for(let k of m.keySet().toArray()){ 45 | console.log(k, '=', m.get(k)); 46 | } 47 | // // console.log(m, m.get("user"), JSON.stringify(m.class.getDeclaredMethods().map(d=>d.toString()),null,4)); 48 | // console.log("--------------------------" + new Date() + "--------------------------"); 49 | // console.log(JSON.stringify(m.keySet().class.getDeclaredMethods().map(d=>d.toString()),null,4)); 50 | m.put("user","user_string"); 51 | console.log(m); 52 | return this.show(m); 53 | } 54 | 55 | } 56 | 57 | function hookTest3(){ 58 | let rsa = Java.use("com.xiaojianbang.app.RSA"); 59 | let data = "hello world"; 60 | 61 | let dataBytes = new TextEncoder().encode(data); 62 | console.log(dataBytes) 63 | 64 | let encDataBytes = rsa.encrypt(dataBytes ); 65 | console.log(encDataBytes) 66 | } 67 | 68 | 69 | 70 | function main(){ 71 | console.log("--------------------------" + new Date() + "--------------------------"); 72 | Java.perform(()=>{ 73 | // testHook1(); 74 | hookTest2(); 75 | hookTest3(); 76 | }) 77 | } 78 | 79 | 80 | setImmediate(main); 81 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "src/**/*" 4 | ], 5 | "exclude": [ 6 | "agent/**/*.ts" 7 | ], 8 | "compilerOptions": { 9 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 10 | /* Projects */ 11 | // "incremental": true, /* Enable incremental compilation */ 12 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 13 | // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ 14 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ 15 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 16 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 17 | 18 | /* Language and Environment */ 19 | "target": "ES2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 20 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 21 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 22 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 23 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 24 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ 25 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 26 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ 27 | // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ 28 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 29 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 30 | 31 | /* Modules */ 32 | "module": "ESNext", /* Specify what module code is generated. */ 33 | // "rootDir": "./", /* Specify the root folder within your source files. */ 34 | "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 35 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 36 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 37 | "rootDirs": ["./src/**/","./dist/"], /* Allow multiple folders to be treated as one when resolving modules. */ 38 | // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ 39 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 40 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 41 | // "resolveJsonModule": true, /* Enable importing .json files */ 42 | // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ 43 | 44 | /* JavaScript Support */ 45 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ 46 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 47 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ 48 | 49 | /* Emit */ 50 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 51 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 52 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 53 | "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 54 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ 55 | "outDir": "./dist/", /* Specify an output folder for all emitted files. */ 56 | // "removeComments": true, /* Disable emitting comments. */ 57 | // "noEmit": true, /* Disable emitting files from a compilation. */ 58 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 59 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ 60 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 61 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 62 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 63 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 64 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 65 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 66 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 67 | // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ 68 | // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ 69 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 70 | // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ 71 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 72 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 73 | 74 | /* Interop Constraints */ 75 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 76 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 77 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ 78 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 79 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 80 | 81 | /* Type Checking */ 82 | "strict": true, /* Enable all strict type-checking options. */ 83 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ 84 | // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ 85 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 86 | // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ 87 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 88 | // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ 89 | // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ 90 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 91 | // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ 92 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ 93 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 94 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 95 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 96 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 97 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 98 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ 99 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 100 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 101 | 102 | /* Completeness */ 103 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 104 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 105 | } 106 | } 107 | --------------------------------------------------------------------------------