NationalSecurityAgency / ghidra

Ghidra is a software reverse engineering (SRE) framework
https://www.nsa.gov/ghidra
Apache License 2.0
49.64k stars 5.71k forks source link

PPC Big-endian VLE instruction emulation error #4392

Open 7ingsong opened 2 years ago

7ingsong commented 2 years ago

Describe the bug

Error during emulation of code PPC Big-endian VLE, Ghidra can't emulate the instruction after branch "se_mflr r0"

emu-error.py> Running...
Address: 0x00000000 (se_mflr r0)
Address: 0x00000002 (se_bl 0x00000010)
Address: 0x00000010 (se_mflr r0)
Emulation Error: 'Instruction decode failed (Unable to resolve constructor at 00000010), PC=00000010'
emu-error.py> Finished!

To Reproduce

                             //
                             // ram 
                             // ram:00000000-ram:0000004f
                             //
                             *************************************************************
                             *                           FUNCTION                         
                             *************************************************************
                             undefined  FUN_00000000 ()
                               assume vle = 0x1
             undefined         r3:1           <RETURN>
                             FUN_00000000
      ram:00000000  00  80             se_mflr    r0
      ram:00000002  E9  07             se_bl      FUN_00000010                                     undefined FUN_00000010()
      ram:00000004  00  90             se_mtlr    r0
      ram:00000006  00  04             se_blr
      ram:00000008  00                 ??         00h
      ram:00000009  00                 ??         00h
      ram:0000000a  00                 ??         00h
      ram:0000000b  00                 ??         00h
      ram:0000000c  00                 ??         00h
      ram:0000000d  00                 ??         00h
      ram:0000000e  00                 ??         00h
      ram:0000000f  00                 ??         00h
                             *************************************************************
                             *                           FUNCTION                         
                             *************************************************************
                             undefined  FUN_00000010 ()
                               assume vle = 0x1
             undefined         r3:1           <RETURN>
                             FUN_00000010                                    XREF[1]:     FUN_00000000:00000002 (c)   
      ram:00000010  00  80             se_mflr    r0
      ram:00000012  00  90             se_mtlr    r0
      ram:00000014  00  04             se_blr
      ram:00000016  00                 ??         00h
      ram:00000017  00                 ??         00h
      ram:00000018  00                 ??         00h
      ram:00000019  00                 ??         00h

def getAddress(offset): return currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(offset)

def getSymbolAddress(symbolName): symbol = SymbolUtilities.getLabelOrFunctionSymbol(currentProgram, symbolName, None) if (symbol != None): return symbol.getAddress() else: raise("Failed to locate label: {}".format(symbolName))

emuHelper = EmulatorHelper(currentProgram)

codeStart=0x0 codeStop=0x6

emuHelper.writeRegister(emuHelper.getPCRegister(), codeStart)

while monitor.isCancelled() is False: executionAddress = emuHelper.getExecutionAddress()

if (executionAddress == getAddress(codeStop)):
    print("Emulation complete.")
    break
try:
    print("Address: 0x{} ({})".format(executionAddress, getInstructionAt(executionAddress)))
except:
    pass
success = emuHelper.step(monitor)   
if (success == False):
    lastError = emuHelper.getLastError()
    print("Emulation Error: '{}'".format(lastError))
    break

Cleanup resources and release hold on currentProgram

emuHelper.dispose()

- Run the script and see an error during emulation instruction "se_mflr r0"
```text
emu-error.py> Running...
Address: 0x00000000 (se_mflr r0)
Address: 0x00000002 (se_bl 0x00000010)
Address: 0x00000010 (se_mflr r0)
Emulation Error: 'Instruction decode failed (Unable to resolve constructor at 00000010), PC=00000010'
emu-error.py> Finished!

Environment (please complete the following information):

Additional context

7ingsong commented 2 years ago

I found a similar issue https://github.com/NationalSecurityAgency/ghidra/issues/3428

I tried to set VLE register to 1 for the region, but it doesn't help. The same error

from ghidra.app.emulator import EmulatorHelper
from ghidra.app.emulator import EmulatorHelper
from ghidra.program.model.symbol import SymbolUtilities
from ghidra.program.model.lang import RegisterValue
from java.math import BigInteger

def getAddress(offset):
    return currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(offset)

def getSymbolAddress(symbolName):
    symbol = SymbolUtilities.getLabelOrFunctionSymbol(currentProgram, symbolName, None)
    if (symbol != None):
        return symbol.getAddress()
    else:
        raise("Failed to locate label: {}".format(symbolName))

vleReg =  currentProgram.language.getRegister("vle")
value = RegisterValue(vleReg, BigInteger.ONE)
loadedMemory = currentProgram.getMemory().getLoadedAndInitializedAddressSet()
#print("addr",loadedMemory.getMinAddress(),loadedMemory.getMaxAddress())
currentProgram.getProgramContext().setRegisterValue(getAddress(0x0), getAddress(0x50), value) 

emuHelper = EmulatorHelper(currentProgram)

codeStart=0x0
codeStop=0x6

emuHelper.writeRegister(emuHelper.getPCRegister(), codeStart)

while monitor.isCancelled() is False:
    executionAddress = emuHelper.getExecutionAddress()  

    if (executionAddress == getAddress(codeStop)):
        print("Emulation complete.")
        break

    print("Address: 0x{} ({})".format(executionAddress, getInstructionAt(executionAddress)))
    success = emuHelper.step(monitor)   
    if (success == False):
        lastError = emuHelper.getLastError()
        print("Emulation Error: '{}'".format(lastError))
        break

emuHelper.dispose()

The error

emu-error.py> Running...
Address: 0x00000000 (se_mflr r0)
Address: 0x00000002 (se_bl 0x00000010)
Address: 0x00000010 (se_mflr r0)
Emulation Error: 'Instruction decode failed (Unable to resolve constructor at 00000010), PC=00000010'
emu-error.py> Finished!
GhidorahRex commented 2 years ago

I'm still looking into this, but I can get the python script to run without error by adding a call to emuHelper.setContextRegister in the try block. If I set the context register at the beginning of the script it won't flow properly to other functions. What is weird here is if I get the register value I get the same value before and after .

from ghidra.app.emulator import EmulatorHelper
from ghidra.program.model.symbol import SymbolUtilities
from ghidra.program.model.symbol import SymbolUtilities
from ghidra.program.model.lang import RegisterValue
from java.math import BigInteger

def getAddress(offset):
    return currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(offset)

def getSymbolAddress(symbolName):
    symbol = SymbolUtilities.getLabelOrFunctionSymbol(currentProgram, symbolName, None)
    if (symbol != None):
        return symbol.getAddress()
    else:
        raise("Failed to locate label: {}".format(symbolName))

emuHelper = EmulatorHelper(currentProgram)

codeStart=0x0
codeStop=0x6

emuHelper.writeRegister(emuHelper.getPCRegister(), codeStart)

while monitor.isCancelled() is False:
    executionAddress = emuHelper.getExecutionAddress()  

    if (executionAddress == getAddress(codeStop)):
        print("Emulation complete.")
        break
    try:
        vleReg = currentProgram.language.getRegister("vle")
        emuHelper.setContextRegister(vleReg,BigInteger.ONE)
        print("Address: 0x{} ({})".format(executionAddress, getInstructionAt(executionAddress)))
    except:
        raise
    success = emuHelper.step(monitor)   
    if (success == False):
        lastError = emuHelper.getLastError()
        print("Emulation Error: '{}'".format(lastError))
        break

# Cleanup resources and release hold on currentProgram
emuHelper.dispose()

Alternatively, replacing the emuHelper.setContextRegister(vleReg,BigInteger.ONE) call with the following also works: emuHelper.setContextRegister(emuHelper.getContextRegister())

GhidorahRex commented 1 year ago

The issue here is that the emulator is not properly setting the seedContext in the disassembler. To correct it, we need to modify Emulate.java with this:

    public void setSeedContext(ProgramContext seedContext) {
        pseudoDisassembler.setSeedContext(new DisassemblerContextImpl(seedContext));
    }

Emulator.java with this:

    public void setSeedContext(ProgramContext seedContext) {
        emulator.setSeedContext(seedContext);
    }

and modify your script to either rely on the context in the program listing:

emHelper.getEmulator().setSeedContext(program.getProgramContext())

or you can just manually set the vle context variable:

ProgramContextImpl context = new ProgramContextImpl(program.getLanguage())
Register vleReg = currentProgram.language.getRegister("vle")
context.setValue(vleReg, startAddr, endAddr, BigInteger.ONE)
emulator.setSeedContext(context)

Either way, I'll get the changes to the emulator incorporated so these steps work out of the box.