diff --git a/java/com/hmtest/ByteUtil.java b/java/com/hmtest/ByteUtil.java new file mode 100644 index 0000000..81e14ab --- /dev/null +++ b/java/com/hmtest/ByteUtil.java @@ -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 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; + } + } +} diff --git a/java/com/hmtest/QQTest.java b/java/com/hmtest/QQTest.java new file mode 100644 index 0000000..1f7ab6a --- /dev/null +++ b/java/com/hmtest/QQTest.java @@ -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 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()); + } + } +} diff --git a/java/com/hmtest/README.md b/java/com/hmtest/README.md new file mode 100644 index 0000000..a17f9f0 --- /dev/null +++ b/java/com/hmtest/README.md @@ -0,0 +1,3 @@ +Hook Module示例 + +注意点已经用注释写在代码里了 diff --git a/java/com/lody/whale/CrossDex.java b/java/com/lody/whale/CrossDex.java new file mode 100644 index 0000000..a591984 --- /dev/null +++ b/java/com/lody/whale/CrossDex.java @@ -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. + } +} diff --git a/java/com/lody/whale/README.md b/java/com/lody/whale/README.md new file mode 100644 index 0000000..6c314b5 --- /dev/null +++ b/java/com/lody/whale/README.md @@ -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 + diff --git a/java/com/lody/whale/WhaleRuntime.java b/java/com/lody/whale/WhaleRuntime.java index 249518d..864556b 100644 --- a/java/com/lody/whale/WhaleRuntime.java +++ b/java/com/lody/whale/WhaleRuntime.java @@ -1,12 +1,14 @@ 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 @@ -14,9 +16,22 @@ * 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) { @@ -24,7 +39,7 @@ private static String getShorty(Member 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 { @@ -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 { @@ -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); diff --git a/java/com/lody/whale/WhaleWrapper.java b/java/com/lody/whale/WhaleWrapper.java new file mode 100644 index 0000000..c6a8050 --- /dev/null +++ b/java/com/lody/whale/WhaleWrapper.java @@ -0,0 +1,7 @@ +package com.lody.whale; + +public class WhaleWrapper { + private static void testInternal(){ + + } +} diff --git a/java/com/lody/whale/enity/HookModule.java b/java/com/lody/whale/enity/HookModule.java new file mode 100644 index 0000000..16713e4 --- /dev/null +++ b/java/com/lody/whale/enity/HookModule.java @@ -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. +} diff --git a/java/com/lody/whale/enity/README.md b/java/com/lody/whale/enity/README.md new file mode 100644 index 0000000..dc3cef8 --- /dev/null +++ b/java/com/lody/whale/enity/README.md @@ -0,0 +1,3 @@ +这个注解被JNI反射获取的时候会引发AssertException,一开始怀疑是依赖问题,排查了一遍dependencies发现没有,最后换了另一种(不太优雅的)实现 + +用速度换稳定性吧 diff --git a/java/com/lody/whale/wrapper/BundleWrapper.java b/java/com/lody/whale/wrapper/BundleWrapper.java new file mode 100644 index 0000000..b9f54df --- /dev/null +++ b/java/com/lody/whale/wrapper/BundleWrapper.java @@ -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(); + } + } +} diff --git a/java/com/lody/whale/wrapper/DeviceInfo.java b/java/com/lody/whale/wrapper/DeviceInfo.java new file mode 100644 index 0000000..a1d2dd2 --- /dev/null +++ b/java/com/lody/whale/wrapper/DeviceInfo.java @@ -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 findMethodByName(Class clazz, String name){ + List result=new ArrayList<>(); + Method[] methods=clazz.getDeclaredMethods(); + for(Method method:methods){ + if(method.getName().equals(name)) result.add(method); + } + return result; + } } diff --git a/java/com/lody/whale/xposed/XposedBridge.java b/java/com/lody/whale/xposed/XposedBridge.java index ee8873f..323ea3c 100644 --- a/java/com/lody/whale/xposed/XposedBridge.java +++ b/java/com/lody/whale/xposed/XposedBridge.java @@ -1,8 +1,8 @@ package com.lody.whale.xposed; -import android.util.Log; import com.lody.whale.WhaleRuntime; +import com.lody.whale.wrapper.LogWrapper; import com.lody.whale.xposed.XC_MethodHook.MethodHookParam; import java.lang.reflect.Constructor; @@ -27,7 +27,6 @@ public final class XposedBridge { * The system class loader which can be used to locate Android framework classes. * Application classes cannot be retrieved from it. * - * @see ClassLoader#getSystemClassLoader */ @SuppressWarnings("unused") public static final ClassLoader BOOTCLASSLOADER = ClassLoader.getSystemClassLoader(); @@ -50,7 +49,7 @@ public final class XposedBridge { */ @SuppressWarnings("unused") public static void log(final String text) { - Log.i(TAG, text); + LogWrapper.log(LogWrapper.Level.INFO,TAG, text); } /** @@ -59,7 +58,29 @@ public static void log(final String text) { * @param t The Throwable object for the stack trace. */ public static void log(final Throwable t) { - Log.e(TAG, Log.getStackTraceString(t)); + LogWrapper.e(TAG,t); + } + + /** + * the static method is lazy resolved, when not resolved, the entry point is a trampoline of + * a bridge, we can not hook these entry. this method force the static method to be resolved. + */ + public static void resolveStaticMethod(Member method) { + //ignore result, just call to trigger resolve + if (method == null) + return; + try { + if (method instanceof Method && Modifier.isStatic(method.getModifiers())) { + ((Method) method).setAccessible(true); + ((Method) method).invoke(new Object(), getFakeArgs((Method) method)); + } + } catch (Exception ignored) { + // we should never make a successful call. + } + } + + private static Object[] getFakeArgs(Method method) { + return method.getParameterTypes().length == 0 ? new Object[]{new Object()} : null; } /** @@ -69,11 +90,7 @@ public static void log(final Throwable t) { * @param hookMethod The method to be hooked. * @param callback The callback to be executed when the hooked method is called. * @return An object that can be used to remove the hook. - * @see XposedHelpers#findAndHookMethod(String, ClassLoader, String, Object...) - * @see XposedHelpers#findAndHookMethod(Class, String, Object...) * @see #hookAllMethods - * @see XposedHelpers#findAndHookConstructor(String, ClassLoader, Object...) - * @see XposedHelpers#findAndHookConstructor(Class, Object...) * @see #hookAllConstructors */ public static XC_MethodHook.Unhook hookMethod(final Member hookMethod, final XC_MethodHook callback) { @@ -98,7 +115,7 @@ public static XC_MethodHook.Unhook hookMethod(final Member hookMethod, final XC_ callbacks.add(callback); if (newMethod) { - XposedHelpers.resolveStaticMethod(hookMethod); + resolveStaticMethod(hookMethod); AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks); long slot = WhaleRuntime.hookMethodNative(hookMethod.getDeclaringClass(), hookMethod, additionalInfo); if (slot <= 0) { diff --git a/whale/CMakeLists.txt b/whale/CMakeLists.txt index 91f7c5b..b01a8dc 100644 --- a/whale/CMakeLists.txt +++ b/whale/CMakeLists.txt @@ -64,8 +64,21 @@ set(WHALE_DARWIN_SOURCES src/dbi/darwin/macho_import_hook.cc ) +set(MSHOOK + src/MSHook/hook.cpp + src/MSHook/ARM.cpp + src/MSHook/Thumb.cpp + src/MSHook/x86.cpp + src/MSHook/x86_64.cpp + src/MSHook/Debug.cpp + src/MSHook/Hooker.cpp + src/MSHook/PosixMemory.cpp + src/MSHook/util.cpp + ) + set(WHALE_ANDROID_ART src/android/art/native_on_load.cc + src/android/art/art_helper.cpp src/android/art/art_runtime.cc src/android/art/art_symbol_resolver.cc src/android/art/java_types.cc @@ -73,6 +86,15 @@ set(WHALE_ANDROID_ART src/android/art/art_method.cc src/android/art/scoped_thread_state_change.cc src/android/art/art_jni_trampoline.cc + #dex + src/android/dex/DexManager.cpp + #hook_modules + src/android/hook_module/JNIHookManager.cpp + src/android/hook_module/world.cpp + #base_tool + src/base/process_util.cpp + src/base/str_util.cpp + native-lib.cpp # func test ) set(WHALE_AARCH32 @@ -151,7 +173,7 @@ endif () if (PLATFORM STREQUAL "Android") - set(WHALE_SOURCES ${WHALE_SOURCES} ${WHALE_ANDROID_ART}) + set(WHALE_SOURCES ${WHALE_SOURCES} ${WHALE_ANDROID_ART} ${MSHOOK}) if (CMAKE_SYSTEM_PROCESSOR MATCHES "^arm") diff --git a/whale/src/MSHook/ARM.cpp b/whale/src/MSHook/ARM.cpp new file mode 100644 index 0000000..0bf12e5 --- /dev/null +++ b/whale/src/MSHook/ARM.cpp @@ -0,0 +1,143 @@ +#include "ARM.h" +#include "PosixMemory.h" + +static uint32_t abs_wrapper(uint32_t i){ + return abs((int)i); +} + +void ARM::SubstrateHookFunctionARM(SubstrateProcessRef process, void *symbol, void *replace, void **result) { + if (symbol == NULL) + return; + + uint32_t *area(reinterpret_cast(symbol)); + uint32_t *arm(area); + + const size_t used(8); + + uint32_t backup[used / sizeof(uint32_t)] = {arm[0], arm[1]}; + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHexEx(area, used + sizeof(uint32_t), 4, name); + } + + if (result != NULL) { + + if (backup[0] == A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8)) { + *result = reinterpret_cast(backup[1]); + return; + } + + size_t length(used); + for (unsigned offset(0); offset != used / sizeof(uint32_t); ++offset) + if (A$pcrel$r(backup[offset])) { + if ((backup[offset] & 0x02000000) == 0 || (backup[offset] & 0x0000f000 >> 12) != (backup[offset] & 0x0000000f)) + length += 2 * sizeof(uint32_t); + else + length += 4 * sizeof(uint32_t); + } + + length += 2 * sizeof(uint32_t); + + uint32_t *buffer(reinterpret_cast(mmap( + NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 + ))); + + if (buffer == MAP_FAILED) { + MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno); + *result = NULL; + return; + } + + if (false) fail: { + munmap(buffer, length); + *result = NULL; + return; + } + + size_t start(0), end(length / sizeof(uint32_t)); + uint32_t *trailer(reinterpret_cast(buffer + end)); + for (unsigned offset(0); offset != used / sizeof(uint32_t); ++offset) + if (A$pcrel$r(backup[offset])) { + union { + uint32_t value; + + struct { + uint32_t rm : 4; + uint32_t : 1; + uint32_t shift : 2; + uint32_t shiftamount : 5; + uint32_t rd : 4; + uint32_t rn : 4; + uint32_t l : 1; + uint32_t w : 1; + uint32_t b : 1; + uint32_t u : 1; + uint32_t p : 1; + uint32_t mode : 1; + uint32_t type : 2; + uint32_t cond : 4; + }; + } bits = {backup[offset+0]}, copy(bits); + + bool guard; + if (bits.mode == 0 || bits.rd != bits.rm) { + copy.rn = bits.rd; + guard = false; + } else { + copy.rn = bits.rm != A$r0 ? A$r0 : A$r1; + guard = true; + } + + if (guard) + buffer[start++] = A$stmdb_sp$_$rs$((1 << copy.rn)); + + buffer[start+0] = A$ldr_rd_$rn_im$(copy.rn, A$pc, (end-1 - (start+0)) * 4 - 8); + buffer[start+1] = copy.value; + + start += 2; + + if (guard) + buffer[start++] = A$ldmia_sp$_$rs$((1 << copy.rn)); + + + *--trailer = static_cast((uintptr_t)area)+ + static_cast((uintptr_t)offset)+ 8; + end -= 1; + } else + buffer[start++] = backup[offset]; + + buffer[start+0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + buffer[start+1] = static_cast((uintptr_t)area + (uintptr_t)used / sizeof(uint32_t)); + + if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) { + MSLog(MSLogLevelError, "MS:Error:mprotect():%d", errno); + goto fail; + } + + *result = buffer; + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", *result); + MSLogHexEx(buffer, length, 4, name); + } + + } + + { + SubstrateHookMemory code(process, symbol, used); + + arm[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + arm[1] = static_cast((uintptr_t)replace); + } + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHexEx(area, used + sizeof(uint32_t), 4, name); + } +} + + diff --git a/whale/src/MSHook/ARM.h b/whale/src/MSHook/ARM.h new file mode 100644 index 0000000..7b27a5a --- /dev/null +++ b/whale/src/MSHook/ARM.h @@ -0,0 +1,83 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#ifndef SUBSTRATE_ARM_HPP +#define SUBSTRATE_ARM_HPP + +#include "CydiaSubstrate.h" +#include "Log.h" +#include "Debug.h" +#include +#include +#include +#include +#include + +static uint32_t abs_wrapper(uint32_t i); + +enum A$r { + A$r0, A$r1, A$r2, A$r3, + A$r4, A$r5, A$r6, A$r7, + A$r8, A$r9, A$r10, A$r11, + A$r12, A$r13, A$r14, A$r15, + A$sp = A$r13, + A$lr = A$r14, + A$pc = A$r15 +}; + +enum A$c { + A$eq, A$ne, A$cs, A$cc, + A$mi, A$pl, A$vs, A$vc, + A$hi, A$ls, A$ge, A$lt, + A$gt, A$le, A$al, + A$hs = A$cs, + A$lo = A$cc +}; + +#define A$mrs_rm_cpsr(rd) /* mrs rd, cpsr */ \ + (0xe10f0000 | ((rd) << 12)) +#define A$msr_cpsr_f_rm(rm) /* msr cpsr_f, rm */ \ + (0xe128f000 | (rm)) +#define A$ldr_rd_$rn_im$(rd, rn, im) /* ldr rd, [rn, #im] */ \ + (0xe5100000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | abs_wrapper(im)) +#define A$str_rd_$rn_im$(rd, rn, im) /* sr rd, [rn, #im] */ \ + (0xe5000000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | abs_wrapper(im)) +#define A$sub_rd_rn_$im(rd, rn, im) /* sub, rd, rn, #im */ \ + (0xe2400000 | ((rn) << 16) | ((rd) << 12) | (im & 0xff)) +#define A$blx_rm(rm) /* blx rm */ \ + (0xe12fff30 | (rm)) +#define A$mov_rd_rm(rd, rm) /* mov rd, rm */ \ + (0xe1a00000 | ((rd) << 12) | (rm)) +#define A$ldmia_sp$_$rs$(rs) /* ldmia sp!, {rs} */ \ + (0xe8b00000 | (A$sp << 16) | (rs)) +#define A$stmdb_sp$_$rs$(rs) /* stmdb sp!, {rs} */ \ + (0xe9200000 | (A$sp << 16) | (rs)) +#define A$stmia_sp$_$r0$ 0xe8ad0001 /* stmia sp!, {r0} */ +#define A$bx_r0 0xe12fff10 /* bx r0 */ + +static inline bool A$pcrel$r(uint32_t ic) { + return (ic & 0x0c000000) == 0x04000000 && (ic & 0xf0000000) != 0xf0000000 && (ic & 0x000f0000) == 0x000f0000; +} + +namespace ARM{ + extern "C" void SubstrateHookFunctionARM(SubstrateProcessRef process, void *symbol, void *replace, void **result); +} +#endif//SUBSTRATE_ARM_HPP diff --git a/whale/src/MSHook/CydiaSubstrate.h b/whale/src/MSHook/CydiaSubstrate.h new file mode 100644 index 0000000..4340399 --- /dev/null +++ b/whale/src/MSHook/CydiaSubstrate.h @@ -0,0 +1,15 @@ +#ifndef CYDIASUBSTRATE_H_ +#define CYDIASUBSTRATE_H_ + +#include +#include + +#define _finline \ + inline __attribute__((__always_inline__)) +#define _disused \ + __attribute__((__unused__)) +#define _extern \ + extern "C" __attribute__((__visibility__("default"))) + +#include "SubstrateStruct.h" +#endif /* CYDIASUBSTRATE_H_ */ diff --git a/whale/src/MSHook/Debug.cpp b/whale/src/MSHook/Debug.cpp new file mode 100644 index 0000000..2f62125 --- /dev/null +++ b/whale/src/MSHook/Debug.cpp @@ -0,0 +1,96 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#include "CydiaSubstrate.h" +#include "Debug.h" + +#include +#include +#include + +_extern bool MSDebug; +bool MSDebug = true; + +static char _MSHexChar(uint8_t value) { + return value < 0x20 || value >= 0x80 ? '.' : value; +} + +#define HexWidth_ 16 +#define HexDepth_ 4 + +void MSLogHexEx(const void *vdata, size_t size, size_t stride, const char *mark) { + const uint8_t *data((const uint8_t *) vdata); + + size_t i(0), j; + + char d[256]; + size_t b(0); + d[0] = '\0'; + + while (i != size) { + if (i % HexWidth_ == 0) { + if (mark != NULL) + b += sprintf(d + b, "[%s] ", mark); + b += sprintf(d + b, "0x%.3zx:", i); + } + + b += sprintf(d + b, " "); + + for (size_t q(0); q != stride; ++q) + b += sprintf(d + b, "%.2x", data[i + stride - q - 1]); + + i += stride; + + for (size_t q(1); q != stride; ++q) + b += sprintf(d + b, " "); + + if (i % HexDepth_ == 0) + b += sprintf(d + b, " "); + + if (i % HexWidth_ == 0) { + b += sprintf(d + b, " "); + for (j = i - HexWidth_; j != i; ++j) + b += sprintf(d + b, "%c", _MSHexChar(data[j])); + + lprintf("%s", d); + b = 0; + d[0] = '\0'; + } + } + + if (i % HexWidth_ != 0) { + for (j = i % HexWidth_; j != HexWidth_; ++j) + b += sprintf(d + b, " "); + for (j = 0; j != (HexWidth_ - i % HexWidth_ + HexDepth_ - 1) / HexDepth_; ++j) + b += sprintf(d + b, " "); + b += sprintf(d + b, " "); + for (j = i / HexWidth_ * HexWidth_; j != i; ++j) + b += sprintf(d + b, "%c", _MSHexChar(data[j])); + + lprintf("%s", d); + b = 0; + d[0] = '\0'; + } +} + +void MSLogHex(const void *vdata, size_t size, const char *mark) { + return MSLogHexEx(vdata, size, 1, mark); +} diff --git a/whale/src/MSHook/Debug.h b/whale/src/MSHook/Debug.h new file mode 100644 index 0000000..f01e6a2 --- /dev/null +++ b/whale/src/MSHook/Debug.h @@ -0,0 +1,34 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#ifndef SUBSTRATE_DEBUG_HPP +#define SUBSTRATE_DEBUG_HPP + +#include "Log.h" +#include +#define lprintf(format, ...) \ + MSLog(MSLogLevelNotice, format, ## __VA_ARGS__) + +extern "C" bool MSDebug; +void MSLogHexEx(const void *vdata, size_t size, size_t stride, const char *mark = 0); +void MSLogHex(const void *vdata, size_t size, const char *mark = 0); + +#endif//SUBSTRATE_DEBUG_HPP diff --git a/whale/src/MSHook/Log.h b/whale/src/MSHook/Log.h new file mode 100644 index 0000000..d14cb8a --- /dev/null +++ b/whale/src/MSHook/Log.h @@ -0,0 +1,67 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) +*/ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . +**/ +/* }}} */ + +#ifndef SUBSTRATE_LOG_HPP +#define SUBSTRATE_LOG_HPP + +#include + +#define MSLogLevelNotice ANDROID_LOG_INFO +#define MSLogLevelWarning ANDROID_LOG_WARN +#define MSLogLevelError ANDROID_LOG_ERROR + +#define DEBUG 1 +#define EXE_PRINTF 0 +#ifndef LOG_TAG + # define LOG_TAG "Native_X" +#endif + +#if DEBUG + #ifdef EXE_PRINTF + #define LOGD(fmt,...) printf("[%12s] " fmt "\n", __FUNCTION__,##__VA_ARGS__) + #define LOGI(fmt,...) printf("[%12s] " fmt "\n", __FUNCTION__,##__VA_ARGS__) + #define LOGV(fmt,...) printf("[%12s] " fmt "\n", __FUNCTION__,##__VA_ARGS__) + #define LOGW(fmt,...) printf("[%12s] " fmt "\n", __FUNCTION__,##__VA_ARGS__) + #define LOGE(fmt,...) printf("[%12s] " fmt "\n", __FUNCTION__,##__VA_ARGS__) + #define LOGF(fmt,...) printf("[%12s] " fmt "\n", __FUNCTION__,##__VA_ARGS__) + + #else + #define LOGD(fmt,...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "[%s]" fmt, __FUNCTION__,##__VA_ARGS__) + #define LOGI(fmt,...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "[%s]" fmt, __FUNCTION__,##__VA_ARGS__) + #define LOGV(fmt,...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, "[%s]" fmt, __FUNCTION__,##__VA_ARGS__) + #define LOGW(fmt,...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "[%s]" fmt, __FUNCTION__,##__VA_ARGS__) + #define LOGE(fmt,...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "[%s]" fmt, __FUNCTION__,##__VA_ARGS__) + #define LOGF(fmt,...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, "[%s]" fmt, __FUNCTION__,##__VA_ARGS__) + #endif +#else + #define LOGD(...) while(0){} + #define LOGI(...) while(0){} + #define LOGV(...) while(0){} + #define LOGW(...) while(0){} + #define LOGE(...) while(0){} + #define LOGW(...) while(0){} +#endif + +#define MSLog(level, fmt,...) do { \ + printf("[%12s] " fmt "\n", __FUNCTION__,##__VA_ARGS__); \ + __android_log_print(level, LOG_TAG, "[%s]" fmt, __FUNCTION__,##__VA_ARGS__); \ +} while (false) +#endif//SUBSTRATE_LOG_HPP diff --git a/whale/src/MSHook/PosixMemory.cpp b/whale/src/MSHook/PosixMemory.cpp new file mode 100644 index 0000000..eb31f64 --- /dev/null +++ b/whale/src/MSHook/PosixMemory.cpp @@ -0,0 +1,67 @@ +/* Cydia Substrate - Powerful Code Insertion Platform + * Copyright (C) 2008-2011 Jay Freeman (saurik) + */ + +/* GNU Lesser General Public License, Version 3 {{{ */ +/* + * Substrate is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Substrate is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Substrate. If not, see . + **/ +/* }}} */ + +#include "CydiaSubstrate.h" +#include "PosixMemory.h" +#include "Log.h" + +#include +#include +#include +#include + +extern "C" SubstrateMemoryRef SubstrateMemoryCreate( + SubstrateAllocatorRef allocator, SubstrateProcessRef process, + void *data, size_t size) { + if (allocator != NULL) { + MSLog(MSLogLevelError, "MS:Error:allocator != NULL"); + return NULL; + } + + if (size == 0) + return NULL; + + int page(getpagesize()); + + uintptr_t base(reinterpret_cast(data) / page * page); + size_t width( + ((reinterpret_cast(data) + size - 1) / page + 1) * page + - base); + void *address(reinterpret_cast(base)); + + if (mprotect(address, width, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) { + MSLog(MSLogLevelError, "MS:Error:mprotect() = %d", errno); + return NULL; + } + + return new SubstrateMemory(address, width); +} + +extern "C" void SubstrateMemoryRelease(SubstrateMemoryRef memory) { + if (mprotect(memory->address_, memory->width_, + PROT_READ | PROT_WRITE | PROT_EXEC) == -1) + MSLog(MSLogLevelError, "MS:Error:mprotect() = %d", errno); + + __clear_cache(reinterpret_cast(memory->address_), + reinterpret_cast(memory->address_) + memory->width_); + + delete memory; +} diff --git a/whale/src/MSHook/PosixMemory.h b/whale/src/MSHook/PosixMemory.h new file mode 100644 index 0000000..2cfdbe8 --- /dev/null +++ b/whale/src/MSHook/PosixMemory.h @@ -0,0 +1,29 @@ +/* + * PosixMemory.h + * + * Created on: 2016222 + * Author: peng + */ + +#ifndef POSIXMEMORY_H_ +#define POSIXMEMORY_H_ + +#include "CydiaSubstrate.h" + + +extern "C" SubstrateMemoryRef SubstrateMemoryCreate(SubstrateAllocatorRef allocator, SubstrateProcessRef process, void *data, size_t size); +extern "C" void SubstrateMemoryRelease(SubstrateMemoryRef memory); +extern "C" void __clear_cache(void *beg, void *end); + +struct SubstrateHookMemory { + SubstrateMemoryRef handle_; + + SubstrateHookMemory(SubstrateProcessRef process, void *data, size_t size) : handle_(SubstrateMemoryCreate(NULL, NULL, data, size)) {} + + ~SubstrateHookMemory() { + if (handle_ != NULL) + SubstrateMemoryRelease(handle_); + } +}; + +#endif /* POSIXMEMORY_H_ */ diff --git a/whale/src/MSHook/README.md b/whale/src/MSHook/README.md new file mode 100644 index 0000000..df7e5b0 --- /dev/null +++ b/whale/src/MSHook/README.md @@ -0,0 +1,4 @@ +这是Cydia的inline hook模块,做了一些小修改好让它能在最新的clang上编译通过(abs函数似乎被新的c++标准重载了?) +whale的那个WInlineHookFunction在我的所有能拿到的测试环境上(SM-P550真机,AOSP,GENYMOTION,雷电模拟器)只要调用原函数必定段错误。。。 +为了hook JNI_CreateJVM混进java世界,只能先上MSHook了。其实xhook也可以,但是iqiyi的东西。。。协议不是人话看不懂,有版权问题就不大好了 +--FKD diff --git a/whale/src/MSHook/SubstrateStruct.h b/whale/src/MSHook/SubstrateStruct.h new file mode 100644 index 0000000..66677cd --- /dev/null +++ b/whale/src/MSHook/SubstrateStruct.h @@ -0,0 +1,20 @@ +/* + * SubstrateMacro.h + * + * Created on: 2016222 + * Author: peng + */ + +#ifndef SUBSTRATEMACRO_H_ +#define SUBSTRATEMACRO_H_ + +#include +typedef struct __SubstrateProcess *SubstrateProcessRef; +typedef void *SubstrateAllocatorRef; +typedef struct SubstrateMemory { + void *address_; + size_t width_; + SubstrateMemory(void *address, size_t width):address_(address), width_(width) {} +}*SubstrateMemoryRef; + +#endif /* SUBSTRATEMACRO_H_ */ diff --git a/whale/src/MSHook/Thumb.cpp b/whale/src/MSHook/Thumb.cpp new file mode 100644 index 0000000..306c688 --- /dev/null +++ b/whale/src/MSHook/Thumb.cpp @@ -0,0 +1,412 @@ +#include +#include "ARM.h" +#include "Thumb.h" + +static uint16_t abs_wrapper_16(uint16_t i){ + return abs(i); +} + +static size_t Thumb::MSGetInstructionWidth(void *start) { + if ((reinterpret_cast(start) & 0x1) == 0) + return MSGetInstructionWidthARM(start); + else + return MSGetInstructionWidthThumb(reinterpret_cast(reinterpret_cast(start) & ~0x1)); +} + +void Thumb::SubstrateHookFunctionThumb(SubstrateProcessRef process, void *symbol, void *replace, void **result){ + if (symbol == NULL) + return; + + uint16_t *area(reinterpret_cast(symbol)); + + unsigned align((reinterpret_cast(area) & 0x2) == 0 ? 0 : 1); + uint16_t *thumb(area + align); + + uint32_t *arm(reinterpret_cast(thumb + 2)); + uint16_t *trail(reinterpret_cast(arm + 2)); + + if ( + (align == 0 || area[0] == T$nop) && + thumb[0] == T$bx(A$pc) && + thumb[1] == T$nop && + arm[0] == A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8) + ) { + if (result != NULL) + *result = reinterpret_cast(arm[1]); + + SubstrateHookMemory code(process, arm + 1, sizeof(uint32_t) * 1); + + arm[1] = (uint32_t)((uintptr_t)replace); + + return; + } + + size_t required((trail - area) * sizeof(uint16_t)); + + size_t used(0); + while (used < required) + used += MSGetInstructionWidthThumb(reinterpret_cast(area) + used); + used = (used + sizeof(uint16_t) - 1) / sizeof(uint16_t) * sizeof(uint16_t); + + size_t blank((used - required) / sizeof(uint16_t)); + + uint16_t backup[used / sizeof(uint16_t)]; + memcpy(backup, area, used); + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHexEx(area, used + sizeof(uint16_t), 2, name); + } + + if (result != NULL) { + + size_t length(used); + for (unsigned offset(0); offset != used / sizeof(uint16_t); ++offset) + if (T$pcrel$ldr(backup[offset])) + length += 3 * sizeof(uint16_t); + else if (T$pcrel$b(backup[offset])) + length += 6 * sizeof(uint16_t); + else if (T2$pcrel$b(backup + offset)) { + length += 5 * sizeof(uint16_t); + ++offset; + } else if (T$pcrel$bl(backup + offset)) { + length += 5 * sizeof(uint16_t); + ++offset; + } else if (T$pcrel$cbz(backup[offset])) { + length += 16 * sizeof(uint16_t); + } else if (T$pcrel$ldrw(backup[offset])) { + length += 4 * sizeof(uint16_t); + ++offset; + } else if (T$pcrel$add(backup[offset])) + length += 6 * sizeof(uint16_t); + else if (T$32bit$i(backup[offset])) + ++offset; + + unsigned pad((length & 0x2) == 0 ? 0 : 1); + length += (pad + 2) * sizeof(uint16_t) + 2 * sizeof(uint32_t); + + uint16_t *buffer(reinterpret_cast(mmap( + NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 + ))); + + if (buffer == MAP_FAILED) { + MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno); + *result = NULL; + return; + } + + if (false) fail: { + munmap(buffer, length); + *result = NULL; + return; + } + + size_t start(pad), end(length / sizeof(uint16_t)); + uint32_t *trailer(reinterpret_cast(buffer + end)); + for (unsigned offset(0); offset != used / sizeof(uint16_t); ++offset) { + if (T$pcrel$ldr(backup[offset])) { + union { + uint16_t value; + + struct { + uint16_t immediate : 8; + uint16_t rd : 3; + uint16_t : 5; + }; + } bits = {backup[offset+0]}; + + buffer[start+0] = T$ldr_rd_$pc_im_4$(bits.rd, T$Label(start+0, end-2) / 4); + buffer[start+1] = T$ldr_rd_$rn_im_4$(bits.rd, bits.rd, 0); + + // XXX: this code "works", but is "wrong": the mechanism is more complex than this + *--trailer = ((static_cast((uintptr_t)area + offset) + 4) & ~0x2) + bits.immediate * 4; + + start += 2; + end -= 2; + } else if (T$pcrel$b(backup[offset])) { + union { + uint16_t value; + + struct { + uint16_t imm8 : 8; + uint16_t cond : 4; + uint16_t /*1101*/ : 4; + }; + } bits = {backup[offset+0]}; + + intptr_t jump(bits.imm8 << 1); + jump |= 1; + jump <<= 23; + jump >>= 23; + + buffer[start+0] = T$b$_$im(bits.cond, (end-6 - (start+0)) * 2 - 4); + + *--trailer = static_cast((uintptr_t)area + offset) + 4 + jump; + *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + *--trailer = T$nop << 16 | T$bx(A$pc); + + start += 1; + end -= 6; + } else if (T2$pcrel$b(backup + offset)) { + union { + uint16_t value; + + struct { + uint16_t imm6 : 6; + uint16_t cond : 4; + uint16_t s : 1; + uint16_t : 5; + }; + } bits = {backup[offset+0]}; + + union { + uint16_t value; + + struct { + uint16_t imm11 : 11; + uint16_t j2 : 1; + uint16_t a : 1; + uint16_t j1 : 1; + uint16_t : 2; + }; + } exts = {backup[offset+1]}; + + intptr_t jump(1); + jump |= exts.imm11 << 1; + jump |= bits.imm6 << 12; + + if (exts.a) { + jump |= bits.s << 24; + jump |= (~(bits.s ^ exts.j1) & 0x1) << 23; + jump |= (~(bits.s ^ exts.j2) & 0x1) << 22; + jump |= bits.cond << 18; + jump <<= 7; + jump >>= 7; + } else { + jump |= bits.s << 20; + jump |= exts.j2 << 19; + jump |= exts.j1 << 18; + jump <<= 11; + jump >>= 11; + } + + buffer[start+0] = T$b$_$im(exts.a ? A$al : bits.cond, (end-6 - (start+0)) * 2 - 4); + + *--trailer = static_cast((uintptr_t)area + offset) + 4 + jump; + *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + *--trailer = T$nop << 16 | T$bx(A$pc); + + ++offset; + start += 1; + end -= 6; + } else if (T$pcrel$bl(backup + offset)) { + union { + uint16_t value; + + struct { + uint16_t immediate : 10; + uint16_t s : 1; + uint16_t : 5; + }; + } bits = {backup[offset+0]}; + + union { + uint16_t value; + + struct { + uint16_t immediate : 11; + uint16_t j2 : 1; + uint16_t x : 1; + uint16_t j1 : 1; + uint16_t : 2; + }; + } exts = {backup[offset+1]}; + + int32_t jump(0); + jump |= bits.s << 24; + jump |= (~(bits.s ^ exts.j1) & 0x1) << 23; + jump |= (~(bits.s ^ exts.j2) & 0x1) << 22; + jump |= bits.immediate << 12; + jump |= exts.immediate << 1; + jump |= exts.x; + jump <<= 7; + jump >>= 7; + + buffer[start+0] = T$push_r(1 << A$r7); + buffer[start+1] = T$ldr_rd_$pc_im_4$(A$r7, ((end-2 - (start+1)) * 2 - 4 + 2) / 4); + buffer[start+2] = T$mov_rd_rm(A$lr, A$r7); + buffer[start+3] = T$pop_r(1 << A$r7); + buffer[start+4] = T$blx(A$lr); + + *--trailer = static_cast((uintptr_t)area + offset) + 4 + jump; + + ++offset; + start += 5; + end -= 2; + } else if (T$pcrel$cbz(backup[offset])) { + union { + uint16_t value; + + struct { + uint16_t rn : 3; + uint16_t immediate : 5; + uint16_t : 1; + uint16_t i : 1; + uint16_t : 1; + uint16_t op : 1; + uint16_t : 4; + }; + } bits = {backup[offset+0]}; + + intptr_t jump(1); + jump |= bits.i << 6; + jump |= bits.immediate << 1; + + //jump <<= 24; + //jump >>= 24; + + unsigned rn(bits.rn); + unsigned rt(rn == A$r7 ? A$r6 : A$r7); + + buffer[start+0] = T$push_r(1 << rt); + buffer[start+1] = T1$mrs_rd_apsr(rt); + buffer[start+2] = T2$mrs_rd_apsr(rt); + buffer[start+3] = T$cbz$_rn_$im(bits.op, rn, (end-10 - (start+3)) * 2 - 4); + buffer[start+4] = T1$msr_apsr_nzcvqg_rn(rt); + buffer[start+5] = T2$msr_apsr_nzcvqg_rn(rt); + buffer[start+6] = T$pop_r(1 << rt); + + *--trailer = static_cast((uintptr_t)area + offset) + 4 + jump; + *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + *--trailer = T$nop << 16 | T$bx(A$pc); + *--trailer = T$nop << 16 | T$pop_r(1 << rt); + *--trailer = T$msr_apsr_nzcvqg_rn(rt); + +#if 0 + if ((start & 0x1) == 0) + buffer[start++] = T$nop; + buffer[start++] = T$bx(A$pc); + buffer[start++] = T$nop; + + uint32_t *arm(reinterpret_cast(buffer + start)); + arm[0] = A$add(A$lr, A$pc, 1); + arm[1] = A$ldr_rd_$rn_im$(A$pc, A$pc, (trailer - arm) * sizeof(uint32_t) - 8); +#endif + + start += 7; + end -= 10; + } else if (T$pcrel$ldrw(backup[offset])) { + union { + uint16_t value; + + struct { + uint16_t : 7; + uint16_t u : 1; + uint16_t : 8; + }; + } bits = {backup[offset+0]}; + + union { + uint16_t value; + + struct { + uint16_t immediate : 12; + uint16_t rt : 4; + }; + } exts = {backup[offset+1]}; + + buffer[start+0] = T1$ldr_rt_$rn_im$(exts.rt, A$pc, T$Label(start+0, end-2)); + buffer[start+1] = T2$ldr_rt_$rn_im$(exts.rt, A$pc, T$Label(start+0, end-2)); + + buffer[start+2] = T1$ldr_rt_$rn_im$(exts.rt, exts.rt, 0); + buffer[start+3] = T2$ldr_rt_$rn_im$(exts.rt, exts.rt, 0); + + // XXX: this code "works", but is "wrong": the mechanism is more complex than this + *--trailer = ((static_cast((uintptr_t)area + offset) + 4) & ~0x2) + (bits.u == 0 ? -exts.immediate : exts.immediate); + + ++offset; + start += 4; + end -= 2; + } else if (T$pcrel$add(backup[offset])) { + union { + uint16_t value; + + struct { + uint16_t rd : 3; + uint16_t rm : 3; + uint16_t h2 : 1; + uint16_t h1 : 1; + uint16_t : 8; + }; + } bits = {backup[offset+0]}; + + if (bits.h1) { + MSLog(MSLogLevelError, "MS:Error:pcrel(%u):add (rd > r7)", offset); + goto fail; + } + + unsigned rt(bits.rd == A$r7 ? A$r6 : A$r7); + + buffer[start+0] = T$push_r(1 << rt); + buffer[start+1] = T$mov_rd_rm(rt, (bits.h1 << 3) | bits.rd); + buffer[start+2] = T$ldr_rd_$pc_im_4$(bits.rd, T$Label(start+2, end-2) / 4); + buffer[start+3] = T$add_rd_rm((bits.h1 << 3) | bits.rd, rt); + buffer[start+4] = T$pop_r(1 << rt); + *--trailer = static_cast((uintptr_t)area + offset) + 4; + + start += 5; + end -= 2; + } else if (T$32bit$i(backup[offset])) { + buffer[start++] = backup[offset]; + buffer[start++] = backup[++offset]; + } else { + buffer[start++] = backup[offset]; + } + } + + buffer[start++] = T$bx(A$pc); + buffer[start++] = T$nop; + + uint32_t *transfer = reinterpret_cast(buffer + start); + transfer[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + transfer[1] = static_cast((uintptr_t)area + used / sizeof(uint16_t)) + 1; + + if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) { + MSLog(MSLogLevelError, "MS:Error:mprotect():%d", errno); + return; + } + + *result = reinterpret_cast(buffer + pad) + 1; + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", *result); + MSLogHexEx(buffer, length, 2, name); + } + + } + + { + SubstrateHookMemory code(process, area, used); + + if (align != 0) + area[0] = T$nop; + + thumb[0] = T$bx(A$pc); + thumb[1] = T$nop; + + arm[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8); + arm[1] = static_cast((uintptr_t)replace); + + for (unsigned offset(0); offset != blank; ++offset) + trail[offset] = T$nop; + } + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHexEx(area, used + sizeof(uint16_t), 2, name); + } +} + diff --git a/whale/src/MSHook/Thumb.h b/whale/src/MSHook/Thumb.h new file mode 100644 index 0000000..50382c8 --- /dev/null +++ b/whale/src/MSHook/Thumb.h @@ -0,0 +1,113 @@ +/* + * Thumb.h + * + * Created on: 2016��2��22�� + * Author: peng + */ + +#ifndef THUMB_H_ +#define THUMB_H_ + +#include "Debug.h" +#include "Log.h" +#include "PosixMemory.h" +#include +#include +#include + +static uint16_t abs_wrapper_16(uint16_t i); + +#define T$Label(l, r) \ + (((r) - (l)) * 2 - 4 + ((l) % 2 == 0 ? 0 : 2)) +#define T$pop_$r0$ 0xbc01 // pop {r0} +#define T$b(im) /* b im */ \ + (0xde00 | (im & 0xff)) +#define T$blx(rm) /* blx rm */ \ + (0x4780 | (rm << 3)) +#define T$bx(rm) /* bx rm */ \ + (0x4700 | (rm << 3)) +#define T$nop /* nop */ \ + (0x46c0) +#define T$add_rd_rm(rd, rm) /* add rd, rm */ \ + (0x4400 | (((rd) & 0x8) >> 3 << 7) | (((rm) & 0x8) >> 3 << 6) | (((rm) & 0x7) << 3) | ((rd) & 0x7)) +#define T$push_r(r) /* push r... */ \ + (0xb400 | (((r) & (1 << A$lr)) >> A$lr << 8) | ((r) & 0xff)) +#define T$pop_r(r) /* pop r... */ \ + (0xbc00 | (((r) & (1 << A$pc)) >> A$pc << 8) | ((r) & 0xff)) +#define T$mov_rd_rm(rd, rm) /* mov rd, rm */ \ + (0x4600 | (((rd) & 0x8) >> 3 << 7) | (((rm) & 0x8) >> 3 << 6) | (((rm) & 0x7) << 3) | ((rd) & 0x7)) +#define T$ldr_rd_$rn_im_4$(rd, rn, im) /* ldr rd, [rn, #im * 4] */ \ + (0x6800 | (((im) & 0x1f) << 6) | ((rn) << 3) | (rd)) +#define T$ldr_rd_$pc_im_4$(rd, im) /* ldr rd, [PC, #im * 4] */ \ + (0x4800 | ((rd) << 8) | ((im) & 0xff)) +#define T$cmp_rn_$im(rn, im) /* cmp rn, #im */ \ + (0x2000 | ((rn) << 8) | ((im) & 0xff)) +#define T$it$_cd(cd, ms) /* it, cd */ \ + (0xbf00 | ((cd) << 4) | (ms)) +#define T$cbz$_rn_$im(op,rn,im) /* cbz rn, #im */ \ + (0xb100 | ((op) << 11) | (((im) & 0x40) >> 6 << 9) | (((im) & 0x3e) >> 1 << 3) | (rn)) +#define T$b$_$im(cond,im) /* b #im */ \ + (cond == A$al ? 0xe000 | (((im) >> 1) & 0x7ff) : 0xd000 | ((cond) << 8) | (((im) >> 1) & 0xff)) +#define T1$ldr_rt_$rn_im$(rt, rn, im) /* ldr rt, [rn, #im] */ \ + (0xf850 | ((im < 0 ? 0 : 1) << 7) | (rn)) +#define T2$ldr_rt_$rn_im$(rt, rn, im) /* ldr rt, [rn, #im] */ \ + (((rt) << 12) | abs_wrapper_16(im)) +#define T1$mrs_rd_apsr(rd) /* mrs rd, apsr */ \ + (0xf3ef) +#define T2$mrs_rd_apsr(rd) /* mrs rd, apsr */ \ + (0x8000 | ((rd) << 8)) +#define T1$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \ + (0xf380 | (rn)) +#define T2$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \ + (0x8c00) +#define T$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \ + (T2$msr_apsr_nzcvqg_rn(rn) << 16 | T1$msr_apsr_nzcvqg_rn(rn)) +#define A$ldr_rd_$rn_im$(rd, rn, im) /* ldr rd, [rn, #im] */ \ + (0xe5100000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | abs_wrapper_16(im)) + +static inline bool T$32bit$i(uint16_t ic) { + return ((ic & 0xe000) == 0xe000 && (ic & 0x1800) != 0x0000); +} + +static inline bool T$pcrel$cbz(uint16_t ic) { + return (ic & 0xf500) == 0xb100; +} + +static inline bool T$pcrel$b(uint16_t ic) { + return (ic & 0xf000) == 0xd000 && (ic & 0x0e00) != 0x0e00; +} + +static inline bool T2$pcrel$b(uint16_t *ic) { + return (ic[0] & 0xf800) == 0xf000 && ((ic[1] & 0xd000) == 0x9000 || (ic[1] & 0xd000) == 0x8000 && (ic[0] & 0x0380) != 0x0380); +} + +static inline bool T$pcrel$bl(uint16_t *ic) { + return (ic[0] & 0xf800) == 0xf000 && ((ic[1] & 0xd000) == 0xd000 || (ic[1] & 0xd001) == 0xc000); +} + +static inline bool T$pcrel$ldr(uint16_t ic) { + return (ic & 0xf800) == 0x4800; +} + +static inline bool T$pcrel$add(uint16_t ic) { + return (ic & 0xff78) == 0x4478; +} + +static inline bool T$pcrel$ldrw(uint16_t ic) { + return (ic & 0xff7f) == 0xf85f; +} + +static size_t MSGetInstructionWidthThumb(void *start) { + uint16_t *thumb(reinterpret_cast(start)); + return T$32bit$i(thumb[0]) ? 4 : 2; +} + +static size_t MSGetInstructionWidthARM(void *start) { + return 4; +} + +namespace Thumb{ + static size_t MSGetInstructionWidth(void *start); + extern "C" void SubstrateHookFunctionThumb(SubstrateProcessRef process, void *symbol, void *replace, void **result); +} +#endif /* THUMB_H_ */ diff --git a/whale/src/MSHook/hde64.h b/whale/src/MSHook/hde64.h new file mode 100644 index 0000000..2fcc4cb --- /dev/null +++ b/whale/src/MSHook/hde64.h @@ -0,0 +1,112 @@ +/* + * Hacker Disassembler Engine 64 + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + * hde64.h: C/C++ header file + * + */ + +#ifndef _HDE64_H_ +#define _HDE64_H_ + +/* stdint.h - C99 standard header + * http://en.wikipedia.org/wiki/stdint.h + * + * if your compiler doesn't contain "stdint.h" header (for + * example, Microsoft Visual C++), you can download file: + * http://www.azillionmonkeys.com/qed/pstdint.h + * and change next line to: + * #include "pstdint.h" + */ +#include + +#define F_MODRM 0x00000001 +#define F_SIB 0x00000002 +#define F_IMM8 0x00000004 +#define F_IMM16 0x00000008 +#define F_IMM32 0x00000010 +#define F_IMM64 0x00000020 +#define F_DISP8 0x00000040 +#define F_DISP16 0x00000080 +#define F_DISP32 0x00000100 +#define F_RELATIVE 0x00000200 +#define F_ERROR 0x00001000 +#define F_ERROR_OPCODE 0x00002000 +#define F_ERROR_LENGTH 0x00004000 +#define F_ERROR_LOCK 0x00008000 +#define F_ERROR_OPERAND 0x00010000 +#define F_PREFIX_REPNZ 0x01000000 +#define F_PREFIX_REPX 0x02000000 +#define F_PREFIX_REP 0x03000000 +#define F_PREFIX_66 0x04000000 +#define F_PREFIX_67 0x08000000 +#define F_PREFIX_LOCK 0x10000000 +#define F_PREFIX_SEG 0x20000000 +#define F_PREFIX_REX 0x40000000 +#define F_PREFIX_ANY 0x7f000000 + +#define PREFIX_SEGMENT_CS 0x2e +#define PREFIX_SEGMENT_SS 0x36 +#define PREFIX_SEGMENT_DS 0x3e +#define PREFIX_SEGMENT_ES 0x26 +#define PREFIX_SEGMENT_FS 0x64 +#define PREFIX_SEGMENT_GS 0x65 +#define PREFIX_LOCK 0xf0 +#define PREFIX_REPNZ 0xf2 +#define PREFIX_REPX 0xf3 +#define PREFIX_OPERAND_SIZE 0x66 +#define PREFIX_ADDRESS_SIZE 0x67 + +#pragma pack(push,1) + +typedef struct { + uint8_t len; + uint8_t p_rep; + uint8_t p_lock; + uint8_t p_seg; + uint8_t p_66; + uint8_t p_67; + uint8_t rex; + uint8_t rex_w; + uint8_t rex_r; + uint8_t rex_x; + uint8_t rex_b; + uint8_t opcode; + uint8_t opcode2; + uint8_t modrm; + uint8_t modrm_mod; + uint8_t modrm_reg; + uint8_t modrm_rm; + uint8_t sib; + uint8_t sib_scale; + uint8_t sib_index; + uint8_t sib_base; + union { + uint8_t imm8; + uint16_t imm16; + uint32_t imm32; + uint64_t imm64; + } imm; + union { + uint8_t disp8; + uint16_t disp16; + uint32_t disp32; + } disp; + uint32_t flags; +} hde64s; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + +/* __cdecl */ +unsigned int hde64_disasm(const void *code, hde64s *hs); + +#ifdef __cplusplus +} +#endif + +#endif /* _HDE64_H_ */ diff --git a/whale/src/MSHook/hook.cpp b/whale/src/MSHook/hook.cpp new file mode 100644 index 0000000..d8b46d0 --- /dev/null +++ b/whale/src/MSHook/hook.cpp @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include "util.h" +#include "Log.h" + +#include "Hooker.h" +#include "hook.h" + +int elfHook(const char *soname, const char *symbol, void *replace_func, + void **old_func) { + int ret = -1; + void *addr = NULL; + if (find_name(getpid(), symbol, soname, (unsigned long *) &addr) < 0) { + LOGW("Not find: %s\n", symbol); + return -1; + } + Cydia::MSHookFunction(addr, replace_func, old_func); + ret = 0; + return ret; +} + +int elfHookDirect(unsigned int addr, void *replace_func, void **old_func) { + if (addr == 0) { + LOGW("hook direct addr:%p error!", (void* )addr); + return -1; + } + Cydia::MSHookFunction((void*) addr, replace_func, old_func); + return 0; +} diff --git a/whale/src/MSHook/hook.h b/whale/src/MSHook/hook.h new file mode 100644 index 0000000..6ab1642 --- /dev/null +++ b/whale/src/MSHook/hook.h @@ -0,0 +1,17 @@ +#ifndef LIBHOOK_H_ +#define LIBHOOK_H_ + +#define HOOK_FAILED -1 +#define HOOK_SUCCESS 0 + +#ifdef __cplusplus +extern "C" { +#endif + +extern int elfHook(const char *soname, const char *symbol, void *replace_func, void **old_func); +extern int elfHookDirect(unsigned int addr, void *replace_func,void **old_func); + +#ifdef __cplusplus +} +#endif +#endif /* LIBHOOK_HOOK2_H_ */ diff --git a/whale/src/MSHook/util.cpp b/whale/src/MSHook/util.cpp new file mode 100644 index 0000000..83a2450 --- /dev/null +++ b/whale/src/MSHook/util.cpp @@ -0,0 +1,436 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Log.h" + +/* memory map for libraries */ +#define MAX_NAME_LEN 256 +#define MEMORY_ONLY "[memory]" +struct mm { + char name[MAX_NAME_LEN]; + unsigned long start, end; +}; + +typedef struct symtab *symtab_t; +struct symlist { + Elf32_Sym *sym; /* symbols */ + char *str; /* symbol strings */ + unsigned num; /* number of symbols */ +}; +struct symtab { + struct symlist *st; /* "static" symbols */ + struct symlist *dyn; /* dynamic symbols */ +}; + +static void* xmalloc(size_t size) { + void *p; + p = malloc(size); + if (!p) { + printf("Out of memory\n"); + exit(1); + } + return p; +} + +static int my_pread(int fd, void *buf, size_t count, off_t offset) { + lseek(fd, offset, SEEK_SET); + return read(fd, buf, count); +} + +static struct symlist* get_syms(int fd, Elf32_Shdr *symh, Elf32_Shdr *strh) { + struct symlist *sl, *ret; + int rv; + + ret = NULL; + sl = (struct symlist *) xmalloc(sizeof(struct symlist)); + sl->str = NULL; + sl->sym = NULL; + + /* sanity */ + if (symh->sh_size % sizeof(Elf32_Sym)) { + //printf("elf_error\n"); + goto out; + } + + /* symbol table */ + sl->num = symh->sh_size / sizeof(Elf32_Sym); + sl->sym = (Elf32_Sym *) xmalloc(symh->sh_size); + rv = my_pread(fd, sl->sym, symh->sh_size, symh->sh_offset); + if (0 > rv) { + //perror("read"); + goto out; + } + if (rv != symh->sh_size) { + //printf("elf error\n"); + goto out; + } + + /* string table */ + sl->str = (char *) xmalloc(strh->sh_size); + rv = my_pread(fd, sl->str, strh->sh_size, strh->sh_offset); + if (0 > rv) { + //perror("read"); + goto out; + } + if (rv != strh->sh_size) { + //printf("elf error"); + goto out; + } + + ret = sl; + out: return ret; +} + +static int do_load(int fd, symtab_t symtab) { + int rv; + size_t size; + Elf32_Ehdr ehdr; + Elf32_Shdr *shdr = NULL, *p; + Elf32_Shdr *dynsymh, *dynstrh; + Elf32_Shdr *symh, *strh; + char *shstrtab = NULL; + int i; + int ret = -1; + + /* elf header */ + rv = read(fd, &ehdr, sizeof(ehdr)); + if (0 > rv) { + LOGD("read\n"); + goto out; + } + if (rv != sizeof(ehdr)) { + LOGD("elf error 1\n"); + goto out; + } + if (strncmp((const char *) ELFMAG, (const char *) ehdr.e_ident, SELFMAG)) { /* sanity */ + LOGD("not an elf\n"); + goto out; + } + if (sizeof(Elf32_Shdr) != ehdr.e_shentsize) { /* sanity */ + LOGD("elf error 2\n"); + goto out; + } + + /* section header table */ + size = ehdr.e_shentsize * ehdr.e_shnum; + shdr = (Elf32_Shdr *) xmalloc(size); + rv = my_pread(fd, shdr, size, ehdr.e_shoff); + if (0 > rv) { + LOGD("read\n"); + goto out; + } + if (rv != size) { + LOGD("elf error 3 %d %d\n", rv, size); + goto out; + } + + /* section header string table */ + size = shdr[ehdr.e_shstrndx].sh_size; + shstrtab = (char *) xmalloc(size); + rv = my_pread(fd, shstrtab, size, shdr[ehdr.e_shstrndx].sh_offset); + if (0 > rv) { + LOGD("read\n"); + goto out; + } + if (rv != size) { + LOGD("elf error 4 %d %d\n", rv, size); + goto out; + } + + /* symbol table headers */ + symh = dynsymh = NULL; + strh = dynstrh = NULL; + for (i = 0, p = shdr; i < ehdr.e_shnum; i++, p++) + if (SHT_SYMTAB == p->sh_type) { + if (symh) { + LOGD("too many symbol tables\n"); + goto out; + } + symh = p; + } else if (SHT_DYNSYM == p->sh_type) { + if (dynsymh) { + LOGD("too many symbol tables\n"); + goto out; + } + dynsymh = p; + } else if (SHT_STRTAB == p->sh_type + && !strncmp(shstrtab + p->sh_name, ".strtab", 7)) { + if (strh) { + LOGD("too many string tables\n"); + goto out; + } + strh = p; + } else if (SHT_STRTAB == p->sh_type + && !strncmp(shstrtab + p->sh_name, ".dynstr", 7)) { + if (dynstrh) { + LOGD("too many string tables\n"); + goto out; + } + dynstrh = p; + } + /* sanity checks */ + if ((!dynsymh && dynstrh) || (dynsymh && !dynstrh)) { + LOGD("bad dynamic symbol table\n"); + goto out; + } + if ((!symh && strh) || (symh && !strh)) { + LOGD("bad symbol table\n"); + goto out; + } + if (!dynsymh && !symh) { + LOGD("no symbol table\n"); + goto out; + } + + /* symbol tables */ + if (dynsymh) + symtab->dyn = get_syms(fd, dynsymh, dynstrh); + if (symh) + symtab->st = get_syms(fd, symh, strh); + ret = 0; + out: free(shstrtab); + free(shdr); + return ret; +} + +static symtab_t load_symtab(char *filename) { + int fd; + symtab_t symtab; + + symtab = (symtab_t) xmalloc(sizeof(*symtab)); + memset(symtab, 0, sizeof(*symtab)); + + fd = open(filename, O_RDONLY); + if (0 > fd) { + LOGE("%s open\n", __func__); + return NULL; + } + if (0 > do_load(fd, symtab)) { + LOGE("Error ELF parsing %s\n", filename); + free(symtab); + symtab = NULL; + } + close(fd); + return symtab; +} + +static int load_memmap(pid_t pid, struct mm *mm, int *nmmp) { + size_t buf_size = 0x40000; + char *p_buf = (char *) malloc(buf_size); // increase this if needed for larger "maps" + char name[MAX_NAME_LEN] = { 0 }; + char *p; + unsigned long start, end; + struct mm *m; + int nmm = 0; + int fd, rv; + int i; + + sprintf(p_buf, "/proc/%d/maps", pid); + fd = open(p_buf, O_RDONLY); + if (0 > fd) { + LOGE("Can't open %s for reading\n", p_buf); + free(p_buf); + return -1; + } + + /* Zero to ensure data is null terminated */ + memset(p_buf, 0, buf_size); + + p = p_buf; + while (1) { + rv = read(fd, p, buf_size - (p - p_buf)); + if (0 > rv) { + LOGE("%s read", __FUNCTION__); + free(p_buf); + return -1; + } + if (0 == rv) + break; + p += rv; + if (p - p_buf >= buf_size) { + LOGE("Too many memory mapping\n"); + free(p_buf); + return -1; + } + } + close(fd); + + p = strtok(p_buf, "\n"); + m = mm; + while (p) { + /* parse current map line */ + rv = sscanf(p, "%08lx-%08lx %*s %*s %*s %*s %s\n", &start, &end, name); + + p = strtok(NULL, "\n"); + + if (rv == 2) { + m = &mm[nmm++]; + m->start = start; + m->end = end; + memcpy(m->name, MEMORY_ONLY, sizeof(MEMORY_ONLY)); + continue; + } + + /* search backward for other mapping with same name */ + for (i = nmm - 1; i >= 0; i--) { + m = &mm[i]; + if (!strcmp(m->name, name)) + break; + } + + if (i >= 0) { + if (start < m->start) + m->start = start; + if (end > m->end) + m->end = end; + } else { + /* new entry */ + m = &mm[nmm++]; + m->start = start; + m->end = end; + memcpy(m->name, name, strlen(name)); + } + } + + *nmmp = nmm; + free(p_buf); + return 0; +} + +/* Find libc in MM, storing no more than LEN-1 chars of + its name in NAME and set START to its starting + address. If libc cannot be found return -1 and + leave NAME and START untouched. Otherwise return 0 + and null-terminated NAME. */ +static int find_libname(char *libn, char *name, int len, unsigned long *start, + struct mm *mm, int nmm) { + int i; + struct mm *m; + char *p; + for (i = 0, m = mm; i < nmm; i++, m++) { + if (!strcmp(m->name, MEMORY_ONLY)) + continue; + p = strrchr(m->name, '/'); + if (!p) + continue; + p++; + if (strncmp(libn, p, strlen(libn))) + continue; + p += strlen(libn); + + /* here comes our crude test -> 'libc.so' or 'libc-[0-9]' */ + if (!strncmp("so", p, 2) || 1) // || (p[0] == '-' && isdigit(p[1]))) + break; + } + if (i >= nmm) + /* not found */ + return -1; + + *start = m->start; + strncpy(name, m->name, len); + if (strlen(m->name) >= len) + name[len - 1] = '\0'; + + mprotect((void*) m->start, m->end - m->start, + PROT_READ | PROT_WRITE | PROT_EXEC); + return 0; +} + +static int lookup2(struct symlist *sl, unsigned char type, char *name, + unsigned long *val) { + Elf32_Sym *p; + int len; + int i; + + len = strlen(name); + for (i = 0, p = sl->sym; i < sl->num; i++, p++) { + //LOGD("name: %s %x\n", sl->str+p->st_name, p->st_value) + if (!strncmp(sl->str + p->st_name, name, len) + && *(sl->str + p->st_name + len) == 0 + && ELF32_ST_TYPE(p->st_info) == type) { + //if (p->st_value != 0) { + *val = p->st_value; + return 0; + //} + } + } + return -1; +} + +static int lookup_sym(symtab_t s, unsigned char type, char *name, + unsigned long *val) { + if (s->dyn && !lookup2(s->dyn, type, name, val)) + return 0; + if (s->st && !lookup2(s->st, type, name, val)) + return 0; + return -1; +} + +static int lookup_func_sym(symtab_t s, char *name, unsigned long *val) { + return lookup_sym(s, STT_FUNC, name, val); +} + +int find_name(pid_t pid, const char *name, const char *libn, + unsigned long *addr) { + struct mm mm[1000] = { 0 }; + unsigned long libcaddr; + int nmm; + char libc[1024] = { 0 }; + symtab_t s; + + if (0 > load_memmap(pid, mm, &nmm)) { + LOGD("cannot read memory map\n"); + return -1; + } + if (0 + > find_libname((char *) libn, (char *) libc, sizeof(libc), + &libcaddr, mm, nmm)) { + LOGD("cannot find lib: %s\n", libn); + return -1; + } + //LOGD("lib: >%s<\n", libc) + s = load_symtab(libc); + if (!s) { + LOGD("cannot read symbol table\n"); + return -1; + } + if (0 > lookup_func_sym(s, (char *) name, addr)) { + LOGD("cannot find function: %s\n", name); + return -1; + } + *addr += libcaddr; + return 0; +} + +int find_libbase(pid_t pid, char *libn, unsigned long *addr) { + struct mm mm[1000] = { 0 }; + unsigned long libcaddr; + int nmm; + char libc[1024] = { 0 }; + symtab_t s; + + if (0 > load_memmap(pid, mm, &nmm)) { + LOGD("cannot read memory map\n"); + return -1; + } + if (0 > find_libname(libn, libc, sizeof(libc), &libcaddr, mm, nmm)) { + LOGD("cannot find lib\n"); + return -1; + } + *addr = libcaddr; + return 0; +} + diff --git a/whale/src/MSHook/util.h b/whale/src/MSHook/util.h new file mode 100644 index 0000000..16e5aaa --- /dev/null +++ b/whale/src/MSHook/util.h @@ -0,0 +1,5 @@ +#ifndef HOOK_UTIL_H_ +#define HOOK_UTIL_H_ +extern int find_name(pid_t pid, const char *name,const char *libn, unsigned long *addr); +extern int find_libbase(pid_t pid, char *libn, unsigned long *addr); +#endif diff --git a/whale/src/MSHook/x86.cpp b/whale/src/MSHook/x86.cpp new file mode 100644 index 0000000..24c6e6b --- /dev/null +++ b/whale/src/MSHook/x86.cpp @@ -0,0 +1,220 @@ +#include "x86.h" +#include "x86_64.h" + +static size_t MSGetInstructionWidthIntel(void *start) { + hde64s decode; + return hde64_disasm(start, &decode); +} + +void x86::SubstrateHookFunctionx86(SubstrateProcessRef process, void *symbol, void *replace, void **result){ + if (MSDebug) + MSLog(MSLogLevelNotice, "SubstrateHookFunctionx86(process:%p, symbol:%p, replace:%p, result:%p)", process, symbol, replace, result); + if (symbol == NULL) + return; + + uintptr_t source(reinterpret_cast(symbol)); + uintptr_t target(reinterpret_cast(replace)); + + uint8_t *area(reinterpret_cast(symbol)); + + size_t required(MSSizeOfJump(target, source)); + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHex(area, 32, name); + } + + size_t used(0); + while (used < required) { + size_t width(MSGetInstructionWidthIntel(area + used)); + if (width == 0) { + MSLog(MSLogLevelError, "MS:Error:MSGetInstructionWidthIntel(%p) == 0", area + used); + return; + } + + used += width; + } + + size_t blank(used - required); + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHex(area, used + sizeof(uint16_t), name); + } + + uint8_t backup[used]; + memcpy(backup, area, used); + + if (result != NULL) { + + if (backup[0] == 0xe9) { + *result = reinterpret_cast(source + 5 + *reinterpret_cast(backup + 1)); + return; + } + + if (!ia32 && backup[0] == 0xff && backup[1] == 0x25) { + *result = *reinterpret_cast(source + 6 + *reinterpret_cast(backup + 2)); + return; + } + + size_t length(used + MSSizeOfJump(source + used)); + + for (size_t offset(0), width; offset != used; offset += width) { + hde64s decode; + hde64_disasm(backup + offset, &decode); + width = decode.len; + //_assert(width != 0 && offset + width <= used); + +#ifdef __LP64__ + if ((decode.modrm & 0xc7) == 0x05) { + if (decode.opcode == 0x8b) { + void *destiny(area + offset + width + int32_t(decode.disp.disp32)); + uint8_t reg(decode.rex_r << 3 | decode.modrm_reg); + length -= decode.len; + length += MSSizeOfPushPointer(destiny); + length += MSSizeOfPop(reg); + length += MSSizeOfMove64(); + } else { + MSLog(MSLogLevelError, "MS:Error: Unknown RIP-Relative (%.2x %.2x)", decode.opcode, decode.opcode2); + continue; + } + } else +#endif + + if (backup[offset] == 0xe8) { + int32_t relative(*reinterpret_cast(backup + offset + 1)); + void *destiny(area + offset + decode.len + relative); + + if (relative == 0) { + length -= decode.len; + length += MSSizeOfPushPointer(destiny); + } else { + length += MSSizeOfSkip(); + length += MSSizeOfJump(destiny); + } + } else if (backup[offset] == 0xeb) { + length -= decode.len; + length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + } else if (backup[offset] == 0xe9) { + length -= decode.len; + length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + } else if ( + backup[offset] == 0xe3 || + (backup[offset] & 0xf0) == 0x70 + // XXX: opcode2 & 0xf0 is 0x80? + ) { + length += decode.len; + length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + } + } + + uint8_t *buffer(reinterpret_cast(mmap( + NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 + ))); + + if (buffer == MAP_FAILED) { + MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno); + *result = NULL; + return; + } + + if (false) fail: { + munmap(buffer, length); + *result = NULL; + return; + } + + { + uint8_t *current(buffer); + + for (size_t offset(0), width; offset != used; offset += width) { + hde64s decode; + hde64_disasm(backup + offset, &decode); + width = decode.len; + //_assert(width != 0 && offset + width <= used); + +#ifdef __LP64__ + if ((decode.modrm & 0xc7) == 0x05) { + if (decode.opcode == 0x8b) { + void *destiny(area + offset + width + int32_t(decode.disp.disp32)); + uint8_t reg(decode.rex_r << 3 | decode.modrm_reg); + MSPushPointer(current, destiny); + MSWritePop(current, reg); + MSWriteMove64(current, reg, reg); + } else { + MSLog(MSLogLevelError, "MS:Error: Unknown RIP-Relative (%.2x %.2x)", decode.opcode, decode.opcode2); + goto copy; + } + } else +#endif + + if (backup[offset] == 0xe8) { + int32_t relative(*reinterpret_cast(backup + offset + 1)); + if (relative == 0) + MSPushPointer(current, area + offset + decode.len); + else { + MSWrite(current, 0xe8); + MSWrite(current, MSSizeOfSkip()); + void *destiny(area + offset + decode.len + relative); + MSWriteSkip(current, MSSizeOfJump(destiny, current + MSSizeOfSkip())); + MSWriteJump(current, destiny); + } + } else if (backup[offset] == 0xeb) + MSWriteJump(current, area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + else if (backup[offset] == 0xe9) + MSWriteJump(current, area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + else if ( + backup[offset] == 0xe3 || + (backup[offset] & 0xf0) == 0x70 + ) { + MSWrite(current, backup[offset]); + MSWrite(current, 2); + MSWrite(current, 0xeb); + void *destiny(area + offset + decode.len + *reinterpret_cast(backup + offset + 1)); + MSWrite(current, MSSizeOfJump(destiny, current + 1)); + MSWriteJump(current, destiny); + } else +#ifdef __LP64__ + copy: +#endif + { + MSWrite(current, backup + offset, width); + } + } + + MSWriteJump(current, area + used); + } + + if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) { + MSLog(MSLogLevelError, "MS:Error:mprotect():%d", errno); + goto fail; + } + + *result = buffer; + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", *result); + MSLogHex(buffer, length, name); + } + + } + + { + SubstrateHookMemory code(process, area, used); + + uint8_t *current(area); + MSWriteJump(current, target); + for (unsigned offset(0); offset != blank; ++offset) + MSWrite(current, 0x90); + } + + if (MSDebug) { + char name[16]; + sprintf(name, "%p", area); + MSLogHex(area, used + sizeof(uint16_t), name); + } +} + diff --git a/whale/src/MSHook/x86.h b/whale/src/MSHook/x86.h new file mode 100644 index 0000000..8a06521 --- /dev/null +++ b/whale/src/MSHook/x86.h @@ -0,0 +1,209 @@ +/* + * x86.h + * + * Created on: 2016222 + * Author: peng + */ + +#ifndef X86_H_ +#define X86_H_ + +#include +#include +#include +#include +#include +#include "CydiaSubstrate.h" +#include "PosixMemory.h" +#include "Log.h" +#include "Debug.h" + +template +_disused static _finline void MSWrite(uint8_t *&buffer, Type_ value) { + *reinterpret_cast(buffer) = value; + buffer += sizeof(Type_); +} + +_disused static _finline void MSWrite(uint8_t *&buffer, uint8_t *data, size_t size) { + memcpy(buffer, data, size); + buffer += size; +} + +#ifdef __LP64__ +static const bool ia32 = false; +#else +static const bool ia32 = true; +#endif + +enum I$r { + I$rax, I$rcx, I$rdx, I$rbx, + I$rsp, I$rbp, I$rsi, I$rdi, + I$r8, I$r9, I$r10, I$r11, + I$r12, I$r13, I$r14, I$r15, +}; + +_disused static bool MSIs32BitOffset(uintptr_t target, uintptr_t source) { + intptr_t offset(target - source); + return int32_t(offset) == offset; +} + +_disused static size_t MSSizeOfSkip() { + return 5; +} + +_disused static size_t MSSizeOfPushPointer(uintptr_t target) { + return uint64_t(target) >> 32 == 0 ? 5 : 13; +} + +_disused static size_t MSSizeOfPushPointer(void *target) { + return MSSizeOfPushPointer(reinterpret_cast(target)); +} + +_disused static size_t MSSizeOfJump(bool blind, uintptr_t target, uintptr_t source = 0) { + if (ia32 || !blind && MSIs32BitOffset(target, source + 5)) + return MSSizeOfSkip(); + else + return MSSizeOfPushPointer(target) + 1; +} + +_disused static size_t MSSizeOfJump(uintptr_t target, uintptr_t source) { + return MSSizeOfJump(false, target, source); +} + +_disused static size_t MSSizeOfJump(uintptr_t target) { + return MSSizeOfJump(true, target); +} + +_disused static size_t MSSizeOfJump(void *target, void *source) { + return MSSizeOfJump(reinterpret_cast(target), reinterpret_cast(source)); +} + +_disused static size_t MSSizeOfJump(void *target) { + return MSSizeOfJump(reinterpret_cast(target)); +} + +_disused static void MSWriteSkip(uint8_t *¤t, ssize_t size) { + MSWrite(current, 0xe9); + MSWrite(current, size); +} + +_disused static void MSPushPointer(uint8_t *¤t, uintptr_t target) { + MSWrite(current, 0x68); + MSWrite(current, target); + + if (uint32_t high = uint64_t(target) >> 32) { + MSWrite(current, 0xc7); + MSWrite(current, 0x44); + MSWrite(current, 0x24); + MSWrite(current, 0x04); + MSWrite(current, high); + } +} + +_disused static void MSPushPointer(uint8_t *¤t, void *target) { + return MSPushPointer(current, reinterpret_cast(target)); +} + +_disused static void MSWriteCall(uint8_t *¤t, I$r target) { + if (target >> 3 != 0) + MSWrite(current, 0x40 | (target & 0x08) >> 3); + MSWrite(current, 0xff); + MSWrite(current, 0xd0 | target & 0x07); +} + +_disused static void MSWriteCall(uint8_t *¤t, uintptr_t target) { + uintptr_t source(reinterpret_cast(current)); + + if (ia32 || MSIs32BitOffset(target, source + 5)) { + MSWrite(current, 0xe8); + MSWrite(current, target - (source + 5)); + } else { + MSPushPointer(current, target); + + MSWrite(current, 0x83); + MSWrite(current, 0xc4); + MSWrite(current, 0x08); + + MSWrite(current, 0x67); + MSWrite(current, 0xff); + MSWrite(current, 0x54); + MSWrite(current, 0x24); + MSWrite(current, 0xf8); + } +} + +template +_disused static void MSWriteCall(uint8_t *¤t, Type_ *target) { + return MSWriteCall(current, reinterpret_cast(target)); +} + +_disused static void MSWriteJump(uint8_t *¤t, uintptr_t target) { + uintptr_t source(reinterpret_cast(current)); + + if (ia32 || MSIs32BitOffset(target, source + 5)) + MSWriteSkip(current, target - (source + 5)); + else { + MSPushPointer(current, target); + MSWrite(current, 0xc3); + } +} + +_disused static void MSWriteJump(uint8_t *¤t, void *target) { + return MSWriteJump(current, reinterpret_cast(target)); +} + +_disused static void MSWriteJump(uint8_t *¤t, I$r target) { + if (target >> 3 != 0) + MSWrite(current, 0x40 | (target & 0x08) >> 3); + MSWrite(current, 0xff); + MSWrite(current, 0xe0 | target & 0x07); +} + +_disused static void MSWritePop(uint8_t *¤t, uint8_t target) { + if (target >> 3 != 0) + MSWrite(current, 0x40 | (target & 0x08) >> 3); + MSWrite(current, 0x58 | target & 0x07); +} + +_disused static size_t MSSizeOfPop(uint8_t target) { + return target >> 3 != 0 ? 2 : 1; +} + +_disused static void MSWritePush(uint8_t *¤t, I$r target) { + if (target >> 3 != 0) + MSWrite(current, 0x40 | (target & 0x08) >> 3); + MSWrite(current, 0x50 | target & 0x07); +} + +_disused static void MSWriteAdd(uint8_t *¤t, I$r target, uint8_t source) { + MSWrite(current, 0x83); + MSWrite(current, 0xc4 | target & 0x07); + MSWrite(current, source); +} + +_disused static void MSWriteSet64(uint8_t *¤t, I$r target, uintptr_t source) { + MSWrite(current, 0x48 | (target & 0x08) >> 3 << 2); + MSWrite(current, 0xb8 | target & 0x7); + MSWrite(current, source); +} + +template +_disused static void MSWriteSet64(uint8_t *¤t, I$r target, Type_ *source) { + return MSWriteSet64(current, target, reinterpret_cast(source)); +} + +_disused static void MSWriteMove64(uint8_t *¤t, uint8_t source, uint8_t target) { + MSWrite(current, 0x48 | (target & 0x08) >> 3 << 2 | (source & 0x08) >> 3); + MSWrite(current, 0x8b); + MSWrite(current, (target & 0x07) << 3 | source & 0x07); +} + +_disused static size_t MSSizeOfMove64() { + return 3; +} + +namespace x86{ + extern "C" void SubstrateHookFunctionx86(SubstrateProcessRef process, void *symbol, void *replace, void **result); +} + +#endif /* X86_H_ */ diff --git a/whale/src/MSHook/x86_64.cpp b/whale/src/MSHook/x86_64.cpp new file mode 100644 index 0000000..60af615 --- /dev/null +++ b/whale/src/MSHook/x86_64.cpp @@ -0,0 +1,374 @@ +#ifndef X86_64_CPP_ +#define X86_64_CPP_ + +#include +#include +#include "x86_64.h" + +unsigned char hde64_table[] = { + 0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5, + 0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1, + 0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea, + 0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0, + 0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab, + 0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92, + 0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90, + 0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b, + 0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b, + 0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc, + 0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20, + 0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff, + 0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00, + 0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01, + 0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10, + 0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00, + 0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00, + 0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, + 0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00, + 0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43, + 0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40, + 0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06, + 0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07, + 0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, + 0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10, + 0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00, + 0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb, + 0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff, + 0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09, + 0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff, + 0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08, + 0x00,0xf0,0x02,0x00 +}; + +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ +unsigned int hde64_disasm(const void *code, hde64s *hs) +{ + uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; + uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0; + uint8_t op64 = 0; + + memset(hs,0,sizeof(hde64s)); + + for (x = 16; x; x--) + switch (c = *p++) { + case 0xf3: + hs->p_rep = c; + pref |= PRE_F3; + break; + case 0xf2: + hs->p_rep = c; + pref |= PRE_F2; + break; + case 0xf0: + hs->p_lock = c; + pref |= PRE_LOCK; + break; + case 0x26: case 0x2e: case 0x36: + case 0x3e: case 0x64: case 0x65: + hs->p_seg = c; + pref |= PRE_SEG; + break; + case 0x66: + hs->p_66 = c; + pref |= PRE_66; + break; + case 0x67: + hs->p_67 = c; + pref |= PRE_67; + break; + default: + goto pref_done; + } + pref_done: + + hs->flags = (uint32_t)pref << 23; + + if (!pref) + pref |= PRE_NONE; + + if ((c & 0xf0) == 0x40) { + hs->flags |= F_PREFIX_REX; + if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8) + op64++; + hs->rex_r = (c & 7) >> 2; + hs->rex_x = (c & 3) >> 1; + hs->rex_b = c & 1; + if (((c = *p++) & 0xf0) == 0x40) { + opcode = c; + goto error_opcode; + } + } + + if ((hs->opcode = c) == 0x0f) { + hs->opcode2 = c = *p++; + ht += DELTA_OPCODES; + } else if (c >= 0xa0 && c <= 0xa3) { + op64++; + if (pref & PRE_67) + pref |= PRE_66; + else + pref &= ~PRE_66; + } + + opcode = c; + cflags = ht[ht[opcode / 4] + (opcode % 4)]; + + if (cflags == C_ERROR) { + error_opcode: + hs->flags |= F_ERROR | F_ERROR_OPCODE; + cflags = 0; + if ((opcode & -3) == 0x24) + cflags++; + } + + x = 0; + if (cflags & C_GROUP) { + uint16_t t; + t = *(uint16_t *)(ht + (cflags & 0x7f)); + cflags = (uint8_t)t; + x = (uint8_t)(t >> 8); + } + + if (hs->opcode2) { + ht = hde64_table + DELTA_PREFIXES; + if (ht[ht[opcode / 4] + (opcode % 4)] & pref) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (cflags & C_MODRM) { + hs->flags |= F_MODRM; + hs->modrm = c = *p++; + hs->modrm_mod = m_mod = c >> 6; + hs->modrm_rm = m_rm = c & 7; + hs->modrm_reg = m_reg = (c & 0x3f) >> 3; + + if (x && ((x << m_reg) & 0x80)) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + + if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { + uint8_t t = opcode - 0xd9; + if (m_mod == 3) { + ht = hde64_table + DELTA_FPU_MODRM + t*8; + t = ht[m_reg] << m_rm; + } else { + ht = hde64_table + DELTA_FPU_REG; + t = ht[t] << m_reg; + } + if (t & 0x80) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (pref & PRE_LOCK) { + if (m_mod == 3) { + hs->flags |= F_ERROR | F_ERROR_LOCK; + } else { + uint8_t *table_end, op = opcode; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_LOCK_OK; + table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; + } else { + ht = hde64_table + DELTA_OP_LOCK_OK; + table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; + op &= -2; + } + for (; ht != table_end; ht++) + if (*ht++ == op) { + if (!((*ht << m_reg) & 0x80)) + goto no_lock_error; + else + break; + } + hs->flags |= F_ERROR | F_ERROR_LOCK; + no_lock_error: + ; + } + } + + if (hs->opcode2) { + switch (opcode) { + case 0x20: case 0x22: + m_mod = 3; + if (m_reg > 4 || m_reg == 1) + goto error_operand; + else + goto no_error_operand; + case 0x21: case 0x23: + m_mod = 3; + if (m_reg == 4 || m_reg == 5) + goto error_operand; + else + goto no_error_operand; + } + } else { + switch (opcode) { + case 0x8c: + if (m_reg > 5) + goto error_operand; + else + goto no_error_operand; + case 0x8e: + if (m_reg == 1 || m_reg > 5) + goto error_operand; + else + goto no_error_operand; + } + } + + if (m_mod == 3) { + uint8_t *table_end; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_ONLY_MEM; + table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM; + } else { + ht = hde64_table + DELTA_OP_ONLY_MEM; + table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; + } + for (; ht != table_end; ht += 2) + if (*ht++ == opcode) { + if (*ht++ & pref && !((*ht << m_reg) & 0x80)) + goto error_operand; + else + break; + } + goto no_error_operand; + } else if (hs->opcode2) { + switch (opcode) { + case 0x50: case 0xd7: case 0xf7: + if (pref & (PRE_NONE | PRE_66)) + goto error_operand; + break; + case 0xd6: + if (pref & (PRE_F2 | PRE_F3)) + goto error_operand; + break; + case 0xc5: + goto error_operand; + } + goto no_error_operand; + } else + goto no_error_operand; + + error_operand: + hs->flags |= F_ERROR | F_ERROR_OPERAND; + no_error_operand: + + c = *p++; + if (m_reg <= 1) { + if (opcode == 0xf6) + cflags |= C_IMM8; + else if (opcode == 0xf7) + cflags |= C_IMM_P66; + } + + switch (m_mod) { + case 0: + if (pref & PRE_67) { + if (m_rm == 6) + disp_size = 2; + } else + if (m_rm == 5) + disp_size = 4; + break; + case 1: + disp_size = 1; + break; + case 2: + disp_size = 2; + if (!(pref & PRE_67)) + disp_size <<= 1; + } + + if (m_mod != 3 && m_rm == 4) { + hs->flags |= F_SIB; + p++; + hs->sib = c; + hs->sib_scale = c >> 6; + hs->sib_index = (c & 0x3f) >> 3; + if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) + disp_size = 4; + } + + p--; + switch (disp_size) { + case 1: + hs->flags |= F_DISP8; + hs->disp.disp8 = *p; + break; + case 2: + hs->flags |= F_DISP16; + hs->disp.disp16 = *(uint16_t *)p; + break; + case 4: + hs->flags |= F_DISP32; + hs->disp.disp32 = *(uint32_t *)p; + } + p += disp_size; + } else if (pref & PRE_LOCK) + hs->flags |= F_ERROR | F_ERROR_LOCK; + + if (cflags & C_IMM_P66) { + if (cflags & C_REL32) { + if (pref & PRE_66) { + hs->flags |= F_IMM16 | F_RELATIVE; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + goto disasm_done; + } + goto rel32_ok; + } + if (op64) { + hs->flags |= F_IMM64; + hs->imm.imm64 = *(uint64_t *)p; + p += 8; + } else if (!(pref & PRE_66)) { + hs->flags |= F_IMM32; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else + goto imm16_ok; + } + + + if (cflags & C_IMM16) { + imm16_ok: + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + } + if (cflags & C_IMM8) { + hs->flags |= F_IMM8; + hs->imm.imm8 = *p++; + } + + if (cflags & C_REL32) { + rel32_ok: + hs->flags |= F_IMM32 | F_RELATIVE; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else if (cflags & C_REL8) { + hs->flags |= F_IMM8 | F_RELATIVE; + hs->imm.imm8 = *p++; + } + + disasm_done: + + if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { + hs->flags |= F_ERROR | F_ERROR_LENGTH; + hs->len = 15; + } + + return (unsigned int)hs->len; +} + + + +#endif /* X86_64_CPP_ */ diff --git a/whale/src/MSHook/x86_64.h b/whale/src/MSHook/x86_64.h new file mode 100644 index 0000000..b22b572 --- /dev/null +++ b/whale/src/MSHook/x86_64.h @@ -0,0 +1,146 @@ +/* + * x86_64.h + * + * Created on: 2016222 + * Author: peng + */ + +#ifndef X86_64_H_ +#define X86_64_H_ + +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ +#define C_NONE 0x00 +#define C_MODRM 0x01 +#define C_IMM8 0x02 +#define C_IMM16 0x04 +#define C_IMM_P66 0x10 +#define C_REL8 0x20 +#define C_REL32 0x40 +#define C_GROUP 0x80 +#define C_ERROR 0xff + +#define PRE_ANY 0x00 +#define PRE_NONE 0x01 +#define PRE_F2 0x02 +#define PRE_F3 0x04 +#define PRE_66 0x08 +#define PRE_67 0x10 +#define PRE_LOCK 0x20 +#define PRE_SEG 0x40 +#define PRE_ALL 0xff + +#define DELTA_OPCODES 0x4a +#define DELTA_FPU_REG 0xfd +#define DELTA_FPU_MODRM 0x104 +#define DELTA_PREFIXES 0x13c +#define DELTA_OP_LOCK_OK 0x1ae +#define DELTA_OP2_LOCK_OK 0x1c6 +#define DELTA_OP_ONLY_MEM 0x1d8 +#define DELTA_OP2_ONLY_MEM 0x1e7 + +/* stdint.h - C99 standard header + * http://en.wikipedia.org/wiki/stdint.h + * + * if your compiler doesn't contain "stdint.h" header (for + * example, Microsoft Visual C++), you can download file: + * http://www.azillionmonkeys.com/qed/pstdint.h + * and change next line to: + * #include "pstdint.h" + */ +#include + +#define F_MODRM 0x00000001 +#define F_SIB 0x00000002 +#define F_IMM8 0x00000004 +#define F_IMM16 0x00000008 +#define F_IMM32 0x00000010 +#define F_IMM64 0x00000020 +#define F_DISP8 0x00000040 +#define F_DISP16 0x00000080 +#define F_DISP32 0x00000100 +#define F_RELATIVE 0x00000200 +#define F_ERROR 0x00001000 +#define F_ERROR_OPCODE 0x00002000 +#define F_ERROR_LENGTH 0x00004000 +#define F_ERROR_LOCK 0x00008000 +#define F_ERROR_OPERAND 0x00010000 +#define F_PREFIX_REPNZ 0x01000000 +#define F_PREFIX_REPX 0x02000000 +#define F_PREFIX_REP 0x03000000 +#define F_PREFIX_66 0x04000000 +#define F_PREFIX_67 0x08000000 +#define F_PREFIX_LOCK 0x10000000 +#define F_PREFIX_SEG 0x20000000 +#define F_PREFIX_REX 0x40000000 +#define F_PREFIX_ANY 0x7f000000 + +#define PREFIX_SEGMENT_CS 0x2e +#define PREFIX_SEGMENT_SS 0x36 +#define PREFIX_SEGMENT_DS 0x3e +#define PREFIX_SEGMENT_ES 0x26 +#define PREFIX_SEGMENT_FS 0x64 +#define PREFIX_SEGMENT_GS 0x65 +#define PREFIX_LOCK 0xf0 +#define PREFIX_REPNZ 0xf2 +#define PREFIX_REPX 0xf3 +#define PREFIX_OPERAND_SIZE 0x66 +#define PREFIX_ADDRESS_SIZE 0x67 + +#pragma pack(push,1) + +typedef struct { + uint8_t len; + uint8_t p_rep; + uint8_t p_lock; + uint8_t p_seg; + uint8_t p_66; + uint8_t p_67; + uint8_t rex; + uint8_t rex_w; + uint8_t rex_r; + uint8_t rex_x; + uint8_t rex_b; + uint8_t opcode; + uint8_t opcode2; + uint8_t modrm; + uint8_t modrm_mod; + uint8_t modrm_reg; + uint8_t modrm_rm; + uint8_t sib; + uint8_t sib_scale; + uint8_t sib_index; + uint8_t sib_base; + union { + uint8_t imm8; + uint16_t imm16; + uint32_t imm32; + uint64_t imm64; + } imm; + union { + uint8_t disp8; + uint16_t disp16; + uint32_t disp32; + } disp; + uint32_t flags; +} hde64s; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + +/* __cdecl */ +unsigned int hde64_disasm(const void *code, hde64s *hs); + +#ifdef __cplusplus +} +#endif + + +#endif /* X86_64_H_ */ diff --git a/whale/src/android/art/art_helper.cpp b/whale/src/android/art/art_helper.cpp new file mode 100644 index 0000000..53d2de7 --- /dev/null +++ b/whale/src/android/art/art_helper.cpp @@ -0,0 +1,15 @@ +// +// Created by FKD on 19-6-21. +// + +#include "art_helper.h" + +namespace whale::art{ + jobject get_system_classloader(JNIEnv *env){ + jclass cl_class=env->FindClass("java/lang/ClassLoader"); + } + + bool set_classloader_parent(jobject target, jobject parent) { + + } +} diff --git a/whale/src/android/art/art_helper.h b/whale/src/android/art/art_helper.h new file mode 100644 index 0000000..6871ba3 --- /dev/null +++ b/whale/src/android/art/art_helper.h @@ -0,0 +1,13 @@ +// +// Created by FKD on 19-6-21. +// + +#ifndef WORLD_ART_HELPER_H +#define WORLD_ART_HELPER_H + +#include "art_runtime.h" +#include +namespace whale::art{ + bool set_classloader_parent(jobject target,jobject parent); +} +#endif //WORLD_ART_HELPER_H diff --git a/whale/src/android/art/art_jni_trampoline.cc b/whale/src/android/art/art_jni_trampoline.cc index f0f1a82..ddb7b5f 100644 --- a/whale/src/android/art/art_jni_trampoline.cc +++ b/whale/src/android/art/art_jni_trampoline.cc @@ -146,11 +146,15 @@ FFIType FFIGetJniParameter(char shorty) { UNREACHABLE(); } } - -void FFIJniDispatcher(FFIClosure *closure, void *resp, void **args, void *userdata) { #define FFI_ARG(name, type) \ builder.Append##name(*reinterpret_cast(args[i])); +#define INVOKE(type) \ + *reinterpret_cast(resp) = InvokeJavaBridge(env, param, this_object, \ + builder.GetArray()); +void FFIJniDispatcher(FFIClosure *closure, void *resp, void **args, void *userdata) { + + ArtHookParam *param = reinterpret_cast(userdata); const char *argument = param->shorty_ + 1; unsigned int argument_len = (unsigned int) strlen(argument); @@ -197,9 +201,6 @@ void FFIJniDispatcher(FFIClosure *closure, void *resp, void **args, void *userda UNREACHABLE(); } } -#define INVOKE(type) \ - *reinterpret_cast(resp) = InvokeJavaBridge(env, param, this_object, \ - builder.GetArray()); switch (param->shorty_[0]) { case 'Z': diff --git a/whale/src/android/art/art_runtime.cc b/whale/src/android/art/art_runtime.cc index 424550f..4d7250c 100644 --- a/whale/src/android/art/art_runtime.cc +++ b/whale/src/android/art/art_runtime.cc @@ -1,4 +1,5 @@ #include +#include #include "whale.h" #include "android/android_build.h" #include "android/art/art_runtime.h" @@ -14,6 +15,7 @@ #include "base/logging.h" #include "base/singleton.h" #include "base/cxx_helper.h" +#include "art_helper.h" namespace whale { namespace art { @@ -29,13 +31,85 @@ void PreLoadRequiredStuff(JNIEnv *env) { ScopedNoGCDaemons::Load(env); } +void set_java_property(JNIEnv *env, const char* key,const char* value){ + jclass sys_class=env->FindClass("java/lang/System"); + jmethodID set_method=env->GetStaticMethodID(sys_class, + "setProperty", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + env->CallStaticObjectMethod(sys_class,set_method, + env->NewStringUTF(key), + env->NewStringUTF(value)); +} -bool ArtRuntime::OnLoad(JavaVM *vm, JNIEnv *env, jclass java_class) { -#define CHECK_FIELD(field, value) \ - if ((field) == (value)) { \ - LOG(ERROR) << "Failed to find " #field "."; \ - return false; \ +static bool whale_dex_loaded=false; +bool ArtRuntime::prepare_whale_dex(JNIEnv *env,bool update_global_class){ + if(java_mode)return true; + char app_in_dex_path[1024]={0}; + //Try to get app's whale dex dir + const char* app_dex_dir=env->GetStringUTFChars(whale::dex::get_dir(env,"whale"), nullptr); + sprintf(app_in_dex_path,"%s/%s\0",app_dex_dir,"whale.dex"); + const char* c_dex_path= app_in_dex_path; + if(access(app_in_dex_path,F_OK)!=0){ + c_dex_path=DEX_PATH; + LOG(INFO)<<"app_in:"<GetStringUTFChars(opt_path, nullptr), + c_dex_path/*lib should have been loaded into app*/); + if(!whale_dex_loaded) { + whale::dex::patch_class_loader(env, + whale::dex::get_app_classloader(env), + dex_class_loader); + set_java_property(env,"com.lody.whale.load_mode","jni");//Need not to loadLibrary("whale") again. + } + if(!WellKnownClasses::java_lang_ClassLoader)//Ensure that WellKnownClasses has been initialized. + WellKnownClasses::Load(env); + jmethodID load_class=env->GetMethodID(WellKnownClasses::java_lang_ClassLoader, + "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + jobject app_classloader=whale::dex::get_app_classloader(env); + LOG(INFO)<<"app_classloader:"<CallObjectMethod(app_classloader,load_class, + env->NewStringUTF(J_CLASS_NAME)/*must be a java String,or throw a stale reference JNI error.*/); + if(runtime_class) { + whale_dex_loaded = true; + LOG(INFO)<<"loaded whale dex:"<DeleteGlobalRef(java_class_); + java_class_ = reinterpret_cast(env->NewGlobalRef(runtime_class)); + } + return !TryCatch(env);//ClassNotFound +} + +bool ArtRuntime::check_java_mode(JNIEnv *env,jclass java_class){ + jclass runtime_class= java_class; + if(!runtime_class){//ClassNotFound + if(prepare_whale_dex(env,true)){ + return check_java_mode(env,java_class_); + } + //java_mode=false; + return false; + }else{ + //java_mode=true; + if(!java_class_)//Reset java_class_ to runtime_class + java_class_ = reinterpret_cast(env->NewGlobalRef(runtime_class)); + bridge_method_ = env->GetStaticMethodID( + java_class_, + "handleHookedMethod", + "(Ljava/lang/reflect/Member;JLjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;" + );//Get java jump-bridge(hand out). + return !TryCatch(env); } +} + +bool ArtRuntime::OnLoad(JavaVM *vm, JNIEnv *env, jclass java_class) { + if ((kRuntimeISA == InstructionSet::kArm || kRuntimeISA == InstructionSet::kArm64) && IsFileInMemory("libhoudini.so")) { @@ -43,17 +117,15 @@ bool ArtRuntime::OnLoad(JavaVM *vm, JNIEnv *env, jclass java_class) { return false; } vm_ = vm; - java_class_ = reinterpret_cast(env->NewGlobalRef(java_class)); - bridge_method_ = env->GetStaticMethodID( - java_class, - "handleHookedMethod", - "(Ljava/lang/reflect/Member;JLjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;" - ); - if (JNIExceptionCheck(env)) { - return false; + java_mode= check_java_mode(env,java_class);//Check whether we have WhaleRuntime class loaded in Java. + if(!java_mode){ + //Ensure switching the state to native mode. + java_class_= nullptr; + bridge_method_= nullptr; } api_level_ = GetAndroidApiLevel(); PreLoadRequiredStuff(env); + //解析未导出符号 const char *art_path = kLibArtPath; art_elf_image_ = WDynamicLibOpen(art_path); if (art_elf_image_ == nullptr) { @@ -69,26 +141,48 @@ bool ArtRuntime::OnLoad(JavaVM *vm, JNIEnv *env, jclass java_class) { size_t entrypoint_filed_size = (api_level_ <= ANDROID_LOLLIPOP) ? 8 : kPointerSize; - u4 expected_access_flags = kAccPrivate | kAccStatic | kAccNative; - jmethodID reserved0 = env->GetStaticMethodID(java_class, kMethodReserved0, "()V"); - jmethodID reserved1 = env->GetStaticMethodID(java_class, kMethodReserved1, "()V"); - - for (offset_t offset = 0; offset != sizeof(u4) * 24; offset += sizeof(u4)) { - if (MemberOf(reserved0, offset) == expected_access_flags) { - access_flags_offset = offset; - break; + u4 expected_access_flags; + jmethodID reserved0= nullptr; + jmethodID reserved1 = nullptr; + ptr_t native_function= nullptr; + + //TODO_D:setting java_mode in OnLoad to false is only for test. + //java_mode=false; + //prepare_whale_dex(env,true); + + if(java_mode){ + java_class=java_class_;//Ensure java_class is not nullptr. + expected_access_flags=kAccPrivate | kAccStatic | kAccNative; + reserved0 = env->GetStaticMethodID(java_class, kMethodReserved0, "()V"); + native_function = reinterpret_cast(WhaleRuntime_reserved0); + if(JNIExceptionCheck(env)){ + return false; } - } - void *native_function = reinterpret_cast(WhaleRuntime_reserved0); - - for (offset_t offset = 0; offset != sizeof(u4) * 24; offset += sizeof(u4)) { - if (MemberOf(reserved0, offset) == native_function) { - jni_code_offset = offset; - break; + reserved1 = env->GetStaticMethodID(java_class, kMethodReserved1, "()V"); + if(JNIExceptionCheck(env)){ + return false; } + LOG(INFO)<<"get offset using stub method"; + }else{ + expected_access_flags=kAccPrivate|kAccNative|kAccFastNative;//524546 我也不知道为什么会有fast_native + jclass obj_class=env->FindClass("java/lang/Object"); + if(JNIExceptionCheck(env)||!obj_class) + return false; + reserved0=env->GetMethodID(obj_class,"internalClone","()Ljava/lang/Object;"); + reserved1 =env->GetMethodID(obj_class,"clone","()Ljava/lang/Object;");//524292 + native_function=WDynamicLibSymbol(art_elf_image_,"_ZN3artL20Object_internalCloneEP7_JNIEnvP8_jobject"); + LOG(INFO)<<"get offset using Object(JNI-style support only)"; } + access_flags_offset=GetOffsetByValue(reserved0,expected_access_flags); + if(!native_function) + return false; + jni_code_offset=GetOffsetByValue(reserved0,native_function); + CHECK_FIELD(access_flags_offset, INT32_MAX) CHECK_FIELD(jni_code_offset, INT32_MAX) + //输出获取到的两个偏移 + LOG(INFO)<<"access_flags_offset:"<CallStaticVoidMethod(java_class, reserved0); + if(java_mode)//Resolve static method. + env->CallStaticVoidMethod(java_class, reserved0); /** * Fallback to do a relative memory search for quick_generic_jni_trampoline, @@ -187,8 +282,8 @@ bool ArtRuntime::OnLoad(JavaVM *vm, JNIEnv *env, jclass java_class) { jlong -ArtRuntime::HookMethod(JNIEnv *env, jclass decl_class, jobject hooked_java_method, - jobject addition_info) { +ArtRuntime::HookMethodJNI(JNIEnv *env, jclass decl_class, jobject hooked_java_method, + ptr_t replace) { ScopedSuspendAll suspend_all; jmethodID hooked_jni_method = env->FromReflectedMethod(hooked_java_method); @@ -245,18 +340,110 @@ ArtRuntime::HookMethod(JNIEnv *env, jclass decl_class, jobject hooked_java_metho } param->origin_native_method_ = env->FromReflectedMethod(origin_java_method); param->hooked_native_method_ = hooked_jni_method; - param->addition_info_ = env->NewGlobalRef(addition_info); + //param->addition_info_ = env->NewGlobalRef(addition_info); param->hooked_method_ = env->NewGlobalRef(hooked_java_method); param->origin_method_ = env->NewGlobalRef(origin_java_method); - BuildJniClosure(param); - hooked_method.SetEntryPointFromJni(param->jni_closure_->GetCode()); + LOG(INFO)<<"hook in jni-style"; + LOG(INFO)<<"replace method address:"<decl_class_ = hooked_method.GetDeclaringClass(); hooked_method_map_.insert(std::make_pair(hooked_jni_method, param)); return reinterpret_cast(param); } + jlong + ArtRuntime::HookMethod(JNIEnv *env, jclass decl_class, jobject hooked_java_method, + jobject addition_info/*As for jni-style hook, it points to replace method.*/, + bool force_jni_style) { + ScopedSuspendAll suspend_all; + + jmethodID hooked_jni_method = env->FromReflectedMethod(hooked_java_method); + ArtMethod hooked_method(hooked_jni_method); + auto *param = new ArtHookParam(); + + param->class_Loader_ = env->NewGlobalRef( + env->CallObjectMethod( + decl_class, + WellKnownClasses::java_lang_Class_getClassLoader + ) + ); + param->shorty_ = hooked_method.GetShorty(env, hooked_java_method); + param->is_static_ = hooked_method.HasAccessFlags(kAccStatic); + + param->origin_compiled_code_ = hooked_method.GetEntryPointFromQuickCompiledCode(); + param->origin_code_item_off = hooked_method.GetDexCodeItemOffset(); + param->origin_jni_code_ = hooked_method.GetEntryPointFromJni(); + param->origin_access_flags = hooked_method.GetAccessFlags(); + jobject origin_java_method = hooked_method.Clone(env, param->origin_access_flags); + + ResolvedSymbols *symbols = GetSymbols(); + if (symbols->ProfileSaver_ForceProcessProfiles) { + symbols->ProfileSaver_ForceProcessProfiles(); + } + // After android P, hotness_count_ maybe an imt_index_ for abstract method + if ((api_level_ > ANDROID_P && !hooked_method.HasAccessFlags(kAccAbstract)) + || api_level_ >= ANDROID_N) { + hooked_method.SetHotnessCount(0); + } + // Clear the dex_code_item_offset_. + // It needs to be 0 since hooked methods have no CodeItems but the + // method they copy might. + hooked_method.SetDexCodeItemOffset(0); + u4 access_flags = hooked_method.GetAccessFlags(); + if (api_level_ < ANDROID_O_MR1) { + access_flags |= kAccCompileDontBother_N; + } else { + access_flags |= kAccCompileDontBother_O_MR1; + access_flags |= kAccPreviouslyWarm_O_MR1; + } + access_flags |= kAccNative; + access_flags |= kAccFastNative; + if (api_level_ >= ANDROID_P) { + access_flags &= ~kAccCriticalNative_P; + } + hooked_method.SetAccessFlags(access_flags); + hooked_method.SetEntryPointFromQuickCompiledCode( + class_linker_objects_.quick_generic_jni_trampoline_ + ); + if (api_level_ < ANDROID_N + && symbols->artInterpreterToCompiledCodeBridge != nullptr) { + hooked_method.SetEntryPointFromInterpreterCode(symbols->artInterpreterToCompiledCodeBridge); + } + param->origin_native_method_ = env->FromReflectedMethod(origin_java_method); + param->hooked_native_method_ = hooked_jni_method; + param->addition_info_ = env->NewGlobalRef(addition_info); + param->hooked_method_ = env->NewGlobalRef(hooked_java_method); + param->origin_method_ = env->NewGlobalRef(origin_java_method); + + if(check_java_mode(env,java_class_)&&!force_jni_style) {//java-style hook(Xposed-style for default) + LOG(INFO)<<"hook in Xposed-style"; + BuildJniClosure(param); + hooked_method.SetEntryPointFromJni(param->jni_closure_->GetCode()); + }else{ + LOG(INFO)<<"hook in jni-style"; + LOG(INFO)<<"replace method address(addition_info):"<decl_class_ = hooked_method.GetDeclaringClass(); + hooked_method_map_.insert(std::make_pair(hooked_jni_method, param)); + return reinterpret_cast(param); + } + + jobjectArray ArtRuntime::ParseParamArray(JNIEnv *env, + jobject param_array[]/*这里面的参数顺序一定要一一对应*/, int array_size) { + jclass obj_class=env->FindClass("java/lang/Object"); + jobjectArray paramArray = env->NewObjectArray(array_size, + obj_class, + nullptr); + for (int i = 0; i < array_size; i++) { + env->SetObjectArrayElement(paramArray, i, param_array[i]); + } + return paramArray; + } + jobject ArtRuntime::InvokeOriginalMethod(jlong slot, jobject this_object, jobjectArray args) { JNIEnv *env = GetJniEnv(); @@ -355,7 +542,7 @@ jlong ArtRuntime::GetMethodSlot(JNIEnv *env, jclass cl, jobject method_obj) { } void ArtRuntime::EnsureClassInitialized(JNIEnv *env, jclass cl) { - // This invocation will ensure the target class has been initialized also. + // This invocation will ensure the targetPkgName class has been initialized also. ScopedLocalRef unused(env, env->AllocObject(cl)); JNIExceptionClear(env); } @@ -486,6 +673,7 @@ void ArtRuntime::FixBugN() { "_ZNK3art20OatQuickMethodHeader7ToDexPcEPNS_9ArtMethodEjb" ); if (symbol) { + LOG(INFO)<<"found ToDexPc symbol,inline hook it"; WInlineHookFunction(symbol, reinterpret_cast(new_ToDexPc), reinterpret_cast(&old_ToDexPc)); } is_hooked = true; diff --git a/whale/src/android/art/art_runtime.h b/whale/src/android/art/art_runtime.h index 55f4773..0660444 100644 --- a/whale/src/android/art/art_runtime.h +++ b/whale/src/android/art/art_runtime.h @@ -13,6 +13,12 @@ #include "base/macros.h" #include "base/primitive_types.h" +#define CHECK_FIELD(field, value) \ + if ((field) == (value)) { \ + LOG(ERROR) << "Failed to find " #field "."; \ + return false; \ + } + #if defined(__LP64__) static constexpr const char *kAndroidLibDir = "/system/lib64/"; static constexpr const char *kLibNativeBridgePath = "/system/lib64/libnativebridge.so"; @@ -67,7 +73,13 @@ class ArtRuntime final { bool OnLoad(JavaVM *vm, JNIEnv *env, jclass java_class); jlong HookMethod(JNIEnv *env, jclass decl_class, jobject hooked_java_method, - jobject addition_info); + jobject addition_info,bool force_jni_style); + + jlong HookMethodJNI(JNIEnv *env, jclass decl_class, jobject hooked_java_method, + ptr_t replace); + + jobjectArray ParseParamArray(JNIEnv *env, + jobject param_array[]/*这里面的参数顺序一定要一一对应*/, int array_size); JNIEnv *GetJniEnv() { JNIEnv *env = nullptr; @@ -127,9 +139,13 @@ class ArtRuntime final { void FixBugN(); + jclass java_class_; + bool check_java_mode(JNIEnv *env,jclass java_class); + private: JavaVM *vm_; - jclass java_class_; + bool java_mode; + jmethodID bridge_method_; s4 api_level_; void *art_elf_image_; @@ -141,6 +157,9 @@ class ArtRuntime final { std::map hooked_method_map_; pthread_mutex_t mutex; + bool prepare_whale_dex(JNIEnv *env,bool update_global_class); + + bool EnforceDisableHiddenAPIPolicyImpl(); DISALLOW_COPY_AND_ASSIGN(ArtRuntime); diff --git a/whale/src/android/art/art_symbol_resolver.cc b/whale/src/android/art/art_symbol_resolver.cc index ad2500c..c6a4279 100644 --- a/whale/src/android/art/art_symbol_resolver.cc +++ b/whale/src/android/art/art_symbol_resolver.cc @@ -68,8 +68,6 @@ SYMBOL kArt_Object_CloneWithSize = "_ZN3art6mirror6Object5CloneEPNS_6ThreadEj"; SYMBOL kArt_JniEnvExt_NewLocalRef = "_ZN3art9JNIEnvExt11NewLocalRefEPNS_6mirror6ObjectE"; - -bool ArtSymbolResolver::Resolve(void *elf_image, s4 api_level) { #define FIND_SYMBOL(symbol, decl, ret) \ if ((decl = reinterpret_cast(WDynamicLibSymbol(elf_image, symbol))) == nullptr) { \ if (ret) { \ @@ -77,6 +75,12 @@ bool ArtSymbolResolver::Resolve(void *elf_image, s4 api_level) { return false; \ } \ } + static bool symbol_resolved=false; +bool ArtSymbolResolver::Resolve(void *elf_image, s4 api_level) { + if(symbol_resolved) {//Prevent twice resolve to cause complex problems. + LOG(INFO)<<"symbol has been resolved,use old result"; + return true; + } FIND_SYMBOL(kArt_GetMethodShorty, symbols_.Art_GetMethodShorty, false); if (symbols_.Art_GetMethodShorty == nullptr) { FIND_SYMBOL(kArt_GetMethodShorty_Legacy, symbols_.Art_GetMethodShorty, false); @@ -110,6 +114,8 @@ bool ArtSymbolResolver::Resolve(void *elf_image, s4 api_level) { } FIND_SYMBOL(kArt_DecodeJObject, symbols_.Thread_DecodeJObject, true); FIND_SYMBOL(kArt_JniEnvExt_NewLocalRef, symbols_.JniEnvExt_NewLocalRef, true); + + symbol_resolved=true; return true; #undef FIND_SYMBOL } diff --git a/whale/src/android/art/native_on_load.cc b/whale/src/android/art/native_on_load.cc index f587d55..6ae804e 100644 --- a/whale/src/android/art/native_on_load.cc +++ b/whale/src/android/art/native_on_load.cc @@ -1,8 +1,9 @@ +#include #include "android/jni_helper.h" #include "android/art/art_runtime.h" #include "base/logging.h" - -#define CLASS_NAME "com/lody/whale/WhaleRuntime" +#include "native_on_load.h" +#include "../hook_module/world.h" #ifndef WHALE_ANDROID_AUTO_LOAD #define JNI_OnLoad Whale_OnLoad @@ -17,7 +18,7 @@ static jlong WhaleRuntime_hookMethodNative(JNI_START, jclass decl_class, jobject method_obj, jobject addition_info) { auto runtime = whale::art::ArtRuntime::Get(); - return runtime->HookMethod(env, decl_class, method_obj, addition_info); + return runtime->HookMethod(env, decl_class, method_obj, addition_info,false); } static jobject @@ -56,6 +57,17 @@ void WhaleRuntime_enforceDisableHiddenAPIPolicy(JNI_START) { runtime->EnforceDisableHiddenAPIPolicy(); } +void WhaleRuntime_handleCallAppOnCreate(JNI_START){ + jobject app_classloader=whale::dex::get_app_classloader(env); + jobject sys_classloader=whale::dex::get_sys_classloader(env); + if(app_classloader==sys_classloader){//app_classloader has not been created yet. + LOG(INFO)<<"patch app_cl with sys_cl is too early"; + return; + } + whale::dex::patch_class_loader(env,app_classloader,sys_classloader); + LOG(INFO)<<"patch app_cl finished"; +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(WhaleRuntime, hookMethodNative, @@ -69,25 +81,38 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(WhaleRuntime, removeFinalFlagNative, "(Ljava/lang/Class;)V"), NATIVE_METHOD(WhaleRuntime, enforceDisableHiddenAPIPolicy, "()V"), + NATIVE_METHOD(WhaleRuntime, handleCallAppOnCreate, "()V"), NATIVE_METHOD(WhaleRuntime, reserved0, "()V"), NATIVE_METHOD(WhaleRuntime, reserved1, "()V") }; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { + if(!GetJavaVM()){ + LOG(INFO)<<"JavaVM has not been created yet,cannot run JNI_OnLoad"; + return JNI_VERSION_1_4; + } JNIEnv *env = nullptr; if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_4) != JNI_OK) { return -1; } + auto runtime = whale::art::ArtRuntime::Get(); jclass cl = env->FindClass(CLASS_NAME); - if (cl == nullptr) { - LOG(ERROR) << "FindClass failed for " << CLASS_NAME; - return JNI_ERR; + if(env->ExceptionCheck()) + env->ExceptionClear(); + if (!cl) { + if(!runtime->check_java_mode(env, nullptr)) {//Manually load WhaleRuntime from dex. + LOG(ERROR) << "FindClass failed for " << CLASS_NAME; + return JNI_ERR; + }else{ + cl=runtime->java_class_; + } } + //Register JNI Methods to WhaleRuntime,no matter how it is loaded. if (env->RegisterNatives(cl, gMethods, NELEM(gMethods)) < 0) { LOG(ERROR) << "RegisterNatives failed for " << CLASS_NAME; return JNI_ERR; } - auto runtime = whale::art::ArtRuntime::Get(); + //Init environment. if (!runtime->OnLoad(vm, env, cl)) { LOG(ERROR) << "Runtime setup failed"; return JNI_ERR; diff --git a/whale/src/android/art/native_on_load.h b/whale/src/android/art/native_on_load.h index 4b2a453..9aac1d4 100644 --- a/whale/src/android/art/native_on_load.h +++ b/whale/src/android/art/native_on_load.h @@ -6,6 +6,10 @@ constexpr const char *kMethodReserved0 = "reserved0"; constexpr const char *kMethodReserved1 = "reserved1"; +#define DEX_PATH "/data/local/tmp/whale.dex" +#define CLASS_NAME "com/lody/whale/WhaleRuntime" +#define J_CLASS_NAME "com.lody.whale.WhaleRuntime" + /** * DO NOT rename the following function */ @@ -17,6 +21,8 @@ void WhaleRuntime_reserved1(JNIEnv *env, jclass cl); } +void WhaleRuntime_handleCallAppOnCreate(JNI_START); + #ifndef WHALE_ANDROID_AUTO_LOAD JNIEXPORT jint JNICALL Whale_OnLoad(JavaVM *vm, void *reserved); #endif diff --git a/whale/src/android/art/well_known_classes.cc b/whale/src/android/art/well_known_classes.cc index 7b4ffc6..fd74f27 100644 --- a/whale/src/android/art/well_known_classes.cc +++ b/whale/src/android/art/well_known_classes.cc @@ -69,8 +69,12 @@ static jmethodID CachePrimitiveBoxingMethod(JNIEnv *env, char prim_name, const c StringPrintf("(%c)L%s;", prim_name, boxed_name).c_str()); } - +static bool wkc_loaded=false; void WellKnownClasses::Load(JNIEnv *env) { + if(wkc_loaded){ + LOG(INFO)<<"WellKnownClasses has been loaded,use the old one"; + return; + } java_lang_Object = CacheClass(env, "java/lang/Object"); java_lang_reflect_Method = CacheClass(env, "java/lang/reflect/Method"); java_lang_Class = CacheClass(env, "java/lang/Class"); @@ -94,6 +98,8 @@ void WellKnownClasses::Load(JNIEnv *env) { "(Z)V"); java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J", true); + + wkc_loaded=true; } diff --git a/whale/src/android/dex/DexManager.cpp b/whale/src/android/dex/DexManager.cpp new file mode 100644 index 0000000..e36e186 --- /dev/null +++ b/whale/src/android/dex/DexManager.cpp @@ -0,0 +1,274 @@ +// +// Created by FKD on 19-6-21. +// + +#include +#include +#include +#include +#include +#include +#include "DexManager.h" + +namespace whale::dex{ + /*public static Object combineArray(Object firstArray, Object secondArray) { + Class localClass = firstArray.getClass().getComponentType(); + int firstArrayLength = Array.getLength(firstArray); + int allLength = firstArrayLength + Array.getLength(secondArray); + Object result = Array.newInstance(localClass, allLength); + for (int k = 0; k < allLength; ++k) { + if (k < firstArrayLength) { + Array.set(result, k, Array.get(firstArray, k)); + } else { + Array.set(result, k, Array.get(secondArray, k - firstArrayLength)); + } + } + return result; + }*/ + jobject combine_array(JNIEnv *env,jobject l_array,jobject r_array){ + jclass array_class=env->FindClass("java/lang/reflect/Array"); + //public static int getLength(Object array) + jmethodID get_length=env->GetStaticMethodID(array_class,"getLength","(Ljava/lang/Object;)I"); + int first_length=env->CallStaticIntMethod(array_class,get_length, + l_array); + int all_length=first_length+ env->CallStaticIntMethod(array_class,get_length, + r_array); + jclass obj_class=env->FindClass("java/lang/Object"); + jmethodID get_class=env->GetMethodID(obj_class,"getClass","()Ljava/lang/Class;"); + jclass la_class= (jclass)env->CallObjectMethod(l_array,get_class); + jmethodID get_component_type=env->GetMethodID(env->GetObjectClass(la_class), + "getComponentType","()Ljava/lang/Class;"); + jclass decl_class=(jclass)env->CallObjectMethod(la_class,get_component_type); + //public static Object newInstance(Class componentType, int length) + jmethodID new_instance=env->GetStaticMethodID(array_class,"newInstance", + "(Ljava/lang/Class;I)Ljava/lang/Object;"); + /*jobject result=env->CallStaticObjectMethod(decl_class,new_instance, + decl_class,all_length);*/ //this will cause a unknown JNI type cast error. + jobject result=env->NewObjectArray(all_length,decl_class, nullptr); + //public static Object get(Object array, int index) + jmethodID array_get=env->GetStaticMethodID(array_class,"get", + "(Ljava/lang/Object;I)Ljava/lang/Object;"); + //public static void set(Object array, int index, Object value) + jmethodID array_set=env->GetStaticMethodID(array_class,"set", + "(Ljava/lang/Object;ILjava/lang/Object;)V"); + for (int i = 0; i < all_length; ++i) { + if (i < first_length) { + jobject item=env->CallStaticObjectMethod(array_class,array_get, + l_array,i); + env->CallStaticVoidMethod(array_class,array_set, + result, i,item); + env->DeleteLocalRef(item); + } else { + //Array.set(result, i, Array.get(secondArray, k - firstArrayLength)); + jobject item=env->CallStaticObjectMethod(array_class,array_get, + r_array,i-first_length); + env->CallStaticVoidMethod(array_class,array_set, + result, i,item); + env->DeleteLocalRef(item); + } + } + return result; + } + + void patch_class_loader(JNIEnv *env,jobject target,jobject dex_class_loader){ + //Get pathList + jclass bdcl_class=env->FindClass("dalvik/system/BaseDexClassLoader"); + jfieldID path_list_field=env->GetFieldID(bdcl_class,"pathList", + "Ldalvik/system/DexPathList;"); + jobject target_path_list=env->GetObjectField(target,path_list_field); + jobject new_path_list=env->GetObjectField(dex_class_loader,path_list_field); + //Get dexElements + jclass path_list_class=env->FindClass("dalvik/system/DexPathList"); + jfieldID dex_elements_field=env->GetFieldID(path_list_class,"dexElements", + "[Ldalvik/system/DexPathList$Element;"); + jobject target_dex_elements=env->GetObjectField(target_path_list,dex_elements_field); + jobject new_dex_elements=env->GetObjectField(new_path_list,dex_elements_field); + //Combine both dexElements(Notice:there is no same object check, + // so do not call it with the same params twice) + jobject all_dex_elemnts=combine_array(env,new_dex_elements,target_dex_elements); + //Patch targetPkgName dexElements with the combined. + env->SetObjectField(target_path_list,dex_elements_field,all_dex_elemnts); + } + + static jobject sys_classloader= nullptr; + jobject get_sys_classloader(JNIEnv *env){ + if(sys_classloader)//Prevent meaningless consuming. + return sys_classloader; + jclass cl_class=env->FindClass("java/lang/ClassLoader"); + jmethodID get_sys_classloader= env->GetStaticMethodID(cl_class, + "getSystemClassLoader", + "()Ljava/lang/ClassLoader;"); + sys_classloader=env->NewGlobalRef( + env->CallStaticObjectMethod(cl_class,get_sys_classloader) + ); + return sys_classloader; + } + + static std::map loaded_dex_map; + jobject get_loaded_dex(const char* dex_path){ + auto iter =loaded_dex_map.begin(); + while (iter!=loaded_dex_map.end()){ + const char* item_dex_path=iter->first; + if(strcmp(item_dex_path,dex_path) == 0){ + return iter->second;//return a DexClassLoader's global reference. + } + iter++; + } + return nullptr; + } + + jobject new_dex_class_loader(JNIEnv *env,const char* dex_path, const char* opt_path,const char* lib_path){ + jobject loaded_dex=get_loaded_dex(dex_path); + if (loaded_dex) { + LOG(INFO)<<"found a loaded dex:"<NewStringUTF(dex_path); + jstring j_opt_path=env->NewStringUTF(opt_path); + jstring j_lib_path= nullptr; + if(lib_path)j_lib_path=env->NewStringUTF(lib_path); + //Get systemClassLoader + jobject sys_classloader=get_sys_classloader(env); + //Get constructor of DexClassLoader + jclass dex_class_loader_class=env->FindClass("dalvik/system/DexClassLoader"); + jmethodID constructor=env->GetMethodID(dex_class_loader_class,"", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V"); + //Construct a new DexClassLoader with global reference + jobject result=env->NewGlobalRef( + env->NewObject(dex_class_loader_class,constructor, + j_dex_path,j_opt_path,j_lib_path,sys_classloader) + ); + loaded_dex_map.insert(std::make_pair(dex_path,result));//Cache loaded dex to prevent repeatedly creation. + //Recycle local vars + env->DeleteLocalRef(j_dex_path); + env->DeleteLocalRef(j_opt_path); + if(j_lib_path)env->DeleteLocalRef(j_lib_path); + return result; + } + + jstring get_dir(JNIEnv *env,const char* name){ + jclass file_class=env->FindClass("java/io/File"); + //public String getAbsolutePath() + jmethodID get_abs_path=env->GetMethodID(file_class,"getAbsolutePath", + "()Ljava/lang/String;"); + int uid=getuid(); + bool privilege_uids=(uid==0||uid==1000); + if(privilege_uids){//Privilege processes + LOG(INFO)<<"privilege process need to use /data/system"; + jmethodID construct=env->GetMethodID(file_class,"","(Ljava/lang/String;)V"); + //Build full dir path + char dir_path[1024]={0}; + sprintf(dir_path,"%s/%s\0","/data/system",name); + jstring j_dir_path=env->NewStringUTF(dir_path); + jobject dir=env->NewObject(file_class,construct,j_dir_path); + env->DeleteLocalRef(j_dir_path); + //Check exists and mkdirs() + jmethodID exists=env->GetMethodID(file_class,"exists","()Z"); + bool is_exists=env->CallBooleanMethod(dir,exists); + if(!is_exists){ + jmethodID mkdirs=env->GetMethodID(file_class,"mkdirs","()Z"); + if(!env->CallBooleanMethod(dir,mkdirs)){ + return nullptr; + } + } + //Notice:(1 TIME) Caller need to use DeleteGlobalRef after path is used. + return (jstring)env->NewGlobalRef( + env->CallObjectMethod(dir,get_abs_path) + ); + } + jobject context=get_app_context(env); + jclass wrapper_class=env->FindClass("android/content/Context"); + jmethodID get_dir_method=env->GetMethodID(wrapper_class,"getDir", + "(Ljava/lang/String;I)Ljava/io/File;"); + jobject dir=env->CallObjectMethod(context,get_dir_method, + env->NewStringUTF(name),0); + jstring dir_str=(jstring)env->CallObjectMethod(dir,get_abs_path); + //Notice:(2 TIMES) Caller need to use DeleteGlobalRef after path is used. + return (jstring)env->NewGlobalRef(dir_str); + } + + static jobject app_context= nullptr; + jobject get_app_context(JNIEnv *env){ + if(app_context) + return app_context; + //Get application + jclass at_class=env->FindClass("android/app/ActivityThread"); + jmethodID current_app=env->GetStaticMethodID(at_class,"currentApplication", + "()Landroid/app/Application;"); + jobject app=env->CallStaticObjectMethod(at_class,current_app); + if(TryCatch(env)||!app)//process has no application object. + return nullptr; + //Get applicationContext + jclass wrapper_class=env->FindClass("android/content/ContextWrapper"); + jmethodID get_app_context_method=env->GetMethodID(wrapper_class, + "getApplicationContext", + "()Landroid/content/Context;"); + app_context=env->NewGlobalRef( + env->CallObjectMethod(app,get_app_context_method) + ); + return app_context; + } + + static jobject app_classloader=nullptr; + jobject get_app_classloader(JNIEnv *env) { + if(app_classloader) + return app_classloader; + + jclass context_class = env->FindClass("android/content/Context"); + jmethodID get_classloader = env->GetMethodID(context_class, + "getClassLoader", + "()Ljava/lang/ClassLoader;"); + //env->EnsureLocalCapacity(10); + jobject context = get_app_context(env); + if (!context) { + LOG(INFO) << "use sys_classloader as app_classloader(context is nullptr)"; + //Naturally return a global reference from get_sys_classloader directly. + return get_sys_classloader(env); + }else { + //LOG(INFO)<<"app_context:"<NewGlobalRef( + env->CallObjectMethod(context, get_classloader) + ); + return app_classloader; + } + } + + jstring get_pkg_name(JNIEnv *env){ + jobject context=get_app_context(env); + if(!context){ + LOG(INFO)<<"return pkg_name as \"android\" because context is nullptr"; + return env->NewStringUTF("android"); + } + jclass wrapper_class=env->FindClass("android/content/ContextWrapper"); + jmethodID get_pkg_name=env->GetMethodID(wrapper_class,"getPackageName", + "()Ljava/lang/String;"); + jstring j_pkg_name=(jstring)env->CallObjectMethod(context,get_pkg_name); + const char* c_pkg_name=env->GetStringUTFChars(j_pkg_name, nullptr); + return env->NewStringUTF(c_pkg_name); + } + + jclass load_by_specific_classloader(JNIEnv *env,jobject class_loader,const char* class_name_wp){ + jclass cl_class=env->FindClass("java/lang/ClassLoader"); + jmethodID load_class=env->GetMethodID(cl_class, + "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + jclass clazz=(jclass)env->CallObjectMethod(class_loader,load_class, + env->NewStringUTF(class_name_wp)/*must be a java String,or throw a stale reference JNI error.*/); + if(TryCatch(env))return nullptr; + return clazz; + } + + jclass load_by_app_classloader(JNIEnv *env,const char* class_name_wp){ + jclass cl_class=env->FindClass("java/lang/ClassLoader"); + jmethodID load_class=env->GetMethodID(cl_class, + "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + jobject app_classloader=whale::dex::get_app_classloader(env); + LOG(INFO)<<"app_classloader:"<CallObjectMethod(app_classloader,load_class, + env->NewStringUTF(class_name_wp)/*must be a java String,or throw a stale reference JNI error.*/); + if(TryCatch(env))return nullptr; + return clazz; + } +} diff --git a/whale/src/android/dex/DexManager.h b/whale/src/android/dex/DexManager.h new file mode 100644 index 0000000..b787da9 --- /dev/null +++ b/whale/src/android/dex/DexManager.h @@ -0,0 +1,26 @@ +// +// Created by FKD on 19-6-21. +// + +#ifndef WORLD_DEXMANAGER_H +#define WORLD_DEXMANAGER_H + +#include +namespace whale::dex { + jobject combine_array(JNIEnv *env,jobject l_array,jobject r_array); + void patch_class_loader(JNIEnv *env,jobject target,jobject dex_class_loader); + jobject get_sys_classloader(JNIEnv *env); + jobject get_loaded_dex(const char* dex_path); + jobject new_dex_class_loader(JNIEnv *env,const char* dex_path, const char* opt_path,const char* lib_path); + jstring get_dir(JNIEnv *env,const char* name); + jobject get_app_context(JNIEnv *env); + jobject get_app_classloader(JNIEnv *env); + jstring get_pkg_name(JNIEnv *env); + jclass load_by_app_classloader(JNIEnv *env,const char* class_name_wp); + jclass load_by_specific_classloader(JNIEnv *env,jobject class_loader,const char* class_name_wp); + + class DexManager { + }; +} + +#endif //WORLD_DEXMANAGER_H diff --git a/whale/src/android/dex/README.md b/whale/src/android/dex/README.md new file mode 100644 index 0000000..2a7f27e --- /dev/null +++ b/whale/src/android/dex/README.md @@ -0,0 +1,8 @@ +提供文件dex加载和类似nuwa的类加载器合并功能(可以让后续加载进来的hook模块方便的访问XposedBridge和WhaleRuntime) + +反射就完事了,android 10目前我没有拿到设备,暂时没办法适配新hidden api policy +(某为锁bl他给我10也没用。。。难道拆机然后直接暴力写BL吗(滑稽)) + +发布的时候请务必删掉这段话。。。 + +--FKD diff --git a/whale/src/android/hook_module/JNIHookManager.cpp b/whale/src/android/hook_module/JNIHookManager.cpp new file mode 100644 index 0000000..868ab3e --- /dev/null +++ b/whale/src/android/hook_module/JNIHookManager.cpp @@ -0,0 +1,218 @@ +// +// Created by FKD on 19-6-21. +// + +#include +#include +#include +#include +#include "JNIHookManager.h" + +namespace whale::art { + jobjectArray get_dexfiles(JNIEnv *env,jobject class_loader){//checked functional + jclass bdcl_class=env->FindClass("dalvik/system/BaseDexClassLoader"); + jfieldID path_list_field=env->GetFieldID(bdcl_class,"pathList", + "Ldalvik/system/DexPathList;"); + jobject path_list=env->GetObjectField(class_loader,path_list_field); + //Get dexElements + jclass path_list_class=env->FindClass("dalvik/system/DexPathList"); + jfieldID dex_elements_field=env->GetFieldID(path_list_class,"dexElements", + "[Ldalvik/system/DexPathList$Element;"); + jobjectArray dex_elements=(jobjectArray)env->GetObjectField(path_list,dex_elements_field); + int elements_size=env->GetArrayLength(dex_elements); + //Get every dexFile + jclass element_class=env->FindClass("dalvik/system/DexPathList$Element"); + jfieldID dex_file_field=env->GetFieldID(element_class,"dexFile", + "Ldalvik/system/DexFile;"); + jclass dex_file_class=env->FindClass("dalvik/system/DexFile"); + jobjectArray result=(jobjectArray)env->NewGlobalRef( + env->NewObjectArray(elements_size,dex_file_class, nullptr) + );//Construct a global array to cache these dexFiles. + for (int i = 0; i < elements_size; ++i) { + jobject element_item=env->GetObjectArrayElement(dex_elements,i); + jobject dex_file=env->GetObjectField(element_item,dex_file_field); + if(dex_file){//Dictionary element's dexFile is null. + env->SetObjectArrayElement(result,i,dex_file); + } + //All temp objects must be recycle at the end of each loop + // to prevent JNI reference table overflow(256 for default). + env->DeleteLocalRef(element_item); + } + return result; + } + + using namespace std; + std::list get_all_class_name(JNIEnv *env,jobject dex_file,const char* prefix){ + list result_list; + if(!dex_file) + return result_list; + if(!prefix) + prefix="";//Let it always true. + //Get all necessary method that will be used in the loop. + jclass dex_file_class=env->FindClass("dalvik/system/DexFile"); + jmethodID get_entries=env->GetMethodID(dex_file_class,"entries", + "()Ljava/util/Enumeration;"); + jclass enum_class=env->FindClass("java/util/Enumeration"); + jmethodID has_more_elements=env->GetMethodID(enum_class,"hasMoreElements","()Z"); + jmethodID next_element=env->GetMethodID(enum_class,"nextElement","()Ljava/lang/Object;"); + jobject entries=env->CallObjectMethod(dex_file,get_entries); + //Enum all classes and cache them. + while (env->CallBooleanMethod(entries,has_more_elements)){ + jstring class_name=(jstring)env->CallObjectMethod(entries,next_element); + const char* c_class_name=env->GetStringUTFChars(class_name, nullptr); + if(!Contains(c_class_name,"android")&&Contains(c_class_name,prefix)) { + result_list.emplace_back(c_class_name);//Cache into result_list. + } + env->ReleaseStringUTFChars(class_name,c_class_name); + env->DeleteLocalRef(class_name); + } + return result_list; + } + + jmethodID get_method_by_name(JNIEnv *env,jclass decl_class,const char* name){ + jclass c_class=env->FindClass("java/lang/Class"); + //public Method[] getDeclaredMethods() + jmethodID get_all_methods=env->GetMethodID(c_class,"getDeclaredMethods", + "()[Ljava/lang/reflect/Method;"); + jobjectArray methods=(jobjectArray)env->CallObjectMethod(decl_class,get_all_methods); + jclass method_class=env->FindClass("java/lang/reflect/Method"); + //public String getName() + jmethodID get_method_name=env->GetMethodID(method_class,"getName","()Ljava/lang/String;"); + int method_count=env->GetArrayLength(methods); + for (int i = 0; i < method_count; ++i) { + jobject method_item=env->GetObjectArrayElement(methods,i); + jstring j_method_name=(jstring)env->CallObjectMethod(method_item,get_method_name); + const char* c_method_name=env->GetStringUTFChars(j_method_name, nullptr); + if(Contains(c_method_name,name)){//Found the method is searching for. + //LOGD("found method %s:%p\n",c_method_name,method_item); + return env->FromReflectedMethod(method_item); + } + //Release local references in loop. + env->ReleaseStringUTFChars(j_method_name,c_method_name); + env->DeleteLocalRef(j_method_name); + env->DeleteLocalRef(method_item); + } + LOG(INFO)<<"method "<FindClass("java/lang/Class"); + //TODO:Finish the remaining parts that load hook modules in every app. + //Get @HookModule annotation + jclass hm_anno_class=whale::dex::load_by_app_classloader(env, + J_HOOK_MODULE_ANNO_NAME); + if(TryCatch(env)||!hm_anno_class){ + LOG(INFO)<<"hm_anno_class is not found"; + return false; + } + //public boolean isAnnotationPresent(Class annotationClass) + jmethodID is_anno_present=env->GetMethodID(c_class,"isAnnotationPresent", + "(Ljava/lang/Class;)Z"); + bool hm_present=env->CallBooleanMethod(clazz,is_anno_present,hm_anno_class); + if(!hm_present) + return false; + + //Get instance of @HookModule. + //public A getAnnotation(Class annotationClass) + jmethodID get_anno=get_method_by_name(env,c_class,"getAnnotation"); + if(!get_anno) + return false;//Log will be output in get_method_by_name. + LOG(INFO)<<"before getAnnotation"; + jobject anno=env->CallObjectMethod(clazz,get_anno, + hm_anno_class); + LOG(INFO)<<"anno instance:"<GetStaticMethodID(clazz,"targetPkgName","()Ljava/lang/String;"); + if(TryCatch(env)) {//NoSuchMethod + LOG(INFO)<<"used both way to get targetPkgName but failed,please watch sample module for help"; + return false; + } + target_pkg=(jstring)env->CallStaticObjectMethod(clazz,get_target_pkg); + }else{//Annotation can be used. + jmethodID get_target_pkg=get_method_by_name(env,hm_anno_class,"targetPkgName"); + if(!get_target_pkg) + return false; + target_pkg=(jstring)env->CallObjectMethod(anno,get_target_pkg); + } + if(!target_pkg) + return false; + const char* c_target_pkg=env->GetStringUTFChars(target_pkg, nullptr); + LOG(INFO)<<"target_pkg_name:"<GetStaticMethodID(module_class,"hook","()V"); + if(!TryCatch(env)){//NoSuchMethod + if(!hook_method){ + LOG(INFO)<<"call hook_method in "<CallStaticVoidMethod(module_class,hook_method); + TryCatch(env);//Whether the hook method returns successfully,the procedure need to be continue. + LOG(INFO)<<"hook_method invoked in pid:"<GetStringUTFChars(opt_path, nullptr); + if(!check_dir_rw(c_opt_path)) { + LOG(INFO)<GetArrayLength(dex_files); + //Patch app_classloader to dex_classloader(let modules access app's "special" classes). + whale::dex::patch_class_loader(env, + dex_class_loader, + whale::dex::get_app_classloader(env)); + //Get all dexFiles in dex_class_loader. + for (int i_df = 0; i_df < dex_files_len; ++i_df) { + jobject dex_file_item=env->GetObjectArrayElement(dex_files,i_df); + //Load each classes possible and execute_hook(). + list class_name_list=get_all_class_name(env,dex_file_item, + J_HOOK_MODULE_PKG_PREFIX); + auto itor=class_name_list.begin(); + for (itor=class_name_list.begin();itor!=class_name_list.end();itor++) { + const char* c_class_name=itor->c_str(); + jclass possible_class= whale::dex::load_by_specific_classloader(env,dex_class_loader, + c_class_name); + LOG(INFO)<<"possible_module_class:"<DeleteLocalRef(possible_class);//Prevent crash when l-r-table is overflowed. + } + //Release local references in loop. + env->DeleteLocalRef(dex_file_item); + } + } + + bool JNIHookManager::AddHook(const char *name, jmethodID target, ptr_t replace) { + auto runtime=ArtRuntime::Get(); + + return false; + } +} \ No newline at end of file diff --git a/whale/src/android/hook_module/JNIHookManager.h b/whale/src/android/hook_module/JNIHookManager.h new file mode 100644 index 0000000..118cc58 --- /dev/null +++ b/whale/src/android/hook_module/JNIHookManager.h @@ -0,0 +1,26 @@ +// +// Created by FKD on 19-6-21. +// + +#ifndef WORLD_JNIHOOKMANAGER_H +#define WORLD_JNIHOOKMANAGER_H + + +#include +#include +#include + + +#define J_HOOK_MODULE_ANNO_NAME "com.lody.whale.enity.HookModule" +#define J_HOOK_MODULE_PKG_PREFIX "com.pvdnc" +namespace whale::art { + void load_hook_module(JNIEnv *env,const char* module_dex_path/*Must can be accessed by all apps.*/); + class JNIHookManager { + public: + bool AddHook(const char *name, jmethodID target, ptr_t replace); + private: + std::map hooked_method_map_; + }; +} + +#endif //WORLD_JNIHOOKMANAGER_H diff --git a/whale/src/android/hook_module/README.md b/whale/src/android/hook_module/README.md new file mode 100644 index 0000000..7346880 --- /dev/null +++ b/whale/src/android/hook_module/README.md @@ -0,0 +1,11 @@ +提供静态注入,全局hook,hook模块动态加载等功能 + +Hook模块路径目前写死:'/data/local/tmp/HMTest.dex',一个dex里可以包含多个hook模块 + +静态注入的实现参考 [https://bbs.pediy.com/thread-252266.htm] + +然后把这个项目编译出来的so改个名丢进/system/lib即可 + +没有root权限或者/system不可写的可以改目标app或者在VA框架里hook + +--FKD diff --git a/whale/src/android/hook_module/world.cpp b/whale/src/android/hook_module/world.cpp new file mode 100644 index 0000000..f47e2cb --- /dev/null +++ b/whale/src/android/hook_module/world.cpp @@ -0,0 +1,182 @@ +// +// Created by FKD on 19-6-22. +// + +#include "world.h" +#include "JNIHookManager.h" +#include +#include +#include +#include +#include +#include +#include + +static JavaVM *current_vm= nullptr; +static jint (*get_created_jvms)(JavaVM**, jsize, jsize*)= nullptr; +JavaVM* GetJavaVM() { + if (current_vm) + return current_vm; + + ptr_t libArt = WDynamicLibOpen(kLibArtPath); + + if (!get_created_jvms) { + get_created_jvms = reinterpret_cast + (WDynamicLibSymbol(libArt, "JNI_GetCreatedJavaVMs")); + LOG(INFO) << "get_created_jvms:" << get_created_jvms; + if (!get_created_jvms) + return nullptr; + } + + JavaVM *vms[10] = {nullptr}; + jint vm_count = 0; + get_created_jvms(vms, 10, &vm_count); + LOG(INFO) << "created VMs count:" << vm_count; + if (vm_count <= 0)//There is no JVM created yet. + return nullptr; + current_vm = vms[0];//As for android,there will be only one JavaVM in all zygote's child process. + return current_vm; +} + +static jint (*old_create_jvm)(JavaVM**, JNIEnv**, void*)= nullptr; +extern "C" jint my_create_jvm(JavaVM** p_vm,JNIEnv** p_env,void* vm_args){ + if(p_vm== nullptr&&p_env== nullptr&&vm_args== nullptr){ + LOG(INFO)<<"Are you OK?"; + return JNI_OK; + } + if(old_create_jvm== nullptr){ + LOG(INFO)<<"Fail to get old_create_jvm."; + return JNI_OK; + } + jint ret=old_create_jvm(p_vm,p_env,vm_args);//After this,we have the java world. + //Beginning of the java world. + LOG(INFO)<<"old_create_jvm got return:"<app_class_loader + LOG(INFO)<<"Java World has been owned"; + + /* + JNIEnv *env=*p_env; + jclass obj_class=env->FindClass("java/lang/Object"); + LOG(INFO)<<"java.lang.Object:"<GetStringUTFChars(j_opt_path, nullptr); + jobject dex_class_loader=whale::dex::new_dex_class_loader(env,DEX_PATH,c_opt_path, nullptr); + env->ReleaseStringUTFChars(j_opt_path,c_opt_path); + env->DeleteGlobalRef(j_opt_path);//@see:get_dir's annotation,this is a global reference. + LOG(INFO)<<"dex_class_loader:"<(my_create_jvm), + reinterpret_cast(&old_create_jvm)); + create_jvm_hooked=true; +} + +static jlong findclass_slot=0; +jclass my_findclass(JNIEnv *env,jobject thiz, + jstring class_name,jobject supress_list){ + LOG(INFO)<<"invoke PathList.findClass"; + auto runtime=whale::art::ArtRuntime::Get(); + if(!findclass_slot){ + LOG(INFO)<<"fail to get origin slot of findClass"; + return nullptr; + } + if(!class_name) + return nullptr; + const char* c_class_name=env->GetStringUTFChars(class_name, nullptr); + jobject args[]={class_name,supress_list}; + if(strstr(c_class_name,"lody")!= nullptr&&create_jvm_hooked){ + LOG(INFO)<<"loading whale class:"<InvokeOriginalMethod(findclass_slot,sys_classloader, + runtime->ParseParamArray(env,args,2)); + if(!TryCatch(env)&&result)//Found whale classes in sys_classloader. + return result; + } + jclass origin_class=(jclass)runtime->InvokeOriginalMethod(findclass_slot,thiz, + runtime->ParseParamArray(env,args,2)); + return origin_class; +} + +static bool findclass_hooked=false; +void hook_findclass(){ + if(findclass_hooked)return; + auto runtime=whale::art::ArtRuntime::Get(); + JNIEnv *env= runtime->GetJniEnv(); + jclass cl_class= env->FindClass("java/lang/ClassLoader"); + jmethodID load_class=env->GetMethodID(cl_class,"loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + jstring j_crossdex_class_name=env->NewStringUTF("com.lody.whale.CrossDex"); + jclass crossdex_class= (jclass)env->CallObjectMethod(whale::dex::get_sys_classloader(env),load_class, + j_crossdex_class_name); + if(TryCatch(env))//ClassNotFound + return; + jmethodID hook_method=env->GetStaticMethodID(crossdex_class,"hook","()V"); + env->CallStaticVoidMethod(crossdex_class,hook_method); + LOG(INFO)<<"JNI confrimed hook findClass finished"; + findclass_hooked=true; +} + +static jlong cao_slot=0; +void my_call_app_oncreate(JNIEnv *env,jobject thiz,jobject thisApp){ + const char* pkg_name=env->GetStringUTFChars( + whale::dex::get_pkg_name(env), nullptr); + LOG(INFO)<<"invoke hooked callApplicationOnCreate ("<ParseParamArray(env,args_array,1); + //Patch app_classloader to let module use WhaleRuntime freely(avoid ugly reflections). + WhaleRuntime_handleCallAppOnCreate(env, nullptr/*since we do not use it.*/); + whale::art::load_hook_module(env,"/data/local/tmp/HMTest.dex"); + LOG(INFO)<<"pass load_hook_module"; + //Allow targetPkgName app to start running its own code. + runtime->InvokeOriginalMethod(cao_slot,thiz,params); +} + +static bool cao_hooked=false; +void hook_call_app_oncreate(){ + if(cao_hooked)return; + auto runtime=whale::art::ArtRuntime::Get(); + JNIEnv *env= runtime->GetJniEnv(); + jclass in_class=env->FindClass("android/app/Instrumentation"); + //public void callApplicationOnCreate(Application app) + jmethodID cao_method=env->GetMethodID(in_class, + "callApplicationOnCreate", + "(Landroid/app/Application;)V"); + cao_slot=runtime->HookMethodJNI(env, in_class, + env->ToReflectedMethod(in_class,cao_method,false), + reinterpret_cast(my_call_app_oncreate)); + LOG(INFO)<<"cao_slot:"< + +JavaVM* GetJavaVM(); +void hook_create_jvm(); +void hook_findclass(); +void hook_call_app_oncreate(); +#endif //WORLD_WORLD_H diff --git a/whale/src/android/jni_helper.h b/whale/src/android/jni_helper.h index b2c6545..e4d8c98 100644 --- a/whale/src/android/jni_helper.h +++ b/whale/src/android/jni_helper.h @@ -27,11 +27,13 @@ static inline bool JNIExceptionCheck(JNIEnv *env) { return false; } -static inline void JNIExceptionClearAndDescribe(JNIEnv *env) { +static inline bool TryCatch(JNIEnv *env) { if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); + return true; } + return false; } template diff --git a/whale/src/base/align.h b/whale/src/base/align.h index 364cc6c..2a48f46 100644 --- a/whale/src/base/align.h +++ b/whale/src/base/align.h @@ -17,7 +17,7 @@ // When you use implicit_cast, the compiler checks that the cast is safe. // Such explicit implicit_casts are necessary in surprisingly many // situations where C++ demands an exact type match instead of an -// argument type convertible to a target type. +// argument type convertible to a targetPkgName type. // // The From type can be inferred, so the preferred syntax for using // implicit_cast is the same as for static_cast etc.: diff --git a/whale/src/base/cxx_helper.h b/whale/src/base/cxx_helper.h index 5835a79..cd11f7f 100644 --- a/whale/src/base/cxx_helper.h +++ b/whale/src/base/cxx_helper.h @@ -2,7 +2,7 @@ #define WHALE_BASE_CXX_HELPER_H_ #include -#include "base/primitive_types.h" +#include "primitive_types.h" template U ForceCast(T *x) { @@ -50,4 +50,13 @@ static inline void AssignOffset(ptr_t ptr, size_t offset, T member) { *OffsetOf(ptr, offset) = member; } +template +static inline offset_t GetOffsetByValue(ptr_t obj, T expected_value){ + for (offset_t offset = 0; offset != sizeof(T) * 24; offset += sizeof(T)) { + if (MemberOf(obj, offset) == expected_value) { + return offset; + } + } + return INT32_MAX; +} #endif // WHALE_BASE_CXX_HELPER_H_ diff --git a/whale/src/base/process_util.cpp b/whale/src/base/process_util.cpp new file mode 100644 index 0000000..58343fa --- /dev/null +++ b/whale/src/base/process_util.cpp @@ -0,0 +1,42 @@ +// +// Created by FKD on 19-6-28. +// + +#include +#include "process_util.h" +#include "../base/logging.h" +using namespace std; +#define BUF_SIZE 1024 +#define FAIL_PROCESS_NAME "" + +string get_name_by_pid(pid_t pid) { + char buf[BUF_SIZE]; + char cmd[100] = {'\0'}; + sprintf(cmd, "cat /proc/%d/cmdline", pid); + FILE *fp=popen(cmd, "r"); + if (fp == nullptr) + return FAIL_PROCESS_NAME; + if (fgets(buf, BUF_SIZE, fp) == nullptr) + return FAIL_PROCESS_NAME; + LOG(INFO)<<"process:"<=0; +} diff --git a/whale/src/base/process_util.h b/whale/src/base/process_util.h new file mode 100644 index 0000000..672ea9c --- /dev/null +++ b/whale/src/base/process_util.h @@ -0,0 +1,12 @@ +// +// Created by FKD on 19-6-28. +// +#pragma once +#ifndef WORLD_PROCESS_UTIL_H +#define WORLD_PROCESS_UTIL_H + +#include +std::string get_name_by_pid(pid_t pid); +void get_name_by_pid(pid_t pid,std::string &process_name); +bool check_dir_rw(const char* path); +#endif //WORLD_PROCESS_UTIL_H diff --git a/whale/src/base/str_util.cpp b/whale/src/base/str_util.cpp new file mode 100644 index 0000000..d0b1f6e --- /dev/null +++ b/whale/src/base/str_util.cpp @@ -0,0 +1,10 @@ +// +// Created by FKD on 19-6-29. +// + +#include +#include "str_util.h" + +bool Contains(const char *u, const char *sub_str) { + return (strstr(u,sub_str)!= nullptr); +} diff --git a/whale/src/base/str_util.h b/whale/src/base/str_util.h new file mode 100644 index 0000000..ea5a1d1 --- /dev/null +++ b/whale/src/base/str_util.h @@ -0,0 +1,8 @@ +// +// Created by FKD on 19-6-29. +// +#pragma once +#ifndef WORLD_STR_UTIL_H +#define WORLD_STR_UTIL_H +bool Contains(const char *u, const char *sub_str); +#endif //WORLD_STR_UTIL_H