traceapi.c -
annotated code of API trace plugin
////////////////////////////////////////////////////////////////////////////////
//                                                                           
//
//             
SAMPLE API CALLS TRACE PLUGIN FOR OLLYDBG
v2.01              
//
//                                                                           
//
//
This plugin allows to trace all calls to system DLLs in a single thread
by //
//
setting one-time memory
breakpoints.                                      
//
//                                                                           
//
//
API trace can be started only if process is paused. Plugin sets break
on   //
//
access on user code. When user code is reached, it removes break on
access //
//
from user code and sets it on system code, and so
on.                     
//
//                                                                           
//
//
Of course, it is possible that user code accesses data in the system
area  //
//
or vice versa. In this case I step over this command and
restore          
//
//
breakpoint. Such cases are
rare.                                          
//
//                                                                           
//
//
This plugin is by no means ideal. It runs only single thread, and
there    //
//
may be problems if program calls ZwContinue(). If DLL unloads,
plugin      //
//
doesn't delete call records. It doesn't check whether one-time
breakpoints //
//
are already set by user. It doesn't allow to protocol only selected
APIs,  //
//
and so
on.                                                                
//
//                                                                           
//
//
This code is distributed "as is", without warranty of any kind,
expressed  //
//
or implied, including, but not limited to warranty of fitness for
any      //
//
particular purpose. In no event will Oleh Yuschuk be liable to you for
any //
//
special, incidental, indirect, consequential or any other damages
caused   //
//
by the use, misuse, or the inability to use of this code, including
any    //
//
lost profits or lost savings, even if Oleh Yuschuk has been advised of
the //
//
possibility of such damages. Or, translated into English: use at your
own  //
//
risk!                                                                     
//
//                                                                           
//
//
This code is free. You can modify it, include parts of it into your
own    //
//
programs and redistribute modified code provided that you remove
all       //
//
copyright messages or substitute them with your own
copyright.            
//
//                                                                           
//
////////////////////////////////////////////////////////////////////////////////
//
Microsoft compilers hate (and maybe justifiably) old-school functions
like
//
wcscpy() that may cause buffer overflow and all related dangers. Still,
I
//
don't want to see all these bloody warnings.
#define
_CRT_SECURE_NO_DEPRECATE
#include
<windows.h>
#include
<stdio.h>
#include
<string.h>
#include
"plugin.h"
#define
PLUGINNAME     L"Trace
API"    // Unique plugin name
#define
VERSION       
L"2.00.00"      // Plugin
version
#define
NCALL         
1023           
// Size of call data extent, calls
//
Current state of the API trace.
#define
TRACE_OFF     
0              
// No trace
#define
TRACE_TILLUSER
1              
// Wait till user code is reached
#define
TRACE_TILLSYS 
2              
// Wait till system code is called
#define
CODE_GUARDED  
0x00010000      // Whether
code block is guarded
#define
CODE_USER     
0x00020000      // Whether
code block is user
#define
CODE_SYS      
0x00040000      // Whether
code block is system
//
Descriptor of call location. Calls are chained in the order of ascending
//
addresses.
typedef
struct t_call
{               
// Call location
 
ulong         
from;                
// Address of call command
 
struct t_call 
*next;               
// Next location or NULL
}
t_call;
//
API list consists of elements of type t_api.
typedef
struct t_api
{                
// Descriptor of API function
 
// Obligatory header, its layout _must_ coincide with t_sorthdr!
 
ulong         
addr;                
// Address of the API entry point
 
ulong         
size;                
// Always 1
 
ulong         
type;                
// Currently unused
 
// Custom data follows header.
 
ulong         
count;               
// Number of hits
 
struct t_call 
*call;               
// Pointer to chain of call locations
 
int           
ncall;               
// Number of elements in call chain
}
t_api;
#define
RET_ACTIVE    
0x00010000      //
Temporary breakpoint is set
typedef
struct t_ret
{                
// Descriptor of return address
 
ulong         
addr;                
// Return address
 
ulong         
size;                
// Always 1
 
ulong         
type;                
// One of RET_xxx
 
ulong         
aapi;                
// Address of the called function
 
ulong         
from;                
// Call address
 
int           
count;               
// Number of recursive breaks
}
t_ret;
//
List of code blocks consists of elements of type t_code.
typedef
struct t_code {
 
ulong         
base;                
// Base address of code block
 
ulong         
size;                
// Size of code block
 
ulong         
type;                
// Additional information, CODE_xxx
}
t_code;
static
HINSTANCE
hdllinst;            
// Instance of plugin DLL
static
int      
trace;               
// Trace state, one of TRACE_xxx
static
ulong    
threadid;            
// ID of the traced thread
static
t_table  
api;                 
// List of APIs that were hit
static
t_sorted 
code;                
// List of code blocks
static
t_sorted 
retlist;             
// List of return addresses
static
t_call   
*pfreecall;          
// Pointer to list of unused calls
static
int      
restoretempbp;       
// Some temp breakpoint must be restored
static
int      
protocolapi;         
// Protocol API calls
static
int      
skipgdi32;           
// Don't protocol GDI32 APIs
static
int      
skipuser32;          
// Don't protocol USER32 APIs
static
int      
skipkernel32;        
// Don't protocol KERNEL32 APIs
static
int      
skipmsvc;            
// Don't protocol MSVC* APIs
static
int      
protocolret;         
// Protocol returned values
static
int      
autoupdate;          
// Whether window in autoupdate mode
////////////////////////////////////////////////////////////////////////////////
/////////////////////////
ONE-SHOT MEMORY BREAKPOINTS //////////////////////////
//
Guarding memory breakpoints are one-shot access breakpoints supported
//
directly by Windows. If program accesses guarded memory, operating
system
//
removes guarding and raises STATUS_GUARD_PAGE exception.
//
//
Routines in this section set and remove guarding breakpoints on the code
//
sections of the loaded modules. Such manipulations are time-consuming.
To
//
accelerate process, plugin keeps list of code sections in the sorted
data
//
named code. if code section is guarded, it is marked as CODE_GUARDED.
//
//
If module loads or unloads, ODBG2_Pluginnotify()
receives PN_NEWMOD or
//
PN_ENDMOD. In the first case it reinstalls guards by calling Setguard().
//
In the second case, it removes module from the list of code sections.
//
Guards all system (guardsys=1) or user code (guardsys=0). If flag force
is
//
set, guarding is forced dependless on its current state.
static
void Setguard(int guardsys,int force) {
 
int i;
 
t_memory *pmem;
 
t_module *pmod;
 
t_code c,*pc;
 
if (force)
   
Deletesorteddatarange(&code,0x00000000,0xFFFFFFFF);
 
// Walk all memory blocks. Note that this function is called when
process is
 
// paused, so memory list can't change.
 
for (i=0; i<memory.sorted.n;
i++) {
   
// Get next memory block.
   
pmem=(t_memory *)Getsortedbyindex(&memory.sorted,i);
   
if (pmem==NULL)
     
continue;                       
// In fact, severe internal error
    if ((pmem->type & MEM_GAP)!=0)
 
    continue;            
           // Unallocated memory
   
// Check whether it contains executable code.
   
if ((pmem->type & (MEM_CODE|MEM_SFX))==0)
     
continue;                       
// Not a code
   
// Get code descriptor, or, if descriptor is missing, create new one.
   
pc=(t_code *)Findsorteddata(&code,pmem->base,0);
   
if (pc==NULL || pc->base!=pmem->base ||
pc->size!=pmem->size) {
     
// Note that it makes no harm if guarding remains set.
     
memset(&c,0,sizeof(c));
     
c.base=pmem->base;
     
c.size=pmem->size;
     
pc=(t_code *)Addsorteddata(&code,&c);
     
if (pc==NULL) continue;
};       // Low
memory or internal error
   
// If memory block is already guarded, do nothing.
   
if (pc->type & CODE_GUARDED)
     
continue;
   
// Check whether code belongs to the system DLL.
   
if ((pc->type & (CODE_USER|CODE_SYS))==0) {
     
pmod=Findmodule(pmem->base);
     
if (pmod==NULL)
continue;       
// Code without module?..
     
if (pmod->type & MOD_SYSTEMDLL)
       
pc->type|=CODE_SYS;
     
else
       
pc->type|=CODE_USER;
     
;
   
};
   
if ((guardsys==0 && (pc->type &
CODE_USER)!=0) ||
     
(guardsys!=0 && (pc->type & CODE_SYS)!=0)
   
) {
     
Guardmemory(pmem->base,pmem->size,1);
     
pc->type|=CODE_GUARDED;
   
};
 
};
};
//
Unguards code block containing specified address.
static
void Unguardblock(ulong addr) {
 
t_code *pc;
 
pc=(t_code *)Findsorteddata(&code,addr,0);
 
if (pc!=NULL && (pc->type &
CODE_GUARDED)!=0) {
   
Guardmemory(pc->base,pc->size,0);
   
pc->type&=~CODE_GUARDED;
 
};
};
//
Unguards all code blocks.
static
void Unguardall(void) {
 
int i;
 
t_memory *pmem;
 
Deletesorteddatarange(&code,0x00000000,0xFFFFFFFF);
 
Deletesorteddatarange(&retlist,0x00000000,0xFFFFFFFF);
 
restoretempbp=0; 
 
// Walk all memory blocks. Note that this function is called when
process is
 
// paused, so memory list can't change.
 
for (i=0; i<memory.sorted.n;
i++) {
   
// Get next memory block.
   
pmem=(t_memory *)Getsortedbyindex(&memory.sorted,i);
   
if (pmem==NULL)
     
continue;                       
// In fact, severe internal error
    if ((pmem->type & MEM_GAP)!=0)
 
    continue;            
           // Unallocated memory
   
// Check whether it contains executable code.
   
if ((pmem->type & (MEM_CODE|MEM_SFX))==0)
     
continue;                       
// Not a code
   
Guardmemory(pmem->base,pmem->size,0);
 
};
};
//
Sets state of the variable trace. If state changes from TRACE_OFF to any
//
other, or back to TRACE_OFF, writes message to log.
static
void Settracestate(int state) {
 
if (trace==TRACE_OFF && state!=TRACE_OFF)
   
Addtolist(0,DRAW_NORMAL,L"API
trace is ON");
 
else if (trace!=TRACE_OFF && state==TRACE_OFF)
   
Addtolist(0,DRAW_NORMAL,L"API
trace is OFF");
 
trace=state;
};
////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////
CALL DATA ///////////////////////////////////
//
The user usually wants to know not only which API functions are called,
but
//
even more from which locations. Most APIs are called from one or two
places,
//
but in extreme cases (EnterCriticalSection(), SelectObject()) there may
be
//
hundreds. Therefore list of fixed size is not an option. I use linked
lists
//
of records of type t_call. Free records reside in a separate list
pointed to
//
by pfreecall. If there are no nore free call records, I allocate new
extent
//
containing NCALL records at once. Extents (t_cext) are also chained.
//
//
After call location is added, plugin keeps it in the list till
application
//
terminates. Therefore there are no routines that return call locations
back
//
to the list of free calls, the only option is to discard all data at
once.
//
Descriptor of call data extent. If there are no more free call
locations,
//
plugin allocates next block and places it before the previous.
typedef
struct t_cext
{               
// Call data extent
 
t_call        
call[NCALL];         
// Block of call addresses
 
struct t_cext 
*nextextent;         
// Pointer to next data extent or NULL
}
t_cext;
static
t_cext   
*extentlist;         
// Pointer to first call data extent
//
Allocates new call data extent and adds all its elements to the chain of
//
free calls. Returns 0 on success and -1 on error.
static
int Allocatenewextent(void) {
 
int i;
 
t_cext *pext;
 
t_call *pcall;
 
// Allocate new data extent.
 
pext=(t_cext *)Memalloc(sizeof(t_cext),ZEROINIT);
 
if (pext==NULL)
   
return
-1;                        
// Low memory
 
// Add elements to the chain of free calls.
 
for (i=0,pcall=pext->call; i<NCALL; i++,pcall++) {
   
pcall->next=pfreecall;
   
pfreecall=pcall; };
 
// Add extent to the chain of extents.
 
pext->nextextent=extentlist;
 
extentlist=pext;
 
return 0;
};
//
Gets free call descriptor. On success, removes it from the queue of free
//
calls and returns pointer. On error, returns NULL.
static
t_call *Getfreecall(void) {
 
t_call *pcall;
 
// Assure that we have free call records.
 
if (pfreecall==NULL) {
   
Allocatenewextent();
   
if (pfreecall==NULL) return NULL; };
 
// Get free call record.
 
pcall=pfreecall;
 
pfreecall=pcall->next;
 
pcall->next=NULL;                   
// Just a precaution
 
return pcall;
};
//
Frees all call data.
static
void Discardcalldata(void) {
 
int i;
 
t_cext *pext;
 
t_api *pa;
 
// Walk list of extents and free all data extents.
 
while (extentlist!=NULL) {
   
pext=extentlist->nextextent;
   
Memfree(extentlist);
   
extentlist=pext; };
 
// There are no more free call descriptors.
 
pfreecall=NULL;
 
// For the case that API list is not yet freed, discard call data in
the list.
 
for (i=0; i<api.sorted.n; i++) {
   
pa=(t_api *)Getsortedbyindex(&(api.sorted),i);
   
if (pa==NULL) continue;
   
pa->call=NULL;
 
};
};
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////
API LIST ///////////////////////////////////
//
Information about called API functions is kept in the table api. This
//
section contains code necessary to maintain and display API list window.
//
Menu function that processes
command "Follow API in Disassembler".
static
int Mfollowapi(t_table
*pt,wchar_t *name,ulong index,int mode) {
 
t_api *papi;
 
papi=(t_api *)Getsortedbyselection(&(api.sorted),
   
api.sorted.selected);
 
if (papi==NULL)
   
return
MENU_ABSENT;               
// No selection
 
if (mode==MENU_VERIFY)
   
return MENU_NORMAL;
 
else if (mode==MENU_EXECUTE) {
   
Setcpu(0,papi->addr,0,0,0,CPU_ASMHIST|CPU_ASMCENTER|CPU_ASMFOCUS);
   
return MENU_REDRAW; };
 
return MENU_ABSENT;
};
//
Menu function that
processes INT3 breakpoint-related
commands "Toggle"
//
(index=0), "Conditional" (index=1) and "Conditional log" (index=2). Note
//
that found locations are sure code, we don't need to verify them.
static
int Mbreakpoint(t_table
*pt,wchar_t *name,ulong index,int mode) {
 
int answer;
 
t_api *papi;
 
t_bpoint *pbrk;
 
POINT coord;
 
papi=(t_api *)Getsortedbyselection(&(api.sorted),
   
api.sorted.selected);
 
if (papi==NULL)
   
return
MENU_ABSENT;               
// No selection
 
if (mode==MENU_VERIFY)
   
return MENU_NORMAL;
 
else if (mode==MENU_EXECUTE) {
   
pbrk=(t_bpoint
*)Findsorteddata(&(bpoint.sorted),papi->addr,0);
   
if (index==0) {
     
// Toggle unconditional breakpoint.
     
if (pbrk!=NULL && pbrk->type & BP_MANUAL)
       
Removeint3breakpoint(papi->addr,BP_MANUAL);
     
else {
       
answer=Setint3breakpoint(papi->addr,BP_MANUAL|BP_BREAK,
         
0,0,0,0,L"",L"",L"");
       
if (answer!=0) {
         
Flash(L"Unable to set
breakpoint");
         
return MENU_NOREDRAW;
       
};
     
}; }
   
else {
     
// Set conditional breakpoint (simple and full forms).
     
if (Gettableselectionxy(&api,2,&coord)<0)
       
coord.x=coord.y=-1;           
// Unknown coordinates, use defaults
     
if (index==1)
answer=Condbreakpoint(pt->hw,&(papi->addr),1,NULL,
       
coord.x,coord.y,api.font);
     
else
answer=Condlogbreakpoint(pt->hw,&(papi->addr),1,0,NULL,
       
coord.x,coord.y,api.font);
     
if (answer<0) Flash(L"Unable
to set breakpoint");
     
if (answer<=0) return MENU_NOREDRAW; };
   
return MENU_REDRAW; };
 
return MENU_ABSENT;
};
//
Callback function that fills Browse
code location dialog with the list
of
//
call locations.
int
Browsecallcallback(int
index,void *data,ulong *addr,wchar_t *s) {
 
t_call **pcall;
 
if (index<0 || data==NULL || addr==NULL || s==NULL)
   
return
0;                         
// Error in input parameters
 
pcall=(t_call **)data;
 
if (*pcall==NULL)
   
return
0;                         
// No more call locations
 
*addr=(*pcall)->from;
 
if (*addr==0)
   
StrcopyW(s,TEXTLEN,L"<unknown
location>");
 
else
   
Decoderelativeoffset(*addr,DM_MODNAME,s,TEXTLEN);
 
*pcall=(*pcall)->next;
 
return
1;                           
// Code location is available
};
//
Menu function that
processes command "Follow call in Disassembler"
(index=0)
//
and "Browse calls in Disassembler" (index=1).
static
int Mfollowcall(t_table
*pt,wchar_t *name,ulong index,int mode) {
 
t_api *papi;
 
t_call *pcall;
 
papi=(t_api *)Getsortedbyselection(&(api.sorted),
   
api.sorted.selected);
 
if (papi==NULL)
   
return
MENU_ABSENT;               
// No selection
 
if (mode==MENU_VERIFY) {
   
if (papi->ncall==1 && index==0)
     
return MENU_NORMAL;
   
if (papi->ncall>1 && index==1)
     
return MENU_NORMAL;
   
return MENU_ABSENT; }
 
else if (mode==MENU_EXECUTE) {
   
pcall=papi->call;
   
if (papi->ncall==1 && pcall!=NULL
&& pcall->from!=0)
     
Setcpu(0,pcall->from,0,0,0,CPU_ASMHIST|CPU_ASMCENTER|CPU_ASMFOCUS);
   
else if (papi->ncall>1)
     
Browsecodelocations(pt->hparent,L"Select
call location",
     
Browsecallcallback,(void *)&pcall);
   
return MENU_REDRAW; };
 
return MENU_ABSENT;
};
//
Menu function that
processes breakpoint-related
commands "Breakpoint on
all
//
calls" (index=0), "Conditional breakpoint on all calls" (index=1),
//
"Conditional log breakpoint on all calls" (index=2) and "Remove
breakpoint
//
from all calls" (index=3).
static
int Mbreakpointall(t_table
*pt,wchar_t *name,ulong index,int mode) {
 
int nlist,hasbp,nobp,ncond,success,answer;
 
ulong *list;
 
t_api *papi;
 
t_call *pcall;
 
t_bpoint *pbrk;
 
POINT coord;
 
papi=(t_api *)Getsortedbyselection(&(api.sorted),
   
api.sorted.selected);
 
if (papi==NULL)
   
return
MENU_ABSENT;               
// No selection
 
if (mode==MENU_VERIFY) {
   
// Calculate how many calls already have breakpoints.
   
hasbp=nobp=ncond=0;
   
pcall=papi->call;
   
while (pcall!=NULL) {
     
pbrk=(t_bpoint
*)Findsorteddata(&(bpoint.sorted),pcall->from,0);
     
if (pbrk==NULL || (pbrk->type & BP_MANUAL)==0)
       
nobp++;                       
// No breakpoint
     
else {
       
hasbp++;                      
// Breakpoint set
       
if (pbrk->type & BP_COND) ncond++; };
     
pcall=pcall->next;
   
};
   
// Check whether menu item applies.
   
if (index==0 && nobp==0 && ncond==0)
     
return MENU_ABSENT;
   
if ((index==1 || index==2) && hasbp+nobp==0)
     
return MENU_ABSENT;
   
if (index==3 && hasbp==0)
     
return MENU_ABSENT;
   
return MENU_NORMAL; }
 
else if (mode==MENU_EXECUTE) {
   
if (index==1 || index==2) {
     
list=(ulong
*)Memalloc(papi->ncall*sizeof(ulong),SILENT|ZEROINIT);
     
if (list==NULL) {
       
Flash(L"Low memory"); return
MENU_NOREDRAW; };
     
nlist=0;
     
pcall=papi->call;
     
while (pcall!=NULL && nlist<papi->ncall) {
       
list[nlist++]=pcall->from;
       
pcall=pcall->next; };
     
if (Gettableselectionxy(pt,2,&coord)<0)
       
coord.x=coord.y=-1;           
// Unknown coordinates, use defaults
     
if (index==1) answer=Condbreakpoint(pt->hw,list,nlist,NULL,
       
coord.x,coord.y,pt->font);
     
else answer=Condlogbreakpoint(pt->hw,list,nlist,0,NULL,
       
coord.x,coord.y,pt->font);
     
if (answer<0)
       
Flash(L"Unable to set all
breakpoints");
     
Memfree(list); }
   
else {
     
pcall=papi->call;
     
success=1;
     
while (pcall!=NULL) {
       
if (index==0)
answer=Setint3breakpoint(pcall->from,BP_MANUAL|BP_BREAK,
         
0,0,0,0,L"",L"",L"");
       
else
         
answer=Removeint3breakpoint(pcall->from,BP_MANUAL);
       
if (answer!=0) success=0;
       
pcall=pcall->next; };
     
if (success==0) {
       
if (index==0) Flash(L"Unable
to set all breakpoints");
       
else Flash(L"Unable to
remove all breakpoints");
     
};
   
};
   
return MENU_REDRAW; };
 
return MENU_ABSENT;
};
static
t_menu apimenu[] =
{           
// Menu of the API list window
 
{ L"Follow API in Disassembler",
      
L"Follow address of selected API function in CPU Disassembler",
      
K_NONE, Mfollowapi, NULL, 0 },
 
{ L"|Toggle breakpoint on API",
      
L"Toggle unconditional breakpoint on selected API function",
      
K_BREAK, Mbreakpoint, NULL, 0 },
 
{ L"Conditional breakpoint on API...",
      
L"Set or edit conditional breakpoint on selected API function",
      
K_CONDBREAK, Mbreakpoint, NULL, 1 },
 
{ L"Conditional log on API...",
      
L"Set or edit conditional logging breakpoint on selected API function",
      
K_LOGBREAK, Mbreakpoint, NULL, 2 },
 
{ L"|Follow call in Disassembler",
      
L"Go to location where API selected function was called",
      
K_FOLLOWDASM, Mfollowcall, NULL, 0 },
 
{ L"Browse calls in Disassembler",
      
L"Show list of locations from which selected API function was called",
      
K_FOLLOWDASM, Mfollowcall, NULL, 1 },
 
{ L"|Breakpoint on all calls",
      
L"Set unconditional breakpoint on all call locations",
      
K_BREAKALL, Mbreakpointall, 0, 0 },
 
{ L"Conditional breakpoint on all calls",
      
L"Set or edit conditional breakpoint on all call locations",
      
K_CONDBPALL, Mbreakpointall, 0, 1 },
 
{ L"Conditional log on all calls",
      
L"Set or edit conditional logging breakpoint on all call locations",
      
K_LOGBPALL, Mbreakpointall, 0, 2 },
 
{ L"Remove breakpoint from all calls",
      
L"Remove breakpoint from all call locations",
      
K_DELBPALL, Mbreakpointall, 0, 3 },
 
{ L"|>STANDARD",
      
L"",                           
// Forwarder to standard menus
      
K_NONE, NULL, NULL, 0
 
}
};
//
Function that compares
two APIs according to the specified criterium.
//
Returns -1 if first item is "lower" (should precede second on the
screen),
//
+1 if first item is "higher" and 0 if they are equal. Criterium n is the
//
index of the column in the table.
int
Apisortfunc(const t_sorthdr
*sh1,const t_sorthdr
*sh2,const int n) {
 
int i=0;
 
wchar_t s1[TEXTLEN],s2[TEXTLEN];
 
t_api *a1,*a2;
 
a1=(t_api *)sh1;
 
a2=(t_api *)sh2;
 
if (n==1)
{                         
// Sort by name, may be time-consuming
   
Decodeaddress(a1->addr,a1->addr,
     
DM_VALID|DM_INMOD|DM_NOMODNAME,s1,TEXTLEN,NULL);
   
Decodeaddress(a2->addr,a2->addr,
     
DM_VALID|DM_INMOD|DM_NOMODNAME,s2,TEXTLEN,NULL);
   
i=_wcsnicmp(s1,s2,TEXTLEN); }
 
else if (n==2)
{                    
// Sort by hit count
   
if (a1->count>a2->count) i=-1;
   
else if (a1->count<a2->count) i=1; };
 
if (i==0)
{                         
// Additionally sort by API address
   
if (a1->addr<a2->addr) i=-1;
   
else if (a1->addr>a2->addr) i=1; };
 
return i;
};
//
Drawing function of API
list window.
int
Apidraw(wchar_t *s,uchar *mask,int *select,
 
t_table *pt,t_drawheader *ph,int
column,void *cache) {
 
int m,n,color;
 
ulong mode;
 
wchar_t text[TEXTLEN];
 
t_bpoint *pbrk;
 
t_api *papi;
 
t_call *pcall;
 
n=0;
 
// For simple tables, t_drawheader
is the pointer to the data element.
It
 
// can't be NULL, except in DF_CACHESIZE, DF_FILLCACHE and DF_FREECACHE.
 
papi=(t_api *)ph;
 
switch (column) {
   
case
DF_CACHESIZE:                
// Request for draw cache size
     
return 0;
   
case
DF_FILLCACHE:                
// Request to fill draw cache
     
break;
   
case
DF_FREECACHE:                
// Request to free cached resources
     
break;
   
case
DF_NEWROW:                   
// Request to start new row in window
     
break;
   
case
0:                           
// API address
     
mode=ADDR_HEXONLY|ADDR_CHECKEIP;
     
pbrk=(t_bpoint
*)Findsorteddata(&(bpoint.sorted),papi->addr,0);
     
if (pbrk!=NULL && (pbrk->type &
BP_MANUAL)!=0) {
       
if (pbrk->type & BP_DISABLED) mode|=ADDR_DISBRK;
       
else if (pbrk->type & BP_COND) mode|=ADDR_CONDBRK;
       
else mode|=ADDR_BREAK; };
     
n=Labeladdress(s,papi->addr,0,REG_UNDEF,0,mask,select,mode);
     
break;
   
case
1:                           
// API name
     
n=Decodeaddress(papi->addr,papi->addr,
       
DM_VALID|DM_INMOD|DM_MODNAME,s,TEXTLEN,NULL);
     
break;
   
case
2:                           
// Hit count
     
n=swprintf(s,L"%i.",papi->count);
     
break;
   
case
3:                           
// Call locations
     
pcall=papi->call;
     
while (pcall!=NULL && n<TEXTLEN-1) {
       
if (n>0) {
         
m=StrcopyW(s+n,TEXTLEN-n,L",
");
         
if (m!=0) memset(mask+n,DRAW_NORMAL,m);
         
n+=m; };
       
Hexprint8W(text,pcall->from);
       
m=StrcopyW(s+n,TEXTLEN-n,text);
       
pbrk=(t_bpoint
*)Findsorteddata(&(bpoint.sorted),pcall->from,0);
       
if (pbrk==NULL || (pbrk->type & BP_MANUAL)==0)
         
color=DRAW_NORMAL;          
// No user INT3 breakpoint
       
else if (pbrk->type & BP_DISABLED)
         
color=DRAW_BDIS;            
// Disabled breakpoint
       
else if (pbrk->type & BP_COND)
         
color=DRAW_COND;            
// Conditional breakpoint
       
else
         
color=DRAW_BREAK;           
// Unconditional breakpoint
       
if (m!=0) memset(mask+n,color,m);
       
n+=m;
       
pcall=pcall->next; };
     
if (n>=TEXTLEN-1) {
       
s[n-1]=L'.'; mask[n-1]=DRAW_NORMAL;
       
s[n-2]=L'.'; mask[n-2]=DRAW_NORMAL;
       
s[n-3]=L'.'; mask[n-3]=DRAW_NORMAL; };
     
*select|=DRAW_MASK;             
// Uses mask
     
break;
   
default: break;
 
};
 
return n;
};
//
Custom table function of API list window. This function is called on
//
WM_DESTROY, WM_CLOSE (by returning -1, you can prevent window from
closing),
//
WM_SIZE (custom tables only), WM_CHAR (only if TABLE_WANTCHAR is set)
and
//
different custom messages WM_USER_xxx (depending on table type). See
//
documentation for details.
long
Apifunc(t_table *pt,HWND hw,UINT msg,WPARAM wp,LPARAM lp) {
 
t_api *papi;
 
t_call *pcall;
 
switch (msg) {
   
case WM_USER_CREATE:
     
// Start autoupdating of the API list window. This means that OllyDbg
     
// will periodically send WM_USER_UPD messages. Whether the window will
     
// be repainted or not depends on the reaction on this message.
     
Setautoupdate(&api,1);
     
break;
   
case
WM_USER_UPD:                 
// Autoupdate contents of the window
     
if (trace!=TRACE_OFF && run.status!=STAT_IDLE
&&
       
run.status!=STAT_PAUSED && run.status!=STAT_FINISHED)
       
InvalidateRect(pt->hw,NULL,FALSE);
     
break;
   
case
WM_USER_DBLCLK:              
// Doubleclick
     
// Get selection.
     
papi=(t_api *)Getsortedbyselection(
       
&(pt->sorted),pt->sorted.selected);
     
if (papi!=NULL) {
       
if (HIWORD(wp)==3)
         
// Doubleclick in the column 3 (list of call locations). If there is
         
// only one call location, follow it in CPU Disassembler pane. If
         
// there are several locations, open browsing dialog. I don't want
         
// to repeat the functionality of Mfollowcall(), therefore I reuse
         
// menu function.
         
Callmenufunction(&api,apimenu,Mfollowcall,papi->ncall==1?0:1);
       
else
         
// Columns 0 to 2. Follow address of the API function in the CPU
         
// Disassembler pane.
         
Setcpu(0,papi->addr,0,0,0,CPU_ASMHIST|CPU_ASMCENTER|CPU_ASMFOCUS);
       
;
     
};
     
return 1;
   
default: break;
 
};
 
return 0;
};
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////
PLUGIN OPTIONS ////////////////////////////////
static
t_control traceapioptions[] = {
 
{ CA_COMMENT,
-1,             
0,   0,   0,   0,
NULL,
                 
PLUGINNAME,
                 
NULL },
 
{ CA_TITLE,  
OPT_TITLE,     
80,   4, 160,  15, NULL,
                 
PLUGINNAME,
                 
NULL },
 
{ CA_CHECK,  
OPT_1,         
90,  26, 120,  10, &protocolapi,
                 
L"Protocol API calls",
                 
L"Protocol calls and call arguments to the log" },
 
{ CA_CHECK,  
OPT_2,        
105,  38, 160,  10, &skipgdi32,
                 
L"Don't protocol GDI32",
                 
L"Ignore all APIs that reside in GDI32" },
 
{ CA_CHECK,  
OPT_3,        
105,  50, 160,  10, &skipuser32,
                 
L"Don't protocol USER32",
                 
L"Ignore all APIs that reside in USER32" },
 
{ CA_CHECK,  
OPT_4,        
105,  62, 160,  10, &skipkernel32,
                 
L"Don't protocol KERNEL32",
                 
L"Ignore all APIs that reside in KERNEL32" },
 
{ CA_CHECK,  
OPT_5,        
105,  74, 160,  10, &skipmsvc,
                 
L"Don't protocol MSVC*",
                 
L"Ignore all APIs that reside in Visual C libraries" },
 
{ CA_CHECK,  
OPT_6,        
105,  86, 160,  10, &protocolret,
                 
L"Protocol returned values",
                 
L"Protocol values that called API function returned" },
 
{ CA_END,    
-1,             
0,   0,   0,   0,
NULL,
                 
NULL,
                 
NULL
 
}
};
// Optional entry, implements plugin's options page in the Plugin options
// dialog. First time this function is called with msg=WM_INITDIALOG, wp=0 and
// lp=0. Note that this is the artificial message. If plugin wants to insert
// its own page (or even several pages), it must return pointer to page
// description (array of structures t_control). Each new page must begin with
// CA_COMMENT. When user makes any actions and dialog receives WM_COMMAND, it
// passes this message here. Returned value is ignored. Finally, after user
// pressed OK or Cancel, this entry is called for the last time with
// msg=WM_CLOSE and wp=1 on OK and 0 on cancel. This is the best time for
// plugin to adopt new settings. Do not forget to undo custom controls on
// Cancel!
extc t_control * __cdecl ODBG2_Pluginoptions(UINT msg,WPARAM wp,LPARAM lp) {
  if (msg==WM_CLOSE && wp!=0) {
    // User pressed OK in the Plugin options dialog. Options are updated, save
    // them to the .ini file.
    Writetoini(NULL,PLUGINNAME,L"Protocol API calls",L"%i",protocolapi);
    Writetoini(NULL,PLUGINNAME,L"Skip GDI32",L"%i",skipgdi32);
    Writetoini(NULL,PLUGINNAME,L"Skip USER32",L"%i",skipuser32);
    Writetoini(NULL,PLUGINNAME,L"Skip KERNEL32",L"%i",skipkernel32);
    Writetoini(NULL,PLUGINNAME,L"Skip MSVC",L"%i",skipmsvc);
    Writetoini(NULL,PLUGINNAME,L"Protocol returned values",L"%i",protocolret);
  };
  // It makes no harm to return page descriptor on all messages.
  return traceapioptions;
};
////////////////////////////////////////////////////////////////////////////////
///////////////////////////
EVENTS AND NOTIFICATIONS ///////////////////////////
//
When trace starts, plugin guards user code and assumes that debuggee is
in
//
the system code. When guarded page is hit, OllyDbg passes event to
//
ODBG2_Pluginexception(). Let's assume that thread is in the user code
now.
//
Plugin should guard system code and unguard user. But such actions are
//
time-consuming, so plugin assumes that code is usually compact and
removes
//
protection only from the code block that was hit. Now plugin guards
system
//
code and returns PE_CONTINUE (continue execution, keep run status).
//
//
When debuggee calls API function, another exception occurs. Plugin
//
determines call and return addresses and adds them to the list. If
plugin
//
is requested to protocol returned values, it also sets temporary
breakpoint
//
with action flag BA_PLUGIN (report break to plugins) on the address of
//
return. Some other plugins may also set breakpoint here. To be sure,
plugin
//
adds this address to the sorted data retlist.
//
//
Why is retlist necessary? Were it not sufficient to save return address
to
//
some variable? No. Consider following scenario: Debugged application
calls
//
DispatchMessage(). This API function passes message to the user-defined
//
window function. Function calls another API, say, EnumChildWindows().
This
//
calls supplied callback which calls third API, GetWindowText(). Now we
have
//
three nested APIs, and in theory there is no depth limit. Even recursive
//
calls are possible!
//
//
Temporary breakpoint (BP_TEMP) is automatically removed when hit. If
there
//
is a recursive call, return values from the previous calls will not be
//
protocolled. To avoid this, retlist items count calls and mark removed
//
breakpoints. They are restored when execution returns into the system
code.
//
//
But what if we are waiting for system code and user code accesses data
in
//
the code block of system code, or vice versa? Breaks on access do not
//
distinguish between execution, reading or writing. We can't restore
break
//
instantly, first we must execute the command. What should we do? In this
//
case ODBG2_Pluginexception() returns PE_STEP. OllyDbg sets temporary
//
breakpoint (INT3 or hardware) with action BA_PLUGIN on the next command
and
//
plugin gets chance to restore protection when breakpoint is hit and
//
ODBG2_Plugintempbreakpoint() is called.
//
//
When there are several concurrent threads, it may happen that one
thread is
//
in the user code and another in system. The resulting chaos will make
//
reliable trace impossible. Therefore only one thread is allowed. When
user
//
runs application (STAT_RUNNING), ODBG2_Pluginnotify() intercepts this
action
//
and substitutes with STAT_RUNTHR.
//
Optional entry, called when Windows report exception (debugging event of
//
type EXCEPTION_DEBUG_EVENT in the debugged application).
extc
int __cdecl ODBG2_Pluginexception(t_run *prun,const t_disasm *da,
 
t_thread *pthr,t_reg *preg,wchar_t *message) {
 
int i,n,nstack,narg;
 
ulong addr,amem,aret,acall,size,from,stack[NARG+1];
 
wchar_t name[TEXTLEN],s[TEXTLEN];
 
t_module *pmod;
 
t_argdec adec[NARG];
 
t_memory *pmem;
 
t_ret ret,*pret;
 
t_api a,*pa;
 
t_call *pcall,**ppcall;
 
// If trace is inactive, or there is an error in parameters, do nothing.
 
if (trace==TRACE_OFF || prun==NULL || da==NULL || preg==NULL)
   
return PE_IGNORED;
 
// Traceapi processes only guarded page exceptions.
 
if
(prun->de.u.Exception.ExceptionRecord.ExceptionCode!=EXCEPTION_GUARD_PAGE)
   
return PE_IGNORED;
 
// Address of command that caused exception.
 
addr=(ulong)prun->de.u.Exception.ExceptionRecord.ExceptionAddress;
 
// Address of guarded memory block.
 
amem=(ulong)prun->de.u.Exception.ExceptionRecord.ExceptionInformation[1];
 
// If guarded memory block is in the memory area opposite to expected,
remove
 
// guard. We didn't do it before to spare time.
 
pmod=Findmodule(amem);
 
if (pmod==NULL)
   
return
PE_IGNORED;                
// Better ideas?
 
if (((pmod->type & MOD_SYSTEMDLL)!=0 &&
trace==TRACE_TILLUSER) ||
   
((pmod->type & MOD_SYSTEMDLL)==0 &&
trace==TRACE_TILLSYS)
 
) {
   
Unguardblock(amem); return PE_CONTINUE; };
 
pmod=Findmodule(addr);
 
// If address is in the wrong area, most probably command accesses
protected
 
// data and must be stepped. Note that we must restore guarding after
 
// temporary breakpoint is hit.
 
if (trace==TRACE_TILLUSER && pmod!=NULL
&& (pmod->type & MOD_SYSTEMDLL)!=0)
   
return
PE_STEP;                   
// User code protected but break in sys
 
if (trace==TRACE_TILLSYS && (pmod==NULL ||
(pmod->type & MOD_SYSTEMDLL)==0))
   
return
PE_STEP;                   
// Sys code protected but break in user
 
if (trace==TRACE_TILLUSER) {
   
// We have reached user code. Unguard user code block and guard system
code.
   
Unguardblock(addr);
   
Setguard(1,0);
   
Settracestate(TRACE_TILLSYS); }
 
else {
   
// We have reached system code. If necessary, restore temporary
breakpoints
   
// on return address.
   
if (restoretempbp)
{              
// Rare event
     
for (i=0; i<retlist.n; i++) {
       
pret=(t_ret *)Getsortedbyindex(&retlist,i);
       
if (pret==NULL || (pret->type & RET_ACTIVE)!=0) continue;
       
Setint3breakpoint(pret->addr,BP_TEMP,-1,-1,-1,BA_PLUGIN,NULL,NULL,NULL);
       
pret->type|=RET_ACTIVE;
     
};
     
restoretempbp=0;
   
};
   
// Unguard this block and guard user code.
   
Unguardblock(addr);
   
Setguard(0,0);
   
Settracestate(TRACE_TILLUSER);
   
// Read stack and determine the caller.
   
size=Readmemory(stack,preg->r[REG_ESP],sizeof(stack),MM_SILENT|MM_PARTIAL);
   
nstack=size/sizeof(ulong);
   
if (nstack==0)
     
aret=from=0;
   
else {
     
aret=stack[0];
     
from=Isretaddr(aret,NULL);
     
if (from==0) aret=0; };
   
// Add entry to API list.
   
pa=(t_api *)Findsorteddata(&(api.sorted),addr,0);
   
if (pa==NULL)
{                   
// First time the API is called
     
memset(&a,0,sizeof(a));
     
a.addr=addr;
     
a.size=1;
     
pa=(t_api *)Addsorteddata(&(api.sorted),&a); };
   
if (pa!=NULL)
{                   
// Opposite would mean severe error
     
pa->count++;
     
// Even when from is 0, I add it to the call list, as an indication that
     
// call origin is unknown.
     
ppcall=&(pa->call);
     
while (1) {
       
if (*ppcall!=NULL &&
(*ppcall)->from<from) {
         
ppcall=&((*ppcall)->next);
         
continue; };
       
if (*ppcall!=NULL && (*ppcall)->from==from)
         
break;                      
// Call address is already in the chain
       
pcall=Getfreecall();
       
if (pcall==NULL)
         
break;                      
// Low memory
       
pcall->from=from;
       
pcall->next=*ppcall;
       
*ppcall=pcall;
       
pa->ncall++;
       
break;
     
};
   
};
   
if (protocolapi) {
     
// If requested not to protocol some functions, check whether this is
     
// the skipped function.
     
if ((skipgdi32!=0 &&
_wcsicmp(pmod->modname,L"GDI32")==0) ||
       
(skipuser32!=0 &&
_wcsicmp(pmod->modname,L"USER32")==0) ||
       
(skipkernel32!=0 &&
_wcsicmp(pmod->modname,L"KERNEL32")==0) ||
       
(skipmsvc!=0 &&
_wcsnicmp(pmod->modname,L"MSVC",4)==0)) ;
     
else {
       
// If requested, set temporary breakpoint on return address to protocol
       
// the value returned by the API. I add this address to retlist so that
       
// I know that this is my breakpoint. Note possible source of errors:
       
// I silently assume that if call is recursive, it always calls the
       
// same API function.
       
if (protocolret!=0 && aret!=0 &&
from!=0) {
         
Setint3breakpoint(aret,BP_TEMP,-1,-1,-1,BA_PLUGIN,NULL,NULL,NULL);
         
pret=(t_ret *)Findsorteddata(&retlist,aret,0);
         
if (pret==NULL) {
           
ret.addr=aret;
           
ret.size=1;
           
ret.type=0;
           
ret.aapi=addr;
           
ret.from=from;
           
ret.count=0;
           
pret=(t_ret *)Addsorteddata(&retlist,&ret); };
         
if (pret!=NULL) {
           
pret->type|=RET_ACTIVE;    // BP_TEMP
breakpoint set
           
pret->count++;            
// Recursive if count>1
         
};
       
};
       
// Decode arguments. Note that first doubleword in the stack is return
       
// address.
       
if (nstack<=1)
         
narg=0;
       
else {
         
for (i=0; i<NARG; i++) {
           
if (i<nstack-1) {
             
adec[i].mode=ADEC_VALID|ADEC_CHGNAME;
             
adec[i].value=stack[i+1]; }
           
else {
             
adec[i].mode=0;
             
adec[i].value=0;
           
};
           
adec[i].pushaddr=0;
         
};
         
narg=Decodeknownbyaddr(addr,NULL,adec,NULL,name,0,1);
       
};
       
// Write name of function and, if available, call address to log.
       
n=StrcopyW(s,TEXTLEN,L"Call to ");
       
n+=Decodeaddress(addr,0,DM_WIDEFORM|DM_MODNAME,s+n,TEXTLEN-n,NULL);
       
if (from!=0) {
         
n+=StrcopyW(s+n,TEXTLEN-n,L" from ");
         
Decodeaddress(from,0,DM_WIDEFORM|DM_MODNAME,s+n,TEXTLEN-n,NULL); };
       
Addtolist(from,DRAW_HILITE,L"%s",s);
       
// Write arguments to log.
       
for (i=0; i<narg; i++) {
         
if (adec[i].name[0]==L'=')     //
Substituted argument without name
            Addtolist(0,DRAW_NORMAL,L"  %08X 
%s",adec[i].value,adec[i].text);
         
else {
            Addtolist(0,DRAW_NORMAL,L"  %08X  %s = %s",
           
adec[i].value,adec[i].name,adec[i].text);
         
};
       
};
     
};
   
};
 
};
 
// Continue execution.
 
return PE_CONTINUE;
};
//
Optional entry, called when temporary breakpoint BP_TEMP with action
flag
//
BA_PLUGIN is hit. Note that several plugins may set temporary breakpoint
//
on the same command. Each BA_PLUGIN breakpoint is reported to all
plugins.
extc
void __cdecl ODBG2_Plugintempbreakpoint(ulong addr,const t_disasm * da,
 
t_thread *pthr,t_reg *preg) {
 
int n;
 
wchar_t s[TEXTLEN],type[TEXTLEN];
 
t_ret *pret;
 
pret=(t_ret *)Findsorteddata(&retlist,addr,0);
 
if (pret!=NULL) {
   
// Write name of called function to log.
   
Decodeaddress(pret->aapi,0,DM_WIDEFORM|DM_MODNAME,s,TEXTLEN,NULL);
    Addtolist(addr,DRAW_HILITE,L"Return from %s",s);
   
if (preg!=NULL) {
     
// For known functions, Analyser saves type of return in the prefixed
     
// name of type NM_RETTYPE. Refer to the documentation to learn what
     
// prefix is.
     
if (FindnameW(addr,NM_RETTYPE,type,TEXTLEN)!=0)
       
Decodeargument(NULL,type,preg->r+REG_EAX,sizeof(ulong),
       
s,TEXTLEN,NULL);
     
else
       
s[0]=L'\0';
     
Addtolist(0,DRAW_NORMAL,L"  EAX = %08X 
%s",preg->r[REG_EAX],s);
   
};
   
pret->count--;
   
if (pret->count==0)
     
Deletesorteddata(&retlist,addr,0);
   
else {
     
pret->type&=~RET_ACTIVE;        
// Temp breakpoint is autoremoved
     
restoretempbp=1;
   
}; }
 
else if (trace==TRACE_TILLSYS)
   
Setguard(1,1);
 
else if (trace==TRACE_TILLUSER)
   
Setguard(0,1);
 
;
};
//
Optional entry, notifies plugin on relatively infrequent events.
extc
void __cdecl ODBG2_Pluginnotify(int action,void *data,ulong p1,ulong
p2) {
 
switch (action) {
   
case
PN_NEWMOD:                   
// New module is added to the table
     
if (trace==TRACE_TILLSYS)
       
Setguard(1,1);
     
else if (trace==TRACE_TILLUSER)
       
Setguard(0,1);
     
break;
   
case
PN_ENDMOD:                   
// Module is removed from the memory
     
Deletesorteddatarange(&code,((t_module *)data)->base,
       
((t_module *)data)->base+((t_module *)data)->size);
     
break;
   
case
PN_RUN:                      
// User continues code execution
      if (trace!=TRACE_OFF && *((t_status *)data)==STAT_RUNNING)
       
*((t_status *)data)=STAT_RUNTHR;
     
break;
   
default:
break;                   
// No action necessary
 
};
};
////////////////////////////////////////////////////////////////////////////////
//////////////////
PLUGIN MENUS EMBEDDED INTO OLLYDBG WINDOWS //////////////////
//
Menu function of main menu, opens or brings to top API list.
static
int Mopenapilist(t_table *pt,wchar_t *name,ulong index,int mode) {
 
if (mode==MENU_VERIFY)
   
return
MENU_NORMAL;               
// Always available
 
else if (mode==MENU_EXECUTE) {
   
if (api.hw==NULL)
     
// Create table window. Third parameter (ncolumn) is the number of
     
// visible columns in the newly created window (ignored if appearance is
     
// restored from the initialization file). If it's lower than the total
     
// number of columns, remaining columns are initially invisible. Fourth
     
// parameter is the name of icon - as OllyDbg resource.
     
Createtablewindow(&api,0,api.bar.nbar,NULL,L"ICO_PLUGIN",PLUGINNAME);
   
else
     
Activatetablewindow(&api);
   
return MENU_NOREDRAW;
 
};
 
return MENU_ABSENT;
};
//
Menu function of main menu, starts or stops API trace.
static
int Mstarttrace(t_table *pt,wchar_t *name,ulong index,int mode) {
 
if (mode==MENU_VERIFY) {
   
if (run.status==STAT_IDLE || run.status==STAT_FINISHED)
     
return
MENU_ABSENT;             
// Application is no more
   
else if (index==0 && trace!=TRACE_OFF)
     
return
MENU_NORMAL;             
// OK to stop trace
   
else if (index==1 && trace==TRACE_OFF
&& run.status!=STAT_RUNNING)
     
return
MENU_NORMAL;             
// OK to start trace
   
else
     
return
MENU_ABSENT;             
// Action is not allowed
   
; }
 
else if (mode==MENU_EXECUTE) {
   
if (index==0)
{                   
// Stop trace
     
Unguardall();
     
Settracestate(TRACE_OFF); }
   
else
{                            
// Start trace
     
Setguard(0,1);
     
Settracestate(TRACE_TILLUSER); };
   
return MENU_NOREDRAW;
 
};
 
return MENU_ABSENT;
};
//
Menu function of main menu, opens plugin-related options.
static int Mopenoptions(t_table *pt,wchar_t *name,ulong index,int mode) {
  if (mode==MENU_VERIFY)
   
return
MENU_NORMAL;               
// Always available
  else if (mode==MENU_EXECUTE) {
    Pluginshowoptions(traceapioptions);
    return MENU_REDRAW;
  };
  return MENU_ABSENT;
};
//
Menu function of main menu, displays About dialog.
static
int Mabout(t_table *pt,wchar_t *name,ulong index,int mode) {
 
int n;
 
wchar_t s[TEXTLEN];
 
if (mode==MENU_VERIFY)
   
return
MENU_NORMAL;               
// Always available
 
else if (mode==MENU_EXECUTE) {
   
// Debuggee should continue execution while message box is displayed.
   
Resumeallthreads();
   
// In this case, Swprintf() would be as good as a sequence of
StrcopyW(),
   
// but secure copy makes buffer overflow impossible.
   
n=StrcopyW(s,TEXTLEN,L"Trace API plugin v");
   
n+=StrcopyW(s+n,TEXTLEN-n,VERSION);
   
// COPYRIGHT POLICY: This bookmark plugin is an open-source freeware.
It's
   
// just a sample. The copyright below is also just a sample and applies
to
   
// the unmodified sample code only. Replace or remove copyright message
if
   
// you make ANY changes to this code!
    StrcopyW(s+n,TEXTLEN-n,L"\nCopyright (C) 2012 Oleh Yuschuk");
   
MessageBox(hwollymain,s,
     
L"Bookmark plugin",MB_OK|MB_ICONINFORMATION);
   
// Suspendallthreads() and Resumeallthreads() must be paired, even if
they
   
// are called in inverse order!
   
Suspendallthreads();
   
return MENU_NOREDRAW;
 
};
 
return MENU_ABSENT;
};
//
Plugin menu that will appear in the main OllyDbg menu. Note that this
menu
//
must be static and must be kept for the whole duration of the debugging
//
session.
static
t_menu mainmenu[] = {
 
{ L"Open API list",
      
L"Open API list window",
      
K_NONE, Mopenapilist, NULL, 0 },
 
{ L"Start API trace",
      
L"Start API trace",
      
K_NONE, Mstarttrace, NULL, 1 },
 
{ L"Stop API trace",
      
L"Stop API trace",
      
K_NONE, Mstarttrace, NULL, 0 },
  { L"|Options",
       L"Open plugin-related options",
       K_NONE, Mopenoptions, NULL, 0 },
 
{ L"|About",
      
L"About Trace API plugin",
      
K_NONE, Mabout, NULL, 0 },
 
{ NULL, NULL, K_NONE, NULL, NULL, 0 }
};
//
Optional entry, adds items either to main OllyDbg menu (type=PWM_MAIN)
or to
//
popup menu in one of the standard OllyDbg windows, like PWM_DISASM. When
//
type matches, plugin should return address of menu. When there is no
menu of
//
given type, it must return NULL. If menu includes single item, it will
//
appear directly in menu, otherwise OllyDbg will create a submenu with
the
//
name of plugin. Therefore, if there is only one item, make its name as
//
descriptive as possible.
extc
t_menu * __cdecl ODBG2_Pluginmenu(wchar_t *type) {
 
if
(wcscmp(type,PWM_MAIN)==0)       
// Main menu
   
return mainmenu;
 
return
NULL;                        
// No menu
};
////////////////////////////////////////////////////////////////////////////////
////////////////////////////
PLUGIN INITIALIZATION /////////////////////////////
//
Entry point of the plugin DLL. Many system calls require DLL instance
//
which is passed to DllEntryPoint() as one of parameters. Remember it. Do
//
not make any initializations here, preferrable way is to place them into
//
ODBG_Plugininit() and cleanup in ODBG_Plugindestroy().
BOOL
WINAPI DllEntryPoint(HINSTANCE hi,DWORD reason,LPVOID reserved) {
 
if (reason==DLL_PROCESS_ATTACH)
   
hdllinst=hi;                      
// Mark plugin instance
 
return
1;                           
// Report success
};
//
ODBG2_Pluginquery() is a "must" for valid OllyDbg plugin. It must check
//
whether given OllyDbg version is correctly supported, and return 0 if
not.
//
Then it should fill plugin name and plugin version (as UNICODE strings)
and
//
return version of expected plugin interface. If OllyDbg decides that
this
//
plugin is not compatible, it will be unloaded. Plugin name identifies it
//
in the Plugins menu. This name is max. 31 alphanumerical UNICODE
characters
//
or spaces + terminating L'\0' long. To keep life easy for users, name
must
//
be descriptive and correlate with the name of DLL. Parameter features is
//
reserved for the future. I plan that features[0] will contain the number
//
of additional entries in features[]. Attention, this function should not
//
call any API functions: they may be incompatible with the version of
plugin!
//
(Windows' APIs are OK).
extc
int __cdecl ODBG2_Pluginquery(int ollydbgversion,ulong *features,
 
wchar_t pluginname[SHORTNAME],wchar_t pluginversion[SHORTNAME]) {
 
// Check whether OllyDbg has compatible version. This plugin uses only
the
 
// most basic functions, so this check is done pro forma, just to
remind of
 
// this option.
 
if (ollydbgversion<201)
   
return 0;
 
// Report name and version to OllyDbg.
 
wcscpy(pluginname,PLUGINNAME);      
// Name of plugin
 
wcscpy(pluginversion,VERSION);      
// Version of plugin
 
return
PLUGIN_VERSION;              
// Expected API version
};
//
Optional entry, called immediately after ODBG2_Pluginquery(). Plugin
should
//
make one-time initializations and allocate resources. On error, it must
//
clean up and return -1. On success, it must return 0.
extc
int __cdecl ODBG2_Plugininit(void) {
 
int restore;
 
OSVERSIONINFO osversion;
 
// Check Windows version. If somebody dares to start this plugin on
Win95,
 
// tell user that this OS does not support one-time memory breakpoints.
 
osversion.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
 
GetVersionEx(&osversion);
 
if (osversion.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS) {
   
Addtolist(0,DRAW_HILITE,L"%s will not work on Win95-based systems",
     
PLUGINNAME);
   
return -1; };
 
// Initialize API list.
 
if (Createsorteddata(&(api.sorted),sizeof(t_api),256,
   
(SORTFUNC *)Apisortfunc,NULL,0)!=0)
   
return -1;
 
// Initialize list of code blocks.
 
if (Createsorteddata(&code,sizeof(t_code),128,NULL,NULL,0)!=0) {
   
Destroysorteddata(&(api.sorted));
   
return -1; };
 
// Initialize list of return addresses. I don't expect much data here.
 
if (Createsorteddata(&retlist,sizeof(t_ret),32,NULL,NULL,0)!=0)
{
    Destroysorteddata(&(api.sorted));
    Destroysorteddata(&code);
   
return -1; };
 
restoretempbp=0;
 
// Restore options.
 
protocolapi=0;
 
Getfromini(NULL,PLUGINNAME,L"Protocol API
calls",L"%i",&protocolapi);
 
skipgdi32=1;
  Getfromini(NULL,PLUGINNAME,L"Skip GDI32",L"%i",&skipgdi32);
 
skipuser32=0;
  Getfromini(NULL,PLUGINNAME,L"Skip USER32",L"%i",&skipuser32);
 
skipkernel32=0;
  Getfromini(NULL,PLUGINNAME,L"Skip
KERNEL32",L"%i",&skipkernel32);
 
skipmsvc=1;
  Getfromini(NULL,PLUGINNAME,L"Skip MSVC",L"%i",&skipmsvc);
 
protocolret=1;
  Getfromini(NULL,PLUGINNAME,L"Protocol returned
values",L"%i",&protocolret);
 
// Initialize API table. OllyDbg uses table name to save the appearance
to
 
// the main initialization file. Keep this name unique, or else.
 
StrcopyW(api.name,SHORTNAME,PLUGINNAME);
 
api.mode=TABLE_SAVEALL|TABLE_AUTOUPD;
 
api.bar.visible=1;
 
api.bar.name[0]=L"Address";
 
api.bar.expl[0]=L"Address of API function";
 
api.bar.mode[0]=BAR_SORT;
 
api.bar.defdx[0]=9;
 
api.bar.name[1]=L"Name";
 
api.bar.expl[1]=L"Name of API function";
 
api.bar.mode[1]=BAR_SORT;
 
api.bar.defdx[1]=24;
 
api.bar.name[2]=L"Hits";
 
api.bar.expl[2]=L"Number of calls to API function";
 
api.bar.mode[2]=BAR_SORT;
 
api.bar.defdx[2]=9;
 
api.bar.name[3]=L"Called from";
 
api.bar.expl[3]=L"Locations this function was called from";
 
api.bar.mode[3]=BAR_FLAT;
 
api.bar.defdx[3]=256;
 
api.bar.nbar=4;
 
api.tabfunc=Apifunc;
 
api.custommode=0;
 
api.customdata=NULL;
 
api.updatefunc=NULL;
 
api.drawfunc=(DRAWFUNC *)Apidraw;
 
api.tableselfunc=NULL;
 
api.menu=apimenu;
 
// OllyDbg saves positions of plugin windows with attribute
TABLE_SAVEPOS to
 
// the .ini file but does not automatically restore them. Let us add
this
 
// functionality here. To conform to OllyDbg norms, window is restored
only
 
// if corresponding option is enabled.
 
if (restorewinpos!=0) {
   
restore=0;                        
// Default
   
Getfromini(NULL,PLUGINNAME,L"Restore window",L"%i",&restore);
   
if (restore)
     
Createtablewindow(&api,0,api.bar.nbar,NULL,L"ICO_PLUGIN",PLUGINNAME);
   
;
 
};
 
// Report success.
 
Settracestate(TRACE_OFF);
 
return 0;
};
//
Optional entry, called when user opens new or restarts current
application.
//
Plugin should reset internal variables and data structures to the
initial
//
state.
extc
void __cdecl ODBG2_Pluginreset(void) {
 
Deletesorteddatarange(&code,0x00000000,0xFFFFFFFF);
 
Deletesorteddatarange(&retlist,0x00000000,0xFFFFFFFF);
 
restoretempbp=0;
 
Deletesorteddatarange(&(api.sorted),0x00000000,0xFFFFFFFF);
 
Discardcalldata();
 
Settracestate(TRACE_OFF);
};
//
OllyDbg calls this optional function when user wants to terminate
OllyDbg.
//
All MDI windows created by plugins still exist. Function must return 0
if
//
it is safe to terminate. Any non-zero return will stop closing
sequence. Do
//
not misuse this possibility! Always inform user about the reasons why
//
termination is not good and ask for his decision! Attention, don't make
any
//
unrecoverable actions for the case that some other plugin will decide
that
//
OllyDbg should continue running.
extc
int __cdecl ODBG2_Pluginclose(void) {
 
// For automatical restoring of open windows, mark in .ini file whether
 
// API list is still open.
 
Writetoini(NULL,PLUGINNAME,L"Restore window",L"%i",api.hw!=NULL);
 
return 0;
};
//
OllyDbg calls this optional function once on exit. At this moment, all
MDI
//
windows created by plugin are already destroyed (and received WM_DESTROY
//
messages). Function must free all internally allocated resources, like
//
window classes, files, memory etc.
extc
void __cdecl ODBG2_Plugindestroy(void) {
 
Destroysorteddata(&code);
  Destroysorteddata(&retlist);
  Destroysorteddata(&(api.sorted));
 
Discardcalldata();
};
See
also: