めいくりぷとのブログ

技術的なことやゲームのことやら・・・

kousin

xigncode あっぷでーと

なんでもコピーすりゃ良いって訳でも無いと思う。
アプデ前からコピーされたntdll.dllがマッピングされてて、NtQueryVirtualMemoryでモジュール列挙するやら、NtOpenProcessからプロセス検出やらあったけど、
アプデ後に、user32.dllとkernelbase.dllのコピーされたdllもマッピングされるようになって、ウィンドウ検出のGetWindowThreadProcessIdとかもコピーされたuser32.dllから呼ばれるようになった。

まあ結局API hookだけで完結したから、どうでも良いけど...

ネタが無いので、ただそれだけの記事。

ドライバ

_Success_(NT_SUCCESS(return))
NTSTATUS
NTAPI
ZwOpenDriver(
	_In_  PUNICODE_STRING DeviceName,
	_Out_ PHANDLE		  DeviceHandle
	)
{
	HANDLE Device;

	if (!DeviceName || !DeviceHandle)
		return STATUS_INVALID_PARAMETER;

	Device = CreateFile(DeviceName->Buffer,
		GENERIC_READ | GENERIC_WRITE | FILE_GENERIC_EXECUTE,
		0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY, 0);

	if (Device != NULL &&
		Device != INVALID_HANDLE_VALUE)
	{
		*DeviceHandle = Device;
		return STATUS_SUCCESS;
	}

	return STATUS_UNSUCCESSFUL;
}

_Success_(NT_SUCCESS(return))
NTSTATUS
NTAPI
ZwCloseDriver(
	_In_ HANDLE DeviceHandle
	)
{
	return NtClose(DeviceHandle);
}

_Success_(NT_SUCCESS(return))
NTSTATUS
NTAPI
ZwStartDriver(
	_In_  PUNICODE_STRING DeviceName,
	_In_  PUNICODE_STRING DeviceDosName,
	_Out_ PHANDLE		  DeviceHandle
	)
{
	SC_HANDLE SCManager;
	SC_HANDLE ServiceHandle;
	HANDLE Device;

	if (!DeviceName || !DeviceDosName || !DeviceHandle)
		return STATUS_INVALID_PARAMETER;

	SCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	ServiceHandle = OpenService(SCManager, DeviceName->Buffer, SERVICE_ALL_ACCESS);

	if (ServiceHandle == NULL)
	{
		ServiceHandle = CreateService(SCManager, DeviceName->Buffer, DeviceName->Buffer, 
			SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER,
			SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
			DeviceName->Buffer, NULL, NULL, NULL, NULL, NULL);

		if (ServiceHandle == NULL)
		{
			CloseServiceHandle(SCManager);
			return STATUS_INVALID_HANDLE;
		}
	}

	if (StartService(ServiceHandle, 0, NULL) == FALSE)
	{
		ULONG ErrorCode = GetLastError();
		if (ErrorCode != ERROR_SERVICE_ALREADY_RUNNING)
		{
			DeleteService(ServiceHandle);
			CloseServiceHandle(ServiceHandle);
			CloseServiceHandle(SCManager);
			return NULL;
		}
	}

	Device = CreateFile(DeviceDosName->Buffer, GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
		OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);

	if (Device == INVALID_HANDLE_VALUE)
	{
		SERVICE_STATUS ServiceStatus = { 0 };
		ControlService(ServiceHandle, SERVICE_CONTROL_STOP, &ServiceStatus);
		return STATUS_INVALID_HANDLE;
	}	

	*DeviceHandle = Device;
	CloseServiceHandle(ServiceHandle);
	CloseServiceHandle(SCManager);	

	return STATUS_SUCCESS;
}

_Success_(NT_SUCCESS(return))
NTSTATUS
NTAPI
ZwStopDriver(
	_In_ PUNICODE_STRING DeviceName
	)
{
	SC_HANDLE SCManager;
	SC_HANDLE ServiceHandle;
	NTSTATUS Status;

	if (!DeviceName)
		return STATUS_INVALID_PARAMETER;

	SCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	ServiceHandle = OpenService(SCManager, DeviceName->Buffer, SERVICE_ALL_ACCESS);

	SERVICE_STATUS ServiceStatus = { 0 };
	ControlService(ServiceHandle, SERVICE_CONTROL_STOP, &ServiceStatus);

	Status = STATUS_UNSUCCESSFUL;
	if (DeleteService(ServiceHandle))
		Status = STATUS_SUCCESS;

	CloseServiceHandle(ServiceHandle);
	CloseServiceHandle(SCManager);

	return Status;
}

マニュアルマッピングされたモジュールをマニュアルリンクする。

_Success_(return != NULL)
PTEB 
WINAPI 
GetThreadEnvironmentBlock(
	)
{
#ifdef _WIN64
	return reinterpret_cast<PTEB>(__readgsqword(0x00000030));
#else
	return reinterpret_cast<PTEB>(__readfsdword(0x00000018));
#endif
}

_Success_(return != NULL)
PPEB 
WINAPI 
GetProcessEnvironmentBlock(
	)
{
	PTEB pThreadEnvironmentBlock;

	pThreadEnvironmentBlock = GetThreadEnvironmentBlock();
	if (pThreadEnvironmentBlock)
		return pThreadEnvironmentBlock->ProcessEnvironmentBlock;

	return NULL;
}

_Success_(return != NULL)
PPEB_LDR_DATA 
WINAPI 
GetLoaderData(
	)
{
	PPEB pProcessEnvironmentBlock;
	PPEB_LDR_DATA pLdrData;

	pProcessEnvironmentBlock = GetProcessEnvironmentBlock();
	if (pProcessEnvironmentBlock != NULL)
	{
		pLdrData = pProcessEnvironmentBlock->Ldr;
		if (pLdrData != NULL)
		{
			return pLdrData;
		}
	}

	return NULL;
}

_Success_(return != FALSE)
BOOL 
WINAPI 
AddLoaderEntry(
	_In_ LPCWSTR lpcwszImageName, 
	_In_ LPVOID lpBaseAddress,
	_In_ SIZE_T uSizeOfImage
	)
{
	PLDR_DATA_TABLE_ENTRY pNewEntry;
	PLDR_DATA_TABLE_ENTRY pLastEntry;
	PPEB_LDR_DATA pLdrData;
	PLIST_ENTRY pBaseEntry;
	PIMAGE_NT_HEADERS pNtHeaders;

	pLdrData = GetLoaderData();
	if (!pLdrData)
		return FALSE;

	pLastEntry = reinterpret_cast<PLDR_DATA_TABLE_ENTRY>(pLdrData->InLoadOrderModuleList.Blink);
	if (!pLastEntry)
		return FALSE;

	pNtHeaders = ImageNtHeader(lpBaseAddress);
	if (!pNtHeaders)
		return FALSE;

	pNewEntry = new LDR_DATA_TABLE_ENTRY;
	pNewEntry->InLoadOrderModuleList.Flink = pLastEntry->InLoadOrderModuleList.Flink;
	pNewEntry->InLoadOrderModuleList.Blink = pLastEntry->InLoadOrderModuleList.Blink;
	pNewEntry->InMemoryOrderModuleList = pLastEntry->InMemoryOrderModuleList;
	pNewEntry->InInitializationOrderModuleList = pLastEntry->InInitializationOrderModuleList;
	pNewEntry->BaseAddress = lpBaseAddress;
	pNewEntry->SizeOfImage = uSizeOfImage;
	pNewEntry->EntryPoint = (PVOID)pNtHeaders->OptionalHeader.AddressOfEntryPoint;
	pNewEntry->Flags = pLastEntry->Flags;
	pNewEntry->LoadCount = pLastEntry->LoadCount;
	pNewEntry->TlsIndex = pLastEntry->TlsIndex;
	pNewEntry->HashTableEntry = pLastEntry->HashTableEntry;
	pNewEntry->CheckSum = pNtHeaders->OptionalHeader.CheckSum;
	pNewEntry->TimeDateStamp = pNtHeaders->FileHeader.TimeDateStamp;
	pNewEntry->EntryPointActivationContext = pLastEntry->EntryPointActivationContext;
	pNewEntry->PatchInformation = pLastEntry->PatchInformation;
	pNewEntry->ForwarderLinks = pLastEntry->ForwarderLinks;
	pNewEntry->ServiceTagLinks = pLastEntry->ServiceTagLinks;
	pNewEntry->StaticLinks = pLastEntry->StaticLinks;
	RtlInitUnicodeString(&pNewEntry->FullDllName, (L"C:\\" + std::wstring(lpcwszImageName)).c_str());
	RtlInitUnicodeString(&pNewEntry->BaseDllName, lpcwszImageName);

	pLastEntry->InLoadOrderModuleList.Flink = reinterpret_cast<PLIST_ENTRY>(pNewEntry);

	//Log(L"xmodule: %s, %08X, %08X\n", lpcwszImageName, lpBaseAddress, uSizeOfImage);

	return TRUE;
}

マニュアルマッピングされたシステムモジュールをアンロードして、正規のモジュールにすり替える。

    EXTERN_C
    NTSTATUS
    NTAPI LdrLoadDll(
        _In_  PWCHAR          PathToFile OPTIONAL,
        _In_  ULONG           Flags OPTIONAL,
        _In_  PUNICODE_STRING ModuleFileName,
        _Out_ PHANDLE         ModuleHandle
        );
 
    VOID Detour_LdrLoadDll()
    {
        static decltype(&LdrLoadDll) _LdrLoadDll = LdrLoadDll;
 
        decltype(&LdrLoadDll) LdrLoadDll_Hook = [](
            _In_  PWCHAR          PathToFile OPTIONAL,
            _In_  ULONG           Flags OPTIONAL,
            _In_  PUNICODE_STRING ModuleFileName,
            _Out_ PHANDLE         ModuleHandle) -> NTSTATUS
        {
            IMAGE_EXPORT_DIRECTORY *pExportDirectory;
            NTSTATUS ntStatus;
            HMODULE  hModule;
            HMODULE  hModuleOriginal;
 
            ntStatus = _LdrLoadDll(PathToFile, Flags, ModuleFileName, ModuleHandle);
            if (NT_SUCCESS(ntStatus))
            {
                hModule = reinterpret_cast<HMODULE>(*ModuleHandle);
                if (hModule != NULL)
                {
                    pExportDirectory = ImageExportDirectory(reinterpret_cast<PVOID>(hModule));
                    if (pExportDirectory != NULL)
                    {
                        hModuleOriginal = GetModuleHandleA(reinterpret_cast<LPCSTR>(PBYTE(hModule) + pExportDirectory->Name));
                        if (hModuleOriginal != NULL &&
                            hModuleOriginal != hModule)
                        {
                            // _tprintf(L"%p, %s\n", hModule, ModuleFileName->Buffer);
                            FreeLibrary(hModule);
                            *ModuleHandle = hModuleOriginal;
                        }
                    }                  
                }
            }
 
            return ntStatus;
        };
 
        DetourFunction(TRUE, reinterpret_cast<LPVOID*>(&_LdrLoadDll), LdrLoadDll_Hook);
    }

ウィンドウ検出のBypass

	BOOL Detour_GetWindowThreadProcessId()
	{
		static decltype(&GetWindowThreadProcessId) _GetWindowThreadProcessId = GetWindowThreadProcessId;

		decltype(&GetWindowThreadProcessId) GetWindowThreadProcessId_Hook = [](
			_In_      HWND    hWnd,
			_Out_opt_ LPDWORD lpdwProcessId) -> DWORD
		{
			HMODULE	hModule;
			DWORD	dwStatus;
			DWORD	dwTemp;

			GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast<LPCWSTR>(_ReturnAddress()), &hModule);
			if (hModule == GetModuleHandle(TARGET_MODULE) ||
				hModule == NULL)
			{
				dwStatus = _GetWindowThreadProcessId(hWnd, &dwTemp);
				if (dwTemp != GetCurrentProcessId())
					dwTemp = GetCurrentProcessId();

				return dwStatus;
			}

			return _GetWindowThreadProcessId(hWnd, lpdwProcessId);
		};

		return DetourFunction(TRUE, reinterpret_cast<LPVOID*>(&_GetWindowThreadProcessId), GetWindowThreadProcessId_Hook);
	}

プロセス検出と最小化とそのBypass

プロセス検出&最小化

// ProcessMinimizer.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"

#include <Windows.h>
#include <winternl.h>
#include <ntstatus.h>
#include <Psapi.h>
#pragma comment(lib, "Psapi.lib")
#pragma comment(lib, "ntdll.lib")

EXTERN_C
NTSTATUS 
WINAPI 
NtQuerySystemInformation(
	_In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,
	_Inout_   PVOID                    SystemInformation,
	_In_      ULONG                    SystemInformationLength,
	_Out_opt_ PULONG                   ReturnLength
	);

_Success_(return != NULL)
HWND
WINAPI
GetWindowHandle(
	_In_  DWORD dwProcessId
	)
{
	HWND hWnd = GetTopWindow(NULL);

	do
	{
		if (GetWindowLong(hWnd, GWL_HWNDPARENT) != 0 || !IsWindowVisible(hWnd))
			continue;
	
		DWORD dwTemp = 0;
		GetWindowThreadProcessId(hWnd, &dwTemp);
		if (dwProcessId == dwTemp)
			return hWnd;
	
	} while ((hWnd = GetNextWindow(hWnd, GW_HWNDNEXT)) != NULL);

	return NULL;
}

_Success_(NT_SUCCESS(return))
NTSTATUS
NTAPI
ProcessMinimizer(
	)
{
	NTSTATUS ntStatus;
	SIZE_T nLength;
	PVOID pBuffer;
	SYSTEM_PROCESS_INFORMATION *pProcessInformation;

	ntStatus = NtQuerySystemInformation(SystemProcessInformation, NULL, sizeof(SYSTEM_PROCESS_INFORMATION), &nLength);
	if (ntStatus == STATUS_INFO_LENGTH_MISMATCH)
		pBuffer = VirtualAlloc(NULL, nLength, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

	if (reinterpret_cast<PVOID*>(pBuffer) != NULL)
	{
		ntStatus = NtQuerySystemInformation(SystemProcessInformation, pBuffer, nLength, NULL);
		if (NT_SUCCESS(ntStatus))
		{
			pProcessInformation = reinterpret_cast<SYSTEM_PROCESS_INFORMATION*>(pBuffer);
			while (pProcessInformation != NULL
				&& pProcessInformation->NextEntryOffset > 0)
			{				
				DWORD dwProcessId = reinterpret_cast<DWORD>(pProcessInformation->UniqueProcessId);
				if (dwProcessId != GetCurrentProcessId())
				{
					HWND hWnd = GetWindowHandle(dwProcessId);
					HANDLE hProcess;
					WCHAR szFileName[MAX_PATH];

					if (hWnd != NULL)
					{
						PostMessage(hWnd, WM_SYSCOMMAND, SC_MINIMIZE, NULL);

						hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
						if (hProcess != INVALID_HANDLE_VALUE)
						{
							GetModuleFileNameEx(hProcess, NULL, szFileName, MAX_PATH);
							_tprintf(L"Minimize ProcessID: %08X ProcessName: %s\n", dwProcessId, szFileName);
						}
						else
						{
							_tprintf(L"Minimize ProcessID: %08X ProcessName: Unknown\n", dwProcessId);
						}
					}
				}

				pProcessInformation = reinterpret_cast<SYSTEM_PROCESS_INFORMATION*>(
					reinterpret_cast<PBYTE>(pProcessInformation) + pProcessInformation->NextEntryOffset);
			}			
		}

                VirtualFree(pBuffer, 0, MEM_RELEASE);
	}

	return ntStatus;
}

int main()
{
	ProcessMinimizer();

	getchar();

    return 0;
}

Bypass

BOOL Detour_NtQuerySystemInformation()
{
	static decltype(&NtQuerySystemInformation) _NtQuerySystemInformation = NtQuerySystemInformation;

	decltype(&NtQuerySystemInformation) NtQuerySystemInformation_Hook = [](
		_In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,
		_Inout_   PVOID                    SystemInformation,
		_In_      ULONG                    SystemInformationLength,
		_Out_opt_ PULONG                   ReturnLength) -> NTSTATUS
	{
		NTSTATUS ntStatus;

		ntStatus = _NtQuerySystemInformation(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
		if (NT_SUCCESS(ntStatus))
			if (SystemInformationClass == SystemProcessInformation)
				PSYSTEM_PROCESS_INFORMATION(SystemInformation)->NextEntryOffset = 0;

		return ntStatus;
	};

	return DetourFunction(TRUE, reinterpret_cast<LPVOID*>(&_NtQuerySystemInformation), NtQuerySystemInformation_Hook);
}

ファイルのハッシュ値を取得する。

BOOL CCalcHash::GetMD2(__in LPCSTR lpcszFileName, __inout BYTE pbData[16])
{
	return GetFileHashA(lpcszFileName, pbData, 16, CALG_MD2);
}

BOOL CCalcHash::GetMD4(__in LPCSTR lpcszFileName, __inout BYTE pbData[16])
{
	return GetFileHashA(lpcszFileName, pbData, 16, CALG_MD4);
}

BOOL CCalcHash::GetMD5(__in LPCSTR lpcszFileName, __inout BYTE pbData[16])
{
	return GetFileHashA(lpcszFileName, pbData, 16, CALG_MD5);
}

BOOL CCalcHash::GetSHA1(__in LPCSTR lpcszFileName, __inout BYTE pbData[20])
{
	return GetFileHashA(lpcszFileName, pbData, 20, CALG_SHA1);
}

BOOL CCalcHash::GetMD2(__in LPCWSTR lpcwszFileName, __inout BYTE pbData[16])
{
	return GetFileHashW(lpcwszFileName, pbData, 16, CALG_MD2);
}

BOOL CCalcHash::GetMD4(__in LPCWSTR lpcwszFileName, __inout BYTE pbData[16])
{
	return GetFileHashW(lpcwszFileName, pbData, 16, CALG_MD4);
}

BOOL CCalcHash::GetMD5(__in LPCWSTR lpcwszFileName, __inout BYTE pbData[16])
{
	return GetFileHashW(lpcwszFileName, pbData, 16, CALG_MD5);
}

BOOL CCalcHash::GetSHA1(__in LPCWSTR lpcwszFileName, __inout BYTE pbData[20])
{
	return GetFileHashW(lpcwszFileName, pbData, 20, CALG_SHA1);
}

BOOL CCalcHash::GetFileHashA(__in LPCSTR lpcszFileName, __inout BYTE* pbData, __in DWORD dwLength, __in DWORD dwFlags)
{
	INT nLength = MultiByteToWideChar(CP_ACP, 0, lpcszFileName, -1, NULL, 0);
	if (nLength == 0)
		return FALSE;

	std::unique_ptr<WCHAR> wszBuffer(new WCHAR[nLength]);
	if (MultiByteToWideChar(CP_ACP, 0, lpcszFileName, nLength, wszBuffer.get(), nLength) == 0)
		return FALSE;

	return GetFileHashW(wszBuffer.get(), pbData, dwLength, dwFlags);
}


BOOL CCalcHash::GetFileHashW(__in LPCWSTR lpcwszFileName, __inout BYTE* pbData, __in DWORD dwLength, __in DWORD dwFlags)
{
	BOOL bResult;
	HCRYPTPROV hProv;
	HCRYPTHASH hHash;
	HANDLE hFile;
	ULARGE_INTEGER lSize;
	BYTE pbHash[MAX_HASH_SIZE];

	if (lpcwszFileName == NULL || dwLength == 0 || dwLength > _countof(pbHash))
		return FALSE;

	ZeroMemory(pbHash, dwLength);

	hFile = CreateFile(lpcwszFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
		return FALSE;	

	lSize.LowPart = GetFileSize(hFile, &lSize.HighPart);
	bResult = FALSE;
	hProv = NULL;
	hHash = NULL;

	if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET))
	{
		ULARGE_INTEGER lPos;
		DWORD dwReadBytes;
		BYTE pbTemp[64000]; // 64kb		

		if (CryptCreateHash(hProv, dwFlags, NULL, 0, &hHash))
		{
			lPos.QuadPart = 0;			

			while (TRUE)
			{
				ReadFile(hFile, pbTemp, _countof(pbTemp), &dwReadBytes, NULL);
				if (dwReadBytes <= 0)
					break;

				lPos.QuadPart += dwReadBytes;
				if (!CryptHashData(hHash, pbTemp, dwReadBytes, 0))
					break;

				if (lPos.QuadPart == lSize.QuadPart)
				{
					if (CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &dwLength, 0))
					{
						memcpy_s(pbData, dwLength, pbHash, dwLength);

						if (!bResult)
						{
							bResult = TRUE;
							break;
						}
					}
				}
			}
		}
	}

	if (!bResult) 
		ZeroMemory(pbHash, dwLength);

	if (hHash) 
		CryptDestroyHash(hHash);

	if (hProv) 
		CryptReleaseContext(hProv, 0);

	CloseHandle(hFile);	

	return bResult;
}