簡(jiǎn)介
- 所有的加固代碼 都需要通過(guò)Classloader加載然后才可以執(zhí)行
classloader介紹
-
雙親委派機(jī)制
雙親委派模式的工作原理的是;如果一個(gè)類加載器收到了類加載請(qǐng)求擦囊,它并不會(huì)自己先去加載麻养,而是把這個(gè)請(qǐng)求委托給父類的加載器去執(zhí)行
如果父類加載器還存在其父類加載器,則進(jìn)一步向上委托侦高,依次遞歸械馆,請(qǐng)求最終將到達(dá)頂層的啟動(dòng)類加載器
如果父類加載器可以完成類加載任務(wù)胖眷,就成功返回,倘若父類加載器無(wú)法完成此加載任務(wù)霹崎,子加載器才會(huì)嘗試自己去加載珊搀,這就是雙親委派模式
即每個(gè)兒子都不愿意干活,每次有活就丟給父親去干尾菇,直到父親說(shuō)這件事我也干不了時(shí)境析,兒子自己想辦法去完成,這個(gè)就是雙親委派派诬。 -
Android中的classloader
ClassLoader為抽象類劳淆;
BootClassLoader預(yù)加載常用類,單例模式千埃。
BaseDexClassLoader是PathClassLoader憔儿、DexClassLoader、InMemoryDexClassLoader的父類放可,類加載的主要邏輯都是在BaseDexClassLoader完成的谒臼。
SecureClassLoader繼承了抽象類ClassLoader,拓展了ClassLoader類加入了權(quán)限方面的功能耀里,加強(qiáng)了安全性蜈缤,其子類URLClassLoader是用URL路徑從jar文件中加載類和資源。
其中重點(diǎn)關(guān)注的是PathClassLoader和DexClassLoader冯挎。
PathClassLoader是Android默認(rèn)使用的類加載器底哥,一個(gè)apk中的Activity等類便是在其中加載。
DexClassLoader可以加載任意目錄下的dex/jar/apk/zip文件房官,比PathClassLoader更靈活趾徽,是實(shí)現(xiàn)插件化、熱修復(fù)以及dex加殼的重點(diǎn)翰守。
Android8.0新引入InMemoryDexClassLoader孵奶,從名字便可看出是用于直接從內(nèi)存中加載dex。
APP 的啟動(dòng)流程
APP類加載過(guò)程
- BootClassLoader加載系統(tǒng)核心庫(kù)
- PathClassLoader加載APP自身dex
- 進(jìn)入APP自身組件開始執(zhí)行蜡峰,調(diào)用聲明Application的attachBaseContext了袁,onCreate
- AppThread中有LoadedApk對(duì)象。
- AppThread handledBindApplication(應(yīng)該是AMS 回調(diào)的ApplicationThread)中湿颅,第一次進(jìn)入到App自身的代碼中去载绿。
- Application attach -> oncreate最先被執(zhí)行,如果加殼的話油航,此時(shí)的classloader只有殼的代碼 沒有自身代碼
- 如果不處理classloader的話崭庸,只有殼代碼,是無(wú)法啟動(dòng)注冊(cè)的Activity Service等的谊囚。
*<mark> 所以殼代碼需要在這里加載真正的代碼</mark>
組件生命周期的處理
DexClassLoader加載的類是沒有組件生命周期的怕享,也就是說(shuō)即使DexClassLoader通過(guò)對(duì)APK的動(dòng)態(tài)加載完成了對(duì)組件類的加載,當(dāng)系統(tǒng)啟動(dòng)該組件時(shí)秒啦,依然會(huì)出現(xiàn)加載類失敗的異常熬粗。
需要替換系統(tǒng)組件的classloader才可以。加固廠商必然饒不過(guò)去
兩種解決方案:
1余境、替換系統(tǒng)組件類加載器為我們的DexClassLoader驻呐,同時(shí)設(shè)置DexClassLoader的parent為系統(tǒng)組件類加載器;
//classloader 已經(jīng)加載了 自身代碼 并且parent是原始的classloader
public void replaceClassloader(ClassLoader classloader){
try {
Class<?> ActivityThreadClazz=classloader.loadClass("android.app.ActivityThread");
Method currentActivityThreadMethod= ActivityThreadClazz.getDeclaredMethod("currentActivityThread");
currentActivityThreadMethod.setAccessible(true);
Object activityThreadObj=currentActivityThreadMethod.invoke(null);
//final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>();
Field mPackagesField=ActivityThreadClazz.getDeclaredField("mPackages");
mPackagesField.setAccessible(true);
ArrayMap mPackagesObj= (ArrayMap) mPackagesField.get(activityThreadObj);
WeakReference wr= (WeakReference) mPackagesObj.get(this.getPackageName());
Object loadedApkObj=wr.get();
Class LoadedApkClazz=classloader.loadClass("android.app.LoadedApk");
//private ClassLoader mClassLoader;
Field mClassLoaderField=LoadedApkClazz.getDeclaredField("mClassLoader");
mClassLoaderField.setAccessible(true);
mClassLoaderField.set(loadedApkObj,classloader);
} catch (Exception e) {
e.printStackTrace();
}
}
2芳来、打破原有的雙親關(guān)系含末,在系統(tǒng)組件類加載器和BootClassLoader的中間插入我們自己的DexClassLoader即可;
public void startTestActivitySecondMethod(Context context,String dexfilepath){
File optfile=context.getDir("opt_dex",0);
File libfile=context.getDir("lib_path",0);
ClassLoader pathClassloader=MainActivity.class.getClassLoader();
ClassLoader bootClassloader=MainActivity.class.getClassLoader().getParent();
DexClassLoader dexClassLoader=new DexClassLoader(dexfilepath,optfile.getAbsolutePath(),libfile.getAbsolutePath(),bootClassloader);
try {
Field parentField=ClassLoader.class.getDeclaredField("parent");
parentField.setAccessible(true);
parentField.set(pathClassloader,dexClassLoader);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
dex整體加密型加固
- 將Dex整體加密即舌,在需要的時(shí)候解密佣盒,通過(guò)Classloader加入內(nèi)存中。
關(guān)鍵代碼
public class TestActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//com.example.myapplication.TestClass.testFunc
Context appContext=this.getApplicationContext();
//testDexClassLoader(appContext,"/sdcard/3.dex");
//startTestActivity(this,"/sdcard/4.dex");
startTestActivitySecondMethod(this,"/sdcard/6.dex");
}
//替換LoadedApk中的mClassLoader 加載自身代碼
public void replaceClassloader(ClassLoader classloader){
try {
Class<?> ActivityThreadClazz=classloader.loadClass("android.app.ActivityThread");
Method currentActivityThreadMethod= ActivityThreadClazz.getDeclaredMethod("currentActivityThread");
currentActivityThreadMethod.setAccessible(true);
Object activityThreadObj=currentActivityThreadMethod.invoke(null);
//final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>();
Field mPackagesField=ActivityThreadClazz.getDeclaredField("mPackages");
mPackagesField.setAccessible(true);
ArrayMap mPackagesObj= (ArrayMap) mPackagesField.get(activityThreadObj);
WeakReference wr= (WeakReference) mPackagesObj.get(this.getPackageName());
Object loadedApkObj=wr.get();
Class LoadedApkClazz=classloader.loadClass("android.app.LoadedApk");
//private ClassLoader mClassLoader;
Field mClassLoaderField=LoadedApkClazz.getDeclaredField("mClassLoader");
mClassLoaderField.setAccessible(true);
mClassLoaderField.set(loadedApkObj,classloader);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
public void startTestActivityFirstMethod(Context context,String dexfilepath){
File optfile=context.getDir("opt_dex",0);
File libfile=context.getDir("lib_path",0);
ClassLoader parentClassloader=MainActivity.class.getClassLoader();
ClassLoader tmpClassLoader=context.getClassLoader();
DexClassLoader dexClassLoader=new DexClassLoader(dexfilepath,optfile.getAbsolutePath(),libfile.getAbsolutePath(),MainActivity.class.getClassLoader());
replaceClassloader(dexClassLoader);
Class<?> clazz=null;
try {
clazz = dexClassLoader.loadClass("com.example.myapplication.TestActivity");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
context.startActivity(new Intent(context,clazz));
}
public void startTestActivitySecondMethod(Context context,String dexfilepath){
File optfile=context.getDir("opt_dex",0);
File libfile=context.getDir("lib_path",0);
ClassLoader pathClassloader=MainActivity.class.getClassLoader();
ClassLoader bootClassloader=MainActivity.class.getClassLoader().getParent();
//加載dex 這里直接加載了未加密的顽聂,可以解密后 再加載肥惭,然后刪除盯仪。也可以替換為memoryclassloader加載 ,實(shí)現(xiàn)不落地加載
DexClassLoader dexClassLoader=new DexClassLoader(dexfilepath,optfile.getAbsolutePath(),libfile.getAbsolutePath(),bootClassloader);
try {
//插入我們自己的dexClassLoader 加入生命周期的處理
Field parentField=ClassLoader.class.getDeclaredField("parent");
parentField.setAccessible(true);
parentField.set(pathClassloader,dexClassLoader);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
/*
* this:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.xyh.loaddex-8_fCxispeBuExjw1ryrRZg==/base.apk"],nativeLibraryDirectories=[/data/app/com.xyh.loaddex-8_fCxispeBuExjw1ryrRZg==/lib/arm64, /system/lib64, /vendor/lib64]]]--parent:dalvik.system.DexClassLoader[DexPathList[[dex file "/sdcard/6.dex"],nativeLibraryDirectories=[/data/user/0/com.xyh.loaddex/app_lib_path, /system/lib64, /vendor/lib64]]]
this:dalvik.system.DexClassLoader[DexPathList[[dex file "/sdcard/6.dex"],nativeLibraryDirectories=[/data/user/0/com.xyh.loaddex/app_lib_path, /system/lib64, /vendor/lib64]]]--parent:java.lang.BootClassLoader@fd4323d
root:java.lang.BootClassLoader@fd4323d*/
ClassLoader tmpClassloader=pathClassloader;
ClassLoader parentClassloader=pathClassloader.getParent();
while(parentClassloader!=null){
Log.i("xyh","this:"+tmpClassloader+"--parent:"+parentClassloader);
tmpClassloader=parentClassloader;
parentClassloader=parentClassloader.getParent();
}
Log.i("xyh","root:"+tmpClassloader);
Class<?> clazz=null;
try {
clazz = dexClassLoader.loadClass("com.example.myapplication.TestActivity");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
context.startActivity(new Intent(context,clazz));
}
}
加固類型-函數(shù)抽取
Dalvik指令抽取原理介紹
Android中實(shí)現(xiàn)「類方法指令抽取方式」加固方案原理解析
Android免Root權(quán)限通過(guò)Hook系統(tǒng)函數(shù)修改程序運(yùn)行時(shí)內(nèi)存指令邏輯
- 在函數(shù)粒度上保護(hù)代碼
- 將函數(shù)指令全部替換為Nop或無(wú)效指令蜜葱,在執(zhí)行時(shí)還原代碼全景。
- 主要原理是將原指令置為nop,然后hook,dexFindClass牵囤,找到原始的方法指令指針爸黄,DexCode->insns,替換insns揭鳞。
Art 抽取殼介紹
Art下實(shí)現(xiàn)難點(diǎn):dex2oat編譯流程炕贵,dex2oat是可以進(jìn)行脫殼,dex2oat完成了對(duì)抽取的dex進(jìn)行編譯生成了oat文件野崇,后續(xù)的函數(shù)運(yùn)行中称开,從oat中取出函數(shù)編譯生成的二進(jìn)制代碼來(lái)執(zhí)行,因此函數(shù)對(duì)dex填充后舞骆,如果時(shí)機(jī)不對(duì)钥弯,時(shí)機(jī)在dex2oat后记餐,自然從dex2oat后那么我們動(dòng)態(tài)修改的dex中的smali指令流就不會(huì)生效针饥,因?yàn)楹竺鎍pp運(yùn)行調(diào)用的真正的代碼就會(huì)從dex2oat編譯生成的oat文件,和以前的dex無(wú)關(guān)了滞项。因此如果希望填充回去smali指令生效要么禁用dex2oat實(shí)現(xiàn)阻止編譯狈惫,這樣對(duì)加載到內(nèi)存中的dex文件進(jìn)行填充始終會(huì)保持生效睛蛛,要么保持dex2oat編譯,但是還原代碼時(shí)機(jī)要早于dex2oat就ok了胧谈,保證dex2oat再次對(duì)dex編譯的時(shí)候忆肾,dex已經(jīng)是一個(gè)完整dex,不會(huì)影響我們填充的代碼菱肖,但是肯定dex文件存在完整的時(shí)候客冈,可以利用dex2oat編譯的流程進(jìn)行脫殼,一般加殼廠商都是犧牲掉app一部分的運(yùn)行效率稳强,干掉dex2oat的過(guò)程场仲,因?yàn)間oogle本身提倡dex2oat就是為了提升app運(yùn)行效率。
<mark>ART模式下抽取殼 要么阻止dex2oat 要么在dex2oat之前保證加載的dex是一個(gè)完整的dex</mark>
如果選擇第一種方案 , 在 dex2oat 之前進(jìn)行恢復(fù) , 這沒有任何意義 , dex2oat 編譯后 , 生成的 oat 文件是完整的 , 此時(shí) 可以 完整的將 oat 文件 dump 到 SD 卡中 , 基本等于沒有加固 , 還是一個(gè)一代殼 ;
因此 , 大部分加固廠商 , 選擇 禁用 dex2oat 機(jī)制 ; 這樣處于安全考慮 , 犧牲了應(yīng)用的運(yùn)行效率 ;
- classloader加載dex的時(shí)候 會(huì)進(jìn)行dex2oat的優(yōu)化退疫,將dex的解釋執(zhí)行渠缕,變成直接執(zhí)行,提升運(yùn)行效率褒繁。
- 7.0之后會(huì)根據(jù)函數(shù)使用頻率進(jìn)行dex2oat的優(yōu)化亦鳞,并不會(huì)直接全部dex2oat
https://source.android.google.cn/devices/tech/dalvik/configure
ART 使用預(yù)先 (AOT) 編譯,并且從 Android 7.0(代號(hào) Nougat,簡(jiǎn)稱 N)開始結(jié)合使用 AOT燕差、即時(shí) (JIT) 編譯和配置文件引導(dǎo)型編譯遭笋。所有這些編譯模式的組合均可配置,我們將在本部分中對(duì)此進(jìn)行介紹谁不。例如坐梯,Pixel 設(shè)備配置了以下編譯流程:
7.0之后最初安裝應(yīng)用時(shí)不進(jìn)行任何 AOT 編譯徽诲。應(yīng)用前幾次運(yùn)行時(shí)刹帕,系統(tǒng)會(huì)對(duì)其進(jìn)行解譯,并對(duì)經(jīng)常執(zhí)行的方法進(jìn)行 JIT 編譯谎替。
當(dāng)設(shè)備閑置和充電時(shí)偷溺,編譯守護(hù)程序會(huì)運(yùn)行,以便根據(jù)在應(yīng)用前幾次運(yùn)行期間生成的配置文件對(duì)常用代碼進(jìn)行 AOT 編譯钱贯。
下一次重新啟動(dòng)應(yīng)用時(shí)將會(huì)使用配置文件引導(dǎo)型代碼挫掏,并避免在運(yùn)行時(shí)對(duì)已經(jīng)過(guò)編譯的方法進(jìn)行 JIT 編譯。在應(yīng)用后續(xù)運(yùn)行期間經(jīng)過(guò) JIT 編譯的方法將會(huì)添加到配置文件中秩命,然后編譯守護(hù)程序?qū)?huì)對(duì)這些方法進(jìn)行 AOT 編譯尉共。
ART 的運(yùn)作方式 在 Android O 版本中,將會(huì)生成以下文件:
.vdex:其中包含 APK 的未壓縮 DEX 代碼弃锐,以及一些旨在加快驗(yàn)證速度的元數(shù)據(jù)袄友。
使用010editer就可以找到dex
.odex:其中包含 APK 中已經(jīng)過(guò) AOT 編譯的方法代碼。
oatdump 可以反編譯odex文件霹菊,可以dump出函數(shù)的smali指令
.art (optional):其中包含 APK 中列出的某些字符串和類的 ART 內(nèi)部表示剧蚣,用于加快應(yīng)用啟動(dòng)速度。
全部進(jìn)行oat優(yōu)化的話旋廷,非常耗時(shí)鸠按,所以7.0之后選取了保守的優(yōu)化策略
odex 優(yōu)化后,oatdump還是可以dump出原始的smali指令饶碘,只是優(yōu)化后的函數(shù) 在CODE段目尖,會(huì)有優(yōu)化后的二進(jìn)制匯編指令,在執(zhí)行的時(shí)候會(huì)直接進(jìn)入quick模式扎运,執(zhí)行匯編指令瑟曲,加快運(yùn)行速度。具有CODE段后绪囱,將不再進(jìn)入解釋執(zhí)行模式测蹲。
- art抽取殼必須在dex2oat之前還原,否則dex2oat之后鬼吵,就無(wú)法還原扣甲。要么阻止dex2oat 要么提前還原
- 首先要干掉Dex2oat的過(guò)程。可以參考github TurboDex(快速加載dex 阻止dex2oat) dex2oat 非常耗時(shí)琉挖;不進(jìn)行oat的話 會(huì)影響dex的運(yùn)行效率启泣。
- 阻止了dex2oat后 所有代碼都是解釋執(zhí)行了。那么必然會(huì)去內(nèi)存中尋找ArtMethod的code_item去執(zhí)行
- 那么在art類加載的時(shí)候還原code_item就可以正常去執(zhí)行了
- 剩下的和dalvik基本一致
- 1示辈、先通過(guò)010edite將一個(gè)函數(shù)全部抽取為nop
- 2寥茫、hook art loadmethod的過(guò)程
- 3、將原始的指令還原到artmethod中
ART的類加載執(zhí)行流程
強(qiáng)烈建議閱讀此文章
ART 在 Android 安全攻防中的應(yīng)用
關(guān)鍵代碼
#include <jni.h>
#include <string>
#include <unistd.h>
#include <android/log.h>
#include <fcntl.h>
#include <asm/fcntl.h>
#include <sys/mman.h>
#include <dlfcn.h>
//import c header
extern "C" {
#include "hook/dlfcn/dlfcn_compat.h"
#include "hook/include/inlineHook.h"
}
typedef unsigned char byte;
#define TAG "SecondShell"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
struct DexFile {
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
// The class we are a part of.
uint32_t declaring_class_;
// Access flags; low 16 bits are defined by spec.
void *begin;
/* Dex file fields. The defining dex file is available via declaring_class_->dex_cache_ */
// Offset to the CodeItem.
uint32_t size;
};
struct ArtMethod {
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
// The class we are a part of.
uint32_t declaring_class_;
// Access flags; low 16 bits are defined by spec.
uint32_t access_flags_;
/* Dex file fields. The defining dex file is available via declaring_class_->dex_cache_ */
// Offset to the CodeItem.
uint32_t dex_code_item_offset_;
// Index into method_ids of the dex file associated with this method.
uint32_t dex_method_index_;
};
void* *(*oriexecve)(const char *__file, char *const *__argv, char *const *__envp);
void* *myexecve(const char *__file, char *const *__argv, char *const *__envp) {
LOGD("process:%d,enter execve:%s", getpid(), __file);
if (strstr(__file, "dex2oat")) {
return NULL;
} else {
return oriexecve(__file, __argv, __envp);
}
}
//void ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file, const ClassDataItemIterator& it,Handle<mirror::Class> klass, ArtMethod* dst)
void *(*oriloadmethod)(void *, void *, void *, void *, void *);
void *myloadmethod(void *a, void *b, void *c, void *d, void *e) {
LOGD("process:%d,before run loadmethod:", getpid());
struct ArtMethod *artmethod = (struct ArtMethod *) e;
struct DexFile *dexfile = (struct DexFile *) b;
LOGD("process:%d,enter loadmethod:dexfilebegin:%p,size:%d", getpid(), dexfile->begin,
dexfile->size);//0,57344
char dexfilepath[100] = {0};
sprintf(dexfilepath, "/sdcard/%d_%d.dex", dexfile->size, getpid());
int fd = open(dexfilepath, O_CREAT | O_RDWR, 0666);
if (fd > 0) {
write(fd, dexfile->begin, dexfile->size);
close(fd);
}
void *result = oriloadmethod(a, b, c, d, e);
LOGD("process:%d,enter loadmethod:code_offset:%d,idx:%d", getpid(),
artmethod->dex_code_item_offset_, artmethod->dex_method_index_);
byte *code_item_addr = static_cast<byte *>(dexfile->begin) + artmethod->dex_code_item_offset_;
LOGD("process:%d,enter loadmethod:dexfilebegin:%p,size:%d,beforedumpcodeitem:%p", getpid(),
dexfile->begin, dexfile->size, code_item_addr);
if (artmethod->dex_method_index_ == 15203) {//TestClass.testFunc->methodidx
LOGD("process:%d,enter loadmethod:dexfilebegin:%p,size:%d,start repire method", getpid(),
dexfile->begin, dexfile->size);
byte *code_item_addr = (byte *) dexfile->begin + artmethod->dex_code_item_offset_;
LOGD("process:%d,enter loadmethod:dexfilebegin:%p,size:%d,beforedumpcodeitem:%p", getpid(),
dexfile->begin, dexfile->size, code_item_addr);
int result = mprotect(dexfile->begin, dexfile->size, PROT_WRITE);
byte *code_item_start = static_cast<byte *>(code_item_addr) + 16;
LOGD("process:%d,enter loadmethod:dexfilebegin:%p,size:%d,code_item_start:%p", getpid(),
dexfile->begin, dexfile->size, code_item_start);
byte inst[16] = {0x1a, 0x00, 0xed, 0x34, 0x1a, 0x01, 0x43, 0x32, 0x71, 0x20, 0x91, 0x05,
0x10, 0x00, 0x0e, 0x00};
for (int i = 0; i < sizeof(inst); i++) {
code_item_start[i] = inst[i];
}
//2343->i am from com.kanxue.test02.TestClass.testFunc
code_item_start[2] = 0x43;//34ed->kanxue
code_item_start[3] = 0x23;
memset(dexfilepath, 0, 100);
sprintf(dexfilepath, "/sdcard/%d_%d.dex_15203_2", dexfile->size, getpid());
fd = open(dexfilepath, O_CREAT | O_RDWR, 0666);
if (fd > 0) {
write(fd, dexfile->begin, dexfile->size);
close(fd);
}
}
LOGD("process:%d,after loadmethod:code_offset:%d,idx:%d", getpid(),
artmethod->dex_code_item_offset_, artmethod->dex_method_index_);//0,57344
return result;
}
void hooklibc() {
LOGD("go into hooklibc");
//7.0 命名空間限制
void *libc_addr = dlopen_compat("libc.so", RTLD_NOW);
void *execve_addr = dlsym_compat(libc_addr, "execve");
if (execve_addr != NULL) {
if (ELE7EN_OK == registerInlineHook((uint32_t) execve_addr, (uint32_t) myexecve,
(uint32_t **) &oriexecve)) {
if (ELE7EN_OK == inlineHook((uint32_t) execve_addr)) {
LOGD("inlineHook execve success");
} else {
LOGD("inlineHook execve failure");
}
}
}
}
void hookART() {
LOGD("go into hookART");
void *libart_addr = dlopen_compat("/system/lib/libart.so", RTLD_NOW);
if (libart_addr != NULL) {
void *loadmethod_addr = dlsym_compat(libart_addr,
"_ZN3art11ClassLinker10LoadMethodERKNS_7DexFileERKNS_21ClassDataItemIteratorENS_6HandleINS_6mirror5ClassEEEPNS_9ArtMethodE");
if (loadmethod_addr != NULL) {
if (ELE7EN_OK == registerInlineHook((uint32_t) loadmethod_addr, (uint32_t) myloadmethod,
(uint32_t **) &oriloadmethod)) {
if (ELE7EN_OK == inlineHook((uint32_t) loadmethod_addr)) {
LOGD("inlineHook loadmethod success");
} else {
LOGD("inlineHook loadmethod failure");
}
}
}
}
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_kanxue_secondshell_180_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "ART SecondShell";
return env->NewStringUTF(hello.c_str());
}
extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_secondshell_180_MainActivity_SecondShell(
JNIEnv *env,
jobject /* this */) {
hooklibc();//hook execve 禁止執(zhí)行dex2oat
hookART();//hook LoadMethod 修復(fù)被抽取的artmethod,并在修復(fù)前修復(fù)后dumpdex
return;
}
加固類型-VMP矾麻,Dex2C
- VMP 自定義解釋器纱耻,解釋執(zhí)行自定義的指令。
- Dex2c 在編譯時(shí)险耀,將java代碼編譯成c代碼 增加破解難度
- 未來(lái)加固的主要方向->VMP弄喘,Dex2C
- VMP 會(huì)把java函數(shù)native化,原理:VMP是做虛擬機(jī)解釋器
- VMP的多個(gè)native函數(shù) 很可能是一個(gè)地址甩牺。多個(gè)native函數(shù) 都是調(diào)用的一個(gè)地方
- 破解需要找到解釋器蘑志,找到smaili的映射關(guān)系即可恢復(fù)
- github 搜索 ADVMP 可以參考 沒具體研究
- Dex2c 開源項(xiàng)目 dcc,原理:對(duì)java語(yǔ)義分析贬派,重新生成native函數(shù)急但。
- python dcc.py dcc.apk -o dcc_out.apk 使用DCC增加保護(hù),java函數(shù)會(huì)變成native函數(shù)搞乏。 只建議部分函數(shù)增加保護(hù)波桩,否則會(huì)嚴(yán)重影響性能
- dcc可以將APK的java代碼 轉(zhuǎn)化為native代碼,并且可以增加ollvm的混淆
- dcc的native注冊(cè)函數(shù) 基本是一一對(duì)應(yīng)
- 基本不太好破解查描。突委。
- VMP 會(huì)把java函數(shù)native化,原理:VMP是做虛擬機(jī)解釋器