上饶ctf 逆向 pvm.exe

昨天帮别人看了道逆向题,涉及到pyc逆向,由于是python3.12,pycdc无法全部反编译,只能根据python字节码来手撕源码。PS:之前没搞过python字节码逆向,今天狠狠的恶补了下

1、利用pyinstxtractor.py反编译出pyc文件:python3 pyinstxtractor.py pvm.exe

2、利用pycdc可以看到,部分源码是无法反编译成功的

3、利用pycdas查看字节码(这里只列出关键部分)

                
                0       RESUME                          0
                2       LOAD_GLOBAL                     1: NULL + input
                12      LOAD_CONST                      1: 'flag:'
                14      CALL                            1
                22      STORE_FAST                      0: flag
                24      LOAD_CONST                      2: 0
                26      LOAD_CONST                      3: ('exit',)
                28      IMPORT_NAME                     1: sys
                30      IMPORT_FROM                     2: exit
                32      STORE_FAST                      1: exit
                34      POP_TOP                         
                36      LOAD_GLOBAL                     7: NULL + len
                46      LOAD_FAST                       0: flag
                48      CALL                            1
                56      LOAD_CONST                      4: 42 //判断flag是否为42位
                58      COMPARE_OP                      55 (!=)  
                62      POP_JUMP_IF_FALSE               13 (to 90)
                64      LOAD_CONST                      2: 0
                66      LOAD_CONST                      3: ('exit',)
                68      IMPORT_NAME                     1: sys
                70      IMPORT_FROM                     2: exit
                72      STORE_FAST                      1: exit
                74      POP_TOP                         
                76      PUSH_NULL                       
                78      LOAD_FAST                       1: exit
                80      CALL                            0
                88      POP_TOP                         
                90      LOAD_FAST                       0: flag
                92      LOAD_CONST                      0: None
                94      LOAD_CONST                      5: 5
                96      BINARY_SLICE                    
                98      LOAD_CONST                      6: 'flag{' //输入是否以'flag{'开头
                100     COMPARE_OP                      55 (!=)
                104     POP_JUMP_IF_FALSE               8 (to 122)
                106     PUSH_NULL                       
                108     LOAD_FAST                       1: exit
                110     LOAD_CONST                      2: 0
                112     CALL                            1
                120     POP_TOP                         
                122     LOAD_FAST                       0: flag
                124     LOAD_CONST                      7: 41
                126     BINARY_SUBSCR                   
                130     LOAD_CONST                      8: '}' //输入是否以'{'结尾
                132     COMPARE_OP                      55 (!=)
                136     POP_JUMP_IF_FALSE               8 (to 154)
                138     PUSH_NULL                       
                140     LOAD_FAST                       1: exit
                142     LOAD_CONST                      2: 0
                144     CALL                            1
                152     POP_TOP                         
                154     LOAD_FAST                       0: flag
                156     LOAD_CONST                      5: 5
                158     LOAD_CONST                      7: 41
                160     BINARY_SLICE                    
                162     STORE_FAST                      2: s //将输入的第5至40位赋给s
                164     NOP                             
                166     LOAD_GLOBAL                     9: NULL + map
                176     LOAD_CONST                      9: <CODE> <lambda>//没看懂,但大概意思就是map+lambda,即s=s.split('-')
                178     MAKE_FUNCTION                   0
                180     LOAD_FAST                       2: s
                182     LOAD_ATTR                       11: split
                202     LOAD_CONST                      10: '-'
                204     CALL                            1
                212     CALL                            2
                220     GET_ITER                        //220-240类似于一个for循环,将s.split('-')的值赋给一个列表
                222     LOAD_FAST_AND_CLEAR             3: i
                224     SWAP                            2
                226     BUILD_LIST                      0
                228     SWAP                            2
                230     FOR_ITER                        4 (to 240)
                234     STORE_FAST                      3: i
                236     LOAD_FAST                       3: i
                238     LIST_APPEND                     2
                240     JUMP_BACKWARD                   6 (to 230)
                242     END_FOR                         
                244     STORE_FAST                      0: flag //flag=s.split('-')
                246     STORE_FAST                      3: i
                248     LOAD_CONST                      2: 0
                250     LOAD_CONST                      2: 0
                252     LOAD_CONST                      2: 0
                254     LOAD_CONST                      2: 0
                256     LOAD_CONST                      2: 0
                258     LOAD_CONST                      2: 0
                260     LOAD_CONST                      2: 0
                262     LOAD_CONST                      2: 0
                264     LOAD_CONST                      2: 0
                266     LOAD_CONST                      2: 0
                268     LOAD_CONST                      11: ('eax', 'ebx', 'ecx', 'edx', 'eip', 'e1', 'e2', 'e3', 'e4', 'e5')
                270     BUILD_CONST_KEY_MAP             10
                272     STORE_FAST                      4: reg // reg={'eax':0,'ebx':0,...}
                274     LOAD_GLOBAL                     13: NULL + cstack
                284     CALL                            0
                292     STORE_FAST                      5: stack
                294     LOAD_FAST                       4: reg
                296     LOAD_FAST                       5: stack
                298     BUILD_LIST                      2
                300     STORE_FAST                      6: cpu //[reg,stack]
                302     LOAD_GLOBAL                     15: NULL + init
                312     LOAD_FAST                       6: cpu
                314     LOAD_FAST                       0: flag
                316     CALL                            2 //调用init(cpu,flag), init的返回值可以看下面的源码,这里就不多分析
                324     POP_TOP                         
                326     BUILD_LIST                      0
                328     LOAD_CONST                      12: (145, 146, 2, 64, 146, 32768, 32, 146, 792896, 16, 146, 23751, 32, 146, 400000, 64, 146, 297, 32, 146, 200000, 64, 225, 145, 146, 324, 48, 146, 1368, 32, 146, 40000, 64, 226, 149, 145, 48, 32, 80, 146, 14, 64, 146, 197, 32, 146, 1000, 64, 227, 145, 146, 521197, 48, 146, 88123111, 80, 146, 554259, 32, 146, 600000, 64, 146, 99, 32, 146, 100, 64, 228, 145, 146, 45499, 32, 80, 146, 19499, 32, 146, 675, 64, 146, 5519, 32, 146, 10000, 64, 229, 131)
                330     LIST_EXTEND                     1 
                332     STORE_FAST                      7: all_code //all_code=[145,146,2,...]
                334     LOAD_GLOBAL                     17: NULL + run
                344     LOAD_FAST                       7: all_code
                346     LOAD_FAST                       6: cpu
                348     CALL                            2 //调用run(cpu,all_code)
                356     POP_TOP                         
                358     LOAD_FAST                       6: cpu
                360     LOAD_CONST                      2: 0
                362     BINARY_SUBSCR                   
                366     LOAD_CONST                      13: 'e1'
                368     BINARY_SUBSCR                   
                372     LOAD_CONST                      14: 1327
                374     COMPARE_OP                      40 (==) //判断cpu[0]['e1']是否为1327,下面的判断类似
                378     POP_JUMP_IF_FALSE               56 (to 492)
                380     LOAD_FAST                       6: cpu
                382     LOAD_CONST                      2: 0
                384     BINARY_SUBSCR                   
                388     LOAD_CONST                      15: 'e2'
                390     BINARY_SUBSCR                   
                394     LOAD_CONST                      16: 387
                396     COMPARE_OP                      40 (==)
                400     POP_JUMP_IF_FALSE               45 (to 492)
                402     LOAD_FAST                       6: cpu
                404     LOAD_CONST                      2: 0
                406     BINARY_SUBSCR                   
                410     LOAD_CONST                      17: 'e3'
                412     BINARY_SUBSCR                   
                416     LOAD_CONST                      18: 521
                418     COMPARE_OP                      40 (==)
                422     POP_JUMP_IF_FALSE               34 (to 492)
                424     LOAD_FAST                       6: cpu
                426     LOAD_CONST                      2: 0
                428     BINARY_SUBSCR                   
                432     LOAD_CONST                      19: 'e4'
                434     BINARY_SUBSCR                   
                438     LOAD_CONST                      20: 454
                440     COMPARE_OP                      40 (==)
                444     POP_JUMP_IF_FALSE               23 (to 492)
                446     LOAD_FAST                       6: cpu
                448     LOAD_CONST                      2: 0
                450     BINARY_SUBSCR                   
                454     LOAD_CONST                      21: 'e5'
                456     BINARY_SUBSCR                   
                460     LOAD_CONST                      22: 111
                462     COMPARE_OP                      40 (==)
                466     POP_JUMP_IF_FALSE               12 (to 492)
                468     LOAD_GLOBAL                     19: NULL + print
                478     LOAD_CONST                      23: 'correct'
                480     CALL                            1
                488     POP_TOP                         
                490     RETURN_CONST                    0: None
                492     LOAD_GLOBAL                     19: NULL + print
                502     LOAD_CONST                      24: 'errrrror'
                504     CALL                            1
                512     POP_TOP                         
                514     RETURN_CONST                    0: None
                516     SWAP                            2
                518     POP_TOP                         
                520     SWAP                            2
                522     STORE_FAST                      3: i
                524     RERAISE                         0
                526     PUSH_EXC_INFO                   
                528     POP_TOP                         
                530     PUSH_NULL                       
                532     LOAD_FAST                       1: exit
                534     LOAD_CONST                      2: 0
                536     CALL                            1
                544     POP_TOP                         
                546     POP_EXCEPT                      
                548     JUMP_BACKWARD                   151 (to 248)
                550     COPY                            3
                552     POP_EXCEPT                      
                554     RERAISE                         1

4、结合pycdc反编译的部分源码和观察字节码后手撕的源码如下

class cstack:
    def __init__(self):
        self.stack = []
        self.length = 0
    def push(self, value):
        self.stack.append(value)
        self.length = self.length + 1
    def pop(self):
        if not self.stack:
            return 0
        self.length = self.length - 1
        cur_value = self.stack.pop()
        return cur_value

def parse_code(cpu, code):
    cur_step = 0
    if code[0] == 16:
        cpu[0]['eax'] = cpu[0]['eax'] + cpu[0]['ebx']
    elif code[0] == 32:
        cpu[0]['eax'] = cpu[0]['eax'] - cpu[0]['ebx']
    elif code[0] == 48:
        cpu[0]['eax'] = cpu[0]['eax'] * cpu[0]['ebx']
    elif code[0] == 64:
        cpu[0]['eax'] = cpu[0]['eax'] // cpu[0]['ebx']
    elif code[0] == 80:
        cpu[0]['eax'] = cpu[0]['eax'] ^ cpu[0]['ebx']
    elif code[0] == 96:
        cpu[0]['eax'] = cpu[0]['eax'] & cpu[0]['ebx']
    elif code[0] == 112:
        cpu[0]['eax'] = cpu[0]['eax'] | cpu[0]['ebx']
    elif code[0] == 128:
        if cpu[0]['eax'] == cpu[0]['ebx']:
            cpu[0]['ecx'] = 1
        else:
            cpu[0]['ecx'] = 0
    elif code[0] == 129:
        cur_step += 1
        if cpu[0]['ecx'] == 1:
            cur_step = code[1]
    elif code[0] == 130:
        cur_step += 1
        cur_step = code[1]
    elif code[0] == 131:
        cur_step = 9999998
    elif code[0] == 144:
        cpu[0]['eax'] = code[1]
        cur_step = 1
    elif code[0] == 145:
        cpu[0]['eax'] = cpu[1].pop()
    elif code[0] == 146:
        cpu[0]['ebx'] = code[1]
        cur_step = 1
    elif code[0] == 147:
        cpu[0]['ebx'] = cpu[1].pop()
    elif code[0] == 148:
        cpu[0]['eax'] = cpu[0]['ebx']
    elif code[0] == 149:
        cpu[0]['ebx'] = cpu[0]['eax']
    elif code[0] == 150:
        cpu[0]['ecx'] = code[1]
        cur_step = 1
    elif code[0] == 151:
        cpu[1].push(cpu[0]['eax'])
    elif code[0] == 225:
        cpu[0]['e1'] = cpu[0]['eax']
    elif code[0] == 226:
        cpu[0]['e2'] = cpu[0]['eax']
    elif code[0] == 227:
        cpu[0]['e3'] = cpu[0]['eax']
    elif code[0] == 228:
        cpu[0]['e4'] = cpu[0]['eax']
    elif code[0] == 229:
        cpu[0]['e5'] = cpu[0]['eax']

    cur_step += 1
    return cur_step


def run(all_code, cpu):
    cur_step=0
    res=0
    while 1:
        res=parse_code(cpu,all_code[cur_step::])
        cur_step=cur_step+res
        if res==9999999:
            break
    #return cpu


def init(cpu, flag):
    for i in flag:
        cpu[1].push(i)
    cpu[0]['eax']=0
    cpu[0]['ebx']=0
    cpu[0]['ecx']=0
    cpu[0]['edx']=0
    cpu[0]['eip']=0
    cpu[0]['e1']=0
    cpu[0]['e2']=0
    cpu[0]['e3']=0
    cpu[0]['e4']=0
    cpu[0]['e5']=0
    #return cpu

encry=['1327','387','521','454','111']
stack=cstack()
cpu=[{},stack]
flag=[1,2,3,4] #测试的flag
init(cpu,flag)
all_code=[145, 146, 2, 64, 146, 32768, 32, 146, 792896, 16, 146, 23751, 32, 146, 400000, 64, 146, 297, 32, 146, 200000, 64, 225, 145, 146, 324, 48, 146, 1368, 32, 146, 40000, 64, 226, 149, 145, 48, 32, 80, 146, 14, 64, 146, 197, 32, 146, 1000, 64, 227, 145, 146, 521197, 48, 146, 88123111, 80, 146, 554259, 32, 146, 600000, 64, 146, 99, 32, 146, 100, 64, 228, 145, 146, 45499, 32, 80, 146, 19499, 32, 146, 675, 64, 146, 5519, 32, 146, 10000, 64, 229, 131]
run(all_code,cpu)

5、可以通过调试判断出程序的执行顺序

all_code可以分为5部分,分别为

第一部分:145, 146, 2, 64, 146, 32768, 32, 146, 792896, 16, 146, 23751, 32, 146, 400000, 64, 146, 297, 32, 146, 200000, 64, 225
第二部分:145, 146, 324, 48, 146, 1368, 32, 146, 40000, 64, 226
第三部分:149, 145, 48, 32, 80, 146, 14, 64, 146, 197, 32, 146, 1000, 64, 227
第四部分:145, 146, 521197, 48, 146, 88123111, 80, 146, 554259, 32, 146, 600000, 64, 146, 99, 32, 146, 100, 64, 228
第五部分:145, 146, 45499, 32, 80, 146, 19499, 32, 146, 675, 64, 146, 5519, 32, 146, 10000, 64, 229, 131]

6、这里就解释下第一部分

145:cpu[0]['eax']=cpu[1].pop(),即将flag[-1]赋给cpu[0]['eax'],栈是先进后出的

146, 2 :cpu[0]['ebx']=2

64:cpu[0]['eax']=cpu[0]['eax']//cpu[0]['ebx']

146, 32768:cpu[0]['ebx']=32768

32:cpu[0]['eax']=cpu[0]['eax']-cpu[0]['ebx']

146, 792896 :cpu[0]['ebx']=792896

16:cpu[0]['eax']=cpu[0]['eax']+cpu[0]['ebx']

后面的过程类似(打字太累了)

到225时将cpu[0]['eax']赋值给cpu[0]['e1']

所以第一部分的结果就是cpu[0]['e1']=(((flag[-1]//2-32768+792896-23751)//400000)-297)//200000

要满足cpu[0]['e1']=1327,只需将上述只要解个方程即可,可以求出flag[-1]=212320236127246

7、将其他部分按上述过程可以得到flag

flag=[753020270,52484,18856,47782,212320236127246]

将其转变成16进制后得flag{2ce22d6e-cd04-49a8-baa6-c11aa840c40e}

题目可以在这里下载:https://cloud.189.cn/t/rEF7VnquMfya (访问码:mzt6)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值