Sie sind auf Seite 1von 38

Shub-Nigurrath [ARTEAM]

Reversing of a Protection Scheme


based on drivers: SandBoxie
Version 1.1
Last Rev.: August 2007

Forewords
Into this Tutorial

Sometime happens to fall into an interesting protection which reveals to be

1.

The target

SandBoxie, a program that has an nice protection schema. I thought it could

2.

Understanding the protection


structure

3.

Understanding the flow of


data

4.

Using Olly and IDA together:


taking advantages from both

nicely implemented and nice to describe into a tutorial. This time is the turn of
have been useful to reverse and document in a tutorial, mostly because I used
a lot a combination of OllyDbg and IDA Debugger.
This time I preferred using IDA as much as possible to understand the code and
then OllyDbg only to verify the assumptions done. This method of investigation is
usually very common when you have to analyze malware, but also very handy,
because IDA allows saving of reversing sessions, code editing, name changing
and so on.

5.

Analyzing the driver with IDA

I need reversing instruments that could be frozen at any time (I have very few

6.

Preparation of a valid patch

and scattered spare time): I usually run the dynamic sessions with OllyDbg on a

7.

Patching the driver

IDA (which can also be closed and started again later for another session).

8.

Keygenning the program

I will end this journey doing a complete keygen of the program, showing the

9.

Conclusions

10. Further Readings/References

VMWARE virtual PC which I can freeze at anytime and the analysis sessions with

process you can use too with other programs and will include in the distribution
its sources (simple C).
As usual there are cracks and keygens too for this program around the net and
this tutorial will not create many troubles than those already created by
someone else before me.
Moreover it will then be the occasion to deeper dig the IDA functionalities in
combination with OllyDbg, I will try to be as much clear as possible, for
everyone.

Have phun,
Shub

Author: Shub-Nigurrath

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 2

Disclaimers
All code included with this tutorial is free to use and modify; we only ask that you mention where you found it. This
tutorial is also free to distribute in its current unaltered form, with all the included supplements.
All the commercial programs used within this document have been used only for the purpose of demonstrating
the theories and methods described. No distribution of patched applications has been done under any media or
host. The applications used were most of the times already been patched, and cracked versions were available
since a lot of time. ARTeam or the authors of the paper cannot be considered responsible for damages to the
companies holding rights on those programs. The scope of this tutorial as well as any other ARTeam tutorial is of
sharing knowledge and teaching how to patch applications, how to bypass protections and generally speaking
how to improve the RCE art and generally the comprehension of what happens under the hood. We are not
releasing any cracked application. We are what we know..

Verification
ARTeam.esfv can be opened in the ARTeamESFVChecker to verify all files have been released by ARTeam and
are unaltered. The ARTeamESFVChecker can be obtained in the release section of the ARTeam site:
http://releases.accessroot.com

Table of Contents
1.
2.

3.
4.

5.

6.
7.

The Target and its limitations ...................................................................................................................................... 3


1.1.
Trial mode limitations.......................................................................................................................................... 4
Understanding the protection structure ................................................................................................................... 5
2.1.
Deeply looking at CheckStatus routine............................................................................................................ 7
2.2.
Analyzing the dll SbieDll.dll ................................................................................................................................ 8
2.2.1 How the InputBuffer is crafted .................................................................................................................... 12
Looking at the driver SbieDrv.sys ............................................................................................................................. 12
3.1.
Deeper look at the DispatchRoutine.............................................................................................................. 14
The final work: patching the driver.......................................................................................................................... 18
4.1.
Patching the handler of StartProcess ............................................................................................................. 18
4.1.1 A deeper look at the HStartProcess routines ............................................................................................. 20
4.2.
Patching the handler of SetLicense ............................................................................................................... 21
4.3.
Patching the handler of GetLincense ............................................................................................................ 23
4.4.
Fixing the header of the driver: checksum of PE Header ............................................................................. 26
Keygenning the Program ......................................................................................................................................... 28
5.1.
Testing the atomicity of the SerialCalculation routine .................................................................................. 29
5.2.
Loading the modified driver into OllyDbg ..................................................................................................... 31
5.3.
Coding the keygenerator ............................................................................................................................... 33
References ................................................................................................................................................................ 38
Greetings ................................................................................................................................................................... 38

This is the tutorial number 200. We


released 200 Tutorials!! This is an
astonishing result for a team as our, 200
original tutorials published on our pages!
Hip hip hip hurray to us. Long live
ARTEAM!!
http://arteam.accessroot.com

PAGE 3

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

1. The Target and its limitations


The target is SandBoxie[1], a program that creates a sandbox for any program running on your system. A sandbox
means a protected execution environment which virtualizes the program inserting a virtual layer between the real
operative system and the application. This layer will take care of the system calls of the application and will divert
them into a safer place, a local database. The target is to have a safer execution place for application without
the load of a complete virtual machine like VMWARE. These environments are indeed really popular because
offer a safer execution environment without slowing down too much the application, like the real virtualization PC
programs (e.g. VMWARE or VirtualPC).
Technically speaking these programs divert the kernel level functions used by the system to access files, registry
and memory adding hooks that divert the execution to a local database (where the data come and go). It is the
same technique used by the rootkits (see Figure 1).

Figure 1 - SandBoxie essentials

Of course the first thing is as usual to install the application and see if and how its components are protected. You
can find in the installation directory these executables:

Control.exe
SbieDll.dll
SbieDrv.sys
Start.exe

There are other executables of course but their names identify them clearly as auxiliary programs, moreover they
do not have any graphic resource inside (no dialog, no strings etc).
For all of them the PEiD reports:

Good, it seems at all a simple target, a target for which a tutorial shouldnt be needed..or not?

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 4

1.1. Trial mode limitations


The limitations of the program in unregistered mode are essentially three:
1. 30 day limit of usage
2. Is not possible to run more than one program into different sandboxes
3. No advanced features, like automatic alarms when a program runs out of any sandbox or launching a
program always into a sandbox
Try to follow these steps to verify what I told:

A)

B)

D)
C)

E)

F)

A) Create a second sandbox called secondone and select the DefaultBox


B) Launch any program into the sandbox, like for example the calc.exe
C) The calc.exe if placed into the sandbox will have a different name with # before and after the window
titlebar.
D) The list of processes you can sandbox into a single sandbox is unlimited but try to change the sandbox ..
E) Now select the secondone and try to launch again the calc.exe but this time into this second
sandbox.
F) You get a message like above..
Other limitations are those in Figure 2, and Figure 4. Figure 3 is interesting also because tells us that the alarm that
will be triggered by the system is of type SBOX1118.

Figure 2 Registration schema and badboy message

PAGE 5

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

Figure 3 automatic warning if a program runs outside the sandbox

Figure 4 about dialog

2. Understanding the protection structure


We know enough of the program in order to start reversing it. We will start from what appears the most logical
point: start.exe.
We analyze start.exe using IDA before running it and looking at the start function we can see that the structure is
quite simple: it calls some exports of the SbieDll.dll dll and then an interesting function at 01003A35 (call
sub_1003120). This call contains a lot of interesting messages, among which there are those we saw speaking of
limitations. We can then name in IDA this function with a more meaningful name: press N over it and write
CheckStatus.
After this call the program takes two directions:
1) Call the function sub_1002580 with argument 1 (push 1).
2) Go on with the routine.
Option 1) ends calling the sub_1002580 with parameter 1 pushed on the stack:
.text:01003A58
.text:01003A5A

push
call

1
sub_1002580

If you go looking at the sub_1002580 you will see that its calling NtTerminateProcess and that the argument
pushed on stack is passed to it as exit code. IDA identified the local argument for you marking it as arg_0 and
interestingly if you highlight it with the mouse it also highlights each usage of that name in the following code.
.text:010025AD
.text:010025B0
.text:010025B1
.text:010025B3

mov
push
push
call

eax, [ebp+arg_0]
eax
0FFFFFFFFh
ds:NtTerminateProcess

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 6

The automatic analysis IDA performs on function is really handy


and allows to statically following the parameters of a function.
Note 1: One advantage of IDA is
the easiness with which you can
rename functions once you
understand what are they used
for.
This
allows
to
better
understand
the
code
and
immediately recognize a function
when
it
used.
The
same
functionality is offered by OllyDbg
but actually rarely used. Also due
to the easiness with which the UDD
files get overwritten (each time the
original exe is changed the UDD is
recreated, except if you have a
proper patch to disable this
feature).

We can then rename the function sub_1002580 into a more meaningful TerminateStart.
The function start.exe call just before the function CheckStatus call two other functions:

One is the function sub_1002FB0, which is interesting because launches the program control.exe as in Figure 5.

Figure 5 start launches control.exe

The program is passed as argument of the function SbieDll_RunFromHome(x,x,x,x) which is a function


exported by SbieDll.dll. Start.exe also creates here some mutex to save from multiple instances of the program.
IDA fortunately was able to understand that the function SbieDll_RunFromHome needs 4 parameters.

PAGE 7

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

The second call is at loc_1003A25 where the sub_10038E0 retrieves the parameters from the start.exe
command-line and launches the program given to start through its command-line, using ShellExecuteExW.
Interestingly IDA also recognize the SHELLEXECUTEINFO elements of the call, you can see the following in IDA. The
code starting at loc_1003908 stores the command-line into the buffer Dest. This buffer will be used later.

We then completely understood what start.exe does:


1)
2)
3)
4)

launches control.exe using SbieDll_RunFromHome;


creates some mutex to control multiple istances;
retrieves from the command-line the name of the program which must be sandboxed and launches it
using ShellExecuteEx, the name of this program is stored in Dest;
does some checks of expiration and validity in a function we renamed CheckStatus.

2.1. Deeply looking at CheckStatus routine


At a first sight one simple solution appears to patch the CheckStatus function or to patch the caller of this
function. I must immediately say that this option wont work, and the limitation will remain in place.
Still using IDA we have a better look at the routine CheckStatus. The function immediately calls the
SbieApi_StartProcess(x,x,x): the call is exactly like this:

SbieApi_StartProcess(Dest, var_8, var_4)


var_8 and var_4 are two local variables IDA recognized for us and are
initialized here:
.text:0100315E
.text:01003165

mov
mov

[ebp+var_8], offset hToken


[ebp+var_4], offset Token

If you follow the variables hToken and Token you will find that are
respectively crated by a call to CreateProcessAsUserW and a call to
SetThreadToken. Dest is instead the process being sandboxed.
This function returns its result in eax (as usual for the functions return values compiled from C), eax (actually ax
only) is then moved to var_414, var_414 is immediately checked against 0. If its equal to 0 the function
SbieDll_InitProcess() is called.

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 8

Otherwise var_414 is repeatedly compared versus some constants.


loc_10031E0:
cmp
[ebp+var_414], 0C0000021h
jnz
short loc_10031F3
loc_10031F3:
cmp
[ebp+var_414], 0C0000268h
jnz
short loc_100322D
loc_100322D:
cmp
[ebp+var_414], 0C0000259h
jnz
short loc_100326E
All these constants are driving the CheckStatus to expired or limited trial messages.
Of course patching at this level is useless because only changes the message to the user, the action has already
not been performed at this level: what we should do is to understand why the SbieApi_StartProcess returns a
value of ax!=0.
The constants we can identify are then (we can also rename them using IDA):

0h

no errors calls also SbieDll_InitProcess()

0C0000021h
ok, no errors, moves al=1 and no other calls. So not a good value but not a limitation
itself

0C0000268h
you have been using sandboxie for more than 30 days..

0C0000268h=UNREG_30DAYSLIMIT

0C0000259h
The unregistered version doesn't support more than one sandboxie...
0C0000259h=UNREG_MORESANDBOXIE
We must understand what the function SbieApi_StartProcess does and generally what all the functions of the dll
SbieDll.dll do.

2.2. Analyzing the dll SbieDll.dll


We understood all the things we said, using IDA and some logic. The dll SbieDll.dll is important to deeply
understand where the check of trial-vs-full is done. Just then open this dll with IDA again and let it analyze.
The function SbieApi_StartProcess is quite simple, see Figure 6 and does not allow any further investigation or
any particular guess: starting from the end of the function we can climb backward to understand where the
values are got from: the function return its result in eax. eax is modified at the following instruction:
loc_7D23B1C1:
.text:7D23B1C1

mov

eax, [ebp-5Ch]

[ebp-5Ch] is changed a little before at:


.text:7D23B177
.text:7D23B17C
.text:7D23B17F

call
mov
cmp

sub_7D23ABA0
[ebp-5Ch], eax
dword ptr [ebp-5Ch], 0

The call sub_7D23ABA0 is what we need to investigate further..

PAGE 9

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

Figure 6 - function SbieApi_StartProcess(x, x, x)

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 10

The call sub_7D23ABA0 is a simple function that initializes a driver called \\Device\\SandboxieDriverApi, if not
already initialized.
At location 7D23ABA6 theres a compare that calls NtOpenFile using the above string (actually opens the driver
SandboxieDriverApi).
.text:7D23ABA6

cmp

drvFileHandle, 0FFFFFFFFh

If drvFileHandle is already opened (!=-1) the function just calls NtDeviceIoControlFile, which is an API that
sends requests to the driver:
The NtDeviceIoControlFile service is a devicedependent interface that extends the control that
applications have over various devices within the
system. This application programming interface (API)
provides a consistent view of the input and output
data to the system while still providing the
application and the driver a device-dependent
method of specifying a communications interface
(see Figure 7).
Even if documentation tells that its a deprecated
function is still used by windows as kernel level and
might be used. What is important among the
parameters is the IoControlCode:
Code that indicates which device I/O control
function is to be executed.
The IoControlCode used is always 222007h in the SbieDll dll.

Figure 7 - Documentation of the NtDeviceIoControlFile

PAGE 11

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

We can then rename this call to a more meaningful name, I used SbieDrv_222007h, just to be able to find it
immediately.
What is needed is to understand what are the other values passed to the API. To
do this we will use our beloved OllyDbg.
Open start.exe into OllyDbg and place a breakpoint at 7D23AC40 (call
ds:NtDeviceIoControlFile) into the SbieDll.dll
What you will see is that the InputBuffer local variable is apparently the only way
the driver has to communicate back the results of the call.
0006F8E0
0006F8E4
0006F8E8
0006F8EC

0006F914
00222007
0006F970
00000040

|Arg5
|Arg6
|Arg7
|Arg8

=
=
=
=

0006F914
00222007
0006F970
00000040

IoStatusBlock
IoControCode
InputBuffer
InputBufferLength

Before reassuming the conclusions we come so far let also go at the


beginning of the function that now we called SbieDrv_222007h. Press X to
see all the references to this function and you will see that it is called in ALL
the functions of the dll we are reversing.
Summing up these the things we discovered so far:
1. We discovered that the real check of the application (which guides start.exe to a trial message or to a
normal work) is done into a driver.
2. We discovered that the driver is always called using the same service IoControlCode (22207h)
3. We discovered that apparently the return code should be into an InputBuffer.
Before moving to the next section we can understand the logic of the point 3 above. Looking at the code of
SbieDrv_222007h we can see that the value of InputBuffer comes from the caller as a parameter of the
function SbieDrv_222007h (always edx). The IoStatusBlock is always set to 0 when the call returns from the
driver then, where the result is stored?
Looking at any of the calls to SbieDrv_222007h we see that after the call the eax is always stored as following:
.text:7D23B602
.text:7D23B603
.text:7D23B608
.text:7D23B60B

push
call
mov
mov

edx
SbieDrv_222007h
[ebp-48h], eax
eax, [ebp-48h]

Looking at SbieDrv_222007h it is evident that the eax value is actually the NTSTATUSReturn
.text:7D23AC49

mov

eax, [ebp+NTSTATUSReturn]

So the function SbieDrv_222007h is a function that actually looks like this:


SbieDrv_222007h(DWORD *InputBuffer) {
extern DWORD drvFileHandle;
DWORD NTSTATUSReturn=0;
if(drvFileHandle==-1) {
UNICODE_STRING str;
RtlInitUnicodeString(&str, L"\\Device\\SandboxieDriverApi").
InitializeObjectAttributes(&oa, ...);
NtOpenFile(&drvHandle, &oa,...);
}
NTSTATUSReturn=NtDeviceIoControlFile(drvFileHandle, 0, 0, 0,
IoStatusBlock, 222007h, &InputBuffer, 40h, 0,0);
return NTStatusBlock;
}
You can decompile it easily looking at the code structure. Usually this decompilation exercise helps fixing
concepts.

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 12

Finally to conclude this trip into the dll we need to fix two left concepts:
1. We landed here looking for the code of the API SbieApi_StartProcess so we need to jump back
looking for the caller, how the parameter InputBuffer is created.
2. Do all the other APIs work like this?

2.2.1 How the InputBuffer is crafted


Using Figure 6 we can see that the parameter of SbieDrv_222007h is actually stored in edx. Edx comes from
[ebp-40h], this is a dynamic element on the stack then we must stop here wondering on IDA and go into OllyDbg
again to see which is the value given to the function.
We can place a breakpoint at the following piece of code
.text:7D23B177
call
SbieDrv_222007h
And see what OllyDbg reports on the stack:

The value of edx points to the value 12340006 which is pushed at 7D23B13F on the stack.
.text:7D23B13F

mov

dword ptr [ecx], 12340006h

This is the service we request to the driver!


So the next logical step will be to enter the driver and look how it works, to see which the correct service
dispatcher is and then patch it correctly. We will learn some basic concepts on reversing of the drivers and
patching it correctly.
We left the other question: Do all the other APIs work like this? The answer is yes, all the dll exports as similar: craft
a proper InputBuffer which is passed to the driver and then the result is forwarded to the caller (start.exe or
control.exe).

3. Looking at the driver SbieDrv.sys


Time has come to worry of the real core of the protection, where the work is performed and the registration
checked.
Open SbieDrv.sys with IDA and let it analyze. This time we cannot anymore use OllyDbg to check anything,
because drivers can only be debugged using a Ring0 debugger like SoftICE or Syser. We want anyway
concentrate on the use of IDA as static analyzing tool, so we will discover that we will not need any Ring0
debugger to fully defeat the program.
If you understood the discussion till now, you should be confident that searching for one of the constants used as
InputBuffer by the SbiApiDll should do the work: try searching for anyone of these constants (e.g. 12340006h)
and you will not find anything!
But we find something instead searching for the service descriptor we found earlier: 222007h. We find it here:
.text:0001113B cmp dword ptr [eax+0Ch], 222007h

PAGE 13

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

The constant is referenced into a function IDA identified as sub_110F0(int,PIRP Irp).


The function sub_110F0 is the real core of our driver and what allow us to find the final handlers of the events
called by the SbieDll.
We now want to analyze the function sub_110F0, starting from the end: it ends with a call to IoCompleteRequest
and returns in eax the value returned by this API 1: see Figure 8.

Figure 8 - end of function sub_110F0, call to IoCompleteRequest

The drivers dispatch of events more or less works like the


following [2].. The I/O Manager creates an IRP object
describing the I/O request and sends its pointer to the
device driver in as the pIrp parameter. It's then
responsibility of the device driver how to handle this IRP. At
the end of the function handling the request we are
supposed to find a call to IoCompleteRequest, indicating
the driver has completed IRP processing and returns it to the
I/O Manager2.
The IoCompleteRequest ends and the I/O manager can
delete the message or leave it if more processing is required
by other drivers (usually used in filter drivers). If the result is a
STATUS_SUCCESS the device is ready to accept another I/O
requests.

Note 2: Before going on with the analysis stop


to think to the calling stack we discovered so
far:
This is the calling tree:
Start.exe
Start() {
CheckStatus();
SbieDll::SbieApi_StartProcess(x,x,x);
SbieDll::SbieDrv_222007h(12340006h);
..then the driver is called.

The function sub_110F0 has several typical things:


1. ends with a call to IoCompleteRequest
2. receives as input a pointer to an IRP object
3. Is directly called by the start function of the driver in:
INIT:00023D69 call
sub_23040
INIT:00023D6E mov
bl, al
And once inside the sub_23040 it is initialized here:
INIT:000230D5 mov
dword ptr [edx+70h], offset DispatchMessage
So we finally discover that the sub_110F0 is the driver DispatchRoutine()!
A suggestion for a more general approach: usually for drivers it is better to not search for immediate constants in
driver as I did for DispatchRoutine, sometimes due to optimization in DDK, the code will be generated similar to
this:

mov [ecx+18], esi <---- pRip->IoStatus.Status (this is same code that returns from Dispatch routines)
I found an implementation of IoCompleteRequest here: http://www.reactos.org/pipermail/ros-diffs/2006June/012842.html
2

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 14

mov eax, [eax+0ch] <--- IO_STACK_LOCATION->Parameteres.DeviceIoContrl.IoontrolCode


sub eax, 2200000h
cmp eax, 1
etc.
and the constants are lost. This same approach will be found later when looking at the services codes, all starting
with 12340000h.
What you should do is in DriverEntry to locate code which performs this:
mov
mov
mov
mov
mov

esi, [ebp+4] ; pDriveObject


[esi+38], offset CreateCloseHandler
[esi+40], offset CreateCloseHanler
[esi+70], offset DispatchHandler
[esi+34], offset DriverUnload

This will bring you right to dispatch routines in the driver, all the times. For example for this specific driver this part of
code is located at loc_230AA, see Figure 9.

Figure 9 - where main functions of driver are assigned

3.1. Deeper look at the DispatchRoutine


If you follow the structure of the DispatchRoutine it is easy to see, with IDA, that theres an initialization loop I
shown in Figure 10. This loop starts from a location called unk_22000 and stores into var_24 a vector of function
pointers (the var_24 can be labeled as vptrTable_unk_2200), until the table ends with a DWORD o zeros (actually
the end is at 000220C0). If you look at the location unk_22000 you will see that its a table of functions, most
probably the services of the driver (we will verify this later), see Figure 11.
What we can do now is then to group all the nodes of this loop to shorten the
DispatchRoutine. We have to select the nodes underlined in Figure 10 holding shift
button and then, using right click, select the menu Group Nodes. We then enter
this text into the new node:
Initialize the unk_2200 table of function pointers.
jz if the table ends
This initialization is done only in case of error of the driver, so only at first launch

PAGE 15

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

Figure 10 - DispatchRoutine initialization loop

Figure 11 - early elements of location unk_22000

What we obtain is then a more simple view of the DispatchMessage() routine, see Figure 12. Figure 12 also shows
that the table unk_2200 is read into memory only the first time when the driver doesnt find it: the driver goes into
error mode calling KeBugCheckEx, otherwise the driver normally handle the request calling IoCompleteRequest.
The call to BugCheckEx is important:
When a driver goes into error mode the KeBugCheckEx
mode is the last resort the driver can take to adjust its status
and avoid wild BSODs. The KeBugCheckEx routine brings
down the system in a controlled manner when the caller
discovers an unrecoverable inconsistency that would
corrupt the system if the caller continued to run.
The function is called when the pointers contained into
unk_2200 are not loaded in memory; the driver sets a BSOD
(Blue Screen of Death) reporting some useful information.

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 16

The KeBugCheckEx has these parameters:


VOID
KeBugCheckEx(
IN ULONG BugCheckCode,
IN ULONG_PTR BugCheckParameter1,
IN ULONG_PTR BugCheckParameter2,
IN ULONG_PTR BugCheckParameter3,
IN ULONG_PTR BugCheckParameter4
);
Any parameter BugCheckParameterX supply additional information, such as the address and data where a
memory-corruption error occurred, depending on the value of BugCheckCode. The most interesting is the
parameter BugCheckParameter2 which is the base of the InputBuffer, its missing and the error message
reports it!

Figure 12 - Condensed view of DispatchMessage

We are not successfully reversed the structure of the DispatchMessage and the meaning of unk_2200 table of
functions.

PAGE 17

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

The unk_2200 table of functions is actually a table of the services offered by the driver, the functions are called
using the value InputBuffer passed as argument to the NtDeviceIoControlFile; the offset of the table is got
subtracting the value 12340000h to InputBuffer.
For example then:
1.

The
constant
12340001h is
used in
the
SbieApi_GetVersion
2. The first position of the unk_22000 table is the
function sub_11240
3. We can rename it as HGetVersion. This is the
handler of the service 12340001h

Note 3: We can now complete the calling


tree of Note 2:
Start.exe
Start() {
CheckStatus();
SbieDll::SbieApi_StartProcess(x,x,x);
SbieDll::SbieDrv_222007h(12340006h);
SbieDrv::DispatchMessage()
SbieDrv::HStartProcess()

The same can be done with all the other function pointers
starting from unk_22000. We can rename this location
pressing N over it with IDA and call it Services_Table.

Generally speaking then the formula to get the correct


handler is this one:
Handler = *Services_table + (*InputBuffer-12340000h)*8h
For example for the SbieApi_StartProcess() we have
InputBuffer=12340006h
Handler= 00022000h + (12340006h 12340000h)*8h = 00022030h sub_1E790 HStartProcess
Better looking at the Services_Table you can find that theres another interesting way to discover what we just
described:
.data:00022030
.data:00022031
.data:00022032
.data:00022033
.data:00022034

db
6
db
0
db 34h ; 4
db 12h
dd offset sub_1E790

The element at 0x00022030 is the number of the handler minus the offset 12340000h.
We now have all the elements to correctly track down the events of the program through the SbieDll APIs.
The most relevant handlers are the following ones (with the final name given in IDA):

SbieApi_StartProcess
SbieApi_SetLicense
SbieApi_SetUserName
SbieApi_GetLicense
SbieApi_QueryConf

12340005h
12340006h
12340015h
12340004h
1234000Fh

5h
6h
15h
04h
0Fh

sub_1E790
sub_1CA50
sub_132D0
sub_1C9F0
sub_13080

HStartProcess
HSetLicence
HSetUserName
HGetLicense
HQueryConf

The complete table is the following one (just the functions):


.data:00022000 Services_Table
.data:00022004
.data:0002200C
.data:00022014
.data:0002201C
.data:00022024
.data:0002202C
.data:00022034
.data:0002203C
.data:00022044
.data:0002204C
.data:00022054
.data:0002205C
.data:00022064
.data:0002206C

db
dd
dd
dd
dd
dd
dd
dd
dd
dd
dd
dd
dd
dd
dd

1
offset
offset
offset
offset
offset
offset
offset
offset
offset
offset
offset
offset
offset
offset

HGetVersion
; handle 12340001
HGetWork
HLog
HCallZero
HGetLicense
HSetLicence
HStartProcess
HQueryProcess
HQueryBoxPath
HQuerProcessPath
HQueryPathList
HEnumProcess
HDisableForceProcess
HGetInjectSaveArea

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE


.data:00022074
.data:0002207C
.data:00022084
.data:0002208C
.data:00022094
.data:0002209C
.data:000220A4
.data:000220AC
.data:000220B4

dd
dd
dd
dd
dd
dd
dd
dd
dd

offset
offset
offset
offset
offset
offset
offset
offset
offset

PAGE 18

HGetSetDeviceMap
HRenameFile
HUsedInternally_to_driver
HCreateDirOrLink
HDuplicateObject
HHookTramp
HReloadConf
HQueryConf
HSetUserName

4. The final work: patching the driver


We are now able to track the elements of the keyboard down to the driver services; we must understand how to
patch a driver. I strongly suggest to use VMWARE or any other virtual PC platform to do your trials, its faster to
reboot and safer. Theres also another reason, the driver is loaded the first time the program loads it and never
unloaded until a new reboot is done, so new patches will become active only after a reboot of the PC, virtual or
real.

4.1. Patching the handler of StartProcess


In Section 2.1 we already identified some constants we called UNREG_30DAYSLIMIT
and
UNREG_MORESANDBOXIE. Of course now we find these same constants in the function HStartprocess function of
the driver. Figure 13 reports the details of the two constants we identified, but this time in the driver SbieDrv.

PAGE 19

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

Figure 13 - HStartProcess details on constants returned

Looking at Figure 13 we can say that for the UNREG_30DAYSLIMIT we must fix the call sub_1CB40 because this
function is called several times around the code. According to code above if al=0 then UNREG_30DAYSLIMIT is
set. So sub_1CB40 must return al!=0 always.
If you look at the code of the function sub_1CB40, there's only one place where al is set to 0
.text:0001CB85
jz
short loc_1CB8F ; this one becomes a JMP
.text:0001CB80 loc_1CB80:
; CODE XREF: sub_1CB40+36j
.text:0001CB80
mov
al, [ebp+arg_0]
.text:0001CB83
test
al, al
.text:0001CB85
jz
short loc_1CB8F
.text:0001CB87
xor
al, al
.text:0001CB89
mov
esp, ebp
.text:0001CB8B
pop
ebp
.text:0001CB8C
retn
4
.text:0001CB8F ; -------------------------------------------------------------------------.text:0001CB8F
.text:0001CB8F loc_1CB8F:
; CODE XREF: sub_1CB40+45j
so the patch1 on the driver is: 0001CB85 JZ becomes a JMP

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 20

The other limitation, connected to UNREG_MORESANDBOXIE has a similar structure, but involves the function
sub_1E6D0, which must return al!=0 as well to be good for us.

This time this specific function is


HStartProcess, but we patch it also.

only

called

in

The last section of the sub_1E6D0 is:


.text:0001E74A loc_1E74A:
.text:0001E74A
mov
edx, off_223F4
.text:0001E750
push
edx
.text:0001E751
push
offset unk_227CC
.text:0001E756
call
sub_18210
.text:0001E75B
mov
al, [ebp+var_1]
.text:0001E75E
pop
esi
.text:0001E75F
mov
esp, ebp
.text:0001E761
pop
ebp
.text:0001E762
retn
.text:0001E762
sub_1E6D0
endp
the register al is set here:
.text:0001E75B

mov

al, [ebp+var_1]

But the var_1 is set to 0 here:


.text:0001E745

mov

[ebp+var_1], 0

So the Patch must be done at 0001E73B


jnz

short loc_1E745 NOP

4.1.1 A deeper look at the HStartProcess routines


The call sub_1CB40 has an argument, as IDA underlines.
proc near
var_20= word ptr -20h
CurrentTime= LARGE_INTEGER ptr -8
arg_0= byte ptr 8
This argument is called arg_0 and is a DWORD.
Inside the code of this function I can see that this argument is used to decide whenever to return a value of al=0
or not.
Particular important is this piece of code:
loc_1CB80:
mov
al, [ebp+arg_0]
test
al, al
jz
short loc_1CB8F ; JMP and the function will not return al=0 anymore
It is equivalent to the following decompiled piece of code:
loc_1CB80:
if (arg_0 == 0) goto loc_1CB8F;

PAGE 21

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

So to have this call to return a value correctly (no badboys) we have to push a value of 0 as argument.
Interestingly the call references to sub_1CB40 are these:
p sub_1E240+8
call
p sub_1E6D0+6
call
p HStartProcess+31 call

sub_1CB40
sub_1CB40
sub_1CB40

The first two gives to the function a value of 1, like for example:
.text:0001E246
.text:0001E248

push
call

1
sub_1CB40

The third one instead is inside HStartProcess, the detail of this third one is the following:

.text:0001E7C0 loc_1E7C0:
.text:0001E7C0
push
ebx
.text:0001E7C1
call
sub_1CB40
the value of ebx is pushed to the function and few lines
above it is xored in this line:
.text:0001E79A

xor

ebx, ebx

Evidently the 30 days limit is build in into the application


and to fix it the option is then to change this last xor into
a MOV EBX, 1

The third way to Patch is to patch at 0001E79A like the following:


xor ebx, ebx
space.

mov ebx, 1 or better to save space a mov bl,1 (B3 01) which fits into the available

4.2. Patching the handler of SetLicense


You surely have found that theres a menu in the program that allows inputting of a serial number and username.
Theres also a corresponding function in the SbieDll dll called SbieApi_SetLicense. If you look with OllyDbg you
will immediately find that it is called by control.exe to handle the result of the registration, see Figure 14.
The function (actually we know thats the driver which handle all) returns a constant all the times equal to:
0C000026Ah. This same constant is found in HSetLicense, as evident from Figure 15. The patch is then very simple
this time:
.text:0001CAC3 jz

short loc_1CAD3 jmp short loc_1CAD3

The result of the patch is shown in Figure 16.


Moreover the following code:
.text:0001CAB6
.text:0001CAB7
.text:0001CAB8

push
push
call

ecx
edi
ds:_wcsicmp

; wchar_t *
; wchar_t *

Looks very promising for another patch: the insertion of a call to DbgPrint3 in order to fish the real serial over a
Dbgmessage...I will leave this for you

The syntax of DbgPrint is almost the same of a printf

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 22

Figure 14 - call of Sbie_SetLicense in control.exe

Figure 15 - portion of HSetLicense in the driver that will be patched

Figure 16 - result of patched HSetLicense

The result of this modification is not only a successful registration check, but also the removal of all the other
limitations we saw at the beginning of this document, see Figure 17.

PAGE 23

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

Figure 17 - final result of patchings

4.3. Patching the handler of GetLincense


The SbieApi_GetLicense needs a little more understanding of the program flow. First of all open OllyDbg and
place a breakpoint in control.exe where the badboy message is composed, see Figure 14. I placed a breakpoint
at 0100E0B1.
The code is the following:

On the stack at location [EBP-1B4] the program control.exe pushed the


string I shown in Figure 17, the call to SbieApi_GetLicense simply after the
call to the driver if the result is different from 0, sets to 0 the first byte of the
location [EBP-1B4] invalidating the string. The code at 0100E0B6 simply
checks this location and if not zero skip the bad boy message.
In other worlds, it looks like the following pseudo-code:
dword var=0;
SbiApi_GetLicense(&var);
if(var==0)
<Not Registered>
else
<Registered>

What the function SbiApi_GetLicense does is to call the driver passing a buffer, it writes 0 at the first byte if the
program is not registered.
The SbieApi_GetLicense is reported in Figure 18. The portion of code circled is the one responsible of zeroing
out the first byte of the argument on the stack, specifically the second mov. This portion of code is executed only
when eax<0, so to have a good result we must force HGetLicence on the driver to return eax>=0.

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 24

Figure 18 - SbieApi_GetLicense

Figure 19 reports the disassembly view of HGetLicense. Into this function as you can see there are two possible
paths: the correct one, on the right, ends with a xor eax, eax which is what we needed to recognize it. So the
patch this time forces the execution of this branch:
Patch
.text:0001C9FA jnz

short loc_1CA01 jmp

short loc_1CA01

Note 4: Blacklists are usual in programs and it is not rare to find


them into a program. This one is not an exception:

PAGE 25

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

Figure 19 - HGetLicense

This last modification allows to nicely seeing our name in the about box:

Note 5: Actually patching only HGetLicense


and HSetLicense is enough to defeat checks,
but the previous discussion on HStartProcess is
useful to better understand the method and
the discussions on drivers.

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 26

4.4. Fixing the header of the driver: checksum of PE Header


Now many of you know that into the PE header file theres a checksum field that is the checksum (CRC32) of the
whole file [3], see also Figure 20.

Figure 20 - Checksum value in PE header of sys driver

The PE files headers include a CheckSum field which is located into the:
IMAGE_NT_HEADER IMAGE_OPTIONAL_HEADER CheckSum
This value is an overall checksum of the whole file, often not set and left to 0x0000 by most compilers and thus
doesn't happens often to worry about it, but sometimes this value is used to check if there have been alterations
in the executable file. There is for example an API, MapFileAndCheckSum(), which calculates the real checksum
of a PE file and reports also the value stored into the PE Header. It is then simple for simple protectors to detect
alterations of a PE file, even of a single byte.
It's a simple technique that advanced protector doesn't use too often and you can of course intercept this API
and modify it online or skip its call, but for example with PocketPC smartphones or system drivers this check is
done by the operative system, so you simply have no choice to intercept this check and the only way is to fix the
value stored in the PE file header. The system uses this value to check the integrity of a driver, before loading it. If
you patch a driver and simply load it the result is that it will not be loaded by the system because the stored
checksum and the calculated one are different.

PAGE 27

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

The MapFileAndCheckSum function computes the checksum of the specified file.


DWORD MapFileAndCheckSum(
PTSTR Filename,
PDWORD HeaderSum,
PDWORD CheckSum
);
Parameters
Filename
[in] Pointer to a null-terminated string that specifies the file name of the file for which the checksum is
to be computed.
HeaderSum
[out] Pointer to a variable that receives the original checksum from the image file, or zero if there is an
error.
CheckSum
[out] Pointer to the variable that receives the computed checksum.
You then should fix this field of the patched sys file and store the value calculated by the above API.
Luckily a few months ago I developed a Checksum Fixer program [4] which does exactly this thing: I met a
program that was checking against this value in the header to see if it was patched or not and decided to
create this tiny tool.
This program simply does this fix conveniently. Already other tools have this functionality (LordPE for example), but
I just wanted a fast program able to fix this checksum in a click (e.g. with LordPE you have to do at least 5, 6
clicks).
The usage of the program is really simple: you can launch it directly and drag&drop the patched sys file, or open
using buttons or do all automatically on the command-line. Figure 21 shows the result

Figure 21 - Checkum Fixer result on patched sys file

You can also automatically fix all using this syntax:


CheckSum.exe sbieDrv.sys -fix
Note 6: Before trying out any patch on the
driver you must fix the checksum header value
of the sys file and then restart the system to
load the modified driver.

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 28

5. Keygenning the Program


This is our last and highest levels result in our reversing journey into SandBoxie. We can keygen the program
without even touching Olly, or, better touching it just to find some evidences we will use for building the keygen.
First of all we need to find the serial generation routine: it should already be clear at this stage that it is into the
driver. Figure 15 shows the most important part of the function HSetLicense: this function is responsible of
understanding if the license code given is correct or not. Figure 15 reports the point where we patched the
function so as to accept any serial. Of course the piece of code above the patch is responsible of the serial
calculation. If you further analyze it, still using IDA, you get the result of Figure 22.

Figure 22 - Serial Calculation in HSetLicense

I properly commented all the parts of the code, it should be clear that the sub_1C320 is the routine that
calculates the serial number, which is then compared with the serial given by the user through a _wcsicmp, that is
a wide char (Unicode) comparison of strings not case sensitive. We can then rename the sub_1C320 with
SerialCalculation.
If you go into the SerialCalculation you can see that IDA
identified two arguments for the function, called arg_0 and arg_4.
These two arguments are passed through the activation record
mechanism typical of any call with the Intel platforms. Depending on
the calling convention used by the compiler/language (the
possibilities are VisualC++ and C in general or Borland and Pascal)
the arguments are pushed on the stack in different order. I know
here, thanks to PEiD, that the driver was compiled using VisualC++
then I used the C parameter pushing convention to restore the
prototype of the SerialCalculation routine. The result is:
SerialCalculation(UCHAR *name, UCHAR *serial);

The SerialCalculation function is quite


complex and we will not analyze it, what
we will do instead is to understand the
general behavior and understand how to
extract it from the driver.

Assembler

I know that the two parameters are


Unicode strings because the program
Control.exe takes them from the
DialogBox in Unicode format and with
that format passes them to the driver.

Note 7: There are two possible methods compilers use to push


arguments of a function on the stack [5]: C and Pascal calling
conventions. Given the function dummy(Arg1,Arg2,..,Argn) the
two possibilities are:

Stack

There are also two local variables which


will be used in the function, var_8 and
var_4.

C Calling Convention
push Argn
..
push Arg2
push Arg1
call dummy

Pascal Calling Convention


push Arg1
push Arg2
..
push Argn
call dummy

Arg1
Arg2

Argn

Argn
...
Arg2
Arg1

The parameters will be sorted on the stack or the code.

PAGE 29

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

The most interesting part of the function is shown in Figure 23, where we understand that the serial is actually
calculated using a string of valid characters (off_223F0), that it is stored in the variable arg_4 and that it is long 0Eh
characters (an Unicode string of 6 characters, that is what we experience using Sandboxie).

Figure 23 - ending part of the SerialCalculation

5.1. Testing the atomicity of the SerialCalculation routine


The Atomicity of a function is an important property of any function that you must verify each time you want to
code a keygen. An Atomic function is simply a function that doesnt rely for doing its work from external
information, except those passed as its arguments, and which actions are only affecting the return value or the
parameters.

External Dependencies
(constants, global variables)

Parameters

Function

Function

Results

Function

Called Functions
Figure 24 - General schema of a Function to test Atomicity

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 30

Figure 24 reports the structure of a general function where, beside the normal flow of Parameters and Results,
there are two side flows of information: the Called Functions and the External Dependencies (constants or global
variables). These flows can also be bi-directional depending on the possibility of the Function to read and
eventually write some values. In order to create a keygen you must understand and document all these flows,
once isolated the function responsible of the serial number calculation. The keygen must re-create into a
separated program the same environment the Function wants, otherwise it will not work4
This property allows taking the function as it is, in assembler, and extracting it from the rest of the program, without
too much problems. A first overview of the function reveals that this function is atomic, but we will verify it also to
show the method for more complex cases and for the drivers specifically.
Easy tests in IDA reports that the function SerialCalculation doesnt call any other function and depend only from
the external constant at off_223F0. We anyway need to test if it can live on its own, with the proper arguments!!
Usually OllyDbg is very handy when you have to verify the atomicity of a function, but we are now reversing a
driver, which cannot be loaded into a Ring3 debugger. What we need is to properly modify the driver so as
OllyDbg will be able to load it (see also [8]), even if not completely execute it.
The method simply changes the *.sys PE Header so as to transform the driver into a normal Dll or a normal
executable: remember that a driver is a native executable or dll, linked to native dlls (usually hal.dll and
ntoskrnl.exe). We will use CFF Explorer to change the driver:

1.

Change the File Header Characteristics and tick File is a DLL

2.

Change the Optional Header SubSystem and select Windows GUI

3.

Change the Data Directories Import Directory RVA and Import Directory Size to 0, to erase the import
tables of system dlls

If this operation is too complex or long you can always create an Oraculum or self-keygen [6, 7]

PAGE 31

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

Now your driver is ready for OllyDbg, of course it will not work, but we can open into OllyDbg and work it out for
our needs.

5.2. Loading the modified driver into OllyDbg


Now we can open the driver within OllyDbg and go to where the function HSetLicense calls the function
SerialCalculation.
The address is not the same shown in OllyDbg (see Note 8), and
then we jump here:
003BCAAC
003BCAAD
003BCAAE

.
.
.

50
56
E8 6DF8FFFF

PUSH EAX
PUSH ESI
CALL SbieDrv.003BC320

Then we move the execution point just here, using Ctrl-Gray-* or


the context menu Set New Origin Here.

Note 8: Due to the modifications we did the


driver, it is not loaded at the same addresses
IDA shows, we must then use the file offsets.
You then need the OllyDbg plugin Olly
Advanced from Markus TH-DJM, which
modifies, among the other things, the goto
menu, adding the possibility to jump to file
offsets.

The result is that we skip all the rest of the code and will go
simulating the driver execution, when it calls the SerialCalculation
routine.
Using IDA we supposed which are the meanings of EAX and ESI and we can then now modify them so as to point
somewhere in memory, useful for our test.
These are the steps I will cover now, being the first already done:
1.

modify the sys so as it can be loaded into OllyDbg.

2.

place the execution point at


003BCAAC

50

PUSH EAX

using CTR+GRAY+* or the menu "set new origin here"

Generally speaking jumping the execution point wheres the call of the function that will generate the
serial number, is a good trick. This allows skipping all the rest of the program and allows us to test the
input conditions to the function so as to prepare for the keygen we are going to build. Its like in a
surgical operation where we want to transplant a living organ from the program to the keygen
3.

find a good place where to let them point, look for the memory map and find a place with all 0000 ... for
example I used:
EAX=3C2440 where there's space for the new string with the serial
ESI=3C2420 where I wrote in Unicode format "Shub-Nigurrath"

4.

change EAX and ESI so as to point respectively to the name in Unicode and to a position where to store
the serial number.

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

5.

PAGE 32

execute step by step the call at


003BCAAE

E8 6DF8FFFF

CALL SbieDrv.003BC320

and stop at the end.

You can see that the function properly filled the buffer pointed by ESI with a serial number.

003C2420
003C2430
003C2440

53 00 68 00 75 00 62 00 2D 00 4E 00 69 00 67 00
75 00 72 00 72 00 61 00 74 00 68 00 00 00 00 00
42 00 47 00 4E 00 4B 00 52 00 41 00 31 00 00 00

S.h.u.b.-.N.i.g.
u.r.r.a.t.h.....
B.G.N.K.R.A.1...

and the registers too are extremely interesting:

6.

Verify that works.

The function SerialCalculation is easily Atomic and can be extracted from the driver!

PAGE 33

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

5.3. Coding the keygenerator


The last step of this drama is to code a keygenerator which calls the function and shows the result. There are
some tricks to learn in order to do it.. First of all I will use the naked functions, I already explained in details in [9]. I
will not then cover this argument again here.
Second important thing is to copy the whole code of the SerialCalculation function and paste it into our
code and edit it like following:
1.

Remove the trailing addresses, leaving only the assembler instructions (remove all those things like
.text:0001C320). The format of the assembler instructions shown by IDA is the same supported by the
C language by means fo the __asm instruction. This is very handy, because you can copy and paste
quite effortlessly the code from IDA to a C program.
Change the relative addresses of the parameters and local variables of the function. The local variables
are not anymore referenced as offset of the EBP register but directly
So for example into IDA there's these lines:
mov
mov

edi, [ebp+arg_0]
[ebp+var_4], edx

now I have instead these two lines instead:


mov
mov

edi, [arg_0]
[var_4], edx

The offset with ebp is correctly handled by the compiler and I don't need it anymore
2.

Insert the assembler into an __asm{} block and then into a naked function (see [9] for an explanation of
what are the naked functions).
Using the naked functions allows skipping the prolog and epilog instructions the compiler places to
correctly handle the calls and the stack. This operation was already been done by the compiler that
created the call we are taking out from the driver. So our keygenerator doesnt need to add them
anymore. We will leave the prolog and epilog that are already between the asm instructions of the
SerialCalculation, without even worrying of which precisely are these instructions..

3.

Add all the external constants and the local variables used by SerialCalculation just outside the
function (the naked functions cannot have such variables into its body):
//string referenced into the program
char aA1b2c3d4e5f6g7[]="A1B2C3D4E5F6G7H8V9JKLMNPQRSTWXYZ";
//simulates the offset call we saw in IDA, through a pointer holding the start
//of the string..
char* off_223F0=aA1b2c3d4e5f6g7;
//local variables IDA identified for us and we placed them back, the routine use them
DWORD var_4=0, var_8=0;

4.

Assemble everything using UNICODE format strings; we saw that the arguments of the
SerialCalculation function are Unicode strings. Instead of printf use the widechar version
wprintf/wscanf or alternatively the printf/scanf with the correct format specifier.
A note: you could also use printf but you must correctly handle the Unicode string. For example:
wchar_t wstring=LExaxmple of string;
wprintf (LI am an Unicode text, shown on not Unicode screen %s, wstring).
printf(I am an Unicode text shown on not Unicode screen %S,wstring);
in this example the Unicode constant shown (I am an Unicode) is not really important, the important
is to show it on screen; the Unicode string wstring is shown correctly using wprint or the printf with %S
format specifier. The same happens with wscanf and scanf.

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 34

The result is the following piece of code..


#include <stdio.h>
#include <windows.h>
//prototype of the function as it is read from IDA..
void CalculateSerial (wchar_t *arg_0, wchar_t *arg_4);
void main() {
//unicode variables
wchar_t name[255]=L"Shub-Nigurrath";
wchar_t serial[8]; //serial is long 0x0E
//keep the things clean and tidy
memset(name,0,sizeof(name));
memset(serial,0,sizeof(serial));
wprintf(L"Enter your name (max 255):");
wscanf(L"%s",name);
//call to the assembler code naked from the application
CalculateSerial(name,serial);
//printf out the result
wprintf(L"Name: %s\n", name);
wprintf(L"Serial: %s\n", serial);
}
//string referenced into the program
char aA1b2c3d4e5f6g7[]="A1B2C3D4E5F6G7H8V9JKLMNPQRSTWXYZ";
//simulates the offset call we saw in IDA, through a pointer holding the start of the string..
char* off_223F0=aA1b2c3d4e5f6g7;
//local variables IDA identified for us and we placed them back, the routine use them
DWORD var_4=0, var_8=0;
//The naked declaration allows to use all the assembler code as it is from IDA and to avoid
//worrying of the context record of the call, it is already correctly used into the asm
//instructions we got from IDA
__declspec( naked ) void CalculateSerial (wchar_t *arg_0, wchar_t *arg_4)
{
//The only differences with the ASM code into IDA are that the local variables are not
//referenced as offset of the EBP register but directly
//So for example into IDA there's these lines:
// mov
edi, [ebp+arg_0]
// mov
[ebp+var_4], edx
//now I have instead these two:
// mov
edi, [arg_0]
// mov
[var_4], edx
//The offset with ebp is correctly handled by the compiler and I don't need it anymore..
//
//All the other instructions are exactly the same as in IDA.
__asm{
push
ebp
mov
ebp, esp
sub
esp, 8
push
ebx
push
esi
push
edi
mov
edi, [arg_0]
xor
bl, bl
xor
esi, esi
cmp
[edi], si
mov
edx, 0BADF00Dh
mov
[var_4], edx
mov
byte ptr [var_8], bl
jz
loc_1C420
loc_1C344:
cmp
esi, 66h
jnb
loc_1C4C0
mov
al, [edi+esi*2]
mov
cl, byte ptr [var_4]
inc
esi

; CODE XREF: SerialCalculation+F7_j

PAGE 35

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE


test
al, 1
jz
short loc_1C35C
add
cl, al
jmp
short loc_1C35E
; --------------------------------------------------------------------------loc_1C35C:
inc
cl

; CODE XREF: SerialCalculation+36_j

loc_1C35E:
; CODE XREF: SerialCalculation+3A_j
test
al, 2
mov
byte ptr [var_4], cl
jz
short loc_1C376
mov
edx, [var_4]
mov
cl, al
shl
cl, 1
shr
edx, 8
add
dl, cl
mov
byte ptr [var_4+1], dl
jmp
short loc_1C37A
; --------------------------------------------------------------------------loc_1C376:
add
byte ptr [var_4+1], 2

; CODE XREF: SerialCalculation+43_j

loc_1C37A:
; CODE XREF: SerialCalculation+54_j
test
al, 4
mov
cl, byte ptr [var_4+2]
jz
short loc_1C38A
mov
dl, al
shl
dl, 2
add
cl, dl
jmp
short loc_1C38D
; --------------------------------------------------------------------------loc_1C38A:
add
cl, 4

; CODE XREF: SerialCalculation+5F_j

loc_1C38D:
; CODE XREF: SerialCalculation+68_j
test
al, 8
mov
byte ptr [var_4+2], cl
jz
short loc_1C3A3
mov
dl, byte ptr [var_4+3]
mov
cl, al
shl
cl, 3
add
dl, cl
mov
byte ptr [var_4+3], dl
jmp
short loc_1C3A7
; --------------------------------------------------------------------------loc_1C3A3:
add
byte ptr [var_4+3], 8

; CODE XREF: SerialCalculation+72_j

loc_1C3A7:
; CODE XREF: SerialCalculation+81_j
test
al, 10h
mov
cl, byte ptr [var_4]
jz
short loc_1C3B7
mov
dl, al
shl
dl, 4
add
cl, dl
jmp
short loc_1C3BA
; --------------------------------------------------------------------------loc_1C3B7:
add
cl, 10h

; CODE XREF: SerialCalculation+8C_j

loc_1C3BA:
test
al, 20h
mov
byte ptr [var_4], cl
jz
short loc_1C3D0
mov
dl, byte ptr [var_4+1]
mov
cl, al
shl
cl, 5
add
dl, cl
mov
byte ptr [var_4+1], dl

; CODE XREF: SerialCalculation+95_j

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 36

jmp
short loc_1C3D4
; --------------------------------------------------------------------------loc_1C3D0:
add
byte ptr [var_4+1], 20h

; CODE XREF: SerialCalculation+9F_j

loc_1C3D4:
; CODE XREF: SerialCalculation+AE_j
test
al, 40h
mov
cl, byte ptr [var_4+2]
jz
short loc_1C3E4
mov
dl, al
shl
dl, 6
add
cl, dl
jmp
short loc_1C3E7
; --------------------------------------------------------------------------loc_1C3E4:
add
cl, 40h

; CODE XREF: SerialCalculation+B9_j

loc_1C3E7:
; CODE XREF: SerialCalculation+C2_j
test
al, al
mov
byte ptr [var_4+2], cl
jns
short loc_1C3FD
mov
dl, byte ptr [var_4+3]
mov
cl, al
shl
cl, 7
add
dl, cl
mov
byte ptr [var_4+3], dl
jmp
short loc_1C401
; --------------------------------------------------------------------------loc_1C3FD:
add
byte ptr [var_4+3], 80h

; CODE XREF: SerialCalculation+CC_j

loc_1C401:
mov
ecx, [var_4]
mov
edx, ecx
shr
edx, 1Fh
add
ecx, ecx
or
edx, ecx
add
bl, al
cmp
word ptr [edi+esi*2], 0
mov
[var_4], edx
jnz
loc_1C344
mov
byte ptr [var_8], bl

; CODE XREF: SerialCalculation+DB_j

loc_1C420:
mov
esi,
and
esi,
mov
ecx,
sub
ecx,
mov
eax,
shr
eax,
mov
ecx,
shl
edx,
pop
edi
or
eax,
mov
edx,

; CODE XREF: SerialCalculation+1E_j

mov
and
movzx
mov
mov
shr
mov
and
movzx
mov
shr
mov
and
movzx

[var_8]
1Fh
20h
esi
edx
cl
esi
cl
edx
off_223F0

; call to the offset of valid characters for the serial


; number..

ecx, eax
ecx, 1Fh
si, byte ptr [edx+ecx]
ecx, [arg_4]
; will store the serial number into the arg_4
; It is passed to the function as pointer to Unicode array
; long 0Eh
[ecx], si
eax, 5
esi, eax
esi, 1Fh
si, byte ptr [edx+esi]
[ecx+2], si
eax, 5
esi, eax
esi, 1Fh
si, byte ptr [edx+esi]

PAGE 37

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE


mov
shr
mov
and
movzx
mov
shr
mov
and
movzx
mov
shr
mov
and
movzx
shr
mov
and
movzx
pop
mov
mov
pop
mov
pop
retn

[ecx+4], si
eax, 5
esi, eax
esi, 1Fh
si, byte ptr [edx+esi]
[ecx+6], si
eax, 5
esi, eax
esi, 1Fh
si, byte ptr [edx+esi]
[ecx+8], si
eax, 5
esi, eax
esi, 1Fh
si, byte ptr [edx+esi]
eax, 5
[ecx+0Ah], si
eax, 1Fh
ax, byte ptr [eax+edx]
esi
[ecx+0Ch], ax
word ptr [ecx+0Eh], 0
ebx
esp, ebp
ebp
8

; --------------------------------------------------------------------------loc_1C4C0:
; CODE XREF: SerialCalculation+27_j
mov
edx, [arg_4]
pop
edi
pop
esi
mov
byte ptr [var_8], bl
mov
word ptr [edx], 0
pop
ebx
mov
esp, ebp
pop
ebp
retn
8
;SerialCalculation endp
} //and of __asm block
} //end of SerialCalculation function

The result of the program is like the following:

You can now add all the digital, amazing effects and musics if you want, or leave rude and direct as it is, but the
essence is already here, the keygen works!!

REVERSING OF A PROTECTION SCHEME BASED ON DRIVERS: SANDBOXIE

PAGE 38

6. References
[1] SandBoxie 3.00.03, www.sandboxie.com or mirrored at
http://arteam.accessroot.com/tools/SandboxieInstall.exe
[2] Windows DDK version 6000.070702 help, available on Microsoft site.
[3] Portable Executable File Format Compendium v11 by Goppit, http://tutorials.accessroot.com
[4] CheckSum Fixer v1.0 by Shub-Nigurrath, http://arteam.accessroot.com
[5] Beginners Tutorial #9, Defeating Magic Byte Protection by Gabri3l, http://tutorials.accessroot.com
[6] Guide on How to play with processes memory, write loaders and Oraculums by Shub-Nigurrath,
http://tutorials.accessroot.com
[7] Coding a Serial Sniffer (Oraculum) by Anorganix, ARTeam e-zine #2, http://tutorials.accessroot.com
[8] A Journey to the Center of the Rustock.B Rootkit by Frank Boldewin, 20th January 2007,
http://reconstructer.org
[9] Improving the HideDebugger function by Shub-Nigurrath, http://tutorials.accessroot.com

7. Greetings
Greets goes out to the entire ARTeam family with its past, present and future members. A word of thank you to
the team members who took the time to read the beta version of this document and also found some important
errors. If youve got suggestions and/or comments about this tutorial or want to contribute with something new or
simply say hi, stop by at the forum and participate to the discussions there. Again, sorry for releasing this tutorial in
different versions, but I think additions deserve a new release of this documents.

Document History

Version 1.0 First Public Release


Version 1.1 Added the keygenning of the program with complete sources too