【收山之作】用yourdiary为例 学习KRKR2 XP3加密静态分析
以前我谈过, 第1章 KRKR游戏系统汉化说明
我觉得问题在于很多商业KRKR系统都是加密过的数据,今天作为收山之文,谈下XP3加密分析和封包算法
今天我们分析的游戏是《yourdiary》,也是前久汉化组来求破解的游戏。
加密VS解密
不管KRKR还是.net 还是JAVA,以及android,其加密解密方法基本差不多,就是HOOK相应函数或者提供相应接口就OK。
无外乎解密存在两种方法
1、动态HOOK 然后内存DUMP
2、静态分析
举例:
1、动态HOOK xp3dumer
2、静态分析 Crass
JAVA 静态解密可以参考鄙人做的java Class文件解密大师发布和说明
对于大家来说动态HOOK简单实用,因为不需要考虑什么加密算法,直接内存DUMP
对于程序猿来说,显然静态分析,逆向其算法更能提高水平。
所以说本文需要程序设计基础非大众读物。
我们用Crass解包直接解包,提示CRC错误。
青子私服c06a(大)-mono.tlg: crc校验失败(0x90d63725), 应该是0xe1eb5d2b.
青子私服c06a(大).tlg: crc校验失败(0x2a395798), 应该是0x9ff1bbbe.
青子私服c06a(近)-mono.tlg: crc校验失败(0xab6e8767), 应该是0x70439e0e.
青子私服c06a(近).tlg: crc校验失败(0xac8ab284), 应该是0x346e11f5.
黒幕.tlg: crc校验失败(0xc41caf48), 应该是0x9014486f.
data.xp3: 成功提取1286 / 1286 个资源文件
怎么回事,CRC错误可能就是1、文件损坏了 2、加密了
显然游戏可以正常运行,否定1。
用UE打开XP3,分析下看不出规律,现在只能从代码入手。
打开plugin目录有个yourdiary.tpm
这个就是加密DLL
用反汇编器打开看看
发现有两个导出函数
V2Link
V2Unlink
注意:KRKR分早期版本和2.22以后版本,其导出函数不一样,大家要注意.不过大家遇到KRKR都不很老,所以本文基本可以通吃。
这里需要大家有KRKR封包加密插件编写的经验才能跟到关键加密代码段
至于加密插件的使用方法请参照水螅大大的教程,这里不再赘述。
我直接跟进关键加密代码段:
.text:10001000
.text:10001000 ; Segment type: Pure code
.text:10001000 ; Segment permissions: Read/Execute
.text:10001000 _text segment para public 'CODE' use32
.text:10001000 assume cs:_text
.text:10001000 ;org 10001000h
.text:10001000 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
.text:10001000
.text:10001000 loc_10001000: ; DATA XREF: V2Link:loc_10001078o
.text:10001000 push esi
.text:10001001 mov esi, [esp+8]
.text:10001005 cmp dword ptr [esi], 18h
.text:10001008 jz short loc_1000102C
.text:1000100A mov eax, dword_1000C2CC
.text:1000100F test eax, eax
.text:10001011 jnz short loc_10001025
.text:10001013 push offset aVoidTvpthrowex ; "void ::TVPThrowExceptionMessage(const t"...
.text:10001018 call sub_100010C0
.text:1000101D add esp, 4
.text:10001020 mov dword_1000C2CC, eax
.text:10001025
.text:10001025 loc_10001025: ; CODE XREF: .text:10001011j
.text:10001025 push offset aIncompatibleTt ; "Incompatible tTVPXP3ExtractionFilterInf"...
.text:1000102A call eax ; dword_1000C2CC
.text:1000102C
.text:1000102C loc_1000102C: ; CODE XREF: .text:10001008j
.text:1000102C xor eax, eax
.text:1000102E cmp [esi+10h], eax
.text:10001031 jbe short loc_1000104B
.text:10001033
.text:10001033 loc_10001033: ; CODE XREF: .text:10001049j
.text:10001033 mov ecx, [esi+0Ch]
.text:10001036 mov dl, [ecx+eax]
.text:10001039 xor dl, [esi+14h]
.text:1000103C add ecx, eax
.text:1000103E xor dl, 0CDh
.text:10001041 add eax, 1
.text:10001044 mov [ecx], dl
.text:10001046 cmp eax, [esi+10h]
.text:10001049 jb short loc_10001033
.text:1000104B
.text:1000104B loc_1000104B: ; CODE XREF: .text:10001031j
.text:1000104B pop esi
.text:1000104C retn 4
.text:1000104C ; ---------------------------------------------------------------------------
.text:1000104F align 10h
.text:10001050 ; Exported entry 1. V2Link
要看懂上面汇编对于新手是很难的,因为计算机不懂什么叫类,什么叫对象,什么叫结构体。
所有的这些概念在编译之后都不复存在。
数据解密最难也在于此,比如[esi+0Ch]那是啥玩意,是数组?还是结构体?还是对象?
就像在漆黑的屋子里面抓到一大团毛绒绒的东西,是啥,是兔子还是猫?
好在解密插件是开源的
大家可以打开KRKR源代码\2.26\kirikiri2\src\plugins\win32\xp3filter\xp3dec 目录下的 main.cpp
可以看到
//---------------------------------------------------------------------------
void TVP_tTVPXP3ArchiveExtractionFilter_CONVENTIONTVPXP3ArchiveExtractionFilter(tTVPXP3ExtractionFilterInfo *info)
{// TVPXP3ArchiveExtractionFilter 関数は本体側から呼び出される// コールバック関数です。// 引数を一つ取り、それは tTVPXP3ExtractionFilterInfo 構造体へのポインタ// です。// TVPXP3ArchiveExtractionFilter は、後述の V2Link 関数内で// TVPSetXP3ArchiveExtractionFilter により設定されます。// ここでは単純に、xp3enc.dll のサンプルで作成された XP3 アーカイブを// 復号すべく、データをすべて FileHash の最下位バイトで XOR// することにします。// この関数は複数のスレッドから同時に呼び出される可能性があるので// 注意してください。/*tTVPXP3ExtractionFilterInfo のメンバは以下の通り* SizeOfSelf : 自分自身の構造体のサイズ* Offset : "Buffer" メンバが指し示すデータが、* : アーカイブに格納されているそのファイルの先頭からの* : どのオフセット位置からのものか、を表す* Buffer : データ本体* BufferSize : "Buffer" メンバの指し示すデータのサイズ(バイト単位)* FileHash : ファイルの暗号化解除状態でのファイル内容の32bitハッシュ*/// 一応構造体のサイズをチェックするif(info->SizeOfSelf != sizeof(tTVPXP3ExtractionFilterInfo)){// 構造体のサイズが違う場合はここでエラーにした方がよいTVPThrowExceptionMessage(TJS_W("Incompatible tTVPXP3ExtractionFilterInfo size"));// TVPThrowExceptionMessage は例外メッセージを投げる関数// この関数は戻らない ( もっと呼び出し元をさかのぼった位置で// 例外が補足されるため )}// 復号tjs_uint i;for(i = 0; i < info->BufferSize; i++)((unsigned char *)info->Buffer)[i] ^= info->FileHash;
}
//------------
我们可以看到上面的汇编就是上面这个官方实例的修改版,
但要知道[esi+0Ch]是啥,还得研究官方源代码,所有KRKR解密依赖于tTVPXP3ExtractionFilterInfo这个结构,显然解密代码就是结构体操作。
下面请大家复习下C语言知识:
类型标识符 | 类型说明 |
长度 (字节) |
范围 | 备注 |
char | 字符型 | 1 | -128 ~ 127 | -27 ~ (27 -1) |
unsigned char | 无符字符型 | 1 | 0 ~ 255 | 0 ~ (28 -1) |
short int | 短整型 | 2 | -32768 ~ 32767 | 2-15 ~ (215 - 1) |
unsigned short int | 无符短整型 | 2 | 0 ~ 65535 | 0 ~ (216 - 1) |
int | 整型 | 4 | -2147483648 ~ 2147483647 | -231 ~ (231 - 1) |
unsigned int | 无符整型 | 4 | 0 ~ 4294967295 | 0 ~ (232-1) |
float | 实型(单精度) | 4 | 1.18*10-38 ~ 3.40*1038 | 7位有效位 |
double | 实型(双精度) | 8 | 2.23*10-308 ~ 1.79*10308 | 15位有效位 |
long double | 实型(长双精度) | 10 | 3.37*10-4932 ~ 1.18*104932 | 19位有效位 |
上面这张表是一般VC 32位程序类型的资料。大家特别注意下长度,你看int 占用4字节,不过不同编译器和平台不一样,数据类型的字节数应该是由CPU决定的,但是实际上主要由编译器决定。
显然一个类似下面这样结构体:
struct Student
{int name;int sex;int age;int addr;
};
/*然后定义一个Student 类型的 student变量*/
struct Student student;
student这样的结构体占用4×4=16字节
内存分布一般为:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
name sex age addr
下面我们看看tTVPXP3ExtractionFilterInfo结构如下:大小和内存位置我已经标注给大家(KRKR源代码\2.26\kirikiri2\src\plugins\win32\tp_stub.cpp文件中)
struct tTVPXP3ExtractionFilterInfo
{const tjs_uint SizeOfSelf; // structure size of tTVPXP3ExtractionFilterInfo itself 4个字节 0-3const tjs_uint64 Offset; // offset of the buffer data in uncompressed stream position 8个字节 4-11void * Buffer; // target data buffer 4个字节 12-15const tjs_uint BufferSize; // buffer size in bytes pointed by "Buffer" 16-19const tjs_uint32 FileHash; // hash value of the file (since inteface v2)tTVPXP3ExtractionFilterInfo(tjs_uint64 offset, void *buffer,tjs_uint buffersize, tjs_uint32 filehash) :Offset(offset), Buffer(buffer), BufferSize(buffersize),FileHash(filehash),SizeOfSelf(sizeof(tTVPXP3ExtractionFilterInfo)) {;}
};
有了这些东西,我们去搞汇编就易如反掌了
上面加密算法伪代码应该这样:
取出[esi+0Ch]里的数据,即Buffer[i]
异或xor [esi+14h] 即 FileHash
异或xor 0CDh
好了解密算法这样写就OK了:
//---------------------------------------------------------------------------
void TVP_tTVPXP3ArchiveExtractionFilter_CONVENTIONTVPXP3ArchiveExtractionFilter(tTVPXP3ExtractionFilterInfo *info)
{// TVPXP3ArchiveExtractionFilter 函数是会从吉里吉里本体调用的回调函数。// 能得到的参数只有一个,就是对 tTVPXP3ExtractionFilterInfo 结构体的指针。// TVPXP3ArchiveExtractionFilter 需要在后述的 V2Link 函数内使用// 吉里吉里本体的 TVPSetXP3ArchiveExtractionFilter 函数设定。// 这个样例只是纯粹的对 xp3enc.dll 样例代码加密的 XP3 文件包解密// 将所有的数据以 FileHash 的最后一个字节作异或(XOR)运算// 请注意,这个函数有可能在多个线程中被调用。/*tTVPXP3ExtractionFilterInfo 的成员如下* SizeOfSelf : 结构体自身的大小* Offset : "Buffer" 成员所指向的数据偏离这个文件头部的偏移值* Buffer : 数据本体* BufferSize : "Buffer" 成员所指向的数据的大小(字节单位)* FileHash : 解密后文件内容的32位 Hash (散列)值*/// 检查结构体的大小if(info->SizeOfSelf != sizeof(tTVPXP3ExtractionFilterInfo)){// 当结构体的大小错误,则最好投出异常TVPThrowExceptionMessage(TJS_W("Incompatible tTVPXP3ExtractionFilterInfo size"));// TVPThrowExceptionMessage 是用于投出异常的函数// 本函数不会返回。}// yourdiary 解密代码 by大师♂罗庄
// ext:10001033 loc_10001033: ; CODE XREF: .text:10001049j
//.text:10001033 mov ecx, [esi+0Ch] //Buffer[i]
//.text:10001036 mov dl, [ecx+eax]//Buffer[i]
//.text:10001039 xor dl, [esi+14h] //FileHash
//.text:1000103C add ecx, eax
//.text:1000103E xor dl, 0CDh
//.text:10001041 add eax, 1
//.text:10001044 mov [ecx], dl
//.text:10001046 cmp eax, [esi+10h]//循环比较BufferSize
//.text:10001049 jb short loc_10001033
//.text:1000104B
//.text:1000104B loc_1000104B: ; CODE XREF: .text:10001031j
//.text:1000104B pop esi
//.text:1000104C retn 4
//.text:1000104C ; ---------------------------------------------------------------------------
//.text:1000104F align 10htjs_uint i;for(i = 0; i < info->BufferSize; i++){unsigned char v4 =((unsigned char *)info->Buffer)[i] ;unsigned char v5 =info->FileHash^ (v4);unsigned char result ;result = v5 ^ 0xCD;((unsigned char *)info->Buffer)[i] =result;}
}
//---------------------------------------------------------------------------
编译生成*.dll 然后改成*.tmp
可以覆盖游戏*.tmp试试,但最好别这样,最好用Crass 带tmp参数进行测试,发现没有错误能解包。
好了课程就这样,下课。
【收山之作】用yourdiary为例 学习KRKR2 XP3加密静态分析相关推荐
- 模式识别 - 处理多演示样例学习(MIL)特征(matlab)
处理多演示样例学习(MIL)特征(matlab) 本文地址: http://blog.csdn.net/caroline_wendy/article/details/27206325 多演示样例学习( ...
- 【OpenFOAM】——OpenFOAM入门算例学习
1 明确目标--为啥费老大劲儿学习OpenFOAM 学习OpenFOAM主要出于课题需要,希望实现以下几个目标: l [ ]学会用SnappyHexMesh生成高质量网格: l [ ]学习使用O ...
- 5_竞赛无人机搭积木式编程——以2021年电赛G题植保无人机国奖标准完整复现为例学习
竞赛无人机搭积木式编程 --以2021年电赛G题植保无人机国奖标准完整复现为例学习 首先我们需要了解下自动飞行任务执行过程几组关键变量的用法与实际作用效果: flight_subtask_cnt用于控 ...
- 非静压模型NHWAVE学习(7)——波浪变形模拟算例学习(Wave transformation over an elliptical shoal)
波浪变形模拟算例学习(Wave transformation over an elliptical shoal) 算例简介 地形与网格 运行参数配置 模型的编译运行 模拟结果处理 本blog介绍了NH ...
- openfoam v8 波浪算例学习日记: 3.手动运行算例
openfoam8 wave算例学习记录 手动运行算例 网格处理 Allrun里第一步为blockMesh划分网格. 此命令读取system/blockMeshDict字典文件.以下为该字典文件内容( ...
- [白话解析] 用水浒传为例学习条件随机场
[白话解析] 用水浒传为例学习条件随机场 0x00 摘要 本文将尽量使用易懂的方式,尽可能不涉及数学公式,而是从整体的思路上来看,运用感性直觉的思考来解释条件随机场.并且用水浒传为例学习.并且从名著中 ...
- python 彩票排列组合_对福彩3D号码进行排列组合为例学习Python的itertools模块的用法...
这里我们以对福彩3D号码进行排列组合为例学习Python的itertools模块的用法.首先我们选择心仪的号码.比如我们选择4,5,7,8 第一种我们只要组六的组合.代码如下 import itert ...
- 非静压模型NHWAVE学习(9)——波浪破碎RANS紊流模型算例学习(Wave Breaking on a Planar Beach)
NHWAVE波浪破碎RANS紊流模型算例学习(Wave Breaking on a Planar Beach) 算例简介 地形与网格 运行参数配置 模型的编译运行 模拟结果 本blog介绍了NHWAV ...
- 非静压模型NHWAVE学习(6)——波浪模拟算例学习(Periodic wave over a submerged bar)
波浪模拟算例学习(Periodic wave over a submerged bar) 算例简介 网格与地形 运行参数配置 模型的编译运行及模拟结果 本blog介绍了NHWAVE模型自带算例Peri ...
- 以ArrayList为例学习序列化和反序列化
一.序列化和反序列化: 在进行对象操作类的实现时,涉及到好几个关键词Serializable和transient 什么是序列化和反序列化? 序列化:将对象转化为字节的过程称为序列化过程 (存储到本地磁 ...
最新文章
- Linux下使用perf进行性能分析,并导出火焰图
- typescript的命名空间
- wxpython多线程消息传递_wxpython多线程间通信
- 使用Remoting技术
- springMVC rest风格
- 美团(Leaf)分布式ID算法
- 诺基亚手机的安全保护。--如何使你的手机丢失,被盗后依然安全。
- ASP.NET基础培训 - Cookie的正确利用
- 瀑布、V、W、快速原型模型、增量、螺旋模型
- (一)流体力学基本概念
- 无法在驱动器1分区上安装W ndOwS,安装win10提示无法在驱动器0分区上安装windows
- 高通APQ8074(骁龙800)处理器
- 苏州大学 数据库题库
- RocketMQ产线Broker Busy问题排查经过
- 服务器1U,2U的含义
- 干货 | 京东云部署Wordpress最佳实践
- mysql在windows安装和卸载步骤,以及相关问题的解决记录
- 个人简历网页搭建(快速搭建GitHub Pages和Apache)
- bzoj2245 [SDOI2011]工作安排
- 新学期|除了认真学习的“flag”,你还立了啥?