├── HookTest.apk
├── LICENSE
├── README.md
├── README_EN.md
├── hook
├── FieldHook.kt
├── HookInit.kt
├── MainHook.kt
├── Tip.kt
├── extension
│ ├── ADBHook.kt
│ ├── AESHook.kt
│ ├── ApplicationHook.kt
│ ├── Base64Hook.kt
│ ├── BaseHook.kt
│ ├── ClickEventHook.kt
│ ├── ClipboardHook.kt
│ ├── ContactHook.kt
│ ├── DialogHook.kt
│ ├── ExitHook.kt
│ ├── FileHook.kt
│ ├── HMACHook.kt
│ ├── HotFixHook.kt
│ ├── IntentHook.kt
│ ├── JSONHook.kt
│ ├── PopupWindowHook.kt
│ ├── SHAHook.kt
│ ├── SensorMangerHook.kt
│ ├── SignatureHook.kt
│ ├── ToastHook.kt
│ ├── VpnCheckHook.kt
│ └── WebHook.kt
└── util
│ ├── ConfigUtil.kt
│ ├── Extension.kt
│ ├── HookHelper.kt
│ ├── HookUtils.kt
│ ├── LogUtil.kt
│ └── Type.kt
└── images
├── config_dialog_screenshot.png
├── config_screenshot.png
├── extension_main_features_shot.png
├── main_extension_print_dialog.png
├── main_extension_screenshot.png
└── main_home_screenshot.png
/HookTest.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/littleWhiteDuck/SimpleHook/6d87c0ec606fda00b66e93a141293784912102ac/HookTest.apk
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **这里是SimpleHook的hook部分代码**
2 |
3 | # simpleHook使用说明
4 |
5 | **中文文档**|[English](README_EN.md)
6 |
7 | > [simpleHook.apk](https://wwp.lanzoub.com/b0177tlri)(密码:simple)
8 | >
9 | > TG交流群: @simpleHook
10 | >
11 | > 本软件主打简单,如名字一样,如果你追求更复杂的hook操作,推荐使用 [jsHook(你可以实现更复杂的功能)](https://github.com/Xposed-Modules-Repo/me.jsonet.jshook)、[曲境(电脑端浏览器操作)](https://github.com/Mocha-L/QuJing);如果你追求更多的扩展功能,推荐使用算法助手等等类似应用
12 | >
13 | > 功能概述:自定义返回值、参数值等,记录常见各种加密算法、toast、dialog、popupwindow、JSONObject创建增加等
14 |
15 |
16 | ## 1. 功能说明
17 |
18 | ### 页面介绍
19 |
20 | **首页**
21 |
22 |
23 |
24 | 点击加号,可添加配置,点击添加配置进入下面页面
25 |
26 | **配置页面**
27 |
28 |
29 |
30 | 点击‘搜索样式’图标,可进入AppList页面,进行选择应用
31 |
32 | 点击‘下载样式’图标,可保存配置
33 |
34 | 点击右下角加号,可在弹出窗口填写配置
35 |
36 |
37 |
38 | 有多种模式可以选择,输入类名前建议了解设置页面(smali to config),它可以简化填写
39 |
40 | **扩展页面**
41 |
42 |
43 |
44 | **具体功能**
45 |
46 |
47 |
48 | 点击“播放样式”按钮,可打开悬浮窗(需要授予悬浮窗权限),然后打开目标应用,可以显示一些信息(开启了打印参数值、返回值、扩展页面大部分功能)
49 |
50 | 悬浮窗
51 |
52 |
53 |
54 | ## 2.自定义Hook编写规则
55 |
56 | 下面是编写规则:(你可以下载*[HookTest.apk](https://github.com/littleWhiteDuck/HookTest/releases)*,此App应用了所有情况,并内附有配置)
57 |
58 | > 使用前请先了解设置页【smali转配置】,它可以简化你的操作(配合MT管理器等逆向分析软件)
59 |
60 | ### 简要的基本介绍
61 |
62 | - 支持Java语法和Smali语法填写配置信息
63 |
64 | ```java
65 | // java
66 | me.simplehook.MainActivty
67 | // smali
68 | Lme/simplehook/MainActivity; //一定要有 --> ; <--
69 | ```
70 |
71 | - 支持基本类型和其它类型参数
72 |
73 | ```java
74 | // 类型 主要用于填写参数类型和变量类型
75 | // 基本类型你可以使用java语法这样填
76 | boolean int long short char byte float double
77 | // 基本类型你也可以使用smali语法这样填
78 | Z I J S C B F D
79 | // 其他类型你可以使用java语法这样填
80 | java.lang.String android.content.Context
81 | // 其他类型你也可以使用smali语法这样填
82 | Ljava/lang/String; Landroid/content/Context; //一定要有 --> ; <--
83 | ```
84 |
85 |
86 | ### 结果值的填写规则
87 |
88 | > 此处应注意,本软件不像其他软件一样需要填写返回值、参数值类型,本软件并不需要,你只需要**按照规则填写**,自动判断
89 |
90 | #### 2.1. 基本类型
91 |
92 | | 类型(java、smali) | 值的例子 | 注意事项 |
93 | | ------------------ | -------------------- | ------------------------------ |
94 | | 布尔值(boolean、Z) | true、false | |
95 | | 整数(int、I) | 1、2、3 | |
96 | | 长整型(long、J) | 1l、120000L、123456l | 要注意:数字 + L |
97 | | 短整型(short、S) | 1short、2short | 要注意:数字 + short |
98 | | 字符(char、C) | 195c | 要注意:符合char类型的字符 + c |
99 | | 字节(byte、B) | 2b、3b | 要注意:符合byte类型的字符 + b |
100 | | 单浮点(float、F) | 2f、3f、3.0f | 要注意:数字 + f |
101 | | 双浮点(double、D) | 2d、3d、3.0d | 要注意:数字 + d |
102 |
103 | #### 2.2. null
104 |
105 | > 其他类型只能返回null(字符串除外),null
106 |
107 | #### 2.3. 字符串
108 |
109 | ##### 2.3.1 一般情况
110 |
111 | > 不符合基本类型和null的全部转化为字符串类型
112 |
113 | ##### 2.3.2 特殊情况
114 |
115 | | 特殊的字符串 | 值的例子 | 注意事项 |
116 | | ------------ | -------------------------------- | ------------------------------------------------------------ |
117 | | 数字 | 111s, 2002s | 常见于 "111111" 这种,但是本软件你需要在数字后面加入s,如果你不加s,会被转成数字,可能导致目标应用崩溃 |
118 | | 布尔 | trues、falses | 常见于 "true" 、"false" 这种,但是本软件你需要在布尔值后面加入s,如果你不加s,会被转成布尔值,可能导致目标应用崩溃 |
119 | | null | nulls | 常见于 "null"这种,但是本软件你需要在null后面加入s,如果你不加s,会被转成null,可能导致目标应用空指针 |
120 | | 空字符串 | 英文单词'empty' 或者中文汉字'空' | 如果你直接填空,将无法保存配置,这样做是为了预防你在使用时不填修改值导致无法正常Hook |
121 |
122 | ##### 2.3.3 随机文本返回值
123 |
124 | > 仅用于返回值,返回值填写下面json代码
125 | >
126 | > ```json
127 | > {
128 | > "random": "abcdefgh123456789",
129 | > "length": 9,
130 | > "key": "key",
131 | > "updateTime": 100,
132 | > "defaultValue": ""
133 | > }
134 | > ```
135 | >
136 | > 上述json格式代码介绍:
137 | > random:字符串,填写随机文本由哪些字符组成
138 | >
139 | > length:整数,代表需要生成多长的随机文本
140 | >
141 | > key:字符串,唯一识别码,可以随便填写,但是一个软件中用到多个随机返回值时需要填不一样的
142 | >
143 | > updateTime:整数,代表着间隔多长时间更新一下随机文本,单位秒, -1代表每次都更新
144 | >
145 | > defaultValue:非必填项
146 |
147 | ## 3.具体的hook模式
148 |
149 | #### hook返回值
150 |
151 | ```java
152 | // 例如1
153 | import simple.example;
154 | Class Example{
155 | public static boolean isFun() {
156 | boolean result = true;
157 | ...
158 | ...
159 | return result
160 | }
161 | }
162 | /*
163 | 模式选择 Hook返回值
164 | 类名应填:simple.example.Example
165 | 方法名应填:isFun
166 | 参数类型应填:(此处留空,因为没有参数)
167 | 修改值应填:true 或者 false
168 | */
169 |
170 | /*
171 | 多个参数参数类型的填法(用英语逗号分开,参数类型支持数组):
172 | boolean,int,android.content.Context
173 | */
174 | // 例如2
175 | import simple.example;
176 | class Example{
177 | public static String isFun(Sring str, Context context, boolean b) {
178 | String result = str;
179 | ...
180 | ...
181 | return result
182 | }
183 | }
184 | /*
185 | 模式选择 Hook返回值
186 | 类名应填:simple.example.Example
187 | 方法名应填:isFun
188 | 参数类型应填:
189 | java语法: java.lang.String,android.content.Context,boolean (使用参数间使用英文逗号分开,仅一个参数不需要加逗号)
190 | smali语法:Ljava/lang/String;,Landroid/content/Context;,Z
191 | 修改值应填:是个字符串 (应符合结果值的填写规则,不需要加引号)
192 | */
193 | ```
194 |
195 | #### hook返回值+
196 |
197 | >此功能可以将json转为对象(使用Gson),你如果不知道这个对象的Json格式是什么样子的,可以使用【记录返回值】功能,复制返回值即可。这个功能并不是万能的,不适用所有情况,简单的数据类应该是没有问题的,暂不支持数组。
198 | >
199 | >模式:hook返回值+
200 | >
201 | >返回值的类名:填返回值的类名
202 | >
203 | >修改值:填json代码,如
204 | >
205 | >```json
206 | >{"isHook":false,"level":10000}
207 | >```
208 | > 举个例子:
209 | >
210 | >```java
211 | >import simple.example;
212 | >
213 | >// 数据类
214 | >public class UserBean {
215 | > private boolean isHook;
216 | > private int level;
217 | >
218 | > public UserBean(boolean isHook, int level) {
219 | > this.isHook = isHook;
220 | > this.level = level;
221 | > }
222 | >}
223 | >
224 | >public class Example{
225 | > public static UserBean isFun() {
226 | > UserBean userBean = new UserBean(true, 10);
227 | > ...
228 | > ...
229 | > return userBean
230 | > }
231 | > }
232 | >/*
233 | >假如hook isFun的返回值
234 | >模式:hook返回值+
235 | >类名:simple.example.Example
236 | >方法名:isFun
237 | >参数类型:
238 | >返回值的类名:simple.example.UserBean
239 | >结果值:{"isHook":false,"level":10000}
240 | >*/
241 | >
242 | >```
243 | >
244 | >
245 |
246 | #### hook参数值
247 |
248 | ```java
249 | // 类型值同 hook返回值类型
250 | //特殊用法,如下面一段代码
251 | public boolean isModuleLive(Context context, String str, int level){
252 |
253 | retrun true
254 | }
255 | //如果你只想要hook level的值,你可以在修改值那一栏向下面这样填
256 | ,,99
257 | //如果你只想要hook str的值,你可以在修改值那一栏向下面这样填
258 | ,啦啦啦,
259 | //如果你只想要hook str、level的值,你可以在修改值那一栏向下面这样填
260 | ,啦啦啦,99
261 | //如果你想要全部hook,你可以在修改值那一栏向下面这样填
262 | null,啦啦啦,99 // context为null也许导致闪退
263 | /*
264 | 多个参数参数类型的填法(用英语逗号分开):
265 | android.content.Context,jave.lang.String,int
266 | 或者如下填写
267 | Landroid/content/Context;,Ljave/lang/String;int
268 | */
269 | ```
270 |
271 | #### 中断执行
272 |
273 | ```java
274 | // 此模式会拦截方法执行
275 | // 如hook返回值或者hook参数值一样填,不需要填写返回值、参数值
276 | public void printString() {
277 | System.out.println("start");
278 | testBreakMethod();
279 | System.out.println("end");
280 |
281 | /*
282 | 输出结果为
283 | start
284 | end
285 |
286 | test Break Mode 没有被输出
287 | */
288 | }
289 |
290 | // 假如:此方法被中断
291 | public void testBreakMethod() {
292 | System.out.println("test Break Mode")
293 | }
294 | ```
295 |
296 | #### Hook所有同名方法
297 |
298 | ```java
299 | /*
300 | Hook一个类所有同名方法,参数类型填写 * 即可
301 | */
302 | ```
303 |
304 | #### Hook一个类中所有方法
305 |
306 | ```java
307 | /*
308 | Hook一个类所有方法,方法名填写 * 即可;参数类型可随意填写,有些h不可为空
309 | */
310 | ```
311 |
312 | #### 构造方法
313 |
314 | ```java
315 | // 方法名填写:
316 | import simple.example;
317 | public class Example{
318 | int a;
319 | int b;
320 | public Example(int a, boolean b) {
321 | this.a = a;
322 | this.b = b
323 | }
324 | }
325 | // Hook模式,根据自己的需求选择,一般为hook参数值/记录参数值,其他模式可能造成软件闪退
326 | /*
327 | 方法名填写:
328 | 例如修改两个参数值
329 | 类名填写:simple.example.Example
330 | 方法名填写:
331 | 参数类型填写:int,int
332 | 结果值填写:88,99
333 | */
334 | ```
335 |
336 | #### HOOK静态变量
337 |
338 | ```java
339 | import simple.example;
340 | public class Example{
341 | public static boolean isTest = false;
342 | }
343 |
344 | import simple.example;
345 | public class MainActivity extends Acitvity {
346 | @Override
347 | protected void onCreate(Bundle savedInstanceState) {
348 | super.onCreate(savedInstanceState);
349 | setContentView(R.layout.activity_main);
350 | initData();
351 | initView();
352 | }
353 |
354 | private void initData(){
355 | Example.isTest = false;
356 | }
357 |
358 | private void initView() {
359 | //你想要修改 isTest为true,所以你应当再这个变量被赋值后再去hook
360 | System.out.println(Example.isTest);
361 | }
362 | }
363 | // 具体的值只支持基本类型,和字符串
364 | // 无需填写变量类型;要符合[结果值]填写规则
365 | /*
366 | 模式选择 Hook静态变量
367 | hook点:after/before 根据需要填写,默认after
368 | 类名应填:simple.example.MainActivity;
369 | 方法名应填: initData
370 | 参数类型应填:(什么都是不填,因为这个方法没有参数)
371 | 变量所在类名:simple.example.Example
372 | 变量名应填:isTest
373 | 修改值应填:true/false
374 | */
375 | ```
376 |
377 | #### HOOK实例变量
378 |
379 | ```java
380 | import simple.example;
381 | public class UseBean {
382 | private boolean isHook;
383 | private int level;
384 |
385 | public UseBean(boolean isHook, int level) {
386 | this.isHook = isHook;
387 | this.level = level;
388 | }
389 |
390 | public boolean isHook() {
391 | return isHook;
392 | }
393 |
394 | public void setHook(boolean hook) {
395 | isHook = hook;
396 | }
397 |
398 | public int getLevel() {
399 | return level;
400 | }
401 |
402 | public void setLevel(int level) {
403 | this.level = level;
404 | }
405 | }
406 |
407 | import simple.example;
408 | public class MainActivity extends Acitvity {
409 | private User user;
410 | @Override
411 | protected void onCreate(Bundle savedInstanceState) {
412 | super.onCreate(savedInstanceState);
413 | setContentView(R.layout.activity_main);
414 | initData();
415 | initView();
416 | }
417 |
418 | private void initData(){
419 | user = new User(true, 100);
420 | }
421 |
422 | private void initView() {
423 | //你想要修改isHook、level,所以你应当再这个变量被赋值后再去hook
424 | System.out.println(user.isHook());
425 | System.out.println(user.getLevel());
426 | }
427 | }
428 | // 具体的值只支持基本类型,和字符串
429 | // 无需填写变量类型;要符合[结果值]填写规则
430 | /*
431 | 模式选择 Hook变量
432 | hook点:after/before 根据需要填写,默认是after
433 | 类名应填:simple.example.UseBean;
434 | 方法名应填: // 表示构造方法
435 | 参数类型应填:boolean,int
436 | 变量名应填:isHook
437 | 修改值应填:true/false
438 |
439 | 实例变量/成员变量:不支持像静态变量一样跨类hook,只能在本类的某个方法执行后,再去hook变量值
440 | */
441 | ```
442 |
443 | #### 记录参数值
444 |
445 | > 方法的参数值会被记录,前往记录页面可以查看、
446 | > 若参数是数组或者list会被转成json格式
447 |
448 | #### 记录返回值
449 |
450 | > 方法的返回值会被记录,前往记录页面可以查看
451 | > 若结果是数组或者list会被转成json格式
452 |
453 | #### 记录参返
454 |
455 | > 方法的参数值、返回值会被一同记录,前往记录页面可以查看
456 | > 若结果是数组或者list会被转成json格式
457 | > 若参数是数组或者list会被转成json格式
458 |
459 | #### 扩展Hook
460 |
461 | > 切记**打开总开关**
462 | > 功能请前往app查看
463 |
464 | ## 4.常见问题(FAQ)
465 |
466 | ### 1.hook没有效果
467 |
468 | > - 可看框架日志,是否有报错等
469 | > - 储存文件更新配置某些情况下需要手动刷新,开启、关闭、编辑保存即可刷新
470 | > - 请授予所需权限(android11以下:储存权限,android11及以上:ROOT权限)
471 |
472 |
473 | ### 2.什么是smali转配置
474 |
475 | > 开启此实验功能后,配置页面顶部会增加‘粘贴板’图标,点击可将应用调用代码或签名,转化为配置(防止手动输入错误),增加配置后你需要手动选择合适的模式以及结果值
476 | > 调用代码例子:
477 | >
478 | > ```smali
479 | > iget v0, p0, Lme/duck/hooktest/bean/UseBean;->level:I
480 | > invoke-virtual {v0}, Lme/duck/hooktest/bean/UseBean;->isHook()Z
481 | > ```
482 | >
483 | > 方法签名、字段签名例子:
484 | >
485 | > ```smali
486 | > Lme/duck/hooktest/bean/UseBean;->level:I
487 | > Lme/duck/hooktest/bean/UseBean;->isHook()Z
488 | > ```
489 | >
490 | > 上述可在MT管理器导航中长按字段或方法选择**复制签名**或者**查找调用**
491 |
492 | ### 3.为什么目标应用运行很慢
493 |
494 | > 请关闭不必要的扩展HOOK和记录参数、返回值功能, 例如:md5、base64等,这些功能会产生大量的Log
495 |
496 | ### 4.
497 |
498 | ### 5.
499 |
500 | ### 6.什么是hook点
501 |
502 | > hook静态变量、实例变量支持手动填写hook点,hook点就是在方法执行前hook还是在方法执行后hook
503 | >
504 | > before:方法执行前hook;
505 | > after:方法执行后hook
506 |
507 | ### 7.什么是删除遗留配置
508 |
509 | > 当你卸载本应用或者清除数据时,目标应用配置文件仍然可能保存在储存文件中
510 | >
511 | > 1. /data/local/tmp/simpleHook/目标应用包名/config/
512 | > 2. /storage/emluated/0/Android/data/目标应用包名/simpleHook/config/
513 | >
514 | > 这个功能就是遍历所有的应用目录并删除无用的配置(本应用内未显示其配置)
515 | >
516 | > 因为需要遍历所有应用会比较慢
517 |
518 |
519 | ### 8.配置说明
520 |
521 | ```json
522 | {
523 | "packageName": "包名",
524 | "appName": "应用名",
525 | "versionName": "版本名",
526 | "description": "描述",
527 | "configs": "配置",
528 | "enable": true,
529 | "id": 0
530 | },
531 | configs 为下列字符串形式
532 | [
533 | {
534 | "mode": 0,
535 | "className": "类名",
536 | "methodName": "方法名",
537 | "params": "参数类型",
538 | "fieldName": "变量名",
539 | "fieldClassName": "变量所在类名",
540 | "resultValues": "返回值",
541 | "hookPoint": "hook点",
542 | "returnClassName": "返回值类名",
543 | "fieldType": "变量类型",
544 | "enable": true
545 | }
546 | ]
547 | ```
548 |
549 | #### mode
550 |
551 | - 0:
552 |
553 | > 代表Hook返回值,
554 | >
555 | > 有效:mode、className、methodName、params、resultValues、enable
556 | >
557 | > params: 多个参数为**,**隔开
558 | >
559 | > resultValues:返回值
560 |
561 | - 1:
562 |
563 | >代表Hook参数值,
564 | >
565 | >有效:mode、className、methodName、params、resultValues、enable
566 | >
567 | >params: 多个参数为**,**隔开
568 | >
569 | >resultValues:多个参数值**,**隔开,留空不hook这个参数
570 |
571 | - 2:
572 |
573 | >代表替换/拦截方法执行
574 | >
575 | >有效:mode、className、methodName、params、enable
576 |
577 | - 3:
578 |
579 | > 代表Hook静态变量
580 | >
581 | > 有效1:mode、className、resultValues、enable、fieldName
582 | >
583 | > 直接Hook变量:
584 | >
585 | > resultValues:变量值
586 | >
587 | >
588 | >
589 | > 有效2:mode、className、methodName、params、resultValues、enable、fieldName、fieldClassName、hookPoint
590 | >
591 | > 某方法Hook前后hook变量:
592 | >
593 | > resultValues: 变量值
594 | >
595 | > hookPoint: before、after
596 |
597 | - 4:
598 |
599 | >代表Hook实例变量
600 | >
601 | >有效:mode、className、methodName、params、resultValues、enable、fieldName、fieldClassName
602 |
603 | - 5:
604 |
605 | >记录参数值
606 | >
607 | >有效:mode、className、methodName、params、enable
608 |
609 | - 6:
610 |
611 | >记录返回值
612 | >
613 | >有效:mode、className、methodName、params、enable
614 |
615 | - 7:
616 |
617 | > 记录参数值和返回值
618 | >
619 | > 有效:mode、className、methodName、params、enable
620 |
621 | - 8:
622 |
623 | > 记录静态变量
624 | >
625 | > 有效:mode、className、methodName、params、resultValues、enable、fieldName、fieldClassName
626 |
627 | - 9:
628 |
629 | > 记录实例变量
630 | >
631 | > 有效:mode、className、methodName、params、resultValues、enable、fieldName、fieldClassNam
632 |
633 | - 10
634 |
635 | > Hook返回值+
636 | >
637 | > 有效:mode、className、methodName、params、resultValues、enable、returnClassName
638 | >
639 | > 通过Gson将json转为returnClassName的对象
640 | >
641 | > resultValues: json字符串
642 |
643 |
644 |
645 |
646 |
--------------------------------------------------------------------------------
/README_EN.md:
--------------------------------------------------------------------------------
1 | **Here is the code for the hook part of SimpleHook**
2 |
3 | # simpleHook instructions
4 |
5 | [Chinese](README.md)|**English**
6 |
7 | > [simpleHook.apk](https://wwp.lanzoub.com/b0177tlri)(password: simple)
8 | >
9 | > TG Group: @simpleHook
10 | >
11 | > **SimpleHook** is mainly simple, just like the name. If you pursue more complex hook operation, it is recommended to use **[jsHook (You can implement very powerful Hook)](https://github.com/Xposed-Modules-Repo/me.jsonet.jshook)**, [Qujing (computer browser operation)](https://github.com/Mocha-L/QuJing); if you pursue more extended functions, it is recommended to use [算法助手]
12 | >
13 | > Function overview: custom return value, parameter value, etc., record common encryption algorithms, toast, dialog, popupwindow, JSONObject creation and put, etc.
14 | >
15 |
16 | ## 1. Function description
17 |
18 | ### Page Introduction
19 |
20 | **front page**
21 |
22 |
23 |
24 | Click the plus sign to add configuration, click Add configuration to enter the following page
25 |
26 | **Configuration page**
27 |
28 |
29 |
30 | Click the 'Search Style' icon to enter the AppList page and select an application
31 |
32 | Click the 'Download Style' icon to save the configuration
33 |
34 | Click the plus sign in the lower right corner to fill in the configuration in the pop-up window
35 |
36 |
37 |
38 | There are multiple modes to choose from. Before entering the class name, it is recommended to understand the settings page (smali to config), which can simplify filling.
39 |
40 | **Extended page**
41 |
42 |
43 |
44 | **Specific function**
45 |
46 |
47 |
48 | Click the "Play Style" button to open the floating window (you need to grant the permission of the floating window), and then open the target application, you can display some information (the print parameter value, return value, and most functions of the extended page are enabled)
49 |
50 | Floating window
51 |
52 |
53 |
54 | ## 2. Custom Hook writing rules
55 |
56 | The following are the writing rules: (You can download *[HookTest.apk](https://github.com/littleWhiteDuck/HookTest/releases)*, this App applies all the situations and includes configuration)
57 |
58 | > Please understand the setting page **[smali to config]** before use, it can simplify your operation (with decompiler apps such as **MT manager**)
59 |
60 | ### A brief basic introduction
61 |
62 | - Support Java syntax and Smali syntax to fill in configuration information
63 |
64 | ````java
65 | // java
66 | me.simplehook.MainActivty
67 | // smali
68 | Lme/simplehook/MainActivity; //must have --> ; <--
69 | ````
70 |
71 | - Support for primitive types and other type parameters
72 |
73 | ````java
74 | // Type is mainly used to fill in the parameter type and variable type
75 | // Basic types you can fill in using java syntax like this
76 | boolean int long short char byte float double
77 | // You can also use smali syntax to fill in basic types like this
78 | Z I J S C B F D
79 | // For other types, you can use java syntax to fill in
80 | jave.lang.String android.content.Context
81 | // You can also use smali syntax to fill in other types like this
82 | Ljava/lang/String; Landroid/content/Context; //must have --> ; <--
83 | ````
84 |
85 |
86 | ### Filling rules for result values
87 |
88 | > It should be noted here that this software does not need to fill in the return value and parameter value type like other software, this software does not need it, you only need to **fill in according to the rules**, automatic judgment
89 |
90 | #### 2.1. Primitive type
91 |
92 | | Type (java, smali) | Examples of value | Note |
93 | | ------------------ | -------------------- | ------------- |
94 | | boolean (boolean, Z) | true, false | |
95 | | int(int, I) | 1, 2, 3 | |
96 | | long(long, J) | 1l, 120000L, 123456l | Note: Number + 'L' |
97 | | short(short, S) | 1short, 2short | Note: number + 'short' |
98 | | char(char, C) | 195c | Note: conforms to char type string + 'c' |
99 | | byte(byte, B) | 2b, 3b | Note: conforms to byte type string + 'b' |
100 | | float(float, F) | 2f, 3f, 3.0f | Note: number + 'f' |
101 | | double(double, D) | 2d, 3d, 3.0d | Note: number + 'd' |
102 |
103 | #### 2.2. null
104 |
105 | > Other types can only return null (except strings), null
106 |
107 | #### 2.3 Strings
108 |
109 | ##### 2.3.1 General
110 |
111 | > Convert everything that does not conform to the primitive type and null to string type
112 |
113 | ##### 2.3.2 Special cases
114 |
115 | | Special Strings | Examples of Values | Notes |
116 | | ------------ | -------------------------------- | ------------------------------------------ |
117 | | Number | 111s, 2002s | Commonly seen in "111111", but in this software you need to add s after the number, if you don't add s, it will be converted into a number, which may cause the target application to crash |
118 | | Boolean | trues, falses | Common in "true" and "false", but in this software, you need to add s after the boolean value. If you do not add s, it will be converted to a boolean value, which may cause the target application to crash |
119 | | null | nulls | Commonly seen in "null", but in this software, you need to add s after null. If you do not add s, it will be converted to null, which may lead to a null pointer in the target application |
120 | | Empty string | English word 'empty' or Chinese character '空' | If you fill in the blank directly, you will not be able to save the configuration, this is to prevent you from not filling in the modified value when you use it, which will cause the normal Hook |
121 |
122 | ##### 2.3.3 Random text return value
123 |
124 | > Fill in the following json code for the return value, for return value only
125 | >
126 | > ```json
127 | > {
128 | > "random": "abcdefgh123456789",
129 | > "length": 9,
130 | > "key": "key",
131 | > "updateTime": 100,
132 | > "defaultValue": ""
133 | > }
134 | > ````
135 | >
136 | > Introduction to the above json format code:
137 | >
138 | > ```json
139 | > "random": string, fill in which characters the random text consists of
140 | > "length": an integer representing how long random text needs to be generated
141 | > "key": string, unique identification code, can be filled in casually, but when multiple random return values are used in a software, different ones need to be filled in
142 | > "updateTime": an integer, representing how long to update the random text at intervals, in seconds, -1 means update every time
143 | > "defaultValue": optional
144 | > ```
145 |
146 | ## 3. Hook mode
147 |
148 | #### hook return value
149 |
150 | ````java
151 | // eg 1
152 | import simple.example;
153 | public class Example{
154 | public static boolean isFun() {
155 | boolean result = true;
156 | ...
157 | ...
158 | return result
159 | }
160 | }
161 | /*
162 | Mode: Hook return value
163 | The class name should be filled in: simple.example.Example
164 | The method name should be filled in: isFun
165 | The parameter type should be filled in: Fill in nothing, because this method has no parameters
166 | The modified value should be filled in: true or false
167 | */
168 |
169 | /*
170 | Filling method of multiple parameter parameter types (separated by English commas, parameter types support arrays):
171 | boolean,int,android.content.Context
172 | */
173 |
174 | // eg 2
175 | import simple.example;
176 | public class Example{
177 | public static String isFun(Sring str, Context context, boolean b) {
178 | String result = str;
179 | ...
180 | ...
181 | return result;
182 | }
183 | }
184 | /*
185 | Mode:Hook return value
186 | The class name should be filled in: simple.example.Example
187 | The method name should be filled in: isFun
188 | Parameter type should be filled in:
189 | java syntax: java.lang.String,android.content.Context,boolean (use commas to separate the parameters, only one parameter does not need a comma)
190 | smali syntax: Ljava/lang/String;,Landroid/content/Context;,Z
191 | The modified value should be filled: it is a string (should meet the filling rules of the result value, no quotation marks are required)
192 | */
193 | ````
194 |
195 | #### hook return value+
196 |
197 | >This function can convert json to object (use **Gson**). If you don't know what the Json format of this object looks like, you can use the function [record return value] and copy the return value. This function is not omnipotent and does not apply to all situations. Simple data classes should be no problem, and arrays are not supported for the time being.
198 | >
199 | >Mode: hook return value+
200 | >
201 | >Class name of the return value: fill in the class name of the return value
202 | >
203 | >Modify the value: fill in the json code, such as
204 | >
205 | >````json
206 | >{"isHook":false,"level":10000}
207 | >````
208 | >For example:
209 | >
210 | >```java
211 | >import simple.example;
212 | >
213 | >// data class
214 | >public class UserBean {
215 | > private boolean isHook;
216 | > private int level;
217 | >
218 | > public UserBean(boolean isHook, int level) {
219 | > this.isHook = isHook;
220 | > this.level = level;
221 | > }
222 | >}
223 | >
224 | >public class Example{
225 | > public static UserBean isFun() {
226 | > UserBean userBean = new UserBean(true, 10);
227 | > ...
228 | > ...
229 | > return userBean;
230 | > }
231 | > }
232 | >/*
233 | >If the return value of hook isFun
234 | >Mode: hook return value +
235 | >Class name: simple.example.Example
236 | >Method name: isFun
237 | >Parameter Type:
238 | >The class name of the return value: simple.example.UserBean
239 | >Result value: {"isHook":false,"level":10000}
240 | >*/
241 | >```
242 | >
243 | >
244 |
245 | #### hook parameter value
246 |
247 | ````java
248 | // The type value is the same as the hook return value type
249 | //Special usage, such as the following code
250 | public boolean isModuleLive(Context context, String str, int level){
251 |
252 | retrun true
253 | }
254 | //If you only want the value of the hook level, you can fill in the following in the column to modify the value
255 | ,,99
256 | //If you only want the value of hook str, you can fill in the following in the column to modify the value
257 | ,hahaha,
258 | //If you only want the value of hook str and level, you can fill in the following in the column to modify the value
259 | ,hahaha,99
260 | //If you want all hooks, you can fill in the following in the modified value column
261 | null,hahaha, 99 // context being null may cause a crash
262 | /*
263 | Filling method of multiple parameter parameter types (separated by English commas):
264 | android.content.Context,jave.lang.String,int
265 | Or fill in as follows
266 | Landroid/content/Context;,Ljave/lang/String;int
267 | */
268 | ````
269 |
270 | #### break execution
271 |
272 | ````java
273 | // This mode will intercept method execution
274 | // Fill in the same as the hook return value or hook parameter value, do not need to fill in the return value and parameter value
275 | public void printString() {
276 | System.out.println("start");
277 | testBreakMethod();
278 | System.out.println("end");
279 |
280 | /*
281 | The output is
282 | start
283 | end
284 |
285 | test Break Mode is not output
286 | */
287 | }
288 |
289 | // if: this method is interrupted
290 | public void testBreakMethod() {
291 | System.out.println("test Break Mode")
292 | }
293 | ````
294 |
295 | #### Hook all methods with the same name
296 |
297 | ````java
298 | /*
299 | Hook all methods of the same name in a class, fill in * for the parameter type
300 | */
301 | ````
302 |
303 | #### Hook all methods in a class
304 |
305 | ````java
306 | /*
307 | Hook all methods in a class, fill in * for the method name.
308 | The parameter type can be filled in freely, some hook types cannot be empty.
309 | */
310 | ````
311 |
312 | #### Construction method
313 |
314 | ````java
315 | // Fill in the method name:
316 | import simple.example;
317 | public class Example{
318 | int a;
319 | int b;
320 | public Example(int a, boolean b) {
321 | this.a = a;
322 | this.b = b
323 | }
324 | }
325 | // Hook mode, choose according to your own needs, generally the hook parameter value/record parameter value, other modes may cause the software to crash
326 | /*
327 | Fill in the method name:
328 | For example, modify two parameter values
329 | Fill in the class name: simple.example.Example
330 | Fill in the method name:
331 | Fill in the parameter type: int, int
332 | Fill in the result value: 88,99
333 | */
334 | ````
335 |
336 | #### HOOK static field
337 |
338 | ````java
339 | import simple.example;
340 | public class Example{
341 | public static boolean isTest = false;
342 | }
343 |
344 | import simple.example;
345 | public class MainActivity extends Acitvity {
346 | @Override
347 | protected void onCreate(Bundle savedInstanceState) {
348 | super.onCreate(savedInstanceState);
349 | setContentView(R.layout.activity_main);
350 | initData();
351 | initView();
352 | }
353 |
354 | private void initData(){
355 | Example.isTest = false;
356 | }
357 |
358 | private void initView() {
359 | //You want to change isTest to true, so you should hook this field after assigning a value
360 | System.out.println(Example.isTest);
361 | }
362 | }
363 | // Concrete values only support primitive types, and strings
364 | // There is no need to fill in the field type; it must comply with the [result value] filling rules
365 | /*
366 | Mode: Hook static field
367 | Hook point: after/before fill in as needed, default after
368 | The class name should be filled in: simple.example.MainActivity;
369 | The method name should be filled in: initData
370 | The parameter type should be filled in: (nothing is filled in, because this method has no parameters)
371 | The class name where the variable is located: simple.example.Example
372 | The variable name should be filled in: isTest
373 | The modified value should be filled in: true/false
374 | */
375 | ````
376 |
377 | #### HOOK instance field
378 |
379 | ````java
380 | import simple.example;
381 | public class UseBean {
382 | private boolean isHook;
383 | private int level;
384 |
385 | public UseBean(boolean isHook, int level) {
386 | this.isHook = isHook;
387 | this.level = level;
388 | }
389 |
390 | public boolean isHook() {
391 | return isHook;
392 | }
393 |
394 | public void setHook(boolean hook) {
395 | isHook = hook;
396 | }
397 |
398 | public int getLevel() {
399 | return level;
400 | }
401 |
402 | public void setLevel(int level) {
403 | this.level = level;
404 | }
405 | }
406 |
407 | import simple.example;
408 | public class MainActivity extends Acitvity {
409 | private User user;
410 | @Override
411 | protected void onCreate(Bundle savedInstanceState) {
412 | super.onCreate(savedInstanceState);
413 | setContentView(R.layout.activity_main);
414 | initData();
415 | initView();
416 | }
417 |
418 | private void initData(){
419 | user = new User(true, 100);
420 | }
421 |
422 | private void initView() {
423 | //You want to modify isHook, level, so you should go to hook after this variable is assigned
424 | System.out.println(user.isHook());
425 | System.out.println(user.getLevel());
426 | }
427 | }
428 | // Concrete values only support primitive types, and strings
429 | // There is no need to fill in the field type; it must comply with the [result value] filling rules
430 | /*
431 | Mode: Hook instance field
432 | Hook point: after/before fill in as needed, the default is after
433 | The class name should be filled in: simple.example.UseBean;
434 | The method name should be filled in: // represents the constructor
435 | The parameter type should be filled in: boolean, int
436 | The variable name should be filled in: isHook
437 | The modified value should be filled in: true/false
438 |
439 | Instance field/member field: cross-class hooks like static field are not supported. You can only hook the field value after a method of this class is executed.
440 | */
441 | ````
442 |
443 | #### record parameter value
444 |
445 | > The parameter values of the method will be recorded, go to the record page to view,
446 | > If the parameter is an array or list, it will be converted to json format
447 |
448 | #### record return value
449 |
450 | > The return value of the method will be recorded, go to the record page to view
451 | > If the result is an array or list, it will be converted to json format
452 |
453 | #### record return
454 |
455 | > The parameter value and return value of the method will be recorded together, go to the record page to view
456 | > If the result is an array or list, it will be converted to json format
457 | > If the parameter is an array or list, it will be converted to json format
458 |
459 | #### Extending Hook
460 |
461 | > Remember to **turn on the main switch**
462 | > Please go to the app to view the functions
463 |
464 | ## Frequently Asked Questions (FAQ)
465 |
466 | ### 1.hook has no effect
467 |
468 | > - You can see the xposed framework(example: LSPosed) log, whether there is an error, etc.
469 | >- In some cases, the storage file update configuration needs to be manually refreshed, open, close, edit and save to refresh
470 | > - Please grant the required permissions (below android11: storage permission, android11 and above: ROOT permission)
471 |
472 |
473 | ### 2. What is smali transfer configuration
474 |
475 | > After this experimental function is enabled, a 'pasteboard' icon will be added to the top of the configuration page. Click to convert the application code or signature into a configuration (to prevent manual input errors). After adding the configuration, you need to manually select the appropriate mode and result value.
476 | > Example of calling code:
477 | >
478 | > ```smali
479 | > iget v0, p0, Lme/duck/hooktest/bean/UseBean;->level:I
480 | > invoke-virtual {v0}, Lme/duck/hooktest/bean/UseBean;->isHook()Z
481 | > ````
482 | >
483 | > Example of method signature and field signature:
484 | >
485 | > ```smali
486 | > Lme/duck/hooktest/bean/UseBean;->level:I
487 | > Lme/duck/hooktest/bean/UseBean;->isHook()Z
488 | > ````
489 | >
490 | > The above can be selected by long-pressing a field or method in the MT Manager navigation to select **Copy Signature** or **Find Call**
491 |
492 | ### 3. Why the target application is running slowly
493 |
494 | > Please turn off unnecessary **EXTENSION HOOK** and **record parameters**, **record return value**...etc, such as: md5, base64, etc., these functions will generate a lot of Log
495 |
496 | ### 4.
497 |
498 | ### 5.
499 |
500 | ### 6. What is hook point
501 |
502 | > Some hooks support manually filling in the hook point. The hook point is the hook before the method is executed or the hook after the method is executed.
503 | >
504 | > before: hook before method execution;
505 | > after: hook after the method is executed
506 |
507 | ### 7. What is [Remove interfering configuration]
508 |
509 | > When you uninstall the app or clear data, the target app configuration file may still be saved in the storage file
510 | >
511 | > 1. /data/local/tmp/simpleHook/target application package name/config/
512 | > 2. /storage/emluated/0/Android/data/target application package name/simpleHook/config/
513 | >
514 | > This function is to traverse all application directories and delete useless configurations (the configuration is displayed in this application).
515 | >
516 | > Because it needs to traverse all applications will be slower
--------------------------------------------------------------------------------
/hook/FieldHook.kt:
--------------------------------------------------------------------------------
1 | package me.simpleHook.hook
2 |
3 | import com.github.kyuubiran.ezxhelper.utils.*
4 | import de.robv.android.xposed.XC_MethodHook
5 | import de.robv.android.xposed.XposedHelpers
6 | import me.simpleHook.bean.ConfigBean
7 | import me.simpleHook.bean.LogBean
8 | import me.simpleHook.constant.Constant
9 | import me.simpleHook.hook.Tip.getTip
10 | import me.simpleHook.hook.util.*
11 | import me.simpleHook.hook.util.HookHelper.appClassLoader
12 | import me.simpleHook.hook.util.HookHelper.hostPackageName
13 | import me.simpleHook.util.LanguageUtils
14 |
15 | object FieldHook {
16 | /**
17 | * @author littleWhiteDuck
18 | * @param configBean 配置类
19 | */
20 | @JvmStatic
21 | fun hookStaticField(configBean: ConfigBean) {
22 | configBean.apply {
23 | if (className.isEmpty() && methodName.isEmpty() && params.isEmpty()) {
24 | // 直接hook
25 | if (mode == Constant.HOOK_RECORD_STATIC_FIELD) {
26 | recordStaticField(fieldClassName, fieldName)
27 | } else {
28 | hookStaticField(fieldClassName, resultValues, fieldName)
29 | }
30 | return
31 | }
32 | val hooker: Hooker = if (mode == Constant.HOOK_RECORD_STATIC_FIELD) {
33 | { recordStaticField(fieldClassName, fieldName) }
34 | } else {
35 | { hookStaticField(fieldClassName, resultValues, fieldName) }
36 | }
37 | hookField(hooker)
38 | }
39 | }
40 |
41 | private fun ConfigBean.hookField(
42 | hooker: Hooker
43 | ) {
44 | val isBeforeHook = hookPoint == "before"
45 | try {
46 | if (methodName == "*") {
47 | findAllMethods(className) {
48 | true
49 | }.hook(isBeforeHook, hooker)
50 | } else if (params == "*") {
51 | if (methodName == "") {
52 | findAllConstructors(className) {
53 | true
54 | }.hook(isBeforeHook, hooker)
55 | } else {
56 | findAllMethods(className) {
57 | name == methodName
58 | }.hook(isBeforeHook, hooker)
59 | }
60 | } else {
61 | if (methodName == "") {
62 | findConstructor(className) {
63 | isSearchConstructor(params)
64 | }.hook(isBeforeHook, hooker)
65 | } else {
66 | findMethod(className) {
67 | name == methodName && isSearchMethod(params)
68 | }.hook(isBeforeHook, hooker)
69 | }
70 | }
71 | } catch (e: Throwable) {
72 | LogUtil.outHookError(className, "$methodName($params)", e)
73 | }
74 | }
75 |
76 | private fun recordStaticField(
77 | fieldClassName: String, fieldName: String
78 | ) {
79 | val type = if (LanguageUtils.isNotChinese()) "Static field" else "静态变量"
80 | val hookClass = XposedHelpers.findClass(fieldClassName, appClassLoader)
81 | val result = XposedHelpers.getStaticObjectField(hookClass, fieldName)
82 | val list = listOf(getTip("className") + fieldClassName,
83 | getTip("fieldName") + fieldName,
84 | getTip("fieldValue") + result)
85 | val logBean = LogBean(type = type, other = list, packageName = hostPackageName)
86 | LogUtil.outLogMsg(logBean)
87 | }
88 |
89 | private fun hookStaticField(
90 | fieldClassName: String, values: String, fieldName: String
91 | ) {
92 | val clazz: Class<*> = XposedHelpers.findClass(fieldClassName, appClassLoader)
93 | XposedHelpers.setStaticObjectField(clazz, fieldName, Type.getDataTypeValue(values))
94 | }
95 |
96 | @JvmStatic
97 | fun hookInstanceField(
98 | configBean: ConfigBean
99 | ) {
100 | configBean.apply {
101 | val hooker: Hooker = if (mode == Constant.HOOK_RECORD_INSTANCE_FIELD) {
102 | { recordInstanceField(className, it, fieldName) }
103 | } else {
104 | { hookInstanceField(it, resultValues, fieldName) }
105 | }
106 | hookField(hooker)
107 | }
108 | }
109 |
110 | private fun recordInstanceField(
111 | className: String, param: XC_MethodHook.MethodHookParam, fieldName: String
112 | ) {
113 | val type = if (LanguageUtils.isNotChinese()) "Instance field" else "实例变量"
114 | val thisObj = param.thisObject
115 | val result = XposedHelpers.getObjectField(thisObj, fieldName)
116 | val list = listOf(getTip("className") + className,
117 | getTip("fieldName") + fieldName,
118 | getTip("fieldValue") + result)
119 | val logBean = LogBean(type = type, other = list, packageName = hostPackageName)
120 | LogUtil.outLogMsg(logBean)
121 | }
122 |
123 | private fun hookInstanceField(
124 | param: XC_MethodHook.MethodHookParam, values: String, fieldName: String
125 | ) {
126 | val thisObj = param.thisObject
127 | XposedHelpers.setObjectField(thisObj, fieldName, Type.getDataTypeValue(values))
128 | }
129 | }
--------------------------------------------------------------------------------
/hook/HookInit.kt:
--------------------------------------------------------------------------------
1 | package me.simpleHook.hook
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import com.github.kyuubiran.ezxhelper.init.EzXHelperInit
6 | import com.github.kyuubiran.ezxhelper.utils.findMethod
7 | import com.github.kyuubiran.ezxhelper.utils.hookAfter
8 | import com.github.kyuubiran.ezxhelper.utils.hookReturnConstant
9 | import de.robv.android.xposed.IXposedHookLoadPackage
10 | import de.robv.android.xposed.callbacks.XC_LoadPackage
11 | import me.simpleHook.BuildConfig
12 | import me.simpleHook.constant.Constant
13 | import me.simpleHook.hook.util.ConfigUtil
14 | import me.simpleHook.hook.util.HookHelper
15 | import me.simpleHook.extension.log
16 |
17 | class HookInit : IXposedHookLoadPackage {
18 |
19 | override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
20 | EzXHelperInit.initHandleLoadPackage(lpparam)
21 | if (lpparam.packageName == BuildConfig.APPLICATION_ID) {
22 | findMethod("me.simpleHook.ui.activity.MainActivity") {
23 | name == "isModuleLive"
24 | }.hookReturnConstant(true)
25 |
26 | } else {
27 | if (HookHelper.isAppContextInitialized) return
28 | findMethod(Application::class.java) {
29 | name == "attach"
30 | }.hookAfter {
31 | HookHelper.initFields(context = it.args[0] as Context, lpparam)
32 | HookHelper.appContext.getExternalFilesDirs(null)
33 | startHook()
34 | }
35 | }
36 | }
37 |
38 | private fun startHook() {
39 | val packageName = HookHelper.hostPackageName
40 | ConfigUtil.getConfigFromFile()?.let {
41 | "get custom config succeed from file".log(packageName)
42 | MainHook.readyHook(it)
43 | } ?: run {
44 | "get custom config failed from file".log(packageName)
45 | ConfigUtil.getCustomConfigFromDB()?.let {
46 | "get custom config succeed from db".log(packageName)
47 | MainHook.readyHook(it)
48 | } ?: "get custom config failed from db".log(packageName)
49 | }
50 | ConfigUtil.getConfigFromFile(Constant.EXTENSION_CONFIG_NAME)?.let {
51 | "get extension config succeed from file".log(packageName)
52 | MainHook.readyExtensionHook(it)
53 | } ?: run {
54 | "get extension config failed from file".log(packageName)
55 | ConfigUtil.getExConfigFromDB()?.let {
56 | "get extension config succeed from db".log(packageName)
57 | MainHook.readyExtensionHook(it)
58 | } ?: "get extension config failed from db".log(packageName)
59 | }
60 | }
61 |
62 | }
--------------------------------------------------------------------------------
/hook/MainHook.kt:
--------------------------------------------------------------------------------
1 | package me.simpleHook.hook
2 |
3 | import android.app.AndroidAppHelper
4 | import android.content.Context
5 | import com.github.kyuubiran.ezxhelper.utils.*
6 | import com.google.gson.Gson
7 | import de.robv.android.xposed.XC_MethodHook
8 | import de.robv.android.xposed.XposedHelpers
9 | import kotlinx.serialization.decodeFromString
10 | import kotlinx.serialization.json.Json
11 | import me.simpleHook.bean.ConfigBean
12 | import me.simpleHook.bean.ExtensionConfig
13 | import me.simpleHook.bean.LogBean
14 | import me.simpleHook.constant.Constant
15 | import me.simpleHook.database.entity.AppConfig
16 | import me.simpleHook.extension.log
17 | import me.simpleHook.extension.random
18 | import me.simpleHook.hook.Tip.getTip
19 | import me.simpleHook.hook.extension.*
20 | import me.simpleHook.hook.util.*
21 | import me.simpleHook.hook.util.HookHelper.appContext
22 | import me.simpleHook.hook.util.HookHelper.hostPackageName
23 | import me.simpleHook.hook.util.HookUtils.getObjectString
24 | import me.simpleHook.hook.util.LogUtil.getStackTrace
25 | import me.simpleHook.hook.util.LogUtil.outHookError
26 | import me.simpleHook.hook.util.LogUtil.outLogMsg
27 | import me.simpleHook.hook.util.Type.getDataTypeValue
28 | import me.simpleHook.util.JsonUtil
29 | import me.simpleHook.util.LanguageUtils
30 | import org.json.JSONObject
31 |
32 |
33 | object MainHook {
34 |
35 | fun readyHook(strConfig: String) {
36 | if (strConfig.isBlank()) return
37 | try {
38 | val appConfig = Json.decodeFromString(strConfig)
39 | if (!appConfig.enable) return
40 | val configs = Json.decodeFromString>(appConfig.configs)
41 | getTip("startCustomHook").log(hostPackageName)
42 | configs.forEach { configBean ->
43 | if (!configBean.enable) return@forEach
44 | configBean.apply {
45 | when (configBean.mode) {
46 | Constant.HOOK_STATIC_FIELD, Constant.HOOK_RECORD_STATIC_FIELD -> {
47 | FieldHook.hookStaticField(configBean)
48 | }
49 | Constant.HOOK_FIELD, Constant.HOOK_RECORD_INSTANCE_FIELD -> {
50 | FieldHook.hookInstanceField(configBean)
51 | }
52 | else -> specificHook(className = className,
53 | methodName = methodName,
54 | values = resultValues,
55 | params = params,
56 | mode = mode,
57 | returnClassName = returnClassName)
58 | }
59 | }
60 | }
61 | } catch (e: Throwable) {
62 | val configTemp = try {
63 | val appConfig = Json.decodeFromString(strConfig)
64 | JsonUtil.formatJson(appConfig.configs)
65 | } catch (e: Throwable) {
66 | strConfig
67 | }
68 | LogUtil.outLog(arrayListOf(getTip("errorType") + getTip("unknownError"),
69 | "config: $configTemp",
70 | getTip("detailReason") + e.stackTraceToString()), "Error Unknown Error")
71 | "config error".log(hostPackageName)
72 | }
73 | }
74 |
75 |
76 | private fun specificHook(
77 | className: String,
78 | methodName: String,
79 | values: String,
80 | params: String,
81 | mode: Int,
82 | returnClassName: String
83 | ) {
84 | val hooker: Hooker = when (mode) {
85 | Constant.HOOK_RETURN -> {
86 | { hookReturnValue(values, it) }
87 | }
88 | Constant.HOOK_RETURN2 -> {
89 | { hookReturnValuePro(values, it, returnClassName) }
90 | }
91 | Constant.HOOK_BREAK -> {
92 | {}
93 | }
94 | Constant.HOOK_PARAM -> {
95 | { hookParamsValue(it, values, className, methodName, params) }
96 | }
97 | Constant.HOOK_RECORD_PARAMS -> {
98 | { recordParamsValue(className, it) }
99 | }
100 | Constant.HOOK_RECORD_RETURN -> {
101 | { recordReturnValue(className, it) }
102 | }
103 | Constant.HOOK_RECORD_PARAMS_RETURN -> {
104 | { recordParamsAndReturn(className, it) }
105 | }
106 | else -> {
107 | throw java.lang.IllegalStateException("读不懂配置")
108 | }
109 | }
110 | try {
111 | if (methodName == "*") {
112 | findAllMethods(className) {
113 | true
114 | }.hook(mode, hooker)
115 | } else if (params == "*") {
116 | if (methodName == "") {
117 | hookAllConstructorBefore(className, hooker = hooker)
118 | } else {
119 | findAllMethods(className) {
120 | name == methodName
121 | }.hook(mode, hooker)
122 | }
123 | } else {
124 | if (methodName == "") {
125 | findConstructor(className) {
126 | isSearchConstructor(params)
127 | }.hookBefore(hooker)
128 | } else {
129 | findMethod(className) {
130 | name == methodName && isSearchMethod(params)
131 | }.hook(mode, hooker)
132 | }
133 | }
134 | } catch (e: Throwable) {
135 | outHookError(className, "$methodName($params)", e)
136 | }
137 |
138 | }
139 |
140 | private fun hookReturnValuePro(
141 | values: String, param: XC_MethodHook.MethodHookParam, returnClassName: String
142 | ) {
143 | val hookClass = XposedHelpers.findClass(returnClassName, HookHelper.appClassLoader)
144 | try {
145 | val hookObject = Gson().fromJson(values, hookClass)
146 | param.result = hookObject
147 | } catch (e: Exception) {
148 | hookReturnValue(values, param)
149 | }
150 | }
151 |
152 | private fun hookReturnValue(
153 | values: String, param: XC_MethodHook.MethodHookParam
154 | ) {
155 | val targetValue = getDataTypeValue(values)
156 | if (targetValue is String) {
157 | try {
158 | val jsonObject = JSONObject(targetValue)
159 | if (jsonObject.has("random") && jsonObject.has("length") && jsonObject.has("key")) {
160 | val randomSeed = jsonObject.optString("random", "a1b2c3d4e5f6g7h8i9k0l")
161 | val len = jsonObject.optInt("length", 10)
162 | val updateTime = jsonObject.optLong("updateTime", -1L)
163 | val key = jsonObject.getString("key")
164 | val defaultValue = jsonObject.optString("defaultValue")
165 | if (updateTime == -1L) {
166 | val result = randomSeed.random(len)
167 | param.result = result
168 | } else {
169 | val sp = AndroidAppHelper.currentApplication()
170 | .getSharedPreferences("me.simpleHook", Context.MODE_PRIVATE)
171 | val oldTime = sp.getLong("time_$key", 0L)
172 | val oldRandom = sp.getString("random_$key", defaultValue)
173 | val currentTime = System.currentTimeMillis() / 1000
174 | if (currentTime - updateTime >= oldTime) {
175 | val result = randomSeed.random(len)
176 | sp.edit().putString("random_$key", result).apply()
177 | sp.edit().putLong("time_$key", currentTime).apply()
178 | param.result = result
179 | } else {
180 | param.result = oldRandom
181 | }
182 | }
183 | }
184 | } catch (e: Exception) {
185 | param.result = targetValue
186 | }
187 | } else {
188 | param.result = targetValue
189 | }
190 | }
191 |
192 | private fun hookParamsValue(
193 | param: XC_MethodHook.MethodHookParam,
194 | values: String,
195 | className: String,
196 | methodName: String,
197 | params: String
198 | ) {
199 | try {
200 | for (i in param.args.indices) {
201 | if (values.split(",")[i] == "") continue
202 | val targetValue = getDataTypeValue(values.split(",")[i])
203 | param.args[i] = targetValue
204 | }
205 | } catch (e: java.lang.Exception) {
206 | val list = listOf(getTip("errorType") + "HookParamsError",
207 | getTip("solution") + getTip("paramsNotEqualValues"),
208 | getTip("filledClassName") + className,
209 | getTip("filledMethodParams") + "$methodName($params)",
210 | getTip("detailReason") + e.stackTraceToString())
211 | LogUtil.outLog(list, "Error HookParamsError")
212 | }
213 | }
214 |
215 | private fun recordParamsValue(
216 | className: String, param: XC_MethodHook.MethodHookParam
217 | ) {
218 | val type = if (LanguageUtils.isNotChinese()) "Param value" else "参数值"
219 | val list = mutableListOf()
220 | list.add(getTip("className") + className)
221 | list.add(getTip("methodName") + param.method.name)
222 | val paramLen = param.args.size
223 | if (paramLen == 0) {
224 | list.add(getTip("notHaveParams"))
225 | } else {
226 | for (i in 0 until paramLen) {
227 | list.add("${getTip("param")}${i + 1}: ${getObjectString(param.args[i] ?: "null")}")
228 | }
229 | }
230 | val items = getStackTrace()
231 | val logBean = LogBean(type, list + items, hostPackageName)
232 | outLogMsg(logBean)
233 | }
234 |
235 | private fun recordReturnValue(
236 | className: String, param: XC_MethodHook.MethodHookParam
237 | ) {
238 | val list = mutableListOf()
239 | val type = if (LanguageUtils.isNotChinese()) "Return value" else "返回值"
240 | list.add(getTip("className") + className)
241 | list.add(getTip("methodName") + param.method.name)
242 | val result = getObjectString(param.result ?: "null")
243 | list.add(getTip("returnValue") + result)
244 | val items = getStackTrace()
245 | val logBean = LogBean(type, list + items, hostPackageName)
246 | outLogMsg(logBean)
247 | }
248 |
249 | private fun recordParamsAndReturn(
250 | className: String, param: XC_MethodHook.MethodHookParam
251 | ) {
252 | val type = if (LanguageUtils.isNotChinese()) "Param&Return Value" else "参返"
253 | val list = mutableListOf()
254 | list.add(getTip("className") + className)
255 | list.add(getTip("methodName") + param.method.name)
256 | val paramLen = param.args.size
257 | if (paramLen == 0) {
258 | list.add(getTip("notHaveParams"))
259 | } else {
260 | for (i in 0 until paramLen) {
261 | list.add("${getTip("param")}${i + 1}: ${getObjectString(param.args[i] ?: "null")}")
262 | }
263 | }
264 | val result = getObjectString(param.result ?: "null")
265 | list.add(getTip("returnValue") + result)
266 | val items = getStackTrace()
267 | val logBean = LogBean(type, list + items, hostPackageName)
268 | outLogMsg(logBean)
269 | }
270 |
271 |
272 | fun readyExtensionHook(
273 | strConfig: String
274 | ) {
275 | try {
276 | if (strConfig.trim().isEmpty()) return
277 | getTip("startExtensionHook").log(hostPackageName)
278 | val configBean = Json.decodeFromString(strConfig)
279 | if (!configBean.all) return
280 | if (configBean.tip) appContext.showToast(msg = "SimpleHook: StartHook")
281 | initExtensionHook(configBean,
282 | DialogHook,
283 | PopupWindowHook,
284 | ToastHook,
285 | HotFixHook,
286 | IntentHook,
287 | ClickEventHook,
288 | VpnCheckHook,
289 | Base64Hook,
290 | SHAHook,
291 | HMACHook,
292 | AESHook,
293 | JSONHook,
294 | WebHook,
295 | ClipboardHook,
296 | ApplicationHook,
297 | SignatureHook,
298 | ContactHook,
299 | SensorMangerHook,
300 | ADBHook,
301 | FileHook,
302 | ExitHook)
303 | } catch (e: Throwable) {
304 | LogUtil.outLog(arrayListOf(getTip("errorType") + getTip("unknownError"),
305 | "config: ${JsonUtil.formatJson(strConfig)}",
306 | getTip("detailReason") + e.stackTraceToString()), "Error Unknown Error")
307 | }
308 | }
309 |
310 | private fun initExtensionHook(
311 | configBean: ExtensionConfig, vararg hooks: BaseHook
312 | ) {
313 | hooks.forEach {
314 | if (it.isInit) return@forEach
315 | it.isInit
316 | it.startHook(configBean)
317 | }
318 | }
319 |
320 | }
321 |
322 |
--------------------------------------------------------------------------------
/hook/Tip.kt:
--------------------------------------------------------------------------------
1 | package me.simpleHook.hook
2 |
3 | import com.google.gson.Gson
4 | import me.simpleHook.util.LanguageUtils
5 |
6 | object Tip {
7 | private const val chineseTip =
8 | "{ \"text\": \"文本:\", \"button\": \"按钮:\", \"callbackType\": \"回调类名:\", \"viewType\": \"控件类型:\", \"encryptType\": \"类型:加密\", \"rawData\": \"原始数据:\", \"encryptResult\": \"加密结果:\", \"decryptResult\": \"解密结果:\", \"key\": \"密钥:\", \"keyAlgorithm\": \"密钥算法:\", \"encrypt\": \"加密\", \"decrypt\": \"解密\", \"isDecrypt\": \"加密/解密:解密\", \"isEncrypt\": \"加密/解密:加密\", \"className\": \"类名:\", \"methodName\": \"方法名:\", \"param\": \"参数\", \"returnValue\": \"返回值:\", \"startCustomHook\": \"开始自定义Hook\", \"startExtensionHook\": \"开始扩展Hook\", \"errorType\": \"错误类型:\", \"solution\": \"解决方案:\", \"filledClassName\": \"所填类名:\", \"filledMethodParams\": \"所填方法(参数):\", \"filledMethodOrField\": \"所填方法(参数)|变量: \", \"detailReason\": \"具体原因:\", \"notFoundClass\": \"请确保填写的类名正确\", \"noSuchMethod\": \"请确保填写的方法名/参数等数据正确\", \"paramsNotEqualValues\": \"请查看修改值个数是否与参数个数相同\", \"useSmali2Config\": \"你的方法/参数/类名填写有问题,请使用【smali转配置】来降低出错的概率\", \"useNormalVersion\": \"请注意,你的机型并不适合使用ROOT版,请使用普通版\", \"notHaveParams\": \"参数:这个方法没有参数!\", \"unknownError\": \"未知错误\", \"encryptOrDecrypt\": \"加密/解密:\", \"result\": \"结果:\", \"setClipboard\": \"写入剪贴板\", \"getClipboard\": \"读取剪贴板\", \"clipboardInfo\": \"信息:\", \"fieldName\": \"变量名:\", \"fieldValue\": \"变量值:\", \"applicationName\": \"Application入口名:\", \"createFile\": \"创建文件\", \"deleteFile\": \"删除文件\", \"readFile\": \"读取文件\", \"writeFile\": \"写出文件\", \"readAssets\": \"读取Assets文件\", \"path\": \"路径:\", \"info\": \"信息(仅显示缓存大小的部分):\", \"notSetCacheSize\": \"没有设置缓存大小\", \"finish\": \"退出: 通过finish()\", \"exit\": \"退出: 通过exit()\", \"killProcess\": \"退出: 通过killProcess()\" }"
9 | private const val englishTip =
10 | "{\n" + "\t\"text\": \"Text: \",\n" + "\t\"button\": \"Button: \",\n" + "\t\"callbackType\": \"callbackType: \",\n" + "\t\"viewType\": \"viewType: \",\n" + "\t\"encryptType\": \"Type: encrypt\",\n" + "\t\"rawData\": \"Raw Data: \",\n" + "\t\"encryptResult\": \"Encrypt result: \",\n" + "\t\"decryptResult\": \"Decrypt result: \",\n" + "\t\"key\": \"Key: \",\n" + "\t\"keyAlgorithm\": \"Key algorithm: \",\n" + "\t\"encrypt\": \"encrypt\",\n" + "\t\"decrypt\": \"decrypt\",\n" + "\t\"isDecrypt\": \"Encrypt/Decrypt: decrypt\",\n" + "\t\"isEncrypt\": \"Encrypt/Decrypt: encrypt\",\n" + "\t\"className\": \"Class name: \",\n" + "\t\"methodName\": \"Method name: \",\n" + "\t\"param\": \"Param\",\n" + "\t\"returnValue\": \"Return value: \",\n" + "\t\"startCustomHook\": \"Start custom hook\",\n" + "\t\"startExtensionHook\": \"start extension hook\",\n" + "\t\"errorType\": \"Error type: \",\n" + "\t\"solution\": \"Solution: \",\n" + "\t\"filledClassName\": \"Filled class name: \",\n" + "\t\"filledMethodParams\": \"Filled method (parameters): \",\n" + "\t\"filledMethodOrField\": \"Filled method(parameters)|Field: \",\n" + "\t\"detailReason\": \"Detail reason: \",\n" + "\t\"notFoundClass\": \"Please make sure the class name is correct\",\n" + "\t\"noSuchMethod\": \"Please make sure that the method name/parameters and other data filled in are correct\",\n" + "\t\"paramsNotEqualValues\": \"Please check whether the number of modified values is the same as the number of parameters\",\n" + "\t\"useSmali2Config\": \"There is a problem with filling in your method/parameter/class name, please use [smali to config] to reduce the probability of errors\",\n" + "\t\"useNormalVersion\": \"Please note that your model is not suitable for the ROOT version, please use the normal version\",\n" + "\t\"notHaveParams\": \"Parameters: This method has no parameters!\",\n" + "\t\"unknownError\": \"Unknown error\",\n" + "\t\"encryptOrDecrypt\": \"Encrypt/Decrypt: \",\n" + "\t\"result\": \"result: \",\n" + "\t\"setClipboard\": \"Write clipboard\",\n" + "\t\"getClipboard\": \"Read clipboard\",\n" + "\t\"clipboardInfo\": \"Info: \",\n" + "\t\"fieldName\": \"Field name: \",\n" + "\t\"fieldValue\": \"Field value: \",\n" + "\t\"applicationName\": \"Application name:\",\n" + "\t\"createFile\": \"Create file\",\n" + "\t\"deleteFile\": \"Delete file\",\n" + "\t\"readFile\": \"Read file\",\n" + "\t\"writeFile\": \"Write file\",\n" + "\t\"readAssets\": \"Read Assets file\",\n" + "\t\"path\": \"Path: \",\n" + "\t\"info\": \"Info(Show only the cache size): \",\n" + "\t\"notSetCacheSize\": \"No cache size set\",\n" + "\t\"finish\": \"Exit: by finish()\",\n" + "\t\"exit\": \"Exit: by exit()\",\n" + "\t\"killProcess\": \"Exit: killProcess()\"\n" + "}"
11 | private val tipMap = Gson().fromJson