逆向手在夹缝中艰难求生系列。

这篇真的存粹是做题笔记了,对内核驱动啥的不太懂,pwn也不会,能做出来的题都是硬逆出来的(

childre最后死活没整出来,后来看大佬的wp才知道对子进程有修改(。)呜呜呜多进程真的不太会啊。

REVERSE

obfu

一堆密码堆起来的题(密码菜鸡卑微落泪

按逻辑来说拿到serial number就能拿到flag了。

通过findcrypt找到三个加密算法的常量,通过交叉引用确定函数用途。

v10和v8相比之下v8更容易出,v8跟输入没有关系,相当于已知字符串(=md5("admin")。

然后就是enctrpy函数。

按照顺序是AES_enc->RC4_dec->循环左移三位复原。

exp:

from binascii import *
from Crypto.Cipher import AES,ARC4
from hashlib import sha256,md5 v8=unhexlify(md5("admin").hexdigest())
admin=[ord(x) for x in unhexlify(sha256("admin").hexdigest())]
tmp=""+chr(admin[0])
for i in range(1,32):
tmp+=chr(admin[i]^admin[i-1])
key=tmp[:16]
iv=tmp[16:]
aes=AES.new(key,AES.MODE_CBC,iv)
cipher=aes.encrypt(v8)
rc4=ARC4.new(key)
input_enc=rc4.decrypt(cipher)
tmps=bin(int(hexlify(input_enc),16))[2:].rjust(16*8,'0')
tmps=tmps[3:]+tmps[:3]
serial=""
for i in range(16):
serial+=hex(int(tmps[i*8:i*8+8],2))[2:].rjust(2,'0')
print(serial)

拿到serial:653b987431e5a2fc7c3d748fba008869

最后拿到flag:0725f66471f85ba9d742eb583c75959c

decryption

送!分!题!泪目了呜呜呜呜呜

加密函数在这里:

懒得逆逻辑了,直接copy算法爆破(毕竟是按字节加密hhh。

arr=[0x12, 0x45, 0x10, 0x47, 0x19, 0x49, 0x49, 0x49, 0x1A, 0x4F, 0x1C, 0x1E, 0x52, 0x66, 0x1D, 0x52, 0x66, 0x67, 0x68, 0x67, 0x65, 0x6F, 0x5F, 0x59, 0x58, 0x5E, 0x6D, 0x70, 0xA1, 0x6E, 0x70, 0xA3]
flag=""
for i in range(32):
for j in range(32,127):
v5=j
v4=i
while True:
v3=2*(v4&v5)
v5^=v4
v4=v3
if v3==0:
break
if v5^0x23==arr[i]:
flag+=chr(j)
break
print(flag)

flag:1e1a6edc1c52e80b539127fccd48f05a

babyre

乍一看主函数逻辑很简单,实际上如果不patch的话是不可能完成的(byte_41A808这里有不可见字符。

看到这篇wp(HWS计划2021硬件安全冬令营线上选拔赛 re wp_20000s的博客-CSDN博客)里说是“内部加载dll,hook了驱动”,好像是这样,不过为啥动态调试的时候没有hook到呢(tcl不懂,选择手动hook

然后注意到了2047这个奇怪的数字,正常来说ZwLoadDriver应该是不会返回这种东西的(吧,所以从左边函数表一个一个翻下来找到了关键函数sub_412A20()

结合前面v9的赋值可以猜测,应该调用这个函数,并且a2是v9,才能对输入的Str调用v4进行加密得到byte_41A808。

所以先直接暴力patch

然后下断点,动态调试,可以看到v4实际上是:

噢,感谢密钥的提示,不用看了hhh,直接sm4解密(从SM4 python_dumpling-cat的博客-CSDN博客抓了脚本改了改)。

# S盒
SboxTable = \
[
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48,
]
# 常数FK
FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc] ; ENCRYPT = 0 ;DECRYPT = 1
# 固定参数CK
CK = \
[
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
]
def padding(data): #填充
print ("plaintext:\t", bytes (data))
file_data_list = list(data)
lenth = len (file_data_list)
#print ("data lenth:", lenth)
remainder = lenth % 16
if remainder != 0:
i=16-remainder #i为需要填充的位数
#print ("padding numbers = ", i)
for j in range(i):
file_data_list.append(i) #填充 char 0-(i-1)
if remainder == 0:
for k in range(16):
file_data_list.append (0x08) #刚好的话 填充0x08
print("after PKCS5 padding:",file_data_list)
return file_data_list
def list_4_8_to_int32(key_data): # 列表4个8位,组成32位
return int ((key_data[0] << 24) | (key_data[1] << 16) | (key_data[2] << 8) | (key_data[3]))
def n32_to_list4_8(n): #把n分别取32位的每8位放入列表
return [int ((n >> 24) & 0xff), int ((n >> 16) & 0xff), int ((n >> 8) & 0xff), int ((n) & 0xff)]
#循环左移
def shift_left_n(x, n):
return int (int (x << n) & 0xffffffff)
def shift_logical_left(x, n):
return shift_left_n (x, n) | int ((x >> (32 - n)) & 0xffffffff) #两步合在一起实现了循环左移n位
def XOR(a, b):
return list (map (lambda x, y: x ^ y, a, b))
#s盒查找
def sbox(idx):
return SboxTable[idx]
def extended_key_LB(ka): #拓展密钥算法LB
a = n32_to_list4_8 (ka) #a是ka的每8位组成的列表
b = [sbox (i) for i in a] #在s盒中每8位查找后,放入列表b,再组合成int bb
bb = list_4_8_to_int32 (b)
rk = bb ^ (shift_logical_left (bb, 13)) ^ (shift_logical_left (bb, 23))
return rk
def linear_transform_L(ka): #线性变换L
a = n32_to_list4_8 (ka)
b = [sbox (i) for i in a]
bb = list_4_8_to_int32 (b) #bb是经过s盒变换的32位数
return bb ^ (shift_logical_left (bb, 2)) ^ (shift_logical_left (bb, 10)) ^ (shift_logical_left (bb, 18)) ^ (shift_logical_left (bb, 24)) #书上公式
def sm4_round_function(x0, x1, x2, x3, rk): #轮函数
return (x0 ^ linear_transform_L (x1 ^ x2 ^ x3 ^ rk))
class Sm4 (object):
def __init__(self):
self.sk = [0] * 32
self.mode = ENCRYPT
def sm4_set_key(self, key_data, mode): #先算出拓展密钥
self.extended_key_last (key_data, mode)
def extended_key_last(self, key, mode): #密钥扩展算法
MK = [0, 0, 0, 0]
k = [0] * 36
MK[0] = list_4_8_to_int32 (key[0:4])
MK[1] = list_4_8_to_int32 (key[4:8])
MK[2] = list_4_8_to_int32 (key[8:12])
MK[3] = list_4_8_to_int32 (key[12:16])
k[0:4] = XOR (MK, FK)
for i in range (32):
k[i + 4] = k[i] ^ (extended_key_LB (k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ CK[i]))
self.sk = k[4:] #生成的32轮子密钥放到sk中
self.mode = mode
if mode == DECRYPT: #解密时rki逆序
self.sk.reverse ()
def sm4_one_round(self, sk, in_put): #一轮算法 ,4个32位的字=128bit=16个字节(8*16)
item = [list_4_8_to_int32 (in_put[0:4]), list_4_8_to_int32 (in_put[4:8]), list_4_8_to_int32 (in_put[8:12]),
list_4_8_to_int32 (in_put[12:16])] #4字节一个字,把每4个字节变成32位的int
x=item
for i in range (32):
temp=x[3]
x[3] = sm4_round_function (x[0], x[1], x[2], x[3], sk[i]) #x[3]成为x[4]
x[0]=x[1]
x[1]=x[2]
x[2]=temp
print("%dround----->"%(i+1),"key:%-12d\n"%sk[i],"result:",x)
res=x
# res = reduce (lambda x, y: [x[1], x[2], x[3], sm4_round_function (x[0], x[1], x[2], x[3], y)],sk, item) #32轮循环加密
res.reverse ()
rev = map (n32_to_list4_8, res)
out_put = []
[out_put.extend (_) for _ in rev]
return out_put
def encrypt(self, input_data):
# 块加密
output_data = []
tmp = [input_data[i:i + 16] for i in range (0, len (input_data), 16)] #输入数据分块
[output_data.extend (each) for each in map (lambda x: self.sm4_one_round (self.sk, x), tmp)]
return output_data
def encrypt(mode, key, data):
sm4_d = Sm4 ()
sm4_d.sm4_set_key (key, mode)
en_data = sm4_d.encrypt (data)
return en_data
def sm4_crypt_cbc(mode, key, iv, data):
sm4_d = Sm4 ()
sm4_d.sm4_set_key (key, mode)
en_data = sm4_d.sm4_crypt_cbc (iv, data)
return en_data
if __name__ == "__main__":
key_data=[ord(c) for c in "Ez_5M4_C1pH@r!!!"]
en_data=b"\xEA\x63\x58\xB7\x8C\xE2\xA1\xE9\xC5\x29\x8F\x53\xE8\x08\x32\x59\xAF\x1B\x67\xAE\xD9\xDA\xCF\xC4\x72\xFF\xB1\xEC\x76\x73\xF3\x06"
sm4_d = Sm4 ()
sm4_d.sm4_set_key (key_data, DECRYPT)
de_data = sm4_d.encrypt (en_data)
flag=''.join(list(map(chr,de_data)))
print(flag)

flag:42b061b4cb41cfa89ca78047bde1856e

Enigma

逻辑很清晰,直接分析loc_4018F0。

这边有一个SetUnhandledExceptionFilter捕获异常。

一开始以为下面的都是花指令,但每次都是0xC7转不了就很奇怪,异常+0xC7想到了平时用的CC断点,再加上sub_401630看起来像是个虚拟机,感觉0xC7是一个中断号(计组刚学,捕获中断然后用sub_401630处理。

每一个case里sub_4011B0是取值,另一个函数则是处理,v18是指令长度。

通过对ExceptionInfo的查阅发现,v19=*(_DWORD *)(*(_DWORD *)(a1 + 4) + 0xB8)开始是异常发生的地址即0xC7的地址,最后则应该是异常处理完毕后要返回的用户空间代码地址。(这么一推异常应该是0xC7FF才对

opcode是0xC7FF的下一个即v19+2,后面的为操作数。

二级函数里面都是对a1[40]~a1[44]进行操作,猜测是寄存器。

于是可以分析出:

// xx为[1,5],rx为对应的a1[40]~a1[44]寄存器,是二级函数中的case
// 1->a1[44],2->a1[41],3->a1[43],4->a1[42],5->a1[40]
// imm为立即数
C7 FF 00 xx imm //add rx,imm
C7 FF 01 xx imm //sub rx,imm
C7 FF 02 xx //inc rx
C7 FF 03 xx //dec rx
C7 FF 04 xx imm //and rx,imm
C7 FF 05 xx imm //or rx,imm
C7 FF 06 xx imm //xor rx,imm
C7 FF 07 xx imm //shl rx,imm
C7 FF 08 xx imm //shr rx,imm

然后把下面的机器码中带有C7 FF系列指令以外的先转换成code。

接下来处理这些异常部分的机器码,比如第一个中断翻译出来是and r1,0,即将r1清空,汇编习惯上更常用xor r1,r1 ,先用注释标注。

然后根据上下文以及上图可以推断出r1-r5分别对应如下:

r1 -> eax;
r2 -> ebx;
r3 -> ecx;
r4 -> edx;
r5 -> esi;

patch程序,多余字节直接nop(因为不记得有些机器码了所以直接用keypatch。

然后得到反编译的加密函数。

根据逻辑逆向写出exp:

from hashlib import md5

dst="938b8f431268f7907a4b6e421301b42120738d68cb19fcf8b26bc4abc89b8d22"
output=[]
for i in range(0,64,2):
output.append(int("0x"+dst[i:i+2],16))
key=[0x42, 0x69, 0x65, 0x72]
tmp2=[output[0]]
for i in range(1,32):
tmp2.append(output[i]^tmp2[i-1]^key[i&3])
tmp1=[]
for i in range(32):
tmp1.append(((tmp2[i]&0xf8)>>3)|((tmp2[(i-1)&0x1F]&0x07)<<5))
v0=0
arr=[]
for _ in range(32):
v0=(v0+17)&0x1F
arr.append(v0)
flag=['-' for _ in range(32)]
for i in range(0,32,2):
flag[arr[i]],flag[arr[i+1]]=chr(tmp1[arr[i+1]]),chr(tmp1[arr[i]])
flag=''.join(flag)
print(md5(flag).hexdigest())

flag:751542a09b8b341dda23ebfc387a5e91

内核安全

easy_kernel

是个ring3调用ring0的题(前半部分。

从ring3看起,v7是flag,走了sub_401005函数。

跳到最后有

int 2E,查了一下是用户模式进入内核模式的中断,于是去看ring0(即DriverXP.sys,调用号是186。

可以看到这里有186,并且参数个数也是6个,猜测sub_401340就是调用的函数。

进一步可以看到,a1==-1,刚好也吻合sub_401005的第一个参数。

findcrypt可以看到DES,并且查交叉引用和反编译代码证实sub_401680就是DES加密过程。

可以看到这边密钥只用了"}aglf_T_ton_5i_sihT_yrroS{galf"的前八字节。

第一层加密确认,从ring3继续往下看。

这个MEMORY[0x5804E8]实在是没看出来是什么,看汇编是call fword ptr,查了一下是个长调用,返回用retf。

gdt表也懒得查,于是在ring3的汇编这里从头开始一个一个往下看retf,准备把碰到的都试一遍。

诶,看到一个,转函数看看。

代到exp里试了一下,发现成了,运气很好hhh,最开始这个就是MEMORY[0x5804E8]。

exp:

from Crypto.Cipher import DES
from hashlib import md5 key='}aglf_T_ton_5i_sihT_yrroS{galf'[:8]
c=[0xB2, 0xC4, 0x86, 0xD5, 0x54, 0x6C, 0x38, 0xAD, 0xBD, 0x69, 0xD4, 0xE9, 0x44, 0x47, 0x36, 0x21, 0x99, 0x91, 0xFB, 0x13, 0x70, 0xD8, 0x6B, 0xE4, 0x80, 0x12, 0xE2, 0x43, 0x2A, 0x4B, 0x49, 0x8E]
for i in range(30,-1,-1):
c[i]^=c[i+1]
cipher=''.join(list(map(chr,c)))
des=DES.new(key)
flag=des.decrypt(cipher)
print(flag)
print(md5(flag).hexdigest())

flag:c1878dfb2b0c23c74ec4e6650d8f7004

固件安全

NodeMCU

strings nodemcu.bin > str.out,导出字符串后直接查找flag即可

flag:6808dcf0-526e-11eb-92de-acde48001122

STM

用bin2hex将bin文件转为hex文件,然后用ida加载hex。

查找STM的datasheet(以常见的STM32F103CB为参照)可知flash映射在0x08000000地址上

于是载入ida的时候还要把基址改成0x08000000。

然后查找字符串,直接看Hello World的交叉引用,找到主函数。

根据相应的函数功能猜测并命名,sub_8000314是加密函数。

逻辑很简单,直接写exp:

arr=[0x7D, 0x77, 0x40, 0x7A, 0x66, 0x30, 0x2A, 0x2F, 0x28, 0x40, 0x7E, 0x30, 0x33, 0x34, 0x2C, 0x2E, 0x2B, 0x28, 0x34, 0x30, 0x30, 0x7C, 0x41, 0x34, 0x28, 0x33, 0x7E, 0x30, 0x34, 0x33, 0x33, 0x30, 0x7E, 0x2F, 0x31, 0x2A, 0x41, 0x7F, 0x2F, 0x28, 0x2E, 0x64]
for x in arr:
print(chr((x^0x1E)+3),end='')

flag{1749ac10-5389-11eb-90c1-001c427bd493}

【wp】HWS计划2021硬件安全冬令营线上选拔赛的更多相关文章

  1. 2021年【线上】第一性原理vasp技术实战培训班

    材料模拟分子动力学课程 3月19号--22号 远程在线课 lammps分子动力学课程 3月12号--15号 远程在线课 第一性原理VASP实战课 3月25号-28号 远程在线课 量子化学Gaussia ...

  2. 2021年【线上】lammps分子动力学技术实战培训班

    材料模拟分子动力学课程 3月19号--22号 远程在线课 lammps分子动力学课程 3月12号--15号 远程在线课 第一性原理VASP实战课 3月25号-28号 远程在线课 量子化学Gaussia ...

  3. 2021 .NET 开发者峰会顺利在网上落幕,线上直播回看汇总

    .NET Conf China 2021 是面向开发人员的社区峰会,基于 .NET Conf 2021的活动,庆祝 .NET 6 的发布和回顾过去一年来 .NET 在中国的发展成果展示,它是由中国各地 ...

  4. 直播预告 | 猪齿鱼V1.1发布,线上新功能详解邀您参加

    2021年11月11日,数智化效能平台猪齿鱼 Choerodon发布 V1.1版本,多项功能新增或优化,多管齐下,全面提升团队工作效能! 通过提供体系化方法论和协作.测试.DevOps及容器工具,猪齿 ...

  5. Springcloud及Git线上配置详解

    SpringCloud 这个阶段该如何学? 三层架构 + MVC 框架: Spring IOC AOP SpringBoot,新一代的JavaEE开发标准,自动装配 模块化~ all in one,代 ...

  6. 线上任务的mysql 重启

    我们的业务是 所使用的数据库是 自己搭建的mysql-server-5.05, 服务器 红帽子6.0. 考虑到 服务的稳定性,计划将数据库向dba进行迁移,由他们进行维护.dba的迁移计划是 1 先创 ...

  7. 【Alpha阶段】第一次线上会议

    会议信息 因编译作业ddl,暂时没有大进展,没有close的issue 时间:2016.11.07 19:00 时长:10min 地点:讨论组 类型:线上会议 NXT:2016.11.08 21:30 ...

  8. 记录一次linux线上服务器被黑事件

    1.原因:本来在家正常休息了,我们放在上海托管机房的线上服务器突然蹦了远程不了,服务启动不了,然后让上海机房重启了一次,还是直接挂了,一直到我远程上才行. 2.现象:远程服务器发现出现这类信息 Hi, ...

  9. TFS线上生成环境发布历程

    继前文 TFS在项目中Devops落地进程(上) TFS在项目中DevOps落地进程(下) 自从之前将开发环境使用TFS进行了自动化之后,就享受在此成果中,其他后续进度就停顿了好一段时间. 毕竟在我们 ...

随机推荐

  1. Docker 的 2020,实 "鼠" 不易!

    元旦你们出去嗨,栈长在家撸文章,惨惨惨- 没错, Docker 的 2020 年也过的不是很顺利,可以说是流年不利.命运多舛,一年发生两件大事,太折腾! 相信大家也已经看到很多相关的报道了,但同时也有 ...

  2. 聊聊ERP的VIP卡充值的那些事

    我们相信许多客户朋友,不管使用什么品牌的ERP系统,可能都有经历过各种各样的操作痛点,以及在某个阶段之前的功能无法满足现有的操作需求.今天我们就聊聊VIP卡充值操作遇到的一些问题以及相关解决方案,最大 ...

  3. Centos7 密钥对登陆(适用于群晖DSM)

    www.swack.cn - 原文链接:Centos7 密钥对登陆(适用于群晖DSM) 1.生成证书 此处证书使用swack用户生成 注:不要使用root生成证书,因为我们后面会禁用root登陆 [s ...

  4. 笔记:学习go语言的网络基础库,并尝试搭一个简易Web框架

    在日常的 web 开发中,后端人员常基于现有的 web 框架进行开发.但单纯会用框架总感觉不太踏实,所以有空的时候还是看看这些框架是怎么实现的会比较好,万一要排查问题也快一些. 最近在学习 go 语言 ...

  5. SQL注入之堆叠注入(堆查询注入)

    Stached injection -- 堆叠注入 0x00 堆叠注入的定义 ​ Stacked injection 汉语翻译过来后,称 为堆查询注入,也有称之为堆叠注入.堆叠注入为攻击者提供了很多的 ...

  6. If you see someone without smile

    If you see someone without smile, give them one of yours. 难怪我每次和不认识的人说话都放肆大笑.

  7. 【Linux】saltstack的使用详解 超详细

    一.salt常用命令 salt 该命令执行salt的执行模块,通常在master端运行,也是我们最常用到的命令 salt [options] '<target>' <function ...

  8. java创建线程安全的类

    如果一个对象想要被多个线程安全的并发访问,那么这个对象必须是或线程安全的或事实不可变的或由锁来保护的. 1.java监视器模式 大多数对象都是组合对象.当从头开始构建一个类,或者将多个非线程安全的类组 ...

  9. ctfhub技能树—文件上传—文件头检查

    打开靶机 尝试上传一个php文件 抓包修改 放包 制作图片马 上传图片马,并修改文件类型为png 测试连接 查找flag 成功拿到flag

  10. 分布式 ID 生成算法 — SnowFlake

    一.概述 分布式 ID 生成算法的有很多种,Twitter 的 SnowFlake 就是其中经典的一种. SnowFlake 算法生成 ID 的结果是一个 64bit 大小的整数,它的结构如下图: 1 ...