同时扫描多个端口时结果不可靠
Unreliable results when scanning multiple ports concurrently
背景:
我正在阅读 Black Hat Go,作者介绍了一个使用 go 例程的简单端口扫描器1:
package main
import (
"fmt"
"net"
)
func main() {
for i := 1; i <= 9000; i++ {
go func(j int) {
address := fmt.Sprintf("127.0.0.1:%d", j)
conn, err := net.Dial("tcp", address)
if err != nil {
return
}
conn.Close()
fmt.Printf("%d open\n", j)
}(i)
}
}
然后他提到了以下内容:
Scanning an excessive number of hosts or ports simultaneously may
cause network or system limitations to skew your results.
为了测试它,我在端口 8000 和 8500 上启动了 2 个 php 服务器 2 和 运行 上面的代码来扫描我的本地端口。
每次都给我不一致的结果。有时它会检测到两个打开的端口,有时则不会。
问题:
是否由于 TCP 的某些限制导致结果不一致?
有没有办法计算出可以并行扫描的最佳端口数,从而使结果保持正确?
编辑:
我似乎在上面的代码中遗漏了等待组。
除此之外,是否有其他任何东西(OS 限制或协议限制)阻止在大型 运行ge 上进行并发端口扫描?
您的主函数将在 for 循环结束后立即退出。如果 main 函数退出,那么由它启动的所有 goroutines 也会退出。您需要等待 goroutines 完成。例如,这可以通过 sync.WaitGroup
实现。
package main
import (
"fmt"
"net"
"sync"
"time"
)
func main() {
// This will help you to keep track of the goroutines
var wg sync.WaitGroup
for i := 1; i <= 9000; i++ {
// Increment the counter for each goroutine you start.
wg.Add(1)
go func(j int) {
// Make sure the wait group counter is decremented when the goroutine exits
defer wg.Done()
address := fmt.Sprintf("127.0.0.1:%d", j)
conn, err := net.DialTimeout("tcp", address, 2 * time.Second)
if err != nil {
return
}
conn.Close()
fmt.Printf("%d open\n", j)
}(i)
}
// Wait for all goroutines to finish before exiting main
wg.Wait()
}
编辑: 对我来说,原来的代码由于缺少文件描述符而无法正常工作。以下功能可靠地工作。
它需要更好的错误处理,但确实有效
package main
import (
"fmt"
"log"
"net"
"sync"
"time"
)
var minPort = 1
var maxPort = 65535
var timeout = 2 * time.Second
const parallel = 50
func main(){
fmt.Println("portscan called")
// Create a buffered channel with a size equal to the number of goroutines
ctrl := make(chan int, parallel)
// Keep track of the currently active goroutines
var wg sync.WaitGroup
for p := 1; p <= parallel; p++ {
wg.Add(1)
// Start a goroutine...
go func(p int) {
log.Printf("Starting goroutine %d", p)
// ...listening to the control channel.
// For every value this goroutine reads from the
// channel...
for i := range ctrl {
address := fmt.Sprintf("127.0.0.1:%d", i)
// ...try to conncet to the port.
conn, err := net.DialTimeout("tcp", address, timeout)
if err == nil {
conn.Close()
log.Printf("[%3d]: %5d open", p, i)
}
// TBD: ERROR HANDLING!!!
}
// If the channel is closed, this goroutine is done.
wg.Done()
log.Printf("[%3d]: Exiting", p)
}(p)
}
// Fill the control channel with values.
// If the channel is full, the write operation
// to the channel will block until one of the goroutines
// reads a value from it.
for i := minPort; i <= maxPort; i++ {
ctrl <- i
}
// We have sent all values, so the channel can be closed.
// The goroutines will finish their current connection attempt,
// notice that the channel is closed and will in turn call wg.Done().
close(ctrl)
// When all goroutines have announced that they are done, we can exit.
wg.Wait()
}
背景:
我正在阅读 Black Hat Go,作者介绍了一个使用 go 例程的简单端口扫描器1:
package main
import (
"fmt"
"net"
)
func main() {
for i := 1; i <= 9000; i++ {
go func(j int) {
address := fmt.Sprintf("127.0.0.1:%d", j)
conn, err := net.Dial("tcp", address)
if err != nil {
return
}
conn.Close()
fmt.Printf("%d open\n", j)
}(i)
}
}
然后他提到了以下内容:
Scanning an excessive number of hosts or ports simultaneously may cause network or system limitations to skew your results.
为了测试它,我在端口 8000 和 8500 上启动了 2 个 php 服务器 2 和 运行 上面的代码来扫描我的本地端口。
每次都给我不一致的结果。有时它会检测到两个打开的端口,有时则不会。
问题:
是否由于 TCP 的某些限制导致结果不一致?
有没有办法计算出可以并行扫描的最佳端口数,从而使结果保持正确?
编辑:
我似乎在上面的代码中遗漏了等待组。
除此之外,是否有其他任何东西(OS 限制或协议限制)阻止在大型 运行ge 上进行并发端口扫描?
您的主函数将在 for 循环结束后立即退出。如果 main 函数退出,那么由它启动的所有 goroutines 也会退出。您需要等待 goroutines 完成。例如,这可以通过 sync.WaitGroup
实现。
package main
import (
"fmt"
"net"
"sync"
"time"
)
func main() {
// This will help you to keep track of the goroutines
var wg sync.WaitGroup
for i := 1; i <= 9000; i++ {
// Increment the counter for each goroutine you start.
wg.Add(1)
go func(j int) {
// Make sure the wait group counter is decremented when the goroutine exits
defer wg.Done()
address := fmt.Sprintf("127.0.0.1:%d", j)
conn, err := net.DialTimeout("tcp", address, 2 * time.Second)
if err != nil {
return
}
conn.Close()
fmt.Printf("%d open\n", j)
}(i)
}
// Wait for all goroutines to finish before exiting main
wg.Wait()
}
编辑: 对我来说,原来的代码由于缺少文件描述符而无法正常工作。以下功能可靠地工作。
它需要更好的错误处理,但确实有效
package main
import (
"fmt"
"log"
"net"
"sync"
"time"
)
var minPort = 1
var maxPort = 65535
var timeout = 2 * time.Second
const parallel = 50
func main(){
fmt.Println("portscan called")
// Create a buffered channel with a size equal to the number of goroutines
ctrl := make(chan int, parallel)
// Keep track of the currently active goroutines
var wg sync.WaitGroup
for p := 1; p <= parallel; p++ {
wg.Add(1)
// Start a goroutine...
go func(p int) {
log.Printf("Starting goroutine %d", p)
// ...listening to the control channel.
// For every value this goroutine reads from the
// channel...
for i := range ctrl {
address := fmt.Sprintf("127.0.0.1:%d", i)
// ...try to conncet to the port.
conn, err := net.DialTimeout("tcp", address, timeout)
if err == nil {
conn.Close()
log.Printf("[%3d]: %5d open", p, i)
}
// TBD: ERROR HANDLING!!!
}
// If the channel is closed, this goroutine is done.
wg.Done()
log.Printf("[%3d]: Exiting", p)
}(p)
}
// Fill the control channel with values.
// If the channel is full, the write operation
// to the channel will block until one of the goroutines
// reads a value from it.
for i := minPort; i <= maxPort; i++ {
ctrl <- i
}
// We have sent all values, so the channel can be closed.
// The goroutines will finish their current connection attempt,
// notice that the channel is closed and will in turn call wg.Done().
close(ctrl)
// When all goroutines have announced that they are done, we can exit.
wg.Wait()
}