Implementing time server NTP synchronization function from Ruby also talk about "reverse engineering"

Source: Internet
Author: User
Tags server port

The cat used to write ASM and C often do not forget the "reverse", and then write the drive when the VM and the like to build a "two-machine" debugging Environment for debugging, but also for some small software crack cd-key the joy of God Horse. Since the use of the bird so-called advanced Dynamic language Ruby, this black inverse mentality seems to gradually weaken ... But it's hard to get the chance to catch the itch.

Ruby+linux's open source way has not been to the bin code as well as dis asm, but sometimes want to figure out some features or to use a little tricks, the following to solve a small problem to show you this stuff

NTP is a clock synchronization protocol used on servers and routers, Ruby also has a lot of related gems, such as NET-NTP, after the gem install NET-NTP can use the following code to get the NTP server Standard Time:

#!/usr/bin/rubyrequire ' NET/NTP ' def get_ntp_time (srv_addr) puts Net::ntp.get (SRV_ADDR). Timeendget_ntp_time (ARGV[0) )
The results of the operation are as follows:

[Email protected]:~/src/ruby_src$./dzh.rb pool.ntp.org2014-12-04 14:06:20 +0800[email protected]:~/src/ruby_src$./ DZH.RB time.nist.gov2014-12-04 14:07:00 +0800

I simply analyzed the next NTP protocol, and found that if I implemented it, I could send some messages to the NTP server Port 123 (NTP service port) by TCP or UDP, and then receive the return. I began to think that the message was arbitrary, because I used to remember to use Telnet IP 123 also returned the time string (now I think it may be mistaken!) So there was my first attempt:

#!/usr/bin/rubyrequire ' NET/NTP ' def get_ntp_time_udp (srv_addr,msg) s = UDPSocket.news.connect srv_addr,123s.send msg, 0response,address = S.recvfrom 1024puts [response,address]      s.closeenddef get_ntp_time (srv_addr) puts Net::NTP.get (SRV_ADDR). timeendif Argv.count = = 1get_ntp_time (argv[0]) elseget_ntp_time_udp (argv[0],argv[1]) end

To run with 2 parameters, the second parameter is the message to be sent:./ut.rb time.nist.gov Hi, but after running for a long time, it seems that the NTP server did not return AH! Analysis, the NTP message may not be random, to have a certain format, but what is the format it? Baidu a bit, the format is more complex, converted into code more trouble Ah! Wouldn't it be better to take a look at NET-NTP's implementation code? Although the NET-NTP is open source, but where is the source file? How to find it? Instead of using Ruby debug mode First debug, Ruby at run time with the-R debug (or-rdebug) can implement debugging functions, and then use the n instruction to achieve a single step, with the S command to step into the tracking:

[Email protected]:~/src/ruby_src$ ruby-rdebug ut.rb time.nist.govDebug.rbEmacs Support available./usr/lib/ruby/2.1.0 /rubygems/core_ext/kernel_require.rb:57:        rubygems_activation_monitor.enter (rdb:1) n/usr/lib/ruby/2.1.0/ rubygems/core_ext/kernel_require.rb:143:    rubygems_activation_monitor.exit (rdb:1) ndzh.rb:2:require ' NET/NTP ' (rdb:1) Ndzh.rb:4:def get_ntp_time_udp (srv_addr,msg) (rdb:1) ndzh.rb:14:def get_ntp_time (srv_addr) (rdb:1) ndzh.rb:18 : if Argv.count = = 1 (rdb:1) ndzh.rb:19:get_ntp_time (Argv[0]) (rdb:1) sdzh.rb:15:puts net::ntp.get (SRV_ADDR). Time (rdb:1 ) s/var/lib/gems/2.1.0/gems/net-ntp-2.1.2/lib/net/ntp/ntp.rb:67:      sock = udpsocket.new

At least you can see where the source code for Net::ntp.get is. Open ntp.rb, not complicated, altogether more than 200 lines of code. Many of the comments are #:nodoc:, is not this implementation of the author also did not find the basis of the document? NTP.RB all source code is as follows:

Require ' socket ' require ' timeout ' module Net #:nodoc:module NTP timeout = #:nodoc:ntp_adj = 2208988800 #:nodoc:ntp_fields = [: byte1,: Stratum,:p oll,:p recision,:d elay,:d elay_fb,:d ISP,:d ISP_FB,: IDE                   NT,: Ref_time,: REF_TIME_FB,: Org_time,: ORG_TIME_FB,: Recv_time,: RECV_TIME_FB,: Trans_time, : TRANS_TIME_FB] MODE = {0 = ' reserved ', 1 = ' symmetric active ', 2 = ' symmetric pas Sive ', 3 = ' client ', 4 = ' server ', 5 = ' broadcast ', 6 = ' reserved for NTP control mess Age ', 7 = ' reserved for private use '} stratum = {0 = ' unspecified or unavailable ', 1 =      ' primary reference (e.g., radio clock) '} 2.upto (All) do |i|      Stratum[i] = ' secondary reference (via NTP or SNTP) ' End 16.upto (255) do |i| Stratum[i] = ' reserved ' End reference_clock_identifier = {' Locl ' = = ' uncalibrated local CLOCKUsed as a primary reference for a subnet without external means of synchronization ', ' PPS ' = ' atomic clock or OT       She pulse-per-second source individually calibrated to national standards ', ' ACTS ' = ' NIST dialup modem Service ', ' USNO ' + ' USNO Modem Service ', ' PTB ' [Germany] Modem Service ', ' TDF ' = ' Allouis (Fran       CE) Radio 164 khz ', ' DCF ' = ' Mainflingen ' (Germany) Radio 77.5 khz ', ' MSF ' = ' Rugby (UK) Radio ', ' WWV ' + ' Ft. Collins (US) Radio 2.5, 5, ten, at + MHz ', ' WWVB ' = ' Boulder (US) Radio kHz ', ' W      WVH ' + ' Kaui Hawaii (US) Radio 2.5, 5, ten MHz ', ' CHU ' = ' Ottawa (Canada) Radio 3330, 7335, 14670 kHz ', ' Lorc ' = ' loran-c radionavigation system ', ' omeg ' + ' OMEGA radionavigation system ', ' GPS ' = ' Gl '  Obal positioning Service ', ' GOES ' = ' Geostationary Orbit Environment satellite '} Leap_indicator = {0    = ' No warning ',  1 = ' Last minute have seconds ', 2 = ' last minute have seconds) ', 3 = ' Alarm condition (clock n ot synchronized) ' # # # # sends an NTP datagram to the specified NTP server and returns # a hash based upon RF    C1305 and RFC2030.      def self.get (host= "pool.ntp.org", port= "NTP", timeout=timeout) sock = Udpsocket.new Sock.connect (host, Port)  Client_localtime = Time.now.to_f Client_adj_localtime = client_localtime + Ntp_adj client_frac_localtime = Frac2bin (client_adj_localtime) ntp_msg = ([' 00011011 ']+array.new (0) +[client_localtime, client_frac_localtime.t O_S]). Pack ("B8 C3 N10 B32") sock.print ntp_msg Sock.flush Read, write, error = Io.select [sock], nil, nil, Timeout if read[0] client_time_receive = Time.now.to_f data, _ = Sock.recvfrom (960) response.new (data, client_time_receive) Else # for backwards compatibility we throw a Timeout error, even # though ThE timeout is being controlled by select () Raise Timeout::error end End Def self.frac2bin (Frac) #:nodoc: Bin = ' while Bin.length < + bin + = (Frac * 2). to_i.to_s Frac = (Frac * 2)-(FRAC * 2)            ). To_i End Bin End Private_class_method:frac2bin class Response attr_reader:client_time_receive Def initialize (Raw_data, client_time_receive) @raw_data = Raw_data @client_time_recei ve = client_time_receive @packet_data_by_field = Nil End def leap_indicator @leap_indicator | | = (Packet_data_by_field[:byte1].bytes.first & 0xC0) >> 6 end def Leap_indicator_text @leap_indi Cator_text | | = Leap_indicator[leap_indicator] End def version_number @version_number | | = (Packet_data_by_field[:byte1].bytes.first & 0x38) >> 3 end def mode @mode | | = (Packet_data_by_field[:byte1].bytes.first & 0x07) End def mode_text @mode_text | | = Mode[mode] End def stratum @stratum | | = Packet_data_by_field[:stratum] End def stratum_text @stratum_text | | = Stratum[stratum] End def poll_interval @poll_interval | | = packet_data_by_field[:p Oll] End def Precision @precision | | = packet_data_by_field[:p recision]-255 end def Root_delay @root_delay | | = Bin2frac (packet_data_by_field[:d ELAY_FB]) end def root_dispersion @root_dispersion | | = packet_data_by_field[:d ISP] End def reference_clock_identifier @reference_clock_identifier | | = Unpack_ip (Packet_data_by_field[:stratum], packet_data_by_field[:ident]) end def Reference_clock_identifier_tex T @reference_clock_identifier_text | |  = Reference_clock_identifier[reference_clock_identifier] End def Reference_timestamp @reference_timestamp || = ((Packet_data_by_field[:ref_time] + Bin2frac (packet_data_by_fIELD[:REF_TIME_FB])-Ntp_adj) End def originate_timestamp @originate_timestamp | |        = (Packet_data_by_field[:org_time] + Bin2frac (PACKET_DATA_BY_FIELD[:ORG_TIME_FB)) End Def Receive_timestamp @receive_timestamp | | = ((Packet_data_by_field[:recv_time] + Bin2frac (PACKET_DATA_BY_FIELD[:RECV_TIME_FB))-Ntp_adj) End Def Transmi T_timestamp @transmit_timestamp | |        = ((Packet_data_by_field[:trans_time] + Bin2frac (PACKET_DATA_BY_FIELD[:TRANS_TIME_FB))-NTP_ADJ) End def time @time | | = time.at (Receive_timestamp) end # as described in http://tools.ietf.org/html/rfc958 def offset @off Set | | = (Receive_timestamp-originate_timestamp + transmit_timestamp-client_time_receive)/2.0 End protected de F Packet_data_by_field #:nodoc:if [Email protected]_data_by_field @packetdata = @raw_data. Unpack ("a C3 n B16 N B16 H8 n B32 n B32 n B32 n B32 ") @packet_data_by_field= {} Ntp_fields.each do |field| @packet_data_by_field [Field] = @packetdata. Shift End @packet_data_by_field End def BI          N2frac (bin) #:nodoc:frac = 0 Bin.reverse.split (""). Each do |b| Frac = (frac + b.to_i)/2.0 End Frac End def unpack_ip (stratum, tmp_ip) #:nodoc:if stra Tum < 2 [Tmp_ip].pack ("H8"). Unpack ("A4"). Bytes.first else ipbytes = [Tmp_ip].pack ("H8"). Unpack ("C4") sprintf ("%d.%d.%d.%d", Ipbytes[0], ipbytes[1], ipbytes[2], ipbytes[3]) end End end EndEnd

You can see that the NET-NTP get method uses TCP, and its implementation of the message format is normal also how to guess Oh (), which wrote a number of methods to implement the net byte order to the native byte order of mutual conversion. In order to make it easy for us to rewrite it to UDP, we do not want to what kind of, directly write the global method, rewrite the following method:

#!/usr/bin/rubyrequire ' net/ntp ' My_ntp_adj = 2208988800 #:nodoc:my_ntp_fields = [: byte1,: Stratum,:p oll,:p recision,:d Elay,:d elay_fb,:d ISP,:d ISP_FB,: Ident,: Ref_time,: REF_TIME_FB,: org_time,: Org_tim         E_FB,: Recv_time,: RECV_TIME_FB,: Trans_time,: TRANS_TIME_FB]def Bin2frac (bin) #:nodoc:frac = 0  Bin.reverse.split (""). Each {|b|frac = (frac + b.to_i)/2.0} fracenddef Frac2bin (frac) #:nodoc:bin      = ' while Bin.length < + bin + = (Frac * 2). to_i.to_s Frac = (Frac * 2)-(FRAC * 2). to_i End Binenddef Packet_data_by_field (raw_data) #:nodoc:packetdata = Raw_data.unpack ("A C3 n B16 n B16 H            8 n B32 n B32 n B32 n B32 ") Packet_data_by_field = {} My_ntp_fields.each do |field|           Packet_data_by_field[field] = Packetdata.shift end puts "bin:" + "@" *50 puts Packet_data_by_field         Puts "@" *54 Packet_data_by_fieldenddef Receive_timestamp (Raw_data) (Packet_data_by_field (Raw_data) [: Recv_time] + Bin2frac (PAC  Ket_data_by_field (Raw_data) [: RECV_TIME_FB])-My_ntp_adjenddef get_ntp_time_udp (srv_addr) s = UDPSocket.new S.connect srv_addr,123 client_localtime = Time.now.to_f client_adj_localtime = client_localtime + net::ntp::ntp_ad J client_frac_localtime = Frac2bin (client_adj_localtime) bin = ([' 00011011 ']+array.new (0) +[client_localtime, CLIENT_FRAC_LOCALTIME.TO_S]). Pack ("B8 C3 N10 B32") s.send bin,0 response,address = S.recvfrom (1024x768) puts "ip:#{address} "Puts" Bin: "+" * "*50 puts response puts" * "*54 puts" GET time is: #{time.at (Receive_timestamp (response))} "Enddef get_ Ntp_time (SRV_ADDR) puts Net::ntp.get (srv_addr). timeendif argv.count = 1 get_ntp_time (argv[0]) Else GET_NTP_TIME_UDP ( Argv[0]) End

Look at the results of the operation:

[email protected]:~/src/ruby_src$/dzh.rb pool.ntp.org 1ip:["Af_inet", 123, "202.112.29.82", "202.112.29.82"] Bin:**************************************************!? _?z?? *???? _st??? '?? ' *?x0?? =?*?x0?? bin:@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@{: Byte1=> "\x1c",: stratum=>2,:p oll=>3,:p recision=>233,:d elay=>0,:d elay_fb=> "0000110100100001",: Disp=>0,:d isp_fb=> "0000101000100000",:ident=> "5fde7ad2",: ref_time=>3626664177,:ref_time_fb=> " 11010000110111000101111101010011 ",: org_time=>1417675511,:org_time_fb=>" 10111000011000001001101000100000 " ,: recv_time=>3626664312,:recv_time_fb=> "00110000100100001001110000111101",: trans_time=>3626664312,: Trans_time_fb=> "00110000100110101101101011011001"}@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ zzfcthotfixz :@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@{:byte1=> "\x1c",: stratum=>2,:p oll=>3,:p recision= >233,:dElay=>0,:d elay_fb=> "0000110100100001",:d isp=>0,:d isp_fb=> "0000101000100000",:ident=> "5fde7ad2", : ref_time=>3626664177,:ref_time_fb=> "11010000110111000101111101010011",: org_time=>1417675511,: Org_ Time_fb=> "10111000011000001001101000100000",: recv_time=>3626664312,:recv_time_fb=> " 00110000100100001001110000111101 ",: trans_time=>3626664312,:trans_time_fb=>" 00110000100110101101101011011001 "}@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ zzfcthotfixz time is:2014-12-04 14:45:12 +0800

Open source has the benefit of open source, otherwise, to analyze this feature, only heavy weapons such as Ida Pro are loaded. Another way is to use the clutch to see NET-NTP sent data format, look after copy! As seen in the "star crossing" after feeling a little bit like God horse: Any problem has a solution, no matter what kind of problem! And there must be more than one solution!

Implementing time server NTP synchronization function from Ruby also talk about "reverse engineering"

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.