metasm 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/BUGS +11 -0
- data/CREDITS +17 -0
- data/README +270 -0
- data/TODO +114 -0
- data/doc/code_organisation.txt +146 -0
- data/doc/const_missing.txt +16 -0
- data/doc/core_classes.txt +75 -0
- data/doc/feature_list.txt +53 -0
- data/doc/index.txt +59 -0
- data/doc/install_notes.txt +170 -0
- data/doc/style.css +3 -0
- data/doc/use_cases.txt +18 -0
- data/lib/metasm.rb +80 -0
- data/lib/metasm/arm.rb +12 -0
- data/lib/metasm/arm/debug.rb +39 -0
- data/lib/metasm/arm/decode.rb +167 -0
- data/lib/metasm/arm/encode.rb +77 -0
- data/lib/metasm/arm/main.rb +75 -0
- data/lib/metasm/arm/opcodes.rb +177 -0
- data/lib/metasm/arm/parse.rb +130 -0
- data/lib/metasm/arm/render.rb +55 -0
- data/lib/metasm/compile_c.rb +1457 -0
- data/lib/metasm/dalvik.rb +8 -0
- data/lib/metasm/dalvik/decode.rb +196 -0
- data/lib/metasm/dalvik/main.rb +60 -0
- data/lib/metasm/dalvik/opcodes.rb +366 -0
- data/lib/metasm/decode.rb +213 -0
- data/lib/metasm/decompile.rb +2659 -0
- data/lib/metasm/disassemble.rb +2068 -0
- data/lib/metasm/disassemble_api.rb +1280 -0
- data/lib/metasm/dynldr.rb +1329 -0
- data/lib/metasm/encode.rb +333 -0
- data/lib/metasm/exe_format/a_out.rb +194 -0
- data/lib/metasm/exe_format/autoexe.rb +82 -0
- data/lib/metasm/exe_format/bflt.rb +189 -0
- data/lib/metasm/exe_format/coff.rb +455 -0
- data/lib/metasm/exe_format/coff_decode.rb +901 -0
- data/lib/metasm/exe_format/coff_encode.rb +1078 -0
- data/lib/metasm/exe_format/dex.rb +457 -0
- data/lib/metasm/exe_format/dol.rb +145 -0
- data/lib/metasm/exe_format/elf.rb +923 -0
- data/lib/metasm/exe_format/elf_decode.rb +979 -0
- data/lib/metasm/exe_format/elf_encode.rb +1375 -0
- data/lib/metasm/exe_format/macho.rb +827 -0
- data/lib/metasm/exe_format/main.rb +228 -0
- data/lib/metasm/exe_format/mz.rb +164 -0
- data/lib/metasm/exe_format/nds.rb +172 -0
- data/lib/metasm/exe_format/pe.rb +437 -0
- data/lib/metasm/exe_format/serialstruct.rb +246 -0
- data/lib/metasm/exe_format/shellcode.rb +114 -0
- data/lib/metasm/exe_format/xcoff.rb +167 -0
- data/lib/metasm/gui.rb +23 -0
- data/lib/metasm/gui/cstruct.rb +373 -0
- data/lib/metasm/gui/dasm_coverage.rb +199 -0
- data/lib/metasm/gui/dasm_decomp.rb +369 -0
- data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
- data/lib/metasm/gui/dasm_graph.rb +1354 -0
- data/lib/metasm/gui/dasm_hex.rb +543 -0
- data/lib/metasm/gui/dasm_listing.rb +599 -0
- data/lib/metasm/gui/dasm_main.rb +906 -0
- data/lib/metasm/gui/dasm_opcodes.rb +291 -0
- data/lib/metasm/gui/debug.rb +1228 -0
- data/lib/metasm/gui/gtk.rb +884 -0
- data/lib/metasm/gui/qt.rb +495 -0
- data/lib/metasm/gui/win32.rb +3004 -0
- data/lib/metasm/gui/x11.rb +621 -0
- data/lib/metasm/ia32.rb +14 -0
- data/lib/metasm/ia32/compile_c.rb +1523 -0
- data/lib/metasm/ia32/debug.rb +193 -0
- data/lib/metasm/ia32/decode.rb +1167 -0
- data/lib/metasm/ia32/decompile.rb +564 -0
- data/lib/metasm/ia32/encode.rb +314 -0
- data/lib/metasm/ia32/main.rb +233 -0
- data/lib/metasm/ia32/opcodes.rb +872 -0
- data/lib/metasm/ia32/parse.rb +327 -0
- data/lib/metasm/ia32/render.rb +91 -0
- data/lib/metasm/main.rb +1193 -0
- data/lib/metasm/mips.rb +11 -0
- data/lib/metasm/mips/compile_c.rb +7 -0
- data/lib/metasm/mips/decode.rb +253 -0
- data/lib/metasm/mips/encode.rb +51 -0
- data/lib/metasm/mips/main.rb +72 -0
- data/lib/metasm/mips/opcodes.rb +443 -0
- data/lib/metasm/mips/parse.rb +51 -0
- data/lib/metasm/mips/render.rb +43 -0
- data/lib/metasm/os/gnu_exports.rb +270 -0
- data/lib/metasm/os/linux.rb +1112 -0
- data/lib/metasm/os/main.rb +1686 -0
- data/lib/metasm/os/remote.rb +527 -0
- data/lib/metasm/os/windows.rb +2027 -0
- data/lib/metasm/os/windows_exports.rb +745 -0
- data/lib/metasm/parse.rb +876 -0
- data/lib/metasm/parse_c.rb +3938 -0
- data/lib/metasm/pic16c/decode.rb +42 -0
- data/lib/metasm/pic16c/main.rb +17 -0
- data/lib/metasm/pic16c/opcodes.rb +68 -0
- data/lib/metasm/ppc.rb +11 -0
- data/lib/metasm/ppc/decode.rb +264 -0
- data/lib/metasm/ppc/decompile.rb +251 -0
- data/lib/metasm/ppc/encode.rb +51 -0
- data/lib/metasm/ppc/main.rb +129 -0
- data/lib/metasm/ppc/opcodes.rb +410 -0
- data/lib/metasm/ppc/parse.rb +52 -0
- data/lib/metasm/preprocessor.rb +1277 -0
- data/lib/metasm/render.rb +130 -0
- data/lib/metasm/sh4.rb +8 -0
- data/lib/metasm/sh4/decode.rb +336 -0
- data/lib/metasm/sh4/main.rb +292 -0
- data/lib/metasm/sh4/opcodes.rb +381 -0
- data/lib/metasm/x86_64.rb +12 -0
- data/lib/metasm/x86_64/compile_c.rb +1025 -0
- data/lib/metasm/x86_64/debug.rb +59 -0
- data/lib/metasm/x86_64/decode.rb +268 -0
- data/lib/metasm/x86_64/encode.rb +264 -0
- data/lib/metasm/x86_64/main.rb +135 -0
- data/lib/metasm/x86_64/opcodes.rb +118 -0
- data/lib/metasm/x86_64/parse.rb +68 -0
- data/misc/bottleneck.rb +61 -0
- data/misc/cheader-findpppath.rb +58 -0
- data/misc/hexdiff.rb +74 -0
- data/misc/hexdump.rb +55 -0
- data/misc/metasm-all.rb +13 -0
- data/misc/objdiff.rb +47 -0
- data/misc/objscan.rb +40 -0
- data/misc/pdfparse.rb +661 -0
- data/misc/ppc_pdf2oplist.rb +192 -0
- data/misc/tcp_proxy_hex.rb +84 -0
- data/misc/txt2html.rb +440 -0
- data/samples/a.out.rb +31 -0
- data/samples/asmsyntax.rb +77 -0
- data/samples/bindiff.rb +555 -0
- data/samples/compilation-steps.rb +49 -0
- data/samples/cparser_makestackoffset.rb +55 -0
- data/samples/dasm-backtrack.rb +38 -0
- data/samples/dasmnavig.rb +318 -0
- data/samples/dbg-apihook.rb +228 -0
- data/samples/dbghelp.rb +143 -0
- data/samples/disassemble-gui.rb +102 -0
- data/samples/disassemble.rb +133 -0
- data/samples/dump_upx.rb +95 -0
- data/samples/dynamic_ruby.rb +1929 -0
- data/samples/elf_list_needed.rb +46 -0
- data/samples/elf_listexports.rb +33 -0
- data/samples/elfencode.rb +25 -0
- data/samples/exeencode.rb +128 -0
- data/samples/factorize-headers-elfimports.rb +77 -0
- data/samples/factorize-headers-peimports.rb +109 -0
- data/samples/factorize-headers.rb +43 -0
- data/samples/gdbclient.rb +583 -0
- data/samples/generate_libsigs.rb +102 -0
- data/samples/hotfix_gtk_dbg.rb +59 -0
- data/samples/install_win_env.rb +78 -0
- data/samples/lindebug.rb +924 -0
- data/samples/linux_injectsyscall.rb +95 -0
- data/samples/machoencode.rb +31 -0
- data/samples/metasm-shell.rb +91 -0
- data/samples/pe-hook.rb +69 -0
- data/samples/pe-ia32-cpuid.rb +203 -0
- data/samples/pe-mips.rb +35 -0
- data/samples/pe-shutdown.rb +78 -0
- data/samples/pe-testrelocs.rb +51 -0
- data/samples/pe-testrsrc.rb +24 -0
- data/samples/pe_listexports.rb +31 -0
- data/samples/peencode.rb +19 -0
- data/samples/peldr.rb +494 -0
- data/samples/preprocess-flatten.rb +19 -0
- data/samples/r0trace.rb +308 -0
- data/samples/rubstop.rb +399 -0
- data/samples/scan_pt_gnu_stack.rb +54 -0
- data/samples/scanpeexports.rb +62 -0
- data/samples/shellcode-c.rb +40 -0
- data/samples/shellcode-dynlink.rb +146 -0
- data/samples/source.asm +34 -0
- data/samples/struct_offset.rb +47 -0
- data/samples/testpe.rb +32 -0
- data/samples/testraw.rb +45 -0
- data/samples/win32genloader.rb +132 -0
- data/samples/win32hooker-advanced.rb +169 -0
- data/samples/win32hooker.rb +96 -0
- data/samples/win32livedasm.rb +33 -0
- data/samples/win32remotescan.rb +133 -0
- data/samples/wintrace.rb +92 -0
- data/tests/all.rb +8 -0
- data/tests/dasm.rb +39 -0
- data/tests/dynldr.rb +35 -0
- data/tests/encodeddata.rb +132 -0
- data/tests/ia32.rb +82 -0
- data/tests/mips.rb +116 -0
- data/tests/parse_c.rb +239 -0
- data/tests/preprocessor.rb +269 -0
- data/tests/x86_64.rb +62 -0
- metadata +255 -0
data/samples/pe-mips.rb
ADDED
@@ -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
|
+
}
|
data/samples/peencode.rb
ADDED
@@ -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
|
+
}
|
data/samples/peldr.rb
ADDED
@@ -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
|