├── .gitmodules
├── DexClassLoader
├── Art_DexClassLoader.png
├── Art_InMemoryDexClassLoader.png
└── Dalvik_DexClassLoader.png
├── README.assets
├── image-20201009105052076.png
├── image-20201102192311913.png
├── image-20201102192324644.png
├── image-20210331.png
├── image-20210406.png
├── image-20210417.png
├── image-20210424-1.png
└── image-20210424-2.png
├── README.md
├── RegisterNatives
├── Art_RegisterNatives.png
└── Dalvik_RegisterNatives.png
├── dex2apk.py
├── frida_dump_backtrace.js
├── hookEnum.js
├── hook_Iterator.js
├── hook_RegisterNative.js
├── hook_classLoader.js
├── hook_constructors.js
├── hook_getContext.js
├── hook_init_array.js
├── hook_mod_init_function.js
├── imgs
├── demo.jpg
├── usage.jpg
├── usage_demo.jpg
└── zsxq.png
├── okio.dex
└── unicorn_so.py
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "DumpApkInfo"]
2 | path = DumpApkInfo
3 | url = https://github.com/Simp1er/DumpApkInfo
4 |
--------------------------------------------------------------------------------
/DexClassLoader/Art_DexClassLoader.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/DexClassLoader/Art_DexClassLoader.png
--------------------------------------------------------------------------------
/DexClassLoader/Art_InMemoryDexClassLoader.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/DexClassLoader/Art_InMemoryDexClassLoader.png
--------------------------------------------------------------------------------
/DexClassLoader/Dalvik_DexClassLoader.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/DexClassLoader/Dalvik_DexClassLoader.png
--------------------------------------------------------------------------------
/README.assets/image-20201009105052076.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/README.assets/image-20201009105052076.png
--------------------------------------------------------------------------------
/README.assets/image-20201102192311913.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/README.assets/image-20201102192311913.png
--------------------------------------------------------------------------------
/README.assets/image-20201102192324644.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/README.assets/image-20201102192324644.png
--------------------------------------------------------------------------------
/README.assets/image-20210331.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/README.assets/image-20210331.png
--------------------------------------------------------------------------------
/README.assets/image-20210406.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/README.assets/image-20210406.png
--------------------------------------------------------------------------------
/README.assets/image-20210417.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/README.assets/image-20210417.png
--------------------------------------------------------------------------------
/README.assets/image-20210424-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/README.assets/image-20210424-1.png
--------------------------------------------------------------------------------
/README.assets/image-20210424-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/README.assets/image-20210424-2.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | * [AndroidSec](#androidsec)
3 | * [0. 以后知识分享都在知识星球了](#0-以后知识分享都在知识星球了有兴趣的可以加下)
4 | * [1. Dalvik下DexClassLoader动态加载关键函数链(基于Android4.4)](#1-dalvik下dexclassloader动态加载关键函数链基于android44)
5 | * [2. Art下DexClassLoader动态加载关键函数链(基于Android8.0)](#2-art下dexclassloader动态加载关键函数链基于android80)
6 | * [3. Art下InMemoryDexClassLoader动态加载关键函数链(基于Android8.0)](#3-art下inmemorydexclassloader动态加载关键函数链基于android80)
7 | * [4. Dalvik下RegisterNatives动态注册关键函数链(基于Android4.4)](#4-dalvik下registernatives动态注册关键函数链基于android44)
8 | * [4. Art下RegisterNatives动态注册关键函数链(基于Android8.1)](#4-art下registernatives动态注册关键函数链基于android81)
9 | * [2020-07-08 新增dex2apk.py](#2020-07-08-新增dex2apkpy)
10 | * [2020-09-19 将自己之前改的的DumpApkInfo工具引入](#2020-09-19-将自己之前改的的dumpapkinfo工具引入)
11 | * [2020-10-09 增加unicorn trace arm64的基本代码](#2020-10-09-增加unicorn-trace-arm64的基本代码)
12 | * [2020-10-10 增加一个byte数组转hexString的dex](#2020-10-10-增加一个byte数组转hexstring的dex)
13 | * [2020-10-31 增加hook_init_array代码](#2020-10-31-增加hook_init_array代码)
14 | * [2020-11-02 增加hook_constructors代码](#2020-11-02-增加hook_constructors代码)
15 | * [2021-03-31 增加主动获取context方法](#2021-03-31-增加主动获取context方法)
16 | * [2021-04-06 增加hook RegisterNative函数的脚本](#2021-04-06-增加hook-registernative函数的脚本)
17 | * [2021-04-14 增加在native层遍历HashMap代码](#2021-04-14-增加在native层遍历hashmap代码)
18 | * [2021-04-17 get到一个新姿势:更改进程名](#2021-04-17-get到一个新姿势更改进程名)
19 | * [2021-04-24 又找到一个anti-frida的方式](#2021-04-24-又找到一个anti-frida的方式)
20 | * [2021-05-23 frida hook enum class 的方式](#2021-05-23-frida-hook-enum-class-的方式)
21 | * [2021-06-03 frida 无法hook上函数总结](#2021-06-03-frida-无法hook上函数总结)
22 | * [2021-07-28 将注册的类加入双亲委派链](#2021-07-28-将注册的类加入双亲委派链)
23 | * [2021-11-10 增加两个打印java调用栈的frida脚本](#2021-11-10-增加两个打印java调用栈的frida脚本)
24 | * [2023-09-11 增加frida hook mod_init_function的代码](#2023-09-11-增加frida-hook-mod_init_function的代码)
25 |
26 |
27 |
28 |
29 |
30 |
31 | # AndroidSec
32 |
33 | ## 0. 以后知识分享都在知识星球了,有兴趣的可以加下
34 |
35 | 
36 |
37 | ## 1. Dalvik下DexClassLoader动态加载关键函数链(基于Android4.4)
38 | 
39 | ## 2. Art下DexClassLoader动态加载关键函数链(基于Android8.0)
40 | 
41 | ## 3. Art下InMemoryDexClassLoader动态加载关键函数链(基于Android8.0)
42 | 
43 | ## 4. Dalvik下RegisterNatives动态注册关键函数链(基于Android4.4)
44 | 
45 | ## 4. Art下RegisterNatives动态注册关键函数链(基于Android8.1)
46 | 
47 |
48 |
49 | ## 2020-07-08 新增[dex2apk.py](dex2apk.py)
50 | 一个用于将脱壳下来的众多dex重新组装成apk的脚本,免去Android安全测试人员在逆向过程中不停`grep`的操作
51 |
52 | - usage: python dex2apk.py [-h] -a APK_PATH -i DEX_PATH [-o OUTPUT]
53 | 
54 |
55 | 
56 |
57 | 将重新组合的`apk`拖入`jadx`和`jeb`,展示结果如下:
58 |
59 | 
60 |
61 |
62 |
63 | ## 2020-09-19 将自己之前改的的DumpApkInfo工具引入
64 |
65 | DumpApkInfo可以用来dump加壳信息、签名信息、APK包名等等功能,具体参见子模块[readme](https://github.com/Simp1er/DumpApkInfo/blob/master/README.md)
66 |
67 |
68 |
69 | ## 2020-10-09 增加[unicorn trace arm64的基本代码](unicorn_so.py)
70 |
71 | 大概效果如下
72 |
73 | 
74 |
75 | 基于unicorn和capstone来trace函数执行流程并记录寄存器信息,具体自己看代码吧,只是一个demo
76 |
77 | ## 2020-10-10 增加一个[byte数组转hexString的dex](okio.dex)
78 |
79 | 手动封装了`okio.ByteString`的函数,并打包成dex,避免frida在hook APP时无法使用`ByteString`的转hex方法,frida使用方式
80 |
81 | 首先将dex push进`/data/local/tmp/`目录下,然后`chmod`给予`dex`执行权限,frida调用时
82 |
83 | ```js
84 | Java.perform(function (){
85 | var okio = Java.openClassFile("/data/local/tmp/okio.dex")
86 | okio.load()
87 | var ByteString = Java.use("com.Simp1er.okio.ByteString")
88 | ByteString.$new(key_bytes).hex()// 其实接下来就是ByteString的函数调用了
89 | })
90 |
91 | ```
92 |
93 | 参考: [ByteString.java](https://android.googlesource.com/platform/external/okhttp/+/3c938a3/okio/src/main/java/okio/ByteString.java)
94 |
95 | ## 2020-10-31 增加[hook_init_array代码](hook_init_array.js)
96 |
97 | 通过hook`linker`的`call_array`函数,hook得到`init_array`地址,在Android8.1 64位程序和32为程序上都测试成功,其他的请自己测试更改。
98 |
99 |
100 |
101 | ## 2020-11-02 增加[hook_constructors代码](hook_constructors.js)
102 |
103 | 通过hook`linker`的`async_safe_format_log`函数,hook得到`init_array`以及`.init_proc`地址,在Android8.1 64位程序和32为程序上都测试成功,其他的请自己测试更改。
104 | 64位效果如下:
105 | 
106 | 32位效果如下:
107 | 
108 |
109 |
110 | ## 2021-03-31 增加[主动获取context方法](hook_getContext.js)
111 |
112 | 利用`ActivityThread`的单例模式获取到应用的上下文`Context`用于后续利用
113 |
114 | 效果如下
115 | 
116 |
117 | ## 2021-04-06 增加[hook RegisterNative函数的脚本](hook_RegisterNative.js)
118 |
119 | 由于jni函数无论是动态注册或者静态注册的函数都会在加载过程中都会调用`RegisterNative`函数注册`JNI`函数最终函数地址,因此可以通过`hook` `RegisterNative`函数获取`JNI`函数最终地址以及函数实现所在模块,最终效果如下:
120 |
121 | 
122 |
123 | 注意:仅在`Android 8.1.0_r1`下测试成功,其他版本可能失效
124 |
125 | 20210730补充:更新由于可能存在匿名内存块的情况导致无法通过`ModuleMap`找到相应模块的情况处理
126 |
127 |
128 | ## 2021-04-14 增加[在native层遍历`HashMap`代码](hook_Iterator.js)
129 |
130 | 在`native`层遍历`HashMap`中`key`和`value`类型都为`String`的脚本
131 |
132 | 传入一个`JNIEnv`和`HashMap`的对象即可
133 | 最终效果如下
134 | ```
135 | xxx => azU7Bc002xAAJ9QEYW1mGJrO8f0Ed9QH0FqzYdOlL8/Md/QHpENnwiLJhCBZB5mhQtIXdU8Pnw43BRB7hD6QY3VDdZfUF9QH1BfUB9
136 | bbbb => HHnB_FCRa80QdwWegx+jn98jVfguVXqGwR3kh9ROtBHavXaYZV+qLX+lUnG4LVQfyqsJ/zFo0JH2gRVVSi98GPkuj9GWADR18oS+VyJ2XhLUQYev/wQDCMFWSAYaGABE2SOBT
137 | cccc => JAE5zHBA7W55NB1VcTLaT8wI/An8Ae8A+wn5Gvsa7wj6CPQN/Qv6CPg=
138 | dddd => hwIABwRLPF9s5QJ40ATaaW1cNymwhLCe
139 | ```
140 |
141 | ## 2021-04-17 get到一个新姿势:更改进程名
142 |
143 | `android.os.Process`中的`setArgV0()`函数可用于改变`APP`的进程名。感谢卓桐大佬!
144 |
145 |
146 | 注意:这里由于`Java.lang.Process`类是默认导入的包,因此在使用时需要单独`import`导入`android.os.Process`类。
147 | ```java
148 | import android.os.Process;
149 |
150 | try {
151 | // https://bbs.pediy.com/thread-253676.htm
152 | Method setArgV0 = Process.class.getDeclaredMethod("setArgV0", String.class);
153 | setArgV0.setAccessible(true);
154 | setArgV0.invoke(null,"com.tencent.mm");
155 |
156 | } catch (Throwable e) {
157 | // java.lang.NoSuchMethodException: setArgV0 [class java.lang.String]
158 | e.printStackTrace();
159 | Log.i("nicai", "onCreate: ");
160 | }
161 | ```
162 |
163 | 这里测试`APP`的包名为`com.test.changeprocessname`但是当使用`ps -e`命令查看进程会发现找不到这个进程,最终效果如下
164 |
165 | 
166 |
167 | ## 2021-04-24 又找到一个anti-frida的方式
168 |
169 | frida在注入进程后如果脚本中使用`registerClass`这个`API`,在内存空间中会出现`frida`为前缀的`vdex/odex`,即使`frida`退出后,本次进程的内存空间仍旧还存在这样的痕迹。
170 |
171 | 展示如下图
172 |
173 | 
174 |
175 |
176 | `anti-anti`的方式是在调用`registerClass`这个`API`之前执行以下语句
177 |
178 | ```js
179 | Java.classFactory.tempFileNaming.prefix = "onetwothree"
180 | ..
181 | Java.registerClass(...)
182 | ```
183 |
184 | 自定义后效果
185 |
186 | 
187 |
188 | ## 2021-05-23 frida hook enum class 的方式
189 |
190 | [hookEnum.js](hookEnum.js)
191 |
192 | 当`hook` `Enum`类时,`this`指针就是`Enum`类中具体成员的值。
193 |
194 | 比如我这里写了一个枚举类,代码如下
195 | ```java
196 | public enum enumClazz {
197 | OAID("QAID"),TOKEN("TOKEN"), VIP("1");
198 |
199 | private String value;
200 | enumClazz(String value){
201 | this.value = value;
202 | }
203 |
204 | public String getValue(){
205 | return this.value;
206 | }
207 | public void setValue(String value){
208 | this.value = value;
209 | Log.e("enum", "className: " + this );
210 | }
211 | }
212 | ```
213 |
214 | 在调用时使用方法像这样
215 | ```java
216 | enumClazz.OAID.setValue("OAID_value");
217 | enumClazz.TOKEN.setValue("TOKEN_value");
218 | enumClazz.VIP.setValue("VIP_value");
219 | ```
220 | 此时调用`setValue(String)`函数打印出来的日志如下
221 | ```bash
222 | 2021-05-23 16:24:31.258 26207-26207/com.simp1er.enumdemo E/enum: className: OAID
223 | 2021-05-23 16:24:31.262 26207-26207/com.simp1er.enumdemo E/enum: className: TOKEN
224 | 2021-05-23 16:24:31.265 26207-26207/com.simp1er.enumdemo E/enum: className: VIP
225 | ```
226 |
227 | 可以发现其实对应的`this`指针就是对应调用的枚举成员。
228 |
229 | 最终使用`frida`进行`hook`时,主要代码如下
230 |
231 | ```js
232 | function hook(){
233 | Java.perform(function(){
234 | var enumClazz = Java.use('com.simp1er.enumdemo.enumClazz')
235 | enumClazz.setValue.implementation = function(){
236 | var value = arguments[0];
237 | console.log('class =>',this,", value =>",value) // this即代表enum类的对象名称。
238 | // console.log('class =>',this.getString())
239 | return this.setValue(value)
240 | }
241 | });
242 | }
243 | ```
244 | 效果如下
245 | ```bash
246 | class => OAID , value => OAID_value
247 | class => TOKEN , value => TOKEN_value
248 | class => VIP , value => VIP_value
249 | ```
250 |
251 | 但是参考中的`this.getString()`方法并没有成功。
252 |
253 |
254 |
255 | 参考:[https://bbs.pediy.com/thread-258772.htm#enum%E6%B5%8B%E8%AF%95](https://bbs.pediy.com/thread-258772.htm#enum%E6%B5%8B%E8%AF%95)
256 |
257 | ## 2021-06-03 frida 无法hook上函数总结
258 |
259 |
260 | 如果函数hook不到,
261 | - 首先,想想这个函数是不是真的执行到了;
262 | - 其次,想想函数所在类是不是接口类、是不是抽象类(检查方法可以通过Objection-WallBreaker搜索是否有对应实例或者直接看官方文档,如果是抽象类要找对应继承类中是否有对应函数的实现、如果是接口类找对应implements)
263 | - 再次,可以试试`Java.deoptimizeEverything()`函数
264 |
265 | -- 记一次hook不到有感
266 |
267 | [Hooking to Context doesn't work](https://github.com/frida/frida-java-bridge/issues/67)
268 |
269 | [Context类](https://developer.android.com/reference/android/content/Context)
270 |
271 |
272 | ## 2021-07-28 将注册的类加入双亲委派链
273 |
274 | [hook代码](hook_classLoader.js)
275 |
276 | 将通过`Java.registerClass() API`注册的类所在的`dexclassLoader`加入系统`classLoader`双亲委派链中,保证能够`Class.forName`函数调用注册的函数时能够成功。
277 |
278 |
279 | ## 2021-11-10 增加两个打印java调用栈的frida脚本
280 |
281 | [hook代码](frida_dump_backtrace.js)
282 |
283 | 参考今天早上看到的一个[repo](https://github.com/ChenJunsen/Hegui3.0#%E6%89%93%E5%8D%B0%E6%96%B9%E6%B3%95%E8%B0%83%E7%94%A8%E6%A0%88)中`Xposed`打印调用栈的方式,写出对应的`frida`脚本。
284 |
285 |
286 | ## 2023-09-11 增加frida hook mod_init_function的代码
287 |
288 | [hook代码](hook_mod_init_function.js)
289 |
290 |
291 | ## Stargazers over time
292 |
293 | [](https://starchart.cc/Simp1er/AndroidSec)
294 |
295 |
296 |
297 |
--------------------------------------------------------------------------------
/RegisterNatives/Art_RegisterNatives.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/RegisterNatives/Art_RegisterNatives.png
--------------------------------------------------------------------------------
/RegisterNatives/Dalvik_RegisterNatives.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/RegisterNatives/Dalvik_RegisterNatives.png
--------------------------------------------------------------------------------
/dex2apk.py:
--------------------------------------------------------------------------------
1 | import os
2 | import zipfile
3 | import argparse
4 |
5 |
6 | def rename_class(path):
7 | files = os.listdir(path)
8 | dex_index = 0
9 | if path.endswith('/'):
10 | path = path[:-1]
11 | print(path)
12 | for i in range(len(files)):
13 | if files[i].endswith('.dex'):
14 | old_name = path + '/' + files[i]
15 | if dex_index == 0:
16 | new_name = path + '/' + 'classes.dex'
17 | else:
18 | new_name = path + '/' + 'classes%d.dex' % dex_index
19 | dex_index += 1
20 | if os.path.exists(new_name):
21 | continue
22 | os.rename(old_name, new_name)
23 | print('[*] 重命名完毕')
24 |
25 |
26 | def extract_META_INF_from_apk(apk_path, target_path):
27 | r = zipfile.is_zipfile(apk_path)
28 | if r:
29 | fz = zipfile.ZipFile(apk_path, 'r')
30 | for file in fz.namelist():
31 | if file.startswith('META-INF'):
32 | fz.extract(file, target_path)
33 | else:
34 | print('[-] %s 不是一个APK文件' % apk_path)
35 |
36 |
37 | def zip_dir(dirname, zipfilename):
38 | filelist = []
39 | if os.path.isfile(dirname):
40 | if dirname.endswith('.dex'):
41 | filelist.append(dirname)
42 | else:
43 | for root, dirs, files in os.walk(dirname):
44 | for dir in dirs:
45 | # if dir == 'META-INF':
46 | # print('dir:', os.path.join(root, dir))
47 | filelist.append(os.path.join(root, dir))
48 | for name in files:
49 | # print('file:', os.path.join(root, name))
50 |
51 | filelist.append(os.path.join(root, name))
52 |
53 | z = zipfile.ZipFile(zipfilename, 'w', zipfile.ZIP_DEFLATED)
54 | for tar in filelist:
55 | arcname = tar[len(dirname):]
56 |
57 | if ('META-INF' in arcname or arcname.endswith('.dex')) and '.DS_Store' not in arcname:
58 | # print(tar + " -->rar: " + arcname)
59 | z.write(tar, arcname)
60 | print('[*] APK打包成功,你可以拖入APK进行分析啦!' )
61 | z.close()
62 |
63 |
64 | def parse_args():
65 | parser = argparse.ArgumentParser(description='repackage dumped dex to apk for jeb/jadx analysis.')
66 | parser.add_argument('-a', '--apk_path', required=True, type=str,
67 | help='apk for extracting META-INF')
68 | parser.add_argument('-i', '--dex_path', required=True, type=str,
69 | help='path of dumped dex')
70 | parser.add_argument('-o', '--output', type=str, default="repacked.apk",
71 | help='apk path after zip')
72 | args = parser.parse_args()
73 | #print(args.apk_path)
74 | return args
75 |
76 |
77 | if __name__ == '__main__':
78 | args = parse_args()
79 | rename_class(args.dex_path)
80 | extract_META_INF_from_apk(args.apk_path, args.dex_path)
81 | # zip_file(args.dex_path)
82 | zip_dir(args.dex_path, args.output)
83 |
--------------------------------------------------------------------------------
/frida_dump_backtrace.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | refer:
4 | private static void dump() {
5 | for (Map.Entry stackTrace : Thread.getAllStackTraces().entrySet()) {
6 | Thread thread = (Thread) stackTrace.getKey();
7 | StackTraceElement[] stack = (StackTraceElement[]) stackTrace.getValue();
8 | // 进行过滤
9 | if (thread.equals(Thread.currentThread())) {
10 | continue;
11 | }
12 | XposedBridge.log("[Dump Stack]" + "**********线程名字:" + thread.getName() + "**********");
13 | int index = 0;
14 | for (StackTraceElement stackTraceElement : stack) {
15 | XposedBridge.log("[Dump Stack]" + index + ": " + stackTraceElement.getClassName()
16 | + "----" + stackTraceElement.getFileName()
17 | + "----" + stackTraceElement.getLineNumber()
18 | + "----" + stackTraceElement.getMethodName());
19 | }
20 | // 增加序列号
21 | index++;
22 | }
23 | XposedBridge.log("[Dump Stack]" + "********************* over **********************");
24 | }
25 | */
26 | function dumpMutiBackTrace() {
27 | Java.perform(function () {
28 | var Thread = Java.use('java.lang.Thread');
29 | var thread_stack_map = Thread.getAllStackTraces();
30 | const mapClass = Java.use("java.util.Map");
31 | const entryClass = Java.use("java.util.Map$Entry");
32 |
33 | const entrySet = mapClass.entrySet.apply(thread_stack_map).iterator();
34 | while (entrySet.hasNext()) {
35 | const entry = Java.cast(entrySet.next(), entryClass);
36 | const thread = Java.cast(entry.getKey(), Thread);
37 |
38 |
39 | const StackTraceElements = entry.getValue();
40 |
41 | // refer: https://bbs.pediy.com/thread-268335.htm
42 | var ArrayClz = Java.use("java.lang.reflect.Array");
43 | var len = ArrayClz.getLength(StackTraceElements);
44 | if (len > 0) {
45 | console.log("\n[Dump Stack]=============================" + thread.getName() + " Stack start=======================");
46 | for (let i = 0; i < len; i++) {
47 | console.log("\t" + Java.cast(ArrayClz.get(StackTraceElements, i), Java.use('java.lang.StackTraceElement')));
48 | }
49 | console.log(("[Dump Stack]=============================" + thread.getName() + " Stack end=======================\r\n"));
50 | }
51 |
52 | }
53 |
54 | });
55 | }
56 | function dumpSingleStackTrace() {
57 | Java.perform(function () {
58 | var Exception = Java.use("java.lang.Exception");
59 | var ins = Exception.$new("Exception");
60 | var Thread = Java.use('java.lang.Thread');
61 | var straces = ins.getStackTrace();
62 | if (undefined == straces || null == straces) {
63 | return;
64 | }
65 |
66 | console.log("[Dump Stack]=============================" + Thread.currentThread().getName() + " Stack start=======================");
67 | console.log("");
68 | for (var i = 0; i < straces.length; i++) {
69 | var str = " " + straces[i].toString();
70 | console.log(str);
71 | }
72 | console.log("");
73 | console.log("[Dump Stack]=============================" + Thread.currentThread().getName() + " Stack end=======================\r\n");
74 | Exception.$dispose();
75 |
76 | });
77 | }
78 |
79 |
80 | // function main() {
81 | // // hook_java();
82 | // }
83 | // setImmediate(main)
84 |
--------------------------------------------------------------------------------
/hookEnum.js:
--------------------------------------------------------------------------------
1 | /**
2 | * public enum enumClazz {
3 | OAID("QAID"),TOKEN("TOKEN"), VIP("1");
4 |
5 | private String value;
6 | enumClazz(String value){
7 | this.value = value;
8 | }
9 |
10 | public String getValue(){
11 | return this.value;
12 | }
13 | public void setValue(String value){
14 | this.value = value;
15 | // 2021-05-23 16:24:31.258 26207-26207/com.simp1er.enumdemo E/enum: className: OAID
16 | // 2021-05-23 16:24:31.262 26207-26207/com.simp1er.enumdemo E/enum: className: TOKEN
17 | // 2021-05-23 16:24:31.265 26207-26207/com.simp1er.enumdemo E/enum: className: VIP
18 | // 2021-05-23 16:24:31.419 26207-26207/com.simp1er.enumdemo E/enum: className: OAID
19 | // 2021-05-23 16:24:31.423 26207-26207/com.simp1er.enumdemo E/enum: className: TOKEN
20 | // 2021-05-23 16:24:31.426 26207-26207/com.simp1er.enumdemo E/enum: className: VIP
21 | Log.e("enum", "className: " + this );
22 | }
23 | }
24 | */
25 | function hook(){
26 | Java.perform(function(){
27 | var enumClazz = Java.use('com.simp1er.enumdemo.enumClazz')
28 | enumClazz.setValue.implementation = function(){
29 | var value = arguments[0];
30 | /**
31 | * class => OAID , value => OAID_value
32 | class => TOKEN , value => TOKEN_value
33 | class => VIP , value => VIP_value
34 | class => OAID , value => OAID_value
35 | class => TOKEN , value => TOKEN_value
36 | class => VIP , value => VIP_value
37 | class => OAID , value => OAID_value
38 | class => TOKEN , value => TOKEN_value
39 | class => VIP , value => VIP_value
40 | class => OAID , value => OAID_value
41 | class => TOKEN , value => TOKEN_value
42 | class => VIP , value => VIP_value
43 | */
44 | console.log('class =>',this,", value =>",value) // this即代表enum类的对象名称。
45 | // console.log('class =>',this.getString())
46 | return this.setValue(value)
47 | }
48 | });
49 | }
50 |
51 | function main(){
52 | hook()
53 | }
54 |
55 |
56 | setImmediate(main)
--------------------------------------------------------------------------------
/hook_Iterator.js:
--------------------------------------------------------------------------------
1 | function IterateSet(env,hasMapObj){
2 | var MapClazz = env.findClass('java/util/HashMap')
3 | if(env.isInstanceOf(hasMapObj,MapClazz)){
4 | // HashMap.get(Object)
5 | var GetMID = env.getMethodId(MapClazz,'get','(Ljava/lang/Object;)Ljava/lang/Object;')
6 | var InvokeGet = env.vaMethod('pointer',['pointer'])
7 | var keySetMID = env.getMethodId(MapClazz,'keySet','()Ljava/util/Set;')
8 | var InvokeKeySet = env.vaMethod('pointer',[])
9 | var setObj = InvokeKeySet(env.handle,retval,keySetMID)
10 |
11 | var SetClazz = env.findClass('java/util/Set')
12 | if(env.isInstanceOf(setObj,SetClazz)){
13 | var iteratorMID = env.getMethodId(SetClazz,'iterator','()Ljava/util/Iterator;')
14 | var Invokeiterator = env.vaMethod('pointer',[])
15 | var IterObj = Invokeiterator(env.handle,setObj,iteratorMID)
16 | var IteratorClazz = env.findClass('java/util/Iterator')
17 | if(env.isInstanceOf(IterObj,IteratorClazz)){
18 | var hasNextMID = env.getMethodId(IteratorClazz,'hasNext','()Z')
19 | var InvokehasNext = env.vaMethod('uint8',[])
20 | var nextMID = env.getMethodId(IteratorClazz,'next','()Ljava/lang/Object;')
21 | var InvokeNext = env.vaMethod('pointer',[])
22 | while(InvokehasNext(env.handle,IterObj,hasNextMID)){
23 | var keyObj = InvokeNext(env.handle,IterObj,nextMID)
24 | var key = env.getStringUtfChars(keyObj).readCString()
25 |
26 | var valueObj = InvokeGet(env.handle,retval,GetMID,keyObj)
27 | var value = env.getStringUtfChars(valueObj).readCString()
28 | console.log(key,'=>',value)
29 | }
30 | }
31 | }
32 |
33 | }
34 | }
35 | /*
36 | xxx => azU7Bc002xAAJ9QEYW1mGJrO8f0Ed9QH0FqzYdOlL8/Md/QHpENnwiLJhCBZB5mhQtIXdU8Pnw43BRB7hD6QY3VDdZfUF9QH1BfUB9
37 | bbbb => HHnB_FCRa80QdwWegx+jn98jVfguVXqGwR3kh9ROtBHavXaYZV+qLX+lUnG4LVQfyqsJ/zFo0JH2gRVVSi98GPkuj9GWADR18oS+VyJ2XhLUQYev/wQDCMFWSAYaGABE2SOBT
38 | cccc => JAE5zHBA7W55NB1VcTLaT8wI/An8Ae8A+wn5Gvsa7wj6CPQN/Qv6CPg=
39 | dddd => hwIABwRLPF9s5QJ40ATaaW1cNymwhLCe
40 | */
41 |
42 |
--------------------------------------------------------------------------------
/hook_RegisterNative.js:
--------------------------------------------------------------------------------
1 | /*
2 | 仅在Android 8.1下测试成功,其他版本可能需要重新修改适配
3 | */
4 |
5 | const STD_STRING_SIZE = 3 * Process.pointerSize;
6 | class StdString {
7 | constructor() {
8 | this.handle = Memory.alloc(STD_STRING_SIZE);
9 | }
10 |
11 | dispose() {
12 | const [data, isTiny] = this._getData();
13 | if (!isTiny) {
14 | Java.api.$delete(data);
15 | }
16 | }
17 |
18 | disposeToString() {
19 | const result = this.toString();
20 | this.dispose();
21 | return result;
22 | }
23 |
24 | toString() {
25 | const [data] = this._getData();
26 | return data.readUtf8String();
27 | }
28 |
29 | _getData() {
30 | const str = this.handle;
31 | const isTiny = (str.readU8() & 1) === 0;
32 | const data = isTiny ? str.add(1) : str.add(2 * Process.pointerSize).readPointer();
33 | return [data, isTiny];
34 | }
35 | }
36 |
37 | function prettyMethod(method_id, withSignature) {
38 | const result = new StdString();
39 | Java.api['art::ArtMethod::PrettyMethod'](result, method_id, withSignature ? 1 : 0);
40 | return result.disposeToString();
41 | }
42 | function attach(addr) {
43 | Interceptor.attach(addr, {
44 | onEnter: function (args) {
45 | this.arg0 = args[0]
46 | },
47 | onLeave: function (retval) {
48 | var modulemap = new ModuleMap()
49 | modulemap.update()
50 | var module = modulemap.find(retval)
51 | var string = Memory.alloc(0x100)
52 | if (module != null) {
53 | console.log('<' + module.name + '> method_name =>',prettyMethod(this.arg0,1), ',offset=>', ptr(retval).sub(module.base), ',module_name=>', module.name)
54 | }else{
55 | console.log(' method_name =>', readStdString(string), ', addr =>', ptr(retval))
56 | }
57 | }
58 | });
59 | }
60 |
61 | function hook_RegisterNative() {
62 | var libart = Process.findModuleByName('libart.so')
63 | var symbols = libart.enumerateSymbols()
64 | for (var i = 0; i < symbols.length; i++) {
65 | if (symbols[i].name.indexOf('RegisterNative') > -1 && symbols[i].name.indexOf('ArtMethod') > -1 && symbols[i].name.indexOf('RuntimeCallbacks') < 0) {
66 | attach(symbols[i].address)
67 | }
68 |
69 | }
70 |
71 | }
72 | function main() {
73 | hook_RegisterNative()
74 | }
75 | setImmediate(main)
76 |
--------------------------------------------------------------------------------
/hook_classLoader.js:
--------------------------------------------------------------------------------
1 |
2 | function hook() {
3 | Java.perform(function () {
4 | // var Six = Java.use('com.dta.test.frida.activity.SixthActivity')
5 | var target = Java.registerClass({
6 | name: 'com.dta.test.frida.activity.RegisterClass',
7 | // implements: [Six],
8 | // superClass: Java.use('java.lang.Object'),
9 | methods: {
10 | $init: function () {
11 | console.log('Constructor called');
12 | },
13 | next: {
14 | returnType: 'java.lang.Boolean',
15 | // returnType: 'java.lang.Boolean',
16 | // argumentTypes: [],
17 | argumentTypes: [],
18 | implementation: function () {
19 | console.log('call next')
20 | return Java.use('java.lang.Boolean').$new('true');
21 | // return Java.use('java.lang.Object').$new('true');
22 | // return true;
23 | },
24 | },
25 | }
26 | })
27 | var Six = Java.use('com.dta.test.frida.activity.SixthActivity')
28 | var pathLoader = Six.class.getClassLoader()
29 | var dexLoader = target.class.getClassLoader()
30 | console.log('activtity loader->',pathLoader)
31 | var parent = pathLoader.parent.value
32 | pathLoader.parent.value = dexLoader
33 | dexLoader.parent.value = parent
34 |
35 |
36 | })
37 |
38 | }
39 | function main() {
40 | hook()
41 | }
42 |
43 | setImmediate(main)
--------------------------------------------------------------------------------
/hook_constructors.js:
--------------------------------------------------------------------------------
1 | function hook_constructor() {
2 | if (Process.pointerSize == 4) {
3 | var linker = Process.findModuleByName("linker");
4 | } else {
5 | var linker = Process.findModuleByName("linker64");
6 | }
7 |
8 | var addr_call_function =null;
9 | var addr_g_ld_debug_verbosity = null;
10 | var addr_async_safe_format_log = null;
11 | if (linker) {
12 | var symbols = linker.enumerateSymbols();
13 | for (var i = 0; i < symbols.length; i++) {
14 | var name = symbols[i].name;
15 | if (name.indexOf("call_function") >= 0){
16 | addr_call_function = symbols[i].address;
17 | }
18 | else if(name.indexOf("g_ld_debug_verbosity") >=0){
19 | addr_g_ld_debug_verbosity = symbols[i].address;
20 |
21 | ptr(addr_g_ld_debug_verbosity).writeInt(2);
22 |
23 | } else if(name.indexOf("async_safe_format_log") >=0 && name.indexOf('va_list') < 0){
24 |
25 | addr_async_safe_format_log = symbols[i].address;
26 |
27 | }
28 |
29 | }
30 | }
31 | if(addr_async_safe_format_log){
32 | Interceptor.attach(addr_async_safe_format_log,{
33 | onEnter: function(args){
34 | this.log_level = args[0];
35 | this.tag = ptr(args[1]).readCString()
36 | this.fmt = ptr(args[2]).readCString()
37 | if(this.fmt.indexOf("c-tor") >= 0 && this.fmt.indexOf('Done') < 0){
38 | this.function_type = ptr(args[3]).readCString(), // func_type
39 | this.so_path = ptr(args[5]).readCString();
40 | var strs = new Array(); //定义一数组
41 | strs = this.so_path.split("/"); //字符分割
42 | this.so_name = strs.pop();
43 | this.func_offset = ptr(args[4]).sub(Module.findBaseAddress(this.so_name))
44 | console.log("func_type:", this.function_type,
45 | '\nso_name:',this.so_name,
46 | '\nso_path:',this.so_path,
47 | '\nfunc_offset:',this.func_offset
48 | );
49 | }
50 | },
51 | onLeave: function(retval){
52 |
53 | }
54 | })
55 | }
56 | }
57 | function main() {
58 | hook_constructor();
59 | }
60 | setImmediate(main);
--------------------------------------------------------------------------------
/hook_getContext.js:
--------------------------------------------------------------------------------
1 | function getApplicationContext() {
2 |
3 | /*
4 | ref : https://blog.csdn.net/u010314594/article/details/49101629
5 | Class> ActivityThread = Class.forName("android.app.ActivityThread");
6 | Method method = ActivityThread.getMethod("currentActivityThread");
7 | Object currentActivityThread = method.invoke(ActivityThread);//获取currentActivityThread 对象
8 |
9 | Method method2 = currentActivityThread.getClass().getMethod("getApplication");
10 | CONTEXT_INSTANCE =(Context)method2.invoke(currentActivityThread);//获取 Context对象
11 | */
12 | Java.perform(function () {
13 | var ActivityThread = Java.use('android.app.ActivityThread')
14 | var currentActivityThread = null
15 | Java.choose('android.app.ActivityThread', {
16 | onMatch: function (ins) {
17 | console.log('found instance', ins)
18 | currentActivityThread = ins
19 | }, onComplete: function () {
20 |
21 | }
22 | })
23 | // var currentActivityThread = ActivityThread.currentActivityThread()
24 | // var currentActivityThread_obj = Java.cast(currentActivityThread,ActivityThread)
25 | var application = currentActivityThread.getApplication()
26 | // var Context = Java.use('android.content.Context')
27 | // var context_obj = Java.cast(application,Context)
28 | console.log('context is ',application)
29 | return application;
30 | })
31 | }
--------------------------------------------------------------------------------
/hook_init_array.js:
--------------------------------------------------------------------------------
1 | function hook_init_array() {
2 | //console.log("hook_constructor",Process.pointerSize);
3 | if (Process.pointerSize == 4) {
4 | var linker = Process.findModuleByName("linker");
5 | }else if (Process.pointerSize == 8) {
6 | var linker = Process.findModuleByName("linker64");
7 |
8 | }
9 |
10 |
11 | var addr_call_array = null;
12 | if (linker) {
13 | var symbols = linker.enumerateSymbols();
14 | for (var i = 0; i < symbols.length; i++) {
15 | var name = symbols[i].name;
16 | if (name.indexOf("call_array") >= 0) {
17 | addr_call_array = symbols[i].address;
18 | }
19 | }
20 | }
21 | if (addr_call_array) {
22 | Interceptor.attach(addr_call_array, {
23 | onEnter: function (args) {
24 |
25 | this.type = ptr(args[0]).readCString();
26 | //console.log(this.type,args[1],args[2],args[3])
27 | if (this.type == "DT_INIT_ARRAY") {
28 | this.count = args[2];
29 | //this.addrArray = new Array(this.count);
30 | this.path = ptr(args[3]).readCString();
31 | var strs = new Array(); //定义一数组
32 | strs = this.path.split("/"); //字符分割
33 | this.filename = strs.pop();
34 | if(this.count > 0){
35 | console.log("path : ", this.path);
36 | console.log("filename : ", this.filename);
37 | }
38 | for (var i = 0; i < this.count; i++) {
39 | console.log("offset : init_array["+i+"] = ", ptr(args[1]).add(Process.pointerSize*i).readPointer().sub(Module.findBaseAddress(this.filename)));
40 | //插入hook init_array代码
41 | }
42 | }
43 | },
44 | onLeave: function (retval) {
45 |
46 | }
47 | });
48 | }
49 | }
50 |
51 | function main() {
52 | if (Java.androidVersion == "8.1.0") {
53 | hook_init_array();
54 | }
55 |
56 | }
57 |
58 |
59 | setImmediate(main);
--------------------------------------------------------------------------------
/hook_mod_init_function.js:
--------------------------------------------------------------------------------
1 | // https://www.romainthomas.fr/post/21-07-pokemongo-anti-frida-jailbreak-bypass/
2 | function hook_mod_init_func(addr,targetModule){
3 | Interceptor.attach(addr,{
4 | onEnter: function(){
5 | var debugSymbol = DebugSymbol.fromAddress(this.context.x1)
6 | if(debugSymbol.moduleName == targetModule){
7 | Interceptor.attach(debugSymbol.address,{
8 | onEnter: function(){
9 | // hook_msHookFunction()
10 | },
11 | onLeave: function(){
12 |
13 | }
14 | })
15 |
16 | }
17 |
18 | },onLeave: function(){
19 |
20 | }
21 | })
22 | }
23 | function findSymbolsAndHook(targetModule){
24 | // frida hook dyld
25 | let dyld = Process.getModuleByName('dyld');
26 | if(dyld){
27 | let symbols = dyld.enumerateSymbols()
28 | if(symbols){
29 | symbols.forEach((symbol) => {
30 | if (symbol.name.indexOf('ImageLoader') >= 0 && symbol.name.indexOf('containsAddress') >= 0){
31 | console.log(`symbol name = ${symbol.name}`)
32 | hook_mod_init_func(symbol.address,targetModule)
33 | }
34 | })
35 | }
36 |
37 | }
38 |
39 | }
40 | function main(){
41 | findSymbolsAndHook("test") // test 替换为自己想要hook的模块名即可。
42 | }
43 | setImmediate(main)
--------------------------------------------------------------------------------
/imgs/demo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/imgs/demo.jpg
--------------------------------------------------------------------------------
/imgs/usage.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/imgs/usage.jpg
--------------------------------------------------------------------------------
/imgs/usage_demo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/imgs/usage_demo.jpg
--------------------------------------------------------------------------------
/imgs/zsxq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/imgs/zsxq.png
--------------------------------------------------------------------------------
/okio.dex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Simp1er/MobileSec/c63f33c85e0c1cc6105187d09e76df6d042d110e/okio.dex
--------------------------------------------------------------------------------
/unicorn_so.py:
--------------------------------------------------------------------------------
1 | from unicorn import *
2 | from unicorn.arm64_const import *
3 | import binascii
4 | from capstone import *
5 | from capstone.arm64 import *
6 |
7 |
8 | md = Cs(CS_ARCH_ARM64, CS_MODE_ARM)
9 | md.detail = True
10 | reg_names = {
11 | UC_ARM64_REG_X0: "X0",
12 | UC_ARM64_REG_X1: "X1",
13 | UC_ARM64_REG_X2: "X2",
14 | UC_ARM64_REG_X3: "X3",
15 | UC_ARM64_REG_X4: "X4",
16 | UC_ARM64_REG_X5: "X5",
17 | UC_ARM64_REG_X6: "X6",
18 | UC_ARM64_REG_X7: "X7",
19 | UC_ARM64_REG_X8: "X8",
20 | UC_ARM64_REG_X9: "X9",
21 | UC_ARM64_REG_X10: "X10",
22 | UC_ARM64_REG_X11: "X11",
23 | UC_ARM64_REG_X12: "X12",
24 | UC_ARM64_REG_X13: "X13",
25 | UC_ARM64_REG_X14: "X14",
26 | UC_ARM64_REG_X15: "X15",
27 | UC_ARM64_REG_X16: "X16",
28 | UC_ARM64_REG_X17: "X17",
29 | UC_ARM64_REG_X18: "X18",
30 | UC_ARM64_REG_X19: "X19",
31 | UC_ARM64_REG_X20: "X20",
32 | UC_ARM64_REG_X21: "X21",
33 | UC_ARM64_REG_X22: "X22",
34 | UC_ARM64_REG_X23: "X23",
35 | UC_ARM64_REG_X24: "X24",
36 | UC_ARM64_REG_X25: "X25",
37 | UC_ARM64_REG_X26: "X26",
38 | UC_ARM64_REG_X27: "X27",
39 | UC_ARM64_REG_X28: "X28"
40 | }
41 | reg_status = None
42 |
43 | def get_reg_info(uc : Uc):
44 | global reg_status
45 | retval = ""
46 | if reg_status is None:
47 | reg_status = {}
48 |
49 | for reg_name in range(UC_ARM64_REG_X0,UC_ARM64_REG_X28+1):
50 | reg_status[reg_names[reg_name]] = uc.reg_read(reg_name)
51 | retval += ("%s=0x%08X " %(reg_names[reg_name],reg_status[reg_names[reg_name]]))
52 |
53 | else:
54 | for reg_name in range(UC_ARM64_REG_X0,UC_ARM64_REG_X28+1):
55 | if uc.reg_read(reg_name) != reg_status[reg_names[reg_name]]:
56 | reg_status[reg_names[reg_name]] = uc.reg_read(reg_name)
57 | retval += ("%s=0x%08X " %(reg_names[reg_name],reg_status[reg_names[reg_name]]))
58 | return retval
59 |
60 |
61 |
62 |
63 | def hook_code(uc: Uc, address, size, user_data):
64 | reg_info = get_reg_info(uc)
65 | #pass
66 | user_data = uc.mem_read(address, size)
67 | for insn in md.disasm(user_data, size):
68 | print(reg_info)
69 | print("0x%08X:\t%s\t%s\t" % (address, insn.mnemonic, insn.op_str),end=" ")
70 |
71 |
72 |
73 |
74 |
75 |
76 | if __name__ == "__main__":
77 | mu = Uc(UC_ARCH_ARM64, UC_MODE_ARM)
78 |
79 | # libnative-lib.so_0x7f8a1d6000_0x3000
80 | so_addr = 0x7f8a1d6000
81 | so_size = 0x3000 * 4
82 |
83 | mu.mem_map(so_addr, so_size)
84 | # stack
85 | stack_addr = so_addr + so_size
86 | stack_size = 0x3000 * 4
87 | stack_top = stack_addr + stack_size - 4 # 减4防止覆盖param
88 |
89 | mu.mem_map(stack_addr, stack_size)
90 |
91 | # param
92 | param_addr = stack_addr + stack_size
93 | param_size = 0x3000 * 4
94 |
95 | mu.mem_map(param_addr, param_size)
96 |
97 | with open('libnative-lib.so_0x7f8a1d6000_0x3000.so', 'rb') as f:
98 | so_data = f.read()
99 |
100 | mu.mem_write(so_addr, so_data)
101 |
102 | mu.reg_write(UC_ARM64_REG_SP, stack_addr)
103 |
104 | input_bytes = b'wcAS3LiZggUmvAxmEZMsHaL1sQpf8IJx8U01'
105 | mu.mem_write(param_addr, input_bytes)
106 |
107 | mu.reg_write(UC_ARM64_REG_X0, param_addr)
108 | mu.reg_write(UC_ARM64_REG_X1, len(input_bytes))
109 |
110 | func_start = so_addr + 0x00006DC
111 | func_end = so_addr + 0x00000DB0
112 |
113 | mu.hook_add(UC_HOOK_CODE, hook_code, begin=func_start, end=func_end)
114 | # 模拟开始
115 | mu.emu_start(func_start, func_end)
116 |
117 | result = mu.mem_read(param_addr, len(input)).decode()
118 | print("input: %s,output: %s" % (input, result))
119 |
120 | pass
121 |
--------------------------------------------------------------------------------