Thursday, May 23, 2013

cygwin如何编译不依赖cygwin1.dll的程序

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,通过。

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.

使用信号灯同步线程

#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; 
}

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

设备读写方法小结

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

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)