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,35 @@
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
+ # here we assemble a dummy MIPS PE
9
+ # TODO autodetect header.machine from cpu, find something to put in
10
+ # the MZ header, make a real mips sample program
11
+ #
12
+
13
+
14
+ require 'metasm'
15
+
16
+ cpu = Metasm::MIPS.new(:little)
17
+ prog = Metasm::PE.assemble(cpu, <<EOS)
18
+ .text
19
+ .entrypoint
20
+ lui r4, 0x42
21
+ jal toto
22
+ add r4, r1, r2
23
+ jr r31
24
+ nop
25
+
26
+ toto:
27
+ jr r31
28
+ ;ldc1 fp12, 28(r4)
29
+ nop
30
+
31
+ .import 'foobar' 'baz'
32
+ EOS
33
+ prog.header.machine='R4000'
34
+ data = prog.encode_file 'mipspe.exe'
35
+
@@ -0,0 +1,78 @@
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
+ # here we will build an executable file that will shut down the machine
9
+ # when run
10
+ # the header part comes from the factorize sample script
11
+ #
12
+
13
+ require 'metasm'
14
+ cpu = Metasm::Ia32.new
15
+ cpu.generate_PIC = false
16
+ Metasm::PE.compile_c(cpu, DATA.read + <<EOS).encode_file('metasm-shutdown.exe')
17
+ int main(void) {
18
+ static HANDLE htok;
19
+ static TOKEN_PRIVILEGES tokpriv;
20
+ OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &htok);
21
+ LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tokpriv.Privileges[0].Luid);
22
+ tokpriv.PrivilegeCount = 1U;
23
+ tokpriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
24
+ AdjustTokenPrivileges(htok, 0, &tokpriv, 0U, NULL, NULL);
25
+ ExitWindowsEx(EWX_SHUTDOWN | EWX_FORCE, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_UPGRADE | SHTDN_REASON_FLAG_PLANNED);
26
+ return 0;
27
+ }
28
+ EOS
29
+
30
+ __END__
31
+ #define EWX_FORCE 0x00000004U
32
+ #define EWX_SHUTDOWN 0x00000001U
33
+ #define LookupPrivilegeValue LookupPrivilegeValueA
34
+ #define NULL ((void *)0)
35
+ #define SE_PRIVILEGE_ENABLED (0x00000002UL)
36
+ #define SHTDN_REASON_FLAG_PLANNED 0x80000000U
37
+ #define SHTDN_REASON_MAJOR_OPERATINGSYSTEM 0x00020000U
38
+ #define SHTDN_REASON_MINOR_UPGRADE 0x00000003U
39
+ #define TOKEN_ADJUST_PRIVILEGES (0x0020U)
40
+ #define TOKEN_QUERY (0x0008U)
41
+ #define __TEXT(quote) quote
42
+ #define TEXT(quote) __TEXT(quote)
43
+ #define SE_SHUTDOWN_NAME TEXT("SeShutdownPrivilege")
44
+
45
+ typedef int BOOL;
46
+ typedef char CHAR;
47
+ typedef unsigned long DWORD;
48
+ typedef void *HANDLE;
49
+ typedef long LONG;
50
+ typedef unsigned int UINT;
51
+ BOOL ExitWindowsEx __attribute__((dllimport)) __attribute__((stdcall))(UINT uFlags, DWORD dwReason);
52
+ HANDLE GetCurrentProcess __attribute__((dllimport)) __attribute__((stdcall))(void);
53
+ typedef const CHAR *LPCSTR;
54
+ typedef DWORD *PDWORD;
55
+ typedef HANDLE *PHANDLE;
56
+
57
+ struct _LUID {
58
+ DWORD LowPart;
59
+ LONG HighPart;
60
+ };
61
+ typedef struct _LUID LUID;
62
+ BOOL OpenProcessToken __attribute__((dllimport)) __attribute__((stdcall))(HANDLE ProcessHandle, DWORD DesiredAccess, PHANDLE TokenHandle);
63
+ typedef struct _LUID *PLUID;
64
+ BOOL LookupPrivilegeValueA __attribute__((dllimport)) __attribute__((stdcall))(LPCSTR lpSystemName, LPCSTR lpName, PLUID lpLuid);
65
+
66
+ struct _LUID_AND_ATTRIBUTES {
67
+ LUID Luid;
68
+ DWORD Attributes;
69
+ };
70
+ typedef struct _LUID_AND_ATTRIBUTES LUID_AND_ATTRIBUTES;
71
+
72
+ struct _TOKEN_PRIVILEGES {
73
+ DWORD PrivilegeCount;
74
+ LUID_AND_ATTRIBUTES Privileges[1];
75
+ };
76
+ typedef struct _TOKEN_PRIVILEGES *PTOKEN_PRIVILEGES;
77
+ typedef struct _TOKEN_PRIVILEGES TOKEN_PRIVILEGES;
78
+ BOOL AdjustTokenPrivileges __attribute__((dllimport)) __attribute__((stdcall))(HANDLE TokenHandle, BOOL DisableAllPrivileges, PTOKEN_PRIVILEGES NewState, DWORD BufferLength, PTOKEN_PRIVILEGES PreviousState, PDWORD ReturnLength);
@@ -0,0 +1,51 @@
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
+ #
9
+ # in this sample we compile 2 PE files, one executable and one dll
10
+ # with the same base address, to check if the base relocation table
11
+ # of the dll is correctly encoded
12
+ #
13
+
14
+ require 'metasm'
15
+
16
+ cpu = Metasm::Ia32.new
17
+
18
+ pe = Metasm::PE.assemble cpu, <<EOS
19
+ .image_base 0x50000
20
+ .section '.text' r w x ; allows merging iat/data/etc
21
+ .entrypoint
22
+ call foobarplt
23
+ xor eax, eax
24
+ ret
25
+
26
+ .import 'pe-foolib' foobar foobarplt
27
+ EOS
28
+ pe.encode_file('pe-testreloc.exe', 'exe')
29
+
30
+ dll = Metasm::PE.assemble cpu, <<EOS
31
+ .image_base 0x50000
32
+ .section '.text' r w x
33
+ foobar:
34
+ push 0
35
+ push msg ; use non-position independant code
36
+ push title
37
+ push 0
38
+ call msgbox
39
+
40
+ xor eax, eax
41
+ ret
42
+
43
+ .align 4
44
+ msg db 'foo', 0
45
+ title db 'bar', 0
46
+
47
+ .import user32 MessageBoxA msgbox
48
+ .export foobar
49
+ .libname 'pe-foolib'
50
+ EOS
51
+ dll.encode_file('pe-foolib.dll', 'dll')
@@ -0,0 +1,24 @@
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
+ #
9
+ # compiles a PE file with the specified resource directory
10
+ # TODO build an icon or something
11
+ #
12
+
13
+ require 'metasm'
14
+
15
+ pe = Metasm::PE.assemble Metasm::Ia32.new, <<EOS
16
+ .entrypoint
17
+ xor eax, eax
18
+ ret
19
+ EOS
20
+
21
+ rsrc = { 1 => { 1 => { 2 => 'xxx' }, 'toto' => { 12 => 'tata' } } }
22
+ pe.resource = Metasm::COFF::ResourceDirectory.from_hash rsrc
23
+
24
+ pe.encode_file('pe-testrsrc.exe')
@@ -0,0 +1,31 @@
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 script takes a list of dll filenames as arguments, and outputs each lib export
8
+ # libname, followed by the list of the exported symbol names, in a format usable
9
+ # by the PE class autoimport functionnality (see metasm/os/windows.rb)
10
+ #
11
+
12
+ require 'metasm'
13
+
14
+ ARGV.each { |f|
15
+ pe = Metasm::PE.decode_file_header(f) rescue next
16
+ pe.decode_exports
17
+ next if not pe.export or not pe.export.libname
18
+ puts pe.export.libname.sub(/\.dll$/i, '')
19
+ line = ''
20
+ pe.export.exports.each { |e|
21
+ next if not e.name
22
+ # next if not e.target # allow forwarders ? (may change name)
23
+ e = ' ' << e.name
24
+ if line.length + e.length >= 160
25
+ puts line
26
+ line = ''
27
+ end
28
+ line << e
29
+ }
30
+ puts line if not line.empty?
31
+ }
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This file is part of Metasm, the Ruby assembly manipulation suite
4
+ # Copyright (C) 2006-2009 Yoann GUILLOT
5
+ #
6
+ # Licence is LGPL, see LICENCE in the top-level directory
7
+
8
+ require 'metasm'
9
+ $opts = { :execlass => Metasm::PE, :srctype_data => 'c' }
10
+ load File.join(File.dirname(__FILE__), 'exeencode.rb')
11
+
12
+ __END__
13
+ __stdcall int MessageBox(int, char*, char*, int);
14
+ __stdcall void ExitProcess(int);
15
+ void main(void)
16
+ {
17
+ MessageBox(0, "kikoo", "lol", 0);
18
+ ExitProcess(0);
19
+ }
@@ -0,0 +1,494 @@
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
+ # Map a PE file under another OS using DynLdr, API imports are redirected to ruby callback for emulation
8
+ #
9
+
10
+ require 'metasm'
11
+
12
+ class PeLdr
13
+ attr_accessor :pe, :load_address
14
+ DL = Metasm::DynLdr
15
+
16
+ # load a PE file, setup basic IAT hooks (raises "unhandled lib!import")
17
+ def initialize(file, hooktype=:iat)
18
+ if file.kind_of? Metasm::PE
19
+ @pe = file
20
+ elsif file[0, 2] == 'MZ' and file.length > 0x3c
21
+ @pe = Metasm::PE.decode(file)
22
+ else # filename
23
+ @pe = Metasm::PE.decode_file(file)
24
+ end
25
+ @load_address = DL.memory_alloc(@pe.optheader.image_size)
26
+ raise 'malloc' if @load_address == 0xffff_ffff
27
+
28
+ puts "map sections" if $DEBUG
29
+ DL.memory_write(@load_address, @pe.encoded.data[0, @pe.optheader.headers_size].to_str)
30
+ @pe.sections.each { |s|
31
+ DL.memory_write(@load_address+s.virtaddr, s.encoded.data.to_str)
32
+ }
33
+
34
+ puts "fixup sections" if $DEBUG
35
+ off = @load_address - @pe.optheader.image_base
36
+ @pe.relocations.to_a.each { |rt|
37
+ base = rt.base_addr
38
+ rt.relocs.each { |r|
39
+ if r.type == 'HIGHLOW'
40
+ ptr = @load_address + base + r.offset
41
+ old = DL.memory_read(ptr, 4).unpack('V').first
42
+ DL.memory_write_int(ptr, old + off)
43
+ end
44
+ }
45
+ }
46
+
47
+ @iat_cb = {}
48
+ @eat_cb = {}
49
+ case hooktype
50
+ when :iat
51
+ puts "hook IAT" if $DEBUG
52
+ @pe.imports.to_a.each { |id|
53
+ ptr = @load_address + id.iat_p
54
+ id.imports.each { |i|
55
+ n = "#{id.libname}!#{i.name}"
56
+ cb = DL.callback_alloc_c('void x(void)') { raise "unemulated import #{n}" }
57
+ DL.memory_write_int(ptr, cb)
58
+ @iat_cb[n] = cb
59
+ ptr += 4
60
+ }
61
+ }
62
+ when :eat, :exports
63
+ puts "hook EAT" if $DEBUG
64
+ ptr = @load_address + @pe.export.func_p
65
+ @pe.export.exports.each { |e|
66
+ n = e.name || e.ordinal
67
+ cb = DL.callback_alloc_c('void x(void)') { raise "unemulated export #{n}" }
68
+ DL.memory_write_int(ptr, cb - @load_address) # RVA
69
+ @eat_cb[n] = cb
70
+ ptr += 4
71
+ }
72
+ end
73
+ end
74
+
75
+ # reset original expected memory protections for the sections
76
+ # the IAT may reside in a readonly section, so call this only after all hook_imports
77
+ def reprotect_sections
78
+ @pe.sections.each { |s|
79
+ p = ''
80
+ p << 'r' if s.characteristics.include? 'MEM_READ'
81
+ p << 'w' if s.characteristics.include? 'MEM_WRITE'
82
+ p << 'x' if s.characteristics.include? 'MEM_EXECUTE'
83
+ DL.memory_perm(@load_address + s.virtaddr, s.virtsize, p)
84
+ }
85
+ end
86
+
87
+ # add a specific hook for an IAT function
88
+ # accepts a function pointer in proto
89
+ # exemple: hook_import('KERNEL32.dll', 'GetProcAddress', '__stdcall int f(int, char*)') { |h, name| puts "getprocaddr #{name}" ; 0 }
90
+ def hook_import(libname, impname, proto, &b)
91
+ @pe.imports.to_a.each { |id|
92
+ next if id.libname != libname
93
+ ptr = @load_address + id.iat_p
94
+ id.imports.each { |i|
95
+ if i.name == impname
96
+ DL.callback_free(@iat_cb["#{libname}!#{impname}"])
97
+ if proto.kind_of? Integer
98
+ cb = proto
99
+ else
100
+ cb = DL.callback_alloc_c(proto, &b)
101
+ @iat_cb["#{libname}!#{impname}"] = cb
102
+ end
103
+ DL.memory_write_int(ptr, cb)
104
+ end
105
+ ptr += 4
106
+ }
107
+ }
108
+ end
109
+
110
+ # add a specific hook in the export table
111
+ # exemple: hook_export('ExportedFunc', '__stdcall int f(int, char*)') { |i, p| blabla ; 1 }
112
+ def hook_export(name, proto, &b)
113
+ ptr = @load_address + @pe.export.func_p
114
+ @pe.export.exports.each { |e|
115
+ n = e.name || e.ordinal
116
+ if n == name
117
+ DL.callback_free(@eat_cb[name])
118
+ if proto.kind_of? Integer
119
+ cb = proto
120
+ else
121
+ cb = DL.callback_alloc_c(proto, &b)
122
+ @eat_cb[name] = cb
123
+ end
124
+ DL.memory_write_int(ptr, cb - @load_address) # RVA
125
+ end
126
+ ptr += 4
127
+ }
128
+ end
129
+
130
+ # run the loaded PE entrypoint
131
+ def run_init
132
+ ptr = @pe.optheader.entrypoint
133
+ if ptr != 0
134
+ ptr += @load_address
135
+ DL.raw_invoke(ptr, [@load_address, 1, 1], 1)
136
+ end
137
+ end
138
+
139
+ # similar to DL.new_api_c for the mapped PE
140
+ def new_api_c(proto)
141
+ proto += ';' # allow 'int foo()'
142
+ cp = DL.host_cpu.new_cparser
143
+ cp.parse(proto)
144
+ cp.toplevel.symbol.each_value { |v|
145
+ next if not v.kind_of? Metasm::C::Variable # enums
146
+ if e = pe.export.exports.find { |e_| e_.name == v.name and e_.target }
147
+ DL.new_caller_for(cp, v, v.name.downcase, @load_address + pe.label_rva(e.target))
148
+ end
149
+ }
150
+
151
+ cp.numeric_constants.each { |k, v|
152
+ n = k.upcase
153
+ n = "C#{n}" if n !~ /^[A-Z]/
154
+ DL.const_set(n, v) if not DL.const_defined?(n) and v.kind_of? Integer
155
+ }
156
+ end
157
+
158
+ # maps a TEB/PEB in the current process, sets the fs register to point to it
159
+ def self.setup_teb
160
+ @@teb = DL.memory_alloc(4096)
161
+ @@peb = DL.memory_alloc(4096)
162
+ populate_teb
163
+ populate_peb
164
+ fs = allocate_ldt_entry_teb
165
+ DL.new_func_c('__fastcall void set_fs(int i) { asm("mov fs, ecx"); }') { DL.set_fs(fs) }
166
+ end
167
+
168
+ # fills a fake TEB structure
169
+ def self.populate_teb
170
+ DL.memory_write(@@teb, 0.chr*4096)
171
+ set = lambda { |off, val| DL.memory_write_int(@@teb+off, val) }
172
+ # the stack will probably never go higher than that whenever in the dll...
173
+ set[0x4, DL.new_func_c('int get_sp(void) { asm("mov eax, esp and eax, ~0xfff"); }') { DL.get_sp }] # stack_base
174
+ set[0x8, 0x10000] # stack_limit
175
+ set[0x18, @@teb] # teb
176
+ set[0x30, @@peb] # peb
177
+ end
178
+
179
+ def self.populate_peb
180
+ DL.memory_write(@@peb, 0.chr*4096)
181
+ set = lambda { |off, val| DL.memory_write_int(@@peb+off, val) }
182
+ end
183
+
184
+ def self.teb ; @@teb ; end
185
+ def self.peb ; @@peb ; end
186
+
187
+ # allocate an LDT entry for the teb, returns a value suitable for the fs selector
188
+ def self.allocate_ldt_entry_teb
189
+ entry = 1
190
+ # ldt_entry base_addr size_in_pages
191
+ # 32bits:1 type:2 (0=data) readonly:1 limit_in_pages:1 seg_not_present:1 usable:1
192
+ struct = [entry, @@teb, 1, 0b1_0_1_0_00_1].pack('VVVV')
193
+ Kernel.syscall(123, 1, DL.str_ptr(struct), struct.length) # __NR_modify_ldt
194
+ (entry << 3) | 7
195
+ end
196
+
197
+ setup_teb
198
+ end
199
+
200
+ # generate a fake PE which exports stuff found in k32/ntdll
201
+ # so that other lib may directly scan this PE with their own getprocaddr
202
+ class FakeWinAPI < PeLdr
203
+ attr_accessor :win_version
204
+ attr_accessor :exports
205
+
206
+ def initialize(elist=nil)
207
+ @exports = []
208
+ @win_version = { :major => 5, :minor => 1, :build => 2600, :sp => 'Service pack 3', :sp_major => 3, :sp_minor => 0 }
209
+
210
+ # if you know the exact list you need, put it there (much faster)
211
+ if not elist
212
+ elist = Metasm::WindowsExports::EXPORT.map { |k, v| k if v =~ /kernel32|ntdll/i }.compact
213
+ elist |= ['free', 'malloc', 'memset', '??2@YAPAXI@Z', '_initterm', '_lock', '_unlock', '_wcslwr', '_wcsdup', '__dllonexit']
214
+ end
215
+
216
+ src = ".libname 'emu_winapi'\ndummy: int 3\n" + elist.map { |e| ".export #{e.inspect} dummy" }.join("\n")
217
+ super(Metasm::PE.assemble(Metasm::Ia32.new, src).encode_string(:lib), :eat) # put 'nil' instead of :eat if all exports are emu
218
+
219
+ @heap = {}
220
+ malloc = lambda { |sz| str = 0.chr*sz ; ptr = DL.str_ptr(str) ; @heap[ptr] = str ; ptr }
221
+
222
+ lasterr = 0
223
+
224
+ # kernel32
225
+ hook_export('CloseHandle', '__stdcall int f(int)') { |a| 1 }
226
+ hook_export('DuplicateHandle', '__stdcall int f(int, int, int, void*, int, int, int)') { |*a| DL.memory_write_int(a[3], a[1]) ; 1 }
227
+ hook_export('EnterCriticalSection', '__stdcall int f(void*)') { 1 }
228
+ hook_export('GetCurrentProcess', '__stdcall int f(void)') { -1 }
229
+ hook_export('GetCurrentProcessId', '__stdcall int f(void)') { Process.pid }
230
+ hook_export('GetCurrentThreadId', '__stdcall int f(void)') { Process.pid }
231
+ hook_export('GetEnvironmentVariableW', '__stdcall int f(void*, void*, int)') { |name, resp, sz|
232
+ next 0 if name == 0
233
+ s = DL.memory_read_wstrz(name)
234
+ s = s.unpack('v*').pack('C*') rescue nil
235
+ puts "GetEnv #{s.inspect}" if $VERBOSE
236
+ v = ENV[s].to_s
237
+ if resp and v.length*2+2 <= sz
238
+ DL.memory_write(resp, (v.unpack('C*') << 0).pack('v*'))
239
+ v.length*2 # 0 if not found
240
+ else
241
+ v.length*2+2
242
+ end
243
+ }
244
+ hook_export('GetLastError', '__stdcall int f(void)') { lasterr }
245
+ hook_export('GetProcAddress', '__stdcall int f(int, char*)') { |h, v|
246
+ v = DL.memory_read_strz(v) if v >= 0x10000
247
+ puts "GetProcAddr #{'0x%x' % h}, #{v.inspect}" if $VERBOSE
248
+ @eat_cb[v] or raise "unemulated getprocaddr #{v}"
249
+ }
250
+ hook_export('GetSystemInfo', '__stdcall void f(void*)') { |ptr|
251
+ DL.memory_write(ptr, [0, 0x1000, 0x10000, 0x7ffeffff, 1, 1, 586, 0x10000, 0].pack('V*'))
252
+ 1
253
+ }
254
+ hook_export('GetSystemTimeAsFileTime', '__stdcall void f(void*)') { |ptr|
255
+ v = ((Time.now - Time.mktime(1971, 1, 1, 0, 0, 0) + 370*365.25*24*60*60) * 1000 * 1000 * 10).to_i
256
+ DL.memory_write(ptr, [v & 0xffffffff, (v >> 32 & 0xffffffff)].pack('VV'))
257
+ 1
258
+ }
259
+ hook_export('GetTickCount', '__stdcall int f(void)') { (Time.now.to_i * 1000) & 0xffff_ffff }
260
+ hook_export('GetVersion', '__stdcall int f(void)') { (@win_version[:build] << 16) | (@win_version[:major] << 8) | @win_version[:minor] }
261
+ hook_export('GetVersionExA', '__stdcall int f(void*)') { |ptr|
262
+ sz = DL.memory_read(ptr, 4).unpack('V').first
263
+ data = [@win_version[:major], @win_version[:minor], @win_version[:build], 2, @win_version[:sp], @win_version[:sp_major], @win_version[:sp_minor]].pack('VVVVa128VV')
264
+ DL.memory_write(ptr+4, data[0, sz-4])
265
+ 1
266
+ }
267
+ hook_export('HeapAlloc', '__stdcall int f(int, int, int)') { |h, f, sz| malloc[sz] }
268
+ hook_export('HeapCreate', '__stdcall int f(int, int, int)') { 1 }
269
+ hook_export('HeapFree', '__stdcall int f(int, int, int)') { |h, f, p| @heap.delete p ; 1 }
270
+ hook_export('InterlockedCompareExchange', '__stdcall int f(int*, int, int)'+
271
+ '{ asm("mov eax, [ebp+16] mov ecx, [ebp+12] mov edx, [ebp+8] lock cmpxchg [edx], ecx"); }')
272
+ hook_export('InterlockedExchange', '__stdcall int f(int*, int)'+
273
+ '{ asm("mov eax, [ebp+12] mov ecx, [ebp+8] lock xchg [ecx], eax"); }')
274
+ hook_export('InitializeCriticalSectionAndSpinCount', '__stdcall int f(int, int)') { 1 }
275
+ hook_export('InitializeCriticalSection', '__stdcall int f(void*)') { 1 }
276
+ hook_export('LeaveCriticalSection', '__stdcall int f(void*)') { 1 }
277
+ hook_export('QueryPerformanceCounter', '__stdcall int f(void*)') { |ptr|
278
+ v = (Time.now.to_f * 1000 * 1000).to_i
279
+ DL.memory_write(ptr, [v & 0xffffffff, (v >> 32 & 0xffffffff)].pack('VV'))
280
+ 1
281
+ }
282
+ hook_export('SetLastError', '__stdcall int f(int)') { |i| lasterr = i ; 1 }
283
+ hook_export('TlsAlloc', '__stdcall int f(void)') { 1 }
284
+
285
+ # ntdll
286
+ readustring = lambda { |p| DL.memory_read(*DL.memory_read(p, 8).unpack('vvV').values_at(2, 0)) }
287
+ hook_export('RtlEqualUnicodeString', '__stdcall int f(void*, void*, int)') { |s1, s2, cs|
288
+ s1 = readustring[s1]
289
+ s2 = readustring[s2]
290
+ puts "RtlEqualUnicodeString #{s1.unpack('v*').pack('C*').inspect}, #{s2.unpack('v*').pack('C*').inspect}, #{cs}" if $VERBOSE
291
+ if cs == 1
292
+ s1 = s1.downcase
293
+ s2 = s2.downcase
294
+ end
295
+ s1 == s2 ? 1 : 0
296
+ }
297
+ hook_export('MultiByteToWideChar', '__stdcall int f(int, int, void*, int, void*, int)') { |cp, fl, ip, is, op, os|
298
+ is = DL.memory_read_strz(ip).length if is == 0xffff_ffff
299
+ if os == 0
300
+ is
301
+ elsif os >= is*2 # not sure with this..
302
+ DL.memory_write(op, DL.memory_read(ip, is).unpack('C*').pack('v*'))
303
+ is
304
+ else 0
305
+ end
306
+
307
+ }
308
+ hook_export('LdrUnloadDll', '__stdcall int f(int)') { 0 }
309
+
310
+ # msvcrt
311
+ hook_export('free', 'void f(int)') { |i| @heap.delete i ; 0}
312
+ hook_export('malloc', 'int f(int)') { |i| malloc[i] }
313
+ hook_export('memset', 'char* f(char* p, char c, int n) { while (n--) p[n] = c; return p; }')
314
+ hook_export('??2@YAPAXI@Z', 'int f(int)') { |i| raise 'fuuu' if i > 0x100000 ; malloc[i] } # at some point we're called with a ptr as arg, may be a peldr bug
315
+ hook_export('__dllonexit', 'int f(int, int, int)') { |i, ii, iii| i }
316
+ hook_export('_initterm', 'void f(void (**p)(void), void*p2) { while(p < p2) { if (*p) (**p)(); p++; } }')
317
+ hook_export('_lock', 'void f(int)') { 0 }
318
+ hook_export('_unlock', 'void f(int)') { 0 }
319
+ hook_export('_wcslwr', '__int16* f(__int16* p) { int i=-1; while (p[++i]) p[i] |= 0x20; return p; }')
320
+ hook_export('_wcsdup', 'int f(__int16* p)') { |p|
321
+ cp = DL.memory_read_wstrz(p) + "\0\0"
322
+ p = DL.str_ptr(cp)
323
+ @heap[p] = cp
324
+ p
325
+ }
326
+ end
327
+
328
+ def hook_export(*a, &b)
329
+ @exports |= [a.first]
330
+ super(*a, &b)
331
+ end
332
+
333
+ # take another PeLdr and patch its IAT with functions from our @exports (eg our explicit export hooks)
334
+ def intercept_iat(ldr)
335
+ ldr.pe.imports.to_a.each { |id|
336
+ id.imports.each { |i|
337
+ next if not @exports.include? i.name or not @eat_cb[i.name]
338
+ ldr.hook_import(id.libname, i.name, @eat_cb[i.name])
339
+ }
340
+ }
341
+ end
342
+ end
343
+
344
+ if $0 == __FILE__
345
+ dl = Metasm::DynLdr
346
+
347
+ l = PeLdr.new('dbghelp.dll')
348
+ #dl.memory_write(l.load_address + 0x33b10, "\x90\xcc") # break on SymInitializeW
349
+
350
+ puts 'dbghelp@%x' % l.load_address if $VERBOSE
351
+
352
+ wapi = FakeWinAPI.new %w[CloseHandle DuplicateHandle EnterCriticalSection GetCurrentProcess GetCurrentProcessId GetCurrentThreadId GetEnvironmentVariableW GetLastError GetProcAddress GetSystemInfo GetSystemTimeAsFileTime GetTickCount GetVersion GetVersionExA HeapAlloc HeapCreate HeapFree InterlockedCompareExchange InterlockedExchange InitializeCriticalSectionAndSpinCount InitializeCriticalSection LeaveCriticalSection QueryPerformanceCounter SetLastError TlsAlloc RtlEqualUnicodeString MultiByteToWideChar free malloc memset ??2@YAPAXI@Z __dllonexit _initterm _lock _unlock _wcslwr _wcsdup GetModuleHandleA LoadLibraryA NtQueryObject NtQueryInformationProcess LdrUnloadDll]
353
+ puts 'wapi@%x' % wapi.load_address if $VERBOSE
354
+
355
+ wapi.hook_export('GetModuleHandleA', '__stdcall int f(char*)') { |ptr|
356
+ s = dl.memory_read_strz(ptr) if ptr
357
+ case s
358
+ when /kernel32|ntdll/i; wapi.load_address
359
+ else 0
360
+ end
361
+ }
362
+ wapi.hook_export('LoadLibraryA', '__stdcall int f(char*)') { |ptr|
363
+ s = dl.memory_read_strz(ptr)
364
+ case s
365
+ when /kernel32|ntdll/i; wapi.load_address
366
+ else puts "LoadLibrary #{s.inspect}" ; 0
367
+ end
368
+ }
369
+ wapi.hook_export('NtQueryObject', '__stdcall int f(int, int, void*, int, int*)') { |h, type, resp, sz, psz|
370
+ puts "NtQueryObject #{h}, #{type}, #{sz}" if $VERBOSE
371
+ if h == 42 and type == 2 and sz >= 24
372
+ dl.memory_write(resp, [14, 16, resp+8].pack('vvV') + "Process\0".unpack('C*').pack('v*'))
373
+ dl.memory_write_int(psz, 24) if psz
374
+ 0
375
+ else
376
+ 0x8000_0000
377
+ end
378
+ }
379
+ wapi.hook_export('NtQueryInformationProcess', '__stdcall int f(int, int, void*, int, int*)') { |h, type, resp, sz, psz|
380
+ puts "NtQueryInformationProcess #{h}, #{type}, #{sz}" if $VERBOSE
381
+ if h == 42 and type == 0
382
+ # reservd peb res res ptr_to_pid
383
+ peb = 0xdead0000
384
+ dl.memory_write(resp, [42, peb, 0, 0, resp, 0].pack('V*'))
385
+ dl.memory_write_int(psz, 24) if psz
386
+ 0
387
+ else
388
+ 0x8000_0000
389
+ end
390
+ }
391
+ #puts wapi.exports.join(' ') # generate arglist for FakeWinAPI.new
392
+ # TODO hook the resolv function of dbghelp to list what it checks
393
+
394
+ wapi.intercept_iat(l)
395
+
396
+
397
+ l.reprotect_sections
398
+
399
+ l.new_api_c <<EOS
400
+ #define SYMOPT_CASE_INSENSITIVE 0x00000001
401
+ #define SYMOPT_UNDNAME 0x00000002
402
+ #define SYMOPT_DEFERRED_LOADS 0x00000004
403
+ #define SYMOPT_NO_CPP 0x00000008
404
+ #define SYMOPT_LOAD_LINES 0x00000010
405
+ #define SYMOPT_OMAP_FIND_NEAREST 0x00000020
406
+ #define SYMOPT_LOAD_ANYTHING 0x00000040
407
+ #define SYMOPT_IGNORE_CVREC 0x00000080
408
+ #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100
409
+ #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200
410
+ #define SYMOPT_EXACT_SYMBOLS 0x00000400
411
+ #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800
412
+ #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000
413
+ #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000
414
+ #define SYMOPT_PUBLICS_ONLY 0x00004000
415
+ #define SYMOPT_NO_PUBLICS 0x00008000
416
+ #define SYMOPT_AUTO_PUBLICS 0x00010000
417
+ #define SYMOPT_NO_IMAGE_SEARCH 0x00020000
418
+ #define SYMOPT_SECURE 0x00040000
419
+ #define SYMOPT_NO_PROMPTS 0x00080000
420
+ #define SYMOPT_DEBUG 0x80000000
421
+
422
+ typedef int BOOL;
423
+ typedef char CHAR;
424
+ typedef unsigned long DWORD;
425
+ typedef unsigned __int64 DWORD64;
426
+ typedef void *HANDLE;
427
+ typedef unsigned __int64 *PDWORD64;
428
+ typedef void *PVOID;
429
+ typedef unsigned long ULONG;
430
+ typedef unsigned __int64 ULONG64;
431
+ typedef const CHAR *PCSTR;
432
+ typedef CHAR *PSTR;
433
+
434
+ struct _SYMBOL_INFO {
435
+ ULONG SizeOfStruct;
436
+ ULONG TypeIndex;
437
+ ULONG64 Reserved[2];
438
+ ULONG info;
439
+ ULONG Size;
440
+ ULONG64 ModBase;
441
+ ULONG Flags;
442
+ ULONG64 Value;
443
+ ULONG64 Address;
444
+ ULONG Register;
445
+ ULONG Scope;
446
+ ULONG Tag;
447
+ ULONG NameLen;
448
+ ULONG MaxNameLen;
449
+ CHAR Name[1];
450
+ };
451
+ typedef struct _SYMBOL_INFO *PSYMBOL_INFO;
452
+
453
+ typedef __stdcall BOOL (*PSYM_ENUMERATESYMBOLS_CALLBACK)(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext);
454
+ __stdcall DWORD SymGetOptions(void);
455
+ __stdcall DWORD SymSetOptions(DWORD SymOptions __attribute__((in)));
456
+ __stdcall BOOL SymInitialize(HANDLE hProcess __attribute__((in)), PSTR UserSearchPath __attribute__((in)), BOOL fInvadeProcess __attribute__((in)));
457
+ __stdcall DWORD64 SymLoadModule64(HANDLE hProcess __attribute__((in)), HANDLE hFile __attribute__((in)), PSTR ImageName __attribute__((in)), PSTR ModuleName __attribute__((in)), DWORD64 BaseOfDll __attribute__((in)), DWORD SizeOfDll __attribute__((in)));
458
+ __stdcall BOOL SymSetSearchPath(HANDLE hProcess __attribute__((in)), PSTR SearchPathA __attribute__((in)));
459
+ __stdcall BOOL SymFromAddr(HANDLE hProcess __attribute__((in)), DWORD64 Address __attribute__((in)), PDWORD64 Displacement __attribute__((out)), PSYMBOL_INFO Symbol __attribute__((in)) __attribute__((out)));
460
+ __stdcall BOOL SymEnumSymbols(HANDLE hProcess __attribute__((in)), ULONG64 BaseOfDll __attribute__((in)), PCSTR Mask __attribute__((in)), PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback __attribute__((in)), PVOID UserContext __attribute__((in)));
461
+ EOS
462
+
463
+
464
+ puts 'run_init'
465
+ l.run_init
466
+
467
+ puts 'sym_init'
468
+ dl.syminitialize(42, 0, 0)
469
+ puts 'sym_setopt'
470
+ dl.symsetoptions(dl.symgetoptions|dl::SYMOPT_DEFERRED_LOADS|dl::SYMOPT_NO_PROMPTS)
471
+ puts 'sym_setsearch'
472
+ sympath = ENV['_NT_SYMBOL_PATH'] || 'srv**/tmp/symbols*http://msdl.microsoft.com/download/symbols'
473
+ dl.symsetsearchpath(42, sympath)
474
+
475
+ puts 'sym_loadmod'
476
+ tg = PeLdr.new('kernel32.dll')
477
+ dl.symloadmodule64(42, 0, 0, 0, tg.load_address, 0)
478
+
479
+ puts 'walk'
480
+ symstruct = [0x58].pack('L') + 0.chr*4*19 + [512].pack('L') # sizeofstruct, ..., nameszmax
481
+ text = tg.pe.sections.find { |s| s.name == '.text' }
482
+ # SymEnumSymbols
483
+ text.rawsize.times { |o|
484
+ sym = symstruct + 0.chr*512 # name concat'ed after the struct
485
+ off = 0.chr*8
486
+ if dl.symfromaddr(42, tg.load_address+text.virtaddr+o, off, sym) and off.unpack('L').first == 0
487
+ symnamelen = sym[19*4, 4].unpack('L').first
488
+ puts ' %x %s' % [text.virtaddr+o, sym[0x54, symnamelen].inspect]
489
+ break
490
+ end
491
+ puts ' %x/%x' % [o, text.rawsize] if $VERBOSE and o & 0xffff == 0
492
+ }
493
+ puts
494
+ end