Quote:
Originally Posted by S?oosh
The game sucks, reversing it and writing "addons" is fun though
|
Yep, I agree lol. I don't think I'd ever want to play it again, but it's an easy game to fiddle with... which is quite fun
Anyway, the tutorial I'm writing has turned out to be a very very long one lol. It's too late and I need some sleep, so I'll finish it tomorrow evening.
90% done though

There should hopefully be something in this one for everyone though, perhaps a few little gems that nobody knew about
EDIT: Ok, deeeeep breath, here goes
Ok, all of the following stuff is actually stuff I was going to keep to myself as I was using it for a pretty kickass external PWI chat interface. Kinda like an MSN sorta thing. I have mentioned this a lot but never finished the bastard thing lol.
So... As it's a little less likely now that I'll ever finish it, here's some tricks I might as well share

They're nothing particularly groundbreaking, but they are a bastard to work out in the first place and they're potentially pretty useful.
Getting information about items linked in chat:
As I begin writing this, I don't intend to go into great detail about how to find all this stuff, but as the tutorial progresses, I'll probably:
a) Cave in and show it anyway because of my OCD
b) Find it's actually necessary to go into detail just to show how it works
c) Decide it's probably worthwhile explaining it so you guys can figure out how to update this stuff next time the client gets updated. (*hint hint*)
Notes:
- All information here is valid as of PWI client v676. Offsets / code excerpts / methods WILL vary for different clients
- I've just realised the guy I'm writing this tutorial for doesn't play this version so I'm gonna have to explain how to find more stuff. D'oh!
- You will need to be semi-proficient in the use of Cheat Engine *AND* OllyDbg (you should be anyway if you're into this kinda stuff)
- I am writing this tutorial based on a the requirement I encountered for the program I was writing, in which I wanted to display the item in an external interface as it was displayed in-game, i.e., name, colour, description.
- A bunch of stuff will simply be explained with C++ code snippets. You may have to figure out what they're doing for yourself if you want to port them to another programming language.
Let's start from the beginning with a little recap.
First post in this thread describes how to find the base addresses for the chat system. More importantly, for the list of messages you see in game in the chat window.
If you have trouble following that, then abort this mission now lol.
For readability, the first offsets of interest are (for PWI):
(By the way, I don't use decimal offsets because that's stupid in this line of work)
Code:
baseCall: 0xB8FBCC
chatBaseAddr: 0xB94C70
lastChatIndex: 0xB94C7C
The structure of a chat message is:
Code:
struct chatObj
{
uint uk1; // 0x00 Unknown
char msgScope; // 0x04 I.e., world, private, faction (see messageTypes)
char smileySet; // 0x05
char uk3; // 0x06
char uk4; // 0x07
wchar *p_msg // 0x08 Pointer to actual unicode message string
uint dwItemObj // 0x0C base offset of an item object linked in chat
uint msgId // 0x10 Unique message ID (can be different from index)
uint uk5; // 0x14
uint uk6; // 0x18
}
Chat message objects/structs are 0x1C in size and there are 199 of them, so to traverse the list of messages to get the actual message text for example:
Code:
// 0 - 199 max
For(i = 0 ; i < lastChatObject ; i++)
{
p->chatObject = [[chatObjectsBase] + i*0x1C]
// Actual message
p->message = [[[chatObjectsBase] + i*0x1C]+0x8]
}
The item object is at +0xC though, so replace that 0x8 with 0xC for those.
/recap
Now for the fun stuff...
Ok, you may be aware that for item objects in general (e.g., inventory items) the description (i.e., the detailed text you see when you hover over an inventory item) is located at:
You may also be aware that this description is not available at that location until you have hovered over the item (or clicked it, in the case of chat-linked items).
That's because when you hover over the item, the game client executes a function which updates this description as and when it is needed. Clearly it would make no sense to automatically have a potentially very long string instantly available for every single item you encounter or walk nearby in game as that would use a lot of memory... and as we know, PWI devs are very careful to efficiently use memory (LOL).
So, when you programatically read a message body, i.e., the message text from the in-game structures, when an item is linked in text, you will find that the item itself is replaced with something like:
?<1><>
Anyway...
Tons of objects in PWI have what's called (or at least what I call

) a vTable, which is basically a list of function pointers that perform some sort of operation with that object. This includes item objects.
The vTable is always located at the 0th entry of the object (that's just how C++ and other OOP compilers work)
The image below shows an example of a chat-linked item object:
I've only highlighted the stuff that's relevant to us for this tutorial.
As it happens, as far as I can tell, chat-linked items use the same item object structure as items found anywhere else, e.g., inventory, catshops, etc. However, for most purposes, we don't need to utilise the functions that we need for the purpose of identifying them in a chat message, so they have, for the most part, remained undiscovered / purposeless.
In the above image, you'll see that there are three function addresses highlighted within the vTable
In brief, those functions are (as I have chosen to name them):
getItemName 0x14
getItemDescription 0x40
getItemColourIndex 0x54
(**NOTE**: Before the Sirens of War update, these function pointers were at 0x14, 0x3C and 0x50 respectively)
Getting the item name
First things first, we want to find out what the item is actually named.
So, we now know the address of the function call for getItemName() is:
Code:
[[[itemBase]+0]+0x14]
Yay! Problem solved!
... Not quite.
We need to figure out how we actually need to call this function, i.e., what parameters it requires and how the return value is formed.
How to actually figure out how to inject a function is beyond the scope of this tutorial (read: I can't be arsed to explain) so I will just show you.
If you want to really understand what's going on, you're going to have to examine the actual functions within the PWI client using a debugger and preferably IDA Pro.
Code:
if(getItemNameAddr != 0)
{
_asm
{
MOV ECX, newMsg.chatItem
MOV EDX, getItemNameAddr
CALL EDX
MOV [itemName], EAX
}
msg.itemName = itemName;
}
This code is taken from my injected DLL so you'll have to figure out how to interpret this into your own program.
Basically:
1 - The function requires that you pass it the base address of the item object in ECX.
[[chatObject]+0xC]
2 - Load the function call address into EDX
[[[[chatObject]+0xC]+0]+0x14]
3 - Call EDX
4 - The pointer to the item name string will be loaded into EAX when the function exits. So you will need to move that somewhere convenient for you to read it.
Getting the item description
This is actually the same method you would use to get the description of an item in your inventory.
So, we now know the address of the function call for getItemDescription() is:
Code:
[[[itemBase]+0]+0x40]
To inject the function:
Code:
if(updateFuncAddr != 0)
{
_asm
{
PUSH 0
MOV EDI, newMsg.chatItem
MOV ECX,EDI
MOV EDX, updateFuncAddr
CALL EDX
}
msg.itemDesc = (*((newMsg).chatItem)).description;
}
Basically:
1 - Push 0 onto the stack - the function just requires this...
2 - The function requires that you pass it the base address of the item object in EDI.
3 - Then copy that address into ECX
4 - Load the function call address into EDX
[[[[chatObject]+0xC]+0]+0x40]
5 - Call EDX
6 - The item description will now be present in the object's item description offset, e.g.,
[[itemBase]+0x40]
7 - You can now read that however you want to
For the record: For anyone who's ever wondered how to read just the item name for an inventory item, rather than getting it from the description and then stripping out the first line (which isn't always the same as you see it in-game

) then here's your answer.
You can use this for any item located in any container in-game.
Ok, so we know the name of the item and the full description of it.
But, say you're displaying this in some nicely designed interface using all the proper in-game colours; we still don't know what colour the item is supposed to be displayed as (white, green, red, purple, blue).
Again, the colour of the item in the top line of the description isn't always the same as it is displayed in the chat window.
So, that's where the last function comes in.
Getting the item colour (as shown in chat)
This one's tricky.
We need to call a function as we did for the last two examples, but all that function does is return a colour index which is typically a value between 4 and 9, representing the colours you will see in-game (white, blue, green, purple, gold, red - perhaps not in that order lol).
Sure, we could just have a list of what each index value represents which colour, but that's not how we roll, right? What if they add more colours later on?
Yes... this sort of thing makes my OCD nerve twitch, so I actually went to the trouble of reverse engineering how the game actually figures this shit out.
There's a structure in-game that I have decided to name:
stringConstStructArray
It's an array of structures that contains string constants!
The base of this structure is at:
[[baseCall]+0x90]
It actually contains tons of pointers to constant strings used all throughout the game, including colour strings.
They're slightly drawn out pointers, but it's basically like this...
Code:
[[[[[baseCall]+0x90+0x8]+i*4]+0]+0]
Note the 0x90+0x8 - I wrote it like that because that's how the game actually resolves it. But you can just resolve it as:
Code:
[[[[[baseCall]+0x98]+i*4]+0]+0]
Note the 'i' in that chain. We'll come back to that later.
Check out this little AutoShit script. It will list all the strings that are present in this array.
Note that these offsets have been consistent through several large updates.
Code:
#include <WinAPI.au3>
#include <NomadMemory.au3>
#include <Array.au3>
Global $kernel32 = DllOpen('kernel32.dll')
Global $pid = ProcessExists('elementclient.exe')
Global $ph = _MemoryOpen($pid)
;Global $baseCall = 0xA521C0
Global $baseCall = 0xB8FBCC
#cs
Colour strings base = [[baseCall] + 0x90] + 0x8
004CC9F8 |> \8B0D C021A500 MOV ECX,DWORD PTR DS:[elementclient.0A521C0]
.
004CCA0C |. 81C1 90000000 ADD ECX,90
004CCA12 |. E8 39C21500 CALL elementclient.00628C50 ; \elementclient.00628C50, get link item colour
00628C5E |. 8B49 08 MOV ECX,DWORD PTR DS:[ECX+8]
00628C61 |. 8B1491 MOV EDX,DWORD PTR DS:[EDX*4+ECX]
#ce
Global $strBase = _MemoryRead($baseCall, $ph) + 0x90
ConsoleWrite("Colour string static base: " & Hex($strBase + 0x8) & @CRLF)
Global $strBaseAddr = _MemoryRead($strBase + 0x8, $ph)
ConsoleWrite("Const string base address: " & Hex($strBaseAddr) & @CRLF & @CRLF)
Global $pppString, $ppString, $pString, $str
$pppString = _MemoryRead($strBaseAddr + 0x14, $ph)
Do
$ppString = _MemoryRead($pppString + 0x4, $ph)
$pString = _MemoryRead($ppString, $ph)
$str = _MemoryRead($pString, $ph, 'wchar[100]')
ConsoleWrite(Hex($pppString) & ' ' & Hex($ppString) & ' ' & Hex($pString) & ' ' & $str & @CRLF);
$pppString = $pppString + 0x30
Until $pString = 0
; Clean up
_MemoryClose($ph)
DllClose($kernel32)
And here's the list of strings it will find:
Code:
Colour string static base: 00B90318
Const string base address: 0118D460
08B1BD60 08B1BD48 08F9192C ^8080ff
08B1BD90 08B1BD78 08F919C4 ^ffdc50
08B1BDC0 08B1BDA8 08F91A5C ^aa32ff
08B1BDF0 08B1BDD8 08F91AF4 ^ff6000
08B1BE20 08B1BE08 08F91B8C ^ffffff
08B1BE50 08B1BE38 08F91C24 ^b0b0b0
08B1BE80 08B1BE68 08F91CBC ^0000ff
08B1BEB0 08B1BE98 08F91D54 ^6cfb4b
08B1BEE0 08B1BEC8 08F91DEC ^ff0000
08B1BF10 08B1BEF8 08F91E84 ^80ffff
08B1BF40 08B1BF28 08F91F1C %s (%d socket(s))
08B1BF70 08B1BF58 08F91FB4 %s
08B1BFA0 08B1BF88 08F9204C Attack Rate(Atks/sec)
08B1BFD0 08B1BFB8 08F920E4 Physical Attack
08B1C000 08B1BFE8 08F9217C Magic Attack
08B1C030 08B1C018 08F92214 Durability
08B1C060 08B1C048 08F922AC Requisite Lv. %d
08B1C090 08B1C078 08F92344 Requisite Strength %d
08B1C0C0 08B1C0A8 08F923DC Requisite Dexterity %d
08B1C0F0 08B1C0D8 08F92474 Price
08B1C120 08B1C108 08F9250C Phys. Res.:
08B1C150 08B1C138 08F925A4 Metal Resistance
08B1C180 08B1C168 08F9263C Wood Resistance
08B1C1B0 08B1C198 08F926D4 Water Resistance
08B1C1E0 08B1C1C8 08F9276C Fire Resistance
08B1C210 08B1C1F8 08F92804 Earth Resistance
08B1C240 08B1C228 08F9289C %s (%d)
08B1C270 08B1C258 08F92934 Duration %d/%d seconds
08B1C2A0 08B1C288 08F929CC %s
08B1C2D0 08B1C2B8 08F92A64 Physical Attack
08B1C300 08B1C2E8 08F92AFC Magic Attack
08B1C330 08B1C318 08F92B94 Evasion
08B1C360 08B1C348 08F92C2C HP:
08B1C390 08B1C378 08F92CC4 MP
08B1C3C0 08B1C3A8 08F92D5C Unable to be traded
08B1C3F0 08B1C3D8 08F92DF4 Requisite Weapons Lv.%d-%d %s
08B1C420 08B1C408 08F92E8C Quest Items
08B1C450 08B1C438 08F92F24 Range %.2f
08B1C480 08B1C468 08F92FBC Physical Defense %+d%%
08B1C4B0 08B1C498 08F93054 Speed %+d%%
08B1C4E0 08B1C4C8 08F930EC Evasion %+d%%
08B1C510 08B1C4F8 08F93184 Reduce Physical damage taken %+d%%
08B1C540 08B1C528 08F9321C HP Recovery %+d
08B1C570 08B1C558 08F932B4 MP Recovery %+d
08B1C5A0 08B1C588 08F9334C Interval Between Hits %.2f seconds
08B1C5D0 08B1C5B8 08F933E4 Channelling %+d%%
08B1C600 08B1C5E8 08F9347C Critical Hit Rate %+d%%
08B1C630 08B1C618 08F93514 Ignition %d seconds
08B1C660 08B1C648 08F935AC Pet Lv. %d
08B1C690 08B1C678 08F93644 Accuracy %+d%%
08B1C6C0 08B1C6A8 08F936DC EXP %+d%%
08B1C6F0 08B1C6D8 08F93774 Unidentified
08B1C720 08B1C708 08F9380C Physical Attack %+d
08B1C750 08B1C738 08F938A4 Magic Attack %+d
08B1C780 08B1C768 08F9393C have a chance to cast %s(Lv.%d)
08B1C7B0 08B1C798 08F939D4 Maximum Physical Attack %+d
08B1C7E0 08B1C7C8 08F93A6C Maximum Magic Attack %+d
08B1C810 08B1C7F8 08F93B04 Maximum Endurance %+d%%
08B1C840 08B1C828 08F93B9C Requirement %+d%%
08B1C870 08B1C858 08F93C34 Mag. Res.:
08B1C8A0 08B1C888 08F93CCC +Metal Resistance %d%%
08B1C8D0 08B1C8B8 08F93FAC +Wood Resistance %d%%
08B1C900 08B1C8E8 08F94044 +Water Resistance %d%%
08B1C930 08B1C918 08F940DC +Fire Resistance %d%%
08B1C960 08B1C948 08F94174 +Earth Resistance %d%%
08B1C990 08B1C978 08F9420C Maximum HP %+d%%
08B1C9C0 08B1C9A8 08F942A4 Maximum MP %+d%%
08B1C9F0 08B1C9D8 08F9433C Range %+.2f
08B1CA20 08B1CA08 08F943D4 Type %s
08B1CA50 08B1CA38 08F9446C Accuracy %+d
08B1CA80 08B1CA68 08F94504 %s %s
08B1CAB0 08B1CA98 08F9459C Over the next %d seconds, %d HP will be restored
08B1CAE0 08B1CAC8 08F94634 Over the next %d seconds, %d MP will be restored
08B1CB10 08B1CAF8 08F946CC Reduce 50%% Poison damage taken
08B1CB40 08B1CB28 08F94764 Detoxifcation
08B1CB70 08B1CB58 08F947FC Reduce Magic damage taken %+d%%
08B1CBA0 08B1CB88 08F94894 Effect:
08B1CBD0 08B1CBB8 08F9492C HP recovers %d
08B1CC00 08B1CBE8 08F949C4 MP recovers %d
08B1CC30 08B1CC18 08F94A5C Recovers %d HP and %d MP
08B1CC60 08B1CC48 08F94AF4 Lv. %d
08B1CC90 08B1CC78 08F94B8C Requirement:
08B1CCC0 08B1CCA8 08F94C24 Requisite Lv. below %d
08B1CCF0 08B1CCD8 08F94CBC Lv.%d-%d Weapon
08B1CD20 08B1CD08 08F94D54 A crystal can charge your aerocraft of %d secs
08B1CD50 08B1CD38 08F94DEC Normal Speed (meters/second) %+.2f
08B1CD80 08B1CD68 08F94E84 MP Cost %d/second
08B1CDB0 08B1CD98 08F94F1C Requisite Class
08B1CDE0 08B1CDC8 08F94FB4 Speed %+.2f meters/second
08B1CE10 08B1CDF8 08F9504C Gender Requirement
08B1CE40 08B1CE28 08F950E4 Wrong Item
08B1CE70 08B1CE58 08F9517C Color
08B1CEA0 08B1CE88 08F95214 ?
08B1CED0 08B1CEB8 08F952AC %s¦¦¦
08B1CF00 08B1CEE8 08F95344 Price
08B1CF30 08B1CF18 08F953DC Repair Fee %d
08B1CF60 08B1CF48 08F95474 Duration %d seconds
08B1CF90 08B1CF78 08F9550C Requisite Vitality %d
08B1CFC0 08B1CFA8 08F955A4 Requisite Magic %d
08B1CFF0 08B1CFD8 08F9563C Wrong Element(%d)
08B1D020 08B1D008 08F956D4 Strength %+d
08B1D050 08B1D038 08F9576C Dexterity %+d
08B1D080 08B1D068 08F95804 Vitality %+d
08B1D0B0 08B1D098 08F9589C Magic %+d
08B1D0E0 08B1D0C8 08F95934 Have a chance to cast %s
08B1D110 08B1D0F8 08F959CC Fast Speed (meters/second) %+.2f
08B1D140 08B1D128 08F95A64 Manufactured by %s
08B1D170 08B1D158 08F95AFC Min. Effective Range %.2f
08B1D1A0 08B1D188 08F95B94 Food Type
08B1D1D0 00000000 00000000
I know... pretty neat, right?
If you've dug around in PW code much, you should instantly recognise that those first 10 strings are colour constants.
Then, upon closer inspection, you'll notice that they're all strings associated with in-game items!
Right, so...
We know where the colour string constants are, so now all we need to do is inject the item's member function to retrieve which colour index is associated with that item.
Remember, the getItemColour function call is found at:
Code:
[[[itemBase]+0]+0x40]
To inject the function:
Code:
if(getColourIndexAddr != 0)
{
_asm
{
MOV ECX, newMsg.chatItem
OR EDI, 0xFFFFFFFF // not sure why...
MOV EDX, getColourIndexAddr
CALL EDX
MOV [itemColourIndex], EAX
}
if(itemColourIndex > 4 && itemColourIndex < 10)
msg.itemColour = baseCall->base->stringConstStructArray->ppStringConst[itemColourIndex]->pStringConst->stringConst;
}
So, remember the 'i' I mentioned in the string constants chain? That's the itemColourIndex that we retrieve from this function.
Basically:
1 - The function requires that you pass it the base address of the item object in ECX.
2 - Whatever's in EDI needs to be OR'ed with oxFFFFFFFF - I don't know why, just do it lol. Anything OR'ed with 0xFFFFFFFF = 0xFFFFFFFF, so we're basically just loading that into EDI
3 - Load the function call address into EDX
[[[[chatObject]+0xC]+0]+0x54]
5 - Call EDX
6 - The function will return with the colour index value in EAX, so move that to somewhere where you can use it.
7 - Use that value to retrieve the colour string constant from the string constants array.
So, there you have it. We now have all we need.
Now you can make some awesome looking thigs like this
Some other notes:
If you're using the PWI font for whatever it is you're making, the [ and ] characters that surround an item name in a chat message are unicode characters 0x3010 and 0x3011 respectively. You'll have to add those manually as they're not part of the item name.
As I may have mentioned earlier, I do all of this stuff within an injected DLL because stuff can change while you're in the middle of reading things, which is obviously a pain in the arse.
You don't necessarily need to use an injected DLL, but I would at least recommend that you use a function hook and do all the magic from within there.
When a message structure is built by the client, it is built in a temporary holding structure which is then transferred to the main chat window.
I place a hook at the end of this function and then do all my function calls from there. This guarantees that you're getting the information at the earliest possible time that you can and it ensures that everything else is put on hold until you have done so.
It's pretty simple to find the place that I insert my hook.
First of all, you'll need your lastChatIndex address (0xB94C7C in PWI Sirens of War v677)
(I prefer to do this in OllyDbg as I like this program a lot!)
So first things first, we need to get the client up and running and attach OllyDbg.
Then, in the view memory window, you need to locate your chatBase (click in window, ctrl+G then type the address)
Then right click this address and select Breakpoint -> Memory
When the "Set memory breakpoint" window pops up, uncheck the "Read axxess" box and ensure the "Write access" box is checked.
Now wait for a new message to arrive and the breakpoint should trigger.
Basically, the chat structure is created in a temporary structure, then copied into the main chat window, then the lastChatIndex is updated.
So, if we look a few lines above where the memory breakpoint was hit (modified lastChatIndex) we can see a function call. So, lets put a breakpoint on that (select that line then hit F2).
Let the client run until a new message arrives and we should hit this breakpoint. You might want to disable the memory breakpoint now by the way (right click the memory address and go Breakpoint -> Memory delete)
Once we hit the new breakpoint on the function call, hit F7 to step into it.
You should now see a function something like this: (in older versions this function is a bit bigger, but the bottom parts looks a little similar).
Ok, when we actually hook our own function call, we will be overwriting some code with a relative call, of which the opcode is 5 bytes.
Code:
E8 XXXXXXXX where XXXXXXXX is the relative call offset
So, look down near the end of the function (after we know everything has been figured out by the client) and we can see there's a 3-byte opcode followed by a 2-byte opcode. Ideal, we can overwrite both of those with our 5 byte relative call.
I'm not going to go into detail about how to hook stuff and how to calculate the relative address of the function call - there are plenty of tutorials all over the internet for that.
Once you've worked out how to redirect the client to our hook function, you can then call all three functions mentioned earlier to gather all the information about the linked item.
When that's done, you need to restore all the registers (POPAD) and then copy the two instructions we overwrote for the hook and then return.
Here's what my hook function looks like. Bear in mind that this is for handling a complete new message arrival, so there's some code you might not need.
Code:
__declspec( naked ) DWORD hHandleNewMsg( )
{
_asm
{
PUSHAD
MOV newMsgOffset, EDI
}
newMsg = *(__chatStruct*)newMsgOffset;
msg.client = baseCall->base->structures->player->name;
msg.msgType = newMsg.msgScope;
msg.itemID = (DWORD)newMsg.chatItem;
msg.itemName = L"";
msg.itemColour = L"";
msg.itemDesc = L"";
msg.msg = newMsg.msg;
if(newMsg.chatItem != 0)
{
updateFuncAddr = (DWORD)(*((*(newMsg.chatItem)).itemFuncs)).updateDescription;
getItemNameAddr = (DWORD)(*((*(newMsg.chatItem)).itemFuncs)).getItemName;
getColourIndexAddr = (DWORD)(*((*(newMsg.chatItem)).itemFuncs)).getColourIndex;
if(updateFuncAddr != 0)
{
_asm
{
PUSH 0
MOV EDI, newMsg.chatItem
MOV ECX,EDI
MOV EDX, updateFuncAddr
CALL EDX
}
msg.itemDesc = (*((newMsg).chatItem)).description;
}
if(getColourIndexAddr != 0)
{
_asm
{
MOV ECX, newMsg.chatItem
OR EDI, 0xFFFFFFFF // not sure why...
MOV EDX, getColourIndexAddr
CALL EDX
MOV [itemColourIndex], EAX
}
if(itemColourIndex > 4 && itemColourIndex < 10)
msg.itemColour = baseCall->base->stringConstStructArray->pppStringConst[itemColourIndex]->ppStringConst->stringConst;
}
if(getItemNameAddr != 0)
{
_asm
{
MOV ECX, newMsg.chatItem
MOV EDX, getItemNameAddr
CALL EDX
MOV [itemName], EAX
}
msg.itemName = itemName;
}
}
msg.serialise();
/*
When sending a PM, a dummy second message triggers with the contents of the chat
entry bar which is an incomplete message and breaks shit.
Only send the message throuh the pipe if it begins with ^, not /
*/
temp = msg.msg[0];
if(temp == L"^")
pipeSendMsg(hPipe, msg.serialised.c_str());
_asm
{
// reinstate registers
POPAD
// replace code overwritten in hook
MOV DWORD PTR DS:[ESI+18],ECX
MOV EAX,ESI
// exit hook
RETN
}
}
So there you go... I've had enough of writing this tutorial now lol.
There should be enough info here for anyone to do what we set out to do.
Obviously you will need to do a lot of work yourself though
Well, I've done the hard work, so you can do some work to get it all to make sense.
Afterall, you might not learn much if I just gave the complete solution
Have fun!