////////////////////////////////////////////////////////////////////// // // Module : WBFILED // Filename : CreateFile.CPP for (CreateFile.dll / WbFileD.DLL) // Copyright: 2004, UMBRELLA SECURITY RESEARCH // http://umbrella.name/ // Creator : Liu Die Yu = liudieyu at umbrella dot name // Updated : 2004/03/31 // // UMBRELLA WINBLOX PROJECT // http://umbrella.name/winblox // /* ***** ***** CHECK LIST ***** ***** Write-To-Memory Functions Used in this file [*] strcat MUST CHECK BUFFER SIZE before EXECUTING [*] _snprintf USAGE: ----------- CHAR szStatus[32] = ""; [...] _snprintf( szStatus, 32,"Child Status=%d.%d",ExecUrlStatus.uHttpStatusCode,ExecUrlStatus.uHttpSubStatus ); ----------- article address: http://msdn.microsoft.com/library/en-us/iissdk/iis/intercepting_all_incoming_iis_requests.asp) STYLE: int _snprintf( char *buffer, size_t count, const char *format [, argument] ... ); RETURN VALUE ----------- snprintf returns the number of bytes stored in buffer, not counting the terminating null character. If the number of bytes required to store the data exceeds count, then count bytes of data are stored in buffer and a negative value is returned. ----------- article address: http://msdn.microsoft.com/library/en-us/vccore98/HTML/_crt__snprintf.2c_._snwprintf.asp?frame=true MUST CHECK IF ALL CONTENT IS WRITTEN. [*] strncpy *strncpy( char *strDest, const char *strSource, size_t count ); MUST CHECK IF ALL CONTENT IS WRITTEN. [*] fgets char *fgets( char *string,int n,FILE *stream ); string : Storage location for data. n : Maximum number of characters to read. stream : Pointer to FILE structure. MUST CHECK IF ALL CONTENT IS READ [*] MultiByteToWideChar int MultiByteToWideChar( UINT CodePage, // code page DWORD dwFlags, // character-type options LPCSTR lpMultiByteStr, // string to map int cbMultiByte, // number of bytes in string - If this parameter is -1, the function processes the entire input string including the null terminator. LPWSTR lpWideCharStr, // wide-character buffer int cchWideChar // size of buffer : Specifies the size, in *wide characters*, NOT in bytes ); If the function fails, the return value is zero. article address: http://msdn.microsoft.com/library/en-us/intl/unicode_17si.asp?frame=true [*] WideCharToMultiByte int WideCharToMultiByte( UINT CodePage, // code page DWORD dwFlags, // performance and mapping flags LPCWSTR lpWideCharStr, // wide-character string int cchWideChar, // number of chars in string. If this value is -1, the string is assumed to be null-terminated and the length is calculated automatically. LPSTR lpMultiByteStr, // buffer for new string int cbMultiByte, // size of buffer : Specifies the size, in bytes LPCSTR lpDefaultChar, // default for unmappable chars LPBOOL lpUsedDefaultChar // set when default char used ); article address: http://msdn.microsoft.com/library/en-us/intl/unicode_2bj9.asp?frame=true [*] Other Windows API functions(GetModuleFileNameEx, etc) [*] Integer Overflow Checking: */ #include #include #include #include #include #include #include "detours.h" //==================================================================== //[START] Define //==================================================================== #define ACCESSDESCRIPTION_MAXNUM 21 // ACCESSDESCRIPTION: text description of dwDesiredAccess in My_CreateFileW #define ACCESSDESCRIPTION_STRMAXLEN 100 // ACCESSDESCRIPTION: text description of dwDesiredAccess in My_CreateFileW // [1] ACCESSDESCRIPTION_STRMAXLEN the the maxlen of not-complicated ACCESSDESCRIPTION // [2] must include the space for pipe("|") // [3] so, the buffer for a complicated ACCESSDESCRIPTION should be larger than // ACCESSDESCRIPTION_MAXNUM*ACCESSDESCRIPTION_STRMAXLEN #define FILENAME_STRMAXLEN 2048 //FILENAME: the name of a file, including directory #define PATTERN_STRMAXLEN 1024*4 //PATTERN: regular expression patterns stored in WBLIST.TXT #define PATTERN_MAXNUM 30 //PATTERN: regular expression patterns stored in WBLIST.TXT #define PATTERN_FILEID "WBLIST.TXT" //PATTERN: regular expression patterns stored in WBLIST.TXT #define DESCRIPTORVAR_STRMAXLEN 2048 //DESCRIPTOR: a string describing current operation, also known as "entry". //DESCRIPTORVAR: the variable part of DESCRIPTOR #define FILTERMODE_FLAG "FilterMode" //FILTERMODE_FLAG: if the first line of WBCONFIG.TXT does not contain "FilterMode(not case sensitive), it will be record mode. #define ERROR_CODE_FOR_BLACKLISTED_ACTION ERROR_PATH_NOT_FOUND //ERROR_CODE_FOR_BLACKLISTED_ACTION will be returned if: winblox is working in FILTER mode and current operation matches one of patterns listed in WBLIST.TXT #define OVECCOUNT 30 //OVECCOUNT: used in PCRE(Perl Compatible Regular Expression) in IsRegExpMatch :: should be a multiple of 3 #define LOGTEXTVAR_STRMAXLEN 2048 //LOGTEXT: one line in the log file(%WINDIR%\WBLOG.TXT ) //LOGTEXTVAR: the input string of RecordLog function #define LOG_FILEID "WBLOG.TXT" //LOG: the log file(%WINDIR%\WBLOG.TXT) #define CONFIG_FILEID "WBCONFIG.TXT" //CONFIG: the config file(%WINDIR%\WBCONFIG.TXT) //==================================================================== //[ENDOF] Define //==================================================================== #pragma warning(disable:4100) // Trampolines don't use formal parameters. //==================================================================== //[START] Global Variables //==================================================================== BOOL g_bConfigFileFailure = FALSE; //g_bConfigFileFailure: failed to read config file? char g_sMainModuleFileName[FILENAME_STRMAXLEN] = ""; //g_sMainModuleFileName: file name of main module(often EXE files); path is included. BOOL g_bRecordMode = FALSE; //g_bRecordMode: ConfigLoader will change it to TRUE if RecordMode is specified in config file. otherwise, it's FilterMode char g_ListedPatterns[PATTERN_MAXNUM][PATTERN_STRMAXLEN]; //g_ListedPatterns: store patterns(=descriptor) listed in "WBLIST.TXT" unsigned int g_NumberOfPatterns=0; //g_NumberOfPatterns: how many patterns were loaded from PATTERN file? FILE * g_fpRecordFile = NULL; // g_NumberOfPatterns: pointer pointing to FILE structure static BOOL bInternal = TRUE; //declared for DETOURS static HINSTANCE g_hInstance = NULL; //declared for DETOURS //==================================================================== //[ENDOF] Global Variables //==================================================================== DETOUR_TRAMPOLINE( HANDLE WINAPI Real_CreateFileW( LPWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile), CreateFileW); //==================================================================== //[START] StripOutEndingNewLineChar //==================================================================== char * StripOutEndingNewLineChar(char * buff) { if(strlen(buff) == 0) return buff; //if ending with 0x0a if(buff[strlen(buff)-1] == 0x0a) buff[strlen(buff)-1] = 0; //now, ending 0x0a was stripped out, but if it still ends with 0x0d... if(buff[strlen(buff)-1] == 0x0d) buff[strlen(buff)-1] = 0; return buff; } //==================================================================== //[ENDOF] StripOutEndingNewLineChar //==================================================================== //==================================================================== //[START] GetFullFileName //==================================================================== char * GetFullFileName(char * sFileId,char *sOutputBuff, size_t nSize) { char sWinDir[FILENAME_STRMAXLEN] = ""; UINT uRet = GetWindowsDirectoryA(sWinDir,FILENAME_STRMAXLEN); // If the length is greater than the size of the buffer, the return value is the size of the buffer required to hold the path. // If the function fails, the return value is zero. To get extended error information, call GetLastError if((uRet == 0)||(uRet > FILENAME_STRMAXLEN)) { sOutputBuff[0] = 0; return sOutputBuff; } int nRet = _snprintf(sOutputBuff,nSize,"%s\\%s",sWinDir,sFileId); if(nRet < 0 ) //number of bytes required to store the data exceeds "nSize" { sOutputBuff[0] = 0; return sOutputBuff; } return sOutputBuff; } //==================================================================== //[ENDOF] GetFullFileName //==================================================================== //==================================================================== //[START] IsRegExpMatch //==================================================================== BOOL WINAPI IsRegExpMatch(char * pattern, char * subject) { /* the following code in this function is copied from: article file id: "pcredemo.c" article address: http://lxr.webperf.org/source.cgi/srclib/pcre/pcredemo.c */ pcre *re; const char *error; int erroffset; int ovector[OVECCOUNT]; int rc; /* Compile the regular expression*/ re = pcre_compile( pattern, // the pattern 0, // default options &error, // for error message &erroffset, // for error offset NULL); // use default character tables if (re == NULL) //if failed to compile return FALSE; rc = pcre_exec( re, // the compiled pattern NULL, // no extra data - we didn't study the pattern subject, // the subject string (int)strlen(subject), // the length of the subject 0, // start at offset 0 in the subject 0, // default options ovector, // output vector for substring information OVECCOUNT); // number of elements in the output vector if (rc < 0) { switch(rc) { case PCRE_ERROR_NOMATCH: return FALSE; break; //AVDANCED HANDLING IS NOT READY YET. default: return FALSE; break; } return FALSE; } /* Match succeded */ /* The output vector wasn't big enough */ if (rc == 0) { //AVDANCED HANDLING IS NOT READY YET. ; } return TRUE; } //==================================================================== //[ENDOF] IsRegExpMatch //==================================================================== //==================================================================== //[START] GetAccessType //==================================================================== BOOL WINAPI GetAccessType(DWORD dwDesiredAccess, char * sBuffer, unsigned int nSize) { //CAUTION: going to Write to sBuffer with "strcat". the size of sBuffer should be big enough if(nSize < ACCESSDESCRIPTION_STRMAXLEN*ACCESSDESCRIPTION_MAXNUM) { sBuffer[0] = 0; return FALSE; } DWORD KnownAccessNumber[]={ FILE_ADD_FILE, // 1 FILE_ADD_SUBDIRECTORY, // 2 FILE_ALL_ACCESS, // 3 FILE_APPEND_DATA, // 4 FILE_CREATE_PIPE_INSTANCE, // 5 FILE_DELETE_CHILD, // 6 FILE_EXECUTE, // 7 FILE_LIST_DIRECTORY, // 8 FILE_READ_ATTRIBUTES, // 9 FILE_READ_DATA, // 10 FILE_READ_EA, // 11 FILE_TRAVERSE, // 12 FILE_WRITE_ATTRIBUTES, // 13 FILE_WRITE_DATA, // 14 FILE_WRITE_EA, // 15 STANDARD_RIGHTS_READ, // 16 STANDARD_RIGHTS_WRITE, // 17 SYNCHRONIZE, // 18 GENERIC_EXECUTE, // 19 GENERIC_READ, // 20 GENERIC_WRITE, // 21 }; char * KnownAccessType[]={ "FILE_ADD_FILEID", //1 "FILE_ADD_SUBDIRECTORY", //2 "FILE_ALL_ACCESS", //3 "FILE_APPEND_DATA", //4 "FILE_CREATE_PIPE_INSTANCE", //5 "FILE_DELETE_CHILD", //6 "FILE_EXECUTE", //7 "FILE_LIST_DIRECTORY", //8 "FILE_READ_ATTRIBUTES", //9 "FILE_READ_DATA", //0 "FILE_READ_EA", //11 "FILE_TRAVERSE", //12 "FILE_WRITE_ATTRIBUTES", //13 "FILE_WRITE_DATA", //14 "FILE_WRITE_EA", //15 "STANDARD_RIGHTS_READ", //16 "STANDARD_RIGHTS_WRITE", //17 "SYNCHRONIZE", //18 "GENERIC_EXECUTE", //19 "GENERIC_READ", //20 "GENERIC_WRITE", //21 }; sBuffer[0] = 0; int i,nFoundAccess=0; for(i=0;i0) strcat(sBuffer,"|"); strcat(sBuffer,KnownAccessType[i]); nFoundAccess++; } } if(nFoundAccess==0) { sBuffer[0] = 0; _snprintf(sBuffer,nSize,"UNKNOWN_ACCESS_CODE:%08x",dwDesiredAccess); //size is already checked at the very beginning, so there is no need to check if all content is written. } return FALSE; } //==================================================================== //[ENDOF] GetAccessType //==================================================================== //==================================================================== //[START] RecordLog //==================================================================== BOOL WINAPI RecordLog(LPSTR sText) //this function works even CREATEFILE API is under interception. { HANDLE hAppend = NULL; DWORD dwPos = 0; DWORD dwDataSize = 0; DWORD dwBytesWritten = 0; char MsgBuff[LOGTEXTVAR_STRMAXLEN + 100] = ""; // "+100": this extra 100 bytes are used to store time and date char sFullFileName[FILENAME_STRMAXLEN] = ""; WCHAR wsFullFileName[FILENAME_STRMAXLEN] =L""; SYSTEMTIME SystemTime; GetLocalTime(&SystemTime); MsgBuff[0] = 0; /* the following check is added after: Oliver Lavery = olavery at pivx dot com reported to me(Liu Die Yu) and bugtraq. */ if(strlen(sText) > LOGTEXTVAR_STRMAXLEN) return FALSE; _snprintf(MsgBuff,LOGTEXTVAR_STRMAXLEN + 100,"%4d/%02d/%02d @ %02d:%02d:%02d.%03d # %s\n",SystemTime.wYear,SystemTime.wMonth,SystemTime.wDay,SystemTime.wHour,SystemTime.wMinute,SystemTime.wSecond,SystemTime.wMilliseconds,sText); //number of bytes required to store the data cannot exceed "LOGTEXTVAR_STRMAXLEN + 100", //so, there is no check whether all data is written by _snprintf dwDataSize = strlen(MsgBuff); GetFullFileName(LOG_FILEID,sFullFileName,FILENAME_STRMAXLEN); int nRet = MultiByteToWideChar( CP_ACP, // code page MB_PRECOMPOSED, // character-type options , default value is used here. sFullFileName, // string to map -1, // number of bytes in string wsFullFileName, // wide-character buffer FILENAME_STRMAXLEN // size of buffer ); //If the function fails, the return value is zero. if(nRet == 0) return FALSE; hAppend = Real_CreateFileW(wsFullFileName, // open GENERIC_WRITE, // open for writing FILE_SHARE_READ|FILE_SHARE_WRITE, // allow multiple readers NULL, // no security OPEN_ALWAYS, // open or create FILE_ATTRIBUTE_NORMAL, // normal file NULL); // no attr. template if (hAppend == INVALID_HANDLE_VALUE) hAppend = Real_CreateFileW(wsFullFileName, // open GENERIC_WRITE, // open for writing FILE_SHARE_READ|FILE_SHARE_WRITE, // allow multiple readers NULL, // no security CREATE_NEW, // open or create : CREATE_NEW - Creates a new file. The function fails if the specified file already exists. FILE_ATTRIBUTE_NORMAL, // normal file NULL); if (hAppend == INVALID_HANDLE_VALUE) return FALSE; dwPos = SetFilePointer(hAppend, 0, NULL, FILE_END); LockFile(hAppend, dwPos, 0, dwDataSize, 0); WriteFile(hAppend, MsgBuff, dwDataSize, &dwBytesWritten, NULL); UnlockFile(hAppend, dwPos, 0, dwDataSize, 0); CloseHandle(hAppend); return TRUE; } //==================================================================== //[ENDOF] RecordLog //==================================================================== //==================================================================== //[START] GetMainModuleFileName //==================================================================== char * GetMainModuleFileName() { if(strlen(g_sMainModuleFileName)>=1) // it's already initialized. return g_sMainModuleFileName; DWORD count; HMODULE hm[1]; HANDLE hCurrentProcess; hCurrentProcess = GetCurrentProcess(); EnumProcessModules(hCurrentProcess,hm,1,&count); DWORD dwRet = GetModuleFileNameEx( hCurrentProcess, hm[0], g_sMainModuleFileName, FILENAME_STRMAXLEN ); //If the function fails, the return value is zero if(dwRet == 0) { g_sMainModuleFileName[0] = 0; return g_sMainModuleFileName; } return g_sMainModuleFileName; } //==================================================================== //[ENDOF] GetMainModuleFileName //==================================================================== //==================================================================== //[START] ConfigLoader() //==================================================================== BOOL WINAPI ConfigLoader(void) { char sFileNameBuff[FILENAME_STRMAXLEN]; FILE * pfListFile; FILE * pfConfigFile; char sBuffer[PATTERN_STRMAXLEN] = ""; pfConfigFile = fopen(GetFullFileName(CONFIG_FILEID,sFileNameBuff,FILENAME_STRMAXLEN),"r"); if(pfConfigFile == NULL) // null pointer indicates an error { g_bConfigFileFailure = TRUE; return FALSE; } fgets(sBuffer , PATTERN_STRMAXLEN , pfConfigFile); if(strstr(_strlwr(sBuffer),_strlwr(FILTERMODE_FLAG))==NULL) //if FILTERMODE_FLAG is not found g_bRecordMode = TRUE; else g_bRecordMode = FALSE; fclose(pfConfigFile); pfListFile = fopen(GetFullFileName(PATTERN_FILEID,sFileNameBuff,FILENAME_STRMAXLEN),"r"); if( pfListFile == NULL ) { g_bConfigFileFailure = TRUE; return FALSE; } g_NumberOfPatterns=0; while( !feof(pfListFile) ) { fgets(sBuffer , PATTERN_STRMAXLEN , pfListFile); StripOutEndingNewLineChar(sBuffer); if(strlen(sBuffer)0) if(sBuffer[0]!=0x23) //0x23 = # char, so we skip all lines starting with "#" { g_NumberOfPatterns++; if(g_NumberOfPatterns > PATTERN_MAXNUM) //if the number of items exceeds our capacity to store return FALSE; else { strncpy(g_ListedPatterns[g_NumberOfPatterns-1],sBuffer,PATTERN_STRMAXLEN); //i have already verified this: (strlen(sBuffer)", "==>", etc unsigned int i=0; int nRet = 0; GetAccessType(dwDesiredAccess,lpAccessType,ACCESSDESCRIPTION_STRMAXLEN*ACCESSDESCRIPTION_MAXNUM); nRet = WideCharToMultiByte(CP_ACP, // code page 0,// performance and mapping flags lpFileName,// wide-character string -1, // number of chars in string. sFileName,// buffer for new string FILENAME_STRMAXLEN,// size of buffer NULL,// default for unmappable chars NULL // set when default char used ); //If the function fails, the return value is zero if(nRet == 0) if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) { SetLastError(ERROR_CODE_FOR_BLACKLISTED_ACTION); return INVALID_HANDLE_VALUE; } lpDescriptor[0] = 0; /* the following check is added after: Oliver Lavery = olavery at pivx dot com reported to me(Liu Die Yu) and bugtraq. */ nRet = _snprintf(lpDescriptor,DESCRIPTORVAR_STRMAXLEN +100,"CreateFile:%s > %s ==> %s --> %s",GetMainModuleFileName(),GetCommandLine(),lpAccessType,sFileName); if(nRet < 0) //number of bytes required to store the data exceeds "DESCRIPTORVAR_STRMAXLEN +100" { SetLastError(ERROR_CODE_FOR_BLACKLISTED_ACTION); return INVALID_HANDLE_VALUE; } for(i=0;i", "==>", etc unsigned int i=0; int nRet = 0; lpDescriptor[0] = 0; /* the following check is added after: Oliver Lavery = olavery at pivx dot com reported to me(Liu Die Yu) and bugtraq. */ nRet = _snprintf(lpDescriptor,DESCRIPTORVAR_STRMAXLEN +100,"CommandLine:%s > %s",GetMainModuleFileName(),GetCommandLine()); if(nRet < 0) //number of bytes required to store the data exceeds "DESCRIPTORVAR_STRMAXLEN +100" if(!g_bRecordMode) //if it's working in filter mode ExitProcess(1); for(i=0;i