我愿意在一个小型3D游戏上修改相机坐标。我已经找到了三个功能,每个轴一个。我们称它们为CameraX,CameraY和CameraZ。当我发现我遗漏了某些东西时,我只与第一个一起工作。
这里是ASM指令,来自Ghidra:
*************************************************************
* FUNCTION
*************************************************************
undefined1 __register CameraX (undefined2 x)
undefined1 AL:1 <RETURN>
undefined2 AX:2 x
undefined1 Stack[-0x14] local_14 XREF[8]: 00478abe (*) ,
00478aca (*) ,
00478b44 (*) ,
00478b53 (*) ,
00478bb3 (*) ,
00478bbf (*) ,
00478c1d (*) ,
00478c29 (*)
undefined4 Stack[-0x18] local_18 XREF[4]: 00478ae8 (R) ,
00478b6e (R) ,
00478bdd (R) ,
00478c47 (R)
undefined4 Stack[-0x1c] local_1c XREF[4]: 00478ae1 (R) ,
00478b67 (R) ,
00478bd6 (R) ,
00478c40 (R)
undefined4 Stack[-0x20] local_20 XREF[8]: 00478ace (*) ,
00478ada (R) ,
00478b57 (*) ,
00478b60 (R) ,
00478bc3 (*) ,
00478bcf (R) ,
00478c2d (*) ,
00478c39 (R)
undefined8 Stack[-0x28] local_28 XREF[4,2]: 004789ec (*) ,
004789f5 (*) ,
00478b06 (*) ,
00478b0f (*) ,
004789f1 (W) ,
00478b0b (W)
undefined4 Stack[-0x2c] local_2c XREF[1]: 00478b40 (*)
CameraX XREF[1]: FUN_0047a280:0047a291 (c)
004789e0 53 PUSH EBX
004789e1 56 PUSH ESI
004789e2 83 c4 e0 ADD ESP ,-0x20
004789e5 8b f0 MOV ESI ,x
004789e7 e8 68 9b CALL FUN_00462554 undefined FUN_00462554()
fe ff
004789ec 89 04 24 MOV dword ptr [ESP ]=> local_28 ,x
004789ef 33 c0 XOR x,x
004789f1 89 44 24 MOV dword ptr [ESP + local_28 +0x4 ],x
04
004789f5 df 2c 24 FILD qword ptr [ESP ]=> local_28
004789f8 dc 66 08 FSUB qword ptr [ESI + 0x8 ]
004789fb d9 1d 18 FSTP dword ptr [DAT_00871218 ] = ??
12 87 00
...
...
...
00478c4e 8b c3 MOV x,EBX
00478c50 83 c4 20 ADD ESP ,0x20
00478c53 5e POP ESI
00478c54 5b POP EBX
00478c55 c3 RET
我知道:-我的可执行文件是32位,由Delphi制作。-x是新的x轴值。
我的目标是将此功能与注入的dll一起使用。我来到这里:
typedef int (__stdcall *_CameraX)(int x);
_CameraX CameraX = (_CameraX)0x04789E0;
但是,在004789fb FSTP dword ptr [DAT_00871218]
行上出现“访问冲突”。这是x值的首次使用。所以我想这是错误的类型。
这是我的理解:-由于原始程序是用Delphi编写的,因此请记住__pascal调用约定。由于它在Visual Studio中已弃用,因此我正在使用__stdcall。我认为,由于只有一个参数*,不会有任何区别。-x总是很大。如果不是32位的话,我会选择很长的时间。-我真的不知道返回类型。我之所以选择int是因为在调用之后,调用者函数会创建一个test al, al
。
* *:吉德拉(Ghidra)告诉我只有一个论点,但是如果我在听我自己的话,就会有两个:
004789e0 53 PUSH EBX
004789e1 56 PUSH ESI
...
...
00478c53 5e POP ESI
00478c54 5b POP EBX
00478c55 c3 RET
所以这是我的问题:-真的只有一种说法吗?-如果正确的是__pascal,我应该使用哪种调用约定,因为它不能在Visual Studio中使用? (如果有多个参数)-如何获取返回值?-为什么我们“发明”了许多电话会议?例如,为什么我们都不使用__cdecl呢?为什么有些人从左到右阅读,为什么使用从右到左?有什么区别吗?
我很确定缺少某些信息,由Ghidra生成的伪代码有用吗?
编辑:
int CameraX(int x)
{
undefined4 *puVar1;
uint uVar2;
undefined4 unaff_EBX;
int iVar3;
float10 in_ST0;
undefined4 local_20;
undefined4 local_1c;
undefined4 local_18;
undefined local_14 [12];
uVar2 = FUN_00462554();
DAT_00871218 = (float)(ulonglong)uVar2 - (float)*(double *)(x + 8);
iVar3 = CONCAT31((int3)((uint)unaff_EBX >> 8),1);
if (*(float *)(x + 0x6c) < DAT_00871218) {
// ...
这似乎使用的是Borland注册调用约定(也称为Borland快速调用)。它分别使用EAX,EDX和ECX作为前三个参数,其余参数进入堆栈。被呼叫者还必须保留EBX,ESI,EDI和EBP寄存器。
您可以看到您的函数保存了EBX和ESI寄存器,并且不使用EDI或EBP。根据约定,它还使用EAX作为第一个参数x
,并在EAX中返回结果。
在现代Visual Studio中,没有针对Borland fastcall的等效调用约定。您可能会依赖使用内联汇编作为调用此Delphi函数的解决方法。
此解决方法可能会使用临时的Microsoft fastcall
函数指针,因为它也使用了第一个参数的寄存器,但EAX除外,我们必须使用内联汇编传递它,例如(不确定代码语法) ,将其视为伪代码):
typedef void (__fastcall *_CameraX)();
_CameraX CameraXTemp = (_CameraX)0x04789E0;
int CameraX(int x) {
int ret;
__asm mov eax, x
CameraXTemp();
__asm mov ret, eax
return ret;
}