├── CiDemoDriver ├── .gitignore ├── CiDemoDriver.sln ├── CiDemoDriver │ ├── CiDemoDriver.inf │ ├── CiDemoDriver.vcxproj │ ├── CiDemoDriver.vcxproj.filters │ ├── CiDemoDriver.vcxproj.user │ └── ImportLibs │ │ ├── x64 │ │ └── ci.lib │ │ └── x86 │ │ └── ci.lib ├── RAIIUtils.h ├── SignatureCheck.cpp ├── SignatureCheck.h ├── ci.h └── main.cpp ├── ExecutablesForTesting └── notepad++.exe ├── GeneratingLibFiles ├── CiStubs.cpp ├── README.md ├── ci.def └── main.cpp ├── LICENSE.txt └── README.md /CiDemoDriver/.gitignore: -------------------------------------------------------------------------------- 1 | /CiDemoDriver/Debug/* 2 | /CiDemoDriver/Release/* 3 | /CiDemoDriver/x64/* 4 | /Debug/* 5 | /Release/* 6 | /x64/* 7 | /.vs/* 8 | -------------------------------------------------------------------------------- /CiDemoDriver/CiDemoDriver.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29215.179 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CiDemoDriver", "CiDemoDriver\CiDemoDriver.vcxproj", "{D5187004-259E-4B49-86B6-20488447F3B2}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|ARM = Debug|ARM 11 | Debug|ARM64 = Debug|ARM64 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|ARM = Release|ARM 15 | Release|ARM64 = Release|ARM64 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {D5187004-259E-4B49-86B6-20488447F3B2}.Debug|ARM.ActiveCfg = Debug|ARM 21 | {D5187004-259E-4B49-86B6-20488447F3B2}.Debug|ARM.Build.0 = Debug|ARM 22 | {D5187004-259E-4B49-86B6-20488447F3B2}.Debug|ARM.Deploy.0 = Debug|ARM 23 | {D5187004-259E-4B49-86B6-20488447F3B2}.Debug|ARM64.ActiveCfg = Debug|ARM64 24 | {D5187004-259E-4B49-86B6-20488447F3B2}.Debug|ARM64.Build.0 = Debug|ARM64 25 | {D5187004-259E-4B49-86B6-20488447F3B2}.Debug|ARM64.Deploy.0 = Debug|ARM64 26 | {D5187004-259E-4B49-86B6-20488447F3B2}.Debug|x64.ActiveCfg = Debug|x64 27 | {D5187004-259E-4B49-86B6-20488447F3B2}.Debug|x64.Build.0 = Debug|x64 28 | {D5187004-259E-4B49-86B6-20488447F3B2}.Debug|x64.Deploy.0 = Debug|x64 29 | {D5187004-259E-4B49-86B6-20488447F3B2}.Debug|x86.ActiveCfg = Debug|Win32 30 | {D5187004-259E-4B49-86B6-20488447F3B2}.Debug|x86.Build.0 = Debug|Win32 31 | {D5187004-259E-4B49-86B6-20488447F3B2}.Debug|x86.Deploy.0 = Debug|Win32 32 | {D5187004-259E-4B49-86B6-20488447F3B2}.Release|ARM.ActiveCfg = Release|ARM 33 | {D5187004-259E-4B49-86B6-20488447F3B2}.Release|ARM.Build.0 = Release|ARM 34 | {D5187004-259E-4B49-86B6-20488447F3B2}.Release|ARM.Deploy.0 = Release|ARM 35 | {D5187004-259E-4B49-86B6-20488447F3B2}.Release|ARM64.ActiveCfg = Release|ARM64 36 | {D5187004-259E-4B49-86B6-20488447F3B2}.Release|ARM64.Build.0 = Release|ARM64 37 | {D5187004-259E-4B49-86B6-20488447F3B2}.Release|ARM64.Deploy.0 = Release|ARM64 38 | {D5187004-259E-4B49-86B6-20488447F3B2}.Release|x64.ActiveCfg = Release|x64 39 | {D5187004-259E-4B49-86B6-20488447F3B2}.Release|x64.Build.0 = Release|x64 40 | {D5187004-259E-4B49-86B6-20488447F3B2}.Release|x64.Deploy.0 = Release|x64 41 | {D5187004-259E-4B49-86B6-20488447F3B2}.Release|x86.ActiveCfg = Release|Win32 42 | {D5187004-259E-4B49-86B6-20488447F3B2}.Release|x86.Build.0 = Release|Win32 43 | {D5187004-259E-4B49-86B6-20488447F3B2}.Release|x86.Deploy.0 = Release|Win32 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {A45D8F27-7DDF-4BE8-93F8-85AB8EE80C61} 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /CiDemoDriver/CiDemoDriver/CiDemoDriver.inf: -------------------------------------------------------------------------------- 1 | ; 2 | ; CiDemoDriver.inf 3 | ; 4 | 5 | [Version] 6 | Signature="$WINDOWS NT$" 7 | Class=System 8 | ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318} 9 | Provider=%ManufacturerName% 10 | DriverVer= 11 | CatalogFile=CiDemoDriver.cat 12 | 13 | [DestinationDirs] 14 | DefaultDestDir = %DIRID_DRIVERS% 15 | CiDemoDriver.DriverFiles = %DIRID_DRIVERS% ;%windir%\system32\drivers 16 | 17 | [DefaultInstall] 18 | OptionDesc = %ServiceDescription% 19 | CopyFiles = CiDemoDriver.DriverFiles 20 | 21 | [DefaultInstall.Services] 22 | AddService = %ServiceName%,,CiDemoDriver.Service 23 | 24 | ;; 25 | ;; Default uninstall sections 26 | ;; 27 | 28 | [DefaultUninstall] 29 | DelFiles = CiDemoDriver.DriverFiles 30 | 31 | [DefaultUninstall.Services] 32 | DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting 33 | 34 | ; 35 | ; Services Section 36 | ; 37 | 38 | [CiDemoDriver.Service] 39 | DisplayName = %ServiceName% 40 | Description = %ServiceDescription% 41 | ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ 42 | ServiceType = %SERVICE_KERNEL_DRIVER% 43 | StartType = %SERVICE_DEMAND_START% 44 | ErrorControl = %SERVICE_ERROR_NORMAL% 45 | 46 | ; 47 | ; Copy Files 48 | ; 49 | 50 | [CiDemoDriver.DriverFiles] 51 | CiDemoDriver.sys 52 | 53 | [CiDemoDriver.UserFiles] 54 | ; No user files 55 | 56 | [SourceDisksFiles] 57 | CiDemoDriver.sys = 1,, 58 | 59 | [SourceDisksNames] 60 | 1 = %DiskName%,,,"" 61 | 62 | [Standard.NT$ARCH$] 63 | 64 | [Strings] 65 | DIRID_DRIVERS = 12 66 | SERVICE_FILE_SYSTEM_DRIVER = 2 67 | SERVICE_DEMAND_START = 3 68 | SERVICE_ERROR_NORMAL = 1 69 | SERVICE_KERNEL_DRIVER = 1 70 | ServiceDescription = "CiDemoDriver" 71 | ServiceName = "CiDemoDriver" 72 | DriverName = "CiDemoDriver" 73 | ManufacturerName="Ido Moshe" 74 | ClassName="" 75 | DiskName="CiDemoDriver Source Disk" 76 | -------------------------------------------------------------------------------- /CiDemoDriver/CiDemoDriver/CiDemoDriver.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | Debug 22 | ARM 23 | 24 | 25 | Release 26 | ARM 27 | 28 | 29 | Debug 30 | ARM64 31 | 32 | 33 | Release 34 | ARM64 35 | 36 | 37 | 38 | {D5187004-259E-4B49-86B6-20488447F3B2} 39 | {dd38f7fc-d7bd-488b-9242-7d8754cde80d} 40 | v4.5 41 | 12.0 42 | Debug 43 | Win32 44 | CiDemoDriver 45 | 46 | 47 | 48 | Windows10 49 | true 50 | WindowsKernelModeDriver10.0 51 | Driver 52 | WDM 53 | 54 | 55 | Windows10 56 | false 57 | WindowsKernelModeDriver10.0 58 | Driver 59 | WDM 60 | 61 | 62 | Windows10 63 | true 64 | WindowsKernelModeDriver10.0 65 | Driver 66 | WDM 67 | 68 | 69 | Windows10 70 | false 71 | WindowsKernelModeDriver10.0 72 | Driver 73 | WDM 74 | 75 | 76 | Windows10 77 | true 78 | WindowsKernelModeDriver10.0 79 | Driver 80 | WDM 81 | 82 | 83 | Windows10 84 | false 85 | WindowsKernelModeDriver10.0 86 | Driver 87 | WDM 88 | 89 | 90 | Windows10 91 | true 92 | WindowsKernelModeDriver10.0 93 | Driver 94 | WDM 95 | 96 | 97 | Windows10 98 | false 99 | WindowsKernelModeDriver10.0 100 | Driver 101 | WDM 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | DbgengKernelDebugger 113 | true 114 | 115 | 116 | DbgengKernelDebugger 117 | true 118 | 119 | 120 | DbgengKernelDebugger 121 | true 122 | 123 | 124 | DbgengKernelDebugger 125 | true 126 | 127 | 128 | DbgengKernelDebugger 129 | 130 | 131 | DbgengKernelDebugger 132 | 133 | 134 | DbgengKernelDebugger 135 | 136 | 137 | DbgengKernelDebugger 138 | 139 | 140 | 141 | /integritycheck %(AdditionalOptions) 142 | ImportLibs\x86\ci.lib;%(AdditionalDependencies) 143 | 144 | 145 | 146 | 147 | /integritycheck %(AdditionalOptions) 148 | ImportLibs\x64\ci.lib;%(AdditionalDependencies) 149 | 150 | 151 | 152 | 153 | /integritycheck %(AdditionalOptions) 154 | ImportLibs\x64\ci.lib;%(AdditionalDependencies) 155 | 156 | 157 | 158 | 159 | /integritycheck %(AdditionalOptions) 160 | ImportLibs\x86\ci.lib;%(AdditionalDependencies) 161 | false 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /CiDemoDriver/CiDemoDriver/CiDemoDriver.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {8E41214B-6785-4CFE-B992-037D68949A14} 18 | inf;inv;inx;mof;mc; 19 | 20 | 21 | 22 | 23 | Driver Files 24 | 25 | 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | 38 | 39 | Source Files 40 | 41 | 42 | Source Files 43 | 44 | 45 | -------------------------------------------------------------------------------- /CiDemoDriver/CiDemoDriver/CiDemoDriver.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /CiDemoDriver/CiDemoDriver/ImportLibs/x64/ci.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ido-Moshe-Github/CiDllDemo/419063dbbc5454cf267496ad9141982b42e6a34c/CiDemoDriver/CiDemoDriver/ImportLibs/x64/ci.lib -------------------------------------------------------------------------------- /CiDemoDriver/CiDemoDriver/ImportLibs/x86/ci.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ido-Moshe-Github/CiDllDemo/419063dbbc5454cf267496ad9141982b42e6a34c/CiDemoDriver/CiDemoDriver/ImportLibs/x86/ci.lib -------------------------------------------------------------------------------- /CiDemoDriver/RAIIUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "ci.h" 6 | 7 | 8 | /** 9 | * create a file handle for read. 10 | * release handle when exiting the current context. 11 | */ 12 | class FileReadHandleGuard 13 | { 14 | public: 15 | FileReadHandleGuard(PCUNICODE_STRING imageFileName): _handle(nullptr), _isValid(false) 16 | { 17 | IO_STATUS_BLOCK ioStatusBlock = { 0 }; 18 | OBJECT_ATTRIBUTES objAttr = { 0 }; 19 | InitializeObjectAttributes( 20 | &objAttr, 21 | const_cast(imageFileName), 22 | OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 23 | nullptr, 24 | nullptr); 25 | 26 | const NTSTATUS openFileRet = ZwOpenFile( 27 | &_handle, 28 | SYNCHRONIZE | FILE_READ_DATA, // ACCESS_MASK, we use SYNCHRONIZE because we might need to wait on the handle in order to wait for the file to be read 29 | &objAttr, 30 | &ioStatusBlock, 31 | FILE_SHARE_READ, 32 | FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT // FILE_SYNCHRONOUS_IO_NONALERT so that zwReadfile will pend for us until reading is done 33 | ); 34 | 35 | if (!NT_SUCCESS(openFileRet)) 36 | { 37 | KdPrint(("failed to open file - openFileRet = %d\n", openFileRet)); 38 | return; 39 | } 40 | 41 | if (ioStatusBlock.Status != STATUS_SUCCESS || _handle == nullptr) 42 | { 43 | KdPrint(("ioStatusBlock.Status != STATUS_SUCCESS, or _handle is null\n")); 44 | return; 45 | } 46 | 47 | _isValid = true; 48 | } 49 | 50 | ~FileReadHandleGuard() 51 | { 52 | if (_handle != nullptr) 53 | { 54 | ZwClose(_handle); 55 | } 56 | } 57 | 58 | HANDLE& get() { return _handle; } 59 | bool isValid() const { return _isValid; } 60 | 61 | private: 62 | HANDLE _handle; 63 | bool _isValid; 64 | }; 65 | 66 | 67 | /** 68 | * create a section handle. 69 | * release handle when exiting the current context. 70 | */ 71 | class SectionHandleGuard 72 | { 73 | public: 74 | SectionHandleGuard(HANDLE& fileHandle) : _handle(nullptr), _isValid(false) 75 | { 76 | OBJECT_ATTRIBUTES objectAttributes = { 0 }; 77 | InitializeObjectAttributes( 78 | &objectAttributes, 79 | nullptr, 80 | OBJ_KERNEL_HANDLE, // to make sure user mode cannot access this handle 81 | nullptr, 82 | nullptr); 83 | 84 | const NTSTATUS createSectionRet = ZwCreateSection( 85 | &_handle, 86 | SECTION_MAP_READ, 87 | &objectAttributes, 88 | nullptr, // maximum size - use the file size, in order to map the entire file 89 | PAGE_READONLY, 90 | SEC_COMMIT, // map as commit and not as SEC_IMAGE, because SEC_IMAGE will not map things which are not needed for the PE - such as resources and certificates 91 | fileHandle 92 | ); 93 | 94 | if (!NT_SUCCESS(createSectionRet)) 95 | { 96 | KdPrint(("failed to create section - ZwCreateSection returned %x\n", createSectionRet)); 97 | return; 98 | } 99 | 100 | _isValid = true; 101 | } 102 | 103 | ~SectionHandleGuard() 104 | { 105 | if (_handle != nullptr) 106 | { 107 | ZwClose(_handle); 108 | } 109 | } 110 | 111 | HANDLE& get() { return _handle; } 112 | bool isValid() const { return _isValid; } 113 | 114 | private: 115 | HANDLE _handle; 116 | bool _isValid; 117 | }; 118 | 119 | 120 | /** 121 | * retrieve a section object from a section handle. 122 | * release object reference when exiting the current context. 123 | */ 124 | class SectionObjectGuard 125 | { 126 | public: 127 | SectionObjectGuard(HANDLE& sectionHandle) : _object(nullptr), _isValid(false) 128 | { 129 | const NTSTATUS ret = ObReferenceObjectByHandle( 130 | sectionHandle, 131 | SECTION_MAP_READ, 132 | nullptr, 133 | KernelMode, 134 | &_object, 135 | nullptr 136 | ); 137 | 138 | if (!NT_SUCCESS(ret)) 139 | { 140 | KdPrint(("ObReferenceObjectByHandle failed - returned %x\n", ret)); 141 | return; 142 | } 143 | 144 | _isValid = true; 145 | } 146 | 147 | ~SectionObjectGuard() 148 | { 149 | if (_object != nullptr) 150 | { 151 | ObfDereferenceObject(_object); 152 | } 153 | } 154 | 155 | PVOID& get() { return _object; } 156 | bool isValid() const { return _isValid; } 157 | 158 | private: 159 | PVOID _object; 160 | bool _isValid; 161 | }; 162 | 163 | 164 | /** 165 | * create a view of file. 166 | * unmap the view when exiting the current context. 167 | */ 168 | class SectionViewGuard 169 | { 170 | public: 171 | SectionViewGuard(PVOID sectionObject) : _baseAddrOfView(nullptr), _viewSize(0), _isValid(false) 172 | { 173 | const NTSTATUS ret = MmMapViewInSystemSpace( 174 | sectionObject, 175 | &_baseAddrOfView, 176 | &_viewSize 177 | ); 178 | 179 | if (!NT_SUCCESS(ret)) 180 | { 181 | KdPrint(("MmMapViewInSystemSpace failed - returned %x\n", ret)); 182 | return; 183 | } 184 | 185 | _isValid = true; 186 | } 187 | 188 | ~SectionViewGuard() 189 | { 190 | if (_baseAddrOfView != nullptr) 191 | { 192 | MmUnmapViewInSystemSpace(_baseAddrOfView); 193 | } 194 | } 195 | 196 | PVOID getViewBaseAddress() const { return _baseAddrOfView; } 197 | SIZE_T getViewSize() const { return _viewSize; } 198 | bool isValid() const { return _isValid; } 199 | 200 | private: 201 | PVOID _baseAddrOfView; 202 | SIZE_T _viewSize; 203 | bool _isValid; 204 | }; 205 | 206 | 207 | /** 208 | * create a PoicyInfo struct. 209 | * Release the memory used by the struct when exiting the current context. 210 | */ 211 | class PolicyInfoGuard 212 | { 213 | public: 214 | PolicyInfoGuard() : _policyInfo{} {} 215 | 216 | ~PolicyInfoGuard() 217 | { 218 | // CiFreePolicyInfo checks internally if there's memory to free 219 | CiFreePolicyInfo(&_policyInfo); 220 | } 221 | 222 | PolicyInfo& get() { return _policyInfo; } 223 | 224 | private: 225 | PolicyInfo _policyInfo; 226 | }; -------------------------------------------------------------------------------- /CiDemoDriver/SignatureCheck.cpp: -------------------------------------------------------------------------------- 1 | #include "RAIIUtils.h" 2 | #include "SignatureCheck.h" 3 | #include "ci.h" 4 | 5 | #define SHA1_IDENTIFIER 0x8004 6 | #define SHA256_IDENTIFIER 0x800C 7 | #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 8 | 9 | 10 | extern "C" PVOID RtlImageDirectoryEntryToData(PVOID BaseAddress, BOOLEAN MappedAsImage, USHORT Directory, PULONG Size); 11 | bool inRange(const BYTE* rangeStartAddr, const BYTE* rangeEndAddr, const BYTE* addrToCheck); 12 | void parsePolicyInfo(const pPolicyInfo policyInfo); 13 | bool ciCheckSignedFileWrapper(const LPWIN_CERTIFICATE win_cert, ULONG sizeOfSecurityDirectory); 14 | 15 | 16 | void validateFileUsingCiCheckSignedFile(PCUNICODE_STRING imageFileName) 17 | { 18 | KdPrint(("Validating file using CiCheckSignedFile...\n")); 19 | 20 | FileReadHandleGuard fileHandleGuard(imageFileName); 21 | if (!fileHandleGuard.isValid()) return; 22 | 23 | // create section for the file 24 | SectionHandleGuard sectionHandleGuard(fileHandleGuard.get()); 25 | if (!sectionHandleGuard.isValid()) return; 26 | 27 | // get section object from section handle 28 | SectionObjectGuard sectionObjectGuard(sectionHandleGuard.get()); 29 | if (!sectionObjectGuard.isValid()) return; 30 | 31 | // map a view of the section 32 | SectionViewGuard viewGuard(sectionObjectGuard.get()); 33 | if (!viewGuard.isValid()) return; 34 | 35 | // fetch the security directory 36 | PVOID securityDirectoryEntry = nullptr; 37 | ULONG securityDirectoryEntrySize = 0; 38 | securityDirectoryEntry = RtlImageDirectoryEntryToData( 39 | viewGuard.getViewBaseAddress(), 40 | TRUE, // we tell RtlImageDirectoryEntryToData it's mapped as image because then it will treat the RVA as offset from the beginning of the view, which is what we want. See https://doxygen.reactos.org/dc/d30/dll_2win32_2dbghelp_2compat_8c_source.html#l00102 41 | IMAGE_DIRECTORY_ENTRY_SECURITY, 42 | &securityDirectoryEntrySize 43 | ); 44 | 45 | if (securityDirectoryEntry == nullptr) 46 | { 47 | KdPrint(("no security directory\n")); 48 | return; 49 | } 50 | 51 | KdPrint(("securityDirectoryEntry found at: %p, size: %x\n", 52 | securityDirectoryEntry, securityDirectoryEntrySize)); 53 | 54 | // Make sure the security directory is contained in the file view 55 | const BYTE* endOfFileAddr = static_cast(viewGuard.getViewBaseAddress()) + viewGuard.getViewSize(); 56 | const BYTE* endOfSecurityDir = static_cast(securityDirectoryEntry) + securityDirectoryEntrySize; 57 | if (endOfSecurityDir > endOfFileAddr || securityDirectoryEntry < viewGuard.getViewBaseAddress()) 58 | { 59 | KdPrint(("security directory is not contained in file view!\n")); 60 | return; 61 | } 62 | 63 | // technically, there can be several WIN_CERTIFICATE in a file. This not common, and, for simplicity, 64 | // we'll assume there's only one 65 | LPWIN_CERTIFICATE winCert = static_cast(securityDirectoryEntry); 66 | KdPrint(("WIN_CERTIFICATE at: %p, revision = %x, type = %x, length = %xd, bCertificate = %p\n", 67 | securityDirectoryEntry, winCert->wRevision, winCert->wCertificateType, winCert->dwLength, static_cast(winCert->bCertificate))); 68 | 69 | ciCheckSignedFileWrapper(winCert, securityDirectoryEntrySize); 70 | } 71 | 72 | 73 | bool ciCheckSignedFileWrapper(const LPWIN_CERTIFICATE win_cert, ULONG sizeOfSecurityDirectory) 74 | { 75 | // prepare the parameters required for calling CiCheckSignedFile 76 | PolicyInfoGuard signerPolicyInfo; 77 | PolicyInfoGuard timestampingAuthorityPolicyInfo; 78 | LARGE_INTEGER signingTime = {}; 79 | const int digestSize = 20; // sha1 len, 0x14 80 | const int digestIdentifier = 0x8004; // sha1 81 | const BYTE digestBuffer[] = // digest of notepad++.exe 82 | { 0x83, 0xF6, 0x68, 0x3E, 0x64, 0x9C, 0x70, 0xB9, 0x8D, 0x0B, 83 | 0x5A, 0x8D, 0xBF, 0x9B, 0xD4, 0x70, 0xE6, 0x05, 0xE6, 0xA7 }; 84 | 85 | // CiCheckSignedFile() allocates memory from the paged pool, so make sure we're at IRQL < 2, 86 | // where access to paged memory is allowed 87 | NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); 88 | 89 | const NTSTATUS status = CiCheckSignedFile( 90 | (PVOID)digestBuffer, 91 | digestSize, 92 | digestIdentifier, 93 | win_cert, 94 | (int)sizeOfSecurityDirectory, 95 | &signerPolicyInfo.get(), 96 | &signingTime, 97 | ×tampingAuthorityPolicyInfo.get()); 98 | KdPrint(("CiCheckSignedFile returned 0x%08X\n", status)); 99 | 100 | if (NT_SUCCESS(status)) 101 | { 102 | parsePolicyInfo(&signerPolicyInfo.get()); 103 | return true; 104 | } 105 | 106 | return false; 107 | } 108 | 109 | void validateFileUsingCiValidateFileObject(PFILE_OBJECT fileObject) 110 | { 111 | KdPrint(("Validating file using CiValidateFileObject...\n")); 112 | NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); 113 | 114 | PolicyInfoGuard signerPolicyInfo; 115 | PolicyInfoGuard timestampingAuthorityPolicyInfo; 116 | LARGE_INTEGER signingTime = {}; 117 | int digestSize = 64; 118 | int digestIdentifier = 0; 119 | BYTE digestBuffer[64] = {}; 120 | 121 | const NTSTATUS status = CiValidateFileObject( 122 | fileObject, 123 | 0, 124 | 0, 125 | &signerPolicyInfo.get(), 126 | ×tampingAuthorityPolicyInfo.get(), 127 | &signingTime, 128 | digestBuffer, 129 | &digestSize, 130 | &digestIdentifier 131 | ); 132 | 133 | KdPrint(("CiValidateFileObject returned 0x%08X\n", status)); 134 | if (NT_SUCCESS(status)) 135 | { 136 | parsePolicyInfo(&signerPolicyInfo.get()); 137 | return; 138 | } 139 | } 140 | 141 | void parsePolicyInfo(const pPolicyInfo policyInfo) 142 | { 143 | if (policyInfo == nullptr) 144 | { 145 | KdPrint(("parsePolicyInfo - paramter is null\n")); 146 | return; 147 | } 148 | 149 | if (policyInfo->structSize == 0) 150 | { 151 | KdPrint(("policy info is empty\n")); 152 | return; 153 | } 154 | 155 | if (policyInfo->certChainInfo == nullptr) 156 | { 157 | KdPrint(("certChainInfo is null\n")); 158 | return; 159 | } 160 | 161 | const pCertChainInfoHeader chainInfoHeader = policyInfo->certChainInfo; 162 | 163 | const BYTE* startOfCertChainInfo = (BYTE*)(chainInfoHeader); 164 | const BYTE* endOfCertChainInfo = (BYTE*)(policyInfo->certChainInfo) + chainInfoHeader->bufferSize; 165 | 166 | if (!inRange(startOfCertChainInfo, endOfCertChainInfo, (BYTE*)chainInfoHeader->ptrToCertChainMembers)) 167 | { 168 | KdPrint(("chain members out of range\n")); 169 | return; 170 | } 171 | 172 | // need to make sure we have enough room to accomodate the chain member struct 173 | if (!inRange(startOfCertChainInfo, endOfCertChainInfo, (BYTE*)chainInfoHeader->ptrToCertChainMembers + sizeof(CertChainMember))) 174 | { 175 | KdPrint(("chain member out of range\n")); 176 | return; 177 | } 178 | 179 | // we are interested in the first certificate in the chain - the signer itself 180 | pCertChainMember signerChainMember = chainInfoHeader->ptrToCertChainMembers; 181 | 182 | KdPrint(("Signer certificate:\n digest algorithm - 0x%x\n size - %zu\n subject - %.*s\n issuer - %.*s\n", \ 183 | signerChainMember->digestIdetifier, \ 184 | signerChainMember->certificate.size, \ 185 | signerChainMember->subjectName.nameLen, \ 186 | static_cast(signerChainMember->subjectName.pointerToName), \ 187 | signerChainMember->issuerName.nameLen, \ 188 | static_cast(signerChainMember->issuerName.pointerToName)) \ 189 | ); 190 | 191 | UNREFERENCED_PARAMETER(signerChainMember); 192 | } 193 | 194 | bool inRange(const BYTE* rangeStartAddr, const BYTE* rangeEndAddr, const BYTE* addrToCheck) 195 | { 196 | if (addrToCheck > rangeEndAddr || addrToCheck < rangeStartAddr) 197 | { 198 | return false; 199 | } 200 | 201 | return true; 202 | } 203 | -------------------------------------------------------------------------------- /CiDemoDriver/SignatureCheck.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | void validateFileUsingCiValidateFileObject(PFILE_OBJECT FileObject); 7 | void validateFileUsingCiCheckSignedFile(PCUNICODE_STRING imageFileName); 8 | -------------------------------------------------------------------------------- /CiDemoDriver/ci.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | 7 | /** 8 | * This struct was copied from and encapsulates a signature used in verifying executable files. 9 | */ 10 | typedef struct _WIN_CERTIFICATE { 11 | DWORD dwLength; // Specifies the length, in bytes, of the signature 12 | WORD wRevision; // Specifies the certificate revision 13 | WORD wCertificateType; // Specifies the type of certificate 14 | BYTE bCertificate[ANYSIZE_ARRAY]; // An array of certificates 15 | } WIN_CERTIFICATE, * LPWIN_CERTIFICATE; 16 | 17 | 18 | /** 19 | * Describes the location (address) and size of a ASN.1 blob within a buffer. 20 | * 21 | * @note The data itself is not contained in the struct. 22 | */ 23 | typedef struct _Asn1BlobPtr 24 | { 25 | int size; // size of the ASN.1 blob 26 | PVOID ptrToData; // where the ASN.1 blob starts 27 | } Asn1BlobPtr, * pAsn1BlobPtr; 28 | 29 | 30 | /** 31 | * Describes the location (address) and size of a certificate subject/issuer name, within a buffer. 32 | * 33 | * @note The data itself (name) is not contained in the struct. 34 | * 35 | * @note the reason for separating these fields into their own struct was to match the padding we 36 | * observed in CertChainMember struct after the second 'short' field - once you enclose it 37 | * into a struct, on x64 bit machines there will be a padding of 4 bytes at the end of the struct, 38 | * because the largest member of the struct is of size 8 and it dictates the alignment of the struct. 39 | */ 40 | typedef struct _CertificatePartyName 41 | { 42 | PVOID pointerToName; 43 | short nameLen; 44 | short unknown; 45 | } CertificatePartyName, * pCertificatePartyName; 46 | 47 | 48 | /** 49 | * Contains various data about a specific certificate in the chain and also points to the actual certificate. 50 | * 51 | * @note the digest described in this struct is the digest that was used to create the certificate - not for 52 | * signing the file. 53 | * 54 | * @note The size reserved for digest is 64 byte regardless of the digest type, in order to accomodate SHA2/3's 55 | * max size of 512bit. The memory is not zeroed, so we must take the actual digestSize into account when 56 | * reading it. 57 | */ 58 | typedef struct _CertChainMember 59 | { 60 | int digestIdetifier; // e.g. 0x800c for SHA256 61 | int digestSize; // e.g. 0x20 for SHA256 62 | BYTE digestBuffer[64]; // contains the digest itself, where the digest size is dictated by digestSize 63 | 64 | CertificatePartyName subjectName; // pointer to the subject name 65 | CertificatePartyName issuerName; // pointer to the issuer name 66 | 67 | Asn1BlobPtr certificate; // ptr to actual cert in ASN.1 - including the public key 68 | } CertChainMember, * pCertChainMember; 69 | 70 | 71 | /** 72 | * Describes the format of certChainInfo buffer member of PolicyInfo struct. This header maps the types, 73 | * locations, and quantities of the data which is contained in the buffer. 74 | * 75 | * @note when using this struct make sure to check its size first (bufferSize) because it's not guaranteed 76 | * that all the fields below will exist. 77 | */ 78 | typedef struct _CertChainInfoHeader 79 | { 80 | // The size of the dynamically allocated buffer 81 | int bufferSize; 82 | 83 | // points to the start of a series of Asn1Blobs which contain the public keys of the certificates in the chain 84 | pAsn1BlobPtr ptrToPublicKeys; 85 | int numberOfPublicKeys; 86 | 87 | // points to the start of a series of Asn1Blobs which contain the EKUs 88 | pAsn1BlobPtr ptrToEkus; 89 | int numberOfEkus; 90 | 91 | // points to the start of a series of CertChainMembers 92 | pCertChainMember ptrToCertChainMembers; 93 | int numberOfCertChainMembers; 94 | 95 | int unknown; 96 | 97 | // ASN.1 blob of authenticated attributes - spcSpOpusInfo, contentType, etc. 98 | Asn1BlobPtr variousAuthenticodeAttributes; 99 | } CertChainInfoHeader, * pCertChainInfoHeader; 100 | 101 | 102 | /** 103 | * Contains information regarding the certificates that were used for signing/timestamping 104 | * 105 | * @note you must check structSize before accessing the other members, since some members were added later. 106 | * 107 | * @note all structs members, including the length, are populated by ci functions - no need 108 | * to fill them in adavnce. 109 | */ 110 | typedef struct _PolicyInfo 111 | { 112 | int structSize; 113 | NTSTATUS verificationStatus; 114 | int flags; 115 | pCertChainInfoHeader certChainInfo; // if not null - contains info about certificate chain 116 | FILETIME revocationTime; // when was the certificate revoked (if applicable) 117 | FILETIME notBeforeTime; // the certificate is not valid before this time 118 | FILETIME notAfterTime; // the certificate is not valid before this time 119 | } PolicyInfo, *pPolicyInfo; 120 | 121 | 122 | /** 123 | * Given a file digest and signature of a file, verify the signature and provide information regarding 124 | * the certificates that was used for signing (the entire certificate chain) 125 | * 126 | * @note the function allocates a buffer from the paged pool --> can be used only where IRQL < DISPATCH_LEVEL 127 | * 128 | * @param digestBuffer - buffer containing the digest 129 | * 130 | * @param digestSize - size of the digest, e.g. 0x20 for SHA256, 0x14 for SHA1 131 | * 132 | * @param digestIdentifier - digest algorithm identifier, e.g. 0x800c for SHA256, 0x8004 for SHA1 133 | * 134 | * @param winCert - pointer to the start of the security directory 135 | * 136 | * @param sizeOfSecurityDirectory - size the security directory 137 | * 138 | * @param policyInfoForSigner[out] - PolicyInfo containing information about the signer certificate chain 139 | * 140 | * @param signingTime[out] - when the file was signed (FILETIME format) 141 | * 142 | * @param policyInfoForTimestampingAuthority[out] - PolicyInfo containing information about the timestamping 143 | * authority (TSA) certificate chain 144 | * 145 | * @return 0 if the file digest in the signature matches the given digest and the signer cetificate is verified. 146 | * Various error values otherwise, for example: 147 | * STATUS_INVALID_IMAGE_HASH - the digest does not match the digest in the signature 148 | * STATUS_IMAGE_CERT_REVOKED - the certificate used for signing the file is revoked 149 | * STATUS_IMAGE_CERT_EXPIRED - the certificate used for signing the file has expired 150 | */ 151 | extern "C" __declspec(dllimport) NTSTATUS _stdcall CiCheckSignedFile( 152 | const PVOID digestBuffer, 153 | int digestSize, 154 | int digestIdentifier, 155 | const LPWIN_CERTIFICATE winCert, 156 | int sizeOfSecurityDirectory, 157 | PolicyInfo* policyInfoForSigner, 158 | LARGE_INTEGER* signingTime, 159 | PolicyInfo* policyInfoForTimestampingAuthority); 160 | 161 | 162 | /** 163 | * Resets a PolicyInfo struct - frees the dynamically allocated buffer in PolicyInfo (certChainInfo) if not null. 164 | * Zeros the entire PolicyInfo struct. 165 | * 166 | * @param policyInfo - the struct to reset. 167 | * 168 | * @return the struct which was reset. 169 | */ 170 | extern "C" __declspec(dllimport) PVOID _stdcall CiFreePolicyInfo(PolicyInfo* policyInfo); 171 | 172 | 173 | /** 174 | * Given a file object, verify the signature and provide information regarding 175 | * the certificates that was used for signing (the entire certificate chain) 176 | * 177 | * @note the function allocates memory from the paged pool --> can be used only where IRQL < DISPATCH_LEVEL 178 | * 179 | * @param fileObject[in] - fileObject of the PE in question 180 | * 181 | * @param a2[in] - unknown, needs to be reversed. 0 is a valid value. 182 | * 183 | * @param a3[in] - unknown, needs to be reversed. 0 is a valid value. 184 | * 185 | * @param policyInfoForSigner[out] - PolicyInfo containing information about the signer certificate chain 186 | * 187 | * @param signingTime[out] - when the file was signed 188 | * 189 | * @param policyInfoForTimestampingAuthority[out] - PolicyInfo containing information about the timestamping 190 | * authority (TSA) certificate chain 191 | * 192 | * @param digestBuffer[out] - buffer to be filled with the digest, must be at least 64 bytes 193 | * 194 | * @param digestSize[inout] - size of the digest. Must be at leat 64 and will be changed by the function to 195 | * reflect the actual digest length. 196 | * 197 | * @param digestIdentifier[out] - digest algorithm identifier, e.g. 0x800c for SHA256, 0x8004 for SHA1 198 | * 199 | * @return 0 if the file digest in the signature matches the given digest and the signer cetificate is verified. 200 | * Various error values otherwise, for example: 201 | * STATUS_INVALID_IMAGE_HASH - the digest does not match the digest in the signature 202 | * STATUS_IMAGE_CERT_REVOKED - the certificate used for signing the file is revoked 203 | * STATUS_IMAGE_CERT_EXPIRED - the certificate used for signing the file has expired 204 | */ 205 | extern "C" __declspec(dllimport) NTSTATUS _stdcall CiValidateFileObject( 206 | struct _FILE_OBJECT* fileObject, 207 | int a2, 208 | int a3, 209 | PolicyInfo* policyInfoForSigner, 210 | PolicyInfo* policyInfoForTimestampingAuthority, 211 | LARGE_INTEGER* signingTime, 212 | BYTE* digestBuffer, 213 | int* digestSize, 214 | int* digestIdentifier 215 | ); -------------------------------------------------------------------------------- /CiDemoDriver/main.cpp: -------------------------------------------------------------------------------- 1 | #include // PsSetCreateProcessNotifyRoutineEx 2 | #include 3 | #include "SignatureCheck.h" 4 | 5 | 6 | DRIVER_UNLOAD MyDriverUnload; 7 | void registerProcessCallback(); 8 | void unregisterProcessCallback(); 9 | void ProcessCreateProcessNotifyRoutineEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo); 10 | 11 | extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) 12 | { 13 | UNREFERENCED_PARAMETER(DriverObject); 14 | UNREFERENCED_PARAMETER(RegistryPath); 15 | DriverObject->DriverUnload = MyDriverUnload; 16 | 17 | KdPrint(("CiDemoDriver load\n")); 18 | 19 | registerProcessCallback(); 20 | 21 | return STATUS_SUCCESS; 22 | } 23 | 24 | VOID MyDriverUnload(_In_ struct _DRIVER_OBJECT* DriverObject) 25 | { 26 | UNREFERENCED_PARAMETER(DriverObject); 27 | KdPrint(("CiDemoDriver unload\n")); 28 | unregisterProcessCallback(); 29 | } 30 | 31 | void registerProcessCallback() 32 | { 33 | const NTSTATUS registerCallbackStatus = PsSetCreateProcessNotifyRoutineEx(ProcessCreateProcessNotifyRoutineEx, FALSE); 34 | if (!NT_SUCCESS(registerCallbackStatus)) 35 | { 36 | KdPrint(("failed to register callback with status %d\n", registerCallbackStatus)); 37 | } 38 | else 39 | { 40 | KdPrint(("successfully registered callback\n")); 41 | } 42 | } 43 | 44 | void unregisterProcessCallback() 45 | { 46 | const NTSTATUS registerCallbackStatus = PsSetCreateProcessNotifyRoutineEx(ProcessCreateProcessNotifyRoutineEx, TRUE); 47 | if (!NT_SUCCESS(registerCallbackStatus)) 48 | { 49 | KdPrint(("failed to unregister callback\n")); 50 | } 51 | else 52 | { 53 | KdPrint(("successfully unregistered callback\n")); 54 | } 55 | } 56 | 57 | void ProcessCreateProcessNotifyRoutineEx( 58 | PEPROCESS Process, 59 | HANDLE ProcessId, 60 | PPS_CREATE_NOTIFY_INFO CreateInfo 61 | ) 62 | { 63 | UNREFERENCED_PARAMETER(Process); 64 | UNREFERENCED_PARAMETER(ProcessId); 65 | 66 | if (CreateInfo == nullptr) return; //process died 67 | 68 | if (CreateInfo->FileObject == nullptr) return; 69 | if (nullptr == CreateInfo->ImageFileName) return; 70 | 71 | KdPrint(("New process - image name: %wZ\n", CreateInfo->ImageFileName)); 72 | 73 | validateFileUsingCiValidateFileObject(CreateInfo->FileObject); 74 | validateFileUsingCiCheckSignedFile(CreateInfo->ImageFileName); 75 | } 76 | -------------------------------------------------------------------------------- /ExecutablesForTesting/notepad++.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ido-Moshe-Github/CiDllDemo/419063dbbc5454cf267496ad9141982b42e6a34c/ExecutablesForTesting/notepad++.exe -------------------------------------------------------------------------------- /GeneratingLibFiles/CiStubs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern "C" __declspec(dllexport) NTSTATUS _stdcall CiCheckSignedFile( 4 | const PVOID digestBuffer, 5 | int digestSize, 6 | int digestIdentifier, 7 | const PVOID winCert, 8 | int sizeOfSecurityDirectory, 9 | PVOID policyInfoForSigner, 10 | PVOID signingTime, 11 | PVOID policyInfoForTimestampingAuthority) 12 | { 13 | NTSTATUS dummyReturnValue = 0; 14 | return dummyReturnValue; 15 | } 16 | 17 | extern "C" __declspec(dllexport) PVOID _stdcall CiFreePolicyInfo(PVOID policyInfoPtr) 18 | { 19 | PVOID dummyReturnValue = nullptr; 20 | return dummyReturnValue; 21 | } 22 | 23 | extern "C" __declspec(dllexport) NTSTATUS _stdcall CiValidateFileObject( 24 | PVOID fileObject, 25 | int a2, 26 | int a3, 27 | PVOID policyInfoForSigner, 28 | PVOID policyInfoForTimestampingAuthority, 29 | LARGE_INTEGER * signingTime, 30 | BYTE * digestBuffer, 31 | int* digestSize, 32 | int* digestIdentifier 33 | ) 34 | { 35 | NTSTATUS dummyReturnValue = 0; 36 | return dummyReturnValue; 37 | } -------------------------------------------------------------------------------- /GeneratingLibFiles/README.md: -------------------------------------------------------------------------------- 1 | # Generating import libraries (.lib files) 2 | 3 | Usually when linking with a certain dll, you’d use an import library provided by the vendor. 4 | In our case, no such ci.lib file is provided and we need to generate it ourselves. 5 | This lib file should be added as a linker input in the project properties. 6 | 7 | ## 64 bit 8 | 9 | Get the exported functions from the dll, using dumpbin utility: 10 | 11 | `dumpbin /EXPORTS c:\windows\system32\ci.dll` 12 | 13 | Create a .def file. It will looks something like this: 14 | 15 | ```c 16 | LIBRARY ci.dll 17 | EXPORTS 18 | CiCheckSignedFile 19 | CiFreePolicyInfo 20 | CiValidateFileObject 21 | ``` 22 | 23 | Generate the .lib file using the lib utility: 24 | 25 | `lib /def:ci.def /machine:x64 /out:ci.lib` 26 | 27 | 28 | ## 32 bit 29 | 30 | Here the situation gets a bit trickier, since in 32bit the functions are decorated to 31 | include the sum of the arguments (in bytes), for example: 32 | 33 | `CiFreePolicyInfo@4` 34 | 35 | But ci.dll is exporting the functions in their non-decorated shape, so we need to create a .lib file that makes this translation. 36 | 37 | - Follow the first two steps of the 64bit section above. 38 | 39 | - Create a C++ file with function stubs - the same signature but dummy body. You basically mimic what the vendor did when exporting 40 | the functions from their code. For example: 41 | 42 | ```c 43 | extern "C" __declspec(dllexport) PVOID _stdcall CiFreePolicyInfo(PVOID policyInfoPtr) 44 | { 45 | PVOID dummyReturnValue = nullptr; 46 | return dummyReturnValue; 47 | } 48 | ``` 49 | 50 | An example of such file is included in this repo under the name CiStubs.cpp. 51 | 52 | - Compile it into an OBJ file. 53 | 54 | - Generate the .lib file using the lib utility, this time with the OBJ file: 55 | 56 | `lib /def:ci.def /machine:x86 /out:ci.lib ` 57 | -------------------------------------------------------------------------------- /GeneratingLibFiles/ci.def: -------------------------------------------------------------------------------- 1 | LIBRARY ci.dll 2 | EXPORTS 3 | CiCheckSignedFile 4 | CiFreePolicyInfo 5 | CiValidateFileObject -------------------------------------------------------------------------------- /GeneratingLibFiles/main.cpp: -------------------------------------------------------------------------------- 1 | int main() 2 | { 3 | return 0; 4 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2020] [Ido Moshe, Liron Zuarets] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CiDemoDriver 2 | 3 | A joint effort of Liron Zuarets and I, CiDemoDriver is a very simple driver which demonstrates using ci.dll 4 | API in order to validate files. This repository complements a [write-up] we published about the subject. 5 | 6 | ### Logic 7 | 8 | The driver registers a ProcessCreateProcessNotify routine and whenever a new process is created, it tries to verify its 9 | Authentocide signature using two ci.dll APIs: 10 | - CiValidateFileObject: which acts directly on the file object 11 | - CiCheckSignedFile: which requires the caller to provide a file digest. Since file digest calculation is 12 | beyond the scope of this demo, we hard-coded the file digest of Notepad++.exe (this PE is included under "ExecutablesForTesting" 13 | folder) and the function will only succeed for this file. 14 | 15 | If the file's signature was verified successfully, the driver will parse the output PolicyInfo structure in order to extract the 16 | signing certificate and its details. 17 | 18 | ### Requirements 19 | 20 | - Supports **Windows 10**. If you want to use earlier OS versions, you need to remove the dependency in CiValidateFileObject 21 | from the code and the lib files, and change the project properties to the appropriate OS. 22 | - Supports **x86**, **x64** architectures 23 | - Can be compiled using Visual Studio 2019. The solution file is included. 24 | - In case you need to link against additional ci.dll functions, refer to the README inside GeneratingLibFiles. 25 | - In order to run the driver, use the .inf file to install and then load the driver by: `sc start CiDemoDriver` 26 | 27 | ### License 28 | 29 | This software is open-source under the MIT license. See the LICENSE.txt file in this repository. 30 | 31 | [write-up]: --------------------------------------------------------------------------------