{
	"id": "768b4552-37c2-4710-8504-da9af884cd6e",
	"created_at": "2026-04-06T00:07:37.081692Z",
	"updated_at": "2026-04-10T13:12:42.879103Z",
	"deleted_at": null,
	"sha1_hash": "1ec973bb1567ba0320c64057a38c350e9b46137b",
	"title": "How To Use Ghidra For Malware Analysis - Identifying, Decoding and Fixing Encrypted Strings",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 6016390,
	"plain_text": "How To Use Ghidra For Malware Analysis - Identifying, Decoding\r\nand Fixing Encrypted Strings\r\nBy Matthew\r\nPublished: 2023-12-05 · Archived: 2026-04-05 16:46:31 UTC\r\nIn this post, we will investigate a Vidar Malware sample containing suspicious encrypted strings. We will use\r\nGhidra cross references to analyse the strings and identify the location where they are used.\r\nUsing this we will locate a string decryption function, and utilise a debugger to intercept input and output to\r\nobtain decrypted strings.\r\nWe will then semi-automate the process, obtaining a full list of decoded strings that can be used to fix the\r\npreviously obfuscated Ghidra database.\r\nSummary\r\nDuring basic analysis of a Vidar file, we can see a large number of base64 strings. These strings are not able to be\r\ndecoded using base64 alone as there is additional encryption. By using Ghidra String References we can where\r\nthe base64 is used, and hence locate the function responsible for decoding.\r\nWith a decoding function found, it is trival to find the \"start\" and \"end\" of the decryption process. Using this\r\nknowledge we can load the file into a debugger and set breakpoints on the beginning and end of the decoding\r\nfunction. This enables us to view the input (encoded string) and output (decoded string) without needing to reverse\r\nengineer the decryption process.\r\nBy further adding a simple log command into the debugger (x32dbg), we can tell x32dbg to print all values at the\r\nstart and end of the decryption function. This is a means of automation that is simple to implement without coding\r\nknowledge.\r\nOnce the encrypted/decrypted contents have been obtained, we can use this to manually edit the original Ghidra\r\nfile and gain a deeper understanding of the malware's hidden functionality.\r\nObtaining the File\r\nThe file can be downloaded here from Malware Bazaar.\r\nSHA256: 0823253d24e0958fa20c6e0c4b6b24028a3743c5c895c577421bdde22c585f9f\r\nInitial Analysis and Identifying Strings\r\nWe can download the file from Malware Bazaar using the link above, we can then unzip the file using the\r\npassword infected .\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 1 of 26\n\nWe like to create a copy of the origininal file with a shorter and more useful file name. In this case we\r\nhave chosen vidar.bin .\r\nWe can perform some basic initial analysis using Detect-it-easy. A typical workflow in detect-it-easy is to look for\r\nstrings contained within the file.\r\nIf we select the \"strings\" option, we can see a large number of base64-like strings.\r\n(You could also use PeStudio or any other tooling that can identify strings)\r\nThe default minimum string length is 5, which results in a lot of junk strings. By increasing this to 10,\r\nwe can more easily identify strings of interest.\r\nIn the screenshot below we can see a group of base64-like strings. In many cases, encoded strings like these are\r\nused to obfuscate functionality and Command-and-Control (C2) servers.\r\nHence, they are a useful indicator to hone in on with tooling like Ghidra.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 2 of 26\n\nNow that we've identified some interesting strings within the file, we can use Ghidra to analyse them further and\r\nattempt to establish some context as to how they are used.\r\nHow To Load a File Into Ghidra\r\nTo analyse these strings further, we can go ahead and load the file into Ghidra.\r\nThis can be done by dragging the file into Ghidra, accepting all default options and allowing the Ghidra analysis\r\nto run for a few minutes.\r\nWe can then continue our analysis by locating the same strings we found during the initial analysis. In this case,\r\nwe can start with the first base64 string of tw+lvmZw5kffvene\r\nThe screenshots below demonstrate how to perform a string search with Ghidra. Search -\u003e For Strings\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 3 of 26\n\nGhidra will present a window like the one below; we can typically go ahead and accept the defaults.\r\nMake sure that Selection Scope -\u003e Search All is selected. Sometimes Ghidra changes to Selection\r\nScope -\u003e Search Selection if you have something highlighted.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 4 of 26\n\nOnce we've accepted the default search options, we can filter at the beginning of our previous string tw+ to\r\nlocate it.\r\nThis will reveal 3 strings, starting with tw+\r\nWe can double-click on any of the returned strings to go to its location within the file.\r\nGhidra will automatically recognise if the location storing the string has been used elsewhere in the file.\r\nThis is known as a cross reference (xref) and is an extremely useful concept to become familiar with.\r\nIn this view, we can also see that one Cross Reference (XREF) is available. This indicates that Ghidra has found\r\none location where the string is used.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 5 of 26\n\nDouble-clicking the xref value will show us where the string has been referenced.\r\nAfter double-clicking on the xref value, we can see the base64 string (as well as others) contained within the\r\nfunction FUN_004016a6 .\r\nWe can also see each of these strings is passed to FUN_00401526 . Since every string is going to the same function,\r\nit is very likely the one responsible for decryption.\r\nSide note - These strings undergo additional obfuscation as well as base64. We won't be able to decode\r\nthem using base64 alone.\r\nIf we click on the FUN_00401526 function taking all the encoded strings, we can see that it's rather long,\r\nconfusing and contains a lot of junk code.\r\nLuckily, we don't need to analyse it in detail in order to decrypt the strings. Since we know the location of the\r\nfunction within the file, we can use a debugger to obtain the decrypted content for us.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 6 of 26\n\nThe name of the function is the location within the file. This is all we need to be able to locate it within\r\na debugger.\r\nEg for function FUN_00401526 , the location of the function will be 00401526 .\r\nAs a side note, if we look at the same function within the disassembly view on the left hand side, we\r\ncan see that there are 542 xrefs available.\r\nThis means that FUN_00401526 is used 542 times throughout the file, a number this high is another\r\nstrong indicator that the function is used for decoding.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 7 of 26\n\nWe now know the location of a function that is likely responsible for decrypting the strings. Although we could\r\nanalyse it statically, this is difficult, time consuming and often unnecessary.\r\nA better method is to load the file into a debugger and use breakpoints to monitor the function's location. This\r\nmethod can be used to obtain input (encrypted string) and output (decrypted string) without needing to analyse the\r\nfunction manually. We just need to know where the function starts.\r\nLoading The File Into x32dbg\r\nSince we now have a function to monitor, we can go ahead and load the file into x32dbg for further analysis.\r\nWe can start this by dragging the file into x32dbg and allowing the file to reach its entry point using F9 or\r\nContinue .\r\nBefore continuing analysis in the debugger, we need to confirm the base address is the same as in Ghidra. This\r\nensures that the function will be stored at the same location.\r\nThe location within Ghidra and X32dbg will always be \u003cbase address\u003e + xyz. But if \u003cbase address\u003e\r\ndiffers, then we occasionally need to fix it.\r\nWe can double-check the base address by clicking on the Memory map option within x32dbg. The base address\r\nwill be the one on the same line as your file name.\r\nThe base address in our case was 0x000f0000 (this address may differ for you)\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 8 of 26\n\nWe need to make sure that this base address is aligned with Ghidra.\r\nThe base address can be found in Display Memory Map -\u003e View Base Address .\r\nIn this case, Ghidra's base address is 0x00400000 , we can manually change this to match the 0x000f0000 found\r\nin x32dbg.\r\nFixing the base address is as simple as changing the value to 0x000f000\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 9 of 26\n\nAfter selecting OK , Ghidra will reload the file with the new base address.\r\nAfter reloading a base address, sometimes Ghidra will get lost. You may need to do another string\r\nsearch + xref (same process as before) to identify the string decryption function again.\r\nWith the correct base address now loaded, the string decryption function will have a new name FUN_000f1526 to\r\nreflect its new location.\r\nWe can now use this address 000f1526 to create a breakpoint within x32dbg.\r\nSetting Breakpoints on the Decryption Function\r\nWe now want to create a breakpoint at the corrected address of the decryption function.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 10 of 26\n\nUsing the new address of 000f1526 , we can go back to x32dbg and create a breakpoint using bp 000f1526\r\nWith the breakpoint set, we can let the malware run until the function is triggered.\r\nWhen the breakpoint is hit, we can view the current encoded string within the stack window on the right-hand side\r\nof x32dbg.\r\nIf we allow the function to complete using the Execute Until Return option, we can jump to the end of the\r\ndecryption function and see if any decrypted output is present.\r\nExecute Until Return tells the debugger to allow the current function to finish without continuing\r\nbeyond the current function. This is an easy way to obtain function output without it getting lost\r\nsomewhere during execution.\r\nThe \"Execute Until Return\" button looks like this.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 11 of 26\n\nAfter the Execute Until Return has completed, we can observe the first decoded string HAL9TH within the\r\nregister window.\r\nThe decoded string is contained within EAX , which is the most common location where function\r\noutput will be stored.\r\nNow that the decoded string is visible, we should note the current location of EIP within the debugger. This will\r\ntell us where we can find a decrypted copy of the string.\r\nIn the screenshot below, we can see that this location is 0x000f16a3 . This is the end of the decryption function,\r\nand we should create another breakpoint here.\r\nCreating a breakpoint here is functionally identical to using Execute Until Return every time we hit\r\nthe function, but creating a second breakpoint is much easier.\r\nThe new breakpoint can be created with bp 000f16a3 or by pressing F2 on the address highlighted in green.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 12 of 26\n\nIf we continue to execute using F9 or Continue , we will hit the original string decryption function again.\r\nThis time, there is a new encoded string present in the stack window lgWSvkdzsA== .\r\nAllowing the malware to run with F9 again, will trigger our second breakpoint, which contains the decoded\r\nvalue of JohnDoe .\r\nAs you obtain decrypted values, it can be useful to google them to determine their purpose within the context of\r\nmalware.\r\nAccording to CyberArk, The two values JohnDoe and HAL9TH are default values used by the Windows\r\nDefender Emulator. The malware likely uses these values later to determine if it's being emulated inside of\r\nWindows Defender.\r\nObtaining Additional Decoded Values\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 13 of 26\n\nBy allowing the malware to execute with F9 , we will continue to hit the existing breakpoints and observe\r\ndecoded values.\r\nHere, we can see that the malware has decrypted some Windows API names (LoadLibraryA, VirtualAlloc) as well\r\nas strings related to Crypto Wallets (Ethereum, ElectronCash, Binance).\r\nThis knowledge allows us to assume that the malware is dynamically loading APIs and likely stealing Crypto\r\nWallet data.\r\nIf we recall, there were 542 references to the string decryption function before. Since there are a few too many to\r\nobserve manually, we can perform some basic automation using a debugger.\r\nAutomating the Process With Conditional Breakpoints\r\nNow that we have existing breakpoints at the start and end of the decryption function, we can add a log condition\r\nto print the interesting values to the log window.\r\nWe can add a log condition by modifying our existing breakpoints. We can do this within the breakpoint window,\r\nand then Right-Click -\u003e Edit on the two existing breakpoints.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 14 of 26\n\nPrinting Encoded Strings With x32dbg\r\nOur first breakpoint is at the \"start\" of the encryption function, and we know from previous analysis that the\r\nencoded value will be inside the stack window.\r\nObserving the stack window closer, we can see that the exact location is [esp+4]\r\nWe can now tell the breakpoint to log the string contained at [esp+4]\r\nWe can do this with the command Encoded: {s:[esp+4]} . The \"Encoded: \" part is not necessary but it makes the\r\noutput easier to read.\r\nSince we don't need to stop at every breakpoint (we just want to log the results), we can add another condition\r\nrun; in Command Text .\r\nThis will tell x32dbg to resume execution after printing the output.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 15 of 26\n\nPrinting Decoded Strings with x32dbg\r\nWe can repeat the same process for the second breakpoint.\r\nThis time instead of printing [esp+4] , we want to print the decoded value contained in eax\r\nAfter editing the second breakpoint, we want it to look something like this.\r\nThis should be identical to the previous breakpoint, with only [esp+4] being replaced with eax .\r\nWe can also change Encoded: to Decoded: to make the final output easier to read.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 16 of 26\n\nWith the new breakpoints saved, we can restart the malware or allow it to continue its current execution. This will\r\nprint all encoded and decoded values to the log window.\r\n(You can find the log window next to the breakpoints window)\r\nAfter restarting the malware and leaving the breakpoints intact, we can see our initial encoded string and its\r\ndecoded value of kernel32.dll .\r\nWe can also see additional decoded values related to Ethereum key stores.\r\nObtaining Only Decrypted Values\r\nBy temporarily disabling the initial breakpoint (right click -\u003e disable) , we can print only the decoded values.\r\nHere, we can see some potential encryption keys, as well as SQL commands used to steal mozilla Firefox cookies.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 17 of 26\n\nWe can also observe that the malware attempts to steal credit card information from web browsers.\r\n,\r\nIf we go back to Ghidra, we can revisit the initial function containing references to encrypted strings.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 18 of 26\n\nSince we now have both the encrypted and decrypted values, we can edit the Ghidra view to reflect the decoded\r\ncontent.\r\nHere, we can see decoded values within x32dbg, reflecting the same encoded values as the above screenshot.\r\nWe can also note that after each call to the decoding function, the result is stored inside of a global variable\r\n(indicated by a green DAT_00138e98 etc, on the left-hand side).\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 19 of 26\n\nThis usually means that the same variable will be referenced each time the decoded string is used. If we\r\nrename the variable once, it will be renamed in all other locations that reference it.\r\nWe will see this in action in a few more screenshots.\r\nUsing the output from x32dbg, we can begin renaming those global variables DAT_000* etc to their decoded\r\nvalues.\r\nThis will significantly improve the readability of the Ghidra code.\r\nThis process can be done manually or by saving the x32dbg output and creating a Ghidra Script. The\r\nprocess of scripting this is in Ghidra is relatively complicated and will be covered in a later post.\r\nFor now, we can edit the names manually (Right Click -\u003e Rename Global Variable)\r\nBelow we can see the same code after some slight renaming. Make sure to reference the x32dbg output.\r\nWe like to prepend each variable with str_ to indicate that it's a string. This is optional but improves\r\nthe readability of the code.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 20 of 26\n\nWith the DAT_* locations modified to their decoded values, any location within Ghidra that contains the same\r\nDAT_ value will now have a suitable name, making it much easier to infer the purpose of the function.\r\nTo determine where a variable is used, we can again use cross references. Double clicking on any of\r\nthe DAT_* values will show it's location and any available cross references where it is used.\r\nFor example, here is the function containing \"JohnDoe\" before the DAT_* values are renamed.\r\nIf we had encountered this function without first decrypting strings, it would be difficult to tell what the function\r\nis doing.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 21 of 26\n\nAfter marking up the DAT_* values with more appropriate names, the function looks like this.\r\nSince we googled these values and determined they are used for Defender Emulation checks, we can infer that this\r\nis (most likely) the purpose of the function.\r\nUsing that assumption, we can change the name to something more useful.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 22 of 26\n\nNow, anywhere where that function is called will be much more understandable.\r\nTo see where a function is called, we can double click it and view the x-refs again to see where the\r\nfunction is used.\r\nHere is one such reference, which doesn't make much sense at an initial glance.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 23 of 26\n\nAfter renaming the function to mw_checkDefenderEmulation , it begins to make more sense.\r\nAfter renaming all remaining DAT_* variables, it begins to make even more sense.\r\nThe malware is temporarily going to sleep and repeatedly checking for signs of Defender Emulation.\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 24 of 26\n\nA similar concept can be seen with the decoded string for VirtualAlloc.\r\nBelow is a function referencing VirtualAlloc, prior to renaming variables.\r\nAfter renaming, we see that its primary purpose is creating memory using VirtualAlloc.\r\n(There are some other things going on, but the primary purpose is memory allocation, hence we can\r\nrename this function to mw_AllocateWithVirtualAlloc )\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 25 of 26\n\nThis process can be repeated until all points of interest have been labelled with appropriate values.\r\nThis is time-consuming if you wish to mark up an entire file, but it is effective and will reveal a significant portion\r\nof the file's previously hidden functionality.\r\nOnce you're comfortable with performing this process manually, you can eventually create a script to do the same\r\nthing for you.\r\nCreating a script will still require obtaining the decrypted strings through some means, but renaming everything\r\ncan be done well with a Ghidra script.\r\nConclusion\r\nWe have now looked at how to identify basic obfuscated strings, decrypt them, and fix their values within Ghidra.\r\nAlthough this is a relatively simple example, the same overall process and workflows are repeatable across many,\r\nmany malware samples.\r\nAs you become more confident, many of these steps can be automated further or scripted. The renaming process\r\ncan be replaced with a Ghidra script, and the \"debugger\" process can be replaced with scripted Emulation\r\n(Unicorn, Dumpulator etc).\r\nRegardless, this blog demonstrates some core skills that are important for building the baseline skills to begin\r\nexploring future automation.\r\nSign up for Embee Research\r\nMalware Analysis and Threat Intelligence Research\r\nNo spam. Unsubscribe anytime.\r\nSource: https://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nhttps://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/\r\nPage 26 of 26\n\n  https://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/    \nWe can also observe that the malware attempts to steal credit card information from web browsers.\n,      \nIf we go back to Ghidra, we can revisit the initial function containing references to encrypted strings.\n    Page 18 of 26",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://embee-research.ghost.io/ghidra-basics-identifying-and-decoding-encrypted-strings/"
	],
	"report_names": [
		"ghidra-basics-identifying-and-decoding-encrypted-strings"
	],
	"threat_actors": [],
	"ts_created_at": 1775434057,
	"ts_updated_at": 1775826762,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/1ec973bb1567ba0320c64057a38c350e9b46137b.pdf",
		"text": "https://archive.orkl.eu/1ec973bb1567ba0320c64057a38c350e9b46137b.txt",
		"img": "https://archive.orkl.eu/1ec973bb1567ba0320c64057a38c350e9b46137b.jpg"
	}
}