mirror of
https://github.com/dobin/avred
synced 2026-06-08 13:54:13 +00:00
241 lines
11 KiB
Python
241 lines
11 KiB
Python
import sys
|
|
from intervaltree import Interval, IntervalTree
|
|
from pcodedmp.lowlevel import *
|
|
|
|
|
|
class DisasmEntry():
|
|
def __init__(self, modulename, line, lineStart, lineEnd, text):
|
|
self.modulename = modulename
|
|
self.lineNr = line
|
|
self.begin = lineStart
|
|
self.end = lineEnd
|
|
self.text = text
|
|
|
|
|
|
def myPrint(append, file=None, end="\n"):
|
|
return append + end
|
|
|
|
|
|
def dumpLine(moduleData, modulename, lineStart, lineLength, endian, vbaVer, is64bit,
|
|
identifiers, objectTable, indirectTable, declarationTable, verbose, line, output_file=sys.stdout):
|
|
varTypesLong = ['Var', '?', 'Int', 'Lng', 'Sng', 'Dbl', 'Cur', 'Date', 'Str', 'Obj', 'Err', 'Bool', 'Var']
|
|
specials = ['False', 'True', 'Null', 'Empty']
|
|
options = ['Base 0', 'Base 1', 'Compare Text', 'Compare Binary', 'Explicit', 'Private Module']
|
|
|
|
#if verbose and (lineLength > 0):
|
|
# print('{:04X}: '.format(lineStart), end='', file=output_file)
|
|
#print('Line #{:d}:'.format(line), file=output_file)
|
|
if lineLength <= 0:
|
|
return
|
|
#if verbose:
|
|
# print(hexdump(moduleData[lineStart:lineStart + lineLength]), file=output_file)
|
|
|
|
offset = lineStart
|
|
endOfLine = lineStart + lineLength
|
|
disasmEntry = ''
|
|
while offset < endOfLine:
|
|
offset, opcode = getVar(moduleData, offset, endian, False)
|
|
opType = (opcode & ~0x03FF) >> 10
|
|
opcode &= 0x03FF
|
|
translatedOpcode = translateOpcode(opcode, vbaVer, is64bit)
|
|
if not translatedOpcode in opcodes:
|
|
disasmEntry += myPrint('Unrecognized opcode 0x{:04X} at offset 0x{:08X}.'.format(opcode, offset), file=output_file)
|
|
# print(disasmEntry)
|
|
return
|
|
instruction = opcodes[translatedOpcode]
|
|
mnemonic = instruction['mnem']
|
|
disasmEntry += myPrint('\t', end='', file=output_file)
|
|
if verbose:
|
|
disasmEntry += myPrint('{:04X} '.format(opcode), end='', file=output_file)
|
|
disasmEntry += myPrint('{} '.format(mnemonic), end='', file=output_file)
|
|
|
|
if mnemonic in ['Coerce', 'CoerceVar', 'DefType']:
|
|
if opType < len(varTypesLong):
|
|
disasmEntry += myPrint('({}) '.format(varTypesLong[opType]), end='', file=output_file)
|
|
elif opType == 17:
|
|
disasmEntry += myPrint('(Byte) ', end='', file=output_file)
|
|
else:
|
|
disasmEntry += myPrint('({:d}) '.format(opType), end='', file=output_file)
|
|
elif mnemonic in ['Dim', 'DimImplicit', 'Type']:
|
|
dimType = []
|
|
if opType & 0x04:
|
|
dimType.append('Global')
|
|
elif opType & 0x08:
|
|
dimType.append('Public')
|
|
elif opType & 0x10:
|
|
dimType.append('Private')
|
|
elif opType & 0x20:
|
|
dimType.append('Static')
|
|
if (opType & 0x01) and (mnemonic != 'Type'):
|
|
dimType.append('Const')
|
|
if len(dimType):
|
|
disasmEntry += myPrint('({}) '.format(' '.join(dimType)), end='', file=output_file)
|
|
elif mnemonic == 'LitVarSpecial':
|
|
disasmEntry += myPrint('({})'.format(specials[opType]), end='', file=output_file)
|
|
elif mnemonic in ['ArgsCall', 'ArgsMemCall', 'ArgsMemCallWith']:
|
|
if opType < 16:
|
|
disasmEntry += myPrint('(Call) ', end='', file=output_file)
|
|
else:
|
|
opType -= 16
|
|
elif mnemonic == 'Option':
|
|
disasmEntry += myPrint(' ({})'.format(options[opType]), end='', file=output_file)
|
|
elif mnemonic in ['Redim', 'RedimAs']:
|
|
if opType & 16:
|
|
disasmEntry += myPrint('(Preserve) ', end='', file=output_file)
|
|
|
|
for arg in instruction['args']:
|
|
if arg == 'name':
|
|
offset, word = getVar(moduleData, offset, endian, False)
|
|
theName = disasmName(word, identifiers, mnemonic, opType, vbaVer, is64bit)
|
|
disasmEntry += myPrint('{}'.format(theName), end='', file=output_file)
|
|
elif arg in ['0x', 'imp_']:
|
|
offset, word = getVar(moduleData, offset, endian, False)
|
|
theImp = disasmImp(objectTable, identifiers, arg, word, mnemonic, endian, vbaVer, is64bit)
|
|
disasmEntry += myPrint('{}'.format(theImp), end='', file=output_file)
|
|
elif arg in ['func_', 'var_', 'rec_', 'type_', 'context_']:
|
|
offset, dword = getVar(moduleData, offset, endian, True)
|
|
if (arg == 'rec_') and (len(indirectTable) >= dword + 20):
|
|
theRec = disasmRec(indirectTable, identifiers, dword, endian, vbaVer, is64bit)
|
|
disasmEntry += myPrint('{}'.format(theRec), end='', file=output_file)
|
|
elif (arg == 'type_') and (len(indirectTable) >= dword + 7):
|
|
theType = disasmType(indirectTable, dword)
|
|
disasmEntry += myPrint('(As {})'.format(theType), end='', file=output_file)
|
|
elif (arg == 'var_') and (len(indirectTable) >= dword + 16):
|
|
if opType & 0x20:
|
|
disasmEntry += myPrint('(WithEvents) ', end='', file=output_file)
|
|
theVar = disasmVar(indirectTable, objectTable, identifiers, dword, endian, vbaVer, is64bit)
|
|
disasmEntry += myPrint('{}'.format(theVar), end='', file=output_file)
|
|
if opType & 0x10:
|
|
word = getWord(moduleData, offset, endian)
|
|
offset += 2
|
|
disasmEntry += myPrint(' 0x{:04X}'.format(word), end='', file=output_file)
|
|
elif (arg == 'func_') and (len(indirectTable) >= dword + 61):
|
|
theFunc = disasmFunc(indirectTable, declarationTable, identifiers, dword, opType, endian, vbaVer, is64bit)
|
|
disasmEntry += myPrint('{}'.format(theFunc), end='', file=output_file)
|
|
else:
|
|
disasmEntry += myPrint('{}{:08X} '.format(arg, dword), end='', file=output_file)
|
|
if is64bit and (arg == 'context_'):
|
|
offset, dword = getVar(moduleData, offset, endian, True)
|
|
disasmEntry += myPrint('{:08X} '.format(dword), end='', file=output_file)
|
|
|
|
if instruction['varg']:
|
|
offset, wLength = getVar(moduleData, offset, endian, False)
|
|
theVarArg = disasmVarArg(moduleData, identifiers, offset, wLength, mnemonic, endian, vbaVer, is64bit)
|
|
disasmEntry += myPrint('{}'.format(theVarArg), end='', file=output_file)
|
|
offset += wLength
|
|
if wLength & 1:
|
|
offset += 1
|
|
|
|
disasmEntry += "\n"
|
|
|
|
#print("{}-{}: {}".format(lineStart, lineStart+lineLength, disasmEntry))
|
|
entry = DisasmEntry(modulename, line, lineStart, lineStart+lineLength, disasmEntry)
|
|
return entry
|
|
|
|
|
|
def pcodeDump(moduleData, vbaProjectData, modulepath, identifiers, is64bit, disasmOnly, verbose, output_file = sys.stdout):
|
|
it = IntervalTree()
|
|
|
|
if verbose and not disasmOnly:
|
|
print(hexdump(moduleData), file=output_file)
|
|
# Determine endinanness: PC (little-endian) or Mac (big-endian)
|
|
if getWord(moduleData, 2, '<') > 0xFF:
|
|
endian = '>'
|
|
else:
|
|
endian = '<'
|
|
# TODO - Handle VBA3 modules
|
|
vbaVer = 3
|
|
try:
|
|
version = getWord(vbaProjectData, 2, endian)
|
|
if verbose:
|
|
print('Internal Office version: 0x{:04X}.'.format(version), file=output_file)
|
|
# Office 2010 is 0x0097; Office 2013 is 0x00A3;
|
|
# Office 2016 PC 32-bit is 0x00B2, 64-bit is 0x00D7, Mac is 0x00D9
|
|
if version >= 0x6B:
|
|
if version >= 0x97:
|
|
vbaVer = 7
|
|
else:
|
|
vbaVer = 6
|
|
if is64bit:
|
|
dwLength = getDWord(moduleData, 0x0043, endian)
|
|
declarationTable = moduleData[0x0047:0x0047 + dwLength]
|
|
dwLength = getDWord(moduleData, 0x0011, endian)
|
|
tableStart = dwLength + 12
|
|
else:
|
|
dwLength = getDWord(moduleData, 0x003F, endian)
|
|
declarationTable = moduleData[0x0043:0x0043 + dwLength]
|
|
dwLength = getDWord(moduleData, 0x0011, endian)
|
|
tableStart = dwLength + 10
|
|
dwLength = getDWord(moduleData, tableStart, endian)
|
|
tableStart += 4
|
|
indirectTable = moduleData[tableStart:tableStart + dwLength]
|
|
dwLength = getDWord(moduleData, 0x0005, endian)
|
|
dwLength2 = dwLength + 0x8A
|
|
dwLength = getDWord(moduleData, dwLength2, endian)
|
|
dwLength2 += 4
|
|
objectTable = moduleData[dwLength2:dwLength2 + dwLength]
|
|
offset = 0x0019
|
|
else:
|
|
# VBA5
|
|
vbaVer = 5
|
|
offset = 11
|
|
dwLength = getDWord(moduleData, offset, endian)
|
|
offs = offset + 4
|
|
declarationTable = moduleData[offs:offs + dwLength]
|
|
offset = skipStructure(moduleData, offset, endian, True, 1, False)
|
|
offset += 64
|
|
offset = skipStructure(moduleData, offset, endian, False, 16, False)
|
|
offset = skipStructure(moduleData, offset, endian, True, 1, False)
|
|
offset += 6
|
|
offset = skipStructure(moduleData, offset, endian, True, 1, False)
|
|
offs = offset + 8
|
|
dwLength = getDWord(moduleData, offs, endian)
|
|
tableStart = dwLength + 14
|
|
offs = dwLength + 10
|
|
dwLength = getDWord(moduleData, offs, endian)
|
|
indirectTable = moduleData[tableStart:tableStart + dwLength]
|
|
dwLength = getDWord(moduleData, offset, endian)
|
|
offs = dwLength + 0x008A
|
|
dwLength = getDWord(moduleData, offs, endian)
|
|
offs += 4
|
|
objectTable = moduleData[offs:offs + dwLength]
|
|
offset += 77
|
|
|
|
if verbose:
|
|
if len(declarationTable):
|
|
print('Declaration table:', file=output_file)
|
|
print(hexdump(declarationTable), file=output_file)
|
|
if len(indirectTable):
|
|
print('Indirect table:', file=output_file)
|
|
print(hexdump(indirectTable), file=output_file)
|
|
if len(objectTable):
|
|
print('Object table:', file=output_file)
|
|
print(hexdump(objectTable), file=output_file)
|
|
|
|
dwLength = getDWord(moduleData, offset, endian)
|
|
offset = dwLength + 0x003C
|
|
offset, magic = getVar(moduleData, offset, endian, False)
|
|
if magic != 0xCAFE:
|
|
return
|
|
offset += 2
|
|
offset, numLines = getVar(moduleData, offset, endian, False)
|
|
pcodeStart = offset + numLines * 12 + 10
|
|
|
|
for line in range(numLines):
|
|
offset += 4
|
|
offset, lineLength = getVar(moduleData, offset, endian, False)
|
|
offset += 2
|
|
offset, lineOffset = getVar(moduleData, offset, endian, True)
|
|
disasmEntry = dumpLine(moduleData, modulepath, pcodeStart + lineOffset, lineLength, endian, vbaVer, is64bit, identifiers,
|
|
objectTable, indirectTable, declarationTable, verbose, line, output_file=output_file)
|
|
|
|
if disasmEntry is None:
|
|
print("Error: When dissemble line {}".format(lineOffset))
|
|
else:
|
|
it.add(Interval(disasmEntry.begin, disasmEntry.end, disasmEntry))
|
|
|
|
except Exception as e:
|
|
print('Error: {}.'.format(e), file=sys.stderr)
|
|
|
|
return it
|