1

我愿意在一个小 3D 游戏上修改相机坐标。我已经能够找到三个功能,每个轴一个。我们称它们为 CameraX、CameraY 和 CameraZ。当我发现我遗漏了一些东西时,我一直只使用第一个。

以下是从 Ghidra 获得的 ASM 说明:

                             *************************************************************
                             *                           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 告诉我只有一个论点,但如果我在听自己的话,就会有 2 个:

        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) {
  // ...

编辑 2:

这是完整的 ASM 代码: https ://pastebin.com/UiGGEju1,这是 Ghidra 生成的伪代码: https ://pastebin.com/1Fc48k1g

所以我想我错了:这不是我认为导致这个问题的线,而是这个:00478c3d 89 46 1c MOV dword ptr [ESI + 0x1c],x 但它仍然是我的 x 值导致它。

我不明白的是“为什么”:x 是一个 int(32 位),程序试图将它作为 dword(32 位)存储在 [ESI + 0x1c] 中。程序是否有可能无法解析 x 在哪里/是什么?(就像我在没有参数的情况下调用函数一样)

顺便问一下这个问题:“-为什么我们“发明”了这么多调用约定?例如,为什么我们不都使用__cdecl?为什么有些人使用从右到左,而有些人从左到右阅读?有没有差异?” 还没有回答,真的很吸引我,如果你有解释,我很乐意听!

4

1 回答 1

3

这看起来像是在使用Borland 寄存器调用约定(也称为Borlandfastcall):

  • 分别取EAX, EDXthenECX作为前三个参数;
  • 第 4 个和更高的参数进入堆栈;
  • 被调用者必须保留EBXESIEDIEBP寄存器;
  • EAX用作 32 位整数的返回方法;
  • ...以及其他一些不适用于此处的规则。

你可以看到你的函数保存了EBXandESI寄存器,也没有使用EDInor EBP。它还EAX用作第一个参数并按照约定x返回结果。EAX

fastcall在现代 Visual Studio 中,Borland 没有等效的调用约定。您可能会依赖使用内联汇编作为调用此 Delphi 函数的解决方法。

这种解决方法可能会使用临时 Microsoftfastcall函数指针,因为它还使用寄存器作为第一个参数,除了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;
}

请注意,如果此方法有效,您将需要反转接下来的两个参数(对于具有 2 个或 3 个参数的函数),因为 Microsoftfastcall期望ECX按此EDX顺序。对于 4 个或更多参数,我假设需要一些堆栈工作。

另一种方法是使用函数,我假设它看起来像这样:

__declspec(naked) int __stdcall CameraX(int x)
{
    __asm
   {
       mov  eax, [esp + 4] // x
       push ebx
       mov  ebx, 0x04789E0
       call ebx
       pop  ebx
       ret  4 // 1 argument in this __stdcall function (1 * 4)
   }
}

为了将来的目的,如果您将此方法与另一个具有多个参数的函数一起使用,您将需要使用RET nwhere nis number of arguments * 4。对于 3 个或更多参数,您还必须像使用前一种方法一样将参数推送到堆栈中。

于 2020-03-13T17:53:05.977 回答