日期:
来源:看雪学苑收集编辑:devseed
本文为看雪论坛精华文章
看雪论坛作者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为例,来分析:
一
脚本文件分析与提取
(1) asb文件的分析
int __thiscall sub_43112A(_DWORD *this, char *script_name){char *raw_data; // ediint v4; // eaxunsigned int v5; // ecx_DWORD *v7[4]; // [esp+8h] [ebp-34h] BYREFint v8; // [esp+18h] [ebp-24h] BYREFunsigned 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);// decompressv5 = 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_validBOOL __stdcall sub_430F6A(char *compressed_data, int compressed_size, int raw_size)// decompresssub_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_len004311D7 | 8D4D EC | lea ecx,dword ptr ss:[ebp-14]004311DA | 57 | push edi | raw_data004311DB | FF75 E0 | push dword ptr ss:[ebp-20] | compressed_len004311DE | FF75 F0 | push dword ptr ss:[ebp-10] | compressed_data004311E1 | E8 7F99FDFF| call lamune.40AB65| decompress
(2) asb文件的解密与提取
/*for lamune.exe v1.0open the game to title, thenfrida -l lamune_hook.js -n lamune.exenext go to the prologue to dump all asbs*/function install_decompress_hook(outdir='./dump'){// hook decompress function to dumpconst 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++ contextvar pthis = ptr(0)Interceptor.attach(addr_loadscript, {onEnter: function(args){pthis = ptr(this.context.ecx)}})install_decompress_hook(outdir)// wait for c++ contextwhile(!pthis.toInt32()){Thread.sleep(0.2);}// dump all scriptsvar 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)}
二
动态替换脚本文件
(1) 替换解密的asb缓冲区
/* for hook new decompressed buffer0043119A | FF75 E0 | push dword ptr ss:[ebp-20]0043119D | E8 A1510000 | call lamune.436343 | new004311A2 | FF75 E4 | push dword ptr ss:[ebp-1C] | [ebp-1c] raw_size004311A5 | 8945 F0 | mov dword ptr ss:[ebp-10],eax004311A8 | 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 stubsvoid __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 sizenewrawbuf_hook_end:popad;// fix origin codepush 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_bufpush [ebp+0x8]; // asbnamecall load_rawasb;test eax, eax;je decompress_origin;ret 0x10;decompress_origin:mov eax, 0x99E15CB4; // this is the original corrent crc valuemov dword ptr ds:[0x0047E718], eax; // this is not worked...jmp dword ptr ds:[g_decompressasbo_40AB65];}}// hook install functionsvoid 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 newrawdataBYTE 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 decompressBYTE 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));}
(2) 修改sjis检测字节支持gbk编码
.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
(3) asb opcode分析
// from the file start, there are several opcodes entriesoptype 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 jmp00 00 00 00 FF FF FF FF FF FF FF FF 00 00 00 0000 00 00 00 00 00 00 00 // end with that
三
图片文件的加载和渲染分析
(1) 定位图片显示缓冲区
0019FD80 0040EF75 50 return to lamune.sub_40EE6F+106 from ??? User // CreateDIBinfo0019FDD0 00401E77 0040EE6F 34 return to lamune.sub_401D0F+168 from lamune.sub_40 User0019FE04 0040955D 24 return to lamune.sub_40951B+42 from ??? User0019FE28 0040686C 0040951B 24 return to lamune.sub_406813+59 from lamune.sub_409 User0019FE4C 004383EA 00406813 A4 return to lamune.EntryPoint+184 from lamune.sub_40 User0019FEF0 0043827E 0043F210 84 return to lamune.EntryPoint+18 from lamune.sub_43F UserDWORD __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
(2) cpb图片加载
.1 cpb结构
00000000 cpb1a_header_t struc ; (sizeof=0x20, mappedto_128)00000000 ; XREF: decompresscpb_41E36F/r00000000 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/r0000000A height dw ? ; XREF: decompresscpb_41E36F+3E/r0000000C max_comprlen dd ? ; XREF: decompresscpb_41E36F+56/r00000010 comprlen dd 4 dup(?); XREF: decompresscpb_41E36F+93/r00000010 ; decompresscpb_41E36F+B7/r ...00000020 cpb1a_header_t ends
.2 prepare DC
void *__thiscall sub_40FDC2(void **this, LONG width, int height){void *result; // eaxHBITMAP v5; // eaxHDC dc; // eaxint (__thiscall **v7)(void **, _DWORD); // eaxBITMAPINFO pbmi; // [esp+8h] [ebp-2Ch] BYREFif ( (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 sizepbmi.bmiHeader.biWidth = width;pbmi.bmiHeader.biPlanes = 1; // must be 1pbmi.bmiHeader.biBitCount = 32; // rgbapbmi.bmiHeader.biCompression = 0;v5 = CreateDIBSection(0, &pbmi, 0, this + 0x28, 0, 0); // this+0x28this[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, FillRectthis[0x52] = result;return result;}}(*((void (__thiscall **)(void **))*this + 0xD))(this);}return 0;}
.3 load cpb
int __thiscall loadimg_419E03(_DWORD *this, char *filename, int *a3){int v3; // ebx_DWORD **v5; // edi_DWORD *v7; // esiint v8; // eaxint v9; // eaxint v10[7]; // [esp+Ch] [ebp-2Ch] BYREFchar cpb_header[16]; // [esp+28h] [ebp-10h] BYREFint 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 fpi = 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 headerif ( v3 == 1 ){v8 = *a3; // 0041D3FB, read full headerv9 = 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 decompressif ( 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; // esiint v8; // ebxint v9; // [esp+8h] [ebp-4h]v9 = 0;if ( (*(*a4 + 0x2C))(a4) != 1 ) // 00401291, movreturn 0;v6 = this[*(a3 + 0x18) + 1];if ( !v6 )return 0;if ( (*(*a4 + 0x1C))(a4) == 8 )// 00401278, mov{if ( *(a3 + 4) == 8 ) // 8bit with color panelreturn (*(*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 decompresscpb24if ( v8 != 32 || (*(*a4 + 0x30))(a4) != 1 ) // 00401298, movreturn v9;return (*(*v6 + 0x10))(v6, a2, a4); // 0041E36F,decompresscpb32}return v9;}
.4 decompress cpb
int __thiscall decompresscpb32_41E36F(void *this, int *obj){int v3; // eaxsize_t pixels; // esichar *raw_buf; // eax MAPDSTchar *pchanel1; // ebxint vv2; // eaxchar *pchannel0; // edi_BYTE *v11; // esi_BYTE *v12; // eaxint v13; // edxcpb1a_header_t cpb_header; // [esp+408h] [ebp-58h] BYREFint v16; // [esp+428h] [ebp-38h]int v17; // [esp+42Ch] [ebp-34h]char *v18; // [esp+430h] [ebp-30h]int width; // [esp+434h] [ebp-2Ch] MAPDSTchar *compressed_buf; // [esp+438h] [ebp-28h] MAPDSTint 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] MAPDSTchar *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, movcompressed_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; // ebxchar *v6; // edxchar *v7; // esichar *v8; // ediunsigned int v9; // ecxsigned int v10; // eaxunsigned int v11; // ecxchar *v12; // esichar v13; // cfbool v14; // ccunsigned 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;}
.5 bitblt screen dc
// for bitbltBOOL __thiscall sub_4123E1(void *this, int x, int y, int a4, int x1, int a6, int a7, int a8, DWORD rop){int v10; // ediint v11; // ebxint v12; // eaxint y1; // ediint v14; // eaxint x_c; // ebxHDC hdc; // eaxHDC 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, movhdc = (*(*this + 4))(this);// 0040124E, movreturn BitBlt(hdc, x, y, x_c, a8 - y1 + 1, srchdc, x1, y1, rop);}return 0;
四
动态替换图片文件
/* for hook decompressed cpb24 buffer0041E2DB | 8B55 0C | mov edx,dword ptr ss:[ebp+C]0041E2DE | 8BC7 | mov eax,edi0041E2E0 | 2BC6 | sub eax,esi0041E2E2 | 42 | inc edx0041E2E3 | 8955 0C | mov dword ptr ss:[ebp+C],edx0041E2E6 | 894D EC | mov dword ptr ss:[ebp-14],ecx0041E2E9 | 85DB | test ebx,ebx0041E2EB | 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 eaxmov g_curcpbname, eax;pop eax;// fix origin codepush 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 codemov edx,dword ptr [ebp+0xC];mov eax,edi;jmp dword ptr ds:[g_copycpb24o_41E2E0];}}void install_cpbhook(){// inlinehook loadcpbBYTE 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
球分享
球点赞
球在看
点击“阅读原文”,了解更多!