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?
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, 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 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]
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.
It is possible but very complicated!! Regardless gdb, you can do the following: