随笔 - 8  文章 - 4  trackbacks - 0
     摘要:          本人进2年来主要在做windbg调试相关的工作, 有一些心得和体会. 我会逐片写在我blog中,希望对大家有用.  windbg调试最重要的是要对系统的方方面面有比较深入的了解. 只有了解了系统工作原理才能够顺藤摸瓜.  一步步展开线索.   windbg基础篇...  阅读全文
posted @ 2009-08-22 13:47 Only Soft 阅读(5148) | 评论 (4)编辑 收藏
寄存器没有地址

也就是寄存器存放着存储器的地址.

CPU通过寄存器中存放的存储器地址来访问存储器中的数据

X86系列的处理器中的寄存器较少,所以
没有给cpu中的寄存器进行编址,而是直接对这些寄存器进行了命名.在访问寄存器的时候只要直接使用寄存器名就可以了.


lea指令举例:
lea 取得地址(第二个参数)后放入到前面的寄存器。
如:lea edi, ebp  ,就是将ebp里存储的地址放入edi, 让edi也只想这个地址。
其实就是edi 指向ebp指向的存储器地址

又如: lea edi, [ebp+0cch],   []中代表存储器地址, 即ebp指向的存储器地址+0cch偏移。


再举一个例子:
mov ecx,30h
mov eax,0CCCCCCCCh
rep   stos dword ptr es:[edi]
stos是串存储指令,他的功能是将eax中的数据存入edi寄存器所指向的存储器地址中。同时,edi会增加4(字节数)
rep是指令重复执行ecx中填写的次数
posted @ 2010-01-22 18:57 Only Soft 阅读(286) | 评论 (0)编辑 收藏
从FS寄存器获取当前线程ID
int GetThreadId()
{
 int ithread = 0;
 _asm{
    xor esi , esi
    mov eax, fs:[esi+18h]     
       mov ecx, [eax+ 20h]
       mov eax, [eax+ 24h]
    mov dword ptr[ithread], eax
 }
 return ithread;
}
从FS寄存器获取当前进程ID
int GetProcessId()
{
 int iProcess = 0;
 _asm{
    xor esi , esi
    mov eax, fs:[esi+18h]
       mov ecx, [eax+ 20h]
       mov eax, [eax+ 24h]
    mov dword ptr[iProcess ], ecx
 }
 return iProcess ;
}

原理:
1.fs:18h 地址指向线程环境块_TEB
打开windbg可以证明:
0:028> dd fs:18h L1
0053:00000018  7eeb8000
0:028> !teb
TEB at 7eeb8000
    ExceptionList:        1f8ff15c
    StackBase:            1f900000
    StackLimit:           1f8fc000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 7eeb8000
    EnvironmentPointer:   00000000
    ClientId:             00001a30 . 00001408
    RpcHandle:            00000000
    Tls Storage:          133d2718
    PEB Address:          7efde000
    LastErrorValue:       0
    LastStatusValue:      c0000302
    Count Owned Locks:    0
    HardErrorMode:        0

2. 在_TEB中找到线程ID和进程ID
0:028> dt ntdll!_TEB
   +0x000 NtTib            : _NT_TIB
   +0x01c EnvironmentPointer : Ptr32 Void
   +0x020 ClientId         : _CLIENT_ID

0:028> dt ntdll!_CLIENT_ID
   +0x000 UniqueProcess    : Ptr32 Void  >进程ID
   +0x004 UniqueThread     : Ptr32 Void  >线程ID

当然从TEB又可以找到_PEB的地址,从_PEB里面可以获取到更多的信息。暂且搁笔~~
posted @ 2010-01-20 15:10 Only Soft 阅读(3465) | 评论 (0)编辑 收藏
      在驱动中分配内存,通常你的驱动会向NT VMM请求内存,这是你必须决定你的内存是可分页的或者非可分页的。
      分配原则:尽量使用可分页内存,但是必须注意高等级IRQL执行不允许page fault。所以不允许采用可分页内存。可以使用PAGED_CODE();宏来检查。
      非分页内存在系统中是一个有限的资源,取决于操作系统和物理内存大小. (NT VMM使用一个私有算法来计算非分页大小,这个算法使用物理内存总是作为计算因子来计算。)
     NT提供如下例程来分配内存:
       ExAllocatePool();
       ExAllocatePoolWithQuota();
       ExAllocatePoolWithTag();
        ExAllocatePoolWithQuotaTag
       调用以上例程分配内存是必须制定内存的类型:
       NonPagedPool
       PagedPool
       NonPagedPoolMustSuccessed.
       ......................

    关于非分页内存碎片问题:
     本来初始化的时候地址都是相邻的非分页池会变成碎片。而且VMM在托大的时候也不保证地址相邻。
    如果请求的分配或者释放小块内存(小于一个PAGE_SIZE),可能导致物理内存碎片化。这回给系统带来各种各样的问题,包括降低系统性能和分配内存失败的情况。
    解决办法使用旁视列表,旁视列表是一个NT4.0开始提供的一个内存分配方式;具体请参考WDK docment.
     顺便提一下内核栈:
     每个在NT平台的线程有一个用户栈在用户模式执行的时候使用,一个内核栈在内核模式执行的时候使用。当线程请求系统服务而切换到内核模式的时候,陷阱机制会切换栈。用分配和线程的内核空间栈来覆盖用户空间栈。
在NT3.51之前,内核栈限制在两页的内存中。NT4.0开始增加到12KB.必须要谨慎的在栈上使用变量以节省占空间,防止超过限制而是系统停止。

posted @ 2009-11-15 23:57 Only Soft 阅读(841) | 评论 (0)编辑 收藏
首先是参数说明宏。参数说明宏一般都是空宏,最常见的是IN和OUT。其实定义很简单,如下所示:
#define IN
#define OUT
        这样一来,IN和OUT就被定义成了空。无论出现在代码中的任何地方,对代码都不会有什么实质的影响。在WDK的代码中,用来作为函数的说明。IN表示这个参数用于输入;OUT表示这个参数用来返回结果。比如下面的例子:
NTSTATUS
  ZwQueryInformationFile(
    IN HANDLE  FileHandle,
    OUT PIO_STATUS_BLOCK  IoStatusBlock,
    OUT PVOID  FileInformation,
    IN ULONG  Length,
    IN FILE_INFORMATION_CLASS  FileInformationClass
    );
        IN和OUT是比较传统的参数说明宏。在WDK中到处可见更复杂的参数说明宏,比如下面的例子:
VOID
NdisProtStatus(
    IN NDIS_HANDLE                          ProtocolBindingContext,
    IN NDIS_STATUS                          GeneralStatus,
    __in_bcount(StatusBufferSize) IN PVOID  StatusBuffer,
    IN UINT                                 StatusBufferSize
    )

        其中的__in_bcount不但说明参数StatusBuffer是一个输入参数,而且说明了StatusBuffer作为一个缓冲区,它的字节长度被另一个参数StatusBufferSize所指定。读者再见到类似的说明宏,就以字面意思理解即可。

        然后是指定函数位置的预编译指令。比如下面的例子:
NTSTATUS
ZwQueryInformationFile(
    IN HANDLE  FileHandle,
    OUT PIO_STATUS_BLOCK  IoStatusBlock,
    OUT PVOID  FileInformation,
    IN ULONG  Length,
    IN FILE_INFORMATION_CLASS  FileInformationClass
    );
        IN和OUT是比较传统的参数说明宏。在WDK中到处可见更复杂的参数说明宏,比如下面的例子:
VOID
NdisProtStatus(
    IN NDIS_HANDLE                          ProtocolBindingContext,
    IN NDIS_STATUS                          GeneralStatus,
    __in_bcount(StatusBufferSize) IN PVOID  StatusBuffer,
    IN UINT                                 StatusBufferSize
    )
         其中的__in_bcount不但说明参数StatusBuffer是一个输入参数,而且说明了StatusBuffer作为一个缓冲区,它的字节长度被另一个参数StatusBufferSize所指定。读者再见到类似的说明宏,就以字面意思理解即可。

        然后是指定函数位置的预编译指令。比如下面的例子:
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, NdisProtUnload)
#pragma alloc_text(PAGE, NdisProtOpen)
#pragma alloc_text(PAGE, NdisProtClose)
#pragma alloc_text

        这个宏仅仅用来指定某个函数的可执行代码在编译出来后在sys文件中的位置。内核模块编译出来之后是一个PE格式的sys文件,这个文件的代码段(text段)中有不同的节(Section),不同的节被加载到内存中之后处理情况不同。读者需要关心的主要是3种节:INIT节的特点是在初始化完毕之后就被释放。也就是说,就不再占用内存空间了。PAGE节的特点是位于可以进行分页交换的内存空间,这些空间在内存紧张时可以被交换到硬盘上以节省内存。如果未用上述的预编译指令处理,则代码默认位于PAGELK节,加载后位于不可分页交换的内存空间中。

        函数DriverEntry显然只需要在初始化阶段执行一次,因此这个函数一般都用#pragma alloc_text(INIT, DriverEntry)使之位于初始化后立刻释放的空间内。为了节约内存,可以把很多函数放在PAGE节中。但是要注意:放在PAGE节中的函数不可以在Dispatch级调用,因为这种函数的调用可能诱发缺页中断。但是缺页中断处理不能在Dispatch级完成。为此,一般都用一个宏PAGED_CODE()进行测试。如果发现当前中断级为Dispatch级,则程序直接报异常,让程序员及早发现。示例如下:
#pragma alloc_text(PAGE, SfAttachToMountedDevice)
……
NTSTATUS
SfAttachToMountedDevice (
    IN PDEVICE_OBJECT DeviceObject,
    IN PDEVICE_OBJECT SFilterDeviceObject
    )
{       
    PSFILTER_DEVICE_EXTENSION newDevExt =
   SFilterDeviceObject->DeviceExtension;
    NTSTATUS status;
    ULONG i;

    PAGED_CODE();
 …

本文摘自《寒江独钓:Windows内核安全编程》

posted @ 2009-11-15 23:03 Only Soft 阅读(644) | 评论 (0)编辑 收藏
User32.dll,kernel32.dll,shell32.dll,gdi32.dll,rpcrt4.dll,comctl32.dll,advapi32.dll,version.dll等dll代表了Win32 API的基本提供者;
Win32 API中的所有调用最终都转向了ntdll.dll,再由它转发至ntoskrnl.exe。ntdll.dll是本机 API用户模式的终端。真正的接口在ntoskrnl.exe里完成。事实上,内核模式的驱动大部分时间调用这个模块,如果它们请求系统服务。Ntdll.dll的主要作用就是让内核函数的特定子集可以被用户模式下运行的程序调用。Ntdll.dll通过软件中断int 2Eh进入ntoskrnl.exe,就是通过中断门切换CPU特权级。
Ntdll.dll 上面的相关API函数原型和参数都没有文档化(Undocumented ):  http://undocumented.ntinternals.net/ 这里提供了Ntdll.dll部分未公开函数的原型.

理解window API及函数原型对我们的调试将是非常重要的: 因为你时常需要去察看一些函数的参数,或者根据参数找到某些输入指针.

例如:
  17  Id: a84.cc4 Suspend: 1 Teb: 7ff3a000 Unfrozen
ChildEBP RetAddr  Args to Child             
187ffdb8 77845e6c 7782fc72 00001938 00000000 ntdll!KiFastSystemCallRet
187ffdbc 7782fc72 00001938 00000000 00000000 ntdll!NtWaitForSingleObject+0xc
187ffe20 7782fb56 00000000 00000000 00000000 ntdll!RtlpWaitOnCriticalSection+0x13e
187ffe48 01b05d13 0x77c8ba60 81fa55ed 028766c8 ntdll!RtlEnterCriticalSection+0x150

从堆栈可以看出线程17 正在进入某一个临界区.  0x77c8ba60 就是传入的临界值 参数.

17> !cs 0x77c8ba60             --> !cs 是用来查看临界区信息的命令
DebugInfo          = 0x77fbde20
Critical section   = 0x77c8ba60 (GDI32!semColorSpaceCache+0x0)
LOCKED
LockCount          = 0x0
OwningThread       = 0x00000dd8
RecursionCount     = 0x1
LockSemaphore      = 0x0
SpinCount          = 0x00000000

可以看到 LOCKED 代表临界区是锁定状态. 即被占用.
OwningThread   即是占用线程.

临界区信息结构定义在ntdll, 可以使用如下指令进行察看.
> dt  ntdll!_RTL_CRITICAL_SECTION
   +0x000 DebugInfo        : Ptr32 _RTL_CRITICAL_SECTION_DEBUG
   +0x004 LockCount        : Int4B
   +0x008 RecursionCount   : Int4B
   +0x00c OwningThread     : Ptr32 Void
   +0x010 LockSemaphore    : Ptr32 Void
   +0x014 SpinCount        : Uint4B

察看某个动态库函数表的指令:
x ntdll!*
x kernal!*

察看结构体定义:
dt ntdll!*

任何动态库包括window 32的用户态dll 和用户自定义动态库都是生长在进程内存空间上的.
DLL 没有自己的"私有"地址空间. 它们总是被影射到应用程序的虚拟地址空间,在需要时才会被读取到物理内存中.
在本系列的其它章节我会谈到虚拟地址空间的内容.

通过指令可以看到ntdll 被映射到77800000 ~ 7793c000的内存空间中.
> x *!
77800000 7793c000   ntdll      (pdb symbols)          c:\mylocalsymbols\ntdll.pdb\F0164DA71FAF4765B8F3DB4F2D7650EA2\ntdll.pdb

当你的代码(线程)栈中出现地址范围在 77800000 ~7793c000 之间的函数调用都表示在call NTDLL.dll
比如:
   7  Id: a84.c34 Suspend: 1 Teb: 7ff3f000 Unfrozen
ChildEBP RetAddr  Args to Child             
089bfe8c 77845e6c 75a0179c 00000d98 00000000 ntdll!KiFastSystemCallRet
089bfe90 75a0179c 00000d98 00000000 00000000 ntdll!NtWaitForSingleObject+0xc
089bfefc 75c9f003 00000d98 ffffffff 00000000 KERNELBASE!WaitForSingleObjectEx+0x98
089bff14 75c9efb2 00000d98 ffffffff 00000000 kernel32!WaitForSingleObjectExImplementation+0x75
089bff28 69434fea 00000d98 ffffffff 0780c178 kernel32!WaitForSingleObject+0x12
WARNING: Stack unwind information not available. Following frames may be wrong.
此线程中WARNING: Stack unwind information not available. Following frames may be wrong.表示windbg无法翻译或者找到对应symbols来显示code stack. 这种错误往往是保存dump file时出现的某种异常信息.window也没有给出合理的解释.
以下是MSDN的原话:
In some cases, the stack trace function will fail in the debugger. This can be caused by a call to an invalid address that caused the debugger to lose the location of the return address; or you may have come across a stack pointer for which you cannot directly get a stack trace; or there could be some other debugger problem. In any case, being able to manually walk a stack is often valuable.

这时候你需要手动的进行恢复栈调用. 如果你了解每个动态库的映射地址你就很容易进行分析了.

察看动态库中每个函数映射的地址可以采用如下指令 :
x ntdll!


手动恢复栈的大致原理如下:
1. 列出线程环境信息
 0:000> !teb

TEB at 7fffe000

    ExceptionList:        0012ff88

    StackBase:            00130000

    StackLimit:           00126000

    ……….

2. 打开整个线程栈.
0:000>
dds 00126000 00130000

3. 察看内存中所有可能是函数返回值.  
>
ln address



posted @ 2009-08-24 11:20 Only Soft 阅读(2986) | 评论 (0)编辑 收藏
     摘要:          本人进2年来主要在做windbg调试相关的工作, 有一些心得和体会. 我会逐片写在我blog中,希望对大家有用.  windbg调试最重要的是要对系统的方方面面有比较深入的了解. 只有了解了系统工作原理才能够顺藤摸瓜.  一步步展开线索.   windbg基础篇...  阅读全文
posted @ 2009-08-22 13:47 Only Soft 阅读(5148) | 评论 (4)编辑 收藏
以下是3D中,经常会碰到的一些纹理或者文件格式。仅供参考~~

TXT
Update: 2004-2-24    
   无任何格式的ASCII文本文件。

X文件
Update: 2004-2-24    
   复杂的几何模型通常是由3D建模软体创建并保存到文件中,Microsoft Direct3D使用的网格模型都是载入这些文件中的对象。

TIFF格式
Update: 2004-2-23    
   TIFF(Tag Image File Format有标签的图像文件格式)是Aldus在Mac初期开发的,目的是使扫描图像标准化。它是跨越Mac与PC平台最广泛的图像打印格式。TIFF使用LZW无损压缩,大大减少了图像体积。另外,TIFF格式最令人激动的功能是可以保存通道,这对于你处理图像是非常有好处的。

JPEG格式
Update: 2004-2-23    
   JPEG(由Joint Photographic Experts Group“联合图形专家组”命名)是我们平时最常用的图像格式。它是一个最有效、最基本的有损压缩格式,被极大多数的图形处理软件所支持。JPEG格式的图像还广泛用于Web的制作。如果对图像质量要求不高,但又要求存储大量图片,使用JPEG无疑是一个好办法。

Amiga IFF格式
Update: 2004-2-23    
   Amiga是由Commodore开发的,由于该公司已退出计算机市场,因此,Amiga IFF格式也将渐渐地被废弃。

PCX格式
Update: 2004-2-23    
   PCX是DOS下的古老程序PC PaintBrush固有格式的扩展名,因此这个格式已不受欢迎。

TGA格式
Update: 2004-2-23    
   TrueVision的TGA(Targa)和NuVista视频板可将图像和动画转入电视中,PC机上的视频应用软件都广泛支持TGA格式。

Scitex CT格式
Update: 2004-2-23    
   Scitex CT格式支持灰度级图像、RGB图像、CMYK图像。Photoshop可以打开诸如Scitex图像处理设备的数字化图像。

PDF格式
Update: 2004-2-23    
   PDF(Portable Document Format)是由Adobe Systems创建的一种文件格式,允许在屏幕上查看电子文档。PDF文件还可被嵌入到Web的HTML文档中。

PICT格式
Update: 2004-2-23    
   PICT是Mac上常见的数据文件格式之一。如果你要将图像保存成一种能够在Mac上打开的格式,选择PICT格式要比JPEG要好,因为它打开的速度相当快。另外,你如果要在PC机上用Photoshop打开一幅Mac上的PICT文件,建议你在PC机上安装QuickTime,否则,将不能打开PICT图像。

BMP格式
Update: 2004-2-23    
   BMP(Windows Bitmap)是微软开发的Microsoft Pain的固有格式,这种格式被大多数软件所支持。BMP格式采用了一种叫RLE的无损压缩方式,对图像质量不会产生什么影响。

PNG格式
Update: 2004-2-23    
   PNG是专门为Web创造的。PNG格式是一种将图像压缩到Web上的文件格式,和GIF格式不同的是,PNG格式并不仅限于256色。

GIF格式
Update: 2004-2-23    
   GIF是输出图像到网页最常采用的格式。GIF采用LZW压缩,限定在256色以内的色彩。GIF格式以87a和89a两种代码表示。GIF87a严格支持不透明像素。而GIF89a可以控制那些区域透明,因此,更大地缩小了GIF的尺寸。

后面的文章将会对Gif格式做一个实现。请期待

posted @ 2009-04-04 01:07 Only Soft 阅读(524) | 评论 (0)编辑 收藏

本程序只关注对Gif纹理的实现。不对GIF加解密进行详细的说明。
GIF文档解析采用gif89a.h, gif89a.cpp实现。

gif89a 代码下载

详细纹理生成代码:


bool Cmenu::CreateTextureFromGif()
{
    HRESULT hr;
  CGif89a
* pGif    = new CGif89a();
    BOOL bopen 
= pGif->open("110743081.gif",true);
    
    
int index = 0;
    LPCFRAME pFrame    
= pGif->getFrame(index);
    
if (!pFrame)
        
return false;


    BYTE
* pColorTable    = pGif->getColorTable(index);
    
if (!pColorTable)
        
return false;

    
//create empty Texture.
    hr = D3DXCreateTexture(m_pDevice,pFrame->imageWidth,pFrame->imageHeight,0,0,D3DFMT_X8B8G8R8,D3DPOOL_MANAGED,&m_pTexture[0]);
    
if(FAILED(hr))
        
return false;

    D3DSURFACE_DESC textureDesc;
    m_pTexture[
0]->GetLevelDesc(0,&textureDesc);

    
if(textureDesc.Format != D3DFMT_X8B8G8R8)
        
return false;

    D3DLOCKED_RECT locketrect;
    hr 
= m_pTexture[0]->LockRect( 0,&locketrect,0,0 );
    
if(FAILED(hr))
        
return false;

    BYTE
* pBytes = (BYTE*)locketrect.pBits;
    DWORD lPitch 
= locketrect.Pitch;

    
int idx_trs    = pFrame->ctrlExt.trsFlag ? pFrame->ctrlExt.trsColorIndex : -1;

    
int x=0,y=0;
    
int    sx, sy;
    
if(x<0)
        sx    
= -x;
    
else
        sx    
= 0;
    
if(y<0)
        sy    
= -y;
    
else
        sy    
= 0;


    DWORD width    
= pFrame->imageWidth;

    pBytes 
+= lPitch*sy;
    
for( DWORD h=sy; y+h<pFrame->imageHeight; h++ )
    
{
        DWORD
* pDstData32 = ((DWORD*)pBytes) + sx;
        WORD
*  pDstData16 = ((WORD*)pBytes) + sx;
        BYTE
* pPixel;

        BYTE* pIdx    = pFrame->dataBuf + (pFrame->imageWidth*(((y>0)?y:0)+h) + ((x>0)?x:0));

        
for( DWORD w=sx; w<width; w++ )
        
{
            pPixel    
= pColorTable + (*pIdx)*3;
            
if (*pIdx==idx_trs)
            
{
                
//if( 32 == ddsd.ddpfPixelFormat.dwRGBBitCount )
                
//    pDstData32[w] = 0;
                
//else
                
//    pDstData16[w] = 0;
            }

            
else
            
{

                DWORD m_nRShiftL 
= 0; DWORD m_nRShiftR = 0;
                DWORD m_nGShiftL 
= 0; DWORD m_nGShiftR = 0;
                DWORD m_nBShiftL 
= 0; DWORD m_nBShiftR = 0;
                DWORD m_nAShiftL 
= 0; DWORD  m_nAShiftR = 0;

                DWORD dr 
= ((DWORD(pPixel[0])>>(m_nRShiftL))<<m_nRShiftR);
                DWORD dg = ((DWORD(pPixel[1])>>(m_nGShiftL))<<8);
                DWORD db = ((DWORD(pPixel[2])>>(m_nBShiftL))<<16);
                DWORD da = ((0xff>>(m_nAShiftL))<<24);
  
           pDstData32[w] = (DWORD)(dr+dg+db+da);
 

            }

            pIdx 
++;
        }

        
        pBytes 
+= lPitch;
    }


    m_pTexture[
0]->UnlockRect(0);
    
return true;
}
如有任何疑问请留言。交流~~
posted @ 2009-04-04 01:05 Only Soft 阅读(800) | 评论 (0)编辑 收藏
仅列出标题