Agentic Reverse Engineering: Build a free, local AI agent to support malware analysis.
Claude Code x Ollama x Ghidra MCP go brrr

Introduction
In this post I will demonstrate how to implement a completely localized instance of the AI agent Claude Code and LLM model to help with malware analysis projects without burning money on API costs. This will include the setup of the GhidraMCP built by LaurieWired to allow the AI agent to interact with samples and (hopefully) speed-up analysis.
This post also contains a small comparison between analysis from the local LLM against a paid model.
The goal here was to add a new tool to my RE toolkit, without having to pay for API/usage costs - I'd say we achieved that goal with this setup, assuming you have the hardware at hand :p.
Install and setup:
This setup will use the shared network used by a 'host-only' adapter used by VMs to communicate with the physical host. This will allow us to separate the LLM and run it on our more powerful hardware (the physical host), while not exposing our malware environment to the internet (bad). This also allows us to repeatedly use Claude during static analysis with snapshots without needing to frequently update network configuration, resulting in a lower chance of compromise via human error.
The setup should look something like this:
Claude Code:
Go to Claude Code by Anthropic | AI Coding Agent, Terminal, IDE.
Follow install instructions.
Then open up the system properties and add the following to the path environment variable:
- PATH:
C:\Users\Cwrw-Mal\.local\bin
- PATH:
Then add the following new environment variables:
ANTHROPIC_AUTH_TOKEN= ollamaANTHROPIC_API_KEY=ANTHROPIC_BASE_URL= http://PATH.TO.WHERE/YOUAREHOSTINGYOURLLM:11434
On first execution of Claude your need to pass these variables directly into the command line, filling in the IP and Model you are using:
\(env:ANTHROPIC_AUTH_TOKEN = "ollama"; \)env:ANTHROPIC_API_KEY = ""; $ANTHROPIC_BASE_URL = "http://192.168.255.1:11434"; claude.exe --model qwen3-coder:30b
This will let you proceed without connecting to an Anthropic account Go through the setup, dark mode and project directory - consider setting up a dedicated Claude folder to cage the agent.
Ollama:
On your physical host go to Download Ollama on Windows.
Follow install instructions.
Run
$env:OLLAMA_CONTEXT_LENGTH=64000; ollama serve
This environment variable needs to be set to give the agent enough of a context window (like AI memory) to not forget what its doing mid-job.
Ghidra MCP:
Claude side:
Initially I had set this up and completed the below testing with the main project from Laurie Kirk Release GhidraMCP 1.4 · LaurieWired/GhidraMCP · GitHub However, when reinstalling this for this blog I found a fork with more functionality and easy setup by Ben Ethington - I'll update this blog if there's any complements or complaints.
- The setup was much the same, however the fork requires a few extra steps - read the readme to get it installed.
Unzip to directory of choice.
Create a
.mcp.jsonfile in the root of the Claude project directory and fill out with the following content:
{
"mcpServers": {
"ghidra": {
"command": "python",
"args": [
"/ABSOLUTE_PATH_TO/bridge_mcp_ghidra.py",
"--ghidra-server",
"http://127.0.0.1:8080/"
]
}
}
}
[!Info] For the fork, take the
mcp-config.jsonfile and rename it as.mcp.jsonin the root of the project - it should be plug and play.
Reload Claude code and you should get an MCP prompt, chose whether to load this single MCP tool or always load in project.
Then use
/mcpto view the status of the tool to confirm its running.
Ghidra side: Original:
Run Ghidra
Select
File->Install ExtensionsClick the
+buttonSelect the
GhidraMCP-1-2.zip(or your chosen version) from the downloaded releaseRestart Ghidra
Make sure the GhidraMCPPlugin is enabled in
File->Configure->DeveloperOptional: Configure the port in Ghidra with
Edit->Tool Options->GhidraMCP HTTP Server
Forked:
.\ghidra-mcp-setup.ps1 -Deploy -GhidraPath "C:\PATH\TO\ghidra_12.0.2_PUBLIC"
Model Comparison
I thought that it may be helpful to do a comparison between the locally hosted model and a paid native model in the Claude eco-system. In order to do this comparison I used Claude to assess a particular function in an open Ghidra project, the sample I used was WannaHusky from the PMAT course by Husky Hacks. The below outlines the prompts used and provides a high-level breakdown of the responses I received as a result.
Prompt 1 - "hey clanker, using Ghidra, what is the purpose of the function wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w?"
Local LLM (qwen3-coder:30b)
Took 11m 22s to complete analysis.
Used more of the Ghidra MCP tools.
Prompted for each use with the option to 'always allow' - enforcing HiTL.
There was more 'fluff' explanation surrounding the actions taken and rationale - most of which pretty helpful and clear.
● I'll help you analyze the purpose of that function in Ghidra. Let me use
the appropriate Ghidra tools to examine this function.
First, I'll check if this function exists in the program and get
information about it.
● ghidra - get_function_by_address (MCP)(address: "0x4015f0")
⎿ {
"result": "No function found at address 0x4015f0"
}
● It seems the function name you provided might be a bit different. Let me
search for functions with similar names to find the one you're referring
to.
● ghidra - search_functions_by_name (MCP)(query: "wannaHusky")
⎿ wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w @ 0040d9c7
● Now I've found the function! Let me decompile it to understand its
purpose.
● ghidra - decompile_function (MCP)(name:"wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w")
⎿ {
"result": "/* __fastcall wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w,0 *
/\r\n\r\nvoid __fastcall wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w(void)
… +87 lines (ctrl+o to expand)
● Based on the decompiled code, I can see that the function
wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w appears to be a cryptographic or
security-related function. Let me analyze its main operations:
This function performs the following key operations:
1. Exception handling setup - It sets up a structured exception handler
using __setjmp3 and local_4ac buffer.
2. File operations - It reads from a file using
readFile__4PGnM9bWmsH0Nu7dnr3XzgA and processes the data.
3. String manipulation - It works with strings using functions like
rawNewString, appendString, copyString.
4. Cryptographic operations - It performs cryptographic operations
including:
- Hashing with digest__CXo4xdrVR0UXF9aOcb9aJFYg
- Encryption with encrypt__dcoBdmUaaCC9cnR23eFxSLAbcmode
- Encoding with encode__npLRSgmGJDNX8bfurW5iRw
5. File writing - It writes data to files using
writeFile__D6Pj9c29aCLEJP9beOWa08HYA and
newFileStream__cwYJiP3D7DOTCJxCdBqBZQ.
6. Memory cleanup - It uses burnMem__4FZHyz34TGxTmMy6XY9cOSg to clear
sensitive data from memory.
The function appears to be a cryptographic routine that:
- Reads input data from a file
- Processes it through cryptographic operations (hashing, encryption)
- Writes the results to output files
- Cleans up sensitive memory
This looks like a security or encryption utility function that handles
file-based cryptographic operations, likely part of a larger security
system or application that processes sensitive data through cryptographic
means.
Let me also check what the function is referencing to understand its
context better.
● ghidra - get_function_xrefs (MCP)(name:"wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w")
⎿ From 0040e08e in NimMainModule [UNCONDITIONAL_CALL]
● Let me also look at the function that calls this one to better understand
its context.
● ghidra - decompile_function_by_address (MCP)(address: "0x40e08e")
⎿ {
"result": "\n/* __fastcall NimMainModule,0 */\n\nvoid __fastcall
NimMainModule(void)\n\n{\n nimRegisterGlobalMarker(&_TM__njFKfyR
… +6 lines (ctrl+o to expand)
● Based on my analysis of the function wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w
in Ghidra, I can determine that this function serves as a cryptographic or
security utility within a Nim application. Here's what it does:
Purpose of the Function
This function appears to be a cryptographic routine that:
1. Reads data from a file using readFile__4PGnM9bWmsH0Nu7dnr3XzgA
2. Processes the data through cryptographic operations including:
- Hashing with digest__CXo4xdrVR0UXF9aOcb9aJFYg
- Encryption with encrypt__dcoBdmUaaCC9cnR23eFxSLAbcmode
- Encoding with encode__npLRSgmGJDNX8bfurW5iRw
3. Writes the processed data to output files using
writeFile__D6Pj9c29aCLEJP9beOWa08HYA
4. Handles sensitive data by clearing memory with
burnMem__4FZHyz34TGxTmMy6XY9cOSg
5. Manages exception handling for robust operation
Context
The function is called from NimMainModule, which suggests it's part of the
main application flow in a Nim program. The function name "wannaHusky"
combined with the cryptographic operations suggests it's likely part of a
security or encryption system that processes sensitive data.
Key Operations
- File I/O: Reads from and writes to files in the home directory
- Cryptography: Performs hashing, encryption, and encoding operations
- Memory Management: Properly cleans up sensitive data from memory
- Error Handling: Uses structured exception handling for robust operation
This function is likely part of a security system that processes sensitive
information through cryptographic means, possibly for authentication,
data protection, or secure communication purposes.
✻ Worked for 11m 22s
Claude Pro Subscription:
Much faster, ~1min 30 seconds to completion.
Did not prompt for MCP tool usage (HiTL).
This prompt used ~6% of my 4 hour token allowance.
Added additional context such as wordplay of WannaCry / WannaHusky.
● I'll decompile that function to analyze its purpose.
● ghidra - decompile_function (MCP)(name: "wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w")
⎿ {
"result": "/* __fastcall wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w,0 *
/\r\n\r\nvoid __fastcall wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w(void)
… +87 lines (ctrl+o to expand) ● Based on the decompiled code, this function appears to be a file
encryption routine, likely part of ransomware or a CTF challenge (given
the "wannaHusky" name which is a play on "WannaCry").
Here's what it does:
1. Sets up exception handling using setjmp/longjmp mechanism
2. Constructs file paths relative to _homeDir__pJnrRb9aEgb0S3dMArlimMA
(home directory)
3. Reads a file into memory using readFile__4PGnM9bWmsH0Nu7dnr3XzgA
4. Performs encryption:
- Creates a digest/hash using digest__CXo4xdrVR0UXF9aOcb9aJFYg
- Initializes encryption contexts with init__QeKCvRTxwnkv4EgDHKgXYA
(likely AES with key and IV)
- Encrypts the file data using encrypt__dcoBdmUaaCC9cnR23eFxSLAbcmode
- Securely wipes cryptographic buffers from memory using
burnMem__4FZHyz34TGxTmMy6XY9cOSg
5. Encodes the encrypted data (likely base64) using
encode__npLRSgmGJDNX8bfurW5iRw
6. Writes the encrypted content to a new file in the home directory
7. Creates a log/ransom note by writing to a file stream
8. Deletes the original file using nosremoveFile
Purpose: This is a file encryption function that encrypts files, writes
them to a new location, and deletes the originals - classic ransomware
behavior. The name "wannaHusky" is likely a parody of "WannaCry".
Prompt 2: "Using Ghidra, verbosely analyze this function as well as any sub-functions within. For any native API calls that are made, please just calculate the values passed rather than assess the underlying code."
Local LLM (qwen3-coder:30b):
Analysis took 27 mins 2 seconds.
Analysis was more verbose in some areas such as listing output of the MCP tools.
Again, the use of the MCP tools were prompted for each use with the option to 'always allow' - enforcing HiTL.
● I'll analyze this function more thoroughly and examine its sub-functions. Let me start by getting more detailed information about the function and
then look at its cross-references to understand what sub-functions it
calls.
● ghidra - get_xrefs_from (MCP)(address: "0x40d9c7")
● Let me check what functions are called from this function by examining the decompiled code more carefully. I'll also look at some of the key
sub-functions that are called.
● ghidra - list_functions (MCP)
⎿ __mingw_invalidParameterHandler at 00401000
⎿ pre_c_init at 00401010
⎿ pre_cpp_init at 00401120
⎿ __tmainCRTStartup at 00401160
⎿ WinMainCRTStartup at 004014a0
⎿ mainCRTStartup at 004014c0
⎿ atexit at 004014e0
⎿ ___gcc_register_frame at 00401500
⎿ ___gcc_deregister_frame at 00401520
⎿ raiseAssert__gpGJG5CoQzE64skFd9bPG7A at 00401530
⎿ failedAssertImpl__W9cjVocn1tjhW7p7xohJj6A at 004015b4
⎿ nimAddInt at 004015c4
⎿ nimSubInt at 004015dd
⎿ newWideCString__K8rR70TBwrP6FNBzDhVVOA at 004015f6
⎿ dollar___NmQ9ct6KsQm3mcX4z6BEBbA at 00401a10
⎿ dollar___NleKxkskBzDhxdrvP1WL7A at 00401c56
⎿ newWideCString__G8rSmf9akUNUzuZWlrfFAmA at 00401c70
⎿ newWideCString__rFkWhJjmVi0m0Hc9agVEZIQ at 00401c96
⎿ stdlib_widestrsDatInit000 at 00401cb8
⎿ nimToCStringConv at 00401d10
⎿ popSafePoint at 00401d24
⎿ nimAddInt at 00401d31
⎿ nimSubInt at 00401d49
⎿ appendString.part.0 at 00401d62
⎿ appendString at 00401d83
⎿ asgnRef at 00401d8a
⎿ fopen__bn2yAzk2l19aVmr8QZTptyA at 00401dce
⎿ open__gq12VLhVO0NBzUTnGgz4nw at 00401dfa
⎿ rawFileSize__2ezUTSmBrmr5VkZNxReTYQ at 00401e90
⎿ raiseEIO__ZYk14k3sVNZUIjJjtqzFZQ at 00401ee6
⎿ checkErr__fU6ZlJAtQ9bre04EDZLdGsA at 00401f74
⎿ readBuffer__f3MIZh4IV2qRTxlOpckbRA at 00402018
⎿ endOfFile__FK5lmHTzEdX6epYBp9bfgMg at 00402057
⎿ readAllBuffer__mQf9blHFlTwuSvJjirhpY6g_2 at 00402083
⎿ readAllFile__W4tKfHpbz7kHhikLsBc0EA at 00402153
⎿ readAll__mQf9blHFlTwuSvJjirhpY6g at 0040229e
⎿ close__fU6ZlJAtQ9bre04EDZLdGsA_2 at 004022eb
⎿ readFile__4PGnM9bWmsH0Nu7dnr3XzgA at 00402300
⎿ writeWindows__JO9bn8JE1R6MjKZqdvhBrWg at 00402422
⎿ write__PArlm09bKklm2BLsCg6YtaA at 0040250b
⎿ writeFile__D6Pj9c29aCLEJP9beOWa08HYA at 00402520
⎿ setFilePos__FA7rp0y5drPpfGuceb3IJg at 0040263b
⎿ getFilePos__ihE3HyH1VBDfrPO1nZT7RQ at 0040266c
⎿ isatty__FK5lmHTzEdX6epYBp9bfgMg_2 at 0040269e
⎿ getOsFileHandle__hOwLbM7eXaZgEJrHWLeiNg_2 at 004026bd
⎿ writeBuffer__f3MIZh4IV2qRTxlOpckbRA_2 at 00402cfd
⎿ echoBinSafe at 00402d43
⎿ stdlib_ioInit000 at 00402df4
⎿ stdlib_ioDatInit000 at 00402e84
⎿ msbit__NR8XkKls428clCUr82szCgsystem at 00402f80
⎿ nimAddInt at 00402fae
⎿ nimSubInt at 00402fc6
⎿ resize__cOCDWr16YD4j3dBrbbH3yQsystem at 00402fe0
⎿ nimToCStringConv at 00402ffd
⎿ rawWrite at 00403011
⎿ nimZeroMem at 00403054
⎿ mappingInsert__SRLfEtcWb2dn0ht85HEwbQsystem at 00403065
⎿ appendString.part.0 at 004030a1
⎿ appendString at 004030c2
⎿ nimGC_setStackBottom at 004030cb
⎿ raiseOutOfMem__mMRdr4sgmnykA9aWeM9aDZlw at 004030eb
⎿ osAllocPages__HMOhWrY1QMa49a2BcJwSDZQsystem at 0040311f
⎿ llAlloc__ovw3NMWXeZ0Qi9cGYq1E2Tg at 00403154
⎿ addHeapLink__LIRFHBfc9aX3C5dmMmLnpwA at 004031bf
⎿ intSetGet__O3FRrWKKUdi8uRTGxiPdIg at 00403228
⎿ contains__9b5xR7VBZVwQDvk5Nr9bDKdQ at 0040323c
⎿ requestOsChunks__stlXHMKRKFIGOvq8t4ynRQ_2 at 00403272
⎿ intSetPut__Cw86Sj6YgVACdT20AkWjcA at 004033be
⎿ incl__tSnfTXv7GxXoDyFDm9bvzqg at 00403400
⎿ splitChunk2__gSNzk4aToVCSTE1opfEv2A at 00403436
⎿ addChunkToMatrix__YSJZJgeU5UU2aa8GNvs3WA at 004034b7
⎿ splitChunk__BqFVAuadgXfvAiq8B9cBjqQ at 00403517
⎿ removeChunkFromMatrix2__XFftAAJrARamxGOKUFQy9aw at 0040353a
⎿ getBigChunk__stlXHMKRKFIGOvq8t4ynRQ at 004035a3
⎿ getSmallChunk__0ixBBlKB5QN59bxrmztRmCw at 004036e2
⎿ getHugeChunk__stlXHMKRKFIGOvq8t4ynRQ_3 at 004036ec
⎿ getBottom__3mqnVBLDtYhZizqw9bvHELA at 0040373e
⎿ allocAvlNode__Du8pyfSfDLyN9aoS2IcBsHg at 0040375a
⎿ skew__NJo8pxZdXEAIa7wkHls9cOw at 004037ad
⎿ split__NJo8pxZdXEAIa7wkHls9cOw_2 at 004037cc
⎿ add__3D9aOyz4rDquPZKBlqn0xig at 004037f5
⎿ rawAlloc__mE4QEVyMvGRVliDWDngZCQ at 00403849
⎿ alloc__9aB7LvPFb7RSPI8u51kYo2Q_2 at 0040398c
⎿ alloc0__9aB7LvPFb7RSPI8u51kYo2Q at 004039a6
⎿ alloc0Impl__aMIFDISudztFhhVWVqortg at 004039c4
⎿ init__wKM37ZoL6WtPOU9bn6Ug18A at 004039d2
⎿ init__Y9c9cQhDWRSgYkHfKWcqFlsQ at 004039f8
⎿ allocImpl__aMIFDISudztFhhVWVqortg_2 at 00403a2a
⎿ removeChunkFromMatrix__YSJZJgeU5UU2aa8GNvs3WA_2 at 00403a3b
⎿ excl__9cAWqpgI1NbhhZ3cVPHhI5A at 00403ac2
⎿ freeBigChunk__IPvsryqksLyNxxag3IQr2g at 00403af0
⎿ del__Io5JDKCS5u26IEWw0J53hQ at 00403c0f
⎿ freeHugeChunk__IPvsryqksLyNxxag3IQr2g_2 at 00403cf2
⎿ rawDealloc__K7uQ6aTKvW6OnOV8EMoNNQ at 00403d73
⎿ dealloc__Jg1OaY9ahkT3MBopLAXRSGw at 00403e8a
⎿ deallocImpl__SAtZpVrJ3o5FJvSdLQe9auA at 00403e92
⎿ add__W9aRfhn7HvnQTPAb8ajo1uwsystem at 00403ea0
⎿ addZCT__Y66tOYFjgwJ0k4aLz4bc0Q at 00403f0a
⎿ decRef__AT1eRuflKWyTTBdLjEDZbgsystem at 00403f28
⎿ asgnRef at 00403f4d
⎿ cellSetRawInsert__a1sVKTgcDTTmcnBQqk9bNdA at 00403f72
⎿ cellSetEnlarge__9bhPFIGFYIneoHljx8OXvqA at 00403f9b
⎿ cellSetPut__6bBl0A4vUXoRvva9bRmnwSQ at 00404010
⎿ incl__azHo9bY5qs9b2EZ9cSse4fmZA at 0040409e
⎿ getDiscriminant__7LnhHf25BuMRNdnPtDbjcw at 004040cb
⎿ selectBranch__2us2RQByTh81i9aW4EEgfmw at 00404102
⎿ cellSetGet__ld9aj9akVqWcvwRCEMEk1MnQ at 00404123
⎿ containsOrIncl__qhy8GaXaPs9bLqr6V8CV9cFg at 0040414f
⎿ stackSize__VOU3z9bbtHMYBiCVB5tMX1g at 004041b9
⎿ stackSize__0yw8cp0rOgL8i0O5kzzb0g at 004041da
⎿ lowGauge__vu9a10GqvNeXA9alSqdG48cw at 004041ec
⎿ highGauge__vu9a10GqvNeXA9alSqdG48cw_2 at 004041fc
⎿ inRange__BIq3l3oBvrBeYSWFT5iXiw at 00404210
⎿ interiorAllocatedPtr__NuzKjA4SX9afyji9cHHIuKpQ at 00404232
⎿ gcMark__x5SbLN3uVBCsEa67N20nPwsystem at 004042c9
⎿ markStackAndRegisters__U6T7JWtDLrWhtmhXSoy9a6g at 004042f9
⎿ prepareDealloc__fvhnFro5wEfzy879alizcUQ at 004043a5
⎿ deinit__Y9c9cQhDWRSgYkHfKWcqFlsQ_3 at 004043c9
⎿ cellsetReset__Y9c9cQhDWRSgYkHfKWcqFlsQ_2 at 0040440e
⎿ contains__ClLkUQKF8KrRxQPdAJDd5w at 00404425
⎿ freeCyclicCell__SOJE9bROCOc8iabVsKM64Sg_2 at 0040447b
⎿ sweep__XHio9cMpnLoH7GyCj1Z9besg_5 at 0040449e
⎿ unmarkStackAndRegisters__XHio9cMpnLoH7GyCj1Z9besg_6 at 004045f7
⎿ isOnStack__plOlFsQAAvcYd3nF5LfWzw at 0040462d
⎿ unsureAsgnRef at 00404657
⎿ writeToStdErr__a2kDfqdSc1eYf0ZCWOGinQ at 00404692
⎿ raiseOverflow at 004046b1
⎿ align__vzThvqZajaR9ct9cQ7SOy1tQ at 0040471e
⎿ forAllChildren__XCvXrotwhq0gugZtuZTNPQ at 0040477b
⎿ markS__SOJE9bROCOc8iabVsKM64Sg at 00404818
⎿ doOperation__sl6eqhLncFedgwzv6TlMVw at 00404866
⎿ forAllChildrenAux__3hKpU9c72lqUqbltnsyFjRw at 00404897
⎿ forAllSlotsAux__ld9axHPi9bpxevVrdgKiDF5Q at 00404923
⎿ nimGCvisit at 004049f3
⎿ markGlobals__XHio9cMpnLoH7GyCj1Z9besg_4 at 00404c46
⎿ collectZCT__EN6T32AMm3va9bsrdxtF0cg at 00404cb7
⎿ collectCycles__XHio9cMpnLoH7GyCj1Z9besg_3 at 00404d03
⎿ collectCTBody__XHio9cMpnLoH7GyCj1Z9besg_2 at 00404d5b
⎿ collectCT__XHio9cMpnLoH7GyCj1Z9besg at 00404e15
⎿ rawNewObj__ehkAaLROrd0Hc9aLROWt1nQ at 00404e55
⎿ newObj at 00404f3a
⎿ rawNewString at 00404f63
⎿ mnewString at 00404f97
⎿ newObjNoInit at 00404fa9
⎿ rawNewStringNoInit at 00404fbe
⎿ resizeString at 00404fe8
⎿ addChar at 00405044
⎿ add__8FwY5enLGB0dFerO6Ny9caw at 004050b4
⎿ setLengthStr at 004050e0
⎿ addInt__mftMOxbyu0h4yByfs3sqjA at 00405174
⎿ nimIntToStr at 00405292
⎿ dollar___liwwu6qv5HyRiFI58oBO0g at 004052cb
⎿ toNimStr at 0040542c
⎿ cstrToNimstr at 00405454
⎿ newObjRC1 at 00405477
⎿ @copyStringRC1@4.part.0 at 004054b6
⎿ copyStringRC1 at 004054fd
⎿ dataPointer__dMaT1dYJWy6HCDx9bdXp24A at 00405510
⎿ newSeq at 00405527
⎿ incrSeqV3 at 004055bf
⎿ raiseExceptionEx at 00405647
⎿ sysFatal__Kx89alMoFPpWrEL9bx2cyJVwsystem at 004056c6
⎿ sysFatal__BVaWgkQV7bsZ7qgkYxa9a0Asystem at 00405734
⎿ reraiseException at 004057a2
⎿ showErrorMessage__zsORN9crdKxsL9cHrQcdHSMw at 0040581e
⎿ reportUnhandledErrorAux__na8C8pUZ9cLQWVwk35l5vfw_3 at 004058c1
⎿ reportUnhandledError__na8C8pUZ9cLQWVwk35l5vfw_2 at 00405a29
⎿ raiseExceptionAux__na8C8pUZ9cLQWVwk35l5vfw at 00405a46
⎿ initGC__amVlU9ajqZ06ujoesRBHcDg at 00405b1a
⎿ nimRegisterThreadLocalMarker at 00405be1
⎿ registerSignalHandler__amVlU9ajqZ06ujoesRBHcDg_2 at 00405c24
⎿ nimLoadLibrary at 00405c97
⎿ nimLoadLibraryError at 00405cb0
⎿ procAddrError at 00405d2d
⎿ nimGetProcAddr at 00405d7f
⎿ nimRegisterGlobalMarker at 00405e79
⎿ nimInt64ToStr at 00405ebc
⎿ raiseRangeErrorI at 00405efb
⎿ copyString at 00405fad
⎿ raiseIndexError2 at 00405fdd
⎿ addQuoted__45fPtFhY4FavRaYwDhRfuA at 00406065
⎿ substr__2yh9cer0ymNRHlOOg8P7IuA at 00406312
⎿ substr__iGg0RIKceRvsmvq8FUHOEw at 00406419
⎿ nimLeaveFinally at 00406434
⎿ newSeq__XSir3OdbhQfBvXkXJS9aoSg at 0040646e
⎿ raiseIndexError at 0040647a
⎿ X5BX5Deq___wCxLFNoF2DOiuJpFEiBO9cQ at 0040648a
⎿ isObj at 004065ab
⎿ raiseObjectConversionError at 004065c2
⎿ clamp__hyQgGui0RxAott4YXwjJHQ at 0040662f
⎿ systemInit000 at 00406646
⎿ systemDatInit000 at 00406673
⎿ nimAddInt at 00406f20
⎿ toLower__eK9b2e49aPf4wAIdUwhbmZsQparseutils at 00406f38
⎿ skipIgnoreCase__Z630VYBL4pZDWlOyn05K5w at 00406fa1
⎿ nsuFindChar at 00407098
⎿ loadLib__Yq5XYz2ycX5V5B9bUM4Uyiw at 00407144
⎿ symAddr__ALH9bdNwXEzg7MPq4PA9csvw at 00407167
⎿ stdlib_winleanInit000 at 00407180
⎿ stdlib_winleanDatInit000 at 004071ae
⎿ stdlib_timesInit000 at 004072da
⎿ stdlib_timesDatInit000 at 004072f5
⎿ nimAddInt at 0040753c
⎿ decRef__AT1eRuflKWyTTBdLjEDZbgsystem at 00407554
⎿ asgnRef at 00407579
⎿ appendString.part.0 at 004075e5
⎿ osErrorMsg__33xViSVWAmDrexoKkLfMhg at 00407606
⎿ newOSError__JXEuze9ctNbkn51HYBflQLg at 0040767e
⎿ raiseOSError__CWyPYlyH9a6rAuZckFyVxPA at 0040774f
⎿ osLastError__9bUWNxbcGnToMWA9b79aTXLIw at 0040778f
⎿ nosgetCurrentDir at 00407795
⎿ getEnvVarsC__580467zYn32AEdYj9cD4LLA at 00407811
⎿ findEnvVar__4kc4cxzsC7aY1IOKtOGazA at 004078ff
⎿ getEnv__hhED57tMl0Iaa5bOg9cJaig at 004079b9
⎿ nosgetHomeDir at 00407a9b
⎿ nostryRemoveFile at 00407add
⎿ nosremoveFile at 00407b47
⎿ nosexecShellCmd at 00407b73
⎿ nossleep at 00407b94
⎿ stdlib_osInit000 at 00407ba6
⎿ stdlib_osDatInit000 at 00407bc1
⎿ nimAddInt at 00407bf4
⎿ encode__npLRSgmGJDNX8bfurW5iRw at 00407c0c
⎿ burnMem__4FZHyz34TGxTmMy6XY9cOSg at 004081dc
⎿ digest__CXo4xdrVR0UXF9aOcb9aJFYg at 00408214
⎿ nimSubInt at 00408378
⎿ nimAddInt.constprop.0 at 00408391
⎿ sha256Transform__BJNBQtWr9bJwzqbyfKXd38Q at 004083a3
⎿ finish__x70ALeeaQ1ry9a63hdOCQWA at 0040ae22
⎿ nimAddInt at 0040af38
⎿ ortho__tEkTGAwQ2Ju9b64CfcPFjKgrijndael at 0040af50
⎿ bitsliceSbox__td6AIXVem9adJuhtvam1mYA at 0040b66d
⎿ subWord__eIyaZ4Ej9atGVh4yLO6rJVQ at 0040b8d0
⎿ keySchedule__atoyT3nOrMuAmdOTI4mO5g at 0040b915
⎿ encrypt__py6wg79aBw8iTzUm11Z7JOA at 0040bc4a
⎿ init__QeKCvRTxwnkv4EgDHKgXYA at 0040c444
⎿ inc128__vRz5m42fv3XKwSYgATX55Q at 0040c4c1
⎿ nimcrypto_sysrandInit000 at 0040c596
⎿ nimcrypto_sysrandDatInit000 at 0040c5a0
⎿ nimAddInt at 0040c7fc
⎿ nimSubInt at 0040c814
⎿ decRef__AT1eRuflKWyTTBdLjEDZbgsystem at 0040c88a
⎿ fsSetPosition__cci01mizUCs1g9a9bQPBwweQ_2 at 0040cb94
⎿ fsGetPosition__9aSaecE8PQfXbgmZSvJsfYw_2 at 0040cbdd
⎿ newStringStream__9aLRtgEYeRMrZKrObtoOslQ at 0040d0b5
⎿ newEIO__Ygv4g5RSSeYVT2oEYcgHSw at 0040d13e
⎿ newFileStream__L2sCmH55dBWs0l9aUTvGiSw at 0040d239
⎿ newFileStream__cwYJiP3D7DOTCJxCdBqBZQ at 0040d2ab
⎿ writeData__4hIni0uSy3RpPUiO9aB3JUQ_3 at 0040d2e5
⎿ write__egjcKjt8WddLFyjTFl5BAw at 0040d2e8
⎿ writeLine__2KoDZXJB4LmoH7PHLGmZ9cg at 0040d307
⎿ close__y1KA3B0U09bKtU09am9a9avRYQ_4 at 0040d36c
⎿ stdlib_streamsDatInit000 at 0040d376
⎿ asgnRef at 0040d830
⎿ _PreMainInner at 0040d874
⎿ appendString.part.0 at 0040d8ae
⎿ appendString at 0040d8cf
⎿ encrypt__dcoBdmUaaCC9cnR23eFxSLAbcmode at 0040d8d6
⎿ wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w at 0040d9c7
⎿ changeBackground__4JhDTDCSrwYIQ19bJbLaL2w_2 at 0040df1c
⎿ _PreMain at 0040dfe2
⎿ _NimMain at 0040e02e
⎿ NimMainModule at 0040e052
⎿ _NimMainInner at 0040e0a3
⎿ __do_global_dtors at 0040e0b0
⎿ __do_global_ctors at 0040e0f0
⎿ __main at 0040e150
⎿ my_lconv_init at 0040e170
⎿ _setargv at 0040e180
⎿ __security_init_cookie at 0040e190
⎿ __report_gsfailure at 0040e250
⎿ __dyn_tls_dtor at 0040e2e0
⎿ __dyn_tls_init at 0040e330
⎿ __tlregdtor at 0040e3d0
⎿ _matherr at 0040e3e0
⎿ _fpreset at 0040e460
⎿ __report_error at 0040e470
⎿ mark_section_writable at 0040e4d0
⎿ _pei386_runtime_relocator at 0040e610
⎿ __mingw_raise_matherr at 0040e890
⎿ __mingw_setusermatherr at 0040e8f0
⎿ _gnu_exception_handler at 0040e900
⎿ __mingwthr_run_key_dtors at 0040eab0
⎿ ___w64_mingwthr_add_key_dtor at 0040eb20
⎿ ___w64_mingwthr_remove_key_dtor at 0040eba0
⎿ __mingw_TLScallback at 0040ec30
⎿ _ValidateImageBase at 0040ed10
⎿ _ValidateImageBase at 0040ed40
⎿ _FindPESection at 0040ed60
⎿ _FindPESectionByName at 0040eda0
⎿ __mingw_GetSectionForAddress at 0040ee40
⎿ __mingw_GetSectionCount at 0040eec0
⎿ _FindPESectionExec at 0040eef0
⎿ _GetPEImageBase at 0040ef50
⎿ _IsNonwritableInCurrentImage at 0040ef80
⎿ __mingw_enum_import_library_names at 0040f010
⎿ ___chkstk_ms at 0040f0c0
⎿ ___divdi3 at 0040f0f0
⎿ wcschr at 0040f240
⎿ vfprintf at 0040f248
⎿ ungetc at 0040f250
⎿ system at 0040f258
⎿ strncmp at 0040f260
⎿ strlen at 0040f268
⎿ strerror at 0040f270
⎿ signal at 0040f278
⎿ setvbuf at 0040f280
⎿ printf at 0040f288
⎿ memcpy at 0040f290
⎿ memchr at 0040f298
⎿ malloc at 0040f2a0
⎿ getenv at 0040f2a8
⎿ fwrite at 0040f2b0
⎿ ftell at 0040f2b8
⎿ fseek at 0040f2c0
⎿ free at 0040f2c8
⎿ fread at 0040f2d0
⎿ fputc at 0040f2d8
⎿ fprintf at 0040f2e0
⎿ fgets at 0040f2e8
⎿ fgetc at 0040f2f0
⎿ fflush at 0040f2f8
⎿ ferror at 0040f300
⎿ fclose at 0040f308
⎿ exit at 0040f310
⎿ clearerr at 0040f318
⎿ calloc at 0040f320
⎿ abort at 0040f328
⎿ __setjmp3 at 0040f330
⎿ __onexit at 0040f338
⎿ __initterm at 0040f340
⎿ __cexit at 0040f348
⎿ __amsg_exit at 0040f350
⎿ ___setusermatherr at 0040f358
⎿ ___set_app_type at 0040f360
⎿ ___p__fmode at 0040f368
⎿ ___p__acmdln at 0040f370
⎿ ___getmainargs at 0040f378
⎿ __acrt_iob_func at 0040f380
⎿ mingw_get_invalid_parameter_handler at 0040f390
⎿ mingw_set_invalid_parameter_handler at 0040f3a0
⎿ .text.startup at 0040f3b0
⎿ register_frame_ctor at 0040f3e0
● Now I'll look more closely at the specific sub-functions called by
wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w to understand what values are being
passed to them. Let me focus on the key cryptographic functions that are
called:
● ghidra - decompile_function (MCP)(name:"readFile__4PGnM9bWmsH0Nu7dnr3XzgA")
⎿ {
"result": "/* __fastcall readFile__4PGnM9bWmsH0Nu7dnr3XzgA,4 */\
r\n\r\nundefined4 __fastcall readFile__4PGnM9bWmsH0Nu7dnr3XzgA(int
… +20 lines (ctrl+o to expand)
● ghidra - decompile_function (MCP)(name:"digest__CXo4xdrVR0UXF9aOcb9aJFYg")
⎿ {
"result": "/* __fastcall digest__CXo4xdrVR0UXF9aOcb9aJFYg,8 */\r \n\r\nundefined4 * __fastcall\r\ndigest__CXo4xdrVR0UXF9aOcb9aJFYg(
… +31 lines (ctrl+o to expand)
● ghidra - decompile_function (MCP)(name:"encrypt__dcoBdmUaaCC9cnR23eFxSLAbcmode")
⎿ {
"result": "/* encrypt__dcoBdmUaaCC9cnR23eFxSLAbcmode */\r\n\r\nv oid __cdecl encrypt__dcoBdmUaaCC9cnR23eFxSLAbcmode(int param_1,uin
… +16 lines (ctrl+o to expand)
● ghidra - decompile_function (MCP)(name: "encode__npLRSgmGJDNX8bfurW5iRw")
⎿ {
"result": "/* WARNING: Globals starting with '_' overlap smaller
symbols at the same address */\r\n/* __fastcall encode__npLRSgmGJ
… +103 lines (ctrl+o to expand)
● ghidra - decompile_function (MCP)(name:"writeFile__D6Pj9c29aCLEJP9beOWa08HYA")
⎿ {
"result": "/* __fastcall writeFile__D6Pj9c29aCLEJP9beOWa08HYA,8 */\r\n\r\nvoid __fastcall writeFile__D6Pj9c29aCLEJP9beOWa08HYA(int
… +19 lines (ctrl+o to expand)
● Based on my analysis of the function wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w and its sub-functions, I can now provide a detailed breakdown of what this function does and the values passed to native API calls:
Function Analysis: wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w
Overall Purpose
This function performs a cryptographic operation that reads data from a
file, processes it through cryptographic operations (hashing and
encryption), and writes the results to output files. It appears to be a
security or data protection utility.
Detailed Operation Flow
1. File Reading:
- Calls readFile__4PGnM9bWmsH0Nu7dnr3XzgA to read data from a file
- The file path is constructed from _homeDir__pJnrRb9aEgb0S3dMArlimMA
(home directory) plus offsets
2. String Manipulation:
- Uses rawNewString with calculated lengths (0x12, 0x1f, 0x18, 0x16,
0x1f)
- Uses appendString and appendString_part_0 for string concatenation
3. Cryptographic Operations:
- Hashing: digest__CXo4xdrVR0UXF9aOcb9aJFYg - performs SHA-256 hashing
- Takes parameters: local_4d4 (output buffer), param_2 + 2 (input
data), and uVar8 (data length)
- Encryption: encrypt__dcoBdmUaaCC9cnR23eFxSLAbcmode - performs AES
encryption in CTR mode
- Takes parameters: param_2 + 2 (input data), and uVar8 (data
length)
- Encoding: encode__npLRSgmGJDNX8bfurW5iRw - performs Base64 encoding
- Takes parameters: param_2 + 2 (input data), uVar8 (data length),
and '\0' (null terminator)
4. File Writing:
- Calls writeFile__D6Pj9c29aCLEJP9beOWa08HYA to write processed data to
a file
- Uses newFileStream__cwYJiP3D7DOTCJxCdBqBZQ for file stream operations
- Writes to a file with path constructed from
_homeDir__pJnrRb9aEgb0S3dMArlimMA plus offsets
5. Memory Management:
- Uses burnMem__4FZHyz34TGxTmMy6XY9cOSg to clear sensitive data from
memory
- Uses init__QeKCvRTxwnkv4EgDHKgXYA for initialization operations
Native API Calls and Parameter Values
6. readFile__4PGnM9bWmsH0Nu7dnr3XzgA:
- Called with file path parameter (constructed from
_homeDir__pJnrRb9aEgb0S3dMArlimMA plus offsets)
7. digest__CXo4xdrVR0UXF9aOcb9aJFYg:
- First parameter: local_4d4 (output buffer, 8 uints = 32 bytes for
SHA-256)
- Second parameter: param_2 + 2 (input data pointer)
- Third parameter: uVar8 (data length)
8. encrypt__dcoBdmUaaCC9cnR23eFxSLAbcmode:
- First parameter: param_2 + 2 (input data pointer)
- Second parameter: uVar8 (data length)
9. encode__npLRSgmGJDNX8bfurW5iRw:
- First parameter: param_2 + 2 (input data pointer)
- Second parameter: uVar8 (data length)
- Third parameter: '\0' (null terminator)
10. writeFile__D6Pj9c29aCLEJP9beOWa08HYA:
- First parameter: file path constructed from
_homeDir__pJnrRb9aEgb0S3dMArlimMA plus offset 0x18
- Second parameter: encoded data from previous operations
11. newFileStream__cwYJiP3D7DOTCJxCdBqBZQ:
- First parameter: file path constructed from
_homeDir__pJnrRb9aEgb0S3dMArlimMA plus offset 0x16
- Second parameter: 1 (write mode)
- Third parameter: 0xffffffff (file access flags)
12. writeLine__2KoDZXJB4LmoH7PHLGmZ9cg:
- First parameter: file stream handle
- Second parameter: string "A" (0x4120dc)
- Third parameter: 1 (line length)
13. close__y1KA3B0U09bKtU09am9a9avRYQ_4:
- First parameter: file stream handle
Key Values and Constants
- String lengths: 0x12, 0x1f, 0x18, 0x16, 0x1f (calculated from home
directory path)
- Buffer sizes: 0x20 (32 bytes) for SHA-256 output, 0x228 (552 bytes) for
memory clearing
- File operations: Uses standard file I/O functions with error handling
- Cryptographic modes: SHA-256 hashing, AES CTR encryption, Base64
encoding
Security Operations
The function implements a complete cryptographic pipeline that:
1. Reads sensitive data from a file
2. Hashes it using SHA-256
3. Encrypts it using AES in CTR mode
4. Encodes the result in Base64
5. Writes the processed data to output files
6. Cleans up sensitive memory with burnMem
This appears to be part of a security system that processes sensitive data
through multiple cryptographic operations for protection or
authentication purposes.
✻ Churned for 27m 2s
Claude Pro Subscription:
Again, much faster completing in 2 mins 27 seconds.
Provided an overview of each function identified.
After this prompt I had used 13% of my 4 hour token allowance.
There was no HiTL for the MCP tool usage.
● I'll perform a comprehensive analysis of this function and its
sub-functions. Let me start by decompiling the key sub-functions called
within wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w.
● ghidra - decompile_function (MCP)(name: "readFile__4PGnM9bWmsH0Nu7dnr3XzgA")
⎿ {
"result": "/* __fastcall readFile__4PGnM9bWmsH0Nu7dnr3XzgA,4 */\
r\n\r\nundefined4 __fastcall readFile__4PGnM9bWmsH0Nu7dnr3XzgA(int … +20 lines (ctrl+o to expand)
● ghidra - decompile_function (MCP)(name:"digest__CXo4xdrVR0UXF9aOcb9aJFYg")
⎿ {
"result": "/* __fastcall digest__CXo4xdrVR0UXF9aOcb9aJFYg,8 */\r \n\r\nundefined4 * __fastcall\r\ndigest__CXo4xdrVR0UXF9aOcb9aJFYg(
… +31 lines (ctrl+o to expand)
● ghidra - decompile_function (MCP)(name: "init__QeKCvRTxwnkv4EgDHKgXYA")
⎿ { "result": "/* __fastcall init__QeKCvRTxwnkv4EgDHKgXYA,20 */\r\n\
r\nvoid __fastcall\r\ninit__QeKCvRTxwnkv4EgDHKgXYA\r\n (u … +11 lines (ctrl+o to expand)
● ghidra - decompile_function (MCP)(name: "encrypt__dcoBdmUaaCC9cnR23eFxSLAbcmode") ⎿ { "result": "/* encrypt__dcoBdmUaaCC9cnR23eFxSLAbcmode */\r\n\r\nv
oid __cdecl encrypt__dcoBdmUaaCC9cnR23eFxSLAbcmode(int param_1,uin
… +16 lines (ctrl+o to expand)
● ghidra - decompile_function (MCP)(name: "encode__npLRSgmGJDNX8bfurW5iRw")
⎿ { "result": "/* WARNING: Globals starting with '_' overlap smaller
symbols at the same address */\r\n/* __fastcall encode__npLRSgmGJ … +103 lines (ctrl+o to expand)
● ghidra - decompile_function (MCP)(name: "writeFile__D6Pj9c29aCLEJP9beOWa08HYA")
⎿ { "result": "/* __fastcall writeFile__D6Pj9c29aCLEJP9beOWa08HYA,8
*/\r\n\r\nvoid __fastcall writeFile__D6Pj9c29aCLEJP9beOWa08HYA(int
… +19 lines (ctrl+o to expand)
● ghidra - decompile_function (MCP)(name: "burnMem__4FZHyz34TGxTmMy6XY9cOSg")
⎿ { "result": "/* __fastcall burnMem__4FZHyz34TGxTmMy6XY9cOSg,8 */\r
\n\r\nvoid __fastcall burnMem__4FZHyz34TGxTmMy6XY9cOSg(undefined1
… +9 lines (ctrl+o to expand)
● ghidra - decompile_function (MCP)(name: "nosremoveFile")
⎿ { "result": "/* __fastcall nosremoveFile,4 */\r\n\r\nvoid __fastca
ll nosremoveFile(uint *param_1)\r\n\r\n{\r\n bool bVar1;\r\n uin
… +5 lines (ctrl+o to expand) ● Let me continue analyzing the deeper sub-functions:
● ghidra - decompile_function (MCP)(name: "sha256Transform__BJNBQtWr9bJwzqbyfKXd38Q")
⎿ {"result":"/* __fastcall sha256Transform__BJNBQtWr9bJwzqbyfKXd38Q,
12 */\r\n\r\nvoid __fastcall sha256Transform__BJNBQtWr9bJwzqbyfKXd 38Q(uint *param_1,uint *param_2,uint param_3)\r\n\r\n{\r\n uint u … +592 lines (ctrl+o to expand)
● ghidra - decompile_function (MCP)(name: "finish__x70ALeeaQ1ry9a63hdOCQWA")
⎿ {
"result": "/* __fastcall finish__x70ALeeaQ1ry9a63hdOCQWA,4 */\r\
n\r\nundefined4 * __fastcall finish__x70ALeeaQ1ry9a63hdOCQWA(undef … +25 lines (ctrl+o to expand)
● ghidra - decompile_function (MCP)(name: "keySchedule__atoyT3nOrMuAmdOTI4mO5g")
⎿ {
"result": "/* __fastcall keySchedule__atoyT3nOrMuAmdOTI4mO5g,12
*/\r\n\r\nvoid __fastcall\r\nkeySchedule__atoyT3nOrMuAmdOTI4mO5g(u … +63 lines (ctrl+o to expand)
● ghidra - decompile_function (MCP)(name: "encrypt__py6wg79aBw8iTzUm11Z7JOA")
⎿ {
"result": "/* __fastcall encrypt__py6wg79aBw8iTzUm11Z7JOA,20 */\ r\n\r\nvoid __fastcall\r\nencrypt__py6wg79aBw8iTzUm11Z7JOA\r\n
… +125 lines (ctrl+o to expand)
● ghidra - decompile_function (MCP)(name: "inc128__vRz5m42fv3XKwSYgATX55Q")
⎿ { "result": "/* __fastcall inc128__vRz5m42fv3XKwSYgATX55Q,8 */\r\n
\r\nvoid __fastcall inc128__vRz5m42fv3XKwSYgATX55Q(int param_1,uin … +9 lines (ctrl+o to expand)
● ghidra - get_xrefs_to (MCP)(address: "0x4120dc")
⎿ From 0040de7e in wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w [DATA]
● Now let me examine the string constants and data references:
● ghidra - list_strings (MCP)(limit: 100, filter: "homeDir")
● ghidra - list_strings (MCP)(limit: 100, filter:"TM__njFKfyRiYvomtvTKocFwDw")
● Bash(echo "Now analyzing the main function flow and cryptographic
operations")
⎿ Now analyzing the main function flow and cryptographic operations
● Comprehensive Verbose Analysis of wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w
This function implements a file encryption routine typical of ransomware. Below is the detailed breakdown:
---
MAIN FUNCTION: wannaHusky__4JhDTDCSrwYIQ19bJbLaL2w
Phase 1: Exception Handler Setup
- Native API: __setjmp3(local_4ac, 0) - Sets up non-local jump for
exception handling
- Creates exception handler chain using
_excHandler__rqLlY5bs9atDw2OXYqJEn5g
- If an exception occurs (local_4b0 != 0), it checks if it's an IOError
object (type 0x422c00)
- On exception: copies exception message, calls echoBinSafe() to output
it, then jumps to cleanup
Phase 2: File Path Construction
The function builds file paths using _homeDir__pJnrRb9aEgb0S3dMArlimMA as
the base directory:
1. First path (length = 0x12 + homeDir length):
- Calls rawNewString(uVar8) to allocate string
- Uses appendString() and appendString_part_0() to build path
- Calculated total: ~18 bytes + home directory length
2. Second path (length = 0x1f + homeDir length):
- Calculated total: ~31 bytes + home directory length
- Likely the target file to encrypt
3. Output paths constructed later:
- Path with 0x18 (24) byte offset for encrypted output
- Path with 0x16 (22) byte offset for log/ransom note
Phase 3: File Reading
Sub-function: readFile__4PGnM9bWmsH0Nu7dnr3XzgA
- Opens file with open__gq12VLhVO0NBzUTnGgz4nw(file_handle, path, mode=0,
max_size=0xffffffff)
- Mode 0 = Read mode
- If open fails, raises IOError exception with message "IOError" +
filename
- Reads entire file content using readAll__mQf9blHFlTwuSvJjirhpY6g()
- Properly closes file with close__fU6ZlJAtQ9bre04EDZLdGsA_2()
- Uses try-finally pattern with exception safety
---
Phase 4: Cryptographic Setup
4.1 Hash Generation
Sub-function: digest__CXo4xdrVR0UXF9aOcb9aJFYg - SHA-256 Hash Function
Initial state (SHA-256 constants):
local_7c[0] = 0x6a09e667
local_7c[1] = 0xbb67ae85
local_7c[2] = 0x3c6ef372
local_7c[3] = 0xa54ff53a
local_7c[4] = 0x510e527f
local_7c[5] = 0x9b05688c
local_7c[6] = 0x1f83d9ab
local_7c[7] = 0x5be0cd19
These are the standard SHA-256 initialization vectors (first 32 bits of
fractional parts of square roots of first 8 primes).
Process:
1. Takes input data (a passphrase/key string from
_TM__njFKfyRiYvomtvTKocFwDw_7)
2. Processes in 64-byte (0x40) blocks
3. Calls sha256Transform__BJNBQtWr9bJwzqbyfKXd38Q() for each block
4. Finalizes with finish__x70ALeeaQ1ry9a63hdOCQWA()
5. Securely wipes memory with burnMem__4FZHyz34TGxTmMy6XY9cOSg(buffer,
0x68) - 104 bytes zeroed
Sub-function: sha256Transform__BJNBQtWr9bJwzqbyfKXd38Q
- Performs byte-swapping (big-endian conversion) on input
- Implements full SHA-256 compression function with:
- 64 rounds of transformation
- Uses standard SHA-256 K constants (0x428a2f98, 0x71374491, 0xb5c0fbcf,
etc.)
- Applies Ch(e,f,g) and Maj(a,b,c) functions
- Sigma rotations: >> 2, >> 13, >> 22 and >> 6, >> 11, >> 25
Sub-function: finish__x70ALeeaQ1ry9a63hdOCQWA
- Pads message with 0x80 byte
- Appends length in bits (big-endian 64-bit)
- Final SHA-256 transform
- Returns 32-byte (256-bit) hash
---
4.2 AES Initialization
Sub-function: init__QeKCvRTxwnkv4EgDHKgXYA - AES-256 CTR Mode Setup
Parameters passed:
- param_1 = Encryption context buffer (local_46c, size 0x228 = 552 bytes)
- param_2 = Key derived from SHA-256 hash (local_4f4)
- param_3 = 0x20 (32 bytes = AES-256 key size)
- param_4 = IV/Nonce (local_504)
- param_5 = 0x10 (16 bytes = AES block size)
Validation:
- Asserts param_5 >= 0x10 (IV must be at least 16 bytes)
- Asserts param_3 >= 0x20 (Key must be at least 32 bytes)
Process:
1. Calls keySchedule__atoyT3nOrMuAmdOTI4mO5g() to expand the key
2. Initializes counter at offset param_1 + 0x79 using
X5BX5Deq___wCxLFNoF2DOiuJpFEiBO9cQ()
Sub-function: keySchedule__atoyT3nOrMuAmdOTI4mO5g - AES Key Schedule
- Sets param_1[0x78] = 0x0e (14 rounds for AES-256)
- Expands 32-byte key into round keys
- Uses subWord__eIyaZ4Ej9atGVh4yLO6rJVQ() for S-box substitutions
- XORs with Rcon (round constants) from _Rcon__QBxa0ZSdl2DRSRpxxtw6IA
- Generates 120 words (0x78 = 120 in decimal) for 14 rounds
- Applies ortho__tEkTGAwQ2Ju9b64CfcPFjKgrijndael() for bitslicing
transformation
---
Phase 5: Encryption Operations
The main function creates three separate buffers from the file content:
1. piVar4 - Original file content
2. puVar5 - First encrypted copy
3. puVar6 - Second encrypted copy (possibly for verification/redundancy)
Sub-function: encrypt__dcoBdmUaaCC9cnR23eFxSLAbcmode - CTR Mode Encryption
Parameters (from register state):
- param_1 = Output buffer address
- param_2 = Output buffer size
- in_ECX = Input size
- in_EDX = Input buffer address
- in_EAX = Encryption context
CTR Mode Process:
1. Checks counter position: uVar2 = in_EAX[0x89]
2. When counter buffer empty (uVar2 == 0):
- Encrypts counter: encrypt__py6wg79aBw8iTzUm11Z7JOA(context,
counter_at_0x79, 0x20, output_at_0x81, 0x20)
- Increments 128-bit counter: inc128__vRz5m42fv3XKwSYgATX55Q(counter,
0x20)
3. XORs plaintext with keystream: output[i] = input[i] ^
keystream[counter_pos]
4. Updates counter position: counter_pos = (counter_pos + 1) & 0xf
Sub-function: encrypt__py6wg79aBw8iTzUm11Z7JOA - AES Block Cipher
This implements bitsliced AES-256 encryption:
1. Input transformation: Calls ortho__tEkTGAwQ2Ju9b64CfcPFjKgrijndael() to
convert to bitsliced form
2. Initial round: XOR with first round key (param_1[0-7])
3. Main rounds (13 iterations, controlled by param_1[0x78] = 14):
- bitsliceSbox__td6AIXVem9adJuhtvam1mYA() - Non-linear S-box
substitution
- Bit permutation operations (shifts and masks)
- MixColumns-equivalent operations using rotations and XORs
- Round key addition
4. Final round: S-box, permutation, key addition (no MixColumns)
5. Output transformation: ortho__tEkTGAwQ2Ju9b64CfcPFjKgrijndael() back to
normal form
Sub-function: inc128__vRz5m42fv3XKwSYgATX55Q - 128-bit Counter Increment
- Treats 16-byte buffer as 128-bit big-endian integer
- Adds 1 with carry propagation from LSB to MSB
- Loop from byte 15 down to 0: counter[i] += carry
---
Phase 6: Encoding
Sub-function: encode__npLRSgmGJDNX8bfurW5iRw - Base64 Encoding
Parameters:
- param_1 = Input buffer
- param_2 = Input size
- param_3 = Encoding type (0 = standard, else = URL-safe)
Alphabet selection:
- If param_3 == 0:
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
- Else: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
Calculation:
- Output size: (input_size * 4 + 2) / 3 (standard base64 expansion)
- Processes 3 input bytes → 4 output characters
- Handles padding with = for incomplete blocks
Process:
1. Divides input into 3-byte chunks
2. Converts each triplet to 4 base64 characters using bit shifts
3. Handles remainder (1 or 2 bytes) with padding
---
Phase 7: File Output
Sub-function: writeFile__D6Pj9c29aCLEJP9beOWa08HYA
- Opens file with open__gq12VLhVO0NBzUTnGgz4nw(handle, path, mode=1,
max_size=0xffffffff)
- Mode 1 = Write mode
- Writes base64-encoded encrypted data using
write__PArlm09bKklm2BLsCg6YtaA()
- Properly closes file
- Uses exception-safe try-finally pattern
Encrypted file path: Home directory + 24-byte offset string
Ransom note creation:
1. Creates file stream with newFileStream__cwYJiP9bD7DOTCJxCdBqBZQ(path,
mode=1, maxsize=0xffffffff)
- Path: Home directory + 22-byte offset
2. Writes line with writeLine__2KoDZXJB4LmoH7PHLGmZ9cg(stream,
address=0x4120dc, flag=1)
- Address 0x4120dc contains the ransom message text
3. Closes stream with close__y1KA3B0U09bKtU09am9a9avRYQ_4()
---
Phase 8: Cleanup
Sub-function: burnMem__4FZHyz34TGxTmMy6XY9cOSg - Secure Memory Wiping
- Called with (buffer, size) parameters
- Performs two-pass zeroing:
a. First pass: zeros size bytes from start
b. Second pass: zeros again (counter-measure against compiler
optimizations)
- Used to wipe:
- Encryption context: 0x228 (552) bytes
- Hash state: 0x68 (104) bytes
Sub-function: nosremoveFile - Original File Deletion
- Attempts to remove file with nostryRemoveFile(path)
- If deletion fails, gets OS error code:
osLastError__9bUWNxbcGnToMWA9b79aTXLIw()
- Raises OS error exception:
raiseOSError__CWyPYlyH9a6rAuZckFyVxPA(error_code, path)
- This permanently deletes the original unencrypted file
---
Summary of Cryptographic Operations
Encryption Chain:
1. Passphrase/Key String
↓
2. SHA-256 Hash → 32-byte key
↓
3. AES-256 Key Schedule (14 rounds)
↓
4. AES-256-CTR Encryption
↓
5. Base64 Encoding
↓
6. Write to .encrypted file
Key Parameters:
- Hash algorithm: SHA-256 (256-bit output)
- Encryption: AES-256 in CTR mode
- Key size: 32 bytes (256 bits)
- Block size: 16 bytes (128 bits)
- Rounds: 14 (standard for AES-256)
- Encoding: Base64
- Memory wiping: Dual-pass zeroing for 656 total bytes
Ransomware Behavior:
1. ✓ Reads target file
2. ✓ Derives encryption key from passphrase via SHA-256
3. ✓ Encrypts file with AES-256-CTR
4. ✓ Encodes with Base64
5. ✓ Writes encrypted version
6. ✓ Writes ransom note
7. ✓ Deletes original file
8. ✓ Securely wipes cryptographic material from memory
This is a complete file encryption ransomware implementation using
industry-standard cryptography (SHA-256 + AES-256-CTR).
✻ Sautéed for 2m 27s
Credit usage:
Conclusion
The locally hosted model impressed me a lot here; despite the increased wait time, the additional justification and output it could be more helpful in some aspects of the analysis. In terms of the analysis returned by the model, it was pretty comparable to that from the paid model, there were some things missing that the paid model did - but that could potentially be resolved by some prompt engineering or follow up prompts.
The Claude Pro model also did a great job in its analysis in translating the technical observations into digestible analysis. Also returning some pieces of information that wasn't found in the local's analysis, such as the suspected encryption algorithms used as well as some the overview per function that was provided. It also did a better job in identifying that this was likely Ransomware, whereas the local model referred to it as an 'encryption or security program'. The most notable improvement here was the speed of the analysis, however the token cost and 4-hour time window may be an issue for longer/more detailed investigation requirements. The 4-hour window issue may be offset by the sheer gain in speed from responses in using the pro model.
This comparison may not paint a complete picture of the benefits or drawbacks of each model, especially considering that this was 'tested' with only two prompts that weren't particularly advanced. Also, the context length may come into play for lengthy analysis and troubleshooting, in this regard the pro model would likely take the cake with their 200k context window as opposed to the 64k set in Ollama - although this can easily be increased up to 256k but if you don't have a big enough rig then that's not feasible.
Overall, if you have the hardware to self-host an LLM along-side your analysis workstation then I would recommend hosting it locally. You save money and get to play with the shiny new toys. I also think that local hosting is viable if you're just testing out the Ghidra MCP and trying to see if an AI integration into your analysis flow is worth it.
I would recommend the paid LLM from Claude if your system just isn't up to the task to run both resources at the same time. Also, the time save for some analysis is definitely worth it, 2 minutes vs 27 minutes is a huge delay if your task is time sensitive. Also, if you wanted to test out Claude in a wider array of use-cases and not just the Ghidra MCP then sure, shoot for the paid too!



