[HGAME 2022 week4]ezvm

分析程序

IDA 打开,main 函数中有很多 switch-case 语句,可以判断可能是 VM 程序

VM 程序首先需要先找到 opcode,(执行指令的顺序)

先查看 switch 的参数 opcode 来源于 EIP

EIP 就一个值为 0,也就是 EIP[0] = 0

opcode 取值为 EIP 的 0+109(0x6D)

计算一下位置为 49F08D

查看 49F08D 什么值都没有,但是注意到数据全是 db(byte)字节,

一般 VM 题目 opcode 都是 dd(Dword)双字

所以将他按照 dword 格式跳转一下(db x 4 = dd, 109 x 4)

查看 49F1D4,看到刚好是一些数据开始的位置

这串数据就是 VM 程序需要的 opcode 了

将这些数据导出,等下写脚本要用,一直到 FFFFFFFF 就是结束了

导出的时候将 Byte 改成 Dword

识别 VM 指令

然后我们开始分析 VM case 的每个指令

case0 - case3

赋值,对应 mov 指令

自增,对应 inc 指令

自减,对饮 dec 指令

异或,对应 xor 指令

case4 - case6

在 case 4 - case 6 全部用到了这个 EIP[ESP + 9]

EIP 在前面取 opcede 的时候用到了,地址 0049F020

这里又多了一个 ESP + 9

查看 ESP 值为 FFFFFFFF

这里用的是补码,FFFFFFFF 在有符号数值里为 -1

所以就是 - 1 + 9,然后 byte 还要 x 4

查看 49F044,这里在内存中分配了一些空字节

在这几个 VM 指令中都是指针自增后,向里面填寄存器的数据

所以这里应该是模拟栈,往栈里填数据就是

入栈:对应 push 指令

case8 - case11

这里和上面一样也是关于 ESP 的栈操作,多了一个判断栈指针是否合法(>= 0)

栈指针自减,然后将栈中数据给寄存器

出栈:对应 pop 指令

case12 - case15

如果 ZF 为 0,EIP(opcode 指针) 加上 EDX 的值(指针偏移)

这里 ZF 的值由 case15 的 cmp 指令赋值的,结合 case15 就能明白

相等跳转:对应 jz [ 指针+偏移 ]

同上

不相等跳转:对应 jnz [ 指针+偏移 ]

绝对跳转:对应 jmp [ 指针+偏移 ]

这里对 ZF 进行赋值,如果 EAX 等于 ECX,ZF=0,如果小于,ZF=-1,如果大于 ZF=1

比较:对应 cmp 指令

case16-case17

调用 getchar 获取输入

调用 putchar 输出 EAX

case18

idx 初始值为 0

调用了 EIP[idx + 209],(byte x 4)

跳转到 49F364,看到都是一些数据

VM 指令中每次将一个数据压入栈中

取数据入栈:用 push data[idx++] 表示

case19 - case20

将栈中指定数据取出到 EAX

mov EAX, [ 栈指针 + 偏移 ]

将 EAX 数据存到指定栈中

mov [ 栈指针 + 偏移 ], EAX

case21

这里可以理解为 << 1 或是 + EAX

左移:对应 shl 指令

最后 EIP 自增,准备读取下一个 opcode

还原伪汇编

分析的时候 dump opcode,然后搓脚本

opcode = [0x12, 0x8, 0x12, 0x9, 0x10, 0x4, 0x1, 0xf, 0xd, 0x2, 0x12, 0x8, 0x12, 0x9, 0x0, 0x4, 0xf, 0xd, 0x12, 0x9, 0x12, 0xa, 0x13, 0x12, 0xb, 0x15, 0x3, 0x14, 0x1, 0x0, 0xf, 0xd, 0x12, 0xa, 0x12, 0x12, 0x12, 0x8, 0x13, 0xf, 0x7, 0x4, 0x9, 0xd, 0x9, 0x8, 0x5, 0x6, 0x4, 0x1, 0x0, 0xf, 0xd, 0x12, 0x9, 0x12, 0x8, 0x12, 0xa, 0x12, 0x7, 0xf, 0xc, 0x11, 0xe, 0xffffffff]
data = [0xa, 0xfffffffb, 0x20, 0x2f, 0xfffffff6, 0x0, 
        0x5e, 0x46, 0x61, 0x43, 0xe, 0x53, 0x49, 0x1f, 0x51, 0x5e, 0x36, 0x37, 0x29, 0x41, 0x63, 0x3b, 0x64, 0x3b, 0x15, 0x18, 0x5b, 0x3e, 0x22, 0x50, 0x46, 0x5e, 0x35, 0x4e, 0x43, 0x23, 0x60, 0x3b, 
        0x0, 0xffffffef, 0x15, 
        0x8e, 0x88, 0xa3, 0x99, 0xc4, 0xa5, 0xc3, 0xdd, 0x19, 0xec, 0x6c, 0x9b, 0xf3, 0x1b, 0x8b, 0x5b, 0x3e, 0x9b, 0xf1, 0x86, 0xf3, 0xf4, 0xa4, 0xf8, 0xf8, 0x98, 0xab, 0x86, 0x89, 0x61, 0x22, 0xc1, 
        0x2, 0x0, 0xfffffffa, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0]

ip = 0
while ip < len(opcode):
    match opcode[ip]:
        case 0:
            print(ip,"mov eax, ebx")
        case 1:
            print(ip,"inc ebx")
        case 2:
            print(ip,"dec ebx")
        case 3:
            print(ip,"xor eax, esi")
        case 4:
            print(ip,"push eax")
        case 5:
            print(ip,"push ecx")
        case 6:
            print(ip,"push edx")
        case 7:
            print(ip,"pop eax")
        case 8:
            print(ip,"pop ecx")
        case 9:
            print(ip,"pop edx")
        case 10:
            print(ip,"pop ebx")
        case 11:
            print(ip,"pop esi")
        case 12:
            print(ip,"jz [ip + edx]")
        case 13:
            print(ip,"jnz [ip + edx]")
        case 14:
            print(ip,"jmp [ip + ebx]")
        case 15:
            print(ip,"cmp eax, ecx")
        case 16:
            print(ip,"getchar(eax)")
        case 17:
            print(ip,"putchar(eax)")
        case 18:
            print(ip,"push data[idx++]")
        case 19:
            print(ip,"mov eax, [esp + ebx]")
        case 20:
            print(ip,"mov [esp + ebx], eax")
        case 21:
            print(ip,"shl eax, 1")
    ip += 1

运行得到下面(伪)汇编代码

0 push data[idx++]      ;0xa
1 pop ecx
2 push data[idx++]      ;0xfffffffb(-5)
3 pop edx
4 getchar(eax)
5 push eax
6 inc ebx
7 cmp eax, ecx
8 jnz [ip + edx]

9 dec ebx
10 push data[idx++]     ;0x20
11 pop ecx
12 push data[idx++]     ;0x2f
13 pop edx
14 mov eax, ebx
15 push eax
16 cmp eax, ecx
17 jnz [ip + edx]

18 push data[idx++]     ;0xfffffff6(-10)
19 pop edx
20 push data[idx++]     ;0x0
21 pop ebx
22 mov eax, [esp + ebx]
23 push data[idx++]     ;共取32个异或值
24 pop esi
25 shl eax, 1
26 xor eax, esi
27 mov [esp + ebx], eax
28 inc ebx
29 mov eax, ebx
30 cmp eax, ecx
31 jnz [ip + edx]

32 push data[idx++]     ;0x0
33 pop ebx
34 push data[idx++]     ;0xffffffef(-17)
35 push data[idx++]     ;0x15
36 push data[idx++]     ;共取32个密文
37 pop ecx
38 mov eax, [esp + ebx]
39 cmp eax, ecx
40 pop eax
41 push eax
42 pop edx
43 jnz [ip + edx]

44 pop edx
45 pop ecx
46 push ecx
47 push edx
48 push eax
49 inc ebx
50 mov eax, ebx
51 cmp eax, ecx
52 jnz [ip + edx]

53 push data[idx++]     ;0x2
54 pop edx
55 push data[idx++]     ;0x0
56 pop ecx
57 push data[idx++]     ;0xfffffffa(-6)
58 pop ebx
59 push data[idx++]     ;'success'
60 pop eax
61 cmp eax, ecx
62 jz [ip + edx]
63 putchar(eax)
64 jmp [ip + ebx]

进行正常的汇编代码分析

分析汇编

0-8

获取输入,直到获取到"\n"结束

0 push data[idx++]      ;0xa	;入栈0xa("\n")
1 pop ecx						;出栈0xa到ecx="\n"
2 push data[idx++]      ;0xfffffffb(-5)			;入栈-5
3 pop edx						;入栈-5到edx=-5
4 getchar(eax)					;获取字符输入到eax
5 push eax						;入栈eax=输入
6 inc ebx						;ebx自增, ebx++ (ebx作为计数器)
7 cmp eax, ecx					;比较eax和ecx, 输入比较"\n"
8 jnz [ip + edx]				;不相等跳转ip+(-5)=3, 执行下一条4
9-17

判断输入长度是否为 32,否则结束程序

9 dec ebx						;ebx自减, ebx--
10 push data[idx++]     ;0x20	;入栈0x20(32)
11 pop ecx						;出栈ecx=32
12 push data[idx++]     ;0x2f	;入栈0x2f(47)
13 pop edx						;出栈edx=47
14 mov eax, ebx					;eax=ebx
15 push eax						;入栈eax
16 cmp eax, ecx					;比较eax和ecx, 输入长度比较32
17 jnz [ip + edx]				;不相等跳转ip+47=64, 执行下一条65
18-31

进行主要运算 (flag << 1) ^ data,循环 32 次

18 push data[idx++]     ;0xfffffff6(-10)		;入栈-10
19 pop edx						;出栈edx=-10
20 push data[idx++]     ;0x0	;入栈0
21 pop ebx						;出栈0到ebx=0
22 mov eax, [esp + ebx]			;eax=栈指针+0, 循环获取输入
23 push data[idx++]     ;共取32个异或值			;获取data中的数据段(循环)
24 pop esi						;入栈数据到esi=数据
25 shl eax, 1					;将输入左移1位
26 xor eax, esi					;异或数据
27 mov [esp + ebx], eax			;栈指针+0=eax, 将运算后的输入返回栈中
28 inc ebx						;ebx自增 ebx++
29 mov eax, ebx					;eax=ebx
30 cmp eax, ecx					;比较eax和ecx, 循环次数对比32
31 jnz [ip + edx]				;不相等跳转ip+(-10)= 21, 执行下一条22
32-43

验证加密后的输入和 data 中的数据是否相等,否则结束程序

32 push data[idx++]     ;0x0	;入栈0
33 pop ebx						;出栈ebx=0
34 push data[idx++]     ;0xffffffef(-17)		;入栈-17
35 push data[idx++]     ;0x15					;入栈0x15(21)
36 push data[idx++]     ;共取32个密文			;入栈data数据段(循环)
37 pop ecx				;出栈数据
38 mov eax, [esp + ebx]	;eax=栈指针+0位置的数据(输入)
39 cmp eax, ecx			;比较eax和ecx, 输入值和后取的数据进行比较(循环)
40 pop eax
41 push eax
42 pop edx				;出栈edx=21
43 jnz [ip + edx]		;不相等跳转ip+21=64, 执行下一条65
44-52

循环验证 32 次

44 pop edx				;出栈edx=-17
45 pop ecx
46 push ecx
47 push edx				;入栈-17
48 push eax				;入栈数据
49 inc ebx				;ebx自增, ebx++
50 mov eax, ebx			;eax=ebx
51 cmp eax, ecx			;比较32和计数器
52 jnz [ip + edx]		;跳转到ip+(-17)=36, 执行下一条37
53-64

输出 'success'

53 push data[idx++]     ;0x2				;入栈2
54 pop edx									;出栈edx=2
55 push data[idx++]     ;0x0				;入栈0
56 pop ecx									;出栈ecx=0
57 push data[idx++]     ;0xfffffffa(-6)		;入栈-6
58 pop ebx									;出栈ebx=-6
59 push data[idx++]     ;'success'			;入栈's'
60 pop eax									;出栈eax='s'
61 cmp eax, ecx								;比较eax和0
62 jz [ip + edx]							;相等跳转到ip+2=64,执行下一条65
63 putchar(eax)								;输出eax中的字符
64 jmp [ip + ebx]							;绝对跳转到ip+(-6)=59, 执行下一条60

最后总结程序的逻辑是将输入的字符进行右移和异或指定字符后,与密文进行对比,成功输出 success

还原伪 C 代码

转成 C 语言代码

#include <stdio.h>

int main() {
    unsigned char key[32] = {0x5e, 0x46, 0x61, 0x43, 0xe, 0x53, 0x49, 0x1f, 0x51, 0x5e, 0x36, 0x37, 0x29, 0x41, 0x63, 0x3b, 0x64, 0x3b, 0x15, 0x18, 0x5b, 0x3e, 0x22, 0x50, 0x46, 0x5e, 0x35, 0x4e, 0x43, 0x23, 0x60, 0x3b};
    unsigned char str1[32] = {0x8e, 0x88, 0xa3, 0x99, 0xc4, 0xa5, 0xc3, 0xdd, 0x19, 0xec, 0x6c, 0x9b, 0xf3, 0x1b, 0x8b, 0x5b, 0x3e, 0x9b, 0xf1, 0x86, 0xf3, 0xf4, 0xa4, 0xf8, 0xf8, 0x98, 0xab, 0x86, 0x89, 0x61, 0x22, 0xc1};
    unsigned char input[33];
    int i = 0;

    while((input[i] = getchar()) != '\n')
        i++;

    if(i != 32)
        return 0;

    for(i = 0; i < 32; i++)
        input[i] = (input[i] << 1) ^ key[i];

    for(i = 0; i < 32; i++)
        if(input[i] != str1[i])
            return 0;

    printf("success");
    return 0;
}

主要逻辑为input = (input << 1) ^ key

EXP

enc = [0x8e, 0x88, 0xa3, 0x99, 0xc4, 0xa5, 0xc3, 0xdd, 0x19, 0xec, 0x6c, 0x9b, 0xf3, 0x1b, 0x8b, 0x5b, 0x3e, 0x9b, 0xf1, 0x86, 0xf3, 0xf4, 0xa4, 0xf8, 0xf8, 0x98, 0xab, 0x86, 0x89, 0x61, 0x22, 0xc1]
key = [0x5e, 0x46, 0x61, 0x43, 0xe, 0x53, 0x49, 0x1f, 0x51, 0x5e, 0x36, 0x37, 0x29, 0x41, 0x63, 0x3b, 0x64, 0x3b, 0x15, 0x18, 0x5b, 0x3e, 0x22, 0x50, 0x46, 0x5e, 0x35, 0x4e, 0x43, 0x23, 0x60, 0x3b]
for i in range(32):
    print(chr((enc[i] ^ key[i]) >> 1),end="")

hgame{Ea$Y-Vm-t0-PrOTeCT_cOde!!}

总结

这是一个 VM 虚拟机程序,和普通 VM 程序不同的地方在于,他使用了指针来指向 opcode、数据、虚拟栈,在分析过程中需要了解软件运行原理,对汇编足够了解,模拟了一个较为完整的软件运行环境,对 VM 虚拟机的进阶学习比较有价值

内容概要:本文是一份针对2025年中国企业品牌传播环境撰写的《全网媒体发稿白皮书》,聚焦企业媒体发稿的策略制定、渠道选择与效果评估难题。通过分析当前企业面临的资源分散、内容同质、效果难量化等核心痛点,系统性地介绍了新闻媒体、央媒、地方官媒和自媒体四大渠道的特点与适用场景,并深度融合“传声港”AI驱动的新媒体平台能力,提出“策略+工具+落地”的一体化解决方案。白皮书详细阐述了传声港在资源整合、AI智能匹配、舆情监测、合规审核及全链路效果追踪方面的技术优势,构建了涵盖曝光、互动、转化与品牌影响力的多维评估体系,并通过快消、科技、零售等行业的实战案例验证其有效性。最后,提出了按企业发展阶段和营销节点定制的媒体组合策略,强调本土化传播与政府关系协同的重要性,助力企业实现品牌声量与实际转化的双重增长。; 适合人群:企业市场部负责人、品牌方管理者、公关传播从业者及从事数字营销的相关人员,尤其适用于初创期至成熟期不同发展阶段的企业决策者。; 使用场景及目标:①帮助企业科学制定媒体发稿策略,优化预算分配;②解决渠道对接繁琐、投放不精准、效果不可衡量等问题;③指导企业在重大营销节点(如春节、双11)开展高效传播;④提升品牌权威性、区域渗透力与危机应对能力; 阅读建议:建议结合自身企业所处阶段和发展目标,参考文中提供的“传声港服务组合”与“预算分配建议”进行策略匹配,同时重视AI工具在投放、监测与优化中的实际应用,定期复盘数据以实现持续迭代。
先展示下效果 https://pan.quark.cn/s/987bb7a43dd9 VeighNa - By Traders, For Traders, AI-Powered. Want to read this in english ? Go here VeighNa是一套基于Python的开源量化交易系统开发框架,在开源社区持续不断的贡献下一步步成长为多功能量化交易平台,自发布以来已经积累了众多来自金融机构或相关领域的用户,包括私募基金、证券公司、期货公司等。 在使用VeighNa进行二次开发(策略、模块等)的过程中有任何疑问,请查看VeighNa项目文档,如果无法解决请前往官方社区论坛的【提问求助】板块寻求帮助,也欢迎在【经验分享】板块分享你的使用心得! 想要获取更多关于VeighNa的资讯信息? 请扫描下方二维码添加小助手加入【VeighNa社区交流微信群】: AI-Powered VeighNa发布十周年之际正式推出4.0版本,重磅新增面向AI量化策略的vnpy.alpha模块,为专业量化交易员提供一站式多因子机器学习(ML)策略开发、投研和实盘交易解决方案: :bar_chart: dataset:因子特征工程 * 专为ML算法训练优化设计,支持高效批量特征计算与处理 * 内置丰富的因子特征表达式计算引擎,实现快速一键生成训练数据 * Alpha 158:源于微软Qlib项目的股票市场特征集合,涵盖K线形态、价格趋势、时序波动等多维度量化因子 :bulb: model:预测模型训练 * 提供标准化的ML模型开发模板,大幅简化模型构建与训练流程 * 统一API接口设计,支持无缝切换不同算法进行性能对比测试 * 集成多种主流机器学习算法: * Lass...
### HGAME 2022 Week1 的网络流量分析 关于 HGAME 2022 Week1 中涉及的网络流量分析相关内容,虽然官方并未提供详细的流量包文件供下载[^1],但从参赛者的经验分享来看,Week1 阶段确实包含了 Web 和 Misc 类型的题目,这些题目可能涉及到基础的网络通信协议解析以及抓包分析技能[^2]。 在网络流量分析方面,通常会使用工具如 Wireshark 或 tcpdump 来捕获和分析 HTTP/HTTPS 请求、DNS 查询或其他类型的网络交互行为。如果比赛中存在需要通过流量分析解密 Flag 的场景,则可以推测其设计思路如下: #### 工具推荐 以下是常用的网络数据分析工具及其功能概述: - **Wireshark**: 图形化界面支持过滤特定协议(HTTP, DNS 等),并能追踪 TCP 流量。 - **tcpdump**: 命令行方式快速获取指定条件下的数据包日志。 #### 技术要点 对于潜在的流量分析挑战,以下几点可能是关键的技术方向: 1. 使用 `tshark` 提取 HTTP POST 请求中的负载字段,查找隐藏的信息片段。 ```bash tshark -r capture.pcapng -Y 'http.request.method == "POST"' -T fields -e data.text ``` 2. 如果加密机制被引入到流量中,尝试识别使用的算法并通过已知明文攻击恢复原始消息。 尽管目前没有具体提及 HGAME 2022 Week1 是否包含专门针对流量分析的任务,但基于 CTF 比赛的一般模式,上述方法论具有较高的适用价值。 ```python import re def extract_payloads(pcap_file_path): """模拟提取PCAP文件内的有效载荷""" with open(pcap_file_path, 'rb') as f: content = f.read() payloads = [] pattern = rb'(?<=Payload: ).*?(?=\n\n)' matches = re.findall(pattern, content) for match in matches: decoded_match = match.decode('utf-8', errors='ignore') payloads.append(decoded_match.strip()) return payloads ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值