黑客编程入门(一)
黑客編程入門(一)
從一個簡單的木馬說起
平臺:Windows
環境:VS6.0
語言:C
基本功能 :
??????????????? 獲取目標主機(服務端 )的計算機名和用戶名
????????????????當客戶端發送指令 "getpcname“時在客戶端打印計算機名
??????????????? 當客戶端發送指令"getusername”時在客戶端打印用戶名
知識儲備:
1 什么是C/S結構
C/S結構即客戶機服務器結構,簡單來說我們的木馬就是種植在服務端的,可以獲取服務端的信息,而我們在客戶端進行一系列的操作來控制木馬的活動以使我們的小馬
可以對服務器進行控制。話說我們的瀏覽器就是一個通用的客戶端呢。
2 TCP協議和UDP協議
我們的木馬的客戶端和服務端肯定要進行通信,兩者之間不斷地發送信息,信息的傳遞當然需要制定一些規則來確保信息的真實性,正確性,因此人們就制定了一系列的大家通用的協議來規范通信的標準,這就有了TCP和UDP(自然不止這兩種了,關于協議的更詳細的內容如果感興趣自己查閱 ) 為了能進行通信,協議規定每個終端都要將各自字符集中的字符先變換為標準字符集的字符后,才進入網絡傳送,到達目的終端之后,再變換為該終端字符集的字符。也就是說我們的小馬在收集好服務端的信息后也要遵循這個準則,將信息統一轉換為標準的字符集進行傳遞,客戶端再進行解析轉換。
?
基本知識準備完畢,開始行動,具體所用的函數和庫我們將在完成代碼時一點點的了解。
首先最核心的功能——獲取主機的計算機名和用戶名。
1. 獲取計算機名的函數:
?
BOOL WINAPI GetComputerName(_Out_ LPTSTR lpBuffer,_Inout_ LPDWORD lpnSize );
參數解釋:
lpBuffer是一個輸出參數,是一個指向緩沖區的指針,用于接受計算機名,它的size必須足夠大來保存MAX_COMPUTERNAME_LENGTH + 1 characters
lpnSize是一個輸入輸出參數,所謂輸入即限定了lpBuffer的size,而輸出即返回的計算機名的大小,不包含末尾的空字符
MSDN地址:http://msdn.microsoft.com/zh-cn/ms724295
2. 獲取用戶名的函數:
?
BOOL WINAPI GetUserName(_Out_ LPTSTR lpBuffer,_Inout_ LPDWORD lpnSize );??
和GetComputerName的參數基本相同 詳細內容:MSDN :http://msdn.microsoft.com/zh-cn/subscriptions/ms724432.aspx
?
接下來我們就來看下基本的實現的完整代碼,暫時不進行通信,別著急慢慢來:
#include<stdio.h> #include<windows.h> #define MAX_SIZE 20 int main(int argc,char* argv[]) {char szComputerName[MAX_SIZE] = {0};char szUserName[MAX_SIZE] = {0}; unsigned long nSize = MAXBYTE;GetComputerName(szComputerName,&nSize);printf("Computer name is %s \n",szComputerName);nSize = MAXBYTE;GetUserName(szUserName,&nSize);printf("User name is %s \n",szUserName);return 0; }哈,最核心的代碼我們已經完成了,是不是很簡單?
然后我們要實現完整的服務端代碼:
預備知識:
1 WinSock接口:
Windows Socket的簡稱,也成為Windows套接字,是微軟根據BSD UNIX 操作系統中流行的Berkeley套接字規范而實現的一套Windows下的網絡編程接口?。我們的小木馬的網絡通信就是基于WinSock實現的,當然還有其他的類似的庫,這里就不一一介紹了。
使用時我們要引入 #include<winsock2.h> 和 #pragma comment (lib,"ws2_32")
2 WinSock服務端開發流程:
WinSock的初始化(對應函數WSAStartupup( ) )——> 建立套接字socket(對應函數socket( ) )——>綁定IP和端口 (對應函數bind( )?)?——> 監聽端口 (對應函數listen( ) )——> 接受請求(對應函數accept( ) )——> 發送或者接收信息 (對應函數(send( ) / recv( ) )——> 關閉套接字 (對應函數closesocket( ) )——> 結束動態鏈接庫
(對應函數( WSACleanup( ) )
int WSAStartup {WORD wVersionRequested,LPWSADATA lpWSAData };該函數的第一個參數指明程序請求使用的Socket版本,其中高位字節 指明副版本、低位字節指明主版本;
操作系統利用第二個參數返回請求的Socket的版本信息。
af 指定通信協議簇,對于TCP/IP協議,該參數為PF_INET
type 指定要創建的套接字類型
protocol 指定使用的通信協議,具體和第二個參數有關,第二個參數為SOCK_STREAM則該參數為IPPROTO_TCP ,
?????????????? 若第二個參數為SOCK_DGRAM,則該參數為IPPROTO_UDP
?
int bind( SOCKET s, const struct sockaddr* name,int namlen );s 服務器端套接字
name?制定一個sockaddr結構
namelen?指定緩沖區的長度
這里有必要介紹一下sockaddr結構——
struct sockaddr_in { short int sin_family; /* Address family */ unsigned short int sin_port; /* Port number */ struct in_addr sin_addr; /* Internet address */ unsigned char sin_zero[8]; /* Same size as struct sockaddr */ };
sin_family 指代協議族
sin_port 存儲端口號(使用網絡字節順序)
sin_addr 存儲IP地址,使用in_addr這個數據結構
sin_zero 是為了讓sockaddr與sockaddr_in兩個數據結構保持大小相同而保留的空字節。
第一個參數很明顯就是處于監聽狀態的套接字
backlog 客戶連接請求隊列
SOCKET accept(SOCKET s, struct sockaddr FAR* addr,int FAR* addrlen );s:套接口描述字,該套接口在listen()后監聽連接。
addr:(可選)指針,指向一緩沖區,其中接收為通訊層所知的連接實體的地址。Addr參數的實際格式由套接口創建時所產生的地址族確定。
addrlen:(可選)指針,指向存有addr地址長度的整形數。
buf是個指向發送的緩沖區的指針,也就是個地址
len是要發送的數據大小,一般是和buf的大小有關
flag 一般設置為0
所需要的函數基本講完了,接下來真正的動手寫代碼吧,上代碼
/*服務器端代碼*/ #include<winsock2.h> #include<windows.h> #pragma comment (lib,"ws2_32") #define MAX_SIZE 20 typedef struct _SYS_INFO {char szComputerName[MAX_SIZE];//保存計算機名char szUserName[MAX_SIZE];//保存當前用戶登錄名 }SYS_INFO,*PSYS_INFO; void GetSysInfo(PSYS_INFO sysinfo) {unsigned long nSize = MAXBYTE;GetComputerName(sysinfo->szComputerName,&nSize);nSize = MAXBYTE;GetUserName(sysinfo->szUserName,&nSize); }; int main(int argc,char* argv[]) {//winsock的初始化WSADATA wsaData;WSAStartup(MAKEWORD(2,2),&wsaData);//創建套接字SOCKET s = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);//進行IP和端口的綁定sockaddr_in sockaddr;sockaddr.sin_family = PF_INET;sockaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");sockaddr.sin_port = htons(827);bind(s,(SOCKADDR*)&sockaddr,sizeof(SOCKADDR));//監聽端口listen(s,1);//接收請求SOCKADDR clientAddr;int Size = sizeof(SOCKADDR);SOCKET clientSock;clientSock = accept(s,(SOCKADDR*)&clientAddr,&Size);//注意,accept函數返回一個新的套接字while(true){//向客戶端發送的信息,用于模擬一個命令行界面send(clientSock,"Hacker@Shell>",strlen("Hacker@Shell>")+sizeof(char),NULL);char buff[MAXBYTE] = {0};//保存接受的信息//接收信息recv(clientSock,buff,MAXBYTE,NULL);//根據從客戶端接收到的指令進行相應的操作if(!strcmp(buff,"getpcname")||(!strcmp(buff,"getusername"))){SYS_INFO SysInfo = {0};GetSysInfo(&SysInfo);send(clientSock,(const char*)&SysInfo,sizeof(SYS_INFO),NULL);}}closesocket(clientSock);//關閉socketclosesocket(s);//關閉socketWSACleanup();//結束動態鏈接庫return 0; }
客戶端的代碼實現和服務器端比較類似 ,直接看完整的代碼:
下面看一下運行的結果吧:
總結
- 上一篇: 从跨境电商到成功转行数据分析师,我拒绝了
- 下一篇: C语言删除链表的倒数第N个节点