2023年腾讯游戏安全pc端初赛wp

2023年腾讯游戏安全pc端初赛wp

练练手

所使用到的工具

1、x64dbg
2、IDA
3、ScyllaHide
4、frida
5、vmpdump   https://github.com/0xnobody/vmpdump
6、Procmon/火绒剑
7、drstrace	https://github.com/DynamoRIO/drmemory
8、010editor
9、ce修改器

寻踪觅迹

die看一下,发现程序被加了vmp

image-20241203153333453

运行一下程序,可以发现程序会一直向当前目录的 contest.txt 文件内写入 ImVkImx9JG12OGtlImV+

用火绒剑或者procmon看一下进程,可以可以发现程序每各一秒就会轮流的向\#\.4%34n484contest.txt写入数据

image-20241203153634637

下面我们就考虑先dump出源程序

抽丝剥茧

笔者第一次脱vmp壳,在网上搜索了一些资料,发现市面上对于3.x的vmp壳的脱法基本大同小异。

我参考了这两篇文章:

https://www.52pojie.cn/thread-1764028-1-1.html

https://0x666.club/vmp3-unpack-trick/

由于程序具有反调试,我们需要用scyllaHide 插件反反调试。

我们需要先执行到 程序.text段再dump,即必须先让vmp恢复完毕所有的源程序代码。

我的实现步骤如下:

首先x64dbg载入 contest.exe程序,我们对 GetSystemTimeAsFileTimeQueryPerformanceCounter 这两个api下断点

之后F9运行,在断在GetSystemTimeAsFileTime的时候回溯返回地址,找到第一个返回地址在 .txt段的位置:

我们尝试运行,第一次 GetSystemTimeAsFileTime被断下来后,回溯返回地址,可发现地址在 vmp段内,那我们只能继续F9知道断在 GetSystemTimeAsFileTime后返回地址在 .txt断

image-20241203155353328 image-20241203155538853

在第二次断到 GetSystemTimeAsFileTime 的时候,可以发现返回地址在 .text段

image-20241203155756163

image-20241203155833944

随后再次F8运行到 QueryPerformanceCounter 后开始单步运行,直到运行到这里:

image-20241203160010435

之后我们就可以找一下入口点的特征,事实上,经过我的多次尝试,0x0007FF62CF8945C 就是程序真正的入口点。

我们我们需要dump下来程序并恢复IAT表,这里常规的dump + iat修复法是不起作用的。经过我的浏览器检索,我找到了一个开源项目:

vmpdump: https://github.com/0xnobody/vmpdump

我们直接 执行:

.\VMPDump.exe 22164 "" ep=0x0007FF62CF8945C -disable-reloc

image-20241203160855016

即可dump出程序并修复好IAT表

之后我们再用 010editor改一下程序的入口点即可正常运行程序。(可能还得固定一下基址)

image-20241203160601208

下面我们的任务就是分析源程序了

层层深入

用IDA打开,最好给IDA也配一个ScyllaHide插件

之后搜索字符串,可以定位到一个可疑字符串:

image-20241203160926765

交叉引用可以找到一个魔改的 base64编码算法,这个函数没有被vmp混淆,所以还是能很容易看出来的

image-20241203160951825

之后我们进入main函数,可以发现main函数被vmp混淆的非常严重

image-20241203161108991

接下来我们找一下函数分发器

image-20241203161235909

尽可能的去人工恢复一下符号(我这里是已经修复过的)

image-20241203161203990

接下来开始我们漫长的调试工作,我们对call下断点,一点点分析

经过分析,我们可以发现,程序首先遍历进程对 idax32dbgx64dbgida64x86 64-SSE4-AVX2.exewiresharkprocesshackernetstat.exenetmontcpview.exefilemon.exereqmon.execain.exe等函数进行检查

之后去明文进行逐字节异或:

image-20241203162149173

然后进行魔改base64编码,随后调用某个函数,进行了文件读写操作,最后关闭文件句柄,Sleep 1秒

程序的大体流程已经很清楚了,接下来就是要写代码进行 hook。

为了方便hook,我用 drstrace工具 trace了一下系统调用

image-20241203162910152

实际上 0x39786d496b566d49 转化为字符串就是 "ImVkImx9"字符串,那我们可以直接 hook NtWriteFile函数来实现写明文。

image-20241203163047066

同理,也可以通过hook NtCreateFile(ZwCreateFile)的方式来实现任意文件写明文。

但通过调试,我发现,我对 ntdll中的 NtCreateFileNtWriteFile 下断点,并不能断下来,但我的火绒剑、Procmon、drstrace工具上明明显示了这些系统调用的执行。

莫非是程序自实现了 syscall? 我尝试全局搜索 syscall,也并未找到对应汇编。

柳暗花明

事实上,程序先alloc了一块内存,再把ntdll的相关函数copy了进去并直接执行这个堆中的代码,而不是 ntdll中对应的函数代码

直接对VirtualProtectEx下断点,经过调试我发现,在main函数执行之前,程序会分配一个 0x100000 大小的内存,之后调用

image-20241203164249596

VirtualProtectEx函数,给其一个可读可写可执行的权限

image-20241203164319612

那么我们用ida打开 ntdll.dll,并在对应函数(ZwWriteFile / NtCreateFile)上搜索特征码,之后用ce修改器定位一下,可以发现确实能定位函数到这个堆块中。之后下内存执行断点,能成功断下来!

落叶归根

那么我的hook思路就是:

程序运行之后,写脚本,先搜索全局内存,在所有具有rwx属性段的内存段中搜索特征码,定位NtCreateFileZwWriteFile函数地址,之后hook 函数参数即可。

简单起见,这里写了个frida脚本

frida_hook.py:

import frida
import sys
import time
import subprocess

Jscode = None
def get_js_code():
    global Jscode
    fp = open("./frida_hook.js","r",encoding="utf8")
    Jscode = fp.read()
    fp.close()


filepath = "tlsn.txt"
def exe_searchcode(pid):
    exe_path = "./search_code.exe"

    process = subprocess.Popen([exe_path,str(pid)])
    process.wait()


def rewrite_filepath():
    global filepath
    fp = open("./data.log","r")
    data = fp.read()
    fp.close()
    fp = open("./data.log","w")
    fp.write(data + "filepath: \n" + filepath + "\n")
    fp.close()



# Attach 方式
local = frida.get_local_device()
# session = local.attach("contest.VMPDump.exe")       # contest.VMPDump.exe
session = local.attach("contest.exe")       # contest.exe


pid = session._impl.pid
exe_searchcode(pid)
rewrite_filepath()


get_js_code()
script = session.create_script(Jscode)
script.load()
sys.stdin.read()  

frida_hook.js:


var data = [ 0x26, 0x24, 0x31, 0x26, 0x2D, 0x28, 0x20, 0x2C, 0x23, 0x3C, 0x2A, 0x30, 0x26, 0x24, 0x2B]; 
var out_buf_addr = 0
var out_length_addr = 0
var ans =0
var xbase64 = ""

function scan_xbase64(){
    // var moduleName = "contest.exe";
    var moduleName = "contest.VMPDump.exe";
    
    var module = Process.getModuleByName(moduleName)
    var searchPattern = "41 56 56 57 53 89 D0 41 BA AB AA AA AA"
    Memory.scan(module.base,module.size,searchPattern,{
        onMatch: function(address,size){

            xbase64 = address
            console.log("Found xbase64 addr: " + address.toString(16));

        },
        onComplete:function(){
            console.log("scan xbase64 finish");
            
            hook_xbase64()
        },
        onError: function(){
            console.log("scan error");
        }
    
    });

}

function readLines(filePath) {
    var alldata = File.readAllText(filePath);
    var lines = alldata.split('\n');  // 按行拆分文件内容
    return lines
}

function hook_WriteFile(){
    var path = "./data.log"
    var lines  = readLines(path);
    var WriteFileBuffAddr = lines[5].trim();
    console.log("ZwWriteFile is: " + WriteFileBuffAddr)

    Interceptor.attach(ptr(WriteFileBuffAddr), {
        onEnter: function(args) {
            console.log("Enter writefile hook")
            var rsp = this.context.rsp
            var outstraddr = rsp.add(0x30).readPointer()
            
            var length = data.length
            for(var i=0;i<data.length;i++){                
                outstraddr.add(i).writeU8(data[i]);
            }
            rsp.add(0x38).writeU64(length);
            console.log("leave writefile hook")
            
        }
    });
}




var newFilePath = ""            // 为了防止alloc的内存被释放,把变量名写道全局变量上也行
function hook_CreateFile(){
    var path = "./data.log"
    var lines  = readLines(path);
    var hook_file_path = lines[7].trim();
    console.log("hook new file path is:",hook_file_path)
    var CreateFileBuffAddr = lines[3].trim();

    newFilePath = Memory.allocUtf16String(hook_file_path)
    var length = hook_file_path.length * 2
    var UNICODE_STRING_file_path_struct = Memory.alloc(0x100)
    UNICODE_STRING_file_path_struct.writeU16(length)
    UNICODE_STRING_file_path_struct.add(2).writeU16(length)
    UNICODE_STRING_file_path_struct.add(8).writePointer(newFilePath)
    Interceptor.attach(ptr(CreateFileBuffAddr), {
        onEnter: function(args) {
            var struct_addr = args[2];
            // 我们需要提前检测一下,写入的是 #\.4%34n484 还是其他的什么
            var old_unicode_struct_addr = struct_addr.add(0x10).readPointer()
            var old_path_addr = old_unicode_struct_addr.add(8).readPointer()
            var old_path = old_path_addr.readUtf16String();
            
            if (old_path == "contest.txt"){
                struct_addr.add(0x10).writePointer(UNICODE_STRING_file_path_struct)
                console.log("finish hook_CreateFile");
            }else{
                console.log("no hook_CreateFile");
            }

        }
    });
}


hook_WriteFile()
hook_CreateFile()







// __kernel_entry NTSTATUS NtCreateFile(
//     [out]          PHANDLE            FileHandle,
//     [in]           ACCESS_MASK        DesiredAccess,
//     [in]           POBJECT_ATTRIBUTES ObjectAttributes,
//     [out]          PIO_STATUS_BLOCK   IoStatusBlock,
//     [in, optional] PLARGE_INTEGER     AllocationSize,
//     [in]           ULONG              FileAttributes,
//     [in]           ULONG              ShareAccess,
//     [in]           ULONG              CreateDisposition,
//     [in]           ULONG              CreateOptions,
//     [in]           PVOID              EaBuffer,
//     [in]           ULONG              EaLength
//   );

// typedef struct _UNICODE_STRING {
//     USHORT Length;
//     USHORT MaximumLength;
//     PWSTR Buffer;
// } UNICODE_STRING;
// 3、hook CreateFile

其中 search_code.exe是我编写的特征码搜索程序

源码如下:

#include <windows.h>
#include <stdio.h>


void* optimized_search(void* mem, size_t mem_size, const char* pattern, size_t pattern_size) {
    if (pattern_size > mem_size) {
        return NULL;
    }

    for (size_t i = 0; i <= mem_size - pattern_size; ++i) {
        if (memcmp((char*)mem + i, pattern, pattern_size) == 0) {
            return (char*)mem + i;
        }
    }

    return NULL;
}



int main(int argc, char* argv[]) {
    if (argc < 2) {
        puts("error argv");
        return 0;
    }
    

    FILE* fp = fopen("./data.log", "w");
    DWORD pid = atoi(argv[1]); // 目标进程ID
    fprintf(fp, "pid: \n%d\n", pid);
    

    
    HANDLE process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid);
    if (process == NULL) {
        printf("OpenProcess failed. Error: %lu\n", GetLastError());
        return 1;
    }


    char pattern1[] = { 0x4C, 0x8B, 0xD1, 0xB8, 0x55, 0x00, 0x00, 0x00 };           // CreateFile
    char pattern2[] = { 0x4C, 0x8B, 0xD1, 0xB8, 0x08, 0x00, 0x00, 0x00 };           // WriteFile

    size_t pattern_size1 = sizeof(pattern1);
    size_t pattern_size2 = sizeof(pattern2);


    SIZE_T startAddr = 0x0;
    SIZE_T endAddr = 0x30000000000;
    SIZE_T chunk_size = 0x100000; // 每次读取1MB
    void* mem = malloc(chunk_size);
    if (mem == NULL) {
        printf("Memory allocation failed.\n");
        CloseHandle(process);
        return 1;
    }

    SIZE_T addr = startAddr;
    SIZE_T NtCreateFileAddr = 0;
    SIZE_T ZwWriteFileAddr = 0;
    while (addr < endAddr) {
        MEMORY_BASIC_INFORMATION mbi;
        if (VirtualQueryEx(process, (LPCVOID)addr, &mbi, sizeof(mbi)) == 0) {
            printf("VirtualQueryEx failed at address 0x%llx. Error: %lu\n", addr, GetLastError());
            addr += chunk_size;
            continue;
        }


        //printf("baseaddress: 0x%x \t size: 0x%x \t protect: 0x%x\n", mbi.BaseAddress, mbi.RegionSize, mbi.Protect);
        //if (mbi.BaseAddress == 0x2070000) {
        //    printf("here\n");
        //}
        if (mbi.State == MEM_COMMIT && (mbi.Protect == PAGE_EXECUTE_READWRITE )) {
            SIZE_T region_size = mbi.RegionSize < chunk_size ? mbi.RegionSize : chunk_size;
            SIZE_T bytes_read;
            if (ReadProcessMemory(process, mbi.BaseAddress, mem, region_size, &bytes_read)) {
                void* result = optimized_search(mem, bytes_read, pattern1, pattern_size1);
                if (result != NULL) {
                    printf("NtCreateFile address: \n0x%llx\n", (SIZE_T)mbi.BaseAddress + (char*)result - (char*)mem);
                    NtCreateFileAddr = (SIZE_T)mbi.BaseAddress + (char*)result - (char*)mem;
                    //fprintf(fp, "NtCreateFile address: \n0x%llx\n", (SIZE_T)mbi.BaseAddress + (char*)result - (char*)mem);
                    
                    //break;
                }

                void* result2 = optimized_search(mem, bytes_read, pattern2, pattern_size2);
                if (result2 != NULL) {
                    printf("ZwWriteFile address: \n0x%llx\n", (SIZE_T)mbi.BaseAddress + (char*)result2 - (char*)mem);
                    ZwWriteFileAddr = (SIZE_T)mbi.BaseAddress + (char*)result2 - (char*)mem;
                    //fprintf(fp, "ZwWriteFile address: \n0x%llx\n", (SIZE_T)mbi.BaseAddress + (char*)result2 - (char*)mem);

                    //break;
                }

            }
            else {
                printf("ReadProcessMemory failed at address 0x%llx. Error: %lu\n", (SIZE_T)mbi.BaseAddress, GetLastError());
            }
        }

        addr += mbi.RegionSize;
    }


    fprintf(fp, "NtCreateFile address: \n0x%llx\n", NtCreateFileAddr);
    fprintf(fp, "ZwWriteFile address: \n0x%llx\n", ZwWriteFileAddr);


    fclose(fp);
    free(mem);
    CloseHandle(process);
    return 0;
}

通过修改 firda_hook.py中的 filepath 变量,可以实现任意文件写明文操作。

最后答案

找到明文的信息

明文应该为 catchmeifyoucan 或者 &$1&-( ,#<*0&$+ 是由 catchmeifyoucan 异或得来的

写入密文信息变为写入明文、让contest.exe 往入自行指定的不同的文件里写入明文信息成功

简单起见,这里用的frida脚本 (用frida脚本的后果就是要修改大量内存,hhh,优点就是方便)

我这套可以实现目录下的任意文件名写明文。

frida_hook.py:

import frida
import sys
import time
import subprocess

Jscode = None
def get_js_code():
    global Jscode
    fp = open("./frida_hook.js","r",encoding="utf8")
    Jscode = fp.read()
    fp.close()


filepath = "tlsn.txt"
def exe_searchcode(pid):
    exe_path = "./search_code.exe"

    process = subprocess.Popen([exe_path,str(pid)])
    process.wait()


def rewrite_filepath():
    global filepath
    fp = open("./data.log","r")
    data = fp.read()
    fp.close()
    fp = open("./data.log","w")
    fp.write(data + "filepath: \n" + filepath + "\n")
    fp.close()



# Attach 方式
local = frida.get_local_device()
# session = local.attach("contest.VMPDump.exe")       # contest.VMPDump.exe
session = local.attach("contest.exe")       # contest.exe


pid = session._impl.pid
exe_searchcode(pid)
rewrite_filepath()


get_js_code()
script = session.create_script(Jscode)
script.load()
sys.stdin.read()  

frida_hook.js:

var data = [ 0x26, 0x24, 0x31, 0x26, 0x2D, 0x28, 0x20, 0x2C, 0x23, 0x3C, 0x2A, 0x30, 0x26, 0x24, 0x2B]; 
var out_buf_addr = 0
var out_length_addr = 0
var ans =0
var xbase64 = ""

function scan_xbase64(){
    // var moduleName = "contest.exe";
    var moduleName = "contest.VMPDump.exe";
    
    var module = Process.getModuleByName(moduleName)
    var searchPattern = "41 56 56 57 53 89 D0 41 BA AB AA AA AA"
    Memory.scan(module.base,module.size,searchPattern,{
        onMatch: function(address,size){

            xbase64 = address
            console.log("Found xbase64 addr: " + address.toString(16));

        },
        onComplete:function(){
            console.log("scan xbase64 finish");
            
            hook_xbase64()
        },
        onError: function(){
            console.log("scan error");
        }
    
    });

}

function readLines(filePath) {
    var alldata = File.readAllText(filePath);
    var lines = alldata.split('\n');  // 按行拆分文件内容
    return lines
}

function hook_WriteFile(){
    var path = "./data.log"
    var lines  = readLines(path);
    var WriteFileBuffAddr = lines[5].trim();
    console.log("ZwWriteFile is: " + WriteFileBuffAddr)

    Interceptor.attach(ptr(WriteFileBuffAddr), {
        onEnter: function(args) {
            console.log("Enter writefile hook")
            var rsp = this.context.rsp
            var outstraddr = rsp.add(0x30).readPointer()
            
            var length = data.length
            for(var i=0;i<data.length;i++){                
                outstraddr.add(i).writeU8(data[i]);
            }
            rsp.add(0x38).writeU64(length);
            console.log("leave writefile hook")
            
        }
    });
}




var newFilePath = ""            // 为了防止alloc的内存被释放,把变量名写道全局变量上也行
function hook_CreateFile(){
    var path = "./data.log"
    var lines  = readLines(path);
    var hook_file_path = lines[7].trim();
    console.log("hook new file path is:",hook_file_path)
    var CreateFileBuffAddr = lines[3].trim();

    newFilePath = Memory.allocUtf16String(hook_file_path)
    var length = hook_file_path.length * 2
    var UNICODE_STRING_file_path_struct = Memory.alloc(0x100)
    UNICODE_STRING_file_path_struct.writeU16(length)
    UNICODE_STRING_file_path_struct.add(2).writeU16(length)
    UNICODE_STRING_file_path_struct.add(8).writePointer(newFilePath)
    Interceptor.attach(ptr(CreateFileBuffAddr), {
        onEnter: function(args) {
            var struct_addr = args[2];
            // 我们需要提前检测一下,写入的是 #\.4%34n484 还是其他的什么
            var old_unicode_struct_addr = struct_addr.add(0x10).readPointer()
            var old_path_addr = old_unicode_struct_addr.add(8).readPointer()
            var old_path = old_path_addr.readUtf16String();
            
            if (old_path == "contest.txt"){
                struct_addr.add(0x10).writePointer(UNICODE_STRING_file_path_struct)
                console.log("finish hook_CreateFile");
            }else{
                console.log("no hook_CreateFile");
            }

        }
    });
}


hook_WriteFile()
hook_CreateFile()







// __kernel_entry NTSTATUS NtCreateFile(
//     [out]          PHANDLE            FileHandle,
//     [in]           ACCESS_MASK        DesiredAccess,
//     [in]           POBJECT_ATTRIBUTES ObjectAttributes,
//     [out]          PIO_STATUS_BLOCK   IoStatusBlock,
//     [in, optional] PLARGE_INTEGER     AllocationSize,
//     [in]           ULONG              FileAttributes,
//     [in]           ULONG              ShareAccess,
//     [in]           ULONG              CreateDisposition,
//     [in]           ULONG              CreateOptions,
//     [in]           PVOID              EaBuffer,
//     [in]           ULONG              EaLength
//   );

// typedef struct _UNICODE_STRING {
//     USHORT Length;
//     USHORT MaximumLength;
//     PWSTR Buffer;
// } UNICODE_STRING;
// 3、hook CreateFile

其中 search_code.exe是我编写的特征码搜索程序

源码如下:

#include <windows.h>
#include <stdio.h>


void* optimized_search(void* mem, size_t mem_size, const char* pattern, size_t pattern_size) {
    if (pattern_size > mem_size) {
        return NULL;
    }

    for (size_t i = 0; i <= mem_size - pattern_size; ++i) {
        if (memcmp((char*)mem + i, pattern, pattern_size) == 0) {
            return (char*)mem + i;
        }
    }

    return NULL;
}



int main(int argc, char* argv[]) {
    if (argc < 2) {
        puts("error argv");
        return 0;
    }
    

    FILE* fp = fopen("./data.log", "w");
    DWORD pid = atoi(argv[1]); // 目标进程ID
    fprintf(fp, "pid: \n%d\n", pid);
    

    
    HANDLE process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid);
    if (process == NULL) {
        printf("OpenProcess failed. Error: %lu\n", GetLastError());
        return 1;
    }


    char pattern1[] = { 0x4C, 0x8B, 0xD1, 0xB8, 0x55, 0x00, 0x00, 0x00 };           // CreateFile
    char pattern2[] = { 0x4C, 0x8B, 0xD1, 0xB8, 0x08, 0x00, 0x00, 0x00 };           // WriteFile

    size_t pattern_size1 = sizeof(pattern1);
    size_t pattern_size2 = sizeof(pattern2);


    SIZE_T startAddr = 0x0;
    SIZE_T endAddr = 0x30000000000;
    SIZE_T chunk_size = 0x100000; // 每次读取1MB
    void* mem = malloc(chunk_size);
    if (mem == NULL) {
        printf("Memory allocation failed.\n");
        CloseHandle(process);
        return 1;
    }

    SIZE_T addr = startAddr;
    SIZE_T NtCreateFileAddr = 0;
    SIZE_T ZwWriteFileAddr = 0;
    while (addr < endAddr) {
        MEMORY_BASIC_INFORMATION mbi;
        if (VirtualQueryEx(process, (LPCVOID)addr, &mbi, sizeof(mbi)) == 0) {
            printf("VirtualQueryEx failed at address 0x%llx. Error: %lu\n", addr, GetLastError());
            addr += chunk_size;
            continue;
        }


        //printf("baseaddress: 0x%x \t size: 0x%x \t protect: 0x%x\n", mbi.BaseAddress, mbi.RegionSize, mbi.Protect);
        //if (mbi.BaseAddress == 0x2070000) {
        //    printf("here\n");
        //}
        if (mbi.State == MEM_COMMIT && (mbi.Protect == PAGE_EXECUTE_READWRITE )) {
            SIZE_T region_size = mbi.RegionSize < chunk_size ? mbi.RegionSize : chunk_size;
            SIZE_T bytes_read;
            if (ReadProcessMemory(process, mbi.BaseAddress, mem, region_size, &bytes_read)) {
                void* result = optimized_search(mem, bytes_read, pattern1, pattern_size1);
                if (result != NULL) {
                    printf("NtCreateFile address: \n0x%llx\n", (SIZE_T)mbi.BaseAddress + (char*)result - (char*)mem);
                    NtCreateFileAddr = (SIZE_T)mbi.BaseAddress + (char*)result - (char*)mem;
                    //fprintf(fp, "NtCreateFile address: \n0x%llx\n", (SIZE_T)mbi.BaseAddress + (char*)result - (char*)mem);
                    
                    //break;
                }

                void* result2 = optimized_search(mem, bytes_read, pattern2, pattern_size2);
                if (result2 != NULL) {
                    printf("ZwWriteFile address: \n0x%llx\n", (SIZE_T)mbi.BaseAddress + (char*)result2 - (char*)mem);
                    ZwWriteFileAddr = (SIZE_T)mbi.BaseAddress + (char*)result2 - (char*)mem;
                    //fprintf(fp, "ZwWriteFile address: \n0x%llx\n", (SIZE_T)mbi.BaseAddress + (char*)result2 - (char*)mem);

                    //break;
                }

            }
            else {
                printf("ReadProcessMemory failed at address 0x%llx. Error: %lu\n", (SIZE_T)mbi.BaseAddress, GetLastError());
            }
        }

        addr += mbi.RegionSize;
    }


    fprintf(fp, "NtCreateFile address: \n0x%llx\n", NtCreateFileAddr);
    fprintf(fp, "ZwWriteFile address: \n0x%llx\n", ZwWriteFileAddr);


    fclose(fp);
    free(mem);
    CloseHandle(process);
    return 0;
}

posted @ 2024-12-03 17:45  TLSN  阅读(4)  评论(0编辑  收藏  举报