Softice's conditional breakpoints settings and macros
by Rhayader
30 October 1998


redCourtesy of Reverser's page of reverse engineering
 
 
SoftICE conditional breakpoints

      In my early cracking experience, I usually set a BPX for GetDlgItemTexta and
      GetWindowTexta inside SoftIce, whenever I found a program that ask for serial. Entering
      dummy code, and 'hoping' that SoftIce would pop up. Most of the time it works. Problem
      is, after I hit F12 (P RET), I usually get lost inside the code. Wondering where's that
      bloody text buffer that I can set a BPR with.

      After digging into SoftIce docs, I finally found a better way to do it (It's in Chapter 7 of the
      User Guide). IMHO, you should read it too. Some of the terms there might be hard to
      understand if you were just started. But, hey, that's what The Forum is for, isn't it? :)

      -----

      My aim here is to get SoftIce show us the text buffer for the two Win32 APIs mentioned
      above. I'll use breakpoint "action" to do that.

      Let's take a look at GetWindowTexta first. It's declared as:

      int GetWindowText( HWND hWnd, LPTSTR lpString, int nMaxCount );

      GetWindowText use stdcall calling convention. That means that argument will be pushed
      right to left. Since SoftIce pop up before the prologue code is executed, the EBP stack
      frame isn't set up yet. So we had to use ESP to adressed the argument. Here's how the
      stack will look like when SoftIce pop up:

      ...
      [ESP+0Ch] - nMaxCount
      [ESP+08h] - lpString
      [ESP+04h] - hwnd
      [ESP+00h] - return EIP

      When the function return, GetWindowTexta will put the text it retrieved to the location
      pointed to by lpString (LPTSTR is a long pointer to a null terminated string). Thus, we
      had to use SoftIce's indirection operator (it's the * character, same as C language, see
      Chapter 8 ;). For example, the command:

      D *(esp+8)

      means, "show in data window, the location pointed to by the content of esp+8". Since,
      this is a very common operation, SoftIce had a shorthand for it: esp->8. Allright then,
      now we can set the breakpoint such as this:

      BPX getwindowtexta DO "D esp->8;"

      And when we hit F12, we return to the caller and the text we entered will sit nicely at the
      top of the data window, waiting for us to set up a BPR with it :) Why don't we do a return
      to the caller automatically? Well, in my case, the screen flashes, and I hate it. But, if you
      want to try, you can set the breakpoint as:

      BPX getwindowtexta DO "D esp->8;P RET;"

      Now, let's take a look at GetDlgItemTexta. It is declared as:

      UINT GetDlgItemText( HWND hDlg, int nIDDlgItem, LPTSTR lpString, int nMaxCount );

      The only difference is nIDDlgItem, which is the ID of the control to get the text from. The
      stack will look like this:

      ...
      [ESP+10h] - nMaxCount
      [ESP+0Ch] - lpString << here it is
      [ESP+08h] - nIDDlgItem
      [ESP+04h] - hwnd
      [ESP+00h] - return EIP

      And the breakpoint to set (I had a feeling that you already find out ;)

      BPX getdlgitemtexta DO "D esp->C;"

      Well, that's all my friends. If you didn't want to type it everytime you want to use it, then
      you had to set up a macro for it. Read chapter 11 :D I'd like to tell you, but this became a
      looong post already. See ya...
 
 


 
Setting breakpoint action in SoftICE...

     You probably knew that GetWindowTexta and GetDlgItemtTexta will fail in a Delphi
      program. Yet, putting a breakpoint on Windows message (BMSG) will placed you too far
      from the calculation routine. So, I usually put a breakpoint when the program need to
      find out, whether we are a registered user or not. For most programs this registration
      information is usually stored in Windows' registry, some use ini file, and sometimes,
      programs use a regfile. In this post I only tackles the registry. I'm sure you can extend
      this to include the .INI and regfile.

      Putting the breakpoint in registry's API is easy. First, you must make sure that
      ADVAPI32.DLL is loaded by SoftIce. You can check it with the "EXP regqueryvalueex"
      command. If SoftIce already load ADVAPI32.DLL, it will respond with the address of the
      routine. To set a breakpoint on registry read, we use the command:

      BPX RegQueryValueExA

      After that, you can start the program that you want to break and SoftIce will break
      everytime the program read the registry. The problem here lies in "everytime". There will
      be hundreds of calls that read the registry. And it's not fun to hit Ctrl+D several time. So,
      we had to find a way, to make SoftIce stops only on the value that we are interested with.
      We're gonna use conditional breakpoint to do that (User Guide Chapter 7, sub:
      Conditional Breakpoints).

      -----

      We'll do a quick review for the API first. RegQueryValueEx is declared like this:

      LONG RegQueryValueEx(
      HKEY hKey, // handle of key to query
      LPTSTR lpValueName, // address of name of value to query
      LPDWORD lpReserved, // reserved
      LPDWORD lpType, // address of buffer for value type
      LPBYTE lpData, // address of data buffer
      LPDWORD lpcbData // address of data buffer size
      );

      When SoftIce break, the stack will look like this:

      ...
      [ESP+18h] - lpcbData
      [ESP+14h] - lpData << this is where the return data will be put by Windoze
      [ESP+10h] - lpType
      [ESP+0Ch] - lpReserved
      [ESP+08h] - lpValueName << this is the name of the data that will be retrieve
      [ESP+04h] - hKey
      [ESP+00h] - return EIP

      I guess you already knew that using a breakpoint action DO "D ESP->14;" will show the
      retrieved data in SoftIce's data window after the function return.

      Now, say for example, Regmon (http://www.sysinternals.com) telling us that the program
      read the info from:

      HKEY_CURRENT_USER\Software\Microsoft\Developer\Setup\RegisteredOwner
      HKEY_CURRENT_USER\Software\Microsoft\Developer\Setup\RegisteredOrganization

      Then, we can put a breakpoint in SoftIce such as this:

      BPX RegQueryValueExA IF *(ESP->8) == 'Regi' DO "D ESP->14;"

      And SoftIce will only break when the registry read the ones that start with "Regi". Here is
      the explanation:

      1. You knew what ESP->8 is. Since Windoze passed an LPTSTR (pointer to
      null-terminated string) to RegQueryValueExA, ESP->8 will evaluate to "the address
      pointed to by the content of [ESP+8]". For more information with the operator, see User
      Guide Chapter 8, sub: Operators. Also, see User Guide Chapter 7, sub: Referencing the
      Stack in Conditional Breakpoint.

      2. The SoftIce indirection operator * in *(ESP->8) will retrieve the content. Thus, the
      expression *(ESP->8) will tell SoftIce that we need "the value stored in the address
      pointed to by the content of [ESP+8]".

      3. Now, the expression *(ESP->8) == 'Regi' means that the expression will evaluate to
      TRUE, only if "the value stored in the address pointed to by the content of [ESP+8]" is
      "equal" to "Regi". Why only use four character? Well, the * operator, only return
      DWORD value (32-bit). So, we can only use the first 32-bit which is equal to four
      character. Also, SoftIce will convert 'Regi' to 0x69676552 (it's Regi in little endian format
      ;). So, it's case-sensitive. I probably should note that, there is two equal sign, and it's a
      single quote. The use of two equal sign and single quote is the same as C language.

      Since the * operator return DWORD value, there will be a problem if we want to retrieve
      only a WORD (16-bit) value. We had to use the SoftIce Word() function. You can see
      User Guide Chapter 8, sub Forming Expressions, sub Built-in Functions, for the Word()
      function. But, since most program align values/structures in 4-byte boundary, the use of
      Word() function is quiet rare. The example that will follow is retrieve 16-bit value, yet we
      can safely use it without the Word() function.

      -----

      If you can understand the above breakpoint, you probably began to wonder, "How
      should I write the breakpoint if the program read a key and a name, and the first four
      char is not equal?" Say, for example, it read from:

      HKEY_CURRENT_USER\Software\Applied Insights\AI Explorer\UN
      HKEY_CURRENT_USER\Software\Applied Insights\AI Explorer\SN

      for the username and serial respectively. Issuing another breakpoint in
      RegQueryValueExA won't work (duplicate breakpoint). But, we can add more conditions
      to SoftIce. And we can set our breakpoint like this:

      BPX RegQueryValueExA IF (*(ESP->8) == 'UN') || (*(ESP->8) == 'SN') DO "D ESP->14;"

      The logical OR operator (||, two pipe characters) will make the expression evaluate to
      TRUE if one of the condition (or both) is TRUE. Problem is, when you try to write it in
      SoftIce, you will hit the SoftIce 80 characters command line limitation. Thus, we had to
      create a macro for it (User Guide Chapter 11, sub: Working with Persistent Macros). We
      can declare the macro such as:

      BPX RegQueryValueExA IF (*(ESP->8) == '%1') || (*(ESP->8) == '%2') DO "D ESP->14;"

      And, name it to something like bpregqv. And when we want to use it, we just use:

      BPREGQV UN SN

      We can safely leave the single quote there in the macro. Because the value name will
      always be a string (Unlike the return value which can be an integer).

      ------

      That's all I guess. Hope this can help you.

Rhayader


You are deep inside reverser's page of reverse engineering, choose your way out:


redHow to use our tools


redhomepage red links red anonymity +ORC redstudents' essays redacademy database
redantismut redtools redcocktails redsearch_forms redmail_reverser
redIs reverse engineering illegal?