Mammon_'s Tales to his Grandson
Mankind comes into the Ice Age



Configuring the Environment

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;".

  • Code ON|OFF : Code ON displays the hexidecimal byte code next to the disassembled listing.
  • Color : Color is used to set the screen colors for the display. The colors are 4-bit hexidecimal values (i.e., a value from 1 to 16, or in hex from 0 to F: 0=black 1=blue 2=green 3=cyan 4=red 5=magenta 6=brown 7=grey 8=dark grey 9=light blue A=light green B=light cyan C=light red D=light magenta E=yellow F=white), and each color setting is a combination of two colors, the background and foreground--such that 0F would be white text on a black background, while F0 would be black text on a white background. The areas of the display that can be set are normal (color of normal text), bold (color of bold/highlit text), reverse (color of reverse-display text), help (color of the help line), and line (color of horizontal lines). The sytnax for the color command is color normal bold reverse help line, such that color 7 F 71 30 2 would color the display as follows: normal text grey on black, bold text white on black, reverse video text blue on grey, help line black on cyan, horizontal line green on black.
  • Exp : Display export symbols from DLLs and EXEs. This command is vital to understanding a disassembled listing; it picks out the Windows calls from the program's internal calls so that you do not spend hours tracing through Kernel32.dll. Typing exp module name will load the symbols from that module; some good choices are kernel32.dll/kernel.exe, user32.dll/user.exe, and gdi32.dll/gdi.exe. Exp can also be used to display the currently loaded modules (exp /), and to list the symbols that match a certain pattern (exp delete would list all of the symbols in the loaded modules that begin with Delete). Note that it is a good idea to load the exports in Winice.dat rather than from the Soft-Ice command line.
  • Lines # : Increase or decrease the number of lines (or font size) of the display.
  • WC # : Enable code window of # lines (e.g. WC 25).
  • WD # : Enable data window of # lines (e.g. WD 15). Shift-F3 changes the format of the data.
  • WR : Enable register window.
  • WW # : Enable watch window of # lines (e.g. WW 5).

    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;"


    Tracing Through Code

    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.

  • P : Execute one program step, executing calls as a single instruction. Shortcut: the F10 key.
  • P RET : Execute until a return instruction is found. Shortcut: the F12 key.
  • T : Execute one instruction. Shortcut: the F8 key.


    Viewing and Editing Memory

    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.

  • D : Display memory contents. Syntax: D size address L length, where size can be B (byte), W (word), D (double word), S (short real), L (long real), or T (10-byte real), and address is the memory address to be displayed (an address pointed to by the contents of a register--like EAX-- or a register offset--like [ebp-04]--can be displayed by typing the register name and offset in place of the address); the l displays the data to the Command window (the default display area is the Data window), with length determining how many rows (at 4 bytes apiece, as in the data windows) of memory to display.
  • S : Search RAM for a string. Syntax: s address L length data-list, with address being the memory address to start searching at, length being the length in bytes of the search area (FFFFFFFF is the max), and data-list being the data you are searching for--strings must be in quotes (either single or double), bytes are simply the hexidecimal bytecodes. Switches: -c makes the search case-insensitive, -u searches for a Unicode string. S 30:0 L ffffffff 'username' searches the entire 4 GB of address space for the string "username". S es:di+10 L ecx 'Hello',12,34 searches the address space starting at es:di for the number bytes of bytes specified (in hex) in the ECX register for the the string "Hello" followed by 12h and 34h.


    Watches

    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.


    Using Breakpoints

    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.

  • Bc # : Clear Breakpoint--erases a breakpoint set in Soft-Ice; you must know the breakpoint number..
  • Bd # : Disable Breakpoint--disables but does not delete a breakpoint set in Soft-Ice; you must know the breakpoint number.
  • Be # : Enable Breakpoint--enables a breakpoint which has been disabled; you must know the breakpoint number.
  • Bl : List Breakpoints--lists all of the breakpoints currently set in Soft-Ice, their status, and their breakpoint numbers.
  • Bmsg : Break on Windows Message. Syntax: BMSG window handle L begin-message end-message
  • Bpint : Break on Interrupt. Only works with interrupts handled by the IDT (95/NT). Syntax: BPINT int-number
  • Bpio: Break on I/O port address read and/or write. Sytnax: BPIO port [R|W|RW] [EQ|NE|GT|LT|M value] [c=count]
  • Bpm: Break on memory read, write, or execution. Syntax: BPM[B|W|DW] address [R|W|RW|X]
  • Bpx : Break on execution. Syntax: BPX address/symbol [c=count] [EQ|NE|GT|LT|M value] [c=count]
  • CSIP : Qualify breakpoints. Syntax: CSIP [off |[not] starting-address ending-address | module-name] Using not causes the breakpoint to occur only if the CSIP is not equal to the starting addresses or Windows modules listed; otherwise execution breaks only if the breakpoints specified by the user are within the specified address/module. "Off" disables the CSIP checking.


    Obtaining System Information

  • Addr : Display or switch to an Address Context.
  • Class : Display Information on Windows Classes.
  • CPU : Display the CPU Registers.
  • Exp : Load/Display Export Symbols from DLL files.
  • GDT : Display the Global Descriptor Table.
  • Heap : Display the Windows Global Heap.
  • Heap32 : Display/Walk the Windows Global Heap.
  • HWND : Display information on Windows Handles.
  • IDT : Display the Interrupt Descriptor Table.
  • LDT : Display the Local Descriptor Table.
  • LHeap : Display the Windows Local Heap.
  • Map32 : Display a memory map of all 32-bit modules loaded in memory.
  • MapV86 : Display the DOS memory map of the current Virtual Machine.
  • Mod : Display the Windows Module List.
  • Page : Display Page Table information.
  • Proc : Display information about a process.
  • Stack : Display a call stack.
  • Sym : Set or display a symbol.
  • Task : Display the Windows Task List.
  • Thread : Display information about a thread.
  • TSS : Display Task State Segment and I/O Port Hooks.
  • VCall : Display the names and addresses of VxD callable routines.
  • VM : Display information on virtual machines.
  • VXD : Display the Windows VXD map.
  • .VMM : Call VMM Debug Informational Services menu.
  • .VPICD : Call VPICD Debug Information Menu.
  • .VXDLDR : Display VXD information.
  • WMSG : Display the names and message numbers of Windows messages.


    Trapping General Protection Faults

    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!).


    Exercises

    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:00000BF4
    
    
    There 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 004047E2
    
    
    
    
    004049BF 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....


    Home * Tools * 95/NT Tech Info * Links