一、运行单一实例

通过创建系统命名互斥对象的方式来实现

1、实现原理

通过CreateMutex函数创建一个命名的互斥对象,如果对象创建成功,而且通过调用GetLastError函数获取的返回码为ERROR_ALREADY_EXISTS,则表示该命名互斥对象存在,即程序重复运行。否则,认为程序首次运行。

2、API

CreateMutex

3、实现代码如下

#include <windows.h>
#include<tchar.h>
#include<stdio.h>

BOOL IsAlreadyRun()
{
HANDLE hMutex = NULL;
hMutex = ::CreateMutex(NULL, FALSE, _T("TEST"));
if (hMutex)
{
if (ERROR_ALREADY_EXISTS == ::GetLastError())
{
return true;
}
}
return false;
}

int main()
{
if (IsAlreadyRun())
{
printf("Already RUN");
system("pause");
}
else
{
printf("NOT Already Run");
system("pause");
}
}


第二章 基础技术_#include

4、小结

这个程序实现起来并不难,关键是熟悉CreateMutex函数的调用。在调用CreateMutex函数来创建命名的互斥对象时,注意互斥对象的名称不要与现有事件、信号量或者文件映射对象等名称相同,否则创建互斥对象会失败。

在实现过程中,特别要注意,程序一定不要调用CloseHandle函数来关闭由CreateMutex函数创建出来的互斥对象的句柄,否则会导致互斥对象判断失败。因为CloseHandle函数会关闭互斥对象的句柄,释放资源。这样,系统上便不会存在对应的命名互斥对象了,通过CreateMutex创建的命名互斥对象都是不会重复的。

5、安全小贴士

使用CreateMutex函数创建的互斥对象,可以通过调用CloseHandle函数来关闭互斥对象的句柄,从属于它的所有句柄都关闭后,就会删除该对象。

在线程同步操作中, ReleaseMutex函数可以释放线程对互斥对象的控制权。

二、DLL延迟加载

使用延迟加载方式编译链接可执行文件

1、实现原理

本程序以加载第三方库——skin++库为例进行讲解演示。首先导入skin++库文件,然后编码,最后对程序编译链接生成exe可执行文件。使用PE查看器PEview.exe查看可执行文件的导入表,便可知道可执行文件必需的DLL文件了。

这样做的好处是可以把必需的DLL文件以资源形式插入到程序中,并使用DLL延迟加载技术延迟加载。在正式调用必需的DLL之前,程序都是可以正常执行的。程序可以在这段时间内,把资源中的DLL释放到本地,等到正式调用DLL的时候释放的文件就会正确地加载执行。这样当使用程序的时候,只需把exe文件发送给用户,而不需要附加DLL文件了,也不需要担心程序会丢失DLL文件。

DLL延迟加载的具体设置步骤为:

属性-->链接器-->输入-->延迟加载的DLL-->输入:SkinPPWTL.dll

2、小结

DLL延迟加载技术不需要编码来实现,只需对VS开发环境设置链接器即可完成。DLL延迟加载技术,配合资源释放技术,可以使程序变得更加方便易用。

3、安全小贴士

在PE结构中, DLL延迟加载的信息存储在ImgDelayDescr延迟导入表中,可以通过数据目录DataDirectory中的IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT项获取延迟导入表RVA相对的偏移地址和数据大小。

三、资源释放

病毒木马之所以会广泛使用资源释放技术,是因为它可以使程序变得更简洁。如果程序额外需要加载一些DLL文件、文本文件、图片文件,或者其他的音/视频文件等,则可以把它们作为资源插入到程序里,等到程序运行后,再把它们释放到本地上。这样做的好处是编译出来的程序只有一个exe文件,而不需要附带其他文件,因而程序变得很简洁。只需把exe植入到用户计算机上,而不需要连同其他文件一起植入,这降低了被发现的风险。

1、资源插入步骤

首先新建一个资源520.txt

第二章 基础技术_#include_02

再自定义添加对话框

第二章 基础技术_延迟加载_03

最后,导入该对话框

2、API

FindResource 确定模块中指定类型和名称的资源所在位置

​https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-findresourcea​

sizeofResource 获取指定资源的字节数

​https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-sizeofresource​

LoadResource 装载指定资源到全局存储器

​https://docs.microsoft.com/en-us/previous-versions/ms915421(v=msdn.10)​

LockResource 锁定资源并得到资源在内存中第一个字节的指针

​https://docs.microsoft.com/en-us/previous-versions/aa932898(v=msdn.10)​

3、实现原理

首先,通过FindResource定位程序里的资源,主要是根据"资源类型"和"资源名称"进行定位,从而获取资源信息块的句柄.

其次,根据上面获取的资源信息块的句柄,利用sizeofResource获取资源的大小之后,再通过LoadResource把资源加载程序内存中.

接着,通过LockResource锁定加载到内存中的资源,防止程序中的其他操作影响这块内存。其中,返回值就是资源在进程内存中的起始地址。

最后,根据资源大小以及进程内存的起始地址,可将资源数据读取出来并保存为本地文件。

注意:要必须明确资源所在的模块,要指明所在模块句柄并且统一。

4、实现代码如下

#include <windows.h>
#include <stdio.h>
#include "resource.h"

void FreeRes_ShowError(char* pszText)
{
char szErr[MAX_PATH] = { 0 };
wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());
}

BOOL FreeMyResource(UINT uiResourceName, char* lpszResourceType, char *lpszSaveFileName)
{
//获取指定模块里的资源
HRSRC hRsrc = ::FindResource(NULL, MAKEINTRESOURCE(uiResourceName), lpszResourceType);
if (NULL == hRsrc)
{
FreeRes_ShowError("FindResource");
return FALSE;
}
// 获取资源的大小
DWORD dwSize = ::SizeofResource(NULL, hRsrc);
if (0 >= dwSize)
{
FreeRes_ShowError("SizeofResource");
return FALSE;
}
// 将资源加载到内存里
HGLOBAL hGlobal = ::LoadResource(NULL, hRsrc);
if (NULL == hGlobal)
{
FreeRes_ShowError("LoadResource");
return FALSE;
}
// 锁定资源
LPVOID lpVoid = ::LockResource(hGlobal);
if (NULL == lpVoid)
{
FreeRes_ShowError("LockResource");
return FALSE;
}
// 保存资源为文件
FILE* fp = NULL;
fopen_s(&fp, lpszSaveFileName, "wb+");
if (NULL == fp)
{
FreeRes_ShowError("LockResource");
return FALSE;
}
fwrite(lpVoid, sizeof(char), dwSize, fp);
fclose(fp);

return TRUE;
}

int main(){
char szSaveName[MAX_PATH] = "520.txt";
// 释放资源
BOOL bRet = FreeMyResource(IDR_MYRES2, "MYRES", szSaveName);
if (FALSE == bRet)
{
MessageBox(NULL, "Free Resource Error!", "ERROR", MB_OK);
}
else
{
MessageBox(NULL, "Free Resource OK!", "OK", MB_OK);
}

return 0;

}


第二章 基础技术_句柄_04


一只小the bug