Се­год­ня мы с тобой в рам­ках изу­чения Go напишем прос­тую прог­рамму, которая смо­жет прос­каниро­вать откры­тые пор­ты по задан­ному адре­су.

Ес­ли ты видишь Go впер­вые, нач­ни с чте­ния пре­дыду­щей статьи «Go кодить», там я пос­тарал­ся объ­яснить базовые кон­цепции, в осо­бен­ности те, что отли­чают Go от дру­гих язы­ков.

warning

Статья име­ет озна­коми­тель­ный харак­тер и пред­назна­чена для спе­циалис­тов по безопас­ности, про­водя­щих тес­тирова­ние в рам­ках кон­трак­та. Автор и редак­ция не несут ответс­твен­ности за любой вред, при­чинен­ный с при­мене­нием изло­жен­ной информа­ции. Рас­простра­нение вре­донос­ных прог­рамм, наруше­ние работы сис­тем и наруше­ние тай­ны перепис­ки прес­леду­ются по закону.

Для начала соз­дадим новый модуль:

mkdir portscan && cd $_
go mod init portscan
touch main.go

В файл main.go вста­вим сле­дующий код:

package main
import (
"fmt"
"net"
"os"
"time"
)
func main() {
if len(os.Args) <= 1 {
fmt.Fprintf(os.Stderr, "Target address not specified\n")
os.Exit(1)
}
target := os.Args[1]
if net.ParseIP(target) == nil {
fmt.Fprintf(os.Stderr, "%s is not a valid address\n", target)
os.Exit(1)
}
ports := []string{"21", "22", "25", "53", "80", "8080", "443", "110", "143", "3306", "3389"}
timeout := 250 * time.Millisecond
for _, port := range ports {
addr := net.JoinHostPort(target, port)
conn, err := net.DialTimeout("tcp", addr, timeout)
if err != nil {
continue
}
conn.Close()
fmt.Printf("Port %s is open\n", port)
}
os.Exit(0)
}

Ар­гумен­ты коман­дной стро­ки содер­жатся в сре­зе строк os.Args, при­чем пер­вый эле­мент — это имя самого исполня­емо­го фай­ла. Мы ожи­даем, что адрес для ска­ниро­вания будет передан как аргу­мент, поэто­му про­веря­ем количес­тво эле­мен­тов в сре­зе вызовом len().

Так­же ты можешь видеть, как мы выводим текст в стан­дар­тный поток оши­бок os.Stderr и воз­вра­щаем код выхода вызовом os.Exit() (ненуле­вой в слу­чае ошиб­ки или ноль при успешном завер­шении работы). Все эти воз­можнос­ти нам пре­дос­тавля­ет пакет os — заг­ляни в до­кумен­тацию, там еще мно­го инте­рес­ного!

info

Яв­но вызывать os.Exit(0) излишне, штат­но завер­шающаяся прог­рамма в любом слу­чае вер­нет код 0. Я добавил эту стро­ку в код толь­ко для наг­ляднос­ти.

 

net

По­доб­но тому как os пре­дос­тавля­ет средс­тва вза­имо­дей­ствия с опе­раци­онной сис­темой, пакет net обес­печива­ет сетевой ввод‑вывод и мно­жес­тво вспо­мога­тель­ных фун­кций. Одну из них мы исполь­зуем для валида­ции вве­ден­ного поль­зовате­лем адре­са: net.ParseIP() пар­сит стро­ку и воз­вра­щает перемен­ную типа IP, содер­жащую бай­ты адре­са, или nil при неуда­че. Фун­кция net.JoinHostPort() объ­еди­няет IP и порт в одну стро­ку, а net.DialTimeout() под­клю­чает­ся по ука­зан­ному адре­су c ука­зан­ным тайм‑аутом и воз­вра­щает откры­тое соеди­нение, в которое мож­но писать и читать, или nil и ошиб­ку.

Да­лее объ­явля­ем срез, содер­жащий номера пор­тов, которые мы будем ска­ниро­вать. Обра­ти вни­мание на син­таксис объ­явле­ния: если бы мы ука­зали раз­мер ports := [10]string{ ..., то резуль­татом был бы мас­сив. Не ука­зывая раз­мер, мы получа­ем срез. В дан­ном слу­чае для нас нет осо­бой раз­ницы, но если бы мы в будущем собира­лись изме­нить раз­мер этой кол­лекции динами­чес­ки, то мас­сив нам не подошел бы. Фун­кцию make() мы здесь не исполь­зовали, пос­коль­ку сра­зу же ини­циали­зиро­вали срез зна­чени­ям.

 

time

Те­перь погово­рим еще об одном пакете, так­же вхо­дящем в стан­дар­тную биб­лиоте­ку, — time. Он пре­дос­тавля­ет фун­кции для работы с вре­менем, датами, интерва­лами и раз­ные пре­обра­зова­ния. Сре­ди про­чих инте­рес­ностей в нем содер­жится тип time.Timer, поз­воля­ющий генери­ровать однократ­ные события по исте­чении ука­зан­ного вре­мени, и time.Ticker, генери­рующий пос­ледова­тель­ность событий с опре­делен­ным интерва­лом.

Для изме­рения дли­тель­нос­тей (нап­ример, что­бы узнать, как дол­го выпол­нялся какой‑то код) полез­на фун­кция time.Since(). Мы же исполь­зуем опре­деле­ние time.Millisecond, что­бы задать жела­емый тайм‑аут.

 

for

На­конец мы доб­рались до самого инте­рес­ного. Аргу­мент (адрес целево­го хос­та) счи­тан и валиди­рован, набор ска­ниру­емых пор­тов опре­делен, тайм‑аут попыт­ки под­клю­чения задан. Теперь мы переби­раем в цик­ле все пор­ты и к каж­дому из них про­буем под­клю­чить­ся. Если попыт­ка неудач­на, перехо­дим к сле­дующе­му пор­ту. В про­тив­ном слу­чае зак­рыва­ем открыв­шееся соеди­нение и выводим в кон­соль сооб­щение с номером пор­та.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    3 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии