NetSimK expiration date patch

February 2017

During our lab lesson of Advanced Networks, we had to use NetSimK, a nice free software developed by Cisco™ to simulate networks. However, support for this software was terminated by the time we started using it, thus becoming unusable. So I thought I might have helped my professor by modifying NetSimK behaviour at runtime: just as the expiration date message is displayed, 'OK' button event handler (which leads to the exiting of the execution) is intercepted, and a trampoline to the address function of the previous reversed stack frame is performed (since we cannot directly control instruction pointer), so that execution flow resumes from there.

Note that binary lacks of both NX (No eXecute) bit and PIE (maybe missing /dynamicbase option at link time, or more likely, it was not enabled by default on 32-bit images of old Windows versions), the latter is responsible for permitting ASLR, so memory address locations are fixed on every instance of the program (they are not slid by a random value), and we can always rely on the same base address. Here's a simple PoC.

/* 2k17 ~antoniofrighetto */

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

#define kNetSimK "NetSimK.exe"
#define kNetSimKWindowTitle "Oh oh"
#define patch "\xB8\xB1\xB6\x5D\x00" \
              "\x90\x90" \
              "\xFF\xE0" /* mov eax, 0x5DB6B1; nop; jmp eax */

void SetDebugPrivileges(BOOL bEnablePrivileges)
{
    HANDLE token;
    LUID luid;
    TOKEN_PRIVILEGES tkp;

    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) {
        if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {
            tkp.PrivilegeCount = 1;
            tkp.Privileges[0].Luid = luid;
            tkp.Privileges[0].Attributes = bEnablePrivileges ? SE_PRIVILEGE_ENABLED : 0;

            AdjustTokenPrivileges(token, false, &tkp, sizeof(tkp), NULL, NULL);
        }
        CloseHandle(token);
    }
}

int main(int argc, const char** argv)
{
    SetDebugPrivileges(1);
    DWORD procID;
    if (!argv[1]) {
        HWND window = FindWindow(NULL, _T(kNetSimKWindowTitle));
        assert((GetWindowThreadProcessId(window, &procID)) != 0);
    } else {
        procID = atoi(argv[1]);
    }
    HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, false, procID);
    if (!process) {
        fprintf(stderr, "could not get process %d.\n", procID);
        exit(1);
    }
    HMODULE module = GetModuleHandle(_T(kNetSimK));
    MODULEINFO moduleinfo;
    DWORD prot;

    if (GetModuleInformation(process, module, &moduleinfo, sizeof(MODULEINFO))) {
        void* addr = ((UINT8*)moduleinfo.EntryPoint + 0x2A9);

        BYTE buffer[9];
        assert((ReadProcessMemory(process, reinterpret_cast<void**>(addr), buffer, sizeof(buffer), NULL)) != 0);

#ifdef DEBUG
        for (int i = 0; i < sizeof(buffer); i++)
            printf("0x%.2x ", buffer[i]);
#endif
        
        VirtualProtect(addr, sizeof(buffer), PAGE_EXECUTE_READWRITE, &prot);
        memcpy(buffer, patch, sizeof(buffer));
        DWORD ret = WriteProcessMemory(process, reinterpret_cast<void**>(addr), buffer, sizeof(buffer), NULL);

        if (ret)
	    printf("successfully patched.\n");
        else
            fprintf(stderr, "could not load patch.\n");
    }
    
    CloseHandle(process);
    return 0;
}