5. gdb_debug
其实逻辑还是挺简单的,当时没认真做
伪代码还算清晰
几个循环的加密之后判断密文
难点是前面有随机数参与加密,不过可以猜测随机数是不变的。
第一段加密
flag异或一组随机数,这里可以在异或的位置下条件断点,用idapython把随机数直接打印出来(会发现是不变的)
al = idaapi.get_reg_val("al")
print(hex(al), end=',')
得到第一段异或的随机数
第二段ptr数组直接点开看不见,鼠标悬停在上面可以看见一个地址,跳转过去0x55610C1B5AC0
跳过去取38个
第三段加密换表+异或
直接从crypto倒推,异或的tmp随机数用相同的方法打印
#gdb wp
crypto = [0x63,0x6f,0x6e,0x67,0x72,0x61,0x74,0x75,0x6c,0x61,0x74,0x69,0x6f,0x6e,0x73,0x74,0x6f,0x79,0x6f,0x75,0x63,0x6f,0x6e,0x67,0x72,0x61,0x74,0x75,0x6c,0x61,0x74,0x69,0x6f,0x6e,0x73,0x74,0x6f,0x79]
key = [ 0xBF, 0xD7, 0x2E, 0xDA, 0xEE, 0xA8, 0x1A, 0x10, 0x83, 0x73,
0xAC, 0xF1, 0x06, 0xBE, 0xAD, 0x88, 0x04, 0xD7, 0x12, 0xFE,
0xB5, 0xE2, 0x61, 0xB7, 0x3D, 0x07, 0x4A, 0xE8, 0x96, 0xA2,
0x9D, 0x4D, 0xBC, 0x81, 0x8C, 0xE9, 0x88, 0x78]
enc2 = [0]*38
for i in range(38):
enc2[i] = crypto[i] ^ key[i]
xor2 = [0xde,0xaa,0x42,0xfc,0x9,0xe8,0xb2,0x6,0xd,0x93,0x61,0xf4,0x24,0x49,0x15,0x1,0xd7,0xab,0x4,0x18,0xcf,0xe9,0xd5,0x96,0x33,0xca,0xf9,0x2a,0x5e,0xea,0x2d,0x3c,0x94,0x6f,0x38,0x9d,0x58,0xea]
for i in range(38):
enc2[i] ^= xor2[i]
ptr = [0x12, 0x0E, 0x1B, 0x1E, 0x11, 0x05, 0x07, 0x01, 0x10, 0x22, 0x06, 0x17, 0x16, 0x08, 0x19, 0x13,
0x04, 0x0F, 0x02, 0x0D, 0x25, 0x0C, 0x03, 0x15, 0x1C, 0x14, 0x0B, 0x1A, 0x18, 0x09, 0x1D, 0x23,
0x1F, 0x20, 0x24, 0x0A, 0x00, 0x21]
enc1 = [0]*38
for i in range(38):
enc1[ptr[i]] = enc2[i]
xor1 = [0xd9,0xf,0x18,0xbd,0xc7,0x16,0x81,0xbe,0xf8,0x4a,0x65,0xf2,0x5d,0xab,0x2b,0x33,0xd4,0xa5,0x67,0x98,0x9f,0x7e,0x2b,0x5d,0xc2,0xaf,0x8e,0x3a,0x4c,0xa5,0x75,0x25,0xb4,0x8d,0xe3,0x7b,0xa3,0x64]
flag = ''
for i in range(38):
flag += chr(enc1[i] ^ xor1[i])
print(flag)
#flag{78bace5989660ee38f1fd980a4b4fbcd}
6. GoReverse
恶心的Golang
主函数没啥东西,调试一下
第9行 while部分会循环一会,是在分配需要的栈帧
第11行 有个反调试,patch一下
调试时遇到异常就discard掉
主逻辑应该在main_FlatControlFlow内
在里面调试,发现走到call rdx这里打印了一句话
再循环走到这里又打印出后面的
然后结束
再动调一遍发现实际上跳到了main__ptr_co6Pxq_Execute
就光打了一句话
第二次
main__ptr_B2bUPq_Execute应该是加密部分
前面是分配占空间和错误提示
注意这里调试需要flag文件作为输入
然后初始化,读取文件,一个错误处理
下面第一个加密函数main_ylFyZv
实际上就一个循环异或
第二个加密函数main_zQyveE,是一个魔改XXTEA加密
一个是改了delta
一个是改了MX
main_Q05qm6主要是做一些字符串切片
然后是main_AkuFrt
好像是个sm4
github可以搜到上面那个函数的源码gmsm/sm4/sm4.go at master · tjfoc/gmsm · GitHub
看一下汇编会发现是CTR加密
也能搜到示例代码cipher package - crypto/cipher - Go Packages,不过是个AES的示例
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
)
func main() {
// Load your secret key from a safe place and reuse it across multiple
// NewCipher calls. (Obviously don't use this example key for anything
// real.) If you want to convert a passphrase to a key, use a suitable
// package like bcrypt or scrypt.
key, _ := hex.DecodeString("6368616e676520746869732070617373")
plaintext := []byte("some plaintext")
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
// It's important to remember that ciphertexts must be authenticated
// (i.e. by using crypto/hmac) as well as being encrypted in order to
// be secure.
// CTR mode is the same for both encryption and decryption, so we can
// also decrypt that ciphertext with NewCTR.
plaintext2 := make([]byte, len(plaintext))
stream = cipher.NewCTR(block, iv)
stream.XORKeyStream(plaintext2, ciphertext[aes.BlockSize:])
fmt.Printf("%s\n", plaintext2)
}
总之大致能看出来NewCipher处参数是key,NewCTR处参数是iv
而且CTR mode is the same for both encryption and decryption, so we can also decrypt that ciphertext with NewCTR.
对称的加解密
然后是main_JrkmHd
CBC的AES cipher package - crypto/cipher - Go Packages
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
)
func main() {
// Load your secret key from a safe place and reuse it across multiple
// NewCipher calls. (Obviously don't use this example key for anything
// real.) If you want to convert a passphrase to a key, use a suitable
// package like bcrypt or scrypt.
key, _ := hex.DecodeString("6368616e676520746869732070617373")
plaintext := []byte("exampleplaintext")
// CBC mode works on blocks so plaintexts may need to be padded to the
// next whole block. For an example of such padding, see
// https://tools.ietf.org/html/rfc5246#section-6.2.3.2. Here we'll
// assume that the plaintext is already of the correct length.
if len(plaintext)%aes.BlockSize != 0 {
panic("plaintext is not a multiple of the block size")
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
// It's important to remember that ciphertexts must be authenticated
// (i.e. by using crypto/hmac) as well as being encrypted in order to
// be secure.
fmt.Printf("%x\n", ciphertext)
}
NewCipher处调用key, NewCBCENcrypter处调用iv
main_NJVCTq就是base32
总体逻辑就出来了
然后就是收集各个参数
假设结果 VPAFNU3PTHPAUQTCYUBTVJY6TGBWY3NGGKZ6OFKZ74JJBPF7LSQ3ZYO4IZLOXPVEIE7KZS46VJKNDJGRBJPUTVKTNFLZRQBLLZUD7VI=
1 xor
v24 = a5;
v19 = a1;
...
a5 = a4;
a4 = v24;
...
v13 = *(i + v19);
...
*(result + i) = a5[i % v24] ^ v13;
a5 = D7BJLsOk9@f&1dWIn53IDlJqUS6$^WhkAk2kk*2GaqmLwiLX^bGGE$&dmqR^g5bL3lCA5^HGK$9qo5T@Bwom9vEXya0HAV3LrWW
v24 = 0x63
v13 = input
2 xxtea_kai
key 0x385E7342, 0x345A772A, 0x6F38756C, 0x6B402652
delta = 0x7FAB4CAD
MX_kai (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y)) + ((key[(p & 3) ^ e] ^ z)))
3 SM4_CTR
key pg5g#k6Qo3L&1EzT
iv 随机生成
4 AES_CBC
key dPGWgcLpqmxw3uOXhKpKV009Cql@@XE6
iv dPGWgcLpqmxw3uOX(key前16位)
5 base32
xor的key很容易得到
xxtea的key
SM4的key
AES的key
SM4的iv是随机的,动调看看怎么个事儿
随机数在这里看到
16个字节,我们把它全改成1跟一下(C0000BC2B0)
跟到AES加密的位置,发现随机数被加到了加密数据的开头?
再从头调
再改一下随机数1111111111111111
看一下SM4输出
随机数被加到了输出的开头,也就是说密文的开头添加了随机数iv
那解密的时候把AES解密的开头16位取出就是iv了
最后解一个魔改XXTEA,然后异或一串字符即可
#go_wp
def byte2uint32(d):
l=[]
for i in range(len(d)//4):
tmp=d[i*4:i*4+4]
l.append(tmp[3]*256**3+tmp[2]*256**2+tmp[1]*256+tmp[0])
return l
def hexprint(arr):
for byte in arr:
print(hex(byte), end=', ')
print()
# 给定的十六进制字符串
hex_string = "69124cadc128ffe15752488b318e78c68f359d69fb2b8e4bc35ae98ee05493ca31ceb32174201bb091c9406e56430d53c046d1d128a0de5862ee5e6d"
# 将十六进制字符串转换为字节数组
byte_array = bytes.fromhex(hex_string)
# 取出前16个字节
first_16_bytes = byte_array[:16]
# 将前16个字节转换为十六进制字符串
first_16_hex_string = first_16_bytes.hex()
# 打印前16个字节的十六进制字符串
print(first_16_hex_string)
enc = [0xe9,0xd3,0xa0,0x68,0x40,0xc8,0x27,0xb6,0x29,0xaf,0x66,0x77,0x61,0xad,0xd2,0x18,0xae,0x3d,0x67,0x5c,0x85,0x9a,0x92,0x1b,0xf6,0xb8,0x5b,0x5d,0x38,0x5a,0x79,0x5e,0x00,0x53,0xde,0x67,0x20,0xe4,0x14,0x34,0x0b,0x91,0xc5,0x54]
enc32 = byte2uint32(enc)
hexprint(enc32)
from ctypes import *
def MX(z, y, sum1, k, p, e):
#return c_uint32(((z.value>>5^y.value<<2)+(y.value>>3^z.value<<4))^((sum1.value^y.value)+(k[(p&3)^e.value]^z.value)))
return c_uint32(( (z.value>>5^y.value<<2)+(y.value>>3^z.value<<4)^(sum1.value^y.value)) + (k[(p&3)^e.value]^z.value))
def btea(v,k,n,delta):
if n>1:
sum1=c_uint32(0)
z=c_uint32(v[n-1])
rounds=6+52//n
e=c_uint32(0)
while rounds>0:
sum1.value+=delta
e.value=((sum1.value>>2)&3) #e都要32位哦
for p in range(n-1):
y=c_uint32(v[p+1])
#v[p]=c_uint32(v[p]+c_uint32((((z.value>>5^y.value<<2)+(y.value>>3^z.value<<4))^((sum1.value^y.value)+(k[(p&3)^e.value]^z.value)))).value).value
v[p] = c_uint32(v[p] + MX(z,y,sum1,k,p,e).value).value
z.value=v[p]
y=c_uint32(v[0])
#v[n-1]=c_uint32(v[n-1]+c_uint32((((z.value>>5^y.value<<2)+(y.value>>3^z.value<<4))^((sum1.value^y.value)+(k[((n-1)&3)^e.value]^z.value)))).value).value #这里tmd传入的是k[((n-1)&3)啊我草,找了半天!!!
v[n-1] = c_uint32(v[n-1] + MX(z,y,sum1,k,n-1,e).value).value
z.value=v[n-1]
rounds-=1
else:
sum1=c_uint32(0)
n=-n
rounds=6+52//n
sum1.value=rounds*delta
y=c_uint32(v[0])
e=c_uint32(0)
while rounds>0:
e.value=((sum1.value>>2)&3) #e都要32位哦
for p in range(n-1, 0, -1):
z=c_uint32(v[p-1])
#y[p]=c_uint32(v[p]-c_uint32((((z.value>>5^y.value<<2)+(y.value>>3^z.value<<4))^((sum1.value^y.value)+(k[(p&3)^e.value]^z.value)))).value).value
v[p] = c_uint32(v[p] - MX(z,y,sum1,k,p,e).value).value
y.value=v[p]
z=c_uint32(v[n-1])
#v[n-1]=c_uint32(v[n-1]-c_uint32((((z.value>>5^y.value<<2)+(y.value>>3^z.value<<4))^((sum1.value^y.value)+(k[((n-1)&3)^e.value]^z.value)))).value).value #这里tmd传入的是k[((n-1)&3)啊我草,找了半天!!!
v[0] = c_uint32(v[0] - MX(z,y,sum1,k,0,e).value).value
y.value=v[0]
sum1.value-=delta
rounds-=1
return v
a=[0x68a0d3e9, 0xb627c840, 0x7766af29, 0x18d2ad61, 0x5c673dae, 0x1b929a85, 0x5d5bb8f6, 0x5e795a38, 0x67de5300, 0x3414e420, 0x54c5910b]
k=[0x385E7342, 0x345A772A, 0x6F38756C, 0x6B402652]
delta=0x7FAB4CAD
n=11
res=btea(a,k,-n,delta)
hexprint(res)
def uint322byte(uint32_list):
byte_array = []
for number in uint32_list:
byte_array.append(number & 0xFF)
byte_array.append((number >> 8) & 0xFF)
byte_array.append((number >> 16) & 0xFF)
byte_array.append((number >> 24) & 0xFF)
return byte_array
flag_byte = uint322byte(res)
flag = ''
xor_key = 'D7BJLsOk9@f&1dWIn53IDlJqUS6$^WhkAk2kk*2GaqmLwiLX^bGGE$&dmqR^g5bL3lCA5^HGK$9qo5T@Bwom9vEXya0HAV3LrWW'
for i in range(len(flag_byte)):
flag += chr(flag_byte[i]^ord(xor_key[i%0x63]))
print(flag)
#flag{3a4575cf-c85c-4350-90ca-baef8252425e}