metasm 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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)