Rustのコンパイラを改造して難読化機能を追加

# 経緯
リバースエンジニアリングがむずい最強バイナリをつくりたいよね。
obfuscator-llvmが難読化手法としてあるがRustには対応していない。でもRustのコンパイルの仕組み的には途中にLLVM-IRを挟むので行けそう...?

ということでやってみた
パッチあてる作業がかなり面倒なのであてたものが[こちら](https://github.com/blend-tea/ollvm-rust-project/tree/rustc/17.6)です。
# 手順
1. LLVMにパッチをあてる
2. config.tomlを編集しRustをソースコードからビルドする

今回こちらを参考に1.76.0のバージョンに対応させました。

https://vrls.ws/posts/2023/06/obfuscating-rust-binaries-using-llvm-obfuscator-ollvm/

# リポジトリのクローン
[Hikari-LLVM15](https://github.com/61bcdefg/Hikari-LLVM15)にはLLVMバージョン17.0.6と18.0.8がある。記事作成時点では18.0.8を使っているRustバージョンがなかったので17.0.6を使うことに。
Rustが使用しているLLVMの情報の調べ方
例
```
$ ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/rustc --version --verbose
rustc 1.77.0 (aedd173a2 2024-03-17)
binary: rustc
commit-hash: aedd173a2c086e558c2b66d3743b344f977621a7
commit-date: 2024-03-17
host: x86_64-unknown-linux-gnu
release: 1.77.0
LLVM version: 17.0.6
```

今回使用するRustのバージョンを1.76.0にした。Rustのソースコード，Rust用LLVMのソースコード，差分を取るためにHikari-LLVMをクローンする
```
git clone --single-branch --branch 1.76.0 --depth 1 https://github.com/rust-lang/rust rust-1.76.0
git clone --single-branch --branch rustc-1.76.0 --depth 1 https://github.com/rust-lang/llvm-project llvm-17.6
git clone --single-branch --branch llvm-17.6.0rel --recursive --depth 1 https://github.com/61bcdefg/Hikari-LLVM15 ollvm-17.6
```
# LLVMの修正・ビルド
## 差分を取る
patchをあてるために差分を取る。
```
git diff llvm-17.6/llvm ollvm-17.6/llvm/ > llvm.patch
```
権限変更に関する情報が多かったので魔法の呪文を唱えて再度差分をとった。
```
chmod 777 *
```
不要そうなtestなどの変更を削除しvimで%s/llvm-17\.6\///gと%s/ollvm-17\.6\///gでパスを修正しパッチをあてられるようにする。ここら辺のどれを削除したとかあまり記憶にない（うごいたからよし！状態）なので[こちら](https://github.com/blend-tea/ollvm-rust-project/blob/rustc/17.6/llvm.patch)のファイルを参照してください。
## パッチをあてる
```
cd llvm-17.6
git apply --reject --ignore-whitespace ../llvm.patch
```
## ビルド
clangなどを用意する。バージョンは17。他にはcmakeとninjaが必要だったけどバージョンはそんなに重要ではない。
```
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 17
```
```
mkdir build && cd build
cmake -G "Ninja" ../llvm -DCMAKE_INSTALL_PREFIX="./llvm_x64" \
  -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;lld;" -DLLVM_TARGETS_TO_BUILD="X86" \
  -DBUILD_SHARED_LIBS=ON -DLLVM_INSTALL_UTILS=ON -DLLVM_INCLUDE_TESTS=OFF -DLLVM_BUILD_TESTS=OFF \
  -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_BUILD_BENCHMARKS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF \
  -DLLVM_ENABLE_BACKTRACES=OFF -DLLVM_BUILD_DOCS=OFF  -DBUILD_SHARED_LIBS=OFF \
  -DCMAKE_CXX_COMPILER=clang++-17 -DCMAKE_C_COMPILER=clang-17
```
問題が起きなければ引き続き...jに続く数字はお好みで
```
cmake --build . -j16
cmake --install .
```
# Rustのビルド
```
cd rust-1.76.0
cp config.example.toml config.toml
```
config.example.tomlがあるのでコピーしてconfig.tomlを作成。
1. \[rust\]セクション内 debug=false
2. \[rust\]セクション内 channel = "nightly"
3. \[target.x86_64-unknown-linux-gnu\]セクション内 llvm-config = "/home/user/.../llvm-17.6/build/bin/llvm-config" 先ほどコンパイルした際に生成されたllvm-configのパスを指定する。

設定が終わったら
```
python3 ./x.py build
python3 ./x.py build --stage 2 rustc
python3 ./x.py build cargo
python3 ./x.py build --stage 2 cargo
```
でコンパイルされることを祈りましょう。
```
$ ls rust-1.76.0/build/x86_64-unknown-linux-gnu
doc/              stage0-codegen/   stage0-sysroot/   stage1/           stage1-tools/     stage2-std/
md-doc/           stage0-rustc/     stage0-tools/     stage1-rustc/     stage1-tools-bin/ stage2-tools/
stage0/           stage0-std/       stage0-tools-bin/ stage1-std/       stage2/           stage2-tools-bin
```
stage0→stage1→stage2の順でコンパイルされていきます。数字が大きいほうがいいと思ってくれれば大丈夫です。コンパイラのビルドプロセスを参考にしてください。

https://zenn.dev/ohno418/articles/f57219c1b2e53e

ということでstage2にはrustcがstage2-tools-binにはcargoが入っていると思います。

# 実験
適当なプログラムをコンパイルしてみる
```rust
fn main() {
    println!("Hello, world!");
}
```
-Cllvm-argsで難読化オプションを指定する。指定できるオプションは[こちら](https://github.com/HikariObfuscator/Hikari/wiki/Usage)
```bash
$ ./rust-1.76.0/build/x86_64-unknown-linux-gnu/stage2/bin/rustc -Cllvm-args=-enable-allobf hello.rs
```
## 解析
とりあえずstripせずに解析
![スクリーンショット 2024-08-31 230425.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3807852/1199202c-5922-3b85-d0be-4fa51006648a.png)
main関数ですが不明なスタック変数が大量にあり更にジャンプ先がわかりません。アセンブリを見たところjmp raxとなっていました。println!におそらく使われるstd :: io :: stdio :: _printのxrefを見ましたがなにもありませんでした。