我试图在nodejs中编写一个简单的portscan脚本。您可以对scanme.nmap.org运行此脚本。通过运行nmap
本身,我知道这个主机有四个开放端口:22,80,9929,31333。
我的脚本是:
var net = require('net');
var host = 'scanme.nmap.org';
var max = 35000;
function scanPort(port) {
try {
var c = new net.Socket();
c.setTimeout(5000);
c.connect({
port: port,
host: host
})
c.on('connect', function () {
console.log(port);
})
c.on('error', function (err) {
c.destroy();
})
c.on('timeout', function () {
c.destroy();
})
} catch (err) {
console.error(err);
}
}
for (i = 1; i<=max; i++) {
scanPort(i);
}
如果我从i = 1
启动循环,脚本将检测端口22和80,此后,它将以退出代码0退出。它不会检测到其他两个端口。如果我使用变量i
,即跳过第一个端口并设置i = 9500
,它将正确检测端口9929。如果我设置i = 31000
,则检测到端口31337,也会发生相同的情况。
我真的不明白为什么它没有按预期工作。我怀疑某些系统限制导致了一些错误,但我尝试将扫描功能包装在try
块中,但没有设法检测到任何错误。
答案 0 :(得分:4)
您可能正在尝试一次打开太多连接并耗尽资源或在接收服务器上被阻止以充斥它。
因为所有套接字都是异步的,所以for
循环会立即尝试打开35,000个套接字。虽然服务器可以配置为执行此操作,但它需要一个非常特殊的配置,并且使用每个插槽需要一个低内存方案(您不会在这里)。
因此,一个简单的解决方案是限制一次打开多少个套接字到一些合理的数量。
这里有一些代码最初会打开固定数量的套接字,然后当每个套接字完成时,它会打开另一个,保持一定数量的开放和活动套接字。它目前设置为一次打开100
个套接字。您可以在特定的环境和操作系统中进行试验,以查看在不产生问题的情况下可以达到多高的数量:
var net = require('net');
var host = 'scanme.nmap.org';
var low = 1;
var high = 35000;
var maxInFlight = 100;
function scanPort(port) {
return new Promise(function(resolve, reject) {
var c = new net.Socket();
c.setTimeout(5000);
c.connect({
port: port,
host: host
})
c.on('connect', function () {
console.log(port);
c.destroy();
resolve(port);
})
c.on('error', function (err) {
c.destroy();
reject(err);
})
c.on('timeout', function () {
c.destroy();
reject({code: "Timeout", port: port});
})
});
}
var cntr = low;
var inFlightCnt = 0;
function run() {
while (inFlightCnt < maxInFlight && cntr <= high) {
++inFlightCnt;
scanPort(cntr++).then(function(port) {
--inFlightCnt;
run();
}, function(err) {
--inFlightCnt;
run();
});
}
}
run();
注意:一次设置为100
时,需要一段时间才能运行所有端口,最多35,000个。它确实找到了你提到的四个开放端口。
而且,这是另一个版本,它收集有关每个端口的信息以及它如何失败/成功,以便最后获得转储。它还显示了控制台的进度,以便您可以查看它是否仍在运行以及完成的距离:
var net = require('net');
var host = 'scanme.nmap.org';
var low = 1;
var high = 35000;
var maxInFlight = 200;
function scanPort(port) {
return new Promise(function(resolve, reject) {
var c = new net.Socket();
c.setTimeout(15000);
c.connect({
port: port,
host: host
})
c.on('connect', function () {
c.destroy();
resolve(port);
})
c.on('error', function (err) {
c.destroy();
reject(err);
})
c.on('timeout', function () {
c.destroy();
reject({code: "timeout", port: port});
})
});
}
var cntr = low;
var inFlightCnt = 0;
var openPorts = [];
var timeouts = [];
var refused = [];
var otherErrors = [];
function run() {
while (inFlightCnt < maxInFlight && cntr <= high) {
++inFlightCnt;
scanPort(cntr++).then(function(port) {
--inFlightCnt;
openPorts.push(port);
console.log(openPorts);
run();
}, function(err) {
if (err.code === "timeout" || err.code === "ETIMEDOUT") {
timeouts.push(err.port);
} else if (err.code === "ECONNREFUSED") {
refused.push(err.port);
} else {
otherErrors.push(err.port);
}
console.log(err.code + ": " + err.port);
--inFlightCnt;
run();
});
}
// if we are all done here, log the open ports
if (inFlightCnt === 0 && cntr >= high) {
console.log("open: " + JSON.stringify(openPorts));
console.log("timeouts: " + JSON.stringify(timeouts));
console.log("otherErrors: " + JSON.stringify(otherErrors));
}
}
run();
这是我在带有节点v4.0.0的Windows 10上运行时生成的输出:
open: [22,80,9929,31337]
timeouts: [25,135,136,137,138,139,445,974,984,972,965,963,964,978,985,980,975,981,987,971,960,977,1000,992,990,986,991,997,1391,1384,7455,7459,7450,7506,7512,23033,23736,33635,33640,33638,33641,33634,33636,33633]
otherErrors: []
我不知道为什么有些端口会超时。大多数获得ECONNREFUSED
,这是您对未打开的端口所期望的,有些从ETIMEDOUT
库中获取net
,并且在超时时有一些超时你的代码(我增加到15秒)。
另外,请注意我在连接后添加了一个destroy。你离开那些套接字打开,只有一对连接时,这可以正常工作,但如果有很多端口连接可能会有问题。
注意事项注意事项:在测试这个程序的过程中,我运行了很多次,大约20次运行后,我的互联网连接断了一段时间,我重启了一下电缆调制解调器让事情重新开始。我怀疑电缆调制解调器内部的某些东西在测试时无法一次又一次地处理这种快速的请求。但是,由于异常活动(大量的端口请求),它也可能是Comcast关闭我的连接。
当然,这可能都是巧合而且我的小故障可能与我正在做的事情没有任何联系,但时间似乎太过关联我认为这只是巧合。
这个程序可以进一步修改为&#34;减慢速度&#34;通过计量每秒不超过N端口探测。