首先将仙剑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;