首页
社区
课程
招聘
[原创]RWhackA远程线程注入式病毒分析(H&NCTF2024)
2024-6-6 00:51 2903

[原创]RWhackA远程线程注入式病毒分析(H&NCTF2024)

2024-6-6 00:51
2903

wp拜读:[原创]H&NCTF RE 部分题解-CTF对抗-看雪-安全社区|安全招聘|kanxue.com

涉及到的知识点:
IDA动态调试
IDAPython脚本
用快照对进程、模块、线程进行遍历(代码段)
WindowsAPI
病毒感染实现(PE infector)
DLL文件分析


发现保护器:保护器: Enigma Virtual Box
工具下载:【更新】Enigma Virtual Box(单文件封装工具) v10.50 - 『精品软件区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
该保护器介绍:Enigma Virtual Box用法 exe封包工具介绍-CSDN博客
这个保护器主要的功能就是把一堆文件合成为一个EXE!

脱壳工具:mos9527/evbunpack: Enigma Virtual Box Unpacker / 解包、脱壳工具 --- mos9527/evbunpack: Enigma Virtual Box Unpacker / 解包、脱壳工具 (github.com)
脱壳失败:

分析main函数逻辑

开始手动静态分析:

静态分析发现有很多內存访问异常点,均出自函数调用上,可能是程序重定位时产生的错误,没办法只能动态调试了。

动调验证MEMORY爆红原因

我想知道为什么会动态调试才出现真实的地址:
找到存放地址的变量去下一个断点:

下个读写断点:

再去看是被哪个函数写入的值!

成功找到!

毫无疑问这个正确的地址是由保护器Enigma Virtual Box动态写入的!!!


发现存在一个非官方库的dll文件,猜这其实是题目要给的附件只是被打包进了exe!

动调DLL文件的Local_Hide函数

继续动态调试,成功加载出myDLL1.dll文件,直接用ida插件dump出来:

v3 = (kernel32_LoadLibraryW)(L"myDLL1.dll", argv, envp);// 成功加载出myDLL1.dll文件,直接用ida插件dump出来

成功:

剩下的就是动态调试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
int LocalHide()
{
  char Src[304]; // [rsp+20h] [rbp-E0h] BYREF
  char Command[304]; // [rsp+150h] [rbp+50h] BYREF
  char FileName[304]; // [rsp+280h] [rbp+180h] BYREF
  CHAR Filename[304]; // [rsp+3B0h] [rbp+2B0h] BYREF
  CHAR Buffer[256]; // [rsp+4E0h] [rbp+3E0h] BYREF
  char v6[10240]; // [rsp+5E0h] [rbp+4E0h] BYREF
  DWORD pcbBuffer; // [rsp+2DF0h] [rbp+2CF0h] BYREF
  FILE *Stream; // [rsp+2DF8h] [rbp+2CF8h] BYREF
 
  memset(v6, 0, sizeof(v6));
  memset(Command, 0, 0x12Cui64);
  memset(Filename, 0, 0x12Cui64);
  memset(Src, 0, 0x12Cui64);
  memset(FileName, 0, 0x12Cui64);
  memset(Buffer, 0, sizeof(Buffer));
  pcbBuffer = 256;
  GetUserNameA(Buffer, &pcbBuffer);             // 获取当前用户名
  stdio_common_vsprintf_s(v6, "C:\\Users\\%s\\Videos", Buffer);
  stdio_common_vsprintf_s_0(Src, "%s\\svchsst.exe");// 拼接出文件路径
  GetModuleFileNameA(0i64, Filename, 0x104u);
  stdio_common_vsprintf_s_0(FileName, "%s\\9434d49b-56e1-34d4-9434-0245943434d4.txt");
  Stream = fopen(FileName, "r");
  if ( !Stream )                                // 在主程序中由于文件不存在导致打开失败
  {
    fopen(FileName, "r");                       // 打开文件会失败,C:\Users\Brinmon\Videos\9434d49b-56e1-34d4-9434-0245943434d4.txt
    ModifyAndRenameFile(Src, "txt", 0);         // 将src的路径后缀改名
    CopyFileContent(Filename, Src);             // 将主exe复制一份名为svchsst.txt的文本文件
    ModifyAndRenameFile(Src, "exe", 1);         // 目录出现exe,修改文件明
    stdio_common_vsprintf_s_0(Command, "start %s");// 拼接出命令start svchsst.exe
    system(Command);                            // 运行完之后会出现一个svchsst.txt
    Stream = 0i64;
    fopen_s(&Stream, FileName, "w");
    fwrite(Filename, 0x12Cui64, 1ui64, Stream); // 写入主文件路径到txt文本:C:\Users\Brinmon\Desktop\隐藏的眼睛\RwHackA - 副本.exe
    fclose(Stream);
    puts("退出\n");
    exit(0);
  }
  printf("身在此山中\n");
  memset(Src, 0, 0x12Cui64);
  fgets(Src, 300, Stream);                      // 读取文本信息,得到主程序的位置C:\Users\Brinmon\Desktop\隐藏的眼睛\RwHackA - 副本.exe
  fclose(Stream);
  stdio_common_vsprintf_s_0(Command, "del %s"); // del C:\Users\Brinmon\Desktop\隐藏的眼睛\RwHackA - 副本.exe
  system(Command);
  stdio_common_vsprintf_s_0(Command, "del %s"); // del C:\Users\Brinmon\Videos\9434d49b-56e1-34d4-9434-0245943434d4.txt
  return system(Command);
}

这段代码的逻辑:
文件有三个:原程序、svchsst.exe、9434d49b-56e1-34d4-9434-0245943434d4.txt

  • 原程序打开txt文件失败,复制其本身到视频文件夹下的svchsst.exe副本中,并启动它,完成后将自身路径写入txt文件中并结束自身运行
  • 副本启动后检测到了txt文件,删除掉了txt文件和原文件,并继续后面的执行过程

继续分析main函数,发现虚拟机检测(CPU数量检查)

继续分析main函数:

这里会检测程序是否运行再虚拟机上!

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
void __noreturn sub_140001070()
{
  __int64 len; // rdx
  char v1[304]; // [rsp+20h] [rbp-498h] BYREF
  char vscCode[304]; // [rsp+150h] [rbp-368h] BYREF
  char v3[304]; // [rsp+280h] [rbp-238h] BYREF
  char v4[264]; // [rsp+3B0h] [rbp-108h] BYREF
  int v5; // [rsp+4C0h] [rbp+8h] BYREF
  __int64 v6; // [rsp+4C8h] [rbp+10h] BYREF
 
  memeset();
  v5 = 256;
  (advapi32_GetUserNameA)(v4, &v5);
  memeset();
  memeset();
  (kernel32_GetModuleFileNameA)(0i64, v1, 260i64);
  stdio_common_vsprintf_s(v3, "C:\\Users\\%s\\Pictures\\WindowsRun.bat", v4);
  v6 = 0i64;
  (ucrtbase_fopen_s)(&v6, v3, "w");
  memeset();
  stdio_common_vsprintf_s(
    vscCode,
    "if \"%%1\"==\"hide\" goto CmdBegin\n"
    "start mshta vbscript:createobject(\"wscript.shell\").run(\"\"\"%%~0\"\" hide\",0)(window.close)&&exit\n"
    ":CmdBegin\n"
    "del %s",
    v1);
  len = -1i64;
  do
    ++len;
  while ( vscCode[len] );
  (ucrtbase_fwrite)(vscCode, len, 1i64, v6);
  (ucrtbase_fclose)(v6);
  stdio_common_vsprintf_s(v1, "start %s", v3);
  (ucrtbase_system)(v1);
  ucrtbase_exit(0i64);
}

这段代码的主要逻辑就是运行vbs代码删除文件!
发现这段vbs的作用就是用来删除文件和隐藏窗口的!

if "%1"=="hide" goto CmdBegin
start mshta vbscript:createobject("wscript.shell").run("""%~0"" hide",0)(window.close)&&exit
:CmdBegin
del C:\Users\Brinmon\Desktop\隐藏的眼睛\RwHackA - 副本.exe

批处理文件首先检查是否有参数传递。如果没有参数,它使用 mshta 命令重新运行自己,并传递 hide 参数,同时隐藏窗口。然后在重新运行时,由于有了 hide 参数,实际的批处理文件逻辑开始执行。

继续分析又发现虚拟机检测(进程快照检查)

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
//使用 CreateToolhelp32Snapshot() 函数获取当前进程的进程快照
  Toolhelp32Snapshot = kernel32_CreateToolhelp32Snapshot(2i64, 0i64);
  //如果获取快照失败,打印错误码
  if ( Toolhelp32Snapshot == -1 )
  {
    v7 = (kernel32_GetLastError)();
    printf("CreateToolhelp32Snapshot:%d\n", v7);
  }
  // 初始化 PROCESSENTRY32W 结构体
  v51[0] = 568;
  //使用 Process32FirstW() 函数遍历进程快照
  if ( (kernel32_Process32FirstW)(Toolhelp32Snapshot, v51) )
  {
  //定义一些字符串,用于检查进程名是否包含这些字符串
    v40 = "Vmtoolsd.exe";
    v41 = "Vmwaretrat.exe";
    v42 = "Vmwareuser.exe";
    v43 = "Vmacthlp.exe";
    v44 = "vboxservice.exe";
    v45 = "vboxtray.exe";
    do
    {
      memeset();
      //将进程名从宽字符转换为多字节字符串
      (kernel32_WideCharToMultiByte)(0i64, 0i64, v52, 0xFFFFFFFFi64, v50, 260, 0i64, 0i64, v40, v41, v42, v43, v44, v45);
      //检查进程名是否包含虚拟机相关的字符串
      for ( i = 0i64; i < 6; ++i )
      {
        v10 = (&v40)[i];
        v11 = (v50 - v10);
        do
        {
          v12 = v11[v10];
          v13 = *v10 - v12;
          if ( v13 )
            break;
          ++v10;
        }
        while ( v12 );
        if ( !v13 )
        {
          printf("为虚拟机exe\n");
          sub_140001070();
        }
      }
    }
    //继续遍历下一个进程
    while ( (kernel32_Process32NextW)(Toolhelp32Snapshot, v51) );
  }
  else
  {
  //如果 Process32FirstW() 函数失败,打印错误码
    v8 = (kernel32_GetLastError)();
    printf("Process32First:%d\n", v8);
  }
  printf("为实体机\n");

这段代码通过遍历进程查找电脑中是否存在vm必备进程!来检查是否是虚拟机!

1
2
3
4
5
6
v40 = "Vmtoolsd.exe";
v41 = "Vmwaretrat.exe";
v42 = "Vmwareuser.exe";
v43 = "Vmacthlp.exe";
v44 = "vboxservice.exe";
v45 = "vboxtray.exe";

遍历PCB进程块查找特定模块

直接手动绕过前面的虚拟机检查就来到了下面代码的位置!

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
// 获取当前进程的环境块(PEB)
Blink = NtCurrentPeb()->Ldr->InLoadOrderModuleList.Blink;
// 初始化链表遍历指针v16为链表头部
v16 = Blink;
// 遍历模块链表
do
{
    // 移动到下一个模块
    v16 = v16->Flink;
 
    // 检查当前模块是否有模块名称
    if (v16[3].Flink)
    {
        // 提取模块名称并存储在v46数组中
        Flink = v16[6].Flink;
        for (j = 0i64; *Flink; v46[j++] = v19)
        {
            // 最多只处理63个字符
            if (j >= 0x3F)
                break;
            v19 = *Flink;//字节存储在v19中
            Flink += 2;//移动两个字节
        }
        v46[j] = 0; // 添加字符串结束符
 
        // 将模块名称转换为小写
        v20 = (user32_CharLowerA)(v46);
 
        // 计算模块名称哈希值
        v21 = 53;
        v22 = -1i64;
        v14 = v20;
        do
            ++v22;
        while (*(v20 + v22));
        v23 = 0;
        if (v22)
        {
            do
            {
                v24 = *v14;
                ++v23;
                ++v14;
                v21 = v24 + 3 * v21;
            }
            while (v23 < v22);
            // 检查哈希值是否匹配目标值0x037C0B5E
            if (v21 == 0x037C0B5E)
                break;
        }
    }
}
// 循环直到遍历完整个模块链表
while (Blink != v16);

遍历程序的所有模块来寻找特点hash值的模块,我们直接下个断点再来看看它找的是哪个模块!

再去看地址发现找到了kernel32

解决了这段代码就来学习一下这个结构体(结构体来至chatgpt)

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
typedef struct _LDR_DATA_TABLE_ENTRY {
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    union {
        LIST_ENTRY HashLinks;
        struct {
            PVOID SectionPointer;
            ULONG CheckSum;
        };
    };
    union {
        struct {
            ULONG TimeDateStamp;
        };
        struct {
            PVOID LoadedImports;
        };
    };
    PVOID EntryPointActivationContext;
    PVOID PatchInformation;
    LIST_ENTRY ForwarderLinks;
    LIST_ENTRY ServiceTagList;
    LIST_ENTRY StaticLinks;
    PVOID ContextInformation;
    ULONG_PTR OriginalBase;
    LARGE_INTEGER LoadTime;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

检查系统中是否存在某些安全软件或者虚拟机相关的进程

继续向下分析

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
78
79
80
81
82
83
84
85
86
87
// 获取kernel32.dll中GetModuleHandleA函数的地址
qword_1400050C8 = (sub_140001230)(v25, 4038080516i64, v14);
 
// 获取kernel32.dll中GetProcAddress函数的地址
qword_1400050C0 = (sub_140001230)(v25, 448915681i64, v26);
 
// 获取kernel32.dll的模块句柄
qword_1400050B8 = qword_1400050C8("kernel32.dll");
 
// 获取ADVAPI32.dll的模块句柄
qword_1400050D0 = qword_1400050C8("ADVAPI32.dll");
 
// 获取ntdll.dll的模块句柄
v27 = qword_1400050C8("ntdll.dll");
 
// 设置 VerSetConditionMask 的参数
LOBYTE(v28) = 3;
v29 = v27;
v30 = (ntdll_VerSetConditionMask)(0i64, 2i64, v28);
LOBYTE(v31) = 3;
v32 = (ntdll_VerSetConditionMask)(v30, 1i64, v31);
LOBYTE(v33) = 3;
(ntdll_VerSetConditionMask)(v32, 4i64, v33);
 
// 尝试获取 ntdll.dll 中 RtlGetVersion 函数的地址
if (v29)
{
    v34 = kernel32_GetProcAddress(v29, "RtlGetVersion");
    if (v34)
    {
        // 调用 RtlGetVersion 函数获取系统版本信息
        memeset();
        v48[0] = 284;
        if (!v34(v48))
        {
            // 根据系统版本信息打印相应的信息
            if (v48[1] == 6 && v48[2] == 1)
            {
                printf("Windows 7\n");
            }
            else
            {
                printf("其他版本\n");
                // 获取进程快照
                v35 = kernel32_CreateToolhelp32Snapshot(2i64, 0i64);
                if (v35 == -1)
                {
                    v36 = (kernel32_GetLastError)();
                    printf("CreateToolhelp32Snapshot:%d\n", v36);
                }
                // 遍历进程快照
                v53[0] = 568;
                if ((kernel32_Process32FirstW)(v35, v53))
                {
                LABEL_34:
                    memeset();
                    (kernel32_WideCharToMultiByte)(0i64, 0i64, v54, 0xFFFFFFFFi64, v49, 260, 0i64, 0i64);
                    v38 = 0;
                    // 检查是否存在"ZhuDongFangYu.exe"和"360Tray.exe"进程
                    if (ucrtbase_strcmp("ZhuDongFangYu.exe", v49))
                    {
                        while (ucrtbase_strcmp("360Tray.exe", v49))
                        {
                            if (++v38 >= 6)
                            {
                                if ((kernel32_Process32NextW)(v35, v53))
                                    goto LABEL_34;
                                goto LABEL_40;
                            }
                        }
                    }
                    printf("检测到360。。。。\n");
                    v5 = 1;
                }
                else
                {
                    v37 = (kernel32_GetLastError)();
                    printf("Process32First:%d\n", v37);
                }
LABEL_40:
                printf("%d\n", v5);
            }
            sub_140001320();
        }
    }
}
return 0;

这段代码的核心函数就是sub_140001320();,前面查找"ZhuDongFangYu.exe" 和 "360Tray.exe" 这两个进程并不会阻止程序的运行!他还会检测计算机所使用的操作系统!

RtlGetVersion 函数所返回的结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef struct _OSVERSIONINFOEXW {
    ULONG dwOSVersionInfoSize;
    ULONG dwMajorVersion;
    ULONG dwMinorVersion;
    ULONG dwBuildNumber;
    ULONG dwPlatformId;
    WCHAR szCSDVersion[128];
    USHORT wServicePackMajor;
    USHORT wServicePackMinor;
    USHORT wSuiteMask;
    BYTE wProductType;
    BYTE wReserved;
} OSVERSIONINFOEXW, *POSVERSIONINFOEXW, *LPOSVERSIONINFOEXW;

分析最终flag隐藏的位置sub_140001320()

解析一下代码:

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// 加载 XMM 寄存器
si128 = _mm_load_si128(xmmword_140003A20);
 
// 获取 Windows API 函数的地址
OpenProcess = kernel32_GetProcAddress(KERNEL32Moudle, "OpenProcess");
VirtualAllocEx = kernel32_GetProcAddress(KERNEL32Moudle, "VirtualAllocEx");
WriteProcessMemory = kernel32_GetProcAddress(KERNEL32Moudle, "WriteProcessMemory");
CreateRemoteThread = kernel32_GetProcAddress(KERNEL32Moudle, "CreateRemoteThread");
 
// 初始化指针变量
v3 = &v41;
v4 = &unk_140003450;
v5 = 7i64;
 
// 循环复制数据
do {
    v3 += 32;
    v6 = *v4;
    v7 = v4[1];
    v4 += 8;
    *(v3 - 8) = v6;
    v8 = *(v4 - 6);
    *(v3 - 7) = v7;
    v9 = *(v4 - 5);
    *(v3 - 6) = v8;
    // 省略剩余赋值操作
    --v5;
} while (v5);
 
// 继续复制数据
v14 = *(v4 + 4);
v15 = 6;
v16 = -1162190778i64;
v17 = 1i64;
*v3 = *v4;
v3[4] = v14;
 
// 循环进行数据变换
do {
    v18 = 227i64;
    v19 = &v45;
    v20 = 227;
    v21 = &v44;
    do {
        v22 = *v21;
        v21 -= 4;
        v19 -= 4;
        v23 = *(&v41 + (v20 + 1) % 0xBu);
        v24 = v17 ^ v18-- & 3;
        *(v19 + 1) -= ((v23 ^ v16) + (si128.m128i_i32[v24] ^ v22)) ^ (((v22 >> 6) ^ (4 * v23)) + ((16 * v22) ^ (v23 >> 3)));
        --v20;
    } while (v20);
    v25 = v42 ^ v16;
    v16 -= 1953785185i64;
    v41 -= (v25 + (si128.m128i_i32[v17] ^ v43)) ^ (((v43 >> 6) ^ (4 * v42)) + ((16 * v43) ^ (v42 >> 3)));
    v17 = (v16 >> 2) & 3;
    --v15;
} while (v15);
 
// 输出处理后的数据
v26 = &v41;
for (i = 0; i < 0x393; ++i)
    printf("%c ", *v26++);
 
// 枚举系统中的进程
Toolhelp32Snapshot = kernel32_CreateToolhelp32Snapshot(2i64, 0i64);
if (Toolhelp32Snapshot == -1) {
    v29 = (kernel32_GetLastError)();
    printf("CreateToolhelp32Snapshot:%d\n", v29);
}
v46[0] = 568;
if ((kernel32_Process32FirstW)(Toolhelp32Snapshot, v46)) {
    // 查找 "exp10rer.exe" 进程
    while (true) {
        v39 = -1i64;
        do {
            if (*(&v46[11] + v39 + 1) != aExp10rerExe[v39 + 1])
                break;
            v39 += 2i64;
            if (v39 == 13) {
                v31 = v46[2];
                goto LABEL_14;
            }
        } while (*(&v46[11] + v39) == aExp10rerExe[v39]);
        if ((kernel32_Process32NextW)(Toolhelp32Snapshot, v46))
            continue;
        break;
    }
} else {
    v30 = (kernel32_GetLastError)();
    printf("Process32First:%d\n", v30);
}
//程序如果未找到目标程序exp10rer.exe,就会将CreateRemoteThread复制给v31
v31 = CreateRemoteThread;  
LABEL_14:
printf("inject process pid: %d\n", v31);
 
// 注入代码到目标进程
v32 = OpenProcess(0x1FFFFFi64, 0i64, v31);
v33 = (kernel32_GetLastError)();
printf("OpenProcess:%d\n", v33);
v34 = VirtualAllocEx(v32, 0i64, 916i64, 12288i64, 64);
v35 = (kernel32_GetLastError)();
printf("VirtualAllocEx:%d\n", v35);
WriteProcessMemory(v32, v34, &v41, 916i64, 0i64);
v36 = (kernel32_GetLastError)();
printf("WriteProcessMemory:%d\n", v36);
CreateRemoteThread(v32, 0i64, 0i64, v34, 0i64, 0, 0i64);
v37 = (kernel32_GetLastError)();
return printf("CreateRemoteThread:%d\n", v37);

这段代码的主要功能是:

  1. 加载一些 Windows API 函数的地址。
  2. 从内存中读取并处理一些数据。
  3. 枚举系统中正在运行的进程,并找到名为 "exp10rer.exe" 的进程。
  4. 将处理后的数据注入到目标进程的内存中。
  5. 在目标进程中创建一个新的远程线程,并执行注入的代码。
    但是电脑中并没有这个程序或进程exp10rer.exe,这个进程大概率在源码中是存在的但是被本题魔改掉了,因为这是ctf比赛,写成这样已经将整个病毒架构写出来了!

分析最终注入其他程序的shellcode

1
2
3
4
// 输出处理后的数据
v26 = &v41;
for (i = 0; i < 0x393; ++i)
    printf("%c ", *v26++);

这段代码会输出最终的shellcode,也就是我们最终要分析的目标!直接在这个位置下个断点!!!dump一下数据就好了!!

利用idapython将数据dump出来!

1
2
3
from ida_bytes import get_bytes, patch_bytes
with open('dump', 'wb') as f:
    f.write(idaapi.get_bytes(0x5FF060, 0x393))

将内存dump出来之后就直接拖入ida看看这段shellcode的作用,当然也可以直接在ida里将shellcode转为指令直接分析不需要dump!

但是我还是dump出来更加清晰:
在shellcode的最后就隐藏这flag:


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

上传的附件:
收藏
免费 1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回