Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

achieve issue #35 #36

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions java/com/hmtest/ByteUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.yourpackagename.hmtest;

import java.util.Arrays;

public class ByteUtil {
private static final int EVERY_LINE=10;
public static String byteArray2String(byte[] data){
StringBuilder sb=new StringBuilder();
//Begin line.
sb.append("\n");
int line_count=1;
for (int i=0;i<data.length;i++){
sb.append(data[i]);
if(line_count<=EVERY_LINE){
sb.append(" ");
line_count++;
}else{
byte[] pl_data= Arrays.copyOfRange(data,i,i+EVERY_LINE);
String str=new String(pl_data);
sb.append(" ");
sb.append(str);
sb.append("\n");
line_count=1;
}
}
//End line.
sb.append("\n");
return sb.toString();
}
}
22 changes: 22 additions & 0 deletions java/com/hmtest/ContextUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.yourpackagename.hmtest;

import android.app.Application;
import android.content.Context;

import java.lang.reflect.Method;

public class ContextUtil {
public static Context getAppContext(){
try {
//It can be ensured that when module calls this,hidden API policy has been disabled.
Class<?> activityThread=Class.forName("android.app.ActivityThread");
Method currentApplication=activityThread.getDeclaredMethod("currentApplication");

Application app=(Application) currentApplication.invoke(null);
return app.getApplicationContext();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
93 changes: 93 additions & 0 deletions java/com/hmtest/QQTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.yourpackagename.hmtest;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.util.Log;
import android.widget.TextView;

import com.lody.whale.enity.HookModule;
import com.lody.whale.xposed.ClassUtils;
import com.lody.whale.xposed.XC_MethodHook;
import com.lody.whale.xposed.XposedBridge;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicBoolean;

@HookModule(name = "QQTest",targetPkgName = "com.tencent.qqlite")
public class QQTest {
private static final String TAG = "QQTest";

/**
* All hook modules 'must' have this method with the same name
* in order to let World know its target.
* @return return to JNI with our target's package name.
*/
public static String targetPkgName(){
return "com.tencent.qqlite";//Only to avoid AssertException.
}

public static void hook() {
new Thread(){
@Override
public void run() {
try {
Thread.sleep(5000);//5s will be enough for QQ to finish its load.
Log.d(TAG,"hook thread has finish its wait status");
Class<?> codeWrapper=ContextUtil.getAppContext().getClassLoader().
loadClass("com.tencent.qphone.base.util.CodecWarpper");
Method encodeRequest= ClassUtils.findMethodByName(codeWrapper,"encodeRequest").get(0);
Log.d(TAG,"found codecWrapper class:"+codeWrapper.getName());
XposedBridge.hookMethod(encodeRequest,new CodeWrapper_Request());
Log.d(TAG,"hook encodeRequest finished");
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
Log.d(TAG,"hook thread has been started");
}

@Deprecated
private static void doHookAsync(Class<?> codecWrapper)throws Exception{

Field soLoadedField= codecWrapper.getDeclaredField("isSoLoaded");
soLoadedField.setAccessible(true);
while (true){
AtomicBoolean isSoLoaded=(AtomicBoolean)soLoadedField.get(null);
if(isSoLoaded.get()){
Log.d(TAG,"");
break;
}
}
}

public static class CodeWrapper_Request extends XC_MethodHook {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
StringBuilder sb=new StringBuilder();
sb.append("invoke before encodeRequest").append("\n");
sb.append("--- params ---").append("\n");
for (int i=0;i<param.args.length;i++){
sb.append(i+1).append(".");
Class<?> argItemClass=param.args[i].getClass();
sb.append("(").append(argItemClass.getName()).append(")");
if(argItemClass.getName().contains(byte[].class.getName())){//byte array.
byte[] data=(byte[])param.args[i];
String str=new String(data,"gb2312");
sb.append(str);//Output its string instead of address.
//arg[5] is action(MessageSvc.PbGetMsg is receive)
//arg[3](4) is QQ version.
//arg[9](10) is current QQ number
//arg[12](13) is MessageSvc.PbSendMsg's part content.
}else {
sb.append(param.args[i]);
}
sb.append("\n");
}
sb.append("--- ---");
Log.d(TAG,sb.toString());
}
}
}
3 changes: 3 additions & 0 deletions java/com/hmtest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Hook Module示例

注意点已经用注释写在代码里了
28 changes: 28 additions & 0 deletions java/com/lody/whale/CrossDex.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.lody.whale;

import com.lody.whale.wrapper.LogWrapper;
import com.lody.whale.xposed.XC_MethodHook;
import com.lody.whale.xposed.XposedBridge;

import java.lang.reflect.Method;
import java.util.List;

public class CrossDex extends XC_MethodHook {
private static final String TAG="CrossDex";
public static void hook(){
try {
Class<?> inClass=Class.forName("android.app.Instrumentation");
Class<?> appClass=Class.forName("android.app.Application");
Method callAppOnCreate= inClass.getDeclaredMethod("callAppOnCreate", appClass);
XposedBridge.hookMethod(callAppOnCreate,new CrossDex());
LogWrapper.log(LogWrapper.Level.INFO,TAG,"hook callAppOnCreate finished");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
LogWrapper.log(LogWrapper.Level.INFO,TAG,"invoke before callAppOnCreate");
WhaleRuntime.handleCallAppOnCreate();//Give the control to JNI.
}
}
11 changes: 11 additions & 0 deletions java/com/lody/whale/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
这个模块类似于Xposed的XposedCompact.jar的作用,提供hook接口

在模块里必须complieOnly!!!

dex路径依然写死:/data/local/tmp/whale.dex在native_onload.h的DEX_PATH宏

主要代码还是由asLody大佬写的,我的工作主要就是加一些jar包模式(AS的jar包默认不引入android类,只能改反射了)
和全局注入下的适配

--FKD

29 changes: 24 additions & 5 deletions java/com/lody/whale/WhaleRuntime.java
Original file line number Diff line number Diff line change
@@ -1,30 +1,45 @@
package com.lody.whale;

import android.os.Build;

import com.lody.whale.wrapper.DeviceInfo;
import com.lody.whale.wrapper.LogWrapper;
import com.lody.whale.xposed.XposedBridge;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
* @author Lody
* <p>
* NOTICE: Do not move or rename any methods in this class.
*/
public class WhaleRuntime {
private static final String TAG="WhaleRuntime";
public static final String LOAD_MODE_KEY="com.lody.whale.load_mode";

private static boolean loadLib() {
String loadMode=System.getProperty(LOAD_MODE_KEY,"java");
LogWrapper.log(LogWrapper.Level.ERROR,TAG,"LoadMode is:"+loadMode);
if(!loadMode.equals("jni")) {
System.loadLibrary("whale");
}
return true;
}

public static boolean CAN_WHALE=false;
static {
System.loadLibrary("whale");
//System.loadLibrary("whale");
CAN_WHALE=loadLib();
}

private static String getShorty(Member member) {
return VMHelper.getShorty(member);
}

public static long[] countInstancesOfClasses(Class[] classes, boolean assignable) {
if (Build.VERSION.SDK_INT < 27) {
if (DeviceInfo.getSDKInt() < 27) {
throw new UnsupportedOperationException("Not support countInstancesOfClasses on your device yet.");
}
try {
Expand All @@ -37,7 +52,7 @@ public static long[] countInstancesOfClasses(Class[] classes, boolean assignable
}

public static Object[][] getInstancesOfClasses(Class[] classes, boolean assignable) {
if (Build.VERSION.SDK_INT < 28) {
if (DeviceInfo.getSDKInt() < 28) {
throw new UnsupportedOperationException("Not support getInstancesOfClasses on your device yet.");
}
try {
Expand All @@ -60,6 +75,10 @@ public static native Object invokeOriginalMethodNative(long slot, Object thisObj

public static native long hookMethodNative(Class<?> declClass, Member method, Object additionInfo);

public static native void testHookNative();//测试native回调hook

public static native void handleCallAppOnCreate();

public static native void setObjectClassNative(Object object, Class<?> parent);

public static native Object cloneToSubclassNative(Object object, Class<?> subClass);
Expand Down
7 changes: 7 additions & 0 deletions java/com/lody/whale/WhaleWrapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.lody.whale;

public class WhaleWrapper {
private static void testInternal(){

}
}
13 changes: 13 additions & 0 deletions java/com/lody/whale/enity/HookModule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.lody.whale.enity;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HookModule {
String name();
String targetPkgName() default "all";//"all" means it will be loaded into every app.
}
3 changes: 3 additions & 0 deletions java/com/lody/whale/enity/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
这个注解被JNI反射获取的时候会引发AssertException,一开始怀疑是依赖问题,排查了一遍dependencies发现没有,最后换了另一种(不太优雅的)实现

用速度换稳定性吧
46 changes: 46 additions & 0 deletions java/com/lody/whale/wrapper/BundleWrapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.lody.whale.wrapper;

import java.io.Serializable;
import java.lang.reflect.Method;

public class BundleWrapper {
private static Class BUNDLE_CLASS;
static{
try {
BUNDLE_CLASS=Class.forName("android.os.Bundle");
} catch (Exception e) {
e.printStackTrace();
}
}
public static Object newBundle(){
try {
return BUNDLE_CLASS.newInstance();
} catch (Exception e){
e.printStackTrace();
}
return null;
}

public static Object getSerializable(Object bundle, String key){
if(!BUNDLE_CLASS.isInstance(bundle))return null;
try {
Method getMethod=BUNDLE_CLASS.getDeclaredMethod("getSerializable",String.class);
getMethod.setAccessible(true);
return getMethod.invoke(bundle,key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

public static void putSerializable(Object bundle,String key,Serializable value){
if(!BUNDLE_CLASS.isInstance(bundle))return;
try {
Method putMethod=BUNDLE_CLASS.getDeclaredMethod("putSerializable",String.class,Serializable.class);
putMethod.setAccessible(true);
putMethod.invoke(bundle,key,value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
43 changes: 43 additions & 0 deletions java/com/lody/whale/wrapper/DeviceInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.lody.whale.wrapper;


import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DeviceInfo {
public String date;
public String time;
public static DeviceInfo getNow(){
SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String nowStr= ft.format(new Date());
DeviceInfo now=new DeviceInfo();
now.date=nowStr.split(" ")[0];
now.time=nowStr.split(" ")[1];
return now;
}

public static String getSystemProp(String name){
try {
Class sysPropClass=Class.forName("andro<id.os.SystemProperties");
Method getMethod=sysPropClass.getDeclaredMethod("get",String.class,String.class);
getMethod.setAccessible(true);
return (String) getMethod.invoke(null,name,FAIL_STR);
} catch (Exception e) {
e.printStackTrace();
return FAIL_STR;
}
}

public static final String FAIL_STR ="unknown";
public static String getDeviceID() {
return getSystemProp("ro.build.id");
}

public static final int FAIL_INT=0;
public static int getSDKInt(){
String sdk=getSystemProp("ro.build.version.sdk");
if(sdk.equals(FAIL_STR))return FAIL_INT;
return Integer.parseInt(sdk);
}
}
Loading