Upload
ice799
View
216
Download
0
Embed Size (px)
Citation preview
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
1/75
Descent into Darkness:Understanding your systems binary
interface is the only way out.
joe damato@joedamato
timetobleed.com
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
2/75
About Joe Damato
ex-vmware, cmu alumni
memprof, ltrace libdl/libunwind patchset,ree/mri thread implementation rewrite
http://timetobleed.com
@joedamato
http://timetobleed.com/http://timetobleed.com/http://timetobleed.com/8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
3/75
Only have 30 minutes...welcome to flight school.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
4/75
No clue why this was
acce ted.
This talk will have about 5 lines of Ruby code.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
5/75
Before we get started
I need to introduce you a good friend of mine...
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
6/75
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
7/75
This talk is about how being evil is totally awesome.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
8/75
Dont do any of this,
ever.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
9/75
The problem
My ruby process is 700 megabytes. Why?
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
10/75
The problem
It is very easy to leak references in yourRuby code.
Leaking a reference to an object causesthat object and all objects it references to
stick around in memory.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
11/75
The problem As long as someone, somewhere is
holding a reference to this instanceof classA, all the objects in thispicture can not be freed
This could add up to a lot ofmemory very fast.
GC will scan each object every run
to see if it is time to free the object.
This could add up to a lot of CPUburned.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
12/75
The problem
But memory is cheap who cares?
Rubys GC is a nave stop the world markand sweep.
The more objects that stick around inmemory the longer your GC runs take.
The longer GC takes, the less time yourapp has to run Ruby code.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
13/75
The problem
Eliminate leaked references, reduce the
length of your GC runs, run more of yourRuby application code.
Cool. But how can you track downreference leaks?
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
14/75
Problem Requirements
I dont want to applypatches and rebuild
Ruby.
I want to gem install,require, and done.
Anything else is toomuch work.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
15/75
Luckily, we can turn to evil.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
16/75
Verbiage amd64 is a CPU spec was proposed by AMD
as a way to add 64bit support to x86.
Intel Architecture 64 (IA64) spec is acompletely new 64bit instruction set.
amd64 != IA64 Intel then decided to adopt AMDs 64bit spec. They did and called it IA-32e, EM64T, and
finally Intel 64.
Intel 64 ~= amd64
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
17/75
amd64
Verbiage
Intel64
compilers generate code that uses thesubset of the amd64 spec that both intel and
amd comply to.
usually called x86_64 or amd64.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
18/75
WTF is an ABI?
Application Binary Interface
describes the low-level interface between aprogram and the operating system oranother application. (wikipedia)
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
19/75
WTF is an ABI?
alignment
calling conventions
object file and library formats
syscalls (how they work, where they live)
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
20/75
WTF is an ABI?
System V ABI (271 pages)
System V ABI AMD64 Architecture ProcessorSupplement (128 pages)
System V ABI Intel386 Architecture ProcessorSupplement (377 pages)
MIPS, ARM, PPC, and IA-64 too!
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
21/75
I brought copies of all three for
everyone.
We will now read them together.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
22/75
No. But lets blaze
through the importantpieces now.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
23/75
Evil Devices
nm - dump symbol table
objdump - disassemble lots of differentobjects. can do lots, lots more.
readelf - dump information
dwarfdump - dump debugging information
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
24/75
nm
000000000048ac90 t Balloc
0000000000491270 T Init_Array
0000000000497520 T Init_Bignum
000000000041dc80 T Init_Binding
000000000049d9b0 T Init_Comparable
000000000049de30 T Init_Dir
00000000004a1080 T Init_Enumerable
00000000004a3720 T Init_Enumerator
00000000004a4f30 T Init_Exception
000000000042c2d0 T Init_File
0000000000434b90 T Init_GC
% nm /usr/bin/ruby
symbolvalue
symbol names
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
25/75
objdump% objdump -D /usr/bin/ruby
offsets opcodes instructions helpful metadata
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
26/75
readelf% readelf -a /usr/bin/ruby
This is a *tiny* subset of the data available
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
27/75
dwarfdump% dwarfdump -a /usr/bin/ruby
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
28/75
Some friends
Registers are important. They are small, fastpieces of memory on the CPU.
Some registers have a specific job:
%rax - holds a return value
%rip - instruction pointer
Can refer to pieces of registers.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
29/75
%rax uncensored
%rax
%rax = 64 bits, 8 bytes, 1 quadword
%eaxlower 32 bits
%eax = 32 bits, 4 bytes, 1 dword
%axlower 16 bits
%ax = 16 bits, 2 bytes, 1 word
%ah
%ah = 8 bits, 1 byte, 1 halfword
upper 8 bits
%al%al = 8 bits, 1 byte, 1 halfword
lower 8 bits
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
30/75
Some x86_64 asm
notes Two different syntaxes: gas/att and intel.
GDB disassembly is gas/att by default.
set disassembly-flavor intel
objdump is gas/att by default
objdump -M intel
I prefer gas/att.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
31/75
unless otherwise
noted, everything willbe in att/gas syntax.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
32/75
Moving stuff
movsource, destmov$0,%rbx # move immediate (0) to registermov%eax,%rax # mov eax into rax.
source and dest cannot both be memory.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
33/75
Calling functions
Lots of different ways to call functions.
Two ways we care about (there are more):callq*%rbx # indirect absolutecallq0xdeadbeef # RIP relative with 32bit displacement
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
34/75
Calling convention
x86_64 function arguments from left to right live in:
%rdi, %rsi, %rdx, %rcx, %r8, %r9
thats for INTEGER class items.
Other stuff gets passed on the stack (likeon i386).
end of argument area must be aligned on a16-byte boundary.
registers can be caller or called saved.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
35/75
intagain(int amount){ intret = 0; ret = amount + 150;
returnret;}
att/gas syntaxintel syntax
Save the old stack frame base pointer.
Set the base pointer to the current stack pointer.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
36/75
intagain(int amount){ intret = 0; ret = amount + 150;
returnret;}
att/gas syntaxintel syntax
*(rbp - 0x14) = amount;
*(rbp - 0x4) = 0;
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
37/75
intagain(int amount){ intret = 0; ret = amount + 150;
returnret;}
att/gas syntaxintel syntax
eax = *(rbp - 0x14);
eax = eax + 0x96; /* 0x96 = 150 :P */
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
38/75
intagain(int amount){ intret = 0; ret = amount + 150;
returnret;}
att/gas syntaxintel syntax
*(rbp - 0x4) = eax; /* not needed */eax = *(rbp - 0x4); /* not needed */
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
39/75
intagain(int amount){ intret = 0; ret = amount + 150;
returnret;}
att/gas syntaxintel syntax
restore the stack pointer and old base pointer
return from the funtion
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
40/75
ELF Objects
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
41/75
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
42/75
Sections that matter to
mem rof
.text - code lives here
.plt - stub code that helps to resolve
absolute function addresses.
.got.plt - absolute function addresses; used
by .plt entries.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
43/75
plt
Procedure Linkage Table (plt) is used to find
functions in shared libraries at runtime.
Shared libraries are position independentand can be mapped anywhere in the
address space.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
44/75
Um, what does this haveto do with Rub ?
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
45/75
The ingredients for evil
we know the x86_64 ABI
we know how ELF objects work
we know ruby calls functions in the VM to
allocate and free objects (rb_newobj,
add_freelist)
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
46/75
You wont.
Lets combine all of this knowledge and ...
Rewrite the Ruby VM in memory
while it is running.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
47/75
Hook rb_newobj
The Ruby VM calls rb_newobj to allocate anew object.
Well need to know when this happens sowe can track objects.
Lets scan the Ruby binary in memory andrewrite all function calls to rb_newobj tocall a handler function instead.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
48/75
Hook rb_newobj
412d16: e8 c1 36 02 00 callq 4363dc #
412d1b: .....
address of this instruction
call opcode*
32bit displacement to thetarget function from the next
instruction.
(objdump output)
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
49/75
Hook rb_newobj
412d16: e8 c1 36 02 00 callq 4363dc #
412d1b: .....
412d1b = 4363dc+ 000236c1
(x86 is little endian)
(objdump output)
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
50/75
Hook rb_newobj
Overwrite the displacement so that all callsto rb_newobj actually call a different function
instead.
It may look like this:
VALUE other_function(){
VALUE new_obj = rb_newobj();
/* set up tracking of new_obj */ return new_obj;}
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
51/75
Doesnt work for all
That trick only works for Ruby built with --disable-shared (no libruby.so)
Ruby built with --enable-shared (withlibruby.so) doesnt work like that.
Code in libruby.so calls rb_newobj via thePLT.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
52/75
How the plt works
0x7ffff7afd6e6
.got.plt entryInitially, the .got.plt entry containsthe address of the instruction after
the jmp.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
53/75
How the plt works
0x7ffff7afd6e6
.got.plt entryAn ID is stored and the rtld is
invoked.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
54/75
How the plt works
0x7ffff7b34ac0
.got.plt entryrtld writes the address of
rb_newobj to the .got.plt entry.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
55/75
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
56/75
Hook the GOT
Redirect execution by overwriting the .got.plt
entry for rb_newobj with a handler functioninstead.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
57/75
0xdeadbeef
.got.plt entryVALUE other_function(){
VALUE new_obj = rb_newobj(); /* set up tracking of new_obj */ return new_obj;
}
Hook the GOT
NO, it isnt. other_function() lives in memprof.so, so itscalls to rb_newobj() use the .plt/.got.plt in memprof.so.
As long as we leave memprof.so unmodified, well avoid aninfinite loop.
WAIT... other_function() calls rb_newobj() isnt that an infinite loop?
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
58/75
Hook add_freelist
Were now tracking objects at the time ofcreation.
In order to find leaks we need to track whenobjects get freed too.
add_freelist is called in the VM when anobject is freed.
Why not just overwrite call instructions orhook the GOT?
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
59/75
Hook add_freelist
Cant because add_freelist is inlined:staticinlinevoid
add_freelist(p) RVALUE*p;
{p->as.free.flags =0;
p->as.free.next= freelist;freelist = p;
}
The compiler has the option of
inserting the instructions of thisfunction directly into the callers.
If this happens, you wont see any calls.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
60/75
So... what now? Look carefully at the generated code:
staticinlinevoidadd_freelist(p) RVALUE*p;
{p->as.free.flags =0;
p->as.free.next= freelist;
freelist = p;}
Notice that freelist gets updated.
freelist has file level scope.
hmmmm......
A ( t id) id
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
61/75
A (stupid) crazy idea
freelist has file level scope, so it lives at somestatic address.
add_freelist updates freelist, so...
Why not search the binary for mov instructionsthat have freelist as the target!
Overwrite that mov instruction with a call to
our code!
But... we have a problem.
The system isnt ready for a call instruction.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
62/75
Isnt ready? What? The 64bit ABI says that the stack must be
aligned to a 16byte boundary after any/allarguments have been arranged.
Since the overwrite is just some random mov,no way to guarantee that the stack is aligned. If we just plop in a call instruction, we wont
be able to arrange for arguments to get put inthe right registers.
Must save caller saved registers.
So now what?
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
63/75
jmp Can use a jmp instruction. call saves a return address
jmp does not.
Transfer execution to anassembly stub that sets thesystem up according to the ABI.
then do the call to the Chandler dont forget to jmp back when
handler is done!
this instruction updates the freelist and comes from
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
64/75
this instruction updates the freelist and comes fromadd_freelist:
Cant overwrite it with a call instruction because thestate of the system is not ready for a function call.
The jmp instruction and its offset are 5 bytes wide.Cant grow or shrink the binary, so insert 2 one byte
NOPs.
address of assembly stub
this instruction updates the freelist and comes from
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
65/75
this instruction updates the freelist and comes fromadd_freelist:
Cant overwrite it with a call instruction because thestate of the system is not ready for a function call.
The jmp instruction and its offset are 5 bytes wide.Cant grow or shrink the binary, so insert 2 one byte
NOPs.
must jump back here
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
66/75
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
67/75
Sample Outputrequire'memprof'Memprof.startrequire"stringio"
StringIO.newMemprof.stats
108 /custom/ree/lib/ruby/1.8/x86_64-linux/stringio.so:0:__node__
14 test2.rb:3:String
2 /custom/ree/lib/ruby/1.8/x86_64-linux/stringio.so:0:Class
1 test2.rb:4:StringIO
1 test2.rb:4:String
1 test2.rb:3:Array
1 /custom/ree/lib/ruby/1.8/x86_64-linux/stringio.so:0:Enumerable
require'memprof'Memprof.startMemprof.track(/tmp/file) {
do_something}
Or just track a block
Or dump the entire heap as JSON
require'memprof'Memprof.startdo_stuff
Memprof.dump_all(/tmp/file)
object count file, line number, class name
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
68/75
Middleware Use memprof as middleware
Get per-request object count information
569 lib/ruby/1.8/yaml.rb:133:String528 gems/sequel-3.9.0/lib/sequel/model/base.rb:393:__node__522 gems/haml-2.2.20/lib/haml/precompiler.rb:545:String522 gems/haml-2.2.20/lib/haml/helpers.rb:135:String522 gems/haml-2.2.20/lib/haml/helpers.rb:135:ActiveSupport::SafeBuffer507 gems/haml-2.2.20/lib/haml/precompiler.rb:317:String488 gems/sequel-3.9.0/lib/sequel/adapters/mysql.rb:410:String445 lib/ruby/1.8/yaml.rb:133:YAML::Syck::Node432 gems/haml-2.2.20/lib/haml/precompiler.rb:566:String
406 gems/sequel-3.9.0/lib/sequel/model/base.rb:392:__node__
require'memprof/middleware'MyApp::Application.configuredoconfig.middleware.useMemprof::Middleware
end
rails 3, environment.rb:
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
69/75
memprof.com
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
70/75
memprof limitations
only works on amd64 linux and snow leopard
only works with MRI and REE 1.8
only works on binaries that are NOT STRIPPED.
OSX System Ruby is NOT supported (yet).
support for EY rubies is forthcoming - you willhave to install -dbg packages, though.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
71/75
More evil is brewing
We have some crazy, scary, stupid ideas thatwe think youll love.
Stay tuned to find out what they are.
1.9 support is one of the ideas.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
72/75
Use RVM.
This would have been really hard to test onall the different Ruby binaries without RVM.
Use it. Donate money. (Not my project).
http://rvm.beginrescueend.com/
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
73/75
Get memprof
This talk was about the memprof Ruby gemwhich is free and provides text output.
github.com/ice799/memprof
gem install memprof
#memprof on irc.freenode.net
memprof.com is separate and visualizes theoutput from the memprof gem.
memprof.com is in alpha.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
74/75
Special Thanks
Aman Gupta (@tmm1) - web ui, json output, andmuch more
Jake Douglas (@jakedouglas) - mach-o layer, bugfixes,and more.
Brian Lopez (@brianmario) - because hes cool.
Brian Mitchell (@binary42) - for convincing me to dothis by telling me I wouldnt and was too scared.
8/14/2019 Descent into Darkness: Understanding your system's binary interface is the only way out.
75/75
Questions ?
@joedamato
timetobleed.com
github.com/ice799