Again, Craig Smith.
What I have is a talk here on creating virtual machines.
These are the type that are going to be embedded into an executable address or program.
So we're going to make our own virtual CPU and our own programming language, our own
compiler.
And at the end of the talk, I'm giving out all the source code for all this stuff so
you can play with it as much as you want.
It should be pretty cool.
I think the talk, if I don't talk too fast, will last about 45 minutes.
There's plenty of time for questions.
If you want to ask them in the middle or at the end, it doesn't really matter.
So again, we're not really talking about virtual machines where the same as VMware or a virtual
PC or Parallels or anything that's going to emulate an entire operating system.
This term is used a little bit loosely.
The method I'm talking about is just we're going to have a processor.
Those particular processors very much so based on the Intel architecture.
So if you're familiar with XA6 assembler, it should be too hard to pick up this VM.
There's another type of VM as well where you may take in Intel code or any kind of code
for that matter, any kind of op codes, convert to a virtual machine op code and then go back
to your regular Intel code.
You overwrite, you basically steal the bytes and you put in virtual machine code and have
your virtual machine decrypted.
It's an anti-debugging method.
I'm not really going into that either.
This is just an embedded process.
Okay.
So, again, we're going to have our own CPU language and our compiler.
The output of our compiler, if you've done writing your code and you've made it for your
virtual machine, we call it the p code.
So if you hear me reference that, that's what I'm referring to.
Why do people make these?
Pretty much just for code obfuscation purposes.
You can hide functionality, things can jump in strange patterns.
It's a good place to put your intellectual property if it's like, especially if it's
math based, such as serial number generations, things of that nature.
It adds a lot to reversing time.
You have to locate the core CPU and then figure out what the different op codes are doing.
If you do it right and you take the time to write language and stuff, as a developer,
you can actually write code pretty quickly and add a lot of time on the reverser's perspective.
And again, there's some anti-dumping methods you can use with this as well.
Typically, that's a game of stealing bytes, but you could, in theory, have multiple processes
in the core CPU running in one and the p code in the other, so you have to watch which ones
you dump.
Okay.
So the cool thing is, since you're writing your own processor, it's built into whatever
you want.
It's just for your own tricks.
So if you want to have one where you're going to utilize a lot of encryption and stuff, you
could have operands that just do cool encryption type functions.
You can have hitting debugging tricks.
There's levels of obfuscation that are just very kind of strange, unique, and we'll go
into some of those that can make it really hard for the reverser.
You can also have self-modifying code.
This is very similar to a regular Intel.
When you're running through the regular debugging, they can modify their own code.
You can do the exact same thing in the virtual world.
So your VM code can also modify itself.
So somebody does take the time to write a debugger, or not a debugger, but something
that can decompile your code.
If it's a VM self-modifying, they're going to have to really analyze it because it can
actually change what you see there.
Again, you can hide library and system calls and junk code.
You can have, just like you had in an Intel, putting in a bunch of operands and stuff that
do nothing.
You can have the same thing with virtual machines.
This particular virtual machine, which I call the Mini-VM, it has a special method of dealing
with junk code that's very easy and makes it trivial for you to add as much junk code
as you want.
So people use this.
It's pretty much so IC mainly used in digital rights management right now.
It's like the media VM protects, obviously, huge with virtual machines.
Blue Ray even has some VM capabilities.
As a security researcher, I really became aware of this with the Honeynet project.
They had a Scan of the Month number 32, and they showed, and there were a slew of other
anti-debugging tricks.
They had a small virtual machine in there as well.
That was the first time I saw one.
I was like, wow, this is kind of fun.
I want one.
So I immediately started Googling, where do I get these embedded processors?
Yeah, they didn't release any.
So I was like, all right.
And it's back in 2004.
So I wrote my own back then, and I've learned a lot since then.
So basically all the odds are state machines.
So your P code is just a series of bytes that are flooding into your program.
Your program's going to take them and pass these series of bytes to a call handling routine.
The handling routine, as you know, could be something complicated or just a giant switch
table.
We're going to go with the latter, the giant switch table.
It's just a lot easier to kind of understand.
It's not the cleanest of code, but I think it works really well for demonstration purposes.
So like in this example, maybe as we're reading through the P code, we grab the byte A1, and
we pass it to our call handler routine.
Call handler routine has a series of different ways of handling different bytes.
It looks at A1 and says, oh, A1 represents pushing a value into our virtual register
1, R1.
So what's most likely would happen in this stream of byte codes here, the P code, is
my guess if it's R1, it's a 32-bit field.
It's going to read in the next four bytes, which is probably going to be the value 255.
What's pushed into R1.
But that's just a guess.
And based on this lookup table here, this is all just theory.
But your registers can code anything you want.
For the purpose of our talk and the virtual machine I'm releasing, the registers are just
simply numbered, R1, 2, 3, et cetera.
So what we're going to do with this is we're going to make a crack-me.
Crack-me for those of you who don't know, it's just a program whose sole purpose in
life is to be cracked.
It's just a problem to be solved.
These are usually serial number algorithm kind of things, and the goal was to get a
key generator or something of that nature.
So our design goal is for this.
We're going to make an application that prompts the user for a password.
I'm going to take the password and I'm going to pass it to our virtual machine.
The virtual machine is going to do some math on it, generate basically a hash, return to
the parent program as a key.
It's going to use that key to jump to the good boy message.
Throwing an extra bonus nugget here and saying that if you put in the wrong password, I'm
going to nullify it out and you're going to just jump to the bad boy message.
There's no crashes, no nothing.
This is an ODA or something you found that was protected, where they would probably end
up doing simply just doing the math and you're going to jump to whatever address you just
did math for, which is just going to cause a crash.
Extra code had to be added to make it a little more easy to debug, but it's really not that
hard.
So for our design goals, you have to ask yourself, what do you want to use your virtual machine
to do?
What's the purpose of this processor?
The one I wrote, the MiniVM that I'm releasing, it's just a general purpose one.
It's for instruction purposes, so there's no real super fancy tricks or anything in
it.
I'm used to using a virtual machine.
You have to figure out how many registers you want to use.
I have chosen nine, which is a lot for a virtual machine.
How do you want to control the program flow?
I'm using something just like you would in Intel, an extended instruction pointer.
So I just go through my p code with a pointer that runs through it.
You could, if you want to, have a structure for p code.
You can make it so you read in a byte for your op codes and read in the next byte to
figure out where your jump address is going to be next.
You can, it's your processor, you can handle it any way you want.
It's basically a state machine that can be controlled in any direction.
If you're going to deal with memory, how do you want to do it?
I have essentially something that works just like a stack, so therefore I want a stack
pointer.
So that's going to be pretty standard.
You could pre-locate memory or handle that any way.
If your virtual machine is small enough, what a lot of people do is they actually write
macros instead of a language.
So all that is is if you want to insert these certain bytes, you just define a macro and
call the macro where you want to insert those bytes.
We're going to go the custom language route.
Again, this is for reusing your virtual machine more.
It makes it easier to test and play with.
And of course you have to figure out what you want to do your compiler in.
For this instance I picked Ruby.
So as far as our four general purpose registers go, we have one through four there that you
can use for anything, although register ones use the most.
Basically I got lazy when I did some of these operations, and so there's some odd ones here
and there.
It really only works in R1, but I state that.
There's ways of dealing with that, but you can use them for whatever you want.
There is an IP and a base IP.
Base IP is a little bit different.
All that really is is points to the beginning of your P code, and that's used for relative
jumps.
So if you want to jump to a 24, it's going to actually inside the core take base IP plus
24.
This way you can embed this wherever you want into a program, and it doesn't really matter.
Stack registers work the same way.
There's a base stack pointer as well as a regular stack pointer.
Base just points to the top.
And flags.
Flags is actually very much a general purpose register.
The only difference is that if you run the compare operation it sets some flags.
But if you want to, you can store values, push and pop to it, whatever.
So our current virtual machine supports these instructions.
Whenever you see an R32, that means all your registers.
That includes your IP, your stack pointer, your flags.
If you see it say just R1, because it only works in the R1 register.
So basically we have moving.
We can move everything around.
We can move things into where R1 is pointing.
We can also move out of wherever R1 is pointing into R1.
We can move any set value.
This can be a label or some hexadecimal value or decimal.
It will convert either one.
Comparisons work.
Increment, decrement.
Also I have add and subtract, which is on this list.
And in or only work on R1 and to set value.
That was just kind of added later.
I didn't feel like coding a whole lot.
X was in there, push, pop.
Now for our jumps, we have a relative and a direct jump.
The default, when you just type in jump and a label, it's a relative jump.
It's based on that base IP.
A direct jump is a hard-coded memory address.
So if you want to jump outside of your virtual machine into kernel space or wherever the
hell you want to jump to, you can.
I haven't really played with the direct stuff.
It's coded into the CPU.
I don't know how useful it is.
I haven't really played with this particular virtual machine.
It was just kind of added.
I also have some comparison jumps.
We have jump of equal, jump of less than, and jump of greater than.
So also there's a call.
We have calls.
The call works with the actual external to the VM.
So you can call whatever R1 is pointing to or set value.
This is meant to call a thing like message box or whatever.
It's not really a call with inside your p code.
It's outside.
And then there's an exit command.
So when we initialize a virtual machine, here's regular assembly code.
And this is all it takes.
This is when you first kick it off.
What we're going to do is you pass it two arguments.
You're going to pass it your p code, and you're going to pass it some pre-located memory space
that's going to represent your stack.
So all we're going to do is we're going to pop that stuff off, initialize some of our
base registers, save some stuff off, set our flags and our stack pointer and all.
And then we're going to call underscore core.
Underscore core is the actual state machine.
That's the big switch table, the call handler.
And then when we're done with that, we just return and go back to our main program.
So there's not much to initialization.
So this is actually from the crack me.
This is how we're going to actually call the program.
So we take the password from the password attribute.
I've got the 20 bytes.
We stick it in.
The pointer's called h password.
And then we have two memory spots.
We have my stack and VM code.
VM code is a pre-compiled p code.
You don't see it on this slide, but that's the actual code that's going to execute.
What we're going to do here is we're going to take my stack and we're going to pre-populate
the stack with a pointer to password.
Then we're going to actually push the p code on and call the virtual machine.
This way the virtual machine will have its stack available and also it has a pointer
to the password that it can deal with.
That's how we're going to transfer things in and out of our virtual machine to our pattern
process.
So our state machine works like this.
We take whatever instruction pointer is pointing to, which one initialization was the base
IP, put it into the ebx register, clear out some basic registers, and then we move one
byte into al.
Then we're going to do some and operations.
This is going to basically make a major and minor code.
And then we have our switch statements.
So like if we have our byte as a C2 or in our first example an A1, it's going to split
them off so that al contains the A and dl contains the 1.
Then you go into a big jump routine and then if it's an A0 for al, then maybe that's a
push instruction.
And then it goes, okay, what kind of push instruction?
And that's when it looks at the minor opcode.
Oh, it's a 1, so therefore it's going to be a pushing of the R2 register, the R1 register,
whatever happens to be.
You don't have to make it this simple.
The reason I did this is just for instruction purposes.
So when you're dealing with another virtual machine out there in the real world, they
may not break it down this way.
It's not easier to read from a debugger perspective when you're looking at it.
And all the switch routines aren't in there, obviously, it's where the three dots are.
And then at the end of that you get basically your call to increment your next IP instruction
pointer and then you jump to it.
And this is actually where the trick is for one of our obfuscation techniques is when
I'm doing here, if I go through this entire list, although you can't see it specifically
because I removed some stuff that fell on the slide.
But when you go through all these operands and if I go through the switch statement,
I don't see one that I know, I can handle it in any way I want to.
It's my CPU.
So instead of just saying, oh, invalid operation or something, I can just increment and go
to the next one.
That means everything I don't know is a no op.
So filling up the junk code is trivial.
You can just fill in any kind of binary you want as long as it's not a valid operation,
it's a no op.
It gives people more garbage to go through and it can be anything you want.
So that's build out.
So for the compiler design, you can make this as complex as you want.
I don't see virtual machines really lasting a whole long time because once somebody's
reversed it thoroughly, it's not all that great for you.
But you can do a whole lexical analyzer and parser and all that crap.
But again, I wrote my first one using Perl and just a bunch of nasty regular expressions.
And I didn't release that one because it was really hard to read.
But now I got one in Ruby.
It's much cleaner.
And it's object-oriented.
We can extend it.
We can make op codes and stuff for your own language.
So you can expand it.
It's very portable.
You'll be able to, after this is done, spend your time working on a virtual machine, make
it any way you want, how you want your processor to be.
And you should be able to write your language using just extending the compiler here and
get a language up and running in about five, ten minutes.
So this is what our code currently looks like.
These are instructions.
Again, it was meant so that if you know x86-assembler, it'll look very familiar.
Here's a sample password one.
This is not the one we're actually using in the crack-me that I released.
But what we're doing is we're popping the value off the stack, which we pre-populate
it into our R1 register.
We take the content off of it and stick it into R1 again, and then we compare it.
In this case, we're comparing it to the ASCII values of one, two, three, four.
If it's good, if it's the same, we jump with equal to a good password, which is denoted
by a colon, very similar to anything you've seen.
And then we'll move one into R1 and push it, or we'll move zero into R1 and push it, and
we jump to exit.
The reason we put a value into R1 as opposed to just pushing a one or pushing a zero is
because my virtual machine doesn't support pushing static values right now, so it has
to be in a register.
So that's why that's there.
And then what you'd have your parent program do is just pop the stuff off the stack because
the now it's been changed, and it would check it for a zero or one to determine if it's
good or bad.
This is a, you know, you wouldn't really use this protection because that's crap, but this
just shows you how it works.
So okay, if you want to extend and write your own compiler, here's how the mini VM parser
works.
We have a VM parser, which what that does is it strips out all of the spaces and tabs
and removes the comments and then tokenizes all the things the person has written.
It basically breaks it down into your operand, up to two arguments, and, you know, it can
also determine if it's a label or a compiler directive.
And then you basically extend that.
So if that's all you want it, you just want to tokenize, extend off a VM parser.
If you want, you can extend off a mini VM parser, which will give you a couple extra
options.
Just override your defined opcodes with any kind of opcode you want yourself.
The way that works is you have your VM opcodes and you do an add.
Add takes, you know, four arguments, whatever you want the command to be, anything you want.
Then you have, you know, first argument, second argument if you want one, and then the P code
you're going to use for that.
So it's pretty simple.
So you know, in the first example, we're supporting push r1, that's going to convert to the hexadecimal
30.
You may notice also in here there is a second compare operation, is a compare r1 to a colon
value.
That's a special type of string in Ruby.
And what that will do with the compiler is it will tell it, okay, we expect, you know,
either a set value, which could be zero hex, you know, zero hex and then a number, just
a decimal number.
It will convert them for you and, or it could be a label.
That's also supported there.
That's what that represents.
It works just like that.
The compiler handles all the details.
It handles the conversions for you for decimal.
It stores it currently in LittleIndian format.
And that's pretty much it.
Oh, and it takes after you do a colon value, it puts in say D8 and then it will read in
the next four bytes and append this at the end in LittleIndian.
And yeah, that's how that works.
So again, this is how you can really quickly add a language.
Once you have your virtual machine, you have the way your state machine is going to work.
You just literally define it over here and your language has a compiler.
The default options for it are this.
It's pretty standard.
You can see it kind of right over at the slash here, but it really used it.
So this was really written as a demonstration about a year ago for NeoHopsis and we used
it for a little bit then and it's just been sitting on the shelf.
So I want to give it out and let people kind of explore with it and build upon it.
The way this pretty much works, we have a, the two options you really care about are
verbose and output.
So for verbose, what you'll actually see is you'll see where each one of the lines you
typed in will be stored into the p code, which is really useful.
So that last example, you know, you can see that the, where the comparison is will be
at offset 17.
It'll tell you that.
Not only will it tell you that, it'll also tell you exactly what bytes and p code it
converted to.
So if you want to debug your own code, very useful.
The output style is just an convenience function.
The initial one just spit out binary p code, but it also now will make a C structure or
make it available that you can include into mass them.
I chose mass them with this mainly because this crap in here is written in mass them,
but there's also limitations when they're defining DB to be a certain size and that
could be annoying.
So it does all that stuff for you.
So very easy to include and update and do things of that nature.
Okay.
So the mini VM also supports two compiler time directives.
These are really just men again as examples, supports a dot DB and a dot DBX.
These two directives allow you to embed strings directly into your p code.
Differences that DBX will let you embed an X word based string.
So what we're going to do in this particular example is, you know, we move the label pointer
to our message into R2 and then we're going to use 76 as our encryption key and then we're
going to just jump over our string and then we continue to decrypt our string.
By doing a dot DBX, the first thing you give it is whatever your X or key is going to be
again 76 and then whatever string we want to encrypt.
What we get with that is basically this.
This isn't a true compile of what we just saw, but it's pretty close.
It's good enough for example.
What you see is what's in red is the string.
So that's the X word based string.
In blue you have your key showing up.
The reason this key shows up, I need to go into a little bit.
There's one at the very end, the 4C there, that's equivalent to 76.
The reason that's there is because when you X or you see password with a null at the end,
you get your key.
So that's why you see it there.
It's one of the downfalls of using a crappy X word encryption.
The other one in the 4C, it doesn't come from using dot DBX.
It actually comes because we moved 76 into R3.
So when that got converted to PD code, it showed up.
So you also notice there's also a lot of zeros in this.
So the strings kind of pop out to me when you're looking at stuff just because they're
different.
And the reason being is because a lot of my values are four bytes.
So if I'm going to move a 2 into something, I have to null pad it.
So you see a lot of nulls.
There's ways around that, but for this particular version machine, it's easy to locate strings
even encrypted just because of the nature of how the virtual machine works.
So adding your own directives, I put three steps.
It's really two, unless you want to inject code.
So because you're just telling your compiler something, you just may want to just tell
it something and it just makes a note of it and keeps going.
In that case, you only need to.
All you have to do is do an add and give it a string.
That's it.
Once you've done my directive, your compiler now supports dot my directive.
It will now be tokenized and passed to your program.
That's usually dealt with process directive.
You get a token, which shows up in the bottom too.
We have a, it's broken as a very simple structure, token directive command and the rest of your
line.
So command is my directive and dot line is whatever came after my directive.
And then you can parse it any way you want because I just don't know what you're going
to use it for.
If you want to inject stuff into your p code, you have to tell the compiler about how big
it's going to be ahead of time and that's what the get directive size is for.
So in that particular case with the dot DB, what I'm going to do is I'm going to analyze
how big the string is and say, okay, well I'm going to use up about this much of p code
space because it needs to know that for labeling and other things.
But if you're not going to, and we have an example where we don't inject p code, you
only need to add the bottom one.
So here's a screenshot of the crack me.
This crack me is available on crackmes.de.
Right now I got published I think Monday and there's a solution out there by Andrew L.
I'll talk about how he did it in a second.
He did it without seeing the virtual machine.
But this is pretty much how it works.
The about password for it is recon08 semicolon and that will get you your good boy message.
Everything else will get you a bad boy message except there's a lot of other passwords you
can use.
That's because the serial algorithm is cheesy.
It's very easy to do.
I don't want a key generator for this.
What I want as a goal is just to find other maybe even creative passwords you can use
for it and then you'll have a much better understanding how the virtual machines work,
how they get embedded and things of that nature.
So when you're debugging, right, you're in a virtual machine, how do you go around and
debug this code because you can't use standard debuggers.
That's kind of the point.
One method is you can make one of your op codes kick off and run in three.
So your VM core could have a function that runs in three which kicks off a software break
point which works fine.
But you have to remember to remove it before you release your virtual machine.
That's one method.
I actually go with the other method which is just putting a break on the call handler
table.
If you use that with conjunction of the dash B for verbose for the compiler, it's pretty
easy to follow along in your book where you're at.
So you just step, step, step, oh there it is.
You know exactly what offset so you even know how many times to step if you really felt
like it.
Also, like I keep my registers together.
So in Aledy blog or something you can bring up a data pane, you know, following up your,
of all the registers for the virtual machine.
So you put a break point where your call handler table is, bring up a window, a data window
for your registers and since there's 32 bit they align real nicely.
And you know, put a break point there and do your F9 each time and you can see the registers
populate and you can see the op codes come through and you can get through it pretty
quick.
So it was actually used by Andrew Allen when he was cracking the crack me online.
So talk about this after that.
So you know, one thing you have to remember is that you know, you have to have your virtual
machine decrypted when you're running because it needs to be completely unencrypted unless
you want to use other methods.
So you can make a polymorphic virtual processor or you can use other methods to basically
pack your processor, you know.
In and of itself the virtual machine doesn't have a whole lot of protection that protects
the virtual processor.
That's an easy thing you can make a signature for.
You can identify this fairly easily.
If somebody were to even modify it, you should be able to trace back and figure out, you
know, okay it's actually a generation of this other one.
The goal is of course to find the call handler routine.
So you have to use traditional methods to protect it.
This is just obfuscation.
This has no security.
So you have to remember that when you're going through it.
So here's just a reiteration step.
We have IDA.
This is the P code.
I'm really not showing you this just to see that obviously you can't see it.
I really included this because every slide needs to have an IDA screenshot.
So there's mine.
I actually will take the time to get through and talk about how Andrew did his thing because
I didn't actually make a slide for it but he had a nice method of going through.
As opposed to just doing the call handler, breaking on and say, okay, well we have this
opcode.
Let's trace through here to figure out, oh is that an add command or that push command,
which I've traditionally done which is, you know, it's real detailed but takes you a long
time.
He used a method that I really liked which was he just put in his password, found the
call handler routine and for every opcode that went through, he basically just wrote
it down and then he compared it to the compiled P code to see where there was duplicates.
And where there was duplicates, he decided, well that's probably a loop.
And you know, so making a lot of assumptions, you know, he went through and was able to
analyze and get a good solution going pretty quickly without really having to take the
time to analyze the whole virtual machine which I thought was really clever and a good
way of dealing with it.
So one thing that the mini VM can do is it can mutate.
It can modify its own code while it's running.
One method of doing it and this wasn't designed for mutation.
It just happens it can do it.
So that's why it looks a little bit hokey but you basically take a label like we did
before, put an R1.
You're going to have to adjust for the relative offsets because when you do a label, it's
based on the base IP.
So you deal with that by adding the IP to it and then this code normally would be next
to where mutate is because it's really obvious here but you'd hide it somewhere else.
And so if you were just, you know, if somebody did write a decompiler for this virtual machine,
you know, they see code like this and it would look like you're moving a 6 into R1.
You do a comparative 5, you know, a 6 greater than 5 and, you know, if so, we're going to
go to fake code which really in real life never really runs because what happens is
where the move of 21 hacks into R1 is going to overwrite GD with GL.
So we're going to have a jump of less than to fake code which is why we never run and
we get to our real code.
So that's something else to keep in mind.
You can always mutate your code and you can write it one way so it changes itself halfway
through them.
Now, I've written these other one virtual machines that did different concepts and I've
played different things.
Unfortunately, compiler for those is in Perl which I refuse to release.
It's just nasty.
But I'll go ahead and talk about how they work because, you know, we can always add
it.
So there's one for, I had an XOR register called XOR key and this also use a compile
time directive.
And what you could do here was anytime you had a register operation, adding something
to register, you know, subtracting, moving things in and out of the register, it would
always run it across this XOR register first.
And by default, it was a zero so it pretty much would work as you'd expect.
But once you've added something to that register, then everything changes.
Initially that was kind of a pan-ass because I'd have really weird values in my registers.
But what I really want to do is I want to develop it in a way that it looked right and
when my end result was right, but the P code was different.
So I want my compiler to change the P code.
So what it would do, a basic move instruction, it would pick up on that and go, oh, you know,
we're going to do something funky here.
So we're going to move a 76 and XOR key.
I need to keep a little flag inside saying, okay, we're now going to be XORing, you know,
all of our P code type stuff.
So when I do a move of a one to R1, the actual byte I write isn't a one, it's a 4D.
Because what's going to happen in the back end is we're going to take 4C, we're going
to XOR 4D and we want the end result to be a one, an R1.
So you can develop so that one's going to R1, that's really what happens.
But if you look at P code, you're going to actually see the operation for move and then
4D for the value.
But that's not really what's going on.
It can't follow every instance of it.
So if I move a one into R1, then I move R1 to XOR key, my compiler can't really keep
track of that because it doesn't keep track of all the registers and what the state happens
to be at.
So that's where the compiler time directive comes in.
This is an example of a directive that doesn't inject anything into P code.
So we just tell the compiler, oh, you know, our XOR key is now a one, it's not a 76 anymore.
And then I move a one in again, and this time the P code will actually write a zero.
So then the end result is a one.
So it's a little hard to kind of grasp a little bit unless you're playing with it, but the
idea is that a developer can write code, it looks pretty much the same, he has to keep
track of XOR key, but other than that he doesn't.
And the compiler can do a lot of obfuscation on the backend.
It's really not hard to implement.
To take it a step further, I had a thing called shipping operands.
It basically works the same way as the XOR key except I had like a register called seed.
And the difference was that any of your operands that you go through, you're going to check,
you're going to run it against the seed.io.
It can be XOR adding or whatever.
For the sake of argument, let's just say it's adding this time.
So what happens is when you bring in your, we'll say C2 is a move of R1, R2, and normally
what we're going to do is we're going to add whatever the seed value is.
So if we want to execute a C2 operation, the compiler needs to write a C0 if the seed is
set to a two.
And that's so that when it's actually going through it, it'll add the number and you'll
get the end result that you want.
The cool thing about doing this is your P code can change halfway through running.
So if somebody starts to debug it and they're taking the time to go through, oh, this is
a move operation.
This is a push operation.
Well, halfway through the program, that's no longer a push operation.
Now it's a move.
You can just move around any way you want.
You can do it incrementing.
You can do it anything.
That can be, if you add those two together, that one and the XOR key, and then say you
do things that are kind of messed up, like anytime you do something into the R3 register,
it automatically increments seed or something of that nature, that can be just freaking
annoying to analyze.
So hopefully, I put everything in as a labs.neilhaps.com is a WordPress blog.
I told it to schedule releasing the source code at the time of the talk.
So hopefully everything's released right now.
So you can go out and you can get the mini VM.
You can also get the compiler in Ruby.
The crack meat is on the crack meat is at DE.
If you haven't been there, also on the blog, there's a link directly to the crack meat.
Don't look at the solution yet.
I mean, unless you just don't want to try it and you just want to see how that worked,
you can read Andrew Ellis there.
If you have any questions, I'm going to be maintaining this code.
I don't really expect you're going to use the mini VM for anything in production.
It's really just meant as an example to play with.
But I could see you using the actual virtual, or the compiler, I mean.
So if there's anything you want to add to that or whatever, I'll be maintaining an SVN
repository.
Currently, SVN repository isn't public at this time, but I'm taking all patches and
whatnot.
Just send me an email at craig.smith at neo-hapses.com.
If anybody has any questions, I can take those now.
Yeah.
So the question is that what I had done is I had to register that was a seed.
And the question is that if you were to go and make it so that every instruction run
automatically changes the seed in some way, shape, or form, right?
So either different instructions maybe change it, or for each instruction automatically
changes that we're saying?
Yeah.
Would it be possible?
And absolutely it's possible.
And you can make it work differently.
You can make it so that normally you have to move something to that register to set
your seed or add to your seed.
But in certain circumstances it automatically changes, which is more obscure.
So if I'm pushing R3 or something, then it adds to the seed value, but nothing else does.
But then maybe if I move a certain value of 24 into something, it also increments the
seed one, which would be hard to track down.
You think you got it.
Like, oh, it's whenever he's moving something in here, I'll compensate for it.
Although it's still hard to write a static decompiler for it because somehow you'd have
to stop and say, oh, I think he's changing crap.
So I'm not real sure how you'd go about doing that from an IDC perspective, writing the
scripts for IDA.
I still think even doing a real basic one about the extra obfuscation, if they change
it in the middle, you're just going to have to figure that out, stop, and redo that part
with a new seed value each time.
And they did it a lot.
And if you hide it, like we're talking about, that will suck.
That would be kind of a pain.
Is that it?
Any other questions?
Oh, sorry.
I'm sorry.
Yes.
Yeah, that'll be great.
This particular code is a demonstration code.
But if you have something you want me to try and implement, like using a certain piece,
if I have, I haven't actually read that paper.
But if I look at that and say, oh, that's actually easy to add to the compiler, I have
no problem whipping that up and we can add it.
I'm not really trying to make a DRM.
I'm really just trying to teach it.
But if somebody wants to and they actually come up with some cool code, then yeah, I'd
like to have it.
Do you have a question?
Oh.
Oh, absolutely.
Yeah, you have probably more for me.
Yeah, mutating VMs is what he was talking about.
And also, I failed to mention that even though I did this all on Massim, you can just compile
the core as an object file and include it in to see.
It doesn't really solve the polymorphic part.
You still have to make a polymorphic engine for it.
But you could embed it along with other VMs if you wanted to into any other higher level
language, which could really be embedded then.
It would be hard to find some of them.
Which would be actually kind of cool to do some research for.
You know how you have signature scanners for IDA where it finds where the cryptographic
hashes are.
Or you have your PEIDs and stuff, which say what packers are.
It would be kind of nice to have a tool that would also tell me, especially if these get
more and more popular, what kind of VM they are, what they're using.
And they could point to the call handler real quickly for me.
That would save me a ton of time.
And signatureize would be easy.
And the other thing is that there's a lot of really important things for polymorphic
engines on VMs that hasn't been more to do around security.
But actually, there are a bunch of different servers and programs for that.
So it would actually be much harder to preserve them than if they call handler a big switch.
They can call them that kind of approach once you find them.
Absolutely.
Yeah, it would be cool to see if we could take some of them.
And I haven't seen any of the Smalltalk stuff.
The small talk would be used as the call handler.
That would be interesting to look at.
I haven't actually looked at any of the Smalltalk VMs or some of the other higher level language
ones.
I think there's a talk later on reversing Python that might be kind of cool.
Yeah, that would be cool to look into as well.
Is that it?
All right, cool.
Thanks.