1. 程式人生 > >程式刪除自己,改寫自己

程式刪除自己,改寫自己

程式刪除自己改寫自己

有這樣一個問題:讓程式本身能限制它的執行次數,比如只能執行5次。
目前使用的方法大都是通過讀取和改寫外部檔案的資料來判斷執行的次數,比如增加配置檔案(.inf),讀寫系統登錄檔,或增加其它類似檔案。也就是說,程式執行要依靠外部資料來判斷執行次數。
有沒有不依靠外部資料的呢。。。
有這樣想法:程中檔案本身有一個數據5,當這個程式結束時,把這個5變成其它數,比如4,當下次執行的時候取讀4,執行結束時再把4變成3,此下去。。。這樣就能通過這個數字的變化來控制程式執行次數。
大家都知道一個windows應用程式在運程中,所佔用的程式檔案是不能常規刪除和改寫的。這就是說,程式在磁碟上的檔案裡面的這個“10”不能在執行時改寫,檔案被系統防寫了(可以讀取)。
現在請做這樣一件事:新建一個文字檔案A.txt,然後在裡面寫入del %0,儲存之後把A.txt檔案字尾後改掉,變成A.bat。del %0的意思刪除檔案本身。這樣就建成了一個批處理檔案,雙擊它就能運行了。雙擊它之後A.bat檔案沒了!(至於為啥windows程式不能刪除自己,bat批處理檔案可以刪除自己,我也說不清楚,我對cmd沒深入瞭解)

程式思路是這樣的:
1、程式hello.exe讀取自己到記憶體中
2、hello.exe在記憶體中查詢“5:次數在這裡”這個字串
3、將上步查詢到的“5:次數在這裡”這個字串改寫為“4:次數在這裡”
4、將記憶體中的全部資料寫入temp.exe檔案
5、hello.exe先建立temp.bat檔案,呼叫temp.bat檔案後退出
6、temp.bat先刪除hello.exe,再將temp.exe改名為hello.exe,後最刪除自已

我用的環境是dev-c++,win8系統,能夠正常編譯執行。(直接copy回去研究吧,呵呵)
#include <iostream>
#include <stdlib.h>
#include <string.h>

struct file_inf{char* name;long len;};//用來儲存檔案的資訊 

void* mem_with_file(struct file_inf&);//將檔案讀入記憶體,並返回記憶體的地址。檔案的長度,檔名存入file_inf結構體 
char* file_with_mem(struct file_inf&,void*);//將記憶體中的內容寫入檔案 
void* write_mem(char *dest,char *src, int n);//更改記憶體中的資料 
char* search_bite(void *buffer, size_t count,char* str);//查詢記憶體中的資料 
bool  write_bat(char*); // 用來建立bat檔案 
char* FileName(char*);//獲取檔名 
//以上函式的具實現,請下main函式後面的定義 

int main(int argc, char** argv) 
{
	file_inf testfile;
	testfile.name=argv[0];//程式的檔名 
	testfile.len=0;
	void* p=mem_with_file(testfile);//開啟程式本身檔案,並將檔案內容讀入記憶體 
	testfile.name="temp.exe";//預設為本程式的檔名,要更改 
	char* A="5:次數在這裡";//用於儲存程式執行次數的資料,初始為5 ,要定義為常量
	//不能用A[]="5:次數在這裡"這條句,常量在編譯時就已經存入了,要用變數  
	char temp[50];
	strcpy(temp,A);
	char* s=search_bite(p,testfile.len,temp);//在記憶體中查詢 5:password 這個字串 
	//if(s!=NULL){printf("find!\n");}else{printf("Can not find!\n");};//顯示查詢結果 
	int times=atoi(A);//將字串轉化成數字,數字應該是5 
	if(times==0){ 
	free(p);
	p=NULL; 
	printf("次數達到0\n",times);//次數限制相關的語句可以放在這裡了 
	getchar();//暫停以便看到輸出的資訊 
	return 0;
	}else{
	printf("剩餘次數%d\n",times);	
	}
	temp[0] ='0'+times-1;//將字元5變成字元4 
	if(s!=NULL)write_mem(s,temp,strlen(temp));//更改記憶體中的資料(以字串改寫的方式) 
	file_with_mem(testfile,p);// 將記憶體中的改好的資料寫入檔案
	free(p);//小小記憶體洩露哦 
	p=NULL; 
	char* MyFileName=FileName(argv[0]);
	write_bat(MyFileName);//建立bat檔案 
	printf("q%c %d3299%d\n有想法可以討論下\n按任意鍵繼續。。。\n",'q',411,56);// 不喜歡就關掉吧,呵呵 
	getchar();//暫停程式,這裡你可以檢視程式生成的臨時檔案 	
	system("start temp.bat") ;
	return 0;
}

void* mem_with_file(file_inf& _inf ) 
{
	long L;//檔案長度單位:位元組 
	FILE* fp;
	void* addr=NULL; 
	fp=fopen(_inf.name,"rb");//二進位制方式讀取文字內容,換行佔兩個位元組 
	//if(fp==NULL) { printf("file can not open!\n");return NULL;}else{printf("file open!\n");};
	fseek(fp,0,2);//檔案內指向末尾 
	L=(long)ftell(fp);//檔案長度 
	addr=malloc(L);//分配記憶體空間 
	if(addr!=NULL)
	{
	fseek(fp,0,0);//檔案內指向開頭
	long i=fread(addr,1,L,fp);
	_inf.len=i;
	} else {
	printf("addr is NULL !\n");return NULL;
	}
	fclose(fp);
	return addr; 
}

char* file_with_mem(struct file_inf& _inf,void* buffer)
{
	FILE* fp;
	fp=fopen(_inf.name,"wb");//二進位制方式,新建一個二進位制檔案,已存在的檔案將被刪除,只允許寫	
	fwrite(buffer,1,_inf.len,fp);
	fclose(fp);	
	return _inf.name;
}

void* write_mem(char *dest,char *src, int n)// 將src中的前n個位元組複製到dest
{
	if(n>strlen(src)) return NULL;
	return strncpy(dest,src,n);
}

char* search_bite(void *buffer, size_t count,char* str)//在以buffer為始地點,count為度長度的一塊記憶體中查詢str,若找到返回找到的位置 
{
	if(strlen(str)>count) return NULL;
	for(size_t i=0;i<=count-strlen(str);i++)
	{
		char* p=strstr((char*)buffer+i,str);
		if(p!=NULL) return p;
	}
	return NULL;
}

bool write_bat(char* fileName)
{
	FILE* fp;
	fp=fopen("temp.bat","w");//文字方式,新建檔案,已存在的檔案將被刪除,只允許寫
	if(fp==NULL) { printf("file can not open!\n");return false;}
	else
	{//以下是寫入bat檔案的具體內容 
	fprintf(fp,"\
	@echo off\n\
	if exist %s del %s\n\
	if exist temp.exe ren temp.exe %s\n\
	del %%0 & taskkill  /f /im cmd.exe\n",fileName,fileName,fileName);
	};
	fclose(fp);
	return true;
}

char* FileName(char* PathName){
	for(int i=strlen(PathName);i>0;i--){
		if(PathName[i-1]=='\\'||PathName[i-1]=='/')
		return &(PathName[i]);
		};
		return NULL;
}