Home / Alt manpages / monodis(1)

  • monodis(1)
  • User command
  • linux

Inspect .NET assemblies safely with monodis

You will finish with a small, repeatable workflow for reading a managed .NET assembly: identify the installed tool, inspect its identity and metadata, browse its CIL, and extract selected heaps or resources into a disposable directory. The original assembly remains untouched unless you deliberately redirect output or extract files.

Allow about fifteen minutes. You need the mono-utils package and a readable .dll or .exe containing ECMA CIL. The commands below were checked with Mono package version 6.8.0.105+dfsg-3.6ubuntu2. Some output is assembly-specific, so treat the shown lines as shape and checkpoints rather than fixed data.

1. Confirm the installed command

Start with read-only checks. No elevated privileges are needed:

$ command -v monodis
/usr/bin/monodis
$ dpkg-query -W -f='${Package} ${Version}\n' mono-utils
mono-utils 6.8.0.105+dfsg-3.6ubuntu2
$ monodis --help
Mono Common Intermediate Language Disassembler
Usage is: monodis [--output=filename] [--filter=filename]
...

The installed help lists a few switches that are newer or more specific than the older local manpage, including --filter and pointer-table switches. Prefer the output of monodis --help on the host where you are working when the two descriptions differ. The alias cli-ildasm refers to the same disassembler documentation; use monodis in scripts and notes so the command name is unambiguous.

Checkpoint

You have confirmed the binary and package version, and you know which local help text defines the available switches.

2. Choose a readable assembly

Pass an explicit file path. A system assembly is convenient for a first test:

$ test -r /usr/lib/mono/4.5/mscorlib.dll
$ printf 'assembly: %s\n' /usr/lib/mono/4.5/mscorlib.dll
assembly: /usr/lib/mono/4.5/mscorlib.dll

Replace that path with ASSEMBLY in later commands when examining your own file. Do not run an unknown binary to understand it: monodis reads the assembly, while mono executes it. Keep analysis copies separate from build or deployment directories, and preserve the original if it may be evidence.

3. Read identity and references

Metadata-table switches select a focused view instead of the complete disassembly. Start with the Assembly table:

$ monodis --assembly /usr/lib/mono/4.5/mscorlib.dll
Assembly Table
Name:          mscorlib
Hash Algoritm: 0x00008004
Version:       4.0.0.0
Flags:         0x00000001
...

This tells you the assembly identity and version recorded inside the file. It is not the same as the Debian package version. To inspect dependencies, use the AssemblyRef table:

$ monodis --assemblyref /usr/lib/mono/4.5/Mono.Posix.dll
AssemblyRef Table
1: Version=4.0.0.0
        Name=mscorlib
...

The exact rows vary by release. A missing file, an unreadable file or an invalid PE/COFF image produces an error and a non-zero status. Check that status instead of treating partial output as a successful inspection:

$ monodis --assembly ASSEMBLY >/tmp/ASSEMBLY-assembly.txt
$ status=$?
$ printf 'monodis status: %s\n' "$status"
monodis status: 0

4. Browse types and CIL

Use --typedef for the TypeDef table, which is often the quickest way to find namespaces and classes:

$ monodis --typedef /usr/lib/mono/4.5/mscorlib.dll | sed -n '1,12p'
Typedef Table
1: (null) (flist=1, mlist=1, flags=0x0, extends=0x0)
2: Internal.IO.File (...)
3: Interop (...)
...

For the complete readable CIL form, omit metadata switches:

$ monodis /usr/lib/mono/4.5/Mono.Posix.dll | sed -n '1,24p'
.assembly extern mscorlib
{
  .ver 4:0:0:0
  ...
}
.assembly 'Mono.Posix'
{

Use a pipe such as sed only for viewing a sample. The command still processes the whole assembly, and the first lines may not include the method or type you need. For a stable record, redirect to a file in a directory you own, then search it with rg. Avoid assuming that CIL is source code: compiler-generated names, attributes and metadata tokens may not map neatly back to the original project.

Checkpoint

You can distinguish an identity query, a metadata-table query and the full CIL dump. If you only need one table, use its switch rather than creating an enormous transcript.

5. Inspect strings and tokens

The --strings and --userstrings switches dump different heaps. The first is the metadata Strings heap; the second is the User-Strings heap used for literal strings in managed code:

$ monodis --strings /usr/lib/mono/4.5/Mono.Posix.dll | sed -n '1,12p'
Strings heap contents
00: ""
01: "<Module>"
0a: "Consts"
11: "Locale"
...

Use --show-tokens with normal disassembly when you need metadata tokens alongside strings, types, methods and fields. Use --show-method-tokens when method tokens are the specific thing you are comparing. Tokens are identifiers inside that assembly, not portable line numbers or promises that another build will use the same values.

6. Save output and handle resources carefully

--output=FILENAME writes the disassembly to a named file and also dumps embedded managed resources. This changes the filesystem, so use a new temporary directory and check its contents first:

$ workdir=$(mktemp -d /tmp/monodis.XXXXXX)
$ monodis --output="$workdir/assembly.il" /usr/lib/mono/4.5/Mono.Posix.dll
$ printf 'files created:\n'
$ find "$workdir" -maxdepth 1 -type f -printf '%f\n' | sort
files created:
assembly.il
$ sed -n '1,12p' "$workdir/assembly.il"
.assembly extern mscorlib
{

Do not point --output at an existing source, build or deployment file. It can overwrite a path you own, and extraction may create additional files. The manpage also describes --mresources, which saves all managed resources in the current directory, and --manifest, which lists manifest resources first. Treat --mresources as an extraction operation: run it only in a disposable directory after checking the manifest.

When you are finished with the temporary inspection directory, remove only that exact directory:

$ rm -rf -- "$workdir"
$ test ! -e "$workdir" && echo 'temporary directory removed'
temporary directory removed

This cleanup is the only destructive command in the guide. Verify the variable if you adapt it, and never substitute a broad path such as your home directory or a project checkout.

7. Know the boundaries

monodis is an inspection and disassembly tool. It does not prove that an assembly is safe, identify every runtime dependency, or recover the original source exactly. A successful dump also does not mean that the assembly can be rebuilt byte-for-byte. The manpage describes round-tripping with ilasm; if you attempt that, work on a copy and review the generated IL and resources before replacing anything. Do not execute the result merely because disassembly succeeded.

Most commands here are ordinary user operations. You need elevated privileges only when your account cannot read the chosen assembly or directory. Prefer granting narrowly scoped read access or copying the file into a controlled analysis directory; avoid running the whole inspection workflow as root.

Done means

  • You confirmed the installed monodis binary and mono-utils version.
  • You inspected assembly identity, references, types, CIL or string heaps with the relevant focused switch.
  • You checked command status and treated output as assembly-specific data.
  • You kept the source assembly untouched and used a disposable directory for generated output.
  • You understand that disassembly is analysis, not execution, source recovery or a safety verdict.