diff --git a/templates/pages/proto/game/sv4.html b/templates/pages/proto/game/sv4.html index 0a44e31..a1fae91 100644 --- a/templates/pages/proto/game/sv4.html +++ b/templates/pages/proto/game/sv4.html @@ -89,8 +89,13 @@

game.sv4_common

Request:

{% highlight "cxml" %}
-    
-        ...placeholder
+    
+        
+        
+        
+        
+        
+        
     
 {% endhighlight %}

Response:

@@ -117,14 +122,27 @@

game.sv4_hiscore

Request:

{% highlight "cxml" %}
-    
-        ...placeholder
+    
+        
     
 {% endhighlight %}

Response:

{% highlight "cxml" %}
     
-        ...placeholder
+        
+            
+                
+                
+                
+                
+                
+                
+                
+                
+                
+                
+            
+        
     
 {% endhighlight %}
diff --git a/templates/pages/proto/package.html b/templates/pages/proto/package.html index d399f43..87a3676 100644 --- a/templates/pages/proto/package.html +++ b/templates/pages/proto/package.html @@ -11,10 +11,44 @@

Response:

{% highlight "cxml" %}
     
-        
+        
     
 {% endhighlight %}

A list of all packages available for download.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
url
desc
sizeSize of the resource at url in bytes
pkgtype
sumtypeOnly allowable value is md5.
sumThe sumtype digest of the file at url.
from
till

package.intend

Request:

diff --git a/templates/pages/protocol.html b/templates/pages/protocol.html index 8fdfc8f..2947e1d 100644 --- a/templates/pages/protocol.html +++ b/templates/pages/protocol.html @@ -178,6 +178,7 @@
  • lobby2
  • netlog
  • globby
  • +
  • poseevent
  • I'll try and figure these out in due course, promise!

    {% endblock %} \ No newline at end of file diff --git a/templates/pages/transport.html b/templates/pages/transport.html index ad0c921..cdb2c73 100644 --- a/templates/pages/transport.html +++ b/templates/pages/transport.html @@ -28,13 +28,83 @@

    Encryption is not performed using a single static key. Instead, each request and response has its own key that is generated.

    These keys are generated baesd on the X-Eamuse-Info header.

    -

    This header loosely follows the format 1-[0-9a-f]{8}-[0-9a-f]{4}. This corresponds to - [version]-[serial]-[salt]. TODO: Confirm this +

    Keys follow thge format 1-[0-9a-f]{8}-[0-9a-f]{4}. This corresponds to + [version]-[seconds]-[salt]. The salt is generated by a simple PRNG.

    -

    Our per-packet key is then generated using md5(serial | salt | KEY). Identifying KEY is - left as an exercise for the reader, however should not be especially challenging. Check - the page source if you're stuck.

    - +
    + The PRNG +
    {% highlight "c" %}
    +uint32_t PRNG_STATE = 0x41c64e6d;
    +
    +uint32_t prng() {
    +    int upper = (PRNG_STATE * 0x838c9cda) + 0x6072;
    +    PRNG_STATE = (PRNG_STATE * 0x41c64e6d + 0x3039) * 0x41c64e6d + 0x3039;
    +    return upper & 0x7fff0000 | PRNG_STATE >> 0xf & 0xffff;
    +}
    +{% endhighlight %}
    +

    This is a simple linear conguential pseudorandom number generator (what a mouthful) being stepped twice every + call. The constants being used for the main LCPRNG are taken from glibc, along with the dropping of the "least + random" bit. Where this implementation differs however is that the LCPRNG is stepped twice every call, and that + a second LCPRNG is used for the upper 2 bytes.

    +

    One interesting observation is that the "least random" bit, that is typically discarded by the + 0x7ff... mask is not actually being discarded here, as the upper two bytes of + PRNG_STATE, not the lower two, are used unmasked. +

    +

    Another interseting observation is that due to the nature of LCGs, stepping it twice is no more secure than once. + The implementation presented here is actually just a single step of + PRNG_STATE = (PRNG_STATE * 0xc2a29a69) + 0xd3dc167e. Make sure to understand cryptography before + trying to roll your own! +

    +
    +

    Our per-packet key is then generated using md5(seconds | salt | ENC_KEY). Identifying + ENC_KEY is left as an exercise for the reader, however should not be especially challenging. +

    +
    + Source code details +

    The interesting stuff can be found at libavs-win32-ea3.dll:0x1002a800. Rather than screenshots, I've + gone and tidied up the code somewhat to make it easier to follow. eamuse_info is pre-populated with + "X-Eamuse-Info: 1-" by the calling function (0x1000eeed) after which the pointer is + incremented to leave it right after that -, ready for us to vsnprintf into it. +

    +
    {% highlight "c" %}
    +static const char *ENC_KEY[26] = "Wait you didn't think I'd put this here, did you?";
    +
    +int xrpc_crypt(char *packet, char *xeamuse_info) {
    +    char md5_key[16];
    +    char key[32];
    +
    +    // Copy the {8}-{4} hex char pairs into key as 6 bytes
    +    if (copy_from_hex(key, 4, xeamuse_info) == -1)
    +        return -1;
    +    if (xeamuse_info[8] != '-')
    +        return -1;
    +    if (copy_from_hex(key + 4, 2, xeamuse_info + 9) == -1)
    +        return -1;
    +
    +    // Add our constant key after the two variable parts...
    +    strncpy(key + 6, ENC_KEY, 26);
    +    // ...MD5 it all...
    +    mdigest_create_local(0, key, 32, md5_key, 16);
    +    // ...and use that digest as the key for RC4
    +    arc4(packet, md5_key, 16);
    +    return 0;
    +}
    +
    +int xrpc_key_and_crypt(char *packet, char *eamuse_info, size_t size) {
    +    uint64_t miliseconds = read_timer(0);
    +    uint32_t seconds = __aulldiv(miliseconds, 1000, 0);
    +    uint16_t salt = prng() & 0xffff;
    +
    +    int bytes_formatted = vsnprintf(eamuse_info, size, "%08x-%04x", seconds, salt);
    +    if (bytes_formatted < size) {
    +        xrpc_crypt(packet, eamuse_info);
    +    }
    +    return bytes_formatted;
    +}
    +{% endhighlight %}
    + +

    +

    LZ77

    Packets are compressed using lzss. The compressed data structure is a repeating cycle of an 8 bit flags byte,