[Python] PyV8을 이용한 Javascript 분석
여기에 작성된 글을 보고 이것을 이용하면 난독화된 악성 스크립트를 안전하고 효율적으로 분석이 가능하겠다는 생각이 들어서 비슷한 방식으로 만들어보기로 마음먹었다.
[PyV8은 여기에서 다운받을 수 있다. 마지막 업로드가 2012년인 것이 조금 걸리지만...]
PyV8은 구글에서 만든 자바스크립트 엔진인 V8의 파이썬 래퍼로, 자바스크립트 코드를 해석하고 실행하여 그 결과를 파이썬에서 받아볼 수 있다. 하지만 PyV8은 자바스크립트 문법을 해석해 줄 뿐 운영체제의 API에 연결되어 있지 않기 때문에, 악성 스크립트에서 자주 사용하는 WScript.Shell 등을 사용하면 정의되지 않았다면서 에러가 발생한다. 그래서 직접 더미 API를 만들어서 넘겨주어야 하는 필요성이 있고, 위에서 언급했던 블로그에서는 각 모듈과 함수들을 직접 정의하여 넘겨주는 방식으로 제작했었다. 그런데 그렇게 만들게 되면 모든 모듈과 함수에 대응하는 클래스 및 메서드를 직접 다 만들어서 전달해야 한다는 불편함이 있었고, 결국 내가 원하는 것은 '어떤 함수가 어떤 인자를 받아서 실행되었는가' 이기 때문에 전부 간단하게 추상화시킬 수 있다고 판단하여 결국 간단하게 추상화 하는 것에 성공하였다. 자세한 내용은 소스코드를 보면서 작성하겠다.
http://5kyc1ad.kr/files/ScriptAnalyzer.zip
(악성코드 분석하려고 만든건데 티스토리에서 악성코드라며 첨부파일을 막아버렸네요; 첨부파일은 제 서버 주소로 대체하겠습니다.
꽤 시간이 흐른 뒤에 발견해버린 터라 실제 본문의 스크립트와는 상당히 달라졌을 수 있습니다.)
ScriptAnalyzer.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | import os import PyV8 from DummyAPI.Global import Global class ScriptAnalyzer: def __init__(self): pass def __del__(self): pass def Analyze(self, targetJSFile): if not os.path.exists(targetJSFile): print "[-] Target File not found : %s" % (targetJSFile) return False with open(targetJSFile, "rb") as f: javaScript = f.read() ctx = PyV8.JSContext(Global()) ctx.enter() ctx.eval(javaScript) return True def main(): scriptAnalyzer = ScriptAnalyzer() scriptAnalyzer.Analyze("..\\script4.js-") if __name__ == '__main__': main() | cs |
PyV8을 이용하여 Javascript를 실행하는 분석기와 그를 이용하는 main 함수가 존재하는 소스코드 파일이다. 별다른건 없고 이후 나올 DummyAPI.Global 모듈에서 Global 클래스를 가져와 넘기는 것만 확인하면 된다.
Global.py
1 2 3 4 5 6 7 | import PyV8 from DummyAPI.AbstractType import AbstractType class Global(PyV8.JSClass): def __getattr__(self, name): return AbstractType(name) | cs |
추상화에 성공하여 무척 간결해져 버린 Global 클래스의 소스코드이다. 원래라면 클래스 멤버 변수로 각 모듈들에 대응하는 클래스의 인스턴스를 가지고 있어야 하지만, 어떤 모듈을 호출할 지 모르므로 __getattr__ 메서드를 정의하는 것으로 한번에 해결해 버렸다. __getattr__ 메서드는 classA.not_exist_value 처럼 클래스 내부에 존재하지 않는 변수, 혹은 메서드에 접근했을 경우 호출되는 함수이다. 현재 Global 클래스 내부에 저 함수 외에는 아무것도 정의되지 않았으므로 Javascript 내에서 어떤 모듈 혹은 Global 함수를 호출하던 간에 AbstractType(name) 을 리턴받게 될 것이다. AbstractType은 직접 작성한 타입으로, 다음 소스코드에서 확인이 가능하다.
AbstractType.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import PyV8 import Utils class AbstractType(PyV8.JSClass): def __init__(self, thisName): self.thisName = thisName # Type is Function def __call__(self, *args, **kwargs): if len(args) > 0 and len(kwargs.keys()) > 0: print ("[*] %s(%s, %s)" % (self.thisName, Utils.argsToStr(args), Utils.kwargsToStr(kwargs))) elif len(kwargs.keys()) > 0: print ("[*] %s(%s)" % (self.thisName, Utils.kwargsToStr(kwargs))) else: print ("[*] %s(%s)" % (self.thisName, Utils.argsToStr(args))) if len(args) > 0: return AbstractType(args[0]) else: return AbstractType("By_%s" % (self.thisName)) # Type is Module def __getattr__(self, name): return AbstractType("%s.%s" % (self.thisName, name)) | cs |
이번에 추상화에서 얻어낸 가장 큰 산물인 AbstractType 클래스이다. 생성자에서 호출한 이름을 인자로 받으며(Global.py 참고), __call__ 메서드를 정의하여 인스턴스가 함수로써 사용될 경우 *args, **kwargs 등 가변 길이 변수를 받아서 print로 이름과 함께 로깅하도록 하였다. 또한 Global.py에서처럼 __getattr__ 메서드를 정의하여 모듈 내의 함수에 접근하는 것과 같은 행위를 할 경우 현재 이름에 새 이름을 붙여 AbstractType 클래스를 새로 생성해 리턴하는 것으로 함수의 경우 위에서 정의한 __call__ 에 의해 자동으로 로깅이 되도록 하였다.
이를 이용하면 따로 모듈과 함수를 직접 구현해주지 않고도 호출하는 문자열을 이용해 그때그때 상황에 맞는 AbstractType을 생성하여 로깅하는 것이 가능해지며, 악성 스크립트 분석에 큰 도움이 될 수 있다.
[실제 악성 스크립트 분석 결과]
실제로 스크립트 분석 결과 구체적인 모듈이나 함수 정의는 전혀 하지 않았음에도 모듈 및 함수, 그 인자까지 전부 로깅되는 것을 볼 수 있다. 한 언어에 대한 이해가 깊어지면 이렇게 효율적이고 추상적인 코드 작성이 가능해진다는 것을 제대로 느낄 수 있는 프로젝트였다. 현재는 print로밖에 로깅을 진행하지 않지만 이후 파일이나 리턴 값으로 받을 수 있게 하면 자동 분석에도 응용이 가능할 것으로 보인다. 좀 더 고도화를 진행해 볼 예정이다.
'Programming' 카테고리의 다른 글
Python + selenium을 이용한 네이버 메일 크롤러 (3) | 2018.09.07 |
---|---|
Visual Studio에서 보호 기법 해제하고 바이너리 빌드하기 (0) | 2018.07.18 |
[Python] 멀티프로세싱 환경에서의 logging doRollOver() Error 32 해결법 (0) | 2018.04.09 |
[Python] 데코레이터(Decorator)의 이해 (0) | 2018.03.11 |
[Python] 파이썬 2 에서의 Division(나누기) 문제 (0) | 2018.02.07 |