Search code examples
pythonlldb

LLDB Python ReadMemory returns NoneType


I have a script in Python where I check value of x0 register which is a pointer to some memory address and read memory from there. But in my Python script, ReadMemory return None and because of that bytearray function call throws an error. In LLDB console memory read <x0 value> works. Code is below:

import random
import lldb

debugger = lldb.SBDebugger.Create()
target = debugger.GetTargetAtIndex(0)


def modify_memory(debugger, command, result, internal_dict):

    thread = debugger.GetThread()
    # if called from console itself, debugger argument type is true but if it comes from breakpoint
    # debugger type == SBFrame. SBFrame has GetThread() method; so debugger above is actually frame
    db = lldb.SBDebugger.Create()
    if thread:
        #db.HandleCommand("print \"a\"")
        frame = thread.GetSelectedFrame()

        # Read the value of register x0
        x0 = frame.FindRegister("x0")
        x0_value = x0.GetValue()
        x0_value = int(x0_value,16)
        print(x0_value)
        # Read memory at the address stored in x0
        memory = process.ReadMemory(x0_value+16+4, 256, lldb.SBError())
        print(memory)
        if memory != None:
            print("finally something!")
            # Modify a random byte in the memory
            random_byte = random.randint(2, 255)
            memory = bytearray(memory)
            memory[random_byte] = random.randint(0, 255)

            # Write the modified memory back to the original location
            process.WriteMemory(x0_value, memory, lldb.SBError())
        
    
        process.Continue()
    else:
        db.HandleCommand("print \"thread NOT found\"")

    process.Continue()
    

I add modify_memory as a command to breakpoints.

I tried to run command from interactive console but did not manage to re-create thread, process etc variables. Also, I add function modify_memory as command but then variable "debugger" come as SBDebugger (which is actually true :) )but if I add this to a breakpoint, "debugger" variable becomes SBFrame which has the method GetThread


Solution

  • You want to use the newer command definition form:

    def command_function(debugger, command, exe_ctx, result, internal_dict):
    

    If you define your function that way, lldb will pass an SBExecutionContext in the exe_ctx parameter that contains the frame/thread/process/target you should act on in the command.

    The point is that at any given stop, lldb first queries all the threads, and for each of them that have stopped for a reason, runs the relevant callbacks, then decides whether to stop or not, then computes the selected thread. So at the time breakpoint callbacks are being run, the selected thread is still the one from the last time the debugee stopped.

    The original form was really an oversight in the design. We kept the old form around for compatibility reasons, but at this point it's unlikely there are any lldb's around that don't include the more useful form. So that's really the one you want to use.