更新:
问题变得更加复杂和复杂,因为在stdcall从cdecl函数调用函数后,Hex-Rays 错误地恢复了堆栈:
.text:00403F2F 074 mov edx, gameScreenHeight
.text:00403F35 074 mov ecx, [eax]
.text:00403F37 **074** push 10h
.text:00403F39 078 push edx
.text:00403F3A 07C mov edx, gameScreenWidth
.text:00403F40 07C push edx
.text:00403F41 080 push eax
.text:00403F42 084 mov eax, [ecx+54h]
.text:00403F45 084 call eax
.text:00403F47 **070** test eax, eax
.text:00403F49 070 jz short loc_4
结果,一个带有 4 个参数的函数只有三个:
坏的: lpDD->lpVtbl->SetDisplayMode(lpDD, gameScreenWidth, gameScreenHeight, 16, **a1**)
好的: lpDD->lpVtbl->SetDisplayMode(lpDD, gameScreenWidth, gameScreenHeight, 16)
因此,该函数可能不会使用寄存器值,而只是保存和恢复它,但由于调用后将指针移至堆栈,因此被认为在某些问题调用中使用。这会导致整个调用链被标记为使用该寄存器作为参数。
最糟糕的是,在分支函数中会出现这样的问题,这些函数有多个退出点,并且在每个退出点中堆栈都是平衡的 (000)。我不能在错误调用后更改堆栈指针。我还必须找到另一个电话并平衡所做的更改。xx
最初的问题: 我需要检测并安全地修复错误识别的函数签名。我怎么能做到这一点?
例如,此功能保存游戏设置:
BOOL __thiscall sub_410640(HKEY this)
{
HKEY v1; // ecx
HKEY v2; // ecx
sub_431C00(this, "volumeMaster", *(_DWORD *)&phkResult);
sub_431C00(*(HKEY *)&g_volumeMusic, "volumeMusic", *(_DWORD *)&g_volumeMusic);
sub_431C00(v1, "volumeFX", *(_DWORD *)&g_volumeFX);
sub_431C00(v2, "volumeSpeech", *(_DWORD *)&g_volumeSpeech);
return sub_431C00(*(HKEY *)&dword_4A262C, "volumeMinimum", *(_DWORD *)&dword_4A262C);
}
.text:00410640 sub_410640 proc near ; CODE XREF: PlayVideo+50↑p
.text:00410640 ; sub_416910+1A6↓p
.text:00410640 000 mov eax, phkResult
.text:00410645 000 push eax ; Data
.text:00410646 004 push offset ValueName ; "volumeMaster"
.text:0041064B 008 call sub_431C00
.text:00410650 008 mov ecx, g_volumeMusic
.text:00410656 008 push ecx ; Data
.text:00410657 00C push offset aVolumemusic ; "volumeMusic"
.text:0041065C 010 call sub_431C00
.text:00410661 010 mov edx, g_volumeFX
.text:00410667 010 push edx ; Data
.text:00410668 014 push offset aVolumefx ; "volumeFX"
.text:0041066D 018 call sub_431C00
.text:00410672 018 mov eax, g_volumeSpeech
.text:00410677 018 push eax ; Data
.text:00410678 01C push offset aVolumespeech ; "volumeSpeech"
.text:0041067D 020 call sub_431C00
.text:00410682 020 mov ecx, dword_4A262C
.text:00410688 020 push ecx ; Data
.text:00410689 024 push offset aVolumeminimum ; "volumeMinimum"
.text:0041068E 028 call sub_431C00
.text:00410693 028 add esp, 28h
.text:00410696 000 retn
.text:00410696 sub_410640 endp
如果我们查看里面的函数,我们可以看到通过寄存器传递的参数并没有以任何方式使用。
另外,很明显,对同一个函数的调用应该是统一的,这样的参数传递没有任何意义。
BOOL __usercall sub_431C00@<eax>(HKEY a1@<ecx>, LPCSTR lpValueName, ...)
{
LONG v3; // esi
HKEY phkResult; // [esp+0h] [ebp-4h]
va_list Data; // [esp+Ch] [ebp+8h]
va_start(Data, lpValueName);
phkResult = a1;
if ( RegOpenKeyExA(HKEY_LOCAL_MACHINE, SubKey, 0, 1u, &phkResult) )
return 0;
v3 = RegSetValueExA(phkResult, lpValueName, 0, 4u, (const BYTE *)Data, 4u);
RegCloseKey(phkResult);
return v3 == 0;
}
.text:00410600 sub_410600 proc near ; CODE XREF: sub_4073F0+2B0↑p
.text:00410600 ; WinMain(x,x,x,x)+5F4↓p ...
.text:00410600
.text:00410600 arg_0 = dword ptr 4
.text:00410600
.text:00410600 000 mov eax, [esp+arg_0]
.text:00410604 000 test eax, eax
.text:00410606 000 mov ecx, 1
.text:0041060B 000 mov dword_4AE978, ecx
.text:00410611 000 mov dword_4AF074, eax
.text:00410616 000 jz short locret_410632
.text:00410618 000 mov dword_4A267C, 69h
.text:00410622 000 mov dword_4AE920, 0
.text:0041062C 000 mov dword_4AF03C, ecx
.text:00410632
.text:00410632 locret_410632: ; CODE XREF: sub_410600+16↑j
.text:00410632 000 retn
.text:00410632 sub_410600 endp
现在我需要调整这些函数的声明,使其符合现实。
但是出现了两个问题:
- 如何理解问题所在?
- 如何安全地进行更正,使一个函数中的一个错误不会导致整个应用程序数据库的堆栈不平衡和反编译错误?
预期结果:
BOOL __usercall sub_431C00(LPCSTR lpValueName, _DWORD value);
sub_431C00("volumeMaster", *(_DWORD *)&phkResult);
sub_431C00("volumeMusic", *(_DWORD *)&g_volumeMusic);
sub_431C00("volumeFX", *(_DWORD *)&g_volumeFX);
sub_431C00("volumeSpeech", *(_DWORD *)&g_volumeSpeech);
哦,是的,有趣的是,在这种情况下,IDA 正确定义了函数签名,但由于某种原因,Hex-Rays 炸毁了屋顶:
国际开发协会: int __cdecl sub_431C00(LPCSTR lpValueName, BYTE Data)
六角射线: BOOL __usercall sub_431C00@<eax>(HKEY a1@<ecx>, LPCSTR lpValueName, ...)