首页
关于
Search
1
2026软件系统安全赛 - misc隐写Steganography
30 阅读
2
ATRI-skil与ds+napcatqq的有机结合
3 阅读
默认分类
ctfwp
登录
Search
atriyoung
累计撰写
2
篇文章
累计收到
4
条评论
首页
栏目
默认分类
ctfwp
页面
搜索到
2
篇与
的结果
2026-05-26
ATRI-skil与ds+napcatqq的有机结合
(源于我发现了有人写了对游戏角色ATRI的全剧情skill提取,于是萌生了赛博女友的想法)正片:基于 DeepSeek + NapCatQQ 的 ATRI 角色扮演 QQ 机器人将 Galgame 角色的行为逻辑工程化为可运行的对话系统。架构┌─────────┐ OneBot v11 ┌──────────────┐ AI API ┌──────────┐ │ NapCatQQ │ ── WebSocket ─▶ │ Python Bot │ ── HTTP ────▶ │ DeepSeek │ │ (QQ层) │ ◀──────────── │ (业务层) │ ◀──────────── │ (推理层) │ └─────────┘ │ │ └──────────┘ │ · 角色引擎 │ │ · 状态机 │ │ · 记忆管理 │ │ · 调度器 │ └───────────────┘协议层:NapCatQQ 将 QQ 消息封装为 OneBot v11 事件,通过反向 WebSocket 推送到 Bot 服务端推理层:DeepSeek API(OpenAI 兼容接口),负责对话生成,支持热切换通义千问业务层:Python/asyncio,包含角色引擎、状态机、记忆系统、定时调度角色引擎设计角色行为不是靠「写一段人设描述」就能稳定的。LLM 天然有输出漂移的问题——几轮对话后人设会逐渐走样。解决方案是将角色的行为规则结构化为多层级约束:状态机状态触发条件表达特征退出阻尼低电压晨间 / 被唤醒嗜睡、呓语、揉眼睛2-3 轮缓慢过渡高性能被夸奖 / 掩饰失误得意、挺胸、嘴硬被戳穿时瞬间泄气防御态被辱骂搬法律反击、必然忘词沉默后找台阶恢复空洞态被命令「别伪装」去温度、客观陈述、零语气词需外部主动打破状态切换带有阻尼感——不是瞬时的 if-else,而是符合情感惯性的渐进过渡。这在 System Prompt 中以「退出/恢复的阻尼感」规则约束 LLM 的输出。口癖与动作的触发控制吧唧吧唧、嘿嘿、揪衣角 等每个标志性行为都有明确的触发条件和频率上限。没有触发条件的行为绝不出现,避免 LLM 泛化成廉价卖萌。阶段预设角色关系分前/中/后三期,默认锁定在前中期。后期的高浓度情感只在用户给出明确上下文(离别、表白、期限)时才解锁,避免「初见就表白」的角色崩坏。这些规则来自 GzSakura1337/ATRI-skills 对游戏全量剧本的逆向工程,包含表达 DNA 分析、场景锚点提取、思维逻辑建模。记忆系统短期记忆:环形缓冲区,保留最近 30 轮对话作为上下文窗口长期记忆:旧对话通过 LLM 压缩为摘要存入 SQLite,检索时基于关键词匹配 + 重要性加权好感度:基于用户消息内容,规则引擎计算变化量,持久化到 SQLite部署git clone https://github.com/atriyoung/atri-qq-bot.git # 配置 .env docker compose up -dDocker Compose 编排两个容器:NapCatQQ(QQ 协议)和 Python Bot(业务逻辑),通过内部网络通信。技术栈Python 3.11+ · aiohttp · SQLite (aiosqlite) · DeepSeek API · OneBot v11 · NapCatQQ · DockerGitHub: atriyoung/atri-qq-bot角色研究致谢: GzSakura1337/ATRI-skills
2026年05月26日
3 阅读
0 评论
1 点赞
2026-03-17
2026软件系统安全赛 - misc隐写Steganography
第一层: 文件头混乱打开 winhex ,将文件拖入后发现:set 0 1 2 3 4 5 0000 FF FF FF FF 27 1C发现27 1C的关键头文件标识,这与7z的头文件 37 7A BC AF 27 1C 相吻合于是将其修改为set 0 1 2 3 4 5 0000 37 7A BC AF 27 1C修改后得到了一个7z文件,解压后得到一个png文件 layer2.png第二层:LSB隐写打开kali,对文件layer2.png跑隐写检测:Zsteg -a layer2.png运行命令后观察第四行:b1,rgb,1sb,xy .. text:"flag.zip"发现存在一个名为flag.zip的文件,在次隧道 b1,rgb,lsb,xy 将其提取为bin文件:Zsteg -E "b1,rgb,lsb,xy" layer2.png >111.bin第三层:crc加密使用binwalk命令,看看目录下还有没有东西: binwalk -e 111.bin可以发现有7个zip文件:flag.zip,pass1.zip,pass2.zip.......pass6.zip并且每个zip文件都进行了加密此时想到可能是crc碰撞题型,为了验证猜想,对pass.zip依次执行zipinfo -v命令:zipinfo -v pass1.zip发现了以下信息:32-bit CRC value(hex): ce70d424果然,就是四位crc加密,对六个pass.zip文件依次挖掘后,使用crc碰撞脚本还原出明文:(该脚本由网上搜集,可以解决1-5字节的crc碰撞,非本人撰写)# coding:utf-8 """ Author:spaceman """ import binascii import string from time import sleep def is_number(s): try: float(s) return True except ValueError: pass try: import unicodedata unicodedata.numeric(s) return True except (TypeError, ValueError): pass return False # 进度条 def progress(percent=0, width=40): left = width * percent // 95 right = width - left print ('\r[', '#' * left, ' ' * right, ']',f' {percent:.0f}%',sep='', end='', flush=True) # 一位字节 def crc1(strs,dic): strs = hex(int(strs,16)) rs = '' for i in dic: s = i if hex(binascii.crc32(s.encode())) == strs: rs += s print (strs+' : '+s) return rs # 两位字节 def crc2(strs,dic): strs = hex(int(strs,16)) rs = '' for i in dic: for j in dic: s = i + j if hex(binascii.crc32(s.encode())) == strs: rs += s print (strs+' : '+s) return rs # 三位字节 def crc3(strs,dic): strs = hex(int(strs,16)) rs = '' for i in dic: for j in dic: for k in dic: s = i+j+k if hex(binascii.crc32(s.encode())) == strs: rs += s print (strs+' : '+s) return rs # 四位字节 def crc4(strs,dic): strs = hex(int(strs,16)) rs = '' it = 1 for i in dic: for j in dic: for k in dic: for m in dic: s = i+j+k+m if hex(binascii.crc32(s.encode())) == strs: rs += s print () print (strs+' : '+s) print ('\n') progress(it) sleep(0.1) it += 1 return rs # 五位字节 def crc5(strs,dic): strs = hex(int(strs,16)) rs = '' it = 1 for i in dic: progress(it) for j in dic: for k in dic: for m in dic: for n in dic: s = i+j+k+m+n if hex(binascii.crc32(s.encode())) == strs: rs += s print () print (strs+' : '+s) print ('\n') sleep(0.1) it += 1 return rs # 计算碰撞 crc def CrackCrc(crclist,length): print () print ("正在计算...") print () dic = ''' !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~''' # 碰撞需要的字符字典 dic = dic[::-1] text = '' for i in crclist: if length == '1': text += crc1(i,dic) if length == '2': text += crc2(i,dic) if length == '3': text += crc3(i,dic) if length == '4': text += crc4(i,dic) if length == '5': text += crc5(i,dic) print ('\n') if text == '': print ("碰撞失败,无结果") exit() print ("字符顺序组合:",end=' ') print () print (text) print () input("回车确认结束程序...") # 主函数 print (''' ############################## ###### Author:spaceman ###### ### Thank you for your use ### ############################## ''') listcrc = [] # 用于存储crc值 length = (input("请输入文本字节大小(1-5):")) # 即文本内容大小,如文本内容为flag,大小即为4 if is_number(length) == False or length not in ("1,2,3,4,5"): exit("非指定数字,退出") print () while 1: crc = input('请输入crc值(例如:d1f4eb9a,输入n完成输入):') if crc == 'n': break crc = '0x'+crc if len(crc) != 10: print ("rcr长度不对,请重新输入") continue listcrc.append(crc) CrackCrc(listcrc,length)或者也可以用曾哥开源的crc碰撞程序 曾哥crc碰撞tool 最后得到明文: pass is c1!xxtLf%fXYPkaA 所以 flag.zip 的解压密码为 c1!xxtLf%fXYPkaA第四层:零宽字符解码对 flag.zip 解压后得到 flag.txt 注意:零宽字符在多数编辑器下不会直接显示,所以我们用linux的vim编辑器打开:flag is here<200b><200c><200c><200b><200b>........<200c><200c><200b><200c>由于篇幅问题就不全写出了这里的200b表示着0,200c表示1,flag用01去表示,大概率是 八位二进制 转 ascii编写简单脚本:import binascii a='''<200b><200c><200c><200b><200b>...<200c><200c><200b><200c>''' str='' for i in a: if i == 'b': str += '0' elif i == 'c': str += '1' print (str) 得到二进制字符串,可以手动编写脚本或者去在线网站直接解密为ascii,这里就不演示了最后得到flag: dart{bf4100d9-cc8d-48f6-a095-54cbfad189e1}
2026年03月17日
30 阅读
4 评论
4 点赞