Usage guide and detailed command listing of ProDebug

Here is a detailed guide to using the protected mode bootable debugger. Please note that the screenshots provided here are from an 386 emulator called bochs for convieniance sake.


Argument notation
[x] means that argument is optional
<x> means that argument is required
x can be in decimal like 15 or in hex (entered as 0xf)

  1. l <startLogicalSector> [numSectors]
    or
    l <startCylinder> <startHead> <startPhysicalSector> [numSectors]

    Loads a program from a 1.44 inch floppy (in A:) .The size of code is <numSectors> (or 1 if not specified) sectors. Each sector is 512 bytes in size.The starting sector can be specified logically (<startLogicalSector>) or in terms of physical disk geometry as shown above . Please note that <startCylinder> ranges from 0 to 79, <startHead> from 0 to 1 ,<startPhysicalSector> from 1 to 18.

    The program is allocated four segments to start with :

    code (cs) : 64k ,readonly
    data (ds) : 64k ,rw
    stack(ss) : 64k ,rw
    extdata (es) : 256k ,rw
    (Above four segments are created in the ldt)

    New segments can be created using the 'roseg' command detailed later. Example :

    to load a program starting from sector 2 , having a size of 600 bytes (hence two sectors long).

    >l 2 2 (Screenshot goes here)

  2. s [numInstr]
    Single steps for numInstr instructions through a program starting from the location CS:IP if it is not specified.

  3. run [runTill]
    runs a program starting from the location CS:IP till the instruction preceding CS:runTill . If runTill is not specified Every program must end with an 'int 1' or else the control would never return to the debugger .

  4. tss
    Shows the TSS of the currently loaded program.This program requires that a program be loaded.

  5. d
    Dumps all the register contents , including the eip and the segment registers on to the screen.In case the program is not loaded a dump of the kernel register contents is provided.

  6. brk <type> <offset>
    Breaks a program at the specified location , more details follow . <type> is one of 'instr'|'read'|'write' , the <offset> means different things depending on the <type>: if type equals
            i : The program shall break when the eip equals the offset value, hence it would just break before the instr cs:<offset> .
            r : The program shall break just before the instr where it tries to read the memory location ds:<offset>
            w : Same as above , but will break when the program tries to write something to the above specified location.
    

    Please note that a maximum of four break points are only currently supported, this is because only four break points are supported in 386.

  7. shbrk
    Every break point created is assigmed a unique no. from 1 to 4 , showbreak lists details about the currently set breakpoints and their numbers, and whether they are enabled.This information helps the following two commands.

  8. dbrk <breakPtNo>
    You can temporarily disable a breakPoint , this is same as removing that breakpoint , but the breakpoint stays , it is just never fired.

  9. ebrk <breakPointNo>
    You can enable disabled breakpoints by specifying their break Point Num.

  10. <regName> = <newValue>
    This commands allows the user to set the variable of the register <regName> to the new value. Please note that only the following registers can be changed by the user : eax,ebx,ecx,edx,esi,edi,ebp,esp,eflags . The segment registers cannot be changed and nor can the following flags in eflags : resume flag,trap flag, interrupt enable flag .

  11. ldt [numEntries]
    Displays numEntries(or 10 if not specified) from the LDT table from the first entry (0th one is reserved by 386) .The user can use this to know how is segments (c/d/s/e) are mapped.Doesn't require a program to be loaded.

  12. gdt [numEntries]
    Displays numEntries(or 10 if not specified) from the GDT table from the first entry (0th one is reserved by 386) .

  13. kill
    Kills the loaded program ,frees up its memory and segment selectors . No program is subsequently loaded for debugging.

  14. pstck [numWords]
    Prints the top numWords (or 10 ) of the user's program stack , ie memory locations from ss:esp to ss:(esp+numWords*4) are printed .Note that a word is of four bytes and the intel stack grows down.

  15. free
    This can be useful for the below described roseg command , it just prints the index of first ten descriptors in LDT , that are currently free.

  16. crseg <selIdx> <startAddress> <endAddress>
    Creates a segment in the LDT, with the specified selIdx with base as startAddress and limit as endAddress -1 . The created segment is a data read only segment . The user can map any area of the available system memory like this to a segment of his choice. This does not require the program to be loaded . If the LDT descriptor with index selIdx is not free an error mesg is returned. The free descriptor indexes can be obtained using the previous command. This command can be used to for example display the system gdt or say to display the bios routines. The segmentSelector corresponding to the selIdx is (selIdx<<3)|0x7 .

  17. dss <segmentSelector>
    Displays details about the given segmentSelector , for example its dpl , rpl , base , limit of the descripor and the table it uses.

  18. md <segmentSel:offset> [numBytes]
    Displays the memory (in hex) starting from <segmentSel:offset> for a numBytes , in rows of 16 bytes , with the starting address on the left (this format is similar to the dos debug program). <segmentSel:offset> address notation examples include CS:0x123 or CS:123 (123 is in dec) , or SS:0x3 , or 0x53:0x12 .(this refers to the 18th byte after the base of the 10th segment selector in the ldt ).

  19. mc <endSegmentSel:endOffset> <endSegmentSel:endOffset> <sizeInBytes>
    Copies <sizeInBytes> bytes from the specified start and end addresses in the notation described earlier . Note that the endSegmentSel:endOffset should be user writable segment and the endOffset should be less than the allowed limit. This pretty much means that you can move code only to your ds and es segments. Hence this command needs that the program be loaded.

  20. mf <segmentSel:offset> <numBytes> <byte1> <byte2> ....<byteN>
    Fills the memory starting from <segmentSel:offset> with <byte1>,<byte2> ... .This is done for <numBytes> . If N is less than numBytes , the bytes are repeated again. Also <byte1> can be in hex or decimal (to represent asci chars type their codes).

  21. hex
    Allows the user to key in hex code for a maximum of 1kb size . The user keys in the hex code like this : 'cd01404040cd01bc40' , ie without spaces , however he is allowed to enter newlines in b/w for more readability . This functions exactly like the load command , except that the code is keyed in , instead of being loaded from a floppy.

  22. code
    Provides a hex dump of the code that is being executed , requires a load.

  23. q
    Prompts a person to reboot the machine , on saying yes the machine is rebooted.

  24. ? [command]
    If command is not specified , a one line description of each command is given . Else a detailed explanation(like given here) is shown for the specified command.

  25. System Calls
    System calls are also implemented, to invoke them from the user program , put the syscall no in eax, the first argument if any in ebx, the second arg if any in ecx and do a software interrupt of 0x80 like 'int 0x80'.For example ,to invoke the exit system call , you can : 'xor eax,eax ; int 0x80' .
    If a system call returns a value like the getch syscall , the return value is stored in eax. Some syscalls may change the eflags bits like kbhit. The following system calls are implemented :
    1. exit : (eax = 0 ) Terminates the user program .
    2. getch (eax = 1) Waits for a keypress froma user , the ascii code of the key is then put into eax,Note that the key is not echoed on the screen. For that you must use the following syscall.
    3. printch (eax = 2) , Prints a character onto the screen , the character to be printed is stored in ebx. ,example to print the character 'a' , you could do this : 'mov ebx,65 ; mov eax,2 ;int 0x80'
    4. isKeyReady (eax = 3) ,If there is a character waiting in the keyboard que , the zero flag is cleared , else it is set .Note that this function does not wait for a key to be pressed , it immediately returns the current status.
    5. peekKeyboard (eax = 4) , If there is a character waiting in the keyboard que , it is returned , else a 0 is returned in the eax , Note that this function peeks the que , ie it does not deque , hence on calling this function consecutively the same output in eax will be obtained.

  26. Scrolling
    To go up a line press Ctrl+U , to go down a line press Ctrl+D , on some keyboards the pgUp , pgDown , and the up/down arrow keys also might work.

Guide to some of the nifty features :

Shortcomings

  1. Can have only a total of four break points using the break command.
  2. Can work with only a PS/2 keyboard, not all (especially XT keyboards) will be supported., Also keys like numLock,Function keys , Numeric keypad keys have no effect.
  3. Extremely inefficient from an OS point of view , as it saves extensive state information about the program being debugged.Also using the debug registers really causes the 386 to run very slowly.