引言:最近發現用戶的多臺機器上出現了Dns被莫名修改的問題,從系統事件上看并未能正常確定到是那個具體軟件所為,現在的需求就是確定和定位哪個軟件具體所為。
解決思路:
- 首先到IPv4設置頁面對Dns進行設置:
- 通過ProcExp確定了該窗口的宿主進程是Explorer.exe,通過ProcMon對Explorer進行監控,并未發現Explorer將靜態Dns的地址寫入注冊表(后來發現其實Explorer是通過DllHost.exe來實現對注冊表修改的,所以沒監控到)。
- 通過對Explorer進行逆向分析發現Explorer實現比較復雜,后來通過網絡發現修改Dns可以通過Netsh.exe這個程序來實現:
- 于是轉到對Netsh.exe的逆向分析上來,經過仔細分析,發現Netsh.exe對dns的修改是通過netiohlp.dll的NhIpHandleSetDnsServer來實現的:
? - 通過進一步定位發現是NhIpAddDeleteSetServer:
- 并發現會通過寫入注冊表來保存相關信息:
并通過定位發現注冊表地址是:
- 并且有重啟Dnscahe服務等相關操作:
- 1)通過設置系統全局鉤子來掛鉤系統下所有進程然后掛鉤SetRegvalue等api監控,該進程通過SetWindowHookEx來設置全局鉤子(其實該掛鉤方式不能掛鉤沒有消息循環的經常),通過inject-helper.exe進程來掛鉤發現不能掛鉤系統下的所有進程,而且新創建的進程也無法掛鉤。
2)通過設置KnowDlls注冊表發現也無法正常掛鉤所有進程。
3)通過底層驅動掛鉤,這個方法能監控到應用層的所有進程對注冊表的操作,但為了回溯到目標進程,可能也需要加入對父子進程的回溯,這個相對麻煩一些。 - 筆者采用相對比較簡單容易操作的方法。采用ProcMon來對注冊表的監控:
1)設置第一項篩選:operation is RegSetValue 操作
2)設置第二項篩選:path is HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{33701f65-c437-47a4-9162-071bd72b3425}\NameServer (復制這個字符串修改UUID即可)
最后的效果如下圖:
設置好后可以看到監控效果:
- 但是我們需要追蹤最初始的設置Dns的進程,比如進程A調用了Netsh或DllHost等其他的三方進程來設置Dns,這個時候僅僅監控到Netsh或者DllHost等進程是沒用的,需要對進程進行父進程的回溯,才知道源操作進程。
- 這個時候需要寫一個ProcMon的插件,然后在ProcMon在監控到操作進程后能第一時間對父進程進行回溯。
編寫一個Dll插件,并通過窗口子類化方式來對ProcMon的ListView控件進行消息監控:
?static DWORD WINAPI DoWork(LPVOID param){swprintf_s(g_szProfilePath, L"%s\\record_%d.log", GetCurrentExePath().c_str(), GetCurrentProcessId());do {HWND hwndTaskManager = FindWindowW(L"PROCMON_WINDOW_CLASS", L"Process Monitor - Sysinternals: www.sysinternals.com");if (!hwndTaskManager){MessageBox(NULL, L"查找進程窗口失敗", L"錯誤", NULL);break;}DWORD TaskManagerPID = 0;GetWindowThreadProcessId(hwndTaskManager, &TaskManagerPID);if (TaskManagerPID != GetCurrentProcessId()){MessageBox(NULL, L"TaskManagerPID != GetCurrentProcessId()", L"錯誤", NULL);break;}//EnumChildWindows(hwndTaskManager, _EnumChildProc, NULL);g_hListView = FindWindowExW(hwndTaskManager, NULL, L"SysListView32", L"");if (!g_hListView){MessageBox(NULL, L"獲取listview句柄為空", L"錯誤", NULL);break;}fnOriginNetworkList = (WNDPROC)SetWindowLongPtrW(g_hListView,GWLP_WNDPROC,(LONG_PTR)NetworkListWndProc);} while (0);return 0;}
// 這里是ListView控件的消息處理函數 LRESULT CALLBACK NetworkListWndProc(HWND hwnd, // handle to windowUINT_PTR uMsg, // message identifierWPARAM wParam, // first message parameterLPARAM lParam // second message parameter){if (uMsg == LVM_SETITEMTEXT || uMsg == LVM_SETITEMTEXTA || uMsg == 4211){ wchar_t szSection[50] = { 0 };wsprintf(szSection, L"%d_%d", g_iPrevItem, pItem->iItem);wchar_t szSubItem[50] = { 0 };wsprintf(szSubItem, L"%d", pItem->iSubItem);WritePrivateProfileStringW(szSection, szSubItem, pItem->pszText, g_szProfilePath);if (pItem->iSubItem == 3) // process ID{time_t tm = time(NULL);struct tm now;localtime_s(&now, &tm);wchar_t str[100] = { 0 };wcsftime(str, sizeof(str) / 2, L"%A %c", &now);WritePrivateProfileStringW(szSection, L"time", str, g_szProfilePath);DWORD pid = _wtoi(pItem->pszText);stuProcNode node = { _wtoi(pItem->pszText), szSection };PushProcNodeQueue(node); // 這里不卡頓消息線程,把父進程回溯拋到另一個獨立線程處理}}LRESULT rc = CallWindowProc(fnOriginNetworkList,hwnd,uMsg,wParam,lParam);return rc;}
// 這里是父進程回溯操作 static DWORD WINAPI DoQueryProcess(LPVOID param){while (1){WaitForSingleObject(g_hQueryProcEvt, INFINITE); // 新的請求進入隊列會觸發事件std::vector<stuProcNode> ProcNodeList;GetProcNodeQueue(ProcNodeList);for (int i = 0; i < ProcNodeList.size(); i++){std::wstring strpPidNameList;DWORD pid = ProcNodeList[i].dwPid;while (1){ // 回溯一下父進程HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);if (hProcess == (HANDLE)-1 || hProcess == 0){break;}WCHAR szPath[MAX_PATH] = { 0 };GetProcessImageFileName(hProcess, szPath, MAX_PATH);strpPidNameList += L" [";wchar_t buffer[20] = { 0 };_itow_s(pid, buffer, 20, 10);strpPidNameList += buffer;strpPidNameList += L" ";strpPidNameList += wcsrchr(szPath, L'\\') ? wcsrchr(szPath, L'\\') + 1 : L"NULL";strpPidNameList += L"] ";PROCESS_BASIC_INFORMATION pbi = { 0 };if (0 == NtQueryInformationProcess(hProcess, 0, &pbi, sizeof(pbi), NULL)){pid = pbi.InheritedFromUniqueProcessId;}else{break;}CloseHandle(hProcess);}WritePrivateProfileStringW(ProcNodeList[i].strSection.c_str(), L"PidNameINFO", strpPidNameList.c_str(), g_szProfilePath);}}return 0;}
- 代碼寫好了,這個時候通過CFF軟件修改ProcMon的導入表,使其依賴我們的插件,這個時候當ProcMon啟動的時候就會自動加載我們的插件了,相當于變相注入到了ProcMon進程。
看看效果: - 這樣的監控程序就算寫好了,可以交付運維去部署監控了,一旦監控到就會輸出到日志文件,并把父子進程進行了回溯。
- ?