180828 逆向-网鼎杯(3-2)
I_like_pack
IDA加载一看啥都没有,再根据题目名显然是个壳
windows下脱壳相对而言麻烦一些,ExeInfoPe查壳啊、各种壳的针对性操作啊啥的
Linux下一方面系统开源随便魔改,另一方面有一个/proc/pid/mem的文件可以直接读取进程的内存,使得dump极为容易
本题放到系统下跑起来后发现如果输入会回显“NO”,而不输入的话大概三秒就会自动结束
这显然是alarm函数的功劳
如果仅是alarm函数的话,其实可以比拼一下手速,毕竟三秒钟还算在人类的反应速度内,另一方面也可以通过sh脚本来执行dump
试了一下cat /proc/pid/mem
会报错,在这里有官方的说明,提供了三种方法
1. 获取maps,根据模块地址来读取程序的内存
2. open mem以后attach目标进程使其暂停,然后即可读
3. gcore pid
尝试了一下,其中第一种方法可以直接使用–因为只是读取mem文件
通过这个脚本
#! /usr/bin/env python
import re
maps_file = open("/proc/self/maps", 'r')
mem_file = open("/proc/self/mem", 'r', 0)
for line in maps_file.readlines(): # for each mapped region
m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+) ([-r])', line)
if m.group(3) == 'r': # if this is a readable region
start = int(m.group(1), 16)
end = int(m.group(2), 16)
mem_file.seek(start) # seek to region start
chunk = mem_file.read(end - start) # read region contents
print chunk, # dump contents to standard output
maps_file.close()
mem_file.close()
操作方法如下:
后台起一个进程可以快速获得pid
然后通过上面的脚本来dump
而第二、第三种方法由于依赖ptrace,对于使用ptrace(TRACE_ME)的进程就会报错
本题中的程序就有使用这个方法来反调试
对于trace_me,有两种方法可以绕过:
1. 直接用调试器启动子进程,断在ptrace之前然后跳过它的执行
不过这种方法会被alarm中断掉,当然也很好绕过,只需要同样跳过alarm的执行就好
缺点有两个,1是浪费时间,2是静态链接很难识别,本题是使用动态链接,相对还算好找
2. 通过LD_PRELOAD来覆盖函数
LD_PRELOAD可以指定加载库,此时如果库中有与其他动态链接库同名的函数将会覆盖,使得原函数失效
这里讲一下后者的操作方法:
unsigned int alarm(unsigned int seconds)
{
;
}
long ptrace()
{
;
}
将上述代码编译成so gcc --shared fake.c -o fake.so
然后通过LD_PRELOAD加载 LD_PRELOAD=./fake.so ./re
此时即可发现alarm失效
然后通过ps或其他方法查到pid后,用gcore pid即可dump
然后通过字符串搜索即可找到main函数
一个数组乱序比对,直接dump即可得到flag
a = [11, 8, 7, 7, 8, 12, 3, 2, 16, 6, 13, 5, 7, 16, 4, 1, 0, 15, 16, 8, 3, 6, 14, 16, 0, 8, 6, 9, 12, 14, 13, 11, 15, 7, 11,14]
for i in range(36):
print(chr(Dword(a[i]*4+0x60f0e0)+45)),
最好的语言
这题问题太大了!
做之前我先去找了web队友过来严阵以待,打开以后根本不是PHP!
打开以后发现跟之前SUCTF的一题一毛一样,给了解析过后的pyc文本
当时写过*可以按照pyc格式进行解析和还原
这个pyc解析网上大概有两种
pyc解析1
pyc解析2
除了都把解析出的字节码删去以外,区别主要在两点
属性标题一种为argcount
另一种为<argcount>xxx</argcount>
排列顺序一种将consts放在names之前,另一种将consts放在之后
我写的脚本仅能针对前者,而本题遇到的后者需要手动修改一下
不过其实难度也不大,通过正则替换还是比较容易的
脚本在这里
解析得到pyc,然后在线反编译即可得到python源码
import base64
from hashlib import md5
import random
import string
f = 'flag{*******}'
def _(b):
o = ''.join(random.sample(string.digits, 4))
s = ''
for i in range(len(b)):
s += chr(ord(b[i]) ^ ord(o[i % 4]))
return s
def ____(a):
___ = md5()
___.update(a)
return ___.digest()
e = _(f[:12]) + ____(f[12:19]) + _(f[19:])
print base64.b64encode(e)
e = 'U1VQU05pSHdqCEJrQu7FS7Vngk1OTQ58qqghXmt2AUdrcFBBUEU='
前后两段是通过随机数异或出来的,中间一段则是md5
分析一下可以知道_
函数加密后长度不变,因此12~12+32
扔去解密得到613u21i
前一段由”flag”得到key=”5914”
后一段由结尾字符”}”得到key的第二位为8
其余位**,筛选出在ASCII范围内的,然后肉眼选择看起来像的
import base64
import random
import string
# "5914"
def foo_a(b):
o = ''.join(random.sample(string.digits, 4))
o = "5914"
s = ''
for i in range(len(b)):
s += chr(ord(b[i]) ^ ord(o[i % 4]))
return s
def foo_b(b):
r = []
for i in range(10):
for j in range(10):
if(j==i):
continue
for k in range(10):
if(k==j or k==i):
continue
if(k==8 or i==8 or j==8):
continue
o = str(i) + "8" + str(j) + str(k)
s = ''
for l in range(len(b)):
if(ord(b[l]) ^ ord(o[l % 4])>127):
break
s += chr(ord(b[l]) ^ ord(o[l % 4]))
else:
r.append((o, s))
return r
e = b'U1VQU05pSHdqCEJrQu7FS7Vngk1OTQ58qqghXmt2AUdrcFBBUEU='
e = base64.b64decode(e)
print(e)
a = e[:12]
b = e[12:12+16]
c = e[12+16:]
print(foo_a(a.decode()),end='')
print("613u21i",end='')
print()
for i in (foo_b(c.decode())):
print(i)
上一篇: 免费的代理IP池
下一篇: .net core使用redis