许嘉文 酒:用VC实现自删除程序 - 爱上未来的你的日志 - 网易博客

来源:百度文库 编辑:九乡新闻网 时间:2024/04/29 23:22:17

用VC实现自删除程序

计算机 2008-05-13 14:09:43 阅读391 评论0 字号: 订阅

用VC实现自删除程序
在开发程序过程中,有许多情况中都需要执行程序自己把自己从物理磁盘上删除,例如,卸装程序,一些黑客程序获取信息后自清除等,我们把这些具有自删除功能的程序统称为“自杀”程序。对于一名程序员,想必都有在程序中使用代码删除物理磁盘文件的经历吧,我们只需要简单的调用DeleteFile API函数就可以搞定,但是该函数并不能删除自己,当它执行删除自己时,将会导致出现“无法删除文件:拒绝访问。源文件可能正被使用”的错误提示,其原因是由于本程序在执行删除自己代码时仍处于内存中,在Windows中,不可以删除正在执行中的程序。

为了实现程序自删除功能,我们可以通过多进程的方法解决这个问题。可执行文件在结束返回前,创建一个运行命令窗口的新进程(在Windows98中为Command进程,在Windows2000/XP中为CMD进程),当然该命令窗口以隐藏方式执行,并通过传递参数执行删除功能。为了避免可执行文件的映像还在内存中就执行删除,需要把当前进程设置为实时优先级,而命令窗口进程设置为很低的IDLE优先级,这样首先可执行文件结束返回,再运行命令窗口的删除命令,就实现了该文件自身的删除功能。

下面我们就一起动手制作“自杀”(具有自删除功能)程序,程序在Visual C++6.0中开发编译。

首先,启动Visual C++6.0,新建一个MFC AppWizard(exe)类型的项目,项目名为SelfDelete,选择基于对话框模式创建程序框架。

接下来,打开资源编辑器,添加一个按钮控件,设置内容为开始“自杀”,如图一所示:

启动ClassWizard,为新添加的控件新建CLICK事件处理函数,再打开该函数,添加“自杀”代码如下:

void CSelfDeleteDlg::OnButton1()
{
// TODO: Add your control notification handler code here
SHELLEXECUTEINFO sei;
TCHAR szModule [MAX_PATH],szComspec[MAX_PATH],szParams [MAX_PATH];
//获取文件路径名
if((GetModuleFileName(0,szModule,MAX_PATH)!=0) &&
(GetShortPathName(szModule,szModule,MAX_PATH)!=0) &&
(GetEnvironmentVariable("COMSPEC",szComspec,MAX_PATH)!=0))
{ //设置命令行参数。
lstrcpy(szParams,"/c del ");
lstrcat(szParams, szModule);
lstrcat(szParams, " > nul");

//初始化SHELLEXECUTEINFO结构成员
sei.cbSize = sizeof(sei); //设置类型大小。
//命令窗口进程句柄,ShellExecuteEx函数执行时设置。
sei.hwnd = 0;
sei.lpVerb = "Open"; //执行动作为“打开执行”。
sei.lpFile = szComspec; //执行程序文件全路径名称。
sei.lpParameters = szParams; //执行参数。
sei.lpDirectory = 0;
//显示方式,此处使用隐藏方式阻止出现命令窗口界面。
sei.nShow = SW_HIDE;
//设置为SellExecuteEx函数结束后进程退出。
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
//创建执行命令窗口进程。
if(ShellExecuteEx(&sei))
{ //设置命令行进程的执行级别为空闲执行,这使本程序有足够的时间从内存中退出。
SetPriorityClass(sei.hProcess,IDLE_PRIORITY_CLASS);
//设置本程序进程的执行级别为实时执行,这本程序马上获取CPU执行权,快速退出。
SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_TIME_CRITICAL);
//通知Windows资源浏览器,本程序文件已经被删除。
SHChangeNotify(SHCNE_DELETE,SHCNF_PATH,szModule,0);
//执行退出程序
EndDialog(0);
}
}
}

如果上面代码中的注释还不能帮助你理解代码的意义,请不要着急,后面我们还要对这些代码做详细的讲解。现在,你可以开始编译该程序项目,运行一下程序,请在Windows资源浏览器中仔细观察程序文件,当你按下“开始自杀”按钮后几秒,该程序文件从Windows资源浏览器中消失了,这也正在本程序想要得到的效果。
体验了“自杀”程序的神奇之后,让我们回过头来好好的分析一下实现“自杀”功能的代码。
前面我们已经谈过,实现“自杀”功能的核心是在程序中创建一个命令窗口新进程,通过向命令窗口进程传递del命令和参数来删除程序文件。命令窗口程序是由环境变量COMSPEC定义的,Win9x/ME使用COMMAND.COM,WinNT/2K/XP使用CMD.COM。程序把命令字符串“/c del filename > nul”传递给命令窗口,其中filename是需要删除文件的全路径文件名,文件名需要转换为8.3格式;/c开关用于命令窗口退出。
在实现代码中,首先就需要获取当前程序模块的全路径,并将其转化为命令窗口需要的8.3格式。代码中GetModuleFileName(0,szModule,MAX_PATH)函数实现了获取当前程序模式的全路径名称,并存放到变量szModule中。接着使用GetShortPathName(szModule,szModule,MAX_PATH)函数将szModule变量中的程序模块全路径名称转换成命令窗口需要的8.3格式。另外,还调用GetEnvironmentVariable("COMSPEC",szComspec,MAX_PATH)函数从系统环境变量COMSPC中获取了命令窗口程序的全路径。接下来,需要将存放在变量szModule中的具有8.3格式的程序模块全路径字符串组合成命令字符串“/c del ”+szModule+ “> nul”。
有了这些信息之后,就可以调用ShellExecuteEx() API函数创建一个新的命令窗口进程,该函数需要一个SHELLEXECUTEINFO类型的参数,调用ShellExecuteEx()函数必须需要初始化这个类型参数,有关SHELLEXECUTEINFO类型的详细说明请参阅MSDN。本处通过该参数将命令窗口进程的执行动作设为Open、执行文件为命令窗口(路径由szComspec提供)、执行文件参数为上面组合而成的命令字符串、显示方式为隐藏方式(隐藏方式可以阻止出现命令窗口界面)。
命令窗口通过调用ShellExecuteEx()函数以单独的进程运行,它的窗口句柄在SHELLEXECTUEINFO结构中的成员变量hProcess定义。自删除需要解决一个特殊的问题,即主程序必须在命令窗口删除它之前退出并关闭其打开的文件句柄。为了做到这一点,我们必须同步两个独立、并行的进程:当前程序进程和命令窗口进程。这可以通过操作CPU资源优先级来临时降低命令窗口的运行优先级别。这样,主程序将分配到CPU的所有资源直到其正常退出,而阻塞其它任何命令窗口的执行直到主程序结束。下面代码实现调整两个进程的执行优先级:
//设置命令行进程的执行级别为空闲执行,
//这使本程序有足够的时间从内存中退出。
SetPriorityClass(sei.hProcess,IDLE_PRIORITY_CLASS);
//设置本程序进程的执行级别为实时执行,
//这本程序马上获取CPU执行权,快速退出。
SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_TIME_CRITICAL);

到此,自杀”功能基本实现。最后还需要做的事是调用SHChangeNotify(SHCNE_DELETE,SHCNF_PATH,szModule,0) 函数通知Windows资源浏览器已成功删除了程序文件。如果用户当前Windows资源浏览器窗口正处于程序文件目录的话,这个通知是非常必要的,它会导致Windows资源浏览器马上从程序文件目录列表中删除该程序文件项。做完了以上工作,一定要调用退出程序的代码,此处使用了EndDialog()函数,如果不及时退出程序的话,命令窗口进程就不能正常删除程序文件,其原因在前面我们已经研究过。
当你阅读完本文之后,一定与我有一个共同的感受:“自杀”也同样如此精彩!
11:35 | 添加评论 | 发送消息 | 固定链接 | 查看引用通告 (0) | 写入日志 | 技术1月29日
VC中无LIB时的DLL隐式链接,制作与VC++相符合的LIB函数符号输入库
请大家注意!这种方法只能应用于输出为C格式的__stdcall调用方式!

1. 使用VC++的工具DUMPBIN将DLL中的导出函数表导出到一定义(.DEF)文件
EXAMPLE:
DUMPBIN VideoDeCoder.dll /EXPROTS /OUT:VideoDeCoder.def
2. 将导出的.DEF文件整理为一符合.DEF个数的函数导出文件
EXAMPLE:
VideoDeCoder.DEF 文件内容如下
Dump of file VideoDeCoder.dll
File Type: DLL
Section contains the following exports for VideoDeCoder.dll
0 characteristics
3D49E48F time date stamp Fri Aug 02 09:46:55 2002
0.00 version
1 ordinal base
11 number of functions
11 number of names
ordinal hint RVA name
1 0 00010F60 _TM_ClearDecoderBuff@4
2 1 00010E80 _TM_CloseDecoder@4
3 2 00010F00 _TM_DecodePicture@4
4 3 00010ED0 _TM_DecodePictureHeader@4
5 4 00010FD0 _TM_GetFileEnd@4
6 5 00011030 _TM_GetUValue@4
7 6 00011060 _TM_GetVValue@4
8 7 00011000 _TM_GetYValue@4
9 8 00010E10 _TM_OpenDecoder@8
10 9 00010F30 _TM_ReturnType@4
11 A 00010F90 _TM_SetFileEnd@8
Summary
2000 .data
1000 .rdata
1000 .reloc
15000 .text
按照以下方法整理:
1>添加LIB说明
LIBRARY "VideoDeCoder" ;"xx"为DLL名称
DESCRIPTION "VideoDeCoder library"
2>去掉导出函数说明端以外的内容,在LIB说明下添加 "EXPROTS" 说明导出函数
LIBRARY "VideoDeCoder"
DESCRIPTION "VideoDeCoder library"

EXPORTS
ordinal hint RVA name
1 0 00010F60 _TM_ClearDecoderBuff@4
2 1 00010E80 _TM_CloseDecoder@4
3 2 00010F00 _TM_DecodePicture@
4 3 00010ED0 _TM_DecodePictureH
5 4 00010FD0 _TM_GetFileEnd@4
6 5 00011030 _TM_GetUValue@4
7 6 00011060 _TM_GetVValue@4
8 7 00011000 _TM_GetYValue@4
9 8 00010E10 _TM_OpenDecoder@8
10 9 00010F30 _TM_ReturnType@4
11 A 00010F90 _TM_SetFileEnd@8
3>将所有的函数放至行首,去掉 "hint" 和 "RVA" 数据,留下函数的序号 "ordinal" ,在序号前加上 "@" 符号
形成 "_导出函数名@参数字节和 @序号" 此种格式(__stdcall 方式调用导出的函数符号是 "函数名称@参数字节和").
最后形成.DEF文件如下:
LIBRARY "VideoDeCoder"
DESCRIPTION "VideoDeCoder library"

EXPORTS
TM_ClearDecoderBuff@4 @1
TM_CloseDecoder@4 @2
TM_DecodePicture@4 @3
TM_DecodePictureHeader@4 @4
TM_GetFileEnd@4 @5
TM_GetUValue@4 @6
TM_GetVValue@4 @7
TM_GetYValue@4 @8
TM_OpenDecoder@8 @9
TM_ReturnType@4 @10
TM_SetFileEnd@8 @11
3. 使用VC++的LIB工具,带/DEF:(.def文件名) /MACHINE:IX86(80X86机器),就输出符合VC++格式的的LIB文件了.
EXAMPLE:
LIB /DEF:VideoDeCoder.def /MACHINE:IX86

4. 接时带上LIB文件链接;注意的是当有些动态库DUMPBIN的只有函数名,无"@nn"的参数格式,如C++Builder写的DLL,输出就只有函数名符号,链接时就会报错:
error LNK2002:unresolved external symbol "functionname@nn"
提示程序中引入的函数符号无法识别,这时只要将DEF文件中相应的函数名称改为functionname@nn方式,重新建立
LIB,重新链接即可.

这样就制作成功了符合VC调用方式的LIB了!

参考:MSDN2000

要值得一说的是!BORLAND C++BUILDER有一个很好的工具IMPDEF可以直接将DLL中的函数输出到.DEF文件中,只要做一点点修改就可以成为符合VC的DEF文件!

IMPDEF xxx.def xxx.dll

这种方法只能应用于输出为C格式的__stdcall调用方式经过我的论证有些错误!我利用Borland C++和VC++做论证,发现:

而在C++BUILDER中!
__cdecl的函数输出前会带一"_"
__stdcall无特征,只输出函数名
__fastcall函数输出前带一"@"
都无"@nn"后缀格式!
在VC中!
__cdecl无特征,只输出函数名
__stdcall的函数输出前会带一"_"后缀带"@nn"
__fastcall函数输出前带一"@"后缀带"@nn

只要将BC的DEF文件中函数申明格式转换为VC识别的格式就可以利用LIB工具生成LIB;要使用C分格输出(extern "C")才是必须的!而且别忘了在DEF文件中的函数申明不要带“_”啊!:)不然会出现error LNK2001的链接错误!
11:36 | 添加评论 | 发送消息 | 固定链接 | 查看引用通告 (0) | 写入日志1月28日
连接GPS后鼠标乱跳的解决方法
方法一,是从网上看来的一种解决方法。
部分使用XP的用户反映,当插入USB的GPS设备后,启动XP时鼠标会在屏幕上乱跳,如果你也有这种情况,可以通过以下的方式解决: 拔掉GPS设备通过以下方式屏闭系统的串口鼠标
1. 打开“我的电脑”的“属性”
2.选择“高级”页的“启动与恢复”的“设置”
3.点击“编辑”
4. 把“/NoSerailMouse”(注意大小写)加到以下这行的最后:multi(0)disk(0)rdisk(0)partition(2) XP=Windows XP/fastdetect 加完后变成这样:multi(0)disk(0)rdisk(0)partition(2)XP=Windows XP/fastdetect/NoSerialMouse
5. 保存并退出

方法二:
1. 打开“我的电脑”的“属性”
2. 选择“硬件”页的“设备管理器”
3. 展开“鼠标和其它指针设备”,你会发现多了一个"Microsoft Serial BallPoint”
4. 选择“Microsoft Serial BallPoint”的“属性”
5. 把“设备用法”设置为“不要使用这个设备(停用)”


微软件的相关资料
http://support.microsoft.com/kb/833721/en-us,就在“/fastdetect”部分。

http://support.microsoft.com/kb/833721/zh-cn
14:04 | 添加评论 | 发送消息 | 固定链接 | 查看引用通告 (0) | 写入日志 | 技术1月23日
大字节序vs小字节序
1.介绍
作者:nero_qiu 提交日期:2005-11-28 22:02:00

大字节序:把高有效位放在低地址段,例如0x1234,在存储器中为
地址 数值
0x4 4
0x3 3
0x2 2
0x1 1

小字节序:把低有效位放在低地址段,例如0x1234,在存储器中为
地址 数值
0x4 1
0x3 2
0x2 3
0x1 4

在传输word, half-word, double-word的时候,读取地址时总是由低往高,通常x86等指令集用的是小字节序,RISC等其他指令集用大字节序。在同一指令集中进行数据传输不需要进行字节序转换,在不同的指令集之间进行传输时需要进行字节序转换,如进行网络传输。网络传输用的都是大字节序。

历史:网络由University of California Berkeley开发,当时用的是RISC指令集。为大字节序,从此以后网络传输为大字节序。

2.判断

如何判断是大字节序还是小字节序
方法1:
union
{
short int i;
char c;
}t;
t.i=1;
if(t.c == 1)
printf("little endian\n");
else
printf("big endian\n");
方法2:

#include
int main (void)
{
int i = 0x12345678;
if (*(char *) & i == 0x12)
printf ("Big endian\n");
else if (*(char *) & i == 0x78)
printf ("Little endian\n");
}

3.实现

#define GLUE_SWAP32(x) \
({ bits32_t _x_ = (x);\
((((_x_ & 0x000000ff) << 24) & 0xff000000)|\
(((_x_ & 0x0000ff00) << 8) & 0x00ff0000)|\
(((_x_ & 0x00ff0000) >> 8) & 0x0000ff00)|\
(((_x_ & 0xff000000) >> 24) & 0x000000ff)); })
11:15 | 添加评论 | 发送消息 | 固定链接 | 查看引用通告 (0) | 写入日志关于结构体内存的对齐方式
在C语言中,我们经常使用结构体(struct) 。对于结构体的描述,很多C语言的书中会提到结构体所占用的内存与其内的成员在结构体中的声明顺序有关,但是很少有书讲述了结构体占用的内存与结构体的成员声明顺序如何相关的(即结构体在内存中的对齐方式是怎么样的)。
首先我们来看以下代码的输出结果:

#include
#pragma pack(2)
struct T
{
char i;
int d;
char ii;
};
#pragma pack()
int main(int argc,char * argv[])
{
printf("%d ",sizeof(struct T));
return 0;
}


最后输出的结果为:8。
语句#pragma pack(2)的作用是定义了结构体的对齐方式,在这里指定了是按规2字节对齐,在这里我们用PPB来表示该指令指定的对齐字节数,上述代码中的PPB=2。
结构体的内存对齐规则是:

1.每个成员分别按自己的对齐字节数和PPB(指定的对齐字节数)两个字节数最小的那个对齐,并能最小化长度。
2.复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度。
3.结构体对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐。
我们按上面的规则来分析上述代码的输出结果为什么是8,分析如下:

变量i默认为1字节,PB=2,所以i按1字节对齐。
变量d默认为4字节(在32位机器中int为4字节),PB=2,所以d按2字节对齐。
变量ii默认为1字节,PB=2,所以ii按1字节对齐。
此时结构体的计算出的字节数为7个字节。最后按规则3,结构体对齐后的字节数为8。可以用图1来示意结构体的字节对齐情况。 █ █ █ █ █ █ █ █

█ 变量占用的内存 █ 变量d占用的内存 █ 变量ii点用的内存 █ 为保证内存对齐填补的内存

图1 结构体内存对齐示意图


对下代码中在一个结构体中声了一个结构体变量。

#include
#pragma pack(2)
struct T
{
char i;
int d;
char ii;
};
#pragma pack()

struct TT
{
char c;
struct T t;
long long d;
};

int main(int argc,char * argv[])
{
printf("%d ",sizeof(struct TT));
return 0;
}


这段代码输出的结果为:24。结构体T占用8字节,那么在结构体TT中它是按什么方式对齐的呢,按照规则2进行对齐。long long 类型为8个字节。所以结构体的内存对齐方式为:1***,1***,1111,1****,11111111。
9:16 | 添加评论 | 发送消息 | 固定链接 | 查看引用通告 (0) | 写入日志内存对齐方式
不同的编译器所采取不同的内存对齐方式,如VC++编译器默认的对齐方式是8BYTE大小,
如下结构
struct Person
{
char Name[16];
char Sex;
int Age;
}
如果用sizeof(Person)得到的结果是24;而不是21 这样做的目的是为了在访问内存的时候达到最快的速度。我们可以使用#pragma pack(size)//size为你指定的内存对齐方式。 并用#pragma pack()来结束指定的方式。如果让size=1;则上述结构的大小就是21BYTE了

用VC实现自删除程序 - 爱上未来的你的日志 - 网易博客 详解用VC实现bmp位图的打开 - fengqing888的日志 - 网易博客4563 VS2008在VC/MFC中为程序定义全局快捷键 - 弘毅的日志 - 网易博客 vc++操作注册表实现自启动程序 靓帽 - 爱上芳菲的日志 - 网易博客 VC操作Excel - hope多多的日志 - 网易博客 母亲的品质决定孩子的未来 -本文引用自牛哥坊的日志 - 网易博客 格式化自己,只为删除你 - 安国的博客的日志 - 网易博客 教你有效删除怪文件 - 理睬的日志 - 网易博客 如何删除上网痕迹 - 哥们干杯!的日志 - 网易博客 如何删除上网痕迹 - 哥们干杯!的日志 - 网易博客 【引用】怎样删除垃圾文件 - abc6671的日志 - 网易博客 删除好友 黄书亭 - 漫步者的日志 - 网易博客 有钩有织的毯子 - 爱上芳菲的日志 - 网易博客 父亲说给儿子的话:你要学会爱上女人 - 南歌走天下的日志 - 网易博客 用 VC 获取其它运行中程序的命令行参数 - wonsoft的专栏 - CSDN博客 钩织漂亮yy - 爱上芳菲的日志 - 网易博客 爱上梦中人 - 武克的日志 - 网易博客 新款毛线袜套 - 爱上芳菲的日志 - 网易博客 钩针技巧完全教程 - 爱上芳菲的日志 - 网易博客 什么是程序集 - 而立的日志 - 网易博客 牡丹花开教程 - cpq731的日志 - 网易博客 - 爱上芳菲的日志 - 网易博客 VC浮动窗口的实现 vc和matlab的混合编程 - Silence的日志 - 网易博客