More Related Content
Similar to 怪しいWindowsプログラミング
Similar to 怪しいWindowsプログラミング (20)
怪しいWindowsプログラミング
- 2. 自己紹介
● 隅須三郎(@nagoya313)
● C++初心者
●
Windows系男子
● 名古屋出身/在住の3月13日生まれという
わけではない
- 3. 今日の内容
● 適当に作ったプログラムを元ソースを弄ら
ずに改造する
➔
Win32APIとDirect3D9製のC++プログラム
➔ 外部からの拡張は考慮せずに作成
- 4. 元となるプログラムの概要
● エントリポイント
int WINAPI WinMain(HINSTANCE hInst,
HINSTANCE hPrevInst,
LPSTR cmdLine,
int cmdShow) {
InitWindow();
InitDirect3D9();
ShowWindow(g_hWnd, cmdShow);
return Loop();
}
- 5. 元となるプログラムの概要
● ウィンドウクラスの登録とウィンドウ作成
void InitWindow() {
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
wc.lpszClassName = kClassName;
RegisterClassEx(&wc);
g_hWnd = CreateWindow(kClassName, L"やみなべ!",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
nullptr,nullptr,
GetModuleHandle(nullptr), nullptr);
}
- 6. 元となるプログラムの概要
● Direct3D9の初期化
void InitDirect3D9() {
g_pD3d9 = Direct3DCreate9(D3D_SDK_VERSION);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.BackBufferWidth = 640;
d3dpp.BackBufferHeight = 480;
g_pD3d9->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
g_hWnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING,
&d3dpp,
&g_pDevice);
D3DXCreateTextureFromFile(g_pDevice, L"d3.png", &g_pTexture);
}
- 7. 元となるプログラムの概要
● メッセージループ
int Loop() {
for (;;) {
MSG msg;
if (PeekMessage (&msg, nullptr, 0, 0, PM_NOREMOVE)) {
auto ret = GetMessage (&msg, nullptr, 0, 0);
if (!ret || ret == -1) {
g_pD3d9->Release();
g_pDevice->Release();
return msg.wParam ;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
} else {
Update();
}
}
}
- 8. 元となるプログラムの概要
● 1Fでの処理(黒で消して画像を表示)
struct VertexImage2D {
float x;
float y;
float z;
float rhw;
float u;
float v;
};
void Update() {
g_pDevice->BeginScene();
g_pDevice->Clear(0, nullptr, D3DCLEAR_TARGET,
D3DCOLOR_XRGB(0, 0, 0), 1.f, 0);
VertexImage2D vertex[4] = {
{0.f, 0.f, 0.f, 1.f, 0.f, 0.f},
{162.f, 0.f, 0.f, 1.f, 1.f, 0.f},
{0.f, 309.f, 0.f, 1.f, 0.f, 1.f},
{162.f, 309.f, 0.f, 1.f, 1.f, 1.f}
};
g_pDevice->SetTexture(0, g_pTexture);
g_pDevice->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
g_pDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, vertex,
sizeof(vertex[0]));
g_pDevice->EndScene();
g_pDevice->Present(nullptr, nullptr, nullptr, nullptr);
}
- 9. 元となるプログラムの概要
● ウィンドウプロシージャ(×を押したら終了)
LRESULT WINAPI WndProc(HWND hWnd,
UINT msg,
WPARAM wp,
LPARAM lp) {
if (msg == WM_DESTROY) {
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wp, lp);
}
- 10. DLL(Dynamic Link Library)
の裏話
● Windowsだとexeがあるディレクトリに同名
のdllがあるとSystem32とかにあるものより
も優先的に読み込まれる
➔ こっそり偽者を置いておけばプログラムが
偽者を勝手に読んでくれる
- 11. d3d9.dllの偽物を作る
● d3d9.dllの偽者を置いておけば勝手にプロ
グラムが読み込む
● 使われる関数がDirect3DCreate9だけなの
で簡単に作れる
● プログラムが読み込む関数はいい加減に
作ったら残当する
➔ 関数名とか引数の数とか呼び出し規約と
かはきっちりする必要がある
- 13. d3d9.dllの偽物を作る
● d3d9.defを用意
● エクスポートする関数名を列挙
●
プロジェクトプロパティ→構成プロパティ→
リンカ→入力→モジュール定義ファイルに
d3d9.defを指定
LIBRARY d3d9
EXPORTS
Direct3DCreate9 @4
- 14. d3d9.dllの偽物を作る
● エントリポイント
BOOL APIENTRY DllMain(HINSTANCE,
DWORD reason,
LPVOID) {
return reason == DLL_PROCESS_ATTACH ?
Init() :
TRUE;
}
- 15. d3d9.dllの偽物を作る
● 本物のDirect3DCreate9を取得
BOOL Init() {
// 本物のD3D9.dllがあるパスを取得
TCHAR strPathBuffer[kBufferMax];
GetSystemDirectory(strPathBuffer, kBufferMax);
PathAppend(strPathBuffer, L"D3D9.DLL");
HMODULE hOriginalModule = LoadLibrary(strPathBuffer);
if (!hOriginalModule) {
return FALSE;
}
// 本物のDirect3DCreate9のアドレスを取得
g_pOriginalDirect3DCreate9 = reinterpret_cast<IDirect3D9 *(WINAPI *)
(UINT)>(GetProcAddress(hOriginalModule, "Direct3DCreate9"));
if (!g_pOriginalDirect3DCreate9) {
return FALSE;
}
return TRUE;
}
- 16. d3d9.dllの偽物を作る
● 偽物のDirect3DCreate9を作る
extern "C" {
IDirect3D9 *WINAPI Direct3DCreate9(UINT
SDKVersion) {
return (*g_pOriginalDirect3DCreate9)
(SDKVersion);
}
}
- 18. 改造1:MessageDialogを仕込む
● Initの中にMessageBoxを仕込む
BOOL Init() {
MessageBox(nullptr, L"侵入!", L"", 0);
// ..
return TRUE;
}
- 19. 改造2:消去色を赤に
● IDirect3D9Device9::Clearを差し替える
● 第4引数を常に赤にして本物のClearに渡す
- 20. Direct3DはC言語でも
開発出来ます?
● とはいいつつもクラスっぽい呼び出しが絡
むDirect3Dでの開発は純粋なC言語で出
来るの?
- 21. Direct3DはC言語でも
開発出来ます
● Direct3D周りにヘッダを読む前に
#define CINTERFACE
を唱えるとDirect3Dが変身
- 22. Direct3DはC言語でも
開発出来ます
g_pDevice->Clear(0, nullptr, D3DCLEAR_TARGET,
D3DCOLOR_XRGB(0, 0, 0), 1.f, 0);
が
g_pDevice->lVptbl->Clear(g_pDevice, 0, nullptr,
D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.f,
0);
こうなる
- 24. IDirect3D9::CreateDevice
を差し替え
● IDirect3DDevice9を製造する大元を叩く
extern "C" {
IDirect3D9 *WINAPI Direct3DCreate9(UINT SDKVersion) {
IDirect3D9 *pD3d9 = (*g_pOriginalDirect3DCreate9)
(SDKVersion);
// 本物を退避
g_pOriginalCreateDevice = pD3d9->lpVtbl-
>CreateDevice;
// 偽物に書き換え
pD3d9->lpVtbl->CreateDevice = MyCreateDevice;
return pD3d9;
}
}
- 25. IDirect3DDevice9::Clear
を差し替え
● 偽CleateDevice内でClearを差し替え
HRESULT WINAPI MyCreateDevice(引数省略...
IDirect3DDevice9**
ppReturnedDeviceInterface) {
HRESULT ret = (*g_pOriginalCreateDevice)(引数省略...,
ppReturnedDeviceInterface);
if (SUCCEEDED(ret)) {
// 本物を退避
g_pOriginalClear = (*ppReturnedDeviceInterface)->lpVtbl-
>Clear;
// 偽物に書き換え
(*ppReturnedDeviceInterface)->lpVtbl->Clear = MyClear;
}
return ret;
}
- 26. 偽IDirect3DDevice9::Clear
● 常に赤を指定した本物を呼び出す
HRESULT WINAPI MyClear(IDirect3DDevice9 *pDevice,
DWORD Count,
const D3DRECT *pRects,
DWORD Flags,
D3DCOLOR Color,
float Z,
DWORD Stencil) {
return (*g_pOriginalClear)(pDevice, Count,
pRects, Flags, D3DCOLOR_XRGB(255, 0, 0), Z,
Stencil);
}
- 27. 差し替える
偽IHogeHoge::Hogeの型
● MSDNに乗っているものに呼び出し規約
WINAPIを付けて第一引数にIHogeHoge *を
加える函数ポインタ
HRESULT Hoge(int, double);
があったら
HRESULT (WINAPI *Hoge)(IHogeHoge *, int, double);
になる
- 28. 実はセグる
● lpVtblがある場所はアクセス保護されてい
る
➔
VirtualProtectを使って書き込み出来るよう
にする必要がある
- 29. CぷらーならRAIIだよね
● VirtualProtectをRAIIっぽく
struct ScopedVirtualProtect {
template <typename P>
ScopedVirtualProtect(P p, DWORD dwNewProtext) : m_p(p),
m_size(sizeof(*p)) {
VirtualProtect(m_p, m_size, dwNewProtext, &m_oldProtect);
}
~ScopedVirtualProtect() {
VirtualProtect(m_p, m_size, m_oldProtect, &m_oldProtect);
}
private:
ScopedVirtualProtect(const ScopedVirtualProtect &);
ScopedVirtualProtect &operator =(const ScopedVirtualProtect &);
LPVOID m_p;
SIZE_T m_size;
DWORD m_oldProtect;
};
- 30. アクセス権限書き換え
● 偽物に書き換える前に仕込む
ScopedVirtualProtect protect(pD3d9->lpVtbl,
PAGE_EXECUTE_WRITECOPY);
pD3d9->lpVtbl->CreateDevice = MyCreateDevice;
ScopedVirtualProtect
protect((*ppReturnedDeviceInterface)->lpVtbl,
PAGE_EXECUTE_WRITECOPY);
(*ppReturnedDeviceInterface)->lpVtbl->Clear = MyClear;
- 31. 改造3:ウィンドウサイズ
変更とEscキーでの終了
● IDIrect3D9::CreateDeviceを差し替えたこと
でフォーカスウィンドウハンドルが手に入る
➔
ウィンドウハンドルを使って色々出来る
- 32. ウィンドウサイズ変更
● 640*480に変えるついでにサイズ変更不可
なスタイルに変更
HRESULT WINAPI MyCreateDevice(引数省略.. HWND hFocusWindow, 引数省略..) {
RECT rect = {0, 0, 640, 480};
// サイズ変更不可スタイル
const DWORD kWinsowStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
WS_MINIMIZEBOX;
// ウィンドウスタイル変更
SetWindowLong(hFocusWindow, GWL_STYLE, kWinsowStyle);
// ウィンドウサイズ計算
AdjustWindowRect(&rect, kWinsowStyle, FALSE);
// ウィンドウサイズ変更
SetWindowPos(hFocusWindow, nullptr,
0, 0,
rect.right – rect.left, rect.bottom - rect.top,
SWP_NOMOVE | SWP_NOZORDER);
// ..
return ret;
}
- 33. Escキーで終了処理を追加
● ウィンドウプロシージャを置き換える
HRESULT WINAPI MyCreateDevice(引数省略. HWND hFocusWindow, 引数省略.) {
// ..
// ウィンドウプロシージャ置き換え
SetWindowLong(hFocusWindow, GWL_WNDPROC,
reinterpret_cast<LONG>(&WndProc));
// ..
return ret;
}
- 34. 新しいウィンドウプロシージャ
● Escキーを押したらウィンドウを破棄
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wp,
LPARAM lp) {
if (msg == WM_DESTROY) {
PostQuitMessage(0);
return 0;
}
// エスケープが押されたら窓を破棄
if (msg == WM_KEYDOWN) {
if (wp == VK_ESCAPE) {
DestroyWindow(hWnd);
}
return 0;
}
return DefWindowProc(hWnd, msg, wp, lp);
}
- 35. 改造4:読み込む画像
ファイル名を修正
● 画像表示といいつつ何もでてない?
➔ ファイルの拡張子を間違って指定
(×bmp→○bmq)
- 37. インポートセクションの取得
● DLL名とか函数名とかが入っている領域
● 先頭には読み込むdllの数だけ
IMAGE_IMPORT_DESCRIPTOR構造体が配
置
// ベースアドレスを取得
HMODULE hBase = GetModuleHandle(nullptr);
DWORD dwSize;
// IMAGE_IMPORT_DESCRIPTOR テーブルのアドレスの取得
PIMAGE_IMPORT_DESCRIPTOR imgDesc =
static_cast<PIMAGE_IMPORT_DESCRIPTOR>(
ImageDirectoryEntryToData(
hBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_IMPORT,
&dwSize));
- 38. d3dx9_xx.dllを探す
● D3DXCreateTextureFromFile函数が入って
いるdllを探す
// 最後のIMAGE_IMPORT_DESCRIPTORは0で埋められている
while (imgDesc->Name) {
// imgDesc->Nameはdll名までの相対オフセット
const char * strModule = reinterpret_cast<char
*>((reinterpret_cast<DWORD>(hBase) + imgDesc->Name));
// 使っているSDKに依存
if (!std::strcmp(strModule, "d3dx9_43.dll")) {
break;
}
++imgDesc;
}
- 39. D3DXCreateTextureFromFile
を探す
● インポートアドレステーブルとインポート
ネームテーブルを取得
// インポートアドレステーブル
PIMAGE_THUNK_DATA pIAT =
reinterpret_cast<PIMAGE_THUNK_DATA>(
reinterpret_cast<DWORD>(hBase) + imgDesc->FirstThunk);
// インポートネームテーブル
PIMAGE_THUNK_DATA pINT =
reinterpret_cast<PIMAGE_THUNK_DATA>(
reinterpret_cast<DWORD>(hBase) + imgDesc-
>OriginalFirstThunk);
- 40. D3DXCreateTextureFromFile
をフック
● D3DXCreateTextureFromFileを差し替え
while (pIAT->u1.Function) {
// 最上位が立ってたら名前じゃない
if (!(pINT->u1.AddressOfData & 0x80000000)) {
// 名前取得
PIMAGE_IMPORT_BY_NAME pImportName =
reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
reinterpret_cast<DWORD>(hBase) + pINT->u1.AddressOfData);
// D3DXCreateTextureFromFileかを比較
if (!std::strcmp(reinterpret_cast<char *>(pImportName->Name),
"D3DXCreateTextureFromFileW")) {
// 本物を退避
g_pOriginalD3DXCreateTextureFromFile =
reinterpret_cast<decltype(g_pOriginalD3DXCreateTextureFromFile)>(pIAT-
>u1.Function);
// 偽物に書き換え
scoped_virtual_protect protect(pIAT, PAGE_READWRITE);
pIAT->u1.Function =
reinterpret_cast<DWORD>(MyD3DXCreateTextureFromFile);
break;
}
}
++pIAT; ++pINT;
}
- 41. 偽D3DXCreateTextureFromFile
● ファイル名の拡張子をbmqに差し替え
HRESULT WINAPI MyD3DXCreateTextureFromFile(LPDIRECT3DDEVICE9 pDevice,
LPCTSTR pSrcFile,
LPDIRECT3DTEXTURE9 *ppTexture) {
LPTSTR pTemp = static_cast<LPTSTR>(std::malloc(sizeof(*pTemp) *
(lstrlen(pSrcFile) + 1)));
lstrcpy(pTemp, pSrcFile);
// 拡張子を差し替え
PathRenameExtension(pTemp, L".bmq");
auto ret = (*g_pOriginalD3DXCreateTextureFromFile)(pDevice, pTemp,
ppTexture);
std::free(pTemp);
return ret;
}