212017
 

忙了一年项目,好久没更新博客了

仙剑6的DLC幻字灰色补丁上次被误删了,趁这次机会把原理写下来

补丁原理就是librivet.dll里面有个导出函数Rivet_DlcVerification,DLC进不去、幻字灰色都是因为这个函数返回0导致的,解决办法很简单,把函数入口代码改成:
mov eax, 1
retn

点击下载成品

  发表在 下午 7:43
052016
 

首先将仙剑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函数
mono_dec_research_0

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;
  发表在 下午 2:02
182013
 

仙剑5前传发售已有一段时间了,该做的工具差不多都做了,因为这学期课程较松的缘故,平时我有较多的自由支配时间,除去考研复习,最近也研究了一下仙剑4音乐SMP的解密,贴上来就当是学习笔记给大家分享了。另外,SMP解密工具网上已经有很多了,所以只想要解密音乐的同学去其他网站上下载别人做的现呈工具吧,这里目的主要是分析一下SMP文件解密原理。

进入正题,用WinHEX查看smp文件,发现文件头标识RST与CPK文件的一致,往下看几乎没有明文,果断上OD。老规矩用CreateFileW断点,在游戏程序创建p01-1.smp文件句柄时断下

返回程序领空到790C8F处,F8单步一路跟下去,790CD8-790CEE这段读取了文件头,并验证文件标识是否为0x1A545352(即RST):

00790CD5  |> 8D5E 08       lea     ebx, dword ptr [esi+8]
00790CD8  |.  68 80000000   push    80
00790CDD  |.  53            push    ebx
00790CDE  |.  50            push    eax
00790CDF  |.  E8 FC090000   call    <ReadFile>
00790CE4  |.  8B03          mov     eax, dword ptr [ebx]
00790CE6  |.  83C4 0C       add     esp, 0C
00790CE9  |.  3D 5253541A   cmp     eax, 1A545352
00790CEE  |.  74 38         je      short 00790D28

继续阅读 »

  发表在 下午 6:38
172011
 

【简介】《三国战魂OL》是由北京软星(即《仙剑奇侠传五》开发商)制作的一款角色扮演的网游,其资源封包后缀为PKG,本工具提供了PKG封包格式的解包功能

【下载】点击下载

【截图】

【源代码】

本来想写一篇分析的,但由于我的硬盘空间不足,游戏已经删了,当时也没怎么记录,所以很抱歉无法提供一份具体分析
封包结构大致如下:封包内文件部分文件经过Zlib压缩(贴图之类的),部分未压缩(MP3音乐等),封包尾部为索引信息,一共两部分:索引MD5效验值和文件索引信息,两者均经过异或加密。

172011
 


这是我的第一篇关于逆向的文章,若有错误,欢迎指正!


在《云之遥》中,部分地图是无法存档的,本文便介绍如何用OD对云之遥程序进行调试并找到相应地方实现随时存档

首先,OD附加入云之遥主程序,F9让他先跑起来(若线程Suspend则Resume即可)

然后返回程序领空,右键查找参考字串,有如下这样一行:

0041B1DA    68 F8C27900     PUSH SwdCF.0079C2F8  ; ASCII “%s%cS%02d.sav”

这不就是存档路径吗?往上看,我们在0041B168下一个断点,单击“储存”,OD就会断下来,F8下去,会看到函数会返回到004FE286,继续在004FE1C0下一个断点,F9运行,然后再单击“储存”,断下来后,观察堆栈,有如下两条:

0012FBCC   004FC0DC  返回到 SwdCF.004FC0DC 来自 SwdCF.004FE1C0

0012FBD8   004FFDAD  返回到 SwdCF.004FFDAD 来自 SwdCF.004FC0A0

到004FFDAD去看下,往上在004FFB34下一个断点,再次单击“储存”可以被断下,根据刚才记录,可以看到存档CALL的地址在004FFDA8:

继续阅读 »