关闭

dalvik下替换so简单dump出梆梆加固保护的odex

标签: c++android脱壳梆梆dalvik
15490人阅读 评论(0) 收藏 举报
分类:
  • 由于保护技术更迭迅速,不保证本文方法适用于后续或者其它版本的梆梆加固,需要读者自行测试。

        梆梆加固后的apk,里面的classes.dex只是个外壳,负责加载libDexHelper.so,而真正的dex被加密放在了\assets\classes0.jar,这不是常规的jar文件无法直接解压,我们的目标就是从内存中dump出解密后的classes0.jar/.dex(并进行适当修复)。

        内存dump的方案有很多, 比如直接dd、DexHunter等,但都有一些缺点,dd需要内存地址和大小,这就要求进行暴力搜索,但是内存完全可能被处理过,dump时机很难掌握,于是转到动态调试期望在关键点下断,但由于反调试对抗的存在又会有一堆麻烦;DexHunter很强大,但它要求定制系统,这对逆向工程来说当然不是事,只是同样有针对DexHunter类似原理的对抗,比如检测不安全ROM,等等这些都会带来不必要麻烦,本文提供的方法似乎直接跳过了这些坑。

        其实,无论是DexHunter还是本文提供的方案,最关键的不过是取得代码执行先机,因为代码一但被安全的注入,在壳启动前执行我们的代码,我们就能完成很多有意思的事情了。怎么实现呢?我们知道libDexHelper.so是整个壳的核心,修改libDexHelper.so?万一有自校验咋办,这里就有了两种替换方案,第一种查看依赖较少的so,将该依赖的so换成我们的,比如liblog.so,但是这要求我们实现和liblog.so相应的接口,否则dlopen会失败,第二种,也是本文采用的方案,我们直接把它替换了不就好了嘛?

        首先,把原来的libDexHelper.so改成libDexHelper2.so, 然后我们需要写一个libDexHelper.so,只需实现JNI_OnLoad即可, 在里面加载libDexHelper2.so并显式调用JNI_OnLoad即可(这里竟然没有检测文件名,呵呵,不过检测也没多大意义,容易bypass。另外由于我用的x86机,梆梆在.cache目录下生成对应的x86 so才是目标):

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *unused)
{
	#define SO "/data/data/blog.csdn.net.rrrfff/.cache/libDexHelper-x862.so"
	LOGI("Trying to load lib " SO " 0x0");
	void *hk = dlopen(SO, RTLD_GLOBAL | RTLD_NOW);
	LOGI("Added shared lib " SO " %p %s", hk, hk == NULL ? dlerror() : "");

	void *ld = dlsym(hk, __FUNCTION__);
	LOGI("JNI_OnLoad at %p", ld);
	reinterpret_cast<__func_type(JNI_OnLoad)>(ld)(jvm, unused);
}
        adb push我们的so到相应目录,am start启动目标app,运行一切正常,代码被顺利注入,接着我们切入重点,把dex相关的api统统hook一遍看看是咋回事,这里我把dexFile*全家桶都给hook了,包括dvmDexFileOpenFromFd,dvmDexFileFree,dexFileParse,dvmDexChangeDex1,dvmDexChangeDex2。
        通过被hook方法的执行情况,我们大概总结下梆梆加固还原dex的大概流程(dalvik系统):
  1. open读取 \assets\classes0.jar 并解密出真正的jar,然后解压到.cache目录,但此时的classes0.dex仍旧是加密的,这步仅.cache目录下没有对应.dex时会发生。
  2. open读取 classes0.dex 并解密出真正的dex,其实是odex,看文件头可以知道。
  3. 通过dvmDexFileOpenFromFd读取dex文件,这里的fd就是上一步的fd,进行内存映射。
  4. 系统调用dexFileParse对映射好的dex内存进行解析并返回DexFile结构,这里是dump的好时机。
  5. 壳调用dvmDexChangeDex2对已解析好的dex内存区进行混淆,写入脏数据,所以我们在程序跑起来后再dump出来的东西是有噪声的。

        知道了这些之后我们就能愉快的干活了,编写代码在dexFileParse时完成dump(据说梆梆把一堆read,write,mmap等libc函数hook了, 防止读取相关dex的区域, 这里绕了个弯,先用一般人不会用到的memmove备份内存,然后跳过dex头写到文件,其实似乎多此一举):

	static info<DvmDex *(*)(const uint8_t* data, size_t length, int flags)> *p0 = NULL;
	p0 = p0->hook(dlsym(dvm, "_Z12dexFileParsePKhji"),
				  [](const uint8_t* data, size_t length, int flags)->DvmDex * {
		LOGI("dexFileParse hit with %p %u %d", data, length, flags);
		if (length > 100000) {
			int  dexv = ::open("/data/data/blog.csdn.net.rrrfff/cache/core.odex", O_CREAT | O_TRUNC | O_WRONLY);
			auto dexp = reinterpret_cast<uint8_t *>(::malloc(length));
			::memmove(dexp, data, length);
			::write(dexv, "bmp", 3);
			::write(dexv, dexp + 3, dexl - 3); // bypass odex header
			::free(dexp);
			::close(dexv);
		} //if
		auto pDvmDex = p4->invoke()(data, length, flags);
		return pDvmDex;
	});

        顺利得到core.odex,jeb可以正常打开,但是会出现一些异常,估计存在一些反反编译手段,我们用DexClassLoader加载试试(因为我们加载的是odex, 所以需要在同一目录下放一个dex, 这里我随便弄了个zip包, 系统发现里面没dex会加载同目录下的odex):

	DexClassLoader *dexloader = new DexClassLoader(env,
												   "/data/data/blog.csdn.net.rrrfff/cache/core.dex",
												   "/data/data/blog.csdn.net.rrrfff/.cache",
												   "/tmp",
												   DexClassLoader::getSystemLoader(env));
	auto cls  = dexloader->loadClass(env, "blog.csdn.net.rrrfff.TBJWelcomeActivity");
	auto cls2 = dexloader->loadClass(env, "blog.csdn.net.rrrfff.bean.UserInfoModel");
	LOGI("cls = %p %p", cls, cls2);
        类加载成功, 证明dump出的odex格式良好(对ClassLoader而言), 为了试试能否正常运行, 这里懒得去进行重打包、签名这些琐碎的工作,我们直接hook dvmLookupClass让它返回找不到的类(去掉对原SO的加载后会报类找不到):

	static info<void *(*)(const char *descriptor, void *loader, bool unprepOkay)> *p0 = NULL;
	p0 = p0->hook(dlsym(dvm, "_Z14dvmLookupClassPKcP6Objectb"),
				  [](const char *descriptor, void *loader, bool unprepOkay)->void * {
		LOGI("dvmLookupClass hit with %s %p %d", descriptor, loader, unprepOkay);
		static volatile int during_load[10240] = { 0 };
		if (during_load[gettid()] == 0 && strstr(descriptor, "Lblog/csdn/net/rrrfff") == descriptor ) {
			char sdescriptor[128];
			int len = strlen(descriptor);
			::memcpy(sdescriptor, descriptor + 1, len - 2);
			sdescriptor[len -= 2] = 0;
			while (--len >= 0) {
				if (sdescriptor[len] == '/') sdescriptor[len] = '.';
			}
			JNIEnv *env;
			g_jvm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
			LOGI("loading %s", sdescriptor);
			during_load[gettid()] = 1;
			auto cls = dexloader->loadClass(env, sdescriptor);
			during_load[gettid()] = 0;
			LOGI("%s loaded at %p", sdescriptor, cls);
			return decode_obj(get_self(), cls);
		} //if
		void *ret = p0->invoke()(descriptor, loader, unprepOkay);
		return ret;
	});
        看日志可以发现很多类被成功加载了,但这里会报一个native方法找不到, com/secneo/apkwrapper/Helper的attach方法, 签名(Landroid/app/Application;Landroid/content/Context;)V, 看参数有Application和Context,应该是壳完成资源修复等工作的切入点,后续再研究,我们给它注册个:

	void(*attach)(JNIEnv *env, jclass, jobject, jobject) = [](JNIEnv *env, jclass, jobject, jobject) {
		DEBUG_LINE_HIT;
	};
	JNINativeMethod gMethods[] = {
		{ "attach", "(Landroid/app/Application;Landroid/content/Context;)V", reinterpret_cast<void *>(attach) }
	};
	env->RegisterNatives(env->FindClass("com/secneo/apkwrapper/Helper"), gMethods, 1);
        最后在 android.content.ContextWrapper.getResources(ContextWrapper.java:89) 处出现 java.lang.NullPointerException 异常, 可能是资源或者那个地方需要修复, 这里就没去深究了, 因为从jeb我们已经得到了想要的结果。

1
0
查看评论

Android常见App加固厂商脱壳方法的整理

目录 简述(脱壳前学习的知识、壳的历史、脱壳方法)第一代壳第二代壳第三代壳第N代壳 简述 Apk文件结构Dex文件结构壳史壳的识别 Apk文件结构 Dex文件结构 壳史 第一代壳 Dex加密 Dex字符串加密资源加密对抗反编译反调试自定...
  • QQ1084283172
  • QQ1084283172
  • 2016-12-11 12:29
  • 6604

12306 2.2版本SO的分析和修复

老早写的,现在已经2.3版本了,把这个放出来,这个方法要比之前的简单很多。       12306的so加载顺序是先libDexHelper.so后libcheckcode.so       一、修复libDexHelper.so ...
  • justFWD
  • justFWD
  • 2016-03-15 20:09
  • 3964

Android常见App加固厂商脱壳方法的整理

目录 简述(脱壳前学习的知识、壳的历史、脱壳方法) 第一代壳 第二代壳 第三代壳 第N代壳 简述 Apk文件结构 Dex文件结构 壳史 壳的识别 Apk文件结构 Dex文件结构 壳史 第一代壳 Dex加密 Dex字符串加密 资源加密 对抗反编译 反调试 自定义DexC...
  • mergerly
  • mergerly
  • 2017-03-14 16:50
  • 4616

Android Apk加固厂商特征,Apk加固哪家强?

爱加密 libexec.so, libexecmain.so,ijiami.dat 娜迦 libchaosvmp.so , libddog.solibfdog.so 梆梆 libsecexe.so, libsecmain.so  梆梆企业版 libDexHelper.so ...
  • ab6326795
  • ab6326795
  • 2017-12-25 16:48
  • 301

DexHunter学习笔记记录

源地址 http://ju.outofmemory.cn/entry/267514 0x00 前言 前段时间遇到一个app,解包后发现了libDexHekper.so,搜索了一下发现是梆梆的壳,于是花了一段时间研究了一下怎么脱。这篇博客文章在 dexhunter...
  • playboyma
  • playboyma
  • 2016-07-29 10:04
  • 1682

W + E load segments are not allowed

在进行android O的兼容性验证中,我们发现有应用force close,查看log会有W + E load segments are not allowed。 08-10 15:37:30.616  3148  3148 W linker  : ...
  • xiashaohua
  • xiashaohua
  • 2017-08-15 20:04
  • 1227

常见app加固厂商脱壳方法研究

转自:http://www.mottoin.com/89035.html 目录 简述(脱壳前学习的知识、壳的历史、脱壳方法)第一代壳第二代壳第三代壳第N代壳 简述 Apk文件结构Dex文件结构壳史壳的识别 Apk文件结构 Dex文件结构 ...
  • threadroc
  • threadroc
  • 2017-07-15 17:37
  • 643

ELF文件格式学习,section修复

0x0001 小弟学习Android逆向也有一段时间,翻看大神们的帖子收获了不少,开始对ELF、so文件很感兴趣,尤其对内存dump和修复的技术很好奇,看了ThomasKing的ELF section修复的帖子更是深受启发,链接:http://www.52pojie.cn/thread-294...
  • yi_nuo_wang
  • yi_nuo_wang
  • 2017-05-22 15:56
  • 980

Android中apk加固完善篇之内存加载dex方案实现原理(不落地方式加载)

时隔半年,困扰的问题始终是需要解决的,之前也算是没时间弄,今天因为有人在此提起这个问题,那么就不能不解决了,这里写一篇文章记录一下吧。那么是什么问题呢?就是关于之前的一个话题:Android中apk加固技术实现关于这个问题,之前的一篇文章已经说过了,没有了解的同学可以点击这里:Android中apk...
  • jiangwei0910410003
  • jiangwei0910410003
  • 2016-06-02 08:22
  • 16255

脱壳步骤二-修复

常规操作: 1、寻找OEP:OD载入,找到OEP,停在OEP处(一般在push ebp代码处)。 2、生成转存文件:打开LordPE,选中程序的进程,右键“修正镜像大小”,然后右键“完整转存”,输入文件名,保存。此步也可用OD自带的插件进行,在代码区右键“用OllyDump脱壳调试进程”...
  • asciil
  • asciil
  • 2014-04-29 10:52
  • 1458
    联系作者
    通过QQ与我联系(全天候7*24小时基本不在线)
    最新评论
    免责声明
    如果转载的文章侵犯了您的版权,请务必告知,我将立刻删除;
    博客所有文章允许转载,原创类不要求注明出处,随意就好;
    如果是转载的文章,建议直接转载原始来源,因为原作者极可能有更新