近日空闲的时候将Hex Workshop v5升成了v6,结果发现它的右键菜单不支持中文路径,造成桌面上的文件无法用右键菜单打开,很是不方便,于是有了给v6动一下手术的想法。首先用上IDA和OD两个法宝,很快确定问题在以下两处位置的代码:
获取并传递文件名的过程:
.text:004862E0 lea edx, [ebp+824h+var_838]
.text:004862E3 push edx
.text:004862E4 lea ecx, [ebp+824h+var_890] ; 读取右键菜单通过临时文件传入的多字节文件名
.text:004862E7 call ?ReadString@CStdioFile@@UAEHAAV?$CStringT@_WV?$StrTraitMFC@_WV?$ChTraitsCRT@_W@ATL@@@@@ATL@@@Z ; CStdioFile::ReadString(ATL::CStringT<wchar_t,StrTraitMFC<wchar_t,ATL::ChTraitsCRT<wchar_t>>> &)
.text:004862EC cmp eax, ebx
.text:004862EE jz short loc_48632A
.text:004862F0 lea ecx, [ebp+824h+var_838]
.text:004862F3 call sub_476130
.text:004862F8 lea ecx, [ebp+824h+var_838]
.text:004862FB call sub_476220
.text:00486300 mov ecx, [ebp+824h+var_838]
.text:00486303 lea edx, [ebp+824h+var_824]
.text:00486306
.text:00486306 loc_486306: ; CODE XREF: sub_4861D0+145 j
.text:00486306 movzx eax, word ptr [ecx]
.text:00486309 mov [edx], ax
.text:0048630C add ecx, 2
.text:0048630F add edx, 2
.text:00486312 cmp ax, bx
.text:00486315 jnz short loc_486306
.text:00486317 mov eax, [ebp+824h+var_83C]
.text:0048631A push ebx
.text:0048631B push ebx
.text:0048631C push eax
.text:0048631D lea ecx, [ebp+824h+var_824]
.text:00486320 push ecx
.text:00486321 mov ecx, edi
.text:00486323 call sub_484850 ; 传入 UNICODE 格式文件名到打开文件过程
.text:00486328 jmp short loc_4862E0
打开文件的过程,由调用CreateFileW可以知道文件名必须是UNICODE格式。
.text:00548222 loc_548222: ; CODE XREF: sub_548140+C4 j
.text:00548222 push 0 ; hTemplateFile
.text:00548224 push 80h ; dwFlagsAndAttributes
.text:00548229 push 3 ; dwCreationDisposition
.text:0054822B push 0 ; lpSecurityAttributes
.text:0054822D push 3 ; dwShareMode
.text:0054822F push 80000000h ; dwDesiredAccess
.text:00548234 lea ecx, [esp+838h+FileName]
.text:00548238 push ecx ; lpFileName
.text:00548239 call ds:CreateFileW ; 打开 UNICODE 文件名的文件
.text:0054823F cmp eax, 0FFFFFFFFh
.text:00548242 jz short loc_548257
.text:00548244 push eax ; hObject
.text:00548245 call ds:CloseHandle
.text:0054824B mov eax, 1
.text:00548250 add esp, 820h
通过上面的两段代码,可以知道Hex Workshop使用了CStdioFile::ReadString读取临时文件传入的多字节文件路径。CStdioFile读取ANSI文本数据时按char类型读取,在_MSBC下可以直接填充到CString,在UNICODE环境下要先将char转换成宽字符WCHAR,然后再填充到CString,即一个汉字的两个char将变成两个UNICODE字符WCHAR。因此CStudioFile在_MSBC环境下读取任何ANSI文本数据都没问题,在UNICODE环境下读取ANSI文本中的中文时就会显示乱码,但原来的汉字信息并没有丢失。知道了原因就好解决了,在text段发现还有100多字节的空间,于是添加下面的补丁代码:
跳转到补丁代码:
.text:004862E0 loc_4862E0: ; CODE XREF: sub_4861D0+158 j
.text:004862E0 lea edx, [ebp+824h+lpWideCharStr]
.text:004862E3 push edx
.text:004862E4 lea ecx, [ebp+824h+var_890]
.text:004862E7 call ?ReadString@CStdioFile@@UAEHAAV?$CStringT@_WV?$StrTraitMFC@_WV?$ChTraitsCRT@_W@ATL@@@@@ATL@@@Z ; CStdioFile::ReadString(ATL::CStringT<wchar_t,StrTraitMFC<wchar_t,ATL::ChTraitsCRT<wchar_t>>> &)
.text:004862EC cmp eax, ebx
.text:004862EE jmp loc_68AF88 ; 跳转到补丁代码
.text:004862F3 ; ---------------------------------------------------------------------------
.text:004862F3
.text:004862F3 loc_4862F3: ; CODE XREF: sub_4861D0+204E2B j
.text:004862F3 call sub_476130
.text:004862F8 lea ecx, [ebp+824h+lpWideCharStr]
.text:004862FB call sub_476220
.text:00486300 mov ecx, [ebp+824h+lpWideCharStr]
.text:00486303 lea edx, [ebp+824h+var_824]
.text:00486306
.text:00486306 loc_486306: ; CODE XREF: sub_4861D0+145 j
.text:00486306 movzx eax, word ptr [ecx]
.text:00486309 mov [edx], ax
.text:0048630C add ecx, 2
.text:0048630F add edx, 2
.text:00486312 cmp ax, bx
.text:00486315 jnz short loc_486306
.text:00486317 mov eax, [ebp+824h+var_83C]
.text:0048631A push ebx
.text:0048631B push ebx
.text:0048631C push eax
.text:0048631D lea ecx, [ebp+824h+var_824]
.text:00486320 push ecx
.text:00486321 mov ecx, edi
.text:00486323 call sub_484850
补丁代码:
.text:0068AF88 loc_68AF88: ; CODE XREF: sub_4861D0+11E j
.text:0068AF88 jz loc_48632A
.text:0068AF8E push 520h ; uBytes
.text:0068AF93 push 40h ; uFlags
.text:0068AF95 call ds:LocalAlloc ;分配内存
.text:0068AF9B cmp eax, ebx
.text:0068AF9D jz short loc_68AFF8
.text:0068AF9F push eax ;保存分配的内存指针
.text:0068AFA0 mov ecx, [ebp+824h+lpWideCharStr] ;将WCHAR转换成多字节
.text:0068AFA3 mov edx, eax
.text:0068AFA5
.text:0068AFA5 loc_68AFA5: ; CODE XREF: sub_4861D0+204DE3 j
.text:0068AFA5 movzx eax, word ptr [ecx]
.text:0068AFA8 mov [edx], al
.text:0068AFAA add ecx, 2
.text:0068AFAD add edx, 1
.text:0068AFB0 cmp ax, bx
.text:0068AFB3 jnz short loc_68AFA5
.text:0068AFB5 pop eax
.text:0068AFB6 push eax
.text:0068AFB7 sub edx, eax ;计算字符串长度
.text:0068AFB9 push edx ; cchWideChar
.text:0068AFBA mov edx, [ebp+824h+lpWideCharStr]
.text:0068AFBD push edx ; lpWideCharStr
.text:0068AFBE push 0FFFFFFFFh ; cbMultiByte
.text:0068AFC0 push eax ; lpMultiByteStr
.text:0068AFC1 push 0 ; dwFlags
.text:0068AFC3 push 0FDE9h ; CodePage
.text:0068AFC8 call ds:MultiByteToWideChar ;转换成UNICODE到原来的字符串内存池
.text:0068AFCE jmp short loc_68AFF0
.text:0068AFCE ; END OF FUNCTION CHUNK FOR sub_4861D0
.text:0068AFCE ; ---------------------------------------------------------------------------
.text:0068AFD0 dd 8 dup(0)
.text:0068AFF0 ; ---------------------------------------------------------------------------
.text:0068AFF0 ; START OF FUNCTION CHUNK FOR sub_4861D0
.text:0068AFF0
.text:0068AFF0 loc_68AFF0: ; CODE XREF: sub_4861D0+204DFE j
.text:0068AFF0 pop eax
.text:0068AFF1 push eax ; hMem
.text:0068AFF2 call ds:LocalFree ;释放申请的内存
.text:0068AFF8
.text:0068AFF8 loc_68AFF8: ; CODE XREF: sub_4861D0+204DCD j
.text:0068AFF8 lea ecx, [ebp+824h+lpWideCharStr] 恢复原位置代码
.text:0068AFFB jmp loc_4862F3 ;返回原跳转处
.text:0068AFFB ; END OF FUNCTION CHUNK FOR sub_4861D0
.text:0068AFFB _text ends.text:0068AFF0 ; START OF FUNCTION CHUNK FOR sub_4861D0
再用神奇F5看看,相应的代码从:
while ( CStdioFile__ReadString(&v50) )
{
sub_476130(&v50);
sub_476220(&v50);
v5 = v50;
v4 = &v55;
do
{
v6 = *(_WORD *)v5;
*(_WORD *)v4 = *(_WORD *)v5;
v5 += 2;
v4 += 2;
}
while ( v6 );
sub_484850(&v55, v49, 0, 0);
}
变成了:
while ( CStdioFile__ReadString(&lpWideCharStr) )
{
//对应补丁代码
v29 = LocalAlloc(0x40u, 0x520u);
if ( v29 )
{
v34 = (int)v29;
v31 = lpWideCharStr;
v30 = v29;
do
{
v32 = *v31;
*(_BYTE *)v30 = *v31;
++v31;
++v30;
}
while ( v32 );
MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)v34, -1, lpWideCharStr, (int)((char *)v30 - v34));
LocalFree((HLOCAL)v34);
}
sub_476130();
sub_476220(&lpWideCharStr);
v6 = lpWideCharStr;
v5 = &v59;
do
{
v4 = *v6;
*(_WORD *)v5 = *v6;
++v6;
v5 += 2;
}
while ( v4 );
sub_484850(&v59, v53, 0, 0);
}
经测试,在XP SP3平台运行效果良好。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课