存档

‘内核编程’ 分类的存档
435 views

TrueCrypt 源码

2010年7月6日

TrueCrypt 可编译版本, 主要做了一些微调, 使得能通过vs2008, 并且不再吐出警告信息, 源码TrueCrypt-6-3-a-Src 下载, 要完整编译本源代码, 需要自己下载以下编译工具:

  • vs 2008 VS 2010 download
  • nasm 下载地址 nasm 下载后将 nasm.exe 文件解压出来, 放到 system32 目录里
  • gzip 下载地址 gzip 下载后将 gzip.exe 文件解压出来, 放到 system32 目录里
  • vc1.5 下载地址这里(本地) 下载后解压到一个文件夹, 如 d:\vc1.5 , 然后创建一个环境变量 MSVC16_ROOT = d:\vc1.5 这是微软提供的最后一个能生成 16 位代码的编译工具
  • WDK 下载地址 微软提供, 安装后用目标目录创建环境变量 BASEDIR, 如 BASEDIR = D:\WinDDK\7600.16385.1 这个工具用于编译驱动

另:

内核编程, 技术心得 ,

439 views

Tips of Windows Programming

2010年6月11日

窗口总在最前

	::SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0,
		SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);

窗口总保持激活状态, 这段代码可以放在定时器内.

void KeepWindowAlwaysActive(HWND hWnd)
{
	HWND hCurWnd = NULL;
	DWORD lMyID = 0;
	DWORD lCurID = 0;

	hCurWnd = ::GetForegroundWindow();
	if(hCurWnd != hWnd)
	{
		lMyID = ::GetCurrentThreadId();
		lCurID = ::GetWindowThreadProcessId(hCurWnd, NULL);
		::AttachThreadInput(lMyID, lCurID, TRUE);
		::SetForegroundWindow(hWnd);
		::AttachThreadInput(lMyID, lCurID, FALSE);
	}
}

怎么跟自旋锁打交道

The code within a critical region guarded by an spin lock must neither be pageable nor make any references to pageable data.
The code within a critical region guarded by a spin lock can neither call any external function that might access pageable data or raise an exception, nor can it generate any exceptions.
The caller should release the spin lock with KeReleaseSpinLock as quickly as possible.

阅读全文…

内核编程, 技术心得 , , , ,

227 views

驱动开发中应该注意的事项

2010年6月10日

1. 一定不要在没有标注 I/O 请求数据包 (IRP) 挂起 (IoMarkIrpPending) 的情况下通过调度例程返回 STATUS_PENDING。
2. 一定不要通过中断服务例程 (ISR) 调用 KeSynchronizeExecution。 它会使系统死锁。
3. 一定不要将 DeviceObject->Flags 设置为 DO_BUFFERED_IO 和 DO_DIRECT_IO。 它会扰乱系统并最终导致致命错误。 而且,一定不要在 DeviceObject->Flags 中设置 METHOD_BUFFERED、METHOD_NEITHER、METHOD_IN_DIRECT 或 METHOD_OUT_DIRECT,因为这些值只在定义 IOCTL 时使用。
4. 一定不要通过页面缓冲池分配调度程序对象。 如果这样做,将会偶尔导致系统故障检测 (Bugcheck)。
5. 当运行于 IRQL >= DISPATCH_LEVEL 时,一定不要通过页面缓冲池分配内存,或访问页面缓冲池中的内存。 这是一个致命错误。
6. 一定不要在 IRQL >= DISPATCH_LEVEL 上等候核心调度程序对象出现非零间隔。 这是一个致命错误。
7. 在 IRQL >= DISPATCH_LEVEL 上执行时,一定不要调用任何导致调用线程发生直接或间接等待的函数。 这是一个致命错误。
8. 一定不要把中断请求级别 (IRQL) 降低到低于您的顶级例程被调用的级别。
9. 如果没有调用过 KeRaiseIrql(),则一定不要调用 KeLowerIrql()。
10. 一定不要使处理器 (KeStallExecutionProcessor) 停止运转的时间超过 50 微秒。
11. 一定不要使旋转锁 (Spin Lock) 保持锁定状态的时间超过您的需要。 要使系统获得更好的总体性能,请不要使任何系统范围内有效的旋转锁的锁定时间超过 25 微秒。
12. 当 IRQL 大于 DISPATCH_LEVEL 时,一定不要调用 KeAcquireSpinLock 和 KeReleaseSpinLock,或 KeAcquireSpinLockAtDpcLevel 和 KeReleaseSpinLockFromDpcLevel。
阅读全文…

内核编程, 技术心得

236 views

内核从 PID 获得进程信息

2010年6月8日
typedef struct PROCESSINFO
{
	HANDLE         hProcess;
	PCLIENT_ID     cidThreads;
	unsigned long  ulNumberOfThreads;
} PROCESSINFO, * PPROCESSINFO;

NTSTATUS GetProcInfoByPid(unsigned long pid, PPROCESSINFO procInfoOut)
{
#define SYS_INFO_PROCESSES_SIZE 256
#define SystemProcessesAndThreadsInformation 5

	NTSTATUS            status = STATUS_UNSUCCESSFUL;
	SYSTEM_PROCESSES * sysProcInfo;
	HANDLE              hHandleBuffer;
	OBJECT_ATTRIBUTES   objAttrib;

	void*               allocationBase;
	unsigned long       bufSize;
	ULONG       i;

	bufSize = SYS_INFO_PROCESSES_SIZE;

	do
	{
		allocationBase = (SYSTEM_PROCESSES *) kmalloc(bufSize);

		if(!allocationBase)
			return status;

		status = ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, allocationBase, bufSize, &i);

		if(status == STATUS_INFO_LENGTH_MISMATCH)
		{
			kfree(allocationBase);
			bufSize += SYS_INFO_PROCESSES_SIZE;
		} else if (!NT_SUCCESS(status)) {
			kfree(allocationBase);
			return status;
		}
	} while (status == STATUS_INFO_LENGTH_MISMATCH);

	status = STATUS_UNSUCCESSFUL;
	sysProcInfo = (SYSTEM_PROCESSES*) allocationBase;

	while(TRUE)
	{
		if(pid == sysProcInfo->ProcessId)
		{
			if((unsigned long)sysProcInfo->ThreadCount != 0)
			{
				InitializeObjectAttributes(&objAttrib, 0, OBJ_KERNEL_HANDLE, 0, 0);
				status = ZwOpenProcess(&hHandleBuffer, PROCESS_ALL_ACCESS,
					&objAttrib, &sysProcInfo->Threads[0].ClientId);
				if (!NT_SUCCESS(status)) {
					break;
				}

				procInfoOut->hProcess = hHandleBuffer;
				procInfoOut->ulNumberOfThreads = sysProcInfo->ThreadCount;

				i = sysProcInfo->ThreadCount * sizeof(CLIENT_ID);

				if(i)
				{
					procInfoOut->cidThreads = (PCLIENT_ID)kmalloc(i);
					for(i = 0; i < sysProcInfo->ThreadCount; i++) {
						RtlCopyMemory((PCHAR)procInfoOut->cidThreads + (i * sizeof(CLIENT_ID)),
							&(sysProcInfo->Threads[i].ClientId), sizeof(CLIENT_ID));
					}
				}
				else
					procInfoOut->cidThreads = NULL;

				status = STATUS_SUCCESS;
			}

			break;
		}

		if(sysProcInfo->NextEntryDelta) {
			(unsigned long)sysProcInfo += (unsigned long)sysProcInfo->NextEntryDelta;
		} else {
			break;
		}
	}

	kfree(allocationBase);

	return status;
}

内核编程

284 views

在 Windows NT 内核如何判断文件访问请求 IRP 来自网络

2010年6月3日
BOOLEAN _IsFromNetAccess(PIRP Irp);

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, _IsFromNetAccess)
#endif

BOOLEAN _IsFromNetAccess(PIRP Irp)
{
	NTSTATUS status;
	PACCESS_TOKEN pToken = NULL;
	PTOKEN_SOURCE pTokenSrc = NULL ;
	PSECURITY_SUBJECT_CONTEXT secSubCtx;
	BOOLEAN bResult = FALSE;
	PIO_STACK_LOCATION IrpSp = NULL;

	PAGED_CODE();

	__try
	{
		IrpSp = IoGetCurrentIrpStackLocation(Irp);

		secSubCtx = & (IrpSp->Parameters.Create.SecurityContext->
			AccessState->SubjectSecurityContext);

		if (secSubCtx->ClientToken != NULL ||
			secSubCtx->PrimaryToken != NULL)
		{
			pToken = SeQuerySubjectContextToken(secSubCtx);
		} 

		if (NULL == pToken)
		{
			__leave;
		}

		//
		// Get TokenSource Name If SourceName is "NtLmSsp",
		// it was logged-in via Lanmanager,
		// "User32" represents locally logged-in users.
		//
		status = SeQueryInformationToken(pToken, TokenSource, &pTokenSrc);
		if (NT_SUCCESS(status))
		{
			pTokenSrc->SourceName[TOKEN_SOURCE_LENGTH-1] = 0x00;

			//kdprintf(NC_DRV_PREFIX "Token Name :%s Len:%d\r\n",
			//	pTokenSrc->SourceName,strlen(pTokenSrc->SourceName)); 

			if (_stricmp(pTokenSrc->SourceName, "NtLmSsp") == 0 )
			{
				// kdprintf(NC_DRV_PREFIX "NetWork Access Token Find\r\n");
				bResult = TRUE;
			}
		}
	}
	__finally
	{
		if ( pTokenSrc ) {
			ExFreePool(pTokenSrc);
		}
	}
	return bResult;
} 

内核编程

357 views

SYSENTER 系统服务调用过程

2010年5月26日

以NtReadFile调用为例。

一.NtDll.Dll中,NtReadFile过程如下:
ntdll!NtReadFile:

7c92d9b0 b8b7000000      mov     eax,0B7h
7c92d9b5 ba0003fe7f      mov     edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c92d9ba ff12            call    dword ptr [edx]
7c92d9bc c22400          ret     24h

二.0x7ffe0300地址存放0x7c92e4f0,反汇编它:
ntdll!KiFastSystemCall:

7c92e4f0 8bd4          mov     edx,esp
7c92e4f2 0f34          sysenter

因为SYSENTER执行时,CPU并不会向CALL一样保存返回地址和状态信息,所以需要这样一个“桩”(STUB)段,通过它来保存返回信息。同样的,下面的(五)就是利用这里保存的返回信息返回值NtReadFile中。

这里的KiFastSystemCall和(五)中的SystemCallReturn 都是保存在 _KUSER_SHARED_DATA结构中。用户态下的0X7FFE0000和系统态下的0XFFDF0000同时指向它。结构如下:

阅读全文…

内核编程

215 views

获取二进制代码的操作码大小

2010年5月25日

头文件

#ifndef __OpCodeSize_H__
#define __OpCodeSize_H__

EXTERN_C ULONG __stdcall GetOpCodeSize(unsigned char * startaddress);

#endif	// __OpCodeSize_H__

阅读全文…

内核编程, 技术心得

171 views

Zw* 与 Nt* 的区别

2010年5月25日

某些Zw*和Nt*函数既在ntdll.dll中导出又在ntoskrnl.exe中导出,他们有什么区别呢?
我们分三部分比较:
step 1: ntdll.dll中的Zw*和Nt*有什么区别?
step 2: ntoskrnl.exe中的Zw*和Nt*有什么区别?
step 3: ntdll.dll中的Zw*与ntoskrnl.exe中的Zw*有什么区别?
ntdll.dll中的Nt*与ntoskrnl.exe中的Nt*有什么区别?
在下面的讨论中我们以ZwCreateFile和NtCreateFile为例

讨论前:我先贴点Kd给我们的答案
Part1:
kd> u Ntdll! ZwCreateFile L4
ntdll!ZwCreateFile:
77f87cac b820000000 mov eax,0×20
77f87cb1 8d542404 lea edx,[esp+0x4]
77f87cb5 cd2e int 2e
77f87cb7 c22c00 ret 0x2c
kd> u Ntdll! NtCreateFile L4
ntdll!ZwCreateFile:
77f87cac b820000000 mov eax,0×20
77f87cb1 8d542404 lea edx,[esp+0x4]
77f87cb5 cd2e int 2e
77f87cb7 c22c00 ret 0x2c
阅读全文…

内核编程

226 views

Windows 中 FS 段寄存器

2010年5月25日

代码运行在 RING0 (系统地址空间)和 RING3 (用户地址空间)时, FS 段寄存器分别指向 GDT( 全局描述符表 ) 中不同段:在 RING3 下, FS 段值是 0x3B (这是 WindowsXP 下值;在 Windows2000 下值为 0×38 。差别就是在 XP 下 RPL=3 );运行在 RING0 下时, FS 段寄存器值是 0×30 。下面以 XP 为例说说。

一. RING3 下的 FS

当代码运行在 Ring3 下时, FS 值为指向的段是 GDT 中的 0×38 段( RPL 为 3 )。该段的长度为 4K ,基地址为 当前线程 的线程环境块( TEB ),所以该段也被称为“ TEB 段”。

WINXPSP1 及以前的 Windows2000 等系统中,进程环境块( PEB )的地址固定为 0X7FFDF000 ,该进程的第一个线程的 TEB 地址为 0X7FFDE000 ,第二个 TEB 的地址为 0X7FFDD000….. 但是自从 WindowsXP  SP2 开始 PEB 和 TEB 的地址都是随机映射的 ( 详见博文: MiCreatePebOrTeb函数注释 ) 。

下图是 WindowsXP SP3 下的 TEB 结构 ( 大小为 0XFB8) :
阅读全文…

内核编程

233 views

获取文件对象的名称

2010年5月25日

一.取文件对象名称

我们可以使用函数ObQueryNameString 来查询获取文件对象(FILE_OBJECT )的名称。由于文件对象有专门的名称查询函数IopQueryName ,所以ObQueryNameString 在内部会直接调用这个函数来查询文件对象名。

我们还有另外一种方法比较“直接”地获得文件对象名称。我们知道:文件对象名包括驱动器名和文件路径名。

FILE_OBJECT 结构中有一个成员FileName ,它只包括文件路径名(注意:不包括驱动器名)。

FILE_OBJECT 结构中另外有一个成员DeviceObject ,它是指向DEVICE_OBJECT 的设备对象。该设备就是包含该文件的驱动器设备对象。我们可以调用ObQueryNameString 查询获取此设备对象的名称。但是设备名称的格式是这样的:\\Device\\HarddiskvolumeX (X 为数字),并不是常见的C/D/E… 驱动器名格式。这是因为我们说的驱动器名其实是上面这些设备对象的符号链接(Symbolic link) 名,我们可以通过IoVolumeDeviceToDosName (在XP/2003 等下使用,在NT/2000 下使用RtlVolumeDeviceToDosName )来将设备名称转成驱动器名。

但是若直接使用ObQueryNameString 查询文件对象的话,返回的名称就是设备名称和文路径名,如:
\Device\HarddiskVolume1\WINDOWS\system32\smss.exe
此时我们是没有设备对象,所以就没有办法调用IoVolumeDeviceToDosName 来转化了,此时该怎么办?有一个比较笨的方法,就是调用ZwOpenSymbolcLink 对象对所有A~Z 字母进行打开链接对象,并调用ZwQuerySymbilicLink 来获得对应的设备对象名,并将这个设备名与上面的ObQueyNameString 返回的设备名进行比较,以此来确定驱动器名。见下面的代码片段:

  …
RtlInitUnicodeString(&SymbolicLink, L"\\??\\C:");
LinkTarget.MaximumLength = 200;
LinkTarget.Buffer = ExAllocatePoolWithTag(NonPagedPool, 200, 'test');
for (c = 'A'; c <='Z'; c++)
{
     SymbolicLink.Buffer[4] = c;
     InitializeObjectAttributes(&oa, &SymbolicLink, OBJ_KERNEL_HANDLE, 0, NULL);
     Status = ZwOpenSymbolicLinkObject(&LinkHandle, GENERIC_READ, &oa);
     if (Status != STATUS_SUCCESS)
          continue;
     Status = ZwQuerySymbolicLinkObject(LinkHandle, &LinkTarget, NULL);
     //LinkTarget 返回的就是设备名称
     ZwClose(LinkHandle);
     if (Status == STATUS_SUCCESS)
     {
          RetLength = LinkTarget.Length;
          if (RtlCompareMemory(LinkTarget.Buffer, ObjectNameInfo->Name.Buffer, RetLength) == RetLength)
          break; //ObjectNameInfo 为ObQueryName 返回的文件对象名
     }
}
ExFreePoolWithTag(LinkTarget.Buffer, 'test');
…

阅读全文…

内核编程