Windows-Hack-Programming
本文章记录windows常见黑客编程技术,实际上都是比较老的了,新的也不可能发出来,之前也是分析恶意代码实践的lab,只能中ida的伪代码中学到一些知识点,比较零散,也没有参考代码,现在买了本《windows黑客编程技术基础》来系统学习学习。
关于vs的一些小操作
主要是属性的设置。
- 字符集的设置。
- 优化
- 编译的exe能在其他电脑上打开https://blog.csdn.net/u014493318/article/details/60875012,主要就是运行库的问题。
- 数据执行保护(DEP)
- 随机基地关闭
- #pragma warning(disable:4996),能直接使用strcmp等函数。
查阅官方文档没有的函数http://undoc.airesoft.co.uk/
第二章-基础技术
运行单一实例
有时候,病毒木马为了被靶机激活,一般都会选择植入到靶机的多个位置等,但是多了就容易暴露,所以有没有方法可以实现同一时间只让一个进程执行我们的恶意代码呢。互斥对象就可以做到这个,当然方法也不唯一,启动进程参数也可以做到这点,比如说彩虹猫的两种main参数。
原理主要是利用CreateMutex的返回值特性完成的。
返回值
如果函数成功,则返回值是新创建的互斥对象的句柄。
如果函数失败,则返回值为NULL。要获取扩展的错误信息,请调用GetLastError。
如果互斥锁是一个命名互斥锁并且该对象在此函数调用之前存在,则返回值是现有对象的句柄,并且GetLastError函数返回ERROR_ALREADY_EXISTS。
互斥对象也在win32的学习中也学过的,下面直接上代码。
1 |
|
DLL延迟加载
设置下vs就可实现,属性->链接器->输入->延迟加载的DLL->输入:SkinPPWTL.dll
资源释放
就是把各种资源放到exe文件里面,这样程序可以就只有一个,但是可以包含很多其他文件,当然大小也会变大。在《恶代》一书中的lab3-3就有这样的技术。
自闭了,写出来一直findresource错误,但是生成的exe明明包含了这个资源。。
第三章-注入技术
dll注入的技术,已经总结了一篇文章,现在只写写当时没总结到的。
突破SEESION 0 隔离的远线程注入
这个也就是CreateRemoteThread的底层函数ZwCreateThreadEx,CreateRemoteThread的本质是调用ZwCreateThreadEx,也是很久前那些大师傅调试CreateRemoteThread函数,最后找到了为什么不能注入SEESION 0进程的原因,就是ZwCreateThreadEx的第七个参数CreateThreadFlags如果为1的话,就会造成注入失败,所以想要向系统进程注入dll文件,就必须设置这个参数为0。
1 |
|
ZwCreateThreadEx和NtCreateThreadEx好像是一样的。用户模式下是一样的。
1 |
|
实际上很多系统进程还是没法注入。
APC注入
新知识点,先了解APC的概念。
APC(Asynchronous Procedure):异步过程调用,指函数在特定线程中被异步执行,看到这,我又产生了疑惑,什么是异步执行。
同步:
所谓同步,就是发出一个功能调用时,在没有得到结果之前,该调用就不返回或继续执行后续操作。
简单来说,同步就是必须一件一件事做,等前一件做完了才能做下一件事。
例如:B/S模式中的表单提交,具体过程是:客户端提交请求->等待服务器处理->处理完毕返回,在这个过程中客户端(浏览器)不能做其他事。
异步:
异步与同步相对,当一个异步过程调用发出后,调用者在没有得到结果之前,就可以继续执行后续操作。当这个调用完成后,一般通过状态、通知和回调来通知调用者。对于异步调用,调用的返回并不受调用者控制。
对于通知调用者的三种方式,具体如下:
状态:即监听被调用者的状态(轮询),调用者需要每隔一定时间检查一次,效率会很低。
通知:当被调用者执行完成后,发出通知告知调用者,无需消耗太多性能。
回调:与通知类似,当被调用者执行完成后,会调用调用者提供的回调函数。
例如:B/S模式中的ajax请求,具体过程是:客户端发出ajax请求->服务端处理->处理完毕执行客户端回调,在客户端(浏览器)发出请求后,仍然可以做其他的事。
每个线程都有一个APC队列,而QueueUserAPC函数可以可以向APC队列插入函数,先进先出,而且插入后,函数不会立即执行,除非函数处于通知(警告)状态,当线程在内部使用SignalObjectAndWait,SleepEx,WaitForSingleObjectEX,WaitForMultipleObjectsEX,等函数把自己挂起就处于了警告状态,就会执行APC队列里面的函数。
也是对APC有了个初步的了解,而QueueUserAPC这个函数更是有和CreateRemoteThread相似的特性。
1 |
|
所以,我们可以将第一个参数设置为LoadLibraryA的函数指针,第三个参数设置为dll路径,就可以达到注入的目的,如果说CreateRemoteThread是在目标进程中创造了一个线程用LoadLibraryA(“dll路径”),QueueUserAPC就是向进程的线程的APC队列插入了个待执行的函数。
为了提高注入成功率,将会对一个进程的所有线程的APC队列进行QueueUserAPC函数注入。
代码
1 |
|
注意事项,环境需要为x64环境编译,并且dll文件也应该是64位的,发现了个奇怪的现象,对QQ.exe注入的时候,直接崩溃报错。
第四章-启动技术
如何启动一个进程。
启动进程API
第一个API,WinExec。
UINT WinExec(
[in] LPCSTR lpCmdLine,
[in] UINT uCmdShow
);
第一个参数就是指定文件路径或者cmd命令行,第二个参数就是是否显示窗口,常见的有SW_HIDE隐藏窗口并激活另一个窗口,SW_SHOWNORMAL 激活并显示一个窗口。
第二个API,ShellExecute。
HINSTANCE ShellExecuteA(
[in, optional] HWND hwnd,
[in, optional] LPCSTR lpOperation,
[in] LPCSTR lpFile,
[in, optional] LPCSTR lpParameters,
[in, optional] LPCSTR lpDirectory,
[in] INT nShowCmd
);
第3个参数代表文件路径,最后个参数代表窗口显示。
第三个API,CreateProcess,不多讲了,功能很多。
下面是三个函数的总合代码。
1 |
|
内存直接加载
本质就是自己用代码去加载一个DLL文件或者exe文件,不得不说,书上讲的真少,实际上过程和脱一个壳都差不多了。需要了解点PE结构的知识,PE结构嘛,无非就是一大堆结构体。
本过程相比于我在第七章讲的傀儡进程-PE映像切换,多了两个步骤,修改PE文件重定位表信息和填写PE文件导入表信息(IAT)。
//《逆向工程核心原理》中都能找到两个概念的介绍
PE重定位
向进程的虚拟内存中加载PE文件时,文件会被加载到PE头的ImageBase所指的地址处,该位置存在被占用可能,此时PE装载器会将其加载到其他未被占用的空间。PE重定位指PE文件无法加载到ImageBase所指位置,而是加载到其他地址时发生的一系列行为。
创建进程后,EXE文件会首先加载到内存,所以EXE文件无需考虑重定位问题。
IAT
简单来说就是存储函数地址的结构体
然后产生了三个问题
- 为什么PE映像切换就不用做这两件事,就可以加载一个exe文件了呢?
- 为什么需要修改PE文件重定位表信息?
- 为什么需要填写PE文件导入表信息(IAT)?
在粗略阅读下面文章后,稍微理解了一些。
windows进程/线程创建过程 — windows操作系统学习
[原创]PE映像切换技术(Process Hollowing)不需要填充IAT表和进行重定位的原因
[原创]为什么某些壳脱壳后需要修复IAT
然后以我自己的理解来解答下,可能并不对。
- 第一个问题:因为我们以挂起线程调用CreateProcess()函数后,然后切换了PE映像,这两个操作都是在重新启动线程后完成的。其实我对此解释也抱着半信半疑的态度,一是根据其文章的描述不能具体看到起函数是否执行了相关操作,二是因为看了对PE重定位的概念,创建进程后,EXE文件会首先加载到内存,所以EXE文件无需考虑重定位问题。在利用CreateProcess()创建进程的前提下,对于exe文件来说,已经分配了4gb的虚拟内存,默认imagebase一般都是0x400000,不需要重定位,所以删除.reloc这个节区对程序的运行一般不会照成任何影响。
- 第二个问题:虽然说exe不需要考虑重定位问题,但是那是在创建进程的前提下,而我们手动加载一个exe文件实际上利用的是VirtualAlloc函数在本进程中再申请一段内存空间作为映像的基址,所以肯定就需要重新定位下了。
- 第三个问题,为什么要填写IAT,就是因为dll不一定都完全按照他们的imagebase来加载到相应的基地址,IAT对应的硬编码不一定就是确切的函数地址,exe加载到内存后准确的地址会取代改位置的值。所以,我们需要自己利用GetProcAddress函数来取得确切的地址,然后填入进去。
下面来利用一个随便写的exe程序来感受下PE重定位和IAT,实际上都可以在《逆向工程核心原理》一书中找到相关知识点讲解。
// Based relocation format.
//
//@[comment("MVI_tracked")]
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
// WORD TypeOffset[1];
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
//
// Based relocation types.
//
#define IMAGE_REL_BASED_ABSOLUTE 0
#define IMAGE_REL_BASED_HIGH 1
#define IMAGE_REL_BASED_LOW 2
#define IMAGE_REL_BASED_HIGHLOW 3
#define IMAGE_REL_BASED_HIGHADJ 4
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_5 5
#define IMAGE_REL_BASED_RESERVED 6
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_7 7
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_8 8
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_9 9
#define IMAGE_REL_BASED_DIR64 10
IAT,还是贴出相关结构体。
IMAGE_IMPORT_DESCRIPTOR
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
} DUMMYUNIONNAME;
DWORD TimeDateStamp; // 0 if not bound,
// -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND)
DWORD ForwarderChain; // -1 if no forwarders
DWORD Name;
DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
IMAGE_IMPORT_BY_NAME
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
CHAR Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
我们修改的主要是IMAGE_IMPORT_DESCRIPTOR结构体中的FirstThunk,也就是IAT中的RVA。
那么如何获得相关函数的地址,然后填上去呢,我们可以利用IMAGE_IMPORT_DESCRIPTOR结构体获得dll的名称,和该程序调用了dll那些函数的名称,然后用GetProcAddress去获得函数实际地址然后填入IAT的相对应位置。并且DLL的导出函数有两种,分为函数名称导出,和序号导出,这也是个新知识点吧https://www.xuebuyuan.com/1266850.html。
这里多说一句,为什么会有IAT这个东西,为什么我们调用某个官方函数时,不直接调用起真实地址。原因就在于,对于不同的windows版本和DLL版本,可能这些地址是不同的,所以采取的一般方式是在程序加载时,才获取真实的函数地址到IAT中的对应位置。
完整代码如下
1 |
|
运行下,还是运行的PE映像切换的弹窗exe。
第六章-提权技术
我们一般的用户就只有用户权限,在自删除技术中我们使用MoveFileEx,就只能利用管理员权限运行程序才能真正修改到注册表相应的位置,并且我们创建或者修改系统服务,修改HKEY_LOCL_MOCHINE注册表,ExitWindows关机的时候,都需要管理员权限,这也解决了当初我写MAZE病毒分析的时候,为什么仿写的关机部分没有成功的原因。也是为什么我们需要学习提权的原因。
进程访问令牌权限提升
感觉都是套路操作了,也没讲原理。
Windows的常见权限https://docs.microsoft.com/zh-cn/windows/win32/secauthz/privilege-constants?redirectedfrom=MSDN
下面代码申请了关机权限,实现了个关机程序
1 |
|
第七章-隐藏技术
进程伪装
所谓进程伪装就是将进程的一些基本信息伪装成某些系统exe进程的信息,使其在任务管理器或者process monitor中看起来合理一点。
主要思路就是获取PEB结构体的地址,然后去修改PEB.processparameter这个结构体的内容。
我们来看看这个结构体的样子。
typedef struct _RTL_USER_PROCESS_PARAMETERS {
BYTE Reserved1[16];
PVOID Reserved2[10];
UNICODE_STRING ImagePathName; //进程的图像文件的路径。
UNICODE_STRING CommandLine; //传递给进程的命令行字符串。
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
我们要修改的就是ImagePathName,和ImagePathName。
但是这两个成员实际上也是UNICODE_STRING结构体。如下。
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
所以我们修改的是ImagePathName.Buffer和ImagePathName.Buffer,可以看到是宽字符,所以我们修改的时候,也要用宽字符。
如果是找自己进程的PEB地址的方法有很多种,在windows反调试文章中已经介绍过了。
而查找指定进程的PEB就需要用到书中的一个函数NtQueryInformationProcess,将其第二个参数设置为ProcessBasicInformation,时就能返回PROCESS_BASIC_INFORMATION结构体信息,里面存储了PebBaseAddress的地址。
1 |
|
上面的例子只能针对本进程,因为如果读取其他进程的PEB地址,用指针的方式会导致访问异常。如果想修改其他进程的话,必须用ReadProcessMemory来读取其他进程的PEB地址。
// 获取指定进程进本信息结构中的PebBaseAddress
::ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL);
// 获取指定进程环境块结构中的ProcessParameters, 注意指针指向的是指定进程空间中
::ReadProcessMemory(hProcess, peb.ProcessParameters, &Param, sizeof(Param), NULL);
傀儡进程-自我创建
书上的这种傀儡进程实际上就是自我创建,和《逆向工程核心原理》中55章内容差不多。实际上可以用来作为反调试的一种方式,但是很容易绕过。
下面的例子和书上实际上差不多,只是书上是用的shellcode,然后写入了父进程,并申请为可执行的空间。而我的例子只是把新ip变成了一个函数的地址。
1 |
|
好家伙,运行直接被火绒杀了,火绒有点凶。
傀儡进程-pe映像切换技术
这个技术我之前也写过文章介绍过,但是没去自己实现下代码,现在来亲自实现下,包括安洵的出题都是用的现成的。
CreateProcess函数创建一个进程前会把该进程映射到虚拟内存中,但是如果我们以挂起模式创建,然后把映射的程序改为另一个程序的映射,就可以达到运行另一个程序的目的了。
1 |
|
real.exe是个弹窗,我们运行下可以看到。
第十章-传输技术
主要是网络通信相关的知识点,因为想要窃取别人电脑的信息,肯定需要网络来通信,将别人电脑的信息发送到自己电脑上。
Socket-TCP
TCP通信是需要服务端和客户端的。主要是字节流传输数据。TCP是基于链接的,每条TCP链接只能是点到点的关系。只能是一个ip一个端口。
服务端,主要的流程是WSAStartup 进行初始化–> socket 创建套接字–> bind 绑定–> listen 监听–>accept 接收请求–> send/recv 发送或接收数据,设计到很多函数,官方文档查就是了。
代码如下
1 |
|
服务端在运行后就会处于等待连接状态,也就是停在accept的位置,等待客户端进行连接,连接成功后就可以发送和接收信息了,这时我们可以用netstat -an命令去看端口信息。
客户端,主要的流程是WSAStartup 进行初始化–> socket 创建套接字–>connect 连接到服务端–> send/recv 发送或接收数据,比服务端的步骤要少一些,而且代码也差不多。
1 |
|
就在我以为万事大吉的时候,编译出来的程序一直互相无法通讯,焯,接下来就是找问题时间,在经过2个小时的努力后,终于调试发现套接字莫名奇妙会变0,造成的原因是在函数内部重新定义了全局变量。。。也是长个教训。
最后还是实现了成功通讯,下面看看效果,先打开服务端等待连接,在打开。
Socket-UDP
UDP面向文件传输数据,不能保证可靠传输,可能丢包,并且相对于TCP,没有那么安全,但相对的UPD更加快速,方便,且是一种无连接传输方式,支持一对一,一对多,多对多。
Socket-UDP的通信实现起来比TCP简单一些,只是需要改变socket创建套接字的第二个参数为SOCK_DGRAM,也就是代表UDP,然后就是需要绑定两个不同的服务器,进行互相通信。
主要的流程是WSAStartup 进行初始化–> socket 创建套接字–> bind 绑定。sendto函数需要加入发送目标的ip的端口信息。
1 |
|
注意点是输入ip和端口后,需要一个getchar()。
同样可以使用netstat -an,去查看本地端口情况。
下面看看效果
功能技术
进程遍历
很常见的代码,之前APC也写过线程遍历的代码。
1 |
|
注意下进程名是宽字符就行了。
文件遍历
之前在分析《恶代》的一个lab见到过,当时也是根据伪代码去写了下。
1 |
|
按键记录
按键记录其实可以分很多种,dll注入的全局钩取,但是那种比较有缺陷,需要64位和32位的都全部钩取,不然会造成某些应用程序卡住。然后就是使用windows自带的Hook函数SetWindowsHookExA,将第一个参数设置为WH_KEYBOARD_LL,就能钩取键盘消息了,也是Lab-3-3的一个例子,感觉比书中的简单一些。然后就是书上的这种了,
SetWindowsHookExA实现,随便隐藏了下窗口,也要感谢书中提供的虚拟键值的替换表,还不错,省了很多功夫。
1 |
|
注意点就是ctitle1需要设置为全局变量,不能在函数中被重置。
桌面截屏
网上找到一段对DC的解释还比较通俗易懂的说法。
首先明白DC的含义,Windows不允许程序员直接访问硬件,它对屏幕的操作是通过环境设备,也就是DC来完成的。屏幕上的没一个窗口都对应一个DC,可以把DC想象成一个视频缓冲区,对这这个缓冲区的操作,会表现在这个缓冲区对应的屏幕窗口上。
在窗口的DC之外,可以建立自己的DC,就是说它不对应窗口,这个方法就是CreateCompatibleDC,这个DC就是一个内存缓冲区,通过这个DC你可以把和它兼容的窗口DC保存到这个DC中,就是说你可以通过它在不同的DC之间拷贝数据。例如:你先在这个DC中建立好数据,然后在拷贝到窗口的DC就是完成了这个窗口的刷新。
用通俗易懂的方式来讲讲过程。
- 简单讲上下文DC理解为一块画布,然后可以根据窗口的句柄来获取窗口的DC,也就是GetDC()这个函数,并且相当于是有画了的画布。
- 但是我们为了复制这个画布的画,我们自己也要有一个画布,所以要创建兼容性DC,也就是CreateCompatibleDC()这个函数。
- 当然,画布肯定是需要规定大小之类的,所有有CreateCompatibleBitmap()函数,创建位图,位图又叫做点阵图,是一个个很小的颜色小方块组合在一起的图片。CreateCompatibleDC()函数也说了,在对DC(画布)绘画前,它必须将正确宽度和高度的位图选择到 DC 中。
- 然后如何传递位图给DC呢,就是用到SelectObject()函数。
- 之后就可以利用BitBlt原设备的上下文(画)复制到目标设备(我们的画布)。
上面的部分就是复制的部分,下面来讲如何绘制鼠标的原理。
- 实际上差不多,
1 |
|
上面就实现了不断截图和删除截图的操作了,注意字符集使用多字节字符集。
远程cmd
我们知道通过一些函数比如说system,WinExec,CreateProcess,等是可以执行cmd命令的,但是有时候我们需要获取返回来的数据,也就执行完cmd命令,下方出现的数据,就可以利用到网络通信和管道的知识。
管道,用来进行进程间通信数据的,本质是一段内存共享。两个进程利用管道文件进行通信时,一个进程为写进程,另一个进程为读进程。写进程通过写端(发送端)往管道文件中写入信息;读进程通过读端(接收端)从管道文件中读取信息。
管道分为匿名管道和命名管道。
- 匿名管道:管道是半双工的,数据只能单向通信;需要双方通信时,需要建立起两个管道;只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。
- 命名管道:可在同一台计算机的不同进程之间或在跨越一个网络的不同计算机的不同进程之间,支持可靠的、单向或双向的数据通信。
这里是用匿名管道实现的cmd返回数据通信,也就是CreatePipe函数,如果是命名管道则是CreateNamedPipe函数。
1 |
|
上面就实现了用管道读取cmd命令ipconfig执行完成后返回的数据。当然还没有实现远程,当然实现远程也很简单,利用socket通讯,TCP或者UDP都行,接收到cmd命令的一方,直接进行管道通信,然后得到返回的数据,然后在发送出去。
代码入下,基于UDP协议,UDP通信感觉还是有点麻烦,和TCP各有优点吧。
按道理也应该分个命令发送端和命令执行端的,但是太麻烦了,我就没有分开写,但是给命令执行端分配了个发送命令执行完成后信息的端口2333,也就是命令发送端的端口。这样命令执行端如果想向命令发送端发送cmd命令是做不到的。
1 |
|
但是也有不好的地方,就是对于命令返回数据过多的的时候会崩掉,主要是设置的空间有限,UDP的单次传输大小好像也有个上限,但是对于普通的命令还是没什么问题,而且能够执行的命令有限不能启动exe文件,不然会导致命令接收端卡住。
下面是测试结果,关闭目标电脑上的notepad.exe,dos命令taskkill /f /im notepad.exe /t
自删除
之前分析一个样本见到的,利用cmd命令删除,/c del filename >> NUL。
1 |
|
利用MoveFileEx函数实现。
BOOL MoveFileExA(
[in] LPCSTR lpExistingFileName,
[in, optional] LPCSTR lpNewFileName,
[in] DWORD dwFlags
);
如果dwFlags指定MOVEFILE_DELAY_UNTIL_REBOOT并且 lpNewFileName为 NULL, 则MoveFileEx注册 lpExistingFileName文件以在系统重新启动时删除。
注意点:在此函数的 ANSI 版本中,名称仅限于MAX_PATH字符。要将此限制扩展到 32,767 个宽字符,请调用函数的 Unicode 版本并在前面加上“?” 到路径。有关详细信息,请参阅 命名文件
提示 从 Windows 10 版本 1607 开始,对于此函数的 unicode 版本 ( MoveFileExW ),您可以选择加入以删除MAX_PATH限制,而无需预先添加“\?\”。有关详细信息,请参阅命名文件、路径和命名空间的“最大路径长度限制”部分。
对于文件 I/O,路径字符串的“\?\”前缀告诉 Windows API 禁用所有字符串解析并将其后面的字符串直接发送到文件系统。
1 |
|
然后当然我去执行代码去发现报错,GetLastError的值为5,无法访问,意思就是权限不够。然后我又去读了官方文档的评论。
如果dwFlags参数指定 MOVEFILE_DELAY_UNTIL_REBOOT, 则如果无法访问注册表,则MoveFileEx将失败。该函数将重新启动时要重命名的文件的位置存储在以下注册表值中: HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ Session Manager \ PendingFileRenameOperations
此注册表值属于REG_MULTI_SZ类型。每个重命名操作都存储以下以 NULL 结尾的字符串之一,具体取决于重命名是否为删除:
szDstFile \0\0
szSrcFile \0 szDstFile \0
字符串szDstFile \0\0 表示文件 szDstFile将在重新启动时被删除。字符串 szSrcFile \0 szDstFile \0 表示 szSrcFile将在重新启动时重命名为szDstFile。
所以这个函数的本质是修改注册表相关位置,然后在重新启动时,进行删除操作。
接下来我们以管理员身份启动该程序,然后去查看注册表相关位置。
然后如果重新启动计算机,就会发现这个exe不见了。
还有种方法就是批处理了,实际上就和第一种差不多,只不过是写成了bat文件,然后CreateProcess函数隐藏窗口执行bat文件。
1 |
|