Jump to content

LexManos

Members
  • Posts

    35
  • Joined

  • Last visited

    Never
  • Donations

    0.00 GBP 

Everything posted by LexManos

  1. Well, from what i've seen 'Warden 2.0' (aside from being a gimmicky name some noobs made up) is simply Warden for Mac. Cuz IIRC warden was just a stub with minimal functionality for Mac. I am still interested in seeing a current module. Also, why the HELL couldn't I find this thread when I was looking for it? Seriously, my profile -> Fime Threads Started By -> Nothing...
  2. Hey guys Figured i'd drop my head back in here. It appears my old Warden thread has disappeared. No worries. Just curious if anyone has a dump of any of the 'new' 'Warden 2.0' modules they would be willing to give me. Figured i'd take a crack at seeing what they did. I got a hold of one of the mac modules, and honestly.. nothing much significant there, they just implemented stuff that was already in windows. I have some time on my hands, i couldn't find any details about what has changed, and i'm bored. So I figured i'd ask.
  3. There is no way, thats what they are saying. Just tell your GMs not to level over 80. You can lower everyone's level whos over 80, but they can simply just set there level again.
  4. Whelp, first time trying to install Mangos on a Linux box. (The others have been Windows) I get this error during the configure step. Configuration of MaNGOS 0.16.0 is now complete. === configuring in dep/ACE_wrappers (/home/lex/mangos/objdir/dep/ACE_wrappers) configure.new: running /bin/sh ../../../dep/ACE_wrappers/configure '--prefix=/mangos' '--sysconfdir=/mangos/etc' '--enable-cli' '--enable-ra' '--datadir=/mangos/data' --cache-file=/dev/null --srcdir=../../../dep/ACE_wrappers checking build system type... i686-pc-linux-gnu checking host system type... i686-pc-linux-gnu checking target system type... i686-pc-linux-gnu checking for a BSD-compatible install... /usr/bin/ginstall -c checking whether build environment is sane... yes checking for gawk... gawk checking whether make sets $(MAKE)... yes checking for grep that handles long lines and -e... /usr/bin/grep checking for egrep... /usr/bin/grep -E checking whether #! works in shell scripts... yes checking for g++... g++ checking for C++ compiler default output file name... configure: error: C++ compiler cannot create executables See `config.log' for more details. configure.new: error: ../../../dep/ACE_wrappers/configure failed for dep/ACE_wrappers I compile C++ on this box all the time. Ideas? Ohy, tracked down the error more, it's caused by the ACE_wrappers configuration script not properly removing the end lines from the version, therefore causing a fuckedup confdefs.h header, causing compilations to fail. dnl particularly with M4, do not modify this macro definition. define([ACE_VERSION], patsubst(esyscmd(grep ACE_VERSION ace/Version.h | sed 's/.*\\" *\\(.*\\)\\".*/\\1/'), [ ]))dnl remove newline ending every `esyscmd' answer Thats from the latest pull. *should* be working fine. I don't know why it isn't but it results in '5.6.6\\r\\n' so it's not doing it's job properly.
  5. Seriously guys? Do any of you actually know anything about C++/C#? Tom_Rus has done a great job expanding on what I posted. And I doubt he will ever post a patch for mangos to add warden support nativly. I'm kinda curious why the main dev team hasn't given an official ya/nay on this. But I'm kinda disappointed that nothing has come out of this. I have it in my to-do to re-write my Warden library, and I may post more information and cleaner code after that. Because in my few re-writes of my code I've found a lot better ways to do a lot of the stuff. Anyways, PowerPC versions of WoW and other games DO support Warden, but yes, IIRC they use a different set of modules because they are different architectures. But basically, functionality wise, they are the exact same. Anyways, if anyone actually gives a crap about warden, and wants to actually work on it and not just ask for code. Feel free to PM me if you have any questions, it sends me an email and I keep forgetting to check this place.
  6. Last time I check the functions were actually the same, jsut compiled/coded in different manors with SLIGHTLY different functionality. And last I checked there were 32 different versions of it.But yes, the best way to deal with warden, is to load the module up, and only handle 0x02 yourself. Honestly get your hands on a copy of mediv.mod (The original module) and load it up. Let it handle all the loading, unloading, etc.. of new modules, and you're golden! Thats how I do it for Battle.net and I haven't had any issues in the last 4 functionality changes. The only problem is determining exactly what to do with the 0x02 packet, currently there are 5 versions, Mediv, which jsut returns a SHA1/MD5 of the data sent in 0x02 (Hence why I call it the test module ) One that only supports memory grabs, which is jsut a array of (DWORD Address BYTE Length) places to check. One that added support for file MD5's so it was now (BYTE) ID [1 for MD5, 0 for Address] If it was MD5 it was a CString file name. Then theres the version jsut previous to this one with all the functionality of the current 0x02, but didn't support 0x04/0x05 And then theres what we have today, with the ability to make new RC4 keys. I am Collecting modules so that I can 1) Track whats being used and when 2) Hopefully derrive an algorythem that will determine what version, and what Check IDs there are. So if you would be a dear and add the function into your packet logger to dump the modules/keys that would be awesome! The .mod file is the fully compressed, encrypted module The .key file is the 16 byte RC4 seed you get in 0x01. They are named with the MD5 of the module.
  7. Oh ya, dua overlooked that. Sounds good. Oh ya, I remember that, probably what skunked me out last time I had them open. I haven't actually opened Warden in IDA in months. If I remember correctly, they did like they did with Lockdown, compiled 2 versions (in warden's case I think it was 8 versions) that did exactly the same thing, in different methods, EA: Some used switch() some used if()..else if(), others used pass through functions, etc.. but it's rather annoying. Interesting, Never bothered to check out what this did (as its not used on Bnet) pretty cool that it actually allows for multiple commands per packet. Though your notes appear to be wrong. What are these Library Index/String Lib? I don't see it passing any strings around. Does the module store a static array of strings like that? Or, are those PStrings like in 0x02? (byte) len (void) str But ya, it has no response, so from a necessity point of view it's not important, but it's rather interesting. Have you taken a look at what is at the last 2 addresses? May simply be a overwriting the function pointers.
  8. Fairly easy jsut change your hard coded values Any ways I can get a hold of him? I'd like to see if we can find a way of progmatically extracting out the Check IDs. I'm fairly sure it'd be simple, haven't looked at a module in IDA for a while but if they use a switch() then it's just a jump table.Anyways, I'd also like to figure out exactly what opcode 0x03 does As for the MPQ checks thats a simple SHA1() of the file. GetTickCount is kinda interesting, not sure why they would care about that... except its used as a seed in a few of there encryptions but beyond that its not important. I'd also be really interested in seeing if we could progmatically extract the Yess/No values used in the response (0xE9)
  9. Oh you're manually parsing check IDs Ouch. But it looks sexy. Just a suggestion, I'd say do ID checks vs the post xored value. Just in case they ever bother to change it during a single connection. So that you can parse multiple connections using the same module, because they choose seemingly random xor byte during each connection. But this is cool, how exactly are you determining what each check does? Just guessing, watching the module run, or do you have the modules loaded up in IDA? Anyway I could get you to upload the binary packet logs? At least the Warden packets. I'd like to some work with the modules you are working with. (yes i *could* parse your text logs but... :*( )
  10. Is your Packet Logger Open source? I'd like to see it, see what you've done. I am really interested in seeing how you split up the requests and extract the proper check IDs. ====== UNK2_CHECK START ====== checkType 5D (A6) Unk bytes: 0x69EC659067021B7FAF4541F6329B809950252859DFF590B6 Unk string: Afd32uu ====== UNK2_CHECK END ====== Also, could that not be a SHA1 hash and Seed? Could it not SHA1 that file and compare it to the SHA1 hash, to test this you would need to find the cheat that modifes that file, and a copy of that file, Do some SHA1ing to it and see how things go If would make sense because it has the same response as a PAGE_CHECK, BTW 0xE9 is the current 'No' response, AE No it was not found, or No it was not a match ====== UNK_CHECK result ====== UNK_CHECK result: 0x01 UNK_CHECK value: 0x1BF697FE ====== UNK_CHECK result END ====== This looks something like: Did it work? Yes/No Result: DWORD Possibly CRC32? Have you tried scanning WoW's memory for FE 97 F6 1B? It may be a constant that Warden is looking for 254.151.246.27 or 27.246.151.254, Those IPs look familiar? Sadly I am Missing a lot of Warden modules. If you'd be so kind as to save/upload them to my site I would <3 you. Anyways, I like the fact that you have been working on this, I'd like to work with you more.
  11. Vary nice, It's been confirmed, as I always suspected. The initial field in 0x02 is not libraries, it just strings. So you got a C# version of module prepping/loading/running done? Thats rather interesting, I haven't delt much with C# but others that I know that have are always bitching how difficult it is to run non managed code from it. I'd be interested in seeing how you've done it. Also, Do you know ASM? I still want to have someone figure out the other opcodes, (and preferably a more reliable way to determine them u.u)
  12. Seriously, nobody else has even touched this?I have a updated version which is more efficient, but the bulk of everything is still the same. Anyways, theres a bit of a battle that goes on here, we have cheats, and a way to detect them, but the cheaters also know that detection method, so the thing we need to do, is just make life difficult for the cheater's to deal with. I've done this quite well by allowing non signed modules to be run. Well, I updated it last month to only allow MY signed modules to be run <3 Anyways, someone do some research into where WoW stores it's Mediv module, and it should be cinch to replace it with your own. I've got no plans to publicly dev this anymore, but I urge others to work on it, seriously, the hard parts are already done!
  13. Indeed thats as far as we went and then droped it. We run the modules for 0x05, and handle 0x00-0x02 ourselves. IF you were to FULLY run the module letting it handle everything then yes you would need to duplicate the eintirity of wow to be save it grabs the correct values in 0x02. hence why i'm shifting it off to you nice fellow <3 I know, and i'm not going to do them! This research was solely done because it started to affect chat bots on Battle.net. Since they do not employ any other scans or opcodes at the channel level, this was all that was needed to keep out chat bots online, so I really don't care about the other stuff. (Though I would like a better pragmatic way to obtain the sub-commands for the scan opcode any tips?) <3Ugh spoke to soon, Warden was just reactivated on Battle.net with a new sub-command enabled u.u Its in channel so people are bitching at me to fix it -.-
  14. Considering the fact that a lot of the inner working of Warden have been public all along. And most people who make real cheats can simply look at the code and see whats going on, I don't see anything new here that would drastically reduce the already vulnerable security of Warden. The things that make warden work, is not HOW it does something its how much, it does something. Basically you got to make something to fucking rediclious that it's a bitch and a half for cheat makers to work around. (having to completely duplicate the entirety of WoW's Memory and screen buffers for example) If you can do something like that, not many people will make cheats. What I would like to see is it being the norm that Servers can create and run there own modules. Yes you would need severe trust int eh server operators because they will have the ability to run arbitrary code. But, to fucking bad! If a server is ran that distributes malicious modules, then you shouldn't be using the server! I don't know about most of you guys but I'm a rather trusting and trustworthy individual. (I am entrusted with over 50,000 cdkeys that run through my JBLS server every hour) Never once have I logged or used any. It's all a matter of trust. Considering the fact that you would have to modify your client yourself to not verify the RSA signature, you should be full aware what that would do.
  15. so reading is good right? Some of the functionality of some packets I do not fully understand, but common I can't hold your hand. And I'm bad with ASM so someones gunna have to get elbow deep and help if you want more documentation then that, But for now, we know how to create modules, we know how to transfer them, we know how to load them, we know how to encrypt/decrypt them, we know how to communicate with them! *Looks for someone open to pass the football to...*BTWm Just a side note, due to the nature of warden, all this shit could change in a heart beat. EXCEPT the fact that the initial module work like this. But once it'd downloaded/loaded a neww module the formats for each packet can be completely different.
  16. It could also be that I can't spell worth crap Anyways, what i'd suggest is that someone who has time, write a little packet logger program that knowns how to decode Warden's communications, then simply log all of said communications. Once it's easy to see the raw decrypted packets it'll be easy for anyone who understands how to read binary data to understand what warden is doing. Anybody up for the challenge?
  17. Interesting simply looks like a cach browser that simply only shows modules. Rather lame, except for the convert to dll.. but meh that part isn't difficult, I'll look at it more when I get home.As for your question. When WoW's connection starts you use Mediv_Random to create the seeds for the encryption keys (Using the session key), gen the keys using RC4. Send 0x00, if they say they did not have the module, you send it with a spew of 0x01's Formats of all the packets are in my last post. I'm not gunna give you actuall code for Mangos itself as I posted that a while ago in my old posts i think. And i dont want to look for/write it again. Anyways, u.u i wanna go to be but can't, to fucking hot!
  18. Got a link to it? Anyways, Mediv_Random: Used to generate the encryption seeds. Feed in the session token, read 16 bytes seed a rc4 with that, thats the client's out key, Do the same for the next 16 bytes and thats the in key. SHA1: Supports various types of the SHA1 algorithm, X-SHA1 used in Bnet password hashing, Normal SHA1 used in Warden, and L-SHA1 used in Checkrevision. It also has a helper function that will compute the packet checksum for cheat responses (Just the 20 bytes of SHA1(Data) Xored together in 32-bit segments) MD5: MD5 implementation fully standard Dynamic_Callbacks: Will create stub functions for the callbacks needed by the warden module. So that a single instance of the app can have multiple Warden modules running at the same time. Config: Simply reading an INI file Module: module_get_prep_size: Returns how big the module will be after fully loaded/preped (used to alloc proper memory) module_prep: Verifies the Module's integraty by checking if MD5(Module) = Module Name Decrypts the module using basic RC4 Checks the Module Signature using RSA - Not done, screw writing a BigInt handler in C -.- Decompresses the module using standard zlib inflate Copies the decompressed module to the final buffer Adjusts references to local variables accordingly. Inserts the proper dll import information The module format is a striped down version of the PE format so meh. module_get_init_address: Gets the physical address of the module's init() function. module_init: Calls the init() function inside of the module after prep, returning a region of memory to be passed to further calls to the function module_init_rc4: Call's the module exported init_rc4 function to initialize the encryption keys used when communicating tot eh server. module_handle_packet: Calls the module internal packet handler to deal with the received, encrypted packet. RC4: Standard rc4 implementation Warden: warden_packet - Deals with the raw incoming packet, decrypts it as needed warden_module_information - Handles opcode 0x00, module information: S->C: MEDIV_MODULE_INFORMATION (0x00) (BYTE[16]) MD5 Checksum - MD5 checksum of the modules compressed encrypted data (BYTE[16]) Module RC4 Seed - RC4 seed for decryption of the module (DWORD) Module Compressed Length - Size of the packet C->S: (BYTE) Status - 0: The module was either correct or not in the cache request transfer, 1: The module was in the cache and loaded successfully warden_module_transfer: - Basic file transfer for the module S->C: MEDIV_MODULE_TRANSFER (0x01) (Word) Payload Length (Void) Payload C->S: When Transfer is complete (Byte) Status - 0: Something went wrong (md5 didn't match could not decrypt etc..) You will be DCed 1: Module loaded successfully ready for requests warden_cheat_check: - Deals with opcode 0x02, the checking for chatzorz! I must note this is module specific, the CheckType IDs change per module, also, there is One (1) module floating around where the check is simply CRC332(Data) & SHA1(Data) This is the test module [Mediv] but oddly enough it's still in rotation S->C: CHEAT_CHECKS (0x02) (PString[]) Libraries - Pascal string array of Libraries used in this check (Pascal strings are (Byte) Length (void) data as opposed to Null terminated strings) (Byte) Check Type - There can be any amount of checks, but the format is the same -PageCheckA - Check the data in the page @ Address of length Length if SHA1(SHA1(Seed) & Data) = SHA1 value (DWORD) Seed (Byte[20]) SHA1 (DWORD) Address (Byte) Length -MemCheck - Simple, read Length bytes at Location Address, if Library != then Address is relative to base address of the library in the above array with index Library (Byte) Library (DWORD) Address (Byte) Length 'There are 3 other checks, which i havn't encountered in the wild so iono what they do (Byte) XOr Code - Used to decdoe the CheckType byte to the actuall checktype ID (CheckType ^= XorCode C->S: (WORD) Payload length (DWORD) Payload checksum (SHA1(Payload) Xored together in 32-bit segments) (Void) Payload -PAGECHECKA (Byte) Status - 1: SHA1 value was found, 0: SHA1 value was NOT found -MemCheck (Byte) Status - 1: Could not read from that address, 0: Read successful If Status = 0 Then (Void) Read data warden_handle_raw Passes the encrypted packet off onto the module to handle, THOUGH! Being sure to update the module's RC4 keys before, and snagging them after, so to keep continuity That *should* be the jist of how things work, if you have a specific question and i mean SPECIFIC please feel free to ask.
  19. It includes all the functions you need to download, unpack, initialize, run, and respond to warden's modules. You use it by reading ti and taking 5 mins to wrap your head around how it works, it's actually rather simple. And parts work under *nix systems, and other parts (executing the raw machine code in the module) does not.If anyone is interested in using it, take a moment and read it to learn what it does yourself. If you get stuck or have questions about a particular aspect of what Warden is doing. Feel free to post. But general questions from people who have no interest/use for this is just annoying. (Not to sound mean i'd just don't have the time to explain every aspect in detail when most of the code speaks for itself)
  20. Most of this is public domain either created by myself or an associate of mine, the MD5 class is a re-write of the sha1 class which is a complete re-write of a sha1 class I took from an associate of mine. But pretty much do with as they will. None of this information is new, its all publically avalible if you know where to look so ya, have fun. I really really hate edrama. I am not going to be posting my dll->module converter because i simply can't find it and I don';t care enough to look any further. BUT! if you wish to contact me with any questions on how things work feel free to post them here or PM me. I'll answer them if i have time and if I feel like it, fair enough? Sorry about the multiple posts, but obviously my posts were to long for single. And I was to damn lazy to paste bin it or something.
  21. /* buffer.c * By Ron * Created August, 2008 * * This module provides an easy way to marshall data coming/going. */ #include <stdio.h> #include <stdlib.h> #include "stdint.h" #include <string.h> #include "buffer.h" #include "types.h" /* The initial max length of the string */ #define STARTING_LENGTH 1 static uint16_t host_to_network_16(uint16_t data) { return htons(data); } static uint16_t host_to_host_16(uint16_t data) { return data; } static uint16_t host_to_little_endian_16(uint16_t data) { uint16_t network = host_to_network_16(data); return ((network >> 8) & 0x00FF) | ((network << 8) & 0xFF00); } static uint16_t host_to_big_endian_16(uint16_t data) { return host_to_network_16(data); } static uint32_t host_to_network_32(uint32_t data) { return htonl(data); } static uint32_t host_to_host_32(uint32_t data) { return data; } static uint32_t host_to_little_endian_32(uint32_t data) { uint32_t network = host_to_network_32(data); return ((network << 24) & 0xFF000000) | ((network << 8) & 0x00FF0000) | ((network >> 8) & 0x0000FF00) | ((network >> 24) & 0x000000FF); } static uint32_t host_to_big_endian_32(uint32_t data) { return host_to_network_32(data); } static uint16_t network_to_host_16(uint16_t data) { return htons(data); } /*static uint16_t host_to_host_16(uint16_t data) { return data; } */ static uint16_t little_endian_to_host_16(uint16_t data) { uint16_t network = network_to_host_16(data); return ((network >> 8) & 0x00FF) | ((network << 8) & 0xFF00); } static uint16_t big_endian_to_host_16(uint16_t data) { return network_to_host_16(data); } static uint32_t network_to_host_32(uint32_t data) { return htonl(data); } /*static uint32_t host_to_host_32(uint32_t data) { return data; } */ static uint32_t little_endian_to_host_32(uint32_t data) { uint32_t network = network_to_host_32(data); return ((network << 24) & 0xFF000000) | ((network << 8) & 0x00FF0000) | ((network >> 8) & 0x0000FF00) | ((network >> 24) & 0x000000FF); } static uint32_t big_endian_to_host_32(uint32_t data) { return network_to_host_32(data); } /* Create a new packet buffer */ buffer_t *buffer_create(BYTE_ORDER_t byte_order) { buffer_t *new_buffer = safe_malloc(sizeof(buffer_t)); new_buffer->byte_order = byte_order; new_buffer->valid = TRUE; new_buffer->position = 0; new_buffer->max_length = STARTING_LENGTH; new_buffer->current_length = 0; new_buffer->data = safe_malloc(STARTING_LENGTH * sizeof(char)); return new_buffer; } /* Create a new packet buffer, with data. The data shouldn't include the packet header, * it will be added. The length is the length of the data, without the header. */ buffer_t *buffer_create_with_data(BYTE_ORDER_t byte_order, const void *data, const uint16_t length) { buffer_t *new_buffer = buffer_create(byte_order); if(!new_buffer) DIE_MEM(); buffer_add_bytes(new_buffer, data, length); return new_buffer; } /* Destroy the buffer and free resources. If this isn't used, memory will leak. */ void buffer_destroy(buffer_t *buffer) { if(!buffer->valid) DIE("Program attempted to use deleted buffer."); buffer->valid = FALSE; memset(buffer->data, 0, buffer->max_length); free(buffer->data); memset(buffer, 0, sizeof(buffer_t)); free(buffer); } uint16_t buffer_get_length(buffer_t *buffer) { return buffer->current_length; } uint16_t buffer_get_current_offset(buffer_t *buffer) { return buffer->position; } /* Add data to the end of the buffer */ buffer_t *buffer_add_int8(buffer_t *buffer, const uint8_t data) { if(!buffer->valid) DIE("Program attempted to use deleted buffer."); buffer_add_bytes(buffer, &data, 1); return buffer; } uint8_t *buffer_create_string(buffer_t *buffer, uint16_t *length) { uint8_t *ret; if(!buffer->valid) DIE("Program attempted to use deleted buffer."); ret = safe_malloc(buffer_get_length(buffer)); memcpy(ret, buffer->data, buffer_get_length(buffer)); if(length) *length = buffer_get_length(buffer); return ret; } uint8_t *buffer_create_string_and_destroy(buffer_t *buffer, uint16_t *length) { uint8_t *ret = buffer_create_string(buffer, length); buffer_destroy(buffer); return ret; } buffer_t *buffer_add_int16(buffer_t *buffer, const uint16_t data) { uint16_t converted; if(!buffer->valid) DIE("Program attempted to use deleted buffer."); switch(buffer->byte_order) { case BO_NETWORK: converted = host_to_network_16(data); break; case BO_HOST: converted = host_to_host_16(data); break; case BO_LITTLE_ENDIAN: converted = host_to_little_endian_16(data); break; case BO_BIG_ENDIAN: converted = host_to_big_endian_16(data); break; } buffer_add_bytes(buffer, &converted, 2); return buffer; } buffer_t *buffer_add_int32(buffer_t *buffer, const uint32_t data) { uint32_t converted; if(!buffer->valid) DIE("Program attempted to use deleted buffer."); switch(buffer->byte_order) { case BO_NETWORK: converted = host_to_network_32(data); break; case BO_HOST: converted = host_to_host_32(data); break; case BO_LITTLE_ENDIAN: converted = host_to_little_endian_32(data); break; case BO_BIG_ENDIAN: converted = host_to_big_endian_32(data); break; } buffer_add_bytes(buffer, &converted, 4); return buffer; } buffer_t *buffer_add_ntstring(buffer_t *buffer, const char *data) { if(!buffer->valid) DIE("Program attempted to use deleted buffer."); buffer_add_bytes(buffer, data, strlen(data) + 1); return buffer; } buffer_t *buffer_add_unicode(buffer_t *buffer, const char *data) { size_t i; if(!buffer->valid) DIE("Program attempted to use deleted buffer."); for(i = 0; i < (strlen(data) + 1); i++) buffer_add_int16(buffer, data[i]); return buffer; } buffer_t *buffer_add_bytes(buffer_t *buffer, const void *data, const uint16_t length) { if(!buffer->valid) DIE("Program attempted to use deleted buffer."); if(buffer->current_length + length < buffer->current_length) DIE("Overflow."); /* Resize the buffer, if necessary. */ if(buffer->current_length + length > buffer->max_length) { do { /* Check for overflow. */ if(buffer->max_length << 1 < buffer->max_length) DIE("Overflow."); /* Double the length. */ buffer->max_length = buffer->max_length << 1; } while(buffer->current_length + length > buffer->max_length); buffer->data = safe_realloc(buffer->data, buffer->max_length); } memcpy(buffer->data + buffer->current_length, data, length); buffer->current_length += length; return buffer; } buffer_t *buffer_add_buffer(buffer_t *buffer, const buffer_t *source) { if(!buffer->valid) DIE("Program attempted to use deleted buffer."); if(!source->valid) DIE("Program attempted to use deleted buffer."); buffer_add_bytes(buffer, source->data, source->current_length); return buffer; } uint8_t buffer_read_next_int8(buffer_t *buffer) { uint8_t ret = buffer_read_int8_at(buffer, buffer->position); buffer->position += 1; return ret; } uint16_t buffer_read_next_int16(buffer_t *buffer) { uint16_t ret = buffer_read_int16_at(buffer, buffer->position); buffer->position += 2; return ret; } uint32_t buffer_read_next_int32(buffer_t *buffer) { uint32_t ret = buffer_read_int32_at(buffer, buffer->position); buffer->position += 4; return ret; } char *buffer_read_next_ntstring(buffer_t *buffer, char *data_ret, uint16_t max_length) { buffer_read_ntstring_at(buffer, buffer->position, data_ret, max_length); buffer->position += strlen(data_ret) + 1; return data_ret; } char *buffer_read_next_unicode(buffer_t *buffer, char *data_ret, uint16_t max_length) { buffer_read_unicode_at(buffer, buffer->position, data_ret, max_length); buffer->position += (strlen(data_ret) + 1) * 2; return data_ret; } void *buffer_read_next_bytes(buffer_t *buffer, void *data, uint16_t length) { buffer_read_bytes_at(buffer, buffer->position, data, length); buffer->position += length; return data; } uint8_t buffer_peek_next_int8(buffer_t *buffer) { return buffer_read_int8_at(buffer, buffer->position); } uint16_t buffer_peek_next_int16(buffer_t *buffer) { return buffer_read_int16_at(buffer, buffer->position); } uint32_t buffer_peek_next_int32(buffer_t *buffer) { return buffer_read_int32_at(buffer, buffer->position); } char *buffer_peek_next_ntstring(buffer_t *buffer, char *data_ret, uint16_t max_length) { return buffer_read_ntstring_at(buffer, buffer->position, data_ret, max_length); } char *buffer_peek_next_unicode(buffer_t *buffer, char *data_ret, uint16_t max_length) { return buffer_read_unicode_at(buffer, buffer->position, data_ret, max_length); } void *buffer_peek_next_bytes(buffer_t *buffer, void *data, uint16_t length) { return buffer_read_bytes_at(buffer, buffer->position, data, length); } uint8_t buffer_read_int8_at(buffer_t *buffer, uint16_t offset) { uint8_t ret; if(!buffer->valid) DIE("Program attempted to use deleted buffer."); buffer_read_bytes_at(buffer, offset, &ret, 1); return ret; } uint16_t buffer_read_int16_at(buffer_t *buffer, uint16_t offset) { uint16_t ret; if(!buffer->valid) DIE("Program attempted to use deleted buffer."); buffer_read_bytes_at(buffer, offset, &ret, 2); switch(buffer->byte_order) { case BO_NETWORK: ret = network_to_host_16(ret); break; case BO_HOST: ret = host_to_host_16(ret); break; case BO_LITTLE_ENDIAN: ret = little_endian_to_host_16(ret); break; case BO_BIG_ENDIAN: ret = big_endian_to_host_16(ret); break; } return ret; } uint32_t buffer_read_int32_at(buffer_t *buffer, uint16_t offset) { uint32_t ret; if(!buffer->valid) DIE("Program attempted to use deleted buffer."); buffer_read_bytes_at(buffer, offset, &ret, 4); switch(buffer->byte_order) { case BO_NETWORK: ret = network_to_host_32(ret); break; case BO_HOST: ret = host_to_host_32(ret); break; case BO_LITTLE_ENDIAN: ret = little_endian_to_host_32(ret); break; case BO_BIG_ENDIAN: ret = big_endian_to_host_32(ret); break; } return ret; } char *buffer_read_ntstring_at(buffer_t *buffer, uint16_t offset, char *data_ret, uint16_t max_length) { uint16_t i = 0; uint8_t ch; if(!buffer->valid) DIE("Program attempted to use deleted buffer."); if(!buffer_can_read_ntstring_at(buffer, offset, max_length)) DIE("Program read off the end of the buffer."); do { ch = buffer->data[offset + i]; data_ret[i] = ch; i++; } while(ch && i < max_length); data_ret[i - 1] = 0; return data_ret; } char *buffer_read_unicode_at(buffer_t *buffer, uint16_t offset, char *data_ret, uint16_t max_length) { uint16_t i = 0; uint8_t ch; if(!buffer->valid) DIE("Program attempted to use deleted buffer."); if(!buffer_can_read_ntstring_at(buffer, offset, max_length)) DIE("Program read off the end of the buffer."); do { ch = (uint8_t)buffer_read_int16_at(buffer, offset + (i * 2)); data_ret[i] = ch; i++; } while(ch && i < max_length); data_ret[i - 1] = 0; return data_ret; } void *buffer_read_bytes_at(buffer_t *buffer, uint16_t offset, void *data, uint16_t length) { if(!buffer->valid) DIE("Program attempted to use deleted buffer."); if(!buffer_can_read_bytes_at(buffer, offset, length)) DIE("Program read off the end of the buffer."); memcpy(data, buffer->data + offset, length); return data; } /* These boolean functions check if there's enough bytes left in the buffer to remove * specified data. These should always be used on the server side to verify valid * packets */ BOOLEAN buffer_can_read_int8(buffer_t *buffer) { return buffer_can_read_bytes(buffer, 1); } BOOLEAN buffer_can_read_int16(buffer_t *buffer) { return buffer_can_read_bytes(buffer, 2); } BOOLEAN buffer_can_read_int32(buffer_t *buffer) { return buffer_can_read_bytes(buffer, 4); } BOOLEAN buffer_can_read_ntstring(buffer_t *buffer) { uint16_t i; if(!buffer->valid) DIE("Program attempted to use deleted buffer."); for(i = buffer->position; i < buffer->current_length; i++) if(buffer->data[i] == '\\0') return TRUE; return FALSE; } /* It's important for the logic in this function to closely match the logic in read_unicode_at(), which * is why I convert it to a uint8_t. */ BOOLEAN buffer_can_read_unicode(buffer_t *buffer) { uint16_t i; if(!buffer->valid) DIE("Program attempted to use deleted buffer."); for(i = buffer->position; i < buffer->current_length; i++) if(((uint8_t)buffer_read_int16_at(buffer, i)) == 0) return TRUE; return FALSE; } BOOLEAN buffer_can_read_bytes(buffer_t *buffer, uint16_t length) { if(!buffer->valid) DIE("Program attempted to use deleted buffer."); if(buffer->position + length < buffer->position) DIE("Overflow."); return buffer_can_read_bytes_at(buffer, buffer->position, length); } BOOLEAN buffer_can_read_int8_at(buffer_t *buffer, uint16_t offset) { return buffer_can_read_bytes_at(buffer, offset, 1); } BOOLEAN buffer_can_read_int16_at(buffer_t *buffer, uint16_t offset) { return buffer_can_read_bytes_at(buffer, offset, 2); } BOOLEAN buffer_can_read_int32_at(buffer_t *buffer, uint16_t offset) { return buffer_can_read_bytes_at(buffer, offset, 4); } BOOLEAN buffer_can_read_ntstring_at(buffer_t *buffer, uint16_t offset, uint16_t max_length) { uint16_t i; BOOLEAN good = TRUE; BOOLEAN done = FALSE; for(i = 0; i < max_length - 1 && !done && good; i++) { if(buffer->data[offset + i] == '\\0') done = TRUE; if(offset + i + 1 > buffer->current_length) good = FALSE; } return good; } BOOLEAN buffer_can_read_unicode_at(buffer_t *buffer, uint16_t offset, uint16_t max_length) { uint16_t i; BOOLEAN good = TRUE; BOOLEAN done = FALSE; for(i = 0; i < max_length - 1 && !done && good; i++) { if(!buffer_can_read_int16_at(buffer, offset + (i * 2))) good = FALSE; else if(((uint8_t)buffer_read_int16_at(buffer, offset + (i * 2))) == 0) done = TRUE; } return good; } BOOLEAN buffer_can_read_bytes_at(buffer_t *buffer, uint16_t offset, uint16_t length) { if(!buffer->valid) DIE("Program attempted to use deleted buffer."); if(offset + length < offset) DIE("Overflow"); return (offset + length - 1) < buffer->current_length; } static char get_character_from_byte(uint8_t byte) { if(byte < 0x20 || byte > 0x7F) return '.'; return byte; } /* Print out the buffer in a nice format */ void buffer_print(buffer_t *buffer){ buffer_print_pad(buffer, ""); } void buffer_print_pad(buffer_t *buffer, uint8_t *padding) { uint16_t length = buffer->current_length; uint16_t i, j; if(!buffer->valid) DIE("Program attempted to use deleted buffer."); printf("%sBuffer contents:", padding); for(i = 0; i < length; i++) { if(!(i % 16)) { if(i > 0) { printf(" "); for(j = 16; j > 0; j--) { printf("%c", get_character_from_byte(buffer->data[i - j])); } } if(i == buffer->position) printf("\\n%s%04X:<", padding, i); else printf("\\n%s%04X: ", padding, i); } if(i == buffer->position) printf("%02X>", buffer->data[i]); else if(i == buffer->position - 1) printf("%02X<", buffer->data[i]); else printf("%02X ", buffer->data[i]); } for(i = length % 16; i < 17; i++) printf(" "); for(i = length - (length % 16); i < length; i++) printf("%c", get_character_from_byte(buffer->data[i])); printf("\\n%sLength: 0x%02X (%d)\\n", padding, length, length); } /* Returns a pointer to the actual buffer */ uint8_t *buffer_get(buffer_t *buffer, uint16_t *length) { if(!buffer->valid) DIE("Program attempted to use deleted buffer."); *length = buffer->current_length; return buffer->data; }
  22. #ifndef MD5_H #define MD5_H //This code is based on [url]http://people.csail.mit.edu/rivest/Md5.c[/url] //Bet heavily edited by me to the point where it isnt recognisable. #include "stdint.h" #include "math.h" #ifndef _MD5_enum_ #define _MD5_enum_ enum{ md5_success = 0, md5_null, /* Null pointer parameter */ md5_input_too_long, /* input data too long, >= 0x10000000000000000*/ md5_state_error /* called Input after Digest */ }; #endif #define md5_hash_size 16 typedef struct md5_context{ uint32_t intermediate_hash[md5_hash_size / 4]; /* Message Digest */ uint32_t length_low; /* Message length in bits */ uint32_t length_high; /* Message length in bits */ int_least16_t message_block_index; /* Index into message block array */ uint8_t message_block[64]; /* 512-bit message blocks */ uint8_t computed; /* Is the digest computed? */ uint8_t corrupted; /* Is the message digest corrupted? */ } md5_context; int __stdcall md5_reset(md5_context *); int __stdcall md5_input(md5_context *, const uint8_t *, uint32_t); int __stdcall md5_digest(md5_context *, uint8_t *); int __stdcall md5_verify_data(uint8_t *, uint32_t, const uint8_t *); #endif #include "md5.h" void md5_process_message_block(md5_context *); #define md5_batoi(ba, i) \\ ((ba[i+3] << 24) | (ba[i+2] << 16) | (ba[i+1] << 8) | ba[i]) #define md5_rol(word, bits) \\ (((word) << (bits)) | ((word) >> (32-(bits)))) #define md5_itoba(a, ba, i) \\ (ba[i+3] = (uint8_t)(a >> 24)); (ba[i+2] = (uint8_t)(a >> 16)); (ba[i+1] = (uint8_t)(a >> 8)); (ba[i] = (uint8_t)a); uint32_t md5_math(uint16_t t, uint32_t B, uint32_t C, uint32_t D){ if(t < 16) return (D ^ (B & (C ^ D))); else if(t < 32) return (C ^ (D & (B ^ C))); else if(t < 48) return (B ^ C ^ D); else return (C ^ (B | ~D)); } uint16_t md5_index(uint16_t t){ if(t < 16) return t; else if(t < 32) return (5 * t + 1) % 16; else if(t < 48) return (3 * t + 5) % 16; else return (7 * t) % 16; } uint16_t md5_shift(uint16_t t){ if(t < 16) return (((t % 4) + 1) * 5 + 2); else if(t < 32) return (t % 4 == 0 ? 5 : (t % 4 == 1 ? 9 : (t % 4 == 2 ? 14 : 20))); else if(t < 48) return (t % 4 == 0 ? 4 : (t % 4 == 1 ? 11 : (t % 4 == 2 ? 16 : 23))); else return (t % 4 == 0 ? 6 : (t % 4 == 1 ? 10 : (t % 4 == 2 ? 15 : 21))); } int __stdcall md5_reset(md5_context *ctx){ uint8_t x = 0; if(!ctx) return md5_null; ctx->length_low = 0; ctx->length_high = 0; ctx->computed = 0; ctx->corrupted = 0; ctx->message_block_index = 0; for(x = 0; x < 64; x++) ctx->message_block[x] = 0; ctx->intermediate_hash[0] = 0x67452301; ctx->intermediate_hash[1] = 0xEFCDAB89; ctx->intermediate_hash[2] = 0x98BADCFE; ctx->intermediate_hash[3] = 0x10325476; return md5_success; } int __stdcall md5_input(md5_context *ctx, const uint8_t *data, uint32_t length){ uint32_t x; if(!length) return md5_success; if(!ctx || !data) return md5_null; if(ctx->computed){ ctx->corrupted = md5_state_error; return md5_state_error; } for(x = 0; x < length; x++){ ctx->message_block[ctx->message_block_index++] = (data[x] & 0xFF); ctx->length_low += 8; if (ctx->length_low == 0){ ctx->length_high++; if(ctx->length_high == 0){ ctx->corrupted = md5_input_too_long; return md5_input_too_long; } } if(ctx->message_block_index == 64) md5_process_message_block(ctx); } return md5_success; } int __stdcall md5_digest(md5_context *ctx, uint8_t *digest){ int i; if (!ctx || !digest) return md5_null; if (ctx->corrupted) return ctx->corrupted; if (!ctx->computed){ if (ctx->message_block_index > 55){ ctx->message_block[ctx->message_block_index++] = 0x80; while(ctx->message_block_index < 64) ctx->message_block[ctx->message_block_index++] = 0; md5_process_message_block(ctx); }else{ ctx->message_block[ctx->message_block_index++] = 0x80; } while(ctx->message_block_index < 56) ctx->message_block[ctx->message_block_index++] = 0; md5_itoba(ctx->length_high, ctx->message_block, 60); md5_itoba(ctx->length_low, ctx->message_block, 56); md5_process_message_block(ctx); ctx->length_low = 0; ctx->length_high = 0; ctx->computed = 1; } for(i = 0; i < 4; i++){ md5_itoba(ctx->intermediate_hash[i], digest, i * 4); } return md5_success; } void md5_process_message_block(md5_context *ctx){ uint16_t t; uint32_t temp; uint32_t W[16]; uint32_t A, B, C, D; const uint32_t K[] = { /* K = floor(abs(sin(x+1) & (2 pow 32))) */ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 }; for(t = 0; t < 16; t++) W[t] = md5_batoi(ctx->message_block, t * 4); A = ctx->intermediate_hash[0]; B = ctx->intermediate_hash[1]; C = ctx->intermediate_hash[2]; D = ctx->intermediate_hash[3]; for(t = 0; t < 64; t++){ temp = B + md5_rol((A + md5_math(t, B, C, D) + W[md5_index(t)] + K[t]), md5_shift(t)); A = D; D = C; C = B; B = temp; } ctx->intermediate_hash[0] += A; ctx->intermediate_hash[1] += B; ctx->intermediate_hash[2] += C; ctx->intermediate_hash[3] += D; ctx->message_block_index = 0; } int __stdcall md5_verify_data(uint8_t *data, uint32_t length, const uint8_t *correct_md5){ md5_context ctx; uint8_t digest[16]; uint32_t x; md5_reset(&ctx); md5_input(&ctx, data, length); md5_digest(&ctx, digest); if(!correct_md5) return 0; for(x = 0; x < 16; x++){ if(digest[x] != correct_md5[x]) return 0; } return 1; } #ifndef DYNAMIC_CALLBACKS_H #define DYNAMIC_CALLBACKS_H #include <stdio.h> #include "types.h" #include "stdint.h" #include "string.h" #include "warden.h" #include "types.h" uint32_t __stdcall memalloc(uint32_t size); void __stdcall memfree(uint32_t address, uint32_t size); void __stdcall module_packet(uint32_t instance, uint32_t packet, uint32_t size); uint32_t __stdcall module_check (uint32_t instance, uint32_t module, uint32_t rc4_key); uint32_t __stdcall module_load (uint32_t instance, uint32_t rc4_key, uint32_t module, uint32_t size); uint32_t __stdcall module_malloc(uint32_t instance, uint32_t size); void __stdcall module_mfree (uint32_t instance, uint32_t address); uint32_t __stdcall module_setrc4(uint32_t instance, uint32_t ptr_keys, uint32_t size); uint32_t __stdcall module_getrc4(uint32_t instance, uint32_t ptr_keys, uint32_t *size); uint32_t get_callback_packet(uint32_t new_function, uint32_t instance); uint32_t get_callback_check (uint32_t new_function, uint32_t instance); uint32_t get_callback_load (uint32_t new_function, uint32_t instance); uint32_t get_callback_malloc(uint32_t new_function, uint32_t instance); uint32_t get_callback_mfree (uint32_t new_function, uint32_t instance); uint32_t get_callback_setrc4(uint32_t new_function, uint32_t instance); uint32_t get_callback_getrc4(uint32_t new_function, uint32_t instance); #endif #ifndef DYNAMIC_CALLBACKS_C #define DYNAMIC_CALLBACKS_C #include "dynamic_callbacks.h" uint32_t __stdcall memalloc(uint32_t size){ LPVOID x = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); VirtualLock(x, size); return (uint32_t)x; } void __stdcall memfree(uint32_t address, uint32_t size){ VirtualUnlock((uint8_t*)address, size); VirtualFree((uint8_t*)address, 0, MEM_RELEASE); } void __stdcall module_packet(uint32_t instance, uint32_t packet, uint32_t size){ /*warden_instance *ctx = (warden_instance*)instance; size += 4; send(ctx->socket_handle, "\\xff\\x5e", 2, 0); send(ctx->socket_handle, (uint8_t*)&size, 2, 0); send(ctx->socket_handle, (uint8_t*)packet, size - 4, 0);*/ } uint32_t __stdcall module_check(uint32_t instance, uint32_t module, uint32_t rc4_key) { return 1; } uint32_t __stdcall module_load(uint32_t instance, uint32_t rc4_key, uint32_t module, uint32_t size){ return 1; } uint32_t __stdcall module_malloc(uint32_t instance, uint32_t size) { return (uint32_t)safe_malloc(size); } void __stdcall module_mfree(uint32_t instance, uint32_t address) { free((uint8_t*)address); } uint32_t __stdcall module_setrc4(uint32_t instance, uint32_t ptr_keys, uint32_t size) { return 1; } uint32_t __stdcall module_getrc4(uint32_t instance, uint32_t ptr_keys, uint32_t *size){ return 1; } uint32_t get_callback_packet(uint32_t new_function, uint32_t instance){ uint32_t function_address = (uint32_t)module_packet; uint32_t function_call = new_function + 22; memcpy((uint8_t*)new_function, "\\xFF\\x74\\x24\\x08\\xFF\\x74\\x24\\x08\\x68\\x0B\\xAD\\xC0\\xDE\\xFF\\x15\\x0B\\xAD\\xC0\\xDE\\xC2\\x08\\x00\\x00\\x00\\x00\\x00", 26); memcpy((uint8_t*)(new_function+9), &instance, 4); memcpy((uint8_t*)(new_function+15), &function_call, 4); memcpy((uint8_t*)(new_function+22), &function_address, 4); return new_function; } uint32_t get_callback_check(uint32_t new_function, uint32_t instance){ uint32_t function_address = (uint32_t)module_check; uint32_t function_call = new_function + 22; memcpy((uint8_t*)new_function, "\\xFF\\x74\\x24\\x08\\xFF\\x74\\x24\\x08\\x68\\x0B\\xAD\\xC0\\xDE\\xFF\\x15\\x0B\\xAD\\xC0\\xDE\\xC2\\x08\\x00\\x00\\x00\\x00\\x00", 26); memcpy((uint8_t*)(new_function+9), &instance, 4); memcpy((uint8_t*)(new_function+15), &function_call, 4); memcpy((uint8_t*)(new_function+22), &function_address, 4); return new_function; } uint32_t get_callback_load(uint32_t new_function, uint32_t instance){ uint32_t function_address = (uint32_t)module_load; uint32_t function_call = new_function + 26; memcpy((uint8_t*)new_function, "\\xFF\\x74\\x24\\x0C\\xFF\\x74\\x24\\x0C\\xFF\\x74\\x24\\x0C\\x68\\x0B\\xAD\\xC0\\xDE\\xFF\\x15\\x0B\\xAD\\xC0\\xDE\\xC2\\x0C\\x00\\x00\\x00\\x00\\x00", 30); memcpy((uint8_t*)(new_function+13), &instance, 4); memcpy((uint8_t*)(new_function+19), &function_call, 4); memcpy((uint8_t*)(new_function+26), &function_address, 4); return new_function; } uint32_t get_callback_malloc(uint32_t new_function, uint32_t instance){ uint32_t function_address = (uint32_t)module_malloc; uint32_t function_call = new_function + 18; memcpy((uint8_t*)new_function, "\\xFF\\x74\\x24\\x04\\x68\\x0B\\xAD\\xC0\\xDE\\xFF\\x15\\x0B\\xAD\\xC0\\xDE\\xC2\\x04\\x00\\x00\\x00\\x00\\x00", 22); memcpy((uint8_t*)(new_function+5), &instance, 4); memcpy((uint8_t*)(new_function+11), &function_call, 4); memcpy((uint8_t*)(new_function+18), &function_address, 4); return new_function; } uint32_t get_callback_mfree(uint32_t new_function, uint32_t instance){ uint32_t function_address = (uint32_t)module_mfree; uint32_t function_call = new_function + 18; memcpy((uint8_t*)new_function, "\\xFF\\x74\\x24\\x04\\x68\\x0B\\xAD\\xC0\\xDE\\xFF\\x15\\x0B\\xAD\\xC0\\xDE\\xC2\\x04\\x00\\x00\\x00\\x00\\x00", 22); memcpy((uint8_t*)(new_function+5), &instance, 4); memcpy((uint8_t*)(new_function+11), &function_call, 4); memcpy((uint8_t*)(new_function+18), &function_address, 4); return new_function; } uint32_t get_callback_setrc4(uint32_t new_function, uint32_t instance){ uint32_t function_address = (uint32_t)module_setrc4; uint32_t function_call = new_function + 22; memcpy((uint8_t*)new_function, "\\xFF\\x74\\x24\\x08\\xFF\\x74\\x24\\x08\\x68\\x0B\\xAD\\xC0\\xDE\\xFF\\x15\\x0B\\xAD\\xC0\\xDE\\xC2\\x08\\x00\\x00\\x00\\x00\\x00", 26); memcpy((uint8_t*)(new_function+9), &instance, 4); memcpy((uint8_t*)(new_function+15), &function_call, 4); memcpy((uint8_t*)(new_function+22), &function_address, 4); return new_function; } uint32_t get_callback_getrc4(uint32_t new_function, uint32_t instance){ uint32_t function_address = (uint32_t)module_getrc4; uint32_t function_call = new_function + 22; memcpy((uint8_t*)new_function, "\\xFF\\x74\\x24\\x08\\xFF\\x74\\x24\\x08\\x68\\x0B\\xAD\\xC0\\xDE\\xFF\\x15\\x0B\\xAD\\xC0\\xDE\\xC2\\x08\\x00\\x00\\x00\\x00\\x00", 26); memcpy((uint8_t*)(new_function+9), &instance, 4); memcpy((uint8_t*)(new_function+15), &function_call, 4); memcpy((uint8_t*)(new_function+22), &function_address, 4); return new_function; } #endif #pragma once #include <windows.h> #include <stdio.h> #include "types.h" uint8_t *read_ini(const uint8_t *file, uint8_t *header, uint8_t *key, uint8_t *defa); #include "config.h" uint8_t *read_ini(const uint8_t *file, uint8_t *header, uint8_t *key, uint8_t *defa){ uint8_t *path = safe_malloc(0x100); uint8_t *buff = safe_malloc(0x100); uint32_t ret; GetCurrentDirectory(0x100, path); sprintf_s(path, 0x100, "%s\\\\%s", path, file); ret = GetPrivateProfileStringA(header, key, defa, buff, 0x100, path); return (ret == 0 ? defa : buff); } #ifndef __PACKET_BUFFER_H__ #define __PACKET_BUFFER_H__ #pragma comment (lib, "Ws2_32.lib") #include <windows.h> #include "types.h" typedef enum { BO_HOST, BO_NETWORK, BO_LITTLE_ENDIAN, BO_BIG_ENDIAN } BYTE_ORDER_t; /* This struct shouldn't be accessed directly */ typedef struct { /* Byte order to use */ BYTE_ORDER_t byte_order; /* The current position in the string, used when reading it. */ uint16_t position; /* The maximum length of the buffer that "buffer" is pointing to. When * space in this runs out, it's expanded */ uint16_t max_length; /* The current length of the buffer. */ uint16_t current_length; /* The current buffer. Will always point to a string of length max_length */ uint8_t *data; /* Set to FALSE when the packet is destroyed, to make sure I don't accidentally * re-use it (again) */ BOOLEAN valid; } buffer_t; /* Create a new packet buffer */ buffer_t *buffer_create(BYTE_ORDER_t byte_order); /* Create a new packet buffer, with data. */ buffer_t *buffer_create_with_data(BYTE_ORDER_t byte_order, const void *data, const uint16_t length); /* Destroy the buffer and free resources. If this isn't used, memory will leak. */ void buffer_destroy(buffer_t *buffer); /* Get the length of the buffer. */ uint16_t buffer_get_length(buffer_t *buffer); /* Get the current location in the buffer. */ uint16_t buffer_get_current_offset(buffer_t *buffer); /* Return the contents of the buffer in a newly allocated string. Fill in the length, if a pointer * is given. Note that this allocates memory that has to be freed! */ uint8_t *buffer_create_string(buffer_t *buffer, uint16_t *length); /* Does the same thing as above, but also frees up the buffer (good for a function return). */ uint8_t *buffer_create_string_and_destroy(buffer_t *buffer, uint16_t *length); /* Add data to the end of the buffer */ buffer_t *buffer_add_int8(buffer_t *buffer, const uint8_t data); buffer_t *buffer_add_int16(buffer_t *buffer, const uint16_t data); buffer_t *buffer_add_int32(buffer_t *buffer, const uint32_t data); buffer_t *buffer_add_ntstring(buffer_t *buffer, const char *data); /* Note: UNICODE support is a hack -- it prints every second character as a NULL, but is otherwise ASCII. */ buffer_t *buffer_add_unicode(buffer_t *buffer, const char *data); buffer_t *buffer_add_bytes(buffer_t *buffer, const void *data, const uint16_t length); buffer_t *buffer_add_buffer(buffer_t *buffer, const buffer_t *source); /* Read the next data from the buffer. The first read will be at the beginning. * An assertion will fail and the program will end if read off * the end of the buffer; it's probably a good idea to verify that enough data can be removed * before actually attempting to remove it; otherwise, a DoS condition can occur */ uint8_t buffer_read_next_int8(buffer_t *buffer); uint16_t buffer_read_next_int16(buffer_t *buffer); uint32_t buffer_read_next_int32(buffer_t *buffer); char *buffer_read_next_ntstring(buffer_t *buffer, char *data_ret, uint16_t max_length); char *buffer_read_next_unicode(buffer_t *buffer, char *data_ret, uint16_t max_length); void *buffer_read_next_bytes(buffer_t *buffer, void *data, uint16_t length); /* Read the next data, without incrementing the current pointer. */ uint8_t buffer_peek_next_int8(buffer_t *buffer); uint16_t buffer_peek_next_int16(buffer_t *buffer); uint32_t buffer_peek_next_int32(buffer_t *buffer); char *buffer_peek_next_ntstring(buffer_t *buffer, char *data_ret, uint16_t max_length); char *buffer_peek_next_unicode(buffer_t *buffer, char *data_ret, uint16_t max_length); void *buffer_peek_next_bytes(buffer_t *buffer, void *data, uint16_t length); /* Read data at the specified location in the buffer (counting the first byte as 0). */ uint8_t buffer_read_int8_at(buffer_t *buffer, uint16_t offset); uint16_t buffer_read_int16_at(buffer_t *buffer, uint16_t offset); uint32_t buffer_read_int32_at(buffer_t *buffer, uint16_t offset); char *buffer_read_ntstring_at(buffer_t *buffer, uint16_t offset, char *data_ret, uint16_t max_length); char *buffer_read_unicode_at(buffer_t *buffer, uint16_t offset, char *data_ret, uint16_t max_length); void *buffer_read_bytes_at(buffer_t *buffer, uint16_t offset, void *data, uint16_t length); /* These boolean functions check if there are enough bytes left in the buffer to remove * specified data. These should always be used on the server side to verify valid * packets */ BOOLEAN buffer_can_read_int8(buffer_t *buffer); BOOLEAN buffer_can_read_int16(buffer_t *buffer); BOOLEAN buffer_can_read_int32(buffer_t *buffer); BOOLEAN buffer_can_read_ntstring(buffer_t *buffer); BOOLEAN buffer_can_read_unicode(buffer_t *buffer); BOOLEAN buffer_can_read_bytes(buffer_t *buffer, uint16_t length); /* These functions check if there are enough bytes in the buffer at the specified location. */ BOOLEAN buffer_can_read_int8_at(buffer_t *buffer, uint16_t offset); BOOLEAN buffer_can_read_int16_at(buffer_t *buffer, uint16_t offset); BOOLEAN buffer_can_read_int32_at(buffer_t *buffer, uint16_t offset); BOOLEAN buffer_can_read_ntstring_at(buffer_t *buffer, uint16_t offset, uint16_t max_length); BOOLEAN buffer_can_read_unicode_at(buffer_t *buffer, uint16_t offset, uint16_t max_length); BOOLEAN buffer_can_read_bytes_at(buffer_t *buffer, uint16_t offset, uint16_t length); /* Print out the buffer in a nice format */ void buffer_print(buffer_t *buffer); void buffer_print_pad(buffer_t *buffer, uint8_t *padding); /* Returns a pointer to the actual buffer (I don't recommend using this). */ uint8_t *buffer_get(buffer_t *buffer, uint16_t *length); #endif
  23. #pragma once #include "types.h" #include "stdint.h" #include "string.h" #include <windows.h> #include <stdio.h> #include "module.h" #include "dynamic_callbacks.h" #include "mediv_random.h" #include "rc4.h" #include "md5.h" #include "buffer.h" #include "config.h" #define WARDEN_SEND (0x00) #define WARDEN_RECV (0x01) #define WARDEN_BNCS (0x02) #define SID_AUTH_INFO (0x50) #define SID_AUTH_CHECK (0x51) #define SID_WARDEN (0x5E) #define MODULE_INFORMATION (0x00) #define MODULE_TRANSFER (0x01) #define CHEAT_CHECKS (0x02) #define NEW_CRYPT_KEYS (0x05) typedef enum{ WARDEN_SUCCESS = 0, //All Went Well, Don't handle the packet Internally WARDEN_TRANSFER_FAILED, //If the module transfer failed WARDEN_UNKNOWN_PROTOCOL, //Not used, will be when adding support for MCP/UDP WARDEN_UNKNOWN_SUBID, //Unknown Sub-ID [Not 0x00, 0x01, 0x02, or 0x05] WARDEN_RAW_FAILURE, //The module was not able to handle the packet itself WARDEN_PACKET_FAILURE, //Something went HORRIBLY wrong in warden_packet, should NEVER happen. WARDEN_INIT_FAILURE, //Calling Init() in the module failed WARDEN_LOAD_FILE_FAILURE, //Could not load module from file [Not to bad, prolly just dosen't exist] WARDEN_LOAD_MD5_FAILURE, //Failed MD5 checksum when loading module [Either Bad tranfer or HD file corrupt] WARDEN_LOAD_INVALID_SIGNATURE, //Module failed RSA verification WARDEN_LOAD_DECOMPRESS_FAILURE, //Module failed to decompress properly WARDEN_LOAD_PREP_FAILURE, //Module prepare failed, Usually if module is corrupt WARDEN_CHECK_UNKNOWN_COMMAND, //Unknown sub-command in CHEAT_CHECKS WARDEN_CHECK_TO_MANY_LIBS, //There were more then 4 libraries in a single 0x02 packet [this is eww yes, but I'll figure out a beter way later] WARDEN_MEM_UNKNOWN_PRODUCT, WARDEN_MEM_UNKNOWN_SEGMENT, //Could not read segment from ini file WARDEN_INVALID_INSTANCE, //If the instance passed to any api is invalid (0) WARDEN_MAX = 0xffffffff }; typedef struct{ uint32_t send_packet; uint32_t check_module; uint32_t load_module; uint32_t mem_alloc; uint32_t mem_free; uint32_t set_rc4; uint32_t get_rc4; }callbacks_t; typedef struct{ callbacks_t *c; }callbacksp_t; typedef struct{ callbacksp_t *callbacks; uint32_t product; uint8_t *seed; uint32_t seed_len; uint8_t *in_key; uint8_t *out_key; uint8_t *module_md5; uint8_t *module_seed; uint32_t module; uint32_t module_size; uint32_t module_position; uint32_t init_data; uint8_t PAGE_CHECK_A; uint8_t MEM_CHECK; uint8_t XOR_CODE; uint32_t session; uint32_t disconnect; uint32_t sendpacket; uint8_t last_opcode; }warden_instance; typedef struct module_info module_info_t; struct module_info{ uint8_t md5[0x10]; uint8_t key[0x10]; uint8_t xor; uint8_t mem_check; uint8_t pag_check; uint8_t *data; uint32_t size; module_info_t *next; }; typedef struct{ module_info_t *modules; }world_instance; void warden_init_rc4keys(warden_instance *ctx); void warden_send_packet(warden_instance *ctx, const uint8_t *data, uint16_t size); void warden_save_file(warden_instance *ctx, uint32_t file_step); uint32_t warden_load_module(warden_instance *ctx); uint32_t warden_mem_check(warden_instance *ctx, buffer_t *buffer, buffer_t *out_buffer, uint8_t *lib); uint32_t warden_page_check(warden_instance *ctx, buffer_t *buffer, buffer_t *out_buffer); uint32_t warden_module_information(warden_instance *ctx, buffer_t *buffer); uint32_t warden_module_transfer(warden_instance *ctx, buffer_t *buffer); uint32_t warden_cheat_check(warden_instance *ctx, buffer_t *buffer); uint32_t __stdcall warden_init(uint32_t world, uint32_t session, uint32_t sendpacket, uint32_t disconnect, uint8_t *seed, uint32_t seedlen); uint32_t __stdcall warden_data(uint32_t main_instance, uint32_t instance, uint32_t length, const uint8_t *data); uint32_t __stdcall warden_cleanup(uint32_t instance); uint32_t __stdcall warden_send_module_information(uint32_t instance); uint32_t warden_recv_module_information(world_instance *world, warden_instance *ctx, buffer_t *in); void warden_send_module_transfer(warden_instance *ctx); uint32_t warden_recv_module_transfer(world_instance *world, warden_instance *ctx, buffer_t *in);\\ void warden_send_cheat_check(world_instance *world, warden_instance *ctx); typedef void (*MangosSendPacket)(uint32_t session_ptr, uint8_t *data, uint32_t data_len); #include "warden.h" uint8_t *to_hex(uint8_t *data, uint32_t size, BOOLEAN spaces){ uint8_t *buff = safe_malloc(size * (spaces == TRUE ? 3 : 2)); uint32_t x = 0; for(x = 0; x < size; x++){ if(spaces == TRUE) sprintf_s((uint8_t*)(buff + (3 * x)), 4, "%02X ", data[x]); else sprintf_s((uint8_t*)(buff + (2 * x)), 4, "%02X", data[x]); } return buff; } uint8_t *load_file(uint8_t *path, uint32_t *size_buf){ FILE *fp; uint32_t result; uint8_t *data; uint32_t size; fopen_s(&fp, path, "rb"); if(fp != NULL){ fseek(fp, 0, SEEK_END); size = ftell(fp); rewind(fp); data = (uint8_t*)safe_malloc(size); result = fread(data, 1, size, fp); fclose(fp); if(result != size){ free(data); if(size_buf != NULL) *size_buf = 0; return NULL; }else{ if(size_buf != NULL) *size_buf = size; return data; } } if(size_buf != NULL) *size_buf = 0; return NULL; } uint32_t __stdcall warden_first_run(){ world_instance *ctx = safe_malloc(sizeof(world_instance)); uint8_t *path = (uint8_t*)safe_malloc(0x30); uint8_t *hex; uint8_t *tmp = "\\xF8\\xA4\\xCB\\x47\\xA7\\x0A\\xBD\\xC0\\xB0\\x03\\xEE\\x4C\\x0C\\xC6\\x44\\xCE"; ctx->modules = safe_malloc(sizeof(module_info_t)); memcpy(&ctx->modules->md5[0], tmp, 0x10); ctx->modules->xor = 0xB1; ctx->modules->mem_check = 0x1B; ctx->modules->pag_check = 0x87; hex = to_hex(ctx->modules->md5, 0x10, FALSE); sprintf_s(path, 0x30, "./warden/%s.bin", hex); ctx->modules->data = load_file(path, &ctx->modules->size); printf("Size: %d\\n", ctx->modules->size ); sprintf_s(path, 0x30, "./warden/%s.key", hex); tmp = load_file(path, 0); if(tmp != NULL) memcpy(&ctx->modules->key[0], tmp, 0x10); return (uint32_t)ctx; } uint32_t __stdcall warden_init(uint32_t world, uint32_t session, uint32_t sendpacket, uint32_t disconnect, uint8_t *seed, uint32_t seedlen){ world_instance *ctx = (world_instance*)world; warden_instance *instance = safe_malloc(sizeof(warden_instance)); uint32_t code_segment = memalloc(0x1c0); memset(instance, 0, sizeof(warden_instance)); instance->callbacks = safe_malloc(sizeof(callbacksp_t)); instance->callbacks->c = safe_malloc(sizeof(callbacks_t)); instance->session = session; instance->sendpacket = sendpacket; instance->disconnect = disconnect; instance->seed = safe_malloc(seedlen); instance->seed_len = seedlen; instance->module = memalloc(ctx->modules->size); instance->module_size = ctx->modules->size; instance->module_md5 = safe_malloc(0x10); instance->module_seed = safe_malloc(0x10); instance->MEM_CHECK = ctx->modules->mem_check; instance->PAGE_CHECK_A = ctx->modules->pag_check; instance->XOR_CODE = ctx->modules->xor; memcpy((uint8_t*)instance->module, ctx->modules->data, ctx->modules->size); memcpy(instance->module_md5, &ctx->modules->md5[0], 0x10); memcpy(instance->module_seed, &ctx->modules->key[0], 0x10); memcpy(instance->seed, seed, seedlen); warden_init_rc4keys(instance); instance->callbacks->c->send_packet = get_callback_packet(code_segment + 0x000, (uint32_t)instance); instance->callbacks->c->check_module = get_callback_check (code_segment + 0x040, (uint32_t)instance); instance->callbacks->c->load_module = get_callback_load (code_segment + 0x080, (uint32_t)instance); instance->callbacks->c->mem_alloc = get_callback_malloc(code_segment + 0x0C0, (uint32_t)instance); instance->callbacks->c->mem_free = get_callback_mfree (code_segment + 0x100, (uint32_t)instance); instance->callbacks->c->set_rc4 = get_callback_setrc4(code_segment + 0x140, (uint32_t)instance); instance->callbacks->c->get_rc4 = get_callback_getrc4(code_segment + 0x180, (uint32_t)instance); warden_send_module_information((uint32_t)instance); return (uint32_t)instance; } uint32_t __stdcall warden_send_module_information(uint32_t instance){ /********************************************** * MEDIV_MODULE_INFORMATION (0x00) * *--------------------------------------------* * S->C: * * (Byte[16]) MD5 Hash of Module * * (Byte[16]) Module Decryption Seed * * (DWORD) Module Length * * C->S: * * (Byte) Status * * 0 - Do not have module, Request Transfer* * 1 - Have Module and Loaded properly * **********************************************/ warden_instance *ctx = (warden_instance*)instance; buffer_t *buff = buffer_create(BO_LITTLE_ENDIAN); buffer_add_int8(buff, '\\x00'); buffer_add_bytes(buff, ctx->module_md5, 0x10); buffer_add_bytes(buff, ctx->module_seed, 0x10); buffer_add_int32(buff, ctx->module_size); warden_send_packet(ctx, buff->data, buff->current_length); buffer_destroy(buff); return 0; } uint32_t warden_recv_module_information(world_instance *world, warden_instance *ctx, buffer_t *in){ /********************************************** * MEDIV_MODULE_INFORMATION (0x00) * *--------------------------------------------* * S->C: * * (Byte[16]) MD5 Hash of Module * * (Byte[16]) Module Decryption Seed * * (DWORD) Module Length * * C->S: * * (Byte) Status * * 0 - Do not have module, Request Transfer* * 1 - Have Module and Loaded properly * **********************************************/ if(buffer_read_next_int8(in) == 1){ printf("They already have the module\\n"); warden_send_cheat_check(world, ctx); }else{ printf("They need the module!\\n"); warden_send_module_transfer(ctx); } return WARDEN_SUCCESS; } uint32_t __stdcall warden_data(uint32_t winstance, uint32_t instance, uint32_t length, const uint8_t *raw_data){ warden_instance *ctx = (warden_instance*)instance; world_instance *world = (world_instance*)winstance; buffer_t *buf = buffer_create(BO_LITTLE_ENDIAN); uint8_t *data = (uint8_t*)safe_malloc(length); uint32_t result = WARDEN_SUCCESS; memcpy(data, raw_data, length); rc4_crypt(ctx->in_key, data, length); buffer_add_bytes(buf, data, (uint16_t)length); if(ctx->last_opcode == MODULE_INFORMATION){ result = warden_recv_module_information(world, ctx, buf); }else if(ctx->last_opcode == MODULE_TRANSFER){ result = warden_recv_module_transfer(world, ctx, buf); }else{ switch(buffer_read_next_int8(buf)){ default: printf("Unknown Warden Packet:\\n"); buffer_print(buf); } } buffer_destroy(buf); free(data); return result; } void warden_send_module_transfer(warden_instance *ctx){ /********************************** * MEDIV_MODULE_TRANSFER (0x01) * *--------------------------------* * S->C: * * (Word) Payload Length * * (Void) Payload * * * * C->S: (When tranfer finishes) * * (Byte) Status * * - 0: Something went wrong * * - 1: Module loaded * **********************************/ uint32_t x = 0; buffer_t *buf = NULL; uint16_t size; printf("Sending module to client\\n"); for(x = 0; x < ctx->module_size; x += 0x100){ size = (uint16_t)(ctx->module_size - x < 0x100 ? ctx->module_size - x : 0x100); buf = buffer_create(BO_LITTLE_ENDIAN); buffer_add_int8(buf, MODULE_TRANSFER); buffer_add_int16(buf, size); buffer_add_bytes(buf, (uint8_t*)((uint32_t)ctx->module + x), size); warden_send_packet(ctx, buf->data, buf->current_length); buffer_destroy(buf); } printf("Module transfer complete\\n"); } uint32_t warden_recv_module_transfer(world_instance *world, warden_instance *ctx, buffer_t *in){ /********************************** * MEDIV_MODULE_TRANSFER (0x01) * *--------------------------------* * C->S: (When tranfer finishes) * * (Byte) Status * * - 0: Something went wrong * * - 1: Module loaded * **********************************/ if(buffer_read_next_int8(in) == 1){ printf("Module transfer successful!\\n"); return WARDEN_SUCCESS; }else{ printf("Module transfer failed!\\n"); return WARDEN_TRANSFER_FAILED; } } uint32_t __stdcall warden_cleanup(uint32_t instance){ warden_instance *ctx = (warden_instance*)instance; if(ctx == NULL) return WARDEN_INVALID_INSTANCE; if(ctx->in_key != NULL) free(ctx->in_key); if(ctx->out_key != NULL) free(ctx->out_key); if(ctx->seed != NULL) free(ctx->seed); if(ctx->module != 0 ) memfree(ctx->module, ctx->module_size); if(ctx->module_md5 != NULL) free(ctx->module_md5); if(ctx->module_seed != NULL) free(ctx->module_seed); if(ctx->callbacks){ memfree(ctx->callbacks->c->send_packet, 0x1000); free(ctx->callbacks); } free(ctx); return WARDEN_SUCCESS; } void warden_init_rc4keys(warden_instance *ctx){ mediv_random_context *rnd = safe_malloc(sizeof(mediv_random_context)); uint8_t *out_seed = safe_malloc(0x10); uint8_t *in_seed = safe_malloc(0x10); ctx->out_key = safe_malloc(0x102); ctx->in_key = safe_malloc(0x102); mediv_random_init(rnd, ctx->seed, ctx->seed_len); mediv_random_get_bytes(rnd, in_seed, 0x10); mediv_random_get_bytes(rnd, out_seed, 0x10); rc4_init(ctx->out_key, out_seed, 0x10); rc4_init(ctx->in_key, in_seed, 0x10); printf("\\n"); printf("Warden Seed: %s\\n", to_hex(ctx->seed, ctx->seed_len, FALSE)); printf("Warden Out Seed: %s\\n", to_hex(out_seed, 0x10, FALSE)); printf("Warden In Seed: %s\\n", to_hex(in_seed, 0x10, FALSE)); printf("Warden Crypt Keys Generated!\\n"); } uint8_t *to_string(uint8_t *data){ uint32_t length; uint8_t *buff; uint32_t x = 0; if(data == NULL) return NULL; length = strlen(data) / 2; buff = safe_malloc(length); for(x = 0; x < length; x++){ sscanf_s(data, "%02x", &buff[x], 1); data += 2; } return buff; } void warden_send_packet(warden_instance *ctx, const uint8_t *data, uint16_t size){ uint8_t *pdata = safe_malloc(size); memcpy(pdata, data, size); ctx->last_opcode = data[0]; rc4_crypt(ctx->out_key, pdata, (uint32_t)size); ((MangosSendPacket)ctx->sendpacket)(ctx->session, pdata, size); free(pdata); } uint32_t warden_load_module(warden_instance *ctx){ uint32_t size; uint32_t temp_module; if(md5_verify_data((uint8_t*)ctx->module, ctx->module_size, ctx->module_md5) == 1){ rc4_crypt_data((uint8_t*)ctx->module, ctx->module_size, ctx->module_seed, 0x10); if((*(uint32_t*)(ctx->module + ctx->module_size - 0x104)) == 'SIGN'){ size = *(uint32_t*)(ctx->module); temp_module = memalloc(size); if(uncompress((uint8_t*)temp_module, &size, (uint8_t*)(ctx->module + 4), ctx->module_size - 0x108) == 0){ memfree(ctx->module, ctx->module_size); ctx->module = temp_module; ctx->module_size = size; size = module_get_prep_size((uint8_t*)ctx->module); temp_module = module_prep((uint8_t*)ctx->module); if(temp_module != 0){ memfree(ctx->module, ctx->module_size); ctx->module = temp_module; ctx->module_size = size; return WARDEN_SUCCESS; }else{ return WARDEN_LOAD_PREP_FAILURE; } }else{ return WARDEN_LOAD_DECOMPRESS_FAILURE; } }else{ return WARDEN_LOAD_INVALID_SIGNATURE; } }else{ return WARDEN_LOAD_MD5_FAILURE; } } void warden_send_cheat_check(world_instance *world, warden_instance *ctx){ /************************************** * CHEAT_CHECKS (0x02) * *------------------------------------* * S->C: * * (PString[]) Libraries * * (Byte) Check Type * * -PageCheckA * * (DWORD) Seed * * (Byte[20]) SHA1 * * (DWORD) Address * * (Byte) Length * * -MemCheck * * (Byte) Library * * (DWORD) Address * * (Byte) Length * * (Byte) XOr Code * * * * C-S: * * (Word) Length of Payload * * (DWORD) Checksum of Payload * * -PageCheckA * * (Byte) Found * * - 0: Hash Was Not found * * - 1: Hash Was Found * * -MemCheck * * (Byte) Status * * - 1: Could not read segment * * - 0: Segment to follow * * If Status = 0 * * (Void) Memory * **************************************/ buffer_t *buf = buffer_create(BO_LITTLE_ENDIAN); buffer_add_int8(buf, CHEAT_CHECKS); buffer_add_int8(buf, 0); buffer_add_int8(buf, ctx->MEM_CHECK ^ ctx->XOR_CODE); buffer_add_int8(buf, 0); buffer_add_int32(buf, 0x00420541); buffer_add_int8(buf, 0x0a); buffer_add_int8(buf, ctx->XOR_CODE); warden_send_packet(ctx, buf->data, buf->current_length); buffer_destroy(buf); } #ifndef RC4_H #define RC4_H #include "stdint.h" void __stdcall rc4_init(uint8_t *key_buffer, uint8_t *base, uint32_t base_length); void __stdcall rc4_crypt(uint8_t *key, uint8_t *data, uint32_t length); void __stdcall rc4_crypt_data(uint8_t *data, uint32_t data_length, uint8_t *base, uint32_t base_length); #endif #ifndef RC4_C #define RC4_C #include "rc4.h" void SWAP(uint8_t *a, uint8_t *b){ uint8_t t; t = *a; *a = *b; *b = t; } void __stdcall rc4_init(uint8_t *key_buffer, uint8_t *base, uint32_t base_length){ uint8_t val = 0; uint32_t position = 0; uint32_t i; for(i = 0; i < 0x100; i++) key_buffer[i] = (uint8_t)i; key_buffer[0x100] = 0; key_buffer[0x101] = 0; for(i = 1; i <= 0x40; i++){ val += key_buffer[(i * 4) - 4] + base[position++ % base_length]; SWAP(&key_buffer[(i * 4) - 4], &key_buffer[val & 0x0FF]); val += key_buffer[(i * 4) - 3] + base[position++ % base_length]; SWAP(&key_buffer[(i * 4) - 3], &key_buffer[val & 0x0FF]); val += key_buffer[(i * 4) - 2] + base[position++ % base_length]; SWAP(&key_buffer[(i * 4) - 2], &key_buffer[val & 0x0FF]); val += key_buffer[(i * 4) - 1] + base[position++ % base_length]; SWAP(&key_buffer[(i * 4) - 1], &key_buffer[val & 0x0FF]); } } void __stdcall rc4_crypt(uint8_t *key, uint8_t *data, uint32_t length){ uint32_t i; for(i = 0; i < length; i++){ key[0x100]++; key[0x101] += key[key[0x100]]; SWAP(&key[key[0x101]], &key[key[0x100]]); data[i] ^= key[(key[key[0x101]] + key[key[0x100]]) & 0x0FF]; } } void __stdcall rc4_crypt_data(uint8_t *data, uint32_t data_length, uint8_t *base, uint32_t base_length){ uint8_t key[0x102]; rc4_init(key, base, base_length); rc4_crypt(key, data, data_length); } #endif
  24. I don't have time to work on this shit, nore do I care that much, I mainly worked on it cuz I saw a lot of people bitchen that they had cheaters ont here forums. Anyways, here you code, the code is ugly as shit but hey #ifndef MODULE_H #define MODULE_H #pragma comment (lib, "zdll.lib") #define ZLIB_DLL #include <zlib.h> #include "types.h" #include "stdint.h" #include "string.h" #include <windows.h> #include <stdio.h> #include "dynamic_callbacks.h" typedef struct{ uint32_t maped_size; /*00*/ uint32_t unknown1; /*04*/ uint32_t ref_table; /*08*/ uint32_t ref_count; /*0C*/ uint32_t init_addr; /*10*/ uint32_t unknown3; /*14*/ uint32_t unknown4; /*18*/ uint32_t lib_table; /*1C*/ uint32_t lib_count; /*20*/ uint32_t unknown5; /*24*/ }module_header, *pmodule_header; typedef struct{ uint32_t name_address; uint32_t function_table; }library_referance, *plibrary_referance; typedef struct{ uint32_t rc4_init; uint32_t unload_module; uint32_t handle_packet; }module_exports; typedef struct{ uint32_t send_packet; uint32_t check_module; uint32_t load_module; uint32_t mem_alloc; uint32_t mem_free; uint32_t set_rc4; uint32_t get_rc4; }module_callbacks; typedef struct{ module_callbacks *c; }module_callbacksp; typedef struct{ module_exports *exports; uint32_t unknown1[5]; module_callbacksp *callbacks; uint32_t unknown2; uint8_t out_key[0x102]; uint8_t in_key[0x102]; }module_init_return, *pmodule_init_return; typedef uint32_t (__fastcall *module_init_t) (uint32_t callbacks); typedef uint32_t (__fastcall *module_init_ran_data) (uint32_t *init_data, uint32_t _set_zero_, uint8_t *data, uint32_t size); typedef uint8_t* (__fastcall *module_handle_packet_t)(uint32_t *init_data, uint32_t _set_zero_, uint8_t *packet_data, uint32_t length, uint32_t *byte_handled); uint32_t __stdcall module_get_prep_size(uint8_t *data); uint32_t __stdcall module_prep(uint8_t *source); uint32_t __stdcall module_init(uint32_t module, uint32_t callbacks); void __stdcall module_init_rc4(uint8_t *init_data, uint8_t *data, uint32_t data_length); uint32_t __stdcall module_get_init_address(uint32_t module); uint32_t __stdcall module_handle_packet(uint8_t *init_data, uint8_t *data, uint32_t length); #endif #pragma once #include "module.h" #define module_get_int32(a, b) \\ (*(uint32_t*)(a + (uint32_t)b)) #define module_get_aint32(a, b) \\ (*(uint32_t*)(&a[b])) #define module_get_int16(a, b) \\ (*(uint16_t*)&a[(uint32_t)b]) #define module_swap_int16(a) \\ (((a & 0xFF00) >> 8) | ((a & 0xFF) << 8)) #define module_set_int32(a, b, c)\\ (*(uint32_t*)(a + b) = c) int __stdcall module_prep(uint8_t *source){ uint32_t src_location; uint32_t dest_location; uint16_t length = 0; uint8_t bskip = 0; uint16_t *refs; uint32_t x = 0; uint32_t y = 0; library_referance *libs; uint8_t *lib; HMODULE handle; uint32_t function; uint32_t func; //uint8_t *msgbuff; uint32_t dest; uint32_t max_size; module_header *header = (pmodule_header)source; max_size = module_get_prep_size(source); dest = memalloc(max_size); memset((uint8_t*)dest, 0, max_size); //msgbuff = safe_malloc(500); //sprintf_s(msgbuff, 500, "Warden Base: 0x%08X\\n", (uint32_t)dest); write_to_file(msgbuff); memcpy((uint8_t*)dest, source, sizeof(module_header)); src_location = sizeof(module_header) + (header->unknown5 * 12); dest_location = module_get_aint32(source, 40); //write_to_file("Copying code blocks to module\\n"); while(dest_location < header->maped_size){ length = module_get_int16(source, src_location); src_location += 2; if(!bskip){ memcpy((uint8_t*)(dest + dest_location), source + src_location, length); src_location += length; } bskip = !bskip; dest_location += length; } refs = (uint16_t*)(dest + header->ref_table); dest_location = 0; //sprintf_s(msgbuff, 500, "Adjusting %d references to global variables...\\n", (int)header->ref_count); write_to_file(msgbuff); for(x = 0; x < header->ref_count; x++){ dest_location += module_swap_int16(refs[x]); module_set_int32(dest, dest_location, (module_get_int32(dest, dest_location) % max_size) + dest); } //write_to_file("Updating API library referances...\\n"); libs = (plibrary_referance)(dest + header->lib_table); for(x = 0; x < header->lib_count; x++){ lib = (uint8_t*)(dest + libs[x].name_address); handle = LoadLibrary(lib); function = libs[x].function_table; while(module_get_int32(dest, function) != 0){ func = module_get_int32(dest, function); if((func & 0x7FFFFFFF) > max_size){ //sprintf_s(msgbuff, 500, "Attempted to read API from offset pass end of module: 0x%08X\\n", func); write_to_file(msgbuff); break; } if(func & 0x80000000){ y = (uint32_t)GetProcAddress(handle, (LPCSTR)(func & 0x7FFFFFFF)); }else{ y = (uint32_t)GetProcAddress(handle, (uint8_t*)(dest + func)); } module_set_int32(dest, function, y); function += 4; } } return dest; } int __stdcall module_get_init_address(uint32_t module){ uint32_t x = module_get_int32(module, 0x10); return module_get_int32(module, x); } int __stdcall module_init(uint32_t address, uint32_t callbacks){ module_init_t init = (module_init_t)address; return init(callbacks); } void __stdcall module_init_rc4(uint8_t *init_data, uint8_t *data, uint32_t data_length){ module_init_return* init = (module_init_return*)init_data; module_init_ran_data rc4_init = (module_init_ran_data)init->exports->rc4_init; rc4_init((uint32_t*)init_data, (uint32_t)0, data, data_length); } uint32_t __stdcall module_handle_packet(uint8_t *init_data, uint8_t *data, uint32_t length){ uint32_t handled = -1; module_init_return *init = (module_init_return*)init_data; module_handle_packet_t handle = (module_handle_packet_t)init->exports->handle_packet; handle((uint32_t*)init_data, (uint32_t)0, data, length, &handled); return handled; } int __stdcall module_get_prep_size(uint8_t *data){return ((pmodule_header)data)->maped_size; } #ifndef MEDIV_RANDOM_H #define MEDIV_RANDOM_H #include <windows.h> #include "types.h" #include "stdint.h" #include "sha1.h" typedef struct mediv_random_context{ uint32_t index; uint8_t data[0x14]; uint8_t source1[0x14]; uint8_t source2[0x14]; }mediv_random_context; void __stdcall mediv_random_init(mediv_random_context *ctx, uint8_t *seed, uint32_t length); void __stdcall mediv_random_get_bytes(mediv_random_context *ctx, uint8_t *buffer, uint32_t length); #endif #include "mediv_random.h" void mediv_random_update(mediv_random_context *ctx){ sha1_context sha; sha.version = SHA1; sha1_reset(&sha); sha1_input(&sha, ctx->source1, 0x14); sha1_input(&sha, ctx->data, 0x14); sha1_input(&sha, ctx->source2, 0x14); sha1_digest(&sha, ctx->data); } void __stdcall mediv_random_init(mediv_random_context *ctx, uint8_t *seed, uint32_t length){ uint32_t length1 = length >> 1; uint32_t length2 = length - length1; sha1_context sha; memset(ctx, 0, sizeof(mediv_random_context)); sha.version = SHA1; sha1_reset(&sha); sha1_input(&sha, seed, length1); sha1_digest(&sha, ctx->source1); sha1_reset(&sha); sha1_input(&sha, seed + length1, length2); sha1_digest(&sha, ctx->source2); mediv_random_update(ctx); } uint8_t mediv_random_get_byte(mediv_random_context *ctx){ uint32_t value = ctx->data[ctx->index++]; if(ctx->index >= 0x14){ mediv_random_update(ctx); ctx->index = 0; } return (uint8_t)(value & 0xFF); } void __stdcall mediv_random_get_bytes(mediv_random_context *ctx, uint8_t *buffer, uint32_t length){ uint32_t x; for(x = 0; x < length; x++) buffer[x] = mediv_random_get_byte(ctx); } /* * sha1.h * * Description: * This is the header file for code which implements the Secure * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published * April 17, 1995. * * Many of the variable names in this code, especially the * single character names, were used because those were the names * used in the publication. * * Please read the file sha1.c for more information. * * Notes: * I took this file from BNCSUtil's source, Why? Lazyness. I have modified it a bit but most of its from him. <3 */ #ifndef _SHA1_H_ #define _SHA1_H_ #include "types.h" #include "stdint.h" #ifndef _SHA_enum_ #define _SHA_enum_ enum{ shaSuccess = 0, shaNull, /* Null pointer parameter */ shaInputTooLong, /* input data too long */ shaStateError /* called Input after Result */ }; #endif #define sha1_hash_size 20 typedef enum{ SHA1, xSHA1, lSHA1, wSHA1, MAX = 0xffffffff }SHA1_t; /* * This structure will hold context information for the SHA-1 * hashing operation */ typedef struct sha1_context{ uint32_t intermediate_hash[sha1_hash_size / 4]; /* Message Digest */ uint32_t length_low; /* Message length in bits */ uint32_t length_high; /* Message length in bits */ int_least16_t message_block_index; /* Index into message block array */ uint8_t message_block[64]; /* 512-bit message blocks */ uint8_t computed; /* Is the digest computed? */ uint8_t corrupted; /* Is the message digest corrupted? */ SHA1_t version; /* What version of SHA1 is this? */ } sha1_context; /* Function Prototypes */ int __stdcall sha1_reset(sha1_context *); int __stdcall sha1_input(sha1_context *, const uint8_t *, uint32_t); int __stdcall sha1_digest(sha1_context *, uint8_t *); uint32_t __stdcall sha1_checksum(uint8_t *data, uint32_t length, uint32_t version); #endif /* * sha1.c * * Description: * This file implements the Secure Hashing Algorithm 1 as * defined in FIPS PUB 180-1 published April 17, 1995. * * The SHA-1, produces a 160-bit message digest for a given * data stream. It should take about 2**n steps to find a * message with the same digest as a given message and * 2**(n/2) to find any two messages with the same digest, * when n is the digest size in bits. Therefore, this * algorithm can serve as a means of providing a * "fingerprint" for a message. * * Portability Issues: * SHA-1 is defined in terms of 32-bit "words". This code * uses <stdint.h> (included via "sha1.h" to define 32 and 8 * bit unsigned integer types. If your C compiler does not * support 32 bit unsigned integers, this code is not * appropriate. * * Caveats: * SHA-1 is designed to work with messages less than 2^64 bits * long. Although SHA-1 allows a message digest to be generated * for messages of any number of bits less than 2^64, this * implementation only works with messages with a length that is * a multiple of the size of an 8-bit character. * * Notes: * I took this file from BNCSUtil's source, Why? Lazyness. I have modified it a bit but most of its from him. <3 */ #include "sha1.h" #define SHA1RoL(bits, word) \\ (((word) << (bits)) | ((word) >> (32-(bits)))) #define xSHA1RoL(word, bits) \\ (((word) << (bits)) | ((word) >> (32-(bits)))) #define SHA1batoi(ba, i) \\ ((ba[i] << 24) | (ba[i+1] << 16) | (ba[i+2] << 8) | ba[i+3]) #define xSHA1batoi(ba, i) \\ ((ba[i+3] << 24) | (ba[i+2] << 16) | (ba[i+1] << 8) | ba[i]) #define SHA1itoba(a, ba, i) \\ (ba[i] = (uint8_t)(a >> 24)); (ba[i+1] = (uint8_t)(a >> 16)); (ba[i+2] = (uint8_t)(a >> 8)); (ba[i+3] = (uint8_t)a); #define xSHA1itoba(a, ba, i) \\ (ba[i+3] = (uint8_t)(a >> 24)); (ba[i+2] = (uint8_t)(a >> 16)); (ba[i+1] = (uint8_t)(a >> 8)); (ba[i] = (uint8_t)a); /* Local Function Prototyptes */ void sha1_pad_message(sha1_context *); void sha1_process_message_block(sha1_context *); /* * SHA1Reset * * Description: * This function will initialize the SHA1Context in preparation * for computing a new SHA1 message digest. * * Parameters: * context: [in/out] * The context to reset. * * Returns: * sha Error Code. * */ int __stdcall sha1_reset(sha1_context *ctx){ uint8_t x; if (!ctx) return shaNull; ctx->length_low = 0; ctx->length_high = 0; ctx->message_block_index = 0; ctx->intermediate_hash[0] = 0x67452301; ctx->intermediate_hash[1] = 0xEFCDAB89; ctx->intermediate_hash[2] = 0x98BADCFE; ctx->intermediate_hash[3] = 0x10325476; ctx->intermediate_hash[4] = 0xC3D2E1F0; for(x = 0; x < 64; x++){ ctx->message_block[x] = 0; } ctx->computed = 0; ctx->corrupted = 0; return shaSuccess; } /* * SHA1Result * * Description: * This function will return the 160-bit message digest into the * Message_Digest array provided by the caller. * NOTE: The first octet of hash is stored in the 0th element, * the last octet of hash in the 19th element. * * Parameters: * context: [in/out] * The context to use to calculate the SHA-1 hash. * Message_Digest: [out] * Where the digest is returned. * * Returns: * sha Error Code. * */ int __stdcall sha1_digest(sha1_context *ctx, uint8_t *digest){ int i; if (!ctx || !digest) return shaNull; if (ctx->corrupted) return ctx->corrupted; if (!ctx->computed){ sha1_pad_message(ctx); ctx->length_low = 0; ctx->length_high = 0; ctx->computed = 1; } if(ctx->version != SHA1){ for(i = 0; i < 5; i++){ xSHA1itoba(ctx->intermediate_hash[i], digest, i * 4); } }else{ for(i = 0; i < 5; i++){ SHA1itoba(ctx->intermediate_hash[i], digest, i * 4); } } return shaSuccess; } /* * SHA1Input * * Description: * This function accepts an array of octets as the next portion * of the message. * * Parameters: * context: [in/out] * The SHA context to update * message_array: [in] * An array of characters representing the next portion of * the message. * length: [in] * The length of the message in message_array * * Returns: * sha Error Code. * */ int __stdcall sha1_input(sha1_context *ctx, const uint8_t *data, uint32_t length){ uint32_t x; if(!length) return shaSuccess; if(!ctx || !data) return shaNull; if(ctx->computed){ ctx->corrupted = shaStateError; return shaStateError; } for(x = 0; x < length; x++){ ctx->message_block[ctx->message_block_index++] = (data[x] & 0xFF); ctx->length_low += 8; if (ctx->length_low == 0){ ctx->length_high++; if(ctx->length_high == 0){ ctx->corrupted = shaInputTooLong; return shaInputTooLong; } } if (ctx->message_block_index == 64) sha1_process_message_block(ctx); } return shaSuccess; } /* * SHA1Math * * Description: * This is simply so I can have a clean way of * doing the Process in one loop insted of 4. */ uint32_t sha1_math(uint16_t t, uint32_t B, uint32_t C, uint32_t D){ if(t < 20) return ((B & C) | ((~B) & D)); else if(t < 40) return (B ^ C ^ D); else if(t < 60) return ((B & C) | (B & D) | (C & D)); else return (B ^ C ^ D); } /* * SHA1ProcessMessageBlock * * Description: * This function will process the next 512 bits of the message * stored in the Message_Block array. * * Parameters: * None. * * Returns: * Nothing. * * Comments: * Many of the variable names in this code, especially the * single character names, were used because those were the * names used in the publication. * * */ void sha1_process_message_block(sha1_context *ctx){ uint16_t t; /* Loop counter */ uint32_t temp; /* Temporary word value*/ uint32_t W[80]; /* Word sequence */ uint32_t A, B, C, D, E; /* Word buffers */ const uint32_t K[] = {0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6}; if(ctx->version == xSHA1){ for(t = 0; t < 16; t++) W[t] = xSHA1batoi(ctx->message_block, t * 4); for(t = 16; t < 80; t++) W[t] = xSHA1RoL(1, W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); }else if(ctx->version == lSHA1){ for(t = 0; t < 16; t++) W[t] = xSHA1batoi(ctx->message_block, t * 4); for(t = 16; t < 80; t++) W[t] = SHA1RoL(1, W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); }else{ for(t = 0; t < 16; t++) W[t] = SHA1batoi(ctx->message_block, t * 4); for(t = 16; t < 80; t++) W[t] = SHA1RoL(1, W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); } A = ctx->intermediate_hash[0]; B = ctx->intermediate_hash[1]; C = ctx->intermediate_hash[2]; D = ctx->intermediate_hash[3]; E = ctx->intermediate_hash[4]; for(t = 0; t < 80; t++){ temp = SHA1RoL(5,A) + sha1_math(t, B, C, D) + E + W[t] + K[t / 20]; E = D; D = C; C = SHA1RoL(30,B); B = A; A = temp; } ctx->intermediate_hash[0] += A; ctx->intermediate_hash[1] += B; ctx->intermediate_hash[2] += C; ctx->intermediate_hash[3] += D; ctx->intermediate_hash[4] += E; ctx->message_block_index = 0; } /* * SHA1PadMessage * * Description: * According to the standard, the message must be padded to an even * 512 bits. The first padding bit must be a '1'. The last 64 * bits represent the length of the original message. All bits in * between should be 0. This function will pad the message * according to those rules by filling the Message_Block array * accordingly. It will also call the ProcessMessageBlock function * provided appropriately. When it returns, it can be assumed that * the message digest has been computed. * * Parameters: * context: [in/out] * The context to pad * ProcessMessageBlock: [in] * The appropriate SHA*ProcessMessageBlock function * Returns: * Nothing. * */ void sha1_pad_message(sha1_context *ctx){ if(ctx->version == xSHA1){ while(ctx->message_block_index < 64) ctx->message_block[ctx->message_block_index++] = 0; }else{ if (ctx->message_block_index > 55){ ctx->message_block[ctx->message_block_index++] = 0x80; while(ctx->message_block_index < 64) ctx->message_block[ctx->message_block_index++] = 0; sha1_process_message_block(ctx); }else{ ctx->message_block[ctx->message_block_index++] = 0x80; } while(ctx->message_block_index < 56) ctx->message_block[ctx->message_block_index++] = 0; if(ctx->version == lSHA1){ xSHA1itoba(ctx->length_high, ctx->message_block, 60); xSHA1itoba(ctx->length_low, ctx->message_block, 56); }else{ SHA1itoba(ctx->length_high, ctx->message_block, 56); SHA1itoba(ctx->length_low, ctx->message_block, 60); } } sha1_process_message_block(ctx); } uint32_t __stdcall sha1_checksum(uint8_t *data, uint32_t length, uint32_t version){ uint8_t digest[20]; sha1_context ctx; ctx.version = version; sha1_reset(&ctx); sha1_input(&ctx, data, length); sha1_digest(&ctx, digest); return *((uint32_t*)&digest[0]) ^ *((uint32_t*)&digest[4]) ^ *((uint32_t*)&digest[8]) ^ *((uint32_t*)&digest[12]) ^ *((uint32_t*)&digest[16]); }
  25. That would probably help, I can patch something together to properly handle the modules. Don't like .net but to damn lazy to write my own logger. I know it uses a module specific MD5 function when generating the new keys and building the response, the best way I've figured to handle that is to actually load the module up and let it deal with 0x05 itself.If you could figure out where the data is stored in memory you could simply extract the new keys after you send out 0x04. When you receive the 1st 0x00, scan the game's memory for the RC4 state struts: byte[0x100] key word position Search for the out key directly followed by the in key. Once you find it, wait till after 0x04 is sent, then snag the values from those positions, and wala! you have the new keys! This would break if 0x05 actually used something in the game's memory to calculate the new keys. But doubt they will change it now. uint32_t warden_handle_raw(warden_instance *ctx, uint8_t *data, uint32_t length){ /****************************************************************************** *Raw, This will attempt to have the module itself handle the packet. * *Currently this is needed for 0x05 as it uses module specific MD5 functions. * ******************************************************************************/ uint32_t handled = 0; if(ctx->init_data == 0) return WARDEN_RAW_FAILURE; memcpy(&ctx->init_data.out_key, ctx->out_key, RC4_LENGTH); //Copy our current encryption key to the module, it is used in building 0x04 handled = module_handle_packet((uint8_t*)ctx->init_data, data, length); //Tell the module to handle the packet if(handled == length){ //Did it work? memcpy(ctx->out_key, &ctx->init_data.out_key, RC4_LENGTH); //Lets get the new keys memcpy(ctx->in_key, &ctx->init_data.in_key, RC4_LENGTH); return WARDEN_SUCCESS; }else{ return WARDEN_RAW_FAILURE; } } Once I get some (A LOT) of free time i'll poke around in the modules see if I can find a way to pragmatically extract the differences in the MD5 function. Would make my life a lot easier. And would make sniffing easier. Haven't had much free time to do anything with this latly, my laptop is on it's last leg -.- randomly shutting down for no decernable reason. Also, can't find my backup of my dll->mod converter i'll re-write it again eventually.
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. Privacy Policy Terms of Use