Quote:
Originally Posted by Castamere
[You must be logged in to view images. Log in or Register.]
Sure took long enough to implement. To me, that time passed was enough for me to believe staff wasn't as concerned about some of the smaller details. Time has proven me wrong.
|
Frankly this argument annoys me. You guys underestimate the difficulty of client side changes. We don't have access to Client source code. We have very limited dissassembly skillsets, and occasionally we rely on pseudocode. Haynar is better than I am at finding specific memory addresses and offsets, and I'm somewhat decent at finding/following code paths for functions to hook and modify their arguments and/or return values. Secrets is better than both of us at anything related to dissassembly, but often too busy to help us.
The Client psuedocode is 468,710 lines of code long, and most of it looks a lot like this:
Code:
//----- (0041ACE9) --------------------------------------------------------
signed int __thiscall sub_41ACE9(_DWORD *this, unsigned int a2)
{
_DWORD *v2; // esi
int v3; // edx
int v4; // eax
int v5; // edi
signed int v6; // ebx
int v7; // edi
int v8; // eax
int v9; // edi
int v10; // eax
int v11; // eax
int v12; // eax
int v13; // eax
int v14; // edi
char v15; // al
int v16; // eax
signed int v17; // ecx
int v18; // eax
int v19; // ebx
signed int v20; // edi
_DWORD *v21; // eax
_DWORD *v22; // eax
int v23; // eax
int v24; // eax
int v25; // edi
int v26; // eax
signed int v28; // [esp+Ch] [ebp-4h]
_DWORD *v29; // [esp+Ch] [ebp-4h]
v2 = this;
v28 = sub_40E722((_DWORD *)((char *)this + *(_DWORD *)(this[1] + 4) + 4), a2);
v4 = v2[2];
if ( v4
&& !*(_BYTE *)(v4 + 580)
&& !*(_BYTE *)(v4 + 598)
&& !sub_4167E2((_DWORD *)((char *)v2 + *(_DWORD *)(v2[1] + 4) + 4)) )
{
v29 = (_DWORD *)(*(int (**)(void))(*(_DWORD *)((char *)v2 + *(_DWORD *)(v2[1] + 4) + 4) + 64))();
v5 = *(_DWORD *)(sub_5EC220((_DWORD *)((char *)v2 + *(_DWORD *)(v2[1] + 4) + 8)) + 4516);
v6 = *(_DWORD *)(sub_5EC220((_DWORD *)((char *)v2 + *(_DWORD *)(v2[1] + 4) + 8)) + 4516);
if ( v6 < (unsigned __int8)sub_5ED510(v29) )
{
v7 = *(_DWORD *)(sub_5EC220((_DWORD *)((char *)v2 + *(_DWORD *)(v2[1] + 4) + 8)) + 4516);
if ( (unsigned __int8)sub_5ED510(v29) >= v7 + 2 )
v5 = *(_DWORD *)(sub_5EC220((_DWORD *)((char *)v2 + *(_DWORD *)(v2[1] + 4) + 8)) + 4516) + 2;
else
v5 = (unsigned __int8)sub_5ED510(v29);
}
v8 = sub_5EC220((_DWORD *)((char *)v2 + *(_DWORD *)(v2[1] + 4) + 8));
v9 = sub_44A448(v5, *(unsigned __int8 *)(v8 + 4508), a2, (int)v29);
if ( sub_40E722((_DWORD *)((char *)v2 + *(_DWORD *)(v2[1] + 4) + 4), a2) < v9 )
v9 = sub_40E722((_DWORD *)((char *)v2 + *(_DWORD *)(v2[1] + 4) + 4), a2);
v10 = sub_5EC220((_DWORD *)((char *)v2 + *(_DWORD *)(v2[1] + 4) + 8));
v11 = sub_4184F4(v2, a2, *(_DWORD *)(v10 + 4504));
v28 = v9;
if ( v9 <= v11 )
v28 = v11;
}
v12 = v2[2];
if ( v12 )
{
if ( !*(_BYTE *)(v12 + 580) )
{
v13 = sub_40E934((int)v2, v3, 122, 0);
v14 = v13;
if ( v13 )
{
sub_44D0D0(*(_DWORD *)(v13 + 4));
if ( v15 )
{
sub_40CBD6(*(_DWORD *)(v14 + 4));
if ( v16 )
{
if ( *(_DWORD *)(v16 + 48) == a2 )
{
v17 = *(_DWORD *)(v16 + 240);
if ( v17 > 0 && v17 < 101 )
v28 = v28 * (100 - v17) / 100;
}
}
}
}
}
}
v18 = v2[2];
v19 = 0;
if ( v18 && !*(_BYTE *)(v18 + 580) )
{
v20 = 0;
do
{
if ( sub_40CC50((_DWORD *)((char *)v2 + *(_DWORD *)(v2[1] + 4) + 4), v20) )
{
v21 = (_DWORD *)sub_40CC50((_DWORD *)((char *)v2 + *(_DWORD *)(v2[1] + 4) + 4), v20);
if ( !sub_5ECCC0(v21) )
{
v22 = (_DWORD *)sub_40CC50((_DWORD *)((char *)v2 + *(_DWORD *)(v2[1] + 4) + 4), v20);
v23 = sub_5E3210(v22, a2, v28, 1);
if ( v23 )
{
if ( v23 > v19 )
v19 = v23;
}
}
}
++v20;
}
while ( v20 < 22 );
if ( v19 )
v28 += v19;
if ( a2 == 35 )
{
if ( *(_DWORD *)(sub_5EC220((_DWORD *)((char *)v2 + *(_DWORD *)(v2[1] + 4) + 8)) + 136) )
{
v24 = sub_5EC220((_DWORD *)((char *)v2 + *(_DWORD *)(v2[1] + 4) + 8));
v25 = sub_5E3210(*(_DWORD **)(v24 + 136), 35, v28, 1);
v26 = sub_5EC220((_DWORD *)((char *)v2 + *(_DWORD *)(v2[1] + 4) + 8));
if ( sub_5ECC00(*(_DWORD **)(v26 + 136)) == 12 )
{
if ( v25 )
v28 += v25;
}
}
}
}
if ( v28 > 252 && sub_44A273(a2) )
v28 = 252;
return v28;
}
// 40CBD6: using guessed type double __stdcall sub_40CBD6(_DWORD);
// 44D0D0: using guessed type double __cdecl sub_44D0D0(_DWORD);
Now, the function I just listed above is a good example, because it handles the client's visual display of the values of skills in the skill window. In this function, it's handling a lot of processing on those values to check various caps based on class and skill ID, which it really should have no reason to do because we just want it to display what the server tells us it is.
So, we hook it. Hooking is essentially rewriting the running code in memory at the location of this function to do a JMP (Jump/Detour) to our own custom function. At which point we access the Skill Value directly, using a global client pointer to the character's data, and return it:
Code:
signed int CHooks::SkillCapCheck2_Detour(unsigned int a1) {
return ((CharData2 *)((*(CharData **)0x905D00)->ExtendedData->pCharData2))->Skill[a1];
}
And this is actually one of the easier examples.