Almost all OllyDbg windows are tables: Modules, Log data, dumps, Help, and even Registers. CPU window is a frame that embeds five table windows.
Standard table displays contents of sorted data, which is part of the table descriptor. To program standard table, you set number of columns and their properties in t_table.bar, write drawing function that determines the contents of table cells, add menu that simultaneously defines keyboard shortcuts, supply several optional components like table and sorting functions and call Createtablewindow().
Custom tables (of type TABLE_USERDEF) are similar but have no associated sorted data. If you know the number of displayed lines in advance, you may use existing selection and scrolling logic. Just set flag TABLE_STDSCR and keep total number of lines in t_table.sorted.n, first visible line in t_table.offset and currently selected line in t_table.sorted.selected.
Sometimes it's hard or impossible to count all lines in the table. For example, if dump window displays assembler code, it would need to disassemble each command in a code section! Even taking into account the high speed of Disasm() in length-only mode, this would cause an unacceptably long delay. Moreover, standard tables support only single-line selection. If your table needs non-standard features, you must process WM_USER_xxx messages by yourself.
Columns
The number of displayed columns and their properties are defined in the structure t_table.bar of type t_bar. Maximal number of columns is limited to NBAR=17.
Sometimes it's necessary to change the number and meaning of the columns. To do it, modify data in bar (only the first 6 described members!), call Defaultbar() and redraw or invalidate the window. Don't modify the remaining bar members (for example, dx) directly!
Drawing function
Each table has mandatory drawing function. Given sorted data item ps and column in the table, this function must return string s which is displayed in the specified table cell:
typedef int DRAWFUNC(wchar_t *s,uchar *mask,int *select,struct t_table *pt,t_sorthdr *ps,int column,void *cache);
In the simplest case, all letters in the string have the same attributes (colour, background, underlining) returned in select, and mask is not used. If drawing function uses graphical elements or different parts of the strings have different attributes, it must set flag DRAW_MASK in select and specify attributes individually for each character.
First part of the attribute is foreground and background colours. Note that colours are selected indirectly: you only specify the index of the requested colour pair, and OllyDbg gets corresponding RGB values from the colour scheme associated with the table. Use DRAW_COLOR mask to extract this index:
|
Another group of indices is used by OllyDbg to highlight commands and operands. Of course, plugins are free to use them for their own purposes. Note that if flag TABLE_SYNTAX is set, screen colours will be taken from the selected highlighting scheme. If highlighting scheme is absent or colour is set to transparent, main colour scheme will be used:
|
Another part of the attribute (DRAW_APP) specifies underlining and graphical symbols. Choose only one item from this group:
|
Next bit changes background to DRAW_SELUL and is used to select the whole cell (in select) or only part of the text (in mask). If DRAW_SELECT is set simultaneously in select and in mask, background is not grayed and partial selection is still visible. DRAW_SELECT doesn't apply if DRAW_EIP, DRAW_BREAK, DRAW_COND, DRAW_BDIS or DRAW_IPBREAK is used. When combined with DRAW_GRAY, it changes foreground colour to DRAW_NORMAL to improve the visibility of the text:
|
The remaining flags can be set only in select:
|
Drawing is influenced by the bar attributes BAR_SHIFTSEL and BAR_WIDEFONT. When BAR_SHIFTSEL is set, character's background is shifted 1/2 character to the left from the character. This trick improves the appearance of the selection, but each selected block must be followed by the selected empty space. BAR_SHIFTSEL is used in the hexadecimal dumps. BAR_WIDEFONT doubles the width of the character box on the screen and prevents Kanji, Chinese and Hangul ideographs from being truncated. This mode is optional for UNICODE dumps.
When drawing or copying table, OllyDbg makes the following sequence of calls to the drawing function:
drawfunc(DF_CACHESIZE)
if cache size is not 0 drawfunc(DF_FILLCACHE)
for each fully or partially visible row in the increasing order do
if cache size is not 0 drawfunc(DF_FILLCACHE)
for each fully or partially visible row in the increasing order do
drawfunc(DF_NEWROW)
for each fully or partially visible column in the increasing order do
for each fully or partially visible column in the increasing order do
drawfunc(column)
enddoenddo
if cache size is not 0 drawfunc(DF_FREECACHE)
if cache size is not 0 drawfunc(DF_FREECACHE)
Increasing row order for the table of type TABLE_DIR means that rows are drawn from the bottom to the top.The sequence is uninterruptable: OllyDbg guarantees that no other drawing function is called inbetween. Here is the more detailed description:
|
Sorting function
Sorted data included into the table may contain sorting function. Given pointers to the two sorted data items and sorting criterium, this function must compare items and decide which one should appear earlier in the table:
typedef int SORTFUNC(const t_sorthdr *sh1,const t_sorthdr *sh2,const int sort);
When bar segment is declared as BAR_SORT and user presses it, OllyDbg sorts data using either quicksort or heapsort. Criterium sort is the index of the column in the table. If, according to this criterium, item pointed to by sh1 should precede sh2, sorting function must return -1. If first item must be displayed after the second, function must return +1. If items are "equal", it must return 0.
Table function
Table function is optional, but usually it is defined in the table:
typedef long TABFUNC(struct t_table *pt,HWND hw,UINT msg,WPARAM wParam,LPARAM lParam);
Table function receives following messages:
|
Selection change notifications
Another optional callback function is tableselfunc:
typedef void TABSELFUNC(struct t_table *pt,int selected,int reason);
It is called whenever selection in the standard table (or custom table of type TABLE_STDSCR) is changed. Parameter selected is the index of the newly selected line, parameter reason indicates which action has changed the selection:
|
Table creation and embedding
Before table can be displayed, its descriptor (structure t_table) and included sorted data (standard tables) must be initialized.
To create standalone table, use Createtablewindow(). If you want to create a resizable collection of tables, like CPU, or a collection of tabs, like in Search, have a look at the frames and tab collections. Also you can create table holder (dialog or standalone window) and integrate one or more tables into it by calling Createottablewindow(). OllyDbg uses this technique in Attach dialog. To integrate dump, use Embeddumpwindow() (Run DLL Export dialog).
API functions:
HWND
Createottablewindow(HWND
hparent,t_table *pt,RECT *rpos);
HWND Createtablewindow(t_table *pt,int nrow,int ncolumn,HINSTANCE hi,wchar_t *icon,wchar_t *title);
HWND Activatetablewindow(t_table *pt);
HWND Createtablechild(t_table *pt,wchar_t *classname,wchar_t *name,wchar_t *help,ulong style,int x,int y,int dx,int dy,int idalign);
int Getcharacterwidth(t_table *pt,int column);
void Defaultbar(t_table *pt);
int Linecount(t_table *pt);
int Gettabletext(t_table *pt,int row,int column,wchar_t *text,uchar *tmask,int *tselect);
int Gettableselectionxy(t_table *pt,int column,POINT *coord);
int Maketableareavisible(t_table *pt,int column,int x0,int y0,int x1,int y1);
int Movetableselection(t_table *pt,int n);
int Settableselection(t_table *pt,int selected);
void Updatetable(t_table *pt,int force);
void Delayedtableredraw(t_table *pt);
void Setautoupdate(t_table *pt,int autoupdate);
HGLOBAL Copytableselection(t_table *pt,int column);
HGLOBAL Copywholetable(t_table *pt,int compatible);
HWND Createtablewindow(t_table *pt,int nrow,int ncolumn,HINSTANCE hi,wchar_t *icon,wchar_t *title);
HWND Activatetablewindow(t_table *pt);
HWND Createtablechild(t_table *pt,wchar_t *classname,wchar_t *name,wchar_t *help,ulong style,int x,int y,int dx,int dy,int idalign);
int Getcharacterwidth(t_table *pt,int column);
void Defaultbar(t_table *pt);
int Linecount(t_table *pt);
int Gettabletext(t_table *pt,int row,int column,wchar_t *text,uchar *tmask,int *tselect);
int Gettableselectionxy(t_table *pt,int column,POINT *coord);
int Maketableareavisible(t_table *pt,int column,int x0,int y0,int x1,int y1);
int Movetableselection(t_table *pt,int n);
int Settableselection(t_table *pt,int selected);
void Updatetable(t_table *pt,int force);
void Delayedtableredraw(t_table *pt);
void Setautoupdate(t_table *pt,int autoupdate);
HGLOBAL Copytableselection(t_table *pt,int column);
HGLOBAL Copywholetable(t_table *pt,int compatible);
Example:
This
is the slightly modified procedure that creates INT3 Breakpoints
window. Its drawing
function is posted here, and
its menu - here. An
example of the sorting function (Modules window) is available here.
extern t_table bpoint;
// Custom table function of breakpoint window.
long Bpointfunc(t_table *pt,HWND hw,UINT msg,WPARAM wp,LPARAM lp) {
switch (msg) {
case WM_USER_DBLCLK: // Doubleclick in column
Callmenufunction(pt,pt->menu,Mfollowbreak,0);
return 1;
default: break;
};
return 0;
};
// Creates new or brings to top existing INT3 breakpoint window. Only one such
// window may exist at a time. Returns handle of the table window (not MDI
// container!) or NULL on error.
HWND Createbpointwindow(int activate) {
// If table is not yet initialized, initialize it now.
if (bpoint.bar.nbar==0) {
wcscpy(bpoint.name,L"INT3 breakpoints");
bpoint.mode=TABLE_SAVEALL;
bpoint.bar.visible=1;
bpoint.bar.name[0]=N(L"Address");
bpoint.bar.expl[0]=N(L"Address of INT3 breakpoint");
bpoint.bar.mode[0]=BAR_SORT;
bpoint.bar.defdx[0]=9;
bpoint.bar.name[1]=N(L"Module");
bpoint.bar.expl[1]=N(L"Name of the module this breakpoint belongs to");
bpoint.bar.mode[1]=BAR_SORT;
bpoint.bar.defdx[1]=9;
bpoint.bar.name[2]=N(L"Status");
bpoint.bar.expl[2]=N(L"Status of INT3 breakpoint");
bpoint.bar.mode[2]=BAR_SORT;
bpoint.bar.defdx[2]=12;
bpoint.bar.name[3]=N(L"Disassembly");
bpoint.bar.expl[3]=N(L"Disassembled command");
bpoint.bar.mode[3]=BAR_FLAT;
bpoint.bar.defdx[3]=40;
bpoint.bar.name[4]=N(L"Comment");
bpoint.bar.expl[4]=N(L"Comment");
bpoint.bar.mode[4]=BAR_FLAT;
bpoint.bar.defdx[4]=256;
bpoint.bar.nbar=5;
bpoint.tabfunc=Bpointfunc;
bpoint.custommode=0;
bpoint.customdata=NULL;
bpoint.updatefunc=NULL;
bpoint.drawfunc=(DRAWFUNC *)Bpointdraw;
bpoint.tableselfunc=NULL;
bpoint.menu=bpointmenu;
};
// Create or update INT3 breakpoint window.
if (bpoint.hw==NULL)
Createtablewindow(&bpoint,0,bpoint.bar.nbar,hinst,L"ICO_B_YELLOW",T(L"INT3 breakpoints"));
else
InvalidateRect(bpoint.hw,NULL,FALSE);
// If requested, activate INT3 breakpoint window and bring it in foreground.
if (bpoint.hparent!=NULL && activate!=0) {
if (hwclient!=NULL)
SendMessage(hwclient,WM_MDIACTIVATE,(WPARAM)bpoint.hparent,0);
if (!IsZoomed(bpoint.hparent))
ShowWindow(bpoint.hparent,SW_RESTORE);
SetFocus(bpoint.hw);
};
return bpoint.hw;
}
extern t_table bpoint;
// Custom table function of breakpoint window.
long Bpointfunc(t_table *pt,HWND hw,UINT msg,WPARAM wp,LPARAM lp) {
switch (msg) {
case WM_USER_DBLCLK: // Doubleclick in column
Callmenufunction(pt,pt->menu,Mfollowbreak,0);
return 1;
default: break;
};
return 0;
};
// Creates new or brings to top existing INT3 breakpoint window. Only one such
// window may exist at a time. Returns handle of the table window (not MDI
// container!) or NULL on error.
HWND Createbpointwindow(int activate) {
// If table is not yet initialized, initialize it now.
if (bpoint.bar.nbar==0) {
wcscpy(bpoint.name,L"INT3 breakpoints");
bpoint.mode=TABLE_SAVEALL;
bpoint.bar.visible=1;
bpoint.bar.name[0]=N(L"Address");
bpoint.bar.expl[0]=N(L"Address of INT3 breakpoint");
bpoint.bar.mode[0]=BAR_SORT;
bpoint.bar.defdx[0]=9;
bpoint.bar.name[1]=N(L"Module");
bpoint.bar.expl[1]=N(L"Name of the module this breakpoint belongs to");
bpoint.bar.mode[1]=BAR_SORT;
bpoint.bar.defdx[1]=9;
bpoint.bar.name[2]=N(L"Status");
bpoint.bar.expl[2]=N(L"Status of INT3 breakpoint");
bpoint.bar.mode[2]=BAR_SORT;
bpoint.bar.defdx[2]=12;
bpoint.bar.name[3]=N(L"Disassembly");
bpoint.bar.expl[3]=N(L"Disassembled command");
bpoint.bar.mode[3]=BAR_FLAT;
bpoint.bar.defdx[3]=40;
bpoint.bar.name[4]=N(L"Comment");
bpoint.bar.expl[4]=N(L"Comment");
bpoint.bar.mode[4]=BAR_FLAT;
bpoint.bar.defdx[4]=256;
bpoint.bar.nbar=5;
bpoint.tabfunc=Bpointfunc;
bpoint.custommode=0;
bpoint.customdata=NULL;
bpoint.updatefunc=NULL;
bpoint.drawfunc=(DRAWFUNC *)Bpointdraw;
bpoint.tableselfunc=NULL;
bpoint.menu=bpointmenu;
};
// Create or update INT3 breakpoint window.
if (bpoint.hw==NULL)
Createtablewindow(&bpoint,0,bpoint.bar.nbar,hinst,L"ICO_B_YELLOW",T(L"INT3 breakpoints"));
else
InvalidateRect(bpoint.hw,NULL,FALSE);
// If requested, activate INT3 breakpoint window and bring it in foreground.
if (bpoint.hparent!=NULL && activate!=0) {
if (hwclient!=NULL)
SendMessage(hwclient,WM_MDIACTIVATE,(WPARAM)bpoint.hparent,0);
if (!IsZoomed(bpoint.hparent))
ShowWindow(bpoint.hparent,SW_RESTORE);
SetFocus(bpoint.hw);
};
return bpoint.hw;
}
See also: