首先将仙剑5前传手游APK解压,在assets\bin\Data\Managed中找到Assembly-CSharp.dll,16进制查看发现其已经加密,其他文件(UnityEngine等)未加密,故考虑游戏应是通过libmono中mono_image_open_from_data_with_name的函数来解密Assembly-CSharp.dll
IDA打开libmono.so(位于lib\x86\libmono.so,x86较arm方便分析),找到mono_image_open_from_data_with_name函数,注意这里有一个GetKey函数
F5分析代码,程序将GetKey函数返回的结果作为src然后memcpy,再新建一个image结构体并将dest赋值到image.raw_data,最后调用do_mono_image_load,根据这个逻辑GetKey返回的应是解密后的Assembly,跟进GetKey函数后发现该函数调用了一个名为EnDeCrypt的函数,从字面意思理解其应为解密函数,IDA已经给出了注释:
.mytext:00282CC8 mov [esp+8], edx ; s .mytext:00282CCC mov [esp+4], eax ; size .mytext:00282CD0 mov eax, [ebp+arg_0] .mytext:00282CD3 mov [esp], eax ; int .mytext:00282CD6 call EnDeCrypt
其中s为字符串mrd2cyou147852369,由于GetKey返回是解密后的Assembly数据,故这里推测int应是加密原始数据的指针,size则是原始加密数据的大小
基于上面,下面开始编写解密程序,由于so用的是x86版本,因此可直接将代码拷贝出来并进行调用;EnDecrypt函数共有4个CALL调用:_x86_get_pc_thunk_bx、strlen、malloc、swapints,其中swapints代码位置刚紧跟EnDecrypt,因此无需处理(因为相对地址不变),_x86_get_pc_thunk_bx直接nop去掉,strlen、malloc用相应函数链接(必须为cdecl)即可
以下是Delphi核心解密代码:
var DecCode: array[0..585] of byte = ( $55, $89, $E5, $53, $8D, $A4, $24, $CC, $F7, $FF, $FF, $E8, $69, $38, $D9, $FF, $81, $C3, $58, $81, $10, $00, $C7, $45, $EC, $00, $00, $00, $00, $C7, $45, $E8, $00, $00, $00, $00, $8B, $45, $10, $89, $04, $24, $E8, $F9, $28, $D9, $FF, $89, $45, $E4, $C7, $45, $F4, $00, $00, $00, $00, $EB, $36, $8B, $45, $F4, $89, $C2, $C1, $FA, $1F, $F7, $7D, $E4, $89, $D0, $89, $C2, $8B, $45, $10, $8D, $04, $02, $0F, $B6, $00, $0F, $BE, $D0, $8B, $45, $F4, $89, $94, $85, $DC, $F7, $FF, $FF, $8B, $45, $F4, $8B, $55, $F4, $89, $94, $85, $DC, $FB, $FF, $FF, $83, $45, $F4, $01, $81, $7D, $F4, $FF, $00, $00, $00, $7E, $C1, $C7, $45, $F4, $00, $00, $00, $00, $C7, $45, $F0, $00, $00, $00, $00, $EB, $4F, $8B, $45, $F4, $8B, $94, $85, $DC, $FB, $FF, $FF, $8B, $45, $F0, $01, $C2, $8B, $45, $F4, $8B, $84, $85, $DC, $F7, $FF, $FF, $01, $C2, $89, $D0, $C1, $F8, $1F, $C1, $E8, $18, $01, $C2, $0F, $B6, $D2, $29, $C2, $89, $D0, $89, $45, $F0, $8B, $45, $F0, $89, $44, $24, $08, $8B, $45, $F4, $89, $44, $24, $04, $8D, $85, $DC, $FB, $FF, $FF, $89, $04, $24, $E8, $EB, $00, $00, $00, $83, $45, $F4, $01, $81, $7D, $F4, $FF, $00, $00, $00, $7E, $A8, $8B, $45, $0C, $89, $04, $24, $E8, $6B, $29, $D9, $FF, $89, $45, $E0, $C7, $45, $F4, $00, $00, $00, $00, $E9, $AB, $00, $00, $00, $8B, $45, $EC, $89, $C2, $83, $C2, $01, $89, $D0, $C1, $F8, $1F, $C1, $E8, $18, $01, $C2, $0F, $B6, $D2, $29, $C2, $89, $D0, $89, $45, $EC, $8B, $45, $EC, $8B, $94, $85, $DC, $FB, $FF, $FF, $8B, $45, $E8, $01, $C2, $89, $D0, $C1, $F8, $1F, $C1, $E8, $18, $01, $C2, $0F, $B6, $D2, $29, $C2, $89, $D0, $89, $45, $E8, $8B, $45, $E8, $89, $44, $24, $08, $8B, $45, $EC, $89, $44, $24, $04, $8D, $85, $DC, $FB, $FF, $FF, $89, $04, $24, $E8, $69, $00, $00, $00, $8B, $45, $EC, $8B, $94, $85, $DC, $FB, $FF, $FF, $8B, $45, $E8, $8B, $84, $85, $DC, $FB, $FF, $FF, $01, $C2, $89, $D0, $C1, $F8, $1F, $C1, $E8, $18, $01, $C2, $0F, $B6, $D2, $29, $C2, $8D, $02, $8B, $84, $85, $DC, $FB, $FF, $FF, $89, $45, $DC, $8B, $55, $F4, $8B, $45, $E0, $01, $C2, $8B, $4D, $F4, $8B, $45, $08, $8D, $04, $01, $0F, $B6, $08, $8B, $45, $DC, $31, $C8, $88, $02, $83, $45, $F4, $01, $8B, $45, $F4, $3B, $45, $0C, $0F, $8C, $49, $FF, $FF, $FF, $8B, $45, $E0, $8D, $A4, $24, $34, $08, $00, $00, $5B, $5D, $C3, $55, $8D, $2C, $24, $8D, $64, $24, $F0, $8B, $45, $0C, $89, $C2, $C1, $E2, $02, $8B, $45, $08, $8D, $04, $02, $8B, $00, $89, $45, $FC, $8B, $45, $0C, $89, $C2, $C1, $E2, $02, $8B, $45, $08, $01, $C2, $8B, $45, $10, $89, $C1, $C1, $E1, $02, $8B, $45, $08, $8D, $04, $01, $8B, $00, $89, $02, $8B, $45, $10, $89, $C2, $C1, $E2, $02, $8B, $45, $08, $01, $C2, $8B, $45, $FC, $89, $02, $C9, $C3, $55, $89, $E5, $53, $8D, $64, $24, $DC, $E8, $5E, $36, $D9, $FF, $81, $C3, $4D, $7F, $10, $00, $8D, $83, $54, $F7, $F3, $FF, $89, $45, $F4, $8B, $45, $0C, $8B, $55, $F4, $89, $54, $24, $08, $89, $44, $24, $04, $8B, $45, $08, $89, $04, $24, $E8, $BD, $FD, $FF, $FF, $8D, $64, $24, $24, $5B, $5D, $C3 ); function myStrLen(szData: PChar): DWORD; cdecl; begin result := StrLen(szData); end; function myMalloc(dwSize: DWORD): DWORD; cdecl; begin result := DWORD(VirtualAlloc(nil, dwSize, MEM_COMMIT, PAGE_READWRITE)); end; var pData, decData : Pointer; fsStream : TFileStream; pCode : Pointer; dwLen : DWORD; decFunc : function(pData: Pointer; dwSize: DWORD; szKey: PChar): Pointer; cdecl; begin pCode := VirtualAlloc(nil, $1C0, MEM_COMMIT, PAGE_EXECUTE_READWRITE); FillChar((@DecCode[$B])^, 5, $90); DWORD((@DecCode[$2B])^) := DWORD(@myStrLen) - (DWORD(pCode) + $2F); DWORD((@DecCode[$E9])^) := DWORD(@myMalloc) - (DWORD(pCode) + $ED); CopyMemory(pCode, @DecCode[0], $24A); @decFunc := pCode; fsStream := TFileStream.Create('H:\Assembly-CSharp.dll', fmOpenRead); dwLen := fsStream.Size; GetMem(pData, dwLen); fsStream.ReadBuffer(pData^, dwLen); fsStream.Free; decData := decFunc(pData, dwLen, 'mrd2cyou147852369'); fsStream := TFileStream.Create('H:\Assembly-CSharp.decrypt.dll', fmCreate); fsStream.WriteBuffer(decData^, dwLen); fsStream.Free; end;