extern
"C"
PVOID RtlImageDirectoryEntryToData(PVOID BaseAddress, BOOLEAN MappedAsImage, USHORT Directory, PULONG Size);
bool
inRange(const BYTE
*
rangeStartAddr, const BYTE
*
rangeEndAddr, const BYTE
*
addrToCheck);
void parsePolicyInfo(const pPolicyInfo policyInfo);
bool
ciCheckSignedFileWrapper(const LPWIN_CERTIFICATE win_cert, ULONG sizeOfSecurityDirectory);
void validateFileUsingCiCheckSignedFile(PCUNICODE_STRING imageFileName)
{
KDPRINT(
"【CiDemoDriver】"
,
"Validating file using CiCheckSignedFile...\n"
);
FileReadHandleGuard fileHandleGuard(imageFileName);
if
(!fileHandleGuard.isValid())
return
;
/
/
create section
for
the
file
SectionHandleGuard sectionHandleGuard(fileHandleGuard.get());
if
(!sectionHandleGuard.isValid())
return
;
/
/
get section
object
from
section handle
SectionObjectGuard sectionObjectGuard(sectionHandleGuard.get());
if
(!sectionObjectGuard.isValid())
return
;
/
/
map
a view of the section
SectionViewGuard viewGuard(sectionObjectGuard.get());
if
(!viewGuard.isValid())
return
;
/
/
fetch the security directory
PVOID securityDirectoryEntry
=
nullptr;
ULONG securityDirectoryEntrySize
=
0
;
securityDirectoryEntry
=
RtlImageDirectoryEntryToData(
viewGuard.getViewBaseAddress(),
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
IMAGE_DIRECTORY_ENTRY_SECURITY,
&securityDirectoryEntrySize
);
if
(securityDirectoryEntry
=
=
nullptr)
{
KDPRINT(
"【CiDemoDriver】"
,
"no security directory\n"
);
return
;
}
KDPRINT(
"【CiDemoDriver】"
,
"securityDirectoryEntry found at: %p, size: %x\n"
,
securityDirectoryEntry, securityDirectoryEntrySize);
/
/
Make sure the security directory
is
contained
in
the
file
view
const BYTE
*
endOfFileAddr
=
static_cast<BYTE
*
>(viewGuard.getViewBaseAddress())
+
viewGuard.getViewSize();
const BYTE
*
endOfSecurityDir
=
static_cast<BYTE
*
>(securityDirectoryEntry)
+
securityDirectoryEntrySize;
if
(endOfSecurityDir > endOfFileAddr || securityDirectoryEntry < viewGuard.getViewBaseAddress())
{
KDPRINT(
"【CiDemoDriver】"
,
"security directory is not contained in file view!\n"
);
return
;
}
/
/
technically, there can be several WIN_CERTIFICATE
in
a
file
. This
not
common,
and
,
for
simplicity,
/
/
we
'll assume there'
s only one
LPWIN_CERTIFICATE winCert
=
static_cast<LPWIN_CERTIFICATE>(securityDirectoryEntry);
KDPRINT(
"【CiDemoDriver】"
,
"WIN_CERTIFICATE at: %p, revision = %x, type = %x, length = %xd, bCertificate = %p\n"
,
securityDirectoryEntry, winCert
-
>wRevision, winCert
-
>wCertificateType, winCert
-
>dwLength, static_cast<PVOID>(winCert
-
>bCertificate));
ciCheckSignedFileWrapper(winCert, securityDirectoryEntrySize);
}
bool
ciCheckSignedFileWrapper(const LPWIN_CERTIFICATE win_cert, ULONG sizeOfSecurityDirectory)
{
/
/
prepare the parameters required
for
calling CiCheckSignedFile
PolicyInfoGuard signerPolicyInfo;
PolicyInfoGuard timestampingAuthorityPolicyInfo;
LARGE_INTEGER signingTime
=
{};
/
/
const
int
digestSize
=
20
;
/
/
sha1
len
,
0x14
const
int
digestSize
=
32
;
/
/
sha256
len
,
0x20
/
/
const
int
digestIdentifier
=
0x8004
;
/
/
sha1
const
int
digestIdentifier
=
0x800C
;
/
/
sha256
const BYTE digestBuffer[]
=
/
/
{
0x5f
,
0x6d
,
0xa4
,
0x95
,
0x78
,
0xa4
,
0x39
,
0x4b
,
0xb4
,
0x0f
,
0xf6
,
0x9b
,
0xaa
,
0x2a
,
0xd7
,
0x02
,
0xda
,
0x7d
,
0x3d
,
0xbe
,
0xb8
,
0x12
,
0xb8
,
0xc7
,
0x24
,
0xcd
,
0xe3
,
0x68
,
0x89
,
0x65
,
0x86
,
0x00
};
/
/
CiCheckSignedFile() allocates memory
from
the paged pool, so make sure we're at IRQL <
2
,
/
/
where access to paged memory
is
allowed
NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
const NTSTATUS status
=
CiCheckSignedFile(
(PVOID)digestBuffer,
digestSize,
digestIdentifier,
win_cert,
(
int
)sizeOfSecurityDirectory,
&signerPolicyInfo.get(),
&signingTime,
×tampingAuthorityPolicyInfo.get());
KDPRINT(
"【CiDemoDriver】"
,
"CiCheckSignedFile returned 0x%08X\n"
, status);
if
(NT_SUCCESS(status))
{
parsePolicyInfo(&signerPolicyInfo.get());
return
true;
}
return
false;
}
UCHAR HexToChar(UCHAR temp)
{
UCHAR dst;
if
(temp
=
=
' '
)
{
/
/
do nothing
dst
=
temp;
}
else
if
(temp <
10
) {
dst
=
temp
+
'0'
;
}
else
{
dst
=
temp
-
10
+
'A'
;
}
return
dst;
}
void validateFileUsingCiValidateFileObject(PFILE_OBJECT fileObject)
{
KDPRINT(
"【CiDemoDriver】"
,
"Validating file using CiValidateFileObject...\n"
);
NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
PolicyInfoGuard signerPolicyInfo;
PolicyInfoGuard timestampingAuthorityPolicyInfo;
LARGE_INTEGER signingTime
=
{};
int
digestSize
=
64
;
/
/
大小必须为
64
,否则返回缓冲区大小太小
int
digestIdentifier
=
0
;
BYTE digestBuffer[
64
]
=
{};
/
/
大小必须为
64
,否则返回缓冲区大小太小
const NTSTATUS status
=
CiValidateFileObject(
fileObject,
0
,
0
,
&signerPolicyInfo.get(),
×tampingAuthorityPolicyInfo.get(),
&signingTime,
digestBuffer,
&digestSize,
&digestIdentifier
);
KDPRINT(
"【CiDemoDriver】"
,
"CiValidateFileObject returned 0x%08X\n"
, status);
if
(NT_SUCCESS(status))
{
CHAR digestTempBuffer[
98
]
=
{
0
};
for
(
int
i
=
0
; i <
=
31
; i
+
+
)
{
digestTempBuffer[
3
*
i]
=
digestBuffer[i] >>
4
;
digestTempBuffer[
3
*
i
+
1
]
=
digestBuffer[i] &
0xf
;
digestTempBuffer[
3
*
i
+
2
]
=
' '
;
}
for
(
int
i
=
0
; i <
96
; i
+
+
)
{
digestTempBuffer[i]
=
HexToChar(digestTempBuffer[i]);
}
KDPRINT(
"【CiDemoDriver】"
,
"Signer certificate:\n digest algorithm - 0x%x\n digest size - %d\r\n digest - %s\n"
,
digestIdentifier, digestSize, digestTempBuffer);
parsePolicyInfo(&signerPolicyInfo.get());
return
;
}
}
void parsePolicyInfo(const pPolicyInfo policyInfo)
{
if
(policyInfo
=
=
nullptr)
{
KDPRINT(
"【CiDemoDriver】"
,
"parsePolicyInfo - paramter is null\n"
);
return
;
}
if
(policyInfo
-
>structSize
=
=
0
)
{
KDPRINT(
"【CiDemoDriver】"
,
"policy info is empty\n"
);
return
;
}
if
(policyInfo
-
>certChainInfo
=
=
nullptr)
{
KDPRINT(
"【CiDemoDriver】"
,
"certChainInfo is null\n"
);
return
;
}
const pCertChainInfoHeader chainInfoHeader
=
policyInfo
-
>certChainInfo;
const BYTE
*
startOfCertChainInfo
=
(BYTE
*
)(chainInfoHeader);
const BYTE
*
endOfCertChainInfo
=
(BYTE
*
)(policyInfo
-
>certChainInfo)
+
chainInfoHeader
-
>bufferSize;
DWORD dwChainCount
=
policyInfo
-
>certChainInfo
-
>numberOfCertChainMembers;
for
(DWORD dwChainIndex
=
0
; dwChainIndex < dwChainCount; dwChainIndex
+
+
)
{
if
(!inRange(startOfCertChainInfo, endOfCertChainInfo, (BYTE
*
)(chainInfoHeader
-
>ptrToCertChainMembers
+
dwChainIndex)))
{
KDPRINT(
"【CiDemoDriver】"
,
"chain members out of range\n"
);
continue
;
}
/
/
need to make sure we have enough room to accomodate the chain member struct
if
(!inRange(startOfCertChainInfo, endOfCertChainInfo, (BYTE
*
)(chainInfoHeader
-
>ptrToCertChainMembers
+
dwChainIndex)
+
sizeof(CertChainMember)))
{
KDPRINT(
"【CiDemoDriver】"
,
"chain member out of range\n"
);
continue
;
}
/
/
we are interested
in
the first certificate
in
the chain
-
the signer itself
pCertChainMember signerChainMember
=
chainInfoHeader
-
>ptrToCertChainMembers
+
dwChainIndex;
UTF8_STRING utf8SubjectName
=
{
0
};
utf8SubjectName.MaximumLength
=
utf8SubjectName.Length
=
signerChainMember
-
>subjectName.nameLen;
utf8SubjectName.
Buffer
=
static_cast<char
*
>(signerChainMember
-
>subjectName.pointerToName);
UNICODE_STRING usSubjectName
=
{
0
};
RtlUTF8StringToUnicodeString(&usSubjectName, &utf8SubjectName, true);
UTF8_STRING utf8IssuerName
=
{
0
};
utf8IssuerName.MaximumLength
=
utf8IssuerName.Length
=
signerChainMember
-
>issuerName.nameLen;
utf8IssuerName.
Buffer
=
static_cast<char
*
>(signerChainMember
-
>issuerName.pointerToName);
UNICODE_STRING usIssuerName
=
{
0
};
RtlUTF8StringToUnicodeString(&usIssuerName, &utf8IssuerName, true);
/
/
KDPRINT(
"【CiDemoDriver】"
,
"Signer certificate:\n digest algorithm - 0x%x\n size - %d\n subject - %.*s\n issuer - %.*s\n"
,
KDPRINT(
"【CiDemoDriver】"
,
"Signer certificate[%d]:\n digest algorithm - 0x%x\n size - %d\n subject - %wZ\n issuer - %wZ\n"
,
dwChainIndex
+
1
,
signerChainMember
-
>digestIdetifier, \
signerChainMember
-
>certificate.size, \
/
*
signerChainMember
-
>subjectName.nameLen,
static_cast<char
*
>(signerChainMember
-
>subjectName.pointerToName),
*
/
& usSubjectName,
/
*
signerChainMember
-
>issuerName.nameLen,
static_cast<char
*
>(signerChainMember
-
>issuerName.pointerToName)
*
/
&usIssuerName);
CHAR digestTempBuffer[
98
]
=
{
0
};
for
(
int
i
=
0
; i <
=
31
; i
+
+
)
{
digestTempBuffer[
3
*
i]
=
signerChainMember
-
>digestBuffer[i] >>
4
;
digestTempBuffer[
3
*
i
+
1
]
=
signerChainMember
-
>digestBuffer[i] &
0xf
;
digestTempBuffer[
3
*
i
+
2
]
=
' '
;
}
for
(
int
i
=
0
; i <
96
; i
+
+
)
{
digestTempBuffer[i]
=
HexToChar(digestTempBuffer[i]);
}
KDPRINT(
"【CiDemoDriver】"
,
"Signer certificate:\n digest algorithm - 0x%x\n digest size - %d\r\n digest - %s\n"
,
signerChainMember
-
>digestIdetifier, signerChainMember
-
>digestSize, digestTempBuffer);
RtlFreeUnicodeString(&usSubjectName);
RtlFreeUnicodeString(&usIssuerName);
}
}
bool
inRange(const BYTE
*
rangeStartAddr, const BYTE
*
rangeEndAddr, const BYTE
*
addrToCheck)
{
if
(addrToCheck > rangeEndAddr || addrToCheck < rangeStartAddr)
{
return
false;
}
return
true;
}