首页
社区
课程
招聘
[求助] 某热门游戏 android so部分解密算法
发表于: 2014-6-2 17:08 42654

[求助] 某热门游戏 android so部分解密算法

2014-6-2 17:08
42654
这个星期在逆向移动平台上非常火热的一款游戏,它是用 cocos2d-x 游戏引擎开发的,游戏逻辑是用 lua 脚本写的,但是很可惜,资源文件里的lua脚本都经过了加密,于是我就逆向了它的c++代码。

它的解密算法一共分为2个部分:

1.  将 /data/ 文件夹下的 <filename>.abc 文件 通过异或算法解密成 .zip 压缩文件.

2. 再用 zlib 库将 zip 压缩文件解压成 lua 代码.

这里我给出最重要的第一部分的逆向结果。

安卓 arm 版本逆向结果:

 loc_23AB9A                              ; CODE XREF: cocos2d::CCFileUtils::getEncryptedFileData(char const*,ulong *)+E6j
.text:0023AB9A 04 95                                   STR     R5, [SP,#0xE8+var_D8] ; R5 = 0x17
.text:0023AB9C 00 24                                   MOVS    R4, #0          ; R4 = 0
.text:0023AB9E ED 17                                   ASRS    R5, R5, #0x1F   ; R5 = r5 >> 31 /// 0
.text:0023ABA0 05 95                                   STR     R5, [SP,#0xE8+var_D4] ; VAR_d4 = r5
.text:0023ABA2 00 20                                   MOVS    R0, #0
.text:0023ABA4 00 21                                   MOVS    R1, #0
.text:0023ABA6 25 1C                                   MOVS    R5, R4
.text:0023ABA8 03 97                                   STR     R7, [SP,#0xE8+var_DC] ; var_DC = this
.text:0023ABAA 22 E0                                   B       loc_23ABF2      ; R2 = data_size
.text:0023ABAC                         ; ---------------------------------------------------------------------------
.text:0023ABAC
.text:0023ABAC                         loc_23ABAC                              ; CODE XREF: cocos2d::CCFileUtils::getEncryptedFileData(char const*,ulong *)+B4j
.text:0023ABAC 44 48                                   LDR     R0, =(aGetfiledatafro - loc_23ABB6)
.text:0023ABAE 45 49                                   LDR     R1, =(aCocos2dXAssert - loc_23ABC0)
.text:0023ABB0 45 4A                                   LDR     R2, =(aSFunctionSLine - loc_23ABC2)
.text:0023ABB2 78 44                                   ADD     R0, PC          ; "getFileDataFromZipData"
.text:0023ABB4 60 30                                   ADDS    R0, #0x60
.text:0023ABB6
.text:0023ABB6                         loc_23ABB6                              ; DATA XREF: .text:off_23ACC0o
.text:0023ABB6 45 4B                                   LDR     R3, =(aUsersBuilde_74 - loc_23ABC6)
.text:0023ABB8 00 90                                   STR     R0, [SP,#0xE8+var_E8]
.text:0023ABBA 45 48                                   LDR     R0, =0x312
.text:0023ABBC 79 44                                   ADD     R1, PC          ; "cocos2d-x assert"
.text:0023ABBE 7A 44                                   ADD     R2, PC          ; "%s function:%s line:%d"
.text:0023ABC0
.text:0023ABC0                         loc_23ABC0                              ; DATA XREF: .text:off_23ACC4o
.text:0023ABC0 01 90                                   STR     R0, [SP,#0xE8+var_E4]
.text:0023ABC2
.text:0023ABC2                         loc_23ABC2                              ; DATA XREF: .text:off_23ACC8o
.text:0023ABC2 7B 44                                   ADD     R3, PC          ; "/Users/builder/Perforce/dgame/d-game/Cl"...
.text:0023ABC4 06 20                                   MOVS    R0, #6
.text:0023ABC6
.text:0023ABC6                         loc_23ABC6                              ; DATA XREF: .text:off_23ACCCo
.text:0023ABC6 0A F7 2C ED                             BLX     __android_log_print
.text:0023ABCA E6 E7                                   B       loc_23AB9A      ; R5 = 0x17
.text:0023ABCC                         ; ---------------------------------------------------------------------------
.text:0023ABCC
.text:0023ABCC                         loc_23ABCC                              ; CODE XREF: cocos2d::CCFileUtils::getEncryptedFileData(char const*,ulong *)+11Ej
.text:0023ABCC 00 23                                   MOVS    R3, #0
.text:0023ABCE D3 F1 D8 EF                             BLX     __aeabi_uldivmod ; A = (R1 << 32) + R0;
.text:0023ABCE                                                                 ; B = (R3 << 32) + R2;
.text:0023ABCE                                                                 ; R1 = (A/B) >> 32;
.text:0023ABCE                                                                 ; R0 = (A/B) & 0xFFFFFFFF;
.text:0023ABCE                                                                 ; R3 = (A%B) >> 32;
.text:0023ABCE                                                                 ; R2 = (A%B) & 0xFFFFFFFF
.text:0023ABD2 08 9E                                   LDR     R6, [SP,#0xE8+var_C8] ; R6 = pFileNameBuffer
.text:0023ABD4 07 9F                                   LDR     R7, [SP,#0xE8+var_CC] ; R7 = pFileDataBuffer
.text:0023ABD6 11 1C                                   MOVS    R1, R2          ; R1 = R0 % R2
.text:0023ABD8 32 5D                                   LDRB    R2, [R6,R4]     ; R2 = *( (char*)pFileNameBuffer + R4)
.text:0023ABDA 7B 5D                                   LDRB    R3, [R7,R5]     ; R3 = *( (char*)pFileDataBuffer + R5)
.text:0023ABDC 06 9E                                   LDR     R6, [SP,#0xE8+var_D0] ; R6 = pOutputBuffer
.text:0023ABDE 60 1C                                   ADDS    R0, R4, #1      ; R0 = R4 + 1
.text:0023ABE0 53 40                                   EORS    R3, R2          ; R3 = R3 ^ R2
.text:0023ABE2 73 54                                   STRB    R3, [R6,R1]     ; *( (char*)pOutputBuffer + R1) = R3
.text:0023ABE4 09 99                                   LDR     R1, [SP,#0xE8+var_C4] ; R1 = strlen(pFileNameBuffer)
.text:0023ABE6 D2 F1 72 EE                             BLX     __aeabi_idivmod ; R1 = R0 % R1
.text:0023ABEA 01 35                                   ADDS    R5, #1          ; ++R5
.text:0023ABEC 0C 1C                                   MOVS    R4, R1          ; R4 = R1
.text:0023ABEE 0A 98                                   LDR     R0, [SP,#0xE8+var_C0]
.text:0023ABF0 0B 99                                   LDR     R1, [SP,#0xE8+var_BC]
.text:0023ABF2
.text:0023ABF2                         loc_23ABF2                              ; CODE XREF: cocos2d::CCFileUtils::getEncryptedFileData(char const*,ulong *)+C6j
.text:0023ABF2 0F 9A                                   LDR     R2, [SP,#0xE8+var_AC] ; R2 = data_size
.text:0023ABF4 04 9E                                   LDR     R6, [SP,#0xE8+var_D8] ; var_D8 = 0x17
.text:0023ABF6 05 9F                                   LDR     R7, [SP,#0xE8+var_D4] ; R7 = 0
.text:0023ABF8 36 18                                   ADDS    R6, R6, R0      ; R6 += R0
.text:0023ABFA 4F 41                                   ADCS    R7, R1          ; R7 += R1
.text:0023ABFC 0A 96                                   STR     R6, [SP,#0xE8+var_C0]
.text:0023ABFE 0B 97                                   STR     R7, [SP,#0xE8+var_BC]
.text:0023AC00 95 42                                   CMP     R5, R2          ; if( R5 < R2 ) ///for looper
.text:0023AC02 E3 D3                                   BCC     loc_23ABCC
.text:0023AC04 07 9C                                   LDR     R4, [SP,#0xE8+var_CC]
.text:0023AC06 03 9F                                   LDR     R7, [SP,#0xE8+var_DC]
.text:0023AC08 00 2C                                   CMP     R4, #0
.text:0023AC0A 02 D0                                   BEQ     loc_23AC12
.text:0023AC0C 20 1C                                   MOVS    R0, R4          ; void *
.text:0023AC0E C5 F1 44 ED                             BLX     _ZdaPv          ; operator delete[](void *)


然后我根据这个逆向结果,写了一个c++的解密算法:

static void decode(const std::vector<unsigned char>& data, const std::size_t data_size,
           const std::vector<unsigned char>& name, const std::size_t name_size,
           std::vector<unsigned char>& out)
{
  const int primes[] = {0x17, 0x19, 0x35, 0x37, 0x4d};

  int prime(0);
  for(auto i : primes) {
    if(data_size % i) {
      prime = i;
      break;
    }
  }

  std::cout << "prime : " << prime << std::endl;
  std::cout << "data_size : " << data_size << std::endl;

  int name_index(0);
  for(int i = 0; i < int(data_size); ++i)
  {
    unsigned int index = prime * i;

    unsigned char r2 = data[i];
    unsigned char r3 = name[name_index];

    unsigned long long A = (0LL << 32) + index;
    unsigned long long B = (0LL << 32) + data_size;

    unsigned long long mod = A % B;

    unsigned int pos = unsigned int(mod & 0xFFFFFFFF);

    out[pos] = r2 ^ r3;

    name_index = (name_index + 1) % name_size;
  }
}


不过,非常可惜的是,我写的解密算法并不能正确的将加密过的资源文件解密成.zip格式的压缩文件。 因为 zip 格式的前4个字节是 0x50, 0x4b, 0x03, 0x04

但是巧合的是,我的解密算法却能保证第一个字节是 0x50, 也就是说,我的解密算法还差最后一个小细节有问题,这个小细节我找了很久都没有找到,能否请各位高手花一点时间提点一下小弟~

谢谢大家~

PS:附件的 data 文件夹里就是加密过的 脚本代码,全部以 .abc 为后缀
libhellolua.so 文件就是 安卓 native 层的动态链接库。

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 0
支持
分享
最新回复 (37)
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
顶,学习了
2014-6-2 20:46
0
雪    币: 507
活跃值: (140)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
3
前排支持,希望楼主能搞定所有代码...
2014-6-3 12:31
0
雪    币: 407
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
这个游戏我也一直在玩,总是钻石和金币不够,冲VIP花了100+,预顶楼主能成功!
2014-6-10 17:08
0
雪    币: 3
活跃值: (414)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
f5 结果
int __fastcall cocos2d::CCFileUtils::getEncryptedFileData(cocos2d::CCFileUtils *this, const char *a2, unsigned __int32 *a3)
{
int v5; // r0@1
int v6; // r1@2
int v7; // r2@2
int v8; // r3@2
char *v9; // r6@2
int v10; // r1@3
int v11; // r2@3
int v12; // r3@3
unsigned int v13; // r5@4
int v14; // r4@8
unsigned __int64 v15; // r0@8
unsigned int v16; // r5@8
int v17; // r3@14
int v18; // r1@15
int v19; // r2@15
int v20; // r3@15
int v21; // r4@3
int result; // r0@18
int v23; // [sp+0h] [bp-E8h]@0
int v24; // [sp+0h] [bp-E8h]@2
char *v25; // [sp+4h] [bp-E4h]@0
signed int v26; // [sp+4h] [bp-E4h]@2
__int64 v27; // [sp+10h] [bp-D8h]@8
void *v28; // [sp+18h] [bp-D0h]@1
void *v29; // [sp+1Ch] [bp-CCh]@1
int v30; // [sp+20h] [bp-C8h]@2
signed int v31; // [sp+24h] [bp-C4h]@2
char v33; // [sp+38h] [bp-B0h]@1
unsigned int v34; // [sp+3Ch] [bp-ACh]@1
int v35; // [sp+40h] [bp-A8h]@1
char v36; // [sp+44h] [bp-A4h]@2
char v37; // [sp+48h] [bp-A0h]@15
char s; // [sp+4Ch] [bp-9Ch]@16
int v39; // [sp+CCh] [bp-1Ch]@1

v39 = _stack_chk_guard;
v34 = 0;
v29 = (void *)(*(int (**)(void))(*(_DWORD *)this + 16))();
v28 = (void *)operator new[](v34);
sub_3EDED4(&v35, a2, &v33);
v5 = sub_3EC2EC(&v35, "/\\", -1);
if ( v5 == -1 )
{
cocos2d::CCLog((cocos2d *)"cocos2d: ERROR: getEncryptedFileData: Invalid filename %s", a2);
v21 = 0;
}
else
{
sub_3EDD74((int)&v36, (int)&v35, v5 + 1);
sub_3EDAD0((int)&v35, (int *)&v36);
sub_3EC914((int)&v36, v6, v7, v8, v23, (int (__fastcall *)(_DWORD))v25);
v30 = v35;
v31 = *(_DWORD *)(v35 - 12);
v9 = byte_47EEA0;
while ( 1 )
{
v13 = *(_DWORD *)v9;
v9 += 4;
if ( v34 % v13 )
break;
if ( v9 == "getEncryptedFileData" )
{
v13 = v34 % v13;
break;
}
}
cc_assert_script_compatible((((signed int)v13 >> 31) - v13) >> 31, "My file can't be that big!");
if ( (signed int)v13 <= 0 )
{
v24 = (int)"getEncryptedFileData";
v26 = 786;
_android_log_print(
6,
"cocos2d-x assert",
"%s function:%s line:%d",
"/Users/builder/Perforce/dgame/d-game/Client/ws.android/longtu/../../cocos2d-2.1rc0-x-2.1.2/cocos2dx/platform/CCFileUtils.cpp");
}
v14 = 0;
v27 = (signed int)v13;
v15 = 0LL;
v16 = 0;
while ( v16 < v34 )
{
*((_BYTE *)v28 + v15 % v34) = *((_BYTE *)v29 + v16++) ^ *(_BYTE *)(v30 + v14);
v14 = (v14 + 1) % v31;
v15 += v27;
}
if ( v29 )
operator delete[](v29);
v17 = sub_3EC2EC(&v35, ".", -1);
if ( v17 != -1 )
{
sub_3EDD74((int)&v37, (int)&v35, 0);
sub_3EDAD0((int)&v35, (int *)&v37);
sub_3EC914((int)&v37, v18, v19, v20, v24, (int (__fastcall *)(_DWORD))v26);
}
sprintf(&s, "cocos2d: ERROR: Invalid filename %s", v35, v17, v24, v26);
v23 = (int)a3;
v25 = &s;
v21 = (*(int (__fastcall **)(cocos2d::CCFileUtils *, void *, unsigned int, _DWORD))(*(_DWORD *)this + 24))(
this,
v28,
v34,
"data");
if ( v28 )
operator delete[](v28);
}
sub_3EC914((int)&v35, v10, v11, v12, v23, (int (__fastcall *)(_DWORD))v25);
result = v21;
if ( v39 != _stack_chk_guard )
_stack_chk_fail(v21);
return result;
}
2014-6-10 17:53
0
雪    币: 228
活跃值: (60)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
请问楼主有新进展嘛
2014-6-14 12:50
0
雪    币: 1413
活跃值: (401)
能力值: (RANK:270 )
在线值:
发帖
回帖
粉丝
7
麻烦楼主看下版规,做适当的修改。
2014-6-15 14:23
0
雪    币: 92
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
已经修改!
2014-6-17 23:00
0
雪    币: 236
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
修改了一下,解密出了zip文件。(传入的name,必须为全文件名,包括后缀。如:UnitRank.abc)
可是解压zip需要密码。。。
对arm逆向不熟,密码实在是找不到。

void decode(const char* data, int data_size, const char* name, int name_size, char* out)
{
	const unsigned int primes[] = {0x2717, 0x2719, 0x2735, 0x2737, 0x274d, 0x2753};

	int prime = 0;
	for(auto i : primes)
	{
		if(data_size % i)
		{
			prime = i;
			break;
		}
	}

	unsigned __int64 index = 0LL;
	int index1 = 0;
	int index2 = 0;

	while( index1 < data_size )
	{
		out[index % data_size] = data[index1] ^ name[index2];

		index1 += 1;
		index2 = (index2 + 1) % name_size;
		index += prime;
	}
}
2014-6-18 11:53
0
雪    币: 1866
活跃值: (95)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
10
动态调试一下。应该可以得到你想要的结果。
2014-6-18 14:12
0
雪    币: 183
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
看来还需要zip解密啊
2014-6-18 14:32
0
雪    币: 5855
活跃值: (438)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
12
debug下,直接用gdb把解密后的lua给dump下来了,附一个例子文件。
不过从来没搞过lua反编译,用了luadec提示

bad code in precompiled chunk


牛人继续

affix.rar
上传的附件:
2014-6-20 18:01
0
雪    币: 217
活跃值: (99)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
13
解出的lua字节码的OP是有混淆替换的, 不过即使替换回正确的OP, luadec也不够给力, 目前也没有更好的反编译工具.
2014-6-20 18:56
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
studing
2014-6-24 09:29
0
雪    币: 92
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
可以通过逆向 Lua 执行引擎,因为它总会把错误的op还原成正确的
2014-6-24 18:05
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
我拿IDA反的, 看到的反编译ARM OPCODE和楼上的不大一样, 也不太知道怎么去读ARM的这些代码 , 学习中... 所以已经有人解出了LUA逻辑了么
2014-6-25 22:33
0
雪    币: 44
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
其实不需要解密。直接hook lua的api可以勾出解密后的字节码,但字节码还没找到好的处理方法,luadec提示失败跟12楼一样,跟请高人指点
2014-7-9 17:31
0
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
请问反解出来的zip文件解压密码是多少??
2014-7-9 22:41
0
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
[QUOTE=NightGuard;1294698]debug下,直接用gdb把解密后的lua给dump下来了,附一个例子文件。
不过从来没搞过lua反编译,用了luadec提示

牛人继续

affix.rar[/QUOTE]

求怎么破解zip 我看到了 unzOpenCurrentFilePassword 函数..第二个参数就是密码..但是不知道密码从哪里来
2014-7-10 13:30
0
雪    币: 5855
活跃值: (438)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
20
看17楼,我也不知道密码,但是能把解压后的东西dump出来就可以了
2014-7-10 16:09
0
雪    币: 5
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
谁有跟到zip的密码?
2014-7-12 03:07
0
雪    币: 5
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
那个dump出来的luac文件我看了,文件头是没错的,lua5.1,不过数据区是加了密,还得解密,真是折腾....
2014-7-14 17:52
0
雪    币: 100
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
关注一下。
2014-7-15 22:26
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
DEBUG抽取出了东西的同学, 只想问一下你们的调试环境是怎样的? 真机实战么? 还是用了什么模拟器。 THANKS
2014-7-15 22:50
0
雪    币: 210
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
密码是cocos2d: ERROR: Invalid filename %s
%s是文件名(不含路径和扩展名)

等高手讲解lua反编译技术
2014-7-24 14:32
0
游客
登录 | 注册 方可回帖
返回
//