首页
社区
课程
招聘
[原创]Windows XP 系统自带扑克牌资源动态链接库cards.dll逆向分析笔记
发表于: 2007-8-13 01:01 22303

[原创]Windows XP 系统自带扑克牌资源动态链接库cards.dll逆向分析笔记

2007-8-13 01:01
22303

真是晕啊,这篇文章写到一半的时候竟然有人说cards.dll的源代码可以在MSDN Library中找到,我看了一下本机的MSDN,反正我是没找到,有谁知道在哪找的麻烦指点一下,哎,害的我后面写那么一大堆废话来凑篇幅

Windows XP 系统自带扑克牌资源动态链接库cards.dll逆向分析笔记

使用工具:IDA Pro, Resource Hacker

0. 前言

  cards.dll是Windows系统目录下的一个动态链接库,主要提供扑克牌图像及相关操作等资源,以供
Windows附带的扑克游戏程序(如纸牌、红心大战等)使用。

  我们希望知道cards.dll具体提供了哪些东西,可供自由编程所用。

1. 反编译

  一般而言,将原始二进制文件还原成高级语言源文件的逆向工程有两个步骤:一是反编译,根据目
标文件反汇编的内容,识别指令、存储单元等基本要素并理解这些要素之间的相互关系,从而写出相应
的高级语言语句;二是自文档化,根据程序的上下文,理解程序中各个变量所代表的含义,给它们重新
赋予意义明了的名称,必要时添加一定的注释,以使得源程序具有一定的文档性,容易看懂和理解。

  由于这里是人工逆向,这两个步骤分得并不清楚,实际上也难以分清楚。毕竟,原始的反汇编文本
中充斥着大量形如dword_70142004以及var_8之类缺乏意义的符号,不同变量的名字看上去都差不多,
如果不及时给它们起个容易分辨的名称,就很容易弄错。这样看来,上述的第二步就部分地提前了。同
时,为了简洁起见,在下面的正文中也不打算贴那种大片的反汇编文本(事实上也不必要,若要看这些
内容,自己用IDA打开这个DLL文件就能看到了),只提供还原出来的源代码,源代码中的变量都已赋予
合适的名称。这就是说,反编译的过程在正文中也省略了。

  但毕竟反编译是本文的一个重要组成部分,不能将它真正省略掉。由于要说清楚这个过程势必又要
附上大量的反汇编文本,因此,关于反编译的一般原则(如识别高级语言的控制结构等内容)我会以附
录形式另起一章,而具体的反汇编过程则把它放到附件里了,附件里的origdasm.asm是最初的没有经过
任何分析的反汇编文本,而annodasm.asm是分析过后的文本,我的分析主要以注释形式放在其中,大家
可以对照着看。

  IDA加载后切换到Exports选项卡,可以看到7个导出函数:

    WEP
      cdtAnimate
      cdtDraw
      cdtDrawExt
      cdtInit
      cdtTerm
      DllEntryPoint

其中DllEntryPoint就是通常所谓的DllMain。其代码如下:

  HINSTANCE hDllModule;    //全局变量
  BOOL DllMain (HINSTANCE hInstDll, DWORD dwReason, LPVOID lpReserved)
  {
      hDllModule = hInstDll;    
      return TRUE;
  } 
 BOOL __stdcall WEP (int Arg1)
  {
      return TRUE;
  }

  BOOL __stdcall cdtAnimate (int Arg1, int Arg2, int Arg3, int Arg4, int Arg5)
  {
      return TRUE;
  }
//全局变量
  int nNormalWidth;         //正常大小下卡片的宽 
  int nNormalHeight;        //正常大小下卡片的长
  int nInitCount = 0;       //“初始化计数”,每调用cdtInit一次增加1,
                            //每调用cdtTerm一次减少1
  HBITMAP hPlainBack = NULL;    //平实型背面的位图句柄
  HBITMAP hCrossBack = NULL;    //斜十字图案型背面的位图句柄
  HBITMAP hCircleBack = NULL;   //圆圈图案型背面的位图句柄
//函数定义体  
  BOOL __stdcall cdtInit (LPINT lpNormalWidth, LPINT lpNormalHeight)
  {
      BITMAP loc_stBM;

      if (nInitCount++ == 0)
      {
           hPlainBack = LoadBitmap (hDllModule, 53);
           hCrossBack = LoadBitmap (hDllModule, 67);
           hCircleBack = LoadBitmap (hDllModule, 68);
           
           if (hPlainBack == NULL || hCrossBack == NULL || hCircleBack == NULL)
           {
                MyDeleteHbm (hPlainBack);
                MyDeleteHbm (hCrossBack);
                MyDeleteHbm (hCircleBack);
                return FALSE;
           }

           GetObject (hPlainBack, sizeof(BITMAP), &loc_stBM);

           nNormalWidth = *lpNormalWidth = loc_stBM.bmWidth;
           nNormalHeight = *lpNormalHeight = loc_stBM.bmHeight;
      }else{
           *lpNormalWidth = nNormalWidth;
           *lpNormalHeight = nNormalHeight;
      }
      return TRUE;
  }
  void __stdcall MyDeleteHbm (HBITMAP hBM)
  {
      if (hBM)
         DeleteObject (hBM);
      return;
  }
//全局变量
  HBITMAP hCards[52];   //一副扑克牌的位图句柄
  HBITMAP hCustomBack;  //自选的卡片背面的位图句柄
//函数定义体
  void cdtTerm (void)
  {
      int loc_i;

      if (--nInitCount <= 0)
      {
          for (loc_i = 0; loc_i < 52; loc_i++)
              MyDeleteHbm (hCards[loc_i]);

          MyDeleteHbm (hPlainBack);
          MyDeleteHbm (hCustomBack);
          MyDeleteHbm (hCrossBack);
          MyDeleteHbm (hCircleBack);
      }
      return;
  }
  BOOL __stdcall cdtDrawExt (HDC hDC,                //将要绘画的目标设备环境句柄
                   int nXDest,             //nXDest和nYDest两个参数表示一个点的x和y坐标
                   int nYDest,             //以该点为左上角绘画卡片
                   int nActualWidth,
                   int nActualHeight,      //以上两个参数表示所绘卡片的实际宽和长
                   int nIndexOfCard,       //指定所绘卡片的序号
                   DWORD dwDrawingOption,  //绘画方式选项
                   COLORREF crBackColor    //背景色
                  )
  {
       COLORREF loc_crCornerData[12];     //卡片四个角的颜色数据。用于圆角方式绘画卡片
       COLORREF loc_crBkColor = crBackColor;   //拷贝一份crBackColor的值
       static HGDIOBJ hObjectToDraw;         
       POINT loc_stP1;
       int loc_dwA;         //代表寄存器eax
       DWORD loc_dwROP = 0;               //ROP码
       DWORD loc_dwDO = dwDrawingOption; //拷贝一份dwDrawingOption的值
       BOOL loc_bSharpCorner = FALSE;    //绘画卡片时选择圆角或尖角
       HGDIOBJ loc_hObj1;
       HDC loc_hCompDC;                  //这两个变量都代表反汇编文本中的arg_8(Arg3)

       if (loc_dwDO & 0x80000000)
       {
           loc_dwDO -= 0x80000000;
           loc_bSharpCorner = TRUE;
       }

       if (loc_dwDO <= 7)
       {
           switch (loc_dwDO)
           {
               case 0:
                  hObjectToDraw = _LoadCard (nIndexOfCard);
                  loc_dwROP = SRCCOPY;
                  loc_crBkColor = 0xFFFFFF;   //白色背景
                  break;
               case 1:
                  if (FLoadBack (nIndexOfCard) == FALSE) return FALSE;
                  hObjectToDraw = hCustomBack;
                  loc_dwROP = SRCCOPY;
                  break;
               case 2:
                  hObjectToDraw = _LoadCard (nIndexOfCard);
                  loc_dwROP = NOTSRCCOPY;     //反色绘制卡片
                  break;
               case 3:
               case 4:
                  if ((loc_hObj1 = CreateSolidBrush (loc_crBkColor)) == NULL)
                      return FALSE;
                  GetDCOrgEx (hDC, &loc_stP1);
                  SetBrushOrgEx (hDC, loc_stP1.x, loc_stP1.y, NULL);
                  if ((loc_hObj1 = SelectObject (hDC, loc_hObj1)) != NULL)
                  {                      
                      PatBlt (hDC, nXDest, nYDest, nActualWidth, 
                                                  nActualHeight, PATCOPY);
                       //在卡片区域涂上crBackColor指定的颜色
                      if ((loc_dwA = (int)SelectObject (hDC, loc_hObj1)) != NULL)
                          DeleteObject ((HGDIOBJ)loc_dwA);
                       //恢复DC中原来的画刷对象,并删除临时对象
                  }
                  if (loc_dwDO == 4) return TRUE;
                  //如果dwDrawingOption等于4,则只着色,不进一步绘画背面
                  //掉入下一分支,继续
               case 5:
                  hObjectToDraw = hPlainBack;
                  loc_dwROP = SRCAND;
                  break;
               case 6:
                  hObjectToDraw = hCrossBack;
                  loc_dwROP = SRCCOPY;
                  break;
               case 7:
                  hObjectToDraw = hCircleBack;
                  loc_dwROP = SRCCOPY;
           }
       }

       if (hObjectToDraw == NULL || (loc_hCompDC = CreateCompatibleDC (hDC)) == NULL)
           return FALSE;
       if ((hObjectToDraw = SelectObject (loc_hCompDC, hObjectToDraw)) == NULL)
       {
           DeleteDC (loc_hCompDC);
           return TRUE;   //这里明显意味着绘画失败,为什么会返回TRUE呢?
       }
       loc_crBkColor = SetBkColor (hDC, loc_crBkColor);  //设置hDC背景色
       if (loc_bSharpCorner == FALSE)
           SaveCorners (hDC, loc_crCornerData, nXDest, nYDest, 
                                            nActualWidth, nActualHeight);
       //画卡片
       if (nActualWidth == nNormalWidth && nActualHeight == nNormalHeight)
       {
           BitBlt (hDC, nXDest, nYDest, nNormalWidth, nNormalHeight,
                                                      loc_hCompDC, 0, 0, loc_dwROP);
       }else{
           StretchBlt (hDC, nXDest, nYDest, nActualWidth, nActualHeight,
                            loc_hCompDC, 0, 0, nNormalWidth, nNormalHeight, loc_dwROP);
       }
       SelectObject (loc_hCompDC, hObjectToDraw);  //恢复loc_hCompDC中原对象

       if (loc_dwDO == 0)
       {
           loc_dwA = (nIndexOfCard / 4) % 13 + (nIndexOfCard % 4) * 13 + 1;
           if (loc_dwA >= 14 && loc_dwA <= 23 || loc_dwA >= 27 && loc_dwA <= 36)
           {
           //如果要绘画的卡片是红心A至10或方块A至10,则给它们画上黑色边框。
           //注意在资源中看到的这几张牌原本是带红色边框的
                PatBlt (hDC, nXDest + 2, nYDest, nActualWidth - 4, 1, BLACKNESS);
                PatBlt (hDC, nXDest + nActualWidth - 1, nYDest + 2, 
                                                1, nActualHeight - 4, BLACKNESS);
                PatBlt (hDC, nXDest + 2, nYDest + nActualHeight - 1, 
                                                 nActualWidth - 4, 1, BLACKNESS);
                PatBlt (hDC, nXDest, nYDest + 2, 1, nActualHeight - 4, BLACKNESS);
                SetPixel (hDC, nXDest + 1, nYDest + 1, 0x0);
                SetPixel (hDC, nXDest + nActualWidth - 2, nYDest + 1, 0x0);
                SetPixel (hDC, nXDest + nActualWidth - 2, 
                                          nYDest + nActualHeight - 2, 0x0);
                SetPixel (hDC, nXDest + 1, nYDest + nActualHeight - 2, 0x0);
           }
       }
       if (loc_bSharpCorner == FALSE)
           RestoreCorners (hDC, loc_crCornerData, nXDest, nYDest, 
                                            nActualWidth, nActualHeight);
       SetBkColor (hDC, loc_crBkColor);  //恢复hDC原来的背景色
       DeleteDC (loc_hCompDC);
       return TRUE;
  }
  void __stdcall SaveCorners (HDC hDC, 
                    LPCOLORREF lpcrCornerData,
                    int nXLeft,
                    int nYLeft,
                    int nWidth,
                    int nHeight)

  {
       if (nWidth == nNormalWidth && nHeight == nNormalHeight)
       //对于正常尺寸的卡片,存储其四个角的颜色数据
       {
           lpcrCornerData[0] = GetPixel (hDC, nXLeft, nYLeft);
           lpcrCornerData[1] = GetPixel (hDC, nXLeft + 1, nYLeft);
           lpcrCornerData[2] = GetPixel (hDC, nXLeft, nYLeft + 1);    //左上角
           lpcrCornerData[3] = GetPixel (hDC, nXLeft + nWidth - 1, nYLeft);
           lpcrCornerData[4] = GetPixel (hDC, nXLeft + nWidth - 2, nYLeft);
           lpcrCornerData[5] = GetPixel (hDC, nXLeft + nWidth - 1, nYLeft + 1);   //右上角
           lpcrCornerData[6] = GetPixel (hDC, nXLeft + nWidth - 1, nYLeft + nHeight - 1);
           lpcrCornerData[7] = GetPixel (hDC, nXLeft + nWidth - 1, nYLeft + nHeight - 2);
           lpcrCornerData[8] = GetPixel (hDC, nXLeft + nWidth - 2, 
                                                          nYLeft + nHeight - 1);  //右下角
           lpcrCornerData[9] = GetPixel (hDC, nXLeft, nYLeft + nHeight - 1);
           lpcrCornerData[10] = GetPixel (hDC, nXLeft + 1, nYLeft + nHeight - 1);
           lpcrCornerData[11] = GetPixel (hDC, nXLeft, nYLeft + nHeight - 2);   //左下角
       }
       return;
  }

  void __stdcall RestoreCorners (HDC hDC, 
                       LPCOLORREF lpcrCornerData,
                       int nXLeft,
                       int nYLeft,
                       int nWidth,
                       int nHeight)

  {    
       if (nWidth == nNormalWidth && nHeight == nNormalHeight)
       //将由SaveCorners所存储的颜色数据恢复到卡片四角
       {
           SetPixel (hDC, nXLeft, nYLeft, lpcrCornerData[0]);
           SetPixel (hDC, nXLeft + 1, nYLeft, lpcrCornerData[1]);
           SetPixel (hDC, nXLeft, nYLeft + 1, lpcrCornerData[2]);
           SetPixel (hDC, nXLeft + nWidth - 1, nYLeft, lpcrCornerData[3]);
           SetPixel (hDC, nXLeft + nWidth - 2, nYLeft, lpcrCornerData[4]);
           SetPixel (hDC, nXLeft + nWidth - 1, nYLeft + 1, lpcrCornerData[5]);
           SetPixel (hDC, nXLeft + nWidth - 1, nYLeft + nHeight - 1, lpcrCornerData[6]);
           SetPixel (hDC, nXLeft + nWidth - 1, nYLeft + nHeight - 2, lpcrCornerData[7]);
           SetPixel (hDC, nXLeft + nWidth - 2, nYLeft + nHeight - 1, lpcrCornerData[8]);
           SetPixel (hDC, nXLeft, nYLeft + nHeight - 1, lpcrCornerData[9]);
           SetPixel (hDC, nXLeft + 1, nYLeft + nHeight - 1, lpcrCornerData[10]);
           SetPixel (hDC, nXLeft, nYLeft + nHeight - 2, lpcrCornerData[11]);
      }
      return;
  }

  BOOL __stdcall FLoadBack (int nIndexOfCard)
  {
      static int nIndexOfLastCard;  //上次调用本函数时所要求的卡片序号
      
      if (nIndexOfCard != nIndexOfLastCard)
      {
           MyDeleteHbm (hCustomBack);
           hCustomBack = LoadBitmap (hDllModule, LOWORD(nIndexOfCard));
           nIndexOfLastCard = (hCustomBack == NULL? 0: nIndexOfCard);
      }
      return (nIndexOfLastCard != 0);
  }

  HBITMAP __fastcall _LoadCard (int nIndexOfCard)  //参数通过eax传递
  {
      static int nNumOfCardsLoaded;    //已装入的卡片位图数目
      static int nSearchIndex;         //这个变量仅作为某种索引使用,无特别含义
      int loc_nTransId;          //为方便而设置的中间变量,等于所需的位图资源ID

      if (hCards[nIndexOfCard] == NULL)
      {
           if (nNumOfCardsLoaded >= 5)     //已装入的卡片位图太多
           {        
               while (hCards[nSearchIndex] == NULL)  //查找下一个不为空的卡片位图  
               {    
                   nSearchIndex = (nSearchIndex == 51? 0: nSearchIndex + 1);
               }
               DeleteObject (hCards[nSearchIndex]);
               --nNumOfCardsLoaded;       //并删除之
               hCards[nSearchIndex] = NULL;
           }
           //将卡片序号转换成位图资源ID
           loc_nTransId = (nIndexOfCard / 4) % 13 + (nIndexOfCard % 4) * 13 + 1;
           while ((hCards[nIndexOfCard] = LoadBitmap (hDllModule, 
                                            LOWORD(loc_nTransId))) == NULL)
           {
               if (nNumOfCardsLoaded == 0) return NULL;
               //如果删完了所有已加载的位图,还是无法装入所要求的位图,则返回NULL
               while (hCards[nSearchIndex] == NULL)  
               {    
                   nSearchIndex = (nSearchIndex == 51? 0: nSearchIndex + 1);
               }
               DeleteObject (hCards[nSearchIndex]);
               hCards[nSearchIndex] = NULL;
               --nNumOfCardsLoaded;               
           }
           ++nNumOfCardsLoaded;
      }
      return hCards[nIndexOfCard];
  }
  BOOL __stdcall cdtDraw (HDC hDC, 
                          int nXDest, 
                          int nYDest, 
                          int nIndexOfCard, 
                          DWORD dwDrawingOption,
                          COLORREF crBackColor
                         )
  {
      return cdtDrawExt (hDC, nXDest, nYDest, nNormalWidth, nNormalHeight, 
                         nIndexOfCard, dwDrawingOption, crBackColor);
  }
         nTransId = (nIndexOfCard / 4) % 13 + (nIndexOfCard % 4) * 13 + 1;
      mov eax, A
      cmp eax, B
      jne @F
                  ;do something
    @@:

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (24)
雪    币: 424
活跃值: (10)
能力值: ( LV9,RANK:850 )
在线值:
发帖
回帖
粉丝
2
天哪,我又抢到沙发了`
剑哥的好文,我得学习一下ida的用法!
ps:怎么不发表情了
2007-8-13 11:02
0
雪    币: 1505
能力值: (RANK:210 )
在线值:
发帖
回帖
粉丝
3
好详细啊 支持这样的帖子
2007-8-13 11:23
0
雪    币: 259
活跃值: (1704)
能力值: ( LV9,RANK:410 )
在线值:
发帖
回帖
粉丝
4
兄弟30篇精华了,怎么还是普通会员..
2007-8-13 11:29
0
雪    币: 1919
活跃值: (901)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
5
好文章,顶了再学习~~~
2007-8-13 12:45
0
雪    币: 392
活跃值: (909)
能力值: ( LV9,RANK:690 )
在线值:
发帖
回帖
粉丝
6
好文章!总结得非常漂亮!
2007-8-13 12:54
0
雪    币: 2950
活跃值: (1793)
能力值: ( LV9,RANK:850 )
在线值:
发帖
回帖
粉丝
7
因为楼主没有时间灌水
2007-8-13 12:56
0
雪    币: 2575
活跃值: (502)
能力值: ( LV2,RANK:85 )
在线值:
发帖
回帖
粉丝
8
这才叫有份量的文章
2007-8-13 14:39
0
雪    币: 214
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
[QUOTE=;]...[/QUOTE]
好文!!真的是好文~以小见大~!
2007-8-13 14:43
0
雪    币: 6075
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
10
先拜楼主。

最后那个问题,同一个块内写两次相同的调用貌似某些编译器也能优化到一起,不过还不如临时变量来的简捷。
2007-8-13 17:07
0
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
11
好贴,帮顶
2007-8-13 17:47
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tiz
12
看了下兄弟的精华贴,几乎全是逆向  
最近不是搞金山的逆向比赛吗,很有拿冠的可能啊
2007-8-13 17:54
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
厉害,厉害,果然是搞手
2007-8-13 19:28
0
雪    币: 494
活跃值: (629)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝
14
再拜楼主,牛人牛人
2007-8-13 19:49
0
雪    币: 112
活跃值: (16)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
15
我也拜一下楼主!
太牛了!
2007-8-13 21:26
0
雪    币: 114
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
很强,分析的很细致!学习了
2007-8-14 14:09
0
雪    币: 236
活跃值: (11)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
17
先顶再说!!!!
2007-8-14 17:40
0
雪    币: 264
活跃值: (30)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
18
拜一下,俺相差甚远
2007-8-14 18:28
0
雪    币: 193
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
真仔细!学问之道,本该如此!
2007-8-14 23:20
0
雪    币: 405
活跃值: (10)
能力值: ( LV9,RANK:1130 )
在线值:
发帖
回帖
粉丝
20
支持,慢慢看看。真是厉害
2007-8-16 22:40
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
顶...是个好帖.多多学习.
2007-9-14 01:38
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
mark, 以后继续学习
2007-9-14 10:24
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
你好..我也想编个纸牌游戏的外挂.你能教教我吗.
2007-9-14 22:13
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
Platform   SDK   -   Reference   -   Code   Samples   -   COM   -   ActiveXControl   -   BaseCtl   -   Card   

源码据说在这
2007-9-14 23:32
0
雪    币: 1718
活跃值: (5400)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
25
收藏!!!!!!!!!!!!!!!1
2007-9-20 15:45
0
游客
登录 | 注册 方可回帖
返回
//