Skip to content

Commit

Permalink
Added more JNI calls
Browse files Browse the repository at this point in the history
  • Loading branch information
AeonLucid committed Jun 2, 2019
1 parent c0f7672 commit a8c9eaf
Show file tree
Hide file tree
Showing 11 changed files with 198 additions and 20 deletions.
Empty file.
17 changes: 17 additions & 0 deletions androidemu/java/classes/constructor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from androidemu.java.java_class_def import JavaClassDef
from androidemu.java.java_field_def import JavaFieldDef
from androidemu.java.java_method_def import JavaMethodDef


class Constructor(metaclass=JavaClassDef,
jvm_name='java/lang/reflect/Constructor',
jvm_fields=[
JavaFieldDef('slot', 'I', False),
JavaFieldDef('declaringClass', 'Ljava/lang/Class;', False)
]):

def __init__(self, clazz: JavaClassDef, method: JavaMethodDef):
self._clazz = clazz
self._method = method
self.slot = method.jvm_id
self.declaringClass = self._clazz
32 changes: 32 additions & 0 deletions androidemu/java/classes/method.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from androidemu.java.classes.constructor import Constructor
from androidemu.java.java_class_def import JavaClassDef
from androidemu.java.java_field_def import JavaFieldDef
from androidemu.java.java_method_def import java_method_def, JavaMethodDef


class Method(metaclass=JavaClassDef,
jvm_name='java/lang/reflect/Method',
jvm_fields=[
JavaFieldDef('slot', 'I', False),
JavaFieldDef('declaringClass', 'Ljava/lang/Class;', False),
]):

def __init__(self, clazz: JavaClassDef, method: JavaMethodDef):
self._clazz = clazz
self._method = method
self.slot = method.jvm_id
self.declaringClass = self._clazz

@staticmethod
@java_method_def(
name="getMethodModifiers",
signature="(Ljava/lang/Class;I)I",
args_list=['jobject', 'jint']
)
def get_method_modifiers(emu, clazz_obj, jvm_method_id):
clazz = clazz_obj.value
method = clazz.find_method_by_id(jvm_method_id)

# TODO: Implement

return 0
3 changes: 3 additions & 0 deletions androidemu/java/helpers/native_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ def native_translate_arg(emu, val):
elif isinstance(val, bytearray):
return emu.java_vm.jni_env.add_local_reference(jbyteArray(val))
elif isinstance(type(val), JavaClassDef):
# TODO: Look into this, seems wrong..
return emu.java_vm.jni_env.add_local_reference(jobject(val))
elif isinstance(val, JavaClassDef):
return emu.java_vm.jni_env.add_local_reference(jobject(val))
else:
raise NotImplementedError("Unable to write response '%s' type '%s' to emulator." % (str(val), type(val)))
Expand Down
3 changes: 2 additions & 1 deletion androidemu/java/java_class_def.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ class JavaClassDef(type):
next_jvm_method_id = itertools.count(start=0xd2000000, step=4)
next_jvm_field_id = itertools.count(start=0xe2000000, step=4)

def __init__(cls, name, base, ns, jvm_name=None, jvm_fields=None):
def __init__(cls, name, base, ns, jvm_name=None, jvm_fields=None, jvm_ignore=False):
cls.jvm_id = next(JavaClassDef.next_jvm_id)
cls.jvm_name = jvm_name
cls.jvm_methods = dict()
cls.jvm_fields = dict()
cls.jvm_ignore = jvm_ignore

# Register all defined Java methods.
for func in inspect.getmembers(cls, predicate=inspect.isfunction):
Expand Down
108 changes: 97 additions & 11 deletions androidemu/java/jni_env.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import logging

from androidemu.hooker import Hooker
from androidemu.java.classes.constructor import Constructor
from androidemu.java.classes.method import Method
from androidemu.java.helpers.native_method import native_method
from androidemu.java.java_classloader import JavaClassLoader
from androidemu.java.jni_const import *
Expand Down Expand Up @@ -305,7 +307,11 @@ def read_args_v(self, mu, args_ptr, args_list):
result = []

for arg_name in args_list:
if arg_name == 'jstring':
if arg_name == 'jint':
ref = int.from_bytes(mu.mem_read(args_ptr, 4), byteorder='little')
result.append(ref)
args_ptr = args_ptr + 4
elif arg_name == 'jstring':
ref = int.from_bytes(mu.mem_read(args_ptr, 4), byteorder='little')
result.append(self.get_reference(ref))
args_ptr = args_ptr + 4
Expand Down Expand Up @@ -343,6 +349,9 @@ def find_class(self, mu, env, name_ptr):
# TODO: Proper Java error?
raise RuntimeError('Could not find class \'%s\' for JNIEnv.' % name)

if clazz.jvm_ignore:
return 0

return self.add_local_reference(jclass(clazz))

@native_method
Expand All @@ -354,8 +363,32 @@ def from_reflected_field(self, mu, env):
raise NotImplementedError()

@native_method
def to_reflected_method(self, mu, env):
raise NotImplementedError()
def to_reflected_method(self, mu, env, class_idx, method_id, is_static):
"""
Converts a method ID derived from cls to a java.lang.reflect.Method or java.lang.reflect.Constructor object.
isStatic must be set to JNI_TRUE if the method ID refers to a static field, and JNI_FALSE otherwise.
Throws OutOfMemoryError and returns 0 if fails.
"""
clazz = self.get_reference(class_idx)

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

method = clazz.value.find_method_by_id(method_id)
if method is None:
raise RuntimeError("Could not find method ('%u') in class %s." % (method_id, clazz.value.jvm_name))

# TODO: Static check.

logger.debug("JNIEnv->ToReflectedMethod(%s, %s, %u) was called" % (clazz.value.jvm_name,
method.name,
is_static))

if method.name == '<init>' and method.signature.endswith('V'):
return Constructor(clazz.value, method)
else:
return Method(clazz.value, method)

@native_method
def get_superclass(self, mu, env):
Expand Down Expand Up @@ -501,8 +534,22 @@ def get_object_class(self, mu, env):
raise NotImplementedError()

@native_method
def is_instance_of(self, mu, env):
raise NotImplementedError()
def is_instance_of(self, mu, env, obj_idx, class_idx):
"""
Tests whether an object is an instance of a class.
Returns JNI_TRUE if obj can be cast to clazz; otherwise, returns JNI_FALSE. A NULL object can be cast to any class.
"""
obj = self.get_reference(obj_idx)
if not isinstance(obj, jobject):
raise ValueError('Expected a jobject.')

clazz = self.get_reference(class_idx)
if not isinstance(clazz, jclass):
raise ValueError('Expected a jclass.')

# TODO: Casting check (?)

return JNI_TRUE if obj.value.jvm_id == clazz.value.jvm_id else JNI_FALSE

@native_method
def get_method_id(self, mu, env, clazz_idx, name_ptr, sig_ptr):
Expand Down Expand Up @@ -548,9 +595,10 @@ def call_object_method_v(self, mu, env, obj_idx, method_id, args):
method.name,
method.signature, args))

# TODO: Args.
# Parse arguments.
constructor_args = self.read_args_v(mu, args, method.args_list)

return method.func(obj.value, self._emu)
return method.func(obj.value, self._emu, *constructor_args)

@native_method
def call_object_method_a(self, mu, env):
Expand Down Expand Up @@ -621,8 +669,27 @@ def call_long_method(self, mu, env):
raise NotImplementedError()

@native_method
def call_long_method_v(self, mu, env):
raise NotImplementedError()
def call_long_method_v(self, mu, env, obj_idx, method_id, args):
obj = self.get_reference(obj_idx)

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

method = obj.value.__class__.find_method_by_id(method_id)

if method is None:
# TODO: Proper Java error?
raise RuntimeError("Could not find method %d in object %s by id." % (method_id, obj.value.jvm_name))

logger.debug("JNIEnv->CallLongMethodV(%s, %s <%s>, 0x%x) was called" % (
obj.value.jvm_name,
method.name,
method.signature, args))

# Parse arguments.
constructor_args = self.read_args_v(mu, args, method.args_list)

return method.func(obj.value, self._emu, *constructor_args)

@native_method
def call_long_method_a(self, mu, env):
Expand Down Expand Up @@ -1032,8 +1099,27 @@ def call_static_int_method(self, mu, env):
raise NotImplementedError()

@native_method
def call_static_int_method_v(self, mu, env):
raise NotImplementedError()
def call_static_int_method_v(self, mu, env, clazz_idx, method_id, args):
clazz = self.get_reference(clazz_idx)

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

method = clazz.value.find_method_by_id(method_id)

if method is None:
# TODO: Proper Java error?
raise RuntimeError("Could not find method %d in class %s by id." % (method_id, clazz.value.jvm_name))

logger.debug("JNIEnv->CallStaticIntMethodV(%s, %s <%s>, 0x%x) was called" % (
clazz.value.jvm_name,
method.name,
method.signature, args))

# Parse arguments.
constructor_args = self.read_args_v(mu, args, method.args_list)

return method.func(self._emu, *constructor_args)

@native_method
def call_static_int_method_a(self, mu, env):
Expand Down
6 changes: 6 additions & 0 deletions androidemu/java/jni_ref.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
class jvalue:

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


class jobject:

def __init__(self, value=None):
Expand Down
27 changes: 23 additions & 4 deletions androidemu/native/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,35 @@


class NativeHooks:

"""
:type memory NativeMemory
:type modules Modules
:type hooker Hooker
"""

def __init__(self, emu, memory, modules, hooker):
self._emu = emu
self._memory = memory
self.atexit = []

modules.add_symbol_hook('__system_property_get', hooker.write_function(self.system_property_get) + 1)
modules.add_symbol_hook('pthread_create', hooker.write_function(self.pthread_create) + 1)
modules.add_symbol_hook('fork', hooker.write_function(self.nop('fork')) + 1)
modules.add_symbol_hook('vfork', hooker.write_function(self.nop('vfork')) + 1)
modules.add_symbol_hook('dladdr', hooker.write_function(self.nop('dladdr')) + 1)
modules.add_symbol_hook('dlsym', hooker.write_function(self.nop('dlsym')) + 1)
modules.add_symbol_hook('tolower', hooker.write_function(self.tolower) + 1)
modules.add_symbol_hook('strcmpi', hooker.write_function(self.nop('strcmpi')) + 1)

@native_method
def tolower(self, uc, charr):
logger.debug("Called tolower(%s)" % chr(charr))
return ord(chr(charr).lower())

@native_method
def system_property_get(self, uc, name_ptr, buf_ptr):
name = memory_helpers.read_utf8(uc, name_ptr)
logger.debug("Called __system_property_get(%s, 0x%x) was called" % (name, buf_ptr))
logger.debug("Called __system_property_get(%s, 0x%x)" % (name, buf_ptr))

if name in self._emu.system_properties:
memory_helpers.write_utf8(uc, buf_ptr, self._emu.system_properties[name])
Expand All @@ -35,5 +48,11 @@ def system_property_get(self, uc, name_ptr, buf_ptr):
return None

@native_method
def nop(self, emu):
raise NotImplementedError()
def pthread_create(self, uc, thread_ptr, attr, start_ptr, arg_ptr):
logger.debug("Called pthread_create(0x%x, 0x%x, 0x%x, 0x%x)" % (thread_ptr, attr, start_ptr, arg_ptr))

def nop(self, name):
@native_method
def nop_inside(emu):
raise NotImplementedError('Symbol hook not implemented %s' % name)
return nop_inside
5 changes: 5 additions & 0 deletions androidemu/vfs/file_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ def stat64(path):
meta_path = path + '.meta_emu'

if not os.path.exists(meta_path):
meta_path_dir = os.path.dirname(meta_path)

if not os.path.isdir(meta_path_dir):
os.makedirs(meta_path_dir)

with open(meta_path, 'w') as f:
json.dump({
'st_dev': 0,
Expand Down
12 changes: 10 additions & 2 deletions androidemu/vfs/file_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ def translate_path(self, filename):
if filename.startswith("/"):
filename = filename[1:]

if os.name == 'nt':
filename = filename.replace(':', '_')

file_path = posixpath.join(self._root_path, filename)
file_path = posixpath.normpath(file_path)

Expand Down Expand Up @@ -77,7 +80,7 @@ def _open_file(self, filename):
flags |= os.O_BINARY
return self._store_fd(orig_filename, file_path, os.open(file_path, flags=flags))
else:
logger.info("File does not exist %s" % file_path)
logger.warning("File does not exist '%s'" % orig_filename)
return -1

def _handle_read(self, mu, fd, buf_addr, count):
Expand Down Expand Up @@ -219,8 +222,13 @@ def _handle_fstatat64(self, mu, dirfd, pathname_ptr, buf, flags):
raise NotImplementedError("Directory file descriptor has not been implemented yet.")

if not flags == 0:
raise NotImplementedError("Flags has not been implemented yet.")
if flags & 0x100: # AT_SYMLINK_NOFOLLOW
pass
if flags & 0x800: # AT_NO_AUTOMOUNT
pass
# raise NotImplementedError("Flags has not been implemented yet.")

logger.info("File fstatat64 '%s'" % pathname)
pathname = self.translate_path(pathname)

stat = file_helpers.stat64(path=pathname)
Expand Down
5 changes: 3 additions & 2 deletions samples/debug_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ def hook_mem_write(uc, access, address, size, value, user_data):

def hook_mem_read(uc, access, address, size, value, user_data):
pc = uc.reg_read(UC_ARM_REG_PC)
data = uc.mem_read(address, size)
logger.debug(">>> Memory READ at 0x%x, data size = %u, pc: %x, data value = 0x%s" % (address, size, pc, data.hex()))
if pc > 0xcbc24cf0 - 8 and pc < 0xcbc24cf0:
data = uc.mem_read(address, size)
logger.debug(">>> Memory READ at 0x%x, data size = %u, pc: %x, data value = 0x%s" % (address, size, pc, data.hex()))


def hook_interrupt(uc, intno, data):
Expand Down

0 comments on commit a8c9eaf

Please sign in to comment.