by devseed , 本贴论坛和我的博客 同时发布 上期链接:Galgame汉化中的逆向(四)_IDA静态分析psv游戏
之前我们谈论的基本上都是静态汉化。所谓静态汉化,即分析文件结构、二进制脚本opcode,然后进行静态封包等方法。与类似于静态编译的语言类似,在运行前数据类型等已经确定完成,程序运行时按照既定的逻辑执行,静态汉化显示的是我们提前准备好的汉化文本。大部分的主机游戏汉化都是静态汉化,因为权限等问题,主机几乎不可能动态调试(即使有,gdbserver等用起来也挺费劲,也可能有兼容性问题调试失败)。再加上在主机上hook也很麻烦,测试极不方便,所以大部分主机游戏汉化以静态汉化为主,有模拟器的可能会结合一些动态调试辅助分析(不过别指望模拟器的调试有多好用了...)。
静态汉化是基础,对于常见文件结构、二进制脚本、算法等有了一定了解后,我们才能更好地找到关键位置dump、文本注入点等,因此我之前的汉化教程都是以静态汉化为主。与静态汉化相对的是动态汉化,往往不需要进行复杂的文件分析和二进制脚本分析,通常也不用考虑封包问题。动态汉化中,文本显示是程序运行时动态注入和替换的,重点是找到:
目前关于动态汉化的分析帖相对来说比较少,下面我们就以Majirov3引擎为例,来谈谈如何进行动态分析、如何进行动态汉化、以及如何解决一些动态汉化中出现的问题。
动态汉化的第一步,动态dump封包中已经解密完成的二进制脚本,从中提取文本和对应的偏移。那么如何去找呢?通常可以在游戏运行时候去搜索内存中的特定文本,找出最像是二进制脚本的那部分(可能有多个搜索结果,但有些并不是源头,类似于用CE去搜索会有多个数值匹配),然后下硬件访问断点,看是哪些代码生成的。
但是这个方法有个问题,解密文本的位置可能是malloc动态生成的缓冲区,重启调试器后位置会改变,导致断点失效。这时候我们可以考虑hook文件访问的API,如fopen
,CreateFile
等,来顺藤摸瓜找到读取封包和解密文本的位置。有可能没有动态链接msvcrt.dll
,而是静态链接到exe里了,导致导入表没有此函数。一般ida可以识别出这些静态链接的C库函数,如下:
之后我们可以对这些函数进行hook,此游戏用的都是c库函数进行文件读取。代码如下:
之后我们可以查看日志,在进入章节的时候,看看是哪些函数调用了文件API。
fread
的读取数据的大小可能和二进制文件的结构相关,比如说第一个fread
先在scenario.arc
文件开头读取了0x1c大小,我们可以推测文件头的大小是0x1c。用同样的方法,可以顺便把封包结构分析出来了,包括封包内的每个子项(mjo
)数据结构。下图为scenario.arc
在开头和0x114dfb
位置的内容,观察发现在utf-8
或sjis
下没有有意义字符串,可以断定mjo
是加密或压缩的
majiroV3封包文件结构总结如下:
当然了,这个封包结构很简单,直接静态黑箱分析也完全能猜出来,上面只是为了演示一下动态分析的一些思路。分析封包文件结构不是必须的,但是可以帮助我们更好的找到解密文本的位置。之后,定位到fopen
,scenario.arc
后的fread
,在缓冲区下写入断点(此地址就是之前所说的每次都会变的malloc地址),即可定位到解密文本的内容。
或者通过日志0x440cd6 fread(0x80f3b0, 0x10, 0x1, 0x4ca198) offset=0x114dfb
的返回地址0x440cd6
,找到准备读取每一个mjo的函数,即sub_440AB0
。这个引擎定位还是比较容易的,有日语错误信息辅助定位,默认显示乱码,需要把IDA的Cstyle default-8bit encoding
改成Shift-jis
编码。
我们已经找到了二进制脚本读取的函数,稍微分析一下不难找到文本解密函数sub_478E70
。虽然用了SSE
指令集优化,但是不难分析的,典型的xor加密,ida伪代码可读性已经很强了。本节以动态dump讲解为主,此处就不再详细分析解密函数了。
关于具体的dump点,可以在sub_440AB0
的末尾进行hook,返回值eax
为mjo_struct
指针,同时储存在[4DC350]
全局变量中。下面为此函数返回处的反汇编代码:
根据sub_440AB0
反汇编伪代码(见上节)中的sub_478E70(*((__m128i **)context + 0x29), *((_DWORD *)context + 0x24))
解密函数,可以得出下面结论:
有了这些信息,我们可以写dump解密文本函数了,如下:
dump完后,查看一下,二进制脚本已经解密。至于提取剧本,简单观察大概是这样的结构40 08 [size 2] text 00
,匹配这种结构即可,当然也可以直接检测sjis编码提取,详见我写的binary_text.py 。
动态汉化的好处是我们不用去费半天劲逆向封包算法、不用再去分析二进制指令opcode。
但是同样动态汉化也有一些问题:
因此,选择文件hook点的位置,原则上越接近原始位置(读取二进制文本的位置)越好 。直接搜索显示在屏幕上的文本,得到的搜索结果可能有多个,分别修改一下看看对游戏产生什么影响。同时也要兼顾能否找到当前文本在脚本中的偏移 来进行定位,在查找到文本缓存的周围(如堆栈中的指针,寄存器,或者反汇编指令里引用的全局变量)来找找有没有标识当前文本在二进制脚本中位置的指针。下面的反汇编为本游戏的一些显示文本位置:
根据测试a. showtext_screen
这个位置最适合作为动态hook替换文本的位置,显示内容最接近实际显示的,而且修改后的字符串也会被记录到游戏backlog里,可供回看文本。其他的hook点有些会调用多次、有些文本不全、有些会显示过多字符(如人名,这些会作为语音标识,但不会显示在对话中)。对于显示函数的hook,总结如下:
写个脚本hook验证一下:
frida -l winterpolaris_hook.js -f Polaris_chs.exe >1.txt
将脚本注入游戏,由于控制台无法显示sjis字符,因此将输出内容重定向到文件中,然后用sjis编码查看。得到的内容如下:
对照我们用binary_text.py 提取的文本, 偏移(当前位置指针-解密缓冲区基址)正好是文件中的偏移,至此这个游戏动态汉化的理论研究已经完成,之后就是用c和内联汇编写程序实践了。下面是我们用于翻译的文本格式,白点列用于原文,黑点列用于译文,每行是●|num|addr|size●
的索引格式,详见binary_text.h
其实有时候如果我们实在找不到文本标识的偏移,也可以强行把游戏从从头到尾过一遍,把每句输出的文本提取出来。汉化的时候,再用hashmap
或Longest Common Subsequence
dp计算当前文本与文本数据库中的相似度,选取相似度最高的匹配用于替换。
以上,我们谈了谈如何进行动态汉化的相关分析,方便起见都是用的frida进行hook。但是frida属于测试环境,不可能要求每个人电脑上都有这个环境,而且也可能有python版本冲突等问题。需要用尽可能少的依赖制作汉化,因此就要结合C与内联汇编来写汉化程序了。在制作汉化程序之前,来科普一下汉化游戏常用的hook方法。
即把相应函数的导入表的地址(FirstThunk
)替换成我们的函数,实现hook。关于IAT结构和导入表相关内容,可以参考我之前写的文章SimpleDpack 。下面是IAT hook的代码,兼容64位,详见我的github, win_hook,c
IAThook
只适用于动态链接外部DLL的函数,对于exe内部的函数,就需要Inline hook
了,操作如下:
不过我们不用再自己解析函数开头处的机器码了,直接用微软的detours
的Inline hook
即可。细节上和上述可能有些区别,不过原理都是一样的。detours
用法如下:
上述hook代码编译成的载体是DLL,我们还需要把此DLL注入目标到exe中,接管某些函数改变其功能。
有三种常用方法:
代码如下,详见我的githubinjectdll.py , win_hook,c
到此,主要问题我们都搞清楚了,现在可以愉快地编写动态汉化程序了。动态汉化程序主要包括下面几个部分:
Inlinehook
处汇编环境与C语言函数的对接, 注意cdecl
和stdcall
,汇编调用C函数要自己保存寄存器
维护日文文本与汉化文本的对应关系,文本偏移的定位等数据。并且用二分法等算法来查找替换文本等。
这里采取的__declspec(naked)
形式进行内联汇编,进行获取当前文本指针、计算在文件中的偏移、调用相应的C函数查找字符串、替换汉化文本等操作。此处为了方便使用了一些全局变量,以g_
前缀开头。
此处用双向链表数据结构来存储文件名与文本项索引,g_mjos
全局变量来指向索引链表,g_cur_mjo
指向当前文本索引位置。当游戏加载脚本时会查询当前链表中是否已经加载过,可以避免重复加载造成的内存泄露。PFTEXTS
数据结构详见我的通用汉化文本格式,binary_text.h 。
由于我们用的是日文和中文对照文本,因此文件用的utf-8
格式存储,动态替换汉化文本要转换为gb2312
格式。
lplf->lfCharSet
改为0x86即可,字体改成simhei
。
当然改完后读取gb2312也可能没法正常显示,因为游戏可能对字符进行限制。未处于sjis
区间的字符可能会显示成方框,也可能会被当成单字节字符显示 ,造成接下来运行错误。
这个游戏比较特殊,没有用cmp xx 81h
等直接判断,而是用了charmap
映射了当前字节数值的类型,与“是否能构成sjis
字符”相关。bp TextOutA
可以发现非sjis
字符会被当成单字节字符。再稍微跟一下,可以看见其通过查表确定是否为sjis
字符,0x4AE2E9
为字符类型映射表,如下图所示。
解决方法也很简单,直接用内联汇编来替换sub_47EE00
,去除sjis
范围限制。当然也可以去修改映射表,但是不确定是不是其他的函数也用这个映射表,改了后可能会出现问题。
最后就是处理一些小问题了,比如说有些字符没有显示全,可能是因为字体高度不够;菜单乱码等问题,可能对应的文本是通过其他函数显示的,或是菜单文本本身是在exe里面的,此处不再赘述。
折腾了半天,现在我们的动态汉化终于成功运行了!完整代码详见我的github, winterpolaris_hook.c 。
虽然难度不大,但是这篇教程写了也快一天才完成,之前搜集素材、编写程序、调试等断断续续地也用了将近一周。主要是想着如何叙述得容易理解,如何使得结构清晰有条理性。其实动态汉化更多的意义在于折腾,自己一步步地探索与改造的乐趣,就像是DIY的乐趣。下面再补充一些关于编译与调试的内容。
因为windows下没有regex.h
头文件,所以一开始我是用mingw
的gcc
来编译的。有个问题是,无法链接msvc
编译的detours.lib
(很多符号找不到,报错),也不太清楚怎么用gcc
编译detours
。而且gcc
貌似没法声明naked
函数类型?
于是就用clang
了,因为-target i686-pc-windows-msvc
可以兼容msvc
的link, 同时语法上也接近gcc
用起来会比较方便。但是这个模式就无法链接GNU的静态库了如libxxx.a
。虽然强行把libregex.dll.a
改名为regex.lib
倒是也能识别,但是没法静态链接,会附加一大堆mingw
的dll。makefile
如下,里面会用到我以前写的一些文件,现在都已上传到GalgameReverse 。
clang编译的时候加入-g
调试信息到dll中,直接用mklink
符号链接把dll链接到exe所在的路径下,vscode里面launch.json
lldb中program填写对应的exe。在C源代码下断点,F5启动exe即可调试。launch.json如下:
不过vscode目前好像不支持内联汇编调试,那么就用x64dbg调试吧,可以读取到调试信息并显示源码行数。这里有个小技巧,我们可以打印出来Inlinehook
的地址,然后再用x64dbg调试,方便定位。
.text:
00488F86
;
FILE
*
__cdecl fopen(const char
*
FileName, const char
*
Mode)
.text:
00488F86
_fopen proc near ; CODE XREF: sub_42D210
+
177
↑p
.text:
00488F86
; sub_42D210
+
3F2
↑p ...
.text:
00488F86
.text:
00488F86
FileName
=
dword ptr
8
.text:
00488F86
Mode
=
dword ptr
0Ch
.text:
00488F86
.text:
00488F86
push ebp
.text:
00488F87
mov ebp, esp
.text:
00488F89
push
40h
;
'@'
; ShFlag
.text:
00488F8B
push [ebp
+
Mode] ; Mode
.text:
00488F8E
push [ebp
+
FileName] ; FileName
.text:
00488F91
call __fsopen
.text:
00488F96
add esp,
0Ch
.text:
00488F99
pop ebp
.text:
00488F9A
retn
.text:
00488F9A
_fopen endp
.text:
00488F86
;
FILE
*
__cdecl fopen(const char
*
FileName, const char
*
Mode)
.text:
00488F86
_fopen proc near ; CODE XREF: sub_42D210
+
177
↑p
.text:
00488F86
; sub_42D210
+
3F2
↑p ...
.text:
00488F86
.text:
00488F86
FileName
=
dword ptr
8
.text:
00488F86
Mode
=
dword ptr
0Ch
.text:
00488F86
.text:
00488F86
push ebp
.text:
00488F87
mov ebp, esp
.text:
00488F89
push
40h
;
'@'
; ShFlag
.text:
00488F8B
push [ebp
+
Mode] ; Mode
.text:
00488F8E
push [ebp
+
FileName] ; FileName
.text:
00488F91
call __fsopen
.text:
00488F96
add esp,
0Ch
.text:
00488F99
pop ebp
.text:
00488F9A
retn
.text:
00488F9A
_fopen endp
var g_base
=
0x400000
;
function hook_fopen_fread()
/
/
print
fopen
and
fread to investigate
file
structor
{
var memove
=
new NativeFunction(ptr(g_base
+
0x8aa80
),
'void'
, [
"pointer"
,
"pointer"
,
"int"
]);
var sprintf
=
new NativeFunction(ptr(g_base
+
0x89493
),
'int'
, [
"pointer"
,
"pointer"
,
"..."
],
"mscdecl"
);
var fopen
=
new NativeFunction(ptr(g_base
+
0x88F86
),
'pointer'
, [
"pointer"
,
"pointer"
]);
/
/
in
this game,
all
file
function
is
static link
var fread
=
new NativeFunction(ptr(g_base
+
0x8B609
),
'size_t'
, [
'pointer'
,
'size_t'
,
'size_t'
,
'size_t'
]);
var fseek
=
new NativeFunction(ptr(g_base
+
0x8DAD2
),
'int'
, [
"pointer"
,
"int"
,
"int"
]);
var ftell
=
new NativeFunction(ptr(g_base
+
0x8EEF6
),
'int'
, [
"pointer"
]);
var g_fargs
=
[];
Interceptor.attach(fopen, {
onEnter: function(args)
{
g_fargs.push(args[
0
].readCString());
},
onLeave: function(retval)
{
var ret_addr
=
this.context.esp.readPointer();
var filepath
=
g_fargs[
0
];
if
(retval.toInt32()!
=
0
)
{
console.log(ret_addr,
"fopen"
,
filepath.split(
'\\')[filepath.split('
\\').length
-
1
],
"fp="
+
retval);
}
g_fargs
=
[]
}
})
Interceptor.attach(fread, {
onEnter: function(args)
{
var ret_addr
=
this.context.esp.readPointer();
var fp
=
args[
3
];
var offset
=
ftell(fp);
console.log(ret_addr,
"fread("
+
args[
0
]
+
", "
+
args[
1
]
+
", "
+
args[
2
]
+
", "
+
fp
+
")"
,
"offset=0x"
+
offset.toString(
16
));
}
})
}
var g_base
=
0x400000
;
function hook_fopen_fread()
/
/
print
fopen
and
fread to investigate
file
structor
{
var memove
=
new NativeFunction(ptr(g_base
+
0x8aa80
),
'void'
, [
"pointer"
,
"pointer"
,
"int"
]);
var sprintf
=
new NativeFunction(ptr(g_base
+
0x89493
),
'int'
, [
"pointer"
,
"pointer"
,
"..."
],
"mscdecl"
);
var fopen
=
new NativeFunction(ptr(g_base
+
0x88F86
),
'pointer'
, [
"pointer"
,
"pointer"
]);
/
/
in
this game,
all
file
function
is
static link
var fread
=
new NativeFunction(ptr(g_base
+
0x8B609
),
'size_t'
, [
'pointer'
,
'size_t'
,
'size_t'
,
'size_t'
]);
var fseek
=
new NativeFunction(ptr(g_base
+
0x8DAD2
),
'int'
, [
"pointer"
,
"int"
,
"int"
]);
var ftell
=
new NativeFunction(ptr(g_base
+
0x8EEF6
),
'int'
, [
"pointer"
]);
var g_fargs
=
[];
Interceptor.attach(fopen, {
onEnter: function(args)
{
g_fargs.push(args[
0
].readCString());
},
onLeave: function(retval)
{
var ret_addr
=
this.context.esp.readPointer();
var filepath
=
g_fargs[
0
];
if
(retval.toInt32()!
=
0
)
{
console.log(ret_addr,
"fopen"
,
filepath.split(
'\\')[filepath.split('
\\').length
-
1
],
"fp="
+
retval);
}
g_fargs
=
[]
}
})
Interceptor.attach(fread, {
onEnter: function(args)
{
var ret_addr
=
this.context.esp.readPointer();
var fp
=
args[
3
];
var offset
=
ftell(fp);
console.log(ret_addr,
"fread("
+
args[
0
]
+
", "
+
args[
1
]
+
", "
+
args[
2
]
+
", "
+
fp
+
")"
,
"offset=0x"
+
offset.toString(
16
));
}
})
}
0x47a51f
fopen scenario.arc fp
=
0x4ca198
/
/
first test the
file
size
0x47a547
fread(
0xd12d2c
,
0x1c
,
0x1
,
0x4ca198
) offset
=
0x0
0x47a796
fread(
0xd12d98
,
0x3a0
,
0x1
,
0x4ca198
) offset
=
0x1c
0x47a80f
fread(
0x80f304
,
0x1
,
0x1
,
0x4ca198
) offset
=
0x14de4a
/
/
end
0x47a82a
fread(
0x80f304
,
0x1
,
0x1
,
0x4ca198
) offset
=
0x14de4b
0x47b705
fopen scenario.arc fp
=
0x4ca198
0x440cd6
fread(
0x80f3b0
,
0x10
,
0x1
,
0x4ca198
) offset
=
0x114dfb
0x440d50
fread(
0xba4477c
,
0x4
,
0x1
,
0x4ca198
) offset
=
0x114e0b
0x440d6c
fread(
0xba44780
,
0x4
,
0x1
,
0x4ca198
) offset
=
0x114e0f
0x440d88
fread(
0xba44774
,
0x4
,
0x1
,
0x4ca198
) offset
=
0x114e13
0x440db4
fread(
0x766f1f0
,
0x28
,
0x1
,
0x4ca198
) offset
=
0x114e17
0x440dcc
fread(
0xba44778
,
0x4
,
0x1
,
0x4ca198
) offset
=
0x114e3f
0x440df0
fread(
0xb99ac90
,
0xeab
,
0x1
,
0x4ca198
) offset
=
0x114e43
/
/
read mjo content
0x47b705
fopen scenario.arc fp
=
0x4ca198
0x440cd6
fread(
0x80f220
,
0x10
,
0x1
,
0x4ca198
) offset
=
0x12e5d2
0x440d50
fread(
0xba446a4
,
0x4
,
0x1
,
0x4ca198
) offset
=
0x12e5e2
0x440d6c
fread(
0xba446a8
,
0x4
,
0x1
,
0x4ca198
) offset
=
0x12e5e6
0x440d88
fread(
0xba4469c
,
0x4
,
0x1
,
0x4ca198
) offset
=
0x12e5ea
0x440db4
fread(
0xb9654b8
,
0x570
,
0x1
,
0x4ca198
) offset
=
0x12e5ee
0x440dcc
fread(
0xba446a0
,
0x4
,
0x1
,
0x4ca198
) offset
=
0x12eb5e
0x440df0
fread(
0xbbb9850
,
0x17e39
,
0x1
,
0x4ca198
) offset
=
0x12eb62
0x47b705
fopen scenario.arc fp
=
0x4ca198
0x440cd6
fread(
0x80f220
,
0x10
,
0x1
,
0x4ca198
) offset
=
0xea802
0x440d50
fread(
0xba4318c
,
0x4
,
0x1
,
0x4ca198
) offset
=
0xea812
0x440d6c
fread(
0xba43190
,
0x4
,
0x1
,
0x4ca198
) offset
=
0xea816
0x440d88
fread(
0xba43184
,
0x4
,
0x1
,
0x4ca198
) offset
=
0xea81a
0x440db4
fread(
0x76774c8
,
0x10
,
0x1
,
0x4ca198
) offset
=
0xea81e
0x440dcc
fread(
0xba43188
,
0x4
,
0x1
,
0x4ca198
) offset
=
0xea82e
0x440df0
fread(
0xbb95f08
,
0x11ea
,
0x1
,
0x4ca198
) offset
=
0xea832
0x47a51f
fopen scenario.arc fp
=
0x4ca198
/
/
first test the
file
size
0x47a547
fread(
0xd12d2c
,
0x1c
,
0x1
,
0x4ca198
) offset
=
0x0
0x47a796
fread(
0xd12d98
,
0x3a0
,
0x1
,
0x4ca198
) offset
=
0x1c
0x47a80f
fread(
0x80f304
,
0x1
,
0x1
,
0x4ca198
) offset
=
0x14de4a
/
/
end
0x47a82a
fread(
0x80f304
,
0x1
,
0x1
,
0x4ca198
) offset
=
0x14de4b
0x47b705
fopen scenario.arc fp
=
0x4ca198
0x440cd6
fread(
0x80f3b0
,
0x10
,
0x1
,
0x4ca198
) offset
=
0x114dfb
0x440d50
fread(
0xba4477c
,
0x4
,
0x1
,
0x4ca198
) offset
=
0x114e0b
0x440d6c
fread(
0xba44780
,
0x4
,
0x1
,
0x4ca198
) offset
=
0x114e0f
0x440d88
fread(
0xba44774
,
0x4
,
0x1
,
0x4ca198
) offset
=
0x114e13
0x440db4
fread(
0x766f1f0
,
0x28
,
0x1
,
0x4ca198
) offset
=
0x114e17
0x440dcc
fread(
0xba44778
,
0x4
,
0x1
,
0x4ca198
) offset
=
0x114e3f
0x440df0
fread(
0xb99ac90
,
0xeab
,
0x1
,
0x4ca198
) offset
=
0x114e43
/
/
read mjo content
0x47b705
fopen scenario.arc fp
=
0x4ca198
0x440cd6
fread(
0x80f220
,
0x10
,
0x1
,
0x4ca198
) offset
=
0x12e5d2
0x440d50
fread(
0xba446a4
,
0x4
,
0x1
,
0x4ca198
) offset
=
0x12e5e2
0x440d6c
fread(
0xba446a8
,
0x4
,
0x1
,
0x4ca198
) offset
=
0x12e5e6
0x440d88
fread(
0xba4469c
,
0x4
,
0x1
,
0x4ca198
) offset
=
0x12e5ea
0x440db4
fread(
0xb9654b8
,
0x570
,
0x1
,
0x4ca198
) offset
=
0x12e5ee
0x440dcc
fread(
0xba446a0
,
0x4
,
0x1
,
0x4ca198
) offset
=
0x12eb5e
0x440df0
fread(
0xbbb9850
,
0x17e39
,
0x1
,
0x4ca198
) offset
=
0x12eb62
0x47b705
fopen scenario.arc fp
=
0x4ca198
0x440cd6
fread(
0x80f220
,
0x10
,
0x1
,
0x4ca198
) offset
=
0xea802
0x440d50
fread(
0xba4318c
,
0x4
,
0x1
,
0x4ca198
) offset
=
0xea812
0x440d6c
fread(
0xba43190
,
0x4
,
0x1
,
0x4ca198
) offset
=
0xea816
0x440d88
fread(
0xba43184
,
0x4
,
0x1
,
0x4ca198
) offset
=
0xea81a
0x440db4
fread(
0x76774c8
,
0x10
,
0x1
,
0x4ca198
) offset
=
0xea81e
0x440dcc
fread(
0xba43188
,
0x4
,
0x1
,
0x4ca198
) offset
=
0xea82e
0x440df0
fread(
0xbb95f08
,
0x11ea
,
0x1
,
0x4ca198
) offset
=
0xea832
scenario.arc, header size:
1C
0
~
0x10
MajiroArcV3.
000
0x10
~
0x1C
index_count
4
, name_table_offset
4
, frist_mjo_offset
4
/
/
41
00
00
00
2C
04
00
00
AA
07
00
00
0x1C
~
0x42C
arc_index[index_count]
/
/
arc_block_num
*
0x10
=
0x410
| unknow1
4
/
/
hash
?
| unknow2
4
| mjo_offset
4
| mjo_size
4
/
/
CA
91
E5
51
F5
10
EE
87
67
C6
0A
00
7B
B7
00
00
0x42C
~
0x7AA
name_table
0x7AA
~ mjo[index_count]
mjo_entry at
0x114dfb
0x0
~
0x10
MajiroObjX1.
000
0x10
~
0x1c
n1
4
, unknow2
4
, mjo_block_num
4
/
/
E9
06
00
00
00
00
00
00
05
00
00
00
0x1c
~
0x44
mjo_block
/
/
mjo_block_num
*
8
=
0x28
0x44
~
0x48
mjo_size
4
scenario.arc, header size:
1C
0
~
0x10
MajiroArcV3.
000
0x10
~
0x1C
index_count
4
, name_table_offset
4
, frist_mjo_offset
4
/
/
41
00
00
00
2C
04
00
00
AA
07
00
00
0x1C
~
0x42C
arc_index[index_count]
/
/
arc_block_num
*
0x10
=
0x410
| unknow1
4
/
/
hash
?
| unknow2
4
| mjo_offset
4
| mjo_size
4
/
/
CA
91
E5
51
F5
10
EE
87
67
C6
0A
00
7B
B7
00
00
0x42C
~
0x7AA
name_table
0x7AA
~ mjo[index_count]
mjo_entry at
0x114dfb
0x0
~
0x10
MajiroObjX1.
000
0x10
~
0x1c
n1
4
, unknow2
4
, mjo_block_num
4
/
/
E9
06
00
00
00
00
00
00
05
00
00
00
0x1c
~
0x44
mjo_block
/
/
mjo_block_num
*
8
=
0x28
0x44
~
0x48
mjo_size
4
0047A537
|
6A
01
| push
1
|
0047A539
|
8DBE
04020000
| lea edi,dword ptr ds:[esi
+
204
] | edi:
"MajiroArcV3.000"
, esi
+
204
:
"MajiroArcV3.000"
0047A53F
|
6A
1C
| push
1C
|
0047A541
|
57
| push edi | edi:
"MajiroArcV3.000"
0047A542
| E8 C2100100 | call <polaris_chs.sub_48B609> | fread
/
/
read scenerio mjo
char
*
__usercall sub_440AB0@<eax>(
int
a1@<ebx>,
int
a2@<edi>,
int
a3@<esi>, char
*
FullPath)
{
char
*
v4;
/
/
ecx
char
*
context;
/
/
esi
int
v7;
/
/
ebx
int
v8;
/
/
edx
int
v9;
/
/
edx
FILE
*
fp;
/
/
eax MAPDST
char
*
v12;
/
/
ecx
char
*
v13;
/
/
edx
bool
v14;
/
/
cf
char
*
v15;
/
/
ecx
char
*
v16;
/
/
edx
void
*
buf_mjoblock;
/
/
eax
void
*
buf_mjo;
/
/
eax
char
*
v19;
/
/
ecx
char v20;
/
/
al
size_t mjo_block_size;
/
/
[esp
-
1Ch
] [ebp
-
32Ch
]
size_t mjo_size;
/
/
[esp
-
1Ch
] [ebp
-
32Ch
]
int
v28;
/
/
[esp
+
4h
] [ebp
-
30Ch
]
int
v29;
/
/
[esp
+
4h
] [ebp
-
30Ch
]
int
v30;
/
/
[esp
+
8h
] [ebp
-
308h
]
char
Buffer
[
255
];
/
/
[esp
+
Ch] [ebp
-
304h
] BYREF
char v32;
/
/
[esp
+
10Bh
] [ebp
-
205h
] BYREF
char mjo_Filename[
512
];
/
/
[esp
+
10Ch
] [ebp
-
204h
] BYREF
_splitpath(FullPath,
0
,
0
, mjo_Filename,
0
);
v4
=
&v32;
while
(
*
+
+
v4 )
;
strcpy(v4,
".mjo"
);
tolower((unsigned __int8
*
)mjo_Filename);
if
( strlen(mjo_Filename) >
0x7F
)
sub_441150(
"ファイル名[%s]が長すぎます%d文字以内にしてください。"
,
(
int
)mjo_Filename,
127
,
(
int
)FullPath,
v28);
context
=
dword_4DC350;
v7
=
0
;
v30
=
0
;
if
( !dword_4DC350 )
goto LABEL_12;
while
( sub_47C550(context, mjo_Filename) )
/
/
strcmp?
{
context
=
(char
*
)
*
((_DWORD
*
)context
+
0x2A
);
if
( !context )
{
LABEL_13:
context
=
(char
*
)try_malloc(
0xB0
);
memset(context,
0
,
0xB0u
);
while
(
1
)
{
if
( sub_47BE30(mjo_Filename) )
/
/
if
not
find target mjo, to load scenario
goto LABEL_16;
sub_47A310(
"scenario"
,
0
);
/
/
test scenario files
sub_47A310(
"scenario9"
,
0
);
sub_47A310(
"scenario8"
,
0
);
sub_47A310(
"scenario7"
,
0
);
sub_47A310(
"scenario6"
,
0
);
sub_47A310(
"scenario5"
,
0
);
sub_47A310(
"scenario4"
,
0
);
sub_47A310(
"scenario3"
,
0
);
sub_47A310(
"scenario2"
,
0
);
sub_47A310(
"scenario1"
,
0
);
if
( sub_47BE30(mjo_Filename) )
{
LABEL_16:
*
((_DWORD
*
)context
+
0x20
)
=
((
int
(__cdecl
*
)(LPCSTR))sub_479FE0)(mjo_Filename);
*
((_DWORD
*
)context
+
0x21
)
=
v9;
fp
=
(
FILE
*
)try_fopen(a2, (
int
)context, mjo_Filename,
"rb"
);
/
/
fopen
if
( fp && fread(
Buffer
,
0x10u
,
1u
, fp)
=
=
1
)
/
/
MajiroObjV1.
000
{
v12
=
off_4C7ABC[
0
];
v13
=
Buffer
;
a2
=
12
;
do
{
if
(
*
(_DWORD
*
)v12 !
=
*
(_DWORD
*
)v13 )
{
v15
=
off_4C7AC0;
v16
=
Buffer
;
a2
=
12
;
while
(
*
(_DWORD
*
)v15
=
=
*
(_DWORD
*
)v16 )
{
v15
+
=
4
;
v16
+
=
4
;
v14
=
(unsigned
int
)a2 <
4
;
a2
-
=
4
;
if
( v14 )
{
v29
=
1
;
goto LABEL_26;
}
}
goto LABEL_32;
}
v12
+
=
4
;
v13
+
=
4
;
v14
=
(unsigned
int
)a2 <
4
;
a2
-
=
4
;
}
while
( !v14 );
v29
=
0
;
LABEL_26:
if
( fread(context
+
0x94
,
4u
,
1u
, fp)
=
=
1
&& fread(context
+
0x98
,
4u
,
1u
, fp)
=
=
1
)
/
/
read n1, n2
{
a2
=
(
int
)(context
+
0x8C
);
if
( fread(context
+
0x8C
,
4u
,
1u
, fp)
=
=
1
)
/
/
read mjo_block_num
{
buf_mjoblock
=
try_malloc(
8
*
*
(_DWORD
*
)a2
+
0x20
);
/
/
malloc
mjo_block_size
=
8
*
*
(_DWORD
*
)a2;
*
((_DWORD
*
)context
+
0x28
)
=
buf_mjoblock;
if
( fread(buf_mjoblock, mjo_block_size,
1u
, fp)
=
=
1
)
/
/
read mjo_block
{
a2
=
(
int
)(context
+
0x90
);
if
( fread(context
+
0x90
,
4u
,
1u
, fp)
=
=
1
)
{
buf_mjo
=
try_malloc(
*
(_DWORD
*
)a2
+
0x20
);
/
/
malloc
mjo_size
=
*
(_DWORD
*
)a2;
*
((_DWORD
*
)context
+
0x29
)
=
buf_mjo;
if
( fread(buf_mjo, mjo_size,
1u
, fp)
=
=
1
)
{
fclose(fp);
if
( v29 )
sub_478E70(
*
((__m128i
*
*
)context
+
0x29
),
*
((_DWORD
*
)context
+
0x24
));
/
/
decrypt mjo, dword
0x24
is
context
+
0x90
v19
=
mjo_Filename;
do
{
v20
=
*
v19
+
+
;
v19[context
-
mjo_Filename
-
1
]
=
v20;
}
while
( v20 );
*
((_DWORD
*
)context
+
0x22
)
=
sub_478E10(context);
if
( !v30 )
{
*
((_DWORD
*
)context
+
0x2A
)
=
dword_4DC350;
dword_4DC350
=
context;
}
*
((_DWORD
*
)context
+
0x27
)
=
sub_43A370(context,
*
((_DWORD
*
)context
+
0x26
));
return
context;
}
}
}
}
}
}
LABEL_32:
v7
=
v30;
}
sub_4793F0(
"MajiroObj : ファイル [%s] の読み込みで失敗しました"
, (
int
)FullPath, a2, a3, a1);
if
(
*
((_DWORD
*
)context
+
0x28
) )
free(
*
((void
*
*
)context
+
0x28
));
if
(
*
((_DWORD
*
)context
+
0x29
) )
free(
*
((void
*
*
)context
+
0x29
));
free(context);
LABEL_12:
if
( !v7 )
goto LABEL_13;
}
}
}
if
(
*
((_DWORD
*
)context
+
0x20
) !
=
((
int
(__cdecl
*
)(LPCSTR))sub_479FE0)(mjo_Filename)
||
*
((_DWORD
*
)context
+
0x21
) !
=
v8 )
{
v7
=
1
;
v30
=
1
;
free(
*
((void
*
*
)context
+
0x28
));
free(
*
((void
*
*
)context
+
0x29
));
goto LABEL_12;
}
return
context;
}
0047A537
|
6A
01
| push
1
|
0047A539
|
8DBE
04020000
| lea edi,dword ptr ds:[esi
+
204
] | edi:
"MajiroArcV3.000"
, esi
+
204
:
"MajiroArcV3.000"
0047A53F
|
6A
1C
| push
1C
|
0047A541
|
57
| push edi | edi:
"MajiroArcV3.000"
0047A542
| E8 C2100100 | call <polaris_chs.sub_48B609> | fread
/
/
read scenerio mjo
char
*
__usercall sub_440AB0@<eax>(
int
a1@<ebx>,
int
a2@<edi>,
int
a3@<esi>, char
*
FullPath)
{
char
*
v4;
/
/
ecx
char
*
context;
/
/
esi
int
v7;
/
/
ebx
int
v8;
/
/
edx
int
v9;
/
/
edx
FILE
*
fp;
/
/
eax MAPDST
char
*
v12;
/
/
ecx
char
*
v13;
/
/
edx
bool
v14;
/
/
cf
char
*
v15;
/
/
ecx
char
*
v16;
/
/
edx
void
*
buf_mjoblock;
/
/
eax
void
*
buf_mjo;
/
/
eax
char
*
v19;
/
/
ecx
char v20;
/
/
al
size_t mjo_block_size;
/
/
[esp
-
1Ch
] [ebp
-
32Ch
]
size_t mjo_size;
/
/
[esp
-
1Ch
] [ebp
-
32Ch
]
int
v28;
/
/
[esp
+
4h
] [ebp
-
30Ch
]
int
v29;
/
/
[esp
+
4h
] [ebp
-
30Ch
]
int
v30;
/
/
[esp
+
8h
] [ebp
-
308h
]
char
Buffer
[
255
];
/
/
[esp
+
Ch] [ebp
-
304h
] BYREF
char v32;
/
/
[esp
+
10Bh
] [ebp
-
205h
] BYREF
char mjo_Filename[
512
];
/
/
[esp
+
10Ch
] [ebp
-
204h
] BYREF
_splitpath(FullPath,
0
,
0
, mjo_Filename,
0
);
v4
=
&v32;
while
(
*
+
+
v4 )
;
strcpy(v4,
".mjo"
);
tolower((unsigned __int8
*
)mjo_Filename);
if
( strlen(mjo_Filename) >
0x7F
)
sub_441150(
"ファイル名[%s]が長すぎます%d文字以内にしてください。"
,
(
int
)mjo_Filename,
127
,
(
int
)FullPath,
v28);
context
=
dword_4DC350;
v7
=
0
;
v30
=
0
;
if
( !dword_4DC350 )
goto LABEL_12;
while
( sub_47C550(context, mjo_Filename) )
/
/
strcmp?
{
context
=
(char
*
)
*
((_DWORD
*
)context
+
0x2A
);
if
( !context )
{
LABEL_13:
context
=
(char
*
)try_malloc(
0xB0
);
memset(context,
0
,
0xB0u
);
while
(
1
)
{
if
( sub_47BE30(mjo_Filename) )
/
/
if
not
find target mjo, to load scenario
goto LABEL_16;
sub_47A310(
"scenario"
,
0
);
/
/
test scenario files
sub_47A310(
"scenario9"
,
0
);
sub_47A310(
"scenario8"
,
0
);
sub_47A310(
"scenario7"
,
0
);
sub_47A310(
"scenario6"
,
0
);
sub_47A310(
"scenario5"
,
0
);
sub_47A310(
"scenario4"
,
0
);
sub_47A310(
"scenario3"
,
0
);
sub_47A310(
"scenario2"
,
0
);
sub_47A310(
"scenario1"
,
0
);
if
( sub_47BE30(mjo_Filename) )
{
LABEL_16:
*
((_DWORD
*
)context
+
0x20
)
=
((
int
(__cdecl
*
)(LPCSTR))sub_479FE0)(mjo_Filename);
*
((_DWORD
*
)context
+
0x21
)
=
v9;
fp
=
(
FILE
*
)try_fopen(a2, (
int
)context, mjo_Filename,
"rb"
);
/
/
fopen
if
( fp && fread(
Buffer
,
0x10u
,
1u
, fp)
=
=
1
)
/
/
MajiroObjV1.
000
{
v12
=
off_4C7ABC[
0
];
v13
=
Buffer
;
a2
=
12
;
do
{
if
(
*
(_DWORD
*
)v12 !
=
*
(_DWORD
*
)v13 )
{
v15
=
off_4C7AC0;
v16
=
Buffer
;
a2
=
12
;
while
(
*
(_DWORD
*
)v15
=
=
*
(_DWORD
*
)v16 )
{
v15
+
=
4
;
v16
+
=
4
;
v14
=
(unsigned
int
)a2 <
4
;
a2
-
=
4
;
if
( v14 )
{
v29
=
1
;
goto LABEL_26;
}
}
goto LABEL_32;
}
v12
+
=
4
;
v13
+
=
4
;
v14
=
(unsigned
int
)a2 <
4
;
a2
-
=
4
;
}
while
( !v14 );
v29
=
0
;
LABEL_26:
if
( fread(context
+
0x94
,
4u
,
1u
, fp)
=
=
1
&& fread(context
+
0x98
,
4u
,
1u
, fp)
=
=
1
)
/
/
read n1, n2
{
a2
=
(
int
)(context
+
0x8C
);
if
( fread(context
+
0x8C
,
4u
,
1u
, fp)
=
=
1
)
/
/
read mjo_block_num
{
buf_mjoblock
=
try_malloc(
8
*
*
(_DWORD
*
)a2
+
0x20
);
/
/
malloc
mjo_block_size
=
8
*
*
(_DWORD
*
)a2;
*
((_DWORD
*
)context
+
0x28
)
=
buf_mjoblock;
if
( fread(buf_mjoblock, mjo_block_size,
1u
, fp)
=
=
1
)
/
/
read mjo_block
{
a2
=
(
int
)(context
+
0x90
);
if
( fread(context
+
0x90
,
4u
,
1u
, fp)
=
=
1
)
{
buf_mjo
=
try_malloc(
*
(_DWORD
*
)a2
+
0x20
);
/
/
malloc
mjo_size
=
*
(_DWORD
*
)a2;
*
((_DWORD
*
)context
+
0x29
)
=
buf_mjo;
if
( fread(buf_mjo, mjo_size,
1u
, fp)
=
=
1
)
{
fclose(fp);
if
( v29 )
sub_478E70(
*
((__m128i
*
*
)context
+
0x29
),
*
((_DWORD
*
)context
+
0x24
));
/
/
decrypt mjo, dword
0x24
is
context
+
0x90
v19
=
mjo_Filename;
do
{
v20
=
*
v19
+
+
;
v19[context
-
mjo_Filename
-
1
]
=
v20;
}
while
( v20 );
*
((_DWORD
*
)context
+
0x22
)
=
sub_478E10(context);
if
( !v30 )
{
*
((_DWORD
*
)context
+
0x2A
)
=
dword_4DC350;
dword_4DC350
=
context;
}
*
((_DWORD
*
)context
+
0x27
)
=
sub_43A370(context,
*
((_DWORD
*
)context
+
0x26
));
return
context;
}
}
}
}
}
}
LABEL_32:
v7
=
v30;
}
sub_4793F0(
"MajiroObj : ファイル [%s] の読み込みで失敗しました"
, (
int
)FullPath, a2, a3, a1);
if
(
*
((_DWORD
*
)context
+
0x28
) )
free(
*
((void
*
*
)context
+
0x28
));
if
(
*
((_DWORD
*
)context
+
0x29
) )
free(
*
((void
*
*
)context
+
0x29
));
free(context);
LABEL_12:
if
( !v7 )
goto LABEL_13;
}
}
}
if
(
*
((_DWORD
*
)context
+
0x20
) !
=
((
int
(__cdecl
*
)(LPCSTR))sub_479FE0)(mjo_Filename)
||
*
((_DWORD
*
)context
+
0x21
) !
=
v8 )
{
v7
=
1
;
v30
=
1
;
free(
*
((void
*
*
)context
+
0x28
));
free(
*
((void
*
*
)context
+
0x29
));
goto LABEL_12;
}
return
context;
}
char __cdecl sub_478E70(__m128i
*
buf, unsigned
int
size)
{
__m128i
*
cur;
/
/
esi
__int32 v3;
/
/
eax
signed
int
v4;
/
/
edx
unsigned
int
v5;
/
/
edi
unsigned
int
i;
/
/
ecx
int
v7;
/
/
ecx
int
v8;
/
/
ecx
cur
=
buf;
LOBYTE(v3)
=
sub_479070(
0xFFFFFFFF
, (
int
)buf,
0
);
v4
=
size;
if
( size >
=
0x400
)
{
v5
=
size >>
10
;
v4
=
-
1024
*
(size >>
10
)
+
size;
do
{
if
( cur > (__m128i
*
)&unk_5CB5C4 || (__m128i
*
)((char
*
)&cur[
63
].m128i_u64[
1
]
+
4
) < &stru_5CB1C8 )
{
v3
=
(__int32)&unk_5CB1D8;
v7
=
0x20
;
do
{
v3
+
=
0x20
;
*
cur
=
_mm_xor_si128(_mm_loadu_si128((const __m128i
*
)(v3
-
0x30
)), _mm_loadu_si128(cur));
cur[
1
]
=
_mm_xor_si128(_mm_loadu_si128((const __m128i
*
)(v3
-
0x20
)), _mm_loadu_si128(cur
+
1
));
cur
+
=
2
;
-
-
v7;
}
while
( v7 );
}
else
{
for
( i
=
0
; i <
256
;
+
+
i )
{
v3
=
stru_5CB1C8.m128i_i32[i];
cur
-
>m128i_i32[
0
] ^
=
v3;
cur
=
(__m128i
*
)((char
*
)cur
+
4
);
}
}
-
-
v5;
}
while
( v5 );
}
if
( v4 >
0
)
{
v8
=
(char
*
)&stru_5CB1C8
-
(char
*
)cur;
do
{
LOBYTE(v3)
=
cur
-
>m128i_i8[v8];
cur
=
(__m128i
*
)((char
*
)cur
+
1
);
cur[
-
1
].m128i_i8[
15
] ^
=
v3;
-
-
v4;
}
while
( v4 >
0
);
}
return
v3;
}
char __cdecl sub_478E70(__m128i
*
buf, unsigned
int
size)
{
__m128i
*
cur;
/
/
esi
__int32 v3;
/
/
eax
signed
int
v4;
/
/
edx
unsigned
int
v5;
/
/
edi
unsigned
int
i;
/
/
ecx
int
v7;
/
/
ecx
int
v8;
/
/
ecx
cur
=
buf;
LOBYTE(v3)
=
sub_479070(
0xFFFFFFFF
, (
int
)buf,
0
);
v4
=
size;
if
( size >
=
0x400
)
{
v5
=
size >>
10
;
v4
=
-
1024
*
(size >>
10
)
+
size;
do
{
if
( cur > (__m128i
*
)&unk_5CB5C4 || (__m128i
*
)((char
*
)&cur[
63
].m128i_u64[
1
]
+
4
) < &stru_5CB1C8 )
{
v3
=
(__int32)&unk_5CB1D8;
v7
=
0x20
;
do
{
v3
+
=
0x20
;
*
cur
=
_mm_xor_si128(_mm_loadu_si128((const __m128i
*
)(v3
-
0x30
)), _mm_loadu_si128(cur));
cur[
1
]
=
_mm_xor_si128(_mm_loadu_si128((const __m128i
*
)(v3
-
0x20
)), _mm_loadu_si128(cur
+
1
));
cur
+
=
2
;
-
-
v7;
}
while
( v7 );
}
else
{
for
( i
=
0
; i <
256
;
+
+
i )
{
v3
=
stru_5CB1C8.m128i_i32[i];
cur
-
>m128i_i32[
0
] ^
=
v3;
cur
=
(__m128i
*
)((char
*
)cur
+
4
);
}
}
-
-
v5;
}
while
( v5 );
}
if
( v4 >
0
)
{
v8
=
(char
*
)&stru_5CB1C8
-
(char
*
)cur;
do
{
LOBYTE(v3)
=
cur
-
>m128i_i8[v8];
cur
=
(__m128i
*
)((char
*
)cur
+
1
);
cur[
-
1
].m128i_i8[
15
] ^
=
v3;
-
-
v4;
}
while
( v4 >
0
);
}
return
v3;
}
sub_440AB0
...
00440E9C
| A1
50C34D00
| mov eax,dword ptr ds:[
4DC350
] | eax:
"SUB_TITLE.MJO"
,
004DC350
:&
"SUB_TITLE.MJO"
00440EA1
|
8986
A8000000 | mov dword ptr ds:[esi
+
A8],eax | eax:
"SUB_TITLE.MJO"
00440EA7
|
8935
50C34D00
| mov dword ptr ds:[
4DC350
],esi |
004DC350
:&
"SUB_TITLE.MJO"
00440EAD
| FFB6
98000000
| push dword ptr ds:[esi
+
98
] |
00440EB3
|
56
| push esi |
00440EB4
| E8 B794FFFF | call <polaris_chs.sub_43A370> | sub_43A370
00440EB9
|
83C4
08
| add esp,
8
|
00440EBC
|
8986
9C000000
| mov dword ptr ds:[esi
+
9C
],eax | eax:
"SUB_TITLE.MJO"
00440EC2
|
8B4D
FC | mov ecx,dword ptr ss:[ebp
-
4
] |
00440EC5
|
8BC6
| mov eax,esi | eax:
"SUB_TITLE.MJO"
00440EC7
|
5F
| pop edi |
00440EC8
|
5E
| pop esi |
00440EC9
|
33CD
| xor ecx,ebp |
00440ECB
|
5B
| pop ebx |
00440ECC
| E8
66950400
| call <polaris_chs.sub_48A437> |
00440ED1
|
8BE5
| mov esp,ebp |
00440ED3
|
5D
| pop ebp |
00440ED4
| C3 | ret | load mjo end;
sub_440AB0
...
00440E9C
| A1
50C34D00
| mov eax,dword ptr ds:[
4DC350
] | eax:
"SUB_TITLE.MJO"
,
004DC350
:&
"SUB_TITLE.MJO"
00440EA1
|
8986
A8000000 | mov dword ptr ds:[esi
+
A8],eax | eax:
"SUB_TITLE.MJO"
00440EA7
|
8935
50C34D00
| mov dword ptr ds:[
4DC350
],esi |
004DC350
:&
"SUB_TITLE.MJO"
00440EAD
| FFB6
98000000
| push dword ptr ds:[esi
+
98
] |
00440EB3
|
56
| push esi |
00440EB4
| E8 B794FFFF | call <polaris_chs.sub_43A370> | sub_43A370
00440EB9
|
83C4
08
| add esp,
8
|
00440EBC
|
8986
9C000000
| mov dword ptr ds:[esi
+
9C
],eax | eax:
"SUB_TITLE.MJO"
00440EC2
|
8B4D
FC | mov ecx,dword ptr ss:[ebp
-
4
] |
00440EC5
|
8BC6
| mov eax,esi | eax:
"SUB_TITLE.MJO"
00440EC7
|
5F
| pop edi |
00440EC8
|
5E
| pop esi |
00440EC9
|
33CD
| xor ecx,ebp |
00440ECB
|
5B
| pop ebx |
00440ECC
| E8
66950400
| call <polaris_chs.sub_48A437> |
00440ED1
|
8BE5
| mov esp,ebp |
00440ED3
|
5D
| pop ebp |
00440ED4
| C3 | ret | load mjo end;
[eax] mjo name , [
4DC350
]
/
/
at
00440ED4
[[eax
+
0x29
*
4
]] decrypted mjo buf,
[eax
+
0x24
*
4
] mjo size
/
/
雨的边界同理,貌似没什么明显的特征,要手动找函数位置
[eax] mjo name , [
4DC350
]
/
/
at
00440ED4
[[eax
+
0x29
*
4
]] decrypted mjo buf,
[eax
+
0x24
*
4
] mjo size
/
/
雨的边界同理,貌似没什么明显的特征,要手动找函数位置
function dump_mjo(mjo_name, dump_dir
=
"./dump/"
)
/
/
to dump decrypted mjo
{
/
/
better to attach process, after initial,
or
access violation
var decrypt_func
=
new NativeFunction(ptr(g_base
+
0x40AB0
),
'pointer'
, [
'pointer'
],
'stdcall'
);
var name_buf
=
Memory.alloc(
256
).writeAnsiString(mjo_name);
var decrypt_ret
=
decrypt_func(name_buf);
let mjo_size
=
decrypt_ret.add(
0x24
*
4
).readU32();
let mjo_buf
=
decrypt_ret.add(
0x29
*
4
).readPointer();
console.log(mjo_name, mjo_buf, mjo_size);
var fp
=
new
File
(dump_dir
+
mjo_name,
"wb"
);
fp.write(mjo_buf.readByteArray(mjo_size));
fp.close()
}
function dump_mjo(mjo_name, dump_dir
=
"./dump/"
)
/
/
to dump decrypted mjo
{
/
/
better to attach process, after initial,
or
access violation
var decrypt_func
=
new NativeFunction(ptr(g_base
+
0x40AB0
),
'pointer'
, [
'pointer'
],
'stdcall'
);
var name_buf
=
Memory.alloc(
256
).writeAnsiString(mjo_name);
var decrypt_ret
=
decrypt_func(name_buf);
let mjo_size
=
decrypt_ret.add(
0x24
*
4
).readU32();
let mjo_buf
=
decrypt_ret.add(
0x29
*
4
).readPointer();
console.log(mjo_name, mjo_buf, mjo_size);
var fp
=
new
File
(dump_dir
+
mjo_name,
"wb"
);
fp.write(mjo_buf.readByteArray(mjo_size));
fp.close()
}
a. showtext_screen
004453E9
|
50
| push eax | [[
5D1A58
]] current text addr
in
mjo
buffer
004453EA
|
53
| push ebx |
004453EB
|
8D85
FCFBFFFF | lea eax,dword ptr ss:[ebp
-
404
] |
004453F1
|
50
| push eax |
004453F2
| FF35 E4CC5200 | push dword ptr ds:[
52CCE4
] |
0052CCE4
:
"煨R"
004453F8
|
68
90065300
| push polaris_chs.
530690
|
004453FD
| E8
1ED4FFFF
| call <polaris_chs.sub_442820> | showtext_screen
b. move to
next
text
0043A750
|
8B15
581A5D00
| mov edx,dword ptr ds:[
5D1A58
] | edx:&
"1"
0043A756
|
8B0A
| mov ecx,dword ptr ds:[edx] | [edx]:
"1"
0043A758
|
0FBF01
| movsx eax,word ptr ds:[ecx] | get_text_len
0043A75B
|
83C1
02
| add ecx,
2
| move to text
0043A75E
|
890A
| mov dword ptr ds:[edx],ecx | [edx]:
"1"
0043A760
| C3 | ret |
c. preshow_text
00445140
|
55
| push ebp |
00445141
|
8BEC
| mov ebp,esp |
00445143
|
81EC
180C0000
| sub esp,C18 |
00445149
| A1
10A04C00
| mov eax,dword ptr ds:[
4CA010
] |
0044514E
|
33C5
| xor eax,ebp |
00445150
|
8945
FC | mov dword ptr ss:[ebp
-
4
],eax |
00445153
|
8B0D
E4CC5200 | mov ecx,dword ptr ds:[
52CCE4
] | [
52cce4
] text
00445159
|
85C9
| test ecx,ecx |
0044515B
|
74
19
| je polaris_chs.
445176
|
0044515D
|
8039
00
|
cmp
byte ptr ds:[ecx],
0
|
00445160
|
75
24
| jne polaris_chs.
445186
|
00445162
| C781
00040000
00
| mov dword ptr ds:[ecx
+
400
],
0
|
0044516C
| C705 E4CC5200
00
| mov dword ptr ds:[
52CCE4
],
0
|
0052CCE4
:
"杼R"
00445176
|
33C0
| xor eax,eax |
00445178
|
8B4D
FC | mov ecx,dword ptr ss:[ebp
-
4
] |
0044517B
|
33CD
| xor ecx,ebp |
0044517D
| E8 B5520400 | call <polaris_chs.sub_48A437> |
00445182
|
8BE5
| mov esp,ebp |
00445184
|
5D
| pop ebp |
00445185
| C3 | ret |
00445186
|
8B15
581A5D00
| mov edx,dword ptr ds:[
5D1A58
] | [[
5D1A58
]] current text addr
in
mjo
buffer
0044518C
| A1
94CB4D00
| mov eax,dword ptr ds:[
4DCB94
] |
00445191
|
53
| push ebx |
00445192
|
33DB
| xor ebx,ebx |
00445194
|
3B02
|
cmp
eax,dword ptr ds:[edx] |
00445196
|
74
1D
| je polaris_chs.
4451B5
|
00445198
|
53
| push ebx | extra always
0
?
00445199
|
51
| push ecx | buf
0044519A
| E8 C1D5FFFF | call <polaris_chs.sub_442760> | show_text
d. memove, copy string to showbuf
00445BA0
| C780 E8D05200
01000000
| mov dword ptr ds:[eax
+
52D0E8
],
1
|
00445BAA
|
8D80
E8CC5200 | lea eax,dword ptr ds:[eax
+
52CCE8
] | eax:L
"簀簀簀簀簀簀簀簀簀"
00445BB0
|
8B35
581A5D00
| mov esi,dword ptr ds:[
5D1A58
] |
5D1A58
, mjo decrypt text(some of)
00445BB6
|
53
| push ebx | size
00445BB7
| A3 E4CC5200 | mov dword ptr ds:[
52CCE4
],eax | write
52cce4
00445BBC
| FF36 | push dword ptr ds:[esi] | src: [esi] mjo decrypt text
00445BBE
|
50
| push eax | dst:
52cce4
, show test
00445BBF
| E8 BC4E0400 | call <polaris_chs.sub_48AA80> | memmove
00445BC4
|
83C4
0C
| add esp,C |
00445BC7
|
011E
| add dword ptr ds:[esi],ebx |
00445BC9
|
5E
| pop esi |
00445BCA
|
5B
| pop ebx |
00445BCB
| C3 | ret |
a. showtext_screen
004453E9
|
50
| push eax | [[
5D1A58
]] current text addr
in
mjo
buffer
004453EA
|
53
| push ebx |
004453EB
|
8D85
FCFBFFFF | lea eax,dword ptr ss:[ebp
-
404
] |
004453F1
|
50
| push eax |
004453F2
| FF35 E4CC5200 | push dword ptr ds:[
52CCE4
] |
0052CCE4
:
"煨R"
004453F8
|
68
90065300
| push polaris_chs.
530690
|
004453FD
| E8
1ED4FFFF
| call <polaris_chs.sub_442820> | showtext_screen
b. move to
next
text
0043A750
|
8B15
581A5D00
| mov edx,dword ptr ds:[
5D1A58
] | edx:&
"1"
0043A756
|
8B0A
| mov ecx,dword ptr ds:[edx] | [edx]:
"1"
0043A758
|
0FBF01
| movsx eax,word ptr ds:[ecx] | get_text_len
0043A75B
|
83C1
02
| add ecx,
2
| move to text
0043A75E
|
890A
| mov dword ptr ds:[edx],ecx | [edx]:
"1"
0043A760
| C3 | ret |
c. preshow_text
00445140
|
55
| push ebp |
00445141
|
8BEC
| mov ebp,esp |
00445143
|
81EC
180C0000
| sub esp,C18 |
00445149
| A1
10A04C00
| mov eax,dword ptr ds:[
4CA010
] |
0044514E
|
33C5
| xor eax,ebp |
00445150
|
8945
FC | mov dword ptr ss:[ebp
-
4
],eax |
00445153
|
8B0D
E4CC5200 | mov ecx,dword ptr ds:[
52CCE4
] | [
52cce4
] text
00445159
|
85C9
| test ecx,ecx |
0044515B
|
74
19
| je polaris_chs.
445176
|
0044515D
|
8039
00
|
cmp
byte ptr ds:[ecx],
0
|
00445160
|
75
24
| jne polaris_chs.
445186
|
00445162
| C781
00040000
00
| mov dword ptr ds:[ecx
+
400
],
0
|
0044516C
| C705 E4CC5200
00
| mov dword ptr ds:[
52CCE4
],
0
|
0052CCE4
:
"杼R"
00445176
|
33C0
| xor eax,eax |
00445178
|
8B4D
FC | mov ecx,dword ptr ss:[ebp
-
4
] |
0044517B
|
33CD
| xor ecx,ebp |
0044517D
| E8 B5520400 | call <polaris_chs.sub_48A437> |
00445182
|
8BE5
| mov esp,ebp |
00445184
|
5D
| pop ebp |
00445185
| C3 | ret |
00445186
|
8B15
581A5D00
| mov edx,dword ptr ds:[
5D1A58
] | [[
5D1A58
]] current text addr
in
mjo
buffer
0044518C
| A1
94CB4D00
| mov eax,dword ptr ds:[
4DCB94
] |
00445191
|
53
| push ebx |
00445192
|
33DB
| xor ebx,ebx |
00445194
|
3B02
|
cmp
eax,dword ptr ds:[edx] |
00445196
|
74
1D
| je polaris_chs.
4451B5
|
00445198
|
53
| push ebx | extra always
0
?
00445199
|
51
| push ecx | buf
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!