Dear viewer, these subtitles were generated by a machine via the service YouTube and therefore are (very) buggy. If you are capable, please help us to create good quality subtitles: https://c3subtitles.de/talk/2864 Thanks!
[Music]
good evening Sal Z our next speaker Ben
is a math afficionado working undercover
as a security researcher he dabbles in
reverse engineering cryptography and
machine learning though his secret dream
well it's not so much of a secret now is
finding an application of metric space
Theory to anything at all put your hands
together for
Ben all right uh hello everyone and
welcome to rust binary analysis feature
by feature well I was already introduced
but here I am introducing myself again
my name is Ben I am a security
researcher my day job involves a lot of
reverse engineering but also sometimes
machine learning and cryptography now do
excuse me I'm a bit under the we today
so even when I Collapse in a fit of
cuffs have some patience please so rust
is great it's fast it runs close to the
metal it revents whole categories of
memory safety bugs but unfortunately
reverse engineering rust binaries has a
pretty notorious reputation so in order
to alleviate this sad State of Affairs
this stock is going to have three main
goals first of all we're going to
understand a rust binary beginning to
end by getting to know all of the ways
that the features Express themselves in
assembly uh related to that we're going
to get more familiar with the way that
the rust compiler expresses itself and
expresses language features in Assembly
Language and finally hopefully we're
going to reach a state of better and
greater understanding of how the RAS
compiler thinks and operates now this
talk is not the equivalent of reading
the book practical malware analysis but
for rust instead of C right
understanding air rust binary is not the
equivalent of being able to understand
every rust binary you're not going to
walk out of here ready to face the big
bad evil jungle of a malicious rust
binaries out there everything here is
compiled in the buug mode with the
symbols intact this is meant to be more
like a a friendly introduction to the
way that the RAS compiler thinks in a
calm and controlled environment now this
talk is based on a gig gantic blog post
we only have 30 minutes so you're going
to get just the highlights the most
exciting Parts there's also an accompany
in git repo where all of the sources and
all of the binaries that you're going to
see are available for you to play around
with to your heart's content so let's
start with some basic programming
language features and how the RAS
compiler expresses them in Assembly
Language in order to test that out we
put together a sample program there's
like nothing special here A bunch of
person objects are being con instructed
and each person has a name an age and a
favorite Beetle and then their ages in
the future are being printed out and
someone's favorite song is being printed
out too the specifics don't really
matter I just wanted you to have the
general context of where all of the
source Snippets that you're going to see
let later are coming from now when
reverse engineering or R binary probably
one of the Fe first things that you're
going to run across is this right rust
binary hard Cod strings do not have null
Terminators which means that us usually
if you're opening such a binary in Ida
for example it's going to squish
together all of the strings into like
one mess so one way to fix this in Ida
for example is to go to the beginning of
the string press asterisk and then
manually feed in the correct string
length but another thing that you could
do is well 3 weeks after we published
the original blog post that I talked
about earlier hex-rays published their
own rust analysis plug-in Tech preview
which is aimed at solving exactly this
issue so I encourage you to try this out
too now when doing RSE engineering uh
it's always very useful to look at the
state of dynamic memory while the
program is executing you're going to see
a lot of things that you don't see just
looking at the static assembly and in
this case one of the first things that
jumps at you is how the string object is
constructed under the hood it's nothing
very complicated there are three Fields
Land keeping track of the string length
buff keeping track of a pointer to the
raw contents of the string and Capac
which is keeping track of the String's
current maximum capacity like I said
nothing too complicated but first of all
it's useful to know and second of all
it's going to pop back up in a right
moment when you talk about some types so
it's useful for you to keep this in mind
now the final basic feature I wanted to
talk about is this for Loop which looks
pretty innocent on the surface but like
under the hood it's an alien wearing
human skin uh it's very complicated for
a for Loop there like an object being
constructed here and some function is
being called on it repeatedly
very strange so let's dive in and see
what's going on here well yes an object
is being constructed here something
called a range object it has two Fields
a start field and an end field start is
initialized to zero and end is
initialized to 10 in this case and the
function is being repeatedly called on
this object this function is called
Next now the next function is doing two
things first of all it returns an object
and this object is of a type that for
the time being I'm going to call a maybe
integer maybe it's an integer and maybe
it's nothing this is implemented using
two Fields the maybe field which is zero
for nothing and one for there's
something here and an integer field for
cling track of the actual integer now
the logic According to which the next
function decides what kind of maybe
integer to return is as follows try
following this if the start field is
less than the end field then next is
going to return the value of the start
field as a maybe inter with the maybe
field at one like an actual integer is
there but if the start field equals the
end field or God forbid it's larger than
the Ed field somehow then the next
function is going to return a maybe
integer with a maybe field set to zero
which means it's just returned nothing
now after deciding what may be integer
to return the next function does another
thing which is mutate the range object
by incrementing the start field by one
so if you follow this what is now going
to happen when we take the range object
and repeatedly call next on it first
next is going to return a maybe integer
like actual an actual something with the
integer set to zero then it's going to
mutate the range object the start field
is now one next is now called again so
now you're going to get the integer one
returned to you as something an actual
something with maybe F set one and in
that way you're going to get uh two as a
maybe integer three as a maybe integer
and so forth and so on until we reach n
n now after we get nine as a maybe
integer the next state of the range
object that next is going to be called
on has start equaling end equaling 10
now based on what I've explained earlier
when next is being called on this object
it's going to return nothing and maybe
integer with the maybe fi set to zero I
hope that looking from the outside in
you are able to appreciate that this
entire Contraption is emulating the
exact logic needed in order to implement
a for Loop you're getting all of those
integers one after the other Z than one
then two etc etc and then finally when
there's nothing more to iterate on this
function X is going to return nothing
this signifies that like the loop is
done now I will excuse you for looking
at this thing and uh calling it over
engineered but actually this is a pretty
useful construction because it allows
the RAS compiler to perform iteration or
for Loop Logic on strs and objects that
are more complicated than just integers
and we are going to see an example of
this later now let's talk about the type
system in R and more specifically let's
talk about some types because we need to
understand some types in order to
understand error handling in Rust and we
need to understand error handling in
Rust it turns out in order to understand
even the most basic stuff that happens
in a rust program once it is compiled so
what is a S type anyway well it's kind
of like an inam except each variant of
an inam of this inam can potentially
wrap another data type I think the
easiest way to understand it is to see
the example such as the example on the
screen right now this Su type one has
two possible variants a one option
wrapping an integer and another option
which also happens to WRA an
integer now how does the compiler
implement this thing not in a
complicated way it allocates two Fields
uh two keywords uh one is the variant
field which keeps track whether this is
a one option or in another option one
option is encoded as zero and another
option as one and another field which
keeps track of the value of the integer
being wrapped here this is again not
complicated if I had given you the task
to implement this thing you would have
implemented this in the exact same way
unfortunately the way that the RAS
compiler constructs some types of the
assembly level is not always so
straightforward when we tried to compile
something a little bit more complicated
than that which is like almost the exact
same some type but this time the another
option variant was wrapping a string we
ran into something strange you would
naively expect this to take up four
Fields right because the string takes up
three we saw that earlier and you need
an another field in order to keep track
of the varant so that's four fields in
total but instead the compiler gets
clever it says I'm not going to do it in
four Fields if I can do it in free how
does it do that it notices that in a
string object the buff pointer is not
legally allowed to take the value zero
right rust doesn't have the amazing
feature known as a null pointer so uh
it based on this the compiler decides to
assign the uh buff the possible value of
buff zero to the possibility that this
is not a string at all this is an
integer and it's the one option variant
and when the compiler needs to generate
code it's going to check what variant
this is it's going to generate code it
goes straight to the buff pointer and
Compares it to zero if it's zero we're
looking at one option otherwise we're
looking at another option pretty simple
after all so now we're ready to talk
about error handling in Russ and
specifically the types option and result
congratulations you already understand
option and result these are just regular
s types it just so happens that they
come with a tone of syntactic sugar out
of the box from the rust language but
otherwise at their hearts there are just
some types right you could have this for
example some type 6 which has two
variants one option wrapping nothing and
another option wrapping an integer
completely analogously by just renaming
the variants you could have a some type
called an option of an I64 where the
variant none is wrapping nothing and the
variant Su is wrapping an integer kind
of like something that can be an integer
or nothing that's right that's the maybe
integer we saw earlier that's like its
proper name it's an option of an I64
similarly just like you could have a
some type 7even there on the bottom left
which has two varant one option wrapping
an integer and another option wrapping a
string you could rename the variance and
end up with this this type called the
result of an I64 in the string where the
uh variants are an okay variant which is
wrapping an integer and an a variant
which is wrapping a string just as a
hypothetical example of when you would
want to return a type like that for
example you have like a function which
is performing some arithmetic uh if
everything went fine you will be
returning the okay variant wrapping the
result and if something went wrong you
will be returning the air variant with
like a description of what went wrong
exactly so how do these option and
result types look like in assembly after
the compiler is implementing them well
there's no huge surprise here we already
saw how the compiler Implement Su types
so like I said these are just regular Su
types if the compiler can piggy back off
of some kind of impossible value such as
the buff pointer in a string that cannot
take the value zero then the compiler is
going to do so and if it cannot it is
obviously not going to do so the one new
thing in this slide that you did not see
earlier is the way the compiler
implements the option of a b bet now a
betle is an inam that goes up to three 0
1 2 or three and the compiler again
piggybacks off of an impossible value
this time the impossible value four the
compiler assigns four to mean none like
there is no Beetle there and in this way
the compiler is able to implement the
option of a beetle in just one field
instead of two so we're ready to talk
about something called the try operator
now a lot of functions in Rust return
options and results and if you're going
to to uh try and handle each one of
those returns option and results with
boilerplate code checking if this is
like you got the result which is fine or
not fine then your program is going to
end up pretty clunky it gets uh very
unwieldy so for this reason rust
introduced like this little toy called
the triy operator which is kind of a
short hand you put it next to an option
or a result or some other type which can
be like fine or not fine I'm going to
say it has like folable nature and later
I'll say more precisely what I mean by
that and from the programmer's point of
view what this question mark does is
that when put next to uh like type that
can be fine or not fine it checks if the
type is fine or not fine if it is fine
the value inside gets unrapped meaning
if you earlier had an like okay of an
integer now you have an actual integer
and the function can continue its
computation but if the variant is not
fine then the logic gets short circuited
and this error value gets propagated
back up the stock immediately this error
value becomes essentially the function's
return value this is like a very handy
and tic way to handle errors options and
results in Rust so there's this short
hand for it and the way that it gets
implemented under the hood by the
compiler is that for each such type that
has like folable nature it can be fine
or not fine there's a function called
Branch it's not the same function for
all the types it's a different function
per type and later we're going to talk
about how and why exactly this is true
but the point is that after branch is
called on such a type it returns another
sound type called the control flow the
variance of a control flow are pretty
self-explanatory continue means
everything's fine execution can continue
and break means stop something went
wrong propagate the error value back up
the stack so now we're ready to talk
about the final sum type that we're
going to see today which is the control
flow of a person and an
error why do we care about that because
in the original program a bunch of these
person objects are being constructed and
the construction can fail so the
programmer naturally puts a question
mark like the triy operator next to the
uh result value that results and now the
compiler in order to decide how to
continue to in order to generate code
that's going to decide how execution is
going to proceed it's going to check
what variant is the control flow struct
that was generated by calling the branch
function on this result so in order to
understand how this check is going to
look at the bite level we need to
understand how this control flow
structure is structured at the bite
level and well we already know how a
person is structured right they have a
name which is a string an age which is
an integer and an option of a be which
goes up to four remember four is none
and there's of course the error inam
which is just a regular inam now it's
not a huge mystery how the compiler is
going to handle finding space for the
error inam once the person structure is
not there there's plenty of space left
the arum can go instead of some part of
the name or the age or whatever the
interesting part is how the compiler is
going to encode whether this control
flow variant is a continue variant or a
break variant meaning if something went
wrong or not and as it turns out it pigb
backs off of the option of a Beatle inam
again right now it notices that the
value five is impossible so it assigns
the value five in that field to mean
that this control flow structure is a
break variant and effectively something
went wrong now what all of what I've
just told you Joly implies is that if
you take a very simple idiomatic program
that constructs a bunch of these person
objects and then the uses the triy
operator on each because the
construction can fail in order to see if
construction went well or not the
compiler is going to uh look at that
person and compare their favorite Beetle
field value to five where uh I'll remind
you the uh according to the programmer
who programmed this the inim values only
go up to three they are supposed to be
only four bitles so when we first saw
this we got really really confused we
thought that like we understood
something fundamentally wrong but based
on everything that I've just told you it
should make complete sense to you why
after a person object is being
constructed the compiler is going to
check if the construction went well or
Not by approaching this person and
asking them whether they really happen
to like Beetle number six out of four
right maybe you originally thought there
were only four beetles but you were
mistaken there are six John Paul George
Ringo none and an error has occurred
during the construction of this person
object
so now with this new insight in mind
we're ready to talk about the trade
system in Russ more specifically traits
generics and monomorph isation and the
surprising second order effects that
these have for the way rust binaries are
construct constructed and are best
approached a tra is kind of like a Java
interface but better this is really not
a place for me to delve into that the
bottom line is that it's kind of like a
contract that the type can Implement for
example if you want your type to
implement the person trait as it shows
up on screen here your type needs to
implement the functions name AG in
future and favorite Beetle all with
these spe specific exact signatures now
rust has a nice feature called generics
also available in some other programming
languages and in Rust among other things
something you can do with generics is
Implement a function instead of for some
specific concrete type Implement a
function for every type in theory that
implements some specific trait such as
for example on screen you can see an
implementation of a function Fu for
every type T which implements the person
trait now of course when the compiler is
putting together the binary it's not
going to be be able to call some
abstract notion of a method that should
exist in theory because the type T
implements the trait what it's going to
do in practice is that it's going to use
the like basically the image on the left
the implementation of Fu as kind of like
a skeleton or a mold and wherever there
is a call to a method that should
theoretically exist the compiler is
going to consider that like a hole it's
going to patch that hole by inserting a
call to the actual implementation of
that concrete type for that method which
is guaranteed to Exist by the compiler
because we required the trait bound this
uh implementing a function like this for
any type that implements a specific
trait is called the blanket
implementation now an
immediate consequence of the existence
of blanket implementations is that you
can end up with like uh seven or eight
or however many different copies of the
same function just implemented for a
different types and nothing here is
happening at runtime regarding the
understanding of what type is being
handled there's no Dynamic dispatch here
so this process of creating different
functions that are almost the same just
the call addresses are different here
and there is called monomorph isation so
how does this whole ecosystem of generic
Straits and monomorph isation affect the
structure of rust binaries it turns out
in many interesting and arming ways
first of all this is more of a general
note it's really very useful to be
thinking in trades when you're looking
at a rust binary like everything I've
talked about up to now was actually
based on trades even though I kind of
skirted around that fact you can only
call printland on a string because it
implements the display trait you can
only call next on a range object because
it implements the iterator trait you can
only call the uh try operator on an
option of a be because remember earlier
I said it has like fallible nature it
can be fine or not fine the Practical
meaning of this is that it implements
the tri trait and tying into that if you
walk into a Ras binary and try to
understand what functions you're looking
at without thinking in traits you're
going to have a really bad time even and
especially if you have access to the
symbols the autogenerated symbols by the
RAS compiler are not so great not so
legible H they get they become a big
mess pretty quickly there are all sorts
of factors uh at blame for this I think
generics is the biggest one but the
thing is there's hope because if you
look at the top example here for example
a same programmer would call this
function just by the name next which
actually appears somewhere in there I
put an arror there to point you at it
and similarly you can understand a lot
of what these functions do and what they
are for if you are thinking in traits
and you are laser focused on four
questions which is this is an implement
ation of what function for what type
returning what type and all of this is
possible because of what tra so for
example if you look at the completely
illegible example on the bottom if you
go hunting for these needles in this
jumble of a name and in the context
surrounding the function call Guided by
the four questions I just outlined you
will be able to find out that this is an
implementation of the function next for
a values type returning an option of a
person of this is possible because the
values type implements the iterator
trait another implication of the likeall
traits ecosystem immediately follows
from uh the way monomorph isation works
it it concerns your favorite function
similarity engine because of monomorph
isation like I said you can easily get
many copies of a function which look
almost exactly the same just some call
addresses are different and if you're
dealing with Library code these can also
be spread across many different binaries
so of course your favorite function
similarity engine can get confused by
this if it's looking at a blanket
implementation and saying hey I
recognize this this looks like function
X for type y maybe you want to give the
function X part more credibility than
the type Y part now this only happens
for blanket implementations if you're
looking at different other kinds of
implementation such as such as an
implementation of a function or a trait
for a certain concrete type or somewhere
where the programmer ask the compiler to
derive an implementation using What's
called the derive macro then you're not
going to get this phenomenon the final
implication that I wanted to go over
with this like whole trade ecosystem is
what I would like to call the issue of
modularity in the same world user code
calls Library code but in Rust word you
can easily end in a situation where the
converse is true how so suppose Eve is
writing some kind of program which is
sorting people's birth records and she
calls the sort function on on the array
of birth records but then the compiler
the r compiler like it likes to do it
complains it says that the record object
does not implement the order trait the
function for comp Atomic comparison of
two records is missing the CMP
comparison function so if implements
this function and then the program
compiles and she is happy now 3 months
later we are looking at this program and
we are essentially looking at a
situation where the function sort which
is Library code if didn't write it is
calling a function which is calling a
function which is calling function which
12 levels down is eventually calling the
one piece of user defined code that we
actually care about uh this comparison
function that if wrote so this is a
spicy situation and you need to be ready
for it like theage goes now you know and
knowing is half the battle now the final
implication of the tra ecosystem and of
thinking in tra in general that I want
to go over is the existence of
containers and code that manipulates
containers Russ has several built-in
containers
ERS the two most important ones are
hashmap and Vector hashmap is kind of
like a python dictionary if you look for
example at this Source what it's doing
is that it pulls these person objects
out of a vector then takes their names
and ages and pushes them as key value
pairs into a hashmap and if you go into
this assembly without thinking in traits
then you're going to have a really
horrible time but if you go in in with a
disciplined mind and focus on the four
questions that I outlined earlier it
turns out that what's going on here is
not so incomprehensible you start out
with this Vector of person objects now
this Vector implements the into iterator
trait so the compiler can call into iter
on it the result of this call is an into
iter object which in turn implements the
iterator trait so the compiler can
repeatedly call next on it remember this
is for Loop logic it's going to every
call to next is going to return an
option of a person either something a
person or nothing so as long long as the
compiler keeps getting these person
objects and not nothing which signifies
that the iteration is done it's going to
call name and AG in future on these
person objects which is possible because
the person object implements the person
trait now the compiler has the person's
name and age almost because age in
future can fail so actually it returns a
result the program is going to put the
triy operator there under the hood the
compiler is going to call branch which
is possible because this result
implements the TR rate we've been over
this already
and
finally uh the compiler now actually has
the person's name and age and it can
call the insert function on the hashmap
with this name and age as parameters the
insert function is implemented for this
kind of hashmap directly and not as part
of any trait see not so terrible after
all now the final subject that I wanted
to touch upon I don't know how much time
we have left but it's not huge it's like
uh three or four slides is the subject
of iter ators and closures now an
iterator is something you can call next
on we've been over this already and a
closure is well it's kind of like an
anomalous function very convenient to
use as an argument or a return type of
another function closures have many
other interesting features and
properties this is not the place for me
to delve into that the bottom line as
far as we are concerned right now is
that together iterators and closures
allow you to write code like what you
see right now in the bottom image uh of
this slide which is the most beautiful
code in the world that no one except for
you is going to be able to understand
now unfortunately some people swear by
this coding style or so I have heard so
we felt compelled to push this through
to the compiler 2 and see what comes out
the other end and we started out with a
simple iterator expression and we got
immediate weirdness back out now on the
surface level there's a some function
being called here which should make
sense right after the range object is
being initialized the sum function which
is part of the iterator trait is
delegated to the sum function which is
part of the sum trade all right this in
turn gets delegated to the fold function
which is back again part of the iterator
trait now the way that fold Works fold
is something called an iterator consumer
it has access to a certain closure and
what it does it takes the first two item
of the iterator gives them as arguments
to the closure and gets a result now it
takes this result and the next item of
the iterator gives them as arguments to
the same closure back again and gets a
result again now the result on the next
item of the iterator again and again and
again until the iterator is finally
consumed I hope you can see that in the
special case where the closure in
question is simple addition then this
fold operation implements summation uh
of this uh iterator items so this is how
summation is implemented under the hood
under this like lasagna of course
concluding in
closure and uh now we're ready to see
the next uh iterator now before uh I go
to the next iterator I just wanted to
point your attention to something
interesting which is that the addition
closure here contrary to what what you
might have expected it's not given as an
argument to The Fault function the call
is placed directly at compile time uh to
the uh into the fault function so uh
this is kind of like what we saw earlier
if monomorph isation you need to be
ready for it now try to compile another
iterator expression uh this time every
integer is multiplied by 19 before the
summation this is done using the map
iterator adapter and again on the
surface level everything seems to be
Dandy there's a call to map and then a
call to sum unfortunately if you drill
down into the functionality you find out
that specifically when this iterator
expression is compiled in debug mode the
map function does not actually do
anything right there's a call to a call
to a call which does Jack squat instead
uh the multiplication and the addition
both live inside of the specialized
closure which is spliced into the co to
fold so this should teach you something
about how the RAS compiler operates it
has no respect for the autonomy or
boundaries of any struct or function if
the compiler has decided that it's more
optimal for the map function not to map
and for the fold function instead to map
in addition to folding then that's just
what's going to happen so now we're
mentally ready to look at the actual
iterator that appears in the sample
program Believe It or Not the image on
the left is equivalent to the for Loop
that you saw in the beginning of the
talk now this is about as complex as the
creature on the right called V3 which is
an iterator which is built out of a call
to iterate and then some iterator
adapters like take while cycle and take
now you're mentally ready to hear that
when compiled in thebug mode most of
those calls do not actually have
functionality in them iterate doesn't do
anything take while doesn't do anything
cycle almost doesn't do anything cycle
forces the compiler to allocate a deep
copy of the initial state of the
iterator right because eventually the
iterator is going to run out of items
and then we're going to have to need in
order to cycle we need to access the
first item in the iterator again and we
found a copy of the fresh pristine state
of the iterator before it started being
exhausted uh there's no guarantee that
the first item will be accessible but
other than that cycle doesn't do
anything finally the call to take uh as
very deep inside of it the
implementation of the function next for
this hideous iterator cure that has been
assembled here and in this to next
you're going to find all of the logic
that you would have naively expected to
live inside of the calls to iterate take
while cycle etc etc and the birth dat
iterator from the sample program behaves
in pretty much an analogous fashion for
example there's a function there called
far enough in future where which in the
assembly it leaves 14 levels down the
col stack from where the initial
iterator is constructed so I uh
hopefully now you uh know better what to
expect when you are analyzing code that
has been compiled from this kind of
source code in debug mode and it is my
sincere wish to you that you never have
to actually do that
so I
hope
that I hope that you have seen and heard
enough today so the next time you see a
rust binary being open in your favorite
disassembler you do not recoil
immediately in horror but are able to
recoil in a more calm and dignified
manner
thank you very much for
[Applause]
listening thank you so much ben we have
signal Angels scouring the internet for
people that are slowly oxidizing with
questions we are monitoring on Mastadon
we're monitoring on Matrix and we're
monitoring on IRC this is room Zeus so
use the appropriate hashtags our signal
angels will take your your questions and
relay them to me I see however in the
hall we have already some questions so
please uh so you said
that uh when you call a function on a
trait it just calls the function
directly how does that work with a box
trait uh box uh actually like I said uh
in the beginning I only had 30 minutes
you don't understand how much stuff went
away before I gave this lecture right
the is read the blog post yeah yeah it's
in the post uh yeah the the thing is
initially I had like a 100 slides it was
a nightmare I like started talking and I
realized that like 2 hours had passed
and I was that not that's not good so
this is how we ended up with the talk as
it is there's a description of box
actually in blog post and d trate and
even a little taste of release bills
actually oh nice so uh I welcome you to
read up on it there thanks I'll read
that thank you I see that the crab
people in the internet have a
question so the internet wants to know
where did you get the graphics from H
the graphics well it's not like big
surprise I think everybody being looking
at those Graphics would be able to guess
where they came from they came from like
D I have a paid Char gbt subscription so
I asked you to generate images well to
be more precise this was an ugly process
what happened was each time I needed an
image and I asked D to generate the
image it was no not like that please not
like that no that's not what I for it
all again and again and again until
eventually something like palatable was
ejected out of this process so like you
were seeing just the shiny end
product see maybe there's a question on
microphone
one um yeah uh thanks for the
introduction to the uh what the rust
compiler does so some people cannot
choose to just close the binary again
they have to analy analyze it um where
would you say like which of these areas
are most amable to devel veloping
tooling to uh ease the process of
reverse engineering it and are you aware
of any good tooling out there that helps
with yeah actually this is this your
question is the exact reason I had like
this huge disclaimer slide in the
beginning because I knew that actually
what I really wanted to do was just
download some kind of rust sample of a
virus and reverse it when I tried to do
that I like I spent the next half an
hour in the corner weeping and I
realized that there's like there's a
First Step that needs to be taken I know
that there are rust uh tools for reverse
engineering out there there's the tech
preview that x-ray is published and
there's I believe a name The Mangler
also floating out there but the the
State ofthe art as far as I know on this
subject is uh really immature and
lacking and personally uh when I'm
thinking about what kind of next work I
might do this is exactly what I'm
thinking thinking of taking all of this
Theory and turning it into actionable
practice for people having to actually
deal with these God out for
binaries thanks I have to imagine that
the internet has more questions about
rust binary analysis no how could that
be thankfully we have questions in the
room yeah thank you for the great talk
uh so I was wondering because all of the
or most of the basic blocks on the
slides had this jump Dollar Plus to so
we are jumping two byes ahead is that
something that the compiler uh just
emits in the debug mode to like I don't
know tell you what what what is the
basic block corresponds to in the code
or why is it there look uh I'm really
sorry but uh questions as to why the
compiler does anything or a real
crapshoot I I'm not sure I can answer
that I can give you like a counter
example of my own uh one of the things
that didn't make it to this presentation
was the way that the stock frames are
built in these the buug compile rust
binaries and the stock frames are much
simpler uh it's they only use RSP no of
RBP except in the main function where
some value is being strategically moved
into RBP and then all the arguments and
all the variables are referred
relatively to this invented value of RBP
I went to Reddit and uh asked on the
rust Forum like does anyone know why
this is the case and no one had any idea
so I'm sorry as I I'm not sure I can
answer this question okay thank you we
can take another question from
microphone 3
yeah yeah that's you yeah um so
uh um oh you were microphone 2 oh sorry
you are microphone 3 I am microphone
that is my mistake I'm so sorry so uh um
what would the best way be to handle the
uh enum values for some types is there
tooling or do you do it manually um the
work I did here was manually truth be
told really most of the challenge was
getting over the shock of seeing the
bfield being compared to five which was
a value that shouldn't have existed I
mean after that it was it wasn't so
difficult working manually but of course
in the future automatic tooling will
have to be developed for this kind of
thing right I agree with you that it's
like it's not a good long-term plan for
people to do this kind of thing
manually do we have a question for
microphone number five oh sorry uh
microphone number
two is the um the allocation and
interpret of invalid values like the uh
the fifth and sixth Beetle uh something
scoped or may change along the the same
frame no I believe that it's like the
moment that the compiler decides how a
sound type is going to be constructed
then this applies across the program
that that is my impression at
least thank you microphone number one
here in the front thanks for your talk
it was only debug mode binaries that you
uh showed right yes that's that's right
there's some release mode binaries in
the blog post but like they didn't make
it into the stock is your experience at
people deploy malware compiled in debug
mode I'm sorry
if I presume you analyze malware by yeah
I look when it comes to rust malware
like on our team a lot of the experience
has been just like the tweet in the
first slide it's like there less
analysis more like
despair um now regarding look regarding
a question of how much debug binaries
represent the real world again I I made
the disclaimer slid specifically to
deflect this question why there was a
level of understanding that needed to be
reached before we go into the real world
and get like our face completely slapped
there so this was the intention of this
talk all right we have time for one more
question from microphone number five I
keep doing that it's almost like I just
wasn't paying any attention to your talk
and that's not a valid number that's not
the case at all one two
what where are you going you got a
question mhone 2 or five okay it's
microphone 2 yeah I also wanted to thank
you for your talk and I also wanted to
ask a little bit about the release mode
of the compiler um I think the compiler
is supposed to strip away most of the
abstractions and how much does it work
so is there actually anything left of
the complicated stuff you showed us uh
look this is interesting I think in
order to understand optimized code it's
very important to first understand the
unoptimized code the optimized code was
generated from and I think a great
example of this is the for Loop logic
because if you write a for Loop like the
one seen in the source there and you
compile it in release mode all of this
uh complicated Machinery of calling next
and all of that is going to be stripped
away we're going to get a for Loop just
like from a c program but if you write a
for Loop for a more complicated type of
object then these structures are going
going to appear again now it's true that
a lot of logic that you saw a lot of
this like useless lasagna logic gets
wiped away usually because uh in release
mode because a lot of these obstructions
uh are useless they don't contribute
anything in the trivial cases that you
run across usually so it's it's correct
that if you take like a normal binary a
run-ofthe-mill binary which doesn't do
anything too complicated and the bug it
in release mode you're going to get like
a total wipe out like an 80% wipe out of
a lot of the stuff you saw here but it's
still important to understand what was
wiped out and why so the most of the
sorry I have to cut you off we're we're
over time but thank you so much ben
let's put our hands
together thank
[Applause]
you
[Music]
la