<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>MaYaFei&#039;s StuDio &#187; 资源研究</title>
	<atom:link href="https://www.mayafei.cn/archives/category/source_research/feed" rel="self" type="application/rss+xml" />
	<link>https://www.mayafei.cn</link>
	<description>Game ReSource Research &#124; Tools &#38; Mods</description>
	<lastBuildDate>Fri, 07 Feb 2025 02:44:07 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=4.2.2</generator>
	<item>
		<title>【旧物】仙剑6 DLC幻字灰色 补丁原理</title>
		<link>https://www.mayafei.cn/archives/704</link>
		<comments>https://www.mayafei.cn/archives/704#comments</comments>
		<pubDate>Tue, 21 Feb 2017 11:43:50 +0000</pubDate>
		<dc:creator><![CDATA[mayafei]]></dc:creator>
				<category><![CDATA[资源研究]]></category>

		<guid isPermaLink="false">http://www.mayafei.cn/?p=704</guid>
		<description><![CDATA[忙了一年项目，好久没更新博客了 仙剑6的DLC幻字灰色补丁上次被误删了，趁这次机会把原理写下来 补丁原理就是l <a href='https://www.mayafei.cn/archives/704' class='excerpt-more'>[...]</a>]]></description>
				<content:encoded><![CDATA[<p>忙了一年项目，好久没更新博客了</p>
<p>仙剑6的DLC幻字灰色补丁上次被误删了，趁这次机会把原理写下来</p>
<p>补丁原理就是librivet.dll里面有个导出函数Rivet_DlcVerification，DLC进不去、幻字灰色都是因为这个函数返回0导致的，解决办法很简单，把函数入口代码改成：<br />
mov eax, 1<br />
retn</p>
<p><a href="https://www.mayafei.cn/wp-content/uploads/2017/02/librivet.rar">点击下载成品</a></p>
]]></content:encoded>
			<wfw:commentRss>https://www.mayafei.cn/archives/704/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>【手游笔记】利用内联汇编进行Assembly-CSharp.dll解密（Pal5Q手游）</title>
		<link>https://www.mayafei.cn/archives/681</link>
		<comments>https://www.mayafei.cn/archives/681#comments</comments>
		<pubDate>Tue, 05 Jan 2016 06:02:31 +0000</pubDate>
		<dc:creator><![CDATA[mayafei]]></dc:creator>
				<category><![CDATA[资源研究]]></category>

		<guid isPermaLink="false">http://www.mayafei.cn/?p=681</guid>
		<description><![CDATA[首先将仙剑5前传手游APK解压，在assets\bin\Data\Managed中找到Assembly-CSh <a href='https://www.mayafei.cn/archives/681' class='excerpt-more'>[...]</a>]]></description>
				<content:encoded><![CDATA[<p>首先将仙剑5前传手游APK解压，在assets\bin\Data\Managed中找到Assembly-CSharp.dll，16进制查看发现其已经加密，其他文件（UnityEngine等）未加密，故考虑游戏应是通过libmono中mono_image_open_from_data_with_name的函数来解密Assembly-CSharp.dll</p>
<p>IDA打开libmono.so（位于lib\x86\libmono.so，x86较arm方便分析），找到mono_image_open_from_data_with_name函数，注意这里有一个GetKey函数<br />
<a href="http://www.mayafei.cn/wp-content/uploads/2016/01/mono_dec_research_0.jpg"><img class="alignnone size-full wp-image-682" src="http://www.mayafei.cn/wp-content/uploads/2016/01/mono_dec_research_0.jpg" alt="mono_dec_research_0" width="1231" height="385" /></a></p>
<p>F5分析代码，程序将GetKey函数返回的结果作为src然后memcpy，再新建一个image结构体并将dest赋值到image.raw_data，最后调用do_mono_image_load，根据这个逻辑GetKey返回的应是解密后的Assembly，跟进GetKey函数后发现该函数调用了一个名为EnDeCrypt的函数，从字面意思理解其应为解密函数，IDA已经给出了注释：</p>
<pre class="brush: c; gutter: true; first-line: 1">
.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</pre>
<p>其中s为字符串mrd2cyou147852369，由于GetKey返回是解密后的Assembly数据，故这里推测int应是加密原始数据的指针，size则是原始加密数据的大小</p>
<p>基于上面，下面开始编写解密程序，由于so用的是x86版本，因此可直接将代码拷贝出来并进行调用；EnDecrypt函数共有4个CALL调用：_x86_get_pc_thunk_bx、strlen、malloc、swapints，其中swapints代码位置刚紧跟EnDecrypt，因此无需处理（因为相对地址不变），_x86_get_pc_thunk_bx直接nop去掉，strlen、malloc用相应函数链接（必须为cdecl）即可</p>
<p>以下是Delphi核心解密代码：</p>
<pre class="brush: c; gutter: true; first-line: 1">
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;
</pre>
]]></content:encoded>
			<wfw:commentRss>https://www.mayafei.cn/archives/681/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>【备用】自制简易进程DLL注入（源码）</title>
		<link>https://www.mayafei.cn/archives/535</link>
		<comments>https://www.mayafei.cn/archives/535#comments</comments>
		<pubDate>Tue, 12 Aug 2014 15:06:43 +0000</pubDate>
		<dc:creator><![CDATA[mayafei]]></dc:creator>
				<category><![CDATA[资源研究]]></category>
		<category><![CDATA[备用工具]]></category>

		<guid isPermaLink="false">http://www.mayafei.cn/?p=535</guid>
		<description><![CDATA[博客上留个底备用~网上delphi的远程进程dll注入源码太繁琐，基本上都在模拟LoadLibrary过程，自 <a href='https://www.mayafei.cn/archives/535' class='excerpt-more'>[...]</a>]]></description>
				<content:encoded><![CDATA[<p>博客上留个底备用~网上delphi的远程进程dll注入源码太繁琐，基本上都在模拟LoadLibrary过程，自己写的这个则是直接利用汇编代码调用LoadLibrary然后注入进去，代码简洁了很多</p>
<p>【传送门】<a href="https://www.mayafei.cn/wp-content/uploads/2014/08/DLLInject.rar">DLLInject.rar</a></p>
]]></content:encoded>
			<wfw:commentRss>https://www.mayafei.cn/archives/535/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>【笔记】《仙剑奇侠传4》SMP音乐解密部分研究（附源代码）</title>
		<link>https://www.mayafei.cn/archives/440</link>
		<comments>https://www.mayafei.cn/archives/440#comments</comments>
		<pubDate>Mon, 18 Mar 2013 10:38:11 +0000</pubDate>
		<dc:creator><![CDATA[mayafei]]></dc:creator>
				<category><![CDATA[资源研究]]></category>

		<guid isPermaLink="false">http://www.mayafei.cn/?p=440</guid>
		<description><![CDATA[仙剑5前传发售已有一段时间了，该做的工具差不多都做了，因为这学期课程较松的缘故，平时我有较多的自由支配时间，除 <a href='https://www.mayafei.cn/archives/440' class='excerpt-more'>[...]</a>]]></description>
				<content:encoded><![CDATA[<p><span style="font-size: small; color: #808080;">仙剑5前传发售已有一段时间了，该做的工具差不多都做了，因为这学期课程较松的缘故，平时我有较多的自由支配时间，除去考研复习，最近也研究了一下仙剑4音乐SMP的解密，贴上来就当是学习笔记给大家分享了。另外，SMP解密工具网上已经有很多了，所以只想要解密音乐的同学去其他网站上下载别人做的现呈工具吧，这里目的主要是分析一下SMP文件解密原理。</span></p>
<p>进入正题，用WinHEX查看smp文件，发现文件头标识RST与CPK文件的一致，往下看几乎没有明文，果断上OD。老规矩用CreateFileW断点，在游戏程序创建p01-1.smp文件句柄时断下</p>
<p>返回程序领空到790C8F处，F8单步一路跟下去，790CD8-790CEE这段读取了文件头，并验证文件标识是否为0x1A545352（即RST）：</p>
<pre class="brush: c; gutter: true; first-line: 1">00790CD5  |&gt; 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    &lt;ReadFile&gt;
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</pre>
<p><span id="more-440"></span>再往下790D28处取了文件0x10偏移的一个DWORD值，并左移5位，算得一个值，并将这个值作为buffer大小继续读取文件</p>
<pre class="brush: cpp; gutter: true; first-line: 1">00790D28  |&gt; 8B56 18       mov     edx, dword ptr [esi+18]
00790D2B  |.  8B86 AC001000 mov     eax, dword ptr [esi+1000AC]
00790D31  |.  C1E2 05       shl     edx, 5
00790D34  |.  8D9E 88000000 lea     ebx, dword ptr [esi+88]
00790D3A  |.  52            push    edx                    ;  dwSize: [esi + 8] &lt;&lt; 5
00790D3B  |.  53            push    ebx                    ;  pBuffer
00790D3C  |.  50            push    eax                    ;  hFile
00790D3D  |.  E8 9E090000   call    &lt;ReadFile&gt;
00790D42  |.  83C4 0C       add     esp, 0C
00790D45  |.  84C0          test    al, al
00790D47  |.  75 38         jnz     short 00790D81</pre>
<p>接下来就是关键了，因为刚才读取的内容的指针被压栈调用CALL 0x7919D0。</p>
<pre class="brush: cpp; gutter: true; first-line: 1">00790D81  |&gt; 68 00001000   push    100000
00790D86  |.  53            push    ebx
00790D87  |.  E8 440C0000   call    007919D0</pre>
<p>F7跟进CALL，791B8F-791C5D这个循环实际作用是memcpy，拷贝了两组大小为0x1000的数据，之后又是两个CALL调用，数据窗口跟随并对比后发现这个CALL是解密数据的</p>
<pre class="brush: c; gutter: true; first-line: 1">00791C63  |.  8D8C24 800000&gt;lea     ecx, dword ptr [esp+80]
00791C6A  |.  8D9424 800100&gt;lea     edx, dword ptr [esp+180]
00791C71  |.  51            push    ecx                     ;  pPasswd
00791C72  |.  68 00040000   push    400                     ;  dwSize
00791C77  |.  52            push    edx                     ;  pData
00791C78  |.  E8 23FCFFFF   call    &lt;CPK_Decode&gt;
00791C7D  |.  8D8424 8C0000&gt;lea     eax, dword ptr [esp+8C]
00791C84  |.  8D8C24 901100&gt;lea     ecx, dword ptr [esp+1190]
00791C8B  |.  50            push    eax                     ;  pPasswd
00791C8C  |.  68 00040000   push    400                     ;  dwSize
00791C91  |.  51            push    ecx                     ;  pData
00791C92  |.  E8 09FCFFFF   call    &lt;CPK_Decode&gt;</pre>
<p>跟进这个解密CALL并进行代码分析，另外插一句，在开始之前我已经用IDA分析了脱壳后的pal4.exe，已经识别出了部分库函数。在开头：</p>
<pre class="brush: c; gutter: true; first-line: 1">007918C1  |.  DF6C24 04     fild    qword ptr [esp+4]                ;  dwSize
007918C5  |.  56            push    esi
007918C6  |.  57            push    edi
007918C7  |.  8B7C24 18     mov     edi, dword ptr [esp+18]
007918CB  |.  83EC 08       sub     esp, 8
007918CE  |.  D83D 6C298400 fdivr   dword ptr [84296C]
007918D4  |.  8B37          mov     esi, dword ptr [edi]
007918D6  |.  D805 68298400 fadd    dword ptr [842968]
007918DC  |.  DD1C24        fstp    qword ptr [esp]
007918DF  |.  E8 4930EEFF   call    &lt;floor&gt;
007918E4  |.  83C4 08       add     esp, 8
007918E7  |.  E8 302BEEFF   call    &lt;_ftol&gt;
007918EC  |.  69C0 B979379E imul    eax, eax, 9E3779B9
007918F2  |.  85C0          test    eax, eax
007918F4  |.  894424 0C     mov     dword ptr [esp+C], eax
007918F8  |.  0F84 BA000000 je      007919B8</pre>
<p>这个运算很简单，用52除以压栈的dwSize再加上6并取整，再乘以0x9E3779B9看是否等于0，如果等于0，则跳过下面那个循环解密过程。<br />
那个循环解密过程的算法就有一点复杂了，分析纯粹是体力活了，这里就不贴具体分析方法了，贴一下我已经根据这段代码写好的Pascal解密代码：<br />
（注：实际上就是XXTEA加密的，密钥见下面源码）</p>
<pre class="brush: pascal; gutter: true; first-line: 1">type
   arrIndex = array of DWORD;
   pIndex = ^arrIndex;

//-----------------------------------------------------------
// Original:
//     Pal4.exe_0x7918A0 | arg_1: pBuffer, arg_2: dwSize, arg_3: pPasswd
// New:
//     arrIndex : 待解密数据数组指针
//     dwSize : 解密数据数组长度
//     arrPwd : 解密密码（已定义为常量）
//-----------------------------------------------------------
procedure CPK_Decode(dwSize: DWORD; arrIndex: pIndex);
const
   arrPwd : array [0..13] of DWORD =
            ($706D6156, $2E657269, $204A2E43, $53207461,
             $7374666F, $20726174, $68636554, $6F6C6F6E,
             $28207967, $6E616853, $69614867, $6F432029,
             $4C202C2E, $00006474
            );//str: "Vampire.C.J at Softstar Technology (ShangHai) Co., Ltd"
var
   tmpNum_A, tmpNum_B, tmpNum_C, tmpNum_D             : DWORD;
   tmpNum_E                                           : DWORD;
   dwLoop                                             : DWORD;
begin
   tmpNum_A := Floor(52 / dwSize + 6) * $9E3779B9;
   tmpNum_D := arrIndex^[0];
   while tmpNum_A &lt;&gt; 0 do
   begin
      tmpNum_B := (tmpNum_A shr 2) and 3;
      dwLoop := dwSize - 1;
      while (dwLoop &gt; 0) do
      begin
         tmpNum_C := arrIndex^[dwLoop-1];
         tmpNum_E := (tmpNum_D * 4) xor (tmpNum_C shr 5);
         tmpNum_E := tmpNum_E + ((tmpNum_D shr $3) xor (tmpNum_C shl $4));
         tmpNum_D := arrIndex^[dwLoop] - (tmpNum_E xor ((tmpNum_A xor tmpNum_D) + (arrPwd[(dwLoop and $3) xor tmpNum_B] xor tmpNum_C)));
         arrIndex^[dwLoop] := tmpNum_D;
         dwLoop := dwLoop - 1;
      end;
      tmpNum_D := arrIndex^[0] - ((((tmpNum_D * 4) xor (arrIndex^[dwSize-1] shr $5)) + ((tmpNum_D shr $3) xor (arrIndex^[dwSize-1] shl $4))) xor (((arrPwd[(dwLoop and $3) xor tmpNum_B]) xor arrIndex^[dwSize - 1]) + (tmpNum_A xor tmpNum_D)));
      arrIndex^[0] := tmpNum_D;
      tmpNum_A := tmpNum_A + $61C88647;
   end;
end;</pre>
<p>解密完成后观察数据，发现这一段解密出来的是索引信息，并非是音乐数据，继续往下分析。791DAA-791EA0这一段代码的作用是将解密后的索引信息拷回去。对拷回去的索引信息下内存访问断点，F9运行，断在了7915A3，很容易看出来这里是拿第一个DWORD作循环比较。<br />
从子程序返回后进入上面那个790A80的call进行分析，发现这里是CRC32效验，效验结果被作为参数压栈调用下一个也就是刚才断下来的那个CALL，因此可以看出仙剑4的CPK索引信息查找是通过查找crc32效验值进行的，当然，索引信息中第一DWORD就是CRC32效验值无误了。进一步分析可以知道这个CRC32效验值是文件名的效验值。</p>
<p>从子程序返回，继续往下分析，在791217处调用了SetFilePointer，Offset是索引信息第四个DWORD值。<br />
既然已经调用了SetFilePointer，接下来很快应该就会ReadFile，直接bp ReadFile，F9运行，断下后观察堆栈，BytesToRead一项刚好是索引信息第五个和第六个DWORD值。<br />
这里有两个一样的值？后续分析可以得知实际上CPK里面部分文件用LZO压缩过了，第五个DWORD实际上是压缩后的大小，第六个是压缩前的大小。</p>
<p>返回程序领空，往下看，发现7909EE处再次调用了上面所提到的解密CALL。F4过去看一下参数，发现dwSize是BytesToRead4。数据窗口跟随，F8直接步过CALL观察，可以发现解密后的数据就是我们想要的音乐数据了。</p>
<p>由于本文主要是探讨SMP音乐文件解密的，因此索引信息方面不再详细描述。根据刚才的分析，现在解密程序的编写思路已经很明了了，因为smp文件中只有一个文件，因此只需解密索引信息后读取第一组索引信息第四个到第六个DWORD，再据此读入数据并解密即可。</p>
<p>附SMP文件解密Delphi7工程源代码：<a href="https://www.mayafei.cn/wp-content/uploads/2013/03/Pal4_SMP.rar">Pal4_SMP.rar</a></p>
]]></content:encoded>
			<wfw:commentRss>https://www.mayafei.cn/archives/440/feed</wfw:commentRss>
		<slash:comments>36</slash:comments>
		</item>
		<item>
		<title>【资源解包】《三国战魂OL》PKG资源封包解包器</title>
		<link>https://www.mayafei.cn/archives/88</link>
		<comments>https://www.mayafei.cn/archives/88#comments</comments>
		<pubDate>Fri, 17 Jun 2011 13:34:14 +0000</pubDate>
		<dc:creator><![CDATA[mayafei]]></dc:creator>
				<category><![CDATA[资源研究]]></category>
		<category><![CDATA[PKG]]></category>
		<category><![CDATA[三国战魂]]></category>
		<category><![CDATA[北京软星]]></category>
		<category><![CDATA[资源封包]]></category>

		<guid isPermaLink="false">http://www.mayafei.cn/?p=88</guid>
		<description><![CDATA[【简介】《三国战魂OL》是由北京软星（即《仙剑奇侠传五》开发商）制作的一款角色扮演的网游，其资源封包后缀为PK <a href='https://www.mayafei.cn/archives/88' class='excerpt-more'>[...]</a>]]></description>
				<content:encoded><![CDATA[<p><span style="font-size: small;">【简介】《三国战魂OL》是由北京软星（即《仙剑奇侠传五》开发商）制作的一款角色扮演的网游，其资源封包后缀为PKG，本工具提供了PKG封包格式的解包功能</span></p>
<p><span style="font-size: small;">【下载】<a href="http://www.mayafei.cn/wp-content/uploads/2011/06/PKG_Extractor.zip">点击下载</a></span></p>
<p><span style="font-size: small;">【截图】</span><br />
<span style="font-size: x-small;"><a rel="attachment wp-att-90" href="http://www.mayafei.cn/archives/88/pkg_extractor-2"><img class="alignnone size-medium wp-image-90" title="PKG_Extractor" src="http://www.mayafei.cn/wp-content/uploads/2011/06/PKG_Extractor-300x145.jpg" alt="" width="300" height="145" /></a></span></p>
<p><span style="font-size: small;">【源代码】</span></p>
<p><span style="color: #ffffff; font-size: small;"><strong>本来想写一篇分析的，但由于我的硬盘空间不足，游戏已经删了，当时也没怎么记录，所以很抱歉无法提供一份具体分析</strong></span><br />
<span style="font-size: small;">封包结构大致如下：封包内文件部分文件经过Zlib压缩（贴图之类的），部分未压缩（MP3音乐等），封包尾部为索引信息，一共两部分：索引MD5效验值和文件索引信息，两者均经过异或加密。</span></p>
]]></content:encoded>
			<wfw:commentRss>https://www.mayafei.cn/archives/88/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>【轩辕剑MOD】《云之遥》随时存档研究及实现（修改）</title>
		<link>https://www.mayafei.cn/archives/38</link>
		<comments>https://www.mayafei.cn/archives/38#comments</comments>
		<pubDate>Fri, 17 Jun 2011 10:38:49 +0000</pubDate>
		<dc:creator><![CDATA[mayafei]]></dc:creator>
				<category><![CDATA[资源研究]]></category>
		<category><![CDATA[调试逆向]]></category>
		<category><![CDATA[轩辕剑]]></category>

		<guid isPermaLink="false">http://www.mayafei.cn/?p=38</guid>
		<description><![CDATA[这是我的第一篇关于逆向的文章，若有错误，欢迎指正！ 在《云之遥》中，部分地图是无法存档的，本文便介绍如何用OD <a href='https://www.mayafei.cn/archives/38' class='excerpt-more'>[...]</a>]]></description>
				<content:encoded><![CDATA[<p><span style="font-family: 宋体;font-size: 9pt;color: white"><br />
<strong>这是我的第一篇关于逆向的文章，若有错误，欢迎指正！</strong></span></p>
<p><span style="font-family: 宋体;font-size: 9pt;color: #C0C0C0"><br />
在《云之遥》中，部分地图是无法存档的，本文便介绍如何用OD对云之遥程序进行调试并找到相应地方实现随时存档</p>
<p>首先，OD附加入云之遥主程序，F9让他先跑起来（若线程Suspend则Resume即可）</p>
<p>然后返回程序领空，右键查找参考字串，有如下这样一行：</p>
<p>0041B1DA    68 F8C27900     PUSH SwdCF.0079C2F8  ; ASCII &#8220;%s%cS%02d.sav&#8221;</p>
<p>这不就是存档路径吗？往上看，我们在0041B168下一个断点，单击“储存”，OD就会断下来，F8下去，会看到函数会返回到004FE286，继续在004FE1C0下一个断点，F9运行，然后再单击“储存”，断下来后，观察堆栈，有如下两条：</p>
<p>0012FBCC   004FC0DC  返回到 SwdCF.004FC0DC 来自 SwdCF.004FE1C0</p>
<p>0012FBD8   004FFDAD  返回到 SwdCF.004FFDAD 来自 SwdCF.004FC0A0</p>
<p>到004FFDAD去看下，往上在004FFB34下一个断点，再次单击“储存”可以被断下，根据刚才记录，可以看到存档CALL的地址在004FFDA8：</p>
<p><span id="more-38"></span></p>
<p>004FFDA8    E8 F3C2FFFF     CALL SwdCF.004FC0A0                      ; 存档 CALL</p>
<p>只要走到这里便能存档，此时我们F8一路单步走，在004FFC18处有一个JMP到存档CALL的上面，先标记一下</p>
<p>此时读取一个无法存档的地图的存档，直接来到储存界面单击“储存”，程序被断下来了，可以看到在这里会跳走从而无法存档</p>
<p>004FFBCC   74 4F           JE SHORT SwdCF.004FFC1D</p>
<p>于是我们把这段代码整理一下：</p>
<p>004FFB34    57              PUSH EDI<br />
004FFB35    85C0            TEST EAX,EAX<br />
004FFB37    0F85 00030000   JNZ SwdCF.004FFE3D                       ; EAX 不为 0 跳走<br />
004FFB3D    A1 283F7E00     MOV EAX,DWORD PTR DS:[7E3F28]<br />
004FFB42    BF 01000000     MOV EDI,1<br />
004FFB47    C680 CC000000 0&gt;MOV BYTE PTR DS:[EAX+CC],0               ; [7E3F28+CC] 置0<br />
004FFB4E    8B4424 08       MOV EAX,DWORD PTR SS:[ESP+8]             ; [ESP+8] -&gt; EAX<br />
004FFB52    3B05 243E8300   CMP EAX,DWORD PTR DS:[833E24]<br />
004FFB58    75 09           JNZ SHORT SwdCF.004FFB63                 ; EAX 不等于 [833E24] 跳走<br />
004FFB5A    33C0            XOR EAX,EAX                              ; EAX 清零<br />
004FFB5C    A3 A03C8300     MOV DWORD PTR DS:[833CA0],EAX            ; [833CA0] = EAX<br />
004FFB61    EB 52           JMP SHORT SwdCF.004FFBB5<br />
004FFB63    3B05 283E8300   CMP EAX,DWORD PTR DS:[833E28]<br />
004FFB69    75 09           JNZ SHORT SwdCF.004FFB74                 ; EAX 不等于 [833E28] 跳走<br />
004FFB6B    8BC7            MOV EAX,EDI<br />
004FFB6D    A3 A03C8300     MOV DWORD PTR DS:[833CA0],EAX<br />
004FFB72    EB 41           JMP SHORT SwdCF.004FFBB5<br />
004FFB74    3B05 2C3E8300   CMP EAX,DWORD PTR DS:[833E2C]<br />
004FFB7A    75 0C           JNZ SHORT SwdCF.004FFB88                 ; EAX 不等于 [833E2C] 跳走<br />
004FFB7C    B8 02000000     MOV EAX,2<br />
004FFB81    A3 A03C8300     MOV DWORD PTR DS:[833CA0],EAX<br />
004FFB86    EB 2D           JMP SHORT SwdCF.004FFBB5<br />
004FFB88    3B05 303E8300   CMP EAX,DWORD PTR DS:[833E30]<br />
004FFB8E    75 0C           JNZ SHORT SwdCF.004FFB9C                 ; EAX 不等于 [833E30] 跳走<br />
004FFB90    B8 03000000     MOV EAX,3<br />
004FFB95    A3 A03C8300     MOV DWORD PTR DS:[833CA0],EAX<br />
004FFB9A    EB 19           JMP SHORT SwdCF.004FFBB5<br />
004FFB9C    3B05 343E8300   CMP EAX,DWORD PTR DS:[833E34]<br />
004FFBA2    75 0C           JNZ SHORT SwdCF.004FFBB0                 ; EAX 不等于 [833E34] 跳走<br />
004FFBA4    B8 04000000     MOV EAX,4<br />
004FFBA9    A3 A03C8300     MOV DWORD PTR DS:[833CA0],EAX<br />
004FFBAE    EB 05           JMP SHORT SwdCF.004FFBB5<br />
004FFBB0    A1 A03C8300     MOV EAX,DWORD PTR DS:[833CA0]            ; EAX = [833CA0]<br />
004FFBB5    83F8 04         CMP EAX,4<br />
004FFBB8    0F87 75020000   JA SwdCF.004FFE33                        ; EAX 大于 4 跳走<br />
004FFBBE    FF2485 44FE4F00 JMP DWORD PTR DS:[EAX*4+4FFE44]<br />
004FFBC5    A0 C13D8300     MOV AL,BYTE PTR DS:[833DC1]              ; AL = [833DC1]<br />
004FFBCA    84C0            TEST AL,AL<br />
004FFBCC    74 4F           JE SHORT SwdCF.004FFC1D                  ; AL 为 0 跳走<br />
004FFBCE    A0 F9FA8300     MOV AL,BYTE PTR DS:[83FAF9]              ; AL = [83FAF9]<br />
004FFBD3    84C0            TEST AL,AL<br />
004FFBD5    75 46           JNZ SHORT SwdCF.004FFC1D                 ; AL 不为 0 跳走<br />
004FFBD7    B9 983C8300     MOV ECX,SwdCF.00833C98<br />
004FFBDC    E8 5FE7FFFF     CALL SwdCF.004FE340<br />
004FFBE1    B9 983C8300     MOV ECX,SwdCF.00833C98<br />
004FFBE6    E8 65EEFFFF     CALL SwdCF.004FEA50<br />
004FFBEB    B9 983C8300     MOV ECX,SwdCF.00833C98<br />
004FFBF0    E8 7BEEFFFF     CALL SwdCF.004FEA70<br />
004FFBF5    A1 5C257E00     MOV EAX,DWORD PTR DS:[7E255C]<br />
004FFBFA    893D A43C8300   MOV DWORD PTR DS:[833CA4],EDI<br />
004FFC00    3BC7            CMP EAX,EDI<br />
004FFC02    75 0D           JNZ SHORT SwdCF.004FFC11<br />
004FFC04    6A 0F           PUSH 0F<br />
004FFC06    57              PUSH EDI<br />
004FFC07    B9 A8088300     MOV ECX,SwdCF.008308A8<br />
004FFC0C    E8 3F2BFDFF     CALL SwdCF.004D2750<br />
004FFC11    8B0D 3C257E00   MOV ECX,DWORD PTR DS:[7E253C]<br />
004FFC17    51              PUSH ECX<br />
004FFC18    E9 75010000     JMP SwdCF.004FFD92                       ; 这里去存档<br />
004FFC1D    6A FF           PUSH -1</p>
<p>很明显，如果条件跳转到4FFC1D这个地址，便无法进行存档，因此必须让以下两个条件为假：</p>
<p>004FFBCC    74 4F           JE SHORT SwdCF.004FFC1D                  ; AL 为 0 跳走<br />
004FFBD5    75 46           JNZ SHORT SwdCF.004FFC1D                 ; AL 不为 0 跳走</p>
<p>因此，若要实现随时存档，写一个内存修改程序，将<span style="text-decoration: underline;">0x833CA0</span>地址上的这个byte值保持为1，<span style="text-decoration: underline;">0x83FAF9</span>地址上的这个byte值保持为0即可</p>
<p>以上研究是在繁体版云之遥上进行的，用同样的方法可以得到简体版的这两个地址，分别为<span style="text-decoration: underline;">0x82B779</span>和<span style="text-decoration: underline;">0x8374B1</span></p>
<p><strong>附ASM实现代码（对应简体版，建议以Timer方式启动）：</strong></p>
<p>.const</p>
<p>ProcessName            db &#8220;SwdCF&#8221;,0<br />
ErrMsg                 db &#8220;Error Open Process!&#8221;,0<br />
ErrMsgT                db &#8220;Error&#8221;,0</p>
<p>.data?</p>
<p>ProcessPID             dd ?<br />
ProcessHWND            dd ?</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;分割线&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>MemoryModifyProc proc;</p>
<p>     LOCAL Found        : byte<br />
     LOCAL hWnd1        : DWORD<br />
     LOCAL bWrite       : byte</p>
<p>         mov Found, 0<br />
         .While !Found<br />
              invoke FindWindow, offset ProcessName, offset ProcessName<br />
              .if eax != 0<br />
                   mov Found, 1<br />
                   invoke GetWindowThreadProcessId, eax, offset ProcessPID<br />
                   invoke OpenProcess, PROCESS_ALL_ACCESS, FALSE, ProcessPID<br />
                   mov ProcessHWND, eax<br />
                   .if eax != 0<br />
                       mov bWrite, 1<br />
                       invoke WriteProcessMemory,ProcessHWND,0082B779h,addr bWrite,1,NULL<br />
                       mov bWrite, 0<br />
                       invoke WriteProcessMemory,ProcessHWND,008374B1h,addr bWrite,1,NULL<br />
                   .else<br />
                       invoke MessageBox, ThisProWND, offset sErrMsg, offset sErrMsgT, MB_ICONERROR<br />
                   .endif<br />
                   invoke  CloseHandle, ProcessHWND<br />
              .else<br />
                   ret<br />
              .endif<br />
         .endW<br />
     ret</p>
<p>MemoryModifyProc endp<br />
</span></p>
]]></content:encoded>
			<wfw:commentRss>https://www.mayafei.cn/archives/38/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
