20180CTF g0g0g0 writeup

论坛 期权论坛 脚本     
匿名技术用户   2020-12-23 12:14   20   0

一道很好的题目,从逆向到数学密码到工具

贴几个writeup吧,比较难找:


https://www.jianshu.com/p/d906619a01b7

https://github.com/xiaohuajiao/my-ctf/tree/master/2018/0ctf/g0g0g0

http://westerns.tokyo/writeups/0ctf2018quals.html#g0g0g0

上面的链接,把solve.py改成solve.sage,linux下装个sage,直接跑就可以得到结果

当得到了数学表达式后,可以从这个网站上直接得到结果:

https://math.stackexchange.com/questions/402537/find-integer-in-the-form-fracabc-fracbca-fraccab/409450


首先是go语言的逆向

重点是如何得到数据的处理流程以及函数的逻辑

Entering main.main at /tmp/gogo.go:172:6.
.0:
  t0 = new string (sa)
  t1 = new string (sb)
  t2 = new string (sc)
  t3 = new [1]interface{} (varargs)
  t4 = &t3[0:int]
  t5 = make interface{} <- string ("Input 3 numbers":string)
  *t4 = t5
  t6 = slice t3[:]
  t7 = fmt.Println(t6...)


  t8 = new [1]interface{} (varargs)
  t9 = &t8[0:int]
  t10 = make interface{} <- *string (t0)
  *t9 = t10
  t11 = slice t8[:]
  t12 = fmt.Scanf("%s":string, t11...)

  t13 = new [1]interface{} (varargs)
  t14 = &t13[0:int]
  t15 = make interface{} <- *string (t1)
  *t14 = t15
  t16 = slice t13[:]
  t17 = fmt.Scanf("%s":string, t16...)

  t18 = new [1]interface{} (varargs)
  t19 = &t18[0:int]
  t20 = make interface{} <- *string (t2)
  *t19 = t20
  t21 = slice t18[:]
  t22 = fmt.Scanf("%s":string, t21...)


  t23 = *t0
  t24 = func6(t23)

  t25 = *t1
  t26 = func6(t25)

  t27 = *t2
  t28 = func6(t27)


  t29 = len(t24)
  t30 = t29 == 0:int
  if t30 goto 1 else 4
.4:
  t43 = len(t26)
  t44 = t43 == 0:int
  if t44 goto 1 else 3
.3:
  t41 = len(t28)
  t42 = t41 == 0:int
  if t42 goto 1 else 2
.2:
  t36 = new [1]int (slicelit)
  t37 = &t36[0:int]
  *t37 = 0:int
  t38 = slice t36[:]
  t39 = func1(t24, t38)


  t40 = t39 <= 0:int
  if t40 goto 5 else 8
.8:
  t74 = new [1]int (slicelit)
  t75 = &t74[0:int]
  *t75 = 0:int
  t76 = slice t74[:]
  t77 = func1(t26, t76)


  t78 = t77 <= 0:int
  if t78 goto 5 else 7
.7:
  t69 = new [1]int (slicelit)
  t70 = &t69[0:int]
  *t70 = 0:int
  t71 = slice t69[:]
  t72 = func1(t28, t71)


  t73 = t72 <= 0:int
  if t73 goto 5 else 6
.6:
  t50 = func2(t24, t26)
  t51 = func2(t24, t28)
  t52 = func2(t26, t28)
  t53 = func4(t50, t51)
  t54 = func4(t53, t24)
  t55 = func4(t50, t52)
  t56 = func4(t55, t26)
  t57 = func4(t51, t52)
  t58 = func4(t57, t28)
  t59 = func2(t56, t58)
  t60 = func2(t54, t59)
  t61 = new [1]int (slicelit)
  t62 = &t61[0:int]
  *t62 = 10:int
  t63 = slice t61[:]
  t64 = func4(t51, t52)
  t65 = func4(t50, t64)
  t66 = func4(t63, t65)
  t67 = func1(t60, t66)
  t68 = t67 == 0:int
   if t68 goto 9 else 11

.11:
  t84 = new [1]interface{} (varargs)
  t85 = &t84[0:int]
  t86 = make interface{} <- string ("Wrong! Try again!!":string)
  *t85 = t86
  t87 = slice t84[:]
  t88 = fmt.Println(t87...)

  jump 10
.10:
  return
Leaving main.main.


下面这个.11的函数块是判断某个数位是否大于等于10的:

.11:
  t33 = &t3[t31]
  t34 = *t33
  t35 = t34 >= 10:int
  if t35 goto 13 else 10


下面这个.5的函数块是得到某个字符串的长度的

.5:
  t9 = len(t3)
  t10 = t9 - 1:int
  t11 = slice t3[:t10]
  t12 = len(t11)
  jump 10


t31是当前位置,t36是t31之后的一个位置,t37和t38是取值,看到/10和%10,结合这个写法,可以猜测是大整数的进位

.13:
  t36 = t31 + 1:int
  t37 = &t3[t36]
  t38 = &t3[t31]
  t39 = *t38
  t40 = t39 / 10:int
  t41 = *t37
  t42 = t41 + t40
  *t37 = t42
  t43 = &t3[t31]
  t44 = *t43
  t45 = t44 % 10:int
  *t43 = t45
  jump 10


在程序里看到了这个,但是发现,t12,t17,t22在最后的验证过程中并没有使用到

16103 :   t12 = fmt.Scanf("%s":string, t11...)
19526 :   t17 = fmt.Scanf("%s":string, t16...)
21608 :   t22 = fmt.Scanf("%s":string, t21...)

可以写一个很简单的小工具,对main中的所有函数调用截取出来分析:

numberid = 0

f = open('trace.log')
for lines in f:
 numberid += 1
 if 'func1(' in lines:
  print numberid,':',lines

得到这些数据:

29452 :   t2 = func0(t0, t1)
24374 :   t39 = func1(t24, t38)
24430 :   t77 = func1(t26, t76)
24486 :   t72 = func1(t28, t71)
32573 :   t67 = func1(t60, t66)
24538 :   t50 = func2(t24, t26)
24722 :   t51 = func2(t24, t28)
24907 :   t52 = func2(t26, t28)
29045 :   t59 = func2(t56, t58)
29447 :   t60 = func2(t54, t59)
25026 :   t53 = func4(t50, t51)
25822 :   t54 = func4(t53, t24)
26991 :   t55 = func4(t50, t52)
27497 :   t56 = func4(t55, t26)
27908 :   t57 = func4(t51, t52)
28442 :   t58 = func4(t57, t28)
29997 :   t64 = func4(t51, t52)
30531 :   t65 = func4(t50, t64)
31741 :   t66 = func4(t63, t65)
24133 :   t24 = func6(t23)
24250 :   t26 = func6(t25)
24292 :   t28 = func6(t27)

根据数值的传递关系,t23,t25,t27,t63为四个未知数,func6函数块应该为大数的初始化


Leaving main.func1, resuming main.main at /tmp/gogo.go:236:13.
  t68 = t67 == 0:int
  if t68 goto 9 else 11
.11:
  t84 = new [1]interface{} (varargs)
  t85 = &t84[0:int]
  t86 = make interface{} <- string ("Wrong! Try again!!":string)
  *t85 = t86
  t87 = slice t84[:]
  t88 = fmt.Println(t87...)
这一段说明,t67应该等于0,那么就是func1(t60,t66)=0,容易猜测是比较函数



分析某一个func2函数块:


Leaving main.func0, resuming main.func2 at /tmp/gogo.go:52:18.
  t3 = t2 + 1:int
  t4 = make []int t3 t3
  t5 = len(t4)
  jump 1
.1:
  t6 = phi [0: 0:int, 9: t22] #carry
  t7 = phi [0: -1:int, 9: t8]
  t8 = t7 + 1:int
  t9 = t8 < t5
  if t9 goto 2 else 3
.2:
  t10 = len(a)
  t11 = t8 < t10
  if t11 goto 4 else 5
.4:
  t12 = &a[t8]
  t13 = *t12
  jump 5
.5:
  t14 = phi [2: 0:int, 4: t13] #a_i
  t15 = len(b)
  t16 = t8 < t15
  if t16 goto 6 else 7
.6:
  t17 = &b[t8]
  t18 = *t17
  jump 7
.7:
  t19 = phi [5: 0:int, 6: t18] #b_i
  t20 = t14 + t19
  t21 = t20 + t6
  t22 = t21 / 10:int
  t23 = t21 >= 10:int
  if t23 goto 8 else 9
.8:
  t24 = t21 % 10:int
  jump 9
.9:
  t25 = phi [7: t21, 8: t24] #tmp
  t26 = &t4[t8]
  *t26 = t25
  jump 1

会发现之后都是重复的东西,重点关注的应该是.7这个小部分,明显是加法!

Entering main.func4 at /tmp/gogo.go:104:6.
.0:
  t0 = len(a)
  t1 = len(b)
  t2 = t0 + t1
  t3 = make []int t2 t2
  t4 = len(t3)
  jump 1
.1:
  t5 = phi [0: -1:int, 2: t6]
  t6 = t5 + 1:int
  t7 = t6 < t4
  if t7 goto 2 else 3
这里的.0已经知道了,是大整数乘法

所以,我们知道了func2是大整数加法,func1是大整数比较,func4是大整数乘法


然后得到表达式:

a/(b+c)+b/(c+a)+c/(a+b)=10

这里可以参考知乎:史上最贱的数学题,看上去简单的式子其实本质上是个椭圆曲线


题目总结:

把写程序想象成一棵代码树,编译器的地方是树根,会先找到main,然后main作为子树的树根,继续调用其它函数

这个题的逆向思路:把树前序遍历一下,就可以理解了,就是程序往哪里走,下一条指令就是什么。把树状的框架变成了线性的

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP