NKCTF2024 re VM?VM!WP

逻辑似乎很简单(个鬼啊)

这个函数是把输入的字符转化为二进制并倒序存储

sub_1570太大了加载不出来,应该是加密的主逻辑,目的是需要输出1

可以通过删除栈的方法强行转化伪代码

首先删掉这部分

9A0改小点

这个也是

栈这里U一下再P

像是虚拟机的分发器,unk_4018是类似opcode的大坨数据

好像有几十万

实际上是一个2324*2324的像素图,在上面进行染色

cable management | /den/face0xff/writeupsicon-default.png?t=N7T8https://ctf.0xff.re/2022/dicectf_2022/cable_management

【游戏框架系列】Wireworld元胞自动机 - 知乎【多图预警】本文含有大量图片写在前面这次实现的是Wireworld元胞自动机,相关资料如下: 维基:Wireworld - Wikipedia 介绍:WireworldFlash模拟:Wireworld Player (Flash) 如何实现简单的计算机:The Wireworld…icon-default.png?t=N7T8https://zhuanlan.zhihu.com/p/25593938

根据资料我才知道,这其实是在模拟Wireworld元胞自动机

组成:

  1. 导体
  2. 电子头
  3. 电子尾

每代变化:

  1. 空→空
  2. 电子头→电子尾
  3. 电子尾→导体
  4. 当仅有一个或仅有两个电子头的邻居是导体时,导体→电子头

对应着理解,0xCD就是导线

0xEA就是电子尾,0xEC是电子头

0x11是数据注入点

0x80相当于空/消除信号

1就是终点,到达就返回1

由此根据数据绘图分析,这实际上是利用wireworld高度抽象的虚拟机

数据量太大,这里用脚本提取

#data_extract
import idaapi  
import idautils  # 设置你要读取数据的起始和结束地址  
START_ADDR = 0x4018  # 替换为你的起始地址  
END_ADDR = 0x535D93  # 替换为你的结束地址  
BYTES_PER_LINE = 16  # 每行显示的字节数  # 打开一个文件用于写入,如果文件不存在则创建它  
with open('output.txt', 'w') as f:  # 用于记录当前行已经写入了多少字节  bytes_written = 0  # 遍历指定地址范围内的每个地址  for ea in range(START_ADDR, END_ADDR + 1):  # 读取当前地址的一个字节  byte_value = idaapi.get_byte(ea)  # 将字节转换为十六进制字符串  hex_string = '0x{:02X}'.format(byte_value)  # 写入文件,并在需要时添加逗号  if bytes_written > 0 and bytes_written % BYTES_PER_LINE != 0:  f.write(',')  f.write(hex_string)  bytes_written += 1  # 如果当前行已经写入了足够的字节数,则换行  if bytes_written % BYTES_PER_LINE == 0:  f.write(',\n')  # 文件会在脚本执行完毕后自动关闭  
print("Data has been written to output.txt")

然后绘图(来自孤恒师傅的脚本)

from PIL import Image
s = [...]
img = Image.new('RGB', (0x914, 0x914), (255, 255, 255))
pixels = img.load()
for i in range(len(s)):if s[i] == 0x1:i_row = i // 0x914i_col = i % 0x914#print("0x01_row:"+f"{i_row:X}" + "  0x01_col:"+f"{i_col:X}")pixels[i_row, i_col] = (255, 0, 0)elif s[i] == 0xEC:i_row = i // 0x914i_col = i % 0x914#print("0xEC_row:"+f"{i_row:X}" + "  0xEC_col:"+f"{i_col:X}")pixels[i_row, i_col] = (0, 0, 255)elif s[i] == 0x11:i_row = i // 0x914i_col = i % 0x914#print("0x11_row:"+f"{i_row:X}" + "  0x11_col:"+f"{i_col:X}"pixels[i_row, i_col] = (0, 255, 0)elif s[i] == 0xCD:i_row = i // 0x914i_col = i % 0x914#print("0xCD:"+f"{i:X}")pixels[i_row, i_col] = (0, 0, 0)elif s[i] == 0x80:i_row = i // 0x914i_col = i % 0x914#print("0xCD:"+f"{i:X}")pixels[i_row, i_col] = (255, 255, 0)elif s[i] == 0xEA:i_row = i // 0x914i_col = i % 0x914#print("0xCD:"+f"{i:X}")pixels[i_row, i_col] = (0, 255, 255)img.save('D:\\下载\\CTF附件\\nk\\image.png')

有大量重复的图案,我们放大进行分析

乍一看难以理解,结合wireworld的规则,我们可以分析出图案蕴藏的含义

最上面就是一条畅通无阻的电线

下面很复杂,先看重复度最高的这部分,注意到不断重复的这个模块

我们用网站模拟

Wireworld Simulatoricon-default.png?t=N7T8https://danprince.github.io/wireworld/

只有一侧输入信号(即一侧1,一侧0)时,电子头会正常向下传导

但如果两边都有

信号就会消失

所以这其实是在模拟异或(逻辑门),原理是在这里

箭头所指的导线周围有三个电子头,但我们的规则是当仅有一个或仅有两个电子头的邻居是导体时,导体→电子头

因此就达到了异或的效果

图中除了大量的异或,还有别的图形

可以用相同的方法分析出这个是与

这个是或

这个则是一个二极管,像神经突触一样,只会从“突触前模”向“突触后膜”单向传导

我们可以根据这些特征把整个图分割开,由于每个输入的字符都转化为8位二进制注入(绿色就是注入点),所以我每八个循环分割一次,最后得到29块,对应29个输入的字符

输入的字符究竟是如何处理的呢?可以看到,每个绿色注入点激活的电子头会向左右两边传导,经过一个异或(输入的字符转化为倒序二进制数,这个数的每相邻两位异或)之后,与蓝色点激活的电子头再次异或,之后得到的信号就会输入下面的向右的二极管结构

也就是说,只要有一个异或结果是1(有电),电子头就会向右传导到失败点。所以我们的目标就是消除所有产生的电子头,使得上面那一条完整的电线上的电子头平安到达左侧的终点

可以看到,蓝色电子头总是在循环结构相同的位置上,代表着这个位置上电信号的1和0,将他们连接在一起就相当于组成了一个由0、1组成的key(可以脚本取key,我嫌麻烦就直接手敲了)

我们要求的flag是上面一排注入点的0/1状态,加密过程就是我之前说的一系列异或,我们期望得到的结果就是全0

由此我们可以写出解密的函数,将key与全0序列异或逆序转化成字符串

def decode(key):key_string = long_to_bytes(int("".join([str(i) for i in key]), 2))#print(key_string)res = [0]for i in range(len(key)-1):tmp = res[i]^key[i]res.append(tmp)#print(res)flag = long_to_bytes(int("".join([str(i) for i in res]), 2))print(flag)

但我们并不能把整段key直接放进去解密,因为整个wireworld结构被中段分割成了两半,右边(相当于前面一半)是插入了与的部分

左边是插入了或的部分

先看前半部分,与的前面有四个注入点,key第四位相当于缺失的

但反正就0和1两种情况,拿出前八位二进制试一下就会发现这一位为1的时候可以得到n(就是nkctf的开头),否则会得到a

然后第9位一直到断点前面都是连续的,可以得到第二段flag

中间这个位置比较尴尬,这四位右边是断开的,左边是个或,只要有一个满足1就能激活

中间四位排列组合一下就能凑出有意义字符

剩下的直接输入

就可以得到完整的flag了

#VM?VM!
from Crypto.Util.number import long_to_bytes
key0 = [1, 0, 1, 1, 0, 0, 1, 0]key1 = [1, 0, 1, 1, 1, 1, 0, 1, 
1, 0, 1, 0, 0, 1, 0, 1,
1, 0, 0, 1, 1, 1, 0, 0,
1, 0, 1, 0, 1, 0, 1, 0, 
1, 0, 0, 0, 1, 1, 0, 1, 
1, 0, 0, 1, 1, 0, 0, 1, 
1, 0, 1, 1, 1, 0, 1, 1, 
1, 1, 1, 1, 0, 1, 1, 0, 
1, 0, 1, 0, 1, 1, 1, 1, 
1, 1, 1, 1, 1, 0, 0, 1, 
1, 0, 1, 1, 0, 0, 0, 1]key2 = [0, 1, 1, 1, 0, 1, 
1, 1, 
1, 0, 
1, 1, 0, 1, 0, 0]key3 = [1, 0, 1, 0, 1, 1, 0, 0, 
1, 1, 1, 0, 0, 0, 0, 1, 
0, 1, 0, 1, 0, 0, 1, 1, 
1, 0, 0, 1, 0, 1, 0, 1, 
1, 1, 1, 0, 0, 0, 0, 1, 
1, 0, 1, 1, 0, 0, 1, 0, 
0, 1, 0, 1, 0, 0, 0, 0, 
1, 0, 0, 1, 1, 1, 0, 0, 
1, 1, 1, 0, 0, 0, 0, 1, 
1, 0, 1, 0, 1, 0, 0, 1, 
1, 0, 1, 1, 0, 0, 0, 1, 
1, 1, 0, 1, 0, 0, 0, 1, 
1, 1, 0, 0, 1, 1, 0, 0, 
0, 1, 1, 0, 0, 0, 1, 1, 
1, 0, 0, 0, 0, 1, 1, 0]
def decode(key):key_string = long_to_bytes(int("".join([str(i) for i in key]), 2))#print(key_string)res = [0]for i in range(len(key)-1):tmp = res[i]^key[i]res.append(tmp)#print(res)flag = long_to_bytes(int("".join([str(i) for i in res]), 2))print(flag)
decode(key0)
decode(key1)
decode(key2)
decode(key3)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/792076.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DFS:深搜+回溯+剪枝解决组合问题

创作不易,感谢支持!!! 一、电话号码的组合 . - 力扣(LeetCode) class Solution { public:string hash[10]{"","","abc","def","ghi","jkl","mno","pqrs"…

爬虫部署平台crawlab使用说明

Crawlab 是一个基于 Go 语言的分布式网络爬虫管理平台,它支持 Python、Node.js、Jar、EXE 等多种类型的爬虫。 Crawlab 提供了一个可视化的界面,并且可以通过简单的配置来管理和监控爬虫程序。 以下是 Crawlab 的一些主要优点: 集中管理&am…

【C】leetcode力扣—— 141. 环形链表Ⅰ

目录 141. 环形链表 Ⅰ题目解题思路分析暴力求解??快慢指针 代码 141. 环形链表 Ⅰ 题目链接: https://leetcode.cn/problems/linked-list-cycle/description/ 题目 题目 给你一个链表的头节点 head ,判断链表中是否有环。 如果链表中有某…

E-魔法猫咪(遇到过的题,做个笔记)

题解: 来自学长们思路: 其中一种正解是写单调队列。限制队列内的数单调递增,方法为每当新来的数据比当前队尾数据小时队 尾出列,直到能够插入当前值,这保证了队头永远是最小值。因此总体思路是队尾不断插入新值的同时 …

openlayers 入门教程(九):overlay 篇

还是大剑师兰特:曾是美国某知名大学计算机专业研究生,现为航空航海领域高级前端工程师;CSDN知名博主,GIS领域优质创作者,深耕openlayers、leaflet、mapbox、cesium,canvas,webgl,ech…

vue-cli打包 nodejs内存溢出 vue2.x Last few GCs

遇到这种情况百度各种博客,什么改package.json里的配置,什么安装increase-memory-limit ,都尝试了并没什么用处,最后解决方案为执行下方名单,再次打包就成功了: export NODE_OPTIONS--max_old_space_size4…

单元测试 mockito(二)

1.返回指定值 2.void返回值指定插桩 3.插桩的两种方式 when(obj.someMethod()).thenXxx():其中obj可以是mock对象 doXxx().wien(obj).someMethod():其中obj可以是mock/spy对象 spy对象在没有插桩时是调用真实方法的,写在when中会导致先执行一次原方法,达不到mock的目的&#x…

好物视频素材在哪找?视频素材大全app下载

创作优质视频内容不仅仅是一种艺术,也是一种科学,需要对素材的深刻理解和精心挑选。掌握了这些高清无水印视频素材,您就拥有了创作引人入胜视频内容的强大工具。以下是更多精选的视频素材网站,旨在为您的视频项目提供更广阔的视野…

Python | Leetcode Python题解之第10题正则表达式匹配

题目: 题解: class Solution:def isMatch(self, s: str, p: str) -> bool:m, n len(s), len(p)dp [False] * (n1)# 初始化dp[0] Truefor j in range(1, n1):if p[j-1] *:dp[j] dp[j-2]# 状态更新for i in range(1, m1):dp2 [False] * (n1) …

专升本--python运算符总结

运算优先级 同一个等级是没有先后顺序的,此外,赋值语言的先后问题: 赋值的顺序从上往下,同一行一般都是代表同时进行赋值,如图所示: 一.and A and B,若A,B有任意一个为假(0&#x…

希尔排序和快排里的小区间优化

希尔排序 希尔排序是插入排序的优化。 当一串数是逆序时,那么每插入一个数,前面的数都会向后面挪动。 那么这是插入排序的时间复杂度,就会达到O(n^2) 希尔排序是对数组里的数进行预排序。 防止插入排序出现最坏的情况。 预排序&#xf…

ABC318 F - Octopus

解题思路 对于每个宝藏维护个区间,答案一定在这些区间中对于每个区间的端点由小到大排序对于每个点进行判断,若当前位置合法,则该点一定为一个右端点则该点到前一个端点之间均为合法点若前一个点不合法,则一定是某一个区间限制的…

Xen Server 8 Install

Xen Sevrer 前言 XenServer(以前称为 Citrix Hypervisor)是业界领先的平台,实现了经济高效的桌面、服务器和云虚拟化基础结构。XenServer 支持任意规模或类型的组织整合计算资源,以及将计算资源转换为虚拟工作负载,从…

c++协程详解(二)

前言 这是c协程实现第二篇,这里开始我们将开始真正意义上开始实现协程。对协程基础流程不清楚的,可以看我的第一篇。 后续可能需要一定的模板知识,可以看下我的模板的文章,那些知识就完全够用了。本篇将实现一个协程封装的异步任…

Redis慢日志

SLOWLOG 是用来读取和重置 Redis 慢查询日志的命令,Redis 2.2.12 版本开始支持 1.Redis 慢查询日志概述 客户端从发送命令到获取返回结果经过了以下几个步骤: 1. 客户端发送命令 2. 该命令进入 Redis 队列排队等待执行 3. Redis 开始执行命令 - Red…

浅析JavaWeb内存马基础原理与查杀思路

文章目录 前言Java内存马内存马分类&原理JavaWeb三大组件注入Servlet内存马注入Filter型内存马JAVA Agent内存马 哥斯拉木马0x01 WebShell0x02 MemShell0x03 FilterShell0x04 Arthas排查0x05 scanner查杀 总结 前言 几年前写过《Web安全-一句话木马》,主要介绍…

PurpleKeep:提供Azure管道以创建基础设施并执行Atomic测试

关于PurpleKeep PurpleKeep是一款功能强大的安全测试自动化工具,该工具能够通过提供Azure管道以创建基础设施,并帮助广大研究人员执行Atomic测试。 随着攻击技术种类的迅速增加,以及EDR(端点检测和响应)和自定义检测规…

二叉树层序遍历 及相关题目

1,力扣102 给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:[[3],[9,20],[15,7]]示例…

Canvas背景绘制-24

本节会详细介绍下,如何绘制面板的背景。 概述 常用的技术称为图块复制(blitting),即从离屏缓冲区中将内容发生变化的那部分背景图像复制到屏幕上,还有其它两种方法是将所有内容擦除并重新绘制&仅重绘内容发生变化的那部分区域。一般是用…

网络:HTTP协议

目录 序列化与反序列化 守护进程 网络计算器的实现 HTTP协议 http的代码演示 HTTPS 初步理解三次握手,四次挥手 ①tcp是面向连接的通信协议,在通信之前,需要进行3次握手,来进行连接的建立(谁connect谁握手) ②当tcp在断开…