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