class Metasm::WinOS
Public Class Methods
returns true if the process pid exists and we can open it with QUERY_INFORMATION
# File metasm/os/windows.rb, line 1655 def check_process(pid) if h = WinAPI.openprocess(WinAPI::PROCESS_QUERY_INFORMATION, 0, pid) WinAPI.closehandle(h) true end end
check if the thread is alive and can be read with QUERY_INFO and optionally if it belongs to pid
# File metasm/os/windows.rb, line 1669 def check_tid(tid, pid=nil) if h = WinAPI.openthread(WinAPI::THREAD_QUERY_INFORMATION, 0, tid) WinAPI.closehandle(h) not pid or list_threads(pid).include?(tid) end end
create a debugger for the target pid/path
# File metasm/os/windows.rb, line 1584 def create_debugger(path) WinDebugger.new(path) end
creates a new thread in the target process, with the specified start address
# File metasm/os/windows.rb, line 1633 def createthread(target, startaddr) WinAPI.createremotethread(target.handle, 0, 0, startaddr, 0, 0, 0) end
# File metasm/os/windows.rb, line 1614 def fixup_shellcode_relocs(shellcode, target, remote_mem) ext = shellcode.reloc_externals binding = {} while e = ext.pop next if binding[e] next if not lib = WindowsExports::EXPORT[e] # XXX could scan all exports... LoadLibrary ftw next if not m = target.modules.find { |m_| m_.path.downcase.include? lib.downcase } lib = LoadedPE.load(remote_mem[m.addr, 0x1000_0000]) lib.decode_header lib.decode_exports lib.export.exports.each { |e_| next if not e_.name or not e_.target binding[e_.name] = m.addr + lib.label_rva(e_.target) } shellcode.fixup! binding end end
try to enable debug privilege in current process
# File metasm/os/windows.rb, line 1494 def get_debug_privilege # TODO use real structs / new_func_c htok = [0].pack('L') return if not WinAPI.openprocesstoken(WinAPI.getcurrentprocess(), WinAPI::TOKEN_ADJUST_PRIVILEGES | WinAPI::TOKEN_QUERY, htok) luid = [0, 0].pack('LL') return if not WinAPI.lookupprivilegevaluea(nil, WinAPI::SE_DEBUG_NAME, luid) # priv.PrivilegeCount = 1; # priv.Privileges[0].Luid = luid; # priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; priv = luid.unpack('LL').unshift(1).push(WinAPI::SE_PRIVILEGE_ENABLED).pack('LLLL') return if not WinAPI.adjusttokenprivileges(htok.unpack('L').first, 0, priv, 0, nil, nil) true end
calls ::inject_shellcode and createthread
# File metasm/os/windows.rb, line 1638 def inject_run_shellcode(target, shellcode) raise "failed to inject shellcode" if not addr = inject_shellcode(target, shellcode) createthread(target, addr) end
Injects a shellcode into the memory space of targetproc target is a WinOS::Process shellcode may be a String (raw shellcode) or an EncodedData With an EncodedData, unresolved relocations are solved using exports of modules from the target address space ; also the shellcode need not be position-independant.
# File metasm/os/windows.rb, line 1594 def inject_shellcode(target, shellcode) raise 'cannot open target memory' if not remote_mem = target.memory return if not injectaddr = WinAPI.virtualallocex(target.handle, 0, shellcode.length, WinAPI::MEM_COMMIT | WinAPI::MEM_RESERVE, WinAPI::PAGE_EXECUTE_READWRITE) puts 'remote buffer at %x' % injectaddr if $VERBOSE if shellcode.kind_of? EncodedData fixup_shellcode_relocs(shellcode, target, remote_mem) shellcode.fixup! shellcode.binding(injectaddr) r = shellcode.reloc.values.map { |r_| r_.target } raise "unresolved shellcode relocs #{r.join(', ')}" if not r.empty? shellcode = shellcode.data end # inject the shellcode remote_mem[injectaddr, shellcode.length] = shellcode injectaddr end
returns the heaps of the process, from a toolhelp snapshot SNAPHEAPLIST this is a hash heap_addr => { :flags => integer (heap flags)
:shared => bool (from flags) :default => bool (from flags) }
# File metasm/os/windows.rb, line 1565 def list_heaps(pid) h = WinAPI.createtoolhelp32snapshot(WinAPI::TH32CS_SNAPHEAPLIST, pid) return [] if h == WinAPI::INVALID_HANDLE_VALUE ret = {} he = WinAPI.alloc_c_struct('HEAPLIST32', :dwsize => :size) return [] if not WinAPI.heap32listfirst(h, he) loop do hash = ret[he.th32heapid] = { :flags => he.dwflags } hash[:default] = true if hash[:flags] & WinAPI::HF32_DEFAULT == WinAPI::HF32_DEFAULT hash[:shared] = true if hash[:flags] & WinAPI::HF32_SHARED == WinAPI::HF32_SHARED # TODO there are lots of other flags in there ! like 0x1000 / 0x8000 break if WinAPI.heap32listnext(h, he) == 0 end WinAPI.closehandle(h) ret end
retrieve the list of Modules for a process with addr/size/path filled
# File metasm/os/windows.rb, line 1528 def list_modules(pid) h = WinAPI.createtoolhelp32snapshot(WinAPI::TH32CS_SNAPMODULE, pid) return [] if h == WinAPI::INVALID_HANDLE_VALUE list = [] me = WinAPI.alloc_c_struct('MODULEENTRY32', :dwsize => :size) return [] if not WinAPI.module32first(h, me) loop do m = Process::Module.new m.addr = me.modbaseaddr m.size = me.modbasesize m.path = me.szexepath.to_strz list << m break if WinAPI.module32next(h, me) == 0 end WinAPI.closehandle(h) list end
returns an array of Processes with pid/ppid/path filled
# File metasm/os/windows.rb, line 1511 def list_processes h = WinAPI.createtoolhelp32snapshot(WinAPI::TH32CS_SNAPPROCESS, 0) list = [] pe = WinAPI.alloc_c_struct('PROCESSENTRY32', :dwsize => :size) return if not WinAPI.process32first(h, pe) loop do p = Process.new(pe.th32processid) p.ppid = pe.th32parentprocessid p.path = pe.szexefile.to_strz list << p if p.pid != 0 break if WinAPI.process32next(h, pe) == 0 end WinAPI.closehandle(h) list end
returns the list of thread ids of the system, optionally filtering by pid
# File metasm/os/windows.rb, line 1547 def list_threads(pid=nil) h = WinAPI.createtoolhelp32snapshot(WinAPI::TH32CS_SNAPTHREAD, 0) list = [] te = WinAPI.alloc_c_struct('THREADENTRY32', :dwsize => :size) return [] if not WinAPI.thread32first(h, te) loop do list << te.th32threadid if not pid or te.th32ownerprocessid == pid break if WinAPI.thread32next(h, te) == 0 end WinAPI.closehandle(h) list end
returns the Process associated to pid if it is alive
# File metasm/os/windows.rb, line 1650 def open_process(pid) Process.new(pid) if check_process(pid) end
returns a Process associated to the process handle
# File metasm/os/windows.rb, line 1644 def open_process_handle(h) pid = WinAPI.getprocessid(h) rescue 0 Process.new(pid, h) end
returns the Thread associated to a tid if it is alive
# File metasm/os/windows.rb, line 1663 def open_thread(tid) Thread.new(tid) if check_tid(tid) end
returns the [major, minor] version of the windows os
# File metasm/os/windows.rb, line 1677 def version v = WinAPI.getversion [v & 0xff, (v>>8) & 0xff] end