Hello everyone, I think I'm the final presentation for this room for today, for ever, no.
Nice to see a lot of you around, forensics is quite a niche, so it's kind of fun to see
a lot of people, also a lot of known faces.
This presentation is called your function signature here, please.
But first about me, boy, I'm Jeffrey Rung, I'm the lead scientist of the Explotation
Team at the NFI, which is a team that gains initial access to devices for forensic research
or other types of investigations.
I've worked there for more than 10 years, I think, 12 now, and my main interests are a low
level intersection between hard and software, like where you're going to write raw registers
and try to de-imagines what I really like.
I was also here two years ago, but then this t-shirt fit me a lot better, a bit of a shame.
So the problem we have is we do a lot of reverse engineering, software reverse engineering,
and software reverse engineering in the most basic form is two things.
You annotate functions, variables, structures, and you form hypotheses about the working
of these functions based on those annotations.
You're essentially labeling stuff and then thinking about what it might do, and as you
label more, you understand more.
Most of the knowledge that you put in there as a researcher is the annotations.
So there's some common problems when we do reversing simple, but very frequently occurring
functions.
I don't think I've ever seen an implementation of anything that didn't have MEMS at
and MEMCOPY, so you will see it a lot, but it will differ in a lot of forms.
Then you have some complex functions, and this doesn't look very fun to reverse, and
it is actually not, but what you will see is that the core logic often looks like this,
especially on firmware or on bad devices, you have these huge switch cases where you're
trying to figure out a protocol that you don't really know based on the code, and then
when you start to gain some insight into it, it goes faster.
But you don't want to do this too often, and if you did this once, you don't want to do
it again the next week.
There's some common problems across implementations of functions, like I said, the MEMCOPY
MEMS at you will see them everywhere, but the assembly that there's generated from your
compiler, of course, it can differ with the same source files, and that is depending on
the compiler options, but also on the way compilers are implemented.
And there's a slightly different implementations of the same algorithms for all kinds
of stuff and data structures, and there's no real easy way, at least, what I think is
an easy way, to store and share this reversing knowledge and work, because again, looking
at this, I don't want to do this two times in a month if I can help it.
So there are some possible solutions.
For example, we use Gidra a lot, and you have B, which is, it can make sort of signatures
of functions that you can then compare and say, oh, does this look like this?
But the problem is it's dependent on the Gidra decompiler.
I don't know if any of you have looked at the Gidra decompiler source, but it's not very
pretty, and it's subject to change every time.
So if you start to build databases based upon these kinds of signatures, then they sort
of don't work anymore over time, because the decompiler evolves.
Now we have fuzzy hashing, which there's some transmigropapers on this.
It works, but you don't really use the context of the function, so for more complicated
stuff, it gets less and less efficient.
And of course, you have Lumina, but that's either dependent, if you're not a problem
in itself, but I prefer something that would be cross-stools.
So start to look at entities with, we have a department that does a big data and neural networks.
And we found some papers that looked very interesting.
One of them is a J-trans, it's a way of training a model based on this assembly that makes
it easy to match functions and basic blocks within functions.
So I don't understand a lot of this, as I'm not a neural networking and data science
kind of guy, but what you do is a function always exists out of multiple basic blocks.
So for example, you run some code, then based on a condition that jumps to a different
piece of the function or not, and these things where it jumps, when you snip them apart,
they're called basic blocks.
And these basic blocks, they have pointers and code paths to each other.
The token embedding stuff, you will have to read the paper, because I cannot tell you how
exactly they encoded this into the tokenizer for the model.
But it is quite nicely done where they take basic block, they strip away all the constants,
and they keep the jumps between basic blocks and the assembly.
So the model is aware of what a function looks like based on how it runs.
That is very handy, because if you throw enough functions at it to train the model,
you're actually training it to look at the structure of the functions, which makes it quite
powerful, so we wanted to try this.
But there is one little problem, JTrans is trained for x86, and we don't do a lot of x86,
we do some, but not a lot, most of our stuff is armed.
So we thought, oh, maybe I can go to these neural network guys at our lab, and just say,
oh, can we retrain this for rm64?
And they said, yeah, sure, turns out it's not that easy, because there was code for pre-training
and everything, code quality is not that great, so at the redrawed lot of stuff, but then
the end they ended up doing it.
So what you then need, so they told me like, if you want to do this, you need a lot of functions
in binary form with the source available, and the way you train a model to say, oh, this function
is this function, but they're not exactly the same, you also need, like, a way to change a function,
and that's quite easy, because you just compile it with different optimization levels, which
was in the original paper as well, and then you take those functions out using Gidra and
compare it into each other and you train on that.
To do that, I took the rs user repo, and just compiled everything, I could get my hands on,
and it took a long time, it took a lot of CPU power as well, but I ended up with about six
thousand binaries and about 2.1 million functions to use for training, and I don't know if we're
going to hear it, but I went on vacation, and the people in my team did not, and I thought,
oh, I'll just leave it run while I'm away for three weeks, and I don't know if I have sound,
I don't think so, no, maybe, no, just like the sound, yeah, no, it sounds like a damn jet engine,
we have these dual computers, and normally they're pretty quiet, but when I get to a warm,
they're just getting to full on panic mode, and I got a text, it said, if you don't turn this shit right off,
no, we're going to burn your computer, so at the end, I ended up training it on my cluster,
it's my beautiful setup that doesn't work, and I was doing this like, it's 24, so
they were not very happy about this, I cannot imagine why, so in the end, I ended up giving
making a dataset, they ended up training them all, but then I thought, okay, how are we going to
actually use this, because you want it to be usable, so then it cannot just run on my machine,
it has to be somewhat usable, so I thought I could make a lot of slides about this, but it's
better to do a live, though, hopefully that will go better than the video, so I think it's easiest
to do, yeah, I can clip it on, this clip is to advance for me, I don't understand any of this,
no, but, it's magnetic, and that works, so, I'm going to get this here,
so here we have Gidra, there with me, while I try to change some weird as display settings,
yes, that works, okay, so here we have Gidra, while you don't have Gidra apparently,
yes I'm gonna take it, I think it's not, but so,
there is more effect and full swing,
Yeah, it looks good.
The way I'm looking at it, it looks good.
Perfect.
There we go.
I'm going to be there for a bit.
We're going to do it like this.
So I wanted to show you the server, but it's not so interesting.
It's just a hypercorn server, which is fast API.
For people who think you're better than everyone.
Let's see.
I broke it.
Not again.
Yeah, it works.
I will find a thingy.
So I run a server in the background.
Just a fast API thing that has now a SQLite database.
You used to be...
You used to be postgres.
And it's going to be postgres again.
It's quite a lot faster, I have to say.
And that just runs the model with an HTTP front end.
So what I've done, maybe I can show you that.
What I've done is I've compiled an entrusted firmware, which is...
I would say the reference implementation for a lot of secure monitor on arm devices.
So the thing that monitors when you switch between press on and back.
And your computer, which is a very interesting surface to reverse engineer.
And I've compiled everything in multiple optimization levels.
So, for example, if we look at O2,
the UI scale is not normally like this after so.
We click this away.
And you should be able to see this.
The UI scale is a bit...
But we'll get through it together, I swear.
Now, if I take the function window...
We just select a function.
And what I usually do when reversing, if I don't know anything yet,
I just sort by reference count and start reversing the most reference functions.
Because I usually get your log functions in your main copy and your main comparison.
So, we take the log function.
And you can see that this is the plugin for a model.
It matches 1 to 1, which is logical, because I've made the signatures based on the O2 version.
We'll do it once more just to show it at the works.
We'll take a bit of a bigger function.
And then again, it's 1.
So, these are the database that this runs against.
So, when you click on a function in Gidra, it just sends the whole basic log structure as Jason to the server.
That makes it an abatting, compares it to the database and sends it back.
These look the most like it.
Now, O2 is not that interesting, of course, because it will match perfectly.
Let's take O0, for example, no optimization.
Then we do the same thing.
Take the function window.
No, no, thank it.
Love me some Python too.
And we do the same thing with take TF log.
Now, this is not the same function, you can imagine.
Because O2 automizes a lot.
So, the assembly will look fully different, which we will never be able to see on this resolution.
But that's fine.
And if we go to the functions of O2, we say TF log.
Oh, shit, sorry, here.
So, O0, it will match by quite a good amount.
So, the problem with this is that the absolute numbers they don't really mean a lot.
It just, what you want is that the first entry here discriminates very well against the next entry.
So, then you know like, oh, it's quite certain that at least this looks like TF log.
Or it has TF log in line, something like that.
And we can do that for a couple of functions.
So, if we take another one, console flush again.
Now, we see also this goes very well.
This is quite easy, because we here have a console flush.
So, we already have the symbols for this thing.
But you can imagine if you're reversing.
It would be quite handy if this thing gives you some decompilation, some disassembly.
And you don't know what you're looking at.
It would be quite nice to have that function there.
Now, by the power of having already done this.
I took a strip to one, and this is OS.
So, it optimizes, but in a very different way.
It will not unroll loops or anything like O3 will.
And here, if we do the same thing, you can see these functions they don't have any names.
So, I don't also know what they are now.
But there you can see, ah, this is a CM set of context.
Where is the OS?
Yeah, yeah. This is CM set of context, for example.
It takes some different ones.
Now, you will also see it.
They're just named FUNK.
Take a different big function.
So, it's a really ergonomic setup, put them on.
Let's see.
Woo.
And still, it will say, ah, that is probably console flush.
So, you can see that if you're reversing, this would be kind of nice to have.
And the nice thing is, it works that way around as well.
When you name a function.
So, when, for example, I name the FUNK bla bla console flush.
Or I double click on this one, which makes it auto name it.
Then it will also send it to the server.
And say, ah, okay, now you've named that.
So, if we're reversing a bunch of us, and there's five of us in one room.
If we're reversing an I name something, then immediately for the next person,
if they click that function, even if it's in a different bootloader or
different secure monitor or anything, it will say, ah, it's probably that.
It's exactly what I was looking for.
Because this model will not change for now.
And even if it changes, you can,
they really make the signatures, because it also sends the basic blocks to the server.
And you can keep on using this.
The assembly will not change and build a database.
It gets bigger and bigger and bigger over time.
And in the end, you're never going to manually name MemCopy MemCompare again.
That scusi switch statement you saw.
That, well, you will also never have to do that by hand again,
because I've already done it once.
So, why do it again?
So, that's what I wanted to show you.
And all of this, like,
got to fix the world's most junky setup.
All of this work is open source.
No.
Let's see.
One sec.
All of this work is open source.
You can find it on our GitHub, the model is on hogging phase.
So, you can use it yourself if you want to as well.
And you can set up your own server, the documentation is all there.
So, I have a QR code somewhere.
But I don't know where anymore.
Ah, one sec.
So, it exists from free parts.
You have us in transformers, which is the model.
It's on hogging phase.
I would also share this presentation with you all so you can have the links.
This is the model.
The training set, unfortunately, I wanted to share it.
But all these things are source.
If you compile them, you're distributing binaries.
And it's quite hard to get through all the licenses and be sure that that's allowed,
or what you have to deliver with it.
So, our legal department doesn't want it.
It might slip on to the internet somewhere.
So, this is just a model.
You can run it standalone.
The partner appers everything are there.
This is the server that we're running against.
It's just a fast API implementation.
It's actively developed.
It gets out as a little bit silent on the repo.
But in May, we are continuing this work again.
With a new cross-instruction set model.
Because first we did a lot of arm 64.
Now we're seeing some risk fee.
I want to do VXIR as well.
Like an intermediate representation.
You can run this server on your own.
And in the setup that I just showed you.
And you have Centencia, which is for now.
Just a Gidra plugin.
The one that I just showed you that you point to the server.
And if you click in a function, it will request what functions it knows.
And if you label a function, it will put it in the database.
I am very much planning on also making an item plugin.
Because for us, some people also use it either.
So we also want to be able to use it.
And that would mean you have a sort of a lumina-like thing
across Gidra, Idra and everything.
So please use it.
And please contribute.
That was it.
Sorry for the janky demo.
Are there any questions?
Excuse me.
In essence, how well does this method generalize?
If the source code is different, for example, rust?
That's a bit of the problem.
If you have a project that's compiled in rust.
And it's different from the same project that was originally seen.
You will not see any generalizations between those two things.
If you were a first or rust project, you're versing the next version of a rust project.
It will still work fine.
Because it's so low level that it only looks at the basic blocks of a function.
And it doesn't really matter if there are v tables or anything.
Because they're in the older version as well.
But can you still relate to assembly?
Because if you use a rust or see,
they are semi-insured with at least the same, right?
Yes.
Yes, so the question is, even if you use rust or see,
you can still use the assembly to use the model.
Yes, yes.
The rust just compiles to assembly.
And it's not in assembly level.
It's not that different.
It is very different with calling conventions and v tables.
And this debug, like stuff and runtime, stuff that it has.
But on the things that the model sees,
it's just jumps between basic blocks and certain minimonics.
And it's almost the same question about sequence blocks.
I mean, doing the rust and change because less from the amateur,
this regard is pretty hard because it's the manual.
And the overloading, et cetera.
So what is learning?
Yeah, it is.
It is also hard to reverse.
Sorry.
If I understand correctly, it's a seatless plus is quite hard to reverse.
Will the model still work correctly and the implementation?
Yeah.
In essence, the reversing is a lot harder.
And that's going to stay that way.
But the model will not see that.
The model will just see the basic blocks, even if it's seatless plus.
And I will see it calling v tables.
But to the model is just the referencing of v table and offset inside it.
So true, but on an assembly level, that doesn't really matter to the model.
So you will have to know it as the reverser.
But if you say this thing is the template for this thing, then to the model,
it just says template for this function.
One of the things I also use this for, maybe I forgot too much.
It's just compile a project from source in different levels.
Throw them in the database and then you start reversing something.
And then it says, oh, I already know these things.
You can do the same thing with C++ or Rust.
Just compile it.
Throw it in Guidro.
Throw it against the database.
And then if you start reversing, you see what the symbols in the assembly, what I've said,
if it had debug symbols.
So reversing is still quite hard.
For C++.
Not yet. Not yet. What I really want is for now.
Sorry, how do you deal with things like v tables and structured definitions?
At this point, we don't.
So now it's only function names.
And what I really want is function parameters.
Structs.
And that's going to be a lot harder because if you do an item plugin,
and it should still be cross platform.
You have to think about how you're going to make that portable,
which of course could be had or false or anything.
Yeah, yeah.
As we go to start problem, for example, Guidro has a collaborative server.
And there you can also collaborate over symbols.
So you can say, oh, I have the symbols for this winery.
My made some structs. You can also add it to them.
And they're essentially something like get.
But I've not done anything to implement that here.
That would definitely be on the wish list.
The nice thing is if you compile the art user repo,
everybody gets to make their own package files.
So he gets a lot of different components.
He also gets rust in there and some different languages.
Because it's like the whole landscape comes back
and to what you've been put into the model.
Sorry.
I've also looked at different compilers.
Yes, but we have.
But because of the way the data set is built.
Yes.
Can you tell your model on your application with for the next?
Yes.
Do you want to write to reverse some of the Android packages?
Yes.
Native libraries, you mean our application?
No, no, because then Guidro can do some of that stuff.
But then you're looking at the Java native byte code.
And at this point, it only supports ARM64.
So you can do native libraries for Android,
but not I will repeat the question.
The question was if we've also looked at Android applications
and stuff like that.
But at this point, it doesn't support it yet.
I have a lot of question.
Because when you show it in the demo,
look at the function names.
We only need the first one.
The green one was like good match.
But the other ones.
Do you need different functions?
Yes.
So you only look at the green one?
Well, you need it.
Sorry.
So the only the top one is like a green.
And it says, oh, this first function is the perfect one.
And that's only the case in most of the functions.
Some still don't discriminate that well.
But it's also because this data set is very synthetic.
But in essence, it doesn't really matter if the function is green.
It mostly matters how much it differs from the other ones.
If the model says, they're all look about the same to me.
Usually, it's not an indication that there's nothing good in there.
So you have any specific sort of false boxes?
Yes.
Well, suppose it is that we don't see a lot.
Only, of course, the model will always give you 25 results.
Now, the server.
So even if it says, oh, it doesn't look like anything to me.
You will still get the top 25.
So if you're now for a different binary,
it's going to say, all these results are shit.
But.
All right.
Thank you.
Yes.
Yes?
All right.
Thank you.