Here I'm going to show you how to crack this crackme. We'll use some basic reversing techniques to figure out how it works and how to break or bypass its copy protection.
Some knowledge of IA-32 assembly would be beneficial to understand what is going on but I'll try to explain it in enough detail that you should be able to follow anyway.
If you want to follow along the setup is:
- 32bit Windows 7 Home Edition installed inside a VMware virtual machine
- OllyDBG installed
- Microsoft Visual Studio Express 2013 for Windows Desktop installed and 'VC/bin/' in the PATH variable
After you download the zip file and look inside it you should seee this:
Drag the folder they are sitting in to the desktop and run
SomeCrypto~01.exe by double clicking on it.
You should see a rather intimidating window with no explaination like this:
Try to put some junk input in the 2 fields but nothing happens:
Let's close this, it tells us nothing, and have a closer look at the binary itself.
One of the first things you should always look at is the imports section of the binary, it tells you what functions are being imported by the application from other libraries (.dll files).
This tells you a lot about the application and there are nearly always imports because the application has to communicate with the OS.
dumpbin is an application that comes with
Microsoft Visual Studio Express 2013 for Windows Desktop and on my test machine resides in
C:\Program Files\Microsoft Visual Studio 12\VC\bin\.
It can be used to look at some of the sections in PE executables.
Open up a command prompt with admin privileges:
And run the command
dumpbin /imports "C:\Users\user\Desktop\SomeCrypto~01\SomeCrypto~01.exe".
The location of the crackme file might be different depending on what your local username is for Windows (mine is
user) and if you extracted it to somewhere other than the desktop.
You should see an output like this:
Microsoft (R) COFF/PE Dumper Version 12.00.21005.1 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file C:\Users\user\Desktop\SomeCrypto~01\SomeCrypto~01.exe File Type: EXECUTABLE IMAGE Section contains the following imports: KERNEL32.dll 402018 Import Address Table 4024F4 Import Name Table 0 time date stamp 0 Index of first forwarder reference 215 GetModuleHandleA USER32.dll 402020 Import Address Table 4024FC Import Name Table 0 time date stamp 0 Index of first forwarder reference 20E MessageBoxA 121 GetDC 265 ReleaseDC 114 GetClientRect 2BB SetTimer DC EndPaint DA EndDialog 1EE LoadImageA 129 GetDlgItemTextA AB DialogBoxParamA 28F SetDlgItemTextA E BeginPaint GDI32.dll 402000 Import Address Table 4024DC Import Name Table 0 time date stamp 0 Index of first forwarder reference 1FB GetObjectA 13 BitBlt E6 DeleteObject 277 SelectObject 30 CreateCompatibleDC Summary 1000 .data 1000 .rdata 69000 .rsrc 1000 .text
This shows us the functions that are being imported and which dll file each function is in.
The first thing that stands out to me is the call to
MessageBoxA on line 25.
These challenges normally create a message box saying "Success" or something when you have done it so a call to this might be where in the application we want to get to.
And once we are there we should be able to trace back through the code to see where the check was done.
The second thing I notice is the call to
GetDlgItemTextA on line 33.
This function looks like it could be responsible for getting our input, if this is true we could follow the code from that point and find the code that checks our input and the success code.
Digging A Little Deeper
We can ignore the rest for now and go straight to opening the application in OllyDBG. Open Olly with administrator privileges:
Click File->Open and choose the
You should then see this:
The big section in the top left is the main disassembly window, this shows the disassembly of the application from the entry point of the application (
004012DE), this is where execution of the application starts and these are the CPU instructions that are going to run.
We can check this using
dumpbin with the following command:
dumpbin /headers "C:\Users\user\Desktop\SomeCrypto~01\SomeCrypto~01.exe" | find /i "entry point"
12DE entry point (004012DE)
The columns are from left to right, the memory address of the instruction, the hex representation of the instruction, the ia-32 assembly representation and finally notes that Olly puts there for us.
The top right section contains the values of the CPU registers, these are used primarily as storage for the CPU while it is running instructions. There are a couple of special ones which I'll explain if I need to.
The format is:
[CPU register name] [value] [Olly notes]
The bottom right section is the stack window and shows the current status of the stack, the stack is used to store function arguments and local variables as well as a few other things.
The columns are from left to right, memory address, 4 byte hex value at that memory address, Olly notes
The bottom left section is the dump window and can be used to dump certain bits of memory to see what is there.
The dump window has titles for its columns.
Let's see if there are any interesting strings in here, right click anywhere and click on Search for->All referenced text strings:
You should see the strings window with 4 entries:
The third entry down looks promising (Success).
Let's have a look where in the application this is, right click on it and click Follow in Disassembler:
You should see something like this:
As you can see the function call is to
MessageBoxA, this is where we want to end up.
Just above the function call are the instructions that decide whether or not the bit of code that calls
MessageBoxA is run (I've highlighted the relevant rows).
Understanding The Authentication Logic
This is basically just calling some internal function at
00401000 then using the return value to decide whether or not to jump to
004012CA. If the return value is
0 the jump happens.
This is the code at
004012CA |> 5F POP EDI 004012CB |. 5E POP ESI 004012CC |. 33C0 XOR EAX,EAX 004012CE |. 5B POP EBX 004012CF |. 8BE5 MOV ESP,EBP 004012D1 |. 5D POP EBP 004012D2 \. C2 1000 RETN 10
It just exits so we don't want to end up here, this is the fail case.
Let's look inside this function to see what it does, right click on the function call (at
0040129D) and click Follow:
You should see this:
This is the function that decides if our name and/or serial are correct. Here is the full disassembly:
00401000 $ 55 PUSH EBP 00401001 . 8BEC MOV EBP,ESP 00401003 . 8A01 MOV AL,BYTE PTR DS:[ECX] 00401005 . 83EC 20 SUB ESP,20 00401008 . 56 PUSH ESI 00401009 . 33F6 XOR ESI,ESI 0040100B . 84C0 TEST AL,AL 0040100D . 0F84 B3000000 JE SomeCryp.004010C6 00401013 . 8D55 E0 LEA EDX,DWORD PTR SS:[EBP-20] 00401016 . 2BD1 SUB EDX,ECX 00401018 > 3C 61 CMP AL,61 0040101A . 0F8C A6000000 JL SomeCryp.004010C6 00401020 . 3C 7A CMP AL,7A 00401022 . 0F8F 9E000000 JG SomeCryp.004010C6 00401028 . 88040A MOV BYTE PTR DS:[EDX+ECX],AL 0040102B . 8A41 01 MOV AL,BYTE PTR DS:[ECX+1] 0040102E . 41 INC ECX 0040102F . 46 INC ESI 00401030 . 84C0 TEST AL,AL 00401032 .^75 E4 JNZ SHORT SomeCryp.00401018 00401034 . 83FE 1A CMP ESI,1A 00401037 . 0F85 89000000 JNZ SomeCryp.004010C6 0040103D . 33C0 XOR EAX,EAX 0040103F . 90 NOP 00401040 > 8A88 10304000 MOV CL,BYTE PTR DS:[EAX+403010] 00401046 . 8888 40314000 MOV BYTE PTR DS:[EAX+403140],CL 0040104C . 40 INC EAX 0040104D . 84C9 TEST CL,CL 0040104F .^75 EF JNZ SHORT SomeCryp.00401040 00401051 . 33C9 XOR ECX,ECX 00401053 . 380D 40314000 CMP BYTE PTR DS:,CL 00401059 . 74 2D JE SHORT SomeCryp.00401088 0040105B . EB 03 JMP SHORT SomeCryp.00401060 0040105D 8D49 00 LEA ECX,DWORD PTR DS:[ECX] 00401060 > 8A81 40314000 MOV AL,BYTE PTR DS:[ECX+403140] 00401066 . 3C 61 CMP AL,61 00401068 . 7C 14 JL SHORT SomeCryp.0040107E 0040106A . 3C 7A CMP AL,7A 0040106C . 7F 10 JG SHORT SomeCryp.0040107E 0040106E . 0E PUSH CS 0040106F . BE C08A9405 MOV ESI,5948AC0 00401074 . 7F FF JG SHORT SomeCryp.00401075 00401076 FF DB FF 00401077 FF DB FF 00401078 . 8891 40314000 MOV BYTE PTR DS:[ECX+403140],DL 0040107E > 41 INC ECX 0040107F . 80B9 40314000 >CMP BYTE PTR DS:[ECX+403140],0 00401086 .^75 D8 JNZ SHORT SomeCryp.00401060 00401088 > 83C8 FF OR EAX,FFFFFFFF 0040108B . BA 40314000 MOV EDX,SomeCryp.00403140 00401090 . 85C9 TEST ECX,ECX 00401092 . 74 19 JE SHORT SomeCryp.004010AD 00401094 > 0FB632 MOVZX ESI,BYTE PTR DS:[EDX] 00401097 . 33F0 XOR ESI,EAX 00401099 . 81E6 FF000000 AND ESI,0FF 0040109F . C1E8 08 SHR EAX,8 004010A2 . 3304B5 5820400>XOR EAX,DWORD PTR DS:[ESI*4+402058] 004010A9 . 42 INC EDX 004010AA . 49 DEC ECX 004010AB .^75 E7 JNZ SHORT SomeCryp.00401094 004010AD > F7D0 NOT EAX 004010AF . 3D 18B291F8 CMP EAX,F891B218 004010B4 . 75 10 JNZ SHORT SomeCryp.004010C6 004010B6 . 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] 004010B9 . C700 40314000 MOV DWORD PTR DS:[EAX],SomeCryp.00403140 004010BF . B0 01 MOV AL,1 004010C1 . 5E POP ESI 004010C2 . 8BE5 MOV ESP,EBP 004010C4 . 5D POP EBP 004010C5 . C3 RETN 004010C6 > 32C0 XOR AL,AL 004010C8 . 5E POP ESI 004010C9 . 8BE5 MOV ESP,EBP 004010CB . 5D POP EBP 004010CC . C3 RETN
Here I will go over this code in detail and try to understand what it is doing.
First it starts with the function prologue:
00401000 $ 55 PUSH EBP 00401001 . 8BEC MOV EBP,ESP
This is common among all stdcall and cdecl functions, it just sets up the stack frame (for more information about stack frames check out the "Stack Frames" section of my post on basic binary auditing).
The next instruction is interesting:
00401003 . 8A01 MOV AL,BYTE PTR DS:[ECX]
This is using the ECX register before anything has been done to it in this function. This means that whatever is stored in ECX was stored there in the calling function and it was passed as an argument to this current function.
The reason it was passed in a register instead of on the stack (how arguments are normally passed) is because the compiler knew it was in control of all points of entry into this function.
The most likely way that the compiler would have known this is if the function was explicitly defined with the static keyword. This means that only functions inside the same source file can call this function.
To figure out what is stored in ECX at this point in the application without running it, we will need to look back through the code that called this function, here is that code:
00401267 |. 8B3D 40204000 MOV EDI,DWORD PTR DS:[<&USER32.GetDlgItemTextA>] ; USER32.GetDlgItemTextA 0040126D |. 6A 40 PUSH 40 ; /Count = 40 (64.) 0040126F |. 8D8C24 C4000000 LEA ECX,DWORD PTR SS:[ESP+C4] ; | 00401276 |. 51 PUSH ECX ; |Buffer 00401277 |. 68 E9030000 PUSH 3E9 ; |ControlID = 3E9 (1001.) 0040127C |. 56 PUSH ESI ; |hWnd 0040127D |. FFD7 CALL EDI ; \GetDlgItemTextA 0040127F |. 6A 40 PUSH 40 ; /Count = 40 (64.) 00401281 |. 8D9424 84000000 LEA EDX,DWORD PTR SS:[ESP+84] ; | 00401288 |. 52 PUSH EDX ; |Buffer 00401289 |. 68 EA030000 PUSH 3EA ; |ControlID = 3EA (1002.) 0040128E |. 56 PUSH ESI ; |hWnd 0040128F |. FFD7 CALL EDI ; \GetDlgItemTextA 00401291 |. 8D4424 0C LEA EAX,DWORD PTR SS:[ESP+C] 00401295 |. 50 PUSH EAX 00401296 |. 8D8C24 84000000 LEA ECX,DWORD PTR SS:[ESP+84] 0040129D |. E8 5EFDFFFF CALL SomeCryp.00401000
As you can see there are 2 calls to
GetDlgItemTextA and we have 2 fields (Name and Serial). But at this time we don't know which field is which however we do have their ID's.
The prototype for
UINT WINAPI GetDlgItemText( _In_ HWND hDlg, _In_ int nIDDlgItem, _Out_ LPTSTR lpString, _In_ int nMaxCount );
And from the manual page for it:
hDlg [in] Type: HWND A handle to the dialog box that contains the control. nIDDlgItem [in] Type: int The identifier of the control whose title or text is to be retrieved. lpString [out] Type: LPTSTR The buffer to receive the title or text. nMaxCount [in] Type: int The maximum length, in characters, of the string to be copied to the buffer pointed to by lpString. If the length of the string, including the null character, exceeds the limit, the string is truncated.
So the second argument is the ID of the field and the third is the buffer that the text is going to be stored in.
This means that we are looking for the control with ID 3EA. Line 16 (on the disassembly of the 2 calls to
GetDlgItemTextA above) shows that ECX is being loaded with the address of ESP+84, just before ESP+84 is loaded as the buffer argument to
GetDlgItemTextA with an ID of 3EA.
If you remember back to when we used
dumpbin to list all of the imported functions, there was also a function called
SetDlgItemTextA being imported.
This function is likely used to set the values to "Enter you name..." and "Enter your serial...". We can use this to figure out which of these ID's (
3EA) is the serial field and which is the name field; and ultimately which is being passed to our checking function in ECX.
We could use the strings window again and find out where "Enter you name..." and "Enter your serial..." are referenced but I'll show you a different way to find them using the function name (
First close OllyDBG, open it again and open the crackme again so that you get to this point again:
Right click anywhere and click Search for->All intermodular calls:
After that, this window should pop up:
You can see the second and third entries are calls to
SetDlgItemTextA, looking at the addresses on the left these calls are right next to each other.
Right click on 1 of them and click Follow in Disassembler or press Enter:
You should see this:
Here is the disassembly of these calls:
00401116 |. 8B3D 48204000 MOV EDI,DWORD PTR DS:[<&USER32.SetDlgIte>; USER32.SetDlgItemTextA 0040111C |. 68 60244000 PUSH SomeCryp.00402460 ; /Text = "Enter your name..." 00401121 |. 68 E9030000 PUSH 3E9 ; |ControlID = 3E9 (1001.) 00401126 |. 56 PUSH ESI ; |hWnd 00401127 |. FFD7 CALL EDI ; \SetDlgItemTextA 00401129 |. 68 74244000 PUSH SomeCryp.00402474 ; /Text = "Enter your serial..." 0040112E |. 68 EA030000 PUSH 3EA ; |ControlID = 3EA (1002.) 00401133 |. 56 PUSH ESI ; |hWnd 00401134 |. FFD7 CALL EDI ; \SetDlgItemTextA
The prototype for this function is:
BOOL WINAPI SetDlgItemText( _In_ HWND hDlg, _In_ int nIDDlgItem, _In_ LPCTSTR lpString );
Looking at this its obvious that the control with ID 3EA is the serial number field because it is being set to "Enter your serial...".
We can verify this by setting a breakpoint at the top of the serial checking function, running the application and checking the value of the ECX register.
First go to the check function again:
Then right click on the top instruction (at address
00401000) and click Breakpoint->Toggle or press F2:
You should then see the background of the address section (on the far left) turn red:
Then run the application by clicking Debug->Run or pressing F9:
You should see this after the breakpoint is hit (it shouldn't take long for the breakpoint to hit):
As you can see in the registers window (in the top right), the value of ECX is the address that contains the string "Enter your serial..." so our static analysis of the code was correct.
Now we can get back to analysing the code in this serial checking function.
The following is the start of the function excluding the prologue:
00401003 . 8A01 MOV AL,BYTE PTR DS:[ECX] 00401005 . 83EC 20 SUB ESP,20 00401008 . 56 PUSH ESI 00401009 . 33F6 XOR ESI,ESI 0040100B . 84C0 TEST AL,AL 0040100D . 0F84 B3000000 JE SomeCryp.004010C6 00401013 . 8D55 E0 LEA EDX,DWORD PTR SS:[EBP-20] 00401016 . 2BD1 SUB EDX,ECX
The first line loads the first byte of our serial into the AL register (The lower byte of the EAX register).
Some space is then reserved on the stack for a local variable. On line 3 the value of the ESI register is saved on the stack and zero'ed out (xor'ing anything with itself makes the result 0).
The byte in the AL register (at this point in time the first character in our serial) is checked for 0 on line 5 and if it is 0 execution jumps to
Let's look at the code at
004010C6 > 32C0 XOR AL,AL 004010C8 . 5E POP ESI 004010C9 . 8BE5 MOV ESP,EBP 004010CB . 5D POP EBP 004010CC . C3 RETN
This clearly just sets the return value to
0 and returns, we already know that we don't want a return value of
0 so this is our failure case.
Following the jump we have an LEA instruction, which loads the value of our local variable, and a SUB command, which calculates the distance from the local variable to where our serial is in memory.
The result of these 2 instructions (the distance from the local variable to where our serial is in memory) is stored in the EDX register.
Next we have the following loop:
00401018 > 3C 61 CMP AL,61 0040101A . 0F8C A6000000 JL SomeCryp.004010C6 00401020 . 3C 7A CMP AL,7A 00401022 . 0F8F 9E000000 JG SomeCryp.004010C6 00401028 . 88040A MOV BYTE PTR DS:[EDX+ECX],AL 0040102B . 8A41 01 MOV AL,BYTE PTR DS:[ECX+1] 0040102E . 41 INC ECX 0040102F . 46 INC ESI 00401030 . 84C0 TEST AL,AL 00401032 .^75 E4 JNZ SHORT SomeCryp.00401018
This is checking if the value in AL is below 61 (lines 1 and 2) or above 7A (lines 3 and 4) and jumping to
004010C6 if it is.
Looking at the ascii table 61 is a and 7A is z.
So if the first character is not a lowercase letter, execution will jump to the same failure case as before.
If the jumps aren't taken the byte is moved to the address pointed to by
EDX+ECX on line 5. This will point to the right position in the local variable due to the earlier
Then (on line 6) the next byte in the serial is moved into AL, both ECX and ESI are incremented. Lastly, on lines 9 and 10, AL is checked for 0 and the jump to
00401018 is only taken if AL is 0.
This is clearly just making sure only lowercase letters are part of the serial, so at least we now know the possible different characters that are allowed in the serial.
Another thing to notice here is that ESI is being used as a counter and at the end will contain the number of characters in the serial.
Let's look at the 2 lines following this loop:
00401034 . 83FE 1A CMP ESI,1A 00401037 . 0F85 89000000 JNZ SomeCryp.004010C6
If you'll remember, ESI contains the number of characters in the serial and here its being checked against
1A (or 26 in decimal). If ESI isn't equal to 26 then our failure case is taken again (
We then zero out EAX and onto the next loop:
00401040 > 8A88 10304000 MOV CL,BYTE PTR DS:[EAX+403010] 00401046 . 8888 40314000 MOV BYTE PTR DS:[EAX+403140],CL 0040104C . 40 INC EAX 0040104D . 84C9 TEST CL,CL 0040104F .^75 EF JNZ SHORT SomeCryp.00401040
This is simply moving a string from
403140 and only stops once it hits a
The data at
403010 we can see by right clicking on the line (line 1 here) and click Follow in Dump -> Address constant:
You should see this:
It will show the following in the dump window:
00403010 49 78 20 6C 7A 63 74 75 Ix lzctu 00403018 73 64 7A 65 74 67 63 2C sdzetgc, 00403020 20 65 78 20 6E 2D 66 73 ex n-fs 00403028 62 20 28 6E 76 66 6E 75 b (nvfnu 00403030 6A 75 76 75 6A 73 78 2D juvujsx- 00403038 66 73 62 29 20 6A 6E 20 fsb) jn 00403040 65 20 66 65 6E 6A 6C 20 e fenjl 00403048 6C 73 61 74 73 78 72 78 lsatsxrx 00403050 75 20 73 77 20 6E 63 61 u sw nca 00403058 61 72 75 7A 6A 6C 20 71 aruzjl q 00403060 72 63 20 65 68 64 73 7A rc ehdsz 00403068 6A 75 67 61 6E 20 70 67 jugan pg 00403070 6A 6C 67 20 74 72 7A 77 jlg trzw 00403078 73 7A 61 6E 20 6E 76 66 szan nvf 00403080 6E 75 6A 75 76 75 6A 73 nujuvujs 00403088 78 2E 20 49 78 20 66 68 x. Ix fh 00403090 73 6C 71 20 6C 6A 74 67 slq ljtg 00403098 72 7A 6E 2C 20 75 67 72 rzn, ugr 004030A0 63 20 65 7A 72 20 75 63 c ezr uc 004030A8 74 6A 6C 65 68 68 63 20 tjlehhc 004030B0 76 6E 72 6D 20 75 73 20 vnrm us 004030B8 73 66 6E 6C 76 7A 72 20 sfnlvzr 004030C0 75 67 72 20 7A 72 68 65 ugr zrhe 004030C8 75 6A 73 78 6E 67 6A 74 ujsxngjt 004030D0 20 66 72 75 70 72 72 78 fruprrx 004030D8 20 75 67 72 20 71 72 63 ugr qrc 004030E0 20 65 78 6D 20 75 67 72 exm ugr 004030E8 20 6C 6A 74 67 72 7A 75 ljtgrzu 004030F0 72 62 75 2E 00 rbu..
This is everything before and including the first
Once this loop has completed we have the following:
00401051 . 33C9 XOR ECX,ECX 00401053 . 380D 40314000 CMP BYTE PTR DS:,CL 00401059 . 74 2D JE SHORT SomeCryp.00401088 0040105B . EB 03 JMP SHORT SomeCryp.00401060
This zero's out ECX and checks the value at
403140 against CL (
0) and if they match jumps to
00401088, otherwise jumps to
Let's see what happens if you jump on line 3 is taken:
00401088 > 83C8 FF OR EAX,FFFFFFFF 0040108B . BA 40314000 MOV EDX,SomeCryp.00403140 00401090 . 85C9 TEST ECX,ECX 00401092 . 74 19 JE SHORT SomeCryp.004010AD
Another jump is taken if ECX is
0 and we know it will at this point. Here is the code at that location:
004010AD > F7D0 NOT EAX 004010AF . 3D 18B291F8 CMP EAX,F891B218 004010B4 . 75 10 JNZ SHORT SomeCryp.004010C6 004010B6 . 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] 004010B9 . C700 40314000 MOV DWORD PTR DS:[EAX],SomeCryp.00403140 004010BF . B0 01 MOV AL,1 004010C1 . 5E POP ESI 004010C2 . 8BE5 MOV ESP,EBP 004010C4 . 5D POP EBP 004010C5 . C3 RETN
This is the end of the function.
Its pretty obvious that this function creates a checksum of a modified version of the string we found earlier and checks it against
F891B218, if it is equal the function returns 1, otherwise it return 0.
At this point I remember that there are no rules to this crackme so patching is allowed.
Exit OllyDBG completely and make a copy of the application like this:
This isn't needed, I just do it to be careful.
Ope the copy and go to the serial checking function and the line under
CMP AL,61, where it says
JL SomeCryp.004010C6 at
0040101A, double click, type
je 4010A2 and click Assemble.
The little window should have stayed open, type
jmp 4010C6 and click Assemble again.
You should see the following:
This should check if the first character in the serial is a and if it is jump to
4010A2, otherwise jump to
4010C6 which is the failure case.
Click Cancel and scroll down the the memory address
4010A2. Double click there, type
mov eax, 0xf891b218 and click Assemble.
You should see this:
Now just fill the rest with NOP's until the
004010AF like this:
This should ensure that if the serial contains an a at the start it should set the value of EAX accordingly.
Save these modifications to the application file by right clicking anywhere and clicking Copy to exe->All modifications:
It should open this window:
Click Copy all and you should see something like this:
Click the close button in the top right corner of this window and you should get the following dialog:
Now if you browse to the directory with the crackme files in you should see a new file:
The file ending in .bak is the backup created by Olly, and the 1 named
SomeCrypto~01 - Copy.exe is our patched file.
Just run it and put anything as the name and any serial starting with an a:
You don't need to fully understand every part of an application while reverse engineering it, it depends on what you are trying to achieve and the complexity of the application.
Try to concentrate as much as possible on the important areas and ignore everything else.
When beating a protection mechanism sometimes its easiest to just bypass the protection as opposed to trying to break it.
The best book I've read on this topic is Reversing: Secrets of Reverse Engineering by Eldad Eilam.
Happy Hacking :-)