Register for your free account! | Forgot your password?

You last visited: Today at 14:24

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

 

Custom Trading Post

Reply
 
Old   #1
 
elite*gold: 0
Join Date: Apr 2009
Posts: 793
Received Thanks: 365
Post Custom Trading Post

Introduction

First of all, you wont find any download in this thread. This thread will contain general informations about the trading post from a technically view and how one can utilize those informations to extend or build your very own trading post. Notice though, that this is not a step by step guide. Instead I will only point out the required informations.

In autuum 2012 the GW2CA team reversed the trading post resulting in a small proof of concept extension allowing unlimited buy orders which was quite handy for trading materials. While we never released that very early version only used by a small group of peopel, we always wanted to write a public fully fledged "extended" trading post capable of showing graphs and similiar things. Well, the time ran by and we never were in the mood to continue work. Now totally occupied by implementing GW2CA its clear to us that we will also never find the time to finish this little project, not now and not in the future. Therefore we hope that some interessted developers will read this thread and continue our work. Maybe some day we will see an really cool public TP overhaul.

Here are some screens of this proof of concept extension showing newly added components aswell as modified ones:


What can be done?

With the informations provided in this thread you can either add, change, replace components of the trading post, or write your own complete overhaul. Writing a trading bot is not possible, as selling and taking items is done via ingame packets and not REST requests. This is not covered in this thread.

How the TP works

The TP is actually a simple website also accessable with your browser. In order to buy items or query information REST requests are sent. The GW2 client uses the framework to render websites. Under the hood Awesomium is an offscreen port of Chromium.

Two things are not mentioned yet: In order to access the TP you have to be authenticated, typically done with a cookie holding the session id. This session is created by the portal server on login and can be attached to a character. An unattached session is sufficient to access the TP and can be obtained from , you wont be able to buy items with that one though.

A feature making the TP special is, that through a JS proxy (window.externalHost) the web-client can directly communicate with the game-client in a REST like fashion. Those request will will result in native calls and are used to query informations like gold, gems, xp or the items possed by the character. Due to that, only a subset of the TP will work if you access it with your regular browser, even with an attached session.

Now, lets finally come to the TP itself. The TP uses the library from Yahoo. This is a very modular framework, quite handy for us, as it allows us to inject our custom modifications in a very simple manner. The JS code of the TP is obfuscated and compressed making it very hard to read. Though by looking on the REST requests and debuging its still possible to get things running. If you really want to write a serious extension I recommend you to rewrite the TP though, as the obfuscated code is a pain in the ass to maintain.

Useful tools

Besides the regular disassemblers like OllyDbg and IDA, two tools have proven useful for this project.

The first one to mention is . Fiddler will capture all your web traffic and allows you to analyze the sites involved and the REST requests sent. Once you get used to it, you will never want to work without it again.

The second one is actually well known, the browser Chrome. Now wait, why should Chrome be useful for reversing? The answere is quite simple: Rember me saying that Awesomium is based on Chromium? Well, Awesomium comes with an remote debugging feature allowing you to use the Chrome Developer Tools for the TP. This is very handy for debugging Arenanets original and your own JS code. You have to enable this feature though. The dll I wrote for this purpose has broken long time ago but I will shortly explain what you have to do to enable it. Basically the only thing to be done is to set remote_debugging_port of . The thing making it a bit tricky is, that you have to modify it in the call to . The problem: The method is called after hitting launch; at this point (in the launcher) awesomium isnt loaded though. The solution: Patch the actual call to point to your function and call the original method in there. Last but not least, the inspector.pak (contained in the awesomium sdk) must be placed into awesomiums working directory, typically in your temp folder starting with gw2cache-. Well back in autuum 2012, the path of this directory once created hasnt changed, so placing the file once should be enough. After all this has been done, you can start debugging by accessing localhost:debugPort in Chrome.

You probably will also find this little website useful to demangle names when you want to set breakpoints:

The concept

Its actually quite easy to build your own TP. Three steps have to be done:
  1. Redirect requests from the client to your own server
  2. Fetch the original site from Arenanets servers
  3. Inject your custom JS code

Code:
           ---- redirected ---->             -------> 
GW2 Client                       Your Server          TP Server
           <--- modified ------------------- <-------
                                      ^                
                                      |
                                      |
                                  Custom.js
I hope that this little graphic isnt too crappy

Redirecting the traffic

This can be done in several ways:
  1. You can hook the UI code
  2. You can hook the awesomium methods
  3. You can change the host file

For both the 1) and the 2) method the best starting point is probably the Awesomium::WebString or Awesomium::WebUrl constructor. Walking up the callstack only by 1 and your in the right method. Pretty easy to reverse as you can simply see the URLs on the stack and in the registers. Just make sure to disable the breakpoints temporaly if you only get trash as Awesomium::WebString is of course used for alot of other stuff too.

The 3) method is probably more stable regarding patches. I dont think that I have to explain here how it is done exactly, simply google it up.

Handling the request

Notice that we used an apache webserver and php. Although you should be able to implement this on any other webserver with any other scripting language you might want to use easily.

The first thing we did was actually setting up a RewriteRule so that every GET requests is handled by a single script called "get.php". Only requests to "authenticate" are handled in a special way.

Code:
RewriteCond %{REQUEST_URI} ^/authenticate
RewriteRule .* authenticate.php

RewriteCond %{REQUEST_METHOD} ^(GET)$
RewriteRule !^(get.php)$ get.php [L]
Now what is "authenticate" for? If the client access this URI, the cookie storing the session id is set (transmitted as GET param, separation) and is redirected to its desired location (also GET param). This behaviour is quite easy to achieve:

Code:
<?php
$s = $_GET['session_key'];
$loc = $_GET['source'];

header("HTTP/1.1 303 Redirection");
header("Location:" . $loc);
header("Set-Cookie: s=" . $s);
?>
In the "get.php" script we build a new GET request (to be sent to the Arenanet server) making sure that the cookie (containing the mandatory session id) is forwarded and Referer is changed to our host:

Code:
$reqURI = $_SERVER['REQUEST_URI'];
$host = $_SERVER['HTTP_HOST'];

$headers = "Cookie: s=" . $_COOKIE['s'] . "\r\n";

if($_SERVER['HTTP_REFERER'] != ""){
	$headers = $headers . "Referer: " . str_replace("http://www.our-host.com","https://tradingpost-live.ncplatform.net",$_SERVER['HTTP_REFERER']) . "\r\n";
}

$opts = array(
  'http'=>array(
    'method'=>"GET",
    'header'=>$headers
  )
);

$context = stream_context_create($opts);
$tpAdr = "https://tradingpost-live.ncplatform.net" . $reqURI;	

$raw = file_get_contents($tpAdr,false,$context);
Please notice that this was only a proof of concept, you probably want to forward / set additional headers (like Accept-language) so that the your request matchs the original request as much as possible.

Now we simply check $reqURI whether we have to inject our scripts and after doing so we send the (probably modified) response back to the client.

Injecting the custom scripts

Now this is the point where you probably want to thank god that Arenanet uses YUI. All you have to do is to add an additional <script> tag refering to your custom script. To add it in the right place, you can simply use str_replace:

Code:
$injectionPoint = '<script src="https://d3eye159mg2h6m.cloudfront.net/combo/_/js/_config.1126439568.4.js"></script>';
$yuiInjection = '<script src="http://our-host.com/custom.js"></script>';
$yuiInjection .= $injectionPoint

$domInjected = str_replace($injectionPoint,$yuiInjection,$raw);

echo $domInjected;
Ok, what do you have to write into this scripts now? We simply replace Arenanet modules with our own ones. A good starting point is too simply copy & paste the original module and start modifying the parts your interessted in. In order to modify the UI (view, not controller nor model), you have to replace the widget templates. Heres a small example only containing the important parts:

Code:
YUI.add("gw2-template-buy-instant", function(Y) {
    var tmpl = Y.Handlebars.template(function(Handlebars, depth0, helpers, partials, data) {
        helpers = helpers || Handlebars.helpers;
        var buffer = "", stack1, stack2, foundHelper, self = this, functionType = "function", helperMissing = helpers.helperMissing, undef = void 0, escapeExpression = this.escapeExpression;
        
	// Writes a simple header and increases the limit of the input.
	// This corrsponds to the first screenshot seen in this thread.
		
        buffer += "<div><h3 class=\"infiny\">Infiny Buy: </h3></div>\r\n";
        buffer += "<h5>\r\n    ";
        
	// Code to resolve localized strings: 
		
	stack1 = "tp.buy.quantity";
        foundHelper = helpers.i18n;
        stack2 = foundHelper || depth0.i18n;
        if (typeof stack2 === functionType) {
            stack1 = stack2.call(depth0, stack1, {hash: {}});
        } 
        else if (stack2 === undef) {
            stack1 = helperMissing.call(depth0, "i18n", stack1, {hash: {}});
        } 
        else {
            stack1 = stack2;
        }
		
        buffer += escapeExpression(stack1) + "\r\n    <input\r\n        type=\"text\"\r\n        data-type=\"number\"\r\n        class=\"yui3-u infiny-input\"\r\n        min=\"1\"\r\n        max=\"" + BUY_MAX + "\"\r\n        maxlength=\"6\"\r\n        placeholder=\"1\"\r\n        value=\"1\"\r\n    />\r\n</h5>\r\n\r\n<div class=\"total yui3-g\">\r\n    <p class=\"yui3-u\">";
        
	// Further tags have been removed for simplicity
		
        return buffer;
    });
    
    
    Y.namespace("GW2.Templates")["buy-instant"] = tmpl; // store the template in a global map
    Y.Handlebars.registerPartial("buyInstant", tmpl); // register the template
}, "", {requires: ["handlebars-base"]});
Notice that most of arenanets stuff was kept. Though this is still a very verbose and "readable" code. Other parts dont look that nice
I think the basic principle should be understood now. Copy Arenanets modules, modifiy or re-implement them and your done. By re-registering, yours and not Arenanets modules are loaded.

The "point of injection" is actually the very last import. You might want to search for it in a different manner (as a quick look reveals that the scriptname has changed now) or simply choose another one. Back in autuum I havent found anything about how YUI loads their modules concretly, especially in the case that some are defined multiple times. Therefore, you might want to play with this a bit.

When reversing the js code its always useful to start with the REST requests as the parameters are known and you can then traverse them back. To take a look at them, simply open Fiddler or browse through the gw2spidy src. On ********* you will also find a long discussion regarding the TP.
The Chrome Developer Tools are extremly useful for reversing the obfuscated js as mentioned above and it really worths activating remote debugging, especially if you plan a bigger and serious project.

Oh and you might ask yourself how do I get the original files? Simply intercept the GET request using Fiddler and copy it into your browser. Equipped with a valid session id you can now inspect the TP with Firebug or the Chrome Developer Tools. They even added a nice background (better than the plain white wall they had back then).

A note on overhauls and other concepts

If you want to write a complete overhaul you probably go better by completly focusing on the REST requests, including those sent to the client. There arent too many and its for sure easier to reimplement them instead of working through compressed and obfuscated code.
Besides that, this removes the additonal delay arising from the two GET requests. This could also be omitted by injecting the js files at clientside. For this you probably have to dig into the core of Awesomium though, which is, as far as im am aware of, not public.
Other methods omitting this delay I came up with are to run the server locally, or to intercept the traffic like Fiddler does (it would acutally be possible to write the whole injection based on Fiddlers scripting capabilities, but i guess you dont want your users to run Fiddler )

Conclusion

I hope that this thread will give you an quick overview about the TP and how to extent it. Please be kind if I didnt go too much into detail at some points but this thread is already yet quite long and the last time I dealt with this topic was some months ago. Im eager to see some progress on this
Attached Images
File Type: jpg owned3.jpg (66.0 KB, 4420 views)
File Type: jpg owned4.jpg (71.7 KB, 4350 views)



Xereon is offline  
Thanks
13 Users
Old   #2
Community Manager
 
elite*gold: 421
The Black Market: 293/0/0
Join Date: Jan 2010
Posts: 21,381
Received Thanks: 9,628
Pics not available.


Dacx is offline  
Thanks
1 User
Old   #3
 
elite*gold: 0
Join Date: Apr 2009
Posts: 793
Received Thanks: 365
Should now be available. Havent got any problems though o.O
Xereon is offline  
Old   #4
 
elite*gold: 0
Join Date: Jan 2005
Posts: 8
Received Thanks: 0
Very good read.


eltret is offline  
Old   #5
 
elite*gold: 0
Join Date: Apr 2009
Posts: 793
Received Thanks: 365
btw, here is a little eyecandy for those who wonder what is possible if you reverse the UI code (credits go to ACB):


For sure an nice addition to an extension too
Attached Images
File Type: jpg owned1.jpg (94.9 KB, 4298 views)
File Type: jpg owned2.jpg (92.1 KB, 4277 views)
Xereon is offline  
Old   #6
 
elite*gold: 0
Join Date: Nov 2010
Posts: 1,017
Received Thanks: 118
Code:
/**
     * Returns the current GuildWars2 ingame Session.
     * @return is the Ingame Session.
     */
    public static String getIngameSession() {
        
        try {
            
            File file = new File(System.getProperty("user.home") 
                    + "\\AppData\\Local\\Temp");
            
            String gw2cacheDir = null;
            
            for (Object o : file.list()) {
                
                if (o.toString().contains("gw2cache")) {
                    gw2cacheDir = o.toString();      
                }
            }  
            
            String loc = System.getProperty("user.home") 
                    + "\\AppData\\Local\\Temp\\"
                    + gw2cacheDir + "\\data\\Cookies";
            
            Class.forName("org.sqlite.JDBC");
            Connection conn = DriverManager.getConnection("jdbc:sqlite:" + loc);
            Statement stat = conn.createStatement();
            ResultSet rs = stat.executeQuery("select * from cookies;");
            
            ArrayList<String[]> possibles = new ArrayList();
            while (rs.next()) {
                
                if (rs.getString("creation_utc") != null
                        && rs.getString("value") != null) {

                    possibles.add(new String[] {rs.getString("creation_utc")
                            ,rs.getString("value")});
                } 
            }
            
            rs.close();
            conn.close();
            int newestUtc = 0;
            String actualSession = "";
            
            for (String[] str : possibles) {
                if (Integer.valueOf(str[0].substring(8)) > newestUtc) {
                    actualSession = str[1];
                }
            }
            
            return actualSession;
            
        } catch (Exception ex) {
            
            return null;
        }  
    }
I hope that helps. Please dont compiled it with macs, its only for winsystems

Regards..
RockThatBodyTBBT is offline  
Old   #7
 
elite*gold: 0
Join Date: Apr 2009
Posts: 793
Received Thanks: 365
Quote:
Originally Posted by RockThatBodyTBBT View Post
Code:
/**
     * Returns the current GuildWars2 ingame Session.
     * @return is the Ingame Session.
     */
    public static String getIngameSession() {
        
        try {
            
            File file = new File(System.getProperty("user.home") 
                    + "\\AppData\\Local\\Temp");
            
            String gw2cacheDir = null;
            
            for (Object o : file.list()) {
                
                if (o.toString().contains("gw2cache")) {
                    gw2cacheDir = o.toString();      
                }
            }  
            
            String loc = System.getProperty("user.home") 
                    + "\\AppData\\Local\\Temp\\"
                    + gw2cacheDir + "\\data\\Cookies";
            
            Class.forName("org.sqlite.JDBC");
            Connection conn = DriverManager.getConnection("jdbc:sqlite:" + loc);
            Statement stat = conn.createStatement();
            ResultSet rs = stat.executeQuery("select * from cookies;");
            
            ArrayList<String[]> possibles = new ArrayList();
            while (rs.next()) {
                
                if (rs.getString("creation_utc") != null
                        && rs.getString("value") != null) {

                    possibles.add(new String[] {rs.getString("creation_utc")
                            ,rs.getString("value")});
                } 
            }
            
            rs.close();
            conn.close();
            int newestUtc = 0;
            String actualSession = "";
            
            for (String[] str : possibles) {
                if (Integer.valueOf(str[0].substring(8)) > newestUtc) {
                    actualSession = str[1];
                }
            }
            
            return actualSession;
            
        } catch (Exception ex) {
            
            return null;
        }  
    }
I hope that helps. Please dont compiled it with macs, its only for winsystems

Regards..
Just for the reference: This was originally posted by Netzgeist on own-edcore.

And im actually wondering why it shouldnt work for mac. Awesomium is actually multiplatform compatible and probably uses the same method to store cookies (namely a SQLite db) like on a windows os. Of course youll have to adjust the directory...

I never took a closer look at this, but my guess is, that this information (well actually the newest session id) will only be available after accessing the tp for the first time in a session. Therefor reading the session id directly from the client might be a better solution. This is a dump of the function writing the session id to a global var:

Code:
CPU Disasm
Address   Hex dump          Command                                  Comments
00417920  /> /57            PUSH EDI
00417921  |. |8BF8          MOV EDI,EAX
00417923  |. |8B47 08       MOV EAX,DWORD PTR DS:[EDI+8]
00417926  |. |83C7 08       ADD EDI,8
00417929  |. |A3 A8C25A01   MOV DWORD PTR DS:[15AC2A8],EAX
0041792E  |. |8B4F 04       MOV ECX,DWORD PTR DS:[EDI+4]
00417931  |. |890D ACC25A01 MOV DWORD PTR DS:[15AC2AC],ECX
00417937  |. |8B57 08       MOV EDX,DWORD PTR DS:[EDI+8]
0041793A  |. |8915 B0C25A01 MOV DWORD PTR DS:[15AC2B0],EDX
00417940  |. |8B47 0C       MOV EAX,DWORD PTR DS:[EDI+0C]
00417943  |. |A3 B4C25A01   MOV DWORD PTR DS:[15AC2B4],EAX
00417948  |. |A1 A0C25A01   MOV EAX,DWORD PTR DS:[15AC2A0]
0041794D  |. |A8 01         TEST AL,01
0041794F  |. |75 34         JNE SHORT 00417985
00417951  |. |56            PUSH ESI
00417952  |. |8B35 A0C25A01 MOV ESI,DWORD PTR DS:[15AC2A0]
00417958  |. |85F6          TEST ESI,ESI
0041795A  |. |74 28         JE SHORT 00417984
0041795C  |. |8D6424 00     LEA ESP,[ESP]
00417960  |> |8B16          /MOV EDX,DWORD PTR DS:[ESI]
00417962  |. |8B46 30       |MOV EAX,DWORD PTR DS:[ESI+30]
00417965  |. |8B52 28       |MOV EDX,DWORD PTR DS:[EDX+28]
00417968  |. |57            |PUSH EDI
00417969  |. |50            |PUSH EAX
0041796A  |. |8BCE          |MOV ECX,ESI
0041796C  |. |FFD2          |CALL EDX
0041796E  |. |A1 98C25A01   |MOV EAX,DWORD PTR DS:[15AC298]
00417973  |. |F64430 04 01  |TEST BYTE PTR DS:[ESI+EAX+4],01
00417978  |. |8D4430 04     |LEA EAX,[ESI+EAX+4]
0041797C  |. |75 06         |JNE SHORT 00417984
0041797E  |. |8B30          |MOV ESI,DWORD PTR DS:[EAX]
00417980  |. |85F6          |TEST ESI,ESI
00417982  |.^|75 DC         \JNE SHORT 00417960
00417984  |> |5E            POP ESI
00417985  |> |5F            POP EDI
00417986  |. |C3            RETN
00417987  |  |CC            INT3
00417988  |  |CC            INT3
00417989  |  |CC            INT3
0041798A  |  |CC            INT3
0041798B  |  |CC            INT3
0041798C  |  |CC            INT3
0041798D  |  |CC            INT3
0041798E  |  |CC            INT3
0041798F  |  |CC            INT3
00417990  |. |8B02          MOV EAX,DWORD PTR DS:[EDX]
00417992  |. |83F8 2B       CMP EAX,2B
00417995  |. |74 0D         JE SHORT 004179A4
00417997  |. |83F8 5A       CMP EAX,5A
0041799A  |. |75 35         JNE SHORT 004179D1
0041799C  |. |8D42 0C       LEA EAX,[EDX+0C]
0041799F  |.^\E9 7CFFFFFF   JMP 00417920
004179A4  |>  33C0          XOR EAX,EAX
004179A6  |.  3942 0C       CMP DWORD PTR DS:[EDX+0C],EAX
004179A9  |.  75 14         JNE SHORT 004179BF
004179AB  |.  A3 B4C25A01   MOV DWORD PTR DS:[15AC2B4],EAX
004179B0  |.  A3 B0C25A01   MOV DWORD PTR DS:[15AC2B0],EAX
004179B5  |.  A3 ACC25A01   MOV DWORD PTR DS:[15AC2AC],EAX
004179BA  |.  A3 A8C25A01   MOV DWORD PTR DS:[15AC2A8],EAX
004179BF  |>  8B52 0C       MOV EDX,DWORD PTR DS:[EDX+0C]
004179C2  |.  83FA 01       CMP EDX,1
004179C5  |.^ 74 05         JE SHORT 004179CC
004179C7  |.  83FA 02       CMP EDX,2
004179CA  |.  75 05         JNE SHORT 004179D1
004179CC  |>^ E9 7FBBFEFF   JMP 00403550
004179D1  \>  C3            RETN
Notice that the session id is stored as raw binary and is formated in this function for the URI:

Code:
CPU Disasm
Address   Hex dump          Command                                  Comments
0085D820  /$  55            PUSH EBP                                 ; Gw2.0085D820(guessed Arg1)
0085D821  |.  8BEC          MOV EBP,ESP
0085D823  |.  8B45 08       MOV EAX,DWORD PTR SS:[ARG.1]
0085D826  |.  56            PUSH ESI
0085D827  |.  57            PUSH EDI
0085D828  |.  0FB678 0F     MOVZX EDI,BYTE PTR DS:[EAX+0F]
0085D82C  |.  57            PUSH EDI                                 ; /<%02X>
0085D82D  |.  0FB678 0E     MOVZX EDI,BYTE PTR DS:[EAX+0E]           ; |
0085D831  |.  57            PUSH EDI                                 ; |<%02X>
0085D832  |.  0FB678 0D     MOVZX EDI,BYTE PTR DS:[EAX+0D]           ; |
0085D836  |.  57            PUSH EDI                                 ; |<%02X>
0085D837  |.  0FB678 0C     MOVZX EDI,BYTE PTR DS:[EAX+0C]           ; |
0085D83B  |.  8B70 04       MOV ESI,DWORD PTR DS:[EAX+4]             ; |
0085D83E  |.  57            PUSH EDI                                 ; |<%02X>
0085D83F  |.  0FB678 0B     MOVZX EDI,BYTE PTR DS:[EAX+0B]           ; |
0085D843  |.  57            PUSH EDI                                 ; |<%02X>
0085D844  |.  0FB678 0A     MOVZX EDI,BYTE PTR DS:[EAX+0A]           ; |
0085D848  |.  57            PUSH EDI                                 ; |<%02X>
0085D849  |.  0FB678 09     MOVZX EDI,BYTE PTR DS:[EAX+9]            ; |
0085D84D  |.  57            PUSH EDI                                 ; |<%02X>
0085D84E  |.  0FB678 08     MOVZX EDI,BYTE PTR DS:[EAX+8]            ; |
0085D852  |.  8B00          MOV EAX,DWORD PTR DS:[EAX]               ; |
0085D854  |.  57            PUSH EDI                                 ; |<%02X>
0085D855  |.  8BFE          MOV EDI,ESI                              ; |
0085D857  |.  C1EF 10       SHR EDI,10                               ; |
0085D85A  |.  57            PUSH EDI                                 ; |<%04X>
0085D85B  |.  0FB7F6        MOVZX ESI,SI                             ; |
0085D85E  |.  56            PUSH ESI                                 ; |<%04X>
0085D85F  |.  50            PUSH EAX                                 ; |<%08X>
0085D860  |.  68 60E22601   PUSH OFFSET 0126E260                     ; |Format = "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"
0085D865  |.  52            PUSH EDX                                 ; |Arg2 => ARG.EDX
0085D866  |.  51            PUSH ECX                                 ; |Arg1 => ARG.ECX
0085D867  |.  E8 C493DDFF   CALL 00636C30                            ; \Gw2.00636C30
0085D86C  |.  83C4 38       ADD ESP,38
0085D86F  |.  5F            POP EDI
0085D870  |.  5E            POP ESI
0085D871  |.  5D            POP EBP
0085D872  \.  C2 0400       RETN 4
You could use both as entry point to find the location, but they are ofc outdated. A simple pattern search should reveal the current functions though.
Xereon is offline  
Thanks
2 Users
Old   #8
 
elite*gold: 0
Join Date: Sep 2009
Posts: 137
Received Thanks: 15
i say it looks really sweet but to hard for te normal player i mean i wood understand it if i read all but there are peupel that only want to klik klik and make it word but this is really sweet work
herre is offline  
Old   #9
 
elite*gold: 0
Join Date: Apr 2009
Posts: 793
Received Thanks: 365
Quote:
Originally Posted by herre View Post
i say it looks really sweet but to hard for te normal player i mean i wood understand it if i read all but there are peupel that only want to klik klik and make it word but this is really sweet work
The intention of the thread as stated in the introduction is not to provide a finished plugin or addon but rather to provide reference like information for developers.
Xereon is offline  
Thanks
1 User
Old   #10
 
elite*gold: 0
Join Date: Sep 2009
Posts: 137
Received Thanks: 15
Quote:
Originally Posted by Xereon View Post
The intention of the thread as stated in the introduction is not to provide a finished plugin or addon but rather to provide reference like information for developers.
i know i read it all and i like it
herre is offline  
Old   #11
 
elite*gold: 0
Join Date: Feb 2012
Posts: 95
Received Thanks: 2
Hurr-Durr too much text. How i can buy materials with small prices? Small note will be cool if it possible.
samtracy is offline  
Old   #12
 
elite*gold: 0
Join Date: Jun 2013
Posts: 14
Received Thanks: 2
I know I still have a long way to go to read this code and understand it but I'm lately into programming and stuff so can you tell me what language is that code in so I can begin learning it ?

Would appreciate some heads up in general too for coding hacks and tools, and don't worry I already know it's not easy at all and it will take quite some time and effort but I'm a hard worker and an eager learner
theguardian759 is offline  
Old   #13
 
elite*gold: 0
Join Date: May 2010
Posts: 87
Received Thanks: 11
Quote:
Originally Posted by theguardian759 View Post
can you tell me what language is that code in so I can begin learning it ?
The trading post stuff is website based so it's HTML/PHP & Javascript (Ajax).
You might require knowledge of other languages also like ASM to look at how the game handles authentication etc as posted by Xereon.
Venlik is offline  
Old   #14
 
elite*gold: 0
Join Date: Sep 2010
Posts: 1
Received Thanks: 4
Post small tool to enable the remote debugger

The inspector.pak (contained in the awesomium sdk) must be placed into awesomiums working directory, typically in your temp folder starting with gw2cache-.

I've written a small tool to enable the remote debugger.
Run it to start Guild Wars 2.
Try to enter localhost:81 in Chrome.
Source:
PHP Code:
#include <iostream>
#include <vector>
#include <string>
#include <windows.h>
#include <algorithm>
#include <iterator>
#include <Psapi.h>
#include <set>

using namespace std;

enum LogLevel {
    
kLogLevel_None 0,
    
kLogLevel_Normal,
    
kLogLevel_Verbose,
};

set<wstringfindGw2() {
    
set<wstringresult;
    
HKEY hKey;
    
wstring muiCacheKeys[] = {
        
L"Software\\Microsoft\\Windows\\ShellNoRoam\\MUICache",
        
L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\Shell\\MuiCache"
    
};
    
bool success false;
    for (
int i 02i++) {
        if (
RegOpenKeyExW(HKEY_CURRENT_USERmuiCacheKeys[i].c_str(), 0KEY_READ, &hKey) == ERROR_SUCCESS) {
            
success true;
        }
    }
    if (!
success)
        return 
result;
    
int index 0;
    
wstring t L"Guild Wars 2 Game Client";
    
wchar_t path[MAX_PATH];
    
byte data[sizeof(L"Guild Wars 2 Game Client") * sizeof(wchar_t)];
    
DWORD type;
    
LSTATUS status;
    do {
        
DWORD sPath sizeof(path) * sizeof(wchar_t);
        
DWORD sData sizeof(data);
        
status RegEnumValueW(hKeyindexpath, &sPathNULL, &typedata, &sData);
        if (
status == ERROR_SUCCESS && type == REG_SZ && wstring((wchar_t*) data0sData sizeof(wchar_t) -1) == t)
            
result.insert(wstring(path0sPath));
        
index++;
    } while (
status != ERROR_NO_MORE_ITEMS);
    
RegCloseKey(hKey);
    return 
result;
}

int main(int argcchar **argv) {
////////////////////////////////////////////////////////////////////
//  Change this if you want:                                      //
    
int port 81;
    
LogLevel log_level kLogLevel_Normal;
//                                                                //
////////////////////////////////////////////////////////////////////
    
PROCESS_INFORMATION processInfo;
    
int pid;
    
set<wstringpaths findGw2();
    
cout << "start Guild Wars 2" << endl;
    
bool success false;
    for (
set<wstring>::iterator i paths.begin(); != paths.end(); i++) {
        
STARTUPINFOW info;
        
ZeroMemory(&infosizeof(info));
        if (
CreateProcessW((*i).c_str(), NULLNULLNULLTRUE0NULLNULL, &info, &processInfo)) {
            
success true;
            break;
        }
    }
    if (!
success) {
        
cout << "Error CreateProcess(...)" << endl;
        
system("pause");
        return 
1;
    }
    
WaitForInputIdle(processInfo.hProcessINFINITE);
    
CloseHandle(processInfo.hThread);
    
pid GetProcessId(processInfo.hProcess);
    
cout << "atach debugger" << endl;
    
cout << "wait for awesomium.dll" << endl;
    if (!
DebugActiveProcess(pid)) {
        
cout << "Error DebugActiveProcess(" << pid << ")" << endl;
        
system("pause");
        return 
1;
    }
    
DEBUG_EVENT e;
    
ZeroMemory(&esizeof(e));
    const 
LPDEBUG_EVENT DebugEv = &e;

    
int dwContinueStatus DBG_EXCEPTION_NOT_HANDLED;
    
SIZE_T c;
    
char backup;
    
PVOID breakPoint NULL;
    const 
char debugBreak 0xCC;
    
bool done false;
    do {
        
dwContinueStatus DBG_EXCEPTION_NOT_HANDLED;
        
ZeroMemory(DebugEvsizeof(DebugEv));

        
// Wait for a debugging event to occur. The second parameter indicates
        // that the function does not return until a debugging event occurs. 
        
WaitForDebugEvent(DebugEvINFINITE);

        
// Process the debugging event code. 
        
switch (DebugEv->dwDebugEventCode) {
        case 
EXCEPTION_DEBUG_EVENT:
            if (
breakPoint && DebugEv->u.Exception.ExceptionRecord.ExceptionAddress == breakPoint) {
                
cout << "reached debugbreak" << endl;
                
// restore backup instruction
                
WriteProcessMemory(processInfo.hProcessbreakPoint, &backupsizeof(backup), &c);
                
FlushInstructionCache(processInfo.hProcessbreakPointsizeof(backup));
                
processInfo.hThread OpenThread(THREAD_GET_CONTEXT THREAD_SET_CONTEXT THREAD_SUSPEND_RESUMEfalseDebugEv->dwThreadId);
                
CONTEXT context;
                
ZeroMemory(&contextsizeof(context));
                
context.ContextFlags CONTEXT_FULL;
                
GetThreadContext(processInfo.hThread, &context);
                
context.Eip--; // -1 to execute the backup instruction
                
cout << "patch log_level <-- " << log_level << endl;
                
WriteProcessMemory(processInfo.hProcess, (PVOIDcontext.Eax, &log_levelsizeof(log_level), &c);
                
cout << "patch port <-- " << port << endl;
                
WriteProcessMemory(processInfo.hProcess, (PVOID) (context.Eax sizeof(int)), &portsizeof(port), &c);
                
SetThreadContext(processInfo.hThread, &context);
                
CloseHandle(processInfo.hThread);
                
dwContinueStatus DBG_CONTINUE;
                
done true;
            }
            break;
        case 
EXIT_PROCESS_DEBUG_EVENT:
            return 
1;

        case 
LOAD_DLL_DEBUG_EVENT:
            
char name[MAX_PATH];
            
int ptr;
            
ptr 0;
            
ReadProcessMemory(processInfo.hProcessDebugEv->u.LoadDll.lpImageName, &ptrsizeof(ptr), &c);
            
ReadProcessMemory(processInfo.hProcess, (void*) ptrnamesizeof(name), &c);
            if (
DebugEv->u.LoadDll.fUnicode) {
                
wstring f;
                
wstring((wchar_t*) name);
                
wstring t;
                
L"awesomium.dll";

                if (
f.length() > t.length() && f.substr(f.length() - t.length(), t.length()) == t) {
                    
cout << "awesomium.dll loaded" << endl;
                    
cout << "insert debugbreak at Awesomium::WebCore::Initialize(Awesomium::WebConfig)" << endl;
                    
breakPoint = (PVOID) ((unsignedDebugEv->u.LoadDll.lpBaseOfDll 0x43D80);
                    
ReadProcessMemory(processInfo.hProcessbreakPoint, &backupsizeof(backup), &c);
                    
WriteProcessMemory(processInfo.hProcessbreakPoint, &debugBreaksizeof(debugBreak), &c);
                }
            }
            break;
        }

        
// Resume executing the thread that reported the debugging event.
        
for (int i 0&& (!ContinueDebugEvent(DebugEv->dwProcessId,
            
DebugEv->dwThreadId,
            
dwContinueStatus)); i++){
        };
    } while (!
done);
    
cout << "detach debugger" << endl;
    if (!
DebugActiveProcessStop(pid)) {
        
cout << "Error DebugActiveProcessStop(" << pid << ")" << endl;
        
system("pause");
        return 
1;
    }
    
CloseHandle(processInfo.hProcess);
    
cout << "try to enter localhost:" << port << " in Chrome" << endl;
    
Sleep(5000);
    return 
0;

Attached Files
File Type: zip GWTPDebug.zip (11.1 KB, 36 views)
MegoBit is offline  
Thanks
4 Users
Old   #15
 
elite*gold: 0
Join Date: Sep 2009
Posts: 91
Received Thanks: 7
Sehr aufschlussreiche präsentation, da lässt sich einiges machen.Der Remote zugriff ist wirklich Gold wert, im wahrsten Sinne des Wortes.


rommsbomms is offline  
Reply



« Previous Thread | Next Thread »

Similar Threads
Beta-Max Online ~ 120 CAP ~ D13 ~ Custom Events ~ Custom Quests ~ Custom Areas
Fantastic Server. If reliablility is what you want then Beta-Max is for you. Unique PVP Battles - Pets - Items - Quests - Jobs - Wonga and so much...
6 Replies - SRO PServer Advertising



All times are GMT +2. The time now is 14:24.


Powered by vBulletin®
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.

Support | Contact Us | FAQ | Advertising | Privacy Policy
Copyright ©2017 elitepvpers All Rights Reserved.