elf-mithril 0.0.1 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/mithril/parser.rb +7 -7
- data/lib/mithril/policy.rb +3 -332
- data/lib/mithril/policy/default/hacks.rb +9 -0
- data/lib/mithril/policy/default/twostate.rb +59 -0
- data/lib/mithril/policy/defaults.rb +2 -0
- data/lib/mithril/policy/dsl.rb +338 -0
- data/lib/mithril/policy/inject_policy.rb +18 -0
- data/lib/mithril/version.rb +1 -1
- data/lib/mithril/writer.rb +5 -0
- data/mithril.gemspec +2 -2
- metadata +8 -3
data/lib/mithril/parser.rb
CHANGED
@@ -656,14 +656,14 @@ module Elf
|
|
656
656
|
@file.symbols.each {|staticsym|
|
657
657
|
@canonical_symbol[staticsym] = staticsym
|
658
658
|
next if staticsym.name == ""
|
659
|
-
next if staticsym.
|
660
|
-
dyn_by_name[staticsym.name].andand.each {|
|
661
|
-
next if
|
662
|
-
next if
|
663
|
-
@canonical_symbol[dynsym] =
|
659
|
+
next if staticsym.bind == STB::STB_LOCAL
|
660
|
+
dyn_by_name[staticsym.name].andand.each {|dynsym|
|
661
|
+
next if dynsym.sectoffset != staticsym.sectoffset
|
662
|
+
next if dynsym.section != staticsym.section
|
663
|
+
@canonical_symbol[dynsym] = staticsym
|
664
664
|
staticsym.is_dynamic = true
|
665
|
-
staticsym.gnu_version =
|
666
|
-
expect_value "Dynamic #{
|
665
|
+
staticsym.gnu_version = dynsym.gnu_version
|
666
|
+
expect_value "Dynamic #{dynsym.name} size", dynsym.size, staticsym.size
|
667
667
|
}
|
668
668
|
}
|
669
669
|
@dynsym.each {|sym|
|
data/lib/mithril/policy.rb
CHANGED
@@ -1,335 +1,6 @@
|
|
1
1
|
require 'bindata'
|
2
2
|
#Nothing to see here. For background, see Dartmouth College CS
|
3
3
|
#TR2013-727 (ELFbac)
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
"_elfp_#{file_name}_#{section_name}"
|
8
|
-
end
|
9
|
-
R = ElfFlags::Relocation
|
10
|
-
ELFP = ElfFlags::ElfPData
|
11
|
-
class Transition
|
12
|
-
attr_accessor :from, :to
|
13
|
-
end
|
14
|
-
class Call < Transition
|
15
|
-
attr_accessor :symbol
|
16
|
-
attr_accessor :parambytes,:returnbytes
|
17
|
-
def initialize(from,to, symbol, parambytes, returnbytes)
|
18
|
-
@from, @to, @symbol, @parambytes, @returnbytes = from,to, symbol, parambytes, returnbytes
|
19
|
-
end
|
20
|
-
def allows_return?
|
21
|
-
@returnbytes >= 0
|
22
|
-
end
|
23
|
-
end
|
24
|
-
class Data < Transition
|
25
|
-
attr_accessor :tag
|
26
|
-
attr_accessor :read,:write, :exec
|
27
|
-
def initialize(from,to,tag , read=false ,write=false,exec=false)
|
28
|
-
@from, @to, @tag, @read,@write,@exec = from,to,tag,read,write,exec
|
29
|
-
end
|
30
|
-
end
|
31
|
-
class MemoryRange
|
32
|
-
attr_accessor :low, :high
|
33
|
-
def initialize(from,to)
|
34
|
-
@low,@high = from,to
|
35
|
-
end
|
36
|
-
end
|
37
|
-
class Policy
|
38
|
-
attr_accessor :data, :calls, :start, :tags
|
39
|
-
attr_accessor :imported_symbols
|
40
|
-
def states
|
41
|
-
t = data.keys + data.values.map(&:keys).flatten + calls.map(&:from) +
|
42
|
-
calls.map(&:to)
|
43
|
-
t.uniq
|
44
|
-
end
|
45
|
-
def <<(*transitions)
|
46
|
-
transitions.each do |t|
|
47
|
-
if t.is_a? Data
|
48
|
-
x= data
|
49
|
-
x[t.from] ||= {}
|
50
|
-
x[t.from][t.to] ||= {}
|
51
|
-
x[t.from][t.to][t.tag] ||= t
|
52
|
-
x[t.from][t.to][t.tag].read ||= t.read
|
53
|
-
x[t.from][t.to][t.tag].write ||= t.write
|
54
|
-
x[t.from][t.to][t.tag].exec ||= t.exec
|
55
|
-
elsif t.is_a? Call
|
56
|
-
calls << t
|
57
|
-
else
|
58
|
-
raise ArgumentError.new "#{t.class} is not a valid transition"
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
def initialize
|
63
|
-
@data={}
|
64
|
-
@calls=[]
|
65
|
-
@states ={}
|
66
|
-
@tags = {}
|
67
|
-
@imported_symbols = {}
|
68
|
-
end
|
69
|
-
def resolve_reference(elffile, relocations,offset, ref)
|
70
|
-
if(ref.is_a? Integer)
|
71
|
-
ref.to_i
|
72
|
-
elsif(ref == "_dl_runtime_resolve") #HACK:HACK:HACK: I couldn't hack ld.so to fix this, so
|
73
|
-
#here comes a nasty hack
|
74
|
-
#note that the address of _dl_runtime_resolve is 16 bytes into PLT.GOT
|
75
|
-
if !elffile.symbols.include? "_elfp_hidden_trampolineaddr"
|
76
|
-
pltgot = elffile.dynamic.pltgot or raise RuntimeError.new "No plt.got for _dl_runtime_resolve hack"
|
77
|
-
elffile.symbols << Elf::Symbol.new("_elfp_hidden_trampolineaddr", pltgot,STT::STT_OBJECT,16, STB::STB_LOCAL,8)
|
78
|
-
end
|
79
|
-
symb = elffile.symbols["_elfp_hidden_trampolineaddr"]
|
80
|
-
relocations << Elf::Relocation.new.tap{|x|
|
81
|
-
x.type = R::R_X86_64_COPY
|
82
|
-
x.offset = offset
|
83
|
-
x.symbol = symb
|
84
|
-
x.is_dynamic = true
|
85
|
-
x.addend = 0
|
86
|
-
}
|
87
|
-
0xDEADBEEF
|
88
|
-
else
|
89
|
-
raise RuntimeError.new "Symbol #{ref} not found" unless elffile.symbols.include? ref
|
90
|
-
relocations << Elf::Relocation.new.tap{|x|
|
91
|
-
x.type = R::R_X86_64_64
|
92
|
-
x.offset = offset
|
93
|
-
x.symbol = elffile.symbols[ref]
|
94
|
-
x.is_dynamic = true
|
95
|
-
x.addend = 0
|
96
|
-
}
|
97
|
-
2**64-1
|
98
|
-
end
|
99
|
-
end
|
100
|
-
def resolve_size(elffile,relocations, offset, ref)
|
101
|
-
if(ref.is_a? Integer)
|
102
|
-
ref.to_i
|
103
|
-
else
|
104
|
-
raise RuntimeError.new "Symbol #{ref} not found" unless elffile.symbols.include? ref
|
105
|
-
relocations << Elf::Relocation.new.tap{|x|
|
106
|
-
x.type = R::R_X86_64_SIZE64
|
107
|
-
x.offset = offset
|
108
|
-
x.symbol = elffile.symbols[ref]
|
109
|
-
x.is_dynamic = true
|
110
|
-
x.addend = 0
|
111
|
-
}
|
112
|
-
2**64-1
|
113
|
-
end
|
114
|
-
end
|
115
|
-
def write_amd64(elffile)
|
116
|
-
factory = ElfStructFactory.instance(:little,64)
|
117
|
-
@imported_symbols.each_key {|symbol|
|
118
|
-
if elffile.symbols.include?(symbol)
|
119
|
-
elffile.symbols[symbol].is_dynamic = true
|
120
|
-
else
|
121
|
-
elffile.symbols << Elf::Symbol.new(symbol,nil,Elf::STT::STT_OBJECT, 0, Elf::STB::STB_GLOBAL, 0).tap {|x|
|
122
|
-
x.semantics = Elf::SHN::SHN_UNDEF
|
123
|
-
}
|
124
|
-
end
|
125
|
-
}
|
126
|
-
out = factory.elfp_header.new()
|
127
|
-
state_ids = {}
|
128
|
-
tag_ids = {}
|
129
|
-
relocations = []
|
130
|
-
states = states()
|
131
|
-
@start = states.first unless states.include? @start
|
132
|
-
#These have to be filled in the order in which they are written
|
133
|
-
#FIXME: Make these aware of double transitions to the same range/ state
|
134
|
-
states.each_with_index do |state,index|
|
135
|
-
id = index + 2
|
136
|
-
id = 1 if @start == state
|
137
|
-
out.states << factory.elfp_state.new.tap {|x|
|
138
|
-
x.id = id
|
139
|
-
x.stackid = 0
|
140
|
-
}
|
141
|
-
state_ids[state] = id
|
142
|
-
print "State #{state} #{id}\n"
|
143
|
-
end
|
144
|
-
tag_ids[:default] = 0
|
145
|
-
@tags.each_with_index do |(name,ranges),index|
|
146
|
-
tag_ids[name] = index+1
|
147
|
-
ranges.each do |data|
|
148
|
-
out.tags << factory.elfp_tag.new.tap {|x|
|
149
|
-
x.tag = index + 1
|
150
|
-
x.addr = 0
|
151
|
-
x.siz = 0
|
152
|
-
}
|
153
|
-
out.tags.last.tap {|x|
|
154
|
-
x.addr = resolve_reference(elffile,relocations,x.addr.offset,data.low)
|
155
|
-
if data.high.nil?
|
156
|
-
x.siz = resolve_size(elffile,relocations,x.siz.offset,data.low)
|
157
|
-
else
|
158
|
-
pp "Warning, emitting SIZE symbol with value #{ data.high.to_i rescue data.high.name}"
|
159
|
-
x.siz = resolve_reference(elffile,relocations,x.siz.offset,data.high)
|
160
|
-
end
|
161
|
-
}
|
162
|
-
end
|
163
|
-
print "Tag #{name} #{index + 1} \n"
|
164
|
-
end
|
165
|
-
self.calls.each do |call|
|
166
|
-
out.calls << factory.elfp_call.new.tap {|x|
|
167
|
-
x.from = state_ids[call.from]
|
168
|
-
x.to = state_ids[call.to]
|
169
|
-
x.parambytes = call.parambytes
|
170
|
-
x.returnbytes = call.parambytes
|
171
|
-
}
|
172
|
-
out.calls.last.off = resolve_reference(elffile,relocations,out.calls.last.off.offset, call.symbol)
|
173
|
-
end
|
174
|
-
self.data.values.map(&:values).flatten.map(&:values).flatten.each do |data|
|
175
|
-
out.data << factory.elfp_data.new.tap {|x|
|
176
|
-
x.from = state_ids[data.from]
|
177
|
-
x.to = state_ids[data.to]
|
178
|
-
x.type = 0
|
179
|
-
x.type |= ELFP::ELFP_RW_READ if data.read
|
180
|
-
x.type |= ELFP::ELFP_RW_WRITE if data.write
|
181
|
-
x.type |= ELFP::ELFP_RW_EXEC if data.exec
|
182
|
-
raise RuntimeError.new "Unknown tag #{data.tag}" unless tag_ids.include? data.tag
|
183
|
-
x.tag = tag_ids[data.tag]
|
184
|
-
}
|
185
|
-
end
|
186
|
-
out = Elf::ProgBits.new(".elfbac",nil,out.to_binary_s)
|
187
|
-
out.align = 8
|
188
|
-
out.flags = SHF::SHF_ALLOC | SHF::SHF_WRITE
|
189
|
-
out.sect_type = SHT::SHT_PROGBITS
|
190
|
-
out.phdr = ElfFlags::PhdrType::PT_ELFBAC
|
191
|
-
out.phdr_flags = ElfFlags::PhdrFlags::PF_R
|
192
|
-
relocations.each { |rel|
|
193
|
-
rel.section = out
|
194
|
-
elffile.relocations << rel
|
195
|
-
}
|
196
|
-
elffile.progbits << out
|
197
|
-
|
198
|
-
end
|
199
|
-
def inject(file)
|
200
|
-
case file.machine
|
201
|
-
when ElfFlags::Machine::EM_X86_64
|
202
|
-
write_amd64(file)
|
203
|
-
else
|
204
|
-
raise RuntimeError.new "Wrong architecture for ARM64"
|
205
|
-
end
|
206
|
-
end
|
207
|
-
end
|
208
|
-
module BuilderHelper
|
209
|
-
def section_start(name, file_name="")
|
210
|
-
Elf::Policy.section_symbol_name(file_name,name).tap{|x| @policy.imported_symbols[x] = true}
|
211
|
-
end
|
212
|
-
end
|
213
|
-
class TagBuilder
|
214
|
-
include BuilderHelper
|
215
|
-
attr_accessor :ranges
|
216
|
-
def initialize(pol)
|
217
|
-
@policy = pol
|
218
|
-
@ranges = []
|
219
|
-
end
|
220
|
-
def section(name,file_name="")
|
221
|
-
range(section_start(name,file_name))
|
222
|
-
end
|
223
|
-
def range(low,high=nil)
|
224
|
-
@ranges << MemoryRange.new(low,high)
|
225
|
-
end
|
226
|
-
def symbol(sym)
|
227
|
-
range(sym)
|
228
|
-
end
|
229
|
-
end
|
230
|
-
class DataBuilder
|
231
|
-
include BuilderHelper
|
232
|
-
def initialize(transition)
|
233
|
-
@transition = transition
|
234
|
-
end
|
235
|
-
def read(v=true) #TODO: Unify transitions? Intervaltree?
|
236
|
-
@transition.read = v
|
237
|
-
end
|
238
|
-
def write(v=true)
|
239
|
-
@transition.write = v
|
240
|
-
@transition.read ||= v
|
241
|
-
end
|
242
|
-
def exec(v=true)
|
243
|
-
@transition.exec = v
|
244
|
-
@transition.read ||= v
|
245
|
-
end
|
246
|
-
end
|
247
|
-
class StateBuilder
|
248
|
-
include BuilderHelper
|
249
|
-
def initialize(from,to,pol)
|
250
|
-
@from = from
|
251
|
-
@to = to
|
252
|
-
@policy = pol
|
253
|
-
end
|
254
|
-
|
255
|
-
{
|
256
|
-
text: ".text",
|
257
|
-
data: ".data",
|
258
|
-
bss:".bss"
|
259
|
-
}.each{|function,name|
|
260
|
-
define_method function, lambda{|library=''| section(name,library)}
|
261
|
-
}
|
262
|
-
def call(symbol, parambytes= 0, returnbytes=0)
|
263
|
-
raise RuntimeError.new "Call has to have a destination" if @from == @to
|
264
|
-
@policy << Call.new(@from,@to, symbol, parambytes, returnbytes)
|
265
|
-
end
|
266
|
-
def call_noreturn(symbol,parambytes=0)
|
267
|
-
call(symbol, parambytes,-1)
|
268
|
-
end
|
269
|
-
def mem(tag, &block)
|
270
|
-
d = Data.new(@from,@to,tag)
|
271
|
-
DataBuilder.new(d).instance_eval(&block)
|
272
|
-
@policy << d
|
273
|
-
end
|
274
|
-
def exec(tag)
|
275
|
-
mem(tag){
|
276
|
-
exec }
|
277
|
-
end
|
278
|
-
def read(tag)
|
279
|
-
mem(tag){
|
280
|
-
read
|
281
|
-
}
|
282
|
-
end
|
283
|
-
def write(tag)
|
284
|
-
mem(tag){
|
285
|
-
write
|
286
|
-
}
|
287
|
-
end
|
288
|
-
def readwrite(tag)
|
289
|
-
mem(tag){
|
290
|
-
read
|
291
|
-
write
|
292
|
-
}
|
293
|
-
end
|
294
|
-
|
295
|
-
def to(name,&block)
|
296
|
-
raise RuntimeError.new "Cannot nest to{} blocks" if @from != @to# or name == @from
|
297
|
-
StateBuilder.new(@from,name, @policy).instance_eval(&block)
|
298
|
-
end
|
299
|
-
def transition(trans)
|
300
|
-
@policy << trans
|
301
|
-
end
|
302
|
-
end
|
303
|
-
class PolicyBuilder
|
304
|
-
include BuilderHelper
|
305
|
-
attr_reader :policy
|
306
|
-
def initialize()
|
307
|
-
@policy = Policy.new()
|
308
|
-
end
|
309
|
-
def state(name, &block)
|
310
|
-
StateBuilder.new(name,name,@policy).instance_eval(&block)
|
311
|
-
end
|
312
|
-
def tag(name, &block)
|
313
|
-
policy.tags[name] ||= []
|
314
|
-
x =TagBuilder.new(@policy)
|
315
|
-
x.instance_eval(&block)
|
316
|
-
policy.tags[name] += x.ranges
|
317
|
-
end
|
318
|
-
def call_noreturn(from,to,symbol, parambytes=0)
|
319
|
-
@policy << Call.new(from,to, symbol, parambytes,-1)
|
320
|
-
end
|
321
|
-
def read
|
322
|
-
@policy << Data.new(from,to,from,to )
|
323
|
-
end
|
324
|
-
def start(name)
|
325
|
-
@policy.start = name
|
326
|
-
end
|
327
|
-
end
|
328
|
-
def self.build(&block)
|
329
|
-
x= PolicyBuilder.new()
|
330
|
-
x.instance_eval(&block)
|
331
|
-
x.policy
|
332
|
-
end
|
333
|
-
end
|
334
|
-
|
335
|
-
end
|
4
|
+
require_relative 'policy/dsl'
|
5
|
+
require_relative 'policy/inject_policy'
|
6
|
+
require_relative 'policy/defaults'
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Elf::Policy::Hacks
|
2
|
+
def self.initializer_functions(filename)
|
3
|
+
lib = `objdump -D #{filename} | grep -B 3 'callq.*libc_start_main' `
|
4
|
+
init_addr = /\$0x([0-9a-f]*),\%r8/.match(lib)[1].to_i(16)
|
5
|
+
fini_addr = /\$0x([0-9a-f]*),\%rcx/.match(lib)[1].to_i(16)
|
6
|
+
main_addr = /\$0x([0-9a-f]*),\%rdi/.match(lib)[1].to_i(16)
|
7
|
+
[init_addr,fini_addr,main_addr]
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Elf::Policy::Generator
|
2
|
+
H= Elf::Policy::Hacks
|
3
|
+
TwoState = Proc.new {|file,filename|
|
4
|
+
|
5
|
+
tag('program') do
|
6
|
+
section('.init')
|
7
|
+
section('.fini')
|
8
|
+
section('.text')
|
9
|
+
section('.plt')
|
10
|
+
section('.data')
|
11
|
+
section('.bss')
|
12
|
+
section('.rodata')
|
13
|
+
section('.got')
|
14
|
+
end
|
15
|
+
tag('libraries') do
|
16
|
+
section('.plt')
|
17
|
+
# libs.each do |lib|
|
18
|
+
# section('.text',lib)
|
19
|
+
# section('.plt',lib)
|
20
|
+
# section('.data',lib)
|
21
|
+
# section('.bss',lib)
|
22
|
+
# section('.rodata',lib)
|
23
|
+
# section('.plt',lib)
|
24
|
+
# section('.got.plt',lib)
|
25
|
+
# end
|
26
|
+
end
|
27
|
+
state('main') do
|
28
|
+
exec 'program'
|
29
|
+
readwrite 'libraries'
|
30
|
+
readwrite 'program'
|
31
|
+
readwrite :default
|
32
|
+
to('libs') do
|
33
|
+
plt = file.progbits.select{|x|x.name==".plt"}.first
|
34
|
+
(plt.addr .. (plt.addr + plt.size)).step(8).each do |plt_addr| #FIXME: This is a
|
35
|
+
#hack
|
36
|
+
call plt_addr
|
37
|
+
end
|
38
|
+
end
|
39
|
+
to('libs') do
|
40
|
+
call '_dl_runtime_resolve'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
state('libs') do
|
44
|
+
exec 'libraries'
|
45
|
+
readwrite 'libraries'
|
46
|
+
readwrite 'program'
|
47
|
+
readwrite :default
|
48
|
+
exec :default # Really, this sucks
|
49
|
+
to 'main' do
|
50
|
+
call file.entry.to_i
|
51
|
+
H::initializer_functions(filename).each {|initializer|
|
52
|
+
call initializer
|
53
|
+
}
|
54
|
+
call '_fini'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
start 'libs'
|
58
|
+
}
|
59
|
+
end
|
@@ -0,0 +1,338 @@
|
|
1
|
+
module Elf
|
2
|
+
|
3
|
+
module Policy
|
4
|
+
def self.section_symbol_name(file_name,section_name)
|
5
|
+
"_elfp_#{file_name}_#{section_name}"
|
6
|
+
end
|
7
|
+
R = ElfFlags::Relocation
|
8
|
+
ELFP = ElfFlags::ElfPData
|
9
|
+
class Transition
|
10
|
+
attr_accessor :from, :to
|
11
|
+
end
|
12
|
+
class Call < Transition
|
13
|
+
attr_accessor :symbol
|
14
|
+
attr_accessor :parambytes,:returnbytes
|
15
|
+
def initialize(from,to, symbol, parambytes, returnbytes)
|
16
|
+
@from, @to, @symbol, @parambytes, @returnbytes = from,to, symbol, parambytes, returnbytes
|
17
|
+
end
|
18
|
+
def allows_return?
|
19
|
+
@returnbytes >= 0
|
20
|
+
end
|
21
|
+
end
|
22
|
+
class Data < Transition
|
23
|
+
attr_accessor :tag
|
24
|
+
attr_accessor :read,:write, :exec
|
25
|
+
def initialize(from,to,tag , read=false ,write=false,exec=false)
|
26
|
+
@from, @to, @tag, @read,@write,@exec = from,to,tag,read,write,exec
|
27
|
+
end
|
28
|
+
end
|
29
|
+
class MemoryRange
|
30
|
+
attr_accessor :low, :high
|
31
|
+
def initialize(from,to)
|
32
|
+
@low,@high = from,to
|
33
|
+
end
|
34
|
+
end
|
35
|
+
class Policy
|
36
|
+
attr_accessor :data, :calls, :start, :tags
|
37
|
+
attr_accessor :imported_symbols
|
38
|
+
def states
|
39
|
+
t = data.keys + data.values.map(&:keys).flatten + calls.map(&:from) +
|
40
|
+
calls.map(&:to)
|
41
|
+
t.uniq
|
42
|
+
end
|
43
|
+
def <<(*transitions)
|
44
|
+
transitions.each do |t|
|
45
|
+
if t.is_a? Data
|
46
|
+
x= data
|
47
|
+
x[t.from] ||= {}
|
48
|
+
x[t.from][t.to] ||= {}
|
49
|
+
x[t.from][t.to][t.tag] ||= t
|
50
|
+
x[t.from][t.to][t.tag].read ||= t.read
|
51
|
+
x[t.from][t.to][t.tag].write ||= t.write
|
52
|
+
x[t.from][t.to][t.tag].exec ||= t.exec
|
53
|
+
elsif t.is_a? Call
|
54
|
+
calls << t
|
55
|
+
else
|
56
|
+
raise ArgumentError.new "#{t.class} is not a valid transition"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
def initialize
|
61
|
+
@data={}
|
62
|
+
@calls=[]
|
63
|
+
@states ={}
|
64
|
+
@tags = {}
|
65
|
+
@imported_symbols = {}
|
66
|
+
end
|
67
|
+
def resolve_reference(elffile, relocations,offset, ref)
|
68
|
+
if(ref.is_a? Integer)
|
69
|
+
ref.to_i
|
70
|
+
elsif(ref == "_dl_runtime_resolve") #HACK:HACK:HACK: I couldn't hack ld.so to fix this, so
|
71
|
+
#here comes a nasty hack
|
72
|
+
#note that the address of _dl_runtime_resolve is 16 bytes into PLT.GOT
|
73
|
+
if !elffile.symbols.include? "_elfp_hidden_trampolineaddr"
|
74
|
+
pltgot = elffile.dynamic.pltgot or raise RuntimeError.new "No plt.got for _dl_runtime_resolve hack"
|
75
|
+
elffile.symbols << Elf::Symbol.new("_elfp_hidden_trampolineaddr", pltgot,STT::STT_OBJECT,16, STB::STB_LOCAL,8)
|
76
|
+
end
|
77
|
+
symb = elffile.symbols["_elfp_hidden_trampolineaddr"]
|
78
|
+
relocations << Elf::Relocation.new.tap{|x|
|
79
|
+
x.type = R::R_X86_64_COPY
|
80
|
+
x.offset = offset
|
81
|
+
x.symbol = symb
|
82
|
+
x.is_dynamic = true
|
83
|
+
x.addend = 0
|
84
|
+
}
|
85
|
+
0xDEADBEEF
|
86
|
+
else
|
87
|
+
raise RuntimeError.new "Symbol #{ref} not found" unless elffile.symbols.include? ref
|
88
|
+
relocations << Elf::Relocation.new.tap{|x|
|
89
|
+
x.type = R::R_X86_64_64
|
90
|
+
x.offset = offset
|
91
|
+
x.symbol = elffile.symbols[ref]
|
92
|
+
x.is_dynamic = true
|
93
|
+
x.addend = 0
|
94
|
+
}
|
95
|
+
2**64-1
|
96
|
+
end
|
97
|
+
end
|
98
|
+
def resolve_size(elffile,relocations, offset, ref)
|
99
|
+
if(ref.is_a? Integer)
|
100
|
+
ref.to_i
|
101
|
+
else
|
102
|
+
raise RuntimeError.new "Symbol #{ref} not found" unless elffile.symbols.include? ref
|
103
|
+
relocations << Elf::Relocation.new.tap{|x|
|
104
|
+
x.type = R::R_X86_64_SIZE64
|
105
|
+
x.offset = offset
|
106
|
+
x.symbol = elffile.symbols[ref]
|
107
|
+
x.is_dynamic = true
|
108
|
+
x.addend = 0
|
109
|
+
}
|
110
|
+
2**64-1
|
111
|
+
end
|
112
|
+
end
|
113
|
+
def write_amd64(elffile)
|
114
|
+
factory = ElfStructFactory.instance(:little,64)
|
115
|
+
@imported_symbols.each_key {|symbol|
|
116
|
+
if elffile.symbols.include?(symbol)
|
117
|
+
elffile.symbols[symbol].is_dynamic = true
|
118
|
+
else
|
119
|
+
elffile.symbols << Elf::Symbol.new(symbol,nil,Elf::STT::STT_OBJECT, 0, Elf::STB::STB_GLOBAL, 0).tap {|x|
|
120
|
+
x.semantics = Elf::SHN::SHN_UNDEF
|
121
|
+
}
|
122
|
+
end
|
123
|
+
}
|
124
|
+
out = factory.elfp_header.new()
|
125
|
+
state_ids = {}
|
126
|
+
tag_ids = {}
|
127
|
+
relocations = []
|
128
|
+
states = states()
|
129
|
+
@start = states.first unless states.include? @start
|
130
|
+
#These have to be filled in the order in which they are written
|
131
|
+
#FIXME: Make these aware of double transitions to the same range/ state
|
132
|
+
states.each_with_index do |state,index|
|
133
|
+
id = index + 2
|
134
|
+
id = 1 if @start == state
|
135
|
+
out.states << factory.elfp_state.new.tap {|x|
|
136
|
+
x.id = id
|
137
|
+
x.stackid = 0
|
138
|
+
}
|
139
|
+
state_ids[state] = id
|
140
|
+
print "State #{state} #{id}\n"
|
141
|
+
end
|
142
|
+
tag_ids[:default] = 0
|
143
|
+
@tags.each_with_index do |(name,ranges),index|
|
144
|
+
tag_ids[name] = index+1
|
145
|
+
ranges.each do |data|
|
146
|
+
out.tags << factory.elfp_tag.new.tap {|x|
|
147
|
+
x.tag = index + 1
|
148
|
+
x.addr = 0
|
149
|
+
x.siz = 0
|
150
|
+
}
|
151
|
+
out.tags.last.tap {|x|
|
152
|
+
x.addr = resolve_reference(elffile,relocations,x.addr.offset,data.low)
|
153
|
+
if data.high.nil?
|
154
|
+
x.siz = resolve_size(elffile,relocations,x.siz.offset,data.low)
|
155
|
+
else
|
156
|
+
pp "Warning, emitting SIZE symbol with value #{ data.high.to_i rescue data.high.name}"
|
157
|
+
x.siz = resolve_reference(elffile,relocations,x.siz.offset,data.high)
|
158
|
+
end
|
159
|
+
}
|
160
|
+
end
|
161
|
+
print "Tag #{name} #{index + 1} \n"
|
162
|
+
end
|
163
|
+
self.calls.each do |call|
|
164
|
+
out.calls << factory.elfp_call.new.tap {|x|
|
165
|
+
x.from = state_ids[call.from]
|
166
|
+
x.to = state_ids[call.to]
|
167
|
+
x.parambytes = call.parambytes
|
168
|
+
x.returnbytes = call.parambytes
|
169
|
+
}
|
170
|
+
out.calls.last.off = resolve_reference(elffile,relocations,out.calls.last.off.offset, call.symbol)
|
171
|
+
end
|
172
|
+
self.data.values.map(&:values).flatten.map(&:values).flatten.each do |data|
|
173
|
+
out.data << factory.elfp_data.new.tap {|x|
|
174
|
+
x.from = state_ids[data.from]
|
175
|
+
x.to = state_ids[data.to]
|
176
|
+
x.type = 0
|
177
|
+
x.type |= ELFP::ELFP_RW_READ if data.read
|
178
|
+
x.type |= ELFP::ELFP_RW_WRITE if data.write
|
179
|
+
x.type |= ELFP::ELFP_RW_EXEC if data.exec
|
180
|
+
raise RuntimeError.new "Unknown tag #{data.tag}" unless tag_ids.include? data.tag
|
181
|
+
x.tag = tag_ids[data.tag]
|
182
|
+
}
|
183
|
+
end
|
184
|
+
out = Elf::ProgBits.new(".elfbac",nil,out.to_binary_s)
|
185
|
+
out.align = 8
|
186
|
+
out.flags = SHF::SHF_ALLOC | SHF::SHF_WRITE
|
187
|
+
out.sect_type = SHT::SHT_PROGBITS
|
188
|
+
out.phdr = ElfFlags::PhdrType::PT_ELFBAC
|
189
|
+
out.phdr_flags = ElfFlags::PhdrFlags::PF_R
|
190
|
+
relocations.each { |rel|
|
191
|
+
rel.section = out
|
192
|
+
elffile.relocations << rel
|
193
|
+
}
|
194
|
+
elffile.progbits << out
|
195
|
+
|
196
|
+
end
|
197
|
+
def inject(file)
|
198
|
+
case file.machine
|
199
|
+
when ElfFlags::Machine::EM_X86_64
|
200
|
+
write_amd64(file)
|
201
|
+
else
|
202
|
+
raise RuntimeError.new "Wrong architecture for ARM64"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
module BuilderHelper
|
207
|
+
def section_start(name, file_name="")
|
208
|
+
Elf::Policy.section_symbol_name(file_name,name).tap{|x| @policy.imported_symbols[x] = true}
|
209
|
+
end
|
210
|
+
end
|
211
|
+
class TagBuilder
|
212
|
+
include BuilderHelper
|
213
|
+
attr_accessor :ranges
|
214
|
+
def initialize(pol)
|
215
|
+
@policy = pol
|
216
|
+
@ranges = []
|
217
|
+
end
|
218
|
+
def section(name,file_name="")
|
219
|
+
range(section_start(name,file_name))
|
220
|
+
end
|
221
|
+
def range(low,high=nil)
|
222
|
+
@ranges << MemoryRange.new(low,high)
|
223
|
+
end
|
224
|
+
def symbol(sym)
|
225
|
+
range(sym)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
class DataBuilder
|
229
|
+
include BuilderHelper
|
230
|
+
def initialize(transition)
|
231
|
+
@transition = transition
|
232
|
+
end
|
233
|
+
def read(v=true) #TODO: Unify transitions? Intervaltree?
|
234
|
+
@transition.read = v
|
235
|
+
end
|
236
|
+
def write(v=true)
|
237
|
+
@transition.write = v
|
238
|
+
@transition.read ||= v
|
239
|
+
end
|
240
|
+
def exec(v=true)
|
241
|
+
@transition.exec = v
|
242
|
+
@transition.read ||= v
|
243
|
+
end
|
244
|
+
end
|
245
|
+
class StateBuilder
|
246
|
+
include BuilderHelper
|
247
|
+
def initialize(from,to,pol)
|
248
|
+
@from = from
|
249
|
+
@to = to
|
250
|
+
@policy = pol
|
251
|
+
end
|
252
|
+
|
253
|
+
{
|
254
|
+
text: ".text",
|
255
|
+
data: ".data",
|
256
|
+
bss:".bss"
|
257
|
+
}.each{|function,name|
|
258
|
+
define_method function, lambda{|library=''| section(name,library)}
|
259
|
+
}
|
260
|
+
def call(symbol, parambytes= 0, returnbytes=0)
|
261
|
+
raise RuntimeError.new "Call has to have a destination" if @from == @to
|
262
|
+
@policy << Call.new(@from,@to, symbol, parambytes, returnbytes)
|
263
|
+
end
|
264
|
+
def call_noreturn(symbol,parambytes=0)
|
265
|
+
call(symbol, parambytes,-1)
|
266
|
+
end
|
267
|
+
def mem(tag, &block)
|
268
|
+
d = Data.new(@from,@to,tag)
|
269
|
+
DataBuilder.new(d).instance_eval(&block)
|
270
|
+
@policy << d
|
271
|
+
end
|
272
|
+
def exec(tag)
|
273
|
+
mem(tag){
|
274
|
+
exec }
|
275
|
+
end
|
276
|
+
def read(tag)
|
277
|
+
mem(tag){
|
278
|
+
read
|
279
|
+
}
|
280
|
+
end
|
281
|
+
def write(tag)
|
282
|
+
mem(tag){
|
283
|
+
write
|
284
|
+
}
|
285
|
+
end
|
286
|
+
def readwrite(tag)
|
287
|
+
mem(tag){
|
288
|
+
read
|
289
|
+
write
|
290
|
+
}
|
291
|
+
end
|
292
|
+
|
293
|
+
def to(name,&block)
|
294
|
+
raise RuntimeError.new "Cannot nest to{} blocks" if @from != @to# or name == @from
|
295
|
+
StateBuilder.new(@from,name, @policy).instance_eval(&block)
|
296
|
+
end
|
297
|
+
def transition(trans)
|
298
|
+
@policy << trans
|
299
|
+
end
|
300
|
+
end
|
301
|
+
class PolicyBuilder
|
302
|
+
include BuilderHelper
|
303
|
+
attr_reader :policy
|
304
|
+
def initialize()
|
305
|
+
@policy = Policy.new()
|
306
|
+
@every_state = []
|
307
|
+
end
|
308
|
+
def state(name, &block)
|
309
|
+
StateBuilder.new(name,name,@policy).tap {|x|
|
310
|
+
x.instance_eval(&block)
|
311
|
+
@every_state.each {|b|
|
312
|
+
x.instance_eval(b)
|
313
|
+
}
|
314
|
+
}
|
315
|
+
end
|
316
|
+
def tag(name, &block)
|
317
|
+
policy.tags[name] ||= []
|
318
|
+
x =TagBuilder.new(@policy)
|
319
|
+
x.instance_eval(&block)
|
320
|
+
policy.tags[name] += x.ranges
|
321
|
+
end
|
322
|
+
def every_state(&block)
|
323
|
+
@every_state << block
|
324
|
+
@policy.state
|
325
|
+
end
|
326
|
+
def start(name)
|
327
|
+
@policy.start = name
|
328
|
+
end
|
329
|
+
|
330
|
+
end
|
331
|
+
def self.build(&block)
|
332
|
+
x= PolicyBuilder.new()
|
333
|
+
x.instance_eval(&block)
|
334
|
+
x.policy
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Elf
|
2
|
+
class ElfFile
|
3
|
+
def build_policy(&builder)
|
4
|
+
#TODO: Allow special 'default_generator' synthax instead of instance_exec
|
5
|
+
Elf::Policy::inject_symbols(self)
|
6
|
+
p = Elf::Policy.build(&builder)
|
7
|
+
p.inject(self)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
def self.policy(&block)
|
11
|
+
#TODO: optional arg?
|
12
|
+
Elf::rewrite(ARGV[0]) do |file|
|
13
|
+
file.build_policy do
|
14
|
+
instance_exec(file,&block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/mithril/version.rb
CHANGED
data/lib/mithril/writer.rb
CHANGED
@@ -85,6 +85,11 @@ module Elf
|
|
85
85
|
def [](idx)
|
86
86
|
@sections[idx]
|
87
87
|
end
|
88
|
+
def to_s
|
89
|
+
out = "Layout\n"
|
90
|
+
@layout.each {|key,value| out << "#{key.to_s(16)} #{value.end.to_s(16)} #{value.name}\n"}
|
91
|
+
out
|
92
|
+
end
|
88
93
|
def add(*sections) #Ordering as follows: Fixed
|
89
94
|
#(non-nil vaddrs) go where they have to go
|
90
95
|
# Flexible sections are added to lowest hole after section of
|
data/mithril.gemspec
CHANGED
@@ -10,10 +10,10 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["jbangert@acm.org"]
|
11
11
|
spec.description = %q{In Soviet Russia, Mithril forges Elf}
|
12
12
|
spec.summary = %q{The Mithril toolkit for canonical elf manipulation}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/jbangert/mithril"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
|
-
spec.files = `git ls-files`.split($/)
|
16
|
+
spec.files = `git ls-files`.split($/).select{|i| !i[/\.pdf$/] }
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elf-mithril
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-11-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -149,6 +149,11 @@ files:
|
|
149
149
|
- lib/mithril/inject_symbols.rb
|
150
150
|
- lib/mithril/parser.rb
|
151
151
|
- lib/mithril/policy.rb
|
152
|
+
- lib/mithril/policy/default/hacks.rb
|
153
|
+
- lib/mithril/policy/default/twostate.rb
|
154
|
+
- lib/mithril/policy/defaults.rb
|
155
|
+
- lib/mithril/policy/dsl.rb
|
156
|
+
- lib/mithril/policy/inject_policy.rb
|
152
157
|
- lib/mithril/version.rb
|
153
158
|
- lib/mithril/writer.rb
|
154
159
|
- lib/mithril/writer2.rb
|
@@ -163,7 +168,7 @@ files:
|
|
163
168
|
- tools/gnu_elf_hash_test.cxx
|
164
169
|
- tools/hash_test
|
165
170
|
- tools/hash_test.c
|
166
|
-
homepage:
|
171
|
+
homepage: https://github.com/jbangert/mithril
|
167
172
|
licenses:
|
168
173
|
- MIT
|
169
174
|
post_install_message:
|