Ok, let's go. Open your client (Assuming 4.0.0l) in
IDA. Use default options and let IDA work. Eat some pasta in the meantime.
When IDA is done, save the database. We'll need it later, not now.
Now start any server emulator (My favourite is POL)
and open your client (One *with* encryption!).
Next, open up OllyDbg and attach it to the client. Set a breakpoint on
"send".
To do so, press ctrl+g and enter "send". To set the actual breakpoint,
press F2.
Now enter any login and password and hit enter. Olly will break and popup.
In the calling stack window in Olly (lower right), you can see the stack
for send. "DataSize" is 0x3E, that's the correct length for
the login packet. But look at "Data", it's encrypted, and that's
not how we want it in UOLog.
The TOS (Top of Stack) always shows the return address of a call, ie.
the location from where the call was made. Remove the breakpoint (F2)
and go to that location with ctrl+g (419A08 in 4.0.0l).
Ok, we're direct on the send call. The packets are encrypted there.
If you scroll up a bit, you'll see the message "dunno, select with
invalid socket". Looks like we're in the client's send function.
Set a breakpoint on top of that function (419980) and login again to make
Olly break there.
When it breaks, you'll notice that the function has no calling stack,
ie. it's func(void);. Trace down using F8 and observe the register window.
You'll notice that 4199E6 ECX contains the length. The next line puts
the address of the encrypted send buffer into EDI.
We put our breakpoint too late, the data is encrypted there already.
Hmm, what can we do? Right! We put a breakpoint on the address of the
encrypted send buffer. Right click on the first byte of the encrypted
datastream in the dump window and select "Breakpoint -> Memory,
on access". Now resume the client. Olly will break immediately, you'll
see a rep stosd, that cleans up the memory by setting it to 00 00...Resume
the client, login again, and olly will break again.
Wow, this time we're in a loop (You'll recognize loops by black borders
around them in olly). The loop is using shr, shl and some other binary
operators. That really looks like the encryption loop, nice. Remove the
memory breakpoint, but don't resume the client yet.
Instead, place a breakpoint on top of the encrypt function (41A360) and
then resume the client.
Login again, and olly will break. Look at all the registers, EDX contains
the length already. But the data isn't in the registers yet, so let's
trace (F8) over the call which is right below the function header.
Look at the registers! Great! EAX contains the length and ESI contains
our packet! That's all we need for send.
We have:
Address where we have the plain packet: 41A370
Register with the length: EAX
Register with the packet: ESI
Now for recv.
We can't use the crypt client anymore because the server doesn't send
back anything since our login packets aren't valid (Well, encrypted).
So let's fire up a no-crypt-client and attach Olly to it.
Login and go to a location without any NPCs and anything so you don't
receive any packets.
Remember that the client used "select" to check if there are
packets ready to be received? Let's set a breakpoint on select.
Of course Olly breaks immediately because the client constantly checks
for packets (Btw, that's one reason why it needs 100% CPU all the time,
but that's another story, PeekMessage). Go to the calling address (419803) and you'll
see the check if select returned -1.
Scroll down a bit and you'll see a call to recv inside a loop. If you
know how TCP/IP works then you know that the packets arrive as a stream,
not at once, that's why they're received in a loop. Place a break on top
of that loop (41985C) and resume the client.
It will break soon because it receives a ping reply packet. Go to the
location where Olly displays "Buffer". That's where recv will
store the received data. Display it in the dump window, step over recv
and then set a memory breakpoint on it.
You'll notice that the memory just gets cleaned, so a memory on write
breakpoint isn't correct. Set a "On access" breakpoint instead.
If you did it right, you'll end up in some loop at 419BC0. Place a break
on top of that function.
When the client breaks, remove the breakpoint and go to the calling address
(419D0C). You will see a big loop below the call. Trace a bit into that
loop and you'll see that it builds the packet and calls 41A4A0 after building
the packet.
Trace into that function and look at the registers, EDI contains the length
and ESI contains the packet. Done.
Now we have all the information for recv:
Address where we have the plain packet: 41A4A0
Register with the length: EDI
Register with the packet: ESI
Now all we have to do is getting the timestamp of the client. Open client.exe
in UltraEdit and search the PE header. It is on top of the file and starts
with "PE", in 4.0.0l it's at 0x0138. Skip the next WORD and
the next DWORD and you'll have the TimeStamp in Little Endian. To convert
it to human readable Big Endian, reverse the bytes, resulting in 3EBFECC0.
That's it, we have all the required information for UOLog!
|