g++ -o /cygdrive/d/temp/client.exe /cygdrive/d/temp/main.cpp -mno-cygwin
g++: The -mno-cygwin flag has been removed; use a mingw-targeted cross-compiler.
网上有人建议用i686-pc-cygwin-gcc/++-4.exe替换g++,我试了一下,还是一样的错误。
目前有个折中的方案,就是采用g++3而非4,于是运行/usr/bin/set-gcc-default-3.sh然后再编译,OK,通过。
Thursday, May 23, 2013
Saturday, May 18, 2013
系统进程和系统线程
系统进程即为ID = 4的名为"System"的进程,系统线程即所有运行于系统进程中的线程,驱动程序的DriverEntry即运行于系统线程之中。如果要创建系统线程,只要设置PsCreateSystemThread的ProcessHandle为NULL就行(使用PsCreateSystemThread创建线程需要特别注意的是线程函数,线程函数的最后一行通常是PsTerminateSystemThread(STATUS_))。
WaitForMultipleObjects 和 KeWaitForMultipleObjects 备忘
WaitForMultipleObjects 和 KeWaitForMultipleObjects 备忘
联系起来看这两个函数才能真正搞明白,先看内核函数,再看API
NTSTATUS
KeWaitForMultipleObjects(
IN ULONG Count,
IN PVOID Object[],
IN WAIT_TYPE WaitType,
IN KWAIT_REASON WaitReason,
IN KPROCESSOR_MODE WaitMode,
IN BOOLEAN Alertable,
IN PLARGE_INTEGER Timeout OPTIONAL,
IN PKWAIT_BLOCK WaitBlockArray OPTIONAL
);
KeWaitForMultipleObjects can return one of the following:
STATUS_SUCCESS
The caller specified WaitAll for the WaitType parameter and all dispatcher objects in the Object array have been set to the signaled state.
STATUS_ALERTED
The wait was interrupted to deliver an alert to the calling thread.
STATUS_USER_APC
The wait was interrupted to deliver a user asynchronous procedure call (APC) to the calling thread.
STATUS_TIMEOUT
A time-out occurred before the specified set of wait conditions was met. This value can be returned when an explicit time-out value of zero is specified, but the specified set of wait conditions cannot be met immediately.
STATUS_WAIT_0 through STATUS_WAIT_63
The caller specified WaitAny for WaitType and one of the dispatcher objects in the Object array has been set to the signaled state. The lower six bits of the return value encode the zero-based index of the object that satisfied the wait.
STATUS_ABANDONED_WAIT_0 through STATUS_ABANDONED_WAIT_63
The caller attempted to wait for a mutex that has been abandoned. The lower six bits of the return value encode the zero-based index of the mutex in the Object array.
Note that the NT_SUCCESS macro recognizes all of these status values as "success" values.
DWORD WINAPI WaitForMultipleObjects(
__in DWORD nCount,
__in const HANDLE* lpHandles,
__in BOOL bWaitAll,
__in DWORD dwMilliseconds
);
nCount表明想要等待的对象个数,最大值MAXIMUM_WAIT_OBJECTS(0x40)。它满足:
0 < MAXIMUM_WAIT_OBJECTS < WAIT_ABANDONED_0
后面会介绍原因。
lpHandles表明想要等待的对象数组,可以等待的对象有事件、信号量(注意一个信号量中有很多信号灯)、互斥量、线程,Process,Console input and so on。
If the function succeeds, the return value indicates the event that caused the function to return. It can be one of the following values. (Note that WAIT_OBJECT_0 is defined as 0 and WAIT_ABANDONED_0 is defined as 0x00000080L.):
WAIT_OBJECT_0 to (WAIT_OBJECT_0 + nCount– 1) =>
If bWaitAll is TRUE, the return value indicates that the state of all specified objects is signaled.
If bWaitAll is FALSE, the return value minus WAIT_OBJECT_0 indicates the lpHandles array index of the object that satisfied the wait. If more than one object became signaled during the call, this is the array index of the signaled object with the smallest index value of all the signaled objects.
WAIT_ABANDONED_0 to (WAIT_ABANDONED_0 + nCount– 1) =>
If bWaitAll is TRUE, the return value indicates that the state of all specified objects is signaled and at least one of the objects is an abandoned mutex object(see KeWaitForMultipleObjects for detail).
If bWaitAll is FALSE, the return value minus WAIT_ABANDONED_0 indicates the lpHandles array index of an abandoned mutex object that satisfied the wait. Ownership of the mutex object is granted to the calling thread, and the mutex is set to nonsignaled.
If a mutex was protecting persistent state information, you should check it for consistency.
联系起来看这两个函数才能真正搞明白,先看内核函数,再看API
NTSTATUS
KeWaitForMultipleObjects(
IN ULONG Count,
IN PVOID Object[],
IN WAIT_TYPE WaitType,
IN KWAIT_REASON WaitReason,
IN KPROCESSOR_MODE WaitMode,
IN BOOLEAN Alertable,
IN PLARGE_INTEGER Timeout OPTIONAL,
IN PKWAIT_BLOCK WaitBlockArray OPTIONAL
);
KeWaitForMultipleObjects can return one of the following:
STATUS_SUCCESS
The caller specified WaitAll for the WaitType parameter and all dispatcher objects in the Object array have been set to the signaled state.
STATUS_ALERTED
The wait was interrupted to deliver an alert to the calling thread.
STATUS_USER_APC
The wait was interrupted to deliver a user asynchronous procedure call (APC) to the calling thread.
STATUS_TIMEOUT
A time-out occurred before the specified set of wait conditions was met. This value can be returned when an explicit time-out value of zero is specified, but the specified set of wait conditions cannot be met immediately.
STATUS_WAIT_0 through STATUS_WAIT_63
The caller specified WaitAny for WaitType and one of the dispatcher objects in the Object array has been set to the signaled state. The lower six bits of the return value encode the zero-based index of the object that satisfied the wait.
STATUS_ABANDONED_WAIT_0 through STATUS_ABANDONED_WAIT_63
The caller attempted to wait for a mutex that has been abandoned. The lower six bits of the return value encode the zero-based index of the mutex in the Object array.
Note that the NT_SUCCESS macro recognizes all of these status values as "success" values.
DWORD WINAPI WaitForMultipleObjects(
__in DWORD nCount,
__in const HANDLE* lpHandles,
__in BOOL bWaitAll,
__in DWORD dwMilliseconds
);
nCount表明想要等待的对象个数,最大值MAXIMUM_WAIT_OBJECTS(0x40)。它满足:
0 < MAXIMUM_WAIT_OBJECTS < WAIT_ABANDONED_0
后面会介绍原因。
lpHandles表明想要等待的对象数组,可以等待的对象有事件、信号量(注意一个信号量中有很多信号灯)、互斥量、线程,Process,Console input and so on。
If the function succeeds, the return value indicates the event that caused the function to return. It can be one of the following values. (Note that WAIT_OBJECT_0 is defined as 0 and WAIT_ABANDONED_0 is defined as 0x00000080L.):
WAIT_OBJECT_0 to (WAIT_OBJECT_0 + nCount– 1) =>
If bWaitAll is TRUE, the return value indicates that the state of all specified objects is signaled.
If bWaitAll is FALSE, the return value minus WAIT_OBJECT_0 indicates the lpHandles array index of the object that satisfied the wait. If more than one object became signaled during the call, this is the array index of the signaled object with the smallest index value of all the signaled objects.
WAIT_ABANDONED_0 to (WAIT_ABANDONED_0 + nCount– 1) =>
If bWaitAll is TRUE, the return value indicates that the state of all specified objects is signaled and at least one of the objects is an abandoned mutex object(see KeWaitForMultipleObjects for detail).
If bWaitAll is FALSE, the return value minus WAIT_ABANDONED_0 indicates the lpHandles array index of an abandoned mutex object that satisfied the wait. Ownership of the mutex object is granted to the calling thread, and the mutex is set to nonsignaled.
If a mutex was protecting persistent state information, you should check it for consistency.
使用信号灯同步线程
#include <windows.h>
#include <process.h> // _beginthread, _endthread
#include <stdio.h>
/*
* 可以理解信号灯内部有N个灯泡。
* 如果有一个灯泡亮着,就代表信号灯处于激发状态,如果全灭,则代表信号灯处于未激发状态。
*/
// 创建线程
UINT WINAPI Thread1(LPVOID para)
{
printf("进入Thread1......\n");
HANDLE* phSemaphore = (HANDLE*)para;
printf("将等待5秒......\n");
// 等待5秒
Sleep(5000);
printf("离开Thread1......\n");
// 参数1:信号灯句柄
// 参数2:本次操作增加的计数
// 参数3:记录以前的计数
// 将信号灯计数器加1,是之处于激发状态
ReleaseSemaphore(*phSemaphore, 1, NULL);
return 0;
}
int main()
{
/*
* 信号灯计数为0时,信号灯为nonsingled状态。
* 也就是说这时用WaitForSingleObject时会等待直到当前数大于0(或超时),
* 如果不为0时,用WaitForSingleObject就马上返回并把当前数减一;
*/
// 参数1:LPSECURITY_ATTRIBUTES pEventAttributes 安区参数 一般用户不用考虑它,传入NULL。
// 参数2:初始化计数个数。
// 参数3:计数器最大个数。
// 参数4:指定事件的对象的名称,是一个以0结束的字符串指针。名称的字符格式限定在MAX_PATH之内。名字是对大小写敏感的。
// 创建同步事件
HANDLE hSemaphore = CreateSemaphore(NULL, 2, 2, NULL); // 将传入Thread1的参数
// 此时的信号灯计数为2,处于触发状态
WaitForSingleObject(hSemaphore, INFINITE);
// 此时的信号灯计数为1,处于触发状态
WaitForSingleObject(hSemaphore, INFINITE);
// 此时的信号灯计数为0,处于未触发状态
// 第1个参数:安全属性,NULL为默认安全属性
// 第2个参数:指定线程堆栈的大小。如果为0,则线程堆栈大小和创建它的线程的相同。一般用0
// 第3个参数:指定线程函数的地址,也就是线程调用执行的函数地址(用函数名称即可,函数名称就表示地址)
// 第4个参数:传递给线程的参数的指针,可以通过传入对象的指针,在线程函数中再转化为对应类的指针
// 第5个参数:线程初始状态,0:立即运行;CREATE_SUSPEND:suspended(悬挂)
// 第6个参数:用于记录线程ID的地址
// 开启新线程,并将同步事件句柄指针传递给新线程
HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, Thread1, &hSemaphore, 0, NULL);
// 等待该事件激发
WaitForSingleObject(hSemaphore, INFINITE);
CloseHandle(hThread); // 关闭内核对象
system("PAUSE");
return 0;
}
#include <process.h> // _beginthread, _endthread
#include <stdio.h>
/*
* 可以理解信号灯内部有N个灯泡。
* 如果有一个灯泡亮着,就代表信号灯处于激发状态,如果全灭,则代表信号灯处于未激发状态。
*/
// 创建线程
UINT WINAPI Thread1(LPVOID para)
{
printf("进入Thread1......\n");
HANDLE* phSemaphore = (HANDLE*)para;
printf("将等待5秒......\n");
// 等待5秒
Sleep(5000);
printf("离开Thread1......\n");
// 参数1:信号灯句柄
// 参数2:本次操作增加的计数
// 参数3:记录以前的计数
// 将信号灯计数器加1,是之处于激发状态
ReleaseSemaphore(*phSemaphore, 1, NULL);
return 0;
}
int main()
{
/*
* 信号灯计数为0时,信号灯为nonsingled状态。
* 也就是说这时用WaitForSingleObject时会等待直到当前数大于0(或超时),
* 如果不为0时,用WaitForSingleObject就马上返回并把当前数减一;
*/
// 参数1:LPSECURITY_ATTRIBUTES pEventAttributes 安区参数 一般用户不用考虑它,传入NULL。
// 参数2:初始化计数个数。
// 参数3:计数器最大个数。
// 参数4:指定事件的对象的名称,是一个以0结束的字符串指针。名称的字符格式限定在MAX_PATH之内。名字是对大小写敏感的。
// 创建同步事件
HANDLE hSemaphore = CreateSemaphore(NULL, 2, 2, NULL); // 将传入Thread1的参数
// 此时的信号灯计数为2,处于触发状态
WaitForSingleObject(hSemaphore, INFINITE);
// 此时的信号灯计数为1,处于触发状态
WaitForSingleObject(hSemaphore, INFINITE);
// 此时的信号灯计数为0,处于未触发状态
// 第1个参数:安全属性,NULL为默认安全属性
// 第2个参数:指定线程堆栈的大小。如果为0,则线程堆栈大小和创建它的线程的相同。一般用0
// 第3个参数:指定线程函数的地址,也就是线程调用执行的函数地址(用函数名称即可,函数名称就表示地址)
// 第4个参数:传递给线程的参数的指针,可以通过传入对象的指针,在线程函数中再转化为对应类的指针
// 第5个参数:线程初始状态,0:立即运行;CREATE_SUSPEND:suspended(悬挂)
// 第6个参数:用于记录线程ID的地址
// 开启新线程,并将同步事件句柄指针传递给新线程
HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, Thread1, &hSemaphore, 0, NULL);
// 等待该事件激发
WaitForSingleObject(hSemaphore, INFINITE);
CloseHandle(hThread); // 关闭内核对象
system("PAUSE");
return 0;
}
Thursday, May 16, 2013
Concepts of Digital Signature and Windows Code Signing
Authenticode:
software publishers use authenticode to sign either a file or a collection of files(such as a driver package)
Microsoft Authenticode:
Authenticode belong to MS. (I believe it's a key which must be safely saved by microsoft)
digital certificate:
Also refered as signing certificate or authenticode certificate, it identify a publisher(exactly, the signer) and the issuer(CA) and it at least contains a public key and an authenticode. (I believe the info about the public key can be queried from CA)
thumbprint:
a (cryptographic) hash of a file(sha256? or something else?) or a package, the thumbprint can be used as the source of private key.
digital signature:
windows first calc the thumbprint of the file, then use the pulic key(public key and its publisher must previous verified) to decry the digital signature, if they two match, the digital signature is OK.
Trusted Publishers certificate store:
HKEY_LOCAL_MACHINE
Software
Microsoft
SystemCertificates
TrustedPublisher
or the curr user:
HKCU
Software
Microsoft
SystemCertificates
TrustedPublisher
and all certificates in HKEY_LOCAL_MACHINE are inherited by current user
Trusted Root Certification Authorities certificate store:
HKEY_LOCAL_MACHINE
Software
Microsoft
SystemCertificates
CA
Refer:
http://msdn.microsoft.com/en-us/library/windows/hardware/ff543743%28v=vs.85%29.aspx
http://technet.microsoft.com/en-us/library/cc962021.aspx
http://office.microsoft.com/en-us/excel-help/how-to-tell-if-a-digital-signature-is-trustworthy-HA001230875.aspx : How to tell if a digital signature of Office doc is trustworthy
software publishers use authenticode to sign either a file or a collection of files(such as a driver package)
Microsoft Authenticode:
Authenticode belong to MS. (I believe it's a key which must be safely saved by microsoft)
digital certificate:
Also refered as signing certificate or authenticode certificate, it identify a publisher(exactly, the signer) and the issuer(CA) and it at least contains a public key and an authenticode. (I believe the info about the public key can be queried from CA)
thumbprint:
a (cryptographic) hash of a file(sha256? or something else?) or a package, the thumbprint can be used as the source of private key.
digital signature:
windows first calc the thumbprint of the file, then use the pulic key(public key and its publisher must previous verified) to decry the digital signature, if they two match, the digital signature is OK.
Trusted Publishers certificate store:
HKEY_LOCAL_MACHINE
Software
Microsoft
SystemCertificates
TrustedPublisher
or the curr user:
HKCU
Software
Microsoft
SystemCertificates
TrustedPublisher
and all certificates in HKEY_LOCAL_MACHINE are inherited by current user
Trusted Root Certification Authorities certificate store:
HKEY_LOCAL_MACHINE
Software
Microsoft
SystemCertificates
CA
Refer:
http://msdn.microsoft.com/en-us/library/windows/hardware/ff543743%28v=vs.85%29.aspx
http://technet.microsoft.com/en-us/library/cc962021.aspx
http://office.microsoft.com/en-us/excel-help/how-to-tell-if-a-digital-signature-is-trustworthy-HA001230875.aspx : How to tell if a digital signature of Office doc is trustworthy
设备读写方法小结
DO_BUFFERED_IO法:
在IoCreateDev的时候需要设置返回的设备对象的pDevObj->Flags |= DO_BUFFERED_IO
由于所有进程共享高2G/1G的虚拟内存,为了避免进程切换带来的风险,所以用户在WriteFile中提供的缓冲区需要被拷贝到内核模式下的缓冲区中,该缓冲区就是AssociatedIRP.SystemBuffer子域,当前层驱动需要读写该缓冲区的多少字节由该层IO_STACK_LOCATION中的Paraments.Read.Length/Paraments.Write.Length决定。
至于实际读写到多少字节,由IRP的IoStatus.Information设置
DO_DIRECT_IO法:
//确定pDevObj->Flags是DO_DIRECT_IO
IrpSp = IoGetCurrentIrpStackLocation(Irp);
ReadLength = IrpSp->Parameters.Read.Length;
MdlLength = MmGetMdlByteCount(Irp->MdlAddress);
//确定ReadLength == MdlLength
MdlOffset = MmGetMdlByteOffset(Irp->MdlAddress);
//缓冲区的虚拟地址
MdlAddress = MmGetMdlVirtualAddress(Irp->MdlAddress); //MdlAddress == MdlAddress->StartVA + MdlAddress->ByteOffset
//获得虚拟缓冲区在内核模式下的映射地址
KernelAddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress);
//设置KernelAddress的内容;
//笔者曾经做过实验:给定一个足够大的ReadLength,memset都能正常工作, 可见映射地址是连续内存
memset(KernelAddress,0xAB,MdlLength)
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = ReadLength;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
其他方法:
直接访问Irp->UserBuffer,这种方法很危险,所以要访问UserBuffer的时候,先用ProbeForWrite/ProbeForRead探测一下,如果这两个函数抛出异常,代表不可访问,读写失败。
设备在创建的时候,其读写方法(应该)就是上述三种方法之一。但是这里有一个"矛盾"的地方,为了说明问题,必须先详细介绍以下函数:
BOOL WINAPI DeviceIoControl(
__in HANDLE hDevice,
__in DWORD dwIoControlCode,
__in_opt LPVOID lpInBuffer,
__in DWORD nInBufferSize,
__out_opt LPVOID lpOutBuffer,
__in DWORD nOutBufferSize,
__out_opt LPDWORD lpBytesReturned,
__inout_opt LPOVERLAPPED lpOverlapped
);
现在介绍dwIoControlCode,程序员可以直接用系统预定义的值(winioctl.h,比如IOCTL_DISK_FORMAT_TRACKS),也可以按照如下规范定义:
#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)
DeviceType
Identifies the device type. This value must match the value that is set in the DeviceType member of the driver's DEVICE_OBJECT structure.
FunctionCode
Identifies the function to be performed by the driver. Values of less than 0x800 are reserved for Microsoft. Values of 0x800 and higher can be used by vendors.
TransferType
Indicates how the system will pass data between the caller of DeviceIoControl (or IoBuildDeviceIoControlRequest) and the driver that handles the IRP. Use one of the following system-defined constants:
METHOD_BUFFERED
METHOD_IN_DIRECT or METHOD_OUT_DIRECT
METHOD_NEITHER
读者可能已经发现,DeviceIoControl有两个缓冲区需要设置读写方式,那么TransferType就需要包含这两个信息,在驱动中需要这么处理:
如果TransferType==METHOD_BUFFERED,那么输入输出都采用AssociatedIRP.SystemBuffer,(ZXXU: 这个buf的长度要求是MAX(nInBufferSize,nOutBufferSize)
如果TransferType==METHOD_IN_DIRECT(暗示hDevice是以读方式打开),那么输入采用AssociatedIRP.SystemBuffer,输出采用MDL;METHOD_OUT_DIRECT同理
如果TransferType==METHOD_NEITHER,那么输入缓冲区采用Parameters.DeviceIoControl.Type3InputBuffer表示,输出采用Irp->UserBuffer
在IoCreateDev的时候需要设置返回的设备对象的pDevObj->Flags |= DO_BUFFERED_IO
由于所有进程共享高2G/1G的虚拟内存,为了避免进程切换带来的风险,所以用户在WriteFile中提供的缓冲区需要被拷贝到内核模式下的缓冲区中,该缓冲区就是AssociatedIRP.SystemBuffer子域,当前层驱动需要读写该缓冲区的多少字节由该层IO_STACK_LOCATION中的Paraments.Read.Length/Paraments.Write.Length决定。
至于实际读写到多少字节,由IRP的IoStatus.Information设置
DO_DIRECT_IO法:
//确定pDevObj->Flags是DO_DIRECT_IO
IrpSp = IoGetCurrentIrpStackLocation(Irp);
ReadLength = IrpSp->Parameters.Read.Length;
MdlLength = MmGetMdlByteCount(Irp->MdlAddress);
//确定ReadLength == MdlLength
MdlOffset = MmGetMdlByteOffset(Irp->MdlAddress);
//缓冲区的虚拟地址
MdlAddress = MmGetMdlVirtualAddress(Irp->MdlAddress); //MdlAddress == MdlAddress->StartVA + MdlAddress->ByteOffset
//获得虚拟缓冲区在内核模式下的映射地址
KernelAddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress);
//设置KernelAddress的内容;
//笔者曾经做过实验:给定一个足够大的ReadLength,memset都能正常工作, 可见映射地址是连续内存
memset(KernelAddress,0xAB,MdlLength)
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = ReadLength;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
其他方法:
直接访问Irp->UserBuffer,这种方法很危险,所以要访问UserBuffer的时候,先用ProbeForWrite/ProbeForRead探测一下,如果这两个函数抛出异常,代表不可访问,读写失败。
设备在创建的时候,其读写方法(应该)就是上述三种方法之一。但是这里有一个"矛盾"的地方,为了说明问题,必须先详细介绍以下函数:
BOOL WINAPI DeviceIoControl(
__in HANDLE hDevice,
__in DWORD dwIoControlCode,
__in_opt LPVOID lpInBuffer,
__in DWORD nInBufferSize,
__out_opt LPVOID lpOutBuffer,
__in DWORD nOutBufferSize,
__out_opt LPDWORD lpBytesReturned,
__inout_opt LPOVERLAPPED lpOverlapped
);
现在介绍dwIoControlCode,程序员可以直接用系统预定义的值(winioctl.h,比如IOCTL_DISK_FORMAT_TRACKS),也可以按照如下规范定义:
#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)
DeviceType
Identifies the device type. This value must match the value that is set in the DeviceType member of the driver's DEVICE_OBJECT structure.
FunctionCode
Identifies the function to be performed by the driver. Values of less than 0x800 are reserved for Microsoft. Values of 0x800 and higher can be used by vendors.
TransferType
Indicates how the system will pass data between the caller of DeviceIoControl (or IoBuildDeviceIoControlRequest) and the driver that handles the IRP. Use one of the following system-defined constants:
METHOD_BUFFERED
METHOD_IN_DIRECT or METHOD_OUT_DIRECT
METHOD_NEITHER
读者可能已经发现,DeviceIoControl有两个缓冲区需要设置读写方式,那么TransferType就需要包含这两个信息,在驱动中需要这么处理:
如果TransferType==METHOD_BUFFERED,那么输入输出都采用AssociatedIRP.SystemBuffer,(ZXXU: 这个buf的长度要求是MAX(nInBufferSize,nOutBufferSize)
如果TransferType==METHOD_IN_DIRECT(暗示hDevice是以读方式打开),那么输入采用AssociatedIRP.SystemBuffer,输出采用MDL;METHOD_OUT_DIRECT同理
如果TransferType==METHOD_NEITHER,那么输入缓冲区采用Parameters.DeviceIoControl.Type3InputBuffer表示,输出采用Irp->UserBuffer
Monday, May 13, 2013
物理内存、虚拟内存、共享内存
如果某物理内存页既映射到进程A的虚拟内存中,也映射到进程B的虚拟内存中,那么该内存页即为进程间共享内存。
对32位应用程序而言,所有进程的高2G或者高1G的虚拟内存的映射方式完全一致。
虚拟内存按照是否可以swap出去,可以分为分页内存和非分页内存, 非分页内存可以在各个中断请求级上用,分页内存只能用在DISPATCH_LEVEL(不包括)之下的请求级。以下是这两种内存的使用方法:
#define PAGEDCODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
#define INITCODE code_seg("INIT")
#define PAGEDDATA data_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATA data_seg("INIT")
#pragma PAGEDCODE
VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject)
另外,分配内存的时候可以指定是分配哪种内存:
ExAllocatePool(PagedPool, 1024)
WDM分层模型
WDM为分层驱动模型,至少两层,下层控制PDOs,上层控制FDOs,还可以有若干过滤驱动,同一层的DOs有相同的StackSize域(物理层的StackSize总是为1),即当前设备栈的大小。
具体到任何一个物理设备或者虚拟设备,它的每一层DO构成它的设备栈,该设备栈为通过AttachedDevice联系的链栈,链首为PDO。要将当前设备压栈,需要使用PDEVICE_OBJECT
PDEVICE_OBJECT IoAttachDeviceToDeviceStack(
IN PDEVICE_OBJECT SourceDevice,
IN PDEVICE_OBJECT TargetDevice
);
函数,该函数的SourceDevice可以是链栈中任何一个对象,返回的一定是栈顶的对象(详见MSDN和张帆的HelloWDM).
为了更好地查看链栈,请翻阅<<张帆P110>>的DeviceTree:
首先由Driver\PnpManager创建\Device\00000040(PDO),然后有一个 \Device\MyWDMDevice附加在其上(注意\Device\MyWDMDevice的命名管理方式并不好,最好是\Device\MyWDMDevice1,\Device\MyWDMDevice2。。。,因为系统中可能有多个FDO设备)。
具体到任何一个物理设备或者虚拟设备,它的每一层DO构成它的设备栈,该设备栈为通过AttachedDevice联系的链栈,链首为PDO。要将当前设备压栈,需要使用PDEVICE_OBJECT
PDEVICE_OBJECT IoAttachDeviceToDeviceStack(
IN PDEVICE_OBJECT SourceDevice,
IN PDEVICE_OBJECT TargetDevice
);
函数,该函数的SourceDevice可以是链栈中任何一个对象,返回的一定是栈顶的对象(详见MSDN和张帆的HelloWDM).
为了更好地查看链栈,请翻阅<<张帆P110>>的DeviceTree:
首先由Driver\PnpManager创建\Device\00000040(PDO),然后有一个 \Device\MyWDMDevice附加在其上(注意\Device\MyWDMDevice的命名管理方式并不好,最好是\Device\MyWDMDevice1,\Device\MyWDMDevice2。。。,因为系统中可能有多个FDO设备)。
Sunday, May 12, 2013
AttachedDevice和NextDevice
VOID DetachSomeDevFromTargetDriver(PDRIVER_OBJECT pOwnerDrvObject)
{
PDEVICE_OBJECT pDevObj,pNextDev;
if (pOwnerDrvObject)
{
//取第一个设备
pNextDev=pOwnerDrvObject->DeviceObject;
do
{
pDevObj=pNextDev;
while(pDevObj->AttachedDevice)
{
if (IsSomeDevice(pDevObj->AttachedDevice))
{
DbgPrint("Detaching Device:0x%08X\n",pDevObj->AttachedDevice);
IoDetachDevice(pDevObj); }
else
{
pDevObj=pDevObj->AttachedDevice;//若不是,取上一层设备
}
}
pNextDev=pNextDev->NextDevice;
} while(pNextDev);
}
}
{
PDEVICE_OBJECT pDevObj,pNextDev;
if (pOwnerDrvObject)
{
//取第一个设备
pNextDev=pOwnerDrvObject->DeviceObject;
do
{
pDevObj=pNextDev;
while(pDevObj->AttachedDevice)
{
if (IsSomeDevice(pDevObj->AttachedDevice))
{
DbgPrint("Detaching Device:0x%08X\n",pDevObj->AttachedDevice);
IoDetachDevice(pDevObj); }
else
{
pDevObj=pDevObj->AttachedDevice;//若不是,取上一层设备
}
}
pNextDev=pNextDev->NextDevice;
} while(pNextDev);
}
}
DriverEntry
NTSTATUS __stdcall DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath):
_DRIVER_OBJECT:
DriverName: 一般为\Driver\<驱动名称>(与设备命名规则类似:\Device\<设备名称>)
PUNICODE_STRING HardwareDatabase:指向注册表中硬件配置信息的路径,用Unicode字符串表示。该字符串内容一般为\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM。
RegistryPath:
8660f008 5c 00 52 00 45 00 47 00-49 00 53 00 54 00 52 00 \.R.E.G.I.S.T.R.
8660f018 59 00 5c 00 4d 00 41 00-43 00 48 00 49 00 4e 00 Y.\.M.A.C.H.I.N.
8660f028 45 00 5c 00 53 00 59 00-53 00 54 00 45 00 4d 00 E.\.S.Y.S.T.E.M.
8660f038 5c 00 43 00 6f 00 6e 00-74 00 72 00 6f 00 6c 00 \.C.o.n.t.r.o.l.
8660f048 53 00 65 00 74 00 30 00-30 00 31 00 5c 00 53 00 S.e.t.0.0.1.\.S.
8660f058 65 00 72 00 76 00 69 00-63 00 65 00 73 00 5c 00 e.r.v.i.c.e.s.\.
8660f068 48 00 65 00 6c 00 6c 00-6f 00 57 00 44 00 4d 00 H.e.l.l.o.W.D.M.
节选自<<张帆>>:驱动加载的时候,系统进程启动新的线程,利用对象管理器创建_DRIVER_OBJECT,利用配置管理器获取RegistryPath(如果以后想用这个字符串,需要复制到安全的地方)
Friday, May 10, 2013
Aeroplan简介
Groupe Aeroplan started to conduct business under the new brand name Aimia. While the names and brand identities of our consumer facing brands Aeroplan in Canada, Nectar in Chile, Italy and the UK, and so on
通过加拿大航空或者合作联盟购票可以获得积分((订往返可以获得更多积分)
预定酒店、乘船、租车、泊车、渡假套餐、火车旅行(与Rail Europe合作)、商场购物等可以获得积分(需要有合作关系)
aeroplan配置可以上aeroplan.com/profile
aeroplan的合作者可以上aeroplan.com/earnmiles查。
aeroplan使用积分可以上aeroplan.com/useyourmiles
Wednesday, May 8, 2013
Sunday, May 5, 2013
DosDevices
DosDevices
there is one global \DosDevices directory and multiple local \DosDevices directories.The global \DosDevices directory holds the MS-DOS device names that are visible system-wide.A local \DosDevices directory holds MS-DOS device names that are visible only in a particular local DosDevices context(see below to understand local DosDevices context).
each logon session has its own local DosDevices context,If a user mounts a network drive as X:, only himself can see this。
Each thread has a current DosDevices context, which can change over the lifetime of a thread.
when the object manager looks up a name in \DosDevices, it first searches the local \DosDevices directory, and then the global \DosDevices directory.
A driver that must create its MS-DOS device names in the global \DosDevices directory can do so by creating its symbolic links in a standard driver routine that is guaranteed to run in a system thread context, such as DriverEntry{RtlInitUnicodeString(&symbolicLinkName, L"\\DosDevices\\Global\\SymbolicLinkName");}.
When a thread in a particular local DosDevices context sends an IOCTL, the driver's DispatchDeviceControl is called from within the current thread context. A driver can create MS-DOS device names in a local \DosDevices directories by creating the symbolic link in response to the IOCTL.
On Windows XP and later, local \DosDevices directories are identified by the AuthenticationID for the logon session's access token.
there is one global \DosDevices directory and multiple local \DosDevices directories.The global \DosDevices directory holds the MS-DOS device names that are visible system-wide.A local \DosDevices directory holds MS-DOS device names that are visible only in a particular local DosDevices context(see below to understand local DosDevices context).
each logon session has its own local DosDevices context,If a user mounts a network drive as X:, only himself can see this。
Each thread has a current DosDevices context, which can change over the lifetime of a thread.
when the object manager looks up a name in \DosDevices, it first searches the local \DosDevices directory, and then the global \DosDevices directory.
A driver that must create its MS-DOS device names in the global \DosDevices directory can do so by creating its symbolic links in a standard driver routine that is guaranteed to run in a system thread context, such as DriverEntry{RtlInitUnicodeString(&symbolicLinkName, L"\\DosDevices\\Global\\SymbolicLinkName");}.
When a thread in a particular local DosDevices context sends an IOCTL, the driver's DispatchDeviceControl is called from within the current thread context. A driver can create MS-DOS device names in a local \DosDevices directories by creating the symbolic link in response to the IOCTL.
On Windows XP and later, local \DosDevices directories are identified by the AuthenticationID for the logon session's access token.
Windows驱动编写注意事项
有些C语言运行时函数如strcpy可以用在驱动中(因为该函数不依赖Windows API实现),但是建议使用内核态的运行时函数。
C++可以实现操作符重载,所以重载的new操作符有可能用在驱动中,但是驱动开发不建议使用C++.
开发时尽量采用checked版本,这样可以用windbg进行源码级调试
makefile文件一般只包含一行(!INCLUDE $(NTMAKEENV)\makefile.def)即可,如果要修改请参考ms-help://MS.WDK.v10.7600.090618.01/DevTest_g/hh/DevTest_g/Build_Ref_31c38413-df20-4483-aa9c-16353d9d82fa.xml.htm或者在wdk中搜索MAKEFILE.INC然后链接过去
如果不明确规定,当前目录下所有源码都会被编译,否则可以编写dirs文件,下面是一个例子:
DIRS= \
1394 \
audio \
AVStream \
biometrics
dirs文件和sources文件配合使用,详见ms-help://MS.WDK.v10.7600.090618.01/DevTest_g/hh/DevTest_g/Build_Ref_78947534-363a-4a43-bc39-5e26c9c05449.xml.htm或者在wdk中搜索TARGETNAME后链接过去
还有一个MAKEFILE.INC可选文件,在wdk中搜索MAKEFILE.INC可以获得帮助信息
由于DbgPrint无法在FreeBuild中使用,所以建议使用KdPrint,该宏定义如下:
#if DBG
#define KdPrint(_x_) DbgPrint _x_
#else
#define KdPrint(_x_)
#endif
所以KdPrint((_x_))会转成DbgPrint(_x_),如果用KdPrint(_x_)则会出错。
符号链接最好用"\?\"的形式,为了兼容以前的驱动,'\DosDevices\'的形式在逻辑是等效的,从应用程序中访问驱动用"\.\"的形式。
如果驱动需要频繁地分配和回收固定大小的内存,为了避免产生内存碎片,windows提供了Lookaside内存。
RtlCopyMemory用于源和目标内存不重合的情况,RtlMoveMemory用于二者有交叉的情况,二者的关系相当于memcpy和memmove
C++可以实现操作符重载,所以重载的new操作符有可能用在驱动中,但是驱动开发不建议使用C++.
开发时尽量采用checked版本,这样可以用windbg进行源码级调试
makefile文件一般只包含一行(!INCLUDE $(NTMAKEENV)\makefile.def)即可,如果要修改请参考ms-help://MS.WDK.v10.7600.090618.01/DevTest_g/hh/DevTest_g/Build_Ref_31c38413-df20-4483-aa9c-16353d9d82fa.xml.htm或者在wdk中搜索MAKEFILE.INC然后链接过去
如果不明确规定,当前目录下所有源码都会被编译,否则可以编写dirs文件,下面是一个例子:
DIRS= \
1394 \
audio \
AVStream \
biometrics
dirs文件和sources文件配合使用,详见ms-help://MS.WDK.v10.7600.090618.01/DevTest_g/hh/DevTest_g/Build_Ref_78947534-363a-4a43-bc39-5e26c9c05449.xml.htm或者在wdk中搜索TARGETNAME后链接过去
还有一个MAKEFILE.INC可选文件,在wdk中搜索MAKEFILE.INC可以获得帮助信息
由于DbgPrint无法在FreeBuild中使用,所以建议使用KdPrint,该宏定义如下:
#if DBG
#define KdPrint(_x_) DbgPrint _x_
#else
#define KdPrint(_x_)
#endif
所以KdPrint((_x_))会转成DbgPrint(_x_),如果用KdPrint(_x_)则会出错。
符号链接最好用"\?\"的形式,为了兼容以前的驱动,'\DosDevices\'的形式在逻辑是等效的,从应用程序中访问驱动用"\.\"的形式。
如果驱动需要频繁地分配和回收固定大小的内存,为了避免产生内存碎片,windows提供了Lookaside内存。
RtlCopyMemory用于源和目标内存不重合的情况,RtlMoveMemory用于二者有交叉的情况,二者的关系相当于memcpy和memmove
Friday, May 3, 2013
windows架构综述
首先声明,本文并非原创,只是对大牛先辈们的文章的收集整理,如有错误,欢迎指正。另外,如无特别说明,本文的概念都适用于32位的windows xp和windows 2003(其他的我不知道)。
Ring3层:
即应用层,该层的应用程序使用各自的子系统进行编程,最核心的子系统是Win32子系统,其他子系统如Posix的API最终要转化为Win32 API;最值得一提的是Wow(windows on windows)子系统,该子系统是为了16位windows程序而设计(同理,该子系统下的程序最终转化为Win32调用)。
Win32 API主要分为三类:User函数(管理图形组件如窗体、菜单、按钮)、GDI函数(提供函数在物理设备上画图)、KERNEL函数(管理进程、文件和通信等)。不管哪类API,都要转化为Native API,(所有)Native API都定义在Ntdll.dll中。NativeAPI通过int 2e(或者sysenter)软中断进行系统调用, 它定义在Ring3,是进入Ring0的窗口。
系统调用会存放在两个表中,第一个是SSDT(KeServiceDescriptorTable),第二个是Shadow SSDT(KeServiceDescriptorTableShadow),前者实现KERNEL函数需要的功能(在ntoskrnl.exe、ntkrnlmp.exe(对称处理器)、ntkrnlpa.exe(物理地址扩展)或者ntkrpamp.exe中),后者实现User函数和GDI函数的功能(放在win32k.sys,后者应该是前者的超集)。Native API通常由Zw或者Nt开头,与系统调用的名字相同(Zw和Nt开头函数在r3(ntdll)中的定义是完全一致的,但在r0中,Nt开头的函数是真正的实现,Zw开头的函数通过SSDT调用其对应的Nt函数)。
Ring0层:
Ring0层分为执行体、内核和驱动程序。
执行体分为对象管理器、进程管理器、虚拟内存管理器、I/O管理器和配置管理器。
对象管理器管理Windows定义的某些数据结构的创建、管理和回收,这些结构中的某些成员可以被读写,相当于公有成员,其他的相当于私有成员,对象管理器提供例程来访问私有成员。
I/O管理器负责创建IRP(IO请求包),IRP由用户或者系统发起,由IO管理器创建,由驱动程序响应(当然,驱动也可以进一步创建IRP给更底层的驱动)。
配置管理器管理注册表。
和执行体相比,内核是非常小的,它主要提供如下功能:
线程调度
对多处理器同步
对中断、异常和陷阱响应
驱动程序用来操作设备。有些设备是虚拟出来的比如虚拟光驱设备,有些设备是真实设备派生出来的设备,比如卷设备,一个分区对应一个卷设备,所有对卷设备的操作由卷驱动最终转化为对磁盘驱动的请求。
Ring3层:
即应用层,该层的应用程序使用各自的子系统进行编程,最核心的子系统是Win32子系统,其他子系统如Posix的API最终要转化为Win32 API;最值得一提的是Wow(windows on windows)子系统,该子系统是为了16位windows程序而设计(同理,该子系统下的程序最终转化为Win32调用)。
Win32 API主要分为三类:User函数(管理图形组件如窗体、菜单、按钮)、GDI函数(提供函数在物理设备上画图)、KERNEL函数(管理进程、文件和通信等)。不管哪类API,都要转化为Native API,(所有)Native API都定义在Ntdll.dll中。NativeAPI通过int 2e(或者sysenter)软中断进行系统调用, 它定义在Ring3,是进入Ring0的窗口。
系统调用会存放在两个表中,第一个是SSDT(KeServiceDescriptorTable),第二个是Shadow SSDT(KeServiceDescriptorTableShadow),前者实现KERNEL函数需要的功能(在ntoskrnl.exe、ntkrnlmp.exe(对称处理器)、ntkrnlpa.exe(物理地址扩展)或者ntkrpamp.exe中),后者实现User函数和GDI函数的功能(放在win32k.sys,后者应该是前者的超集)。Native API通常由Zw或者Nt开头,与系统调用的名字相同(Zw和Nt开头函数在r3(ntdll)中的定义是完全一致的,但在r0中,Nt开头的函数是真正的实现,Zw开头的函数通过SSDT调用其对应的Nt函数)。
Ring0层:
Ring0层分为执行体、内核和驱动程序。
执行体分为对象管理器、进程管理器、虚拟内存管理器、I/O管理器和配置管理器。
对象管理器管理Windows定义的某些数据结构的创建、管理和回收,这些结构中的某些成员可以被读写,相当于公有成员,其他的相当于私有成员,对象管理器提供例程来访问私有成员。
I/O管理器负责创建IRP(IO请求包),IRP由用户或者系统发起,由IO管理器创建,由驱动程序响应(当然,驱动也可以进一步创建IRP给更底层的驱动)。
配置管理器管理注册表。
和执行体相比,内核是非常小的,它主要提供如下功能:
线程调度
对多处理器同步
对中断、异常和陷阱响应
驱动程序用来操作设备。有些设备是虚拟出来的比如虚拟光驱设备,有些设备是真实设备派生出来的设备,比如卷设备,一个分区对应一个卷设备,所有对卷设备的操作由卷驱动最终转化为对磁盘驱动的请求。
Subscribe to:
Posts (Atom)