180627 逆向 pyc還原指令碼

2021-08-21 02:28:07 字數 4473 閱讀 9061

針對suctf的python**好一題,通過解析內容還原pyc的指令碼

對於不同的解析內容需要另加修改,但原理一致–將元素按照 格式識別符號-len-內容的形式遞迴填入即可

值得說明的一點是指令碼無法還原出原來一模一樣的pyc,但可以還原出相同的py檔案

這是因為python在編譯pyc的時候,會將一些同樣的字串,通過引用來使用,即格式識別符號「r」;另外還有一些字串使用的是interend,格式識別符號』t』

這些表現出來的形式與常規字串』s』相同,因此不易區分。而pyc中用常規字串來解析,在執行和反編譯的時候也是沒有區別的

原理是這樣的:引用字串只是為了壓縮pyc的大小,使得同樣的字串不用出現多次。而未經優化的pyc也是可以正常使用的。

指令碼如下:

from struct import pack

defp32

(i):

return pack(", i)

defstring

(d, fout):

l = len(d)

fout.write(b"s")

if(type(d)==type("1")):

if(d!=""

and d[0]=="'"):

d = d.strip("'")

l -= 2

d = d.encode()

l = p32(l)

fout.write(l)

fout.write(d)

deftumple

(d, fout):

l = p32(len(d))

fout.write(b"(")

fout.write(l)

for s in d:

string(s, fout)

deftumple_line

(i, l, fout):

fout.write(b'(')

fout.write(p32(l))

while(data[i][:5]!="names"):

# print(data[i])

if(data[i]=="none"):

fout.write(b"n")

elif(data[i]=="code"):

i = analyse("code", i+1, fout)-1

elif(data[i][0]=='('):

analyse("tumple", i, fout)

elif(data[i][0]=="'"):

string(data[i][1:-1], fout)

else:

fout.write(b"i")

fout.write(p32(int(data[i])))

i += 1

return i

defcalc_len

(i):

l = 0

n = 1

while(n):

if(data[i].find("argcount")!=-1):

n += 1

if(n==1):

l += 1

# print(n, l, data[i])

if(data[i][:6]=="lnotab"):

n -= 1

i += 1

return l-8

defanalyse

(kind, i, fout):

# print(data[i])

global data

if(kind=="code"):

fout.write(b"c")

argcount = int(data[i].split(' ')[1])

i += 1

fout.write(p32(argcount))

nlocals = int(data[i].split(' ')[1])

i += 1

fout.write(p32(nlocals))

stacksize = int(data[i].split(' ')[1])

i += 1

fout.write(p32(stacksize))

flags = int(data[i].split(' ')[1], 16)

i += 1

fout.write(p32(flags))

i = analyse("co_code", i, fout)

i = analyse("consts", i, fout)

i = analyse("names", i, fout)

i = analyse("varnames", i, fout)

i = analyse("freevars", i, fout)

i = analyse("cellvars", i, fout)

i = analyse("filename", i, fout)

i = analyse("name", i, fout)

i = analyse("firstlineno", i, fout)

i = analyse("lnotab", i, fout)

if(kind=="co_code"):

if(len(data[i].split(' '))==1):

s = ""

while(data[i]!="consts"):

i += 1

s += data[i]

s = s.replace("consts", "")

else:

s = data[i].split(" ")[1]

i += 1

try:

s = bytes.fromhex(s)

except:

s = bytes.fromhex("0"+s)

string(s, fout)

i += 1

if(kind=="consts"):

l = calc_len(i)

i = tumple_line(i, l, fout)

if(kind in ["names", "varnames", "freevars", "cellvars", "tumple"]):

if(data[i][0]!="("):

data[i] = "".join(data[i].split(' ')[1:])

d = data[i].replace("(", "").replace(")", "").replace(" ", "").split(",")

if(d[-1]==""):

d.pop(-1)

tumple(d, fout)

i += 1

if(kind in ["filename", "name"]):

d = data[i].split(" ")[1].replace("'", "")

string(d, fout)

i += 1

if(kind=="firstlineno"):

d = int(data[i].split(" ")[1])

fout.write(p32(d))

i += 1

if(kind=="lnotab"):

try:

s = data[i].split(" ")[1]

except:

s = ""

try:

s = bytes.fromhex(s)

except:

s = bytes.fromhex("0"+s)

string(s, fout)

i += 1

return i

if(__name__=="__main__"):

fout = open("output.pyc", "wb")

with open("opcode.txt","r", encoding='utf-8') as f:

data = f.readlines()

# utf-8格式的opcode使用下述語句解析,因為showfile重定向的結果出來是utf-8格式的orz

# data = f.read().replace("\x00", "").split("\n\n")

for i in range(len(data)):

data[i] = data[i].strip()

print(data)

magic = bytes.fromhex(data[0].split(" ")[1])

moddate = bytes.fromhex(data[1].split(" ")[1])

fout.write(magic)

fout.write(moddate)

t = analyse("code", 3, fout)

print("all lines:", t)

fout.close()

寫的比較亂(:з」∠)有問題可以私戳我詢問,如果日後有功夫會回來重構一下的xd

逆向 簡單的pyc

這個是i c的比賽逆向題目 比賽還沒結束qaq 題目內容很簡單 提示說要逆向乙個pyc 直接拉進去 執行 得到如下內容 import base64 def encode message s for i in message x ord i 32 x x 16 s chr x return base6...

關於pyc檔案的逆向

關於pyc檔案的逆向 最近感覺遇到的pyc檔案逆向的越來越多了,所以就來總結下。參考了大佬的blog 0x1 pyc的檔案結構 在命令列輸入 python m filename.py的時候,便會得到乙個對應的filename.pyc。拖進hxd中看二進位制。其中,開頭的4個位元組是magic num...

pyc逆向之opcode簡單置換

最近做了一道pyc的逆向題,主要難點在於python環境的opcode被置換,就簡單記錄一下相關知識。opcode其實是指python原始碼的操作碼,python源 py編譯後可以得到二進位制檔案 pyc,pyc檔案中就含有opcode序列。對於不同版本的python,其opcode是不完全相同的,...