这是一篇ida pro的基础课。

 

准备工具

WINRAR,IDAPRO 6.8等

爆破对象

山海战记B服 4.11.1

 

正式开始

首先使用winrar解压apk文件

使用dex2jar反编译dex (classes.dex) 文件,使用jd-gui查看其代码,发现java层什么都没有干,直接调用了native代码,游戏引擎是cocos2dx。

 

cd /lib/armeabi 找到两枚so库[libbsutils.so,libtriberush.so]

libbsutils.so只有250kb,肯定不是我们要找的东西。 使用ida pro载入libtriberush.so,等待其分析代码完成。QQ截图20160116224702 点击exports标签使用Ctrl+F搜索“java_” 所有以java_开头的函数是在java代码中可以被调用的 如果不清楚游戏的引擎,就需要从这里入手,配合java代码,找到so库的“主函数” 不过因为我们已经知道山海战记使用的是cocos2dx,所以我们可以直接在左侧function栏内搜索DidFinish, 找到一个名叫applicationDidFinishLaunching的函数。 双击进入。 这时候右边的ida-view窗口会显示applicationDidFinishLaunching函数的流程图, 按空格键切换其显示为汇编代码。 这时候就能看到arm的汇编代码了。 这样看起来会很吃力,使用F5将其反编译为c代码。#注意:个别情况ida插件无法反编译或反编译错误,依然需要能看懂汇编代码,并不是能反编译就万事大吉了(比如下面那段。

#如果address栏为红色,需要先找到function起始部分,右键createFuntion创建函数给ida识别

这样码农们辛辛苦苦码的代码就一览无遗了。

 this__ = cocos2d::CCFileUtils::sharedFileUtils(v25);
 v27 = (cocos2d::CCFileUtils *)(*(int (__fastcall **)(int *, int, const char *))(*(_DWORD *)this__ + 28))(
 &v40,
 this__,
 "scripts/main.lua");
 v41 = 0;
 v28 = cocos2d::CCFileUtils::sharedFileUtils(v27);
 v29 = (*(int (**)(void))(*(_DWORD *)v28 + 16))();
 sub_4B0AB4(&v42, v29, v41, (int)&v34);
 (*(void (__fastcall **)(cocos2d::CCScriptEngineManager *, int))(*(_DWORD *)v13 + 24))(v13, v42);
 string::delete(&v42);

/*代码节选*/

可以清楚地看到,游戏是载入一个名为“scripts/main.lua”的脚本,并把它喂给EngineManager。

一般这样使用脚本来实现所有逻辑,会节约开发成本,但是游戏会很卡就是了。

”’

可以看一眼tolua_allpkgs_open这个函数

  tolua_function(v1, “base64Encode”, sub_17D418);

这句

lua执行base64Encode实际上是调用了sub_17D418这个函数。lua原生并没有base64Encode

”’

 

寻找资源名处理的函数

cd /assets/

发现scripts文件夹,cd scripts,并没有看到main.lua,反而看到了 “%s.dat”%hash

读取文件的时候一定对文件名进行了一定操作,才能打开它。

回到函数applicationDidFinishLaunching

cocos2d::CCFileUtils::sharedFileUtils返回了一个虚指针给this__,然后把调用了(this__ + 0x1c)这个function,并把”scripts/main.lua”作为参数传递进了这个函数。
那么我们在左边的function栏搜索一下cocos2d::CCFileUtils,于是cocos2d::CCFileUtils::md5PathForFilename这个函数就蹦到眼前了_(:3」∠)_

cocos2d::CCFileUtils *__fastcall cocos2d::CCFileUtils::md5PathForFilename(cocos2d::CCFileUtils *this, const char *a2, int a3)
{
 // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
v24 = _stack_chk_guard;
 string::constructor_0(this, &unk_516466, &v14);//ida直接反编译出来是sub_xxxxx不会明摆着告诉你这是string的构造函数
 string::constructor_0(&v18, a3, &v15);         //你需要进去看一看才能知道这到底是干什么的,记住这个类型,以后看到string相关的函数就能一眼认出
 v7 = sub_4AEE8C((int)&v18, "/", -1);
 while ( v7 != -1 )
 {
 string::substr((int)&v19, &v18, 0);            //这个ida也不会告诉你
 string::constructor_1(this, &v19);             //这是string构造函数的一个重载
 string::substr((int)&v20, &v18, v7 + 1);       //此外,反编译过程中还可能遇到各种各样的玩意,比如rbtree(hashmap)啥的,ida能认出来,其余还得自行辨认
 string::constructor_string(&v18, &v20);
 string::delete(&v20);
 v7 = sub_4AEE8C((int)&v18, "/", -1);
 string::delete(&v19);
 }
 string::append((int)&v18, "5m1l3t3ch", v5, v6);
 md5(v18, &v23);
 string::constructor_0(&v21, &v23, &v16);
 string::constructor_1(this, &v21);
 string::delete(&v21);
 string::constructor_0(&v22, a3, &v17);
/*篇幅有限,此段省略*/
 result = this;
 if ( v24 != _stack_chk_guard )
 _stack_chk_fail(this);
 return result;
}//_stack_chk_guard这个东西是为了防止栈溢出造成流程劫持的,有兴趣可以搜一下

很清楚的看到,文件名加密的方式是md5(fileName+”5m1l3t3ch”)+extend

验证一下main.lua5m1l3t3ch的md5  c00165e4fc4ccf7b65678f89e213a19f 是不是在scripts/目录下?

照葫芦画瓢,找到文件内容解密函数,就可以解出游戏资源。

 

_(:3」∠)_除了正着找,还可以有各种找法:

倒着找:资源文件在apk压缩包内,解压文件需要zlib的uncompress库,在uncompress函数上右击,选择xrefs to,一路往上游。

中间找:资源文件加密后有个神奇的magic“#!h”,搜索对其验证的代码也可以找到文件解密函数。

或者Alt+B能够搜索字符串。

 

解密立绘文件

立绘文件在 images\heros 下

解密完毕能看到一系列无法用图片浏览器打开的bin文件

使用UE打开

可以在第二行看到PNG的文件头。

删除第一行,保存,解得一个黑白图像。

现在研究第一行

00000000h: 84 BE 01 00 00 00 00 00 3D 4E 04 00 00 00 00 00 ; 劸……=N……

文件大小396,497 字节

小端 *(DWORD*)(0)+*(DWORD*)(8)==396497

可以怀疑bin内堆放了两个文件

跳转到*(DWORD*)(8),看到了jpeg文件头

拆出,得到彩色立绘。

(PNG应该是alpha通道)

 

2016.2.20

CC-BY-SA 3.0

 

全立绘打包

百度云

 

2 thoughts on “山海战记资源解密记录

发表评论

电子邮件地址不会被公开。