从编程角度揭示病毒感染原理 "十步杀一人,千里不留痕"用这样的诗句来比喻如今的病毒是最恰当不过的在微软NT系列操作系统中我们日常的应用程序启动都逃不过任务管理器的掌控熟悉进程的电脑好爱者或是系统管理员通过查看系统任务管理器可以在很短的时间内找出系统中正在运行的可疑程序,于是那些病毒作者便想尽办法希望可以逃过系统管理员最直观的肉眼识别有很多木马会将自己命名成和系统文件名极其相似,希望以此来混水摸鱼逃过系统管理员的眼睛,比如SVCHOST.exe(系统)-SVCH0ST.exe(病毒);SERVICES.exe(系统)-SERVLCES.exe(病毒)等等还有一些干脆就用加载同名不同路径的方法,那么你就无法直接用名称来辨别了 (一)DLL远程注入的技术背景 当调用CreateProcess时,系统就会创建一个进程内核对象该进程内核对象不是进程本身,而是操作系统管理进程时使用的一个较小的数据结构可以将进程内核对象视为由进程的统计信息组成的一个较小的数据结构然后,系统为新进程创建一个虚拟地址空间,并将可执行文件或任何必要的DLL文件的代码和数据加载到该进程的地址空间中然后,系统为新进程的主线程创建一个线程内核对象(纯属个人理解)请注意必要这个关键词,如果不是必要的DLL文件,我们是否可以模仿其加载必要DLL文件的代码和数据的方法将那些不是必要的东东混进去呢? 总的来讲DLL远程注入技术就是将自己代码加载到宿主进程的空间去执行,并且共享宿主进程的空间资源从而达到隐形的目的因为根本不存在自身的进程,所以在任务管理器上也无法显示出来 (二)具体实现步骤
//****************************************************************************** #include <windows.h> BOOL APIENTRY DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) { switch (fdwReason) { case DLL_PROCESS_ATTACH: //假设"病毒"被加载运行 MessageBox(NULL,TEXT("你中毒了!"),TEXT("提示"),MB_OK); break; case DLL_PROCESS_DETACH: //如果此"病毒"被清除,那么也将出提示对话框 MessageBox(NULL,TEXT("病毒已被清除!"),TEXT("提示"),MB_OK); break; } return true; } //****************************************************************************** 这段代码虽然是小儿科,但它反映出了Non-MFC DLL编程最基本的框架,包括文件被加载和卸载的事件过程 2.接下来我要将此"病毒"远程注入到别的进程去运行显然我不可能在其DLL文件上双击鼠标DLL文件只能通过exe文件才能加载到进程空间如此,我就得为其写一个专门用来注入进程的exe程序首先我得物色一个用来注入的宿主进程我选择了Explorer.exe这个系统进程,目前大多数病毒都选择此进程为注入目标所以在开始我必须找到此进程(获得Explorer.exe进程的ID)代码如下:
//************************寻找并获取iexplorer进程的ID******************************* DWORD kuaizhao() { PROCESSENTRY32 pe32; pe32.dwSize = sizeof(pe32); HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessSnap == INVALID_HANDLE_VALUE) { return false; } BOOL bMore =Process32First(hProcessSnap, &pe32); while (bMore) { if ( !stricmp(pe32.szExeFile, "explorer.exe") ) { //获取该进程的句柄 HANDLE handle=OpenProcess (PROCESS_ALL_ACCESS, false,pe32.th32ProcessID); if (handle==NULL) { exit(0); } CloseHandle(hProcessSnap); CloseHandle(handle); break; } else bMore =Process32Next(hProcessSnap, &pe32); } //返回此进程ID return pe32.th32ProcessID; } //******************************************************************************
通过上面这段代码,我们就可以得到当前系统中Explorer.exe的动态ID了有了进程ID,我们就可以打开宿主进程,对其进行访问和操作了!我们的操作很简单,就是将"病毒"DLL文件加载到此进程中加载DLL文件需要用到LoadLibrary这个API函数,其语法格式为: 但是在Win32系统下,每个进程都拥有自己的4G虚拟地址空间,在这里,我们当作参数传入的字符串"D://ks.dll"(假设病毒文件路径为"D://ks.dll")其实是一个数值,它表示这个字符串位于ks.exe(假设我用来注入ks.dll文件的exe文件加载程序名称为ks.exe)地址空间之中的地址,而这个地址在传给Explorer.exe之后,它指向的东西就失去了有效性因为各个进程之间都是相互独立的,我们在ks.exe进程空间中传递的"D://ks.dll"这一数值到Explorer.exe进程空间后就不是"D://ks.dll"这一概念了这好比我们拿第11栋学生公寓205寝室的钥匙去开第12栋学生公寓205寝室的门,同样的钥匙遇到不同的门,当然无法打开了 由此看来,我就需要做这么一系列略显繁杂的手续首先在宿主进程中分配一段内存空间,然后向这段空间写入我们要加载的DLL路径,最后再调用LoadLibrary来加载DLL文件,不同的是此时LoadLibrary函数中的参数取自宿主进程空间中写入的DLL路径的数值于是原本应该一句LoadLibrary( "D://ks.dll" );
//********************线程注入************************************************** BOOL InjectDll(const char *DllFullPath, const DWORD dwRemoteProcessId) { HANDLE hProcess; hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dwRemoteProcessId ); char *pszdll; //在远程进程的内存地址空间分配DLL文件名空间 pszdll = (char *) VirtualAllocEx( hProcess, NULL, lstrlen(DllFullPath)+1, MEM_COMMIT, PAGE_READWRITE); //将DLL的路径名写入到远程进程的内存空间 WriteProcessMemory(hRemoteProcess, pszdll, (void *) DllFullPath, lstrlen(DllFullPath)+1, NULL); DWORD dwID; LPVOID pFunc = LoadLibrary; HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, pszdll, 0, &dwID ); CloseHandle(hProcess); CloseHandle(hThread); return TRUE; } //*******************************************************************************
好了,现在你可以尝试着整理这些代码并编译运行那么我猜想你会关心exe加载程序(如本文中的ks.exe)在这一过程所扮演的角色分量因为实现远程注入的代码都是写在exe加载程序中的足以说明其分量的举足轻重它本身不是病毒,但它的存亡直接关系到病毒DLL文件的存亡 当exe加载程序运行时,它会不可避免的在系统中新建一个ks.exe的进程,但是当它运行结束后程序的生命周期也就结束了那一过程在一瞬间就完成所以这一新进程也会在瞬间消亡但此时病毒体已经被加载进了宿主进程空间"十步杀一人,千里不留痕"的行动已在不知不觉中秘密展开了当然,本例的病毒程序只是在系统中弹出一个简单的对话框并显示"你中毒了!",但你在任务管理器上看不到任何与之有关的进程出现与其说是进程被隐藏倒不如说根本就没创建过进程 当然,每次启动系统时exe加载程序必须首先运行那么你必须将其打入系统启动项中关于写注册表代码请参考后面部分,由于exe加载程序的地位重大病毒作者们通常会在DLL代码中加入了监控和修复其加载程序的功能一旦其加载文件被删,那么内存中的病毒代码会在瞬间将其恢复这就是为什么我们平常手工将病毒文件删除后不久又出现了,或者当我们将注册表里的病毒启动项删除后刷新一下又复原的原因所在 总结一下,利用了远程注入进程技术的病毒,可以在系统中踏腊无痕水上飘但关键还在于病毒的内部代码的合理编写,如果内部代码写的烂,那么会严重干扰系统的正常运行,导致电脑速度卡,系统死机甚至系统崩溃,蓝屏(那些系统破坏型病毒除外)我个人认为这种技术用在木马上较多因为木马讲究的是隐蔽,在宁静的海域,风平浪静,一面孤帆随风飘来,谁知不远处还有一艘潜艇正伺机而动... (三)兵来将挡 那么我认为步骤大致如下:
//****************自定义卸载病毒DLL模块函数******************************************** bool KillDll(const char *DllFullPath,const DWORD ProcessId) { HANDLE Process; Process = OpenProcess( PROCESS_ALL_ACCESS,FALSE, ProcessId ); char *psplease; psplease = (char *) VirtualAllocEx( Process, NULL, lstrlen(DllFullPath)+1, MEM_COMMIT, PAGE_READWRITE); WriteProcessMemory(Process,psplease, (void *) DllFullPath, lstrlen(DllFullPath)+1, NULL); DWORD dwExit; DWORD dwID; // 使目标进程调用GetModuleHandle,获得DLL在目标进程中的句柄 LPVOID pFunc = GetModuleHandleA; //插入目标进程来获取病毒模块的句柄 HANDLE hThread = CreateRemoteThread(Process,NULL, 0,(LPTHREAD_START_ROUTINE)pFunc,psplease,0,&dwID); if (!hThread) { return false; } WaitForSingleObject(hThread,INFINITE); /* 获得GetModuleHandle返回病毒模块的退出信息,存在dwExit变量中 这里因为FreeLibrary需要该模块的退出码 */ GetExitCodeThread(hThread,&dwExit); pFunc = FreeLibrary; hThread = CreateRemoteThread(Process, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, (LPVOID)dwExit, 0, &dwID); WaitForSingleObject( hThread, INFINITE ); if (!hThread) { return false; } VirtualFreeEx(Process, psplease, lstrlen(DllFullPath)+1, MEM_DECOMMIT ); CloseHandle(Process); CloseHandle(hThread); return true; } //*************************************************************************************** 2. 将病毒残余信息从系统中清除在这只给出注册表清理例子: //**********************自定义注册表清除病毒启动项函数*********************************** bool Inreg() { HKEY hkey; LPCTSTR dataPath="software//Microsoft//Windows NT//CurrentVersion//Winlogon//"; long rt=RegOpenKeyEx(HKEY_LOCAL_MACHINE,dataPath,0,KEY_ALL_ACCESS,&hkey); LPCTSTR pk="Userinit"; unsigned char ks[512]="C://WINDOWS//system32//userinit.exe"; unsigned char query[512]; DWORD size=512; if (rt==ERROR_SUCCESS) { long jj=RegQueryValueEx(hkey,pk,0,NULL,query,&size); if (jj==ERROR_SUCCESS) { if (!stricmp((char*)query, (char*)ks)) { return true; } else { size=512; long xie=RegSetValueEx(hkey,pk,0,REG_SZ,ks,size); if (xie==ERROR_SUCCESS) { return true; } return false; } } else return false; } else { return false; } RegCloseKey(hkey); } //******************************************************************************************
好了这期就先到这! |
----------------------------