EDIT: Should now work on PWI, PW Russia and Indo
Well, here it is lol... I'm too tired to do the writeup right now, but I'll try to update this post tomorrow with ins and outs of how it was all done ^_^
I figure some people will probably want it straight away due to the crazy red spam issue on PWI since the last update, hence I'm posting the code now.
A quick overview:
(By the way, that red text in the screenshot showing the last login IP seems to not come under any of the normal filter categories)
As a side note, I have a feeling (not confirmed) that the last filter option (the third 'other' option) might be a Horn filter hehe.
[Only registered and activated users can see links. Click Here To Register...]
EDIT:
Ok, finally got some time to explain the process of working this program out :)
I don't claim to be a great hacker or anything, but I like to explain how I do things and give an insight into my thought process when doing this sort of thing, in the hope that it might teach people something they might not have known before, or to think about things in a slightly different way in their approach to reverse engineering.
I'm sure most people will probably just scroll down and get the files and use them, but for those interested, here goes :P
So, the first thing we need to do is find out what changes when we alter the filters using the in-game settings -> game tab interface, so for this, we fire up cheat engine and attach it to the elementclient.exe process.
Now, there are 6 chat regions (common, squad, faction, etc) and in each region, there are 11 filter options (normal, world, faction, squad, etc). So intuition might tell us that there is an array of 66 values somewhere which store these filter settings. Being that there are so many, intuition might also tell us that they could be stored as bytes for efficiency (well, booleans, i.e., 1 or 0) and that a 1 would mean the filter is enabled and a 0 would mean it's disabled.
So, once CE is attached and we have the game settings tab open, make sure the 'Normal' filter is enabled in the 'Common' chat region and do a memory search in CE for a byte with a value of 1 (see pic 1).
[Only registered and activated users can see links. Click Here To Register...]
The search will find several million results, although had we started by searching for a 0 there would have been even more... hence we search for non-zero values first.
Now, disable the 'Normal' filter and change the value in the CE search to 0 then hit next scan. Now we just have a few thousand results. Leave the search value as 0 then hit 'next scan' a few more times - The number of results will pretty much be halved.
Now, re-enable the filter and search for 1 again. Rinse and repeat this process until we only have a few results. I managed to get down to 2 results which both clearly changed each time I clicked on the filter checkbox.
Take the lowest address and right click -> browse memory region.
Now we can modify a few different filters in-game and see some bytes changing near the address we found.
So, we know we've found a memory region which reflects the chat filters, however you may notice that they do not appear to be in the same order that they appear in-game.
[Only registered and activated users can see links. Click Here To Register...]
All is not lost... Those of you who read my [Only registered and activated users can see links. Click Here To Register...] might notice that they do match the order described there...
This also indicates that there are not 11 filters per region as previously expected, but 13 - The final two are hidden. Merkada has confirmed that the 13th filter value is actually for horns... Not so sure about the 12th.
So, we actually have 13 * 6 = 78 filters and the offsets from here for each chat region are:
So, now we know we've found something associated with the chat filters, lets try something...
In the 'browse memory region' window, adjust one of the values for which we know which filter it applies to, i.e., the common -> normal filter. Change the value in CE and you will notice it doesn't change in game. However, if you change the value, then click on a different chat region (i.e., squad) then click back on 'Common' you'll see it has changed! If we adjust the next byte after the 'Normal' chat filter (world chat) then switch regions, then switch back to common, we'll see that the world chat filter is unchecked. You can now click apply, and any world chat messages will be removed from the in-game chat window. Yay! Seems we're almost there huh? Well, not quite.
Adjust the world chat filter to 0 (off), switch regions, switch back, click apply... World chat disappears. Awesome... Now click confirm, or close the game settings interface.
Now open the game settings interface again. Bugger... World chat is enabled again. All the previous world chat messages haven't been restored in the chat window though, but any subsequent messages will be displayed.
So, we now know that there must be another storage area for these filters.
There are a couple of ways we could find this... The first way is to do a search for an array of bytes that matches the array we've already found (do this after applying any filters you've adjusted).
Alternatively (and in my opinion, preferrably) we can find out what writes to this address.
For this, I will use OllyDbg simply because the disassembly is more readable and I can copy and paste code from it more easily :P
So, fire up OllyDbg and attach it to the PW client (you will need to detach CE from the process if you have previously attached it). Once Olly has attached, press F9 to unpause the client.
In the "Executable modules" list (Alt+E) double click "ElementClient" twice to take us to the elementclient module.
Now click in the memory dump area then hit Ctrl+G (Goto address) and type in the address of the memory location we found in CE which stores the filter byte for Common->Normal.
Now right click on this address and set a memory breakpoint on write access (see pic_3 - Depending on the version of Olly you have, this interface might look slightly different)
[Only registered and activated users can see links. Click Here To Register...]
Now open the game settings interface and we should get a hit at:
If we examine the code around here, we can see this:
If we now remove our memory write breakpoint and run the client again, then set a normal breakpoint at the line:
Then open the game settings interface again, we'll see that EAX contains the value of our baseCall static offset ( &baseCall = 0xAFF144, *baseCall = AFF7E8).
Just for your information, the code above is basically like a block memory copy, which copies our non-static filter values to an area of memory that actually contains the static game settings.
So, from these few lines of code, we can get our complete offset list for the static filters offset, which is:
Add this to your CE offset list and set the type to 'Array of bytes' and set the size to 78 bytes (pic 4)
[Only registered and activated users can see links. Click Here To Register...]
You can also add the address we found earlier for the non-static filters in the same format, i.e., array of 78 bytes so we can compare side by side (pic 5)
[Only registered and activated users can see links. Click Here To Register...]
But wait a minute... They don't seem to quite correlate. The static ones seem to be offset by 4 bytes, so what are these first 4 bytes? Well if we examine the 4 bytes of memory just before our non-static filters, and then change the 'Basic' settings in the game tab, then hit apply, we'll see that these bytes contain the boolean values for those four checkboxes (Zoom, A,D Keys, Squad Invites, Trade).
We can also find some of our other settings around this area, such as the autoreply status and message, which is somewhere just after our filters.
So to get to our chat filters offset, add 4 to the final offset we had before, i.e.,
Now our two arrays of bytes in CE should be aligned (after setting filters and clicking apply).
We've now established where our filters are statically stored, where they are temporarily stored for the gui, and that the normally 'disabled' filters (world chat, whispers, etc) somehow get adjusted back to default when we reopen the game settings tab after successfully disabling them. So, let's find out what evil witchery keeps forcing us to endure that annoying red and yellow spam...
We know that world chat is one of the filters that gets forced back on, so lets set a memory write breakpoint on this in OllyDbg. I'll save us some time here by mentioning that the static filter isn't specifically modified, but the non static one is - The non-static filters are then bulk copied to the static offsets.
So, the address is our non-static filter base + 1. If you've lost the address of your non-static filters, I'll save some more time here and tell you it's at [[[[[baseCall]+0x18]+0x8]+0x4B0]+0x218] so put the breakpoint one byte above this address (for world chat filter) then open the game settings tab. It will break first where it did last time, so when it hits, hit F9 again to run to the next BP... Which hits a few lines down, here:
When you open the game settings tab, what actually happens is something like this:
Static filter settings are copied to the non-static ones (the gui ones)
Some of the non-static ones get forced to different values (i.e., world / system chat)
Non-static filters are copied back to the static ones and the gui is displayed.
So, basically, there is no way we can just modify the filters and have them 'stick' the next time the game settings interface is opened. Bummer, eh? Well this just means we need to do a little patch.
Going back to the code we found a minute ago which modified the world chat filter, below that we can see that a few others get modified too.
Yeah... the 'General' chat region sounds like heaven. Unfortunately we're not given it as an option in the actual game chat regions lol.
Now, you might notice that those offsets don't cover any of the normal annoying settings, i.e., red system spam in all the other channels. If we scroll up a little bit in OllyDbg, we can see the culprit for this:
This loops through each chat region (hence the ADD EAX, 0D) and sets a few filters. Note that earlier I gave the offset for the non-static filters as [[[[[baseCall]+0x18]+0x8]+0x4B0]+0x218], but a few lines above this loop we can see the line:
So we start at the 5th filter on the Common chat region, which is 'whisper', so this loop above forces some filters on as follows:
The only way we can stop these filters being reset every time we open the game settings interface is to apply a patch. In my program, I do the patch on the already opened client, i.e., in the client's program memory rather than patching the actual .exe file... 'cause that would be naughty :P
In my code, I read the client's memory into a big string, then do a string search for this code:
And then overwrite it with this:
If you run my code (at the end of this guide) and attach to your client, then hit the patch button, you'll see that any changes you make now do not get changed back when you open the game settings tab. Yay!
So it would seem we're almost done, however, just updating the filter values in memory doesn't apply the filters to the contents of the in game chat box, i.e., say for example you filtered out world chat, the yellow text won't disappear from the current chat box although any subsequent world chat type messages will not be shown. The only way to clear previous messages in the chat box is to hit the apply button in the game filters interface. So, we need to do this automatically with some magic ^_^
I had some trouble finding the specific function in the client which actually propagates the changes to the chat box, so I took a slightly different approach and did a few injections into the client's function that handles all the button clicks within the gui menu system.
Because of the thing I mentioned earlier, whereby the filters are read from static memory into the gui memory, then back to static memory, the solution I came up with was to do the following:
Close the game settings interface if it's already open
Write your preferred filter settings to the static filter array
Call the gui control function to open the game settings interface (this syncs the static filters to the non-static ones, which are used when changes are applied)
Call the gui control function again to simulate hitting the "apply" button
Close the game settings interface
This actually often all happens so quickly that you won't even see the window open and close :P
Finding the gui control function has actually been described before in toxic6666's awesome thread on [Only registered and activated users can see links. Click Here To Register...] - If you're new to fiddling with PW then I highly recommend reading this thread.
However, for continuity, I'll describe here how to actually find this function (termed 'guiCommand()' by toxic6666)
(also updated)
toxic6666's method involved using w32dasm to find a string constant, however, this program is actually pretty difficult to find these days and most downloads you might find for it tend to have infections and/or be from 'questionable' sites. Fortunately, we don't need w32dasm for this as we can do it in OllyDbg.
From within OllyDgb, right click somewhere within the code window, i.e., actually click on some line of code then choose 'Search for' -> 'All referenced strings'
This should pop up a window with a list of all the string constants used in the client. Right click in this window and choose 'Search for text' (or if you're using the newer version of Olly, just hit Ctrl+F)
Search for "Dlg_Building" - there should only be one occurrence of it. When you find it, double click this line and it should take you to the code. At this point, you could open up IDA, enter the address that you just landed at in Olly then find the start of the function. However, the code here isn't too messy so you can just scroll up a few pages until you find a bunch of NOPS. The start of the function is the first line of code after these NOPS. In the current PWI version, this is at 0x604B30 and looks like this:
The 2nd and 4th instructions here will load the gui command and the gui object pointer respectively. So, set a breakpoint at the PUSH EDI line (5th line shown above) then click the apply button in the game settings window.
When the BP hits, we'll see a value in ESI which will be the gui object and a string reference in EBX which will be the command. Fortunately, the commands used for the gui are quite reader friendly lol... The command for the apply button is simply a text string "apply".
If you do this a few times for some other things, i.e., click the game settings tab or click the close (X) button you'll get the command strings for these too. These are "gamesetting" and "Btn_Close" respectively.
Make a note of the value in ESI when you get into this function after hitting the apply button from within the game settings tab.
I won't go into detail about finding the guiBase pointers here as I think toxic6666 explained it all pretty well in his thread. He also explains how to get the offsets for the gui object for this function too, so I'll skip that part and just state that the offset we need is:
This is the gamesettings tab object and thankfully, all the commands we need to send are sent to this object.
Basically, this is the pseudo code for what we want to do now...
Assuming the guiCommand() function prototype is something like this:
We would do...
As for how to actually inject into this function, just look at the guiCommand() function in my code - I think this explains it :)
I think I have explained all the relevant parts of the code... The other bits are mostly just fancy trimmings hehe. As for the offset finding stuff, that's kind of beyond the scope of this guide, but I'll be posting a handy tool I made to aid this very time consuming process... maybe tonight.
That's all folks, hope you're not all bored to sleep now!
So, here's the code - You need all three files plus NomadMemory (you should all have this)
PW_ChatFilter.au3 (thanks to t212 for multiclient fix)
filterOffsets.au3
MultiAssocArray.au3 (This is awesome! Found it [Only registered and activated users can see links. Click Here To Register...])
Enjoy!!
Well, here it is lol... I'm too tired to do the writeup right now, but I'll try to update this post tomorrow with ins and outs of how it was all done ^_^
I figure some people will probably want it straight away due to the crazy red spam issue on PWI since the last update, hence I'm posting the code now.
A quick overview:
- Can choose which client to attach to
- Patches the client so that the filter overrides don't reset your filter settings when you open the game settings console
- Can turn any chat type on or off on any chat region tab, i.e., in the image below you can see that world chat and system chat are disabled in the common chat region
- Automatically finds all offsets required, so might be update proof :P
- Might work on other versions of PW - I'm not sure yet and this will require testing. Please give feedback if it works on your version
- Run your PWI client as normal and log in (must be logged in for the clients to be detected)
- Run the PW_ChatFilter.au3 file from AutoIt, or alternatively, compile it first
- (I didn't upload it as an exe because it uses some injections and therefore might throw some false positives with some antivirus programs... I can't be arsed to deal with people QQing that it has some virus, hence the code is here for all to see)
- When you run the program, it should populate the dropdown list at the top with the character names of all your opened clients
- Choose the one you want to fiddle with and hit 'Attach'
- Now hit the 'Patch' button. It won't work without this (I'll probably make this automatic when attaching at a later point... can't remember why I did it like this now lol)
- After a couple of seconds, it should hopefully tell you that it's successfully attached.
- Now, set whatever filters you want on or off and hit the 'Apply' button
- Yay! No more red spam!
(By the way, that red text in the screenshot showing the last login IP seems to not come under any of the normal filter categories)
As a side note, I have a feeling (not confirmed) that the last filter option (the third 'other' option) might be a Horn filter hehe.
[Only registered and activated users can see links. Click Here To Register...]
EDIT:
Ok, finally got some time to explain the process of working this program out :)
I don't claim to be a great hacker or anything, but I like to explain how I do things and give an insight into my thought process when doing this sort of thing, in the hope that it might teach people something they might not have known before, or to think about things in a slightly different way in their approach to reverse engineering.
I'm sure most people will probably just scroll down and get the files and use them, but for those interested, here goes :P
So, the first thing we need to do is find out what changes when we alter the filters using the in-game settings -> game tab interface, so for this, we fire up cheat engine and attach it to the elementclient.exe process.
Now, there are 6 chat regions (common, squad, faction, etc) and in each region, there are 11 filter options (normal, world, faction, squad, etc). So intuition might tell us that there is an array of 66 values somewhere which store these filter settings. Being that there are so many, intuition might also tell us that they could be stored as bytes for efficiency (well, booleans, i.e., 1 or 0) and that a 1 would mean the filter is enabled and a 0 would mean it's disabled.
So, once CE is attached and we have the game settings tab open, make sure the 'Normal' filter is enabled in the 'Common' chat region and do a memory search in CE for a byte with a value of 1 (see pic 1).
[Only registered and activated users can see links. Click Here To Register...]
The search will find several million results, although had we started by searching for a 0 there would have been even more... hence we search for non-zero values first.
Now, disable the 'Normal' filter and change the value in the CE search to 0 then hit next scan. Now we just have a few thousand results. Leave the search value as 0 then hit 'next scan' a few more times - The number of results will pretty much be halved.
Now, re-enable the filter and search for 1 again. Rinse and repeat this process until we only have a few results. I managed to get down to 2 results which both clearly changed each time I clicked on the filter checkbox.
Take the lowest address and right click -> browse memory region.
Now we can modify a few different filters in-game and see some bytes changing near the address we found.
So, we know we've found a memory region which reflects the chat filters, however you may notice that they do not appear to be in the same order that they appear in-game.
[Only registered and activated users can see links. Click Here To Register...]
All is not lost... Those of you who read my [Only registered and activated users can see links. Click Here To Register...] might notice that they do match the order described there...
Code:
messageTypes (see chatObj->msgScope)
Local // 0
World // 1
Squad // 2
Faction // 3
Whisper // 4
5 // 5
6 // 6
Trade // 7
Notification // 8
System // 9
Gen. Info // 0xa
Local info(b) // 0xb
Local info(c) // 0xc
So, we actually have 13 * 6 = 78 filters and the offsets from here for each chat region are:
Code:
Common -> Normal = 0 Squad -> Normal = 0xD Faction -> Normal = 0x1A Whisper -> Normal = 0x27 Trade -> Normal = 0x34 General -> Normal = 0x41
In the 'browse memory region' window, adjust one of the values for which we know which filter it applies to, i.e., the common -> normal filter. Change the value in CE and you will notice it doesn't change in game. However, if you change the value, then click on a different chat region (i.e., squad) then click back on 'Common' you'll see it has changed! If we adjust the next byte after the 'Normal' chat filter (world chat) then switch regions, then switch back to common, we'll see that the world chat filter is unchecked. You can now click apply, and any world chat messages will be removed from the in-game chat window. Yay! Seems we're almost there huh? Well, not quite.
Adjust the world chat filter to 0 (off), switch regions, switch back, click apply... World chat disappears. Awesome... Now click confirm, or close the game settings interface.
Now open the game settings interface again. Bugger... World chat is enabled again. All the previous world chat messages haven't been restored in the chat window though, but any subsequent messages will be displayed.
So, we now know that there must be another storage area for these filters.
There are a couple of ways we could find this... The first way is to do a search for an array of bytes that matches the array we've already found (do this after applying any filters you've adjusted).
Alternatively (and in my opinion, preferrably) we can find out what writes to this address.
For this, I will use OllyDbg simply because the disassembly is more readable and I can copy and paste code from it more easily :P
So, fire up OllyDbg and attach it to the PW client (you will need to detach CE from the process if you have previously attached it). Once Olly has attached, press F9 to unpause the client.
In the "Executable modules" list (Alt+E) double click "ElementClient" twice to take us to the elementclient module.
Now click in the memory dump area then hit Ctrl+G (Goto address) and type in the address of the memory location we found in CE which stores the filter byte for Common->Normal.
Now right click on this address and set a memory breakpoint on write access (see pic_3 - Depending on the version of Olly you have, this interface might look slightly different)
[Only registered and activated users can see links. Click Here To Register...]
Now open the game settings interface and we should get a hit at:
Code:
00569167 - 8D 85 1C020000 - lea eax,[ebp+0000021C]
Code:
00569151 |. 8B40 18 MOV EAX,DWORD PTR DS:[EAX+18] ; settings base address 00569154 |. 8DBD 14020000 LEA EDI,[EBP+214] 0056915A |. B9 3A000000 MOV ECX,3A ; Number of integers to copy 0056915F |. B2 01 MOV DL,1 00569161 |. 8DB0 55010000 LEA ESI,[EAX+155] ; ** static filters offset ** 00569167 |. 8D85 1C020000 LEA EAX,[EBP+21C] ; points to the filters byte array we found 0056916D |. F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS
Code:
00569151 |. 8B40 18 MOV EAX,DWORD PTR DS:[EAX+18] ; settings base address
Just for your information, the code above is basically like a block memory copy, which copies our non-static filter values to an area of memory that actually contains the static game settings.
So, from these few lines of code, we can get our complete offset list for the static filters offset, which is:
Code:
[[[baseCall]+0x18]+0x155]
[Only registered and activated users can see links. Click Here To Register...]
You can also add the address we found earlier for the non-static filters in the same format, i.e., array of 78 bytes so we can compare side by side (pic 5)
[Only registered and activated users can see links. Click Here To Register...]
But wait a minute... They don't seem to quite correlate. The static ones seem to be offset by 4 bytes, so what are these first 4 bytes? Well if we examine the 4 bytes of memory just before our non-static filters, and then change the 'Basic' settings in the game tab, then hit apply, we'll see that these bytes contain the boolean values for those four checkboxes (Zoom, A,D Keys, Squad Invites, Trade).
We can also find some of our other settings around this area, such as the autoreply status and message, which is somewhere just after our filters.
So to get to our chat filters offset, add 4 to the final offset we had before, i.e.,
Code:
[[[baseCall]+0x18]+0x159]
We've now established where our filters are statically stored, where they are temporarily stored for the gui, and that the normally 'disabled' filters (world chat, whispers, etc) somehow get adjusted back to default when we reopen the game settings tab after successfully disabling them. So, let's find out what evil witchery keeps forcing us to endure that annoying red and yellow spam...
We know that world chat is one of the filters that gets forced back on, so lets set a memory write breakpoint on this in OllyDbg. I'll save us some time here by mentioning that the static filter isn't specifically modified, but the non static one is - The non-static filters are then bulk copied to the static offsets.
So, the address is our non-static filter base + 1. If you've lost the address of your non-static filters, I'll save some more time here and tell you it's at [[[[[baseCall]+0x18]+0x8]+0x4B0]+0x218] so put the breakpoint one byte above this address (for world chat filter) then open the game settings tab. It will break first where it did last time, so when it hits, hit F9 again to run to the next BP... Which hits a few lines down, here:
Code:
00569186 |. 8895 19020000 MOV BYTE PTR SS:[EBP+219],DL
Static filter settings are copied to the non-static ones (the gui ones)
Some of the non-static ones get forced to different values (i.e., world / system chat)
Non-static filters are copied back to the static ones and the gui is displayed.
So, basically, there is no way we can just modify the filters and have them 'stick' the next time the game settings interface is opened. Bummer, eh? Well this just means we need to do a little patch.
Going back to the code we found a minute ago which modified the world chat filter, below that we can see that a few others get modified too.
Code:
00569186 |. 8895 19020000 MOV BYTE PTR SS:[EBP+219],DL ; Common -> World On 0056918C |. 889D 5A020000 MOV BYTE PTR SS:[EBP+25A],BL ; General -> World off 00569192 |. 889D 62020000 MOV BYTE PTR SS:[EBP+262],BL ; General -> System off (red) 00569198 |. 889D 5D020000 MOV BYTE PTR SS:[EBP+25D],BL ; General -> Whisper off 0056919E |. 889D 65020000 MOV BYTE PTR SS:[EBP+265],BL ; General -> Horn? off
Now, you might notice that those offsets don't cover any of the normal annoying settings, i.e., red system spam in all the other channels. If we scroll up a little bit in OllyDbg, we can see the culprit for this:
Code:
00569174 |> 8850 05 /MOV BYTE PTR DS:[EAX+5],DL 00569177 |. 8810 |MOV BYTE PTR DS:[EAX],DL 00569179 |. 8850 08 |MOV BYTE PTR DS:[EAX+8],DL 0056917C |. 83C0 0D |ADD EAX,0D 0056917F |. 49 |DEC ECX 00569180 |.^ 75 F2 \JNE SHORT 00569174
Code:
00569167 |. 8D85 1C020000 LEA EAX,[EBP+21C]
Code:
00569174 |> 8850 05 /MOV BYTE PTR DS:[EAX+5],DL ; Red system 00569177 |. 8810 |MOV BYTE PTR DS:[EAX],DL ; Whisper 00569179 |. 8850 08 |MOV BYTE PTR DS:[EAX+8],DL ; Horn? (filter 13 or 0xC)
In my code, I read the client's memory into a big string, then do a string search for this code:
Code:
CPU Disasm Address Hex dump Command Comments 00569174 |> /8850 05 /MOV BYTE PTR DS:[EAX+5],DL 00569177 |. |8810 |MOV BYTE PTR DS:[EAX],DL 00569179 |. |8850 08 |MOV BYTE PTR DS:[EAX+8],DL 0056917C |. |83C0 0D |ADD EAX,0D 0056917F |. |49 |DEC ECX 00569180 |.^\75 F2 \JNE SHORT 00569174 00569182 |. 33DB XOR EBX,EBX 00569184 |. 8BCD MOV ECX,EBP 00569186 |. 8895 19020000 MOV BYTE PTR SS:[EBP+219],DL 0056918C |. 889D 5A020000 MOV BYTE PTR SS:[EBP+25A],BL 00569192 |. 889D 62020000 MOV BYTE PTR SS:[EBP+262],BL 00569198 |. 889D 5D020000 MOV BYTE PTR SS:[EBP+25D],BL 0056919E |. 889D 65020000 MOV BYTE PTR SS:[EBP+265],BL
Code:
CPU Disasm Address Hex dump Command Comments 00569174 |> /90 /NOP 00569175 |. |90 |NOP 00569176 |. |90 |NOP 00569177 |. |90 |NOP 00569178 |. |90 |NOP 00569179 |. |90 |NOP 0056917A |. |90 |NOP 0056917B |. |90 |NOP 0056917C |. |83C0 0D |ADD EAX,0D 0056917F |. |49 |DEC ECX 00569180 |.^\75 F2 \JNE SHORT 00569174 00569182 |. 33DB XOR EBX,EBX 00569184 |. 8BCD MOV ECX,EBP 00569186 |. 90 NOP 00569187 |. 90 NOP 00569188 |. 90 NOP 00569189 |. 90 NOP 0056918A |. 90 NOP 0056918B |. 90 NOP 0056918C |. 90 NOP 0056918D |. 90 NOP 0056918E |. 90 NOP 0056918F |. 90 NOP 00569190 |. 90 NOP 00569191 |. 90 NOP 00569192 |. 90 NOP 00569193 |. 90 NOP 00569194 |. 90 NOP 00569195 |. 90 NOP 00569196 |. 90 NOP 00569197 |. 90 NOP 00569198 |. 90 NOP 00569199 |. 90 NOP 0056919A |. 90 NOP 0056919B |. 90 NOP 0056919C |. 90 NOP 0056919D |. 90 NOP 0056919E |. 90 NOP 0056919F |. 90 NOP 005691A0 |. 90 NOP 005691A1 |. 90 NOP 005691A2 |. 90 NOP 005691A3 |. 90 NOP
So it would seem we're almost done, however, just updating the filter values in memory doesn't apply the filters to the contents of the in game chat box, i.e., say for example you filtered out world chat, the yellow text won't disappear from the current chat box although any subsequent world chat type messages will not be shown. The only way to clear previous messages in the chat box is to hit the apply button in the game filters interface. So, we need to do this automatically with some magic ^_^
I had some trouble finding the specific function in the client which actually propagates the changes to the chat box, so I took a slightly different approach and did a few injections into the client's function that handles all the button clicks within the gui menu system.
Because of the thing I mentioned earlier, whereby the filters are read from static memory into the gui memory, then back to static memory, the solution I came up with was to do the following:
Close the game settings interface if it's already open
Write your preferred filter settings to the static filter array
Call the gui control function to open the game settings interface (this syncs the static filters to the non-static ones, which are used when changes are applied)
Call the gui control function again to simulate hitting the "apply" button
Close the game settings interface
This actually often all happens so quickly that you won't even see the window open and close :P
Finding the gui control function has actually been described before in toxic6666's awesome thread on [Only registered and activated users can see links. Click Here To Register...] - If you're new to fiddling with PW then I highly recommend reading this thread.
However, for continuity, I'll describe here how to actually find this function (termed 'guiCommand()' by toxic6666)
(also updated)
toxic6666's method involved using w32dasm to find a string constant, however, this program is actually pretty difficult to find these days and most downloads you might find for it tend to have infections and/or be from 'questionable' sites. Fortunately, we don't need w32dasm for this as we can do it in OllyDbg.
From within OllyDgb, right click somewhere within the code window, i.e., actually click on some line of code then choose 'Search for' -> 'All referenced strings'
This should pop up a window with a list of all the string constants used in the client. Right click in this window and choose 'Search for text' (or if you're using the newer version of Olly, just hit Ctrl+F)
Search for "Dlg_Building" - there should only be one occurrence of it. When you find it, double click this line and it should take you to the code. At this point, you could open up IDA, enter the address that you just landed at in Olly then find the start of the function. However, the code here isn't too messy so you can just scroll up a few pages until you find a bunch of NOPS. The start of the function is the first line of code after these NOPS. In the current PWI version, this is at 0x604B30 and looks like this:
Code:
CPU Disasm Address Hex dump Command Comments 00604B30 /. 53 PUSH EBX 00604B31 |. 8B5C24 08 MOV EBX,DWORD PTR SS:[ARG.1] 00604B35 |. 56 PUSH ESI 00604B36 |. 8B7424 10 MOV ESI,DWORD PTR SS:[ARG.2] 00604B3A |. 57 PUSH EDI 00604B3B |. 56 PUSH ESI ; /Arg2 => [ARG.2] 00604B3C |. 53 PUSH EBX ; |Arg1 => [ARG.1] 00604B3D |. E8 EE221E00 CALL 007E6E30 ; \ElementClient.007E6E30 00604B42 |. 84C0 TEST AL,AL 00604B44 |. 74 24 JE SHORT 00604B6A
When the BP hits, we'll see a value in ESI which will be the gui object and a string reference in EBX which will be the command. Fortunately, the commands used for the gui are quite reader friendly lol... The command for the apply button is simply a text string "apply".
If you do this a few times for some other things, i.e., click the game settings tab or click the close (X) button you'll get the command strings for these too. These are "gamesetting" and "Btn_Close" respectively.
Make a note of the value in ESI when you get into this function after hitting the apply button from within the game settings tab.
I won't go into detail about finding the guiBase pointers here as I think toxic6666 explained it all pretty well in his thread. He also explains how to get the offsets for the gui object for this function too, so I'll skip that part and just state that the offset we need is:
Code:
[[[[[baseCall]+0x1C]+0x18]+0x8]+0x4B0]
Basically, this is the pseudo code for what we want to do now...
Assuming the guiCommand() function prototype is something like this:
Code:
void guiCommand(string * command, guiObjectPointer)
Code:
If gameSettingsTab is open
{
guiCommand("Btn_Close", gamesettingTabObject) // close the tab
Wait until it's closed
}
setChatFilters() // Write our preferred filters to the static filter array
guiCommand("gamesetting", gamesettingTabObject) // Open the tab again
guiCommand("apply", gamesettingTabObject) // Hit apply
guiCommand("Btn_Close", gamesettingTabObject) // close the tab again
I think I have explained all the relevant parts of the code... The other bits are mostly just fancy trimmings hehe. As for the offset finding stuff, that's kind of beyond the scope of this guide, but I'll be posting a handy tool I made to aid this very time consuming process... maybe tonight.
That's all folks, hope you're not all bored to sleep now!
So, here's the code - You need all three files plus NomadMemory (you should all have this)
PW_ChatFilter.au3 (thanks to t212 for multiclient fix)
filterOffsets.au3
MultiAssocArray.au3 (This is awesome! Found it [Only registered and activated users can see links. Click Here To Register...])
Enjoy!!