// PortScan.cpp : Defines the entry point for the console application.
//
///=============================================================================
/// PortScan - 連接埠掃描程式
///
/// 2005-7-8 16:49:52
///=============================================================================
#include <winsock2.h>
#include <stdio.h> //printf函數要用的標頭檔
#pragma comment(lib,"ws2_32.lib")
void Help();
DWORD WINAPI ScanThread(LPVOID lp);
void ReportAPIError();
//儲存掃描的主機IP和連接埠的結構
typedef struct{
char HostIp[17];
int ScanPort;
}HostStruct;
long lPortOpen = 0; // 統計開放連接埠數
int main(int argc, char* argv[])
{
WORD wVersion = MAKEWORD(2,0); //socket的版本
WSADATA wsaData;
int iFromPort; //開始連接埠
int iToPort; //結束連接埠
int iNowPort; //正在掃描的連接埠
int iPortCount; //連接埠總數
//如果命令列下參數不是4個(包括portscan.exe本身),提示正確的用法
if(argc != 4)
{
Help();
return -1;
}
//儲存使用者輸入的要掃描的開始端點口和結束連接埠,由於使用者輸入的是char型,所以要先轉成int型
iFromPort = atoi(argv[2]);
iToPort = atoi(argv[3]);
//對使用者輸入的連接埠進行判斷
if(iFromPort > iToPort || iFromPort < 0 || iFromPort >65535 || iToPort <0 || iToPort >65535)
{
printf("開始端點口不能大於結束連接埠,且範圍為:1-65535!/n");
return 0;
}
if (WSAStartup(wVersion , &wsaData))
{
printf("初始化失敗!");
return -1;
}
//要掃描的連接埠總數
iPortCount = iToPort - iFromPort + 1;
printf("連接埠範圍: %d -> %d,共%d個./n", iFromPort,iToPort , iPortCount);
printf("/n======= 開始掃描 =======/n");
// 根據連接埠數建立線程控制代碼數
HANDLE *hThreads = new HANDLE[iPortCount];
ZeroMemory(hThreads,sizeof(HANDLE)*iPortCount);
//
// 根據連接埠數建立線程參數結構指標,使用它只是為了記錄結構指標,用於主線程釋放動態記憶體
//
HostStruct** pScanHosts = new HostStruct*[iPortCount];
ZeroMemory(pScanHosts,sizeof(HostStruct*)*iPortCount);
//迴圈串連連接埠,以判斷連接埠是否開放
int iThreadCount = 0; // 成功建立的線程數目計數
int iParamCount = 0; // 線程控制代碼數組索引
for(iParamCount = 0, iNowPort = iFromPort; iNowPort <= iToPort; iParamCount++,iNowPort++)
{
// 為單獨的線程提供單獨的線程參數,此參數不能在局部棧上分配
pScanHosts[iParamCount] = new HostStruct;
strcpy( pScanHosts[iParamCount]->HostIp,argv[1]);
pScanHosts[iParamCount]->ScanPort = iNowPort;
HANDLE hThread;
hThread = CreateThread(NULL,NULL,ScanThread,pScanHosts[iParamCount],NULL,NULL);
if(hThread == NULL)
{
ReportAPIError();
continue;
}
else
hThreads[iThreadCount++] = hThread;//只統計正常啟動並執行線程
}
printf("線程建立完畢!/n");
//
// 等待線程執行完畢,這一步是必須的,否則上面分配的動態記憶體會過早釋放,線程執行時參數資料將出現異常
//
// WaitForMultipleObjects 所能等待的對象最大不能超過MAXIMUM_WAIT_OBJECTS (64)個,如果超過,進行分組等待
//
for(int iGroup = 0; iThreadCount>0; iGroup ++, iThreadCount -= MAXIMUM_WAIT_OBJECTS)
{
DWORD res = WaitForMultipleObjects(
iThreadCount>MAXIMUM_WAIT_OBJECTS ? MAXIMUM_WAIT_OBJECTS : iThreadCount,
hThreads+iGroup*MAXIMUM_WAIT_OBJECTS,
TRUE,
INFINITE);
if(res == WAIT_FAILED)
{
ReportAPIError();
continue;
}
}
printf("掃描完畢!/n");
printf("共開放連接埠: %ld 個 /n",lPortOpen);
// 釋放所有動態分配的記憶體
delete[iPortCount] hThreads;
for(int ii=0; ii < iPortCount; ii ++ )
{
delete pScanHosts[ii];
}
delete[iPortCount] pScanHosts;
WSACleanup();
return 0;
}
//==============================================================================
// 線程函數
// 2005-7-8 16:52:41
//==============================================================================
DWORD WINAPI ScanThread(LPVOID lp)
{
struct sockaddr_in sin; //sockaddr_in結構
SOCKET s; //儲存建立socket時的傳回值
//將參數傳化成HostStruct結構指標
HostStruct *lpScanHost=(HostStruct*)lp;
s = socket(AF_INET,SOCK_STREAM,0);
if(s == INVALID_SOCKET)
{
printf("建立socket()失敗!/n");
// WSACleanup();//這不是你應該在這裡做的
}
//給結構成員賦值
sin.sin_family = AF_INET;
sin.sin_port = htons(lpScanHost->ScanPort);
sin.sin_addr.S_un.S_addr = inet_addr(lpScanHost->HostIp);
//建立串連
if(connect(s,(struct sockaddr*)&sin,sizeof(sin)) == SOCKET_ERROR)
{
printf("%s -> %d:未開放/n",lpScanHost->HostIp,lpScanHost->ScanPort);
// closesocket(s);//not necessary!
}
else
{
printf("%s -> %d:開放/n",lpScanHost->HostIp,lpScanHost->ScanPort);
//iPortOpen ++;
InterlockedIncrement(&lPortOpen);
closesocket(s);
}
return 0;
}
//以下為協助函數內容
void Help()
{
printf("/nPortScan V1.0 by:∮明天去要飯/n");
printf("/nPortScan V1.1 by:meteor135 smith_135@163.com/n");
printf("Usage:/n");
printf(" PortScan.exe <TargetIP> <BeginPort> <EndPort>/n");
printf("Example:/n");
printf(" PortScan.exe 127.0.0.1 1 445/n");
}
//==============================================================================
// 報告API錯誤
// 2005-7-8 16:52:19
//==============================================================================
void ReportAPIError()
{
LPVOID lpMsgBuf;
if (!FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL ))
{
// Handle the error.
return ;
}
// Display the string.
printf("%s/n",lpMsgBuf);
// Free the buffer.
LocalFree( lpMsgBuf );
}
特此感謝 meteor135(流星雨) 對我的程式進行的改進。現在這個多線程的掃描器,速度是挺快,不過還是有些小問題,以後如果有更好的版本,我會發出來給大家研究的。