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,19 @@
1
+ #!/usr/bin/env ruby
2
+ # This file is part of Metasm, the Ruby assembly manipulation suite
3
+ # Copyright (C) 2006-2009 Yoann GUILLOT
4
+ #
5
+ # Licence is LGPL, see LICENCE in the top-level directory
6
+
7
+ #
8
+ # this file takes preprocessor files as arguments
9
+ # it preprocesses their content and dump the result to stdout
10
+ # it also dumps all macro definitions
11
+ #
12
+
13
+ require 'metasm/preprocessor'
14
+
15
+ p = Metasm::Preprocessor.new
16
+ p.feed(ARGF.read)
17
+ raw = p.dump
18
+ puts p.dump_macros(p.definition.keys, false)
19
+ puts raw
@@ -0,0 +1,308 @@
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 creates/uses a ring0 driver for tracing a program
7
+ # x86/windows/singlecore only
8
+ # the scripts allows interacting with the driver
9
+ # you still have to set the target thread in singlestep mode (eg using Debugger)
10
+
11
+ # How does it work:
12
+ # the driver hooks the IDT int1/int0f (NOT SMP PROOF)
13
+ # on int1, it logs eip in a memory buffer
14
+ # on int0f, it returns the memory buffer (memcopy to mem pointed by eax)
15
+ # the buffer 1st dword is the number of used dwords in the buffer (0 = empty)
16
+ # on overflow, eips are lost
17
+
18
+ require 'metasm'
19
+ include Metasm
20
+
21
+ $drv = 'r0trace.sys'
22
+ # size of the eip buffer (in dwords)
23
+ TRACE_BUF_SZ = 4*1024*1024-4
24
+ if not File.exist? $drv
25
+ PE.assemble(Ia32.new, <<EOS).encode_file($drv, 'kmod')
26
+ #define bufsz #{TRACE_BUF_SZ}
27
+
28
+ .data
29
+ oldi1 dd 0,0
30
+ oldi15 dd 0,0
31
+ buf dd bufsz dup(?)
32
+
33
+ .text
34
+ .entrypoint
35
+ mov eax, [esp+4]
36
+ mov dword ptr [eax+0x34], unload // drv->DriverUnload
37
+ call setup_idt
38
+ xor eax, eax
39
+ mov [buf], eax // buf used size
40
+ ret
41
+
42
+ unload:
43
+ // XXX smp
44
+ call get_idt
45
+ push [oldi1+4]
46
+ push [oldi1]
47
+ pop [eax+8*1]
48
+ pop [eax+8*1+4]
49
+ push [oldi15+4]
50
+ push [oldi15]
51
+ pop [eax+8*15]
52
+ pop [eax+8*15+4]
53
+ xor eax, eax
54
+ ret
55
+
56
+ setup_idt:
57
+ // XXX smp
58
+ call get_idt
59
+ push [eax+8*1]
60
+ push [eax+8*1+4]
61
+ pop [oldi1+4]
62
+ pop [oldi1]
63
+ push [eax+8*15]
64
+ push [eax+8*15+4]
65
+ pop [oldi15+4]
66
+ pop [oldi15]
67
+
68
+ mov ecx, i1hook
69
+ mov [eax+8*1], ecx
70
+ mov [eax+8*1+4], ecx
71
+ mov ecx, cs
72
+ mov [eax+8*1+2], cx
73
+ mov word ptr [eax+8*1+4], (15 + (3<<5) + (1<<7)) << 8 // call gate
74
+
75
+ mov ecx, i15hook
76
+ mov [eax+8*15], ecx
77
+ mov [eax+8*15+4], ecx
78
+ mov ecx, cs
79
+ mov [eax+8*15+2], cx
80
+ mov word ptr [eax+8*15+4], (15 + (3<<5) + (1<<7)) << 8 // call gate
81
+ ret
82
+
83
+ get_idt:
84
+ sub esp, 8
85
+ sidt [esp]
86
+ mov eax, [esp+2]
87
+ add esp, 8
88
+ ret
89
+
90
+ i1hook:
91
+ push eax
92
+ mov eax, buf
93
+ cmp [eax], bufsz
94
+ jae 1f
95
+ inc [eax]
96
+ add eax, [eax]
97
+ push [esp+4]
98
+ pop [eax]
99
+ 1:
100
+ pop eax
101
+ iret
102
+
103
+ i15hook:
104
+ push esi
105
+ push edi
106
+ push ecx
107
+ mov esi, buf
108
+ mov edi, eax
109
+ mov ecx, [esi]
110
+ inc ecx
111
+ rep movsd
112
+ mov dword ptr [buf], 0
113
+ pop ecx
114
+ pop edi
115
+ pop esi
116
+ iret
117
+ EOS
118
+ end
119
+
120
+ DynLdr.new_api_c <<EOS
121
+ typedef int BOOL;
122
+ typedef char CHAR;
123
+ typedef unsigned long DWORD;
124
+ typedef const CHAR *LPCSTR;
125
+ typedef DWORD *LPDWORD;
126
+ typedef void *HANDLE;
127
+ struct SC_HANDLE__ { int unused; };
128
+ struct _SERVICE_STATUS {
129
+ DWORD dwServiceType;
130
+ DWORD dwCurrentState;
131
+ DWORD dwControlsAccepted;
132
+ DWORD dwWin32ExitCode;
133
+ DWORD dwServiceSpecificExitCode;
134
+ DWORD dwCheckPoint;
135
+ DWORD dwWaitHint;
136
+ };
137
+ typedef struct SC_HANDLE__ *SC_HANDLE;
138
+ typedef struct _SERVICE_STATUS *LPSERVICE_STATUS;
139
+
140
+ __stdcall BOOL CloseServiceHandle(SC_HANDLE hSCObject __attribute__((in)));
141
+ __stdcall SC_HANDLE CreateServiceA(SC_HANDLE hSCManager __attribute__((in)), LPCSTR lpServiceName __attribute__((in)), LPCSTR lpDisplayName __attribute__((in)), DWORD dwDesiredAccess __attribute__((in)), DWORD dwServiceType __attribute__((in)), DWORD dwStartType __attribute__((in)), DWORD dwErrorControl __attribute__((in)), LPCSTR lpBinaryPathName __attribute__((in)), LPCSTR lpLoadOrderGroup __attribute__((in)), LPDWORD lpdwTagId __attribute__((out)), LPCSTR lpDependencies __attribute__((in)), LPCSTR lpServiceStartName __attribute__((in)), LPCSTR lpPassword __attribute__((in)));
142
+ __stdcall BOOL DeleteService(SC_HANDLE hService __attribute__((in)));
143
+ __stdcall SC_HANDLE OpenSCManagerA(LPCSTR lpMachineName __attribute__((in)), LPCSTR lpDatabaseName __attribute__((in)), DWORD dwDesiredAccess __attribute__((in)));
144
+ __stdcall SC_HANDLE OpenServiceA(SC_HANDLE hSCManager __attribute__((in)), LPCSTR lpServiceName __attribute__((in)), DWORD dwDesiredAccess __attribute__((in)));
145
+ __stdcall BOOL StartServiceA(SC_HANDLE hService __attribute__((in)), DWORD dwNumServiceArgs __attribute__((in)), LPCSTR *lpServiceArgVectors __attribute__((in)));
146
+ __stdcall BOOL ControlService(SC_HANDLE hService __attribute__((in)), DWORD dwControl __attribute__((in)), LPSERVICE_STATUS lpServiceStatus __attribute__((out)));
147
+
148
+ __stdcall HANDLE OpenThread(DWORD dwDesiredAccess __attribute__((in)), BOOL bInheritHandle __attribute__((in)), DWORD dwThreadId __attribute__((in)));
149
+ __stdcall DWORD ResumeThread(HANDLE hThread __attribute__((in)));
150
+ __stdcall DWORD SuspendThread(HANDLE hThread __attribute__((in)));
151
+ __stdcall BOOL SetThreadContext(HANDLE hThread __attribute__((in)), void *lpContext __attribute__((in)));
152
+ __stdcall BOOL GetThreadContext(HANDLE hThread __attribute__((in)), void *lpContext);
153
+ __stdcall BOOL CloseHandle(HANDLE hObject __attribute__((in)));
154
+
155
+
156
+
157
+ #define STANDARD_RIGHTS_REQUIRED (0x000F0000L)
158
+
159
+ #define SC_MANAGER_CONNECT 0x0001
160
+ #define SC_MANAGER_CREATE_SERVICE 0x0002
161
+ #define SC_MANAGER_ENUMERATE_SERVICE 0x0004
162
+ #define SC_MANAGER_LOCK 0x0008
163
+ #define SC_MANAGER_QUERY_LOCK_STATUS 0x0010
164
+ #define SC_MANAGER_MODIFY_BOOT_CONFIG 0x0020
165
+ #define SC_MANAGER_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
166
+ SC_MANAGER_CONNECT | \
167
+ SC_MANAGER_CREATE_SERVICE | \
168
+ SC_MANAGER_ENUMERATE_SERVICE | \
169
+ SC_MANAGER_LOCK | \
170
+ SC_MANAGER_QUERY_LOCK_STATUS | \
171
+ SC_MANAGER_MODIFY_BOOT_CONFIG)
172
+
173
+ #define SERVICE_QUERY_CONFIG 0x0001
174
+ #define SERVICE_CHANGE_CONFIG 0x0002
175
+ #define SERVICE_QUERY_STATUS 0x0004
176
+ #define SERVICE_ENUMERATE_DEPENDENTS 0x0008
177
+ #define SERVICE_START 0x0010
178
+ #define SERVICE_STOP 0x0020
179
+ #define SERVICE_PAUSE_CONTINUE 0x0040
180
+ #define SERVICE_INTERROGATE 0x0080
181
+ #define SERVICE_USER_DEFINED_CONTROL 0x0100
182
+ #define SERVICE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
183
+ SERVICE_QUERY_CONFIG | \
184
+ SERVICE_CHANGE_CONFIG | \
185
+ SERVICE_QUERY_STATUS | \
186
+ SERVICE_ENUMERATE_DEPENDENTS | \
187
+ SERVICE_START | \
188
+ SERVICE_STOP | \
189
+ SERVICE_PAUSE_CONTINUE | \
190
+ SERVICE_INTERROGATE | \
191
+ SERVICE_USER_DEFINED_CONTROL)
192
+
193
+ #define SERVICE_KERNEL_DRIVER 0x00000001
194
+ #define SERVICE_FILE_SYSTEM_DRIVER 0x00000002
195
+ #define SERVICE_ADAPTER 0x00000004
196
+ #define SERVICE_RECOGNIZER_DRIVER 0x00000008
197
+ #define SERVICE_DRIVER (SERVICE_KERNEL_DRIVER | \
198
+ SERVICE_FILE_SYSTEM_DRIVER | \
199
+ SERVICE_RECOGNIZER_DRIVER)
200
+ #define SERVICE_WIN32_OWN_PROCESS 0x00000010
201
+ #define SERVICE_WIN32_SHARE_PROCESS 0x00000020
202
+ #define SERVICE_WIN32 (SERVICE_WIN32_OWN_PROCESS | \
203
+ SERVICE_WIN32_SHARE_PROCESS)
204
+ #define SERVICE_INTERACTIVE_PROCESS 0x00000100
205
+ #define SERVICE_TYPE_ALL (SERVICE_WIN32 | \
206
+ SERVICE_ADAPTER | \
207
+ SERVICE_DRIVER | \
208
+ SERVICE_INTERACTIVE_PROCESS)
209
+
210
+ #define SERVICE_BOOT_START 0x00000000
211
+ #define SERVICE_SYSTEM_START 0x00000001
212
+ #define SERVICE_AUTO_START 0x00000002
213
+ #define SERVICE_DEMAND_START 0x00000003
214
+ #define SERVICE_DISABLED 0x00000004
215
+
216
+ #define SERVICE_ERROR_IGNORE 0x00000000
217
+ #define SERVICE_ERROR_NORMAL 0x00000001
218
+ #define SERVICE_ERROR_SEVERE 0x00000002
219
+ #define SERVICE_ERROR_CRITICAL 0x00000003
220
+
221
+ #define SYNCHRONIZE 0x00100000L
222
+ #define THREAD_TERMINATE 0x0001
223
+ #define THREAD_SUSPEND_RESUME 0x0002
224
+ #define THREAD_GET_CONTEXT 0x0008
225
+ #define THREAD_SET_CONTEXT 0x0010
226
+ #define THREAD_SET_INFORMATION 0x0020
227
+ #define THREAD_QUERY_INFORMATION 0x0040
228
+ #define THREAD_SET_THREAD_TOKEN 0x0080
229
+ #define THREAD_IMPERSONATE 0x0100
230
+ #define THREAD_DIRECT_IMPERSONATION 0x0200
231
+ #define THREAD_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3FF)
232
+
233
+ #define CONTEXT_i386 0x00010000
234
+ #define CONTEXT_CONTROL (CONTEXT_i386 | 0x00000001L) // SS:SP, CS:IP, FLAGS, BP
235
+ EOS
236
+
237
+ DynLdr.new_func_c <<EOS
238
+ int get_trace_buf(void *ptr)
239
+ {
240
+ asm("mov eax, [ebp+8] int 0fh");
241
+ return *(int*)ptr;
242
+ }
243
+ EOS
244
+
245
+ def loadmod(mod=$drv)
246
+ sh = DynLdr.openscmanagera(0, 0, DynLdr::SC_MANAGER_ALL_ACCESS)
247
+ raise "cannot openscm" if (sh == 0)
248
+ rh = DynLdr.createservicea(sh, mod, mod, DynLdr::SERVICE_ALL_ACCESS, DynLdr::SERVICE_KERNEL_DRIVER, DynLdr::SERVICE_DEMAND_START, DynLdr::SERVICE_ERROR_NORMAL, File.expand_path(mod), 0, 0, 0, 0, 0)
249
+ if (DynLdr.startservicea(rh, 0, 0) == 0)
250
+ raise "cannot start service"
251
+ end
252
+ DynLdr.CloseServiceHandle(rh)
253
+ DynLdr.CloseServiceHandle(sh)
254
+ end
255
+
256
+ def unloadmod(mod=$drv)
257
+ sh = DynLdr.openscmanagera(0, 0, DynLdr::SC_MANAGER_ALL_ACCESS)
258
+ raise "cannot openscm" if (sh == 0)
259
+ rh = DynLdr.openservicea(sh, mod, DynLdr::SERVICE_ALL_ACCESS)
260
+ DynLdr.controlservice(rh, DynLdr::SERVICE_CONTROL_STOP, 0.chr*4*32)
261
+ DynLdr.deleteservice(rh)
262
+ DynLdr.CloseServiceHandle(rh)
263
+ DynLdr.CloseServiceHandle(sh)
264
+ end
265
+
266
+ def trace(tid, delay=1)
267
+ # put thread in singlestep mode
268
+ th = DynLdr.openthread(DynLdr::THREAD_GET_CONTEXT | DynLdr::THREAD_SET_CONTEXT | DynLdr::THREAD_SUSPEND_RESUME, 0, tid)
269
+ raise "openthread" if (th == 0)
270
+ DynLdr.suspendthread(th)
271
+ ctx = 0.chr * 1024
272
+ ctx[0, 4] = [DynLdr::CONTEXT_CONTROL].pack('V')
273
+ DynLdr.getthreadcontext(th, ctx)
274
+ ctx[192, 4] = [ctx[192, 4].unpack('V').first | (1 << 8)].pack('V')
275
+ DynLdr.setthreadcontext(th, ctx)
276
+ DynLdr.resumethread(th)
277
+ DynLdr.closehandle(th)
278
+
279
+
280
+ buf = 0.chr * 4 * TRACE_BUF_SZ
281
+ loop do
282
+ sleep delay.to_f
283
+ nr = DynLdr.get_trace_buf(buf)
284
+ puts "got #{'%x' % nr} instrs"
285
+ # eips = buf[4, 4*nr].unpack('V*')
286
+ end
287
+
288
+ ensure
289
+ th = DynLdr.openthread(DynLdr::THREAD_GET_CONTEXT | DynLdr::THREAD_SET_CONTEXT | DynLdr::THREAD_SUSPEND_RESUME, 0, tid)
290
+ if (th != 0)
291
+ DynLdr.suspendthread(th)
292
+ ctx = 0.chr * 1024
293
+ ctx[0, 4] = [DynLdr::CONTEXT_CONTROL].pack('V')
294
+ DynLdr.getthreadcontext(th, ctx)
295
+ ctx[192, 4] = [ctx[192, 4].unpack('V').first & ~(1 << 8)].pack('V')
296
+ DynLdr.setthreadcontext(th, ctx)
297
+ DynLdr.resumethread(th)
298
+ DynLdr.closehandle(th)
299
+ end
300
+ end
301
+
302
+ if $0 == __FILE__
303
+ case ARGV.shift
304
+ when /unload/; unloadmod(*ARGV)
305
+ when /load/; loadmod(*ARGV)
306
+ when /trace/; trace(*ARGV)
307
+ end
308
+ end
@@ -0,0 +1,399 @@
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
+ # this exemple illustrates the use of the PTrace class to implement a pytstop-like functionnality
8
+ # Works on linux/x86
9
+ #
10
+
11
+ require 'metasm'
12
+
13
+ class Rubstop < Metasm::PTrace
14
+ EFLAGS = {0 => 'c', 2 => 'p', 4 => 'a', 6 => 'z', 7 => 's', 9 => 'i', 10 => 'd', 11 => 'o'}
15
+ # define accessors for registers
16
+ %w[eax ebx ecx edx ebp esp edi esi eip orig_eax eflags dr0 dr1 dr2 dr3 dr6 dr7 cs ds es fs gs].each { |reg|
17
+ define_method(reg) { peekusr(REGS_I386[reg.upcase]) & 0xffffffff }
18
+ define_method(reg+'=') { |v|
19
+ @regs_cache[reg] = v
20
+ v = [v & 0xffffffff].pack('L').unpack('l').first if v >= 0x8000_0000
21
+ pokeusr(REGS_I386[reg.upcase], v)
22
+ }
23
+ }
24
+
25
+ def cont(signal=0)
26
+ @ssdontstopbp = nil
27
+ singlestep(true) if @wantbp
28
+ super(signal)
29
+ ::Process.waitpid(@pid)
30
+ return if child.exited?
31
+ @oldregs.update @regs_cache
32
+ readregs
33
+ checkbp
34
+ end
35
+
36
+ def singlestep(justcheck=false)
37
+ super()
38
+ ::Process.waitpid(@pid)
39
+ return if child.exited?
40
+ case @wantbp
41
+ when ::Integer; bpx @wantbp ; @wantbp = nil
42
+ when ::String; self.dr7 |= 1 << (2*@wantbp[2, 1].to_i) ; @wantbp = nil
43
+ end
44
+ return if justcheck
45
+ @oldregs.update @regs_cache
46
+ readregs
47
+ checkbp
48
+ end
49
+
50
+ def stepover
51
+ i = curinstr.instruction if curinstr
52
+ if i and (i.opname == 'call' or (i.prefix and i.prefix[:rep]))
53
+ eaddr = @regs_cache['eip'] + curinstr.bin_length
54
+ bpx eaddr, true
55
+ cont
56
+ else
57
+ singlestep
58
+ end
59
+ end
60
+
61
+ def stepout
62
+ # XXX @regs_cache..
63
+ stepover until curinstr.opcode.name == 'ret'
64
+ singlestep
65
+ end
66
+
67
+ def syscall
68
+ @ssdontstopbp = nil
69
+ singlestep(true) if @wantbp
70
+ super()
71
+ ::Process.waitpid(@pid)
72
+ return if child.exited?
73
+ @oldregs.update @regs_cache
74
+ readregs
75
+ checkbp
76
+ end
77
+
78
+ def state; :stopped end
79
+ def ptrace; self end
80
+
81
+ attr_accessor :pgm, :regs_cache, :breakpoints, :singleshot, :wantbp,
82
+ :symbols, :symbols_len, :filemap, :has_pax, :oldregs
83
+ def initialize(*a)
84
+ super(*a)
85
+ @pgm = Metasm::ExeFormat.new Metasm::Ia32.new
86
+ @pgm.encoded = Metasm::EncodedData.new Metasm::LinuxRemoteString.new(@pid)
87
+ @pgm.encoded.data.dbg = self
88
+ @regs_cache = {}
89
+ @oldregs = {}
90
+ readregs
91
+ @oldregs.update @regs_cache
92
+ @breakpoints = {}
93
+ @singleshot = {}
94
+ @wantbp = nil
95
+ @symbols = {}
96
+ @symbols_len = {}
97
+ @filemap = {}
98
+ @has_pax = false
99
+
100
+ stack = self[regs_cache['esp'], 0x1000].to_str.unpack('L*')
101
+ stack.shift # argc
102
+ stack.shift until stack.empty? or stack.first == 0 # argv
103
+ stack.shift
104
+ stack.shift until stack.empty? or stack.first == 0 # envp
105
+ stack.shift
106
+ stack.shift until stack.empty? or stack.shift == 3 # find PHDR ptr in auxv
107
+ if phdr = stack.shift
108
+ phdr &= 0xffff_f000
109
+ loadsyms phdr, phdr.to_s(16)
110
+ end
111
+ end
112
+
113
+ def set_pax(bool)
114
+ if bool
115
+ @pgm.encoded.data.invalidate
116
+ code = @pgm.encoded.data[eip, 4]
117
+ if code != "\0\0\0\0" and @pgm.encoded.data[eip+0x6000_0000, 4] == code
118
+ @has_pax = 'segmexec'
119
+ else
120
+ @has_pax = 'pax'
121
+ end
122
+ else
123
+ @has_pax = false
124
+ end
125
+ end
126
+
127
+ def readregs
128
+ %w[eax ebx ecx edx esi edi esp ebp eip orig_eax eflags dr0 dr1 dr2 dr3 dr6 dr7 cs ds].each { |r| @regs_cache[r] = send(r) }
129
+ @curinstr = nil if @regs_cache['eip'] != @oldregs['eip']
130
+ @pgm.encoded.data.invalidate
131
+ end
132
+
133
+ def curinstr
134
+ @curinstr ||= mnemonic_di
135
+ end
136
+
137
+ def child
138
+ $?
139
+ end
140
+
141
+ def checkbp
142
+ ::Process::waitpid(@pid, ::Process::WNOHANG) if not child
143
+ return if not child
144
+ if not child.stopped?
145
+ if child.exited?; log "process exited with status #{child.exitstatus}"
146
+ elsif child.signaled?; log "process exited due to signal #{child.termsig} (#{Signal.list.index child.termsig})"
147
+ else log "process in unknown status #{child.inspect}"
148
+ end
149
+ return
150
+ elsif child.stopsig != ::Signal.list['TRAP']
151
+ log "process stopped due to signal #{child.stopsig} (#{Signal.list.index child.stopsig})"
152
+ return # do not check 0xcc at eip-1 ! ( if curinstr.bin_length == 1 )
153
+ end
154
+ ccaddr = @regs_cache['eip']-1
155
+ if @breakpoints[ccaddr] and self[ccaddr] == 0xcc
156
+ if @ssdontstopbp != ccaddr
157
+ self[ccaddr] = @breakpoints.delete ccaddr
158
+ self.eip = ccaddr
159
+ @wantbp = ccaddr if not @singleshot.delete ccaddr
160
+ @ssdontstopbp = ccaddr
161
+ else
162
+ @ssdontstopbp = nil
163
+ end
164
+ elsif @regs_cache['dr6'] & 15 != 0
165
+ dr = (0..3).find { |dr_| @regs_cache['dr6'] & (1 << dr_) != 0 }
166
+ @wantbp = "dr#{dr}" if not @singleshot.delete @regs_cache['eip']
167
+ self.dr6 = 0
168
+ self.dr7 = @regs_cache['dr7'] & (0xffff_ffff ^ (3 << (2*dr)))
169
+ readregs
170
+ end
171
+ end
172
+
173
+ def bpx(addr, singleshot=false)
174
+ @singleshot[addr] = singleshot
175
+ return if @breakpoints[addr]
176
+ if @has_pax
177
+ set_hwbp 'x', addr
178
+ else
179
+ begin
180
+ @breakpoints[addr] = self[addr]
181
+ self[addr] = 0xcc
182
+ rescue Errno::EIO
183
+ log 'i/o error when setting breakpoint, switching to PaX mode'
184
+ set_pax true
185
+ @breakpoints.delete addr
186
+ bpx(addr, singleshot)
187
+ end
188
+ end
189
+ end
190
+
191
+ def mnemonic_di(addr = eip)
192
+ @pgm.encoded.ptr = addr
193
+ di = @pgm.cpu.decode_instruction(@pgm.encoded, addr)
194
+ @curinstr = di if addr == @regs_cache['eip']
195
+ di
196
+ end
197
+
198
+ def mnemonic(addr=eip)
199
+ mnemonic_di(addr).instruction
200
+ end
201
+
202
+ def regs_dump
203
+ [%w[eax ebx ecx edx orig_eax], %w[ebp esp edi esi eip]].map { |l|
204
+ l.map { |reg| "#{reg}=#{'%08x' % @regs_cache[reg]}" }.join(' ')
205
+ }.join("\n")
206
+ end
207
+
208
+ def findfilemap(s)
209
+ @filemap.keys.find { |k| @filemap[k][0] <= s and @filemap[k][1] > s } || '???'
210
+ end
211
+
212
+ def findsymbol(k)
213
+ file = findfilemap(k) + '!'
214
+ if s = @symbols[k] ? k : @symbols.keys.find { |s_| s_ < k and s_ + @symbols_len[s_].to_i > k }
215
+ file + @symbols[s] + (s == k ? '' : "+#{(k-s).to_s(16)}")
216
+ else
217
+ file + ('%08x' % k)
218
+ end
219
+ end
220
+
221
+ def set_hwbp(type, addr, len=1)
222
+ dr = (0..3).find { |dr_| @regs_cache['dr7'] & (1 << (2*dr_)) == 0 and @wantbp != "dr#{dr}" }
223
+ if not dr
224
+ log 'no debug reg available :('
225
+ return false
226
+ end
227
+ @regs_cache['dr7'] &= 0xffff_ffff ^ (0xf << (16+4*dr))
228
+ case type
229
+ when 'x'; addr += (@has_pax == 'segmexec' ? 0x6000_0000 : 0)
230
+ when 'r'; @regs_cache['dr7'] |= (((len-1)<<2)|3) << (16+4*dr)
231
+ when 'w'; @regs_cache['dr7'] |= (((len-1)<<2)|1) << (16+4*dr)
232
+ end
233
+ send("dr#{dr}=", addr)
234
+ self.dr6 = 0
235
+ self.dr7 = @regs_cache['dr7'] | (1 << (2*dr))
236
+ readregs
237
+ true
238
+ end
239
+
240
+ def clearbreaks
241
+ @wantbp = nil if @wantbp == @regs_cache['eip']
242
+ @breakpoints.each { |addr, oct| self[addr, 1] = oct }
243
+ @breakpoints.clear
244
+ if @regs_cache['dr7'] & 0xff != 0
245
+ self.dr7 = 0
246
+ readregs
247
+ end
248
+ end
249
+
250
+ def loadsyms(baseaddr, name)
251
+ @loadedsyms ||= {}
252
+ return if @loadedsyms[name] or self[baseaddr, 4] != "\x7fELF"
253
+ @loadedsyms[name] = true
254
+
255
+ set_status " loading symbols from #{name}..."
256
+ e = Metasm::LoadedELF.load self[baseaddr, 0x100_0000]
257
+ e.load_address = baseaddr
258
+ begin
259
+ e.decode
260
+ #e = Metasm::ELF.decode_file name rescue return # read from disk
261
+ rescue
262
+ log "failed to load symbols from #{name}: #$!"
263
+ ($!.backtrace - caller).each { |l| log l.chomp }
264
+ @filemap[baseaddr.to_s(16)] = [baseaddr, baseaddr+0x1000]
265
+ return
266
+ end
267
+
268
+ if e.tag['SONAME']
269
+ name = e.tag['SONAME']
270
+ return if name and @loadedsyms[name]
271
+ @loadedsyms[name] = true
272
+ end
273
+
274
+ last_s = e.segments.reverse.find { |s| s.type == 'LOAD' }
275
+ vlen = last_s.vaddr + last_s.memsz
276
+ vlen -= baseaddr if e.header.type == 'EXEC'
277
+ @filemap[name] = [baseaddr, baseaddr + vlen]
278
+
279
+ oldsyms = @symbols.length
280
+ e.symbols.each { |s|
281
+ next if not s.name or s.shndx == 'UNDEF'
282
+ sname = s.name
283
+ sname = 'weak_'+sname if s.bind == 'WEAK'
284
+ sname = 'local_'+sname if s.bind == 'LOCAL'
285
+ v = s.value
286
+ v = baseaddr + v if v < baseaddr
287
+ @symbols[v] = sname
288
+ @symbols_len[v] = s.size
289
+ }
290
+ if e.header.type == 'EXEC'
291
+ @symbols[e.header.entry] = 'entrypoint'
292
+ end
293
+ set_status nil
294
+ log "loaded #{@symbols.length-oldsyms} symbols from #{name} at #{'%08x' % baseaddr}"
295
+ end
296
+
297
+ def loadallsyms
298
+ File.read("/proc/#{@pid}/maps").each { |l|
299
+ name = l.split[5]
300
+ loadsyms l.to_i(16), name if name and name[0] == ?/
301
+ }
302
+ end
303
+
304
+ def loadmap(mapfile)
305
+ # file fmt: addr type name eg 'c01001ba t setup_idt'
306
+ minaddr = maxaddr = nil
307
+ File.read(mapfile).each { |l|
308
+ addr, type, name = l.chomp.split
309
+ addr = addr.to_i(16)
310
+ minaddr = addr if not minaddr or minaddr > addr
311
+ maxaddr = addr if not maxaddr or maxaddr < addr
312
+ @symbols[addr] = name
313
+ }
314
+ if minaddr
315
+ @filemap[minaddr.to_s(16)] = [minaddr, maxaddr+1]
316
+ end
317
+ end
318
+
319
+ def scansyms
320
+ addr = 0
321
+ fd = @pgm.encoded.data.readfd
322
+ while addr <= 0xffff_f000
323
+ addr = 0xc000_0000 if @has_pax and addr == 0x6000_0000
324
+ log "scansym: #{'%08x' % addr}" if addr & 0x0fff_ffff == 0
325
+ fd.pos = addr
326
+ loadsyms(addr, '%08x'%addr) if (fd.read(4) == "\x7fELF" rescue false)
327
+ addr += 0x1000
328
+ end
329
+ end
330
+
331
+ def backtrace
332
+ s = findsymbol(@regs_cache['eip'])
333
+ if block_given?
334
+ yield s
335
+ else
336
+ bt = []
337
+ bt << s
338
+ end
339
+ fp = @regs_cache['ebp']
340
+ while fp >= @regs_cache['esp'] and fp <= @regs_cache['esp']+0x10000
341
+ s = findsymbol(self[fp+4, 4].unpack('L').first)
342
+ if block_given?
343
+ yield s
344
+ else
345
+ bt << s
346
+ end
347
+ fp = self[fp, 4].unpack('L').first
348
+ end
349
+ bt
350
+ end
351
+
352
+ def [](addr, len=nil)
353
+ @pgm.encoded.data[addr, len]
354
+ end
355
+ def []=(addr, len, str=nil)
356
+ @pgm.encoded.data[addr, len] = str
357
+ end
358
+
359
+ attr_accessor :logger
360
+ def log(s)
361
+ @logger ||= $stdout
362
+ @logger.puts s
363
+ end
364
+
365
+ # set a temporary status info (nil for default value)
366
+ def set_status(s)
367
+ @logger ||= $stdout
368
+ if @logger != $stdout
369
+ @logger.statusline = s
370
+ else
371
+ s ||= ' '*72
372
+ @logger.print s + "\r"
373
+ @logger.flush
374
+ end
375
+ end
376
+ end
377
+
378
+ if $0 == __FILE__
379
+ # start debugging
380
+ rs = Rubstop.new(ARGV.shift)
381
+
382
+ begin
383
+ while rs.child.stopped? and rs.child.stopsig == Signal.list['TRAP']
384
+ if $VERBOSE
385
+ puts "#{'%08x' % rs.eip} #{rs.mnemonic}"
386
+ rs.singlestep
387
+ else
388
+ rs.syscall ; rs.syscall # wait return of syscall
389
+ puts "#{rs.orig_eax.to_s.ljust(3)} #{rs.syscallnr.index rs.orig_eax}"
390
+ end
391
+ end
392
+ p rs.child
393
+ puts rs.regs_dump
394
+ rescue Interrupt
395
+ rs.detach rescue nil
396
+ puts 'interrupted!'
397
+ rescue Errno::ESRCH
398
+ end
399
+ end