Windows驱动中使用数字签名验证控制设备访问权限

1. 背景

  在一般的驱动开发时,创建了符号链接后在应用层就可以访问打开我们的设备并进行通讯。

  但我们有时候不希望非自己的进程访问我们的设备并进行交互,虽然可以使用 IoCreateDeviceSecure 来创建有安全描述符的设备,但大数的用户账户为了方便都是管理员,因此该方法不太完整。

2. 一般方法

  一般情况下进行限制的方法是允许打开设备,但在打开设备后做一些校验,如果不通过,之后的其它请求都拒绝。

  例如 Dell 在一个漏洞驱动 pcdsrvc_x64.pkms 中处理如下(IDA逆向代码):

__int64 __fastcall DeviceIoControl(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{_IO_STACK_LOCATION* pIosp; // r9unsigned int nBytesReturn; // edxULONG nIoControlCode; // ecxunsigned int ntStatus; // ebxDWORD* dwInitailizeCode; // raxpIosp = pIrp->Tail.Overlay.CurrentStackLocation;nBytesReturn = 0;nIoControlCode = pIosp->Parameters.DeviceIoControl.IoControlCode;if (nIoControlCode != 0x222004 && !bInitialized){ntStatus = 0xC0000022;pIrp->IoStatus.Information = 0i64;LABEL_18:LOBYTE(nBytesReturn) = 0;goto LABEL_36;}if (nIoControlCode - 0x222000 <= 0x7C){ntStatus = 0;if (nIoControlCode == 0x222000){if (pIosp->Parameters.DeviceIoControl.OutputBufferLength >= 4){*(_DWORD*)pIrp->AssociatedIrp.SystemBuffer = 0x6020300;LABEL_16:nBytesReturn = 4;goto LABEL_17;}}else{if (nIoControlCode != 0x222004){ntStatus = 0xC0000010;LABEL_17:pIrp->IoStatus.Information = nBytesReturn;goto LABEL_18;}if (pIosp->Parameters.DeviceIoControl.InputBufferLength >= 4&& pIosp->Parameters.DeviceIoControl.OutputBufferLength >= 4)// 初始化驱动{dwInitailizeCode = (DWORD*)pIrp->AssociatedIrp.SystemBuffer;if (*dwInitailizeCode == 0xA1B2C3D4){bInitialized = 1;*dwInitailizeCode = 0;}else{*dwInitailizeCode = 1;}goto LABEL_16;}}ntStatus = 0xC000000D;goto LABEL_17;}......
}

  按照原逻辑是在打开使用后发送一个 0x222004 的请求码,并传入 0xA1B2C3D4,至此以后就相当于初始化了,之后的请求都可以正常进行;如果没有这个初始化的请求,之后的请求都是拒绝的。

  虽然这个方法可行,但稍微懂点逆向的相当于可以看到源代码逻辑,这样只要稍作处理这个方法就失效了。

3. 使用验证数字签名

  使用该方法可以在设备打开的逻辑中验证操作进程的数字签名,如果为允许的签名就返回成功,打开设备也就成功;否则返回失败也就打开失败。

  驱动中验证PE的数字签名见《Windows驱动中校验数字签名(使用 ci.dll)》。里边有两种方法,一种是 CiValidateFileObject,另一种是 CiCheckSignedFile。

  其中 CiCheckSignedFile 更为严格,需要指定签名算法和摘要,此摘要是 PE 文件签名后的摘要,因此随时 PE 文件的不同摘要也不同,这里并不太方便。

  因此本文用的是 CiValidateFileObject,首先它可以验证签名是否有效,即签名后文件进行了篡改后验证是不通过的;假的数字签名证书也不能通过验证。再者它可以返回证书链以及每个证书的摘要,我们可在在这里判断证书的摘要,如果是我们指定的证书则通过验证,即使是用的同一证书签名不同的文件,也可以一次代码,多次使用。

4. 代码

4.1 驱动代码:

4.1.1 ci.h

#pragma once#include <wdm.h>
#include <minwindef.h>#if DBG
#define KDPRINT(projectName, format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,\projectName "::【" __FUNCTION__  "】" ##format, \##__VA_ARGS__ ) 
#else
#define KDPRINT(format, ...)
#endif/**
*  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 signatureWORD  wRevision;                        // Specifies the certificate revisionWORD  wCertificateType;                 // Specifies the type of certificateBYTE  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 blobPVOID 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 SHA256int digestSize;                     // e.g. 0x20 for SHA256BYTE digestBuffer[64];              // contains the digest itself, where the digest size is dictated by digestSizeCertificatePartyName subjectName;   // pointer to the subject nameCertificatePartyName issuerName;    // pointer to the issuer nameAsn1BlobPtr 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 bufferint bufferSize;// points to the start of a series of Asn1Blobs which contain the public keys of the certificates in the chainpAsn1BlobPtr ptrToPublicKeys;int numberOfPublicKeys;// points to the start of a series of Asn1Blobs which contain the EKUspAsn1BlobPtr ptrToEkus;int numberOfEkus;// points to the start of a series of CertChainMemberspCertChainMember 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 chainFILETIME revocationTime;            // when was the certificate revoked (if applicable)FILETIME notBeforeTime;             // the certificate is not valid before this timeFILETIME 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
);

4.1.2 SignatureCheck.h

#pragma once#include <wdm.h>
#include <minwindef.h>#define SHA1_IDENTIFIER 0x8004
#define SHA1_DEGIST_LENGTH  20
#define SHA256_IDENTIFIER 0x800C
#define SHA256_DEGIST_LENGTH  32
#define IMAGE_DIRECTORY_ENTRY_SECURITY  4
#define PRINT_DIGEST trueBOOLEAN ValidateFileUsingCiValidateFileObject(PFILE_OBJECT fileObject, const BYTE crtDigest[], UINT nLength);

4.1.3 SignatureCheck.cpp

#include "SignatureCheck.h"
#include "ci.h"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;
}BOOLEAN ValidateFileUsingCiValidateFileObject(PFILE_OBJECT fileObject , const BYTE crtDigest[], UINT nLength)
{KDPRINT("【IrpCertCheck】", "Validating file using CiValidateFileObject...\n");NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);PolicyInfo signerPolicyInfo = {0};PolicyInfo timestampingAuthorityPolicyInfo = {0};LARGE_INTEGER signingTime = {0};int digestSize = 64;  //大小必须为64,否则返回缓冲区大小太小int digestIdentifier = 0;BYTE digestBuffer[64] = {0}; //大小必须为64,否则返回缓冲区大小太小BOOLEAN bValidate = false;const NTSTATUS status = CiValidateFileObject(fileObject,0,0,&signerPolicyInfo,&timestampingAuthorityPolicyInfo,&signingTime,digestBuffer,&digestSize,&digestIdentifier);KDPRINT("【IrpCertCheck】", "CiValidateFileObject returned 0x%08X\n", status);if (NT_SUCCESS(status)){
#if PRINT_DIGEST CHAR digestTempBuffer[98] = { 0 };for (int i = 0; i < 32; i++){digestTempBuffer[3 * i] = signerPolicyInfo.certChainInfo->ptrToCertChainMembers->digestBuffer[i] >> 4;digestTempBuffer[3 * i + 1] = signerPolicyInfo.certChainInfo->ptrToCertChainMembers->digestBuffer[i] & 0xf;digestTempBuffer[3 * i + 2] = ' ';}for (int i = 0; i < 96; i++){digestTempBuffer[i] = HexToChar(digestTempBuffer[i]);}KDPRINT("【IrpCertCheck】", "Signer certificate:\n digest algorithm - 0x%x\n digest size - %d\r\n digest - %s\n",digestIdentifier, digestSize, digestTempBuffer);
#endifif (RtlCompareMemory(signerPolicyInfo.certChainInfo->ptrToCertChainMembers->digestBuffer, crtDigest, nLength) == nLength){bValidate =  true;}}CiFreePolicyInfo(&signerPolicyInfo);CiFreePolicyInfo(&timestampingAuthorityPolicyInfo);return bValidate;
}

4.1.4 main.cpp

#include <ntddk.h> // PsSetCreateProcessNotifyRoutineEx
#include <wdm.h>
#include <wdmsec.h>#include "SignatureCheck.h"
#include "ci.h"
#pragma comment(lib, "Wdmsec.lib")#define IRP_CERT_CHECK_DEVICE_NAME      (L"\\Device\\IrpCertCheck")
#define IRP_CERT_CHECK_DEVICE_SYMBOLIC_NAME      (L"\\??\\IrpCertCheck")
#define CTL_CODE_TEST  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1000, METHOD_BUFFERED, FILE_ANY_ACCESS)EXTERN_C
NTKERNELAPI
NTSTATUS
PsReferenceProcessFilePointer(IN PEPROCESS Process,OUT PVOID* pFilePointer
);
EXTERN_C
NTKERNELAPI
NTSTATUS
IoQueryFileDosDeviceName(_In_  PFILE_OBJECT FileObject,_Out_ POBJECT_NAME_INFORMATION* ObjectNameInformation
);
DRIVER_UNLOAD Unload;
PDEVICE_OBJECT g_pDeviceObject = NULL;const static BYTE Cert256Digest[SHA256_DEGIST_LENGTH] = {0xD6, 0x43, 0x45, 0x88, 0x65, 0x45, 0x27, 0x40, 0x44, 0xDD,0x87, 0xD8, 0xCF, 0x67, 0x4B, 0x34, 0x78, 0x42, 0xE5, 0xA3,0x70, 0x35, 0x05, 0xB8, 0x90, 0x15, 0xD4, 0xA4, 0xB7, 0x7F,0xBA, 0x31 };NTSTATUS
DeviceIoControl(IN PDEVICE_OBJECT pDeviceObject,IN PIRP pIrp
)
{UNREFERENCED_PARAMETER(pDeviceObject);NTSTATUS ntStatus = STATUS_INVALID_PARAMETER;PIO_STACK_LOCATION pIosp = IoGetCurrentIrpStackLocation(pIrp);if (pIosp->Parameters.DeviceIoControl.IoControlCode == CTL_CODE_TEST){KDPRINT("【IrpCertCheck】", "This Is Test Control Code\n");ntStatus = STATUS_SUCCESS;}pIrp->IoStatus.Information = 0;pIrp->IoStatus.Status = ntStatus;IoCompleteRequest(pIrp, IO_NO_INCREMENT);return ntStatus;
}NTSTATUS CommonDispatchRoutine(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{UNREFERENCED_PARAMETER(pDeviceObject);NTSTATUS ntStatus = STATUS_NOT_IMPLEMENTED;pIrp->IoStatus.Information = 0;pIrp->IoStatus.Status = ntStatus;IoCompleteRequest(pIrp, IO_NO_INCREMENT);return ntStatus;
}NTSTATUS
Create(IN PDEVICE_OBJECT pDeviceObject,IN PIRP pIrp
)
{UNREFERENCED_PARAMETER(pDeviceObject);BOOLEAN bAllow = false;NTSTATUS ntStatus = STATUS_ACCESS_DENIED;PEPROCESS pCurrentProcess = PsGetCurrentProcess();PFILE_OBJECT pFileObject = NULL;ntStatus = PsReferenceProcessFilePointer(pCurrentProcess, (PVOID*)&pFileObject);if (NT_SUCCESS(ntStatus)){POBJECT_NAME_INFORMATION name =NULL;ntStatus = IoQueryFileDosDeviceName(pFileObject, &name);if (NT_SUCCESS(ntStatus)){KDPRINT("【IrpCertCheck】", "Operation Process Name:%wZ\n", &name->Name);HANDLE hFile = NULL;WCHAR szBuffer[MAX_PATH] = { 0 };UNICODE_STRING usFileDosPath = { 0 };usFileDosPath.MaximumLength = MAX_PATH * 2;usFileDosPath.Buffer = szBuffer;RtlAppendUnicodeToString(&usFileDosPath, L"\\??\\");RtlAppendUnicodeStringToString(&usFileDosPath, &name->Name);OBJECT_ATTRIBUTES oba = { 0 };InitializeObjectAttributes(&oba, &usFileDosPath, OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL);IO_STATUS_BLOCK iosb = { 0 };ntStatus = ZwOpenFile(&hFile, GENERIC_READ, &oba, &iosb, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);if (NT_SUCCESS(ntStatus)){PFILE_OBJECT pFileRef = NULL;ntStatus = ObReferenceObjectByHandle(hFile, GENERIC_READ, *IoFileObjectType, KernelMode, (PVOID*)&pFileRef, NULL);if (NT_SUCCESS(ntStatus)){//第一个参数的 PFILE_OBJECT 不能直接使用 PsReferenceProcessFilePointer返回的,可能//因为映射的方式不同,需要重新打开对应的文件取得 PFILE_OBJECTif (ValidateFileUsingCiValidateFileObject(pFileRef, Cert256Digest, SHA256_DEGIST_LENGTH)){ bAllow = true;KDPRINT("【IrpCertCheck】", "ValidateFileUsingCiValidateFileObject Pass\n");}else{KDPRINT("【IrpCertCheck】", "ValidateFileUsingCiValidateFileObject Failed\n");}ObDereferenceObject(pFileRef);}ZwClose(hFile);}}ObDereferenceObject(pFileObject);}else{KDPRINT("【IrpCertCheck】", "PsReferenceProcessFilePointer failed, code:0x%08x\n", ntStatus);}if (bAllow){ntStatus = STATUS_SUCCESS;}else{ntStatus = STATUS_ACCESS_DENIED;}pIrp->IoStatus.Information = 0;pIrp->IoStatus.Status = ntStatus;IoCompleteRequest(pIrp, IO_NO_INCREMENT);return ntStatus;
}extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{UNREFERENCED_PARAMETER(DriverObject);UNREFERENCED_PARAMETER(RegistryPath);DriverObject->DriverUnload = Unload;KDPRINT("【IrpCertCheck】", "IrpCertCheck Enter...\n");UNICODE_STRING usDeviceName = RTL_CONSTANT_STRING(IRP_CERT_CHECK_DEVICE_NAME);UNICODE_STRING usSddl = RTL_CONSTANT_STRING(L"D:P(A;;GA;;;WD)");//NTSTATUS ntStatus = IoCreateDeviceSecure(//        DriverObject,//        0,//        &usDeviceName,//        FILE_DEVICE_UNKNOWN,//        FILE_DEVICE_SECURE_OPEN,//        false,//        &usSddl,//        NULL,//        &g_pDeviceObject);NTSTATUS ntStatus = IoCreateDevice(DriverObject,0,&usDeviceName,FILE_DEVICE_UNKNOWN,FILE_DEVICE_SECURE_OPEN,false,&g_pDeviceObject);if (NT_SUCCESS(ntStatus)){UNICODE_STRING usSymbolic = RTL_CONSTANT_STRING(IRP_CERT_CHECK_DEVICE_SYMBOLIC_NAME);ntStatus = IoCreateSymbolicLink(&usSymbolic, &usDeviceName);if (NT_SUCCESS(ntStatus)){for (int i = 0 ; i < IRP_MJ_MAXIMUM_FUNCTION; i++){DriverObject->MajorFunction[i] = CommonDispatchRoutine;}DriverObject->MajorFunction[IRP_MJ_CREATE] = Create;DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceIoControl;}else{KDPRINT("【IrpCertCheck】", "IoCreateSymbolicLink failed, code:0x%08x\n", ntStatus);}}else{KDPRINT("【IrpCertCheck】", "IoCreateDevice failed, code:0x%08x\n", ntStatus);}return ntStatus;
}VOID Unload(_In_ struct _DRIVER_OBJECT* DriverObject)
{UNREFERENCED_PARAMETER(DriverObject);KDPRINT("【IrpCertCheck】", "IrpCertCheck Enter..\n");UNICODE_STRING usSymbolic = RTL_CONSTANT_STRING(IRP_CERT_CHECK_DEVICE_SYMBOLIC_NAME);IoDeleteSymbolicLink(&usSymbolic);if (g_pDeviceObject){IoDeleteDevice(g_pDeviceObject);g_pDeviceObject = NULL;}
}

4.2 应用层代码

#include <iostream>
#include <windows.h>
#include <iomanip>
#define CTL_CODE_TEST  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1000, METHOD_BUFFERED, FILE_ANY_ACCESS)
int main()
{HANDLE hDevice = CreateFile(L"\\\\.\\IrpCertCheck", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if (hDevice == INVALID_HANDLE_VALUE){std::string strInfo = "打开设备失败, 错误码:0x";std::cout << strInfo << std::setbase(16) << std::setfill('0') <<std::setw(8) << GetLastError() << std::endl;}else{DWORD nByteRetuen = 0;bool bOK = DeviceIoControl(hDevice, CTL_CODE_TEST, nullptr, 0, NULL, 0, &nByteRetuen, NULL);if (bOK){std::string strInfo = "发送控制码成功";std::cout << strInfo <<std::endl;}else{std::string strInfo = "发送控制码失败, 错误码:0x";std::cout << strInfo << std::setbase(16) << std::setfill('0') << std::setw(8) << GetLastError() << std::endl;}}
}

5. 代码逻辑及注意事项

  • 驱动中 IRP_MJ_CREATE 对应的 Create 函数调用 ValidateFileUsingCiValidateFileObject 来判断数字签名是否是允许的,不允许情况下直接返回失败,对应的应用层打开设备也就失败。
  • 驱动中 IRP_MJ_CREATE 对应的 Create 函数调用 ValidateFileUsingCiValidateFileObject 的第一个参数 fileObject 不能直接使用 PsReferenceProcessFilePointer 返回的 FILE_OBJECT,原因可能是返回的 FILE_OBJECT 对应的文件映射方式不同,需要再次使用 ZwOpenFile 返回的文件对象。
  • SignatureCheck.cpp 中的 ValidateFileUsingCiValidateFileObject 函数第 52 行至 66 行打印了验证返回的数字证书摘要,此摘要为证书的摘要而非 PE 文件的摘要,而此证书为整个证书链最顶层的证书。这个打印逻辑可以用来先获取证书摘要 ,再通过 第67 至 70 行的判断来决定证书是否通过验证。
  • main.cpp 中的 Cert256Digest 的数据即是上一条方法中获取的证书摘要。
  • 应用层逻辑为打开设备成功后送一个控制请求。

6. 实验效果

  使用代码中 Cert256Digest 摘要对应的数字证书签名应用层程序,如下:

  加载驱动后运行应用层程序,效果如下:

  可以看到验证通过,应用层控制码也发送成功,main.cpp 中的 Cert256Digest 数据即为图中红框所标部分。

  之后换掉用层程序的数字签名(SHA256 改为 SHA1)后验证如下:

  可以发现数字证书虽然验证成功,但证书不是我们指定证书,最终还是返回失败,应用层打开设备直接返回拒绝访问,目的达到。

7.注意事项

  •   CiValidateFileObject 由 ci.dll 导出,编译驱动时需要链接 ci.dll, 具体方法见《Windows驱动中校验数字签名(使用 ci.dll)》。
  •   在多重数字签名的情况下,CiValidateFileObject 只能返回一个签名的摘要,因此需要应用层只有一个数字签名。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/202730.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二叉树的右视图[中等]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给定一个二叉树的 根节点root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 示例 1: 输入: [1,2,3,null,5,null,4] 输出: [1,3,4] 示例 2: 输入: [1,null,3] 输出…

西工大计算机学院计算机系统基础实验一(函数编写11~14)

稳住心态不要慌&#xff0c;如果考试周冲突的话&#xff0c;可以直接复制这篇博客和上一篇博客西工大计算机学院计算机系统基础实验一&#xff08;函数编写1~10&#xff09;-CSDN博客最后的代码&#xff0c;然后直接提交&#xff0c;等熬过考试周之后回过头再慢慢做也可以。 第…

如何做好软文推广的选题?媒介盒子分享常见套路

选题是软文推广的重中之重&#xff0c;主题选得好&#xff0c;不仅能够戳到用户&#xff0c;提高转化率&#xff0c;还能让各位运营的写作效率大幅度提升&#xff0c;今天媒介盒子就来和大家分享软文选题的常见套路&#xff0c;助力各位品牌进行选题。 一、 根据产品选题 软文…

【AI-Fix】解决地图展示包leafmap在Jupyter NoteBook中地图不显示的问题

1. 问题描述 新创建的环境想使用leafmap在Jupyter中进行地图展示&#xff0c;结果发现运行完成之后不显示&#xff0c;不论怎么重启都没有办法显示出来&#xff0c;以经验来看&#xff0c;多半是缺了包了。 于是去leafmap的官方文档查找原因&#xff0c;一开始并没有发现什么问…

C++ 拷贝构造函数

目录 拷贝构造函数概述 拷贝构造函数特性 拷贝构造函数概述 当我们定义好一个类&#xff0c;不做任何处理时&#xff0c;编译器会自动生成以下6个默认成员函数&#xff1a; 默认成员函数&#xff1a;如果用户没有手动实现&#xff0c;则编译器会自动生成的成员函数。 同样&…

JavaWeb(十)

一、JavaWeb概述 Web&#xff1a;全球广域网&#xff0c;也称为万维网(www)&#xff0c;能够通过浏览器访问的网站。 JavaWeb&#xff1a;使用 Java技术进行web互联网开发。 二、JavaWeb 技术栈 2.1、B/S 架构 B/S 架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器…

解决找不到msvcr120.dll无法执行代码的4个方法,快来看看解决方法!

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中最常见的就是“缺少xxx.dll文件”。而msvcr120.dll就是其中之一。那么&#xff0c;msvcr120.dll到底是什么呢&#xff1f;它又有什么作用呢&#xff1f;本文将从多个方面对msvcr120.dll进行详细的解析…

华为鸿蒙爆发真实力!原生应用媲美iOS,使用流畅度将提升20至30%

随着华为鸿蒙原生应用开发计划的启动&#xff0c;一场席卷全球的科技浪潮正在涌动。鸿蒙生态的快速发展&#xff0c;吸引了无数企业和开发者的关注&#xff0c;他们纷纷拥抱这个新兴的生态系统&#xff0c;共同构建一个更加繁荣的鸿蒙世界。 华为鸿蒙原生应用开发计划引爆全球…

【电路笔记】-交流电路中的电阻器

交流电路中的电阻器 文章目录 交流电路中的电阻器1、概述2、交流电路中的电阻器示例 13、交流电路中的电阻器示例2 电阻器也可用于交流电源&#xff0c;其中消耗的电压、电流和功率以有效值给出。 1、概述 在之前的文章中&#xff0c;我们研究了电阻器及其连接&#xff0c;并使…

Leetcode刷题笔记题解(C++):BM11 链表相加(二)

思路&#xff1a;先对两个链表进行反转&#xff0c;反转求和注意进位运算&#xff0c;求和完成之后再进行反转得到结果 /*** struct ListNode {* int val;* struct ListNode *next;* ListNode(int x) : val(x), next(nullptr) {}* };*/ #include <cstddef> class Soluti…

Excel 删除空白行

目录 一. 方式一: 筛选删除二. 方式二: 定位条件三. 方式三: 隐藏非空白行&#xff0c;删除空白行 一. 方式一: 筛选删除 选中空白行对应的列&#xff0c;按下Ctrl Shift L&#xff0c;给列添加过滤条件。过滤出空白行&#xff0c;然后删除即可。 二. 方式二: 定位条件 按下…

【Qt】QLineEdit显示输入十六进制,位数不足时按照规则填充显示及每两个字符以空格填充

问题 在实际开发中&#xff0c;有时候需要对输入进行限制&#xff0c;一是更加合理&#xff0c;二是防止出现误操作。 比如&#xff1a; 使用Qt进行应用程序开发时&#xff0c;对单行编辑框QLineEdit控件&#xff0c;设置只可输入十六进制。 限制输入的方式常用且经典的是使用…

Linux常用指令详解

目录 前言&#xff1a; Linux的目录结构 Linux常用指令简介 whoami指令 ls指令 pwd指令 cd指令 tree指令 touch指令 mkdir指令 rmdir指令与rm指令 man指令 cp&#xff08;copy&#xff09;指令 mv&#xff08;move&#xff09;指令 cat指令 重定向及重定向的类型…

Redis——某马点评day03——part2:秒杀业务异步优化

异步秒杀思路 原本的流程是如下所示&#xff0c;必须从开始到创建订单成功才会返回响应。就像饭店里面从下单到上菜都是一个人在服务&#xff0c;就导致服务员利用率很低&#xff0c;后一个顾客要等到前一个顾客上完菜才可以下单。 最简单的优化就是加员工&#xff0c;一次性…

6.1810: Operating System Engineering 2023 <Lab3: page tables>

一、本节任务 实验环境&#xff1a; 二、要点 如何防止程序破坏内核或其他进程空间&#xff1f;隔离地址空间&#xff0c;进程只能读写自己的内存空间。 在保证隔离的同时&#xff0c;如何将多个地址空间复用到一个物理内存上&#xff1f;虚拟内存/页表。操作系统通过页表来为…

DDSP-SVC-3.0完全指南:一步步教你用AI声音开启音乐之旅

本教程教你怎么使用工具训练数据集推理出你想要转换的声音音频&#xff0c;并且教你处理剪辑伴奏和训练后的音频合并一起&#xff0c;快来试试看把&#xff01; 1.使用的工具 要想训练ai声音&#xff0c;首先需要有各种工具&#xff0c;还需要我们提供你需要训练的声音&#…

Avalonia中如何将View事件映射到ViewModel层

前言 前面的文章里面我们有介绍在Wpf中如何在View层将事件映射到ViewModel层的文章&#xff0c;传送门&#xff0c;既然WPF和Avalonia是两套不同的前端框架&#xff0c;那么WPF里面实现模式肯定在这边就用不了&#xff0c;本篇我们将分享一下如何在Avalonia前端框架下面将事件…

陀螺仪LSM6DSV16X与AI集成(2)----姿态解算

陀螺仪LSM6DSV16X与AI集成.2--姿态解算 概述视频教学样品申请完整代码下载欧拉角万向节死锁四元数法姿态解算双环PI控制器偏航角陀螺仪解析代码上位机通讯加速度演示陀螺仪工作方式主程序演示 概述 LSM6DSV16X包含三轴陀螺仪与三轴加速度计。 姿态有多种数学表示方式&#xff…

多人聊天室

多人聊天包 由于要先创建服务面板&#xff0c;接收客户端连接的信息&#xff0c;此代码使用顺序为先启动服务端&#xff0c;在启动客户端&#xff0c;服务端不用关&#xff0c;不然会报错。多运行几次客户端&#xff0c;实现单人聊天 1.创建服务面板 package yiduiy;import j…

【计算机二级MS Office】word(上)

这里写目录标题 文件选项卡保存和另存为属性检查文档 开始选项卡字体更改字体和字号设置中文和英文为两种不同字体的快捷方式介绍其余图标文本效果突出颜色如何挑选字体颜色字符底纹带圈字符字体对话框&#xff08;隐藏&#xff09; 段落 插入选项卡设计选项卡布局选项卡引用选…