#!/usr/bin/env python
from os import popen
from sys import argv

dll = argv[1]
objdump = "~/xda/wince-cross/usr/local/wince/cross-tools/bin/arm-wince-pe-objdump"

section_name   = []
section_size   = []
section_vma    = []
section_offset = []

imagebase = 0

#
# is this an executable with headers?
#

ftype = popen("file %s" % dll).readlines()[0].split(' ')[1].replace("\n","")
if ftype != "data":

	#
	# parse section headers
	#

	for l in popen("%s -h %s | grep ' \.'" % (objdump, dll)).readlines():
		t = l.split()
		section_name   = section_name   + [t[1]]
		section_size   = section_size   + [int(t[2],16)]
		section_vma    = section_vma    + [int(t[3],16)]
		section_offset = section_offset + [int(t[5],16)]

else:

	section_name   = ["none"]
	section_size   = [256*1024]
	section_vma    = [0]
	section_offset = [0]

#
# virtual memory address <--> file offset translation
#

def vma_to_offset(vma):
	i=0
	while i < len(section_size):
		if (vma >= section_vma[i]) and (vma < section_vma[i]+section_size[i]):
			return vma-section_vma[i]+section_offset[i]
		i=i+1

def offset_to_vma(offset):
	i=0
	while i < len(section_size):
		if (offset >= section_offset[i]) and (offset < section_offset[i]+section_size[i]):
			return offset-section_offset[i]+section_vma[i]
		i=i+1

def vma_in_section(vma):
	i=0
	while i < len(section_size):
		if (vma >= section_vma[i]) and (vma < section_vma[i]+section_size[i]):
			return True
		i=i+1
	return False

#
# parse strings into a dictionary (0xADDR : "string")
#

strings = {}
strlen = {}

for l in popen("strings -tx %s" % dll).readlines():
	l = l.split(None,1)
	if len(l) != 2:
		continue
	addr = offset_to_vma(int(l[0],16))
	text = l[1].replace("\n","")
	strings[addr] = text
	strlen[addr] = (len(text)-1)/4+1

# unicode strings
for l in popen("strings -tx -el %s" % dll).readlines():
	l = l.split(None,1)
	if len(l) != 2:
		continue
	addr = offset_to_vma(int(l[0],16))
	text = l[1].replace("\n","")
	if not strings.has_key(addr):
		strings[addr] = text
		strlen[addr] = len(text)/2+1

#
# parse function table into a dictionary (0xADDR : "name")
#

pipe = popen("%s -p %s | grep '^ImageBase\|\s*\[[0-9 ]*\]'" % (objdump, dll))

ll=pipe.readlines()
pipe.close()

addr = []
ftable = {}

if len(ll) > 1:
	imagebase = int(ll[0].split()[1],16)
	print "%x" % imagebase
	i=1
	l=ll[i].split()
	addr={}
	while len(l) == 7:
		n=int(l[1][0:-1])
		addr[n] = int(l[4],16)
		i=i+1
		l=ll[i].split()

	while i+1 < len(ll):
		i=i+1
		l=ll[i].split()
		n=int(l[1][0:-1])
		ftable[imagebase+addr[n]] = l[2]

#
# disassemble
#

if ftype == "data":
	pipe = popen("%s -b binary -m arm -D %s" % (objdump, dll))
else:
	pipe = popen("%s -D %s" % (objdump, dll))
ll=pipe.readlines()
pipe.close()

datadict   = {}
referenced = {}
jumped_to  = {}

print "Finding references..."

i = 0
while i+1 < len(ll):
	i=i+1
	l=ll[i]
	if (l == '\t...') or (l == '\n'):
		continue
	t = l.split('\t')
	if (len(t[0]) == 0) or (t[0][-1] != ':') or (len(t) < 3):
		continue
	addr = int(t[0][0:-1].strip(),16)
	data = int(t[1],16)
	datadict[addr] = data

	# only evaluate references
	inst = t[2][0:3]

	if (inst == "ldr") or (inst == "str"):
		if t[len(t)-1][0:4] == '; 0x':
			ref = int(t[len(t)-1][4:],16)
			if vma_in_section(ref):
				referenced[ref] = 0

	if (inst[0:2] == 'bl'):
		if (len(inst) == 2) or (inst[2] != 's'):
			target = int(t[3][2:],16)
			if vma_in_section(target):
				if target in jumped_to.keys():
					jumped_to[target].append("0x%08x" % addr)
				else:
					jumped_to[target] = ["0x%08x" % addr]

print "Resolving references..."

for ref in referenced.keys():
	if datadict.has_key(ref):
		referenced[ref] = datadict[ref]
		if datadict.has_key(referenced[ref]):
			referenced[referenced[ref]] = datadict[referenced[ref]]
	else:
		referenced[ref] = 0

print "Generating disassembled output..."

i = 0
while i+1 < len(ll):
	i=i+1
	l=ll[i].strip('\n')
	if (l == '\t...') or (l == '\n'):
		print l
		continue
	t = l.split('\t')
	if (len(t[0]) == 0) or (t[0][-1] != ':') or (len(t) < 3):
		print l
		continue
	addr = int(t[0][0:-1].strip(),16)
	data = int(t[1],16)
	inst = t[2]

	if referenced.has_key(addr):
		if strings.has_key(addr):
			print "%8x:\t\"%s\"" % (addr, strings[addr])
			if(strlen[addr]>1):
				i=i+strlen[addr]-1
		elif strings.has_key(data-0x90030000):
			print "%8x:\t%08x \"%s\"" % (addr, data, strings[data-0x90030000])
		else:
			if referenced.has_key(data):
				if datadict.has_key(referenced[data]):
					if strings.has_key(referenced[data]):
						print "%8x:\t%08x --> \"%s\"" % (addr, data, strings[referenced[data]])
					elif strings.has_key(referenced[data]-0x90030000):
						print "%8x:\t%08x --> \"%s\"" % (addr, data, strings[referenced[data]])
					else:
						print "%8x:\t%08x --> %08x" % (addr, data, datadict[referenced[data]])
				else:
					print "%8x:\t%08x --> ? (at %08x)" % (addr, data, referenced[data])
			else:
				print "%s\t; (referenced somewhere)" % l
	elif strings.has_key(addr):
		print "%8x:\t\"%s\" (no direct reference)" % (addr, strings[addr])
		if(strlen[addr]>1):
			i=i+strlen[addr]-1
	elif t[len(t)-1][0:4] == '; 0x':
		ref = int(t[len(t)-1][4:],16)
		if referenced.has_key(ref):
			if datadict.has_key(referenced[ref]):
				if strings.has_key(referenced[ref]):
					print "%s = 0x%08x --> \"%s\"" % (l, referenced[ref], strings[referenced[ref]])
				else:
					print "%s = 0x%08x --> %08x" % (l, referenced[ref], datadict[referenced[ref]])
			else:
				if strings.has_key(referenced[ref]-0x90030000):
					print "%s --> 0x%08x\"%s\"" % (l, referenced[ref], strings[referenced[ref]-0x90030000])
				else:
					print "%s --> 0x%08x" % (l, referenced[ref])
		else:
			print l
	elif addr in ftable.keys():
		print "\n/*\n * %s\n */\n%s\n" % (ftable[addr], l),
		if addr in jumped_to.keys():
			print "\n/*\n *",
			print jumped_to[addr],
			print "\n */\n%s" % l
	elif addr in jumped_to.keys():
		print "\n/*\n *",
		print jumped_to[addr],
		print "\n */\n%s" % l
	else:
		print l
		continue
