PDA

View Full Version : CRC32 Checksum How to in Player Profile


cybernine186
05-07-2008, 06:31 PM
I have developed a tool for viewing/editing the player profile in the character_ table. The viewing and editing works for int values only (Which almost every value seems to be a numeric value) but I am having trouble trying to figure out how the CRC32 check works. So far from my research the CRC32's value runs off the sizeof value. I am using PHP and it has build in capabilities for CRC32 and sizeof. Can anyone help me or point me in the right direction of being able to recreate the checksum in the player profile in the database or is the checksum totally worthless to the EQ client?

Theeper
05-07-2008, 08:46 PM
Take a look at crc32.cpp. You'll need the data from "int32 CRC32Table[256]" at the top. Also the function "SetEQChecksum()" is passed a reference to the profile and it creates the checksum and then copies it into the profile.

I don't know if the checksum is actually used by the server, but it is set.

I wrote a C++ app a while back to display the profile using the profile struct from the server code. I started to make it write the profile back to the DB but was unsure of what type of problems it could create. I got lazy and never finished it.

I do know that some events cause the server to save the profile like a change in coin, zoning and inventory changes. Spells don't seem to cause the server to save the profile.

You'll need to unpack() the data with PHP unless you're just doing a string replace on the binary data. AndMetal wrote a nice PHP script to unpack the profile here (http://www.eqemulator.net/wiki/wikka.php?wakka=ConvertProfile).

cybernine186
05-07-2008, 10:56 PM
I am using substr_replace in php to just replace the data that is changed instead of repacking the entire blob. Just seems to make more since to me that way. So far everything in the editor works perfectly, all char info, money, features, colors, languages, skills, etc. Just mostly the basics no LDON stuff yet. But that still doesn't tell me what the CRC32 checksum uses for the actual checksum. It has to be part or all of the info in the profile but what. I ant a C++ expert so any help is appreciated.

AndMetal
05-08-2008, 12:29 AM
I'll give you a hint: don't worry about the checksum.

I tried messing with it a little bit, until I discovered there wasn't any real reason to. This is what I came up with:

From my PHP-based utility (http://customeqemu.cvs.sourceforge.net/customeqemu/customeqemu/includes/functions.php?view=markup#l_73):

73 /*
74 SetEQChecksum taken from crc32.cpp source code, translated to PHP
75
76 Original source:
77 void CRC32::SetEQChecksum(uchar* in_data, int32 in_length)
78 {
79 unsigned long data;
80 unsigned long check = 0xffffffff;
81
82 assert(in_length >= 4 && in_data);
83
84 for(int32 i=4; i<in_length; i++)
85 {
86 data = in_data[i];
87 data = data ^ (check);
88 data = data & 0x000000ff;
89 check = check >> 8;
90 data = CRC32Table[data];
91 check = check ^ data;
92 }
93
94 memcpy(in_data, (char*)&check, 4);
95 }
96 */
97 function SetEQChecksum($in_data) {
98 $in_length = strlen($in_data);
99 $check = 0xffffffff;
100 for ($i=4; $i<in_length; $i++) {
101 $data = $in_data[$i];
102 $data = $data XOR ($check);
103 $data = $data & 0x000000ff;
104 $check = $check >> 8;
105 $data = $CRC32Table[$data];
106 $check = $check XOR $data;
107 };
108 return $check;
109 };

Plus the CRC32Table (http://customeqemu.cvs.sourceforge.net/customeqemu/customeqemu/includes/constants.php?view=markup#l_380):

380 $CRC32Table = array(
381 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
382 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
383 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
384 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
385 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
386 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
387 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
388 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
389 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
390 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
391 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
392 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
393 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
394 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
395 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
396 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
397
398 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
399 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
400 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
401 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
402 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
403 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
404 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
405 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
406 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
407 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
408 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
409 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
410 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
411 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
412 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
413 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
414
415 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
416 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
417 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
418 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
419 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
420 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
421 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
422 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
423 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
424 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
425 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
426 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
427 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
428 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
429 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
430 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
431
432 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
433 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
434 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
435 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
436 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
437 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
438 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
439 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
440 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
441 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
442 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
443 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
444 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
445 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
446 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
447 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
448 );


Unfortunately, it just ANDs the entire thing to all binary 1's. I didn't really try anything else after that, but I'm sure it's pretty straightforward to figure out, it will just take some debugging.

Btw, if you're looking for a somewhat basic way to just look at the profile blob, I put together a script to break it down:

<html>

<head>

<?php

// Database Info
$db_user = "eqemu"; // Username
$db_pass = "password"; // Password
$db_host = "localhost"; // Hostname/IP Address
$db_name = "peq"; // Database Name
$db = mysql_connect($db_host,$db_user,$db_pass) or die(mysql_error());
mysql_select_db($db_name);
// include "../includes/constants.php";

// Default settings for output
$field = "profile";
$line_size = 16;
$limit = 20; // Max lines to display per page. 0 to show all
$offset = 0; // Usually won't change from 0, but can be used to set an offset starting point (in rows)

// Set values to defaults if not passed in $_GET
if (!isset($_GET[field]) || ($_GET[field] == "")) $_GET[field] = $field;
if (!isset($_GET[line_size]) || ($_GET[line_size] == "")) $_GET[line_size] = $line_size;
if (!isset($_GET[limit]) || ($_GET[limit] == "")) $_GET[limit] = $limit;
if (!isset($_GET[offset]) || ($_GET[offset] == "")) $_GET[offset] = $offset;

?>

<title></title>

<style type="text/css">
td
{
text-align: center;
/* width: 5px; */
}
</style>

</head>

<body>

<form name="NoID" method="GET" action="">Enter ID: <input type="text" name="id" size="2" value="<?php echo $_GET['id']?>"><button type="submit">Go</button>&nbsp;<button type="reset">Reset</button><br>
<input type="radio" name="field" value="profile"<?php if ($_GET[field] == "profile") echo " checked"?>>Profile</input> <input type="radio" name="field" value="extprofile"<?php if ($_GET[field] == "extprofile") echo " checked"?>>Extended Profile</input><br>
Characters per line: <input type="text" name="line_size" size="2" value="<?php echo $_GET[line_size]?>"><br>
Lines per page: <input type="text" name="limit" size="3" value="<?php echo $_GET[limit]?>"><br>
Start: <input type="text" name="offset" size="3" value="<?php echo $_GET[offset]?>"><br>
</form>

<?php

if ($_GET['id']) {

flush(); ob_flush();

$query = "SELECT " . $_GET[field] . " FROM character_ WHERE id='" . $_GET['id'] . "' ";
$result = mysql_query($query,$db);
$db_row = mysql_fetch_array($result);
$total_chars = strlen($db_row[$_GET[field]]);
$total_chars_len = strlen($total_chars);

?>

<table border="1">
<form method="GET" action="">
<caption align="top" style="text-align:left">
<a href="?id=<?php echo $_GET['id']?>&line_size=<?php echo $_GET[line_size]?>&limit=<?php echo $_GET[limit]?>&offset=<?php if ($_GET[offset] - ($_GET[line_size] * $_GET[limit]) >= 0) {echo $_GET[offset] - ($_GET[line_size] * $_GET[limit]);};?>">&lt;&lt;Prev</a>
<a href="?id=<?php echo $_GET['id']?>&line_size=<?php echo $_GET[line_size]?>&limit=<?php echo $_GET[limit]?>&offset=<?php echo $_GET[offset] + ($_GET[line_size] * $_GET[limit]);?>">Next&gt;&gt;</a>
</caption>
</form>
<tr><td align="center">Row</td><td colspan="<?php echo $_GET[line_size]?>">Val</td><td colspan="<?php echo $_GET[line_size]?>">Char</td></tr>
<?php

if ($_GET[limit] == 0) $_GET[limit] = ($total_chars / $_GET[line_size]) - 1;
for ($row = $_GET[offset]; $row <= ((($_GET[line_size] * $_GET[limit]) + $_GET[offset]) - 1) && $row <= $total_chars; $row += $_GET[line_size]) {

?>
<tr><td align="right"><?php echo str_pad($row,$total_chars_len,0,STR_PAD_LEFT)?></td><?php
//Output for HEX
for ($char = $row; $char <= ($row + ($_GET[line_size] - 1)); $char++) {
?>
<td title="<?php echo str_pad($char,$total_chars_len,0,STR_PAD_LEFT)?>"><?php echo strtoupper(str_pad(dechex(ord($db_row[$_GET[field]][$char])),2,0,STR_PAD_LEFT))?></td><?php
};

// Output for Characters
for ($char = $row; $char <= ($row + ($_GET[line_size] - 1)); $char++) {
?>
<td title="<?php echo str_pad($char,$total_chars_len,0,STR_PAD_LEFT)?>"><?php
$char_dec_val = ord($db_row[$_GET[field]][$char]);
echo "";
if (($char_dec_val >= 33) && ($char_dec_val <= 126)) {echo $db_row[$_GET[field]][$char];}
elseif ($char_dec_val == 32) {echo "&nbsp;";};
?></td><?php
};
?>
</tr>
<?php
flush(); ob_flush();
};
?>
</table>

<?php
};
?>

</body>

</html>


I'll eventually include it with the other stuff, but that's for another day.

Anyways, hope this points you in the right direction.