1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-22 07:29:01 +00:00

Document the packet NSE module.

This commit is contained in:
david
2008-10-15 23:09:51 +00:00
parent 12e34eb5b0
commit a31f0890d5

View File

@@ -8,16 +8,18 @@ require "bit"
---------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------
-- extract number from binary string --- Get an 8-bit integer at a 0-based byte offset in a binary string.
function u8(b, i) function u8(b, i)
return string.byte(b, i+1) return string.byte(b, i+1)
end end
--- Get a 16-bit integer at a 0-based byte offset in a binary string.
function u16(b, i) function u16(b, i)
local b1,b2 local b1,b2
b1, b2 = string.byte(b, i+1), string.byte(b, i+2) b1, b2 = string.byte(b, i+1), string.byte(b, i+2)
-- 2^8 2^0 -- 2^8 2^0
return b1*256 + b2 return b1*256 + b2
end end
--- Get a 32-bit integer at a 0-based byte offset in a binary string.
function u32(b,i) function u32(b,i)
local b1,b2,b3,b4 local b1,b2,b3,b4
b1, b2 = string.byte(b, i+1), string.byte(b, i+2) b1, b2 = string.byte(b, i+1), string.byte(b, i+2)
@@ -26,15 +28,20 @@ function u32(b,i)
return b1*16777216 + b2*65536 + b3*256 + b4 return b1*16777216 + b2*65536 + b3*256 + b4
end end
-- insert number to binary string --- Set an 8-bit integer at a 0-based byte offset in a binary string
-- (big-endian).
function set_u8(b, i, num) function set_u8(b, i, num)
local s = string.char(bit.band(num, 0xff)) local s = string.char(bit.band(num, 0xff))
return b:sub(0+1, i+1-1) .. s .. b:sub(i+1+1) return b:sub(0+1, i+1-1) .. s .. b:sub(i+1+1)
end end
--- Set a 16-bit integer at a 0-based byte offset in a binary string
-- (big-endian).
function set_u16(b, i, num) function set_u16(b, i, num)
local s = string.char(bit.band(bit.rshift(num, 8), 0xff)) .. string.char(bit.band(num, 0xff)) local s = string.char(bit.band(bit.rshift(num, 8), 0xff)) .. string.char(bit.band(num, 0xff))
return b:sub(0+1, i+1-1) .. s .. b:sub(i+1+2) return b:sub(0+1, i+1-1) .. s .. b:sub(i+1+2)
end end
--- Set a 32-bit integer at a 0-based byte offset in a binary string
-- (big-endian).
function set_u32(b,i, num) function set_u32(b,i, num)
local s = string.char(bit.band(bit.rshift(num,24), 0xff)) .. local s = string.char(bit.band(bit.rshift(num,24), 0xff)) ..
string.char(bit.band(bit.rshift(num,16), 0xff)) .. string.char(bit.band(bit.rshift(num,16), 0xff)) ..
@@ -45,7 +52,7 @@ end
-- Checksum -- Checksum
---- Standard BSD internet checksum routine check nmap/tcpip.cc --- Calculate a standard Internet checksum.
function in_cksum(b) function in_cksum(b)
local sum = 0 local sum = 0
local c local c
@@ -92,14 +99,16 @@ IPPROTO_UDPLITE = 136 -- UDP-Lite (RFC 3828)
---------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------
-- Packet is a class -- Packet is a class
Packet = {} Packet = {}
-- Constructor
-- packet - binary string with packet data --- Create a new Packet object.
-- packet_len - packet length, it could be more than string.len(packet) -- @param packet binary string with packet data.
-- force_continue - whether error in parsing headers should be fatal or not. -- @param packet_len packet length, it could be more than string.len(packet).
-- especially usefull at parsing icmp packets, where on small icmp payload -- @param force_continue whether error in parsing headers should be
-- could be tcp header. The problem is that parsing this payload normally -- fatal or not. Especially useful at parsing ICMP packets, where a
-- would fail (broken packet, because tcp header is too small) -- small ICMP payload could be a TCP header. The problem is that parsing
-- The basic question is if too short tcp header should be treated as fatal error. -- this payload normally would fail (broken packet, because TCP header
-- is too small) The basic question is if too short TCP header should be
-- treated as fatal error.
function Packet:new(packet, packet_len, force_continue) function Packet:new(packet, packet_len, force_continue)
local o = setmetatable({}, {__index = Packet}) local o = setmetatable({}, {__index = Packet})
o.buf = packet o.buf = packet
@@ -122,7 +131,8 @@ end
-- Helpers -- Helpers
-- from ip notation as string (like 1.2.3.4) to raw_string(4 bytes long) --- Convert a dotted-quad IP address string (like 1.2.3.4) to a raw
-- string four bytes long.
function iptobin(str) function iptobin(str)
local ret = "" local ret = ""
for c in string.gmatch(str, "[0-9]+") do for c in string.gmatch(str, "[0-9]+") do
@@ -130,41 +140,50 @@ function iptobin(str)
end end
return ret return ret
end end
-- from raw_ip (four bytes string) to dot-notation (like 1.2.3.4) --- Convert a four-byte raw string to a dotted-quad IP address string.
function toip(raw_ip_addr) function toip(raw_ip_addr)
if not raw_ip_addr then if not raw_ip_addr then
return "?.?.?.?" return "?.?.?.?"
end end
return string.format("%i.%i.%i.%i", string.byte(raw_ip_addr,1,4)) return string.format("%i.%i.%i.%i", string.byte(raw_ip_addr,1,4))
end end
-- get unsigned byte --- Get an 8-bit integer at a 0-based byte offset in the packet.
function Packet:u8(index) function Packet:u8(index)
return u8(self.buf, index) return u8(self.buf, index)
end end
--- Get a 16-bit integer at a 0-based byte offset in the packet.
function Packet:u16(index) function Packet:u16(index)
return u16(self.buf, index) return u16(self.buf, index)
end end
--- Get a 32-bit integer at a 0-based byte offset in the packet.
function Packet:u32(index) function Packet:u32(index)
return u32(self.buf, index) return u32(self.buf, index)
end end
--- Return the packet contents as a byte string.
function Packet:raw(index, length) function Packet:raw(index, length)
return string.char(string.byte(self.buf, index+1, index+1+length-1)) return string.char(string.byte(self.buf, index+1, index+1+length-1))
end end
--- Set an 8-bit integer at a 0-based byte offset in the packet.
-- (big-endian).
function Packet:set_u8(index, num) function Packet:set_u8(index, num)
self.buf = set_u8(self.buf, index, num) self.buf = set_u8(self.buf, index, num)
return self.buf return self.buf
end end
--- Set a 16-bit integer at a 0-based byte offset in the packet.
-- (big-endian).
function Packet:set_u16(index, num) function Packet:set_u16(index, num)
self.buf = set_u16(self.buf, index, num) self.buf = set_u16(self.buf, index, num)
return self.buf return self.buf
end end
--- Set a 32-bit integer at a 0-based byte offset in the packet.
-- (big-endian).
function Packet:set_u32(index, num) function Packet:set_u32(index, num)
self.buf = set_u32(self.buf, index, num) self.buf = set_u32(self.buf, index, num)
return self.buf return self.buf
end end
-- PARSE IP PACKET HEADER --- Parse an IP packet header.
function Packet:ip_parse(force_continue) function Packet:ip_parse(force_continue)
self.ip_offset = 0 self.ip_offset = 0
if string.len(self.buf) < 20 then -- too short if string.len(self.buf) < 20 then -- too short
@@ -196,43 +215,44 @@ function Packet:ip_parse(force_continue)
self.ip_data_offset = self.ip_offset + self.ip_hl*4 self.ip_data_offset = self.ip_offset + self.ip_hl*4
return true return true
end end
-- set header length field --- Set the header length field.
function Packet:ip_set_hl(len) function Packet:ip_set_hl(len)
self:set_u8(self.ip_offset + 0, bit.bor(bit.lshift(self.ip_v, 4), bit.band(len, 0x0F))) self:set_u8(self.ip_offset + 0, bit.bor(bit.lshift(self.ip_v, 4), bit.band(len, 0x0F)))
self.ip_v = bit.rshift(bit.band(self:u8(self.ip_offset + 0), 0xF0), 4) self.ip_v = bit.rshift(bit.band(self:u8(self.ip_offset + 0), 0xF0), 4)
self.ip_hl = bit.band(self:u8(self.ip_offset + 0), 0x0F) -- header_length or data_offset self.ip_hl = bit.band(self:u8(self.ip_offset + 0), 0x0F) -- header_length or data_offset
end end
-- set packet length field --- Set the packet length field.
function Packet:ip_set_len(len) function Packet:ip_set_len(len)
self:set_u16(self.ip_offset + 2, len) self:set_u16(self.ip_offset + 2, len)
end end
-- set ttl --- Set the TTL.
function Packet:ip_set_ttl(ttl) function Packet:ip_set_ttl(ttl)
self:set_u8(self.ip_offset + 8, ttl) self:set_u8(self.ip_offset + 8, ttl)
end end
-- set checksum --- Set the checksum.
function Packet:ip_set_checksum(checksum) function Packet:ip_set_checksum(checksum)
self:set_u16(self.ip_offset + 10, checksum) self:set_u16(self.ip_offset + 10, checksum)
end end
-- count checksum for packet and save it --- Count checksum for packet and save it.
function Packet:ip_count_checksum() function Packet:ip_count_checksum()
self:ip_set_checksum(0) self:ip_set_checksum(0)
local csum = in_cksum( self.buf:sub(0, self.ip_offset + self.ip_hl*4) ) local csum = in_cksum( self.buf:sub(0, self.ip_offset + self.ip_hl*4) )
self:ip_set_checksum(csum) self:ip_set_checksum(csum)
end end
-- set source ip --- Set the source IP address.
function Packet:ip_set_bin_src(binip) function Packet:ip_set_bin_src(binip)
nrip = u32(binip, 0) nrip = u32(binip, 0)
self:set_u32(self.ip_offset + 12, nrip) self:set_u32(self.ip_offset + 12, nrip)
self.ip_bin_src = self:raw(self.ip_offset + 12,4) -- raw 4-bytes string self.ip_bin_src = self:raw(self.ip_offset + 12,4) -- raw 4-bytes string
end end
-- set destination ip --- Set the destination IP address.
function Packet:ip_set_bin_dst(binip) function Packet:ip_set_bin_dst(binip)
nrip = u32(binip, 0) nrip = u32(binip, 0)
self:set_u32(self.ip_offset + 16, nrip) self:set_u32(self.ip_offset + 16, nrip)
self.ip_bin_dst = self:raw(self.ip_offset + 16,4) self.ip_bin_dst = self:raw(self.ip_offset + 16,4)
end end
-- set ip options field (and move the data, count new length etc) --- Set the IP options field (and move the data, count new length,
-- etc.).
function Packet:ip_set_options(ipoptions) function Packet:ip_set_options(ipoptions)
-- packet = <ip header> + ipoptions + <payload> -- packet = <ip header> + ipoptions + <payload>
local buf = self.buf:sub(0+1,self.ip_offset + 20) .. ipoptions .. self.buf:sub(self.ip_data_offset+1) local buf = self.buf:sub(0+1,self.ip_offset + 20) .. ipoptions .. self.buf:sub(self.ip_data_offset+1)
@@ -251,7 +271,7 @@ function Packet:ip_set_options(ipoptions)
end end
end end
-- return short information about ip header --- Return a short string representation of the IP header.
function Packet:ip_tostring() function Packet:ip_tostring()
return string.format( return string.format(
"IP %s -> %s", "IP %s -> %s",
@@ -259,7 +279,7 @@ function Packet:ip_tostring()
self.ip_dst) self.ip_dst)
end end
-- parse ip/tcp options to dict structure --- Parse IP/TCP options into a table.
function Packet:parse_options(offset, length) function Packet:parse_options(offset, length)
local options = {} local options = {}
local op = 1 local op = 1
@@ -287,7 +307,7 @@ function Packet:parse_options(offset, length)
return options return options
end end
-- print short information about current packet --- Return a short string representation of the packet.
function Packet:tostring() function Packet:tostring()
if self.tcp then if self.tcp then
return self:tcp_tostring() return self:tcp_tostring()
@@ -300,7 +320,7 @@ function Packet:tostring()
end end
---------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------
-- PARSE ICMP PACKET HEADER --- Parse an ICMP packet header.
function Packet:icmp_parse(force_continue) function Packet:icmp_parse(force_continue)
self.icmp_offset = self.ip_data_offset self.icmp_offset = self.ip_data_offset
if string.len(self.buf) < self.icmp_offset + 8 then -- let's say 8 bytes minimum if string.len(self.buf) < self.icmp_offset + 8 then -- let's say 8 bytes minimum
@@ -322,13 +342,13 @@ function Packet:icmp_parse(force_continue)
end end
return true return true
end end
-- return short information about icmp header --- Return a short string representation of the ICMP header.
function Packet:icmp_tostring() function Packet:icmp_tostring()
return self:ip_tostring() .. " ICMP(" .. self.icmp_payload:tostring() .. ")" return self:ip_tostring() .. " ICMP(" .. self.icmp_payload:tostring() .. ")"
end end
---------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------
-- PARSE TCP HEADER FROM PACKET -- Parse a TCP packet header.
function Packet:tcp_parse(force_continue) function Packet:tcp_parse(force_continue)
self.tcp = true self.tcp = true
self.tcp_offset = self.ip_data_offset self.tcp_offset = self.ip_data_offset
@@ -368,7 +388,7 @@ function Packet:tcp_parse(force_continue)
return true return true
end end
-- return short information about tcp packet --- Return a short string representation of the TCP packet.
function Packet:tcp_tostring() function Packet:tcp_tostring()
return string.format( return string.format(
"TCP %s:%i -> %s:%i", "TCP %s:%i -> %s:%i",
@@ -377,7 +397,7 @@ function Packet:tcp_tostring()
) )
end end
-- parse options for tcp header --- Parse options for TCP header.
function Packet:tcp_parse_options() function Packet:tcp_parse_options()
local eoo = false local eoo = false
for _,opt in ipairs(self.tcp_options) do for _,opt in ipairs(self.tcp_options) do
@@ -399,29 +419,31 @@ function Packet:tcp_parse_options()
end end
end end
--- Set the TCP source port.
function Packet:tcp_set_sport(port) function Packet:tcp_set_sport(port)
self:set_u16(self.tcp_offset + 0, port) self:set_u16(self.tcp_offset + 0, port)
end end
--- Set the TCP destination port.
function Packet:tcp_set_dport(port) function Packet:tcp_set_dport(port)
self:set_u16(self.tcp_offset + 2, port) self:set_u16(self.tcp_offset + 2, port)
end end
-- set tcp sequence field --- Set the TCP sequence field.
function Packet:tcp_set_seq(new_seq) function Packet:tcp_set_seq(new_seq)
self:set_u32(self.tcp_offset + 4, new_seq) self:set_u32(self.tcp_offset + 4, new_seq)
end end
-- set tcp flags field (like syn, ack, rst) --- Set the TCP flags field (like SYN, ACK, RST).
function Packet:tcp_set_flags(new_flags) function Packet:tcp_set_flags(new_flags)
self:set_u8(self.tcp_offset + 13, new_flags) self:set_u8(self.tcp_offset + 13, new_flags)
end end
-- set urgent pointer field --- Set the urgent pointer field.
function Packet:tcp_set_urp(urg_ptr) function Packet:tcp_set_urp(urg_ptr)
self:set_u16(self.tcp_offset + 18, urg_ptr) self:set_u16(self.tcp_offset + 18, urg_ptr)
end end
-- set tcp checksum field --- Set the TCP checksum field.
function Packet:tcp_set_checksum(checksum) function Packet:tcp_set_checksum(checksum)
self:set_u16(self.tcp_offset + 16, checksum) self:set_u16(self.tcp_offset + 16, checksum)
end end
-- count and save tcp checksum field --- Count and save the TCP checksum field.
function Packet:tcp_count_checksum() function Packet:tcp_count_checksum()
self:tcp_set_checksum(0) self:tcp_set_checksum(0)
local proto = self.ip_p local proto = self.ip_p
@@ -436,7 +458,7 @@ function Packet:tcp_count_checksum()
self:tcp_set_checksum(in_cksum(b)) self:tcp_set_checksum(in_cksum(b))
end end
-- small database, mtu to link type string. Stolen from p0f. --- Map an MTU to a link type string. Stolen from p0f.
function Packet:tcp_lookup_link() function Packet:tcp_lookup_link()
local mtu_def = { local mtu_def = {
{["mtu"]=256, ["txt"]= "radio modem"}, {["mtu"]=256, ["txt"]= "radio modem"},
@@ -498,7 +520,8 @@ end
---------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------
-- UTILS -- UTILS
-- get binary string as hex string
--- Convert a binary string to a hex string.
function bintohex(str) function bintohex(str)
local b = "" local b = ""
if not str then -- nil if not str then -- nil
@@ -512,12 +535,13 @@ end
-- Parse specifically printed hex string as binary --- Convert a hex string to a binary string.
-- \n\n
-- Only bytes [a-f0-9A-F] from input are interpreted. The rest is ignored. -- Only bytes [a-f0-9A-F] from input are interpreted. The rest is ignored.
-- Number of interpreted bytes _must_ be even. *The input is interpreted in pairs*. -- Number of interpreted bytes _must_ be even. *The input is interpreted in pairs*.\n
-- hextobin("20 20 20") -> " " -- hextobin("20 20 20") -> " "\n
-- hextobin("414243") -> "ABC" -- hextobin("414243") -> "ABC"\n
-- hextobin("\\41\\42\\43") -> "ABC" -- hextobin("\\41\\42\\43") -> "ABC"\n
-- hextobin(" 41 42 43 ")-> "ABC" -- hextobin(" 41 42 43 ")-> "ABC"
function hextobin(str) function hextobin(str)
local ret = "" local ret = ""