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.
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.
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.
shah.exe launch target.exe script.oo
shah.exe l target script
– short form of the above callshah l target
– the same again – Script.oo and Script.txt are detected in current folder and used if omitted from the command lineshah.exe i target myhook.dll
ApiHook is still in beta version – all listed options are supported but not all tasks are.
ApiHook distribution usually contains the following files:
Most of the configuration files use common INI-style syntax that, however, differ in details.
ApiHook uses Windows INI file format for its configuration and script files. However, there are certain differences listed in corresponding sections.
conf[ReadFile]
confprefix notation = on
defines a key «prefix notation» with value «on»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:
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;
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.
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:
With Float and Boolean values set of constants cannot be used and thus only matches on usual «is equal» condition.
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:
conf[Constants]
section of the script file;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
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:
conf[Constants]
– defines extra constants for this scriptconf[ApiHook]
– sets hooking optionsScript 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:
Boolean options are considered off (False) if none of the below is matched:
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:
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.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:
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:
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.
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.
ApiHook captures all x86 registers: EAX, ECX, EDX, EBX, EBP, ESI, EDI and:
asmCALL
address being pushed onto the stack – in other words,
asm[ESP]
points to the last pushed parameter.asm[EIP]
points after the original
asmCALL
instruction.Registers are accessed by their upper-case name in expandable strings.
ApiHook allows accessing routine return value in post-actions. In expandable strings and RPN expressions this is done using circumflex (^) symbol.
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:
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 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 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 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 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.
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 copies memory block of given length into a user file (location of user files is configured using --user-files loader option).
Action parameters:
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 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:
Action parameters:
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 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 outputs stack trace of the called routine. It accepts the following optional parameters (first two can be given in any order):
asmESP+x
offset.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.
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:
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
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
Function names are case-sensitive.
Arithmetic (expect two integers or floats): +, –, *, /, % (modulus).
Bitwise (expect two integers): << (shift left), >> (shift right), & (AND), | (OR), ^ (XOR).
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. |
Constant names are case-sensitive.
Operator names are case-sensitive.
Numeric (expect two floats): LEQ (Less or EQual, <=), GEQ (Greater or EQual, >=), LESS (<), MORE (>).
ApiHook generally has two kind of expressions:
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.
This section describes ApiHook internal working mechanics. User-end information is given in previous sections: The basics and The reference.
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.
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:
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:
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:
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:
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:
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:
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.
This section briefly describes the structure of ApiHook source code:
3rd party/standalone libraries (located in Lib):
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.
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.
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…