I used generally the same method above for
BeetleMania.
Talking about obfuscation, don't forget Strings are stored literally, so you need to make sure you mask that, otherwise your higschore URL is clearly visible in the class file. BeetleMania used a normal GET request, here's how I hid the request strings:
Code:
data = new int[][] {
// GET /beetlemania/query.php?x=
{71,69,84,32,47,98,101,101,116,108,101,109,97,110,105,97,47,113,117,101,114,121,46,112,104,112,63,120,61},
// &y=
{38,121,61},
// &z=
{38,122,61},
// HTTP/1.1
{32,72,84,84,80,47,49,46,49},
// From: beetlemania@javaunlimited.net
{70,114,111,109,58,32,98,101,101,116,108,101,109,97,110,105,97,64,106,97,
118,97,117,110,108,105,109,105,116,101,100,46,110,101,116},
// Referer: BeetleMania
{82,101,102,101,114,101,114,58,32,66,101,101,116,108,101,77,97,110,105,97},
// User-Agent: BeetleMania
{85,115,101,114,45,65,103,101,110,116,58,32,66,101,101,116,108,101,77,97,110,105,97},
// Host: javaunlimited.net
{72,111,115,116,58,32,106,97,118,97,117,110,108,105,109,105,116,101,100,46,110,101,116},
// Connection: close
{67,111,110,110,101,99,116,105,111,110,58,32,99,108,111,115,101},
// http://javaunlimited.net/beetlemania/scores.php?txt=1
{104,116,116,112,58,47,47,106,97,118,97,117,110,108,105,109,105,116,101,100,46,110,101,
116,47,98,101,101,116,108,101,109,97,110,105,97,47,115,99,111,114,101,115,46,112,104,112,63,116,120,116,61,49
};
Just very simple ASCII encoding.. you can take that a step futher and use your own mapping
Obviously, for sending the score across the tubes, it needs to be encrypted. I wrote my own encryption.. not very advanced but hard enough to throw off most hackers.
See these encodings:
Code:
XlJm27@Ib1#HY1*FI1uE^1EAU1 = 14257
W:I41-HC1KGP2]E]1RDW1eCT2ZBf1hAN2 = 10337
WGI21lH_2BFC2-Dv2gB+1gA[2 = 11480
XWJr23}F|1PE}1FDl1OC^1nBH1 = 11838
W<IW1dHg2xF62mDY2-C@1kB^1GAD1 = 11488
WwIN2jGU1nFM1IE(1VCX1oB>1|AI1 = 14188
XiJ:25vH*1jGh1CFp1:Ei1aDm1vBP1 = 13050
W-IA1SHc1qGl2qF$2.EC1MDm2vCd2cAp1 = 10846
X`Jo26zIS1.Hy1TG,1-Eq1pDp1 = 13784
VqGQ3[F{1pE+3MDN2)Cm2)B02AA52 = 14250
So even if a hacker is sniffing packets, it would still be difficult to crack the encryption, because there isn't much consistency. In fact, this particular encyption strategy can generate different values for the same number.
Here's the code for sending it across the net to the php script:
Code:
Socket sock = new Socket("woogley.net",80);
PrintStream out = new PrintStream(sock.getOutputStream());
BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
String s = d(0)+scr+d(1)+name+d(2)+num+d(3)+"\r\n";
out.print(s);
out.print(d(4)+"\r\n");
out.print(d(5)+"\r\n");
out.print(d(6)+"\r\n");
out.print(d(7)+"\r\n");
out.print(d(8)+"\r\n\r\n");
That d() method is just decoding the ASCII values above to actual characters. This way none of the request (URLs, etc) is visible inside the class file.
So as you can see, the 'security' in these things is just giving hoops for hackers to jump. The idea is to wear the hacker out, because you can't truly secure this.
Even with all this obfuscation.. if a hacker is able to figure out which variable is holding the score, he can just change all variables to be "public" and then do stuff like:
Code:
BeetleMania game = new BeetleMania();
game.score = 250000;
Finding the variable isn't as hard as you think, either. All you need is a decent debugger. I suppose this is where the checksum thing comes in handy, but I personally don't know how to do that.
Before I go, here's the PHP script on the backend:
Code:
<?php
function decode($s) {
$x = 0;
$num = 0;
$pow = 0;
$mul = 0;
$base = 90-ord($s{$x++});
$tmp = "";
while ($x < strlen($s)) {
if (!is_digit($s{x})) {
$x++;
$pow = ord($s{$x})-65;
$x+=2;
$tmp = "";
while ($x < strlen($s) && is_digit($s{$x})) $tmp.=$s{$x++};
$mul = $tmp;
$num+=pow($base,$pow)*$mul;
}
}
return floor($num);
}
function is_digit($num) {
return ($num == '0' || $num == '1' || $num == '2' || $num == '3' || $num == '4' || $num == '5' || $num == '6' || $num == '7' || $num == '8' || $num == '9');
}
function is_name_valid($s) {
if (strlen($s) > 16) {
return false;
}
for ($j = 0;$j < strlen($s);$j++) {
$c = $s{$j};
$x = ord($c);
if (!(($x >= 65 && $x <= 90) || ($x >= 48 && $x <= 57) || $c === '_' || $c === '-' || $c === '.' || $c === ':')) {
return false;
}
}
return true;
}
if (!isset($_GET["x"]) || !isset($_GET["y"]) || !is_name_valid($_GET["y"]) || !isset($_GET["z"]) || (decode($_GET["x"]) != $_GET["z"]) || $_SERVER["HTTP_CONNECTION"] !== "close" || $_SERVER["HTTP_HOST"] !== "javaunlimited.net" || $_SERVER["HTTP_REFERER"] !== "BeetleMania" || $_SERVER["HTTP_USER_AGENT"] !== "BeetleMania") {
header("Location: http://javaunlimited.net/");
exit;
}
else {
$name = $_GET["y"];
while (strlen($name) < 16) $name.=" ";
$score = decode($_GET["x"]);
$link = @mysql_connect("localhost","username","password");
if (@mysql_select_db("beetlemania")) {
@mysql_query("INSERT INTO `scores` (`name`,`score`,`code`,`ip`) VALUES(\"{$name}\",{$score},\"{$_GET['x']}\",\"{$_SERVER['REMOTE_ADDR']}\")");
}
@mysql_close($link);
print $_SERVER["REMOTE_ADDR"];
}
?>
I left the decryption code in there purposefully, just in case you're curious as to how the ecnryption is working.. who knows, it may give you ideas
Also note the tiny bit of extra security added by checking HTTP headers to weed out people trying to use a browser to submit the score