extensions/tf2/asm/asm.c
author Kyle Sanderson <kyle.leet@gmail.com>
Thu Jul 18 15:50:57 2013 -0400 (2013-07-18)
changeset 3951 c3f26df2183f
parent 3727 ce26029551e7
permissions -rw-r--r--
Removed -Wno-parentheses from posix build and fixed warnings (bug 5745, r=psychonic).
pred@2136
     1
#include "asm.h"
pred@2136
     2
pred@2600
     3
#ifndef WIN32
pred@2600
     4
#define _GNU_SOURCE
pred@2600
     5
#include <dlfcn.h>
pred@2600
     6
#include <string.h>
pred@2600
     7
pred@2600
     8
#define REG_EAX			0
pred@2600
     9
#define REG_ECX			1
pred@2600
    10
#define REG_EDX			2
pred@2600
    11
#define REG_EBX			3
pred@2600
    12
pred@2600
    13
#define IA32_MOV_REG_IMM		0xB8	// encoding is +r <imm32>
pred@2600
    14
#endif
pred@2600
    15
pred@2600
    16
extern void Msg( const char *, ... );
pred@2600
    17
pred@2600
    18
/**
pred@2600
    19
* Checks if a call to a fpic thunk has just been written into dest.
pred@2600
    20
* If found replaces it with a direct mov that sets the required register to the value of pc.
pred@2600
    21
*
pred@2600
    22
* @param dest		Destination buffer where a call opcode + addr (5 bytes) has just been written.
pred@2600
    23
* @param pc		The program counter value that needs to be set (usually the next address from the source).
pred@2600
    24
* @noreturn
pred@2600
    25
*/
pred@2600
    26
void check_thunks(unsigned char *dest, unsigned char *pc)
pred@2600
    27
{
pred@2600
    28
#if defined WIN32
pred@2600
    29
	return;
pred@2600
    30
#else
pred@2600
    31
	/* Step write address back 4 to the start of the function address */
pred@2600
    32
	unsigned char *writeaddr = dest - 4;
pred@2600
    33
	unsigned char *calloffset = *(unsigned char **)writeaddr;
pred@2600
    34
	unsigned char *calladdr = (unsigned char *)(dest + (unsigned int)calloffset);
pred@2600
    35
pred@2600
    36
	/* Lookup name of function being called */
pred@2600
    37
	if ((*calladdr == 0x8B) && (*(calladdr+2) == 0x24) && (*(calladdr+3) == 0xC3))
pred@2600
    38
	{
pred@2600
    39
		//a thunk maybe?
pred@2600
    40
		char movByte = IA32_MOV_REG_IMM;
pred@2600
    41
pred@2600
    42
		/* Calculate the correct mov opcode */
pred@2600
    43
		switch (*(calladdr+1))
pred@2600
    44
		{
pred@2600
    45
		case 0x04:
pred@2600
    46
			{
pred@2600
    47
				movByte += REG_EAX;
pred@2600
    48
				break;
pred@2600
    49
			}
pred@2600
    50
		case 0x1C:
pred@2600
    51
			{
pred@2600
    52
				movByte += REG_EBX;
pred@2600
    53
				break;
pred@2600
    54
			}
pred@2600
    55
		case 0x0C:
pred@2600
    56
			{
pred@2600
    57
				movByte += REG_ECX;
pred@2600
    58
				break;
pred@2600
    59
			}
pred@2600
    60
		case 0x14:
pred@2600
    61
			{
pred@2600
    62
				movByte += REG_EDX;
pred@2600
    63
				break;
pred@2600
    64
			}
pred@2600
    65
		default:
pred@2600
    66
			{
pred@2600
    67
				Msg("Unknown thunk: %c\n", *(calladdr+1));
pred@2600
    68
				break;
pred@2600
    69
			}
pred@2600
    70
		}
pred@2600
    71
pred@2600
    72
		/* Move our write address back one to where the call opcode was */
pred@2600
    73
		writeaddr--;
pred@2600
    74
pred@2600
    75
pred@2600
    76
		/* Write our mov */
pred@2600
    77
		*writeaddr = movByte;
pred@2600
    78
		writeaddr++;
pred@2600
    79
pred@2600
    80
		/* Write the value - The provided program counter value */
pred@2600
    81
		*(void **)writeaddr = (void *)pc;
pred@2600
    82
		writeaddr += 4;
pred@2600
    83
	}
pred@2600
    84
pred@2600
    85
	return;
pred@2600
    86
#endif
pred@2600
    87
}
pred@2600
    88
pred@2136
    89
//if dest is NULL, returns minimum number of bytes needed to be copied
pred@2136
    90
//if dest is not NULL, it will copy the bytes to dest as well as fix CALLs and JMPs
pred@2136
    91
//http://www.devmaster.net/forums/showthread.php?t=2311
pred@2136
    92
int copy_bytes(unsigned char *func, unsigned char* dest, int required_len) {
pred@2136
    93
	int bytecount = 0;
pred@2136
    94
pred@2136
    95
	while(bytecount < required_len && *func != 0xCC)
pred@2136
    96
	{
pred@2136
    97
		// prefixes F0h, F2h, F3h, 66h, 67h, D8h-DFh, 2Eh, 36h, 3Eh, 26h, 64h and 65h
pred@2136
    98
		int operandSize = 4;
pred@2136
    99
		int FPU = 0;
pred@2136
   100
		int twoByte = 0;
pred@2136
   101
		unsigned char opcode = 0x90;
pred@2136
   102
		unsigned char modRM = 0xFF;
pred@2136
   103
		while(*func == 0xF0 ||
pred@2136
   104
			  *func == 0xF2 ||
pred@2136
   105
			  *func == 0xF3 ||
pred@2136
   106
			 (*func & 0xFC) == 0x64 ||
pred@2136
   107
			 (*func & 0xF8) == 0xD8 ||
pred@2136
   108
			 (*func & 0x7E) == 0x62)
pred@2136
   109
		{
pred@2136
   110
			if(*func == 0x66)
pred@2136
   111
			{
pred@2136
   112
				operandSize = 2;
pred@2136
   113
			}
pred@2136
   114
			else if((*func & 0xF8) == 0xD8)
pred@2136
   115
			{
pred@2136
   116
				FPU = *func;
pred@2136
   117
				if (dest)
pred@2136
   118
						*dest++ = *func++;
pred@2136
   119
				else
pred@2136
   120
						func++;
pred@2136
   121
				bytecount++;
pred@2136
   122
				break;
pred@2136
   123
			}
pred@2136
   124
pred@2136
   125
			if (dest)
pred@2136
   126
				*dest++ = *func++;
pred@2136
   127
			else
pred@2136
   128
				func++;
pred@2136
   129
			bytecount++;
pred@2136
   130
		}
pred@2136
   131
pred@2136
   132
		// two-byte opcode byte
pred@2136
   133
		if(*func == 0x0F)
pred@2136
   134
		{
pred@2136
   135
			twoByte = 1;
pred@2136
   136
			if (dest)
pred@2136
   137
				*dest++ = *func++;
pred@2136
   138
			else
pred@2136
   139
				func++;
pred@2136
   140
			bytecount++;
pred@2136
   141
		}
pred@2136
   142
pred@2136
   143
		// opcode byte
pred@2136
   144
		opcode = *func++;
pred@2136
   145
		if (dest) *dest++ = opcode;
pred@2136
   146
		bytecount++;
pred@2136
   147
pred@2136
   148
		// mod R/M byte
pred@2136
   149
		modRM = 0xFF;
pred@2136
   150
		if(FPU)
pred@2136
   151
		{
pred@2136
   152
			if((opcode & 0xC0) != 0xC0)
pred@2136
   153
			{
pred@2136
   154
				modRM = opcode;
pred@2136
   155
			}
pred@2136
   156
		}
pred@2136
   157
		else if(!twoByte)
pred@2136
   158
		{
pred@2136
   159
			if((opcode & 0xC4) == 0x00 ||
kyle@3951
   160
			   ((opcode & 0xF4) == 0x60 && ((opcode & 0x0A) == 0x02 || (opcode & 0x09) == 0x09)) ||
pred@2136
   161
			   (opcode & 0xF0) == 0x80 ||
kyle@3951
   162
			   ((opcode & 0xF8) == 0xC0 && (opcode & 0x0E) != 0x02) ||
pred@2136
   163
			   (opcode & 0xFC) == 0xD0 ||
pred@2136
   164
			   (opcode & 0xF6) == 0xF6)
pred@2136
   165
			{
pred@2136
   166
				modRM = *func++;
pred@2136
   167
				if (dest) *dest++ = modRM;
pred@2136
   168
				bytecount++;
pred@2136
   169
			}
pred@2136
   170
		}
pred@2136
   171
		else
pred@2136
   172
		{
kyle@3951
   173
			if(((opcode & 0xF0) == 0x00 && (opcode & 0x0F) >= 0x04 && (opcode & 0x0D) != 0x0D) ||
pred@2136
   174
			   (opcode & 0xF0) == 0x30 ||
pred@2136
   175
			   opcode == 0x77 ||
pred@2136
   176
			   (opcode & 0xF0) == 0x80 ||
kyle@3951
   177
			   ((opcode & 0xF0) == 0xA0 && (opcode & 0x07) <= 0x02) ||
pred@2136
   178
			   (opcode & 0xF8) == 0xC8)
pred@2136
   179
			{
pred@2136
   180
				// No mod R/M byte
pred@2136
   181
			}
pred@2136
   182
			else
pred@2136
   183
			{
pred@2136
   184
				modRM = *func++;
pred@2136
   185
				if (dest) *dest++ = modRM;
pred@2136
   186
				bytecount++;
pred@2136
   187
			}
pred@2136
   188
		}
pred@2136
   189
pred@2136
   190
		// SIB
pred@2136
   191
		if((modRM & 0x07) == 0x04 &&
pred@2136
   192
		   (modRM & 0xC0) != 0xC0)
pred@2136
   193
		{
pred@2136
   194
			if (dest)
pred@2136
   195
				*dest++ = *func++;   //SIB
pred@2136
   196
			else
pred@2136
   197
				func++;
pred@2136
   198
			bytecount++;
pred@2136
   199
		}
pred@2136
   200
pred@2136
   201
		// mod R/M displacement
pred@2136
   202
pred@2136
   203
		// Dword displacement, no base
pred@2136
   204
	if((modRM & 0xC5) == 0x05) {
pred@2136
   205
		if (dest) {
pred@2136
   206
			*(unsigned int*)dest = *(unsigned int*)func;
pred@2136
   207
			dest += 4;
pred@2136
   208
		}
pred@2136
   209
		func += 4;
pred@2136
   210
		bytecount += 4;
pred@2136
   211
	}
pred@2136
   212
pred@2136
   213
		// Byte displacement
pred@2136
   214
	if((modRM & 0xC0) == 0x40) {
pred@2136
   215
		if (dest)
pred@2136
   216
			*dest++ = *func++;
pred@2136
   217
		else
pred@2136
   218
			func++;
pred@2136
   219
		bytecount++;
pred@2136
   220
	}
pred@2136
   221
pred@2136
   222
		// Dword displacement
pred@2136
   223
	if((modRM & 0xC0) == 0x80) {
pred@2136
   224
		if (dest) {
pred@2136
   225
			*(unsigned int*)dest = *(unsigned int*)func;
pred@2136
   226
			dest += 4;
pred@2136
   227
		}
pred@2136
   228
		func += 4;
pred@2136
   229
		bytecount += 4;
pred@2136
   230
	}
pred@2136
   231
pred@2136
   232
		// immediate
pred@2136
   233
		if(FPU)
pred@2136
   234
		{
pred@2136
   235
			// Can't have immediate operand
pred@2136
   236
		}
pred@2136
   237
		else if(!twoByte)
pred@2136
   238
		{
pred@2136
   239
			if((opcode & 0xC7) == 0x04 ||
pred@2136
   240
			   (opcode & 0xFE) == 0x6A ||   // PUSH/POP/IMUL
pred@2136
   241
			   (opcode & 0xF0) == 0x70 ||   // Jcc
pred@2136
   242
			   opcode == 0x80 ||
pred@2136
   243
			   opcode == 0x83 ||
pred@2136
   244
			   (opcode & 0xFD) == 0xA0 ||   // MOV
pred@2136
   245
			   opcode == 0xA8 ||			// TEST
pred@2136
   246
			   (opcode & 0xF8) == 0xB0 ||   // MOV
pred@2136
   247
			   (opcode & 0xFE) == 0xC0 ||   // RCL
pred@2136
   248
			   opcode == 0xC6 ||			// MOV
pred@2136
   249
			   opcode == 0xCD ||			// INT
pred@2136
   250
			   (opcode & 0xFE) == 0xD4 ||   // AAD/AAM
pred@2136
   251
			   (opcode & 0xF8) == 0xE0 ||   // LOOP/JCXZ
pred@2136
   252
			   opcode == 0xEB ||
kyle@3951
   253
			   (opcode == 0xF6 && (modRM & 0x30) == 0x00))   // TEST
pred@2136
   254
			{
pred@2136
   255
				if (dest)
pred@2136
   256
					*dest++ = *func++;
pred@2136
   257
				else
pred@2136
   258
					func++;
pred@2136
   259
				bytecount++;
pred@2136
   260
			}
pred@2136
   261
		else if((opcode & 0xF7) == 0xC2) // RET
pred@2136
   262
			{
pred@2136
   263
				if (dest) {
pred@2136
   264
					*(unsigned short*)dest = *(unsigned short*)func;
pred@2136
   265
					dest += 2;
pred@2136
   266
				}
pred@2136
   267
				func += 2;
pred@2136
   268
				bytecount += 2;
pred@2136
   269
			}
pred@2136
   270
			else if((opcode & 0xFC) == 0x80 ||
pred@2136
   271
					(opcode & 0xC7) == 0x05 ||
pred@2136
   272
					(opcode & 0xF8) == 0xB8 ||
pred@2136
   273
					(opcode & 0xFE) == 0xE8 ||	  // CALL/Jcc
pred@2136
   274
					(opcode & 0xFE) == 0x68 ||
pred@2136
   275
					(opcode & 0xFC) == 0xA0 ||
pred@2136
   276
					(opcode & 0xEE) == 0xA8 ||
pred@2136
   277
					opcode == 0xC7 ||
kyle@3951
   278
					(opcode == 0xF7 && (modRM & 0x30) == 0x00))
pred@2136
   279
			{
pred@2136
   280
				if (dest) {
pred@2136
   281
					//Fix CALL/JMP offset
pred@2136
   282
					if ((opcode & 0xFE) == 0xE8) {
pred@2136
   283
						if (operandSize == 4)
pred@2600
   284
						{
pred@2136
   285
							*(long*)dest = ((func + *(long*)func) - dest);
pred@2600
   286
pred@2600
   287
							//pRED* edit. func is the current address of the call address, +4 is the next instruction, so the value of $pc
pred@2600
   288
							check_thunks(dest+4, func+4);
pred@2600
   289
						}
pred@2136
   290
						else
pred@2136
   291
							*(short*)dest = ((func + *(short*)func) - dest);
pred@2600
   292
pred@2136
   293
					} else {
pred@2136
   294
						if (operandSize == 4)
pred@2136
   295
							*(unsigned long*)dest = *(unsigned long*)func;
pred@2136
   296
						else
pred@2136
   297
							*(unsigned short*)dest = *(unsigned short*)func;
pred@2136
   298
					}
pred@2136
   299
					dest += operandSize;
pred@2136
   300
				}
pred@2136
   301
				func += operandSize;
pred@2136
   302
				bytecount += operandSize;
pred@2136
   303
pred@2136
   304
			}
pred@2136
   305
		}
pred@2136
   306
		else
pred@2136
   307
		{
pred@2136
   308
			if(opcode == 0xBA ||			// BT
pred@2136
   309
			   opcode == 0x0F ||			// 3DNow!
pred@2136
   310
			   (opcode & 0xFC) == 0x70 ||   // PSLLW
pred@2136
   311
			   (opcode & 0xF7) == 0xA4 ||   // SHLD
pred@2136
   312
			   opcode == 0xC2 ||
pred@2136
   313
			   opcode == 0xC4 ||
pred@2136
   314
			   opcode == 0xC5 ||
pred@2136
   315
			   opcode == 0xC6)
pred@2136
   316
			{
pred@2136
   317
				if (dest)
pred@2136
   318
					*dest++ = *func++;
pred@2136
   319
				else
pred@2136
   320
					func++;
pred@2136
   321
			}
pred@2136
   322
			else if((opcode & 0xF0) == 0x80) // Jcc -i
pred@2136
   323
			{
pred@2136
   324
				if (dest) {
pred@2136
   325
					if (operandSize == 4)
pred@2136
   326
						*(unsigned long*)dest = *(unsigned long*)func;
pred@2136
   327
					else
pred@2136
   328
						*(unsigned short*)dest = *(unsigned short*)func;
pred@2136
   329
pred@2136
   330
					dest += operandSize;
pred@2136
   331
				}
pred@2136
   332
				func += operandSize;
pred@2136
   333
				bytecount += operandSize;
pred@2136
   334
			}
pred@2136
   335
		}
pred@2136
   336
	}
pred@2136
   337
pred@2136
   338
	return bytecount;
pred@2136
   339
}
pred@2136
   340
pred@2136
   341
//insert a specific JMP instruction at the given location
pred@2136
   342
void inject_jmp(void* src, void* dest) {
pred@2136
   343
	*(unsigned char*)src = OP_JMP;
pred@2136
   344
	*(long*)((unsigned char*)src+1) = (long)((unsigned char*)dest - ((unsigned char*)src + OP_JMP_SIZE));
pred@2136
   345
}
pred@2136
   346
pred@2136
   347
//fill a given block with NOPs
pred@2136
   348
void fill_nop(void* src, unsigned int len) {
pred@2136
   349
	unsigned char* src2 = (unsigned char*)src;
pred@2136
   350
	while (len) {
pred@2136
   351
		*src2++ = OP_NOP;
pred@2136
   352
		--len;
pred@2136
   353
	}
pred@2136
   354
}
pred@2136
   355
pred@2136
   356
void* eval_jump(void* src) {
pred@2136
   357
	unsigned char* addr = (unsigned char*)src;
pred@2136
   358
pred@2136
   359
	if (!addr) return 0;
pred@2136
   360
pred@2136
   361
	//import table jump
pred@2136
   362
	if (addr[0] == OP_PREFIX && addr[1] == OP_JMP_SEG) {
pred@2136
   363
		addr += 2;
pred@2136
   364
		addr = *(unsigned char**)addr;
pred@2136
   365
		//TODO: if addr points into the IAT
pred@2136
   366
		return *(void**)addr;
pred@2136
   367
	}
pred@2136
   368
pred@2136
   369
	//8bit offset
pred@2136
   370
	else if (addr[0] == OP_JMP_BYTE) {
pred@2136
   371
		addr = &addr[OP_JMP_BYTE_SIZE] + *(char*)&addr[1];
pred@2136
   372
		//mangled 32bit jump?
skamonkey@3727
   373
		if (addr[0] == OP_JMP) {
pred@2136
   374
			addr = addr + *(int*)&addr[1];
pred@2136
   375
		}
pred@2136
   376
		return addr;
pred@2136
   377
	}
pred@2136
   378
	/*
pred@2136
   379
	//32bit offset
pred@2136
   380
	else if (addr[0] == OP_JMP) {
pred@2136
   381
		addr = &addr[OP_JMP_SIZE] + *(int*)&addr[1];
pred@2136
   382
	}
pred@2136
   383
	*/
pred@2136
   384
pred@2136
   385
	return addr;
pred@2136
   386
}
pred@2136
   387
/*
pred@2136
   388
from ms detours package
pred@2136
   389
static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress)
pred@2136
   390
{
pred@2136
   391
	MEMORY_BASIC_INFORMATION mbi;
pred@2136
   392
	VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi));
pred@2136
   393
	__try {
pred@2136
   394
		PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase;
pred@2136
   395
		if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
pred@2136
   396
			return false;
pred@2136
   397
		}
pred@2136
   398
pred@2136
   399
		PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
pred@2136
   400
														  pDosHeader->e_lfanew);
pred@2136
   401
		if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
pred@2136
   402
			return false;
pred@2136
   403
		}
pred@2136
   404
pred@2136
   405
		if (pbAddress >= ((PBYTE)pDosHeader +
pred@2136
   406
						  pNtHeader->OptionalHeader
pred@2136
   407
						  .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) &&
pred@2136
   408
			pbAddress < ((PBYTE)pDosHeader +
pred@2136
   409
						 pNtHeader->OptionalHeader
pred@2136
   410
						 .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress +
pred@2136
   411
						 pNtHeader->OptionalHeader
pred@2136
   412
						 .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) {
pred@2136
   413
			return true;
pred@2136
   414
		}
pred@2136
   415
		return false;
pred@2136
   416
	}
pred@2136
   417
	__except(EXCEPTION_EXECUTE_HANDLER) {
pred@2136
   418
		return false;
pred@2136
   419
	}
pred@2136
   420
}
pred@2136
   421
*/