secret
hgameweek4的题目
对这个题目可以说一直很迷,一直感觉毫无问题,但是写python脚本就是不对,今天又想起来了,拿c按着他的加密方式i写了一个,结果还真ok了,c中的程序中使用的是一个uint_32
类型,然后python中数据长度会溢出, 然后之前一直试图去用&
去控制,但是也很尴尬的一直没弄好,
题目文件:
https://www.jianguoyun.com/p/DeAJhdAQiNbmBxi4xuIC (访问密码:WBhhV3)
emmm 百度网盘卡住没打开? 明天补一个
获取code
首先进入main函数是这样的,中间他监听了一个端口,从这个端口获取了一段code, 并运行起来,这里就隐藏掉了程序中间加密的位置,想起来了前面那个自解密的题目, 尝试动调下,但是在main函数之前会推出来,当时翻了下函数表乱乱的,
后来发现这个函数是0x4017EF的这个函数: 中间随便的位置跳一下就可以了
当时就直接像那个脱壳的题目gdb去attach上去看看,
在查进程的时候就可以发现有两个进程,其中的一个是另一个的子进程
然后挂上显示在等待输入的子进程, 会停在read的位置,这时候我们去读取那个code位置的数据就可以读取到,
x/200i 0x401BFC
, 可以得到这段汇编代码:
code - 汇编代码
0x401bfc: push rbp
0x401bfd: mov rbp,rsp
0x401c00: sub rsp,0x20
0x401c04: mov DWORD PTR [rbp-0x10],0x9e3779b9
0x401c0b: call 0x400a70 <getppid@plt>
0x401c10: mov ecx,eax
0x401c12: mov rax,QWORD PTR [rbp-0x10]
0x401c16: mov rdx,rax
0x401c19: mov esi,0x22
0x401c1e: mov edi,ecx
0x401c20: call 0x400a40 <sigqueue@plt> ;sigqueue(pid, 0x22)
0x401c25: mov edi,0x3e8
0x401c2a: call 0x400ab0 <usleep@plt>
0x401c2f: mov DWORD PTR [rbp-0x10],0x9e3779b9
0x401c36: call 0x400a70 <getppid@plt>
0x401c3b: mov ecx,eax
0x401c3d: mov rax,QWORD PTR [rbp-0x10]
0x401c41: mov rdx,rax
0x401c44: mov esi,0x23
0x401c49: mov edi,ecx
0x401c4b: call 0x400a40 <sigqueue@plt> ;sigqueue(pid, 0x23)
0x401c50: mov edi,0x3e8
0x401c55: call 0x400ab0 <usleep@plt>
0x401c5a: mov DWORD PTR [rbp-0x10],0x9e822efc
0x401c61: call 0x400a70 <getppid@plt>
0x401c66: mov ecx,eax
0x401c68: mov rax,QWORD PTR [rbp-0x10]
0x401c6c: mov rdx,rax
0x401c6f: mov esi,0x23
0x401c74: mov edi,ecx
0x401c76: call 0x400a40 <sigqueue@plt> ;sigqueue(pid, 0x23)
0x401c7b: mov edi,0x3e8
0x401c80: call 0x400ab0 <usleep@plt>
0x401c85: mov DWORD PTR [rbp-0x10],0xda278c92
0x401c8c: call 0x400a70 <getppid@plt>
0x401c91: mov ecx,eax
0x401c93: mov rax,QWORD PTR [rbp-0x10]
0x401c97: mov rdx,rax
0x401c9a: mov esi,0x23
0x401c9f: mov edi,ecx
0x401ca1: call 0x400a40 <sigqueue@plt> ;sigqueue(pid, 0x23)
0x401ca6: mov edi,0x3e8
0x401cab: call 0x400ab0 <usleep@plt>
0x401cb0: mov DWORD PTR [rbp-0x10],0x4e355a62
0x401cb7: call 0x400a70 <getppid@plt>
0x401cbc: mov ecx,eax
0x401cbe: mov rax,QWORD PTR [rbp-0x10]
0x401cc2: mov rdx,rax
0x401cc5: mov esi,0x23
0x401cca: mov edi,ecx
0x401ccc: call 0x400a40 <sigqueue@plt> ;sigqueue(pid, 0x23)
0x401cd1: mov edi,0x3e8
0x401cd6: call 0x400ab0 <usleep@plt>
0x401cdb: mov DWORD PTR [rbp-0x18],0x0
<<< 0x401ce2: jmp 0x401dbd ;for i in range(6):
>>> 0x401ce7: mov rdx,QWORD PTR [rip+0x20161a] # 0x603308
0x401cee: mov eax,DWORD PTR [rbp-0x18]
0x401cf1: shl eax,0x3
0x401cf4: cdqe
0x401cf6: add rax,rdx
0x401cf9: mov edx,0x8
0x401cfe: mov rsi,rax ;buf
0x401d01: mov edi,0x0
0x401d06: call 0x4009d0 <read@plt> ;read
0x401d0b: mov rax,QWORD PTR [rip+0x2015f6] # 0x603308 >> 0x7f....
0x401d12: mov QWORD PTR [rbp-0x10],rax
0x401d16: call 0x400a70 <getppid@plt>
0x401d1b: mov ecx,eax
0x401d1d: mov rax,QWORD PTR [rbp-0x10]
0x401d21: mov rdx,rax
0x401d24: mov esi,0x24
0x401d29: mov edi,ecx
0x401d2b: call 0x400a40 <sigqueue@plt> ;sigqueue(pid, 0x24)
0x401d30: mov edi,0x3e8
0x401d35: call 0x400ab0 <usleep@plt>
0x401d3a: mov DWORD PTR [rbp-0x14],0x0
< 0x401d41: jmp 0x401d98 ; for j in range(0x1f):
> 0x401d43: call 0x400a70 <getppid@plt>
0x401d48: mov esi,0x25
0x401d4d: mov edi,eax
0x401d4f: call 0x400a30 <kill@plt> ;kill(pid, 0x25)
0x401d54: mov edi,0x3e8
0x401d59: call 0x400ab0 <usleep@plt>
0x401d5e: call 0x400a70 <getppid@plt>
0x401d63: mov esi,0x26
0x401d68: mov edi,eax
0x401d6a: call 0x400a30 <kill@plt> ;kill(pid, 0x26)
0x401d6f: mov edi,0x3e8
0x401d74: call 0x400ab0 <usleep@plt>
0x401d79: call 0x400a70 <getppid@plt>
0x401d7e: mov esi,0x27
0x401d83: mov edi,eax
0x401d85: call 0x400a30 <kill@plt> ;kill(pid,0x27)
0x401d8a: mov edi,0x3e8
0x401d8f: call 0x400ab0 <usleep@plt>
0x401d94: add DWORD PTR [rbp-0x14],0x1
> 0x401d98: cmp DWORD PTR [rbp-0x14],0x1f
< 0x401d9c: jle 0x401d43
0x401d9e: call 0x400a70 <getppid@plt>
0x401da3: mov esi,0x28
0x401da8: mov edi,eax
0x401daa: call 0x400a30 <kill@plt> ;kill(pid, 0x28)
0x401daf: mov edi,0x3e8
0x401db4: call 0x400ab0 <usleep@plt>
0x401db9: add DWORD PTR [rbp-0x18],0x1
>>> 0x401dbd: cmp DWORD PTR [rbp-0x18],0x6
<<< 0x401dc1: jle 0x401ce7
0x401dc7: call 0x400a70 <getppid@plt>
0x401dcc: mov esi,0x29
0x401dd1: mov edi,eax
0x401dd3: call 0x400a30 <kill@plt> ;kill(pid, 0x29) >>> check()
0x401dd8: mov edi,0x0
0x401ddd: call 0x400a80 <exit@plt>
分析code
其实当时得到这个还是很奇怪,原本以为这里会是个加密的函数,但是基本都是获取pid, 然后kill,再usleep, 中间有两个循环,然后感觉奇怪查了下kill后面的参数的意义,没找到啥,但是前面的一个sigqueue
函数,是发信号的,然后就开始注意到这个位置了, 去ida找到了这个位置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nz9iKfIR-1582557992335)(rec/image-20200224225636469.png)]
同时进去几个函数看了看,最后一个恰好是判断cipher是否正确的函数,基本可以猜到了,就是那边的调用中间的参数就是实际上去调用了这边的这几个函数,其实当时以为是自己调用自身的,没考虑另一个进程,还尝试下断点也没断到还很奇怪,后来官方wp说到是这个子进程接收输入和发送各种符号,其实符号调用到的函数是由父进程去执行的,
就大概知道那段汇编就可以转化成为各个函数调用了,这里又有点莫名像虚拟机的感觉,拿一串字符每个都是个函数,会调用不同东西。 基本示意如下:
call 0x22
call 0x23
call 0x23
call 0x23
for i in range(7):
read(0, buf, 8);
call 0x24
for j in range(0x20):
call 0x25
call 0x26
call 0x27
call 0x28
call 0x29
然后去翻找下相关的函数定义, 基本应该是这个样子:
def encode():
var_3 = 0
var_4 = 0x9e3779b9
var_2 = 0x61616161
var_1 = 0x62626262
for j in range(0x20):
var_2 = (((arr[var_3 & 3] + var_3) ^ (((var_1 >>5) ^ 16 * var_1) + var_1)) + var_2)
var_3 = (var_3 + var_4)
var_1 = (((arr[(var_3 >> 11) & 3] + var_3) ^ (((var_2 >> 5) ^ 16 * var_2) + var_2)) + var_1)
cipher[var_i - 2] = var_2
cipher[var_i - 1] = var_1
而且同时我们也得到判定位置的那段数据:
[665438441, 3131667380, 2864739065, 2751102976, 3656696531, 4215237573, 3553876318, 2257984887, 3858810175, 2562467769, 162186156, 1792812068, 101049092, 1709673591]
这个加密方式是两个数运算后异或再和第三个数相加,我们可以注意到异或的数据和被加密的数据没有关系,其实这个加密就是个不断混合的相加,我们逆向脚本直接先求得异或数据,然后直接减掉就ok,
解密
但这个位置就遇到了我迷茫的地方,写出来的加密和解密脚本测试几个数据都ok,但是对这个解密flag就会有问题,首先是最后结果是负数了,这个位置感觉到是数据溢出了,但是也不知道具体应该要的大小是多大,试了几次都没办法,
官方答案出来一以后了解到是xtea加密方式,然后看对应的代码,看起来是一模一样的,再试,还是不行,emmmm
emmm今天想起来了,他的代码是c, 里面的数据储存溢出就自己没了,emmm,拿c写了下,运行,成功了,
然后改了改,吧所有数据都解密出来了:
#include<stdio.h>
#include<stdint.h>
int decipher(unsigned int num, uint32_t v[2], uint32_t const key[4]){
unsigned int i;
uint32_t v0=v[0], v1=v[1], delta=0x9e3779b9, sum=delta*num;
for(i=0; i< num; i++){
v1 -= (((v0 <<4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}
v[0] = v0;
v[1] = v1;
}
int main(){
uint32_t data[14] = {665438441, 3131667380, 2864739065, 2751102976, 3656696531, 4215237573, 3553876318, 2257984887, 3858810175, 2562467769, 162186156, 1792812068, 101049092, 1709673591};
uint32_t const k[4] = {0x42655f29, 0x9e822efc, 0xda278c92, 0x4e355a62};
for (int i=0; i < 14; i+=2){
uint32_t v[2] = {data[i], data[i+1]};
decipher(0x20, v, k);
printf("0x%x, 0x%x, " , v[0], v[1]);
}
}
然后最后输出出来这个十六进制数, 其实可以看到正确的hga, 最起码也都是可显示字符,emmm
然后python处理一下:
arr = [0x6d616768, 0x6f4e7b65, 0x654e305f, 0x4e34635f, 0x5f30745f, 0x405f6542, 0x6174245f, 0x68542e52, 0x735f7933, 0x4c6c3174, 0x6e41435f, 0x4e34635f, 0x3148735f, 0x7d2e336e]
for i in range(14):
for j in range(4):
print(chr((arr[i]>>j*8)&0xff), end='')
得到flag: