Tables

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:

Index Used by OllyDbg to draw Colours in the Black on white scheme (FG / BG)
DRAW_NORMAL Normal text black / white
DRAW_HILITE Highlighted text light red / white
DRAW_GRAY Grayed text dark gray / white
DRAW_EIP Current execution point white / black
DRAW_BREAK Unconditional breakpoint black / light red
DRAW_COND Conditional breakpoint black / light magenta
DRAW_BDIS Disabled breakpoint black / yellow
DRAW_IPBREAK Breakpoint on the current execution point light red / black
DRAW_AUX Elements of the table that are not controlled by the drawing function light gray / white
DRAW_SELUL Underlining (FG) and selection (BG) black / light gray

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:


Index Used by OllyDbg to draw
DRAW_PLAIN Commands that are not in any other group
DRAW_JUMP Unconditional near and far jumps
DRAW_CJMP Conditional jumps
DRAW_PUSHPOP PUSH and POP commands
DRAW_CALL Near and far calls and interrupts
DRAW_RET Near and far returns
DRAW_FPU FPU, MMX, 3DNow! and SSE commands
DRAW_SUSPECT I/O, system and privileged commands
DRAW_FILL Commands that were recognized as filling between the procedures
DRAW_MOD Commands and data that differ from backup (higher priority than any preceding item)
DRAW_IREG Integer registers (8/16/32 bit)
DRAW_FREG FPU, MMX, 3DNow! and SSE registers
DRAW_SYSREG Segment, control and debugging registers
DRAW_STKMEM Memory operands accessed via ESP or EBP
DRAW_MEM Other memory operands
DRAW_MCONST Constants that point to existing memory
DRAW_CONST Other constants

Another part of the attribute (DRAW_APP) specifies underlining and graphical symbols. Choose only one item from this group:


DRAW_TEXT Plain text
DRAW_ULTEXT Underlined plain text
DRAW_GRAPH Characters in the text string are interpreted as graphical elements (constants G_xxx). For example, thick parentheses that mark recognized procedures and structures consist of G_BEGIN, followed by several G_BODY, followed by G_END. Full list of graphical elements is available in plugin.h

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:

DRAW_SELECT Selected text

The remaining flags can be set only in select:

DRAW_MASK Informs drawing routines that mask is used
DRAW_VARWIDTH Specifies that variable character width is possible. Table windows allow only fixed-pitch fonts, but Kanji, Chinese and some other characters may take double space even if font is fixed-pitch. Use this flag when displaying comments. If flag is not set, non-standard characters are truncated into the single character space. Take care when assigning attributes in mask, they may be applied to false positions or truncate displayed text
DRAW_EXTSEL Extends attributes specified in the last element of the mask to the end of the cell
DRAW_TOP Draws upper half of the character at the bottom of the line. When combined with the same text and DRAW_BOTTOM attribute on the next line, creates text shifted by half of the line vertically. This trick is used to display EIP and EFL in the Registers
DRAW_BOTTOM Draws lower half of the character at the top of the line
DRAW_INACTIVE Grays all text in the cell that is not highlighted
DRAW_RAWDATA Outputs each character separately, preventing Windows API from interpreting ligatures, accents and surrogates. Used in ASCII and UNICODE dumps
DRAW_NEW Highlights normal text. Has lower priority than DRAW_INACTIVE

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
drawfunc(DF_NEWROW)
for each fully or partially visible column in the increasing order do
drawfunc(column)
enddo
enddo
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:

DF_CACHESIZE Always the very first call to the drawing function. The functions must return required size of the cache in bytes (or 0 if additional cache is not necessary). The cache is available in all consecutive calls, and its contents is not modified by the API. Note that parameter ps for custom tables is pointer to t_drawheader which contains the number of fully or partially visible table rows
DF_FILLCACHE Follows DF_CACHESIZE if returned cache size is not 0. Drawing function must fill the cache with the data that will be used in all subsequent calls. For example, dump table reads the contents of the whole displayed memory
DF_NEWROW Informs drawing function that new visible table row starts, giving it cthe chance to make time-consuming operations that apply to the row as a whole. For example, Disassembler window calls Disasm() and caches hexadecimal command dump, disassembled command and code-related comments for columns 1, 2 and 3
0..N-1 (N is the number of columns in the table) Request to draw contents of the table cell. Attention, if table is scrolled in the horizontal direction so that column is not visible, or column is minimized, this call is skipped. Therefore if one column relies on the data calculated for other column, always make such calculations in DF_NEWROW
DF_FREECACHE Always the very last call to the drawing function. Called only if cache size is not 0. Drawing function must free all allocated resources, like memory or handles


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:

WM_DESTROY Called when table window receives WM_DESTROY. Custom table function must free resources allocated by itself, for example, the table. Note that during this call, doubleword of extra window memory at GWL_USR_TABLE (keeps pointer to the rable) is already set to 0. Returned value is ignored
WM_CLOSE Called when table window receives WM_CLOSE. By returning -1, custom table function prevents window from closing. Do not misuse this option!
WM_SIZE (Passed only to custom table functions) - called when size of the window is changed to give table chance to update itself. Parameter lParam is replaced by the number of visible lines. Returned value is ignored
WM_CHAR (Passed to the table function only if flag TABLE_WANTCHAR is set) - called when table window receives WM_CHAR and both Control and Alt keys are released. Table function must return 0 when message was not processed, 1 if message was processed and window must be updated, and -1 if message was processed but update is not necessary. WM_CHAR has higher priority than standard shortcuts
WM_USER_xxx Custom messages and notifications, descibed in details here. Custom table functions in OllyDbg frequently intercept WM_USER_BAR and WM_USER_DBLCLKOther messages are usually processed by the default table function. To pass processing to the default function, custom function should return 0.


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:

TSC_KEY Selection was changed due to pressed keyboard key (Home, PgUp, arrow etc.)
TSC_MOUSE Selection was changed due to the mouse click
TSC_CALL Selection was changed as a result of call to Movetableselection() or Settableselection()


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);


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;
}



See also:

Dialogs, dumps, frames and tab collections, sorted data