真是晕啊,这篇文章写到一半的时候竟然有人说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期)