Moon3r 发表于 2019-3-26 20:05

hgame一次pyc文件恢复


# 一次pyc文件恢复

## Python Pyc的文件格式

```c

/* Bytecode object */
typedef struct {
    PyObject_HEAD
    int co_argcount;      /* #arguments, except *args */
    int co_nlocals;   /* #local variables */
    int co_stacksize;       /* #entries needed for evaluation stack */
    int co_flags;       /* CO_..., see below */
    PyObject *co_code;      /* instruction opcodes */
    PyObject *co_consts;    /* list (constants used) */
    PyObject *co_names;   /* list of strings (names used) */
    PyObject *co_varnames;/* tuple of strings (local variable names) */
    PyObject *co_freevars;/* tuple of strings (free variable names) */
    PyObject *co_cellvars;      /* tuple of strings (cell variable names) */
    /* The rest doesn't count for hash/cmp */
    PyObject *co_filename;/* string (where it was loaded from) */
    PyObject *co_name;      /* string (name, for reference) */
    int co_firstlineno;   /* first source line number */
    PyObject *co_lnotab;    /* string (encoding addr<->lineno mapping) */
} PyCodeObject;
```

## 加载pyc co_code

```python
In : import dis,marshal

In : f=open('third.pyc')

In : f.read(4)
Out: '\x03\xf3\r\n'

In : f.read(4)
Out: '\xf1\xe1S\\'

In : code = marshal.load(f)

In : code.co_consts
Out:
(-1,
None,
'+',
'/',
'FcjTCgD1EffEm2rPC3bTyL5Wu2bKBI9KAZrwFgrUygHN',
<code object encode at 0x7f0420ee7f30, file "third.py", line 7>,
"Welcome to Processor's Python Classroom Part 3&4!\n",
'qi shi wo jiu shi lan cai ba liang dao ti fang zai yi qi.',
"Now let's start the origin of Python!\n",
'Plz Input Your Flag:\n',
2,
0,
1,
'',
"You're right! ",
"You're Wrong! ")

In : code.co_varnames
Out: ()

In : code.co_names
Out:
('string',
'list',
'letters',
'digits',
'dec',
'encode',
'raw_input',
'enc',
'lst',
'reverse',
'len',
'llen',
'range',
'i',
'chr',
'ord',
'enc2',
'join',
'enc3')

In : code.co_code
Out: 'q\x03\x00q\t\x00d\x0f\x00q\x0e\x00Gdd\x00\x00d\x01\x00l\x00\x00Z\x00\x00e\x01\x00e\x00\x00j\x02\x00\x83\x01\x00e\x01\x00e\x00\x00j\x03\x00\x83\x01\x00\x17d\x02\x00d\x03\x00g\x02\x00\x17Z\x02\x00d\x04\x00Z\x04\x00d\x05\x00\x84\x00\x00Z\x05\x00d\x06\x00GHd\x07\x00GHd\x08\x00GHd\t\x00GHe\x06\x00\x83\x00\x00Z\x07\x00e\x01\x00e\x07\x00\x83\x01\x00Z\x08\x00e\x08\x00j\t\x00\x83\x00\x00\x01e\n\x00e\x08\x00\x83\x01\x00Z\x0b\x00xc\x00e\x0c\x00e\x0b\x00\x83\x01\x00D]U\x00Z\r\x00e\r\x00d\n\x00\x16d\x0b\x00k\x02\x00r\xc4\x00e\x0e\x00e\x0f\x00e\x08\x00e\r\x00\x19\x83\x01\x00d\n\x00\x18\x83\x01\x00e\x08\x00e\r\x00<n\x00\x00e\x0e\x00e\x0f\x00e\x08\x00e\r\x00\x19\x83\x01\x00d\x0c\x00\x17\x83\x01\x00e\x08\x00e\r\x00<q\x8d\x00Wd\r\x00Z\x10\x00e\x10\x00j\x11\x00e\x08\x00\x83\x01\x00Z\x10\x00e\x05\x00e\x10\x00\x83\x01\x00Z\x12\x00e\x12\x00e\x04\x00k\x02\x00r\x1b\x01d\x0e\x00GHn\x05\x00d\x0f\x00GHd\x01\x00S'

```

使用`dis`库对`co_code`进行解释

```python
In : dis.dis(code.co_code)
          0 JUMP_ABSOLUTE       3
    >>    3 JUMP_ABSOLUTE       9
          6 LOAD_CONST         15 (15)
    >>    9 JUMP_ABSOLUTE      14
         12 PRINT_ITEM   
         13 LOAD_CONST      100 (100)
         16 STOP_CODE      
         17 LOAD_CONST          1 (1)
         20 IMPORT_NAME         0 (0)
         23 STORE_NAME          0 (0)
         26 LOAD_NAME         1 (1)
         29 LOAD_NAME         0 (0)
         32 LOAD_ATTR         2 (2)
         35 CALL_FUNCTION       1
         38 LOAD_NAME         1 (1)
         41 LOAD_NAME         0 (0)
         44 LOAD_ATTR         3 (3)
         47 CALL_FUNCTION       1
         50 BINARY_ADD   
         51 LOAD_CONST          2 (2)
         54 LOAD_CONST          3 (3)
         57 BUILD_LIST          2
         60 BINARY_ADD   
         61 STORE_NAME          2 (2)
         64 LOAD_CONST          4 (4)
         67 STORE_NAME          4 (4)
         70 LOAD_CONST          5 (5)
         73 MAKE_FUNCTION       0
         76 STORE_NAME          5 (5)
         79 LOAD_CONST          6 (6)
         82 PRINT_ITEM   
         83 PRINT_NEWLINE
         84 LOAD_CONST          7 (7)
         87 PRINT_ITEM   
         88 PRINT_NEWLINE
         89 LOAD_CONST          8 (8)
         92 PRINT_ITEM   
         93 PRINT_NEWLINE
         94 LOAD_CONST          9 (9)
         97 PRINT_ITEM   
         98 PRINT_NEWLINE
         99 LOAD_NAME         6 (6)
      102 CALL_FUNCTION       0
      105 STORE_NAME          7 (7)
      108 LOAD_NAME         1 (1)
      111 LOAD_NAME         7 (7)
      114 CALL_FUNCTION       1
      117 STORE_NAME          8 (8)
      120 LOAD_NAME         8 (8)
      123 LOAD_ATTR         9 (9)
      126 CALL_FUNCTION       0
      129 POP_TOP      
      130 LOAD_NAME          10 (10)
      133 LOAD_NAME         8 (8)
      136 CALL_FUNCTION       1
      139 STORE_NAME         11 (11)
      142 SETUP_LOOP         99 (to 244)
      145 LOAD_NAME          12 (12)
      148 LOAD_NAME          11 (11)
      151 CALL_FUNCTION       1
      154 GET_ITER      
      155 FOR_ITER         85 (to 243)
      158 STORE_NAME         13 (13)
      161 LOAD_NAME          13 (13)
      164 LOAD_CONST         10 (10)
      167 BINARY_MODULO
      168 LOAD_CONST         11 (11)
      171 COMPARE_OP          2 (==)
      174 POP_JUMP_IF_FALSE   196
      177 LOAD_NAME          14 (14)
      180 LOAD_NAME          15 (15)
      183 LOAD_NAME         8 (8)
      186 LOAD_NAME          13 (13)
      189 BINARY_SUBSCR
      190 CALL_FUNCTION       1
      193 LOAD_CONST         10 (10)
    >>196 BINARY_SUBTRACT
      197 CALL_FUNCTION       1
      200 LOAD_NAME         8 (8)
      203 LOAD_NAME          13 (13)
      206 STORE_SUBSCR   
      207 JUMP_FORWARD      0 (to 210)
    >>210 LOAD_NAME          14 (14)
      213 LOAD_NAME          15 (15)
      216 LOAD_NAME         8 (8)
      219 LOAD_NAME          13 (13)
      222 BINARY_SUBSCR
      223 CALL_FUNCTION       1
      226 LOAD_CONST         12 (12)
      229 BINARY_ADD   
      230 CALL_FUNCTION       1
      233 LOAD_NAME         8 (8)
      236 LOAD_NAME          13 (13)
      239 STORE_SUBSCR   
      240 JUMP_ABSOLUTE   141
    >>243 POP_BLOCK      
    >>244 LOAD_CONST         13 (13)
      247 STORE_NAME         16 (16)
      250 LOAD_NAME          16 (16)
      253 LOAD_ATTR          17 (17)
      256 LOAD_NAME         8 (8)
      259 CALL_FUNCTION       1
      262 STORE_NAME         16 (16)
      265 LOAD_NAME         5 (5)
      268 LOAD_NAME          16 (16)
      271 CALL_FUNCTION       1
      274 STORE_NAME         18 (18)
      277 LOAD_NAME          18 (18)
      280 LOAD_NAME         4 (4)
    >>283 COMPARE_OP          2 (==)
      286 POP_JUMP_IF_FALSE   283
      289 LOAD_CONST         14 (14)
      292 PRINT_ITEM   
      293 PRINT_NEWLINE
      294 JUMP_FORWARD      5 (to 302)
      297 LOAD_CONST         15 (15)
      300 PRINT_ITEM   
      301 PRINT_NEWLINE
    >>302 LOAD_CONST          1 (1)
      305 RETURN_VALUE   

```

刚开始走了一些弯路,通读了`opcode`,结果发现并不需要,只要将反`uncompyle2`的部分去掉,修改`co_code`长度即可正常反编译,期望修改后的`opcode`首行为

```python
                0 LOAD_CONST 0(0)
                1 LOAD_CONST 1(1)
                ...
```

使用`hexdump`查看文件



> 0x64 操作为LOAD_CONST,用法举例:LOAD_CONST 1      HEX: 640100
>
> 0x71 操作为JUMP_ABSOLUTE,用法举例:JUMP_ABSOLUTE 14                HEX: 710e00
>
> 0x65 操作为LOAD_NAME,用法举例:LOAD_NAME 1                HEX: 650100
>
> ...

## 修改原始pyc

通过`opcode`及`hexdump`可以确定,当前`co_code`长度为`0x132`(此处为小端显示,`0x1a1b`位置),`0x1e`到`0x2c`(左闭右开)这部分为混淆代码,直接从16进制数据中删除,然后修改`co_code`长度为`0x132-(0x2c-0x1e)`,即改为`24 01`,保存代码

```python
In : with open('third.pyc','r') as f:
   ...:   dt = f.read()
   ...:   

In : dt = dt[:0x1a]+'\x24'+dt+dt

In : with open('third_test2.pyc', 'w') as f:
   ...:   f.write(dt)
   ...:   

```

然后使用`uncompyle2 third_test2.pyc > third_source.py`进行反编译

源码如下:

```python
# 2019.02.16 14:17:20 CST
#Embedded file name: third.py
import string
letters = list(string.letters) + list(string.digits) + ['+', '/']
dec = 'FcjTCgD1EffEm2rPC3bTyL5Wu2bKBI9KAZrwFgrUygHN'

def encode(input_str):
    str_ascii_list = [ '{:0>8}'.format(str(bin(ord(i))).replace('0b', '')) for i in input_str ]
    output_str = ''
    equal_num = 0
    while str_ascii_list:
      temp_list = str_ascii_list[:3]
      if len(temp_list) != 3:
            while len(temp_list) < 3:
                equal_num += 1
                temp_list += ['00000000']

      temp_str = ''.join(temp_list)
      temp_str_list = [ temp_str for x in [0,
         6,
         12,
         18] ]
      temp_str_list = [ int(x, 2) for x in temp_str_list ]
      if equal_num:
            temp_str_list = temp_str_list
      output_str += ''.join([ letters for x in temp_str_list ])
      str_ascii_list = str_ascii_list

    output_str = output_str + '=' * equal_num
    return output_str


print "Welcome to Processor's Python Classroom Part 3&4!\n"
print 'qi shi wo jiu shi lan cai ba liang dao ti fang zai yi qi.'
print "Now let's start the origin of Python!\n"
print 'Plz Input Your Flag:\n'
enc = raw_input()
lst = list(enc)
lst.reverse()
llen = len(lst)
for i in range(llen):
    if i % 2 == 0:
      lst = chr(ord(lst) - 2)
    lst = chr(ord(lst) + 1)

enc2 = ''
enc2 = enc2.join(lst)
enc3 = encode(enc2)
if enc3 == dec:
    print "You're right! "
else:
    print "You're Wrong! "
# +++ okay decompyling third_test2.pyc
# decompiled 1 files: 1 okay, 0 failed, 0 verify failed
```

至此,代码已经还原,剩下的题目就很简单了。

## 解读代码

`encode`函数实现了一个`base64`,这里有一点点坑,这里的`base64`编码范围为`abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/`,并非原生的`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`,直接转换一下就好

solve.py↓

```python
#!/usr/bin/python
# -*- coding: utf-8 -*-
def decode(input_str):
    output_str = ''
    for i in input_str:
      if ord(i)>57 and ord(i)<91:
            output_str += i.lower()
      elif ord(i)>91:
            output_str += i.upper()
      else:
            output_str += i
    lst = list(output_str.decode('base64'))
    llen = len(lst)
    for i in range(llen):
      lst = chr(ord(lst) - 1)
      if i % 2 == 0:
            lst = chr(ord(lst) + 2)
    lst.reverse()
    return ''.join(lst)
if __name__ == '__main__':
      dec = 'FcjTCgD1EffEm2rPC3bTyL5Wu2bKBI9KAZrwFgrUygHN'
      print decode(dec)
```



## 参考文章

(http://butian.360.cn/School/content?id=429)

https://0x48.pw/2017/03/20/0x2f

Moon3r 发表于 2019-3-27 15:45

HugoMok 发表于 2019-3-27 14:53
hgame?我觉得我应该没有看错,大佬大佬。

确实是hgame,应该是第三周的python3&4那个题目,具体的记不太清了

hcldxw 发表于 2019-3-26 20:18

厉害大佬

Moon3r 发表于 2019-3-26 21:00

hcldxw 发表于 2019-3-26 20:18
厉害大佬

互相学习:handshake

一叶知夏 发表于 2019-3-27 08:49

向大佬学习~

dctm 发表于 2019-3-27 13:03

厉害厉害,

HugoMok 发表于 2019-3-27 14:53

hgame?我觉得我应该没有看错,大佬大佬。

迷途的小蜜蜂 发表于 2019-3-28 10:16

哇,大佬,不明觉厉。。。。膜拜大佬操作神仙

pleaidler 发表于 2019-3-30 10:34

厉害,收藏学习

Bayerischen 发表于 2019-3-30 21:46

向大佬学习
页: [1] 2 3 4
查看完整版本: hgame一次pyc文件恢复