这个星期在逆向移动平台上非常火热的一款游戏,它是用 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 层的动态链接库。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)