![]() |
![]() ![]()
Process Stalking is a term coined to describe the combined process of run-time profiling, state mapping and tracing. Consisting of a series of tools and scripts the goal of a successful stalk is to provide the reverse engineer with an enjoyable interface to filtered, meaningful, run-time block-level trace data.
![]() To install the IDA plug-in, copy the appropriate version of process_stalker.plw to the plugins folder in your top-level IDA Pro install directory. A successful install will generate the following message in the IDA log window upon restart:
[*] pStalker> Process Stalker - Profiler The IDA plug-in is used for pre-processing target binaries and generates the following outputs:
![]() Process Stalker IDA plug-in dialog The default options should be sufficient in most cases however here is a description of what they are:
[*] pStalker> Profile analysis 25% complete. Graphs are generated in the GML language specifically for GDE Community Edition, a freeware GML viewer available for download from www.oreas.com. Graphs are interactive, editable, sport instruction level coloring for easy reading and can be displayed with a number of layout algorithms such as hierarchical, orthogonal, symmetric, circular, etc... Entry points are highlighted in green, true/false branches are colored green/red accordingly and implicit edges are colored blue. Process Stalker - Tracer![]() The Process Stalker stand alone tracer utility requires no installation. The executable, process_stalker.exe, is a command line utility used to attach to or load a process:
usage:
$ process_stalker -a 864 --one-time
As breakpoints are hit Process Stalker will generate a log entry to STDOUT. The format of the entry is as follows. The first field, 004d68bc, displays the current tick count as reported by Windows. The second field, T:00000a74, displays the thread id that this breakpoint was reached in. The third field, [bp], displays the recorder that this entry was written to. Recording is off by default and is denoted by the tag '[bp]', otherwise the recording number would appear in the tag. The fourth field, 6747D0EB, displays the address of the breakpoint. The final field, xor eax,eax, displays the disassembly of the instructions at the breakpoint address.
---------- MODULE LIST ---------- Use the [q] hotkey to close any open recordings as well as the debuggee. Alternatively, on systems that support detaching (Windows XP and 2003) use the [d] hotkey to close any open recordings, reset all breakpoints and detach from the debuggee. Python Utilities![]()
A number of Python utilities are provided for the instrumentation of breakpoint lists, recordings, statistics and graphs. Note, the utilities that deal with recordings expect processed recordings (see ps_process_recording). A description and sample usage of each utility follows (alphabetical order):
ps_add_register_metadata <recording-regs> <GML file> [rebase address] The output graph is rendered with live register data and shows the immediate 32-bit register values. Registers that are determined to be potential pointers into stack or heap space are dereferenced and displayed. If an ASCII or UNICODE string is detected then it will be displayed in place of the 4-byte aligned data.
ps_bp_filter.py
The "in/out" modifier specifies whether or not the functions in the function list are to be filtered into the output list or out of the input list. The output of this file must be redirected into the new breakpoint list. Example usage:
ps_bp_filter <in bpl> <out bpl> <functions|<func1>:[func2]:[...] <in | out>> This utility is used frequently for fine tuning our process stalking to only interesting areas of code. Consider the following when stalking a process with a GUI. Take a recording of the target process while heavily interacting with the GUI. Use the ps_recording_to_list utility with a 'module' argument to extract the function list from the recording. Use ps_bp_filter to remove these uninteresting nodes from the breakpoint list.
ps_gde_fixup.py
ps_gde_fixup <in.gml>
ps_graph_cat.py
ps_graph_cat [-x <xrefs file> -b <base addr>] <file_1> [file_2] ... The cross-references file name and the base address are frequently used variables so it helps to add them to your environment prior to starting your work. Note that there is a significant performance decrease visualizing and interacting with large interconnected graphs.
ps_graph_highlight.py
Command line options are made available to control whether or not "all" or just "hit" interesting nodes should be highlighted as well as controlling the highlight conditions (reps, ints, str, mem, all). Example usage:
ps_graph_highlight [--nodes hit,missed,all] [--reps,--ints,--str,--mem] <GML>
ps_idc_gen.py
ps_idc_gen <file 1> [file 2] [...]
ps_process_recording.py
ps_process_recording <recording> [base address] The above command would generate a separate file for each encountered thread found the recording. Filenames are generated in the form: recording.0.thread_id-processed. This utility expects the appropriate .bpl breakpoint lists to exist in the current directory.
ps_recording_to_list.py
ps_recording_to_list <processed recording> [module] The optional 'module' argument must be used if the output of this utility is destined for use as an argument to ps_bp_filter.
ps_recursive_view.py
ps_recursive_view <GML file> <xrefs file> <base address> The same caveat applies as above, large interconnected graphs tend to render very slow. If you need to visualize large graphs use a faster layout algorithm like circular as opposed to hierarchical or orthogonal.
ps_state_mapper.py
ps_state_mapper <xrefs file> <base address> <recorder0 recorder1 [...]> One interesting use of this visualization is for charting the maturity of a developing fuzzer. A state mapper view can be generated on a weekly basis for example and compared to see if the fuzzer is getting "deeper" into the target process space.
ps_view_recording_funcs.py
ps_view_recording_funcs [-f function] [-x <xrefs file> -b <base addr>] <recording> Specify the optional 'function' argument to view the hit-graph of only that function. Note, this utility expects the .gml function graphs to exist in the current directory. ps_view_recording_stats.py
ps_view_recording_stats <recording> [sort] Specify the optional 'sort' argument to sort the output by count/time as opposed to address. ps_view_recording_trace.py
ps_view_recording_trace <recording> The generated graph, while not pretty, is useful for locating run-time logic driven loops. Note, this utility expects the .gml function graphs to exist in the current directory. Python Instrumentation API![]() The bundled Python utilities will provide adequate instrumentation functionality for the majority of users. To appease the needs of "power" users an API was developed to abstract tedious tasks such as file parsing and graph rendering. The API is inline documented via Epydoc and exports the following modules/classes:
![]() A plethora of visualizations can be generated using a combination of filtering and graph instrumentation with the above described utilities. To help familiarize new users, the following examples and graph screenshot excerpts have been compiled. The following graph excerpt demonstrates the inclusion and highlighting of "chunked" function code: ![]() Nodes highlighted in yellow represent chunked segments.
Chunked functions are generally seen in Microsoft optimize compiled binaries. At compile time, the Microsoft compiler will combine and extract similar and "less likely to be executed" code from various functions and store them outside the contiguous function space. Function fragments are displayed as the color maroon in the IDA navigation bar. The standard IDA grapher does not support visualization of function chunks and will simply display them as dead end red blocks. With the introduction of IDA 4.7 came chunked function support in the SDK but the grapher does not utilize it. To the security engineer, "less likely to be executed" code is interesting, especially as common security handling routines are classified as such. The process stalking suite supports chunked functions in both tracing and visualization.
![]() Nodes highlighted in red represent run-time hits.
A common difficulty that arises when conducting an assembly level code audit is the determination of where to start looking. Most binaries contain a number of functions ranging from the low end of ~500 well into the thousands on the high end. Researchers commonly start with library routines known to process user input, such as recv(), and manually trace through potentially traversed code. Using the process stalking suite we are able to quickly and visually determine which functions are involved in processing specified inputs and equally as important which of the underlying basic blocks within that function were traversed.
![]() Nodes highlighted in purple represent potentially "interesting" nodes.
The ps_graph_highlight utility is easily extensible, customizable and controllable via command line options. The analyst can specify whether or not to highlight all potentially interesting nodes, only those that are potentially interesting and were traversed during the trace or only those that are potentially interesting and not traversed during the trace. In the above graph excerpt an inline string copy is immediately detected and highlighted. Three nodes earlier in the graph, the branch condition can be analyzed to determine how the supplied user input can be modified to alter the flow of control to reach this potentially vulnerable block.
![]() Register values are displayed with automated "smart" dereferencing and string detection. The nodes from the above graph were rendered with live register data and show the immediate 32-bit register values. Registers that are determined to be potential pointers into stack or heap space are dereferenced and displayed. If an ASCII or UNICODE string is detected then it will be displayed in place of the 4-byte aligned data. A number of layout algorithms are available to the user, each with it's own benefits. The following graph excerpt shows a number of functions displayed in a single graph with hierarchical layout applied: ![]() Low-res excerpt from graph concatenation of multiple graphs. The above graph may have been generated with ps_view_recording_funcs or ps_graph_cat. Again, there are a multitude of approaches leaving flexibility in the hands of the researcher. As mentioned above the red nodes represent basic blocks that were "hit" during process tracing. The excerpt is from a much larger graph containing at least twenty functions. While it is not recommended viewing such a large graph concatenation as interconnected, the researcher could do so: ![]() Low-res excerpt from interconnected graph concatenation of the same multiple graphs from above. The above generated graph is very complex and presented only as a demo. Further filtering could and should have been applied to reduce the function set necessary to visualize. Alternatively in cases where such large function coverage is unavoidable, function groups should be created and analyzed separately. With extremely large interconnected graphs the circular layout provides a fast visualization: ![]() Circular layout view. Circular layouts are fast in comparison to hierarchical and orthogonal. Though not as useful it certainly is pretty and may be useful in manually spotting and filtering frequently referenced (high edge count) yet unimportant functions and blocks. As a final example consider the following generated cluster orthogonal view: ![]() The cluster orthogonal view is used to visualize state maps and group function nodes together. Utilities that display multiple combined functions, such as pg_graph_cat, group nodes into clusters by their containing function. This view is useful for visually separating functions on screen while allowing the researcher to see the relationships between them. The explorer panel in GDE constructs collapsible labeled trees allowing for quick search and access to functions and their underlying nodes. | ![]() |