|
|
发表于
2025-10-26 12:27:05
来自手机
|
显示全部楼层
| 阅读模式
来自 福建
本帖最后由 岩浆怕 于 2025-10-26 15:07 编辑
近期,落叶思秋更新了dpt-shell函数抽取壳,导致现在无法正常脱dex
源码github下载
我们来分析一下怎么做到的
Frida 都知道吧?dpt-shell 针对 Frida 设计了专门的检测机制:
- // 从 dpt_risk.cpp 中提取的关键代码片段
- extern "C" void* detectFridaOnThread(void* arg) {
- // 无限循环检测,持续监控环境
- while (1) {
- // 检测内存中是否存在 frida-agent 字符串
- if (find_in_maps("frida-agent")) {
- // 发现 Frida 注入,触发崩溃保护
- dpt_crash();
- }
-
- // 检测特定的 Frida 线程名
- if (find_in_threads_list("frida-thread")) {
- // 发现可疑线程,触发崩溃保护
- dpt_crash();
- }
-
- // 休眠一段时间后继续检测
- usleep(100000); // 100ms
- }
- return nullptr;
- }
复制代码
这段代码通过两种方式检测 Frida:
扫描进程的内存映射,查找"frida-agent"特征字符串
遍历线程列表,寻找可疑的 Frida 线程名
一旦发现异常,立即调用 dpt_crash() 函数让应用崩溃
反调试
Ptrace 是 Linux 系统上用于进程跟踪和调试的系统调用。dpt-shell 利用了 Ptrace 的特性来防止调试:
- // doPtrace 函数
- extern "C" void doPtrace() {
- // 使用 PTRACE_TRACEME 让进程跟踪自身
- if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1) {
- // 如果 ptrace 失败,说明进程已经被其他调试器附加
- // 触发崩溃保护
- dpt_crash();
- }
-
- // 省略
- }
复制代码
这个机制的原理
当一个进程已经被调试器附加时,再次调用 [bPTRACE_TRACEME[/b会失败
DPT 利用这一点来检测是否有调试器正在附加到目标应用
进程保护
通过 fork()创建子进程:
- // createAntiRiskProcess 函数
- extern "C" void createAntiRiskProcess() {
- // 创建子进程
- pid_t pid = fork();
-
- if (pid < 0) {
- // fork 失败,直接退出
- exit(0);
- } else if (pid == 0) {
- // 子进程逻辑
- // 在子进程中执行 Frida 检测
- detectFrida();
- // 使用 ptrace 跟踪父进程
- doPtrace();
-
- // 进入无限循环,持续监控
- while (1) {
- sleep(1);
- }
- } else {
- // 父进程逻辑
- // 父进程也执行 Frida 检测
- detectFrida();
- // 保护子进程不被杀死
- protectChildProcess(pid);
- }
- }
复制代码
代码完整验证
dpt-shell还实现了代码完整性验证,通过垃圾代码保护机制来防止 DEX 文件被修改:
- // junkCodeDexProtect 函数
- extern "C" jboolean junkCodeDexProtect(JNIEnv* env, jobject obj, jobject dexFile) {
- // 获取 DEX 文件相关信息
- // ...
-
- // 验证代码完整性
- if (!verifyCodeIntegrity(dexFile)) {
- // 代码被修改,触发崩溃保护
- dpt_crash();
- return JNI_FALSE;
- }
-
- return JNI_TRUE;
- }
复制代码
junkCodeDexProtect() 函数验证垃圾代码类是否存在
在 patchClass() 函数中实现对垃圾代码类调用模式的检查
- // DPT_ENCRYPT:
- DPT_ENCRYPT void patchClass(__unused const char* descriptor, // 类描述符,如"Lcom/example/MyClass;"
- const void* dex_file, // DexFile结构体指针
- const void* dex_class_def) // ClassDef结构体指针
- {
- // 检测:检查是否是恶意调用的垃圾类
- const char *junkClassName = AY_OBFUSCATE(JUNK_CLASS_FULL_NAME); // AY_OBFUSCATE可能是字符串混淆宏
- if(descriptor != nullptr && UNLIKELY(dpt_strstr(descriptor, junkClassName) != nullptr)) {
- size_t descriptorLength = dpt_strlen(descriptor);
- char ch = descriptor[descriptorLength - 2]; // 获取描述符倒数第二个字符
-
- DLOGD("Attempt patch junk class %s ,char is '%c'",descriptor,ch);
-
- // 如果字符是数字,说明是恶意调用(正常类名不会以数字结尾)
- if(isdigit(ch)) {
- DLOGE("Find illegal call, desc: %s!", descriptor);
- dpt_crash(); // 触发崩溃,防止分析
- return;
- }
- }
- // 主要的类修补
- if(LIKELY(dex_file != nullptr)){
- std::string location; // dex文件路径
- uint8_t *begin = nullptr; // dex文件内存起始地址
- uint64_t dexSize = 0; // dex文件大小
- // 根据Android SDK版本选择不同的DexFile结构体
- if(g_sdkLevel >= 35) {
- // Android 14+ (API 35+)
- auto* dexFileV35 = (V35::DexFile *)dex_file;
- location = dexFileV35->location_;
- begin = (uint8_t *)dexFileV35->begin_;
- dexSize = dexFileV35->header_->file_size_;
- }
- else if(g_sdkLevel >= __ANDROID_API_P__){
- // Android 9+ (API 28+)
- auto* dexFileV28 = (V28::DexFile *)dex_file;
- location = dexFileV28->location_;
- begin = (uint8_t *)dexFileV28->begin_;
- dexSize = dexFileV28->size_ == 0 ? dexFileV28->header_->file_size_ : dexFileV28->size_;
- }
- else {
- // Android 5.0-8.1 (API 21-27)
- auto* dexFileV21 = (V21::DexFile *)dex_file;
- location = dexFileV21->location_;
- begin = (uint8_t *)dexFileV21->begin_;
- dexSize = dexFileV21->size_ == 0 ? dexFileV21->header_->file_size_ : dexFileV21->size_;
- }
- // 检查是否是目标dex文件(包含被抽取的函数)
- if(location.rfind(DEXES_ZIP_NAME) != std::string::npos && dex_class_def){
- // 从文件路径解析dex索引号
- int dexIndex = parse_dex_number(&location);
- auto* class_def = (dex::ClassDef *)dex_class_def;
- NLOG("class_desc = '%s', class_idx_ = 0x%x, class data off = 0x%x",
- descriptor, class_def->class_idx_, class_def->class_data_off_);
- // 确保类有数据(class_data_off_不为0)
- if(LIKELY(class_def->class_data_off_ != 0)) {
- size_t read = 0;
- // 计算类数据在内存中的位置
- auto *class_data = (uint8_t *) ((uint8_t *) begin + class_def->class_data_off_);
- // 读取类数据头信息(使用ULEB128变长编码)
- uint64_t static_fields_size = 0;
- read += DexFileUtils::readUleb128(class_data, &static_fields_size);
- uint64_t instance_fields_size = 0;
- read += DexFileUtils::readUleb128(class_data + read, &instance_fields_size);
- uint64_t direct_methods_size = 0; // 直接方法数
- read += DexFileUtils::readUleb128(class_data + read, &direct_methods_size);
- uint64_t virtual_methods_size = 0; // 虚方法数
- read += DexFileUtils::readUleb128(class_data + read, &virtual_methods_size);
- // 跳过字段信息(不需要处理字段)
- // staticFields
- read += DexFileUtils::getFieldsSize(class_data + read, static_fields_size);
- // instanceFields
- read += DexFileUtils::getFieldsSize(class_data + read, instance_fields_size);
- // 读取直接方法信息
- auto *directMethods = new dex::ClassDataMethod[direct_methods_size];
- read += DexFileUtils::readMethods(class_data + read, directMethods, direct_methods_size);
- // 读取虚方法信息
- auto *virtualMethods = new dex::ClassDataMethod[virtual_methods_size];
- read += DexFileUtils::readMethods(class_data + read, virtualMethods, virtual_methods_size);
- // 修补所有直接方法
- for (uint64_t i = 0; i < direct_methods_size; i++) {
- auto method = directMethods[i];
- patchMethod(begin, location.c_str(), dexSize, dexIndex,
- method.method_idx_delta_, method.code_off_);
- }
- // 修补所有虚方法
- for (uint64_t i = 0; i < virtual_methods_size; i++) {
- auto method = virtualMethods[i];
- patchMethod(begin, location.c_str(), dexSize, dexIndex,
- method.method_idx_delta_, method.code_off_);
- }
- // 释放内存
- delete[] directMethods;
- delete[] virtualMethods;
- }
- else {
- NLOG("class_def->class_data_off_ is zero"); // 类数据偏移为0(可能是接口或抽象类)
- }
- }
- }
- }
复制代码
字符串混淆
字符串混淆,防静态分析:
- // AY_OBFUSCATE 宏定义,用于混淆关键字符串
- #define AY_OBFUSCATE(str) []() {
- static const char* s = str;
- return s;
- }()
- // 示例
- const char* keyword = AY_OBFUSCATE("frida-agent");
复制代码
崩溃
实现了一个内联汇编崩溃函数,用于终止程序
- // dpt_crash 函数在不同架构上的实现
- #ifdef __arm__
- extern "C" void dpt_crash() {
- // ARM 架构上的实现
- __asm__ __volatile__("b ."); // 死循环
- }
- #elif defined(__aarch64__)
- extern "C" void dpt_crash() {
- // ARM64 架构上的实现
- __asm__ __volatile__("b ."); // 死循环
- }
- #else
- extern "C" void dpt_crash() {
- // x86 等其他架构
- *(int*)0 = 0; // 触发段错误
- }
- #endif
复制代码
工作流程
初始化流程:
- // init_dpt 初始化函数,整个保护系统的入口点
- extern "C" void init_dpt(JNIEnv* env, jclass clazz) {
- // 解密代码
- decrypt_section();
-
- // 初始化日志
- init_log();
-
- // 安装钩子
- dpt_hook(env, clazz);
-
- // 创建反风险进程
- createAntiRiskProcess();
-
- // ...
- }
复制代码
magisk和kernelsu反反脱壳模块开发中.......
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
-
查看全部评分
总评分:好评 +1
金币 +2
|