turborex 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +38 -0
- data/README.rdoc +19 -0
- data/examples/alpc_client.rb +15 -0
- data/examples/alpc_server.rb +14 -0
- data/examples/com_client.rb +19 -0
- data/examples/com_finder.rb +39 -0
- data/examples/create_instance.rb +15 -0
- data/examples/cstruct.rb +19 -0
- data/examples/find_com_client_calls.rb +16 -0
- data/examples/find_rpc_security_callback.rb +12 -0
- data/examples/rpc_finder.rb +117 -0
- data/examples/scan_exports.rb +5 -0
- data/examples/scan_imports.rb +5 -0
- data/examples/tinysdk.rb +17 -0
- data/lib/turborex.rb +21 -0
- data/lib/turborex/cstruct.rb +565 -0
- data/lib/turborex/cstruct/struct_helper.rb +7 -0
- data/lib/turborex/exception.rb +65 -0
- data/lib/turborex/fuzzer.rb +204 -0
- data/lib/turborex/fuzzer/containers.rb +115 -0
- data/lib/turborex/fuzzer/coverage.rb +67 -0
- data/lib/turborex/fuzzer/mutators.rb +25 -0
- data/lib/turborex/fuzzer/seed.rb +30 -0
- data/lib/turborex/monkey.rb +11 -0
- data/lib/turborex/msrpc.rb +14 -0
- data/lib/turborex/msrpc/decompiler.rb +244 -0
- data/lib/turborex/msrpc/midl.rb +747 -0
- data/lib/turborex/msrpc/ndrtype.rb +167 -0
- data/lib/turborex/msrpc/rpcbase.rb +777 -0
- data/lib/turborex/msrpc/rpcfinder.rb +1426 -0
- data/lib/turborex/msrpc/utils.rb +70 -0
- data/lib/turborex/pefile.rb +8 -0
- data/lib/turborex/pefile/pe.rb +61 -0
- data/lib/turborex/pefile/scanner.rb +82 -0
- data/lib/turborex/utils.rb +321 -0
- data/lib/turborex/windows.rb +402 -0
- data/lib/turborex/windows/alpc.rb +844 -0
- data/lib/turborex/windows/com.rb +266 -0
- data/lib/turborex/windows/com/client.rb +84 -0
- data/lib/turborex/windows/com/com_finder.rb +330 -0
- data/lib/turborex/windows/com/com_registry.rb +100 -0
- data/lib/turborex/windows/com/interface.rb +522 -0
- data/lib/turborex/windows/com/utils.rb +210 -0
- data/lib/turborex/windows/constants.rb +82 -0
- data/lib/turborex/windows/process.rb +56 -0
- data/lib/turborex/windows/security.rb +12 -0
- data/lib/turborex/windows/security/ace.rb +76 -0
- data/lib/turborex/windows/security/acl.rb +25 -0
- data/lib/turborex/windows/security/security_descriptor.rb +118 -0
- data/lib/turborex/windows/tinysdk.rb +89 -0
- data/lib/turborex/windows/utils.rb +138 -0
- data/resources/headers/alpc/ntdef.h +72 -0
- data/resources/headers/alpc/ntlpcapi.h +1014 -0
- data/resources/headers/rpc/common.h +162 -0
- data/resources/headers/rpc/guiddef.h +191 -0
- data/resources/headers/rpc/internal_ndrtypes.h +262 -0
- data/resources/headers/rpc/rpc.h +10 -0
- data/resources/headers/rpc/rpcdce.h +266 -0
- data/resources/headers/rpc/rpcdcep.h +187 -0
- data/resources/headers/rpc/rpcndr.h +39 -0
- data/resources/headers/rpc/v4_x64/rpcinternals.h +154 -0
- data/resources/headers/rpc/wintype.h +517 -0
- data/resources/headers/tinysdk/tinysdk.h +5 -0
- data/resources/headers/tinysdk/tinysdk/comdef.h +645 -0
- data/resources/headers/tinysdk/tinysdk/dbghelp.h +118 -0
- data/resources/headers/tinysdk/tinysdk/guiddef.h +194 -0
- data/resources/headers/tinysdk/tinysdk/memoryapi.h +12 -0
- data/resources/headers/tinysdk/tinysdk/poppack.h +12 -0
- data/resources/headers/tinysdk/tinysdk/pshpack4.h +13 -0
- data/resources/headers/tinysdk/tinysdk/winnt.h +1059 -0
- data/resources/headers/tinysdk/tinysdk/wintype.h +326 -0
- metadata +290 -0
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# TurboRex
|
2
|
+
---------------------
|
3
|
+
TurboRex is a Ruby gem for exploring MSRPC and COM. It is mainly a proof of concept for the topic "Automated Hunting for Cross-Server Xrefs in Microsoft RPC and COM" on Code Blue 2020.
|
4
|
+
|
5
|
+
|
6
|
+
## Author
|
7
|
+
Exist@SycloverSecurity
|
8
|
+
|
9
|
+
## Features
|
10
|
+
---------------------
|
11
|
+
* MSRPC server/client routines finder
|
12
|
+
* COM interface methods finder
|
13
|
+
* COM client finder(Not very useful)
|
14
|
+
* ALPC server/client
|
15
|
+
* COM client
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
------------------
|
19
|
+
To install Turborex, run
|
20
|
+
```
|
21
|
+
gem install turborex
|
22
|
+
```
|
23
|
+
And then [install Metasm](https://github.com/jjyg/metasm/blob/master/INSTALL), please DON'T use the old version of Metasm hosted by Rubygems
|
24
|
+
|
25
|
+
## Examples
|
26
|
+
-----------------
|
27
|
+
Please take a look at the examples directory.
|
28
|
+
|
29
|
+
|
30
|
+
## Troubleshooting
|
31
|
+
-----------------
|
32
|
+
#### It is too slow, especially when searching for RPC client routines
|
33
|
+
There are many reasons for this result, such as my poor code quality, and the Ruby interpreter runs slower on Windows than Linux. There is a trick that can greatly increase the speed without changing too much code: running in WSL. But I did not fully test whether it is available in WSL, it may be necessary to modify the core library code.
|
34
|
+
|
35
|
+
|
36
|
+
## License
|
37
|
+
-----------------
|
38
|
+
See this license at LICENSE file.
|
data/README.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= turborex
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Contributing to turborex
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
9
|
+
* Fork the project.
|
10
|
+
* Start a feature/bugfix branch.
|
11
|
+
* Commit and push until you are happy with your contribution.
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2019 dkk. See LICENSE.txt for
|
18
|
+
further details.
|
19
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'turborex'
|
2
|
+
|
3
|
+
endpoint = ARGV[0]
|
4
|
+
message = ARGV[1]
|
5
|
+
|
6
|
+
unless endpoint.start_with?("\\RPC Control")
|
7
|
+
endpoint = "\\RPC Control\\#{endpoint}"
|
8
|
+
end
|
9
|
+
|
10
|
+
client = TurboRex::Windows::ALPC::Client.new endpoint
|
11
|
+
server, msg = client.connect do |server|
|
12
|
+
a = server.send_recv ARGV[1], recv_attr: TurboRex::Windows::ALPC::MessageAttribute.new.struct
|
13
|
+
puts a.message_id
|
14
|
+
puts a.payload
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'turborex'
|
2
|
+
|
3
|
+
endpoint = ARGV[0]
|
4
|
+
|
5
|
+
unless endpoint.start_with?("\\RPC Control")
|
6
|
+
endpoint = "\\RPC Control\\#{endpoint}"
|
7
|
+
end
|
8
|
+
|
9
|
+
server = TurboRex::Windows::ALPC::Server.new endpoint
|
10
|
+
server.run do |client|
|
11
|
+
m = client.gets
|
12
|
+
print "Enter the response message: "
|
13
|
+
client.puts STDIN.gets.chomp, m.message_id
|
14
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'turborex'
|
2
|
+
|
3
|
+
client = TurboRex::Windows::COM::Client.new('E60687F7-01A1-40AA-86AC-DB1CBF673334')
|
4
|
+
interface = TurboRex::Windows::COM::Interface.define_interface('5B311480-E5CE-4325-90CD-586C1A123FD3', {
|
5
|
+
Proc5: 'HRESULT Proc5(void *this, GUID* p0, wchar_t* p1, void* p2);',
|
6
|
+
Proc6: 'HRESULT Proc6(void *this, GUID* p0);',
|
7
|
+
Proc7: 'HRESULT Proc7(void *this, void* p0, int p1, wchar_t* p2);'
|
8
|
+
}, TurboRex::Windows::COM::Interface::IClassFactory)
|
9
|
+
client.create_instance cls_context: TurboRex::Windows::COM::CLSCTX_LOCAL_SERVER, interface: interface
|
10
|
+
|
11
|
+
|
12
|
+
_iid = "{5B311480-E5CE-4325-90CD-586C1A123FD3}"
|
13
|
+
pstr_iid = TurboRex::Windows::Win32API.alloc_c_ary('OLECHAR', _iid.chars.push(0).map{|c|c.ord})
|
14
|
+
piid = TurboRex::Windows::Win32API.alloc_c_struct('CLSID')
|
15
|
+
TurboRex::Windows::Win32API.clsidfromstring(pstr_iid, piid)
|
16
|
+
|
17
|
+
interface.Proc6(piid)
|
18
|
+
interface.Release()
|
19
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'turborex'
|
3
|
+
require 'turborex/windows/com'
|
4
|
+
|
5
|
+
options = {}
|
6
|
+
OptionParser.new do |opts|
|
7
|
+
opts.banner = "Usage: com_finder.rb [options]"
|
8
|
+
|
9
|
+
opts.on("-c", "--clsid CLSID", "CoClass ID") do |c|
|
10
|
+
options[:clsid] = c
|
11
|
+
end
|
12
|
+
|
13
|
+
opts.on("-i", "--iid IID", "Interface ID") do |i|
|
14
|
+
options[:iid] = i
|
15
|
+
end
|
16
|
+
|
17
|
+
opts.on("-oop", "--out-of-process", "Using out-of-process finder") do |o|
|
18
|
+
options[:oop] = true
|
19
|
+
end
|
20
|
+
|
21
|
+
opts.on("-r", "--relative", "Specify whether the output address is rva") do |o|
|
22
|
+
options[:relative] = true
|
23
|
+
end
|
24
|
+
end.parse!
|
25
|
+
|
26
|
+
if options[:oop]
|
27
|
+
finder = TurboRex::Windows::COM::OutOfProcFinder.new(options[:clsid])
|
28
|
+
else
|
29
|
+
finder = TurboRex::Windows::COM::InProcFinder.new(options[:clsid])
|
30
|
+
end
|
31
|
+
|
32
|
+
methods = finder.locate_interface_methods(options[:iid], options[:relative])
|
33
|
+
puts "Module: #{methods[:module]}"
|
34
|
+
|
35
|
+
methods[:methods].each do |m|
|
36
|
+
puts "index: #{m[:index]}"
|
37
|
+
puts "Address: 0x#{(m[:va] || m[:rva]).to_s(16)}"
|
38
|
+
puts
|
39
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'turborex'
|
2
|
+
require 'turborex/windows/com'
|
3
|
+
|
4
|
+
clsid = ARGV[0]
|
5
|
+
ctx = ARGV[1]
|
6
|
+
client = TurboRex::Windows::COM::Client.new(clsid)
|
7
|
+
interface = TurboRex::Windows::COM::Interface::IUnknown.new
|
8
|
+
client.create_instance cls_context: ctx, interface: interface
|
9
|
+
|
10
|
+
objref = TurboRex::Windows::Win32API.decode_c_struct('OBJREF', interface.marshal_to_string)
|
11
|
+
pid = objref.u_standard.std.ipid.Data2
|
12
|
+
|
13
|
+
puts "Create COM object in process #{pid}."
|
14
|
+
interface.Release
|
15
|
+
|
data/examples/cstruct.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# My syntactic sugar for simply defining the structure.
|
2
|
+
# Does not support enum, union, the first letter of member variables must be lowercase
|
3
|
+
# This is a very crude DSL,I strongly recommend you to use Metasm's cparser instead of this
|
4
|
+
|
5
|
+
require 'turborex'
|
6
|
+
|
7
|
+
include TurboRex::CStruct
|
8
|
+
|
9
|
+
StructMgr = define_structs(arch: 'x64') do
|
10
|
+
struct MyStruct {
|
11
|
+
PVOID member1;
|
12
|
+
ULONG length;
|
13
|
+
};
|
14
|
+
end
|
15
|
+
|
16
|
+
s1 = StructMgr['MyStruct'].from_str "\x00\x00\x00\x00\x00\x00\x00\x01\xA\x00\x00\x00"
|
17
|
+
puts s1['length'].to_s.unpack('L')
|
18
|
+
|
19
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'turborex'
|
2
|
+
|
3
|
+
file = ARGV[0]
|
4
|
+
finder = TurboRex::Windows::COM::ClientFinder.new(file)
|
5
|
+
res = finder.find_client_call
|
6
|
+
|
7
|
+
if res
|
8
|
+
res.each do |r|
|
9
|
+
puts "Class ID: #{r[:clsid]}"
|
10
|
+
puts "Interface ID: #{r[:iid]}"
|
11
|
+
puts "Context: #{r[:context]}"
|
12
|
+
puts "Method index: #{r[:method_index]}"
|
13
|
+
puts "Call site: 0x#{r[:call_site].to_s(16)}"
|
14
|
+
puts
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'turborex'
|
2
|
+
|
3
|
+
include TurboRex::MSRPC::RPCFinder::StaticRPCBacktracer
|
4
|
+
|
5
|
+
dasm = _disassemble_executable_sections(Metasm::PE.decode_file(ARGV[0]))
|
6
|
+
res = bt_security_callback(dasm, true)
|
7
|
+
res.each do |r|
|
8
|
+
puts "Interface id: #{r[:interface_id]}"
|
9
|
+
puts "Callback address: 0x#{r[:callback].to_s(16)}"
|
10
|
+
puts "Flags: #{r[:flags]}"
|
11
|
+
puts
|
12
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'turborex'
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
|
5
|
+
options = {}
|
6
|
+
OptionParser.new do |opts|
|
7
|
+
opts.banner = "Usage: rpc_finder.rb [options]"
|
8
|
+
|
9
|
+
opts.on("-f", "--file FILE", "File") do |f|
|
10
|
+
options[:file] = f
|
11
|
+
end
|
12
|
+
|
13
|
+
opts.on("-d", "--directory DIRECTORY", "Specify search directory") do |d|
|
14
|
+
options[:directory] = d
|
15
|
+
end
|
16
|
+
|
17
|
+
opts.on("-e", "--extension EXTENSION", "Specify extension") do |e|
|
18
|
+
options[:extension] = e
|
19
|
+
end
|
20
|
+
|
21
|
+
opts.on("", "--only-client", "Search client only") do |o|
|
22
|
+
options[:only_client] = o
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on("", "--find-client", "Finding client") do |f|
|
26
|
+
options[:find_client] = f
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on("", "--only-server", "Search server only") do |s|
|
30
|
+
options[:only_server] = s
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on("-csx", "--csx", "Search cross-server xrefs") do |x|
|
34
|
+
options[:csx] = x
|
35
|
+
end
|
36
|
+
end.parse!
|
37
|
+
|
38
|
+
def solve_cross_server_xrefs(finder)
|
39
|
+
finder.draw_ifs_xrefs
|
40
|
+
finder.server_interfaces.each do |si|
|
41
|
+
si.xrefs_from.each do |xref|
|
42
|
+
ci = xref[0]
|
43
|
+
call_info = xref[1]
|
44
|
+
|
45
|
+
puts "Server Interface Id: #{si.interface_id}"
|
46
|
+
puts "Client Interface Id: #{ci.interface_id}"
|
47
|
+
|
48
|
+
call_info.each do |c|
|
49
|
+
puts "Found path:"
|
50
|
+
puts " From: #{c[:caller].map{|r|'0x'+r.addr.to_s(16)}.join(', ')}"
|
51
|
+
puts " To: 0x#{c[:called].addr.to_s(16)}"
|
52
|
+
puts " Proc Number: #{c[:called].proc_num}"
|
53
|
+
puts "------------------------------------------------------------"
|
54
|
+
puts
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
#pelist = TurboRex::MSRPC::RPCFinder::ImageFinder.glob('C://windows/system32', ['.dll'])
|
61
|
+
if options[:file]
|
62
|
+
pelist = [options[:file]]
|
63
|
+
elsif options[:directory] && options[:extension]
|
64
|
+
pelist = TurboRex::MSRPC::RPCFinder::ImageFinder.glob(options[:directory], options[:extension].split(',').map {|e| e.strip})
|
65
|
+
else
|
66
|
+
raise "The file path must be specified"
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
c = TurboRex::MSRPC::RPCFinder::Collection.new
|
71
|
+
pelist.each do |pe|
|
72
|
+
f = File.new(pe)
|
73
|
+
|
74
|
+
begin
|
75
|
+
finder = TurboRex::MSRPC::RPCFinder::ImageFinder.new pe, collection_proxy: c
|
76
|
+
rescue StandardError => e
|
77
|
+
next
|
78
|
+
end
|
79
|
+
|
80
|
+
res = finder.auto_find do
|
81
|
+
if options[:only_client]
|
82
|
+
only_client
|
83
|
+
elsif options[:only_server]
|
84
|
+
only_server
|
85
|
+
else
|
86
|
+
only_server
|
87
|
+
end
|
88
|
+
|
89
|
+
if options[:find_client]
|
90
|
+
find_client_routines
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
finder.server_interfaces.each do |si|
|
95
|
+
puts "Server Interface ID: #{si.interface_id}"
|
96
|
+
puts "Server Routines: "
|
97
|
+
si.routines.each_with_index do |r, i|
|
98
|
+
puts "Index: #{i} - 0x#{r.addr.to_s(16)}"
|
99
|
+
end
|
100
|
+
puts
|
101
|
+
end
|
102
|
+
|
103
|
+
finder.client_interfaces.each do |ci|
|
104
|
+
puts "Client Interface ID: #{ci.interface_id}"
|
105
|
+
if options[:find_client]
|
106
|
+
puts "Client Routines: "
|
107
|
+
ci.routines.each do |r|
|
108
|
+
puts "Procedure Number: #{r.proc_num} - 0x#{r.addr.to_s(16)}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
puts
|
112
|
+
end
|
113
|
+
|
114
|
+
if options[:csx]
|
115
|
+
solve_cross_server_xrefs(finder)
|
116
|
+
end
|
117
|
+
end
|
data/examples/tinysdk.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'turborex'
|
2
|
+
|
3
|
+
sdk = TurboRex::Windows::TinySDK.instance
|
4
|
+
sdk.load
|
5
|
+
|
6
|
+
sdk.np.parse <<-EOS
|
7
|
+
typedef struct tagBIND_OPTS {
|
8
|
+
DWORD cbStruct;
|
9
|
+
DWORD grfFlags;
|
10
|
+
DWORD grfMode;
|
11
|
+
DWORD dwTickCountDeadline;
|
12
|
+
} BIND_OPTS, *LPBIND_OPTS;
|
13
|
+
EOS
|
14
|
+
|
15
|
+
bind_opts = sdk.np.alloc_c_struct('BIND_OPTS')
|
16
|
+
bind_opts.grfMode = 1
|
17
|
+
puts bind_opts.str
|
data/lib/turborex.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module TurboRex
|
2
|
+
def self.root
|
3
|
+
File.expand_path('../..', __FILE__)
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'os'
|
7
|
+
require 'securerandom'
|
8
|
+
require 'parallel' unless ::OS.windows?
|
9
|
+
require 'metasm'
|
10
|
+
require 'turborex/monkey'
|
11
|
+
require 'turborex/utils'
|
12
|
+
require 'turborex/exception'
|
13
|
+
require 'turborex/windows'
|
14
|
+
require 'turborex/fuzzer'
|
15
|
+
require 'turborex/pefile'
|
16
|
+
require 'turborex/msrpc'
|
17
|
+
require 'turborex/cstruct'
|
18
|
+
|
19
|
+
|
20
|
+
include TurboRex::Utils
|
21
|
+
end
|
@@ -0,0 +1,565 @@
|
|
1
|
+
require 'turborex/cstruct/struct_helper'
|
2
|
+
require 'docile'
|
3
|
+
require 'rex/struct2'
|
4
|
+
|
5
|
+
module RefineAllocCStruct
|
6
|
+
refine Metasm::C::AllocCStruct do
|
7
|
+
def to_string # Warning: must be changed
|
8
|
+
self.str[self.stroff, self.sizeof]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module TurboRex
|
14
|
+
module CStruct
|
15
|
+
extend TurboRex::CStruct::Helper
|
16
|
+
|
17
|
+
Docile::FallbackContextProxy.class_eval do # Monkey patch
|
18
|
+
NON_FALLBACK_METHODS = Set[:class, :self, :respond_to?, :instance_of?]
|
19
|
+
|
20
|
+
def initialize(receiver, fallback)
|
21
|
+
@__receiver__ = receiver
|
22
|
+
@__fallback__ = fallback
|
23
|
+
|
24
|
+
# Enables calling DSL methods from helper methods in the block's context
|
25
|
+
unless fallback.respond_to?(:method_missing)
|
26
|
+
# NOTE: There's no {#define_singleton_method} on Ruby 1.8.x
|
27
|
+
singleton_class = (
|
28
|
+
class << fallback;
|
29
|
+
self;
|
30
|
+
end)
|
31
|
+
|
32
|
+
# instrument {#method_missing} on the block's context to fallback to
|
33
|
+
# the DSL object. This allows helper methods in the block's context to
|
34
|
+
# contain calls to methods on the DSL object.
|
35
|
+
singleton_class.
|
36
|
+
send(:define_method, :method_missing) do |method, *args, &block|
|
37
|
+
m = method.to_sym
|
38
|
+
if !NON_FALLBACK_METHODS.include?(m) && !fallback.respond_to?(m) && receiver.respond_to?(m) || receiver.respond_to?(:method_missing)
|
39
|
+
receiver.__send__(method.to_sym, *args, &block)
|
40
|
+
else
|
41
|
+
super(method, *args, &block)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# instrument a helper method to remove the above instrumentation
|
46
|
+
singleton_class.
|
47
|
+
send(:define_method, :__docile_undo_fallback__) do
|
48
|
+
singleton_class.send(:remove_method, :method_missing)
|
49
|
+
singleton_class.send(:remove_method, :__docile_undo_fallback__)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
::Rex::Struct2::CStruct.class_eval do
|
56
|
+
attr_reader :name_table
|
57
|
+
|
58
|
+
def offset(struct, index, base_offset = 0)
|
59
|
+
if index == 0
|
60
|
+
return index
|
61
|
+
end
|
62
|
+
|
63
|
+
offset = 0
|
64
|
+
|
65
|
+
(0...index).each do |i|
|
66
|
+
offset += struct[i].slength
|
67
|
+
end
|
68
|
+
|
69
|
+
base_offset + offset
|
70
|
+
end
|
71
|
+
|
72
|
+
def padding_align(base_offset = 0, pack = nil)
|
73
|
+
i = 0
|
74
|
+
|
75
|
+
loop do
|
76
|
+
current_offset = offset(self, i, base_offset)
|
77
|
+
if self[i].is_a? Rex::Struct2::CStruct
|
78
|
+
|
79
|
+
self[i].pack(@pack)
|
80
|
+
self[i].padding_align(@pack)
|
81
|
+
#break if self[i + 1] == nil
|
82
|
+
#binding.pry if @pack == 8
|
83
|
+
|
84
|
+
padding = calc_padding(self[i].self_align, current_offset, @pack)
|
85
|
+
else
|
86
|
+
padding = calc_padding(self[i].slength, current_offset, @pack)
|
87
|
+
end
|
88
|
+
|
89
|
+
if padding == 0
|
90
|
+
break if self[i + 1] == nil
|
91
|
+
|
92
|
+
i += 1
|
93
|
+
next
|
94
|
+
end
|
95
|
+
|
96
|
+
s = Rex::Struct2::CStructTemplate.new
|
97
|
+
padding.times do
|
98
|
+
s.template << ['uint8', 'padding', 0]
|
99
|
+
end
|
100
|
+
|
101
|
+
insert(i, s.make_struct)
|
102
|
+
|
103
|
+
i += 1
|
104
|
+
break if self[i + 1] == nil
|
105
|
+
i += 1
|
106
|
+
end
|
107
|
+
|
108
|
+
self.trailing_padding(@pack)
|
109
|
+
end
|
110
|
+
|
111
|
+
def trailing_padding(pack = nil)
|
112
|
+
pack ||= @pack
|
113
|
+
trailing = self.slength % effective_align(pack)
|
114
|
+
if trailing != 0
|
115
|
+
s = Rex::Struct2::CStructTemplate.new
|
116
|
+
trailing.times do
|
117
|
+
s.template << ['uint8', 'padding', 0]
|
118
|
+
end
|
119
|
+
|
120
|
+
append(s.make_struct)
|
121
|
+
end
|
122
|
+
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
def get_member_length(arr = [])
|
127
|
+
self.each do |member|
|
128
|
+
#if member.is_a? Rex::Struct2::CStruct
|
129
|
+
# member.get_member_length(arr)
|
130
|
+
#else
|
131
|
+
arr << member.slength
|
132
|
+
#end
|
133
|
+
end
|
134
|
+
|
135
|
+
arr
|
136
|
+
end
|
137
|
+
|
138
|
+
def self_align
|
139
|
+
arr = []
|
140
|
+
|
141
|
+
self.each do |member|
|
142
|
+
if member.is_a? Rex::Struct2::CStruct
|
143
|
+
arr << member.get_member_self_align.max
|
144
|
+
else
|
145
|
+
arr << member.slength
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
arr.max
|
150
|
+
end
|
151
|
+
|
152
|
+
def get_member_self_align
|
153
|
+
arr = []
|
154
|
+
self.each do |member|
|
155
|
+
if member.is_a? Rex::Struct2::CStruct
|
156
|
+
arr << member.self_align
|
157
|
+
else
|
158
|
+
arr << member.slength
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
arr
|
163
|
+
end
|
164
|
+
|
165
|
+
def effective_align(pack = nil)
|
166
|
+
pack ||= @pack
|
167
|
+
effective_value = get_member_self_align.max
|
168
|
+
unless pack.nil?
|
169
|
+
effective_value = [effective_value, pack].min
|
170
|
+
end
|
171
|
+
|
172
|
+
return effective_value
|
173
|
+
end
|
174
|
+
|
175
|
+
def pack(n)
|
176
|
+
@pack = n
|
177
|
+
end
|
178
|
+
|
179
|
+
private def insert(index, obj)
|
180
|
+
elements.insert(index, obj)
|
181
|
+
@name_table.insert(index, 'padding')
|
182
|
+
end
|
183
|
+
|
184
|
+
private def append(obj)
|
185
|
+
elements.push(obj)
|
186
|
+
@name_table.push('padding')
|
187
|
+
end
|
188
|
+
|
189
|
+
private def calc_padding(length, offset, pack = nil)
|
190
|
+
if pack != nil && pack < length
|
191
|
+
length = pack
|
192
|
+
end
|
193
|
+
|
194
|
+
(length - (offset % length)) % length
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
::Rex::Struct2::CStructTemplate.class_eval do
|
199
|
+
def initialize(*tem)
|
200
|
+
self.template = tem
|
201
|
+
self.template_create_restraints = []
|
202
|
+
self.template_apply_restraint = []
|
203
|
+
@natural_align = nil
|
204
|
+
@pack = nil
|
205
|
+
end
|
206
|
+
|
207
|
+
def natural_align(align = true)
|
208
|
+
@natural_align = align
|
209
|
+
|
210
|
+
self
|
211
|
+
end
|
212
|
+
|
213
|
+
def pack(n)
|
214
|
+
@pack = n
|
215
|
+
|
216
|
+
self
|
217
|
+
end
|
218
|
+
|
219
|
+
def make_struct(pack = nil, natural_align = nil)
|
220
|
+
s = ::Rex::Struct2::CStruct.new(*self.template).
|
221
|
+
create_restraints(*self.template_create_restraints).
|
222
|
+
apply_restraint(*self.template_apply_restraint)
|
223
|
+
|
224
|
+
pack ||= @pack
|
225
|
+
natural_align ||= @natural_align
|
226
|
+
|
227
|
+
if pack
|
228
|
+
s.pack(pack)
|
229
|
+
end
|
230
|
+
|
231
|
+
if natural_align
|
232
|
+
s.padding_align
|
233
|
+
end
|
234
|
+
|
235
|
+
s
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def define_structs(opts = {}, str = nil, &block)
|
240
|
+
if block_given?
|
241
|
+
Docile.dsl_eval(StructMgr.new(opts), &block)
|
242
|
+
elsif opts[:native_parse] # parse with Metasm::C::Parser
|
243
|
+
opts.delete[:native_parse]
|
244
|
+
NativeParser.new(str, opts)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
class StructMgr
|
249
|
+
attr_reader :structs_table
|
250
|
+
|
251
|
+
def initialize(opts)
|
252
|
+
@structs_table = {}
|
253
|
+
@opts = opts
|
254
|
+
end
|
255
|
+
|
256
|
+
def [](name)
|
257
|
+
@structs_table[name.to_sym]
|
258
|
+
end
|
259
|
+
|
260
|
+
def build
|
261
|
+
self
|
262
|
+
end
|
263
|
+
|
264
|
+
def struct(obj)
|
265
|
+
@structs_table[obj.struct_name.to_sym] = obj
|
266
|
+
TurboRex::CStruct::CStructBuilder.create_method(obj.struct_name.to_sym) do |name|
|
267
|
+
self.s.template << ['template', name.to_s, obj.s]
|
268
|
+
end
|
269
|
+
|
270
|
+
self
|
271
|
+
end
|
272
|
+
|
273
|
+
def method_missing(m, *args, &block)
|
274
|
+
if block
|
275
|
+
arch = @opts[:arch] || 'x86'
|
276
|
+
return Docile.dsl_eval(CStructBuilder.new(arch), &block).build(m.to_s)
|
277
|
+
end
|
278
|
+
|
279
|
+
FieldsProxy.new m.to_s
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
class FieldsProxy
|
284
|
+
attr_reader :count
|
285
|
+
attr_reader :name
|
286
|
+
|
287
|
+
def initialize(name)
|
288
|
+
@name = name
|
289
|
+
@count = 1
|
290
|
+
@point_to = nil
|
291
|
+
end
|
292
|
+
|
293
|
+
def [](count)
|
294
|
+
@count = count.to_i
|
295
|
+
|
296
|
+
self
|
297
|
+
end
|
298
|
+
|
299
|
+
def to_s
|
300
|
+
@name.to_s
|
301
|
+
end
|
302
|
+
|
303
|
+
def point_to(cstruct)
|
304
|
+
@point_to = cstruct
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
class CStructBuilder
|
309
|
+
attr_reader :struct_name
|
310
|
+
attr_reader :s
|
311
|
+
|
312
|
+
def initialize(arch = 'x86')
|
313
|
+
@s = ::Rex::Struct2::CStructTemplate.new
|
314
|
+
@arch = arch
|
315
|
+
define_variable_length_type
|
316
|
+
end
|
317
|
+
|
318
|
+
def char(field, init_value = 0)
|
319
|
+
add_object('int8', field, init_value)
|
320
|
+
end
|
321
|
+
|
322
|
+
def uchar(field, init_value = 0)
|
323
|
+
add_object('uint8', field, init_value)
|
324
|
+
end
|
325
|
+
|
326
|
+
def short(field, init_value = 0, endian = 'v') # 'v' is little-endian, 'n' is big-endian
|
327
|
+
add_object('int16' + endian, field, init_value)
|
328
|
+
end
|
329
|
+
|
330
|
+
def ushort(field, init_value = 0, endian = 'v')
|
331
|
+
add_object('uint16' + endian, field, init_value)
|
332
|
+
end
|
333
|
+
|
334
|
+
def int(field, init_value = 0, endian = 'v')
|
335
|
+
add_object('int32' + endian, field, init_value)
|
336
|
+
end
|
337
|
+
|
338
|
+
def uint(field, init_value = 0, endian = 'v')
|
339
|
+
add_object('uint32' + endian, field, init_value)
|
340
|
+
end
|
341
|
+
|
342
|
+
def word(field, init_value = 0, endian = 'v')
|
343
|
+
add_object('uint16' + endian, field, init_value)
|
344
|
+
end
|
345
|
+
|
346
|
+
def dword(field, init_value = 0, endian = 'v')
|
347
|
+
add_object('uint32' + endian, field, init_value)
|
348
|
+
end
|
349
|
+
|
350
|
+
def int64(field, init_value = 0, endian = 'v')
|
351
|
+
add_object('int64' + endian, field, init_value)
|
352
|
+
end
|
353
|
+
|
354
|
+
def __int64(field, init_value = 0, endian = 'v')
|
355
|
+
add_object('int64' + endian, field, init_value)
|
356
|
+
end
|
357
|
+
|
358
|
+
def uint64(field, init_value = 0, endian = 'v')
|
359
|
+
add_object('uint64' + endian, field, init_value)
|
360
|
+
end
|
361
|
+
|
362
|
+
def pvoid(field, init_value = 0, endian = 'v')
|
363
|
+
case @arch
|
364
|
+
when 'x86'
|
365
|
+
add_object('uint32' + endian, field, init_value)
|
366
|
+
when 'x64'
|
367
|
+
add_object('uint64' + endian, field, init_value)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
# https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
372
|
+
# https://docs.microsoft.com/en-us/cpp/cpp/data-type-ranges?redirectedfrom=MSDN&view=vs-2019
|
373
|
+
# The data type range follows Microsoft VC/C++ compiler
|
374
|
+
# data model llp64 on 64-bit arch
|
375
|
+
alias ATOM word
|
376
|
+
alias WORD word
|
377
|
+
alias DWORD dword
|
378
|
+
alias SHORT short
|
379
|
+
alias QWORD uint64
|
380
|
+
alias BYTE uchar
|
381
|
+
alias BOOLEAN BYTE
|
382
|
+
alias BOOL int
|
383
|
+
alias CCHAR char
|
384
|
+
alias CHAR char
|
385
|
+
alias UCHAR uchar
|
386
|
+
alias COLORREF DWORD
|
387
|
+
alias DWORDLONG uint64
|
388
|
+
alias DWORD32 uint
|
389
|
+
alias DWORD64 uint64
|
390
|
+
alias HFILE int
|
391
|
+
alias UINT uint
|
392
|
+
alias INT int
|
393
|
+
alias INT8 char
|
394
|
+
alias INT16 short
|
395
|
+
alias INT32 int
|
396
|
+
alias INT64 int64
|
397
|
+
alias UINT8 uchar
|
398
|
+
alias UINT16 ushort
|
399
|
+
alias UINT32 uint
|
400
|
+
alias UINT64 uint64
|
401
|
+
alias LANGID WORD
|
402
|
+
alias LCID DWORD
|
403
|
+
alias LCTYPE DWORD
|
404
|
+
alias LGRPID DWORD
|
405
|
+
alias LONG32 int
|
406
|
+
alias LONG64 int64
|
407
|
+
alias LONG int
|
408
|
+
alias ULONG uint
|
409
|
+
alias ULONG32 uint
|
410
|
+
alias ULONG64 uint64
|
411
|
+
alias USHORT ushort
|
412
|
+
alias PVOID pvoid
|
413
|
+
alias HANDLE PVOID
|
414
|
+
|
415
|
+
|
416
|
+
def alias_singleton_method(alias_sym, method)
|
417
|
+
self.singleton_class.send(:alias_method, alias_sym, method)
|
418
|
+
end
|
419
|
+
|
420
|
+
def define_variable_length_type
|
421
|
+
arch_32? ? alias_singleton_method(:ULONG_PTR, :uint) : alias_singleton_method(:ULONG_PTR, :uint64)
|
422
|
+
arch_32? ? alias_singleton_method(:ULONG_PTR_T, :uint) : alias_singleton_method(:ULONG_PTR_T, :uint64)
|
423
|
+
end
|
424
|
+
|
425
|
+
def struct(&block)
|
426
|
+
yield
|
427
|
+
end
|
428
|
+
|
429
|
+
def add_object(type, field, init_value)
|
430
|
+
if field.count == 1
|
431
|
+
@s.template << [type, field.to_s, init_value]
|
432
|
+
else
|
433
|
+
struct_temp = ::Rex::Struct2::CStructTemplate.new
|
434
|
+
field.count.times do |i|
|
435
|
+
struct_temp.template << [type, field.to_s + '_' + i.to_s, init_value]
|
436
|
+
end
|
437
|
+
|
438
|
+
@s.template << ['template', field.to_s, struct_temp]
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
def build(name)
|
443
|
+
set_struct_name(name)
|
444
|
+
|
445
|
+
self
|
446
|
+
end
|
447
|
+
|
448
|
+
def self.create_method(method_name, &block)
|
449
|
+
define_method(method_name, &block)
|
450
|
+
end
|
451
|
+
|
452
|
+
def set_struct_name(name)
|
453
|
+
@struct_name = name
|
454
|
+
end
|
455
|
+
|
456
|
+
def make(opts = {})
|
457
|
+
@s.make_struct(opts[:pack], opts[:align])
|
458
|
+
end
|
459
|
+
|
460
|
+
def from_str(s)
|
461
|
+
struct = @s.make_struct
|
462
|
+
struct.from_s(s)
|
463
|
+
struct
|
464
|
+
end
|
465
|
+
|
466
|
+
private
|
467
|
+
|
468
|
+
def arch_32?
|
469
|
+
@arch == 'x86'
|
470
|
+
end
|
471
|
+
|
472
|
+
def arch_64?
|
473
|
+
!arch_32?
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
class NativeParser
|
478
|
+
attr_reader :parser
|
479
|
+
attr_reader :cpu
|
480
|
+
|
481
|
+
def initialize(str, opts={})
|
482
|
+
@cpu = opts[:cpu].new rescue nil || Metasm::Ia32.new
|
483
|
+
@parser = @cpu.new_cparser
|
484
|
+
|
485
|
+
if opts[:predefined] # TODO: more Predefined macros
|
486
|
+
if opts[:cpu] == Metasm::Ia32
|
487
|
+
@parser.lexer.define("_WIN32")
|
488
|
+
elsif opts[:cpu] == Metasm::X86_64
|
489
|
+
@parser.lexer.define("_WIN64")
|
490
|
+
@parser.llp64
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
@parser.send(opts[:data_model].to_s) if opts[:data_model]
|
495
|
+
|
496
|
+
if opts[:visual_studio]
|
497
|
+
@parser.prepare_visualstudio
|
498
|
+
end
|
499
|
+
|
500
|
+
if opts[:gcc]
|
501
|
+
@parser.prepare_gcc
|
502
|
+
end
|
503
|
+
|
504
|
+
@include_path = opts[:include_path] || []
|
505
|
+
perform_include_path
|
506
|
+
@parser.lexer.warn_redefinition = false
|
507
|
+
@parser.lexer.include_search_path = @include_path
|
508
|
+
if opts[:file]
|
509
|
+
@parser.parse_file opts[:file]
|
510
|
+
elsif str
|
511
|
+
@parser.parse str
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
def find_c_struct(name)
|
516
|
+
@parser.find_c_struct(name)
|
517
|
+
end
|
518
|
+
|
519
|
+
def find_c_type(name)
|
520
|
+
@parser.find_c_type(name)
|
521
|
+
end
|
522
|
+
|
523
|
+
def [](name)
|
524
|
+
NativeStructProxy.new(@parser, name)
|
525
|
+
end
|
526
|
+
|
527
|
+
def method_missing(m, *args, &block)
|
528
|
+
@parser.send(m, *args, &block)
|
529
|
+
end
|
530
|
+
|
531
|
+
private
|
532
|
+
|
533
|
+
def perform_include_path
|
534
|
+
@include_path += TurboRex::Windows.tinysdk.include_path
|
535
|
+
@include_path.uniq!
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
class NativeStructProxy
|
540
|
+
attr_reader :size
|
541
|
+
attr_reader :sizeof
|
542
|
+
attr_reader :name
|
543
|
+
attr_reader :struct
|
544
|
+
|
545
|
+
def initialize(parser, name)
|
546
|
+
@parser = parser
|
547
|
+
@name = name
|
548
|
+
@size = @sizeof = parser.sizeof(parser.find_c_struct(name))
|
549
|
+
end
|
550
|
+
|
551
|
+
def from_str(str, offset = 0)
|
552
|
+
struct = @parser.find_c_struct(@name)
|
553
|
+
@struct = ::Metasm::C::AllocCStruct.new(@parser, struct, str, offset)
|
554
|
+
end
|
555
|
+
|
556
|
+
def to_s
|
557
|
+
@struct.str
|
558
|
+
end
|
559
|
+
|
560
|
+
def method_missing(m, *args, &block)
|
561
|
+
@struct.send(m, *args, &block)
|
562
|
+
end
|
563
|
+
end
|
564
|
+
end
|
565
|
+
end
|