当前位置: 首页 > article >正文

Frida反调试对抗系列(二)

有需要过frida反调试的可以去联系博主主页V

let V = '15232101239';

  今天我们主要学习上一篇提到的优秀Frida魔改项目,分析其修改的具体位置。在实际操作中,针对每个Frida脚本去找到所有检测点成本过高,因此我们先要识别Frida中容易被检测的地方并进行修改。这样做的思路是先借鉴前人的经验,站在他们的肩膀上进行扩展,遇到无法绕过的检测时,再具体分析。这样的方法会更加高效。

一、学习优秀项目Florida魔改点

下面我们简单看一些patchs的修改点:

1.0001-Florida-string_frida_rpc.patch

getRpcStr() 方法的新增
public string getRpcStr(bool quote){
    string result = (string) GLib.Base64.decode((string) GLib.Base64.decode("Wm5KcFpHRTZjbkJq"));
    if(quote){
        return "\"" + result + "\"";
    } else {
        return result;
    }
}
  • 功能getRpcStr() 方法解码一个经过两次 Base64 编码的字符串,并返回解码后的结果。如果 quote 参数为 true,则在结果前后加上双引号("),如果 quotefalse,则返回纯字符串。

  • 具体过程

    这个字符串实际上是 frida:rpc,这是 RPC 请求的标识符。通过 getRpcStr(),你可以在不同的上下文中获得这个标识符,并可以选择是否加上引号

    • GLib.Base64.decode() 用于对给定的 Base64 编码的字符串进行解码。
    • "Wm5KcFpHRTZjbkJq" 是一个经过 Base64 编码的字符串,它经过两次解码后将返回一个原始字符串。
修改 call() 方法
public async Json.Node call (string method, Json.Node[] args, Cancellable? cancellable) throws Error, IOError {
    string request_id = Uuid.string_random ();

    var request = new Json.Builder ();
    request
        .begin_array ()
        .add_string_value (getRpcStr(false))
        .add_string_value (request_id)
        .add_string_value ("call")
        .add_string_value (method)
        .add_array_value (args)
        .end_array ();

    return await send_request(request, cancellable);
}
  • 变化:在 call() 方法中,将原来硬编码的 "frida:rpc" 字符串替换成了 getRpcStr(false)。这意味着 RPC 调用的标识符不再是固定的字符串,而是通过 getRpcStr(false) 动态获取。
修改try_handle_message() 方法
public bool try_handle_message (string json) {
    if (json.index_of (getRpcStr(true)) == -1)
        return false;

    var parser = new Json.Parser ();
    if (!parser.load_from_data(json, -1)) {
        return false;
    }

    var rpc_message = parser.get_root();
    if (rpc_message.get_type() != Json.NodeType.ARRAY) {
        return false;
    }

    string? type = rpc_message.get_element(0).get_string ();
    if (type == null || type != getRpcStr(false))
        return false;

    var request_id_value = rpc_message.get_element(1);
    // 处理其他消息逻辑...
}
修改 rpc_message 类型检查
string? type = rpc_message.get_element(0).get_string ();
if (type == null || type != getRpcStr(false))
    return false;
  • 变化:原本硬编码的 "frida:rpc" 字符串也被替换为 getRpcStr(false),用来验证消息中的类型是否匹配。如果类型不匹配,则返回 false

总结:

这个补丁的主要目的是引入了一个新的 getRpcStr() 方法,用于动态生成或获取 "frida:rpc" 字符串。通过这种方式,代码变得更加灵活,减少了硬编码字符串的使用,从而提升了代码的可维护性和可扩展性。

具体修改点:

  1. getRpcStr(bool quote):解码 Wm5KcFpHRTZjbkJq 并返回 "frida:rpc" 字符串,支持是否加引号。
  2. call() 方法:将 "frida:rpc" 字符串替换为 getRpcStr(false)
  3. try_handle_message() 方法:在判断消息是否为 RPC 类型时,使用 getRpcStr(true) 来匹配带引号的字符串,并确保消息符合预期的格式。

这些修改有助于提高代码的灵活性,使其能够更容易地适应未来可能的变化,例如修改 RPC 标识符的方式。

2.0002-Florida-frida_agent_so.patch

具体修改内容分析:
  1. 增加随机前缀

    var random_prefix = GLib.Uuid.string_random();

    这行代码调用 GLib.Uuid.string_random() 生成一个随机的 UUID 字符串,作为前缀。这是为了避免多个不同实例之间发生冲突。

  2. 修改代理文件的路径和资源名称: 代码中的 PathTemplate ("frida-agent-<arch>.so") 被修改为:

    PathTemplate (random_prefix + "-<arch>.so")

    这意味着 frida-agent-<arch>.so 的路径模板现在包含一个随机前缀,例如:a1b2c3d4-arm.so,使得每个实例的路径具有唯一性。

  3. 修改 AgentResource 的文件名: 之前的文件名是 "frida-agent-arm.so""frida-agent-arm64.so",而现在它们被修改为:、

    random_prefix + "-arm.so" random_prefix + "-arm64.so"

    这些文件名也包含了随机前缀,使得每个资源的文件名独一无二。

3.0008-Florida-pool-frida.patch:

From 0f3391327c044f6c2ab0ee3322185904b0afa2c5 Mon Sep 17 00:00:00 2001
From: Ylarod <me@ylarod.cn>
Date: Thu, 20 Jul 2023 10:01:20 +0800
Subject: [PATCH 8/9] Florida: pool-frida

---
 src/frida-glue.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/frida-glue.c b/src/frida-glue.c
index ee8f0737..43cc8167 100644
--- a/src/frida-glue.c
+++ b/src/frida-glue.c
@@ -40,6 +40,8 @@ frida_init_with_runtime (FridaRuntime rt)
     g_io_module_openssl_register ();
 #endif
 
+    g_set_prgname ("ggbond");
+
     if (runtime == FRIDA_RUNTIME_OTHER)
     {
       main_context = g_main_context_ref (g_main_context_default ());
-- 
2.42.0

这个代码提交 (PATCH 8/9) 是对 frida-glue.c 文件的修改,主要添加了一行代码来设置程序的名称。

具体修改内容

  • 文件路径:src/frida-glue.c
  • 修改内容:在 frida_init_with_runtime 函数中,新增了 g_set_prgname ("ggbond"); 这一行。

代码详细分

g_set_prgname ("ggbond");

  • g_set_prgname 是 GLib 库中的一个函数,通常用于设置程序的名字(可用于显示或日志记录等)。程序的名称通常在调试信息、日志输出或者进程管理时很有用。
  • 这里将程序名设置为 "ggbond",意味着这个应用程序在运行时,其进程名称会被标记为 ggbond,即使它实际的文件名可能不同。

4.0007-Florida-update-python-script.patch

From 19026255ad926b166e7ffb8759e896fd6b5bff94 Mon Sep 17 00:00:00 2001
From: Ylarod <me@ylarod.cn>
Date: Tue, 18 Jul 2023 19:55:59 +0800
Subject: [PATCH 7/9] Florida: update python script

---
 src/anti-anti-frida.py | 59 +++++++++++++++++++++++++++++-------------
 1 file changed, 41 insertions(+), 18 deletions(-)

diff --git a/src/anti-anti-frida.py b/src/anti-anti-frida.py
index b4b8dca6..d1ce5f62 100644
--- a/src/anti-anti-frida.py
+++ b/src/anti-anti-frida.py
@@ -2,36 +2,59 @@ import lief
 import sys
 import random
 import os
-
+ 
+def log_color(msg):
+    print(f"\033[1;31;40m{msg}\033[0m")
+ 
 if __name__ == "__main__":
     input_file = sys.argv[1]
-    print(f"[*] Patch frida-agent: {input_file}")
-    random_name = "".join(random.sample("ABCDEFGHIJKLMNO", 5))
-    print(f"[*] Patch `frida` to `{random_name}``")
-
+    random_charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+    log_color(f"[*] Patch frida-agent: {input_file}")
     binary = lief.parse(input_file)
-
+ 
     if not binary:
+        log_color(f"[*] Not elf, exit")
         exit()
+    
+    random_name = "".join(random.sample(random_charset, 5))
+    log_color(f"[*] Patch `frida` to `{random_name}`")
 
     for symbol in binary.symbols:
         if symbol.name == "frida_agent_main":
             symbol.name = "main"
-        
+ 
         if "frida" in symbol.name:
             symbol.name = symbol.name.replace("frida", random_name)
-
+ 
         if "FRIDA" in symbol.name:
             symbol.name = symbol.name.replace("FRIDA", random_name)
-
+ 
+    all_patch_string = ["FridaScriptEngine", "GLib-GIO", "GDBusProxy", "GumScript"]  # 字符串特征修改 尽量与源字符一样
+    for section in binary.sections:
+        if section.name != ".rodata":
+            continue
+        for patch_str in all_patch_string:
+            addr_all = section.search_all(patch_str)  # Patch 内存字符串
+            for addr in addr_all:
+                patch = [ord(n) for n in list(patch_str)[::-1]]
+                log_color(f"[*] Patching section name={section.name} offset={hex(section.file_offset + addr)} orig:{patch_str} new:{''.join(list(patch_str)[::-1])}")
+                binary.patch_address(section.file_offset + addr, patch)
+ 
     binary.write(input_file)
-
-    # gum-js-loop thread
-    random_name = "".join(random.sample("abcdefghijklmn", 11))
-    print(f"[*] Patch `gum-js-loop` to `{random_name}`")
+ 
+    # thread_gum_js_loop
+    random_name = "".join(random.sample(random_charset, 11))
+    log_color(f"[*] Patch `gum-js-loop` to `{random_name}`")
     os.system(f"sed -b -i s/gum-js-loop/{random_name}/g {input_file}")
-
-    # gmain thread
-    random_name = "".join(random.sample("abcdefghijklmn", 5))
-    print(f"[*] Patch `gmain` to `{random_name}`")
-    os.system(f"sed -b -i s/gmain/{random_name}/g {input_file}")
\ No newline at end of file
+ 
+    # thread_gmain
+    random_name = "".join(random.sample(random_charset, 5))
+    log_color(f"[*] Patch `gmain` to `{random_name}`")
+    os.system(f"sed -b -i s/gmain/{random_name}/g {input_file}")
+ 
+    # thread_gdbus
+    random_name = "".join(random.sample(random_charset, 5))
+    log_color(f"[*] Patch `gdbus` to `{random_name}`")
+    os.system(f"sed -b -i s/gdbus/{random_name}/g {input_file}")
+
+    log_color(f"[*] Patch Finish")
\ No newline at end of file
-- 
2.42.0

这段代码是一个用于修改可执行文件中的符号名称以及某些字符串特征的 Python 脚本。其主要用途似乎是在进行逆向工程或者保护某些程序免受分析工具(比如 Frida)干扰。下面是对这段代码的详细解释:

1. 导入依赖库

 
import lief import sys import random import os
  • lief:用于解析和修改 ELF、PE 和 Mach-O 文件格式。它被用来加载和修改二进制文件。
  • sys:提供对 Python 解释器的一些访问,主要用于处理命令行参数。
  • random:用来生成随机数,特别是用来生成随机字符串。
  • os:与操作系统交互,主要用于执行系统命令(如使用 sed 命令修改文件内容)。

2. 定义辅助函数 log_color

def log_color(msg): print(f"\033[1;31;40m{msg}\033[0m")
  • 该函数用于将输出消息以红色打印到控制台。
  • \033[1;31;40m 是 ANSI 转义序列,用于修改终端文字的颜色,这里是设置为红色。
  • \033[0m 用来重置颜色,确保后续输出不受影响。

3. 主程序入口

if __name__ == "__main__": input_file = sys.argv[1]
  • 该部分是脚本的主逻辑部分,首先通过 sys.argv[1] 获取命令行传递的输入文件路径。

4. 加载二进制文件

binary = lief.parse(input_file)
  • 使用 lief 库来解析给定的二进制文件,返回一个 binary 对象,表示这个文件。如果文件无法解析,binary 将是 None

5. 检查是否为 ELF 文件

if not binary: log_color(f"[*] Not elf, exit") exit()
  • 如果解析失败,即 binary 为 None,则打印警告并退出程序。这部分检查文件是否为 ELF 格式。

6. 修改二进制中的符号名称

random_charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" log_color(f"[*] Patch frida-agent: {input_file}")
  • 该代码片段设置了用于生成随机名称的字符集。
  • 打印正在处理的输入文件。
random_name = "".join(random.sample(random_charset, 5)) log_color(f"[*] Patch `frida` to `{random_name}`")
  • 随机选择 5 个字符来生成新的名称,用于替换文件中与 frida 相关的符号。
for symbol in binary.symbols: if symbol.name == "frida_agent_main": symbol.name = "main" if "frida" in symbol.name: symbol.name = symbol.name.replace("frida", random_name) if "FRIDA" in symbol.name: symbol.name = symbol.name.replace("FRIDA", random_name)
  • 遍历二进制文件中的所有符号。
  • 如果符号名称包含 frida 或 FRIDA,则替换成随机生成的名称。
  • 还将 frida_agent_main 修改为 main

7. 修改 .rodata 区段中的字符串

all_patch_string = ["FridaScriptEngine", "GLib-GIO", "GDBusProxy", "GumScript"] for section in binary.sections: if section.name != ".rodata": continue for patch_str in all_patch_string: addr_all = section.search_all(patch_str) for addr in addr_all: patch = [ord(n) for n in list(patch_str)[::-1]] log_color(f"[*] Patching section name={section.name} offset={hex(section.file_offset + addr)} orig:{patch_str} new:{''.join(list(patch_str)[::-1])}") binary.patch_address(section.file_offset + addr, patch)
  • 该部分代码遍历 .rodata 区段并查找某些字符串(如 FridaScriptEngineGLib-GIOGDBusProxyGumScript)。
  • 在找到这些字符串后,进行反转替换并输出日志信息。
  • 然后使用 binary.patch_address() 方法将修改后的字符串写入到内存中的相应位置。

8. 使用 sed 命令替换文件中的特定字符串

os.system(f"sed -b -i s/gum-js-loop/{random_name}/g {input_file}") os.system(f"sed -b -i s/gmain/{random_name}/g {input_file}") os.system(f"sed -b -i s/gdbus/{random_name}/g {input_file}")
  • 这些 sed 命令在文件中查找 gum-js-loopgmain, 和 gdbus 等字符串,并将其替换为随机生成的名称。
  • -i 选项表示直接在文件中修改,-b 选项表示使用二进制模式。

9. 脚本完成

log_color(f"[*] Patch Finish")
  • 最后,打印出脚本执行完成的日志。

总结

这个 Python 脚本的目的是修改一个二进制文件,特别是针对某些特定的符号和字符串(如与 frida 相关的内容)进行随机化和替换。这样做可能是为了防止 Frida 等工具的动态分析,或者是为了保护二进制文件不被轻易逆向工程分析。


http://www.kler.cn/a/406160.html

相关文章:

  • 彻底理解消息队列的作用及如何选择
  • 从复合字符串中分割并解析多个JSON字符串
  • OceanBase数据库产品与工具介绍
  • 国产linux系统(银河麒麟,统信uos)使用 PageOffice 动态生成word文件
  • 使用redis-shake工具进行redis的数据同步
  • WPF如何全局应用黑白主题效果
  • pcb元器件选型与焊接测试时的一些个人经验
  • SELinux知识点
  • C++学习第四天
  • 深入了解 Spring Security 的授权核心功能
  • idea 程序打包 jar 发布
  • 前端 vue 如何区分开发环境
  • 使用php和Xunsearch提升音乐网站的歌曲搜索效果
  • vue 目录结构
  • 【Linux】进程-PCB
  • vue制作代码比较工具
  • 本地云存储 MinIO 中修改用户密码
  • Oracle-物化视图基本操作
  • Spring |(二)IoC相关内容 | bean
  • [JAVA]用MyBatis框架实现一个简单的数据查询操作
  • go版本,google-authenticator动态口令算法,二次安全校验
  • Elasticsearch面试内容整理-分析与映射
  • wsl2中kali linux下的docker使用教程(教程总结)
  • 深入实践 Shell 脚本编程:高效自动化操作指南
  • 【通俗理解】ELBO(证据下界)——机器学习中的“情感纽带”
  • leetcode hot100【LeetCode 53.最大子数组和】java实现