对于常规应用程序来说,校验数字签名在在应用层可以使用 WinVerifyTrust, 在驱动层使用常规的 API无法使用,自己分析数据又太麻烦。
但在内核中 ci.dll 包装了数据签名验证相关的功能,我们可以使用该 dll 来实现我们的数字签名验证。
详细的分析见《内核中的代码完整性:深入分析ci.dll》。下面直接上相关代码。
源代码地址为 https://github.com/Ido-Moshe-Github/CiDllDemo。
这里作了稍微的修改以及添加一些打印信息。
main.cpp 中使用 PsSetCreateProcessNotifyRoutineEx 添加一个创建进程回调,回调用使用进程对象的 FileObject 和文件路径 ImageFileName来进行文件数字签名的检验。
SignatureCheck.cpp 的 ciCheckSignedFileWrapper 函数中 digestSize 和 digestIdentifier 为验证签名的参数 ,基中 digestSize = 20 时表示签名算法为 sha1 时的长度,为32时表示算法为 sha256 时的长度; 其中 digestIdentifier =0x8004 表示算法为 sha1的标识符,0x800c 为 sha256的标识符。
SignatureCheck.cpp 的 ciCheckSignedFileWrapper 函数中 digestBuffer 值为签名文件的数字摘要,其内容在 validateFileUsingCiValidateFileObject 第 158 至 170 中 KDPRINT获取并打印。由于文件内容不同,签名得到的数字摘要也会不同。
SignatureCheck.cpp 的 parsePolicyInfo 函数打印数字证书的证书链上每个证书的详细情况及摘要。其中要注意的是此摘要是数字证书的摘要,即使文件内容不同,只要签名的数字证书为同一个,其摘要的内容是一样的,这个在以后可以加以利用。
SignatureCheck.cpp 的 validateFileUsingCiValidateFileObject 中 digestSize 和 digestBuffer 的大小必须大于等于64,否则 CiValidateFileObject 将返回“缓冲区大小太小”。
SignatureCheck.cpp 的 validateFileUsingCiValidateFileObject 中使用 CiValidateFileObject 获取签名的摘要时要注意只能获取第一个签名的摘要,比如同时双签名了 sha1 以及 sha256 证书,获取到的证书摘要只有 sha1的,而无法获取到 sha256的,因此如果要再获取 sha356 的摘要,需要再使用只有 sha256 签名证书的文件进行分析。
SignatureCheck.cpp 中使用的 CiValidateFileObject 以及 CiCheckSignedFile 是在 ci.dll 中导出,链接时需要一些方法,详见 <<5.链接 Ci.dll>>。
用来验证文件的数字签名是否正确,它验证了整个证书链,如果文件在签名后进行了修改,此验证不通过。
它验证完的同时会返回一个证书链的相关信息,我们可以判断证书链中的一些数字证书的摘要来确定是否为相应的签名。
用来检查相应的摘要签名,通过传入PE文件的 IMAGE_DIRECTORY_ENTRY_SECURITY 表,以及给定的摘要数据来进行判断签名是否有效。此摘要是整个文件的数字签名摘要,可以通过 CiValidateFileObject 来获取,并不是证书的摘要。
《内核中的代码完整性:深入分析ci.dll》文中也指出了相关方法,即使用 lib 工具来创建一个.lib文件。
先创建一个.def文件,内容如下:
然后使用 lib 工具执行命令:
加载驱动,然后运行一个有数字签名的程序。该程序添加了数字签名。
加载驱动后的打印消息如下:
去掉数字证书后如下:
再加载驱动调试信息如下:
两种签名验证的方法都不通过。
projectName
"::【"
__FUNCTION__
"】"
/
*
*
*
This struct was copied
from
<wintrust.h>
and
encapsulates a signature used
in
verifying executable files.
*
/
typedef struct _WIN_CERTIFICATE {
DWORD dwLength;
/
/
Specifies the length,
in
bytes, of the signature
WORD wRevision;
/
/
Specifies the certificate revision
WORD wCertificateType;
/
/
Specifies the
type
of certificate
BYTE bCertificate[ANYSIZE_ARRAY];
/
/
An array of certificates
} WIN_CERTIFICATE,
*
LPWIN_CERTIFICATE;
/
*
*
*
Describes the location (address)
and
size of a ASN.
1
blob within a
buffer
.
*
*
@note The data itself
is
not
contained
in
the struct.
*
/
typedef struct _Asn1BlobPtr
{
int
size;
/
/
size of the ASN.
1
blob
PVOID ptrToData;
/
/
where the ASN.
1
blob starts
} Asn1BlobPtr,
*
pAsn1BlobPtr;
/
*
*
*
Describes the location (address)
and
size of a certificate subject
/
issuer name, within a
buffer
.
*
*
@note The data itself (name)
is
not
contained
in
the struct.
*
*
@note the reason
for
separating these fields into their own struct was to match the padding we
*
observed
in
CertChainMember struct after the second
'short'
field
-
once you enclose it
*
into a struct, on x64 bit machines there will be a padding of
4
bytes at the end of the struct,
*
because the largest member of the struct
is
of size
8
and
it dictates the alignment of the struct.
*
/
typedef struct _CertificatePartyName
{
PVOID pointerToName;
short nameLen;
short unknown;
} CertificatePartyName,
*
pCertificatePartyName;
/
*
*
*
Contains various data about a specific certificate
in
the chain
and
also points to the actual certificate.
*
*
@note the digest described
in
this struct
is
the digest that was used to create the certificate
-
not
for
*
signing the
file
.
*
*
@note The size reserved
for
digest
is
64
byte regardless of the digest
type
,
in
order to accomodate SHA2
/
3
's
*
max
size of
512bit
. The memory
is
not
zeroed, so we must take the actual digestSize into account when
*
reading it.
*
/
typedef struct _CertChainMember
{
int
digestIdetifier;
/
/
e.g.
0x800c
for
SHA256
int
digestSize;
/
/
e.g.
0x20
for
SHA256
BYTE digestBuffer[
64
];
/
/
contains the digest itself, where the digest size
is
dictated by digestSize
CertificatePartyName subjectName;
/
/
pointer to the subject name
CertificatePartyName issuerName;
/
/
pointer to the issuer name
Asn1BlobPtr certificate;
/
/
ptr to actual cert
in
ASN.
1
-
including the public key
} CertChainMember,
*
pCertChainMember;
/
*
*
*
Describes the
format
of certChainInfo
buffer
member of PolicyInfo struct. This header maps the types,
*
locations,
and
quantities of the data which
is
contained
in
the
buffer
.
*
*
@note when using this struct make sure to check its size first (bufferSize) because it's
not
guaranteed
*
that
all
the fields below will exist.
*
/
typedef struct _CertChainInfoHeader
{
/
/
The size of the dynamically allocated
buffer
int
bufferSize;
/
/
points to the start of a series of Asn1Blobs which contain the public keys of the certificates
in
the chain
pAsn1BlobPtr ptrToPublicKeys;
int
numberOfPublicKeys;
/
/
points to the start of a series of Asn1Blobs which contain the EKUs
pAsn1BlobPtr ptrToEkus;
int
numberOfEkus;
/
/
points to the start of a series of CertChainMembers
pCertChainMember ptrToCertChainMembers;
int
numberOfCertChainMembers;
int
unknown;
/
/
ASN.
1
blob of authenticated attributes
-
spcSpOpusInfo, contentType, etc.
Asn1BlobPtr variousAuthenticodeAttributes;
} CertChainInfoHeader,
*
pCertChainInfoHeader;
/
*
*
*
Contains information regarding the certificates that were used
for
signing
/
timestamping
*
*
@note you must check structSize before accessing the other members, since some members were added later.
*
*
@note
all
structs members, including the length, are populated by ci functions
-
no need
*
to fill them
in
adavnce.
*
/
typedef struct _PolicyInfo
{
int
structSize;
NTSTATUS verificationStatus;
int
flags;
pCertChainInfoHeader certChainInfo;
/
/
if
not
null
-
contains info about certificate chain
FILETIME revocationTime;
/
/
when was the certificate revoked (
if
applicable)
FILETIME notBeforeTime;
/
/
the certificate
is
not
valid before this time
FILETIME notAfterTime;
/
/
the certificate
is
not
valid before this time
} PolicyInfo,
*
pPolicyInfo;
/
*
*
*
Given a
file
digest
and
signature of a
file
, verify the signature
and
provide information regarding
*
the certificates that was used
for
signing (the entire certificate chain)
*
*
@note the function allocates a
buffer
from
the paged pool
-
-
> can be used only where IRQL < DISPATCH_LEVEL
*
*
@param digestBuffer
-
buffer
containing the digest
*
*
@param digestSize
-
size of the digest, e.g.
0x20
for
SHA256,
0x14
for
SHA1
*
*
@param digestIdentifier
-
digest algorithm identifier, e.g.
0x800c
for
SHA256,
0x8004
for
SHA1
*
*
@param winCert
-
pointer to the start of the security directory
*
*
@param sizeOfSecurityDirectory
-
size the security directory
*
*
@param policyInfoForSigner[out]
-
PolicyInfo containing information about the signer certificate chain
*
*
@param signingTime[out]
-
when the
file
was signed (FILETIME
format
)
*
*
@param policyInfoForTimestampingAuthority[out]
-
PolicyInfo containing information about the timestamping
*
authority (TSA) certificate chain
*
*
@
return
0
if
the
file
digest
in
the signature matches the given digest
and
the signer cetificate
is
verified.
*
Various error values otherwise,
for
example:
*
STATUS_INVALID_IMAGE_HASH
-
the digest does
not
match the digest
in
the signature
*
STATUS_IMAGE_CERT_REVOKED
-
the certificate used
for
signing the
file
is
revoked
*
STATUS_IMAGE_CERT_EXPIRED
-
the certificate used
for
signing the
file
has expired
*
/
extern
"C"
__declspec(dllimport) NTSTATUS _stdcall CiCheckSignedFile(
const PVOID digestBuffer,
int
digestSize,
int
digestIdentifier,
const LPWIN_CERTIFICATE winCert,
int
sizeOfSecurityDirectory,
PolicyInfo
*
policyInfoForSigner,
LARGE_INTEGER
*
signingTime,
PolicyInfo
*
policyInfoForTimestampingAuthority);
/
*
*
*
Resets a PolicyInfo struct
-
frees the dynamically allocated
buffer
in
PolicyInfo (certChainInfo)
if
not
null.
*
Zeros the entire PolicyInfo struct.
*
*
@param policyInfo
-
the struct to reset.
*
*
@
return
the struct which was reset.
*
/
extern
"C"
__declspec(dllimport) PVOID _stdcall CiFreePolicyInfo(PolicyInfo
*
policyInfo);
/
*
*
*
Given a
file
object
, verify the signature
and
provide information regarding
*
the certificates that was used
for
signing (the entire certificate chain)
*
*
@note the function allocates memory
from
the paged pool
-
-
> can be used only where IRQL < DISPATCH_LEVEL
*
*
@param fileObject[
in
]
-
fileObject of the PE
in
question
*
*
@param a2[
in
]
-
unknown, needs to be
reversed
.
0
is
a valid value.
*
*
@param a3[
in
]
-
unknown, needs to be
reversed
.
0
is
a valid value.
*
*
@param policyInfoForSigner[out]
-
PolicyInfo containing information about the signer certificate chain
*
*
@param signingTime[out]
-
when the
file
was signed
*
*
@param policyInfoForTimestampingAuthority[out]
-
PolicyInfo containing information about the timestamping
*
authority (TSA) certificate chain
*
*
@param digestBuffer[out]
-
buffer
to be filled with the digest, must be at least
64
bytes
*
*
@param digestSize[inout]
-
size of the digest. Must be at leat
64
and
will be changed by the function to
*
reflect the actual digest length.
*
*
@param digestIdentifier[out]
-
digest algorithm identifier, e.g.
0x800c
for
SHA256,
0x8004
for
SHA1
*
*
@
return
0
if
the
file
digest
in
the signature matches the given digest
and
the signer cetificate
is
verified.
*
Various error values otherwise,
for
example:
*
STATUS_INVALID_IMAGE_HASH
-
the digest does
not
match the digest
in
the signature
*
STATUS_IMAGE_CERT_REVOKED
-
the certificate used
for
signing the
file
is
revoked
*
STATUS_IMAGE_CERT_EXPIRED
-
the certificate used
for
signing the
file
has expired
*
/
extern
"C"
__declspec(dllimport) NTSTATUS _stdcall CiValidateFileObject(
struct _FILE_OBJECT
*
fileObject,
int
a2,
int
a3,
PolicyInfo
*
policyInfoForSigner,
PolicyInfo
*
policyInfoForTimestampingAuthority,
LARGE_INTEGER
*
signingTime,
BYTE
*
digestBuffer,
int
*
digestSize,
int
*
digestIdentifier
);
projectName
"::【"
__FUNCTION__
"】"
/
*
*
*
This struct was copied
from
<wintrust.h>
and
encapsulates a signature used
in
verifying executable files.
*
/
typedef struct _WIN_CERTIFICATE {
DWORD dwLength;
/
/
Specifies the length,
in
bytes, of the signature
WORD wRevision;
/
/
Specifies the certificate revision
WORD wCertificateType;
/
/
Specifies the
type
of certificate
BYTE bCertificate[ANYSIZE_ARRAY];
/
/
An array of certificates
} WIN_CERTIFICATE,
*
LPWIN_CERTIFICATE;
/
*
*
*
Describes the location (address)
and
size of a ASN.
1
blob within a
buffer
.
*
*
@note The data itself
is
not
contained
in
the struct.
*
/
typedef struct _Asn1BlobPtr
{
int
size;
/
/
size of the ASN.
1
blob
PVOID ptrToData;
/
/
where the ASN.
1
blob starts
} Asn1BlobPtr,
*
pAsn1BlobPtr;
/
*
*
*
Describes the location (address)
and
size of a certificate subject
/
issuer name, within a
buffer
.
*
*
@note The data itself (name)
is
not
contained
in
the struct.
*
*
@note the reason
for
separating these fields into their own struct was to match the padding we
*
observed
in
CertChainMember struct after the second
'short'
field
-
once you enclose it
*
into a struct, on x64 bit machines there will be a padding of
4
bytes at the end of the struct,
*
because the largest member of the struct
is
of size
8
and
it dictates the alignment of the struct.
*
/
typedef struct _CertificatePartyName
{
PVOID pointerToName;
short nameLen;
short unknown;
} CertificatePartyName,
*
pCertificatePartyName;
/
*
*
*
Contains various data about a specific certificate
in
the chain
and
also points to the actual certificate.
*
*
@note the digest described
in
this struct
is
the digest that was used to create the certificate
-
not
for
*
signing the
file
.
*
*
@note The size reserved
for
digest
is
64
byte regardless of the digest
type
,
in
order to accomodate SHA2
/
3
's
*
max
size of
512bit
. The memory
is
not
zeroed, so we must take the actual digestSize into account when
*
reading it.
*
/
typedef struct _CertChainMember
{
int
digestIdetifier;
/
/
e.g.
0x800c
for
SHA256
int
digestSize;
/
/
e.g.
0x20
for
SHA256
BYTE digestBuffer[
64
];
/
/
contains the digest itself, where the digest size
is
dictated by digestSize
CertificatePartyName subjectName;
/
/
pointer to the subject name
CertificatePartyName issuerName;
/
/
pointer to the issuer name
Asn1BlobPtr certificate;
/
/
ptr to actual cert
in
ASN.
1
-
including the public key
} CertChainMember,
*
pCertChainMember;
/
*
*
*
Describes the
format
of certChainInfo
buffer
member of PolicyInfo struct. This header maps the types,
*
locations,
and
quantities of the data which
is
contained
in
the
buffer
.
*
*
@note when using this struct make sure to check its size first (bufferSize) because it's
not
guaranteed
*
that
all
the fields below will exist.
*
/
typedef struct _CertChainInfoHeader
{
/
/
The size of the dynamically allocated
buffer
int
bufferSize;
/
/
points to the start of a series of Asn1Blobs which contain the public keys of the certificates
in
the chain
pAsn1BlobPtr ptrToPublicKeys;
int
numberOfPublicKeys;
/
/
points to the start of a series of Asn1Blobs which contain the EKUs
pAsn1BlobPtr ptrToEkus;
int
numberOfEkus;
/
/
points to the start of a series of CertChainMembers
pCertChainMember ptrToCertChainMembers;
int
numberOfCertChainMembers;
int
unknown;
/
/
ASN.
1
blob of authenticated attributes
-
spcSpOpusInfo, contentType, etc.
Asn1BlobPtr variousAuthenticodeAttributes;
} CertChainInfoHeader,
*
pCertChainInfoHeader;
/
*
*
*
Contains information regarding the certificates that were used
for
signing
/
timestamping
*
*
@note you must check structSize before accessing the other members, since some members were added later.
*
*
@note
all
structs members, including the length, are populated by ci functions
-
no need
*
to fill them
in
adavnce.
*
/
typedef struct _PolicyInfo
{
int
structSize;
NTSTATUS verificationStatus;
int
flags;
pCertChainInfoHeader certChainInfo;
/
/
if
not
null
-
contains info about certificate chain
FILETIME revocationTime;
/
/
when was the certificate revoked (
if
applicable)
FILETIME notBeforeTime;
/
/
the certificate
is
not
valid before this time
FILETIME notAfterTime;
/
/
the certificate
is
not
valid before this time
} PolicyInfo,
*
pPolicyInfo;
/
*
*
*
Given a
file
digest
and
signature of a
file
, verify the signature
and
provide information regarding
*
the certificates that was used
for
signing (the entire certificate chain)
*
*
@note the function allocates a
buffer
from
the paged pool
-
-
> can be used only where IRQL < DISPATCH_LEVEL
*
*
@param digestBuffer
-
buffer
containing the digest
*
*
@param digestSize
-
size of the digest, e.g.
0x20
for
SHA256,
0x14
for
SHA1
*
*
@param digestIdentifier
-
digest algorithm identifier, e.g.
0x800c
for
SHA256,
0x8004
for
SHA1
*
*
@param winCert
-
pointer to the start of the security directory
*
*
@param sizeOfSecurityDirectory
-
size the security directory
*
*
@param policyInfoForSigner[out]
-
PolicyInfo containing information about the signer certificate chain
*
*
@param signingTime[out]
-
when the
file
was signed (FILETIME
format
)
*
*
@param policyInfoForTimestampingAuthority[out]
-
PolicyInfo containing information about the timestamping
*
authority (TSA) certificate chain
*
*
@
return
0
if
the
file
digest
in
the signature matches the given digest
and
the signer cetificate
is
verified.
*
Various error values otherwise,
for
example:
*
STATUS_INVALID_IMAGE_HASH
-
the digest does
not
match the digest
in
the signature
*
STATUS_IMAGE_CERT_REVOKED
-
the certificate used
for
signing the
file
is
revoked
*
STATUS_IMAGE_CERT_EXPIRED
-
the certificate used
for
signing the
file
has expired
*
/
extern
"C"
__declspec(dllimport) NTSTATUS _stdcall CiCheckSignedFile(
const PVOID digestBuffer,
int
digestSize,
int
digestIdentifier,
const LPWIN_CERTIFICATE winCert,
int
sizeOfSecurityDirectory,
PolicyInfo
*
policyInfoForSigner,
LARGE_INTEGER
*
signingTime,
PolicyInfo
*
policyInfoForTimestampingAuthority);
/
*
*
*
Resets a PolicyInfo struct
-
frees the dynamically allocated
buffer
in
PolicyInfo (certChainInfo)
if
not
null.
*
Zeros the entire PolicyInfo struct.
*
*
@param policyInfo
-
the struct to reset.
*
*
@
return
the struct which was reset.
*
/
extern
"C"
__declspec(dllimport) PVOID _stdcall CiFreePolicyInfo(PolicyInfo
*
policyInfo);
/
*
*
*
Given a
file
object
, verify the signature
and
provide information regarding
*
the certificates that was used
for
signing (the entire certificate chain)
*
*
@note the function allocates memory
from
the paged pool
-
-
> can be used only where IRQL < DISPATCH_LEVEL
*
*
@param fileObject[
in
]
-
fileObject of the PE
in
question
*
*
@param a2[
in
]
-
unknown, needs to be
reversed
.
0
is
a valid value.
*
*
@param a3[
in
]
-
unknown, needs to be
reversed
.
0
is
a valid value.
*
*
@param policyInfoForSigner[out]
-
PolicyInfo containing information about the signer certificate chain
*
*
@param signingTime[out]
-
when the
file
was signed
*
*
@param policyInfoForTimestampingAuthority[out]
-
PolicyInfo containing information about the timestamping
*
authority (TSA) certificate chain
*
*
@param digestBuffer[out]
-
buffer
to be filled with the digest, must be at least
64
bytes
*
*
@param digestSize[inout]
-
size of the digest. Must be at leat
64
and
will be changed by the function to
*
reflect the actual digest length.
*
*
@param digestIdentifier[out]
-
digest algorithm identifier, e.g.
0x800c
for
SHA256,
0x8004
for
SHA1
*
*
@
return
0
if
the
file
digest
in
the signature matches the given digest
and
the signer cetificate
is
verified.
*
Various error values otherwise,
for
example:
*
STATUS_INVALID_IMAGE_HASH
-
the digest does
not
match the digest
in
the signature
*
STATUS_IMAGE_CERT_REVOKED
-
the certificate used
for
signing the
file
is
revoked
*
STATUS_IMAGE_CERT_EXPIRED
-
the certificate used
for
signing the
file
has expired
*
/
extern
"C"
__declspec(dllimport) NTSTATUS _stdcall CiValidateFileObject(
struct _FILE_OBJECT
*
fileObject,
int
a2,
int
a3,
PolicyInfo
*
policyInfoForSigner,
PolicyInfo
*
policyInfoForTimestampingAuthority,
LARGE_INTEGER
*
signingTime,
BYTE
*
digestBuffer,
int
*
digestSize,
int
*
digestIdentifier
);
/
*
*
*
create a
file
handle
for
read.
*
release handle when exiting the current context.
*
/
class
FileReadHandleGuard
{
public:
FileReadHandleGuard(PCUNICODE_STRING imageFileName) : _handle(nullptr), _isValid(false)
{
IO_STATUS_BLOCK ioStatusBlock
=
{
0
};
OBJECT_ATTRIBUTES objAttr
=
{
0
};
InitializeObjectAttributes(
&objAttr,
const_cast<PUNICODE_STRING>(imageFileName),
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
nullptr,
nullptr);
const NTSTATUS openFileRet
=
ZwOpenFile(
&_handle,
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
&objAttr,
&ioStatusBlock,
FILE_SHARE_READ,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
/
/
FILE_SYNCHRONOUS_IO_NONALERT so that zwReadfile will pend
for
us until reading
is
done
);
if
(!NT_SUCCESS(openFileRet))
{
KDPRINT(
"【CiDemoDriver】"
,
"failed to open file - openFileRet = %d\n"
, openFileRet);
return
;
}
if
(ioStatusBlock.Status !
=
STATUS_SUCCESS || _handle
=
=
nullptr)
{
KDPRINT(
"【CiDemoDriver】"
,
"ioStatusBlock.Status != STATUS_SUCCESS, or _handle is null\n"
);
return
;
}
_isValid
=
true;
}
~FileReadHandleGuard()
{
if
(_handle !
=
nullptr)
{
ZwClose(_handle);
}
}
HANDLE& get() {
return
_handle; }
bool
isValid() const {
return
_isValid; }
private:
HANDLE _handle;
bool
_isValid;
};
/
*
*
*
create a section handle.
*
release handle when exiting the current context.
*
/
class
SectionHandleGuard
{
public:
SectionHandleGuard(HANDLE& fileHandle) : _handle(nullptr), _isValid(false)
{
OBJECT_ATTRIBUTES objectAttributes
=
{
0
};
InitializeObjectAttributes(
&objectAttributes,
nullptr,
OBJ_KERNEL_HANDLE,
/
/
to make sure user mode cannot access this handle
nullptr,
nullptr);
const NTSTATUS createSectionRet
=
ZwCreateSection(
&_handle,
SECTION_MAP_READ,
&objectAttributes,
nullptr,
/
/
maximum size
-
use the
file
size,
in
order to
map
the entire
file
PAGE_READONLY,
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
fileHandle
);
if
(!NT_SUCCESS(createSectionRet))
{
KDPRINT(
"【CiDemoDriver】"
,
"failed to create section - ZwCreateSection returned %x\n"
, createSectionRet);
return
;
}
_isValid
=
true;
}
~SectionHandleGuard()
{
if
(_handle !
=
nullptr)
{
ZwClose(_handle);
}
}
HANDLE& get() {
return
_handle; }
bool
isValid() const {
return
_isValid; }
private:
HANDLE _handle;
bool
_isValid;
};
/
*
*
*
retrieve a section
object
from
a section handle.
*
release
object
reference when exiting the current context.
*
/
class
SectionObjectGuard
{
public:
SectionObjectGuard(HANDLE& sectionHandle) : _object(nullptr), _isValid(false)
{
const NTSTATUS ret
=
ObReferenceObjectByHandle(
sectionHandle,
SECTION_MAP_READ,
nullptr,
KernelMode,
&_object,
nullptr
);
if
(!NT_SUCCESS(ret))
{
KDPRINT(
"【CiDemoDriver】"
,
"ObReferenceObjectByHandle failed - returned %x\n"
, ret);
return
;
}
_isValid
=
true;
}
~SectionObjectGuard()
{
if
(_object !
=
nullptr)
{
ObfDereferenceObject(_object);
}
}
PVOID& get() {
return
_object; }
bool
isValid() const {
return
_isValid; }
private:
PVOID _object;
bool
_isValid;
};
/
*
*
*
create a view of
file
.
*
unmap the view when exiting the current context.
*
/
class
SectionViewGuard
{
public:
SectionViewGuard(PVOID sectionObject) : _baseAddrOfView(nullptr), _viewSize(
0
), _isValid(false)
{
const NTSTATUS ret
=
MmMapViewInSystemSpace(
sectionObject,
&_baseAddrOfView,
&_viewSize
);
if
(!NT_SUCCESS(ret))
{
KDPRINT(
"【CiDemoDriver】"
,
"MmMapViewInSystemSpace failed - returned %x\n"
, ret);
return
;
}
_isValid
=
true;
}
~SectionViewGuard()
{
if
(_baseAddrOfView !
=
nullptr)
{
MmUnmapViewInSystemSpace(_baseAddrOfView);
}
}
PVOID getViewBaseAddress() const {
return
_baseAddrOfView; }
SIZE_T getViewSize() const {
return
_viewSize; }
bool
isValid() const {
return
_isValid; }
private:
PVOID _baseAddrOfView;
SIZE_T _viewSize;
bool
_isValid;
};
/
*
*
*
create a PoicyInfo struct.
*
Release the memory used by the struct when exiting the current context.
*
/
class
PolicyInfoGuard
{
public:
PolicyInfoGuard() : _policyInfo{} {}
~PolicyInfoGuard()
{
/
/
CiFreePolicyInfo checks internally
if
there's memory to free
CiFreePolicyInfo(&_policyInfo);
}
PolicyInfo& get() {
return
_policyInfo; }
private:
PolicyInfo _policyInfo;
};
/
*
*
*
create a
file
handle
for
read.
*
release handle when exiting the current context.
*
/
class
FileReadHandleGuard
{
public:
FileReadHandleGuard(PCUNICODE_STRING imageFileName) : _handle(nullptr), _isValid(false)
{
IO_STATUS_BLOCK ioStatusBlock
=
{
0
};
OBJECT_ATTRIBUTES objAttr
=
{
0
};
InitializeObjectAttributes(
&objAttr,
const_cast<PUNICODE_STRING>(imageFileName),
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
nullptr,
nullptr);
const NTSTATUS openFileRet
=
ZwOpenFile(
&_handle,
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
&objAttr,
&ioStatusBlock,
FILE_SHARE_READ,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
/
/
FILE_SYNCHRONOUS_IO_NONALERT so that zwReadfile will pend
for
us until reading
is
done
);
if
(!NT_SUCCESS(openFileRet))
{
KDPRINT(
"【CiDemoDriver】"
,
"failed to open file - openFileRet = %d\n"
, openFileRet);
return
;
}
if
(ioStatusBlock.Status !
=
STATUS_SUCCESS || _handle
=
=
nullptr)
{
KDPRINT(
"【CiDemoDriver】"
,
"ioStatusBlock.Status != STATUS_SUCCESS, or _handle is null\n"
);
return
;
}
_isValid
=
true;
}
~FileReadHandleGuard()
{
if
(_handle !
=
nullptr)
{
ZwClose(_handle);
}
}
HANDLE& get() {
return
_handle; }
bool
isValid() const {
return
_isValid; }
private:
HANDLE _handle;
bool
_isValid;
};
/
*
*
*
create a section handle.
*
release handle when exiting the current context.
*
/
class
SectionHandleGuard
{
public:
SectionHandleGuard(HANDLE& fileHandle) : _handle(nullptr), _isValid(false)
{
OBJECT_ATTRIBUTES objectAttributes
=
{
0
};
InitializeObjectAttributes(
&objectAttributes,
nullptr,
OBJ_KERNEL_HANDLE,
/
/
to make sure user mode cannot access this handle
nullptr,
nullptr);
const NTSTATUS createSectionRet
=
ZwCreateSection(
&_handle,
SECTION_MAP_READ,
&objectAttributes,
nullptr,
/
/
maximum size
-
use the
file
size,
in
order to
map
the entire
file
PAGE_READONLY,
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
fileHandle
);
if
(!NT_SUCCESS(createSectionRet))
{
KDPRINT(
"【CiDemoDriver】"
,
"failed to create section - ZwCreateSection returned %x\n"
, createSectionRet);
return
;
}
_isValid
=
true;
}
~SectionHandleGuard()
{
if
(_handle !
=
nullptr)
{
ZwClose(_handle);
}
}
HANDLE& get() {
return
_handle; }
bool
isValid() const {
return
_isValid; }
private:
HANDLE _handle;
bool
_isValid;
};
/
*
*
*
retrieve a section
object
from
a section handle.
*
release
object
reference when exiting the current context.
*
/
class
SectionObjectGuard
{
public:
SectionObjectGuard(HANDLE& sectionHandle) : _object(nullptr), _isValid(false)
{
const NTSTATUS ret
=
ObReferenceObjectByHandle(
sectionHandle,
SECTION_MAP_READ,
nullptr,
KernelMode,
&_object,
nullptr
);
if
(!NT_SUCCESS(ret))
{
KDPRINT(
"【CiDemoDriver】"
,
"ObReferenceObjectByHandle failed - returned %x\n"
, ret);
return
;
}
_isValid
=
true;
}
~SectionObjectGuard()
{
if
(_object !
=
nullptr)
{
ObfDereferenceObject(_object);
}
}
PVOID& get() {
return
_object; }
bool
isValid() const {
return
_isValid; }
private:
PVOID _object;
bool
_isValid;
};
/
*
*
*
create a view of
file
.
*
unmap the view when exiting the current context.
*
/
class
SectionViewGuard
{
public:
SectionViewGuard(PVOID sectionObject) : _baseAddrOfView(nullptr), _viewSize(
0
), _isValid(false)
{
const NTSTATUS ret
=
MmMapViewInSystemSpace(
sectionObject,
&_baseAddrOfView,
&_viewSize
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!