def test_decompiler_basic(): # # pie # process = revenge.Process(basic_one_path, resume=False, verbose=False) process.radare2._r2.cmd("aaa") sleep(0.2) a = process.decompiler.decompile_address("basic_one:0x66d") assert len(a) == 1 assert 0x66d in a assert a[0x66d].address == 0x66d assert "sym.func" in a[0x66d].src process.quit() # # No pie # process = revenge.Process(basic_one_ia32_nopie_path, resume=False, verbose=False) process.radare2._r2.cmd("aaa") sleep(0.2) a = process.decompiler.decompile_address(0x08048460) assert len(a) == 1 assert 0x08048460 in a assert a[0x08048460].address == 0x08048460 assert "sym.func" in a[0x08048460].src process.quit()
def test_frida_process_stdio(capsys): process = revenge.Process("/bin/cat", verbose=False, resume=True) process.stdin("hello world\n") assert b"hello world" in stdout_expect(process, b"world") process.quit() process = revenge.Process("/bin/cat", verbose=False, resume=False) process._stdout_echo = True process.memory[process.entrypoint].breakpoint = False process.stdin("hello world\n") sleep(0.5) assert "hello world" in capsys.readouterr().out process.quit() process = revenge.Process([cat_stderr_path, "hello world"], verbose=False, resume=True) assert b"hello world" in stderr_expect(process, b"world") process.quit() process = revenge.Process([cat_stderr_path, "Test2 Blerg"], verbose=False, resume=False) process._stderr_echo = True process.memory[process.entrypoint].breakpoint = False sleep(0.5) assert "Test2 Blerg" in capsys.readouterr().out process.quit()
def test_process_arch(): basic_one = revenge.Process(basic_one_path, resume=False, verbose=False, load_symbols='basic_one') basic_one_ia32 = revenge.Process(basic_one_ia32_path, resume=False, verbose=False, load_symbols='basic_one_ia32') assert basic_one.arch == "x64" assert basic_one_ia32.arch == "ia32" basic_one.quit() basic_one_ia32.quit()
def test_plt(): process = revenge.Process(basic_one_path, resume=False, verbose=False) basic_one_64_nopie = revenge.Process(basic_one_64_nopie_path, resume=False) basic_one_ia32 = revenge.Process(basic_one_ia32_path, resume=False) basic_one_ia32_nopie = revenge.Process(basic_one_ia32_nopie_path, resume=False) # # First parse # basic_one_mod = process.modules['basic_one'] assert basic_one_mod.plt & 0xfff == 0x510 printf = process.memory[basic_one_mod.symbols['plt.printf']] assert printf("123456") == 6 assert 'printf' in process.memory.describe_address(basic_one_mod.symbols['plt.printf']) assert 'printf' in process.memory.describe_address(basic_one_mod.symbols['got.printf']) assert 'printf' in process.memory.describe_address(process.memory[basic_one_mod.symbols['got.printf']].pointer) basic_one_mod = basic_one_64_nopie.modules['basic_one*'] assert basic_one_mod.plt == 0x4003e0 printf = basic_one_64_nopie.memory[basic_one_mod.symbols['plt.printf']] assert printf("123456") == 6 assert 'printf' in basic_one_64_nopie.memory.describe_address(basic_one_mod.symbols['plt.printf']) assert 'printf' in basic_one_64_nopie.memory.describe_address(basic_one_mod.symbols['got.printf']) assert 'printf' in basic_one_64_nopie.memory.describe_address(basic_one_64_nopie.memory[basic_one_mod.symbols['got.printf']].pointer) basic_one_mod = basic_one_ia32.modules['basic_one*'] assert basic_one_mod.plt & 0xfff == 0x3a0 printf = basic_one_ia32.memory[basic_one_mod.symbols['plt.printf']] # This uses thunks... No easy way of testing call through plt rn.. #assert printf("123456") == 6 assert 'printf' in basic_one_ia32.memory.describe_address(basic_one_mod.symbols['plt.printf']) assert 'printf' in basic_one_ia32.memory.describe_address(basic_one_mod.symbols['got.printf']) assert 'printf' in basic_one_ia32.memory.describe_address(basic_one_ia32.memory[basic_one_mod.symbols['got.printf']].pointer) basic_one_mod = basic_one_ia32_nopie.modules['basic_one*'] assert basic_one_mod.plt == 0x80482d0 printf = basic_one_ia32_nopie.memory[basic_one_mod.symbols['plt.printf']] # This uses thunks... No easy way of testing call through plt rn.. assert printf("123456") == 6 assert 'printf' in basic_one_ia32_nopie.memory.describe_address(basic_one_mod.symbols['plt.printf']) assert 'printf' in basic_one_ia32_nopie.memory.describe_address(basic_one_mod.symbols['got.printf']) assert 'printf' in basic_one_ia32_nopie.memory.describe_address(basic_one_ia32_nopie.memory[basic_one_mod.symbols['got.printf']].pointer) process.quit() basic_one_64_nopie.quit() basic_one_ia32.quit() basic_one_ia32_nopie.quit()
def test_memory_on_message(): process = revenge.Process(basic_one_path, resume=False, verbose=False, load_symbols='basic_one') f = process.memory.find(types.StringUTF8("This is my string")) f.sleep_until_completed() payload = {'payload': [{'address': 1337}, {'address': 31337}]} f._on_message(payload, None) assert 1337 in f assert 31337 in f f.completed = False f._script = None payload = {'payload': "DONE"} f._on_message(payload, None) assert f.completed == True # Unexpected message payload = {'payload': 1.12} f._on_message(payload, None) process.quit()
def test_access_violation_execute(): p = revenge.Process(exceptions_path, resume=False, verbose=False) do_access_exec_violation = p.memory[ p.modules['exceptions'].symbols['do_access_exec_violation']] do_good = p.memory[p.modules['exceptions'].symbols['do_good']] e = do_access_exec_violation() assert isinstance(e, revenge.native_exception.NativeException) str(e) repr(e) assert e.type == 'access-violation' # 0x666 shouldn't be in any module assert p.modules[e.address] is None assert p.memory.describe_address(e.address) == "0x666" assert isinstance(e.backtrace, revenge.native_exception.NativeBacktrace) assert isinstance(e.context, revenge.cpu.contexts.x64.X64Context) assert e.memory_address == 0x666 assert e.memory_operation == 'execute' # If we handled exception correctly, process should still be in good state assert not isinstance(do_good, revenge.native_exception.NativeException) p.quit()
def test_timeless_basic_amd64(): p = revenge.Process(timeless_one_path, resume=False, verbose=True) timeless = p.techniques.NativeTimelessTracer() repr(timeless) str(timeless) timeless.apply() t = list(timeless)[0] p.memory[p.entrypoint].breakpoint = False # Right after decrypting after_call = p.memory['timeless_one:0x6F5'].address ti = t.wait_for(after_call) assert ti.context.rax.next.thing == "SuperS3cretFl@g" # This is to make sure we're cache invalidating correctly.. assert ti.context.rdi.next.thing == "SuperS3cretFl@g" assert int(ti.context.pc) == after_call repr(timeless) str(timeless) repr(t) str(t) for ti in t: repr(ti) str(ti) p.quit()
def test_process_run_script_generic_include_js_dispose(): process = revenge.Process(basic_one_path, resume=False, verbose=False, load_symbols='basic_one') mem1 = process.memory.alloc(8) mem2 = process.memory.alloc(8) mem1.int32 = 0 mem2.int32 = 0 script = """dispose_push(function () {{ {}.writeS32(1337); }}); dispose_push(function () {{ {}.writeS32(1337); }});""".format( mem1.address.js, mem2.address.js) process.run_script_generic(script, raw=True, unload=True, include_js="dispose.js") assert mem1.int32 == 1337 assert mem2.int32 == 1337 process.quit()
def test_access_violation_read(): p = revenge.Process(exceptions_path, resume=False, verbose=False) do_access_read_violation = p.memory[ p.modules['exceptions'].symbols['do_access_read_violation']] do_good = p.memory[p.modules['exceptions'].symbols['do_good']] e = do_access_read_violation() assert isinstance(e, revenge.native_exception.NativeException) str(e) repr(e) assert e.type == 'access-violation' # This abort is run by throwing the signal from libc assert p.modules[e.address].name == 'exceptions' assert p.memory.describe_address( e.address).startswith("exceptions:do_access_read_violation") assert isinstance(e.backtrace, revenge.native_exception.NativeBacktrace) assert isinstance(e.context, revenge.cpu.contexts.x64.X64Context) assert e.memory_address == 0x666 assert e.memory_operation == 'read' # If we handled exception correctly, process should still be in good state assert not isinstance(do_good, revenge.native_exception.NativeException) p.quit()
def test_thread_breakpoint(): p = revenge.Process(basic_threads_path, resume=False, verbose=False, load_symbols='basic_threads') t = list(p.threads)[0] # Wait to hit the breakpoint while not t.breakpoint: pass start_address = p.memory["basic_threads:0x670"].address assert t.id in p.threads._breakpoint_context assert t.context.pc == start_address t.breakpoint = False # breakpoint doesn't directly update. it has to get updated by the script # terminating in frida while True: t = list(p.threads)[0] if not t.breakpoint: break elif t.breakpoint and t.context.pc != start_address: break p.quit()
def test_argument_types(): p = revenge.Process(basic_one_path, resume=False, verbose=False, load_symbols='basic_one') strlen = p.memory[':strlen'] assert strlen.argument_types is None # This should produce logger error and not set strlen.argument_types = 12 assert strlen.argument_types is None strlen.argument_types = types.Int32 assert strlen.argument_types == (types.Int32, ) strlen.argument_types = (types.Int32, types.Double) assert strlen.argument_types == (types.Int32, types.Double) strlen.argument_types = [types.Int32, types.Double] assert strlen.argument_types == (types.Int32, types.Double) p.quit()
def test_thread_join_linux(): process = revenge.Process(basic_threads_path, resume=False, verbose=False) malloc = process.memory['malloc'] malloc.argument_types = types.Int malloc.return_type = types.Pointer func = process.memory.create_c_function( "void *func() { return (void *)1337; }") t = process.threads.create(func.address) assert t.join() == 1337 func = process.memory.create_c_function( "void* func() { double d=1337; double *dp = malloc(sizeof(double)); *dp = d; return (void *)dp; }", malloc=malloc) t = process.threads.create(func.address) assert process.memory[t.join()].double == 1337.0 func = process.memory.create_c_function( "void* func() { float f=1337; float *fp = malloc(sizeof(float)); *fp = f; return (void *)fp; }", malloc=malloc) t = process.threads.create(func.address) assert process.memory[t.join()].float == 1337.0 process.quit()
def test_ctypes(): process = revenge.Process(basic_struct_path, resume=False, verbose=False) assert types.Int8.ctype == "char" assert types.UInt8.ctype == "unsigned char" assert types.Char.ctype == "char" assert types.UChar.ctype == "unsigned char" assert types.Int16.ctype == "short" assert types.UInt16.ctype == "unsigned short" assert types.Short.ctype == "short" assert types.UShort.ctype == "unsigned short" assert types.Int32.ctype == "int" assert types.UInt32.ctype == "unsigned int" assert types.Int.ctype == "int" assert types.UInt.ctype == "unsigned int" assert types.Int64.ctype == "long" assert types.UInt64.ctype == "unsigned long" assert types.Long.ctype == "long" assert types.ULong.ctype == "unsigned long" assert types.Float.ctype == "float" assert types.Double.ctype == "double" assert types.Pointer.ctype == "void *" assert types.StringUTF8.ctype == "char *" process.quit()
def test_basic_one_trace_thread_int(): basic_one = revenge.Process(basic_one_path, resume=False, verbose=False, load_symbols='basic_one') thread = list(basic_one.threads)[0] t = basic_one.techniques.NativeInstructionTracer(exec=True) t.apply([thread.id]) str(t) t2 = list(t)[0] while len(t2) < 15: time.sleep(0.1) with pytest.raises(Exception): t3 = basic_one.techniques.NativeInstructionTracer(exec=True) t3.apply([12.12]) # Testing exception for attempting to create another trace on a thread that is already being traced with pytest.raises(Exception): t3 = basic_one.techniques.NativeInstructionTracer(exec=True) t3.apply() t2.stop() # This should not raise an exception now t = basic_one.techniques.NativeInstructionTracer(exec=True) t.apply() basic_one.quit()
def test_basic_one_trace_thread(): basic_one = revenge.Process(basic_one_path, resume=False, verbose=False, load_symbols='basic_one') thread = list(basic_one.threads)[0] t = basic_one.techniques.NativeInstructionTracer(exec=True) t.apply([thread]) t2 = list(t)[0] while len(t2) < 15: time.sleep(0.1) assert len(t2) > 0 t2.stop() time.sleep(0.3) t = basic_one.techniques.NativeInstructionTracer(exec=True) t.apply(thread) t2 = list(t)[0] basic_one.memory[basic_one.entrypoint].breakpoint = False t2.wait_for('basic_one:0x692') # final ret assert len(t2) > 0 # TODO: Figure out why this final trace stop causes things to hang... #t2.stop() basic_one.quit()
def test_trace_exclude_ranges(): p = revenge.Process(basic_one_path, resume=False, verbose=False, load_symbols='basic_one') # Create a thread that will just spin m = p.memory.alloc(8) m.int64 = 0 func = p.memory.create_c_function( """ void func() {{ while ( *(void *){} == 0 ) {{ ; }} }}""".format( hex(m.address))) # Start-er up t = p.threads.create(func.address) # Kick off thread, explicitly ignoring the thread code itself trace = p.techniques.NativeInstructionTracer( exec=True, exclude_ranges=[[func.address, func.address + 0x100]]) trace.apply(t) m.int64 = 1 time.sleep(0.2) # We should have nothing in our trace, since we excluded it all assert len(t.trace) == 0 p.quit()
def test_memory_find_general(): process = revenge.Process(basic_one_path, resume=False, verbose=False, load_symbols='basic_one') f = process.memory.find(types.StringUTF8("This is my string")) f.sleep_until_completed() assert len(f) > 0 for addr in f: assert process.memory[addr].string_utf8 == "This is my string" # Test invalid find with pytest.raises(Exception): util.memory.find(1.12) """ This doesn't work. Probably because Frida is hiding it's own memory regions from returning my_string = types.StringUTF8('Hello world!') mem = process.memory.alloc_string(my_string) f = process.memory.find(my_string) f.sleep_until_completed() assert mem.address in f """ process.quit()
def test_access_violation_amd64_main_thread(caplog): p = revenge.Process(dvb_path_x86_64, resume=True, verbose=False) caplog.set_level(logging.INFO) p.stdout("?> ") p.stdin("1\n") p.stdout("input: ") p.stdin("A" * 128 + "\n") assert isinstance(list(p.threads)[0].exceptions, list) while list(p.threads)[0].exceptions == []: pass e = list(p.threads)[0].exceptions[0] str(e) repr(e) assert isinstance(e.context.pc, types.Telescope) assert e.type == 'access-violation' assert e.context.ip.next.thing.mnemonic == "ret" assert e.context.rbp.thing == 0x4141414141414141 while "Caught exception in thread" not in caplog.text: pass p.quit()
def test_techniques_basic(): p = revenge.Process(basic_one_path, resume=False, verbose=False) list(p.techniques) # All techniques (as dict) should be subclass of Technique assert all(issubclass(x, Technique) for x in p.techniques) # Everything in dict should be a callable property assert all( callable(getattr(p.techniques, x.__name__)) for x in p.techniques) repr(p.techniques) for t in p.techniques: assert t.TYPE in Technique.TYPES # Just make sure we're populating at least assert p.techniques.InstructionTracer is not None # Try giving a memory map range range = p.memory.maps[p.memory['strlen'].address] tech = p.techniques.InstructionTracer(exec=True) tech._technique_code_range(range) p.quit()
def test_memory_cast_struct(caplog): process = revenge.Process(basic_one_path, resume=False, verbose=False, load_symbols='basic_one') basic_one_module = process.modules['basic_one'] basic_one_i8_addr = basic_one_module.symbols['i8'].address # Just need scratch space addr = basic_one_i8_addr struct = types.Struct() struct.add_member('test1', types.Int32(-5)) struct.add_member('test2', types.Int8(-12)) struct.add_member('test3', types.UInt16(16)) struct.add_member('test4', types.Pointer(4444)) struct.add_member('test5', types.Int16) # This should cause warning struct.add_member('test6', types.Pointer(5555)) mem = process.memory[addr] assert mem.cast(types.Struct) is None s = mem.cast(struct) assert s is struct assert s.memory is mem process.quit()
def test_basic_one_traceitem_manual_creation(): basic_one = revenge.Process(basic_one_path, resume=False, verbose=False, load_symbols='basic_one') module = basic_one.modules['basic_one'] for i in trace_items: ti = TraceItem(basic_one, i) str(ti) repr(ti) assert ti.type == i['type'] with pytest.raises(RevengeInvalidArgumentType): ti.type = 12 # Invalid type t = ti.type ti.type = 'blerg' assert ti.type == t str(ti) repr(ti) basic_one.quit()
def test_native_instruction_counting_basic_x86_64(): process = revenge.Process(basic_one_path, resume=False) # Grab the main thread t = next(thread for thread in process.threads if thread.state == "stopped") counters = process.techniques.NativeInstructionCounter( from_modules='basic*') counters.apply(t) counter = list(counters)[0] process.resume() # Not sure there's always going to be an exact number... # Just ballpark that we're counting something while counter.count < 500: continue counters.remove() """Run with func not yet supported on windows func = process.memory[0x00401560] # 5 instructions in func counters = process.techniques.NativeInstructionCounter(from_modules="basic*") # Call with technique func(techniques=counters) counter = list(counters)[0] while counter.count != 5: continue """ process.quit()
def test_thread_create_linux(): process = revenge.Process(basic_threads_path, resume=False, verbose=False) # Create and set an int a = process.memory.alloc(8) a.int64 = 0 # Create the cmodule function that will set this variable func = process.memory.create_c_function( "void func() {{ long *x = (long *){}; while ( 1 ) {{ *x = 1337; }} }}". format(hex(a.address))) # Kick off the thread t = process.threads.create(func.address) # Wait for it (super fast likely) while a.int64 == 0: pass assert a.int64 == 1337 # Make sure we have a pthread_id assert t.pthread_id is not None process.quit()
def test_basic_one_traceitem(): basic_one = revenge.Process(basic_one_path, resume=False, verbose=False, load_symbols='basic_one') module = basic_one.modules['basic_one'] t = basic_one.techniques.NativeInstructionTracer() t.apply() tid = list(t)[0]._tid for i in trace_items: i['tid'] = tid t._on_message({'type': 'send', 'payload': [[i]]}, None) repr(t) t2 = list(t)[0] for i in list(t2): assert isinstance(i, TraceItem) len(t2) str(t2) repr(t2) assert isinstance(t2[0], TraceItem) basic_one.quit()
def test_techniques_basic(): p = revenge.Process(basic_one_path, resume=False, verbose=False) list(p.techniques) # All techniques (as dict) should be subclass of Technique assert all(issubclass(x, Technique) for x in p.techniques) # Everything in dict should be a callable property assert all(callable(getattr(p.techniques, x.__name__)) for x in p.techniques) repr(p.techniques) for t in p.techniques: assert t.TYPE in Technique.TYPES # Just make sure we're populating at least assert p.techniques.NativeInstructionTracer is not None # Try giving a memory map range range = p.memory.maps[p.memory['strlen'].address] tech = p.techniques.NativeInstructionTracer(exec=True) tech._technique_code_range(range) # Try using two stalking techniques at once time = p.memory['time'] timeless = p.techniques.NativeTimelessTracer() trace = p.techniques.NativeInstructionTracer(exec=True) with pytest.raises(RevengeInvalidArgumentType): time(0, techniques=[timeless, trace]) p.quit()
def test_native_instruction_counting_basic_x86_64(): process = revenge.Process(basic_one_path, resume=False) counters = process.techniques.NativeInstructionCounter() counters.apply() counter = list(counters)[0] process.resume() # Just an estimate while counter.count < 1000: continue counters.remove() basic = process.modules['basic*'] func = basic.symbols['func'].memory # 5 instructions in func counters = process.techniques.NativeInstructionCounter( from_modules="basic*") # Call with technique func(techniques=counters) counter = list(counters)[0] while counter.count != 5: continue process.quit()
def test_memory_write_struct(caplog): process = revenge.Process(basic_one_path, resume=False, verbose=False, load_symbols='basic_one') basic_one_module = process.modules['basic_one'] basic_one_i8_addr = basic_one_module.symbols['i8'].address # Just need scratch space addr = basic_one_i8_addr struct = types.Struct() struct.add_member('test1', types.Int32(-5)) struct.add_member('test2', types.Int8(-12)) struct.add_member('test3', types.UInt16(16)) struct.add_member('test4', types.Pointer(4444)) struct.add_member('test5', types.Int16) # This should cause warning struct.add_member('test6', types.Pointer(5555)) mem = process.memory[addr] caplog.clear() mem.struct = struct assert "was left uninitialized" in caplog.records[0].msg caplog.clear() assert mem.int32 == -5 assert process.memory[addr + 4].int8 == -12 assert process.memory[addr + 4 + 1].uint16 == 16 assert process.memory[addr + 4 + 1 + 2].pointer == 4444 assert process.memory[addr + 4 + 1 + 2 + 8 + 2].pointer == 5555 process.quit()
def test_native_instruction_counting_basic_ia32(): process = revenge.Process(basic_one_ia32_path, resume=False) counters = process.techniques.NativeInstructionCounter( from_modules='basic*') counters.apply() counter = list(counters)[0] process.resume() # This seemed right and theoretically shouldn't change. while counter.count != 158: continue counters.remove() basic = process.modules['basic*'] func = basic.symbols['func'].memory # 5 instructions in func counters = process.techniques.NativeInstructionCounter( from_modules="basic*") # Call with technique func(techniques=counters) counter = list(counters)[0] while counter.count != 9: continue process.quit()
def test_angr_symbion_x86_64(): process = revenge.Process(basic_constraints, resume=False, verbose=False) # Just for fun, let's start part way in begin = process.memory['basic_constraints:0x11f3'] begin.breakpoint = True process.memory[process.entrypoint].breakpoint = False t = list(process.threads)[0] while not t.pc == begin.address: t = list(process.threads)[0] simgr = t.angr.simgr avoid = [ process.memory['basic_constraints:0x1241'].address, process.memory['basic_constraints:0x12a5'].address ] find = [process.memory['basic_constraints:0x1297'].address] basic = process.modules['basic_constraints'] # Checking our custom rehooking is working assert t.angr.project._sim_procedures[int( basic.symbols['plt.puts'].address)].display_name == 'puts' simgr.explore(find=find, avoid=avoid) assert len(simgr.found) == 1 assert simgr.found[0].posix.dumps(0) == b"31337 \xba\xb333337 \xba\xb3" process.quit()
def test_angr_symbion_x86_64(): process = revenge.Process(basic_constraints, resume=False, verbose=False) # Just for fun, let's start part way in begin = process.memory[0x4015af] begin.breakpoint = True process.resume() t = list(process.threads)[0] while not t.pc == begin.address: t = list(process.threads)[0] def fgets_hook(state): state.regs.r8 = 0 simgr = t.angr.simgr # Force to stdin t.angr.project.hook(0x4015e6, fgets_hook, length=0) t.angr.project.hook(0x401650, fgets_hook, length=0) avoid = [0x401613, 0x40167d] find = [0x40166f] puts = process.memory['puts'] # Checking our custom rehooking is working assert t.angr.project._sim_procedures[puts.address].display_name == 'puts' simgr.explore(find=find, avoid=avoid) assert len(simgr.found) == 1 assert simgr.found[0].posix.dumps(3) == b"31337 \xba\xb333337 \xba\xb3" process.quit()