Detach
Class
Genint
Mapv86
Eresource
faults
fiber
file and table
flash
fmutex
fobj
gdt
hwnd
I
I3here
I1here
Irb
Irp
Xframe
Zap
Ldt
Msr
Set casesensitive on
Set checkstrings on
Set disassemblyhints
Set exclude
Set faults
Set forcepallete
Set i3here
Set i1here
Set longtypenames
Set reference
Set symbols
Set threaddp
Set typeformat
We run the command hwnd and softice tells us that it cannot find a desktop window. We then set a breakpoint on the MessageBoxA function run the above program a.exe and we fall into softice. Now when we run the hwnd command we get a list of all the windows that have been created by the all the programs running on our system.
For example the getright program is sitting as an icon on our tray and it has created 6 windows. For some reason no program running likes to create a single window. The reason we have switched to this command is so that we can learn how to set breakpoints on windows messages.
SoftIce
We double click on the desktop icon in the tray, in the left pane we click on general and write the following in the text box Initialization String
X;Set Maximize ON;set font 2;
The X is already there, the Maximize on allows us to use the maximum height and width of the window as the default Is way too small.
The above way may work but the purists will not be too happy with it. The X will write out a X on the command line in softice and the semi colon will bring in a enter. Thus each time softice starts we quit out and then the rest of the commands execute. This is why we see the above commands in the command window. The way out is to place the X; at the very end like
Set Maximize ON;set font 2;X;
This will make sure that the above commands run, softice closes and then opens up again and the initializations commands do not display.
The Font options allows us to choose from four fonts and font 2 looks the best for us, your mileage may vary.
The above commands get called at startup and thus here place here all initialization commands.
To move the softice window around, keep Ctrl and Alt pressed and then use the left and right arrows to move the screen around a line at a time. The page up and page down, end and home do not move it much as we use the maximum size.
The table command shows us a list of tables or symbol files loaded.
File * the files that make up the application.
U WinMain takes us to WinMain function in the code window.
Bpx winmain
Set a breakpoint on a function that triggers softice. Double clicking on a function name in the code window also sets a break point or pressing f9
Bd removes all break points set
X will exit out of softice similar to Ctrl D.
Wr toggles the register window
Wc 34 set the size of the code window to 34 lines
Wc +10 increase size of code window by 10, wc –10 reduces size by 10
We first set path to the directory C:\PROGRA~1\COMPUW~1\DRIVER~1\SoftICE where a program nmsym is present. We create a simple C program b.c as
b.c
main()
{
int i;
i = abc(10,20);
printf("%d\n",i);
}
abc(int i ,int j )
{
return 100;
}
cl /Zi b.c
The Zi option adds debugging information.
nmsym b.exe
creates a file b.nms which contains symbols. We execute it by either running it or right mouse button, Load into softice . We skip when it wants some files. Now in softice when we run the tables command we see the table b. File * shows us b.c.
The table command shows us tables or symbol files loaded and if we reboot our computer they go away. Hence we need to run the nms files each time. We run the following commands in softice
Table
File b.c
U main
The last command shows us the main function and the rest of above code. We then write the command
Bpx abc
This sets a breakpoint on the function abc. We then run our program b.exe and we get right into our debugger softice. Here we are at the start of the function abc. Pressing T for Trace lets us execute a line of code. We single step the return 100 and see that the eax register has the value 64. We double click on this register value with our USB mouse and change it to 4. We then ctrl-D out of softice and the program b continues running. The value displayed is 4. F5 is equivalent to Ctrl-D.
We disable all breakpoints by using bd. We then set a breakpoint on main by using the command
Bpx main
We then write the command src. This shows us both C and assembly code Thus the lines of code that the C function call abc becomes is displayed below it. When we use the t command it single steps and enters the code of a function. If we at the call press p instead of T, we do not enter a function we move out of it or step over and not step into. Function key F3 does the same job.
We now run once again and single step into the first line of the function abc. Here we invoke the locals window by writing the command wl.
Wl
This shows us an empty window and when we come to the first line of the code it shows us two variables I and J by name and their positions on the stack using ebp. We are also shown their values. This is how we must debug our code, we can see how are variables change values after every instruction. Move out of the function and the locals window is blank.
abc(int i ,int j )
{
return i;
}
We set a breakpoint on the function abc by double clicking on it in the code window. We then use the mouse to activate the locals window, select the variable I and then press Alt-E to enter editing mode. We can now change the value of variable i say to B press enter and then press Ctrl-D. The value returned is 11. If we have a function like printf whose source code is not know to the debugger, P and T do the same thing.
c.cs
class zzz
{
public static void Main()
{
System.Console.WriteLine("hell");
}
}
csc /debug c.cs
nmsym c.exe
The code window will always display the next instruction to be executed. The other windows are WR Register, hot key ALT R , WL Locals ALT L, Ws Stack Alt S, WW Watch ALT W, WD data ALT D and Wt Thread ALT T. The hot key takes you in and out of the window.
Code on displays the hex opcodes and code off knocks them off. The exp or export command displays all the exports. Exp MessageBoxA tells us that this function come with which module or dll. Exp Message* will display all the functions beginning with Message.
The F12 key or p ret takes you out of a function call and straight out to the ret. F8 or T one instruction, F10 or P one program step.
Bl will list all the breakpoints set. Bc with a number clears the breakpoint, bd will disable it and be will enable it again. You can break on virtually anything, a windows message, an interrupt, IO ports read write, memory read write, execution etc.
b.c
#include <windows.h>
main()
{
__asm mov eax,1
MessageBox(0,"hi","bye",0);
}
We set the breakpoint bpx as
Bpx MessageBoxA if eax == 0
When we run the program b softice does not get called as the eax register is 1 and not 0. We then mov 0 into as eax mov eax, 0 and now when we run the program b softice gets called.
We then run bc * to clear the break point and now try out the do option
Bpx MessageBoxA Do “wc +10”
Now each time a breakpoint come on, the code window becomes 10 lines longer. We can use any macro which can be called when a breakpoint is reached.
The ? allows us to evaluate an expressions as we may nit have a calculator handy. Typing
? 64+1 gives us
<ulong> = 0x65 , 101 , e
We first get a type that tells us what the answer is, then a hex, decimal and a char value. The numbers we type by default are in hex and not decimal.
The . dot command is very useful if we get lost in the code window. As long as the code is displayed pressing a . get the next instruction to be executed highlighted. This is the same instruction that eip is pointing to. Thus the dot command locates the current instruction and highlights it.
The ADDR command with no parameters does what the task manager does. It tells us all the programs that are running on our system. It starts with the pid and the name plus other useful details. The last process is the idle process that is the active process as it has a star asterix in front of it and it is also highlighted. These processes are called contexts within softice.
The first column is called CR3 or the physical address of the page directory that will be placed into the register cr3 when we switch to that process. The second column is blank and it is the LDT of the process. The third column is the address of the Peb that is created for each process. This is a undocumented structure. Then we have the pid and the name.
The program ntvdm is one the few programs that has a ldt associated with it. The context is a very useful feature as whatever we give softice is with respect to a context. Thus we write a simple program that displays a message box.
#include <windows.h>
char *p = "vijay";
char *q = "mukhi";
main()
{
printf("%x %x\n", p , q);
MessageBox(0,p,q,0);
}
This program tells us that the strings p and q are stored at locations 40c000 40c00c. We set a breakpoint on MesageBoxA by using command bpx MessageBoxA. We run the above program and we move into softice. Here we write the command d 40c000. We see vijay first so junk bytes and then Mukhi.
We now change the context to program lsass by writing the command addr lsass. The minute we do this the data window now becomes all question marks as the address 40c000 in lsass makes no sense. This is why a context is so important in softice, whatever we do is with reference to a context. Thus is something does not work as advertised change the context.
We used the ALTKEY command to change the hotkey from CTRL-D to CTRL-X by ALTKEY ctrl x. The problem was that this interfered with the CTRL-X of windows. Thus choose your hotkey with care.
Running this command changes the hot key instantaneously but it is a better idea to place it in the initialization string. The ALTSCR command we did not try out as we can connect two monitors to our computer. We could then display softice in the second monitor. ALTSCR allows us to switch between mono and vga.
We can talk remotely to softice using a modem and the there are a series of commands like answer that make it happen. We will focus on running softice on a single machine only. The apc command allows us to see all the Asynchronous Procedure calls that are current. We get none when we run the command. .
Attach do later
The bc command accepts breakpoint index separated by spaces or commas. Bc 1 4 6 will clear the breakpoints 1 4 and 6. Bc * clears all breakpoints. bd follows same syntax as bc but will disable for the moment the breakpoint. Be will enable and all disabled breakpoints will be marked with a asterix.
BH will show us a history of the last 32 breakpoints that we have used. We can scroll up and down this list, pressing insert once will select, insert again will deselect. The enter key will add all the breakpoints selected to the breakpoint list and ESC will exit out like enter but without adding the breakpoints to bl. The breakpoints are saved to the file C:\winnt\system32\drivers\inice.brk.
Bl will list all breakpoints starting with the breakpoint index number. A star means the breakpoint is disabled. This is followed by the type of breakpoint used bpx, bpload etc. Depending upon the type of breakpoint we will see further data.
By default and the –x options shows us the name of the module and the name of the function separated by a !. The –a option converts the same into an actual address of where the function is in the module. This is how we know that the MessageBoxA function starts at 77e13d81.
We first create a breakpoint on MesageBoxA as
Bpx MessageBoxA if eax == 0
We now decide to change the register from eax to ebx. Earlier we would first remove the break point and then add a fresh one. Using bpe 0 allows us to edit the breakpoint. We see the breakpoint 0 on our command line with the cursor at the end. We use the normal cursor keys to edit and pressing enter saves, escape undoes.
This command first clears the earlier breakpoint and then sets a new one. If we press ESC then we are left with no breakpoint at all.
main()
{
__asm int 9
}
In the above program we are calling interrupt 9. We first use the bpint 9 command to set an breakpoint on int 9. We use the int command in assembler to invoke interrupt 9. We move into softice but our command line says that we have made a status access violation. When we press keys on our keyboard an int 9 does not get generated.
Bpint works only for those interrupts that are enabled though the IDT. Thus the mistake we made was that the ntvdm first captures the software interrupt. Control is then passed to the windows protected mode interrupt handler. To prevent software clashes windows maps interrupt 0 to interrupt 30, int 9 to interrupt 39.
Thus we now write the command as bpint 39 and after pressing enter, pressing any key activates the breakpoint. Pressing CTRL-D activates the breakpoint and hence we cannot quit out of softice. We used to use interrupts heavily under dos not under windows.
We have bpload to break whenever we ran our program. This helped us to set the breakpoints on our programs context. This breakpoint by default happened whenever we loaded our program. Lets now set the breakpoint when we unload our program. Bpload a U is what we write and now when we run the program nothing happens. We thought that when the module unloads a breakpoint will be called. In our case nothing happens.
We then run bc * to clear the breakpoints and then we run bpload a B which means trigger the breakpoint on both load and unload. For some reason the breakpoint only gets called for load and not unload. We can use wildcards in the module name. We do not use file extensions because even if we do softice first removes them and then does the checks.
Writing bpload a L if BPLOG will now not set the breakpoint at all but will log all the breakpoints. We run the program a 4 times and softice does not get called. When we CTRL-D to softice we see the logging keyword followed by the bpload keyword. This we do not have to break into softice always, we can log the breakpoints and see the breakpoints later.
a.c
#include <windows.h>
main()
{
MessageBox(0,"p","q",0);
MessageBox(0,"p","q",0);
MessageBox(0,"p","q",0);
}
We always start by clearing all the breakpoints in softice using command bc *. We then set a breakpoint on the messageboxa function. When we run program a, we move into softice before the messagebox can be displayed. In softice we write ? pid and we are shown a value of 1784. Your mileage will vary and even if we run the program the second time, we will get a different answer.
We press CTRL-Alt_Del to get at the task manager that confirms that process id a has a pid of 1784. We need to be careful here because otherwise we will see the pid as zero if we are in the idle process context. We run a.exe again, move into softice, ? pid tells us our pid and now we write the command as
Bpx.p messageboxa
This .p tells the breakpoint to fire only if the pid of the process is 2232 which is the pid of a. We press CTRL-d and as we are in the same pid the breakpoint gets fired, we now run a.exe again by starting a fresh dos box bearing in mind that we already have a copy of a running. We have yet not clicked on the second message box. As we thought no softice popped up on the invocation of the message boxes.
We clicked on the messagebox waiting for us and now we get back to softice. Here we type bl and get the following line bpx Messageboxa if (( pid == 8b8)). Thus using the .p command allows us to set breakpoints for a certain process only. This isolates softice from popping up if another program somewhere also calls a function. Most breakpoints have a .p addition.
Running a.exe does not trigger softice as it may never ever get a pid of 2232. The .t specifies a thread and in our case tid and pid have the same values. If we use bpx.t the breakpoint uses tid instead of pid in the if statement. The .t triggers on the active thread, the .p on the process.
Another way of setting a breakpoint is by pressing ALT-C, move the cursor to the line and then write bpx. Doing this once sets the breakpoint, twice removes it. Use bl to see the breakpoint set. This is called the point and shoot method. If the cursor is in the code window, we do not have to specify a address for the bpx command. The breakpoint command works because softice replace the first byte with a CC or int 3.
If the address is in rom we have a problem as we cannot change rom code, so softice uses a breakpoint register. Any address that w specify below 2gb is context sensitive, any address larger is global. Thus for addresses below 2gb make sure that we have the correct context or the breakpoint will not trigger.
Lots of times we want to set the breakpoint 15 bytes from where we are, we would write bpx eip+15.
One useful command is the bstat that gives us statistics on how many times our breakpoints got called. This gives us details on breakpoint hits and misses and also whether softice popped up or the breakpoint was logged in the command window. As mentioned before if the BPLOG macro is used in the if statement, the breakpoint gets logged.
The if statement or conditional expression is evaluated just before the breakpoint is triggered, if we are using a variable that is not in the context of where the breakpoint is triggered, we will get an error. The total section gives us the Hits, breaks, popups, logged, misses and errors. Hits is the number of times that softice has evaluated this breakpoint.
Breaks is the number of times the hits have evaluated to true, softice will now popup or log the breakpoint. Popups is the number of times softice poped up when the hit and thus the break was true. Logged is when softice did not popup but was logged. Logged and popup equal to breaks.
Misses is the number of hits where the if condition has evaluated to false. The errors is when the if statement at runtime gave us an error. Thus breaks and misses and errors equal to hits as the breakpoint will either be true or false or an error will result.
The current and Misc in our case gives us no results.
The command bpt 1 will allow us to use the breakpoint 1 as a template for creating a new breakpoint. This is a way of reusing a complex breakpoint. The original breakpoint is not touched at all and a new one is created. This is unlike editing a new breakpoint. The same cursor keys can be used to edit the old breakpoint.
The bpm command allows us to set a breakpoint on a memory location. This can be reading or writing or executing memory. The best way of explaining this is by writing our own code.
a.c
#include <windows.h>
char *p = "hi";
main()
{
printf("%x\n", p );
MessageBox(0,p,"bye",0);
}
In the above program we refer to the string p twice once in the printf and the second time in the messagebox function. We first run the program a.exe and the printf tells us that the string p starts at 40c000. We then set a breakpoint in softice bpload a. Now when we run the program a we land up in softice.
We now set a breakpoint bpm 40c000. We press CTRL-D and now we land up again in softice at the memory breakpoint. Thus each time we read and write 40c000 a memory breakpoint gets called. For some reason for our tiny program it gets called thrice instead of twice as we expected.
We let our program run., get into softice and now run the command bl. It says bpmb 0003:00000000 RW DR0. DR0 is the debug register, RW means read and write the default. This is because the context has not been set for the bpm command. We now run the a.exe again we fall into softice and the breakpoint now reads bpmb 0023:0040c000 RW DR0.
When we want to reuse an existing breakpoint press b and then up arrow will show us the last command we used beginning with b. Lots of times we want to start executing from main avoiding the startup code. In our case main starts from 401000. We first set up a bpload a. We then run a.exe and move into softice. We now run the command bpm 401000 X. The X means when we execute something at location 401000.
When we now CTRL-D, we stop at function main. After executing a we once again move into softice and now do a bl, the actual location 401000 does not show up as the contexts are different. Whenever we specify a memory location we also specify a context within which that memory location has meaning. The .t and .[ have meaning here for the context.
Normally we use BPM when we could have used BPMB for byte, BPMW for word and BPMD for double word. If we end with a D, then we are monitoring access of four bytes and not 1 which is the default. There are four debug registers DR0 to DR3. The documentation says that the debug registers starting from dr3 and moving backwards are used. In our case it starts using from DR0 and upwards.
For a R and W, softice first executes our code and then pops up, for a X it does not execute the code and pops up first. A RW will not execute if we execute code at that location. If we use W or D as a size prefix then we must also specify a address that begins at a word or double word boundary. Thus specifying bpmd 401001 gives us an error as the address is not on a double word boundary. For the same reason bpmd 401002 also gives us an error as it starts on a word and not a double boundary.
Bpmw 401002 gives us no error as it starts on a word boundary.
a.c
#include <windows.h>
int i;
main()
{
printf("%x\n",&i);
printf("A\n");
i = 4;
printf("B\n");
printf("%d\n",i);
printf("C\n");
}
when we first run the above program it tells us that the global variable I starts at 40d7d0. We then set a breakpoint on module a by issuing a command bpload a. We then run the program a and fall into softice. We now issue another breakpoint bpmd 40d7d0 W. We press CTRL-D and now fall back into softice. The printf a is displayed as the minute we execute the line i = 4 the breakpoint gets triggered. Pressing CTRL-D executes the rest of the code.
We then bc *, bpload a and fall back into softice and issue a breakpoint bpmd 40d7d0 R. Now when we CTRL-D both A and B get displayed as the breakpoint comes in the minute we execute the printf which is after the printf’s of A and B. This is how we can trigger a breakpoint on a read or write. Displaying the address of a variable does not trigger a breakpoint, so the first printf has no effect.
We once again write bc *, bpload a, run a , go to softice. Here we write the following command ? BPCOUNT and we get an error saying that this macro cannot be used here. We once again bc *, now write the following, bpload a if bpcount >=3 . Now when we run the program a twice no breakpoint is set. When we run a.exe the third time, softice pos up.
The variable bpcount keeps track of how many times a breakpoint has occurred and in our case softice will activate the breakpoint after the third time we run a.exe. The truth is that every third time we run a.exe softice pops up. This is because each time the breakpoint is set, the macro bpcount is set to zero.
The macro bptotal works the way we want, it keeps a running total of number of times our breakpoint has been called. Thus the first two times nothing happens, from then on running a activates our breakpoint. BPLOG and BPINDEX work as advertised. If we now write the breakpoint as bpload a if bpmiss >= 3, the first three times nothing happens, from then on the breakpoint gets called. Thus we get a breakpoint every four times we run the program a.exe. A miss happens whenever the breakpoint rule is false. The minute it fails three times, bpmiss is true but after the third failed attempt.
a.c
#include <windows.h>
char *p = "ABCD";
char *q = "ABCDE";
main()
{
printf("%x %x\n", p , q);
MessageBox(0,p,q,0);
}
When we run the above program we are told that the first string begins at 40c000 and the second at 40c00c. We set a breakpoint bpload a and then run program a, we land up in softice and issue the following breakpoint bpx 401000. We then press CTRL-D and move back to softice where we are at the start of our function main. We then run the command dd 40c000 and we see our two strings abcd and abcde.
Our strings get allocated memory before the first line of code of main can be called. We now want to compare two blocks of memory, the command c 40c000 L 4 40c00c will check length 4 bytes of memory areas 40c000 and 40c00c. If there is a mismatch it will display them. In our case as the first four bytes of our two strings are abcd, we get a blank.
We now change the above command as c 40c000 L 5 40c00c and now we get an output that tells us where the mismatch occurs. It first displays 40c004 00 40c010 45. This is because 45 is ascii of E and our first string terminates at a NULL. If we run the above in softice without our context being a.exe, we may get a page not present message.
The command cls does what it has been doing for years under dos, clears all the lines in our command window. This is great as we as itself have a small command window and we need all the empty space we can get.
The code command decides whether the op codes of every assembler instruction bytes will be displayed or not. code on will display the hex bytes , code off will put them off the default and code by itself will simply gives us the status of the code parameter. We like the code parameter to be on always.
The color command lets us choose the actual color used to display. Each time we use the color command we give as many as five colors. They are normal, bold, reverse, help and line. Thus we write the color command as
Color blue+white green+gray yellow+black red+lightgreen brown+gray
The first color is the foreground color the color after the plus is the background color. The first color is the color of normal text, the second bold text , the third text that is reverse, the fourth any help displayed at the bottom and line is the color of the horizontal line dividing softice windows.
We can specify the same colors as hex numbers also but who is going to remember these values. The background color is optional and the current background color will be used. Hex colors are used as 72 foreground color 7 background color 2. The best option is color reset which brings us back to the dull default colors.
The cpu command shows us tons of stuff of our cpu. It starts with a repeat of what our register window shows us but we have all the control registers and debug registers. It shows us at the end the features of our cpu. The –I option displays the I/O APIC or Advanced Program Interrupt Controller which our portable does not use.
The csip command is used for breakpoints and is used for 16 bit code and hence obsolete for us.
When we run a.exe it tells us that the first string starts at 400c00. We set a breakpoint for bpload a, run a.exe, set bpx 401000, CTRL-D , in softice we now run the d command to display memory. D 40c000 will display 8 lines of 16 bytes each. D 40c000 l 4 should display 4 bytes but softice displays one line minimum.
Writing the command as d or db gives us the same answer, as the size by default is the last size used which for us is byte. Using dw gives u two memory locations at a time, dd gives us 4 at a time. The display changes depending upon what the size is. S is short real or 32 bits, l is long real 64 bits and T is a 10 byte real.
If a data window is already there, d uses the data window, if a data window is not there, it will not create one but display in the command window. If we do not give an address the data command displays the next bytes after the last bytes, Thus by pressing d we can repeatedly scan through memory. If we do not have a data window, the same display is shown in the command window.
Using the –p option allows us to specify a physical address the default being a virtual address. Thus d –p 40c000 by following the above procedure gives us all junk as a physical address is not what we windows programmers work with.
The data command allows us to open up to 4 data windows. We write the data command 4 times and we see 4 data windows. Data 3 moves us to the third data window. Once we have activated a data window we can use the d command to display an area of memory that we want. The command wd.0, wd.1 etc will toggle the windows on and off.
The device command shows us a list of device drivers running on our system. Softice in that sense gives you everything you wanted to know about windows but were afraid to ask. In the long list displayed one of the devices was udp, the command device udp gives us more information about this device. This information is obtained from the DEVICE_OBJECT structure that is maintained for each device by windows.
The first column is our device objects reference count which for the udp device is15. The second is a handle to the DRIVER_OBJECT which owns this device. These device drivers form a linked list and the next drivers driver object structures handle. The next three have a value of 0 and the last is the name of our driver.
The devnode command by itself displays information about the root device note. One of these fields is the child node. If we now run devnode –c and specify this address of the child node we will get a list of children. This is how we can get lots of information about device nodes as we have half a dozen options to this command.
The dex command attaches a expression to a data window. We first run wd to display a data window, then dex 0 esp will display the contents of the esp register in the data window. This is how we can keep track say of the stack each time soft ice pops up. 0 is the data window and we can use any expression with the dex command. Dex 0 will remove the expression associated with a data window.
Typing dex with no parameters shows us all the data window indexes and the expressions associated with them. We could also use the name of variable within the expressions.
The dial command redirects the console to a modem. We have not tries these commands.
The dpc command displays the differed procedure calls and in our case the queue is empty.
A device driver creates devices which we displayed earlier. The command driver will display all the device drivers running on our computer. One of them is vmm. The command driver vmm will show us more details about this driver. The tools with the device driver kit displays driver and device information.
We set a breakpoint bpload a, run a, in softice we write d 40c000 to see the string abcd. We then run the edit command e 40c000 ‘vij’,0. We now press CTRL-D and the messagebox now shows us vij. Thus we can either use the e command to change data or move into the data window and make the changes there as explained before.
The e command takes the same size parameter as the d command. If we do not specify a address with the e command, e 40c000 it opens a data window if not there, if there it displays the contents of the memory 40c000 and allows us to edit. The tab key takes us into the ascii side where we can write actual characters.
The ec or F6 toggles the cursor between the code window and the command window. If the code window is not present it gets created. In a code window, we can set breakpoints using point and shoot. The bpx command needs no address. We can move the cursor to a code position and typing here or f7 will execute to this point. The up and down arrows move us a line at a time, pgup and pgdown a screen full at a time.
The event command displays bound checker events a product we have not installed. Commands beginning with ev will not be covered here.
The exp command with no parameters gives us all the functions or symbols which are exported by all the modules that softice has loaded. These include kernel32.dll, gdi32.dll etc. Writing exp kernel32!* will list out all functions from the dll kernel32.dll. Writing user32!m* will show us those functions beginning with m. We can use exp on modules that we have created and loaded the nms file into softice.
The exp command does what dumpbin /exports does with windows. The command exp ! shows us all the export tables that softice has loaded. We see six of them. These are kernel32, user32, gdi32, hal , ntoskrnl and msvbvm50 that is the visual basic runtime that we added in the configuration ourselves.
The * matches anything and the comma matches a single character for wildcard matches in the symbol name. We can use very complex regular expressions but we must enclose them in slash characters. We can use regular expressions both for module name as well as symbol names. The basic regular expressions like ^ standing for the beginning of the line apply here also.
Lots of times we want to fill memory with some repeatable value. In the above a.exe our string begins at 40c000. We set the two breakpoints as before bpload a and bpx 401000. We then use the d 40c000 to see the string abcd. We then use the fill memory command f as f 40c000 l 5 ‘ab’. Now when we run the d 40c000 command again we see the memory as ababa. Pressing CTRL-D shows us the message box with the same string.
Thus the fill command starts at memory in our case 40c000 and fills 5 bytes of it for us. The string ab is 2 bytes long and hence it is repeated two and a half times.
The command fkey by itself will show us all the strings that will get executed whenever we press that function key. We have two windows on our screen code and register. Writing the command fkey f1 wc;wr; will now toggle the code and register window each time we press the f1 key. We see one huge screen. The semicolon stands for a enter. The commands are repeated on the screen for us.
If we preface a function key with a S it becomes Shift and the function key SF1, CF1 is control and AF1 is ALT. Writing the command as fkey f1 ^wc;wr; will hide the wc command for us and only show us the wr command. Thus place a caret over every command you would like to make invisible to the user. The same can be done using the keyboard mappings in the configuration.
Ending the command with a + does not end the command but displays the + so that the user can complete the command. Fkey f1 wc;wr;d+.
We create a data window by the command wd. By default the window at the top says byte if this is the last format used. Running the format command will change the format of the display to word, then dword, then short real, then long real and finally 10 bytes real and this order will be cyclical.
The g command makes our life much simpler. We first bpload a, run a and now move into softice. Here we want to bypass the startup code and jump to main. All that we do is not set a breakpoint that we did earlier but run the command g 401000. Softice executes all our code to this address we stop here. Hence no more bpx 401000 if we want to do things only once.
We then again run the command g 40101a and we execute the printf and see our dos box with the value 40c000. The g command is useful for us to jump to a certain code address and execute all code till there. This is like placing a one time breakpoint on our code. If we start an address with a equal to sign execution takes from here.
a.c
#include <windows.h>
char *p = "ABCD";
char *q = "ABCDE";
main()
{
p = "hi";
q = "bye";
MessageBox(0,p,q,0);
}
In the above program we have made only a small change in the code. We actually change the strings p and q. We first bpload a, run a, in softice we g 401000. Now when we see the code we realize that at code location 40100d we are placing string bye in memory and before this the string hi. We run the command g =40100d 40102e. The code location 40102e is the line after the call of the message box. As we are bypassing the line that sets variable p to hi we will see in the messagebox bye and abcd and not bye and hi. This is how we can ask softice to execute code from one location to another. Very useful command in certain situations when we do not want some code to be executed.
The one time breakpoint is one time, softice clears it each time it starts again. The g command by itself quits us out of softice like X and CTRL-D does.
#include <windows.h>
main()
{
char *p,*q;
p = malloc(10);
q = malloc(20);
printf("%x %x\n", p , q);
}
2f2e48 2f2e60
The malloc function allocates memory for us on the heap. The memory locations allocated for us are 2f2e48 and 2f2e60. We now set a breakpoint on bpload a and then run a.exe. In softice we now run the command heap32. This gives us a list of programs running along with the memory allocated by them on the heap.
We see dozens of programs and their multiple allocations but as our program has not yet run, we see no mention of program a. We run program a once again and in softice jump to the start of our function main using the go command as g 401000. Now when run the command heap32 we see three entries at the bottom of the list for program a. A better way of running heap32 is by writing the command heap32 a which will give us only the three entries for program a.
We now run the command heap32 –w a.exe which walks the heap showing us each of the entries. For the third heap entry we check for the two memory handles 2f2e48 and 2f2e60. We see the first but there is a word free next to it. We then see the source code that tells us that we are calling the function malloc whose address is stored at 401145.
This instruction starts at 401008 and the next instruction is at 40100d. We use the command g 40100d to execute code till the first malloc function. When we now run the command heap32 –w a, we scroll down to the third heap entry where we see the line 002f2e48 alloc a 01. The a is the size of memory allocated and the free changes to alloc. There is no entry for the next malloc at 2f2e60.
We now go using the command g 40101a and now the command heap32 –w a shows us one entry, 002f2e60 alloc 14 01 as the size of this memory is 20 bytes. This is how we can see the memory being allocated on the heap for each and every process. Running the a.exe file again, command g 401000 in softice and the command heap32 –x a gives us a lot of other extended information about our heap that we do not understand like a Virtually Allocated Block VAB and a UnCommitted Range table UCR..
We do the same as above but now run the command heap32 –s a gives us a summary of our segments. Segments are created so that we can map the linear address space for a region. A heap can be made up of a maximum of 16 segments.
Using –v instead of –s validates the heap for our program and the –b option allows us to change the mode in which addresses and heap sizes display. Our third heap buffer starts at 2f0000. Thus writing heap32 –w 002f0000 walks us through a single heap handle. To use the –trace option we must set a flag or else we cannot trace into a buffer.
When we run heap32 a the first column which is 002f0000 is the base address of the heap, it is also called the heap handle. The second column is the heap id and it starts from 1 and increases by 1 for each heap entry. The third column is the amount of committed present and reserved memory used for heap entries. We see 4/4/c as our value.
The fourth column is the number of segments within the heap and we have only one segments in all three heaps. The fifth column is flags where a value of 2 tells us that the heap is grow able. The last column is the name of the program that created the heap a.exe in our case.
The –w switch gives us some more data. The first column is the heap entry base address or handle. The second can be one of 5 values, heap if represents a heap header, segment a heap segment, alloc a active heap entry which we get when we use malloc, free inactive heap entry and varblock a virtually allocated block vab. The next column is the size of the heap entry, the value we pass as a parameter to malloc and then the segment number followed by flags which are normally null.
The h command is the help command and is mapped to the f1 key. Command H by itself gives us a list of all commands and command h heap32 gives us a small help on the command heap32 with the syntax and a actual example.
The hboot command allows us to do what ctrl-Alt-del does, a soft reset. Why would someone carry out this command from softice is anyones guess.
The filter command creates and displays filters for us. As we have never used a filter before we get nothing when we run the command filter. We now write our first filter command filter ‘breakpoint’ red+lightblue. All that happens is that our command line becomes red on a lightblue border. We then run the command h for help and now all those help commands that have the word breakpoint in them have changed color.
Thus the filter command allows us to change the appearance of text that match a filter to a different color. This enables us to find something more quickly. Now when we write filter we see one entry with an index 0. Running the command filter –d 0 will remove the filter we have set. These filters are called display filters and therefore do not act on other windows like the code and register window.
We use the filter to color a line in the history window and also use the do command to execute some lines of code if a match is found. We could use a regular expression instead of a simple string match. The history of softice is comprised of what we type plus any output of the DbgPrint function output that we use in device driver. We cannot use wildcards if we are using a string match. For some reason the dbgprint output is not displayed within softice.
We clear all breakpoints, set bpload a, run program a, g 401000. Now in the code window we would like softice to execute say the next 5 instructions and then we want to single step. We press ALT-C to move into the cod window, then the up and down arrows to move to the line we want to execute and then we choose the line by pressing enter.
Now we can either use the command here or press key f7 to tell softice to run all code from where it is to this point. It is faster than setting a breakpoint but it allows us to execute code till a point. We need to press enter on the code line or else we will get a syntax error. The here command is only available if the cursor is active in the code window.
The here command sets up a one time execution breakpoint. This breakpoint is cleared once we reach this code line.
The command hs which allows us to search the history buffer is behaving in a very erratic manner. Will come back to it a little later. Hs ‘bpload’ should show us the last time we ran this command. The whole screen that we saw when we ran this command is sometimes seen.
The command idt displays the 255 interrupts that make up the interrupt descriptor table. It gets this information from reading the idt register of our microprocessor. The first column is the interrupt number, a value from 0 to ff, the second the interrupt type which can be a 16bit or 32 bit call gate callg32, 16 or 42 bit task gate taskg32, 16 or 32 bit interrupt gate intg32. We have a mixture of the above.
The next is the selector:offset or address of the interrupt handler. This is followed by the selectors descriptors privilege which can be 0 1 2 or 3. This is followed by the present or not present p or np and finally the owner name and the offset from this name. Most of the interrupts fall into the ntoskrnl. We could also use idt 8 to give us details for a specific interrupt.
The intobj command shows us information about the system interrupt objects present on our system. We get at least a dozen of them. If we write intobj 853a5408 we will get some more information about the interrupt object that starts at the above address. This address was given to us by the first column of the intobj command.
Ioctl 40000 gives us description of the ioctl code 40000. These ioctl codes are defined in the header file winioctl.h that comes along with the ddk.
The lines command by itself gives us the number of lines that softice uses. In our case it is 51 as we have set maximize on. When maximize is on we cannot use the lines command as maximize uses the maximum possible value of lines which is 51. We then set maximize off using command set maximize off and now write lines 128 the maximum value used.
Softice rejects this value as it tells us that the screen would otherwise scroll off. We cannot use any values for lines and are told by the help to use 25, 43 50 and 60. Using 60 puts us back to 51 lines. As our portable does not support a vga driver we cannot use the larger values of the lines command which basically allow us to use more of the screen when softice pops up. The current font also decides how many lines softice will display.
a.c
#include <windows.h>
char *p = "ABCD";
char *q = "VWXYZ";
main()
{
printf("%x %x\n", p , q);
MessageBox(0,p,q,0);
}
When we run the above program the string p starts from 40c000. We set a breakpoint on bpload a , run a , run up to g 401000. We then run the command m 40c000 l 4 40c000c, this will move or better still copy length four bytes from 40c000 to destination 40c000c. Thus pressing CTRL-D will show us a message box with title abcdz and not vwxyz.
We now set a breakpoint on bpx messageboxa and run our program a.exe. When we move into softice we run the map32 command. This tells us every module loaded on our system along with each section that this module contain. A.exe contains 3 sections .text. .rdata, .data. The first column is the module name, the second the names of the section, the third the section numbers, the selector:offset address or address of the section in memory, this is followed by the size of the section, its type code or code, idata for initialized data and udata for data that us un-initialized. The last bit of information is whether the section is Read Only RO, RW for Read Write or Shared for shared.
Map32 a gives us the information of a single module a. Map32 –s gives us all those modules in system space which include all device drivers also whereas map32 –u displays those 32 modules in user space. Drivers under windows are always loaded above 2g whereas applications and dll’s below 2gb.
We have already set two breakpoints bpload a and bpx messagebox. Thus when we run a once again eip is pointing to ntoskrnl. Now if we write map32 eip, we get all the sections of ntoskrnl. We now press CTRL-D move back into softice and now eip points to user32. Running he command map32 eip now gives us the sections of user32.dll. Pressing f12 executes the messagebox and we now land up in the code of a.exe. Running map32 eip gives us the sections of a.exe.
The ver command gives us 6 lines of our softice version number. Useful to check which version you are using. In our case it is 4.3.2.
The wc command toggles the code window on and off. If we specify wc 29 it will not toggle the window but set it to size 29 lines. If the code window is closed, it will open to be 29 lines long. When ever we close the code window the lucky window to get its lines is the command window. When we open a code window, the lines are given to the code window from the command window first and then the data window.
The wc –o open will always open the code window irrespective whether it is open or closed. The wc –c option will always close the code window. These option are useful in macros as the wc is a toggle switch. Thus we are sure when we use the –o and –c switches what the final state of the code window is. F6 or the ec command toggles us from the code window to the command window. If the code window is closed and we press f6 it open and the cursor is positioned in the code window.
The wd.0, wd.1, wd.2 wd.3 commands work with the 4 data windows that we can create. This wd command works like the wc command but as we can have 4 data windows, we use the window number along with the wd command. The same –o and –c switches work for all the w commands. If we do not use the window number writing wd is like writing wd.0. When we open a data window, the lines are taken in from the command and the code window.
The e command moves us into the data window and out.
The wf command toggles the floating point register window on and off and display the values of the 8 registers st0 to st7. We do not understand these registers or what they do. We can change the format that this window displays by using some option unique to the wf command.
The width command is analogous to the lines command to change width of the window softice displays itself in. The width by itself gives us a value of 86, to change it we set maximize off and it cannot be set to more than 86 characters. The documentation tells us that it can go up to a value of 160. Its range is 80 to 160 anything smaller will not do.
The winerror command takes a number and converts it into the equivalent win32 macro which is more readable. For example winerror 2 displays ERROR_FILE_NOT_FOUND. It picks up these values from a header file. Useful when we call a windows function which returns a error value and we want a more English description.
The wl toggles the locals window and it takes its lines from the command and code window. All else remains the same.
All windows message have a number and it is impossible to remember these numbers. For example if we want to find the number of the message WM_LBUTTONDOWN all that we do is use the command wmsg wm_l*. This give us four message that begin with l and the third one tells us the value of lbuttondown is 201. Do the reverse wmsg 205 tells us that the message number is owned by rbuttonup.
The wr command toggles the register window and it takes its lines from the command and code window. The floating point registers window is also toggled by the wr command.
The ws command toggles the call stack window.
a.c
#include <windows.h>
main()
{
abc();
}
abc()
{
pqr();
}
pqr()
{
xyz();
}
xyz()
{
MessageBox(0,"p","q",0);
}
We set a breakpoint bpx MessageBoxA and then run a. As our call stack window is open, we will first the MessageBoxa function followed by at least three entries specifying the program a. As we have not compiled with debugger info, no functions names like abc, pqr are displayed. The call stack tells us where we are.
A another way is by placing the messagebox function in main and then stepping though our code and seeing the call stack fill itself. The keys Alt-S takes us in and out of the call stack.
The wt command toggles the thread window on and off and the hot key is ALT-W. We see a single thread in the window with a star to tell us that this is the currently running thread. Using the ADDR command we can switch the context to a another program and see this window change. As we writing this explanation in word we use the command addr winword. This shows all the threads that the application winword has created.
In our case winword creates five threads. When we press ALT-T to move into the thread window and press enter to select a thread, the stack, code and locals window changes to reflect the thread chosen. A S is displayed in front of the thread we select. All else remains the same.
The ww command toggles the watch window for us which gets its lines from command, code and data. The hot key is ALT-F4.
The wx command toggles the xmm register window that shows the xmm0 to xmm7 registers found on the Pentium iii and later. We do not understand what these registers are upto and can change the format of the display.
The x command is faster than the CTRL-D to quit us out of softice. This is the g command with no parameters.
a.cs
class zzz
{
public static void Main()
{
}
}
The smallest c# program when we run and put a breakpoint bpload a, and then run the command xframe we see three structured exception handlers. Running a C program also show us the same output. In a idle process the command shows us no try catch finally clauses.
Kevent displays kernel events. Create one on our display driver.
Kmutex shows us all the mutexes created. Create one in our device driver.
Ksem shows us all the semaphores created. Create one in our display driver.
a.c
#include <windows.h>
main()
{
MessageBox(0,"hi","bye",0);
}
We set two breakpoints like always, bpload a, bpx messageboxa. We then run program a and in softice run the command mod. We see a list of all modules that includes dll’s, drivers and programs that we have run like a. The first column is a module handle that windows gives each program that it runs.
For a.exe this value is the first parameter is the handle which is null, the second parameter is the base a value passed to winmain 400000 or where our program is loaded in memory. This way we can find out where our other dll’s like kernel32 or user32 or ntdll are loaded in memory. The third is the address of the pe header in memory which in our case is 4000e0. The third is the module name and the last the complete path name with no C: but a /.
The net command allows us to do remote debugging using tcp-ip. Will do later
Ntstatus 0x5e example from the documentation does not work.
The command opinfo call, opinfo test gives us some help on the assembly language instruction. It starts off with a brief help and syntax and more important the flags that the instruction affects. If there is a M below the flag, the instruction modifies that flag. The test instruction modifies the S Z and F flags.
A O means that the flag is reset, test resets flags of and CF. A – means that the effect it has on the flag is undefined AF is the case for test. A T means that the instruction tests the flag, , 1 means it sets the flag, R instruction restores the old value of flag finally blank no effect like the case of call, it effects no flags at all. An extremely useful summary of every assembler instruction.
The P command hot key is f10 and it executes one logical program step. It executes only one line of assembly instruction with four exceptions call, loop, interrupt and repeated string instruction. In all these cases it executes more than one line of code. It finishes executing the function and then comes to the next line of code.
For a loop it executes all the code and then comes out. In source mode one line of source code is executed. Normally for a single step any register changed is highlighted. If we are on a call, then the registers not preserved are highlighted. Do p ret later.
The pause command by itself will display the status of the pause command. In case it is on. That is why we had to pause at the end of each page. We run the command pause off and now when we run the mod command, there is no pause at the end of every screen.
The r command allows us to edit the values of our registers. R by itself places the cursor in the registers window. ALT-R does the same. R ebx positions the cursor on the ebx register so that we can change its value. R eax=10 changes the eax register to 10. r fl=+o turns on the o flag r fl=-o turns it off. Each time we run r fl=o toggles the state of the o flag. In the same vein r fl=+o-c turns on the o flag and turns off the c flag.
The flags are o overflow, d direction, I interrupt, s sign, z zero, a auxiliary carry flag, p parity and c carry flag. The registers that can be changes are al, ah ax, eax etc.
The rs command shows us the windows screen or program screen that we were in before softice poped up. Pressing any key takes us back into soft ice. This is useful when our screen keeps changing and we want to take another look at what our application is visually upto. The hot key is f4.
The irq command display the hardware irq’s on our system. The first column is the hardware irq, the second the vector it is being assigned to and the third status information on whether it is masked or not. On our machine as we have a 8259 Programmable Interrupt Controller or PIC we see these three column. On a APIC system, we see more data.
This vector field is an index into the IDT or interrupt descriptor table. The idt command of softice can locate the actual code that will be called at these hardware interrupts. The status field tells us whether the interrupt is masked at the interrupt controller or not. Lets take an actual example.
We first run the usb command that tells us that we have 4 usb controllers on our portable. The first usb entry has a device id 1d and function number 0. We also know that this is a PCI device bus 0. We now run the pci command as pci 0 1d 0 This gives us lots of data on our serial bus controller and it also tells us that the hardware interrupt used is 0x0b.
We run the irq command irq 0x0b which tells us that the vector used is 3b. The command idt 3b gives us the address of this interrupt service routine isr at 0008:85700dc4.
The command lheap works on 16 bit software and not 32 bit.
The command pci gives us tons of information about the zillion pci devices on our computer. It dumps all the registers of all pci devices. Pci –terse gives us one line per pci device. The pci command used earlier had the bus, device and function number in that order. Pci –extended gives us the 256 bytes that make up the functions pci space.
The proc function gives us a list like task manger of all programs running on our system. Proc winword tells us all about Microsoft Word. The first column is the program name, the second the address of the kpeb the kernel process environment block, the pid of the program in hex or process id,
the number of threads this program has created for winword it is 5, the base priority of the process winword has 8, the amount of time this program has spend in executing code in ring 3, if we start program a this gives us a time of 2, the next is the amount of time spend executing code in the kernel level and finally the status that can be running program is currently running, ready - ready to run in the future, idle program is now idle, swapped the process is inactive and its address space has been deleted, transition between states and finally terminating process is terminating .
the command proc –x winword gives us more information on each thread that winword creates. The meaning of all this extended information is useful it understand windows better. However the actual meaning of these fields cannot be found out using the softice documentation. Proc -o winword gives us a list of objects in the process handle table of the program winword. This include the object pointer, handle and the object type. Winword has 223 entries in its process handle table.
This object information is allocated from the systems pageable memory pool an dif the page is not present in memory the name of the object will not be know and hence we will see ??? for the type. Common types are thread, event, file etc. Proc –m winword shows us the memory usage of our program. The –x option includes the –m also.
These values are got from os internal data structures not all are documented. The process that is highlighted is the active process before softice poped up. The process with a star is the one whose context is the currently active context for softice.
The command query winword shows us the virtual address space of winword which is a long listing. We also run our program a.exe and in softice write the command query a. What we see is that both winword and a map the dll user32 from locations 77e10000 to 77e70000. Thus the query command allows us to see how our program is seeing memory. This also shows us what dll are mapped to what portions of our memory.
The program a for example shows us that at location 400000 we see that the program a is loaded in memory. The second column is the start and end of the memory range. The third us the flags from the node structure for user32 is has value 71000002, the next column is the memory management structure pointer 8519cc48, the next the ProtoPTE’s structure e1620040 and finally the name of the memory mapped file dll or exe or words like stack heap etc.
Task is for 16 bit processes.
The thread command by itself displays all threads running in our computer by all processes. Thread winword shows us threads created for program winword. The first column is the thread id 08a8, the second kernel thread environment block 849aa020, the address of the bottom of the threads stack ed8b9000, ed8c0000 the start of the threads stack, ed8bf930 the threads current stack pointer value which lies between the above two values, the threads user thread environment block 7ffde000 and owners name and in brackets the pid winword ( 8c0).
Thread –x winword gives us lots and lots of extended information about our threads like the number of context switches made, the address at which the thread will restart when alive. The priority of the thread and also whether in ring 0 or ring 3. the command thread –r will give us the values of the registers of each thread. At times it will give us the message thread stack is not present. Thread –w winword will show us all the objects that a thread is waiting on.
If we run the thread command thread –w 08a8 where 08a8 is the tid of one of our threads we will get the objects that this thread is waiting on.
The timer command by itself gives us a list of timer objects the system hs created. Whereas if 84e37a40 is the handle of a timer, the command timer 84e37a40gives us more details about this timer object. One of the interesting details is the number of seconds left for the timer.
The tss command gives us the task state segment. It first reads the tr or task register to get the address of the tss. This is the current tss and we can use the gdt command to display all the gdt’s on our system. The first column of this display is the gdt handle that we supply as a parameter to the tss. In our case the gdt handle is 3f0 so tss 3f0 tsss 3f8 will display the tss of the gdt selector.
The first value is the tss selector number 0028. The second the selector base or the linear address of the selector 80acf0000. the next the limit or size of the tss 20ab. The next four lines show us the ldt, gs , fs and all the other registers. The last lines show us the I.O map base and size. We have a size of 0 as our I/O is trapped.
The command usb shows us that we have 4 usb controllers on our system. The hc or host controllers numbers start from 0 and move one up at a time. We have two usb standards UHCI universal host Controller Interface or OHCI Open host Controller Interface. USB 2.0 supports one standard EHCI.
Usb –dumpregs 0 where 0 is the HC number which can go in our case from 0 to 3 shows us the devices control registers. Usb –sc 0 will show us the currently active entries in the schedule of the host controller. Usb –sc –v 0 shows us all the inactive entries as well. The usb specifications tell us what the above data means.
The what command tells us what something in the system means to softice. What winword tells us that winword is a program running on our computer. It also tells us that it has a value 845cc7a0. Running what 845cc7a0 tells us that this is the kpeb for a process winword. The process id for winword is 8c0. what 8c0 tells us that this number is a process is for winword.
The parameter to what can be a name or a expression. To force it to be used as a symbol use +winword. The eax register has a value of 0 and what eax tells us that 0 is the pid for the idle process. What eip tells us the name of the process eip is pointing to.
The ntcall command displays all ntoskrnl calls that are used by ntdll. Finally all code gets called in ring 0 and not in ring 3. Thus ntdll does no work and the work is done by ntoskrnl. The system uses int2e to transfer control to ring 0 placing the function call number in the eax register. Int 2e is used to effect a transfer from ring 3 to ring 0 where the real work is done.
The edx register points to the ring 3 parameter stack frame whereas as said before eax the index number of the function. Softice will actually show us this transition from ring 3 to ring 0 along with the number of parameters that are being passed on the stack pointed to by edx. To see actual names and not addresses we need to load the symbols of ntoskrnl. The ntcall command displays all the ring 0 api calls staring with the index number, the address of the function using selector:offset , the number of parameters passed to the function and its name and offset within ntoskrnl.
As an example the call to the function _ntsetEvent would first place 95 in the eax register, then edx will contain the address of the call stack, a call to int 2e and then ret 8 as we have two parameters on the stack.
Mov eax , 00000095
Lea edx, [esp+4]
Int 2e; _ntsetevent ( params = 2)
Ret 0008
The packet command helps us o see a network packet in English. This command has to be passed a ndis_packet structure address which we did not know how to supply.
The prn command tells us what printer port we are connected to. By default it is lpt1. The command prn com1 switches the port to com1.
The macro command allows us to create our own macros. Macro vijay = “wc” creates a macro vijay for us. Until we do not finish writing vijay we get an error invalid command at the bottom. Writing vijay shows us the actual definition of vijay. Now when we write the command macro it shows us one entry vijay and its full definition.
Writing macro vijay* deletes the macro vijay. Our macros can be as complex as we like. We now try the command macro vijay = “wc ; wr”. If we now press the up arrow key we will not see vijay but wr and then wc as softice actually breaks the macro as individual commands and executes then for us.
The name of the macro must be from 3 to 8 characters and thus a name like v1 will give us an error. The macro gets added to the command set of softice. We write the command bpx messageboxa do “vijay” and then run our program a. A breakpoint is called and in softice the code and register windows disappear. Thus the macros we write can be used in place of softice commands.
The macro name can contain any alpha and numeric characters and the underscore. If the macro already exists the new definition over rites the old macro. The macro name cannot be a existing softice command name. Within the semi colons we can use other macro names that we have created. To escape a % sign or double inverted commas use the backslash as \” or \%. A semicolon at the end is optional.
The maximum number of macros that can be created is 256. In the softice loader we can create macros using the same syntax that we have used. Macros can refer to itself but this recursion is not useful as at some point we need to stop. Softice allows recursion upto 32 levels and then stops. The command macro vijay = “wc;wr;vijay” creates a recursive macro.
Running vijay keeps toggling the code and register window for ever. Pressing a key stops it , pressing ctrl-D takes us out of softice but when we come back the macro does not stop. So we run the command macro vijay* to delete the macro and now it stops. We write the command macro popup = “wc;wr”. This macro popup Is special. Anytime softice pops up this macro gets run. The first time we run it we see no code or register window, the second time we run it we see the two window.
Thus anything we want to do before softice loads up we place it in the popup macro. We now define one more macro popdown = “bc *”. A macro by the name popdown is called each time we exit softice. We have created a breakpoint on messagebox. We ctrl-D out of softice, the popdown macro clears all breakpoints and when we comeback bl gives us no breakpoint defined.
Thus if there is some softice command that we want called each time we enter and leave softice use the popup and popdown commands. Writing the command macro popdown allow us to edit or change the macro definition. Remember macro p* does not delete all macros beginning with p.
The command macro vijay = “h %1” now allows us to pass parameters to the macro. Thus vijay will gives us a help of all commands, vijay ver will give us help on the ver command only. Writing Vijay ver abc gives us no error as we can specify as many parameters as we like softice ignores the rest of them.
The command macro vijay = “h %2” gives us an error as we have tried to reference parameter %2 without referring to parameter %1.The order of reference is not important, we must reference both parameters as we are not allowed to reference the second and not the first.
The src command or f3 allows us to toggle the code window in source mode code mode and mixed mode.
The t command or the F8 key traces one instruction at a time. This command uses the single step flag to single step. Writing a equal to sign allows us to start executing at a certain address. T =1000 should not be tries as we get a blue screen of death. T 3 will execute 3 instructions at a time. This is different from the p command as it actually steps into the call instruction. We need at times to step into and step out of our call instruction.
Softice has a large number of internal variables like pause that change the way softice functions. It is important to understand each of these internal variables for better use of softice. We use the set command to display as well as change the value if these variables. When we use set with a internal variable and then a on and off we enable or disable that option. We also use set and the variable name and a value, we give our internal variable that value. Also set and only the variable name tells us what the current state is.
Thus set pause will display on or off the two values the pause variable can have. Set by itself displays all the softice variables and their current state. Set pa followed by a tab will complete the word for us.
The set altscr mono will redirect the output of softice to a alternative monochrome monitor which we cannot test. Set altscr vga will do the same for a vga monitor cannot check. The default is what we use set altscr off.
Whenever we run the function rtlassert softice will pop up if the set assert is on. Set buttonreverse on reverses the meaning of the right and left mouse button. We have a problem getting the mouse working with softice and hence did not test.
The set center on centers the screen for us. We first put it off, use ctrl+alt and move the screen around and then when we put center on again the softice screen comes back to the center for us.
The set code on shows us the opcodes in assembly, set code off does not. Helps us in writing shell code.
The set flash option should be off as otherwise softice redraws the screen each time we do a trace or t command. This gets annoying and unless you need it because the softice displays changes a lot flash must be off.
The set lowercase on will show us all the assembly instructions in lower case. If we put it off the assembler in the code window will be caps. Simple things but we need to please everybody.
The set monitor shows us all the monitors we have, we have one and if we had more than 1 we could change to them.
The set mouse on and off allows us to enable and disable the mouse, better to disable the mouse in our case.
The set origin command tells us were the left hand origin of softice will be. If we write set origin 11 the softice window shifts to extreme left of our screen.
The set selectors instruction allows us to display the selector registers value before a instruction or not. It is better to keep selectors on.
The set wheellines works with a intellipoint point that we do not have.
The set tab is by default 4 and we can change it to the value we want
It is better to turn verbose off so that the softice messages from the commands load32, unload32 and exit32 do not get displayed.