How could I intercept a windows API function?
I have seen people who did something like this somewhere, but I don't remember where.
I would like to write an app that would intercept the function DeleteFile and display me a message asking if I aprove. If I let the file be deleted, the app would call the actual DeleteFile function.
I think this has something to do with changing the kernel dll memory.
I hope you can understand what I am trying to ask.
Thanks in advance.
Posted on 2001-10-15 20:39:46 by dilau
I have been checking into this myself, and believe me, the info can be quite hard to find :)

There are two different methods you could try. There is the method used in the app Regmon, you can read about it and download the source from Check their site out, it has some good stuff on it.

Matt Pietrek also has a method for doing it, which is detailed in his book "Windows 95 System Programming Secrets". The method he uses will work on both W9x and NT machines. This book is out of print, but there is a pdf version of it floating around on the net, get back to me if you can't obtain it (wink).

There are also a couple of MSJ articles from circa '94 and '95 dealing with it, however i have been unable to track down the actual text of them, let me know if you manage to get them (MS conveniently have left these article titles in their indexes, but have failed to include the actual article on their site or on the MSDN cds).

Posted on 2001-10-15 21:26:15 by sluggy
There are lots of tutorials on this topic (API Hooking) and I am sure you will have no trouble finding tutorials and articles about this rather popular topic. I would rather recommend ApiHooks from ELiCZ, check it out at
Posted on 2001-10-15 21:53:53 by comrade
There is an API spy program available at . If you download it and test it, you'll find it's an excellent program. You can send him an email and he'll send you the source code but you have to make an agreement about not using the source for profit.

Posted on 2001-10-15 21:58:37 by _Shawn
Anything in ""Windows 95 System Programming Secrets" should be
taken with a grain of salt, a lot of the information isn't valid anymore :/.
EliCZ stuff is good. A pretty funky and well-working (as far as I know)
method is the one used in "detours", which can be found at
Posted on 2001-10-16 15:41:33 by f0dder
Take a look at Lucifer48 Site

-->100% Win32ASM
--> Project II - Hookall

this project is unfinished and replaces kernel-Api-code with yours via Ring-0 (vxd)
but there are other ways to bypass the writeprotection in memory-areas (Kernel for example)

hope this helps a bit
Posted on 2001-10-17 01:57:57 by CRYO
Thanks everyone.
Those sites really helped me understand how to do what I want.
I read some articles about changing the import table of the EXEs that I want to intercept. But since I want to intercept all of them, wouldn't be easier to change the kernel32.dll export table?
Is that possible? Does anyone have any information about that?
Thanks in advance.
Posted on 2001-10-20 10:02:28 by dilau
You'll definitely have to go ring0 to do that... iirc, virtualprotect and
read/writeprocessmemory don't work above "a magic limit", where
the kernel related files live.
Posted on 2001-10-20 10:07:32 by f0dder
The "detours" library only works correctly on Windows 2000, NT, and XP. Alot of the stuff wont work on Windows 9x platforms.

I wanted to try some of this info, but I guess I cant.

Posted on 2001-10-20 10:57:31 by Torch
hey fodder,
that's not true at all.

i' ve found a way to write to kernel without entering Ring-0 in some eZine 'Matrix#2'. Give it a try! It plays around with the pagetable. But be warned! It's a little bit abstract and hard to understand. But it works.
But maybe there are some compatiblity prblems on NT or 2K

the article follows here:


xlated from russian for MATRiX #2 E-Zine
(x) 2000 Z0MBiE

Before reading this text, it is strongly recommended to read some
documentation about page table, protected mode, memory organisation, etc.


Here will be described a method of writing into any region of memory
from ring3 by means of pagetable modification.

Our task is to write data into some virtual (linear) address X.
And let the write-protected memory page that contains address X
will be called PageX.

[*] getting access rights to address X

It is possible to get information about our access rights to address X
in the following ways:

1. call win32 api: IsBadXXXPtr (XXX=Read,Write,Code,etc.)
2. use SEH

1. IsBadXXXPtr
push size-in-bytes
push address
call IsBadReadPtr
or eax, eax
jnz __can_not_read
__can_read: ...

2. SEH
call __seh_init
mov esp,
jmp __seh_exit
__seh_init: push dword ptr fs:[0]
mov fs:[0], esp

mov al, byte ptr

__seh_exit: pop dword ptr fs:[0]
pop eax

jc __can_not_read
__can_read: ...

Btw, IsBadXXXPtr functions (in win9X at least) uses SEH method too,
so their execution with the same result just takes more time.

So, now we know how to find if PageX is write-protected

[*] Page Table format

What is page table?
All physical memory is divided into 4k-pages, which may be:
read/write-protected and mapped into linear 4GB-length address space.
Information about all this shit is stored in the page table.

Page Table:

1st-level directory page 1024 x physical memory
aka Page Directory 2nd-level directory pages pages

single 4k-page, totally 1024*1024 DWORD-descriptors,
physical addr in CR3 for any 4k page in 4GB address space

?????????????? ?????????????
? DWORD #0 ?-------------->? ????????????? ????????????
?????????????? ?? DWORD #0 ?---------->? ... ?
? DWORD #1 ?---------------->????????????? ?????????????
?????????????? ?? DWORD #1 ?---------->????????????
: ... : : ????????????? ? ... ?
: ... : : : ... : ?????????????
?????????????? ? : ...?????????????
? DWORD #1023 ?--------------------->? DWORD #0 ?-------> ...
??????????????? ?? ?????????????
????? DWORD #1 ?-------> ...
: ... :
: ... :
? DWORD #1023?-------> ...

Each DWORD has the following format:
(to find more details see documentation)

31 12 11 0
| | | | | |P|P|U|R| |
| physical address 31..12 |AVAIL|0 0|D|A|C|W|/|/|P|
| | | | | |D|T|S|W| |

P - present (if P=0, all other data is unused and the corresponding
page is absent in linear address space)

R/W - read/write -- bit 1 (0x02)
U/S - user/supervisor -- bit 2 (0x04)

[*] finding pagetable: variant 1

High 20 bits of the CR3 register is a physycal address of the page table,
low 12 bits are zero (it just means that pagetable base is 4k-aligned).

But, if you will use 'MOV EAX, CR3' instruction, you will suck.
This is because it is a privileged instruction, which may not be
called from ring-3.
Indeed, win9X will not generate an exception in this situation, but,
it will not modify EAX too.

Now we may think a little, and find out, that copy of the CR3 register
(for the current context!) is stored in the TSS.

Where to find TSS?
TSS selector may be found using STR (Store Task Register) command.

So, we know everything to find CR3 register value.

; subroutine: get_cr3
; output: EBX=CR3

get_cr3: push eax

mov ebx, ; EBX<--GDT.base

str ax ; EAX<--TSS selector
and eax, 0FFF8h ; optionally

add ebx, eax ; EBX<--TSS descriptor offset

mov ah, ; EAX<--TSS linear address
mov al,
shl eax, 16
mov ax,

mov ebx, ; EBX<--CR3
and ebx, 0FFFFF000h ; EBX<--pagetable phys. offs

pop eax

Btw, if you will change 'mov ebx, ' into 'mov ebx, ' in
the code shown above, you will get ESP0, i.e. ESP of the code, that
has called current task.

So, now we have CR3 register value, which is physical address of the
page table.

But how can we use this fucking physical address in the PE file?
Of course, in the ring-0 it may be converted into linear address
using VxDcalls, but in winthin the win32 api there are no such
api functions...

[*] finding pagetable: variant 2

Now lets try to find pagetable in memory.

We will search not the first-level directory page,
but that second-level directory page,
that contains DWORD which describes PageX.

What do we know about this second-level page? Seems nothing.

But, life is not so bad. Above-mentioned IsBadReadPtr function can
tell us, is some address readable or not. (a whole bit!)
And each DWORD-element in the second-level directory page has U/S bitflag,
which has the same meaning. ;-)
And there are another bit (R/W) which determines writeability of the
memory page, which may be found using IsBadWritePtr function.

ring-3 function flags in the page descriptor
read IsBadReadPtr U/S user/supervisor
write IsBadWritePtr R/W read/write

So, calling IsBadRead/WritePtr functions for 1024 different pages we can
generate 1024 DWORDs, and each DWORD will contain the same 2 bits,
as in the 2nd-level directory page.

After that we will scan all the memory (really C0000000..D0000000),
comparing each memory page to our generated page,
but checking only these 2 bits in each DWORD.

needtbl dd 1024 dup (?)

; subroutine: find_kernel_pagetable
; return: EBX=2ndlevel directory page (for addresses BFC00000...C0000000)


; create page of DWORDs (with 2 meaningful bits in each DWORD)

lea edi, needtbl ; page of DWORDs

mov esi, 0BFC00000h ; ESI--starting page

__maketbl: xor ebp, ebp

push 4096
push esi
callW IsBadReadPtr

xor eax, 1 ; 0 <--> 1
shl eax, 2 ; 1 --> 4
or ebp, eax

push 4096
push esi
callW IsBadReadPtr

xor eax, 1 ; 0 <--> 1
shl eax, 1 ; 1 --> 2
or eax, ebp


add esi, 4096 ; +1 page
cmp esi, 0C0000000h ; totally 1024 pages
jne __maketbl

; find 2nd-level directory page

mov ebx, 0C0000000h ; start address
push 4096 ; is current page readable?
push ebx
callW IsBadReadPtr
or eax, eax
jnz __cont

xor edi, edi ; compare pages

__scan: mov eax,
and eax, 2+4 ; only 2 bits has meaning for us
xor eax, needtbl ; compare
jnz __cont

add edi, 4
cmp edi, 4096
jb __scan

clc ; found!

__cont: add ebx, 4096 ; +1 page
cmp ebx, 0D0000000h ; end-address
jb __cycle

stc ; not found...

So, we have found 2nd level directory page which describes
1024 pages in range.

Now, if we wanna change RW bit for address BFF70000 we do the following:

; find 2nd level directory page
call find_kernel_pagetable
jc __damnedsonofabitch

; clear protection (make page writeable)
c1 = (0BFF70000h-0BFC00000h)/1024
or dword ptr , 2 ; RW=1

; check if all okey, if page really became writeable
push 4096
push 0BFF70000h
call IsBadReadPtr
or eax, eax
jnz __exit

; fuck the kernel
not dword ptr ds:[0BFF70000h]

; restore protection
and dword ptr , not 2 ; RW=0

Now you may ask me: why did u used BFC00000 and C0000000 constants in
the example above?
This numbers are BFF70000 address, rounded up and down to the 4MB range.

[*] damned shit

Maybe you think, we've just patched kernel? No. This really sucks.
As it were found, the fucking 2nd-level directory page is just ABSENT
in the current context, by default.
So you really will find nothing.
But, under fucking Soft-ICE all works. This is because it commits this
2ndlevel page into current context, performing something like this:

push 0
push 4096
push physical-page-offset
VMMcall MapPhysToLinear
add esp,3*4

And fucking VMM_MapPhysToLinear calls first VMM_PageReserve and then
VMM_LinPageLock, in such way allocating new page in the current context
and mapping physical page there.

So, we may find pagetable only if it is loaded into current context.

[*] finding pagetable: back to variant 1

So, we have the following trouble: 2nd-level directory page may not be
found, because it is absent in the current context.

Solution may be the following:
1. try to find this fucking page in the different contexts
2. map this page into current context

2nd variant seems better, so, question:
how to convert physical address into linear without entering ring-0?

And here it is.

KERNEL@ORD0 dd 0BFF713D4h ; surely, it may be found

; subroutine: phys2linear
; input: EBX=physical address
; output: EBX=linear address

phys2linear: pusha

movzx ecx, bx ; BX:CX<--EBX=phys addr
shr ebx, 16

mov eax, 0800h ; physical address mapping

xor esi, esi ; SI:DI=size (1 byte)
xor edi, edi
inc edi

push ecx
push eax
push 0002A0029h ; INT 31 (DPMI services)

shl ebx, 16 ; EBX<--BX:CX=linear address
or ebx, ecx

mov , ebx ; popa.ebx


So, now we know CR3 value and may xlate it into linear address.

; subroutine: get_pagetable_entry
; input: ESI=address
; output: EDI=pagetable entry address

get_pagetable_entry: pusha

call get_cr3 ; take CR3
and ebx, 0FFFFF000h ; EBX<--pagetable phys. addr

call phys2linear ; EBX<--pagetable linear addr

mov eax, esi
and eax, 0FFC00000h
sub esi, eax
shr eax, 20 ; EAX<--1st-level dir page
shr esi, 10 ; ESI<--2nd-level dir page

mov ebx, ; EBX<--physical address
and ebx, 0FFFFF000h ; of the needed page

call phys2linear ; EBX<--linear address

add esi, ebx ; ESI<--DWORD to patch

mov , esi ; popa.edi

And, at last, all this shit may be used as this:

mov esi, 0BFF70000H
call get_pagetable_entry
or dword ptr , 2 ; PAGEFLAG_RW
mov byte ptr , xxxx ; fuckup kernel
and dword ptr , not 2

Thats all!
* * *
Posted on 2001-10-21 00:50:21 by CRYO
yep...ring0 is not necessary under win9x to modify kernel.
the easyest way is to use the Int 2eh service, or deprotect the address you want to write to in kernel using the ORD_01 (VxDCall)-look in Vecna's hide proc which u should find on iczelion's site.
Posted on 2001-10-21 06:41:06 by DZA
Interesting article, CRYO :alright:.
I guess I should have written "You'll definitely have to go ring0 to do that (in a clean way)". :)

I don't believe that the method set forth in the article will work under
nt/2k, btw. I mean, "kernel32 ord0" is VXDCall iirc, and... I find it hard
to believe that you have VXDCall under nt/2k ;). So, even if you can
find the physical adresses for the kernel tables, you still need to map
phys->linear, and that requires ring0 (or at least a detour to ring0),
which nt/2k generally isn't very fond of ;). I'll try playing around with
the stuff a bit and see if I can come up with something.
Posted on 2001-10-21 08:59:16 by f0dder
What about this???
-You could patch the export table of kernel32 when it is on disk, with the address of a new func, so when a prog loads with and imports the address of the DeleteFile API it will get your value, and call your proc. Then you can call the real DeleteFile.

Would that work?

I am assuming you would have to update the CRC of kernel32 and this method would cause problems if your proc was relocated.

You would also have to have your dll or whatever loaded with the prog to handle the calls with each running program.
Posted on 2001-10-22 03:27:31 by huh
Well, you could "sort of" do this. Either you'd have to make the export
point to "something else" in kernel32, or you'd have to add a bunch
of code to k32 (either with the use of caves, or by adding a new
section). This *is* possible.

Updating the crc is not necessary. The only time PE CRCs are used,
are for nt/2k/xp drivers, to prevent corrupted drivers from being loaded.

However, messing with kernel32 on disk is a bad idea, for a couple
of reasons. If you mess up, you're in trouble :). Second, patching the
file is not so easy, as the file is always in use. Under 9x you can boot
to real dos mode and fix it, but it's a bit harder under nt/2k. Also,
under 2k/xp (and WinMe?), you will be battling system file protection.
Posted on 2001-10-22 04:04:50 by f0dder
Thanks again everyone. I think I am on my way to get this done.
The HideProc example in the Source Codes section does exactly what I want, but it intercepts the functions Process32First and Process32Next. I want to intercept DeleteFile.
The program is written for TASM. I use MASM. I think I was able to translate the .inc files (I wasn't able to compile the program in MASM yet). I didn't understand the main .asm file. There is code inside the .data section. Is that possible? I think MASM didn't accept that.
Here is the beginning of the code:

.model flat

ofs equ offset
by equ byte ptr
wo equ word ptr
dwo equ dword ptr

include ;29A inc files


titulo db "HIDEPROC - Stealth W9x process", 0
msg1 db "KERNEL32 already patched", 0
msg2 db "KERNEL32 has not available space for our code", 0
msg3 db "KERNEL32 patched", 0

dll db "kernel32.dll", 0
api001 db "Process32First", 0
api002 db "Process32Next", 0

vxdcall0 dd 0


call swap_p32f
push dwo ;buffer
push dwo ;snapshot
call _p32f
test eax, eax
jz @@error ;wasnt sucessful...
call check_name
jnz @@error
call swap_p32f
push dwo ;yeahh, stealth it!
push dwo
call p32n_entry
ret 2*4
(lots of code here)
(Here is the end of the data section:)
mov eax,
xchg , eax
mov , eax
mov al, ;swap 5-byte buffers
xchg , al
mov , al

p32n_code db 5 dup (0)

p32f_code db 5 dup (0)



Can anyone help me translate this to MASM?
Or can someone help me find some kind of TASM manual that explains this?
Thanks in advance.
Posted on 2001-10-26 17:26:27 by dilau
Well there are always times you'll want systemwide hooks or hooking multiple things but in all hookingprojects I've done single application spesific hooking is what I've wanted todo because
1: I only want to hook stuff and get info/do stuff with that app? doh :P
2: Because it's cleaner when you are hooking just what you want....

The easiest way you could hook some app you want to hook is to write a loader/GUI that either

a: Lets you choose a file on disk, then CreateProcess it suspended and inject a DLL
b: Use Toolhelp32 to find all running processes and let the user choose that way and inject the DLL (or as I've done in my prog, let you choose both running and progs on disk).

Then in the DllMain on attach just rewrite the import-table of the process you loaded into (GetModuleHandle(0)...) and you'll have patched only that app.... There is only one negative side with doing it this way and that is if the application is packed or simular because then you often can't get the importtable (atleast not without a lot of reversing)....

Oh yea and if you are having problems hooking ie GetProcAddress inn kernel32.dll then you can hook LdrGetProcedureAddress inn ntdll.dll instead :P
Posted on 2003-07-08 16:56:01 by SFP