################################################################################ # Disrespect Copyrights in Practice # # by Nomenumbra/[0x00SEC] # ################################################################################ (code and other files associated with this article (and my other articles featured in hackthiszine #4 are located at http://www.hackbloc.org/zine/vivalarevolution.rar - pass is 'anarchism') Disclaimer: Some official shit that's needed: This document is to be used for legal and educational purposes only. The author, nor anyone publishing this article can and/or will/might/shall not be held responsible in any way for any damage (potentially) done by anything described in this article. If this informotion makes you want to rape, murder, lie, cheat, pillage, extort, be hyporitical and capatalisitic I strongly advise you to cut your veins and die ... Foreword: In this globalist world there are only two values left, how much one can consume for the hightest possible price and how much one can produce for the least possible pay, all to serve the great green god, commonly referred to as 'the dollar', and it's imperialistic hegemonistic pions, commonly referred to as 'CEOs'. Their ways of extortion of third world countries and the social 'lowerclass' and abduction of free speech and thought in the first world have taken gross forms in today's society.. And like this isn't enough, they have been joined by whitehats to help 'secure' their software from people who break their unrighteous copyrights. This article will give the reader a standard overview of techniques used to protect applications and ways to bypass them.. The target applications (called "Acts" (Act I,Act II,etc)) come with this zine (if everything goes ok :p ) Have phun! Introduction: Well people, reversing applications can range in difficulity level from extremely easy to mindcrushing. Since this article is an introduction, I won't discuss extremely advanced schemes but I will show you some nice reversing tricks. Required knowledge to understand this article: -)Basic understanding of 32-bit windows ASM -)Basic understanding of the usage of Debuggers/Disassemblers -)A brain You can either try to crack each app first and read my tutorial afterwards or just follow along, you choice. Each Act is given an "objective" so you know what to look for and what you can learn there (all passwords are normal words,eg. no Ae534RKLjl passwords but SOMEPASSWORD). Act I: Difficulity: [....] Tools: OllyDbg Objective: Find the password Ok, imagine you just downloaded a nice game ("LameGame V 1.0") and you're ready to enjoy playing it. You launch the bitch and THIS jumps up: LameGame V1.0 (c) MegaCorp 2006-2099 Usage: cp1 Ok, THAT sucks ass, now we'll have to supply a password as a command-line argument... Well, it shouldn't be THAT difficult to crack... Let's fire up OllyDbg and load our app .... One of the first things I always do when reversing an app is checking what strings are inside the body. Now, if we scroll down a bit we'll see the text "LameGame V1.0" displayed. Now we take a look at the assembler in that area we see a call to where the result of a call to 0042A040 (this result is argv[1]) gets compared to the "BULKMONEY". That was foolish, leaving the password in plaintext in the executable.... Act II: Difficulity: My granny could do this Tools: OllyDbg Objective: Find the password MegaCorp recently released a new version of "LameGame" since V1.0 was could be cracked by any no-brains monkey. The new version claims to be more secure than the first, but is this true? We fire up OllyDbg again and we see that the string "HMPCBMJTU" gets copied to the address 00443010. Now we search for the "LameGame V1.1" string. This time argv[1] gets compared to 00443010, so argv[1] is compared to "HMPCBMJTU" or is it? Take a closer look and you'll see that the result of strlen("HMPCBMJTU") gets stored at EAX, and compared to DWORD PTR SS:[EBP-4] (which is obviously a counter), if it isn't below (so we've reached the end of the string "HMPCBMJTU") we leave this subroutine. Now notice the following: DWORD PTR SS:[EBP-4] gets stored at EAX, then the offset of "HMPCBMJTU" is added (we now have the address of the current character in EAX), the next interesting thing is the decrease of that character's value (MOVZX EAX,BYTE PTR DS:[EAX] then DEC AL). Then we load the counter in EAX and increase it and continue the loop. So what happens is that every character gets decreased with 1, so the password should be "GLOBALIST".... Pathetic company, they really don't know their shit, now do they?..... Act III: Difficulity: Easy as pie.... Tools: OllyDbg Objective: Find the password Well, MegaCorp anounced they recently hired a new programmer to ensure the cracking of their game would be made impossible by implementing a far more sophisticated encryption algorithm [that'd be time....]. Well, we fire up Olly again and see not much has changed, the subroutine structures have remained the same. But when we take a closer look we can see the cryptoscheme HAS been improved (still pathetic and breakable within 13 seconds but hey....) Well, we don't want to go trough all the hassle of thinking :D so we'll just let the debugger do the job... See the POP EBP at 004013F8? well, we'll put a breakpoint there to freeze execution once we get there (so we can see how the cryptostring is decrypted).Now press F9 and GO! Watch the dump an Voila, we got it 004013CF |. 81C1 10304400 |ADD ECX,Cp1.00443010 ; ASCII "EXTORTION" Act IV: Difficulity: Medium Tools: OllyDbg Objective: Find the password or find hash-collision Instead of reducing the absurdly high price of "LameGame" MegaCorp gave up it's production because all they care about is profit and not their customers. But they just brought out a new product, a new firewall named "Infernal Barricade". In order to install "Infernal Barricade" we need to bypass their newest copyright scheme. Let's take them on with OllyDbg once again... Hmm... no strcmp anymore? That means they have though of something else than using a password. Let's take a closer look. It seems that the program makes the final desicion as to whether your key was correct or not here: 00401491 |> 807D FF 00 CMP BYTE PTR SS:[EBP-1],0 00401495 |. 74 26 JE SHORT Cp1.004014BD 00401497 |. C74424 04 3400>MOV DWORD PTR SS:[ESP+4],Cp1.00440034 ; ASCII "Installing 'Infernal Barricade'..." And these call/cmp constructions are probably used to analyze your key too: 0040146B |. E8 308C0200 CALL Cp1.0042A0A0 00401470 |. 837D 08 01 CMP DWORD PTR SS:[EBP+8],1 00401474 |. 7E 1B JLE SHORT Cp1.00401491 00401476 |. 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C] 00401479 |. 83C0 04 ADD EAX,4 0040147C |. 8B00 MOV EAX,DWORD PTR DS:[EAX] 0040147E |. 890424 MOV DWORD PTR SS:[ESP],EAX 00401481 |. E8 0AFFFFFF CALL Cp1.00401390 00401486 |. 3D 10030000 CMP EAX,310 0040148B |. 75 04 JNZ SHORT Cp1.00401491 0040148D |. C645 FF 01 MOV BYTE PTR SS:[EBP-1],1 after analyzing each call it turns out this one: 00401481 |. E8 0AFFFFFF CALL Cp1.00401390 is the most interesting (looks like the decryption-constructions we've seen before). The function returns a value in EAX that gets compared to the static value 0x310. If we examine the function we can see the argument passed (argv[1] in this case) is manipulated into a hash value, let's test this thesis. To fake a command-line go to Debug->Arguments and supply your argument. Ok, time to put a breakpoint before the end of the subroutine (located at 004013F9) and F9! Now take a look at the EAX register's value (seen in the right part of the screen), I used "FUCKYOU" as an argument, resolving to 0x21C .... That means we must supply a commandline argument that will be resolved to 0x310. We could do this in two ways, by looking for a collision in the algorithm or by bruteforce. Let's rip the algorithm first. Ok, to make things clear: DWORD PTR SS:[EBP-8] is the counter (i) DWORD PTR SS:[EBP+8] is the beginning of argv[1] DWORD PTR SS:[EBP-C] is input[i] (DWORD PTR SS:[EBP-8]+DWORD PTR SS:[EBP-8]) 004013A4 |> 8B45 08 /MOV EAX,DWORD PTR SS:[EBP+8] ; | 004013A7 |. 890424 |MOV DWORD PTR SS:[ESP],EAX ; | 004013AA |. E8 C1F30000 |CALL ; \strlen 004013AF |. 3945 F8 |CMP DWORD PTR SS:[EBP-8],EAX 004013B2 |. 73 45 |JNB SHORT Cp1.004013F9 004013B4 |. 8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8] 004013B7 |. 0345 F8 |ADD EAX,DWORD PTR SS:[EBP-8] 004013BA |. 0FBE00 |MOVSX EAX,BYTE PTR DS:[EAX] 004013BD |. 8945 F4 |MOV DWORD PTR SS:[EBP-C],EAX 004013C0 |. C745 F0 000000>|MOV DWORD PTR SS:[EBP-10],0 004013C7 |. 8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8] 004013CA |. 0345 F8 |ADD EAX,DWORD PTR SS:[EBP-8] 004013CD |. 8038 00 |CMP BYTE PTR DS:[EAX],0 004013D0 |. 74 0D |JE SHORT Cp1.004013DF 004013D2 |. 837D F8 00 |CMP DWORD PTR SS:[EBP-8],0 ;if i is 0 result is 0 004013D6 |. 74 07 |JE SHORT Cp1.004013DF 004013D8 |. C745 F0 010000>|MOV DWORD PTR SS:[EBP-10],1 004013DF |> 8B45 F0 |MOV EAX,DWORD PTR SS:[EBP-10];-> (input[i] && i) 004013E2 |. 3345 F8 |XOR EAX,DWORD PTR SS:[EBP-8];-> EAX XoR i 004013E5 |. 0345 F8 |ADD EAX,DWORD PTR SS:[EBP-8];-> (EAX XoR i) + i 004013E8 |. 8B55 F4 |MOV EDX,DWORD PTR SS:[EBP-C] 004013EB |. 31C2 |XOR EDX,EAX ;-> ((EAX XoR i)+i) ^ input[i]) 004013ED |. 8D45 FC |LEA EAX,DWORD PTR SS:[EBP-4] 004013F0 |. 0110 |ADD DWORD PTR DS:[EAX],EDX 004013F2 |. 8D45 F8 |LEA EAX,DWORD PTR SS:[EBP-8] 004013F5 |. FF00 |INC DWORD PTR DS:[EAX] 004013F7 |.^EB AB \JMP SHORT Cp1.004013A4 "Hash" algorithm: (input[i] XoR (((input[i] && i) XoR i) + i)) Well, writing a bruteforcer for this is peanuts but there must be an easier way....through algorithmic collision. Let's see, the input "TEST" generates 319 as a value, now let's try "UEST" ... 320, how predictable and let's try "TFST" -> 322. Now we're getting somewhere :D. Ok, let's try filling up the bitch with A's. "AAAAAAAAAA" resolves to 721 while 1 A more gives us 805, so we need to sit somewhere in between. "AAAAAAAAAZ" resolves to 716 ,"AAAAAAAABZ" to 719 and "AAAAAAAACZ" to 718, let me predict, "AAAAAAAAEZ" wil resolve to 720.... <.< Ok, we need 784... after some trying we find out "AAAAAAA{{Z" resolves to 784.. Let's try >:).. YES! It works... Our collisive hash managed to trick the program into installing, without having having to know the 'real' password (which was MILITARISM btw).... Act V: Difficulity: Medium Tools: OllyDbg, Hexeditor Objective: Find the password, defeat anti-debugging MegaCorp got fed up with being cracked over and over so they consulted some whitehat corporate lapdog to strengthen their apps and sell our scene out at the same time... Rumor has it he implemented an anti-debugging trick in the newest version of "Infernal Barricade". Let's fire up OllyDbg YET AGAIN! Ok, lets see what they have been trying to do this time... 0040144F |. C600 00 MOV BYTE PTR DS:[EAX],0 ; || 00401452 |. E8 E9F50000 CALL ; ||[IsDebuggerPresent 00401457 |. 85C0 TEST EAX,EAX ; || 00401459 |. 74 18 JE SHORT Cp1.00401473 ; || 0040145B |. C70424 0C00440>MOV DWORD PTR SS:[ESP],Cp1.0044000C ; ||ASCII "Your attempt to debug this application is considered a crime by the U.S governement, legal action will be taken against you... " 00401462 |. E8 69F30000 CALL ; |\printf 00401467 |. C70424 FFFFFFF>MOV DWORD PTR SS:[ESP],-1 ; | 0040146E |. E8 4DF30000 CALL ; \exit LOL! They use a standard win32 API called IsDebuggerPresent to check if the application is being debugged.... hmmm, 004013C4 |. C74424 04 0000>MOV DWORD PTR SS:[ESP+4],Cp1.00440000 ; |ASCII "LOIACU]QH" seems to be the encrypted password, we don't want to spend a lot of time to rip the algorithm and decrypt it by hand so let's debug it! As expected the application terminates when we debug it this way. Let's take a closer look at the anti-debug technique: 00401452 |. E8 E9F50000 CALL ; ||[IsDebuggerPresent 00401457 |. 85C0 TEST EAX,EAX ; || 00401459 |. 74 18 JE SHORT Cp1.00401473 ; || This piece is interesting, it calls IsDebuggerPresent and sees if true is returned in EAX, if so, it ends, if not it continues... hmm interesting conditional jump, what if we'd make it an uncomditional jump, always jumping to continue the application (JMP is 0xEB, keep that in mind)..... Fire up a hexeditor (or just do it in OllyDBG, i just want to let you play with HexEditors as well :D ) and open the app in it. Now look for the following sequence of bytes: 00401457 |. 85C0 TEST EAX,EAX ; || 00401459 74 18 JE SHORT Cp1.00401473 ; || find: 85C07418 and replace the 74 with EB... That was easy, we already broke their anti-debugging technique (fuckers). Now all we gotta do is put a breakpoint on 00401470 . C600 00 MOV BYTE PTR DS:[EAX],0 so we can watch ECX being "IGNORANCE"... yet another application broken, hehe There are many commercial copyright-protection schemes which would make life difficult if we'd reverse only in the ways described, but there are other ways too, by taking advantage over the fact that the target program runs in YOUR environment, you control the OS! That means you can manipulate it from all sides. One way is process hijacking by DLL injection, which i'll describe here: Process Hijacking Process hijacking involves executing you code in another process' context (not as in exploiting it to make it execute shellcode). This can be achieved in two ways, either directly by executing a part of you executables code in the remote process, or by DLL injection. With the advent of Windows DEP (Data Execution Prevention) this leaves us the latter. Injecting your DLL into another process goes as follows: Fetch the target process' PID (Process ID) Open a handle to the target process Fetch the address of LoadLibraryA dynamically Allocate enough memory for an argument to LoadLibraryA Do a VirtualProtectEx to set the code pages to PAGE_EXECUTE_READWRITE write the name of the DLL to load ,into the memory (we obviously can't use a local address) restore the old permissions Here follows a sourcecode example in c++: BOOL WriteToMemroy(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize) { DWORD dwOldProtect; BOOL boolReturn = FALSE; if(hProcess == NULL) // own process? { VirtualProtect(lpBaseAddress, nSize, PAGE_EXECUTE_READWRITE, &dwOldProtect); // now Ex needed, only a VirtualProtect boolReturn = ((memcpy(lpBaseAddress, lpBuffer, nSize))? 1 : 0); //memcpy instead of WriteProcessMemory VirtualProtect(lpBaseAddress, nSize, dwOldProtect, &dwOldProtect); // set back } else { VirtualProtectEx(hProcess, lpBaseAddress, nSize, PAGE_EXECUTE_READWRITE, &dwOldProtect); // Virtualprotectex to be able to read and write code boolReturn = WriteProcessMemory(hProcess, lpBaseAddress, (LPVOID)lpBuffer, nSize, 0); // Write to memory VirtualProtectEx(hProcess, lpBaseAddress, nSize, dwOldProtect, &dwOldProtect); //set back } VirtualFreeEx(hProcess, lpBaseAddress, nSize, MEM_RELEASE); // free memory return boolReturn; } BOOL InjectDLL(char* ProcessName, char* strHookDLL) { printf("Initiating injection of '%s' into '%s'\n",strHookDLL,ProcessName); DWORD dwPID = GetProcessID(ProcessName); if(dwPID == 0) { printf("Couldn't retreive valid ProcessID for process '%s'!\n",ProcessName); return FALSE; } HANDLE hProcess; HMODULE hKernel; LPVOID RemoteStr, LoadLibraryAddr; hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID); // open the process if(hProcess == INVALID_HANDLE_VALUE) //couldn't open? { printf("Couldn't open process '%s' with ID %d!\n",ProcessName,dwPID); return FALSE; } hKernel = LoadLibrary("kernel32.dll"); //load kernel32.dll if(hKernel == NULL)// couldn't load? { printf("Couldn't load Kernel32.dll!\n"); CloseHandle(hProcess); return FALSE; } LoadLibraryAddr = (LPVOID)GetProcAddress(hKernel, "LoadLibraryA");// fetch address of LoadLibraryA RemoteStr = (LPVOID)VirtualAllocEx(hProcess, NULL, strlen(strHookDLL), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); // allocate memory size of argument if(WriteProcessBytes(hProcess, (LPVOID)RemoteStr, strHookDLL, strlen(strHookDLL)) == FALSE) // write it to memory { printf("Couldn't write to process '%s' memory!\n",ProcessName);// failed? CloseHandle(hProcess); return FALSE; } HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryAddr, (LPVOID)RemoteStr, 0, NULL);// remotely load our DLL if(hRemoteThread == INVALID_HANDLE_VALUE)// failure? { printf("Couldn't create remote thread within process '%s'!\n",ProcessName); CloseHandle(hRemoteThread); CloseHandle(hProcess); return FALSE; } CloseHandle(hProcess); printf("'%s' successfully injected into process '%s' with ID %d!\n",strHookDLL,ProcessName,dwPID); return TRUE; } Well that wasn't THAT difficult, now was it? The next question that arises is "What to inject?". Well you can do a lot once your DLL is loaded, ranging from process termination to full-blown input/output manipulation. The template of your DLL should look like this: BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { DisableThreadLibraryCalls((HMODULE)hModule); //don't get re-called // do what you want once attached return true; }break; case DLL_PROCESS_DETACH: { // bring back to old state }break; } return true; } Imagine the following application: int main(int argc, char *argv[]) { system("PAUSE"); if (argc-1) { if (strcmp(argv[1],"XPLT") == 0) MessageBoxA(0,"Accepted","Accepted",0); } return 0; } Ok, this simple app can be fooled by hijacking the main function it relies on, strcmp. Strcmp is a string comparing function located in the Dll ntdll.dll. The pause is used to ensure we get the time to inject our DLL into the victim app. Ok, we'll hijack the function by using a detours trampoline. Detours patching, as described in: http://research.microsoft.com/~galenh/Publications/HuntUsenixNt99.pdf goes as follows: Here follows a small example in C++: DWORD InlineHook(const char *Library, const char *FuncName, void *Function, unsigned char *backup) { DWORD addr = (DWORD)GetProcAddress(GetModuleHandle(Library), FuncName); // Fetch function's address BYTE jmp[6] = { 0xe9, //jmp 0x00, 0x00, 0x00, 0x00, //address 0xc3 // retn }; ReadProcessMemory(GetCurrentProcess(), (void*)addr, backup, 6, 0); // Read 6 bytes from address of hooked function from rooted process into backup DWORD calc = ((DWORD)Function - addr - 5); //((to)-(from)-5) memcpy(&jmp[1], &calc, 4); //build trampoline WriteProcessMemory(GetCurrentProcess(), (void*)addr, jmp, 6, 0); // write the 6 bytes long trampoline to address of hooked function to current process return addr; } This function resolves the address of the function to be hooked, and builds a trampoline as follows: JMP <4 empty bytes for addres to jump to> RETN the address to jump to (the hook) is resolved like this: ((To)-(From)-5) == ((HookAddress)-(TargetAddress)-5) the old address is backed up, to be able to unhook the function later (by overwriting the trampoline with the original address). Ok, now let's hijack our little app to make any password work: int WINAPI strcmphook(const char* str1,const char* str2); // prototype DWORD Faddr=0; // address BYTE Fbackup[6]; // backup DWORD InlineHook(const char *Library, const char *FuncName, void *Function, unsigned char *backup) { DWORD addr = (DWORD)GetProcAddress(GetModuleHandle(Library), FuncName); // Fetch function's address BYTE jmp[6] = { 0xe9, //jmp 0x00, 0x00, 0x00, 0x00, //address 0xc3 // retn }; ReadProcessMemory(GetCurrentProcess(), (void*)addr, backup, 6, 0); // Read 6 bytes from address of hooked function from rooted process into backup DWORD calc = ((DWORD)Function - addr - 5); //((to)-(from)-5) memcpy(&jmp[1], &calc, 4); //build trampoline WriteProcessMemory(GetCurrentProcess(), (void*)addr, jmp, 6, 0); // write the 6 bytes long trampoline to address of hooked function to current process return addr; } BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { DisableThreadLibraryCalls((HMODULE)hModule); //keeps it from being re-called Faddr = InlineHook("ntdll.dll","strcmp",strcmphook,Fbackup); // strcmp in ntdll.dll return true; }break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: { WriteProcessMemory(GetCurrentProcess(), (void*)Faddr, Fbackup, 6, 0); // restore address }break; } return true; } int WINAPI strcmphook(const char* str1,const char* str2) { return 0; // always return 0, no matter what password was. }; Once we inject this DLL into our victim app like this: InjectDLL("Victim.exe","hijack.dll"), you will notice that it doesn't matter what password you supplied as a commandline argument, you will always get the "Accepted" messagebox. As you can see process Hijacking can get you many things. You could subvert an application to elevate your privileges, create an extra account, download & execute an app with the privileges under which the app runs, you could even backdoor the app itself by letting it execute code to run the DLL injector @ startup, thus effectively taking over the app. Act VI: Difficulity: Hard Tools: OllyDbg,PEiD,DeYoda (found here: http://xtaz3k.free.fr/decryptors/Dy.ace) Objective: Get the MessageBox with the password to popup (the password IS encrypted and is not to be found in plaintext in the app, you can also decrypt the password by hand since the 'encryption' is pathetic, but that way you'll miss some valuable knowledge) Ok, there is this new IDE, called BulkIDE, you really wanna get your hands on, it is said to be quite nice, but the price tag is a 'little' high, $3000, outrageous for such a simple IDE, so let's crack the bitch. You managed to lay your hands on the main installer executable, but you seem to be missing the installation CD, but hey, we should get this working without that stupid license .exe :) It is rumored though that the programmers behind this IDE are fans of "security through obscurity" meaning we can expect a lot of opaque predicates (a function that evaluates to true or false and of which the outcome is known to the programmer on forehand, sometimes used as useless code that seems important or anti-debugging). First of all we load up PeiD and check the app, result: yoda's cryptor 1.2 This is probably your first encounter with a packer/crypter. Many software these days (especially commercial software and malware) is packed/crypted to make reversing a tiny whiny bit harder and to reduce executable size. Yoda's cryptor is quite a nice compressor/packer/crypter for PE files, but it can be undone in a wink, just fire up DeYoda, load the app and GO! Fire up PEiD again: Nothing found * Nice, that's what we wanna see. Now fire up OllyDBG and load the unpacked executable. We won't start looking at all strings, cause they are too obvious to be real passwords, they're just bogus shit to confuse the cracker. The first thing we see is: 00401000 >/$ 68 0A204000 PUSH unpacked.0040200A; /FileName = "user32.dll" 00401005 |. E8 B5020000 CALL ; LoadLibraryA 0040100A |. 68 15204000 PUSH unpacked.00402015;ProcNameOrOrdinal = "BlockInput" 0040100F |. 50 PUSH EAX; |hModule 00401010 |. E8 92020000 CALL ;GetProcAddress 00401015 |. A3 24204000 MOV DWORD PTR DS:[402024],EAX 0040101A |. 6A 01 PUSH 1 0040101C |. FF15 24204000 CALL DWORD PTR DS:[402024] Well, the following happens: GetProcAddress(LoadLibrary("user32.dll"),"BlockInput") gets stored in DWORD PTR DS:[402024]. BlockInput is a function to halt all keyboard and mouse input if it's argument is true, and resume it if it is false. If we look a bit further, at ox0040101A we see a call to BlockInput with a true parameter and at 0x00401048 we see it with a false parameter. So obviously the program attempts to block any input during program execution to prevent debugging and reversing. Well to get rid of this nuisance, we'll just nop those PUSH CALLDWORD PTR DS:[402024] structures out with right click -> binary -> fill with NOP's. Then we have another IsDebuggerPresent call, just breakpoint the test eax,eax after the call, set EAX to 0 and continue. 00401030 |> 50 PUSH EAX 00401031 |. BE EC114000 MOV ESI,unpacked.004011EC ; Entry address 00401036 |. B9 08000000 MOV ECX,8 0040103B |. E8 1C010000 CALL unpacked.0040115C Hmmm, what's this? Let's first take a look at unpacked.0040115C: 0040115C /$ 33D2 XOR EDX,EDX 0040115E |> 51 /PUSH ECX 0040115F |. AD |LODS DWORD PTR DS:[ESI] 00401160 |. E8 17000000 |CALL unpacked.0040117C 00401165 |. 03D0 |ADD EDX,EAX 00401167 |. 59 |POP ECX 00401168 |.^E2 F4 \LOOPD SHORT unpacked.0040115E 0040116A \. C3 RETN Ok, let's put it all in an ordered way: -)EDX is set to 0 -)ECX is saved -)EAX is loaded from ESI -)unpacked.0040117C is called -)EAX (probably the result of unpacked.0040117C) is added to EDX -)ECX is restored -)This is looped So this is an additive repeation of unpacked.0040117C. Let's check unpacked.0040117C out: 0040117C /$ B9 20000000 MOV ECX,20 00401181 |> D1E8 /SHR EAX,1 00401183 |. 73 05 |JNB SHORT unpacked.0040118A 00401185 |. 35 2083B8ED |XOR EAX,EDB88320 0040118A |>^E2 F5 \LOOPD SHORT unpacked.00401181 0040118C \. C3 RETN Some people (Vxers, reversers and comp. Sci. Students) will recognize this as a Cyclic Redudancy Check and that's what it is. A Cyclic Redudancy Check is a type of hash function used to produce a checksum, in order to detect errors in transmission or storage. Hmm so it seems unpacked.0040115C does an additive CRC over ECX bytes, to calculate the CRC checksum of the code area unpacked.004011EC and the next 8 bytes. This is obviously to check if the cracker made any modifications (breakpoints, nops,etc) to this code area. Now let's check what this area is all about: 004011EC /$ 6A 00 PUSH 0 004011EE |. 68 0D124000 PUSH unpacked.0040120D ; ASCII "DAEMON" 004011F3 |. 64:67:A1 3000 MOV EAX,DWORD PTR FS:[30] 004011F8 |. 0FB640 02 MOVZX EAX,BYTE PTR DS:[EAX+2] 004011FC |. 0AC0 OR AL,AL 004011FE |. 74 02 JE SHORT unpacked.00401202 00401200 |. EB 04 JMP SHORT unpacked.00401206 00401202 |> 33C0 XOR EAX,EAX 00401204 |. C9 LEAVE 00401205 |. C3 RETN 00401206 |> B8 01000000 MOV EAX,1 0040120B |. C9 LEAVE 0040120C \. C3 RETN Hmm, more experienced crackers will recognize this as a common trick to detect OllyDBG. To circumvent this we don't need to modify this section at all, we just need the Olly-Invisible plugin. Now, back to where we were, 0x0040103B. It seems the result of this check, along with the result of a call to 0x004011EC (the ollyDBG detection function) is stored in EDX and then 0x00401057 is called. Now we need to watch out since we are gonna be stuffed with Opaque predicates. All shit is bogus until this piece of code: 00401076 |. 68 06204000 PUSH unpacked.00402006 ; /RootPathName = "E:\" 0040107B |. E8 2D020000 CALL ; \GetDriveTypeA 00401080 |. 83F8 05 CMP EAX,5 Here the DriveType of E:\ is determined (since this is a test program not all drives are enumerated but E:\ is assumed as the CD-ROM drive, whatever since we don't have the installation CD it doesn't matter :D) and then it is checked if E:\ is a CD-ROM drive (5 being DRIVE_CDROM). The next important call is a call to GetVolumeInformationA, that will retrieve the CD-Serial in unpacked.00402020. As we can see here: 004010A6 |. 813D 20204000 >CMP DWORD PTR DS:[402020],DEADBEEF the serial is expected to be 0xDEADBEEF. Since we don't have the CD, we'll nop out the conditional jump right after the CMP (it's a JNZ jump, meaning the serial was invalid and only nasty stuff can happen afterwards so...). Now 0xDEADBEEF is stored in EDX (or at least, we store it there >:p) and a call to unpacked.004011A1 is made, which seems to be a decryption function based on this piece of code: 004011C6 |. B9 03000000 MOV ECX,3 004011CB |. BE 92124000 MOV ESI,unpacked.00401292 ; ASCII "es`" 004011D0 |. 8BFE MOV EDI,ESI 004011D2 |> AC /LODS BYTE PTR DS:[ESI] 004011D3 |. 34 32 |XOR AL,32 004011D5 |. AA |STOS BYTE PTR ES:[EDI] 004011D6 |.^E2 FA \LOOPD SHORT unpacked.004011D2 What we see here is interesting too: 004011A1 /$ E8 1F010000 CALL ; [GetTickCount 004011A6 |. 8BD8 MOV EBX,EAX 004011A8 |. CC INT3 004011A9 |. E8 17010000 CALL ; [GetTickCount 004011AE |. 2BC3 SUB EAX,EBX 004011B0 |. 3D 58270000 CMP EAX,2758 A call to GetTickCount (Function that retrieves the number of milliseconds that have elapsed since the system was started) is made, then INT3 is called and another call to GetTickCount is made, the results being substracted (EAX thus holding the difference). The interesting thing is INT3, INT3 is a breakpoint, thus halting the debugger and pausing the run of the app. You already feel it coming eh? Because a normal run of the app with a correct CD in the CD-drive would go fine (without CD the app would get lost in invalid,buggy and useless Opaque predicates) and smooth (INT3 doesn't break the app when not being debugged) the difference between the first and second GetTickCount would be nihil, but when debugging you either need to react very very fast (I gave you more time with 2758 milliseconds than most apps that use this trick) or just nop the shit out (providing you don't spot any nasty CRC tricks on that code ). For those that think "TO HELL, NOP THOSE CRCs OUT TOO! FUCK YEAH!", those CRCs could actually be used as an arithmetic parameter to a string decryption function. Well, to counter this, we would just fire up the debugger, run it check the CRC of the non-modified piece of code, note it restart all shit, modify the code and feed the good CRC to the decryption function, but that is another story. Then this function is called: 0040118D /$ AC LODS BYTE PTR DS:[ESI] 0040118E |. 3D CC000000 CMP EAX,0CC 00401193 |. 75 06 JNZ SHORT unpacked.0040119B 00401195 |. B8 01000000 MOV EAX,1 0040119A |. C3 RETN 0040119B |> B8 00000000 MOV EAX,0 004011A0 \. C3 RETN apperently a check if the breakpoint is left intact <.< A pathetic attempt, since we'll just manipulate the register holding the result (EAX). Now we continue and voila! We get the popup with the password: WAR. Afterword: Well, this was just the top of the iceberg, letting you taste the 'forbidden fruit' of reverse engineering, a most enjoyable and profitible practice, usefull for crackers,vxers and exploit developpers alike. There are many,many more ways for a programmer to protect his program from being cracked. The programmer could also make his program decrypt @ runtime (much like a virus) when the correct key is provided, but a reverse-engineer could whipe out the key-checking procedure with nop's (0x90) or turn the conditional jump after the key-checker into an unconditional one. He could make the app run in ring-0 but then we could use soft-ice to debug the app. The programmer could use rootkit techniques to hide his app from userland and kernelland, but then we could use the same techniques as rootkitdetectors. As you can see, there are endless amounts of ways to protect a program ... but even more to break it :D. I hope you enjoyed reading this article, I certainly enjoyed writing it and remember kids, don't let copyrights on shit products stop you, but give credit where credit is due! Outro: Greets and shouts go to HTS (zine staff) members, NullSec, ASO/PTP members, VX.netlux members, .aware crew,RRLF, the guys over at Smash The Stack, reversing.be (hagger in special for being such a fucking good reverser) and IRC dudes. # milw0rm.com [2006-09-27]