1

GDB has a x/i command (and also disassemble) which allows to view the instructions at a given address.

How can we do the reverse – assemble an instruction and write to a given address?

  • I'll just note that you should be extremely careful about the legth of the instructions. Even stuff that looks trivial might be dangerous, like operand override (0x66) when using eax instead of ax – Amirag Jun 3 '18 at 1:03
4

GDB doesn't seem to have any native command to assemble instructions. But we can use its Python scripting facility to create a custom command, which would prepare an assembly source file, feed it to an assembler, and write the resulting bytes to the inferior's address space.

Here's an example python script (which you can paste into your .gdbinit) for x86/x86_64, using FASM as the assembler:

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

Then you'd use it like

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]
2

if you have gdb version > 7.7 installing gef with python support claims to leverage keystone for in place assembly

it appears to be pythonified gdbinit (peda pwndbg workalike)

the screen shots page has some nice displays

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.