总结下初学windows API编程杀毒的经历
Abstract:On the way of fighting with computer virus, I learned much. At the beginning, I used software tools wrote by others to kill virus manually, then I tried shell command to auto execute. At last, I learned to use windows api to help me to do this, it was effecient. In this blog, I will introduce to the antivirus windows api programming.
刚接触windowsAPI编程,就是缘于反病毒。由于学校内打印机是公用的,有个病毒一度爆发,病毒行为是自我复制到除系统盘以外的分区根目录(包含U盘,也正因如此,病毒在优盘根目录下写入了autorun.inf信息,当插入优盘,而系统又正好开启了硬件自动播放功能,就会自动执行病毒,导致了广泛传播),将原始文件夹全部设置为系统属性和隐藏属性,因而原始文件夹不可见,然后病毒伪装成原始文件夹,虽然没观察到有进一步恶意行为,但是对于没有开启隐藏文件夹可见的童鞋,还以为自己文件丢失了,着急忙慌的找人修。我细细看了下,病毒没怎么注意自我保护,只开启了双进程保护,进程名叫安全卫士.exe和ppsap.exe,哈哈哈,所以还比较好处理,用内核级反病毒神器xuetr同时杀两个进程(单独杀一个,会被另一个进程再次启动),删除注册表,清除根目录病毒文件,还原原始文件夹属性,就搞定了,但是帮几个同学手动杀了之后就开始想自动化的办法。首先是用批处理的方法:
核心指令 attrib –s –h “filename”
那么封装一下逻辑,先结束进程,然后判断是否有拖入批处理程序的文件或文件夹,没有报错,如果是文件,弹出操作提示,如果是文件夹,再弹出是否处理子目录及递归子目录。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
@echo off echo *********************************** echo This program is edited by atp echo. @理学院一队 echo phone:88557(内线) echo *********************************** echo. echo Now let's begin to clean the virus! pause taskkill /f /im iexplore.exe /im ppsap.exe /im 安全卫士.exe /t if exist C:\Windows\System32\ppsap.exe (del C:\Windows\System32\ppsap.exe & echo ppsap.exe has been cleaned) else echo ppsap.exe missing if exist C:\Windows\System32\安全卫士.exe (del C:\Windows\System32\安全卫士.exe & echo 安全卫士.exe has been cleaned) else echo ppsap.exe missing echo *********************************** echo Congratulations! echo This virus has been cleaned! echo *********************************** pause setlocal enabledelayedexpansion echo *********************************** echo Now start to change the attribute! echo *********************************** if "%~1"=="" ( echo 您没有拖入任何磁盘或文件或文件夹 echo. echo 请把要处理的磁盘或文件或文件夹 echo 拖到批处理文件图标中会自动执行 echo. echo 不要双击启动! echo. pause ) :loop if "%~1"=="" goto :eof if not exist "%~1" (echo 路径不存在)&pause&shift&goto loop echo 当前处理的磁盘或文件或文件夹: echo %~1 echo. echo 请选择要进行的操作,然后按回车 echo. echo 加系统和隐藏属性……………………………1 echo 去系统和隐藏属性……………………………2 echo 不处理…………………………………………3 echo. set /p "c1=选择 (1,2,3) (不输入为!c1!,默认为2):" if "!c1!"=="1" (set "d=+") else if "!c1!"=="3" (pause&shift&goto loop) else set "d=-" set "a=%~a1" if /i "!a:~0,1!"=="d" ( echo. echo 这是一个文件夹,请选择要进行的操作,然后按回车 echo. echo 只对该文件夹本身……………………………1 echo 只对该文件夹中的文件夹和文件……………2 echo 对该目录下的一切的文件夹和文件…………3 echo. set /p "c2=选择 (1,2,3) (不输入为!c2!,默认为1):" if "!c2!"=="2" ( pushd "%~1" for /f "delims=" %%i in ('dir /a /b "%~1"') do attrib.exe !d!s !d!h "%%i" popd ) else if "!c2!"=="3" ( for /f "delims=" %%i in ('dir /a /b /s "%~1"') do attrib.exe !d!s !d!h "%%i" ) else ( attrib.exe !d!s !d!h "%~1" ) ) else ( attrib.exe !d!s !d!h "%~1" ) echo *********************************** echo Congratulations! echo All the operations have been done! echo Thanks for using ! echo *********************************** pause&shift&goto loop |
用这个批处理确实大大提高了清楚病毒的效率,就只需要手动清楚下注册表启动项(不清楚也没关系,哈哈),然后挨个磁盘拖到批处理就搞定了。
不过,正好接触了window API编程,这个清除病毒的过程正好涉及进程、注册表、文件这几大块,那就写个程序拿这个病毒练练手呗。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
// kill48KB.cpp : 定义控制台应用程序的入口点。 /* 一个文件夹病毒的专杀工具,很初级的,我是通过编写这个才入门API的,代码在visual studio 2010,windows7 7600下测试通过,我学写的时候苦于没有可模仿的现成的代码,希望帮助那些也想学习API的人 */ #include "stdafx.h" #include<windows.h> #include<stdio.h> #include<TlHelp32.h> #include"Psapi.h" #include<string> #include <stdlib.h> #include<time.h> //#define _WIN32_WINNT 0x0501 #define BUFSIZE MAX_PATH using namespace std; BOOL EnablePriv(); //进程权限提升函数 BOOL TerminateProcessFromId(DWORD PID); //进程结束函数 BOOL KillProcess(); //扫描并查找病毒进程返回PID VOID ShowInformation(); //打印信息 BOOL CleanVirusFile(); //清除病毒文件 BOOL FileCleaninDrive(LPSTR szDrive); //清除磁盘根目录下的病毒 BOOL GetDirverInfo(LPSTR szDrive); //获取驱动器信息 BOOL CleanRegister(); //清除注册表信息 BOOL RepairTheDamage(LPSTR szDrive); //修复被病毒更改的属性 int _tmain(int argc, _TCHAR* argv[]) { SetConsoleTitle("48KB-Killer"); ShowInformation(); //输出信息 double RunTime[2]={0.0,0.0}; RunTime[0]=(double)clock()/CLOCKS_PER_SEC; EnablePriv(); //提升进程权限 KillProcess(); //结束进程 CleanVirusFile(); //清除病毒母本及副本 CleanRegister(); //清除病毒启动项 for(int i=0;i<argc-1;i++){ RepairTheDamage(argv[i+1]); //按传入参数扫描磁盘恢复被病毒修改的属性 } RunTime[1]=(double)clock()/CLOCKS_PER_SEC; printf("\n *****************************************\n"); printf("\nAll Finished , totally using time : %.3lf seconds\n",RunTime[1]-RunTime[0]); printf("Best wishes to you & your computer! #^_^\n"); printf("\nYou can exit this program now!\n"); printf(" Or it will exit itself in 60 seconds\n"); Sleep(60000); //观察输出信息 return 0; } void ShowInformation(){ HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); //获取控制台窗口句柄 if(hConsole!=INVALID_HANDLE_VALUE){ SetConsoleTextAttribute(hConsole,FOREGROUND_GREEN|FOREGROUND_INTENSITY); } //设置文字颜色和文字高亮 printf(" *****************************************\n"); printf(" *This program was written by Atp *\n"); printf(" * or QQ:344174246 *\n"); printf(" *****************************************\n"); //printf("\n请以管理员身份运行此程序,\n 否则可能无法删除病毒母本!\n"); printf("\nPlease run this program as a Administrator,\n Or it may not clean the viruses!\n"); //printf("具体方法是在右键菜单中点击\"以管理员身份运行\"~\n"); printf("The way is right click the icon ,\n then choose\"Run as a Administrator\" from the menu~\n"); //倒数 for(int i=0; i<3; i++) { if(i<2) printf("%u seconds left to run!\n", 3-i); else printf("%u second left to run!\n", 3-i); /*if(i==0) printf("Press Enter to continue.\n"); if(!getchar()){ break; }*/ Sleep(1000); // 每毫秒打印一次 } printf("\n * * * * * * * * Start ! * * * * * * * *\n"); printf(" *****************************************\n"); } BOOL KillProcess(){ HANDLE hSnap = INVALID_HANDLE_VALUE;//进程快照句柄 PROCESSENTRY32 pe; //进程信息结构 BOOL VirusFlag=false; //标记病毒存在信息 BOOL bNext; pe.dwSize = sizeof(pe); LPSTR Virus[3]={"安全卫士.exe","ppsap.exe","iexplore.exe"}; hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); //建立一个进程快照 //获得第一个进程句柄,调用成功返回TRUE,否则返回FALSE bNext = Process32First(hSnap,&pe); while(bNext){ if(pe.th32ProcessID==0||pe.th32ProcessID==4){ bNext = Process32Next(hSnap,&pe); continue; }//忽略System和System Idle Process进程 //结束进程部分 if(stricmp(Virus[0],pe.szExeFile)==0||stricmp(Virus[1],pe.szExeFile)==0||stricmp(Virus[2],pe.szExeFile)==0){ //判断进程名是否为病毒进程名 if(stricmp(Virus[2],pe.szExeFile)!=0){ printf(" Find virus's process:%s PID:%d\n",pe.szExeFile,pe.th32ProcessID);//结束进程函数 } if(TerminateProcessFromId(pe.th32ProcessID)) { printf(" Kill process %s successful!^_^!\n",pe.szExeFile); } else{ printf(" Kill process %s failed~ -_-#\n",pe.szExeFile); } VirusFlag=true; } bNext = Process32Next(hSnap,&pe); } CloseHandle(hSnap); //关闭进程快照 return VirusFlag; } BOOL CleanVirusFile(){ TCHAR SystemPath[MAX_PATH]; ULONGLONG liFileSize; GetSystemDirectory(SystemPath,MAX_PATH); LPSTR VirusPath[8]={"\\安全卫士.exe","\\ppsap.exe","C:\\Windows\\SysWOW64\\安全卫士.exe","C:\\Windows\\SysWOW64\\ppsap.exe","C:\\WINDOWS\\system32\\安全卫士.exe","C:\\WINDOWS\\system32\\ppsap.exe","C:\\Windows\\System32\\安全卫士.exe","C:\\Windows\\System32\\ppsap.exe"}; char* Virus[2]={"安全卫士.exe","ppsap.exe"}; //C:\\Windows\\System32\\安全卫士.exe WIN32_FILE_ATTRIBUTE_DATA FileData; for(int i=0;i<8;i++){ if(i<2) lstrcat(VirusPath[i],SystemPath); GetFileAttributesExA(VirusPath[i],GetFileExInfoStandard,&FileData); //获取文件属性 liFileSize=FileData.nFileSizeHigh; liFileSize <<= sizeof(DWORD)*8; liFileSize += FileData.nFileSizeLow; DWORD a; if(liFileSize==49152&&GetBinaryTypeA(VirusPath[i], &a)){ SetFileAttributes(VirusPath[i],FileData.dwFileAttributes&~(FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_READONLY)); //设置文件属性 int j=0; i%2==0?j=0:j=1; if(!DeleteFileA(VirusPath[i])) //需要以管理员身份运行 { //printf("File %s delete:\n",Virus[j]); } else { printf(" File %s delete success!\n",Virus[j]); } } } //*****遍历磁盘寻找最后一个和可移动磁盘 CHAR szLogicalDriveStrings[BUFSIZE]; PCHAR szDrive; ZeroMemory(szLogicalDriveStrings,BUFSIZE);//初始化缓冲区 GetLogicalDriveStrings(BUFSIZE - 1,szLogicalDriveStrings); // 获取逻辑驱动器卷标名 szDrive = (PCHAR)szLogicalDriveStrings; // 循环处理每个卷 do { GetDirverInfo(szDrive); szDrive += (lstrlen(szDrive)+1); } while(*szDrive!='\x00'); return true; } BOOL GetDirverInfo(LPSTR szDrive) { UINT uDriveType; uDriveType = GetDriveType(szDrive); //获取驱动器类型 int FixDrivCount=0; switch(uDriveType){ case DRIVE_REMOVABLE: FileCleaninDrive(szDrive); break; case DRIVE_FIXED: FileCleaninDrive(szDrive); break; case DRIVE_RAMDISK: FileCleaninDrive(szDrive); break; default: break; } return TRUE; } BOOL FileCleaninDrive(LPSTR szPath){ CHAR szFilePath[MAX_PATH]; //构造代表子目录和文件夹路径的字符串,使用通配符“*” lstrcpy(szFilePath, szPath); lstrcat(szFilePath, "\\*"); WIN32_FIND_DATA FindFileData; HANDLE hListFile; //WIN32_FILE_ATTRIBUTE_DATA FileAttData; bool VirusFlag=false; //查找第一个文件/目录,获得查找句柄 hListFile = FindFirstFile(szFilePath,&FindFileData); if(hListFile==INVALID_HANDLE_VALUE)//判断句柄 { //printf("错误:%d",GetLastError()); return 1; } else { do { ULONGLONG liFileSize; CHAR szFullPath[MAX_PATH]; DWORD a; lstrcpy(szFullPath, szPath); lstrcat(szFullPath, "\\"); //计算文件大小 lstrcat(szFullPath,FindFileData.cFileName); liFileSize=FindFileData.nFileSizeHigh; //高位移动32位 liFileSize <<= sizeof(DWORD)*8; liFileSize += FindFileData.nFileSizeLow; if(liFileSize==49152&&GetBinaryTypeA(szFullPath, &a)){ //判断文件大小和类型是否为可执行文件 SetFileAttributes(szFullPath,FindFileData.dwFileAttributes&~(FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_READONLY)); if(DeleteFile(szFullPath)){ //删除文件 printf(" File %s Delete Success!\n",FindFileData.cFileName); } VirusFlag=true; } } while(FindNextFile(hListFile, &FindFileData)); if(VirusFlag==true){ RepairTheDamage(szPath); } } return true; } BOOL RepairTheDamage(LPSTR szPath){ CHAR szFilePath[MAX_PATH]; //构造代表子目录和文件夹路径的字符串,使用通配符“*” lstrcpy(szFilePath, szPath); lstrcat(szFilePath, "\\*"); WIN32_FIND_DATA FindFileData; HANDLE hListFile; //WIN32_FILE_ATTRIBUTE_DATA FileAttData; bool VirusFlag=false; //查找第一个文件/目录,获得查找句柄 hListFile = FindFirstFile(szFilePath,&FindFileData); if(hListFile==INVALID_HANDLE_VALUE)//判断句柄 { //printf("错误:%d",GetLastError()); return 1; } else { do { CHAR szFullPath[MAX_PATH]; lstrcpy(szFullPath, szPath); lstrcat(szFullPath, "\\"); lstrcat(szFullPath,FindFileData.cFileName); //修复文件属性:隐藏,系统 if(FindFileData.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN&&FindFileData.dwFileAttributes&FILE_ATTRIBUTE_SYSTEM){ //判断属性是否是系统隐藏 if(SetFileAttributes(szFullPath,FindFileData.dwFileAttributes&~(FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_READONLY))){ printf(" The arribuates of file %s has recovered to normal!\n",FindFileData.cFileName); } } } while(FindNextFile(hListFile, &FindFileData)); } return true; } BOOL CleanRegister(){ HKEY hKey=NULL; RegOpenKeyExA(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",0,KEY_ALL_ACCESS,&hKey); //打开注册表项 if(!RegDeleteValue(hKey,"PPS Accelerator")){ //删除键值 printf(" The KEY has been repaired\n"); } //else{ // printf("the KEY repairing failed\n"); //} return true; } BOOL EnablePriv()//提升进程权限 { HANDLE hToken; if ( OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken) ) { TOKEN_PRIVILEGES tkp; LookupPrivilegeValue( NULL,SE_DEBUG_NAME,&tkp.Privileges[0].Luid );//修改进程权限 tkp.PrivilegeCount=1; tkp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges( hToken,FALSE,&tkp,sizeof tkp,NULL,NULL );//通知系统修改进程权限 return( (GetLastError()==ERROR_SUCCESS) ); } } BOOL TerminateProcessFromId(DWORD PID) //结束进程函数 { BOOL bReturn=FALSE; HANDLE hProcess=::OpenProcess(PROCESS_ALL_ACCESS,FALSE,PID); if(hProcess!=NULL){ bReturn=::TerminateProcess(hProcess,0); } CloseHandle(hProcess); return bReturn; } |
代码注释还算详细,所以就不多说,其中判断病毒文件用了文件大小的方法,其实蛮愚蠢的,也可能误杀,但是效率确实特别高,读取文件系统的属性可比磁盘IO快很多,不过为以防万一,后面还是写了个基于md5散列检测的查杀方法,可以先根据文件大小,然后计算散列值,再杀,如此误杀概率几乎为0。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
// AllScanAndDelete.cpp : 定义控制台应用程序的入口点。 // #include<stdio.h> #include<TlHelp32.h> #include"Psapi.h" #include<string> #include <stdlib.h> #include<time.h> #include <iostream> #include <fstream> #include <string.h> #include <windows.h> #include "md5.h" #include "stdafx.h" using namespace std; #define MAX_PATH_LENGTH 500 void allScanEXE(); //遍历所有磁盘分区 void scanEXE(char * lpPath); //扫描指定路径 void dealProcess(char * lpPath); //处理指定文件 void md5Process(char * lpPath, char * md5String); //计算md5 bool searchSignature(char * Md5String); //比对签名 void doFindProcess(char * lpPath); //处理病毒 int counter; int _tmain(int argc, _TCHAR* argv[]) { counter = 0; // scanEXE("R"); allScanEXE(); cout<<"There are"<<counter<<"files found in total~"<<endl; return 0; return 0; } void scanEXE(char * lpPath) { CHAR szFilePath[MAX_PATH]; //构造代表子目录和文件夹路径的字符串,使用通配符“*” lstrcpy(szFilePath, szPath); lstrcat(szFilePath, "\\*"); WIN32_FIND_DATA FindFileData; HANDLE hListFile; //WIN32_FILE_ATTRIBUTE_DATA FileAttData; bool VirusFlag=false; //查找第一个文件/目录,获得查找句柄 hListFile = FindFirstFile(szFilePath,&FindFileData); char szFind[MAX_PATH_LENGTH]; WIN32_FIND_DATA FindFileData; strcpy(szFind,lpPath); strcat(szFind,"\\*.*"); HANDLE hFind = ::FindFirstFileA(szFind,&FindFileData); if(INVALID_HANDLE_VALUE == hFind) return; while(1) { if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if(FindFileData.cFileName[0] != '.') { char szFile[MAX_PATH_LENGTH]; strcpy(szFile,lpPath); strcat(szFile,"\\"); strcat(szFile,FindFileData.cFileName); scanEXE(szFile); } } else { int len = strlen(FindFileData.cFileName); const char *p = (char *)&FindFileData.cFileName[len-3]; if(_stricmp(p,"exe") == 0) { char strFileName[MAX_PATH_LENGTH]; strcpy(strFileName,lpPath); strcat(strFileName,"\\"); strcat(strFileName,FindFileData.cFileName); dealProcess(strFileName); } } if(!FindNextFile(hFind,&FindFileData)) break; } FindClose(hFind); } void allScanEXE() { for(char cLabel='c';cLabel<='z';cLabel++) { char strRootPath[] = {"c:\\"}; strRootPath[0] = cLabel; if(GetDriveType(strRootPath) == DRIVE_FIXED) { strRootPath[2] = '\0'; scanEXE(strRootPath); } } } void dealProcess(char * lpPath) { cout<<lpPath<<endl; char Md5String[33]; bool find = false; md5Process(lpPath,Md5String); find = searchSignature(Md5String); if(find) { counter++; doFindProcess(lpPath); } } void md5Process(char * lpPath, char * Md5String) { char asc[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; unsigned char * buffer; unsigned char digest[16]; unsigned int len; MD5_CTX md5; ifstream inFile; inFile.open(lpPath,ios::in|ios::binary); if(!inFile) { cout<<"file open failed!"<<endl; return; } else { inFile.seekg(0,ios::end); len = inFile.tellg(); if(len > 52428800) { inFile.close(); return; } inFile.seekg(0); buffer = new unsigned char[len]; inFile.read((char *)buffer,len); inFile.close(); } md5.MD5Update(buffer,len); md5.MD5Final(digest); for(int j=0;j<16;j++) { Md5String[2*j] = asc[digest[j]>>4]; Md5String[2*j+1] = asc[digest[j]&0x0f]; } Md5String[32] = '\0'; delete buffer; } bool searchSignature(char * Md5String) { char Signature[] = "3F6DDC443BE05FC52A345C820CB1B64A"; //更改此处为病毒签名即可 if(_stricmp(Signature,Md5String) == 0) return true; else return false; } void doFindProcess(char * lpPath) { int yn; cout<<"Really delete "<<lpPath<<" ?"<<endl; cin>>yn; if(yn) DeleteFile(lpPath); return; } |
由于这里只是我演示了下根据md5签名扫描病毒的技术,并没有实际使用,所有没有处理交互逻辑,处理某个病毒只需要改searchSignature里的那个数组。其实可以用来寻找病毒隐藏的母本,但前提是母本和病毒是相同的文件。
代码下载:
https://github.com/atp798/BlogStraka/tree/master/AntiVirusSimple