Запрос к NTP-серверам на Ассемблере
Запрос к NTP-серверам на Ассемблере
NTP - сетевой протокол для синхронизации внутренних часов компьютера с использованием локальной сети или сети общего доступа, такой как Интернет. С его помощью любой желающий может обеспечить точным временем свои компьютеры или другие устройства. Я тоже отношусь к таким желающим, поэтому тоже озадачился этим вопросом и написал на Ассемблере функцию синхронизации времени по NTP-серверу.
В интернете есть немало открытых NTP-серверов, их имена и адреса без труда находятся любым поисковиком. Чтобы получить точное время, достаточно просто послать им запрос на определенный порт (я использую порт 37 - TIME) и прочитать ответ. Возвращаемый результат по запросу TIME представляет собой 64-битное число (8 байт), состоящее из 32-битного счетчика секунд и 32-битного счетчика долей секунды. Также следует учитывать, что время NTP отсчитывается с полуночи 1 января 1900 года, а не с 1970, поэтому его нужно дополнительно корректировать, чтобы совместить с принятой в системе точкой отсчета времени. В моей функции синхронизации этот важный момент учитывается. Но сперва надо определить некоторые структуры и константы в сегменте данных. Сделать все данные локальными не получилось, функция вылетает с ошибкой. Но как вариант, можно внутри функции динамически выделять под них память.
Code (Assembler) : Убрать нумерацию
- ; Адрес NTP-сервера
- ntp db 'time.nist.gov',0
- ; Структуры для работы со временем
- ltime SYSTEMTIME
- ftime FILETIME
- ; Данные для запроса к серверу
- wsadata WSADATA
- saddr sockaddr_in
- IPPROTO_TCP = 6
Code (Assembler) : Убрать нумерацию
- ;----------------------------------------------------------
- ; Функция синхронизации времени по NTP-серверу
- ;----------------------------------------------------------
- ; Параметры:
- ; szNtp - указатель на строку с именем NTP-сервера или IP
- ; На выходе:
- ; EAX = 1 - синхронизация прошла успешно
- ; EAX = 0 - ошибка синхронизации
- ;----------------------------------------------------------
- proc sync_time szNtp:DWORD
- locals
- result dd ?
- hSock dd ?
- ntp_buff rb 10
- endl
- pusha
- ; По умолчанию результат - неудача
- mov [result],0
- ; NTP-сервер задан?
- mov eax,[szNtp]
- cmp byte[eax],0
- je .loc_ret
- invoke WSAStartup,0101h,wsadata
- ; Это IP-адрес?
- stdcall text2ip,[szNtp]
- or eax,eax
- jnz @f
- ; Попробовать отрезолвить адрес по имени
- invoke gethostbyname,[szNtp]
- or eax,eax
- jz .loc_ret
- mov eax,[eax+hostent.h_addr_list]
- mov eax,[eax]
- mov eax,[eax]
- @@:
- mov [saddr.sin_addr],eax
- mov al,00
- mov ah,37 ; port 37
- mov [saddr.sin_port],ax
- mov [saddr.sin_family],AF_INET
- ; Создать сокет
- invoke socket, AF_INET, SOCK_STREAM, IPPROTO_TCP
- cmp eax,-1
- jne @f
- invoke WSACleanup
- jmp .loc_ret
- @@:
- mov [hSock], eax
- xchg eax, esi
- ; Создать соединение
- invoke connect, esi, saddr, sizeof.sockaddr_in
- cmp eax,-1
- jne @f
- invoke closesocket,esi
- invoke WSACleanup
- jmp .loc_ret
- @@:
- ; Отправить и получить данные с NTP-сервера
- invoke send,esi,.sender,2,0
- lea eax,[ntp_buff]
- invoke recv, esi, eax, 10, 0
- invoke closesocket,esi
- invoke WSACleanup
- ; NTP считает дату от 1 января 1900 года
- mov [ltime.wYear],1900
- mov [ltime.wMonth],1
- mov [ltime.wDay],1
- mov [ltime.wHour],0
- mov [ltime.wMinute],0
- mov [ltime.wSecond],0
- invoke SystemTimeToFileTime,ltime,ftime
- ; Количество секунд по данным NTP-сервера
- lea eax,[ntp_buff]
- mov eax,[eax]
- or eax,eax
- jz .loc_ret
- bswap eax
- xor edx,edx
- mov ecx,989680h
- mul ecx
- add [ftime.dwLowDateTime],eax
- adc [ftime.dwHighDateTime],edx
- invoke FileTimeToSystemTime,ftime,ltime
- ; Установить новое время
- invoke SetSystemTime,ltime
- mov [result],1
- .loc_ret:
- popa
- mov eax,[result]
- ret
- .sender db 10
- endp
Code (Assembler) : Убрать нумерацию
- ;----------------------------------------------------------
- ; Функция преобразования строки IP-адреса в DWORD
- ;----------------------------------------------------------
- ; Параметры:
- ; szIP - указатель на строку с IP-адресом
- ; На выходе:
- ; EAX > 0 - преобразованный IP-адрес
- ; EAX = 0 - переданная строка не является IP-адресом
- ;----------------------------------------------------------
- proc text2ip szIP:DWORD
- locals
- result dd ?
- endl
- pusha
- mov [result],0
- xor ebx,ebx
- mov esi,[szIP]
- ; Первый октет
- cmp byte [esi],'1'
- jb .loc_ret
- cmp byte [esi],'9'
- ja .loc_ret
- stdcall str2dec,esi
- cmp eax,255
- ja .loc_ret
- add ebx,eax
- shl ebx,8
- inc esi
- cmp eax,10
- jb @f
- inc esi
- cmp eax,100
- jb @f
- inc esi
- @@:
- cmp byte [esi],'.'
- jne .loc_ret
- inc esi
- ; Второй и третий октет
- mov ecx,2
- .loc23:
- cmp byte [esi],'0'
- jb .loc_ret
- cmp byte [esi],'9'
- ja .loc_ret
- stdcall str2dec,esi
- cmp eax,255
- ja .loc_ret
- add ebx,eax
- shl ebx,8
- inc esi
- cmp eax,10
- jb @f
- inc esi
- cmp eax,100
- jb @f
- inc esi
- @@:
- cmp byte [esi],'.'
- jne .loc_ret
- inc esi
- loop .loc23
- ; Четвертый октет
- cmp byte [esi],'0'
- jb .loc_ret
- cmp byte [esi],'9'
- ja .loc_ret
- stdcall str2dec,esi
- cmp eax,255
- ja .loc_ret
- add ebx,eax
- bswap ebx
- mov [result],ebx
- .loc_ret:
- popa
- mov eax,[result]
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;----------------------------------------------------------
- ; Функция преобразования десятичной строки в число
- ;----------------------------------------------------------
- ; На входе: указатель на строку
- ; На выходе: EAX = число или 0 если не получилось
- ;----------------------------------------------------------
- proc str2dec lpStr:dword
- push ebx edx esi
- xor eax,eax
- mov esi,[lpStr]
- .str2dec_loop:
- movsx ebx,byte [esi]
- sub bl,'0'
- ; Для системы счисления с другим основанием замените следующую
- ; строчку на cmp bl,основание_системы
- cmp bl,10
- jnb .str2dec_ret
- ; Для системы счисления с другим основанием замените следующую
- ; строчку на imul eax,основание_системы
- imul eax,10
- add eax,ebx
- inc esi
- jmp .str2dec_loop
- .str2dec_ret:
- pop esi edx ebx
- ret
- endp
По просьбам трудящихся сделал программу SyncTime, которая синхронизирует системное время с NTP-сервером. Адрес сервера передается в командной строке, если он недоступен или не указан, синхронизация выполняется с одним из серверов из внутреннего списка.
Коды возврата (ERRORLEVEL):
0 = ошибка синхронизации
1 = время успешно синхронизировано
Пример использования в файле synctime.bat
Просмотров: 3479 | Комментариев: 16
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
Sergey_K
(12.04.2016 в 14:30):
Спасибо, искал что-то подобное(SyncTime).
ManHunter
(15.03.2016 в 23:59):
Меняется SystemTime, а не LocalTime. Часовые пояса тут вообще остаются не при делах.
Как-то так.
Как-то так.
Compiller
(15.03.2016 в 23:55):
Стоило бы заодно проверять наличие последних изменений базы часовых поясов в реестре. А то мало ли что...
Kemper
(15.03.2016 в 20:09):
ManHunter, трудящиеся в восторге!
ManHunter
(15.03.2016 в 12:16):
Добавил программку SyncTime. Описание читайте в readme, пример использования в файле synctime.bat
Можно юзать в планировщике или в автозапуске, или где это требуется.
UPD: добавил запрос привилегий процессом, а то SetSystemTime с обычными правами недоступна.
Можно юзать в планировщике или в автозапуске, или где это требуется.
UPD: добавил запрос привилегий процессом, а то SetSystemTime с обычными правами недоступна.
Андрей
(15.03.2016 в 06:34):
Удобная, минимум функций но в последнее время стала определять на час больше реального. От ManHunter определяет правильно.
Спасибо :)
Compiller
(14.03.2016 в 13:22):
Так есть же всякие Neutron, DayTime, MSI i-Speeder, InternetTime и прочие утилиты...
Последний даже с исходниками.
Последний даже с исходниками.
ManHunter
(12.03.2016 в 19:32):
Наколхозю какое-нибудь отдельное поделие.
Kemper
(12.03.2016 в 13:54):
а чего не 123 порт? таки может выбор порта сделать
и можно чтоб хоть какое то сообщение выдавалось - а то непонятно синхронизировалось или пингвины с гор катаются)
губозакаточную машинку уже собираю)
и можно чтоб хоть какое то сообщение выдавалось - а то непонятно синхронизировалось или пингвины с гор катаются)
губозакаточную машинку уже собираю)
Vladimir
(12.03.2016 в 10:49):
Пробовал сначала в Ольке патчить, потом пересобрал исходник, а всё равно в errorlevel-е пусто, странно. Продолжаем эксперименты.
ManHunter
(12.03.2016 в 10:33):
У меня не сработало, поэтому пришлось велосипедить.
Jon
(12.03.2016 в 09:51):
gethostbyname работает как с доменами, так и с текстовым представлением айпи
ManHunter
(11.03.2016 в 18:12):
Vladimir, достаточно поменять одну строчку invoke ExitProcess,0 на invoke ExitProcess,eax и результат синхронизации будет возвращаться в %errorlevel%, например, в .bat-файле
Ну и перекомпилировать исходник, конечно.
Ну и перекомпилировать исходник, конечно.
Василий
(11.03.2016 в 17:00):
Спасибо! Забрал в коллекцию ))
Локальными можно, если заранее обнулить
Локальными можно, если заранее обнулить
Vladimir
(11.03.2016 в 16:05):
Если бы еще можно было параметром указывать сервер для синхронизации и получать сообщение о её успешности/неуспешности - было бы идеально. У меня почему-то отвалилась системная синхронизация, так что приходится пользоваться сторонними утилитами: cmdtime 3 отсюда http://www.softshape.com/download/.
Добавить комментарий
Заполните форму для добавления комментария
Спасибо!