tools/gdc-psyfork/gdc.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Sun May 27 11:39:50 2012 -0400 (2012-05-27)
changeset 3514 377c7da70d7c
parent 3513 50b5b98b3400
child 3574 b51285ca203f
permissions -rw-r--r--
Added example scripts missing from last commit (NPOTB).
     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <dlfcn.h>
     4 #include <fcntl.h>
     5 #include <math.h>
     6 #include "gdc.h"
     7 #include "GameConfigs.h"
     8 #include "MemoryUtils.h"
     9 
    10 MemoryUtils mu;
    11 
    12 char *game = NULL;
    13 char *engine = NULL;
    14 char *game_binary = NULL;
    15 char *engine_binary = NULL;
    16 char *wgame_binary = NULL;
    17 char *wengine_binary = NULL;
    18 
    19 int main(int argc, char **argv)
    20 {
    21 	char *gamedata = NULL;
    22 	bool debug = false;
    23 
    24 	opterr = 0;
    25 	int opt;
    26 	while ((opt = getopt(argc, argv, "b:de:f:g:x:w:y:")) != -1)
    27 		switch (opt)
    28 		{
    29 			case 'd':
    30 				debug = true;
    31 				break;
    32 			case 'g':
    33 				game = optarg;
    34 				break;
    35 
    36 			case 'e':
    37 				engine = optarg;
    38 				break;
    39 
    40 			case 'f':
    41 				gamedata = optarg;
    42 				break;
    43 
    44 			case 'b':
    45 				game_binary = optarg;
    46 				break;
    47 
    48 			case 'x':
    49 				engine_binary = optarg;
    50 				break;
    51 
    52 			case 'w':
    53 				wgame_binary = optarg;
    54 				break;
    55 
    56 			case 'y':
    57 				wengine_binary = optarg;
    58 				break;
    59 
    60 			case '?':
    61 				printf("Usage: ./gdc -g <game> -e <engine name> -f <gamedata file> -b <game binary> -x <engine binary> -w <win game binary> -y <win engine binary>\n");
    62 				return 0;
    63 
    64 			default:
    65 				printf("WHAT\n");
    66 				return 0;
    67 		}
    68 
    69 	if (!game || !engine || !gamedata || !game_binary || !engine_binary || !wgame_binary || !wengine_binary)
    70 	{
    71 		printf("Usage: ./gdc -g <game> -e <engine name> -f <gamedata file> -b <game binary> -x <engine binary> -w <win game binary> -y <win engine binary>\n");
    72 		return 0;
    73 	}
    74 
    75 	printf("Game: %s\n", game);
    76 	if (debug)
    77 	{
    78 		printf("Engine: %s\nGame binary: %s\nEngine binary: %s\nWin game binary: %s\nWin engine binary: %s\n",
    79 			engine, game_binary, engine_binary, wgame_binary, wengine_binary
    80 			);
    81 	}
    82 	printf("Gamedata: %s\n\n", gamedata);
    83 
    84 	void *ghandle;
    85 	ghandle = dlopen(game_binary, RTLD_LAZY);
    86 	if (!ghandle)
    87 	{
    88 		printf("Couldn't open %s (%s)\n", game_binary, dlerror());
    89 			return 0;
    90 	}
    91 	void *ehandle;
    92 	ehandle = dlopen(engine_binary, RTLD_LAZY);
    93 	if (!ehandle)
    94 	{
    95 		printf("Couldn't open %s (%s)\n", engine_binary, dlerror());
    96 			return 0;
    97 	}
    98 
    99 	int wgfile = open(wgame_binary, O_RDONLY);
   100 	if (wgfile == -1)
   101 	{
   102 		printf("Couldn't open %s\n", wgame_binary);
   103 		return 0;
   104 	}
   105 
   106 	int wefile = open(wengine_binary, O_RDONLY);
   107 	if (wefile == -1)
   108 	{
   109 		printf("Couldn't open %s\n", wengine_binary);
   110 		return 0;
   111 	}
   112 
   113 	CGameConfig gc;
   114 	char err[512];
   115 	if (!gc.EnterFile(gamedata, err, sizeof(err)))
   116 	{
   117 		printf("%s: %s\n", gamedata, err);
   118 		return 0;
   119 	}
   120 
   121 	CGameConfig symbols;
   122 	if (!symbols.EnterFile("symbols.txt", err, sizeof(err)))
   123 	{
   124 		printf("symbols.txt: %s\n", err);
   125 		return 0;
   126 	}
   127 
   128 	const char *vtsym = symbols.GetKeyValue("vtsym");
   129 	if (!vtsym)
   130 	{
   131 		printf("Couldn't find vtsym\n");
   132 		return 0;
   133 	}
   134 
   135 	void **vt = (void**) mu.ResolveSymbol(ghandle, vtsym);
   136 	if (!vt)
   137 	{
   138 		printf("Couldn't find vtable %s\n", vtsym);
   139 		dlclose(ghandle);
   140 		dlclose(ehandle);
   141 		close(wgfile);
   142 		close(wefile);
   143 
   144 		return 0;
   145 	}
   146 
   147 
   148 	for (list<Offset>::iterator it = gc.m_Offsets.begin(); it != gc.m_Offsets.end(); it++)
   149 	{
   150 		if (debug)
   151 		{
   152 			printf("DEBUG %s - l: %d w: %d\n", it->name, it->lin, it->win);
   153 		}
   154 
   155 		const char *symbol = symbols.GetKeyValue(it->name);
   156 		if (symbol)
   157 		{
   158 			int newOffset;
   159 			char symvt[128];
   160 			strcpy(symvt, it->name);
   161 			strcat(symvt, "_vt");
   162 			const char *newvtsym = symbols.GetKeyValue(symvt);
   163 			if (newvtsym && newvtsym[0])
   164 			{
   165 				void **newvt = (void**) mu.ResolveSymbol(ghandle, newvtsym);
   166 			        if (!newvt)
   167 				{
   168 					printf("O: %-22s - can't find, skipping\n", symvt);
   169 					continue;
   170 				}
   171 
   172 				newOffset = findVFunc(ghandle, newvt, symbol);
   173 			}
   174 			else
   175 			{
   176 				newOffset = findVFunc(ghandle, vt, symbol);
   177 			}
   178 
   179 			if (newOffset == it->lin)
   180 			{
   181 				printf("O: %-22s - GOOD.    current [ w: %3d, l: %3d ].\n", it->name, it->win, it->lin);
   182 			}
   183 			else
   184 			{
   185 				printf("O: %-22s - CHANGED. old [ w: %3d, l: %3d ]. new [ w: %3d, l: %3d ].\n",
   186 					it->name,
   187 					it->win, it->lin,
   188 					newOffset - (it->lin - it->win), newOffset
   189 					);
   190 			}
   191 		}
   192 		else // !symbol
   193 		{
   194 			printf("O: %-22s - no Linux symbol, skipping\n", it->name);
   195 		}
   196 	}
   197 
   198 	printf("\nWindows offsets are (semi-)wild guesses!\n\n");
   199 
   200 	for (list<Sig>::iterator it = gc.m_Sigs.begin(); it != gc.m_Sigs.end(); it++)
   201 	{
   202 		if (debug)
   203 		{
   204 			printf("DEBUG %s - %s - l: %s\n", it->name, it->lib == Server ? "server" : "engine", it->lin);
   205 		}
   206 
   207 		const char *linSymbol = it->lin;
   208 		const char *winSymbol = it->win;
   209 		bool hasLinux   = (linSymbol && linSymbol[0]);
   210 		bool hasWindows = (winSymbol && winSymbol[0]);
   211 		if (!hasLinux && !hasWindows)
   212 		{
   213 			printf("S: %-22s - hasn't linux nor windows data, skipping\n", it->name);
   214 			continue;
   215 		}
   216 		
   217 		int  winFile;
   218 		void *linHandle;
   219 		if (it->lib == Server)
   220 		{
   221 			winFile   = wgfile;
   222 			linHandle = ghandle;
   223 		}
   224 		else if (it->lib == Engine)
   225 		{
   226 			winFile   = wefile;
   227 			linHandle = ehandle;
   228 		}
   229 		else
   230 		{
   231 			printf("S: %-22s - isn't from server nor engine, skipping\n", it->name);
   232 			continue;
   233 		}
   234 		
   235 		bool foundLinux   = (!hasLinux   || checkSigStringL(linHandle, linSymbol));
   236 		bool foundWindows = (!hasWindows || checkSigStringW(winFile,   winSymbol));
   237 			
   238 		// Prepare for ternery ABUSE
   239 		
   240 		if (foundLinux && foundWindows)
   241 		{
   242 			// too much clutter to print current data if it matches anyway, unlike with offsets
   243 			/*
   244 			printf("%-22s (%s) - GOOD. current:\n\t[w:%s]\n\t[l:%s]\n",
   245 				it->name,
   246 				(it->lib == Server) ? "server" : "engine",
   247 				winSymbol ? winSymbol : "",
   248 				linSymbol ? linSymbol : ""
   249 				);
   250 			*/
   251 			printf("S: %-22s (%s) - GOOD.\n",
   252 				it->name,
   253 				(it->lib == Server) ? "server" : "engine"
   254 				);
   255 		}
   256 		else
   257 		{
   258 			// extra \n at end is intentional to add buffer after possibly long sigs
   259 			printf("S: %-22s (%s) - w: %s - l: %s\n%s%s%s%s%s%s%s%s\n",
   260 					it->name,
   261 					(it->lib == Server) ? "server" : "engine",
   262 					hasWindows ? (foundWindows ? "GOOD" : "CHANGED") : "UNKNOWN",
   263 					hasLinux   ? (foundLinux   ? "GOOD" : "CHANGED") : "UNKNOWN",
   264 					
   265 					hasWindows ? "\tcurrent - w: " : "",
   266 					(hasWindows && foundWindows) ? "    (found) \"" : (hasWindows ? "(NOT found) \"" : ""),
   267 					(hasWindows && winSymbol)  ? winSymbol : "",
   268 					hasWindows ? "\"\n" : "",
   269 					
   270 					hasLinux ? "\tcurrent - l: " : "",
   271 					(hasLinux && foundLinux) ? "    (found) \"" : (hasLinux ? "(NOT found) \"" : ""),
   272 					(hasLinux && linSymbol)  ? linSymbol : "",
   273 					hasLinux ? "\"\n" : ""
   274 					);
   275 			
   276 		}
   277 		
   278 		//  j/k, no way that anyone could have been prepared for that
   279 	}
   280 
   281 	return 0;
   282 }
   283 
   284 bool checkSigStringW(int file, const char* symbol)
   285 {
   286 	void *addr = NULL;
   287 	bool isAt = (symbol[0] == '@');
   288 	// we can't support this on windows from here
   289 	if (isAt)
   290 		return false;
   291 	
   292 	unsigned char real_sig[511];
   293 	size_t real_bytes = UTIL_DecodeHexString(real_sig, sizeof(real_sig), symbol);
   294 
   295 	if (real_bytes >= 1)
   296 	{
   297 		addr = mu.FindPatternInFile(file, (char*)real_sig, real_bytes);
   298 	}
   299 
   300 	if (addr)
   301 		return true;
   302 	
   303 	return false;
   304 }
   305 
   306 bool checkSigStringL(void *handle, const char* symbol)
   307 {
   308 	void *addr = NULL;
   309 	bool isAt = (symbol[0] == '@');
   310 	
   311 	if (isAt)
   312 	{
   313 		addr = mu.ResolveSymbol(handle, symbol + 1);
   314 	}
   315 	else
   316 	{
   317 		unsigned char real_sig[511];
   318 		size_t real_bytes = UTIL_DecodeHexString(real_sig, sizeof(real_sig), symbol);
   319 
   320 		if (real_bytes >= 1)
   321 		{
   322 			addr = mu.FindPattern(handle, (char*)real_sig, real_bytes);
   323 		}
   324 	}
   325 
   326 	if (addr)
   327 		return true;
   328 	
   329 	return false;
   330 }
   331 
   332 /* 
   333    takes a mangled member function symbol and returns the position where the function name and parameters start
   334    01234567890123456789012345678
   335    _ZN9CTFPlayer12ForceRespawnEv
   336                 ^13            ^28
   337 */
   338 void findFuncPos(const char *sym, int &func, int &params)
   339 {
   340 	int i = 0;
   341 	while ((sym[i] < '0') || (sym[i] > '9')) i++;
   342 	int classLen = atoi(sym + i);
   343 	func = i + (int)ceil(log10(classLen)) + classLen;
   344 	int funcLen = atoi(sym + func);
   345 	params = func + (int)ceil(log10(funcLen)) + funcLen;
   346 }
   347 
   348 int findVFunc(void *handle, void **vt, const char *symbol)
   349 {
   350 	int offset = 1;
   351 	int funcPos, paramPos;
   352 	findFuncPos(symbol, funcPos, paramPos);
   353 
   354 
   355 
   356 	for (int i = 0; i < 1000; i++)
   357 	{
   358 		void *pFunc = vt[i];
   359 		const char *s;
   360 
   361 		if (!(s = mu.ResolveAddr(handle, pFunc)))
   362 			continue;
   363 
   364 		if ((i > 1) && (strncmp(s, "_ZTI", 4) == 0))
   365 			break;
   366 
   367 
   368 		int tempFuncPos, tempParamPos;
   369 		findFuncPos(s, tempFuncPos, tempParamPos);
   370 
   371 		if (strcmp(s + tempFuncPos, symbol + funcPos) == 0)
   372 		{
   373 			offset = i;
   374 			break;
   375 		}
   376 	}
   377 
   378 	return offset - 2;
   379 }
   380 
   381 unsigned int strncopy(char *dest, const char *src, size_t count)
   382 {
   383 	if (!count)
   384 	{
   385 		return 0;
   386 	}
   387 
   388 	char *start = dest;
   389 	while ((*src) && (--count))
   390 	{
   391 		*dest++ = *src++;
   392 	}
   393 	*dest = '\0';
   394 
   395 	return (dest - start);
   396 }
   397 
   398 size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
   399 {
   400 	va_list ap;
   401 	va_start(ap, fmt);
   402 	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
   403 	va_end(ap);
   404 
   405 	if (len >= maxlength)
   406 	{
   407 		buffer[maxlength - 1] = '\0';
   408 		return (maxlength - 1);
   409 	}
   410 	else
   411 	{
   412 		return len;
   413 	}
   414 }
   415 
   416 size_t UTIL_DecodeHexString(unsigned char *buffer, size_t maxlength, const char *hexstr)
   417 {
   418 	size_t written = 0;
   419 	size_t length = strlen(hexstr);
   420 
   421 	for (size_t i = 0; i < length; i++)
   422 	{
   423 		if (written >= maxlength)
   424 			break;
   425 		
   426 		buffer[written++] = hexstr[i];
   427 		if (hexstr[i] == '\\' && hexstr[i + 1] == 'x')
   428 		{
   429 			if (i + 3 >= length)
   430 				continue;
   431 			/* Get the hex part. */
   432 			char s_byte[3];
   433 			int r_byte;
   434 			s_byte[0] = hexstr[i + 2];
   435 			s_byte[1] = hexstr[i + 3];
   436 			s_byte[2] = '\0';
   437 			/* Read it as an integer */
   438 			sscanf(s_byte, "%x", &r_byte);
   439 			/* Save the value */
   440 			buffer[written - 1] = r_byte;
   441 			/* Adjust index */
   442 			i += 3;
   443 		}
   444 	}
   445 
   446 	return written;
   447 }
   448