├── LICENSE ├── README.md ├── build.sh └── main.c /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 John Kindem 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 😀Scanner 2 | --- 3 | > FBI warning 该程序需要在linux上运行,而不是windows,请不要尝试在windows环境下编译或运行 4 | 5 | `Scanner`是一个网络扫描器,可以扫描当前局域网下的所有主机,并显示他们的主机名、`IP`、开放的`TCP`端口,是`NUAA 网络通信实现技术`的课设 6 | 7 | # 💻 编译 8 | 如果还没有安装编译套件,请先安装`build-essential`包,以`Ubuntu/Debian`系为例,指令如下: 9 | 10 | ``` 11 | sudo apt-get install build-essential 12 | ``` 13 | 14 | 接下来,进入克隆的项目的根目录: 15 | 16 | ``` 17 | cd ./scanner 18 | ``` 19 | 20 | 使用我写的编译脚本开始编译: 21 | 22 | ``` 23 | ./build.sh 24 | ``` 25 | 26 | 如果无法执行,尝试赋予更高的权限或以`sudo`账户执行: 27 | 28 | ``` 29 | sudo chmod 777 ./build.sh 30 | sudo ./build.sh 31 | ``` 32 | 33 | # 🏃 运行 34 | 基本运行: 35 | 36 | ``` 37 | sudo ./scanner -ip ipAddress [startPort] [endPort] [threadNum] 38 | sudo ./scanner -lan [startPort] [endPort] [threadNum] 39 | ``` 40 | 41 | 参数及用法说明: 42 | * `-ip`:扫描特定`IP`地址的主机的`TCP`端口 43 | * `ipAddress`:要扫描的主机的`IP`地址 44 | * `startPort`:扫描开始的端口号 45 | * `endPort`:扫描结束的端口号 46 | * `threadNum`:同时扫描的线程数 47 | * `-lan`:扫描当前局域网下所有主机的`IP`地址、主机名,并且对所有主机进行`TCP`端口扫描 48 | * `startPort`:扫描开始的端口号 49 | * `endPort`:扫描结束的端口号 50 | * `threadNum`:同时扫描的线程数 51 | 52 | # 😘 关于 53 | * 江湖艺名:John Kindem 54 | * ID in NUAA:161520311 55 | 56 | 觉得不错的话记得点个小星星⭐ 57 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | gcc main.c -o scanner -pthread 2 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | // 宏定义 17 | #define MAX_LAN_HOST_NUM 200 18 | #define BUFFER_SIZE 1024 19 | #define CHAR_BUFFER_SIZE 40 20 | #define DEFAULT_THREAD_NUM 8 21 | #define DEFAULT_START_PORT 1 22 | #define DEFAULT_END_PORT 1024 23 | 24 | // 函数定义 25 | void showLanHostAndIP(); 26 | int tcpPortScan(char *, int); 27 | int scan(char *, int, int, int); 28 | void *scanThreadFunc(void *args); 29 | 30 | // 定义结构体 31 | // 主机结构体 32 | typedef struct { 33 | char name[40]; 34 | char ip[40]; 35 | } Host; 36 | // 扫描线程参数结构体 37 | typedef struct { 38 | int pthreadNum; 39 | char *ip; 40 | int port; 41 | int end; 42 | } ScanThreadArgs, *ScanThreadArgsPtr; 43 | 44 | // 全局变量 45 | // 用于存储局域网主机名和IP的数组 46 | Host lanHosts[MAX_LAN_HOST_NUM]; 47 | int lanHostsNum = 0; 48 | 49 | int main(int argc, char *argv[]) { 50 | // 首先将本机也加入 LanHosts 中 51 | strcpy(lanHosts[lanHostsNum].name, "localhost"); 52 | strcpy(lanHosts[lanHostsNum++].ip, "127.0.0.1"); 53 | 54 | // 初始化参数 55 | int startPort = DEFAULT_START_PORT, endPort = DEFAULT_END_PORT, threadNum = DEFAULT_THREAD_NUM; 56 | int i; 57 | 58 | // 参数判断 59 | if (argc < 2) { 60 | printf("Usage:\n"); 61 | printf("1. ./scanner -lan\n"); 62 | printf("2. ./scanner -ip ipAddress\n"); 63 | return 0; 64 | } 65 | 66 | // 两种用法 67 | if (!strcmp(argv[1], "-lan")) { 68 | // 获取并显示局域网主机名和IP 69 | showLanHostAndIP(); 70 | 71 | // 获取参数 72 | if (argc > 1) startPort = atoi(argv[2]); 73 | if (argc > 2) endPort = atoi(argv[3]); 74 | if (argc > 3) threadNum = atoi(argv[4]); 75 | 76 | // 对每一个局域网主机,都进行一遍扫描 77 | for (i = 0; i < lanHostsNum; i++) { 78 | scan(lanHosts[i].ip, startPort, endPort, threadNum); 79 | } 80 | } else if (!strcmp(argv[1], "-ip")) { 81 | // 获取参数 82 | if (argc > 2) startPort = atoi(argv[3]); 83 | if (argc > 3) endPort = atoi(argv[4]); 84 | if (argc > 4) threadNum = atoi(argv[5]); 85 | 86 | // 开始扫描 87 | scan(argv[2], startPort, endPort, threadNum); 88 | } else { 89 | printf("Unknown params.\n"); 90 | } 91 | 92 | return 0; 93 | } 94 | 95 | // 显示局域网主机名和IP 96 | void showLanHostAndIP() { 97 | // 文件结构体 98 | FILE *hostNameFile, *hostIpFile; 99 | // 缓冲区 100 | char buffer1[CHAR_BUFFER_SIZE], buffer2[CHAR_BUFFER_SIZE]; 101 | int i; 102 | 103 | // 执行shell指令获取局域网所有主机名和IP,并将结果输出到.host 104 | printf("\n"); 105 | system("sudo arp -a | cut -d \" \" -f 1 > .hname"); 106 | system("sudo arp -a | cut -d \" \" -f 2 | sed \"s/\(//g\" | sed \"s/)//g\" > .hip"); 107 | 108 | // 打开文件 109 | hostNameFile = fopen("./.hname", "r"); 110 | hostIpFile = fopen("./.hip", "r"); 111 | 112 | // 不断读取并且存入结构体 113 | while (fgets(buffer1, CHAR_BUFFER_SIZE, hostNameFile) && fgets(buffer2, CHAR_BUFFER_SIZE, hostIpFile)) { 114 | // 如果最后一个字符是 \n,将其改成 \0 115 | if (buffer1[strlen(buffer1) - 1] == '\n') buffer1[strlen(buffer1) - 1] = '\0'; 116 | if (buffer2[strlen(buffer2) - 1] == '\n') buffer2[strlen(buffer2) - 1] = '\0'; 117 | 118 | // 拷贝 119 | strcpy(lanHosts[lanHostsNum].name, buffer1); 120 | strcpy(lanHosts[lanHostsNum++].ip, buffer2); 121 | } 122 | 123 | // 关闭文件 124 | fclose(hostNameFile); 125 | fclose(hostIpFile); 126 | 127 | // 先输出局域网主机信息 128 | printf("All host info in LAN: \n"); 129 | printf("No\tName\t\tIP\n"); 130 | for (i = 0; i < lanHostsNum; i++) { 131 | printf("%d\t%s\t\t%s\n", i + 1, lanHosts[i].name, lanHosts[i].ip); 132 | }; 133 | printf("\n"); 134 | } 135 | 136 | // 扫描函数 137 | int scan(char *ip, int startPort, int endPort, int threadNum) { 138 | // 线程结构体数组 139 | pthread_t *pthreads; 140 | // 线程参数结构体数组 141 | ScanThreadArgsPtr argsArray; 142 | 143 | int i, pthreadNum = threadNum; 144 | 145 | // 输出日志 146 | printf("\nScanning %s......\n", ip); 147 | 148 | // 分配空间 149 | pthreads = (pthread_t *) malloc(sizeof(pthread_t) * pthreadNum); 150 | argsArray = (ScanThreadArgsPtr) malloc(sizeof(ScanThreadArgs) * pthreadNum); 151 | 152 | // 建立线程 153 | for (i = 0; i < pthreadNum; i++) { 154 | // 设置参数 155 | memset(&argsArray[i], 0, sizeof(argsArray[i])); 156 | argsArray[i].pthreadNum = pthreadNum; 157 | argsArray[i].port = startPort + i; 158 | argsArray[i].ip = ip; 159 | argsArray[i].end = endPort; 160 | 161 | if (pthread_create(&pthreads[i], NULL, scanThreadFunc, (void *) &argsArray[i]) == -1) { 162 | printf("Can;t create pthread! Please try again!\n"); 163 | return 0; 164 | } 165 | } 166 | 167 | // 睡一会,让子线程先执行 168 | sleep(1); 169 | 170 | // 等待线程释放 171 | for (i = 0; i < pthreadNum; i++) { 172 | pthread_join(pthreads[i], NULL); 173 | } 174 | 175 | // 释放空间 176 | free(pthreads); 177 | free(argsArray); 178 | 179 | // 日志 180 | printf("Scan down.\n\n"); 181 | 182 | return 0; 183 | } 184 | 185 | // 扫描线程函数 186 | void *scanThreadFunc(void *args) { 187 | // 获取参数 188 | ScanThreadArgsPtr temp; 189 | temp = (ScanThreadArgsPtr) args; 190 | 191 | // 一个线程扫描一部分端口 192 | while (temp->port <= temp->end) { 193 | // 如果扫描到了,则在日志中输出 194 | if (tcpPortScan(temp->ip, temp->port)) printf("Port %d tcp - Open\n", temp->port); 195 | // TODO UDP 196 | 197 | temp->port += temp->pthreadNum; 198 | } 199 | } 200 | 201 | // tcp 端口扫描 202 | int tcpPortScan(char *ip, int port) { 203 | // socket文件描述符,文件描述符状态,连接状态 204 | int sockfd, len, fcntlStatus, connectStatus; 205 | // socket地址结构体 206 | struct sockaddr_in addr; 207 | // 超时 208 | struct timeval timeout; 209 | // 读写用的文件句柄 210 | fd_set fdr, fdw; 211 | 212 | // 置空结构体 213 | memset(&addr, 0, sizeof(addr)); 214 | memset(&timeout, 0, sizeof(timeout)); 215 | 216 | // 设置为IP通信 217 | addr.sin_family = AF_INET; 218 | // 设置地址结构体中的IP地址 219 | addr.sin_addr.s_addr = inet_addr(ip); 220 | // 设置地址结构体中的端口号 221 | addr.sin_port = htons(port); 222 | 223 | // 创建 socket 套接字 224 | if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 225 | return 0; 226 | } 227 | 228 | // 设置套接字为非阻塞模式 229 | fcntlStatus = fcntl(sockfd, F_GETFL, 0); 230 | if (fcntlStatus < 0) { 231 | close(sockfd); 232 | return 0; 233 | } 234 | fcntlStatus |= O_NONBLOCK; 235 | if (fcntl(sockfd, F_SETFL, fcntlStatus) < 0) { 236 | close(sockfd); 237 | return 0; 238 | } 239 | 240 | // 尝试连接 241 | connectStatus = connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)); 242 | if (connectStatus != 0) { 243 | if (errno == EINPROGRESS) { 244 | FD_ZERO(&fdr); 245 | FD_ZERO(&fdw); 246 | FD_SET(sockfd, &fdr); 247 | FD_SET(sockfd, &fdw); 248 | // 设置1s超时 249 | timeout.tv_sec = 1; 250 | timeout.tv_usec = 0; 251 | connectStatus = select(sockfd + 1, &fdr, &fdw, NULL, &timeout); 252 | 253 | // 如果连接超时或者调用失败 254 | if (connectStatus <= 0 || connectStatus == 2) { 255 | close(sockfd); 256 | return 0; 257 | } 258 | 259 | // 如果连接成功 260 | if (connectStatus == 1 && FD_ISSET(sockfd, &fdw)) { 261 | close(sockfd); 262 | return 1; 263 | } 264 | close(sockfd); 265 | return 0; 266 | } 267 | } 268 | } 269 | --------------------------------------------------------------------------------