Soft-Ice has a file called WINICE.DAT that can be modified to configure your debugger for optimal use. All of the
configuration options can be entered from the Soft-Ice command line, so you may want to experiment before overwriting
the program defaults. In general the Function keys can be left alone, but you may wish to modify the visible windows,
the size and color of the display, the function exports and the code display. Changes can be added to the INIT="X;" line
in WINICE.DAT just as they would be on the command line, such that one possible initialization string could be
INIT="Code ON;WR;lines 50;WC25;X;".
Here is a sample "init" line from WINICE.DAT:
INIT="lines 60;wd 13;wc 25;wr;wl;dex 1 ss:esp;watch *ds:esi;watch ds:esi;watch eax; watch *eax;watch es:edi;code on;color 07 0B 71 0C 02;X;"
The whole point of using a debugger is that you can single-step through program code as watch the changes made to
registers, memory, variables, and flags as the program runs. In general you will want to "Program Step" using the F10 key,
thereby skipping over irrelevant calls and OS service routines. When you find a section of interest, and whose code you want to thoroughly
disassemble (often part of a disassembler's ASM listing that needs clarification), you will "Single Step" through the code using the F8 key to see
exactly what the program is doing. Is is important to have the export symbols loaded for the Windows .DLLs, for unless you are debugging the
kernel, you don't want to be led off track by a standard API call that winds through various functions and sub-functions of USER.EXE. Should you get
caught in a call that you desperately want out of, you can use the F12 key to jump ahead to the next RET instruction.
As you trace through code, you will often need to view the contents of memory locations in order to keep track of variables, pointers, and interrupts; in addition, you may need to search the entire addressable memory range for a specific piece of data--say, for example, a user name--in order to set a breakpoint on access to that data. The contents of any of the windows in the Soft-Ice display can be editted by accessing the window with an Alt key sequence (Alt-R for registers, Alt-D for data, Alt-C for code, etc.) and directly modifying the displayed data.
Watches allow you track a variable while you are debugging a program; needless to say, this is a very important function for cracking and reverse engineering. To open the watch window, type ww at the Soft-Ice command line; typing watch followed by an variable name (eg watch user_id ) adds that variable (and its value) to the watch window. Registers and stack offsets (not to mention memory values) can be watched by using them in place of the variable name, such as watch es:di and watch [ebp+18]. In addition, since many registers and stack offsets merely point to address where the real variables are stored, you can watch the value referenced by the register or stack offset by typing a * before the name of the register/offset (eg, watch *es:di). Good variables to watch are es:di, *es:di, eax, and any [esp+?] or [ebp+?] that references input by a user.
It is impossible to debug effectively without a working knowledge of breakpoints. A breakpoint is simply an instruction for the cpu to halt execution upon access to a certain memory area or upon system and program events (such as windows messages and program instructions) and turn control over to the debugger. When you set a breakpoint in Soft-Ice and then run the target program, Soft-Ice will pop up when the condition for the breakpoint is met with its usual display of code, registers, and data--halted at the instruction that caused the breakpoint to activate.
This section is still in the "experimentation" phase. Soft-Ice traps all of your GPFs, but this is little consolation as there is not a whole lot you can do about it. I have noticed two patterns with GPFs caught by Soft-Ice: one in which you are trapped in a Wait_Semaphore loop, and the other in which all of the opcodes in the code window are "FFFFFFFF INVALID". In the former case, you can F12 back to the main Kernel code and try to "feel" (that's crackerspeak for stepping through again and again until you notice a pattern) which Jcc (conditional jump, for example a JNZ) is causing the problem, then edit the flags (R FL Z in Soft-Ice to toggle ON or OFF the Zero flag) before that particular JNZ (this has worked exactly once for me, and even then all it did was save my computer from locking up; the app still crashed); in the case of the latter, you can (pray! hah, just kidding...) assemble a RET statement using the Soft-Ice A command (A address, where address is the line with the FFFFFFFF INVALID code) and hope that this area of code was CALLed and not JMPed (this has not worked for me at all, but the theory is more-or-less OK ; ). Other than that, unless you wrote and/or memorized the Windows Kernel source code, you are going to have to live with the GPF (but you must go down fighting!).
Soft-Ice is an extremely powerful program with a complex interface that is best learned through experience. The following exercises are provided as a means for users to familiarize themselves with Soft-Ice.
Exercise 1: Debugging an existing application
Often you will experience frustration when using a program written by a third party (third-party in this case meaning "not yourself", rather than "not Microsoft"); Soft-Ice is an excellent tool for resolving such situations. Inso Corporation's Quick View Plus, for instance, has a bug in the "Register" window: when you enter a serial number and click "Register", the program displays a message box claiming the number you have entered is invalid. Needless to say, Inso Corp has labelled this bug a "security feature", but nevertheless we are here to fix it.
Start out by installing Quick View Plus Trial edition; the Install Wizard will guide you through its dismal blue screens and grey dialog boxes. When it finishes, Quick View Plus will run and a small grey window with three buttons ("Purchase", "Uninstall", "Continue") will appear; it is here that the exercise will begin in earnest.
1) Click "Purchase", then "$49", then "Accept", and finally "Unlock by Phone".
2) Press Ctrl-D to bring up Soft-Ice. Our approach is going to be to capture messages to the "Register" window, and to accomplish this we need to do a little scouting: BMSG requires an hwnd parameter, and the HWND command requires a process name. We will therefore first type TASK at the Soft-Ice command line, which shows us the following:
Taskname SS:SP StackTop StackBot StackLow TaskDB hQueue Events Order32 0000:0000 005ED000 005F0000 11DE 2EEF 0000 ...There it is, right on top: Order32. The Syntax for HWND is HWND task, so we will now type HWND ORDER32, which will give the following output:
Window-Handle hQueue SZ QOwner Class_Name Window Procedure 07A8(1) 2EEF 32 ORDER32 #32770 (Dialog) 173F:00004757 07AC(2) 2EEF 32 ORDER32 Static 173F:000052FA ....... 07DC 2EEF 32 ORDER32 Edit 173F:00000BF4There are a ton of windows listed, with 07A8 being the main dialog box, but the one we are interested in is the only edit control: 07DC.
3) What we are going to do now is set a breakpoint on window 07DC (the edit control) for the windows message WM_GETTEXT; by only trapping this message (which retrieves text from an edit control), we avoid having to cycle through the various screen-painting and mouse-over routines that Windows processes every millionth of a second. Note that we could instead breakpoint on GetDlgItemText, GetDlgItemTextA, GetWindowText, and GetWindowTextA to achieve the same effect, but it is wise to set only 1 breakpoint instead of 4 (plus the programmer may have written their own "Get Text" routine, in which case these breakpoints would fail us). The command we will type at the Soft-Ice prompt is BMSG 07DC WM_GETTEXT.
4) Now we are ready for action. Enter the Unlocking Code you like best (I lean towards 666111666) and press "OK". You will pop immediately into Soft-Ice, in USER!BOZOSLIVEHERE--Microsoft's little idea of a joke, I guess; press F12 to RET out of the function and you will be in USER!BEAR498, F12 again to USER!GLOBALGETATOMNAME, F12 once again to end up in PROT16 code, another F12 brings you to USER!DIALOGBOXINDIRECTPARAM, and finally one more F12 and you will end up in KERNEL.Alloc. This is fairly important to notice, for in most cases where your breakpoint dumps you into Windows code (and F11 will get you out of only a single layer), you will come across KERNEL.Alloc right before you come back to the application's code. A good rule of thumb is therefore to F12 fanatically until you reach KERNEL.Alloc, then press F12 once to get back to the application.
5) Press F12 that one final time, and you will find yourself in ORDER32!.text+39B9, with the following code:
0137:004049B9 Call [User32!GetDlgItemTextA] 0137:004049BF LEA ECX,[EBP-68] 0137:004049C2 PUSH ECX 0137:004049C3 CALL 004047E2 0137:004049C8 ADD ESP,04 0137:004049CB TEST EAX,EAX 0137:004049CD JNZ 004047E2004049BF LEA ECX,[EBP-68] is the current line of code; Call User32!GetDlgItemTxtA had just been executed. Looking at the next few lines of code, you will realize that this is all we need: this is a classic TEST/JNZ scheme. The Unlock Code has been stored in EBP-68 by GetDlgItemTextA; it is then moved to ECX and pushed on to the stack as the only parameter to the function at 004047E2. When the program returns from the call, the stack is corrected (ADD ESP,04) and a boolean value (1 or 0, equaling TRUE or FALSE) has been stored in EAX by the function. The value in EAX is then tested to find out if it is TRUE (1) and if so, the program moves on to the "good-unlock-code-now-register-the-poor-fellow" code. In psuedo-C-code, this would look like
CHAR UnlockCode; BOOLEAN IsGood; ... GetDlgItemText(hwnd 07DC, int UnlockCodeField, charbuf [EBP-68], int 8); UnlockCode=[EBP-68]; IsGood=ValidateUnlockCode(UnlockCode); if (IsGood) UnlockProgram(); else MessageBeep(); ...Now we must make sure we are right about this code. F8 through the LEA instruction, then type D ECX: at the Soft-Ice command line and there in the data window you will see
013F:005EF604 36 36 36 31 31 31 36 36-00 05 00 00 7F 16 85 63 66611166....^..c
OK, so that is our serial about to be manipulated. Look close, what do you see?
Our dear little 666111666 (or the first eight digits of it, actually) being loaded into ECX as a parameter
to 004047e2 ValidateUnLockCode(UnlockCode). F10 down to the JNZ, but halt there. Notice it says JNZ 00404A29 (NO JUMP)
Now we will toggle the zero flag, so that it becomes not-zero, by typing R FL Z.
Notice how the code has changed to JNZ 00404A29 (JUMP)
At this point we will Ctrl-D to return control back to the program and see if we were right...a new window pops up:
Quick View PLus Unlocked
Thank you for purchasing ya-di-ya-di-ya....
Press "OK", the lesson is over!
To clean up, press Ctrl-D and type
BC *
to clear all of your breakpoints.
Exercise 2: Regaining Lost Access
When you have your system set up for maximum security, it is always a danger that you will forget your password and thereby lose access to your data. In most cases you can simply perform a dictionary attack on your encrypted files (or whichever target is giving you trouble), but when you have walked away from your PC for awhile and that screensaver you forgot you installed kicks in, protected by a password you have long forgotten, you will have to reboot--and lose data--in order to access your machine at all. Soft-Ice, however, is powerful enough to get you through these difficult moments.
To begin, install a screensaver with password protection (if you haven't already) through the Display Control Panel in Windows 95. Wait for it to kick in, then move the mouse to activate the password login dialog box. Here goes:
1). Type in the wrong password, then Ctrl-D and put a breakpoint on hmemcpy (bpx hmemcpy)--hmemcpy being the kernel function used by Windows to shuffle strings around in memory, for instance when they are input from a dialog box being checked for authenticity. Press Ctrl-D again to return to the screen saver, the punch the OK button. Soft-Ice will pop up and you will end up in KERNEL!LOGERROR+0123
2). F12 until you read Kernel.Alloc (10 times), then once more to get back into the target code, in this case called (amazingly enough) PASSWORD!.text
Note that this points us immediately to our target executable, password.cpl in the windows\system directory. Password.cpl is a 37,376 byte Control Panel extension--so immediately we know that .scr files use the Passwords Control Panel applet (or sub-applet) to protect your machine. Password.cpl exports the following functions:
0000 00001151 CPlApplet
0001 00003f3b PPChangePassword
0002 00003eb9 PPGetPasswordStatus
0003 00004006 VerifyScreenSavePwd
and (like any Windows program) imports a good many more, including these:
MPR.dll
0015 PwdSetPasswordStatusA
004e WNetVerifyPasswordA
0011 PwdChangePasswordA
0013 PwdGetPasswordStatusA
003f WNetGetUserA
This target is getting more interesting by the moment...it may require a "full reverse" in the Projects page...3). Look closely at the code:
0137:7C45428F CALL [7C4582BC]
0137:7C454295 TEST EDI, EDI
0137:7C454297 JNZ 7C4542B1
0137:7C454299 LEA EAX, [EBP-04]
0137:7C45429C LEA ECX, [EBP-14]
0137:7C45429F PUSH EAX
0137:7C4542A0 PUSH ECX
0137:7C4542A1 CALL 7C454536
0137:7C4542A6 TEST EAX, EAX
0137:7C4542A8 JZ 7C4542DE
0137:7C4542AA MOV EAX,00000001
0137:7C4542AF JMP 7C454322
Password protection schemes usually do not place an unencrypted copy of the password in memory; in general,
the user input is encrypted using the same algorithm as the password-encryptor, then the two encrypted strings are
compared (yes, it can be made much tougher than this, but that is the basic idea). What this means is that if you are
hunting for the password, you are in for a lot of math; if you just want to get access, then it is a simple
matter of a CMP statement--the "flag-toggling" approach.If you eyeball the code a little, You'll notice that there are two classes of jumps: one that dumps you in the 7C4542xx range (the JNZ at 7C454295 and the JZ at 7C4542A8), and the one that dumps you in the 7C4543xx range (the JMP at 7C454322). What it seems like--and this is an intuition that will come after scrolling through tons of such code--is that there are two checks (maybe one for password length and one for password contents, though in this exercise the purpose of the checks is trivial), then an EAX=00000001 (in boolean, "TRUE") followed by a jump to a location after the "consequences" of the checks.
It appears, therefore, that you wish to fail both of the checks (i.e., not jump) and end up at 7C454322. How do you know? At this point, just a guess really. But if you trace through the code, you will notice that with the wrong password, you will take both of these jumps (i.e., if you fake past the first one, the second will get you) and end up with an "invalid password" message.
(4) F10 through the code until the JNZ 7C4542B1
. At this point you will toggle the zero flag ( r fl z), then
F10 through the code until the JZ 7C4542DE
. Once again, toggle the zero flag, and press CTRL-D to
return to the screensaver...which will immediately disappear and allow you to access the desktop. Be sure to clean up that hmemcpy
breakpoint before you leave...
The Good Part? Now you know how to break past a puny screensaver password-protection scheme using Soft-Ice (and many similar schemes, use your imagination). The Bad Part? No-one whose machine you want to break into is going to have Soft-Ice installed. Of course, after a full reverse-engineering, you can write a utility to kill the .scr thread or fool the .cpl file, then stick it in the autorun.inf of a CD-ROM and hope the target computer has "autoplay" enabled....