Register for your free account! | Forgot your password?

Go Back   elitepvpers > MMORPGs > Guild Wars > GW Bots
You last visited: Today at 05:38

  • Please register to post and access all features, it's quick, easy and FREE!

Advertisement


GWA2 Pattern Scanning

Discussion on GWA2 Pattern Scanning within the GW Bots forum part of the Guild Wars category.

Reply
 
Old   #1
 
elite*gold: 0
Join Date: Jul 2019
Posts: 103
Received Thanks: 78
GWA2 Pattern Scanning

Computer Science Concepts For Further Reading:
Endianess
Read/Write/Arithmetic for hexadecimal/Binary
Process Context
Thread Context
Windows Process Layout
User Mode vs Kernel Mode
C calling conventions
InterProcessCommunication
Windows Functions(VirtualAllocEx, ReadProcessMemory, WriteProcessMemory, CreateRemoteThread,LoadLibrary)

Tools:
Dynamic Analysis Tools: Cheat Engine, x32dbg, Immunity Debugger, ollydbg, ReClass,etc
Static Analysis Tools: Ghidra, IDA Pro, either one with a variation of binary diff plugins
My preferred Tools: Cheat Engine, ReClass, Immunity Debugger, and Ghidra.

What is GWA2?
It is a Application Programming Interface(API) for Guild Wars 1. What this really means is that it gives a way to interact with the Guild Wars clients through code and in this case autoit.

How does it do this magic?
GWA2 on the simplest level has a basic x86-asm compiler that converts mnemonics of assembly code written in autoit to functioning executable code. It then uses various windows functions to pattern scan for specific locations to place said payload for actions such as function hooks. Once desired locations are found for whatever features are needed the API uses windows messages and Read/WriteProcessMemory to act aka InterProcessCommunication. That is the high-level overview of how it allows you to write autoit but act within the game client.

So, what are we going to look at today?
Pattern Scanning seems to be the main topic people are posting repeatedly about.

What is pattern scanning from a bird’s eye view?
It is taking the process memory map pictured below and searching it byte by byte to find a pattern in that specific program. Generally, most patterns are in the .text section which is really the program instructions that are generated when you write code like If statements or loops.

Image Courtesy of

What do we do with a pattern once it is found?
Usually one or more offsets are added to it to get to the desired address in the game client. Once we are there we can do things like read the list of agents to find our own agent and then get hit points or the location for a function hook such as sendpacket so we can send network packets directly.

Pattern of interest today: ScanTraderFunction
The goal today is to show quick and dirty tricks for fixing common but simple pattern breaks.

Tips and Tricks:
Keep a folder of gw.exe from every patch, keep an associated listing of gwa2 patterns as they change along with offsets. In your static analysis tool of choice set bookmarks of pattern locations when they are known good to compare to an updated gw.exe.

Shorter patterns are generally better as there are less bytes to break but too short and you get too many false positives.

GWA2 Pattern Scanning Peculiarities:
The address returned by the pattern in most pattern scanners is at the very byte the pattern started. In GWA2 it is that location minus one. The scanner in GWA2 doesn’t support wild cards.

How does a pattern break?
One or more bytes change in the game executable after an update or the pattern now matches 2 or more results. The other possible situation is code outside the pattern changes slightly so that the offset must be adjusted even thought the pattern still works. Anticheat is another one but doesn’t apply for our current client.

Code:
Starting patterns:
_('ScanTraderFunction:')
    AddPattern('83FF10761468AC210000') ; Old version of scantrader

SetValue('TraderFunction', '0x' & Hex(GetScannedAddress('ScanTraderFunction', -0x1E), 8));offset
Dirty Trick 1:
Start chopping 1 byte off the end at a time searching until you get a few possible patterns.
Example:
Start: 83FF10761468AC210000
Chop 1: 83FF10761468AC2100 ; not found
Chop 2: 83FF10761468AC21 ; not found
Chop 3: 83FF10761468AC ; not found
Chop 4: 83FF10761468 found 2 patterns


Now lets look at the memory regions. I highlight in red in the lower window the interesting part.



Well look at that, looks identical to the old pattern but one byte changed. AC -> D2. We can confirm this by plugging it into gwa2 and running a test script or bot. We can also check it in Cheat Engine.



One result and I know for a fact that is a current working pattern. Something to note is in this case the start of our scan at 83 FF didn’t change so offset didn’t change. This could also be confirmed in your static and dynamic debuggers of choice outside cheat engine.

Dirty Trick #2:
Same exact technique but start chopping from the front starting with 83 instead of the end being 00.

Dirty Trick #3:
History repeats itself and thus lets us abuse wild cards in patterns that don’t change heavily.
Original Pattern: 83FF10761468AC210000
Fix 1: 83FF10761468AE210000
Fix 2: Fix 1: 83FF10761468D2210000
Notice how historically its just the one byte changing over and over? That does break our pattern since gwa2 doesn’t support wild cards but we can get a quick fix from cheat engine.
Boom got em:


Dirty Trick 4:
Binary Comparison using static analysis. This is my personal favorite to take one of the many saved gw.exe and do a diff in ghidra between the newest gw.exe. I am currently working on some old code to port so this isn’t the latest gw.exe but two older ones being compared to find the pattern update.
Notice nicely in green it showed us where the bytes changed. Specifically the update that went from:
83FF10761468AE210000 to 83FF10761468D2210000, image below:

Dirty Trick 5:
Assertion strings can be an easy way to find a pattern after a major break. So just do a string search for some of these in the picture: = "p:\\code\\gw\\char\\charmsg.cpp", = "giveItemCount <= CHAR_MAX_TRANSACT_ITEM_COUNT"

Parting Thoughts On Dirty Tricks:
Save old patterns and gw.exe to make finding them again simple.

How the hell did they find these originally?
Nearly an infinite number of ways but the most common techniques are mapping call trees based on assertion strings. My personal favorite is tracing back ws32_send until you get the pre encryption function for sendpacket. Then set a breakpoint on send packet and when the packet header matches the action of interest trace the call stack back through the functions and then map out the function for intended use. I could write books on various ways but those are the 2 most common I know of and that are openly used by people.

In summary:
ScanTraderFunction Currently equals 83FF10761468D2210000 or a variation of said pattern/offset. If fixing the pattern doesn’t work that may mean you have a broken assembly stub or offset.
Code:
ASM stub:
_('CommandTraderBuy:')
		_('push 0')
		_('push TraderCostID')
		_('push 1')
		_('push 0')
		_('push 0')
		_('push 0')
		_('push 0')
		_('mov edx,dword[TraderCostValue]')
		_('push edx')
		_('push C')
		_('mov ecx,C')
		_('call TraderFunction')
		_('add esp,24')
		_('mov dword[TraderCostID],0')
		_('mov dword[TraderCostValue],0')
		_('ljmp CommandReturn')
list comprehension is offline  
Thanks
10 Users
Old 05/03/2021, 02:41   #2
 
elite*gold: 0
Join Date: Apr 2017
Posts: 16
Received Thanks: 7
Hey, thank you very much for this guide, very helpful - did all the step by myself on both cheat engine and ghidra and I was able to find the updated pattern. We'll see after next update if I can truly do it myself!

However I got curious and checked if I could find the new pattern for the salvage function, and I found out that the pattern is actually the same. So I assume the assembly stuff need to be reworked, am I right? If so, would you have a lead on how to repair it?

Here is what I found on Ghidra, but not sure what to do now ....

Pmolik1809 is offline  
Old 05/04/2021, 03:41   #3
 
elite*gold: 0
Join Date: Jul 2019
Posts: 103
Received Thanks: 78
Quote:
Originally Posted by Pmolik1809 View Post
Hey, thank you very much for this guide, very helpful - did all the step by myself on both cheat engine and ghidra and I was able to find the updated pattern. We'll see after next update if I can truly do it myself!

However I got curious and checked if I could find the new pattern for the salvage function, and I found out that the pattern is actually the same. So I assume the assembly stuff need to be reworked, am I right? If so, would you have a lead on how to repair it?

Here is what I found on Ghidra, but not sure what to do now ....

I don't have time tonight to look at the asm stub in depth but salvage is a unique case. In the event you have the correct pattern for ScanSalvageFunction, you must also have the correct pattern for ScanSalvageGlobal. Then the accompanying offsets for both of those patterns. If those are all good the next 2 things that can break it are incorrect packet header or assembly stub. You are on the right trail though to get it working.

Post the patterns, offsets, and asm stubs you are using for salvage and when I get a chance I will work through it to see what is broken on it.
list comprehension is offline  
Old 05/04/2021, 13:12   #4
 
elite*gold: 0
Join Date: Apr 2017
Posts: 16
Received Thanks: 7
Hi! Thanks a lot for your help, I kept on trying to find a solution meanwhile without any success. I realized few things though, for example that the function I posted above is not the correct one.

To find that out I took an old GW.exe (last used November 2020) and search for the pattern which was working back then in Ghidra

Code:
SetValue('SalvageFunction', '0x' & Hex(GetScannedAddress('ScanSalvageFunction', -10), 8))
SetValue('SalvageGlobal', '0x' & Hex(MemoryRead(GetScannedAddress('ScanSalvageGlobal', 1) - 0x4), 8))
_('ScanSalvageFunction:')
AddPattern('33C58945FC8B45088945F08B450C8945F48B45108945F88D45EC506A10C745EC7E')
_('ScanSalvageGlobal:')
AddPattern('8B5104538945F48B4108568945E88B410C578945EC8B4110528955E48945F0')
And then I checked the closest solution with the same pattern in Ghidra and assumed the following to be correct

Code:
SetValue('SalvageFunction', '0x' & Hex(GetScannedAddress('ScanSalvageFunction', -10), 8))
SetValue('SalvageGlobal', '0x' & Hex(MemoryRead(GetScannedAddress('ScanSalvageGlobal', 1) - 0x4), 8))
_('ScanSalvageFunction:')
AddPattern('33C58945FC8B45088945F08B450C8945F48B45108945F88D45EC506A10C745EC70')
_('ScanSalvageGlobal:')
AddPattern('8B5104538945F48B4108568945E88B410C578945EC8B4110528955E48945F0')

Here is a Ghidra comparision between SalvageGlobal before december and now



And here is a Ghidra comparision between SalvageFunction before december and now



There is something weird that I don't understand though - it is about offset. I read that the offset should be the difference between the start of the function and the pattern plus 0x1 (I read somewhere that GWA2 has a static offset of +0x1)

So that works well for SalvageFunction as I count -11 + 1 = -10.

But for SalvageGlobal I count -32 + 1 = -0x1F but in our function we have -0x4 ?

Am I doing something wrong here?

Here is my current ASM Stub

Code:
_('CommandSalvage:')
_('push eax')
_('push ecx')
_('push ebx')
_('mov ebx,SalvageGlobal')
_('mov ecx,dword[eax+4]')
_('mov dword[ebx],ecx')
_('add ebx,4')
_('mov ecx,dword[eax+8]')
_('mov dword[ebx],ecx')
_('mov ebx,dword[eax+4]')
_('push ebx')
_('mov ebx,dword[eax+8]')
_('push ebx')
_('mov ebx,dword[eax+c]')
_('push ebx')
_('call SalvageFunction')
_('add esp,C')
_('pop ebx')
_('pop ecx')
_('pop eax')
_('ljmp CommandReturn')
Thanks again for your help, and nothing urgent here, just trying to understand if I'm doing something wrong, and if yes, how can I search better

EDIT: I should also mention that my GW does not crash with the new pattern, but it won't salvage anyway.
Pmolik1809 is offline  
Old 05/04/2021, 13:51   #5
 
elite*gold: 0
Join Date: May 2014
Posts: 262
Received Thanks: 317
AFAIK all "Updates" for Salvage-Func made since Dec. 2019 (Compiler change and Register Call (fastcall) changed to cdecl) haven't been made correctly.
I recommend you to start with a GW-Build < Dec. 2019.
I applaud your efforts, tho!
DerMoench14 is offline  
Old 05/04/2021, 14:06   #6
 
elite*gold: 0
Join Date: Apr 2017
Posts: 16
Received Thanks: 7
Quote:
Originally Posted by DerMoench14 View Post
AFAIK all "Updates" for Salvage-Func made since Dec. 2019 (Compiler change and Register Call (fastcall) changed to cdecl) haven't been made correctly.
I recommend you to start with a GW-Build < Dec. 2019.
I applaud your efforts, tho!
Thanks a lot! Unfortunately I don't have a GW-Build < December 2019 - would you have one by any chance so I could look at it? I'm afraid I won't really see the difference but maybe if I spend a month looking at it I may start noticing differences

Pretty sure I could find the pattern back then somewhere on the forum though
Pmolik1809 is offline  
Old 05/05/2021, 19:15   #7
 
elite*gold: 26
Join Date: Apr 2019
Posts: 56
Received Thanks: 24
Thanks a lot !

Using your tricks & tools, I manage to fix my broken GWA2 patterns.
I even found shorter patterns which should break less easily with future update.
Ghidra was definitely the game changer for me.


I can tell you that you are in the right track. ASM is not the problem. Pattern is.
Using Ghidra "Search Memory" view on the latest gw.exe, plus the "Dirty Trick 1" from @list comprehension, you sould find that there is 11 candidates that could be your fix.
Just brute force them in GWA2 since (like me) you do not have the pre-break gw executable to do a binary diff and voilą.

Happy debugging to my fellow developers.
Yoshikawa91 is offline  
Thanks
1 User
Old 05/05/2021, 19:35   #8
 
elite*gold: 0
Join Date: Apr 2017
Posts: 16
Received Thanks: 7
Quote:
Originally Posted by Yoshikawa91 View Post
Thanks a lot !

Using your tricks & tools, I manage to fix my broken GWA2 patterns.
I even found shorter patterns which should break less easily with future update.
Ghidra was definitely the game changer for me.


I can tell you that you are in the right track. ASM is not the problem. Pattern is.
Using Ghidra "Search Memory" view on the latest gw.exe, plus the "Dirty Trick 1" from @list comprehension, you sould find that there is 11 candidates that could be your fix.
Just brute force them in GWA2 since (like me) you do not have the pre-break gw executable to do a binary diff and voilą.

Happy debugging to my fellow developers.
This is the first thing I tried, to take all pattern and bruteforce it - maybe I made a mistake somewhere, let me try again!

Works now, thanks!
Pmolik1809 is offline  
Old Yesterday, 21:56   #9
 
elite*gold: 0
Join Date: Apr 2018
Posts: 39
Received Thanks: 14
Thank you.

Finally i had some free time and was able to install and follow all your steps.
My antivirus keep deleting Cheat Engine just after the download but allowed Ghidra to stay and run.

It was easy to find the right patterns for ScanTraderFunction and ScanSalvageFunction and now i'm confident i can repair minor chages like those.

Maybe it's a very noob question but in the ScanSalvageFunction we have 3 pairs of 00 at the end of the pattern that are ignored in GWA2 but the two at the end of the ScanTraderFunction are kept, why?

Now i need to undestand how count Offsets if the start of the scan change, can someone explain plz?

regards.
OneStrangeGuy is offline  
Reply


Similar Threads Similar Threads
[Help]Signature/Pattern Scanning for Co Loader
02/12/2018 - CO2 Programming - 0 Replies
Hello Epvp Member, i'm trying to get Pattern to Edit Co Loader i see many guide to Scan Signature/Pattern But all of Them for Healthing Moving Etc i want to get Pattern Of login IP so my question here i must scan while i login?... and if i must Do that there is Unique Key to search by it like IP or Something else to get right Pattern? sorry i just beginner in that so i want little steps to help me Thanks :handsdown:
GWA2 A/mo Vaettir Bot; Need Help!!
02/19/2012 - GW Bots - 1 Replies
http://www.elitepvpers.com/forum/gw-exploits-hacks -bots-tools-macros/1568881-gwa-vaettir-mo-farm-bot .html ENGLISH: I can't seem to get this to work. I've spent 3-4 hours, read the entire thread here, tried to search for a detailed guide on how to get this to work but it's not. What I've done was: Downloaded the 2 files shown in the Original Post... "GWA2 A_Mo Vaettirs Farm by bl4ck3lit3.zip" Extracted it to desktop Ran GW.EXE as admin. Set-up assassin with appropriate gear and...



All times are GMT +2. The time now is 05:38.


Powered by vBulletin®
Copyright ©2000 - 2021, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Support | Contact Us | FAQ | Advertising | Privacy Policy | Terms of Service | Abuse
Copyright ©2021 elitepvpers All Rights Reserved.