返回列表 发新帖

【逆向分析】落叶思秋(dpt-shell)反脱壳逻辑

  [复制链接]

139

主题

859

回帖

4477

积分

大学生

Rank: 5Rank: 5

金币
968
好评
37
信誉
100

MT论坛帅哥考神MT论坛新人MT论坛最佳新人MT论坛活跃会员

QQ
发表于 2025-10-26 12:27:05 来自手机  | 显示全部楼层 | 阅读模式  来自 福建
本帖最后由 岩浆怕 于 2025-10-26 15:07 编辑

近期,落叶思秋更新了dpt-shell函数抽取壳,导致现在无法正常脱dex
github
我们来分析一下怎么做到的
Frida 都知道吧?dpt-shell 针对 Frida 设计了专门的检测机制:

  1. // 从 dpt_risk.cpp 中提取的关键代码片段
  2. extern "C" void* detectFridaOnThread(void* arg) {
  3.     // 无限循环检测,持续监控环境
  4.     while (1) {
  5.         // 检测内存中是否存在 frida-agent 字符串
  6.         if (find_in_maps("frida-agent")) {
  7.             // 发现 Frida 注入,触发崩溃保护
  8.             dpt_crash();
  9.         }
  10.         
  11.         // 检测特定的 Frida 线程名
  12.         if (find_in_threads_list("frida-thread")) {
  13.             // 发现可疑线程,触发崩溃保护
  14.             dpt_crash();
  15.         }
  16.         
  17.         // 休眠一段时间后继续检测
  18.         usleep(100000); // 100ms
  19.     }
  20.     return nullptr;
  21. }
复制代码

这段代码通过两种方式检测 Frida:
扫描进程的内存映射,查找"frida-agent"特征字符串
遍历线程列表,寻找可疑的 Frida 线程名
一旦发现异常,立即调用 dpt_crash() 函数让应用崩溃
反调试
Ptrace 是 Linux 系统上用于进程跟踪和调试的系统调用。dpt-shell 利用了 Ptrace 的特性来防止调试:
  1. // doPtrace 函数
  2. extern "C" void doPtrace() {
  3.     // 使用 PTRACE_TRACEME 让进程跟踪自身
  4.     if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1) {
  5.         // 如果 ptrace 失败,说明进程已经被其他调试器附加
  6.         // 触发崩溃保护
  7.         dpt_crash();
  8.     }
  9.    
  10.     // 省略
  11. }
复制代码


这个机制的原理
[bPTRACE_TRACEME[/b
DPT
进程保护
通过 fork()创建子进程:
  1. // createAntiRiskProcess 函数
  2. extern "C" void createAntiRiskProcess() {
  3.     // 创建子进程
  4.     pid_t pid = fork();
  5.    
  6.     if (pid < 0) {
  7.         // fork 失败,直接退出
  8.         exit(0);
  9.     } else if (pid == 0) {
  10.         // 子进程逻辑
  11.         // 在子进程中执行 Frida 检测
  12.         detectFrida();
  13.         // 使用 ptrace 跟踪父进程
  14.         doPtrace();
  15.         
  16.         // 进入无限循环,持续监控
  17.         while (1) {
  18.             sleep(1);
  19.         }
  20.     } else {
  21.         // 父进程逻辑
  22.         // 父进程也执行 Frida 检测
  23.         detectFrida();
  24.         // 保护子进程不被杀死
  25.         protectChildProcess(pid);
  26.     }
  27. }
复制代码


代码完整验证
dpt-shell还实现了代码完整性验证,通过垃圾代码保护机制来防止 DEX 文件被修改:

  1. // junkCodeDexProtect 函数
  2. extern "C" jboolean junkCodeDexProtect(JNIEnv* env, jobject obj, jobject dexFile) {
  3.     // 获取 DEX 文件相关信息
  4.     // ...
  5.    
  6.     // 验证代码完整性
  7.     if (!verifyCodeIntegrity(dexFile)) {
  8.         // 代码被修改,触发崩溃保护
  9.         dpt_crash();
  10.         return JNI_FALSE;
  11.     }
  12.    
  13.     return JNI_TRUE;
  14. }
复制代码



junkCodeDexProtect() 函数验证垃圾代码类是否存在
patchClass() 函数中实现对垃圾代码类调用模式的检查
  1. // DPT_ENCRYPT:
  2. DPT_ENCRYPT void patchClass(__unused const char* descriptor,  // 类描述符,如"Lcom/example/MyClass;"
  3.                  const void* dex_file,      // DexFile结构体指针
  4.                  const void* dex_class_def) // ClassDef结构体指针
  5. {

  6.     // 检测:检查是否是恶意调用的垃圾类
  7.     const char *junkClassName = AY_OBFUSCATE(JUNK_CLASS_FULL_NAME); // AY_OBFUSCATE可能是字符串混淆宏
  8.     if(descriptor != nullptr && UNLIKELY(dpt_strstr(descriptor, junkClassName) != nullptr)) {
  9.         size_t descriptorLength = dpt_strlen(descriptor);
  10.         char ch = descriptor[descriptorLength - 2]; // 获取描述符倒数第二个字符
  11.         
  12.         DLOGD("Attempt patch junk class %s ,char is '%c'",descriptor,ch);
  13.         
  14.         // 如果字符是数字,说明是恶意调用(正常类名不会以数字结尾)
  15.         if(isdigit(ch)) {
  16.             DLOGE("Find illegal call, desc: %s!", descriptor);
  17.             dpt_crash(); // 触发崩溃,防止分析
  18.             return;
  19.         }
  20.     }

  21.     // 主要的类修补
  22.     if(LIKELY(dex_file != nullptr)){
  23.         std::string location;  // dex文件路径
  24.         uint8_t *begin = nullptr;  // dex文件内存起始地址
  25.         uint64_t dexSize = 0;      // dex文件大小

  26.         // 根据Android SDK版本选择不同的DexFile结构体
  27.         if(g_sdkLevel >= 35) {
  28.             // Android 14+ (API 35+)
  29.             auto* dexFileV35 = (V35::DexFile *)dex_file;
  30.             location = dexFileV35->location_;
  31.             begin = (uint8_t *)dexFileV35->begin_;
  32.             dexSize = dexFileV35->header_->file_size_;
  33.         }
  34.         else if(g_sdkLevel >= __ANDROID_API_P__){
  35.             // Android 9+ (API 28+)
  36.             auto* dexFileV28 = (V28::DexFile *)dex_file;
  37.             location = dexFileV28->location_;
  38.             begin = (uint8_t *)dexFileV28->begin_;
  39.             dexSize = dexFileV28->size_ == 0 ? dexFileV28->header_->file_size_ : dexFileV28->size_;
  40.         }
  41.         else {
  42.             // Android 5.0-8.1 (API 21-27)
  43.             auto* dexFileV21 = (V21::DexFile *)dex_file;
  44.             location = dexFileV21->location_;
  45.             begin = (uint8_t *)dexFileV21->begin_;
  46.             dexSize = dexFileV21->size_ == 0 ? dexFileV21->header_->file_size_ : dexFileV21->size_;
  47.         }

  48.         // 检查是否是目标dex文件(包含被抽取的函数)
  49.         if(location.rfind(DEXES_ZIP_NAME) != std::string::npos && dex_class_def){
  50.             // 从文件路径解析dex索引号
  51.             int dexIndex = parse_dex_number(&location);

  52.             auto* class_def = (dex::ClassDef *)dex_class_def;
  53.             NLOG("class_desc = '%s', class_idx_ = 0x%x, class data off = 0x%x",
  54.                  descriptor, class_def->class_idx_, class_def->class_data_off_);

  55.             // 确保类有数据(class_data_off_不为0)
  56.             if(LIKELY(class_def->class_data_off_ != 0)) {
  57.                 size_t read = 0;
  58.                 // 计算类数据在内存中的位置
  59.                 auto *class_data = (uint8_t *) ((uint8_t *) begin + class_def->class_data_off_);

  60.                 // 读取类数据头信息(使用ULEB128变长编码)
  61.                 uint64_t static_fields_size = 0;
  62.                 read += DexFileUtils::readUleb128(class_data, &static_fields_size);

  63.                 uint64_t instance_fields_size = 0;
  64.                 read += DexFileUtils::readUleb128(class_data + read, &instance_fields_size);

  65.                 uint64_t direct_methods_size = 0;  // 直接方法数
  66.                 read += DexFileUtils::readUleb128(class_data + read, &direct_methods_size);

  67.                 uint64_t virtual_methods_size = 0; // 虚方法数
  68.                 read += DexFileUtils::readUleb128(class_data + read, &virtual_methods_size);

  69.                 // 跳过字段信息(不需要处理字段)
  70.                 // staticFields
  71.                 read += DexFileUtils::getFieldsSize(class_data + read, static_fields_size);
  72.                 // instanceFields
  73.                 read += DexFileUtils::getFieldsSize(class_data + read, instance_fields_size);

  74.                 // 读取直接方法信息
  75.                 auto *directMethods = new dex::ClassDataMethod[direct_methods_size];
  76.                 read += DexFileUtils::readMethods(class_data + read, directMethods, direct_methods_size);

  77.                 // 读取虚方法信息
  78.                 auto *virtualMethods = new dex::ClassDataMethod[virtual_methods_size];
  79.                 read += DexFileUtils::readMethods(class_data + read, virtualMethods, virtual_methods_size);

  80.                 // 修补所有直接方法
  81.                 for (uint64_t i = 0; i < direct_methods_size; i++) {
  82.                     auto method = directMethods[i];
  83.                     patchMethod(begin, location.c_str(), dexSize, dexIndex,
  84.                                 method.method_idx_delta_, method.code_off_);
  85.                 }

  86.                 // 修补所有虚方法
  87.                 for (uint64_t i = 0; i < virtual_methods_size; i++) {
  88.                     auto method = virtualMethods[i];
  89.                     patchMethod(begin, location.c_str(), dexSize, dexIndex,
  90.                                 method.method_idx_delta_, method.code_off_);
  91.                 }

  92.                 // 释放内存
  93.                 delete[] directMethods;
  94.                 delete[] virtualMethods;
  95.             }
  96.             else {
  97.                 NLOG("class_def->class_data_off_ is zero"); // 类数据偏移为0(可能是接口或抽象类)
  98.             }
  99.         }
  100.     }
  101. }
复制代码

字符串混淆
字符串混淆,防静态分析:

  1. // AY_OBFUSCATE 宏定义,用于混淆关键字符串
  2. #define AY_OBFUSCATE(str) []() {
  3.     static const char* s = str;
  4.     return s;
  5. }()

  6. // 示例
  7. const char* keyword = AY_OBFUSCATE("frida-agent");

复制代码

崩溃

实现了一个内联汇编崩溃函数,用于终止程序

  1. // dpt_crash 函数在不同架构上的实现
  2. #ifdef __arm__
  3. extern "C" void dpt_crash() {
  4.     // ARM 架构上的实现
  5.     __asm__ __volatile__("b ."); // 死循环
  6. }
  7. #elif defined(__aarch64__)
  8. extern "C" void dpt_crash() {
  9.     // ARM64 架构上的实现
  10.     __asm__ __volatile__("b ."); // 死循环
  11. }
  12. #else
  13. extern "C" void dpt_crash() {
  14.     // x86 等其他架构
  15.     *(int*)0 = 0; // 触发段错误
  16. }
  17. #endif
复制代码

工作流程
初始化流程:
  1. // init_dpt 初始化函数,整个保护系统的入口点
  2. extern "C" void init_dpt(JNIEnv* env, jclass clazz) {
  3.     // 解密代码
  4.     decrypt_section();
  5.    
  6.     // 初始化日志
  7.     init_log();
  8.    
  9.     // 安装钩子
  10.     dpt_hook(env, clazz);
  11.    
  12.     // 创建反风险进程
  13.     createAntiRiskProcess();
  14.    
  15.     // ...
  16. }
复制代码


magisk和kernelsu反反脱壳模块开发中.......


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
已有2人评分好评 金币 理由
青春向上 + 1 感谢分享。
紫檀 + 1 + 1

查看全部评分 总评分:好评 +1  金币 +2 

回复

使用道具 举报

9

主题

1984

回帖

7092

积分

硕士生

Rank: 6Rank: 6

金币
3063
好评
4
信誉
101

MT论坛最佳新人MT论坛帅哥考神MT论坛新人

发表于 2025-10-26 12:27:52 来自手机  | 显示全部楼层  来自 浙江
看看
回复

使用道具 举报

4

主题

1921

回帖

6407

积分

硕士生

Rank: 6Rank: 6

金币
4434
好评
1
信誉
102

考神MT论坛最佳新人

QQ
发表于 2025-10-26 12:28:58 来自手机  | 显示全部楼层  来自 山西
看看
回复

使用道具 举报

688

主题

1万

回帖

2万

积分

博士后

Rank: 8Rank: 8

金币
3412
好评
164
信誉
119

考神MT论坛新人MT论坛活跃会员MT论坛帅哥MT论坛灌水老大MT论坛侠客MT论坛最佳新人

发表于 2025-10-26 12:29:08 来自手机  | 显示全部楼层  来自 广东
看看隐藏
回复

使用道具 举报

1

主题

1361

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
4101
好评
7
信誉
100
发表于 2025-10-26 12:35:56 来自手机  | 显示全部楼层  来自 广东
看看隐藏,感谢分享
回复

使用道具 举报

18

主题

4876

回帖

1万

积分

博士生

MW

Rank: 7Rank: 7Rank: 7

金币
2516
好评
2
信誉
100

考神MT论坛新人MT论坛帅哥MT论坛最佳新人

发表于 2025-10-26 12:35:56 来自手机  | 显示全部楼层  来自 新疆
感谢分享,看看隐藏
回复

使用道具 举报

12

主题

2303

回帖

7930

积分

硕士生

Rank: 6Rank: 6

金币
4376
好评
1
信誉
100

考神MT论坛帅哥

发表于 2025-10-26 12:36:31 来自手机  | 显示全部楼层  来自 广东
学习一下
回复

使用道具 举报

0

主题

1732

回帖

4582

积分

大学生

Rank: 5Rank: 5

金币
2021
好评
0
信誉
100
发表于 2025-10-26 12:37:00 来自手机  | 显示全部楼层  来自 福建
感谢分享
回复

使用道具 举报

17

主题

947

回帖

3815

积分

大学生

Rank: 5Rank: 5

金币
1227
好评
0
信誉
98
发表于 2025-10-26 12:37:52 来自手机  | 显示全部楼层  来自 江西
收藏
回复

使用道具 举报

162

主题

8064

回帖

2万

积分

博士后

滑稽

Rank: 8Rank: 8

金币
754
好评
474
信誉
210

MT论坛新人MT论坛最佳新人MT论坛活跃会员考神MT论坛帅哥MT论坛灌水老大

QQ
发表于 2025-10-26 12:38:01 来自手机  | 显示全部楼层  来自 福建
路过
回复

使用道具 举报

0

主题

2799

回帖

5667

积分

硕士生

Rank: 6Rank: 6

金币
777
好评
0
信誉
96
发表于 2025-10-26 12:39:44 | 显示全部楼层  来自 江西
看看
回复

使用道具 举报

8

主题

1260

回帖

5710

积分

硕士生

棺材板

Rank: 6Rank: 6

金币
2287
好评
51
信誉
103
发表于 2025-10-26 12:41:26 来自手机  | 显示全部楼层  来自 四川
看看隐藏
回复

使用道具 举报

74

主题

2002

回帖

7012

积分

硕士生

Rank: 6Rank: 6

金币
3894
好评
6
信誉
90
发表于 2025-10-26 12:41:47 来自手机  | 显示全部楼层  来自 重庆
看看
回复

使用道具 举报

38

主题

989

回帖

3580

积分

大学生

新手小白

Rank: 5Rank: 5

金币
1393
好评
4
信誉
100

考神MT论坛帅哥MT论坛新人MT论坛最佳新人

QQ
发表于 2025-10-26 12:42:17 来自手机  | 显示全部楼层  来自 山东
看看隐藏
回复

使用道具 举报

0

主题

1082

回帖

3172

积分

大学生

Rank: 5Rank: 5

金币
2621
好评
0
信誉
100
发表于 2025-10-26 12:42:30 来自手机  | 显示全部楼层  来自 广西
谢谢分享!
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表