読者です 読者をやめる 読者になる 読者になる

めいくりぷとのブログ

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

Wrapper opengl32.dll

// opengl32.dll export function
namespace opengl32
{
	pfnglAlphaFunc	_glAlphaFunc;
	pfnglBegin	_glBegin;
	pfnglBitmap	_glBitmap;
	pfnglBlendFunc	_glBlendFunc;
	pfnglClear	_glClear;
        ....
        ....
}

namespace opengl32
{
	typedef void (WINAPI * pfnglAlphaFunc)(GLenum func, GLclampf ref);
	typedef void (WINAPI * pfnglBegin)(GLenum mode);
	typedef void (WINAPI * pfnglBitmap)(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap);
	typedef void (WINAPI * pfnglBlendFunc)(GLenum sfactor, GLenum dfactor);
	typedef void (WINAPI * pfnglClear)(GLbitfield mask);
        ....
        ....
}

かなり面倒ですが、opengl32.dll のエクスポート関数(360個)を自動生成なりで書いていきます。
自分は自動生成という概念すら思い浮かばなかったので、1つ1つ書いていきましたが...


そして、必要な関数だけ、opengl32.dllのそれに対応したエクスポート関数のポインタを渡します。
で、フックしたい関数を __declspec(dllexport) を使ってやればオーケー


以上

ハードウェアID(PC固有)情報を調べる

Anti-Cheatプロテクターにも HWID BAN というものがありますよね。
今回は、それらを紹介していきたいと思います。


マイナーな手法として、以下のように、NIC(ネットワークインターフェースカード)のGUIDを列挙し、それに対応したMac-Addressを取得するといったものです。

#include "stdafx.h"

#include <NtDDNdis.h>
#include <string>
#include <strsafe.h>
#include <IPHlpApi.h>
#pragma comment(lib, "IPHlpApi.lib")

BOOL WINAPI GetPhysicalMacAddress()
{
	IP_INTERFACE_INFO *pInterface = { 0 };
	HANDLE hDevice;
	DWORD dwLength;

	std::wstring wsBuffer;
	WCHAR szPath[MAX_PATH];
	UCHAR uch[6];
	DWORD dwIoObj = OID_802_3_CURRENT_ADDRESS;

	if (GetInterfaceInfo(pInterface, &dwLength) == ERROR_INSUFFICIENT_BUFFER)
		pInterface = (IP_INTERFACE_INFO *)malloc(dwLength);

	if (GetInterfaceInfo(pInterface, &dwLength) == NO_ERROR)
	{
		for (int i = 0; i < pInterface->NumAdapters; i++)
		{
			wsBuffer = pInterface->Adapter[i].Name;

			StringCchPrintf(szPath, MAX_PATH, TEXT("\\\\.\\%s"), wsBuffer.substr(wsBuffer.size() - 38, 38).c_str());

			hDevice = CreateFile(szPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);

			if (hDevice == INVALID_HANDLE_VALUE)
				continue;

			if (!DeviceIoControl(hDevice, IOCTL_NDIS_QUERY_GLOBAL_STATS, &dwIoObj, sizeof(dwIoObj), &uch, sizeof(uch), &dwLength, NULL))
				continue;

			_tprintf(TEXT("%s - %02X:%02X:%02X:%02X:%02X:%02X\n"), 
				szPath, uch[0], uch[1], uch[2], uch[3], uch[4], uch[5]);
		}

		return TRUE;
	}

	return FALSE;
}

int main()
{
	GetPhysicalMacAddress();

	getchar();

    return 0;
}

↑の実行結果
\\.\{XXXXXXXX-AAAA-BBBB-CCCC-YYYYYYYYYYYY} - AA:BB:CC:DD:EE:FF
というようになります。興味がある方は実行してみてください。


他にも、HDDのシリアルナンバーを取得するものがありますが、かなりメジャーな手法ですので、簡単にbypass出来てしまいます。

API Check

こちらを参考にしてAPI Checkを作ってみました。
mhllchan.hatenablog.com


www.youtube.com

比較元のDLL(copy_ntdll等)をどのように持ってくるかで強度さが変わってくるでしょう。

ラッパーAPI _GetProcAddress

GetProcAddressのラッパーAPI書いてみました。
第二引数に序数を指定した場合は対応出来ていません。

#define EXPORT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]

FARPROC WINAPI _GetProcAddress(
    _In_ HMODULE hModule,
    _In_ LPCSTR  lpProcName)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
    if (hModule == NULL) {
        pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleW(NULL);
    }

    __try {
        if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
            SetLastError(ERROR_BAD_EXE_FORMAT);
            return NULL;
        }

        PIMAGE_NT_HEADERS pNtHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(reinterpret_cast<PBYTE>(pDosHeader) + pDosHeader->e_lfanew);
        if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
            SetLastError(ERROR_INVALID_EXE_SIGNATURE);
            return NULL;
        }
        if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {
            SetLastError(ERROR_EXE_MARKED_INVALID);
            return NULL;
        }

        PIMAGE_EXPORT_DIRECTORY pExportDir = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(reinterpret_cast<PBYTE>(pDosHeader) + pNtHeader->EXPORT_DIRECTORY.VirtualAddress);
        LPDWORD lpdwName = reinterpret_cast<LPDWORD>(reinterpret_cast<PBYTE>(pDosHeader) + pExportDir->AddressOfNames);
        LPDWORD lpdwFunction = reinterpret_cast<LPDWORD>(reinterpret_cast<PBYTE>(pDosHeader) + pExportDir->AddressOfFunctions);
        LPWORD lpwOrdinals = reinterpret_cast<LPWORD>(reinterpret_cast<PBYTE>(pDosHeader) + pExportDir->AddressOfNameOrdinals);

        for (int i = 0; i < pExportDir->NumberOfNames; i++) {
            WORD wOrdinal = lpwOrdinals[i];
            LPSTR pszName = reinterpret_cast<LPSTR>(reinterpret_cast<PBYTE>(pDosHeader) + lpdwName[i]);
            LPVOID lpvFunction = reinterpret_cast<LPVOID>(reinterpret_cast<PBYTE>(pDosHeader) + lpdwFunction[wOrdinal]);

            if (strcmp(pszName, lpProcName) == 0) {
                SetLastError(NO_ERROR);
                return (FARPROC)lpvFunction;
            }
        }

        SetLastError(ERROR_INVALID_DATA);
        return NULL;
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        SetLastError(ERROR_EXE_MARKED_INVALID);
        return NULL;
    }
}

PEBを用いたモジュール隠蔽 + α

PEBを理解するために適当に書いたコードです。

///////////////////////////////////////////////////////////
// peb.hpp
///////////////////////////////////////////////////////////
#pragma once
 
#ifndef Padding
#define Padding(x) struct { unsigned char __padding##x[(x)]; };
#endif
 
typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
 
typedef struct _PEB_LDR_DATA
{
    Padding(0x0C);
    LIST_ENTRY InLoadOrderModuleList; // 0x0c
    LIST_ENTRY InMemoryOrderModuleList; // 0x14
    LIST_ENTRY InInitializationOrderModuleList; // 0x1c
}PEB_LDR_DATA, PPEB_LDR_DATA;
 
typedef struct _PEB
{
#ifdef _WIN64
    Padding(0x18);
#else
    Padding(0x0C);
#endif
    PEB_LDR_DATA* Ldr;
}PEB, *PPEB;
 
typedef struct _LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY            InLoadOrderModuleList;
    LIST_ENTRY            InMemoryOrderModuleList;
    LIST_ENTRY            InInitializationOrderModuleList;
    PVOID                 BaseAddress;
    PVOID                 EntryPoint;
    ULONG                 SizeOfImage;
    UNICODE_STRING        FullDllName;
    UNICODE_STRING        BaseDllName;
    ULONG                 Flags;
    USHORT                LoadCount;
    USHORT                TlsIndex;
    LIST_ENTRY            HashTableEntry;
    union
    {
        LIST_ENTRY HashLinks;
        struct
        {
            PVOID SectionPointer;
            ULONG CheckSum;
        };
    };
    union
    {
        ULONG TimeDateStamp;
        PVOID LoadedImports;
    };
    _ACTIVATION_CONTEXT *   EntryPointActivationContext;
    PVOID                   PatchInformation;
    LIST_ENTRY              ForwarderLinks;
    LIST_ENTRY              ServiceTagLinks;
    LIST_ENTRY              StaticLinks;
    PVOID                   ContextInformation;
    DWORD                   OriginalBase;
    LARGE_INTEGER           LoadTime;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
///////////////////////////////////////////////////////////
// dllmain.cpp
///////////////////////////////////////////////////////////
 
#include "stdafx.h"
 
#include "peb.hpp"
#include <algorithm>
 
#define UNLINK(x)                   \
    (x).Flink->Blink = (x).Blink;   \
    (x).Blink->Flink = (x).Flink;
 
// hide module
VOID WINAPI HideModule(__in HMODULE hModule)
{
#ifdef _WIN64
    PEB *pPEB = reinterpret_cast<PEB*>(__readfsqword(0x60));
#else
    PEB *pPEB = reinterpret_cast<PEB*>(__readfsdword(0x30));
#endif
 
    PLDR_DATA_TABLE_ENTRY pData;
    PLIST_ENTRY           pEntry;
 
    pEntry = pPEB->Ldr->InLoadOrderModuleList.Flink;
 
    while (pEntry != &pPEB->Ldr->InLoadOrderModuleList &&
        pEntry != NULL)
    {
        pData = CONTAINING_RECORD(pEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList);
 
        if (pData->BaseAddress == hModule)
        {
#ifdef _DEBUG
            _tprintf(TEXT("\nDll name: %s \nBase: %08X \nEntryPoint: %08X \n"),
                pData->BaseDllName.Buffer, pData->BaseAddress, pData->EntryPoint);
#endif
 
            UNLINK(pData->InLoadOrderModuleList);
            UNLINK(pData->InInitializationOrderModuleList);
            UNLINK(pData->InMemoryOrderModuleList);
        }
 
        pEntry = pEntry->Flink;
    }
}
 
// remove pe header
VOID WINAPI EraseHeaders(__in HMODULE hModule)
{
    DWORD i, size, protect;
 
    IMAGE_DOS_HEADER *pDosHeader = PIMAGE_DOS_HEADER(hModule);
    IMAGE_NT_HEADERS *pNtHeaders = reinterpret_cast<IMAGE_NT_HEADERS*>(reinterpret_cast<PBYTE>(hModule) + pDosHeader->e_lfanew);
 
    size = sizeof(IMAGE_DOS_HEADER);
 
    if (VirtualProtect(pDosHeader, size, PAGE_READWRITE, &protect))
    {
        for (i = 0; i < size; i++)
            *(BYTE*)((BYTE*)pDosHeader + i) = 0x00;
    }
 
    size = sizeof(IMAGE_NT_HEADERS);
 
    if (pNtHeaders && VirtualProtect(pNtHeaders, size, PAGE_READWRITE, &protect))
    {
        for (i = 0; i < size; i++)
            *(BYTE*)((BYTE*)pNtHeaders + i) = 0x00;
    }
}

終わり。

WDDM Hook

Windows Display Driver Model (WDDM) hook について適当に書きます。

前記事でOpenGL ICD hookについて書きましたが、ほとんど同じです。
今回も大雑把に説明します。


まずは、各ディスプレイドライバのエクスポート関数 "OpenAdapter" をフックしします。

NVIDIA GeForce => nvd3dum.dll
Radeon => aticfx32.dll
Intel Graphics => igdumd32.dll

OpenAdapterの第一引数の D3DDDIARG_OPENADAPTER 構造体から pOpenData->pAdapterFuncs->pfnCreateDevice のアドレスを取得し、そのアドレスを元に pfnCreateDevice をフックします。

pfnCreateDevice(HANDLE hDevice, D3DDDIARG_CREATEDEVICE *pDeviceData);

第二引数の D3DDDIARG_CREATEDEVICE 構造体の D3DDDI_DEVICEFUNS *pDeviceFuncs がvtableへのポインタになりますので、
pfnCreateDevice のフックコード内で取得し、vtable内の各関数をフックします。
以下にサンプルソースを貼っておきます。

BOOL WINAPI Detour_OpenAdapter(__in BOOL fEnable);

HRESULT APIENTRY CreateDevice(
	__in HANDLE hAdapter,
	__inout D3DDDIARG_CREATEDEVICE *pDeviceData)
{
	CDriverManager *pDriver = CDriverManager::GetInstance();
	HRESULT hResult;

	// create driver context
	hResult = pDriver->CreateDevice(hAdapter, pDeviceData);
	if (hResult == S_OK
		&& pDeviceData
		&& pDeviceData->pDeviceFuncs)
	{
		// valid device funcs
		if (pDriver->IsValidDeviceFuncs() == FALSE)
		{
			// copy device funcs
			pDriver->CopyDeviceFuncs(*pDeviceData->pDeviceFuncs);
			if (pDriver->IsValidDeviceFuncs())
			{
				// init graphics context & swap device funcs function table
				CGraphicsContext::GetInstance()->Init(pDeviceData->pDeviceFuncs);

				// detour remove
				Detour_OpenAdapter(FALSE);
			}
		}
	}

	return hResult;
}

BOOL WINAPI Detour_OpenAdapter(__in BOOL fEnable)
{
	typedef HRESULT(APIENTRY *pfnOpenAdapter)(
		__inout D3DDDIARG_OPENADAPTER *pOpenData
		);

	static pfnOpenAdapter _OpenAdapter =
		reinterpret_cast<pfnOpenAdapter>(GetProcAddress(CDriverManager::GetInstance()->GetDriver(), "OpenAdapter"));

	pfnOpenAdapter OpenAdapter_Hook = [](
		__inout D3DDDIARG_OPENADAPTER *pOpenData) -> HRESULT
	{
		CDriverManager *pDriver;
		LPVOID pDevice;
		HRESULT hResult;

		hResult = _OpenAdapter(pOpenData);
		if (hResult == S_OK 
			&& pOpenData 
			&& pOpenData->pAdapterFuncs)
		{
			pDevice = pOpenData->pAdapterFuncs->pfnCreateDevice;
			if (pDevice)
			{
				pDriver = CDriverManager::GetInstance();
				pDriver->SetDevice(reinterpret_cast<PFND3DDDI_CREATEDEVICE>(pDevice));
				if (pDriver->IsValidDevice())
					pOpenData->pAdapterFuncs->pfnCreateDevice = CreateDevice;
			}
		}

		return hResult;
	};

	return DetourFunction(fEnable, reinterpret_cast<LPVOID*>(&_OpenAdapter), OpenAdapter_Hook);
}
#include "stdafx.h"

#include "CDriverManager.hpp"
#include "D3DHeaders.hpp"

#include <ShlObj.h>

CDriverManager::CDriverManager()
{
	this->m_hDriver = NULL;
	this->m_pfnCreateDevice = NULL;
	this->m_pDeviceFuncs = NULL;

	this->m_vDriver.push_back(L"nvd3dum.dll");		// NVIDIA GeForce
	this->m_vDriver.push_back(L"aticfx32.dll");		// Radeon
	this->m_vDriver.push_back(L"igdumd32.dll");		// Intel Graphics
}

CDriverManager::~CDriverManager()
{
	this->m_vDriver.clear();
}

BOOL CDriverManager::Init()
{
	for (int i = 0; i < this->m_vDriver.size(); i++)
	{
		m_hDriver = this->LoadDriver(this->m_vDriver.at(i));
		if (m_hDriver)
			return TRUE;
	}

	return FALSE;
}

BOOL CDriverManager::IsValidDevice()
{
	return (this->m_pfnCreateDevice != NULL);
}

BOOL CDriverManager::IsValidDeviceFuncs()
{
	return (this->m_pDeviceFuncs != NULL);
}

void CDriverManager::SetDevice(__in PFND3DDDI_CREATEDEVICE pDevice)
{
	this->m_pfnCreateDevice = pDevice;
}

D3DDDI_DEVICEFUNCS *CDriverManager::GetDeviceFuncs()
{
	return this->m_pDeviceFuncs;
}

void CDriverManager::CopyDeviceFuncs(__in const D3DDDI_DEVICEFUNCS pDevice)
{
	this->m_pDeviceFuncs = new D3DDDI_DEVICEFUNCS(pDevice);
}

HRESULT CDriverManager::CreateDevice(__in HANDLE hAdapter, __inout D3DDDIARG_CREATEDEVICE *pDeviceData)
{
	return this->m_pfnCreateDevice(hAdapter, pDeviceData);
}

HMODULE CDriverManager::GetDriver()
{
	return this->m_hDriver;
}

HMODULE CDriverManager::LoadDriver(__in LPCWSTR lpFileName)
{
	HMODULE hModule = GetModuleHandle(lpFileName);

	if (!hModule)
	{
		TCHAR szPath[MAX_PATH];

		if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_SYSTEMX86, NULL, 0, szPath)))
			return NULL;

		if (!SUCCEEDED(StringCchPrintf(szPath, MAX_PATH, TEXT("%s\\%s"), szPath, lpFileName)))
			return NULL;

		hModule = LoadLibrary(szPath);

		if (!hModule)
			return NULL;
	}

	return hModule;
}

以上。

OpenGL ICD Hook

OpenGL ICD hook (グラフィックドライバフック)が完成したので手法を大雑把に説明します。

まずは、これらのAPIからICDを取得します。
GDI32.D3DKMTOpenAdapterFromHdc
GDI32.D3DKMTQueryAdapterInfo
GDI32.D3DKMTCloseAdapter

BOOL CDriverManager::Init()
{
	HDC hDC = GetDC(GetDesktopWindow());
	if (!hDC)
		return FALSE;

	HMODULE hGDI32 = this->LoadDriver(_T("gdi32.dll"), CSIDL_SYSTEM);
	if (hGDI32 == NULL)
		return FALSE;

	this->m_pfnD3DKMTOpenAdapterFromHdc = reinterpret_cast<pfnD3DKMTOpenAdapterFromHdc>(GetProcAddress(hGDI32, "D3DKMTOpenAdapterFromHdc"));
	this->m_pfnD3DKMTQueryAdapterInfo = reinterpret_cast<pfnD3DKMTQueryAdapterInfo>(GetProcAddress(hGDI32, "D3DKMTQueryAdapterInfo"));
	this->m_pfnD3DKMTCloseAdapter = reinterpret_cast<pfnD3DKMTCloseAdapter>(GetProcAddress(hGDI32, "D3DKMTCloseAdapter"));

	if (this->m_pfnD3DKMTOpenAdapterFromHdc == NULL
		|| this->m_pfnD3DKMTQueryAdapterInfo == NULL
		|| this->m_pfnD3DKMTCloseAdapter == NULL)
		return FALSE;

	D3DKMT_OPENADAPTERFROMHDC HdcAdapter		= { 0 };
	D3DKMT_CLOSEADAPTER		  CloseAdapter		= { 0 };
	D3DKMT_QUERYADAPTERINFO	  QueryAdapterInfo  = { 0 };
	D3DKMT_OPENGLINFO		  OpenGLInfo		= { 0 };
	WCHAR wszFilename[MAX_PATH];

	HdcAdapter.hDc = hDC;

	if (NT_SUCCESS(this->m_pfnD3DKMTOpenAdapterFromHdc(&HdcAdapter)))
	{
		QueryAdapterInfo.hAdapter	= HdcAdapter.hAdapter;
		QueryAdapterInfo.Type		= KMTQAITYPE_UMOPENGLINFO;
		QueryAdapterInfo.pPrivateDriverData		= &OpenGLInfo.UmdOpenGlIcdFileName;
		QueryAdapterInfo.PrivateDriverDataSize	= sizeof(D3DKMT_OPENGLINFO);

		if (NT_SUCCESS(this->m_pfnD3DKMTQueryAdapterInfo(&QueryAdapterInfo)))
		{
			CloseAdapter.hAdapter = HdcAdapter.hAdapter;

			this->m_pfnD3DKMTCloseAdapter(&CloseAdapter);

			StringCchPrintf(wszFilename, MAX_PATH, _T("%s"), QueryAdapterInfo.pPrivateDriverData);

			this->m_hDriver = this->LoadDriver(wszFilename, CSIDL_SYSTEMX86);
			if (this->m_hDriver)
				return TRUE;
		}
	}

	return FALSE;
}

取得したICD名から、GetModuleHandleまたはLoadLibraryでモジュールハンドルを取得します。
ICDのモジュールハンドルを元に DrvSetContext をフックし、glDispatchTable を取得します。その後にglDispatchTableの各関数をフックします。

VOID Detour_DrvSetContext(__in BOOL bEnable);

typedef PGLCLTPROCTABLE(GLAPIENTRY *pfnDrvSetContext)(_In_ HDC hdc, _In_ DHGLRC dhglrc, _In_ PFN_SETPROCTABLE pfnSetProcTable);
static pfnDrvSetContext _DrvSetContext = NULL;
static pfnDrvSetContext DrvSetContext_Hook = [](
	_In_ HDC hdc, 
	_In_ DHGLRC dhglrc, 
	_In_ PFN_SETPROCTABLE pfnSetProcTable) -> PGLCLTPROCTABLE
{
	CGraphicsContext *pGraphicsContext;
	GLCLTPROCTABLE *pProcTable;
	
	pProcTable = _DrvSetContext(hdc, dhglrc, pfnSetProcTable);
	if (pProcTable != NULL)
	{
		pGraphicsContext = CGraphicsContext::GetInstance();
		if (pGraphicsContext->IsValidDispatchTable() == FALSE)
		{
			pGraphicsContext->SetDispatchTable(pProcTable->glDispatchTable);
			if (pGraphicsContext->Init(pProcTable))
			{
				// detour remove
				Detour_DrvSetContext(FALSE);
			}
		}
	}

	return pProcTable;
};

VOID Detour_DrvSetContext(__in BOOL bEnable)
{
	if (!_DrvSetContext)
		_DrvSetContext = reinterpret_cast<pfnDrvSetContext>(
			GetProcAddress(CDriverManager::GetInstance()->GetDriver(), "DrvSetContext"));

	DetourFunction(bEnable, reinterpret_cast<LPVOID*>(&_DrvSetContext), DrvSetContext_Hook);
}
 
VOID Initialize(__in HINSTANCE hInstance)
{
	DisableThreadLibraryCalls(hInstance);

	if (CDriverManager::GetInstance()->Init())
	{
		Detour_DrvSetContext(TRUE);

		int args = 0;
		QApplication app(args, NULL);
		CMainWindow mainWindow;
		mainWindow.show();
		app.exec();
	}

	FreeLibraryAndExitThread(hInstance, 0);
}