首页
论坛
专栏
课程

[原创]内存dump 获得基于Unity3d的游戏相关代码

lhfzhh
1
2017-12-30 22:50 5217

1.准备

解压apk,在lib文件夹看到有libmono.so,libunity.so 便是Unity3d的程序。又看到有libDexHelper.so,便是梆梆的加固,把 libmono.so拖到IDA是解析不出来的。然后去看 assets\bin\Data\Managed 下,便是 Unity3d的.NET程序,把对应dll拖到ILSpy 也是不识别的。

这种的加密手段一般都是通过修改 mono代码 (现在都是这个函数mono_image_open_from_data_with_name) 在加载dll的时候进行解密,所以需要先还原 libmono.so。

2.dump内存

用什么dump呢,自然是需要找一个好点的工具,我是用了GameGuardian来导出内存。需要注意的是,需要配置在激活  GameGuardian 的时候,暂停程序,因为程序一般都会监听 /proc/pid/mem或/proc/pid/pagemap,遇到读内存的时候杀掉自身进程。用这个工具有时候也是不能导出内存,也不知道为什么,有时候就可以。


选择对应的libmono.so地址,主要怕导出的少了,就导出的范围广一些, 14c00000-15000000。导出到文件后就开始分析了。

3.修正dump的so

选用010 Editor来编辑二进制文件,这个工具上有模板插件帮助分析elf文件格式。


修正内存dump出来的so文件还是比较简单的,主要是修正program_header_table。重要的是Loadable Segment和Dynamic Segment,前一个是代码段,后一个是符号表。其他段完全可以设置为0。基本原理就是系统加载so的过程,主要就是段的地址,大小及对齐方式在文件和内存中可能不一样。对于IDA来讲是解析的文件形式的。那么只要把对应文件里面的段地址及大小和对齐改成和内存中的一致就可以了。如下图修改前和修改后的对比

修改前的段的地址


修改后的段的地址

主要就是修改 p_offset_FROM_FILE_BEGIN 和 p_filesz_SEGMENT_FILE_LENGTH 为对应的内存地址。至于section就忽略了。
然后用IDA打开就能看到对应的代码了

搜索一下字符串Assembly-CSharp,就能看到对应的代码引用


4.查找dll

简单分析一下应该是用了xxtea的一个变种加密算发,因为从网上找了源码解码对应的dll不成功。怎么办呢?难道要分析解码过程? 这有点麻烦。
想想还是从内存中找吧,毕竟mono也是按照windows的PE格式加载dll的。但是dll的内存地址信息不会像so一样可以通过/proc/pid/maps等看到,那就只有把内存全部dump出来再找了。还是用 GameGuardian导出所有地址的内容,他会按照 /proc/pid/maps指明的段保存内存。文件太多怎么找呢?只有写程序了,毕竟PE的格式也是固定的。
查找过程如下代码:
char * findDosHeader(char * buffer, int length, int *out_length) {
	char * pe = NULL;
	for (int i = 0; i < length - 0x200; i++) {
		WORD * p = (WORD *)(buffer + i);
		if (*p != IMAGE_DOS_SIGNATURE) {
			continue;
		}
		IMAGE_NT_HEADERS * ntheader = (IMAGE_NT_HEADERS *)(buffer + 0x80 + i);
		if (ntheader->Signature != IMAGE_NT_SIGNATURE){
			continue;
		}
		IMAGE_FILE_HEADER * fileheader = &ntheader->FileHeader;
		if (!(fileheader->Characteristics & IMAGE_FILE_DLL)){
			continue;
		}
		IMAGE_SECTION_HEADER * section = IMAGE_FIRST_SECTION(ntheader);
		int size = 0;
		for (int j = 0; j < fileheader->NumberOfSections; j++) {
			if ((section + j)->SizeOfRawData + (section + j)->PointerToRawData > size) {
				size = (section + j)->SizeOfRawData + (section + j)->PointerToRawData;
			}
		}
		*out_length = size;
		return (buffer + i);
	}
	return NULL;
}

void find(const char * fileName) {
	FILE *fp = NULL;
	fopen_s(&fp, fileName, "rb");
	if (fp == NULL) {
		return;
	}
	fseek(fp, 0L, SEEK_END);
	int length = ftell(fp);
	fseek(fp, 0L, SEEK_SET);

	char * pFileBuffer = new char[length];
	fread_s(pFileBuffer, length, 1, length, fp);
	char* ppe = pFileBuffer;
	int image_length = 0;
	int buffer_length = length;
	while (true)
	{
		char f[512];
		buffer_length -= image_length;
		ppe = findDosHeader(ppe + image_length, length - (ppe - pFileBuffer) - image_length, &image_length);
		if (ppe != NULL) {
			sprintf_s(f, "%s.%x-%x.dll", fileName, ppe, image_length);
			FILE* outfp = 0;
			fopen_s(&outfp, f, "wb+");
			fwrite(ppe, image_length, 1, outfp);
			fclose(outfp);
			printf("%x %x\n", ppe, image_length);
		}
		else{
			break;
		}
	}
	
	delete[] pFileBuffer;
	fclose(fp);
}
查完后会把找到的dll存储到对应的文件夹下。
然后用ILSpy打开查看dll,呵呵,代码都正常显示。当然,如果想要改代码,要么是查解码dll的算法,要么就是直接修改没有加密的dll,植入相关的代码也可以。如果要是把dll也加密混淆一下,就伤脑筋了。



[防守篇]2018看雪.TSRC CTF 挑战赛(团队赛)11月1日征题开启!

最新回复 (3)
zylyy 2017-12-31 10:07
2

0

wx_心如止水 2018-1-12 10:47
3

0

请问你是怎么用GG修改器看这些文件的内存范围的
就这样看 2018-3-23 14:56
4

0

国内首个Unity3D加密解决方案——Virbox  Protector加壳工具,一键加壳,无需手动编写代码。可对代码及资源进行保护,防止代码被反编译,防止资源被拷贝,驱动级别反调试,秒杀常见调试器。快来加入QQ群【Unity3D保护】(群号730803578),发现精彩内容。  ​​​​下载地址:http://www.sense.com.cn/VirboxProtector.html
返回