首页
社区
课程
招聘
[原创] 第四题西西弗斯的滚石未完成的wp
发表于: 2020-4-23 12:49 5399

[原创] 第四题西西弗斯的滚石未完成的wp

2020-4-23 12:49
5399

我这么喜欢写前言不仅仅是为了凑字数

本来一直打算做防守方的, 因为当攻击方的话会暴露我题啥都做不出来的事实. 每次比赛都只做个签到题凑凑人数. 但是这一题到第三天早上攻破人数还是 0, 我作为防守方压力很大啊, 强烈怀疑 ccfer, 风神, 以及 riatre 这几天疯狂划水没认真玩. 这一题刚出来的时候可能大爷在打 Plaid CTF, 然后打完 Plaid CTF 之后大爷可能觉得自己没有 ak 有点不开心就没来做看雪 CTF 了. 希望大爷做我那题的时候也手下留情 (强烈暗示想 py 交易.

 

总之, 各大高手都没出马, 我作为无名小卒只能上上滥竽充数了. 昨天做了一天, 等到昨晚被我妈强行拉出门散步的时候我就知道我这题肯定是做不完的了. 今天早上起来打开电脑确认了一下这个事实, 也就干脆放弃了. 不过虽然没来得及搞完, 我大概把 smc 的部分给去掉了. 想想没人做出来的题可能也没人写 writeup, 题目下面空荡荡的有点不好看, 我就献丑一下, 简单说一下我的思路和进度. 感觉如果再有一天是应该是可以做出来的, 再次谴责大爷不出手, 没给我膜的机会.

 

(散个步都能散出来 SSA)
图片描述

这个已经不是前言了但是废话依然很多

先说一下目前的进展, 就是大概把程序每一个大循环的验证逻辑抽出来编译成了一个新的 binary, 可能比源程序好看一点, 我上传个附件扔到下面. 拖进 IDA 里面大概长这样:
图片描述
这个函数已经没有分支啥的了, 但是还有不少混淆没去掉, 可以隐隐约约看出来有个 Feistel 的结构. 反编译出来有 2k 行, 不太是人写出来的, 看上去有明显的规律性, 应该还是程序生成的. 至于怎么得到这个验证逻辑的就看下面详细一点的废话吧.

大标题用得久了人就会情不自禁的想换成小标题

首先打开 IDA, 简单看了下逻辑, 发现大概是一个关键函数把序列号变换成用户名, 点击去一看发现一大堆乱七八糟的长得不像代码没被标记成代码也不太能被标记成代码的玩意. 中国有一句古话, (叫做闷声大发...), 是如果一个玩意长得不像鸭子, 叫的不像鸭子, 走的也不像鸭子, 那它就不是鸭子. 好, 果断(删掉)关掉 IDA.

 

然后打开 x64dbg, 进入函数调试了一下, 首先第一个反应是这个函数真他喵的慢. 直接运行了一下确认了不是调试器的问题, (那应该就是我电脑太便宜了的问题需要换电脑了). 简单跟了一下 smc, 只觉人力有时尽, ret 无可期. 试着把 rsp 设到了一个页的边缘, 果然找到了 ret, 但是周围白茫茫一片全是乱码. 感觉这样下去迟早会(精尽人亡)精疲力竭, 于是放弃了手工调试.

 

不过调试了一会也有所得, 就是这个函数输入是 rcx, rdx, 输出也是 rcx, rdx, 似乎只用到了栈空间. 在开头某个位置打断点, 发现会被触发很多次, 仔细观察发现只有 rcx 和 rdx 改变了, 连栈都被填充成 0x12345678. 但是程序是没有死循环的, 那么肯定有条件使它跳出循环, 而这个条件多半和(神奇的海螺) rcx rdx 无关. 仔细观察发现每一次循环的时候代码都会有少许的变动, 把代码区域 dump 下来 diff 了一下发现变动还不少, 但是都是 smc 解密后对应到指令常数的改变. 至此这个程序大概就有个(没什么卵用的)印象了.

(有一说一加括号还真的挺好玩的我已经爱上它了)

作为一个只会用工具的菜鸡, 常用工具用完了就来试试不常用的工具. 首先我做了几点假设, 这个函数的行为多半只是一个数据变换, 和 block cipher 行为类似, 就是说不同输入所对应的路径应该是一样的(虽然后来发现不完全一样). 然后就是这个函数多半比较老实, 没有出现调用其他函数或者 syscall 的调皮行为, 再次就是假设这个函数只用到自己的代码和栈的一小部分的内存(后面这两个假设都得到了验证). 最后就是假设该函数的每个大循环都大同小异了. 对于这种冰冷的数据变换机器, 有一个叫 Triton 的 DBI 框架其实还挺适合的.

 

Triton 的介绍就懒得介绍了, 文档勉强还算丰富, 示例倒是挺多的. 下下来编译安装, 把程序 load 进去, 发现一个循环要跑 20 来分钟... 不过还算可以接受, 毕竟完全可以搞个 64 核的服务器并行跑不同轮的循环.

 

跑完之后, Triton 会包含一个输出到输入的 AST 结构, 我们把这个结构稍稍处理一下就能转换成 LLVM IR, 然后就能依赖我们强大的编译器的优化来帮我们去掉混淆了. 但是在处理的过程中, 发现 Triton 对于简单的不透明谓词的处理不是很好, 比如 if(1 == 0) {balabala(a)} else {1} 这种, 其实应该是返回一个常量, 但是 triton 会认为它是一个 symbolic expression. 看了看它的代码后, 发现只要简单改改 src/libtriton/ast/ast.cpp 就可以了:

1
2
3
4
5
for (triton::uint32 index = 0; index < this->children.size(); index++) {
  this->children[index]->setParent(this);
  this->symbolized |= this->children[index]->isSymbolized();
  this->level = std::max(this->children[index]->getLevel() + 1, this->level);
}

它判断 symbolized 的时候只要有一个 child 是 symbolized 就行, 这个太笨了. 把它改成:

1
2
3
4
5
6
7
8
9
10
for (triton::uint32 index = 0; index < this->children.size(); index++) {
  this->children[index]->setParent(this);
  //this->symbolized |= this->children[index]->isSymbolized();
  this->level = std::max(this->children[index]->getLevel() + 1, this->level);
}
if(this->children[0]->isSymbolized()){
  this->symbolized = true;
}else{
  this->symbolized = this->children[0]->evaluate() ? this->children[1]->isSymbolized() : this->children[2]->isSymbolized();
}

除了这种不透明谓词导致的分支, 还有一种分支来源于 a ^ 0x100 = if(a & 0x100) {a-0x100} else {a|0x100} 这一类的混淆, 于是在结算分支的时候顺便也把它干掉了.
然后发现 arybo 对 mod 支持不行, 但是里面所有的 mod 全是 mod 0x40, 所以就干脆当成 & 0x3F 来处理了.
其实还发现好多问题, 但是都因为这个程序一些 specific 的因素慢慢排除掉了. 所以下面的代码直接拿过来用到其他程序上肯定是行不通的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
import sys,resource
sys.setrecursionlimit(50000)
resource.setrlimit(resource.RLIMIT_STACK, (resource.RLIM_INFINITY, resource.RLIM_INFINITY))
from triton import *
 
from arybo.lib.mba_exprs import ExprCond
 
import ctypes
import os
import random
import string
import struct
import time
import lief
 
ctx = TritonContext()
ctx.setArchitecture(ARCH.X86_64)
ctx.setMode(MODE.ALIGNED_MEMORY, True)
ctx.setMode(MODE.ONLY_ON_SYMBOLIZED, True)
ctx.setAstRepresentationMode(AST_REPRESENTATION.PYTHON)
 
func2 = open("func2", "rb").read()
stack2 = open("stack2", "rb").read()
reg2=[610839792,
 916259688,
 2147483647,
 2147483647,
 1221679584,
 1527099480,
 1832519376,
 2137939272,
 2443359168,
 2748779064,
 3054198960,
 3359618856,
 3970458648,
 3970458648,
 5368808131]
regs=[ctx.registers.rax, ctx.registers.rbx, ctx.registers.rbp, ctx.registers.rsp, ctx.registers.rsi, ctx.registers.rdi, ctx.registers.r8, ctx.registers.r9, ctx.registers.r10, ctx.registers.r11, ctx.registers.r12, ctx.registers.r13, ctx.registers.r14, ctx.registers.r15, ctx.registers.rip]
 
ctx.setConcreteMemoryAreaValue(0x1400011E0, func2)
ctx.setConcreteMemoryAreaValue(0x7fffffff-len(stack2), stack2)
 
for i in range(len(regs)):
    ctx.setConcreteRegisterValue(regs[i], reg2[i])
 
ctx.concretizeAllRegister()
ctx.concretizeAllMemory()
 
ctx.setConcreteRegisterValue(ctx.registers.rcx, 0x68A046A2B0886BFE)
ctx.setConcreteRegisterValue(ctx.registers.rdx, 0x92D4E06F09775269)
 
in0=ctx.symbolizeRegister(ctx.registers.rcx)
in1=ctx.symbolizeRegister(ctx.registers.rdx)
 
pc = ctx.getConcreteRegisterValue(ctx.registers.rip)
while pc:
    opcodes = ctx.getConcreteMemoryAreaValue(pc, 16)
    instruction = Instruction()
    instruction.setOpcode(opcodes)
    instruction.setAddress(pc)
    if ctx.processing(instruction) == False:
        debug('[-] Instruction not supported: %s' %(str(instruction)))
        break
    if instruction.getType() == OPCODE.X86.RET:
        print("return")
        break
    pc = ctx.getConcreteRegisterValue(ctx.registers.rip)
    if pc == 0x0000001400182C3:
        print("Loop")
        break
 
out0=ctx.getSymbolicRegister(ctx.registers.rcx)
out1=ctx.getSymbolicRegister(ctx.registers.rdx)
 
from arybo.lib import MBA, MBAVariable, flatten
import arybo.lib.mba_exprs as EX
import operator
import six
from six.moves import reduce
mba=MBA(64)
 
def _get_mba(n,use_esf):
    mba = MBA(n)
    mba.use_esf = use_esf
    return mba
 
def my_tritonast2arybo(e, use_exprs=True, use_esf=False, context=None):
    ''' Convert a subset of Triton's AST into Arybo's representation
    Args:
        e: Triton AST
        use_esf: use ESFs when creating the final expression
        context: dictionnary that associates Triton expression ID to arybo expressions
    Returns:
        An :class:`arybo.lib.MBAVariable` object
    '''
 
    children_ = e.getChildren()
    children = (my_tritonast2arybo(c,use_exprs,use_esf,context) for c in children_)
    reversed_children = (my_tritonast2arybo(c,use_exprs,use_esf,context) for c in reversed(children_))
 
    Ty = e.getType()
    if Ty == AST_NODE.ZX:
        n = next(children)
        v = next(children)
        n += v.nbits
        if n == v.nbits:
            return v
        return v.zext(n)
    if Ty == AST_NODE.SX:
        n = next(children)
        v = next(children)
        n += v.nbits
        if n == v.nbits:
            return v
        return v.sext(n)
    if Ty == AST_NODE.INTEGER:
        return e.getInteger()
    if Ty == AST_NODE.BV:
        cst = next(children)
        nbits = next(children)
        if use_exprs:
            return EX.ExprCst(cst, nbits)
        else:
            return _get_mba(nbits,use_esf).from_cst(cst)
    if Ty == AST_NODE.EXTRACT:
        last = next(children)
        first = next(children)
        v = next(children)
        return v[first:last+1]
    if Ty == AST_NODE.CONCAT:
        if use_exprs:
            return EX.ExprConcat(*list(reversed_children))
        else:
            return flatten(reversed_children)
    if Ty == AST_NODE.VARIABLE:
        name = e.getSymbolicVariable().getName()
        ret = _get_mba(e.getBitvectorSize(),use_esf).var(name)
        if use_exprs:
            ret = EX.ExprBV(ret)
        return ret
    if Ty == AST_NODE.REFERENCE:
        if context is None:
            raise ValueError("reference node without context can't be resolved")
        id_ = e.getSymbolicExpression().getId()
        ret = context.get(id_, None)
        if ret is None:
            print("Evaluate: ", e.getSymbolicExpression())
            return EX.ExprCst(e.evaluate(), 64)
            #raise ValueError("expression id %d not found in context" % id_)
        return ret
    if Ty == AST_NODE.LET:
        # Alias
        # djo: "c'est pas utilise osef"
        raise ValueError("unsupported LET operation")
 
    # Logical/arithmetic shifts
    shifts = {
        AST_NODE.BVASHR: lambda a,b: a.ashr(b),
        AST_NODE.BVLSHR: lambda a,b: a.lshr(b),
        AST_NODE.BVSHL:  operator.lshift,
        AST_NODE.BVROL:  lambda x,n: x.rol(n),
        AST_NODE.BVROR:  lambda x,n: x.ror(n)
    }
    shift = shifts.get(Ty, None)
    if not shift is None:
        v = next(children)
        n = next(children)
        return shift(v,n)
 
    # Unary op
    unops = {
        AST_NODE.BVNOT: lambda x: ~x,
        AST_NODE.LNOT:  lambda x: ~x,
        AST_NODE.BVNEG: operator.neg
    }
    unop = unops.get(Ty, None)
    if not unop is None:
        return unop(next(children))
 
    binops = {
        AST_NODE.BVADD:  operator.add,
        AST_NODE.BVSUB:  operator.sub,
        AST_NODE.BVAND:  operator.and_,
        AST_NODE.BVOR:   operator.or_,
        AST_NODE.BVXOR:  operator.xor,
        AST_NODE.BVMUL:  operator.mul,
        AST_NODE.BVNAND: lambda x,y: ~(x&y),
        AST_NODE.BVNOR:  lambda x,y: ~(x|y),
        AST_NODE.BVXNOR: lambda x,y: ~(x^y),
        AST_NODE.BVUDIV: lambda x,y: x.udiv(y),
        AST_NODE.BVSDIV: lambda x,y: x.sdiv(y),
        AST_NODE.BVUREM: lambda x,y: x.urem(y),
        AST_NODE.BVSREM: lambda x,y: x.srem(y),
        AST_NODE.BVSMOD: lambda x,y: x & 0x3F, # Temp sol but it works
        AST_NODE.LAND:   operator.and_,
        AST_NODE.LOR:    operator.or_
    }
    binop = binops.get(Ty, None)
    if not binop is None:
        if Ty == AST_NODE.BVSMOD:
            assert e.getChildren()[1].getChildren()[0].getInteger() == 0x40
        return reduce(binop, children)
 
    # Logical op
    lops = {
        AST_NODE.EQUAL:    lambda x,y: EX.ExprCmpEq(x,y),
        AST_NODE.DISTINCT: lambda x,y: EX.ExprCmpNeq(x,y),
        AST_NODE.BVUGE:    lambda x,y: EX.ExprCmpGte(x,y,False),
        AST_NODE.BVUGT:    lambda x,y: EX.ExprCmpGt(x,y,False),
        AST_NODE.BVULE:    lambda x,y: EX.ExprCmpLte(x,y,False),
        AST_NODE.BVULT:    lambda x,y: EX.ExprCmpLt(x,y,False),
        AST_NODE.BVSGE:    lambda x,y: EX.ExprCmpGte(x,y,True),
        AST_NODE.BVSGT:    lambda x,y: EX.ExprCmpGt(x,y,True),
        AST_NODE.BVSLE:    lambda x,y: EX.ExprCmpLte(x,y,True),
        AST_NODE.BVSLT:    lambda x,y: EX.ExprCmpLt(x,y,True)
    }
    lop = lops.get(Ty, None)
    if not lop is None:
        return reduce(lop, children)
 
    # Conditional
    if Ty != AST_NODE.ITE:
        raise ValueError("unsupported node type %s" % str(Ty))
    cond = next(children)
    ifc = next(children)
    elsec = next(children)
    if not e.getChildren()[0].isSymbolized():
        print(e.getParents())
        if e.getChildren()[0].evaluate():
            return ifc
        else:
            return elsec
    #print(e.getChildren()[1].getChildren()[1])
    #return EX.ExprCond(cond, ifc, elsec)
    return my_tritonast2arybo(e.getChildren()[1].getChildren()[0],use_exprs,use_esf,context) ^ my_tritonast2arybo(e.getChildren()[1].getChildren()[1],use_exprs,use_esf,context)
 
def my_tritonexprs2arybo(exprs):
    context = {}
    e = None
    for id_,e in sorted(exprs.items()):
        if id_ in context:
            raise ValueError("expression id %d is set multiple times!" % id_)
        e = my_tritonast2arybo(e.getAst(), True, False, context)
        context[id_] = e
    return e, context
 
astctx=ctx.getAstContext()
dest0=astctx.bv(0xA11F5C1A02B84180, 64)
dest1=astctx.bv(0xB42247957CDD5A66, 64)
eq0=astctx.equal(dest0, out0.getAst())
eq1=astctx.equal(dest1, out1.getAst())
flag=astctx.bvand(eq0,eq1)
 
sep=ctx.getSymbolicExpressions()
e,symcontext=my_tritonexprs2arybo(sep)
 
 
my_tritonast2arybo(eq0, True, False, symcontext)
my_tritonast2arybo(eq1, True, False, symcontext)
e=my_tritonast2arybo(flag, True, False, symcontext)
 
in0=tritonast2arybo(ctx.getAstContext().variable(ctx.getSymbolicVariable(0)))
in1=tritonast2arybo(ctx.getAstContext().variable(ctx.getSymbolicVariable(1)))
 
debug_temp = None
try:
    import llvmlite.ir as ll
    import llvmlite.binding as llvm
    import ctypes
    llvmlite_available = True
    __llvm_initialized = False
except ImportError:
    llvmlite_available = False
 
import six
import collections
import hashlib
 
import arybo.lib.mba_exprs as EX
from arybo.lib.exprs_passes import lower_rol_ror, CachePass
 
def IntType(n):
    return ll.IntType(int(n))
 
class ToLLVMIr(CachePass):
    def __init__(self, sym_to_value, IRB):
        super(ToLLVMIr,self).__init__()
        self.IRB = IRB
        self.sym_to_value = sym_to_value
        self.values = {}
 
    def visit_wrapper(self, e, cb):
        ret = super(ToLLVMIr, self).visit_wrapper(e, cb)
        if not isinstance(ret, tuple):
            return (ret,self.IRB.block)
        else:
            return ret
 
    def visit_value(self, e):
        return EX.visit(e, self)[0]
 
    def visit_Cst(self, e):
        return ll.Constant(IntType(e.nbits), e.n)
 
    def visit_BV(self, e):
        name = e.v.name
        value = self.sym_to_value.get(name, None)
        if value is None:
            raise ValueError("unable to map BV name '%s' to an LLVM value!" % name)
        # TODO: check value bit-size
        #ret,nbits = value
        #if e.nbits != nbits:
        #    raise ValueError("bit-vector is %d bits, expected %d bits" % (e.nbits, nbits))
        return value
 
    def visit_Not(self, e):
        return self.IRB.not_(self.visit_value(e.arg))
 
    def visit_ZX(self, e):
        return self.IRB.zext(self.visit_value(e.arg), IntType(e.n))
 
    def visit_SX(self, e):
        return self.IRB.sext(self.visit_value(e.arg), IntType(e.n))
 
    def visit_Concat(self, e):
        # Generate a suite of OR + shifts
        # TODO: pass that lowers concat
        arg0 = e.args[0]
        ret = self.visit_value(arg0)
        type_ = IntType(e.nbits)
        ret = self.IRB.zext(ret, type_)
        cur_bits = arg0.nbits
        for a in e.args[1:]:
            cur_arg = self.IRB.zext(self.visit_value(a), type_)
            ret = self.IRB.or_(ret,
                self.IRB.shl(cur_arg, ll.Constant(type_, cur_bits)))
            cur_bits += a.nbits
        return ret
 
    def visit_Slice(self, e):
        # TODO: pass that lowers slice
        ret = self.visit_value(e.arg)
        idxes = e.idxes
        # Support only sorted indxes for now
        if idxes != list(range(idxes[0], idxes[-1]+1)):
            raise ValueError("slice indexes must be continuous and sorted")
        if idxes[0] != 0:
            ret = self.IRB.lshr(ret, ll.Constant(IntType(e.arg.nbits), idxes[0]))
        return self.IRB.trunc(ret, IntType(len(idxes)))
 
    def visit_Broadcast(self, e):
        # TODO: pass that lowers broadcast
        # left-shift to get the idx as the MSB, and them use an arithmetic
        # right shift of nbits-1
        type_ = IntType(e.nbits)
        ret = self.visit_value(e.arg)
        ret = self.IRB.zext(ret, type_)
        ret = self.IRB.shl(ret, ll.Constant(type_, e.nbits-e.idx-1))
        return self.IRB.ashr(ret, ll.Constant(type_, e.nbits-1))
 
    def visit_nary_args(self, e, op):
        return op(*(self.visit_value(a) for a in e.args))
 
    def visit_BinaryOp(self, e):
        ops = {
            EX.ExprAdd: self.IRB.add,
            EX.ExprSub: self.IRB.sub,
            EX.ExprMul: self.IRB.mul,
            EX.ExprShl: self.IRB.shl,
            EX.ExprLShr: self.IRB.lshr,
            EX.ExprAShr: self.IRB.ashr
        }
        op = ops[type(e)]
        return self.visit_nary_args(e, op)
 
    def visit_Div(self, e):
        return self.visit_nary_args(e, self.IRB.sdiv if e.is_signed else self.IRB.udiv)
 
    def visit_Rem(self, e):
        return self.visit_nary_args(e, self.IRB.srem if e.is_signed else self.IRB.urem)
 
    def visit_NaryOp(self, e):
        ops = {
            EX.ExprXor: self.IRB.xor,
            EX.ExprAnd: self.IRB.and_,
            EX.ExprOr: self.IRB.or_,
        }
        op = ops[type(e)]
        return self.visit_nary_args(e, op)
 
    def visit_Cmp(self, e):
        f = self.IRB.icmp_signed if e.is_signed else self.IRB.icmp_unsigned
        cmp_op = {
            EX.ExprCmp.OpEq:  '==',
            EX.ExprCmp.OpNeq: '!=',
            EX.ExprCmp.OpLt:  '<',
            EX.ExprCmp.OpLte: '<=',
            EX.ExprCmp.OpGt:  '>',
            EX.ExprCmp.OpGte: '>='
        }
        return f(cmp_op[e.op], self.visit_value(e.X), self.visit_value(e.Y))
 
    def visit_Cond(self, e):
        global debug_temp
        cond = self.visit_value(e.cond)
        selfbb = self.IRB.basic_block
        bb_name = selfbb.name
        ifbb = self.IRB.append_basic_block(hashlib.sha256((bb_name + ".if").encode("utf-8")).hexdigest())
        elsebb = self.IRB.append_basic_block(hashlib.sha256((bb_name + ".else").encode("utf-8")).hexdigest())
        endbb = self.IRB.append_basic_block(hashlib.sha256((bb_name + ".endif").encode("utf-8")).hexdigest())
        #ifb = self.IRB.append_basic_block(bb_name + ".if")
        #elseb = self.IRB.append_basic_block(bb_name + ".else")
        #endb = self.IRB.append_basic_block(bb_name + ".endif")
        self.IRB.cbranch(cond, ifbb, elsebb)
 
        self.IRB.position_at_end(ifbb)
        ifv,ifb = EX.visit(e.a, self)
        self.IRB.branch(endbb)
 
        empty = False
        inst = selfbb.terminator.operands[-2].instructions
        if len(inst) == 1 and inst[0].opname == "br":
            empty = True
            try:
                assert inst[0].operands[0] == endbb
                self.IRB.position_at_end(selfbb)
                selfbb.terminator.operands[-2] = endbb
                selfbb.function.blocks.remove(ifbb)
                debug_temp = selfbb
            except:
                debug_temp = inst
                print("Except")
                empty = False
 
        self.IRB.position_at_end(elsebb)
        elsev,elseb = EX.visit(e.b, self)
        self.IRB.branch(endbb)
 
        self.IRB.position_at_end(endbb)
        ret = self.IRB.phi(IntType(e.nbits))
        if not empty:
            ret.add_incoming(ifv, ifb)
        else:
            ret.add_incoming(ifv, selfbb)
        ret.add_incoming(elsev, elseb)
        return ret,endbb
 
def llvm_get_target(triple_or_target=None):
    global __llvm_initialized
    if not __llvm_initialized:
        # Lazy initialisation
        llvm.initialize()
        llvm.initialize_all_targets()
        llvm.initialize_all_asmprinters()
        __llvm_initialized = True
 
    if isinstance(triple_or_target, llvm.Target):
        return triple_or_target
    if triple_or_target is None:
        return llvm.Target.from_default_triple()
    return llvm.Target.from_triple(triple_or_target)
 
def _create_execution_engine(M, target):
    target_machine = target.create_target_machine()
    engine = llvm.create_mcjit_compiler(M, target_machine)
    return engine
 
def to_llvm_ir(exprs, sym_to_value, IRB):
    if not llvmlite_available:
        raise RuntimeError("llvmlite module unavailable! can't assemble to LLVM IR...")
 
    if not isinstance(exprs, collections.abc.Iterable):
        exprs = (exprs,)
 
    ret = None
    visitor = ToLLVMIr(sym_to_value, IRB)
    for e in exprs:
        e = lower_rol_ror(e)
        ret = visitor.visit_value(e)
    return ret
 
def to_llvm_function(exprs, vars_, name="__arybo"):
    if not llvmlite_available:
        raise RuntimeError("llvmlite module unavailable! can't assemble to LLVM IR...")
 
    if not isinstance(exprs, collections.abc.Iterable):
        exprs = (exprs,)
 
    M = ll.Module()
    args_types = [IntType(v.nbits) for v in vars_]
    fntype = ll.FunctionType(IntType(exprs[-1].nbits), args_types)
    func = ll.Function(M, fntype, name=name)
    func.attributes.add("nounwind")
    BB = func.append_basic_block()
 
    IRB = ll.IRBuilder()
    IRB.position_at_end(BB)
 
    sym_to_value = {}
    for i,v in enumerate(vars_):
        arg = func.args[i]
        arg.name = v.name
        sym_to_value[v.name] = arg
    ret = to_llvm_ir(exprs, sym_to_value, IRB)
    IRB.ret(ret)
    return M
 
M = to_llvm_function(e,[in0.v, in1.v])
open("m2.ll", "w").write(str(M).replace('__arybo', 'func').replace('unknown-unknown-unknown', 'x86_64-pc-linux-gnu'))

上面 func2 和 stack2 都是 dump 下来的栈和函数代码的区域, 对于每一次循环都 dump 一次, 这里用的第二次循环的数据. 总之, 把 Triton 的 AST 转换成 LLVM IR 之后, 加个 main.c 编译一下就可以得到开头的结果了.

如果还有下一步的话我可能会做些什么

当然是膜大爷啦!

图片描述
diff 了一下不同次循环, 代码都是类似的, 只有数据不同, 理论上只要能解一个循环就可以了. 看了下 IDA 的 F5 结果, 有 Feistel 结构的影子. 大概三种方法, 一种是模式匹配, 可以用正则也可以用 binary ninja 的 HLIL 之类的, 因为代码有很强的规律性. 第二种方法是用 reachable 和 liveness 的分析, 把每一层 Feistel 的两个变量找出来. 第三种方法的话... 就是找 Pizza 秒了他!

为什么不找大爷

被大爷发现我这么菜会被踢出粉丝团的.

用小标题再次谴责大佬们这几天疯狂划水的行为!!!

为什么不用大标题? 因为弱鸡只敢小声BB


[注意]看雪招聘,专注安全领域的专业人才平台!

最后于 2020-4-23 16:43 被半盲道人编辑 ,原因:
上传的附件:
收藏
免费
支持
分享
最新回复 (1)
雪    币: 2761
活跃值: (13354)
能力值: ( LV12,RANK:312 )
在线值:
发帖
回帖
粉丝
2
你脑海中浮现了刘大爷对你笑的面容
2020-4-23 13:31
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册