├── 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]:
--------------------------------------------------------------------------------