如何使用 GDB 组装和注入指令?

逆向工程 部件 数据库
2021-06-23 15:52:11

GDB 有一个x/i命令(还有disassemble),它允许查看给定地址的指令。

我们如何做相反的事情——组装一条指令并写入给定的地址?

4个回答

GDB 似乎没有任何本机命令来组装指令。但是我们可以使用它的 Python 脚本工具来创建一个自定义命令,该命令将准备一个汇编源文件,将其提供给汇编器,并将结果字节写入下级地址空间。

这是一个.gdbinit用于 x86/x86_64 的Python 脚本示例(您可以将其粘贴到您的),使用 FASM 作为汇编程序:

python
from tempfile import mkstemp
import subprocess
import os

class Assemble(gdb.Command):
    """Assemble an instruction
Usage: assemble 0xADDRESS instruction to assemble
"""
    def __init__(self):
        super(Assemble, self).__init__("assemble", gdb.COMMAND_DATA, gdb.COMPLETE_NONE, True)
    def invoke(self, arg, from_tty):
        argPieces=arg.split()
        try:
            # addresses like those resulting from $pc can have trailing
            # junk, which we'll clear here by conversion to long
            address=gdb.execute('printf "%#lx",'+argPieces[0], to_string=True)
        except:
            print("Failed to parse address")
            return
        instruction=" ".join(argPieces[1:])
        bits=format(8*int(gdb.parse_and_eval("sizeof(void*)")),"d")

        fd,srcPath=mkstemp(prefix="gdb_asm_")
        src=os.fdopen(fd, 'w')
        src.write("use%s\norg %s\n%s\n" % (bits, address, instruction))
        src.close()

        try:
            subprocess.check_output(["fasm",srcPath], stderr=subprocess.STDOUT)
        except subprocess.CalledProcessError as err:
            print("Assembler failed with exit code", err.returncode)
            print("Output:", err.output)
            os.remove(srcPath)
            return
        os.remove(srcPath)

        binPath=srcPath+".bin"
        binaryFile=open(binPath, 'rb')
        offset=0
        while True:
            byteStr=binaryFile.read(1)
            if not byteStr:
                break
            byte=byteStr[0]
            if not isinstance(byte, int): # compatibility with python 2
                byte=ord(byte)
            gdb.execute("set *(unsigned char*)("+address+'+'+
                            format(offset,'d')+")="+format(byte,"#02x"))
            offset+=1
        binaryFile.close()
        os.remove(binPath)
        if offset==0:
            print("Assembler output an empty file")
            return

        gdb.execute("x/i "+address)
Assemble()
end

然后你会像这样使用它

gdb -q -ex starti /bin/true
Reading symbols from /bin/true...(no debugging symbols found)...done.
Starting program: /bin/true 

Program stopped.
0xf7fdd800 in _start () from /lib/ld-linux.so.2
(gdb) disas
Dump of assembler code for function _start:
=> 0xf7fdd800 <+0>:     mov    eax,esp
   0xf7fdd802 <+2>:     call   0xf7fe2160 <_dl_start>
(gdb) assemble $pc+2 mov ebp, [esi+235+edx*2]
   0xf7fdd802 <_start+2>:       mov    ebp,DWORD PTR [esi+edx*2+0xeb]

如果您的 gdb 版本 > 7.7 安装了带有 python 支持的gef声称可以利用 keystone 进行就地组装

它似乎是 pythonified gdbinit (peda pwndbg workalike)

屏幕截图页面 有一些不错的显示

compile code 命令

7.9左右引入,允许在当前位置进行代码编译和注入,文档:https : //sourceware.org/gdb/onlinedocs/gdb/Compiling-and-Injecting-Code.html

我在以下位置给出了一个最小的可运行示例:https : //stackoverflow.com/questions/5480868/how-to-call-assembly-in-gdb/31709579#31709579

我不知道如何在特定位置注入,但是如果您对此很认真,在扩展compile code功能中修补此功能可能是要走的路。

这是可能的,但非常复杂!!不管gdb,你都可以做到以下几点:

  1. 使用静态二进制检测来检测二进制文件并添加您的任意代码。像 Dyninst 这样的 API 在这方面非常强大。
  2. 使用 gcc-plugin:您可以编写自己的 gcc-plugin 以向 gcc 编译器添加额外的传递。gcc-plugin 做这样的工作非常强大。但是,您应该阅读 gcc 编译器和 gcc-plugin API(头文件)的内部架构来编写插件。
  3. 跟踪