ApiHook – Win32 API hooking tool
  1. 1. Download & license
  2. 2. Features
  3. 3. The basics
    1. 3.1. Common file syntax
    2. 3.2. API Catalog
      1. 3.2.1. Types
      2. 3.2.2. Set of constants
    3. 3.3. Constant definitions file
    4. 3.4. Script file
      1. 3.4.1. Options
      2. 3.4.2. Procedures
  4. 4. The reference
    1. 4.1. Call context information
      1. 4.1.1. Registers
      2. 4.1.2. Return value
      3. 4.1.3. Parameters
      4. 4.1.4. Variables
    2. 4.2. Actions
      1. 4.2.1. Log
      2. 4.2.2. Int03
      3. 4.2.3. Save
      4. 4.2.4. Dump
      5. 4.2.5. Light
      6. 4.2.6. If
      7. 4.2.7. Stack
    3. 4.3. RPN expressions
      1. 4.3.1. Value types
      2. 4.3.2. Entities
      3. 4.3.3. Functions
      4. 4.3.4. Constants
      5. 4.3.5. Operators
    4. 4.4. Expandable strings
  5. 5. The mechanics
    1. 5.1. Injecting
    2. 5.2. Named pipe connection
    3. 5.3. Hooking
    4. 5.4. Source code map
  6. 6. Loader help text
  7. 7. Examples
    1. 7.1. Logging opened files
    2. 7.2. Logging opened registry keys
    3. 7.3. Logging file reads with corresponding file name
  8. 8. Misc – source code stats

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 & license

ApiHook is released in public domain – feel free to do anything you want with its source code or binaries. I will always appreciate a back link and a comment, though :)

First release was on 10 February 2012.

http://proger.i-forge.net/Мои проги/Delphi/ApiHook/github.png

Download ApiHook from GitHub. Binaries and all runtime data are found in the Out directory (Win32). If you don’t need sources you only need the contents of this folder.

Sources are built with Delphi 7. 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.

Features

http://proger.i-forge.net/Мои проги/Delphi/ApiHook/help.png

Invoking examples:

ApiHook is still in beta version – all listed options are supported but not all tasks are.

The basics

ApiHook distribution usually contains the following files:

ah.exe
Console loader program; injects the hook library into a process where it performs its tasks.
ApiHook.dll
ApiHook library – the core component hooking target routines based on given script and running actions when they’re called.
Catalog.ini
API Catalog defining routines that can be hooked in the script.
My.ini
The same as Catalog.ini meant for user-defined or overriden) routines (both files are merged together) to avoid messing up with distributed catalog file.
Constants.ini
Optional Constant definitions file listing constants that can be used to format logged values or in several other places. Can also be part of the API Catalog and script files under the section [Constants].
en.ini
Optional language file; if absent ApiHook frontends (console ah.exe) uses LANG resource inside its EXE.

Most of the configuration files use common INI-style syntax that, however, differ in details.

Common file syntax

ApiHook uses Windows INI file format for its configuration and script files. However, there are certain differences listed in corresponding sections.

Common file syntax rules:

API Catalog

API Catalog is a configuration file in common file syntax defining known routines that can be hooked in the script. Usually, a routine is an exported procedure from some Link Library (DLL).

Each routine (also called procedure here) starts with section header ( conf[ProcName]) and has the following properties:

Lib
DLL file name, e.g. kernel32.dll. Can be empty (omitted) if Addr is specified.
Addr
Optional hexadecimal routine address (relative to Lib’s image base or process’ EXE if it’s empty). Can be used to hook internal non-exported functions either within main module or within one of its DLLs.
Call
Calling convention – currently stdcall and cdecl are supported, plus non-standard point type (lets it create «breakpoints» in arbitrary locations of program code, not instead of function beginning). Note: this affects how parameters and return value are retrived; for stdcall, parameters are oly available for pre-actions.
Prologue
Size of prologue in bytes of the original routine body that can be safely transferred to another location without breaking an opcode midway. If unset or not a number hot swapping is used; the same happens if it’s smaller than the required minimal length (6 bytes).
Return
Return value type.
AnyCaller
If set to 1 turns off skipping of calls not originating from the program’s code (that is, from a library). Acts as if --module=* was passed for this particular function. Useful when hooking WinProc and similar callback functions that are invoked by a library.

All of the above props are specified using keys of the same name.

Apart from properties each routine has zero or more parameters (arguments) – they are also specified in confKey=Value fashion but key starts with a colon (:). For example:

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

And its Delphi equivalent from Windows.pas:

function CreateFileA(lpFileName: PAnsiChar; dwDesiredAccess, dwShareMode: DWORD;
  lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition,
  dwFlagsAndAttributes: DWORD; hTemplateFile: THandle): THandle; stdcall;

Types

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.

Note: value «type» is sometimes referred to as value «kind» (especially in the source code) because of Delphi having Type as a reserved name.

Set of constants

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:

Numeric
Bitwise AND operator is used: (ParamValue and ConstValue) <> 0.
String
Substring (WideString) is searched: Pos(ConstValue, ParamValue) <> 0.
Bytes
The same as String but raw substring is searched: Pos(ConstValue, ParamValue) <> 0.

With Float and Boolean values set of constants cannot be used and thus only matches on usual «is equal» condition.

Constant definitions file

Constants are used in generating log messages and in some other places. Constants can be specified as parameter values in API Catalog. Sets of multiple constants is also allowed.

This file (usually Constants.ini) uses Common file syntax. It does not contain any sections.

Constant names consist of a-z, A-Z, 0-9 and «_» symbols.

Constant values are defined separately and can be of any RPN type. There are several ways of defining a constant:

  1. In the conf[Constants] section of the script file;
  2. Standard – using a separate constant file (its location is controlled by --consts loader command line option which defaults to Constants.ini);
  3. Using --define command line option of the loader.
  4. In the conf[Constants] section of the API Catalog;

Constants with the same name defined in different places override each other in the above order (item overrides those above it).

Constant value is specified using the uniform format:

Everything that doesn’t match any of the above rules is treated as a String.

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

Script file

ApiHook script file is what drives the hooking process; it tweaks the hooking process and lists action to be performed on each hooked routine call.

This file (usually with .oo or .txt extension) uses Common file syntax. There are 3 kinds of sections:

  1. conf[Constants] – defines extra constants for this script
  2. conf[ApiHook] – sets hooking options
  3. Everything else – hook actions for a specific procedure (must be listed in API Catalog)

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.

Options

Hooking options are defined in conf[ApiHook] section of the script file. Currently there is only one option:

prefix notation
Switches built-in RPN expression evaluator from postfix notation (e.g. 1 2 + 3 -) to prefix (- 3 + 2 1).

Boolean options are considered off (False) if none of the below is matched:

  1. The value is omitted – defaults to True;
  2. The value is given and is not 1, on, yes, y or true (case-insensitive).

Procedures

Each non-special section of the script file sets up a block of hook actions that is ran when target program calls listed routine (must be defined in API Catalogue).

Procedure conf[sections] in the script file have several features:

  1. A section can be entirely escaped by putting ; or # after the opening [ – ApiHook will ignroe such section altogether. The same effect can be achieved by commenting out the conf[header] and all lines inside it but thiis shortcut is often more convenient;
  2. Section (procedure) names are case-insensitive;
  3. If section name contains a space everything after it is ignored; this is useful to create section-comments – for example, if a section contains if action you can name this action block as conf[ReadFile - file header] to remind yourself what this block is for.
  4. Sections ending on asterisk (*) switch hooking mode from prologue rewriting to Import Address Table patching. This means that only calls made by the program itself, not by DLLs loaded into its address space, will be caught which reduces the overhead. However, dynamic imports (e.g. via GetProcAddress) won’t be hooked.

Each non-blank and non-comment line in procedure section defines a single action. Each action can be ran on 3 conditions determined by the symbol that line starts with:

  1. Anything apart from below defines pre-action – ran before the call to the routine is invoked;
  2. Period (.) defines post-action – ran after the call is done;
  3. Asterisk (*) defines both pre- and post-action – exactly the same as duplicating the same action line as both pre and post.

Usually action lines are indented with two spaces for pre-actions and prefixed with corresponding symbol and a space for others. For example:

conf[CreateFileA]
  log Opening file :fn...
. log Returned handle :h:x.

The above script sets up a hook on CreateFileA Windows function and adds two log actions to it:

  1. When CreateFileA is called it outputs the name of file being opened;
  2. After the procedure is called it outputs returned handle in hexadecimal notation using shorthand call of the fmt RPN function.

ApiHook allows nested interseptions of procedures (e.g. CreateFileA calling CreateFileW and with both routines hooked in script file) but risk of crash increases with each nesting level.

Download ApiHook | Page top.

The reference

Call context information

This section describes all information that can be accessed from within a hooked procedure script block.

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.

Registers

ApiHook captures all x86 registers: EAX, ECX, EDX, EBX, EBP, ESI, EDI and:

Registers are accessed by their upper-case name in expandable strings.

Return value

ApiHook allows accessing routine return value in post-actions. In expandable strings and RPN expressions this is done using circumflex (^) symbol.

Parameters

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.

In expandable strings and RPN expressions parameters are prefixed with a colon (:) and can be referred to by name (:param) or index (0-based, e.g. :0). The same is valid for registers.

Parameter name, index or register can be followed by one of these (ANU = AlphaNumeric character or Underscore):

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.

To prevent this from happening refer to the parameter in upper-case form – since parameter names are case-insensitive this will work. Examples:

  1. log :dwShareMode – outputs something like 2 [FILE_SHARE_WRITE];
  2. log :DWSHAREMODE – outputs just 2 – the original value.

Variables

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.

Both lists can be read using load function and written using save action, the only difference is how variables in them are referred to:

Apart from this different variable names are case-insensitive.

Actions

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.

Standard ApiHook actions are defined in AhScript.pas – each class being a separate action.

Log

Log simply outputs a string to the console or any other configured means, such as a log file. It accepts an expandable string which is simply output.

Example that outputs the hexadecimal address of the routine calling instruction (EIP register) – see also routine parameters:

conf[MessageBoxW]
  log MessageBoxW called from :EIP:x.

Int03

Int03 is another very simple action that just calls for software debugger interrupt. If you have a debugger attached to target process (which has ApiHook library loaded) doing this action will trigger breakpoint behaviour. This is sometimes useful in investigation of crashes.

This action has no parameters.

Save

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.

This action can be used to save parameters in pre-action for later use by post-actions in the same action block.

It has several call forms:

  1. One or more parameter saving – Save is given comma-separated list of parameter names that it saves to the variable of the same name, either context or global ;
  2. Setting single variable of the exact name – evaluates an expression and stores the result in specified exact (invariable) name; for example: save bufAddr :buf:X:fmt – stores value of buf parameter in upper-case hexadecimal form in context variable «bufAddr»;
  3. Setting variable with variable name – similar to the above but stores evaluated value in variable named after executing the second expression; for example: save <h2fn ^ cat up> ^fn – writes variable named «h2fn» + routine return value (as a result of cat function that concatenates both parts) and converts it to upper case (to address a global variable slot).

Examples of different Save 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

Dump copies memory block of given length into a user file (location of user files is configured using --user-files loader option).

It has two forms:

  1. dump fileName startAddr .. size (spaces around .. are optional)
  2. dump fileName startAddr -- endAddr (spaces around .. are optional)

Action parameters:

fileName
An expandable string that additionallly may contain (or be an) asterisks (*) which are replaced by a sequental dump number (first dumped file has index 0). Can also be a relative or absolute path – directories that don’t exist will be created. If file name has no extension .dat is appended.
startAddr
RPN expression specifying the memory address to copy data from.
size, endAddr
RPN expressions specifying where dumped memory block ends.

If size or endAddr evaluates to a value less than startAddr an error occurs on each attempt to perform the dump.

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 []

Light

Light action lets you examine blocks of data right on the spot with Lightpath modelling language. For example, you can break down data that the program reads into a tree or illustrate network communication.

This action is similar to Dump and has two forms:

  1. script:model startAddr .. size (spaces around .. are optional)
  2. script:model startAddr -- endAddr (spaces around .. are optional)

Action parameters:

script
File name (e.g. my.lp) and if omitted defaults to target program’s EXE base name plus .lp extension (it’s also added if this has no extension).
Model
The name of subsection in that script; first supported section is used if omitted.
startAddr
RPN expression specifying the memory address to copy data from.
size, endAddr
RPN expressions specifying where dumped memory block ends.

If both script and model can be omitted a single colon (:) is written instead. See also notes on the Dump action.

Example that shows the structure of packets being sent with recv:

conf[recv]
  save buf

; this would save the buffer:
;. dump *-recv ^buf..^

; this breaks it up:
. light : ^buf..^

If

If stops execution of its action script block if the condition doesn’t evaluate to True. Since actions are ran in the order they’re listed If only stops actions following it from being performed.

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>.

Stack

Stack outputs stack trace of the called routine. It accepts the following optional parameters (first two can be given in any order):

  1. Format character used to prefix stack values with – any character value for fmt function with an integer argument is valid plus two extra symbols:
  2. Stack depth to trace – defaults to 30.
  3. Base address for tracing – defaults to asmESP itself; this parameter is an RPN expression that must evaluate to an integer.

Once called, Stack outputs a log message like this (on one line):

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+0], 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.

Trailing .dll, if preset, is removed. If the address belongs to the main program itself Stack outputs SELF.

Note that original caller address is not inculded – it’s stored in the EIP register.

RPN expressions

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.

A typical RPN expression looks like this:

^var 'text' pos GEQ 3 :param & $0F NEQ 0 AND

The above expression reads as follows:

  1. Take the value of var variable and string text and run pos function on both operands; the result is pushed onto (now empty) stack;
  2. Take the result and compare it with integer value 3; boolean result is pushed onto the stack (now it contains one value);
  3. Take the value of param routine parameter and run bitwise AND function (&) on it;
  4. Take the result of above calculation and compare it with 0;
  5. Finally, take all stack values (two) and compare them using logical AND.

The result of this expression is boolean and can be used, for example, by if action.

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

Value types

ApiHook uses RPNit unit from the D7X library and thus its values can have the same types:

Float
All numbers regardless of their fractional part are stored as double-precision floating point values. In RPN expression they are specified using traditional syntax:
Boolean
Logical type that has two values (can be accessed using corresponding consants): TRUE and FALSE.
String
A Unicode string. Note that this should not be treated as an array of bytes but rather as a logically opaque «string» object (array of characters). In RPN expression, everything that is not a function, content or another data type is a string; string type can also be forced by surrounding a value with single apostrophes () – in this case the string may contain spaces and other symbols that otherwise would break it into separate operands. Note that such strings must use quoting by doubling any apostrophe that appears within them – for example, 'That''s a fine day' evaluates to the string value «That’s a fine day» – with single apostrophe.
Raw
bytes An array of raw byte values; unlike /.Strings// this type cannot be inserted into an RPN expression directly and usually appears as a result of calculations.

Entities

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

Functions

Function names are case-sensitive.

Arithmetic (expect two integers or floats): +, , *, /, % (modulus).

Bitwise (expect two integers): << (shift left), >> (shift right), & (AND), | (OR), ^ (XOR).

String:

arg
Returns human-readable version of the called routine parameter; expects one string value. Example: dwMoveMethod arg – note that parameter name is not prefixed with colon (:) and is a string because if its value was already retrieved (by writing :dwMoveMethod arg) it won’t be possible to link it with parameter listed in API Catalog and, thus, with its constants.
cat
Concatenates everything on stack (must be at least two strings) into a single string. Example: 'he' 'llo' ' ' 'world' cat – returns «hello world»; the same can be written without apostrophes since «he», «llo» and «world» are not the names of any RPN entity: he llo ' ' world cat.
catX
The same as cat but with limited arity (the number of arguments a function accepts) – «X» is a number of arguments to take off the stack and concatenate. Example: a b c cat2 rev cat – first concats «b» and «c» into «bc», then reverses the order of stack items and concats remaining «a» (now on top of the stack) and newly pushed «bc» into a single string «bca».
chr
Returns string with single Unicode character by given integer index; expects one integer value. Example: $203C chr – returns double exclamation mark.
down
Converts given string to lower-case.
fmt
Formats a single value of arbitrary type using given format character; returns a string. Valid format characters: d x X f s u – details. Example: 'abc' d fmt – returns «61 62 63».
load
Returns a a context or global variable with given name; requires one string. Example: fn ^h cat2 load – reads variable named «fn» + the value of «h» variable (see also RPN entities).
pos
Searches for a substring (needle) in given haystack; expects either two strings or two raw byte values; returns both integer (index of first match – -1 if none) and boolean (indicating if the substring was found or not). Example: haystack hay pos – returns 0 («hay» is found in «haystack» at first position) and TRUE.
posi
Case-insensitive version of pos that only works on two string operands.
rev
Reverts the order of all stack values (can be of any type); needs at least one value; returns nothing. Example: 1 '2' 3.33 rev – turns stack into 3.33 (float), 2 (string) and 1 (integer).
up
Converts given string to upper-case.

fmt function operands

Format /
Operand
d x X f s u
Integer Hexadecimal representation: 7B

Shortest possible string representation (as %g)

String representation: 123
Float

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.

Memory:

[] or DW[]
Returns 32-bit unsigned (DWord) value located at given address; expects one integer value. Example: ^lpBuffer DW[] – returns DWord located at previously saved lpBuffer parameter.
W[]
Returns 16-bit unsigned (Word) value – see also DW[].
B[]
Returns 8-bit unsigned (Byte) value – see also DW[].

Other:

nil
Returns TRUE if current stack top value is undefined, i.e. is not an integer, boolean, string or raw.

Constants

Constant names are case-sensitive.

TRUE
Boolean TRUE.
FALSE
Boolean FALSE.
NIL
«No value», «null», «undefined» – i.e. a value that is not an integer, boolean, string or raw. Usually used in comparisons, for example, h ^h cat load NIL NEQ returns TRUE if a variable named «h» + the value of variable «h» (^h) exists and FALSE otherwise. See also nil function.

Operators

Operator names are case-sensitive.

NOT
Negates given numeric or boolean value. Example: $8000 NOT results in $7FFF value (all bits inverted).
Logical AND
expects two boolean values.
Logical OR
expects two boolean values.
EQU
Tests two values for equality; returns TRUE if both have the same type and are equal, if both are NIL (undefined) or if one is string and another is integer or float – in this case they are converted to a string and compared. Other operand combinations will cause an error.
NEQ
Not equal version of EQU.

Numeric (expect two floats): LEQ (Less or EQual, <=), GEQ (Greater or EQual, >=), LESS (<), MORE (>).

Expandable strings

ApiHook generally has two kind of expressions:

  1. RPN expressions – which are like math formulae and return any valid value type;
  2. Expandable strings – contain special sequences (routine parameters, registers, nested RPN expressions, etc.) that are expanded; always return string value.

The syntax of expandable strings is similar to some of RPN entities except that string is not evaluated as an expressions but is simply the source string with special sequences replaced.

Expandable strings can contain the following special sequences:

Everything else is returned as it. All of the above sequences can be escaped by doubling their symbol:

A typical expandable string looks like this and is used, for example, in log action:

Seeking file :h:X (name <h2fn :h x fmt cat up load>) as :method to :pos.

The above example might result in the following string when expanded:

Seeking file 40C (name C:\boot.ini) as FILE_BEGIN to 102.

Download ApiHook | Page top.

The mechanics

This section describes ApiHook internal working mechanics. User-end information is given in previous sections: The basics and The reference.

Injecting

ApiHook uses fairly standard injecting mechanism: first a process is created or opened, then a remote thread is created that loads the ApiHook library.

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.

Named pipe connection

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.

Both ApiHook parts are communicating in client-server fashion (loader acts as server). Server sends commands in simple format:

  1. First goes the upper-case command name wrapped in angular braces, e.g. <HELLO>;
  2. Then goes command data, if any;
  3. After this server sends EOS (End Of Stream) string indicating that command has been fully transmitted.

The above scheme doesn’t provide much stability in case of unknown commands, etc. but it’s not intendent to do so because protocol is meant for usage by ApiHook components alone.

After the cilent (library) has connected to the server (loader) commands begin issuing in this order:

  1. HELLO – sets up the connection and basic library settings. After the command name these parts are send in turn:
    1. VERSION – after this the server sends its own version in hex format (e.g. $0100 which means v1.0);
    2. LOG LEVEL – then 32-bit value follows; after recieving this the library will filter all of messages it transmits using the named pipe and skip those that are below this level;
    3. LOGS – configures library logging; before receiving this the library attempts to create a log file in several locations starting with %TEMP% (see loader help for details) where it writes errors occurred during initialization;
    4. USER PATH – sets path for user-generated files – such as memory dumps.
  2. CATALOG – then full API Catalog text follows; it also contains user-defined constants and constants from Constants.ini.
  3. SCRIPT – then script file follows; this command causes library to set up the hooks (resetting them if some script has been already loaded);

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:

  1. Message format string – since it contains no variable arguments inside it’s possible to translate library messages as well as loader’s. Each argument is represented by single dollar symbol ($); if $ is doubled it appears as is.
  2. Message arguments – pipe-separated list of arguments replacing dollar symbols ($) in message format string. Any pipe symbols (|) appearing in arguments are doubled.

Eventually the library will disconnect from the loader and this happens by sending DETACH command which closes client side of the named pipe and terminates library pipe thread.

DETACH is followed by a string indicating the type of shutdown:

Hooking

Attention: mechanism described here (hot prologue swapping) was used in v0.83 and earlier but has changed in v0.84.

Hooking is entirely the prerogative of ApiHook library which sets up routine hooks on the process it’s loaded into after receiving the SCRIPT command via the named pipe connection.

Low-level hooking work is coded in the AhLowLevel.pas unit.

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

On the low level the following is done to hook a procedure:

  1. Library described in corresponding procedure record of the loaded API Catalog is loaded in the target process if it didn’t already exist.
  2. Requested procedure is searched in the library’s export list.
  3. Preparation is complete and thus hooked procedure name is assigned a unique index as a more efficient form used to distinguish calls of different procedures since all of them eventually arrive to the same prehook and posthook procedures (see Jumper code below).
  4. Memory block of 6 bytes is made writable at the determined address.
  5. A new prologue corresponding to the following asm code is written:
asm68  XX XX XX XX       PUSH  <Jumper address>
C3                    RET

The above code imitates a jump to absolute address by first pushing that address onto the stack and then «returning» to it as if a asmCALL has been done.

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.

Jumper is a custom asm code unique per each hooked routine and is located in the ApiHook library address space. It does the following:

  1. Calls prehook procedure with corresponding procdure index;
  2. Saves return address (routine caller) to a 4-byte memory slot in library address space;
  3. Replaces return address with return to the same Jumper code and calls the original routine (from the original library, e.g. kernel32.WriteFile);
  4. When called routine returns posthook procedure with corresponding procdure index is invoked;
  5. Finally, a jump to absolute address (previously saved routine caller) is performed using the abovementioned memory slot;
  6. The target program continues operating normally.

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.

Assembly code of the Jumper is as follows:

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.

DoPostHook puts new prologue (redirecting the call to Jumper) back into its place, runs associated actions, destroys the call context and leaves the critical section (if configured).

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.

Source code map

This section briefly describes the structure of ApiHook source code:

ApiHook units:

DLL/AhApiCatalog.pas
API Catalog and related classes (parameter retrieving per supported calling conventions, etc.).
DLL/AhLowLevel.pas
Low-level routines – the nexus accumulating and dispatching all hooked procedure calls and associated actions. Among other things defines call context class.
DLL/AhApiScript.pas
Classes dealing with ApiHook script itself. A script contains procedures which in turn contain actions that are run by the low-level layer when corresponding procedure is invoked. This unit implements:
Lib/AhCommon.pas
Defines simple types and constants used by both ApiHook components and also base client/server pipe classes. Implements loogers (although currently only library uses them) and TAhConstants class handling constant definitions file.

3rd party/standalone libraries (located in Lib):

FastShareMem
Lightweight replacement for standard Borland ShareMem. Doesn’t need Borlndmm.dll.
D7X
Delphi 7 eXtension library – a set of mostly independent classes and functions. Freeware.

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 is built using Delphi 7.

Download ApiHook | Page top.

Loader help text

ApiHook is still in beta version – all listed options are supported but not all tasks are.

                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.

Download ApiHook | Page top.

Examples

Logging opened files

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

Logging opened registry keys

conf[RegOpenKeyW]
  if :sub microsoft posi
  log Opening reg key :sub from :key.

Logging file reads with corresponding file name

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.

Download ApiHook | Page top.

Misc – source code stats

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…