I have hooked a exported MFC DLL function using naked function.
the definition of naked function is as follows :
__declspec(naked)
static void __cdecl GenericHook(void* __this,class CScrollViewAccess* objParam1, class CRect& objParam2,unsigned int iParam1, unsigned long iParam2, char* szParam1,
void* vParam1, class CFont* objParam3,class CFont* objParam4,
class CBrush* objParam5)
{ /*function body start*/
__asm pushad; /* first "argument", which is also used to store registers */
__asm push ecx; /* padding so that ebp+8 refers to the first "argument" */
/* set up standard prologue */
__asm push ebp;
__asm mov ebp, esp;
__asm sub esp, __LOCAL_SIZE;
if(flg == false)
{
//RECT* rct = reinterpret_cast(&objParam2);
hInst = LoadLibrary("C:\\Sample.dll"); /// MFC Dll
funcPTR = (CMYCLASS_)(((int)hInst)+((int)0x00001032));
funcPTR(__this,objParam2);
/* standard epilogue */
__asm mov esp, ebp;
__asm pop ebp;
__asm pop ecx; /* clear padding */
__asm popad; /* clear first "argument" */
__asm jmp [Trampoline];
}
/*function body end*/
The Mfc dll has following function:
void CMyClass::returnRect(class CRect& objParam)
{
int width = objParam.Width();
int height = objParam.Height();
CPoint pt = objParam.TopLeft();
FILE* fp;
char szEnter[6] = {13,0,10,0,0,0};
fp = fopen("c:\\LogFolder\\log.txt","ab+");
fprintf(fp,"Width: %d Height: %d X co-ord: %d Y co-ord: %d\n%s",width,height,pt.x,pt.y,szEnter);
fclose(fp);
}
after passing CRect& parameter to the MFC DLL the values logged are wrong.
How to process the reference object?
I have solved this hooking problem as follows:
extern "C" __declspec(naked) __declspec(dllexport) void __stdcall GenericHook()
{ /*function body start*/
/* set up standard prologue */
__asm push ebp;
__asm mov ebp, esp;
__asm pushad;
// __asm sub esp, __LOCAL_SIZE; // Grow stack size
__asm mov eax,[ebp+4]; //Return Address
__asm mov objParam1,eax;
__asm mov eax,DWORD ptr[ebp+8]; //arg1
__asm mov objParam2,eax;
__asm mov eax,DWORD ptr[ebp+12]; //arg2
__asm mov objParam3,eax;
__asm mov eax,DWORD ptr[ebp+16]; //arg3
__asm mov objParam4,eax;
__asm mov eax,DWORD ptr[ebp+20]; //arg4
__asm mov objParam5,eax;
/*-------------PROCESSING START---------------------*/
fp = fopen("c:\\LogFolder\\log.txt","ab+");
fprintf(fp,"arg1: %lu~arg2: %lu~arg3: %lu~arg4: %lu~ar5: %lu\n",objParam1,objParam2,objParam3,objParam4,objParam5);
fprintf(fp,"==========================================================================\n\n");
fclose(fp);
/*-------------PROCESSING END-----------------------*/
/* standard epilogue
__asm add esp, __LOCAL_SIZE;*/
__asm popad;
__asm mov esp, ebp;
__asm pop ebp;
__asm jmp [Trampoline];
}
Related
I'm trying to convert assembly written in AT&T syntax from a DevC++ project to inline assembly in Visual Studio.
This is the AT&T I'm trying to convert:
void Painter::drawRectangle(int surface, int x, int y, int width, int height, int red, int green, int blue) {
asm("mov %0, %%eax":: "r" (0x004EAA90));
asm("call *%eax");
asm("mov %eax, %ecx");
asm("mov (%ecx), %eax");
asm("push %0":: "m" (blue));
asm("push %0":: "m" (green));
asm("push %0":: "m" (red));
asm("push %0":: "m" (height));
asm("push %0":: "m" (width));
asm("push %0":: "m" (y));
asm("push %0":: "m" (x));
asm("push %0":: "m" (surface));
asm("call *0x14(%eax)");
}
what i've done so far:
void _drawrectangle(int surface, int x, int y, int width, int height, int red, int green, int blue)
{
__asm
{
mov eax, 0x004eaa90
call dword ptr [eax]
mov ecx, eax
mov eax, [ecx]
push blue
push green
push red
push height
push width
push y
push x
push surface
call dword ptr [eax + 0x14]
}
}
I'm writing this in my DLL, which I've already injected into the game. The game crashes on opening. And I've already hooked another drawing function in C++, which worked.
Hopefully you can help me/guide me in the right direction. Thank you.
Here's how you could write your function in C++ without the use of inline assembly:
#ifndef _MSC_VER
/* For GCC and clang */
#undef __thiscall
#define __thiscall __attribute__((thiscall))
#endif
struct some_interface {
virtual void _unknown_0() = 0;
virtual void _unknown_4() = 0;
virtual void _unknown_8() = 0;
virtual void _unknown_C() = 0;
virtual void _unknown_10() = 0;
virtual void __thiscall drawRectangle(int surface, int x, int y,
int width, int height,
int red, int green, int blue) = 0;
};
const auto get_interface = (some_interface *(*)()) 0x4EAA90;
void
drawRectangle(int surface, int x, int y, int width, int height,
int red, int green, int blue) {
get_interface()->drawRectangle(surface, x, y, width, height,
red, green, blue);
}
The code you're trying to translate first calls a function that returns a pointer to some class object with at least 6 virtual methods defined. It then calls the 6th virtual method of that object. The some_interface struct minimally recreates that class so the 6th virtual method can be called. The get_interface constant is a function pointer that points to the function located at 0x4EAA90 and in C++ function pointers can be used just like a function.
The above code generates the following assembly in GCC 8.2:
drawRectangle(int, int, int, int, int, int, int, int):
subl $12, %esp
movl $5155472, %eax
call *%eax
movl (%eax), %edx
movl %eax, %ecx
pushl 44(%esp)
pushl 44(%esp)
pushl 44(%esp)
pushl 44(%esp)
pushl 44(%esp)
pushl 44(%esp)
pushl 44(%esp)
pushl 44(%esp)
call *20(%edx)
addl $12, %esp
ret
and the following assembly with Visual C++ 2017:
void drawRectangle(int,int,int,int,int,int,int,int) PROC ; drawRectangle, COMDAT
mov eax, 5155472 ; 004eaa90H
call eax
push DWORD PTR _blue$[esp-4]
mov ecx, eax
push DWORD PTR _green$[esp]
mov edx, DWORD PTR [eax]
push DWORD PTR _red$[esp+4]
push DWORD PTR _height$[esp+8]
push DWORD PTR _width$[esp+12]
push DWORD PTR _y$[esp+16]
push DWORD PTR _x$[esp+20]
push DWORD PTR _surface$[esp+24]
call DWORD PTR [edx+20]
ret 0
void drawRectangle(int,int,int,int,int,int,int,int) ENDP ; drawRectangle
I was trying to reduce the clutter in my original question (below), but I am afraid that made it harder to follow. So here is the original source along with IDA's disassembly.
My question still is this: why does getStruct() pop the return argument and only the return argument off the stack? (It's calling ret 4 instead of ret for no arguments or ret 12 for all three arguments).
#include <iostream>
struct SomeStruct {
char m_buff[0x1000];
};
SomeStruct getStruct(uint32_t someArg1, uint32_t someArg2)
{
return SomeStruct();
}
int main(int argc, const char * argv[])
{
SomeStruct myLocalStruct = getStruct(0x20,0x30);
return 0;
}
; _DWORD __stdcall getStruct(unsigned int, unsigned int)
public getStruct(unsigned int, unsigned int)
getStruct(unsigned int, unsigned int) proc near ; CODE XREF: _main+4Dp
var_8 = dword ptr -8
var_4 = dword ptr -4
arg_0 = dword ptr 8
arg_4 = dword ptr 0Ch
arg_8 = dword ptr 10h
push ebp
mov ebp, esp
sub esp, 18h
mov eax, [ebp+arg_8]
mov ecx, [ebp+arg_4]
mov edx, [ebp+arg_0]
mov [ebp+var_4], ecx
mov [ebp+var_8], eax
mov eax, esp
mov [eax], edx
mov dword ptr [eax+4], 1000h
call ___bzero
add esp, 18h
pop ebp
retn 4
getStruct(unsigned int, unsigned int) endp
; ---------------------------------------------------------------------------
align 10h
; =============== S U B R O U T I N E =======================================
; Attributes: bp-based frame
; int __cdecl main(int argc, const char **argv, const char **envp)
public _main
_main proc near
var_1020 = dword ptr -1020h
var_101C = dword ptr -101Ch
var_1018 = dword ptr -1018h
var_14 = dword ptr -14h
var_10 = dword ptr -10h
var_C = dword ptr -0Ch
argc = dword ptr 8
argv = dword ptr 0Ch
envp = dword ptr 10h
push ebp
mov ebp, esp
push edi
push esi
sub esp, 1030h
mov eax, [ebp+argv]
mov ecx, [ebp+argc]
lea edx, [ebp+var_1018]
mov esi, 20h
mov edi, 30h
mov [ebp+var_C], 0
mov [ebp+var_10], ecx
mov [ebp+var_14], eax
mov [esp], edx ; ptr to destination
mov dword ptr [esp+4], 20h ; unsigned int
mov dword ptr [esp+8], 30h
mov [ebp+var_101C], esi
mov [ebp+var_1020], edi
call getStruct(uint,uint)
sub esp, 4
mov eax, 0
add esp, 1030h
pop esi
pop edi
pop ebp
retn
_main endp
Original question below:
I have some function with the following declaration:
SomeStruct getStruct(uint32_t someArg1, uint32_t someArg2);
getStruct is being called like this:
myLocalStruct = getStruct(someArg1,someArg2);
When compiling this using clang on x86 the calling code looks roughly like this:
lea esi, [ebp-myLocalStructOffset]
mov [esp], esi
mov [esp+4], someArg1
mov [esp+8], someArg2
call getStruct;
sub esp, 4;
So the caller is restoring its stack pointer after the call. Sure enough, the implementation of getStruct ends with a ret 4, effectively popping the structs pointer.
This looks like it is partially cdecl with the caller being responsible for the stack cleanup, and partially stdcall with the callee removing the arguments. I just cannot figure out what the reason is for this approach. Why not leave all the cleanup to the caller? Is there any benefit to this ?
It looks as if you forgot to quote the few lines of assembler above the part you quoted. I assume there is something like:
sub esp,12
somewhere above what you quoted. The calling convention looks like pure stdcall, and the return value is in reality passed as a hidden pointer argument, i.e. the code is in fact compiled as if you had declared:
void __stdcall getStruct(SomeStruct *returnValue, uint32_t someArg1, uint32_t someArg2);
I'm having trouble with the call to BeginPaint(hWnd, lpPaint) in the WindowPaint event. It seems that the function BeginPaint overwrite the stack.
A little C program tell me that the size of PAINTSTRUCT (lpPaint) should be 34.
If i put the adresse to EBP-34 or a little more EBP-56 for the return value of lpPaint, it crash. If I allow alot of stack and put the return adresse to EBP-170 it works.
Simple code:
;Note : All Win32 API functions preserve the EBP, EBX, ESI, and EDI registers
extern GetModuleHandleA
extern ExitProcess
extern GetCommandLineA
extern RegisterClassExA
extern LoadIconA
extern LoadCursorA
extern CreateWindowExA
extern ShowWindow
extern UpdateWindow
extern MessageBoxA
extern GetMessageA
extern TranslateMessage
extern DispatchMessageA
extern PostQuitMessage
extern DefWindowProcA
extern DrawTextA
extern BeginPaint
extern EndPaint
import GetModuleHandleA kernel32.dll
import ExitProcess kernel32.dll
import GetCommandLineA kernel32.dll
import RegisterClassExA user32.dll
import LoadIconA user32.dll
import LoadCursorA user32.dll
import CreateWindowExA user32.dll
import ShowWindow user32.dll
import UpdateWindow user32.dll
import MessageBoxA user32.dll
import GetMessageA user32.dll
import TranslateMessage user32.dll
import DispatchMessageA user32.dll
import PostQuitMessage user32.dll
import DefWindowProcA user32.dll
import DrawTextA user32.dll
import BeginPaint user32.dll
import EndPaint user32.dll
section .text use32
..start:
;Handle from calling process
push dword 0
call [GetModuleHandleA]
mov dword [hInstance], eax
;Get command line
call [GetCommandLineA]
mov dword [commandLine], eax
;Main window
Call WindowMain
;Exit
Push eax
call [ExitProcess]
;========================================================================================
; Create a main windows
;========================================================================================
WindowMain:
push ebp
mov ebp, esp
;Local variable
;==============
;EBP-48 = WNDCLASSEX Structure 48 bytes
;EBP-72 = MSG 24 bytes
;EBP-76 = HWND, handle of our window 4 bytes
sub esp, 76
lea ebx, [ebp-48] ;EBX is now address of WNDCLASSEX
;WNDCLASSEX Structure : http://msdn.microsoft.com/en-us/library/ms633577(v=vs.85).aspx
mov dword [ebx+00], 48 ;Size of the structure
mov dword [ebx+04], 3 ;Style of the window
mov dword [ebx+08], WindowProcedure ;Callback function for events
mov dword [ebx+12], 0
mov dword [ebx+16], 0
mov dword [ebx+20], hInstance ;Handle to the window
;[ebx+24] = HICON
;[ebx+28] = HCURSOR
mov dword [ebx+32], 6 ;Background brush
mov dword [ebx+36], 0 ;Menu name NULL
mov dword [ebx+40], ClassName ;Class name for the window
;[ebx+44] = HICON
;LoadIconA(0, IDI_APPLICATION);
push 32512
push 0
call [LoadIconA]
mov dword [ebx+24], eax ;Icon for our window
mov dword [ebx+44], eax ;Small icon for our window
;LoadCursorA(0, IDC_ARROW);
push dword 32512
push 0
call [LoadCursorA]
mov dword [ebx+28], eax
;RegisterClassExA(WNDCLASSEX address);
push ebx
call [RegisterClassExA]
;CreateWindowEx(0, ClassName, window title, WS_OVERLAPPEDWINDOW, x, y, width, height, handle to parent window, handle to menu, hInstance, NULL);
push 0
push hInstance
push 0
push 0
push 400 ;High
push 500 ;Wide
push dword 0x80000000 ;CW_USEDEFAULT
push dword 0x80000000 ;CW_USEDEFAULT
;WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
push dword 0x00 | 0xC00000 | 0x80000 | 0x40000 | 0x20000 | 0x10000 ;WS_OVERLAPPEDWINDOW
push dword ApplicationName
push dword ClassName
push dword 0
call [CreateWindowExA]
mov dword [ebp-76], eax ;Handle of our window
cmp eax, 0
jz .newWindowsFailed
;ShowWindow(hWind, SW_SHOWDEFAULT);
push dword 10
push dword [ebp-76]
call [ShowWindow]
;UpdateWindow(hWind);
push dword [ebp-76]
call [UpdateWindow]
.MessageLoop:
;GetMessageA(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax);
push dword 0
push dword 0
push dword 0
lea ebx, [ebp-72]
push ebx
call [GetMessageA]
cmp eax, 0 ;WM_QUIT
jz .MessageLoopExit
;TranslateMessage(lpMsg);
lea ebx, [ebp-72]
push ebx
call [TranslateMessage]
;DispatchMessageA(lpMsg);
lea ebx, [ebp-72]
push ebx
call [DispatchMessageA]
jmp .MessageLoop
.MessageLoopExit:
jmp .finish
.newWindowsFailed:
push dword 0
push dword 0
push errMsg
push 0
call [MessageBoxA]
mov eax, 1 ;Return error
mov esp, ebp
pop ebp
ret
.finish:
mov esp, ebp
pop ebp
ret
;========================================================================================
; Handle the events that our window sends us.
;========================================================================================
;LRESULT CALLBACK WindowProc(
; _In_ HWND hwnd,
; _In_ UINT uMsg,
; _In_ WPARAM wParam,
; _In_ LPARAM lParam
;);
;========================================================================================
WindowProcedure:
push ebp
mov ebp, esp
;Local variable
;==============
;EBP-56 = PAINTSTRUCT <- RECALCULER LA TAILLE DE LA STUCTURE!!!
;EBP-60 = HDC
;EBP-76 = RECT
sub esp, 170
mov eax, dword [ebp+12] ;uMsg
cmp eax, 2 ;WM_DESTROY
jz .WindowDestroy
cmp eax, 0X0F ;WM_PAINT
jz .WindowPaint
.WindowDefault:
push dword [ebp+20]
push dword [ebp+16]
push dword [ebp+12]
push dword [ebp+08]
call [DefWindowProcA]
;The return value is from DefWindowProcA so we can't change EAX
mov esp, ebp
pop ebp
ret 16
.WindowDestroy:
;We pass 0 as an argument to the PostQuitMessage() function, to tell it
;to pass 0 as the value of wParam for the next message. At that point,
;GetMessage() will return 0, and the message loop will terminate.
;PostQuitMessage(nExitCode);
push dword 0
call [PostQuitMessage]
jmp .WindowProcedureFinish
.WindowPaint:
;BeginPaint(hWnd, lpPaint);
lea ebx, [ebp-34]
push ebx
push dword [ebp+8]
call [BeginPaint]
mov dword [ebp-60], eax ;Save device context
jmp .WindowProcedureFinish
.WindowProcedureFinish:
xor eax, eax
mov esp, ebp
pop ebp
ret 16
msgBox:
; MessageBoxA(0, msg1, title, 0);
push dword 0
push dword ClassName
push dword TextLabel
push dword 0
call [MessageBoxA]
ret
section .data
ClassName db "Main Window", 0
ApplicationName db "Win32 Assembler", 0
errMsg db "An error occured while making the new window.", 0
TextLabel db "Welcom to the main event!", 13, 10, 13, 10, 0
section .bss
hInstance resd 1
commandLine resd 1
Compile with :
nasm -fobj main.asm
alink -oPE main.obj -o main.exe
A little C program tell me that the size of PAINTSTRUCT (lpPaint)
should be 34
Really? What C compiler is that??? If you look up PAINTSTRUCT on MSDN:
typedef struct tagPAINTSTRUCT {
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT, *PPAINTSTRUCT;
hdc = 4 bytes
fErase = 4 bytes
rcPaint = RECT` = 16 bytes
fRestore = 4 bytes
fIncUpdate = 4 bytes
rgbReserved = 32 bytes
and RECT:
typedef struct _RECT {
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT, *PRECT;
All of that added together gives you 64 bytes!!!
That is why [EBP-56] won't work, but [EBP-170] will work, plenty of room for the structure.
test a cdecl calling convention,but it's a little confusion about this:
original C code:
int __attribute__((cdecl)) add(int a,int b)
{
int i;
i = a+b;
return i;
}
void __attribute__((cdecl)) print(int i, ...)
{
int j,a,b;
a = 2;
b = 3;
j = add(a,b);
}
void __attribute__((cdecl)) main(void)
{
print(2,3);
}
void __attribute__((cdecl)) main(void)
{
print(2,3);
}
compile code with this:
gcc test3.c -o test3 -g -mrtd
the corresponding asm like that:
(gdb) disassemble print
Dump of assembler code for function print:
0x080483cb <+0>: push ebp
0x080483cc <+1>: mov ebp,esp
0x080483ce <+3>: sub esp,0x18
0x080483d1 <+6>: mov DWORD PTR [ebp-0x8],0x2
0x080483d8 <+13>:mov DWORD PTR [ebp-0xc],0x3
0x080483df <+20>:mov eax,DWORD PTR [ebp-0xc]
0x080483e2 <+23>:mov DWORD PTR [esp+0x4],eax
0x080483e6 <+27>:mov eax,DWORD PTR [ebp-0x8]
0x080483e9 <+30>:mov DWORD PTR [esp],eax
0x080483ec <+33>:call 0x80483b4 <add>
0x080483f1 <+38>:mov DWORD PTR [ebp-0x4],eax
0x080483f4 <+41>:leave
0x080483f5 <+42>:ret
(gdb) disassemble add
Dump of assembler code for function add:
0x080483b4 <+0>: push ebp
0x080483b5 <+1>: mov ebp,esp
0x080483b7 <+3>: sub esp,0x10
0x080483ba <+6>: mov eax,DWORD PTR [ebp+0xc]
0x080483bd <+9>: mov edx,DWORD PTR [ebp+0x8]
0x080483c0 <+12>:lea eax,[edx+eax*1]
0x080483c3 <+15>:mov DWORD PTR [ebp-0x4],eax
0x080483c6 <+18>:mov eax,DWORD PTR [ebp-0x4]
0x080483c9 <+21>:leave
0x080483ca <+22>:ret
(gdb) disassemble main
Dump of assembler code for function main:
0x080483f6 <+0>: push ebp
0x080483f7 <+1>: mov ebp,esp
0x080483f9 <+3>: sub esp,0x8
0x080483fc <+6>: mov DWORD PTR [esp+0x4],0x3
0x08048404 <+14>:mov DWORD PTR [esp],0x2
0x0804840b <+21>:call 0x80483cb <print>
0x08048410 <+26>:leave
0x08048411 <+27>:ret
the cdecl convention don't add some instruction like:
add esp 8 or other similar instructions after the calling function.
why this? Thank you.
The code uses the leave instruction, which restores the stack frame. Therefore there is no need to adjust the stack pointer separately.
I have a function in VS where I pass a pointer to the function. I then want to store the pointer in a register to further manipulate. How do you do that?
I have tried
float __declspec(align(16)) x[16] =
{
0.125000, 0.125000, 0.125000, 0,
-0.125000, 0.125000, -0.125000, 0,
0.125000, -0.125000, -0.125000, 0,
-0.125000, -0.125000, 0.125000, 0
};
void e()
{
__asm mov eax, x // doesn't work
__asm mov ebx, [eax]
}
void f(float *p)
{
__asm mov eax, p // does work
__asm mov ebx, [eax]
}
int main()
{
f(x);
e();
}
Option 1 actually seems to work fine. Consider the following program:
#include <stdio.h>
void f(int *p) {
__asm mov eax, p
__asm mov ebx, [eax]
// break here
}
void main()
{
int i = 0x12345678;
f(&i);
}
With Visual Studio 2008 SP1, a single-file C++ program and debug build, I'm getting the following in the registers window when stepping into the end of f():
EAX = 004DF960
EBX = 12345678
ECX = 00000000
EDX = 00000001
ESI = 00000000
EDI = 004DF884
EIP = 003013C3
ESP = 004DF7B8
EBP = 004DF884
EFL = 00000202
Looking at the values in EAX, EBX and ESP, that looks like a pretty good evidence that you actually have the pointer you wanted in EAX. The address in EAX is just a tad higher than the one in ESP, suggesting it's one frame higher up the stack. The dereferenced value loaded into EBX suggests we got the right address.
Loading the address of a global is subtly different. The following example uses the LEA instruction to accomplish the task.
#include <stdio.h>
int a[] = { 0x1234, 0x4567 };
void main()
{
// __asm mov eax, a ; interpreted as eax <- a[0]
__asm lea eax, a ; interpreted as eax <- &a[0]
__asm mov ebx, [eax]
__asm mov ecx, [eax+4]
// break here
}
Stepping to the end of main() gets you the following register values. EAX gets the address of the first element in the array, while EBX an ECX get the values of its members.
EAX = 00157038
EBX = 00001234
ECX = 00004567
EDX = 00000001
ESI = 00000000
EDI = 0047F800
EIP = 001513C9
ESP = 0047F734
EBP = 0047F800
EFL = 00000202
The magic isn't in the LEA instruction itself. Rather, it appears that the __asm directive treats C/C++ identifiers differently depending on whether a MOV or an LEA instruction is used. Here is the ASM dump of the same program, when the MOV instruction is uncommented. Notice how the MOV instruction gets the content of a[] as its argument (DWORD PTR), while the LEA instruction gets its offset.
; ...
PUBLIC ?a##3PAHA ; a
_DATA SEGMENT
?a##3PAHA DD 01234H ; a
DD 04567H
_DATA ENDS
; ...
mov eax, DWORD PTR ?a##3PAHA
lea eax, OFFSET ?a##3PAHA
mov ebx, DWORD PTR [eax]
mov ecx, DWORD PTR [eax+4]
; ...
I'm not sure if this is correct, but have you tried casting *p to an int first and then loading that value?
void f(*p)
{
int tmp = (int)p;
// asm stuff...
}