SRL Archive - Unofficial Development

RS3 DirectX Intercept/Hook - Identifying rock types


Kimon

2015-05-08 15:07:49

Hi,

After successfully hooking DirectX, I was able to detect the whereabouts of mining rocks which were easy to identify during calls to DrawIndexedPrimitive(…). This is sufficient for most objects in the game. The various types of trees for example, are easily identified using this method. However rock types seem to be somewhat more illusive!

Running RS3 with the "Medium" graphics presets, all rocks appear to consist of two primitives. One primitive models the main body of the rock which has a common appearance across all rock types, while a second primitive models the colored segments of the rock.

As the appearance of the second primitive varies with each rock type and has a color to represent each rock type (Copper, Iron etc), this appears to be the primary point of interest. My first thought was to call GetTexture() during the DrawIndexedPrimative call and produce a CRC of the texture's data. However this produces an identical CRC regardless of the rock type.

I've since spent far too long inspecting the data obtainable with most "IDirect3DDevice9::Get…" functions, however I have not found any information which can be used to reliably differentiate between the rock types. It's also interesting to note that if I save all of the textures created during calls to CreateTexture() and those which are encountered during DrawIndexedPrimative(), none resemble the color of a Copper, Iron, Mithril rock etc, however the various grass and ground textures are clearly identifiable.

At this stage, I could have saved a great deal of time using color based techniques to identify the rock, however a DirectX solution would yield many advantages in terms of performance and reliability.

If you have successfully used OpenGL or DirectX hooking methods to identify rock types, any information would be appreciated. I have even considered that Jagex are deliberately rendering this particular part of the rock outwith DirectX haha… I'm probably just looking in the wrong place.

Thanks!

Brotein

2015-05-08 15:55:15

You should use the new OpenGL library made by Obscurity and Clarity, it's pretty good and easy to program with.

I don't know exactly what is going on with the models for the rocks, but I would imagine that the model is composed to be a dynamic object. You said the base for the rock is the same across all ore types, so I would say the model is compiled as a single model and texture. The model consisting of 2 attachments, the base and the ores themselves. The game is rendering an idle animation, and a depleted animation which displays 2 attachments for the first mentioned, and only one for the later. The texture could easily be colored by the engine as say an additive mix of the color of the ore over the texture that makes up the ore. I don't really know because I haven't seen your outputs or textures but I'm just basing it off how I've handled models like that in games before, as a 3D modeler of course. But because I think it's working with attachments based on animations(animation doesn't mean it moves just its bone position) it might not be displaying them as different models because they aren't. Like I said, just my thoughts not sure what they really do.

Kimon

2015-05-08 16:21:35

From: Brotein
You should use the new OpenGL library made by Obscurity and Clarity, it's pretty good and easy to program with.

I don't know exactly what is going on with the models for the rocks, but I would imagine that the model is composed to be a dynamic object. You said the base for the rock is the same across all ore types, so I would say the model is compiled as a single model and texture. The model consisting of 2 attachments, the base and the ores themselves. The game is rendering an idle animation, and a depleted animation which displays 2 attachments for the first mentioned, and only one for the later. The texture could easily be colored by the engine as say an additive mix of the color of the ore over the texture that makes up the ore. I don't really know because I haven't seen your outputs or textures but I'm just basing it off how I've handled models like that in games before, as a 3D modeler of course. But because I think it's working with attachments based on animations(animation doesn't mean it moves just its bone position) it might not be displaying them as different models because they aren't. Like I said, just my thoughts not sure what they really do.


Thanks for your reply!

I had suspected that a common base texture was in use across all ores, the appearance of which is then altered. This is certainly what the texture CRCs suggest.

Interestingly enough, if I pass my own texture to SetTexture before the DrawIndexedPrimative call, for example, a bright pink texture, there is still a noticeable difference in color between the rock types. They are different shades of pink. This would suggest that another influence is at play, perhaps a pixel shader or vertex shader. After a fair bit of digging, around inside the VertexShader and PixelShaders at the time of the DrawIndexedPrimitive call, I was unable to find any uniquely identifiable data.

I'll be sure to provide some screenshots when I reach the 5 post requirement.

For clarity, there appears to be two calls to DrawIndexedPrimitive for each rock; one for the base and another to represent the colored ore.

The Mayor

2015-05-08 17:21:44

From: Kimon
Thanks for your reply!

I had suspected that a common base texture was in use across all ores, the appearance of which is then altered. This is certainly what the texture CRCs suggest.

Interestingly enough, if I pass my own texture to SetTexture before the DrawIndexedPrimative call, for example, a bright pink texture, there is still a noticeable difference in color between the rock types. They are different shades of pink. This would suggest that another influence is at play, perhaps a pixel shader or vertex shader. After a fair bit of digging, around inside the VertexShader and PixelShaders at the time of the DrawIndexedPrimitive call, I was unable to find any uniquely identifiable data.

I'll be sure to provide some screenshots when I reach the 5 post requirement.

For clarity, there appears to be two calls to DrawIndexedPrimitive for each rock; one for the base and another to represent the colored ore.


I'm not sure if you've seen Brandon's GitHub project: https://github.com/Brandon-T/Genevieve

And these posts about dx hooks: https://villavu.com/forum/showthread.php?t=104932&page=7&p=1312438#post1312438

Kimon

2015-05-08 20:53:42

From: The Mayor
I'm not sure if you've seen Brandon's GitHub project: (URL)

And these posts about dx hooks: (URL)


Indeed, I spotted that thread a few days back. Very interesting stuff!

These replies in particular:

From: riwu

Is there any extra info to hook from models? Different type of mining rocks have the same model ID, so i have to find color within a box of the model point returned, which makes the ore finding more color-based than hooking based…


From: Brandon
There is.. but I don't have much time to hook it or dive into hooking it. I heard Jagex fixed OpenGL bugs, yeah?

Everything in RS has a texture. That includes models. If you hook model textures and save them, you'd hash them, you'd have unique ID's per rock model. You could also hook the vertices for each model. Similar to the Direct-X hook code that's on github.


As far as I have been able to investigate, it looks like all rock types use the same texture. (I could be wrong!)
It's possible that calling IDirect3DDevice9::GetTexture() during the DrawIndexedPrimitive call is insufficient, however from the perspective of DrawIndexedPrimitive, the vertex shader function data and pixel shader function data also yield no unique data.

I would love to hear @Brandon's input regarding the rock textures, and indeed any method that can be used to identify the various types of rock.

Brandon

2015-05-08 21:39:10

From: Kimon
Indeed, I spotted that thread a few days back. Very interesting stuff!

These replies in particular:





As far as I have been able to investigate, it looks like all rock types use the same texture. (I could be wrong!)
It's possible that calling IDirect3DDevice9::GetTexture() during the DrawIndexedPrimitive call is insufficient, however from the perspective of DrawIndexedPrimitive, the vertex shader function data and pixel shader function data also yield no unique data.

I would love to hear @Brandon's input regarding the rock textures, and indeed any method that can be used to identify the various types of rock.



1 word: Interleaving. DrawIndexedPrimitive is used because textures, colours and vertices can be interleaved and rendered in a single call.

Example, a vertex can be represented by:


typedef struct
{
float X, Y, Z, RHW;
unsigned int Colour;
float U, V;
} D3DVertex;

OR:


typedef struct
{
float X, Y, Z;
} D3DVertex;

OR other variations. But again, you can see that the "Colour" field is the colour of THAT specific vertex. If Jagex uses an array of said structure (and they do in some places), the colour would be the representation for EACH vertex; where U, V are the normals and RHW is the Reciprocal Homogeneous W.

Also, textures are very very tricky to hook so make sure you actually have it done right. Otherwise that would be one reason all the hashes are the same or 0.

Kimon

2015-05-09 11:48:23

From: Brandon
1 word: Interleaving. DrawIndexedPrimitive is used because textures, colours and vertices can be interleaved and rendered in a single call.

Example, a vertex can be represented by:


typedef struct
{
float X, Y, Z, RHW;
unsigned int Colour;
float U, V;
} D3DVertex;

OR:


typedef struct
{
float X, Y, Z;
} D3DVertex;

OR other variations. But again, you can see that the "Colour" field is the colour of THAT specific vertex. If Jagex uses an array of said structure (and they do in some places), the colour would be the representation for EACH vertex; where U, V are the normals and RHW is the Reciprocal Homogeneous W.

Also, textures are very very tricky to hook so make sure you actually have it done right. Otherwise that would be one reason all the hashes are the same or 0.


Hi Brandon,

I do appreciate the reply.

I'm not entirely sure if this is what you had in mind however:

I've been using this code to traverse the vertex data. The "value" variable can be incremented and decremented at runtime with key presses. For each of the rocks, we then hash the data at "pVoid + value" with size "sizeof(unsigned int)". The hashed value is displayed above the corresponding rocks using D3DTextF().

Using this method, I have been able to identify the rotation of the rock models however the vertex structure in use does not appear to contain any color information. (As far as I can see!)


//Prototype of crc32()
DWORD crc32(unsigned char* data, unsigned DWORD length);

DWORD value = 0;
HRESULT WINAPI hkDrawIndexedPrimitive(LPDIRECT3DDEVICE9 pDevice, D3DPRIMITIVETYPE Type, int BaseVertexIndex, UINT MinIndex, UINT NumVertices, UINT StartIndex, UINT PrimitiveCount)
{
//Calculate IDs etc (Omitted for clarity)
...

//Increment/decrement value when keys are pressed
...

//5308636 and 5767341 are the rock ore primitives.
if (ID == 5308636 || ID == 5767341)
{
IDirect3DVertexBuffer9* vertexBuffer;
UINT offset;
UINT stride;

if (pDevice->GetStreamSource(0, &vertexBuffer, &offset, &stride) == D3D_OK)
{
void* pVoid;

D3DVERTEXBUFFER_DESC desc;
vertexBuffer->GetDesc(&desc);

vertexBuffer->Lock(StartIndex, value + sizeof(unsigned int), (void**)&pVoid, 0);

DWORD crc = crc32((BYTE*)pVoid + value, sizeof(unsigned int));
D3DTextF(D3DCOLOR_XRGB(0, 255, 0), x, y, "%lu", crc);

vertexBuffer->Unlock();
}
}

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

With regard to the textures, I have not attempted to create a proxy class such as your IDirect3DBaseTexture9Proxy from the Genevieve project however I may pursue that next if it seems like the way to go.


//Prototype of crc32()
DWORD crc32(unsigned char* data, unsigned DWORD length);

DWORD value = 0;
HRESULT WINAPI hkDrawIndexedPrimitive(LPDIRECT3DDEVICE9 pDevice, D3DPRIMITIVETYPE Type, int BaseVertexIndex, UINT MinIndex, UINT NumVertices, UINT StartIndex, UINT PrimitiveCount)
{
//Calculate IDs etc (Omitted for clarity)
...

//Increment/decrement value when keys are pressed
...

//5308636 and 5767341 are the rock ore primitives.
if (ID == 5308636 || ID == 5767341)
{
IDirect3DBaseTexture9* baseTexture;
pDevice->GetTexture(0, &baseTexture);

IDirect3DTexture9* texture = (IDirect3DTexture9*)baseTexture;
D3DLOCKED_RECT lockedRect;

if (texture->LockRect(0, &lockedRect, NULL, D3DLOCK_READONLY) == D3D_OK)
{
DWORD *pData = (DWORD*)lockedRect.pBits;

D3DSURFACE_DESC desc;
texture->GetLevelDesc(0, &desc);

DWORD crc = crc32((BYTE*)pData, value);
//DWORD crc = crc32((BYTE*)pData + value, 4);

D3DTextF(D3DCOLOR_XRGB(0, 255, 0), x, y, "%lu", crc);
}

texture->UnlockRect(0);
}

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

Using various mutations of the above code, each rock has the same texture hash, however other textures such as ground textures have different hashes. Again, the "value" variable is incremented and decremented at runtime to explore the data.

Brandon

2015-05-09 16:44:09

From: Kimon
…



Your code now exhibits undefined behaviour. There was a time when they used interleaved structures (which is why I suggested that) and they still do in OpenGL but it seems in Direct-X, ALL strides are "12" (X, Y, Z) * (sizeof(float)). It wasn't always this way (tightly packed).

So you're right, they're rendering their textures another way. I'll have to look into it to see how.


As for why your code is undefined behaviour? pDevice->GetTexture(0, &tex) returns a texture of size 1 x 1. Calculating a CRC32 hash on anything except that ONE pixel is undefined. So if you're "increasing value", you're causing UB.

Using the texture wrapper from the github, you can save the width and height of the texture upon creation and it turns out to be 21 x 3.

However, ALL the textures point to the same memory location: http://i.imgur.com/trg7pjk.png

The 1 x 1 texture is just an alpha texture to tell the graphics card to render everything with a white tint (0xFFFFFFFF).

If you change the texture, everything will render to whatever texture you apply (purple in my post).


Basically, model hooks are a lot easier now, but texture hooks are harder and require more debugging of the client.


Animated textures are rendered with "DrawPrimitive" you can hook that and see the sparkle on the rock ID.
Models ONLY are rendered with "DrawIndexedPrimitive" you can hook that for models and do a hash on the vertices.

For regular textures, I have to look into it.

Kimon

2015-05-09 17:08:04

From: Brandon
Your code now exhibits undefined behaviour. There was a time when they used interleaved structures (which is why I suggested that) and they still do in OpenGL but it seems in Direct-X, ALL strides are "12" (X, Y, Z) * (sizeof(float)). It wasn't always this way (tightly packed).

So you're right, they're rendering their textures another way. I'll have to look into it to see how.


As for why your code is undefined behaviour? pDevice->GetTexture(0, &tex) returns a texture of size 1 x 1. Calculating a CRC32 hash on anything except that ONE pixel is undefined. So if you're "increasing value", you're causing UB.

Using the texture wrapper from the github, you can save the width and height of the texture upon creation and it turns out to be 21 x 3.

However, ALL the textures point to the same memory location: (url)

The 1 x 1 texture is just an alpha texture to tell the graphics card to render everything with a white tint (0xFFFFFFFF).

If you change the texture, everything will render to whatever texture you apply (purple in my post).


Basically, model hooks are a lot easier now, but texture hooks are harder and require more debugging of the client.


Animated textures are rendered with "DrawPrimitive" you can hook that and see the sparkle on the rock ID.
Models ONLY are rendered with "DrawIndexedPrimitive" you can hook that for models and do a hash on the vertices.

For regular textures, I have to look into it.


This is very interesting stuff!

Here's what I was talking about earlier, and indeed, evidence of what you've said above. Notice that the Iron ore appears to be almost a purple color, while a noticeably different color is applied to the Copper rocks.

After applying a pink texture:
[ATTACH=CONFIG]25855[/ATTACH]
(Click to enlarge)

And here's the code used to produce this:

HRESULT WINAPI hkDrawIndexedPrimitive(LPDIRECT3DDEVICE9 pDevice, D3DPRIMITIVETYPE Type, int BaseVertexIndex, UINT MinIndex, UINT NumVertices, UINT StartIndex, UINT PrimitiveCount)
{
//5308636 and 5767341 are the rock ore primitives.
if (ID == 5308636 || ID == 5767341)
{
LPDIRECT3DTEXTURE9 pinkTexture;

const BYTE bPink[58] =
{
0x42, 0x4D, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0x00, 0xFF, 0x00
};

D3DXCreateTextureFromFileInMemory(pDevice, (LPCVOID)&bPink, 58, &pinkTexture);
pDevice->SetTexture(0, pinkTexture);
}

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

Brandon

2015-05-09 21:34:04

From: Kimon
…



Yup. That's what I meant. It's a tint over the entire client. Notice how all the RED ID's below are the same texture ID.

I wrote the ID generation for textures just now to see if ALL textures would show up but it seems models are being applied a module texture which cannot be locked (button says OpenGL but it's direct-x, SRL loads both plugins):



I'll figure it out soon but this is one step closer than I was a couple hours ago (these are all TEXTURE IDs [CRC32].. not model IDs). I believe model textures might have to have their ID's generated on start and may render with a shader rather than SetTexture.


Green ones are regular textures of type: D3DRTYPE_TEXTURE, red ones seem to be materials or volumetric or some other type of texture.. white ones are textures used in animations (sparkles on the rocks).

More debugging is required to see what calls are being made for model textures.

My logs look like:


SetTexture
DrawIndexedPrimitive
SetTexture
SetSamplerState
SetSamplerState
DrawIndexedPrimitive
SetTexture
SetSamplerState
SetSamplerState
DrawIndexedPrimitive
SetTexture
DrawIndexedPrimitive
SetTexture
DrawIndexedPrimitive


SetTexture
DrawIndexedPrimitive
TestCooperativeLevel
SetDepthStencilSurface
SetStreamSource
SetTexture
SetIndices

Kimon

2015-05-09 22:19:01

From: Brandon

…


Materials appeared to be somewhat of a dead end. Assuming that I have correctly hooked SetMaterial, it would seem that the function is never called.

I do wonder if the developers have come to this arrangement to deter our efforts, or indeed, if there is some other productive explanation.

Brandon

2015-05-10 00:47:10

From: Kimon
..


They are doing:


IDirect3DDevice9_SetStreamSource(00000000, 3F2EA3A0, 00000000, 0000000C)
IDirect3DDevice9_SetStreamSource(00000001, 3D850240, 00000000, 00000004)
IDirect3DDevice9_SetStreamSource(00000002, 3D850680, 00000000, 00000008)
IDirect3DDevice9_SetIndices(2B166B00)
IDirect3DDevice9_SetStreamSource(00000003, 3D8515A0, 00000000, 0000000C)
IDirect3DDevice9_SetTexture(00000000, 3123DEE0)
IDirect3DDevice9_SetSamplerState(00000000, 00000002, 00000003)
IDirect3DDevice9_SetVertexShaderConstantF(00000000, 306C2020, 0000000C)
IDirect3DDevice9_SetPixelShaderConstantF(00000000, 306C2020, 00000005)
IDirect3DDevice9_DrawIndexedPrimitive(00000004, 00000000, 00000000, 00000082, 00000000, 0000004E)

.
.
.

IDirect3DDevice9_SetTexture(00000000, 3D8D4880)
IDirect3DDevice9_SetStreamSource(00000000, 31B573A0, 00000000, 00000020)
IDirect3DDevice9_SetVertexDeclaration(310440A0)
IDirect3DDevice9_SetVertexShaderConstantF(00000000, 306C2020, 00000006)
IDirect3DDevice9_SetPixelShaderConstantF(00000000, 306C2020, 00000001)
IDirect3DDevice9_DrawPrimitive(00000006, 00000000, 00000002)


So yeah.. they are rendering the texture but using a shader to do it now. If you call pDevice->SetVertexShader(nullptr), you will notice that – that one section of the model that contains the ore is "hollow".

You can disassemble the shader and see what parameters they are passing to it. Grab the colour(s) from the shader stream source.

Take note of the stream numbers: 0, 1, 2, 3.
3-4 streams in use at any given time. The same thing is done in OpenGL except they interleave it.

The Mayor

2015-05-10 02:01:36

This is great stuff :D

Kimon

2015-05-10 10:14:57

From: Brandon

You can disassemble the shader and see what parameters they are passing to it. Grab the colour(s) from the shader stream source.

Take note of the stream numbers: 0, 1, 2, 3.
3-4 streams in use at any given time. The same thing is done in OpenGL except they interleave it.


Here's a dump of the shader from pDevice->GetVertexShader() at the time of the DrawIndexedPrimitive call:


//
// Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022
//
// Parameters:
//
// float3 AmbientColour;
// float3 AntiSunColour;
// float4 DistanceFogPlane;
// float3 EyePos;
// float4 HeightFogPlane;
// float3 SunColour;
// float3 SunDir;
// float4x2 TexCoordMatrix;
// float4x4 WVPMatrix;
//
//
// Registers:
//
// Name Reg Size
// ---------------- ----- ----
// WVPMatrix c0 4
// TexCoordMatrix c4 2
// EyePos c6 1
// DistanceFogPlane c7 1
// HeightFogPlane c8 1
// SunDir c9 1
// SunColour c10 1
// AntiSunColour c11 1
// AmbientColour c12 1
//

vs_2_0
def c13, 0, 1, 0, 0
dcl_position v0
dcl_texcoord v1
dcl_normal v2
dcl_color v3
dp4 oPos.x, v0, c0
dp4 oPos.y, v0, c1
dp4 oPos.z, v0, c2
dp4 oPos.w, v0, c3
dp4 oT0.x, v1, c4
dp4 oT0.y, v1, c5
dp3 r0.x, v2, c9
max r0.y, r0.x, c13.x
max r0.x, -r0.x, c13.x
min r0.xy, r0, c13.y
mov r1.xyz, c10
mad r0.yzw, r0.y, r1.xxyz, c12.xxyz
mad oT2.xyz, r0.x, c11, r0.yzww
add r0.xyz, -v0, c6
dp3 r0.w, r0, r0
rsq r0.w, r0.w
mul oT1.xyz, r0.w, r0
dp4 r0.x, v0, c8
max r0.x, r0.x, c13.x
min oT4.x, r0.x, c13.y
dp4 r0.x, v0, c7
max r0.x, r0.x, c13.x
min oT4.y, r0.x, c13.y
mov oD0, v3
mov oT0.zw, c13.x
mov oT3.xyz, v2
mov oT5, c13.x
mov oT6.xyz, c13.x
mov oT7, c13.x

// approximately 29 instruction slots used


I would assume out of that, "float3 AmbientColour" (C12) is the primary point of interest. However I'm not entirely sure how to read from the "shader stream source", are calls to SetStreamSource used to set the stream source for shaders as well as vertex buffers? My knowledge of DirectX is somewhat limited haha

From reading around, the most likely function seemed to be IDirect3DVertexShader9::GetFunction which "gets a pointer to the shader data" as shown here: https://msdn.microsoft.com/en-us/library/windows/desktop/bb205924%28v=vs.85%29.aspx

I also found this: "Stream-output data can be read by programmable shaders using load functions (such as Load)."
Here: https://msdn.microsoft.com/en-us/library/windows/desktop/bb205121%28v=vs.85%29.aspx

Which could possibly be somewhere along the right lines?

Brandon

2015-05-10 20:29:17

From: Kimon
Which could possibly be somewhere along the right lines?


Sorry for the late reply.


Mat4 wvp;
pDevice->GetVertexShaderConstantF(0, reinterpret_cast<float*>(&wvp), 4);

In the above, we are accessing register C0 for the WorldViewMatrix. It is of size 4 and is a float4x4 matrix.

That's all there is to it. If you wanted to access the data BEFORE the vertex shader gets it, just hook SetStreamSource and iterate it pulling out whatever you need from it OR call GetStreamSource(StreamNumber, &vbo) in DrawIndexedPrimitive.

There are 3 or 4 streams in use at any given time. If I had to guess, stream 1 is the colours of every vertex of the model. I made this assumption based on the stride for stream 1.


IDirect3DDevice9_SetStreamSource(00000001, 3D850240, 00000000, 00000004)

The stride is 4 and stream number is 1. A colour consists of 4 components (ARGB).

Stream 2 is texCoords or Normals (most likely normals [U, V]).


IDirect3DDevice9_SetStreamSource(00000002, 3D850680, 00000000, 00000008)

I made this assumption based on its stride as well. The stride is 8: X, Y – where both are float – 4 bytes each.

Finally, Stream 0 or 3 is the vertices (stride of 12 – X, Y, Z – where all are float – 4 bytes each).


I don't have time to test all of this atm, but I'm fairly confident, this is the case. I will test it during the week.

Kimon

2015-05-11 00:02:09

From: Brandon
Sorry for the late reply.

Mat4 wvp;
pDevice->GetVertexShaderConstantF(0, reinterpret_cast<float*>(&wvp), 4);

In the above, we are accessing register C0 for the WorldViewMatrix. It is of size 4 and is a float4x4 matrix.

That's all there is to it. If you wanted to access the data BEFORE the vertex shader gets it, just hook SetStreamSource and iterate it pulling out whatever you need from it OR call GetStreamSource(StreamNumber, &vbo) in DrawIndexedPrimitive.

There are 3 or 4 streams in use at any given time. If I had to guess, stream 1 is the colours of every vertex of the model. I made this assumption based on the stride for stream 1.


IDirect3DDevice9_SetStreamSource(00000001, 3D850240, 00000000, 00000004)

The stride is 4 and stream number is 1. A colour consists of 4 components (ARGB).

Stream 2 is texCoords or Normals (most likely normals [U, V]).


IDirect3DDevice9_SetStreamSource(00000002, 3D850680, 00000000, 00000008)

I made this assumption based on its stride as well. The stride is 8: X, Y – where both are float – 4 bytes each.

Finally, Stream 0 or 3 is the vertices (stride of 12 – X, Y, Z – where all are float – 4 bytes each).


I don't have time to test all of this atm, but I'm fairly confident, this is the case. I will test it during the week.


Thanks Brandon, very useful information as always.

I've used GetPixelShaderConstantF to retrieve the coordinates of the model which can then be transformed to produce normalized device coordinates, thus allowing the IDs to be drawn on the screen etc. I didn't have any luck using GetPixelShaderConstantF for anything color related though, so I turned to the stream source.

So far, I have only focused on stream 1, which as you said, has the tell-tail stride of 4.

This code:

HRESULT WINAPI hkDrawIndexedPrimitive(LPDIRECT3DDEVICE9 pDevice, D3DPRIMITIVETYPE Type, int BaseVertexIndex, UINT MinIndex, UINT NumVertices, UINT StartIndex, UINT PrimitiveCount)
{
//Calculate IDs etc (Omitted for clarity)
...

//Increment/decrement value when keys are pressed
...

//5308636 and 5767341 are the rock ore primitives.
if (ID == 5308636 || ID == 5767341)
{
IDirect3DVertexBuffer9* vertexBuffer;
UINT offset;
UINT stride;

if (pDevice->GetStreamSource(1, &vertexBuffer, &offset, &stride) == D3D_OK)
{
char* pVerts;

//I've locked the entire stream for now to avoid potential mistakes with offsets and lengths
vertexBuffer->Lock(0, 0, (void**)&pVerts, D3DLOCK_READONLY);

D3DXCOLOR color = *reinterpret_cast<D3DXCOLOR*>(&pVerts[(stride * value) + offset]);

DrawTextF(D3DCOLOR_XRGB(0, 255, 0), x, y, "%f, %f, %f, %f", color.r, color.g, color.b, color.a);

vertexBuffer->Unlock();
}
}

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

Results in this:

So it's safe to say I've gone wrong somewhere lmao

I haven't had a chance to approach the problem from the perspective of SetStreamSource (hooked) as I'm hooking quite late, after the client loads. If I have time, I'll probably persevere with the above code and indeed, look into wrapping IDirect3DDevice9 with hope of hooking SetStreamSource thereafter.

Thanks again!

Method

2015-05-11 00:12:24

From: Brandon
Sorry for the late reply.


Mat4 wvp;
pDevice->GetVertexShaderConstantF(0, reinterpret_cast<float*>(&wvp), 4);

In the above, we are accessing register C0 for the WorldViewMatrix. It is of size 4 and is a float4x4 matrix.

That's all there is to it. If you wanted to access the data BEFORE the vertex shader gets it, just hook SetStreamSource and iterate it pulling out whatever you need from it OR call GetStreamSource(StreamNumber, &vbo) in DrawIndexedPrimitive.

There are 3 or 4 streams in use at any given time. If I had to guess, stream 1 is the colours of every vertex of the model. I made this assumption based on the stride for stream 1.


IDirect3DDevice9_SetStreamSource(00000001, 3D850240, 00000000, 00000004)

The stride is 4 and stream number is 1. A colour consists of 4 components (ARGB).

Stream 2 is texCoords or Normals (most likely normals [U, V]).


IDirect3DDevice9_SetStreamSource(00000002, 3D850680, 00000000, 00000008)

I made this assumption based on its stride as well. The stride is 8: X, Y – where both are float – 4 bytes each.

Finally, Stream 0 or 3 is the vertices (stride of 12 – X, Y, Z – where all are float – 4 bytes each).


I don't have time to test all of this atm, but I'm fairly confident, this is the case. I will test it during the week.


If it's still the same as it was for client build #666 , then you're pretty much spot on. Stream 0 is positions, 1 is colors, 2 is texcoords, and 3 (if present) is normals.

Brandon

2015-05-11 09:50:32

From: Method
If it's still the same as it was for client build #666 , then you're pretty much spot on. Stream 0 is positions, 1 is colors, 2 is texcoords, and 3 (if present) is normals.



:D Perfect! This is good news. Thanks a lot! I will have to test whether its the same though (maybe on the weekend).


From: Kimon
…


Try:


std::uint32_t GetColour(std::uint8_t *colours, std::int16_t index, std::uint16_t stride)
{
std::uint32_t res = 0;
memcpy(&res, &colours[index * stride], sizeof(std::uint32_t));
return res;
}


HRESULT IDirect3DDevice9Proxy::DrawIndexedPrimitive(D3DPRIMITIVETYPE PrimitiveType, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT startIndex, UINT primCount)
{
//Get stream 1 (VBO) & its IBO.
//pDevice->GetIndices(&ibo);

std::uint8_t *colours = NULL;
std::uint16_t *indices = NULL;
UINT EndIndex = startIndex + (primCount * 3);

VertexBuffer->Lock(0, 0, reinterpret_cast<void **>(&colours), 0);
IndexBuffer->Lock(0, primCount * 3 * sizeof(std::uint16_t), reinterpret_cast<void **>(&indices), 0);

for (UINT I = startIndex; I < EndIndex; ++I)
{
std::uint32_t colour = GetColour(colours, indices[I] + BaseVertexIndex, Stride);
}

IndexBuffer->Unlock();
VertexBuffer->Unlock();


return pDevice->DrawIndexedPrimitive(PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
}


You may or may not need the index buffer. Again, I have to test but I only get weekends off from work so.. you can mess around with it and report back.

Also, the colour is NOT a float. I said: ARGB which if the stride is four, becomes an unsigned integer. If the stride was 16, then EACH component of the ARGB colour would be a float. However, that's not the case. I might have been confusing in my previous post since I rushed to type it out but hopefully this clears things up.

To split the unsigned integer representation into its components, you can use a union:


typedef union
{
std::uint8_t A, R, G, B;
std::uint32_t Colour;
} ARGB;

std::uint8_t R = reinterpret_cast<ARGB*>(&your_colour_here)->R;
std::uint8_t G = reinterpret_cast<ARGB*>(&your_colour_here)->G;
std::uint8_t B = reinterpret_cast<ARGB*>(&your_colour_here)->B;


//OR:

ARGB col = {0};
col.Colour = your_colour_here;

std::uint8_t R = col.R;
std::uint8_t G = col.G;
std::uint8_t B = col.B;

Kimon

2015-05-11 11:01:09

From: Brandon

…



I don't think there's too much more to be said really! lmao

There appears to be a slight color shift/randomization between logins. It was fairly trivial to get around by searching the vertice colors with a tolerance (+/- 2 seems to be sufficient). In reality, we only need to determine the color of the model once so this seems like a great solution.

The Mayor

2015-05-11 11:02:27

From: Kimon

I don't think there's too much more to be said really! lmao

There appears to be a slight color shift/randomization between logins. It was fairly trivial to get around by searching the vertice colors with a tolerance (+/- 2 seems to be sufficient). In reality, we only need to determine the color of the model once so this seems like a great solution.


That is the coolest thing I have seen 2k15! What are you planning to do from here?

Kimon

2015-05-11 11:35:42

From: The Mayor
That is the coolest thing I have seen 2k15! What are you planning to do from here?


Hopefully build a functional bot from the ground up.

I've always been concerned that known bot clients could easily be detected, or at least draw some attention if known fixed parameters such as the client's resolution became known. I haven't used SMART for a good few years now, I could have sworn at one point the resolution was fixed. (Maybe it still is?) This is probably just paranoia but anything we can do to blend in has to be good haha

I'm not sure if this approach has been taken before, however I've been building upon the RuneScape client source (which you can find here). As far as clients go, I don't think we can get any closer to "the real thing" than this.

Now that we have a way to detect the models, their position and their variants, I'll probably look into navigation and eventually other requirements such as mouse hooks. There's an interesting texture that seems to display a large area around you:
This could hopefully be used to pinpoint your current world position. If there seems to be a lack of less expensive options, we could initially locate this sub-section of the map (using an image of the full map). Thereafter, we would most likely keep track of movement using the player's surroundings.

A few years back, I had a look into HAAR training to track models using OpenCV. It turned out to be semi-reliable, but too expensive for use within a finished bot. I dare say template matching with OpenCV could be used to establish the player's location (initially).

Perhaps there's a much better alternative, some information that is accessible via DirectX.

And so it begins!

Brandon

2015-05-11 14:10:35

From: Kimon
I don't think there's too much more to be said really! lmao




:O Was I right? OR??

Btw (just a "suggestion"), to calculate things once, use a wrapper. I say this because for textures, instead of calling "GetTexture" every single time and doing a hash on it in DrawIndexedPrimitive, I wrote a wrapper for it and I give it to the client. When the client calls "Unlock", I hash the texture and store its ID.

This is only done when the texture is created by the client OR modified. That way, every frame (DrawIndexedPrimitive), you can just do:

pDevice->GetTexture(….)->GetHash();

:) Did the same thing for models and it works brilliantly. 50-FPS.

Kimon

2015-05-11 14:27:07

From: Brandon
:O Was I right? OR??


You were spot on!

It's a shame there's some randomisation at play. We could otherwise produce a simple hash but the color search works just as well. I haven't encountered any issues with the accuracy.

The Mayor

2015-05-11 14:33:55

50 FPS sure does sound better than 5 FPS

Thomas

2015-05-16 14:27:13

From: Brandon

:) Did the same thing for models and it works brilliantly. 50-FPS.


Do you have any ETA on a directx plugin?
Having allot of trouble with the FPS drop after a few hours within OGL..

Brandon

2015-05-16 15:48:13

From: lovebotter
Do you have any ETA on a directx plugin?
Having allot of trouble with the FPS drop after a few hours within OGL..



I don't have an ETA and I won't and I'm sorry for that. Something like a Direct-X hook plugin by myself would take quite a bit of free time. I don't have that luxury atm. When I started on it, I had tons of free time and I could have finished it or released a beta plugin but I don't have that time anymore.

It's not hard, it's just I don't have time. It could take a weekend at most to get a beta out and then bug fixes, feedback, suggestions would begin and take more time than an initial release.

When the current project at work cools down and I don't have to work weekends and weekdays and overtime, then I can probably put in quite a bit of work on a plugin base (models, textures, text, etc). I'm actually working right now ( on my weekend :l ).

The little time I have, I visit the forums to answer questions or help out where I can otherwise I spend it exercising so I don't become a fat programmer.


To complete a beta plugin.. Basically:

1. Override IDirect3DTexture9's lock and unlock functions to do a hash of the pixels.
2. Override IDirect3DDevice9's DrawPrimitive and DrawIndexedPrimitive to get the current texture into an std::vector<IDirect3DDevice9*>.
3. Override IDirect3DVertexBuffers' lock and unlock functions to do a hash of the vertices, colours, or normals.
4. Override IDirect3DDeviceSwapChain9's Present function to feed the data to Simba or Draw the ID's on screen.
5. Override IDirect3DDevice9's Reset function to font reset and restore for safe ID drawing.
6. Debug and test everything then release a beta plugin + source.

The first is already done.
The second is already done.
The third is partially done (see this thread for more details above [only thing missing is the overriding and adding other functionality]).
The fourth is no where in sight but can be copied from the OpenGL 4.0 plugin source.
The fifth is already done.
The sixth you do every time you add new functionality.

Thomas

2015-05-16 16:10:31

Thanks for the detailed reply!

Wouldnt copying the fourth part from the OGL plugin reproduce the same lag experienced?

Brandon

2015-05-16 16:16:23

From: lovebotter
Thanks for the detailed reply!

Wouldnt copying the fourth part from the OGL plugin reproduce the same lag experienced?


Nope. Communication is not the source of lag in OpenGL. It's the design of the plugin and the mass amount of patches since its very first release. The latest patches reduced that lag a lot but it isn't enough. I have to implement the new way of determining what fonts are rendered (described on the OpenGL thread). Changing the storage of fonts for OpenGL plugin will increase the speed drastically.

Thomas

2015-05-16 17:27:01

From: Brandon
Nope. Communication is not the source of lag in OpenGL. It's the design of the plugin and the mass amount of patches since its very first release. The latest patches reduced that lag a lot but it isn't enough. I have to implement the new way of determining what fonts are rendered (described on the OpenGL thread). Changing the storage of fonts for OpenGL plugin will increase the speed drastically.


I'm currenlty only using getModel in my script though? Yet it still starts laging horribly after a couple of hours. I'll pm you the script later if that's of any help.

Brandon

2015-05-23 13:11:33

From: lovebotter
I'm currenlty only using getModel in my script though? Yet it still starts laging horribly after a couple of hours. I'll pm you the script later if that's of any help.



I'll release a beta plugin for Direct-X this weekend or sometime this week (I'll try to aim for tomorrow or tonight). I started writing the code for sending information to Simba. It'll be on github shortly. You won't have any lag.

The OpenGL plugin will take way more time (even though I already wrote most of it for OSX and Linux, the Windows part is a pain).. It's not as easy as Direct-X and requires more attention to detail and efficiency (not that Direct-X doesn't require the same, but it's a lot easier to maintain).

Thomas

2015-05-23 14:04:36

From: Brandon
I'll release a beta plugin for Direct-X this weekend or sometime this week (I'll try to aim for tomorrow or tonight). I started writing the code for sending information to Simba. It'll be on github shortly. You won't have any lag.

The OpenGL plugin will take way more time (even though I already wrote most of it for OSX and Linux, the Windows part is a pain).. It's not as easy as Direct-X and requires more attention to detail and efficiency (not that Direct-X doesn't require the same, but it's a lot easier to maintain).

That is great news! Hope it works out because my current dragon pickaxe farming script (srl-6 based) already missed out on 2 dpicks (out of 37 though). That shouldnt happen with ogl/directx.

Clarity

2015-05-23 14:18:56

Sounds like it's time to write dxLib :)