Dear viewer, these subtitles were generated by a machine via the service Trint and therefore are (very) buggy. If you are capable, please help us to create good quality subtitles: https://c3subtitles.de/talk/871 Thanks!
So for the next talk of today, we're
going to talk about a new source
DBI framework, which is called Kubi, the
DBI may means dynamic
binary instrumentation dynamics.
Well, you know, binary also
instrumentation means to observe,
monitor and modify some
parts of the some parts of a program
or a library.
And we have a speaker, Charles Shubane
Sedrick this year, because
they are both security researchers.
Charles likes attacking Whitebook Crypto
and collaborated with Graham
and Sedrick.
Instead, he's focused on reverse
engineering and he likes whiskey.
There you go. The stage is yours.
Give them a hand, please.
So I think the jurors did
a good job at introducing us,
so we both work at Quickstop,
which is a French consulting security
company, and during
for our work we use a lot DBI.
And for the past year and a half, we've
been together researching DBI frameworks
and also working on our own
DBI framework.
And what we want today is to try to
demystify what DBI framework
he's outworks and
try to show you how we went about
implementing our own DBI.
And we hope to inspire
you about the usage of Dibia framework
and maybe even helping us
on our own.
So I'll start easy and start with
an introduction of what is exactly
instrumentation.
So basically this
is the transformation of a program into
its own measurement tool.
So you're making an instrument
and this instrument, what you can do is
that you can observe any state of the
program any time during the
runtime.
And then after that, what you will
the tool will automate the data
collection and the processing of those
data.
So that's quite abstract.
So what could you do with that kind
of instrumentation?
Well, I'm pretty sure most
of you, if you program into C++,
you already use vulgarized
and maybe the magic tool and
that's the background is in
instrumentation framework and music is an
instrumentation tool.
And what it does, it's quite simple.
It will track memory allocation and
geolocation during the runtime and
also track memory accesses.
And with that information, it is able
to both detect
use after free memory leaks, the
belfry, but also out of memory
access and then can tell you when your
you are accessing the area to buy it
outside of the boundary that are
that you've allocated at that place in
the program.
So it's quite useful.
Another
popular use case is fuzzing.
So in fuzzing one of the
first thing, you're generating random
input and sending it to a program and try
to make it crash.
But one of the things you want to do is
to generate interesting random inputs.
So you want to know if
between two random inputs, if the new
one you're generating are
are exploring more part of the code,
are generating more state transition and
things like that.
So you want to measure the execution.
And one of the main
criteria you can measure is the measuring
the cloud coverage.
So DBI framework is a very good
thing to measure cloud coverage.
And if you use Winnefeld, basically
it's AFL using a DPI framework
which is interloping to gather
good coverage informations.
And if you go deeper, you can even use
the information you have about what
instructional executed to try to build
a symbolic representation of the
programing, etc..
And then you get the DA Procyon
conference that was
held one year and a
half ago.
The last thing you can do is
simply execution Trista's, which
means recording everything that happens
in a program.
Once you have that, you can replay
those execution traces and you get
something that looks like a timeless
debugger where you can go forward and
backward and you can try to
track everything that's happening in the
program without relaunching it.
Also, something that I've been working
on before was a software side channel
attack against cryptography.
So you're using the execution tricks to
generate Citronelle information and
trying to recover a key from
obfuscated cryptography.
So so far, one of the questions
for you might be asking
is why not just the burgers?
Because the burgers can do all of that.
The problem is that these burgers,
although there are some they are meant
for human, not for machines,
and they are very slow.
So if you imagine
a burger that's attached to a target
and you imagine the target exposed
and you press continue, this
is what happens on the on
your system.
So the burgers and a resume
call to the colonel, the
colonel decides to reschedule the
execution of the target.
So you have the whole scheduling of the
process that happens.
And then the target hit a break point,
which is like a trap to interrupt,
which jumps back into the kernel.
And then the colonel
decide to that.
It seems that the interrupters
should be captured by the debugger,
basically. So it's going to send a signal
to the debugger and reschedule the
execution of the debugger.
And while you're doing that for
one continuous breakpoint hit
you're doing for a boundary crossing
between Ukraine and Colonel End, and
also you're trying to process
rescheduling.
So it's quite slow.
How slow
maybe you've
seen those kind of attacks you can do
in cities where you have to reverse
engineer and binary that checks the
password. And what you do is you
try to measure the execution time,
what you're trying different password and
not really the execution time.
Something more interesting, which is the
instruction count, the number of
instructions executed,
and you can try to brute force the
password character by character this way.
And what you see on the top is GDP.
And what you see on the bottom is
interacting.
So on the bottom, you see that it
actually can take a lot of cases
and that you see the number of
instructions executed.
And while GDP is extremely
slow and even though the instruction
is much lower.
So, yeah, you don't want to
use GDP, you don't want to use a
debugger, you want to use a dynamic
instrumentation framework.
So the solution?
Well, it's to get rid of the kernel
and how. Well, the only solution, except
if you want to really have a bare metal
system, is to run the instrumentation
inside the target.
So instrumentation
techniques, so there is a few different
way to do that, the first one
is from source code and I'm pretty sure
everybody has done that is just adding
print statement to your code and get
information. It's basically
instrumentation, but you
could be more smarter and
try to do that automatically at
compile time with the compiler plugin.
The other solution is when you work from
binary and when you work from binary,
it is basically to approach.
The first one is to take your binary and
statically patch it and add hooks
to additional code you insert into the
binary.
And the last one is dynamic binary
instrumentation.
So from source code is really boring.
There's nothing very complex and
interesting to know about that,
from static binary patching and hooking
while it's crude in BOBRICK
because you have to know in advance where
you want to insert your hook, which is
not really possible.
And so this talk is about dynamic binary
instrumentation.
So there's a few existing frameworks.
And the first one is vulgarized,
as I mentioned, that exist since 2000
is open source, however, only
supports Unix platforms and is very
complex to use
not to using the command line tool, but
to write your instrumentation to.
Another one is Diana Muriel, which
existed in 2002, which is open
source, its cross platform,
cross architecture.
However, it's very hard to use too,
because you basically have to manipulate
the instructions in assembly yourself,
which is quite often.
And the last one is interloping,
which is the most popular one because
it's very user friendly.
However, it only support internal
platforms and it's closed source.
So why we made our own
in 2015.
What we wanted was to have a cross
platform and cross architecture DBI
framework, and all of those frameworks
were developed quite a
long time ago. So there was new things
you could try to do that.
And we wanted to focus on mobile and
embedded targets because that's basically
where what we're working on in
our daily job also wanted
to try to have something that was simpler
and that was modular, meaning we could
use it with other reverse
engineering tools easily.
And also because of our past
in cracking DRM, basically,
we wanted to focus on heavy
instrumentation, which means
instrumenting a lot of things, generating
lots of data.
So I'm going now to
to understand the following of the talk.
You need to understand how DBI exactly
work. So I'm going to try to
explain that if I get it correctly.
So the
simple idea is that dynamic
instrumentation is about dynamically
inserting the instrumentation.
So your code you wrote
inside the binary during runtime,
so it looks like this, you
would disassemble the code that's going
to be executed, then
you pass the instructions to
your instrumentation tool that
analyze them and generate the appropriate
instrumentation.
And then the instrumentation get inserted
into the execution flow and
it's executed. And then you have backbend
for scale.
However, I mean, this this
looks simple, but there's
a few problems with this, this graphic.
Not not back.
Other problem, the first
one is disassembling.
You cannot just you don't know
when you get the binary what part of the
binary code? Because this data data
is good and there is a lot of
unpredictable branching and jump
everywhere.
So you cannot just disassemble the whole
binary in advance.
So you need to discover what is going to
be the code of the binary
as you go during the execution.
Basically what this means is that
you will execute a short piece of code, a
block of code, something that
is a series of instructions you can
predict will be executed.
And then that ends with
a controlled flow statement like
conditional dump or call
that you cannot resolve.
And you execute just this piece of code
and then you look where it wants to
flow next. What's the next instruction
you want to execute?
And this way you discover the next block
of code you want to execute and you
simply execute the next block of code.
So this basically form a short cycle
of execution.
Not the other problem you have is
about the instrumentation, the code that
you're generating, because the
instrumentation code you generate is
much larger than the original code,
because the sum of the original code plus
the instrumentation you're adding
and compiler are not dumb and
tried to tightly fits all the code
into the binary code, not with space.
So there's not much space left around
in the code.
S. segment of your binary,
so you cannot
instrumented in place, you can just added
in the binary.
There's not enough space so you need
to write it somewhere else.
And this introduces the problem of
relocating the original code of the
binary.
So the problem with relocating is
that the code contains a
relative reference to memory addresses.
So you might have a germ that's
now jumped to the instruction 20 byte
forward, or you might have a memory
access that say, oh, no, I need to access
that piece of data, which is one thousand
thirty seven bytes backwards
from my position.
And if you remove if you move everything
well, this reference becomes invalid.
So this means we need to actually
conclude you write the original code
to fix all those references
and in our engine
and what will present during this
talk? This is what we call batching.
So if you summarize everything you need
to do, this is the cycle of life of a
DBI engine.
It start by disassembling
the first piece of code it wants
to execute.
Then it's it batched to make it
relocatable.
Then it adds instrumentation to the
batched piece of code.
It assemble it somewhere in executable
memory somewhere else, and
then you can execute it.
And once the execution finish, it
looks at what the next piece of code to
execute and start again.
So now I let Cedric explain some
of the low level obstruction, we have to
deal with all of that.
All
right. So for this
before
and so
basic
stuff like
that, that's what we call
so
together, they are
confusing what is called a control.
So really, at the end of a busy
block of time in the door and switch on,
which can be a jam, for example.
Um, let's go to another one.
And with this kind of structure,
the control flow would just go on.
I mean, you start a function and it's
just execute every basic block and
you don't have any way to interact with
it.
So what we want is some kind
of under control
flow.
And by this I mean we want
after the first block to take the
control back.
We want to be able to skip
the jump or of
modify it to jump inside our
own code. They are we will execute
the logic of the engine and
we will be able to go to
the next block and so on.
But if, in fact, everything
is about keeping control of the execution
and this is much
more difficult that we can expect,
because what we want,
it's it's for
us, it requires to modify
the original instruction of the binary.
But we don't want to modify
the original behavior of the software.
We want to be able
to execute it as
it was and touch.
And this is quite difficult.
And we create lots of low level
tools.
So what do we need?
In fact, very, very
simple thing. We need a multi
architecture disassembled
and we also need multi architecture
assembler and if possible,
it should be able to cross platform
players and
we need some kind of intermediate
representation to work on.
And this representation
should be linked a link
between the disassembly and
the assembler.
So this requirements were
quite strong.
And also what we don't want
well, basically we don't have
unlimited resources.
So if possible, we
don't want, in fact, to implement an
architecture, does a smaller and smaller
and also
about the intermediate representation.
We don't really want to,
like, parse every single
manual for every CPU
of every single architecture we want to
support because
yeah, I like developer manual, but
I'm not that firm, so we don't have
10 years to spend on passing them.
But things are changed recently.
Since a few years.
There is a new player and
that player is called
LVM.
And what is LVM?
11 basically was created to
focus on a compiler technology
and especially just in
time engine.
Um, but basically Agim
is the core foundation,
a framework of a very well
known software today, which is the
Klank compiler.
I'm sure like everybody
in this room knows of the compiler
and but it's smaller than
this. It's like a war tool kit
that provides tons of things to play
with binaries.
So, in fact, for us, LVM
already has everything to
support. A lot of architecture, like all
the major one I am
excited, excited for
it provides both of these
are smaller and smaller.
But more than this, it
also provides this
intermediate representation, the
that link, basically the disassembly
and the assembler.
And maybe some of you already
heard about the LVM
intermediate representation.
But if this is not this one, this is
not the intermediate representation
which is used for the compiler pass.
It's another one, a more low level one,
which is called a matching
code and or VMC,
and this low level representation
is this glue between the disassembly
and assembler.
So what is a little VMC?
Let's start with a very basic, simple
instruction here
for the DBI.
It's just while in the process
of exploring the code, an instructions,
just a series of bytes.
So basically what LRM
provide us, we feed it with
this list of bytes and Olivia
just provided this.
And this in fact is an intermediate
representation already.
It's a very simple one.
You have like two chip.
There is like the MSA insert, which is
the instruction and the instruction is
composed of a list of operand.
But basically if if
it's simple, you have like all
the interesting information that you
need and it's already something that we
can work on.
So of inventory, it's, as
you said, very minimalist with only
a few structures
and it's also totally generic.
So for all the architecture of the use
of the same representation, which is
quite good to work on, because
we can do things in a totally generic
way, but still it includes
lots of things about an instruction.
So at least everything
we need to work on the intuition,
but the down downside, it's kind of very
raw. OK, it's simple, but
structures are simple, but using them
to do complex things, it's going to
feel like something bit complex and we
will see why.
And also they wanted it to
be generic.
So for this, we met some
compromises.
And for example, they don't encode
everything but an instruction that a lot
of glue called a bit of
everywhere, in fact.
And it's makes using this layer
a bit tricky.
So, for example, we want to create
instruction. It's one thing that we will
need to do for DBI.
And if every instruction use the same
representation, in fact, they
use it in slightly a bit different
way. For example, here we have a move.
And this move, as we have seen, is
like, OK, a series of up
around grouped in an instruction.
But the thing is, this
representation, it's not documented.
It's not really standard.
Every instruction possibly used
is on encoding.
And tonight you need to look
at the glue code.
So it's it's not it
has not been created to be an
intermediate representation that you can
work on, basically.
And here we also
want to batch instruction, so we need to
modify it. So if you take a look at the
jump, it's very simple, only
like one instruction, one operand, which
is an immediate.
But if you want to modify it, we will
need to go from there to this.
And you can see, OK, it's totally
different. We will be forced to create
some like outcome modification of them
and.
We also have we want to create Bache,
so a series of transformations
and it's very
easy, very simple example of what we
what is in this that we will need
to patch. In fact, if it's an instruction
which is referencing memory, using the
program conter.
So basically, as I said,
at the moment, we will need to move this
instruction elsewhere in the memory.
But if you move the instruction elsewhere
there of the reference will be broken
because it was based on the current
address of this interaction, which is
EER.
So let's have a look.
We are moving it,
but what we will be forced to do
is, OK, we have more the instruction.
It's located on another address in
memory, but we need to replace
the register of the program
counter with another registar
and this register will be loaded with
the original address value.
And this way, when we will execute this
instruction, the reference will be the
same.
But as you can see, we are breaking
things here. We are modifying the
behavior of the program because we are
erasing this register.
So basically what you need is
also to back up the astrologist
and after
like executing the modifying
instruction, you also need to restore
that first picture. So, as you can see,
creation, creation, KRISHAN modification,
lots of operation here on the
intermediate representation, which is
kind of odd, kind of difficult to work
in.
And so
the encoding is a
bit painful to work on.
And you can see here it was
very, very simple touch
and it's already quite complex with a lot
of transformation and a lot of steps.
And we also had a feeling which
is you can see that we have been forced
to back up something to back a projector
so you can feel that
it will be not only the only patch
when you need to back up a register so
you can feel that you will need
some generic steps that
will be needed like a bit everywhere.
And so basically, we need abstractions.
And the idea is to have like
a magical engine, which we can
call the touch engine, it's
its real name, in fact, and
this it will take one instruction
and input, one original instruction,
apply our transformation
on it using abstraction,
and then I'll
at least one or more
instruction.
And for this,
basically, we are vision
like some twisted vision,
we have said, OK,
maybe, maybe
we can identify
the steps that are required
to apply the transformation.
So maybe you can say, OK,
this patch is in fact just a series
of transformation.
Some of them are totally generic.
So maybe we can just regroup them
and try to integrate them in
sort of like a language,
a specific language
which will be specialized in
patching a binary.
And on the part of the idea
was, OK, the instruction,
even if the representation is generic,
the representation, every instruction we
will have will be architecture specific.
So the patch will be architecture
specific, but possibly, maybe
that the language can be generic
and express modification for more
than one architecture.
And so after
some Edek we add, this
schematic will not explain
everything here.
But what we can explain
is that basically the issue that you
have is that you have two worlds.
You have the world of the original
program and you are the world of
the DBI.
So one guest, one asked
and you have interaction between them.
So the idea of the abstraction was
trying to make things a bit
more organized to, in
fact, be able to see the
relation, the precise relation between
them and
by identifying this
relation, also trying to
create abstractions that allow us
to work more easily
with the binary and to express
the complex theme in a simple way.
I will show you what it really means
with you by using this.
This is something
which is part of the language and it's
something which is a temporary register.
So against you have seen a temporary
register before in this
example. So this is the same example than
before. And this was our temporary
register with our language.
If you want to create something like
this, says a series of instruction,
in fact, you can just say, OK, I want
a temporary register, so I want a
stamp.
And the register will be
just as a free register
automatically by the engine and it will
be identified by an idea so you can
work on with letter
and.
By working on it, I mean, OK,
this back up where needed for
modifying this original instruction
and to do this modification,
in fact, what we are doing here, it's
basically replacing
the register in the original instruction.
So we are doing a substitution
with a temporary register
on the original register, which is
PC, and by using
a reference to a temporary one.
So you can see it's kind of a language
with like keywords and
kind of variables and things like this.
And another idea was
basically putting a binary is
just applying a series of rules,
because if you want to modify
something is because there is a need
to do it, there is,
in fact, a condition, one or more
condition that foreign import
restriction will apply
a series of actions and actions.
Basically, there are our transformations
that we want to apply on.
So let's take
a look at let's have a look at us, what
we call a rules, in fact.
So this is just extracted from
the original code of the debate.
And this is our patch.
This is the things that
replace the program
conter with a temporary
register. And you'll see there is like
one condition here.
So this is only one rule, one condition,
which is you must use a register
PC and then the actual you can
see if it's all very some of them.
But I will not enter into the detail.
But you can find any other substitute
stamp and the temporary register
and so on.
And
a nice thing with this rule, it's
generic.
I mean, it's exactly the same rule that
we are using on a ram x
86 64.
And another example here, it's pure
ram. And here we
basically need to replace the jump, as
you have seen. So that's exactly what
you are doing. The condition is a bit
more complex because we have a
condition which apply on
several inspection.
But the idea is always the same.
You have like keywords for your language,
valuables and things like this.
So what do we learn first
album is really a magic
piece of software.
It's very robust.
It provides staffing and basically
just save us on this.
But the problem we had was
the intermediate representation was
so simple, in fact, that it became very
complex to play with.
And you can do it by and you can really
create patch by like with a giant
switch case.
It's it doesn't work.
It's just breaking your head.
So you really need to focus on
obstruction. And we were quite surprised
or how difficult it was to
create this obstruction.
And honestly, it's still a work
in progress.
But, yeah, it allows us to
make quite complex
transformation in something
that is very easy to read you just off
your list of operation.
It's very easy to understand what the DBA
is doing on your binary.
So I will.
Yet the remote tushar for the next part
of the next part I want to talk about
is how you need to think about
cross architectural support.
So if some
of the DBI formwork like
interloping, has that trouble supporting
instruction set like they actually tried
and then they decided they would not
support it anymore.
And one of the reason is that if you
don't think about cross architectural
support from the start, it can
becomes a very complex.
And I show you how we handle this issue
in our DBI engine.
So if you think about what's
going on in your in the process,
you can divide the space into
two entities.
The first one is the horse.
So the horse contained the DBI engine
and the instrumentation tool
you've written.
And the second part is the guests and
the guests contained the original binary
and the instrumented code generated
by the DBI engine.
So you see that this houseguest
terminology is taken from the VM world
because it kind of makes sense.
Now, this is not a VM.
So the problem is these
two context. They share the same memory
in the same CPU context because they are
just one process.
And this means
that we we're going to need to switch
between the two context of the host
and the guest at every cycle during the
execution.
However, we do not get any help
from the kernel or the CPU because this
is not a VM. We are not going to use a
visualization extension and things like
that.
So this basically means
saving and restoring the CPU context
from the get in the horse and vice versa.
At every time you switch between the two.
However, you need to avoid any
side effect on the guest because the host
is aware that it's a DBI and
does exhibiting.
But the guest is not aware and should not
be aware that you're doing context,
which is between basically another
process inside this process.
So this means that you cannot modify it
stuck and you cannot erase
any of it register or else the program
will just crash.
And the only way to
have something that worked this way
is that the guests need
to be able to relatively
access memory from the host
because you cannot just
compute the memory addressing a register
because you cannot erase a register and
you cannot save that for.
So you want to understand because you
cannot modify the text.
So the only ways that you need to be able
to do direct reference to
Roulet to a relative memory address
in the guest,
however, relate to addressing
is extremely constrained by Sepo
architecture.
So under 86 you can
do 32 bit relative memory
address, but under armor you only
have 12 bits.
And basically, if you
look at the encoding, this mean that
you're limited by a plus forty
four thousand ninety six bits for
the minus underarm.
So the conclusion of this is that
if you want to have a context which that
works nicely
on cross architecture support, you need
to have a situation
where you have memory
really close to the guest goes to the
instrument that could you generated.
The other problem is that if you want to
play nice with data execution prevention
so we could put data next to the
guest, it's simple because
we are generating that code.
However,
we don't want you cannot have a memory
page that would be read, write, execute,
because on some operating system this is
not allowed. So what we're doing
basically is allocating to contiguous
memory. Page one of them is really
execute. The other right is read right.
And this way we can have
satisfied all those conditions.
So it looks like this.
So this is the first page we call
the code block and this is the second
page we call the data block.
This one is really execute.
This one is read. Right.
So the first piece of
code in the code block is the Prolog,
which will is basically in charge of the
context which and to do that context,
which gets simply
stored the host context dir and load
the context they're using relative
addressing.
This is the instrumented code, the one we
generate with the engine, and this is the
epilog which does the inverse
switch.
So the idea behind the blog basically
is to buy an instrument that goes with
instrumentation data,
so data is guaranteed
to be directly addressable.
However, when I said one memory page one
memory is four kilobytes and the
most operating system and this
is a lot of space.
So if you take one basic block
and then you use Facebook page for basic
blocks, you're never going to.
Well, you could, but you need a lot of
ram.
So the solution that you need to be able
to put multiple instruments, a basic
block into the code block
and that you basically
have also a lot of data space left so
you can try to do things with that
data space.
So this is basically what
we got in the end
here. We have a very special things,
which is a selector and the selector
is a jump to someplace
inside the code block and
the selector.
And basically it's a programable job, but
we cannot modify the clock because it's
really execute.
So what it does is to jump to
an address that's actually containing to
block. And this way we can select
which basic block because here we are
going to store multiple basic book which
are basically OK, we want to execute and
each basic log jam to Diplomate yet.
So if you just programed the selector
and then start the execution from the
top, you will execute the basic lock
you want
from the disk space.
We are exploiting the remaining little
space for things we call constant and
shadows, basically.
So the
constant, the instrumentation constant.
Well, you've seen in the patch example
that we wanted to replace the
program counter and wanted to load
a constant.
So this works very well.
And that is Eighty-six where you have a
move that has a 64 bit immediate
into a register.
But this is because it's a Siskins
instruction set and the arm,
you're also very limited in the size
of the image you can load into a
register.
So basically we use this
constant space in the same way as Arm
Litoral for if you're already reverse
engineer arm code, you know that a piece
of code and the next piece of data
and constant that are directly loaded
referenced by the code and
this were the same way.
So this way we can load any kind of data
we want into our instrumentation
without wasting space.
And the other thing, which is a more
interesting concept, is what we call
instruction chateaus.
This is not entirely new.
It's inspired from vulgarized,
vulgarized, the way they track
memory allocation.
Geolocation is that
they create what they call memory shadows
for. So for one page
of memory you allocate in your program,
they create a small buffer that
inside the buffer, each bits represent
the state basically of each byte
of the memory page.
And this is shadowing basically
the memory.
And so it's some kind of variable that's
bind it to the to the memory.
And we we wanted to do the same stuff,
but for instructions.
So it's it's
basically a means for us to
abstract the idea of giving variables
inside your instrumentation code
to make
inline instrumentation very
easy.
One of the main use keys right
now is to record memory access.
So memory access are a bit of a problem
if you want to record everything that's
going on in your program, because
to record read
memory access, you need to instrument
before the instruction, write them or
access you into instrument after the
instruction.
So you adding a lot of instrumentation.
And for example, if you want to call your
own code in a callback,
then you need to switch context again.
So you're making to context for
instruction and then your instrumentation
becomes very slow.
Um, so the solution
is to do what we call in line
instrumentation, where you
are going to do
the recording of the memory access
directly in assembly without using
the instrumentation tool itself.
And these variables,
the shadows basically are used
for that. So for the instruction
that the memory access will create
shadows that are used to simply
stored the memory access and the
address accessed so you can execute the
whole basic block. And then at the end
you can just create a shadow of that
basic log to know which address
and what data were transferred.
So to realize all of that,
uh, it's not easy if you're thinking
about in a military architecture context,
because we need to cross platform mean
basically memory management unit.
And we need obstruction of the debt
because we want to allocate memory and
won't want to change those permissions
and also we want
the architecture assembler that works
in memory.
We don't want to create a binary on the
disk.
And it's not that simple.
A lot of assembler simply assume that
there are going to create sections and a
binary object and things like that.
But this is not the case.
But guess what?
LVM saves us again, because
when the 11 project was started,
it was a little gem for low level vitrine
machine. So they had that bad code
that was across nearly
cross architecture.
And one of the things they did is
build just in engine to execute
that bytecode.
And meeting being a Just-In-Time
engine, it means it's very close to the
design of Adibi.
And so they have everything
we would need for that.
And we cannot
use directly the Just-In-Time engine,
however, because although they're very
well-designed, they do not really fit our
use case in the way we work.
But inside of you have all the
functions you need if you want to create
a just in time all the cross
architecture, memory management
abstractions and also
this powerful in-memory assembler, which
is LMC.
So what we learn from that is that
really, if you want to create a
Just-In-Time, LVM is really perfect for
the job.
But also designing a Just-In-Time
engine for DBI, taking
into account the cross architecture
problem is really difficult because you
can easily be locked down into a
simple architecture if you start assuming
that you can simply access
memory with a 32 bit offset, for example.
And so you need to think about
portability from the start if you want
to design that kind of project.
So all of this,
in fact, are small parts of
our project, which is called
comedy, I could be the stand
for Kwok's club dynamic
binary instrumentation.
We are like very imaginative.
And so it's a cross
platform, cross architecture,
DBI framework
by cross platform, which
means that today it runs
Linux, MapQuest, Windows, Android
and iOS.
And we really focused
on the last few months to
bring something user-friendly, which
is kind of odd, you have like a big
engine, it's a complex machinery,
but we really wanted to have something
really easy. So basically, we focus
on clean his extensive documentation
and we also provide binary packages
for major operating system
or Linux distribution.
And it's a modular design, and by
this, what we means is basically
the car engine of the DBI
should only do what is essential
for DBI.
So no Auntie a.T.M
for nothing related to
Tebogo, I don't know, like everything
which is not part of the DBI.
And the idea was of
keeping the things simple is that by
keeping the things simple, you don't
force users to do things in
your way.
Basically, you don't we don't
have like one injection method
that you need to use
that force you to do things
in a certain way and limit you basically
by doing this.
And what we have
at the end, it's basically is integration
because the idea is just a library,
static or dynamic library, your choice.
And so we have created with this library
python bindings to allow very
fast experimentation.
And we also have like
full featured integration with Freida
Freud. I'm sure lots of you already
know what is it? It's like very a very
nice framework for instrumenting binary
in a different way.
And they are really perfect
by using together,
really, if you combine the power of the
DBI, goodbye.
And Freeda, it's something really
impressive. We will see you with a demo,
current one that basically we
have some we are a bit late
for the LRM support, but you have
seen it's basically
adding rules.
In fact, the engine itself is already
here, is already working, running on
ihram. We just need to
just finish the rules and maybe also
focus on the sixty four.
We need to improve the memory access
because currently we don't have the
same memory access, which is a bit of a
problem also.
And we also want to focus on Milty
shredding and the exception, but not in
the same way that most of engine work,
because we really want to keep the core
very simple. So it will be probably
integrating. We have something
like an Aleppo library or something and
a better side of the project, but we
don't know all exactly right now.
So the more time
so far, the more will be on the
engine itself and its python bindings.
So we just need to I
hope we will not break everything by
doing this.
Um, maybe we should just drag
and drop this.
Good.
I think it's OK.
So, uh, uh, so
it's a very simple demo, I just want
you want to show you how
easy and simple we
try to make the.
So this is a simple binary that like a
password on inputs
and the password.
Uh, well, uh, you cannot find it easily
if you just look at the software.
So that's
where I'm very glad that my
colleague use Freking
AZT keyboards, which
is.
Yeah. Yeah.
As it works.
Uh, so yeah.
So what we're going to try to do is just
display every memory accessed by
the program.
And because there's a lot of memory
access, usually we just going to focus
on. Right. So we use the Wikipedia,
the bindings and
the main thing is the disk callback
which is called Wunder, the DB
want to start and we simply do two
things.
We add the memory access callback and
we run the engine from start to stop.
Um, so the memory callback
is just right here.
And what does it just get an
analysis of the instruction and,
uh, the information about the memory
access made and simply,
uh, print. Uh, well you wrote the blah
blah blah. Uh
oh yeah. Great.
So, uh
uh.
Listen.
No, no, no, it's a reality.
Is there a password somewhere?
No, but we want
to run it maybe.
Yeah, yeah.
OK, I want to run it.
It's not in the story because it was too
easy.
So basically, all will this
be of.
OK, so there
we go. Yeah, thank you.
Uh, so, yeah, we can run
it and you can see there is a lot of
memory access that are made by this.
This thing, so
right now, it's not really readable,
there's a lot of memory point of stuff,
so maybe because we know it's a password
we want to filter about by memory
access that our one one
byte long. So there we do that.
We check if the size is one.
We only printed the size sizes one.
Um, so let's
run that, that's all.
So here it's more interesting.
Um, uh, we can see
we have bad value on the left
and we have a lot of instruction that
print that and there is a bit of
saunders' and maybe
also when you see so you usually think
Krypto or we are the
image manipulation algorithms.
Um, so
what we can do is now, uh, simply
do the same stuff. But we also filter
folks or instruction to see what we
get.
Um.
Yeah, and so it's
so now there's not that many,
uh, so, uh, instruction
left and why it looks like
random at the beginning, the end
is interesting because always in the same
kind of memory range, uh, that
looks like ASCII.
So, uh, the last thing
the last idea is simply to
aggregate those bytes into a buffer.
So you see that you have a data reader
is passed as a parameter to the callback
and so on.
Each of those instructions would simply
append the data to our array
and will fringier at
the end.
And if we do that.
We can see we have some
garbage and then at the end, some
text that looks like import Tritan.
So
if we do to
improve it.
Well, it's the correct password, so it
was simply decrypts of memory and we can
look at it at the end.
So this show you how
simple this kind of binding can be.
And that's really not hard to use a DBI.
OK, another we have time for another
Dimo, so but I will
just maybe, I don't know,
um, yeah, OK.
I will try to do it on the remote screen,
but at least you don't have
to use a nasty keyboard.
Yeah, I'm French,
so yeah, we
have a demo binary which will launch
it.
It's small but we increase
the size and we can see OK, it's done.
Nothing like it takes.
Oh I have an input screen which is Illo
and they do things with Illo and
OK. And the idea is ok, we want
to reverse engineering it
and to understand what it does.
So
we will do this using the
radar framework and.
OK, so it's
a reboot in my laptop in fact.
OK, so first we will load Freida.
Dismal failure, then
we will applaud the binary.
Is located, I think, our
Lokoja binary, OK, freed
up from Iraq and we'll separate,
OK, I want to load the demo binary.
But we are launching Frieder, I don't see
it from there, so,
OK, I take control.
OK, so here we are in Frieder.
It's the environment most
of you already know if you have your
freedom. And what we can do with
freedom basically is things
like, OK, I want I have been
reversed a bit the binary and I have
seen that there is a function which is
called like sekret.
So I would just do
a call if it's
only through the API, which is OK, I
want the address of secret, so
I just return the address of secret
and I say, OK, we need an input for
this function and basically
I will
use a string for this input.
So Freude allow us to basically
do a remote location of memory and inject
the string inside the remote process
memory.
And using this you
can say, OK, I want Nattie function.
And this is like a JavaScript function,
which allows you to do the call of
the function address that you
resolve and by doing.
So there is a completion
if you do some input, basically
Fris executing your remote function
with the input that 040
like elsewhere. OK, so
what we can do with a Adibi
and the integration that we can simply
create a virtual c.p.u.
So we will allocate our DBI.
So by doing this simply.
All right, OK, so we have
the object with them, we will create
a state thing
like the competition very easy,
and then we will try to strike using
Esterina competition because we don't
have lots of time.
And every we go, we have a basically a
virtual CPU, which is
we have initialize each state and we have
a virtual stack for it.
And what we need to do now is to say,
OK, we want to instrument
the demo binary.
There it's to avoid the call to
the external library is this is the
future of the four hour debates that
we can really choose, what part of the
code we want to instrument and really let
everything's executed by themselves?
So we are we are ready.
You can just do a call coding.
So you using the Nattie
function pointer of freedom and then
the list of input,
a list of arguments and which is input.
And you say, OK, we have exactly the same
output and the same return
value and you say, OK, but you
have exactly the same thing.
So your DB is kind of less.
Now we are adding right
now we can do it's adding instrumentation
to it because here we have just executed
the original code, but inside
the binary device
and to creating the
to add the instrumentation, you
just need to use the industry
and create something like
an instruction callback.
OK,
so what I have done basically
is I have created a JavaScript
function that will be called at runtime
for every instruction and that's
called back with basically IR just
dump the general-purpose registers
and also ask for total
analysis of the instruction,
another to the Assembly of the Assembly
three and the original address.
And this
analysis, by the way, they are managed by
the Devi and they are
there is cache, there is a lot of design,
but there is simply we don't
know what happened.
But it's magic.
You have like the analysis of the
instruction and after this,
you only need to add
your callback so
that I cheat again.
So what I ask here is that
the code callback for
before every instruction and I
will call my JavaScript function.
So now the callback is added
and we can just go back
and call again our
input function.
And here we go, what we have is
our JavaScript function, which has been
executed, and we see for every
instruction we have the address, we are
the instruction, we are the full
context of the general purpose registers
and lots of them.
We can see also that in between we are
the call for the
external library.
Yeah, we are jumping outside the DBI
and it's basically the standard library
of the Lipsy, which is doing stuff
and the arm or not surprising,
we have a written section and it's
written in zero. So everything works as
before.
So Goodbye
is an open source project
which have just released it a few days
ago, so
don't hesitate to to give it a try.
It's also been released under
a permissive license.
So feel free for any suggestions
or requests or anything.
We have a channel on Freenode.
Just join us if you're interested
in this project.
And I would like to end
by really
giving a big thanks to our colleague
across Labor and for all
the beta test and for supporting us every
day and
also especially to Paul and Joe, because
I've done like major contribution to this
really, and it will not be the same
without them. And also big funds for
Kwok's lab, our company, to allow us
to release this software,
like with a massive license
and allow it to grow by his own.
So thank you very much.
So if you yeah,
if you want to ask a question, it's like
a speed dating and we only have one
minute, 10 seconds left.
I'll try, hopefully a quick answer,
can you contrast us with
deterrence and its use of land support?
Uh, deterrence is really
cool.
It's, uh, but
it's not really cross platform and not
cross architectures from the main point
to be very, very fast because we don't
have time.
The main point is like air.
You really have it's all about the
granularity and you have
the granularity is instructions.
And more than that, you can be before
or after every instruction.
And this only I can
provide you or if you have
like tons of time.
But yeah, this is really the main
difference. It's complementary tools,
basically.
I'm sorry, but you
can get them later and
the next hour would be 15 minutes.
It's growing up software development.
Thanks. Thank you very much.
Thank you.