向二进制文件添加指令的选项有哪些?

逆向工程 部件
2021-06-16 19:25:15

如果任何术语不正确,我对倒车还很陌生,所以提前道歉。

我目前正在 Windows 上使用 ghidra 来查看二进制文件的指令/反编译,我希望向现有函数添加一些指令以更改其行为。在这种情况下,这是相当简单的,因为我只想向现有函数参数添加一个固定值,但我还想了解一些有关插入代码稍微复杂的更高级情况的信息。

我做了一个小测试程序来测试这个,我设法通过在十六进制编辑器中编辑二进制文件并简单地移动函数字节并插入我自己的来添加指令。但是我意识到这只是因为函数后面有一堆空内存,所以我可以将它们全部向下移动,但情况并非总是如此。

                         **************************************************************
                         *                          FUNCTION                          *
                         **************************************************************
                         ulonglong __fastcall FUN_140011810(int param_1, int para
         ulonglong         RAX:8          <RETURN>
         int               ECX:4          param_1
         int               EDX:4          param_2
         undefined4        Stack[0x10]:4  local_res10                             XREF[3]:     140011810(W), 
                                                                                               140011835(R), 
                                                                                               140011848(R)  
         undefined4        Stack[0x8]:4   local_res8                              XREF[2]:     140011814(W), 
                                                                                               14001184e(R)  
         undefined1        Stack[-0x10]:1 local_10                                XREF[1]:     140011867(*)  
         undefined4        Stack[-0xf4]:4 local_f4                                XREF[4]:     140011858(W), 
                                                                                               14001185b(R), 
                                                                                               140011861(W), 
                                                                                               140011864(R)  
         undefined1        Stack[-0xf8]:1 local_f8                                XREF[1]:     140011821(*)  
                         FUN_140011810                                   XREF[1]:     thunk_FUN_140011810:14001104b(T), 
                                                                                      thunk_FUN_140011810:14001104b(j)  
   140011810 89 54 24 10     MOV        dword ptr [RSP + local_res10],param_2
   140011814 89 4c 24 08     MOV        dword ptr [RSP + local_res8],param_1
   140011818 55              PUSH       RBP
   140011819 57              PUSH       RDI
   14001181a 48 81 ec        SUB        RSP,0x108
             08 01 00 00
   140011821 48 8d 6c        LEA        RBP=>local_f8,[RSP + 0x20]
             24 20
   140011826 48 8b fc        MOV        RDI,RSP
   140011829 b9 42 00        MOV        param_1,0x42
             00 00
   14001182e b8 cc cc        MOV        EAX,0xcccccccc
             cc cc
   140011833 f3 ab           STOSD.REP  RDI
   140011835 8b 8c 24        MOV        param_1,dword ptr [RSP + local_res10]
             28 01 00 00
   14001183c 48 8d 0d        LEA        param_1,[DAT_140021008]                          = 01h
             c5 f7 00 00
   140011843 e8 44 f8        CALL       thunk_FUN_140011e80                              undefined thunk_FUN_140011e80(ch
             ff ff
   140011848 8b 85 08        MOV        EAX,dword ptr [RBP + local_res10]
             01 00 00
   14001184e 8b 8d 00        MOV        param_1,dword ptr [RBP + local_res8]
             01 00 00
   140011854 03 c8           ADD        param_1,EAX
   140011856 8b c1           MOV        EAX,param_1
   140011858 89 45 04        MOV        dword ptr [RBP + local_f4],EAX
   14001185b 8b 45 04        MOV        EAX,dword ptr [RBP + local_f4]
   14001185e 83 c0 0a        ADD        EAX,0xa
   140011861 89 45 04        MOV        dword ptr [RBP + local_f4],EAX
   140011864 8b 45 04        MOV        EAX,dword ptr [RBP + local_f4]
   140011867 48 8d a5        LEA        RSP=>local_10,[RBP + 0xe8]
             e8 00 00 00
   14001186e 5f              POP        RDI
   14001186f 5d              POP        RBP
   140011870 c3              RET
   140011871 cc              ??         CCh
   140011872 cc              ??         CCh
   140011873 cc              ??         CCh
   140011874 cc              ??         CCh
   140011875 cc              ??         CCh
   140011876 cc              ??         CCh
   140011877 cc              ??         CCh
   140011878 cc              ??         CCh
   140011879 cc              ??         CCh
   14001187a cc              ??         CCh
   14001187b cc              ??         CCh
   14001187c cc              ??         CCh
   14001187d cc              ??         CCh
   14001187e cc              ??         CCh
   14001187f cc              ??         CCh
   140011880 cc              ??         CCh
   140011881 cc              ??         CCh
   140011882 cc              ??         CCh

具体来说,14001185e 83 c0 0a ADD EAX,0xa 我可以复制此指令并更改0xa以更改输出值。

在更复杂的二进制文件中,我有一个具有类似参数的更大函数,只是在函数末尾没有额外的内存,因此这种移动剩余字节的方法不起作用,因为下面有另一个函数。我也不能删除任何当前的指令来腾出空间,因为这可能会破坏现有的功能。二进制文件的其他地方有很多空内存,所以我想添加一条 jmp 指令来执行一些指令,然后跳回去,但有些指令使用局部变量,所以我不确定这是否可行。

因此,鉴于上面的示例,并且函数末尾没有额外的内存,我该如何插入一些自定义指令?

3个回答

您真正要寻找的是代码螺柱代码洞使用未使用的空间来跳跃添加您自己的代码并跳回。代码洞穴的问题在于 PE 二进制文件中有一个大小限制,不会给你很多空间。

代码螺柱上另一方面是增加一个加法.text段。使用此方法,您可以避免 DEP 的陷阱并拥有更多空间。我可以轻松地使用多达 8 MB的空间。

您需要做的就是在 StudPE 中打开二进制文件并添加一个部分并确保它是可执行的,然后直接跳转到它......做任何你想做的事然后跳回去。

也不要担心插入 BYTES 只需在调试器(olly 或 x64dbg)中使用 Ram Michael 的多汇编工具HERE,然后复制并粘贴汇编代码。它需要匹配 MASM 语法,但转换很容易。

我已经完成了这样的整个项目,所以请放心,这是做事的简单方法,可以节省自己的工作时间。

您可以在此处使用此工具创建一个Stud PE

我相信,您必须将该地址更改为适当的“JMP”命令,并在可执行文件的末尾附加 ADD EAX,0xa,所有其他命令(如果被覆盖)以及其余所需的操作以及完成后,jmp 返回增加的地址,您将需要更正可执行文件头文件以及修改后的文件长度。当然,在添加您自己的指令时,请记住更正所有更改的寄存器指针,例如堆栈...

您正在询问如何在现有代码中插入“代码洞穴”或“阳台”。你可以像这样继续:

  1. 在代码中选择您希望分支到代码洞穴的位置。您应该准备好用五个字节、一个JMP代码和四个偏移字节替换现有代码,并可能在多余的字节之后使用NOP语句来避免垃圾代码。
  2. 仔细注意汇编程序助记符以及要由您的JMP.
  3. 计算偏移量并修补现有代码,例如在您选择的十六进制编辑器中。请记住,您处于应用 RIP 相对寻址的 64 位世界(您的示例显示了这一点)。
  4. 在你的代码洞的位置,重新输入你替换的指令JMP如果涉及到地址,则按照RIP相对寻址重新计算。
  5. 保存您打算修改的所有寄存器/标志,并且在离开代码洞时应该恢复其原始值。
  6. 输入您的新代码字节。
  7. 适当地恢复寄存器/标志。
  8. JMP 回到原始代码的下一条语句。

我举个例子,如何计算JMP语句的目标地址

假设你想更换

LEA param_1, [DAT_140021008]

带有JMP to 140018000代码洞可用空间的语句,以及随后LEA在新位置重新插入该命令的位置。

当然

  • 该地址必须允许执行代码(注意 DEP“数据执行保护”)。
  • 你应该能够在你的十六进制编辑器中找到那个位置。我不知道 Ghidra 是否允许直接在代码中打补丁。

计算地址偏移量:取你的目的地址,得到下一条指令的差值。您替换的代码将类似于以下代码(语法可能不正确):

14001183c E9 xx xx xx xx    JMP 140018000       ; the xx's to be calculated
140011841 90                NOP
140011842 90                NOP
140011843 e8 44 f8 ff ff    CALL thunk_FUN_140011e80 ;existing code

Offset: 140018000 - 140011841 = 67bf, the JMP line becoming
14001183c E9 bf 67 00 00    JMP 140018000       

在 address140018000您可能希望重新插入 LEA 声明:

140018000 48 8d 0d 79 9e ff ff      LEA param_1, [DAT_140011e80]
140018007 Your new code
...
JMP back to 140011843

已计算出 LEA 调用的正确偏移量:

140011e80 - 140018007 = ffff9e79 = -6187

也许助记符param_1将被替换为ECX,因为该寄存器保存了param_1.

在你的代码洞结束时,你必须以JMP同样的方式计算回你的原始代码。

您可能已经注意到,由于需要重新计算目标地址,因此您的简单“下移”方法在一般情况下也需要特别注意。

备注:如果您在地址处的语句中查看示例代码140011843

CALL       thunk_FUN_140011e80

您可能会注意到“ thunk_”前缀。这意味着立即地址不同于140011e80. 它是一个“代理”,可能是JMP编译器插入的一个目标,指向 Ghidra 代码中指示的地址。Ghidra 会为你计算这个。

概述的方法是勾勒出代码洞的一般结构。必须考虑诸如位于堆栈上的局部变量(保持堆栈一致)或PE64 标头的重定位表中列出的项目等问题。必须小心正确处理这些。