metasm 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/BUGS +11 -0
- data/CREDITS +17 -0
- data/README +270 -0
- data/TODO +114 -0
- data/doc/code_organisation.txt +146 -0
- data/doc/const_missing.txt +16 -0
- data/doc/core_classes.txt +75 -0
- data/doc/feature_list.txt +53 -0
- data/doc/index.txt +59 -0
- data/doc/install_notes.txt +170 -0
- data/doc/style.css +3 -0
- data/doc/use_cases.txt +18 -0
- data/lib/metasm.rb +80 -0
- data/lib/metasm/arm.rb +12 -0
- data/lib/metasm/arm/debug.rb +39 -0
- data/lib/metasm/arm/decode.rb +167 -0
- data/lib/metasm/arm/encode.rb +77 -0
- data/lib/metasm/arm/main.rb +75 -0
- data/lib/metasm/arm/opcodes.rb +177 -0
- data/lib/metasm/arm/parse.rb +130 -0
- data/lib/metasm/arm/render.rb +55 -0
- data/lib/metasm/compile_c.rb +1457 -0
- data/lib/metasm/dalvik.rb +8 -0
- data/lib/metasm/dalvik/decode.rb +196 -0
- data/lib/metasm/dalvik/main.rb +60 -0
- data/lib/metasm/dalvik/opcodes.rb +366 -0
- data/lib/metasm/decode.rb +213 -0
- data/lib/metasm/decompile.rb +2659 -0
- data/lib/metasm/disassemble.rb +2068 -0
- data/lib/metasm/disassemble_api.rb +1280 -0
- data/lib/metasm/dynldr.rb +1329 -0
- data/lib/metasm/encode.rb +333 -0
- data/lib/metasm/exe_format/a_out.rb +194 -0
- data/lib/metasm/exe_format/autoexe.rb +82 -0
- data/lib/metasm/exe_format/bflt.rb +189 -0
- data/lib/metasm/exe_format/coff.rb +455 -0
- data/lib/metasm/exe_format/coff_decode.rb +901 -0
- data/lib/metasm/exe_format/coff_encode.rb +1078 -0
- data/lib/metasm/exe_format/dex.rb +457 -0
- data/lib/metasm/exe_format/dol.rb +145 -0
- data/lib/metasm/exe_format/elf.rb +923 -0
- data/lib/metasm/exe_format/elf_decode.rb +979 -0
- data/lib/metasm/exe_format/elf_encode.rb +1375 -0
- data/lib/metasm/exe_format/macho.rb +827 -0
- data/lib/metasm/exe_format/main.rb +228 -0
- data/lib/metasm/exe_format/mz.rb +164 -0
- data/lib/metasm/exe_format/nds.rb +172 -0
- data/lib/metasm/exe_format/pe.rb +437 -0
- data/lib/metasm/exe_format/serialstruct.rb +246 -0
- data/lib/metasm/exe_format/shellcode.rb +114 -0
- data/lib/metasm/exe_format/xcoff.rb +167 -0
- data/lib/metasm/gui.rb +23 -0
- data/lib/metasm/gui/cstruct.rb +373 -0
- data/lib/metasm/gui/dasm_coverage.rb +199 -0
- data/lib/metasm/gui/dasm_decomp.rb +369 -0
- data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
- data/lib/metasm/gui/dasm_graph.rb +1354 -0
- data/lib/metasm/gui/dasm_hex.rb +543 -0
- data/lib/metasm/gui/dasm_listing.rb +599 -0
- data/lib/metasm/gui/dasm_main.rb +906 -0
- data/lib/metasm/gui/dasm_opcodes.rb +291 -0
- data/lib/metasm/gui/debug.rb +1228 -0
- data/lib/metasm/gui/gtk.rb +884 -0
- data/lib/metasm/gui/qt.rb +495 -0
- data/lib/metasm/gui/win32.rb +3004 -0
- data/lib/metasm/gui/x11.rb +621 -0
- data/lib/metasm/ia32.rb +14 -0
- data/lib/metasm/ia32/compile_c.rb +1523 -0
- data/lib/metasm/ia32/debug.rb +193 -0
- data/lib/metasm/ia32/decode.rb +1167 -0
- data/lib/metasm/ia32/decompile.rb +564 -0
- data/lib/metasm/ia32/encode.rb +314 -0
- data/lib/metasm/ia32/main.rb +233 -0
- data/lib/metasm/ia32/opcodes.rb +872 -0
- data/lib/metasm/ia32/parse.rb +327 -0
- data/lib/metasm/ia32/render.rb +91 -0
- data/lib/metasm/main.rb +1193 -0
- data/lib/metasm/mips.rb +11 -0
- data/lib/metasm/mips/compile_c.rb +7 -0
- data/lib/metasm/mips/decode.rb +253 -0
- data/lib/metasm/mips/encode.rb +51 -0
- data/lib/metasm/mips/main.rb +72 -0
- data/lib/metasm/mips/opcodes.rb +443 -0
- data/lib/metasm/mips/parse.rb +51 -0
- data/lib/metasm/mips/render.rb +43 -0
- data/lib/metasm/os/gnu_exports.rb +270 -0
- data/lib/metasm/os/linux.rb +1112 -0
- data/lib/metasm/os/main.rb +1686 -0
- data/lib/metasm/os/remote.rb +527 -0
- data/lib/metasm/os/windows.rb +2027 -0
- data/lib/metasm/os/windows_exports.rb +745 -0
- data/lib/metasm/parse.rb +876 -0
- data/lib/metasm/parse_c.rb +3938 -0
- data/lib/metasm/pic16c/decode.rb +42 -0
- data/lib/metasm/pic16c/main.rb +17 -0
- data/lib/metasm/pic16c/opcodes.rb +68 -0
- data/lib/metasm/ppc.rb +11 -0
- data/lib/metasm/ppc/decode.rb +264 -0
- data/lib/metasm/ppc/decompile.rb +251 -0
- data/lib/metasm/ppc/encode.rb +51 -0
- data/lib/metasm/ppc/main.rb +129 -0
- data/lib/metasm/ppc/opcodes.rb +410 -0
- data/lib/metasm/ppc/parse.rb +52 -0
- data/lib/metasm/preprocessor.rb +1277 -0
- data/lib/metasm/render.rb +130 -0
- data/lib/metasm/sh4.rb +8 -0
- data/lib/metasm/sh4/decode.rb +336 -0
- data/lib/metasm/sh4/main.rb +292 -0
- data/lib/metasm/sh4/opcodes.rb +381 -0
- data/lib/metasm/x86_64.rb +12 -0
- data/lib/metasm/x86_64/compile_c.rb +1025 -0
- data/lib/metasm/x86_64/debug.rb +59 -0
- data/lib/metasm/x86_64/decode.rb +268 -0
- data/lib/metasm/x86_64/encode.rb +264 -0
- data/lib/metasm/x86_64/main.rb +135 -0
- data/lib/metasm/x86_64/opcodes.rb +118 -0
- data/lib/metasm/x86_64/parse.rb +68 -0
- data/misc/bottleneck.rb +61 -0
- data/misc/cheader-findpppath.rb +58 -0
- data/misc/hexdiff.rb +74 -0
- data/misc/hexdump.rb +55 -0
- data/misc/metasm-all.rb +13 -0
- data/misc/objdiff.rb +47 -0
- data/misc/objscan.rb +40 -0
- data/misc/pdfparse.rb +661 -0
- data/misc/ppc_pdf2oplist.rb +192 -0
- data/misc/tcp_proxy_hex.rb +84 -0
- data/misc/txt2html.rb +440 -0
- data/samples/a.out.rb +31 -0
- data/samples/asmsyntax.rb +77 -0
- data/samples/bindiff.rb +555 -0
- data/samples/compilation-steps.rb +49 -0
- data/samples/cparser_makestackoffset.rb +55 -0
- data/samples/dasm-backtrack.rb +38 -0
- data/samples/dasmnavig.rb +318 -0
- data/samples/dbg-apihook.rb +228 -0
- data/samples/dbghelp.rb +143 -0
- data/samples/disassemble-gui.rb +102 -0
- data/samples/disassemble.rb +133 -0
- data/samples/dump_upx.rb +95 -0
- data/samples/dynamic_ruby.rb +1929 -0
- data/samples/elf_list_needed.rb +46 -0
- data/samples/elf_listexports.rb +33 -0
- data/samples/elfencode.rb +25 -0
- data/samples/exeencode.rb +128 -0
- data/samples/factorize-headers-elfimports.rb +77 -0
- data/samples/factorize-headers-peimports.rb +109 -0
- data/samples/factorize-headers.rb +43 -0
- data/samples/gdbclient.rb +583 -0
- data/samples/generate_libsigs.rb +102 -0
- data/samples/hotfix_gtk_dbg.rb +59 -0
- data/samples/install_win_env.rb +78 -0
- data/samples/lindebug.rb +924 -0
- data/samples/linux_injectsyscall.rb +95 -0
- data/samples/machoencode.rb +31 -0
- data/samples/metasm-shell.rb +91 -0
- data/samples/pe-hook.rb +69 -0
- data/samples/pe-ia32-cpuid.rb +203 -0
- data/samples/pe-mips.rb +35 -0
- data/samples/pe-shutdown.rb +78 -0
- data/samples/pe-testrelocs.rb +51 -0
- data/samples/pe-testrsrc.rb +24 -0
- data/samples/pe_listexports.rb +31 -0
- data/samples/peencode.rb +19 -0
- data/samples/peldr.rb +494 -0
- data/samples/preprocess-flatten.rb +19 -0
- data/samples/r0trace.rb +308 -0
- data/samples/rubstop.rb +399 -0
- data/samples/scan_pt_gnu_stack.rb +54 -0
- data/samples/scanpeexports.rb +62 -0
- data/samples/shellcode-c.rb +40 -0
- data/samples/shellcode-dynlink.rb +146 -0
- data/samples/source.asm +34 -0
- data/samples/struct_offset.rb +47 -0
- data/samples/testpe.rb +32 -0
- data/samples/testraw.rb +45 -0
- data/samples/win32genloader.rb +132 -0
- data/samples/win32hooker-advanced.rb +169 -0
- data/samples/win32hooker.rb +96 -0
- data/samples/win32livedasm.rb +33 -0
- data/samples/win32remotescan.rb +133 -0
- data/samples/wintrace.rb +92 -0
- data/tests/all.rb +8 -0
- data/tests/dasm.rb +39 -0
- data/tests/dynldr.rb +35 -0
- data/tests/encodeddata.rb +132 -0
- data/tests/ia32.rb +82 -0
- data/tests/mips.rb +116 -0
- data/tests/parse_c.rb +239 -0
- data/tests/preprocessor.rb +269 -0
- data/tests/x86_64.rb +62 -0
- metadata +255 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
3
|
+
#
|
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
|
5
|
+
|
|
6
|
+
# This samples generates a binary loader that will load and patch a Win32 program in memory
|
|
7
|
+
# The patch data are read from assembly files named 'patch_<hex addr>.asm'
|
|
8
|
+
# The 1st mandatory argument is the name of the target binary to load
|
|
9
|
+
# The 2nd optional argument is the name of the loader to be generated
|
|
10
|
+
|
|
11
|
+
require 'metasm'
|
|
12
|
+
|
|
13
|
+
target = ARGV.shift
|
|
14
|
+
loader = ARGV.shift || "loader.exe"
|
|
15
|
+
|
|
16
|
+
abort "need a target binary name to load&patch" if not target
|
|
17
|
+
|
|
18
|
+
cpu = Metasm::Ia32.new
|
|
19
|
+
|
|
20
|
+
# assemble the patches, to put the binary in the C source
|
|
21
|
+
patches = Dir['patch_*.asm'].map { |f|
|
|
22
|
+
puts " [+] assembling #{f}"
|
|
23
|
+
addr = f[/patch_(.*)\.asm/, 1].to_i(16)
|
|
24
|
+
sc = Metasm::Shellcode.assemble_file(cpu, f)
|
|
25
|
+
sc.base_addr = addr
|
|
26
|
+
raw = sc.encode_string
|
|
27
|
+
[addr, raw]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# the C program skeleton
|
|
32
|
+
c_src = <<EOS
|
|
33
|
+
|
|
34
|
+
void main(void)
|
|
35
|
+
{
|
|
36
|
+
static PROCESS_INFORMATION pi;
|
|
37
|
+
static STARTUPINFO si = { .cb = sizeof(si) };
|
|
38
|
+
int i;
|
|
39
|
+
|
|
40
|
+
CreateProcess(#{target.inspect}, 0, 0, 0, 0, CREATE_SUSPENDED, 0, 0, &si, &pi);
|
|
41
|
+
|
|
42
|
+
#{patches.map { |addr, raw|
|
|
43
|
+
"WriteProcessMemory(pi.hProcess, (void*)#{'0x%X' % addr}, #{raw.inspect}, #{raw.length}, 0);"
|
|
44
|
+
}.join("\n\t")}
|
|
45
|
+
|
|
46
|
+
CloseHandle(pi.hProcess);
|
|
47
|
+
|
|
48
|
+
ResumeThread(pi.hThread);
|
|
49
|
+
CloseHandle(pi.hThread);
|
|
50
|
+
|
|
51
|
+
ExitProcess(0);
|
|
52
|
+
}
|
|
53
|
+
EOS
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# the winapi definitions (generated by factorize_headers.rb)
|
|
57
|
+
c_hdr = <<EOS
|
|
58
|
+
#define CREATE_SUSPENDED 0x00000004
|
|
59
|
+
#define CreateProcess CreateProcessA
|
|
60
|
+
|
|
61
|
+
typedef int BOOL;
|
|
62
|
+
typedef unsigned char BYTE;
|
|
63
|
+
typedef char CHAR;
|
|
64
|
+
typedef unsigned long DWORD;
|
|
65
|
+
typedef void *HANDLE;
|
|
66
|
+
typedef const void *LPCVOID;
|
|
67
|
+
typedef void *LPVOID;
|
|
68
|
+
typedef unsigned int UINT;
|
|
69
|
+
typedef unsigned long ULONG_PTR;
|
|
70
|
+
typedef unsigned short WORD;
|
|
71
|
+
|
|
72
|
+
__stdcall BOOL CloseHandle __attribute__((dllimport))(HANDLE hObject);
|
|
73
|
+
__noreturn __stdcall void ExitProcess __attribute__((dllimport))(UINT uExitCode);
|
|
74
|
+
typedef BYTE *LPBYTE;
|
|
75
|
+
typedef const CHAR *LPCSTR;
|
|
76
|
+
typedef CHAR *LPSTR;
|
|
77
|
+
__stdcall DWORD ResumeThread __attribute__((dllimport))(HANDLE hThread);
|
|
78
|
+
typedef ULONG_PTR SIZE_T;
|
|
79
|
+
|
|
80
|
+
struct _PROCESS_INFORMATION {
|
|
81
|
+
HANDLE hProcess;
|
|
82
|
+
HANDLE hThread;
|
|
83
|
+
DWORD dwProcessId;
|
|
84
|
+
DWORD dwThreadId;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
struct _SECURITY_ATTRIBUTES {
|
|
88
|
+
DWORD nLength;
|
|
89
|
+
LPVOID lpSecurityDescriptor;
|
|
90
|
+
BOOL bInheritHandle;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION;
|
|
94
|
+
typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
|
|
95
|
+
typedef struct _PROCESS_INFORMATION PROCESS_INFORMATION;
|
|
96
|
+
__stdcall BOOL WriteProcessMemory __attribute__((dllimport))(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten);
|
|
97
|
+
|
|
98
|
+
struct _STARTUPINFOA {
|
|
99
|
+
DWORD cb;
|
|
100
|
+
LPSTR lpReserved;
|
|
101
|
+
LPSTR lpDesktop;
|
|
102
|
+
LPSTR lpTitle;
|
|
103
|
+
DWORD dwX;
|
|
104
|
+
DWORD dwY;
|
|
105
|
+
DWORD dwXSize;
|
|
106
|
+
DWORD dwYSize;
|
|
107
|
+
DWORD dwXCountChars;
|
|
108
|
+
DWORD dwYCountChars;
|
|
109
|
+
DWORD dwFillAttribute;
|
|
110
|
+
DWORD dwFlags;
|
|
111
|
+
WORD wShowWindow;
|
|
112
|
+
WORD cbReserved2;
|
|
113
|
+
LPBYTE lpReserved2;
|
|
114
|
+
HANDLE hStdInput;
|
|
115
|
+
HANDLE hStdOutput;
|
|
116
|
+
HANDLE hStdError;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
typedef struct _STARTUPINFOA *LPSTARTUPINFOA;
|
|
120
|
+
typedef struct _STARTUPINFOA STARTUPINFOA;
|
|
121
|
+
|
|
122
|
+
__stdcall BOOL CreateProcessA __attribute__((dllimport))(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation);
|
|
123
|
+
typedef STARTUPINFOA STARTUPINFO;
|
|
124
|
+
EOS
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
puts c_src, ''
|
|
128
|
+
|
|
129
|
+
# compile the loader
|
|
130
|
+
Metasm::PE.compile_c(cpu, c_hdr+c_src).encode_file(loader)
|
|
131
|
+
|
|
132
|
+
puts " [+] #{loader} saved"
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
3
|
+
#
|
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
#
|
|
8
|
+
# in this exemple we will patch a process specified on the commandline (pid or part of the image name)
|
|
9
|
+
# we will retrieve the user32.dll library mapped, and hook every exported function.
|
|
10
|
+
# each hook will redirect the code flow to our shellcode, which will display the hooked function
|
|
11
|
+
# name in a messagebox.
|
|
12
|
+
# The hook is this time a real hook: we overwrite the first instructions with a jump to our code,
|
|
13
|
+
# and run those overwritten instruction again before giving control back to original function.
|
|
14
|
+
#
|
|
15
|
+
# usage: ruby w32hook-advance.rb notepad
|
|
16
|
+
# use ruby -d to impress your friends :)
|
|
17
|
+
#
|
|
18
|
+
# XXX obsolete, should replace all virtallocex etc by WinOS calls
|
|
19
|
+
|
|
20
|
+
require 'metasm'
|
|
21
|
+
|
|
22
|
+
include Metasm
|
|
23
|
+
|
|
24
|
+
# open target
|
|
25
|
+
WinOS.get_debug_privilege
|
|
26
|
+
if not pr = WinOS.find_process(ARGV.first)
|
|
27
|
+
# display list of running processes and exit
|
|
28
|
+
puts WinOS.list_processes.sort_by { |pr_| pr_.pid }
|
|
29
|
+
exit
|
|
30
|
+
end
|
|
31
|
+
raise 'cannot open target process' if not pr.handle
|
|
32
|
+
|
|
33
|
+
# the main shellcode
|
|
34
|
+
sc = Shellcode.assemble Ia32.new, <<EOS
|
|
35
|
+
main_hook:
|
|
36
|
+
pushfd ; save registers
|
|
37
|
+
pushad
|
|
38
|
+
|
|
39
|
+
mov eax, dword ptr [in_hook] ; check if we are in the hook (yay threadsafe)
|
|
40
|
+
test eax, eax
|
|
41
|
+
jnz main_hook_done
|
|
42
|
+
mov dword ptr [in_hook], 1
|
|
43
|
+
|
|
44
|
+
mov eax, dword ptr [esp+4+4*9] ; get the function name (1st argument)
|
|
45
|
+
|
|
46
|
+
push 0
|
|
47
|
+
push eax
|
|
48
|
+
push eax
|
|
49
|
+
push 0
|
|
50
|
+
call messageboxw
|
|
51
|
+
|
|
52
|
+
mov dword ptr [in_hook], 0
|
|
53
|
+
main_hook_done:
|
|
54
|
+
popad
|
|
55
|
+
popfd
|
|
56
|
+
ret 4
|
|
57
|
+
|
|
58
|
+
.align 4
|
|
59
|
+
in_hook dd 0 ; l33t mutex
|
|
60
|
+
EOS
|
|
61
|
+
|
|
62
|
+
# this is where we store every function hook
|
|
63
|
+
hooks = {}
|
|
64
|
+
prepare_hook = lambda { |mpe, base, export|
|
|
65
|
+
hooklabel = sc.new_label('hook')
|
|
66
|
+
namelabel = sc.new_label('name')
|
|
67
|
+
|
|
68
|
+
# this will overwrite the function entrypoint
|
|
69
|
+
target = base + export.target
|
|
70
|
+
hooks[target] = Shellcode.new(sc.cpu).share_namespace(sc).parse("jmp #{hooklabel}").assemble.encoded
|
|
71
|
+
|
|
72
|
+
# backup the overwritten instructions
|
|
73
|
+
# retrieve instructions until their length is >= our hook length
|
|
74
|
+
mpe.encoded.ptr = export.target
|
|
75
|
+
sz = 0
|
|
76
|
+
overwritten = []
|
|
77
|
+
while sz < hooks[target].length
|
|
78
|
+
di = sc.cpu.decode_instruction mpe.encoded, target
|
|
79
|
+
if not di or not di.opcode or not di.instruction
|
|
80
|
+
puts "W: unknown instruction in #{export.name} !"
|
|
81
|
+
break
|
|
82
|
+
end
|
|
83
|
+
overwritten << di.instruction
|
|
84
|
+
sz += di.bin_length
|
|
85
|
+
end
|
|
86
|
+
puts "overwritten at #{export.name}:", overwritten, '' if $DEBUG
|
|
87
|
+
resumeaddr = target + sz
|
|
88
|
+
|
|
89
|
+
# append the call-specific shellcode to the main hook code
|
|
90
|
+
sc.cursource << Label.new(hooklabel)
|
|
91
|
+
sc.parse <<EOS
|
|
92
|
+
push #{namelabel}
|
|
93
|
+
call main_hook ; log the call
|
|
94
|
+
; rerun the overwritten instructions
|
|
95
|
+
#{overwritten.join("\n")}
|
|
96
|
+
jmp #{resumeaddr} ; get back to original code flow
|
|
97
|
+
EOS
|
|
98
|
+
sc.cursource << Label.new(namelabel)
|
|
99
|
+
sc.parse "dw #{export.name.inspect}, 0"
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
msgboxw = nil
|
|
103
|
+
# decode interesting libraries from address space
|
|
104
|
+
pr.modules[1..-1].each { |m|
|
|
105
|
+
# search for messageboxw
|
|
106
|
+
if m.path =~ /user32/i
|
|
107
|
+
mpe = LoadedPE.load pr.memory[m.addr, 0x1000000]
|
|
108
|
+
mpe.decode_header
|
|
109
|
+
mpe.decode_exports
|
|
110
|
+
mpe.export.exports.each { |e| msgboxw = m.addr + mpe.label_rva(e.target) if e.name == 'MessageBoxW' }
|
|
111
|
+
end
|
|
112
|
+
# prepare hooks
|
|
113
|
+
next if m.path !~ /user32/i # filter interesting libraries
|
|
114
|
+
puts "handling #{File.basename m.path}" if $VERBOSE
|
|
115
|
+
|
|
116
|
+
if not mpe
|
|
117
|
+
mpe = LoadedPE.load pr.memory[m.addr, 0x1000000]
|
|
118
|
+
mpe.decode_header
|
|
119
|
+
mpe.decode_exports
|
|
120
|
+
end
|
|
121
|
+
next if not mpe.export or not mpe.export.exports
|
|
122
|
+
|
|
123
|
+
# discard exported data
|
|
124
|
+
text = mpe.sections.find { |s| s.name == '.text' }
|
|
125
|
+
mpe.export.exports.each { |e|
|
|
126
|
+
next if not e.target or not e.name
|
|
127
|
+
next if e.name =~ /(?:Translate|Get|Dispatch)Message|CallNextHookEx|TranslateAccelerator/
|
|
128
|
+
|
|
129
|
+
# ensure we have an offset and not a label name
|
|
130
|
+
e.target = mpe.label_rva(e.target)
|
|
131
|
+
|
|
132
|
+
# ensure the exported thing is in the .text section
|
|
133
|
+
next if e.target < text.virtaddr or e.target >= text.virtaddr + text.virtsize
|
|
134
|
+
|
|
135
|
+
# prepare the hook
|
|
136
|
+
prepare_hook[mpe, m.addr, e]
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
raise 'Did not find MessageBoxW !' if not msgboxw
|
|
141
|
+
|
|
142
|
+
puts 'linking...'
|
|
143
|
+
sc.assemble
|
|
144
|
+
puts 'done'
|
|
145
|
+
|
|
146
|
+
# allocate memory for our code
|
|
147
|
+
raise 'remote allocation failed' if not injected_addr = WinAPI.virtualallocex(pr.handle, 0, sc.encoded.length, WinAPI::MEM_COMMIT|WinAPI::MEM_RESERVE, WinAPI::PAGE_EXECUTE_READWRITE)
|
|
148
|
+
|
|
149
|
+
puts "Injecting hooks at #{'%x' % injected_addr}"
|
|
150
|
+
|
|
151
|
+
# fixup & inject our code
|
|
152
|
+
binding = { 'messageboxw' => msgboxw }
|
|
153
|
+
hooks.each { |addr, edata| binding.update edata.binding(addr) }
|
|
154
|
+
binding.update sc.encoded.binding(injected_addr)
|
|
155
|
+
|
|
156
|
+
# fixup
|
|
157
|
+
sc.encoded.fixup(binding)
|
|
158
|
+
# inject
|
|
159
|
+
pr.memory[injected_addr, sc.encoded.data.length] = sc.encoded.data
|
|
160
|
+
|
|
161
|
+
# now overwrite entry points
|
|
162
|
+
hooks.each { |addr, edata|
|
|
163
|
+
edata.fixup(binding)
|
|
164
|
+
pr.memory[addr, edata.data.length] = edata.data
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
puts 'done'
|
|
168
|
+
|
|
169
|
+
WinAPI.closehandle(pr.handle)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
3
|
+
#
|
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
#
|
|
8
|
+
# in this exemple we will patch a process specified on the commandline (pid or part of image name)
|
|
9
|
+
# the IAT entry matching /WriteFile/ will be replaced by a pointer to a malicious code we inject,
|
|
10
|
+
# which calls back the original function.
|
|
11
|
+
# Our shellcode will display the first bytes of the data to be written, using MessageBoxW (whose
|
|
12
|
+
# pointer is also retrieved from the target IAT)
|
|
13
|
+
#
|
|
14
|
+
# usage: ruby w32hook.rb notepad ; then go in notepad, type some words and save to a file
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
require 'metasm'
|
|
18
|
+
|
|
19
|
+
include Metasm
|
|
20
|
+
|
|
21
|
+
# open target
|
|
22
|
+
WinOS.get_debug_privilege
|
|
23
|
+
if not pr = WinOS.find_process(ARGV.first)
|
|
24
|
+
# display list of running processes if no target found
|
|
25
|
+
puts WinOS.list_processes.sort_by { |pr_| pr_.pid }
|
|
26
|
+
exit
|
|
27
|
+
end
|
|
28
|
+
raise 'cannot open target process' if not pr.handle
|
|
29
|
+
|
|
30
|
+
# read the target PE structure
|
|
31
|
+
pe = LoadedPE.load pr.memory[pr.modules[0].addr, 0x1000000]
|
|
32
|
+
pe.decode_header
|
|
33
|
+
pe.decode_imports
|
|
34
|
+
|
|
35
|
+
# find iat entries
|
|
36
|
+
target = nil
|
|
37
|
+
target_p = nil
|
|
38
|
+
msgboxw_p = nil
|
|
39
|
+
iat_entry_len = pe.encode_xword(0).length # 64bits portable ! (shellcode probably won't work)
|
|
40
|
+
pe.imports.each { |id|
|
|
41
|
+
id.imports.each_with_index { |i, idx|
|
|
42
|
+
case i.name
|
|
43
|
+
when 'MessageBoxW'
|
|
44
|
+
msgboxw_p = pr.modules[0].addr + id.iat_p + iat_entry_len * idx
|
|
45
|
+
when /WriteFile/
|
|
46
|
+
target_p = pr.modules[0].addr + id.iat_p + iat_entry_len * idx
|
|
47
|
+
target = id.iat[idx]
|
|
48
|
+
end
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
raise "iat entries not found" if not target or not msgboxw_p
|
|
52
|
+
|
|
53
|
+
# here we write our shellcode (no need to code position-independant)
|
|
54
|
+
sc = Shellcode.assemble(Ia32.new, <<EOS)
|
|
55
|
+
pushad
|
|
56
|
+
mov esi, dword ptr [esp+20h+8] ; 2nd arg = buffer
|
|
57
|
+
mov edi, message
|
|
58
|
+
mov ecx, 19
|
|
59
|
+
xor eax, eax
|
|
60
|
+
copy_again:
|
|
61
|
+
lodsb
|
|
62
|
+
stosw
|
|
63
|
+
loop copy_again
|
|
64
|
+
|
|
65
|
+
push 0
|
|
66
|
+
push title
|
|
67
|
+
push message
|
|
68
|
+
push 0
|
|
69
|
+
call [msgboxw]
|
|
70
|
+
popad
|
|
71
|
+
jmp target
|
|
72
|
+
|
|
73
|
+
.align 4
|
|
74
|
+
; strings to display
|
|
75
|
+
message dw 20 dup(?)
|
|
76
|
+
title dw 'I see what you did there...', 0
|
|
77
|
+
EOS
|
|
78
|
+
|
|
79
|
+
# alloc some space in the remote process to put our shellcode
|
|
80
|
+
raise 'remote allocation failed' if not injected = WinAPI.virtualallocex(pr.handle, 0, sc.encoded.length, WinAPI::MEM_COMMIT|WinAPI::MEM_RESERVE, WinAPI::PAGE_EXECUTE_READWRITE)
|
|
81
|
+
puts "injected malicous code at %x" % injected
|
|
82
|
+
|
|
83
|
+
# fixup the shellcode with its known base address, and with the addresses it will need from the IAT
|
|
84
|
+
sc.base_addr = injected
|
|
85
|
+
sc.encoded.fixup! 'msgboxw' => msgboxw_p, 'target' => target
|
|
86
|
+
raw = sc.encode_string
|
|
87
|
+
|
|
88
|
+
# inject the shellcode
|
|
89
|
+
pr.memory[injected, raw.length] = raw
|
|
90
|
+
|
|
91
|
+
# rewrite iat entry
|
|
92
|
+
iat_h = pe.encode_xword(injected).data
|
|
93
|
+
pr.memory[target_p, iat_h.length] = iat_h
|
|
94
|
+
|
|
95
|
+
# done
|
|
96
|
+
WinAPI.closehandle(pr.handle)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
3
|
+
#
|
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
require 'metasm'
|
|
8
|
+
Metasm.require 'samples/metasm-shell'
|
|
9
|
+
|
|
10
|
+
include Metasm
|
|
11
|
+
|
|
12
|
+
# open target
|
|
13
|
+
WinOS.get_debug_privilege
|
|
14
|
+
if not pr = WinOS.find_process(ARGV.first)
|
|
15
|
+
puts WinOS.list_processes.sort_by { |pr_| pr_.pid }
|
|
16
|
+
exit
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# retrieve the pe load address
|
|
20
|
+
baseaddr = pr.modules[0].addr
|
|
21
|
+
|
|
22
|
+
# decode the COFF headers
|
|
23
|
+
pe = Metasm::LoadedPE.load pr.memory[baseaddr, 0x100000]
|
|
24
|
+
pe.decode_header
|
|
25
|
+
|
|
26
|
+
# get the entrypoint address
|
|
27
|
+
eip = baseaddr + pe.label_rva(pe.optheader.entrypoint)
|
|
28
|
+
|
|
29
|
+
# use degraded disasm mode: assume all calls will return
|
|
30
|
+
String.cpu.opcode_list.each { |op| op.props.delete :stopexec if op.props[:saveip] }
|
|
31
|
+
|
|
32
|
+
# disassemble & dump opcodes
|
|
33
|
+
puts pe.encoded[pe.optheader.entrypoint, 0x100].data.decode(eip)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
3
|
+
#
|
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
|
5
|
+
|
|
6
|
+
# here we compile and inject a C stub into a running process
|
|
7
|
+
# the sample stub will scan the whole process memory for a 32bit value (aligned)
|
|
8
|
+
# and report the results using MessageBox
|
|
9
|
+
#
|
|
10
|
+
# This shows how to mix C and asm, and a few WinOS functions.
|
|
11
|
+
|
|
12
|
+
require 'metasm'
|
|
13
|
+
|
|
14
|
+
include Metasm
|
|
15
|
+
|
|
16
|
+
abort 'usage: scan <targetprocess> <value>' if ARGV.length != 2
|
|
17
|
+
targetproc = ARGV.shift
|
|
18
|
+
searchval = Integer(ARGV.shift)
|
|
19
|
+
|
|
20
|
+
raise 'cannot find target' if not target = WinOS.find_process(targetproc)
|
|
21
|
+
|
|
22
|
+
sc = Shellcode.compile_c(Ia32.new, <<'EOC')
|
|
23
|
+
asm {
|
|
24
|
+
sc_start:
|
|
25
|
+
xor edi, edi
|
|
26
|
+
mov ebp, edi // ebp = match count
|
|
27
|
+
push fs:[edi] // backup UEH
|
|
28
|
+
call setup_ueh
|
|
29
|
+
|
|
30
|
+
// our UEH: edi += 4k, check for end of addrspace
|
|
31
|
+
ueh:
|
|
32
|
+
mov eax, [esp+0ch]
|
|
33
|
+
add eax, 7ch // optimize code size
|
|
34
|
+
add dword ptr [eax+21h], 10h // ctx[edi] += 4k
|
|
35
|
+
cmp word ptr [eax+22h], -1
|
|
36
|
+
jb ueh_retloc
|
|
37
|
+
|
|
38
|
+
call ueh_endloop // ctx[eip] = &jmp walk_loop_end
|
|
39
|
+
jmp walk_loop_end
|
|
40
|
+
ueh_endloop:
|
|
41
|
+
pop dword ptr [eax+3ch]
|
|
42
|
+
|
|
43
|
+
ueh_retloc:
|
|
44
|
+
xor eax, eax
|
|
45
|
+
ret // UEH: return(CONTINUE)
|
|
46
|
+
// end of UEH
|
|
47
|
+
|
|
48
|
+
setup_ueh:
|
|
49
|
+
push -1
|
|
50
|
+
mov fs:[edi], esp
|
|
51
|
+
mov eax, SEARCHEDVALUE
|
|
52
|
+
|
|
53
|
+
walk_loop_next:
|
|
54
|
+
cmp edi, 0xffff_fff0
|
|
55
|
+
jae walk_loop_end
|
|
56
|
+
scasd
|
|
57
|
+
jnz walk_loop_next
|
|
58
|
+
|
|
59
|
+
found:
|
|
60
|
+
call found_value
|
|
61
|
+
|
|
62
|
+
jmp walk_loop_next
|
|
63
|
+
|
|
64
|
+
walk_loop_end:
|
|
65
|
+
pop eax
|
|
66
|
+
pop ebx
|
|
67
|
+
inc eax
|
|
68
|
+
pop dword ptr fs:[eax]
|
|
69
|
+
|
|
70
|
+
call metasm_intern_geteip
|
|
71
|
+
mov [eax+matchcount-metasm_intern_geteip], ebp
|
|
72
|
+
|
|
73
|
+
call scan_finished
|
|
74
|
+
|
|
75
|
+
// virtualfree shellcode & exitthread
|
|
76
|
+
push 8000h // type = MEM_RELEASE
|
|
77
|
+
push 0 // size = 0
|
|
78
|
+
call end_getaddr // addr = sc_start
|
|
79
|
+
end_getaddr:
|
|
80
|
+
add dword ptr [esp], end_getaddr - sc_start
|
|
81
|
+
push ExitThread
|
|
82
|
+
jmp VirtualFree
|
|
83
|
+
// end of main
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
// found new match at edi
|
|
87
|
+
found_value:
|
|
88
|
+
push eax
|
|
89
|
+
cmp ebp, 1024
|
|
90
|
+
jae skipsave
|
|
91
|
+
call metasm_intern_geteip
|
|
92
|
+
add eax, table - metasm_intern_geteip
|
|
93
|
+
sub edi, 4
|
|
94
|
+
mov dword ptr [eax+4*ebp], edi
|
|
95
|
+
add edi, 4
|
|
96
|
+
inc ebp
|
|
97
|
+
skipsave:
|
|
98
|
+
pop eax
|
|
99
|
+
mov dword ptr [esp-4], 0 // hide the searched value from our stack
|
|
100
|
+
ret
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
__declspec(stdcall) void MessageBoxA(int, char*, char*, int);
|
|
105
|
+
int wsprintfA(char *buf, char *fmt, ...);
|
|
106
|
+
|
|
107
|
+
unsigned long table[1024];
|
|
108
|
+
unsigned long matchcount;
|
|
109
|
+
char outbuf[4096];
|
|
110
|
+
|
|
111
|
+
void scan_finished(void)
|
|
112
|
+
{
|
|
113
|
+
int off = 0;
|
|
114
|
+
int i;
|
|
115
|
+
|
|
116
|
+
off += wsprintfA(outbuf+off, "Found %d matches: ", matchcount);
|
|
117
|
+
for (i=0 ; i<matchcount ; i++) {
|
|
118
|
+
if (off > sizeof(outbuf)-20)
|
|
119
|
+
break;
|
|
120
|
+
off += wsprintfA(outbuf+off, "%X, ", table[i]);
|
|
121
|
+
}
|
|
122
|
+
outbuf[off-2] = '.';
|
|
123
|
+
outbuf[off-1] = 0;
|
|
124
|
+
|
|
125
|
+
MessageBoxA(0, outbuf, "search finished", 0);
|
|
126
|
+
}
|
|
127
|
+
EOC
|
|
128
|
+
|
|
129
|
+
sc = sc.encoded
|
|
130
|
+
|
|
131
|
+
sc.fixup! 'SEARCHEDVALUE' => searchval
|
|
132
|
+
|
|
133
|
+
WinOS.inject_run_shellcode(target, sc)
|