1.从产品角度的宏观结构及特点
1.1 宏观结构
产品组成
客户端:
负责ui显示与获取用户输入。
执行端:
1.负责对客户本地环境的信息收集与环境提取
2.负责控制客户本地环境的修改与操作
查杀端:
云端数据库(知识产权保护)。
文件大小:
1.负责病毒库与其他识别特征的存储
2.负责样本的匹配与识别
产品逻辑
静态查杀的逻辑:
文件落地—–>执行端——>提取文件和环境信息并做信息简化—–>查杀端—–>判断识别—–>如果是恶意文件—–>返回给执行端—–>给客户端—–>弹框—–>如果要查杀—–>发送给执行端执行。
动态查杀的逻辑:
运行程序——>监控——>匹配行为——>把这个进程对应的静态文件信息提取出来——>查杀端——>判断识别——>如果是恶意文件——>返回给执行端——>给客户端——>弹框——>如果要查杀——>发送给执行端执行。
1.2 特点与思考
杀毒软件作为产品需要的特点:
1.查杀速度快
为了提高查杀速度,杀软获取木马特征时可能只获取部分程序的地址信息,这时可以将恶意代码写到其他不检测的地址里。
2.占用空间少
为了减少杀软在客户的电脑内存,杀软会将病毒库放在云端数据库。
3.误杀率低
为了减少误杀率,杀软会将常用的系统api加入白名单,这时使用白名单api来调用恶意代码就可以绕过杀软。
2.从代码角度的处理细节分析
2.1 读取二进制文件内容
OVERLAPPED __Overlapped = { 0 };
void main()
{
unsigned long lpNumber=0;
char lpBuffer[]="";//文件读取的内容
char IP_path[]="test.exe";
HANDLE hFile= CreateFile(IP_path,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
if(hFile==INVALID_HANDLE_VALUE)
{
MessageBox(NULL,"创建文件句柄出错","error",MB_OK);
}
int filesucc=ReadFileEx(hFile,lpBuffer,sizeof(lpBuffer),//读取文件中多少内容
&__Overlapped, NULL);
CloseHandle(hFile);
printf("内容是:%x",lpBuffer);
if(filesucc==0)
{
MessageBox(NULL,"读取文件失败","error",MB_OK);
}
return;
}
2.2 内存扫描
2.2.1 内存内容扫描
void main() {
STARTUPINFO si;
PROCESS_INFORMATION pi;
HANDLE hProcess = NULL;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcess(L".\test.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
printf("CreateProcess2 failed (%d)n", GetLastError());
}
WaitForSingleObject(pi.hProcess, 1000);//创建了一个进程
byte* readtemp = new byte[256 * 16];//定义要存放的长度
DWORD dwNumberOfBytesRead;//每个循环读取出来存放的位置
hProcess = OpenProcess(PROCESS_ALL_ACCESS,
FALSE, pi.dwProcessId);//句柄:位置--权限
if (hProcess != NULL)
{
int i = 0x00020000;//读取位置开始的地方
while (!ReadProcessMemory(hProcess, (LPCVOID)i, readtemp, 0x10, &dwNumberOfBytesRead)) {
i++;
}
//读取的流程
/*
读到的东西做一些正则匹配
*/
printf("readsuccess:");
for (int i = 0; i < dwNumberOfBytesRead; i++) {
printf("n");
printf("\x%X", readtemp[i]);
}
printf("n");
}//显示的流程
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
读取内存内容实例:
2.2.2 内存属性扫描
VOID ScanProcessMemory(HANDLE hProc)
{
SIZE_T stSize = 0;
PBYTE pAddress = (PBYTE)0;
SYSTEM_INFO sysinfo;
MEMORY_BASIC_INFORMATION mbi = { 0 };
//MemoryBasicInformation
//获取页的大小
ZeroMemory(&sysinfo, sizeof(SYSTEM_INFO));
GetSystemInfo(&sysinfo);
// 得到的镜像基地址
pAddress = (PBYTE)sysinfo.lpMinimumApplicationAddress;
printf("------------------------------------------------------------------------ n");
printf("开始地址 t 结束地址 tt 大小 t 状态 t 内存类型 n");
printf("------------------------------------------------------------------------ n");
// 判断只要当前地址小于最大地址就循环
while (pAddress < (PBYTE)sysinfo.lpMaximumApplicationAddress)
{
ZeroMemory(&mbi, sizeof(MEMORY_BASIC_INFORMATION));
stSize = VirtualQueryEx(hProc, pAddress, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
if (stSize == 0)
{
pAddress += sysinfo.dwPageSize;
continue;
}
printf("0x%08X t 0x%08X t %8d K t ", mbi.BaseAddress,
((DWORD)mbi.BaseAddress + (DWORD)mbi.RegionSize), mbi.RegionSize >> 10);
switch (mbi.State)
{
case MEM_FREE: printf("空闲 t"); break;
case MEM_RESERVE: printf("保留 t"); break;
case MEM_COMMIT: printf("提交 t"); break;
default: printf("未知 t"); break;
}
switch (mbi.Type)
{
case MEM_PRIVATE: printf("私有 t"); break;
case MEM_MAPPED: printf("映射 t"); break;
case MEM_IMAGE: printf("镜像 t"); break;
default: printf("未知 t"); break;
}
if (mbi.Protect == 0)
printf("---");
else if (mbi.Protect & PAGE_EXECUTE)
printf("E--");
else if (mbi.Protect & PAGE_EXECUTE_READ)
printf("ER-");
else if (mbi.Protect & PAGE_EXECUTE_READWRITE)
printf("ERW");
else if (mbi.Protect & PAGE_READONLY)
printf("-R-");
else if (mbi.Protect & PAGE_READWRITE)
printf("-RW");
else if (mbi.Protect & PAGE_WRITECOPY)
printf("WCOPY");
else if (mbi.Protect & PAGE_EXECUTE_WRITECOPY)
printf("EWCOPY");
printf("n");
// 每次循环累加内存块的位置
pAddress = (PBYTE)mbi.BaseAddress + mbi.RegionSize;
}
}
int main(int argc, char* argv[])
{
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
ScanProcessMemory(hProc);
CloseHandle(hProc);
system("pause");
return 0;
}
读取内存属性实例:
2.3 注册表监控
using namespace std;
bool RegeditNotifyChanged(HKEY hKey_, char* path_)
{
// 1.创建事件
//类似于回调函数的效果
/*
回调函数:
函数A(函数B)
当触发函数A的条件的时候,执行函数B。
*/
HANDLE hNotify = CreateEvent(NULL, FALSE, TRUE, "RegeditNotifyChanged");
if (hNotify == INVALID_HANDLE_VALUE)
{
cout << "监控事件创建失败" << endl;
CloseHandle(hNotify);
return false;
}
//2. 打开注册表对应位置
HKEY hRegKey;
if (RegOpenKeyEx(hKey_, path_, 0, KEY_NOTIFY, &hRegKey) != ERROR_SUCCESS)
{
cout << "打开注册表失败" << endl;
CloseHandle(hNotify);
RegCloseKey(hRegKey);
return false;
}
//3.使用RegNotifyChangeKeyValue进行监控
if (RegNotifyChangeKeyValue(hRegKey, TRUE, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES | REG_NOTIFY_CHANGE_LAST_SET, hNotify, TRUE) != ERROR_SUCCESS)
{
cout << "监控失败" << endl;
CloseHandle(hNotify);
RegCloseKey(hRegKey);
return false;
}
if (WaitForSingleObject(hNotify, INFINITE) != WAIT_FAILED)
{
cout << "监控项发生改变" << endl;
//知道谁改的话 RegQueryValue 路径
CloseHandle(hNotify);
RegCloseKey(hRegKey);
return true;
}
CloseHandle(hNotify);
RegCloseKey(hRegKey);
return false;
}
void main(void)
{
//根键、子键名称和到子键的句柄
HKEY hRoot = HKEY_CURRENT_USER;
char* szSubKey = (char*)"Software\Microsoft\Windows\CurrentVersion\Run";
RegeditNotifyChanged(hRoot,szSubKey);
}
监控注册表实例:
原文始发于微信公众号(ZackSecurity):【杀软对抗】杀毒软件的基本结构