这是一篇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,等待其分析代码完成。 点击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
全立绘打包
留个名,研究研究。。
给大触跪跪跪跪跪跪跪跪,占坑刘明。。。