不管用什么计算机语言写代码,文件遍历都是不可或缺的操作,以便对文件进行批量处理。
对恶意代码来说文件读写就是必备功能了,比如说木马病毒窃取机密文件然后开一个隐秘端口将文件内容发送回去。
相关API
函数
这里用到了windows api提供的FindFirstFile
和FindNextFile
函数。
msdn对这两个函数的描述:
FindFirstFile
定义如下:
该函数查找指定目录的第一个文件或目录并返回它的句柄
HANDLE FindFirstFile(
LPCTSTR lpFileName,
LPWIN32_FIND_DATA lpFindFileData
);
FindNextFile
定义如下:
BOOL FindNextFileW(
HANDLE hFindFile,
LPWIN32_FIND_DATAW lpFindFileData
);
#define FindNextFile FindNextFileW
结构体
这里用到了WIN32_FIND_DATA
结构体
typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes; //文件属性
FILETIME ftCreationTime; // 文件创建时间
FILETIME ftLastAccessTime; // 文件最后一次访问时间
FILETIME ftLastWriteTime; // 文件最后一次修改时间
DWORD nFileSizeHigh; // 文件长度高32位
DWORD nFileSizeLow; // 文件长度低32位
DWORD dwReserved0; // 系统保留
DWORD dwReserved1; // 系统保留
TCHAR cFileName[ MAX_PATH ]; // 长文件名
TCHAR cAlternateFileName[ 14 ]; // 8.3格式文件名
} WIN32_FIND_DATA, *PWIN32_FIND_DATA;
操作流程
- 调用
FindFirstFile
函数,该函数接收文件路径,第二个参数指向WIN32_FIND_DATA
结构的指针。若函数成功则返回搜索句柄。该结构包含文件的名称,创建日期,属性,大小等信息。 - 调用
FindNextFile
搜索下一个文件,根据返回值判断是否搜索到文件,若没有则说明文件遍历结束 - 搜索完毕后,调用
FindClose
函数关闭搜索句柄,释放资源缓冲区资源
示例代码
#include<Windows.h>
#include<stdio.h>
void SearchFile(wchar_t *pszDirectory)
{
// 搜索指定类型文件
DWORD dwBufferSize = 2048;
wchar_t *pszFileName = NULL;
wchar_t *pTempSrc = NULL;
WIN32_FIND_DATA FileData = { 0 };
BOOL bRet = FALSE;
// 申请动态内存
pszFileName = new wchar_t[dwBufferSize];
pTempSrc = new wchar_t[dwBufferSize];
// 构造搜索文件类型字符串, *.*表示搜索所有文件类型
::wsprintf(pszFileName, L"%s\\*.*", pszDirectory);
// 搜索第一个文件
HANDLE hFile = ::FindFirstFile(pszFileName, &FileData);
if (INVALID_HANDLE_VALUE != hFile)
{
do
{
// 要过滤掉 当前目录"." 和 上一层目录"..", 否则会不断进入死循环遍历
if ('.' == FileData.cFileName[0])
{
continue;
}
// 拼接文件路径
::wsprintf(pTempSrc, L"%s\\%s", pszDirectory, FileData.cFileName);
// 判断是否是目录还是文件
if (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// 目录, 则继续往下递归遍历文件
SearchFile(pTempSrc);
}
else
{
// 文件
printf("%S\n", pTempSrc);
}
// 搜索下一个文件
} while (::FindNextFile(hFile, &FileData));
}
// 关闭文件句柄
::FindClose(hFile);
// 释放内存
delete[]pTempSrc;
pTempSrc = NULL;
delete[]pszFileName;
pszFileName = NULL;
}
int _tmain(int argc, _TCHAR* argv[])
{
SearchFile(L"D:\\projects\\python");
return 0;
}
运行结果就不展示了,就是把指定目录下所有文件名列举出来,这里并没有设计文件读写等操作,纯粹的文件夹遍历,这里字符类型我都是用wchar_t
,所以字符串值前面也加上L
,并且printf
格式化输出用大写S
。
IDA反汇编后的伪代码
main
函数的就不放出了,直接方出SearchFile
的
void __cdecl sub_411410(int a1)
{
int v1; // [esp+Ch] [ebp-38Ch]
WCHAR *v2; // [esp+14h] [ebp-384h]
WCHAR *v3; // [esp+20h] [ebp-378h]
void *v4; // [esp+2Ch] [ebp-36Ch]
void *v5; // [esp+38h] [ebp-360h]
HANDLE hFindFile; // [esp+104h] [ebp-294h]
int v7; // [esp+110h] [ebp-288h]
struct _WIN32_FIND_DATAW FindFileData; // [esp+11Ch] [ebp-27Ch]
LPWSTR v9; // [esp+374h] [ebp-24h]
LPWSTR v10; // [esp+380h] [ebp-18h]
int v11; // [esp+38Ch] [ebp-Ch]
v11 = 2048;
v10 = 0;
v9 = 0;
FindFileData.dwFileAttributes = 0;
j_memset(&FindFileData.ftCreationTime, 0, 0x24Cu);
v7 = 0;
v2 = (WCHAR *)operator new(0x1000u);
v10 = v2;
v3 = (WCHAR *)operator new(2 * v11);
v9 = v3;
wsprintfW(v10, L"%s\\*.*", a1);
hFindFile = FindFirstFileW(v10, &FindFileData);
if ( hFindFile != (HANDLE)-1 )
{
do
{
if ( FindFileData.cFileName[0] != 46 )
{
wsprintfW(v9, L"%s\\%s", a1, FindFileData.cFileName);
if ( FindFileData.dwFileAttributes & 0x10 )
sub_4110F5((int)v9);
else
printf("%S\n", v9);
}
}
while ( FindNextFileW(hFindFile, &FindFileData) );
}
FindClose(hFindFile);
v4 = v9;
operator delete(v9);
if ( v4 )
{
v9 = (LPWSTR)33059;
v1 = 33059;
}
else
{
v1 = 0;
}
v9 = 0;
v5 = v10;
operator delete(v10);
}
可以看到这里用的是去除了宏定义的wsprintfW
,FindFirstFileW
和FindNextFileW
(我用的VS2013,wsprintfW
,FindFirstFileW
和FindNextFileW
是预先宏定义好的,如下),可以验证宏定义在编译时预处理阶段就被处理了。
#define wsprintf wsprintfW
#define FindFirstFile FindFirstFileW
#define FindNextFile FindNextFileW