0

I am trying to do some script hooking in C++, and have setup a simple test function for this case.

   void __declspec(naked) testFunct()
{
    int myInt;
    myInt = 2000;
    __asm{
        mov eax, myInt
        jmp [jmp_back_address]
    }
}

when using this to pass in the integer, the function fails when it is called and the project crashes. However, when using this instead, without an integer value, it successfully passes through.

   void __declspec(naked) testFunct()
{
    __asm{
        mov eax, 2000
        jmp [jmp_back_address]
    }
} 

How can I successfully pass the integer?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847

2 Answers2

0

The correct solution for my situation was to simply do everything within the ourFunct() through ASM instead, as mixing both C++ and ASM for passing variables was creating buggy assembly code. Example with a function call that works:

int CalculateTotalScore()
{
    return (int)*Game::current_speed_score;
}

DWORD jmpBackAddress;
void __declspec(naked) ourFunct()
{
    __asm{
        call CalculateTotalScore
        jmp [jmpBackAddress]
    }
}
  • So probably like in GNU C, `naked` functions can only use `asm{}` blocks, not any plain C++ code. – Peter Cordes Nov 19 '21 at 04:20
  • Correct, even though it doesn't throw any warnings, and will still compile, the x86 code it compiles will not take into account the handwritten asm code. – Onomichio Kun Nov 19 '21 at 06:08
  • Actually the problem is that compiler-generated code doesn't take into account that it's in a naked function, and does things like assume EBP is set up as a frame pointer without emitting code to do that. https://godbolt.org/z/5n41KK5zz. The hand-written asm itself isn't the problem. But yeah, it's odd that MSVC at least doesn't even warn about that non-asm code in a `naked` function. – Peter Cordes Nov 19 '21 at 06:25
-1

The assembler doesn't know what "myInt" means. Most compilers support inline assembly with the possibility to pass values. For instance, with GCC, you may try to define a macro like

#define MY_ASM_MACRO(myInt) ({ asm volatile("mov eax,%0\n\t \
          jmp [jmp_back_address]" : : "r"(myInt) : ); })

And use it like

void __declspec(naked) testFunct()
{
   int myInt;
   myInt = 2000;
   MY_ASM_MACRO(myInt)
}
sftac gsv
  • 34
  • 2
  • MSVC inline asm is completely different; it *does* use C var names inside asm *blocks*. (Like `clang -fasm-blocks`). That and `__declspec` are how we can tell this is Visual C++, not GNU C++ syntax. [What is the difference between 'asm', '\_\_asm' and '\_\_asm\_\_'?](https://stackoverflow.com/a/35959859) – Peter Cordes Nov 19 '21 at 04:13
  • Also, your macro isn't safe; you need a `__builtin_unreachable();` after the asm statement, because you unconditionally jump out of it. Also, just ask for `myInt` in EAX in the first place with `"a"(myInt)`. It's silly to make the compiler materialize the value in a different register, and not even let it use an immediate. – Peter Cordes Nov 19 '21 at 04:15
  • Or actually this is an `__attribute__((naked))` function, so `__builtin_unreachable()` isn't needed, but also it's not supported to use GNU C Extended Asm. Only Basic Asm is supported in naked functions in GNU C. https://gcc.gnu.org/onlinedocs/gcc/x86-Function-Attributes.html#index-naked-function-attribute_002c-x86 – Peter Cordes Nov 19 '21 at 04:19