Skip to content

Commit

Permalink
Added basic java classloader using a metaclass.
Browse files Browse the repository at this point in the history
  • Loading branch information
AeonLucid committed Feb 1, 2019
1 parent 09a8733 commit 735e2b8
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 14 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ This is an educational project to learn more about the ELF file format and [Unic

> In the future this will be possible through pypi.
Make sure you are using python 3.
Make sure you are using python 3.7.

1. Clone the repository
2. Run `pip install -r requirements.txt`
Expand All @@ -26,7 +26,7 @@ Make sure you are using python 3.
> If you have trouble getting the `keystone-engine` dependency on Windows (as I did):
> 1. Clone their [repository](https://github.com/keystone-engine/keystone)
> 2. Open a terminal in `bindings/python`
> 3. Run `python setup.py install` (Make sure you are using python 3)
> 3. Run `python setup.py install` (Make sure you are using python 3.7)
> 4. Download their `Windows - Core engine` package [here](http://www.keystone-engine.org/download/) for your python arch.
> 5. Put the `keystone.dll` in `C:\location_to_python\Lib\site-packages\keystone\`.
Expand Down
4 changes: 3 additions & 1 deletion androidemu/emulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from androidemu.hooker import Hooker
from androidemu.internal.memory import Memory
from androidemu.internal.modules import Modules
from androidemu.java.java_classloader import JavaClassLoader
from androidemu.java.java_vm import JavaVM
from androidemu.native.hooks import NativeHooks
from androidemu.native.memory import NativeMemory
Expand Down Expand Up @@ -52,7 +53,8 @@ def __init__(self, vfs_root=None, vfp_inst_set=False):
self.hooker = Hooker(self.mu, config.MEMORY_BASE, config.MEMORY_SIZE)

# JavaVM
self.java_vm = JavaVM(self.hooker)
self.java_classloader = JavaClassLoader()
self.java_vm = JavaVM(self.java_classloader, self.hooker)

# Native
self.native_memory = NativeMemory(self.mu, config.MEMORY_DYN_BASE, config.MEMORY_DYN_SIZE, self.syscall_handler)
Expand Down
17 changes: 17 additions & 0 deletions androidemu/java/helpers/java_class_def.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import itertools


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_natives = list()
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):
self.jvm_natives.append((name, signature, ptr_func))
34 changes: 34 additions & 0 deletions androidemu/java/java_classloader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from androidemu.java.helpers.java_class_def import JavaClassDef


class JavaClassLoader:

"""
:type class_by_id dict[int, JavaClassDef]
:type class_by_name dict[string, JavaClassDef]
"""
def __init__(self):
self.class_by_id = dict()
self.class_by_name = dict()

def add_class(self, clazz):
if not isinstance(clazz, JavaClassDef):
raise ValueError('Expected a JavaClassDef.')

if clazz.jvm_name in self.class_by_name:
raise KeyError('The class \'%s\' is already registered.' % clazz.jvm_name)

self.class_by_id[clazz.jvm_id] = clazz
self.class_by_name[clazz.jvm_name] = clazz

def find_class_by_id(self, jvm_id):
if jvm_id not in self.class_by_id:
return None

return self.class_by_id[jvm_id]

def find_class_by_name(self, name):
if name not in self.class_by_name:
return None

return self.class_by_name[name]
6 changes: 4 additions & 2 deletions androidemu/java/java_vm.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from androidemu.hooker import Hooker
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_env import JNIEnv

Expand All @@ -13,9 +14,10 @@
class JavaVM:

"""
:type class_loader JavaClassLoader
:type hooker Hooker
"""
def __init__(self, hooker):
def __init__(self, class_loader, hooker):
(self.address_ptr, self.address) = hooker.write_function_table({
3: self.destroy_java_vm,
4: self.attach_current_thread,
Expand All @@ -24,7 +26,7 @@ def __init__(self, hooker):
7: self.attach_current_thread
})

self.jni_env = JNIEnv(hooker)
self.jni_env = JNIEnv(class_loader, hooker)

@native_method
def destroy_java_vm(self, mu):
Expand Down
29 changes: 22 additions & 7 deletions androidemu/java/jni_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from androidemu.hooker import Hooker
from androidemu.java.helpers.native_method import native_method
from androidemu.java.java_classloader import JavaClassLoader
from androidemu.java.jni_const import *
from androidemu.utils import memory_helpers

Expand All @@ -12,9 +13,12 @@
class JNIEnv:

"""
:type class_loader JavaClassLoader
:type hooker Hooker
"""
def __init__(self, hooker):
def __init__(self, class_loader, hooker):
self._class_loader = class_loader

(self.address_ptr, self.address) = hooker.write_function_table({
4: self.get_version,
5: self.define_class,
Expand Down Expand Up @@ -262,8 +266,17 @@ def find_class(self, mu, env, name_ptr):
"""
name = memory_helpers.read_utf8(mu, name_ptr)
logger.debug("JNIEnv->FindClass(%s) was called" % name)
# TODO: Actually retrieve a class id from a class map.
return 0xFA

if name.startswith('['):
raise NotImplementedError('Array type not implemented.')

clazz = self._class_loader.find_class_by_name(name)

if clazz is None:
# TODO: Proper Java error?
raise RuntimeError('Could not find class \'%s\' for JNIEnv.' % name)

return clazz.jvm_id

@native_method
def from_reflected_method(self, mu, env):
Expand Down Expand Up @@ -1151,7 +1164,11 @@ def set_double_array_region(self, mu, env):
raise NotImplementedError()

@native_method
def register_natives(self, mu, env, clazz, methods, methods_count):
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)

for i in range(0, methods_count):
ptr_name = memory_helpers.read_ptr(mu, (i * 12) + methods)
ptr_sign = memory_helpers.read_ptr(mu, (i * 12) + methods + 4)
Expand All @@ -1160,9 +1177,7 @@ def register_natives(self, mu, env, clazz, methods, methods_count):
name = memory_helpers.read_utf8(mu, ptr_name)
signature = memory_helpers.read_utf8(mu, ptr_sign)

print(name, signature, "%x" % ptr_func)

# TODO: Store these so we can call them.
clazz.register_native(name, signature, ptr_func)

return JNI_OK

Expand Down
21 changes: 19 additions & 2 deletions example_jni.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import logging
import os
import sys
import posixpath

Expand All @@ -8,6 +7,15 @@

import debug_utils
from androidemu.emulator import Emulator
from androidemu.java.helpers.java_class_def import JavaClassDef


# Create java class.
class MainActivity(metaclass=JavaClassDef, jvm_name='local/myapp/testnativeapp/MainActivity'):

def __init__(self):
pass


# Configure logging
logging.basicConfig(
Expand All @@ -24,6 +32,9 @@
vfs_root=posixpath.join(posixpath.dirname(__file__), "vfs")
)

# Register Java class.
emulator.java_classloader.add_class(MainActivity)

# Load all libraries.
emulator.load_library("example_binaries/libdl.so")
emulator.load_library("example_binaries/libc.so")
Expand All @@ -50,8 +61,14 @@
# Run JNI_OnLoad.
try:
emulator.mu.emu_start(base_address + 0x7DEC + 1, base_address + 0x7EEA)

# Dump natives found.
logger.info("Exited EMU.")
logger.info("Native methods registered to MainActivity:")

for (name, sig, ptr) in MainActivity.jvm_natives:
logger.info("- [0x%08x] %s - %s" % (ptr, name, sig))
except UcError as e:
print("Exit at %x" % emulator.mu.reg_read(UC_ARM_REG_PC))
raise

logger.info("Exited EMU.")

0 comments on commit 735e2b8

Please sign in to comment.