
import와 같은 코드는 문자열이 아니기 때문에 코드를 쉽게 읽을 수 있습니다.
따라서 import, 클래스의 함수 등을 문자열로 변환하여 난독화할 수 있습니다.
다음의 예제는 subprocess를 난독화하는 방법을 서술합니다. 먼저 원본은 다음과 같습니다.
import subprocess
p = subprocess.Popen(['whoami'], stdout=subprocess.PIPE, shell=True)
print(p.communicate())
import를 통한 모듈은 __import__로 변경해서 다음과 같이 문자열로 변경할 수 있습니다.
p = __import__("subprocess").Popen(["whoami"], stdout=__import__("subprocess").PIPE, shell=True)
print(p.communicate())
다음으로는 Popen이나 PIPE, communicate와 같은 속성 값들은 getattr를 이용하여 문자열로 변경할 수 있습니다.
p = getattr(__import__("subprocess"), "Popen")(["whoami"], stdout=getattr(__import__("subprocess"), 'PIPE'), shell=True)
print(getattr(p, "communicate")())
문자열의 난독화는 python 기본 문법을 이용하여 진행할 수 있습니다.
예를 들어, True의 클래스 이름은 bool이고 인덱싱을 통해 b를 추출할 수 있습니다.
print(True.__class__.__name__[0]) # b
이와 같은 패턴으로 모든 영문자를 정의할 수 있고, 대문자의 경우 lower 혹은 upper를 이용할 수 있으며 이를 다시 getattr로 난독화 시킬 수 있습니다.
다음의 스크립트는 Popen을 난독화한 예시입니다.
obfuscation_dict = {
'a':'().__class__.__eq__.__class__.__name__[2]',
'b':'True.__class__.__name__[0]',
'c':'().__class__.__eq__.__class__.__name__[11]',
'd':'{}.__class__.__name__[0]',
'e':'().__class__.__name__[-1]',
'f':'vars.__class__.__name__[8]',
'g':'True.__doc__[41]',
'h':'vars.__class__.__name__[-3]',
'i':'().__class__.__eq__.__class__.__name__[-5]',
'j':'{}.__doc__[92]',
'k':'{}.__doc__[104]',
'l':'[].__class__.__name__[0]',
'm':'vars.__class__.__name__[-6]',
'n':'vars.__class__.__name__[6]',
'o':'True.__class__.__name__[1]',
'p':'str.__class__.__name__[2]',
'q':'vars.__doc__[50]',
'r':'().__class__.__eq__.__class__.__name__[-1]',
's':'().__class__.__eq__.__class__.__name__[10]',
't':'str.__class__.__name__[0]',
'u':'().__class__.__name__[1]',
'v':'{}.__doc__[109]',
'w':'().__class__.__eq__.__class__.__name__[0]',
'x':'True.__doc__[5]',
'y':'str.__class__.__name__[1]',
'z':'().__doc__[56]',
'_':'abs.__class__.__name__[7]',
}
def Obfuscator(string):
result = ''
for s in string:
if s.isupper(): s = 'getattr(' + Obfuscator(s.lower()) + ', ' + Obfuscator('upper') + ')()'
try: result += (obfuscation_dict[s] + '+')
except: result += (s + '+')
return result[:-1]
print(Obfuscator('Popen')) # getattr(str.__class__.__name__[2], ().__class__.__name__[1]+str.__class__.__name__[2]+str.__class__.__name__[2]+().__class__.__name__[-1]+().__class__.__eq__.__class__.__name__[-1])()+True.__class__.__name__[1]+str.__class__.__name__[2]+().__class__.__name__[-1]+vars.__class__.__name__[6]
Popen이 난독화된 whoami를 호출하는 코드는 다음과 같습니다.
p = getattr(__import__("subprocess"), getattr(str.__class__.__name__[2], ().__class__.__name__[1]+str.__class__.__name__[2]+str.__class__.__name__[2]+().__class__.__name__[-1]+().__class__.__eq__.__class__.__name__[-1])()+True.__class__.__name__[1]+str.__class__.__name__[2]+().__class__.__name__[-1]+vars.__class__.__name__[6])(["whoami"], stdout=getattr(__import__("subprocess"), "PIPE"), shell=True)
print(getattr(p, "communicate")())
문자열이 난독화가 되었다면 숫자도 난독화할 수 있습니다.
언더 바(_)의 경우 개수를 보기 난해하고, 기본 속성에도 사용되는 문자라서 가독성을 해치기 좋습니다.
1의 경우 다음과 같이 lambda를 이용하여 난독화를 수행할 수 있습니다.
print(
(lambda _,__,___,____,_____:_)
(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))
) # 1
💡 편의상 여러 줄로 나누었지만, 한 줄로 쓴다면 구분하기 피곤해지기 때문에 효과적입니다.
다른 수의 경우 첫 번째 lambda에서 쌍 점(:) 뒤의 언더 바(_) 개수로 조절이 가능합니다.
print(
(lambda _,__,___,____,_____:___) # <--- : 뒤의 _갯수를 통해 조정
(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))
)
사칙연산을 통해서도 수를 조절할 수 있습니다.
print(
(lambda _,__,___,____,_____:__*___) # 2 * 3 = 6
(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))
)
다음은 print("Hello")를 난독화한 코드입니다.
print(getattr(vars.__class__.__name__[-(lambda _,__,___,____,_____:___)(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))], ().__class__.__name__[(lambda _,__,___,____,_____:_)(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))]+str.__class__.__name__[(lambda _,__,___,____,_____:__)(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))]+str.__class__.__name__[(lambda _,__,___,____,_____:__)(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))]+().__class__.__name__[-(lambda _,__,___,____,_____:_)(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))]+().__class__.__eq__.__class__.__name__[-(lambda _,__,___,____,_____:_)(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))])()+().__class__.__name__[-(lambda _,__,___,____,_____:_)(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))]+[].__class__.__name__[0]+[].__class__.__name__[0]+True.__class__.__name__[(lambda _,__,___,____,_____:_)(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))])
해당 코드를 문자열로 취급한 뒤 base64로 인코딩을 수행합니다.
import base64
data = "print(getattr(vars.__class__.__name__[-(lambda _,__,___,____,_____:___)(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))], ().__class__.__name__[(lambda _,__,___,____,_____:_)(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))]+str.__class__.__name__[(lambda _,__,___,____,_____:__)(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))]+str.__class__.__name__[(lambda _,__,___,____,_____:__)(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))]+().__class__.__name__[-(lambda _,__,___,____,_____:_)(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))]+().__class__.__eq__.__class__.__name__[-(lambda _,__,___,____,_____:_)(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))])()+().__class__.__name__[-(lambda _,__,___,____,_____:_)(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))]+[].__class__.__name__[0]+[].__class__.__name__[0]+True.__class__.__name__[(lambda _,__,___,____,_____:_)(*(lambda _,__,___: _(_,__,___))((lambda _,__,___:[__(___[(lambda: _).__code__.co_nlocals])]+_(_,__,___[(lambda _: _).__code__.co_nlocals:]) if ___ else []),lambda _: _.__code__.co_argcount,(lambda _:_,lambda _,__:_,lambda _,__,___:_,lambda _,__,___,____:_,lambda _,__,___,____,_____:_)))])"
print(base64.b64encode(data.encode()))
문자열을 코드로 변환하여 실행하는 eval과 compile의 복합 사용으로 난독화한 코드를 다시 한번 난독화해볼 수 있습니다.
import base64
eval(compile(base64.b64decode(b'cHJpbnQoZ2V0YXR0cih2YXJzLl9fY2xhc3NfXy5fX25hbWVfX1stKGxhbWJkYSBfLF9fLF9fXyxfX19fLF9fX19fOl9fXykoKihsYW1iZGEgXyxfXyxfX186IF8oXyxfXyxfX18pKSgobGFtYmRhIF8sX18sX19fOltfXyhfX19bKGxhbWJkYTogXykuX19jb2RlX18uY29fbmxvY2Fsc10pXStfKF8sX18sX19fWyhsYW1iZGEgXzogXykuX19jb2RlX18uY29fbmxvY2FsczpdKSBpZiBfX18gZWxzZSBbXSksbGFtYmRhIF86IF8uX19jb2RlX18uY29fYXJnY291bnQsKGxhbWJkYSBfOl8sbGFtYmRhIF8sX186XyxsYW1iZGEgXyxfXyxfX186XyxsYW1iZGEgXyxfXyxfX18sX19fXzpfLGxhbWJkYSBfLF9fLF9fXyxfX19fLF9fX19fOl8pKSldLCAoKS5fX2NsYXNzX18uX19uYW1lX19bKGxhbWJkYSBfLF9fLF9fXyxfX19fLF9fX19fOl8pKCoobGFtYmRhIF8sX18sX19fOiBfKF8sX18sX19fKSkoKGxhbWJkYSBfLF9fLF9fXzpbX18oX19fWyhsYW1iZGE6IF8pLl9fY29kZV9fLmNvX25sb2NhbHNdKV0rXyhfLF9fLF9fX1sobGFtYmRhIF86IF8pLl9fY29kZV9fLmNvX25sb2NhbHM6XSkgaWYgX19fIGVsc2UgW10pLGxhbWJkYSBfOiBfLl9fY29kZV9fLmNvX2FyZ2NvdW50LChsYW1iZGEgXzpfLGxhbWJkYSBfLF9fOl8sbGFtYmRhIF8sX18sX19fOl8sbGFtYmRhIF8sX18sX19fLF9fX186XyxsYW1iZGEgXyxfXyxfX18sX19fXyxfX19fXzpfKSkpXStzdHIuX19jbGFzc19fLl9fbmFtZV9fWyhsYW1iZGEgXyxfXyxfX18sX19fXyxfX19fXzpfXykoKihsYW1iZGEgXyxfXyxfX186IF8oXyxfXyxfX18pKSgobGFtYmRhIF8sX18sX19fOltfXyhfX19bKGxhbWJkYTogXykuX19jb2RlX18uY29fbmxvY2Fsc10pXStfKF8sX18sX19fWyhsYW1iZGEgXzogXykuX19jb2RlX18uY29fbmxvY2FsczpdKSBpZiBfX18gZWxzZSBbXSksbGFtYmRhIF86IF8uX19jb2RlX18uY29fYXJnY291bnQsKGxhbWJkYSBfOl8sbGFtYmRhIF8sX186XyxsYW1iZGEgXyxfXyxfX186XyxsYW1iZGEgXyxfXyxfX18sX19fXzpfLGxhbWJkYSBfLF9fLF9fXyxfX19fLF9fX19fOl8pKSldK3N0ci5fX2NsYXNzX18uX19uYW1lX19bKGxhbWJkYSBfLF9fLF9fXyxfX19fLF9fX19fOl9fKSgqKGxhbWJkYSBfLF9fLF9fXzogXyhfLF9fLF9fXykpKChsYW1iZGEgXyxfXyxfX186W19fKF9fX1sobGFtYmRhOiBfKS5fX2NvZGVfXy5jb19ubG9jYWxzXSldK18oXyxfXyxfX19bKGxhbWJkYSBfOiBfKS5fX2NvZGVfXy5jb19ubG9jYWxzOl0pIGlmIF9fXyBlbHNlIFtdKSxsYW1iZGEgXzogXy5fX2NvZGVfXy5jb19hcmdjb3VudCwobGFtYmRhIF86XyxsYW1iZGEgXyxfXzpfLGxhbWJkYSBfLF9fLF9fXzpfLGxhbWJkYSBfLF9fLF9fXyxfX19fOl8sbGFtYmRhIF8sX18sX19fLF9fX18sX19fX186XykpKV0rKCkuX19jbGFzc19fLl9fbmFtZV9fWy0obGFtYmRhIF8sX18sX19fLF9fX18sX19fX186XykoKihsYW1iZGEgXyxfXyxfX186IF8oXyxfXyxfX18pKSgobGFtYmRhIF8sX18sX19fOltfXyhfX19bKGxhbWJkYTogXykuX19jb2RlX18uY29fbmxvY2Fsc10pXStfKF8sX18sX19fWyhsYW1iZGEgXzogXykuX19jb2RlX18uY29fbmxvY2FsczpdKSBpZiBfX18gZWxzZSBbXSksbGFtYmRhIF86IF8uX19jb2RlX18uY29fYXJnY291bnQsKGxhbWJkYSBfOl8sbGFtYmRhIF8sX186XyxsYW1iZGEgXyxfXyxfX186XyxsYW1iZGEgXyxfXyxfX18sX19fXzpfLGxhbWJkYSBfLF9fLF9fXyxfX19fLF9fX19fOl8pKSldKygpLl9fY2xhc3NfXy5fX2VxX18uX19jbGFzc19fLl9fbmFtZV9fWy0obGFtYmRhIF8sX18sX19fLF9fX18sX19fX186XykoKihsYW1iZGEgXyxfXyxfX186IF8oXyxfXyxfX18pKSgobGFtYmRhIF8sX18sX19fOltfXyhfX19bKGxhbWJkYTogXykuX19jb2RlX18uY29fbmxvY2Fsc10pXStfKF8sX18sX19fWyhsYW1iZGEgXzogXykuX19jb2RlX18uY29fbmxvY2FsczpdKSBpZiBfX18gZWxzZSBbXSksbGFtYmRhIF86IF8uX19jb2RlX18uY29fYXJnY291bnQsKGxhbWJkYSBfOl8sbGFtYmRhIF8sX186XyxsYW1iZGEgXyxfXyxfX186XyxsYW1iZGEgXyxfXyxfX18sX19fXzpfLGxhbWJkYSBfLF9fLF9fXyxfX19fLF9fX19fOl8pKSldKSgpKygpLl9fY2xhc3NfXy5fX25hbWVfX1stKGxhbWJkYSBfLF9fLF9fXyxfX19fLF9fX19fOl8pKCoobGFtYmRhIF8sX18sX19fOiBfKF8sX18sX19fKSkoKGxhbWJkYSBfLF9fLF9fXzpbX18oX19fWyhsYW1iZGE6IF8pLl9fY29kZV9fLmNvX25sb2NhbHNdKV0rXyhfLF9fLF9fX1sobGFtYmRhIF86IF8pLl9fY29kZV9fLmNvX25sb2NhbHM6XSkgaWYgX19fIGVsc2UgW10pLGxhbWJkYSBfOiBfLl9fY29kZV9fLmNvX2FyZ2NvdW50LChsYW1iZGEgXzpfLGxhbWJkYSBfLF9fOl8sbGFtYmRhIF8sX18sX19fOl8sbGFtYmRhIF8sX18sX19fLF9fX186XyxsYW1iZGEgXyxfXyxfX18sX19fXyxfX19fXzpfKSkpXStbXS5fX2NsYXNzX18uX19uYW1lX19bMF0rW10uX19jbGFzc19fLl9fbmFtZV9fWzBdK1RydWUuX19jbGFzc19fLl9fbmFtZV9fWyhsYW1iZGEgXyxfXyxfX18sX19fXyxfX19fXzpfKSgqKGxhbWJkYSBfLF9fLF9fXzogXyhfLF9fLF9fXykpKChsYW1iZGEgXyxfXyxfX186W19fKF9fX1sobGFtYmRhOiBfKS5fX2NvZGVfXy5jb19ubG9jYWxzXSldK18oXyxfXyxfX19bKGxhbWJkYSBfOiBfKS5fX2NvZGVfXy5jb19ubG9jYWxzOl0pIGlmIF9fXyBlbHNlIFtdKSxsYW1iZGEgXzogXy5fX2NvZGVfXy5jb19hcmdjb3VudCwobGFtYmRhIF86XyxsYW1iZGEgXyxfXzpfLGxhbWJkYSBfLF9fLF9fXzpfLGxhbWJkYSBfLF9fLF9fXyxfX19fOl8sbGFtYmRhIF8sX18sX19fLF9fX18sX19fX186XykpKV0p'),"<string>","exec"))