int $0x80

LLVM 입문 - LLVM Obfuscator의 구조 본문

컴퓨터공부/엘엘비엠

LLVM 입문 - LLVM Obfuscator의 구조

cd80 cd80 2016.07.15 21:04

이번 글에서는 LLVM 3.6.1기반의 LLVM Obfuscator와, LLVM 3.6.1을 디핑해 LLVM을 커스터마이즈 하기 위해 어떻게 하면 되는 가를 공부합니다


먼저 LLVM Obfuscator를 클론합니다


git clone -b llvm-3.6.1 https://github.com/obfuscator-llvm/obfuscator.git



이제 LLVM 3.6.1을 받아야하는데, http://clang.llvm.org/get_started.html 을 그대로 따라하면 최신버젼으로 받아지기 때문에 release_36 브랜치를 직접 지정해 받아야합니다.

약간 슬픈일은 release_36브랜치의 버젼이 3.6.1이 아닌 3.6.2입니다

그래서 약간 차이가 더 생기긴 하지만 그래도 3.9.0이랑 비교하는것보단 적게 차이가 존재합니다


예를들어

svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm

이 명령문을

svn co http://llvm.org/svn/llvm-project/llvm/branches/release_36 llvm

으로 바꿔 실행하면됩니다.

그 외에는 모두 동일하게 하면 되는데 libcxx는 건너뛰어주세요


모두 완료하시면 위와 같이 llvm 디렉토리와 obfuscator디렉토리가 존재합니다


diff 결과가 263줄이나 나오지만 이건 LLVM 모듈 개발하면서 겪는 상황보다는 아주 쾌적합니다

그리고 다행히 이중에서 약 10줄정도만 보면 됩니다


`

이 밑으로도 약 200개 가량의 결과가 있지만 총 diff결과(스샷에 있는것 외에도)에서 필요한것만 추려보자면

Only in obfuscator/include/llvm: CryptoUtils.h

Only in obfuscator/include/llvm/Transforms: Obfuscation

Files llvm/lib/Transforms/CMakeLists.txt and obfuscator/lib/Transforms/CMakeLists.txt differ

Files llvm/lib/Transforms/IPO/LLVMBuild.txt and obfuscator/lib/Transforms/IPO/LLVMBuild.txt differ

Files llvm/lib/Transforms/IPO/PassManagerBuilder.cpp and obfuscator/lib/Transforms/IPO/PassManagerBuilder.cpp differ

Files llvm/lib/Transforms/LLVMBuild.txt and obfuscator/lib/Transforms/LLVMBuild.txt differ

Files llvm/lib/Transforms/Makefile and obfuscator/lib/Transforms/Makefile differ

Only in obfuscator/lib/Transforms: Obfuscation

위 7개 결과의 차이점을 분석해보겠습니다

우선 맨 윗 두줄은 include 경로에 있는 차이기 때문에 굳이 보지 않겠습니다


 ✘ ⚡ root@vultr  ~/diffLLVM  diff  llvm/lib/Transforms/CMakeLists.txt  obfuscator/lib/Transforms/CMakeLists.txt

8a9

> add_subdirectory(Obfuscation)

 ✘ ⚡ root@vultr  ~/diffLLVM 

맨처음으로 lib/Transforms/CMakeLists.txt 의 차이가 존재합니다


 ⚡ root@vultr  ~/diffLLVM/obfuscator/lib/Transforms   llvm-3.6.1  cat CMakeLists.txt

add_subdirectory(Utils)

add_subdirectory(Instrumentation)

add_subdirectory(InstCombine)

add_subdirectory(Scalar)

add_subdirectory(IPO)

add_subdirectory(Vectorize)

add_subdirectory(Hello)

add_subdirectory(ObjCARC)

add_subdirectory(Obfuscation)

 ⚡ root@vultr  ~/diffLLVM/obfuscator/lib/Transforms   llvm-3.6.1  ls

CMakeLists.txt  Hello  IPO  InstCombine  Instrumentation  LLVMBuild.txt  Makefile  Obfuscation  ObjCARC  Scalar  Utils  Vectorize

 ⚡ root@vultr  ~/diffLLVM/obfuscator/lib/Transforms   llvm-3.6.1 

lib/Transforms/ 밑에 Obfuscation이라는 폴더가 있고 CMakeLists.txt에서 해당 디렉토리를 add_subdirectory() 를 이용해 추가해주면 됩니다

우리가 새로운 모듈을 제작할때도 예를들어 CustomModules라는 디렉토리를 만들어서 코드를 작성하고, lib/Transforms/CMakeLists.txt에 add_subdirectory(CustomModules)를 추가하면 됩니다



⚡ root@vultr  ~/diffLLVM  diff llvm/lib/Transforms/IPO/LLVMBuild.txt obfuscator/lib/Transforms/IPO/LLVMBuild.txt

23c23

< required_libraries = Analysis Core IPA InstCombine Scalar Support Target TransformUtils Vectorize

---

> required_libraries = Analysis Core IPA InstCombine Scalar Support Target TransformUtils Vectorize Obfuscation

 ✘ ⚡ root@vultr  ~/diffLLVM 

여기서는 required_libraries라는 줄에 Obfuscation이 있냐 없냐가 차이인데,

여기서 한가지 설명이 필요합니다

LLVM은 아주 다양한 용도로 사용되고 있기 때문에 각 상황마다 꼭 필요한 기능이 있고 전혀 필요없는 기능이 있습니다

그래서 각각 기능들은 개별적인 라이브러리 파일로 빌드가 되고

LLVM은 이런 의존성(required_libraries 등등)을 검사해 필요한 라이브러리만 링크시킵니다

여기서 required_libraries에 Obfuscation이 없다면, 빌드시에는 헤더를 잘 참조하기 때문에 문제가 생기지 않지만 링크과정에서 라이브러리가 링크되지 않기 때문에 undefined references for symbol "llvm obfuscator에서 사용하는 함수" 라는 에러가 발생하게 됩니다

우리도 모듈을 개발하면 여기에 의존성을 명시해야 합니다



다음 차이입니다



(사진이 커 생략됨)

✘ ⚡ root@vultr  ~/diffLLVM  diff llvm/lib/Transforms/IPO/PassManagerBuilder.cpp obfuscator/lib/Transforms/IPO/PassManagerBuilder.cpp

31a32,37

> #include "llvm/Transforms/Obfuscation/BogusControlFlow.h"

> #include "llvm/Transforms/Obfuscation/Flattening.h"

> #include "llvm/Transforms/Obfuscation/Split.h"

> #include "llvm/Transforms/Obfuscation/Substitution.h"

> #include "llvm/CryptoUtils.h"

>

80a87,102

> // Flags for obfuscation

> static cl::opt<bool> Flattening("fla", cl::init(false),

>                                 cl::desc("Enable the flattening pass"));

>

> static cl::opt<bool> BogusControlFlow("bcf", cl::init(false),

>                                       cl::desc("Enable bogus control flow"));

>

> static cl::opt<bool> Substitution("sub", cl::init(false),

>                                   cl::desc("Enable instruction substitutions"));

>

> static cl::opt<std::string> AesSeed("aesSeed", cl::init(""),

>                                     cl::desc("seed for the AES-CTR PRNG"));

>

> static cl::opt<bool> Split("spli", cl::init(false),

>                            cl::desc("Enable basic block splitting"));

>

98a121,126

>

>     // Initialization of the global cryptographically

>     // secure pseudo-random generator

>     if(!AesSeed.empty()) {

>         llvm::cryptoutils->prng_seed(AesSeed.c_str());

>     }

161a190,193

>   MPM.add(createSplitBasicBlock(Split));

>   MPM.add(createBogus(BogusControlFlow));

>   MPM.add(createFlattening(Flattening));

>

179a212

>     MPM.add(createSubstitution(Substitution));

373a407

>   MPM.add(createSubstitution(Substitution));

이번엔 모두 필요합니다

우선 맨처음에 include하는것은 자명합니다

그 다음


> // Flags for obfuscation

> static cl::opt<bool> Flattening("fla", cl::init(false),

>                                 cl::desc("Enable the flattening pass"));

>

> static cl::opt<bool> BogusControlFlow("bcf", cl::init(false),

>                                       cl::desc("Enable bogus control flow"));

>

> static cl::opt<bool> Substitution("sub", cl::init(false),

>                                   cl::desc("Enable instruction substitutions"));

>

> static cl::opt<std::string> AesSeed("aesSeed", cl::init(""),

>                                     cl::desc("seed for the AES-CTR PRNG"));

>

> static cl::opt<bool> Split("spli", cl::init(false),

>                            cl::desc("Enable basic block splitting"));

이렇게 flag를 선언해주는데, 첫번재 인자는 커맨드라인 명령이고, 두번째는 초기값, 세번째는 플래그에 대한 설명입니다

Flattening을 초기값(cl::init(false))에서 true로 변경하고 싶다면 -mllvm -fla나 -mllvm -fla=1 을 컴파일 시 추가 하면 됩니다.

Flattening, BogusControlFlow, Substitution 등은 모두 플래그의 값을 갖는 스태틱변수로, 플래그에 해당하는 각각의 Pass들을 실행할지 말지 결정할 수 있습니다


이 플래그들은 바로 그 다음 diff에서 보이는 코드에서 사용됩니다

>

>     // Initialization of the global cryptographically

>     // secure pseudo-random generator

>     if(!AesSeed.empty()) {

>         llvm::cryptoutils->prng_seed(AesSeed.c_str());

>     }

161a190,193

>   MPM.add(createSplitBasicBlock(Split));

>   MPM.add(createBogus(BogusControlFlow));

>   MPM.add(createFlattening(Flattening));

>

179a212

>     MPM.add(createSubstitution(Substitution));

373a407

>   MPM.add(createSubstitution(Substitution));

이 코드들은 지금 diff한 파일인

lib/Transforms/IPO/PassManagerBuilder.cpp 의 populateModulePassManager 함수에 있습니다

여기서는 플래그들을 인자로 넘겨 해당 함수에서 플래그값을 이용해 난독화 적용 여부를 결정합니다



마지막으로 아래 diff 결과는 직접 어떻게 다른지 확인해보시길 바랍니다

Files llvm/lib/Transforms/LLVMBuild.txt and obfuscator/lib/Transforms/LLVMBuild.txt differ

Files llvm/lib/Transforms/Makefile and obfuscator/lib/Transforms/Makefile differ

Only in obfuscator/lib/Transforms: Obfuscation

특히 맨아래의 Only in obfuscator/lib/Transforms: Obfuscation은 LLVM Obfuscator 모듈의 실제 구현 파일들이 존재합니다




중요한 포인트를 정리하자면

1. lib/Transforms/ 밑에 우리 디렉토리를 만들어 작업한다

2. lib/Transforms/CMakeLists.txt 와 lib/Transforms/LLVMBuild.txt에 우리 모듈 디렉토리를 추가해준다

3. lib/Transforms/IPO/LLVMBuild.txt 의 required_libraries에 우리 모듈 이름을 추가한다

4. lib/Transforms/IPO/PassManagerBuilder.txt 의 populateModulePassManager 함수에서 우리 모듈을 ModulePassManager에 추가하도록 한다


이번 글의 내용을 꼭 직접 하나씩 확인해보시면서 진행하시길 권합니다. 새로운 모듈을 개발할 때마다 하나씩 까먹고 고생하는 일이 초반에 종종 생깁니다

5 Comments
댓글쓰기 폼