ins可以看到别人几分钟前在线吗,BUUCTF Misc Page2-6部分题目 (2024)

执行hsql :

select user_id, prod_id, size(sw_prod) as item_cnt

from i_order_prod_log lateral view explode(sw_prod) prodTable as prod_id;

发生下面的错误:

java.lang.RuntimeException: org.apache.hadoop.hive.ql.metadata.HiveException: Hive Runtime Error while processing row {"user_id":"33333333333333309035","plat_user_id":"","order_id":"","order_no":"","prod_no":"","prod_name":"","prod_price":null,"prod_cnt":null,"orig_prod_type":null,"prod_type_name":"","order_price":null,"camp_id":null,"oper_time":"1970-01-01 08:00:00","coll_type":null,"ip":"","page_url":"","refer_url":"","prod_type":null,"sw_prod":["10605"],"dstc_id":null,"domain_name":"","refer_domain_name":"","day_id":"20120903","plat_type":"3","inf_id":"305"}

at org.apache.hadoop.hive.ql.exec.ExecMapper.map(ExecMapper.java:161)

at org.apache.hadoop.mapred.MapRunner.run(MapRunner.java:50)

at org.apache.hadoop.mapred.MapTask.runOldMapper(MapTask.java:436)

at org.apache.hadoop.mapred.MapTask.run(MapTask.java:372)

at org.apache.hadoop.mapred.Child$4.run(Child.java:255)

at java.security.AccessController.doPrivileged(Native Method)

at javax.security.auth.Subject.doAs(Subject.java:396)

at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1121)

at org.apache.hadoop.mapred.Child.main(Child.java:249)

Caused by: org.apache.hadoop.hive.ql.metadata.HiveException: Hive Runtime Error while processing row {"user_id":"33333333333333309035","plat_user_id":"","order_id":"","order_no":"","prod_no":"","prod_name":"","prod_price":null,"prod_cnt":null,"orig_prod_type":null,"prod_type_name":"","order_price":null,"camp_id":null,"oper_time":"1970-01-01 08:00:00","coll_type":null,"ip":"","page_url":"","refer_url":"","prod_type":null,"sw_prod":["10605"],"dstc_id":null,"domain_name":"","refer_domain_name":"","day_id":"20120903","plat_type":"3","inf_id":"305"}

at org.apache.hadoop.hive.ql.exec.MapOperator.process(MapOperator.java:548)

at org.apache.hadoop.hive.ql.exec.ExecMapper.map(ExecMapper.java:143)

... 8 more

Caused by: java.lang.IllegalArgumentException: Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]

at java.sql.Timestamp.valueOf(Timestamp.java:185)

at org.apache.hadoop.hive.serde2.lazy.LazyTimestamp.init(LazyTimestamp.java:74)

at org.apache.hadoop.hive.serde2.columnar.ColumnarStructBase$FieldInfo.uncheckedGetField(ColumnarStructBase.java:109)

at org.apache.hadoop.hive.serde2.columnar.ColumnarStructBase.getFieldsAsList(ColumnarStructBase.java:231)

at org.apache.hadoop.hive.serde2.objectinspector.ColumnarStructObjectInspector.getStructFieldsDataAsList(ColumnarStructObjectInspector.java:168)

at org.apache.hadoop.hive.serde2.objectinspector.UnionStructObjectInspector.getStructFieldsDataAsList(UnionStructObjectInspector.java:148)

at org.apache.hadoop.hive.ql.exec.LateralViewJoinOperator.processOp(LateralViewJoinOperator.java:128)

at org.apache.hadoop.hive.ql.exec.Operator.process(Operator.java:471)

at org.apache.hadoop.hive.ql.exec.Operator.forward(Operator.java:762)

at org.apache.hadoop.hive.ql.exec.SelectOperator.processOp(SelectOperator.java:70)

at org.apache.hadoop.hive.ql.exec.Operator.process(Operator.java:471)

at org.apache.hadoop.hive.ql.exec.Operator.forward(Operator.java:762)

at org.apache.hadoop.hive.ql.exec.LateralViewForwardOperator.processOp(LateralViewForwardOperator.java:37)

at org.apache.hadoop.hive.ql.exec.Operator.process(Operator.java:471)

at org.apache.hadoop.hive.ql.exec.Operator.forward(Operator.java:762)

at org.apache.hadoop.hive.ql.exec.TableScanOperator.processOp(TableScanOperator.java:83)

at org.apache.hadoop.hive.ql.exec.Operator.process(Operator.java:471)

at org.apache.hadoop.hive.ql.exec.Operator.forward(Operator.java:762)

at org.apache.hadoop.hive.ql.exec.MapOperator.process(MapOperator.java:531)

... 9 more

分析原因:

看似是时间戳问题,但该hsql中根本不涉及到时间戳字段;生产环境测试没有问题,测试环境不行。后查配置发现有多一个配置项

?

? hive.input.format

? org.apache.hadoop.hive.ql.io.HiveInputFormat

注释掉可成功运行。

Page 2

1.被劫持的神秘礼物-流量分析、字符查找

打开后文件有2种数据流,HTTP和TCP,两个都追踪一下

这是TCP,好像没什么东西

再看看HTTP流;查看密码既可以随便打开一个HTTP流,也可以整个追踪,

得到用户名和密码(name和word),找个网站弄一下,得到flag

2.刷新过的图片-F5隐写

解压之后得到了一张奇怪的图片,用stegsolve和010查看后没什么不对的地方,

根据题目描述,刷新键既F5键能有什么神奇的作用吗?不是还有F5隐写吗?我去百度了一下,(28条消息) 【隐写术】F5隐写_m0_46296905的博客-CSDN博客_f5隐写

然后学习了一下F5工具如何使用。

在kali中失败了很多次之后,终于提取出了一个奇怪的txt文件

一个经验就是在kali中所有的文件夹命名不要用汉字,因为在终端里你无法输入汉字。

直接打开txt看不出什么东西,用010查看一下,应该是个zip文件。

修改文件后缀,解压一下,结果还有个伪加密

好办,修改一下deFlags

得到flag

3.snake

得到一张绿色的小蛇的图片,这条蛇长得实在不咋地

用010看出了很多蹊跷,不仅有几个网站,还隐含着一个压缩包

用Kali分离一下,得到几个文件

在这个key文件中,

解密后得到

这是什么意思呢?百度一下这个人名,查一下她的歌,

经过一个个地查词之后,找到了这个Anaconda的意思非常接近

然后我也不清楚该怎么做了,提交这个单词也不对,欸,不是还有一个文件嘛,借鉴一下他人的经验,解密一下,单词中的A不要大写,要改成小写。

下载二进制文件后得到flag,直接用010看,不用管文件格式

4.认真你就输了–乱找

得到一个外形是excel但本质却是zip的文件,修改一下后缀,

看这个名字就知道不能把这个题想复杂了

解压zip文件后,在几个文件夹中翻一翻,不小心找到了flag,果然不能认真做。

5.藏藏藏–jpg隐写

解压之后得到一个txt和图片,提示flag是5个字符,

010检查图片发现有隐藏的压缩包,

但是Kali的binwalk分离后没有东西,这就奇怪了,flag藏在哪里呢?

但是当我换了foremost分离出来了,?,foremost果然比binwalk好用

在output文件的docx文件中有一个二维码,扫描后得到flag,但是这个格式和txt里的也不一样啊,迷惑我

6.被偷走的文件–流量分析

经过一番仔细查看,在这个FTP一条里看到了flag字符,追踪一下

另存为一个未知格式文件就行,用010看一下是个什么东西,

好像不行,会不会隐藏着什么文件呢?用Kali的foremost分离一下原来的流量文件(桌面的output文件必须为空,追踪流得到的东西提示我们flag在一个rar文件里头,而不是让我们分析得到的未知格式文件),

得到一个加密的rar

密码是什么格式呢?

用archpr暴力破解一下试试,多试几次总能试出来的

得到密码解压,得到flag

7.菜刀–流量分析

这个流量包有HTTP和TCP两种协议

追踪HTTP流发现,

这张图片里的信息我试了好几次也没找到,先借用一下网上的

得到一张图片,Th1s_1s_p4sswd_!!!

这个应该就是压缩包的密码了,解密用foremost 分离出来的zip文件,

得到flag

8.佛系青年–ZIP伪加密

有一个加密txt,密码在哪呢?

图片没有什么密码信息,会不会是伪加密呢?一看,还真有

打开txt后,是一串符号和佛经,得到flag

9.你猜我是个啥

给了一个不是zip的zip文件,文件头是png,修改后缀得到一个二维码图片,扫一下试试,

那flag应该藏在哪呢?

会不会是图片隐写呢?回头看看010,在最后面发现flag

10.梅花香之苦寒来

010查看得到的图片,在文件尾FFD9后面发现了一长长串奇怪的未知字符,

将这些16进制字符转换成ASCII码试试,(删去上面的,保留下面的,实在是太长了,不好复制),16進製到ASCII字符串在線轉換工具 - Coding.Tools

这些坐标代表什么呢?

查阅一下,又发现了kali上一个新的工具,gnuplot绘图工具绘制出图片,这个工具识别格式没有括号和逗号,给修改一下,用记事本里的替换,(复制文本中的括号和逗号,自己打出来的会找不到)

这个命令Kali竟然没有,又要自己安装了,去网上搜一下,结合这篇回答,得到一张二维码

(28条消息) gnuplot安装 - CSDN

扫描得到flag

11秘密文件

题目描述和被偷走的文件差不多,

用foremost分离一下原文件,

得到一个rar并且有密码

暴力破解得到密码,解压得到flag

12神奇的二维码-音频+图片+zip

010看一下

binwalk分离得到几个文件(foremost没分离出来)

在这个encode.txt文件中看到了一串base64编码,转换后得到了某个压缩包的密码(我猜的),试了一下,是看看flag在不在里面这个压缩包的密码。

asdfghjkl1234567890

但是这个压缩包里没有什么东西,只有一张图片

还有一个doc文件,打开之后全是base64编码

解码之后还是一串base64编码,那就奇怪了

搜了一下,原来这是多次编码,人家用python脚本做的,我很菜,不会写脚本,那就借鉴一下吧

最后跑出来了这个

comEON_YOuAreSOSoS0great

解压后得到一个音频,应该是摩斯密码吧

写完以后得到这个

– — .-. … . … … …- . .-. -.-- …- . .-. -.-- . .- … -.–

去转换一下,

这个应该是flag吧,试一下,正确。

flag{morseisveryveryeasy}

13just_a_rar

暴力破解一下

解压

这张图片还挺好看

010瞅一瞅

得到flag,藏得好隐蔽

flag{Wadf_123}

14一叶障目

这是个啥

这种情况还是第一次遇见,去查了一下,修改宽高应该就可以,宽是第二行的2,3列,高是第二行的6,7列

尝试多次后,得到一个模糊的flag,貌似是5个6

这种方法实在是很麻烦,不如去搜一个脚本试试看。(虽然他说有错误,但是能跑出来就行)

得到一张图片,好神奇,这个flag多清晰啊,前面那个方法太low了

15鸡你太美-GIF

得到一个篮球.gif和篮球副本.gif,010比较一下发现副本没有文件头,插入文件头后得到flag

flag{zhi-yin-you-are-beautiful}

嘿,这个flag不对哦,正确的应该把“-”换成“_",又是陷阱。。。

这个flag倒是和题目名称有点联系

16穿越时空的思念

很明显这是一个音频隐写题目,我们用audacity打开看一下频谱,注意空格,这么好听的一个曲子被加上了这个,太可惜了。。。我去听一下原版

废了半天劲写出来摩斯电码是这个样子的:…-. ----- …— ----. -… -… -… …-. … … .---- .---- …-- ----. . . -… . -… —… . …- … .- .---- --… … -… ----- --… —… -…

这个地方摩斯密码有两段,后面那段和前面的一部分是重复的,我试了一下没有后面这段才是对的

然后翻译一下得到了F029BD6F551139EEDEB8E45A175B0786,提交上去却不对,为什么呢?注意题目要求,flag是小写的:flag{f029bd6f551139eedeb8e45a175b0786}

可以用python干这件事,当然,也可以用C语言,自己转换容易出错

例如,用python大概是这么写的:

#encoding:UTF-8

msg = 'F029BD6F551139EEDEB8E45A175B0786'

print(msg.lower() )

而用C语言就比较麻烦啦,就不用了吧。

17[BJDCTF2020]纳尼

是一个无法打开的gif,那么他可能缺少了文件头啥的,先看看再说

这个样子试试行不行

果然可以打开了,用stegsolve打开,一共有4帧,连起来应该就是flag了吧:

Q1RGe3dhbmdfYmFvX3FpYW5nX2lzX3NhZH0=

瞅着个东西的样子应该不是flag,试了一下果然不是,那应该就是base64编码了

OK得到flag:flag{wang_bao_qiang_is_sad}

18[ACTF新生赛

这个jpg没有文件头和文件尾,加上一个试试

加上好像也不行,

flag.txt里说猜flag在哪里,只有一张可爱的猫猫图片了,看一下有什么信息

这是个什么?难道是flag?不可能这么简单,而且这是汉字,排除,那这是用来干什么的呢?

在CTF那个工具网站里有核心价值观解码,那这个abc应该就是什么东西的密码了

CTF在线工具-在线核心价值观编码|核心价值观编码算法|Core Values Encoder (hiencode.com)

再回头观察一下题目,outguess,这是Kali上的一个工具,百度一下outguess的用法,Linux下安装和使用steghide、outguess - 简书 (jianshu.com)

把图片搞到kali上之后,利用上面揭秘出来的abc继续操作,得到一个flag.txt文件夹

注意图片在哪里就要在哪里打开终端

flag{gue33_Gu3Ss!2020}

19[SWPU2019]我有一只马里奥

这道题目貌似无法采用上一道题的方法了,那该怎么处理呢?

直接运行程序后,出来了这个:

然后打开这个1.txt,是这个:

这是啥?ntfs肯定不会是flag,百度一下

这个需要用notepad打开,

这是成功的页面,需要在notepad++安装路径文件夹中打开powershell,然后还要注意输入形式:.\notepad++ 1.txt:flag.txt

这行命令的意思参考上张图片,

然而,我也遇到了很多困难,例如,我不知道哪天把powershell的path变量给删了,导致出现了这个:

在path系统变量中添加:

C:\Windows\System32\WindowsPowerShell\v1.0;

就搞好了,

又例如,我打开powershell路径不太对,

困难重重啊,最后终于得到flag:flag{ddg_is_cute},OK

20谁赢了比赛?

解压之后打开是一张图片,

这里面有什么玄机吗?

先用010看,在最后看到了这个:

按照之前的经验,试试这张图片里是否有其他文件,

欸,还真有。

不过这也太多文件夹了吧,我一个个的找太费劲了,并且,全都是空的!(不要问我怎么知道的)

然后我不死心,又用binwalk分离了一下:

得到了一个非空的压缩包!

但是还需要密码,

先看一下是不是伪加密,先复习一下伪加密判断方法:

啊,这好像是zip的。。。

rar伪加密判断方法在这里:

很明显,这个rar是加密的

并且,在最后还有这个

总感觉他在侮辱我,但我找不到证据。。。

不管了,先修改一下试试,

出错了,那应该不是伪加密,快改回去

只能用暴力破解了,一般来说,暴力破解的密码都比较简单

事实证明确实如此。

这是一个GIF文件,那应该用stegsolve吧

一共有360帧,在第310帧发现了flag

flag{do_you_know_where_is_the_flag}

但是为什么不对呢?

原来我忘记了stegsolve一帧保存下来的东西了。。。

保存下来后,调整一下通道,出来了一张二维码

OK,扫描得到flag.

flag{shanxiajingwu_won_the_game}

21.[HBNIS2018]excel破解

打开后是一个加密的表格文件,这还没遇到过这种题目,

先用010打开看看吧,先按最原始的方法查找一下flag,真棒,一找就找到了

22.[HBNIS2018]来题中等的吧

这是个啥?

好像也没有什么东西,不是LSB隐写,也没有藏什么文件,那应该怎么处理呢?

再仔细观察一下图片,诶,这有点像频谱图啊,先写下来试试

.- .-… .–. … .- .-… .- -…

去翻译一下

还真有点东西,这应该就是flag了吧,试了一下,正确。

flag{alphalab}

23[GXYCTF2019]gakki

开图雷击

010看一看,应该是藏着rar,这好办,

搞出来一个加密的文件,应该不是伪加密,暴力破解吧

打开之后是这样的

这个我好像在哪里看到过,找了一下,好像不一样,接下来该怎么办呢? 看了大佬们的wp才知道这题是要记字频统计,出这道题的,好厉害, 借鉴一下网上的脚本

alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_+- =\{\}[]"

strings = open('E:\\常用\\日常MISC\\P2--BUU\\23wolaopo.jpg.extracted/flag.txt').read()

result = {}

for i in alphabet:

counts = strings.count(i)

i = '{0}'.format(i)

result[i] = counts

res = sorted(result.items(), key=lambda item: item[1], reverse=True)

for data in res:

print(data)

for i in res:

flag = str(i[0])

print(flag[0], end="")

运行出来这个:

得到flag{gaki_IsMyw1fe}

24[WUSTCTF2020]find_me

看样子应该又是一道图片隐写

010看着倒也还正常

但是备注有这个,好像是盲文

去翻译一下

????????????????????????????????????=

flag{y

0u_f

1

1

1n

d

d

d_M

e

e

ee

e

e

ee$e}

25[ACTF新生赛2020]base64隐写

打开之后是一张二维码和一串base64字符

二维码应该是某个公众号吧,试试看

不对,应该用手机,是一个叫做“中南极光网安实验室”的公众号,

再看那一个txt文件,

错误思路

I2luY2x1ZGU8aW9zdHJlYW0+Cg==

这似乎仍然是base64编码,再解一次

是一条语句,这是什么意思呢?发送到公众号试试,不过好像没反应。

那该怎么办呢?去百度了一下其他人的做法,原来,我方向错了,那些字符串并不是只有一个,他有好多个,不是直接解码,而是通过python脚本跑出来,但是这个hint指的公众号不是就没用了嘛。奇怪,

base64python脚本是这个样子的:

import base64

def get_base64_diff_value(s1, s2):

base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

res = 0

for i in range(len(s2)):

if s1[i] != s2[i]:

return abs(base64chars.index(s1[i]) - base64chars.index(s2[i]))

return res

def solve_stego():

with open('ComeOn!.txt') as f:

file_lines = f.readlines()

bin_str = ''

for line in file_lines:

steg_line = line.replace('\n', '')

# norm_line = line.replace('\n', '').decode('base64').encode('base64').replace('\n','')

norm_line = line.replace('\n', '').encode('utf-8')

norm_line = base64.b64decode(norm_line).decode('utf-8')

norm_line = base64.b64encode(norm_line.encode('utf-8')).decode('utf-8')

norm_line = norm_line.replace('\n', '')

diff = get_base64_diff_value(steg_line, norm_line)

print(diff)

pads_num = steg_line.count('=')

if diff:

bin_str += bin(diff)[2:].zfill(pads_num * 2)

else:

bin_str += '0' * pads_num * 2

print(goflag(bin_str))

def goflag(bin_str):

res_str = ''

for i in range(0, len(bin_str), 8):

res_str += chr(int(bin_str[i:i + 8], 2))

return res_str

if __name__ == '__main__':

solve_stego()

注意

txt文件一定要与脚本文件在同一目录下,一些小错误自己可以尝试根据提示修改。

flag{6aseb4_f33!}

26[SWPU2019]伟大的侦探

密码txt什么都没有,并且显示密码是编码,010看一下

010一共有12种常见编码,比较常用的是这几种

多试几次,在EBCDIC这个编码方式下看到了一些可读字符

wllm_is_the_best_team!

解压出来是一些小人图,有意思

这些小人代表什么呢?查了一查,原来是一种密码

福尔摩斯小人密码

(2条消息) CTF密码学常见加解密总结_攀登-CSDN博客_ctf密码学

翻译一下,就是 iloveholmesandwllm

flag{iloveholmesandwllm}

27[GUET-CTF2019]KO

是一个txt文件

这可真巧,刚在上一道题的一篇文章里看到过这种密码,找个网站搞一下

Ook!密码

[脑筋急转弯/哎呀!模糊处理/编码 splitbrain.org]

flag{welcome to CTF}

28黑客帝国

又是一个txt文件,我还以为是流量分析题呢

这个开头很熟悉啊,

原来是RAR的文件头,弄到010里看看

果然是rar文件

另存为一个文件看看,好像不行,那就换个方法,借个脚本吧

16进制存为文件

import binascii

hex_data='这里填十六进制数据'

out=open('res.rar','wb')

out.write(binascii.unhexlify(hex_data))

out.close()

搞出来一个加密的rar,破解一下密码

好不容易打开后,又说这个图片有问题

原来是少了个文件头与文件尾不对应

头是png,尾是jpg,

那应该改哪个?都试试

似乎不行,那就改成jpg

注意,要把原来png的文件头89504E47全部替换或删除,并且把文件后缀改成jpg

修改之后,图片正常打开

flag{57cd4cfd4e07505b98048ca106132125}

29[MRCTF2020]你能看懂音符吗

29[MRCTF2020]你能看懂音符吗

和上一道题目差不多的套路,文件头顺序颠倒了

改一下,解压打开,是一个word文档

啊这,都是套路,还是用010看吧

这个开头的PK不太正常吧,可能是个zip

修改后缀后解压,在word中的document.xml中发现了一串音符,

解密后得到

文本加密为音乐符号,可自设密码|文本在线加密解密工具 (qqxiuzi.cn)

flag{thEse_n0tes_ArE_am@zing~}

31[MRCTF2020]ezmisc

010打开后,提示

CRC校验码错误,那应该是宽高被修改了吧

修改之后就看到了

flag{1ts_vEryyyyyy_ez!}

32.[SWPU2019]你有没有好好看网课?

得到两个加密zip,flag3和flag2

flag3

flag2

1.暴破flag3的密码,

一下就出来了

flag2的密码不知道是不是也是这个格式,先试一下

试了好像并不是

flag3解压出一个视频和word文档

文档中的520711是干什么的呢?难不成是flag2的密码?试了一下,并不是,先留着

这个视频有什么东西呢?用一下potplayer(kinovea有点问题,打不开)看看

2.potplayer逐帧查看

potplayer如何调整时间间隔,百度一下步骤

1.直接点击鼠标右键,选择“播放”-“定位”,取消“以关键帧定位”。

2.右键找到定位,选择“时间跨度设置”。

3.将调解的大小设置合适的时长,取消下侧的框选。

4.还要注意定位的快捷键是使用“D/F”。

… …/… ./… ./… …/

敲击码

CTF之常见编码(三) - ziwang - 博客园 (cnblogs.com)

对应下来就是

WLLM

继续看,7.36秒发现

Base64

dXBfdXBfdXA=

up_up_up

这俩连起来应该就是前面压缩包的密码了(但是需要把前面的大写转换为小写)

wllmup_up_up

010打开

在结尾发现flag

flag{A2e_Y0u_Ok?}

Page 3

1,2 Caesar+低个头

第一道题目,应该可以想到凯撒密码,毕竟题目名称都暗示这么明显了(Caesar是凯撒的意思)而且f→g,l→m,a→b,g→h也很明显,移位数多试几次,这道题移位数是1,解出来的正好含有题目名称,包上flag交上就好了

再看第二题,这个题就有意思了,低个头,看的是什么呢?

对了,是键盘,而且看一下这几个字母,想到了键盘上的字母,用线连起来,就构成了几个字母

虽然有点难看,但是能看出来这是个CTF,看来每个题目的题目都是有深意的~

EWAZX RTY TGB IJN IO KL

flag{CTF}

3Mysterious(待定)

1.题目描述

2.题解过程

竟然是一个exe文件,运行试试,总不能有病毒吧

需要输入密码,随便输了一个之后就自动关闭了,接下来就要寻找密码了,

010先看看

DOS是什么?

继续寻找线索

卡了,

4[SUCTF2018]single dog

1.题目描述

2.解题过程

是一道图片题

010打开,在FFD9之后还藏着一个zip,用kali的binwalk -e分离一下

分出来一个zip和txt(txt就是zip里的内容,用的binwalk)

(奇怪,文件名中不能含有空格吗?失败了好几次)

txt打开是一些奇怪的东西,这个就是AAencode编码(头一次见)

找个网站

CTF在线工具-在线AAencode编码|AA编码|AAencode解码|AAencode编码原理|AAencode编码算法 (hiencode.com)

flag{happy double eleven}

和题目遥相呼应y

5.喵喵喵(太难了)

1.题目描述

2.解题过程

LSB隐写

题目中出现扫一扫,首先考虑是不是藏着二维码,可能是LSB隐写,用stegsolve看一看

在blue,green,red的0通道都发现了一张一样的有点奇怪的照片,只有半截

调整通道

调整三个通道的位数均为0,然后LSBfirst,然后试一下Bit plane order红绿蓝的顺序,调整好后,是这样的

试了好几次才找到,不过文件头好像有点问题,另存为一个文件看看

(另存为text可能会有点问题,如图)

修改文件头

save bin后缀改成png,010打开修改,

删去FF FE后,得到了半张二维码

关闭后重新用010打开,会提示这个

CRC校验码错误,按照这个修改一下

修改高度

第4,5,6列都是高,改一下(从第6列开始改,不需要太大)

得到了一个完整的二维码

扫二维码

是一个百度网盘分享rar,(QR Research好像扫不出来),用微信扫一扫

解压出来一个txt,结果…

这实在是太难了,接下来思路全无

去看了一下大佬的wp,才知道这原来是NTFS数据流隐写,要用到NTFs Streams Editor这个新工具

https://files.cnblogs.com/files/rainbow7/ntfsstreamseditor.zip

这是好不容易才找到的一个纯净的

NTFS数据流搜索

一番操作后,导出来一个pyc文件(只能搜索文件夹,把flag.txt放到一个文件夹里就好)

然后这是一个反编译,好难

反编译

借鉴一下大佬的wp,https://tool.lu/pyc/是一个反编译网站

代码如下

import base64

def encode():

flag = '*************'

ciphertext = []

for i in range(len(flag)):

s = chr(i ^ ord(flag[i]))

if i % 2 == 0:

s = ord(s) + 10

else:

s = ord(s) - 10

ciphertext.append(str(s))

return ciphertext[::-1]

ciphertext = [

'96',

'65',

'93',

'123',

'91',

'97',

'22',

'93',

'70',

'102',

'94',

'132',

'46',

'112',

'64',

'97',

'88',

'80',

'82',

'137',

'90',

'109',

'99',

'112']

写脚本

def decode(arg1):

ciphertext = arg1[::-1]

flag = ''

for i in range(len(ciphertext)):

if i % 2 == 0:

s = int(ciphertext[i]) - 10

else:

s = int(ciphertext[i]) + 10

s = s ^ i

flag += chr(s)

print(flag)

if __name__ == '__main__':

ciphertext = [

'96',

'65',

'93',

'123',

'91',

'97',

'22',

'93',

'70',

'102',

'94',

'132',

'46',

'112',

'64',

'97',

'88',

'80',

'82',

'137',

'90',

'109',

'99',

'112']

decode(ciphertext)

得到

3.flag

flag{Y@e_Cl3veR_C1Ever!}

6[ACTF新生赛2020]NTFS数据流(待定)

1.题目描述

解压之后

2.解题过程

先学习一下有关Ntfs数据流的知识

在Ntfs文件系统中,病毒、木马和蠕虫或者它们的结合体等恶意程序可能隐藏在“Ntfs数据流”这样的特殊"文件"中,一般的文件管理器和工具不能发现和处理;Ntfs数据流处理工具NtfsStreamsEditor提供了强大的搜索扫描能力,帮助你找出危险所在;同时NtfsStreamsEditor提供了最强大的删除、附加、导入、导出、备份和还原等完整处理手段,是您必备的工具软件。

1.什么是Ntfs数据流(交换数据流或者ADS) ? Ntfs数据流:Ntfs交换数据流 或 Ntfs可选数据流 ;Alternate Data Streams (ADS) Ntfs数据流给文件附加了一些额外的信息,这些本不属于该文件的信息就叫做“交换数据流”;而文件本身(宿主文件)则叫做该文件的“主数据流(Primary Data Stream)”。 Ntfs数据流允许文件与多个数据流进行关联。例如,一个名为 text.txt 的文件可以包含一个名为 text.txt:secret(格式是“文件名:流名”)的可选数据流,并只允许知道该数据流名称或专门为数据流而设计的浏览程序(NtfsStreamsEditor)查看。可选数据流无法从原始文件长度中得知,但会随着对原始文件的删除,或将原始文件移动/复制到不支持可选数据流的分区(如 FAT 分区、软盘或网络共享位置)上而丢失。可选数据流在多方面有重要用途,但也容易因为被遗忘或未被探测到而白白耗费磁盘空间。 简单的创建一个数据流: 按住win+R(开始_运行) 调出"运行"对话框 输入 notepad c::123.txt 回答“是” 输入字符保存就创建了一个“隐藏的文件123.txt”。我们无法在“资源管理器”中看到有关数据流文件的变化。这种缺陷让木马有机可趁,通过NTFS数据流将自己隐藏起来,从此就从“资源管理器”中消失了。例如Rootkit木马中的文件隐藏功能,就是利用了NTFS数据流,而NtfsStreamsEditor完全能检测出来。 Ntfs数据流(ADS)的宿主文件可以是一个磁盘(像上例),一个目录,一个文件,因为在操作系统内部,这些都是“文件”,ADS不能没有宿主文件。 备注:在操作系统内部任何文件名都有:KaTeX parse error: Undefined control sequence: \1 at position 14: DATA一个扩展;比如C:\?1?.txt 实际为C:\1.tx…DATA (2个冒号) ;而关联的数据流C:\1.txt:mysex.jpg 实际为 C:\1.txt:mysex.jpg:$DATA(前后个一个冒号),这也是为什么冒号:不能作为文件名的原因之一。

2.Ntfs数据流的用途?常见的数据流。 可以存放一些附加的信息。 @卡巴斯基(kaspersky)杀毒软件的iStream 技术使用KAVICHS作为处理过程的标志。 @下载的文件具有Zone.Identifier的数据流:当我们使用IE浏览器把网上的可执行文件保存到NTFS文件系统的分区上,IE浏览器就会自动给下载回来的文件附加一个交换数据流,当我们双击运行带有这种数据流的文件时,系统就会出现一个“安全警告”对话框提示。 @收藏夹的网址快捷方式一般具有名为favicon的数据流,存放的就是网站图标。 @隐藏其他的一些信息,比如木马,NtfsStreamsEditor完全能检测出来。

备注: NTFS是微软Windows NT内核的系列操作系统支持的、一个特别为网络和磁盘配额、文件加密等管理安全特性设计的磁盘格式。NTFS比FAT文件系统更稳定,更安全, 功能也更为强大。如果要让FAT文件系统转换为NTFS文件系统,可以在“命令提示符”中输入“convert 分区盘符: /fs:ntfs”,即可将该分区的文件系统转换为NTFS。

模仿上一道题目的解题过程,解压之后直接扫描一下

7.我吃三明治

1.题目描述

2.解题过程

010看

在FF D9文件尾之后还有一串不明字符

在结尾又有一个FF D9,可能是另一张图片?

不过这个开头好像有点问题,保存下来看看

是这样一串字符

MZWGCZ33GZTDCNZZG5SDIMBYGBRDEOLCGY2GIYJVHA4TONZYGA2DMM3FGMYH2

会不会是Base 32编码呢?虽然并不常见,试试看

竟然真是,OK

复习一下base32

Base32编码是使用32个可打印字符(字母A-Z和数字2-7)对任意字节数据进行编码的方案,编码后的字符串不用区分大小写并排除了容易混淆的字符,可以方便地由人类使用并由计算机处理。

Base32将任意字符串按照字节进行切分,并将每个字节对应的二进制值(不足8比特高位补0)串联起来,按照5比特一组进行切分,并将每组二进制值转换成十进制来对应32个可打印字符中的一个。

由于数据的二进制传输是按照8比特一组进行(即一个字节),因此Base32按5比特切分的二进制数据必须是40比特的倍数(5和8的最小公倍数)。例如输入单字节字符“%”,它对应的二进制值是“100101”,前面补两个0变成“00100101”(二进制值不足8比特的都要在高位加0直到8比特),从左侧开始按照5比特切分成两组:“00100”和“101”,后一组不足5比特,则在末尾填充0直到5比特,变成“00100”和“10100”,这两组二进制数分别转换成十进制数,通过上述表格即可找到其对应的可打印字符“E”和“U”,但是这里只用到两组共10比特,还差30比特达到40比特,按照5比特一组还需6组,则在末尾填充6个“=”。填充“=”符号的作用是方便一些程序的标准化运行,大多数情况下不添加也无关紧要,而且,在URL中使用时必须去掉“=”符号。

与Base64相比,Base32具有许多优点:

适合不区分大小写的文件系统,更利于人类口语交流或记忆。 结果可以用作文件名,因为它不包含路径分隔符 “/”等符号。 排除了视觉上容易混淆的字符,因此可以准确的人工录入。(例如,RFC4648符号集忽略了数字“1”、“8”和“0”,因为它们可能与字母“I”,“B”和“O”混淆)。 排除填充符号“=”的结果可以包含在URL中,而不编码任何字符。 Base32也比Base16有优势:

Base32比Base16占用的空间更小。(1000比特数据Base32需要200个字符,而Base16则为250个字符) Base32的缺点:

Base32比Base64多占用大约20%的空间。因为Base32使用8个ASCII字符去编码原数据中的5个字节数据,而Base64是使用4个ASCII字符去编码原数据中的3个字节数据。 本页Base32编码方案符合RFC4648文档描述。输入字符集为UTF-8编码。

3.flag

flag{6f1797d4080b29b64da5897780463e30}

8.john-in-the-middle

1.题目概述

2.解题过程

是一道流量分析题目

首先追踪流

TCP

HTTP,好像有点东西,应该有一些图片之类的

那就导出试试

果然有些东西,save all看看

重点看看几张图片吧,毕竟其他的也看不懂

不过这个视频好像打不开

然后再010看一下

好像也没什么,我又卡壳了

借鉴一下大佬的思路,原来是两张图片拼凑到一起才能看到flag,第一次见到,用stegsolve操作一下

logo的这个通道有点东西,用Image Combiner进行叠加,调整通道发现flag(可是我还不明白为什么就是这两张图片,这背后的原理又是什么呢?)

3.flag

flag{J0hn_th3_Sn1ff3r}

9.弱口令(hard)

1.题目概述

2.解题过程

名叫弱口令

判断加密方式

一个加密的zip,看看是不是伪加密

爆破试试

并不是伪加密,那就爆破,密码应该并不复杂

但是速度快到我以为是伪加密,这是为什么?

查看压缩包属性

再回过头仔细看看

这个东西好像有点奇怪,看不到,在哪里能看到呢?

原来要到sublime text里去看,新东西

摩斯密码

好神奇,这应该是莫斯密码吧,去解一下看看,换行符打成空格,点就是·,横就是-,

… . .-… .-… ----- …-. — .-. …- –

在线摩斯密码翻译器_摩斯密码-ME2在线工具 (metools.info)

这应该就是解压密码了吧(我还以为是flag)

010看图片

并没有什么,stegsolve看看

试了几次好像也没啥,又不会了

接下来又是别人的解答

python脚本

首先了解一下cloacked-pixel这个工具一个LSB分析工具

下载地址(下载要注册一个账号)

cloacked-pixel: LSB steganography and detection (gitee.com)

然后是常见的弱口令也要了解一下

常见的弱口令总结 - Fright-Moch - 博客园 (cnblogs.com)

已经给你准备好脚本了,挺好(但是python3运行会出错,我还专门又下了一个python2)

/cloacked-pixel-master# python lsb.py extract .\1.png 2.txt 12345

[+] image size: 500x500 pixels.

[+] Written extracted data to 2.txt

apt-get install cloacked-pixel-master

sudo vim /etc/apt/sources.list

阿里云 deb http://mirrors.aliyun.com/kali kali-rolling main non-free contrib deb-src http://mirrors.aliyun.com/kali kali-rolling main non-free contrib

python2 .\lsb.py extract .\1.png flag

[+] Image size: 500x500 pixels.

[+] Written extracted data to flag.txt

3.flag

flag{HELL0FONUM}

10.SXMgdGhpcyBiYXNlPw

1.题目概述

2.解题过程

base64隐写

题目名称就很形象,有点像base64隐写

试试上次的脚本能不能跑出来

脚本

很顺利

脚本如下(flag.txt与脚本文件放在同一文件夹可以直接跑)

import base64

def get_base64_diff_value(s1, s2):

base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

res = 0

for i in range(len(s2)):

if s1[i] != s2[i]:

return abs(base64chars.index(s1[i]) - base64chars.index(s2[i]))

return res

def solve_stego():

with open('flag.txt') as f:

file_lines = f.readlines()

bin_str = ''

for line in file_lines:

steg_line = line.replace('\n', '')

# norm_line = line.replace('\n', '').decode('base64').encode('base64').replace('\n','')

norm_line = line.replace('\n', '').encode('utf-8')

norm_line = base64.b64decode(norm_line).decode('utf-8')

norm_line = base64.b64encode(norm_line.encode('utf-8')).decode('utf-8')

norm_line = norm_line.replace('\n', '')

diff = get_base64_diff_value(steg_line, norm_line)

print(diff)

pads_num = steg_line.count('=')

if diff:

bin_str += bin(diff)[2:].zfill(pads_num * 2)

else:

bin_str += '0' * pads_num * 2

print(goflag(bin_str))

def goflag(bin_str):

res_str = ''

for i in range(0, len(bin_str), 8):

res_str += chr(int(bin_str[i:i + 8], 2))

return res_str

if __name__ == '__main__':

solve_stego()

3.flag

flag{fazhazhenhaoting}

11swp

1.题目概述

2.解题过程

没有什么提示,直接按照以前的步骤走一遍

但是这个题目有一些没见过的协议

先去了解一下

TPLV1.3

(2条消息) TLSv1.3学习_lbl的博客-CSDN博客

DNS MDNS LLMNR SSDP协议

DNS DNS只是提供了域名和IP地址之间的静态对应关系,当IP地址发生变化时,DNS无法动态的更新域名和IP地址之间的对应关系,从而导致访问失败。但是DDNS系统是将用户的动态IP地址映射到一个固定的域名解析服务上

DDNS DDNS用来动态更新DNS服务器上域名和IP地址之间的对应关系,从而保证通过域名访问到正确的IP地址。很多机构都提供了DDNS服务,在后台运行并且每隔数分钟来检查电脑的IP地址,如果IP发生变更,就会向DNS服务器发送更新IP地址的请求

NBNS--------NetBIOS漏洞【询问主机名】 NBNS是网络基本输入/输出系统 (NetBIOS) 名称服务器的缩写。它也是TCP/IP协议的一部分。它负责将计算机名转化为对应的IP。NBNS在WIndows用的较少,Windows普遍采用LLMNR协议。 特点 Name query NB是请求包, Name query response NB是响应包。 双方的端口号均为137。

mDNS【局域网中的DNS】 mdns 即多播dns(Multicast DNS),mDNS主要实现了在没有传统DNS服务器的情况下使局域网内的主机实现相互发现和通信,遵从dns协议,使用现有的DNS信息结构、名语法和资源记录类型。并且没有指定新的操作代码或响应代码。 特点 端口为5353 主机的域名为.local 结尾 mdns 使用组播地址为: 224.0.0.251 (ipv6: FF02::FB)

在局域网中,设备和设备之前相互通信需要知道对方的ip地址的,大多数情况,设备的ip不是静态ip地址,而是通过dhcp 协议动态分配的ip 地址,如何设备发现呢,就是要mdns大显身手,例如:现在物联网设备和app之间的通信,要么app通过广播,要么通过组播,发一些特定信息,感兴趣设备应答,实现局域网设备的发现,当然mdns 比这强大的多。 比如,A主机进入局域网,开启了 mDNS 服务,并向 mDNS 服务注册以下信息:我提供 FTP 服务,我的IP是 192.168.1.101,端口是 21。当B主机进入局域网,并向 B 主机的 mDNS 服务请求,我要找局域网内 FTP 服务器,B主机的 mDNS 就会去局域网内向其他的 mDNS 询问,并且最终告诉你,有一个IP地址为 192.168.1.101,端口号是 21 的主机,也就是 A 主机提供 FTP 服务,所以 B 主机就知道了 A 主机的 IP 地址和端口号了。 mDNS 具体协议规范地址如下 : http://www.ietf.org/rfc/rfc6762.txt

LLMNR【是DNS的备胎方案】

在DNS 服务器不可用时,DNS 客户端计算机可以使用本地链路多播名称解析 (LLMNR—Link-Local Multicast Name Resolution)(也称为多播 DNS 或 mDNS)来解析本地网段上的名称。例如,如果路由器出现故障,从网络上的所有 DNS 服务器切断了子网,则支持 LLMNR 的子网上的客户端可以继续在对等基础上解析名称,直到网络连接还原为止。 除了在网络出现故障的情况下提供名称解析以外,LLMNR 在建立临时对等网络(例如,机场候机区域)方面也非常有用。

链路本地多播名称解析(LLMNR)是一个基于域名系统(DNS)数据包格式的协议,IPv4和IPv6的主机可以通过此协议对同一本地链路上的主机执行名称解析。Windows 操作系统从 Windows Vista开始就内嵌支持,Linux系统也通过systemd实现了此协议。 LLMNR定义在RFC 4795。 224.0.0.252,对应的 IPv6 地址是 [FF02::1:3] LLMNR工作过程 (1) 主机在自己的内部名称缓存中查询名称。如果在缓存中没有找到名称,那么主机就会向自己配置的主DNS服务器发送查询请求。如果主机没有收到回应或收到了错误信息,主机还会尝试搜索配置的备用DNS服务器。如果主机没有配置DNS服务器,或者如果在连接DNS服务器的时候没有遇到错误但失败了,那么名称解析会失败,并转为使用LLMNR。 (2) 主机通过用户数据报协议(UDP)发送多播查询,查询主机名对应的IP地址,这个查询会被限制在本地子网(也就是所谓的链路局部)内。 (3) 链路局部范围内每台支持LLMNR,并且被配置为响应传入查询的主机在收到这个查询请求后,会将被查询的名称和自己的主机名进行比较。如果没有找到匹配的主机名,那么计算机就会丢弃这个查询。如果找到了匹配的主机名,这台计算机会传输一条包含了自己IP地址的单播信息给请求该查询的主机。

LLDPDU协议 网络设备可以通过在本地网络中发送LLDPDU(Link Layer Discovery Protocol Data Unit)来通告其他设备自身的状态。是一种能够使网络中的设备互相发现并通告状态、交互信息的协议。LLDP协议使得接入网络的一台设备的主要能力,管理地址,设备标识,接口标识等信息发送给同一个局域网的其他设备,当一个设备从网络中接收到其它设备的信息时,就将这些信息以MIB的形式存储起来。

SSDP协议 SSDP(简单服务发现协议)是应用层协议,是构成UPnP(通用即插即用)技术的核心协议之一。它为网络客户端(network client)提供了一种发现网络服务(network services)的机制,采用基于通知和发现路由的多播方式实现。 SDP协议的设计初衷是使用HTTP协议进行设备的发现,是UPnP协议的组成部分。

知道这么多应该差不多了,然后再来看题

追踪流

TCP

HTTP

UDP

HTTP流里好像有点东西,导出来看看

根据名字判断,最可疑的应该是这个压缩包,打开看看

是个加密的zip,

判断伪加密

确实是

关于ZIP伪加密,又找了几个方法

zip伪加密 - 浩琦很好奇 - 博客园 (cnblogs.com)

今天我们来试试新方法(以前的方法肯定可以,不过还是尝试一下新方法吧)

win rar修复

然后会产生一个新的压缩包

打开这个压缩包,就是无加密的了,flag也很容易看到

Java环境下使用ZipCenOp.jar

先下载一个文件

ZipCenOp.jar 链接:https://pan.baidu.com/s/1RLRPN0fKWmqdaqLlV409Wg 密码:twl4

下载完成后打开(需要安装Java环境)

把ZipCenOp.jar文件和secret.zip放到同一个文件夹,然后在空白处 Ctrl+Shift+右键,在此处打开Windows Powershell 窗口

然后输入

java -jar .\ZipCenOp.jar r .\secret.zip

这个样子就成功了,然后我们打开这个目录下的zip,就是未加密的啦

打开flag这个文件,看到flag

3.flag

flag{c5558bcf-26da-4f8b-b181-b61f3850b9e5}

12黄金6年

1.题目概述

2.解题过程

是一道视频题,以前做过这个类型的题目

现在再试试kinovea这个工具,看看能不能试出来什么

kinovea下载链接

Kinovea

逐帧分析

在75帧出现了一个二维码

继续看看,还有什么东西

148帧

246帧

一共发现了3个二维码,扫扫看

扫描二维码

75帧

148帧

246帧

key1-key3连起来是 i want play

感觉这个句子少了点什么,而且从语法角度也不太合适

再去看看还有没有二维码,好吧,我并没有找到

突然想起来potplayer也可以逐帧播放,下载链接

PotPlayer下载 - PotPlayer中文网 (potplayercn.com)

并且可以调整时间间隔,百度一下步骤

1.直接点击鼠标右键,选择“播放”-“定位”,取消“以关键帧定位”。

2.选择“时间跨度设置”。

3.将调解的大小设置合适的时长,取消下侧的框选。

4.还要注意定位的快捷键是使用“D/F”。

然后我就神奇地发现了二维码,potplayer太棒了

扫一下

获得key

key1-key3连起来是 iwantplayctf

那么这个是干什么的呢?老方法,回过头用010看看视频

base64编码

发现了最后有一串类似base64编码的尾巴,去解解看

UmFyIRoHAQAzkrXlCgEFBgAFAQGAgADh7ek5VQIDPLAABKEAIEvsUpGAAwAIZmxhZy50eHQwAQAD Dx43HyOdLMGWfCE9WEsBZprAJQoBSVlWkJNS9TP5du2kyJ275JzsNo29BnSZCgMC3h+UFV9p1QEf JkBPPR6MrYwXmsMCMz67DN/k5u1NYw9ga53a83/B/t2G9FkG/IITuR+9gIvr/LEdd1ZRAwUEAA==

这好像是个rar文件,然后保存成rar文件

保存成rar

不知道为啥,他打不开,

win rar都修复不了~换个完整的文件头也不行。。。

解码方式(弯路)

然后改了半天,才发现是这个网站的问题,不知道为什么,它解码的时候给漏了好多个00,从这2张图可以很明显地看出来

我改变了一下输出结果的格式就好了,输出为16进制(血泪教训)

010再试试

终于可以了

密码应该就是那4个key拼凑而成的,解压得到flag

3.flag

flag{CTF-from-RuMen-to-RuYuan}

13.间谍启示录

1.题目概述

2.解题过程

一共就几个文件,都打开看看吧

文档说明

在打开systemzx.exe时

点击安装之后,在相应D出现了几个新的文件

点击运行试试看

运行flag.exe后,产生了一个机密文件.txt,打开看看

发现了flag,不明白这个题目在考察什么???

3.flag

Flag{379:7b758:g7dfe7f19:9464f:4g9231}

14.zip

1.题目概述

2.解题过程

解压出了68个zip,根据题目描述,拼在一起,如何拼在一起呢?

先打开几个看看

那就是真加密了

接下来俺爆破无果,010看也没看出什么,只能求助网上的wp了,这是其中一个思路

这个东西我一直没有太过关注,

利用CRC校验码爆破这个方法还是头一次见

那么这个题目的思路应该就是把这68个zip的crc校验码拼到一起,然后去解码,解码结果保存为文件,就可以了吧(后面没的思路和上一道题目差不多)

这是网上找的爆破脚本(脚本与68个压缩包放在同一文件夹就可)

import zipfile

import string

import binascii

def CrackCrc(crc):

for i in dic:

for j in dic:

for k in dic:

for h in dic:

s = i + j + k + h

if crc == (binascii.crc32(s.encode())):

f.write(s)

return

def CrackZip():

for i in range(0,68):

file = 'out'+str(i)+'.zip'

crc = zipfile.ZipFile(file,'r').getinfo('data.txt').CRC

CrackCrc(crc)

dic = string.ascii_letters + string.digits + '+/='

f = open('out.txt','w')

CrackZip()

print("CRC32碰撞完成")

f.close

得到所有zip的CRC校验码(但是好慢)

z5BzAAANAAAAAAAAAKo+egCAIwBJAAAAVAAAAAKGNKv+a2MdSR0zAwABAAAAQ01UCRUUy91BT5UkSNPoj5hFEVFBRvefHSBCfG0ruGnKnygsMyj8SBaZHxsYHY84LEZ24cXtZ01y3k1K1YJ0vpK9HwqUzb6u9z8igEr3dCCQLQAdAAAAHQAAAAJi0efVT2MdSR0wCAAgAAAAZmxhZy50eHQAsDRpZmZpeCB0aGUgZmlsZSBhbmQgZ2V0IHRoZSBmbGFnxD17AEAHAA==

解码,保存下来

根据提示修复文件,文件头没有见过,文件尾是3D 7B 00 40 07 00,这是RAR的文件尾,添加文件头52 61 72 21 1A 07 00,保存

打开rar看到flag

3.flag

15.吹着贝斯扫二维码

1.题目概述

2.解题过程

打开flag.zip看看

是个加密的zip,在注释里有一串貌似是base32的编码,可能是密码

去解解看

不过好像解不出来,那先放一放,回去看看那些不明文件,

010打开一个看看

头是jpg,尾巴多了一点东西

改后缀

那就改改后缀看看能不能打开

可以顺利打开,很明显这是一张二维码的一小部分,可以猜想,把全部的文件后缀都改成jpg,然后再把他们拼起来,不就是一张完整的二维码嘛,说干就干,不过也要借助一下脚本,python真是个好东西

import os

path = 'D://CTF//attachment//1' #文件具体路径,这个1是那堆文件所在的文件夹,不成功时可以改个路径

for i in os.listdir('D://CTF//attachment//1'): #路径最好用绝对路径,不会出错

#if i == '修改后缀.py':

#continue

#else:

oldname = os.path.join(path,i)

newname = os.path.join(path,i+'.jpg')

os.rename(oldname,newname)

运行完后,该怎么把这36张图片拼到一起呢?这是个问题(你也不知道顺序)

确认顺序

其实,在之前的图010看的时候,在文件尾后面还有一个数字,

猜测这就是顺序,可以验证一下,

找一个3个角的特殊位置的图片,猜测数字可能是1,6,或31(根据图片数量确定位置)

不放心可以再看看另外两个角

这下可以百分百确定了,这个数字就是拼图顺序

命名文件

可以利用python脚本根据这个顺序命名文件

import os

from PIL import Image

#目录路径

dir_name = r"./"

#获取目录下文件名列表

dir_list = os.listdir('./')

#print(dir_list)

#从列表中依次读取文件

for file in dir_list:

if '.jpg' in file:

f=open(file ,'rb')

n1 = str(f.read())

n2 = n1[-3:]

#经过测试发现这里要读取最后3个字节,因为最后还有一个多余的字节,不知道是不是转字符串的原因导致在末尾多了一个字符

#print(file) #输出文件内容

#print(n2)

f.close()#先关闭文件才能重命名,否则会报`文件被占用`错误

os.rename(file,n2+'.jpg')#重命名文件

这些90多的,其实是1-9,代码写的不够好,出了点问题,改一下吧

利用ps拼图

拼图步骤如下:

1.新建,调整高度和宽度,我调整为24X24

2.文件->置入嵌入对象,选择图片

3.操作

注意有些地方可能会有一点点重合,

嘿,拼的还不错

扫一下

确认解码顺序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x4Pyxsn1-1657866852338)(https://wangyuhan2021-1308234555.cos.ap-nanjing.myqcloud.com/image-20220128225515420.png)]

解码

哦,这个应该就是那串编码的解码顺序了!难怪解不出来

base32→16进制→13→85→85→64→85(根据那串编码的格式判断顺序)

这个13不是base家族的,查了一下,是rot13编码

GNATOMJVIQZUKNJXGRCTGNRTGI3EMNZTGNBTKRJWGI2UIMRRGNBDEQZWGI3DKMSFGNCDMRJTII3TMNBQGM4TERRTGEZTOMRXGQYDGOBWGI2DCNBY base32解码:3A715D3E574E36326F733C5E625D213B2C62652E3D6E3B7640392F3137274038624148 base16解码::q]>WN62os<^b]!;,be.=n;v@9/17’@8bAH rot13解码::d]>JA62bf<^o]!;,or.=a;i@9/17’@8oNU base85解码:PCtvdWU4VFJnQUByYy4mK1lraTA= base64解码:<+oue8TRgA@rc.&+Yki0 base85解码:ThisIsSecret!233 解压密码就是ThisIsSecret!233

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9E31blIi-1657866852339)(https://wangyuhan2021-1308234555.cos.ap-nanjing.myqcloud.com/image-20220128234122574.png)]

3.flag

flag{Qr_Is_MeAn1nGfuL}

16.小易的U盘

1.题目概述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XBgzGg2I-1657866852340)(https://wangyuhan2021-1308234555.cos.ap-nanjing.myqcloud.com/image-20220129160833649.png)]

2.解题过程

flag.txt打开

根据这个提示,flag可能要通过某些步骤生成,第一时间想到那个exe程序,打开看看

然后这个程序就一直是这个界面,再看看其他的文件吧

刚刚打开的是副本(2),打开副本(32)看看

那就根据它的提示,用按照它的提示,用VISUAL C++之类的东西打开,不过我想试试直接用WIN RAR 打开看看行不行

查找一下,找到flag

3.flag

flag{29a0vkrlek3eu10ue89yug9y4r0wdu10}

17.alison_likes

1.题目概述

2.解题过程

txt打开

010看看两张图

这张图挺正常

很明显,这个图隐藏着一个zip,用Kali的binwalk分离一下看看,

分离图片

打开zip看看

是一个加密的,看看是不是伪加密,可以用win rar修复功能判断

那就是真加密,密码

寻找ZIP密码

先爆破试试

4个长度的数字

6个长度的数字

解压打开txt

WVRKc2MySkhWbmxqV0Zac1dsYzBQUT09

有点像base64编码,解解看

解码注意

不过好像有点问题,这个地方去查了一下,原来这里要连续解码3次,学到了学到了

最后解出来是 killerqueen(正好是我喜欢的一首歌诶)

然而这个并不是flag,好奇怪,哪个地方没有处理好呢?

去百度了一下,原来第二张图片是outguess隐写,但是我不知道怎么看出来的

outguess隐写

那这个killerqueen就是密码,用Kali命令去解一下

outguess -k 'killerqueen' -r jljy.jpg out.txt

终于得到flag了

3.flag

flag{pretty_girl_alison_likes_jojo}

18.从娃娃抓起

1.题目概述

2.解题过程

题目描述.txt

从娃娃抓起.txt

md5还不太了解,去查一查

一.作用:一般用于互联网一种数据的唯一性标识。

二.实体:本身是一个128位的0/1比特。一般被表示为16进制的字符串。4个比特位组成一个16进制字符,因此常常能见到的是(128/4=)32个16进制字符组成的字符串 4951 dd1c bff8 cbbe 4cd4 475c a939 fc8b,当然它实质是一种消息摘要算法

三.特点:

1.完全相同的一段数据,不论时间地点(加密算法相同的条件)加密出的32位的字符串完全相同。

2.加密过程本身就是一个有损的加密过程。因此几乎不能还原出原始数据。安全

补充:一般还原出原始数据都是使用碰撞的方式来还原。意思大概和穷举的意思一样,有足够多的原始数据量,世界上的所有数据用 1n来表示,把1n都用MD5计算一遍得1xnx(x表示计算后得的数)。把1n当成键值对的键,把1x~nx放入键值对的值,当得到一个MD5字符串,通过值去找键,就知道是什么数据了。因为你本来就拥有数据,因此才能找到数据。但当你没有原始数据时,想通过128比特的玩意去恢复大于128比特的数据本身就是玩笑,数据量都不一样,至于原理有兴趣的朋友可以研究研究信息论。不想研究的可以回想回想之前学的香农公式。再从这一点去研究。又有大的世界。 ———————————————— 版权声明:本文为CSDN博主「Anakki」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_29519041/article/details/84403649

然后需要知道这句话指的是什么,这串数字和字母去百度一下

那就好办了,这句话就是人工智能也要从娃娃抓起,

附上两个网站

中文电码查询 Chinese Commercial Code - 标准电报码免费在线查询|姓名电码|美国签证电码 (mcdvisa.com)

五笔输入法编码在线查询系统.汉字转拼音 (zd9999.com)

把这句话去加密一下,得到flag

MD5在线加密 - 站长工具 (chinaz.com)

3.flag

flag{3b4b5dccd2c008fe7e2664bd1bc19292}

19.zips

1.题目概述

2.解题过程

和套娃似地,一个zip套着一个zip,

看看这个ZIP是不是伪加密

判断加密类型

不是伪加密,那先爆破

爆破

解压

再判断加密类型

修改后解压

这个ZIP仍然是加密的,

使用WIN RAR修复之后仍然是加密的,那就是真加密了,但是尝试爆破试不出来

再看看这个setup.sh文件

#!/bin/bash

\#

zip -e --password=`python -c "print(__import__('time').time())"` flag.zip flag

原来如此,这个ZIP的解压密码的掩码是要通过这个文件来获得的,那就在python环境下运行一下代码,多试几次

那掩码应该就是1643644???.??去试试

试了半天不行就很奇怪,难道我做错了?

百度

原来如此,再设置一下(注意,这个题目要求的是python2,python2的精度是两位)

解压得到flag

3.flag

flag{fkjabPqnLawhvuikfhgzyffj}

20.爬

1.题目概述

2.解题过程

010查看

猜测这应该是一个PDF文件,修改后缀再打开

只有一张图片,把照片移一下,出来一个图片(用的破解版的Adobe Acrobat DC)

把内容保存下来,可以用QQ的文字识别

'0x77637466323032307b746831735f31735f405f7064665f616e645f7930755f63616e5f7573655f70686f7430736830707d ’

0x可以不要,试试010

3.flag

flag{th1s_1s_@_pdf_and_y0u_can_use_phot0sh0p}

21.(╯°□°)╯︵ ┻━┻

1.题目概述

2.解题过程

这个题目很难想

只有这一个字符串,base解码解不出来,其他的思路也没想到

观察这个字符串的特点,差不多都是

010编辑一下,先把它看成十六进制

换到右边

可以观察到左侧数字非常有规律,差不多都是60,30左右的样子,我们可以考虑ASCII,位数需要算一下,根据这个字符串中最小的来算,或者多试几次,总可以试出来的,在这里应该是128

借一个一个python脚本

key='d4e8e1f4a0f7e1f3a0e6e1f3f4a1a0d4e8e5a0e6ece1e7a0e9f3baa0c4c4c3d4c6fbb9b2b2e1e2b9b9b7b4e1b4b7e3e4b3b2b2e3e6b4b3e2b5b0b6b1b0e6e1e5e1b5fd'

hex=[]

flag=''

for i in range(0,len(key)-1,2):

zdh=key[i:i+2]

hex.append(zdh)

for i in range(len(hex)):

zdh1=hex[i]

flag+=chr(int(int(zdh1,16))-128)

print(flag)

3.flag

flag{922ab9974a47cd322cf43b50610faea5}

22.docx

1.题目概述

2.解题过程

打开是一个word文档,和黑客有关

010打开

竟然没有模板,按照老思路,修改成zip后缀试试,(文件头都一样)

一个个地找呗

3.flag

flag{unz1p_3v3ryth1ng}

23.百里挑一

1.题目概述

2.解题过程

一道流量分析题目,根据题目描述,流量包里应该有图片类文件

老思路。先追踪

再导出

save all,好家伙,直接一百七十多张照片

那么flag藏在哪张图片里呢?wires hark看看有什么特殊的流

Kali上的新工具exiftool *|grep flag试一试

ExifTool是一种用于提取文件元数据的强大工具。它不仅适用于图像,还适用于其他格式的文件,比如PDF和mp4等。它使我们能够更新和删除文件的元数据,并提供有关文件的许多信息。可以通过这个回答了解

(2条消息) ubuntu 安装kali_如何在Linux,Windows,Kali,Ubuntu,Mint和示例中安装和使用exiftool_cunjiu9486的博客-CSDN博客

安装(对于Kali2021.3版本,如已安装请忽略)

sudo apt-get install libimage-exiftool-perl

搜索flag

另一半就不好找了,在Kali上用工具我搞了半天也没找出什么,还是回到wires hark上直接找吧(毕竟以前就有这个解法的题目)

在TCP114流中,找到另一半flag(不是我找出来的,找半天没找到,百度的)

3.flag

flag{ae58d0408e26e8f26a3c0589d23edeec}

24.千层套路

1.题目概述

2.解题过程

先看看是不是伪加密

win rar修复之后,仍然是加密的,那就是真加密,去爆破

解压

再爆破(忽然发现密码就是zip名)

解压

盲猜密码是0653

当我解压到这个步骤的时候,我感觉事情好像不太对\0573\0114\0653\0234\0976\0669\0540\0248\0275\0149\0028\0099\0894\0991\0291\0414\0296\0241\0510\0914\0340\0140\0286\0532\0347\0345

所以,只能求助脚本,要不根本解不完啊

脚本

import zipfile

name = '0573'

while True:

fz = zipfile.ZipFile(name + '.zip', 'r')

fz.extractall(pwd=bytes(name, 'utf-8'))

name = fz.filelist[0].filename[0:4]

fz.close()

好家伙,解压出来1000个zip

还跑出来一个qr.zip,打开,有点像RGB三色素那个(而且不是255 255 255就是0 0 0,绝对是黑白的二维码)

用脚本把它转换成图片吧

from PIL import Image

import os

string = ''

file = open('./qr.txt')

MAX = 200

picture = Image.new("RGB", (MAX, MAX))

for y in range(MAX):

for x in range(MAX):

string = file.readline()

picture.putpixel([x, y], eval(string)) # 直接使用eval()可以转为元组

picture.show()

运行之后自动出现一张二维码

扫描得到 flag

3.flag

flag{ta01uyout1nreet1n0usandtimes}

25.girlfriend

1.题目概述

2.解题过程

解压之后是一个音频,用Audacity打开,应该是段摩斯电码吧

写下来

— . … . … . . … . … . . . .-- . - . … . … . …- … . … . … . … . … . . . …

忽然感觉不太对,这是摩斯电码吗,再仔细听一听,好像是拨号按键时的声音(大意了)

原来是DTMF拨号音识别,这有个程序可以识别dtmf2num.exe

下载地址,实在是太不好找了

https://www.softpedia.com/get/Multimedia/Audio/Other-AUDIO-Tools/DTMF2NUM.shtml#download

在当前文件夹中打开power shell(鼠标点击空白,Ctrl+Shift+右键),输入

.\ dtmf2num.exe girlfriend.wav

回车

DTMF numbers: 999*666*88*2*777*33*6*999*4*4444*777*555*333*777*444*33*66*3*7777

然后就联想一下以前的九键手机按键,大概是这样的

盲猜999即按3次9得到的字母(小时候打字的经验),666即按3次6得到的字母,以此类推:

999–y,666–o,88–u,

这前三个拼起来便是单词you,所以猜想是对的,继续:

2–a,777–r,33–e,6–m,999–y,4–g,4444–i,777–r,555–l,333–f,777–r,444–i,33–e,66–n,3–d,7777–s.

7777和4444只能是S和I,毕竟没有第四个字母。

连起来便是 youaremygirlfriends

3.flag

flag{ youaremygirlfriends}

26.Attack

1.题目概述

2.解题过程

是一道流量分析题,

先追踪流

HTTP

TCP

追踪出的内容是一模一样的

导出对象

HTTP(只有这里面有文件)

保存下来看看

这个lsass.dmp文件有些可疑,文件很大,

lsass是windows系统的一个进程,用于本地安全和登陆策略。

在foremost分离时有了发现

其他文件夹里都是些没用的图片

加密的txt,

提示administrator的密码,联想到前面那个dmp文件,得知需要用一个新工具来获取dmp文件中的用户密码

下载地址

Releases · gentilkiwi/mimikatz (github.com)

解压之后忽略安全软件提出的警告

mimikatz使用教程

(4条消息) 内网渗透神器(Mimikatz)——使用教程_W小哥-CSDN博客_mimikatz

然后把lsass文件放到mimikatz.exe文件目录下,再打开mimikatz

输入

privilege::debug

sekurlsa::minidump 1.dmp

sekurlsa::logonpasswords full

回车

password:W3lc0meToD0g3

解压打开

得到flag

3.flag

flag{This is the secret of the administrator!}

27.Game

1.题目概述

2.解题过程

010看图片

图片修改高之后也没出现什么东西

foremost分离也没有东西

再回头看看那几个不认识的文件吧

先打开这个html文件看看

用记事本打开,按照经验查查找flag或者password

出现一串base32编码,

ON2WG5DGPNUECSDBNBQV6RTBNMZV6RRRMFTX2===

解出来

试了一下发现并不是flag,emmm,只能再重点看一下图片了

会不会是LSB隐写呢?用stegsolve看看

LSB隐写

试了几次找出来了一串字符

U2FsdGVkX1+zHjSBeYPtWQVSwXzcVFZLu6Qm0To/KeuHg8vKAxFrVQ==

但是却解不出来,好奇怪

百度一下大神的wp,才知道这原来是DES加密,第一次见

DES加密

介绍(好复杂看不懂)

DES算法详解 - songoo - 博客园 (cnblogs.com)

常用DES解密网站

在线Triple DES加密 | Triple DES解密- 在线工具 (sojson.com)

解出来

3.flag

flag{U_F0und_1t}

28.followme

1.题目概述

2.解题过程

流量分析题

追踪流

导出HTTP对象,果然有东西

save all之后,有很多东西

这个题和前面那个题目有点像,都是有好多文件

对,是百里挑一这道题,利用Kali命令直接搜索flag,CTF或者password之类的字样

grep

打开Kali,在该文件目录下打开终端

输入命令

grep -r 'CTF'

找到flag

但是如果把CTF改成password,可能会找不到

3.flag

flag{password_is_not_weak}

29.CyberPunk

1.题目概述

2.解题过程

打开这个程序是这样的

我就等了一会,说不定会出现什么东西

但是过了很久也没出现啥

忽然发现他说到2020.9.17会告诉我们flag

于是我把电脑时间改了一下

修改时间

修改时间之后重新打开,出现了flag

3.flag

flag{We1cOm3_70_cyber_security}

30.通行证

1.题目概述

2.解题过程

a2FuYmJyZ2doamx7emJfX19ffXZ0bGFsbg==

然后我试了试也不是凯撒密码,那该怎么办呢?

经过一番搜集,原来这是栅栏密码

什么是栅栏密码呢?

栅栏密码

栅栏密码(Fence)——python解密_有人_295的博客-CSDN博客_栅栏密码

几种加密方式简介

栅栏密码(The Rail-Fence Cipher)详解 - SwordCreater - 博客园 (cnblogs.com)

然后,多试几次每组字数,并且,这个题比较神奇地地方是要我们加密,而不是平常的解密,好家伙,这谁能想到

CTF在线工具-在线栅栏密码加密|在线栅栏密码解密|栅栏密码算法|Railfence Cipher (hiencode.com)

这就是个简单的凯撒密码啦,耐心试位数(不要认为开头一定是flag或CTF之类的哦)

3.flag

flag{oyay_now_you_get_it}

31.USB

1.题目概述

2.解题过程

010查看压缩包

修改zip文件头

把74改成7A,再打开压缩包,多了一张图片

010看图片没看出什么,再看看是不是LSB隐写

LSB隐写

在blue 0通道有一张二维码

扫一下

ci{v3erf_0tygidv2_fc0}

栅栏密码

这是干嘛的?先放在这,去看看另一个文件

010看看,发现了一些东西

保存下来看看

7-zip解压,虽然说文件尾错误,但是还是可以解压出来的,win-rar就不行

打开,是USB数据包内容(百度得知)

又要学到新东西了,使用命令提取键盘的数据

Kali命令

提取USB键盘的数据

tshark -r key.pcap -T fields -e usb.capdata > usbdata.txt

打开txt

利用脚本获取信息

脚本下载地址

WangYihang/UsbKeyboardDataHacker: USB键盘流量包取证工具 , 用于恢复用户的击键信息 (github.com)

Kali命令

python UsbKeyboardDataHacker.py key.pcap

获得key:xinan

联想上面那个位置的东西,应该是维吉尼亚密码(其实我不知道,第一次见)

维吉尼亚密码

网站

维吉尼亚密码在线加密解密 - 千千秀字 (qqxiuzi.cn)

然后就是栅栏密码了

栅栏密码

3.flag

flag{vig3ne2e_is_c00l}

32.虚假的压缩包

1.题目概述

2.解题过程

真实的压缩包.zip伪加密

修改为00后,解压

其实不用这么麻烦,7-zip可以直接打开

RSA加密

第一次接触到RSA加密

介绍

(4条消息) 非对称加密算法–RSA加密原理_Vincent的专栏-CSDN博客_非对称加密算法

RSA破解脚本

import gmpy2

p=gmpy2.mpz(3)

q=gmpy2.mpz(11)

e=gmpy2.mpz(3)

l=(p-1)*(q-1)

d=gmpy2.invert(e,l)

c=gmpy2.mpz(26)

n=p*q

ans=pow(c,d,n)

print (ans)

如果pychrm或python报错,那可能是你没有安装gmpy2的缘故,安装步骤如下

gmpy2安装

下载gmpy2.whl

1.进入如下网址安装whell(轮子):(别用pip install whell,不管用)

https://www.lfd.uci.edu/~gohlke/pythonlibs/

cp27代表2.7版本cp36代表3.6版本amd64代表64位win32代表32位

(小贴士,ctrl+F搜索gmpy2即可找到gmpy2库,省时又省力)

2.下载好后,把文件拖入到你对应编辑器的Scripts文件夹里(不用改名字)

3.在Scripts文件夹下打开power shell窗口(Ctrl+Shift+右键),输入

pip install + 你下载的文件放入的文件夹路径 + \文件名.whl(+字不用写进去)

举例:

pip install E:\Programes\python_work\Scripts\gmpy2-2.0.8-cp39-cp39-win_amd64.whl

然后回车,

上面已经显示gmpy2安装成功了可以通过pip install gmpy2测试一波,显示如下

但是,我的安装之路却错误百出,一上来下错了文件,找到正确的后版本又不行,唉,不过又试了一个版本,发现可以,然后还更新了一波,如图

OK,安装完成

运行后,出来了密码

密码

当我以为解压密码是5的时候,却解不出来

呃,这里也有个坑,回过头看那个txt文档

所以解压密码是:答案是5

太坑人了

解压

010打开图片

从最简单的修改高度看看

第二行4-7列,从第七列开始修改,不要修改的太高了

果真有东西

^是C语言中的异或,那我们尝试将“亦真亦假”这个文件的16进制按照每位与5异或看看,(毕竟他没有上场)

首先修改一下名字,改成英文,比如cuihua,然后复制到pycharm存放python文件的文件夹下

异或(待定)

脚本

f1 = open('./cuihua','r')

data = f1.read()

f1.close()

flag_data = ""

for i in data:

tmp = int(i,16) ^ 5

flag_data += hex(tmp)[2:]

f2 = open('./flag.doc','wb')

f2.write(flag_data.decode('hex'))

f2.close()

如果报错,类似于这样

这是因为python3并不支持python2中的一些写法,

具体解释

解决方法如下

在python scripts文件夹下打开power shell窗口,输入

pip install h5py==2.10 -i https://pypi.doubanio.com/simple

3.flag

Page4

1.[SWPU2019]Network(TTL隐写)

1.题目概述

2.解题过程

文档中的数字代表什么呢?会不会是RGB?

看了一下以前做过的题目,好像并不是

那是什么呢?百度告诉我这是TTL隐写,哇,长见识了

TTL是 Time To Live的缩写,该字段指定IP包被路由器丢弃之前允许通过的最大网段数量。TTL是IPv4报头的一个8 bit字段。注意:TTL与DNS TTL有区别。二者都是生存时间,前者指ICMP包的转发次数(跳数),后者指域名解析信息在DNS中的存在时间。

IP报文在路由间穿梭的时候每经过一个路由,TTL就会减1。 这个东西是用来防止数据过多的。计算机网络知识。

大多数情况下通常只需要经过很小的跳数就能完成报文的转发,远远比上限255小得多,所以我们可以用TTL值的前两位来进行传输隐藏数据。 所以加密的方法就是把一个ASCII码分4截,分到4个TTL里面,有点像是最低位像素的隐写。

啊,这些好像也没啥用,

按照以下步骤,

1.转成2进制,

2.写个脚本。取出前两位。重新组合。再以8位一组。

3.转换为16进制字符。再将16进制字符转换为字符(好复杂)

借助大佬的脚本,开干!

脚本如下

with open('Network.txt') as f:

lines = f.readlines()

n_num = []

#分析出所有的数

for i in lines:

if i!='\n':

n_num.append(int(i))

#拿到每个TTL值的高位

rlt = ''

for i in range(0,len(lines)):

tmp = bin(n_num[i])[2:]

tmp = '0'*(8-len(tmp)) + tmp

rlt += tmp[0:2]

#得到最终的结果并存到文件中

rlt2 = ''

for i in range(0,len(rlt),8):

rlt2 += chr(int(rlt[i:i+8],2))

with open('output.txt','w') as f:

f.write(rlt2.rstrip())

出来一个类似于zip文件的编码,存下来

打开,竟然是加密的

是伪加密

直接用7-zip打开

这些base64编码和以前做过的一道题很像

再试一下

多重Base64破解

import base64

f = open('1flag.txt','rb').read()

while True:

f = base64.b64decode(f)

if b'{' in f:

print(f)

break

else:

continue

pycharm python2环境下运行, 可能需要安装scikit-learn安装包(在设置里)

3.flag

flag{189ff9e5b743ae95f940a6ccc6dbd9ab}

2.[RCTF2019]draw

1.题目概述

2.解题过程

题目名称叫“draw",肯定与图片有关

百度

是logo语言,丢进网站

Logo解释器 (calormen.com)

不同的浏览器出来的结果可能不太一样,但内容是一样的

修改一下字母顺序

RCTF_HeyLogo

3.flag

flag{RCTF_HeyLogo}

3.[UTCTF2020]file header

1.题目概述

2.解题过程

题目名称叫file header,而且图片打不开猜想是文件头有问题

010打开

果然,文件头没有,加上看看

修改文件头

89504E47

能打开了

太感动了,终于遇到一道简单题了

3.flag

flag{3lit3_h4ck3r}

4.[ACTF新生赛2020]明文攻击

1.题目概述

2.解题过程

题目叫明文攻击,又有加密的zip

先看看这个图片

在文件尾FFD9后面还有一串未知字符

Kali下binwalk分离看看

看来没有什么东西,线索在这串字符中

这不是zip文件头的一部分嘛,而且还有flag.txt字符,存下来,把文件头补充完整

打开这个zip

它也有一个flag.txt文件,联想到题目名称和那个res.zip里的文件,使用archpr进行明文攻击

复习一下明文攻击原理

明文攻击是一种较为高效的攻击手段,大致原理是当你不知道一个zip的密码,但是你有zip中的一个已知文件(文件大小要大于12Byte)时,

因为同一个zip压缩包里的所有文件都是使用同一个加密密钥来加密的,所以可以用已知文件来找加密密钥,利用密钥来解锁其他加密文件。

archpr跑起来

运行一会后,停止攻击,

会出来一个密钥和一个新的压缩包,新的压缩包就和加密的压缩包被破解之后的

3.flag

flag{3te9_nbb_ahh8}

5.二维码

1.题目概述

2.解题过程

是一个撕破了的二维码,把它拼起来应该就好了

废了半天劲才拼成这样,关键是还扫不出来

借用一下别人的图

qr-reasearch好像扫不出来,用微信扫一扫可以(也不容易扫出来)

3.flag

flag{7bf116c8ec2545708781fd4a0dda44e5}

6.[BSidesSF2019]zippy

1.题目概述

2.解题过程

追踪TCP流发现了这个

哇,还是个加密的压缩包,但是导出对象里没有文件,那就用binwalk分离一下试试

有戏,打开看看

密码应该就是前面那个supercomplexpassword

3.flag

flag{this_flag_is_your_flag}

7.[MRCTF2020]Hello_ misc

1.题目概述

2.解题过程

rar是加密的

打开图片

名叫try to restore it, 提示我们修复它,如何修复呢?

感觉这个图片像LSB隐写,stegsolve打开看看

LSB隐写

调整通道没有看到什么,改一下位深度

调整到这个数值的时候看到了熟悉的png文件头

保存下来打开

password:!@#$%67*()-+

按照提示binwalk分离原图片

binwalk分离

打开压缩包,用前面那个password解压打开

TTL隐写

又是熟悉的感觉,和network那道题很像

借助同样的思路,用脚本破解

with open('out.txt') as f:

lines = f.readlines()

n_num = []

#分析出所有的数

for i in lines:

if i!='\n':

n_num.append(int(i))

#拿到每个TTL值的高位

rlt = ''

for i in range(0,len(lines)):

tmp = bin(n_num[i])[2:]

tmp = '0'*(8-len(tmp)) + tmp

rlt += tmp[0:2]

#得到最终的结果并存到文件中

rlt2 = ''

for i in range(0,len(rlt),8):

rlt2 += chr(int(rlt[i:i+8],2))

with open('out1.txt','w') as f:

f.write(rlt2.rstrip())

打开out1.txt

rar-passwd:0ac1fe6b77be5dbe

解压

感觉这有点像word后缀改成zip再解压,改成doc后缀看看

zip改doc后缀

啊?就这,玩捉迷藏呢

在文档的下面,这几个换行符有点不正常

全选以后,有猫腻,应该是不可见字符,复制到sublime里看看

不可见字符

MTEwMTEwMTExMTExMTEwMDExMTEwMTExMTExMTExMTExMTExMTExMTExMTExMTExMTAxMTEwMDAwMDAxMTExMTExMTExMDAxMTAx MTEwMTEwMTEwMDAxMTAxMDExMTEwMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTAxMTExMTExMTExMTExMTEwMTEwMDEx MTEwMDAwMTAxMTEwMTExMDExMTEwMTExMTExMTAwMDExMTExMTExMTExMDAxMDAxMTAxMTEwMDAwMDExMTExMDAwMDExMTExMTEx MTEwMTEwMTAwMDAxMTExMDExMTEwMTExMTExMDExMTAxMTExMTExMTEwMTEwMTEwMTAxMTExMTExMTAwMTEwMTExMTExMTExMTEx MTEwMTEwMTAxMTExMTExMDExMTEwMTExMTAxMDExMTAxMTExMTExMTEwMTEwMTEwMTAxMTAxMTExMTAwMTEwMTExMTExMTExMTEx MTEwMTEwMTAwMDAxMTAwMDAwMTEwMDAwMDAxMTAwMDExMTAwMDAwMTEwMTEwMTEwMTAxMTEwMDAwMDAxMTExMDA

是一些base64编码,解码看看

是一串二进制字符,转成16进制看看

没转出来啥东西,哪里错了呢?

忽然发现这些base64字符分行有点奇怪,会不会让我们分行解呢?

分行解base64

试一下

110110111111110011110111111111111111111111111111101110000001111111111001101

110110110001101011110111111111111111111111111111111101111111111111110110011

110000101110111011110111111100011111111111001001101110000011111000011111111

110110100001111011110111111011101111111110110110101111111100110111111111111

110110101111111011110111101011101111111110110110101101111100110111111111111

11011010000110000011000000110001110000011011011010111000000111100

哇,这就有意思了,这些01代表什么呢?

百度一下wp,这原来是可以变成图片的,脑洞太大了

用记事本替换功能把1或者0换成空格,试了一下,把1换成空格可以

把0连起来,构成了flag{He1Lo_mi5c~}

字符错了可以多试几次

3.flag

flag{He1Lo_mi5c~}

8.[UTCTF2020]basic-forensics

1.题目概述

2.解题过程

题目叫basic-forensics,基本取证

图片打不开010看看

没有文件头,并且有许多可读字符

搜flag字符

flag{fil3_ext3nsi0ns_4r3nt_r34l}

3.flag

flag{fil3_ext3nsi0ns_4r3nt_r34l}

9.[WUSTCTF2020]spaceclub

1.题目概述

2.解题过程

txt文档大小为4kb但是打开却显示空白,肯定有猫腻

选中之后是这样的

010打开

16进制下数值非常规律,

用sublime打开看看(弱口令那道题也是这样的方法,可以去复习一下)

Ctrl+A全选之后,可以看到文档中的大量 “·”,这个长度不应该是摩斯密码,而且太规律了,怀疑是长的一行代表一个东西,短的一行代表一个东西,这就可以联想到二进制

把长的一行换成1,短的一行换成0看看

1.在sublime里选中长的一行(不要先选短的),复制

然后用记事本的替换,把复制的一行粘贴,替换成1,

2.1.在sublime里选中短的一行,复制

然后用记事本的替换,把复制的一行粘贴,替换成0,

结果如图所示

011101110110001101110100011001100011001000110000001100100011000001111011011010000011001101110010011001010101111100110001011100110101111101111001001100000111010101110010010111110110011001101100010000000110011101011111011100110011000101111000010111110111001100110001011110000101111101110011001100010111100001111101

然后二进制转字符

得到flag

3.flag

flag{h3re_1s_y0ur_fl@g_s1x_s1x_s1x}

10.[MRCTF2020]Unravel!!

1.题目概述

2.解题过程

先用010打开图片

在最后面,发现了一点提示

aes,pngPK,可能是这个图片文件包含某个png,然后再对这个png进行AES解密

binwalk分离

得到Tokyo.png,

这应该就是AES密钥

查看wav文件

老规矩,用Audacity打开分析频谱

好像也没啥东西

010看看(文件名提示Look_at_the_file_ending)

key=U2FsdGVkX1/nSQN+hoHL8OwV9iJB/mSdKk5dmusulz4=

AES解密

网站

在线AES加密 | AES解密 - 在线工具 (sojson.com)

解得CCGandGulu,先放这里

最后看这个win-win.zip

需要密码,试试刚刚得到的这个,应该就是解压密码

成功提取

Audacity没找出什么东西,再用Silent Eye看看

得到flag

3.flag

flag{Th1s_is_the_3nd1n9}

11.[MRCTF2020]不眠之夜(待定)

1.题目概述

2.解题过程

这么多图片拼起来好像有字(红色字迹)

Kali工具montage和gaps

下载步骤

安装montage: #Kali Linux

apt-get install graphicsmagick-imagemagick-compat

然后慢慢等他安装就好了

安装gaps: git clone https://github.com/nemanja-m/gaps.git

montage *jpg -tile 10x12 -geometry 200x100+0+0 out.jpg #把图片碎片合成一个图片

将目录中的jpg文件按顺序拼成x轴10块,y轴12块,每个图块大小为200x100像素,输出文件为out.jpg

montage *jpg -tile 10x12 -geometry 200x100+0+0 out.jpg

gaps --image=out.jpg --generations=50 --population=120 --size=100

#把图片碎片合成一个图片

# 将目录中的jpg文件按顺序拼成x轴10块,y轴12块,每个图块大小为200x100像素,输出文件为out.jpg

#还原原图片

//gaps --image=out.jpg --generations=50 --population=120 --size=100 #还原原图片

--image 指向拼图的路径

--size 拼图块的像素尺寸

--generations 遗传算法的代的数量

--population 个体数量

--verbose 每一代训练结束后展示最佳结果

--save 将拼图还原为图像

montage *jpg -tile 10x12 -geometry 200x100+0+0 out.jpg

3.flag

12.[ACTF新生赛2020]music

1.题目概述

2.解题过程

打开听一听

010打开,添加文件头后仍然打不开

忽然发现第二行A1和第一行添加的00位置非常对应,A1的异或正好是00,找到方法了

二进制异或

把原先添加的文件头删掉

可以打开了,听到flag

flag{abcdfghijk}

3.flag

flag{abcdfghijk}

13.蜘蛛侠呀

1.题目概述

2.解题过程

wireshark查看

随便打开几个看看

好像他们的后面都有一串东西

Tshark提取

使用Kali工具将其提取出来

tshark -r out.pcap -T fields -e data > flag.txt

大量数据,可以看出有很多重复

python脚本去重

with open('flag.txt', 'r') as file:

res_list = []

lines = file.readlines()

print('[+]去重之前一共{0}行'.format(len(lines)))

print('[+]开始去重,请稍等.....')

for i in lines:

if i not in res_list:

res_list.append(i)

print('[+]去重后一共{0}行'.format(len(res_list)))

print(res_list)

with open('flag1.txt', 'w') as new_file:

for j in res_list:

new_file.write(j)

输出仍然有很多行,和好像是16进制

然后将十六进制数据转为字符

十六进制数据转为字符

脚本(只适用于python2)

import binascii()

with open('flag1.txt','r') as file:

with open('data2.txt','wb') as data:

for i in file.readlines():

data.write(binascii.unhexlify(i[:-1]))

还是网站香

在线字符串和16进制互转 - 在线工具 (bugscaner.com)

结果保存下来

首尾这两行没有用,删掉

利用记事本的替换功能,删掉

S

T

A

R

T

START

START

Base64转16进制

方法一:

利用python将base64解码以字节流形式写成zip

import base64

with open('data.txt','rb') as file:

with open('res.zip','wb') as new_file:

new_file.write(base64.b64decode(file.read()))

方法二:

利用网站(修改编码输出格式,数据量大,网站可能会卡)

得到相同的16进制数据后,打开010,在编辑里选择粘贴自16进制文本

然后打开压缩包,是一个gif文件,先用stegsolve和Kinovea打开都没有发现什么,百度才知道这是另一种隐写,叫时间隐写(真是稀奇)

时间隐写

Kali工具

identify -format “%T” flag.gif

得到2050这样的重复,

字符替换

模仿上一题(spaceclub)的思路,把20替换成0,50替换成1,最后的4个6就忽略,得到(利用记事本这个强大的工具)

011011010100010000110101010111110011000101110100

然后把这个二进制转成字符

[在线二进制转换字符串EN] - 转换 (txttool.com)

把这个字符MD5加密

多试几次看看哪个对,试了一下,32位小写是正确答案

3.flag

flag{f0f1003afe4ae8ce4aa8e8487a8ab3b6}

14.[UTCTF2020]zero

1.题目概述

2.解题过程

题目提示0;

Sublime打开,看到一些奇怪的东西

是一些零宽度字符,利用网站

Unicode Steganography with Zero-Width Characters (330k.github.io)

3.flag

flag{whyNOT@sc11_4927aajbqk14}

15.easy misc

1.题目概述

decode被加密了

read里面是这样的

2.解题过程

压缩包破解

打开有提示密码

算了一下,根号后面这一串数字结果是7,那么解压密码应该就是7+NNULLULL,7代表什么呢,试了一下,不是数字7,那么应该就是位数,Archpr破解试试

(注意,?要在英文模式下输入,别忘了还有个“,”

解压打开

先放这儿

再去看那些txt文档

按照hint提示,提取什么东西的前十六个字符,先打开几个文档看看;

24内容是这样的

13

1

呃,好像也没啥思路,要不先看看那张照片吧

分离图片

foremost分离

两张一模一样的图片,但是名字不一样,猜测是盲水印(虽然我也不太懂)

盲水印

学习一下

(5条消息) 隐写术(盲水印):从入门到出门_guofei9987的博客-CSDN博客_文本隐写术

要是觉得太难,就看这个简单的

【CTF】图片隐写术 · 盲水印 - 双份浓缩馥芮白 - 博客园 (cnblogs.com)

具体工具

bwmforpy3.py脚本

下载网站

GitHub - chishaxie/BlindWaterMark: 盲水印 by python

用法(如果报错,比如 No module named 'matplotlib’或者No module named ‘cv2’,就在power shell窗口输入以下代码)

注意,一定是这个,不要打开终端,可能跑不出来

python -m pip install matplotlib #python3环境

pip3 install opencv-python #python3环境

如果没有报错,python3环境输入

python bwmforpy3.py decode 00000000.png 00000232.png output.png

但是运行出来是这样的

查了一下才知道,工具里有个说明:注意程序python2和python3版本的加解密结果会有所不同,主要原因是python2和python3 random的算法不同,如果要让python3兼容python2的random算法请加 --oldseed参数。 因此,加上“–oldseed”:

然后输入

python bwmforpy3.py decode 00000000.png 00000232.png result.png --oldseed

出来了

提示打开11.txt,去看看

前面有个提示,提取前16个字符,那肯定不是文本里的前16个字符,根据咱们丰富的经验(bushi),应该是字频前16

脚本跑起来

字频脚本

这里引用别人的代码,在此表示感谢:

import re

file = open('11.txt')#根据路径自行调整

line = file.readlines()

file.seek(0,0)

file.close()

result = {}

for i in range(97,123):

count = 0

for j in line:

find_line = re.findall(chr(i),j)

count += len(find_line)

result[chr(i)] = count

res = sorted(result.items(),key=lambda item:item[1],reverse=True)

num = 1

for x in res:

print('频数第{0}: '.format(num),x)

num += 1

前16个字符是:

即:

etaonrhisdluygwm

然后利用解压出来的decode那个文件进行替换(血泪教训)

替换出来是

QW8obWdIWT9pMkFSQWtRQjVfXiE/WSFTajBtcw==

去base64网站解码

然后再去base85网站解码

3.flag

flag{have_a_good_day1}

16.派大星的烦恼

1.题目概述

2.解题过程

仔细揣摩题目概述,两种伤疤,而且还有0x22,0x44这种,可以想到二进制(smart)

010打开

这后面为啥全是一样的,搜一搜看看

如果只是滑滚轮的话很可能看不到,毕竟22和44太少了(有点像一种隐写)

替换字符

把这些22和44提取出来,然后利用记事本的替换,把22换成0,把44换成1,试试看

(这样选择就可以复制22和44了)

0110110000101100000011000110110010011100101011000000110010000110101011000010110010001100000111000010110001000110001001101010110001100110101001100110110001000110011011001010011010101100010001100010110011000110101001100010110011001100000111001100110001001100

接下来该干什么呢?陷入了僵局

题目又提示 (答案为32位的一串字符串),所以尝试将其转化为字符,发现是一堆无意义的东西

要不先转成16进制,(去掉空格)再转字符?

卡壳了许久之后,突然想到倒序

倒序

脚本

a = "01101100 00101100 00001100 01101100 10011100 10101100 00001100 10000110 10101100 00101100 10001100 00011100 00101100 01000110 00100110 10101100 01100110 10100110 01101100 01000110 01101100 10100110 10101100 01000110 00101100 11000110 10100110 00101100 11001100 00011100 11001100 01001100"

a = a.split()

flag = ''

for i in a:

flag += chr(int(i[::-1],2))

print (flag)

6406950a54184bd5fe6b6e5b4ce43832

再转字符(我怕我是傻了)

这不就是flag吗?

3.flag

flag{6406950a54184bd5fe6b6e5b4ce43832}

17.[GKCTF 2021]签到

1.题目概述

2.解题过程

追踪HTTP流

在下面发现了一串可疑字符

Base16转base64

放到010里看看

复制下来,去转字符

好像不是,再回去找找其他的

又发现了这个

看来flag与Base64编码有关,那么怎么才能得到呢?

可以利用base16转成base64,然后base64又可以转成字符,就这么干

又找出来2个base16字符串

复制到010

但是第一个解出来啥都没有

再解解第二个

看来有戏,但是解了一下没出来什么东西,还提示编码有误

看了看才发现,他这个地方有点问题

正常的==应该在最后,把他逆序一下

wIDIgACIgACIgAyIK0wIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMiCNoQD

jMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjoQDjACIgACIgACIggDM6EDM6AjMgAzMtMDMtEjM

t0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0iCNMyIjMyIjMyIjMyI

6AjMgAzMtMDMtEjMwIjO0eZ62ep5K0wKrQWYwVGdv5EItAiM1Aydl5mK6M6jlfpqnrQDt0SLt0SL

t0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLK0AIdZavo75mlvlCNMTM6EDM

z0yMw0SMyAjM6Q7lpb7lmrQDrsCZhBXZ09mTg0CIyUDI3VmbqozoPW+lqeuCN0SLt0SLt0SLt0SL

sxWZld1V913e7d2ZhFGbsZmZg0lp9iunbW+Wg0lp9iunbW+Wg0lp9iunbW+WK0wMxoTMwoDMyACM

DN0QDN0QDlWazNXMx0Wbf9lRGRDNDN0ard0Rf9VZl1WbwADIdRampDKilvFIdRampDKilvVKpM2Y

==QIhM0QDN0Q

#正常情况应该是XXX==这种,所以逆序一下试试,注意是每行逆序才能得到标准的XXX==

然后建一个脚本:

每行逆序

a='wIDIgACIgACIgAyIK0wIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMiCNoQD'

b='jMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjMyIjoQDjACIgACIgACIggDM6EDM6AjMgAzMtMDMtEjM'

c='t0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0iCNMyIjMyIjMyIjMyI'

d='6AjMgAzMtMDMtEjMwIjO0eZ62ep5K0wKrQWYwVGdv5EItAiM1Aydl5mK6M6jlfpqnrQDt0SLt0SL'

e='t0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLt0SLK0AIdZavo75mlvlCNMTM6EDM'

f='z0yMw0SMyAjM6Q7lpb7lmrQDrsCZhBXZ09mTg0CIyUDI3VmbqozoPW+lqeuCN0SLt0SLt0SLt0SL'

g='sxWZld1V913e7d2ZhFGbsZmZg0lp9iunbW+Wg0lp9iunbW+Wg0lp9iunbW+WK0wMxoTMwoDMyACM'

h='DN0QDN0QDlWazNXMx0Wbf9lRGRDNDN0ard0Rf9VZl1WbwADIdRampDKilvFIdRampDKilvVKpM2Y'

i='==QIhM0QDN0Q'

print(a[::-1])

print(b[::-1])

print(c[::-1])

print(d[::-1])

print(e[::-1])

print(f[::-1])

print(g[::-1])

print(h[::-1])

print(i[::-1])

#耐心一点,正确分行

逆序后再转一下看看

base64转字符

哇,终于出来了,去重一下

flag{Welc0me_GkC4F_m1siC!}

(注意,下划线_不要漏了)

或者利用栅栏密码网站,调整一下{}的位置就好了

这道签到题好难啊!!!

3.flag

flag{Welc0me_GkC4F_m1siC!}

18.粽子的来历

1.题目概述

2.解题过程

DBAPP是什么意思呢?

打开文档

既然已经损坏,用010打开看看

A中发现了这个

B

C里发现了DBAPP字符

最后看看D

那C里的字符是最可疑的,尝试修复一下C文件看看,

49436F6D6546726F6D44626170706168

从上面我们可以知道,这几个word文档缺少文件头,加上看看能不能打开

加上 504B0304之后,还是打不开

利用zip文件头和word相同这一特点,把它改成zip看看

打开后还是没有什么东西,用binwalk分离一下

binwalk分离

还是没有,看来前面的思路不太对,再用用其他的修复方式

把C里面IcomefromDBAPP那几个不一样的字符全部改成周围的字符一样看看(删去前面添加的文件头)

哇,终于打开了

可是接下来该干什么呢?没有思路了

忽然这个文档的发现行间距不一样,

把1.5倍的看成1,1.0倍的看成0试试

行间距

得到

100100100001

MD5加密

MD5在线加密 - 站长工具 (chinaz.com)

32位小写

d473ee3def34bd022f8e5233036b3345

3.flag

flag{d473ee3def34bd022f8e5233036b3345}

19.[CFI-CTF 2018]webLogon capture

1.题目概述

2.解题过程

导出HTTP文件,

%20%43%46%49%7b%31%6e%73%33%63%75%72%33%5f%6c%30%67%30%6e%7d%20

base64转字符

方法一:

去掉百分号后:

204346497b31637333637572335f6c3067306e7d20

base16转字符;利用网站或直接搞到010里

方法二:

利用python

print(‘%20%43%46%49%7b%31%6e%73%33%63%75%72%33%5f%6c%30%67%30%6e%7d%20’.replace(‘%’,‘’)) #去掉%

import binascii

binascii.a2b_hex(‘204346497b316e7333637572335f6c3067306e7d20’) #转字符

3.flag

flag{1cs3cur3_l0g0n}

20.hashcat

1.题目概述

2.解题过程

文件叫这是什么类型的文件,并且题目是hashcat,应该和哈希有关

法一:爆破

010打开

文件头没有搜到,看看文件尾

也没搜到文件尾的信息,用虚拟机的binwalk看看

XML文件,也就是数据文件,可以用Excel打开

添加后缀.xml,打开,有密码

爆破

使用Accent OFFICE Password Recovery破解密码,

下载地址https://url82.ctfile.com/f/26363582-542017838-b06171(访问密码:6878)

没有给密码提示,那就暴力破解,纯数字,2-6位差不多

密码9919,解压打开

这个有点可疑

标黑看看

得到flag

还给了一个MD5的小结,有良心

法二:office2join+hash(待定)

修改后缀后(比如pptx),利用office2join.py命令得到哈希值

需要先安装这个脚本

参考这个文章安装

How to Crack Password-Protected Microsoft Office Files, Including Word Docs & Excel Spreadsheets ? Null Byte :: WonderHowTo

然后输入命令:

python office2john.py 20-1.pptx > hash.txt #把由这个文档得到的哈希值存储到hash.txt中

3.flag

flag{okYOUWIN}

21.[SCTF2019]电单车

1.题目概述

题目的原本描述是这样的:

截获了一台电动车的钥匙发射出的锁车信号,3分钟之内,我要获得它地址位的全部信息。flag内容二进制表示即可。

2.解题过程

audicity打开,观察频谱图

然后查一查这是什么协议

观察频谱图,一细一粗,把细的换成0,粗的换成1,(只看前面这一段就好,别漏了最初的0)

0/011101001010101001100(这个0有点奇怪,多出来一位)/0100

地址位:01110100101010100110

知识点:

参考这篇文章

https://www.freebuf.com/articles/wireless/191534.html

PT2242信号:前面4bit表示同步码,中间的20bit表示地址码,后面的4bit表示功能码,最后一位是停止码。

3.flag

flag{01110100101010100110}

22.[watevrCTF 2019]Evil Cuteness

1.题目概述

2.解题过程

010打开

隐藏着压缩包,binwalk分离

竟然如此简单,菜鸟狂喜

3.flag

flag{7h475_4c7u4lly_r34lly_cu73_7h0u6h}

23.[MRCTF2020]pyFlag(zip数据移接)

1.题目概述

2.解题过程

010看看这几张图片

furan.jpg文件尾后面有一串未知字符

miku后面也有

setsuna

而且分别是part2,part3,part1,把它们用binwalk-e命令分离一下

得到一个加密的压缩包,

爆破行不通

再分离其他两个jpg看看

3.flag

24.[QCTF2018]X-man-A face

1.题目概述

2.解题过程

随便找个二维码把左上和左下的小方块给裁下来拼到这个图片上,利用画图工具就可以

QR-reasarch扫不出来,就用微信扫一扫

得到:

KFBVIRT3KBZGK5DUPFPVG2LTORSXEX2XNBXV6QTVPFZV6TLFL5GG6YTTORSXE7I=

很长时间没有接触base32编码了,一下子没反应过来

解码

Base32编码解码,Base32在线转换工具 - 千千秀字 (qqxiuzi.cn)

3.flag

flag{Pretty_Sister_Who_Buys_Me_Lobster}

25.[UTCTF2020]File Carving

1.题目概述

2.解题过程

png格式,010打开

修改高度无果,搜索PK发现zip数据

binwalk -e分离

打开这里面的zip

法一:010

未知格式,010打开,搜索flag(根据hidden提示)

这里面有一些奇怪的字符,删掉

得到

flag{2fbe9adc2ad89c71da48cabe90a121c0}

法二:Kali运行elf文件

binwalk分析这个hidden_binary文件

elf文件,百度

kali可以直接运行,挺好

这个方法更快

3.flag

flag{2fbe9adc2ad89c71da48cabe90a121c0}

26.[INSHack2017]sanity

1.题目概述

2.解题过程

3.flag

flag{Youre_sane_Good_for_you}

27.[GUET-CTF2019]soul sipse

1.题目概述

2.解题过程

audacity打开音频

突然就没有思路了。。。

binwalk分离

也没有什么东西,那应该是隐写了

steghide extract -sf out.wav

无密码

Firefox浏览器能打开,edge竟然打不开

打开

010打开

文件头损坏,改成47

\u0034\u0030\u0037\u0030\u000d\u000a\u0031\u0032\u0033\u0034\u000d\u000a

unicode编码,转换一下

Unicode编码转换 - 站长工具 (chinaz.com)

数学题1,加起来等于5304,哦,这就是flag

3.flag

flag{5304}

28.voip

1.题目概述

2.解题过程

viop百度

哇,那应该和电话有关

打开之后,有一段音频

播放

声音很诡异,我愣是没听出来,提交都是错的

正确的flag:(俺也不知道咋听出来的,离谱)

flag{9001IVR}

3.flag

flag{9001IVR}

29.寂静之城

1.题目概述

2.解题过程

删掉链接后面的无用字,再打开链接,如果自动的跳转到其他页面,就在加载的时候点一下这个

这个题很坑,我看了半天文章没找到什么信息,结果flag竟然藏在点赞的人里

打开这个出题人的主页,发现了

base64

evL5S5J+a5HzxAKR8xXiUXPUd1LDKPYYNHSjN6ZF8i0=

接下来就不会做了,百度别人的wp都没有说怎么做

flag{31010419920831481363542021127}

哈哈哈,点开评论太有意思了

3.flag

flag{31010419920831481363542021127}

30.Business Planning Group

1.题目概述

2.解题过程

010打开,显示结尾有错误

题目缩写BPG,直接搜索BPG

百度

从来没见过

把这些数据存下来

下载个软件打开这个bpg

BPG Image format (bellard.org)

下载后把bpg图片拖到bpgview.exe中(直接打开bpgview.exe会出错)

YnNpZGVzX2RlbGhpe0JQR19pNV9iM3R0M3JfN2g0bl9KUEd9Cg==

base64,转换一下

得到flag

flag{BPG_i5_b3tt3r_7h4n_JPG}

3.flag

flag{BPG_i5_b3tt3r_7h4n_JPG}

31.[湖南省赛2019]Findme

1.题目概述

2.解题过程

010打开这几张图片

先简单分析一下这几张图片

简单分析

1.png

从外观上,1.png明显高度太低,需要更改

2.png

2.png末尾有疑似其他文件的名称

3.png

3.png暂时没发现什么东西

4.png

4.png末尾有一串不明base64

cExlX1BsY

5.png

5.png末尾也有一串base64字符和提示性话语

Yzcllfc0lN

然后再开始深入分析1.png

简单修改高后打开

仍然没有什么信息

那来个专业的脚本看一下它原来的宽高是多少

import zlib

import struct

filename = '1.png'

with open(filename, 'rb') as f:

all_b = f.read()

crc32key = int(all_b[29:33].hex(), 16)

data = bytearray(all_b[12:29])

n = 4095

for w in range(n):

width = bytearray(struct.pack('>i', w))

for h in range(n):

height = bytearray(struct.pack('>i', h))

for x in range(4):

data[x + 4] = width[x]

data[x + 8] = height[x]

crc32result = zlib.crc32(data)

if crc32result == crc32key:

print("宽为:", end="")

print(width)

print("高为:", end="")

print(height)

按照这个数据给改一下

再打开

继续分析

IDAT隐写

使用工具pngcheck检查IDAT

下载地址:

pngcheck Home Page (libpng.org)

命令:

pngcheck.exe -v 1.png

显示有错误,010再打开1.png,

很明显,这两和其他人不一样

chunk2和chunk3的CTYPE缺少IDAT字样,给加上(在16进制数据里改)IDAT的十六进制标识为49 44 41 54

改好后再打开

LSB隐写

按照它原来的模样猜测有二维码

stegsolve打开

QR-reasearch扫描

哦耶,又找到一个线索

ZmxhZ3s0X3

再看2.png,按照前面的分析,2.png里应该藏有其他文件的数据,但是并没有找到zip之类压缩包的数据

会不会藏有其他格式的压缩包呢?搜一下看看

在搜索7Z时,有了一点线索

哈哈,笑死我了,出题人为了咱们能找到7Z竟然加了2000多个7Z,也不忘记加上个nothing here,有心了

7Z替换成PK

这些7Z后面要么是03 04 要么就是01 02,这不妥妥的zip数据的模样吗?把7Z全部改成PK

然后binwalk分离

打开

得到了999的txt究竟谁是最特殊的那个呢?

按照大小排序,618.txt最大,打开

哦耶,第二个线索

1RVcmVfc

或者直接搜索find,这样更简单,但是不好才到关键词是find

010看3.png,会发现,除了chunk7的CRC是正常的长度,其他的都不正常,太短了

把这6个crc保存下来,是

33 52 6C 5A 33 30 3D

然后放010里

哦耶,线索3

3RlZ30=

把5条线索集中到一起

1-5分别是

ZmxhZ3s0X3

1RVcmVfc

3RlZ30=

cExlX1BsY

Yzcllfc0lN

然后我就按照1-5的顺序直接解,结果不行???

排序,3肯定在最后,

1的解码

2解不出来

4

5也解不出来,

1开头,3结尾,4中间,那只有2种可能

12453或15423,试了一下15423的顺序正确

ZmxhZ3s0X3Yzcllfc0lNcExlX1BsY1RVcmVfc3RlZ30=

终于做出来了!!!

3.flag

flag{4_v3rY_sIMpLe_PlcTUre_steg}

32.[ACTF新生赛2020]剑龙

1.题目概述

2.解题过程

直接解压www.zip,出了一个o_o文件,O_O是pyc文件,题目名称叫剑龙

pyc文件

借一段话,转自

一文让你完全弄懂Stegosaurus - Angel_Kitty - 博客园 (cnblogs.com)

简单来说, pyc 文件就是 Python 的字节码文件,是个二进制文件。我们都知道 Python 是一种全平台的解释性语言,全平台其实就是 Python 文件在经过解释器解释之后(或者称为编译)生成的 pyc 文件可以在多个平台下运行,这样同样也可以隐藏源代码。其实, Python 是完全面向对象的语言, Python 文件在经过解释器解释后生成字节码对象 PyCodeObject , pyc 文件可以理解为是 PyCodeObject 对象的持久化保存方式。而 pyc 文件只有在文件被当成模块导入时才会生成。也就是说, Python 解释器认为,只有 import 进行的模块才需要被重用。 生成 pyc 文件的好处显而易见,当我们多次运行程序时,不需要重新对该模块进行重新的解释。主文件一般只需要加载一次,不会被其他模块导入,所以一般主文件不会生成 pyc 文件。

pyc文件破解需要密码,先放在这里,去寻找密码

www.zip解压后是一个hint.zip,010查看发现藏着其他压缩包,

biwalk分离

打开图片

打开pwd.txt

AAencode编码

CTF在线工具-在线AAencode编码|AA编码|AAencode解码|AAencode编码原理|AAencode编码算法 (hiencode.com)

welcom3!

再看看图片,

不是LSB隐写,有密码,可能是steghide隐写

steghide隐写

隐写工具Steghide - 简书 (jianshu.com)

Steghide是一个可以将文件隐藏到图片或音频中的工具

下载地址:

Find out more about steghide | SourceForge.net

命令

./steghide.exe extract -sf hh.jpg

想要flag吗?解出我的密文吧~

U2FsdGVkX1/7KeHVl5984OsGUVSanPfPednHpK9lKvp0kdrxO4Tj/Q==

再找密码

查看属性

@#$%^&%%$)

解密网址

在线DES加密 | DES解密- 在线工具 (sojson.com)

stegosaurus加密

回到前面那个O_O.pyc的文件,使用反编译工具stegosaurus

下载地址

AngelKitty/stegosaurus: A steganography tool for embedding payloads within Python bytecode. (github.com)

python stegosaurus.py -x O_O.pyc

python版本为3.7.9

3.flag

flag{3teg0Sauru3_!1}

33[GKCTF 2021]Excel骚操作

1.题目概述

2.解题过程

010打开

zip开头,kali binwalk 分离

都是一些xml文件,打开看看没啥东西,再回去看看excel表格

excel据说有个好玩的点:

如果单元格类型为“;;;”时,excel会隐藏单元格中的数字

随便打开几个单元格,会发现有的值为1,却没显示,有的值为空,联想以前做过的一道题,猜想可能是1与0转换成图形

MRCTF[2020]Hello_misc

Ctrl+F找到所有值为1的单元格,然后替换成黑色的单元格(根据1的值地域分布不连续推测不会直接是flag字样,而可能是二维码)

替换完成

修改一下单元格的宽,使之近似为正方形

这样子应该差不多

这个二维码看起来有点奇怪,微信扫不出来,网上一查这叫汉信码, 打开中国编码网下载对应app,

软件下载 (ancc.org.cn)

也可以直接扫描这个二维码下载

扫码得到flag,flag{9ee0cb62-f443-4a72-e9a3-43c0b910757e}

3.flag

flag{9ee0cb62-f443-4a72-e9a3-43c0b910757e}

Page 5

1.寂静之城(4-29)

2.[UTCTF2020]spectogram

1.题目概述

2.解题过程

听了一下没有什么东西,那就用audacity打开看看

打开是这个东西,这是什么意思?

看看频谱

似乎有什么信息,但是看不清

silenteye打开没有加密信息

百度一下题目名称

看来又是一种新的东西

spectogram分析博客

(9条消息) 211130-Python谱图(Spectogram)分析Demo_GuokLiu的博客-CSDN博客_demo谱分析

spectogram下载

下载声波可视化器 (sonicvisualiser.org)

add layer spectogram

下载后打开文件,在layer里添加spectogram,看到隐藏信息

flag{sp3trogr4mophon3}

欸,这个不对哦,仔细看这个类似o的比m高,所以应该是数字0,又是个坑

flag{sp3tr0gr4m0ph0n3}

3.flag

flag{sp3tr0gr4m0ph0n3}

3.[UTCTF2020]sstv

1.题目概述

2.解题过程

又是一道wav隐写题目

audacity打开看波形,竟然是一道平的

看看频谱

百度题目名称SSTV

好眼熟,好像在哪里见到过(想了一下,原来是高中时候的阅读理解)

搜了一下,QSSTV这个玩意可以解码

Kali安装QSSTV

apt-get install qsstv

下载好后,在终端输入qsstv,系统自动打开qsstv,然后点击Options->configuration->sound->sound input->from file

点击OK,然后点击这个按钮,选择要解码的文件,然后就开始解码啦

解码

3.flag

flag{6bdfeac1e2baa12d6ac5384cdfd166b0}

4.[*CTF2019]otaku

1.题目概述

BUU上少了题目描述

One day,you and your otaku friend went to the comic expo together and he had a car accident right beside you.Before he died,he gave you a USB hard disk which contained this zip.Please find out his last wish.

提示:The txt is GBK encoding.

2.解题过程

010打开

藏着其他的zip,kali分离

binwalk分离文件

寻找docx隐藏内容

爆破无果,去前面那个docx文档里寻找线索

很明显,这个文档隐藏了一些内容

在搜索框里搜索显示/隐藏编辑标记,显示出隐藏的内容

对比发现,隐藏的内容为:

Hello everyone, I am Gilbert. Everyone thought that I was killed, but actually I survived. Now that I have no cash with me and I’m trapped in another country. I can't contact Violet now. She must be desperate to see me and I don't want her to cry for me. I need to pay 300 for the train, and 88 for the meal. Cash or battlenet point are both accepted. I don't play the Hearthstone, and I don't even know what is Rastakhan's Rumble."

去掉不可见符号

利用记事本的替换功能

Hello everyone, I am Gilbert. Everyone thought that I was killed, but actually I survived. Now that I have no cash with me and I’m trapped in another country. I can't contact Violet now. She must be desperate to see me and I don't want her to cry for me. I need to pay 300 for the train, and 88 for the meal. Cash or battlenet point are both accepted. I don't play the Hearthstone, and I don't even know what is Rastakhan's Rumble."

然后联想刚刚那个加密压缩包里的last words.txt,猜想是明文攻击,把这段文字改成GBK编码(BUU上漏掉了这一个关键性的提示)

转换为GBK

# -*- coding:GBK -*-

f = open('flag1.txt','w')

s = "Hello everyone, I am Gilbert. Everyone thought that I was killed, but actually I survived. Now that I have no cash with me and I’m trapped in another country. I can't contact Violet now. She must be desperate to see me and I don't want her to cry for me. I need to pay 300 for the train, and 88 for the meal. Cash or battlenet point are both accepted. I don't play the Hearthstone, and I don't even know what is Rastakhan's Rumble."

f.write(s)

f.close()

打开转换后的GBK文件

这里有个疑问,为什么文件内容没有变化?

然后把flag1.txt压缩为zip,然后观察CRC校验码和前面那个flag.zip的是否一样(这个真没想到)

明文攻击

确实是一样的,那就开始明文攻击(打开的文件是加密的文件,明文文件是刚刚flag.txt压缩后的zip)

解压

txt文件和前面那个txt文件内容一样,010打开图片没发现什么东西

看看是不是LSB隐写

png数据隐写

法一:stegsolve-data extract

法二:kali工具zsteg

(查看PNG和BMP文件隐藏信息的工具)

3.flag

flag{vI0l3t_Ev3rg@RdeN}

5.[HDCTF2019]你能发现什么蛛丝马迹吗

1.题目概述

2.解题过程

文件类型是光盘映像文件,百度一下IMG文件

010打开

这是一种新的题型,要用到另一个内存取证工具:

volatility

下载地址:(我下载的版本2.6)

Release Downloads | Volatility Foundation

然后把刚刚那个memory.img文件放到解压之后的文件夹里

查看文件的Profile

我把程序名字稍微改了一下,只要输入的和存在的同名就行

打开终端,输入

./volatility.exe -f memory.img imageinfo

可以看到,profile是Win2003SP1x86

查看进程

./volatility -f memory.img --profile=Win2003SP1x86 pslist

看完镜像直接查看这个镜像上有那些进程正在运行 pslist应该比较好理解就是进程的列表的意思。

可以看到有许多exe文件,并且最后一个用过的程序名字还是DumpIt这个敏感的名字,

查看cmd命令使用记录

./volatility -f memory.img --profile=Win2003SP1x86 cmdscan

发现flag字符,继续

将DumpIt.exe这个程序dump下来

./volatility -f memory.img --profile=Win2003SP1x86 memdump -p 1992 --dump-dir=./

产生一个1992.dmp文件,010打开

没发现什么东西,

foremost分离

打开有好多东西

(binwalk会出来一大串奇奇怪怪的东西)

打开分离出的文件

png

第一个扫码:

jfXvUoypb8p3zvmPks8kJ5Kt0vmEw0xUZyRGOicraY4=

第二个扫码:

内容是一样的

打开有字符的图片

iv是偏移量,有key有字符,有偏移量,是AES加密

jfXvUoypb8p3zvmPks8kJ5Kt0vmEw0xUZyRGOicraY4=

key:Th1s_1s_K3y00000

iv:1234567890123456

在线AES加密解密、AES在线加密解密、AES encryption and decryption–查错网 (chacuo.net)

flag{F0uNd_s0m3th1ng_1n_M3mory}

3.flag

flag{F0uNd_s0m3th1ng_1n_M3mory}

6.真的很杂

1.题目概述

2.解题过程

只有一张图片,010打开

结尾发现zip,binwalk分离(foremost分离出一堆没用的照片)

安卓逆向介绍

Android逆向 - 简书 (jianshu.com)

准备安卓逆向工具

1.apktool——可以反编译软件的布局文件、图片等资源,方便大家学习一些很好的布局; 2.dex2jar——将apk反编译成java源码(classes.dex转化成jar文件); 3.jd-gui——查看APK中classes.dex转化成出的jar文件,即源码文件。

apktool(选最新版)

iBotPeaches / Apktool / Downloads — Bitbucket 然后把这个网址里的东西复制粘贴保存为apktool.bat,和上面下载的apktool.jar放在同一个目录下

https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/windows/apktool.bat

安装工具dex2jar2.0

Download dex2jar from SourceForge.net

下载jd-gui(我下载的win版)

程序:

Java Decompiler (java-decompiler.github.io)

jar文件

java-decompiler/jd-gui: A standalone Java Decompiler GUI (github.com)

开干

把class.dex文件放到dex2jar目录下,打开终端,输入

./d2j-dex2jar.bat classes.dex

然后会生成一个jar文件

或者直接把class.dex文件拖到d2j-dex2jar.bat上面,也能生成这个文件

然后把这个jar文件用jd-gui.exe打开

不难看到flag(我近视都能发现,你不会看不到吧?)

flag{25f991b27f" + i + "dc2f7a82a2b34" + j + "86e81c4}

i和j代表题目中要我们爆破的字母和数字

多试几十次之后,得到flag

flag{25f991b27fcdc2f7a82a2b34386e81c4}

3.flag

flag{25f991b27fcdc2f7a82a2b34386e81c4}

7.[INSHack2019]INSAnity

1.题目概述

2.解题过程

这不秒得flag?

3.flag

flag{YouRe_Crazy_and_I_Love_it}

8.greatescape(TLS密钥流量分析)

1.题目概述

2.解题过程

wireshark打开

追踪TCP流,总共有大约90个流,一个个的看一看,在18发现了ssc key

紧接着,在19发现了私钥

先把私钥存下来

继续看看剩下的几十条流,没有什么线索

分析文件猜测,这应该在向ftp服务器传送私钥,我们得到了私钥,就可以解密TLS报文,

具体步骤:

编辑->首选项->protools->TLS->edit->点击±>添加key.txt

下拉,找到TLS

然后继续追踪TLS流,会默认出来tcp.stream eq80/81 发现flag

3.flag

flag{OkThatWasWay2Easy}

9.[INSHack2019]Sanity

1.题目概述

2.解题过程

签到题

flag{Welcome}

3.flag

flag{Welcome}

10.[INSHack2017]insanity-

1.题目概述

2.解题过程

签到题

flag{Youre_crazy_I_like_it}

3.flag

flag{Youre_crazy_I_like_it}

11.key不在这里

1.题目概述

2.解题过程

扫一扫看看

扫码

内容是一个网址

https://cn.bing.com/search?q=key%E4%B8%8D%E5%9C%A8%E8%BF%99%E9%87%8C&m=10210897103375566531005253102975053545155505050521025256555254995410298561015151985150375568&qs=n&form=QBRE&sp=-1&sc=0-38&sk=&cvid=2CE15329C18147CBA4C1CA97C8E1BB8C

这个网址是搜索"key不在这里",用bing随便找个搜索key这个单词的网址对比一下看看有什么不同

正常:

https://cn.bing.com/search?q=key&qs=n&form=QBRE&sp=-1&pq=ke%27y&sc=8-4&sk=&cvid=5C1A3D12026745D28A50BB2341A4B04D

题目:

https://cn.bing.com/search?q=key%E4%B8%8D%E5%9C%A8%E8%BF%99%E9%87%8C&m=10210897103375566531005253102975053545155505050521025256555254995410298561015151985150375568&qs=n&form=QBRE&sp=-1&sc=0-38&sk=&cvid=2CE15329C18147CBA4C1CA97C8E1BB8C

找不同

(绿色代表共同点,红色标出的就有点不正常)

10210897103375566531005253102975053545155505050521025256555254995410298561015151985150375568

数了一下,一共有92个数字,而且f的ascii码值为102,l的ascii码值为108,接下来把这一串ascii码转成字符

ascii码转成字符

python的chr()函数

ascii转为字符

s = '10210897103375566531005253102975053545155505050521025256555254995410298561015151985150375568'

temp = ''

while len(s):

if int(s[:3]) < 127:

temp += chr(int(s[:3]))

s = s[3:]

else:

temp += chr(int(s[:2]))

s = s[2:]

print(temp)

flag%7B5d45fa256372224f48746c6fb8e33b32%7D

%7B和%7D分别是是URL编码中的”{“和“}”

3.flag

flag{5d45fa256372224f48746c6fb8e33b32}

12.[MRCTF2020]小O的考研复试

1.题目概述

2.解题过程

百度一下取模运算(数学不好)

2的19260817次方>0,1e9+1也大于0,所以flag就是2的19260817次方除1e9+7的余数

1e9+7=100000007,代码敲起来

C或C++可能需要好久才能跑出来结果,不如万能的python,几秒就跑出来了

a=2

for i in range(19260816):

a = a * 10 + 2

a%=(1e9+7)

print(a)

577302567.0

去除小数点,flag{577302567}

3.flag

flag{577302567}

13.[INSHack2018]Self Congratulation

1.题目概述

2.解题过程

010打开,数据比较正常

仔细看,这里好像有个二维码的一角

不是LSB隐写,不是CRC隐写,binwalk也没有,就没有什么思路了

百度大佬的wp,又有一个新的脑洞,这个小角是11x6的长方形,把黑色小块当作1,白色小块当作0,如下:

00110001001

10010001100

11001101000

01101010011

01100011011

10011100000

然后把它们连起来

001100010011001000110011001101000011010100110110001101110011100000

然后二进制转字符

网站

汉字二进制转换器,字符与2-36进制相互转换 - 千千秀字 (qqxiuzi.cn)

python脚本

s = '001100010011001000110011001101000011010100110110001101110011100000'

def bin2asc(string):

temp = ''

for i in range(int(len(string) / 8)):

temp += chr(int(string[i * 8: i * 8 + 8], 2))

print(temp)

return

bin2asc(s)

3.flag

flag{12345678}

14.[GWCTF2019]huyao(待定)

1.题目概述

2.解题过程

两张一摸一样的图片。猜测是盲水印,这是以前做过的一道盲水印的题

但是这个方法不适用于这次的盲水印,原来这是频域盲水印,那就换一个方法吧

借鉴一下大佬的脚本

# coding=utf-8

import cv2

import numpy as np

import random

import os

from argparse import ArgumentParser

ALPHA = 5

def build_parser():

parser = ArgumentParser()

parser.add_argument('--original', dest='ori', required=True)

parser.add_argument('--image', dest='img', required=True)

parser.add_argument('--result', dest='res', required=True)

parser.add_argument('--alpha', dest='alpha', default=ALPHA)

return parser

def main():

parser = build_parser()

options = parser.parse_args()

ori = options.ori

img = options.img

res = options.res

alpha = options.alpha

if not os.path.isfile(ori):

parser.error("original image %s does not exist." % ori)

if not os.path.isfile(img):

parser.error("image %s does not exist." % img)

decode(ori, img, res, alpha)

def decode(ori_path, img_path, res_path, alpha):

ori = cv2.imread(ori_path)

img = cv2.imread(img_path)

ori_f = np.fft.fft2(ori)

img_f = np.fft.fft2(img)

height, width = ori.shape[0], ori.shape[1]

watermark = (ori_f - img_f) / alpha

watermark = np.real(watermark)

res = np.zeros(watermark.shape)

random.seed(height + width)

x = range(height / 2)

y = range(width)

random.shuffle(x)

random.shuffle(y)

for i in range(height / 2):

for j in range(width):

res[x[i]][y[j]] = watermark[i][j]

cv2.imwrite(res_path, res, [int(cv2.IMWRITE_JPEG_QUALITY), 100])

if __name__ == '__main__':

main()

把这个文件存为blind watermask.py,

pip install opencv-python==4.2.0.32 -i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com

在当前目录下打开终端,输入命令:

python2 blindwatermark.py --original --huyao.png --image stillhuyao.png --result output.png

python2 blindwatermark.py --original huyao.png --image stillhuyao.png --result res.png

python2 blindwatermark.py --original huyao.png --image stillhuyao.png --result out.png

python encode.py --image huyao.png --watermark stillhuyao.png --result out.png

pip install --target=/home/xxxx/anaconda3/envs/huayan/lib/python3.6/site-packages opencv-python==4.2.0.34 -i https://pypi.doubanio.com/simple

python encode.py --image huyao.png --watermark stillhuyao.png --result put.png

python decode.py --original huyao.png --image stillhuyao.png --result put.png

python2 water2.py --original huyao.png --image stillhuyao.png --result out.png

3.flag

15.[BSidesSF2019]table-tennis

1.题目概述

2.解题过程

仔细观察协议,再ICMP下发现一些base64字符

Q1RG

e0p1c3RB

UzBuZ0Fi

MHV0UDFu

Z1Awbmd9

一共有这些(注意重复)

Q1RG/e0p1c3RB/UzBuZ0Fi/MHV0UDFu/Z1Awbmd9/

Q1RGe0p1c3RBUzBuZ0FiMHV0UDFuZ1Awbmd9

找个网站解base64

3.flag

flag{JustAS0ngAb0utP1ngP0ng}

16.[GKCTF 2021]你知道apng吗?

1.题目概述

2.解题过程

我还真不知道APNG是啥,百度

chrome安装apng插件后可以打开

是一个短的动画,并且隐藏着一个二维码,chrome没法编辑,那就用另一个新工具吧

apng图片分割器-APNG图片分解器(APNG Disassembler)下载v2.7 绿色版-绿色资源网 (downcc.com)

打开文件,会自动分解,真不错

得到了4张二维码

1

2

3

4

第一张有点问题,需要借助强大的PS扭曲一下(使劲拉,一次不行再弄一次)

搞得差不多就可以扫出来了

第2张扫码

-9b9d

第3张扫码:

-ad20

最后一个

拼起来就是

flag{a3c7e4e5-9b9d-ad20-0327-288a235370ea}

这里有一个地方需要注意,如果直接用kinovea打开或potplayer逐帧查看,非常难发现第2张二维码(别问我怎么知道的,不信你可以试试看)

3.flag

flag{a3c7e4e5-9b9d-ad20-0327-288a235370ea}

17.[RCTF2019]disk

1.题目概述

2.解题过程

010打开

搜索CTF(或者RCTF),得到第一部分flag

ctf{unseCure_quick_form4t_vo1ume

然后7Z打开,很神奇的得到了一个fat文件,这我属实是没想到

FAT文件就可使用VeraCrypt进行挂载,VeraCrypt下载地址

Download VeraCrypt from SourceForge.net

VeraCrypt打开fat文件

挂载

因为密码没有什么线索,所以考虑最简单的rctf或者题目名称,密码是rctf,然后出现这个:

然后打开挂载的E盘

(原先电脑里并没有,是新建的)

打开password.txt

图片名暗示先忽略图片线索,并且password中出现了另一个密码,这就有意思了

再次挂载

这是一个容易忽视的点。那就是在挂载的时候,不同的密码可以进入不同的文件系统

然后又创建了一个磁盘,奇怪的是无法打开,必须格式化(坑),010也打开驱动器也打不开

搜了一下,原来这代表磁盘被加密了,牛

winhex可以打开,具体步骤:

可能会报错,不用在意,它还是可以打开

随便一拉就看到了flag的另一半(狗头)

_and_corrupted_1nner_v0lume}

注意

这里有一个弯路,就是直接打开磁盘的时候会提示必须格式化才能继续使用嘛,这个时候千万不要格式化,一旦格式化了这个磁盘名字就无法继续使用了,必须换一个,我就是格式化之后有建了一次,结果打开就是这个样子:

3.flag

flag{unseCure_quick_form4t_vo1ume_and_corrupted_1nner_v0lume}

18.[ACTF新生赛2020]frequency

1.题目概述

2.解题过程

根据题目名称frequency与文件内容猜测应该是字频方向

如果打开文档是空白的,就搜索

显示隐藏内容

a2draGxmY290bnRpdWZwZ2hodGN3dWprY2ttb3ducGNrbXdseWd0bHBtZmtneWFhaWh1Y2RsYXRveXVjb2lnZ3JwbGt2a2Ftcmt0cXp4ZW1taXdrbGh1YWVrY2VvbHBvY2ZtdGFobWdmbWF2YWpuYmNwbWx0anRwdWZqY2FwY3RvanBqYmZmYmpid2h1YWxnZ3lqbmFtY2JmeWFjamJheGtpeGxtbXFpa3NtcHRxeW9qZXJ0ZmVrdGR4ZHh4YnRyeGNhbmd5bXNpbWh2dXdrdGV4c2dscnRwZ2FrdGJtZnVjZ3ZubXRqdWZvZWt5bXRsaW14ZGlqanB4eWl0YWJwbWt1Y2NubGtwb2V0Z2NkY3Bvc2tpenZ5eHJ0enhyYXh0bm9paHFjeGZvYWFhbHBhanlja2VrYnljZnZqb21sbGthamd5bWdmZGNycGVxa2xmc2NtZWppY3BqaWtjcHBhY3h5ZXZma3ljcHBia2R6Y2ZsbGlrcW5pdGNrYmhqb3JuZGhzb21mdHlwYWhwcW94cnlpbWhmbGNoY21rb3JldG1yb3RrYXJjanRobWZ0aWxpam55a3V0aWhienR0dW1zbmdmdGxtcmJmZmx0ZndjbmptZmF0bGZiemxva3RscHBsbWZpY29rcHBucGFjbWZ1Z21wdW5kdnRvbXdldmNqc2dhamdmZXF1dXBhaWV0eW5mamJicGpzbHZ5bmFmdG1scHBka3R0b2Z1emppanhpdGJmaXJtb3ZwemVraXJic2Zqc2d6bHVrb2x5dm9obXZnY3BrdGhzeGZ6bW1ibm1sZHp5dWljZHZrbXpiYXlidG9yY2ZvdHRkYW1jY25iYXBucmd4bGN5cGh5Zm5jZXhidmRubG9rZ295aWxwcmxvbnNodGNranR4bmFiamhsbWJwZGNtaGtqbmxnbXRnam5qYWtyaXpsbHBtbWFscHhhbXVuaW51cGtwZGlhcHNzbXZrZGp2Z2l5b2R1bXBuYXBsamtqYmNmaHRoc2tpb2twZ3R0eWhubmRzeGtxanp2dmRvc2VwcG9pZ3l0bW5uYXZjdG9wZHlpeHZiZG9zb2JtY3ViaXVhanhoeWZrdnJremdjdXlpbHB2YXdheW5xYWFwbGJrd2lpeHJjdGN0bGt4ZmpscGVhbW1qbmF1amNvdWlmbXZpa2ZpbXJvYXF0Y3RjZm1hdWJnYWdva2FyZnFmaGVtb3NydHlmb3B1a3VkY2FhaW1oZGZvZ25oa3JjZWxwY2F0Y3RweWpsYXZva2xnY2xhdGx0bXR6eWdwZWhma3poY3R6bmdtb2ZjaXpsdm54dG5sdWFqbHRvdmNqYWp1YnphdHBlaGhma25uZ2dwbHlsaXZmZWFpZHJteWp0YWNhbXhjbmtmeXN0d2ZuZmx5bmJta2NrYXJ4YWlzcGpsa3ZjdHZrbHh1bmNmcGJ4dmlyaXFleXBtdXZ1bHZsamNrY3lwcHRwdmV0b3hobWlwYmlsbmplb3drd3VjdG9rbmFmcHdvYXBmdGNsemhwaHhjY2F0dGh1bXZ3aHpvbWFmd3Fxbmxzb3lhYnV0bHpwaWF0Zm1tYWprcmR2bGN6d2pwc3Nwb2FiaWZpcGhrb2NocHRrYXRrYWZlb255YmZpdmVjbGR6b2ZhdGV0Z2FsaGFmYW1vYXlvc291bm5hZmlhdGNqdGl3b29sYWNya2N1YWRwdXRreWxweXBiZ2ZlcHdwc25jd2tjd2xsYXJ5anNjYW5id3BkcHpicHR1dG5sbm9wd3BpdGJsb3RsbHppZmtsYWF1cmpwaWFqZnB0a2ZteHBic3VjdmpzZ21jYWxhbnRyc2NrYmt1eWZnYWFrZmFjbmxkdXZxZXR5amdqbW5hZWFjbmdheGNuYW1qbWlna2tpdW1sbmR3Y2ttdWFuYW52cnji*znp4enl1dWVob25lbWxjanp1dm9hanVmZGdqamp

jZ21ucHRmdXVjdWJjdGpoYW1sb2xmaG9pZnZia2themNwb3pjeXVjYnJnb2picG5haGNneXV0dGR2bXR0dndqbWhic2ptYmJhdmNkbHlob3Fqb21wY3B2aHRrb2FpcnZ0bWtmZnlhdGttcHR1dW9vb2xncG5udWVsaGZodnZpc3Vrd3lubWlhY25sbHVtaHRqZWt1YXV1cGxyeGtpZXB1anhsaWNma2NiY2htbmdsZ3BsaWhteWNybnNvbWF3dWZ1b29tdXVuaGRvb2FydWRhbW9hbW9ocW9vY2Z1cGp1aWFieHh1dnl2bm9zb3Vvb3Zha2xjZmt0eXJmYWdmYXl2cHVmdnBiZ3RhZmVraXBpY292dGZ0bnV4c2phdmpkcWt2ZnVpa2x0bWRrYmJua3BhZnhycXBmZ2N0dmFzY3VqamN1Y2h1YXpjaXVtdHRkbmF3aWhtbW9qZmJoeHZvbXRmcGJmaHR2aXdsYXVlb2dwcG1qc3BjYWxmaGNhcmtsYmlzcGh0anBhbmhsaXNwbnRza2tjbGpnZ2tjenRmaG5lY25wdGlmZnRyZG10amZla2ZpdGthc2RnbmVscHVoYmZpbXB1Y2JrcGtjbXhsZmtwaWlqdmh0amtzeWx6cm9vZmFjeGNscGpuaGJpcmN5ZGp0Y2xqZG9ibHlyeW1hdGdoaWZvam1qanNla29vbW9mY2FjdGF2ZmN5Zm11Znhoc3Rqd3VwYmpreW9nbnlyeXBseXBxbGF5eW1veHRhbnFkcHVyYnd6cGxsb2traGhtYW5kam5hdGNibGtjb3Rna2x1dHR3YmRhdHFybWF6cHJ2YXd6anhlZmhqdGRraWt1cmxsY2xjam9naG1sd3RhbWRkY2NucXVyb3Jha2N5b2JsYXJ6YWNtbnFjbWV0dHVheWF1eWl2c21ma25uYW5sdGNtaWdmcmdhYmlwdG50aG1tdXRwaWJ5bHJhdGhqY2doY2ZtbG92cGNudHFwZW96bG90ZGtlaW9jZmtjaXZ1eWx6Ympvb3hjc2FjbmdkdXZ4dG50aGphZXBhdX1rbGFocGNtdnppY2twYWFwaG9jZ2lvZ2p0dnB0Z2poZG9udW5scGFvbG5kcWJxZmRtYnBqam94Ym9tbGlreXVpcG54cXh6Y2lmb3JhaGhldXl5dHpoanV0Z2Z3dHVscmpjZnhvaWFneWpmYnBqaWFrZ3l0eGJmbnBsZnBxd3RkaXFuaXR2dmF1amRqbGlmamlvcnltdmZ4bXhnb3JpY3ljZGZob2ZiYnlnZmxhdGlyamRpZGFkcXZpa2pvY2l5ZmR6aHJvZ255eWlia2dubnZobWpsb2xhdndmaWpjZ2dma3B0a3BnY3FmYWZheXNpdHltYWN2a3FweWxoaGJ1Ymh4c2x1emN5dmxvcmlwdGxwZmxjdWljcG5mc2hpeXh2a2tiY2p5dWtvdGFsZmNpcGhjZGd4aWZ0a2xkZ29wanNtdXJ0eWp5cGhia2JmbmJ3YnNvZmlheHRsdWhwbXJmZGFrdWx1cGVhcHZyeXhtYWVwaGF5bmV4emZsbmV2am1pYndvcml0aGh4YmJ5cG1tYWJvYmZuZmNvanR0Y3Jram1naXJ2bWlzdW5mbHVodGVudHJodGVvamtjaGtwZnBhZXNnd2dscWRrdnZudWx1bnF7bG1sdGFscGhvdWZqcGlhbGNmbGZ5ZHZmd3lkb2ZraGFpeWF3bGx3Y2pvYXJxdnpqbGZmZ2xjdGNsYmxwa2JzZmxocnRqZGFvd3ByZGJjdWJmbHlveWJodmh3ZndvZWl0Z254YnpuaWZwbGx4bXN0a251aWhvYmZlZWZra2FreW5uYWNra2NkdWFtZ3N2bnBoY3RmZ3NybnJvZWh2ZW5kYmZpb21xZm14Ym1paWl1bGF2b2dma2dhY2lrYWFtcHBybGpmbXBqY3VhYXNja2l1cWlmY2liamx1dGNtcGF0b2pyanZmeGdsenBvcGpkZ2NoanVqbGtuZnd0cG5qZnBhY3JrcHRmaGNzamdyaXBjcmZjZGFsem5ob25mZGNvaG9zZmhvaGVha250aXRtamZsbmJvcGNsY3hjdWlnb3hja3JiYWxyYWVidGFhcml

查看备注

在备注中又发现了一段Base64字符

0ZWZtemNxaWRvZmd0ZnFnYmFkaWNubWhvdGlvbm9iZnlubGdvenRkYXZ2aW14b2JvdGlra2Z4d2lyb3JwZmNjdXpob3BoZmRjaWVrY2p5b21lamtjZ2Zmam51bmhvcGFkdGZndG1sdA==

再结合文档内容,合并在一起去解码(如果直接统计字频没有什么思路,刚刚试过了)

base64解码

a2draGxmY290bnRpdWZwZ2hodGN3dWprY2ttb3ducGNrbXdseWd0bHBtZmtneWFhaWh1Y2RsYXRveXVjb2lnZ3JwbGt2a2Ftcmt0cXp4ZW1taXdrbGh1YWVrY2VvbHBvY2ZtdGFobWdmbWF2YWpuYmNwbWx0anRwdWZqY2FwY3RvanBqYmZmYmpid2h1YWxnZ3lqbmFtY2JmeWFjamJheGtpeGxtbXFpa3NtcHRxeW9qZXJ0ZmVrdGR4ZHh4YnRyeGNhbmd5bXNpbWh2dXdrdGV4c2dscnRwZ2FrdGJtZnVjZ3ZubXRqdWZvZWt5bXRsaW14ZGlqanB4eWl0YWJwbWt1Y2NubGtwb2V0Z2NkY3Bvc2tpenZ5eHJ0enhyYXh0bm9paHFjeGZvYWFhbHBhanlja2VrYnljZnZqb21sbGthamd5bWdmZGNycGVxa2xmc2NtZWppY3BqaWtjcHBhY3h5ZXZma3ljcHBia2R6Y2ZsbGlrcW5pdGNrYmhqb3JuZGhzb21mdHlwYWhwcW94cnlpbWhmbGNoY21rb3JldG1yb3RrYXJjanRobWZ0aWxpam55a3V0aWhienR0dW1zbmdmdGxtcmJmZmx0ZndjbmptZmF0bGZiemxva3RscHBsbWZpY29rcHBucGFjbWZ1Z21wdW5kdnRvbXdldmNqc2dhamdmZXF1dXBhaWV0eW5mamJicGpzbHZ5bmFmdG1scHBka3R0b2Z1emppanhpdGJmaXJtb3ZwemVraXJic2Zqc2d6bHVrb2x5dm9obXZnY3BrdGhzeGZ6bW1ibm1sZHp5dWljZHZrbXpiYXlidG9yY2ZvdHRkYW1jY25iYXBucmd4bGN5cGh5Zm5jZXhidmRubG9rZ295aWxwcmxvbnNodGNranR4bmFiamhsbWJwZGNtaGtqbmxnbXRnam5qYWtyaXpsbHBtbWFscHhhbXVuaW51cGtwZGlhcHNzbXZrZGp2Z2l5b2R1bXBuYXBsamtqYmNmaHRoc2tpb2twZ3R0eWhubmRzeGtxanp2dmRvc2VwcG9pZ3l0bW5uYXZjdG9wZHlpeHZiZG9zb2JtY3ViaXVhanhoeWZrdnJremdjdXlpbHB2YXdheW5xYWFwbGJrd2lpeHJjdGN0bGt4ZmpscGVhbW1qbmF1amNvdWlmbXZpa2ZpbXJvYXF0Y3RjZm1hdWJnYWdva2FyZnFmaGVtb3NydHlmb3B1a3VkY2FhaW1oZGZvZ25oa3JjZWxwY2F0Y3RweWpsYXZva2xnY2xhdGx0bXR6eWdwZWhma3poY3R6bmdtb2ZjaXpsdm54dG5sdWFqbHRvdmNqYWp1YnphdHBlaGhma25uZ2dwbHlsaXZmZWFpZHJteWp0YWNhbXhjbmtmeXN0d2ZuZmx5bmJta2NrYXJ4YWlzcGpsa3ZjdHZrbHh1bmNmcGJ4dmlyaXFleXBtdXZ1bHZsamNrY3lwcHRwdmV0b3hobWlwYmlsbmplb3drd3VjdG9rbmFmcHdvYXBmdGNsemhwaHhjY2F0dGh1bXZ3aHpvbWFmd3Fxbmxzb3lhYnV0bHpwaWF0Zm1tYWprcmR2bGN6d2pwc3Nwb2FiaWZpcGhrb2NocHRrYXRrYWZlb255YmZpdmVjbGR6b2ZhdGV0Z2FsaGFmYW1vYXlvc291bm5hZmlhdGNqdGl3b29sYWNya2N1YWRwdXRreWxweXBiZ2ZlcHdwc25jd2tjd2xsYXJ5anNjYW5id3BkcHpicHR1dG5sbm9wd3BpdGJsb3RsbHppZmtsYWF1cmpwaWFqZnB0a2ZteHBic3VjdmpzZ21jYWxhbnRyc2NrYmt1eWZnYWFrZmFjbmxkdXZxZXR5amdqbW5hZWFjbmdheGNuYW1qbWlna2tpdW1sbmR3Y2ttdWFuYW52cnji*znp4enl1dWVob25lbWxjanp1dm9hanVmZGdqampjZ21ucHRmdXVjdWJjdGpoYW1sb2xmaG9pZnZia2themNwb3pjeXVjYnJnb2picG5haGNneXV0dGR2bXR0dndqbWhic2ptYmJhdmNkbHlob3Fqb21wY3B2aHRrb2FpcnZ0bWtmZnlhdGttcHR1dW9vb2xncG5udWVsaGZodnZpc3Vrd3lubWlhY25sbHVtaHRqZWt1YXV1cGxyeGtpZXB1anhsaWNma2NiY2htbmdsZ3BsaWhteWNybnNvbWF3dWZ1b29tdXVuaGRvb2FydWRhbW9hbW9ocW9vY2Z1cGp1aWFieHh1dnl2bm9zb3Vvb3Zha2xjZmt0eXJmYWdmYXl2cHVmdnBiZ3RhZmVraXBpY292dGZ0bnV4c2phdmpkcWt2ZnVpa2x0bWRrYmJua3BhZnhycXBmZ2N0dmFzY3VqamN1Y2h1YXpjaXVtdHRkbmF3aWhtbW9qZmJoeHZvbXRmcGJmaHR2aXdsYXVlb2dwcG1qc3BjYWxmaGNhcmtsYmlzcGh0anBhbmhsaXNwbnRza2tjbGpnZ2tjenRmaG5lY25wdGlmZnRyZG10amZla2ZpdGthc2RnbmVscHVoYmZpbXB1Y2JrcGtjbXhsZmtwaWlqdmh0amtzeWx6cm9vZmFjeGNscGpuaGJpcmN5ZGp0Y2xqZG9ibHlyeW1hdGdoaWZvam1qanNla29vbW9mY2FjdGF2ZmN5Zm11Znhoc3Rqd3VwYmpreW9nbnlyeXBseXBxbGF5eW1veHRhbnFkcHVyYnd6cGxsb2traGhtYW5kam5hdGNibGtjb3Rna2x1dHR3YmRhdHFybWF6cHJ2YXd6anhlZmhqdGRraWt1cmxsY2xjam9naG1sd3RhbWRkY2NucXVyb3Jha2N5b2JsYXJ6YWNtbnFjbWV0dHVheWF1eWl2c21ma25uYW5sdGNtaWdmcmdhYmlwdG50aG1tdXRwaWJ5bHJhdGhqY2doY2ZtbG92cGNudHFwZW96bG90ZGtlaW9jZmtjaXZ1eWx6Ympvb3hjc2FjbmdkdXZ4dG50aGphZXBhdX1rbGFocGNtdnppY2twYWFwaG9jZ2lvZ2p0dnB0Z2poZG9udW5scGFvbG5kcWJxZmRtYnBqam94Ym9tbGlreXVpcG54cXh6Y2lmb3JhaGhldXl5dHpoanV0Z2Z3dHVscmpjZnhvaWFneWpmYnBqaWFrZ3l0eGJmbnBsZnBxd3RkaXFuaXR2dmF1amRqbGlmamlvcnltdmZ4bXhnb3JpY3ljZGZob2ZiYnlnZmxhdGlyamRpZGFkcXZpa2pvY2l5ZmR6aHJvZ255eWlia2dubnZobWpsb2xhdndmaWpjZ2dma3B0a3BnY3FmYWZheXNpdHltYWN2a3FweWxoaGJ1Ymh4c2x1emN5dmxvcmlwdGxwZmxjdWljcG5mc2hpeXh2a2tiY2p5dWtvdGFsZmNpcGhjZGd4aWZ0a2xkZ29wanNtdXJ0eWp5cGhia2JmbmJ3YnNvZmlheHRsdWhwbXJmZGFrdWx1cGVhcHZyeXhtYWVwaGF5bmV4emZsbmV2am1pYndvcml0aGh4YmJ5cG1tYWJvYmZuZmNvanR0Y3Jram1naXJ2bWlzdW5mbHVodGVudHJodGVvamtjaGtwZnBhZXNnd2dscWRrdnZudWx1bnF7bG1sdGFscGhvdWZqcGlhbGNmbGZ5ZHZmd3lkb2ZraGFpeWF3bGx3Y2pvYXJxdnpqbGZmZ2xjdGNsYmxwa2JzZmxocnRqZGFvd3ByZGJjdWJmbHlveWJodmh3ZndvZWl0Z254YnpuaWZwbGx4bXN0a251aWhvYmZlZWZra2FreW5uYWNra2NkdWFtZ3N2bnBoY3RmZ3NybnJvZWh2ZW5kYmZpb21xZm14Ym1paWl1bGF2b2dma2dhY2lrYWFtcHBybGpmbXBqY3VhYXNja2l1cWlmY2liamx1dGNtcGF0b2pyanZmeGdsenBvcGpkZ2NoanVqbGtuZnd0cG5qZnBhY3JrcHRmaGNzamdyaXBjcmZjZGFsem5ob25mZGNvaG9zZmhvaGVha250aXRtamZsbmJvcGNsY3hjdWlnb3hja3JiYWxyYWVidGFhcml0ZWZtemNxaWRvZmd0ZnFnYmFkaWNubWhvdGlvbm9iZnlubGdvenRkYXZ2aW14b2JvdGlra2Z4d2lyb3JwZmNjdXpob3BoZmRjaWVrY2p5b21lamtjZ2Zmam51bmhvcGFkdGZndG1sdA==

Base64编码转换工具,Base64加密解密 (qqxiuzi.cn)

复制下来

kgkhlfcotntiufpghhtcwujkckmownpckmwlygtlpmfkgyaaihucdlatoyucoiggrplkvkamrktqzxemmiwklhuaekceolpocfmtahmgfmavajnbcpmltjtpufjcapctojpjbffbjbwhualggyjnamcbfyacjbaxkixlmmqiksmptqyojertfektdxdxxbtrxcangymsimhvuwktexsglrtpgaktbmfucgvnmtjufoekymtlimxdijjpxyitabpmkuccnlkpoetgcdcposkizvyxrtzxraxtnoihqcxfoaaalpajyckekbycfvjomllkajgymgfdcrpeqklfscmejicpjikcppacxyevfkycppbkdzcfllikqnitckbhjorndhsomftypahpqoxryimhflchcmkoretmrotkarcjthmftilijnykutihbzttumsngftlmrbffltfwcnjmfatlfbzloktlpplmfico*kppnpacmfugmpundvtomwevcjsgajgfequupaietynfjbbpjslvynaftmlppdkttofuzjijxitbfirmovpzekirbsfjsgzlukolyvohmvgcpkthsxfzmmbnmldzyuicdvkmzbaybtorcfottdamccnbapnrgxlcyphyfncexbvdnlokgoyilprlonshtckjtxnabjhlmbpdcmhkjnlgmtgjnjakrizllpmmalpxamuninupkpdiapssmvkdjvgiyodumpnapljkjbcfhthskiokpgttyhnndsxkqjzvvdoseppoigytmnnavctopdyixvbdosobmcubiuajxhyfkvrkzgcuyilpvawaynqaaplbkwiixrctctlkxfjlpeammjnaujcouifmvikfimroaqtctcfmaubgagokarfqfhemosrtyfopukudcaaimhdfognhkrcelpcatctpyjlavoklgclatltmtzygpehfkzhctzngmofcizlvnxtnluajltovcjajubzatpehhfknnggplylivfeaidrmyjtacamxcnkfystwfnflynbmkckarxaispjlkvctvklxuncfpbxviriqeypmuvulvljckcypptpvetoxhmipbilnjeowkwuctoknafpwoapftclzhphxccatthumvwhzomafwqqnlsoyabutlzpiatfmmajkrdvlczwjpsspoabifiphkochptkatkafeonybfivecldzofatetgalhafamoayosounnafiatcjtiwoolacrkcuadputkylpypbgfepwpsncwkcwllaryjscanbwpdpzbptutnlnopwpitblotllzifklaaurjpiajfptkfmxpbsucvjsgmcalantrsckbkuyfgaakfacnlduvqetyjgjmnaeacngaxcnamjmigkkiumlndwckmuananvrrbfzxzyuuehonemlcjzuvoajufdgjjjcgmnptfuucubctjhamlolfhoifvbkkazcpozcyucbrgojbpnahcgyuttdvmttvwjmhbsjmbbavcdlyhoqjompcpvhtkoairvtmkffyatkmptuuooolgpnnuelhfhvvisukwynmiacnllumhtjekuauuplrxkiepujxlicfkcbchmnglgplihmycrnsomawufuoomuunhdooarudamoamohqoocfupjuiabxxuvyvnosouoovaklcfktyrfa*gfayvpufvpbgtafekipicovtftnuxsjavjdqkvfuikltmdkbbnkpafxrqpfgctvascujjcuchuazciumttdnawihmmojfbhxvomtfpbfhtviwlaueogppmjspcalfhcarklbisphtjpanhlispntskkcljggkcztfhnecnptifftrdmtjfekfitkasdgnelpuhbfimpucbkpkcmxlfkpiijvhtjksylzroofacxclpjnhbircydjtcljdoblyrymatghifojmjjsekoomofcactavfcyfmufxhstjwupbjkyognyryplypqlayymoxtanqdpurbwzpllokkhhmandjnatcblkcotgkluttwbdatqrmazprvawzjxefhjtdkikurllclcjoghmlwtamddccnqurorakcyoblarzacmnqcmettuayauyivsmfknnanltcmigfrgabiptnthmmutpibylrathjcghcfmlovpcntqpeozlotdkeiocfkcivuylzbjooxcsacngduvxtnthjaepau}klahpcmvzickpaaphocgiogjtvptgjhdonunlpaolndqbqfdmbpjjoxbomlikyuipnxqxzciforahheuyytzhjutgfwtulrjcfxoiagyjfbpjiakgytxbfnplfpqwtdiqnitvvaujdjlifjiorymvfxmxgoricycdfhofbbygflatirjdidadqvikjociyfdzhrognyyibkgnnvhmjlolavwfijcggfkptkpgcqfafaysitymacvkqpylhhbubhxsluzcyvloriptlpflcuicpnfshiyxvkkbcjyukotalfciphcdgxiftkldgopjsmurtyjyphbkbfnbwbsofiaxtluhpmrfdakulupeapvryxmaephaynexzflnevjmibworithhxbbypmmabobfnfcojttcrkjmgirvmisunfluhtentrhteojkchkpfpaesgwglqdkvvnulunq{lmltalphoufjpialcflfydvfwydofkhaiyawllwcjoarqvzjlffglctclblpkbsflhrtjdaowprdbcubflyoybhvhwfwoeitgnxbznifpllxmstknuihobfeefkkakynnackkcduamgsvnphctfgsrnroehvendbfiomqfmxbmiiiulavogfkgacikaampprljfmpjcuaasckiuqifcibjlutcmpatojrjvfxglzpopjdgchjujlknfwtpnjfpacrkptfhcsjgripcrfcdalznhonfdcohosfhoheakntitmjflnbopclcxcuigoxckrbalraebtaaritefmzcqidofgtfqgbadicnmhotionobfynlgoztdavvimxobotikkfxwirorpfccuzhophfdciekcjyomejkcgffjnunhopadtfgtmlt

统计字频

可以用网站,也可以用脚本

网站:

语料库在线–字词频统计 (zhonghuayuwen.org)

序号 字词 出现次数 出现频率 1 a 198 6.2658 2 c 194 6.1392 3 t 188 5.9494 4 f 180 5.6962 5 p 174 5.5063 6 l 170 5.3797 7 o 164 5.1899 8 k 159 5.0316 9 m 152 4.8101 10 i 145 4.5886 11 j 139 4.3987 12 n 133 4.2089 13 u 128 4.0506 14 h 116 3.6709 15 b 109 3.4494 16 y 106 3.3544 17 g 100 3.1646 18 v 92 2.9114 19 r 87 2.7532 20 d 79 2.5 21 x 73 2.3101 22 e 68 2.1519 23 s 61 1.9304 24 z 54 1.7089 25 w 48 1.519 26 q 41 1.2975 27 { 1 0.0316 28 } 1 0.0316

显然,flag是按照字频从大到小排列的({和}除外)

flag{plokmijnuhbygvrdxeszwq}

脚本

import re

file = open('18.txt') #根据路径自行调整

line = file.readlines()

file.seek(0,0)

file.close()

result = {}

for i in range(97,123):

count = 0

for j in line:

find_line = re.findall(chr(i),j)

count += len(find_line)

result[chr(i)] = count

res = sorted(result.items(),key=lambda item:item[1],reverse=True)

num = 1

for x in res:

print('频数第{0}: '.format(num),x)

num += 1

不过使用脚本会有一点点小问题,那就是{和}没有给统计上(不知是因为频率太低还是特殊字符的愿因,这时就需要我们给它加上了

频数第1: (‘a’, 198) 频数第2: (‘c’, 194) 频数第3: (‘t’, 188) 频数第4: (‘f’, 180) 频数第5: (‘p’, 174) 频数第6: (‘l’, 170) 频数第7: (‘o’, 164) 频数第8: (‘k’, 159) 频数第9: (‘m’, 152) 频数第10: (‘i’, 145) 频数第11: (‘j’, 139) 频数第12: (‘n’, 133) 频数第13: (‘u’, 128) 频数第14: (‘h’, 116) 频数第15: (‘b’, 109) 频数第16: (‘y’, 106) 频数第17: (‘g’, 100) 频数第18: (‘v’, 92) 频数第19: (‘r’, 87) 频数第20: (‘d’, 79) 频数第21: (‘x’, 73) 频数第22: (‘e’, 68) 频数第23: (‘s’, 61) 频数第24: (‘z’, 54) 频数第25: (‘w’, 48) 频数第26: (‘q’, 41)

flag{plokmijnuhbygvrdxeszwq}

3.flag

flag{plokmijnuhbygvrdxeszwq}

19.很好的色彩呃?

1.题目概述

2.解题过程

实物与描述严重不符(狗头)

PS打开,具体操作步骤如下:

1

8b8b61

2

8b8b61

3

8b8b70

4

8b8b6a

5

8b8b65

6

8b8b73

8b8b618b8b618b8b708b8b6a8b8b658b8b73

16进制,直接放到010里

aapjes

包上flag衣服·就好了

flag{aapjes}

3.flag

flag{aapjes}

20.[INSHack2019]gflag

1.题目概述

2.解题过程

打开description.md

这段话的翻译是:

https://static.ctf.insecurity-insa.fr/a7572eb34ba9700b39f1ba7f5869bf301b67d406.tar.gz

网址打不开,再看看gflag

根据翻译的提示,这应该和编程有关,这个文件就很可疑,有大量重复性的语句

在维基百科上找到了一种很相似的

看了看代码示例,应该就是G代码

找个网站运行

NC Viewer // GCode Viewer and Machine Simulator

flag{3d_pr1nt3d_fl49}

3.flag

flag{3d_pr1nt3d_fl49}

21.我爱Linux

1.题目概述

2.解题过程

图片名称有点可疑。是base16,但是没解出来什么

b0d3e5f34e36b189b47a1a57a0a43ba4

010打开图片

文件头似乎有点问题,尝试着修复了一下,把png文件头给删掉

修复文件头

重新打开

很明显文件尾后面有一些东西

存下来(直接复制出了点问题),存为txt文件

保存末尾多余数据

打开

虽然是乱码,但是我们可以把它转化一下,这是序列化文件

转化序列化文件

python脚本

import pickle #pickle库

fp = open("211.txt","rb+") #打开序列化文件

fw = open("pickle.txt", "w") #开保存文件

a=pickle.load(fp) #反序列化文件编译

pickle=str(a) #转换成字符串要不然不能保存

fw.write( pickle ) #写入文件

fw.close() #关闭文件

fp.close()

打开pickle

是一些类似于坐标的东西,用脚本搞一下

fw = open("pickle.txt","r")

text=fw.read( )

i=0

a=0

while i

if(text[i]==']'):

print('\n')

a=0

elif(text[i]=='('):

if(text[i+2]==','):

b=text[i+1]

d=text[i+1]

b=int(b)-int(a)

c=1

while c

print(" ", end="")

c += 1

print(text[i+5], end="")

a=int(d)

else:

b=text[i+1]+text[i+2]

d=text[i+1]+text[i+2]

b=int(b)-int(a)

c=1

while c

print(" ", end="")

c += 1

print(text[i+6], end="")

a=int(d)

i +=1

打开图形

不得不说,这也太考验想象力了,需要多试几次才能得到,尤其是9和该选哪个,还有6和b

flag{a273fdedf3d746e97db9086ebbb195d6}

再搜一下sl命令

感觉好有意思,试试看

打开kali,以root身份打开终端,输入

apt install sl

然后输入sl,如果出现了一辆会跑的小火车,表明安装成功,如下:

哇,忽然发现typora也可以放视频,真不错

3.flag

flag{a273fdedf3d746e97db9086ebbb195d6}

22.大流量分析(一)未做

1.题目概述

2.解题过程

随便打开一个看看,题目要求我们获得黑客的IP地址,黑客攻击时会产生大量的数据包。利用wireshark统计工具分析一下

然后统计-端点,同样发现183.129.152.140的包数最多

最后统计-IPv4 Statistics-ALL Address。统计下IP,183.129.152.140的数量最多。

所以,183.129.152.140应该就是黑客的IP。分析其它数据包也印证了这个结果

3.flag

flag{183.129.152.140}

23.[INSHack2018]INSanity

1.题目概述

2.解题过程

打开即可看到flag

3.flag

flag{let_the_game_begin!}

24.[DDCTF2018]流量分析

1.题目概述

2.解题过程

txt

90c490781f9c320cd1ba671fcb112d1c

和前面的一道题很像

导出对象,

在IMF里发现:

存下来,

打开(10).eml,在这个邮件里发现一张有base64的图片,这里面的内容应该就是密钥了

使用在线把它提取出来,可以大致看一下是否有错误

在线识别图像

Free Online OCR - convert PDF to Word, Images to text, JPEG to Word

-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQDCm6vZmclJrVH1AAyGuCuSSZ8O+mIQiOUQCvN0HYbj8153JfSQ LsJIhbRYS7+zZ1oXvPemWQDv/u/tzegt58q4ciNmcVnq1uKiygc6QOtvT7oiSTyO vMX/q5iE2iClYUIHZEKX3BjjNDxrYvLQzPyGD1EY2DZIO6T45FNKYC2VDwIDAQAB AoGAbtWUKUkx37lLfRq7B5sqjZVKdpBZe4tL0jg6cX5Djd3Uhk1inR9UXVNw4/y4 QGfzYqOn8+Cq7QSoBysHOeXSiPztW2cL09ktPgSlfTQyN6ELNGuiUOYnaTWYZpp/ QbRcZ/eHBulVQLlk5M6RVs9BLI9X08RAl7EcwumiRfWas6kCQQDvqC0dxl2wIjwN czILcoWLig2c2u71Nev9DrWjWHU8eHDuzCJWvOUAHIrkexddWEK2VHd+F13GBCOQ ZCM4prBjAkEAz+ENahsEjBE4+7H1HdIaw0+goe/45d6A2ewO/lYH6dDZTAzTW9z9 kzV8uz+Mmo5163/JtvwYQcKF39DJGGtqZQJBAKa18XR16fQ9TFL64EQwTQ+tYBzN +04eTWQCmH3haeQ/0Cd9XyHBUveJ42Be8/jeDcIx7dGLxZKajHbEAfBFnAsCQGq1 AnbJ4Z6opJCGu+UP2c8SC8m0bhZJDelPRC8IKE28eB6SotgP61ZqaVmQ+HLJ1/wH /5pfc3AmEyRdfyx6zwUCQCAH4SLJv/kprRz1a1gx8FR5tj4NeHEFFNEgq1gmiwmH 2STT5qZWzQFz8NRe+/otNOHBR2Xk4e8IS+ehIJ3TvyE= -----END RSA PRIVATE KEY-----

手动检查一下是否有错误,按照正确格式保存一下,存为key.txt,一定要确保密钥是对的,否则后面做不出来

-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQDCm6vZmclJrVH1AAyGuCuSSZ8O+mIQiOUQCvN0HYbj8153JfSQ LsJIhbRYS7+zZ1oXvPemWQDv/u/tzegt58q4ciNmcVnq1uKiygc6QOtvT7oiSTyO vMX/q5iE2iClYUIHZEKX3BjjNDxrYvLQzPyGD1EY2DZIO6T45FNKYC2VDwIDAQAB AoGAbtWUKUkx37lLfRq7B5sqjZVKdpBZe4tL0jg6cX5Djd3Uhk1inR9UXVNw4/y4 QGfzYqOn8+Cq7QSoBysHOeXSiPztW2cL09ktPgSlfTQyN6ELNGuiUOYnaTWYZpp/ QbRcZ/eHBulVQLlk5M6RVs9BLI9X08RAl7EcwumiRfWas6kCQQDvqC0dxl2wIjwN czILcoWLig2c2u71Nev9DrWjWHU8eHDuzCJWvOUAHIrkexddWEK2VHd+F13GBCOQ ZCM4prBjAkEAz+ENahsEjBE4+7H1HdIaw0+goe/45d6A2ewO/lYH6dDZTAzTW9z9 kzV8uz+Mmo5163/JtvwYQcKF39DJGGtqZQJBAKa18XR16fQ9TFL64EQwTQ+tYBzN +04eTWQCmH3haeQ/0Cd9XyHBUveJ42Be8/jeDcIx7dGLxZKajHbEAfBFnAsCQGq1 AnbJ4Z6opJCGu+UP2c8SC8m0bhZJDelPRC8IKE28eB6SotgP61ZqaVmQ+HLJ1/wH /5pfc3AmEyRdfyx6zwUCQCAH4SLJv/kprRz1a1gx8FR5tj4NeHEFFNEgq1gmiwmH 2STT5qZWzQFz8NRe+/otNOHBR2Xk4e8IS+ehIJ3TvyE= -----END RSA PRIVATE KEY-----

然后按照以前那个greatspace那道题类似的步骤添加密钥

添加TLS密钥

追踪HTTP流

发现flag

flag{0ca2d8642f90e10efd9092cd6a2831c0}

3.flag

flag{0ca2d8642f90e10efd9092cd6a2831c0}

25.[MRCTF2020]摇滚DJ

1.题目概述

2.解题过程

听了一下,真难听,

audacity打开,波形是平的

和前面那道题有点像

kali的qsstv工具

安装qsstv

apt install qsstv

安装成功后,在终端下输入qsstv,自动打开

下载好后,在终端输入qsstv,系统自动打开qsstv,然后点击Options->configuration->sound->sound input->from file

点击OK,然后点击这个按钮,选择要解码的文件,然后就开始解码啦

flag{r3ce1ved_4n_img}

3.flag

flag{r3ce1ved_4n_img}

26.[XMAN2018]排位赛]file

1.题目概述

2.解题过程

看来又是一道内存取证题

查看基本信息

./volatility -f 26.img imageinfo

奇怪了,怎么没有结果

那就要使用另一个程序来处理了

trid下载与安装,参考

(11条消息) 文件类型识别工具:TrID(trid)下载安装及使用_Red snow的博客-CSDN博客

然后就可以进行分析了

trid分析文件

./trid 26.img

可以看到可能的文件类型

没发现什么,再去虚拟机挂载看看

mount挂载

命令:

mount 26.img /mnt

进行挂载,不成功的话可以加个sudo或者多试几次,直至输入ls出现文件

输入cd /mnt,再输入ls出现文件表明挂载成功

然后再输入ls -lha查看具体信息

到文件系统中打开mnt文件夹,查看分离出的图片

并没有找到什么,但是前面ls查看时有lost+found字样,百度一下

ext指extundelete,可以恢复文件,具体可以参考这篇博客

(11条消息) 文件恢复原理&&Linux文件恢复工具-foremost&extundelete_Red snow的博客-CSDN博客_foremost 恢复

extundelete恢复文件

使用命令:

extundelete 26.img --restore-all

然后打开恢复的文件夹

打印输出

使用cat命令打印或者010打开文件

cat file.17

或者先查看恢复的文件是什么,然后再打印出来

010

3.flag

flag{fugly_cats_need_luv_2}

4.知识点

cat,ls,extundelete命令恢复文件,mount挂载文件

27.[SCTF2019]Ready_Player_One

1.题目概述

2.解题过程

一上来有点懵,这是啥?

运行一下那个程序看看,是一个游戏,靠最右侧直接一路上去就发现flag了

flag{You_Are_The_Ready_Player_One!!!For_Sure!!!}

3.flag

flag{You_Are_The_Ready_Player_One!!!For_Sure!!!}

4.知识点

玩游戏获得flag

28.[GUET-CTF2019]520的暗示

1.题目概述

2.解题过程

这个题目名称好有意思

010打开,

photo.dat,那应该和图片有关系,分析开头和结尾

有点类似于jpg的FFD8FF文件头和FFD9文件尾诶,那要怎么样转换成jpg格式呢?想到了那几个常用的计算方法——二进制与,二进制或,二进制异或和二进制反转,优先考虑异或,这个比较常见,CC和FF的二进制分别是:

CC 11001100

FF 11111111

CC^x=11111111

异或运算符是这个意思,参与运算的两个值,如果两个相应bit位相同,则结果为0,否则为1

(1) 00=0,01=1 0异或任何数=任何数

(2) 10=1,11=0 1异或任何数-任何数取反

(3) 任何数异或自己=把自己置0

这样就可以一位位地推导出x是多少了,1-8位x与CC的bit位全是不同的,这就很简单,x应该是

00110011,转换成16进制就是33

在线进制转换器 | 菜鸟工具 (runoob.com)

与33异或

010

看起来很成功

保存,修改后缀为jpg,打开,

这张图片还从来没见过,百度一下

原来需要根据图片中的信息定位,查询网站

https://v.juhe.cn/cell/Triangulation/index.html?s=inner

这个flag很离谱,是中文

flag{桂林电子科技大学花江校区}

3.flag

flag{桂林电子科技大学花江校区}

4.知识点

二进制异或,联通基站定位

29.Beautiful_Side

1.题目概述

2.解题过程

看题目描述,应该和二维码有关

010打开

在文件尾后面发现了png数据,存下来

选中后复制为16进制文本

然后新建一个010文件,粘贴自16进制文本

存下来,打开

是一张残缺的二维码,试了试扫不出来

再回到刚刚图片,stegsolve打开也没发现啥,silent eye也没解出来东西,

百度一下能不能修复

试试看

借助网站

QRazyBox - QR Code Analysis and Recovery Toolkit (merricx.github.io)

新建一个project

然后新建空白的二维码(导入会出错)

调整大小为29x29,这是根据那个残缺的二维码确定的

尺寸说明如下,共29个间隔

然后就一点点的根据这一半二维码进行填充,记得,补全黑色部分后,也要把白色的部分填补上,因为灰色是算“空”的。借助这个分隔进行填充,不然容易搞错

然后点击Tools-Extract QR Infomation

就看到flag了

3.flag

flag{OQWIC_4DS1A_S034S}

4.知识点

二维码修复工具

30.大流量分析(二)

1.题目概述

2.解题过程

打开第一个看看

邮箱协议

POP3

POP3是Post Office Protocol 3的简称,即邮局协议的第3个版本,它规定怎样将个人计算机连接到Internet的邮件服务器和下载电子邮件的电子协议。它是因特网电子邮件的第一个离线协议标准,POP3允许用户从服务器上把邮件存储到本地主机(即自己的计算机)上,同时删除保存在邮件服务器上的邮件,而POP3服务器则是遵循POP3协议的接收邮件服务器,用来接收电子邮件的。(与IMAP有什么区别?)

SMTP

SMTP 的全称是“Simple Mail Transfer Protocol”,即简单邮件传输协议。它是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转方式。SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。SMTP 服务器就是遵循 SMTP 协议的发送邮件服务器。   SMTP 认证,简单地说就是要求必须在提供了账户名和密码之后才可以登录 SMTP 服务器,这就使得那些垃圾邮件的散播者无可乘之机。   增加 SMTP 认证的目的是为了使用户避免受到垃圾邮件的侵扰。

IMAP

IMAP全称是Internet Mail Access Protocol,即交互式邮件存取协议,它是跟POP3类似邮件访问标准协议之一。不同的是,开启了IMAP后,您在电子邮件客户端收取的邮件仍然保留在服务器上,同时在客户端上的操作都会反馈到服务器上,如:删除邮件,标记已读等,服务器上的邮件也会做相应的动作。所以无论从浏览器登录邮箱或者客户端软件登录邮箱,看到的邮件以及状态都是一致的。(与POP3有什么区别?)

参考:什么是POP3、SMTP和IMAP?-163邮箱常见问题

百度一下常用的SMTP协议

过滤smtp流量,可以很明显的看到几个邮箱地址

打开其中一条

可以知发送邮箱是 xsser@live.cn

3.flag

flag{ xsser@live.cn}

4.知识点

SMTP邮箱协议

31.[GKCTF 2021]FireFox Forensics

1.题目概述

2.解题过程

取证题目,名称是火狐,可以借助一个工具

firepwd ,github下载工具

https://github.com/lclevy/firepwd

打开解压后的文件夹,打开终端,输入

python firepwd.py logins.json

在最后面看到flag

如果出错自行百度

注意这个flag不用flag{}包起来

3.flag

GKCTF{9cf21dda-34be-4f6c-a629-9c4647981ad7}

4.知识点

firepwd工具的使用

32.[DDCTF2018]第四扩展FS

1.题目概述

2.解题过程

010打开

百度一下The fourth extended filesystem

这不是前面那个文件恢复程序嘛

没有img文件,先放着,

查看图片属性

有备注,第一时间联想到压缩包密码

用Kali的foremost分离看看

foremost分离

jpg文件夹里还是刚刚那张图片,压缩包打开是加密的,正好用前面那个备注里的信息破解一下试试

Pactera

还真是解压密码,得到一个file.txt

这熟悉的感觉,

统计字频

脚本

import re

file = open('file.txt') #根据路径自行调整

line = file.readlines()

file.seek(0,0)

file.close()

result = {}

for i in range(97,123):

count = 0

for j in line:

find_line = re.findall(chr(i),j)

count += len(find_line)

result[chr(i)] = count

res = sorted(result.items(),key=lambda item:item[1],reverse=True)

num = 1

for x in res:

print('频数第{0}: '.format(num),x)

num += 1

D:\Programes\py37_work\Scripts\python.exe D:/Programes/pythonProject/字频统计.py

频数第1: ('h', 1700)

频数第2: ('u', 1650)

频数第3: ('a', 1600)

频数第4: ('n', 1550)

频数第5: ('w', 1500)

频数第6: ('e', 1450)

频数第7: ('s', 1350)

频数第8: ('i', 1300)

频数第9: ('k', 1250)

频数第10: ('o', 1150)

频数第11: ('b', 0)

频数第12: ('c', 0)

频数第13: ('d', 0)

频数第14: ('f', 0)

频数第15: ('g', 0)

频数第16: ('j', 0)

频数第17: ('l', 0)

频数第18: ('m', 0)

频数第19: ('p', 0)

频数第20: ('q', 0)

频数第21: ('r', 0)

频数第22: ('t', 0)

频数第23: ('v', 0)

频数第24: ('x', 0)

频数第25: ('y', 0)

频数第26: ('z', 0)

进程已结束,退出代码0

结果似乎有点问题,还是用网站吧

网站

语料库在线–字词频统计 (zhonghuayuwen.org)

3.flag

flag{huanwe1sik4o!}

4.知识点

图片属性,foremost分离,字频统计

Page 6

1.[INSHack2018]42.tar.xz

1.题目概述

2.解题过程

随便打开一个tar文件

继续打开也是这样,好家伙,这么多文件地嘛,

有点类似于套娃

python脚本

import tarfile

import os

current_path = r"\D:/Programes/pythonProject/new/tar"

if __name__ == "__main__":

i = 0

target = "42.tar.xz"

tarname = current_path + target

while True:

i += 1

print("当前层数:{0}".format(i))

tar = tarfile.open(tarname)

filenames = tar.getnames()

if target not in filenames: break

tar.extract(target,current_path)

tar.close()

print("最后一层:{}".format(filenames),"\n正在解压……")

tar.extractall(current_path)

tar.close()

不过速度太慢了,借用一下一个bash,

while [ "`find . -type f -name '*.tar.xz' | wc -l`" -gt 0 ]; do find -type f -name "*.tar.xz" -exec tar xf '{}' \; -exec rm -- '{}' \;; done;

好家伙,一运行直接磁盘爆满,一共就4GB内存,运行到一半我及时地暂停了

直接cat查找flag

3.flag

flag{04ebb0d6a87f9771f2eea4dce5b91a85e7623c13301a8007914085a91b3ca6d9}

4.知识点

破解套娃,bash语句,cat命令

2.[XMAN2018排位赛]AutoKey(待定)

1.题目概述

2.解题过程

有点像USB数据包,和以前做过的一道题很像

解法一

使用同样的tshark命令获取数据

tshark -r 2.pcapng -T fields -e usb.capdata > usbdata.txt

打开usbdata文件,和USB那道题很类似

解法二

利用脚本获取信息

UsbKeyboardDataHacker爆破

WangYihang/UsbKeyboardDataHacker: USB键盘流量包取证工具 , 用于恢复用户的击键信息 (github.com)

下载地址

WangYihang/UsbKeyboardDataHacker: USB键盘流量包取证工具 , 用于恢复用户的击键信息 (github.com)

python UsbKeyboardDataHacker.py 2.pcapng

【+] Found : autokey(‘****’).decipheer(‘mplrvffczeyoujfjkybxgzvdgqaurkxzolkolvtufblrnjesqitwahxnsijxpnmplshcjbtyhzealogviaaissplfhlfswfehjncrwhtinsmambvexopzeiz’)

有效信息

处理一下密文,把前面的一个字母给删去(del代表delete删除键,看看键盘就知道了)

mplrvffczeyoujfjkybxgzvdgqaurkxzolkolvtufblrnjesqitwahxnsijxpnmplshcjbtyhzealogviaaissplfhlfswfehjncrwhtinsmambvexpziz

https://github.com/WangYihang/UsbKeyboardDataHacker)

breakautokey爆破

下载地址

hotzzzzy/breakautokey: breakautokey (github.com)

环境配置

脚本

实用密码学 (practicalcryptography.com)

配置文件

Practical Cryptography

解压后,把文件名里的"english_"删去,放在Python2文件夹中。最后一个py文件放在lib文件夹里。

把框出来的这部分换成我们要破解的数据,然后就可以跑脚本啦

python2 break_autokey.py

3.flag

4.知识点

3.[QCTF2018]X-man-Keyword(待定)

1.题目概述

2.解题过程

png图片有密码,联想到了lsb

Stegsolve打开,在red0通道发现了一点东西,blue0和green0里面也有,尝试着合并也没出来什么东西

那就利用一个lsb工具试试看

livz/cloacked-pixel: LSB steganography and detection (github.com)

cloacked-pixel-master

用法

python2 lsb.py extract 3.png out lovekfc

提示把keyword放到前面,根据提示,

3.flag

flag{cCgeLdnrIBCX9G1g13KFfeLNsnMRdOwf}

4.知识点

4.[BSidesSF2019]diskimage

1.题目概述

2.解题过程

题目名称叫disk image,就是磁盘照片,和磁盘有关系

照片上部分有点问题,010打开

第一眼没有什么问题,stegsolve打开看看

好像也没啥,换个工具看看,

zsteg查看

导出镜像信息

zsteg -e 'b8,rgb,lsb,xy' 4.png > disk.dat

导出文件的格式选择dat,txt文档无法正常查看

打开dat文件看看

没有什么东西

恢复磁盘信息

使用kali自带的testdisk工具,命令

testdisk 4disk.dat

打开之后

回车

默认none,再次回车

方向键右键选择undelete

然后向下查看所有文件,找到一个不正常的

根据提示,按c,回车,拷贝,

打开

3.flag

flag{FAT12_FTW}

4.知识点

zsteg查看png隐藏信息;testdisk工具处理磁盘

5.[INSHack2017]hiding-in-plain-sight

1.题目概述

2.解题过程

打开description

翻译一下

漏洞题目

010打开图片,发现jpg文件头

foremost分离

打开jpg文件,发现flag

3.flag

flag{I337_h4xx0r5_c0mmun1c473_w17h_PNGs}

4.知识点

binwalk分离

6.[HDCTF2019]信号分析

1.题目概述

提示:

2.解题过程

听了听没有东西,audacity打开

熟悉的套路,频谱是重复的,选取其中一段,宽的代表1,窄的代表0,提取出来

0101010101010101000000110

再按照提示,转化成:

FFFFFFFF0001,

最后一个0就不算了

flag{FFFFFFFF0001}

3.flag

flag{FFFFFFFF0001}

4.知识点

频谱长短转化;根据提示解题;

7.[MRCTF2020]寻找xxx

1.题目概述

2.解题过程

听了听是电话按键音,dtmf2num工具识别一下

下载地址,实在是太不好找了

https://www.softpedia.com/get/Multimedia/Audio/Other-AUDIO-Tools/DTMF2NUM.shtml#download

打开终端,输入

.\ dtmf2num.exe 密码.wav

DTMF numbers: 18688422216D09

电话号码是纯数字,删掉D,1868842221609

也可以利用网站,解出来没有字母;

Detect DTMF Tones (dialabc.com)

然后发送电话号码到公众号,没找到,这里直接得出flag了

3.flag

flag{Oh!!!_Y0u_f1nd_my_secret}

4.知识点

DTMF电话音识别;

8.[INSHack2017]remote-multimedia-controller

1.题目概述

2.解题过程

题目描述:

翻译一下

追踪TCP流,

发现flag.txt字样,并且还跟着一串base64编码

Vmxkd1NrNVhVbk5qUlZKU1ltdGFjRlJYZEhOaWJFNVhWR3RPV0dKVmJEWldiR1JyV1ZkS1ZXRXphRnBpVkVaVFYycEtVMU5IUmtobFJYQlRUVmhDTmxZeFdtdGhhelZ5WWtWYWFWSlViRmRVVlZaYVRURmFjbFpyT1ZaV2JXUTJWa1pvYTFkck1YVlVhbHBoVWxack1GUlZaRXRqVmxaMVZHMTRXRkpVUlRCWFdIQkdUbGRHY2s1VmFFOVdNWEJoV1Zkek1XSldaSFJPVm1SclZsZDRXbFJWVm5wUVVUMDk=

找网站解了一下没解出来,多试几次就出来了,

VldwSk5XUnNjRVJSYmtacFRXdHNibE5XVGtOWGJVbDZWbGRrWVdKVWEzaFpiVEZTV2pKU1NHRkhlRXBTTVhCNlYxWmthazVyYkVaaVJUbFdUVVZaTTFaclZrOVZWbWQ2VkZoa1drMXVUalphUlZrMFRVZEtjVlZ1VG14WFJURTBXWHBGTldGck5VaE9WMXBhWVdzMWJWZHROVmRrVld4WlRVVnpQUT09

VWpJNWRscERRbkZpTWtsblNWTkNXbUl6VldkYWJUa3hZbTFSWjJSSGFHeEpSMXB6V1Zkak5rbEZiRTlWTUVZM1ZrVk9VVmd6VFhkWk1uTjZaRVk0TUdKcVVuTmxXRTE0WXpFNWFrNUhOV1paYWs1bVdtNVdkVWxZTUVzPQ==

R29vZCBqb2IgISBZb3UgZm91bmQgdGhlIGZsYWc6IElOU0F7VENQX3MwY2szdF80bjRseXMxc19jNG5fYjNfZnVuIX0K

也可以用python脚本,根据最终字符串中含有flag为线索

import base64

cipher_text = 'Vmxkd1NrNVhVbk5qUlZKU1ltdGFjRlJYZEhOaWJFNVhWR3RPV0dKVmJEWldi' \

'R1JyV1ZkS1ZXRXphRnBpVkVaVFYycEtVMU5IUmtobFJYQlRUVmhDTmxZeFdt' \

'dGhhelZ5WWtWYWFWSlViRmRVVlZaYVRURmFjbFpyT1ZaV2JXUTJWa1pvYTFk' \

'ck1YVlVhbHBoVWxack1GUlZaRXRqVmxaMVZHMTRXRkpVUlRCWFdIQkdUbGRH' \

'Y2s1VmFFOVdNWEJoV1Zkek1XSldaSFJPVm1SclZsZDRXbFJWVm5wUVVUMDk='

plain_text = ''

while 'flag' not in plain_text:

cipher_text, plain_text = plain_text, base64.b64decode(cipher_text).decode()

print(plain_text)

3.flag

flag{TCP_s0ck3t_4n4lys1s_c4n_b3_fun!}

4.知识点

追踪流;base64多次编码;

9.一路到底

1.题目概述

2.解题过程

好家伙,155534个txt文件,

打开start

找到这个文件

按照内容再打开下一个文件

继续

这就有点奇怪了,太像套娃了,这些txt文件内容前面都有数字,这代表什么呢?写出来

2055577251202304

转换一下进制

在线进制转换器 | 菜鸟工具 (runoob.com)

套路好深,那就用一个脚本提取这些数据吧

import re

current_path = "D:\\CTF\\P6-BUU\\9\\files\\" #最好使用绝对路径

start = open(current_path + "start.txt","r")

zipfile = open("data.zip","wb") #创建一个文件存储数据

content = start.read()

while True:

num = re.findall(r"^[0-9]+",content)[0]

zipfile.write((int(num)).to_bytes(2, byteorder = 'big'))

filename = re.findall(r" ([A-Za-z0-9.]+)$",content)

if filename:

nextfile = open(current_path + filename[0],"r")

content = nextfile.read()

nextfile.close()

else:

break

print(content)

start.close()

zipfile.close()

打开

有密码,那就爆破

爆破了十几分钟之后终于解出来了,范围是

tgb678

打开

010打开,原来是文件头被改了

修改为FF D8 FF ,还是不可以,看来修改的太少了

随便找一张jpg

修改为FF D8 FF E0,可以打开了

3.flag

flag{0c6b489ca956e2fd94dce12be4bf0729}

4.知识点

10进制与16进制转换;python脚本获取文件信息;jpg文件头

10.[SUCTF2018]dead_z3r0

1.题目概述

2.解题过程

不知道啥格式,binwalk也没分析出来,

010打开

仔细观察,有点像反编译

而且文件头加入了一些base64数据

去掉base64数据

如图中选中部分

保存为pyc文件

再用stegosaurus脚本工具,和剑龙那道题一样

python stegosaurus.py -x 10.pyc

3.flag

flag{Z3r0_fin411y_d34d}

4.知识点

反编译;stegosaurus脚本使用

11.INSHack2018(not) so deep

1.题目概述

2.解题过程

description:

audacity查看频谱

很明显有flag的痕迹,只是显示不全,

调整一下频率

INSA{Aud1o_st3G4n

得到一半flag,继续寻找,联系题目名称和要求,这次要用到一个新工具

deep sound,在歌曲中隐藏文件的工具

链接:https://pan.baidu.com/s/14LOBPAzm4mWNMPJ3ccT4SQ?pwd=ujso 提取码:ujso

打开提示需要密码,使用脚本破解她的密码,脚本下载地址

john/deepsound2john.py at bleeding-jumbo · openwall/john · GitHub

破解密码

打开终端

python deepsound2john.py final_flag.wav > password.txt

final_flag.wav:$dynamic_1529$b8f858d9deb0b805797cef03299e3bdd8990f48a

尝试对获取final_flag.wav 密码的hash值然后使用john进行爆破

john爆破

John在Kali上有,可以直接用

password:azerty

导出文档

按照提示找到导出文档

0_1s_4lwayS_Th3_S4me}

3.flag

flag{Aud1o_st3G4n0_1s_4lwayS_Th3_S4me}

4.知识点

audacity查看并调整频谱;deep sound获取音频隐藏信息;john破解

12.大流量分析

1.题目概述

2.解题过程

3.flag

4.知识点

13.[INSHack2017]10-cl0v3rf13ld-lane-signal

1.题目概述

2.解题过程

翻译一下

010打开

是jpg文件,

修改一下后缀

在后面发现了png数据

导出png数据

仔细观察会发现

放大

短的是.,长的是-

… . .-… .–. – .

就是HELPME

再回到png的010,结尾后面还有数据

改后缀

也复制下来,后缀改为ogg,

原来是个音频文件,audacity打开

查看摩斯密码

… -. … .- -.–. – ----- .-.

… …-- …–.- .-- .---- .-…

.-… …–.- -. …-- …- …-- .-.

…–.- …- --. …-- -.-.-- -.–.-

合起来

… -. … .- -.–. – ----- .-. … …-- …–.- .-- .---- .-… .-… …–.- -. …-- …- …-- .-. …–.- …- --. …-- -.-.-- -.–.-

翻译一下

INSAM0R53W1LLN3V3R4G3

这个网站不对劲,换个网站试试

这个靠谱,(改成{

所以前面的helpme没用上

3.flag

flag{M0R53_W1LL_N3V3R_4G3!}

4.知识点

010数据处理

摩斯密码

14.[NPUCTF2020]碰上彩虹,吃定彩虹!

1.题目概述

2.解题过程

maybehint

根据以前的题目推测应该和NTFS数据流有关

NTFS

导出后打开这个out.txt

==联想到base64,那就统计一下字频

字频统计

ZW5jcnlwdG8=

和推测一致,解码

那应该和密码有关,先放着

再打开lookatme,发现底部有摩斯密码

摩斯密码

.- …- - — -.- . -.–

是AUTOKEY

achnrvxzzuglarucalznwcygfggrufryvbzqjoxjymxvchhhdmliddcwmhghclpebtzwlojvew

那文本内容应该使用了autokey加密,使用脚本破解一下(pycharm坏了,重新安装,就没跑)

autokey加密

解出来的密码是:iamthepasswd

010打开secret

这一段文字需要删掉才可以继续解密

然后使用

encrypto解密

搞出来一张图片

010打开图片发现zip数据

导出来打开

又是加密的zip,密码在哪呢?

很久很久以前也做过一道题

采用同样的办法提取颜色的十六进制

ps拾色器

ffff70

ffff40

ffff73

ffff73 …

6条黄色带的16进制就是:

ffff70 ffff40 ffff73 ffff73 ffff57 ffff64

提取后2位

704073735764

转一下ascii

打开文档

显示隐藏内容

这是一种编程语言,ALphu*ck,借个网站

alphu*ck在线开发工具,在线编译IDE_w3cschool

eeeeeeeeeepaeaeeeaeAeeeeeeaeeeeeeeeeeccccisaaaaeejeeeeeejiiiiiiLiiiiijeeeeeejeeeeeeeeeeeeeeeeeeeejcceeeeeeeeeeePeeeeeeeejaaiiiiiiijcciiiiiiiiiijaaijiiiiiiiiiiiiiiiiiiiijeeeeeeHeeeeeeeeeeeeeeeeejcceeeeeeeeeeeejaaiiiijeeeeeeejceeeeeeeeeeeeeeeeeeeeeeeeejceeeeeeeeeeeeeeeeejaeeeeeejciiUiiiiiiiiiiiiiiiiijaeeeejceeeeeeeeeCeeeeeeeeejajciiiiiiiiiiiiiiiiiiijaaiiiijiijeeeeeeeeeeejKcciiiiiiiiiiiiiiijaaij

删掉大写APHKC几个字母

终于得到flag了!

3.flag

flag{1t's_v3ry_De1iCi0us~!}

4.知识点

太多了!

15.[RoarCTF2019]forensic

1.题目概述

2.解题过程

3.flag

4.知识点

16.[WMCTF2020]行为艺术

1.题目概述

2.解题过程

打开hint

md5sum flag.zip

17f5b08342cf65f6dc08ed0b4c9bd334 flag.zip

推测和MD5加密,zip加密有关

打开图片,数字是zip的16进制数据

010打开

CRC校验码有误,跑个脚本看一下它的正确宽高

高度有问题,改一下,再打开

一看就是zip的16进制,识别不出来,就用无影手敲出来

很快,我们得到:

504B0304140000000800DB93C55086A39007D8000000DF01000008000000666C61672E74787475504B0E823010DD93708771DDCCB0270D5BBD0371815A9148AC6951C2ED9D271F89C62E2693D7F76BB7DE9FC80D2E6E68E782A326D2E01F81CE6D55E76972E9BA7BCCB3ACEF7B89F7B6E90EA16A6EE2439D45179ECDD1C5CCFB6B9AA489C1218C92B898779D765FCCBB58CC920B6662C5F91749931132258F32BBA7C288C5AE103133106608409DAC419F77241A3412907814AB7A922106B8DED0D25AEC8A634929025C46A33FE5A1D3167A100323B1ABEE4A7A0708413A19E17718165F5D3E73D577798E36D5144B66315AAE315078F5E51A29246AF402504B01021F00140009000800DB93C55086A39007D8000000DF010000080024000000000000002000000000000000666C61672E7478740A00200000000000010018004A0A9A64243BD601F9D8AB39243BD6012D00CA13223BD601504B050600000000010001005A000000FE00000000000000

保存一下,打开

Ook!密码,找个网站借一下

3.flag

flag{wai_bi_baaaa_bo!2333~~~}

4.知识点

png高度隐写;

Ook!密码

17.[watevrCTF 2019]Unspaellablle

1.题目概述

2.解题过程

打开txt

像是一首小诗,翻译一下

很离谱,居然这么多词儿

搜一搜这个SG是个啥玩意

有一些内容是多余的,查找一下{

这个题目找半天没找到一个完美的原文文档

解题思路大致就是利用beyond compare软件找不同,借个图

多出来的的内容组合到一起就是flag

3.flag

flag{icantspeel_tiny.cc/2qtdez}

4.知识点

beyond compare

搜索能力

18.[羊城杯 2020]signin

1.题目概述

2.解题过程

打开txt

这道题没啥思路,常见的几种编码都试了也不行,解题过程参考百度

根据题目提示,一表人才,二表倒立,以及玩具toy,猜测是toy加密

这是一篇介绍toy加密的文章(全英文,看不懂就不看了)

301.pdf (iacr.org)

简单来说,TOY加密有2个过程,

简单写了一下过程,大致就是2次映射,如有错误请指教:

每个字母的加密过程如下图

“一表人才”:

“二表倒立”:

然后就可以借助脚本解一下内容(当然内容很少的话也可以手动解)

list1 = {'M':'ACEG','R':'ADEG','K':'BCEG','S':'BDEG','A':'ACEH','B':'ADEH','L':'BCEH','U':'BDEH','D':'ACEI','C':'ADEI','N':'BCEI','V':'BDEI','H':'ACFG','F':'ADFG','O':'BCFG','W':'BDFG','T':'ACFH','G':'ADFH','P':'BCFH','X':'BDFH','E':'ACFI','I':'ADFI','Q':'BCFI','Y':'BDFI'}

#首先把每个字母的最终映射写出来

list2 = original_list = ['M','R','K','S','A','B','L','U','D','C','N','V','H','F','O','W','T','G','P','X','E','I','Q','Y']

#原始内容会出现的24个字母(Z J已被替换)

list2_re =list2[::-1]

ori_str = 'BCEHACEIBDEIBDEHBDEHADEIACEGACFIBDFHACEGBCEHBCFIBDEGBDEGADFGBDEHBDEGBDFHBCEGACFIBCFGADEIADEIADFH'

#原始字符串,可替换

flag_1 = ''

for i in range(0,len(ori_str),4):

_val = ori_str[i:i+4]

for key, val in list1.items():

if val == _val:

flag_1 += key

print(flag_1)

flag = ''

for i in flag_1:

for j,k in enumerate(list2):

if i == k:

flag += list2_re[j]

print(flag)

跑一下

GWHT是此次羊城杯的flag头

3.flag

flag{TOYSAYGREENTEAISCOOL}

4.知识点

Toy加密

19.[INSHack2019]Passthru

1.题目概述

description

2.解题过程

打开发现传输层协议是TLS,并且被加密

文件中还有一个SSLkey文件(SSL3.0就是TLS)

和以前做过的一道题类似,TLS密钥,

导入密钥文件

打开首选项

打开protocols

下拉找到TLS,导入

3.flag

4.知识点

20.[XMAN2018排位赛]ppap

1.题目概述

2.解题过程

3.flag

4.知识点

21.

1.题目概述

2.解题过程

3.flag

4.知识点

22.[BSidesSF2020]barcoder

1.题目概述

2.解题过程

打开说明

重点观察一下图片

因为条形码上下都是一样的,而且这张图片“巧合地”露出了每一竖条的一部分

画图打开(不太会PS),猜想把每一竖条码没有被涂鸦的的区域摘出来,然后拉长,组合到一起,扫一扫就行

开干

矩形工具选中每一细竖条没有涂鸦的地方,然后拉伸

中国编码APP扫一扫

3.flag

flag{way_too_common}

4.知识点

条形码遮挡处理

24.[CTF2019]She

1.题目概述

2.解题过程

打开这个游戏

走进左下角这个屋子,遇到了一只鸡,打败这只鸡才能得到flag线索

这只鸟好强,根本打不过,一上来就over了

不行,我要开外挂

这个游戏类型是RPG,那就下载一个RPG

RPG Maker XP | RPG Maker | Make Your Own Game! (rpgmakerweb.com)

新建进程,把Game.rxproj放到She下,然后再次打开进程选择She下的Game.rxproj

打开后我们要对这只鸟的参数进行修改

Tools——database——

参数可以都调成1

然后再当我们打完鸟,往后走,有个幽灵,我们一碰就Game Over

我们编辑事件,先点击箭头的蓝色块

选中幽灵图标右键编辑事件,然后清空事件页就行ROOM1-4但凡有幽灵的都给清除

然后再进入游戏

开挂当然稳赢

然后我们从第一个门开始打开门,发现打不开然后挨个尝试,按照该顺序获取到的数值是371269(第几个门),按照房间顺序排列得到213697。(第一个门是2,第二个门给的数字是1以此类推)

将213697进行md5加密

3.flag

flag{d6f3fdffbcb462607878af65d059f274}

4.知识点

利用RPG软件开挂

文章目录

入门篇操作系统入门 Linux

编程技能专业基础篇编程语言Java 语言学习 Go 语言

理论学科数据结构和算法其它理论基础知识

系统知识C10K 问题实践项目

软件设计篇编程范式一些软件设计的相关原则一些软件设计的读物

高手成长篇Linux 系统、内存和网络(系统底层知识)Linux 系统相关内存相关计算机网络网络学习网络调优网络协议

异步 I/O 模型和 Lock-Free 编程(系统底层知识)异步 I/O 模型Lock-Free 编程相关其它相关论文小结

Java 底层知识Java 字节码相关JVM 相关小结

数据库关系型数据库NoSQL 数据库各种 NoSQL 数据库小结

分布式架构入门(分布式架构)分布式架构入门分布式理论小结

分布式架构经典图书和论文(分布式架构)经典图书经典论文分布式事务Paxos 一致性算法Raft 一致性算法Gossip 一致性算法分布式存储和数据库分布式消息系统日志和数据分布式监控和跟踪数据分析与编程相关的论文其它的分布式论文阅读列表

小结

分布式架构工程设计 (分布式架构)设计模式设计与工程实践分布式系统的故障测试弹性伸缩一致性哈希数据库分布式缓存消息队列关于日志方面关于性能方面关于搜索方面各公司的架构实践

小结

微服务微服务架构微服务和 SOA设计模式和最佳实践相关资源小结

容器化和自动化运维DockerKubernetes小结

机器学习和人工智能基本原理简介相关课程相关图书相关文章相关算法相关资源小结

前端基础和底层原理(前端方向)HTML 5CSSJavaScript浏览器原理网络协议小结

前端性能优化和框架(前端方向)前端框架React.js 框架Vue.js 框架

小结

UI/UX 设计(前端方向)图书和文章推荐原子设计(Atomic Design)设计语言和设计系统Fluent Design SystemMaterial Design其它公司动画效果设计

相关资源文章资源设计收集

小结

技术资源集散地YouTube 技术频道

入门篇

通过这一系列文章,我主要想回答以下几个问题。

理论和现实的差距。你是否觉得自己从学校毕业的时候只做过小玩具一样的程序?走入职场后哪怕没有什么经验也可以把文中提到的这些课外练习走一遍。学校课程总是从理论出发,作业项目都看不出有什么实际作用,到了工作上发现自己什么也不会干。 技术能力的瓶颈。你又是否觉得,在工作当中需要的技术只不过是不断地堆业务功能,完全没有什么技术含量。而你工作一段时间后,自己都感觉得非常地迷茫和彷徨,感觉到达了提高的瓶颈,完全不知道怎么提升了。 技术太多学不过来。你是否又觉得,要学的技术多得都不行了,完全不知道怎么学?感觉完全跟不上。有没有什么速成的方法?

对此,我有如下的一些解释,以端正一下你的态度。

并不是理论和现实的差距大,而是你还没有找到相关的场景,来感受到那些学院派知识的强大威力。算法与数据结构、操作系统原理、编译原理、数据库原理、计算机原理……这些原理上的东西,是你想要成为一个专家必须要学的东西。这就是“工人”和“工程师”的差别,是“建筑工人”和“建筑架构师”的差别。如果你觉得这些理论上的东西无用,那么只能说明,你只不过在从事工人的工作,而不是工程师的工作。技术能力的瓶颈,以及技术太多学不过来,只不过是你为自己的能力不足或是懒惰找的借口罢了。技术的东西都是死的,这些死的知识只要努力就是可以学会的。只不过聪明的人花得时间少,笨点的人花得时间多点罢了。这其中的时间差距主要是由学习方法的不同,基础知识储备的不同决定的。只要你方法得当,多花点时间在基础知识上,会让你未来学习应用知识的时间大大缩短。以绝大多数人努力的程度,和为自己不努力找借口的程度为参考,只要你坚持正常的学习就可以超过大多数人了。这里没有学习技术的速成的方法,真正的牛人不是能够培训出来的,一切都是要靠你自己去努力和持续地付出。如果你觉得自己不是一个能坚持的人,也不是一个想努力的人,而是一个想找捷径的人,那么,这篇文章并不适合你。这篇文章中的成长路径是需要思考、精力和相关的经验的,这都需要时间,而且是不短的时间。你先问问自己有没有花十年磨一剑的决心,如果没有,那这篇文章对你没有任何作用。

学习建议。

一定要坚持,要保持长时间学习,甚至终生学习的态度。一定要动手,不管例子多么简单,建议至少自己动手敲一遍看看是否理解了里头的细枝末节。一定要学会思考,思考为什么要这样,而不是那样。还要举一反三地思考。不要乱买书,不要乱追新技术新名词,基础的东西经过很长时间积累,会在未来至少 10 年通用。回顾一下历史,看看历史时间线上技术的发展,你才能明白明天会是什么样的。

操作系统入门 Linux

学习编程你还需要会玩 Linux,虽然 Windows 占据着更多的桌面市场,但是你还是要了解 Linux。这里,你可以看一下,W3CSchool 上的在线教程 Linux 教程。

记得要看官方文档!!!!!

编程技能

在系统地学习编程技能之前,我希望你能先看一下 " The Key To Accelerating Your Coding Skills", 这篇文章会告诉你如何有效地快速提高自己的编程能力。

然后接下来是下面几大块内容,但还只是入门级的。

编程技巧方面 - 你可以开始看怎么把程序写好的书了,这里推荐的是**《代码大全》**。这本书好多年没有更新了,其中有一些内容可能有点过时,但还是一本非常好的书,有点厚,你不需要马上就看完。在你的编程路上,这本书可以陪你走很久,因为当你有更多的编程经验时,踩过更多的坑后,再把这本书拿出来看看,你会有更多的体会。好的书和不好的书最大的区别就是,好的书在你不同的阶段来读,你会有不同的收获,而且还会产生更多的深层次的思考! 《代码大全》就是这样的一本书。编程语言方面 - 这个阶段,你可以开始了解一下 Java 语言了,我个人觉得 Java 是世界上目前为止综合排名最好的语言。你一定要学好这门语言。推荐《Java 核心技术(卷 1)》,除了让你了解 Java 的语法,它还会让你了解面向对象编程是个什么概念(如果你觉得这本书有点深,那么,你可以降低难度看更为基础的《Head First Java》)。然后,既然开始学习 Java 了,那就一定要学 Spring,推荐看看《Spring in Action》或是直接从最新的 Spring Boot 开始,推荐看看《Spring Boot 实战》。关于 Spring 的这两本书,里面可能会有很多你从来没有听说过的东西,比如,IoC 和 AOP 之类的东西,能看懂多少就看懂多少,没事儿。——Spring实战操作系统 - 这里你可以看看《鸟哥的 Linux 私房菜》,这本书会让你对计算机和操作系统,以及 Linux 有一个非常全面的了解,并能够管理或是操作好一个 Linux 系统。当然,这本书有很多比较专业的知识,你可能会看不懂,没关系,就暂时略过就好了。这本书的确并不适合初学者,你能看多少就看多少吧。网络协议 - 你需要系统地了解一下 HTTP 协议,请到 MDN 阅读一下其官方的 HTTP 的文档。你需要知道 HTTP 协议的几个关键点:1)HTTP 头,2)HTTP 的请求方法,3)HTTP 的返回码。还有,HTTP 的 Cookie、缓存、会话,以及链接管理,等等,在 MDN 的这个文档中都有了。对于 HTTP 协议,你不需要知道所有的东西,你只需要了解这个协议的最关键的那些东西就好了。数据库设计 - 你需要系统地了解一下数据库设计中的那些东西,这里推荐慕课网的一个在线课程:数据库设计的那些事。每个小课程不过 5-6 分钟,全部不到 2 个小时,我相信你一定能跟下来。你需要搞清楚数据的那几个范式,还有 SQL 语句的一些用法。当然,你还要学习和使用一下数据库,这里推荐学习开源的 MySQL。你可以看官方文档,也可以看一下这本书《MySQL 必知必会》。前端方面 - 前端的东西不算复杂,你需要学习几个东西。一个是和 JavaScript 相关的 jQuery,另一个是和 CSS 相关的 Bootstrap,学习这两个东西都不复杂,直接上其官网看文档就好了。最重要的是,你要学习一下如何使用 JavaScript Ajax 请求后端的 API 接口,而不是再像前面那样用后端来向前端返回 HTML 页面的形式了。这里,你需要学习一下,JavaScript 的 Promise 模式。阮一峰翻译的 ES6 的教程中有相关的内容。当然,你 Google 一下,也可以找到一堆学习资料。字符编码方面 - 在你处理中文时有时会发现有乱码出现,此时需要了解 ASCII 和 Unicode 这样的字符编码。这里推荐一篇文章 - “关于字符编码,你所需要知道的(ASCII,Unicode,Utf-8,GB2312…)” 或是英文文章 “The history of Character Encoding” 以及 Wikipedia - Character encoding。还有 GitHub 上的这两个 Awesome 仓库:Awesome Unicode 和 Awesome Code Points。

专业基础篇

编程语言

Java 语言

学习 Java 语言有以下入门级的书(注意:下面一些书在入门篇中有所提及,但为了完整性,还是要在这里提一下,因为可能有朋友是跳着看的)。

《Java 核心技术:卷 1 基础知识》,这本书本来是 Sun 公司的官方用书,是一本 Java 的入门参考书。对于 Java 初学者来说,是一本非常不错的值得时常翻阅的技术手册。书中有较多地方进行 Java 与 C++ 的比较,因为当时 Java 面世的时候,又被叫作 “C++ Killer”。而我在看这本书的时候,发现书中有很多 C++ 的东西,于是又去学习了 C++。学习 C++ 的时候,发现有很多 C 的东西不懂,又顺着去学习了 C。然后,C -> C++ -> Java 整条线融汇贯通,这对我未来的技术成长有非常大的帮助。有了上述的入门后,Java 的 Spring 框架是你玩 Java 所无法回避的东西,所以接下来是两本 Spring 相关的书,《Spring 实战》和《Spring Boot 实战》。前者是传统的 Spring,后者是新式的微服务的 Spring。如果你只想看一本的话,那么就看后者吧。

前面推荐的几本书可以帮你成功入门 Java,但想要进一步成长,就要看下面我推荐的几本进阶级别的书了。

接下来,你需要了解了一下如何编写高效的代码,于是必需看一下《Effective Java》(注意,这里我给的引用是第三版的,也是 2017 年末出版的书),这本书是模仿 Scott Meyers 的经典图书《Effective C++》的。Effective 这种书基本上都是各种经验之谈,所以,这是一本非常不错的书,你一定要读。这里需要推荐一下 Google Guava 库 ,这个库不但是 JDK 的升级库,其中有如:集合(collections)、缓存(caching)、原生类型支持(primitives support)、并发库(concurrency libraries)、通用注解(common annotations)、字符串处理(string processing)、I/O 等库,其还是 Effective Java 这本书中的那些经验的实践代表。 《Java 并发编程实战》,是一本完美的 Java 并发参考手册。书中从并发性和线程安全性的基本概念出发,介绍了如何使用类库提供的基本并发构建块,用于避免并发危险、构造线程安全的类及验证线程安全的规则,如何将小的线程安全类组合成更大的线程安全类,如何利用线程来提高并发应用程序的吞吐量,如何识别可并行执行的任务,如何提高单线程子系统的响应性,如何确保并发程序执行预期任务,如何提高并发代码的性能和可伸缩性等内容。最后介绍了一些高级主题,如显式锁、原子变量、非阻塞算法以及如何开发自定义的同步工具类。 了解如何编写出并发的程序,你还需要了解一下如何优化 Java 的性能。我推荐《Java 性能权威指南》。通过学习这本书,你可以比较大程度地提升性能测试的效果。其中包括:使用 JDK 中自带的工具收集 Java 应用的性能数据,理解 JIT 编译器的优缺点,调优 JVM 垃圾收集器以减少对程序的影响,学习管理堆内存和 JVM 原生内存的方法,了解如何最大程度地优化 Java 线程及同步的性能,等等。看完这本书后,如果你还有余力,想了解更多的底层细节,那么,你有必要去读一下《深入理解 Java 虚拟机》。 《Java 编程思想》,真是一本透着编程思想的书。上面的书让你从微观角度了解 Java,而这本书则可以让你从一个宏观角度了解 Java。这本书和 Java 核心技术的厚度差不多,但这本书的信息密度比较大。所以,读起来是非常耗大脑的,因为它会让你不断地思考。对于想学好 Java 的程序员来说,这是一本必读的书。 《精通 Spring 4.x》,也是一本很不错的书,就是有点厚,一共有 800 多页,都是干货。我认为其中最不错的是在分析原理,尤其是针对前面提到的 Spring 技术,应用与原理都讲得很透彻,IOC 和 AOP 也分析得很棒,娓娓道来。其对任何一个技术都分析得很细致和全面,不足之处就是内容太多了,所以导致很厚,但这并不影响它是一本不错的工具书。 当然,学 Java 你一定要学面向对象的设计模式,这里就只有一本经典的书《设计模式》。如果你觉得有点儿难度了,那么可以看一下《Head First 设计模式》。学习面向对象的设计模式时,你不要迷失在那 23 个设计模式中,你一定要明白这两个原则: Program to an ‘interface’, not an 'implementation’

使用者不需要知道数据类型、结构、算法的细节。使用者不需要知道实现细节,只需要知道提供的接口。利于抽象、封装,动态绑定,多态。符合面向对象的特质和理念。 Favor ‘object composition’ over 'class inheritance’

继承需要给子类暴露一些父类的设计和实现细节。父类实现的改变会造成子类也需要改变。我们以为继承主要是为了代码重用,但实际上在子类中需要重新实现很多父类的方法。继承更多的应该是为了多态。

至此,如果你把上面的这些知识都融汇贯通的话,那么,你已是一个高级的 Java 程序员了,我保证你已经超过了绝大多数程序员了。基本上来说,你在技术方面是可以进入到一线公司的,而且还不是一般的岗位,至少是高级程序员或是初级架构师的级别了。

学习 Go 语言

C 语言太原始了,C++ 太复杂了,Go 语言是不二之选。有了 C/C++ 的功底,学习 Go 语言非常简单。

首推 Go by Example 作为你的入门教程。然后,Go 101 也是一个很不错的在线电子书。如果你想看纸书的话,The Go Programming Language 一书在豆瓣上有 9.2 分,但是国内没有卖的。(当然,我以前也写过两篇入门的供你参考 “GO 语言简介(上)- 语法” 和 “GO 语言简介(下)- 特性”)。

另外,Go 语言官方的 Effective Go 是必读的,这篇文章告诉你如何更好地使用 Go 语言,以及 Go 语言中的一些原理。

Go 语言最突出之处是并发编程,Unix 老牌黑客罗勃·派克(Rob Pike)在 Google I/O 上的两个分享,可以让你学习到一些并发编程的模式。

Go Concurrency Patterns( 幻灯片和演讲视频)。Advanced Go Concurrency Patterns(幻灯片、演讲视频)。

然后,Go 在 GitHub 的 wiki 上有好多不错的学习资源,你可以从中学习到多。比如:

Go 精华文章列表。Go 相关博客列表。Go Talks。

此外,还有个内容丰富的 Go 资源列表 Awesome Go,推荐看看。

理论学科

数据结构和算法

算法是比较难学习的,而且学习“算法”是需要智商的。数组、链表、哈希表、二叉树、排序算法等一些基础知识,对大多数人来说是没什么问题的。但是一旦进入到路径规划、背包问题、字符串匹配、动态规划、递归遍历等一些比较复杂的问题上,就会让很多人跟不上了,不但跟不上,而且还会非常痛苦。是的,解决算法问题的确是可以区分人类智商的一个比较好的方式,这也是为什么好些公司用算法题当面试题来找到智商比较高的程序员。

然而,在很多时候,我们在工作中却发现根本用不到算法,或是一些基本的算法也没有必要实现,只需要使用一下第三方的库就好了。于是,导致社会上出现很多“算法无用论”的声音。

对此,我想说,算法真的很重要。我这 20 年的经历告诉我,无论是做业务还是做底层系统,经常需要使用算法处理各种各样的问题。比如,业务上我需要用算法比较两个数组中差异的布隆过滤器,或是在做监控系统时实时计算过去一分钟的 P99 统计时的蓄水池算法,或是数据库的 B+ 树索引,还有 Linux 内核中的 epoll 的红黑树,还有在做服务调度里的“背包问题”等都会用算法,真的是会本质上帮助到你,也是会让你瞬间会产生成就感的事情。

虽然算法很难,需要智商,但我还是想鼓励你,这其中是有很多的套路是可以学习的,一旦学会这些套路,你会受益无穷的。

这里有几本书着重推荐一下。

基础知识。《算法》,是算法领域经典的参考书,不但全面介绍了关于算法和数据结构的必备知识,还给出了每位程序员应知应会的 50 个算法,并提供了实际代码。最不错的是,其深入浅出的算法介绍,让一些比较难的算法也变得容易理解,尤其是书中对红黑树的讲解非常精彩。其中,还有大量的图解,详尽的代码和讲解,也许是最好的数据结构入门图书。不好的是不深,缺乏进一步的算法设计内容,甚至连动态规划都未提及。另外,如果你觉得算法书比较枯燥的话,你可以看看这本有趣的《算法图解》。理论加持。如果说上面这本书偏于实践和工程,而你看完后,对算法和数据结构的兴趣更浓了,那么你可以再看看另一本也是很经典的偏于理论方面的书——《算法导论》。虽然其中的一些理论知识在《算法》那本书中也有提过,但《算法导论》这本书更为专业一些,是美国计算机科学本科生的教科书。思维改善。还有一本叫《编程珠玑》的书,写这本书的人是世界著名计算机科学家乔恩·本特利(Jon Bentley),被誉为影响算法发展的十位大师之一。你可能不认识这个人,但是你知道他的学生有多厉害吗?我例举几个,一个是 Tcl 语言设计者约翰·奥斯德奥特(John Ousterhout),另一个是 Java 语言设计者詹姆斯·高斯林(James Gosling),还有一个是《算法导论》作者之一查尔斯·雷斯尔森(Charles Leiserson),还有好多好多。这本书也是很经典的算法书,其中都是一些非常实际的问题,并以其独有的洞察力和创造力,来引导读者理解并学会解决这些问题的方法,也是一本可以改善你思维方式的书。

然后,你需要去做一些题来训练一下自己的算法能力,这里就要推荐 LeetCode 这个网站了。它是一个很不错的做算法训练的地方。现在也越做越好了。基本上来说,这里会有两类题。

基础算法题。其中有大量的算法题,解这些题都是有套路的,不是用递归(深度优先 DFS、广度优先 BFS),就是要用动态规划(Dynamic Programming),或是折半查找(Binary Search),或是回溯(Back tracing),或是分治法(Divide and Conquer),还有大量的对树、数组、链表、字符串和 hash 表的操作。通过做这些题能让你对这些最基础的算法的思路有非常扎实的了解和训练。对我而言,Dynamic Programming 是我的短板,尤其是一些比较复杂的问题,在推导递推公式上总是有思维的缺陷(数学是我的硬伤)。做了这些题后,我能感到我在动态编程的思路上受到了很大的启发。编程题。比如:atoi、strstr、add two nums、括号匹配、字符串乘法、通配符匹配、文件路径简化、Text Justification、反转单词等,这些题的 Edge Case 和 Corner Case 有很多。这些题需要你想清楚了再干,只要你稍有疏忽,就会有几个 case 让你痛不欲生,而且一不小心就会让你的代码写得又臭又长,无法阅读。通过做这些题,可以非常好地训练你对各种情况的考虑,以及你对程序代码组织的掌控(其实就是其中的状态变量)。

我觉得每个程序员都应该花时间和精力做这些题,因为你会从这些题中得到很大的收益。我在 Leetcode 上做的一些题的代码在这——我的 GitHub 上,可以给你一些参考。

如果能够把这些算法能力都掌握了,那么你就有很大的概率可以很容易地通过这世界上最优的公司的面试,比如:Google、Amazon、Facebook 之类的公司。对你来说,如果能够进入到这些公司里工作,那么你未来的想像空间也会大很多。

最后,我们要知道这个世界上的数据结构和算法有很多,下面给出了两个网站。

List of Algorithms ,这个网站罗列了非常多的算法,完全可以当成一个算法字典,或是用来开阔眼界。还有一个数据结构动画图的网站 Data Structure Visualizations。

其它理论基础知识

下面这些书,基本上是计算机科学系的大学教材。如果你想有科班出身的理论基础,那么这些书是必读的。当然,这些理论基础知识比较枯燥,但我觉得如果你想成为专业的程序员,那么应该要找时间读一下。

《数据结构与算法分析》,这本书曾被评为 20 世纪顶尖的 30 部计算机著作之一,作者 Mark Allen Weiss 在数据结构和算法分析方面卓有建树,他在数据结构和算法分析等方面的著作尤其畅销,并广受好评,已被世界 500 余所大学用作教材。《数据库系统概念》,它是数据库系统方面的经典教材之一。国际上许多著名大学包括斯坦福大学、耶鲁大学、德克萨斯大学、康奈尔大学、伊利诺伊大学、印度理工学院等都采用本书作为教科书。这本书全面介绍了数据库系统的各种知识,透彻阐释数据库管理的基本概念。不仅讨论了数据库查询语言、模式设计、数据仓库、数据库应用开发、基于对象的数据库和 XML、数据存储和查询、事务管理、数据挖掘与信息检索以及数据库系统体系结构等方面的内容,而且对性能评测标准、性能调整、标准化以及空间与地理数据、事务处理监控等高级应用主题进行了广泛讨论。《现代操作系统》,这本书是操作系统领域的经典之作,书中集中讨论了操作系统的基本原理,包括进程、线程、存储管理、文件系统、输入 / 输出、死锁等,同时还包含了有关计算机安全、多媒体操作系统、掌上计算机操作系统、微内核、多核处理机上的虚拟机以及操作系统设计等方面的内容。《计算机网络》,这本书采用了独创的自顶向下方法,即从应用层开始沿协议栈向下讲解计算机网络的基本原理,强调应用层范例和应用编程接口,内容深入浅出,注重教学方法,理论与实践相结合。新版中还增加了无线和移动网络一章,并扩充了对等网络、BGP、MPLS、网络安全、广播选路和因特网编址及转发方面的材料。是一本不可多得的教科书。《计算机程序的构造和解释》,这本书也很经典,是 MIT 的计算机科学系的教材。这本书中主要证实了很多程序是怎么构造出来的,以及程序的本质是什么。整本书主要是使用 Scheme/Lisp 语言,从数据抽象、过程抽象、迭代、高阶函数等编程和控制系统复杂性的思想,到数据结构和算法,到编译器 / 解释器、编程语言设计。《编译原理》,这本书又叫 " 龙书 ",其全面、深入地探讨了编译器设计方面的重要主题,包括词法分析、语法分析、语法制导定义和语法制导翻译、运行时刻环境、目标代码生成、代码优化技术、并行性检测以及过程间分析技术,并在相关章节中给出大量的实例。与上一版相比,本书进行了全面的修订,涵盖了编译器开发方面的最新进展。每章中都提供了大量的系统及参考文献。

系统知识

进入专业的编程领域,学习系统知识是非常关键的一部分。

首先推荐的是翻译版图书《深入理解计算机系统》,原书名为《Computer Systems A Programmer’s Perspective》。不过,这本书叫做《程序员所需要了解的计算机知识》更为合适。

本书的最大优点是为程序员描述计算机系统的实现细节,帮助其在大脑中构造一个层次型的计算机系统。从最底层的数据在内存中的表示到流水线指令的构成,到虚拟存储器,到编译系统,到动态加载库,到最后的用户态应用。通过掌握程序是如何映射到系统上,以及程序是如何执行的,你能够更好地理解程序的行为为什么是这样的,以及效率低下是如何造成的。

再强调一下,这本书是程序员必读的一本书!

然后就是美国计算机科学家 理查德·史蒂文斯(Richard Stevens) 的三套巨经典无比的书。(理查德·史蒂文斯于 1999 年 9 月 1 日离世,终年 48 岁。死因不详,有人说是滑雪意外,有人说是攀岩意外,有人说是滑翔机意外。总之,家人没有透露。大师的 个人主页 今天还可以访问。)

《Unix 高级环境编程》。《Unix 网络编程》 第 1 卷 套接口 API 、第 2 卷 进程间通信 。《TCP/IP 详解 卷 I 协议》。

这几本书的地位我就不多说了,你可以自己看相关的书评。但是,这三本书可能都不容易读,一方面是比较厚,另一方面是知识的密度太大了,所以,读起来有点枯燥和乏味。但是,这没办法,你得忍住。

这里要重点说一下《TCP/IP 详解》这本书,是一本很奇怪的书。这本书迄今至少被 近五百篇学术论文引用过 。这本写给工程师看的书居然被各种学院派的论文来引用,也是很神奇的一件事了。而且,虽然理查德·史蒂文斯不是 TCP 的发明人,但是这本书中把这个协议深入浅出地讲出来,还画了几百张时序图,也是令人叹为观止了。

如果你觉得上面这几本经典书比较难啃,你可以试试下面这些通俗易懂的(当然,如果读得懂上面那三本的,下面的这些也就不需要读了)。

《Linux C 编程一站式学习》。《TCP/IP 网络编程》。《图解 TCP/IP》,这本书其实并不是只讲了 TCP/IP,应该是叫《计算机网络》才对,主要是给想快速入门的人看的。《The TCP/IP Guide》,这本书在豆瓣上的评分 9.2,这里给的链接是这本书的 HTML 英文免费版的,里面的图画得很精彩。

另外,学习网络协议不单只是看书,你最好用个抓包工具看看这些网络包是什么样的。所以,这里推荐一本书《Wireshark 数据包分析实战》。在这本书中,作者结合一些简单易懂的实际网络案例,图文并茂地演示使用 Wireshark 进行数据包分析的技术方法,可以让我们更好地了解和学习网络协议。当然,也拥有了一定的黑客的技能。

看完《Unix 高级环境编程》后,你可以趁热打铁看看《Linux/Unix 系统编程手册》或是罗伯特·拉姆(Robert Love)的 Linux System Programming 英文电子版 。其中文翻译版Linux 系统编程 也值得一读,虽然和《Unix 高级环境编程》很像,不过其主要突出的是 Linux 的一些关键技术和相关的系统调用。

关于 TCP 的东西,你还可以看看下面这一系列的文章。

Let’s code a TCP/IP stack, 1: Ethernet & ARPLet’s code a TCP/IP stack, 2: IPv4 & ICMPv4Let’s code a TCP/IP stack, 3: TCP Basics & HandshakeLet’s code a TCP/IP stack, 4: TCP Data Flow & Socket APILet’s code a TCP/IP stack, 5: TCP Retransmission

对于系统知识,我认为主要有以下一些学习要点。

用这些系统知识操作一下文件系统,实现一个可以拷贝目录树的小程序。用 fork / wait / waitpid 写一个多进程的程序,用 pthread 写一个多线程带同步或互斥的程序。比如,多进程购票的程序。用 signal / kill / raise / alarm / pause / sigprocmask 实现一个多进程间的信号量通信的程序。学会使用 gcc 和 gdb 来编程和调试程序(参看我的《用 gdb 调试程序》一、二、三、四、五、六、七)。学会使用 makefile 来编译程序(参看我的《跟我一起写 makefile》一、二、三、四、五、六、七、八、九、十、十一、十二、十三、十四)。Socket 的进程间通信。用 C 语言写一个 1 对 1 的聊天小程序,或是一个简单的 HTTP 服务器。

C10K 问题

然后,当你读完《Unix 网络编程》后,千万要去读一下 “C10K Problem (中文翻译版)”。提出这个问题的人叫丹·凯格尔(Dan Kegel),目前在 Google 任职。

他从 1978 年起开始接触计算机编程,是 Winetricks 的作者,也是 Wine 1.0 的管理员,同时也是 Crosstool( 一个让 gcc/glibc 编译器更易用的工具套件)的作者。还是 Java JSR 51 规范的提交者并参与编写了 Java 平台的 NIO 和文件锁,同时参与了 RFC 5128 标准中有关 NAT 穿越(P2P 打洞)技术的描述和定义。

C10K 问题本质上是操作系统处理大并发请求的问题。对于 Web 时代的操作系统而言,对于客户端过来的大量的并发请求,需要创建相应的服务进程或线程。这些进程或线程多了,导致数据拷贝频繁(缓存 I/O、内核将数据拷贝到用户进程空间、阻塞), 进程 / 线程上下文切换消耗大,从而导致资源被耗尽而崩溃。这就是 C10K 问题的本质。

了解这个问题,并了解操作系统是如何通过多路复用的技术来解决这个问题的,有助于你了解各种 I/O 和异步模型,这对于你未来的编程和架构能力是相当重要的。

另外,现在,整个世界都在解决 C10M 问题,推荐看看 The Secret To 10 Million Concurrent Connections -The Kernel Is The Problem, Not The Solution 一文。

实践项目

我们已经学习完了编程语言、理论学科和系统知识三部分内容,下面就来做几个实践项目,小试牛刀一下。实现语言可以用 C、C++ 或 Java。

实现一个 telnet 版本的聊天服务器,主要有以下需求。

每个客户端可以用使用telnet ip:port的方式连接到服务器上。新连接需要用用户名和密码登录,如果没有,则需要注册一个。然后可以选择一个聊天室加入聊天。管理员有权创建或删除聊天室,普通人员只有加入、退出、查询聊天室的权力。聊天室需要有人数限制,每个人发出来的话,其它所有的人都要能看得到。

实现一个简单的 HTTP 服务器,主要有以下需求。

解释浏览器传来的 HTTP 协议,只需要处理 URL path。然后把所代理的目录列出来。在浏览器上可以浏览目录里的文件和下级目录。如果点击文件,则把文件打开传给浏览器(浏览器能够自动显示图片、PDF,或 HTML、CSS、JavaScript 以及文本文件)。如果点击子目录,则进入到子目录中,并把子目录中的文件列出来。

实现一个生产者 / 消费者消息队列服务,主要有以下需求。

消息队列采用一个 Ring-buffer 的数据结构。可以有多个 topic 供生产者写入消息及消费者取出消息。需要支持多个生产者并发写。需要支持多个消费者消费消息(只要有一个消费者成功处理消息就可以删除消息)。消息队列要做到不丢数据(要把消息持久化下来)。能做到性能很高。

到今天,我们已经学习完了专业编程方面最为重要的三部分内容:编程语言、理论学科和系统知识,我们针对这些内容做个小结。如果想看完我推荐的那些书和知识,并能理解和掌握,我估计怎么也得需要 4-5 年的时间。嗯,是的,就是一个计算机科学系科班出身的程序员需要学习的一些东西。这其中,最重要的是下面这几点。

编程语言。以工业级的 C、C++、Java 这三门语言为主,这三门语言才是真正算得上工业级的编程语言,因为有工业级的标准化组织在控制着这几门语言,而且也有工业级的企业应用。尤其是 Java,还衍生出了大量的企业级架构上的开源生态。你至少需要掌握 C 语言和 Java 语言,这对你以后面对各式各样的编程语言是非常重要的。

此外,还推荐学习 Go 语言,它已成为云计算领域事实上的标准语言,尤其是在 Docker、Kubernetes 等项目中。而且,Go 语言在国内外一些知名公司中有了一定的应用和实践,并且其生态圈也越来越好。

算法和数据结构。这个太重要了,尤其是最基础的算法和数据结构,这是任何一个称职的程序员都需要学习和掌握的。你必需要掌握。

计算机的相关系统。你至少要掌握三个系统的基础知识,一个是操作系统,一个是网络系统,还有一个是数据库系统。它们分别代表着计算机基础构架的三大件——计算、存储、网络。

如果你能够走到这里,把前面的那些知识都了解了(不用精通,因为精通是需要时间和实践来慢慢锤炼出来的,所以,你也不用着急),那么你已经是一个合格的程序员了,而且你的潜力和可能性是非常非常高的。

如果经历过这些比较枯燥的理论知识,而且你还能有热情和成就感,那么我要恭喜你了。因为你已经超过了绝大多数人,而且还是排在上游的比较抢手的程序员了。我相信你至少可以找到年薪 50 万以上的工作了。

但是,你还需要很多的经验或是一些实践,以及一些大系统大项目的实际动手的经验。没关系,我们后面会有教你怎么实操的方法和攻略。

但是,往后面走,你需要开始需要术业有专攻了。下面给一些建议的方向。

底层方向:操作系统、文件系统、数据库、网络……架构方向:分布式系统架构、微服务、DevOps、Cloud Native……数据方向:大数据、机器学习、人工智能……前端方向:你对用户体验或是交互更感兴趣,那么你走前端的路吧。其它方向:比如,安全开发、运维开发、嵌入式开发……

这些方向你要仔细选择,因为一旦选好,就要勇往直前地走下去,当然,你要回头转别的方向也没什么问题,因为你有前面的这些基础知识在身,所以,不用害怕。只是不同的方向上会有不同的经验积累,经验积累是看书看不来的,这个是转方向的成本。

下篇文章,我们将进入《软件设计篇》。敬请期待。

软件设计篇

编程范式

编程范式

学习编程范式可以让你明白编程的本质和各种语言的编程方式。因此,我推荐以下一些资料,以帮助你系统化地学习和理解。

一个是我在极客时间写的《编程范式游记》系列文章,目录如下。

编程范式游记(1)- 起源编程范式游记(2)- 泛型编程编程范式游记(3)- 类型系统和泛型的本质编程范式游记(4)- 函数式编程编程范式游记(5)- 修饰器模式编程范式游记(6)- 面向对象编程编程范式游记(7)- 基于原型的编程范式编程范式游记(8)- Go 语言的委托模式编程范式游记(9)- 编程的本质编程范式游记(10)- 逻辑编程范式编程范式游记(11)- 程序世界里的编程范式 Wikipedia: Programming paradigm ,维基百科上有一个编程范式的页面,顺着这个页面看下去,你可以看到很多很多有用的和编程相关的知识。这些东西对你的编程技能的提高会非常非常有帮助。 Six programming paradigms that will change how you think about coding,中文翻译版为 六个编程范型将改变你对编程的看法。这篇文章讲了默认支持并发(Concurrent by default)、依赖类型(Dependent types)、连接性语言(Concatenative languages)、声明式编程(Declarative programming)、符号式编程(Symbolic programming)、基于知识的编程(Knowledge-based programming)等六种不太常见的编程范式,并结合了一些你没怎么听说过的语言来分别进行讲述。 比如在讲 Concatenative languages 时,以 Forth、cat 和 joy 三种语言为例讲述这一编程范式背后的思想——语言中的所有内容都是一个函数,用于将数据推送到堆栈或从堆栈弹出数据;程序几乎完全通过功能组合来构建(concatenation is composition)。作者认为,这些编程范式背后的思想十分有魅力,能够改变对编程的思考。我看完此文,对此也深信不疑。虽然这些语言和编程范式不常用到,但确实能在思想层面给予人很大的启发。这也是我推荐此文的目的。 Programming Paradigms for Dummies: What Every Programmer Should Know ,这篇文章的作者彼得·范·罗伊(Peter Van Roy)是比利时鲁汶大学的计算机科学教师。他在这篇文章里分析了编程语言在历史上的演进,有哪些典型的、值得研究的案例,里面体现了哪些值得学习的范式。 比如,在分布式编程领域,他提到了 Erlang、E、Distributed Oz 和 Didactic Oz 这四种编程语言。虽然它们都是分布式编程语言,但各有特色,各自解决了不同的问题。通过这篇文章能学到不少在设计编程语言时要考虑的问题,让你重新审视自己所使用的编程语言应该怎样用才能用好,有什么局限性,这些局限性能否被克服等。 斯坦福大学公开课:编程范式,这是一门比较基础且很详细的课程,适合学习编程语言的初学者。它通过讲述 C、C++、并发编程、Scheme、Python 这 5 门语言,介绍了它们各自不同的编程范式。以 C 语言为例,它解释了 C 语言的基本要素,如指针、内存分配、堆、C 风格的字符串等,并解释了为什么 C 语言会在泛型编程、多态等方面有局限性。通过学习这门课程,你会对一些常用的编程范式有所了解。

一些软件设计的相关原则

Don’t Repeat Yourself (DRY) ,DRY 是一个最简单的法则,也是最容易被理解的。但它也可能是最难被应用的(因为要做到这样,我们需要在泛型设计上做相当的努力,这并不是一件容易的事)。它意味着,当在两个或多个地方发现一些相似代码的时候,我们需要把它们的共性抽象出来形成一个唯一的新方法,并且改变现有地方的代码让它们以一些合适的参数调用这个新的方法。 Keep It Simple, Stupid(KISS) ,KISS 原则在设计上可能最被推崇,在家装设计、界面设计和操作设计上,复杂的东西越来越被众人所鄙视了,而简单的东西越来越被人所认可。宜家(IKEA)简约、高效的家居设计和生产思路;微软(Microsoft)“所见即所得”的理念;谷歌(Google)简约、直接的商业风格,无一例外地遵循了“KISS”原则。也正是“KISS”原则,成就了这些看似神奇的商业经典。而苹果公司的 iPhone 和 iPad 将这个原则实践到了极至。 Program to an interface, not an implementation,这是设计模式中最根本的哲学,注重接口,而不是实现,依赖接口,而不是实现。接口是抽象是稳定的,实现则是多种多样的。在面向对象的 S.O.L.I.D 原则中会提到我们的依赖倒置原则,就是这个原则的另一种样子。还有一条原则叫 Composition over inheritance(喜欢组合而不是继承),这两条是那 23 个经典设计模式中的设计原则。 You Ain’t Gonna Need It (YAGNI) ,这个原则简而言之为——只考虑和设计必须的功能,避免过度设计。只实现目前需要的功能,在以后你需要更多功能时,可以再进行添加。如无必要,勿增复杂性。软件开发是一场 trade-off 的博弈。 Law of Demeter,迪米特法则 (Law of Demeter),又称“最少知识原则”(Principle of Least Knowledge),其来源于 1987 年荷兰大学的一个叫做 Demeter 的项目。克雷格·拉尔曼(Craig Larman)把 Law of Demeter 又称作“不要和陌生人说话”。在《程序员修炼之道》中讲 LoD 的那一章将其叫作“解耦合与迪米特法则”。 关于迪米特法则有一些很形象的比喻:1) 如果你想让你的狗跑的话,你会对狗狗说还是对四条狗腿说?2) 如果你去店里买东西,你会把钱交给店员,还是会把钱包交给店员让他自己拿?和狗的四肢说话?让店员自己从钱包里拿钱?这听起来有点儿荒唐,不过在我们的代码里这几乎是见怪不怪的事情了。对于 LoD,正式的表述如下: 对于对象 ‘O’ 中一个方法’M’,M 应该只能够访问以下对象中的方法:

对象 O;与 O 直接相关的 Component Object;由方法 M 创建或者实例化的对象;作为方法 M 的参数的对象。 [面向对象的 S.O.L.I.D 原则](">http://en.wikipedia.org/wiki/Solid_(object-oriented_design):

SRP(Single Responsibility Principle)- 职责单一原则。关于单一职责原则,其核心的思想是:一个类,只做一件事,并把这件事做好,其只有一个引起它变化的原因。单一职责原则可以看作是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。 职责过多,可能引起它变化的原因就越多,这将导致职责依赖,相互之间就产生影响,从而极大地损伤其内聚性和耦合度。单一职责,通常意味着单一的功能,因此不要为一个模块实现过多的功能点,以保证实体只有一个引起它变化的原因。

OCP(Open/Closed Principle)- 开闭原则。关于开发封闭原则,其核心的思想是:模块是可扩展的,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。LSP(Liskov substitution principle)- 里氏代换原则。软件工程大师罗伯特·马丁(Robert C. Martin)把里氏代换原则最终简化为一句话:“Subtypes must be substitutable for their base types”。也就是,子类必须能够替换成它们的基类。即子类应该可以替换任何基类能够出现的地方,并且经过替换以后,代码还能正常工作。另外,不应该在代码中出现 if/else 之类对子类类型进行判断的条件。里氏替换原则 LSP 是使代码符合开闭原则的一个重要保证。正是由于子类型的可替换性才使得父类型的模块在无需修改的情况下就可以扩展。ISP(Interface Segregation Principle )- 接口隔离原则。接口隔离原则的意思是把功能实现在接口中,而不是类中,使用多个专门的接口比使用单一的总接口要好。举个例子,我们对电脑有不同的使用方式,比如:写作、通讯、看电影、打游戏、上网、编程、计算和数据存储等。 如果我们把这些功能都声明在电脑的抽象类里面,那么,我们的上网本、PC 机、服务器和笔记本的实现类都要实现所有的这些接口,这就显得太复杂了。所以,我们可以把这些功能接口隔离开来,如工作学习接口、编程开发接口、上网娱乐接口、计算和数据服务接口,这样,我们的不同功能的电脑就可以有所选择地继承这些接口。

DIP(Dependency Inversion Principle)- 依赖倒置原则。高层模块不应该依赖于低层模块的实现,而是依赖于高层抽象。举个例子,墙面的开关不应该依赖于电灯的开关实现,而是应该依赖于一个抽象的开关的标准接口。这样,当我们扩展程序的时候,开关同样可以控制其它不同的灯,甚至不同的电器。也就是说,电灯和其它电器继承并实现我们的标准开关接口,而开关厂商就可以不需要关于其要控制什么样的设备,只需要关心那个标准的开关标准。这就是依赖倒置原则。 CCP(Common Closure Principle) - 共同封闭原则,一个包中所有的类应该对同一种类型的变化关闭。一个变化影响一个包,便影响了包中所有的类。一个更简短的说法是:一起修改的类,应该组合在一起(同一个包里)。如果必须修改应用程序里的代码,那么我们希望所有的修改都发生在一个包里(修改关闭),而不是遍布在很多包里。 CCP 原则就是把因为某个同样的原因而需要修改的所有类组合进一个包里。如果两个类从物理上或者从概念上联系得非常紧密,它们通常一起发生改变,那么它们应该属于同一个包。CCP 延伸了开闭原则(OCP)的“关闭”概念,当因为某个原因需要修改时,把需要修改的范围限制在一个最小范围内的包里。 CRP(Common Reuse Principle)- 共同重用原则 ,包的所有类被一起重用。如果你重用了其中的一个类,就重用全部。换个说法是,没有被一起重用的类不应该组合在一起。CRP 原则帮助我们决定哪些类应该被放到同一个包里。依赖一个包就是依赖这个包所包含的一切。 当一个包发生了改变,并发布新的版本,使用这个包的所有用户都必须在新的包环境下验证他们的工作,即使被他们使用的部分没有发生任何改变。因为如果包中包含未被使用的类,即使用户不关心该类是否改变,但用户还是不得不升级该包并对原来的功能加以重新测试。CCP 则让系统的维护者受益。CCP 让包尽可能大(CCP 原则加入功能相关的类),CRP 则让包尽可能小(CRP 原则剔除不使用的类)。它们的出发点不一样,但不相互冲突。 好莱坞原则 - Hollywood Principle ,好莱坞原则就是一句话——“don’t call us, we’ll call you.”。意思是,好莱坞的经纪人不希望你去联系他们,而是他们会在需要的时候来联系你。也就是说,所有的组件都是被动的,所有的组件初始化和调用都由容器负责。 简单来讲,就是由容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在:1) 不创建对象,而是描述创建对象的方式。2)在代码中,对象与服务没有直接联系,而是容器负责将这些联系在一起。控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转。好莱坞原则就是IoC(Inversion of Control) 或DI(Dependency Injection)的基础原则。 高内聚, 低耦合 & - High Cohesion & Low/Loose coupling,这个原则是 UNIX 操作系统设计的经典原则,把模块间的耦合降到最低,而努力让一个模块做到精益求精。内聚,指一个模块内各个元素彼此结合的紧密程度;耦合指一个软件结构内不同模块之间互连程度的度量。内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身。对于面向对象来说,你也可以看看马萨诸塞州戈登学院的面向对象课中的这一节讲义High Cohesion and Low Coupling。 CoC(Convention over Configuration)- 惯例优于配置原则 ,简单点说,就是将一些公认的配置方式和信息作为内部缺省的规则来使用。例如,Hibernate 的映射文件,如果约定字段名和类属性一致的话,基本上就可以不要这个配置文件了。你的应用只需要指定不 convention 的信息即可,从而减少了大量 convention 而又不得不花时间和精力啰里啰嗦的东东。 配置文件在很多时候相当影响开发效率。Rails 中很少有配置文件(但不是没有,数据库连接就是一个配置文件)。Rails 的 fans 号称其开发效率是 Java 开发的 10 倍,估计就是这个原因。Maven 也使用了 CoC 原则,当你执行 mvn -compile 命令的时候,不需要指定源文件放在什么地方,而编译以后的 class 文件放置在什么地方也没有指定,这就是 CoC 原则。 SoC (Separation of Concerns) - 关注点分离 ,SoC 是计算机科学中最重要的努力目标之一。这个原则,就是在软件开发中,通过各种手段,将问题的各个关注点分开。如果一个问题能分解为独立且较小的问题,就是相对较易解决的。问题太过于复杂,要解决问题需要关注的点太多,而程序员的能力是有限的,不能同时关注于问题的各个方面。 正如程序员的记忆力相对于计算机知识来说那么有限一样,程序员解决问题的能力相对于要解决的问题的复杂性也是一样的非常有限。在我们分析问题的时候,如果我们把所有的东西混在一起讨论,那么就只会有一个结果——乱。实现关注点分离的方法主要有两种,一种是标准化,另一种是抽象与包装。标准化就是制定一套标准,让使用者都遵守它,将人们的行为统一起来,这样使用标准的人就不用担心别人会有很多种不同的实现,使自己的程序不能和别人的配合。 就像是开发镙丝钉的人只专注于开发镙丝钉就行了,而不用关注镙帽是怎么生产的,反正镙帽和镙丝钉按照标准来就一定能合得上。不断地把程序的某些部分抽象并包装起来,也是实现关注点分离的好方法。一旦一个函数被抽象出来并实现了,那么使用函数的人就不用关心这个函数是如何实现的。同样的,一旦一个类被抽象并实现了,类的使用者也不用再关注于这个类的内部是如何实现的。诸如组件、分层、面向服务等这些概念都是在不同的层次上做抽象和包装,以使得使用者不用关心它的内部实现细节。 DbC(Design by Contract)- 契约式设计 ,DbC 的核心思想是对软件系统中的元素之间相互合作以及“责任”与“义务”的比喻。这种比喻从商业活动中“客户”与“供应商”达成“契约”而得来。如果在程序设计中一个模块提供了某种功能,那么它要:

期望所有调用它的客户模块都保证一定的进入条件:这就是模块的先验条件(客户的义务和供应商的权利,这样它就不用去处理不满足先验条件的情况)。保证退出时给出特定的属性:这就是模块的后验条件(供应商的义务,显然也是客户的权利)。在进入时假定,并在退出时保持一些特定的属性:不变式。 ADP(Acyclic Dependencies Principle)- 无环依赖原则 ,包(或服务)之间的依赖结构必须是一个直接的无环图形,也就是说,在依赖结构中不允许出现环(循环依赖)。如果包的依赖形成了环状结构,怎么样打破这种循环依赖呢? 有两种方法可以打破这种循环依赖关系:第一种方法是创建新的包,如果 A、B、C 形成环路依赖,那么把这些共同类抽出来放在一个新的包 D 里。这样就把 C 依赖 A 变成了 C 依赖 D 以及 A 依赖 D,从而打破了循环依赖关系。第二种方法是使用 DIP(依赖倒置原则)和 ISP(接口分隔原则)设计原则。无环依赖原则(ADP)为我们解决包之间的关系耦合问题。在设计模块时,不能有循环依赖。

一些软件设计的读物

《领域驱动设计》 ,本书是领域驱动设计方面的经典之作。全书围绕着设计和开发实践,结合若干真实的项目案例,向读者阐述如何在真实的软件开发中应用领域驱动设计。书中给出了领域驱动设计的系统化方法,并将人们普遍接受的一些实践综合到一起,融入了作者的见解和经验,展现了一些可扩展的设计新实践、已验证过的技术以及便于应对复杂领域的软件项目开发的基本原则。《UNIX 编程艺术》 ,这本书主要介绍了 Unix 系统领域中的设计和开发哲学、思想文化体系、原则与经验,由公认的 Unix 编程大师、开源运动领袖人物之一埃里克·雷蒙德(Eric S. Raymond)倾力多年写作而成。包括 Unix 设计者在内的多位领域专家也为本书贡献了宝贵的内容。本书内容涉及社群文化、软件开发设计与实现,覆盖面广、内容深邃,完全展现了作者极其深厚的经验积累和领域智慧。《Clean Architecture》,如果你读过 《Clean Code》 和 《The Clean Coder》这两本书。你就能猜得到这种 Clean 系列一定也是出自“Bob 大叔”之手。没错,就是 Bob 大叔的心血之作。除了这个网站,《Clean Architecture》也是一本书,这是一本很不错的架构类图书。对软件架构的元素、方法等讲得很清楚。示例都比较简单,并带一些软件变化历史的讲述,很开阔视野。The Twelve-Factor App ,如今,软件通常会作为一种服务来交付,它们被称为网络应用程序,或软件即服务(SaaS)。12-Factor 为构建 SaaS 应用提供了方法论,这也是架构师必读的文章。(中译版) 这篇文章在业内的影响力很大,必读!Avoid Over Engineering ,有时候,我们会过渡设计我们的系统,过度设计会把我们带到另外一个复杂度上,所以,我们需要一些工程上的平衡。这篇文章是一篇非常不错地告诉你什么是过度设计的文章。Instagram Engineering’s 3 rules to a scalable cloud application architecture ,Instagram 工程的三个黄金法则:1)使用稳定可靠的技术(迎接新的技术);2)不要重新发明轮子;3)Keep it very simple。我觉得这三条很不错。其实,Amazon 也有两条工程法则,一个是自动化,一个是简化。How To Design A Good API and Why it Matters - Joshua Bloch ,Google 的一个分享,关于如何设计好一个 API。关于 Restful API 的设计,你可以学习并借鉴一下下面这些文章。

Best Practices for Designing a Pragmatic RESTful APIIdeal REST API designHTTP API Design GuideMicrosoft REST API GuidelinesIBM Watson REST API GuidelinesZalando RESTful API and Event Scheme Guidelines The Problem With Logging ,一篇关于程序打日志的短文,可以让你知道一些可能以往不知道的打日志需要注意的问题。Concurrent Programming for Scalable Web Architectures ,这是一本在线的免费书,教你如何架构一个可扩展的高性能的网站。其中谈到了一些不错的设计方法和知识。

高手成长篇

Linux 系统、内存和网络(系统底层知识)

这一篇章,是本系列中最长的一篇,其中包括了如下的内容。

系统底层相关。 主要是以 Linux 系统为主,其中有大量的文章可以让你学习到 Linux 内核,以及内存、网络、异步 I/O 模型、Lock-free 的无锁编程,还有其它和系统底层相关的东西。注意,系统底层要是深下去是可以完全不见底的。而且内存方面的知识也是比较多的,所以,这里还是主要给出一些非常有价值的基础性的知识和技术。学好这些东西,你会对系统有很深的理解,而且可以把这些知识反哺到架构设计上来。数据库相关。数据库方面主要是 MySQL 和各种开源 NoSQL 的一些相关的有价值的文章和导读,主要是让你对这些数据库的内在有一定的了解,但又不会太深。真正的深入是需要扎入到源代码中的。需要说明的是,这块技术不是我的长项,但又是每个架构师需要知道的,所以,我在这里给的学习资源可能会比较浅,这点还希望你来补充和指正。分布式架构。这一部分是最长最多的。其中有架构入门、分布式理论中各种非常有价值的经典论文,然后是一些分布式工程设计方面的文章,其中包括设计模式和工程应用,最后还有各大公司的架构供参考。微服务。有了分布式架构理论和工程的基础,接下来是对微服务的学习。在这部分内容中,我会罗列几个介绍微服务架构非常系统的文章,然后比较一下微服务和 SOA 的差别,最后则是一些工程实践和最佳实践。容器化和自动化运维。在容器化和自动化运维中,主要是学习 Docker 和 Kubernetes 这两个自动化运维的杀手型技术。而不是 Salt、Puppet、Chef 和 Ansible 这样比较传统的工具。原因很简单,因为自动化部署根本不够,还需要对环境和运行时的管理和运维才够,而只有 Docker 和 Kubernetes 才是未来。所以,这里重点让你学习这两个技术,其中有很多文章需要一些系统底层的知识。机器学习和人工智能。机器学习和人工智能,也不是我的长项,我也只是一个入门者。这里,我主要给了一些基础性的知识,其中包括基本原理、图书、课程、文章和相关的算法。你顺着我画的这路走,不能说能成为一个人工智能专家,但成为一个机器学习的高级工程师甚至准专家还是可能的。前端开发。这里的前端主要是 HTML 5 的前端了,这一节会带你学习一下前端开发所需要知道的基础知识,尤其是对前端开发语言 JavaScript 的学习,我花费了相当的篇幅列出了很多很经典的学习资料,必定会让你成为一个 JavaScript 高手。然后你还需要了解浏览器是怎样工作的,还有相关的网络协议和一些性能优化的技巧。最后则是 JavaScript 框架的学习,这里我只给了 React.js 和 Vue.js,并通过 React.js 带出来函数式编程的学习。我虽然不是一个前端程序员,但是,我相信我这个后端程序员给出来的这组前端开发的学习资料和路径会比前端程序员更靠谱一些。信息源。最后,则是一些信息源,其中包括各大公司的技术 Blog,还有相关的论文集散地。

另外,这里需要说明几点。

我假设你在前面已经打下了非常扎实的基础,但是要成为一个高手,基础知识只是一个地基,你还需要很多更为具体的技术。对我来说,就是看各种各样的文章、手册、论文、分享…… 其实,学习到一定程度,就是要从书本中走出去,到社区里和大家一起学习,而且还需要自己找食吃了。所以,对于这里面的文章,有很多都是在罗列各种文章和资源,只是为你梳理信息源,而不是喂你吃饭。老实说,我已经为你梳理并过滤掉了很多的信息,这里只留下了 30% 我觉得最经济也最有价值的信息。虽然对于不同定位和不同需求的人还可以再对这些信息进行删减,但是觉得我这么一做就会对其它人不公平了。所以,这也是我觉得最小数量集的信息和资源吧。你也可以把我这里的东西当成一个索引来对待。这些内容,不能说是隔离开来的,应该说是相辅相成的。也没什么顺序,可以各取所需。虽然看上去内容很多,但你也别害怕,真的不用害怕,你会越学越快,越实践越有感觉,也越有效率。在一开始可能会很慢,但是坚持住,积累一段时间后就会越来越快的。 而且,我要告诉你,绝大多数人是坚持不下来的。只要你能坚持下来,我保证,你一定会成为各个大公司的抢手货,这点你一定要相信我。你不需要特别努力,只需要日进一步,3-5 年后,你就会发现,绝大多数人都在你身后很远的地方了。

今天分享的内容为系统底层知识中的 Linux 系统、内存和网络等方面的相关知识及推荐的学习资料。

Linux 系统相关

学习 Linux 操作系统的原理是通向系统工程师的必经之路。我觉得,Unix/Linux 操作系统里的东西并不难学。你千万不要一下子扎到源代码里去,那样没用——你还是要在上层先通过读一些不错的文档来学习。下面我罗列了一些很不错的站点,其中有很多内容供你去钻研和探索。

我在这里默认你前面已经读过并读懂了我推荐的那些和 Unix/Linux 相关的图书了。所以,我相信你对 Unix/Linux 下的编程已经是有一些基础了,因此,你继续深挖 Linux 下的这些知识应该也不是很难的事了。

Red Hat Enterprise Linux 文档 。Red Hat Enterprise Linux(RHEL)是老牌 Linux 厂商 Red Hat 出品的面向商业的 Linux 发行版。Red Hat 网站上的这个文档中有很多很有价值的内容,值得一看。Linux Insides ,GitHub 上的一个开源电子书,其中讲述了 Linux 内核是怎样启动、初始化以及进行管理的。LWN’s kernel page ,上面有很多非常不错的文章来解释 Linux 内核的一些东西。Learn Linux Kernel from Android Perspective ,从 Android 的角度来学习 Linux 内核,这个站点上的 Blog 相对于前面的比较简单易读一些。Linux Kernel Doc, Linux 的内核文档也可以浏览一下。Kernel Planet ,Linux 内核开发者的 Blog,有很多很不错的文章和想法。Linux Performance and Tuning Guidelines ,这是 IBM 出的红皮书,虽然有点老了,但还是非常值得一读的。TLK: The Linux Kernel ,这是一本相对比较老的书了,Linux 内核版本为 2.0.33,但了解一下前人的思路,也是很有帮助的。Linux Performance ,这个网站上提供了和 Linux 系统性能相关的各种工具和文章收集,非常不错。Optimizing web servers for high throughput and low latency ,这是一篇非常底层的系统调优的文章,来自 DropBox,从中你可以学到很多底层的性能调优的经验和知识。

内存相关

计算机内存管理是每一个底层程序员需要了解的非常重要的事儿。当然,这里我们重点还是 Linux 操作系统相关的内存管理上的知识。

首先,LWN.net 上有一系列的 “What every programmer should know about memory” 文章你需要读一下。当然,你可以直接访问一个完整的 PDF 文档。下面是这个系列文章的网页版列表。读完这个列表的内容,你基本上就对内存有了一个比较好的知识体系了。

Part 1: Introduction ,中译版为 “每个程序员都应该了解的内存知识【第一部分】”Part 2: CPU cachesPart 3 (Virtual memory)Part 4 (NUMA systems)Part 5 (What programmers can do - cache optimization)Part 6 (What programmers can do - multi-threaded optimizations)Part 7 (Memory performance tools)Part 8 (Future technologies)Part 9 (Appendices and bibliography)

然后是几篇和内存相关的论文。下面这三篇论文是我个人觉得能对你非常有帮助的文章,尤其是你要做一些程序的性能优化方面。

Memory Barriers: a Hardware View for Software Hackers。内存的读写屏障是线程并发访问共享的内存数据时,从程序本身、编译器到 CPU 都必须遵循的一个规范。有了这个规范,才能保证访问共享的内存数据时,一个线程对该数据的更新能被另一个线程以正确的顺序感知到。在 SMP(对称多处理)这种类型的多处理器系统(包括多核系统)上,这种读写屏障还包含了复杂的缓存一致性策略。这篇文章做了详细解释。A Tutorial Introduction to the ARM and POWER Relaxed Memory Models,对 ARM 和 POWER 的宽松内存模型的一个教程式的简介。本篇文章的焦点是 ARM 和 POWER 体系结构下多处理器系统内存并发访问一致性的设计思路和使用方法。与支持较强的 TSO 模型的 x86 体系结构不同,ARM 和 POWER 这两种体系结构出于对功耗和性能的考虑,使用了一种更为宽松的内存模型。本文详细讨论了 ARM 和 POWER 的模型。x86-TSO: A Rigorous and Usable Programmer’s Model for x86 Multiprocessors,介绍 x86 的多处理器内存并发访问的一致性模型 TSO。

接下来是开发者最关心的内存管理方面的 lib 库。通常来说,我们有三种内存分配管理模块。就目前而言,BSD 的 jemalloc 有很大的影响力。后面我们可以看到不同公司的实践性文章。

ptmalloc 是 glibc 的内存分配管理。tcmalloc 是 Google 的内存分配管理模块,全称是 Thread-Caching malloc,基本上来说比 glibc 的 ptmalloc 快两倍以上。jemalloc 是 BSD 提供的内存分配管理。其论文为 A Scalable Concurrent malloc(3) Implementation for FreeBSD,这是一个可以并行处理的内存分配管理器。

关于 C 的这些内存分配器,你可以参看 Wikipedia 的 “C Dynamic Memory Allocation”这个词条。

下面是几篇不错的文章,让你感觉一下上面那三种内存分配器的一些比较和工程实践。

ptmalloc,tcmalloc 和 jemalloc 内存分配策略研究内存优化总结:ptmalloc、tcmalloc 和 jemallocScalable memory allocation using jemallocDecreasing RAM Usage by 40% Using jemalloc with Python & Celery

计算机网络

网络学习

首先,推荐一本书——《计算机网络(第五版)》,这本“计算机网络”和前面推荐的那本计算机网络不一样,前面那本偏扫盲,这本中有很多细节。这本书是国内外使用最广泛、最权威的计算机网络经典教材。全书按照网络协议模型自下而上(物理层、数据链路层、介质访问控制层、网络层、传输层和应用层)有系统地介绍了计算机网络的基本原理,并结合 Internet 给出了大量的协议实例。

这本书还与时俱进地引入了最新的网络技术,包括无线网络、3G 蜂窝网络、RFID 与传感器网络、内容分发与 P2P 网络、流媒体传输与 IP 语音,以及延迟容忍网络等。另外,本书针对当前网络应用中日益突出的安全问题,用了一整章的篇幅对计算机网络的安全性进行了深入讨论,而且把相关内容与最新网络技术结合起来阐述。这本书读起来并不枯燥,因为其中有很多小故事和小段子。

然后,有两个网上的教程和讲义也可以让人入门。

渥汰华大学的一个课程讲义你也可以一看 Computer Network Design 。GeeksforGeeks 上也有一个简单的 Computer Network Tutorials 。

网络调优

接下来,你可能需要一些非常实用的可以操作的技术,下面的几篇文章相信可以帮助到你。

《Linux 的高级路由和流量控制 HowTo》(Linux Advanced Routing & Traffic Control HOWTO ),这是一个非常容易上手的关于 iproute2、流量整形和一点 netfilter 的指南。关于网络调优,你可以看一下这个文档 Red Hat Enterprise Linux Network Performance Tuning Guide。还有一些网络工具能够帮上你的大忙,这里有一个网络工具的 Awesome 列表 Awesome Pcap Tools ,其中罗列了各种网络工具,能够让你更从容地调试网络相关的程序。Making Linux TCP Fast ,一篇非常不错的 TCP 调优的论文。下面是在 PackageCloud 上的两篇关于 Linux 网络栈相关的底层文章,非常值得一读。

Monitoring and Tuning the Linux Networking Stack: Receiving DataMonitoring and Tuning the Linux Networking Stack: Sending Data

网络协议

接下来,想要学习网络协议最好的方式就是学习通讯相关的 RFC。所以,在这里我会推荐一系列值得读的 RFC 给你。读 RFC 有几个好处,一方面可以学习技术,另一方面,你可以通过 RFC 学习到一个好的技术文档是怎么写的,还能看到各种解决问题的方案和思路。

对于第 2 层链路层,你可能需要了解一下 ARP:

RFC 826 - An Ethernet Address Resolution Protocol

以及 Tunnel 相关的协议:

RFC 1853 - IP in IP TunnelingRFC 2784 - Generic Routing Encapsulation (GRE)RFC 2661 - Layer Two Tunneling Protocol “L2TP”RFC 2637 - Point-to-Point Tunneling Protocol (PPTP)

对于第 4 层,你最需要了解的是 TCP/IP 了。和 TCP 相关的 RFC 相当多,这里给一系列经典的 RFC。这些 RFC 我都引用在了我在 CoolShell 上的《TCP 的那些事儿(上)》和《TCP 的那些事儿(下)》两篇文章中。如果你看不懂 RFC,你也可以去看我上述的文章。

RFC 793 - Transmission Control Protocol - 最初的 TCP 标准定义,但不包括 TCP 相关细节。RFC 813 - Window and Acknowledgement Strategy in TCP - TCP 窗口与确认策略,并讨论了在使用该机制时可能遇到的问题及解决方法。RFC 879 - The TCP Maximum Segment Size and Related Topics - 讨论 MSS 参数对控制 TCP 分组大小的重要性,以及该参数与 IP 分段大小的关系等。RFC 896 - Congestion Control in IP/TCP Internetworks - 讨论拥塞问题和 TCP 如何控制拥塞。RFC 2581 - TCP Congestion Control - 描述用于拥塞控制的四种机制:慢启动、拥塞防御、快重传和快恢复。后面这个 RFC 被 RFC 5681 所更新。还有 RFC 6582 - The NewReno Modification to TCP’s Fast Recovery Algorithm 中一个改进的快速恢复算法。RFC 2018 - TCP Selective Acknowledgment Options - TCP 的选择确认。RFC 2883 - An Extension to the Selective Acknowledgement (SACK) Option for TCP - 对于 RFC 2018 的改进。RFC 2988 - Computing TCP’s Retransmission Timer - 讨论与 TCP 重传计时器设置相关的话题,重传计时器控制报文在重传前应等待多长时间。也就是经典的 TCP Karn/Partridge 重传算法。RFC 6298 - Computing TCP’s Retransmission Timer - TCP Jacobson/Karels Algorithm 重传算法。

我个人觉得 TCP 最牛的不是不丢包,而是拥塞控制。对此,如果你感兴趣,可以读一下经典论文《Congestion Avoidance and Control》。

关于 Linux 下的 TCP 参数,你需要仔仔细细地读一下TCP 的 man page 。

对于第 7 层协议,HTTP 协议是重点要学习的。

首先推荐的是《HTTP 权威指南 》,这本书有点厚,可以当参考书来看。这本书中没有提到 HTTP/2 的事,但是可以让你了解到 HTTP 协议的绝大多数特性。

HTTP 1.1 的原始 RFC 是 1999 年 6 月的 RFC 2616,但其在 2014 后很快被下面这些 RFC 给取代了。

RFC 7230 - Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and RoutingRFC 7231 - Hypertext Transfer Protocol (HTTP/1.1): Semantics and ContentRFC 7232 - Hypertext Transfer Protocol (HTTP/1.1): Conditional RequestsRFC 7233 - Hypertext Transfer Protocol (HTTP/1.1): Range RequestsRFC 7234 - Hypertext Transfer Protocol (HTTP/1.1): CachingRFC 7235 - Hypertext Transfer Protocol (HTTP/1.1): Authentication

关于HTTP/2,这是 HTTP 的一个比较新的协议,它于 2015 年被批准通过,现在基本上所有的主流浏览器都默认启用这个协议。所以,你有必要学习一下这个协议。下面是相关的学习资源。

Gitbook - HTTP/2 详解http2 explained(中译版)HTTP/2 for a Faster WebNginx HTTP/2 白皮书HTTP/2 的两个 RFC:

RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2) ,HTTP/2 的协议本身RFC 7541 - HPACK: Header Compression for HTTP/2 ,HTTP/2 的压缩算法

最后,你可以上 Wikipedia 的 Internet Protocol Suite 上看看,这是一个很不错的网络协议的词条汇集地。顺着这些协议,你可以找到很多有用的东西。

异步 I/O 模型和 Lock-Free 编程(系统底层知识)

异步 I/O 模型

异步 I/O 模型是我个人觉得所有程序员都必需要学习的一门技术或是编程方法,这其中的设计模式或是解决方法可以借鉴到分布式架构上来。再说一遍,学习这些模型,是非常非常重要的,你千万要认真学习。

史蒂文斯(Stevens)在《UNIX 网络编程》一书 6.2 I/O Models 中介绍了五种 I/O 模型。

阻塞 I/O非阻塞 I/OI/O 的多路复用(select 和 poll)信号驱动的 I/O(SIGIO)异步 I/O(POSIX 的 aio_functions)

然后,在前面我们也阅读过了 - C10K Problem 。相信你对 I/O 模型也有了一定的了解。 这里,我们需要更为深入地学习 I/O 模型,尤其是其中的异步 I/O 模型。

首先,我们看一篇和 Java 相关的 I/O 模型的文章来复习一下之前的内容。Thousands of Threads and Blocking I/O: The Old Way to Write Java Servers Is New Again (and Way Better) ,这个 PPT 中不仅回顾和比较了各种 I/O 模型,而且还有各种比较细节的方案和说明,是一篇非常不错的文章。

然后,你可以看一篇 Java 相关的 PPT - 道格·莱亚(Doug Lea)的 Scalable IO in Java,这样你会对一些概念有个了解。

接下来,我们需要了解一下各种异步 I/O 的实现和设计方式。

IBM - Boost application performance using asynchronous I/O ,这是一篇关于 AIO 的文章。Lazy Asynchronous I/O For Event-Driven Servers ,这篇文章也很不错。另外,异步 I/O 模型中的 Windows I/O Completion Ports , 你也需要了解一下。如果 MSDN 上的这个手册不容易读,你可以看看这篇文章 Inside I/O Completion Ports。另外,关于 Windows,Windows Internals 这本书你可以仔细读一下,非常不错的。其中有一节 I/O Processing 也是很不错的,这里我给一个网上免费的链接I/O Processing 你可以看看 Windows 是怎么玩的。接下来是 Libevent。你可以看一下其主要维护人员尼克·马修森(Nick Mathewson)写的 Libevent 2.0 book。还有一本国人写的电子书 《Libevent 深入浅出》。再接下来是 Libuv。你可以看一下其官网的 Libuv Design Overview 了解一下。

我简单总结一下,基本上来说,异步 I/O 模型的发展技术是: select -> poll -> epoll -> aio -> libevent -> libuv。Unix/Linux 用了好几十年走过这些技术的变迁,然而,都不如 Windows I/O Completion Port 设计得好(免责声明:这个观点纯属个人观点。相信你仔细研究这些 I/O 模型后,你会有自己的判断)。

看过这些各种异步 I/O 模式的实现以后,相信你会看到一个编程模式——Reactor 模式。下面是这个模式的相关文章(读这三篇就够了)。

Understanding Reactor Pattern: Thread-Based and Event-DrivenReactor PatternThe reactor pattern and non-blocking IO

然后是几篇有意思的延伸阅读文章。

The Secret To 10 Million Concurrent Connections -The Kernel Is The Problem, Not The Solution - C10M 问题来了……还有几篇可能有争议的文章,让你从不同的角度思考。

Select is fundamentally brokenEpoll is fundamentally broken 1/2Epoll is fundamentally broken 2/2

Lock-Free 编程相关

Lock-Free - 无锁技术越来越被开发人员重视,因为锁对于性能的影响实在是太大了,所以如果想开发出一个高性能的程序,你就非常有必要学习 Lock-Free 的编程方式。

关于无锁的数据结构,有几篇教程你可以看一下。

Dr.Dobb’s: Lock-Free Data StructuresAndrei Alexandrescu: Lock-Free Data Structures

然后强烈推荐一本免费的电子书:Is Parallel Programming Hard, And, If So, What Can You Do About It? ,这是大牛 保罗·麦肯尼(Paul E. McKenney) 写的书。这本书堪称并行编程的经典书,必看。

此时,Wikipedia 上有三个词条你要看一下,以此了解并发编程中的一些概念:Non-blocking algorithm 、Read-copy-update 和 Seqlock。

接下来,读一下以下两篇论文 。

Implementing Lock-Free Queues, 这也是一篇很不错的论文,我把它介绍在了我的网站上 ,文章为“无锁队列的实现”。Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms ,这篇论文给出了一个无阻塞和阻塞的并发队列算法。

最后,有几个博客你要订阅一下。

1024cores - 德米特里·伐由科夫(Dmitry Vyukov)的和 lock-free 编程相关的网站。Paul E. McKenney - 保罗(Paul)的个人网站。Concurrency Freaks - 关于并发算法和相关模式的网站。Preshing on Programming - 加拿大程序员杰夫·普莱辛(Jeff Preshing)的技术博客,主要关注 C++ 和 Python 两门编程语言。他用 C++11 实现了类的反射机制,用 C++ 编写了 3D 小游戏 Hop Out,还为该游戏编写了一个游戏引擎。他还讨论了很多 C++ 的用法,比如 C++14 推荐的代码写法、新增的某些语言构造等,和 Python 很相似。阅读这个技术博客上的内容能够深深感受到博主对编程世界的崇敬和痴迷。Sutter’s Mill - 赫布·萨特(Herb Sutter)是一位杰出的 C++ 专家,曾担任 ISO C++ 标准委员会秘书和召集人超过 10 年。他的博客有关于 C++ 语言标准最新进展的信息,其中也有他的演讲视频。博客中还讨论了其他技术和 C++ 的差异,如 C# 和 JavaScript,它们的性能特点、怎样避免引入性能方面的缺陷等。Mechanical Sympathy - 博主是马丁·汤普森(Martin Thompson),他是一名英国的技术极客,探索现代硬件的功能,并提供开发、培训、性能调优和咨询服务。他的博客主题是 Hardware and software working together in harmony,里面探讨了如何设计和编写软件使得它在硬件上能高性能地运行。非常值得一看。

接下来,是一些编程相关的一些 C/C++ 的类库,这样你就不用从头再造轮子了(对于 Java 的,请参看 JDK 里的 Concurrent 开头的一系列的类)。

Boost.Lockfree - Boost 库中的无锁数据结构。ConcurrencyKit - 并发性编程的原语。Folly - Facebook 的开源库(它对 MPMC 队列做了一个很好的实现)。Junction - C++ 中的并发数据结构。MPMCQueue - 一个用 C++11 编写的有边界的“多生产者 - 多消费者”无锁队列。SPSCQueue - 一个有边界的“单生产者 - 单消费者”的无等待、无锁的队列。Seqlock - 用 C++ 实现的 Seqlock。Userspace RCU - liburcu 是一个用户空间的 RCU(Read-copy-update,读 - 拷贝 - 更新)库。libcds - 一个并发数据结构的 C++ 库。liblfds - 一个用 C 语言编写的可移植、无许可证、无锁的数据结构库。

其它

关于 64 位系统编程,只要去一个地方就行了: All about 64-bit programming in one place,这是一个关于 64 位编程相关的收集页面,其中包括相关的文章、28 节课程,还有知识库和相关的 blog。 What Scalable Programs Need from Transactional Memory ,事务性内存(TM)一直是许多研究的重点,它在诸如 IBM Blue Gene/Q 和 Intel Haswell 等处理器中得到了支持。许多研究都使用 STAMP 基准测试套件来评估其设计。然而,我们所知的所有 TM 系统上的 STAMP 基准测试所获得的加速比较有限。 例如,在 IBM Blue Gene/Q 上有 64 个线程,我们观察到使用 Blue Gene/Q 硬件事务内存(HTM)的中值加速比为 1.4 倍,使用软件事务内存(STM)的中值加速比为 4.1 倍。什么限制了这些 TM 基准的性能?在本论文中,作者认为问题在于用于编写它们的编程模型和数据结构上,只要使用合适的模型和数据结构,程序的性能可以有 10 多倍的提升。 Improving OpenSSL Performance ,这篇文章除了教你如何提高 OpenSSL 的执行性能,还讲了一些底层的性能调优知识。 关于压缩的内容。为了避免枯燥,主要推荐下面这两篇实践性很强的文章。

How eBay’s Shopping Cart used compression techniques to solve network I/O bottlenecks ,这是一篇很好的文章,讲述了 eBay 是如何通过压缩数据来提高整体服务性能的,其中有几个比较好的压缩算法。除了可以让你学到相关的技术知识,还可以让你看到一种比较严谨的工程师文化。Linkedin: Boosting Site Speed Using Brotli Compression ,LinkedIn 在 2017 年早些时候开始使用 Brotli 来替换 gzip,以此带来更快的访问,这篇文章讲述了什么是 Brotli 以及与其它压缩程序的比较和所带来的性能提升。 这里有两篇关于 SSD 硬盘性能测试的文章。Performance Testing with SSDs, Part 1 和 Performance Testing with SSDs Part 2 ,这两篇文章介绍了测试 SSD 硬盘性能以及相关的操作系统调优方法。 Secure Programming HOWTO - Creating Secure Software ,这是一本电子书,其中有繁体中文的翻译,这本电子书讲了 Linux/Unix 下的一些安全编程方面的知识。

相关论文

Hints for Computer System Design ,计算机设计的忠告,这是 ACM 图灵奖得主 Butler Lampson 在 Xerox PARC 工作时的一篇论文。这篇论文简明扼要地总结了他在做系统设计时的一些想法,非常值得一读。(用他的话来说,“Studying the design and implementation of a number of computer has led to some general hints for system design. They are described here and illustrated by many examples, ranging from hardware such as the Alto and the Dorado to application programs such as Bravo and Star“。) The 5 minute rule for trading memory for disc accesses and the 5 byte rule for trading memory for CPU time ,根据文章名称也可以看出,5 分钟法则是用来衡量内存与磁盘的,而 5 字节法则则是在内存和 CPU 之间的权衡。这两个法则是 Jim Gray 和 Franco Putzolu 在 1986 年的文章。 在该论文发表 10 年后的 1997 年,Jim Gray 和 Goetz Graefe 又在 The Five-Minute Rule Ten Years Later and Other Computer Storage Rules of Thumb 中对该法则进行了重新审视。2007 年,也就是该论文发表 20 年后,这年的 1 月 28 日,Jim Gray 驾驶一艘 40 英尺长的船从旧金山港出海,目的是航行到附近的费拉隆岛,在那里撒下母亲的骨灰。出海之后,他就同朋友和亲属失去了联系。为了纪念和向大师致敬,时隔 10 多年后的 2009 年 Goetz Graefe 又发表了 The Five-Minute Rule 20 Years Later (and How Falsh Memory Changes the Rules)。 注明一下,Jim Gray 是关系型数据库领域的大师。因在数据库和事务处理研究和实现方面的开创性贡献而获得 1998 年图灵奖。美国科学院、工程院两院院士,ACM 和 IEEE 两会会士。他 25 岁成为加州大学伯克利分校计算机科学学院第一位博士。在 IBM 工作期间参与和主持了 IMS、System R、SQL/DS、DB2 等项目的开发。后任职于微软研究院,主要关注应用数据库技术来处理各学科的海量信息。

小结

好了,总结一下今天的内容。异步 I/O 模型是我个人觉得所有程序员都必需要学习的一门技术或是编程方法,这其中的设计模式或是解决方法可以借鉴到分布式架构上来。而且我认为,学习这些模型非常重要,你千万要认真学习。

接下来是 Lock-Free 方面的内容,由于锁对于性能的影响实在是太大了,所以它越来越被开发人员所重视。如果想开发出一个高性能的程序,你非常有必要学习 Lock-Free 的编程方式。随后,我给出系统底层方面的其它一些重要知识,如 64 位编程、提高 OpenSSL 的执行性能、压缩、SSD 硬盘性能测试等。最后介绍了几篇我认为对学习和巩固这些知识非常有帮助的论文,都很经典,推荐你务必看看。

Java 底层知识

前两篇文章分享的是系统底层方面的内容,今天我们进入高手成长篇的第二部分——Java 底层知识。

Java 字节码相关

首先,Java 最黑科技的玩法就是字节码编程,也就是动态修改或是动态生成 Java 字节码。Java 的字节码相当于汇编,其中的一些细节你可以从下面的这几个教程中学习。

Java Zone: Introduction to Java Bytecode ,这篇文章图文并茂地向你讲述了 Java 字节码的一些细节,是一篇很不错的入门文章。IBM DeveloperWorks: Java bytecode ,虽然这篇文章很老了,但是这篇文章是一篇非常好的讲 Java 字节码的文章。Java Bytecode and JVMTI Examples,这是一些使用 JVM Tool Interface 操作字节码的比较实用的例子。包括方法调用统计、静态字节码修改、Heap Taggin 和 Heap Walking。

当然,一般来说,我们不使用 JVMTI 操作字节码,而是用一些更好用的库。这里有三个库可以帮你比较容易地做这个事。

asmtools - 用于生产环境的 Java .class 文件开发工具。Byte Buddy - 代码生成库:运行时创建 Class 文件而不需要编译器帮助。Jitescript - 和 BiteScript 类似的字节码生成库。

就我而言,我更喜欢 Byte Buddy,它在 2015 年还获了 Oracle 的 “Duke’s Choice”大奖,其中说 Byte Buddy 极大地发展了 Java 的技术。

使用字节码编程可以玩出很多高级玩法,最高级的还是在 Java 程序运行时进行字节码修改和代码注入。听起来是不是一些很黑客,也很黑科技的事?是的,这个方式使用 Java 这门静态语言在运行时可以进行各种动态的代码修改,而且可以进行无侵入的编程。

比如, 我们不需要在代码中埋点做统计或监控,可以使用这种技术把我们的监控代码直接以字节码的方式注入到别人的代码中,从而实现对实际程序运行情况进行统计和监控。如果你看过我的《编程范式游记》,你就知道这种技术的威力了,其可以很魔法地把业务逻辑和代码控制分离开来。

要做到这个事,你还需要学习一个叫 Java Agent 的技术。Java Agent 使用的是 “Java Instrumentation API”,其主要方法是实现一个叫 premain() 的方法(嗯,一个比 main() 函数还要超前执行的 main 函数),然后把你的代码编译成一个 jar 文件。

在 JVM 启动时,使用这样的命令行来引入你的 jar 文件:java -javaagent:yourAwesomeAgent.jar -jar App.jar。更为详细的文章你可以参看:“Java Code Geeks: Java Agents”,你还可以看一下这个示例项目:jvm-monitoring-agent 或是 EntryPointKR/Agent.java。如果想用 ByteBuddy 来玩,你可以看看这篇文章 “通过使用 Byte Buddy,便捷地创建 Java Agent”。如果你想学习如何用 Java Agent 做监控,你可以看一下这个项目 Stage Monitor。

JVM 相关

接下来讲讲 Java 底层知识中另一个非常重要的内容——JVM。

说起 JVM,你有必要读一下 JVM 的规格说明书,我在这里放一个 Java 8 的, The Java Virtual Machine Specification Java SE 8 Edition 。对于规格说明书的阅读,我认为是系统了解 JVM 规范的最佳文档,这个文档可以让你对于搞不清楚或是诡异的问题恍然大悟。关于中文翻译,有人在 GitHub 上开了个 Repo - “java-virtual-machine-specification”。

另外,也推荐一下 JVM Anatomy Park JVM 解剖公园,这是一个系列的文章,每篇文章都不长,但是都很精彩,带你一点一点地把 JVM 中的一些技术解开。

学习 Java 底层原理还有 Java 的内存模型,官方文章是 JSR 133。还有马里兰大学的威廉·皮尤(William Pugh)教授收集的和 Java 内存模型相关的文献 - The Java Memory Model ,你可以前往浏览。

对于内存方面,道格·利(Doug Lea)有两篇文章也是很有价值的。

The JSR-133 Cookbook for Compiler Writers,解释了怎样实现 Java 内存模型,特别是在考虑到多处理器(或多核)系统的情况下,多线程和读写屏障的实现。Using JDK 9 Memory Order Modes,讲了怎样通过 VarHandle 来使用 plain、opaque、release/acquire 和 volatile 四种共享内存的访问模式,并剖析了底层的原理。

垃圾回收机制也是需要好好学习的,在这里推荐一本书 《The Garbage Collection Handbook》,在豆瓣上的得分居然是 9.9(当然,评价人数不多)。这本书非常全面地介绍了垃圾收集的原理、设计和算法。但是这本书也是相当难啃的。中文翻译《垃圾回收算法手册》翻译得很一般,有人说翻译得很烂。所以,如果可能,还是读英文版的。如果你对从事垃圾回收相关的工作有兴趣,那么你需要好好看一下这本书。

当然,更多的人可能只需要知道怎么调优垃圾回收, 那么推荐读读 Garbage Collection Tuning Guide ,它是 Hotspot Java 虚拟机的垃圾回收调优指南,对你很有帮助。

Quick Tips for Fast Code on the JVM 也是一篇很不错的文章,里面有写出更快的 Java 代码的几个小提示,值得一读。

小结

好了,总结一下今天学到的内容。Java 最黑科技的玩法就是字节码编程,也就是动态修改或是动态生成 Java 字节码。Java 的字节码相当于汇编,学习其中的细节很有意思,为此我精心挑选了 3 篇文章,供你学习。我们一般不使用 JVMTI 操作字节码,而是用一些更好用的库,如 asmtools、Byte Buddy 和 BiteScript 等。使用字节码编程可以玩出很多高级玩法,其中最高级的玩法是在 Java 程序运行时进行字节码修改和代码注入。同时,我介绍了 Java Agent 技术,帮助你更好地实现这种高级玩法。

JVM 也是学习 Java 过程中非常重要的一部分内容。我推荐阅读一下 JVM 的规格说明书,我认为,它是系统了解 JVM 规范的最佳文档,可以让你对于搞不清楚或是诡异的问题恍然大悟。同时推荐了 JVM Anatomy Park 系列文章,也非常值得一读。

随后介绍的是 Java 的内存模型和垃圾回收机制,尤其给出了如何调优垃圾回收方面的资料。这些内容都很底层,但也都很重要。对于想成为高手的你来说,还是有必要花时间来啃一啃的。

数据库

对于数据库方向,重点就是两种数据库,一种是以 SQL 为代表的关系型数据库,另一种是以非 SQL 为代表的 NoSQL 数据库。关系型数据库主要有三个:Oracle、MySQL 和 Postgres。

在这里,我们只讨论越来越主流的 MySQL 数据库。首先,我们要了解数据库的一些实现原理和内存的一些细节,然后我们要知道数据的高可用和数据复制这些比较重要的话题,了解一下关系型数据库的一些实践和难点。然后,我们会进入到 NoSQL 数据库的学习。

NoSQL 数据库千奇百怪,其主要是解决了关系型数据库中的各种问题。第一个大问题就是数据的 Schema 非常多,用关系型数据库来表示不同的 Data Schema 是非常笨拙的,所以要有不同的数据库(如时序型、键值对型、搜索型、文档型、图结构型等)。另一个大问题是,关系型数据库的 ACID 是一件很讨厌的事,这极大地影响了数据库的性能和扩展性,所以 NoSQL 在这上面做了相应的妥协以解决大规模伸缩的问题。

对于一个程序员,你可能觉得数据库的事都是 DBA 的事,然而我想告诉你你错了,这些事才真正是程序员的事。因为程序是需要和数据打交道的,所以程序员或架构师不仅需要设计数据模型,还要保证整体系统的稳定性和可用性,数据是整个系统中关键中的关键。所以,作为一个架构师或程序员,你必须了解最重要的数据存储——数据库。

关系型数据库

今天,关系型数据库最主要的两个代表是闭源的 Oracle 和开源的 MySQL。当然,还有很多了,比如微软的 SQL Server,IBM 的 DB2 等,还有开源的 PostgreSQL。关系型数据库的世界中有好多好多产品。当然,还是 Oracle 和 MySQL 是比较主流的。所以,这里主要介绍更为开放和主流的 MySQL。

如果你要玩 Oracle,我这里只推荐一本书《Oracle Database 9i/10g/11g 编程艺术》,无论是开发人员还是 DBA,它都是必读的书。这本书的作者是 Oracle 公司的技术副总裁托马斯·凯特(Thomas Kyte),他也是世界顶级的 Oracle 专家。

这本书中深入分析了 Oracle 数据库体系结构,包括文件、内存结构以及构成 Oracle 数据库和实例的底层进程,利用具体示例讨论了一些重要的数据库主题,如锁定、并发控制、事务等。同时分析了数据库中的物理结构,如表、索引和数据类型,并介绍采用哪些技术能最优地使用这些物理结构。

学习 MySQL,首先一定是要看MySQL 官方手册。 然后,官方还有几个 PPT 也要学习一下。

How to Analyze and Tune MySQL Queries for Better PerformanceMySQL Performance Tuning 101MySQL Performance Schema & Sys SchemaMySQL Performance: Demystified Tuning & Best PracticesMySQL Security Best PracticesMySQL Cluster Deployment Best PracticesMySQL High Availability with InnoDB Cluster 然后推荐《高性能 MySQL》,这本书是 MySQL 领域的经典之作,拥有广泛的影响力。不但适合数据库管理员(DBA)阅读,也适合开发人员参考学习。不管是数据库新手还是专家,都能从本书中有所收获。 如果你对 MySQL 的内部原理有兴趣的话,可以看一下这本书《MySQL 技术内幕:InnoDB 存储引擎》。当然,还有官网的MySQL Internals Manual 。 数据库的索引设计和优化也是非常关键的,这里还有一本书《数据库的索引设计与优化》也是很不错的。虽然不是讲 MySQL 的,但是原理都是相通的。这也是上面推荐过的《高性能 MySQL》在其索引部分推荐的一本好书。 你千万不要觉得只有做数据库你才需要学习这种索引技术。不是的!在系统架构上,在分布式架构中,索引技术也是非常重要的。这本书对于索引性能进行了非常清楚的估算,不像其它书中只是模糊的描述,你一定会收获很多。

下面还有一些不错的和 MySQL 相关的文章。

MySQL 索引背后的数据结构及算法原理Some study on database storage internalsSharding Pinterest: How we scaled our MySQL fleetGuide to MySQL High AvailabilityChoosing MySQL High Availability SolutionsHigh availability with MariaDB TX: The definitive guide

最后,还有一个 MySQL 的资源列表 Awesome MySQL,这个列表中有很多的工具和开发资源,可以帮助你做很多事。

MySQL 有两个比较有名的分支,一个是 Percona,另一个是 MariaDB,其官网上的 Resources 页面中有很多不错的资源和文档,可以经常看看。 Percona Resources、MariaDB Resources ,以及它们的开发博客中也有很多不错的文章,分别为 Percona Blog 和 MariaDB Blog。

然后是关于 MySQL 的一些相关经验型的文章。

Booking.com: Evolution of MySQL System Design ,Booking.com 的 MySQL 数据库使用的演化,其中有很多不错的经验分享,我相信也是很多公司会遇到的的问题。Tracking the Money?-?Scaling Financial Reporting at Airbnb ,Airbnb 的数据库扩展的经验分享。Why Uber Engineering Switched from Postgres to MySQL ,无意比较两个数据库谁好谁不好,推荐这篇 Uber 的长文,主要是想让你从中学习到一些经验和技术细节,这是一篇很不错的文章。

关于 MySQL 的集群复制,下面有这些文章供你学习一下,都是很不错的实践性比较强的文章。

Monitoring Delayed Replication, With A Focus On MySQLMitigating replication lag and reducing read load with freno另外,Booking.com 给了一系列的文章,你可以看看:

Better Parallel Replication for MySQL Evaluating MySQL Parallel Replication Part 2: Slave Group Commit Evaluating MySQL Parallel Replication Part 3: Benchmarks in Production [Evaluating MySQL Parallel Replication Part 4: More Benchmarks in Production ](https://medium.com/booking-com-infrastructure/evaluating-mysql-parallel-replication-part-4-more-benchmarks-in-production-49ee255043ab) Evaluating MySQL Parallel Replication Part 4, Annex: Under the Hood

对于 MySQL 的数据分区来说,还有下面几篇文章你可以看看。

StackOverflow: MySQL sharding approaches?Why you don’t want to shard[How to Scale Big Data Applications](https://www.percona.com/sites/default/files/presentations/How to Scale Big Data Applications.pdf)MySQL Sharding with ProxySQL

然后,再看看各个公司做 MySQL Sharding 的一些经验分享。

[MailChimp: Using Shards to Accommodate Millions of Users ](https://devs.mailchimp.com/blog/using-shards-to-accommodate-millions-of-users/) Uber: Code Migration in Production: Rewriting the Sharding Layer of Uber’s Schemaless Datastore Sharding & IDs at Instagram Airbnb: How We Partitioned Airbnb’s Main Database in Two Weeks

NoSQL 数据库

关于 NoSQL 数据库,其最初目的就是解决大数据的问题。然而,也有人把其直接用来替换掉关系型数据库。所以在学习这个技术之前,我们需要对这个技术的一些概念和初衷有一定的了解。下面是一些推荐资料。

Martin Fowler 在 YouTube 上分享的 NoSQL 介绍 Introduction To NoSQL, 以及他参与编写的 NoSQL Distilled - NoSQL 精粹,这本书才 100 多页,是本难得的关于 NoSQL 的书,很不错,非常易读。NoSQL Databases: a Survey and Decision Guidance,这篇文章可以带你自上而下地从 CAP 原理到开始了解 NoSQL 的种种技术,是一篇非常不错的文章。Distribution, Data, Deployment: Software Architecture Convergence in Big Data Systems,这是卡内基·梅隆大学的一篇讲分布式大数据系统的论文。其中主要讨论了在大数据时代下的软件工程中的一些关键点,也说到了 NoSQL 数据库。No Relation: The Mixed Blessings of Non-Relational Databases,这篇论文虽然有点年代久远。但这篇论文是 HBase 的基础,你花上一点时间来读读,就可以了解到,对各种非关系型数据存储优缺点的一个很好的比较。NoSQL Data Modeling Techniques ,NoSQL 建模技术。这篇文章我曾经翻译在了 CoolShell 上,标题为 NoSQL 数据建模技术,供你参考。

MongoDB - Data Modeling Introduction ,虽然这是 MongoDB 的数据建模介绍,但是其很多观点可以用于其它的 NoSQL 数据库。Firebase - Structure Your Database ,Google 的 Firebase 数据库使用 JSON 建模的一些最佳实践。 因为 CAP 原理,所以当你需要选择一个 NoSQL 数据库的时候,你应该看看这篇文档 Visual Guide to NoSQL Systems。

选 SQL 还是 NoSQL,这里有两篇文章,值得你看看。

SQL vs. NoSQL Databases: What’s the Difference?Salesforce: SQL or NoSQL

各种 NoSQL 数据库

学习使用 NoSQL 数据库其实并不是一件很难的事,只要你把官方的文档仔细地读一下,是很容易上手的,而且大多数 NoSQL 数据库都是开源的,所以,也可以通过代码自己解决问题。下面我主要给出一些典型的 NoSQL 数据库的一些经验型的文章,供你参考。

列数据库 Column Database

Cassandra 相关

沃尔玛实验室有两篇文章值得一读。

Avoid Pitfalls in Scaling Cassandra Cluster at WalmartStoring Images in Cassandra at Walmart Yelp: How We Scaled Our Ad Analytics with Apache Cassandra ,Yelp 的这篇博客也有一些相关的经验和教训。Discord: How Discord Stores Billions of Messages ,Discord 公司分享的一个如何存储十亿级消息的技术文章。Cassandra at Instagram ,Instagram 的一个 PPT,其中介绍了 Instagram 中是怎么使用 Cassandra 的。Netflix: Benchmarking Cassandra Scalability on AWS?-?Over a million writes per second ,Netflix 公司在 AWS 上给 Cassandra 做的一个 Benchmark。 HBase 相关

Imgur Notification: From MySQL to HBASEPinterest: Improving HBase Backup EfficiencyIBM : Tuning HBase performanceHBase File Locality in HDFSApache Hadoop Goes Realtime at FacebookStorage Infrastructure Behind Facebook Messages: Using HBase at ScaleGitHub: Awesome HBase

针对于 HBase 有两本书你可以考虑一下。

首先,先推荐两本书,一本是偏实践的《HBase 实战》,另一本是偏大而全的手册型的《HBase 权威指南》。当然,你也可以看看官方的 The Apache HBase? Reference Guide另外两个列数据库:

ClickHouse - Open Source Distributed Column Database at YandexScaling Redshift without Scaling Costs at GIPHY

文档数据库 Document Database - MongoDB, SimpleDB, CouchDB

Data Points - What the Heck Are Document Databases?eBay: Building Mission-Critical Multi-Data Center Applications with MongoDBThe AWS and MongoDB Infrastructure of Parse: Lessons LearnedMigrating Mountains of Mongo DataCouchbase Ecosystem at LinkedInSimpleDB at ZendeskGithub: Awesome MongoDB

数据结构数据库 Data structure Database - Redis

Learn Redis the hard way (in production) at TrivagoTwitter: How Twitter Uses Redis To Scale - 105TB RAM, 39MM QPS, 10,000+ InstancesSlack: Scaling Slack’s Job Queue - Robustly Handling Billions of Tasks in Milliseconds Using Kafka and RedisGitHub: Moving persistent data out of Redis at GitHubInstagram: Storing Hundreds of Millions of Simple Key-Value Pairs in RedisRedis in Chat Architecture of Twitch (from 27:22)Deliveroo: Optimizing Session Key Storage in RedisDeliveroo: Optimizing Redis StorageGitHub: Awesome Redis

时序数据库 Time-Series Database

What is Time-Series Data & Why We Need a Time-Series DatabaseTime Series Data: Why and How to Use a Relational Database instead of NoSQLBeringei: High-performance Time Series Storage Engine @FacebookIntroducing Atlas: Netflix’s Primary Telemetry Platform @NetflixBuilding a Scalable Time Series Database on PostgreSQLScaling Time Series Data Storage - Part I @NetflixDesign of a Cost Efficient Time Series Store for Big DataGitHub: Awesome Time-Series Database

图数据库 - Graph Platform

首先是 IBM Devloperworks 上的两个简介性的 PPT。

Intro to graph databases, Part 1, Graph databases and the CRUD operationsIntro to graph databases, Part 2, Building a recommendation engine with a graph database 然后是一本免费的电子书《Graph Database》。接下来是一些图数据库的介绍文章。

Handling Billions of Edges in a Graph DatabaseNeo4j case studies with Walmart, eBay, AirBnB, NASA, etcFlockDB: Distributed Graph Database for Storing Adjacency Lists at TwitterJanusGraph: Scalable Graph Database backed by Google, IBM and HortonworksAmazon Neptune

搜索数据库 - ElasticSearch

Elasticsearch: The Definitive Guide 这是官网方的 ElasticSearch 的学习资料,基本上来说,看这个就够了。接下来是 4 篇和性能调优相关的工程实践。

Elasticsearch Performance Tuning Practice at eBayElasticsearch at Kickstarter9 tips on ElasticSearch configuration for high performanceElasticsearch In Production?-?Deployment Best Practices 最后是 GitHub 上的资源列表 GitHub: Awesome ElasticSearch 。

小结

好了,总结一下今天分享的内容。虽然有人会认为数据库与程序员无关,是 DBA 的事儿。但我坚信,数据库才真正是程序员的事儿。因为程序是需要和数据打交道的,所以程序员或架构师不仅需要设计数据模型,还要保证整体系统的稳定性和可用性,数据是整个系统中关键中的关键。

对于数据库方向,重点就是两种数据库,一种是以 SQL 为代表的关系型数据库,另一种是以非 SQL 为代表的 NoSQL 数据库。因而,在这篇文章中,我给出了 MySQL 和各种开源 NoSQL 的一些相关的有价值的文章和导读,主要是让你对这些数据库的内在有一定的了解,但又不会太深。同时给出了一些知名企业使用数据库的工程实践,这对于了解各种数据库的优劣非常有帮助,值得认真读读。

分布式架构入门(分布式架构)

学习分布式系统跟学习其它技术非常不一样,分布式系统涵盖的面非常广,具体来说涵盖如下几方面:

服务调度,涉及服务发现、配置管理、弹性伸缩、故障恢复等。资源调度,涉及对底层资源的调度使用,如计算资源、网络资源和存储资源等。流量调度,涉及路由、负载均衡、流控、熔断等。数据调度,涉及数据复本、数据一致性、分布式事务、分库、分表等。容错处理,涉及隔离、幂等、重试、业务补偿、异步、降级等。自动化运维,涉及持续集成、持续部署、全栈监控、调用链跟踪等。

所有这些形成了分布式架构的整体复杂度,也造就了分布式系统中的很多很多论文、图书以及很多很多的项目。要学好分布式系统及其架构,我们需要大量的时间和实践才能真正掌握这些技术。

这里有几点需要你注意一下。

分布式系统之所以复杂,就是因为它太容易出错了。这意味着,你要把处理错误的代码当成正常功能的代码来处理。开发一个健壮的分布式系统的成本是单体系统的几百倍甚至几万倍。这意味着,我们要自己开发一个,需要能力很强的开发人员。非常健壮的开源的分布式系统并不多,或者说基本没有。这意味着,如果你要用开源的,那么你需要 hold 得住其源码。管理或是协调多个服务或机器是非常难的。这意味着,我们要去读很多很多的分布式系统的论文。在分布式环境下,出了问题是很难 debug 的。这意味着,我们需要非常好的监控和跟踪系统,还需要经常做演练和测试。在分布式环境下,你需要更科学地分析和统计。这意味着,我们要用 P90 这样的统计指标,而不是平均值,我们还需要做容量计划和评估。在分布式环境下,需要应用服务化。这意味着,我们需要一个服务开发框架,比如 SOA 或微服务。在分布式环境下,故障不可怕,可怕的是影响面过大,时间过长。这意味着,我们需要花时间来开发我们的自动化运维平台。

总之,在分布式环境下,一切都变得非常复杂。要进入这个领域,你需要有足够多的耐性和足够强的心态来接受各式各样的失败。当拥有丰富的实践和经验后,你才会有所建树。这并不是一日之功,你可能要在这个领域花费数年甚至数十年的时间。

分布式架构入门

学习如何设计可扩展的架构将会有助于你成为一个更好的工程师。系统设计是一个很宽泛的话题。在互联网上,关于架构设计原则的资源也是多如牛毛。所以,你需要知道一些基本概念,对此,这里你先阅读下面两篇文章。

Scalable Web Architecture and Distributed Systems ,这篇文章会给你一个大概的分布式架构是怎么来解决系统扩展性问题的粗略方法。Scalability, Availability & Stability Patterns ,这个 PPT 能在扩展性、可用性、稳定性等方面给你一个非常大的架构设计视野和思想,可以让你感受一下大概的全景图。

然后,我更强烈推荐 GitHub 上的一篇文档 - System Design Primer ,这个仓库主要组织收集分布式系统的一些与扩展性相关的资源,它可以帮助你学习如何构建可扩展的架构。

目前这个仓库收集到了好些系统架构和设计的基本方法。其中包括:CAP 理论、一致性模型、可用性模式、DNS、CDN、负载均衡、反向代理、应用层的微服务和服务发现、关系型数据库和 NoSQL、缓存、异步通讯、安全等。

我认为,上面这几篇文章基本足够可以让你入门了,因为其中基本涵盖了所有与系统架构相关的技术。这些技术,足够这世上 90% 以上的公司用了,只有超级巨型的公司才有可能使用更高层次的技术。

分布式理论

下面,我们来学习一下分布式方面的理论知识。

首先,你需要看一下 An introduction to distributed systems。 这只是某个教学课程的提纲,我觉得还是很不错的,几乎涵盖了分布式系统方面的所有知识点,而且辅以简洁并切中要害的说明文字,非常适合初学者提纲挈领地了解知识全貌,快速与现有知识结合,形成知识体系。这也是一个分布式系统的知识图谱,可以让你看到分布式系统的整体全貌。你可以根据这个知识图 Google 下去,然后你会学会所有的东西。

然后,你需要了解一下拜占庭将军问题(Byzantine Generals Problem)。这个问题是莱斯利·兰波特(Leslie Lamport)于 1982 年提出用来解释一致性问题的一个虚构模型(论文地址)。拜占庭是古代东罗马帝国的首都,由于地域宽广,守卫边境的多个将军(系统中的多个节点)需要通过信使来传递消息,达成某些一致的决定。但由于将军中可能存在叛徒(系统中节点出错),这些叛徒将努力向不同的将军发送不同的消息,试图会干扰一致性的达成。拜占庭问题即为在此情况下,如何让忠诚的将军们能达成行动的一致。

对于拜占庭问题来说,假如节点总数为 N,叛变将军数为 F,则当 N >= 3F + 1 时,问题才有解,即拜占庭容错(Byzantine Fault Tolerant,BFT)算法。拜占庭容错算法解决的是,网络通信可靠但节点可能故障情况下一致性该如何达成的问题。

最早由卡斯特罗(Castro)和利斯科夫(Liskov)在 1999 年提出的实用拜占庭容错(Practical Byzantine Fault Tolerant,PBFT)算法,是第一个得到广泛应用的 BFT 算法。只要系统中有 2/3 的节点是正常工作的,则可以保证一致性。PBFT 算法包括三个阶段来达成共识:预准备(Pre-Prepare)、准备(Prepare)和提交(Commit)。

这里有几篇和这个问题相关的文章,推荐阅读。

Dr.Dobb’s - The Byzantine Generals ProblemThe Byzantine Generals ProblemPracticle Byzantine Fault Tolerance

拜占庭容错系统研究中有三个重要理论:CAP、FLP 和 DLS。

CAP 定理,CAP 理论相信你应该听说过不下 N 次了。CAP 定理是分布式系统设计中最基础也是最为关键的理论。CAP 定理指出,分布式数据存储不可能同时满足以下三个条件:一致性(Consistency)、可用性(Availability)和 分区容忍(Partition tolerance)。 “在网络发生阻断(partition)时,你只能选择数据的一致性(consistency)或可用性(availability),无法两者兼得”。 论点比较直观:如果网络因阻断而分隔为二,在其中一边我送出一笔交易:“将我的十元给 A”;在另一半我送出另一笔交易:“将我的十元给 B”。此时系统要不是,a)无可用性,即这两笔交易至少会有一笔交易不会被接受;要不就是,b)无一致性,一半看到的是 A 多了十元而另一半则看到 B 多了十元。要注意的是,CAP 理论和扩展性(scalability)是无关的,在分片(sharded)或非分片的系统皆适用。 FLP impossibility,在异步环境中,如果节点间的网络延迟没有上限,只要有一个恶意的节点存在,就没有算法能在有限的时间内达成共识。但值得注意的是, “Las Vegas” algorithms(这个算法又叫撞大运算法,其保证结果正确,只是在运算时所用资源上进行赌博,一个简单的例子是随机快速排序,它的 pivot 是随机选的,但排序结果永远一致)在每一轮皆有一定机率达成共识,随着时间增加,机率会越趋近于 1。而这也是许多成功的共识算法会采用的解决问题的办法。 容错的上限,从DLS 论文 中我们可以得到以下结论:

在部分同步(partially synchronous)的网络环境中(即网络延迟有一定的上限,但我们无法事先知道上限是多少),协议可以容忍最多 1/3 的拜占庭故障(Byzantine fault)。在异步(asynchronous)的网络环境中,具有确定性质的协议无法容忍任何错误,但这篇论文并没有提及 randomized algorithms,在这种情况下可以容忍最多 1/3 的拜占庭故障。在同步(synchronous)网络环境中(即网络延迟有上限且上限是已知的),协议可以容忍 100% 的拜占庭故障,但当超过 1/2 的节点为恶意节点时,会有一些限制条件。要注意的是,我们考虑的是"具有认证特性的拜占庭模型(authenticated Byzantine)“,而不是"一般的拜占庭模型”;具有认证特性指的是将如今已经过大量研究且成本低廉的公私钥加密机制应用在我们的算法中。

当然,还有一个著名的“8 条荒谬的分布式假设(Fallacies of Distributed Computing)”。

网络是稳定的。网络传输的延迟是零。网络的带宽是无穷大。网络是安全的。网络的拓扑不会改变。只有一个系统管理员。传输数据的成本为零。整个网络是同构的。

阿尔农·罗特姆 - 盖尔 - 奥兹(Arnon Rotem-Gal-Oz)写了一篇长文 Fallacies of Distributed Computing Explained 来解释为什么这些观点是错误的。另外,加勒思·威尔逊(Gareth Wilson)的文章 则用日常生活中的例子,对这些点做了通俗的解释。为什么我们深刻地认识到这 8 个错误?是因为,这要我们清楚地认识到——在分布式系统中错误是不可能避免的,我们在分布式系统中,能做的不是避免错误,而是要把错误的处理当成功能写在代码中。

下面分享几篇一致性方面的论文。

当然,关于经典的 CAP 理论,也存在一些误导的地方,这个问题在 2012 年有一篇论文 CAP Twelve Years Later: How the Rules Have Changed (中译版)中做了一些讨论,主要是说,在 CAP 中最大的问题就是分区,也就是 P,在 P 发生的情况下,非常难以保证 C 和 A。然而,这是强一致性的情况。 其实,在很多时候,我们并不需要强一致性的系统,所以后来,人们争论关于数据一致性和可用性时,主要是集中在强一致性的 ACID 或最终一致性的 BASE。当时,BASE 还不怎么为世人所接受,主要是大家都觉得 ACID 是最完美的模型,大家很难接受不完美的 BASE。在 CAP 理论中,大家总是觉得需要“三选二”,也就是说,P 是必选项,那“三选二”的选择题不就变成数据一致性 (consistency)、服务可用性 (availability) 间的“二选一”? 然而,现实却是,P 很少遇到,而 C 和 A 这两个事,工程实践中一致性有不同程度,可用性也有不同等级,在保证分区容错性的前提下,放宽约束后可以兼顾一致性和可用性,两者不是非此即彼。其实,在一个时间可能允许的范围内是可以取舍并交替选择的。 Harvest, Yield, and Scalable Tolerant Systems ,这篇论文是基于上面那篇“CAP 12 年后”的论文写的,它主要提出了 Harvest 和 Yield 概念,并把上面那篇论文中所讨论的东西讲得更为仔细了一些。 Base: An Acid Alternative (中译版),本文是 eBay 的架构师在 2008 年发表给 ACM 的文章,是一篇解释 BASE 原则,或者说最终一致性的经典文章。文中讨论了 BASE 与 ACID 原则的基本差异, 以及如何设计大型网站以满足不断增长的可伸缩性需求,其中有如何对业务做调整和折中,以及一些具体的折中技术的介绍。一个比较经典的话是——“在对数据库进行分区后, 为了可用性(Availability)牺牲部分一致性(Consistency)可以显著地提升系统的可伸缩性 (Scalability)”。 Eventually Consistent ,这篇文章是 AWS 的 CTO 维尔纳·沃格尔(Werner Vogels)在 2008 年发布在 ACM Queue 上的一篇数据库方面的重要文章,阐述了 NoSQL 数据库的理论基石——最终一致性,对传统的关系型数据库(ACID,Transaction)做了较好的补充。

小结

好了,总结一下今天分享的内容。文章的开头,我给出了学习分布式架构需要注意的几个关键点,然后列出了入门学习的资源,基本涵盖了所有与系统架构相关的技术。随后讲述了拜占庭容错系统研究中有三个重要理论:CAP、FLP 和 DLS,以及 8 条荒谬的分布式假设,从理论和认知等角度让你更为清楚地理解分布式系统。最后分享了几篇一致性相关的论文,很实用很经典,推荐阅读。

分布式架构经典图书和论文(分布式架构)

经典图书

首先,我推荐几本分布式架构方面的经典图书。

Distributed Systems for fun and profit,这是一本免费的电子书。作者撰写此书的目的是希望以一种更易于理解的方式,讲述以亚马逊的 Dynamo、谷歌的 Bigtable 和 MapReduce 等为代表的分布式系统背后的核心思想。 Designing Data Intensive Applications,这本书是一本非常好的书,我们知道,在分布式的世界里,数据结点的扩展是一件非常麻烦的事。这本书深入浅出地用很多的工程案例讲解了如何让数据结点做扩展。作者马丁·科勒普曼(Martin Kleppmann)在分布式数据系统领域有着很深的功底,并在这本书中完整地梳理各类纷繁复杂设计背后的技术逻辑,不同架构之间的妥协与超越,很值得开发人员与架构设计者阅读。 这本书深入到 B-Tree、SSTables、LSM 这类数据存储结构中,并且从外部的视角来审视这些数据结构对 NoSQL 和关系型数据库的影响。这本书可以让你很清楚地了解到真正世界的大数据架构中的数据分区、数据复制的一些坑,并提供了很好的解决方案。最赞的是,作者将各种各样技术的本质非常好地关联在一起,令你触类旁通。 而且,这本书完全就是抽丝剥茧,循循善诱,从“提出问题”到“解决问题”、“解决方案”、“优化方案”和“对比不同的方案”,一点一点地把非常晦涩的技术和知识展开。本书的引用相当多,每章后面都有几百个 Reference,通过这些 Reference 你可以看到更为广阔、更为精彩的世界。 [Distributed Systems: Principles and Paradigms](http://barbie.uta.edu/~jli/Resources/MapReduce&Hadoop/Distributed Systems Principles and Paradigms.pdf) ,本书是由计算机科学家安德鲁·斯图尔特·塔能鲍姆(Andrew S. Tanenbaum)和其同事马丁·范·斯蒂恩(Martin van Steen)合力撰写的,是分布式系统方面的经典教材。 语言简洁,内容通俗易懂,介绍了分布式系统的七大核心原理,并给出了大量的例子;系统讲述了分布式系统的概念和技术,包括通信、进程、命名、同步化、一致性和复制、容错以及安全等;讨论了分布式应用的开发方法(即范型)。但本书不是一本指导“如何做”的手册,仅适合系统性地学习基础知识,了解编写分布式系统的基本原则和逻辑。中文翻译版为《分布式系统原理与范型》(第二版)。 Scalable Web Architecture and Distributed Systems, 这是一本免费的在线小册子,其中文翻译版 可扩展的 Web 架构和分布式系统。本书主要针对面向互联网(公网)的分布式系统,但其中的原理或许也可以应用于其他分布式系统的设计中。作者的观点是,通过了解大型网站的分布式架构原理,小型网站的构建也能从中受益。本书从大型互联网系统的常见特性,如高可用、高性能、高可靠、易管理等出发,引出了一个类似于 Flickr 的典型的大型图片网站的例子。 Principles of Distributed Systems ,本书是苏黎世联邦理工学院的教材。它讲述了多种分布式系统中会用到的算法。虽然分布式系统的不同场景会用到不同算法,但并不表示这些算法都会被用到。不过,作为学生来说,掌握了算法设计的精髓也就能举一反三地设计出解决其他问题的算法,从而得到分布式系统架构设计中所需的算法。

经典论文

分布式事务

想了解分布式模型中最难的“分布式事务”,你需要看看 Google App Engine 联合创始人瑞恩·巴雷特(Ryan Barrett)在 2009 年的 Google I/O 大会上的演讲《Transaction Across DataCenter》(YouTube 视频)。

在这个演讲中,巴雷特讲述了各种经典的解决方案如何在一致性、事务、性能和错误上做平衡。而最后得到为什么分布式系统的事务只有 Paxos 算法是最好的。

下面这个图是这个算法中的结论。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kxc6l59o-1666747390244)(data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAD/AjYDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJico*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*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*rn8F+K7H4geEtL17R5mutL1q2S8s5gpAnicZVh7EEH0wRV/wBvZw4+0WJq8qsm+eWl9uvkyXToppWWvkjeHiPUj/zEdQ/8CH/xo/4SLUh/zEb/AP8AAh/8app9zd/Dx83bnp+dP8pnj3KrbfUDgVP+sObf9BVT/wADn/mV7Gl/KvuJ/wDhI9Sx/wAhC+/8CH/xp3/CR6kf+Yhff+BD/wCNeK/tifFrxl8DPhzb+JvC6+FprG11PTbDUYNVtriSd1vNStLMNC0ciKpVbh2+cNkqvbOfYZF8uV1/usQK662aZxTw9PFfW5uM3JK1Sd042und6aSXfczjTpOThyrTyRa/4SHUv+gjff8AgQ/+NH/CQ6l/0Er/AP8AAh/8aqUVxf6xZr/0FVP/AAOX+Zp9Xpfyr7kWv+Eh1L/oI3//AIEP/jR/wkWpf9BG/wD/AAIf/GqtFH+sWbf9BVT/AMDl/mH1el/KvuLR8RakB/yEb/8A8CH/AMa5/wCPni/WNJ0Dwk9nq2qWj3EV2ZWgu5IzLtePG4qRnGSBnpmtY9K5n9ov/kXPBn/XG8/9GRV+keH2d5jUpZi6mIqO1FWvOTs/bUlda6Ozav5nyHG0I08tco*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*ck+RaPiXVD/wAxTUv/AAKk/wAa+xv+CTurXl9oXxa8+8urjy49G2+bMz7cyXucZPtXxa3Wvsv/AIJJf8gD4u/9c9F/9GXteJxNUksmxjTf8Gp/6RI/M/GDC0Y8KYmUYJPmpdF/z9pn1P58n/PVv++jR58n/PVv++jRRX+bH1zEf8/Jfez+TvZx7fgHnyf89W/76NHnyf8APVv++jRRR9cxH/PyX3sPZx7fgHnyf89W/wC+jR58n/PVv++zRRT+uYj/AJ+S+9h7OPb8A8+T/nq3/fRo8+T/AJ6t/wB9GgnAprNxR9cxH88vvYezj2/AcbmTP+sf/vo0G4kB/wBZJ/30a8R/4Wr47+K3xL+JWj+B9U8F6XN8M7uPTDpOsafNdXGrTyadb3sU8ssc6G0tXa5WJXEUpPkzOM42D1Twzrl03gTTNR8Qw2ui6hJZQTajC048mynZV8yMOeMLISoji*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*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)]

你也可以移步看一下我在 Coolshell 上写的这篇文章《分布式系统的事务处理》。

Paxos 一致性算法

Paxos 算法,是莱斯利·兰伯特(Lesile Lamport)于 1990 年提出来的一种基于消息传递且具有高度容错特性的一致性算法。但是这个算法太过于晦涩,所以一直以来都属于理论上的论文性质的东西。其真正进入工程圈,主要是来源于 Google 的 Chubby lock——一个分布式的锁服务,用在了 Bigtable 中。直到 Google 发布了下面这两篇论文,Paxos 才进入到工程界的视野中来。

Bigtable: A Distributed Storage System for Structured DataThe Chubby lock service for loosely-coupled distributed systems

Google 与 Bigtable 相齐名的还有另外两篇论文。

The Google File SystemMapReduce: Simplified Data Processing on Large Clusters

不过,这几篇文章中并没有讲太多的 Paxos 算法上的细节,反而是在Paxos Made Live - An Engineering Perspective 这篇论文中提到了很多工程实现的细节。这篇论文详细解释了 Google 实现 Paxos 时遇到的各种问题和解决方案,讲述了从理论到实际应用二者之间巨大的鸿沟。

Paxos 算法的原版论文比较晦涩,也不易懂。这里推荐一篇比较容易读的—— Neat Algorithms - Paxos 。这篇文章中还有一些小动画帮助你读懂。还有一篇可以帮你理解的文章是 Paxos by Examples。

Raft 一致性算法

因为 Paxos 算法太过于晦涩,而且在实际的实现上有太多的坑,并不太容易写对。所以,有人搞出了另外一个一致性的算法,叫 Raft。其原始论文是 In search of an Understandable Consensus Algorithm (Extended Version) ,寻找一种易于理解的 Raft 算法。这篇论文的译文在 InfoQ 上,题为《Raft 一致性算法论文译文》,推荐你读一读。

这里推荐几个不错的 Raft 算法的动画演示。

Raft - The Secret Lives of DataRaft Consensus AlgorithmRaft Distributed Consensus Algorithm Visualization

Gossip 一致性算法

后面,业内又搞出来一些工程上的东西,比如 Amazon 的 DynamoDB,其论文Dynamo: Amazon’s Highly Available Key Value Store 的影响力非常大。这篇论文中讲述了 Amazon 的 DynamoDB 是如何满足系统的高可用、高扩展和高可靠的。其中展示了系统架构是如何做到数据分布以及数据一致性的。GFS 采用的是查表式的数据分布,而 DynamoDB 采用的是计算式的,也是一个改进版的通过虚拟结点减少增加结点带来数据迁移的一致性哈希。

这篇文章中有几个关键的概念,一个是 Vector Clock,另一个是 Gossip 协议。

Time, Clocks and the Ordering of Events in a Distributed System ,这篇文章是莱斯利·兰伯特(Leslie Lamport)于 1978 年发表的,并在 2007 年被选入 SOSP 的名人堂,被誉为第一篇真正的“分布式系统”论文,该论文曾一度成为计算机科学史上被引用最多的文章。分布式系统中的时钟同步是一个非常难的问题,因为分布式系统中是使用消息进行通信的,若使用物理时钟来进行同步,一方面是不同的 process 的时钟有差异,另一方面是时间的计算也有一定的误差,这样若有两个时间相同的事件,则无法区分它们谁前谁后了。这篇文章主要解决分布式系统中的时钟同步问题。马萨诸塞大学课程 Distributed Operating System 中第 10 节 Clock Synchronization,这篇讲义讲述了时钟同步的问题。关于 Vector Clock,你可以看一下 Why Vector Clocks are Easy 和 Why Vector Clocks are Hard 这两篇文章。

用来做数据同步的 Gossip 协议的原始论文是 Efficient Reconciliation and Flow Control for Anti-Entropy Protocols。Gossip 算法也是 Cassandra 使用的数据复制协议。这个协议就像八卦和谣言传播一样,可以“一传十、十传百”传播开来。但是这个协议看似简单,细节上却非常麻烦。

Gossip 协议也是 NoSQL 数据库 Cassandra 中使用到的数据协议,你可以上 YouTube 上看一下这个视频介绍: Understanding Gossip (Cassandra Internals)。

关于 Gossip 的一些图示化的东西,你可以看一下动画 Gossip Visualization。

分布式存储和数据库

除了前面的 Google 的 BigTable 和 Google File System 那两篇论文,还有 Amazon 的 DynamoDB 的论文,下面也有几篇也是要读一下的。

一篇是 AWS Aurora 的论文 Amazon Aurora: Design Considerations for High Throughput Cloud -Native Relation Databases。另一篇是比较有代表的论文是 Google 的 Spanner: Google’s Globally-Distributed Database。 其 2017 年的新版论文:Spanner, TrueTime & The CAP Theorem。F1 - The Fault-Tolerant Distributed RDBMS Supporting Google’s Ad Business 。Cassandra: A Decentralized Structured Storage System 。CRUSH: Controlled, Scalable, Decentralized Placement of Replicated Data, 这里提到的算法被应用在了 Ceph 分布式文件系统中,其架构可以读一下 RADOS - A Scalable, Reliable Storage Service for Petabyte-scale Storage Clusters 以及 Ceph 的架构文档。

分布式消息系统

分布式消息系统,你一定要读一下 Kafka 的这篇论文 Kafka: a Distributed Messaging System for Log Processing。Wormhole: Reliable Pub-Sub to Support Geo-replicated Internet Services ,Wormhole 是 Facebook 内部使用的一个 Pub-Sub 系统,目前还没有开源。它和 Kafka 之类的消息中间件很类似。但是它又不像其它的 Pub-Sub 系统,Wormhole 没有自己的存储来保存消息,它也不需要数据源在原有的更新路径上去插入一个操作来发送消息,是非侵入式的。其直接部署在数据源的机器上并直接扫描数据源的 transaction logs,这样还带来一个好处,Wormhole 本身不需要做任何地域复制(geo-replication)策略,只需要依赖于数据源的 geo-replication 策略即可。All Aboard the Databus! LinkedIn’s Scalable Consistent Change Data Capture Platform , 在 LinkedIn 投稿 SOCC 2012 的这篇论文中,指出支持对不同数据源的抽取,允许不同数据源抽取器的开发和接入,只需该抽取器遵循设计规范即可。该规范的一个重要方面就是每个数据变化都必须被一个单调递增的数字标注(SCN),用于同步。这其中的一些方法完全可以用做异地双活的系统架构中。(和这篇论文相关的几个链接如下:PDF 论文 、 PPT 分享。)

日志和数据

The Log: What every software engineer should know about real-time data’s unifying abstraction ,这篇文章好长,不过这是一篇非常好非常好的文章,这是每个工程师都应用知道的事,必看啊。你可以看中译版《日志:每个软件工程师都应该知道的有关实时数据的统一概念》。 The Log-Structured Merge-Tree (LSM-Tree) ,N 多年前,谷歌发表了 Bigtable 的论文,论文中很多很酷的方面,其一就是它所使用的文件组织方式,这个方法更一般的名字叫 Log Structured-Merge Tree。LSM 是当前被用在许多产品的文件结构策略:HBase、Cassandra、LevelDB、SQLite,甚至在 MongoDB 3.0 中也带了一个可选的 LSM 引擎(Wired Tiger 实现的)。LSM 有趣的地方是它抛弃了大多数数据库所使用的传统文件组织方法。实际上,当你第一次看它时是违反直觉的。这篇论文可以让你明白这个技术。(如果读起来有些费解的话,你可以看看中文社区里的这几篇文章:文章一、文章二。) Immutability Changes Everything ,这篇论文是现任 Salesforce 软件架构师帕特·赫兰德(Pat Helland)在 CIDR 2015 大会上发表的(相关视频演讲)。 Tango: Distributed Data Structures over a Shared Log)。这个论文非常经典,其中说明了不可变性(immutability)架构设计的优点。随着为海量数据集存储和计算而设计的以数据为中心的新型抽象技术的出现,分布式系统比以往任何时候都更容易构建。但是,对于元数据的存储和访问不存在类似的抽象。 为了填补这一空白,Tango 为开发人员提供了一个由共享日志支持的内存复制数据结构(例如地图或树)的抽象。Tango 对象易于构建和使用,通过共享日志上简单的追加和读取操作来复制状态,而不是复杂的分布式协议。在这个过程中,它们从共享日志中获得诸如线性化、持久性和高可用性等属性。Tango 还利用共享日志支持跨不同对象的快速事务处理,允许应用程序跨机器进行状态划分,并在不牺牲一致性的情况下扩展到底层日志的上限。

分布式监控和跟踪

Google 的分布式跟踪监控论文 - Dapper, a Large-Scale Distributed Systems Tracing Infrastructure, 其开源实现有三个 Zipkin、Pinpoint 和 HTrace。我个人更喜欢 Zipkin。

数据分析

The Unified Logging Infrastructure for Data Analytics at Twitter ,Twitter 公司的一篇关于日志架构和数据分析的论文。[Scaling Big Data Mining Infrastructure: The Twitter Experience](http://www.datascienceassn.org/sites/default/files/Scaling Big Data Mining Infrastructure - The Twitter Experience.pdf) ,讲 Twitter 公司的数据分析平台在数据量越来越大,架构越来越复杂,业务需求越来越多的情况下,数据分析从头到底是怎么做的。Dremel: Interactive Analysis of Web-Scale Datasets,Google 公司的 Dremel,是一个针对临时查询提供服务的系统,它处理的是只读的多层数据。本篇文章介绍了它的架构与实现,以及它与 MapReduce 是如何互补的。Resident Distributed Datasets: a Fault-Tolerant Abstraction for In-Memory Cluster Computing,这篇论文提出了弹性分布式数据集(Resilient Distributed Dataset,RDD)的概念,它是一个分布式存储抽象,使得程序员可以在大型集群上以容错的方式执行内存计算;解释了其出现原因:解决之前计算框架在迭代算法和交互式数据挖掘工具两种应用场景下处理效率低下的问题,并指出将数据保存在内存中,可以将性能提高一个数量级;同时阐述了其实现原理及应用场景等多方面内容。很有趣儿,推荐阅读。

与编程相关的论文

Distributed Programming ModelPSync: a partially synchronous language for fault-tolerant distributed algorithmsProgramming Models for Distributed ComputingLogic and Lattices for Distributed Programming

其它的分布式论文阅读列表

除了上面上的那些我觉得不错的论文,下面还有三个我觉得不错的分布式系统论文的阅读列表,你可以浏览一下。

Services Engineering Reading ListReadings in Distributed SystemsGoogle Research - Distributed Systems and Parallel Computing

小结

今天分享的内容是分布式架构方面的经典图书和论文,并给出了导读文字,几乎涵盖了分布式系统架构方面的所有关键的理论知识。这些内容非常重要,是学好分布式架构的基石,请一定要认真学习。

分布式架构工程设计 (分布式架构)

要学好分布式架构,你首先需要学习一些架构指导性的文章和方法论,即分布式架构设计原则。下面是几篇很不错的文章,值得一读。

Designs, Lessons and Advice from Building Large Distributed Systems,Google 杰夫·迪恩(Jeff Dean)2009 年一次演讲的 PPT。2010 年,斯坦福大学请杰夫·迪恩到大学里给他们讲了一节课,你可以在 YouTube 上看一下,Building Software Systems At Google and Lessons Learned ,其回顾了 Google 发展的历史。The Twelve-Factor App ,如今,软件通常会作为一种服务来交付,它们被称为网络应用程序,或软件即服务(SaaS)。12-Factor 为构建 SaaS 应用提供了方法论,是架构师必读的文章。(中译版)这篇文章在业内的影响力很大,必读!Notes on Distributed Systems for Young Bloods ,给准备进入分布式系统领域的人的一些忠告。On Designing and Deploying Internet-Scale Services(中译版),微软 Windows Live 服务平台的一些经验性的总结文章,很值得一读。4 Things to Keep in Mind When Building a Platform for the Enterprise ,Box 平台 VP 海蒂·威廉姆斯(Heidi Williams)撰写的一篇文章,阐述了为企业构建平台时需要牢记的四件关于软件设计方面的事:1. Design Broadly, Build Narrowly; 2. Platforms Are Powerful and Flexible. Choose wisely what to expose when!;3. Build Incrementally, Get Feedback, and Iterate;4. Create a Platform-first Mentality。文章中有详细的解读,推荐看看。Principles of Chaos Engineering ,我们知道,Netflix 公司有一个叫 Chaos Monkey 的东西,这个东西会到分布式系统里“瞎搞”,以此来测试系统的健壮和稳定性。这个视频中,Netflix 分享了一些软件架构的经验和原则,值得一看。Building Fast & Resilient Web Applications ,伊利亚·格里高利克(Ilya Grigorik)在 Google I/O 2016 上的一次关于如何通过弹力设计来实现快速和可容错的网站架构的演讲,其中有好些经验分享。Design for Resiliency ,这篇文章带我们全面认识“弹力(Resiliency)”,以及弹力对于系统的重要性,并详细阐述了如何设计和实现系统的弹力。微软的 Azure 网站上有一系列的 Design Principle 的文章,你可以看看这几篇: Design for Self-healing 、Design for Scaling Out 和 Design for Evolution 。Eventually Consistent ,AWS CTO 维尔纳·沃格尔斯(Werner Vogels)发布在自己 Blog 上的一篇关于最终一致性的好文。Writing Code that Scales ,Rackspace 的一篇很不错的博文,告诉我们一些很不错的写出高扩展和高性能代码的工程原则。Automate and Abstract: Lessons from Facebook on Engineering for Scale ,软件自动化和软件抽象,这是软件工程中最重要的两件事了。通过这篇文章,我们可以看到 Facebook 的关于这方面的一些经验教训。

设计模式

有了方法论后,你还需要学习一些比较细节的落地的技术。最好的方式就是学习被前人总结出来的设计模式,虽然设计模式也要分场景,但是设计模式可以让你知道一些套路,这些套路对于我们设计的分布式系统有非常大的帮助,不但可以让我们少走一些弯路,而且还能让我们更为系统和健壮地设计我们的架构。

下面是一些分布式架构设计模式的网站。

首先,需要重点推荐的是微软云平台 Azure 上的设计模式。 Cloud Design Patterns ,这个网站上罗列了分布式设计的各种设计模式,可以说是非常全面和完整。对于每一个模式都有详细的说明,并有对其优缺点的讨论,以及适用场景和不适用场景的说明,实在是一个非常不错的学习分布式设计模式的地方。其中有如下分类。

设计模式:可用性;设计模式:数据管理;设计模式:设计和实现;设计模式:消息;设计模式:管理和监控;设计模式:性能和扩展;设计模式:系统弹力;设计模式:安全。

除此之外,还有其它的一些关于分布式系统设计模式的网站和相关资料。

AWS Cloud Pattern ,这里收集了 AWS 云平台的一些设计模式。Design patterns for container-based distributed systems ,这是 Google 给的一篇论文,其中描述了容器化下的分布式架构的设计模式。Patterns for distributed systems ,这是一个 PPT,其中讲了一些分布式系统的架构模式,你可以顺着到 Google 里去搜索。

我个人觉得微服务也好,SOA 也好,都是分布式系统的一部分,这里有两个网站罗列了各种各样的服务架构模式。

A Pattern Language for Micro-Services ;SOA Patterns。

当然,还有我在极客时间上写的那些分布式的设计模式的总结。

弹力设计篇,内容包括:认识故障和弹力设计、隔离设计、异步通讯设计、幂等性设计、服务的状态、补偿事务、重试设计、熔断设计、限流设计、降级设计、弹力设计总结。管理设计篇,内容包括:分布式锁、配置中心、边车模式、服务网格、网关模式、部署升级策略等。性能设计篇,内容包括:缓存、异步处理、数据库扩展、秒杀、边缘计算等。

设计与工程实践

分布式系统的故障测试

FIT: Failure Injection Testing ,Netflix 公司的一篇关于做故障注入测试的文章。Automated Failure Testing ,同样来自 Netflix 公司的自动化故障测试的一篇博文。Automating Failure Testing Research at Internet Scale ,Netflix 公司伙同圣克鲁斯加利福尼亚大学和 Gremlin 游戏公司一同撰写的一篇论文。

弹性伸缩

4 Architecture Issues When Scaling Web Applications: Bottlenecks, Database, CPU, IO ,本文讲解了后端程序的主要性能指标,即响应时间和可伸缩性这两者如何能提高的解决方案,讨论了包括纵向和横向扩展,可伸缩架构、负载均衡、数据库的伸缩、CPU 密集型和 I/O 密集型程序的考量等。Scaling Stateful Objects ,这是一本叫《Development&Deployment of Multiplayer Online Games》书中一章内容的节选,讨论了有状态和无状态的节点如何伸缩的问题。虽然还没有写完,但是可以给你一些很不错的基本概念和想法。Scale Up vs Scale Out: Hidden Costs ,Coding Horror 上的一篇有趣的文章,详细分析了可伸缩性架构的不同扩展方案(横向扩展或纵向扩展)所带来的成本差异,帮助你更好地选择合理的扩展方案,可以看看。Best Practices for Scaling Out ,OpenShift 的一篇讨论 Scale out 最佳实践的文章。Scalability Worst Practices ,这篇文章讨论了一些最差实践,你需要小心避免。Reddit: Lessons Learned From Mistakes Made Scaling To 1 Billion Pageviews A Month ,Reddit 分享的一些关于系统扩展的经验教训。下面是几篇关于自动化弹性伸缩的文章。

Autoscaling Pinterest;Square: Autoscaling Based on Request Queuing;PayPal: Autoscaling Applications;Trivago: Your Definite Guide For Autoscaling Jenkins;Scryer: Netflix’s Predictive Auto Scaling Engine。

一致性哈希

Consistent Hashing ,这是一个一致性哈希的简单教程,其中还有代码示例。Consistent Hashing: Algorithmic Tradeoffs ,这篇文章讲述了一致性哈希的一些缺陷和坑,以及各种哈希算法的性能比较,最后还给了一组代码仓库,其中有各种哈希算法的实现。Distributing Content to Open Connect ,Netflix 的一个对一致性哈希的实践,提出了 Uniform Consistent Hashing,是挺有意思的一篇文章。Consistent Hashing in Cassandra ,这是 Cassandra 中使用到的一致性哈希的相关设计。

数据库分布式

Life Beyond Distributed Transactions ,该文是 Salesforce 的软件架构师帕特·赫兰德(Pat Helland)于 2016 年 12 月发表的针对其在 2007 年 CIDR(创新数据库研究会议)上首次发表的同名文章的更新和缩写版本。业界谈到分布式事务通常指两段提交 2PC 事务(Spring/JEE 中 JTA 等) 或者 Paxos 与 Raft,这些事务都有明显缺点和局限性。 而赫兰德在本文讨论的是另外一种基于本地事务情况下的事务机制,它是基于实体和活动(Activity)的概念,其实类似 DDD 聚合根和领域事件的概念,这种工作流类型事务虽然需要程序员介入,依靠消息系统实现,但可以实现接近无限扩展的大型系统。赫兰德文中提出了重要的观点:“如果你不能使用分布式事务,那么你就只能使用工作流。” How Sharding Works ,这是一篇很不错的探讨数据 Sharding 的文章。基本上来说,数据 Sharding 可能的问题都在这篇文章里谈到了。 Why you don’t want to shard ,这是 Percona 的一篇文章,其中表达了,不到万不得已不要做数据库分片。是的,最好还是先按业务来拆分,先把做成微服务的架构,然后把数据集变简单,然后再做 Sharding 会更好。 [How to Scale Big Data Applications](https://www.percona.com/sites/default/files/presentations/How to Scale Big Data Applications.pdf) ,这也是 Percona 给出的一篇关于怎样给大数据应用做架构扩展的文章。值得一读。 MySQL Sharding with ProxySQL ,用 ProxySQL 来支撑 MySQL 数据分片的一篇实践文章。

缓存

缓存更新的套路,这是我在 CoolShell 上写的缓存更新的几个设计模式,包括 Cache Aside、Read/Write Through、Write Behind Caching。Design Of A Modern Cache ,设计一个现代化的缓存系统需要注意到的东西。Netflix: Caching for a Global Netflix ,Netflix 公司的全局缓存架构实践。Facebook: An analysis of Facebook photo caching ,Facebook 公司的图片缓存使用分析,这篇文章挺有意思的,用数据来调优不同的缓存大小和算法。How trivago Reduced Memcached Memory Usage by 50% ,Trivago 公司一篇分享自己是如何把 Memcached 的内存使用率降了一半的实践性文章。很有意思,可以让你学到很多东西。Caching Internal Service Calls at Yelp ,Yelp 公司的缓存系统架构。

消息队列

Understanding When to use RabbitMQ or Apache Kafka ,什么时候使用 RabbitMQ,什么时候使用 Kafka,通过这篇文章可以让你明白如何做技术决策。Trello: Why We Chose Kafka For The Trello Socket Architecture ,Trello 的 Kafka 架构分享。LinkedIn: Running Kafka At Scale ,LinkedIn 公司的 Kafka 架构扩展实践。Should You Put Several Event Types in the Same Kafka Topic? ,这个问题可能经常困扰你,这篇文章可以为你找到答案。Billions of Messages a Day - Yelp’s Real-time Data Pipeline ,Yelp 公司每天十亿级实时消息的架构。Uber: Building Reliable Reprocessing and Dead Letter Queues with Kafka ,Uber 公司的 Kafka 应用。Uber: Introducing Chaperone: How Uber Engineering Audits Kafka End-to-End ,Uber 公司对 Kafka 消息的端到端审计。Publishing with Apache Kafka at The New York Times ,纽约时报的 Kafka 工程实践。Kafka Streams on Heroku ,Heroku 公司的 Kafka Streams 实践。Salesforce: How Apache Kafka Inspired Our Platform Events Architecture ,Salesforce 的 Kafka 工程实践。Exactly-once Semantics are Possible: Here’s How Kafka Does it ,怎样用 Kafka 让只发送一次的语义变为可能。这是业界中一个很难的工程问题。Delivering billions of messages exactly once 同上,这也是一篇挑战消息只发送一次这个技术难题的文章。Benchmarking Streaming Computation Engines at Yahoo!。Yahoo! 的 Storm 团队在为他们的流式计算做技术选型时,发现市面上缺乏针对不同计算平台的性能基准测试。于是,他们研究并设计了一种方案来做基准测试,测试了 Apache Flink、Apache Storm 和 Apache Spark 这三种平台。文中给出了结论和具体的测试方案。(如果原文链接不可用,请尝试搜索引擎对该网页的快照。)

关于日志方面

Using Logs to Build a Solid Data Infrastructure - Martin Kleppmann ,设计基于 log 结构应用架构的一篇不错的文章。Building DistributedLog: High-performance replicated log service ,Distributed 是 Twitter 2016 年 5 月份开源的一个分布式日志系统。在 Twitter 内部已经使用 2 年多。其主页在 distributedlog.io。这篇文章讲述了这个高性能日志系统的一些技术细节。另外,其技术负责人是个中国人,其在微信公众号中也分享过这个系统 Twitter 高性能分布式日志系统架构解析。LogDevice: a distributed data store for logs ,Facebook 分布式日志系统方面的一些工程分享。

关于性能方面

Understand Latency ,这篇文章收集并整理了一些和系统响应时间相关的文章,可以让你全面了解和 Latency 有关的系统架构和设计经验方面的知识。Common Bottlenecks ,文中讲述了 20 个常见的系统瓶颈。Performance is a Feature ,Coding Horror 上的一篇让你关注性能的文章。Make Performance Part of Your Workflow ,这篇文章是图书《Designing for Performance》中的节选(国内没有卖的),其中给出来了一些和性能有关的设计上的平衡和美学。CloudFlare: How we built rate limiting capable of scaling to millions of domains,讲述了 CloudFlare 公司是怎样实现他们的限流功能的。从最简单的每客户 IP 限流开始分析,进一步讲到 anycast,在这种情况下 PoP 的分布式限流是怎样实现的,并详细解释了具体的算法。

关于搜索方面

Instagram: Search ArchitectureeBay: The Architecture of eBay SearcheBay: Improving Search Engine Efficiency by over 25%LinkedIn: Introducing LinkedIn’s new search architectureLinkedIn: Search Federation Architecture at LinkedInSlack: Search at SlackDoorDash: Search and Recommendations at DoorDashTwitter: Search Service at Twitter (2014)Pinterest: Manas: High Performing Customized Search SystemSherlock: Near Real Time Search Indexing at FlipkartAirbnb: Nebula: Storage Platform to Build Search Backends

各公司的架构实践

High Scalability ,这个网站会定期分享一些大规模系统架构是怎样构建的,下面是迄今为止各个公司的架构说明。

YouTube ArchitectureScaling PinterestGoogle ArchitectureScaling TwitterThe WhatsApp ArchitectureFlickr ArchitectureAmazon ArchitectureStack Overflow ArchitecturePinterest ArchitectureTumblr ArchitectureInstagram ArchitectureTripAdvisor ArchitectureScaling MailboxSalesforce ArchitectureESPN ArchitectureUber ArchitectureDropBox DesignSplunk Architecture

小结

今天我们分享的内容是高手成长篇分布式架构部分的最后一篇——分布式架构工程设计,讲述了设计原则、设计模式等方面的内容,尤其整理和推荐了国内外知名企业的设计思路和工程实践,十分具有借鉴意义。

微服务

微服务是分布式系统中最近比较流行的架构模型,也是 SOA 架构的一个进化。微服务架构并不是银弹,所以,也不要寄希望于微服务架构能够解决所有的问题。微服务架构主要解决的是如何快速地开发和部署我们的服务,这对于一个能够适应快速开发和成长的公司是非常必要的。同时我也觉得,微服务中有很多很不错的想法和理念,所以学习微服务是每一个技术人员迈向卓越的架构师的必经之路。

首先,你需要看一下,Martin Fowler 的这篇关于微服务架构的文档 - Microservice Architecture (中译版),这篇文章说明了微服务的架构与传统架构的不同之处在于,微服务的每个服务与其数据库都是独立的,可以无依赖地进行部署。你也可以看看 Martin Fowler 老人家现身说法的视频。

另外,你还可以简单地浏览一下,各家对微服务的理解。

AWS 的理解 - What are Microservices?。Microsoft 的理解 - Microservices architecture style。Pivotal 的理解 - Microservices。

微服务架构

接下来,你可以看一下 IBM 红皮书:Microservices Best Practices for Java ,这本书非常好,不但有通过把 Spring Boot 和 Dropwizard 来架建 Java 的微服务,而且还谈到了一些标准的架构模型,如服务注册、服务发现、API 网关、服务通讯、数据处理、应用安全、测试、部署、运维等,是相当不错的一本书。

当然,有一本书你也可以读一下—— 微服务设计。这本书全面介绍了微服务的建模、集成、测试、部署和监控,通过一个虚构的公司讲解了如何建立微服务架构。主要内容包括认识微服务在保证系统设计与组织目标统一上的重要性,学会把服务集成到已有系统中,采用递增手段拆分单块大型应用,通过持续集成部署微服务,等等。

与此相似的,也有其它的一系列文章,值得一读。

下面是 Nginx 上的一组微服务架构的系列文章。

Introduction to MicroservicesBuilding Microservices: Using an API GatewayBuilding Microservices: Inter-Process Communication in a Microservices ArchitectureService Discovery in a Microservices ArchitectureEvent-Driven Data Management for MicroservicesChoosing a Microservices Deployment StrategyRefactoring a Monolith into Microservices

下面这是 Auto0 Blog 上一系列的微服务的介绍,有代码演示。

An Introduction to Microservices, Part 1API Gateway. An Introduction to Microservices, Part 2An Introduction to Microservices, Part 3: The Service RegistryIntro to Microservices, Part 4: Dependencies and Data SharingAPI Gateway: the Microservices Superglue

还有 Dzone 的这个 Spring Boot 的教程。

Microservices With Spring Boot - Part 1 - Getting StartedMicroservices With Spring Boot - Part 2 - Creating a Forex MicroserviceMicroservices With Spring Boot - Part 3 - Creating Currency Conversion MicroserviceMicroservices With Spring Boot - Part 4 - Using Ribbon for Load BalancingMicroservices With Spring Boot - Part 5 - Using Eureka Naming Server

当然,如果你要玩得时髦一些的话,我推荐你使用下面的这套架构。

前端:React.js 或 Vue.js。后端:Go 语言 + 微服务工具集 Go kit ,因为是微服务了,所以,每个服务的代码就简单了。既然简单了,也就可以用任何语言了,所以,我推荐 Go 语言。通讯:gRPC,这是 Google 远程调用的一个框架,它比 Restful 的调用要快 20 倍到 50 倍的样子。API:Swagger ,Swagger 是一种 Restful API 的简单但强大的表示方式,标准的,语言无关,这种表示方式不但人可读,而且机器可读。可以作为 Restful API 的交互式文档,也可以作为 Restful API 形式化的接口描述,生成客户端和服务端的代码。今天,所有的 API 应该都通过 Swagger 来完成。网关:Envoy 其包含了服务发现、负载均衡和熔断等这些特性,也是一个很有潜力的网关。当然,Kubernetes 也是很好的,而且它也是高扩展的,所以,完全可以把 Envoy 通过 Ingress 集成进 Kubernetes。这里有一个开源项目就是干这个事的 - contour。日志监控:fluentd + ELK 。指标监控:Prometheus 。调用跟踪:Jaeger 或是 Zipkin,当然,后者比较传统一些,前者比较时髦,最重要的是,其可以和 Prometheus 和 Envory 集成。自动化运维:Docker + Kubernetes 。

微服务和 SOA

在对微服务有了一定的认识以后,一定有很多同学分不清楚微服务和 SOA 架构,对此,你可以看一下这本电子书 - 《Microservices vs. Service-Oriented Architecture》。通过这本书,你可以学到,服务化架构的一些事实,还有基础的 SOA 和微服务的架构知识,以及两种架构的不同。这本书的作者马克·理查兹(Mark Richards)同学拥有十年以上的 SOA 和微服务架构的设计和实现的经验。

另外,还有几篇其它对比 SOA 和微服务的文章你也可以看看。

DZone: Microservices vs. SOADZone: Microservices vs. SOA - Is There Any Difference at All?Microservices, SOA, and APIs: Friends or enemies?

除此之外,我们还需要知道微服务和其它架构的一些不同和比较,这样我们就可以了解微服务架构的优缺点。下面几篇文章将帮助获得这些知识。

PaaS vs. IaaS for Microservices Architectures: Top 6 DifferencesMicroservices vs. Monolithic Architectures: Pros, Cons, and How Cloud Foundry (PaaS) Can HelpMicroservices - Not A Free Lunch!The Hidden Costs Of Microservices

设计模式和最佳实践

然后,你可以看一下微服务的一些设计模式。

Microservice Patterns,微服务架构的设计模式和最佳实践。Microservice Antipatterns and Pitfalls,微服务架构的一些已知的反模式和陷阱。Microservice Architecture: All The Best Practices You Need To Know,这是一篇长文,里面讲述了什么是微服务、微服务架构的优缺点、微服务最大的挑战和解决方案是什么、如何避免出错,以及构建微服务架构的最佳实践等多方面的内容。推荐阅读。Best Practices for Building a Microservice Architecture ,这篇文章分享了构建微服务架构的最佳实践。Simplicity by Distributing Complexity,这是一篇讲如何使用事件驱动构建微服务架构的文章,其中有很多不错的设计上的基本原则。

相关资源

Microservices Resource Guide ,这个网页上是 Martin Fowler 为我们挑选的和微服务相关的文章、视频、书或是 podcast。Awesome Microservices ,一个各种微服务资源和相关项目的集中地。

小结

好了,总结一下今天的内容。我认为,微服务中有很多很不错的想法和理念,所以学习微服务是每一个技术人员迈向卓越的架构师的必经之路。在这篇文章中,我先给出了 AWS、Microsoft 和 Pivotal 对微服务的理解;然后给出了好几个系列的教程,帮你全面学习和理解微服务架构;然后通过一系列文章帮你来区分何为微服务,何为 SOA;最后给出了微服务架构的设计模式和最佳实践,以及相关资源。相信通过这一系列内容的学习,你一定会对微服务有全面、透彻的理解。

容器化和自动化运维

这篇文章我们来重点学习 Docker 和 Kubernetes,它们已经是分布式架构和自动化运维的必备工具了。对于这两个东西,你千万不要害怕,因为技术方面都不算复杂,只是它们的玩法和传统运维不一样,所以你不用担心,只要你花上一点时间,一定可以学好的。

Docker

你可以先看一下 Docker 的官方介绍 Docker Overview 。然后再去一个 Web 在线的 Playground 上体验一下, Katacoda Docker Playground 或者是 Play With Docker 。接下来,跟着 Learn Docker 这个文档中的教程自己安装一个 Docker 的环境,实操一把。然后跟着 Docker Curriculum 这个超详细的教程玩一下 Docker。

有了上述的一些感性体会之后,你就可以阅读 Docker 官方文档 Docker Documentation 了,这是学习 Docker 最好的方式。

如果你想了解一下 Docker 的底层技术细节,你可以参看我的文章。

Docker 基础技术:Linux Namespace(上)Docker 基础技术:Linux Namespace(下)Docker 基础技术:CgroupDocker 基础技术:AUFSDocker 基础技术:DeviceMapper

还有一些不错的与 Docker 网络有关的文章你需要阅读及实践一下。

A container networking overviewDocker networking 101 - User defined networksUnderstanding CNI (Container Networking Interface)Using CNI with Docker

Docker 有下面几种网络解决方案:Calico 、Flannel 和 Weave ,你需要学习一下。另外,还需要学习一下 netshoot ,这是一个很不错的用来诊断 Docker 网络问题的工具集。

关于这几个容器网络解决方案的性能对比,你可以看一下下面这几篇文章或报告。

Battlefield: Calico, Flannel, Weave and Docker Overlay NetworkComparison of Networking Solutions for KubernetesDocker Overlay Networks: Performance analysis in high-latency enviroments

如果你对 Docker 的性能有什么问题的话,你可以看一下下面这些文章。

IBM Research Report: An Updated Performance Comparison of Virtual Machines and Linux ContainersAn Introduction to Docker and Analysis of its Performance

下面是一些和存储相关的文章。

Storage Concepts in Docker: Network and Cloud StorageStorage Concepts in Docker: Persistent StorageStorage Concepts in Docker: Shared Storage and the VOLUME directive

然后是跟运维相关的文章。

Docker Monitoring with the ELK Stack: A Step-by-Step Guide

最后,推荐看看 Valuable Docker Links ,其中收集并罗列了一系列非常不错的 Docker 文章。

最佳实践

下面分享一些与 Docker 相关的最佳实践。

Best Practices for Dockerfile ,Docker 官方文档里的 Dockerfile 的最佳实践。Docker Best Practices ,这里收集汇总了存在于各个地方的使用 Docker 的建议和实践。Container Best Practices ,来自 Atomic 项目,是一个介绍容器化应用程序的架构、创建和管理的协作型文档项目。Eight Docker Development Patterns ,八个 Docker 的开发模式:共享基础容器、共享同一个卷的多个开发容器、开发工具专用容器、测试环境容器、编译构建容器、防手误的安装容器、默认服务容器、胶黏容器。

Kubernetes

Kubernetes 是 Google 开源的容器集群管理系统,是 Google 多年大规模容器管理技术 Borg 的开源版本,也是 CNCF 最重要的项目之一,主要功能包括:

基于容器的应用部署、维护和滚动升级;负载均衡和服务发现;跨机器和跨地区的集群调度;自动伸缩;无状态服务和有状态服务;广泛的 Volume 支持;插件机制保证扩展性。

Kubernetes 发展非常迅速,已经成为容器编排领域的领导者。

首先,我推荐你阅读 Kubernetes 前世今生的一篇论文。

Borg, Omega, and Kubernetes ,看看 Google 这十几年来从这三个容器管理系统中得到的经验教训。

学习 Kubernetes,有两个免费的开源电子书。

《Kubernetes Handbook》,这本书记录了作者从零开始学习和使用 Kubernetes 的心路历程,着重于经验分享和总结,同时也会有相关的概念解析。希望能够帮助你少踩坑,少走弯路,还会指引你关注 kubernetes 生态周边,如微服务构建、DevOps、大数据应用、Service Mesh、Cloud Native 等领域。《Kubernetes 指南》,这本书旨在整理平时在开发和使用 Kubernetes 时的参考指南和实践总结,形成一个系统化的参考指南以方便查阅。

这两本电子书都不错,前者更像是一本学习教程,而且面明显广一些,还包括 Cloud Natvie、Service Mesh 以及微服务相关的东西。而后者聚焦于 Kubernetes 本身,更像一本参考书。

另外,我这两天也读完了《Kubernetes in Action》一书,感觉写的非常好,一本很完美的教科书,抽丝剥茧,图文并茂。如果你只想读一本有关 Kubernetes 的书来学习 Kubernetes,那么我推荐你就选这本。

但是也别忘了 Kubernetes 的官方网站:Kubernetes.io,上面不但有全面的文档 ,也包括一个很不错的 官方教程 。

此外,还有一些交互式教程,帮助你理解掌握,以及一些很不错的文章推荐你阅读。

一些交互式教程

KatacodaKubernetes Bootcamp

一些文章

这里还有一些不错的文档,你应该去读一下。

Kubernetes tips & tricksAchieving CI/CD with KubernetesHow to Set Up Scalable Jenkins on Top of a Kubernetes Cluster10 Most Common Reasons Kubernetes Deployments Fail Part I 和 Part IIHow to Monitor Kubernetes ,一共有 4 个篇章Logging in Kubernetes with Fluentd and ElasticsearchKubernetes Monitoring: Best Practices, Methods, and Existing Solutions

网络相关的文章

要学习 Kubernetes,你只需要读一下,下面这个 Kubernetes 101 系列的文章。

Kubernetes 101 - NetworkingKubernetes networking 101 - PodsKubernetes networking 101 - ServicesKubernetes networking 101 - (Basic) External access into the clusterKubernetes Networking 101 - Ingress resourcesGetting started with Calico on Kubernetes

CI/CD 相关的文章

Automated Image Builds with Jenkins, Packer, and KubernetesJenkins setups for Kubernetes and Docker WorkflowLab: Build a Continuous Deployment Pipeline with Jenkins and Kubernetes

最佳实践

Kubernetes Best Practices by Sachin Arote ,AWS 工程师总结的最佳实践。Kubernetes Best Practices by Sandeep Dinesh ,Google 云平台工程师总结的最佳实践。

Docker 和 Kubernetes 资源汇总

下面是 GitHub 上和 Docker & Kubernetes 相关的 Awesome 系列。

Awesome Docker。Awesome Kubernetes。

虽然上面的这些系列非常全的罗列了很多资源,但是我觉得很不系统。对于系统的说明 Docker 和 Kubernetes 生态圈,我非常推荐大家看一下 The New Stack 为 Kubernetes 出的一系列的电子书或报告。

The New Stack eBook Series ,非常完整和详实的 Docker 和 Kubernetes 生态圈的所有东西。

Book 01: The Docker Container EcosystemBook 02: Applications & Microservices with Docker & ContainersBook 03: Automation & Orchestration with Docker & ContainersBook 04: Network, Security & Storage with Docker & ContainersBook 05: Monitoring & Management with Docker & ContainersBook 06: Use Cases for KubernetesBook 07: State of the Kubernetes EcosystemBook 08: Kubernetes Deployment & Security PatternsBook 09: CI/CD with KubernetesBook 10: Kubernetes solutions DirectoryBook 11: Guid to Cloud-Native Microservices

小结

总结一下今天的内容。Docker 和 Kubernetes 已经成为分布式架构和自动化运维方面的不可或缺的两大基本构成,是你必需要学习的。虽然它们的玩法跟传统运维不一样,但技术方面并不算复杂,只要你花上一点时间,一定会学好的。

在这篇文章中,我推荐了 Docker 和 Kubernetes 基础技术方面的学习资料,并给出了存储、运维、网络、CI/CD 等多方面的资料,同时列出了与之相关的最佳实践。相信认真学习和消化这些知识,你一定可以掌握 Docker 和 Kubernetes 两大利器。

机器学习和人工智能

我之前写过一篇机器学习的入门文章,因为我也是在入门和在学习的人,所以,那篇文章和这篇机器学习和人工智能方向的文章可能都会有点太肤浅。如果你有更好的学习方式或资料,欢迎补充。

基本原理简介

我们先来介绍一下机器学习的基本原理。

机器学习主要有两种方式,一种是监督式学习(Supervised Learning),另一种是非监督式学习(Unsupervised Learning)。下面简单地说一下这两者的不同。

监督式学习(Supervised Learning)。所谓监督式学习,也就是说,我们需要提供一组学习样本,包括相关的特征数据和相应的标签。我们的程序可以通过这组样本来学习相关的规律或是模式,然后通过得到的规律或模式来判断没有被打过标签的数据是什么样的数据。 举个例子,假设需要识别一些手写的数字,我们要找到尽可能多的手写体数字的图像样本,然后人工或是通过某种算法来明确地标注上什么是这些手写体的图片,谁是 1,谁是 2,谁是 3…… 这组数据叫样本数据,又叫训练数据(training data)。然后通过机器学习的算法,找到每个数字在不同手写体下的特征,找到规律和模式。通过得到的规律或模式来识别那些没有被打过标签的手写数据,以此完成识别手写体数字的目的。 非监督式学习(Unsupervised Learning)。对于非监督式学习,也就是说,数据是没有被标注过的,所以相关的机器学习算法需要找到这些数据中的共性。因为大量的数据是没被被标识过的,所以这种学习方式可以让大量的未标识的数据能够更有价值。而且,非监督式学习,可以为我们找到人类很难发现的数据里的规律或模型,所以也有人称这种学习为“特征点学习”,其可以让我们自动地为数据进行分类,并找到分类的模型。 一般来说,非监督式学习会应用在一些交易型的数据中。比如,你有一堆堆的用户购买数据,但是对于人类来说,我们很难找到用户属性和购买商品类型之间的关系。所以,非监督式学习算法可以帮助我们找到它们之间的关系。比如,一个在某年龄段的女性购买了某种肥皂,有可能说明这个女性在怀孕期,或是某人购买儿童用品,有可能说明这个人的关系链中有孩子,等等。于是,这些信息会被用作一些所谓的精准市场营销活动,从而可以增加商品销量。

我们这么来说吧,监督式学习是在被告诉过了正确的答案后的学习,而非监督式学习是在没有被告诉正确答案时的学习。所以,非监督式学习是在大量的非常乱的数据中找寻一些潜在的关系,这个成本也比较高。非监督式学习经常被用来检测一些不正常的事情发生,比如信用卡的诈骗或是盗刷。也被用在推荐系统,比如买了这个商品的人又买了别的什么商品,或是如果某个人喜欢某篇文章、某个音乐、某个餐馆,那么他可能会喜欢某个车、某个明星或某个地方。

在监督式学习算法下,我们可以用一组“狗”的照片来确定某个照片中的物体是不是狗。而在非监督式学习算法下,我们可以通过一个照片来找到其中有与其相似的事物的照片。这两种学习方式都有些有用的场景。

关于机器学习,你可以读一读 Machine Learning is Fun! ,这篇文章(中文翻译版)恐怕是全世界最简单的入门资料了。

Data Science Simplified Part 1: Principles and ProcessData Science Simplified Part 2: Key Concepts of Statistical LearningData Science Simplified Part 3: Hypothesis TestingData Science Simplified Part 4: Simple Linear Regression ModelsData Science Simplified Part 5: Multivariate Regression ModelsData Science Simplified Part 6: Model Selection MethodsData Science Simplified Part 7: Log-Log Regression ModelsData Science Simplified Part 8: Qualitative Variables in Regression ModelsData Science Simplified Part 9: Interactions and Limitations of Regression ModelsData Science Simplified Part 10: An Introduction to Classification ModelsData Science Simplified Part 11: Logistic Regression

相关课程

接下来,我们需要比较专业地学习一下机器学习了。

在学习机器学习之前,我们需要学习数据分析,所以,我们得先学一些大数据相关的东西,也就是 Data Science 相关的内容。下面是两个不错的和数据科学相关的教程以及一个资源列表。

UC Berkeley’s Data 8: The Foundations of Data Science 和电子书 Computational and Inferential Thinking 会讲述数据科学方面非常关键的概念,会教你在数据中找到数据的关联、预测和相关的推断。Learn Data Science ,这是 GitHub 上的一本电子书,主要是一些数据挖掘的算法,比如线性回归、逻辑回归、随机森林、K-Means 聚类的数据分析。然后,donnemartin/data-science-ipython-notebooks 这个代码仓库中用 TensorFlow、scikit-learn、Pandas、NumPy、Spark 等把这些经典的例子实现了个遍。Data Science Resources List ,这个网站上有一个非常长的和数据科学相关的资源列表,你可以从中得到很多你想要的东西。

之后,有下面几门不错的在线机器学习的课程供你入门,也是非常不错。

吴恩达教授(Andrew Ng)在 Coursera 上的免费机器学习课程 非常棒。我强烈建议从此入手。对于任何拥有计算机或科学学位的人,或是还能记住一点点数学知识的人来说,都应该非常容易入门。这个斯坦福大学的课程请尽量拿满分。可以在 网易公开课 中找到这一课程。除此之外,吴恩达教授还有一组新的和深度学习相关的课程,现在可以在网易公开课上免费学习——Deep Learning Specialization。Deep Learning by Google ,Google 的一个关于深度学习的在线免费课程,其支持中英文。这门课会教授你如何训练和优化基本神经网络、卷积神经网络和长短期记忆网络。你将通过项目和任务接触完整的机器学习系统 TensorFlow。卡内基梅隆大学汤姆·米切尔(Tom Mitchell)的机器学习 英文原版视频与课件 PDF 。2013 年加利福尼亚理工学院亚瑟·阿布 - 穆斯塔法(Yaser Abu-Mostafa)的 Learning from Data 课程视频及课件 PDF,内容更适合进阶。关于神经网络方面,YouTube 上有一个非常火的课程视频,由宾夕法尼亚大学的雨果·拉罗歇尔(Hugo Larochelle)出品的教学课程 - Neural networks class - Université de Sherbrooke 。

除此之外,还有很多的在线大学课程可以供你学习。比如:

斯坦福大学的《统计学学习》、《机器学习》、《卷积神经网络》、《深度学习之自然语言处理》等。麻省理工大学的《神经网络介绍 》、《机器学习》、《预测》等。

更多的列表,请参看——Awesome Machine Learning Courses。

相关图书

《Pattern Recognition and Machine Learning》,这本书是机器学习领域的圣经之作。该书也是众多高校机器学习研究生课程的教科书,Google 上有[PDF 版的下载](http://users.isr.ist.utl.pt/~wurmd/Livros/school/Bishop - Pattern Recognition And Machine Learning - Springer 2006.pdf)。这本书很经典,但并不适合入门来看。GitHub 上有这本中的 Matlab 实现。 下面这两本电子书也是比较经典的,其中讲了很多机器学习的知识,可以当做手册或字典。

《Understanding Machine Learning: From Theory to Algorithms》。《The Elements of Statistical Learning - Second Edition》。 《Deep Learning: Adaptive Computation and Machine Learning series》 中文翻译为《深度学习》。这本书由全球知名的三位专家伊恩·古德费洛(Ian Goodfellow)、友华·本吉奥(Yoshua Bengio)和亚伦·考维尔(Aaron Courville)撰写,是深度学习领域奠基性的经典教材。 全书内容包括 3 部分:第 1 部分介绍基本的数学工具和机器学习的概念,它们是深度学习的预备知识;第 2 部分系统深入地讲解现今已成熟的深度学习方法和技术;第 3 部分讨论某些具有前瞻性的方向和想法,它们被公认为是深度学习未来的研究重点。这本书的官网为 “deeplearningbook.org”,在 GitHub 上也有中文翻译 - 《Deep Learning 中文翻译》。 《Neural Networks and Deep Learning》(中文翻译版),这是一本非常不错的神经网络的入门书,在豆瓣上评分 9.5 分,从理论讲到了代码。虽然有很多数学公式,但是有代码相助,就不难理解了。其中讲了很多如激活函数、代价函数、随机梯度下降、反向传播、过度拟合和规范化、权重初始化、超参数优化、卷积网络的局部感受野、混合层、特征映射的东西。 《Introduction to Machine Learning with Python》,算是本不错的入门书,也是本比较易读的英文书。其是以 Scikit-Learn 框架来讲述的。如果你用过 Scikit 这个框架,那么你学这本书还是很不错的。 《Hands-On Machine Learning with Scikit-Learn and TensorFlow 》,这是一门以 TensorFlow 为工具的入门书,其用丰富的例子从实站的角度来让你学习。这本书对于无基础的人也是适合的,对于小白来说虽然略难但是受益匪浅。

相关文章

除了上述的那些课程和图书外,下面这些文章也很不错。

YouTube 上的 Google Developers 的 Machine Learning Recipes with Josh Gordon ,这 9 集视频,每集不到 10 分钟,从 Hello World 讲到如何使用 TensorFlow,非常值得一看。还有 Practical Machine Learning Tutorial with Python Introduction 上面一系列的用 Python 带着你玩 Machine Learning 的教程。Medium 上的 Machine Learning - 101 ,讲述了好些我们上面提到过的经典算法。Medium 上的 Marchine Learning for Humans。Dr. Jason Brownlee 的博客 ,也非常值得一读,其中好多的 “How-To”,会让你有很多的收获。Rules of Machine Learning: Best Practices for ML Engineering ,一些机器学习相关的最佳实践。i am trask ,也是一个很不错的博客。关于 Deep Learning 中的神经网络,YouTube 上有介绍视频 Neural Networks。麻省理工学院的电子书 Deep Learning。用 Python 做自然语言处理Natural Language Processing with Python。最后一个是 Machine Learning 和 Deep Learning 的相关教程列表,Machine Learning & Deep Learning Tutorials。

下面是一些和神经网络相关的不错的文章。

The Unreasonable Effectiveness of Recurrent Neural Networks ,这是一篇必读的文章 ,告诉你为什么要学 RNN,以及展示了最简单的 NLP 形式。Neural Networks, Manifolds, and Topology ,这篇文章可以帮助你理解神经网络的一些概念。Understanding LSTM Networks ,解释了什么是 LSTM 的内在工作原理。Attention and Augmented Recurrent Neural Networks ,用了好多图来说明了 RNN 的 attention 机制。Recommending music on Spotify with deep learning ,一个在 Spotify 的实习生分享的音乐聚类的文章。

相关算法

下面是 10 个非常经典的机器学习的算法。

对于监督式学习,有如下经典算法。

决策树(Decision Tree),比如自动化放贷、风控。朴素贝叶斯分类器(Naive Bayesian classifier),可以用于判断垃圾邮件、对新闻的类别进行分类,比如科技、政治、运动、判断文本表达的感情是积极的还是消极的、人脸识别等。最小二乘法(Ordinary Least Squares Regression),是一种线性回归。逻辑回归(Logisitic Regression),一种强大的统计学方法,可以用一个或多个变量来表示一个二项式结果。可以用于信用评分,计算营销活动的成功率,预测某个产品的收入。支持向量机(Support Vector Machine,SVM),可以用于基于图像的性别检测、图像分类等。集成方法(Ensemble methods),通过构建一组分类器,然后通过它们的预测结果进行加权投票来对新的数据点进行分类。原始的集成方法是贝叶斯平均,但最近的算法包括纠错输出编码、Bagging 和 Boosting。 对于无监督式的学习,有如下经典算法。

聚类算法(Clustering Algorithms)。聚类算法有很多,目标是给数据分类。有 5 个比较著名的聚类算法你必需要知道:K-Means、Mean-Shift、DBSCAN、EM/GMM、和 Agglomerative Hierarchical。主成分分析(Principal Component Analysis,PCA)。PCA 的一些应用包括压缩、简化数据便于学习、可视化等。奇异值分解(Singular Value Decomposition,SVD)。实际上,PCA 是 SVD 的一个简单应用。在计算机视觉中,第一个人脸识别算法使用 PCA 和 SVD 来将面部表示为"特征面"的线性组合,进行降维,然后通过简单的方法将面部匹配到身份。虽然现代方法更复杂,但很多方面仍然依赖于类似的技术。独立成分分析(Independent Component Analysis,ICA)。ICA 是一种统计技术,主要用于揭示随机变量、测量值或信号集中的隐藏因素。

如果你想了解更全的机器学习的算法列表,你可以看一下 Wikipedia 上的 List of Machine Learning Algorithms。

在 A Tour of Machine Learning Algorithms ,这篇文章带你概览了一些机器学习算法,其中还有一个"脑图"可以下载,并还有一些 How-To 的文章供你参考。

对于这些算法,SciKit-Learn有一些文档供你学习。

1. Supervised learning2.3 Clustering2.5. Decomposing signals in components (matrix factorization problems)3. Model selection and evaluation4.3. Preprocessing data

相关资源

对于初学者来说,动手是非常非常重要的,不然,你会在理论的知识里迷失掉自己,这里有篇文章"8 Fun Machine Learning Projects for Beginners",其中为初学者准备了 8 个很有趣的项目,你可以跟着练练。学习机器学习或是人工智能你需要数据,这里有一个非常足的列表给你足够多的公共数据 – 《Awesome Public Datasets》,其中包括农业、生物、天气、计算机网络、地球科学、经济、教育、金融、能源、政府、健康、自然语言、体育等。GitHub 上的一些 Awesome 资源列表。

Awesome Deep LearningAwesome - Most Cited Deep Learning PapersAwesome Deep learning papers and other resources

小结

总结一下今天的内容。我首先介绍了机器学习的基本原理:监督式学习和非监督式学习,然后给出了全世界最简单的入门资料 Machine Learning is Fun!。随后给出了与机器学习密切相关的数据分析方面的内容和资料,然后推荐了深入学习机器学习知识的在线课程、图书和文章等,尤其列举了神经网络方面的学习资料。最后描述了机器学习的十大经典算法及相关的学习资料。

在机器学习和人工智能领域,我也在学习,也处于入门阶段,所以本文中推荐的内容,可能在你看来会有些浅。如果你有更好的信息和资料,欢迎补充。目前文章中给出来的是,我在学习过程中认为很不错的内容,我从中受益良多,所以希望它们也能为你的学习提供帮助。

前端基础和底层原理(前端方向)

对于前端的学习和提高,我的基本思路是这样的。首先,前端的三个最基本的东西 HTML 5、CSS 3 和 JavaScript(ES6)是必须要学好的。这其中有很多很多的技术,比如,CSS 3 引申出来的 Canvas(位图)、SVG(矢量图) 和 WebGL(3D 图),以及 CSS 的各种图形变换可以让你做出非常丰富的渲染效果和动画效果。

ES6 简直就是把 JavaScript 带到了一个新的台阶,JavaScript 语言的强大,大大释放了前端开发人员的生产力,让前端得以开发更为复杂的代码和程序,于是像 React 和 Vue 这样的框架开始成为前端编程的不二之选。

我一直认为学习任何知识都要从基础出发,所以这篇文章我会着重介绍基础知识和基本原理,尤其是如下的这些知识,都是前端程序员需要花力气啃下来的硬骨头。

JavaScript 的核心原理。这里我会给出好些网上很不错的讲 JavaScript 的原理的文章或图书,你一定要学好语言的特性,并且详细了解其中的各种坑。浏览器的工作原理。这也是一块硬骨头,我觉得这是前端程序员需要了解和明白的关键知识点,不然,你将无法深入下去。网络协议 HTTP。也是要着重了解的,尤其是 HTTP/2,还有 HTTP 的几种请求方式:短连接、长连接、Stream 连接、WebSocket 连接。前端性能调优。有了以上的这些基础后,你就可以进入前端性能调优的主题了,我相信你可以很容易上手各种性能调优技术的。框架学习。我只给了 React 和 Vue 两个框架。就这两个框架来说,Virtual DOM 技术是其底层技术,组件化是其思想,管理组件的状态是其重点。而对于 React 来说,函数式编程又是其编程思想,所以,这些基础技术都是你需要好好研究和学习的。UI 设计。设计也是前端需要做的一个事,比如像 Google 的 Material UI,或是比较流行的 Atomic Design 等应该是前端工程师需要学习的。

而对于工具类的东西,这里我基本没怎么涉及,因为本文主要还是从原理和基础入手。那些工具我觉得都很简单,就像学习 Java 我没有让你去学习 Maven 一样,因为只要你去动手了,这种知识你自然就会获得,我们还是把精力重点放在更重要的地方。

下面我们从前端基础和底层原理开始讲起。先来讲讲 HTML5 相关的内容。

HTML 5

HTML 5 主要有以下几本书推荐。

HTML 5 权威指南 ,本书面向初学者和中等水平 Web 开发人员,是牢固掌握 HTML 5、CSS 3 和 JavaScript 的必读之作。书看起来比较厚,是因为里面的代码很多。HTML 5 Canvas 核心技术 ,如果你要做 HTML 5 游戏的话,这本书必读。

对于 SVG、Canvas 和 WebGL 这三个对应于矢量图、位图和 3D 图的渲染来说,给前端开发带来了重武器,很多 HTML5 小游戏也因此蓬勃发展。所以,你可以学习一下。

学习这三个技术,我个人觉得最好的地方是 MDN。

SVG: Scalable Vector GraphicsCanvas APIThe WebGL API: 2D and 3D graphics for the web

最后是几个资源列表。

Awesome HTML5 。GitHub 上的 Awesome HTML5,其中有大量的资源和技术文章。Awesome SVGAwesome CanvasAwesome WebGL

CSS

在《程序员练级攻略》系列文章最开始,我们就推荐过 CSS 的在线学习文档,这里再推荐一下 MDN Web Doc - CSS 。我个人觉得只要你仔细读一下文档,CSS 并不难学。绝大多数觉得难的,一方面是文档没读透,另一方面是浏览器支持的标准不一致。所以,学好 CSS 最关键的还是要仔细地读文档。

之后,在写 CSS 的时候,你会发现,你的 CSS 中有很多看起来相似的东西。你的 DRY - Don’t Repeat Yourself 洁癖告诉你,这是不对的。所以,你需要学会使用 LESS 和 SaSS 这两个 CSS 预处理工具,其可以帮你提高很多效率。

然后,你需要学习一下 CSS 的书写规范,前面的《程序员修养》一文中提到过一些,这里再补充几个。

Principles of writing consistent, idiomatic CSSOpinionated CSS styleguide for scalable applicationsGoogle HTML/CSS Style Guide

如果你需要更有效率,那么你还需要使用一些 CSS Framework,其中最著名的就是 Twitter 公司的 Bootstrap,其有很多不错的 UI 组件,页面布局方案,可以让你非常方便也非常快速地开发页面。除此之外,还有,主打清新 UI 的 Semantic UI 、主打响应式界面的 Foundation 和基于 Flexbox 的 Bulma。

当然,在使用 CSS 之前,你需要把你浏览器中的一些 HTML 标签给标准化掉。所以,推荐几个 Reset 或标准化的 CSS 库:Normalize、MiniRest.css、sanitize.css 和 unstyle.css。

关于更多的 CSS 框架,你可以参看Awesome CSS Frameworks 上的列表。

接下来,是几个公司的 CSS 相关实践,供你参考。

CodePen’s CSSGithub 的 CSSMedium’s CSS is actually pretty f***ing goodCSS at BBC SportRefining The Way We Structure Our CSS At Trello

最后是一个可以写出可扩展的 CSS 的阅读列表 A Scalable CSS Reading List 。

JavaScript

下面是学习 JavaScript 的一些图书和文章。

JavaScript: The Good Parts ,中文翻译版为《JavaScript 语言精粹》。这是一本介绍 JavaScript 语言本质的权威图书,值得任何正在或准备从事 JavaScript 开发的人阅读,并且需要反复阅读。学习、理解、实践大师的思想,我们才可能站在巨人的肩上,才有机会超越大师,这本书就是开始。Secrets of the JavaScript Ninja ,中文翻译版为《JavaScript 忍者秘籍》,本书是 jQuery 库创始人编写的一本深入剖析 JavaScript 语言的书。适合具备一定 JavaScript 基础知识的读者阅读,也适合从事程序设计工作并想要深入探索 JavaScript 语言的读者阅读。这本书有很多晦涩难懂的地方,需要仔细阅读,反复琢磨。Effective JavaScript ,Ecma 的 JavaScript 标准化委员会著名专家撰写,作者凭借多年标准化委员会工作和实践经验,深刻辨析 JavaScript 的内部运作机制、特性、陷阱和编程最佳实践,将它们高度浓缩为极具实践指导意义的 68 条精华建议。接下来是 ES6 的学习,这里给三个学习手册源。

ES6 in Depth,InfoQ 上有相关的中文版 - ES6 深入浅出。还可以看看 A simple interactive ES6 Feature list ,或是看一下 阮一峰翻译的 ES6 的教程 。ECMAScript 6 Tools ,这是一堆 ES6 工具的列表,可以帮助你提高开发效率。Modern JS Cheatsheet ,这个 Cheatsheet 在 GitHub 上有 1 万 6 千颗星,你就可见其影响力了。 然后,还有一组很不错的《You Don’t Know JS 系列》 的书。

[You Don’t Know JS: “Up & Going”](https://github.com/getify/You-Dont-Know-JS/blob/master/up & going/README.md#you-dont-know-js-up–going)[You Don’t Know JS: “Scope & Closures”](https://github.com/getify/You-Dont-Know-JS/blob/master/scope & closures/README.md#you-dont-know-js-scope–closures)[You Don’t Know JS: “this & Object Prototypes”](https://github.com/getify/You-Dont-Know-JS/blob/master/this & object prototypes/README.md#you-dont-know-js-this–object-prototypes)[You Don’t Know JS: “Types & Grammar”](https://github.com/getify/You-Dont-Know-JS/blob/master/types & grammar/README.md#you-dont-know-js-types–grammar)[You Don’t Know JS: “Async & Performance”](https://github.com/getify/You-Dont-Know-JS/blob/master/async & performance/README.md#you-dont-know-js-async–performance)[You Don’t Know JS: “ES6 & Beyond”](https://github.com/getify/You-Dont-Know-JS/blob/master/es6 & beyond/README.md#you-dont-know-js-es6–beyond) 接下来是一些和编程范式相关的文章。

Glossary of Modern JavaScript Concepts: Part 1 ,首先推荐这篇文章,其中收集了一些编程范式方面的内容,比如纯函数、状态、可变性和不可变性、指令型语言和声明式语言、函数式编程、响应式编程、函数式响应编程。Glossary of Modern JavaScript Concepts: Part 2 ,在第二部分中主要讨论了作用域和闭包,数据流,变更检测,组件化…… 下面三篇文章是德米特里·索什尼科夫(Dmitry Soshnikov)个人网站上三篇讲 JavaScript 内在的文章。

JavaScript. The Core: 2nd EditionJavaScript. The Core (older ES3 version)JS scope: static, dynamic, and runtime-augmented “How JavaScript Works” 是一组非常不错的文章(可能还没有写完),强烈推荐。这一系列的文章是 SessionStake 的 CEO 写的,现在有 13 篇,我感觉可能还没有写完。这个叫 亚历山大·兹拉特科夫(Alexander Zlatkov) 的 CEO 太猛了。

An overview of the engine, the runtime, and the call stackInside the V8 engine + 5 tips on how to write optimized code ,了解 V8 引擎。这里,也推荐 Understanding V8’s Bytecode 这篇文章可以让你了解 V8 引擎的底层字节码。Memory management + how to handle 4 common memory leaks ,内存管理和 4 种常见的内存泄露问题。Event loop and the rise of Async programming + 5 ways to better coding with async/await ,Event Loop 和异步编程。Deep dive into WebSockets and HTTP/2 with SSE + how to pick the right path ,WebSocket 和 HTTP/2。A comparison with WebAssembly + why in certain cases it’s better to use it over JavaScript ,JavaScript 内在原理。The building blocks of Web Workers + 5 cases when you should use them ,Web Workers 技术。Service Workers, their lifecycle and use cases ,Service Worker 技术。The mechanics of Web Push Notifications ,Web 端 Push 通知技术。Tracking changes in the DOM using MutationObserver ,Mutation Observer 技术。The rendering engine and tips to optimize its performance ,渲染引擎和性能优化。Inside the Networking Layer + How to Optimize Its Performance and Security ,网络性能和安全相关。Under the hood of CSS and JS animations + how to optimize their performance ,CSS 和 JavaScript 动画性能优化。 接下来是 Google Chrome 工程经理 阿迪·奥斯马尼(Addy Osmani) 的几篇 JavaScript 性能相关的文章,也是非常好的。

The Cost Of JavaScriptJavaScript Start-up Performance 其它与 JavaScript 相关的资源。

JavScript has Unicode Problem ,这是一篇很有价值的 JavaScript 处理 Unicode 的文章。JavaScript Algorithms ,用 JavaScript 实现的各种基础算法库。JavaScript 30 秒代码 ,一堆你可以在 30 秒内看懂各种有用的 JavaScript 的代码,在 GitHub 上有 2 万颗星了。What the f*ck JavaScript ,一堆 JavaScript 搞笑和比较 tricky 的样例。Airbnb JavaScript Style Guide ,Airbnb 的 JavaScript 的代码规范,GitHub 上有 7 万多颗星。JavaScript Patterns for 2017 ,YouTube 上的一个 JavaScript 模式分享,值得一看。

浏览器原理

你需要了解一下浏览器是怎么工作的,所以,你必需要看《How browsers work》。这篇文章受众之大,后来被人重新整理并发布为《How Browsers Work: Behind the scenes of modern web browsers》,其中还包括中文版。这篇文章非常非常长,所以,你要有耐心看完。如果你想看个精简版的,可以看我在 Coolshell 上发的《浏览器的渲染原理简介》或是看一下这个幻灯片。

然后,是对 Virtual DOM 的学习。Virtual DOM 是 React 的一个非常核心的技术细节,它也是前端渲染和性能的关键技术。所以,你有必要要好好学习一下这个技术的实现原理和算法。当然,前提条件是你需要学习过前面我所推荐过的浏览器的工作原理。下面是一些不错的文章可以帮你学习这一技术。

How to write your own Virtual DOMWrite your Virtual DOM 2: Props & EventsHow Virtual-DOM and diffing works in ReactThe Inner Workings Of Virtual DOM深度剖析:如何实现一个 Virtual DOM 算法以及两个 Vitual-DOM 实现供你参考:

Matt-Esch/Virtual-DOMMaquette

网络协议

High Performance Browser Networking ,本书是谷歌公司高性能团队核心成员的权威之作,堪称实战经验与规范解读完美结合的产物。本书目标是涵盖 Web 开发者技术体系中应该掌握的所有网络及性能优化知识。 全书以性能优化为主线,从 TCP、UDP 和 TLS 协议讲起,解释了如何针对这几种协议和基础设施来优化应用。然后深入探讨了无线和移动网络的工作机制。最后,揭示了 HTTP 协议的底层细节,同时详细介绍了 HTTP 2.0、 XHR、SSE、WebSocket、WebRTC 和 DataChannel 等现代浏览器新增的能力。 另外,HTTP/2也是 HTTP 的一个新的协议,于 2015 年被批准通过,现在基本上所有的主流浏览器都默认启用这个协议。所以,你有必要学习一下这个协议。下面相关的学习资源。

Gitbook - HTTP/2 详解http2 explained(中译版)HTTP/2 for a Faster WebNginx HTTP/2 白皮书HTTP/2 的两个 RFC:

RFC 7540 - Hypertext Transfer Protocol Version 2 (HTTP/2) ,HTTP/2 的协议本身。RFC 7541 - HPACK: Header Compression for HTTP/2 ,HTTP/2 的压缩算法。 新的 HTML5 支持 WebSocket,所以,这也是你要学的一个重要协议。

HTML5 WebSocket: A Quantum Leap in Scalability for the Web ,这篇文章比较了 HTTP 的几种链接方式,Polling、Long Polling 和 Streaming,并引入了终级解决方案 WebSocket。你知道的,了解一个技术的缘由是非常重要的。StackOverflow: My Understanding of HTTP Polling, Long Polling, HTTP Streaming and WebSockets ,这是 StackOverflow 上的一个 HTTP 各种链接方式的比较,也可以让你有所认识。An introduction to Websockets ,一个 WebSocket 的简单教程。Awesome Websockets ,GitHub 的 Awesome 资源列表。一些和 WebSocket 相关的想法,可以开阔你的思路:

Introducing WebSockets: Bringing Sockets to the WebWebsockets 101Real-Time Web by Paul BanksAre WebSockets the future?

小结

总结一下今天的内容。我一直认为学习任何知识都要从基础出发,所以今天我主要讲述了 HTML 5、CSS 3 和 JavaScript(ES6)这三大基础核心,给出了大量的图书、文章以及其他一些相关的学习资源。之后,我建议你学习浏览器的工作原理和网络协议相关的内容。我认为,掌握这些原理也是学好前端知识的前提和基础。值得花时间,好好学习消化。

前端性能优化和框架(前端方向)

首先是推荐几本前端性能优化方面的图书。

Web Performance in Action ,这本书目前国内没有卖的。你可以看电子版本,我觉得是一本很不错的书,其中有 CSS、图片、字体、JavaScript 性能调优等。 Designing for Performance ,这本在线的电子书很不错,其中讲了很多网页优化的技术和相关的工具,可以让你对整体网页性能优化有所了解。 High Performance JavaScript ,这本书在国内可以买到,能让你了解如何提升各方面的性能,包括代码的加载、运行、DOM 交互、页面生存周期等。雅虎的前端工程师尼古拉斯·扎卡斯(Nicholas C. Zakas)和其他五位 JavaScript 专家介绍了页面代码加载的最佳方法和编程技巧,来帮助你编写更为高效和快速的代码。你还会了解到构建和部署文件到生产环境的最佳实践,以及有助于定位线上问题的工具。 High Performance Web Sites: Essential Knowledge for Front-End Engineers ,这本书国内也有卖,翻译版为《高性能网站建设指南:前端工程师技能精髓》。作者给出了 14 条具体的优化原则,每一条原则都配以范例佐证,并提供了在线支持。 全书内容丰富,主要包括减少 HTTP 请求、Edge Computing 技术、Expires Header 技术、gzip 组件、CSS 和 JavaScript 最佳实践、主页内联、Domain 最小化、JavaScript 优化、避免重定向的技巧、删除重复 JavaScript 的技巧、关闭 ETags 的技巧、Ajax 缓存技术和最小化技术等。 除了上面这几本书之外,Google 的 Web Fundamentals 里的 Performance 这一章节也有很多非常不错的知识和经验。

接下来是一些最佳实践性的文档。

Browser Diet ,前端权威性能指南(中文版)。这是一群为大型站点工作的专家们建立的一份前端性能的工作指南。PageSpeed Insights Rules ,谷歌给的一份性能指南和最佳实践。Best Practices for Speeding Up Your Web Site ,雅虎公司给的一份 7 个分类共 35 个最佳实践的文档。

接下来,重点推荐一个性能优化的案例学习网站 WPO Stats 。WPO 是 Web Performance Optimization 的缩写,这个网站上有很多很不错的性能优化的案例分享,一定可以帮助你很多。

然后是一些文章和案例。

A Simple Performance Comparison of HTTPS, SPDY and HTTP/2 ,这是一篇比较浏览器的 HTTPS、SPDY 和 HTTP/2 性能的文章,除了比较之外,还可以让你了解一些技术细节。7 Tips for Faster HTTP/2 Performance ,对于 HTTP/2 来说,Nginx 公司给出的 7 个增加其性能的小提示。Reducing Slack’s memory footprint ,Slack 团队减少内存使用量的实践。Pinterest: Driving user growth with performance improvements ,Pinterest 关于性能调优的一些分享,其中包括了前后端的一些性能调优实践。其实也是一些比较通用的玩法,这篇文章主要是想让前端的同学了解一下如何做整体的性能调优。10 JavaScript Performance Boosting Tips ,10 个提高 JavaScript 运行效率的小提示,挺有用的。17 Statistics to Sell Web Performance Optimization ,这个网页上收集了好些公司的 Web 性能优化的工程分享,都是非常有价值的。Getting started with the Picture Element ,这篇文章讲述了 Responsive 布局所带来的一些负面的问题。主要是图像适配的问题,其中引出了一篇文章"Native Responsive Images" ,值得一读。Improve Page Load Times With DNS Prefetching ,这篇文章教了你一个如何降低 DNS 解析时间的小技术——DNS prefetching。Jank Busting for Better Rendering Performance ,这是一篇 Google I/O 上的分享,关于前端动画渲染性能提升。JavaScript Memory Profiling ,这是一篇谷歌官方教你如何使用 Chrome 的开发工具来分析 JavaScript 内存问题的文章。

接下来是一些性能工具。在线性能测试分析工具太多,这里只推荐比较权威的。

PageSpeed ,谷歌有一组 PageSpeed 工具来帮助你分析和优化网站的性能。Google 出品的,质量相当有保证。YSlow ,雅虎的一个网页分析工具。GTmetrix ,是一个将 PageSpeed 和 YSlow 合并起来的一个网页分析工具,并且加上一些 Page load 或是其它的一些分析。也是一个很不错的分析工具。Awesome WPO ,在 GitHub 上的这个 Awesome 中,你可以找到更多的性能优化工具和资源。

另外,中国的网络有各种问题(你懂的),所以,你不能使用 Google 共享的 JavaScript 链接来提速,你得用中国自己的。你可以到这里看看中国的共享库资源,Forget Google and Use These Hosted JavaScript Libraries in China 。

前端框架

接下来,要学习的是 Web 前端的几大框架。目前而言,前端社区有三大框架 Angular.js、React.js 和 Vue.js。我认为,React 和 Vue 更为强劲一些,所以,我这里只写和 React 和 Vue 相关的攻略。关于两者的比较,网上有好多文章。我这里推荐几篇我觉得还不错的,供你参考。

Angular vs. React vs. Vue: A 2017 comparisonReact or Vue: Which JavaScript UI Library Should You Be Using?ReactJS vs Angular5 vs Vue.js?-?What to choose in 2018?

其实,比较这些框架的优缺点还有利弊并不是要比出个输赢,而是让你了解一下不同框架的优缺点。我觉得,这些框架都是可以学习的。而在我们生活工作中具体要用哪个框架,最好还是要有一些出发点,比如,你是为了找份好的工作,为了快速地搭一个网站,为了改造一个大规模的前端系统,还是纯粹地为了学习……

不同的目的会导致不同的决定。我并不希望上述的这些比较会让你进入“二选一”或是“三选一”的境地。我只是想通过这些文章让你知道这些框架的设计思路和实现原理,这些才是让你受益一辈子的事。

React.js 框架

下面先来学习一下 React.js 框架。

入门

React 学起来并不复杂,就看 React 官方教程 和其文档就好了( React 的中文教程 )。

然后,下面的文章会带你了解一下 React.js 的基本原理。

All the fundamental React.js concepts ,这篇文章讲了所有的 React.js 的基本原理。Learn React Fundamentals and Advanced Patterns ,这篇文章中有几个短视频,每个视频不超过 5 分钟,是学习 React 的一个很不错的地方。Thinking in React,这篇文章将引导你完成使用 React 构建可搜索产品数据表的思考过程。

提高

学习一个技术最重要的是要学到其中的思想和方法。下面是一些我觉得学习 React 中最重要的东西。

状态,对于富客户端来说是非常麻烦也是坑最多的地方,这里有几篇文章你可以一读。

Common React.js mistakes: Unneeded state ,React.js 编程的常见错误——不必要的状态。State is an Anti-Pattern ,关于如何做一个不错的组件的思考,很有帮助。Why Local Component State is a Trap ,一些关于 “Single state tree” 的想法。Thinking Statefully ,几个很不错的例子让你对声明式有状态的技术有更好的理解。传统上,解决 React 的状态问题一般用 Redux。在这里推荐 Tips to learn React + Redux in 2018 。Redux 是一个状态粘合组件,一般来说,我们会用 Redux 来做一些数据状态和其上层 Component 上的同步。这篇教程很不错。最后是 "State Architecture Patterns in React " 系列文章,非常值得一读。

Part 1: A ReviewPart 2: The Top-Heavy Architecture, Flux and PerformancePart 3: Articulation Points, zine and An Overall StrategyPart 4: Purity, Flux-duality and Dataflow 函数式编程。从 jQuery 过来的同学一定非常不习惯 React,而从 Java 等后端过来的程序员就会很习惯了。所以,我觉得 React 就是后端人员开发的,或者说是做函数式编程的人开发的。对此,你需要学习一下 JavaScript 函数式编程的东西。 这里推荐一本免费的电子书 《Professor Frisby’s Mostly Adequate Guide to Functional Programming》,其中译版为《JS 函数式编程指南中文版》。 下面有几篇文章非常不错。前两篇和函数式编程有关的文章非常值得一读。后三篇是一些比较实用的函数式编程和 React 结合的文章。

Master the JavaScript Interview: What is Functional Programming?The Rise and Fall and Rise of Functional Programming (Composing Software)Functional UI and Components as Higher Order FunctionsFunctional JavaScript: Reverse-Engineering the HypeSome Thoughts on Function Components in React 设计相关。接下来是学习一些 React 的设计模式。React Pattern 是一个不错的学习 React 模式的地方。除此之外,还有如下的一些不错的文章也会对你很有帮助的。

React Higher Order Components in depthPresentational and Container ComponentsControlled and uncontrolled form inputs in React don’t have to be complicatedFunction as Child ComponentsWriting Scalable React Apps with the Component Folder PatternReusable Web Application StrategiesCharacteristics of an Ideal React Architecture 实践和经验

还有一些不错的实践和经验。

9 things every React.js beginner should knowBest practices for building large React applicationsClean Code vs. Dirty Code: React Best PracticesHow to become a more productive React Developer8 Key React Component Decisions

资源列表

最后就是 React 的资源列表。

Awesome React ,这是一些 React 相关资源的列表,很大很全。React/Redux Links ,这也是 React 相关的资源列表,与上面不一样的是,这个列表主要收集了大量的文章,其中讲述了很多 React 知识和技术,比上面的列表好很多。React Rocks ,这个网站主要收集各种 React 的组件示例,可以让你大开眼界。

Vue.js 框架

Vue 可能是一个更符合前端工程师习惯的框架。不像 React.js 那样使用函数式编程方式,是后端程序员的思路。

通过文章 “Why 43% of Front-End Developers want to learn Vue.js” ,你可以看出其编程方式和 React 是大相径庭的,符合传统的前端开发的思维方式。通过文章 Replacing jQuery With Vue.js: No Build Step Necessary ,我们可以看到,从 jQuery 是可以平滑过渡到 Vue 的。另外,我们可以通过 “10 things I love about Vue” ,了解 Vue 的一些比较优秀的特性。

最令人高兴的是,Vue 的作者是我的好朋友尤雨溪(Evan You),最近一次对他的采访 “Vue on 2018?-?Interview with Evan You” 当中有很多故事以及对 Vue 的展望。(注意:Vue 是完全由其支持者和用户资助的,这意味着它更接近社区而不受大公司的控制。)

要学习 Vue 并不难,我认为上官网看文档( Vue 官方文档(中文版)),照着搞一搞就可以很快上手了。Vue.js screencasts 是一个很不错的英文视频教程。

另外,推荐 新手向:Vue 2.0 的建议学习顺序 ,这是 Vue 作者写的,所以有特殊意义。

Vue 的确比较简单,有 Web 开发经验的人上手也比较快,所以这里也不会像 React 那样给出很多的资料。下面是一些我觉得还不错的内容,推荐给你。

How not to Vue ,任何技术都有坑,了解 Vue 的短板,你就能扬长避短,就能用得更好。Vue.js Component Communication Patterns4 AJAX Patterns For Vue.js AppsHow To (Safely) Use A jQuery Plugin With Vue.js7 Ways To Define A Component Template in Vue.jsUse Any Javascript Library With Vue.jsDynamic and async components made easy with Vue.js

当然,最后一定还有 Awesome Vue ,Vue.js 里最为巨大最为优秀的资源列表。

小结

总结一下今天的内容。我先介绍的是前端性能优化方面的内容,推荐了图书、最佳实践性的文档、案例,以及一些在线性能测试分析工具。随后重点讲述了 React 和 Vue 两大前端框架,给出了大量的文章、教程和相关资源列表。我认为,React.js 使用函数式编程方式,更加符合后端程序员的思路,而 Vue 是更符合前端工程师习惯的框架。因此,两者比较起来,Vue 会更容易上手一些。

UI/UX 设计(前端方向)

上面的技术都讲完了,前端还有一个很重要的事就是设计。作为前端人员,我们有必要了解现在的一些知名且流行的设计语言或是一些设计规范或是设计方法,学习它们的设计思想和方法,有助于我们拓宽眼界、与时俱进。我并不觉得这些内容是设计师要学习的,如果你要成为一个前端程序员,那么学习这些设计上的东西可以让你有更好的成长空间。

对于学习设计的新手来说,推荐看看 7 steps to become a UI/UX designer ,这是一篇很不错的让新手入门的文章,非常具有指导性。首先,你得开始学习设计的一些原则和套路,如配色、平衡、排版、一致性等。还有用户体验的 4D 步骤——Discover、Define、Develop 和 Delivery。然后,开始到一些网站上找灵感。接下来,是到不同的网站上读各种文章和资源,开始学习使用设计工具,最后是找人拜师。此外,其中还链接了其它一些不错的文章、网站、博客和工具。我认为,这篇文章是一篇很不错的设计师从入门到精通的练级攻略。

虽然有这么一个速成的教程,但我觉得还是应该系统地学习一下,所以有了下面这些推荐。

图书和文章推荐

先推荐几本书。

Don’t Make Me Think ,这是我看的第一本和设计相关的书。这本书对我的影响也比较深远。这本书践行了自己的理论,整本书短小精悍,语言轻松诙谐,书中穿插大量色彩丰富的屏幕截图、趣味丛生的卡通插图以及包含大量信息的图表,使枯燥的设计原理变得平易近人。Simple and Usable Web,Mobile,and Interaction Design ,中文版译名为《简约至上》。本书作者贾尔斯(Giles)有 20 多年交互式设计的探索与实践。提出了合理删除、分层组织、适时隐藏和巧妙转移这四个达成简约至上的终极策略,讲述了为什么应该站在主流用户一边,以及如何从他们的真实需求和期望出发,简化设计,提升易用性。Designing with the Mind in Mind: Simple Guide to Understanding User Interface Design Rules ,中文版译名为《认知与设计:理解 UI 设计准则》。这本书语言清晰明了,将设计准则与其核心的认知学和感知科学高度统一起来,使得设计准则更容易在具体环境中得到应用。涵盖了交互计算机系统设计的方方面面,为交互系统设计提供了支持工程方法。不仅如此,这也是一本人类行为原理的入门书。Designing Interfaces: Patterns for Effective Interaction Design ,中文版译名为《界面设计模式》。这本书开篇即总结了“与人有关”的各类问题,为读者提供了界面设计总体思路上的指引,帮助读者举一反三。然后,收集并分析了很多常用的界面设计模式,帮助读者理解在实现级别的各种常用解决方案,将它们灵活地运用到自己的设计中。

除了上面的这几本书,还有下面的这几篇文章也是很不错的,推荐一读。

The Psychology Principles Every UI/UX Designer Needs to Know ,这篇文章讲述了 6 大用户界面用户体验设计的心理学原则。18 designers predict UI/UX trends for 2018, 我倒不觉得这篇文章中所说的 UI/UX 是在 2018 年的趋势,我反而觉得,这 18 条原则是指导性的思想。The Evolution of UI/UX Designers Into Product Designers ,这篇文章是 Adobe 公司的一篇博客,其在回顾整个产品设计的演化过程中有一些不错的思考和想法,并提供了一些方法论。

原子设计(Atomic Design)

在 2013 年网页设计师布拉德·弗罗斯特(Brad Frost)从化学中受到启发:原子(Atoms)结合在一起,形成分子(Molecules),进一步结合形成生物体(Organisms)。布拉德将这个概念应用在界面设计中,我们的界面就是由一些基本的元素组成的。

乔希·杜克(Josh Duck)的“HTML 元素周期表”完美阐述了我们所有的网站、App、企业内部网、hoobadyboops 等是如何由相同的 HTML 元素组成的。通过在大层面(页)和小层面(原子)同时思考界面,布拉德认为,可以利用原子设计建立一个适应组件的动态系统。

为什么要玩原子设计,我认为,这对程序员来说是非常好理解的,因为这就是代码模块化重用化的体现。于是,你就是要像搭积木一样开发和设计网页,当你把其模块化组件化了,也更容易规范整体的风格,而且容易维护……这些都意味着你可以更容易地维护你的代码。所以,这个方法论导致了 Web 组件化的玩法。这是设计中非常重要的方法论。

关于这个设计方法论,你可以阅读一下下面这几篇文章。

Atomic Design 原子设计┃构建科学规范的设计系统网页设计:Atomic Design 简介及工作实例

但是,真正权威的地方还是布拉德·弗罗斯特的电子书、博客和实验室,可以从中获取更多的信息。

电子书:Atomic Design by Brad Frost 是布拉德·弗罗斯特写的一本书。博 客:Atomic Design 是布拉德·弗罗斯特的博客。实验室:Pattern lab 是布拉德·弗罗斯特依照这个设计系统所建立的一套工具,可以前往 Pattern Lab 的 GitHub 来试试 Atomic design。

接下来是关于这个设计方法和 React.js 框架的几篇文章。

Atomic Design with ReactAtomic Components: Managing Dynamic React Components using Atomic Design

设计语言和设计系统

下面来介绍一下设计语言和设计系统。

Fluent Design System

Fluent Design System 中文翻译为流畅设计体系,是微软于 2017 年开发的设计语言。流畅设计是 Microsoft Design Language 2 的改版,其中包含为所有面向 Windows 10 设备和平台设计的软件中的设计和交互的指导原则。

该体系基于五个关键元素:光感、深度、动效、材质和缩放。新的设计语言包括更多对动效、深度及半透明效果的使用。过渡到流畅设计体系是一个长期项目,没有具体的完成目标,但是从创作者更新以来,新设计语言的元素已被融入到个别应用程序中。它将在未来的 Windows 10 秋季创作者更新中更广泛地使用,但微软也表示,该设计体系不会在秋季创作者更新内完成。

微软于 2017 年 5 月 11 日的 Microsoft Build 2017 开发者大会上公开了该设计体系。

What’s new and coming for Windows UI: XAML and composition ,从概念上讲了一下 Fluent Design System 的各个部分。Introducing Fluent Design ,介绍了 Fluent Design System 的各个部分。

还有 Build 2018 上的一些微软的 YouTube 分享。

Fluent Design: Evolving our Design System : Build 2018Microsoft Build 2018 - Fluent Design System DemoMicrosoft Build 2018 - Fluent Design System EvolutionFluent Design System inside of Microsoft: Office : Build 2018

Material Design

Material Design 中文翻译为质感设计,或是材质设计、材料设计。这是由 Google 开发的设计语言。扩展于 Google Now 的“卡片”设计,Material Design 基于网格的布局、响应动画与过渡、填充、深度效果(如光线和阴影)。设计师马蒂亚斯·杜阿尔特(Matías Duarte)解释说:“与真正的纸张不同,我们的数字材质可以智能地扩大和变形。材质具有实体的表面和边缘。接缝和阴影表明组件的含义。”Google 指出他们的新设计语言基于纸张和油墨。

Material Design 于 2014 年的 Google I/O 大会上发布(参看 Google I/O 2014 - Material witness: How Android material applications work)。其可借助 v7 appcompat 库用于 Android 2.1 及以上版本,几乎支持所有 2009 年以后制造的 Android 设备。随后,Material Design 扩展到 Google 的网络和移动产品阵列,提供一致的跨平台和应用程序体验。Google 还为第三方开发人员发布了 API,开发人员可将质感设计应用到他们的应用程序中。

除了到 官网 学习 Material Design,你还可以访问 Material Design 中文版 来学习。

另外,Wikipedia 上有一张 Material Design 实现的比较表,供你参考。

下面是几个可供你使用的 Material UI 的工程实现。

Material Design Lite ,这是 Google 官方的框架,简单易用。Materialize ,一组类似于 Bootstrap 的前端 UI 框架。Material-UI 是基于 Google Material Design 的 React 组件实现。MUI 是一个轻量级的 CSS 框架,遵循 Google 的 Material Design 设计方针。

其它公司

接下来再来推荐其它几家公司的设计语言。

苹果公司的设计指南,在这个网站有苹果的各种设备的设计规范和指导,一方面可以让你的 App 能和苹果的 UI 融合在一起,另一方面,你也可以从中看到苹果的审美和思维方式。IBM 公司的设计语言 ,我们总觉得 IBM 公司是一家比较传统的没有新意的公司,但是并不是这样的。IBM 公司的这个设计语言的确比较出众。所以,在这里推荐一下。Salesforce 公司的 Lightning Design System ,是在 Salesforce 生态系统中用于创建统一 UI 的设计模式、组件和指南的集合,是一个企业级的产品。Facebook Design - What’s on our mind? ,Facebook 的设计师们收集的一系列的文章、视频和资源。很不错哦。

动画效果设计

我认为,要了解 Web 动画效果设计的第一步,最好的地方是 CodePen。这个网站不只是让人分享 HTML、CSS 和 JavaScript 代码的网站。其中也有很多分享样例都和动画效果有关。这个网站可以让你对动画效果有一些感性认识,当然还有代码供你参考。

接下来,我们要了解动画效果设计的一些方法。基本上来说,动画设计都会受 “动画的 12 项基本法则 ”的影响,这个方法论源自于迪士尼动画师奥利·约翰斯顿(Ollie Johnston)和弗兰克·托马斯(Frank Thomas)在 1981 年所出的《The Illusion of Life: Disney Animation》一书。这些法则已被普遍采用,至今仍与制作 3D 动画法则有关联。这里还有一篇文章 “Understand the 12 principles of animation” 是对这个法则的解读和理解。

除此之外,还有几个动画设计指南和相关文章供你参考和学习。

6 Animation Guidelines for UX Design。这是 Prototypr 公司的一个指南,其中主要指出,动画效果不是为了炫配,而是能让你的 UI/UX 能活起来,自然,不消耗时间,并且是生动故事型的动画效果。其中还推荐了如下几篇很不错的文章。

Transitional InterfacesUI Animation and UX: A Not-So-Secret FriendshipInvisible animationCreating Usability with Motion: The UX in Motion Manifesto Designing Interface Animation ,这篇文章同样说明,任何一个小动画都是要讲一个微故事的,而且这些微故事会和你的品牌和产品理念相融合。动画会给人更深的印象,让人们更容易记住你。这篇文章主要是讲品牌动画。Animation principles in motion design ,这篇文章有点像设计模式,给了一些动画效果的套路和演示。Creating Usability with Motion: The UX in Motion ManifestoIntegrating Animation into a Design SystemGreat UI/UX Animations 是设计师丹尼尔(Daniel)收集的一些很不错的动画,可以给你一些灵感。

Great UI/UX Animations 第一组Great UI/UX Animations 第二组

相关资源

下面分享一下 UI/UX 设计的相关资源。文章资源主要有以下这些。

文章资源

Web Designer News ,一个文章聚合的网站。除此之外,还有两个文章聚合网站,你也可以订阅。一个是Designer News ,另一个是 Reddit Web Design。Marvel Blog ,Marvel 团队的博客。The Next Web ,内容主要涵盖国际技术新闻、商业和文化等多个方面。Medium - Design ,Medium 现在已经成为一个好文章的集散地了,这个地方必去。Smashing Magazine ,这个地方是给专业的 Web 设计师和程序员的。不但有设计还有 HTML、CSS 和 JavaScript 等各种资源。Sitepoint ,这个网站上也有很多不错的给 Web 前端程序员和设计师看的文章(当然,给程序员看的有点简单了,我觉得更像是让设计师来学写程序的网站)。

设计收集

接下来推荐一些优秀设计的聚集地。

Awwwards ,这个网站给一些设计得不错网站的评分,在这里你可以看到很多设计不错的网站。One Page Love ,就是一个单页的网页设计的收集。Inspired UI ,移动 App 的设计模式。Behance,这个地言有很不错的很有创意的作品。Dribbble ,这应该是设计师都知道也都爱去的网站。除了你可以看到一些很不错的作品外,你还可以在这里看到很多不错的设计师。UI Movement ,也是个设计的收集网站,上面有很多很不错的 UI 设计,大量的动画。虽说会像抖音一样,让你不知不觉就看了好几小时,但是它比抖音让你的收获大多了。

小结

总结一下今天的内容。我并不认为 UI/UX 设计这些内容只是设计师要学习的,如果你要成为一个前端程序员,那么学习这些设计上的东西可以让你有更好的成长空间。首先,我推荐了一些图书和文章,让你更好地了解经典的设计原则和指导思想。

然后介绍了原子设计,以及深入学习和理解这一设计方法论的图书、文章和其他相关资源。最后分享了当下主流和知名公司中在用的设计语言和设计系统,并给出了大量的学习资源,推荐了一些优秀设计的聚集地。相信通过学习这些内容,你在 UI/UX 设计方面不仅能收获方法,还能获得非常多的灵感。

技术资源集散地

首先,我先推荐一些不错的个人技术博客。

Coding Horror ,这是杰夫·阿特伍德(Jeff Atwood)于 2004 年创办的博客,记录其在软件开发经历中的所思所想、点点滴滴。时至今日,该博客每天都有近 10 万人次的访问量,读者纷纷参与评论,各种观点与智慧在这里不断地激情碰撞。其博文选集在中国被翻译成《高效能程序员的修练》,在豆瓣上有 8.3 的高分。2008 年,他和 Joel Spolsky 联合创办了 StackOverflow 问答网站,为程序员在开发软件时节省了非常多的时间,并开启了“StackOverflow Copy + Paste 式编程”。Joel on Software ,Joel Spolsky 的这个博客在全世界都有很多的读者和粉丝,其博文选集在中国被翻译成《软件随想录》在豆瓣上有 8.7 的高分。这是一本关于软件技术、人才、创业和企业管理的随想文集,作者以诙谐幽默的笔触将自己在软件行业的亲身感悟娓娓道来,观点新颖独特,简洁实用。Clean Coder Blog ,这是编程大师“Bob 大叔”的博客,其真名叫 Robert C. Martin,世界级软件开发大师,设计模式和敏捷开发先驱,敏捷联盟首任主席,C++ Report 前主编,被后辈程序员尊称为“Bob 大叔”。其博文选集在中国被翻译成《程序员的职业素养》,在豆瓣上有 8.8 的高分。Martin Fowler ,这是另外一个程序员大师,Martin 主要专注于面向对象分析与设计、统一建模语言、领域建模,以及敏捷软件开发方法,包括极限编程。他的《重构》、《分析模式》、《企业应用架构模式》、《领域特定语言》和《NoSQL 精粹》都是非常不错的书。在他的博客上有很多很多的编程和架构模式方法可以学习。Paul Graham Essays ,美国著名程序员、风险投资家、博客和技术作家。《黑客与画家》是他的著作之一。2005 年他与人共同创建了科技创业孵化器 Y Combinator,孵化了 Airbnb、Dropbox、Stripe 等知名互联网公司。他有几篇创业方面的文章都很经典,如果你想创业,可以读一读这几篇:《How to Get Startup Ideas》、《Do Things that Don’t Scale》、《Startup = Growth》。Paul Graham 的文章以清新自然,思想深刻见长。不仅可以跟 Paul Graham 学创业,学思考,学技术,更可以学习写作。Steve Yegge ,Steve Yegge 这个人算是一个知名的程序员了,在 Amazon 呆过,现在在 Google,他的文章都是长篇大论,最知名的文章就是对 Amazon 和 Google 平台的吐槽,这篇文章引发了大家的讨论和议论。Bruce Eckel’s Programming Blog ,《Thinking in Java》作者的博客,他之前的博客在 artima - Computing Thoughts 。Herb Sutter ,C++ 大拿,C++ 标准委员会专家,微软软件架构师。《Exceptional C++ 》、《More Exceptional C++》、《Exceptional C++ Style》作者。Eli Bendersky’s website ,这位老哥从 2003 年就一直写博客到今天,其中的文章都非常不错,原理型的,主要是 C、C++ 和 Python 相关的。里面有很多干货。Peter Krumins’ blog ,这位老哥从 2007 年开始写博客,他博客里好玩的东西太多了。Brendan D. Gregg ,Brendan 是 Netflix 的工程师,他的博客里有大量的非常不错的文章,基本上都是和 Linux 性能分析相关的,这是一个如果你要玩底层性能分析一定不能错过的博客。Evan Klitzke ,主要讨论 Linux 和 C++ 相关的内容。Julia Evans ,主要讨论 Linux debug 工具和网络相关的内容。null program ,和 C/C++ 相关的一个博客。其中关于 Linux 系统调用、GPU、无锁编程、JIT 编译的一些文章非常不错。Fluent {C++} ,博主是 Murex 的首席工程师,主要玩 C++,在这个博客里有很多很不错的 C++ 相关的文章。Preshing on Programming ,这也是一个和 C/C++ 相关的博客,其中有很多的干货。Programming is Terrible ,这个博客有很多强观点的文章,主要是软件开发中的一些教训。Accidentally Quadratic ,姑且翻译成事故二次方,这里有好些非常有趣的文章。Hacker Noon ,这是一个一堆人在写的博客,里面有很多质量很高的文章。

其实还有很多不错的博客,不过,现在国外不错的博客都在一个叫 Medium 的网站,我也发现我 Google 很多东西时都会到这个网站上。这个网站上的内容不只有技术的,还有很多很多其他方面的内容,比如文化、艺术、科学等等。这个网站就是一个博客发布系统,其是由 Twitter 联合创始人埃文·克拉克·威廉姆斯(Evan Clark Williams)和克里斯多福·艾萨克·比兹·斯通(Christopher Isaac Biz Stone)创办的,这两个人觉得 Twitter 上全是垃圾没有营养的信息。所以,创办了 Medium,这个平台上有专业和非专业的贡献者,亦有受雇的编者。

我已经感觉到,未来高质量的文章都会在 Medium 这个平台上出现,因为有一些公司的技术博客也在这个平台上发布了,比如 Netflix 的。所以,你有必要上到这个平台上 follow 一些作者、专栏和主题。

YouTube 技术频道

下面是我订阅的一些我认为还不错的和编程相关的频道,推荐给你。

Devoxx ,Devoxx 的频道,其中有各种很不错的技术分享。Coding Tech ,也是个非常不错的编程频道,涵盖各种技术。[Amazon Web Services](https://www.youtube.com/channel/UC

ins可以看到别人几分钟前在线吗,BUUCTF Misc Page2-6部分题目 (2024)
Top Articles
OHLottery Pick 3 Evening Smart Picks
OHLottery MEGA Millions Smart Picks
Foxy Roxxie Coomer
Fat Hog Prices Today
El Paso Pet Craigslist
Tyson Employee Paperless
Arkansas Gazette Sudoku
Craigslist Cars And Trucks For Sale By Owner Indianapolis
Aadya Bazaar
Senior Tax Analyst Vs Master Tax Advisor
Find All Subdomains
Us 25 Yard Sale Map
Mustangps.instructure
Lesson 1 Homework 5.5 Answer Key
Does Pappadeaux Pay Weekly
Best Restaurants Ventnor
Ivegore Machete Mutolation
Bjork & Zhulkie Funeral Home Obituaries
This Modern World Daily Kos
Northern Whooping Crane Festival highlights conservation and collaboration in Fort Smith, N.W.T. | CBC News
Ostateillustrated Com Message Boards
Haunted Mansion Showtimes Near Millstone 14
Mikayla Campinos Laek: The Rising Star Of Social Media
Music Go Round Music Store
Tu Pulga Online Utah
Conan Exiles Sorcery Guide – How To Learn, Cast & Unlock Spells
Page 2383 – Christianity Today
Xxn Abbreviation List 2017 Pdf
Democrat And Chronicle Obituaries For This Week
Sams Gas Price Sanford Fl
NV Energy issues outage watch for South Carson City, Genoa and Glenbrook
Phoenixdabarbie
Autopsy, Grave Rating, and Corpse Guide in Graveyard Keeper
Ultra Clear Epoxy Instructions
Most popular Indian web series of 2022 (so far) as per IMDb: Rocket Boys, Panchayat, Mai in top 10
20 Best Things to Do in Thousand Oaks, CA - Travel Lens
Adam Bartley Net Worth
Craigslist Florida Trucks
Newsweek Wordle
Nid Lcms
8776725837
About Us
Best Haircut Shop Near Me
This Doctor Was Vilified After Contracting Ebola. Now He Sees History Repeating Itself With Coronavirus
Kaamel Hasaun Wikipedia
Craigslist Marshfield Mo
Craigslist Psl
Unit 4 + 2 - Concrete and Clay: The Complete Recordings 1964-1969 - Album Review
Southwind Village, Southend Village, Southwood Village, Supervision Of Alcohol Sales In Church And Village Halls
Bones And All Showtimes Near Emagine Canton
Adams County 911 Live Incident
Famous Dave's BBQ Catering, BBQ Catering Packages, Handcrafted Catering, Famous Dave's | Famous Dave's BBQ Restaurant
Latest Posts
Article information

Author: Corie Satterfield

Last Updated:

Views: 6349

Rating: 4.1 / 5 (42 voted)

Reviews: 81% of readers found this page helpful

Author information

Name: Corie Satterfield

Birthday: 1992-08-19

Address: 850 Benjamin Bridge, Dickinsonchester, CO 68572-0542

Phone: +26813599986666

Job: Sales Manager

Hobby: Table tennis, Soapmaking, Flower arranging, amateur radio, Rock climbing, scrapbook, Horseback riding

Introduction: My name is Corie Satterfield, I am a fancy, perfect, spotless, quaint, fantastic, funny, lucky person who loves writing and wants to share my knowledge and understanding with you.