某坏人游戏app
分析了十几款协议棋牌app协议,挑一个简单的记录一下
unsigned __int8 *__fastcall cocos2d::CCFileUtils::getFileData(cocos2d::CCFileUtils *this, const char *a2, const char *a3, unsigned int *a4)
...
///r4保存new出来的地址
.text:76BD1D64 MOV R0, R5 ; unsigned int
.text:76BD1D68 BL j__Znaj ; operator new[](uint)
.text:76BD1D6C MOV R4, R0
.text:76BD1D70 MOV R0, R4 ; ptr
.text:76BD1D74 MOV R1, #1 ; size
.text:76BD1D78 MOV R2, R5 ; n
.text:76BD1D7C MOV R3, R7 ; stream
//fopen读取文件
.text:76BD1D80 BL fread
.text:76BD1D84 STR R0, [R6]
.text:76BD1D88 MOV R0, R7 ; stream
.text:76BD1D8C BL fclose
.text:76BD1D90 LDR R0, [R6]
.text:76BD1D94 CMP R0, #8
.text:76BD1D98 BCC loc_76BD1E44
.text:76BD1D9C LDRB R1, [R4]
//读取到的文件内容的头8个字节与 fuckyou! 依次比较
.text:76BD1DA0 CMP R1, #0x66 ; 'f'
.text:76BD1DA4 LDREQB R1, [R4,#1]
.text:76BD1DA8 CMPEQ R1, #0x75 ; 'u'
.text:76BD1DAC BNE loc_76BD1E44
.text:76BD1DB0 LDRB R1, [R4,#2]
.text:76BD1DB4 CMP R1, #0x63 ; 'c'
.text:76BD1DB8 LDREQB R1, [R4,#3]
.text:76BD1DBC CMPEQ R1, #0x6B ; 'k'
.text:76BD1DC0 BNE loc_76BD1E44
.text:76BD1DC4 LDRB R1, [R4,#4]
.text:76BD1DC8 CMP R1, #0x79 ; 'y'
.text:76BD1DCC LDREQB R1, [R4,#5]
.text:76BD1DD0 CMPEQ R1, #0x6F ; 'o'
.text:76BD1DD4 BNE loc_76BD1E44
.text:76BD1DD8 LDRB R1, [R4,#6]
.text:76BD1DDC CMP R1, #0x75 ; 'u'
.text:76BD1DE0 LDREQB R1, [R4,#7]
.text:76BD1DE4 CMPEQ R1, #0x21 ; '!'
.text:76BD1DE8 BNE loc_76BD1E44
//memmove8个字节,也就是跳过'fuckyou!'
.text:76BD1DEC SUB R7, R0, #8
.text:76BD1DF0 ADD R1, R4, #8
.text:76BD1DF4 MOV R0, R4
.text:76BD1DF8 MOV R2, R7
.text:76BD1DFC STR R7, [R6]
.text:76BD1E00 BL __aeabi_memmove
.text:76BD1E04 CMP R7, #0
.text:76BD1E08 BEQ loc_76BD1E44
//r1保存byte_76F8FD78地址, r0 = 0
.text:76BD1E0C LDR R1, =(byte_76F8FD78 - 0x76BD1E1C)
.text:76BD1E10 MOV R0, #0
.text:76BD1E14 ADD R1, PC, R1 ; byte_76F8FD78
.text:76BD1E18
.text:76BD1E18 MOV R3, R0,ASR#31
.text:76BD1E1C LDRB R2, [R4,R0]
.text:76BD1E20 ADD R3, R0, R3,LSR#24
.text:76BD1E24 BIC R3, R3, #0xFF
.text:76BD1E28 SUB R3, R0, R3
.text:76BD1E2C LDRB R3, [R1,R3]
//内容异或
.text:76BD1E30 EOR R2, R2, R3
.text:76BD1E34 STRB R2, [R4,R0]
//每次递增r0
.text:76BD1E38 ADD R0, R0, #1
.text:76BD1E3C CMP R0, R7
.text:76BD1E40 BCC loc_76BD1E18
.text:76BD1E44
.text:76BD1E44 loc_76BD1E44 ; CODE XREF: cocos2d::CCFileUtils::getFileData(char const*,char const*,ulong *)+5C↑j
.text:76BD1E44 ; cocos2d::CCFileUtils::getFileData(char const*,char const*,ulong *)+C4↑j ...
.text:76BD1E44 LDR R1, =(off_770283F8 - 0x76BD1E54)
.text:76BD1E48 LDR R0, [SP,#0x28+filename]
.text:76BD1E4C LDR R1, [PC,R1] ; dword_77058630
.text:76BD1E50 SUB R6, R0, #0xC
.text:76BD1E54 CMP R6, R1
.text:76BD1E58 BNE loc_76BD1ED4
...
int LuaStack::luaLoadBuffer(lua_State *L, const char *chunk, int chunkSize, const char *chunkName)
...
.text:004CB0C4 MOV R0, #0
.text:004CB0C8 SUB R1, R6, R5 ; unsigned int
.text:004CB0CC STR R0, [SP,#0x28+var_20]
.text:004CB0D0 ADD R0, SP, #0x28+var_20
.text:004CB0D4 LDR R2, [R4,#0x20] ; unsigned __int8 *
.text:004CB0D8 LDR R3, [R4,#0x24] ; unsigned int
.text:004CB0DC STR R0, [SP,#0x28+var_28] ; unsigned int *
.text:004CB0E0 ADD R0, R7, R5 ; unsigned __int8 *
//调用_byds_d_解密
.text:004CB0E4 BL j__Z8_byds_d_PhjS_jPj ; _byds_d_(uchar *,uint,uchar *,uint,uint *)
.text:004CB0E8 LDR R4, [SP,#0x28+var_20]
.text:004CB0EC MOV R5, R0
.text:004CB0F0 MOV R0, #0x100000
.text:004CB0F4 STR R0, [SP,#0x28+destLen]
.text:004CB0F8 MOV R0, #0x100000 ; unsigned int
.text:004CB0FC BL j__Znaj ; operator new[](uint)
.text:004CB100 ADD R1, SP, #0x28+destLen ; destLen
.text:004CB104 MOV R2, R5 ; source
.text:004CB108 MOV R3, R4 ; sourceLen
.text:004CB10C MOV R6, R0
//调用uncompress解压
.text:004CB110 BL uncompress
...
//_byds_d_内部调用sub_76AC060C
.text:76AC060C sub_76AC060C
...
//0xB54CDA56, 0x9E3779B9
.text:76AC072C BL sub_76E97B68
.text:76AC0730 LDR R1, =0xB54CDA56
.text:76AC0734 LDR R3, =0x9E3779B9
.text:76AC0738 LDR R6, [SP,#0x30+ptr]
.text:76AC073C SUBS R10, R4, #1
.text:76AC0740 MLANE LR, R0, R3, R1
.text:76AC0744 CMPNE LR, #0
.text:76AC0748 BEQ loc_76AC080C
.text:76AC074C LDR R1, [SP,#0x30+var_28]
.text:76AC0750 MOV R0, R6
...
.rodata:76F74B00 byte_76F74B00 DCB 5, 0, 0, 0, 1, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 9
.rodata:76F74B00 ; DATA XREF: HttpManagerLua::sendHttpRequest(HttpReqDefLua,char const*,char const*,uint)+484↑o
.rodata:76F74B00 ; .text:off_76862F94↑o ...
.rodata:76F74B00 DCB 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 3, 0
.rodata:76F74B00 DCB 0, 0, 0, 0, 0, 0
.text:768624C8 ; HttpManagerLua::sendHttpRequest(HttpReqDefLua, char const*, char const*, unsigned int)
...
.text:76862930 loc_76862930 ; CODE XREF: HttpManagerLua::sendHttpRequest(HttpReqDefLua,char const*,char const*,uint)+454↑j
.text:76862930 MOV R0, R4 ; unsigned int
.text:76862934 BL j__Znaj ; operator new[](uint)
.text:76862938 MOV R6, R0
.text:7686293C CMP R7, #0
.text:76862940 BEQ loc_76862984
.text:76862944 LDR R1, =(byte_76F74B00 - 0x76862954)
.text:76862948 MOV R0, #0
.text:7686294C ADD R1, PC, R1 ; byte_76F74B00
.text:76862950
// 除以10, 被编译成优化成乘法计算,
.text:76862950 LDR R2, =0x66666667
.text:76862954 SMULL R3, R4, R0, R2
.text:76862958 MOV R2, R4,LSR#2
.text:7686295C LDRB R3, [R5,R0]
.text:76862960 ADD R2, R2, R4,LSR#31
.text:76862964 ADD R2, R2, R2,LSL#2
.text:76862968 SUB R2, R0, R2,LSL#1
.text:7686296C LDR R2, [R1,R2,LSL#2]
//异或
.text:76862970 EOR R2, R2, R3
.text:76862974 STRB R2, [R6,R0]
.text:76862978 ADD R0, R0, #1
.text:7686297C CMP R7, R0
.text:76862980 BNE loc_76862950
以上汇编还原成python代码,大概就是
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import zlib
import struct
import xxtea
xor_key = [
0xA4, 0x11, 0x98, 0xD3, 0xB2, 0x41, 0x27, 0x8F, 0x56, 0x14, 0xF1, 0x74, 0xF6, 0xCA, 0xE0, 0x7D,
0x32, 0xFF, 0x92, 0xE2, 0x36, 0xA8, 0x94, 0x6E, 0xF0, 0x10, 0xDC, 0xF7, 0xC6, 0x66, 0x81, 0x91,
0xE4, 0xA1, 0x12, 0x28, 0xF5, 0x3B, 0xAE, 0x1B, 0xD9, 0x6E, 0x64, 0x19, 0xC4, 0xD2, 0x19, 0x59,
0x93, 0x23, 0x2D, 0xA1, 0x92, 0x54, 0xDD, 0xF2, 0xEF, 0x24, 0xD0, 0x9B, 0x3A, 0x20, 0xBE, 0x7C,
0xF2, 0x6D, 0x3A, 0x66, 0x85, 0x77, 0x9B, 0x2B, 0x1A, 0xC8, 0xC6, 0x64, 0xFA, 0x62, 0xCB, 0xCA,
0xAB, 0xC1, 0xC9, 0x21, 0x71, 0x96, 0xA3, 0xA9, 0xC7, 0xB0, 0x9A, 0xC2, 0x6B, 0x46, 0x9A, 0xBF,
0xFC, 0x6C, 0xFA, 0x16, 0x5C, 0xEF, 0x90, 0xED, 0x9F, 0x6C, 0xAB, 0x25, 0x94, 0x5A, 0x5C, 0xAF,
0x4E, 0x2A, 0xC4, 0xC6, 0xB0, 0xD8, 0x71, 0x50, 0x38, 0x82, 0x1A, 0xF7, 0x2A, 0x45, 0xE8, 0x2E,
0x64, 0x9E, 0x65, 0x32, 0x7A, 0x3C, 0x33, 0x13, 0x51, 0x1F, 0xA7, 0x82, 0x67, 0x8D, 0xE9, 0x2C,
0x4C, 0x7F, 0xA7, 0xCF, 0x9E, 0x2E, 0x79, 0x72, 0xB9, 0x72, 0xBF, 0x4F, 0x14, 0x53, 0x9B, 0xFC,
0xD9, 0x3B, 0xEE, 0x15, 0xF8, 0xB0, 0x4C, 0xF1, 0xCB, 0x79, 0x9B, 0x5D, 0x42, 0xF7, 0xEB, 0xAE,
0xEB, 0x8F, 0xB9, 0xBC, 0xBE, 0xA1, 0x7E, 0x9E, 0x38, 0xBE, 0xE9, 0x7D, 0xB2, 0x8D, 0x3D, 0x82,
0x90, 0x39, 0xCD, 0x17, 0x49, 0xCF, 0x4B, 0xF2, 0x1C, 0x59, 0x2A, 0xA3, 0x73, 0x72, 0x66, 0xFE,
0x3E, 0x30, 0xD5, 0xE4, 0xB9, 0x19, 0xD7, 0xAC, 0x72, 0x5F, 0x21, 0x37, 0x2E, 0x6B, 0x14, 0xA4,
0x8C, 0x35, 0xC1, 0x16, 0xFF, 0xCC, 0xB3, 0x3F, 0x1D, 0x1A, 0xA4, 0xA5, 0xF1, 0x83, 0xD1, 0x99,
0x29, 0x80, 0x25, 0xBD, 0xD0, 0x4E, 0xD8, 0x9C, 0xBE, 0xE9, 0xB8, 0xDD, 0xCB, 0x21, 0x94, 0x49
]
def lua_decrypt(path):
with open(path, 'rb') as f:
buf = f.read()[len('byds'):]
zlib_buf = xxtea.decrypt(buf, '-4051-3138-1229-1250192182')
ret = zlib.decompress(zlib_buf)
return ret
def res_decrypt(path):
out = []
with open(path, 'rb') as f:
buf = f.read()[len('fuckyou!'):]
for index, i in enumerate(buf):
out.append(chr(ord(i) ^ xor_key[index % len(xor_key)]))
return ''.join(out)
def wanneng_xor(data):
ret = []
byte_76F74B00 = [5, 1, 6, 7, 9, 2, 4, 8, 3, 0]
for index, i in enumerate(data):
ret.append(chr(byte_7692FC10[index % len(byte_76F74B00)] ^ ord(i)))
return ''.join(ret)
解密出lua和资源后,意外发现如果粗心的程序员打包了pb定义文件。
如果是标准protoc编译出来的cpp文件里面保存了二进制格式pb定义, 可以直接利用protoc的一些类库直接反向生成.proto文件
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
int main(int argc, char *argv[])
{
google::protobuf::FileDescriptorProto fileProto;
fileProto.ParseFromFileDescriptor(open(argv[1], O_RDONLY));
google::protobuf::DescriptorPool pool;
const google::protobuf::FileDescriptor* desc =
pool.BuildFile(fileProto);
std::cout << desc->DebugString() << std::endl;
return 0;
}
有了pb,再分析出组包逻辑,签名算法。最后scrapy通过adsl代理做高频的协议巡检