In this section

Volatility Symbol Tables: banners, isfinfo, ISF Files and Why an Exact Match Is Required

Module 0

Introduction

Six subs have now run plugins that worked, on an image that Volatility could read. This one is about the morning they do not, which arrives sooner than anybody expects and produces no error message to work from.

Every plugin in those six subs depends on layout information that is not in the image and never was, and when Volatility cannot find information matching the build it is reading, it cannot read a single field. A plugin with no fields to report prints nothing at all, which means the symptom of a total tooling failure is indistinguishable, on screen, from the symptom of a machine on which nothing happened. That ambiguity is what this sub is really about.

Scenario

A colleague sends you an image from a Linux server that was decommissioned last year, and every plugin you run returns an empty listing. No error, no warning, no partial output. You check your syntax, which is fine. You check the file, which is the right size and not obviously corrupt. Nothing you can see distinguishes this from a machine on which nothing happened, and the correct conclusion is not that the server was clean. It is that Volatility has never been able to read it at all, and the fix is a file that may no longer exist anywhere.

01

Confirming It Is Symbols and Not the Image

windows.info first, every time, for this reason

MF0.1 said to run windows.info before anything else, and the section below explains what it establishes. This is the sub where that habit pays for itself.

$ vol -f decommissioned.raw windows.info
Volatility 3 Framework 2.28.2
Progress: 100.00   PDB scanning finished
Variable   Value
$ echo $?
0

An info run that completes and prints no variables is the diagnostic. The command itself worked, the framework loaded, the image was read from disk, and nothing could be determined about what operating system produced it. That is a symbol problem, and it is a different thing entirely from a corrupt file or a wrong command.

Compare that with the shape of other failures so you can tell them apart quickly. A file that is not a memory image at all produces an error about being unable to stack layers or determine a format. A wrong path produces an error about the file. A plugin name that does not exist produces an error naming the plugin. None of those is silent, and silence specifically points at layout information.

The single most expensive mistake in early memory forensics is treating that silence as a result. Somebody runs a process listing, gets nothing, writes that no suspicious processes were found, and moves on. The sentence is not false in a narrow sense and it badly misleads every reader, because it implies an examination that did not happen. The correct sentence is that the image could not be analyzed, which is a limitation rather than a finding.

That is the same discipline this course applies to evidence, turned on the tooling instead: explain an absence before reasoning from it. An absence caused by a mechanism that produces absences indiscriminately tells you about the mechanism.

So the discipline is simple and it costs seconds. Run windows.info, or its equivalent for other operating systems, on every image before running anything else, and treat an empty result from it as a stop rather than as a step. Everything downstream is meaningless until that command produces output.

02

banners, and the String That Must Match

Asking the image what it needs

Once you know it is a symbol problem, the next question is which symbols exactly, and the image itself is the thing that answers it.

$ vol -f decommissioned.raw banners
Offset     Banner
0x1a3c0a0  Linux version 5.14.0-284.11.1.el9_2.x86_64 (mockbuild@x86-64)
           (gcc 11.3.1 20220421, GNU ld 2.35.2) #1 SMP PREEMPT_DYNAMIC

That string is the kernel's own description of itself, written into memory when it was built, and it is what a matching symbol table must carry. Read what is in it: a version, a build identifier, the machine that compiled it, the compiler version, and the linker version.

Every one of those is part of the match. The Volatility documentation is explicit that the banner in the symbol file must match the banner in the image exactly, including compilation time and compiler version, and that there is deliberately no simple way to make a near miss match. That is why a version number does not select a symbol table: two kernels an administrator would call the same version, built at different times or with different compilers, produce different banners and can lay their structures out differently.

The banners plugin works without symbols, which is the property that makes it useful here. It finds the string by scanning rather than by interpreting structures, so it is one of the few things that still works on an image nothing else can read. That is why it is the second command to run, immediately after an empty info.

The complementary command tells you what you already have.

$ vol -f decommissioned.raw isfinfo

isfinfo lists the symbol tables the installation knows about and the banners they carry. Between the two commands you have the string the image needs and the set of strings you hold, and the question stops being why nothing works and becomes whether those two sets intersect.

Why does banners work on an image where every other plugin returns nothing?pick one, then the tell

Those two commands are the whole diagnosis. If the banner from the image appears in the isfinfo list, the table is present and your problem is something else entirely. If it does not, you now know precisely which file you need, expressed in the only terms that matter, and the rest of this sub is about getting it or accepting that you cannot.

03

Where Symbol Tables Come From

Packs, generation, and a directory on your disk

Symbol tables are ordinary files, they live in a directory beside the framework, and understanding how they get there tells you what your options are when one is missing.

Volatility publishes packs for Windows, Linux and Mac, which are downloaded and placed in the symbols directory as they come. Those cover an enormous amount of ordinary work and are the reason most analysts never think about any of this. The framework also caches the mapping from identifiers to files, so repeated runs do not re-scan the whole collection.

volatility3/symbols/          where tables live
  windows/                    downloaded pack, keyed by PDB identifiers
  linux/                      downloaded pack plus anything you generate
  mac/
# the framework also caches the identifier-to-file mapping separately.

For Windows the situation is comfortable, and it is worth knowing why rather than being pleasantly surprised. Microsoft publishes debugging symbols for its own builds, centrally and durably, so the information a symbol table is built from is obtainable for a Windows image years after the machine is gone. That is one vendor, with an interest in keeping an archive, doing so reliably for a long time.

For Linux there is no equivalent. Debugging information ships as a package, distributions name those packages differently, there are a great many distributions, and a distribution has no obligation to keep packages for a kernel it stopped shipping. When the package is gone, the information a symbol table would be generated from is gone.

The Volatility documentation is candid about this rather than optimistic, and says outright that it may simply not be possible to find the right symbols for a Linux image. Mac sits in between, with far fewer kernels and a single distributor, which makes the match much easier to obtain.

Where the debugging information does exist, a converter turns it into the JSON format the framework reads, and the result goes into the symbols directory where it is picked up automatically. That is the generation path, and it is mechanical once you have the input. The difficulty is never the conversion step, which is mechanical and quick. It is obtaining the debug package for one specific kernel build, years after anybody had a reason to keep it.

04

The Order to Work Through It

Five steps, and the fifth is a legitimate outcome

There is a fixed sequence for working through a symbol problem, and it is worth committing to memory rather than improvising, because the day you need it you will be under time pressure.

1 run windows.info, or the equivalent, and confirm the silence 2 run banners and read the exact string the image carries 3 run isfinfo and see whether you already hold it 4 find or generate a table for that exact build 5 record that the image cannot be analyzed with this tool
  1. Confirm the silence first. Everything else is wasted if the real problem is a wrong path or a file that is not an image at all. This costs seconds and rules out a whole category.
  2. Then the banner. It comes out of the image, it is the exact string any matching table must carry, and without it every later step is guesswork about what you are even looking for.
  3. Then what you have. The answer is sometimes that the table is present and something else is wrong, which is worth discovering before you go hunting for a download.
  4. Then obtain one. Published packs first, then community collections for Linux organized by kernel version, then generation from the matching debug package.
  5. Then stop, and write it down. If the debug package no longer exists anywhere, this image cannot be analyzed with this tool, and that is a limit to record rather than a failure to hide.

Two things about that sequence are worth noticing. It runs cheapest first, so the steps that cost seconds come before the ones that cost an afternoon. And each step produces something usable even when it does not solve the problem: the banner is worth recording whether or not you find a match, and the isfinfo comparison is worth having whether or not it is the answer.

Step five is the one this sub exists to make thinkable, because analysts find it very hard to write. Reaching it does not mean you did anything wrong. It means the analyzability of that image depended on somebody keeping a debugging package for a kernel nobody runs any more, and that decision was made by people who were not thinking about forensics at all.

Writing it down is far more useful than a quiet absence of findings. A report saying the image could not be analyzed, naming the banner and what was needed, tells the next person exactly what to obtain and tells the organization something actionable about its own estate.

There is one more thing worth doing at step five, which is to check whether another image of the same build exists somewhere in your own collection. Symbol tables are per build rather than per machine, so a table obtained for one server works for every server running that exact kernel, and organizations frequently have dozens of machines on one build.

It also has a consequence for acquisition that belongs in a procedure rather than in an analyst's head. If symbols may become unobtainable later, capturing the information that identifies the build at the time of collection costs nothing and preserves the option. That is the same class of decision as recording which machine an image came from.

05

Why a Near Match Is Refused

The alternative is confident nonsense

The obvious complaint about all of this is that the tool should be more forgiving. If it has symbols for a kernel one patch level away, why not use them and warn the analyst?

The answer is what those symbols would produce. A symbol table is a map from field names to positions within a structure, so using the wrong map does not fail; it reads bytes from the wrong places and reports them as values. The output has the right shape, the right columns and the right number of rows, and its contents are meaningless.

A tool reads process structures with symbols from a nearby build. What does the listing look like?predict, then reveal
the fields are read from slightly wrong offsets ...
PID    PPID   ImageFileName   CreateTime
4      0      Syst\x00\xf3..  1601-01-01 00:00:00 UTC
19216  61455  (blank)         2148-09-02 11:04:33 UTC
61456  28293  ????.exe        (blank)
Not an error. A listing that looks exactly like the ones in the last six subs, with names read from the middle of another field, times decoded from bytes that were never a time, and identifiers no operating system would issue. Every row is wrong and nothing announces it.

Now consider the position that puts you in. You would be reading plausible-shaped output with no way to tell it apart from a correct reading, and any finding built on it would be fabricated. Worse, a partly-wrong table might read some fields correctly and others not, producing a listing that is mostly sensible with a few wrong values scattered through it, which is far harder to detect than total nonsense.

Refusing to guess is therefore a design decision in your favor, and it is worth recognizing as one rather than resenting it. A tool that offered an override would be offering to make its own output untrustworthy on request, and the analyst who used it would have no way to know which rows to believe.

It is also worth understanding that the refusal is not a conservative default somebody could relax with more engineering effort. The framework matches on the banner because the banner is the only thing that reliably identifies a build, and there is deliberately no mechanism for declaring two banners equivalent. That is a considered position rather than an unfinished feature.

The same reasoning is why an internal-consistency check on the output is worth running even when symbols matched. A creation time before the machine booted, a parent identifier larger than any the system issues, a name with unprintable characters in it: those are the signature of a reading problem, and they are the only warning you get in the rare cases where something partly works.

06

Failure Modes That Are Not Symbols

Three other reasons for an empty listing

Not every empty result is a symbol problem, and knowing the alternatives stops you chasing the wrong fix.

A plugin can be pointed at a process that does not exist. Filtering by a PID that is not in the image returns an empty listing rather than an error, and on an image where identifiers have been reused it can return the wrong process entirely, which the section above covered. Check the process list before concluding anything about a filtered run.

A plugin can also be the wrong one for the operating system. Running a Windows plugin against a Linux image is a mistake people make more often than they admit, particularly when working from a cheat sheet, and the result is unhelpful rather than obvious. The plugin prefix is the guard against this and it is worth reading twice.

an empty listing info also empty symbols info works a result, or a bad filter wrong prefix, or the plugin moved STOP nothing downstream is evidence carry on fix the command, or accept the result

Only the left branch invalidates everything after it. One command tells you which branch you are on.

And a plugin can have moved, which MF0.3 met directly. A command copied from an older write-up can name a plugin that no longer exists at that path, and while that produces an error rather than silence, it is the same family of problem: the tooling has changed and the documentation has not.

There is a fourth case worth adding because it is genuinely a result rather than a problem. A plugin can return nothing because there is nothing to return: a process with no unbacked executable memory produces no malfind rows, and a machine with no established connections produces no established rows. That is an answer, and the way you know it is an answer rather than a failure is that other plugins on the same image are producing output normally.

One plugin is empty and the others work normally. What does that tell you?pick one, then the tell

The general rule is worth stating once. An empty result is a statement about the tool until you have established otherwise, and the cost of establishing otherwise is one command. Analysts who adopt that habit early never write the sentence about no suspicious processes being found on an image that was never read.

07

What This Means for Your Estate

A retention question nobody owns

This sub has a consequence that reaches well outside the analysis itself, and it is the kind of thing worth acting on quietly before an incident rather than discovering during one.

Whether an image from your estate will be analyzable in two years depends on whether the information needed to build a symbol table still exists then. For Windows machines that is somebody else's archive and it is likely to be there. For Linux machines it depends on your distribution's retention of debug packages for kernels you no longer run, which is nobody's decision in particular and is usually nobody's job to track.

The mitigation is genuinely cheap and it is a procedure change rather than a technical project. Record the kernel build identifier when a machine is decommissioned, or better, keep the matching debug package alongside whatever else is retained. Either one turns a future dead end into a lookup that takes a minute.

None of this requires a project. It requires one person to ask, once, and write the answer down somewhere the incident response procedure can find it.

There is a second and more immediate version of the same problem. An analysis environment deliberately isolated from the internet, which is a sound control, cannot download a symbol pack on the night of an incident. The packs have to be obtained in advance and carried across, and that is a task with a lead time that somebody has to have done.

Which of your machines is most likely to be unanalyzable in two years?pick one, then the tell

Both of those are the kind of thing that gets discovered at three in the morning by somebody who cannot fix it then. Asking the question now costs an email. The answer is either reassuring, in which case you have lost ten minutes, or it identifies a gap that is cheap to close today and impossible to close on the night it matters. That asymmetry is why it is worth raising even when you are fairly sure of the answer.

08

Practice

Make the failure happen on purpose
hands on
Do this See the silence before it costs you an afternoon
  1. Find the symbols directory in your Volatility installation and look at what is in it. Seeing the files makes the dependency concrete in a way no explanation does.
  2. Rename that directory, run any plugin against an image that worked a minute ago, and watch what happens. Do this deliberately, once, so the shape of the failure is familiar rather than alarming.
  3. Run banners against every image you have and record the strings. Two machines you would describe as the same version may well give different answers.
  4. Run isfinfo and compare its list against those banners. Where they match you can work; where they do not you cannot, and knowing that in advance is worth having.
  5. Write the two sentences you would put in a report for an unanalyzable image: what was attempted, and what was needed. Having them drafted turns an awkward conversation into a short one.
What you should end up with: first-hand experience of what a missing-symbols failure looks like, a record of the banner for each image you hold, a clear view of which of them you can actually analyze, and two sentences ready for the image you cannot. Step two is the one that matters.

Extend it

These turn a one-off understanding into something your organization benefits from.

  1. Find out whether your analysis environment can reach the internet, and if not, who would obtain a symbol pack and how long it would take. The answer is often that nobody has considered it.
  2. For one Linux system in your estate, check whether the debug package for its current kernel is still available from the distribution. Then check one you decommissioned last year.
  3. Count the symbol files shipped for Windows in your installation. The number is a good measure of how often the underlying structures change.
  4. Take an image, deliberately analyze it correctly, then write down three values you would check for internal consistency if you suspected a symbol mismatch. Having the check ready is the point.

Drill one is the one most likely to surprise somebody other than you. An analysis environment that cannot reach the internet is a good control and a serious constraint, and the fact that nobody has worked out who would carry a symbol pack across is usually news to whoever owns that environment.

Drill two is the one with a finding attached. Most people are surprised by how quickly debug packages disappear, and discovering that a machine decommissioned last year is already unanalyzable is a concrete argument for a retention change that a general warning never wins.

You can tell a symbol failure from a clean result, run banners to read what the image needs and isfinfo to see what you hold, explain why a near match is refused rather than warned about, work through the five steps in order, and recognize the three common empty results that are not symbol problems at all. MF0.7 builds the environment this course runs on, and the tools that capture an image in the first place.