2

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?

1
  • 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
    – user22970
    Jun 3, 2018 at 1:03

4 Answers 4

5

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

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

2

compile code command

Introduced around 7.9, it allows code compilation and injection at the current location, documentation: https://sourceware.org/gdb/onlinedocs/gdb/Compiling-and-Injecting-Code.html

I have given a minimal runnable example at: https://stackoverflow.com/questions/5480868/how-to-call-assembly-in-gdb/31709579#31709579

I can't find out how to inject at a specific location, but if you are serious about this, patching this feature in extending compile code functionality might be the way to go.

3
  • 1
    I did not know this command of gdb, thanks for pointing it out!
    – perror
    Sep 16, 2019 at 11:05
  • 1
    But according to the comments to the answer you link to, we can't compile assembly code with this command, at least currently.
    – Ruslan
    Sep 16, 2019 at 12:09
  • @Ruslan yes, this is potentially not a super direct answer to this question, but it might be what some of the Googlers are looking for when it comes to interaction on the C level. It would be good to understand why inline assembly does not work in more detail however to see if it is just a bug or fundamental limitation, feels like bug. Sep 16, 2019 at 13:09
-1

It is possible but very complicated!! Regardless gdb, you can do the following:

  1. Using static binary instrumentation to instrument a binary file and add your arbitrary code. APIs like Dyninst is very powerful in this area.
  2. Using gcc-plugin: you can write your own gcc-plugin to add additional pass to gcc compiler. gcc-plugin is very powerful to do such works. However, you should read the internal architecture of gcc compiler and gcc-plugin API (headers file) to write a plugin.
  3. Ptrace
1
  • points 1 and 2 have nothing to do with gdb. Point 3 requires additional explanation.
    – julian
    May 21, 2019 at 14:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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