标 题: 【原创】Detour补丁实现加载任意SkinSharp皮肤文件(有码)
作 者: NoGood
时 间: 2012-06-14,16:05:11
链 接:
http://bbs.pediy.com/showthread.php?p=1080040#post1080040
开篇:
古老的技术,大牛绕道吧。如果版主觉得对初学者有帮助就给个那啥吧。挂机好无聊的。
最近买了本看雪的《加密与解密第三版》,看到PE文件那章时,自己用VS2010仿写了书上的PEInfo程序。不过最后完工时发现界面不美观。
于是,便给程序价格皮肤,用的是Skin++,可不知怎么的,就是编译不过,以前用VS2008没出现过,心想可能它不支持VS2010吧。于是乎,在网上搜有关的皮肤文件,忽然看到一篇博文(VC皮肤库SkinSharp 1.0.6.6的使用):
VC皮肤库SkinSharp 1.0.6.6的使用
按照上面方法一时果然成功。
不过,我发现它只能加载本目录下的skinh.she皮肤文件,如果想加载其他皮肤文件,必须把文件名改成skinh.she才行。Oh My god,不能加载任意皮肤,让我感觉很是不爽。于是便有了下文:
分析篇:
根据那篇博文的描述可知:
1.SkinH_Attach() 加载本目录下的skinh.she皮肤文件
2. 如果第1条成立,那么它肯定在代码中用到了skinh.she文件名作为参数,再如果它
没有对字符串加密的话,我们可以很轻松地替换这个字符串为我们自己指定的皮肤文
件名,实现加载任意皮肤文件的目的。
嗯,说干就干,咱们现在就开始分析这个SkinH_Attach函数。那怎么找到这个函数呢?
我用的方法是:
1.在调用SkinH_Attach前,先调用MessageBox函数,里面指定特殊的字符串
2.我们在OD中查找此字符串,这样可以找到MessageBox的函数调用处。进而在它
(MessageBox)下面的函数调用就是SkinH_Attach了。这样我们就可以分析它了。
以下是我在MFC的OnInitDialog函数中的代码:
MessageBox( "Test" ); //在OD中查找Test,来到MessageBox函数调用处,
SkinH_Attach(); //进而来到SkinH_Attach函数调用处。
1.OD中查找Test:
2.对找到的Test字符串双击或回车:
3.此时我们便来到函数调用处:
4.我们对此行(SkinH_Attach的调用处)单击并按回车
5. 来到如下代码处:咦,怎么不是函数体而是一个跳转?我们对这个跳转按回车跟进去
6.来到了真正的函数体部分:哇!代码好多啊,不必惊慌,这些都不是我们要分析的,我们主要看能不能找含有字符串“skinh.she”的部分。
7.滚动鼠标的滑轮或下拉右侧的滚动条,并注意观察OD反汇编窗口中的注释部分,走着走着,果然不出我们所料,就在100195DC这行我们找到了字符串“skinh.she”。
它应该是个相对路径并且是UNICODE字符串,因为有个'\',而且还调用了wcscat进行串联。
继续分析,我们看到这句PUSH SkinHu.1002A644(机器码为:68 44A60210)压入的是绝对地址,也就说这个字符串它在全局数据段,它是个全局变量或静态变量。
总结:
1.函数的开头是一个固定的jmp指令(因为用的是偏移地址,而不是绝对地址,如果是绝对地址,可能要重定位,改变指令机器码),由它跳转到真正的函数体部分,我们可以将此指令作为特征码,对函数进行匹配并获取真正的函数体部分。
2.函数体中字符串“skinh.she”是个存放在全局数据段UNICODE字符串,而且它是个相对路径,也就是说我们要替换的话一定要注意这一点。
补丁篇:
有了上面的成果后,我们就可以编程实现Detour补丁了。
具体步骤如下:
1.获取SkinH_Attach函数入口地址,对它的第一条指令进行解析(字节码匹配和获取真正的函数体)
2.在真正的函数体中,定位含有字符串“skinh.she”的代码,并替换里面的路径名为
我们自己的皮肤文件路径名。
以下是我实现的代码:
BOOL DetourPatchSkinName( WCHAR szPath[] )
{
#define ADDR_SIZE 4 //地址长度,32位为4字节
#define PAGE_SIZE 0x2000 //按页搜索
byte JumpCode[] = { 0xE9, 0xEB, 0x58, 0xFF, 0xFF }; //第一处特征码
byte PushCode[] = { 0x68, 0x44, 0xA6, 0x02, 0x10 }; //第二处特征码
LPBYTE skinFunc; //待解析的函数(也就是SkinH_Attach)
DWORD codeSize; //第一处特征码长度
DWORD pathAddr; //新路径名的地址
int destAddr; //跳转到真函数体的偏移
DWORD i; //扫描特征码时要用的索引
static WCHAR SkinPath[MAX_PATH] = {0}; //皮肤文件路径名,要注意放在数据段,因为SkinH_Attach函数用的是绝对地址
//Step 1.Copy path of skin into static buffer.
memcpy( SkinPath, szPath, wcslen( szPath ) * sizeof(WCHAR) );
//Step 2.Check first feature code.
skinFunc = (LPBYTE)SkinH_Attach;
codeSize = sizeof( JumpCode );
i = 0; //从偏移0开始匹配
while ( codeSize-- ){
if ( skinFunc[i] != JumpCode[i] ){
MessageBox( NULL, "特征码不匹配!", "Hint", MB_OK );
break;
}
i++;
}
//Step 3.Check second feature code.
memcpy( &destAddr, (skinFunc+1), ADDR_SIZE ); //得到偏移
skinFunc += destAddr; //移动到真正的函数体部分
i = 0; //从偏移0开始匹配
while ( TRUE ){
if ( skinFunc[i+0] == 0x68 &&
skinFunc[i+1] == 0x44 &&
skinFunc[i+2] == 0xA6 &&
skinFunc[i+3] == 0x02 &&
skinFunc[i+4] == 0x10 )
{
//MessageBox( NULL, "Found it!", "Hint", MB_OK );
break;
}else if ( i >= PAGE_SIZE ){ //按页搜索
//MessageBox( NULL, "Don't Found it!", "Hint", MB_OK );
return FALSE; //找不到,走人
}
i++;
}
//Step 4.Detour patch.
pathAddr = (DWORD)SkinPath; //新皮肤路径地址
memcpy( &skinFunc[i+1], &pathAddr, ADDR_SIZE ); //替换旧的
return TRUE;
}
OnInitDialog函数体进行调用:
如果必要请改下SkinH_Attach函数所在section的属性为可写。
最后附上源码和皮肤文件:
源码和皮肤文件
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!