pollux's Dairy

FUPK源码阅读

字数统计: 804阅读时长: 5 min
2019/12/15 Share

1、获得完整的DEX

frameworks/base/core/java/android/app/ActivityThread.javahandleBindApplication处插桩

1
2
3
4
5
6
7
8
try {
UpkConfig config = new UpkConfig();
if (config.load() && config.mTargetPackage.equals(data.info.getPackageName())) {
Fupk upk = new Fupk(config.mTargetPackage);
upk.unpackAfter(10000);
}
} catch (Throwable t) {
}

创建Fupk的实例,创建实例过程加载so文件,然后调用其unpackAfter函数,其中调用unpackNow初始化其类变量appLoader,最后调用native函数core_Fupk::unpackAll

1
2
3
4
5
public final static String hookSo = "/data/local/tmp/libFupk3.so";

static {
System.load(Global.hookSo);
}

该so文件存在JNI_OnLoad函数,在app/src/main/cpp/main.cpp中,主要做了两部分内容:1、动态注册native函数core_Fupk::unpackAll;2、通过dlopen、dlsym函数获得libdvm.so中的接口(函数指针)

1
core_Fupk::registerNativeMethod(env)

回到core_Fupk::unpackAll,该函数首先将dump方法的函数绑定到app/src/main/cpp/DexHelper/DexDumper.cpp中的fupk_ExportMethod中

然后调用了app/src/main/cpp/DexHelper/Fupk.cpp中的对象的unpackAll方法

1
2
3
4
void ::core_Fupk::unpackAll(JNIEnv *env, jobject obj, jstring folder) {
interface->ExportMethod = fupk_ExportMethod;
Fupk upk(env, sFolder, obj);
upk.unpackAll();

在实例化Fupk对象时,通过自定义接口dlsym(libdvm, "dvmGetUserDexFiles");获得gDvm.userDexFiles返回的HashTable,从中提取DvmDex信息

1
auto dvmDex = mCookie.getCookieAt(i, name, mIgnoreCase);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
struct DvmDex {
/* pointer to the DexFile we're associated with */
DexFile* pDexFile;

/* clone of pDexFile->pHeader (it's used frequently enough) */
const DexHeader* pHeader;

/* interned strings; parallel to "stringIds" */
struct StringObject** pResStrings;

/* resolved classes; parallel to "typeIds" */
struct ClassObject** pResClasses;

/* resolved methods; parallel to "methodIds" */
struct Method** pResMethods;

/* resolved instance fields; parallel to "fieldIds" */
/* (this holds both InstField and StaticField) */
struct Field** pResFields;

/* interface method lookup cache */
struct AtomicCache* pInterfaceCache;

/* shared memory region with file contents */
bool isMappedReadOnly;
MemMapping memMap;

jobject dex_object;

/* lock ensuring mutual exclusion during updates */
pthread_mutex_t modLock; // TODO import
};

这样就获得了内存中完整的Dex,下面开始主动调用方法

1
2
DexDumper dumper(mEnv, dvmDex, mUpkObj);
dumper.rebuild();//dump方法体@cs

这个mUpkObj是内核中Fupk的实例,创建DexDumper实例后,开始主动调用其方法

1
2
3
4
5
//app/src/main/cpp/DexHelper/DexDumper.cpp
bool DexDumper::rebuild() {
jclass upkClazz = mEnv->GetObjectClass(mUpkObj);
auto loaderObject = JniInfo::GetObjectField(mEnv, mUpkObj, "appLoader", "Ljava/lang/ClassLoader;");
auto tryLoadClass_method = mEnv->GetMethodID(upkClazz, "tryLoadClass", "(Ljava/lang/String;)Ljava/lang/Class;");

2、类的主动加载

  1. 获得内核Fupk实例的Class
  2. 获得ClassLoader实例systemClassLoader
  3. 获得tryLoadClass方法id
1
auto jClazz = mEnv->CallObjectMethod(mUpkObj, tryLoadClass_method, jDotDescriptor);//通过tryLoadClass获取类对象

参数jDotDescriptor即为获得的类签名

1
2
3
4
5
6
7
8
9
// I will load the class from java(the loader may has been replaced)
public Class tryLoadClass(String className) {
try {
return FRefInvoke.getClass(appLoader, className);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
1
2
3
4
5
6
7
8
9
10
public static Class getClass(ClassLoader loader, String class_name) throws Exception{
if (loader != null) {
try {
return loader.loadClass(class_name);//主动加载@cs
} catch (Exception e){
// class not found from loader, used Class.forName instead
}
}
return Class.forName(class_name);
}

通过tryLoadClass函数,使用systemClassLoader去主动加载这个类

3、主动调用方法,dump方法体

回到native层

后面通过DexDumper::fixMethodByDvm主动调用方法

1
2
3
4
5
6
7
8
9
10
11
12
bool DexDumper::fixMethodByDvm(DexSharedData &shared, DexMethod *dexMethod,
ClassDefBuilder* builder, u4 &lastIndex) {
lastIndex = lastIndex + dexMethod->methodIdx;
auto m = builder->getMethodMap(lastIndex);

assert(m != nullptr && "Unable to fix MethodBy Dvm, this should happened");

shared.mCurMethod = dexMethod;
FupkImpl::fupkInvokeMethod(m);//调用方法@cs
shared.mCurMethod = nullptr;
return true;
}

FupkImpl::fupkInvokeMethod(m)即为

app/src/main/cpp/DexHelper/DexDumper.cpp中的fupk_ExportMethod方法

该方法插桩到了内核中的invoke函数中

在fupk_ExportMethod中,将方法指针保存在了shared变量中

1
shared->extra.append((char*)item, code_item_len);//保存方法体@cs
CATALOG
  1. 1. 1、获得完整的DEX
  2. 2. 2、类的主动加载
  3. 3. 3、主动调用方法,dump方法体