Skip to content

Commit

Permalink
Added reference tables.
Browse files Browse the repository at this point in the history
  • Loading branch information
AeonLucid committed Feb 3, 2019
1 parent 397ac1a commit 294f89f
Show file tree
Hide file tree
Showing 11 changed files with 314 additions and 35 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ Make sure you are using python 3.7.
- Improve file descriptors in `vfs/file_system.py` so they are re-useable.
- Add a way for the VirtualFileSystem to give back dynamic files, such as `/proc/self/status`, `/proc/self/status` but also `/dev/urandom`.
- Library consumers must be able to easily rebuild the needed Java classes for a native library, which are used by the native library through the JNIEnv.
- Classes
- Objects
- ~~Classes~~
- ~~Objects~~
- ~~Methods~~
- ~~Native methods~~
- Fields
- Types
- Reflection
Expand All @@ -56,6 +58,8 @@ All resources used while developing AndroidNativeEmu.
- https://programtalk.com/python-examples/pyelftools.elftools.elf.relocation.Relocation/
- http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044f/IHI0044F_aaelf.pdf
- https://wiki.osdev.org/ELF_Tutorial
- https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html
- https://android.googlesource.com/platform/dalvik/+/donut-release/vm/Jni.c

### Code sources
- https://github.com/lunixbochs/usercorn
Expand Down
37 changes: 32 additions & 5 deletions androidemu/emulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from random import randint

from unicorn import Uc, UC_ARCH_ARM, UC_MODE_ARM
from unicorn.arm_const import UC_ARM_REG_SP, UC_ARM_REG_LR
from unicorn.arm_const import UC_ARM_REG_SP, UC_ARM_REG_LR, UC_ARM_REG_R0

from androidemu import config
from androidemu.config import MEMORY_BASE, MEMORY_SIZE
Expand Down Expand Up @@ -109,7 +109,34 @@ def call_symbol(self, module, symbol_name, *argv):
logger.error('Unable to find symbol \'%s\' in module \'%s\'.' % (symbol_name, module.filename))
return

native_write_args(self.mu, *argv)
stop_pos = randint(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE) | 1
self.mu.reg_write(UC_ARM_REG_LR, stop_pos)
self.mu.emu_start(symbol.address, stop_pos - 1)
self.call_native(symbol.address, *argv)

def call_native(self, addr, *argv):
# Detect JNI call
is_jni = False

if len(argv) >= 1:
is_jni = argv[0] == self.java_vm.address_ptr or argv[0] == self.java_vm.jni_env.address_ptr

# TODO: Write JNI args to local ref table if jni.

try:
# Execute native call.
native_write_args(self.mu, *argv)
stop_pos = randint(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE) | 1
self.mu.reg_write(UC_ARM_REG_LR, stop_pos)
self.mu.emu_start(addr, stop_pos - 1)

# Read result from locals if jni.
if is_jni:
result_idx = self.mu.reg_read(UC_ARM_REG_R0)
result = self.java_vm.jni_env.get_local_reference(result_idx)

if result is None:
return result

return result.value
finally:
# Clear locals if jni.
if is_jni:
self.java_vm.jni_env.clear_locals()
1 change: 0 additions & 1 deletion androidemu/hooker.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ def _hook(self, mu, address, size, user_data):
hook_func = self._hooks[hook_id]

# Call hook.

try:
hook_func(mu)
except:
Expand Down
17 changes: 0 additions & 17 deletions androidemu/java/helpers/java_class_def.py

This file was deleted.

42 changes: 42 additions & 0 deletions androidemu/java/java_class_def.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import inspect
import itertools
import logging

logger = logging.getLogger(__name__)


class JavaClassDef(type):
next_jvm_id = itertools.count(start=1)

def __init__(cls, name, base, ns, jvm_name=None):
cls.jvm_id = next(JavaClassDef.next_jvm_id)
cls.jvm_name = jvm_name
cls.jvm_methods = list()

# Register all defined Java methods.
for func in inspect.getmembers(cls, predicate=inspect.isfunction):
if hasattr(func[1], 'jvm_method'):
cls.jvm_methods.append(func[1].jvm_method)

type.__init__(cls, name, base, ns)

def __new__(mcs, name, base, ns, **kargs):
return type.__new__(mcs, name, base, ns)

def register_native(self, name, signature, ptr_func):
found = False
found_method = None

# Search for a defined jvm method.
for method in self.jvm_methods:
if method.name == name and method.signature == signature:
method.native_addr = ptr_func
found = True
found_method = method
break

if not found:
raise RuntimeError("Register native ('%s', '%s') failed on class %s." % (name, signature, self.__name__))

logger.debug("Registered native function ('%s', '%s') to %s.%s" % (name, signature,
self.__name__, found_method.func_name))
2 changes: 1 addition & 1 deletion androidemu/java/java_classloader.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from androidemu.java.helpers.java_class_def import JavaClassDef
from androidemu.java.java_class_def import JavaClassDef


class JavaClassLoader:
Expand Down
31 changes: 31 additions & 0 deletions androidemu/java/java_method_def.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class JavaMethodDef:

def __init__(self, func_name, name, signature, native):
self.func_name = func_name
self.name = name
self.signature = signature
self.native = native
self.native_addr = None


def java_method_def(name, signature, native=False):
def java_method_def_real(func):
def native_wrapper(self, emulator, *argv):
return emulator.call_native(
native_wrapper.jvm_method.native_addr,
emulator.java_vm.jni_env.address_ptr, # JNIEnv*
0x0, # this, TODO: Implement proper "this", a reference to the Java object inside which this native
# method has been declared in
*argv # Extra args.
)

def normal_wrapper(*args, **kwargs):
print('Got called wow!')
result = func(*args, **kwargs)
return result

wrapper = native_wrapper if native else normal_wrapper
wrapper.jvm_method = JavaMethodDef(func.__name__, name, signature, native)
return wrapper

return java_method_def_real
50 changes: 46 additions & 4 deletions androidemu/java/jni_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from androidemu.java.helpers.native_method import native_method
from androidemu.java.java_classloader import JavaClassLoader
from androidemu.java.jni_const import *
from androidemu.java.jni_ref import *
from androidemu.java.reference_table import ReferenceTable
from androidemu.utils import memory_helpers

logger = logging.getLogger(__name__)
Expand All @@ -18,6 +20,8 @@ class JNIEnv:
"""
def __init__(self, class_loader, hooker):
self._class_loader = class_loader
self._locals = ReferenceTable()
self._globals = ReferenceTable()

(self.address_ptr, self.address) = hooker.write_function_table({
4: self.get_version,
Expand Down Expand Up @@ -251,6 +255,36 @@ def __init__(self, class_loader, hooker):
232: self.get_object_ref_type
})

def add_local_reference(self, obj):
if not isinstance(obj, jobject):
raise ValueError('Expected a jobject.')

return self._locals.add(obj)

def get_local_reference(self, idx):
return self._locals.get(idx)

def delete_local_reference(self, obj):
if not isinstance(obj, jobject):
raise ValueError('Expected a jobject.')

self._locals.remove(obj)

def clear_locals(self):
self._locals.clear()

def add_global_reference(self, obj):
if not isinstance(obj, jobject):
raise ValueError('Expected a jobject.')

return self._globals.add(obj)

def delete_global_reference(self, obj):
if not isinstance(obj, jobject):
raise ValueError('Expected a jobject.')

return self._globals.remove(obj)

@native_method
def get_version(self, mu, env):
raise NotImplementedError()
Expand All @@ -276,7 +310,7 @@ def find_class(self, mu, env, name_ptr):
# TODO: Proper Java error?
raise RuntimeError('Could not find class \'%s\' for JNIEnv.' % name)

return clazz.jvm_id
return self.add_local_reference(jclass(clazz))

@native_method
def from_reflected_method(self, mu, env):
Expand Down Expand Up @@ -972,8 +1006,11 @@ def release_string_chars(self, mu, env):
raise NotImplementedError()

@native_method
def new_string_utf(self, mu, env):
raise NotImplementedError()
def new_string_utf(self, mu, env, bytes_ptr):
string = memory_helpers.read_utf8(mu, bytes_ptr)
logger.debug("JNIEnv->NewStringUtf(%s) was called" % string)

return self.add_local_reference(jstring(string))

@native_method
def get_string_utf_length(self, mu, env):
Expand Down Expand Up @@ -1167,7 +1204,12 @@ def set_double_array_region(self, mu, env):
def register_natives(self, mu, env, clazz_id, methods, methods_count):
logger.debug("JNIEnv->RegisterNatives(%d, 0x%08x, %d) was called" % (clazz_id, methods, methods_count))

clazz = self._class_loader.find_class_by_id(clazz_id)
clazz = self.get_local_reference(clazz_id)

if not isinstance(clazz, jclass):
raise ValueError('Expected a jclass.')

clazz = clazz.value

for i in range(0, methods_count):
ptr_name = memory_helpers.read_ptr(mu, (i * 12) + methods)
Expand Down
84 changes: 84 additions & 0 deletions androidemu/java/jni_ref.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
class jobject:

def __init__(self, value=None):
self.value = value


class jclass(jobject):

def __init__(self, value=None):
super().__init__(value)


class jstring(jobject):

def __init__(self, value=None):
super().__init__(value)


class jarray(jobject):

def __init__(self, value=None):
super().__init__(value)


class jobjectArray(jarray):

def __init__(self, value=None):
super().__init__(value)


class jbooleanArray(jarray):

def __init__(self, value=None):
super().__init__(value)


class jbyteArray(jarray):

def __init__(self, value=None):
super().__init__(value)


class jcharArray(jarray):

def __init__(self, value=None):
super().__init__(value)


class jshortArray(jarray):

def __init__(self, value=None):
super().__init__(value)


class jintArray(jarray):

def __init__(self, value=None):
super().__init__(value)


class jlongArray(jarray):

def __init__(self, value=None):
super().__init__(value)


class jfloatArray(jarray):

def __init__(self, value=None):
super().__init__(value)


class jdoubleArray(jarray):

def __init__(self, value=None):
super().__init__(value)


class jthrowable(jobject):

def __init__(self, value=None):
super().__init__(value)


Loading

0 comments on commit 294f89f

Please sign in to comment.