Big commit GetProcAddrss 32 and 64 (fixed)

This commit is contained in:
Clement Rouault
2016-01-29 18:05:21 +01:00
parent 5b5db57e17
commit d720170781
9 changed files with 278 additions and 42 deletions
+7 -1
View File
@@ -24,6 +24,10 @@ TODO:
- code generated by generate_python_exec_shellcode_64[32] may be reused
Just need to passe the address of the python string as argument
- GetProcesAddress
- CRASH if API is not found..
- fail when resolving xxxW function
FIXME:
- WMI
- COM initialisation when injected in another process
@@ -31,4 +35,6 @@ FIXME:
- Fix that
- setup.py build seems to raise an error
- winutils.create_process : use WinProcess._from_handle
- winutils.create_process : use WinProcess._from_handle
- Push("[ECX]") in simple_x64 as a "H" rex and i think it should not..
+68 -34
View File
@@ -8,42 +8,47 @@ import windows.utils as utils
from .native_exec import simple_x86 as x86
from .native_exec import simple_x64 as x64
from windows.native_exec.nativeutils import GetProcAddress64
from windows.native_exec.nativeutils import GetProcAddress64, GetProcAddress32
from windows.dbgprint import dbgprint
def load_dll_in_remote_process(target, dll_name):
rpeb = target.peb
if rpeb.Ldr:
# LDR est parcourable, ca va etre deja plus simple..
modules = rpeb.modules
if any(mod.name == dll_name for mod in modules):
# DLL already loaded
dbgprint("DLL already present in DLL", "DLLINJECT")
return True
k32 = [mod for mod in modules if mod.name.lower() == "kernel32.dll"]
if k32:
# We have kernel32 \o/
k32 = k32[0]
try:
load_libraryA = k32.pe.exports["LoadLibraryA"]
except KeyError:
raise ValueError("Kernel32 have no export <LoadLibraryA> (wtf)")
def perform_manual_getproc_loadlib_32(target, dll_name):
dll = "KERNEL32.DLL\x00".encode("utf-16-le")
api = "LoadLibraryA\x00"
dll_to_load = dll_name + "\x00"
addr = target.virtual_alloc(0x1000)
target.write_memory(addr, dll_name + "\x00")
t = target.create_thread(load_libraryA, addr)
t.wait()
windows.winproxy.VirtualFreeEx(target.handle, addr)
dbgprint("DLL Injected via (LoadLibray)", "DLLINJECT")
return True
# Hardcore mode
# We don't have k32 or PEB->Ldr
# Go inject a GetProcAddress(LoadLib) + LoadLib shellcode :D
if target.bitness == 32:
raise NotImplementedError("Manuel GetProcAddress 32bits")
RemoteManualLoadLibray = x86.MultipleInstr()
code = RemoteManualLoadLibray
code += x86.Mov("ECX", x86.mem("[ESP + 4]"))
code += x86.Push(x86.mem("[ECX + 4]"))
code += x86.Push(x86.mem("[ECX]"))
code += x86.Call(":FUNC_GETPROCADDRESS32")
code += x86.Push(x86.mem("[ECX + 8]"))
code += x86.Call("EAX") # LoadLibrary
code += x86.Pop("ECX")
code += x86.Pop("ECX")
code += x86.Ret()
RemoteManualLoadLibray += GetProcAddress32
addr = target.virtual_alloc(0x1000)
addr2 = addr + len(dll)
addr3 = addr2 + len(api)
addr4 = addr3 + len(dll_to_load)
target.write_memory(addr, dll)
target.write_memory(addr2, api)
target.write_memory(addr3, dll_to_load)
target.write_qword(addr4, addr)
target.write_qword(addr4 + 4, addr2)
target.write_qword(addr4 + 0x8, addr3)
t = target.execute(RemoteManualLoadLibray.get_code(), addr4)
t.wait()
return True
def perform_manual_getproc_loadlib_64(target, dll_name):
dll = "KERNEL32.DLL\x00".encode("utf-16-le")
api = "LoadLibraryA\x00"
dll_to_load = dll_name + "\x00"
@@ -80,9 +85,41 @@ def load_dll_in_remote_process(target, dll_name):
t = target.execute(RemoteManualLoadLibray.get_code(), addr4)
t.wait()
dbgprint("DLL Injected via manual GetProc(LoadLibray)", "DLLINJECT")
return True
def load_dll_in_remote_process(target, dll_name):
rpeb = target.peb
if rpeb.Ldr:
# LDR est parcourable, ca va etre deja plus simple..
modules = rpeb.modules
if any(mod.name == dll_name for mod in modules):
# DLL already loaded
dbgprint("DLL already present in target", "DLLINJECT")
return True
k32 = [mod for mod in modules if mod.name.lower() == "kernel32.dll"]
if k32:
# We have kernel32 \o/
k32 = k32[0]
try:
load_libraryA = k32.pe.exports["LoadLibraryA"]
except KeyError:
raise ValueError("Kernel32 have no export <LoadLibraryA> (wtf)")
addr = target.virtual_alloc(0x1000)
target.write_memory(addr, dll_name + "\x00")
t = target.create_thread(load_libraryA, addr)
t.wait()
windows.winproxy.VirtualFreeEx(target.handle, addr)
dbgprint("DLL Injected via LoadLibray", "DLLINJECT")
return True
# Hardcore mode
# We don't have k32 or PEB->Ldr
# Go inject a GetProcAddress(LoadLib) + LoadLib shellcode :D
if target.bitness == 32:
return perform_manual_getproc_loadlib_32(target, dll_name)
return perform_manual_getproc_loadlib_64(target, dll_name)
python_function_32_bits = {}
# 32 to 32 injection
def generate_python_exec_shellcode_32(target, PYCODE_ADDR, PyDll):
@@ -163,10 +200,8 @@ def generate_python_exec_shellcode_64(target, PYCODE_ADDR, PyDll):
Py_Initialize = Py_exports["Py_Initialize"]
PyRun_SimpleString = Py_exports["PyRun_SimpleString"]
Reserve_space_for_call = x64.MultipleInstr([x64.Push('RDI')] * 4)
Clean_space_for_call = x64.MultipleInstr([x64.Pop('RDI')] * 4)
code = x64.MultipleInstr()
# Do stack alignement
code += x64.Push('RCX')
@@ -189,7 +224,6 @@ def generate_python_exec_shellcode_64(target, PYCODE_ADDR, PyDll):
code += x64.Call('RAX')
code += x64.Mov('RCX', 'R15')
code += x64.Mov('R15', 'RAX')
code += x64.Mov('RAX', PyGILState_Release)
code += x64.Call('RAX')
code += x64.Cmp("RDI", 0)
+132 -2
View File
@@ -1,6 +1,7 @@
import windows
import windows.native_exec.simple_x64 as x64
import windows.native_exec.simple_x86 as x86
from windows.generated_def.winstructs import *
@@ -26,7 +27,7 @@ StrlenA64 += x64.Label(":FUNC_STRLENA64")
StrlenA64 += x64.Push("RCX")
StrlenA64 += x64.Push("RDI")
StrlenA64 += x64.Mov("RDI", "RCX")
StrlenA64 += x64.Xor("RAX", "rax")
StrlenA64 += x64.Xor("RAX", "RAX")
StrlenA64 += x64.Xor("RCX", "RCX")
StrlenA64 += x64.Dec("RCX")
StrlenA64 += x64.Repne + x64.ScasB()
@@ -85,7 +86,7 @@ GetProcAddress64 += x64.Add("RCX", "RBX") # ;rcx = export_dir
GetProcAddress64 += x64.Mov("RAX", "RCX") # ;RAX = export_dir
GetProcAddress64 += x64.Push("RAX") # ;Save it for after function search
# ; EBX = BASE | EAX = EXPORT DIR
GetProcAddress64 += x64.Mov("ECX", x64.mem("[RAX + 24] ")) # rax = PEBASE RVA
GetProcAddress64 += x64.Mov("ECX", x64.mem("[RAX + 24] "))
GetProcAddress64 += x64.Mov("R13", "RCX") # ;r13 = NB names
GetProcAddress64 += x64.Mov("EDX", x64.mem("[RAX + 32] ")) # EDX = names array RVA
GetProcAddress64 += x64.Add("RDX", "RBX") # RDX = names array
@@ -98,6 +99,7 @@ GetProcAddress64 += x64.Mov("RCX", "R12")
GetProcAddress64 += x64.Call(":FUNC_STRLENA64") # TODO: mov outside the loop :D
GetProcAddress64 += x64.Mov("RCX", "RAX")
GetProcAddress64 += x64.Mov("RDI", "R12")
GetProcAddress64 += x64.Inc("RCX")
GetProcAddress64 += x64.Rep + x64.CmpsB()
GetProcAddress64 += x64.Mov("EAX", "ECX")
GetProcAddress64 += x64.Pop("RCX")
@@ -136,3 +138,131 @@ GetProcAddress64 += StrlenW64
GetProcAddress64 += StrlenA64
###### 32 bits #######
StrlenW32 = x86.MultipleInstr()
StrlenW32 += x86.Label(":FUNC_STRLENW32")
StrlenW32 += x86.Push("EDI")
StrlenW32 += x86.Mov("EDI", x86.mem("[ESP + 8]"))
StrlenW32 += x86.Push("ECX")
StrlenW32 += x86.Xor("EAX", "EAX")
StrlenW32 += x86.Xor("ECX", "ECX")
StrlenW32 += x86.Dec("ECX")
StrlenW32 += x86.Repne + x86.ScasW()
StrlenW32 += x86.Not("ECX")
StrlenW32 += x86.Dec("ECX")
StrlenW32 += x86.Mov("EAX", "ECX")
StrlenW32 += x86.Pop("ECX")
StrlenW32 += x86.Pop("EDI")
StrlenW32 += x86.Ret()
StrlenA32 = x86.MultipleInstr()
StrlenA32 += x86.Label(":FUNC_STRLENA32")
StrlenA32 += x86.Push("EDI")
StrlenA32 += x86.Mov("EDI", x86.mem("[ESP + 8]"))
StrlenA32 += x86.Push("ECX")
StrlenA32 += x86.Xor("EAX", "EAX")
StrlenA32 += x86.Xor("ECX", "ECX")
StrlenA32 += x86.Dec("ECX")
StrlenA32 += x86.Repne + x86.ScasB()
StrlenA32 += x86.Not("ECX")
StrlenA32 += x86.Dec("ECX")
StrlenA32 += x86.Mov("EAX", "ECX")
StrlenA32 += x86.Pop("ECX")
StrlenA32 += x86.Pop("EDI")
StrlenA32 += x86.Ret()
GetProcAddress32 = x86.MultipleInstr()
GetProcAddress32 += x86.Label(":FUNC_GETPROCADDRESS32")
GetProcAddress32 += x86.Push("EBX")
GetProcAddress32 += x86.Push("ECX")
GetProcAddress32 += x86.Push("EDI")
GetProcAddress32 += x86.Push("ESI")
GetProcAddress32 += x86.Push("EBP")
GetProcAddress32 += x86.Mov("EAX", x86.mem("FS:[0x30]"))
GetProcAddress32 += x86.Mov("EAX", x86.mem("[EAX + 0xC]"))
GetProcAddress32 += x86.Mov("EAX", x86.mem("[EAX + 0xC]")) # ; RAX on the first elt of the list (first module)
GetProcAddress32 += x86.Mov("EDX", "EAX")
GetProcAddress32 += x86.Label(":a_dest")
GetProcAddress32 += x86.Mov("EAX", "EDX")
GetProcAddress32 += x86.Mov("EBX", x86.mem("[EAX + 0x18]")) # EBX : first base ! (base of current module)
GetProcAddress32 += x86.Cmp("EBX", 0)
GetProcAddress32 += x86.Jz(":NOT_FOUND")
GetProcAddress32 += x86.Mov("ECX", x86.mem("[EAX + 0x30]")) # RCX = NAME (UNICODE_STRING.Buffer)
GetProcAddress32 += x86.Push("ECX")
GetProcAddress32 += x86.Call(":FUNC_STRLENW32")
GetProcAddress32 += x86.Pop("EDI") # Current name
GetProcAddress32 += x86.Mov("ECX", "EAX")
GetProcAddress32 += x86.Mov("ESI", x86.mem("[ESP + 0x18]"))
GetProcAddress32 += x86.Rep + x86.CmpsW()
GetProcAddress32 += x86.Test("ECX", "ECX")
GetProcAddress32 += x86.Jz(":DLL_FOUND")
GetProcAddress32 += x86.Mov("EDX", x86.mem("[EDX]"))
GetProcAddress32 += x86.Jmp(":a_dest")
GetProcAddress32 += x86.Label(":DLL_FOUND")
GetProcAddress32 += x86.Mov("EAX", x86.mem("[EBX + 0x3c]")) # rax = PEBASE RVA
GetProcAddress32 += x86.Add("EAX", "EBX") # RAX = PEBASE
GetProcAddress32 += x86.Add("EAX", 0x18) # ;OPTIONAL HEADER
GetProcAddress32 += x86.Mov("ECX", x86.mem("[EAX + 0x60]")) # ;ecx = RVA export dir
GetProcAddress32 += x86.Add("ECX", "EBX") # ;ecx = export_dir
GetProcAddress32 += x86.Mov("EAX", "ECX")
GetProcAddress32 += x86.Push("EAX") # Save it
# ; EBX = BASE | EAX = EXPORT DIR
GetProcAddress32 += x86.Mov("ECX", x86.mem("[EAX + 24] "))
GetProcAddress32 += x86.Mov("EBP", "ECX") # ;EBP = NB names
GetProcAddress32 += x86.Mov("EDX", x86.mem("[EAX + 32] ")) # EDX = names array RVA
GetProcAddress32 += x86.Add("EDX", "EBX") # RDX = names array
GetProcAddress32 += x86.Xor("ECX", "ECX")
GetProcAddress32 += x86.Mov("ESI", x86.mem("[ESP + 0x20]"))
GetProcAddress32 += x86.Label(":SEARCH_LOOP")
GetProcAddress32 += x86.Mov("EDI", x86.mem("[EDX + ECX * 4]")) # ;Get function name RVA
GetProcAddress32 += x86.Add("EDI", "EBX") # ;Get name addr
GetProcAddress32 += x86.Push("ECX") # Save current index
GetProcAddress32 += x86.Push("ESI")
GetProcAddress32 += x86.Call(":FUNC_STRLENA32")
GetProcAddress32 += x86.Mov("ECX", "EAX")
GetProcAddress32 += x86.Push("EDI")
GetProcAddress32 += x86.Call(":FUNC_STRLENA32")
GetProcAddress32 += x86.Pop("EDI")
GetProcAddress32 += x86.Cmp("EAX", "ECX")
GetProcAddress32 += x86.Jnz(":ABORT_STRCMP")
GetProcAddress32 += x86.Inc("ECX")
GetProcAddress32 += x86.Rep + x86.CmpsB()
GetProcAddress32 += x86.Label(":ABORT_STRCMP")
GetProcAddress32 += x86.Pop("ESI")
GetProcAddress32 += x86.Mov("EAX", "ECX")
GetProcAddress32 += x86.Pop("ECX")
GetProcAddress32 += x86.Inc("ECX")
GetProcAddress32 += x86.Test("EAX", "EAX")
GetProcAddress32 += x86.Jnz(":SEARCH_LOOP")
GetProcAddress32 += x86.Dec("ECX")
#GetProcAddress32 += x86.Int3()
#GetProcAddress32 += x86.Int3() # da poi(edx + (ecx * 4)) + ebx; da esi
GetProcAddress32 += x86.Pop("EAX") # ;Restore export_dir addr
GetProcAddress32 += x86.Mov("EDX", x86.mem("[EAX + 36]")) # ;EDX = AddressOfNameOrdinals RVX
GetProcAddress32 += x86.Add("EDX", "EBX")
#GetProcAddress32 += x86.Mov("ECX", x86.mem("[EDX + ECX * 2]"))
GetProcAddress32 += x86.OperandSizeOverride + x86.Mov("ECX", x86.mem("[EDX + ECX * 2]"))
# ; ecx = Ieme ordinal (short array)
GetProcAddress32 += x86.And('ECX', 0xffff)
GetProcAddress32 += x86.Mov("EDX", x86.mem("[EAX + 28]")) # ; AddressOfFunctions RVA
GetProcAddress32 += x86.Add("EDX", "EBX")
GetProcAddress32 += x86.Mov("EDX", x86.mem("[EDX + ECX * 4]"))
GetProcAddress32 += x86.Add("EDX", "EBX")
GetProcAddress32 += x86.Mov("EAX", "EDX")
GetProcAddress32 += x86.Pop("EBP")
GetProcAddress32 += x86.Pop("ESI")
GetProcAddress32 += x86.Pop("EDI")
GetProcAddress32 += x86.Pop("ECX")
GetProcAddress32 += x86.Pop("EBX")
GetProcAddress32 += x86.Ret()
GetProcAddress32 += x86.Label(":NOT_FOUND")
GetProcAddress32 += x86.Xor("EAX", "EAX")
GetProcAddress32 += x86.Ret()
GetProcAddress32 += StrlenW32
GetProcAddress32 += StrlenA32
+2 -1
View File
@@ -715,7 +715,8 @@ class JmpType(Instruction):
class Push(Instruction):
encoding = [(RawBits.from_int(5, 0x50 >> 3), X64RegisterSelector()),
(RawBits.from_int(8, 0x68), Imm32())]
(RawBits.from_int(8, 0x68), Imm32()),
(RawBits.from_int(8, 0xff), Slash(6))]
class Pop(Instruction):
+2 -1
View File
@@ -612,7 +612,8 @@ class Jnb(JmpType):
class Push(Instruction):
encoding = [(RawBits.from_int(5, 0x50 >> 3), X86RegisterSelector()),
(RawBits.from_int(8, 0x68), Imm32())]
(RawBits.from_int(8, 0x68), Imm32()),
(RawBits.from_int(8, 0xff), Slash(6))]
class Pop(Instruction):
+4
View File
@@ -164,6 +164,10 @@ assert Test(mem('[RDI + 0x100]'), 'RCX').get_code() == Test('RCX', mem('[RDI + 0
TestInstr(Push)('R15')
TestInstr(Push)(0x42)
TestInstr(Push)(-1)
TestInstr(Push)(mem("[ECX]"))
TestInstr(Push)(mem("[RCX]"))
TestInstr(Call)('RAX')
TestInstr(Call)(mem('[RAX + RCX * 8]'))
TestInstr(Cpuid)()
+3 -1
View File
@@ -115,6 +115,8 @@ TestInstr(Add)(mem('[EAX]'), 10)
TestInstr(Mov)('EAX', mem('fs:[0xfffc]'))
TestInstr(Mov)(mem('fs:[0xfffc]'), 0)
TestInstr(Push)('ECX')
TestInstr(Push)(mem('[ECX + 8]'))
TestInstr(Sub)('ECX', 'ESP')
TestInstr(Sub)('ECX', mem('[ESP]'))
@@ -153,7 +155,7 @@ TestInstr(CmpsD, expected_result="cmpsd dword ptr [esi], dword ptr es:[edi]")()
TestInstr(Test)('EAX', 'EAX')
TestInstr(Test, expected_result="test edi, ecx ")('ECX', 'EDI')
TestInstr(Test, expected_result="test edi, ecx")('ECX', 'EDI')
TestInstr(Test)(mem('[ECX + 0x100]'), 'ECX')
+2 -2
View File
@@ -1,3 +1,3 @@
from mytest import WindowsTestCase, WindowsAPITestCase, DebuggerTestCase, pop_calc_32, pop_calc_64, Calc32, Calc64
from mytest import WindowsTestCase, WindowsAPITestCase, DebuggerTestCase, NativeUtilsTestCase, pop_calc_32, pop_calc_64, Calc32, Calc64
__all__ = ["WindowsTestCase", "WindowsAPITestCase", "DebuggerTestCase"]
__all__ = ["WindowsTestCase", "WindowsAPITestCase", "DebuggerTestCase", "NativeUtilsTestCase"]
+58
View File
@@ -3,6 +3,7 @@ import struct
import time
import os
import textwrap
import random
from contextlib import contextmanager
sys.path.append(".")
@@ -11,9 +12,12 @@ import windows
import windows.debug
import windows.native_exec.simple_x86 as x86
import windows.native_exec.simple_x64 as x64
import windows.native_exec.nativeutils as nativeutils
from windows.generated_def.winstructs import *
is_process_32_bits = windows.current_process.bitness == 32
is_process_64_bits = windows.current_process.bitness == 64
@@ -299,6 +303,59 @@ class WindowsAPITestCase(unittest.TestCase):
with self.assertRaises(WindowsError) as ar:
windows.winproxy.CreateFileA("NONEXISTFILE.FILE")
class NativeUtilsTestCase(unittest.TestCase):
@process_64bit_only
def test_strlenw64(self):
strlenw64 = windows.native_exec.create_function(nativeutils.StrlenW64.get_code(), [UINT, LPCWSTR])
self.assertEqual(strlenw64("YOLO"), 4)
self.assertEqual(strlenw64(""), 0)
@process_64bit_only
def test_strlena64(self):
strlena64 = windows.native_exec.create_function(nativeutils.StrlenA64.get_code(), [UINT, LPCSTR])
self.assertEqual(strlena64("YOLO"), 4)
self.assertEqual(strlena64(""), 0)
@process_64bit_only
def test_getprocaddr64(self):
getprocaddr64 = windows.native_exec.create_function(nativeutils.GetProcAddress64.get_code(), [ULONG64, LPCWSTR, LPCSTR])
k32 = [mod for mod in windows.current_process.peb.modules if mod.name == "kernel32.dll"][0]
exports = [(x,y) for x,y in k32.pe.exports.items() if isinstance(x, basestring)]
for i in range(15):
name, addr = random.choice(exports)
name = name.encode()
compute_addr = getprocaddr64("KERNEL32.DLL", name)
# Put name in test to know which function caused the assert fails
self.assertEqual((name, hex(addr)), (name, hex(compute_addr)))
@process_32bit_only
def test_strlenw32(self):
strlenw32 = windows.native_exec.create_function(nativeutils.StrlenW32.get_code(), [UINT, LPCWSTR])
self.assertEqual(strlenw32("YOLO"), 4)
self.assertEqual(strlenw32(""), 0)
@process_32bit_only
def test_strlena32(self):
strlena32 = windows.native_exec.create_function(nativeutils.StrlenA32.get_code(), [UINT, LPCSTR])
self.assertEqual(strlena32("YOLO"), 4)
self.assertEqual(strlena32(""), 0)
@process_32bit_only
def test_getprocaddr32(self):
getprocaddr32 = windows.native_exec.create_function(nativeutils.GetProcAddress32.get_code(), [UINT, LPCWSTR, LPCSTR])
k32 = [mod for mod in windows.current_process.peb.modules if mod.name == "kernel32.dll"][0]
exports = [(x,y) for x,y in k32.pe.exports.items() if isinstance(x, basestring)]
for i in range(1500):
name, addr = random.choice(exports)
name = name.encode()
compute_addr = getprocaddr32("KERNEL32.DLL", name)
# Put name in test to know which function caused the assert fails
self.assertEqual((name, hex(addr)), (name, hex(compute_addr)))
class DebuggerTestCase(unittest.TestCase):
@@ -447,6 +504,7 @@ if __name__ == '__main__':
alltests.addTest(unittest.makeSuite(WindowsTestCase))
alltests.addTest(unittest.makeSuite(WindowsAPITestCase))
alltests.addTest(unittest.makeSuite(DebuggerTestCase))
alltests.addTest(unittest.makeSuite(NativeUtilsTestCase))
alltests.debug()
tester = unittest.TextTestRunner(verbosity=2)
tester.run(alltests)