MU Coding Lessons

Experienced Elementalist
Joined
Feb 28, 2010
Messages
223
Reaction score
400
Some little tuts about coding in MUOnline for ASM lang (MASM32 compiler) if somebody wants to pass the tutorial to delphi, C++ or C# you welcome.

For follow this and others tutorial youll have to get a little notion about what is MASM32:

Code:
- MASM32 is a microsoft compiler, in case you didnt know this... That means that it belongs to microsoft copyrigth, but it got a special licence that allows you to use MASM32 for make any project profit or non profit, the only thing that MASM32 doenst allow is to reedistrubute the compiler has your own.

Now that you know this we ready to start, if you dont know wtf is ASM lang then you should probably read a tutorial from iczelion a good ASM coder:



Finally if you dont have time to learn much abouut ASM that isnt mch of a problem, since coding for MuOnline and for any general game that involves hooking and dissamble its pretty much easy...

Lets start !​
Tools: Ollydbg, NotePad, Brain (most important)

Lesson 1: Hooking your own DLL​

Theory:​

The theory its ofcourse that if we want to mod any game or any application itself we need to hook our own code in order to make changes into the target, this is well knowed has "Hooking" and its the basis of everything youll do. Hooking can be from simple methods has using API to very complex methods has API Hooking, Kernel Hooking, Injecting code, etc, etc.

For MuOnline the changes we want to do are in server side and of course has you already think, we need to hook in GameServer.exe wich is the server side client.

Now that you are thinking a little more, lets pass into how we should hook into GameServer. We name several methods, but lets use the most simple one, via API and writting our code into GameServer.

In order to do this lest find a veryusefull API (if you dont know what a API is then this tutorial isnt for you).
The API LoadLibrary its perfect for our job, lets say what MSDN have to say about it:




Code:
C++

HMODULE WINAPI LoadLibrary(
  __in  LPCTSTR lpFileName
);



MASM32:
.data
 lpFileName db "MyDLL.dll", 0 ;<--- This is a string
Invoke LoadLibrary, Addr lpFileName  <--- We call our API with the argumment of the DLL name



Pure ASM:
push Offset lpFileName ;<--- This is the string
call LoadLibrary ;We call LoadLibrary

Ok smartass has you figure out by now isnt a great challenge, but lets keep on the theory. The LoadLibrary API use 1 argumment that is only the DLL name that we want to load and the returned value is the Handler to our DLL.

But this isnt enough caused LoadLibrary doesnt actually call the DLL (excuse me coders i know in fact it calls it but i put here has it doesnt) it just load it and return the Handler. So we need some way to call a procedure or fuction of our DLL.

If we look closer to our API list we will find this cool thing:

Code:
C++

FARPROC WINAPI GetProcAddress(
  __in  HMODULE hModule,
  __in  LPCSTR lpProcName
);



MASM32:
.Data ?
hModule DD ? ;Handler of the DLL
.Data
lpProcName DB "OurProcName", 0
Invoke GetProcAddress, hModule, lpProcName



Pure ASM:
push lpProcName ;<-- String "OurProcName"
push hModule ;<-- Handler of the DLL
call GetProcAddress

Now that you look this API, you wondering wtf is that, well ill explain it a little fast:

GetProcAddress took the Address Or Offset of a procedure in a specific module, this means in our case that with help of the returned Handler from LoadLibrary we can return the Address of any Procedure of our DLL that we want.

Example if we have our procedure called "Start" then we can get its address thanks to this API, we just need the handler has first param (of DLL) and the proc name ("Start") in this case.

Finally if you come to this place you ready then for leave the theory behind.

In Work:​

Now that we are ready to apply what we learn, you guys will follow it into steps.

1.- Open Ollydbg and open GameServer.exe (a fresh one not hooked)
2.- Ollydbg will load GameServer in this place (at least in my fresh GS version):

Code:
005828E5   > 55             PUSH EBP
005828E6   . 8BEC           MOV EBP,ESP
005828E8   . 6A FF          PUSH -1
005828EA   . 68 58A56A00    PUSH IAT-Game.006AA558
005828EF   . 68 C8015800    PUSH IAT-Game.005801C8                   ;  SE handler installation
005828F4   . 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
005828FA   . 50             PUSH EAX
005828FB   . 64:8925 000000>MOV DWORD PTR FS:[0],ESP
00582902   . 83EC 58        SUB ESP,58
00582905   . 53             PUSH EBX
00582906   . 56             PUSH ESI
00582907   . 57             PUSH EDI
00582908   . 8965 E8        MOV DWORD PTR SS:[EBP-18],ESP
0058290B   . FF15 50CD650C  CALL DWORD PTR DS:[<&KERNEL32.GetVersion>;  kernel32.GetVersion

How we know isnt hooked? caused we dont see any LoadLibrary call at the start and we see in the end a call to GetVersion (indicate that is the real Entry Point of this GameServer).

3.- We can see that GameServer has plenty of empty space lets choose one offset for set our hook, in this case i choose:

Code:
00525063 > $ CC            INT3

4.- Now we have our empty space lets apply what we learn, first we need to call LoadLibrary and make sure you have written the DLL name in a empty offset (if you dont know how read a Ollydbg Guide).

Code:
00525064   . 68 40505200    PUSH IAT-Game.00525040                   ; /FileName = "GsPlugin.dll"
00525069   . FF15 04CD650C  CALL DWORD PTR DS:[<&KERNEL32.LoadLibrar>; \LoadLibraryA

Has you can see we PUSH the first argumment that is the address that holds our string, in this case "00525040" and later we call LoadLibrary. But how do we find LoadLibrary API address?. Well that is simple lets press CONTROL+N in Ollydbg and we will see a window with a reference from all APIs that GameServer uses, now we will writte "LoadLibrary" (just writte it dont do anything else) and you will see this:

Code:
Names in IAT-Game, item 250
 Address=0C65CD04
 Section=.idata
 Type=Import  (Known)
 Name=KERNEL32.LoadLibraryA

So we can see the Address of LoadLibrary is "0C65CD04" and thats the address we use for writte:

Code:
Call Dword ptr ds:[0C65CD04] -> LoadLibrary

5.- Now lets writte GetProcAddress, we will obtain the address of GetProc by the same way we obtain LoadLibrary (CONTROL+N and writte API name):

Code:
0052506F   . 68 4D505200    PUSH IAT-Game.0052504D                   ; /ProcNameOrOrdinal = "MyProcedure"
00525074   . 50             PUSH EAX                                 ; |hModule
00525075   . FF15 00CD650C  CALL DWORD PTR DS:[<&KERNEL32.GetProcAdd>; \GetProcAddress

You meaby get confused here, what we do is first PUSH the last argument, but why?
Caused the way you call arguments in pure ASM its inverted piramidal, this means that you push the last argument first and continue to the first. (If you dont get this part i explain better in Lesson Review)

Now that you get that part, we can push the first argument, but wait... What is push eax?

Well since we call LoadLibrary then the returned value its stored in EAX regist (all APIs return their values in EAX regist) so we just push the value that EAX holds (this means our Handler to the DLL).

And finally call GetProcAddress, easy isnt?.

6.- Well you done fine till this part, once we got our address to our procedure (of course returned in EAX regist) then we just need to CALL EAX, so we can enter into our procedure just like this:

Code:
0052509A   . FFD0           CALL EAX

7.- But has you can realise when we start our GameServer we will start in the Entry Point "005828E5" and not in the address where we load our DLL, how we can fix this, very simple lets go to the our Entry Point by pressing CONTROL+G and paste the address "005828E5". Now that we are there we will do a JMP to our DLL space, in my case will be just like this:

Code:
005828E5    ^E9 7A27FAFF    JMP IAT-Game.00525064

This will make when you load GameServer the Entry Points reedirect the flow of execution to first load your DLL.

8.- We arent ready yet, just a little more. We need to now restore has you can see the instructions that were deleted when you place the JMP to your DLL space load, that instructions where:

Code:
005828E5     55             PUSH EBP
005828E6     8BEC           MOV EBP,ESP
005828E8     6A FF          PUSH -1

So we will go to our DLL address space and after the CALL EAX we will add this INSTRUCTIONS like this:

Code:
0052509A   . FFD0           CALL EAX
0052509C     55             PUSH EBP
0052509D     8BEC           MOV EBP,ESP
0052509F     6A FF          PUSH -1

9.- Finally we can end this, but we need to return the execution flow back to where GameServer start.
But if you smart enough you will realise if we return the flow execution back to the Entry Point, the JMP we put there will make our DLL load eternal times, so yeah smart boy we need to put a JMP to the INSTRUCTION down of our JMP in the Entry Point wich is this:

Code:
005828EA   . 68 58A56A00    PUSH IAT-Game.006AA558

So the address is 005828EA, and we do the JMP to that address in our DLL space code like this:

Code:
005250A1     E9 44D80500    JMP IAT-Game.005828EA

This will make that after you got done by calling your Procedure, it will jump to the EntryPoint for continue the execution flow and make GameServer start like nothing happens.

10.- Lets save all the changes, lets press Secondary Buttom>Copy to Executable > All modifications. And in the new window Secondary Buttom > Save to File.

11.- Voila you finally done with hooking your DLL ! =) .

Lesson Review:​

Here is the full code divided by Entry Point space and DLL space:

[Entry Point]:

Code:
005828E5    ^E9 7A27FAFF    JMP IAT-Game.00525064

[DLL Space]:

Code:
00525083   . 68 A4505200    PUSH IAT-Game.005250A4                   ; /FileName = "GsPlugin.dll"
00525088   . FF15 04CD650C  CALL DWORD PTR DS:[<&KERNEL32.LoadLibrar>; \LoadLibraryA
0052508E   . 68 B3505200    PUSH IAT-Game.005250B3                   ; /ProcNameOrOrdinal = "MyProcedure"
00525093   . 50             PUSH EAX                                 ; Push Handler of DLL loaded
00525094   . FF15 00CD650C  CALL DWORD PTR DS:[<&KERNEL32.GetProcAdd>; \GetProcAddress
0052509A   . FFD0           CALL EAX            ;Call our Procedure
0052509C     55             PUSH EBP             ;Restore EntryPoint Instruction
0052509D     8BEC           MOV EBP,ESP      ;Restore EntryPoint Instruction
0052509F     6A FF          PUSH -1              ;Restore EntryPoint Instruction
005250A1     E9 44D80500    JMP IAT-Game.005828EA ;Execution back to EntryPoint instruction after JMP

Has we can see isnt that hard, its very simple once you get used to coding and ASM.
I must admit that the piramidal system of pushing arguments its a little complex to understand but this is how ASM works and also how all langs converted to ASM of course will work.

Inverted Piramidal System can be resumed has this:

Code:
Push 4Argument  ;Last Argument
Push 3Argument
Push 2Argument
Push 1Argument  ;First Argument
Call Fuction

The rest its pretty simple to understand.

Special Notes:​

1 .- I know the way of reedirection from Entry Point to DLL space its pretty noob (the JMP thing) but its a very simple way for starters to learn. If you want to do a more PRO way you can always use LordPE or any PE editor and change the address of DLL space has the Entry Point and dont do any JMP from EntryPoint to DLL space.

2 .- If you plan to make the DLL in MASM32 youll have to know that you need to make a .def file, the .def file must contain inside this kind of format:

Code:
LIBRARY TEST ;NAME OF DLL 
EXPORTS MyProcedure ;NAME OF PROCEDURE TO EXPORT

So the EXPORT procedure you put in that file can be called by GetProcAddress.

3.- Sorry for the lack of images i dont have much time.

4.- Sorry for the lack of lang, english is not my natal lang.

5.- If you want to make a resume yourself or explain it in another way (lang, theory, etc) you welcome. The same if you want to change the visual style of the lesson (caused i suck on the visual thing. Just send me a PM).

6.- Ill only answer questions about the lesson, same has questions about ASM, General coding & MuOnline coding (I mean real questions, not like: "Please can i know how code S5?")


Credits: [INDG]FeN$x
 
Last edited:
Lesson 2

Lesson 2: Dissamble GameServer​

Tools: Ollydbg, GameServer, NotePad, Brain

Theory:​

Well in theory we call dissamble the process from convert a portion of unknown code into a full functionable code. In most of cases this consist of open a debugger and convert the Pure ASM code into our C++, Delphi or even MASM32 code.

The thing get more complex when we found obfuscation techniques, permutation opcodes, metamorph instructions or new techniques of Virtual Machines.

In this case what we want to do is dissamble some GameServer code so we can know in what we working of, and by learning this you can learn how to use and mod any procedure of GameServer, probably making your own one in DLL or making a full emulator (with alot of patient).

In MUOnline case thanks to the leakings of original Server Files and the stupidity of a company that doesnt know how protect their files, we count with the PDB file format for GameServer so we can know allmost all Procedures names and some local variable names also, making all this process much more simple.

But i love the old way so if you learn how dissamble without the help of your precious PDB then later youll dissablem the whole GameServer.

In Work:​

Warning GameServer code contains several degrees of stupidity !

1.- Open Ollydbg and our GameServer in this case without PDB
2.- Lets found a easy proc to dissamble something that you can learn but not that simple at the same time, my candidate was the procedure for Load Terrains maps.

3.- Lets take a look to this piece of trash:

Code:
Creation of Terrain strings maps
Degree of stupidity : MAX

FROM:
00518A21   > A1 8CE46900    MOV EAX,DWORD PTR DS:[69E48C]
TO:
00519286   . 66:8945 E1     MOV WORD PTR SS:[EBP-1F],AX


Code:
Loop code for load Terrains by they name
Degree of stupidity : MEDIUM

00519298   > 8B55 F0        MOV EDX,DWORD PTR SS:[EBP-10]
0051929B   . 83C2 01        ADD EDX,1
0051929E   . 8955 F0        MOV DWORD PTR SS:[EBP-10],EDX
005192A1   > 837D F0 28     CMP DWORD PTR SS:[EBP-10],28
005192A5   . 7D 47          JGE SHORT IAT-Game.005192EE
005192A7   . 8B4D F0        MOV ECX,DWORD PTR SS:[EBP-10]
005192AA   . 69C9 68100500  IMUL ECX,ECX,51068
005192B0   . 81C1 E8EAFA09  ADD ECX,IAT-Game.09FAEAE8
005192B6   . E8 1BA7EEFF    CALL IAT-Game.004039D6
005192BB   . 8B45 F0        MOV EAX,DWORD PTR SS:[EBP-10]
005192BE   . 50             PUSH EAX
005192BF   . 8B4D F0        MOV ECX,DWORD PTR SS:[EBP-10]
005192C2   . 6BC9 0F        IMUL ECX,ECX,0F
005192C5   . 8D940D 8CFDFFF>LEA EDX,DWORD PTR SS:[EBP+ECX-274]
005192CC   . 52             PUSH EDX
005192CD   . B9 287BC50A    MOV ECX,IAT-Game.0AC57B28
005192D2   . E8 5A9CEEFF    CALL IAT-Game.00402F31
005192D7   . 50             PUSH EAX
005192D8   . 8B4D F0        MOV ECX,DWORD PTR SS:[EBP-10]
005192DB   . 69C9 68100500  IMUL ECX,ECX,51068
005192E1   . 81C1 E8EAFA09  ADD ECX,IAT-Game.09FAEAE8
005192E7   . E8 75A3EEFF    CALL IAT-Game.00403661
005192EC   .^EB AA          JMP SHORT IAT-Game.00519298

Ok has we can see Webzen coders didnt find any more usefull that to load a simple strings in memory to local code by using allmost more than 100 Opcodes (Instructions, be familiar with the therm).

4.- Now that we have our procedure selected for decompile lets analyze the creation of strings:

Code:
I took this last portion of code:

00519266   . A1 1CE36900    MOV EAX,DWORD PTR DS:[69E31C]
0051926B   . 8945 D5        MOV DWORD PTR SS:[EBP-2B],EAX
0051926E   . 8B0D 20E36900  MOV ECX,DWORD PTR DS:[69E320]
00519274   . 894D D9        MOV DWORD PTR SS:[EBP-27],ECX
00519277   . 8B15 24E36900  MOV EDX,DWORD PTR DS:[69E324]
0051927D   . 8955 DD        MOV DWORD PTR SS:[EBP-23],EDX
00519280   . 66:A1 28E36900 MOV AX,WORD PTR DS:[69E328]
00519286   . 66:8945 E1     MOV WORD PTR SS:[EBP-1F],AX
0051928A   . 33C9           XOR ECX,ECX
0051928C   . 884D E3        MOV BYTE PTR SS:[EBP-1D],CL

Ok what we see here is the address "69E31C" contains a string inside (Secondary Buttom in address>Follow In Dump>Memory Addr).

If you arent familiar with ASM coding then youll find this kind of chinnese lang but ill make it simple:

Code:
Move the first 4 bytes of String to EAX regist
MOV EAX,DWORD PTR DS:[69E31C]

Code:
Save this 4 bytes of string into a local variable
MOV DWORD PTR SS:[EBP-2B],EAX

Has you can see GameServer is trying to copy the string to a local variable in the worst way possible, but when you reach this opcode youll wondering what it does too:

Code:
Move the last 2 bytes from string to local variable
00519280   . 66:A1 28E36900 MOV AX,WORD PTR DS:[69E328]
00519286   . 66:8945 E1     MOV WORD PTR SS:[EBP-1F],AX

And finally this:

Code:
The XOR ECX, ECX fuction converts ECX == 0
And later move the byte 00 to the end of string copied.

0051928A   . 33C9           XOR ECX,ECX
0051928C   . 884D E3        MOV BYTE PTR SS:[EBP-1D],CL

Yeah has you guess all strings must be terminated un 00 byte, wow you such a pro guy...

5.- Has we dissamble and understand that Webzen moves their strings memory, to local variable strings for no reason apparent we can continue to dissamble the main core of Terrains Load:

Code:
00519298   > 8B55 F0        MOV EDX,DWORD PTR SS:[EBP-10]
0051929B   . 83C2 01        ADD EDX,1
0051929E   . 8955 F0        MOV DWORD PTR SS:[EBP-10],EDX
005192A1   > 837D F0 28     CMP DWORD PTR SS:[EBP-10],28
005192A5   . 7D 47          JGE SHORT IAT-Game.005192EE
005192A7   . 8B4D F0        MOV ECX,DWORD PTR SS:[EBP-10]
005192AA   . 69C9 68100500  IMUL ECX,ECX,51068
005192B0   . 81C1 E8EAFA09  ADD ECX,IAT-Game.09FAEAE8
005192B6   . E8 1BA7EEFF    CALL IAT-Game.004039D6
005192BB   . 8B45 F0        MOV EAX,DWORD PTR SS:[EBP-10]
005192BE   . 50             PUSH EAX
005192BF   . 8B4D F0        MOV ECX,DWORD PTR SS:[EBP-10]
005192C2   . 6BC9 0F        IMUL ECX,ECX,0F
005192C5   . 8D940D 8CFDFFF>LEA EDX,DWORD PTR SS:[EBP+ECX-274]
005192CC   . 52             PUSH EDX
005192CD   . B9 287BC50A    MOV ECX,IAT-Game.0AC57B28
005192D2   . E8 5A9CEEFF    CALL IAT-Game.00402F31
005192D7   . 50             PUSH EAX
005192D8   . 8B4D F0        MOV ECX,DWORD PTR SS:[EBP-10]
005192DB   . 69C9 68100500  IMUL ECX,ECX,51068
005192E1   . 81C1 E8EAFA09  ADD ECX,IAT-Game.09FAEAE8
005192E7   . E8 75A3EEFF    CALL IAT-Game.00403661
005192EC   .^EB AA          JMP SHORT IAT-Game.00519298

Ok this looks like a hard **** to decompile but in fact it isnt, you can see by first look this opcodes:

Code:
0051929B   . 83C2 01        ADD EDX,1
0051929E   . 8955 F0        MOV DWORD PTR SS:[EBP-10],EDX
005192A1   > 837D F0 28     CMP DWORD PTR SS:[EBP-10],28
005192A5   . 7D 47          JGE SHORT IAT-Game.005192E

...

005192EC   .^EB AA          JMP SHORT IAT-Game.00519298

Lets analyze this part:

Code:
- Add 1 to EDX regist (wich of course its NULL already)
- Mov the value of EDX to a local Variable
- If the local variable value its greater or equal than 28 then
  we jump to exit the code.
- The JMP jump at the end returns us to ADD EDX, 1.

0051929B   . 83C2 01        ADD EDX,1
0051929E   . 8955 F0        MOV DWORD PTR SS:[EBP-10],EDX
005192A1   > 837D F0 28     CMP DWORD PTR SS:[EBP-10],28
005192A5   . 7D 47          JGE SHORT IAT-Game.005192E
...
005192EC   .^EB AA          JMP SHORT IAT-Game.00519298

So this is cleary a LOOP CODE something like:

Code:
.While (MapNumber < 40) ;Repeat while MapCounter is less than 40
  LOAD OUR MAP ;Here we load the maps
  Add MapNumber, 1 ;Add 1 map loaded to Counter
.Endw ;And repeat the whole thing.

Ok great now we understand its a LOOP CODE and the repeat the code while the condition that the maps loaded are less than 40 (28 HEX).

6.- Now we lack only the most important part wich is load the terrains itself lets look at them:

Code:
005192A7   . 8B4D F0        MOV ECX,DWORD PTR SS:[EBP-10]
005192AA   . 69C9 68100500  IMUL ECX,ECX,51068
005192B0   . 81C1 E8EAFA09  ADD ECX,IAT-Game.09FAEAE8
005192B6   . E8 1BA7EEFF    CALL IAT-Game.004039D6
005192BB   . 8B45 F0        MOV EAX,DWORD PTR SS:[EBP-10]
005192BE   . 50             PUSH EAX
005192BF   . 8B4D F0        MOV ECX,DWORD PTR SS:[EBP-10]
005192C2   . 6BC9 0F        IMUL ECX,ECX,0F
005192C5   . 8D940D 8CFDFFF>LEA EDX,DWORD PTR SS:[EBP+ECX-274]
005192CC   . 52             PUSH EDX
005192CD   . B9 287BC50A    MOV ECX,IAT-Game.0AC57B28
005192D2   . E8 5A9CEEFF    CALL IAT-Game.00402F31
005192D7   . 50             PUSH EAX
005192D8   . 8B4D F0        MOV ECX,DWORD PTR SS:[EBP-10]
005192DB   . 69C9 68100500  IMUL ECX,ECX,51068
005192E1   . 81C1 E8EAFA09  ADD ECX,IAT-Game.09FAEAE8
005192E7   . E8 75A3EEFF    CALL IAT-Game.00403661

Lets do this fast by code blocks:

Code:
- Always first look whats inside the CALL first for get an idea
- If we look inside the call we see that the fuction does nothing
- Yeah this is an uncomplete procedure that our pals from Webzen didnt remove, pretty lazy f*ckers.

005192A7   . 8B4D F0        MOV ECX,DWORD PTR SS:[EBP-10]
005192AA   . 69C9 68100500  IMUL ECX,ECX,51068
005192B0   . 81C1 E8EAFA09  ADD ECX,IAT-Game.09FAEAE8
005192B6   . E8 1BA7EEFF    CALL IAT-Game.004039D6

Now that we know this procedure is uncomplete and doesnt do a **** we can try to decompile the next code block:

Code:
- First we move to EAX the current MapNumber

- We push this value has the last argumment (Invert Piramidal System, read first lesson for understand it)
- We move again but to ECX the MapNumber value and we multiply it for 15 (0F HEX).

- The next opcode LEA means that we move the Address of whats inside of [EBP+ECX-274], has you can see its a hardcoded operation, but it uses ECX and remember that ECX contains MapNumber * 15, wich tell us that this is probably the string address that we are pushing.

In order to know this we can see this string:

0069E31C  74 65 72 72 61 69 6E 34  terrain4
0069E324  30 2E 61 74 74 00 00 00  0.att...

If we count the bytes in total are 15 (0F HEX) so yeah this fuction gets the address of the string in calculation of the MapNumber.

- We push the address of the current code in base of MapNumber * SizeOf String (15 decimal or 0F HEX) has the first argumment.

- And finally we move to ECX and strange address that we will know what it is later.


005192BB   . 8B45 F0        MOV EAX,DWORD PTR SS:[EBP-10]
005192BE   . 50             PUSH EAX
005192BF   . 8B4D F0        MOV ECX,DWORD PTR SS:[EBP-10]
005192C2   . 6BC9 0F        IMUL ECX,ECX,0F
005192C5   . 8D940D 8CFDFFF>LEA EDX,DWORD PTR SS:[EBP+ECX-274]
005192CC   . 52             PUSH EDX
005192CD   . B9 287BC50A    MOV ECX,IAT-Game.0AC57B28
005192D2   . E8 5A9CEEFF    CALL IAT-Game.00402F31

So this can be decompiled has:

Code:
mov ecx, MapNumber ;ECX == MapNumber
imul ecx, ecx, SizeOf StringLen ;MapNumber * 15
lea edx, [MapStrings + ecx] ;EDX == Addr of string for MapNumber
Invoke StrangeProc, edx, MapNumber

Or more simple has:

Code:
StrangeProc(*MapName, MapNumber)

And caused we dont know what this procedure does we will just put a BREAKPOINT (F2 Key) in the down instructions wich is:

Code:
005192D7   . 50             PUSH EAX

And press F9 key for run the GameServer and we can debug in real time what EAX contains.

Once you do this we will see that in the PUSH EAX the value that EAX hold is a pointer to a string that contains the full path name where the terrain file is, example:

Code:
PUSH EAX ;.Data/Terrain40.att

So we know that the recent decompiled fuction does get the path of where the TerrainFile input where (pretty stupid procedure).

And we can finally resume it has:

Code:
GetTerrainPath(TerainName,MapNumber)

At least we can decompile the last block of code and we ready:

Code:
- We push the full path name of the terrain to be loaded
- Move to ECX the MapNumber and multiply it for 51068 HEX
- We add to that result in ECX an strange offset

005192D7   . 50             PUSH EAX
005192D8   . 8B4D F0        MOV ECX,DWORD PTR SS:[EBP-10]
005192DB   . 69C9 68100500  IMUL ECX,ECX,51068
005192E1   . 81C1 E8EAFA09  ADD ECX,IAT-Game.09FAEAE8
005192E7   . E8 75A3EEFF    CALL IAT-Game.00403661

MapNumber*51068 HEX + Addr 09FAEAE8 its in plain sight an structure and we can determinate its length by the fact it multiply the MapNumber with the ammount of space that one map should use in this case 51068 HEX. If this looks confusing ill do an example:

Code:
MapNumber * SpaceForUse + MyStructure
Each MapNumber use the limited space given in "SpaceForUse".

So finally we can decompile the last procedure has:

Code:
mov ecx, MapNumber
imul ecx, ecx, SizeOf MapSpace (51068 HEX)
add ecx, MyMapStructure
invoke LoadTerrain, TerrainPath

More simplify:

LoadTerrain(*TerrainPath)

We know this procedure is LoadTerrain caused if you open the CALL and see inside you can see APIs for load memory buffers like GlobalAlloc and some fuctions of C++ like fread

7.- Now that we end decompiling we can see Webzen do some pretty stupid **** and we can make a much better procedure, more faster and more easy to use like this example:

Code:
.Data
MapNames DB "./Data/Maps/terrain1.att", 0
         DB "./Data/Maps/terrain2.att", 0
         DB "./Data/Maps/terrain3.att", 0
         DB "./Data/Maps/terrain4.att", 0
         DB "./Data/Maps/terrain5.att", 0
         DB "./Data/Maps/terrain6.att", 0
         DB "./Data/Maps/terrain7.att", 0
         DB "./Data/Maps/terrain8.att", 0
         DB "./Data/Maps/terrain9.att", 0
         DB "./Data/Maps/terrain10.att", 0
         DB "./Data/Maps/terrain11.att", 0
         DB "./Data/Maps/terrain12.att", 0
         DB "./Data/Maps/terrain19.att", 0
         DB "./Data/Maps/terrain25.att", 0
         DB "./Data/Maps/terrain31.att", 0
         DB "./Data/Maps/terrain32.att", 0
         DB "./Data/Maps/terrain33.att", 0
         DB "./Data/Maps/terrain34.att", 0
         DB "./Data/Maps/terrain35.att", 0
         DB "./Data/Maps/terrain36.att", 0
         DB "./Data/Maps/terrain37.att", 0
         DB "./Data/Maps/terrain38.att", 0
         DB "./Data/Maps/terrain39.att", 0
         DB "./Data/Maps/terrain40.att", 0

Map_LoadAll Proc
Local MapNumber:DWord ;Variable for MapNumbers
Local StringSize:DWord ;Variable for string sizes
Local Reloadtimes:DWord ;Variable for reload times
 And MapNumber, 0 ;Init like null variable
 And StringSize, 0 ;Init like null variable
 .Repeat ;Repeat code block
    Mov Edx, MapNumber
    Mov Eax, StringSize
    IMul Edx, Eax ;MapNumber * Old String Size
 	Invoke StrLen, Addr MapNames + [Edx] ;SizeOf of the string
    Mov StringSize, Eax ;save the string size
    .If (MapCounter == ATT12) ;Terrain12.att
    	Mov Reloadtimes, 7 ;We load terrain12, 7 times
    .ElseIf (MapCounter == ATT19) ;Terrain19.att
    	Mov Reloadtimes, 6 ;We load Terrain19 6 times
    .ElseIf (MapCounter == ATT24) ;Terrain25.att
    	Mov Reloadtimes, 6 ;We load Terrain25 6 times
    .Else
        Mov Reloadtimes, 0
    .EndIf
    .Repeat
      mov eax, MapNumber
      imul eax, eax, 51068 ;Space for one map in struct
      add eax, MapStruct
      Mov Ecx, Eax ; ECX = Pointer to MapStruct[MapNumber]
      Mov Edx, MapNumber ; EDX = MapNumber
      Mov Eax, StringSize ; EAX = StringSize
      IMul Edx, Eax ;MapNumber * String Size
 	  Invoke LoadTerrain, Addr MapNames + [Edx]
      .If (Reloadtimes != 0) ;If ReloadTimes local var its different than 0
      	Sub Reloadtimes, 1 ;One reload already done
      .EndIf
      Add MapNumber, 1 ;Next map
    .Until (Reloadtimes == 0) ;Repeat Until ReloadTimes == 0
 .Until (MapNumber == 40) ;Repeat all till Last map 
 Ret
V_CMap_LoadAll EndP

Lesson review:​

We can see that decompiling isnt that hard, of course depending of the procedure, but when debuggin in real time everything is much more easy.

Since we have a PDB file everybody can dissamble, but what matters is that you learn how without any help.

Excercise with this and youll soon develop a good "eye" for dissamble allmost every procedure without even debug in real time, but like i say somethings must be debugged.

You can remake procedures from scratch, optimise them or just dissamble for understand how it works, it all depends of your imagination, the more you dissamble the more you understand in what you working on.



Special Notes:​

- I know its hard to follow a long lesson but once you follow the rythm you can understand easy.

- Webzen dont remove some un used procedures caused their lazy ass coders dont want

- Webzen server code lacks of the optimisation, but since its a Server Side application it doesnt need much, of course that talk very bad about their coders.

- Why i dont do an emulator, i work in other project non releated to MU, i just do this lessons when extra time.


[INDG]FeN$x
 
Last edited:
Lesson 3

Lesson 3: Your own options​

Tools: Ollydbg, GameServer, MASM*** NotePad, Brain.

Theory:​

Making options or make the user choose between options its one of the first steps to be an "Intelligent Coder". In some cases you see software products that doesnt allow the user to change or interact with the soft has they where the source codes owner, well this isnt in fact a easy to do thing (depending of the soft).

In a personal example i develop a protection system for games (youll see it soon in big games) and i try to make it the most modificable possible for my customers, making it very editable even for myself. In a MUOnline approach this can help us to get more in "Touch with the community" to help making our code more editable or just for raise our coder rank
(yeah this community judges alot by how many options you add to some ****).


Anyway, we can add options into files like most of MuOnline developers do, the so called "CommonLoc Options" or the INI file in the DLL that everybody uses. In IAT selling files days they used to call them "Scripts" caused you can do like this:

Code:
MaxZen "30000"

Wich is in fact a big stupid **** caused we can use some Microsoft API for read .ini options but it will look like this:

Code:
MaxZen = 30000

So people tell wow it is a "Script" caused i use comas (") instead of the equal symbol, or wow its .script instead of .ini extension file. :w00t:

Now leaving behind that old and fool days when everybody was a pro coder caused adding a few "LIES" about scripting and options, we can start our own without beg mercy to anybody.

In Work:​

Warning i know how community get excited, add 1 option and make a full new repack with the guy that added that option in credits, but please dont do it anymore, its such a noob ****.

1.- We will open Ollydbg and GameServer
2.- Lets search for some editable part of code, i can see millions of them at plain sigth but lets find it by a simple way:

Secondary Buttom>Search For>All referenced strings

Ok we can see all the strings and you got your eyes full with strings that reference some part of code that can be edited, so ill pick one random:

Code:
004978B8  |. 68 30A16900    PUSH IAT-Game.0069A130                   ;  ASCII "[PotionMix][ShieldPotion Lv1 Mix] - Mix Start"

Ok Ok, i choose this string and lets see what this procedure about creating a PotionMix have to show us. By a first look i can see this hardcoded value:

Code:
004978E1  |. C745 D8 231C00>MOV DWORD PTR SS:[EBP-28],1C23

1C23 seems like an Item number mmm probably must be one, but why is that number, well ill explain its conversion like this:

Code:
ItemIndex * 512 + ItemID

If you want to test this just take an Item Index from Item(Kor), the ItemID of that index, do the calculation, later convert it to HEX and youll get value that GameServer uses for items.

Anyway now we got our value that is, the Item ID of creation when Mixing a Potion (dont ask me what kind of potion caused i dont give a ****).

Lets take the exact offset (address) where the value 1C23 is, by putting secondary click on the instruction and Follow In Dump>Selection, we will see this:

Code:
004978E1  C7 45 D8 23 1C 00 00     ÇEØ#..

So "004978E1" its byte C7, lets count like we were in the school...

Now the final offset we need is 004978E4 caused is where the 23 byte (1C23) start.

3.- Now that we have the final offset we need for, edit lets pass to the coding part:

We first need to create a file, name it like whatever you want, ill do it has:

Code:
OptionsArentGreat.wow

And put inside like this:

Code:
[Context]
 PotionMixIndex = 14
 PotionMixID = 35

4.- Now lets find an API caused i know you wont be able to code your own routine for reading chars, strings and others so nevermind and let microsoft help us:


Code:
C++

UINT WINAPI GetPrivateProfileInt(
  __in  LPCTSTR lpAppName,
  __in  LPCTSTR lpKeyName,
  __in  INT nDefault,
  __in  LPCTSTR lpFileName
);


MASM32

Invoke GetPrivateProfileInt, Addr lpAppName, Addr lpKeyName, DefaultValue, Addr lpFileName


PURE ASM:

push lpFileName
push DefaultValue
push lpKeyName
push lpAppName
call GetPrivateProfileInt

This API has you can see can help us to get the interger value from any file, of any extension that follow the syntax for reading inside... Wich is:

Code:
[AppName]
 KeyName = 1

So now lets code in our DLL a simple fuction for read from this file:

Code:
.586
.Model flat, StdCall
Option CaseMap:none
Include	\masm32\include\windows.inc
Include	\masm32\include\kernel32.inc
Include	\masm32\include\user32.inc
IncludeLib	\masm32\lib\kernel32.lib
IncludeLib	\masm32\lib\user32.lib
Includelib      \masm32\lib\gdi32.lib

.const
.data?
.data
 FilePath DB "./OptionsArentGreat.wow", 0
 Context DB "Context", 0
 szPotionMixIndex DB "PotionMixIndex", 0
 szPotionMixID DB "PotionMixID", 0
.code

DllEntry Proc hInst:HINSTANCE, reason:DWord, reserved1:DWord
 mov eax, TRUE ;Return TRUE when loaded DLL by GameServer
 ret
DllEntry EndP

ReadWoWFile Proc
 Invoke GetPrivateProfileInt, addr Context, addr szPotionMixIndex, 0, Addr FilePath
 imul eax, eax, 512 ;Trasnform Index
 push eax ;Save the ItemIndex in invert piramidal stack
 invoke GetPrivateProfileInt, addr Context, addr szPotionMixID,
 0, Addr FilePath
 mov edx, eax ;EDX = ItemID
 pop eax ;Restore the saved ItemIndex value from stack
 add edx, eax ;ItemIndex + ItemID = Final item ID
 mov word ptr [004978E4], Ax ;Since our item ID are 2 bytes then we move a WORD and not a DWORD (4 bytes)
 ret ;Lets quick of the procedure
ReadWoWFile Endp

5.- Now thats all, we can compile our DLL with MASM*** if you dont know how ill put how in Special Notes of this lesson.

6.- Now you know how to do the so called OPTIONS that everyone does and make a full repack for just adding that shits...

7.- READ LESSON REVIEW IF YOU WANT TO LEARN MORE !

Lesson Review:​

Well it was pretty simple, probably the most easy lesson of the 3 i have done so far. For people that want to load strings you can always use:

Code:
GetPrivateProfileString API

Learn in MDSN how use it and test yourself.

You migth be wondering what [CONTEXT] means, well its a identifier and probably a reference to the API for read, it isnt intended to be used for read ALL KIND of extencion files, it was intended for read Regist keys and .INI file format.

You can define many [CONTEXT] and by any name like this:

Code:
[ChaosMixOptionsHere]
 MyChaosMixID = 10
[ZenOptions]
 MyMaxZen = 10000000
[StupidFocker]
 SuchAndIdiot = 1

Also define strings like:

Code:
[STRINGS]
MyString = YES

And interpretate the string for assignate a value.

Just have fun and learn !

Special Notes:​

- For compile the DLL lesson from here you need to download MASM32 and make a batch file like this:

Code:
if exist TEST.obj del TEST.obj
if exist TEST.dll del TEST.dll

\masm32\bin\ml /c /coff TEST.asm
\masm32\bin\Link /SUBSYSTEM:WINDOWS /DLL /DEF:TEST.def /LIBPATH:c\masm32\lib TEST.obj

pause

- This lesson was easy probably the most difficult part was the Item Calculation thing ^^

- If you want to keep following the next lessons youll have to learn about ASM coding.

- The .def file contains the exports procedures for be readed by another application, please read lesson one for better reference.


- DONT BE AN IDIOT AND DO SOME OPTION FOR ANOTHER GAMERSERVER (not yours) AND PUT YOU FULL CREDITS.

[INDG]FeN$x

---------- Post added at 11:17 AM ---------- Previous post was at 11:12 AM ----------

Updated first lesson in Special Notes the point 2 wich i explain why we need to do a .DEF file in MASM32 DLL compilation.

I forget about saying it so please read it if you using MASM32 for this lessons.
 
Lesson 4

Lesson 4 : Player Object Struct​

Tools: Ollydbg + PDB, Hooked DLL, notepad & Brain (not the user LOL).

Theory:​

Ok this is one hard **** to understand, probably if you code in C++ or in any other lang and you are a medium level coder, you already know about the Structs. Hell yeah structs means structure (wow), and has you can imagine an structure its a most simple way for define multiple variables and later call them in a structured way.

Since structures itself doesnt exist (caused its just data aligned into a specific memory address) they where started in high level langs like C#, C++, delphi, etc.

But in MASM32 we also have structures and yeah they help our life alot in coding, now lets see a simple structure:

Code:
MyStruct struct
 Variable1 Byte ?
 Variable2 Byte ?
 Variable3 Byte ?
MyStruct Ends

It is that simple has it looks, we define an struct called MyStruct with 3 VARIABLES of 1 BYTE SIZE.

Ok lets say we want to initialise another struct with variables of DWORD size (4 bytes), then its very simple has:

Code:
MyStruct struct
 Variable1 Dword ?
 Variable2 Dword ?
 Variable3 Dword ?
MyStruct Ends

Simple isnt?, lets get a bit further, if we want to make an struct with custom size for each variable we can define like this:

Code:
MyStruct struct
 Variable1 Byte 10 Dup(?)
 Variable2 Byte 30 Dup(?)
 Variable3 Byte 40 Dup(?)
MyStruct Ends

What we do here is that each Variable contains a BYTE repeated 10 times wich in total is 10 (of course), in the second variable we have 1 byte repeated 30 times, so the size is 30 and like that.

Now what means the DUP(?) OPERATOR?.
Well it means that our 10 bytes of variable1 can be initialised to some value, lets see how this work:

Code:
Variable1 Byte 10 Dup(1)

This means that Variable1 of 10 bytes size its initialised to 1 value in all their byte spaces. If isnt clear yet see this:

Code:
00 00 00 00 00 00 00 00 00 00 = Byte 10 Dup(0)
01 01 01 01 01 01 01 01 01 01 = Byte 10 Dup(1)
11 11 11 11 11 11 11 11 11 11 = Byte 10 Dup(11)

Ok more clear impossible, now when we use the Dup(?) means that its non initialized data, in that case our compiler will fill the struct bytes of the variable with random data.

Now finally i dont want to confuse you with the details, lets say the structure isnt initialize yet (yeah is hard to understand this part), but for really initialize the struct we need to link the struct to a global variable like this:

Code:
.Data
InitStruct MyStruct <>

So yeah now we initialize the struct, if we want to use them just do this in code:

Code:
mov eax, InitStruct.Variable1
mov edx, InitStruct.Variable2
mov ecx, InitStruct.Variable3
mov InitStruct.Variable1, eax
....

Simple, but we can also initalise the structure not in .Data section, we can do it inside a procedure with the LOCAL sentence (this means stack space):

Code:
MyProcedure Proc
LOCAL StackStruct:MyStruct
mov StackStruct.Variable1, eax
mov StactStruct.Variable2, edx
...

Enough of the theory isnt?. Well you guys needed to know this, caused GameServer use alof of structures and one of them is the Player Object Structure. Yeah this structure holds important data for each player that connects to the GameServer, data like:

Code:
- Zen
- Map
- Level
- Class, Current Quest, X & Y cords, etc, etc, etc

So this **** is the Holy grail of MuOnline coding, since if we know how manage the Player struct we can modify anything in time and invent anything. Enough of the theory...

In Work:​


1.-
Open Ollydbg and GameServer with PDB (if you dont know how read Special Notes), now isnt easy to start this **** of player struct but i think i will explain some points of this player struct:

Code:
- Player Struct it holds data of all players, so it needs to be 
  a very big space of memory for hold all that data.

- Player Struct doesnt need to be Initialized Data.

- Since Player Struct hold data of all players it has a special calculation (not complex) in base of PlayerID.

Now we know that 3 points we can pass to how this big struct is calculated and like i say its in base of PlayerID, so lets view how:

Code:
PlayerID*SizeOf PlayerStruct + Addr PlayerStruct

Ok this migth look chinnese to you if dont know much about coding, but lets review it on other way:

Code:
PlayerID * Size of the struct + Pointer to the struct

If you still confused, you are an idiot but this lessons are for idiots so here an example:

Code:
6400 * 50 + Addr MyStruct
6400 = PlayerID
50 = SizeOf Struct
Addr MyStruct = Pointer to MyStruct

Ok if you still doesnt understand i recommend you see a doctor...

2.- Now we know how Player Struct is calculated we can search for it in the god danm GameServer:

Lets press CONTROL+N for OllyDBG show us the procedures names of GameServer (FOR THIS IS REQUIRED PDB) and we will search for the proc name called:

Code:
gObjCalCharacter

Now inside of it the first **** we see is:

Code:
004C265C  |. 8B45 08        MOV EAX,DWORD PTR SS:[EBP+8]
004C265F  |. 69C0 68190000  IMUL EAX,EAX,1968
004C2665  |. 05 78F06006    ADD EAX,OFFSET 660F078

Now this is our so called PlayerObject Struct, since it follow our rule:

Code:
PlayerID*1968+660F078

But wait, how we know what [EBP+8] means?. Well if you pay attention to LESSON 1 about the INVERTED PIRAMIDAL STACK, we know about pushing arguments, and the way of read them inside of the procedure is:

Code:
Each argumment can be assumed here has a DWORD (4 bytes)

[EBP+8]--> Argument1
[EBP+0CH]--> Argument2
[EBP+10]---> Argument3

Now this was an extra, the thing is that our pushed argument its the PlayerID and i know it, caused the procedure its called "gObjCalCharacter" and the "gObj" word is usally used in GameServer procedures where the Player Struct is used and where one of the argumment its the PlayerID.

3.- Now that we find the offset of our PlayerObject Struct and we see how its calculated in the GameServer we can start getting values from it:

Lets search for procedures releated to PlayerStruct, ill pick a procedure called:

Code:
gObjLevelUP

(Ok i know you get exited with Level up things)
Once inside of this procedure we can see nothing releated to PlayerObject Struct creation, so what the f*ck is happening?

Well i can assume that Player Object was calculated before calling the procedure and probably was pushed has an argumment, but thats only my idea, lets see if its real.

Code:
004DC49D  |. 68 5C036C00    PUSH OFFSET GameServ.??_C@_0FC@HCJN@Experience?5?3?5Map?$FL?$CFd?>;  ASCII "Experience : Map[%d]-(%d,%d) [%s][%s](%d) %u %d MonsterIndex : %d, EventType : %d"

Ok se here we got our salvation we can see that it assembles a message with things has Map, Cords and other shits, so lets follow how it is called:

Code:
004DC47B  |. 8B45 08        MOV EAX,DWORD PTR SS:[EBP+8]
004DC47E  |. 0FBF88 0A01000>MOVSX ECX,WORD PTR DS:[EAX+10A]
004DC485  |. 51             PUSH ECX
004DC486  |. 8B55 08        MOV EDX,DWORD PTR SS:[EBP+8]
004DC489  |. 0FBF82 0801000>MOVSX EAX,WORD PTR DS:[EDX+108]
004DC490  |. 50             PUSH EAX
004DC491  |. 8B4D 08        MOV ECX,DWORD PTR SS:[EBP+8]
004DC494  |. 33D2           XOR EDX,EDX
004DC496  |. 8A91 0D010000  MOV DL,BYTE PTR DS:[ECX+10D]
004DC49C  |. 52             PUSH EDX
004DC49D  |. 68 5C036C00    PUSH OFFSET GameServ.??_C@_0FC@HCJN@Experience?5?3?5Map?$FL?$CFd?>;  ASCII "Experience : Map[%d]-(%d,%d) [%s][%s](%d) %u %d MonsterIndex : %d, EventType : %d"
004DC4A2  |. FF15 2C993506  CALL DWORD PTR DS:[VEBLOODCASTLE@@H@Z@4FA]

I know the argumments are more, but i just pick this important ones:

Code:
004DC491  |. 8B4D 08        MOV ECX,DWORD PTR SS:[EBP+8]
004DC494  |. 33D2           XOR EDX,EDX
004DC496  |. 8A91 0D010000  MOV DL,BYTE PTR DS:[ECX+10D]

Map[%d]

004DC486  |. 8B55 08        MOV EDX,DWORD PTR SS:[EBP+8]
004DC489  |. 0FBF82 0801000>MOVSX EAX,WORD PTR DS:[EDX+108]
004DC490  |. 50             PUSH EAX
004DC47B  |. 8B45 08        MOV EAX,DWORD PTR SS:[EBP+8]
004DC47E  |. 0FBF88 0A01000>MOVSX ECX,WORD PTR DS:[EAX+10A]
004DC485  |. 51             PUSH ECX

-(%d)(%d)

Ok if you are smarter enough youll know that our PlayerObject is in there since we push MAP, CordX, CordY, so has you already guess the Player Struct is the argumment:

Code:
MOV ECX,DWORD PTR SS:[EBP+8]

And what is this?

Code:
MOV DL,BYTE PTR DS:[ECX+10D]

Well this means that in base of PlayerStruct+10D we have our Map number of the player, ill explain it like this:

Code:
mov dl, PlayerStruct.MAPNumber

So we finally get our first PlayerStruct number:

Code:
PlayerStruct + 10D = MapNumber

But what we can do with this?. Well things like that:

Code:
.const
MapNumber equ 10Dh ;Define that 10D = MapNumber
Lorencia equ NULL ;Define that 00 == Lorencia
Noria equ 01 ;Define that 1 == Noria

.code

TestProcedure Proc PlayerID:Dword
mov eax, PlayerID ;PlayerID -.-
imul eax, eax, 1968 ;Size of the struct
add eax, 660F078 ;Addr of the struct
mov dl, byte ptr [eax+MapNumber] ;DL == MapNumber
.if (dl == Lorencia) ;If we are in Lorencia
  ...
.elseif (dl == Noria) ;If we are in Noria
  ...
.else ;If we are not in Lorencia or Noria
  ...
.endif

Ok so you now understand why is this the Holy Grail of MuOnline?
Caused if you master the PlayerStruct and know all its values, you can retrieve any information and do whatever you want with it, like check if we are in a Map at some Cords then we do something, or if we have this ammount of zen, then i move you to this map, etc, etc, etc.

4.- Now we know about the PlayerObject struct how find it and how retrieve some values, the real deal is to find all the values for the Player Object struct, we have 2 ways for do that:

Code:
- Download a PDB dumper
- Dissamble ourself

If i dont remember well Syser debugger on it first versions can read GameServer.exe with the PDB and display the whole structures of Player Object along with their values in a good looking way.

Code:
http://www.sysersoft.com/

5.- Enjoy looking Player Object struct values.

Lesson Review:​

This was a little hard to understand but not that hard...
In old days this used to be the big secret of MuOnline coding, and it all turned arround to look and find more player object values so you can edit everything, but has you read, "EDIT", since if we want to add new spaces to the current Player Object struct then we will have to dissamble, add space by LordPE and adjust everything (a big ****).

But later in FHX team (FeN$x and Holy) the first server versions with custom Player Object came to ligth, now on days on this community you can still see custom player objects, dissamble gameservers sources, emulators, etc.

I recommend if you want to do a good thing for the community that you decompile and create your own emu or share information with the rest of coders. Also if you are only a guy that want to EDIT and do nothing out of common then just stay with the current Player Object struct and find all its values.


Special Notes:​

- Dont be a retarded... Use PDB for get all the Player object struct values (look in google some PDB dumper)

- Wait for the next lesson for how to work with this Player struct in combination of GameServer procedures.
 
Last edited:
No i mean is this supposed to bypass the gameguard? because I can't find this file in my MUonline folder

This has nothing to do about gameguard please if you dont know how to read just avoid posting, the title say MU coding lessons...

If "coding" means GameGuard then ill assume the world is so lost that we will have third world war for a god danm frog...
 
Lesson 5 part 1

Lesson 5: Player Object & GameServer Procedures​
The Real Deal​
Part 1​

Tools: Ollydbg, Your DLL, Brain !

Theory:​

Well like in the old lesson we learn how to identify the Player Object struct, how its calculated and for what is used to, we can finally start making real changes in MuOnline, this is the real deal my friend, and if you pay attention and read the last lessons this is your reward.

Using GameServer procedures in combination of Player Object struct will allow you to edit the whole game if you want to (but not making it a new one) we can start from simple things like teleporting any player, like complex thing like making a whole new event.

This is why i have been preparing you with the other tutorials, dissamble fuctions will help you to know how GameServer works better, learning player object will give you thons of help, adding options will make you edit important pieces of code.

Well i wont bore you that much, since you want to learn this **** fast and i want you do it. (more in fact, i want you fix this god danm community instead of tearing it apart with noob things).



In Work:​

1.- Open GameServer.exe with PDB

2.- Now ill teach you how teleport a god danm player that simple, so we can start looking for basic things about teleporting, lets think like a MuOnline gamer:

Code:
Player Cord X
Player Cord Y
Player Map
Player ID

Ok this things are nessesarie to make a teleport, now lets think like coders, where we can find that kind of ****. Well we can find it on Player Object Struct, but lets think a little better with this question:

Code:
What happen if i edit cords and map, it will teleport the player automatic?

Well it wont, since we need to send a Packet, so isnt that simple like editing values from Player Object struct, so what we can do?. We can search for a GameServer procedure that do our whole job of teleporting, yeah just like that i found this:

Code:
gObjTeleport

Remember you have to search procedures names with CONTROL+N

3.- Lets see how this procedure works:

Inside gObjTeleport we can see this first:

Code:
004FCAE9  |. 837D 08 00     CMP DWORD PTR SS:[EBP+8],0
004FCAED  |. 7C 09          JL SHORT GameServ.004FCAF8
004FCAEF  |. 817D 08 E71C00>CMP DWORD PTR SS:[EBP+8],1CE7
004FCAF6  |. 7E 05          JLE SHORT GameServ.004FCAFD
004FCAF8  |> E9 DB020000    JMP GameServ.004FCDD8

This is definitely PlayerID argument (read lesson 3 and 4 about inverted piramidal stack for arguments pushed), but does really PlayerID start from 0 to 1CE7h?.

Well the truth is that doesnt, PlayerID start from 1900h (HEX) to 1CE7h (HEX), so why from 0 in this procedure?

Well caused this procedure accepts to teleport even Monsters ! :scared:

And has you guess then Monsters ID start from 0 to 18FFh. Great now we have the first argument that is PlayerID we can search others:

Code:
004FCB0E  |. 8B4D 0C        MOV ECX,DWORD PTR SS:[EBP+C]
004FCB11  |. 51             PUSH ECX
004FCB12  |. E8 087BF0FF    CALL GameServ.0040461F

If we see the call name in this place i see its called:

Code:
0040461F   $ E9 FCB00800    JMP GameServ.MapNumberCheck

Ok MapNumberCheck, so yeah this argument MUST BE Player Map. Now ill keep looking more arguments and i found last 2:

Code:
004FCBBF  |> 8A55 14        MOV DL,BYTE PTR SS:[EBP+14]
004FCBC2  |. 52             PUSH EDX
004FCBC3  |. 8A45 10        MOV AL,BYTE PTR SS:[EBP+10]
004FCBC6  |. 50             PUSH EAX

Ok lets see what this two shits can be, ok if we have PlayerID, PlayerMap, guess what this 2 things are...

Yeah cord X and cord Y, and i can know it caused it uses 1 byte (MOV DL, Byte ptr) so this is all we have all we know, and since i dont see more arguments this everything, lets see how our procedure gets:


Code:
gObjTeleport(PlayerID, Map, CordX, CordY)

4.- Ok so now that we have everything lets apply it by hooking in somewhere where we can test this ****:

If you smart enough youll figure out we need to hook in a place where we use PlayerID, since we dont know what PlayerID to use and we cant be sending random people to random places XD.

I find a simple and idiot NPC procedure:

Code:
00403A7B   $ E9 30FE1400    JMP GameServ.NpcDeviasMadam

Who the f*ck know this NPC?. Anyway we can use him has a test experiment.

First lets see what arguments this NPC use:

Code:
005538D3  |. 8B45 0C        MOV EAX,DWORD PTR SS:[EBP+C]
005538D6  |. 8B08           MOV ECX,DWORD PTR DS:[EAX]

Ok just one argument but if you see the call before it pushes 2 arguments (idiot Webzen) anyway like we use only one i can assume it must be PlayerID or NPCID.

But if we look closer we can see that its moving to ECX REGIST, the value inside of the argument, so this can be only one god danm ****... The argument its the Player Object Struct and what it moves to ECX its the PlayerID :scared:

Yeah in fact the first variable of Player Object Struct its:

Code:
PlayerObject.PlayerID

So finally we can do some real deal.

5.- We will use for hook, my easy library called "HookThis", you can download from RZ released section and finally the source will look like this:

Code:
.586
.Model flat, StdCall
Option CaseMap:none
Include	\masm32\include\windows.inc
Include	\masm32\include\kernel32.inc
IncludeLib \masm32\lib\kernel32.lib
IncludeLib \masm32\lib\user32.lib
include HookThis.inc
includelib HookThis.lib

.const
  gObjTeleport DD 00402CA7h ;Define Addr of gObjTeleport
.data?
.data
.code

DllEntry Proc hInst:HINSTANCE, reason:DWord, reserved1:DWord
 .if (reason == DLL_PROCESS_ATTACH)
   invoke HookThis, 005538B0h, Offset NPCNoria, 1 ;Hook
 .endif
 mov eax, TRUE
 ret
DllEntry EndP

NPCNoria Proc
local CordX:Byte
local CordY:Byte
local MapID:Dword
 mov CordX, 156 ;Play with this value
 mov CordY, 45 ;Play with this value
 mov MapID, 2  ;Play with this value
 mov eax, dword ptr [ebp+8] ;Player Obj Struct
 mov edx, [eax] ;EDX = PlayerID
;-----------------------------------------------
 push CordX
 push CordY
 push MapID
 push edx ;PlayerID
 call gObjTeleport
;------------------------------------------------
 invoke UnHookThis, 005538B0h, 1 ;UnHook
 ret
NPCNoria Endp

End DllEntry

Ok ill analyze the important parts:

Code:
invoke HookThis, 005538B0h, Offset NPCNoria, 1

This will hook into the addr 005538B0h wich is the start of NPCNoriaRara procedure:

Code:
00553860 >/> 55             PUSH EBP

Now this part:

Code:
 push CordX
 push CordY
 push MapID
 push edx ;PlayerID
 call gObjTeleport

We call gObjTeleport that its the addr 00402CA7h with the right arguments for everything to work.

Finally:

Code:
invoke UnHookThis, 005538B0h, 1

We unhook the addr 005538B0 of NPCNoriaRara and keep the execution back to where it belongs (display NPC window).

6.- Finally you have done your real first **** in MuOnline, youll talk to NPC noria rara wich i dont know where is founded and youll be teleported to the place you put in DLL :P:

7.-Enjoy you now a beginner in MuOnline coding.

Lesson Review:​


I can review this lesson in this basic steps:


Code:
1.- Find gObjTeleport
2.- Dissamble to find arguments
3.- Find a place to hook for test
4.- Test the hook and gObjTeleport
5.- Enjoy teleporting.

But it can be more resumed and involved into a global description like:

Code:
1.- Find a procedure
2.- Dissamble it for know how it works
3.- Find how test it
4.- Test it
5.- Enjoy.

Special Notes:​

- Compile the DLL same way has on lesson 3:

Code:
if exist TEST.obj del TEST.obj
if exist TEST.dll del TEST.dll

\masm32\bin\ml /c /coff TEST.asm
\masm32\bin\Link /SUBSYSTEM:WINDOWS /DLL /DEF:TEST.def /LIBPATH:c\masm32\lib TEST.obj

pause

- We dont need to make a valid .def file since we dont use GetProcAddress, we just use LoadLibrary and load the hook in DLLEntry procedure

- The second part of this lesson is mixing Player Object struct with procedures for do bigger things

- Use PDB for avoid using your brain too much and searching interesting procedures

- Download HookThis library it fucking rulz.


---------- Post added at 06:06 AM ---------- Previous post was at 05:53 AM ----------

Last lesson was about teleporting a player, things are getting interesting the part 2 of this lesson i have to choose between:

- Upgrading Zen
- Upgrading Level
- Hook into another NPC

Ill choose between Zen and Level.
 
Last edited:
Hi, please check. Did I do everything correct? ( about hooking dll)
I was using 1.00.87 original which draklv posted

Before saving
Code:
/////////////////////////////////////////////////////////////////////////////////////////////////
Entry Point
00619675 >   E9 664A0F00    JMP GameServ.0070E0E0
/////////////////////////////////////////////////////////////////////////////////////////////////
DLL
0070E100     68 30E17000    PUSH GameServ.0070E130                   ;  ASCII "MyDLL.dll"
0070E105     FF15 F0ACFF0A  CALL DWORD PTR DS:[<&KERNEL32.LoadLibrar>;  kernel32.LoadLibraryA
0070E10B     68 3FE17000    PUSH GameServ.0070E13F
0070E110     50             PUSH EAX
0070E111     FF15 ECACFF0A  CALL DWORD PTR DS:[<&KERNEL32.GetProcAdd>;  kernel32.GetProcAddress
0070E117     FFD0           CALL EAX
0070E119     55             PUSH EBP
0070E11A     8BEC           MOV EBP,ESP
0070E11C     6A FF          PUSH -1
0070E11E     68 8A966100    PUSH GameServ.0061968A



After Saving
Code:
/////////////////////////////////////////////////////////////////////////////////////////////////
Entry Point
00619675 > $ E9 664A0F00    JMP GameServ.0070E0E0
0061967A   . 68 204D7900    PUSH GameServ.00794D20
0061967F   . 68 286E6100    PUSH GameServ.00616E28                   ;  SE handler installation
00619684   . 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
0061968A   . 50             PUSH EAX
/////////////////////////////////////////////////////////////////////////////////////////////////
DLL
0070E100   . 68 30E17000    PUSH GameServ.0070E130                   ; /FileName = "MyDLL.dll"
0070E105   . FF15 F0ACFF0A  CALL DWORD PTR DS:[<&KERNEL32.LoadLibrar>; \LoadLibraryA
0070E10B   . 68 3FE17000    PUSH GameServ.0070E13F                   ; /ProcNameOrOrdinal = ""
0070E110   . 50             PUSH EAX                                 ; |hModule
0070E111   . FF15 ECACFF0A  CALL DWORD PTR DS:[<&KERNEL32.GetProcAdd>; \GetProcAddress
0070E117   . FFD0           CALL EAX
0070E119   . 55             PUSH EBP
0070E11A   . 8BEC           MOV EBP,ESP
0070E11C   . 6A FF          PUSH -1
0070E11E   . 68 8A966100    PUSH GameServ.0061968A
0070E123   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E125   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E127   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E129   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E12B   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E12D   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E12F   . 004D 79        ADD BYTE PTR SS:[EBP+79],CL
0070E132   . 44             INC ESP
0070E133   . 4C             DEC ESP
0070E134   . 4C             DEC ESP
0070E135   . 2E:            PREFIX CS:                               ;  Superfluous prefix
0070E136   . 64:6C          INS BYTE PTR ES:[EDI],DX                 ;  I/O command
0070E138   . 6C             INS BYTE PTR ES:[EDI],DX                 ;  I/O command
0070E139   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E13B   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E13D   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E13F   . 0000           ADD BYTE PTR DS:[EAX],AL
 
Hi, please check. Did I do everything correct? ( about hooking dll)
I was using 1.00.87 original which draklv posted

Before saving
Code:
/////////////////////////////////////////////////////////////////////////////////////////////////
Entry Point
00619675 >   E9 664A0F00    JMP GameServ.0070E0E0
/////////////////////////////////////////////////////////////////////////////////////////////////
DLL
0070E100     68 30E17000    PUSH GameServ.0070E130                   ;  ASCII "MyDLL.dll"
0070E105     FF15 F0ACFF0A  CALL DWORD PTR DS:[<&KERNEL32.LoadLibrar>;  kernel32.LoadLibraryA
0070E10B     68 3FE17000    PUSH GameServ.0070E13F
0070E110     50             PUSH EAX
0070E111     FF15 ECACFF0A  CALL DWORD PTR DS:[<&KERNEL32.GetProcAdd>;  kernel32.GetProcAddress
0070E117     FFD0           CALL EAX
0070E119     55             PUSH EBP
0070E11A     8BEC           MOV EBP,ESP
0070E11C     6A FF          PUSH -1
0070E11E     68 8A966100    PUSH GameServ.0061968A



After Saving
Code:
/////////////////////////////////////////////////////////////////////////////////////////////////
Entry Point
00619675 > $ E9 664A0F00    JMP GameServ.0070E0E0
0061967A   . 68 204D7900    PUSH GameServ.00794D20
0061967F   . 68 286E6100    PUSH GameServ.00616E28                   ;  SE handler installation
00619684   . 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
0061968A   . 50             PUSH EAX
/////////////////////////////////////////////////////////////////////////////////////////////////
DLL
0070E100   . 68 30E17000    PUSH GameServ.0070E130                   ; /FileName = "MyDLL.dll"
0070E105   . FF15 F0ACFF0A  CALL DWORD PTR DS:[<&KERNEL32.LoadLibrar>; \LoadLibraryA
0070E10B   . 68 3FE17000    PUSH GameServ.0070E13F                   ; /ProcNameOrOrdinal = ""
0070E110   . 50             PUSH EAX                                 ; |hModule
0070E111   . FF15 ECACFF0A  CALL DWORD PTR DS:[<&KERNEL32.GetProcAdd>; \GetProcAddress
0070E117   . FFD0           CALL EAX
0070E119   . 55             PUSH EBP
0070E11A   . 8BEC           MOV EBP,ESP
0070E11C   . 6A FF          PUSH -1
0070E11E   . 68 8A966100    PUSH GameServ.0061968A
0070E123   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E125   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E127   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E129   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E12B   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E12D   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E12F   . 004D 79        ADD BYTE PTR SS:[EBP+79],CL
0070E132   . 44             INC ESP
0070E133   . 4C             DEC ESP
0070E134   . 4C             DEC ESP
0070E135   . 2E:            PREFIX CS:                               ;  Superfluous prefix
0070E136   . 64:6C          INS BYTE PTR ES:[EDI],DX                 ;  I/O command
0070E138   . 6C             INS BYTE PTR ES:[EDI],DX                 ;  I/O command
0070E139   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E13B   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E13D   . 0000           ADD BYTE PTR DS:[EAX],AL
0070E13F   . 0000           ADD BYTE PTR DS:[EAX],AL

Ok you where close look here:

Code:
0070E10B   . 68 3FE17000    PUSH GameServ.0070E13F                   ; /ProcNameOrOrdinal = ""

The PUSH 0070E13F needs to have a string inside, you put no string inside, also this string needs to be the procedure name of the DLL to load.

If you loading another people DLL you should ask him what is the "EXPORT" string for his DLL to load.

Example:

Code:
0070E10B   . 68 3FE17000    PUSH GameServ.0070E13F                   ; /ProcNameOrOrdinal = "MyProcName"

Now here the last problem:

Code:
0070E119   . 55             PUSH EBP
0070E11A   . 8BEC           MOV EBP,ESP
0070E11C   . 6A FF          PUSH -1
0070E11E   . 68 8A966100    PUSH GameServ.0061968A
0070E123   . 0000           ADD BYTE PTR DS:[EAX],AL

You miss the JMP jump to the EntryPoint (but remember it cant be the same JMP you writte at the begin of GameServer, or youll make an eternal loop), look this example:

Code:
0070E119   . 55             PUSH EBP
0070E11A   . 8BEC           MOV EBP,ESP
0070E11C   . 6A FF          PUSH -1
0070E11E   . 68 8A966100    PUSH GameServ.0061968A
0070E123   . 0000           JMP 0061967A

If you have more problems tell me.
 
Last edited:
Now here the last problem:

Code:
0070E119   . 55             PUSH EBP
0070E11A   . 8BEC           MOV EBP,ESP
0070E11C   . 6A FF          PUSH -1
0070E11E   . 68 8A966100    PUSH GameServ.0061968A
0070E123   . 0000           ADD BYTE PTR DS:[EAX],AL
You miss the JMP jump to the EntryPoint (but remember it cant be the same JMP you writte at the begin of GameServer, or youll make an eternal loop), look this example:

Code:
0070E119   . 55             PUSH EBP
0070E11A   . 8BEC           MOV EBP,ESP
0070E11C   . 6A FF          PUSH -1
0070E11E   . 68 8A966100    PUSH GameServ.0061968A
0070E123   . 0000           JMP 0061967A
I got this, my mistake. I was thinking u added +5 more addresses i shouldn't do this, just next after entry point.




Code:
0070E10B   . 68 3FE17000    PUSH GameServ.0070E13F                    ; /ProcNameOrOrdinal = ""
The PUSH 0070E13F needs to have a string inside, you put no string inside, also this string needs to be the procedure name of the DLL to load.

If you loading another people DLL you should ask him what is the "EXPORT" string for his DLL to load.

Example:

Code:
0070E10B   . 68 3FE17000    PUSH GameServ.0070E13F                    ; /ProcNameOrOrdinal = "MyProcName"
I have a question about my procedure name. Could it be any name ? or should it be a specific name? or should it be a specific name from dll sources?

Thank you.
 
I got this, my mistake. I was thinking u added +5 more addresses i shouldn't do this, just next after entry point.





I have a question about my procedure name. Could it be any name ? or should it be a specific name? or should it be a specific name from dll sources?

Thank you.

Any name you want if its your DLL, just define it on a EXPORT file (.def extension).


Code:
LIBRARY TEST ;NAME OF DLL 
EXPORTS MyProcedure ;NAME OF PROCEDURE TO EXPORT


LIBRARY "Test" (that test string should be the name of your DLL)
EXPORTS MyProcedure (the procedure you want to export, by later using it with GetProcAddress)



I recommend instead you passing all that problems just use my easy hook library (HookThis) is called and you can donwnload in RZ released section.

So you just LoadLibrary and writte in your DLL entry point with HookLibrary to hook the next instruction after the LoadLibrary and it will work exactly the same has you put GetProcAddress.

Its more simple, read the documentation i left and the examples on the HookThis released.

Also if you want to do old way, remembert to make the .def file with the name of the procedure you want to export and finally call it with GetProcAddress.



PS: If its not your DLL ask for the coder the EXPORT name for load into getprocaddress.
 
Last edited:
I want to make my dll in C# as I saw you said you are welcome with C#. I want to try to do everything in C# as you did here in asm. And also to become familiar with asm ofc )))
Just need to understand how everything works.

Thank you for helping.
 
Last edited:
I want to make my dll in C# as I saw you said you are welcome with C#. I want to try to do everything in C# as you did here in asm. And also to become familiar with asm ofc )))
Just need to understand how everything works.

Thank you for helping.

The .def file its the same for C++ or C# just read here:



This is becaused MASM32 the compiler itself is part of Visual Studio C++ ^^.

Anyway just read that microsoft paper and its exactly the same has on ASM, if you have more questions add me to my mail.

Send me a PM with yours and ill add you =P
 
sry for noob question but i just start now to understand something XD

on lesson 3

1st passage

how did you get
Code:
004978E1  |. C745 D8 231C00>MOV DWORD PTR SS:[EBP-28],1C23

from
Code:
004978B8  |. 68 30A16900    PUSH IAT-Game.0069A130                   ;  ASCII "[PotionMix][ShieldPotion Lv1 Mix] - Mix Start"
i don't undesrtand this :|
 
sry for noob question but i just start now to understand something XD

on lesson 3

1st passage

how did you get
Code:
004978E1  |. C745 D8 231C00>MOV DWORD PTR SS:[EBP-28],1C23

from
Code:
004978B8  |. 68 30A16900    PUSH IAT-Game.0069A130                   ;  ASCII "[PotionMix][ShieldPotion Lv1 Mix] - Mix Start"
i don't undesrtand this :|


I just choose a procedure to debug for seek for an "HARDCODED" value, understand by hardcoded that are values you can see at simple sight and most of them belongs to something that you can edit and expect a different result.

Depending of the context (it was a PotionMix) and i see this HARDCODED value (just looking down in OllyDBG) i could assume this was something to catch an eye on...

With experience youll find this values in the blink of an eye.
 
in my gs
when i try to do wings of 3 lvl;
if i fail the exellent don't disappear ( only decrease lvl of item)
i want to do that if i fall all item disappear...

i go to all reference text string... and search for wing combination..

theunknownguy - MU Coding Lessons - RaGEZONE Forums

i think i must go to
Third Wing Mix Level_1 ( fail)
theunknownguy - MU Coding Lessons - RaGEZONE Forums
or
Third Wing Mix Level_2 ( fail)
theunknownguy - MU Coding Lessons - RaGEZONE Forums

but what is the difference ? :| 1 from 2 ?
anyway are the same ...
 
Back