最近应用开发的进程中出现了一个小问题,顺便记录一下原因和方法--文件进程
http://blog.csdn.net/jiazhijun/article/details/8892635
作者:Jack_Jia 邮箱: 309zhijun@163.com
一、叙言
现在Android市场充斥着大量的盗版软件,开发者的官方应用被“打包党”们恶意改动。如何使程序代码免受盗版改动就成了开发者面临的头等大事,今天我们将分析一个不错的处理方案---梆梆加固(http://www.secneo.com/appProtect/)。
通过对App进行加固掩护。梆梆可以有效防止移动应用在经营推广进程中被破解、盗版、二次打包、注入、反编译等损坏,保证程序的安全性、稳定性,对移动应用的整体逻辑结构进行掩护,保证了移动应用的用户体验。
二、梆梆加固逆向分析进程
首先我们通过对APK加固前后文件结构的比拟,来懂得梆梆加固对APK文件所做的处理。为了使分析进程充足简略,我新建一个最简略的测试程序,并上传到梆梆加固,整个加固进程大概须要4天的时间才可以实现,这是一个漫长的等待进程。
该测试程序包含了Activity、Service、ContentProvider、BroadcastRecevier四大组件、Application、一般类、Jni调用等7类对象,目的就是全面的懂得梆梆的加固效果。
1、apk加固前后静态文件结构及动态运行时对比分析
(1) 加固前后静态文件结构变更(左为加固前,右为加固后)
加固后apk新增以下文件:
assets\meta-data\manifest.mf //APK文件列表SHA1-Digest
assets\meta-data\rsa.pub //RSA公钥信息
assets\meta-data\rsa.sig //数字签名文件
assets\classes.jar //已加密原classes.dex文件
assets\com.example.hellojni //ARM平台二进制可执行文件
assets\com.example.hellojni.x86 //x86功能同上
libs\armeabi\libsecexe.so //ARM平台同享库文件
libs\x86\libsecexe.so //x86功能同上
加固后修改文件:
AndroidMainfest.xml //(如果应用配置有Application信息,则该文件加固前后雷同,如果应用未配置Application信息,则该文件加固前后不 //雷同,梆梆会配置Application信息为自己实现类)
classes.dex
对classes.dex进行反编译,视察代码树结构变更:(左为加固前,右为加固后)
(2)加固前后动态运行时变更
运行原程序,系统仅创建一个相干进程,但是加固的程序,系统会为其同时创建三个相干程序进程:
进程启动次序:597进程创建605进程,605进程又创建了607进程
通过查看maps文件获取597进程映射文件信息
通过map文件可以看出,597进程为主进程,android各组件在该进程中运行。
605和607进程并无与apk文件相干文件信息,通过cmdline查看启动参数:
初步疑惑该进程为assets\com.example.hellojni 可执行文件运行结果。
2、梆梆加固掩护效果分析
我们通过逆向分析加固后的app,来看看梆梆加固对app的掩护效果。
程序代码的第一执行点是Application对象,首先查看TestApplication类对象。
程序的Util类实现大部分的java层逻辑,
ACall类主要实现对libsecexe.so JNI的调用:
查看libsecexe.so文件导出函数,发现全部函数名都经过加密处理,与我们平常jni调用产生的函数名并不同。平常jni产生的函数名应该为这样格式Java_com_secapk_wrapper_ACall_{函数名}
抗静态分析:
Util类通过MyClassLoader实现对加密classes.jar的动态加载,内存中解密classes.jar,实现动态加载。
jni方法对应so函数名的混淆。
抗动态调试:
当应用IDA动态调试该程序时,程序无法建立连接调试。
梆梆加固可以有效常用的逆向分析方法。
三、梆梆加固技术实现关键点料想
(1)如何使DexClassLoader动态加载组件具有生命周期?
根据APK文件是不是在AndroidManifest.xml配置Applicaiton信息,梆梆加固会做不同的处理:
通过上传Applicaiton不同配置的APK文件,我们发现:
当APK配置有Applicaition信息时,梆梆加固重写Application类
当APK未配置Application信息时,梆梆加固新建类,并在AndroidManifest.xml中配置自己Application类
因此Applicaiton就是程序的第一执行点。
我们晓得DexClassLoader加载的类是没有组件生命周期的,也就是说即使DexClassLoader通过对dex的动态加载实现了对组件类的加载, 当系统启动该组件时,还会出现加载类失败的异常。我已在“Android APK加壳技术方案”
(http://blog.csdn.net/jiazhijun/article/details/8809542)中提出了一种使DexClassLoader加载组件类具有生命周期的方法。
运行加固后的程序并通过Mat内存分析工具查看类加载情况:
如上图所示,组件类的加载类已被修改为com.secapk.wrapper.MyClassLoader类,可以得出结论,该方式和我提出方式基本雷同,通过 修改系统组件类ClassLoader来实现。
(2)如何混淆native方法在so库函数对应关系?
jni方法注册方式有两种,
1、通过javah产生函数头,该种方式产生的方法具有牢固的格式。该方式使逆向分析职员比拟轻易获取java层native方法对应的本地方 法。
2、在JNI_OnLoad方法中手动注册jni方法,不轻易查找对应关系。
应用第二种方式可以实现混淆java层native方法和so函数的对应关系。
#include <string.h> #include <jni.h> JNIEXPORT jstring JNICALL abcdefghijklmn( JNIEnv* env,jobject thiz ) { return (*env)->NewStringUTF(env, "Hello from JNI !"); } JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) { return JNI_ERR; } JNINativeMethod gMethods[] = { { "stringFromJNI", "()Ljava/lang/String;", (void*)abcdefghijklmn }, }; jclass clazz = (*env)->FindClass(env, "com/example/hellojni/HelloJni"); if (clazz == NULL) { return JNI_ERR; } if ((*env)->RegisterNatives(env, clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0){ return JNI_ERR; } /* success -- return valid version number */ result = JNI_VERSION_1_4; return result; }
以上代码中的字符串都是明文(比如“stringFromJNI”),如果这些文明字符串都换成密文的话,再通过运行时解密,响应的对应关系更不轻易看出。
(3)如何使DexClassLoader加载加密的dex文件?
虽然不懂得梆梆加固是怎么做的,不过通过分析它的运行逻辑,我推测了一种可行的实现方案:懂得该方案须要对Android DexClassLoader的整个加载流程须要有清晰的懂得。
首先推断assets\classes.jar是一个加密的jar包。
正常的DexClassLoader加载的流程如下:会有一个DexOpt产生odex进程
但是梆梆加固后的应用DexClassLoader加载进程并没有该进程的log信息。
推断加密的jar包里头含有odex文件,如果不是odex文件的话,DexClassLoader肯定会在运行时释放未加密的odex文件到目录,这样的话 被掩护的逻辑也就泄露了。
DexClassLoader加载进程会在java层和C层产生不同的数据结构,java层并没有实质性的数据,全部的数据都在c层,我们可用通过底层代 码实现dex数据的解析。底层dex分析是可以支持byte[]数组的,解密odex数据,传递从前就行了。这样java层就能够调用了。
以下是大概伪代码实现步调:
int loadDex(char * dexFileName)
{
char *dvm_lib_path = "/system/lib/libdvm.so";
void * handle;
DvmGlobals gDvm;
handle = dlopen( dvm_lib_path, int mode);
1、读取dexFileName文件内容并解密到byte数组。
2、调用dexFileParse函数解析byte数组为DexFile
\dalvik\libdex\DexFile.c
DexFile* dexFileParse(const u1* data, size_t length, int flags)//dlsym(handle, "dexFileParse");
3、调用allocateAuxStructures转换DexFile为DvmDex,(由于该方法为static方法,因此须要按照其逻辑自行实现)
\dalvik\vm\DvmDex.c
static DvmDex* allocateAuxStructures(DexFile* pDexFile)
4、添加DvmDex到gDvm.userDexFiles
\dalvik\vm\Init.c
struct DvmGlobals gDvm; //gDvm = dlsym(handle, "gDvm");
5、修改MyDexClassLoader中的mDexs对象的mCookie值,mCookie主要用于映射底层DvmDex数据
DexClassLoader.mDexs[0].mCookie值
}
(4)so如何实现程序的反调试?
同linux反调试基本原理雷同,这里提供一种方式就是在JNI_Onload中调用ptrace方法,ptrace被普遍用于调试(比如IDA)和进程代码注入(比如LBE,金山等权限管理功能实现),一个进程只能被一个进程ptrace,如果你自己调用ptarce,这样其它程序就无法通过ptrace调试或者 向您程序进程注入代码。
ptrace(PTRACE_TRACEME,0 ,0 ,0);
通过本人实验,该种方式可以实现so的反调试。
三、总结
通过以上分析,梆梆加固的确可以有效防止移动应用在经营推广进程中被破解、盗版、二次打包、注入、反编译等损坏,不过如果Android恶意软件也通过这种方式加固掩护,这将会给移动安全分析职员带来巨大的挑战,因为安全分析职员经常应用的代码静态逻辑分析和动态调试分析在该情况下都失效了。
梆梆官方宣称不会对恶意软件进行加固,的确在加固的进程中发现存在安全软件扫描信息和云测试处理流程,不过这些办法只能增加而不能彻底杜绝恶意软件通过梆梆加固掩护。如何不被恶意软件利用是梆梆须要处理的问题。
文章结束给大家分享下程序员的一些笑话语录:
雅虎最擅长的不是开通新业务,是关闭旧业务。
---------------------------------
原创文章 By
文件和进程
---------------------------------