Here is a quick and dirty tutorial on how I found the OP_Charm opcode for Titanium, based on information in the Wiki on
disassembly using IDA.
Requirements: The 6.2 Client, The Titanium Client, and an OpCode that is known for the 6.2 client
but unknown in Titanium (this is what we want to find).
Download IDA Freeware 4.9 from:
http://www.hex-rays.com/idapro/idadown.htm
Fire up IDA, Select 'New', Select PE Executable, then navigate to your 6.2 eqgame.exe, then let IDA
analyse it. This will take several minutes. It will say 'Idle' in the status bar when it has finished.
In 6.2, Opcodes are handled in the 'Dispatch' routine at address 45A8B6.
To Jump to this address, press G and enter the address 45A8B6.
The routine looks like this ( I added the comments down near the bottom):
Code:
.text:0045A8B6
.text:0045A8B6 sub_45A8B6 proc near ; CODE XREF: sub_407D2B+27p
.text:0045A8B6
.text:0045A8B6 var_5980 = dword ptr -5980h
.text:0045A8B6 var_597C = dword ptr -597Ch
.text:0045A8B6 var_5978 = dword ptr -5978h
.text:0045A8B6 var_5974 = dword ptr -5974h
.text:0045A8B6 var_5970 = dword ptr -5970h
.text:0045A8B6 var_596C = dword ptr -596Ch
.text:0045A8B6 var_5968 = dword ptr -5968h
.text:0045A8B6 var_5964 = dword ptr -5964h
.text:0045A8B6
.text:0045A8B6 mov eax, offset loc_5F3AC7
.text:0045A8BB call __EH_prolog
.text:0045A8C0 mov eax, 5954h
.text:0045A8C5 call __alloca_probe
.text:0045A8CA mov eax, dword_75D3D4
.text:0045A8CF mov edx, [ebp+0Ch]
.text:0045A8D2 push ebx
.text:0045A8D3 push esi
.text:0045A8D4 mov esi, [ebp+10h]
.text:0045A8D7 mov [ebp-10h], eax
.text:0045A8DA push edi ; char
.text:0045A8DB mov eax, 4665h
.text:0045A8E0 xor edi, edi
.text:0045A8E2 cmp edx, eax
.text:0045A8E4 mov [ebp-58B8h], ecx
.text:0045A8EA mov ecx, 5ACh
.text:0045A8EF mov ebx, offset byte_95E7B8
.text:0045A8F4 ja loc_45C52C
.text:0045A8FA jz loc_45DA84
.text:0045A900 mov eax, 2089h ; The Opcode of the packet being processed is in register edx
.text:0045A905 cmp edx, eax
.text:0045A907 ja loc_45B969 ; If the Opcode > 0x2089, jump to 45B969
.text:0045A90D jz loc_45B95E
.text:0045A913 mov eax, 1336h
.text:0045A918 cmp edx, eax
.text:0045A91A ja loc_45ADB6 ; If the Opcode > 0x1336, jump to 45ADB6
.text:0045A920 jz loc_45BA1B
.text:0045A926 mov eax, 0A2Ah
.text:0045A92B cmp edx, eax
.text:0045A92D ja loc_45AB94 ; If the Opcode > 0x0A2A, jump to 45AB94
.text:0045A933 jz loc_45AB89
Starting at address 45A900, is where it is checking which routine to jump to, based on the Opcode.
We know that for 6.2, OP_Charm is 0x10A1, so the first test that will pass is, is opcode > 0x0A2A, and
we jump to loc_45AB94.
Move the cursor over the loc_45AB94 in the ja instruction, right click, and select jump to operand, which
takes us to 45AB94:
Code:
.text:0045AB94 loc_45AB94: ; CODE XREF: sub_45A8B6+77j
.text:0045AB94 mov eax, 0F40h
.text:0045AB99 cmp edx, eax
.text:0045AB9B ja loc_45ACAC
.text:0045ABA1 jz loc_45DB94
There are some more comparisons. In this case, our opcode 0x10A1 is greater than 0x0F40, so we now jump to
loc_45ACAC. (Jump to operand as before).
Code:
.text:0045ACAC loc_45ACAC: ; CODE XREF: sub_45A8B6+2E5j
.text:0045ACAC mov eax, edx
.text:0045ACAE sub eax, 0F66h
.text:0045ACB3 jz loc_45AD6E
.text:0045ACB9 sub eax, 9Eh
.text:0045ACBE jz loc_45AD63
.text:0045ACC4 sub eax, 9Dh
.text:0045ACC9 jz loc_45AD58 ; Jump to Charm Routine
.text:0045ACCF sub eax, 5
.text:0045ACD2 jz short loc_45AD4A
.text:0045ACD4 sub eax, 4Eh
.text:0045ACD7 jz loc_45DE25
What it does now is start subtracting values from the opcode, then jumping to a routine if the result is 0.
0x10A1 - F66 - 9E - 9D = 0, so we jump to 45AD58.
Code:
.text:0045AD58 loc_45AD58: ; CODE XREF: sub_45A8B6+413j
.text:0045AD58 push esi
.text:0045AD59 call sub_457270
.text:0045AD5E jmp loc_45B3A0
This just takes us to 457270:
Code:
.text:00457270 sub_457270 proc near ; CODE XREF: sub_45A8B6+4A3p
.text:00457270
.text:00457270 arg_0 = dword ptr 10h
.text:00457270
.text:00457270 push ebx
.text:00457271 push esi
.text:00457272 push edi
.text:00457273 mov edi, [esp+arg_0]
.text:00457277 push dword ptr [edi]
.text:00457279 call sub_402DD2
.text:0045727E push dword ptr [edi+4]
.text:00457281 mov esi, eax
.text:00457283 call sub_402DD2
.text:00457288 test esi, esi
.text:0045728A pop ecx
.text:0045728B pop ecx
.text:0045728C mov ebx, eax
.text:0045728E jz short loc_4572EC
.text:00457290 test ebx, ebx
.text:00457292 jz short loc_4572EC
.text:00457294 cmp byte ptr [ebx+1B3h], 0
.text:0045729B jnz short loc_4572EC
This is where it seems to start doing some processing, so we guess this is the guts of the charm routine.
Now we need to find the same routine in Titanium. Close IDA and start it up again, this time selecting the
Titanium eqgame.exe. Let it do it's thing for a few minutes.
When IDA is done processing, our goal is to find a routine in the Titanium client that looks the same, or very
similar to the one above. In this case, what I did was search for the 'immediate' value, 0x1B3.
Press Alt-I, and enter 0x1b3 in the search box. After a few seconds, a list of matches will be displayed. What
we are after is one that is:
Code:
cmp byte ptr [ebx+1B3h], 0
to match the instruction in the 6.2 client. In the list of occurences of 0x1B3 in IDA, the second match is the one we are
after. Double click on it and it will take you to the routine. Scroll up a bit and it looks like this:
Code:
.text:00453D7B sub_453D7B proc near ; CODE XREF: sub_45B8F0+D1Dp
.text:00453D7B
.text:00453D7B arg_0 = dword ptr 10h
.text:00453D7B
.text:00453D7B push ebx
.text:00453D7C push esi
.text:00453D7D push edi
.text:00453D7E mov edi, [esp+arg_0]
.text:00453D82 push dword ptr [edi]
.text:00453D84 call sub_401B28
.text:00453D89 push dword ptr [edi+4]
.text:00453D8C mov esi, eax
.text:00453D8E call sub_401B28
.text:00453D93 test esi, esi
.text:00453D95 pop ecx
.text:00453D96 pop ecx
.text:00453D97 mov ebx, eax
.text:00453D99 jz short loc_453DF7
.text:00453D9B test ebx, ebx
.text:00453D9D jz short loc_453DF7
.text:00453D9F cmp byte ptr [ebx+1B3h], 0
As you can see, this looks like the right routine, as it is identical in all ways that matter to the 6.2 code.
Now we need to work our way backwards. Click on the 'Sub_453D7B' which precedes 'proc near', and then press
the X key.
This will produce a list of locations that this subroutine is called from. There is only one place, so double
click it, and it takes us to:
Code:
.text:0045C60C loc_45C60C: ; CODE XREF: sub_45B8F0+BD0j
.text:0045C60C push ebx
.text:0045C60D call sub_453D7B
.text:0045C612 jmp loc_45F673
Nothing interesting here, so click on loc_45C60C, press X. This will produce a list of where 45C60C is called
from (just the one location). Double click on it in the result dialog box.
This looks more interesting.
Code:
.text:0045C4BA loc_45C4BA: ; CODE XREF: sub_45B8F0+B6Fj
.text:0045C4BA sub ecx, 12E5h
.text:0045C4C0 jz loc_45C60C
.text:0045C4C6 sub ecx, 21h
.text:0045C4C9 jz short loc_45C517
.text:0045C4CB sub ecx, 171h
.text:0045C4D1 jnz loc_45F674
Here the code is subtracting 12E5 from ecx (which is the opcode), and following a jump which leads to the
charm routine if the result is zero.
We could continue moving back through the call tree to find if there are any other subtractions being done
on the opcode value, but in this case there isn't, 0x12e5 is the opcode for OP_Charm in Titanium.