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
{% highlight "cxml" %}- {% endhighlight %}- ...placeholder + + + + + + +
game.sv4_hiscore
{% highlight "cxml" %}- {% endhighlight %}- ...placeholder + +
{% highlight "cxml" %}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 @@{% endhighlight %} - ...placeholder + + + ++ + + + + + + + + +
{% highlight "cxml" %}{% endhighlight %} - - +
A list of all packages available for download.
+url |
+ + |
desc |
+ + |
size |
+ Size of the resource at url in bytes |
+
pkgtype |
+ + |
sumtype |
+ Only allowable value is md5 . |
+
sum |
+ The sumtype digest of the file at url . |
+
from |
+ + |
till |
+ + |
package.intend
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.
{% 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.
+
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 %}+ + +
Packets are compressed using lzss. The compressed data structure is a repeating cycle of an 8 bit flags byte,