4、叉叉助手逆向分析续集--模拟实现游戏插件框架--再扩展到脱壳机
这里以HOOK程序启动后调用天天星联盟为例,下面是2014年4月30日抠出来的天天星联盟插件APK代码:
package com.xxAssistant.UI; import android.app.Activity; import android.content.Context; import android.util.Log; import android.widget.RelativeLayout; import android.widget.Toast; public class UniversalUIextends RelativeLayout { private static String mSoPath; private static UniversalUI me = null; private Activity mActivity; private Context mContext; private boolean mShow; private UniversalUI(Activity paramActivity) { super(paramActivity); this.mContext = paramActivity; this.mActivity = paramActivity; this.mShow = false; } public static void init(Activity paramActivity, String paramString) { if (me ==null); for (UniversalUI localUniversalUI = new UniversalUI(paramActivity); ; localUniversalUI = me) { me = localUniversalUI; mSoPath = paramString; me.show(); return; } } private native void setqudao();private void show() { if (!this.mShow) { this.mShow = true; Log.d("native", "叉叉辅助已成功装载"); Toast.makeText(this.mContext, "叉叉辅助已成功装载", 1).show(); System.load(mSoPath); xxdohook(); } }private native void xxdohook(); }
测试只要把so加载和调用的部分去掉即可,反编译后去掉UniversalUI.smali的以下代码:
package com.xxAssistant.UI; import android.app.Activity; import android.content.Context; import android.util.Log; import android.widget.RelativeLayout; import android.widget.Toast; public class UniversalUIextends RelativeLayout { private static String mSoPath; private static UniversalUI me = null; private Activity mActivity; private Context mContext; private boolean mShow; private UniversalUI(Activity paramActivity) { super(paramActivity); this.mContext = paramActivity; this.mActivity = paramActivity; this.mShow = false; } public static void init(Activity paramActivity, String paramString) { if (me ==null); for (UniversalUI localUniversalUI = new UniversalUI(paramActivity); ; localUniversalUI = me) { me = localUniversalUI; mSoPath = paramString; me.show(); return; } } private native void setqudao();private void show() { if (!this.mShow) { this.mShow = true; Log.d("native", "叉叉辅助已成功装载"); Toast.makeText(this.mContext, "叉叉辅助已成功装载", 1).show(); } } }
void LOGMSG(const char *lpszText) { LOGD("%s", lpszText); if ( g_context!=NULL ) { jclass jclazz = g_env->GetObjectClass(g_context); jmethodID logMsg = g_env->GetMethodID(jclazz, "logMsg", "(Ljava/lang/String;)V"); if ( logMsg ) {//LOGD("found: logMsg"); jstring jstr = str2jstring(g_env, lpszText, strlen(lpszText), NULL); //LOGD("CallObjectMethod 1"); g_env->CallVoidMethod(g_context, logMsg, jstr); //LOGD("CallObjectMethod 2"); } } }
但是这个仅限于在当前进程中,一旦注入到其他进程中,这种方式就不行,可以考虑其他通信方式。
//参数二的形式为:android.app.ActivityThread@41683ec8 //参数三的形式为:AppBindData{appInfo=ApplicationInfo{4178ead0 com.huawei.ChnUnicomAutoReg}} //因此可以通过参数三的包名进行过滤
static void OnCallback_handleBindApplicaton(JNIEnv *jni, jobject jthis, jobject appBindData) { LOGD("[%s] begin", __FUNCTION__); string strName; strName = getObjectClassName(jni, appBindData); if ( (int)strName.size() > 0 ) { LOGD("[%s] class: %s", __FUNCTION__, strName.c_str()); } //读取hook配置信息,这里每次都要读,考虑到配置文件会发生变动 bool bSuccess = LoadConfig(g_cfg); if ( bSuccess==false ) { LOGE("[%s] load config failed", __FUNCTION__); }else{ //根据包名过滤,hook指定的apk//string::size_type position = strName.npos; if ( strName.find(g_cfg.strHostPackage)!=strName.npos ) {//是目标app LOGD("[%s] target apk running: %s", __FUNCTION__, g_cfg.strHostPackage.c_str());// //hook目标的activity的类加载以及onCreate //MSJavaHookClassLoad(jni, g_cfg.strHostActivity.c_str(), &OnCallback_JavaClassLoad, NULL);// //上下两块独立互不干扰,可以同时使用也可分开使用 // //简易脱壳机:可脱我们自己的壳,梆梆加固,爱加密,360加固保等// void * handle=dlopen("libdvm.so", RTLD_NOW); LOGD("handle:%p", handle); JNINativeMethod *dvm_dalvik_system_DexFile = (JNINativeMethod*)dlsym(handle, "dvm_dalvik_system_DexFile"); dvmCreateCstrFromString = (dvmCreateCstrFromStringPtr)dlsym(handle,"_Z23dvmCreateCstrFromStringPK12StringObject"); LOGD("dvmCreateCstrFromString:%p", dvmCreateCstrFromString); dvmRawDexFileOpen = (dvmRawDexFileOpenPtr)dlsym(handle,"_Z17dvmRawDexFileOpenPKcS0_PP10RawDexFileb"); dvmJarFileOpen = (dvmRawDexFileOpenPtr)dlsym(handle,"_Z14dvmJarFileOpenPKcS0_PP7JarFileb"); LOGD("dvmRawDexFileOpen:%p", dvmRawDexFileOpen); LOGD("dvmJarFileOpen:%p", dvmJarFileOpen); if ( dvmRawDexFileOpen ) { MSHookFunction(dvmRawDexFileOpen, newdvmRawDexFileOpen, &olddvmRawDexFileOpen); } if ( dvmJarFileOpen ) { MSHookFunction(dvmJarFileOpen, newdvmJarFileOpen, &olddvmJarFileOpen); } //dvmDexFileOpenPartial = (dvmDexFileOpenPartialPtr)dlsym(handle, "_Z21dvmDexFileOpenPartialPKviPP6DvmDex");//LOGD("dvmDexFileOpenPartial: %p", dvmDexFileOpenPartial); //if ( dvmDexFileOpenPartial ) { //MSHookFunction(dvmDexFileOpenPartial, newdvmDexFileOpenPartial, &olddvmDexFileOpenPartial); //}//dvmDexFileOpenPartial会调用dexFileParse,因此只需要hookdexFileParse即可。 dexFileParse = (dexFileParsePtr)dlsym(handle, "_Z12dexFileParsePKhji"); LOGD("dexFileParse: %p", dexFileParse); if ( dexFileParse ) { MSHookFunction(dexFileParse, newdexFileParse, &olddexFileParse); }if(dvm_dalvik_system_DexFile){ lookup(dvm_dalvik_system_DexFile, "openDexFile", "([B)I", &openDexFile); LOGD("openDexFile:%p",openDexFile); MSHookFunction(openDexFile, newDalvik_dalvik_system_DexFile_openDexFile_bytearray, &oldDalvik_dalvik_system_DexFile_openDexFile_bytearray); lookup(dvm_dalvik_system_DexFile,"openDexFile", "(Ljava/lang/String;Ljava/lang/String;I)I", &openDexFile); LOGD("openDexFile:%p",openDexFile); MSHookFunction(openDexFile, newDalvik_dalvik_system_DexFile_openDexFile, &oldDalvik_dalvik_system_DexFile_openDexFile); }// } } //继续原函数处理(*old_handleBindApplication)(jni, jthis, appBindData); LOGD("[%s] end", __FUNCTION__); }
//当类被加载时触发的回调函数 static void OnCallback_JavaClassLoad(JNIEnv *jni, jclass _class, void *arg) { LOGD("[%s] begin", __FUNCTION__); if (!g_cfg.IsValid()) { LOGE("[%s] hook config is not valid", __FUNCTION__); return; } jmethodID onCreate = jni->GetMethodID(_class, "onCreate", "(Landroid/os/Bundle;)V"); if (onCreate == NULL) { LOGE("[%s] \"onCreate\" not found in class: %s", __FUNCTION__, g_cfg.strHostActivity.c_str()); return; } old_onCreate = NULL; LOGD("[%s] hook \"onCreate\"", __FUNCTION__); MSJavaHookMethod(jni, _class, onCreate, (void *) (&OnCallback_ClassOnCreate), (void **) (&old_onCreate)); if ( old_onCreate==NULL ) { LOGE("[%s] old_onCreate returned NULL", __FUNCTION__); } LOGD("[%s] end", __FUNCTION__); }
主要目的是HOOK onCreate函数,这样当启动类的 onCreate被调用时首先是进入到我们的hook函数:
//当类的onCreate调用时触发回调,动态加载插件apk并调用插件函数 /* ClassLoader localClassLoader = ClassLoader.getSystemClassLoader(); DexClassLoader localDexClassLoader = new DexClassLoader(dexpath, dexoutputpath, null, localClassLoader); 动态加载时参数dexOutputDir为/data/data/目标APK包名/app_dex,获取方法: getDir("dex",0).getAbsolutePath() 其他路径会失败,错误:optimizedDirectory not readable/writable:+路径; 参见:http://www.cnblogs.com/LittleRedPoint/p/3429709.html 由于app_dex目录默认是不存在的,需要动态创建以及修改文件夹权限,比较繁琐,这里直接使用cache目录。 old_onCreate在最开始调用了,所以后面的函数都可以不限制地调用return */ static voidOnCallback_ClassOnCreate(JNIEnv *jni, jobject activity, jobject bundle) { stringstrName; strName = getObjectClassName(jni, activity); LOGD("[%s] begin: %s::OnCreate", __FUNCTION__, strName.c_str()); (*old_onCreate)(jni, activity, bundle); LOGD("[%s] dynamic load plug apk", __FUNCTION__); if (!g_cfg.IsValid()){ LOGD("[%s] config invalid", __FUNCTION__); return; } jclass ClassLoader = jni->FindClass("java/lang/ClassLoader"); if ( ClassLoader==NULL ) { LOGE("[%s] not found: java/lang/ClassLoader", __FUNCTION__); return; } jmethodID getSystemClassLoader = jni->GetStaticMethodID(ClassLoader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;"); if ( getSystemClassLoader==NULL ) { LOGE("[%s] not found: getSystemClassLoader", __FUNCTION__); return; } jobject systemLoader = jni->CallStaticObjectMethod(ClassLoader, getSystemClassLoader); if ( systemLoader==NULL ) { LOGE("[%s] getSystemClassLoader() return NULL", __FUNCTION__); return; } jclass dexLoaderClass = jni->FindClass("dalvik/system/DexClassLoader"); if ( dexLoaderClass==NULL ) { LOGE("[%s] not found: dalvik/system/DexClassLoader", __FUNCTION__); return; } jmethodID initDexLoaderMethod = jni->GetMethodID(dexLoaderClass, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V");if ( initDexLoaderMethod==NULL ) { LOGE("[%s] not found: dalvik/system/DexClassLoader::<init>", __FUNCTION__); return; } //注意这里必须是当前进程的目录,不能跨进程目录读写 string app_dex = "/data/data/" + g_cfg.strHostPackage +"/cache"; //LOGD("app_dex is %s", app_dex.c_str()); jstring apkPath = jni->NewStringUTF(g_cfg.strPlugApkPath.c_str()); jstring dexOutputDir = jni->NewStringUTF(app_dex.c_str()); jobject dexClassLoader = jni->NewObject(dexLoaderClass, initDexLoaderMethod, apkPath, dexOutputDir, NULL, systemLoader); //jni->DeleteLocalRef(apkPath); //jni->DeleteLocalRef(dexOutputDir);if ( dexClassLoader==NULL ) { LOGE("[%s] new DexClassLoader(...) return NULL", __FUNCTION__); return; } jmethodID loadClass = jni->GetMethodID(dexLoaderClass,"loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); if ( loadClass==NULL ) { LOGE("[%s] not found: dalvik/system/DexClassLoader::loadClass", __FUNCTION__);return; } jstring javaClassName = jni->NewStringUTF(g_cfg.strPlugActivity.c_str()); jclass plugClass = (jclass)jni->CallObjectMethod(dexClassLoader, loadClass, javaClassName); //jni->DeleteLocalRef(apkPath); if ( plugClass==NULL ) { LOGE("[%s] DexClassLoader::loadClass not found class: %s", __FUNCTION__, g_cfg.strPlugActivity.c_str()); return; } //调用插件的静态函数init,原型:public static void init(Activity activity, String soPath) jmethodID plugUIinit = jni->GetStaticMethodID(plugClass, "init", "(Landroid/app/Activity;Ljava/lang/String;)V");if ( plugUIinit==NULL ) { LOGE("[%s] not found \"init\" in plug activity: public static void init(Activity activity, String soPath)", __FUNCTION__); }else{ LOGD("[%s] invoke %s::init", __FUNCTION__, g_cfg.strPlugActivity.c_str()); jstring soPath = jni->NewStringUTF(g_cfg.strPlugSoPath.c_str()); jni->CallStaticVoidMethod(plugClass, plugUIinit, activity, soPath); //jni->DeleteLocalRef(soPath); } LOGD("[%s] end", __FUNCTION__); }
2、反编译结果再打包成DEX:
java -Xmx512M -jar smali.jar out -o classes.dex
3、DEX转JAR看源码。
static char chCount = 0x30; //dvmDexFileOpenPartial会调用dexFileParse,因此只需要HOOK dexFileParse即可。int newdvmDexFileOpenPartial(const void* addr, int len, void* ppDvmDex) { LOGD("[%s] begin", __FUNCTION__); int nret = olddvmDexFileOpenPartial(addr, len, ppDvmDex); string dexFileName = "/data/data/" + g_cfg.strHostPackage + "/cache/o.odex"; dexFileName.append(1, ++chCount); if ( saveFile(addr, len, dexFileName.c_str()) ) { int nret = chmod(dexFileName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); LOGD("[%s] dumped file: %s chmod return: %d error: %s", __FUNCTION__, dexFileName.c_str(), nret, dlerror()); } LOGD("[%s] end", __FUNCTION__); return nret; }
[newdvmJarFileOpen] fileName: /data/data/com.mxy/.cache/classes.jar odexOutputName: /data/data/com.mxy/.cache/classes.dex
[newdvmRawDexFileOpen] fileName: /data/data/com.example.helloapplication/app_bangcleplugin/collector.dex odexOutputName: /data/data/com.example.helloapplication/app_outdex.collector.f961d3dbc359c93741d5c63d553c0f57/collector.dex
void newDalvik_dalvik_system_DexFile_openDexFile(const u4* args, JValue* pResult) { LOGD("[%s] begin", __FUNCTION__); oldDalvik_dalvik_system_DexFile_openDexFile(args, pResult); StringObject* sourceNameObj = (StringObject*) args[0]; StringObject* outputNameObj = (StringObject*) args[1]; char* sourceName = NULL; char* outputName = NULL; if ( dvmCreateCstrFromString!=NULL ) { sourceName = dvmCreateCstrFromString(sourceNameObj); outputName = dvmCreateCstrFromString(outputNameObj); LOGD("[%s] sourceName: %s outputName: %s", __FUNCTION__, sourceName, outputName); //复制备份 if ( strstr(sourceName, "classes.jar")!=NULL ) { string dexFileName = "/data/data/" + g_cfg.strHostPackage + "/cache/classes.jar"; dexFileName.append(1, ++chCount); LOGD("[%s] backup file to: %s", __FUNCTION__, dexFileName.c_str()); copyFile(sourceName, dexFileName.c_str()); chmod(dexFileName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); LOGD("[%s] dumped file: %s", __FUNCTION__, dexFileName.c_str()); }/*else if ( strstr(sourceName, "collector.dex")!=NULL || strstr(sourceName, "container.dex")!=NULL ) { string dexFileName = "/data/data/" + g_cfg.strHostPackage + "/cache/out.dex"; dexFileName.append(1, ++chCount); LOGD("[%s] backup file to: %s", __FUNCTION__, dexFileName.c_str()); copyFile(sourceName, dexFileName.c_str()); }*/ } LOGD("[%s] end", __FUNCTION__); }
org.jf.util.ExceptionWithContext: Cannot locate boot class path file /system/fra
mework/core.odex
at org.jf.dexlib2.analysis.ClassPath.loadClassPathEntry(ClassPath.java:2
17)
at org.jf.dexlib2.analysis.ClassPath.fromClassPath(ClassPath.java:161)
at org.jf.baksmali.baksmali.disassembleDexFile(baksmali.java:67)
at org.jf.baksmali.main.main(main.java:280)
//dvmDexFileOpenPartial会调用dexFileParse,因此只需要HOOK dexFileParse即可。void* newdexFileParse(const u1* data, size_t length, int flags) { LOGD("[%s] begin", __FUNCTION__); void* nret = olddexFileParse(data, length, flags); string dexFileName = "/data/data/" + g_cfg.strHostPackage + "/cache/o.odex"; dexFileName.append(1, ++chCount); if ( saveFile(data, length, dexFileName.c_str()) ) { intnret = chmod(dexFileName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); LOGD("[%s] dumped file: %s chmod return: %d error: %s", __FUNCTION__, dexFileName.c_str(), nret, dlerror()); } LOGD("[%s] end", __FUNCTION__); return nret; }
//hook内存加载odex的函数void newDalvik_dalvik_system_DexFile_openDexFile_bytearray(const u4* args, JValue* pResult) { LOGD("[%s] begin", __FUNCTION__); ArrayObject* fileContentsObj = (ArrayObject*) args[0];string dexFileName = "/data/data/" + g_cfg.strHostPackage + "/cache/out.odex"; dexFileName.append(1, ++chCount); if ( saveFile(fileContentsObj->contents, fileContentsObj->length, dexFileName.c_str()) ) { chmod(dexFileName.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); LOGD("[%s] dumped file: %s", __FUNCTION__, dexFileName.c_str()); } LOGD("[%s] end", __FUNCTION__); oldDalvik_dalvik_system_DexFile_openDexFile_bytearray(args, pResult); }
4、叉叉助手逆向分析续集--模拟实现游戏插件框架--再扩展到脱壳机相关推荐
- 2、叉叉助手逆向分析(上)
描述:主要讲解如何有条例地逆向分析出软件的主要逻辑. 工具:APKIDE,JD-GUI 方法:顺藤摸瓜,smali代码主要看invoke关键函数调用,定位到相应的类中看代码. 使用APKIDE反编译x ...
- 3、叉叉助手逆向分析(下)
plugin/117/xxFknsg.apk --ui-name:com/xxAssistant/FknsgUI/xxMain --activity-name:com/babeltimes/main/ ...
- 6、XPOSED二、叉叉助手框架--用XPOSED实现
继<xposed框架初探>之后,编写一个小小的demo应用,刚好之前分析叉叉的游戏辅助框架(参考<叉叉助手逆向分析续集--模拟实现游戏插件框架--再扩展到脱壳机>,我们是用了l ...
- [系统安全] 二.如何学好逆向分析及吕布传游戏逆向案例
您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列.因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全.逆向分 ...
- [系统安全] 三十.CS逆向分析 (1)你的游戏子弹用完了吗?Cheat Engine工具入门普及
您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列.因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全.逆向分 ...
- APP逆向分析之钉钉抢红包插件的实现-iOS篇
花费了很多天的原创文章,转载请注明出处https://yohunl.com/ding-ding-qiang-hong-bao-cha-jian-iospian/ ,谢谢! 网络上关于微信红包的分析文章 ...
- 用C++逆向分析解密,制作游戏破解辅助外挂竟如此简单!
QQ农场辅助主要是实现自动偷菜.自动买东西等.是非常不错的学习C++的东东.里面的介绍非常的详细,希望有兴趣的朋友帮忙研究研究. 我用VC++写的QQ农场辅助(waigua)源代码,还有一些和朋友一起 ...
- 玩转代码|逆向分析一下4399小游戏绕过实名认证
4399的实名认证真是越来越恶心了,本以为只是响应国家号召做点表面功夫,没想到现在他们又在网页上加了反调试.看来是4399是认真的. 目录 0X00实名认证提醒 0X01分析 0X03断点发现=关键元 ...
- [系统安全] 二十四.逆向分析之OllyDbg调试INT3断点、反调试、硬件断点与内存断点
您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列.因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全.逆向分 ...
最新文章
- 七天学会「股票数据分析软件」的开发(中)
- silverlight turntable
- UNIX再学习 -- 函数abort
- 操作系统真实的虚拟内存是什么样的
- 【codevs2452】【SCOI2005】【BZOJ1088】扫雷Mine
- pytorch LSTM_regression
- 《Webservice的应用与开发》学习笔记 ·001【Web服务、XML文档】
- Python eval 函数
- EP主机分销PHP源码,最新EP主机分销全解密源码+详细搭建教程
- 软件设计师和软件评测师有什么区别?若想从事软件开发方面,哪个更适合?
- VMware虚拟机安装Ubuntu20.04详细图文教程
- 移动智能终端PIN码破解
- 1602液晶显示屏显示字符
- 零基础从零开始写VO视觉里程计
- python的写操作_Python—文件读写操作
- Linux 文件/文件夹无法删除问题解决方案
- linux 触摸屏驱动分析
- 探究js中arguments
- Android代码对齐快捷键
- 信用标准评分卡模型开发及实现