asm/asm.c
changeset 0 4c53fd0b014e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/asm/asm.c	Fri Feb 20 15:24:04 2009 +1300
     1.3 @@ -0,0 +1,421 @@
     1.4 +#include "asm.h"
     1.5 +
     1.6 +#ifndef WIN32
     1.7 +#define _GNU_SOURCE
     1.8 +#include <dlfcn.h>
     1.9 +#include <string.h>
    1.10 +
    1.11 +#define REG_EAX			0
    1.12 +#define REG_ECX			1
    1.13 +#define REG_EDX			2
    1.14 +#define REG_EBX			3
    1.15 +
    1.16 +#define IA32_MOV_REG_IMM		0xB8	// encoding is +r <imm32>
    1.17 +#endif
    1.18 +
    1.19 +extern void Msg( const char *, ... );
    1.20 +
    1.21 +/**
    1.22 +* Checks if a call to a fpic thunk has just been written into dest.
    1.23 +* If found replaces it with a direct mov that sets the required register to the value of pc.
    1.24 +*
    1.25 +* @param dest		Destination buffer where a call opcode + addr (5 bytes) has just been written.
    1.26 +* @param pc		The program counter value that needs to be set (usually the next address from the source).
    1.27 +* @noreturn
    1.28 +*/
    1.29 +void check_thunks(unsigned char *dest, unsigned char *pc)
    1.30 +{
    1.31 +#if defined WIN32
    1.32 +	return;
    1.33 +#else
    1.34 +	/* Step write address back 4 to the start of the function address */
    1.35 +	unsigned char *writeaddr = dest - 4;
    1.36 +	unsigned char *calloffset = *(unsigned char **)writeaddr;
    1.37 +	unsigned char *calladdr = (unsigned char *)(dest + (unsigned int)calloffset);
    1.38 +
    1.39 +	/* Lookup name of function being called */
    1.40 +	if ((*calladdr == 0x8B) && (*(calladdr+2) == 0x24) && (*(calladdr+3) == 0xC3))
    1.41 +	{
    1.42 +		//a thunk maybe?
    1.43 +		char movByte = IA32_MOV_REG_IMM;
    1.44 +
    1.45 +		/* Calculate the correct mov opcode */
    1.46 +		switch (*(calladdr+1))
    1.47 +		{
    1.48 +		case 0x04:
    1.49 +			{
    1.50 +				movByte += REG_EAX;
    1.51 +				break;
    1.52 +			}
    1.53 +		case 0x1C:
    1.54 +			{
    1.55 +				movByte += REG_EBX;
    1.56 +				break;
    1.57 +			}
    1.58 +		case 0x0C:
    1.59 +			{
    1.60 +				movByte += REG_ECX;
    1.61 +				break;
    1.62 +			}
    1.63 +		case 0x14:
    1.64 +			{
    1.65 +				movByte += REG_EDX;
    1.66 +				break;
    1.67 +			}
    1.68 +		default:
    1.69 +			{
    1.70 +				Msg("Unknown thunk: %c\n", *(calladdr+1));
    1.71 +				break;
    1.72 +			}
    1.73 +		}
    1.74 +
    1.75 +		/* Move our write address back one to where the call opcode was */
    1.76 +		writeaddr--;
    1.77 +
    1.78 +
    1.79 +		/* Write our mov */
    1.80 +		*writeaddr = movByte;
    1.81 +		writeaddr++;
    1.82 +
    1.83 +		/* Write the value - The provided program counter value */
    1.84 +		*(void **)writeaddr = (void *)pc;
    1.85 +		writeaddr += 4;
    1.86 +	}
    1.87 +
    1.88 +	return;
    1.89 +#endif
    1.90 +}
    1.91 +
    1.92 +//if dest is NULL, returns minimum number of bytes needed to be copied
    1.93 +//if dest is not NULL, it will copy the bytes to dest as well as fix CALLs and JMPs
    1.94 +//http://www.devmaster.net/forums/showthread.php?t=2311
    1.95 +int copy_bytes(unsigned char *func, unsigned char* dest, int required_len) {
    1.96 +	int bytecount = 0;
    1.97 +
    1.98 +	while(bytecount < required_len && *func != 0xCC)
    1.99 +	{
   1.100 +		// prefixes F0h, F2h, F3h, 66h, 67h, D8h-DFh, 2Eh, 36h, 3Eh, 26h, 64h and 65h
   1.101 +		int operandSize = 4;
   1.102 +		int FPU = 0;
   1.103 +		int twoByte = 0;
   1.104 +		unsigned char opcode = 0x90;
   1.105 +		unsigned char modRM = 0xFF;
   1.106 +		while(*func == 0xF0 ||
   1.107 +			  *func == 0xF2 ||
   1.108 +			  *func == 0xF3 ||
   1.109 +			 (*func & 0xFC) == 0x64 ||
   1.110 +			 (*func & 0xF8) == 0xD8 ||
   1.111 +			 (*func & 0x7E) == 0x62)
   1.112 +		{
   1.113 +			if(*func == 0x66)
   1.114 +			{
   1.115 +				operandSize = 2;
   1.116 +			}
   1.117 +			else if((*func & 0xF8) == 0xD8)
   1.118 +			{
   1.119 +				FPU = *func;
   1.120 +				if (dest)
   1.121 +						*dest++ = *func++;
   1.122 +				else
   1.123 +						func++;
   1.124 +				bytecount++;
   1.125 +				break;
   1.126 +			}
   1.127 +
   1.128 +			if (dest)
   1.129 +				*dest++ = *func++;
   1.130 +			else
   1.131 +				func++;
   1.132 +			bytecount++;
   1.133 +		}
   1.134 +
   1.135 +		// two-byte opcode byte
   1.136 +		if(*func == 0x0F)
   1.137 +		{
   1.138 +			twoByte = 1;
   1.139 +			if (dest)
   1.140 +				*dest++ = *func++;
   1.141 +			else
   1.142 +				func++;
   1.143 +			bytecount++;
   1.144 +		}
   1.145 +
   1.146 +		// opcode byte
   1.147 +		opcode = *func++;
   1.148 +		if (dest) *dest++ = opcode;
   1.149 +		bytecount++;
   1.150 +
   1.151 +		// mod R/M byte
   1.152 +		modRM = 0xFF;
   1.153 +		if(FPU)
   1.154 +		{
   1.155 +			if((opcode & 0xC0) != 0xC0)
   1.156 +			{
   1.157 +				modRM = opcode;
   1.158 +			}
   1.159 +		}
   1.160 +		else if(!twoByte)
   1.161 +		{
   1.162 +			if((opcode & 0xC4) == 0x00 ||
   1.163 +			   (opcode & 0xF4) == 0x60 && ((opcode & 0x0A) == 0x02 || (opcode & 0x09) == 0x09) ||
   1.164 +			   (opcode & 0xF0) == 0x80 ||
   1.165 +			   (opcode & 0xF8) == 0xC0 && (opcode & 0x0E) != 0x02 ||
   1.166 +			   (opcode & 0xFC) == 0xD0 ||
   1.167 +			   (opcode & 0xF6) == 0xF6)
   1.168 +			{
   1.169 +				modRM = *func++;
   1.170 +				if (dest) *dest++ = modRM;
   1.171 +				bytecount++;
   1.172 +			}
   1.173 +		}
   1.174 +		else
   1.175 +		{
   1.176 +			if((opcode & 0xF0) == 0x00 && (opcode & 0x0F) >= 0x04 && (opcode & 0x0D) != 0x0D ||
   1.177 +			   (opcode & 0xF0) == 0x30 ||
   1.178 +			   opcode == 0x77 ||
   1.179 +			   (opcode & 0xF0) == 0x80 ||
   1.180 +			   (opcode & 0xF0) == 0xA0 && (opcode & 0x07) <= 0x02 ||
   1.181 +			   (opcode & 0xF8) == 0xC8)
   1.182 +			{
   1.183 +				// No mod R/M byte
   1.184 +			}
   1.185 +			else
   1.186 +			{
   1.187 +				modRM = *func++;
   1.188 +				if (dest) *dest++ = modRM;
   1.189 +				bytecount++;
   1.190 +			}
   1.191 +		}
   1.192 +
   1.193 +		// SIB
   1.194 +		if((modRM & 0x07) == 0x04 &&
   1.195 +		   (modRM & 0xC0) != 0xC0)
   1.196 +		{
   1.197 +			if (dest)
   1.198 +				*dest++ = *func++;   //SIB
   1.199 +			else
   1.200 +				func++;
   1.201 +			bytecount++;
   1.202 +		}
   1.203 +
   1.204 +		// mod R/M displacement
   1.205 +
   1.206 +		// Dword displacement, no base
   1.207 +	if((modRM & 0xC5) == 0x05) {
   1.208 +		if (dest) {
   1.209 +			*(unsigned int*)dest = *(unsigned int*)func;
   1.210 +			dest += 4;
   1.211 +		}
   1.212 +		func += 4;
   1.213 +		bytecount += 4;
   1.214 +	}
   1.215 +
   1.216 +		// Byte displacement
   1.217 +	if((modRM & 0xC0) == 0x40) {
   1.218 +		if (dest)
   1.219 +			*dest++ = *func++;
   1.220 +		else
   1.221 +			func++;
   1.222 +		bytecount++;
   1.223 +	}
   1.224 +
   1.225 +		// Dword displacement
   1.226 +	if((modRM & 0xC0) == 0x80) {
   1.227 +		if (dest) {
   1.228 +			*(unsigned int*)dest = *(unsigned int*)func;
   1.229 +			dest += 4;
   1.230 +		}
   1.231 +		func += 4;
   1.232 +		bytecount += 4;
   1.233 +	}
   1.234 +
   1.235 +		// immediate
   1.236 +		if(FPU)
   1.237 +		{
   1.238 +			// Can't have immediate operand
   1.239 +		}
   1.240 +		else if(!twoByte)
   1.241 +		{
   1.242 +			if((opcode & 0xC7) == 0x04 ||
   1.243 +			   (opcode & 0xFE) == 0x6A ||   // PUSH/POP/IMUL
   1.244 +			   (opcode & 0xF0) == 0x70 ||   // Jcc
   1.245 +			   opcode == 0x80 ||
   1.246 +			   opcode == 0x83 ||
   1.247 +			   (opcode & 0xFD) == 0xA0 ||   // MOV
   1.248 +			   opcode == 0xA8 ||			// TEST
   1.249 +			   (opcode & 0xF8) == 0xB0 ||   // MOV
   1.250 +			   (opcode & 0xFE) == 0xC0 ||   // RCL
   1.251 +			   opcode == 0xC6 ||			// MOV
   1.252 +			   opcode == 0xCD ||			// INT
   1.253 +			   (opcode & 0xFE) == 0xD4 ||   // AAD/AAM
   1.254 +			   (opcode & 0xF8) == 0xE0 ||   // LOOP/JCXZ
   1.255 +			   opcode == 0xEB ||
   1.256 +			   opcode == 0xF6 && (modRM & 0x30) == 0x00)   // TEST
   1.257 +			{
   1.258 +				if (dest)
   1.259 +					*dest++ = *func++;
   1.260 +				else
   1.261 +					func++;
   1.262 +				bytecount++;
   1.263 +			}
   1.264 +		else if((opcode & 0xF7) == 0xC2) // RET
   1.265 +			{
   1.266 +				if (dest) {
   1.267 +					*(unsigned short*)dest = *(unsigned short*)func;
   1.268 +					dest += 2;
   1.269 +				}
   1.270 +				func += 2;
   1.271 +				bytecount += 2;
   1.272 +			}
   1.273 +			else if((opcode & 0xFC) == 0x80 ||
   1.274 +					(opcode & 0xC7) == 0x05 ||
   1.275 +					(opcode & 0xF8) == 0xB8 ||
   1.276 +					(opcode & 0xFE) == 0xE8 ||	  // CALL/Jcc
   1.277 +					(opcode & 0xFE) == 0x68 ||
   1.278 +					(opcode & 0xFC) == 0xA0 ||
   1.279 +					(opcode & 0xEE) == 0xA8 ||
   1.280 +					opcode == 0xC7 ||
   1.281 +					opcode == 0xF7 && (modRM & 0x30) == 0x00)
   1.282 +			{
   1.283 +				if (dest) {
   1.284 +					//Fix CALL/JMP offset
   1.285 +					if ((opcode & 0xFE) == 0xE8) {
   1.286 +						if (operandSize == 4)
   1.287 +						{
   1.288 +							*(long*)dest = ((func + *(long*)func) - dest);
   1.289 +
   1.290 +							//pRED* edit. func is the current address of the call address, +4 is the next instruction, so the value of $pc
   1.291 +							check_thunks(dest+4, func+4);
   1.292 +						}
   1.293 +						else
   1.294 +							*(short*)dest = ((func + *(short*)func) - dest);
   1.295 +
   1.296 +					} else {
   1.297 +						if (operandSize == 4)
   1.298 +							*(unsigned long*)dest = *(unsigned long*)func;
   1.299 +						else
   1.300 +							*(unsigned short*)dest = *(unsigned short*)func;
   1.301 +					}
   1.302 +					dest += operandSize;
   1.303 +				}
   1.304 +				func += operandSize;
   1.305 +				bytecount += operandSize;
   1.306 +
   1.307 +			}
   1.308 +		}
   1.309 +		else
   1.310 +		{
   1.311 +			if(opcode == 0xBA ||			// BT
   1.312 +			   opcode == 0x0F ||			// 3DNow!
   1.313 +			   (opcode & 0xFC) == 0x70 ||   // PSLLW
   1.314 +			   (opcode & 0xF7) == 0xA4 ||   // SHLD
   1.315 +			   opcode == 0xC2 ||
   1.316 +			   opcode == 0xC4 ||
   1.317 +			   opcode == 0xC5 ||
   1.318 +			   opcode == 0xC6)
   1.319 +			{
   1.320 +				if (dest)
   1.321 +					*dest++ = *func++;
   1.322 +				else
   1.323 +					func++;
   1.324 +			}
   1.325 +			else if((opcode & 0xF0) == 0x80) // Jcc -i
   1.326 +			{
   1.327 +				if (dest) {
   1.328 +					if (operandSize == 4)
   1.329 +						*(unsigned long*)dest = *(unsigned long*)func;
   1.330 +					else
   1.331 +						*(unsigned short*)dest = *(unsigned short*)func;
   1.332 +
   1.333 +					dest += operandSize;
   1.334 +				}
   1.335 +				func += operandSize;
   1.336 +				bytecount += operandSize;
   1.337 +			}
   1.338 +		}
   1.339 +	}
   1.340 +
   1.341 +	return bytecount;
   1.342 +}
   1.343 +
   1.344 +//insert a specific JMP instruction at the given location
   1.345 +void inject_jmp(void* src, void* dest) {
   1.346 +	*(unsigned char*)src = OP_JMP;
   1.347 +	*(long*)((unsigned char*)src+1) = (long)((unsigned char*)dest - ((unsigned char*)src + OP_JMP_SIZE));
   1.348 +}
   1.349 +
   1.350 +//fill a given block with NOPs
   1.351 +void fill_nop(void* src, unsigned int len) {
   1.352 +	unsigned char* src2 = (unsigned char*)src;
   1.353 +	while (len) {
   1.354 +		*src2++ = OP_NOP;
   1.355 +		--len;
   1.356 +	}
   1.357 +}
   1.358 +
   1.359 +void* eval_jump(void* src) {
   1.360 +	unsigned char* addr = (unsigned char*)src;
   1.361 +
   1.362 +	if (!addr) return 0;
   1.363 +
   1.364 +	//import table jump
   1.365 +	if (addr[0] == OP_PREFIX && addr[1] == OP_JMP_SEG) {
   1.366 +		addr += 2;
   1.367 +		addr = *(unsigned char**)addr;
   1.368 +		//TODO: if addr points into the IAT
   1.369 +		return *(void**)addr;
   1.370 +	}
   1.371 +
   1.372 +	//8bit offset
   1.373 +	else if (addr[0] == OP_JMP_BYTE) {
   1.374 +		addr = &addr[OP_JMP_BYTE_SIZE] + *(char*)&addr[1];
   1.375 +		//mangled 32bit jump?
   1.376 +		if (addr[0] = OP_JMP) {
   1.377 +			addr = addr + *(int*)&addr[1];
   1.378 +		}
   1.379 +		return addr;
   1.380 +	}
   1.381 +	/*
   1.382 +	//32bit offset
   1.383 +	else if (addr[0] == OP_JMP) {
   1.384 +		addr = &addr[OP_JMP_SIZE] + *(int*)&addr[1];
   1.385 +	}
   1.386 +	*/
   1.387 +
   1.388 +	return addr;
   1.389 +}
   1.390 +/*
   1.391 +from ms detours package
   1.392 +static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress)
   1.393 +{
   1.394 +	MEMORY_BASIC_INFORMATION mbi;
   1.395 +	VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi));
   1.396 +	__try {
   1.397 +		PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase;
   1.398 +		if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
   1.399 +			return false;
   1.400 +		}
   1.401 +
   1.402 +		PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
   1.403 +														  pDosHeader->e_lfanew);
   1.404 +		if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
   1.405 +			return false;
   1.406 +		}
   1.407 +
   1.408 +		if (pbAddress >= ((PBYTE)pDosHeader +
   1.409 +						  pNtHeader->OptionalHeader
   1.410 +						  .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) &&
   1.411 +			pbAddress < ((PBYTE)pDosHeader +
   1.412 +						 pNtHeader->OptionalHeader
   1.413 +						 .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress +
   1.414 +						 pNtHeader->OptionalHeader
   1.415 +						 .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) {
   1.416 +			return true;
   1.417 +		}
   1.418 +		return false;
   1.419 +	}
   1.420 +	__except(EXCEPTION_EXECUTE_HANDLER) {
   1.421 +		return false;
   1.422 +	}
   1.423 +}
   1.424 +*/