每次都是一个假期不开VC,可能都是在学校做的多了,回家了要换个口味。
已经在学校呆了好几天,明天还要补考,想着想着就泪流满面了。正逢着大一新生到来的时间,我也想到了去年自己刚坐在这所学校的课堂里时,那份激动与不安。也是那时候,我写了我人生第一个Widnows程序,也就此跟VC结下了不解之缘。
当时写的东西是“编程实现自启动”。当时还是懵懂初开,查了不少资料,写了一个基于修改注册表实现的自启动。实现自启动的方式还有很多,当时再就没有继续深入了。
于是,在这一周年之际,我来继续完成我当年没有完成的任务。
这次先来个低烈度的,ActiveX自启动,基本也是通过修改注册表某个键值来达到自启动的效果。
拿来开刀的是类似这个键:HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{C9B4C1CD-B018-4511-B0A1-5476DBF70821}
我们win+R 输入regedit启动注册表,找到该键。
呵呵,当你找的时候可能就发现,你注册表中并没有这个键。其实{C9B4C1CD-B018-4511-B0A1-5476DBF70821}这串字符串是可以更改的,随便更换几个字符,只要是16进制允许的字符都可以。(格式xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
在该键下增加一个子键,键名为“StubPath”,键值为我们待启动的程序。
还有一点要注意的,我们系统在第一次ActiveX自启动完成后,会生成一个键:HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{C9B4C1CD-B018-4511-B0A1-5476DBF70821}
注意哟,它在根节点HKEY_CURRENT_USER下,名字和之前那个相同。当该键存在时,下次自启动就不会加载这个ActiveX了。
所以我们编程时候要注意,每次启动起来后,删除掉该键,这样下次才能继续自启动。
废话不多说,我写了一个小小的示例代码。
int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow // show state ) { HKEY hKey; DWORD dwDpt = REG_OPENED_EXISTING_KEY; long lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{C9B4C1CD-B018-4511-B0A1-5476DBF70821}", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, &hKey); if (ERROR_SUCCESS != lRet) { char SelfFile[MAX_PATH]; char SystemPath[MAX_PATH + 20]; GetSystemDirectory(SystemPath, sizeof(SystemPath)); strcat(SystemPath, "\\activexrun.exe"); GetModuleFileName(NULL, SelfFile, MAX_PATH); CopyFile(SelfFile, SystemPath, FALSE); CreateStringReg(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{C9B4C1CD-B018-4511-B0A1-5476DBF70821}", "StubPath", SystemPath); return 0; } MyCode(); return 0; }
首先使用RegOpenKeyEx函数打开HKEY_LOCAL_MACHINE下的键,如果打开成功,返回ERROR_SUCCESS。
这里判断其打开成功或失败的原因是,如果打开成功,说明有该键不存在,进一步说明这个程序是第一次自启动,这时我们就执行if语句中的内容。
if中代码作用,就是把本程序拷贝进系统目录,并新建该键,以备下次启动。
其中有一个函数,CreateStringReg是我自己定义的。这个函数在这里就是创建一个键名为StubPath的子键,其值是我们需要自启动的程序的绝对地址。
//修改或创建字符串类型的键值 void CreateStringReg(HKEY hRoot, char *szSubkey, char * ValueName, char *Data) { HKEY hKey; long lRet = RegCreateKeyEx(hRoot, szSubkey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL); if (ERROR_SUCCESS != lRet) { //TRACE("error on RegCreateKeyEx: %s\n", szSubkey); return ; } //修改或创建注册表键值 lRet = RegSetValueEx(hKey, ValueName, 0, REG_SZ, (BYTE *)Data, strlen(Data)); if (ERROR_SUCCESS != lRet) { //TRACE("error on RegSetValueEx: %s\n", ValueName); return ; } RegCloseKey(hKey); }
最后,我们的代码在MyCode函数中。这个其实都可以扩展了,我们可以开启一个线程,或者把代码注入到其他进程中。
我的MyCode()函数很简单:
void MyCode(){ WinExec("C:\\windows\\system32\\calc.exe", SW_SHOWDEFAULT); WinExec("C:\\windows\\system32\\start.bat", SW_HIDE); }第一行是打开一个计算器。
第二行很重要,是我写的一个批处理。我之前说到了,第一次ActiveX自启动成功后,会在HKEY_CURRENT_USER生成一个键:\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{C9B4C1CD-B018-4511-B0A1-5476DBF70821}。这个键直接导致下次不会再次自启动,所以我们每次自启动的时候要删除该键。
start.bat:
reg delete "HKEY_CURRENT_USER\Software\Microsoft\Active Setup\Installed Components\{C9B4C1CD-B018-4511-B0A1-5476DBF70821}" /f
原本我是预备在这个程序中删除该键的,但是试了很多次都没有成功。我估计原因是这样,第一次程序自启动成功后,在程序结束运行的时候系统才新建这个键。所以我程序中删除该键的时候,该键其实并不存在。所以,我只好调用一个外部程序(.bat),来删除这个讨厌的键。
这只是一个简单的例子,可以删除这个键的方法还有很多,比如我们把代码注入explorer进程中,利用explorer来删除这个键。
最后说一个小技巧,如何缩小可执行程序的体积。
以前我一直抵触用VC6写程序,过了很久我才明白很多木马用VC6的原因:缩小体积。
VS2010中,在编译的时候会加入很多乱七八糟的编译选项,导致生成物体积庞大。而VC6顾虑没有这么多,所以生成的可执行文件体积小。gh0st为什么把客户端和服务端分开编写的原因我也顿悟了,被控端放在VC6下编译,而主控端没有大小要求就放在VS2010下编译。
还有一个小文件Tiny.h,大家把他include进你的程序最上面,它能极大地减小编译完成后的体积:
/*************** * Leavesongs.com * By phith0n ****************/ #ifdef NDEBUG #pragma optimize("gsy", on) #pragma comment(linker, "/RELEASE") #ifdef _MERGE_RDATA_ #pragma comment(linker, "/merge:.rdata=.data") #endif #pragma comment(linker, "/merge:.text=.data") #pragma comment(linker, "/merge:.reloc=.data") #if _MSC_VER >= 1000 #endif #endif #pragma comment(linker, "/ENTRY:WinMain") #pragma comment(linker, "/MERGE:.rdata=.data") #pragma comment(linker, "/MERGE:.text=.data") #pragma comment(lib, "msvcrt.lib") #if (_MSC_VER < 1300) #pragma comment(linker, "/IGNORE:4078") #pragma comment(linker, "/OPT:NOWIN98") #endif #define WIN32_LEAN_AND_MEAN可能有些地方还需要根据情况修改,倒数第9行的WinMain是入口点函数,大家要酌情修改。
在没有include<tiny.h>时,编译好的程序有16k左右,而包含了该头后,程序只有1.50k了。