Skip to content

Commit

Permalink
Now uses symbols from previously loaded libraries.
Browse files Browse the repository at this point in the history
  • Loading branch information
AeonLucid committed Jul 10, 2018
1 parent 992484d commit a5417c2
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 39 deletions.
9 changes: 4 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
### Custom ###
files/*
main_test.py

# Created by https://www.gitignore.io/api/venv,python,pycharm+all

### PyCharm+all ###
Expand Down Expand Up @@ -193,4 +189,7 @@ pyvenv.cfg
pip-selfcheck.json


# End of https://www.gitignore.io/api/venv,python,pycharm+all
# End of https://www.gitignore.io/api/venv,python,pycharm+all

# Custom
!example_binaries/*.so
8 changes: 3 additions & 5 deletions androidemu/emulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ class Emulator:
:type mu Uc
:type memory Memory
"""
def __init__(self, filename):
self.filename = filename

def __init__(self):
# Initialize unicorn.
self.mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)

Expand All @@ -26,5 +24,5 @@ def __init__(self, filename):
self.modules = Modules(self)
self.memory = Memory(self)

def load(self):
self.modules.load_module(self.filename)
def load_library(self, filename):
return self.modules.load_module(filename)
6 changes: 5 additions & 1 deletion androidemu/internal/memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ def __init__(self, emu):
self.counter_stack = config.STACK_ADDR + config.STACK_SIZE

def mem_reserve(self, size):
(_, size_aligned) = align(0, size, True)

ret = self.counter_memory
self.counter_memory += size

self.counter_memory += size_aligned

return ret

def mem_map(self, address, size, prot):
Expand Down
47 changes: 47 additions & 0 deletions androidemu/internal/module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from collections import defaultdict

from elftools.elf.elffile import SymbolTableSection


class Module:

"""
:type filename str
:type base_addr int
:type size int
:type dynsym SymbolTableSection
"""
def __init__(self, filename, address, size, dynsym):
self.filename = filename
self.base_addr = address
self.size = size
self.symbols = ModuleSymbols(dynsym)


# Thanks to
# https://github.com/eliben/pyelftools/blob/82299758cc0c0ca788de094ee2d83f6f490a8ef4/elftools/elf/sections.py#L143
class ModuleSymbols:

def __init__(self, dynsym):
self._symbols = list(dynsym.iter_symbols())
self._symbol_name_map = None

def get_symbol_by_name(self, name):
if self._symbol_name_map is None:
self._symbol_name_map = defaultdict(list)
for i, sym in enumerate(self.iter_symbols()):
self._symbol_name_map[sym.name].append(i)

symnums = self._symbol_name_map.get(name)

return [self.get_symbol(i) for i in symnums] if symnums else None

def num_symbols(self):
return len(self._symbols)

def get_symbol(self, n):
return self._symbols[n]

def iter_symbols(self):
for i in range(self.num_symbols()):
yield self.get_symbol(i)
73 changes: 45 additions & 28 deletions androidemu/internal/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,20 @@
from unicorn import UC_PROT_ALL

from androidemu.internal import get_segment_protection, arm


class Module:

"""
:type filename str
:type address int
:type size int
"""
def __init__(self, filename, address, size):
self.filename = filename
self.address = address
self.size = size
from androidemu.internal.module import Module


class Modules:

"""
:type emu androidemu.emulator.Emulator
:type module_main Module
:type modules list[Module]
"""
def __init__(self, emu):
self.emu = emu
self.module_main = None
self.modules = list()

def load_module(self, filename, main=True):
def load_module(self, filename):
with open(filename, 'rb') as fstream:
elf = ELFFile(fstream)

Expand Down Expand Up @@ -70,14 +56,6 @@ def load_module(self, filename, main=True):
self.emu.memory.mem_map(load_base + segment.header.p_vaddr, segment.header.p_memsz, prot)
self.emu.memory.mem_write(load_base + segment.header.p_vaddr, segment.data())

# Store information about loaded module.
module = Module(filename, load_base, bound_high - bound_low)

if main:
self.module_main = module
else:
self.modules.append(module)

# Parse section header (Linking view).
dynsym = elf.get_section_by_name(".dynsym")
dynstr = elf.get_section_by_name(".dynstr")
Expand All @@ -101,10 +79,19 @@ def load_module(self, filename, main=True):

# Write the new value
self.emu.mu.mem_write(rel_addr, value.to_bytes(4, byteorder='little'))
elif rel_info_type == arm.R_ARM_GLOB_DAT: # Dyn | Data | Op: (S + A) | T
pass
elif rel_info_type == arm.R_ARM_JUMP_SLOT: # Dyn | Data | Op: (S + A) | T
pass
elif rel_info_type == arm.R_ARM_GLOB_DAT or rel_info_type == arm.R_ARM_JUMP_SLOT:
# Resolve the symbol.
(sym_base, resolved_symbol) = self._resolv_symbol(load_base, dynsym, sym)

if resolved_symbol is None:
# print("%s symbol was missing." % sym.name)
continue

# Create the new value.
value = sym_base + resolved_symbol['st_value']

# Write the new value
self.emu.mu.mem_write(rel_addr, value.to_bytes(4, byteorder='little'))
elif rel_info_type == arm.R_ARM_RELATIVE:
if sym_value == 0:
# Load address at which it was linked originally.
Expand All @@ -120,3 +107,33 @@ def load_module(self, filename, main=True):
raise NotImplementedError()
else:
print("Unhandled relocation type %i." % rel_info_type)

# Store information about loaded module.
self.modules.append(Module(filename, load_base, bound_high - bound_low, dynsym))

return load_base

def _resolv_symbol(self, load_base, symbol_table, symbol):
# First we check our own symbol table.
symbols = symbol_table.get_symbol_by_name(symbol.name)
symbol = symbols[0]

if symbol['st_shndx'] != 'SHN_UNDEF':
return load_base, symbol

# Next we check previously discovered symbol tables.
for module in self.modules:
symbols = module.symbols.get_symbol_by_name(symbol.name)

if symbols is None:
continue

for symbol in symbols:
if symbol['st_shndx'] != 'SHN_UNDEF':
return module.base_addr, symbol

return None, None

def __iter__(self):
for x in self.modules:
yield x
32 changes: 32 additions & 0 deletions example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from unicorn import UC_HOOK_CODE
from unicorn.arm_const import *

from androidemu.emulator import Emulator

# Initialize emulator
emulator = Emulator()
emulator.load_library("example_binaries/libc.so")
my_base = emulator.load_library("example_binaries/libnative-lib.so")

# Show loaded modules.
print("Loaded modules:")

for module in emulator.modules:
print("[0x%x] %s" % (module.base_addr, module.filename))


# Add debugging.
def hook_code(mu, address, size, user_data):
instruction = mu.mem_read(address, size)
instruction_str = ''.join('{:02x} '.format(x) for x in instruction)

print('# Tracing instruction at 0x%x, instruction size = 0x%x, instruction = %s' % (address, size, instruction_str))


emulator.mu.hook_add(UC_HOOK_CODE, hook_code)

# Runs a method of "libnative-lib.so" that calls an imported
# function "strlen" from "libc.so".
emulator.mu.emu_start(my_base + 0x7E6 + 1, my_base + 0x7EA)

print("String length is: %i" % emulator.mu.reg_read(UC_ARM_REG_R0))
Binary file added example_binaries/libc.so
Binary file not shown.
Binary file added example_binaries/libnative-lib.so
Binary file not shown.

0 comments on commit a5417c2

Please sign in to comment.