Blog. Just Blog

Запрос к NTP-серверам на Ассемблере

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Запрос к NTP-серверам на Ассемблере
Запрос к NTP-серверам на Ассемблере

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

В интернете есть немало открытых NTP-серверов, их имена и адреса без труда находятся любым поисковиком. Чтобы получить точное время, достаточно просто послать им запрос на определенный порт (я использую порт 37 - TIME) и прочитать ответ. Возвращаемый результат по запросу TIME представляет собой 64-битное число (8 байт), состоящее из 32-битного счетчика секунд и 32-битного счетчика долей секунды. Также следует учитывать, что время NTP отсчитывается с полуночи 1 января 1900 года, а не с 1970, поэтому его нужно дополнительно корректировать, чтобы совместить с принятой в системе точкой отсчета времени. В моей функции синхронизации этот важный момент учитывается. Но сперва надо определить некоторые структуры и константы в сегменте данных. Сделать все данные локальными не получилось, функция вылетает с ошибкой. Но как вариант, можно внутри функции динамически выделять под них память.
  1. ; Адрес NTP-сервера
  2. ntp             db 'time.nist.gov',0
  3.  
  4. ; Структуры для работы со временем
  5. ltime           SYSTEMTIME
  6. ftime           FILETIME
  7.  
  8. ; Данные для запроса к серверу
  9. wsadata         WSADATA
  10. saddr           sockaddr_in
  11.  
  12. IPPROTO_TCP     = 6
И сама функция синхронизации:
  1. ;----------------------------------------------------------
  2. ; Функция синхронизации времени по NTP-серверу
  3. ;----------------------------------------------------------
  4. ; Параметры:
  5. ;   szNtp - указатель на строку с именем NTP-сервера или IP
  6. ; На выходе:
  7. ;   EAX = 1 - синхронизация прошла успешно
  8. ;   EAX = 0 - ошибка синхронизации
  9. ;----------------------------------------------------------
  10. proc sync_time szNtp:DWORD
  11.  
  12. locals
  13.         result   dd ?
  14.         hSock    dd ?
  15.         ntp_buff rb 10
  16. endl
  17.  
  18.         pusha
  19.  
  20.         ; По умолчанию результат - неудача
  21.         mov     [result],0
  22.  
  23.         ; NTP-сервер задан?
  24.         mov     eax,[szNtp]
  25.         cmp     byte[eax],0
  26.         je      .loc_ret
  27.  
  28.         invoke  WSAStartup,0101h,wsadata
  29.  
  30.         ; Это IP-адрес?
  31.         stdcall text2ip,[szNtp]
  32.         or      eax,eax
  33.         jnz     @f
  34.  
  35.         ; Попробовать отрезолвить адрес по имени
  36.         invoke  gethostbyname,[szNtp]
  37.         or      eax,eax
  38.         jz      .loc_ret
  39.  
  40.         mov     eax,[eax+hostent.h_addr_list]
  41.         mov     eax,[eax]
  42.         mov     eax,[eax]
  43. @@:
  44.         mov     [saddr.sin_addr],eax
  45.         mov     al,00
  46.         mov     ah,37          ; port 37
  47.         mov     [saddr.sin_port],ax
  48.         mov     [saddr.sin_family],AF_INET
  49.  
  50.         ; Создать сокет
  51.         invoke  socket, AF_INET, SOCK_STREAM, IPPROTO_TCP
  52.         cmp     eax,-1
  53.         jne     @f
  54.  
  55.         invoke  WSACleanup
  56.         jmp     .loc_ret
  57. @@:
  58.         mov     [hSock], eax
  59.         xchg    eax, esi
  60.  
  61.         ; Создать соединение
  62.         invoke  connect, esi, saddr, sizeof.sockaddr_in
  63.         cmp     eax,-1
  64.         jne     @f
  65.  
  66.         invoke  closesocket,esi
  67.         invoke  WSACleanup
  68.         jmp     .loc_ret
  69. @@:
  70.         ; Отправить и получить данные с NTP-сервера
  71.         invoke  send,esi,.sender,2,0
  72.         lea     eax,[ntp_buff]
  73.         invoke  recv, esi, eax, 10, 0
  74.         invoke  closesocket,esi
  75.         invoke  WSACleanup
  76.  
  77.         ; NTP считает дату от 1 января 1900 года
  78.         mov     [ltime.wYear],1900
  79.         mov     [ltime.wMonth],1
  80.         mov     [ltime.wDay],1
  81.         mov     [ltime.wHour],0
  82.         mov     [ltime.wMinute],0
  83.         mov     [ltime.wSecond],0
  84.         invoke  SystemTimeToFileTime,ltime,ftime
  85.  
  86.         ; Количество секунд по данным NTP-сервера
  87.         lea     eax,[ntp_buff]
  88.         mov     eax,[eax]
  89.         or      eax,eax
  90.         jz      .loc_ret
  91.         bswap   eax
  92.         xor     edx,edx
  93.         mov     ecx,989680h
  94.         mul     ecx
  95.  
  96.         add     [ftime.dwLowDateTime],eax
  97.         adc     [ftime.dwHighDateTime],edx
  98.  
  99.         invoke  FileTimeToSystemTime,ftime,ltime
  100.  
  101.         ; Установить новое время
  102.         invoke  SetSystemTime,ltime
  103.  
  104.         mov     [result],1
  105. .loc_ret:
  106.         popa
  107.  
  108.         mov     eax,[result]
  109.         ret
  110.  
  111. .sender db      10
  112. endp
Единственный параметр вызова - szNtp - указатель на строку с именем NTP-сервера или его IP-адресом. На выходе EAX=1, если удалось получить результат и синхронизировать по нему системные часы или EAX=0 в случае неудачи. Для преобразования строки IP-адреса в DWORD я написал еще одну вспомогательную функцию. Если адрес NTP-сервера передан в виде доменного имени, то он резолвится через функцию gethostbyname, если в виде IP-адреса, то обрабатывается этой функцией.
  1. ;----------------------------------------------------------
  2. ; Функция преобразования строки IP-адреса в DWORD
  3. ;----------------------------------------------------------
  4. ; Параметры:
  5. ;   szIP - указатель на строку с IP-адресом
  6. ; На выходе:
  7. ;   EAX > 0 - преобразованный IP-адрес
  8. ;   EAX = 0 - переданная строка не является IP-адресом
  9. ;----------------------------------------------------------
  10. proc text2ip szIP:DWORD
  11.         locals
  12.                 result dd ?
  13.         endl
  14.  
  15.         pusha
  16.  
  17.         mov     [result],0
  18.         xor     ebx,ebx
  19.  
  20.         mov     esi,[szIP]
  21.         ; Первый октет
  22.         cmp     byte [esi],'1'
  23.         jb      .loc_ret
  24.         cmp     byte [esi],'9'
  25.         ja      .loc_ret
  26.         stdcall str2dec,esi
  27.         cmp     eax,255
  28.         ja      .loc_ret
  29.         add     ebx,eax
  30.         shl     ebx,8
  31.         inc     esi
  32.         cmp     eax,10
  33.         jb      @f
  34.         inc     esi
  35.         cmp     eax,100
  36.         jb      @f
  37.         inc     esi
  38. @@:
  39.         cmp     byte [esi],'.'
  40.         jne     .loc_ret
  41.         inc     esi
  42.  
  43.         ; Второй и третий октет
  44.         mov     ecx,2
  45. .loc23:
  46.         cmp     byte [esi],'0'
  47.         jb      .loc_ret
  48.         cmp     byte [esi],'9'
  49.         ja      .loc_ret
  50.         stdcall str2dec,esi
  51.         cmp     eax,255
  52.         ja      .loc_ret
  53.         add     ebx,eax
  54.         shl     ebx,8
  55.         inc     esi
  56.         cmp     eax,10
  57.         jb      @f
  58.         inc     esi
  59.         cmp     eax,100
  60.         jb      @f
  61.         inc     esi
  62. @@:
  63.         cmp     byte [esi],'.'
  64.         jne     .loc_ret
  65.         inc     esi
  66.         loop    .loc23
  67.  
  68.         ; Четвертый октет
  69.         cmp     byte [esi],'0'
  70.         jb      .loc_ret
  71.         cmp     byte [esi],'9'
  72.         ja      .loc_ret
  73.         stdcall str2dec,esi
  74.         cmp     eax,255
  75.         ja      .loc_ret
  76.         add     ebx,eax
  77.  
  78.         bswap   ebx
  79.         mov     [result],ebx
  80.  
  81. .loc_ret:
  82.         popa
  83.         mov     eax,[result]
  84.  
  85.         ret
  86. endp
Еще одна небольшая вспомогательная функция, которая уже проскакивала на этом сайте. Это просто преобразование строки в число. Продублирую ее еще раз, чтобы не искать.
  1. ;----------------------------------------------------------
  2. ; Функция преобразования десятичной строки в число
  3. ;----------------------------------------------------------
  4. ; На входе: указатель на строку
  5. ; На выходе: EAX = число или 0 если не получилось
  6. ;----------------------------------------------------------
  7. proc    str2dec lpStr:dword
  8.         push    ebx edx esi
  9.  
  10.         xor     eax,eax
  11.         mov     esi,[lpStr]
  12. .str2dec_loop:
  13.         movsx   ebx,byte [esi]
  14.         sub     bl,'0'
  15.         ; Для системы счисления с другим основанием замените следующую
  16.         ; строчку на cmp bl,основание_системы
  17.         cmp     bl,10
  18.         jnb     .str2dec_ret
  19.         ; Для системы счисления с другим основанием замените следующую
  20.         ; строчку на imul eax,основание_системы
  21.         imul    eax,10
  22.         add     eax,ebx
  23.         inc     esi
  24.         jmp     .str2dec_loop
  25.  
  26. .str2dec_ret:
  27.         pop     esi edx ebx
  28.         ret
  29. endp
В приложении пример программы с исходным текстом, которая использует функцию из статьи и при запуске пытается синхронизировать системное время с NTP-сервером time.nist.gov.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

NTP.Sync.Demo.zip (3,255 bytes)

По просьбам трудящихся сделал программу SyncTime, которая синхронизирует системное время с NTP-сервером. Адрес сервера передается в командной строке, если он недоступен или не указан, синхронизация выполняется с одним из серверов из внутреннего списка.

Коды возврата (ERRORLEVEL):
0 = ошибка синхронизации
1 = время успешно синхронизировано

Пример использования в файле synctime.bat

SyncTime 1.0SyncTime 1.0

SyncTime.1.0-PCL.zip (5,050 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 3420 | Комментариев: 16

Метки: Assembler, сеть
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
Vladimir (26.05.2016 в 13:59):
В последнее время стал шалить биос и пришлось занести вашу программу в автозагрузку, чтоб не лицезреть 2002 год в системе. Другие консольные аналоги, что я пробовал, отказывались синхронизировать, если разница времени слишком уж велика, а ваша на ура работает.
Спасибо!
Sergey_K (12.04.2016 в 14:30):
Спасибо, искал что-то подобное(SyncTime).
ManHunter (15.03.2016 в 23:59):
Меняется SystemTime, а не LocalTime. Часовые пояса тут вообще остаются не при делах.
ЦитатаThe SetSystemTime function sets the current system time and date. The system time is expressed in Coordinated Universal Time (UTC).

ЦитатаThe system uses UTC internally. Therefore, when you call SetLocalTime, the system uses the current time zone information to perform the conversion, including the daylight saving time setting.

Как-то так.
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 с обычными правами недоступна.
Андрей (15.03.2016 в 06:34):
ЦитатаТак есть же всякие Neutron

Удобная, минимум функций но в последнее время стала определять на час больше реального. От 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):
Спасибо! Забрал в коллекцию ))
Локальными можно, если заранее обнулить
Цитатаlocals
        result   dd ?
        hSock    dd ?
        ntp_buff rb 10
        ; Данные для запроса к серверу
        wsadata WSADATA 0, 0, 0, 0, 0, 0, 0, 0
        saddr sockaddr_in 0, 0, 0, 0
        sizesaddr = $-saddr
        ; Структуры для работы со временем
        ftime FILETIME 0, 0
        ltime SYSTEMTIME 0, 0, 0, 0, 0, 0, 0, 0
endl
Vladimir (11.03.2016 в 16:05):
Если бы еще можно было параметром указывать сервер для синхронизации и получать сообщение о её успешности/неуспешности - было бы идеально. У меня почему-то отвалилась системная синхронизация, так что приходится пользоваться сторонними утилитами: cmdtime 3 отсюда http://www.softshape.com/download/.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.06 сек. / MySQL: 2 (0.0041 сек.) / Память: 4.5 Mb
Наверх