- Joined
- Jan 18, 2010
- Messages
- 3,109
- Reaction score
- 1,140
Eric Class
Since I'm bored and was testing some client stuff out for a friend, I figured why not release this now that implementing custom skills was publicly released.
Before I start, in case you're confused about what the heck an "Eric Class" is, check out Nexon's official April Fools video:
Furthermore, please note that this was specifically intended for v90 only, but can be altered obviously to fit any version. I never ended up updating this when we had moved to v92 since I was too busy working on resolution hacks, so this is all I got.
In order to pull this off you'll need a few tools:
-
You must be registered to see links
(password: tuts4you)-
You must be registered to see links
- STREDIT
- HaRepacker (or any alt. WZ editing tool)
-
You must be registered to see links
-
You must be registered to see links
- Basic understanding of assembly code
Step 1. Creating a new Section Header
After you've downloaded and configured the above tools, the first thing we're going to do is construct a new section within the MapleStory application. Go ahead and open CFF Explorer, then browse and open your Localhost.exe application. Once you've loaded it into the suite, navigate to the 'Section Headers' tab - you should see the current sections within the client:
Scroll down to the bottom of the list like I've done, and right-click anywhere within the tab. Upon right-clicking, select the "Add Section (Empty Space)" option for a new window to appear.
Within the new dialogue, input 1000h and click OK - this is going to allocate a new 4096-byte section.
After the new section is created and loaded into the tab, double-click into the empty namebox and name the section. If done correctly, it will now look like this:
Finally, you can save your new (and bigger!) client via File->Save As, and name it whatever you want. This is the client we'll be using to further perform assembly changes.
Step 2. Code-caving to create a new class
Since we've successfully created an entire new section for ourselves in the client, we're going to make the client jump there and continue executing additional instructions before jumping back. The first modification we're going to be doing is appending jobs to the client's get_job_name function.
Funfact: Ever wondered why adding a new job XML to the WZ files would always crash your client at startup? This is because that JobID doesn't exist in the get_job_name function. that we're about to modify ;-)
Load up OllyDbg and open the new client you just exported from CFF Explorer. If you open the Memory Map, you'll notice that it has loaded in your newly created section and its starting point address:
Assuming you're using a clean v90 client without any previous changes and you did everything correctly, the address will be the same as mine (01161000).
Alright, so we need to move to the get_job_name code-cave point. Using the CTRL + G hotkey, jump to the address 004D299F. Now, select all of the instructions from that address down to 004D29CD and right-click on them. Navigate to Binary->Fill with NOPs and select the option.
Once you've nopped out the instructions, assemble the instruction jmp 00D95F86 on the initial nopped address (SPACE hotkey). Then, click/highlight the new instruction and follow it (ENTER hotkey).
Let's repeat the same process - highlight and NOP from the initial address (00D95F86) to 00D95F88 (the first two lines). So now we're going to rewrite what we just removed, but we'll be executing it at the very end of the PE's code section. To make it easier for you so that there's no need to manually re-write each instruction, simply copy this to your clipboard:
Code:
C7 45 E4 B8 0D 00 00 68 48 17 00 00 8D 45 E0 50 E8 10 83 AD FF 8B C8 E8 16 0B 67 FF 50 8D 45 E4
50 8B CE E8 7F E1 73 FF 8D 4D E0 89 5D FC E8 89 09 67 FF E9 43 B0 3C 00
Before we continue, save your current changes (right-click, Copy to executable->All changes) and then load the saved executable! Since the next code-cave will be assembled outside of the PE's code section, it will not copy all of your modifications; you must save them separately.
If you've done everything correctly, then you can follow the last instruction (the custom section code-cave jump) by using the ENTER hotkey at address 00D95FBC. The location that you're at now is where you can add as many new jobs as you wish. Let us start by adding Eric Class - copy the following binary data to clipboard:
Code:
C7 45 E4 A0 0F 00 00 68 A4 06 00 00 8D 45 E0 50 E8 95 D2 70 FF 8B C8 E8 9B 5A 2A FF 50 8D 45 E4
50 8B CE E8 04 31 37 FF 8D 4D E0 89 5D FC E8 0E 59 2A FF E9 95 19 37 FF
So how does the above structure work exactly? Each block of instructions that you just pasted contains two key identifiers: 1) the JobID to push into the hashmap, and 2) the StringPool ID that links to the string containing the name of the job. I have highlighted both below.
Code:
01161004 C745 E4 D80E0000 MOV DWORD PTR SS:[EBP-0x1C],0xFA0 ; Job ID
0116100B 68 A4060000 PUSH 0x6A4 ; StringPoolID of Class Name
01161010 8D45 E0 LEA EAX,DWORD PTR SS:[EBP-0x20]
01161013 50 PUSH EAX
01161014 E8 95D270FF CALL 0086E2AE ; StringPool::GetInstance
01161019 8BC8 MOV ECX,EAX
0116101B E8 9B5A2AFF CALL 00406ABB ; StringPool::GetString
01161020 50 PUSH EAX
01161021 8D45 E4 LEA EAX,DWORD PTR SS:[EBP-0x1C]
01161024 50 PUSH EAX
01161025 8BCE MOV ECX,ESI
01161027 E8 043137FF CALL 004D4130 ; ZMap<long_ZXString<char>_long>::Insert
0116102C 8D4D E0 LEA ECX,DWORD PTR SS:[EBP-0x20]
0116102F 895D FC MOV DWORD PTR SS:[EBP-0x4],EBX
01161032 E8 0E592AFF CALL 00406945 ; ZXString<char>::_Release
01161037 - E9 951937FF JMP 004D29D1 ; Return back to get_job_name
Step 3. Updating the StringPool
Open up STREDIT and browse for your new Eric Class client. On the right hand side you'll see a command textbox. Recalling that the StringPoolID was 1700 (decimal), type the command Id=1700 and click Run. It should fetch some unused korean string, just double-click the Content textbox and change it to the job name "Eric Class".
Finally, simply File->Save it, and your client is all setup to become the Eric Class in-game!
Step 4. Adding new skills to the WZ files
At this point, the client is now capable of job changing into the Eric Class and will properly display it above your name. However, once you attempt to open your skill window, you'll crash with some sick WZ errors. This is because you're missing the skill tree for the class in the game files.
Go ahead and load up HaRepacker, and browse for your Skill.wz file. Select the WzFile node itself ("Skill.wz"), and navigate to Tools->Import->XML and select the Eric Class XML that you downloaded. You'll notice that the XML contains skills, but don't worry because we're going to be code-caving their functionality next. After the skill tree has imported ("4000.img"), you're done - go ahead and save it.
Unload the Skill.wz file, load String.wz, and then repeat the process. However, this time before importing the new Skill.img XML, delete the current one (select the "Skill.img" node and use DEL hotkey). Save it once it's finished importing, and you're finished with the WZ Editing portion.
Step 5. Code-caving to add custom skills
From here, you should now be able to get in-game with your new job and open up your skill window/allocate skill points, but unable to physically use any of the Eric Class skills. Let's fix that, shall we?
Open up OllyDbg and browse for your latest custom client. We must first redirect CUserLocal:oActiveSkill to our custom region before it's able to register our dank new skills. Go to address 00AA43CC and assemble the instruction JG 01161105. Just like last time, the next edits are outside of this section and must be saved separately. So, save your changes to file and then load that new file into Olly.
Once you've loaded your last updated client, move to address 01161103. Right-click and navigate to Binary->Fill with NOPs on that address. Copy this last block of binary data to clipboard:
Code:
81 FE E8 5D 62 02 0F 8C 67 33 94 FF 81 FE E9 5D 62 02 0F 84 47 2F 94 FF 81 FE EA 5D 62 02 0F 84
1A 20 94 FF 81 FE EB 5D 62 02 0F 84 C0 29 94 FF 81 FE EC 5D 62 02 0F 84 B4 29 94 FF E9 54 33 94
FF 90
Alright, so now to explain what we just did in case you want to add more skills in the future, I've commented the checks it's performing at each jump below.
Code:
00AA43CC - 0F8F 33CD6B00 JG 01161105 ; if (nSkillID > 35111012) jmp
01161103 90 NOP
01161104 90 NOP
01161105 81FE E8542502 CMP ESI, 2625DE8
0116110B - 0F8C 673394FF JL 00AA4478 ; if (nSkillID < 40001000, aka invalid skill) jmp
01161111 81FE 69D94302 CMP ESI, 2625DE9 ; Eric Class - Retreat (40001001)
01161117 - 0F84 472F94FF JE 00AA4064
0116111D 81FE 6AD94302 CMP ESI, 2625DEA ; Eric Class - Jump (40001002)
01161123 - 0F84 1A2094FF JE 00AA3143
01161129 81FE 6BD94302 CMP ESI, 2625DEB ; Eric Class - Camouflage (40001003)
0116112F - 0F84 C02994FF JE 00AA3AF5
01161135 81FE 6CD94302 CMP ESI, 2625DEC ; Eric Class - Eric Blessing (40001004)
0116113B - 0F84 B42994FF JE 00AA3AF5
01161141 - E9 543394FF JMP 00AA449A
01161146 90 NOP
Notice how I'm jumping to a few different locations based on the SkillID? This is because I'm calling to specific skill action functions. Below are the ones that I've personally noted and used.
Code:
// -- Buffs => 00AA3AF5 ; CUserLocal::DoActiveSkill_StatChange
// -- Melee Attacks => 00AA2765 ; CUserLocal::DoActiveSkill_MeleeAttack
// -- Teleport => 00AA4064 ; CUserLocal::TryRegisterTeleport
// -- Flying => 00AA3C28 ; CUserLocal::DoActiveSkill_Flying
// -- Wings => 00AA3143 ; CUserLocal::TryDoingWings
Well, that should be everything - hopefully I didn't forget anything. Enjoy!
- Eric