Определение виртуальных функций в дизасcемблерном листинге IDA

Пусть у нас есть код на С++ вызова виртуальной функции SetCooperativeLevel из библиотеки DirectDraw:


	#include <ddraw.h>
	#pragma comment (lib, "ddraw.lib")

	HWND hWnd;
	LPDIRECTDRAW lpdd;

	void Func()
	{
	
		DirectDrawCreate(NULL, &lpdd, NULL);

		lpdd->SetCooperativeLevel(hWnd, DDSCL_NORMAL );

	}
	

В дизассемблере IDA функция трансформируется в следующих ассемблерный код:


.text:00401000 Func            proc near               ; CODE XREF: WinMain(x,x,x,x)+DCp
.text:00401000                 push    ebp
.text:00401001                 mov     ebp, esp
.text:00401003                 sub     esp, 40h
.text:00401006                 push    ebx
.text:00401007                 push    esi
.text:00401008                 push    edi
.text:00401009                 push    0               ; pUnkOuter
.text:0040100B                 push    offset lpdd     ; lplpDD
.text:00401010                 push    0               ; lpGUID
.text:00401012                 call    _DirectDrawCreate@12 ; DirectDrawCreate(x,x,x)
.text:00401017                 push    8
.text:00401019                 mov     eax, hWnd
.text:0040101E                 push    eax
.text:0040101F                 mov     ecx, lpdd
.text:00401025                 mov     edx, [ecx]
.text:00401027                 mov     eax, lpdd
.text:0040102C                 push    eax
.text:0040102D                 mov     ecx, [edx+50h]
.text:00401030                 call    ecx
.text:00401032                 pop     edi
.text:00401033                 pop     esi
.text:00401034                 pop     ebx
.text:00401035                 mov     esp, ebp
.text:00401037                 pop     ebp
.text:00401038                 retn
.text:00401038 Func            endp

	

Теперь мы видим что функция DirectDrawCreate создает указатель lpdd на интерфейс DirectDraw.

В следующих строках происходит разыменование указателя на таблицу виртуальных функций интерфейса DirectDraw:

	
.text:0040101F                 mov     ecx, lpdd
.text:00401025                 mov     edx, [ecx]

Теперь edx указывает на таблицу виртуальных функций интерфейса DirectDraw. Далее происходит вызов самой виртуальной функции которую мы определим:

	
.text:0040102D                 mov     ecx, [edx+50h]
.text:00401030                 call    ecx

Смещеине 50h в десятичном виде это число 80, которое нам надо разделить на 4, где 4 это число байт которое занимает указатель на одну виртуальную функцию в таблице. 80 разделить на 4 будет 20 десятичной системе счисления. Далее нам нужно отркыть файл ddraw.h и найти описание интерфеса DirectDraw:


/*
 * IDirectDraw
 */
#if defined( _WIN32 ) && !defined( _NO_COM )
#undef INTERFACE
#define INTERFACE IDirectDraw
DECLARE_INTERFACE_( IDirectDraw, IUnknown )
{
    /*** IUnknown methods ***/
    STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE;
    STDMETHOD_(ULONG,AddRef) (THIS)  PURE;
    STDMETHOD_(ULONG,Release) (THIS) PURE;
    /*** IDirectDraw methods ***/
    STDMETHOD(Compact)(THIS) PURE;
    STDMETHOD(CreateClipper)(THIS_ DWORD, LPDIRECTDRAWCLIPPER FAR*, IUnknown FAR * ) PURE;
    STDMETHOD(CreatePalette)(THIS_ DWORD, LPPALETTEENTRY, LPDIRECTDRAWPALETTE FAR*, IUnknown FAR * ) PURE;
    STDMETHOD(CreateSurface)(THIS_  LPDDSURFACEDESC, LPDIRECTDRAWSURFACE FAR *, IUnknown FAR *) PURE;
    STDMETHOD(DuplicateSurface)( THIS_ LPDIRECTDRAWSURFACE, LPDIRECTDRAWSURFACE FAR * ) PURE;
    STDMETHOD(EnumDisplayModes)( THIS_ DWORD, LPDDSURFACEDESC, LPVOID, LPDDENUMMODESCALLBACK ) PURE;
    STDMETHOD(EnumSurfaces)(THIS_ DWORD, LPDDSURFACEDESC, LPVOID,LPDDENUMSURFACESCALLBACK ) PURE;
    STDMETHOD(FlipToGDISurface)(THIS) PURE;
    STDMETHOD(GetCaps)( THIS_ LPDDCAPS, LPDDCAPS) PURE;
    STDMETHOD(GetDisplayMode)( THIS_ LPDDSURFACEDESC) PURE;
    STDMETHOD(GetFourCCCodes)(THIS_  LPDWORD, LPDWORD ) PURE;
    STDMETHOD(GetGDISurface)(THIS_ LPDIRECTDRAWSURFACE FAR *) PURE;
    STDMETHOD(GetMonitorFrequency)(THIS_ LPDWORD) PURE;
    STDMETHOD(GetScanLine)(THIS_ LPDWORD) PURE;
    STDMETHOD(GetVerticalBlankStatus)(THIS_ LPBOOL ) PURE;
    STDMETHOD(Initialize)(THIS_ GUID FAR *) PURE;
    STDMETHOD(RestoreDisplayMode)(THIS) PURE;
    STDMETHOD(SetCooperativeLevel)(THIS_ HWND, DWORD) PURE;

	

Далее начиная с нуля (это функция QueryInterface) отсчитываем ровно 20 функций (функция AddRef будет 1я, Release 2я и т.д.), и получаем что 20я функция в списке виртуальных функций это SetCooperativeLevel, и это наша функция в коде С++.

Так же следует отметить что негласно первым параметром при вызове виртуальной функции идет первый параметр это указатель на интерфейс. Как видим в ассемблерном коде ниже первый параметр lpdd не передается в коде С++. Первый параметр в коде С++ идет HWND окна:

	
.text:00401027                 mov     eax, lpdd
.text:0040102C                 push    eax
.text:0040102D                 mov     ecx, [edx+50h]
.text:00401030                 call    ecx