日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

像加载DLL一样加载EXE

發布時間:2024/4/11 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 像加载DLL一样加载EXE 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

介紹

你可能已經被警告過,不要用LoadLibrary()加載可執行文件,你可能嘗試這么做過,然后程序就崩潰了,所以你可能會認為這是不可能的。

但實際上這是可行的,本文就將介紹具體的方法。

聲明

這好像跟微軟說的有點不一樣。實際上,微軟沒說不要加載,他們只是說“不要用LoadLibrary()加載可執行文件,應該用CreateProcess() ”。不過除非你很清楚你在做什么,否則不要把這用在產品代碼中,我已經警告過你了|?

準備可執行文件

首先要做的是把可執行文件標記為可重定位的文件,能夠從任何的基地址(任何DLL)加載。你可以用/FIXED:NO來實現,如果想要提高安全性,還可以使用/DYNAMICBASE(默認就是開啟的)。EXE文件可能設置了/FIXED:YES,那樣的話exe就只能在它的首選基地址加載了,如果沒有用/BASE設置過的話這個地址就是0×400000。

下一步的準備工作就是要我們需要從另外的exe文件調用的函數,這跟調DLL很類似

extern?"C"?void?__stdcall?some_func(){...} #ifdef?_WIN64 #pragma?comment(linker,?"/EXPORT:some_func=some_func") #else #pragma?comment(linker,?"/EXPORT:some_func=_some_func@0") #endif

使用LoadLibrary加載可執行文件

在使用LoadLibraryEx()加載可執行文件時候,不要指定LOAD_LIBRARY_AS_DATAFILE或者LOAD_LIBRARY_AS_IMAGE_RESOURCE,如果這么做的話,exe中的導出的函數就不能成功導出,而執行GetProcAddress()時就會失敗。

調用LoadLibrary()后,我們就可以得到一個有效的HINSTANCE handle。但是當我們用LoadLibrary()加載exe文件時,以下兩件關鍵的事沒有發生:

1.?CRT運行庫沒有初始化,包括所有全局變量 2.?導入地址表沒有正確配置,這就意味著所有對導入函數的調用就會導致崩潰

更新導入表

首先我們得要更新可執行文件的導入表。下面的程序片段展示了其過程:

?void?ParseIAT(HINSTANCE?h){//?Find?the?IAT?sizeDWORD?ulsize?=?0;PIMAGE_IMPORT_DESCRIPTOR?pImportDesc?=?(PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(h,TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT,&ulsize);if?(!pImportDesc)return;//?Loop?namesfor?(;?pImportDesc->Name;?pImportDesc++){PSTR?pszModName?=?(PSTR)((PBYTE)h?+?pImportDesc->Name);if?(!pszModName)break;HINSTANCE?hImportDLL?=?LoadLibraryA(pszModName);if?(!hImportDLL){//?...?(error)}//?Get?caller's?import?address?table?(IAT)?for?the?callee's?functionsPIMAGE_THUNK_DATA?pThunk?=?(PIMAGE_THUNK_DATA)((PBYTE)h?+?pImportDesc->FirstThunk);//?Replace?current?function?address?with?new?function?addressfor?(;?pThunk->u1.Function;?pThunk++){FARPROC?pfnNew?=?0;size_t?rva?=?0; #ifdef?_WIN64if?(pThunk->u1.Ordinal?&?IMAGE_ORDINAL_FLAG64) #elseif?(pThunk->u1.Ordinal?&?IMAGE_ORDINAL_FLAG32) #endif{//?Ordinal #ifdef?_WIN64size_t?ord?=?IMAGE_ORDINAL64(pThunk->u1.Ordinal); #elsesize_t?ord?=?IMAGE_ORDINAL32(pThunk->u1.Ordinal); #endifPROC*?ppfn?=?(PROC*)&pThunk->u1.Function;if?(!ppfn){//?...?(error)}rva?=?(size_t)pThunk;char?fe[100]?=?{0};sprintf_s(fe,100,"#%u",ord);pfnNew?=?GetProcAddress(hImportDLL,(LPCSTR)ord);if?(!pfnNew){//?...?(error)}}else{//?Get?the?address?of?the?function?addressPROC*?ppfn?=?(PROC*)&pThunk->u1.Function;if?(!ppfn){//?...?(error)}rva?=?(size_t)pThunk;PSTR?fName?=?(PSTR)h;fName?+=?pThunk->u1.Function;fName?+=?2;if?(!fName)break;pfnNew?=?GetProcAddress(hImportDLL,fName);if?(!pfnNew){//?...?(error)}}//?Patch?it?now...auto?hp?=?GetCurrentProcess();if?(!WriteProcessMemory(hp,(LPVOID*)rva,&pfnNew,sizeof(pfnNew),NULL)?&&?(ERROR_NOACCESS?==?GetLastError())){DWORD?dwOldProtect;if?(VirtualProtect((LPVOID)rva,sizeof(pfnNew),PAGE_WRITECOPY,&dwOldProtect)){if?(!WriteProcessMemory(GetCurrentProcess(),(LPVOID*)rva,&pfnNew,sizeof(pfnNew),NULL)){//?...?(error)}if?(!VirtualProtect((LPVOID)rva,sizeof(pfnNew),dwOldProtect,&dwOldProtect)){//?...?(error)}}}}}}

?

這個函數在整個IAT導入表中循環,將對導入函數的無效引用替換成我們自己的IAT表中的正確引用(來自LoadLibrary()和GetProcAddress())。

初始化CRT

可執行文件的入口點不是WinMain而是WinMainCRTStartup()。這個函數會初始化CRT,建立異常處理器,加載argc和argv,并且調用WinMain。當WinMain返回時,WinMainCRTStartup則會調用exit()。

因此你得要從你的exe中導出調用WinMainCRTStartup的函數:

extern?"C"?void?WinMainCRTStartup(); extern?"C"?void?__stdcall?InitCRT(){WinMainCRTStartup();}

問題是,這樣的話你的WinMain會被調用。所以你得要放一個global flag。

extern?"C"?void?WinMainCRTStartup(); bool?DontDoAnything?=?false; extern?"C"?void?__stdcall?InitCRT(){DontDoAnything?=?true;WinMainCRTStartup();}int?__stdcall?WinMain(...){if?(DontDoAnything)return?0;//?...}

現在又有另外的問題了,當WinMain return的時候,WinMainCRTStartup會調用exit(),但你并不希望那樣。因此,你不希望WinMain return:?

int?__stdcall?WinMain(...){if?(DontDoAnything){for(;;){???Sleep(60000);}???}//?...}

但這么做又會影響到你的初始化,因此你還得這么修改:?

std::thread?t([]?(){InitCRT();}); t.detach();

?但是你其實還得要知道CRT什么時候完成初始化,所以最終的解決方案應該是使用事件:

HANDLE?hEv?=?CreateEvent(0,0,0,0); void(__stdcall?*?InitCRT)(HANDLE)?=?(void(__stdcall*)(HANDLE))?GetProcAddress(hL,"InitCRT"); if?(!InitCRT)return?0; std::thread?t([&]?(HANDLE?h){InitCRT(h);},hEv); t.detach(); WaitForSingleObject(hEv,INFINITE);

?其他的代碼:

extern?"C"?void?WinMainCRTStartup(); HANDLE?hEV?=?0; extern?"C"?void?__stdcall?InitCRT(HANDLE?hE){hEV?=?hE;WinMainCRTStartup();}int?__stdcall?WinMain(...){if?(hEV){SetEvent(hEV);for(;;){???Sleep(60000);}???}}

不用LoadLibrary/GetProcAddress鏈接EXE

幸運的是,LINK.EXE會為我們的DLLEXE.EXE生成一個.lib,因此我們可以用它從我們的exe中鏈接另外的exe,就好像鏈接DLL一樣:

#pragma?comment(lib,"..\\dllexe\\dllexe.lib") extern?"C"????void?__stdcall?e0(HANDLE); extern?"C"????void?__stdcall?e1();

我們還是得要修改IAT,然后調用CRT初始化,但我們不再需要對函數進行GetProcAddress()了:

dllexe.exe14017B578?Import?Address?Table14017BC18?Import?Name?Table0?time?date?stamp0?Index?of?first?forwarder?reference0?e01?e1

源代碼下載:

http://download.csdn.net/download/liujiayu2/9972121

總結

以上是生活随笔為你收集整理的像加载DLL一样加载EXE的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。