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.
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 );
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
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:
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;"
we hit F12, we return to the caller and the text we entered will sit nicely
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 );
difference is nIDDlgItem, which is the ID of the control to get the text
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;"
all my friends. If you didn't want to type it everytime you want to use
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.
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:
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:
We'll do a quick review for the API first. RegQueryValueEx is declared like this:
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
you already knew that using a breakpoint action DO "D ESP->14;" will show
retrieved data in SoftIce's data window after the function return.
for example, Regmon (http://www.sysinternals.com) telling us that the program
read the info from:
Then, we can put a breakpoint in SoftIce such as this:
BPX RegQueryValueExA IF *(ESP->8) == 'Regi' DO "D ESP->14;"
will only break when the registry read the ones that start with "Regi".
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]".
the expression *(ESP->8) == 'Regi' means that the expression will evaluate
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.
* 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\SN
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;"
OR operator (||, two pipe characters) will make the expression evaluate
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.