其他
使用Frida简单实现函数粒度脱壳
本文为看雪论坛优秀文章
看雪论坛作者ID:无造
本文为看雪安卓高研2w班(6月班)优秀学员作品。
下面先让我们来看看讲师对学员学习成果的点评,以及学员的学习心得吧!
不管是frida脚本的编写,还是Xposed插件的开发,ClassLoader都是绕不开的必须掌握的知识点。
而对于dex中类列表的获取,最根本的还是通过获取到DexFile对象以后,自行解析其中的类列表,这就需要对dex文件的结构有着非常清楚的认识。
在脱壳的过程中,对于任何ART支持的Android系统,只要知道了ArtMethod对象是贯穿app中的java类函数的加载和执行生命周期过程中的最为关键的成员,对于app的脱壳就会有非常深入的认识,接下来就可以再去参考下诸如Xposed以及frida等hook框架是如何对java函数进行hook的了。
这里尝试使用Frida进行脱壳,脚本完全模仿默认版本的Fart运行流程进行编写,当然很多函数Frida完全没修改源码来的直接方便。
这里也是为了熟悉下Frida所以进行的尝试,很多函数也都是直接用Frida编码实现,比如解析Dex中类,计算Dex函数代码长度等,相对于hanbing老师的Frida脱壳麻烦很多。水平太差只能用笨方法了。
实现过程中,加深以下几点知识点的理解:
1. 了解Fart,尝试解决一些自定义问题
2. Frida 遍历Dex类,类方法,类函数
3. Frida主动调用指定函数
4. 自定义ClassLoader对脱壳的影响
1
一、解题思路
1
二、查看一些被动调用还原的代码
用的yang大佬的dump脚本,Dump下Dex后,发现是自定义ClassLoader导致Fart无法正常运行。
1
三、编写Frida脱壳脚本
1
四、遍历类并遍历函数调用
function hook_java(){
Java.perform(function(){
console.log("---------------Java.enumerateClassLoaders");
Java.enumerateClassLoaders({
onMatch: function(cl){
fartwithClassloader(cl);
},
onComplete: function(){
}
});
});
}
function fartwithClassloader(cl){
Java.perform(function(){
var clstr = cl.$className.toString();
if(clstr.indexOf("java.lang.BootClassLoader") >= 0 ){
return
}
console.log(" |------------",cl.$className);
var class_BaseDexClassLoader = Java.use("dalvik.system.BaseDexClassLoader");
var pathcl = Java.cast(cl, class_BaseDexClassLoader);
console.log(".pathList",pathcl.pathList.value);
var class_DexPathList = Java.use("dalvik.system.DexPathList");
var dexPathList = Java.cast(pathcl.pathList.value, class_DexPathList);
console.log(".dexElements:",dexPathList.dexElements.value.length);
var class_DexFile = Java.use("dalvik.system.DexFile");
var class_DexPathList_Element = Java.use("dalvik.system.DexPathList$Element");
for(var i=0;i<dexPathList.dexElements.value.length;i++){
var dexPathList_Element = Java.cast(dexPathList.dexElements.value[i], class_DexPathList_Element);
// console.log(".dexFile:",dexPathList_Element.dexFile.value);
if(dexPathList_Element.dexFile.value){
//可能为空
var dexFile = Java.cast(dexPathList_Element.dexFile.value, class_DexFile);
var mcookie = dexFile.mCookie.value;
// console.log(".mCookie",dexFile.mCookie.value);
if(dexFile.mInternalCookie.value){
// console.log(".mInternalCookie",dexFile.mInternalCookie.value);
mcookie = dexFile.mInternalCookie.value;
}
var classNameArr = dexPathList_Element.dexFile.value.getClassNameList(mcookie);
console.log("dexFile.getClassNameList.length:",classNameArr.length);
console.log(" |------------Enumerate ClassName Start");
for(var i=0; i<classNameArr.length; i++){
// console.log(" ",classNameArr[i]);
if(classNameArr[i].indexOf(TestCalss) > -1){
loadClassAndInvoke(cl, classNameArr[i]);
}
}
console.log(" |------------Enumerate ClassName End");
}
}
});
}
var classResult = Java.use(className).class;
if(!classResult) return;
var methodArr = classResult.getDeclaredConstructors();
methodArr = methodArr.concat(classResult.getDeclaredMethods());
var argsTypes = methodArr[i].getParameterTypes();
var args = []
// int类型
var class_int = Java.use("java.lang.Integer");
args[0] = class_int.$new(0x1);
// String类型
var class_String = Java.use("java.lang.String");
args[0] = class_String.$new("TEST");
// 例:android.os.Bundle类型,OnCreate
var class_Bundle = Java.use("android.os.Bundle");
args[0] = class_Bundle.$new();
// 参数列表
var arr = Java.array("Ljava.lang.Object;",args);
methodArr[i].setAccessible(true)
console.log("invoke result:",methodArr[i].invoke(null,arr));
// 非静态需要传第一个参数
// var class_MainActivity = Java.use("com.aipao.hanmoveschool.activity.MainActivity");
// class_MainActivity.$new();
// Java.choose("com.aipao.hanmoveschool.activity.MainActivity",{
// onMatch: function(ins){
// try {
// console.log(methodArr[i].invoke(ins,arr)); //.overload('java.lang.Object', '[Ljava.lang.Object;')
// } catch (error) {
// console.log("Java.choose:[",methodArr[i].toString(),']',error);
// }
// },
// onComplete: function(){
// }
// });
var invokeSize = Memory.alloc(0x10).writeU32(6);
var invokeStr = Memory.alloc(0x100).writeUtf8String("fart");
var allocPrettyMethod = Memory.alloc(0x100);
var allocPrettyMethodInit = []
ArtMethod_invoke_replace(ptr(methodArr[i].getArtMethod()), ptr(0), ptr(0), 6, invokeSize, invokeStr);
五、HOOK art_method.cc文件中的ArtMethod::Invoke,根据参数Dump函数
var dex_code_item_offset_ = args[0].add(sizeU32*2).readU32();
var dex_method_index_ = args[0].add(sizeU32*3).readU32();
if(dex_code_item_offset_ <= 0){
//com.aipao.hanmoveschool.activity.StepDetector$OnSensorChangeList
console.log("dex_code_item_offset_ error:",dex_code_item_offset_);
return;
}
// console.log("dex_code_item_offset_:",dex_code_item_offset_.toString
// console.log("dex_method_index_:",dex_method_index_.toString(16));
if(DexBase){
var addrCodeOffset = DexBase.add(dex_code_item_offset_);
// console.log("addrCodeOffset:",hexdump(addrCodeOffset));
var tries_size = addrCodeOffset.add(sizeShort*3).readU16();
var insns_size = addrCodeOffset.add(sizeU32*3).readU16();
if(tries_size > 256){
console.log("tries_size:",tries_size.toString(16));
console.log("insns_size:",insns_size.toString(16));
return;
}
// console.log("tries_size:",tries_size.toString(16));
// console.log("insns_size:",insns_size.toString(16));
var codeLen = 16 + insns_size*2;
if(tries_size > 0){
var addrTryStart = addrCodeOffset.add(codeLen);
// if(addrTryStart.readU16() == 0){ //padding
// addrTryStart = addrTryStart.add(0x2);
// }
if(codeLen %4 != 0){ //padding
addrTryStart = addrTryStart.add(0x2);
}
// console.log("addrTryStart:",hexdump(addrTryStart));
var addrTryEnd = addrTryStart.add(sizePointer*tries_size);
var addrCodeEnd = CodeItemEnd(addrTryEnd);
codeLen = addrCodeEnd - addrCodeOffset;
}
var allins = "";
for(var i=0;i<codeLen;i++){
var u8data = addrCodeOffset.add(i).readU8();
if(u8data <= 0xF){
allins += "0";
}
allins += u8data.toString(16);
}
var codedtl = "{name:"+methodName+
",method_idx:"+dex_method_index_+
",offset:"+dex_code_item_offset_+
",code_item_len:"+codeLen+
",ins:"+allins+
"};";
console.log(codedtl);
write_file_log(codedtl);
dumpMethodNameInvoke.push(methodName);
Interceptor.attach(addr_ClassLinker_DefineClass, {
onEnter: function(args){
if(DexBase) {
//找到就不运行下面了
return;
}
console.log("addr_ClassLinker_DefineClass:",DexBase);
var dex_file = args[5];
var base = ptr(dex_file).add(Process.pointerSize).readPointer();
var size = ptr(dex_file).add(Process.pointerSize *2).readUInt();
console.log("base:",base,"\tsize:",size);
if(size > 0x3b0000 && size < 0x3f0000){
DexBase = base;
}
},
onLeave: function(retval) {
}
});
1
六、使用Frida脱壳脚本
function hook_java(){
Java.perform(function(){
loadClassAndInvoke("com.sup.android.superb.SplashActivity");
});
}
function loadClassAndInvoke(className) {
Java.perform(function(){
try {
var classResult = Java.use(className).class;
if(!classResult) return;
var methodArr = classResult.getDeclaredConstructors();
methodArr = methodArr.concat(classResult.getDeclaredMethods());
console.log(className,"\t",methodArr.length);
for(var i=0;i<methodArr.length;i++){
var methodName = methodArr[i].toString();
if(methodName.indexOf(TestFunction) > -1){
if(methodName in dumpMethodName){
continue;
}
console.log("methodName:",methodName);
// c++层调用
if(ArtMethod_invoke_replace){
//每次都会报错,但是我还没找到更方便的
try{
dumpMethodName.push(methodName);
// console.log("getArtMethod:", hexdump(ptr(methodArr[i].getArtMethod())));
ArtMethod_invoke_replace(ptr(methodArr[i].getArtMethod()), ptr(0), ptr(0), 6, invokeSize, invokeStr);
} catch(error){
// console.log("ArtMethod_invoke error:[",className,"]",error);
}
}
}
}
} catch (error) {
console.log("loadClassAndInvoke error:[",className,"]",error);
}
});
}
1
七、解决枚举Dex类
DexBase = base;
DexSize = size;
// console.log("DexBase:",hexdump(base));
var string_ids_size = DexBase.add(0x38).readU32();
var string_ids_off = DexBase.add(0x3c).readU32();
console.log("uint string_ids_size:",string_ids_size); //.toString(1
console.log("uint string_ids_off:",string_ids_off);
var type_ids_size = ptr(DexBase).add(0x40).readU32();
var type_ids_off = ptr(DexBase).add(0x44).readU32();
console.log("uint type_ids_size:",type_ids_size);
console.log("uint type_ids_off:",type_ids_off);
var class_idx = ptr(DexBase).add(0x60).readU32();
var class_defs_off = ptr(DexBase).add(0x64).readU32();
console.log("uint class_idx:",class_idx);
console.log("uint class_defs_off:",class_defs_off);
// var offsetStrEnd = DexBase.add(type_ids_off);
// console.log("offsetStrEnd:",offsetStrEnd);
for(var i=0; i<class_idx; i++){
var offsetClass = DexBase.add(class_defs_off+i*0x20);
// console.log("offsetClass:",offsetClass);
var type_idx = offsetClass.readU32();
// console.log("type_idx:",type_idx);
var descriptor_idx = DexBase.add(type_ids_off+type_idx*0x4).rea
// console.log("descriptor_idx:",descriptor_idx);
var offsetStr = DexBase.add(string_ids_off + descriptor_idx*4).
// console.log("offsetStr:",offsetStr);
if(offsetStr > size){
console.log("offsetStr > size:",offsetStr,">",size);
break;
}
var addrStr = DexBase.add(offsetStr);
// console.log("addrStr:", hexdump(addrStr));
// console.log("addrStr.readU32:",);
var classNameLen = addrStr.readU8();
if(classNameLen > 0x7f){
//这里类名都没超过0x7F
console.log("ClassName Len > 0x7f:",addrStr);
var lebdtl = DecodeUnsignedLeb128(addrStr);
addrStr = addrStr.add(lebdtl[1]);
}else{
addrStr = addrStr.add(1);
}
// console.log("addrStr:",addrStr);
// 读utf16有错误
// var str = addrStr.readUtf16String();
var str = addrStr.readUtf8String();
// console.log(i,":", str);
// console.log(hexdump(addrStr));
// break;
str = str.replace(/L([^;]+);/,"$1").replace(/\//g,'.');
classArr.push(str);
}
console.log("classArr.length:",classArr.length);
1
八、脱壳操作
function hook_java(){
console.log("--------------------Start Invoke:",new Date().getTime());
for(var i=0; i<classArr.length; i++ ){
if(classArr[i].indexOf(TestCalss) >= 0){
console.log("class:",classArr[i]);
loadClassAndInvoke(classArr[i]);
}
}
console.log("--------------------End Invoke:",new Date().getTime());
dump_dex("fixed.dex");
}
1
九、优化整体脱壳速度
1
十、总结
看雪ID:无造
https://bbs.pediy.com/user-571058.htm
*本文由看雪论坛 无造 原创,转载请注明来自看雪社区。
活动专区
在本文下方留言,
留言点赞第一名 可以获得 看雪论坛 转正邀请码一个(价值1000雪币)。
使用邀请码后,可使临时会员转正成功,升级至正式会员!
推荐文章++++
好书推荐