x86 CPU supports four hardware breakpoints on memory (size of byte/word/dword) but quite often we want to breakpoint larger memory blocks.

OllyDbg allows us to do it via page attributes (like PAGE_NOACCESS). it works! and sometimes works even better than hardware breakpoints, coz protections and viruses try to detect and defeat hardware breakpoints, but just a few protections are aware of page attributes.

however, some protections are aware! what we’re going to do? well, there is an alternative. CPU allows us to load 0..3 selectors into DS, ES, FS and GS segment registers from user-level without generation #GP(0). these selectors are invalid, thus every memory operation like MOV EAX, FS:[0] will generate the access violation exception. we should catch it, restore the segment registers, perform a singe step to execute current instruction and load the invalid selectors back (NOTE: if for some reasons we don’t want to perform single a step - there is another way: just set software/hardware breakpoint on the next instruction and start executing without setting the trace flag).

this is CPU stuff, so it works as for Windows, as for Linux/BSD and all other operation systems (however, there is no good protections for Linux/BSD, so I tested it only under Windows. please, mail me to info#re-lab.org if you meet any problem with Linux/BSD system)

breakpointing FS register is very useful for malware debugging. Windows keeps system information there and malware-writters use it via FS. legal applications and API functions also use FS for their needs, so we have to apply some filers to skip the calls we’re not interested in. the most simple filer just checks - if the address of the instruction belongs to PE image and if it’s outside - it should be skipped. also, we can memorize all triggered addresses and skip them to not break the same address more than once.

breaking DS register is very useful for manual unpacking. normally, modern programs use local vars and stack arguments addressed via SS register. DS register is for global vars, but global vars are not very popular today. in other hand, most of unpackers use DS to perform unpacking, so breakpointing DS helps us to find the place where unpacking (decrypting) is performed.

to demonstrate the idea I wrote a simple script for IDA-Pro. how to use it?
1) start IDA-Pro debugger (F2 to set breakpoint at the Entry Point then F9 to run);
2) load the script (CTRL-F3 or File/Load File/IDC File in console IDA-Pro or File/IDC File in GUI, and select “seg_bp.idc” script);
3) SHIFT-F2 to call console and type “break_ds_set();” or/and “break_fs_set();” to set breakpoint(s) on DS or/and FS;
4) press CTRL-ENTER or “OK” button;
5) start tracing the program or run it by pressing F9 key;
6) IDA-Pro will tell us something like “the instruction at 0×40120E referenced memory at 0xFFFFFFFF. The memory could not be read”. it means that the breakpoint at 40120Eh has been triggered. address FFFFFFFFh is incorrect, ignore it;
7) press ALT-F7 to restore segment registers and perform a single step to execute the breakpointed instruction;
8) continue tracing the program or press F9 again;
9) to remove the breakpoint press SHIFT-F2 and type “break_ds_unset();” or/and “break_fs_unset();”

well, you got it. now you know how to set and remove breakpoints. let’s learn more.
1) you probably want to apply a filter to skip the triggers you’re not interested in. to demonstrate how to do it, I wrote run_to_next() function and assigned it to ALT-F9 hot key. basically, it works like F9, but it automatically calls step() to perform a single step and applies two filers. the first filer skips all breakpoints triggered above 10000000h (they probably belongs to system DLLs), the second filer skip instructions with “skip_this” comment. to make a comment just press “:” and enter the text. to remove a comment - press “:” again and delete the text. of course, you can modify run_to_next() to create your own custom filter. the script can be reloaded on the fly, without restarting the debugger;

2) by default, IDA-Pro shows you dialog boxes to inform us than the exception has been generated. these modal dialog boxes stop executing of the program, thus run_to_next() is unable to continue executing the program automatically. the solution is simple: check “don’t display this message again” checkbox and the message box will never appear;

NOTE 1: if a breakpoint was triggered and removed without pressing ATL-F7, IDA will ask you should she pass the expectation to the program. press “N” to do not pass it;

NOTE 2: to display breakpoints/current segment registers values press SHIFT-F2 and type “show_segs();”, CTRL-ENTER;

NOTE 3: if you don’t want to type commands name - assign hot keys (see main func of the script how to do it);

NOTE 4: if something goes wrong, press SHIFT-F2 and type “break_reinit();” to reinitialize script data;

WARRING! if a program changes DS or FS - the script will fail. however, normally, programs do not change segment registers.

download the script