惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Secure Thoughts
P
Proofpoint News Feed
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Project Zero
Project Zero
Cyberwarzone
Cyberwarzone
K
Kaspersky official blog
AWS News Blog
AWS News Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
N
News | PayPal Newsroom
S
Schneier on Security
O
OpenAI News
S
Security @ Cisco Blogs
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
月光博客
月光博客
GbyAI
GbyAI
T
Tenable Blog
B
Blog
人人都是产品经理
人人都是产品经理
Engineering at Meta
Engineering at Meta
T
Troy Hunt's Blog
量子位
S
Security Affairs
Security Archives - TechRepublic
Security Archives - TechRepublic
The Cloudflare Blog
W
WeLiveSecurity
U
Unit 42
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
The GitHub Blog
The GitHub Blog
Cloudbric
Cloudbric
A
About on SuperTechFans
Hacker News - Newest:
Hacker News - Newest: "LLM"
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Google DeepMind News
Google DeepMind News
博客园_首页
I
Intezer
P
Proofpoint News Feed
N
News and Events Feed by Topic
SecWiki News
SecWiki News
Microsoft Security Blog
Microsoft Security Blog
TaoSecurity Blog
TaoSecurity Blog
博客园 - 三生石上(FineUI控件)
NISL@THU
NISL@THU
Latest news
Latest news
H
Help Net Security
G
Google Developers Blog
博客园 - Franky
T
The Exploit Database - CXSecurity.com
Cisco Talos Blog
Cisco Talos Blog
Know Your Adversary
Know Your Adversary

Yesterday17's Blog

2026 新年解密红包 / Melody Flag | Yesterday17's Blog 谈谈 Iori 的设计思路(二):如何实现一个 Showroom 录制工具? | Yesterday17's Blog 谈谈 Iori 的设计思路(一):从 Nico Timeshift 说起 | Yesterday17's Blog Iori Minyami 0.1.0 发布 | Yesterday17's Blog 2025 新年解密红包 / Melody Flag | Yesterday17's Blog 使用 Cloudflare Warp 解决罗森票务的海外登录问题 | Yesterday17's Blog How To Blog 04: The Astro v5 Era | Yesterday17's Blog 谈谈 tokio::select! 的公平性 | Yesterday17's Blog Learning Pingora 05 - Connect with TLS | Yesterday17's Blog Leaving Bytedance | Yesterday17's Blog 大橋彩香 AsiaTour「Reflection」上海公演 个人向记录 & Repo | Yesterday17's Blog Recoving from burnout - What happened? | Yesterday17 Yubikey 重建手册 | Yesterday17's Blog How To Blog 03: Heimus | Yesterday17's Blog 🪧 Blog Migration Accouncement | Yesterday17's Blog Learn Your IDE - VSCode 是如何仅重启插件的? | Yesterday17's Blog How To Blog 02: Astro❤️Password | Yesterday17's Blog How To Blog 01: Why, How, and the Future | Yesterday17's Blog Learning Pingora 04 - Establish L4 Connection | Yesterday17's Blog Learning Pingora 03 - Upstreams and Peers | Yesterday17's Blog Learning Pingora 02 - A Simple HTTP Server | Yesterday17's Blog Learning Pingora 01 - Getting Started | Yesterday17's Blog 2024 新年解密红包 / Melody Flag | Yesterday17's Blog 向新的一年飞驰——记录 2023 | Yesterday17's Blog 「サクラノ刻」对话选摘(2) | Yesterday17's Blog PGP Key Revocation 注销声明 | Yesterday17's Blog 「サクラノ刻」对话选摘(1) | Yesterday17's Blog 2023 新年解密红包 / Melody Flag | Yesterday17's Blog 『蒼の彼方のフォーリズム』通关感想 | Yesterday17's Blog 单显卡直通教程 | Yesterday17's Blog 对博客与笔记的思考 | Yesterday17's Blog Project Anni 之旅(3)自动化 Flutter 应用 CI/CD 上架流程 | Yesterday17's Blog AsobiStage 直接播放链接 | Yesterday17 如何在后分P时代进行投稿——sswa使用详解 | Yesterday17's Blog JSON RPC 与 LSP 协议基础 | Yesterday17's Blog Grajapa Shueisha / BookEnd 加密方式调查 | Yesterday17's Blog 【2022篇+WriteUp】如何再收一个新年红包? | Yesterday17's Blog 如何将良心云的良心功能清理干净 | Yesterday17's Blog 【油猴脚本】bilibili 投稿页面返回旧版+旧版页面强制允许分P上传 | Yesterday17's Blog Cloudr1v1 授权方式分析 | Yesterday17 Typora 1.0.2 逆向实录 | Yesterday17's Blog Project Anni 之旅(2)ValueAfterTable——toml-rs的实现与限制 | Yesterday17's Blog IPv4透明代理+IPv6 Passthrough——树莓派单臂软路由折腾记 | Yesterday17's Blog Chaos; Child 汉化补丁 神秘编码探索 | Yesterday17's Blog 镣铐与舞蹈——个性与共性之迷思 | Yesterday17's Blog Go 学习笔记 02 - 找准 io 之道 | Yesterday17's Blog NAT Slipstreaming v1 原理浅析 | Yesterday17's Blog 绕过「9-nine-」的 CDKEY 验证——KrkrPlugin 正(?)向实录 | Yesterday17's Blog 静流的青春纪念册——「サクラノ刻 -櫻の森の下を歩む-」体验版感言 | Yesterday17's Blog Project Anni 之旅 01 - 从 clap-builder 到 derive | Yesterday17's Blog [Google CTF 2021] CPP WriteUp | Yesterday17's Blog 获取 アソビステージ 的实际播放链接 | Yesterday17's Blog 90 行 Rust 代码实现 AsyncTeeReader | Yesterday17's Blog 或许还算有价值一读的文章列表 | Yesterday17's Blog 从零开始的 Seedbox 之旅 | Yesterday17's Blog [随笔]技术型博客行文迷思(1) | Yesterday17's Blog 浅谈 git fetch 的工作方式 | Yesterday17's Blog 『ソーサレス*アライヴ! ~the World's End Fallen Star~』通关感想" | Yesterday17's Blog Rust std::fmt 格式语法简述 | Yesterday17's Blog 日亚修改居住国的解决方案 | Yesterday17's Blog [Windows/Linux] GC553 的 Switch 完美采集之路 | Yesterday17's Blog 【翻译】Subtyping and Variance / 子类型与变型 | Yesterday17's Blog Berd's Red Envelope 2021 WriteUp | Yesterday17's Blog 【中英对照】ALSA 音频 API 使用教程/A Tutorial on Using the ALSA Audio API | Yesterday17's Blog 从 cue_scanner.l 看 CUE Sheet 的词法单元 | Yesterday17's Blog Postman 历史记录导出的解决方案 | Yesterday17's Blog 《恋爱绮谭 不存在的夏天》通关感想 | Yesterday17's Blog [微机实验/TD-PITE] 微机接口综合实验 | Yesterday17's Blog [微机实验/TD-PITE] 键盘扫描及数码管显示实验 | Yesterday17's Blog [微机实验/TD-PITE] 数码管显示实验 | Yesterday17's Blog Airsonic Advanced+Google Drive+Caddy 部署纪实 | Yesterday17's Blog X-NUCA 2020 - hellowasm 题解 | Yesterday17's Blog [微机实验/TD-PITE] 8251 串行接口实验 | Yesterday17's Blog Node.js child_process.fork 与 env 污染 RCE | Yesterday17's Blog EP.01 「夜の向日葵」 | Yesterday17's Blog [微机实验/TD-PITE] 8254 定时/计数器实验+选做实验 | Yesterday17's Blog [JLU CTF/2020] babywasm WriteUp | Yesterday17's Blog PHP 反序列化与经典利用 | Yesterday17's Blog WebAssembly 逆向简述 | Yesterday17's Blog 『彼女、お借りします』一期完结点评 | Yesterday17's Blog [微机实验/TD-PITE] D/A 转换实验+选做实验 | Yesterday17's Blog [微机实验/TD-PITE] A/D 转换实验+选做实验 | Yesterday17's Blog 开源项目申请 JetBrains Open Source License 简单流程 | Yesterday17's Blog 微软拼音与 JetBrains 搜索快捷键冲突的解决方案 | Yesterday17's Blog [微机实验/TD-PITE] 8259 中断优先级实验+选做实验 | Yesterday17's Blog IFTTT 测试(续) | Yesterday17 IFTTT 测试 | Yesterday17's Blog [微机实验/TD-PITE] 存储器扩展实验+选做实验 | Yesterday17's Blog 新版 GCC 针对 -fdump-translation-unit 的替代方案 | Yesterday17's Blog 一次 HSTS 策略配置的排错之旅 | Yesterday17's Blog YukiNative 踩坑记——Windows 的消息队列 | Yesterday17's Blog 我是我自己——论获取 HTTPS 证书时的验证步骤 | Yesterday17's Blog 【设计文档】对 PUG 的大规模设计修订(1.1) | Yesterday17's Blog GS65 折腾记(2)加装固态,分区,Grub2 引导 Manjaro LiveCD | Yesterday17's Blog 「さくら、もゆ。」的空白字体列表——一次逆向问题定位过程实录 | Yesterday17's Blog GSuite 探索篇(1)使用 Service Account 向 Google Drive 传输文件 | Yesterday17's Blog 『サクラノ詩 -櫻の森の上を舞う-』通关感想 | Yesterday17's Blog 《ATRI -My Dear Moments-》通关感想 | Yesterday17's Blog [工具][VSCode 扩展] AegiKit——方便 Aegisub 使用的工具箱 | Yesterday17's Blog 贝塞尔曲线、字体矢量化与曲线运算 | Yesterday17's Blog
Shinym@s 初探 04 - resource_hash | Yesterday17's Blog
Yesterday17 · 2020-04-16 · via Yesterday17's Blog

才过去一天,我又在研究 SC 的东西了(你的作业呢!!!!!!!!

嘛,我感觉我要死了,但是研究都研究了,时间也花了,没研究出什么不是很浪费,于是抱着这样的心态我开始接触 WASM。估计大部分人初次接触 WebAssembly 应该都是以各种其他语言开始的吧,然而谁让我在逆向呢(悲)。

ToC

  • WebAssembly 文本格式
  • 开始
    • 从二进制到文本文件
    • JavaScript 层入口
    • WASM 入口
  • decryptResource
    • 数据段
    • XOR 解密
  • encryptPath
    • 数据段
    • f302
    • f262
  • 结语

WebAssembly 文本格式

这部分的内容由于校赛需要被移到这里了:

WebAssembly 逆向简述

下面就直接开始了(

开始

从二进制到文本文件

二进制显然是看不了的,这里我们就需要用到 wasmwat 的工具了。我这里使用的是 [wasm2wat](https://webassembly.github.io/wabt/doc/wasm2wat.1.html)

输入命令:

./wasm2wat ./resource_hash.wasm > resource_hash.wat

我们就得到了逆向所需要的基本文件。

JavaScript 层入口

一切都需要有个入口,而 WebAssembly 的逆向又需要分为 JS 层面和 WebAssembly 层面的。JS 层面的入口我选择了虾泥助手对应的函数,你也可以自行选择。

选择入口的目的是打断点。虽然 ChromeWASM 调试还有些问题,但动态分析还是能很大程度上帮助我们了解函数执行的全貌(帮助我们更好地猜出实现)。

WASM 入口

WASM 的入口位于 f289,也就是 (;289;)。这个函数通过相对调用的方式调配了 decryptResourceencryptPath 两个函数的执行。真正的入口其实是:

  • decryptResourcef304
  • encryptPathf302

这里就用到查表了,我们虽然不一定要完全弄清楚 Table,但使用还是要会的(

decryptResource

既然是调用的 f304,那我们直接来看:

(func (;304;) (type 4) (param i32 i32 i32) (result i32) ;; decryptResource called

(local i32)

(;

arg#0, arg#1, arg#2, local#3

;)

global.get 6 ;; global#6

local.set 3 ;; local#3 = global$6

;; save current global$6

global.get 6

i32.const 16

i32.add

global.set 6 ;; global#6 += 10000b

;; (the following 4 lines describe the stack after i32.const 3)

local.get 3 ;; local#0 // fn

local.get 1 ;; local#2 // byteLength

local.get 2 ;; local#1 // toDecrypt

local.get 0 ;; local#3 // local#3(global$6 + 10000b)

i32.const 3

i32.and ;; local$0 -> local$0 & 011b

i32.const 453

i32.add ;; p0 -> p0 & 011b + 111000101b

call_indirect (type 2) ;; type2: (param i32 i32 i32) with no return

;; result: 454, jump to 596

local.get 3

call 165

local.set 0

local.get 3

call 31

local.get 3

global.set 6 ;; restore global$6

local.get 0)

可以看到,这里仍然没有对数据作什么太多的操作,我们跟着进 f596

(func (;596;) (type 2) (param i32(;local#0;) i32(;local#1 toDecrypt;) i32(;local#2 byteLength;)) ;; decryptResource, called by f304

(local i32 (;local#3;))

global.get 6

local.set 3 ;; save current global#6

global.get 6

i32.const 16

i32.add

global.set 6 ;; global$6 += 16(4 bytes)

local.get 3

local.get 1

i32.store ;; store local#1(toDecrypt) to [local#3]

local.get 3

local.get 2

i32.store offset=4 ;; store local#2(byteLength) to [local#3+4]

local.get 0 ;; current local#0(original global#6)

local.get 3

i32.const 21514

call 300 ;; call f300(local#3, 21514) ;; might be part of decrypt

local.tee 0 ;; save _malloc addr to local#0

i32.load ;; load (second malloc addr)

local.get 0

i32.load offset=4 ;; load byteLength

call 299 ;; f299(local#0, second malloc addr, byteLength)

local.get 0

i32.load

call 33 ;; _free

local.get 0

call 33 ;; _free

local.get 3 ;; recover global#6

global.set 6)

这里就有点意思了,出现了一个有点意思的未知数字:21514。这里我们还不知道它是什么,去看 f300

;; 前半

(func (;300;) (type 1) (param i32 i32) (result i32) ;; called by decryptResource-2

(local i32 i32 i32 i32 i32 i32 i32) ;; 7 local variables

i32.const 8

call 47 ;; f47 is _malloc, _malloc(8)

local.tee 3 ;; tee_local: like set_local, but also returns the set value

;; save _malloc return value to local#3, also keep it in stack

i32.const 0

i32.store ;; clear front 4 bytes of _malloced space

local.get 3

i32.const 4

i32.add

local.tee 6 ;; save mid addr of _malloced space to local#6

i32.const 0

i32.store ;; clear latter 4 bytes of _malloced space

local.get 0

i32.load ;; load one byte from local#0(local#3 of the caller) ->

;; local#0: |------------|

;; -> | toDecrypt |

;; | byteLength |

;; |------------|

i32.eqz

if ;; label = @1

local.get 3 ;; return malloced space directly, which should be zero

return

end

local.get 0

i32.load offset=4 ;; load byteLength

local.set 4 ;; save byteLength to local#4

local.get 1 ;; 21514

call 60

local.set 7 ;; save return value to local#7 ;; strlen(HEADER)

local.get 4 ;; get byteLength

i32.const 1

i32.add

local.tee 2 ;; save byteLength+1 to local#2

call 47 ;; _malloc(byteLength+1)

local.tee 5 ;; save return value to local#5

i32.const 0

local.get 2

call 84 ;; f84(byteLength + 1, 0, __malloc_space__) // looks like memset // TODO

drop ;; explictly pop value from stack, ignore the return value

local.get 0

i32.load ;; load [local#0] ;; toDecrypt

local.set 8 ;; local#8 = [local#0] ;; toDecrypt

这里出现了超级敏感的操作:分配内存,而且还做了两次,一次是分配了两个 i32 的大小,而另一次则是和我们的输入有关的,byteLength + 1 大小。然而到这里,当时的我还是一无所知,不过看下面:

;; 中段

i32.const 0

local.set 2 ;; local#2 = 0

i32.const 0

local.set 0 ;; local#0 = 0

loop ;; label = @1 ;; for (int local#0=0;local#0 != local#4;local#0++)

local.get 0

local.get 4

i32.ne

if ;; label = @2 ;; if local#0 != local#4 ;; byteLength;;--------------------------------------------------------------------------

;; for (int i=0;i != byteLength;i++)

;; local#0: i

;; local#1: 21514, addr of HEADER string

;; local#4: byteLength

;; local#5: addr of _malloc(byteLength + 1)

;; local#7: length of HEADER string

;; local#8: toDecrypt

local.get 5

local.get 0

i32.add ;; local#5 + local#0(*) ;; malloc_addr + offset

local.get 8

local.get 0

i32.add

i32.load8_s ;; load a byte from [local#8+local#0](*) ;; toDecrypt[offset]

local.get 1 ;; local#1(*) ;; 21514

i32.const 0

local.get 2

local.get 2

local.get 7

i32.eq ;; local#2 == local#7 ? 0 : local#2 ;; local#2 = local2 % local#7 ;; j %= strlen(HEADER);

select

local.tee 2 ;; save to local#2 and keep in stack

i32.add ;; result local#1

i32.load8_s ;; load [result + local#1] ;; HEADER[local#2]

i32.xor ;; (local#8 + local#0) ^ [result + local#1] ;; toDecrypt[local#0] ^ HEADER[local#2]

i32.store8 ;; store a byte to [local#5+local#0] ;; toDecrypt[offset] ;; malloced[i] = toecrypt[i] ^ HEADER[j];

local.get 2

i32.const 1

i32.add

local.set 2 ;; local#2++ ;; j++;

local.get 0 ;; ----------------------------------------------------------------------------------------------------------

i32.const 1

i32.add

local.set 0 ;; local#0++

br 1 (;@1;) ;; goto @1

end

end

这里出现了一个循环和一个 xor。这已经是很明显的特征了,然而当时的我并没有领悟,懵懵懂懂地看了过去,直到机缘巧合下返回来看……

我们看到,这个循环是对整个输入数组进行的,而循环的过程中对另一个量(实际上是数组,不过我们假装不知道)进行的循环的偏移访问,由此,我们可以推断:另一个量也是一个数组,并且存储的很有可能就是 XOR 的 key。

这时候就要问一个问题了:21514 究竟是什么?

数据段

仍然是 MDN 告诉我们:数据段允许字符串字节在实例化时被写在一个指定的偏移量。而且,它与原生的可执行格式中的数据(.data)段是类似的。而 21514 在执行过程中不断被当作地址使用,我们很容易推断:这是内存空间的一个地址。

于是我们来看 resource_hash.wasm 定义的 data

纵向全貌
纵向全貌

顺藤摸瓜,我们看到了熟悉的东西:

眼熟吧(
眼熟吧(

这个 WL[ 基本可以算是刻在 DNA 里的特征了,而且正好这个字符串开头就是对应的 21514,于是此时我们大胆推断:call 60 得到的结果就是字符串的长度。

想要验证这个推断是否正确,可以使用静态分析或者动态分析,这里我直接用了 Chrome 的调试工具,最终证明推断是正确的。但在推断之前,我也静态分析了很长一段时间,因此很难说到底是谁的功劳,应该是二者都有吧,没有静态分析我就不会作出这样的假设,而没有动态分析,估计这时候我还没把第一个函数推出来不是(

XOR 解密

之后就是随手搓一个 XOR 的时间了,这里给出一个 xjb 写的 C 程序:

#include

#define KEY_LEN 54

char key[KEY_LEN] = "B'KYWL[DI\\vqUIyw_we_are_hiring_https://knocknote.co.jp";

int main() {

FILE* fi = fopen("test.in", "rb");

FILE* fo = fopen("test.out", "wb");

char c;

int offset = 0;

do {

c = fgetc(fi);

fputc(c ^ key[offset], fo);

offset = (offset + 1) % KEY_LEN;

} while (!feof(fi));

fclose(fi);

fclose(fo);

}

然后随便下一个资源下来,我用的是 asset-map-aac011a61415a220560587aaf0177b2d98e8c7897d7697db4dd51b509162040d,执行,binwalk,然后我就傻了:

再一看 Dolphin

连图标都变了
连图标都变了

ArkKate打开:

好嘛,游戏结束了
好嘛,游戏结束了
wtcl
wtcl

于是 decryptResource 成果解密(我之前怎么就没想到是 gzip 的魔数呢

encryptPath

数据段

有了上一个函数的经验,这里我们首先看的就是 data 段:

果不其然,甚至和刚才那段是同一行,还在那段的前面,出现了我们想要的信息:picosha2。于是我们推断:最终结果是某一个值的 sha256 结果。

(其实也没那么容易作出判断,这里我是和 picosha2 的源码作比对之后确认了 sha256 计算在最后才得出的推论,但今后其实可以大胆一点,毕竟推错了也没什么太大影响,但一旦对了就能节省不少时间(

f302

同样是从入口开始,这次是 f302

(func (;302;) (type 4) (param i32(;2;) i32(;path;) i32(;tail;)) (result i32)

(local i32 i32)

global.get 6

local.set 3

global.get 6

i32.const 48

i32.add

global.set 6 ;; global#6 += 48

local.get 3

i32.const 12

i32.add

local.tee 4 ;; local#4 = global#6(original) + 12

local.get 1 ;; path

call 164 ;; f164(global#6_original + 12, path)

local.get 3

local.get 2

call 164 ;; f164(global#6_original, tail)

local.get 3

i32.const 24

i32.add

local.tee 1 ;; local#1 = global#6_original + 24, keep in stack* ;; +24

local.get 4 ;; local#4* ;; after_path +12

local.get 3 ;; global#6_original* ;; after_tail +0

local.get 0

i32.const 3

i32.and

i32.const 453

i32.add ;; 2 & 3 + 453

call_indirect (type 2) ;; 455, jump to f262

local.get 1

call 165

local.set 0

local.get 1 ;; _frees

call 31

local.get 3

call 31

local.get 4

call 31

local.get 3

global.set 6

local.get 0)

可以看到,这里经过了两次 f164,不知道干了什么,但这里我当时选择了先跳过,于是我们来看 f262(并且后来证明 f164 并不大影响理解(

f262

不得不说的是,这部分的解密带有运气的色彩

(func (;262;) (type 2) (param i32 i32 i32)

(local i32 i32 i32 i32)

global.get 6

local.set 3

global.get 6

i32.const 32

i32.add

global.set 6 ;; local#6 = global#6 + 32

local.get 2

i32.load8_s offset=11

i32.const 0

i32.lt_s

if ;; label = @1

local.get 2

i32.load

local.set 2

end

local.get 3

i32.const 20

i32.add

local.set 5

local.get 3

i64.const 0

i64.store

local.get 2

i32.load8_s

local.set 4 ;; save ascii of first character of tail to local#4

local.get 2

local.get 2

call 60 ;;strlen(tail)

i32.const -1

i32.add ;; strlen - 1

i32.add ;; local#2 + strlen - 1 (tail)

i32.load8_s ;; ascii of last character of tail

local.set 6 ;; save to local#6

local.get 3

i32.const 8

i32.add

local.tee 2 ;; local#2 = #global(ori) + 8

local.get 4

i32.store ;; [local#2] = local#4(first character)

local.get 2

local.get 6

i32.store offset=4 ;; [local#2+4] = local#6(last character)

local.get 3 ;; global_original

i32.const 8 ;; 8

i32.const 21154 ;; %c%c

local.get 2 ;; global_original + 8

这里最大的发现是它截取了 tail(也就是第二个参数)的首尾字母,并且下面的 21154 的值恰好就是 %c%c,于是猜测这两个字母是连续的。测试了一下随便更换 tail 的中间字母,发现也没有任何变化,基本上可以证实了:

完 全 一 致
完 全 一 致
函数本体
函数本体

接下来就是这两个字母到底是怎么放的问题了,于是我又开始猜测了:是不是开头/结尾?果不其然,结果是开头:

这是我万万没想到的
这是我万万没想到的
不过总算是结束了(
不过总算是结束了(

就此,整个 resource_hash.wasm 宣告解密完毕。

结语

这次的逆向过程可以说是我第一次认真对汇编级别的源码进行分析的过程。中间有试过用 wasm2c 转成 C 再用 gcc 编译再导入 IDA,但效果并不尽如人意(还是我不大会 x86 汇编的锅)。相比之下,WASM 本身比 x86 的指令简单了 114514 倍,强行转过去不是自己给自己增加难度吗(

一晚上就这样过去了,不过总算是有结果了,也算是不辜负我这一晚上的爆肝吧(