首頁 > 後端開發 > Python教學 > 混淆'世界你好!” Python 上的混淆

混淆'世界你好!” Python 上的混淆

Linda Hamilton
發布: 2025-01-04 03:37:44
原創
181 人瀏覽過

Obfuscating “Hello world!” obfuscate on Python

創建最奇怪的混淆程序,列印字串「Hello world!」。我決定寫一篇解釋它到底是如何運作的。所以,這是 Python 2.7 中的條目:

(lambda _, __, ___, ____, _____, ______, _______, ________:
    getattr(
        __import__(True.__class__.__name__[_] + [].__class__.__name__[__]),
        ().__class__.__eq__.__class__.__name__[:__] +
        ().__iter__().__class__.__name__[_____:________]
    )(
        _, (lambda _, __, ___: _(_, __, ___))(
            lambda _, __, ___:
                chr(___ % __) + _(_, __, ___ // __) if ___ else
                (lambda: _).func_code.co_lnotab,
            _ << ________,
            (((_____ << ____) + _) << ((___ << _____) - ___)) + (((((___ << __)
            - _) << ___) + _) << ((_____ << ____) + (_ << _))) + (((_______ <<
            __) - _) << (((((_ << ___) + _)) << ___) + (_ << _))) + (((_______
            << ___) + _) << ((_ << ______) + _)) + (((_______ << ____) - _) <<
            ((_______ << ___))) + (((_ << ____) - _) << ((((___ << __) + _) <<
            __) - _)) - (_______ << ((((___ << __) - _) << __) + _)) + (_______
            << (((((_ << ___) + _)) << __))) - ((((((_ << ___) + _)) << __) +
            _) << ((((___ << __) + _) << _))) + (((_______ << __) - _) <<
            (((((_ << ___) + _)) << _))) + (((___ << ___) + _) << ((_____ <<
            _))) + (_____ << ______) + (_ << ___)
        )
    )
)(
    *(lambda _, __, ___: _(_, __, ___))(
        (lambda _, __, ___:
            [__(___[(lambda: _).func_code.co_nlocals])] +
            _(_, __, ___[(lambda _: _).func_code.co_nlocals:]) if ___ else []
        ),
        lambda _: _.func_code.co_argcount,
        (
            lambda _: _,
            lambda _, __: _,
            lambda _, __, ___: _,
            lambda _, __, ___, ____: _,
            lambda _, __, ___, ____, _____: _,
            lambda _, __, ___, ____, _____, ______: _,
            lambda _, __, ___, ____, _____, ______, _______: _,
            lambda _, __, ___, ____, _____, ______, _______, ________: _
        )
    )
)

登入後複製
登入後複製
登入後複製
登入後複製

不允許使用字串文字,但我為了好玩設定了一些其他限制:它必須是單一表達式(因此沒有列印語句),具有最少的內建用法,並且沒有整數文字。
開始使用

由於我們無法使用列印,我們可以寫入 stdout 檔案物件:

import sys
sys.stdout.write("Hello world!\n")
登入後複製
登入後複製
登入後複製
登入後複製

但是讓我們使用較低等級的東西:os.write()。我們需要 stdout 的檔案描述符,它是 1(可以使用 print sys.stdout.fileno() 檢查)。

import os
os.write(1, "Hello world!\n")
登入後複製
登入後複製
登入後複製

我們想要一個表達式,所以我們將使用 import():

__import__("os").write(1, "Hello world!\n")
登入後複製
登入後複製
登入後複製

我們也希望能夠混淆 write(),因此我們將引入 getattr():

getattr(__import__("os"), "write")(1, "Hello world!\n")
登入後複製
登入後複製
登入後複製

這是起點。從現在開始,一切都將混淆三個字串和整數。
將字串串在一起

「os」和「write」相當簡單,因此我們將透過連接各個內建類別的部分名稱來建立它們。有很多不同的方法可以做到這一點,但我選擇了以下方法:

"o" from the second letter of bool: True.__class__.__name__[1]
"s" from the third letter of list: [].__class__.__name__[2]
"wr" from the first two letters of wrapper_descriptor, an implementation detail in CPython found as the type of some builtin classes’ methods (more on that here): ().__class__.__eq__.__class__.__name__[:2]
"ite" from the sixth through eighth letters of tupleiterator, the type of object returned by calling iter() on a tuple: ().__iter__().__class__.__name__[5:8]
登入後複製
登入後複製

我們開始取得一些進展!

getattr(
    __import__(True.__class__.__name__[1] + [].__class__.__name__[2]),
    ().__class__.__eq__.__class__.__name__[:2] +
    ().__iter__().__class__.__name__[5:8]
)(1, "Hello world!\n")

登入後複製
登入後複製

「Hello world!n」更複雜。我們將把它編碼為一個大整數,它由每個字元的 ASCII 代碼乘以 256 的字元在字串中的索引次方組成。換句話說,以下總和:
Σn=0L−1cn(256n)

哪裡L
是字串的長度,cn 是 n

的 ASCII 碼 字串中的第

個字元。建立號碼:

>>> codes = [ord(c) for c in "Hello world!\n"]
>>> num = sum(codes[i] * 256 ** i for i in xrange(len(codes)))
>>> print num
802616035175250124568770929992
登入後複製
登入後複製

現在我們需要程式碼將此數字轉換回字串。我們使用一個簡單的遞歸演算法:

>>> def convert(num):
...     if num:
...         return chr(num % 256) + convert(num // 256)
...     else:
...         return ""
...
>>> convert(802616035175250124568770929992)
'Hello world!\n'
登入後複製
登入後複製

用 lambda 重寫一行:

convert = lambda num: chr(num % 256) + convert(num // 256) if num else ""

登入後複製
登入後複製

現在我們使用匿名遞歸將其轉換為單一表達式。這需要一個組合器。從這個開始:

>>> comb = lambda f, n: f(f, n)
>>> convert = lambda f, n: chr(n % 256) + f(f, n // 256) if n else ""
>>> comb(convert, 802616035175250124568770929992)
'Hello world!\n'
登入後複製
登入後複製

現在我們只需將這兩個定義代入表達式中,我們就得到了我們的函數:

>>> (lambda f, n: f(f, n))(
...     lambda f, n: chr(n % 256) + f(f, n // 256) if n else "",
...     802616035175250124568770929992)
'Hello world!\n'
登入後複製
登入後複製

現在我們可以將其貼到先前的程式碼中,一路替換一些變數名稱 (f → , n → _):

getattr(
    __import__(True.__class__.__name__[1] + [].__class__.__name__[2]),
    ().__class__.__eq__.__class__.__name__[:2] +
    ().__iter__().__class__.__name__[5:8]
)(
    1, (lambda _, __: _(_, __))(
        lambda _, __: chr(__ % 256) + _(_, __ // 256) if __ else "",
        802616035175250124568770929992
    )
)

登入後複製
登入後複製

函數內部

我們在轉換函數的主體中留下了一個「」(記住:沒有字串文字!),以及我們必須以某種方式隱藏的大量數字。讓我們從空字串開始。我們可以透過檢查某個隨機函數的內部結構來即時製作一個:

(lambda _, __, ___, ____, _____, ______, _______, ________:
    getattr(
        __import__(True.__class__.__name__[_] + [].__class__.__name__[__]),
        ().__class__.__eq__.__class__.__name__[:__] +
        ().__iter__().__class__.__name__[_____:________]
    )(
        _, (lambda _, __, ___: _(_, __, ___))(
            lambda _, __, ___:
                chr(___ % __) + _(_, __, ___ // __) if ___ else
                (lambda: _).func_code.co_lnotab,
            _ << ________,
            (((_____ << ____) + _) << ((___ << _____) - ___)) + (((((___ << __)
            - _) << ___) + _) << ((_____ << ____) + (_ << _))) + (((_______ <<
            __) - _) << (((((_ << ___) + _)) << ___) + (_ << _))) + (((_______
            << ___) + _) << ((_ << ______) + _)) + (((_______ << ____) - _) <<
            ((_______ << ___))) + (((_ << ____) - _) << ((((___ << __) + _) <<
            __) - _)) - (_______ << ((((___ << __) - _) << __) + _)) + (_______
            << (((((_ << ___) + _)) << __))) - ((((((_ << ___) + _)) << __) +
            _) << ((((___ << __) + _) << _))) + (((_______ << __) - _) <<
            (((((_ << ___) + _)) << _))) + (((___ << ___) + _) << ((_____ <<
            _))) + (_____ << ______) + (_ << ___)
        )
    )
)(
    *(lambda _, __, ___: _(_, __, ___))(
        (lambda _, __, ___:
            [__(___[(lambda: _).func_code.co_nlocals])] +
            _(_, __, ___[(lambda _: _).func_code.co_nlocals:]) if ___ else []
        ),
        lambda _: _.func_code.co_argcount,
        (
            lambda _: _,
            lambda _, __: _,
            lambda _, __, ___: _,
            lambda _, __, ___, ____: _,
            lambda _, __, ___, ____, _____: _,
            lambda _, __, ___, ____, _____, ______: _,
            lambda _, __, ___, ____, _____, ______, _______: _,
            lambda _, __, ___, ____, _____, ______, _______, ________: _
        )
    )
)

登入後複製
登入後複製
登入後複製
登入後複製

我們在這裡真正要做的是查看函數中包含的程式碼物件的行號表。由於它是匿名的,因此沒有行號,因此字串為空。將 0 替換為 _ 以使其更加混亂(這並不重要,因為該函數沒有被呼叫),然後將其插入。我們也將把 256 重構為一個參數,該參數傳遞給我們混淆的 Convert()連同號碼。這需要為組合器添加一個參數:

import sys
sys.stdout.write("Hello world!\n")
登入後複製
登入後複製
登入後複製
登入後複製

繞道

讓我們暫時解決一個不同的問題。我們想要一種方法來混淆程式碼中的數字,但是每次使用它們時重新建立它們會很麻煩(而且不是特別有趣)。如果我們可以實作range(1, 9) == [1, 2, 3, 4, 5, 6, 7, 8],那麼我們可以將目前的工作包裝在一個函數中,該函數接受包含以下數字的變數1 到8,並用這些變數取代正文中出現的整數文字:

import os
os.write(1, "Hello world!\n")
登入後複製
登入後複製
登入後複製

即使我們還需要形成 256 和 802616035175250124568770929992,它們也可以透過對這八個「基本」數字進行算術運算來創建。 1-8 的選擇是任意的,但似乎是一個很好的中間立場。

我們可以透過函數的程式碼物件來取得函數接受的參數數量:

__import__("os").write(1, "Hello world!\n")
登入後複製
登入後複製
登入後複製

建構參數計數在 1 到 8 之間的函數元組:

getattr(__import__("os"), "write")(1, "Hello world!\n")
登入後複製
登入後複製
登入後複製

使用遞歸演算法,我們可以將其轉換為 range(1, 9) 的輸出:

"o" from the second letter of bool: True.__class__.__name__[1]
"s" from the third letter of list: [].__class__.__name__[2]
"wr" from the first two letters of wrapper_descriptor, an implementation detail in CPython found as the type of some builtin classes’ methods (more on that here): ().__class__.__eq__.__class__.__name__[:2]
"ite" from the sixth through eighth letters of tupleiterator, the type of object returned by calling iter() on a tuple: ().__iter__().__class__.__name__[5:8]
登入後複製
登入後複製

和之前一樣,我們將其轉換為 lambda 形式:

getattr(
    __import__(True.__class__.__name__[1] + [].__class__.__name__[2]),
    ().__class__.__eq__.__class__.__name__[:2] +
    ().__iter__().__class__.__name__[5:8]
)(1, "Hello world!\n")

登入後複製
登入後複製

然後,進入匿名遞歸形式:

>>> codes = [ord(c) for c in "Hello world!\n"]
>>> num = sum(codes[i] * 256 ** i for i in xrange(len(codes)))
>>> print num
802616035175250124568770929992
登入後複製
登入後複製

為了好玩,我們將 argcount 操作分解為一個附加函數參數,並混淆一些變數名稱:

>>> def convert(num):
...     if num:
...         return chr(num % 256) + convert(num // 256)
...     else:
...         return ""
...
>>> convert(802616035175250124568770929992)
'Hello world!\n'
登入後複製
登入後複製

現在有一個新問題:我們仍然需要一種隱藏 0 和 1 的方法。我們可以透過檢查任意函數中局部變數的數量來獲得這些:

convert = lambda num: chr(num % 256) + convert(num // 256) if num else ""

登入後複製
登入後複製

儘管函數體看起來相同,但第一個函數中的 _ 不是參數,也不是在函數中定義的,因此 Python 將其解釋為全域變數:

>>> comb = lambda f, n: f(f, n)
>>> convert = lambda f, n: chr(n % 256) + f(f, n // 256) if n else ""
>>> comb(convert, 802616035175250124568770929992)
'Hello world!\n'
登入後複製
登入後複製

無論 _ 是否實際在全域範圍內定義,都會發生這種情況。

將其付諸實踐:

>>> (lambda f, n: f(f, n))(
...     lambda f, n: chr(n % 256) + f(f, n // 256) if n else "",
...     802616035175250124568770929992)
'Hello world!\n'
登入後複製
登入後複製

現在我們可以取代 funcs 的值,然後使用 * 將結果整數列表作為八個單獨的變數傳遞,我們得到:

getattr(
    __import__(True.__class__.__name__[1] + [].__class__.__name__[2]),
    ().__class__.__eq__.__class__.__name__[:2] +
    ().__iter__().__class__.__name__[5:8]
)(
    1, (lambda _, __: _(_, __))(
        lambda _, __: chr(__ % 256) + _(_, __ // 256) if __ else "",
        802616035175250124568770929992
    )
)

登入後複製
登入後複製

移位

快到了!我們將用、_、_ 等來取代n{1..8} 變量,因為它會與中使用的變數產生混淆我們的內在功能。這不會造成實際問題,因為範圍規則意味著將使用正確的規則。這也是我們將 256 重構為 _ 指涉 1 而不是我們混淆的 Convert() 函數的原因之一。有點長了,就只貼前半部了:

(lambda _, __, ___, ____, _____, ______, _______, ________:
    getattr(
        __import__(True.__class__.__name__[_] + [].__class__.__name__[__]),
        ().__class__.__eq__.__class__.__name__[:__] +
        ().__iter__().__class__.__name__[_____:________]
    )(
        _, (lambda _, __, ___: _(_, __, ___))(
            lambda _, __, ___:
                chr(___ % __) + _(_, __, ___ // __) if ___ else
                (lambda: _).func_code.co_lnotab,
            _ << ________,
            (((_____ << ____) + _) << ((___ << _____) - ___)) + (((((___ << __)
            - _) << ___) + _) << ((_____ << ____) + (_ << _))) + (((_______ <<
            __) - _) << (((((_ << ___) + _)) << ___) + (_ << _))) + (((_______
            << ___) + _) << ((_ << ______) + _)) + (((_______ << ____) - _) <<
            ((_______ << ___))) + (((_ << ____) - _) << ((((___ << __) + _) <<
            __) - _)) - (_______ << ((((___ << __) - _) << __) + _)) + (_______
            << (((((_ << ___) + _)) << __))) - ((((((_ << ___) + _)) << __) +
            _) << ((((___ << __) + _) << _))) + (((_______ << __) - _) <<
            (((((_ << ___) + _)) << _))) + (((___ << ___) + _) << ((_____ <<
            _))) + (_____ << ______) + (_ << ___)
        )
    )
)(
    *(lambda _, __, ___: _(_, __, ___))(
        (lambda _, __, ___:
            [__(___[(lambda: _).func_code.co_nlocals])] +
            _(_, __, ___[(lambda _: _).func_code.co_nlocals:]) if ___ else []
        ),
        lambda _: _.func_code.co_argcount,
        (
            lambda _: _,
            lambda _, __: _,
            lambda _, __, ___: _,
            lambda _, __, ___, ____: _,
            lambda _, __, ___, ____, _____: _,
            lambda _, __, ___, ____, _____, ______: _,
            lambda _, __, ___, ____, _____, ______, _______: _,
            lambda _, __, ___, ____, _____, ______, _______, ________: _
        )
    )
)

登入後複製
登入後複製
登入後複製
登入後複製

只剩下兩件事了。我們從簡單的開始:256. 256=28

,所以我們可以將其改寫為 1

我們將對 802616035175250124568770929992 使用相同的想法。一個簡單的分而治之演算法可以將其分解為數字總和,這些數字本身就是移位在一起的數字總和,依此類推。例如,如果我們有 112,我們可以將其分解為 96 16,然後是 (3 >),這兩者都是涉及其他I/O 方式的轉移注意力的內容。

數字可以用多種方式分解;沒有一種方法是正確的(畢竟,我們可以將其分解為 (1

import sys
sys.stdout.write("Hello world!\n")
登入後複製
登入後複製
登入後複製
登入後複製

這裡的基本思想是,我們測試一定範圍內的數字的各種組合,直到得出兩個數字,基數和移位,使得基數

range() 的參數 span 表示搜尋空間的寬度。這不能太大,否則我們最終將得到 num 作為我們的基數和 0 作為我們的移位(因為 diff 為零),並且由於基數不能表示為單個變量,所以它會重複,無限遞歸。如果它太小,我們最終會得到類似於上面提到的 (1 span=⌈log1.5|num|⌉ ⌊24−深度⌋

將偽代碼翻譯成 Python 並進行一些調整(支援深度參數,以及一些涉及負數的警告),我們得到:

(lambda _, __, ___, ____, _____, ______, _______, ________:
    getattr(
        __import__(True.__class__.__name__[_] + [].__class__.__name__[__]),
        ().__class__.__eq__.__class__.__name__[:__] +
        ().__iter__().__class__.__name__[_____:________]
    )(
        _, (lambda _, __, ___: _(_, __, ___))(
            lambda _, __, ___:
                chr(___ % __) + _(_, __, ___ // __) if ___ else
                (lambda: _).func_code.co_lnotab,
            _ << ________,
            (((_____ << ____) + _) << ((___ << _____) - ___)) + (((((___ << __)
            - _) << ___) + _) << ((_____ << ____) + (_ << _))) + (((_______ <<
            __) - _) << (((((_ << ___) + _)) << ___) + (_ << _))) + (((_______
            << ___) + _) << ((_ << ______) + _)) + (((_______ << ____) - _) <<
            ((_______ << ___))) + (((_ << ____) - _) << ((((___ << __) + _) <<
            __) - _)) - (_______ << ((((___ << __) - _) << __) + _)) + (_______
            << (((((_ << ___) + _)) << __))) - ((((((_ << ___) + _)) << __) +
            _) << ((((___ << __) + _) << _))) + (((_______ << __) - _) <<
            (((((_ << ___) + _)) << _))) + (((___ << ___) + _) << ((_____ <<
            _))) + (_____ << ______) + (_ << ___)
        )
    )
)(
    *(lambda _, __, ___: _(_, __, ___))(
        (lambda _, __, ___:
            [__(___[(lambda: _).func_code.co_nlocals])] +
            _(_, __, ___[(lambda _: _).func_code.co_nlocals:]) if ___ else []
        ),
        lambda _: _.func_code.co_argcount,
        (
            lambda _: _,
            lambda _, __: _,
            lambda _, __, ___: _,
            lambda _, __, ___, ____: _,
            lambda _, __, ___, ____, _____: _,
            lambda _, __, ___, ____, _____, ______: _,
            lambda _, __, ___, ____, _____, ______, _______: _,
            lambda _, __, ___, ____, _____, ______, _______, ________: _
        )
    )
)

登入後複製
登入後複製
登入後複製
登入後複製

現在,當我們呼叫convert(802616035175250124568770929992)時,我們得到了一個很好的分解:

import sys
sys.stdout.write("Hello world!\n")
登入後複製
登入後複製
登入後複製
登入後複製

將其作為 802616035175250124568770929992 的替代品,並將所有部件放在一起:

import os
os.write(1, "Hello world!\n")
登入後複製
登入後複製
登入後複製

這就是你的。
附錄:Python 3 支持

自從寫這篇文章以來,有幾個人詢問了 Python 3 支援的問題。我當時沒有想到這一點,但隨著 Python 3 不斷獲得關注(感謝您!),這篇文章顯然早就該更新了。

幸運的是,Python 3(截至撰寫本文時為 3.6)不需要我們進行太多更改:

__import__("os").write(1, "Hello world!\n")
登入後複製
登入後複製
登入後複製

這是完整的 Python 3 版本:

getattr(__import__("os"), "write")(1, "Hello world!\n")
登入後複製
登入後複製
登入後複製

感謝您的閱讀!我仍然對這篇文章的受歡迎程度感到驚訝。

以上是混淆'世界你好!” Python 上的混淆的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板