예제 #1
0
파일: gacutil.cpp 프로젝트: layeka/EasyHook
extern "C" __declspec(dllexport) LPINTERNAL_CONTEXT __stdcall GacCreateContext(){
	LPINTERNAL_CONTEXT	Result = NULL;

	if((Result = (LPINTERNAL_CONTEXT)RtlAllocateMemory(TRUE, sizeof(INTERNAL_CONTEXT))) == NULL)
		return NULL;

	memset(Result, 0, sizeof(INTERNAL_CONTEXT));

	if((Result->hMsCorEE = LoadLibrary(L"mscoree.dll")) == NULL)
		goto ERROR_ABORT;

	if((Result->LoadLibraryShim = (LoadLibraryShim_PROC)GetProcAddress(Result->hMsCorEE, "LoadLibraryShim")) == NULL)
		goto ERROR_ABORT;

	Result->LoadLibraryShim(L"fusion.dll", 0, 0, &Result->hFusionDll);

	if(Result->hFusionDll == NULL)
		goto ERROR_ABORT;

	if((Result->CreateAssemblyCache = (CreateAsmCache)GetProcAddress(Result->hFusionDll, "CreateAssemblyCache")) == NULL)
		goto ERROR_ABORT;

	if (!SUCCEEDED(Result->CreateAssemblyCache(&Result->Cache, 0)))
		goto ERROR_ABORT;

	return Result;

ERROR_ABORT:
	
	GacReleaseContext(&Result);

	return NULL;
}
BOOL RtlMoveMemory(
            PVOID InDest,
            PVOID InSource,
            ULONG InByteCount)
{
    PVOID       Buffer = RtlAllocateMemory(FALSE, InByteCount);

    if(Buffer == NULL)
        return FALSE;

    RtlCopyMemory(Buffer, InSource, InByteCount);
    RtlCopyMemory(InDest, Buffer, InByteCount);

    return TRUE;
}
예제 #3
0
EASYHOOK_NT_EXPORT HookCompleteInjection(LPREMOTE_INFO InInfo)
{
/*
Description:

    This method is directly called from assembler code. It will update
    the symbols with the ones of the current process, adjust the PATH
    variable and invoke one of two above injection completions.

*/

    WCHAR*	    	PATH = NULL;
	ULONG		    ErrorCode = 0;
	HMODULE         hMod = GetModuleHandleA("kernel32.dll");
    ULONG           DirSize;
    ULONG           EnvSize;

    /*
		To increase stability we will now update all symbols with the
		real local ones...
	*/
	InInfo->LoadLibraryW = GetProcAddress(hMod, "LoadLibraryW");
	InInfo->FreeLibrary = GetProcAddress(hMod, "FreeLibrary");
	InInfo->GetProcAddress = GetProcAddress(hMod, "GetProcAddress");
	InInfo->VirtualFree = GetProcAddress(hMod, "VirtualFree");
	InInfo->VirtualProtect = GetProcAddress(hMod, "VirtualProtect");
	InInfo->ExitThread = GetProcAddress(hMod, "ExitThread");
	InInfo->GetLastError = GetProcAddress(hMod, "GetLastError");

    

	/* 
        Make directory of user library path available to current process...
	    This is to find dependencies without copying them into a global
	    directory which might cause trouble.
    */

	DirSize = RtlUnicodeLength(InInfo->PATH);
	EnvSize = GetEnvironmentVariableW(L"PATH", NULL, 0) + DirSize;

	if((PATH = (wchar_t*)RtlAllocateMemory(TRUE, EnvSize * 2 + 10)) == NULL)
		UNMANAGED_ERROR(1);

	GetEnvironmentVariableW(L"PATH", PATH, EnvSize);

	// add library path to environment variable
	if(!RtlMoveMemory(PATH + DirSize, PATH, (EnvSize - DirSize) * 2))
        UNMANAGED_ERROR(1);

	RtlCopyMemory(PATH, InInfo->PATH, DirSize * 2);

	if(!SetEnvironmentVariableW(L"PATH", PATH))
		UNMANAGED_ERROR(2);

    if(!RTL_SUCCESS(RhSetWakeUpThreadID(InInfo->WakeUpThreadID)))
        UNMANAGED_ERROR(3);

	// load and execute user library...
	if(InInfo->IsManaged)
        ErrorCode = CompleteManagedInjection(InInfo);
    else
        ErrorCode = CompleteUnmanagedInjection(InInfo);

ABORT_ERROR:

    // release resources
	if(PATH != NULL)
		RtlFreeMemory(PATH);

	if(InInfo->hRemoteSignal != NULL)
		CloseHandle(InInfo->hRemoteSignal);

	return ErrorCode;
}
예제 #4
0
파일: thread.c 프로젝트: chulup/Easyhook
EASYHOOK_NT_EXPORT RhInjectLibrary(
		ULONG InTargetPID,
		ULONG InWakeUpTID,
		ULONG InInjectionOptions,
		WCHAR* InLibraryPath_x86,
		WCHAR* InLibraryPath_x64,
		PVOID InPassThruBuffer,
        ULONG InPassThruSize)
{
/*
Description:

    Injects a library into the target process. This is a very stable operation.
    The problem so far is, that only the NET layer will support injection
    through WOW64 boundaries and into other terminal sessions. It is quite
    complex to realize with unmanaged code and that's why it is not supported!

    If you really need this feature I highly recommend to at least look at C++.NET
    because using the managed injection can speed up your development progress
    about orders of magnitudes. I know by experience that writing the required
    multi-process injection code in any unmanaged language is a rather daunting task!

Parameters:

    - InTargetPID

        The process in which the library should be injected.
    
    - InWakeUpTID

        If the target process was created suspended (RhCreateAndInject), then
        this parameter should be set to the main thread ID of the target.
        You may later resume the process from within the injected library
        by calling RhWakeUpProcess(). If the process is already running, you
        should specify zero.

    - InInjectionOptions

        All flags can be combined.

        EASYHOOK_INJECT_DEFAULT: 
            
            No special behavior. The given libraries are expected to be unmanaged DLLs.
            Further they should export an entry point named 
            "NativeInjectionEntryPoint" (in case of 64-bit) and
            "_NativeInjectionEntryPoint@4" (in case of 32-bit). The expected entry point 
            signature is REMOTE_ENTRY_POINT.

        EASYHOOK_INJECT_MANAGED: 
        
            The given user library is a NET assembly. Further they should export a class
            named "EasyHook.InjectionLoader" with a static method named "Main". The
            signature of this method is expected to be "int (String)". Please refer
            to the managed injection loader of EasyHook for more information about
            writing such managed entry points.

        EASYHOOK_INJECT_STEALTH:

            Uses the experimental stealth thread creation. If it fails
            you may try it with default settings. 

		EASYHOOK_INJECT_HEART_BEAT:
			
			Is only used internally to workaround the managed process creation bug.
			For curiosity, NET seems to hijack our remote thread if a managed process
			is created suspended. It doesn't do anything with the suspended main thread,


    - InLibraryPath_x86

        A relative or absolute path to the 32-bit version of the user library being injected.
        If you don't want to inject into 32-Bit processes, you may set this parameter to NULL.

    - InLibraryPath_x64

        A relative or absolute path to the 64-bit version of the user library being injected.
        If you don't want to inject into 64-Bit processes, you may set this parameter to NULL.

    - InPassThruBuffer

        An optional buffer containg data to be passed to the injection entry point. Such data
        is available in both, the managed and unmanaged user library entry points.
        Set to NULL if no used.

    - InPassThruSize

        Specifies the size in bytes of the pass thru data. If "InPassThruBuffer" is NULL, this
        parameter shall also be zero.

Returns:

    

*/
	HANDLE					hProc = NULL;
	HANDLE					hRemoteThread = NULL;
	HANDLE					hSignal = NULL;
	UCHAR*					RemoteInjectCode = NULL;
	LPREMOTE_INFO			Info = NULL;
    LPREMOTE_INFO           RemoteInfo = NULL;
	ULONG					RemoteInfoSize = 0;
	BYTE*					Offset = 0;
    ULONG                   CodeSize;
    BOOL                    Is64BitTarget;
    NTSTATUS				NtStatus;
    LONGLONG                Diff;
    HANDLE                  Handles[2];

    ULONG                   UserLibrarySize;
    ULONG                   PATHSize;
    ULONG                   EasyHookPathSize;
    ULONG                   EasyHookEntrySize;
    ULONG                   Code;

    SIZE_T                  BytesWritten;
    WCHAR                   UserLibrary[MAX_PATH+1];
    WCHAR					PATH[MAX_PATH + 1];
    WCHAR					EasyHookPath[MAX_PATH + 1];
#ifdef _M_X64
	CHAR*					EasyHookEntry = "HookCompleteInjection";
#else
	CHAR*					EasyHookEntry = "_HookCompleteInjection@4";
#endif

    // validate parameters
    if(InPassThruSize > MAX_PASSTHRU_SIZE)
        THROW(STATUS_INVALID_PARAMETER_7, L"The given pass thru buffer is too large.");

    if(InPassThruBuffer != NULL)
    {
        if(!IsValidPointer(InPassThruBuffer, InPassThruSize))
            THROW(STATUS_INVALID_PARAMETER_6, L"The given pass thru buffer is invalid.");
    }
    else if(InPassThruSize != 0)
        THROW(STATUS_INVALID_PARAMETER_7, L"If no pass thru buffer is specified, the pass thru length also has to be zero.");

	if(InTargetPID == GetCurrentProcessId())
		THROW(STATUS_NOT_SUPPORTED, L"For stability reasons it is not supported to inject into the calling process.");

	// open target process
	if((hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, InTargetPID)) == NULL)
	{
		if(GetLastError() == ERROR_ACCESS_DENIED)
		    THROW(STATUS_ACCESS_DENIED, L"Unable to open target process. Consider using a system service.")
		else
			THROW(STATUS_NOT_FOUND, L"The given target process does not exist!");
	}

	/*
		Check bitness...

		After this we can assume hooking a target that is running in the same
		WOW64 level.
	*/
#ifdef _M_X64
	FORCE(RhIsX64Process(InTargetPID, &Is64BitTarget));
      
    if(!Is64BitTarget)
        THROW(STATUS_WOW_ASSERTION, L"It is not supported to directly hook through the WOW64 barrier.");

    if(!GetFullPathNameW(InLibraryPath_x64, MAX_PATH, UserLibrary, NULL))
        THROW(STATUS_INVALID_PARAMETER_5, L"Unable to get full path to the given 64-bit library.");
#else
	FORCE(RhIsX64Process(InTargetPID, &Is64BitTarget));
      
    if(Is64BitTarget)
        THROW(STATUS_WOW_ASSERTION, L"It is not supported to directly hook through the WOW64 barrier.");

	if(!GetFullPathNameW(InLibraryPath_x86, MAX_PATH, UserLibrary, NULL))
        THROW(STATUS_INVALID_PARAMETER_4, L"Unable to get full path to the given 32-bit library.");
#endif

	/*
		Validate library path...
	*/
	if(!RtlFileExists(UserLibrary))
    {
    #ifdef _M_X64
        THROW(STATUS_INVALID_PARAMETER_5, L"The given 64-Bit library does not exist!");
    #else
        THROW(STATUS_INVALID_PARAMETER_4, L"The given 32-Bit library does not exist!");
    #endif
    }

	// import strings...
    RtlGetWorkingDirectory(PATH, MAX_PATH - 1);
    RtlGetCurrentModulePath(EasyHookPath, MAX_PATH);

	// allocate remote information block
    EasyHookPathSize = (RtlUnicodeLength(EasyHookPath) + 1) * 2;
    EasyHookEntrySize = (RtlAnsiLength(EasyHookEntry) + 1);
    PATHSize = (RtlUnicodeLength(PATH) + 1 + 1) * 2;
    UserLibrarySize = (RtlUnicodeLength(UserLibrary) + 1 + 1) * 2;

    PATH[PATHSize / 2 - 2] = ';';
    PATH[PATHSize / 2 - 1] = 0;

	RemoteInfoSize = EasyHookPathSize + EasyHookEntrySize + PATHSize + InPassThruSize + UserLibrarySize;

	RemoteInfoSize += sizeof(REMOTE_INFO);

	if((Info = (LPREMOTE_INFO)RtlAllocateMemory(TRUE, RemoteInfoSize)) == NULL)
		THROW(STATUS_NO_MEMORY, L"Unable to allocate memory in current process.");

	Info->LoadLibraryW = (PVOID)GetProcAddress(hKernel32, "LoadLibraryW");
	Info->FreeLibrary = (PVOID)GetProcAddress(hKernel32, "FreeLibrary");
	Info->GetProcAddress = (PVOID)GetProcAddress(hKernel32, "GetProcAddress");
	Info->VirtualFree = (PVOID)GetProcAddress(hKernel32, "VirtualFree");
	Info->VirtualProtect = (PVOID)GetProcAddress(hKernel32, "VirtualProtect");
	Info->ExitThread = (PVOID)GetProcAddress(hKernel32, "ExitThread");
	Info->GetLastError = (PVOID)GetProcAddress(hKernel32, "GetLastError");

    Info->WakeUpThreadID = InWakeUpTID;
    Info->IsManaged = InInjectionOptions & EASYHOOK_INJECT_MANAGED;

	// allocate memory in target process
	CodeSize = GetInjectionSize();

	if((RemoteInjectCode = (BYTE*)VirtualAllocEx(hProc, NULL, CodeSize + RemoteInfoSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) == NULL)
        THROW(STATUS_NO_MEMORY, L"Unable to allocate memory in target process.");

	// save strings
	Offset = (BYTE*)(Info + 1);

	Info->EasyHookEntry = (char*)Offset;
	Info->EasyHookPath = (wchar_t*)(Offset += EasyHookEntrySize);
	Info->PATH = (wchar_t*)(Offset += EasyHookPathSize);
	Info->UserData = (BYTE*)(Offset += PATHSize);
    Info->UserLibrary = (WCHAR*)(Offset += InPassThruSize);

	Info->Size = RemoteInfoSize;
	Info->HostProcess = GetCurrentProcessId();
	Info->UserDataSize = 0;

	Offset += UserLibrarySize;

	if((ULONG)(Offset - ((BYTE*)Info)) > Info->Size)
        THROW(STATUS_BUFFER_OVERFLOW, L"A buffer overflow in internal memory was detected.");

	RtlCopyMemory(Info->EasyHookPath, EasyHookPath, EasyHookPathSize);
	RtlCopyMemory(Info->PATH, PATH, PATHSize);
	RtlCopyMemory(Info->EasyHookEntry, EasyHookEntry, EasyHookEntrySize);
    RtlCopyMemory(Info->UserLibrary, UserLibrary, UserLibrarySize);


	if(InPassThruBuffer != NULL)
	{
		RtlCopyMemory(Info->UserData, InPassThruBuffer, InPassThruSize);

		Info->UserDataSize = InPassThruSize;
	}

	// copy code into target process
	if(!WriteProcessMemory(hProc, RemoteInjectCode, GetInjectionPtr(), CodeSize, &BytesWritten) || (BytesWritten != CodeSize))
		THROW(STATUS_INTERNAL_ERROR, L"Unable to write into target process memory.");

	// create and export signal event>
	if((hSignal = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL)
        THROW(STATUS_INSUFFICIENT_RESOURCES, L"Unable to create event.");

	// Possible resource leck: the remote handles cannt be closed here if an error occurs
	if(!DuplicateHandle(GetCurrentProcess(), hSignal, hProc, &Info->hRemoteSignal, EVENT_ALL_ACCESS, FALSE, 0))
		THROW(STATUS_INTERNAL_ERROR, L"Failed to duplicate remote event.");

	// relocate remote information
	RemoteInfo = (LPREMOTE_INFO)(RemoteInjectCode + CodeSize);
	Diff = ((BYTE*)RemoteInfo - (BYTE*)Info);

	Info->EasyHookEntry = (char*)(((BYTE*)Info->EasyHookEntry) + Diff);
	Info->EasyHookPath = (wchar_t*)(((BYTE*)Info->EasyHookPath) + Diff);
	Info->PATH = (wchar_t*)(((BYTE*)Info->PATH) + Diff);
    Info->UserLibrary = (wchar_t*)(((BYTE*)Info->UserLibrary) + Diff);

	if(Info->UserData != NULL)
		Info->UserData = (BYTE*)(((BYTE*)Info->UserData) + Diff);

	Info->RemoteEntryPoint = RemoteInjectCode;

	if(!WriteProcessMemory(hProc, RemoteInfo, Info, RemoteInfoSize, &BytesWritten) || (BytesWritten != RemoteInfoSize))
		THROW(STATUS_INTERNAL_ERROR, L"Unable to write into target process memory.");

	if((InInjectionOptions & EASYHOOK_INJECT_STEALTH) != 0)
	{
		FORCE(RhCreateStealthRemoteThread(InTargetPID, (LPTHREAD_START_ROUTINE)RemoteInjectCode, RemoteInfo, &hRemoteThread));
	}
	else
	{
		if(!RTL_SUCCESS(NtCreateThreadEx(hProc, (LPTHREAD_START_ROUTINE)RemoteInjectCode, RemoteInfo, FALSE, &hRemoteThread)))
		{
			// create remote thread and wait for injection completion
			if((hRemoteThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)RemoteInjectCode, RemoteInfo, 0, NULL)) == NULL)
				THROW(STATUS_ACCESS_DENIED, L"Unable to create remote thread.");
		}
	}

	/*
	 * The assembler codes are designed to let us derive extensive error information...
	*/
    Handles[1] = hSignal;
	Handles[0] = hRemoteThread;

	Code = WaitForMultipleObjects(2, Handles, FALSE, INFINITE);

	if(Code == WAIT_OBJECT_0)
	{
		// parse error code
		GetExitCodeThread(hRemoteThread, &Code);

		SetLastError(Code & 0x0FFFFFFF);

		switch(Code & 0xF0000000)
		{
		case 0x10000000: THROW(STATUS_INTERNAL_ERROR, L"Unable to find internal entry point.");
		case 0x20000000: THROW(STATUS_INTERNAL_ERROR, L"Unable to make stack executable.");
		case 0x30000000: THROW(STATUS_INTERNAL_ERROR, L"Unable to release injected library.");
		case 0x40000000: THROW(STATUS_INTERNAL_ERROR, L"Unable to find EasyHook library in target process context.");
		case 0xF0000000: // error in C++ injection completion
			{
				switch(Code & 0xFF)
				{
#ifdef _M_X64
                case 20: THROW(STATUS_INVALID_PARAMETER_5, L"Unable to load the given 64-bit library into target process.");
                case 21: THROW(STATUS_INVALID_PARAMETER_5, L"Unable to find the required native entry point in the given 64-bit library.");
                case 12: THROW(STATUS_INVALID_PARAMETER_5, L"Unable to find the required managed entry point in the given 64-bit library.");
#else
                case 20: THROW(STATUS_INVALID_PARAMETER_4, L"Unable to load the given 32-bit library into target process.");
                case 21: THROW(STATUS_INVALID_PARAMETER_4, L"Unable to find the required native entry point in the given 32-bit library.");
                case 12: THROW(STATUS_INVALID_PARAMETER_4, L"Unable to find the required managed entry point in the given 32-bit library.");
#endif
                
                case 13: THROW(STATUS_DLL_INIT_FAILED, L"The user defined managed entry point failed in the target process. Make sure that EasyHook is registered in the GAC. Refer to event logs for more information.");
				case 1: THROW(STATUS_INTERNAL_ERROR, L"Unable to allocate memory in target process.");
				case 2: THROW(STATUS_INTERNAL_ERROR, L"Unable to adjust target's PATH variable.");
                case 10: THROW(STATUS_INTERNAL_ERROR, L"Unable to load 'mscoree.dll' into target process.");
				case 11: THROW(STATUS_INTERNAL_ERROR, L"Unable to bind NET Runtime to target process.");
				case 22: THROW(STATUS_INTERNAL_ERROR, L"Unable to signal remote event.");
				default: THROW(STATUS_INTERNAL_ERROR, L"Unknown error in injected C++ completion routine.");
				}
			}break;
		case 0:
			THROW(STATUS_INTERNAL_ERROR, L"C++ completion routine has returned success but didn't raise the remote event.");
		default:
			THROW(STATUS_INTERNAL_ERROR, L"Unknown error in injected assembler code.");
		}
	}
	else if(Code != WAIT_OBJECT_0 + 1)
		THROW(STATUS_INTERNAL_ERROR, L"Unable to wait for injection completion due to timeout. ");

    RETURN;

THROW_OUTRO:
FINALLY_OUTRO:
    {
		// release resources
		if(hProc != NULL)
			CloseHandle(hProc);

		if(Info != NULL)
			RtlFreeMemory(Info);

		if(hRemoteThread != NULL)
			CloseHandle(hRemoteThread);

		if(hSignal != NULL)
			CloseHandle(hSignal);

        return NtStatus;
	}
}
예제 #5
0
EASYHOOK_NT_INTERNAL LhUpdateModuleInformation()
{
	NTSTATUS						NtStatus;
	PSYSTEM_MODULE_INFORMATION		NativeList = NULL;
	ULONG							RequiredSize = 0;
	ULONG							i;
	PSYSTEM_MODULE					Mod;
	MODULE_INFORMATION*				List = NULL;

	if(!LhModuleListChanged)
		return STATUS_SUCCESS;

	LhModuleListChanged = FALSE;

	if(ZwQuerySystemInformation(11, NULL, 0, &RequiredSize) != STATUS_INFO_LENGTH_MISMATCH)
		THROW(STATUS_INTERNAL_ERROR, L"Unable to enumerate system modules.");

	if((NativeList = RtlAllocateMemory(TRUE, RequiredSize)) == NULL)
		THROW(STATUS_NO_MEMORY, L"Unable to allocate memory.");

	if(!RTL_SUCCESS(ZwQuerySystemInformation(11, NativeList, RequiredSize, &RequiredSize)))
		THROW(STATUS_INTERNAL_ERROR, L"Unable to enumerate system modules.");

	if((List = RtlAllocateMemory(FALSE, sizeof(MODULE_INFORMATION) * NativeList->Count)) == NULL)
		THROW(STATUS_NO_MEMORY, L"Unable to allocate memory.");

	for(i = 0; i < NativeList->Count; i++)
	{
		Mod = &NativeList->Modules[i];

		List[i].BaseAddress = Mod->ImageBaseAddress;
		List[i].ImageSize = Mod->ImageSize;
		List[i].ModuleName = Mod->Name + Mod->NameOffset;
		
		memcpy(List[i].Path, Mod->Name, 256);

		if(i + 1 < NativeList->Count)
			List[i].Next = &List[i + 1];
		else
			List[i].Next = NULL;
	}

	RtlAcquireLock(&GlobalHookLock);
	{
		if(LhNativeModuleArray != NULL)
			RtlFreeMemory(LhNativeModuleArray);

		if(LhModuleArray != NULL)
			RtlFreeMemory(LhModuleArray);

		LhNativeModuleArray = NativeList;
		LhModuleArray = List;
		LhModuleCount = NativeList->Count;
	}
	RtlReleaseLock(&GlobalHookLock);

    RETURN;

THROW_OUTRO:
	{
		if(NativeList != NULL)
			RtlFreeMemory(NativeList);

		if(List != NULL)
			RtlFreeMemory(List);
	}
FINALLY_OUTRO:
    return NtStatus;
}
예제 #6
0
EASYHOOK_NT_INTERNAL LhUpdateModuleInformation()
{
	
/*
Description:

    Is supposed to be called interlocked... "ProcessModules" is 
    outsourced to prevent "__chkstk".
    Will just enumerate current process modules and extract
    required information for each of them.
*/
    LONG					NtStatus = STATUS_UNHANDLED_EXCEPTION;
    ULONG					Index;
    ULONG					ModIndex;
    MODULEINFO*				NativeList = NULL;
    ULONG					ModuleCount;
	MODULEINFO*				Mod = NULL;
	MODULE_INFORMATION*		List = NULL;
	CHAR*					PathList = NULL;
	CHAR*					ModPath = NULL;
	ULONG					ModPathSize = 0;
	LONG					iChar = 0;

    // enumerate modules...
	RtlAcquireLock(&GlobalHookLock);
	{
		if(!EnumProcessModules(
				GetCurrentProcess(),
				ProcessModules,
				sizeof(ProcessModules),
				&ModuleCount))
		{
			RtlReleaseLock(&GlobalHookLock);

			THROW(STATUS_INTERNAL_ERROR, L"Unable to enumerate current process modules.");
		}
	}
	RtlReleaseLock(&GlobalHookLock);

    ModuleCount /= sizeof(HMODULE);

    // retrieve module information
    if((NativeList = (MODULEINFO*)RtlAllocateMemory(FALSE, ModuleCount * sizeof(NativeList[0]))) == NULL)
        THROW(STATUS_NO_MEMORY, L"Unable to allocate memory for module information.");

	if((List = (MODULE_INFORMATION*)RtlAllocateMemory(TRUE, sizeof(MODULE_INFORMATION) * ModuleCount)) == NULL)
		THROW(STATUS_NO_MEMORY, L"Unable to allocate memory.");

	if((PathList = (CHAR*)RtlAllocateMemory(TRUE, MAX_PATH * ModuleCount)) == NULL)
		THROW(STATUS_NO_MEMORY, L"Unable to allocate memory.");

    for(Index = 0, ModIndex = 0; Index < ModuleCount; Index++)
    {
		// collect information
        if(!GetModuleInformation(
                GetCurrentProcess(),
                ProcessModules[Index],
                &NativeList[ModIndex],
                sizeof(NativeList[ModIndex])))
            continue;

		GetModuleFileNameA(
				ProcessModules[Index],
				&PathList[ModIndex * MAX_PATH],
				MAX_PATH);

		if(GetLastError() != ERROR_SUCCESS)
			continue;

        Mod = &NativeList[ModIndex];

		// normalize module information
		List[ModIndex].BaseAddress = (UCHAR*)Mod->lpBaseOfDll;
		List[ModIndex].ImageSize = Mod->SizeOfImage;

		memcpy(List[ModIndex].Path, &PathList[ModIndex * MAX_PATH], MAX_PATH + 1);

		ModPath = List[ModIndex].Path;
		ModPathSize = RtlAnsiLength(ModPath);

		for(iChar = ModPathSize; iChar >= 0; iChar--)
		{
			if(ModPath[iChar] == '\\')
			{
				List[ModIndex].ModuleName = &ModPath[iChar + 1];

				break;
			}
		}

		if(ModIndex + 1 < ModuleCount)
			List[ModIndex].Next = &List[ModIndex + 1];
		else
			List[ModIndex].Next = NULL;

        ModIndex++;
    }

    // save changes...
	RtlAcquireLock(&GlobalHookLock);
	{
		if(LhNativeModuleArray != NULL)
			RtlFreeMemory(LhNativeModuleArray);

		if(LhModuleArray != NULL)
			RtlFreeMemory(LhModuleArray);

		if(LhNativePathArray != NULL)
			RtlFreeMemory(LhNativePathArray);

		LhNativePathArray = PathList;
		LhNativeModuleArray = NativeList;
		LhModuleArray = List;
		LhModuleCount = ModIndex;
	}
	RtlReleaseLock(&GlobalHookLock);

    RETURN;

THROW_OUTRO:
	{
		if(NativeList != NULL)
			RtlFreeMemory(NativeList);

		if(List != NULL)
			RtlFreeMemory(List);
	}
FINALLY_OUTRO:
    return NtStatus;
}
예제 #7
0
ULONGLONG LhBarrierIntro(LOCAL_HOOK_INFO* InHandle, void* InRetAddr, void** InAddrOfRetAddr)
{
/*
Description:

    Will be called from assembler code and enters the 
    thread deadlock barrier.
*/
    LPTHREAD_RUNTIME_INFO		Info;
    RUNTIME_INFO*		        Runtime;
	BOOL						Exists;

	#ifdef _M_X64
		InHandle -= 1;
	#endif

	// are we in OS loader lock?
	if(IsLoaderLock())
	{
		/*
			Execution of managed code or even any other code within any loader lock
			may lead into unpredictable application behavior and therefore we just
			execute without intercepting the call...
		*/

		/*  !!Note that the assembler code does not invoke LhBarrierOutro() in this case!! */

		return FALSE;
	}

	// open pointer table
	Exists = TlsGetCurrentValue(&Unit.TLS, &Info);

	if(!Exists)
	{
		if(!TlsAddCurrentThread(&Unit.TLS))
			return FALSE;
	}

	/*
		To minimize APIs that can't be hooked, we are now entering the self protection.
		This will allow anybody to hook any APIs except those required to setup
		self protection.

		Self protection prevents any further hook interception for the current fiber,
		while setting up the "Thread Deadlock Barrier"...
	*/
	if(!AcquireSelfProtection())
	{
		/*  !!Note that the assembler code does not invoke LhBarrierOutro() in this case!! */

		return FALSE;
	}

	ASSERT(InHandle->HLSIndex < MAX_HOOK_COUNT);

	if(!Exists)
	{
		TlsGetCurrentValue(&Unit.TLS, &Info);

		Info->Entries = (RUNTIME_INFO*)RtlAllocateMemory(TRUE, sizeof(RUNTIME_INFO) * MAX_HOOK_COUNT);

		if(Info->Entries == NULL)
			goto DONT_INTERCEPT;
	}

	// get hook runtime info...
	Runtime = &Info->Entries[InHandle->HLSIndex];

	if(Runtime->HLSIdent != InHandle->HLSIdent)
	{
		// just reset execution information
		Runtime->HLSIdent = InHandle->HLSIdent;
		Runtime->IsExecuting = FALSE;
	}

	// detect loops in hook execution hiearchy.
	if(Runtime->IsExecuting)
	{
		/*
			This implies that actually the handler has invoked itself. Because of
			the special HookLocalStorage, this is now also signaled if other
			hooks invoked by the related handler are calling it again.

			I call this the "Thread deadlock barrier".

			!!Note that the assembler code does not invoke LhBarrierOutro() in this case!!
		*/

		goto DONT_INTERCEPT;
	}

	Info->Callback = InHandle->Callback;
	Info->Current = Runtime;

	/*
		Now we will negotiate thread/process access based on global and local ACL...
	*/
#ifndef DRIVER
	Runtime->IsExecuting = IsThreadIntercepted(&InHandle->LocalACL, GetCurrentThreadId());
#else
	Runtime->IsExecuting = IsProcessIntercepted(&InHandle->LocalACL, (ULONG)PsGetCurrentProcessId());
#endif

	if(!Runtime->IsExecuting)
		goto DONT_INTERCEPT;

	// save some context specific information
	Runtime->RetAddress = InRetAddr;
	Runtime->AddrOfRetAddr = InAddrOfRetAddr;

	ReleaseSelfProtection();
	
	return TRUE;

DONT_INTERCEPT:
	/*  !!Note that the assembler code does not invoke UnmanagedHookOutro() in this case!! */

	if(Info != NULL)
	{
		Info->Current = NULL;
		Info->Callback = NULL;

		ReleaseSelfProtection();
	}

	return FALSE;
}
예제 #8
0
파일: alloc.c 프로젝트: EasyHook/EasyHook
///////////////////////////////////////////////////////////////////////////////////
/////////////////////// LhAllocateMemoryEx
///////////////////////////////////////////////////////////////////////////////////
void* LhAllocateMemoryEx(void* InEntryPoint, ULONG* OutPageSize)
{
/*
Description:

    Allocates one page of hook specific memory. The page size is returned in OutPageSize

Parameters:

    - InEntryPoint

        Ignored for 32-Bit versions and drivers. In 64-Bit user mode, the returned
        pointer will always be in a 31-bit boundary around this parameter. This way
        a relative jumper can still be placed instead of having to consume much more entry
        point bytes for an absolute jump!

    - OutPageSize

        Will be updated to contain the page size.

Returns:

    NULL if no memory could be allocated, a valid pointer otherwise.

*/

    UCHAR*			    Res = NULL;

#if defined(_M_X64) && !defined(DRIVER)
    LONGLONG            Base;
    LONGLONG		    iStart;
    LONGLONG		    iEnd;
    LONGLONG            Index;

#endif
	
#if !defined(DRIVER)
    SYSTEM_INFO		    SysInfo;
    ULONG               PAGE_SIZE;

    GetSystemInfo(&SysInfo);

    PAGE_SIZE = SysInfo.dwPageSize;
    *OutPageSize = PAGE_SIZE;
#endif


    // reserve page with execution privileges
#if defined(_M_X64) && !defined(DRIVER)

    /*
        Reserve memory around entry point...
    */
    iStart = ((LONGLONG)InEntryPoint) - ((LONGLONG)0x7FFFFF00);
    iEnd = ((LONGLONG)InEntryPoint) + ((LONGLONG)0x7FFFFF00);

    if(iStart < (LONGLONG)SysInfo.lpMinimumApplicationAddress)
        iStart = (LONGLONG)SysInfo.lpMinimumApplicationAddress; // shall not be null, because then VirtualAlloc() will not work as expected

    if(iEnd > (LONGLONG)SysInfo.lpMaximumApplicationAddress)
        iEnd = (LONGLONG)SysInfo.lpMaximumApplicationAddress;

    // we are trying to get memory as near as possible to relocate most RIP-relative instructions
    for(Base = (LONGLONG)InEntryPoint, Index = 0; ; Index += PAGE_SIZE)
    {
		BOOLEAN end = TRUE;
		if(Base + Index < iEnd)
		{
			if((Res = (UCHAR*)VirtualAlloc((void*)(Base + Index), PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)) != NULL)
				break;
			end = FALSE;
		}

        if(Base - Index > iStart)
        {
	        if((Res = (BYTE*)VirtualAlloc((void*)(Base - Index), PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)) != NULL)
		        break;
			end = FALSE;
        }

		if (end)
			break;
    }

    if(Res == NULL)
	    return NULL;
#else
    
	*OutPageSize = PAGE_SIZE;
	// in 32-bit mode the trampoline will always be reachable
	// In 64-bit driver mode we use an absolute address so the trampoline will always be reachable
    if((Res = (UCHAR*)RtlAllocateMemory(TRUE, PAGE_SIZE)) == NULL)
        return NULL;

#endif

    return Res;
}