Cobalt Strike BOF实现的原理

admin 2025年3月31日19:44:02评论6 views字数 15598阅读51分59秒阅读模式
Cobalt Strike BOF实现的原理
Cobalt Strike BOF实现的原理
Cobalt Strike BOF实现的原理
蛇来运转,鸿运新年
Cobalt Strike BOF实现的原理
序言


       Beacon在接收执行obj前,Cobalt Strike会先对这个obj文件进行一些处理,比如解析obj文件中一些需要的段.text,.data,在处理一些表比如IMAGE_RELOCATION,IMAGE_SYMBOL等等,然后在经过一系列的处理后,会把需要的部分按照一定格式打包起来随后在发送给Beacon,这时Beacon接收到的是Cobalt Strike已经解析处理过的obj文件数据,并非是原本的obj文件,所以Beacon主要做的是必须是在进程内才能确定并完成的事情比如处理重定位,填充函数指针等等,最后去执行go入口点

Cobalt Strike BOF实现的原理

COFF解读



     微软官方pe coff文档:https://learn.microsoft.com/zh-cn/windows/win32/debug/pe-format

COFF 文件基本结构

COFF 文件主要由以下几部分组成:

  1. 文件头 (File Header)

  2. 可选头 (Optional Header) - 可能不存在

  3. 节头表 (Section Headers)

  4. 节数据 (Section Data)

  5. 符号表 (Symbol Table)

  6. 字符串表 (String Table)

文件头 (File Header)

typedef struct {    uint16_t Machine;              // 目标机器类型    uint16_t NumberOfSections;     // 节的数量    uint32_t TimeDateStamp;        // 文件创建时间戳    uint32_t PointerToSymbolTable; // 符号表偏移量    uint32_t NumberOfSymbols;      // 符号表条目数    uint16_t SizeOfOptionalHeader; // 可选头大小    uint16_t Characteristics;      // 文件属性标志} COFF_FileHeader;
Cobalt Strike BOF实现的原理

可选头 (Optional Header)

typedef struct {    uint16_t Magic;                 // 标识类型    uint8_t  MajorLinkerVersion;    // 链接器主版本    uint8_t  MinorLinkerVersion;    // 链接器次版本    uint32_t SizeOfCode;            // 代码段大小    uint32_t SizeOfInitializedData; // 初始化数据大小    uint32_t SizeOfUninitializedData; // 未初始化数据大小    uint32_t AddressOfEntryPoint;   // 入口点地址    uint32_t BaseOfCode;            // 代码段基址    uint32_t BaseOfData;            // 数据段基址    // PE特有字段会继续扩展...} COFF_OptionalHeader;

节头表 (Section Headers)

typedef struct {    char     Name[8];             // 节名称(8字节或以0填充)    uint32_t VirtualSize;         // 节在内存中的大小    uint32_t VirtualAddress;      // 节在内存中的虚拟地址    uint32_t SizeOfRawData;       // 节在文件中的大小    uint32_t PointerToRawData;    // 节数据在文件中的偏移    uint32_t PointerToRelocations;// 重定位信息偏移    uint32_t PointerToLinenumbers;// 行号信息偏移    uint16_t NumberOfRelocations; // 重定位条目数    uint16_t NumberOfLinenumbers; // 行号条目数    uint32_t Characteristics;     // 节属性标志} COFF_SectionHeader;

Cobalt Strike BOF实现的原理


符号表 (Symbol Table)

typedef struct {    union {        char ShortName[8];        // 短名称(8字节)        struct {            uint32_t Zeroes;      // 0表示使用长名称            uint32_t Offset;     // 字符串表中的偏移        } LongName;    } Name;    uint32_t Value;              // 符号值(地址或其他)    int16_t  SectionNumber;      // 节号(正数表示节索引)    uint16_t Type;               // 符号类型    uint8_t  StorageClass;       // 存储类别    uint8_t  NumberOfAuxSymbols; // 附加符号数} COFF_Symbol;

Cobalt Strike BOF实现的原理

重定位信息 (Relocations)

typedef struct {    uint32_t VirtualAddress;     // 需要重定位的位置地址    uint32_t SymbolTableIndex;   // 符号表索引    uint16_t Type;               // 重定位类型} COFF_Relocation;

Cobalt Strike BOF实现的原理

COFF 文件解析流程

  1. 读取文件头,获取基本信息

  2. 如果有可选头,读取可选头

  3. 读取节头表,了解各节信息

  4. 根据节头表中的信息读取各节数据

  5. 读取符号表和字符串表(如果有)

  6. 处理重定位信息(如果有)

常见节类型

  • .text: 代码节

  • .data: 已初始化数据

  • .bss: 未初始化数据

  • .rdata: 只读数据

  • .reloc: 重定位信息

  • .debug: 调试信息


编写解析器

#include <stdio.h>#include <Windows.h>void parse_coff() {    HANDLE hFile = CreateFile("C:\Users\liuhua\Desktop\Demon.obj", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);    if (hFile == INVALID_HANDLE_VALUE)    {        printf("CreateFile error.n");        return;    }    int file_size = 0;    file_size = GetFileSize(hFile, NULL);    char* data;    data = (char*)malloc(file_size);    DWORD dwRead;    if (!ReadFile(hFile, data, file_size, &dwRead, NULL))    {        printf("ReadFile error.n");        return;    }    //COFF文件头    PIMAGE_FILE_HEADER PECOFF_FileHeader = (PIMAGE_FILE_HEADER)data;    printf("COFF File Header:n");    printf("Machine: %x n", PECOFF_FileHeader->Machine);    printf("NumberOfSections %d n", PECOFF_FileHeader->NumberOfSections);    printf("TimeDateStamp %d n", PECOFF_FileHeader->TimeDateStamp);    printf("PointerToSymbolTable %d n", PECOFF_FileHeader->PointerToSymbolTable);    printf("NumberOfSymbols %d n", PECOFF_FileHeader->NumberOfSymbols);    printf("SizeOfOptionalHeader %d n", PECOFF_FileHeader->SizeOfOptionalHeader);    printf("Characteristics %x n", PECOFF_FileHeader->Characteristics);    // 跳过可选头(如果有)    if (PECOFF_FileHeader->SizeOfOptionalHeader > 0) {            }    // 计算节表起始位置    char* section_table = data + sizeof(IMAGE_FILE_HEADER) + PECOFF_FileHeader->SizeOfOptionalHeader;    // 读取节表    printf("nSection Headers:n");    PIMAGE_SECTION_HEADER section_headers = (PIMAGE_SECTION_HEADER)section_table;    for (int i = 0; i < PECOFF_FileHeader->NumberOfSections; i++) {        printf("  Section %d: %-8sn", i + 1, section_headers[i].Name);        printf("    Virtual Size: 0x%08Xn", section_headers[i].Misc.VirtualSize);        printf("    Virtual Address: 0x%08Xn", section_headers[i].VirtualAddress);        printf("    Raw Data Size: 0x%08Xn", section_headers[i].SizeOfRawData);        printf("    Raw Data Pointer: 0x%08Xn", section_headers[i].PointerToRawData);        printf("    Characteristics: 0x%08Xn", section_headers[i].Characteristics);    }    // 解析符号表(如果有)    if (PECOFF_FileHeader->PointerToSymbolTable != 0 && PECOFF_FileHeader->NumberOfSymbols > 0) {        printf("nSymbol Table:n");        PIMAGE_SYMBOL symbol_table = (PIMAGE_SYMBOL)(data + PECOFF_FileHeader->PointerToSymbolTable);        // 字符串表紧跟在符号表后面        DWORD string_table_size = *(DWORD*)(symbol_table + PECOFF_FileHeader->NumberOfSymbols);        char* string_table = (char*)(symbol_table + PECOFF_FileHeader->NumberOfSymbols);        for (int i = 0; i < PECOFF_FileHeader->NumberOfSymbols; i++) {            char name_buffer[9] = { 0 };            const char* name = NULL;            // 检查是短名称还是长名称            if (symbol_table[i].N.Name.Short != 0) {                // 短名称(最多8个字符)                memcpy(name_buffer, symbol_table[i].N.ShortName, 8);                name_buffer[8] = '';                name = name_buffer;            }            else {                // 长名称(在字符串表中)                DWORD offset = symbol_table[i].N.Name.Long;                if (offset < string_table_size) {                    name = string_table + offset;                }                else {                    name = "(invalid string table offset)";                }            }            printf("  Symbol %d: %sn", i + 1, name);            printf("    Value: 0x%08Xn", symbol_table[i].Value);            printf("    Section: %dn", symbol_table[i].SectionNumber);            printf("    Type: 0x%04Xn", symbol_table[i].Type);            printf("    Storage Class: 0x%02Xn", symbol_table[i].StorageClass);            // 跳过辅助符号            i += symbol_table[i].NumberOfAuxSymbols;        }    }    free(data);}int main(int argc, char* argv[]) {    parse_coff();    return 0;}


Cobalt Strike BOF实现的原理

武器化Demon代码



       参考wbglil的思路:

  1. 加载读取obj文件

  2. 解析IMAGE_FILE_HEADER头

  3. 解析IMAGE_SECTION_HEADER节表

  4. 解析IMAGE_SYMBOL符号表

  5. 处理数据重定位和填充函数指针

  6. 根据符号表寻找go函数的地址然后call过去


修改重定位表

修复重定位表(Relocation Table)的目的是 在运行时动态修正代码/数据中的内存地址引用,确保程序能正确执行。

.obj 文件本身没有标准入口点

  • 目标文件(.obj) 是编译器生成的中间文件,尚未经过链接器处理。

  • 它可能包含多个函数(如 maingofunc1 等),但没有默认的 EntryPoint 字段(不像 PE 文件的 AddressOfEntryPoint)。

  • 必须通过符号表(Symbol Table)手动查找入口函数(如 main 或 go


demo.c

以下代码使用 cl /c /GS- /Fo Demon.obj go_main.c编译为obj(注:请使用cl并且编译为x64位

#include <stdio.h>#include <windows.h>DECLSPEC_IMPORT void   vPrintf(char * fmt, ...);DECLSPEC_IMPORT DWORD WINAPI User32$MessageBoxA(HWND,LPCTSTR,LPCTSTR,UINT);void go() {      vPrintf("Hello World");      User32$MessageBoxA(NULL,"hello",NULL,NULL);}//这里的vPrintf函数其实就相当于Beacon里的Beaconxxxx函数//至于其他的win api使用方式我还是按照CS的来采用DllName$FunName 这种格式

loader.c

以下代码使用 cl.exe /GS-  loader.c  /Fe loader.exe 或者Vs直接贴入运行

//仅支持cl编译的x64 obj#include<Windows.h>#include<stdio.h>void vPrintf(char* fmt) {	printf(fmt);}int main(){	HANDLE hFile = CreateFile("C:\Users\***\Desktop\Demon.obj", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);	if (hFile == INVALID_HANDLE_VALUE)	{		printf("CreateFile error.n");		return 0;	}	int file_size = 0;	file_size = GetFileSize(hFile, NULL);	char* buff;	buff = (char*)malloc(file_size);	DWORD dwRead;	if (!ReadFile(hFile, buff, file_size, &dwRead, NULL))	{		printf("ReadFile error.n");		return 0;	}	//COFF文件头	PIMAGE_FILE_HEADER PECOFF_FileHeader = (PIMAGE_FILE_HEADER)buff;	printf("Machine: %x n", PECOFF_FileHeader->Machine);	printf("NumberOfSections %d n", PECOFF_FileHeader->NumberOfSections);	printf("TimeDateStamp %d n", PECOFF_FileHeader->TimeDateStamp);	printf("PointerToSymbolTable %d n", PECOFF_FileHeader->PointerToSymbolTable);	printf("NumberOfSymbols %d n", PECOFF_FileHeader->NumberOfSymbols);	printf("SizeOfOptionalHeader %d n", PECOFF_FileHeader->SizeOfOptionalHeader);	printf("Characteristics %x n", PECOFF_FileHeader->Characteristics);	//SizeOfOptionalHeader no	//COFF节表处理	PIMAGE_SECTION_HEADER* PECOFF_SectionHeader_arr = (PIMAGE_SECTION_HEADER*)malloc(PECOFF_FileHeader->NumberOfSections * sizeof(PIMAGE_SECTION_HEADER));	memset(PECOFF_SectionHeader_arr, 0, PECOFF_FileHeader->NumberOfSections * sizeof(PIMAGE_SECTION_HEADER));	PIMAGE_SECTION_HEADER PECOFF_SectionHeader = (PIMAGE_SECTION_HEADER)(buff + sizeof(IMAGE_FILE_HEADER));	for (size_t i = 0; i <= PECOFF_FileHeader->NumberOfSections - 1; i++)	{		PECOFF_SectionHeader_arr[i] = PECOFF_SectionHeader;		printf("段名称 %s n", PECOFF_SectionHeader->Name);		printf("段大小 %d n", PECOFF_SectionHeader->SizeOfRawData);		PECOFF_SectionHeader++;	}	//重定位表	int Relocation_len = 0;	for (int i = 0; i <= PECOFF_FileHeader->NumberOfSections - 1; i++)	{		Relocation_len += PECOFF_SectionHeader_arr[i]->NumberOfRelocations;	}	int x = 0;	PIMAGE_RELOCATION* PECOFF_Relocation_arr = (PIMAGE_RELOCATION*)malloc(Relocation_len * sizeof(PIMAGE_RELOCATION));	memset(PECOFF_Relocation_arr, 0, Relocation_len * sizeof(PIMAGE_RELOCATION));	for (int i = 0; i <= PECOFF_FileHeader->NumberOfSections - 1; i++)	{		if (PECOFF_SectionHeader_arr[i]->NumberOfRelocations)		{			PIMAGE_RELOCATION PECOFF_Relocation = (PIMAGE_RELOCATION)(buff + PECOFF_SectionHeader_arr[i]->PointerToRelocations);			for (int y = 0; y < PECOFF_SectionHeader_arr[i]->NumberOfRelocations; y++)			{				PECOFF_Relocation_arr[x] = PECOFF_Relocation;				PECOFF_Relocation++;				x++;			}		}	}	//打印输出	//符号表	PIMAGE_SYMBOL PECOFF_SYMBOL = (PIMAGE_SYMBOL)(buff + PECOFF_FileHeader->PointerToSymbolTable);	PIMAGE_SYMBOL* PECOFF_SYMBOL_arr = (PIMAGE_SYMBOL*)malloc(PECOFF_FileHeader->NumberOfSymbols * sizeof(PIMAGE_SYMBOL));	memset(PECOFF_SYMBOL_arr, 0, PECOFF_FileHeader->NumberOfSymbols * sizeof(PIMAGE_SYMBOL));	for (int i = 0; i <= PECOFF_FileHeader->NumberOfSymbols - 1; i++)	{		PECOFF_SYMBOL_arr[i] = PECOFF_SYMBOL;		PECOFF_SYMBOL++;	}	//无需处理NumberOfAuxSymbols	//处理重定位和函数指针	char* Fun_ptr = buff + PECOFF_SectionHeader_arr[0]->PointerToRawData;	for (int i = 0; i <= PECOFF_FileHeader->NumberOfSections - 1; i++)	{		if (PECOFF_SectionHeader_arr[i]->NumberOfRelocations)		{			PIMAGE_RELOCATION PECOFF_Relocation = (PIMAGE_RELOCATION)(buff + PECOFF_SectionHeader_arr[i]->PointerToRelocations);			for (int y = 0; y < PECOFF_SectionHeader_arr[i]->NumberOfRelocations; y++)			{				int sys_index = PECOFF_Relocation->SymbolTableIndex;				if (PECOFF_SYMBOL_arr[sys_index]->StorageClass == 3)				{					char* patch_data = buff + (PECOFF_Relocation->VirtualAddress + PECOFF_SectionHeader_arr[i]->PointerToRawData);					*(DWORD*)patch_data = ((DWORD64)(buff + ((PECOFF_SYMBOL_arr[sys_index]->Value) + (PECOFF_SectionHeader_arr[PECOFF_SYMBOL_arr[sys_index]->SectionNumber - 1]->PointerToRawData))) - (DWORD64)(patch_data + 4));				}				else				{					if (!(PECOFF_SYMBOL_arr[sys_index]->N.Name.Short))					{						char* pstr = (buff + PECOFF_FileHeader->PointerToSymbolTable) + (PECOFF_FileHeader->NumberOfSymbols * sizeof(IMAGE_SYMBOL));						pstr += (DWORD)(PECOFF_SYMBOL_arr[sys_index]->N.Name.Long);						if (!strcmp(pstr, "__imp_vPrintf"))						{							char* patch_data = buff + (PECOFF_Relocation->VirtualAddress + PECOFF_SectionHeader_arr[i]->PointerToRawData);							*(DWORD64*)Fun_ptr = (DWORD64)vPrintf;							*(DWORD*)patch_data = ((DWORD64)Fun_ptr - (DWORD64)(patch_data + 4));							DWORD64* ptr = (DWORD64*)Fun_ptr;							ptr++;							Fun_ptr = (char*)ptr;						}						else						{							pstr += 6;							char* dllname;							char* funname;							dllname = strtok(pstr, "$");							funname = strtok(NULL, "$");							DWORD64 fun_add = (DWORD64)GetProcAddress(LoadLibraryA(dllname), funname);							char* patch_data = buff + (PECOFF_Relocation->VirtualAddress + PECOFF_SectionHeader_arr[i]->PointerToRawData);							*(DWORD64*)Fun_ptr = (DWORD64)fun_add;							*(DWORD*)patch_data = ((DWORD64)Fun_ptr - (DWORD64)(patch_data + 4));							DWORD64* ptr = (DWORD64*)Fun_ptr;							ptr++;							Fun_ptr = (char*)ptr;						}					}				}				PECOFF_Relocation++;			}		}	}	//寻找go函数作为入口点	DWORD oep;	for (int i = 0; i < PECOFF_FileHeader->NumberOfSymbols - 1; i++)	{		if (!strncmp((char*)(PECOFF_SYMBOL_arr[i]->N.ShortName), "go", 2))		{			oep = PECOFF_SYMBOL_arr[i]->Value;		}	}	char* jmp;	for (int i = 0; i < PECOFF_FileHeader->NumberOfSections - 1; i++)	{		if (!strncmp((char*)PECOFF_SectionHeader_arr[i]->Name, ".text", 5))		{			jmp = (buff + PECOFF_SectionHeader_arr[i]->PointerToRawData + oep);		}	}	printf("0x%016I64x n", jmp);	DWORD Protect;	if (VirtualProtect(buff, file_size, PAGE_EXECUTE_READWRITE, &Protect) != 0)	{		((void(*)(void))jmp)();	};	//printf("%x",GetLastError());	return 0;}

      这里是参考的wbglil的代码,我自己写的代码在修复重定位表时有点问题,也希望大佬能斧正斧正我的代码,如下

#include <stdio.h>#include <Windows.h>void parse_coff() {    HANDLE hFile = CreateFile("C:\Users\***\Desktop\Demon.obj", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);    if (hFile == INVALID_HANDLE_VALUE) {        printf("CreateFile error.n");        return;    }    int file_size = GetFileSize(hFile, NULL);    char* data = (char*)malloc(file_size);    DWORD dwRead;    if (!ReadFile(hFile, data, file_size, &dwRead, NULL)) {        printf("ReadFile error.n");        return;    }    // COFF 文件头    PIMAGE_FILE_HEADER PECOFF_FileHeader = (PIMAGE_FILE_HEADER)data;    printf("COFF File Header:n");    printf("Machine: %x n", PECOFF_FileHeader->Machine);    printf("NumberOfSections %d n", PECOFF_FileHeader->NumberOfSections);    printf("TimeDateStamp %d n", PECOFF_FileHeader->TimeDateStamp);    printf("PointerToSymbolTable %d n", PECOFF_FileHeader->PointerToSymbolTable);    printf("NumberOfSymbols %d n", PECOFF_FileHeader->NumberOfSymbols);    printf("SizeOfOptionalHeader %d n", PECOFF_FileHeader->SizeOfOptionalHeader);    printf("Characteristics %x n", PECOFF_FileHeader->Characteristics);    // 跳过可选头(如果有)    if (PECOFF_FileHeader->SizeOfOptionalHeader > 0) {        // 可选头处理代码    }    // 计算节表起始位置    char* section_table = data + sizeof(IMAGE_FILE_HEADER) + PECOFF_FileHeader->SizeOfOptionalHeader;    // 读取节表    printf("nSection Headers:n");    PIMAGE_SECTION_HEADER section_headers = (PIMAGE_SECTION_HEADER)section_table;    for (int i = 0; i < PECOFF_FileHeader->NumberOfSections; i++) {        printf("  Section %d: %-8sn", i + 1, section_headers[i].Name);        printf("    Virtual Size: 0x%08Xn", section_headers[i].Misc.VirtualSize);        printf("    Virtual Address: 0x%08Xn", section_headers[i].VirtualAddress);        printf("    Raw Data Size: 0x%08Xn", section_headers[i].SizeOfRawData);        printf("    Raw Data Pointer: 0x%08Xn", section_headers[i].PointerToRawData);        printf("    Characteristics: 0x%08Xn", section_headers[i].Characteristics);        // 读取重定位表        if (section_headers[i].PointerToRelocations != 0) {            PIMAGE_RELOCATION relocation_table = (PIMAGE_RELOCATION)(data + section_headers[i].PointerToRelocations);            printf("    Relocation Table:n");            for (int j = 0; j < section_headers[i].NumberOfRelocations; j++) {                printf("      Relocation: Type: 0x%04X, Offset: 0x%08Xn",                    relocation_table[j].Type, relocation_table[j].VirtualAddress);                // 处理重定位:根据类型修改地址                // 通常是符号的地址需要调整                // 你可以在这里处理重定位表的类型和具体的调整逻辑            }        }    }    // 解析符号表(如果有)    if (PECOFF_FileHeader->PointerToSymbolTable != 0 && PECOFF_FileHeader->NumberOfSymbols > 0) {        printf("nSymbol Table:n");        PIMAGE_SYMBOL symbol_table = (PIMAGE_SYMBOL)(data + PECOFF_FileHeader->PointerToSymbolTable);        // 字符串表紧跟在符号表后面        DWORD string_table_size = *(DWORD*)(symbol_table + PECOFF_FileHeader->NumberOfSymbols);        char* string_table = (char*)(symbol_table + PECOFF_FileHeader->NumberOfSymbols);        //寻找go函数作为入口点        DWORD oep = 0;        for (int i = 0; i < PECOFF_FileHeader->NumberOfSymbols; i++) {            char name_buffer[9] = { 0 };            const char* name = NULL;            // 检查是短名称还是长名称            if (symbol_table[i].N.Name.Short != 0) {                // 短名称(最多8个字符)                memcpy(name_buffer, symbol_table[i].N.ShortName, 8);                name_buffer[8] = '';                name = name_buffer;            }            else {                // 长名称(在字符串表中)                DWORD offset = symbol_table[i].N.Name.Long;                if (offset < string_table_size) {                    name = string_table + offset;                }                else {                    name = "(invalid string table offset)";                }            }            printf("  Symbol %d: %sn", i + 1, name);            printf("    Value: 0x%08Xn", symbol_table[i].Value);            printf("    Section: %dn", symbol_table[i].SectionNumber);            printf("    Type: 0x%04Xn", symbol_table[i].Type);            printf("    Storage Class: 0x%02Xn", symbol_table[i].StorageClass);            if (strcmp(name, "go") == 0) {                printf("发现_GO函数n");                oep = symbol_table[i].Value;            }            // 跳过辅助符号            i += symbol_table[i].NumberOfAuxSymbols;        }        if (oep != 0) {            printf("Go 入口点: 0x%08Xn", oep);        }        else {            printf("未找到 Go 函数n");        }    }    free(data);}int main(int argc, char* argv[]) {    parse_coff();    return 0;}


Cobalt Strike BOF实现的原理

运行效果



       Cobalt Strike BOF实现的原理

       最后进行一些总结这次的文章,COFF像极了PE格式,一个是链接后的,一个是链接前,而PE是有entiry入口点,而COFF是无的,它的所有函数都放在符号表中,较长的函数名还会存在字符串表。

        而加载呢,却和PEload很像,修复解析格式,重定位表,最后一步两者就有区别了,PE是指定入口,而我们的COFF就要指定函数名(通过遍历符号表寻找)这也就是为啥CS的BOF是指定GO函数为编写函数了,因为官方解析时候只找GO函数的符号

END

参考文章:

https://wbglil.gitbook.io/cobalt-strike/cobalt-strike-yuan-li-jie-shao/untitled-3#obj-mu-biao-wen-jian

原文始发于微信公众号(T3Ysec):Cobalt Strike BOF实现的原理

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年3月31日19:44:02
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Cobalt Strike BOF实现的原理https://cn-sec.com/archives/3901938.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息