問題描述:
采用全域鉤子的方式將dll注入了目標行程,并成功hook了目標行程里的函式Fun。使其呼叫時會先進入我的函式Fun_My。這些都沒有問題。但是在我要卸載鉤子的時候,如果剛好代碼執行在我的函式Fun_My的代碼里。就會崩潰。因為我已經卸載了dll,也就是dll已經不在目標行程里了。這時候Fun_My也是無效的內容了,并不是以前的正確的代碼段。所以目標行程就崩潰了。
這種情況下,在我的dll執行detach即將卸載的時候,如何確保代碼并不在Fun_My里面執行呢。
當然我肯定是提前執行了對函式的unhook的,只是最后的一次hook到的函式,可能還在Fun_My里面執行,還沒有執行完回傳。這就出了問題。
大家有遇到過么,或是思考過這個問題,有啥好的處理方法啊,關于技術的意見建議討論都歡迎。感謝。
uj5u.com熱心網友回復:
參考WinAPIOverride32源代碼?uj5u.com熱心網友回復:
多謝回復,我研究研究去
uj5u.com熱心網友回復:
一搬都是陣列越界,或指標指向了非法地址造成的。uj5u.com熱心網友回復:
看下HOOK代碼,跳轉指令是怎么樣的?處理呼叫原函式?uj5u.com熱心網友回復:
感謝樓上2位回復,不過你們貌似沒看明白問題,還是感謝。uj5u.com熱心網友回復:
不好處理,即使不涉及hook,僅僅是LoadLibrary,呼叫,FreeLibrary,也存在這種問題。我做過的所有此類專案,都是注入DLL后直到程式結束也不卸載DLL了,如果要關閉監視,直接令Hook函式呼叫原始函式而不做另外的處理。
uj5u.com熱心網友回復:
多謝回答,我除錯過幾個專案,確實如你所說,注入dll后,一直存在不會卸載。這確實是個方式。
但同時我也除錯到其他幾個類似軟體,卸載了的。且不會出問題,當然我不100%保證一定沒問題,最少我花 了些時間測驗的,一次都沒出個問題。
目前我正在進一步除錯,大概有個思路了,測驗過繼續和大家討論。
uj5u.com熱心網友回復:
誠如樓上所言,我也認為這是一個普遍問題,只要做這類軟體,一定會面臨這個問題。我也做了幾個了,一直沒有尋找到一個理想的解決辦法,所以提出來和大家討論討論。
目前有想到個方式,理論上可以優雅的解決這個問題,正在測驗
uj5u.com熱心網友回復:
我 記得 程式 FreeLibrary 后,系統 并沒有 馬上 Free 掉。 大概 要 15 分鐘 ?? 后 系統才 真正Free掉,這是系統 防止 短時間內 不斷 free 和 load。uj5u.com熱心網友回復:
多謝回答。
你的意思我大概明白,但是我的理解是:系統可能做了一個全域的緩沖區(姑且容我這么稱呼吧),對最近加載了的dll,運行過的程式等做快取,以加快再次運行同一程式或再次加載同一dll情況下的速度。這可能就是很多程式第一次運行比較慢,但是運行一次后可能會快一些。
但是以上這個假設,是系統做的,對于我們行程本身來說,我們什么都感覺不到。
也就是說,只要我free卸載了dll。行程空間中以前本來加載該dll的地址空間,就已經被廢棄,成為了無效的內容,不會再繼續是有效的可執行的代碼(即使這時候可能系統底層可能對該dll已經做了快取,但是我行程本身地址空間內一定是沒有的,而我行程的運行只能在我自己的地址空間,所以問題就出現了)。
uj5u.com熱心網友回復:
getprocess的之后,那個執行free好像 可以 判斷是否制定完畢,否則free掉就沒法呼叫了uj5u.com熱心網友回復:
6666666666666666uj5u.com熱心網友回復:
遍歷所有執行緒,除了卸載行程所有執行緒全部暫停,然后卸載執行緒先卸載HOOK,然后在恢復有所執行緒。uj5u.com熱心網友回復:
遍歷所有執行緒,除了卸載執行緒之外的所有執行緒全部暫停,然后卸載執行緒先卸載HOOK,然后在恢復有所執行緒。uj5u.com熱心網友回復:
learning~uj5u.com熱心網友回復:
66666666666666uj5u.com熱心網友回復:
感謝回復。
你的想法和我覺得最理想的處理方式不謀而合!!!!
我的想法是:首先unhook所有我鉤了的函式。然后列舉所有執行緒,暫停,取該執行緒的呼叫堆疊。判斷呼叫堆疊里是否有我dll模塊所在的加載地址。如果沒有,那該執行緒沒問題。如果有就記錄該執行緒句柄。然后sleep一下后重復上面的判斷。直到確定所有執行緒的呼叫堆疊里,沒有我dll的呼叫。然后可以完全卸載dll。
這也是我想到最理想的方式。
但是,,,凡是都有但是。。。。
列舉執行緒,暫停執行緒,取堆疊等等系列操作,存在需要權限,會失敗等等問題。導致判斷流程有問題不在準確。一旦不能100%確保正確,那么也就意味著整個方案的失敗。
uj5u.com熱心網友回復:
dll detach的時候直接去掉回呼的函式不行嗎uj5u.com熱心網友回復:
這個操作是必須的,但是并不足夠,會存在我說的這個問題
uj5u.com熱心網友回復:
a.exe使用了b.dll注入
則在a要卸載b.dll時,如果fun_my正在被執行,則等待fun_my執行完畢后再卸載。
b.dll卸載時判斷fun是不是在呼叫fun_my,如果正在呼叫則等待呼叫完畢后將回呼置空。
uj5u.com熱心網友回復:
dll detach的時候直接去掉回呼的函式不行嗎
這個操作是必須的,但是并不足夠,會存在我說的這個問題
a.exe使用了b.dll注入
則在a要卸載b.dll時,如果fun_my正在被執行,則等待fun_my執行完畢后再卸載。
b.dll卸載時判斷fun是不是在呼叫fun_my,如果正在呼叫則等待呼叫完畢后將回呼置空。
我覺得這樣是可以解決問題的。如果這樣不能滿足你的要求,你可以考慮使用記憶體映射來實作呼叫,這樣就完全解耦了。
uj5u.com熱心網友回復:
dll detach的時候直接去掉回呼的函式不行嗎
這個操作是必須的,但是并不足夠,會存在我說的這個問題
a.exe使用了b.dll注入
則在a要卸載b.dll時,如果fun_my正在被執行,則等待fun_my執行完畢后再卸載。
b.dll卸載時判斷fun是不是在呼叫fun_my,如果正在呼叫則等待呼叫完畢后將回呼置空。
邏輯清晰明了,但是有個問題,怎么判斷是否正在呼叫fun_my??
這也是問題核心所在。。。
uj5u.com熱心網友回復:
dll detach的時候直接去掉回呼的函式不行嗎
這個操作是必須的,但是并不足夠,會存在我說的這個問題
a.exe使用了b.dll注入
則在a要卸載b.dll時,如果fun_my正在被執行,則等待fun_my執行完畢后再卸載。
b.dll卸載時判斷fun是不是在呼叫fun_my,如果正在呼叫則等待呼叫完畢后將回呼置空。
邏輯清晰明了,但是有個問題,怎么判斷是否正在呼叫fun_my??
這也是問題核心所在。。。
設定一個標志就可以了,例如:
fun()
{)
{
if (flag) {
fun_my();
}
}
detach:
unload()
{
if (flag) {
flag = !flag;
int timeout_count = 8;
while (timeout_count -- > 0) {
sleep(20); // or event.wait(20)
if (!flag) {
break;
}
}
}
}
uj5u.com熱心網友回復:
bool is_call_ = false;
fun()
{
if (flag) {
is_call_ = true;
fun_my();
is_call_ = false;
}
}
detach:
unload()
{
if (flag) {
flag = !flag;
int timeout_count = 8;
while (!is_call_ && timeout_count-- > 0) {
sleep(20); // or event.wait(20)\
}
}
}
uj5u.com熱心網友回復:
while (!is_call_改為while (is_call_uj5u.com熱心網友回復:
dll detach的時候直接去掉回呼的函式不行嗎
這個操作是必須的,但是并不足夠,會存在我說的這個問題
a.exe使用了b.dll注入
則在a要卸載b.dll時,如果fun_my正在被執行,則等待fun_my執行完畢后再卸載。
b.dll卸載時判斷fun是不是在呼叫fun_my,如果正在呼叫則等待呼叫完畢后將回呼置空。
邏輯清晰明了,但是有個問題,怎么判斷是否正在呼叫fun_my??
這也是問題核心所在。。。
設定一個標志就可以了,例如:
fun()
{)
{
if (flag) {
fun_my();
}
}
detach:
unload()
{
if (flag) {
flag = !flag;
int timeout_count = 8;
while (timeout_count -- > 0) {
sleep(20); // or event.wait(20)
if (!flag) {
break;
}
}
}
}
首先感謝你的回復。
你的意思我明白,說下我的理解。
一個函式的呼叫,存在呼叫堆疊的保存,壓參等操作。也就是說。并不是c++代碼看到的第一行就是匯編代碼的第一行,前面還有很多行匯編代碼的(這里不討論直接內嵌匯編的特殊情況)。所以,假設一種運行狀態為:
呼叫進入我fun_my,正在壓參,注意這時候已經進入了函式,但是卻又還沒有設定我們希望看到的標志量。既然沒有設定標志量,那我就認為已經不呼叫了,卸載了dll。但實際情況卻是正在呼叫。
當然我們可以在多等待一段時間,而且我目前也確實是這么做的。但是這個等待的時間不好確定,肯定是越長越好。能解決,但不優雅。
uj5u.com熱心網友回復:
dll detach的時候直接去掉回呼的函式不行嗎
這個操作是必須的,但是并不足夠,會存在我說的這個問題
a.exe使用了b.dll注入
則在a要卸載b.dll時,如果fun_my正在被執行,則等待fun_my執行完畢后再卸載。
b.dll卸載時判斷fun是不是在呼叫fun_my,如果正在呼叫則等待呼叫完畢后將回呼置空。
邏輯清晰明了,但是有個問題,怎么判斷是否正在呼叫fun_my??
這也是問題核心所在。。。
設定一個標志就可以了,例如:
fun()
{)
{
if (flag) {
fun_my();
}
}
detach:
unload()
{
if (flag) {
flag = !flag;
int timeout_count = 8;
while (timeout_count -- > 0) {
sleep(20); // or event.wait(20)
if (!flag) {
break;
}
}
}
}
首先感謝你的回復。
你的意思我明白,說下我的理解。
一個函式的呼叫,存在呼叫堆疊的保存,壓參等操作。也就是說。并不是c++代碼看到的第一行就是匯編代碼的第一行,前面還有很多行匯編代碼的(這里不討論直接內嵌匯編的特殊情況)。所以,假設一種運行狀態為:
呼叫進入我fun_my,正在壓參,注意這時候已經進入了函式,但是卻又還沒有設定我們希望看到的標志量。既然沒有設定標志量,那我就認為已經不呼叫了,卸載了dll。但實際情況卻是正在呼叫。
當然我們可以在多等待一段時間,而且我目前也確實是這么做的。但是這個等待的時間不好確定,肯定是越長越好。能解決,但不優雅。
如果是單執行緒的,用24#貼的代碼不存在你說的這種情況的;如果是多執行緒的在這個代碼上加個鎖就行了。
uj5u.com熱心網友回復:
也就是說,這個設定標志量的方式,能有一定的作用,如果為true,說明一定在呼叫,不能卸載。但是如果為false。卻不能保證沒有呼叫。uj5u.com熱心網友回復:
fun_my函式的呼叫執行緒,和dll的卸載執行緒。怎么可能在同一條執行緒呢,自己呼叫來卸載自己所在的dll?uj5u.com熱心網友回復:
也就是說,這個設定標志量的方式,能有一定的作用,如果為true,說明一定在呼叫,不能卸載。但是如果為false。卻不能保證沒有呼叫。
我覺得你想多了,試試就知道了。
uj5u.com熱心網友回復:
fun_my函式的呼叫執行緒,和dll的卸載執行緒。怎么可能在同一條執行緒呢,自己呼叫來卸載自己所在的dll?
那加個鎖就可以啦。
uj5u.com熱心網友回復:
至于加鎖,和我上面說的同一個道理,函式的呼叫原理。uj5u.com熱心網友回復:
加鎖,連while回圈判斷都省了uj5u.com熱心網友回復:
也就是說,這個設定標志量的方式,能有一定的作用,如果為true,說明一定在呼叫,不能卸載。但是如果為false。卻不能保證沒有呼叫。
我覺得你想多了,試試就知道了。
兄臺,我提出來這個問題,我肯定是除錯過的。
另外,你可以除錯下函式的呼叫程序,顯示匯編代碼看看。你就會明白我的意思。
uj5u.com熱心網友回復:
至于加鎖,和我上面說的同一個道理,函式的呼叫原理。
你想多了,加鎖后,fun沒呼叫完detach會被阻塞的。
uj5u.com熱心網友回復:
加鎖,連while回圈判斷都省了
那恕我直言,你對鎖的理解不對,或者說不完整。
既然你鎖能鎖住,那就一定進代碼了,也就一定進入了函式。如果你覺得沒進入函式,那又是怎么鎖住的呢?
uj5u.com熱心網友回復:
至于加鎖,和我上面說的同一個道理,函式的呼叫原理。
你想多了,加鎖后,fun沒呼叫完detach會被阻塞的。
按你的說法,我們假設現在detach拿到了鎖。你認為fun_my就不能執行了么???
請注意,這里只是fun_my拿不到鎖,并不是不能執行?
拿不到 和不能執行。。。完全2個概念
uj5u.com熱心網友回復:
fun()
{
auto_lock;
if (flag) {
fun_my();
}
}
detach:
unload()
{
auto_lock;
if (flag) {
flag = !flag;
}
}
這樣應該沒有問題呀,無論怎么進鎖,只要detach了都不會再調到fun_my了呀。
uj5u.com熱心網友回復:
總共兩種情況:1、detach時fun已經進入鎖了,則detach等待fun呼叫完成后執行,fun再次被呼叫時無法呼叫fun_my
2、detach時fun正在等待進入鎖,則detach執行完畢后fun進入鎖,發現flagbei
uj5u.com熱心網友回復:
總共兩種情況:1、detach時fun已經進入鎖了,則detach等待fun呼叫完成后執行,fun再次被呼叫時無法呼叫fun_my
2、detach時fun正在等待進入鎖,則detach執行完畢后fun進入鎖,發現flag被設定為false,則不會呼叫fun_my
uj5u.com熱心網友回復:
假設detach拿到了鎖,你認為 fun_my就會被鎖住。那問題來了,請問fun_my為什么會被鎖住,答案肯定是因為它試圖去拿鎖而不得。。。。注意,既然它試圖去拿鎖,試圖去做一件事情。是不是恰恰說明它已經在執行某些代碼了????uj5u.com熱心網友回復:
總共兩種情況:
1、detach時fun已經進入鎖了,則detach等待fun呼叫完成后執行,fun再次被呼叫時無法呼叫fun_my
2、detach時fun正在等待進入鎖,則detach執行完畢后fun進入鎖,發現flag被設定為false,則不會呼叫fun_my
這里你理解錯誤。。。并不是“無法呼叫fun_my” 。。。二是能呼叫,只是拿不到鎖。前面我已經說了,這是2個不同的概念。。
uj5u.com熱心網友回復:
關鍵你一定要理解到,不是不能呼叫,而是能呼叫,代碼能執行,只是要等鎖。uj5u.com熱心網友回復:
假設detach拿到了鎖,你認為 fun_my就會被鎖住。那問題來了,請問fun_my為什么會被鎖住,答案肯定是因為它試圖去拿鎖而不得。。。。注意,既然它試圖去拿鎖,試圖去做一件事情。是不是恰恰說明它已經在執行某些代碼了????
總共兩種情況:
1、detach時fun已經進入鎖了,則detach等待fun呼叫完成后執行,fun再次被呼叫時無法呼叫fun_my
2、detach時fun正在等待進入鎖,則detach執行完畢后fun進入鎖,發現flag被設定為false,則不會呼叫fun_my
這里你理解錯誤。。。并不是“無法呼叫fun_my” 。。。二是能呼叫,只是拿不到鎖。前面我已經說了,這是2個不同的概念。。
先別著急哈,我們慢慢看這段代碼:
fun()
{
auto_lock;
if (flag) {
fun_my();
}
}
detach:
unload()
{
auto_lock;
if (flag) {
flag =false;
}
}
fun在等待進入鎖的時候unload已經將flag設定為false了,所以fun進入鎖后無法呼叫到fun_my
uj5u.com熱心網友回復:
或這么說,會比較好理解,既然鎖是在函式內部的區域變數,是不是就只能內部有效。既然內部有效,首先肯定要執行到函式內部啊 。uj5u.com熱心網友回復:
老大,冷靜。。。。fun是原始函式,你不能修改,唯一能修改的是fun_my啊。。。。。
uj5u.com熱心網友回復:
你直接貼一下你的原始碼吧,從原始碼上講可能不容易有誤解uj5u.com熱心網友回復:
哈哈,假設這里說的fun就是系統API:MessageBox我就是為了注入別人的程式,去攔截這個呼叫到我的 MessageBox_My。
就這個意思。
uj5u.com熱心網友回復:
哈哈,假設這里說的fun就是系統API:MessageBox
我就是為了注入別人的程式,去攔截這個呼叫到我的 MessageBox_My。
就這個意思。
哦,明白了。
在DLL_PROCESS_ATTACH的時候intall,在DLL_PROCESS_DETACH的時候uninstall;
則,在install時注入需要執行的函式,同時保存源函式指標;
在uninstall的時候講保存的源函式指標反注冊回去。
我在某個商用軟體里就是這么干的,用戶量也有幾十萬吧,好像沒有收到說的這種問題的反饋報告。
uj5u.com熱心網友回復:
其實你的思路我明白,
哈哈,假設這里說的fun就是系統API:MessageBox
我就是為了注入別人的程式,去攔截這個呼叫到我的 MessageBox_My。
就這個意思。
哦,明白了。
在DLL_PROCESS_ATTACH的時候intall,在DLL_PROCESS_DETACH的時候uninstall;
則,在install時注入需要執行的函式,同時保存源函式指標;
在uninstall的時候講保存的源函式指標反注冊回去。
我在某個商用軟體里就是這么干的,用戶量也有幾十萬吧,好像沒有收到說的這種問題的反饋報告。
那我們假設一種情況:正在執行fun_my。這時候關閉你的軟體,你注入的dll會被卸載么?如果卸載,那正在執行的fun_my又恰好在你的dll模塊代碼里,這時候就崩潰了啊。因為執行代碼所在的模塊都被卸載沒有了。。
uj5u.com熱心網友回復:
不會的,freelibrary時作業系統應該會等待你的函式呼叫完成,如果超過一定時間沒有呼叫完成才會出現崩潰,所以盡量保證你注入的函式不要阻塞就可以了。如果實在不放心的話在反注冊后等待幾十毫秒就可以了。uj5u.com熱心網友回復:
不會的,freelibrary時作業系統應該會等待你的函式呼叫完成,如果超過一定時間沒有呼叫完成才會出現崩潰,所以盡量保證你注入的函式不要阻塞就可以了。如果實在不放心的話在反注冊后等待幾十毫秒就可以了。
朋友你想多了
uj5u.com熱心網友回復:
不會的,freelibrary時作業系統應該會等待你的函式呼叫完成,如果超過一定時間沒有呼叫完成才會出現崩潰,所以盡量保證你注入的函式不要阻塞就可以了。如果實在不放心的話在反注冊后等待幾十毫秒就可以了。
朋友你想多了
我覺得還是你哪個細節上的處理有問題,正常來講不應該出現你說的這個問題的。你看看下面這段代碼對你有沒有幫助。
bool xxx::RemoteLoadLibrary(HANDLE hProcess, LPCTSTR lpPathDLL)
{
bool ret = false;
HMODULE hKernel32 = GetModuleHandle(_T("Kernel32"));
if (hKernel32)
{
FARPROC pfnThreadRtn = GetProcAddress(hKernel32, "LoadLibraryW");
if (pfnThreadRtn)
{
LPVOID pParaRtn = VirtualAllocEx(hProcess, NULL, MAX_PATH, MEM_COMMIT, PAGE_READWRITE);
if (pParaRtn)
{
DWORD iLen = wcslen(lpPathDLL) * sizeof(wchar_t)+2;
DWORD dwWritten = 0;
if (WriteProcessMemory(hProcess, pParaRtn, lpPathDLL, iLen, &dwWritten))
{
if (dwWritten == iLen)
{
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pfnThreadRtn, pParaRtn, 0, NULL);
if (hThread != INVALID_HANDLE_VALUE)
{
WaitForSingleObject(hThread, 3000);
CloseHandle(hThread);
ret = true;
}
}
}
VirtualFreeEx(hProcess, pParaRtn, 0, MEM_RELEASE);
}
}
}
return ret;
}
bool xxx::RemoteFreeLibrary(LPCTSTR lpPathDLL, HANDLE hProcess)
{
bool ret = false;
HANDLE hModuleSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetProcessId(hProcess));
if (INVALID_HANDLE_VALUE != hModuleSnap)
{
MODULEENTRY32 me32;
memset(&me32, 0, sizeof(MODULEENTRY32));
me32.dwSize = sizeof(MODULEENTRY32);
bool bFound = false;
BOOL bOK = ::Module32First(hModuleSnap, &me32);
while (bOK)
{
bFound = (0 == ::_tcsicmp(me32.szModule, lpPathDLL) || 0 == ::_tcsicmp(me32.szExePath, lpPathDLL));
if (bFound)
{
break;
}
bOK = ::Module32Next(hModuleSnap, &me32);
}
if (bFound)
{
LPTHREAD_START_ROUTINE lpThreadFun = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(_T("Kernel32")), "FreeLibrary");
if (lpThreadFun)
{
HANDLE hThread = ::CreateRemoteThread(hProcess, NULL, 0, lpThreadFun, me32.modBaseAddr /* 模塊地址 */, 0, NULL);
if (hThread)
{
::WaitForSingleObject(hThread, 200);
::CloseHandle(hThread);
ret = true;
}
}
}
::CloseHandle(hModuleSnap);
}
return ret;
}
uj5u.com熱心網友回復:
首先感謝你的代碼,遠程執行緒注入也有用到。關于我說到的這個問題,不是代碼問題,理論想清楚,這個問題是一定存在的。
就像你說的,多等待一會,也就是為了防這個問題。
而且和函式的執行時間長短,呼叫頻率等都有關系,所以是概率的。但是一定存在。
uj5u.com熱心網友回復:
EnterCriticalSection( &m_cs );FreeLibrary(hModule);
LeaveCriticalSection(&CriticalSection);
dll 中 函式 先
EnterCriticalSection( &m_cs );
uj5u.com熱心網友回復:
EnterCriticalSection( &m_cs );
FreeLibrary(hModule);
LeaveCriticalSection(&CriticalSection);
dll 中 函式 先
EnterCriticalSection( &m_cs );
大哥,函式的呼叫理解不對,你這樣用鎖或臨界的方式是不對的。前面我解釋過這個問題。
uj5u.com熱心網友回復:
這么說吧,鎖或臨界,都是對它后面的起作用,位于它前面的,它根本管不了。前面部分的代碼照常執行。。。uj5u.com熱心網友回復:
不要說這是函式的第一行。函式的呼叫方式,前面還有很多匯編代碼的。執行到臨界的時候,早已經進入了函式了。uj5u.com熱心網友回復:
首先感謝你的代碼,遠程執行緒注入也有用到。
關于我說到的這個問題,不是代碼問題,理論想清楚,這個問題是一定存在的。
就像你說的,多等待一會,也就是為了防這個問題。
而且和函式的執行時間長短,呼叫頻率等都有關系,所以是概率的。但是一定存在。
如果只是理論上的話,也許你想像中的理論存在你不知道的機制。可以從呼叫的結果上做出反推。
我注入的那個dll中的函式一秒鐘會呼叫幾十次以上的,而且是一直呼叫,沒出現你說的這個問題。
卸載時呼叫CreateRemoteThread有時候會被阻塞一段時間,我推測它可能在進行一些清理的作業。
這是一個比較常見的場景,如果這種場景都會出現崩潰,官方設計的這個機制就顯得太脆弱了。
uj5u.com熱心網友回復:
《Windows核心編程》uj5u.com熱心網友回復:
首先感謝你的代碼,遠程執行緒注入也有用到。
關于我說到的這個問題,不是代碼問題,理論想清楚,這個問題是一定存在的。
就像你說的,多等待一會,也就是為了防這個問題。
而且和函式的執行時間長短,呼叫頻率等都有關系,所以是概率的。但是一定存在。
如果只是理論上的話,也許你想像中的理論存在你不知道的機制。可以從呼叫的結果上做出反推。
我注入的那個dll中的函式一秒鐘會呼叫幾十次以上的,而且是一直呼叫,沒出現你說的這個問題。
卸載時呼叫CreateRemoteThread有時候會被阻塞一段時間,我推測它可能在進行一些清理的作業。
這是一個比較常見的場景,如果這種場景都會出現崩潰,官方設計的這個機制就顯得太脆弱了。
我實際測驗過的,構建個環境就能知道了。
uj5u.com熱心網友回復:
執行到臨界的時候,還沒有進入 FreeLibrary(hModule); !uj5u.com熱心網友回復:
那我們假設一種情況:正在執行fun_my。這時候關閉你的軟體,你注入的dll會被卸載么?如果卸載,那正在執行的fun_my又恰好在你的dll模塊代碼里,這時候就崩潰了啊。因為執行代碼所在的模塊都被卸載沒有了。。
我試了下,在DLL中case DLL_PROCESS_DETACH里加了Sleep(10000)延時10秒,
程式呼叫UnhookWindowsHookEx(hHook);卸載HOOK,DLL還是要等DLL_PROCESS_DETACH中的代碼執行完才會從行程中卸載,
說明不是一呼叫UnhookWindowsHookEx(hHook)或FreeLibrary(),DLL就會馬上從行程中卸載
uj5u.com熱心網友回復:
"在DLL中case DLL_PROCESS_DETACH里加了Sleep(10000)延時10秒,"可行
uj5u.com熱心網友回復:
那我們假設一種情況:正在執行fun_my。這時候關閉你的軟體,你注入的dll會被卸載么?如果卸載,那正在執行的fun_my又恰好在你的dll模塊代碼里,這時候就崩潰了啊。因為執行代碼所在的模塊都被卸載沒有了。。
我試了下,在DLL中case DLL_PROCESS_DETACH里加了Sleep(10000)延時10秒,
程式呼叫UnhookWindowsHookEx(hHook);卸載HOOK,DLL還是要等DLL_PROCESS_DETACH中的代碼執行完才會從行程中卸載,
說明不是一呼叫UnhookWindowsHookEx(hHook)或FreeLibrary(),DLL就會馬上從行程中卸載
DLL_PROCESS_DETACH里的代碼,當然會執行。
uj5u.com熱心網友回復:
我又特地試了下,似乎沒有出現你所的那種現象.DLL代碼:
LPVOID pfnMessageBox = 0;
int WINAPI HookMessageBox(
_In_opt_ HWND hWnd,
_In_opt_ LPCTSTR lpText,
_In_opt_ LPCTSTR lpCaption,
_In_ UINT uType)
{
LPTSTR Text = TEXT("Hook!!!");
int a = 0;
do
{
a++;
Sleep(2000);
} while (a < 5); //等待10秒
__asm
{
push uType
push lpCaption
push Text
push hWnd
call dword ptr pfnMessageBox
}
}
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // handle to DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpReserved) // reserved
{
// Perform actions based on the reason for calling.
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
// Initialize once for each new process.
// Return FALSE to fail DLL load.
hDll = hinstDLL;
GetModuleFileName(NULL, szModuleFileName, _countof(szModuleFileName));
if (_tcsicmp(PathFindFileName(szModuleFileName), TEXT("Hash.exe")) == 0)
{
pfnMessageBox = HookApi(TEXT("user32.dll"), "MessageBoxW", HookMessageBox, 5); //inline Hook,函式頭部寫入E9 xxxx方式
MessageBox(NULL, TEXT("123456"), NULL, MB_ICONASTERISK);
}
break;
case DLL_THREAD_ATTACH:
// Do thread-specific initialization.
break;
case DLL_THREAD_DETACH:
// Do thread-specific cleanup.
break;
case DLL_PROCESS_DETACH:
/*if (_tcsicmp(PathFindFileName(szModuleFileName), TEXT("Hash.exe")) == 0)
{
OutputDebugString(L"Hash.exe DLL_PROCESS_DETACH Start!!!!\n");
Sleep(10000);
OutputDebugString(L"Hash.exe DLL_PROCESS_DETACH End!!!!\n");
}*/
// Perform any necessary cleanup.
break;
}
return TRUE; // Successful DLL_PROCESS_ATTACH.
}
LRESULT CALLBACK GetMsgProc(
_In_ int code,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
return CallNextHookEx(hHook, code, wParam, lParam);
}
int SetHook()
{
hHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, hDll, 0);
return 1;
}
int UnSetHook()
{
UnhookWindowsHookEx(hHook);
return 1;
}
EXE代碼:
int WINAPI _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nShowCmd)
{
HMODULE hDll = LoadLibrary(TEXT("MsgHook.dll"));
if (hDll)
{
PROC Hook = GetProcAddress(hDll, "SetHook");
PROC UnHook = GetProcAddress(hDll, "UnSetHook");
Hook();
Sleep(5000);
UnHook();
}
return 0;
}
uj5u.com熱心網友回復:
在uninstall的時候講保存的源函式指標反注冊回去uj5u.com熱心網友回復:
直到程式結束,不卸載DLL了不可以嗎?uj5u.com熱心網友回復:
遍歷所有執行緒,除了卸載行程所有執行緒全部暫停,然后卸載執行緒先卸載HOOK,然后在恢復有所執行緒。
感謝回復。
你的想法和我覺得最理想的處理方式不謀而合!!!!
我的想法是:首先unhook所有我鉤了的函式。然后列舉所有執行緒,暫停,取該執行緒的呼叫堆疊。判斷呼叫堆疊里是否有我dll模塊所在的加載地址。如果沒有,那該執行緒沒問題。如果有就記錄該執行緒句柄。然后sleep一下后重復上面的判斷。直到確定所有執行緒的呼叫堆疊里,沒有我dll的呼叫。然后可以完全卸載dll。
這也是我想到最理想的方式。
但是,,,凡是都有但是。。。。
列舉執行緒,暫停執行緒,取堆疊等等系列操作,存在需要權限,會失敗等等問題。導致判斷流程有問題不在準確。一旦不能100%確保正確,那么也就意味著整個方案的失敗。
去參考微軟的 Detours 原始碼。
uj5u.com熱心網友回復:
"在DLL中case DLL_PROCESS_DETACH里加了Sleep(10000)延時10秒,"
可行
為什么可行,那我假設fun_my里有個運算,耗時11秒。怎么辦?
只是假設出問題的情況。當然10秒了,肯定
遍歷所有執行緒,除了卸載行程所有執行緒全部暫停,然后卸載執行緒先卸載HOOK,然后在恢復有所執行緒。
感謝回復。
你的想法和我覺得最理想的處理方式不謀而合!!!!
我的想法是:首先unhook所有我鉤了的函式。然后列舉所有執行緒,暫停,取該執行緒的呼叫堆疊。判斷呼叫堆疊里是否有我dll模塊所在的加載地址。如果沒有,那該執行緒沒問題。如果有就記錄該執行緒句柄。然后sleep一下后重復上面的判斷。直到確定所有執行緒的呼叫堆疊里,沒有我dll的呼叫。然后可以完全卸載dll。
這也是我想到最理想的方式。
但是,,,凡是都有但是。。。。
列舉執行緒,暫停執行緒,取堆疊等等系列操作,存在需要權限,會失敗等等問題。導致判斷流程有問題不在準確。一旦不能100%確保正確,那么也就意味著整個方案的失敗。
去參考微軟的 Detours 原始碼。
肯定的告訴你,它是有這個問題的。當然我用的是免費版,不確定付費版是否有解決,畢竟1萬刀,付不起。
uj5u.com熱心網友回復:
to zwfgdlc:附加到目標行程除錯。
斷case DLL_PROCESS_DETACH:和__asm。
看看到detach的時候,上面回圈是否執行完成。
uj5u.com熱心網友回復:
to zwfgdlc:你這個測驗的代碼貌似不對。
將呼叫放在DLL_PROCESS_ATTACH里是不行的。
因為attach和detach肯定是串行的啊。這么寫就不會有多執行緒環境了。
uj5u.com熱心網友回復:
to zwfgdlc:
你這個測驗的代碼貌似不對。
將呼叫放在DLL_PROCESS_ATTACH里是不行的。
因為attach和detach肯定是串行的啊。這么寫就不會有多執行緒環境了。
你仔細看下我代碼中的邏輯,
當EXE執行UnHook()時,被注入的行程正在執行DLL領空HookMessageBox()函式中的do...while回圈代碼,
不正是你所說的那樣"但是在我要卸載鉤子的時候,如果剛好代碼執行在我的函式Fun_My的代碼里。就會崩潰。"
uj5u.com熱心網友回復:
我明白你的意思。但是你不覺得比較怪么。Hook();
Sleep(5000);
UnHook();
這里的Hook() 會安裝鉤子,安裝鉤子是不是會注入dll。那么dll里的attach會不會同步執行完畢呢?如果會。那attach里對messagebox的呼叫,會不會已經執行了??
你測驗的時候,確定UnHook()都執行了,attach呼叫過去的messagebox卻正在執行?
uj5u.com熱心網友回復:
也就是detach都只新完畢了,attach的呼叫卻正在執行???怎么感覺就很怪呢
關鍵就是對messagebox的呼叫,并不是注入行程其他地方呼叫過來的,而是你在attach呼叫的。。。
uj5u.com熱心網友回復:
我嘗試解釋下你測驗為什么不出問題。按我的理解,attach一定會先于detach呼叫完畢。它們一定是會串行呼叫。所以你在attach里的呼叫。永遠會在detach后面。所以不出問題
uj5u.com熱心網友回復:
我確定是UnHook()都執行了,并且我的EXE行程都已經退出了,被注入的行程還在執行回圈代碼,最后MessageBox彈出后,DLL才從行程中卸載uj5u.com熱心網友回復:
我確定是UnHook()都執行了,并且我的EXE行程都已經退出了,被注入的行程還在執行回圈代碼,最后MessageBox彈出后,DLL才從行程中卸載
老大,你怎么就沒明白呢。這個流程不就對了嗎。。
這是因為你代碼有問題,把messagebox的呼叫放到了attach。被你自己強行寫成串行執行了。
如果messagebox的呼叫來自程式其他地方,不來自attach。注意是不來自attach!!!!!就有問題了阿。
uj5u.com熱心網友回復:
//inline Hook,函式頭部寫入E9 xxxx方式pfnMessageBox = HookApi(TEXT("user32.dll"), "MessageBoxW", HookMessageBox, 5);
我是先HOOK了MessageBox再呼叫MessageBox,既然已經HOOK了,呼叫MessageBox肯定是會跳轉到DLL中HookMessageBox函式,然后在那執行回圈延時10秒.
MessageBox(NULL, TEXT("123456"), NULL, MB_ICONASTERISK);
uj5u.com熱心網友回復:
這是因為你代碼有問題,把messagebox的呼叫放到了attach。被你自己強行寫成串行執行了。如果messagebox的呼叫來自程式其他地方,不來自attach。注意是不來自attach!!!!!就有問題了阿
把messagebox的呼叫放到了attach。被你自己強行寫成串行執行了。
把messagebox的呼叫放到了attach。被你自己強行寫成串行執行了。
把messagebox的呼叫放到了attach。被你自己強行寫成串行執行了。
把messagebox的呼叫放到了attach。被你自己強行寫成串行執行了。
如果messagebox的呼叫來自程式其他地方,不來自attach。注意是不來自attach!!!!!就有問題了阿
如果messagebox的呼叫來自程式其他地方,不來自attach。注意是不來自attach!!!!!就有問題了阿
如果messagebox的呼叫來自程式其他地方,不來自attach。注意是不來自attach!!!!!就有問題了阿
如果messagebox的呼叫來自程式其他地方,不來自attach。注意是不來自attach!!!!!就有問題了阿
uj5u.com熱心網友回復:
不用糾結是否先hook。注意我說的好么,呼叫來自attach就不對。。。。
注意我說的好么,呼叫來自attach就不對。。。。
注意我說的好么,呼叫來自attach就不對。。。。
注意我說的好么,呼叫來自attach就不對。。。。
uj5u.com熱心網友回復:
不用糾結是否先hook。
注意我說的好么,呼叫來自attach就不對。。。。
注意我說的好么,呼叫來自attach就不對。。。。
注意我說的好么,呼叫來自attach就不對。。。。
注意我說的好么,呼叫來自attach就不對。。。。
明白,看來我想得太簡單了
uj5u.com熱心網友回復:
因為attach和detach一定是串行的。或是理解為它們一定在同一個執行緒。代碼在它們之間,肯定會被串行話。呼叫如果來自其他地方,也就是其他執行緒,這樣就不在它們的執行緒里。才會出問題。。
uj5u.com熱心網友回復:
將卸載執行緒和呼叫執行緒hook在同一個執行緒就OK了uj5u.com熱心網友回復:
在被hook的行程下申請兩段記憶體,hook時先跳轉到第一段記憶體,然后再跳轉到dll中的代碼執行,然后再跳轉到第二段記憶體執行。第一段記憶體的代碼主要是加鎖,判斷dll是否被卸載,如果被卸載,就跳轉到原被hook的函式處,然后再跳轉到第二段記憶體;否則直接跳轉到dll的代碼執行。
第二段記憶體的代碼就是解鎖。
dll卸載時,加鎖,設定卸載標記,卸載。
申請的兩段記憶體不用釋放。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/122911.html
標籤:基礎類
