空结尾的字符串,你可以用普通得C语法表示字符串常量
1) DbgPrint(“Hello World!”); //直接打印字符串。
2) char variable_string[] = “Hello World”;
   DbgPrint(“%s”,variable_string);

空结尾的宽字符串(WCHAR类型)
WCHAR    string_w[] = L“Hello World!”;
DbgPrint(“%ws”,string_w);

Unicode串,由UNICODE_STRING结构描述,包含16位字符。         typedef   struct _UNICODE_STRING{
       USHORT Length;
       USHORT MaximumLength;
      PWSTR   Buffer;
}UNICODE_STRING , *PUNICODE_STRING;

UNICODE_STRING    string_unicode = L”Hello World!”;
DbgPrint(“%ws\n”,string_unicode.Buffer);      4.                      ANSI串,由ANSI_STRING结构描述,包含8位字符。

typedef struct _STRING{
     USHORT Length;
     USHORT MaximumLength;
     PCHAR   Buffer;
}STRING, *PANSI_STRING;

STRING bar;
或者:ANSI_STRING bar;
RtlInitAnsiString(&bar,”Hello World!”);
DbgPrint(“%s\n”,bar.Buffer);5.                      分配和释放字符串缓冲区
RtlInitAnsiString函数初始化ANSI_STRING字符串。
RtlInitUnicodeString函数初始化UNICODE_STRING字符串。
RtlAnsiStringToUnicodeString函数把ANSI_STRING转化成UNICODE_STRING。
RtlFreeUnicodeString函数释放给字符串动态分配的内存。

RtlInitAnsiString和RtlInitUnicodeString初始化时不分配内存。不能使用RtlFreeUnicodeString函数。ANSI_STRING和UNICODE_STRING中的Buffer指向一个字符串常量,当调用RtlFreeUnicodeString时字符串常量占用的地址被释放。所以产生错误。
RtlAnsiStringToUnicodeString该函数被调用时,将为目标变量分配内存。所以在不使用该变量时要用该函数释放内存,以免内存泄漏。

例:
UNICODE_STRING string_unicode;
ANSI_STRING   string_ansi;
RtlInitUnicodeString(&string_unicode,L”Hello World!”);//不用释放内存
RtlInitAnsiString(&string_ansi,”Hello World!”);
RtlAnsiStringToUnicodeString(&string_unicode,&string_ansi,TRUE);//需要释放内存。
RtlFreeUnicodeString(&string_unicode);//释放动态分配的内存。

DebugPrint格式说明符
------------------------------------------------
符号   格式说明符     类型
------------------------------------------------
%c   ANSI字符     char
%C   宽字符      wchar_t
%d,%i   十进制有符号整数    int
%D   十进制_int64     _int64
%I   IRP主功能代码和次功能代码   PIRP
%L   十六进制的LARGE_INTEGER    LARGE_INTEGER
%s   NULL终止的ANSI字符串    char*
%S   NULL终止的宽字符串    wchar_t*
%T   UNICODE_STRING     PUNICODE_STRING
%u   十进制的ULONG     ULONG
%x   十六进制的ULONG     ULONG

3月 15th, 2008城里城外看SSDT

点这里下载本文的配套代码

引子

2006年,中国互联网上的斗争硝烟弥漫。这时的战场上,先前颇为流行的窗口挂钩、API挂钩、进程注入等技术已然成为昨日黄花,大有逐渐淡出之势;取而代之的,则是更狠毒、更为赤裸裸的词汇:驱动、隐藏进程、Rootkit……

前不久,我不经意翻出自己2005年9月写下的一篇文章《DLL的远程注入技术》,在下面看到了一位名叫L4bm0s的网友说这种技术已经过时了。虽然我也曾想过拟出若干辩解之词聊作应对,不过最终还是作罢了——毕竟,拿出些新的、有技术含量的东西才是王道。于是这一次,李马首度从ring3(应用层)的围城跨出,一跃而投身于ring0(内核层)这一更广阔的天地,便有了这篇《城里城外看SSDT》。——顾名思义,城里和城外的这一墙之隔,就是ring3与ring0的分界。

在这篇文章里,我会用到太多杂七杂八的东西,比如汇编,比如内核调试器,比如DDK。这诚然是一件令我瞻前顾后畏首畏尾的事情——一方面在ring0我不得不依靠这些东西,另一方面我实在担心它们会导致我这篇文章的阅读门槛过高。所以,我决定尽可能少地涉及驱动、内核与DDK,也不会对诸如如何使用内核调试器等问题作任何讲解——你只需要知道我大概在做些什么,这就足够了。

什么是SSDT?

什么是SSDT?自然,这个是我必须回答的问题。不过在此之前,请你打开命令行(cmd.exe)窗口,并输入“dir”并回车——好了,列出了当前目录下的所有文件和子目录。

那么,以程序员的视角来看,整个过程应该是这样的:

  1. 由用户输入dir命令。
  2. cmd.exe获取用户输入的dir命令,在内部调用对应的Win32 API函数FindFirstFile、FindNextFile和FindClose,获取当前目录下的文件和子目录。
  3. cmd.exe将文件名和子目录输出至控制台窗口,也就是返回给用户。

到此为止我们可以看到,cmd.exe扮演了一个非常至关重要的角色,也就是用户与Win32 API的交互。——你大概已经可以猜到,我下面要说到的SSDT亦必将扮演这个角色,这实在是一点新意都没有。

没错,你猜对了。SSDT的全称是System Services Descriptor Table,系统服务描述符表。这个表就是一个把ring3的Win32 API和ring0的内核API联系起来的角色,下面我将以API函数OpenProcess为例说明这个联系的过程。

你可以用任何反汇编工具来打开你的kernel32.dll,然后你会发现在OpenProcess中有类似这样的汇编代码:

call ds:NtOpenProcess

这就是说,OpenProcess调用了ntdll.dll的NtOpenProcess函数。那么继续反汇编之,你会发现ntdll.dll中的这个函数很短:

mov eax, 7Ah
mov edx, 7FFE0300h
call dword ptr [edx]
retn 10h

Read the rest of this entry »


© 2007 Raullen的上善若水 | iKon Wordpress Theme by Windows Vista Administration | Powered by Wordpress