本文为看雪论坛精华文章
看雪论坛作者ID:devseed
本贴代码开源详见我的github: GalgameReverse(https://github.com/YuriSizuku/GalgameReverse), ReverseUtil(https://github.com/YuriSizuku/ReverseUtil)。
上篇链接:Galgame汉化中的逆向:动态汉化分析-以MAJIROv3引擎为例(https://bbs.kanxue.com/thread-268508.htm)
0
前言
上节 Galgame汉化中的逆向(六):动态汉化分析_以MAJIROv3引擎为例,我们介绍了动态汉化。动态汉化不用分析封包结构,不用分析opcode,看上去很方便,但是动态汉化解决同步问题会很麻烦,比如说改完文本后backlog文本仍是日文、返回主界面再载入文本没有变动等问题。动态汉化也有可能出现莫名其妙的崩溃bug,且这些bug不容易被调试。
针对动态汉化的上述缺点,本节我们将介绍一种这种半动态汉化的方案。与上节的方法不同,本节不进行文本级替换,而是文件级别的替换。即去hook相关函数,动态将解密后的缓冲区替换为我们汉化后的文件。适合于那种封包与加密特别麻烦或复杂的游戏。
本文将以azsystem为例,来分析:
一
脚本文件分析与提取
int __thiscall sub_43112A(_DWORD *this, char *script_name)
{
char *raw_data; // edi
int v4; // eax
unsigned int v5; // ecx
_DWORD *v7[4]; // [esp+8h] [ebp-34h] BYREF
int v8; // [esp+18h] [ebp-24h] BYREF
unsigned int compressed_size; // [esp+1Ch] [ebp-20h]
unsigned int raw_size; // [esp+20h] [ebp-1Ch]
int v11; // [esp+24h] [ebp-18h]
int (__thiscall **v12)(void *, char); // [esp+28h] [ebp-14h]
char *compressed_data; // [esp+2Ch] [ebp-10h]
int v14; // [esp+38h] [ebp-4h]
v7[0] = off_460A6C;
sub_40BD95(v7);
v14 = 1;
v12 = &off_462CDC;
v11 = 0;
sub_430FC9((int)this);
if ( fopen_40C102(v7, script_name, 0x80000000) != 1 )
{
logprintf_407C41("CScript::Create", byte_4679CC, script_name);
goto LABEL_13;
}
readfile_40C03E(v7, (char *)&v8, 0xC);
if ( v8 == 0x1A425341 ) // asb\x1a
{
compressed_data = (char *)operator new(compressed_size);
raw_data = (char *)operator new(raw_size);
readfile_40C03E(v7, compressed_data, compressed_size);
if ( sub_430F6A(compressed_data, compressed_size, raw_size) )
{
v4 = decompress_40AB65(compressed_data, compressed_size, raw_data, raw_size);// decompress
v5 = raw_size;
if ( v4 == raw_size )
{
this[4] = 0;
this[1] = raw_data;
this[2] = v5;
this[3] = raw_data;
this[5] = raw_data;
v11 = 1;
LABEL_10:
if ( compressed_data )
j__free(compressed_data);
goto LABEL_13;
}
logprintf_407C41("CScript::Create", byte_467A38, script_name);
}
else
{
logprintf_407C41("CScript::Create", byte_467A0C, script_name);// error
}
if ( raw_data )
j__free(raw_data);
goto LABEL_10;
}
LABEL_13:
v14 = -1;
v12 = &off_462CDC;
v7[0] = off_460A6C;
sub_40BFDD(v7);
return v11;
}
typedef struct {
s8 magic[4]; /* "ASB" */
u32 comprlen;
u32 uncomprlen;
u32 unknown;
} asb_header_t;
typedef struct {
s8 magic[4]; /* "ASB\x1a" 通过此magic来定位*/
u32 comprlen;
u32 uncomprlen;
} asb1a_header_t;
// CScript.constructor, 这里不再自己构造了,在游戏调用的时候记录下this指针
void *__thiscall sub_43277F(_DWORD *this)
// check_valid
BOOL __stdcall sub_430F6A(char *compressed_data, int compressed_size, int raw_size)
// decompress
sub_40AB65(char *compressed_data, int compressed_len, char *raw_data, int raw_len)
0043112A | B8 9EE54500 | mov eax,lamune.45E59E |load_script(char* name)
004311D4 | FF75 E4 | push dword ptr ss:[ebp-1C] | raw_len
004311D7 | 8D4D EC | lea ecx,dword ptr ss:[ebp-14]
004311DA | 57 | push edi | raw_data
004311DB | FF75 E0 | push dword ptr ss:[ebp-20] | compressed_len
004311DE | FF75 F0 | push dword ptr ss:[ebp-10] | compressed_data
004311E1 | E8 7F99FDFF| call lamune.40AB65| decompress
/*
for lamune.exe v1.0
open the game to title, then
frida -l lamune_hook.js -n lamune.exe
next go to the prologue to dump all asbs
*/
function install_decompress_hook(outdir='./dump')
{
// hook decompress function to dump
const addr_decompress = ptr(0x40AB65);
var raw_asbname = "";
var raw_asbdata = ptr(0);
var raw_asbsize = 0;
Interceptor.attach(addr_decompress, {
onEnter: function(args)
{
raw_asbdata = ptr(args[2]);
raw_asbsize = args[3].toUInt32();
raw_asbname = ptr(this.context.ebp).add(8).
readPointer().readAnsiString();
},
onLeave: function(retval)
{
//var asbname = asbname_buf.readAnsiString();
var asbname = raw_asbname;
console.log(asbname,
", raw_asbdata addr at", raw_asbdata,
", raw_asbsize ", raw_asbsize)
try{
var fp = new File(outdir+"/"+asbname, 'wb');
fp.write(raw_asbdata.readByteArray(raw_asbsize));
fp.close();
}
catch(e)
{
console.log("file error!", e);
}
}
})
}
function dump_asbs(names, outdir="./dump")
{
const addr_loadscript = ptr(0x43112A);
const load_script = new NativeFunction(addr_loadscript,
'void', ['pointer', "pointer"], 'thiscall');
console.log("load_script at:", load_script)
// use this to store c++ context
var pthis = ptr(0)
Interceptor.attach(addr_loadscript, {
onEnter: function(args)
{
pthis = ptr(this.context.ecx)
}
})
install_decompress_hook(outdir)
// wait for c++ context
while(!pthis.toInt32())
{
Thread.sleep(0.2);
}
// dump all scripts
var name_buf = Memory.alloc(0x100);
for(var i=0;i<names.length;i++)
{
console.log("try to dump", names[i], ", this=",pthis);
name_buf.writeAnsiString(names[i]);
load_script(pthis, name_buf);
}
console.log("dump asbs finished!\n");
}
function dump_scenario()
{
var names_v103 = ["00suzuk.asb"]
dump_asbs(names_v103)
}
二
动态替换脚本文件
/* for hook new decompressed buffer
0043119A | FF75 E0 | push dword ptr ss:[ebp-20]
0043119D | E8 A1510000 | call lamune.436343 | new
004311A2 | FF75 E4 | push dword ptr ss:[ebp-1C] | [ebp-1c] raw_size
004311A5 | 8945 F0 | mov dword ptr ss:[ebp-10],eax
004311A8 | E8 96510000 | call lamune.436343 | new raw_buf
*/
const DWORD g_newrawbufi_4311A2 = 0x4311A2;
const DWORD g_newrawbufo_4311A8 = 0x4311A8;
/* for hook decompress asb
.text:004311D4 FF 75 E4 push [ebp+raw_size] ; raw_len
.text:004311D7 8D 4D EC lea ecx, [ebp+var_14]
.text:004311DA 57 push edi ; raw_data
.text:004311DB FF 75 E0 push [ebp+compressed_size] ; compressed_len
.text:004311DE FF 75 F0 push [ebp+compressed_data] ; compressed_data
.text:004311E1 E8 7F 99 FD FF call decompress_40AB65
*/
const DWORD g_decompressasbi_4311E1 = 0x4311E1;
const DWORD g_decompressasbo_40AB65 = 0x40AB65;
// inlinehook stubs
void __declspec(naked) newrawbuf_hook_4311A2()
{
__asm{
pushad;
xor eax, eax;
// size_t __stdcall load_rawasb(char *name, PBYTE buf)
push eax;
push [ebp+8];
call load_rawasb;
test eax, eax;
je newrawbuf_hook_end;
mov [ebp-0x1c], eax; // change raw buf size
newrawbuf_hook_end:
popad;
// fix origin code
push dword ptr [ebp-0x1c];
mov dword ptr [ebp-0x10], eax;
jmp dword ptr ds:[g_newrawbufo_4311A8];
}
}
void __declspec(naked) decompressasb_hook_4311E1()
{
//sub_40AB65(char *compressed_data, int compressed_len, char *raw_data, int raw_len)
__asm {
push [esp+0xc]; // after push ret addr, above, raw_buf
push [ebp+0x8]; // asbname
call load_rawasb;
test eax, eax;
je decompress_origin;
ret 0x10;
decompress_origin:
mov eax, 0x99E15CB4; // this is the original corrent crc value
mov dword ptr ds:[0x0047E718], eax; // this is not worked...
jmp dword ptr ds:[g_decompressasbo_40AB65];
}
}
// hook install functions
void install_asbhook()
{
/* inlinehook check_valid
.text:0040AB8A 6A 00 push 0
.text:0040AB8C 8D 43 FC lea eax, [ebx-4]
.text:0040AB8F 50 push eax
.text:0040AB90 8D 77 04 lea esi, [edi+4]
.text:0040AB93 56 push esi
.text:0040AB94 E8 27 D9 FF FF call makecrc_4084C0
.text:0040AB99 83 C4 0C add esp, 0Ch
.text:0040AB9C 39 07 cmp [edi], eax
.text:0040AB9E 75 64 jnz short loc_40AC04
*/
BYTE nop2[0x2]={0x90, 0x90};
winhook_patchmemory((LPVOID)0x4311d2,
nop2, sizeof(nop2));
winhook_patchmemory((LPVOID)0x40AB9E,
nop2, sizeof(nop2));
// inlinehook newrawdata
BYTE jmpE8buf[0x5]={0xE9}; // jmp relative
*(DWORD*)(jmpE8buf+1) = (DWORD)newrawbuf_hook_4311A2-
((DWORD)g_newrawbufi_4311A2 + sizeof(jmpE8buf));
winhook_patchmemory((LPVOID)g_newrawbufi_4311A2,
jmpE8buf, sizeof(jmpE8buf));
// inlinehook decompress
BYTE callE9buf[0x5]={0xE8}; // call relative
*(DWORD*)(callE9buf+1) =(DWORD)decompressasb_hook_4311E1-
((DWORD)g_decompressasbi_4311E1 + sizeof(jmpE8buf));
winhook_patchmemory((LPVOID)g_decompressasbi_4311E1,
callE9buf, sizeof(callE9buf));
}
.text:004340F6 loc_4340F6:
.text:004340F6 mov ecx, [ebp+74h+var_4]
.text:004340F9 mov cl, [ecx]
.text:004340FB mov dl, cl
.text:004340FD xor dl, 20h
.text:00434100 add dl, 5Fh ; '_'
.text:00434103 cmp dl, 3Bh ; ';'
.text:00434106 ja loc_434215
// from the file start, there are several opcodes entries
optype 4, oplengh 4, payload n
[26|27 00 00 00], oplengh 4, [00]*0x10, optext // 26 music, 27 text
[0d 00 00 00], oplengh 4, [00]*4, option_num 4, [00] * 8, text1, 00, text2 ... // option
[0a 00 00 00], [18 00 00 00] , addr 4, [00]*4, unknow1 4, unknow2 4 // jmp
[0b 00 00 00], [1c 00 00 00] , addr 4, [00]*4, unknow1 4, unknow2 4 // option jmp
00 00 00 00 FF FF FF FF FF FF FF FF 00 00 00 00
00 00 00 00 00 00 00 00 // end with that
三
图片文件的加载和渲染分析
0019FD80 0040EF75 50 return to lamune.sub_40EE6F+106 from ??? User // CreateDIBinfo
0019FDD0 00401E77 0040EE6F 34 return to lamune.sub_401D0F+168 from lamune.sub_40 User
0019FE04 0040955D 24 return to lamune.sub_40951B+42 from ??? User
0019FE28 0040686C 0040951B 24 return to lamune.sub_406813+59 from lamune.sub_409 User
0019FE4C 004383EA 00406813 A4 return to lamune.EntryPoint+184 from lamune.sub_40 User
0019FEF0 0043827E 0043F210 84 return to lamune.EntryPoint+18 from lamune.sub_43F User
DWORD __thiscall sub_42A199(int *this) // neko_logo.cpb
| loadimg_419E03(off_473088, "neko_logo.cpb", this + 0x214);
| readcpb_40C03E((_DWORD **)this + 1, cpb_header, 0x10);
| (*(_DWORD *)*v7+4))(*v7, cpb_header) //check magic cpb\x1a
| (*(v8 + 0x3C))(v10[4], v10[5]) // 0041D3FB, read full
| v3=(*(**v7+12))(*v7, v5, v10,a3);// 0041D453, check depth
|return (*(*v6 + 0x10))(v6, a2, a4);// 0041E36F, 0041ddb8,decompress
|decompress2_40AA38(char *compressed_buf, size_t |compressed_size, char *raw_buf, size_t raw_len) // lzss?
| sub_40C9C1(DWORD *this, int a2, int a3, int *a4, DWORD *a5)
|sub_4101EB(v9 + 2, a2, a3, a4, *a5, a5[1], a5[2], a5[3], 0);// bltalpha
|(*(this[2] + 0x48))(this + 2, a2, a3, a4, *a5, a5[1], a5[2], a5[3], 0xCC0020);// 004123E1, to bitblt
00000000 cpb1a_header_t struc ; (sizeof=0x20, mappedto_128)
00000000 ; XREF: decompresscpb_41E36F/r
00000000 magic db 4 dup(?) ; string(C)
00000004 unknow1 db ?
00000005 color_depth db ?
00000006 unknow2 db ?
00000007 version db ?
00000008 width dw ? ; XREF: decompresscpb_41E36F+39/r
0000000A height dw ? ; XREF: decompresscpb_41E36F+3E/r
0000000C max_comprlen dd ? ; XREF: decompresscpb_41E36F+56/r
00000010 comprlen dd 4 dup(?); XREF: decompresscpb_41E36F+93/r
00000010 ; decompresscpb_41E36F+B7/r ...
00000020 cpb1a_header_t ends
void *__thiscall sub_40FDC2(void **this, LONG width, int height)
{
void *result; // eax
HBITMAP v5; // eax
HDC dc; // eax
int (__thiscall **v7)(void **, _DWORD); // eax
BITMAPINFO pbmi; // [esp+8h] [ebp-2Ch] BYREF
if ( (void *)width == this[41] && (void *)height == this[42] )
return (void *)(*((int (__thiscall **)(void **, _DWORD))*this + 26))(this, 0);
(*((void (__thiscall **)(void **))*this + 13))(this);
if ( width > 0 && height > 0 )
{
memset(&pbmi, 0, sizeof(pbmi));
pbmi.bmiHeader.biHeight = -height;
pbmi.bmiHeader.biSize = 0x28;// struct size
pbmi.bmiHeader.biWidth = width;
pbmi.bmiHeader.biPlanes = 1; // must be 1
pbmi.bmiHeader.biBitCount = 32; // rgba
pbmi.bmiHeader.biCompression = 0;
v5 = CreateDIBSection(0, &pbmi, 0, this + 0x28, 0, 0); // this+0x28
this[37] = v5;
if ( v5 )
{
dc = CreateCompatibleDC(0);
this[0x27] = dc;
if ( dc )
{
this[0x26] = SelectObject(dc, this[0x25]);
this[0x29] = (void *)width;
this[0x2A] = (void *)height;
this[0x2E] = (void *)(height - 1);
v7 = (int (__thiscall **)(void **, _DWORD))*this;
this[0x2B] = 0;
this[0x2C] = 0;
this[0x2D] = (void *)(width - 1);
result = (void *)v7[0x1A](this, 0); // 0041295A, FillRect
this[0x52] = result;
return result;
}
}
(*((void (__thiscall **)(void **))*this + 0xD))(this);
}
return 0;
}
int __thiscall loadimg_419E03(_DWORD *this, char *filename, int *a3)
{
int v3; // ebx
_DWORD **v5; // edi
_DWORD *v7; // esi
int v8; // eax
int v9; // eax
int v10[7]; // [esp+Ch] [ebp-2Ch] BYREF
char cpb_header[16]; // [esp+28h] [ebp-10h] BYREF
int i; // [esp+40h] [ebp+8h]
v3 = 0;
if ( !filename || !a3 )
return 0;
v5 = (this + 1);
if ( fopen_40C102(this + 1, filename, 0x80000000) != 1 )
{
logprintf_407C41("CGraphicLoader::GDILoad", "指定されたファイルが見つかりません [%s]", filename);
return 0;
}
readcpb_40C03E(this + 1, cpb_header, 0x10); // this+1 fp
i = 0;
v7 = this + 5; // for test magic?
do
{
if ( *v7 )
{
if ( (*(**v7 + 4))(*v7, cpb_header) == 1 )// 0041D0E8, 0041D3E9
// check magic cpb\x1a,
{
sub_40C0A0(v5, 0, 0);
memset(v10, 0, sizeof(v10));
v3 = (*(**v7 + 8))(*v7, v5, v10); // 0041D3FB, read full header
if ( v3 == 1 )
{
v8 = *a3; // 0041D3FB, read full header
v9 = v10[3] == 1 ? (*(v8 + 0x3C))(v10[4], v10[5]) : (*(v8 + 0x38))(v10[4], v10[5]);
v3 = v9;
if ( v9 == 1 )
{
sub_40C0A0(v5, 0, 0);
v3 = (*(**v7 + 12))(*v7, v5, v10, a3);// 0041D453, check depth and decompress
if ( v3 == 1 )
break;
}
}
}
}
++i;
++v7;
}
while ( i < 4 );
sub_40BFDD(v5);
return v3;
}
int __thiscall sub_41D453(_DWORD *this, int a2, int a3, int a4)
{
int v6; // esi
int v8; // ebx
int v9; // [esp+8h] [ebp-4h]
v9 = 0;
if ( (*(*a4 + 0x2C))(a4) != 1 ) // 00401291, mov
return 0;
v6 = this[*(a3 + 0x18) + 1];
if ( !v6 )
return 0;
if ( (*(*a4 + 0x1C))(a4) == 8 )// 00401278, mov
{
if ( *(a3 + 4) == 8 ) // 8bit with color panel
return (*(*v6 + 4))(v6, a2, a4);
}
else if ( (*(*a4 + 0x1C))(a4) == 32 ) // 32bit rgba
{
v8 = *(a3 + 4);
if ( v8 == 8 )
return (*(*v6 + 8))(v6, a2, a4);
if ( v8 == 24 )
return (*(*v6 + 0xC))(v6, a2, a4); // 0041e1c8 decompresscpb24
if ( v8 != 32 || (*(*a4 + 0x30))(a4) != 1 ) // 00401298, mov
return v9;
return (*(*v6 + 0x10))(v6, a2, a4); // 0041E36F,decompresscpb32
}
return v9;
}
int __thiscall decompresscpb32_41E36F(void *this, int *obj)
{
int v3; // eax
size_t pixels; // esi
char *raw_buf; // eax MAPDST
char *pchanel1; // ebx
int vv2; // eax
char *pchannel0; // edi
_BYTE *v11; // esi
_BYTE *v12; // eax
int v13; // edx
cpb1a_header_t cpb_header; // [esp+408h] [ebp-58h] BYREF
int v16; // [esp+428h] [ebp-38h]
int v17; // [esp+42Ch] [ebp-34h]
char *v18; // [esp+430h] [ebp-30h]
int width; // [esp+434h] [ebp-2Ch] MAPDST
char *compressed_buf; // [esp+438h] [ebp-28h] MAPDST
int pcurvv2; // [esp+43Ch] [ebp-24h]
int i; // [esp+444h] [ebp-1Ch]
char *pchanel3; // [esp+448h] [ebp-18h]
_BYTE *vv1; // [esp+44Ch] [ebp-14h]
int j; // [esp+450h] [ebp-10h]
int v27; // [esp+45Ch] [ebp-4h]
char *obja; // [esp+46Ch] [ebp+Ch] MAPDST
char *pchanel2; // [esp+46Ch] [ebp+Ch]
v27 = 0;
j = 0;
if ( readcpb_40C03E(obj, cpb_header.magic, 0x20) )
{
v3 = *obja;
width = cpb_header.width;
i = cpb_header.height;
pixels = cpb_header.width * cpb_header.height;
v17 = (*(v3 + 0x24))(obja); // 00401283, mov
compressed_buf = operator new(cpb_header.max_comprlen);
raw_buf = operator new(4 * pixels);
pchanel1 = &raw_buf[pixels];
pchanel2 = &raw_buf[pixels + pixels];
pchanel3 = &pchanel2[pixels];
vv1 = (*(*obja + 0xC))(obja); // 0040125C, mov this+0x28, get hdc buffer
// CreateDIBSection(0, &pbmi, 0, this + 0x28, 0, 0);
vv2 = (*(*obja + 0x20))(obja);
pcurvv2 = vv2;
if ( readcpb_40C03E(obj, compressed_buf, cpb_header.comprlen[0])
&& decompress_channel_40AA38(compressed_buf, cpb_header.comprlen[0], raw_buf, pixels) != -1
&& readcpb_40C03E(obj, compressed_buf, cpb_header.comprlen[1])
&& decompress_channel_40AA38(compressed_buf, cpb_header.comprlen[1], pchanel1, pixels) != -1
&& readcpb_40C03E(obj, compressed_buf, cpb_header.comprlen[2])
&& decompress_channel_40AA38(compressed_buf, cpb_header.comprlen[2], pchanel2, pixels) != -1
&& readcpb_40C03E(obj, compressed_buf, cpb_header.comprlen[3])
&& decompress_channel_40AA38(compressed_buf, cpb_header.comprlen[3], pchanel3, pixels) != -1 )
{
if ( i > 0 )
{
pchannel0 = &pchanel1[-pixels];
++vv1;
j = i;
do // copy data to dc buf
{
if ( width > 0 )
{
v11 = vv1;
v16 = pchanel2 - pchanel1;
v18 = (pchanel3 - pchanel1);
v12 = pchanel1;
v13 = pcurvv2 - pchanel1;
i = width;
do
{
v11[1] = v12[pchannel0 - pchanel1];
*v11 = *v12;
*(v11 - 1) = v12[v16];
v12[v13] = v12[v18];
++v12;
v11 += 4;
--i;
}
while ( i );
}
pchanel2 += width;
pchanel3 += width;
vv1 += 4 * width;
pcurvv2 += v17;
pchanel1 += width;
pchannel0 += width;
--j;
}
while ( j );
}
j = 1;
}
if ( raw_buf )
j__free(raw_buf);
if ( compressed_buf )
j__free(compressed_buf);
}
return j;
}
int __stdcall decompress_channel_40AA38(char *compressed_buf, size_t compressed_size, char *raw_buf, size_t raw_len)
{
char *v5; // ebx
char *v6; // edx
char *v7; // esi
char *v8; // edi
unsigned int v9; // ecx
signed int v10; // eax
unsigned int v11; // ecx
char *v12; // esi
char v13; // cf
bool v14; // cc
unsigned int v15; // [esp+Ch] [ebp-4h]
signed int dstsizea; // [esp+24h] [ebp+14h]
if ( *(compressed_buf + 4) > raw_len )
return -1;
v5 = compressed_buf + 20;
v6 = &compressed_buf[*(compressed_buf + 1) + 20];
v7 = &v6[*(compressed_buf + 2)];
dstsizea = *(compressed_buf + 4);
v8 = raw_buf;
v15 = 0x80808080;
do
{
if ( (v15 & *v5) != 0 )
{
v9 = *v6;
v6 += 2;
v10 = (v9 >> 13) + 3;
qmemcpy(v8, &v8[-(v9 & 0x1FFF) - 1], v10);
v8 += v10;
}
else
{
v11 = *v7 + 1;
v12 = v7 + 1;
v10 = v11;
qmemcpy(v8, v12, v11);
v7 = &v12[v11];
v8 += v11;
}
v13 = v15 & 1;
v15 = __ROR4__(v15, 1);
if ( v13 )
++v5;
v14 = dstsizea <= v10;
dstsizea -= v10;
}
while ( !v14 );
return v8 - raw_buf;
}
// for bitblt
BOOL __thiscall sub_4123E1(void *this, int x, int y, int a4, int x1, int a6, int a7, int a8, DWORD rop)
{
int v10; // edi
int v11; // ebx
int v12; // eax
int y1; // edi
int v14; // eax
int x_c; // ebx
HDC hdc; // eax
HDC srchdc; // [esp-10h] [ebp-20h]
v10 = a7 - x1 + 1;
v11 = a8 - a6 + 1;
if ( x >= 0 )
{
if ( x + v10 > (*(*this + 16))(this) )// 00401263, mov
{
if ( (*(*this + 16))(this) - x <= 0 )
return 0;
a7 = (*(*this + 16))(this) + x1 - x - 1;
}
}
else
{
if ( v10 + x <= 0 )
return 0;
x1 -= x;
v12 = (*(*this + 16))(this) + x1 - 1;
if ( v12 < a7 )
a7 = v12;
x = 0;
}
if ( y >= 0 )
{
if ( y + v11 > (*(*this + 20))(this) )// 0040126A, mov
{
if ( (*(*this + 20))(this) - y <= 0 )
return 0;
a8 = (*(*this + 20))(this) + a6 - y - 1;
}
y1 = a6;
}
else
{
if ( v11 + y <= 0 )
return 0;
y1 = a6 - y;
v14 = (*(*this + 20))(this) + a6 - 1 - y;
if ( v14 < a8 )
a8 = v14;
y = 0;
}
x_c = a7 - x1 + 1;
if ( x_c > 0 && a8 - y1 + 1 > 0 )
{
srchdc = (*(*a4 + 4))(a4);// 0040124E, mov
hdc = (*(*this + 4))(this);// 0040124E, mov
return BitBlt(hdc, x, y, x_c, a8 - y1 + 1, srchdc, x1, y1, rop);
}
return 0;
四
动态替换图片文件
/* for hook decompressed cpb24 buffer
0041E2DB | 8B55 0C | mov edx,dword ptr ss:[ebp+C]
0041E2DE | 8BC7 | mov eax,edi
0041E2E0 | 2BC6 | sub eax,esi
0041E2E2 | 42 | inc edx
0041E2E3 | 8955 0C | mov dword ptr ss:[ebp+C],edx
0041E2E6 | 894D EC | mov dword ptr ss:[ebp-14],ecx
0041E2E9 | 85DB | test ebx,ebx
0041E2EB | 7E 35 | jle lamune_chs.41E322
*/
const char* g_curcpbname = NULL;
const DWORD g_copycpb24i_41E2DB = 0x41E2DB;
const DWORD g_copycpb24o_41E2E0 = 0x41E2E0;
void __declspec(naked) loadcpb_hook_419E03()
{
__asm {
push eax;
mov eax, dword ptr [esp+8]; // after push eax
mov g_curcpbname, eax;
pop eax;
// fix origin code
push ebp;
mov ebp, esp;
sub esp, 0x2c;
jmp dword ptr ds:[g_loadcpbo_419E09];
}
}
void __declspec(naked) copycpb24_hook_41E2DB()
{
__asm {
pushad;
push [ebp-0x20];
push g_curcpbname;
// size_t __stdcall load_rawcpb(char *name, PBYTE buf)
call load_rawcpb;
popad;
// fix origin code
mov edx,dword ptr [ebp+0xC];
mov eax,edi;
jmp dword ptr ds:[g_copycpb24o_41E2E0];
}
}
void install_cpbhook()
{
// inlinehook loadcpb
BYTE jmpE8buf[0x5]={0xE9}; // jmp relative
*(DWORD*)(jmpE8buf+1) = (DWORD)loadcpb_hook_419E03-
((DWORD)g_loadcpbi_419E03 + sizeof(jmpE8buf));
winhook_patchmemory((LPVOID)g_loadcpbi_419E03,
jmpE8buf, sizeof(jmpE8buf));
// inlinehook copycpb24
*(DWORD*)(jmpE8buf+1) = (DWORD)copycpb24_hook_41E2DB-
((DWORD)g_copycpb24i_41E2DB + sizeof(jmpE8buf));
winhook_patchmemory((LPVOID)g_copycpb24i_41E2DB,
jmpE8buf, sizeof(jmpE8buf));
}
size_t __stdcall load_rawcpb(char *name, PBYTE buf)
{
char path[MAX_PATH] = {SYSGRAPH_DIR "/" "\0"};
strcat(path, name);
strcpy(path + strlen(path)-
strlen(SYSGRAPH_EXT),SYSGRAPH_EXT);
int width, height, channel;
printf("load_rawcpb(%s, %p)", path, buf);
size_t entry_size = load_arc_entry(path, NULL);
const BYTE *tmpbuf = (BYTE*)malloc(entry_size);
load_arc_entry(path, (PBYTE)tmpbuf);
char* img = (char*)stbi_load_from_memory(tmpbuf,
entry_size, &width, &height, &channel, 0);
free((void*)tmpbuf);
if(!img)
{
printf(" not found!\n");
return 0;
}
printf(" width=%d, heigth=%d, channel=%d\n",
width, height, channel);
for(int y=0;y<height;y++)
{
for(int x=0;x<width;x++)
{
char r = *(img + channel * (width*y + x) + 0);
char g = *(img + channel * (width*y + x) + 1);
char b = *(img + channel * (width*y + x) + 2);
*(buf + 0*height*width + width*y+x) = r;
*(buf + 1*height*width + width*y+x) = g;
*(buf + 2*height*width + width*y+x) = b;
if(channel==4)
{
char a = *(img + channel * (width*y + x) + 3);
*(buf + 3*height*width + width*y+x) = a;
}
}
}
stbi_image_free(img);
return width*height*channel;
}
五
后记
看雪ID:devseed
https://bbs.kanxue.com/user-home-617776.htm
# 往期推荐
2、源代码静态分析方法——代码属性图Code Property Graphs
球分享
球点赞
球在看
点击“阅读原文”,了解更多!