Skip to content

Commit

Permalink
Big set of updates:
Browse files Browse the repository at this point in the history
Change default hook type to indirect jump, it has better compatibility with prologue-stealing protectors that fail to relocate instructions properly
Improve the reliability of hook installation in live processes -- freeze all other threads during hook installation
Fix a ridiculous cuckoomon bug that was preventing it from logging anything when run in the context of a SYSTEM process (or any other process with a different %TEMP% than the analyzer), visible with the Dyre banking trojan
Add hooking of CryptAcquireContext[A,W], and InternetGetConnectedState
Disable hooking of CreateWindowEx[A,W] while I try to resolve yet another cuckoomon bug
Move all runtime API resolution to a single function called at init time, since it's not safe to resolve APIs in hooked functions that may hold the loader lock
Fix types of several function prototypes specifying const 'out' pointers
Fix the pipe usage of UrlDownloadToFileW -- it was missing the length argument and thus couldn't have worked properly and would possibly just crash
Display the Flags arguments for HttpOpenRequest* and DnsQuery* as hex
Comment out the UNMAP pipe message, as it's not used at all by the analyzer
Convert some alloca usage to calloc
Preempt the installation of malware's APC injection instead of use CreateRemoteThread
Don't inject at NtOpenThread time
Notify of the SetContextThread before it happens so we can inject first, ditto for RtlCreateUserThread
Add a backtrace_disable flag to the hook info so we can do some inspection without triggering infinite recursion
If we're in debug mode, write out the logs to files instead of just reporting send errors
Add %p as a format string for pipe()
If we're not in debug mode, then make failure to read the cuckoomon config a critical error
  • Loading branch information
brad-sp committed Nov 14, 2014
1 parent 3e4f081 commit 3a533ff
Show file tree
Hide file tree
Showing 19 changed files with 456 additions and 291 deletions.
104 changes: 53 additions & 51 deletions config.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,66 +20,68 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ntapi.h"
#include "config.h"

void read_config()
int read_config(void)
{
// TODO unicode support
char buf[512], config_fname[MAX_PATH];
sprintf(config_fname, "%s\\%ld.ini",
getenv("TEMP"), GetCurrentProcessId());
sprintf(config_fname, "C:\\%u.ini", GetCurrentProcessId());

FILE *fp = fopen(config_fname, "r");
if(fp != NULL) {
while (fgets(buf, sizeof(buf), fp) != NULL) {
// cut off the newline
char *p = strchr(buf, '\r');
if(p != NULL) *p = 0;
p = strchr(buf, '\n');
if(p != NULL) *p = 0;
if (fp == NULL)
return 0;

// split key=value
p = strchr(buf, '=');
if(p != NULL) {
*p = 0;
while (fgets(buf, sizeof(buf), fp) != NULL)
{
// cut off the newline
char *p = strchr(buf, '\r');
if(p != NULL) *p = 0;
p = strchr(buf, '\n');
if(p != NULL) *p = 0;

const char *key = buf, *value = p + 1;
// split key=value
p = strchr(buf, '=');
if(p != NULL) {
*p = 0;

if(!strcmp(key, "pipe")) {
strncpy(g_config.pipe_name, value,
ARRAYSIZE(g_config.pipe_name));
}
else if(!strcmp(key, "results")) {
strncpy(g_config.results, value,
ARRAYSIZE(g_config.results));
}
else if(!strcmp(key, "analyzer")) {
strncpy(g_config.analyzer, value,
ARRAYSIZE(g_config.analyzer));
}
else if(!strcmp(key, "shutdown-mutex")) {
strncpy(g_config.shutdown_mutex, value,
ARRAYSIZE(g_config.shutdown_mutex));
}
else if(!strcmp(key, "first-process")) {
g_config.first_process = value[0] == '1';
}
else if(!strcmp(key, "startup-time")) {
g_config.startup_time = atoi(value);
}
else if(!strcmp(key, "retaddr-check")) {
g_config.retaddr_check = value[0] == '1';
}
else if(!strcmp(key, "host-ip")) {
g_config.host_ip = inet_addr(value);
}
else if(!strcmp(key, "host-port")) {
g_config.host_port = atoi(value);
}
else if(!strcmp(key, "force-sleepskip")) {
g_config.force_sleepskip = value[0] == '1';
}
const char *key = buf, *value = p + 1;

if(!strcmp(key, "pipe")) {
strncpy(g_config.pipe_name, value,
ARRAYSIZE(g_config.pipe_name));
}
else if(!strcmp(key, "results")) {
strncpy(g_config.results, value,
ARRAYSIZE(g_config.results));
}
else if(!strcmp(key, "analyzer")) {
strncpy(g_config.analyzer, value,
ARRAYSIZE(g_config.analyzer));
}
else if(!strcmp(key, "shutdown-mutex")) {
strncpy(g_config.shutdown_mutex, value,
ARRAYSIZE(g_config.shutdown_mutex));
}
else if(!strcmp(key, "first-process")) {
g_config.first_process = value[0] == '1';
}
else if(!strcmp(key, "startup-time")) {
g_config.startup_time = atoi(value);
}
else if(!strcmp(key, "retaddr-check")) {
g_config.retaddr_check = value[0] == '1';
}
else if(!strcmp(key, "host-ip")) {
g_config.host_ip = inet_addr(value);
}
else if(!strcmp(key, "host-port")) {
g_config.host_port = atoi(value);
}
else if(!strcmp(key, "force-sleepskip")) {
g_config.force_sleepskip = value[0] == '1';
}
}
fclose(fp);
DeleteFile(config_fname);
}
fclose(fp);
DeleteFile(config_fname);
return 1;
}
2 changes: 1 addition & 1 deletion config.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ struct {
unsigned short host_port;
} g_config;

void read_config();
int read_config(void);
67 changes: 52 additions & 15 deletions cuckoomon.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ static hook_t g_hooks[] = {
// Window Hooks
//

HOOK(user32, CreateWindowExA),
HOOK(user32, CreateWindowExW),
//HOOK(user32, CreateWindowExA),
//HOOK(user32, CreateWindowExW),
HOOK(user32, FindWindowA),
HOOK(user32, FindWindowW),
HOOK(user32, FindWindowExA),
Expand Down Expand Up @@ -281,6 +281,7 @@ static hook_t g_hooks[] = {
//

HOOK(urlmon, URLDownloadToFileW),
HOOK(wininet, InternetGetConnectedState),
HOOK(wininet, InternetOpenA),
HOOK(wininet, InternetOpenW),
HOOK(wininet, InternetConnectA),
Expand Down Expand Up @@ -368,6 +369,8 @@ static hook_t g_hooks[] = {
// Crypto Functions
//

HOOK(advapi32, CryptAcquireContextA),
HOOK(advapi32, CryptAcquireContextW),
HOOK(advapi32, CryptProtectData),
HOOK(advapi32, CryptUnprotectData),
HOOK(advapi32, CryptProtectMemory),
Expand All @@ -387,7 +390,7 @@ static hook_t g_hooks[] = {
// get a random hooking method, except for hook_jmp_direct
//#define HOOKTYPE randint(HOOK_NOP_JMP_DIRECT, HOOK_MOV_EAX_INDIRECT_PUSH_RETN)
// error testing with hook_jmp_direct only
#define HOOKTYPE HOOK_JMP_DIRECT
#define HOOKTYPE HOOK_JMP_INDIRECT

void set_hooks_dll(const wchar_t *library)
{
Expand All @@ -405,19 +408,48 @@ void set_hooks()
VirtualProtect(g_hooks, sizeof(g_hooks), PAGE_EXECUTE_READWRITE,
&old_protect);

hook_disable();
// before modifying any DLLs, let's first freeze all other threads in our process
// otherwise our racy modifications can cause the task to crash prematurely
// This code itself is racy as additional threads could be created while we're
// processing the list, but the risk is at least greatly reduced
PHANDLE suspended_threads = (PHANDLE)calloc(4096, sizeof(HANDLE));
DWORD num_suspended_threads = 0;
DWORD i;
HANDLE hSnapShot;
THREADENTRY32 threadInfo;
DWORD our_tid = GetCurrentThreadId();
DWORD our_pid = GetCurrentProcessId();
memset(&threadInfo, 0, sizeof(threadInfo));
threadInfo.dwSize = sizeof(threadInfo);

hook_disable();

hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
Thread32First(hSnapShot, &threadInfo);
do {
if (threadInfo.th32OwnerProcessID != our_pid || threadInfo.th32ThreadID == our_tid || num_suspended_threads >= 4096)
continue;
suspended_threads[num_suspended_threads] = OpenThread(THREAD_SUSPEND_RESUME, FALSE, threadInfo.th32ThreadID);
if (suspended_threads[num_suspended_threads]) {
SuspendThread(suspended_threads[num_suspended_threads]);
num_suspended_threads++;
}
} while (Thread32Next(hSnapShot, &threadInfo));

// now, hook each api :)
for (int i = 0; i < ARRAYSIZE(g_hooks); i++) {
if(g_hooks[i].allow_hook_recursion != FALSE) {
hook_api(&g_hooks[i], HOOKTYPE);
}
else {
hook_api(&g_hooks[i], HOOKTYPE);
}
//pipe("INFO:Hooking %z", g_hooks[i].funcname);
hook_api(&g_hooks[i], HOOKTYPE);
}

hook_enable();
for (i = 0; i < num_suspended_threads; i++) {
ResumeThread(suspended_threads[i]);
CloseHandle(suspended_threads[i]);
}

free(suspended_threads);

hook_enable();
}

#if REPORT_EXCEPTIONS
Expand Down Expand Up @@ -449,8 +481,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
unsigned int length = sizeof(pids);

if(dwReason == DLL_PROCESS_ATTACH) {
// make sure advapi32 is loaded
LoadLibrary("advapi32");
resolve_runtime_apis();

// there's a small list of processes which we don't want to inject
if(is_ignored_process()) {
Expand All @@ -472,7 +503,13 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
file_init();

// read the config settings
read_config();
if (!read_config())
#ifdef CUCKOODBG
;
#else
// if we're not debugging, then failure to read the cuckoomon config should be a critical error
return TRUE;
#endif
g_pipe_name = g_config.pipe_name;

// obtain all protected pids
Expand Down Expand Up @@ -505,7 +542,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)

// notify analyzer.py that we've loaded
char name[64];
sprintf(name, "CuckooEvent%ld", GetCurrentProcessId());
sprintf(name, "CuckooEvent%u", GetCurrentProcessId());
HANDLE event_handle = OpenEvent(EVENT_ALL_ACCESS, FALSE, name);
if(event_handle != NULL) {
SetEvent(event_handle);
Expand Down
3 changes: 3 additions & 0 deletions cuckoomon.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>true</LinkIncremental>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<RunCodeAnalysis>false</RunCodeAnalysis>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
Expand Down Expand Up @@ -73,6 +75,7 @@
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>E:\git\cuckoomon\distorm3.2-package\include;E:\git\cuckoomon\bson;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<BufferSecurityCheck>true</BufferSecurityCheck>
<EnablePREfast>false</EnablePREfast>
</ClCompile>
<Link>
<TargetMachine>MachineX86</TargetMachine>
Expand Down
24 changes: 24 additions & 0 deletions hook_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,30 @@ static char *get_alg_name(char *buf, ALG_ID AlgId)
}
}

HOOKDEF(BOOL, WINAPI, CryptAcquireContextA,
_Out_ HCRYPTPROV *phProv,
_In_ LPCSTR pszContainer,
_In_ LPCSTR pszProvider,
_In_ DWORD dwProvType,
_In_ DWORD dwFlags
) {
BOOL ret = Old_CryptAcquireContextA(phProv, pszContainer, pszProvider, dwProvType, dwFlags);
LOQ_bool("crypto", "ssp", "Container", pszContainer, "Provider", pszProvider, "Flags", dwFlags);
return ret;
}

HOOKDEF(BOOL, WINAPI, CryptAcquireContextW,
_Out_ HCRYPTPROV *phProv,
_In_ LPCWSTR pszContainer,
_In_ LPCWSTR pszProvider,
_In_ DWORD dwProvType,
_In_ DWORD dwFlags
) {
BOOL ret = Old_CryptAcquireContextW(phProv, pszContainer, pszProvider, dwProvType, dwFlags);
LOQ_bool("crypto", "uup", "Container", pszContainer, "Provider", pszProvider, "Flags", dwFlags);
return ret;
}

HOOKDEF(BOOL, WINAPI, CryptProtectData,
_In_ DATA_BLOB *pDataIn,
_In_ LPCWSTR szDataDescr,
Expand Down
9 changes: 5 additions & 4 deletions hook_misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ HOOKDEF(NTSTATUS, WINAPI, LdrLoadDll,
return ret;
}

// Called with the loader lock held
HOOKDEF(NTSTATUS, WINAPI, LdrGetDllHandle,
__in_opt PWORD pwPath,
__in_opt PVOID Unused,
Expand Down Expand Up @@ -246,7 +247,7 @@ HOOKDEF(BOOL, WINAPI, GetCursorPos,
}

HOOKDEF(BOOL, WINAPI, GetComputerNameA,
_Out_ PCTSTR lpBuffer,
_Out_ LPSTR lpBuffer,
_Inout_ LPDWORD lpnSize
) {
BOOL ret = Old_GetComputerNameA(lpBuffer, lpnSize);
Expand All @@ -255,7 +256,7 @@ HOOKDEF(BOOL, WINAPI, GetComputerNameA,
}

HOOKDEF(BOOL, WINAPI, GetComputerNameW,
_Out_ PCWSTR lpBuffer,
_Out_ LPWSTR lpBuffer,
_Inout_ LPDWORD lpnSize
) {
BOOL ret = Old_GetComputerNameW(lpBuffer, lpnSize);
Expand All @@ -264,7 +265,7 @@ HOOKDEF(BOOL, WINAPI, GetComputerNameW,
}

HOOKDEF(BOOL, WINAPI, GetUserNameA,
_Out_ PCTSTR lpBuffer,
_Out_ LPSTR lpBuffer,
_Inout_ LPDWORD lpnSize
) {
BOOL ret = Old_GetUserNameA(lpBuffer, lpnSize);
Expand All @@ -273,7 +274,7 @@ HOOKDEF(BOOL, WINAPI, GetUserNameA,
}

HOOKDEF(BOOL, WINAPI, GetUserNameW,
_Out_ PCWSTR lpBuffer,
_Out_ LPWSTR lpBuffer,
_Inout_ LPDWORD lpnSize
) {
BOOL ret = Old_GetUserNameW(lpBuffer, lpnSize);
Expand Down
Loading

0 comments on commit 3a533ff

Please sign in to comment.