// dll_injector.cpp — Generic LoadLibraryA DLL Injector
// Usage: set TARGET_PROCESS and DLL_NAME, build and run as admin.

#include <Windows.h>
#include <TlHelp32.h>
#include <iostream>

// ── Config ────────────────────────────────────────────────────────────────────
constexpr const char* TARGET_PROCESS = "target.exe";
constexpr const char* DLL_NAME       = "payload.dll";
// ─────────────────────────────────────────────────────────────────────────────

// Find PID by process name (returns 0 if not found)
static DWORD FindPID(const char* name)
{
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snap == INVALID_HANDLE_VALUE) return 0;

    PROCESSENTRY32 pe{ sizeof(pe) };
    for (BOOL ok = Process32First(snap, &pe); ok; ok = Process32Next(snap, &pe))
        {
            char buf[MAX_PATH];
            wcstombs_s(nullptr, buf, pe.szExeFile, MAX_PATH);
            if (!lstrcmpA(buf, name))
            {
                CloseHandle(snap);
                return pe.th32ProcessID;
            }
        }
    CloseHandle(snap);
    return 0;
}

// Copy DLL to %TEMP% to avoid file-lock issues, returns path in out[]
static bool CopyToTemp(const char* src, char* out)
{
    char tempDir[MAX_PATH], tempFile[MAX_PATH];
    GetTempPathA(MAX_PATH, tempDir);
    GetTempFileNameA(tempDir, "inj", 0, tempFile);
    lstrcpyA(out, tempFile);
    lstrcatA(out, ".dll");
    return CopyFileA(src, out, FALSE);
}

// Open file dialog to pick a DLL (fallback when DLL_NAME not found alongside exe)
static bool PickDll(char* out)
{
    OPENFILENAMEA ofn{};
    ofn.lStructSize = sizeof(ofn);
    ofn.lpstrFilter = "DLL Files (*.dll)\0*.dll\0All Files\0*.*\0";
    ofn.lpstrFile   = out;
    ofn.nMaxFile    = MAX_PATH;
    ofn.Flags       = OFN_FILEMUSTEXIST;
    return GetOpenFileNameA(&ofn);
}

// Core injection: WriteProcessMemory + CreateRemoteThread(LoadLibraryA)
static bool Inject(HANDLE hProc, const char* dllPath)
{
    SIZE_T len = lstrlenA(dllPath) + 1;

    void* remote = VirtualAllocEx(hProc, nullptr, len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!remote) return false;

    if (!WriteProcessMemory(hProc, remote, dllPath, len, nullptr))
    {
        VirtualFreeEx(hProc, remote, 0, MEM_RELEASE);
        return false;
    }

    FARPROC loadLib = GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
    if (!loadLib) return false;

    HANDLE hThread = CreateRemoteThread(hProc, nullptr, 0,
    reinterpret_cast<LPTHREAD_START_ROUTINE>(loadLib), remote, 0, nullptr);
    if (!hThread) return false;

    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
    // Note: remote memory is intentionally left allocated — LoadLibraryA needs the string
    //       only during the call, but freeing early can race. Free here if your DLL is
    //       guaranteed loaded by the time WaitForSingleObject returns (it is).
    VirtualFreeEx(hProc, remote, 0, MEM_RELEASE);
    return true;
}

int main()
{
    // 1. Resolve DLL path (next to exe → file dialog fallback)
    char dllPath[MAX_PATH]{};
    GetFullPathNameA(DLL_NAME, MAX_PATH, dllPath, nullptr);
    if (GetFileAttributesA(dllPath) == INVALID_FILE_ATTRIBUTES)
    {
        std::cout << "[!] " << DLL_NAME << " not found, opening file dialog...\n";
        if (!PickDll(dllPath)) { std::cout << "[-] Cancelled.\n"; return 1; }
    }

    // 2. Find target process
    DWORD pid = FindPID(TARGET_PROCESS);
    if (!pid) { std::cout << "[-] Process not found: " << TARGET_PROCESS << "\n"; return 1; }
    std::cout << "[+] PID: " << pid << "\n";

    // 3. Open process
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (!hProc) { std::cout << "[-] OpenProcess failed: " << GetLastError() << "\n"; return 1; }

    // 4. Copy DLL to temp to avoid source-file locking
    char tempDll[MAX_PATH]{};
    if (!CopyToTemp(dllPath, tempDll)) { std::cout << "[-] CopyToTemp failed.\n"; return 1; }
    std::cout << "[+] Temp DLL: " << tempDll << "\n";

    // 5. Inject
    if (!Inject(hProc, tempDll))
        std::cout << "[-] Injection failed: " << GetLastError() << "\n";
    else
        std::cout << "[+] Inject OK.\n";

    CloseHandle(hProc);
    std::cin.get();
    return 0;
}