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

めいくりぷとのブログ

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

WDDM Hook (2)

Direct3D DDIは、OpenGL ICDとは違って、グラフィックドライバ自体にテーブルは無くd3dx.dllにドライバのテーブルがあるみたいですね...(ICD同様にグラフィックドライバ自体にテーブルが用意されているものだと勘違いしておりました。)


まずDDIの関数テーブル自体が、グラフィックドライバのテーブル関数(?) CreateDevice関数 によって初期化されますし、同じ所にあるものだと普通思いますよね...


一見どうでも良いようにも思えますが...
少し前のプロテクターにはD3D関数テーブルの整合性チェックがあっても、ドライバの関数テーブルのチェックは殆ど無かったため問題はありませんでした。ですが、最近のものはd3dx.dll自体にメモリチェックがあったりするので、どうでも良いとは言い切れません。


どうにかグラフィックドライバ自体に存在するドライバ関数?だけで完結したいですよね..


一応、DDIの各ドライバ関数の第一引数 HANDLE hDeviceにDisplay Device(関数テーブル?)のポインタが格納されており、それに対応した関数が呼ばれているので、何とかなるような気もしますけどね。


また何か進展があれば記事にしたいと思います。以上


追記:
よくよく考えれば、D3D DDIの各ドライバ関数をフックしなくても、各ドライバ関数へのテーブル自体をすり替えてしまえば、ポインタチェック?とかはされない限りは、CRCがあっても問題ありません。(.exeは別みたいですが。)


関数テーブル上の関数は、asm上では call dword ptr [eax+04] とかで呼ばれため、
テーブル自体の書き換えは、直接メモリを書き換える訳でもないので検出が難しいみたいですね。
出来るとすれば、関数ポインタ先が、メモリ内or外、またはBase Relocation (.dll+XXXX) などが挙げられるようです。


他にもポインタ自体のチェックがありますが、プロテクタがそのポインタを読み込む前に書き換えてしまえば難なく突破出来てしまいます。
良く海外フォーラム等にWallhackなどが落ちていますよね。モノによってはプロテクタの有無に関わらず、そのまま使えたりするものもありますよね。そうです、そういうことです。


少し話が逸れますが、関数テーブルの書き換えによって行うチートの検出は難しいと思いました。


更に追記:
冒頭にグラフィックドライバ自体に関数テーブルが用意されてないだとか書きましたが、OpenAdapterはグラフィックドライバの関数テーブルの他に、D3Dの何らかの関数テーブルの初期化時にも呼ばれることが分かりました。
今までは決め打ちでやっていたため、D3Dの何らかの関数テーブルの方を取得してたみたいです。

ウィンドウフレームを消して、ウィンドウを移動出来るようにする。

eventFilter(QObject* object, QEvent* event); をオーバーライドし、
event->type() から各マウスの状態から制御します。以下ソースコードです。

CMainWindow::CMainWindow(__in QWidget *parent) : QMainWindow(parent)
{
    ....
    this->installEventFilter(this);                 // eventFilterを有効にする
    this->setWindowFlags(Qt::FramelessWindowHint);  // ウィンドウの枠?を消す
    ....
}
 
bool CMainWindow::eventFilter(QObject* object, QEvent* event)
{
    if (this->m_mousePressed)
    {
        if (event->type() == QEvent::MouseMove)
        {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
            this->m_currentPos = mouseEvent->pos();
            this->move(
                this->pos().x() + this->m_currentPos.x() - this->m_originPos.x(),
                this->pos().y() + this->m_currentPos.y() - this->m_originPos.y());
        }
 
        if (event->type() == QEvent::MouseButtonRelease)
        {
            this->m_mousePressed = false;
        }
    }
    else
    {
        if (event->type() == QEvent::MouseButtonPress)
        {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
            this->m_originPos = mouseEvent->pos();
            this->m_mousePressed = true;
        }
    }
 
    return false;
}

Auto Login

static CLogin::UpdateT _CLogin__Update =
reinterpret_cast<CLogin::UpdateT>(ADDRESS_CLogin__Update);
void(__fastcall *CLogin__Update__Hook)(CLogin *) = [](
	CLogin *pLogin) -> void
{
	CAutoHack::GetInstance()->ProcessAutoLogin(pLogin);

	return _CLogin__Update(pLogin);
};

BOOL CAutoHack::ProcessAutoLogin(CLogin *pLogin)
{
	static DWORD start_time = 0;

	if (!this->m_bToggleAutoLogin)
		return FALSE;

	if (start_time == 0)
		start_time = GetTickCount();

	// 3000 ms
	if (GetTickCount() > start_time + 3000)
	{
		start_time = 0;

		if (pLogin->m_nLoginStep == 0)
		{
			CUITitle *pUITitle = CUITitle::GetInstance();
			if (!pUITitle)
				return FALSE;

			pUITitle->m_pEditID.p->SetText(this->m_sLoginID.c_str());
			pUITitle->m_pEditPasswd.p->SetText(this->m_sLoginPW.c_str());
			pUITitle->CFadeWnd.IGObj.vfptr->SetRet(&pUITitle->CFadeWnd.IGObj, 1);
		}
		else if (pLogin->m_nLoginStep == 1)
		{
			pLogin->SendLoginPacket(this->m_nWorldID, this->m_nChannelID, FALSE);
		}
		else if (pLogin->m_nLoginStep == 2)
		{
			if (pLogin->m_nCharCount < 1)
				return FALSE;

			for (int i = 0; i < pLogin->m_nCharCount; i++)
			{
				AvatarData *pData = pLogin->GetSelectedAvatarData();
				if (!pData)
					return FALSE;

				if (strcmp(pData->characterStat.sCharacterName, this->m_sLoginCharacter.c_str()) == 0)
				{
					pLogin->m_nCharSelected = i;
					pLogin->m_bOfflineMode = this->m_bOfflineMode;
					pLogin->m_sSPW.Assign(this->m_sLoginSPW.c_str());
                                        return TRUE;
				}
			}
		}
	}

	return FALSE;
}

^o^

NIC Mac Address Authentication

// CAuthentication.hpp
#pragma once

#include <vector>

class CAuthentication
{
public:
	CAuthentication();
	~CAuthentication();

public:
	BOOL IsUserAllowed();
	void SetWhiteList();

public:
	static CAuthentication *GetInstance()
	{
		static CAuthentication instance;
		return &instance;
	}

private:
        void RegisterMACAddress(LPCWSTR lpcwszAddress);

	std::vector<LPCWSTR> m_vData;
};
// CAuthentication.cpp
#include "stdafx.h"

#include "CAuthentication.hpp"
#include "utils.hpp"

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

CAuthentication::CAuthentication()
{

}
CAuthentication::~CAuthentication()
{

}

void CAuthentication::SetWhiteList()
{
	this->RegisterMACAddress(L"AA:BB:CC:DD:EE:FF");
}

BOOL CAuthentication::IsUserAllowed()
{
	IP_INTERFACE_INFO *pInterface;
	HANDLE hDevice;
	ULONG uLength;
	UCHAR uResult[6];
	WCHAR wszAdapter[MAX_PATH];
	LPVOID lpEthernet = (LPVOID)OID_802_3_CURRENT_ADDRESS;

	if (GetInterfaceInfo(NULL, &uLength) == ERROR_INSUFFICIENT_BUFFER)
	{
		pInterface = (IP_INTERFACE_INFO*)malloc(uLength);

		if (GetInterfaceInfo(pInterface, &uLength) == NO_ERROR)
		{
			for (int i = 0; i < pInterface->NumAdapters; i++)
			{
                                // NIC GUID
				std::wstring wszBuffer, wszTemp;
				wszBuffer = pInterface->Adapter[i].Name;
				wszBuffer = L"\\\\.\\" + wszBuffer.substr(wszBuffer.find_first_of('{'), wszBuffer.size());

                                // Open NIC from GUID
				hDevice = CreateFile(wszBuffer.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
				if (hDevice == INVALID_HANDLE_VALUE)
					return FALSE;

                                // Get Physical Mac-Address
				if (!DeviceIoControl(hDevice, IOCTL_NDIS_QUERY_GLOBAL_STATS,
					&lpEthernet, sizeof(lpEthernet), &uResult, sizeof(uResult), NULL, NULL))
					return FALSE;

				for (int i = 0; i < 6; i++)
					StringCchPrintf(wszAdapter, _countof(wszAdapter), _T("%s%02X:"), wszAdapter, uResult[i]);

				wszTemp = wszAdapter;
				wszTemp = wszTemp.substr(0, wszTemp.size() - 1);
				if (this->m_vData.size() > 0)
				{
					for (int i = 0; i < this->m_vData.size(); i++)
					{
						if (wcscmp(this->m_vData.at(i), wszTemp.c_str()) == NO_ERROR)
						{
							return TRUE;
						}
					}
				}
			}
		}
	}

	return FALSE;
}

void CAuthentication::RegisterMACAddress(LPCWSTR lpcwszAddress)
{
	this->m_vData.push_back(lpcwszAddress);
}
// main.cpp
#include "stdafx.h"

#include "CAuthentication.hpp"

int main()
{
        CAuthentication *pAuthentication = CAuthentication::GetInstance();
        pAuthentication->SetWhiteList();
        if (!pAuthentication->IsUserAllowed())
        {
                MessageBox(NULL, L"Authentication failed. ", L"License Error", MB_OK);
                TerminateProcess(GetCurrentProcess(), -1);
        }

        MessageBox(NULL, L"Authentication succeeded! ", L"License", MB_OK);

        return 0;
}

Invisible MobFilter

JMS v357.1

Invisible MobFilter

#define ADDRESS_MobFilter1 0x0102C580
#define ENDADDR_MobFilter1 0x0102C72E
#define ADDRESS_MobFilter2 0x0102CA71
#define ENDADDR_MobFilter2 0x0102CD56 

DWORD MobFilter1_Ret = ADDRESS_MobFilter1 + 6;
DWORD MobFilter1_End = ENDADDR_MobFilter1;
void __declspec(naked) MobFilter1_Asm()
{
	__asm
	{
		cmp eax,9999999
		jne original
		jmp dword ptr [MobFilter1_End]

	original:
		mov [esp+0x18],eax
		test esi, esi
		jmp dword ptr [MobFilter1_Ret]
	};
}

DWORD MobFilter2_Ret = ADDRESS_MobFilter2 + 5;
DWORD MobFilter2_End = ENDADDR_MobFilter2;
void __declspec(naked) MobFilter2_Asm()
{
	__asm
	{
		cmp eax,9999999
		jne original
		jmp dword ptr[MobFilter2_End]

	original:
		mov ebp, eax
		push esi
		mov ecx, ebx
		jmp dword ptr[MobFilter2_Ret]
	}
}

^o^

Config

youtu.be

gyazo.com

std::map で名前をキーとして、QWidgetのインスタンス保存 -> ifstream/ofstreamでread/writeやってます。
connect書いておけば、signal飛ぶから自動で更新されるとかなんとか、、、便利すぎて感動しました。。。それだけです。

Portal Teleport

いろいろと雑

Edited

enum PORTALTYPE
{
	PORTALTYPE_STARTPOINT = 0x0,
	PORTALTYPE_INVISIBLE = 0x1,
	PORTALTYPE_VISIBLE = 0x2,
	PORTALTYPE_COLLISION = 0x3,
	PORTALTYPE_CHANGABLE = 0x4,
	PORTALTYPE_CHANGABLE_INVISIBLE = 0x5,
	PORTALTYPE_TOWNPORTAL_POINT = 0x6,
	PORTALTYPE_SCRIPT = 0x7,
	PORTALTYPE_SCRIPT_INVISIBLE = 0x8,
	PORTALTYPE_COLLISION_SCRIPT = 0x9,
	PORTALTYPE_HIDDEN = 0xA,
	PORTALTYPE_SCRIPT_HIDDEN = 0xB,
	PORTALTYPE_COLLISION_VERTICAL_JUMP = 0xC,
	PORTALTYPE_COLLISION_CUSTOM_IMPACT = 0xD,
	PORTALTYPE_SCRIPT_INVISIBLE_CHANGEABLE = 0xE,
	PORTALTYPE_COLLISION_CUSTOM_IMPACT2 = 0xF,
	PORTALTYPE_NONE = 0xFFFFFFFF,
};

namespace MapleStory
{
	void TeleportToPoint(CUserLocal *pUserLocal, POINT pt)
	{
		if (!pUserLocal)
			return;

		CVecCtrl *pVecCtrl = pUserLocal->User.VecCtrl.vfptr->GetVecCtrl(&pUserLocal->User.VecCtrl);
		pVecCtrl->IWzVector2D.vfptr->raw_Move(&pVecCtrl->IWzVector2D, pt.x, pt.y);
	}

	BOOL find_portal(CPortalList *pPortalList, LPPOINT lppt)
	{
		static int nIdx = 0;

		int nCount = 0;
		ZRef<PORTAL> *ref;
		PORTAL *pPortal;

		if (!pPortalList)
			return FALSE;

		ref = pPortalList->m_aPortal.a;
		if (ref)
			ref = (ZRef<PORTAL> *)ref[-1].p;

		nCount = (int)((char *)&ref[-1].p + 3);
		if (nCount < 0)
			return FALSE;

		while (pPortal = pPortalList->m_aPortal.a[nIdx].p)
		{
			nIdx += 1;
			if (nIdx > nCount)
				break;

			if (pPortal->nType == PORTALTYPE_VISIBLE ||
				pPortal->nType == PORTALTYPE_SCRIPT)
			{
				lppt->x = pPortal->ptPos.x;
				lppt->y = pPortal->ptPos.y;
				return TRUE;
			}
		}

		nIdx = 0;

		return FALSE;
	}

}

/////////////////////////////////////////////////////////////////////////////////

#ifndef _MapVirtualKey
#define _MapVirtualKey(uCode) (MapVirtualKey(uCode, MAPVK_VK_TO_VSC) << 16)
#endif

static CUserLocal::UseFuncKeyMappedT _UseFuncKeyMapped =
reinterpret_cast<CUserLocal::UseFuncKeyMappedT>(ADDRESS_UseFuncKeyMapped);
BOOL(__fastcall *UseFuncKeyMapped__Hook)(CUserLocal *, LPVOID, LPARAM, WPARAM) = [](
	CUserLocal *pUserLocal, LPVOID lpvEdx, LPARAM lParam, WPARAM wParam) -> BOOL
{
	if (lParam == _MapVirtualKey(VK_BACK)) // backspace
	{
                POINT pt;

		if (MapleStory::find_portal(CPortalList::GetInstance(), &pt))
                {
                        MapleStory::TeleportToPoint(pUserLocal, pt);
                }
	}

        return _UseFuncKeyMapped(pUserLocal, lParam, wParam);
}

...
CUserLocal *pUserLocal = CUserLocal::GetInstance();
if (pUserLocal)
{
        pUserLocal->UseFuncKeyMapped(_MapVirtualKey(VK_BACK), VK_BACK);
}
...

^-^