为什么不能编辑伪代码?

逆向工程 艾达 吉德拉
2021-06-11 02:21:16

在这里涉足,所以可能是一个愚蠢的问题......我知道 ida、二进制忍者和 ghidra 在生成伪代码方面非常强大。从我读过的所有伪代码都无法实时编辑,但可以作为程序集进行编辑,我只是​​想知道为什么您不能执行与下面类似的过程?

  1. 将exe反编译为假代码

  2. 制作用于在 c 或 c++ 中编译的代码模板

  3. 将假代码加载到模板中

  4. 编译基本模板并在汇编代码生成时停止

  5. 从中途编译的 c++ exe 复制程序集

  6. 将中途编译的代码作为汇编自动替换为 ida 汇编代码

或者,

  1. 将exe反编译为假代码

  2. 制作用于在 c 或 c++ 中编译的代码模板

  3. 将假代码加载到模板中

  4. 编译基本模板并在汇编代码生成时停止

  5. 使用 pdb 将函数加载到 ida 等中

  6. 为您的模板 exe 生成假代码

  7. 转到函数并从那里获取程序集以复制和替换

我知道这种方法不会很快……但我很惊讶据我所知没有人尝试过这样的方法?我只是错过了一些明显的东西吗?

3个回答

从我读过的所有伪代码都无法实时编辑,但可以作为程序集进行编辑

这并不完全正确。甚至恰恰相反:反编译器不可能是完美的(编译步骤丢失了太多信息)。因此,他们需要人类(逆向工程师)的帮助。至少在我看来,提供这种帮助是逆向工程中最重要的一步:正确获取数据类型。当然,重命名变量对可读性有很大帮助,但反编译器并不真正需要它。但是,更改变量或函数的类型会将信息反馈回反编译器,然后反编译器可以运行另一遍并改进结果。然后,人类可以再次进一步改进该结果。如果我需要命名静态二进制逆向工程中最重要的步骤,我会调用这个循环:反编译、重新输入、重复。

既然我们已经解决了这个问题,我将尝试解决您提出的建议步骤:我不完全确定我理解您的意思,但我认为第 2/4 步中出现了一个大问题:

制作用于在 c 或 c++ 中编译的代码模板

反编译器生成的代码并不是真正的C/C++ 代码。从形式上讲,它只能作为具有类似 C 语法的伪代码。有效的 C 和“反编译器 C”之间的区别当然取决于你的反编译器(Hex-Rays、Binary Ninja、Ghidra),但举一个简单的例子(还有更多,其中许多要严重得多):如果 Ghidra 的反编译器不确定给定变量是什么数据类型,它将分配“类型”undefined这当然不是 C 中的有效数据类型,因此无法编译为可执行文件(即步骤 4 失败)。

@born 提出了一些很棒的观点,但我认为绝对值得一提的,整个想法本质上并没有什么不可能不过,编译和抓取程序集可能不是最好的选择。

将整个事情视为不可能的事情是不对的。IDA 显然在该领域具有潜力;选择伪代码并单击“复制到程序集”。它将在程序集中生成注释,将其映射到伪代码函数的来源。

这是一个程序的三个不同相关阶段的比较;源代码、伪代码和 ASM:

来源 ( clang -w -o test) :

int main(void)
{
  printf("hello world");
}

请注意不正确但功能正常的 使用printf("string")而不是printf("%s", "string")这是另一场辩论,但它会搞砸反编译

IDA 反编译(伪代码):

int __cdecl main(int argc, const char **argv, const char **envp)
{
  printf("hello world", argv, envp);
  return 0;
}

这是错误的。printf 不会接受这些值,由于“hello world”中缺少格式化“%s”字符串,因此需要 0 个额外参数。一个简单的错误搞砸了伪代码输出。

由 IDA 反汇编(注意其中一些说明可能不正确)

push    rbp
mov     rbp, rsp
sub     rsp, 10h
; 2:   printf("hello world", argv, envp);
lea     rdi, aHelloWorld ; "hello world"
mov     al, 0
call    _printf
; 3:   return 0;
xor     ecx, ecx
mov     [rbp+var_4], eax
mov     eax, ecx
add     rsp, 10h
pop     rbp
retn

假设您想编辑字符串:
当然,只需编辑它引用的位置。哦,但是您想要一个超过 11 个字符的字符,因此您需要找到某个未使用的地方并将字符串指针映射到该地址。那很复杂。

该程序的整个可执行部分也有 12 条指令长。你几乎没有空间来改变任何东西,添加东西是一个完全不同的球赛。

尚未完成的可能原因

  • 一个巨大的障碍是伪代码有时是多么不可靠。将 Hopper 伪代码与 IDA/Ghidra 的某个时候进行比较,作为一个很好的例子。这是一种有根据的猜测,而不是可靠的猜测。有些甚至不创建变量,尝试编译 Hopper 伪代码是浪费时间。
  • 大多数需要修补二进制文件的人想要或需要修补程序集。ASM 的工作方式与 C 不同,当您打补丁时,您需要更多地考虑程序集的工作方式,而不是用于创建它的 C 代码。
  • 我所知道的大多数反编译器在单独修补程序集方面已经相当糟糕。一个非常基本的十六进制编辑器做得更好。尝试修补超过 4 个字节的 IDA 会让你头疼

不过,我不认为这几乎是不可能的。不是通过编译肯定的。但是你可以尝试的是这样的:

  • 跟踪反编译器如何将 <x assembly> 映射到 <y psuedocode>,并且每当 <y psuedocode> 更改时,为创建它的 <x assembly> 创建二进制补丁。

    • 这可以说是做到这一点的“最佳”方式,并且需要很长时间来编写。
  • 将函数调用替换为您自己代码在别处的分支(假设可以找到空间)。如果我没记错的话,“Cheat Engine”(我已经有一段时间没有使用 Windows 了,抱歉)有类似的东西。那么也许可以使用编译器来生成函数。

    • 这是一种懒惰的方法,最终可能会花费更多的工作来使反编译器输出可编译。只有 IDA/Ghidra 反编译足够接近可行。我以前手动完成过。

这两者都需要了解组装以验证补丁是否正确;错误的程序会使您的程序停止运行,并且没有任何工具可以可靠地正确执行此操作。

#2 仍然有缺陷。我花了 2 个多小时来完善单个函数的反编译/反汇编;正确命名内存中的所有内容,手动定义每个结构等。即使使用完美的反编译,它仍然需要编译。

也许您可以在一些复杂的脚本中自己完成这项工作。这是一个问题,我建议您在有经验时重新审视;这是一个非常有趣的话题,IDAPython 可能使它几乎可行。

对于在每个拥有的伪代码中进行编辑,请务必考虑它的心理学相关性,然后您会为那些没有根据它改变 indetity 的人获得人类的意义,只有那些没有阅读过的人

但是如果代码不可访问(没有伪代码),您可以将字符字典制作为二进制及其在 cpu 上的函数,这些字典在 java 或 python 上具有高性能(慢)