首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
Android安全
发新帖
27
16
[原创]《安卓逆向这档事》第二十二课、抓包学得好,牢饭吃得饱(下)
发表于: 2024-11-16 13:14
41695
[原创]《安卓逆向这档事》第二十二课、抓包学得好,牢饭吃得饱(下)
正己
2024-11-16 13:14
41695
 # 一、课程目标 1.了解hook抓包与混淆对抗 2.了解底层网络自吐 3.了解ebpf抓包 4.简单实战加解密协议 # 二、工具 1.教程Demo 2.r0capture&ecapture 3.Reqable 4.wireshark # 三、课程内容 ## 1.Hook抓包&&关键定位&&混淆对抗 Hook 抓包是一种截取应用程序数据包的方法,通过 Hook 应用或系统函数来获取数据流。在应用层 Hook 时,通过查找触发请求的函数来抓包,优点是不受防抓包手段影响,缺点是抓包数据不便于我们分析和筛选。 `常见安卓网络开发框架` | 框架名称 | 描述 | GitHub 地址 | | ------------------ | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | Volley | 由Google开源的轻量级网络库,支持网络请求处理、小图片的异步加载和缓存等功能 | <a href="elink@28dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6Y4L8$3!0Y4L8r3g2Q4x3V1k6$3L8$3I4D9k6i4V1`."><mark class="encrypted">e55K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6Y4L8$3!0Y4L8r3g2Q4x3V1k6$3L8$3I4D9k6i4V1`.</mark></a> | | Android-async-http | 基于Apache HttpClient的一个异步网络请求处理库 | <a href="elink@0e5K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6S2L8X3c8J5L8$3W2V1i4K6u0V1j5i4y4&6L8X3y4Q4x3X3c8Z5N6s2c8H3i4K6u0r3j5h3&6V1M7X3!0A6k6q4)9J5k6r3q4K6P5h3&6U0i4K6u0V1K9s2c8@1M7l9`.`."><mark class="encrypted">77cK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6S2L8X3c8J5L8$3W2V1i4K6u0V1j5i4y4&6L8X3y4Q4x3X3c8Z5N6s2c8H3i4K6u0r3j5h3&6V1M7X3!0A6k6q4)9J5k6r3q4K6P5h3&6U0i4K6u0V1K9s2c8@1M7l9`.`.</mark></a> | | xUtils | 类似于Afinal,但被认为是Afinal的一个升级版,提供了HTTP请求的支持 | <a href="elink@83bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6%4P5h3!0#2k6X3I4X3i4K6u0r3P5q4g2@1K9h3I4K6x3H3`.`."><mark class="encrypted">2b5K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6%4P5h3!0#2k6X3I4X3i4K6u0r3P5q4g2@1K9h3I4K6x3H3`.`.</mark></a> | | OkHttp | 一个高性能的网络框架,已经被Google官方认可,在Android 6.0中底层源码已经使用了OkHttp来替代HttpURLConnection | <a href="elink@5c9K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6K6M7i4g2S2M7X3g2Q4x3V1k6G2K9$3S2@1N6s2l9`."><mark class="encrypted">96cK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6K6M7i4g2S2M7X3g2Q4x3V1k6G2K9$3S2@1N6s2l9`.</mark></a> | | Retrofit | 提供了一种类型安全的HTTP客户端接口,简化了HTTP请求的编写,通常与OkHttp配合使用 | <a href="elink@83bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6K6M7i4g2S2M7X3g2Q4x3V1k6J5k6i4c8J5L8$3k6A6N6l9`.`."><mark class="encrypted">d14K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6K6M7i4g2S2M7X3g2Q4x3V1k6J5k6i4c8J5L8$3k6A6N6l9`.`.</mark></a> | <a href="elink@debK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2U0L8X3u0D9L8$3N6K6i4K6u0W2j5$3!0E0i4K6u0r3L8r3W2&6N6i4c8A6j5h3&6Q4x3V1k6H3i4K6u0r3z5e0b7^5z5e0l9I4y4W2)9J5k6h3S2@1L8h3H3`.">【译】OkHttp3 拦截器(Interceptor)</a> 拦截器是 OkHttp 提供的对 Http 请求和响应进行统一处理的强大机制,它可以实现网络监听、请求以及响应重写、请求失败充实等功能。 OkHttp 中的 Interceptor 就是典型的责任链的实现,它可以设置任意数量的 Intercepter 来对网络请求及其响应做任何中间处理,比如设置缓存,Https证书认证,统一对请求加密/防篡改社会,打印log,过滤请求等等。 OkHttp 中的拦截器分为 Application Interceptor(应用拦截器) 和 NetWork Interceptor(网络拦截器)两种 - Network Interceptor(网络拦截器) 通过调用 OkHttpClient.Builder 的 addNetworkInterceptor() 方法来注册网络拦截器 ```java OkHttpClient client = new OkHttpClient.Builder() .addNetworkInterceptor(new LoggingInterceptor()) .build(); Request request = new Request.Builder() .url("https://www.52pojie.cn/") .header("User-Agent", "OkHttp Example") .build(); Response response = client.newCall(request).execute(); response.body().close(); ``` 参考项目: <a href="elink@d6dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6K6K9i4W2#2K9X3W2W2i4K6u0r3e0$3E0t1N6s2c8H3e0r3!0Y4k6$3g2J5i4K6u0V1c8Y4u0A6k6r3p5`.">OkHttpLogger-Frida</a> `源码解析:` <a href="elink@9abK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6K6K9i4W2#2K9X3W2W2i4K6u0r3L8$3E0Z5N6s2c8H3i4K6g2X3k6X3W2F1k6q4)9J5c8X3u0D9L8$3u0Q4x3V1k6V1j5K6j5&6j5X3t1J5k6X3q4W2j5X3t1@1y4h3u0S2z5o6l9K6k6r3x3K6x3K6R3&6j5X3c8X3k6r3x3J5x3X3u0X3y4e0R3@1k6e0x3@1i4K6u0r3L8r3W2T1M7X3q4J5P5i4y4Q4x3V1k6K6M7X3y4Q4x3V1k6E0j5h3W2F1i4K6u0r3K9X3q4$3j5g2)9J5c8X3y4G2L8g2)9J5c8Y4y4A6L8X3N6D9k6h3#2S2L8W2)9J5c8X3!0C8K9s2c8@1M7q4)9J5c8V1!0C8d9s2c8@1M7p5k6A6L8X3c8W2M7W2)9J5k6h3A6S2N6X3q4Q4x3U0y4x3y4U0R3`.">定位OkHttpClient关键点</a> ```java /** * 查找并配置OkHttpClient的Client和Builder类。 * 该方法通过反射扫描指定类的字段和方法来确定其是否符合OkHttpClient的结构特征。 * 如果找到符合的类,则会进一步配置和注入相关拦截器。 * * @param classes 当前扫描的类 * @param className 类名,用于查找和调试 */ private void findClientAndBuilderAndBuildAnd(Class classes, String className) { try { // 确认类是final且静态 if (Modifier.isFinal(classes.getModifiers()) && Modifier.isStatic(classes.getModifiers())) { int listCount = 0; // 记录List类型字段的数量 int finalListCount = 0; // 记录final修饰的List字段数量 int listInterfaceCount = 0;// 记录List中包含接口的字段数量 Field[] fields = classes.getDeclaredFields(); Field.setAccessible(fields, true); // 设置字段访问权限 for (Field field : fields) { String type = field.getType().getName(); if (type.contains(List.class.getName())) { listCount++; // 判断字段是否为List类型 // 检查List是否是接口类型 Class genericClass = getGenericClass(field); if (null != genericClass && genericClass.isInterface()) { listInterfaceCount++; } } // 判断字段是否为final修饰的List类型 if (type.contains(List.class.getName()) && Modifier.isFinal(field.getModifiers())) { finalListCount++; } } // 符合OkHttpClient特征的条件检查 if (listCount == 4 && finalListCount == 2 && listInterfaceCount == 2) { // 获取并确认OkHttpClient的包结构和父类 Class OkHttpClientClazz = classes.getEnclosingClass(); if (Cloneable.class.isAssignableFrom(OkHttpClientClazz)) { OkCompat.Cls_OkHttpClient = OkHttpClientClazz.getName(); if (null != classes && null != classes.getPackage()) { Compat_PackageName = classes.getPackage().getName(); } Class builderClazz = classes; // 查找并注入拦截器 find_interceptor(builderClazz); // 查找OkHttpClient相关类 findClientAbout(OkHttpClientClazz); findTag1 = true; // 标记找到目标 } } } } catch (Throwable th) { // 捕获所有异常以防止中断流程,但不处理 } } /** * 查找并注入Interceptor拦截器到Builder类中。 * 此方法会扫描Builder类的字段,找到符合拦截器的字段并进行配置。 * * @param builderClazz 需要查找的Builder类 */ private void find_interceptor(Class builderClazz) { // 检查包名是否符合条件 if (!checkPackage(builderClazz)) return; Field[] declaredFields = builderClazz.getDeclaredFields(); Field.setAccessible(declaredFields, true); // 设置字段访问权限 int index = 0; // 用于计数找到的拦截器字段 for (Field field : declaredFields) { // 检查字段是否为final修饰的List类型且包含接口 if (List.class.isAssignableFrom(field.getType()) && Modifier.isFinal(field.getModifiers()) && getGenericClass(field).isInterface()) { if (index == 0) { // 注入自定义Interceptor,提供给JS调用的回调 findInterceptor(field); index++; } } } } ``` <a href="elink@4a4K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6K6K9i4W2#2K9X3W2W2i4K6u0r3e0$3E0t1N6s2c8H3e0r3!0Y4k6$3g2J5i4K6u0V1c8Y4u0A6k6r3q4Q4x3V1k6T1L8r3!0T1i4K6u0r3j5K6M7H3k6r3p5I4y4X3b7I4x3o6N6U0y4U0N6V1y4o6f1I4k6o6M7I4x3e0u0U0k6X3g2W2y4$3g2W2x3o6V1H3y4e0R3&6j5e0f1J5y4#2)9J5c8X3!0C8K9s2c8@1M7q4)9#2k6Y4m8G2K9$3g2J5i4K6u0W2K9Y4y4Q4x3U0y4x3y4o6R3&6">拦截器加载关键点</a> ```java /** * hookRealCall - 拦截 OkHttp 的 RealCall 类的网络请求。 * 该方法通过拦截 RealCall 类的 `enqueue`(异步请求)和 `execute`(同步请求)方法, * 实现对网络请求和响应的捕获和处理。 * * @param {string} realCallClassName - OkHttp RealCall 类的完整类名。 */ function hookRealCall(realCallClassName) { Java.perform(function () { console.log(" ........... hookRealCall : " + realCallClassName) // 获取 RealCall 类 var RealCall = Java.use(realCallClassName) // 检查是否定义了 Cls_CallBack 类(用于异步请求拦截) if ("" != Cls_CallBack) { // 拦截 RealCall 类中的异步方法 enqueue RealCall[M_Call_enqueue].overload(Cls_CallBack).implementation = function (callback) { // 获取 callback 的类 var realCallBack = Java.use(callback.$className) // 拦截 callback 中的 onResponse 方法,修改返回的响应数据 realCallBack[M_CallBack_onResponse].overload(Cls_Call, Cls_Response).implementation = function(call, response) { // 使用自定义的 buildNewResponse 方法创建新的响应数据 var newResponse = buildNewResponse(response) // 继续执行原始的 onResponse 方法,传入新的响应数据 this[M_CallBack_onResponse](call, newResponse) } // 调用原始的 enqueue 方法,传入修改后的 callback this[M_Call_enqueue](callback) // 释放 callback 类引用 realCallBack.$dispose } } // 拦截 RealCall 类中的同步方法 execute RealCall[M_Call_execute].overload().implementation = function () { // 调用原始的 execute 方法,获取响应数据 var response = this[M_Call_execute]() // 使用自定义的 buildNewResponse 方法创建新的响应数据 var newResponse = buildNewResponse(response) // 返回新的响应数据 return newResponse; } }) } ``` `使用操作:` 1.将 `okhttpfind.dex` 拷贝到 `/data/local/tmp/` 目录下(顺带设置一下777权限) 2.执行命令启动`frida -U wuaipojie -l okhttp_poker.js` 可追加 `-o [output filepath]`保存到文件 3.执行find()和hold()方法看看效果 ``` D:\Program Files\WORKON_HOME\frida16\frida-agent-example>frida -U wuaipojie -l okhttp_poker.js ____ / _ | Frida 16.1.3 - A world-class dynamic instrumentation toolkit | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at https://frida.re/docs/home/ . . . . . . . . Connected to Redmi K30 (id=30d9b4bf) Attaching... ------------------------- OkHttp Poker by SingleMan [V.20201130]------------------------------------ API: >>> find() 检查是否使用了Okhttp & 是否可能被混淆 & 寻找okhttp3关键类及函数 >>> switchLoader("okhttp3.OkHttpClient") 参数:静态分析到的okhttpclient类名 >>> hold() 开启HOOK拦截 >>> history() 打印可重新发送的请求 >>> resend(index) 重新发送请求 ---------------------------------------------------------------------------------------- [Redmi K30::wuaipojie ]-> find() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 未 混 淆 (仅参考)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ likelyClazzList size :352 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Start Find~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Find Result~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ var Cls_Call = "okhttp3.Call"; var Cls_CallBack = "okhttp3.Callback"; var Cls_OkHttpClient = "okhttp3.OkHttpClient"; var M_rsp$builder_build = "build"; var M_rsp_newBuilder = "newBuilder"; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Find Complete~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [Redmi K30::wuaipojie ]-> hold() [Redmi K30::wuaipojie ]-> ........... hookRealCall : okhttp3.RealCall ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── | URL: http://192.168.124.21:5000/get_user_data | | Method: GET | | Request Headers: 0 | no headers | |--> END | | URL: http://192.168.124.21:5000/get_user_data | | Status Code: 200 / OK | | Response Headers: 5 | ┌─Server: Werkzeug/2.3.3 Python/3.10.11 | ┌─Date: Sun, 27 Oct 2024 04:27:52 GMT | ┌─Content-Type: application/json | ┌─Content-Length: 104 | └─Connection: close | | Response Body: | {"user_data":"{\"user_id\": \"zj2595\", \"is_vip\": true, \"vip_level\": \"5\", \"coin_amount\": 115}"} | |<-- END HTTP └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ``` ## 2.底层网络自吐&r0capture 问题:如果app不是用okhttp开发的呢?或者混淆定位不到? [[原创]android抓包学习的整理和归纳](https://bbs.kanxue.com/thread-267940.htm) <a href="elink@a09K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6J5x3s2W2K6N6h3g2Q4x3V1k6J5x3r3y4S2M7s2c8#2M7X3f1`.">r0capture开源地址</a> ### 1.java 层 http 发包   `Hook实现` ```js // 使用 Java.use 方法获取 java.net.SocketOutputStream 类,并重写 socketWrite0 方法 Java.use("java.net.SocketOutputStream").socketWrite0.overload('java.io.FileDescriptor', '[B', 'int', 'int').implementation = function (fd, bytearry, offset, byteCount) { // 调用原始的 socketWrite0 方法 var result = this.socketWrite0(fd, bytearry, offset, byteCount); // 创建一个消息对象用于存储数据 var message = {}; message["function"] = "HTTP_send"; // 标识为 HTTP 发送操作 message["ssl_session_id"] = ""; // SSL 会话 ID 为空 // 获取本地地址和端口 message["src_addr"] = ntohl(ipToNumber((this.socket.value.getLocalAddress().toString().split(":")[0]).split("/").pop())); message["src_port"] = parseInt(this.socket.value.getLocalPort().toString()); // 获取远程地址和端口 message["dst_addr"] = ntohl(ipToNumber((this.socket.value.getRemoteSocketAddress().toString().split(":")[0]).split("/").pop())); message["dst_port"] = parseInt(this.socket.value.getRemoteSocketAddress().toString().split(":").pop()); // 获取调用栈信息 message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); // 将要发送的数据拷贝到内存中 var ptr = Memory.alloc(byteCount); for (var i = 0; i < byteCount; ++i) Memory.writeS8(ptr.add(i), bytearry[offset + i]); // 发送消息和数据 send(message, Memory.readByteArray(ptr, byteCount)); // 返回原始方法的结果 return result; } // 使用 Java.use 方法获取 java.net.SocketInputStream 类,并重写 socketRead0 方法 Java.use("java.net.SocketInputStream").socketRead0.overload('java.io.FileDescriptor', '[B', 'int', 'int', 'int').implementation = function (fd, bytearry, offset, byteCount, timeout) { // 调用原始的 socketRead0 方法 var result = this.socketRead0(fd, bytearry, offset, byteCount, timeout); // 创建一个消息对象用于存储数据 var message = {}; message["function"] = "HTTP_recv"; // 标识为 HTTP 接收操作 message["ssl_session_id"] = ""; // SSL 会话 ID 为空 // 获取远程地址和端口(作为源地址) message["src_addr"] = ntohl(ipToNumber((this.socket.value.getRemoteSocketAddress().toString().split(":")[0]).split("/").pop())); message["src_port"] = parseInt(this.socket.value.getRemoteSocketAddress().toString().split(":").pop()); // 获取本地地址和端口(作为目标地址) message["dst_addr"] = ntohl(ipToNumber((this.socket.value.getLocalAddress().toString().split(":")[0]).split("/").pop())); message["dst_port"] = parseInt(this.socket.value.getLocalPort()); // 获取调用栈信息 message["stack"] = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); // 如果读取到的数据字节数大于 0,将数据拷贝到内存并发送 if (result > 0) { var ptr = Memory.alloc(result); for (var i = 0; i < result; ++i) Memory.writeS8(ptr.add(i), bytearry[offset + i]); send(message, Memory.readByteArray(ptr, result)); } // 返回原始方法的结果 return result; } ``` 通过拦截 Java 中的 `socketWrite0` 和 `socketRead0` 方法,在数据发送和接收时收集相关信息并发送给指定的接收方,以便进行监控或调试 ### 2.java 层 https 发包  `Hook实现` ```js // 拦截 SSLOutputStream 类的 write 方法 Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLOutputStream").write.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) { // 调用原始的 write 方法 var result = this.write(bytearry, int1, int2); // 获取当前调用栈的字符串形式,存储 SSL 数据写入时的调用栈 SSLstackwrite = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); // 返回原始方法的结果 return result; } // 拦截 SSLInputStream 类的 read 方法 Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream").read.overload('[B', 'int', 'int').implementation = function (bytearry, int1, int2) { // 调用原始的 read 方法 var result = this.read(bytearry, int1, int2); // 获取当前调用栈的字符串形式,存储 SSL 数据读取时的调用栈 SSLstackread = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString(); // 返回原始方法的结果 return result; } ``` 拦截了 `SSLOutputStream` 和 `SSLInputStream` 类的 `write` 和 `read` 方法,在进行数据读写时获取当前的调用栈信息 ### 3.native 层 http 发包  | 函数名称 | 描述 | | ---------------------- | ----------------------------------------------------- | | native.socketWrite0 | 这是一个 native 方法,负责从 Java 层向底层网络接口写入数据。 | | libopenjdk.so.NET_Send | 这是 `libopenjdk.so` 中的一个函数,调用底层的 `sendto` 方法,用于发送数据。 | | libc.so.sendto | 这是一个底层系统调用函数,将数据发送到指定的网络地址。 | | native.socketRead0 | 这是一个 native 方法,用于从底层网络接口读取数据。 | | libopenjdk.so.NET_Read | 这是 `libopenjdk.so` 中的一个函数,调用底层的 `recvfrom` 方法,负责接收数据。 | | libopenjdk.so.recvfrom | 这是一个底层系统调用函数,用于从网络接口接收数据包。 | `Hook实现` ```js // 获取 libc.so 库中的 sendto 和 recvfrom 函数的指针 var sendtoPtr = Module.getExportByName("libc.so", "sendto"); var recvfromPtr = Module.getExportByName("libc.so", "recvfrom"); console.log("sendto:", sendtoPtr, ", recvfrom:", recvfromPtr); // 拦截 sendto 函数 // sendto(int fd, const void *buf, size_t n, int flags, const struct sockaddr *addr, socklen_t addr_len) Interceptor.attach(sendtoPtr, { onEnter: function(args) { // 获取文件描述符 fd var fd = args[0]; // 获取要发送的缓冲区指针 buff var buff = args[1]; // 获取数据大小 size var size = args[2]; // 获取套接字的相关信息 var sockdata = getSocketData(fd.toInt32()); console.log(sockdata); // 打印缓冲区的十六进制内容 console.log(hexdump(buff, { length: size.toInt32() })); }, onLeave: function(retval) { // 离开 sendto 函数时不做额外处理 } }); // 拦截 recvfrom 函数 // recvfrom(int fd, void *buf, size_t n, int flags, struct sockaddr *addr, socklen_t *addr_len) Interceptor.attach(recvfromPtr, { onEnter: function(args) { // 获取文件描述符 fd this.fd = args[0]; // 获取缓冲区指针 buff this.buff = args[1]; // 获取数据大小 size this.size = args[2]; }, onLeave: function(retval) { // 获取套接字的相关信息 var sockdata = getSocketData(this.fd.toInt32()); console.log(sockdata); // 打印接收到的缓冲区的十六进制内容 console.log(hexdump(this.buff, { length: this.size.toInt32() })); } }); ``` 拦截 `sendto` 和 `recvfrom` 函数,捕获发送和接收的数据包。`onEnter` 钩子函数用于在函数调用前处理参数,获取文件描述符和缓冲区地址,调用 `hexdump` 打印缓冲区内容以便查看实际发送或接收的数据 ### 4.native 层 https 发包  `Hook实现` ```js // 获取 libc.so 库中的 write 和 read 函数的指针 var writePtr = Module.getExportByName("libc.so", "write"); var readPtr = Module.getExportByName("libc.so", "read"); console.log("write:", writePtr, ", read:", readPtr); // 拦截 write 函数 // write(int fd, const void *buf, size_t count) Interceptor.attach(writePtr, { onEnter: function(args) { // 获取文件描述符 fd var fd = args[0]; // 获取写入的数据缓冲区指针 buff var buff = args[1]; // 获取数据大小 size var size = args[2]; // 获取套接字信息(假设 getSocketData 是自定义函数) var sockdata = getSocketData(fd.toInt32()); // 如果套接字是 TCP 类型,打印相关数据 if (sockdata.indexOf("tcp") !== -1) { console.log(sockdata); console.log(hexdump(buff, { length: size.toInt32() })); } }, onLeave: function(retval) { // 离开 write 函数时不做额外处理 } }); // 拦截 read 函数 // read(int fd, void *buf, size_t count) Interceptor.attach(readPtr, { onEnter: function(args) { // 获取文件描述符 fd this.fd = args[0]; // 获取读取的缓冲区指针 buff this.buff = args[1]; // 获取数据大小 size this.size = args[2]; }, onLeave: function(retval) { // 获取套接字信息 var sockdata = getSocketData(this.fd.toInt32()); // 如果套接字是 TCP 类型,打印相关数据 if (sockdata.indexOf("tcp") !== -1) { console.log(sockdata); console.log(hexdump(this.buff, { length: this.size.toInt32() })); } } }); // 获取 libssl.so 中的 SSL_write、SSL_read 和 SSL_get_rfd 函数的指针 var sslWritePtr = Module.getExportByName("libssl.so", "SSL_write"); var sslReadPtr = Module.getExportByName("libssl.so", "SSL_read"); console.log("sslWrite:", sslWritePtr, ", sslRead:", sslReadPtr); // 获取 SSL_get_rfd 函数的指针,用于从 SSL 结构体中获取文件描述符 var sslGetFdPtr = Module.getExportByName("libssl.so", "SSL_get_rfd"); // 使用 NativeFunction 创建对 SSL_get_rfd 函数的调用 var sslGetFdFunc = new NativeFunction(sslGetFdPtr, 'int', ['pointer']); // 拦截 SSL_write 函数 // int SSL_write(SSL *ssl, const void *buf, int num) Interceptor.attach(sslWritePtr, { onEnter: function(args) { // 获取 SSL 对象指针 var sslPtr = args[0]; // 获取要发送的缓冲区指针 var buff = args[1]; // 获取数据大小 var size = args[2]; // 使用 SSL_get_rfd 获取文件描述符 var fd = sslGetFdFunc(sslPtr); // 获取套接字的数据(假设 getSocketData 是自定义函数) var sockdata = getSocketData(fd); // 打印套接字数据和发送数据的十六进制内容 console.log(sockdata); console.log(hexdump(buff, { length: size.toInt32() })); }, onLeave: function(retval) { // 离开 SSL_write 函数时不做额外处理 } }); // 拦截 SSL_read 函数 // int SSL_read(SSL *ssl, void *buf, int num) Interceptor.attach(sslReadPtr, { onEnter: function(args) { // 获取 SSL 对象指针 this.sslPtr = args[0]; // 获取接收缓冲区指针 this.buff = args[1]; // 获取接收数据的大小 this.size = args[2]; }, onLeave: function(retval) { // 使用 SSL_get_rfd 获取文件描述符 var fd = sslGetFdFunc(this.sslPtr); // 获取套接字的数据 var sockdata = getSocketData(fd); // 打印套接字数据和接收到的十六进制数据 console.log(sockdata); console.log(hexdump(this.buff, { length: this.size.toInt32() })); } }); ``` `r0capture简介` - 仅限安卓平台,测试安卓7-14 可用 ; - 无视所有证书校验或绑定; - 通杀TCP/IP四层模型中的应用层中的全部协议; - 通杀协议包括:Http,WebSocket,Ftp,Xmpp,Imap,Smtp,Protobuf等等、以及它们的SSL版本; - 通杀所有应用层框架,包括HttpUrlConnection、Okhttp1/3/4、Retrofit/Volley等等; - 无视加固 局限:部分开发实力过强的大厂或框架,采用的是自身的SSL框架,比如WebView、部分融合App、小程序或Flutter,这部分目前暂未支持。 ``` python3 r0capture.py -U wuaipojie -v -p test.pcap ``` <a href="elink@a39K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2%4K9i4u0W2M7$3S2S2M7X3E0Q4x3X3g2G2M7X3N6Q4x3V1k6V1L8%4N6F1L8r3!0S2k6q4)9J5k6h3S2@1L8h3H3`.">wireshark下载地址</a> ## 3.ebpf抓包实战&ecapture ### 1.什么是ebpf <a href="elink@8fbK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6W2j5Y4m8X3i4K6u0W2K9h3!0Q4x3V1k6%4K9r3q4@1i4K6u0V1K9i4y4Q4x3X3c8W2j5Y4m8X3i4K6u0r3">what-is-ebpf</a> eBPF是一个运行在 Linux 内核里面的虚拟机组件,它可以在无需改变内核代码或者加载内核模块的情况下,安全而又高效地拓展内核的功能。  ### 2.ebpf的功能之网络抓包 | 功能 | 描述 | 优势 | | ---------- | ---------------------------------------------------------- | ------------------------------------------------ | | **系统调用监控** | 使用 eBPF 脚本监控应用程序的系统调用,帮助分析应用行为。 | - 不需要修改目标程序<br>- 不易被应用程序检测<br>- 性能开销低 | | **应用程序插桩** | 通过 kprobe/uprobe/tracepoints/USDT 对应用程序进行动态插桩,用于监视或修改程序状态。 | - 高度便携<br>- 无需重新编译应用程序<br>- 支持内核和用户空间 | | **性能问题分析** | 利用 eBPF 监控内核关键路径,识别性能瓶颈。 | - 直接在内核层面工作,减少干扰<br>- 开销低,准确性高<br>- 易于实施,已有工具支持 | | **网络抓包** | 在内核网络层面上使用 eBPF 实现高效的数据包捕获,包括 HTTPS 流量。 | - 无需设置代理或使用其他中间件<br>- 支持加密流量的捕获(理论上)<br>- 更加安全可靠 | <a href="elink@3e5K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6Y4L8$3A6#2k6g2)9J5c8X3g2U0j5i4m8@1N6i4u0W2">ecapture</a> <a href="elink@506K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6W2j5$3q4H3N6s2g2J5k6g2)9J5k6h3y4U0i4K6u0r3P5X3S2Q4x3V1k6W2P5r3q4E0M7r3I4W2M7#2)9J5c8X3q4F1k6s2u0G2K9h3c8Q4x3X3g2Z5N6r3#2D9">官方案例</a> eCapture主要利用了eBPF和HOOK技术: - **eBPF加载机制**:利用eBPF技术进行数据包的捕获和处理,eBPF程序是事件驱动的,当内核或应用程序通过某个挂钩点时运行。预定义的钩子包括系统调用、函数入口/出口、内核跟踪点、网络事件和其他几个; - **HOOK机制**:使用eBPF uprobe相关函数进行用户态函数的HOOK,支持对不同编程语言实现的加密库进行HOOK,如OpenSSL、GnuTLS、NSS/NSPR。 eCapture 的工作原理涉及到用户态和内核态。用户态就是运行应用程序的地方,比如各种 App。在这个区域中,eCapture 通过一个共享的模块(Shared Object)获取应用程序的网络数据。然后,它将这些数据传递给内核态的 eBPF 程序进行分析和处理。 在内核空间,eCapture 通过 eBPF 插件捕捉网络层的数据流,比如数据包是从哪里来的、发到了哪里去。这一过程不需要修改应用程序本身,所以对系统性能影响很小。  安卓设备的内核版本只有在5.10版本上才可以进行无任何修改的开箱抓包操作(如果你的设备是安卓13,应该可以正常使用ecapture。低于13的安卓设备,如果内核是5.10,理论也是可行的。 因为安卓使用的linux内核的ebpf环境受内核版本号的影响,而工作良好的ebpf接口是在内核5.5版本时才全部使能。) 可通过adb命令查看自己的设备的内核版本 ``` adb shell cat /proc/version 或者adb shell uname -a ``` <a href="elink@e2dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6Y4L8$3A6#2k6g2)9J5c8X3g2U0j5i4m8@1N6i4u0W2i4K6u0r3M7X3g2D9k6h3q4K6k6i4x3`.">下载地址</a> ``` adb push ecapture /data/local/tmp/ adb shell chmod 777 /data/local/tmp/ecapture ``` `使用说明` ``` NAME: eCapture - 通过eBPF捕获SSL/TLS明文数据,无需安装CA证书。支持Linux/Android内核,适用于amd64/arm64架构。 USAGE: eCapture [flags] VERSION: androidgki_arm64:v0.8.9:6.5.0-1025-azure COMMANDS: bash 捕获bash命令的执行信息 gotls 捕获使用TLS/HTTPS加密的Golang程序的明文通信 help 获取有关任何命令的帮助信息 tls 用于捕获TLS/SSL明文内容,无需CA证书。支持OpenSSL 1.0.x/1.1.x/3.x或更新版本。 DESCRIPTION: eCapture(旁观者)是一个可以捕获如HTTPS和TLS等明文数据包的工具,且不需要安装CA证书。 它还可以捕获bash命令,适用于安全审计场景,比如mysqld数据库审计等(在Android中禁用)。 支持Linux(Android)系统,内核版本为X86_64 4.18或aarch64 5.5及更高版本。 项目仓库:https://github.com/gojue/ecapture 官方主页:https://ecapture.cc 使用方法: ecapture tls -h ecapture bash -h Docker使用示例: docker pull gojue/ecapture:latest docker run --rm --privileged=true --net=host -v ${HOST_PATH}:${CONTAINER_PATH} gojue/ecapture -h NAME: tls - 用于捕获TLS/SSL明文内容,无需CA证书。支持OpenSSL 1.0.x/1.1.x/3.x及更新版本。 USAGE: eCapture tls [flags] DESCRIPTION: 使用eBPF uprobe/TC捕获进程事件数据和网络数据。还支持pcap-NG格式。 示例: ecapture tls -m [text|keylog|pcap] [flags] [pcap过滤表达式(用于pcap模式)] ecapture tls -m pcap -i wlan0 -w save.pcapng host 192.168.1.1 and tcp port 443 ecapture tls -l save.log --pid=3423 ecapture tls --libssl=/lib/x86_64-linux-gnu/libssl.so.1.1 ecapture tls -m keylog --pcapfile save_3_0_5.pcapng --ssl_version="openssl 3.0.5" --libssl=/lib/x86_64-linux-gnu/libssl.so.3 ecapture tls -m pcap --pcapfile save_android.pcapng -i wlan0 --libssl=/apex/com.android.conscrypt/lib64/libssl.so --ssl_version="boringssl 1.1.1" tcp port 443 Docker使用示例: docker pull gojue/ecapture docker run --rm --privileged=true --net=host -v /etc:/etc -v /usr:/usr -v ${PWD}:/output gojue/ecapture tls -m pcap -i wlp3s0 --pcapfile=/output/ecapture.pcapng tcp port 443 OPTIONS: --cgroup_path="/sys/fs/cgroup" 设置cgroup路径,默认值:/sys/fs/cgroup。 -h, --help[=false] 获取tls命令的帮助信息 -i, --ifname="" (TC Classifier) 要附加探针的网络接口名称 -k, --keylogfile="ecapture_openssl_key.og" 存储SSL/TLS密钥的文件,eCapture捕获加密通信中的密钥并将其保存到该文件 --libssl="" 指定libssl.so文件路径,默认从curl中自动查找 -m, --model="text" 捕获模型,可以是:text(明文内容),pcap/pcapng(原始数据包格式),key/keylog(SSL/TLS密钥) -w, --pcapfile="save.pcapng" 将原始数据包以pcapng格式写入文件 --ssl_version="" 指定OpenSSL/BoringSSL版本,例如:--ssl_version="openssl 1.1.1g" 或 --ssl_version="boringssl 1.1.1" GLOBAL OPTIONS: -b, --btf=0 启用BTF模式(0:自动选择;1:核心模式;2:非核心模式) -d, --debug[=false] 启用调试日志 --eventaddr="" 设置接收捕获事件的服务器地址。默认值与logaddr相同(例如:tcp://127.0.0.1:8090) --hex[=false] 以十六进制字符串打印字节数据 --listen="localhost:28256" 设置HTTP服务器的监听地址,默认值:127.0.0.1:28256 -l, --logaddr="" 设置日志服务器的地址。例如:-l /tmp/ecapture.log 或 -l tcp://127.0.0.1:8080 --mapsize=1024 设置每个CPU的eBPF映射大小(事件缓冲区)。默认值:1024 * PAGESIZE(单位:KB) -p, --pid=0 设置目标进程ID。如果为0,则目标为所有进程 -u, --uid=0 设置目标用户ID。如果为0,则目标为所有用户 ``` ``` adb shell ps | findstr 应用包名(获取进程pid) ./ecapture tls -p pid -m text ``` ## 4.简单加解密协议实战 `服务端代码:` ```python import hashlib import json import base64 import time from Crypto.Cipher import AES from Crypto.Util.Padding import pad from cryptography.hazmat.primitives import padding from flask import Flask, jsonify, request app = Flask(__name__) # 加密函数 def aes_encrypt(data: str) -> str: key = b'1234567890abcdefwuaipojie0abcdef' iv = b'1234567wuaipojie' # Initialization Vector cipher = AES.new(key, AES.MODE_CBC, iv) encrypted_data = cipher.encrypt(pad(data.encode('utf-8'), AES.block_size)) return base64.b64encode(encrypted_data).decode('utf-8') # 解密函数 def aes_decrypt(encrypted_data: str) -> dict: key = b'1234567890abcdefwuaipojie0abcdef' iv = b'1234567wuaipojie' # Initialization Vector cipher = AES.new(key, AES.MODE_CBC, iv) encrypted_bytes = base64.b64decode(encrypted_data) decrypted_data = cipher.decrypt(encrypted_bytes) unpadder = padding.PKCS7(AES.block_size * 8).unpadder() decrypted_unpadded = unpadder.update(decrypted_data) + unpadder.finalize() decrypted_str = decrypted_unpadded.decode('utf-8') return json.loads(decrypted_str) # 读取用户数据 with open('user_data.json', 'r') as file: user_data = json.load(file) # 写入本地JSON文件 def write_json_file(file_path: str, data: dict): with open(file_path, 'w') as file: json.dump(data, file, indent=4) # 生成签名函数 def generate_signature(user_id: str, coin: int, timestamp: int) -> str: message = f"{user_id}&{coin}&{timestamp}" hash_object = hashlib.md5(message.encode()) return hash_object.hexdigest() @app.route('/get_coin', methods=['POST']) def get_coin(): # 获取加密的数据 encrypted_data = request.json.get('user_data') if not encrypted_data: return jsonify({"error": "数据有误!"}), 400 try: # 解密数据 decrypted_data = aes_decrypt(encrypted_data) # 验证签名 timestamp = int(decrypted_data.get('timestamp')) current_time = int(time.time()*1000) print(timestamp) print(abs(current_time - timestamp)) if abs(current_time - timestamp) > 5000: return jsonify({"error": "请求过期!"}), 400 sign = decrypted_data.get('sign') # 计算签名 expected_sign = generate_signature(decrypted_data["user_id"], 1, timestamp) if sign != expected_sign: return jsonify({"error": "签名验证失败!"}), 401 # 验证成功后,获取用户的金币数量 user_id = decrypted_data.get('user_id') if user_id in user_data['user_id']: user_data['coin_amount'] += 1 # 增加金币数量 write_json_file('user_data.json', user_data) # 写入文件 return jsonify({"投币成功,当前数量为:": user_data['coin_amount']}) else: return jsonify({"error": "用户未找到!"}), 404 except Exception as e: return jsonify({"error": f"处理请求时出错: {str(e)}"}), 500 @app.route('/get_user_data', methods=['GET']) def get_user_data(): # 将数据转换成字符串形式以便于加密 data_str = json.dumps(user_data) return jsonify({"user_data": data_str}) if __name__ == '__main__': app.run(host='192.168.73.82', port=5000) ``` `协议实现:` ```python import json import base64 import hashlib import time from Crypto.Cipher import AES from Crypto.Util.Padding import pad from datetime import datetime import requests # 生成签名函数 def generate_signature(user_id: str, coin: int, timestamp: int) -> str: message = f"{user_id}&{coin}&{timestamp}" hash_object = hashlib.md5(message.encode()) return hash_object.hexdigest() # 加密函数 def aes_encrypt(data: str) -> str: key = b'1234567890abcdefwuaipojie0abcdef' iv = b'1234567wuaipojie' # Initialization Vector cipher = AES.new(key, AES.MODE_CBC, iv) encrypted_data = cipher.encrypt(pad(data.encode('utf-8'), AES.block_size)) return base64.b64encode(encrypted_data).decode('utf-8') # 模拟用户数据 user_data = { "user_id": "zj2595", "timestamp": int(time.time()*1000), # 当前时间的时间戳 "sign": "", # 这个稍后计算并赋值 } # 计算签名 user_data["sign"] = generate_signature(user_data["user_id"], 1, user_data["timestamp"]) # 转换为JSON字符串 data_str = json.dumps(user_data) # 加密数据 encrypted_data = aes_encrypt(data_str) # 发送POST请求 try: response = requests.post( 'http://192.168.73.82:5000/get_coin', json={"user_data": encrypted_data}, ) if response.status_code == 200: print("投币成功") print("Response:", response.json()) else: print(f"Request failed with status code: {response.status_code}") except requests.exceptions.RequestException as e: print(f"请求出现异常: {e}") ``` # 六、视频及课件地址 <a href="elink@398K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6H3j5h3&6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8g2)9J5c8Y4y4Q4x3V1j5I4j5@1k6i4g2p5I4F1x3e0c8B7k6g2N6X3M7q4S2^5L8s2R3K6M7%4W2k6N6#2)9K6c8Y4m8%4k6q4)9K6c8r3&6I4N6e0V1`.">百度云</a> <a href="elink@af7K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2S2L8r3W2&6N6h3&6V1M7X3W2$3k6g2)9J5k6h3y4G2L8g2)9J5c8Y4y4Q4x3V1k6f1d9X3!0w2e0f1D9$3k6s2f1$3P5l9`.`.">阿里云</a> <a href="elink@06aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2T1K9h3I4A6j5X3W2D9K9g2)9J5k6h3y4G2L8g2)9J5c8Y4k6A6k6r3g2G2i4K6u0r3b7W2j5I4N6#2b7@1x3e0q4z5y4%4y4h3i4K6u0r3i4K6y4r3M7%4m8E0i4K6g2X3K9h3c8Q4y4h3k6X3M7X3!0E0i4K6y4p5x3K6x3K6i4K6u0W2y4K6R3^5i4K6t1$3N6X3c8Q4y4h3k6K6L8%4g2J5j5$3g2Q4x3@1b7$3k6r3c8W2x3e0k6V1j5K6j5@1y4K6W2X3x3o6l9$3z5e0c8T1j5h3q4X3y4K6y4S2x3U0t1J5y4e0b7#2x3R3`.`.">哔哩哔哩</a> <a href="elink@474K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6K9d9U0f1&6y4g2)9J5c8V1q4F1k6s2u0G2K9h3c8d9k6i4k6W2M7Y4y4W2">教程开源地址</a> PS:解压密码都是52pj,阿里云由于不能分享压缩包,所以下载exe文件,双击自解压 # 八、参考文档 [炒冷饭汇总抓包姿势-上](https://bbs.kanxue.com/thread-278142.htm) <a href="elink@585K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0L8r3!0#2k6q4)9J5k6i4c8W2L8X3y4W2L8Y4c8Q4x3X3g2U0L8$3#2Q4x3V1k6V1k6i4k6W2L8r3!0H3k6i4u0Q4x3V1k6S2M7Y4c8A6j5$3I4W2i4K6u0r3x3e0j5$3x3K6R3J5z5l9`.`.">安卓 App 逆向课程之四 frida 注入 Okhttp 抓包中篇</a>
登录后可查看完整内容
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
最后于
2024-11-16 20:51 被正己编辑 ,原因:
#工具脚本
#源码框架
#系统相关
#HOOK注入
#协议分析
#逆向分析
收藏
・
27
点赞
・
16
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
mb_mywnpifi
这个讨论对我很有帮助,谢谢!
2026-7-15 18:02
AGSFGBKILO
+1
感谢你分享这么好的资源!
2026-2-23 16:06
PLEBFE
感谢你的贡献,论坛因你而更加精彩!
2025-5-15 01:48
学海无涯-宇
非常支持你的观点!
2025-3-19 03:43
SFQin
为你点赞!
2024-12-6 14:01
yccccc
+1
期待更多优质内容的分享,论坛有你更精彩!
2024-11-27 13:42
mb_lltiqhzx
期待更多优质内容的分享,论坛有你更精彩!
2024-11-25 15:05
vay
+1
感谢你的贡献,论坛因你而更加精彩!
2024-11-21 09:06
西贝Legion
为你点赞!
2024-11-21 08:51
菜小基
感谢你分享这么好的资源!
2024-11-18 10:33
养只猫不好么
+2
感谢你的贡献,论坛因你而更加精彩!
2024-11-18 10:18
OYyunshen
+1
非常支持你的观点!
2024-11-18 10:07
墨穹呢
非常支持你的观点!
2024-11-17 21:09
像风一样呢
你的帖子非常有用,感谢分享!
2024-11-17 14:21
陈某人
你的分享对大家帮助很大,非常感谢!
2024-11-17 00:37
Payne-Wu
+5
这个讨论对我很有帮助,谢谢!
2024-11-16 17:15
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
10
)
墨穹呢
雪 币:
4704
活跃值:
(8122)
能力值:
( LV3,RANK:20 )
在线值:
发帖
2
回帖
224
粉丝
18
关注
私信
墨穹呢
2
楼
感谢分享
2024-11-17 21:09
0
huangjw
雪 币:
6679
活跃值:
(11862)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
510
粉丝
2
关注
私信
huangjw
3
楼
看大佬的文章,学了许多frida的技能。 感谢分享
最后于
2024-11-18 08:56 被huangjw编辑 ,原因:
2024-11-18 08:56
0
养只猫不好么
雪 币:
1643
活跃值:
(4626)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
99
粉丝
1
关注
私信
养只猫不好么
4
楼
感谢分享
2024-11-18 10:18
0
mb_hgshugsg
雪 币:
195
活跃值:
(195)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
2
粉丝
0
关注
私信
mb_hgshugsg
5
楼
感谢分享
2024-11-19 08:52
0
mb_sslwzxww
雪 币:
12
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
6
粉丝
0
关注
私信
mb_sslwzxww
6
楼
感谢分享~
2024-11-20 15:31
0
_winter_
雪 币:
241
活跃值:
(186)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
2
粉丝
0
关注
私信
_winter_
7
楼
感谢分享
2024-11-21 15:17
0
litianp
雪 币:
2870
活跃值:
(2745)
能力值:
( LV2,RANK:10 )
在线值:
发帖
3
回帖
36
粉丝
1
关注
私信
litianp
8
楼
大佬,现在dou音怎么抓? Xposed环境有方便的方法吗
2024-11-22 01:27
0
微微T52G25
雪 币:
3561
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
27
粉丝
1
关注
私信
微微T52G25
9
楼
正哥在这里也能看到你,哈哈
2024-11-25 07:49
0
Hades一KXXY
雪 币:
4837
活跃值:
(2460)
能力值:
( LV2,RANK:10 )
在线值:
发帖
2
回帖
99
粉丝
2
关注
私信
Hades一KXXY
10
楼
期待整点脱壳这一类的视频,现在外面的app都是带壳的,不脱能玩的就很少了
2024-11-26 10:02
0
git_97580helord
雪 币:
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
4
粉丝
0
关注
私信
git_97580helord
11
楼
coole
2026-1-16 19:42
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
正己
18
发帖
61
回帖
110
RANK
关注
私信
他的文章
[原创]《安卓逆向这档事》第二十六课、Unidbg之补完环境我就睡(下)
19170
[原创]《安卓逆向这档事》第二十五课、Unidbg之补完环境我就睡(中)
18464
《安卓逆向这档事》第二十四课、Unidbg之补完环境我就睡(上)
11070
《安卓逆向这档事》第二十三课、黑盒魔法之Unidbg
65808
[原创]《安卓逆向这档事》第二十二课、抓包学得好,牢饭吃得饱(下)
41695
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部