This fragment is about to be reported (you'll remain on this page):
You can enter a comment to clarify the mistake if you would like to:
A full-featured console WinAPI hooking tool written in Delphi 7. Freeware. Open source. Public domain.
ApiHook is a freeware (public domain) open-source program written in Delphi 7 for hooking library calls in Win32 systems. In fact, it can be used to hook any snippet of assembly code in a single EXE file as well but this needs a bit of source code tweaking.
ApiHook lets you track and view information about what functions and how exactly the target program calls. It is implemented in two parts: loader and library. The first injects the ApiHook library into the target process (starting it or attaching to an already running one) while the second does the actual work.
ApiHook lets you examine values of registers the called routine was passed and also capture its parameters (using stored
asmESP value) and returned value through them.
The mechanics is simple: you write a script file specifying what actions must be performed when a specific function is called; actions receive the snapshot of the call-time registers and can log them, dump memory blocks or do something else.
Actions can run both before and after the call (so-called pre- and post-actions).
See also script examples for a quick start.
Download ApiHook binaries – Win32, built with Delphi 7.
Download ApiHook source codes. There are no external dependdencies except for D7X – Delphi 7 eXtension library library – put it into Lib\D7X and ApiHook will compile. If you’re going to explore the sources make sure to read The mechanics or at least Source code map section to get a quick start.
Please ask questions, suggest features or just express yourself in the comments.
shah.exe launch target.exe script.oo
shah.exe l target script– short form of the above call
shah l target– the same again – Script.oo and Script.txt are detected in current folder and used if omitted from the command line
shah.exe i target myhook.dll
ApiHook is still in beta version – all listed options are supported but not all tasks are.
Most of the configuration files use common INI-style syntax that, however, differ in details.
confprefix notation = ondefines a key «prefix notation» with value «on»
conf: dwDesiredAccess access = DWord flags = set of GENERIC_READ GENERIC_WRITE
The above defines one parameter named dwDesiredAccess with one alias (name equivalent – any number of space-separated aliases are allowed) access and of type DWord. flags that follows the type is a type hint which is currently unused by ApiHook.
Note the equality sign (=) at the end of the line – it connects the value of this parrameter with one or more constants. Those constants can be listed on the same line or, as in the example above, can be wrapped to the next (this is the «official» ApiHook format).
Constants are specified as a space-separated list of names; this list may optionally start with «set of» phrase meaning that this parameter is not a single constant value but rather their combination – for numeric constants this is bitwise mask (e.g. 0x01 is one constant, 0x02 – another, 0x04 – third and so on), for others check this section.
Constants may be specified using two wildcard metacharacters – * (matches zero or more symbols) and ? (matches exactly one symbol). However, note that this affects performance: when constant name is specified exactly (without wildcards) fast hash table lookup is performed in one go; on the contrary, to get the list of names matching a wildcard list scan is done.
(It should be noted that list scan is optimized and if wildcard doesn’t appear as the first name character the scan is stopped when wildcard-free prefix differs from the following item because constant definition list is kept sorted.)
This is the full definition of CreateFileA function as an example:
conf[CreateFileA] Lib = kernel32.dll Call = stdcall Return = DWord handle : lpFileName fn = PChar : dwDesiredAccess access = DWord flags = set of GENERIC_READ GENERIC_WRITE : dwShareMode share = DWord flags = set of FILE_SHARE_READ FILE_SHARE_WRITE FILE_SHARE_DELETE : lpSecurityAttributes security = Pointer PSecurityAttributes : dwCreationDistribution distribution distrib = DWord enum = CREATE_NEW CREATE_ALWAYS OPEN_EXISTING OPEN_ALWAYS TRUNCATE_EXISTING : dwFlagsAndAttributes flags = DWord flags = set of FILE_ATTRIBUTE_* : hTemplateFile template = DWord handle
function CreateFileA(lpFileName: PAnsiChar; dwDesiredAccess, dwShareMode: DWORD; lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD; hTemplateFile: THandle): THandle; stdcall;
Procedure parameters and result (see API Catalog) can be of the following types (case-insensitive) that correspond to standard Delphi and C++ types:
Also, type name can be followed by a space and a space-separated list of type hints. Currently ApiHook makes no use of type hints; they are intended to give more details to the standard types listed above. Sample type hints: handle, flags, enum, PSecurityAttributes (for a Pointer type), etc.
API Catalog allows specifying of set of constants for a procedure parameter. This changes the way ApiHook matches parameter value with given constant names – instead of simple «is equal» condition the algorithm changes depending on the parameter value type:
This file (usually Constants.ini) uses Common file syntax. It does not contain any sections.
Constant values are defined separately and can be of any RPN type. There are several ways of defining a constant:
conf[Constants]section of the script file;
conf[Constants]section of the API Catalog;
The following sample defines constants of various type:
confStrConst = 'abc' IntConst = 65261 HexIntConst = $FEED FloatConst = 3.14 StrConst2 = '3.14' ATrue = TRUE AFalse = FALSE StrConst3 = 'TRUE' RawConst = x ff 3E StrConst4 = X ff 3e
This file (usually with .oo or .txt extension) uses Common file syntax. There are 3 kinds of sections:
conf[Constants]– defines extra constants for this script
conf[ApiHook]– sets hooking options
Script file might contain sections with identical names – in this case the first two section types are merged as if they were one and the last (hook actions) are kept in separate blocks of actions running for the same hooked routine. Blocks of actions run one after another and some actions (e.g. if) can skip execution of the remaining actions within a single block.
Hooking options are defined in
conf[ApiHook] section of the script file. Currently there is only one option:
conf[sections] in the script file have several features:
conf[header]and all lines inside it but thiis shortcut is often more convenient;
conf[ReadFile - file header]to remind yourself what this block is for.
conf[CreateFileA] log Opening file :fn... . log Returned handle :h:x.
When a hooked routine gets called by the target process a new context is created that is destroyed when hooked call ends – after the last post-action has run. Each context has registers, return value, parameters and variables with values relevant to that particular hook call.
ApiHook captures all x86 registers: EAX, ECX, EDX, EBX, EBP, ESI, EDI and:
asmCALLaddress being pushed onto the stack – in other words,
asm[ESP]points to the last pushed parameter.
asm[EIP]points after the original
Registers are accessed by their upper-case name in expandable strings.
ApiHook provides access to any of the hooked routine parameters that are listed in API Catalog either by parameter name or any of its aliases. Parameter names are case-insensitive.
When parameters are inserted into some expression or expandable string they appear in human-readable form. Currently this means that those parameters that have associated constants in their API Catalog entry get matching constants output after the actual value.
Each context has its own separate variable list (with such variables called context or temporary) amd there’s also a global variable list that is shared by all contexts running in the same ApiHook library instance.
Actions are what produces any visible effect when a hooked procedure is invoked by the target process. Although not all actions produce any effect but you can’t do anything without first listing an action in corresponding procedure script block.
conf[MessageBoxW] log MessageBoxW called from :EIP:x.
Save stores a value in a temporary variable list associated with the current context (called context or temporary variable) or in global list that can be accessed by any action block – not necessary the same that has saved the value.
Temporary vars are named in mixed case while global vars always use upper-case characters – see this section for details.
Certain calling conventions specify that stack is cleaned by the called routine and thus all parameters that were passed to it are also lost. This is the case for stdcall that is used in Windows API function calls.
conf[CreateFileA] ; single parameter saving: save fn ; writing of "h2fn" + file handle (return value in upper-case hex form); ; upper case is important - without it variable is stored in CreateFileA's ; private context and is inaccessible from within ReadFile block: . save <h2fn ^ x fmt cat up> ^fn [ReadFile] ; accessing value stored in CreateFileA post-hook Save: log Reading :size bytes from <h2fn :f x fmt cat up load> ; multiple parameter saving: save buf, read ; dumping memory block with read bytes using saved parameters: . dump dumped ^buf .. ^read 
Dump copies memory block of given length into a user file (location of user files is configured using --user-files loader option).
Attention: if you’re dumping memory being read by a hooked routine don’t forget to call Dump as a post-action – calling it as /pre-action will save block that was not yet filled by the called routine.
Example that saves blocks read by ReadFile in files with semi-random names:
conf[ReadFile] save f, buf, size, read ; construct dump file name from word "handle", file handle "f" converted to ; upper-case hex form and the value of "size" (nNumberOfBytesToRead) parameter: . save dumpFN handle <^f X fmt> :size ; saves dump of actually read bytes written in "read" DWord: . dump ^dumpFN ^buf .. ^read 
Note that If is a regular action and thus works in usual pre- and post-hook phases (or both).
Example that outputs handles and names of opened files only if they contain «myarc.zip» (case-insensitive posi function):
conf[CreateFileA] save fn . if ^fn myarc.zip posi . log Opened ^fn, handle <^ X fmt>.
asmESPitself; this parameter is an RPN expression that must evaluate to an integer.
Stack trace: 0=00000000, 4=00C246E0 8=[ApiHook], C=0015D314, 10=77124800 [oleaut32], 14=0043A667 [SELF], ...
DWord values are output starting from base address (see the corresponding parameter above) and further (
asm[Base+4], etc.). It then treats each value as an address and if it belongs to a any loaded module’s address space the base name of that module in square brackets ([ and ]) is appended.
Note that original caller address is not inculded – it’s stored in the EIP register.
In its expressions ApiHook can use Reverse Polish Notation (RPN, also called postfix notation) or Polish Notation (also called prefix notation). The exact notation used depends on per-script prefix notation setting and defaults to RPN.
RPN is a parenthesis-free expression syntax operating on stack where arguments are taken and function or operator return values are put. Operands are separated by spaces. RPN expressions share some common syntax with expandable strings – registers, parameters and other entities are accessed the same way.
^var 'text' pos GEQ 3 :param & $0F NEQ 0 AND
When prefix notation script option is enabled the order of the above operands must be reversed:
AND 0 NEQ $0F & :param 3 GEQ pos 'text' ^var
ApiHook uses RPNit unit from the D7X library and thus its values can have the same types:
RPN expression can contain the following entities, or operands:
For example, this RPN expression returns variable named as the value of parameter «arg» converted to upper-case and concatenated with the upper hexadecimal form of the sum of EAX register and «var» variable values:
:arg up EAX ^var + cat X fmt load
When prefix notation script option is enabled the order of the above operands must be reversed:
load fmt X cat + ^var EAX up :arg
Arithmetic (expect two integers or floats): +, –, *, /, % (modulus).
Bitwise (expect two integers): << (shift left), >> (shift right), & (AND), | (OR), ^ (XOR).
|Integer||—||Hexadecimal representation: 7B||
Shortest possible string representation (as %g)
|String representation: 123||—|
Shortest possible string representation (as %g)
|Boolean||0 (FALSE), 1 (TRUE)||—||—||Converts to string: FALSE, TRUE||—|
|String||Decimal byte codes: 13 128 5 ...||Hexadecimal codes: 0D 80 05 ...||—||—||Hexadecimal character codes: 800D 0005 ...|
|Raw||Decimal byte codes: 13 128 5 ...||Hexadecimal codes: 0D 80 05 ...||—||Converts to ANSI string||Converts to Unicode string (adds zero character on uneven length)|
|Non-valid convertions (—) cause an error.|
Numeric (expect two floats): LEQ (Less or EQual, <=), GEQ (Greater or EQual, >=), LESS (<), MORE (>).
Seeking file :h:X (name <h2fn :h x fmt cat up load>) as :method to :pos.
Seeking file 40C (name C:\boot.ini) as FILE_BEGIN to 102.
The library does the actual work – hooks routines in the target (its own) process and runs actions when they get called. The loader is used to, first, inject the library into a process and then to establish connection with it using a named pipe. This connection is used to set up the library (load API Catalog, script, etc.) and to read log messages generated by itself and assigned script actions.
ApiHook library and loader are communicating by using a named pipe with semi-random name composed of \\.\pipe\ApiHook\ and a random number.
Although the loader can be ((#help --detach))ed named pipe is still used to initiate the library and hooking. After this the loader can be detached but the library will continue operating, running corresponding hooks, logging messages to files (if specified using [[#help --lib-logs]]) and doing other actions.
This marks the end of initialization and the library now fully operates. Depending on the settings loader can now detach itself ([[#help --detach]]) or start receiving log messages by sending LOG command once per each configured interval.
The LOG command is followed by either NONE or ONE strings that indicate if there were any new log messages in library log queue. If ONE was send 32-bit log message level follows, after which goes the message in two parts:
ApiHook uses prologue rewriting on the target routines to redirect calls from them to the library’s internal functions that trigger scripted actions. Because of prologue rewriting, the target program doesn’t have to be statically linked with a procedure for ApiHook to be able to hook it, nor does it need to call it directly ApiHook will catch all calls to it regardless if they were done from the program process itself or from one of the libraries (for example, kernel32.dll calls CreateFileW in its CreateFileA implementation; this is true for most other ANSI ↔ Unicode API functions).
asm68 XX XX XX XX PUSH <Jumper address> C3 RET
Unhooking is done in the reversed order: critical sections (if configured) are left and deleted, original routine prologue is restored, and virtual protection of the 6-byte memory block is restored to its original value.
If library is configured to use critical sections (using --thread-safe option of the loader) then steps from #1 to #5 (inclusive) are ran in a single thread.
asm68 XX XX XX XX PUSH <procedure index> FF 15 XX XX XX XX CALL <prehook> 50 PUSH EAX 8B 44 24 04 MOV EAX, <original return address in ESP+4> A3 XX XX XX XX MOV <slot>, EAX 58 POP EAX 58 POP EAX ; removes original return address FF 15 XX XX XX XX CALL <original routine> 68 XX XX XX XX PUSH <procedure index> FF 15 XX XX XX XX CALL <posthook> FF 25 XX XX XX XX JMP <slot> XX XX XX XX DD <32-bit memory slot>
Prehook and posthook procedures are identical asm wrappers that capture register values as they are per this moment (before and after hooked routine call) and invoke native procedures: DoPreHook and DoPostHook.
DoPreHook restores original routine prologue, enters critical section (if configured using --thread-safe loader option), creates call context and runs script actions associated with the called routine.
Both routnies do nothing except patching the prologue if the calling instruction is located within the ApiHook library address space – otherwise ReadFile and other functions that the library uses will trigger script actions even though the target process not necessary calls them.
This section briefly describes the structure of ApiHook source code:
You can download ApiHook source code from here; it uses no external libraries except for FastShareMem (included) and D7X (can be downloaded from here and placed into Lib) so it should compile out of the box.
ApiHook 0.84 http://proger.i-forge.net/ApiHook by Proger_XP, 20.05.2013 ------------------------------- ApiHook basic help ------------------------------- Usage: ah.exe [--option --...] l/a <program/process> <script.oo/txt> ah.exe [--option --...] s <script.oo/txt> ah.exe [--option --...] i/e <program/process> <library.dll> Tasks: (no) Show basic help h help Show detailed help with options version Show program version l launch Start a program and hook it a attach Hook a running process i inject Start a program and inject an arbitrary DLL into it e extend Inject an arbitrary DLL into a running process s self Debug: Attach the library to the loader itself ------------------------------- ApiHook detailed help ------------------------------- Arguments program/process For launch this is the path to an .exe file to run and attach hooks to. For attach this is process' ID or module name. If no file with this name exists attempts to append .exe automatically. script.oo/txt ApiHook script file (usually with .oo extension) used to setup hooks. If omitted will search for Script.oo, Script.txt files in current directory (in this order). If this has no extension attempts to append .oo, .txt extensions. If no file with given name exists this argument is treated as inline script with several semicolon-separated actions in format: ProcToHook action string[; ...] Options (default) --no-wait & --wait & --wait-on-error Toggles waiting for Enter pressing before the program exits. --show-exit-code=1 Toggles displaying non-zero exit code on program finish. --console-cp=auto Switches console output to given codepage: auto (current CP), utf8 (65001 CP, requires a Unicode vector font like Lucida Console) or any numeric value (CP identifier). Old codepage is restored when ApiHook exists. --utf8-warning=1 Toggles warning message appearing when a string being output contains Unicode symbols with code > 127 that cannot be represented using default console raster fonts. Only appears on enabled --utf8-output. --colors=1 Enables colors to be used in the console output. --thread-safe=0/p/1 Toggles usage of critical sections in both ApiHook loader and library. 0 disables them which is the fastest option but will result in crashes for lots of concurrent calls (especially ReadFile/CreateFile). p enables per-hooked procedure safety whuch might not save you from all crashes. 1 makes all actions run in single thread which is the slowest but most reliable way even for ReadFile. --threads=1 Debug: Eables ApiHook loader to use a thread to serve interprocess communication pipe between itself and the injected ApiHook library. --logs=[+][#]file.log [,[+][#]second.log [,...]] - Enables logging of all strings into given file. Unless starts with + the file is overwritten on each ApiHook run. One or more leading # decrease log verbosity (# = debug, ## = info, ### = user, #### = error). Relative to current directory (see also --chdir). Multiple logs can be separated with commas (without surrounding spaces). --lib-logs=[+][#|!|%]file.log [,...] - Enables logging of ApiHook library messages to one or more comma-separated files. Unless a file name starts with + it's overwritten on each run. There can be 3 special prefixes: # controls verbosity (see --log); ! enables recording of named pipe communication errors; % enables through logging of all pipe commands and data being sent and received by the ApiHook library. Relative to current directory (see also --chdir). If no prefix is given file records core library messages of ## verbosity. By default logging is disabled after the library loads fine but is enabled otherwise - see Library Logging below for details. Dash (-) or empty value turns library logging off. --lib-verbose=d/i/u/e Sets min message level to be sent from ApiHook library to loader via the named pipe. Note that setting this to d will generate heavy pipe I/O and might cause Access violations. See also --verbose that additionally filters all messages being output to the console. --watch-interval=500 Refresh interval to check --lib-logs for new lines in milliseconds. Also affects --detach and watch. --user-path=User Location of user files. This is used to save dumped and other script-related data. Path will be created if it doesn't exist. --verbose=d/i/u/e Controls how verbose console output is: debug, info, user, error. See also --lib-verbose. --log-time=h:nn If not blank, changes format of log message times appearing in the console output. If blank, removes time prefix from log messages altogether. String format is identical to that of Delphi's FormatDateTime: http://docwiki.embarcadero.com/Libraries/en/System.SysUtils.FormatDateTime --detach=1/w/0 After hooks have been set up the loader (ah.exe) will exit unless it's w - if so --lib-logs file will be watched and new lines displayed in the console. --lib=ApiHook.dll Path to the ApiHook core library being injected into the process. --catalog=Catalog.ini Changes location of the API catalog file. --consts=Constants.ini Changes location of the constants list file. If dash (-) or empty disables it (consts can still be specified in the API catalog file (see --catalog) and script under [Constants] sections. --define=const=value[,const=value[...]] Defines extra constants (see also --consts) separated by commas; values follow usual type rules that work as if they were listed in a file (i.e. string $FFFF is a number in hexadecimal form). Double ,, doesn't start next const but is converted to regular ,. --chdir=$ Sets new working directory for ApiHook and its child processes. See also --newdir. If starts with $ refers to ApiHook's root. --newdir=. Launch only: current directory to set for the newly launched process. This doesn't affect ApiHook's startup CWD - it's set by --chdir. This is relative to --chdir). If starts with $ refers to the program's folder. --cl="extra command line" "" Launch only: command line for newly launched process; use quotes if it contains spaces. --proc-flags=20 Launch only: custom CreationFlags (an integer) for the new process. CREATE_SUSPENDED (0x0004) is always present. CREATE_NEW_CONSOLE (0x0010) is set unless overriden. --open-flags=1066 Launch/inject: Custom DesiredAccess for opening target process. Defaults to PROCESS_QUERY_INFORMATION, PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION and PROCESS_VM_WRITE (0x042A). Attach/extend: Custom DesiredAccess for opening process' main thread. Defaults to THREAD_QUERY_INFORMATION, THREAD_SUSPEND_RESUME and THREAD_TERMINATE (0x0043). --suspend=1 If disabled, removes CREATE_SUSPENDED flag from --open-flags. Some applications do not start up properly or just terminate immediately after ApiHook runs - try starting them with this option disabled. --debug-loader=0/1/r 1. Outputs the memory address to which the loader code was written in target process (using VirtualAllocEx); 2? If --debug-loader is r then target's main thread isn't suspended - useful if your debugger doesn't list processes that were suspended right from the start; 3. Waits for Enter before resuming the loader so that you can attach a debugger to its location and set a breakpoint; 4. Disables timeout for the loader thread to exit. Outputs loader thread exit code only if it has encountered an error. --module=[library.dll|*] "" Name of image (EXE or DLL file) to bind prologue hooks to. If empty only reports main module's hits (the program EXE's), if * hooks all loaded modules including system like kernel32.dll. Otherwise is image name part like kern; first matching module is hooked. Library Logging ApiHook library and loader are communicating via a named pipe. Since DLLs have no means of directly outputting messages ApiHook library by default records its messages into a file named ApiHook.dll.log that is attempted to be created in one of the following locations in turn: %TEMP% %APPDATA% \ (current disk root) C:\ D:\ Logging can be altered after the ApiHook library and its loader have successfully established interprocess connection by using --lib-log loader option (see above). Until this happens ApiHook DLL will create a log file and record initialization messages there. Also, ApiHook library always logs all its messages including debug and pipe data using OutputDebugString API function - you can use DebugView to view them. Known problems ReadFile hooks. If your script sets up hooks on this function Apihook library might be crashing with Access violations at random times. If enabling --thread-safe doesn't fix this try using --lib-logs and --detach to disconnect the library from the loader. This will also help with performance. Crashes might also occur when using --module=* on a high-load process. This problem is usually due to ReadFile being actively used by the library in its interprocess communication with the loader. Target process immediately closes. By default ApiHook loader starts new process with CREATE_SUSPENDED flag. This makes some programs exit immediately after creation - for them try using --suspend=0 option. Nothing appears to operate. When hooking functions with ANSI/Unicode variants (like MessageBoxA/W) by prologue be aware default --module="" will rule out calls not originating from the program itself. Thus if you hook MessageBoxW and the program calls MessageBoxW you will see no activity. You can either hook MessageBoxW instead, hook both or use --module=* but be ready for excessive output by other libraries including system ones like kernel32.dll for widely used functions like CreateFile.
conf[CreateFileW] log Opening :fn as :distrib in :access mode (share = :share). . log Returned handle <^ X fmt>. [CreateFileW] if :flags 0 NEQ log dwFlagsAndAttributes = :flags
The above script will output at least two lines on each CreateFileW call. The third line with file attributes (FILE_ATTRIBUTE_NORMAL, etc.) will only be output if any attributes were passed to the procedure (second
conf[CreateFileW] block above).
conf[RegOpenKeyW] if :sub microsoft posi log Opening reg key :sub from :key.
conf[CreateFileW] save fn . save <h2fn ^ s fmt cat up> ^fn [ReadFile] log Reading :size bytes from <h2fn :h s fmt cat up load> (handle = :h:X).
See also save action description.
This is more of a section for myself. The first version of ApiHook was written from scratch to initial release in 8 days (with varying time per day). I was interested in comparing its code statistics (codelines) with my other project – Hanagatari – which took me 6 days to be (relatively) completed.
So, ApiHook 0.82 stats as reported by CLOC:
20 unique files. http://cloc.sourceforge.net v 1.54 T=1.0 s (6.0 files/s, 5033.0 lines/s) ------------------------------------------------------------------------------------ File blank comment code ------------------------------------------------------------------------------------ ./DLL/AhScript.pas 219 15 1098 ./Loader/ah.dpr 195 21 828 ./DLL/AhLowLevel.pas 173 14 689 ./Lib/AhCommon.pas 139 16 579 ./DLL/ApiHook.dpr 126 20 468 ./DLL/AhApiCatalog.pas 85 6 342 ------------------------------------------------------------------------------------ SUM: 937 92 4004 ------------------------------------------------------------------------------------
As it turns out ApiHook is 20% short of Hanagatari’s codelines (5225 in total). And it seems like I’ve been putting more blank lines since then – that’s interesting…