当前位置:编程学习 > 网站相关 >>

托管和非托管联手打造DONET灰色按钮克星

文/图 程晨 www.chenchen.net
“灰色按钮克星”是一款不错的软件,可以激活灰掉的按钮,无形中达到破解的目的。但是,如果用它激活在DONET平台上编写的软件时,你会发现没有任何作用。我想这可能是DONET平台自身的特点造成的吧。那么,我们如何激活DONET平台上的软件呢?在阅读本文核心内容前,先说说我是如何想到并一步一步实现的。
在C#编程中,如果我们想让一个Panel中的所有控件都灰掉,是怎么做到的呢?不会是根据每个控件的name来一个一个灰掉吧?如果控件很多,岂不是要写很多条语句。最简单的写法莫过于此:

foreach(Control control in panel1.Controls)
{
control.Enable = false;
}

反之,如果想让每个控件都激活,那么把它的Enable设置为true就行了。于是我突发奇想,如果让这段代码在其他已运行的DONET软件上面执行,不就可以实现按钮的激活了吗?那么如何在其他DONET软件上面执行这段代码呢?——聪明的你肯定也想到了,对!就是进程注入!
   众所周知,在非托管编程中,当进程注入成功后,软件会自动执行DLL文件中的DllMain函数里面的内容。那么,托管DLL文件的DllMain函数在哪儿呢?带着这样的疑问,我做了实验,将C#写的DLL文件注入到其他进程后却根本没法执行。(由于本人能力有限,还不知道怎么去执行托管DLL的入口点函数,望高手指点)失望之后,冷静的思考了一下,既然C#不行,那就尝试使用VC++.net啊!因为它可以同时进行托管和非托管的混合编程,说不定可以实现我们的目的^_^。
   拿出VS2005,新建一个VC++.net的Win32项目,在“应用程序类型”中选择DLL,一步一步下来后就会生成这个项目。记得设置该项目的属性,让其支持托管代码并添加引用System 和System.Windows.Forms的命名空间,如图1和图2所示。

图1
  
图2
首先我尝试在DllMain函数中加入以下代码。

::System::Windows::Forms::MessageBox::Show("hello");

编译后发现报错:“System::Windows::Forms::MessageBox:托管类型或托管函数不能用于非托管函数”。我再次尝试写了一个全新的纯托管函数:

void HelloWord()
{
::System::Windows::Forms::MessageBox::Show("hello");
}

当我把这个函数放在如下代码段的后面时,编译通过了!

#ifdef _MANAGED
#pragma managed(pop)
#endif

那么,该如何去调用这个函数呢?如果在DllMain中直接调用,仍然会出现“托管类型或托管函数不能用于非托管函数”的编译错误。最终我想到了C++的导出函数。

extern "C" _declspec(dllexport) void HelloWord()
{
::System::Windows::Forms::MessageBox::Show("hello");
}

编译通过!我迫不及待地使用Stud_PE来查看这个生成的DLL文件,惊喜的发现,导出函数已经存在了,如图3所示。
  
图3
   有了这个导出函数就足够了,这意味着我们可以通过非托管代码来调用这个托管代码的函数了。聪明的你也肯定想到了向其他进程注入托管代码的办法了。我们一起来设想一下步骤:
1)用VC++.net写一个有导出函数的DLL项目,该导出函数包括托管代码,该托管代码的作用就是用于激活DONET的按钮。
2)写一个非托管的DLL,在这个DLL文件的DLLMain函数中调用第1步中的导出函数。
3)编写一个WinForm程序,将第2步生成的DLL文件注入到DONET程序中去。
看了上面的介绍,不知大家有没有被我绕晕了,还是用代码来说明问题吧。

第1步的实现
上面我说过,在C#中可以使用foreach循环达到使按钮激活的方法,现在我们用VC++.net来实现,顺便再增加一个可以显示隐藏按钮的功能,只要在合适的位置调用这个方法就行了。详细代码参见随文附件。

private: void EnableControl(Control ^ctl)
{
for each (Control ^control in ctl->Controls)
{
if(checkBoxIsShowHidden->Checked)
if(!control->Visible) control->Visible =true;
if(!control->Enabled) control->Enabled =true;
}
}
    
但是还有一个问题,这个DLL一旦进入目标程序后,就会不停的把所有按钮都激活了,可能会影响软件的正常使用。于是我采用了比较笨的办法,就是在这个DLL中新建一个Form,再加两个按钮,让用户选择是激活还是不激活。当然,还有其他的一些细节问题,我自认为都不是本文重点部分,有兴趣的可以看看附件中的源代码。

第2步的实现
   为了突出说明非托管可以调用前面写的托管函数,我们使用VC++6.0来写这个非托管项目。打开VC++6.0,新建一个DLL项目,如图4所示,写一段函数类似于如下的代码。
  
图4

DWORD WINAPI LoadCSDll(LPVOID lpParam)
{
HMODULE hand = LoadLibraryA("LoadCSDll.dll");
//这个DLL为前面vc.net编译的
if(hand == NULL)
{
::MessageBoxA(NULL,"无法载入动态连接库","error",MB_ICONERROR);
return NULL;
}
typedef HRESULT (*GetDll)();
GetDll pFunc;
pFunc = (GetDll)::GetProcAddress(hand,"LoadDoNet");
if ( pFunc != NULL )
{
(*pFunc)();
}
}

之后,再在DllMain中加一个线程操作,免得注入后把主程序卡死。

switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
CreateThread(NULL,NULL,LoadCSDll,NULL,NULL,NULL);
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;

第3步的实现
线程注入操作已经被高手们研究“烂”了,随便“摆渡”一下肯定会有很多不同语言写的模板代码。由于不是本文的重点部分,我就不班门弄斧了,还是一起来看看我写的程序吧。如图5所示,我的这款软件名字叫做“超级灰色按钮克星”,当然也包括了Win32平台的灰色按钮激活功能,不过我新增加了一个“显示隐藏控件”的功能,其实就是调用IsWindowVisible(hwnd)和ShowWindow(hwnd,SW_SHOW)这个两个API就可以了,没有什么技术含量^_^。
  
图5
软件用法很简单,只要在“箭靶”上按住鼠标左键不动,将其“箭靶”拖动到目标程序就可以了。关于这个拖动“箭靶”的实现原理,大家可以参考《黑客防线》2007年第3期《VC轻松打造SPY++》一文,相信会有更多的收获!
下面,我再说说DONET平台的激活使用。
1)        把平台选择切换到DONET平台。
2)        拖动“箭靶”到需要激活的程序上(附件提供测试程序),如图6所示.
  
图6
3)        如果使用“是否显示隐藏控件”功能,那么点击checkbox后,将鼠标再次移回需要显示隐藏控件的窗体即可(本例为Form1),如图7所示.
  
图7
    注入到进程后,主要就是通过鼠标位置来确定需要激活或显示控件了,具体代码请参考附件源码。还有一点要注意一下,开启杀毒软件的主动防御功能,可能会影响到注入操作。本文程序在Windows XP SP2 + NET2.0 + VS2005 + VC6.0编译测试通过
补充:综合编程 , 其他综合 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,