Skip to content

Commit

Permalink
Update for 5.1
Browse files Browse the repository at this point in the history
  • Loading branch information
mar-v-in committed Mar 11, 2015
1 parent b7076b1 commit 0eb269d
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 8 deletions.
4 changes: 2 additions & 2 deletions lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ repositories {
}

android {
compileSdkVersion 'android-21'
compileSdkVersion 'android-22'
buildToolsVersion "21.1.2"

defaultConfig {
minSdkVersion 19
targetSdkVersion 21
targetSdkVersion 22
versionCode 1
versionName "1.0"

Expand Down
75 changes: 75 additions & 0 deletions lib/src/main/java/de/larma/arthook/ArtMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;

/**
* A helper class to access {@link java.lang.reflect.ArtMethod} using reflection.
Expand All @@ -35,8 +40,15 @@ public class ArtMethod {
private static final String FIELD_ACCESS_FLAGS = "accessFlags";
private static final String FIELD_DEX_METHOD_INDEX = "dexMethodIndex";
private static final String FIELD_ENTRY_POINT_FROM_JNI = "entryPointFromJni";
private static final int FIELD_ENTRY_POINT_FROM_JNI_MR1_PRIV_INDEX = 1;
private static final String FIELD_ENTRY_POINT_FROM_INTERPRETER = "entryPointFromInterpreter";
private static final int FIELD_ENTRY_POINT_FROM_INTERPRETER_MR1_PRIV_INDEX = 0;
private static final String FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE = "entryPointFromQuickCompiledCode";
private static final int FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_MR1_PRIV_INDEX = 2;

private static final int LOLLIPOP_MR1_PUB_FIELDS = 36;
private static final int LOLLIPOP_MR1_PRIV_FIELDS = Native.is64Bit() ? 24 : 12;
private static final int LOLLIPOP_MR1_OBJECT_SIZE = LOLLIPOP_MR1_PRIV_FIELDS + LOLLIPOP_MR1_PUB_FIELDS;

private final Object artMethod;

Expand Down Expand Up @@ -83,16 +95,73 @@ public static ArtMethod of(Method method) {
}

private Object get(String name) {
if (SDK_INT >= LOLLIPOP_MR1) {
switch (name) {
case FIELD_ENTRY_POINT_FROM_INTERPRETER:
return getMr1Priv(FIELD_ENTRY_POINT_FROM_INTERPRETER_MR1_PRIV_INDEX);
case FIELD_ENTRY_POINT_FROM_JNI:
return getMr1Priv(FIELD_ENTRY_POINT_FROM_JNI_MR1_PRIV_INDEX);
case FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE:
return getMr1Priv(FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_MR1_PRIV_INDEX);
}
}
return get(getField(name));
}

private long getMr1Priv(int num) {
long objectAddress = Unsafe.getObjectAddress(artMethod);
int intSize = Native.is64Bit() ? 8 : 4;
byte[] bytes = Native.memget_verbose(objectAddress + LOLLIPOP_MR1_PUB_FIELDS + intSize * num, intSize);
if (Native.is64Bit()) {
return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getLong();
} else {
return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt();
}
}

private void set(String name, Object value) {
if (SDK_INT >= LOLLIPOP_MR1) {
switch (name) {
case FIELD_ENTRY_POINT_FROM_INTERPRETER:
setMr1Priv(FIELD_ENTRY_POINT_FROM_INTERPRETER_MR1_PRIV_INDEX, (Long) value);
return;
case FIELD_ENTRY_POINT_FROM_JNI:
setMr1Priv(FIELD_ENTRY_POINT_FROM_JNI_MR1_PRIV_INDEX, (Long) value);
return;
case FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE:
setMr1Priv(FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_MR1_PRIV_INDEX, (Long) value);
return;
}
}
set(getField(name), value);
}

private void setMr1Priv(int num, long value) {
long objectAddress = Unsafe.getObjectAddress(artMethod);
int intSize = Native.is64Bit() ? 8 : 4;
byte[] bytes;
if (Native.is64Bit()) {
bytes = ByteBuffer.allocate(intSize).order(ByteOrder.LITTLE_ENDIAN).putLong(value).array();
} else {
bytes = ByteBuffer.allocate(intSize).order(ByteOrder.LITTLE_ENDIAN).putInt((int) value).array();
}
Native.memput_verbose(bytes, objectAddress + LOLLIPOP_MR1_PUB_FIELDS + intSize * num);
}

@SuppressWarnings({"CloneDoesntCallSuperClone", "CloneDoesntDeclareCloneNotSupportedException"})
public ArtMethod clone() {
ArtMethod clone = new ArtMethod();
if (SDK_INT >= LOLLIPOP_MR1) {
long objectAddress = Unsafe.getObjectAddress(artMethod);
long map = Native.mmap_verbose(LOLLIPOP_MR1_OBJECT_SIZE);
Native.memcpy(objectAddress, map, LOLLIPOP_MR1_OBJECT_SIZE);
try {
long pointerOffset = Unsafe.objectFieldOffset(ArtMethod.class.getDeclaredField("artMethod"));
Unsafe.putLong(clone, pointerOffset, map);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
writeTo(clone);
return clone;
}
Expand All @@ -114,6 +183,12 @@ public void writeTo(ArtMethod target) {
field.setAccessible(true);
target.set(field, get(field));
}
if (SDK_INT >= LOLLIPOP_MR1) {
// Also write priv fields:
target.setEntryPointFromInterpreter(getEntryPointFromInterpreter());
target.setEntryPointFromJni(getEntryPointFromJni());
target.setEntryPointFromQuickCompiledCode(getEntryPointFromQuickCompiledCode());
}
} catch (ClassNotFoundException ignored) {
}
}
Expand Down
19 changes: 19 additions & 0 deletions lib/src/main/java/de/larma/arthook/Native.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,23 @@ public static void munprotect_verbose(long addr, long len) {
Log.d(TAG, "Disabling mprotect from " + DebugHelper.intHex(addr));
munprotect(addr, len);
}

public static native void ptrace(int pid);

public static void ptrace_verbose(int pid) {
Log.d(TAG, "ptrace'ing " + pid);
ptrace(pid);
}

private static Boolean sixtyFour;

public static boolean is64Bit() {
if (sixtyFour == null)
try {
sixtyFour = (Boolean) Class.forName("dalvik.system.VMRuntime").getDeclaredMethod("is64Bit").invoke(Class.forName("dalvik.system.VMRuntime").getDeclaredMethod("getRuntime").invoke(null));
} catch (Exception e) {
throw new RuntimeException("Can't determine int size number!", e);
}
return sixtyFour;
}
}
21 changes: 20 additions & 1 deletion lib/src/main/java/de/larma/arthook/Unsafe.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ public static int arrayBaseOffset(Class cls) {
}
}

@SuppressWarnings("unchecked")
public static long objectFieldOffset(Field field) {
try {
return (long) unsafeClass.getDeclaredMethod("objectFieldOffset", Field.class).invoke(unsafe, field);
} catch (Exception e) {
Log.w(TAG, e);
return 0;
}
}

@SuppressWarnings("unchecked")
public static int getInt(Object array, long offset) {
try {
Expand All @@ -70,10 +80,19 @@ public static long getLong(Object array, long offset) {
}
}

@SuppressWarnings("unchecked")
public static void putLong(Object array, long offset, long value) {
try {
unsafeClass.getDeclaredMethod("putLong", Object.class, long.class, long.class).invoke(unsafe, array, offset, value);
} catch (Exception e) {
Log.w(TAG, e);
}
}

public static long getObjectAddress(Object obj) {
try {
Object[] array = new Object[]{obj};
if ((boolean) Class.forName("dalvik.system.VMRuntime").getDeclaredMethod("is64Bit").invoke(Class.forName("dalvik.system.VMRuntime").getDeclaredMethod("getRuntime").invoke(null))) {
if (Native.is64Bit()) {
return getLong(array, arrayBaseOffset(Object[].class));
} else {
return getInt(array, arrayBaseOffset(Object[].class));
Expand Down
5 changes: 5 additions & 0 deletions lib/src/main/jni/hook.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <sys/mman.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ptrace.h>

#define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "ArtHook_native", __VA_ARGS__))

Expand Down Expand Up @@ -77,4 +78,8 @@ JNIEXPORT void JNICALL Java_de_larma_arthook_Native_munmap(JNIEnv *env, jclass _
if (r == -1) {
LOGV("munmap failed: %d",errno);
}
}

JNIEXPORT void JNICALL Java_de_larma_arthook_Native_ptrace(JNIEnv* env, jclass _cls, jint pid) {
ptrace(PTRACE_ATTACH,(pid_t)pid,0,0);
}
2 changes: 1 addition & 1 deletion test/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ android {
defaultConfig {
applicationId "de.larma.ahook.test"
minSdkVersion 21
targetSdkVersion 21
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
Expand Down
6 changes: 4 additions & 2 deletions test/src/main/java/de/larma/arthook/test/MyActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ protected void onResume() {
//ArtHook.about(System.class.getDeclaredMethod("arraycopy", Object.class, int.class, Object.class, int.class, int.class));
//ArtHook.backupMethods.get(new MethodInfo(Activity.class, "setContentView", int.class)).invoke(this, R.layout.activity_my);
} catch (Exception e) {
Log.d(TAG, "blub", e);
Log.d(TAG, "Catching exception");
Log.d(TAG, "e: ", e);
}
}

@Override
public void setContentView(int layoutResID) {
Log.d(TAG, "my setContentView: "+layoutResID);
Log.d(TAG, "before Activity.setContentView");
super.setContentView(layoutResID);
//Log.d(TAG, "after Activity.setContentView");
}

@Override
Expand Down
8 changes: 6 additions & 2 deletions test/src/main/java/de/larma/arthook/test/MyApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@ public void onCreate() {
} catch (Exception e) {
Log.w(TAG, e);
}

try {
Log.d("MyApplication", "Time:" + System.currentTimeMillis());
Log.d("MyApplication", "BackupTime:" + OriginalMethod.byOriginal(System.class
.getDeclaredMethod("currentTimeMillis")).invokeStatic());
} catch (NoSuchMethodException e) {
Log.w(TAG, e);
}

}

public void pieceGame() {
Expand All @@ -53,11 +55,13 @@ public void pieceGame() {
*/
@Hook("android.app.Activity->setContentView")
public static void Activity_setContentView(Activity activity, int layoutResID) {
Log.d(TAG, "Now you see me");
Log.d(TAG, "before Original[Activity.setContentView]");
OriginalMethod.by(new $() {}).invoke(activity, layoutResID);
//Log.d(TAG, "after Original[Activity.setContentView]");
TextView text = ((TextView) activity.findViewById(R.id.helloWorldText));
text.append("\n -- I am god");
text.append("\n " + new Date().toString());
//Log.d(TAG, "end Hook[Activity.setContentView]");
}

/**
Expand Down Expand Up @@ -87,7 +91,7 @@ public static long System_currentTimeMillis() {
@Hook("java.lang.Class->getDeclaredMethod")
@BackupIdentifier("Class_getDeclaredMethod")
public static Method Class_getDeclaredMethod(Class cls, String name, Class[] params) {
Log.d(TAG, "I'm hooked in getDeclaredMethod");
Log.d(TAG, "I'm hooked in getDeclaredMethod: " + cls + " -> " + name);
if (name.contains("War") || name.contains("war")) {
Log.d(TAG, "make piece not war!"); // This is a political statement!
name = name.replace("War", "Piece").replace("war", "piece");
Expand Down

0 comments on commit 0eb269d

Please sign in to comment.