逆向环境的配置 pycharm 和 adb权限
010 Editor 激活码生成器 + 使用方法 https://blog.aoe.top/notes/437 安卓的adb 的权限配置 https://github.com/snowdream/51-android pycharm的关联的目录 https://www.jetbrains.com/help/pycharm/directories-used-by-the-ide-to-store-settings-caches-plugins-and-logs.html
kanxue 2w r0yuse chapter01 keywords
Android Studio多版本共存 配置 1, 迁移 /home/calleng/.gradle2022 为新目录 2, 迁移 /home/calleng/Android/Sdk2022 为新目录 3, 迁移 /home/calleng/AndroidProject2022 为新目录 4, 迁移 /home/calleng/.cache/Google/android-studio-21.1 5, 迁移 /home/calleng/.config/Google/android-studio-21.1 6, 迁移 /home/calleng/option/correct_JDK_1.8.1/ 7, 迁移 /home/calleng/option/android-studio-2022 迁移完成后,在 android-studio-2022 配置中,修改,如下选项 1, SDK, 2, Gradle, 3, AndroidProject, 4, Graddle_Project_JDK_1.8. jeb 的注册, 从这里开始 课时3, 体验Frida内存dump 脱壳(上) 8:00 分钟的时候。 第九课, App的类的加载器 Android 的类加载器 ClassLoader 为抽象类 BootClassLoader预加载常用的类,单栗子模式, 与java 中的BootClassLoader 不同, 它并不是由 C/CPP 代码实现,而是Java实现的 BaseDexClassLoader是 PathClassLoader,DexClassLoader, InMemoryDexClassLoader的父类, 类加载的主要逻辑都是在 BaseDexClassLoader完成的。 SecureClassLoader 继承了 抽象类 ClassLoader, 拓展了ClassLoade类加入了权限方面的功能,加强了安全性, 其子类 URLClassLoader是用 URL路径从 jar 文件中加载类和资源 启动重点关注 是 PathClassLoader 和 DexClassLoader. PathClassLoader 是Android 默认使用的类加载器, 一个apk中, 的 Activity 等类便是在其中加载。 DexClassLoader可以加载任意目录下的 dex/jar/apk/zip 文件, 比PathClassLoader更加灵活, 是实现插件化,热修复, 以及dex加壳的重点。 Android8.0 新引入的 InMemoryDexClassLoader , 从名字上可以看出是直接用于从内存中加载 Dex sailfish 安装的 aosp的谷歌驱动, 一个谷歌, 一个高通 vendor/ vendor/google_devices/ vendor/google_devices/sailfish/ vendor/google_devices/sailfish/android-info.txt vendor/google_devices/sailfish/BoardConfigPartial.mk vendor/google_devices/sailfish/proprietary/ vendor/google_devices/sailfish/proprietary/vendor.img vendor/google_devices/sailfish/device-partial.mk vendor/google_devices/marlin/ vendor/google_devices/marlin/BoardConfigVendor.mk vendor/google_devices/marlin/device-vendor-sailfish.mk vendor/ vendor/qcom/ vendor/qcom/sailfish/ vendor/qcom/sailfish/BoardConfigPartial.mk vendor/qcom/sailfish/proprietary/ vendor/qcom/sailfish/proprietary/lib64/ vendor/qcom/sailfish/proprietary/lib64/libaptX_encoder.so vendor/qcom/sailfish/proprietary/lib64/libbcc.so vendor/qcom/sailfish/proprietary/lib64/libaptXHD_encoder.so vendor/qcom/sailfish/proprietary/lib64/libiperf.so vendor/qcom/sailfish/proprietary/lib64/libLLVM.so vendor/qcom/sailfish/proprietary/lib64/libminui.so vendor/qcom/sailfish/proprietary/ATT_profiles.xml vendor/qcom/sailfish/proprietary/pktlogconf vendor/qcom/sailfish/proprietary/VZW_profiles.xml vendor/qcom/sailfish/proprietary/libaptX_encoder.so vendor/qcom/sailfish/proprietary/libaptXHD_encoder.so vendor/qcom/sailfish/proprietary/ROW_profiles.xml vendor/qcom/sailfish/proprietary/libclcore_neon.bc vendor/qcom/sailfish/proprietary/sanitizer-status vendor/qcom/sailfish/proprietary/libiperf.so vendor/qcom/sailfish/proprietary/com.android.ims.rcsmanager.jar vendor/qcom/sailfish/proprietary/libminui.so vendor/qcom/sailfish/proprietary/libion.so vendor/qcom/sailfish/proprietary/iperf3 vendor/qcom/sailfish/proprietary/com.android.ims.rcsmanager.xml vendor/qcom/sailfish/device-partial.mk vendor/google_devices/ vendor/google_devices/sailfish/ vendor/google_devices/sailfish/android-info.txt vendor/google_devices/marlin/ vendor/google_devices/marlin/BoardConfigVendor.mk vendor/google_devices/marlin/device-vendor-sailfish.mk
案例 车智赢 的hook 脚本 和 总体的java逻辑的提取
function UpgradeBlock() { var UpgradeAppBean = Java.use(\'com.che168.autotradercloud.upgradeapp.UpgradeAppBean\'); // Hook isForce 方法 UpgradeAppBean.isForce.implementation = function () { send(\'Hooked isForce\'); return false; // 强制返回 false,跳过强制更新 }; // Hook isLatestVersion 方法 UpgradeAppBean.isLatestVersion.implementation = function () { send(\'Hooked isLatestVersion\'); return true; // 强制返回 true,表示已是最新版本 }; } // 把最开始的 登陆的参数的 给 hook 出来. // 通过 tradercloud/sealed/login/login.ashx 这个 URI 搜索的到内部的加密算法的位置 loginByPassword() 函数中的 最后的一个参数加密的位置. encodeMD5() // 密码 被 MD5 重新加密了. function encodeMD5() { var SecurityUtil = Java.use(\"com.autohome.ahkit.utils.SecurityUtil\"); SecurityUtil.encodeMD5.implementation = function ( str ){ console.log(\"md5 加密, 明文---> \", str); var result = this.encodeMD5(str); console.log(\" md5 加密后的秘闻 是---> \",result); return result; }; } // 把最开始的 登陆的参数的 给 hook 出来. // 通过 tradercloud/sealed/login/login.ashx 这个 URI 搜索的到内部的加密算法的位置 loginByPassword() function loginByPassword_bakcup() { let UserModel = Java.use(\"com.che168.autotradercloud.user.model.UserModel\"); UserModel.implementation = function (str, str2, str3, responseCallback) { console.log(\"tradercloud/sealed/login/login.ashx 验证密码登陆 是否可通过 \\n\"); console.log(`UserModel.loginByPassword is called: str=${str}, str2=${str2}, str3=${str3}, responseCallback=${responseCallback}`); // 调用原方法并捕获返回值 var result = this.loginByPassword(str, str2, str3, responseCallback); // 执行后打印结果 console.log(`After Execution: result=${result}`); // 返回原方法的结果 return result; }; } function loginByPassword() { let UserModel = Java.use(\"com.che168.autotradercloud.user.model.UserModel\"); // 指定的手机号和密码 const TARGET_PHONE = \"13541722917\"; const TARGET_PASSWORD = \"aA.11111\"; // Hook the loginByPassword method UserModel.loginByPassword.implementation = function (str, str2, str3, responseCallback) { console.log(\"tradercloud/sealed/login/login.ashx 验证密码登陆 是否可通过 \\n\"); // 打印原始参数 console.log(`原始参数: str=${str}, str2=${str2}, str3=${str3}, responseCallback=${responseCallback}`); // 强制替换为指定的手机号和密码 str2 = TARGET_PHONE; str3
c 语言的基础语法.
c 语言的基础. c 语言没有字符串. 一个 int 整形, 占用 4 个字节. ===> sizeof 是指 字节的长度 , 不是元素的个数. ..... 想要得到有多少个元素, 就直接 就是 sizeof(v2)/sizeof(char), 或者 sizeof(v3)/sizeof(int) ==>. 得到元素的个数.... 5 期 day9, 10.4 语法--字符....和 字符串.... 中文实际上使用的是 utf-8 编码的. 一个中文, 字, 占用 3 个字节... # include <stdio.h> # include <string.h> int main(int argc, char const *argv) { // 字符类型,用1个字节来存储。 char v1 = \'w\'; printf(\"v1的值为: %c\\n\", v1); // 字符数组 -> 字符串 char v2 = {\'w\',\'u\',\'p\',\'e\',\'i\',\'q\',\'i\',\'\\0\'}; printf(\"v2的值为: %s\\n\", v2); // 字符数组, sizeof大小 char v3 = \"wupeiqi\"; int length = sizeof(v3)/sizeof(char); printf(\"v3的值为: %s,长度为:%d\\n\", v3, length); // 字符数组 char v4 = \"武沛齐\"; int len = sizeof(v4)/sizeof(char); printf(\"v4的值为: %s,长度为:%d\\n\", v4, len); // 字符串长度 unsigned long dataLen = strlen(v4); // c 语言自身提供的. 不包含 \\0,结束的长度... printf(\"Length: %d\\n\", dataLen); return 0; } // /Users/exp/.idapro/plugins/untitled/cmake-build-debug/untitled // v1的值为: w // v2的值为: wupeiqi // v3的值为: wupeiqi,长度为:8 // v4的值为: 武沛齐,长度为:10 // Length: 9 // // Process finished with exit code 0 指针 其实是 一种类型. 数据类型和 指针是有关系的. char 类型的指针的, 就是 char * , 就是 char 星. 指针类型是 可以 进行,相加 和 相减的... ==> 他根据 他的类型,,, 往后跳多少 ? 跳多少 ? prinf() 是输出 sprintf () 是格式化 ....
Frida检测___对待武沛奇老师的课程需怀有敬畏的心态.
proc检测 .../local/tmp # ps -ef | grep che168 u0_a205 16928 12075 6 18:16 ? 00:00:04 com.che168.autotradercloud u0_a205 17051 12075 0 18:16 ? 00:00:00 com.che168.autotradercloud:ipc u0_a205 17118 12075 2 18:16 ? 00:00:01 com.che168.autotradercloud:pushservice root 17804 17584 2 18:17 pts/1 00:00:00 grep che168 .../local/tmp # ps -A | grep com.che168.autotradercloud 16928 ? 00:00:05 com.che168.autotradercloud 17051 ? 00:00:00 com.che168.autotradercloud:ipc 17118 ? 00:00:01 com.che168.autotradercloud:pushservice .../local/tmp # cd /pro bash: cd: /pro: No such file or directory .../local/tmp # cd /pro proc/ product/ product_services .../local/tmp # cd /proc /proc # cd 16928 /proc/16928 # ls attr/ comm fd/ mem oom_adj root@ smaps syscall autogroup coredump_filter fdinfo/ mountinfo oom_score sched smaps_rollup task/ auxv cpuset io mounts oom_score_adj sched_group_id stack time_in_state cgroup cwd@ limits mountstats pagemap sched_init_task_load stat timerslack_ns clear_refs environ map_files/ net/ personality sched_wake_up_idle statm top_app cmdline exe@ maps ns/ reclaim schedstat status wchan /proc/16928 # maps文件 , 记录了当前app运行时候, 加载的依赖. 全部都存储到了文件中. 车智赢 所有加载的app的so文件,都会在map中存在的. 这里面就有一些 libnative.so 这样的文件. 所以存在于maps文件中. /proc/16928 # cat maps | grep libnative 78f6b5b000-78f6c1b000 r-xp 00000000 103:05 5431580 /data/app/com.che168.autotradercloud-LTGEbEGKyOrdPogMOL27SQ==/lib/arm64/libnative-lib.so 78f6c1c000-78f6c23000 r--p 000c0000 103:05 5431580 /data/app/com.che168.autotradercloud-LTGEbEGKyOrdPogMOL27SQ==/lib/arm64/libnative-lib.so 78f6c23000-78f6c24000 rw-p 000c7000 103:05 5431580 /data/app/com.che168.autotradercloud-LTGEbEGKyOrdPogMOL27SQ==/lib/arm64/libnative-lib.so 79f840f000-79f8412000 r--p 00000000 fd:00 340 /apex/com.android.runtime/lib64/libnativehelper.so 79f8412000-79f8415000 r-xp 00003000 fd:00 340 /apex/com.android.runtime/lib64/libnativehelper.so 79f8415000-79f8416000 rw-p 00006000 fd:00 340 /apex/com.android.runtime/lib64/libnativehelper.so 79f8416000-79f8417000 r--p 00007000 fd:00 340
移动安全_抓包__底层通信_http和SSL请求的本质
例如:你在Python中使用requests模块发送一个http请求,其底层就是使用socket模块+TCP实现发送的请求。 import requests res = requests.get(\"http://192.168.1.37:8080/index\") print(res.text) 如果基于底层的socket模块+TCP协议实现如下: import socket client = socket.socket() client.connect((\'192.168.1.37\', 8080)) content_list = content_list.append(\"GET /index HTTP/1.1\\r\\n\") content_list.append(\"host: wiki.mikecrm.com\\r\\n\") content_list.append(\"user-Agent: test\\r\\n\") content_list.append(\"\\r\\n\") content_string = \"\".join(content_list) client.sendall(content_string.encode(\'utf-8\')) response = b\"\" while True: chunk = client.recv(1024) if not chunk: break response += chunk print(response.decode(\'utf-8\')) client.close() 2.1 http请求 详见示例 --- net.demo2.netd10_ssl01 public class MainActivity extends AppCompatActivity { private Button btn1; // 定义一个按钮 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); // 启用边到边显示 setContentView(R.layout.activity_main);// 设置布局文件 ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {// 处理窗口内边距, 适应系统栏 Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); // 初始化按钮 btn1 = findViewById(R.id.btn1); //通过 ID 找到按钮 btn1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { doRequest(); } }); } private void doRequest() { new Thread() { @Override public void run() { try { // http://wiki.mikecrm.com/index?ajax=1&page=2 Socket socket = new Socket(\"192.168.1.37\", 8080); // 1.构造请求头 StringBuilder sb = new StringBuilder(); sb.append(\"GET /index HTTP/1.1\\r\\n\"); sb.append(\"host: wiki.mikecrm.com\\r\\n\"); sb.append(\"user-Agent: test\\r\\n\"); sb.append(\"\\r\\n\"); // 2.写入数据(发送数据) // java.net.SocketOutputStream OutputStream outputStream = socket.getOutputStream(); outputStream.write(sb.toString().getBytes()); Log.e(\"outputStream的类 => \", outputStream.getClass().getName()); // 3.读取数据(获取数据) // java.net.SocketInputStream InputStream inputStream = socket.getInputStream(); Log.e(\"inputStream的类 => \", inputStream.getClass().getName()); while (true) { byte buffer = new byte; int len = inputStream.read(buffer, 0, buffer.length); if (len == -1) { break; } Log.e(\"读取相应内容 =>\", new
自定义证书的 客户端校验 和 服务端校验. [客户端证书校验源码和证明] pinner + host + 证书 [okhttp的单向校验-代码混淆解读]
关于一些方法 1, okhttp的一些特征, 找 Frida特征的时候, 被那么多的人拔掉底裤分析.那么分析 被混淆的 retrofio 和 okhttp3的时候,同样适用, 昨天这几天把混淆的 okhttp3的源码,特别证书校验的部分, 搞明白, 跨版本的逻辑小小改变是有的, 但是大体逻辑不会变化太多. 如果在2024年常用的库的分析 , okhttp3的跨大版本的更新. 知道变化的趋势. 2, 在看视频时候,一定要知道,特别原理分析时候, 调用栈, 是哪个包,哪个函数被hook, 重点照顾它,熟悉它, 因为被混淆的函数和没有被混淆的,逻辑是不会变得. 做笔记时候, 给出 hook的具体命令,和脚本路径. 可以溯源. 3, 多次观看,多次实践, >= 5次左右, 任何细节,绝不放过. 哪怕一个字一个字打出来! 4, 抓包的设计, UDP, TCP, QUIC协议, websockt协议, 双向证书校验. 5, Frida 反调试, so层面反调试, 反hook, 修改So的逻辑, dump出修复, Native层的 模拟执行. 非常多的知识点. 6, 调用栈分成, 调用第三方库的某个具体函数, 是由1-屏幕事件, 2-调用,界面元素, 3-app内部路由, 4-底层功能模块这样的路径. 如果遇到混淆, SSLSession 作为第二参数, 寻找,到hook, =====>因为系统包, 他混淆不了.!!! 所以是这样. 搜索 SSLSession 作为第二参数出现在 , 7, 校验完成了,没有问题了,才会发送.---> 请求. 你提到的三个步骤(证书校验、主机校验、pinner公钥校验)是客户端对服务器的验证过程,属于 单向认证 ============================================= 在哪里执行 的这个三个校验--> okhttp3.internal.connection.RealCoonnection. 类别中的 connectTLS方法. 1+++++> 证书 2+++++>HOST 3+++++>Pinner 类名和方法名字会变, 但是代码的执行过程是不会变的. 客户端校验的顺序分别是: - 第1步:调用证书校验--服务端--> 合法性 - 第2步:主机校验--->是否域名匹配 - 第3步:pinner公钥校验,这个校验过程本质上是调用`CertificatePinner`类中的`check`方法--> 服务端证书公钥sha256-->是否本地一致.不被监听 ========================================================= -1, 证书校验:验证服务端证书的合法性(证书链、有效期、吊销状态)。 -2, 主机校验:验证服务端证书中的域名是否与客户端请求的主机名匹配。 -3, Pinner 公钥校验:验证服务端证书的公钥 SHA-256 是否与本地预置的公钥哈希值一致。 1, 生成 key (RSA 私要) openssl genrsa -out server/server-key.key 2048 // 无加密 2, 生成服务端证书请求文件, openssl req -new -out server/server-req.csr -key server/server-key.key 3, 生成 服务端证书 // openssl 3.x openssl x509 -req -in server/server-req.csr -out server/server-cert.cer -signkey server/server-key.key -days 3650 1, 生成客户端 私密钥 openssl genrsa -out client/client-key.key 2048 // 无加密 2, 生成客户端证书请求文件 openssl req -new -out client/client-req.csr -key client/client-key.key // 3, 生成客户端证书 // openssl 3.x openssl x509 -req -in client/client-req.csr -out client/client-cert.cer -signkey client/client-key.key -days 3650 生成客户端带密码的p12证书(可集成在安卓中来实现服务端校验) openssl pkcs12 -export -clcerts -in client/client-cert.cer -inkey client/client-key.key -out client/client.p12
关于抓包的总体的思路和方法. 2025年1月14号
1, 抓包的单向证书, 通过实际的例子去解读这个问题. , 服务端证书, 校验原理. --- 本质 ---> 后续只要发送, 就能发送过去. 接收到请求, 才能, 返回数据. 才能集成证书. 通过离线的代码python 和 加上证书, 脱离app 做操作. 如果有服务端证书校验, 你的charles 没有证书,使用抓包工具自带证书, 你也不能做到. ----> 找到 服务端可以绕过证书校验,就可以返回值. 使用 Charles 可以愉快的进行抓包了. 绕过: 1, 找到集成在手机上的证书.p12 . 或者 .bks 后缀而存在的. 2, 需要找到证书的密码, (导入证书需要) 手机集成客户端校验, piner, 集成 sha256 , 集成服务端,校验 + p12 的证书, + 密码, 举例子的代码: KeyStore.getInstance(\"BKS\") ===> KeyStore.getInstance(\"PKCS12\") ==> 这两种不同的格式的证书. ===> 使用了, openRawResource 在 Raw 这个目录下存在的 ===> 使用额, getAssets().open(\"TLS\"); 就是在 Assets这个目录下存在. Request 请求的证书, 需要把 Request 发送过去. hook密码 Java.perform(function () { var KeyStore = Java.use(\"java.security.KeyStore\"); KeyStore.load.overload(\'java.io.InputStream\', \'
unidbg 调试补充环境的入门.
关于一些的函数的参数的类型. (Landroid/content/Context; JLjava/lang/String; J)Ljava/lang/String; 这个字符串是一个描述 Java 方法签名的 **JNI (Java Native Interface)** 描述符,具体如下: ### 格式说明 在 JNI 中,方法签名格式为: ``` (参数类型列表)返回值类型 ``` #### 1. 参数部分 `(参数类型列表)` - **`Landroid/content/Context;`** 表示第一个参数是一个 Java 对象,类型为 `android.content.Context`。 - **`J`** 表示第二个参数是一个 `long` 类型的参数。 - **`Ljava/lang/String;`** 表示第三个参数是一个 Java 对象,类型为 `java.lang.String`。 - **`J`** 表示第四个参数是另一个 `long` 类型的参数。 #### 2. 返回值部分 `Ljava/lang/String;` - **`Ljava/lang/String;`** 表示返回值是一个 Java 对象,类型为 `java.lang.String`。 --- ### 完整解释 这是一个方法签名,其含义是: **方法参数** 1. 一个 `android.content.Context` 类型的对象。 2. 一个 `long` 类型的参数。 3. 一个 `java.lang.String` 类型的对象。 4. 一个 `long` 类型的参数。 **返回值** - 返回一个 `java.lang.String` 类型的对象。 --- ### 应用场景 - 在 JNI 中,你可以使用这个签名来定义或调用对应的 Java 方法。 - 如果你是通过 `JNIEnv` 调用该方法,可以这样写: ```cpp jmethodID methodID = env->GetMethodID(clazz, \"methodName\", \"(Landroid/content/Context;JLjava/lang/String;J)Ljava/lang/String;\"); jstring result = (jstring) env->CallObjectMethod(obj, methodID, context, jlong1, jstring1, jlong2); ``` 如果还有疑问,随时问我! 函数里面可能会出现重载, 要么参数不一样, 要么返回值不一样. 所以这里是用他来找到,标志杂们的函数. 传递的话就是 context 对象, package com.nb.demo; import com.github.unidbg.AndroidEmulator; import com.github.unidbg.Module; import com.github.unidbg.linux.android.AndroidEmulatorBuilder; import com.github.unidbg.linux.android.AndroidResolver; import com.github.unidbg.linux.android.dvm.*; import com.github.unidbg.memory.Memory; import java.io.File; public class che extends AbstractJni { // 必须从 AbstractJni 当前这个类中寻找. 继承来自 extends AbstractJni 后面涉及到补充环境,那么我们在当前的这个类中,给他补充环境就可以了. 可以理解为固定搭配 public static AndroidEmulator emulator; public static Memory memory; public static VM vm; public static Module module; che() { // 1.创建设备(32位或64位模拟器), 具体看so文件在哪个目录。 在armeabi-v7a就选择32位 emulator = AndroidEmulatorBuilder.for32Bit().setProcessName(\"com.che168.autotradercloud\").build(); // 2.获取内存对象(可以操作内存) memory = emulator.getMemory(); // 3.设置安卓sdk版本(只支持19、23) memory.setLibraryResolver(new AndroidResolver(23)); // 4.创建虚拟机(运行安卓代码需要虚拟机,就想运行py代码需要python解释器一样) vm = emulator.createDalvikVM(new File(\"apks/che/atc241.apk\")); // this 代指当前这个类的对象. 设置这个意义在于. vm.setJni(this); // 后续so文件,如果没用调用java中的代码; 如果so中有使用
