逆向手在夹缝中艰难求生系列。
这篇真的存粹是做题笔记了,对内核驱动啥的不太懂,pwn也不会,能做出来的题都是硬逆出来的(
childre最后死活没整出来,后来看大佬的wp才知道对子进程有修改(。)呜呜呜多进程真的不太会啊。
逆向手在夹缝中艰难求生系列。
这篇真的存粹是做题笔记了,对内核驱动啥的不太懂,pwn也不会,能做出来的题都是硬逆出来的(
childre最后死活没整出来,后来看大佬的wp才知道对子进程有修改(。)呜呜呜多进程真的不太会啊。
REVERSE
obfu
一堆密码堆起来的题(密码菜鸡卑微落泪
按逻辑来说拿到serial number就能拿到flag了。
通过findcrypt找到三个加密算法的常量,通过交叉引用确定函数用途。
v10和v8相比之下v8更容易出,v8跟输入没有关系,相当于已知字符串(=md5("admin")。
然后就是enctrpy函数。
按照顺序是AES_enc->RC4_dec->循环左移三位复原。
exp:
拿到serial:653b987431e5a2fc7c3d748fba008869
最后拿到flag:0725f66471f85ba9d742eb583c75959c
decryption
送!分!题!泪目了呜呜呜呜呜
加密函数在这里:
懒得逆逻辑了,直接copy算法爆破(毕竟是按字节加密hhh。
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博客 抓了脚本改了改)。
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 = [0xa3b1bac6 , 0x56aa3350 , 0x677d9197 , 0xb27022dc ] ; ENCRYPT = 0 ;DECRYPT = 1
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)
remainder = lenth % 16
if remainder != 0 :
i=16 -remainder
for j in range (i):
file_data_list.append(i)
if remainder == 0 :
for k in range (16 ):
file_data_list.append (0x08 )
print ("after PKCS5 padding:" ,file_data_list)
return file_data_list
def list_4_8_to_int32 (key_data ):
return int ((key_data[0 ] << 24 ) | (key_data[1 ] << 16 ) | (key_data[2 ] << 8 ) | (key_data[3 ]))
def n32_to_list4_8 (n ):
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 )
def XOR (a, b ):
return list (map (lambda x, y: x ^ y, a, b))
def sbox (idx ):
return SboxTable[idx]
def extended_key_LB (ka ):
a = n32_to_list4_8 (ka)
b = [sbox (i) for i in a]
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 ):
a = n32_to_list4_8 (ka)
b = [sbox (i) for i in a]
bb = list_4_8_to_int32 (b)
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 :]
self.mode = mode
if mode == DECRYPT:
self.sk.reverse ()
def sm4_one_round (self, sk, in_put ):
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 ])]
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[0 ]=x[1 ]
x[1 ]=x[2 ]
x[2 ]=temp
print ("%dround----->" %(i+1 ),"key:%-12d\n" %sk[i],"result:" ,x)
res=x
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]进行操作,猜测是寄存器。
于是可以分析出:
然后把下面的机器码中带有C7 FF系列指令以外的先转换成code。
接下来处理这些异常部分的机器码,比如第一个中断翻译出来是and r1,0
,即将r1清空,汇编习惯上更常用xor r1,r1
,先用注释标注。
然后根据上下文以及上图可以推断出r1-r5分别对应如下:
patch程序,多余字节直接nop(因为不记得有些机器码了所以直接用keypatch。
然后得到反编译的加密函数。
根据逻辑逆向写出exp:
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:
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}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程