Skip to content

apitable ​

Tool tools/pe/apitable.py (program, Windows PE). Write the API table of a 32-bit Windows DLL: every export, its calling convention and the bytes of arguments it takes, read from the DLL's own code.

Run it with ./gameframe run apitable ARGS or python3 tools/pe/apitable.py. Source on GitHub.

Its own help ​

This is the help text in the tool itself, as ./gameframe tool apitable prints it.

text
Write the API table of a 32-bit Windows DLL: every export, its calling convention and the
bytes of arguments it takes, read from the DLL's own code.

Usage: apitable.py DLL [--header FILE ...] [--out FILE]
       apitable.py IMPORT.LIB [--header FILE ...] [--out FILE]

  DLL          a 32-bit x86 DLL, such as a 1990s card driver library (glide2x.dll, sgl.dll)
  IMPORT.LIB   or the library's import library from its SDK, when the DLL is not at hand.
               Its decorated names (_ATI3DCIF_Init@0) give the stdcall exports
  --header F   a C header of the API. Each export's prototype there gives its parameter count
               and the kind of value it returns. Give several headers to search them all
  --out FILE   write the table there (default: standard output)

Output: a tab-separated table with a header line and one row per export, sorted by ordinal:

  ordinal  export  symbol  convention  bytes  params  returns  evidence

  export      the name as exported (`_grDrawTriangle@12`, `sgl_render`)
  symbol      the plain name (`grDrawTriangle`)
  convention  stdcall (the callee removes `bytes` of arguments), cdecl (the caller does),
              data (the export is not code), or unknown
  bytes       argument bytes for stdcall, from the decorated name or the `ret N` the
              function ends with; 0 for cdecl
  params      the parameter count from the header, or - when no header has the prototype
  returns     from the prototype: float (float or double, which comes back in the x87
              register st0), void, int (anything else, in eax), or - without one
  evidence    where the convention came from: name (decorated), ret (the code), name+ret
              (both agree), lib (an import library), or a note such as `ret differs: 8`

The tool follows the code of each export from its first byte with capstone. It follows both
paths of each conditional branch. It also follows direct jumps. Each `ret` found gives the
bytes the callee removes. A single value is the answer. No value leaves `unknown`: the code
ends in a jump through a pointer, or runs past 4096 instructions. Two values are reported as
a conflict. An export inside a section that is not executable is `data`.

A replacement library must export every call the original exports. Each call must remove the
same bytes from the stack. Otherwise a game that calls one the library lacks fails to load, or
crashes after the call. This table lists the calls to stub (wrappers/stubgen.py) and then to
implement.

It does not read the DLL's imports, follow calls into other functions, or say what each
argument is. Standard library, pefile and capstone. tests/run.sh checks it against a DLL
built from our own C.

Code: Apache 2.0. Content: CC0 1.0. No game material: bring your own copy.