PointBlank / Project Blackout – How to start reversing / hacking it

May 25, 2013 by Johan — 14 Comments

I recently started looking into this game briefly for a friend. Since this game still seems rather popular I figured I might write something about it.
Since the engine is divided in multiple DLL-files, it makes finding out how stuff works very easy, even for the beginner at reverse-engineering.

I’ve got PointBlank Thailand (since I live there), there are multiple versions for different regions, the core engine is the same but offsets might vary.

We start by checking out the games Wikipedia page Point Blank (2008 video game) – Wikipedia
PointBlank Wikipedia
“Engine: ICube” .. never heard of this before.

Let’s start by looking at what files there are in the game folder
PointBlank files

The DLL files starting with “i3″ belong to this engine, iCube. “i3GfxDx” sounds very interesting, let’s open it up in IDA Pro

In IDA we go to Imports and we see this interesting function
PointBlank Direct3DCreate9

Double-click that, then mark the function name Direct3DCreate9, right-click it and Jump to xref to operand or press X
PointBlank D3D9 imports
In the popup dialog that appears click OK

Now we land here
PointBlank i3RenderContext::Create

.text:10033492                 call    Direct3DCreate9
.text:10033497                 mov     [esi+5364h], eax

Here it calls Direct3DCreate9 to create an instance of IDirect3D9, and puts the pointer in +5364h of the i3RenderContext class. For more information on Direct3DCreate9, take a look at MSDN; Direct3DCreate9 function definition.

Now it would be very nice to have a pointer to the i3RenderContext class the game uses. Let’s analyze other modules and see what we can find.. Here is a little play-by-play:

i3SceneDx.dll
PointBlank i3SceneDx
PointBlank i3RenderContext::isReady
PointBlank i3RenderContext* g_pRenderContext

Hey, that’s cool, now we’ve found the static pointer g_pRenderContext to i3RenderContext!

Before we move on, let’s look up the i3RenderContext::isReady(void) function in i3GfxDx.dll, we can use that later in our hack to determine if D3D is initialized yet. So open the DLL back up in IDA again, find the function and it looks like this:
PointBlank i3GfxDx i3RenderContext::isReady(void)
We can see all it does is return the value of this+28h, so we know that at 0×28 into the i3RenderContext class is the boolean value for “IsReady”. This way we can check if the renderer is ready or not before trying to hook it. :)

What we know so far?

class i3RenderContext
{
public:
	char _0x0000[40];
	__int32 isReady; //0x0028 
	char _0x002C[21304];
	IDirect3D9* pD3D; //0x5364 
	IDirect3DDevice9* pD3DDevice; //0x5368 
};

Now let’s use this knowledge to start building a PointBlank D3D hack.

First we need to build a byte pattern/signature to find the i3RenderContext* g_pRenderContext easily. Here is a memory dump:

04C8CFCF   CC               INT3
04C8CFD0   83EC 60          SUB ESP,60
04C8CFD3   A1 54EDDF04      MOV EAX,DWORD PTR DS:[4DFED54]
04C8CFD8   33C4             XOR EAX,ESP
04C8CFDA   894424 5C        MOV DWORD PTR SS:[ESP+5C],EAX
04C8CFDE   A1 BC98D504      MOV EAX,DWORD PTR DS:[4D598BC]           ; i3RenderContext * g_pRenderContext
04C8CFE3   53               PUSH EBX
04C8CFE4   8B5C24 6C        MOV EBX,DWORD PTR SS:[ESP+6C]
04C8CFE8   55               PUSH EBP
04C8CFE9   8B6C24 6C        MOV EBP,DWORD PTR SS:[ESP+6C]
04C8CFED   56               PUSH ESI
04C8CFEE   8BF1             MOV ESI,ECX
04C8CFF0   8B08             MOV ECX,DWORD PTR DS:[EAX]
04C8CFF2   FF15 9496D504    CALL DWORD PTR DS:[4D59694]              ; i3RenderContext::IsReady(void)

And here is a decent pattern for it A1 ?? ?? ?? ?? 53 8B 5C 24 ?? 55

// Define our byte pattern
BYTE patRenderContext[] = "\xA1\x00\x00\x00\x00\x53\x8B\x5C\x24\x6C\x55";
char maskRenderContext[] = "x????xxxx?x";

UINT Stride = 0;

// Our DrawIndexedPrimitive hook, this is where chams, wallhack, etc. go
typedef HRESULT ( __stdcall* tDrawIndexedPrimitive )( LPDIRECT3DDEVICE9 pDevice, D3DPRIMITIVETYPE Type, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT StartIndex, UINT PrimitiveCount );
tDrawIndexedPrimitive oDrawIndexedPrimitive = NULL;
HRESULT __stdcall hDrawIndexedPrimitive( LPDIRECT3DDEVICE9 pDevice, D3DPRIMITIVETYPE Type, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT StartIndex, UINT PrimitiveCount )
{
	if( pDevice->GetStreamSource(0, &Stream_Data, &Offset, &Stride) == D3D_OK )
		Stream_Data->Release();

	if( Stride == 40 || Stride == 44 || Stride == 52 )
	{
		// Draw chams/wallhack
	}

	return oDrawIndexedPrimitive( pDevice, Type, BaseVertexIndex, MinVertexIndex, NumVertices, StartIndex, PrimitiveCount );
}

// Function for the thread
DWORD MainThread( LPVOID lpArgs )
{
	// Wait for 'i3GfxDx.dll' to load
	while( i3GfxDx == NULL )
		i3GfxDx = GetModuleHandleA( "i3GfxDx" );
	Log->Write( "i3GfxDx %p", i3GfxDx );

	// Wait for 'i3SceneDx.dll' to load
	while( i3SceneDx == NULL )
		i3SceneDx = GetModuleHandleA( "i3SceneDx" );
	Log->Write( "i3SceneDx %p", i3SceneDx );

	// Both DLL's are loaded, let's find the static i3RenderContext pointer
	DWORD addrRenderContext = FindPattern( i3SceneDx, 0, patRenderContext, maskRenderContext, 1 ); // 1 is for +1
#ifdef DEBUG
	Log->Write( "addrRenderContext %p", addrRenderContext );
#endif

	// Wait while game initializes renderer
	while( (i3RenderContext*)addrRenderContext == NULL ||
		*(i3RenderContext**)addrRenderContext == NULL ||
		**(i3RenderContext***)addrRenderContext == NULL ||
		***(i3RenderContext****)addrRenderContext == NULL )
		Sleep(10);

	pRenderContext = ***(i3RenderContext****)addrRenderContext;
	Log->Write( "pRenderContext %p", pRenderContext );

	// The isReady function/variable we found earlier is very useful now
	while( pRenderContext->isReady == 0 )
		Sleep( 10 );
	Log->Write( "pRenderContext->isReady %d", pRenderContext->isReady );

	// And finally hook the device table, virtual functions table, midfunction hook, or whatever you use.

	// Hooking the vtable of the Direct3D9 Device is detected so I feel ok showing that:
	oDrawIndexedPrimitive = (tDrawIndexedPrimitive)VTableHook( *(DWORD*)pRenderContext->pD3DDevice, 42, (DWORD)&hDrawIndexedPrimitive );
	Log->Write( "oDrawIndexedPrimitive %p", oDrawIndexedPrimitive);
}

// DLL entry point
BOOL APIENTRY DllMain( HMODULE hModule, DWORD dwReason, LPVOID lpReserved )
{
	if( dwReason == DLL_PROCESS_ATTACH )
	{
		CreateThread( NULL, NULL, (LPTHREAD_START_ROUTINE)MainThread, NULL, NULL, NULL );
	}
	else if( dwReason == DLL_PROCESS_DETACH )
	{
	}

	return TRUE;
}

Change the vtable hook to an undetected hooking method, or add an HackShield bypass, then you should have a working D3D PointBlank hack. :)

Incoming search terms:

  • mov dword ptr ds : [pdevice] eax i3gfxdx dll project blacaut
  • apa itu hook game point blank
  • edit hook pointblank pakai ida
  • point blank logo 2013
  • source code hook point blank

Johan

Posts Twitter Facebook

Blogging out of many years of experience with gamehacking, programming and reverse-engineering. Currently freelancing in webprogramming.

14 responses to PointBlank / Project Blackout – How to start reversing / hacking it

  1. Endscene is 42 ..
    you have a DIP ?

  2. Can you give me a bypass hackshiled for blackout please…

  3. hello bro, where can i get 3RenderContext* g_pRenderContext memory dump..?

  4. Are you a Game Master or what? :D

    I have so many Question. :)) My Course is I.T.

    and I want to Learn how to Hack in Online games. Like PB PH and Cabal PH.

    Can you teach me. But I’m not Good in C++ and other Programming Languages. ^^

    • It might seem difficult to find a starting point in gamehacking, but browse the forums that exist like UnknownCheats (linked in sidebar), read what people write, look at the tutorials available to you.
      With a bit of smarts, alot of patience for trial’n’error, and a bit of programming knowledge, you will reach your goals :)

      Start small, start with easy games, set up small goals for yourself and push through on them.

      Good luck!

  5. Can you be my teacher ????

  6. I want to learn, Can I be your student ???

  7. how to dumping memory like this ??
    04C8CFCF CC INT3
    04C8CFD0 83EC 60 SUB ESP,60
    04C8CFD3 A1 54EDDF04 MOV EAX,DWORD PTR DS:[4DFED54]
    04C8CFD8 33C4 XOR EAX,ESP
    04C8CFDA 894424 5C MOV DWORD PTR SS:[ESP+5C],EAX
    04C8CFDE A1 BC98D504 MOV EAX,DWORD PTR DS:[4D598BC] ; i3RenderContext * g_pRenderContext
    04C8CFE3 53 PUSH EBX
    04C8CFE4 8B5C24 6C MOV EBX,DWORD PTR SS:[ESP+6C]
    04C8CFE8 55 PUSH EBP
    04C8CFE9 8B6C24 6C MOV EBP,DWORD PTR SS:[ESP+6C]
    04C8CFED 56 PUSH ESI
    04C8CFEE 8BF1 MOV ESI,ECX
    04C8CFF0 8B08 MOV ECX,DWORD PTR DS:[EAX]
    04C8CFF2 FF15 9496D504 CALL DWORD PTR DS:[4D59694] ; i3RenderContext::IsReady(void)

Leave a Reply