turborex 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +38 -0
  4. data/README.rdoc +19 -0
  5. data/examples/alpc_client.rb +15 -0
  6. data/examples/alpc_server.rb +14 -0
  7. data/examples/com_client.rb +19 -0
  8. data/examples/com_finder.rb +39 -0
  9. data/examples/create_instance.rb +15 -0
  10. data/examples/cstruct.rb +19 -0
  11. data/examples/find_com_client_calls.rb +16 -0
  12. data/examples/find_rpc_security_callback.rb +12 -0
  13. data/examples/rpc_finder.rb +117 -0
  14. data/examples/scan_exports.rb +5 -0
  15. data/examples/scan_imports.rb +5 -0
  16. data/examples/tinysdk.rb +17 -0
  17. data/lib/turborex.rb +21 -0
  18. data/lib/turborex/cstruct.rb +565 -0
  19. data/lib/turborex/cstruct/struct_helper.rb +7 -0
  20. data/lib/turborex/exception.rb +65 -0
  21. data/lib/turborex/fuzzer.rb +204 -0
  22. data/lib/turborex/fuzzer/containers.rb +115 -0
  23. data/lib/turborex/fuzzer/coverage.rb +67 -0
  24. data/lib/turborex/fuzzer/mutators.rb +25 -0
  25. data/lib/turborex/fuzzer/seed.rb +30 -0
  26. data/lib/turborex/monkey.rb +11 -0
  27. data/lib/turborex/msrpc.rb +14 -0
  28. data/lib/turborex/msrpc/decompiler.rb +244 -0
  29. data/lib/turborex/msrpc/midl.rb +747 -0
  30. data/lib/turborex/msrpc/ndrtype.rb +167 -0
  31. data/lib/turborex/msrpc/rpcbase.rb +777 -0
  32. data/lib/turborex/msrpc/rpcfinder.rb +1426 -0
  33. data/lib/turborex/msrpc/utils.rb +70 -0
  34. data/lib/turborex/pefile.rb +8 -0
  35. data/lib/turborex/pefile/pe.rb +61 -0
  36. data/lib/turborex/pefile/scanner.rb +82 -0
  37. data/lib/turborex/utils.rb +321 -0
  38. data/lib/turborex/windows.rb +402 -0
  39. data/lib/turborex/windows/alpc.rb +844 -0
  40. data/lib/turborex/windows/com.rb +266 -0
  41. data/lib/turborex/windows/com/client.rb +84 -0
  42. data/lib/turborex/windows/com/com_finder.rb +330 -0
  43. data/lib/turborex/windows/com/com_registry.rb +100 -0
  44. data/lib/turborex/windows/com/interface.rb +522 -0
  45. data/lib/turborex/windows/com/utils.rb +210 -0
  46. data/lib/turborex/windows/constants.rb +82 -0
  47. data/lib/turborex/windows/process.rb +56 -0
  48. data/lib/turborex/windows/security.rb +12 -0
  49. data/lib/turborex/windows/security/ace.rb +76 -0
  50. data/lib/turborex/windows/security/acl.rb +25 -0
  51. data/lib/turborex/windows/security/security_descriptor.rb +118 -0
  52. data/lib/turborex/windows/tinysdk.rb +89 -0
  53. data/lib/turborex/windows/utils.rb +138 -0
  54. data/resources/headers/alpc/ntdef.h +72 -0
  55. data/resources/headers/alpc/ntlpcapi.h +1014 -0
  56. data/resources/headers/rpc/common.h +162 -0
  57. data/resources/headers/rpc/guiddef.h +191 -0
  58. data/resources/headers/rpc/internal_ndrtypes.h +262 -0
  59. data/resources/headers/rpc/rpc.h +10 -0
  60. data/resources/headers/rpc/rpcdce.h +266 -0
  61. data/resources/headers/rpc/rpcdcep.h +187 -0
  62. data/resources/headers/rpc/rpcndr.h +39 -0
  63. data/resources/headers/rpc/v4_x64/rpcinternals.h +154 -0
  64. data/resources/headers/rpc/wintype.h +517 -0
  65. data/resources/headers/tinysdk/tinysdk.h +5 -0
  66. data/resources/headers/tinysdk/tinysdk/comdef.h +645 -0
  67. data/resources/headers/tinysdk/tinysdk/dbghelp.h +118 -0
  68. data/resources/headers/tinysdk/tinysdk/guiddef.h +194 -0
  69. data/resources/headers/tinysdk/tinysdk/memoryapi.h +12 -0
  70. data/resources/headers/tinysdk/tinysdk/poppack.h +12 -0
  71. data/resources/headers/tinysdk/tinysdk/pshpack4.h +13 -0
  72. data/resources/headers/tinysdk/tinysdk/winnt.h +1059 -0
  73. data/resources/headers/tinysdk/tinysdk/wintype.h +326 -0
  74. metadata +290 -0
@@ -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.
@@ -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
+
@@ -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
@@ -0,0 +1,5 @@
1
+ require 'turborex'
2
+
3
+ pelist = TurboRex::MSRPC::RPCFinder::ImageFinder.glob('C://windows/system32', ['.dll'])
4
+ res = TurboRex::Windows::Utils.find_export_func('GetProxyDllInfo', pelist)
5
+ puts res
@@ -0,0 +1,5 @@
1
+ require 'turborex'
2
+
3
+ pelist = TurboRex::MSRPC::RPCFinder::ImageFinder.glob('C://windows/system32', ['.dll'])
4
+ res = TurboRex::Windows::Utils.find_import_func('CoCreateInstance', pelist)
5
+ puts res
@@ -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
@@ -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