Open-source project
michaelweber/Macrome avatar
michaelweber/Macrome

Macrome builds and unmasks obfuscated Excel 97-2003 macro documents

Excel Macro Document Reader/Writer for Red Teamers & Analysts

523 stars77 forksC#MIT

At a glance

What is it?
Macrome is a C# command line tool that writes Excel 97-2003 workbooks whose macro sheets carry an obfuscated payload, and reads such workbooks back through dump and deobfuscation modes. It still runs on dotnet 5.0, and every tagged release predates 2022.
Who is it for?
Macrome earns a place in a malware analysis workflow and in the hands of a red team that needs to reproduce a hostile workbook quickly, since the dump and deobfuscation modes work against documents you did not create. It is the wrong choice for macro-free formats, for embedding a .NET assembly in one step, or for any production pipeline, given that the newest tagged release is v0.5.1 from 2021 and the last push was on 2026-05-19.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 157 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Macrome targets the BIFF8 XLS era, which is the whole point

Macrome is a C# tool for reading and writing Excel documents that carry VBA macro sheets, aimed at red teamers who need to produce a hostile workbook and at analysts who need to take one apart. The file format constraint is not incidental. The documentation asks you to save the decoy sheet as type `Excel 97-2003 Workbook (*.xls)` rather than the newer `Excel Workbook (*.xlsx)` format, and the project's own blog links sit under a heading about BIFF8 XLS. That older binary workbook format stores records in a stream the tool can rewrite record by record, which is why the repository contains BiffRecordExtensions.cs, RecordHelper.cs, WorkbookStream.cs and WorkbookEditor.cs. The xlsx format, being a zipped package of XML parts, does not offer the same record-level surface, and nothing in the documentation suggests Macrome handles it. Anyone arriving with modern .xlsx samples will find the tool refuses the premise of the task.

Build, dump and deobfuscation are three separate modes

The command line exposes three modes of operation: build, dump and deobfuscation. Build mode generates an Excel document containing an obfuscated macro sheet, using a decoy document you supply and a payload you supply. The other two modes read a document back, which is the half of the tool that matters for an analyst facing a sample of unknown origin. Full usage text comes from the help flag on the build command, `dotnet Macrome.dll build -h`, and the deobfuscation mode takes a path argument. The split mirrors the two very different jobs here. Producing a workbook requires control over a decoy, a payload, an encoding method, a preamble and an output name. Reading one requires only a file path, since everything the tool needs to reconstruct the payload has to be recoverable from the document itself. That recoverability is a design constraint of the format, not an extra feature, and it is why the same file can be dissected by someone who never had access to the build environment.

Building a document from the command line

The project is built from source. The README shows two routes, one running directly from the solution directory and one building first and then invoking the produced DLL:

code
dotnet run -- build --decoy-document Docs\decoy_document.xls --payload Docs\popcalc.bin 
code
dotnet build
cd bin/Debug/netcoreapp2.0
dotnet Macrome.dll deobfuscate --path obfuscated_document.xls

Note that a 5.0+ build of dotnet is required for this to work as configured, and the download page for that runtime is linked from the README. A fuller invocation shows how many knobs exist at once:

code
Macrome build --decoy-document decoy_document.xls --payload beacon.bin --payload64-bit beacon64.bin --payload-method Base64 --method ArgumentSubroutines --password VelvetSweatshop --preamble preamble.txt --output-file-name ReadyToPhish.xls

The payload flag takes a binary blob, and the documentation describes the bundled examples as calc-popping payloads generated with msfvenom. Binary releases that do not require dotnet are published in the Release section for Windows, OSX and Linux. One constraint catches people out: a 64-bit payload still requires an x86 payload to be supplied, and if that is not an issue you can pass garbage for the x86 flag.

Encoding choices decide how readable the macro sheet looks

The macro sheet can be written in three ways, and the choice determines what a reviewer sees when they open the workbook instead of parsing it. CharSubroutine replaces repeated CHAR() calls with a subroutine placed at a random cell, invoked through a long chain of IF and SET.NAME functions. ObfuscatedCharFunc is the original approach: it invokes CHAR() but appends the result to a random empty cell and wraps the value in a ROUND function. ObfuscatedCharFuncAlt is described as a variation on the original encoding, and the documentation text for it is cut off mid-sentence, so the exact difference is not visible here. Separately, the payload encoding changed at version 0.5.0, where every payload became base64 encoded, which allows the payload to carry any byte sequence including null bytes. The legacy path still exists through the `--payload-method` flag with SheetPackingMethod, and there is a real trade-off in it: an alpha-numeric payload produces a smaller macro file because letters and numbers are cheaper to express in macro form than repeated CHAR function invocations.

Macro payloads use a semicolon grid and start at A1

Not every payload has to be shellcode. Macrome also accepts a plain text file of VBA macros, and the format it expects is unusual enough to trip up first-time users. Macros should have columns separated by `;` characters and rows separated by newlines, and the content is written and executed beginning at A1. The documentation notes that support for choosing a different start location is planned rather than present. Sample files live in the repository at /Docs/macro_example.txt and /Docs/multi_column_macro_example.txt, so a working macro can be copied rather than written from scratch. The invocation is the build command with the payload type set explicitly:

code
dotnet Macrome.dll build --decoy-document decoy_document.xls --payload macro-example.txt --payload-type Macro

This path is the one that makes Macrome more than a payload dropper, since it lets an analyst build a workbook with a known macro body, then compare the produced document against a sample to see how the encoding layer alters the original text.

Embedding a .NET assembly is not a one step operation here

Direct embedding of .NET assemblies is described as eventual support rather than a current feature, and the documentation points elsewhere for that job, recommending EXCELntDonut for generating a macro and, for embedding executables, Donut with the command `donut.exe -a 3 -b 1 -z 1 executableToEmbed.exe`, followed by embedding the result as both the 32-bit and 64-bit payload. Amber is offered as another route for carrying larger Go binaries. This is the clearest example of a boundary in the tool: Macrome is good at carrying opaque bytes into a macro sheet and at reading them back out, and it deliberately leaves the job of turning an executable into a payload to other projects. A related limitation lives in the decoy rather than the payload, since the documentation states that if you use XOR obfuscation to protect your document you currently cannot add an image to your decoy.

A dotnet 5.0 dependency and 2021 releases define the upgrade cost

Two facts about upkeep are visible. The latest tagged release is v0.5.1 from 2021-10-04, preceded by v0.5.0 in July 2021 and v0.4.1 in March 2021, so the release history stops more than four years before the last push on 2026-05-19, and the repository is not archived. In other words, work continues on the source while nothing has been packaged as a numbered version in a long time, which means there is no modern binary to point at and no release notes to diff against. The dependency story is equally dated, since the configured runtime is dotnet 5.0, which is out of mainstream support, and the packages.config file plus the App.config and Macrome.csproj at the repository root point to a solution shaped around that era of .NET. Licence terms are simple, with an MIT LICENSE file at the root and no stated restrictions on use, so the legal side offers no barrier even though the runtime side does.

Editorial conclusion

Macrome earns a place in a malware analysis workflow and in the hands of a red team that needs to reproduce a hostile workbook quickly, since the dump and deobfuscation modes work against documents you did not create. It is the wrong choice for macro-free formats, for embedding a .NET assembly in one step, or for any production pipeline, given that the newest tagged release is v0.5.1 from 2021 and the last push was on 2026-05-19. Before adopting it, build the repository against dotnet 5.0 or newer and confirm that its output still opens cleanly in the Excel version your targets run.

Frequently asked questions

What is Macrome and who is it for?

Macrome is an Excel macro document reader and writer described as a tool for red teamers and analysts. It builds Excel 97-2003 workbooks whose macro sheet carries an obfuscated payload, and it reads such documents back through dump and deobfuscation modes.

How do I build and run Macrome?

Clone or download the repository and use dotnet, either with `dotnet run -- build` from the solution directory or by running `dotnet build` and invoking the resulting Macrome.dll. A 5.0 or newer build of dotnet is required for it to work as configured, and binary releases for Windows, OSX and Linux are in the Release section.

Which Excel file formats does Macrome support?

Macrome works with the older Excel 97-2003 Workbook (*.xls) format, and the documentation asks you to save the decoy sheet in that format rather than Excel Workbook (*.xlsx). The repository is built around BIFF8 record handling, so the modern xlsx package format is out of scope.

How does Macrome encode the payload in the macro sheet?

Since version 0.5.0 every payload is base64 encoded so it can carry any byte sequence including null bytes, and the macro sheet can use CharSubroutine, ObfuscatedCharFunc or ObfuscatedCharFuncAlt to express the characters. The legacy encoding is still reachable through the `--payload-method` flag with SheetPackingMethod.

Can Macrome embed a .NET assembly directly in the document?

Not yet, as direct .NET assembly embedding is described as eventual support. The documentation points to EXCELntDonut for creating a macro and to Donut, run as `donut.exe -a 3 -b 1 -z 1 executableToEmbed.exe`, for producing a payload to embed as both the 32-bit and 64-bit payloads.

Official sources

  1. Issues
  2. License: MIT
  3. michaelweber/Macrome on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/michaelweber-macrome.svg)](https://hysenlabs.com/projects/michaelweber-macrome)

Was this useful?