From 654e6bff713fd694fe20d954512de053593b3457 Mon Sep 17 00:00:00 2001 From: Clement Rouault Date: Thu, 13 Apr 2017 11:14:09 +0200 Subject: [PATCH] change remote_calc sample to use notepad + add demo of test_code sample + Stos[BWD] in simple_x86 + --- docs/source/sample.rst | 184 +++++++++++++++++++++++------- samples/encryption_demo.py | 2 +- samples/remote_calc.py | 92 --------------- samples/remote_process.py | 92 +++++++++++++++ samples/test_code.py | 49 +++----- windows/native_exec/simple_x86.py | 10 ++ windows/test/test_crypto.py | 7 +- 7 files changed, 266 insertions(+), 170 deletions(-) delete mode 100644 samples/remote_calc.py create mode 100644 samples/remote_process.py diff --git a/docs/source/sample.rst b/docs/source/sample.rst index 0eff11d..c86df28 100644 --- a/docs/source/sample.rst +++ b/docs/source/sample.rst @@ -1,10 +1,13 @@ Samples of code =============== +Processes +""""""""" + .. _sample_current_process: ``windows.current_process`` -""""""""""""""""""""""""""" +''''''''''''''''''''''''''' .. literalinclude:: ..\..\samples\current_process.py @@ -26,34 +29,34 @@ Output:: .. _sample_remote_process: Remote process : :class:`WinProcess` -"""""""""""""""""""""""""""""""""""" +'''''''''''''''''''''''''''''''''''' -.. literalinclude:: ..\..\samples\remote_calc.py +.. literalinclude:: ..\..\samples\remote_process.py Output:: - (cmd λ) python.exe remote_calc.py - Creating a calc - Looking for calcs in the processes - They are currently <1> calcs running on the system - Let's play with our calc: <> - Our calc pid is 8052 - Our calc is a <32> bits process - Our calc is a SysWow64 process ? - Our calc have threads ! <[, , ]> - Exploring our calc PEB ! - Command line is - Here are 3 loaded modules: [, , ] - Allocating memory in our calc - Allocated memory is at <0x5c90000> + (cmd λ) python.exe remote_process.py + Creating a notepad + Looking for notepads in the processes + They are currently <1> notepads running on the system + Let's play with our notepad: <> + Our notepad pid is 2044 + Our notepad is a <32> bits process + Our notepad is a SysWow64 process ? + Our notepad have threads ! <[, , ...]> + Exploring our notepad PEB ! + Command line is + Here are 3 loaded modules: [, , ] + Allocating memory in our notepad + Allocated memory is at <0x6f80000> Writing 'SOME STUFF' in allocated memory Reading allocated memory : <'SOME STUFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'> - Execution some native code in our calc (write 0x424242 at allocated address + return 0x1337 + Execution some native code in our notepad (write 0x424242 at allocated address + return 0x1337) Executing native code ! Return code = 0x1337L Reading allocated memory : <'BBBB STUFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'> Executing python code ! - Reading allocated memory : <'HELLO FROM CALC\x00\x00\x00\x00\x00'> + Reading allocated memory : <'HELLO FROM notepad\x00\x00'> Trying to import in remote module 'FAKE_MODULE' Remote ERROR ! Traceback (most recent call last): @@ -61,13 +64,13 @@ Output:: File "", line 2, in func ImportError: No module named FAKE_MODULE - That's all ! killing the calc + That's all ! killing the notepad .. _sample_peb_exploration: :class:`PEB` exploration -"""""""""""""""""""""""" +'''''''''''''''''''''''' .. literalinclude:: ..\..\samples\peb.py @@ -97,7 +100,30 @@ Output:: IAT Entry for ntdll!NtCreateFile = | addr = 0x77541128L Sections: [, , , , ] +.. _sample_iat_hook: +IAT hooking +''''''''''' + +.. literalinclude:: ..\..\samples\iat_hook.py + +Output:: + + (cmd λ) python iat_hook.py + Asking for + Hook called | hKey = 0x12d687 | lpSubKey = + Secret key asked, returning magic handle 0x12345678 + Result = 0x12345678 + + Asking for + Hook called | hKey = 0x12d687 | lpSubKey = + Asked for a failing key: returning 0x2a + WindowsError(42, 'Windows Error 0x2A') + + Asking for + Hook called | hKey = 0x80000001L | lpSubKey = + Non-secret key : calling normal function + Result = 0x108 .. _sample_system: @@ -149,30 +175,7 @@ Output:: Handle is in process pid=14340> Name is <\Device\ConDrv> -.. _sample_iat_hook: -IAT hooking -""""""""""" - -.. literalinclude:: ..\..\samples\iat_hook.py - -Output:: - - (cmd λ) python iat_hook.py - Asking for - Hook called | hKey = 0x12d687 | lpSubKey = - Secret key asked, returning magic handle 0x12345678 - Result = 0x12345678 - - Asking for - Hook called | hKey = 0x12d687 | lpSubKey = - Asked for a failing key: returning 0x2a - WindowsError(42, 'Windows Error 0x2A') - - Asking for - Hook called | hKey = 0x80000001L | lpSubKey = - Non-secret key : calling normal function - Result = 0x108 .. _sample_network_exploration: @@ -401,6 +404,99 @@ Ouput:: Exiting process + +Native code tester +~~~~~~~~~~~~~~~~~~ + +.. literalinclude:: ..\..\samples\test_code.py + + +Ouput:: + + (cmd λ) python.exe .\samples\test_code.py "mov eax, 0x42424242" "eax=0x11223344" + Testing x86 code + Startup context is: + Eip -> 0x3f0000L + Esp -> 0x3bfae4L + Eax -> 0x11223344L + Ebx -> 0x5a6000L + Ecx -> 0x0L + Edx -> 0x0L + Ebp -> 0x0L + Edi -> 0x0L + Esi -> 0x0L + EFlags -> 0x202L + EEflags(0x202L:IF) + ==Post-exec context== + Eip -> 0x3f0007L + Esp -> 0x3bfae4L + Eax -> 0x42424242L + Ebx -> 0x5a6000L + Ecx -> 0x0L + Edx -> 0x0L + Ebp -> 0x0L + Edi -> 0x0L + Esi -> 0x0L + EFlags -> 0x202L + EEflags(0x202L:IF) + + ==DIFF== + Eip: 0x3f0000 -> 0x3f0007 (+0x7) + Eax: 0x11223344 -> 0x42424242 (+0x31200efe) + + + (cmd λ) python64 .\samples\test_code.py --x64 "mov r15, 0x11223344; push r14; call r15" "rcx=1; r14=0x4242424243434343" + Testing x64 code + Startup context is: + Rip -> 0x205a1d60000L + Rsp -> 0xe24a88fa88L + Rax -> 0x0L + Rbx -> 0x0L + Rcx -> 0x1L + Rdx -> 0xe24aaf9000L + Rbp -> 0x0L + Rdi -> 0x0L + Rsi -> 0x0L + R8 -> 0x0L + R9 -> 0x0L + R10 -> 0x0L + R11 -> 0x0L + R12 -> 0x0L + R13 -> 0x0L + R14 -> 0x4242424243434343L + R15 -> 0x0L + EFlags -> 0x200L + EEflags(0x200L:IF) + ==Post-exec context== + Rip -> 0x11223344L + Rsp -> 0xe24a88fa78L + Rax -> 0x0L + Rbx -> 0x0L + Rcx -> 0x1L + Rdx -> 0xe24aaf9000L + Rbp -> 0x0L + Rdi -> 0x0L + Rsi -> 0x0L + R8 -> 0x0L + R9 -> 0x0L + R10 -> 0x0L + R11 -> 0x0L + R12 -> 0x0L + R13 -> 0x0L + R14 -> 0x4242424243434343L + R15 -> 0x11223344L + EFlags -> 0x10202L + EEflags(0x10202L:IF|RF) + at <0x11223344> + ==DIFF== + Rip: 0x205a1d60000 -> 0x11223344 (-0x20590b3ccbc) + Rsp: 0xe24a88fa88 -> 0xe24a88fa78 (-0x10) + R15: 0x0 -> 0x11223344 (+0x11223344) + EFlags: 0x200 -> 0x10202 (+0x10002) + Negative Stack: dumping: + E24A88FA88 0C 00 D6 A1 05 02 00 00 43 43 43 43 42 42 42 42 ........CCCCBBBB + + .. _sample_local_debugger: diff --git a/samples/encryption_demo.py b/samples/encryption_demo.py index 1dafa41..54922e1 100644 --- a/samples/encryption_demo.py +++ b/samples/encryption_demo.py @@ -80,7 +80,7 @@ def genkeys(common_name, pfxpassword, outname, **kwargs): prov = HCRYPTPROV() winproxy.CryptAcquireContextW(prov, PFW_TMP_KEY_CONTAINER, None, PROV_RSA_FULL, CRYPT_DELETEKEYSET) -parser = argparse.ArgumentParser(prog='PROG') +parser = argparse.ArgumentParser(prog=__file__) subparsers = parser.add_subparsers(description='valid subcommands',) cryptparse = subparsers.add_parser('crypt') diff --git a/samples/remote_calc.py b/samples/remote_calc.py deleted file mode 100644 index 77c85f9..0000000 --- a/samples/remote_calc.py +++ /dev/null @@ -1,92 +0,0 @@ -import sys -import os.path -sys.path.append(os.path.abspath(__file__ + "\..\..")) - -import windows -import windows.native_exec.simple_x86 as x86 -import windows.native_exec.simple_x64 as x64 - -print("Creating a calc") -calc = windows.utils.create_process(r"C:\windows\system32\calc.exe") -# You don't need to do that in our case, but it's useful to now -print("Looking for calcs in the processes") -all_calcs = [proc for proc in windows.system.processes if proc.name == "calc.exe"] -print("They are currently <{0}> calcs running on the system".format(len(all_calcs))) - -print("Let's play with our calc: <{calc}>".format(calc=calc)) -print("Our calc pid is {calc.pid}".format(calc=calc)) -print("Our calc is a <{calc.bitness}> bits process".format(calc=calc)) -print("Our calc is a SysWow64 process ? <{calc.is_wow_64}>".format(calc=calc)) -print("Our calc have threads ! <{calc.threads}>".format(calc=calc)) - -# PEB STUFF -peb = calc.peb -print("Exploring our calc PEB ! {peb}".format(peb=peb)) -print("Command line is {peb.commandline}".format(peb=peb)) -modules = peb.modules -print("Here are 3 loaded modules: {0}".format(modules[:3])) -# See iat_hook.py for module exploration - - -# Remote alloc / read / write - -print("Allocating memory in our calc") -addr = calc.virtual_alloc(0x1000) -print("Allocated memory is at <{0}>".format(hex(addr))) -print("Writing 'SOME STUFF' in allocated memory") -calc.write_memory(addr, "SOME STUFF") -print("Reading allocated memory : <{0}>".format(repr(calc.read_memory(addr, 20)))) - - -# Remote Execution - -print("Execution some native code in our calc (write 0x424242 at allocated address + return 0x1337)") - -if calc.bitness == 32: - # Let's generate some native code - code = x86.MultipleInstr() - code += x86.Mov(x86.deref(addr), 0x42424242) - code += x86.Mov("EAX", 0x1337) - code += x86.Ret() -else: - code = x64.MultipleInstr() - code += x64.Mov('RAX', addr) - code += x64.Mov(x64.mem("[RAX]"), 0x42424242) - code += x64.Mov("RAX", 0x1337) - code += x64.Ret() - -print("Executing native code !") -t = calc.execute(code.get_code()) -t.wait() -print("Return code = {0}".format(hex(t.exit_code))) -print("Reading allocated memory : <{0}>".format(repr(calc.read_memory(addr, 20)))) - -print("Executing python code !") -# Make 'windows' importable in remote python -calc.execute_python("import sys; sys.path.append(r'{0}')".format(sys.path[-1])) - -calc.execute_python("import windows") -# Let's write in the calc 'current_process' memory :) -calc.execute_python("addr = {addr}; windows.current_process.write_memory(addr, 'HELLO FROM CALC')".format(addr=addr)) -print("Reading allocated memory : <{0}>".format(repr(calc.read_memory(addr, 20)))) - -# python_execute is 'safe': -# - it waits for the thread completion -# - it raise an error if remote code raised some - -try: - print("Trying to import in remote module 'FAKE_MODULE'") - calc.execute_python("def func():\n import FAKE_MODULE\nfunc()") -except windows.injection.RemotePythonError as e: - print("Remote ERROR !") - print(e) - -print("That's all ! killing the calc") -calc.exit() - - - - - - - diff --git a/samples/remote_process.py b/samples/remote_process.py new file mode 100644 index 0000000..8230d76 --- /dev/null +++ b/samples/remote_process.py @@ -0,0 +1,92 @@ +import sys +import os.path +sys.path.append(os.path.abspath(__file__ + "\..\..")) + +import windows +import windows.native_exec.simple_x86 as x86 +import windows.native_exec.simple_x64 as x64 + +print("Creating a notepad") ## Replaced calc.exe by notepad.exe cause of windows 10. +notepad = windows.utils.create_process(r"C:\windows\system32\notepad.exe") +# You don't need to do that in our case, but it's useful to now +print("Looking for notepads in the processes") +all_notepads = [proc for proc in windows.system.processes if proc.name == "notepad.exe"] +print("They are currently <{0}> notepads running on the system".format(len(all_notepads))) + +print("Let's play with our notepad: <{notepad}>".format(notepad=notepad)) +print("Our notepad pid is {notepad.pid}".format(notepad=notepad)) +print("Our notepad is a <{notepad.bitness}> bits process".format(notepad=notepad)) +print("Our notepad is a SysWow64 process ? <{notepad.is_wow_64}>".format(notepad=notepad)) +print("Our notepad have threads ! <{notepad.threads}>".format(notepad=notepad)) + +# PEB STUFF +peb = notepad.peb +print("Exploring our notepad PEB ! {peb}".format(peb=peb)) +print("Command line is {peb.commandline}".format(peb=peb)) +modules = peb.modules +print("Here are 3 loaded modules: {0}".format(modules[:3])) +# See iat_hook.py for module exploration + + +# Remote alloc / read / write + +print("Allocating memory in our notepad") +addr = notepad.virtual_alloc(0x1000) +print("Allocated memory is at <{0}>".format(hex(addr))) +print("Writing 'SOME STUFF' in allocated memory") +notepad.write_memory(addr, "SOME STUFF") +print("Reading allocated memory : <{0}>".format(repr(notepad.read_memory(addr, 20)))) + + +# Remote Execution + +print("Execution some native code in our notepad (write 0x424242 at allocated address + return 0x1337)") + +if notepad.bitness == 32: + # Let's generate some native code + code = x86.MultipleInstr() + code += x86.Mov(x86.deref(addr), 0x42424242) + code += x86.Mov("EAX", 0x1337) + code += x86.Ret() +else: + code = x64.MultipleInstr() + code += x64.Mov('RAX', addr) + code += x64.Mov(x64.mem("[RAX]"), 0x42424242) + code += x64.Mov("RAX", 0x1337) + code += x64.Ret() + +print("Executing native code !") +t = notepad.execute(code.get_code()) +t.wait() +print("Return code = {0}".format(hex(t.exit_code))) +print("Reading allocated memory : <{0}>".format(repr(notepad.read_memory(addr, 20)))) + +print("Executing python code !") +# Make 'windows' importable in remote python +notepad.execute_python("import sys; sys.path.append(r'{0}')".format(sys.path[-1])) + +notepad.execute_python("import windows") +# Let's write in the notepad 'current_process' memory :) +notepad.execute_python("addr = {addr}; windows.current_process.write_memory(addr, 'HELLO FROM notepad')".format(addr=addr)) +print("Reading allocated memory : <{0}>".format(repr(notepad.read_memory(addr, 20)))) + +# python_execute is 'safe': +# - it waits for the thread completion +# - it raise an error if remote code raised some + +try: + print("Trying to import in remote module 'FAKE_MODULE'") + notepad.execute_python("def func():\n import FAKE_MODULE\nfunc()") +except windows.injection.RemotePythonError as e: + print("Remote ERROR !") + print(e) + +print("That's all ! killing the notepad") +notepad.exit() + + + + + + + diff --git a/samples/test_code.py b/samples/test_code.py index 73cc0e3..d3bc91b 100644 --- a/samples/test_code.py +++ b/samples/test_code.py @@ -1,4 +1,5 @@ import sys +import argparse import windows import windows.test @@ -7,6 +8,7 @@ import windows.native_exec.simple_x86 as x86 import windows.native_exec.simple_x64 as x64 from windows.generated_def import * + def hexdump(string, start_addr=0): result = "" if len(string) == 0: @@ -112,17 +114,17 @@ class CodeTesteur(dbg.Debugger): print(hexdump(data, start.sp)) -def test_code_x86(raw=False): +def test_code_x86(code, regs=None, raw=False, **kwargs): print("Testing x86 code") process = windows.test.pop_calc_32(dwCreationFlags=DEBUG_PROCESS) if raw: - code = sys.argv[1].replace(" ", "").decode('hex') + code = code.replace(" ", "").decode('hex') else: - code = x86.assemble(sys.argv[1]) + code = x86.assemble(code) start_register = {} - if len(sys.argv) > 2: - for name_value in sys.argv[2].split(";"): + if regs: + for name_value in regs.split(";"): name, value = name_value.split("=") name = name.strip().capitalize() if name == "Eflags": @@ -134,19 +136,19 @@ def test_code_x86(raw=False): x = CodeTesteur(process, code, start_register) x.loop() -def test_code_x64(raw=False): +def test_code_x64(code, regs=None, raw=False, **kwargs): print("Testing x64 code") if windows.current_process.bitness == 32: raise ValueError("Cannot debug a 64b process from 32b python") process = windows.test.pop_calc_64(dwCreationFlags=DEBUG_PROCESS) if raw: - code = sys.argv[1].replace(" ", "").decode('hex') + code = code.replace(" ", "").decode('hex') else: - code = x64.assemble(sys.argv[1]) + code = x64.assemble(code) start_register = {} - if len(sys.argv) > 2: - for name_value in sys.argv[2].split(";"): + if regs: + for name_value in regs.split(";"): name, value = name_value.split("=") name = name.strip().capitalize() if name == "Eflags": @@ -158,27 +160,14 @@ def test_code_x64(raw=False): x = CodeTesteur(process, code, start_register) x.loop() -import sys -if len(sys.argv) < 2: - print("Need x86 code to exec as first argument") - exit(1) -if sys.argv[1] == "-x64": - sys.argv.remove("-x64") - test_code_x64() -elif sys.argv[1] == "--raw": - sys.argv.remove("--raw") - test_code_x86(raw=True) -elif sys.argv[1] == "--raw64": - sys.argv.remove("--raw64") - test_code_x64(raw=True) -else: - test_code_x86() +parser = argparse.ArgumentParser(prog=__file__) +parser.add_argument('--x64', action='store_const', dest="func", const=test_code_x64, default=test_code_x86, help='Code is x64') +parser.add_argument('--raw', action='store_true', help='argument is raw assembled code (in hex)') +parser.add_argument('code', help='The code to execute') +parser.add_argument('regs', nargs="?", help='The default values of the registers') +res = parser.parse_args() - - - - -#test_code(c, "\xcc") \ No newline at end of file +res.func(**res.__dict__) \ No newline at end of file diff --git a/windows/native_exec/simple_x86.py b/windows/native_exec/simple_x86.py index 235f4a3..ad542ed 100644 --- a/windows/native_exec/simple_x86.py +++ b/windows/native_exec/simple_x86.py @@ -779,6 +779,16 @@ class ScasD(Instruction): encoding = [(RawBits.from_int(8, 0xAF),)] +class StosB(Instruction): + encoding = [(RawBits.from_int(8, 0xAA),)] + +class StosW(Instruction): + encoding = [(RawBits.from_int(16, 0x66AB),)] + +class StosD(Instruction): + encoding = [(RawBits.from_int(8, 0xAB),)] + + class CmpsB(Instruction): default_32_bits = True encoding = [(RawBits.from_int(8, 0xa6),)] diff --git a/windows/test/test_crypto.py b/windows/test/test_crypto.py index 59c8b4e..9c69e1f 100644 --- a/windows/test/test_crypto.py +++ b/windows/test/test_crypto.py @@ -54,9 +54,10 @@ DgMCGgQU70h/rEXLQOberGvgJenggoWU5poEFCfdE1wNK1M38Yp3+qfjEqNIJGCPAgIH0A== class CryptoTestCase(unittest.TestCase): - def setUp(self): - self.raw_cert = TEST_CERT.decode("base64") - self.raw_pfx = TEST_PFX.decode("base64") + @classmethod + def setUpClass(cls): + cls.raw_cert = TEST_CERT.decode("base64") + cls.raw_pfx = TEST_PFX.decode("base64") def test_certificate(self): cert = windows.crypto.CertificateContext.from_buffer(self.raw_cert)