DF Progress

So, I ended up ditching my initial code base that I wrote over christmas last week started up again.

I did the following things:

  • Wrote a java class file parser
  • Wrote a jvm bytecode disassembler
  • Wrote a basic bytecode injector

The class file parser took me 3 days working on it (not all day mind you), and the disassembler took me no more than a few hours. The bytecode injector wasn’t very tough either.

The reason I wrote these tools was so that I could hook methods. Basically what I have support now, is at the entry point of a method, I inject bytecode that will call a native function via JNI. To do this, I parse the signature of the java method and generate bytecode that will push each argument to the function onto the stack and then call the native function of my choice that has a matching signature.

For example, say I have the Java method:


public void setName(String name) {
this.name = name;
}

//bytecode
setWeight (I)V
0 aload_0 2a
1 iload_1 1b
2 putfield b5 00 02
5 return b1

It takes 1 argument, a string.

I will generate the following bytecode

setWeight (I)V
0 aload_0 2a
1 iload_1 1b
2 invokestatic b8 00 21
5 aload_0 2a
6 iload_1 1b
7 putfield b5 00 03
10 return b1

My native code looks something like this for my testing:


extern "C"
{
JNIEXPORT void JNICALL Java_Animal_hook_1dog_1set_1weight(JNIEnv *jni_env, jobject jo, jint ji)
{
pantheios::log_NOTICE("Java_test_Animal_hook_1dog_1set_1weight: ", pantheios::integer(ji));
return;
}
};

The parameters to the function are:
1. The jnienv pointer
2. The jobject representing the this pointer for the java class instance this method is being invoked with
3. The list of arguments to the method, in this case a single jint.

The next problem I wanted to solve was now that I have my code being called in a reasonably easy manner, how can I easily interact with all the objects and fields passed to my code? JNI is particularly verbose in regards to getting values of fields, or executing methods on an object and I didn’t really want to write 10k LOC of JNI to help use the java framework/runtime classes such as HashMap, String, Set, etc.

I searched the web and came across JunC++ion and Jace. JunC++ion is not free and not an easily trial download, Jace is free and open source which for me was really nice as I was able to fix a few bugs and fix some intricacies to my use cases.

What Jace does is generate wrappers around JNI allowing you to write code this:

#include "jace/proxy/java/util/Map.h"
using jace::proxy::java::util::Map;

#include "jace/proxy/java/util/HashMap.h"
using jace::proxy::java::util::HashMap;

#include "jace/proxy/java/util/Map.Entry.h"
using jace::proxy::java::util::Map_Entry;

#include "jace/proxy/java/util/Iterator.h"
using jace::proxy::java::util::Iterator;

....
....
....
Map map = HashMap();

map.put( Integer( "1" ), String( "Hello 1" ) );
map.put( Integer( "2" ), String( "Hello 2" ) );
map.put( Integer( "3" ), String( "Hello 3" ) );

Set entrySet( map.entrySet() );

for ( Iterator it( entrySet.iterator() ); it.hasNext(); ) {
Map_Entry entry = jace::java_cast( it.next() );
Integer key = jace::java_cast( entry.getKey() );
String value = jace::java_cast( entry.getValue() );
cout << "key: <" << key < value: <" << value <" << endl;
}

The code generated by Jace looks something like this for using an instance of java.util.Map:

#ifndef JACE_PROXY_JAVA_UTIL_MAP_H
#define JACE_PROXY_JAVA_UTIL_MAP_H

#ifndef JACE_OS_DEP_H
#include "jace/os_dep.h"
#endif

#ifndef JACE_NAMESPACE_H
#include "jace/namespace.h"
#endif

#ifndef JACE_JOBJECT_H
#include "jace/proxy/JObject.h"
#endif

#ifndef JACE_JENLISTER_H
#include "jace/JEnlister.h"
#endif

#ifndef JACE_JARRAY_H
#include "jace/JArray.h"
#endif

#ifndef JACE_JFIELD_PROXY_H
#include "jace/JFieldProxy.h"
#endif

/**
* The super class for this class.
*
*/
#ifndef JACE_PROXY_JAVA_LANG_OBJECT_H
#include "jace/proxy/java/lang/Object.h"
#endif

/**
* Forward declarations for the classes that this class uses.
*
*/
BEGIN_NAMESPACE_3( jace, proxy, types )
class JInt;
END_NAMESPACE_3( jace, proxy, types )

BEGIN_NAMESPACE_3( jace, proxy, types )
class JBoolean;
END_NAMESPACE_3( jace, proxy, types )

BEGIN_NAMESPACE_3( jace, proxy, types )
class JVoid;
END_NAMESPACE_3( jace, proxy, types )

BEGIN_NAMESPACE_4( jace, proxy, java, util )
class Set;
END_NAMESPACE_4( jace, proxy, java, util )

BEGIN_NAMESPACE_4( jace, proxy, java, util )
class Collection;
END_NAMESPACE_4( jace, proxy, java, util )

BEGIN_NAMESPACE_4( jace, proxy, java, util )

/**
* The Jace C++ proxy class for java/util/Map.
* Please do not edit this class, as any changes you make will be overwritten.
* For more information, please refer to the Jace Developer's Guide.
*
*/
class Map : public virtual ::jace::proxy::java::lang::Object {

public:

/**
* size
*
*/
::jace::proxy::types::JInt size();

/**
* isEmpty
*
*/
::jace::proxy::types::JBoolean isEmpty();

/**
* containsKey
*
*/
::jace::proxy::types::JBoolean containsKey( ::jace::proxy::java::lang::Object p0 );

/**
* containsValue
*
*/
::jace::proxy::types::JBoolean containsValue( ::jace::proxy::java::lang::Object p0 );

/**
* get
*
*/
::jace::proxy::java::lang::Object get( ::jace::proxy::java::lang::Object p0 );

/**
* put
*
*/
::jace::proxy::java::lang::Object put( ::jace::proxy::java::lang::Object p0, ::jace::proxy::java::lang::Object p1 );

/**
* remove
*
*/
::jace::proxy::java::lang::Object remove( ::jace::proxy::java::lang::Object p0 );

/**
* putAll
*
*/
void putAll( ::jace::proxy::java::util::Map p0 );

/**
* clear
*
*/
void clear();

/**
* keySet
*
*/
::jace::proxy::java::util::Set keySet();

/**
* values
*
*/
::jace::proxy::java::util::Collection values();

/**
* entrySet
*
*/
::jace::proxy::java::util::Set entrySet();

/**
* equals
*
*/
::jace::proxy::types::JBoolean equals( ::jace::proxy::java::lang::Object p0 );

/**
* hashCode
*
*/
::jace::proxy::types::JInt hashCode();

/**
* The following methods are required to integrate this class
* with the Jace framework.
*
*/
/**
* Special no arg constructor for interface virtual inheritance
*
*/
Map();
Map( jvalue value );
Map( jobject object );
Map( const Map& object );
Map( const NoOp& noOp );
virtual const JClass* getJavaJniClass() const throw ( JNIException );
static const JClass* staticGetJavaJniClass() throw ( JNIException );
static JEnlister enlister;
};

END_NAMESPACE_4( jace, proxy, java, util )

#ifndef PUT_TSDS_IN_HEADER
template ::jace::ElementProxy::ElementProxy( jarray array, jvalue element, int index );
template ::jace::ElementProxy::ElementProxy( const jace::ElementProxy& proxy );
#else
template jace::ElementProxy::ElementProxy( jarray array, jvalue element, int index ) :
jace::proxy::java::util::Map( element ), Object( NO_OP ), mIndex( index ) {
JNIEnv* env = ::jace::helper::attach();
parent = static_cast( ::jace::helper::newGlobalRef( env, array ) );
}
template jace::ElementProxy::ElementProxy( const jace::ElementProxy& proxy ) :
jace::proxy::java::util::Map( proxy.getJavaJniObject() ), Object( NO_OP ), mIndex( proxy.mIndex ) {
JNIEnv* env = ::jace::helper::attach();
parent = static_cast( ::jace::helper::newGlobalRef( env, proxy.parent ) );
}
#endif

#ifndef PUT_TSDS_IN_HEADER
template ::jace::JFieldProxy::JFieldProxy( jfieldID fieldID_, jvalue value, jobject parent_ );
template ::jace::JFieldProxy::JFieldProxy( jfieldID fieldID_, jvalue value, jclass parentClass_ );
template ::jace::JFieldProxy::JFieldProxy( const ::jace::JFieldProxy& object );
#else
template ::jace::JFieldProxy::JFieldProxy( jfieldID fieldID_, jvalue value, jobject parent_ ) :
::jace::proxy::java::util::Map( value ), Object( NO_OP ), fieldID( fieldID_ ) {
JNIEnv* env = ::jace::helper::attach();

if ( parent_ ) {
parent = ::jace::helper::newGlobalRef( env, parent_ );
}
else {
parent = parent_;
}

parentClass = 0;
}
template ::jace::JFieldProxy::JFieldProxy( jfieldID fieldID_, jvalue value, jclass parentClass_ ) :
::jace::proxy::java::util::Map( value ), Object( NO_OP ), fieldID( fieldID_ ) {
JNIEnv* env = ::jace::helper::attach();

parent = 0;
parentClass = static_cast( ::jace::helper::newGlobalRef( env, parentClass_ ) );
}
template ::jace::JFieldProxy::JFieldProxy( const ::jace::JFieldProxy& object ) :
::jace::proxy::java::util::Map( object.getJavaJniValue() ), Object( NO_OP ) {

fieldID = object.fieldID;

if ( object.parent ) {
JNIEnv* env = ::jace::helper::attach();
parent = ::jace::helper::newGlobalRef( env, object.parent );
}
else {
parent = 0;
}

if ( object.parentClass ) {
JNIEnv* env = ::jace::helper::attach();
parentClass = static_cast( ::jace::helper::newGlobalRef( env, object.parentClass ) );
}
else {
parentClass = 0;
}
}
#endif

#endif // #ifndef JACE_PROXY_JAVA_UTIL_MAP_H

and the CPP

#ifndef JACE_PROXY_JAVA_UTIL_MAP_H
#include "jace/proxy/java/util/Map.h"
#endif

/**
* Standard Jace headers needed to implement this class.
*
*/
#ifndef JACE_JARGUMENTS_H
#include "jace/JArguments.h"
#endif
using jace::JArguments;

#ifndef JACE_JMETHOD_H
#include "jace/JMethod.h"
#endif
using jace::JMethod;

#ifndef JACE_JFIELD_H
#include "jace/JField.h"
#endif
using jace::JField;

#ifndef JACE_JCLASS_IMPL_H
#include "jace/JClassImpl.h"
#endif
using jace::JClassImpl;

/**
* Headers for the classes that this class uses.
*
*/
#ifndef JACE_TYPES_JINT_H
#include "jace/proxy/types/JInt.h"
#endif
#ifndef JACE_TYPES_JBOOLEAN_H
#include "jace/proxy/types/JBoolean.h"
#endif
#ifndef JACE_TYPES_JVOID_H
#include "jace/proxy/types/JVoid.h"
#endif
#ifndef JACE_PROXY_JAVA_UTIL_SET_H
#include "jace/proxy/java/util/Set.h"
#endif
#ifndef JACE_PROXY_JAVA_UTIL_COLLECTION_H
#include "jace/proxy/java/util/Collection.h"
#endif

BEGIN_NAMESPACE_4( jace, proxy, java, util )

/**
* The Jace C++ proxy class source for java/util/Map.
* Please do not edit this source, as any changes you make will be overwritten.
* For more information, please refer to the Jace Developer's Guide.
*
*/
#ifndef VIRTUAL_INHERITANCE_IS_BROKEN
#define Map_INITIALIZER : ::jace::proxy::java::lang::Object( NO_OP )
#else
#define Map_INITIALIZER
#endif

::jace::proxy::types::JInt Map::size() {
::jace::JArguments arguments;
return ::jace::JMethod( "size" ).invoke( *this, arguments );
}

::jace::proxy::types::JBoolean Map::isEmpty() {
::jace::JArguments arguments;
return ::jace::JMethod( "isEmpty" ).invoke( *this, arguments );
}

::jace::proxy::types::JBoolean Map::containsKey( ::jace::proxy::java::lang::Object p0 ) {
::jace::JArguments arguments;
arguments << p0;
return ::jace::JMethod( "containsKey" ).invoke( *this, arguments );
}

::jace::proxy::types::JBoolean Map::containsValue( ::jace::proxy::java::lang::Object p0 ) {
::jace::JArguments arguments;
arguments << p0;
return ::jace::JMethod( "containsValue" ).invoke( *this, arguments );
}

::jace::proxy::java::lang::Object Map::get( ::jace::proxy::java::lang::Object p0 ) {
::jace::JArguments arguments;
arguments << p0;
return ::jace::JMethod( "get" ).invoke( *this, arguments );
}

::jace::proxy::java::lang::Object Map::put( ::jace::proxy::java::lang::Object p0, ::jace::proxy::java::lang::Object p1 ) {
::jace::JArguments arguments;
arguments << p0 << p1;
return ::jace::JMethod( "put" ).invoke( *this, arguments );
}

::jace::proxy::java::lang::Object Map::remove( ::jace::proxy::java::lang::Object p0 ) {
::jace::JArguments arguments;
arguments << p0;
return ::jace::JMethod( "remove" ).invoke( *this, arguments );
}

void Map::putAll( ::jace::proxy::java::util::Map p0 ) {
::jace::JArguments arguments;
arguments << p0;
::jace::JMethod( "putAll" ).invoke( *this, arguments );
}

void Map::clear() {
::jace::JArguments arguments;
::jace::JMethod( "clear" ).invoke( *this, arguments );
}

::jace::proxy::java::util::Set Map::keySet() {
::jace::JArguments arguments;
return ::jace::JMethod( "keySet" ).invoke( *this, arguments );
}

::jace::proxy::java::util::Collection Map::values() {
::jace::JArguments arguments;
return ::jace::JMethod( "values" ).invoke( *this, arguments );
}

::jace::proxy::java::util::Set Map::entrySet() {
::jace::JArguments arguments;
return ::jace::JMethod( "entrySet" ).invoke( *this, arguments );
}

::jace::proxy::types::JBoolean Map::equals( ::jace::proxy::java::lang::Object p0 ) {
::jace::JArguments arguments;
arguments << p0;
return ::jace::JMethod( "equals" ).invoke( *this, arguments );
}

::jace::proxy::types::JInt Map::hashCode() {
::jace::JArguments arguments;
return ::jace::JMethod( "hashCode" ).invoke( *this, arguments );
}

/**
* The following methods are required to integrate this class
* with the Jace framework.
*
*/
/**
* Special no arg constructor for interface virtual inheritance
*
*/
Map::Map() : Object( NO_OP ) {
}

Map::Map( jvalue value ) Map_INITIALIZER {
setJavaJniValue( value );
}

Map::Map( jobject object ) Map_INITIALIZER {
setJavaJniObject( object );
}

Map::Map( const Map& object ) Map_INITIALIZER {
setJavaJniObject( object.getJavaJniObject() );
}

Map::Map( const NoOp& noOp ) Map_INITIALIZER {
}

const JClass* Map::staticGetJavaJniClass() throw ( JNIException ) {
static JClassImpl javaClass( "java/util/Map" );
return &javaClass;
}

const JClass* Map::getJavaJniClass() const throw ( JNIException ) {
return Map::staticGetJavaJniClass();
}

JEnlister Map::enlister;

END_NAMESPACE_4( jace, proxy, java, util )

#ifndef PUT_TSDS_IN_HEADER
template jace::ElementProxy::ElementProxy( jarray array, jvalue element, int index ) :
jace::proxy::java::util::Map( element ), Object( NO_OP ), mIndex( index ) {
JNIEnv* env = ::jace::helper::attach();
parent = static_cast( ::jace::helper::newGlobalRef( env, array ) );
}
template jace::ElementProxy::ElementProxy( const jace::ElementProxy& proxy ) :
jace::proxy::java::util::Map( proxy.getJavaJniObject() ), Object( NO_OP ), mIndex( proxy.mIndex ) {
JNIEnv* env = ::jace::helper::attach();
parent = static_cast( ::jace::helper::newGlobalRef( env, proxy.parent ) );
}
#endif
#ifndef PUT_TSDS_IN_HEADER
template ::jace::JFieldProxy::JFieldProxy( jfieldID fieldID_, jvalue value, jobject parent_ ) :
::jace::proxy::java::util::Map( value ), Object( NO_OP ), fieldID( fieldID_ ) {
JNIEnv* env = ::jace::helper::attach();

if ( parent_ ) {
parent = ::jace::helper::newGlobalRef( env, parent_ );
}
else {
parent = parent_;
}

parentClass = 0;
}
template ::jace::JFieldProxy::JFieldProxy( jfieldID fieldID_, jvalue value, jclass parentClass_ ) :
::jace::proxy::java::util::Map( value ), Object( NO_OP ), fieldID( fieldID_ ) {
JNIEnv* env = ::jace::helper::attach();

parent = 0;
parentClass = static_cast( ::jace::helper::newGlobalRef( env, parentClass_ ) );
}
template ::jace::JFieldProxy::JFieldProxy( const ::jace::JFieldProxy& object ) :
::jace::proxy::java::util::Map( object.getJavaJniValue() ), Object( NO_OP ) {

fieldID = object.fieldID;

if ( object.parent ) {
JNIEnv* env = ::jace::helper::attach();
parent = ::jace::helper::newGlobalRef( env, object.parent );
}
else {
parent = 0;
}

if ( object.parentClass ) {
JNIEnv* env = ::jace::helper::attach();
parentClass = static_cast( ::jace::helper::newGlobalRef( env, object.parentClass ) );
}
else {
parentClass = 0;
}
}
#endif

Jace is really powerful stuff.

After this progress, I am pretty optimistic about future endeavors.

Advertisements

~ by ra1ndog on March 18, 2010.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: