首页
社区
课程
招聘
[原创]ring3注入学习(1)导入表注入
发表于: 2021-8-18 20:47 22045

[原创]ring3注入学习(1)导入表注入

2021-8-18 20:47
22045

在编程中常常用到“导入函数”(Import functions),导入函数就是被程序调用但其执行代码又不在程序中的函数,这些函数的代码位于一个或者多个DLL中,在调用者程序中只保留一些函数信息,包括函数名及其驻留的DLL名等。

于磁盘上的PE 文件来说,它无法得知这些输入函数在内存中的地址,只有当PE 文件被装入内存后,Windows 加载器才将相关DLL 装入,并将调用输入函数的指令和函数实际所处的地址联系起来。这就是“动态链接”的概念。动态链接是通过PE 文件中定义的“导入表”来完成的,导入表中保存的正是函数名和其驻留的DLL 名等。

导入表由一系列IMAGE_IMPORT_DESCRIPTOR结构体组成:

结构的数量取决于程序要使用的DLL文件的数量,每一个结构对应一个DLL文件

该结构体的定义如下:

具体在PE文件中,就像这个样子:

在IMAGE_IMPORT_DESCRIPTOR的结构体的最后面,附带着许多小结构体,里面记录的信息是该导入表的DLL要导入使用的API

以上便是导入表的基础结构

由《加密与解密》中得知。Windows装载PE文件时,会检查导入表,将导入表所包含要使用的DLL加载进程序中,这也就产生了一个DLL注入的点,如果将恶意DLL写入导入表中,就可以让程序在运行时调用恶意DLL中的代码,达到DLL注入的效果

要实现导入表注入,需要把要注入的DLL构造的结构体塞进导入表里,但是由于导入表默认不是在PE文件的最后,所以导入表原来所在的位置不一定有足够大的空间塞入一个新的结构体,这时我们就需要把导入表移动到新的足够大的位置,有两种办法,扩大最后一个节和直接新增一个节,比较方便的方法是选择新增一个节,在新增节后将原导入表放进去,如何在最后写入自己的导入表,再新增8字节的INT和8字节IAT,至于为什么要增加8字节的INT表和8字节的IAT表,可以这样解释:

如图,当我们新写入一个IMAGE_IMPORT_DESCRIPTOR结构,我们就需要建立INT和IAT对于该结构的映射,此时就需要扩充IAT和INT表

具体实现步骤如下:

注入前

注入后

运行时

由于是静态注入,直接修改了exe文件,所以可使用校验的方式去检测有无被注入

《32位PE解析、PE修改、导入表注入》
PE基础之导入表注入
Ring3注入总结及编程实现

 
 
 
 
 
struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;
        DWORD   OriginalFirstThunk;        
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;
    DWORD   ForwarderChain;        
    DWORD   Name;//导入模块名的RVA
    DWORD   FirstThunk;             
} IMAGE_IMPORT_DESCRIPTOR;
struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;
        DWORD   OriginalFirstThunk;        
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;
    DWORD   ForwarderChain;        
    DWORD   Name;//导入模块名的RVA
    DWORD   FirstThunk;             
} IMAGE_IMPORT_DESCRIPTOR;
 
 
typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;
    BYTE    Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;
    BYTE    Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
 
 
 
 
 
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
 
 
extern "C" __declspec(dllexport) void puts()
{
    MessageBoxA(0, "hi", "hello", 0);
    return;
}
 
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        puts();
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
 
 
extern "C" __declspec(dllexport) void puts()
{
    MessageBoxA(0, "hi", "hello", 0);
    return;
}
 
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        puts();
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
#include<Windows.h>
#include<stdio.h>
#define DLLNAMELENGTH 0xE
#define FUNCTIONNAMELENGTH 0xF
#define FUNCTIONNAME "puts"
#define DLLNAME "Dll1.dll"
 
//获取DOS头
PIMAGE_DOS_HEADER GetDosHeader(_In_ char* pBase) {
    return PIMAGE_DOS_HEADER(pBase);
}
 
//获取NT头
PIMAGE_NT_HEADERS GetNtHeader(_In_ char* pBase) {
    return PIMAGE_NT_HEADERS(GetDosHeader(pBase)->e_lfanew + (SIZE_T)pBase);
}
 
//获取文件头
PIMAGE_FILE_HEADER GetFileHeader(_In_ char* pBase) {
    return &(GetNtHeader(pBase)->FileHeader);
}
 
//获取OPT头
PIMAGE_OPTIONAL_HEADER32 GetOptHeader(_In_ char* pBase) {
    return &(GetNtHeader(pBase)->OptionalHeader);
}
 
PIMAGE_SECTION_HEADER GetSecByName(_In_ char* pBase, _In_ const char* name) {
    DWORD Secnum = GetFileHeader(pBase)->NumberOfSections;
    PIMAGE_SECTION_HEADER Section = IMAGE_FIRST_SECTION(GetNtHeader(pBase));
    char buf[10] = { 0 };
    for (DWORD i = 0; i < Secnum; i++) {
        memcpy_s(buf, 8, (char*)Section[i].Name, 8);
        if (!strcmp(buf, name)) {
            return Section + i;
        }
    }
    return nullptr;
}
 
//获取最后一个区段
PIMAGE_SECTION_HEADER GetLastSec(_In_ char* PeBase) {
    DWORD SecNum = GetFileHeader(PeBase)->NumberOfSections;
    PIMAGE_SECTION_HEADER FirstSec = IMAGE_FIRST_SECTION(GetNtHeader(PeBase));
    PIMAGE_SECTION_HEADER LastSec = FirstSec + SecNum - 1;
    return LastSec;
}
 
char* OpenPeFiles(_In_ const char* Path, _Out_opt_ DWORD* nFileSize)
{
    //读文件
    HANDLE hFile = CreateFileA(Path,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        //printf("打开文件失败");
        return NULL;
    }
    DWORD PeSize = GetFileSize(hFile, NULL);
    if (nFileSize)
        *nFileSize = PeSize;
    DWORD ReadSize = 0;
    char* PeBase = new CHAR[PeSize]{ 0 };
    ReadFile(hFile, PeBase, PeSize, &ReadSize, NULL);
 
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)PeBase;
    //检测DOS头和NT头
    if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
    {
        //printf("不是PE文件\n");
        //system("pause");
        return NULL;
    }
    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(PeBase + pDosHeader->e_lfanew);
    if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
    {
        //printf("不是PE文件\n");
        //system("pause");
        return NULL;
    }
 
    CloseHandle(hFile);
    return PeBase;
}
 
//粒度对齐处理
int AlignMent(_In_ int size, _In_ int alignment) {
    return (size) % (alignment) == 0 ? (size) : ((size) / alignment + 1) * (alignment);
}
 
//新增节
char* AddSection(_In_ char*& PeBase, _In_ DWORD& PeSize, _In_ const char* Section_name, _In_ const int Section_size)
{
    GetFileHeader(PeBase)->NumberOfSections++;
    PIMAGE_SECTION_HEADER LastPeSection = GetLastSec(PeBase);
 
    memcpy(LastPeSection->Name, Section_name, 8);
    LastPeSection->Misc.VirtualSize = Section_size;
    LastPeSection->VirtualAddress = (LastPeSection - 1)->VirtualAddress + AlignMent((LastPeSection - 1)->SizeOfRawData, GetOptHeader(PeBase)->SectionAlignment);
    LastPeSection->SizeOfRawData = AlignMent(Section_size, GetOptHeader(PeBase)->FileAlignment);
    LastPeSection->PointerToRawData = AlignMent(PeSize, GetOptHeader(PeBase)->FileAlignment);
    LastPeSection->Characteristics = 0xc0000040;//节表属性设为该值,意为该节表可读可写且包含已初始化的数据
 
    GetOptHeader(PeBase)->SizeOfImage = LastPeSection->VirtualAddress + LastPeSection->SizeOfRawData;
 
    int NewSize = LastPeSection->PointerToRawData + LastPeSection->SizeOfRawData;
 
    char* NewPeBase = new char [NewSize] {0};
    //向新缓冲区录入数据
    memcpy(NewPeBase, PeBase, PeSize);
    //缓存区更替
    delete PeBase;
    PeSize = NewSize;
    return NewPeBase;
}
 
//保存文件
void SaveFile(_In_ const char* path, _In_ const char* data, _In_ int FileSize) {
    HANDLE hFile = CreateFileA(
        path,
        GENERIC_WRITE,
        FILE_SHARE_READ,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );
    DWORD Buf = 0;
    WriteFile(hFile, data, FileSize, &Buf, NULL);
    CloseHandle(hFile);
}
 
//将RVA的值转换成FOA
LPVOID RvaToFoa(LPVOID pFileBuffer, LPSTR virtualAddress) {
    LPSTR sectionAddress = NULL;//记录距离节头的距离
    LPSTR fileAddress = NULL;//记录文件中的偏移
    PIMAGE_DOS_HEADER pDosHeader = NULL;
    PIMAGE_NT_HEADERS pNTHeader = NULL;
    PIMAGE_FILE_HEADER pPEHeader = NULL;
    PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
    PIMAGE_SECTION_HEADER pSectionHeader = NULL;
 
    if (pFileBuffer == NULL) {
        printf("文件写入内存失败!\n");
        return NULL;
    }
 
    pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
    pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
    pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + 20);
    pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
 
    if ((DWORD)virtualAddress <= pOptionHeader->SizeOfHeaders) {
        return virtualAddress;
    }
 
    for (DWORD i = 1; i <= pPEHeader->NumberOfSections; i++) {
        if ((DWORD)virtualAddress < pSectionHeader->VirtualAddress) {
            pSectionHeader--;
            break;
        }
        else if (i == pPEHeader->NumberOfSections) {
            break;
        }
        else {
            pSectionHeader++;
        }
 
    }
 
    //距离该节头的距离
    sectionAddress = virtualAddress - pSectionHeader->VirtualAddress;
    fileAddress = pSectionHeader->PointerToRawData + sectionAddress;
 
    return (LPVOID)fileAddress;
}
 
//将FOA的值转换成RVA
LPVOID FoaToRva(LPVOID pFileBuffer, LPSTR fileaddress) {
    LPSTR sectionAddress = NULL;//记录距离节头的距离
    LPSTR virtualaddress = NULL;//记录内存中的偏移
    PIMAGE_DOS_HEADER pDosHeader = NULL;
    PIMAGE_NT_HEADERS pNTHeader = NULL;
    PIMAGE_FILE_HEADER pPEHeader = NULL;
    PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
    PIMAGE_SECTION_HEADER pSectionHeader = NULL;
 
    if (pFileBuffer == NULL) {
        printf("文件写入内存失败!\n");
        return NULL;
    }
 
    pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
    pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
    pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + 20);
    pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
 
    if ((DWORD)fileaddress <= pOptionHeader->SizeOfHeaders) {
        return fileaddress;
    }
 
    for (DWORD i = 1; i <= pPEHeader->NumberOfSections; i++) {
        if ((DWORD)fileaddress < pSectionHeader->PointerToRawData) {
            pSectionHeader--;
            break;
        }
        else if (i == pPEHeader->NumberOfSections) {
            break;
        }
        else {
            pSectionHeader++;
        }
 
    }
 
    //距离该节头的距离
    sectionAddress = fileaddress - pSectionHeader->PointerToRawData;
    virtualaddress = pSectionHeader->VirtualAddress + sectionAddress;
 
    return (LPVOID)virtualaddress;
}
 
char* inject_dll(_In_ char*& PeBase, _In_ DWORD& PeSize)
{
    PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;//定位表目录
    PIMAGE_IMPORT_DESCRIPTOR importTableAddress = NULL;//定位导入表的真正位置
    LPVOID returnAddress = NULL;//记录RVAtoFOA的返回值
 
    //定位到新节的位置和导入表的位置
    pDataDirectory = (PIMAGE_DATA_DIRECTORY)GetOptHeader(PeBase)->DataDirectory;
    pDataDirectory += 0x1;
 
    DWORD sectionLength = pDataDirectory->Size + 0x28 + +0x10 + DLLNAMELENGTH + FUNCTIONNAMELENGTH + 0x2;
    sectionLength = AlignMent(sectionLength, GetOptHeader(PeBase)->FileAlignment);
 
    char SecName[] = ".ddjsq";
    char* NewPeBase = AddSection(PeBase, PeSize, SecName, sectionLength);
 
    pDataDirectory = (PIMAGE_DATA_DIRECTORY)GetOptHeader(NewPeBase)->DataDirectory;
    pDataDirectory += 0x1;
 
    PDWORD pNewSection = (PDWORD)(GetLastSec(NewPeBase)->PointerToRawData + (DWORD)NewPeBase);
    returnAddress = RvaToFoa(NewPeBase, (LPSTR)pDataDirectory->VirtualAddress);
    importTableAddress = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)returnAddress + (DWORD)NewPeBase);
 
    //复制原导入表,在原导入表后新增一个导入表
    memcpy(pNewSection, importTableAddress, pDataDirectory->Size);
    importTableAddress = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pNewSection + pDataDirectory->Size - 0x14);
 
    //增加8字节INT
    PIMAGE_THUNK_DATA32 pIntTable = (PIMAGE_THUNK_DATA32)((DWORD)importTableAddress + 0x28);//保留20字节的0
    PIMAGE_THUNK_DATA32 repairIntTable = pIntTable;
    pIntTable++;
    pIntTable->u1.Ordinal = 0x0;
    pIntTable++;
 
    //增加8字节IAT表
    PIMAGE_THUNK_DATA32 pIatTable = (PIMAGE_THUNK_DATA32)(pIntTable);
    PIMAGE_THUNK_DATA32 repairIatTable = pIatTable;
    pIatTable++;
    pIatTable->u1.Ordinal = 0x0;
    pIatTable++;
 
    //分配空间存储DLL名称字符串
    PDWORD dllNameAddress = (PDWORD)pIatTable;
    memcpy(dllNameAddress, DLLNAME, DLLNAMELENGTH);
 
    //增加IMAGE_IMPORT_BY_NAME 结构
    PIMAGE_IMPORT_BY_NAME functionNameAddress = (PIMAGE_IMPORT_BY_NAME)((DWORD)dllNameAddress + DLLNAMELENGTH);
    PDWORD pFunctionName = (PDWORD)((DWORD)functionNameAddress + 0x2);
    memcpy(pFunctionName, FUNCTIONNAME, FUNCTIONNAMELENGTH);
 
    //将IMAGE_IMPORT_BY_NAME结构的RVA赋值给INT和IAT表中的第一项
    repairIntTable->u1.AddressOfData = (DWORD)FoaToRva(NewPeBase, (LPSTR)((DWORD)functionNameAddress - (DWORD)NewPeBase));
    repairIatTable->u1.AddressOfData = repairIntTable->u1.Ordinal;
 
    //修正导入表Name、OriginalFirstThunk、FirstThunk
    importTableAddress->Name = (DWORD)FoaToRva(NewPeBase, (LPSTR)((DWORD)dllNameAddress - (DWORD)NewPeBase));
    importTableAddress->OriginalFirstThunk = (DWORD)FoaToRva(NewPeBase, (LPSTR)((DWORD)repairIntTable - (DWORD)NewPeBase));
    importTableAddress->FirstThunk = (DWORD)FoaToRva(NewPeBase, (LPSTR)((DWORD)repairIatTable - (DWORD)NewPeBase));
 
    //修正IMAGE_DATA_DIRECTORY结构的VirtualAddress和Size
    pDataDirectory->VirtualAddress = (DWORD)FoaToRva(NewPeBase, (LPSTR)((DWORD)pNewSection - (DWORD)NewPeBase));
    pDataDirectory->Size += 0x14;
 
    return NewPeBase;
}
 
int main()
{
    char path[] = "路径";
    DWORD pesize;
    char* PeBase = OpenPeFiles(path, &pesize);
    if (!PeBase)
    {
        printf("wrong");
        return 0;
    }
    char* NewPeBase = inject_dll(PeBase, pesize);
    SaveFile("路径", NewPeBase, pesize);
}
#include<Windows.h>
#include<stdio.h>
#define DLLNAMELENGTH 0xE
#define FUNCTIONNAMELENGTH 0xF
#define FUNCTIONNAME "puts"
#define DLLNAME "Dll1.dll"
 
//获取DOS头
PIMAGE_DOS_HEADER GetDosHeader(_In_ char* pBase) {
    return PIMAGE_DOS_HEADER(pBase);
}
 
//获取NT头
PIMAGE_NT_HEADERS GetNtHeader(_In_ char* pBase) {
    return PIMAGE_NT_HEADERS(GetDosHeader(pBase)->e_lfanew + (SIZE_T)pBase);
}
 
//获取文件头
PIMAGE_FILE_HEADER GetFileHeader(_In_ char* pBase) {
    return &(GetNtHeader(pBase)->FileHeader);
}
 
//获取OPT头
PIMAGE_OPTIONAL_HEADER32 GetOptHeader(_In_ char* pBase) {
    return &(GetNtHeader(pBase)->OptionalHeader);
}
 
PIMAGE_SECTION_HEADER GetSecByName(_In_ char* pBase, _In_ const char* name) {
    DWORD Secnum = GetFileHeader(pBase)->NumberOfSections;
    PIMAGE_SECTION_HEADER Section = IMAGE_FIRST_SECTION(GetNtHeader(pBase));
    char buf[10] = { 0 };
    for (DWORD i = 0; i < Secnum; i++) {
        memcpy_s(buf, 8, (char*)Section[i].Name, 8);
        if (!strcmp(buf, name)) {
            return Section + i;
        }
    }
    return nullptr;
}
 
//获取最后一个区段
PIMAGE_SECTION_HEADER GetLastSec(_In_ char* PeBase) {
    DWORD SecNum = GetFileHeader(PeBase)->NumberOfSections;
    PIMAGE_SECTION_HEADER FirstSec = IMAGE_FIRST_SECTION(GetNtHeader(PeBase));
    PIMAGE_SECTION_HEADER LastSec = FirstSec + SecNum - 1;
    return LastSec;
}
 
char* OpenPeFiles(_In_ const char* Path, _Out_opt_ DWORD* nFileSize)
{
    //读文件
    HANDLE hFile = CreateFileA(Path,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        //printf("打开文件失败");
        return NULL;
    }
    DWORD PeSize = GetFileSize(hFile, NULL);
    if (nFileSize)
        *nFileSize = PeSize;
    DWORD ReadSize = 0;
    char* PeBase = new CHAR[PeSize]{ 0 };
    ReadFile(hFile, PeBase, PeSize, &ReadSize, NULL);
 
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)PeBase;
    //检测DOS头和NT头
    if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
    {
        //printf("不是PE文件\n");
        //system("pause");
        return NULL;
    }
    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(PeBase + pDosHeader->e_lfanew);
    if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
    {
        //printf("不是PE文件\n");
        //system("pause");
        return NULL;
    }
 
    CloseHandle(hFile);
    return PeBase;
}
 
//粒度对齐处理
int AlignMent(_In_ int size, _In_ int alignment) {
    return (size) % (alignment) == 0 ? (size) : ((size) / alignment + 1) * (alignment);
}
 
//新增节
char* AddSection(_In_ char*& PeBase, _In_ DWORD& PeSize, _In_ const char* Section_name, _In_ const int Section_size)
{
    GetFileHeader(PeBase)->NumberOfSections++;
    PIMAGE_SECTION_HEADER LastPeSection = GetLastSec(PeBase);
 
    memcpy(LastPeSection->Name, Section_name, 8);
    LastPeSection->Misc.VirtualSize = Section_size;
    LastPeSection->VirtualAddress = (LastPeSection - 1)->VirtualAddress + AlignMent((LastPeSection - 1)->SizeOfRawData, GetOptHeader(PeBase)->SectionAlignment);
    LastPeSection->SizeOfRawData = AlignMent(Section_size, GetOptHeader(PeBase)->FileAlignment);
    LastPeSection->PointerToRawData = AlignMent(PeSize, GetOptHeader(PeBase)->FileAlignment);
    LastPeSection->Characteristics = 0xc0000040;//节表属性设为该值,意为该节表可读可写且包含已初始化的数据
 
    GetOptHeader(PeBase)->SizeOfImage = LastPeSection->VirtualAddress + LastPeSection->SizeOfRawData;
 
    int NewSize = LastPeSection->PointerToRawData + LastPeSection->SizeOfRawData;
 
    char* NewPeBase = new char [NewSize] {0};
    //向新缓冲区录入数据
    memcpy(NewPeBase, PeBase, PeSize);
    //缓存区更替
    delete PeBase;
    PeSize = NewSize;
    return NewPeBase;
}
 
//保存文件
void SaveFile(_In_ const char* path, _In_ const char* data, _In_ int FileSize) {

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2021-8-18 21:27 被Ddjsq_2333编辑 ,原因: 添加参考链接
收藏
免费 6
支持
分享
最新回复 (5)
雪    币: 248
活跃值: (3789)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
还可以把代码存放在区块里面,然后注入到exe,就不用dll了
2021-8-19 16:06
0
雪    币: 3561
活跃值: (541)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3

good

最后于 2021-8-19 17:35 被朱年吉祥编辑 ,原因:
2021-8-19 17:34
0
雪    币: 1058
活跃值: (1187)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
4
yy虫子yy 还可以把代码存放在区块里面,然后注入到exe,就不用dll了
确实,不过把它们独立开也可以,这样做其他注入时就不用再跑到这个项目把代码复制过去
2021-8-20 13:14
0
雪    币: 560
活跃值: (359)
能力值: ( LV13,RANK:1370 )
在线值:
发帖
回帖
粉丝
5
非常不错,再放个64位的。
2021-12-2 12:14
0
雪    币: 185
活跃值: (1265)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
有点牛
2023-5-12 10:13
0
游客
登录 | 注册 方可回帖
返回
//