[普及]程序自启动方式 - ActiveX

    每次都是一个假期不开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了。

赞赏

喜欢这篇文章?打赏1元

评论

该昵称已屏蔽 回复

该评论已屏蔽

该昵称已屏蔽 回复

该评论已屏蔽1

该昵称已屏蔽 回复

该评论已屏蔽

captcha