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.
Files changed (192) hide show
  1. data/BUGS +11 -0
  2. data/CREDITS +17 -0
  3. data/README +270 -0
  4. data/TODO +114 -0
  5. data/doc/code_organisation.txt +146 -0
  6. data/doc/const_missing.txt +16 -0
  7. data/doc/core_classes.txt +75 -0
  8. data/doc/feature_list.txt +53 -0
  9. data/doc/index.txt +59 -0
  10. data/doc/install_notes.txt +170 -0
  11. data/doc/style.css +3 -0
  12. data/doc/use_cases.txt +18 -0
  13. data/lib/metasm.rb +80 -0
  14. data/lib/metasm/arm.rb +12 -0
  15. data/lib/metasm/arm/debug.rb +39 -0
  16. data/lib/metasm/arm/decode.rb +167 -0
  17. data/lib/metasm/arm/encode.rb +77 -0
  18. data/lib/metasm/arm/main.rb +75 -0
  19. data/lib/metasm/arm/opcodes.rb +177 -0
  20. data/lib/metasm/arm/parse.rb +130 -0
  21. data/lib/metasm/arm/render.rb +55 -0
  22. data/lib/metasm/compile_c.rb +1457 -0
  23. data/lib/metasm/dalvik.rb +8 -0
  24. data/lib/metasm/dalvik/decode.rb +196 -0
  25. data/lib/metasm/dalvik/main.rb +60 -0
  26. data/lib/metasm/dalvik/opcodes.rb +366 -0
  27. data/lib/metasm/decode.rb +213 -0
  28. data/lib/metasm/decompile.rb +2659 -0
  29. data/lib/metasm/disassemble.rb +2068 -0
  30. data/lib/metasm/disassemble_api.rb +1280 -0
  31. data/lib/metasm/dynldr.rb +1329 -0
  32. data/lib/metasm/encode.rb +333 -0
  33. data/lib/metasm/exe_format/a_out.rb +194 -0
  34. data/lib/metasm/exe_format/autoexe.rb +82 -0
  35. data/lib/metasm/exe_format/bflt.rb +189 -0
  36. data/lib/metasm/exe_format/coff.rb +455 -0
  37. data/lib/metasm/exe_format/coff_decode.rb +901 -0
  38. data/lib/metasm/exe_format/coff_encode.rb +1078 -0
  39. data/lib/metasm/exe_format/dex.rb +457 -0
  40. data/lib/metasm/exe_format/dol.rb +145 -0
  41. data/lib/metasm/exe_format/elf.rb +923 -0
  42. data/lib/metasm/exe_format/elf_decode.rb +979 -0
  43. data/lib/metasm/exe_format/elf_encode.rb +1375 -0
  44. data/lib/metasm/exe_format/macho.rb +827 -0
  45. data/lib/metasm/exe_format/main.rb +228 -0
  46. data/lib/metasm/exe_format/mz.rb +164 -0
  47. data/lib/metasm/exe_format/nds.rb +172 -0
  48. data/lib/metasm/exe_format/pe.rb +437 -0
  49. data/lib/metasm/exe_format/serialstruct.rb +246 -0
  50. data/lib/metasm/exe_format/shellcode.rb +114 -0
  51. data/lib/metasm/exe_format/xcoff.rb +167 -0
  52. data/lib/metasm/gui.rb +23 -0
  53. data/lib/metasm/gui/cstruct.rb +373 -0
  54. data/lib/metasm/gui/dasm_coverage.rb +199 -0
  55. data/lib/metasm/gui/dasm_decomp.rb +369 -0
  56. data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
  57. data/lib/metasm/gui/dasm_graph.rb +1354 -0
  58. data/lib/metasm/gui/dasm_hex.rb +543 -0
  59. data/lib/metasm/gui/dasm_listing.rb +599 -0
  60. data/lib/metasm/gui/dasm_main.rb +906 -0
  61. data/lib/metasm/gui/dasm_opcodes.rb +291 -0
  62. data/lib/metasm/gui/debug.rb +1228 -0
  63. data/lib/metasm/gui/gtk.rb +884 -0
  64. data/lib/metasm/gui/qt.rb +495 -0
  65. data/lib/metasm/gui/win32.rb +3004 -0
  66. data/lib/metasm/gui/x11.rb +621 -0
  67. data/lib/metasm/ia32.rb +14 -0
  68. data/lib/metasm/ia32/compile_c.rb +1523 -0
  69. data/lib/metasm/ia32/debug.rb +193 -0
  70. data/lib/metasm/ia32/decode.rb +1167 -0
  71. data/lib/metasm/ia32/decompile.rb +564 -0
  72. data/lib/metasm/ia32/encode.rb +314 -0
  73. data/lib/metasm/ia32/main.rb +233 -0
  74. data/lib/metasm/ia32/opcodes.rb +872 -0
  75. data/lib/metasm/ia32/parse.rb +327 -0
  76. data/lib/metasm/ia32/render.rb +91 -0
  77. data/lib/metasm/main.rb +1193 -0
  78. data/lib/metasm/mips.rb +11 -0
  79. data/lib/metasm/mips/compile_c.rb +7 -0
  80. data/lib/metasm/mips/decode.rb +253 -0
  81. data/lib/metasm/mips/encode.rb +51 -0
  82. data/lib/metasm/mips/main.rb +72 -0
  83. data/lib/metasm/mips/opcodes.rb +443 -0
  84. data/lib/metasm/mips/parse.rb +51 -0
  85. data/lib/metasm/mips/render.rb +43 -0
  86. data/lib/metasm/os/gnu_exports.rb +270 -0
  87. data/lib/metasm/os/linux.rb +1112 -0
  88. data/lib/metasm/os/main.rb +1686 -0
  89. data/lib/metasm/os/remote.rb +527 -0
  90. data/lib/metasm/os/windows.rb +2027 -0
  91. data/lib/metasm/os/windows_exports.rb +745 -0
  92. data/lib/metasm/parse.rb +876 -0
  93. data/lib/metasm/parse_c.rb +3938 -0
  94. data/lib/metasm/pic16c/decode.rb +42 -0
  95. data/lib/metasm/pic16c/main.rb +17 -0
  96. data/lib/metasm/pic16c/opcodes.rb +68 -0
  97. data/lib/metasm/ppc.rb +11 -0
  98. data/lib/metasm/ppc/decode.rb +264 -0
  99. data/lib/metasm/ppc/decompile.rb +251 -0
  100. data/lib/metasm/ppc/encode.rb +51 -0
  101. data/lib/metasm/ppc/main.rb +129 -0
  102. data/lib/metasm/ppc/opcodes.rb +410 -0
  103. data/lib/metasm/ppc/parse.rb +52 -0
  104. data/lib/metasm/preprocessor.rb +1277 -0
  105. data/lib/metasm/render.rb +130 -0
  106. data/lib/metasm/sh4.rb +8 -0
  107. data/lib/metasm/sh4/decode.rb +336 -0
  108. data/lib/metasm/sh4/main.rb +292 -0
  109. data/lib/metasm/sh4/opcodes.rb +381 -0
  110. data/lib/metasm/x86_64.rb +12 -0
  111. data/lib/metasm/x86_64/compile_c.rb +1025 -0
  112. data/lib/metasm/x86_64/debug.rb +59 -0
  113. data/lib/metasm/x86_64/decode.rb +268 -0
  114. data/lib/metasm/x86_64/encode.rb +264 -0
  115. data/lib/metasm/x86_64/main.rb +135 -0
  116. data/lib/metasm/x86_64/opcodes.rb +118 -0
  117. data/lib/metasm/x86_64/parse.rb +68 -0
  118. data/misc/bottleneck.rb +61 -0
  119. data/misc/cheader-findpppath.rb +58 -0
  120. data/misc/hexdiff.rb +74 -0
  121. data/misc/hexdump.rb +55 -0
  122. data/misc/metasm-all.rb +13 -0
  123. data/misc/objdiff.rb +47 -0
  124. data/misc/objscan.rb +40 -0
  125. data/misc/pdfparse.rb +661 -0
  126. data/misc/ppc_pdf2oplist.rb +192 -0
  127. data/misc/tcp_proxy_hex.rb +84 -0
  128. data/misc/txt2html.rb +440 -0
  129. data/samples/a.out.rb +31 -0
  130. data/samples/asmsyntax.rb +77 -0
  131. data/samples/bindiff.rb +555 -0
  132. data/samples/compilation-steps.rb +49 -0
  133. data/samples/cparser_makestackoffset.rb +55 -0
  134. data/samples/dasm-backtrack.rb +38 -0
  135. data/samples/dasmnavig.rb +318 -0
  136. data/samples/dbg-apihook.rb +228 -0
  137. data/samples/dbghelp.rb +143 -0
  138. data/samples/disassemble-gui.rb +102 -0
  139. data/samples/disassemble.rb +133 -0
  140. data/samples/dump_upx.rb +95 -0
  141. data/samples/dynamic_ruby.rb +1929 -0
  142. data/samples/elf_list_needed.rb +46 -0
  143. data/samples/elf_listexports.rb +33 -0
  144. data/samples/elfencode.rb +25 -0
  145. data/samples/exeencode.rb +128 -0
  146. data/samples/factorize-headers-elfimports.rb +77 -0
  147. data/samples/factorize-headers-peimports.rb +109 -0
  148. data/samples/factorize-headers.rb +43 -0
  149. data/samples/gdbclient.rb +583 -0
  150. data/samples/generate_libsigs.rb +102 -0
  151. data/samples/hotfix_gtk_dbg.rb +59 -0
  152. data/samples/install_win_env.rb +78 -0
  153. data/samples/lindebug.rb +924 -0
  154. data/samples/linux_injectsyscall.rb +95 -0
  155. data/samples/machoencode.rb +31 -0
  156. data/samples/metasm-shell.rb +91 -0
  157. data/samples/pe-hook.rb +69 -0
  158. data/samples/pe-ia32-cpuid.rb +203 -0
  159. data/samples/pe-mips.rb +35 -0
  160. data/samples/pe-shutdown.rb +78 -0
  161. data/samples/pe-testrelocs.rb +51 -0
  162. data/samples/pe-testrsrc.rb +24 -0
  163. data/samples/pe_listexports.rb +31 -0
  164. data/samples/peencode.rb +19 -0
  165. data/samples/peldr.rb +494 -0
  166. data/samples/preprocess-flatten.rb +19 -0
  167. data/samples/r0trace.rb +308 -0
  168. data/samples/rubstop.rb +399 -0
  169. data/samples/scan_pt_gnu_stack.rb +54 -0
  170. data/samples/scanpeexports.rb +62 -0
  171. data/samples/shellcode-c.rb +40 -0
  172. data/samples/shellcode-dynlink.rb +146 -0
  173. data/samples/source.asm +34 -0
  174. data/samples/struct_offset.rb +47 -0
  175. data/samples/testpe.rb +32 -0
  176. data/samples/testraw.rb +45 -0
  177. data/samples/win32genloader.rb +132 -0
  178. data/samples/win32hooker-advanced.rb +169 -0
  179. data/samples/win32hooker.rb +96 -0
  180. data/samples/win32livedasm.rb +33 -0
  181. data/samples/win32remotescan.rb +133 -0
  182. data/samples/wintrace.rb +92 -0
  183. data/tests/all.rb +8 -0
  184. data/tests/dasm.rb +39 -0
  185. data/tests/dynldr.rb +35 -0
  186. data/tests/encodeddata.rb +132 -0
  187. data/tests/ia32.rb +82 -0
  188. data/tests/mips.rb +116 -0
  189. data/tests/parse_c.rb +239 -0
  190. data/tests/preprocessor.rb +269 -0
  191. data/tests/x86_64.rb +62 -0
  192. 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)