PDA

View Full Version : HPC Halo Hash Verifier [Halo PC/Halo CE]



urbanyoung
April 12th, 2012, 01:59 AM
So, I couldn't be fucked doing math today and so I took a look at the gamespy hash authentication system. Basically, the server sends the client a key which it uses (along with a random key it generates) to produce two md5 strings which are sent to the server. One string is the cd key hash (used for banning) and the other is used by gamespy to authenticate the first one, so you can't steal someone else's hash.

Anyway, I made a little program that checks if a hash is valid. It's not all that useful but maybe someone somewhere will find it helpful. I've included both the .exe and the source code. I got a warning from Chrome when I downloaded it, check the source if you want.

Download: attached

(http://www.mediafire.com/?qww48gwwgqn9w4g)main.cpp

#include <windows.h>#include <stdio.h>
#include <vector>
#include "Stream.h"
#include "resource.h"


#pragma comment(lib, "ws2_32.lib")


hostent * GetHost(const char * host);
INT_PTR CALLBACK Main_DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
std::vector<std::string> TokenizeString(const std::string& str, const std::string& delim);


hostent* host = 0;
sockaddr_in svr, local;
SOCKET s = 0;


int WINAPI WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in_opt LPSTR lpCmdLine, __in int nShowCmd )
{
HWND hWnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, Main_DlgProc);


MSG Msg = {0};
while(GetMessage(&Msg, 0, 0, 0) > 0)
{
// Process GUI messages
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}


return 0;
}


void transformData(LPBYTE stream, int len)
{
const char* secString = "gamespy";
int secLen = strlen(secString);
for (int i = 0, j = 0; i < len; i++)
{
if (!secString[j]) j = 0;
stream[i] ^= secString[j++];
}
}


INT_PTR CALLBACK Main_DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// Handle the message
switch(uMsg)
{
// Dialog initialize
case WM_INITDIALOG:
{
WSADATA wsaData = {0};


// Try to start the winsock library
DWORD dwError = WSAStartup(MAKEWORD(2, 2), &wsaData);


if(dwError != ERROR_SUCCESS)
{
MessageBox(0, "Cannot startup WINSOCK", "ERROR", MB_ICONERROR);
return 0;
}


host = GetHost("halor.master.gamespy.com");


if (!host)
{
MessageBox(0, "Cannot resolve halor.master.gamespy.com.", "ERROR", MB_ICONERROR);
return 0;
}


// Setup the connection properties
svr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
svr.sin_family = AF_INET;
svr.sin_port = htons(29910);


local.sin_family = AF_INET;
local.sin_addr.s_addr = 0;
local.sin_port = 0; // choose any


s = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
// bind to the local address
int r = bind(s, (sockaddr *)&local, sizeof(local));


if (r == SOCKET_ERROR)
{
MessageBox(0, "Cannot bind the local socket.", "ERROR", MB_ICONERROR);
return 0;
}


}break;


// Commands
case WM_COMMAND:
{
int button = LOWORD(wParam);
// This gets called when a button is pressed, the case is the button id.
switch (button)
{
case IDC_CHECK:
{
if (GetWindowTextLength(GetDlgItem(hWnd, IDC_HASH)) == 32)
{
char hash_to_check[33] = {0};
GetWindowText(GetDlgItem(hWnd, IDC_HASH), hash_to_check, sizeof(hash_to_check));


streamBuilder b;
b.AppendString("\\auth\\\\pid\\793"); // game info
b.AppendString("\\ch\\nxdqevx"); // server key (any random 7 letter str)
b.AppendString("\\resp\\");
b.AppendString(hash_to_check); // hash to verify
b.AppendString("12345678"); // client key
b.AppendString(hash_to_check); // should be the auth token but we can't possibly generate a valid one
b.AppendString("\\ip\\0"); // ip address as 32bit number, 0 works fine too
b.AppendString("\\skey\\12345"); // used to id response


LPBYTE stream = b.getStream();
transformData(stream, b.getStreamSize());

// send the pkt
int ret = sendto( s, (char*)stream, b.getStreamSize(), 0, (sockaddr *)&svr, sizeof(svr));


char buff[1024] = {0};
sockaddr_in from;
int len = sizeof(from);


int bytesReceived = recvfrom(s, (char*)&buff, sizeof(buff), 0, (sockaddr *)&from, &len);
transformData((LPBYTE)buff, bytesReceived);


std::vector<std::string> tokens = TokenizeString(buff, "\\");


if (tokens.size())
{
if (tokens[tokens.size()-1] == "Invalid CD Key")
MessageBox(hWnd, "The CD Key is invalid.", "Response Received", MB_ICONEXCLAMATION);
else if (tokens[tokens.size()-1] == "Invalid authentication")
MessageBox(hWnd, "The CD Key is valid!", "Valid CD Key", MB_ICONINFORMATION);
else
MessageBox(hWnd, "An unknown error occurred :(", "Response Received", MB_ICONERROR);


}
else
MessageBox(hWnd, "An unknown error occurred :(", "Response Received", MB_ICONERROR);


SetWindowText(GetDlgItem(hWnd, IDC_HASH), "");
}
else
MessageBox(hWnd, "The hash must be 32 characters long.", "Error", MB_ICONERROR);

} break;
case IDCANCEL:
{
PostQuitMessage(0);


} break;
}
} break;


//-------------------------------------------------------------------------


default:
{
return FALSE;
}
}


// Message handled
return TRUE;
}


// http://www.gamedev.net/community/forums/topic.asp?topic_id=381544#TokenizeString
std::vector<std::string> TokenizeString(const std::string& str, const std::string& delim)
{
using namespace std;
vector<string> tokens;
size_t p0 = 0, p1 = string::npos;
while(p0 != string::npos)
{
p1 = str.find_first_of(delim, p0);
if(p1 != p0)
{
string token = str.substr(p0, p1 - p0);
tokens.push_back(token);
}
p0 = str.find_first_not_of(delim, p1);
}
return tokens;
}


hostent * GetHost(const char * host)
{
if(inet_addr(host) == INADDR_NONE)
{
return gethostbyname(host);
}
else
{
unsigned long addr = 0;
addr = inet_addr(host);
return gethostbyaddr((char*)&addr, sizeof(addr), AF_INET);
}
}

Sean Aero
April 12th, 2012, 04:10 AM
maybe someone somewhere will find it helpful.
"Somewhere over the rainbow...."

Thanks for sharing, just of curiosity how did you come up with the idea to look at the hash verification system?

urbanyoung
April 12th, 2012, 05:49 AM
"Somewhere over the rainbow...."

Thanks for sharing, just of curiosity how did you come up with the idea to look at the hash verification system?

I always had an idea of "borrowing" someone's hash or making it seem as though they were in my server when they really weren't. I decided to finally look into it and confirm what I assumed; it isn't possible. Plus, the second packet sent from c->s is "mostly" just this gamespy stuff and because I want to make a clientless I should probably understand what it's doing. I want to make a clientless so I can fill my server easily without having to run 16 clients, which I need to do while testing reserved slots.

Kornman00
April 12th, 2012, 11:00 AM
http://developer.poweredbygamespy.com/user/register

Ryx
April 16th, 2012, 12:53 AM
lol I've actually been looking at faking hashes at CE recently, found the function that gets the key from the registry and enctrypts it so I modified that (probably a little too nop-happy). well this explains why it didn't work :(

however, it still seems possible to fake hashes if you could get the key the server sent, the rand number, and just generate an md5 in a codecave. since you have both md5 source strings? I know there's not a lot of room to jump to the codecave, but it seems possible.

e: invalid or deleted file :|

urbanyoung
April 16th, 2012, 08:57 PM
There's no way to "fake" a hash, however there is a bug in Halo's processing that gives the same effect. The way the hash checks go is like this:

1. The client generates a random number and creates an 8 digit hex string of it.
2. Creates a string of %s%d%s, cd key, random number % 0xff, server key
3. Get an MD5 hash of the cd key (this is used for banning)
4. Gets an MD5 hash of the cd key with the server key appended to it.
4. Builds the response which consists of cd key hash, client key string, hash of cd key + server key.

The server sends the response off to gamespy which verifies it. The way it verifies it is (probably) by means of a database lookup and then rebuilding the two hashes. If the CD key hash is invalid, it returns an invalid cd key message. If it is valid the second hash is checked, if it's valid gamespy returns ok, otherwise it returns an authentication error. If any error is received the server gives you the "Invalid CD Key" error.

You cannot fake the check unless you can generate a valid cd key (not cd key hash, the one it reads + encrypts from registry).

If you want to look at the routine that generates the response, search for "CD key challenge too long", it's the only function with that reference.

Sean Aero
April 17th, 2012, 01:05 AM
You cannot fake the check unless you can generate a valid cd key (not cd key hash, the one it reads + encrypts from registry).

Let's not go down this road :cop:

urbanyoung
April 17th, 2012, 05:36 AM
Let's not go down this road :cop:

Well I don't really think there's a road to go down, not that I'm aware of anyway.

Ryx
April 17th, 2012, 10:55 AM
Let's not go down this road :cop:
By what, bruteforcing every cd combination? At that point you don't even need to fake hash.

Amit
April 17th, 2012, 02:12 PM
What good will come of this discussion?

urbanyoung
April 17th, 2012, 05:45 PM
What good will come of this discussion?

What harm will come from it? Halo has a good system for hash validation, you can't just conjure up a cd key and make it work.

Amit
April 17th, 2012, 06:12 PM
What harm will come from it? Halo has a good system for hash validation, you can't just conjure up a cd key and make it work.

I don't think you understood my question. I was asking what the point of the discussion is if it's just a dead end convo.

urbanyoung
April 17th, 2012, 06:20 PM
I don't think you understood my question. I was asking what the point of the discussion is if it's just a dead end convo.

Oh my bad, in that case I agree with you

Ryx
April 17th, 2012, 06:23 PM
I don't think you understood my question. I was asking what the point of the discussion is if it's just a dead end convo.
modacity -> dead convos? please.

think of it as a learning opportunity. at least we weren't not fighting like every other thread on the forum.

Amit
April 17th, 2012, 10:14 PM
What exactly are we learning? Well, I guess it's a rather effective system that is used by Gamespy, but unless we were to implement it into a multiplayer system, there's no real purpose to it.

Ryx
April 18th, 2012, 01:34 AM
What exactly are we learning? Well, I guess it's a rather effective system that is used by Gamespy, but unless we were to implement it into a multiplayer system, there's no real purpose to it.
I see what you did there :3

urbanyoung
April 18th, 2012, 01:48 AM
What exactly are we learning? Well, I guess it's a rather effective system that is used by Gamespy, but unless we were to implement it into a multiplayer system, there's no real purpose to it.

knowledge is power

Amit
April 19th, 2012, 08:57 PM
Knowledge is power if you know how to use it ;)

Patrickssj6
April 20th, 2012, 05:23 AM
What exactly are we learning? Well, I guess it's a rather effective system that is used by Gamespy, but unless we were to implement it into a multiplayer system, there's no real purpose to it.

It's interesting...knowing how a car engine works doesn't mean you will build a tank :P

Amit
April 20th, 2012, 08:29 PM
I thought you were going to say "knowing how a car engine works doesn't mean you will build a car." lol

Patrickssj6
April 24th, 2012, 10:02 AM
wanted something more destructive xD

jakallan3
September 26th, 2012, 05:31 PM
The download link doesn't work. Can someone provide another!?

Ryx
September 27th, 2012, 02:15 AM
Source is included in OP. Just add this:
At the top:

#define IDC_HASH 666
#define IDC_CHECK 667
#define IDD_DIALOG1 668
#define IDCANCEL 669 // is it already defined? can't rememer whatever idc

In the WinMain function:


HWND hWnd = CreateWindow(NULL, L"Halo Hash Verifier - Oxide", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, 300, 160, NULL, NULL, hInstance, NULL);

CreateWindowEx(NULL, L"button", L"Check Hash", WS_CHILD | WS_VISIBLE, 60, 60, 200, 20, hWnd, (HMENU)IDC_CHECK, hInstance, NULL);

CreateWindowEx(NULL, L"button", L"Check Hash", WS_CHILD | WS_VISIBLE, 60, 90, 200, 20, hWnd, (HMENU)IDCANCEL, hInstance, NULL);

CreateWindowEx(WS_EX_CLIENTEDGE, L"edit", L"", WS_TABSTOP|WS_VISIBLE|BS_FLAT|ES_CENTER|WS_CHILD, 60, 110, 200, 20, hWnd, (HMENU)IDC_HASH, GetModuleHandle(NULL), NULL);

Just guessing the positions they'd be at. Then just recompile it and you don't need a resource file.

jakallan3
September 28th, 2012, 06:59 PM
I don't know how to compile the program or whatever I need to do.

Btcc22
September 29th, 2012, 12:00 AM
I don't know how to compile the program or whatever I need to do.

Required files are missing anyhow and Ryx's fix needs fixed. ;)

Stream.h (http://phasor.proboards.com/index.cgi?board=newstuff&action=display&thread=361) for anybody that wants to get it working.

urbanyoung
September 29th, 2012, 12:46 AM
Reuploaded attached to first post

jakallan3
October 4th, 2012, 12:05 PM
Thanks! I appreciate it. I needed a way to see who's spoofing their hash and who's not.