Designing an API-checker
Posted: Wed Apr 06, 2011 12:03 pm
OK, so I needed another way to debug this lousy flat-memory model (in my terminal application), that after a while crashes because of a corrupt heap. I think the heap-manager actually works, but it is pointers that gets corrupts, which leads to random overwrites. A few months ago I had another debug-technique aimed to find this elusive bug. Then I allocated every memory chunk page-aligned, and never reused pages. I found some problems this way, but the heap corruption problem just disappeared (because every object was page-aligned, and there where no linked lists of free/used memory blocks that could become corrupt).
The method is really simple. I added a new include file that looks like this (it's a little shorter than the real thing):
A sample of how to use it: (the function will return something in edi)
Yesterday I implemented this API-checking function in some hundred functions, and tested. It turns out that the bug that corrupts the heap problably is in the DeleteSocket function, which failed to save lower part of ESI (SI). Because the compiler is aggressively optimizing code by using registers as much as possible, it is quite likely that some pointer will point to some new (random) memory block as ESI changes. I already knew before that it was related to the connection/deletion of sockets, because when I reduced this, heap corruption errors became less common.
If I had validated with a segmented memory-model I would have caught this problem very fast, but with paging only there is no simple way to find it.
The method is really simple. I added a new include file that looks like this (it's a little shorter than the real thing):
Code: Select all
; DEFINE DO_API_CHECK ; decomment to enable API checks
IFDEF DO_API_CHECK
ApiSaveEax Macro
push eax
Endm
ApiCheckEax Macro
local check_ok
push bp
mov bp,sp
pushf
cmp eax,[bp+2]
je check_ok
;
int 3
check_ok:
popf
pop bp
pop eax
Endm
ApiSaveEbx Macro
push ebx
Endm
ApiCheckEbx Macro
local check_ok
push bp
mov bp,sp
pushf
cmp ebx,[bp+2]
je check_ok
;
int 3
check_ok:
popf
pop bp
pop ebx
Endm
ELSE
ApiSaveEax Macro
Endm
ApiCheckEax Macro
Endm
ApiSaveEbx Macro
Endm
ApiCheckEbx Macro
Endm
ENDIF
Code: Select all
Somefunction Proc
ApiSaveEax
ApiSaveEbx
ApiSaveEcx
ApiSaveEdx
ApiSaveEsi
; do something
mov edi,1234h
ApiCheckEsi
ApiCheckEdx
ApiCheckEcx
ApiCheckEbx
ApiCheckEax
retf32
Somefunction Endp
If I had validated with a segmented memory-model I would have caught this problem very fast, but with paging only there is no simple way to find it.