Try   HackMD

android porting ollvm 混淆

check clang version

C:\Users\x213212\AppData\Local\Android\Sdk\ndk\26.1.10909125\toolchains\llvm\prebuilt\windows-x86_64\bin\
C:\Users\x213212\AppData\Local\Android\Sdk\ndk\26.1.10909125\toolchains\llvm\prebuilt\windows-x86_64\bin>clang -v
Android (10552028, based on r487747d) clang version 17.0.2 (https://android.googlesource.com/toolchain/llvm-project d9f89f4d16663d5012e5c09495f3b30ece3d2362)
Target: x86_64-w64-windows-gnu
Thread model: posix
InstalledDir: C:/Users/x213212/AppData/Local/Android/Sdk/ndk/26.1.10909125/toolchains/llvm/prebuilt/windows-x86_64/bin

git clone depth 1 branch llvmorg-17.0.2 https://github.com/llvm/llvm-project.git

install visual studio

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

C:\llvm-project\llvm\lib\Passes\Obfuscation\IPObfuscationContext.cpp

    NF->getBasicBlockList().splice(NF->begin(), F->getBasicBlockList());
      NF->splice(NF->begin(), F);

C:\llvm-project\llvm\lib\Passes\Obfuscation\Utils.cpp

static bool valueEscapes(const Instruction &Inst) {
  if (!Inst.getType()->isSized())
    return false;

  const BasicBlock *BB = Inst.getParent();
  for (const User *U : Inst.users()) {
    const Instruction *UI = cast<Instruction>(U);
    if (UI->getParent() != BB || isa<PHINode>(UI))
      return true;
  }
  return false;
}

C:\llvm-project\llvm\lib\Passes\Obfuscation\Flattening.cpp

#include "Utils.h"
#include "CryptoUtils.h"
#include "Flattening.h"
#include "SplitBasicBlock.h"

// using namespace ...
using namespace llvm;
using std::vector;

#define DEBUG_TYPE "flattening"
// Stats
STATISTIC(Flattened, "Functions flattened");

// --------------------------------------------------
PreservedAnalyses FlatteningPass::run(Function &F, FunctionAnalysisManager &AM) {
    // 確認是否需要對此函式進行 Flatten
    if (toObfuscate(flag, &F, "fla")) {
        INIT_CONTEXT(F);
        if (flatten(F)) {
            ++Flattened;
        }
        return PreservedAnalyses::none();
    }
    return PreservedAnalyses::all();
}

// --------------------------------------------------
bool FlatteningPass::flatten(Function &F) {
    // 若基本塊數量 <= 1, 無需平坦化
    if (F.size() <= 1) {
        return false;
    }

    // 避免處理特定函式, 例如 basic_ostream
    if (F.getName().str().find("$basic_ostream") != std::string::npos) {
        outs() << "[obf] force_nofla: " << F.getName().str() << "\n";
        return false;
    }

    // 1. 收集除 entryBB 以外的基本塊
    std::vector<BasicBlock *> origBB;
    for (BasicBlock &BB : F) {
        origBB.push_back(&BB);
    }
    // 移除第一個 (entryBB)
    origBB.erase(origBB.begin());
    BasicBlock &entryBB = F.getEntryBlock();

    // 2. 若 entryBB 終結指令是條件跳轉,則嘗試 splitBasicBlock
    bool bEntryBB_isConditional = false;
    if (auto *br = dyn_cast<BranchInst>(entryBB.getTerminator())) {
        if (br->isConditional()) {
            // 檢查 terminator 之後是否還有其他指令
            Instruction *Term = entryBB.getTerminator();
            if (Instruction *NextI = Term->getNextNode()) {
                // 若 terminator 之後還有 IR 指令,才需要分割
                BasicBlock *newBB = entryBB.splitBasicBlock(NextI, "newBB");
                // 把新的 BB 插入陣列最前
                origBB.insert(origBB.begin(), newBB);
            }
            bEntryBB_isConditional = true;
        }
    }

    // 3. 建立分發 (dispatchBB) 與返回 (returnBB) 基本塊
    BasicBlock *dispatchBB = BasicBlock::Create(*CONTEXT, "dispatchBB", &F, &entryBB);
    BasicBlock *returnBB   = BasicBlock::Create(*CONTEXT, "returnBB",   &F, &entryBB);
    // 在 returnBB 裡插入一條無條件跳轉到 dispatchBB
    BranchInst::Create(dispatchBB, returnBB);

    // 將 entryBB 移到 dispatchBB 前
    entryBB.moveBefore(dispatchBB);

    // 4. 若 entryBB 原先是 conditional,則替換其 terminator
    if (bEntryBB_isConditional) {
        Instruction *OldTerm = entryBB.getTerminator();
        BranchInst::Create(dispatchBB, &entryBB);  // 插入新的無條件分支
        OldTerm->eraseFromParent();                // 安全刪除舊terminator
    } else {
        // 否則直接插入分支
        BranchInst::Create(dispatchBB, &entryBB);
    }

    // 5. 在 entryBB 末尾插入 alloca + store 初始化 switch 變數
    int randNumCase = rand();
    auto *brDispatchBB = dyn_cast<BranchInst>(entryBB.getTerminator());
    AllocaInst *swVarPtr = new AllocaInst(TYPE_I32, 0, "swVar.ptr", brDispatchBB);
    new StoreInst(CONST_I32(randNumCase), swVarPtr, brDispatchBB);

    // 6. 在 dispatchBB 裡 load 該 switch 變數
    LoadInst *swVar = new LoadInst(TYPE_I32, swVarPtr, "swVar", false, dispatchBB);

    // 建一個 switch 指令
    BasicBlock *swDefault = BasicBlock::Create(*CONTEXT, "swDefault", &F, returnBB);
    BranchInst::Create(returnBB, swDefault);
    SwitchInst *swInst = SwitchInst::Create(swVar, swDefault, 0, dispatchBB);

    // 7. 將原BB移到returnBB前 & 分配 case
    for (BasicBlock *BB : origBB) {
        BB->moveBefore(returnBB);
        swInst->addCase(CONST_I32(randNumCase), BB);
        randNumCase = rand();
    }

    // 8. 在每個原BB尾端更新switch 變數 & 跳轉到 returnBB
    for (BasicBlock *BB : origBB) {
        Instruction *Term = BB->getTerminator();
        unsigned numSucc  = Term->getNumSuccessors();

        // 若無 successors (e.g. ret), 不處理
        if (numSucc == 0) {
            continue;
        }
        // 若單一路徑
        if (numSucc == 1) {
            // 先取出舊 terminator
            Instruction *OldTerm = BB->getTerminator();
            BasicBlock *sucBB    = OldTerm->getSuccessor(0);

            // 找對應 case
            ConstantInt *numCase = swInst->findCaseDest(sucBB);
            new StoreInst(numCase, swVarPtr, BB);
            // 刪除舊 terminator
            OldTerm->eraseFromParent();
            // 新增無條件 branch 到 returnBB
            BranchInst::Create(returnBB, BB);

        // 若雙路徑(條件)
        } else if (numSucc == 2) {
            Instruction *OldTerm = BB->getTerminator();
            auto *br = dyn_cast<BranchInst>(OldTerm);
            if (!br || !br->isConditional()) {
                // 不是條件跳轉就略過
                continue;
            }
            // true/false 兩分支
            ConstantInt *numCaseTrue  = swInst->findCaseDest(br->getSuccessor(0));
            ConstantInt *numCaseFalse = swInst->findCaseDest(br->getSuccessor(1));

            // 用 select 決定要存哪個 case
            SelectInst *sel = SelectInst::Create(br->getCondition(), numCaseTrue, numCaseFalse, "", OldTerm);
            new StoreInst(sel, swVarPtr, BB);
            // 刪除舊 terminator
            OldTerm->eraseFromParent();
            // 新增無條件 branch
            BranchInst::Create(returnBB, BB);
        }
    }

    // 9. 呼叫 fixStack 來修復 PHI 與逃逸變數
    fixStack(F);

    return true;
}

// --------------------------------------------------
FlatteningPass *llvm::createFlattening(bool flag) {
    return new FlatteningPass(flag);
}

這邊把build 好的 llvm 直接替換到 android sdk

C:\llvm-project\build
C:\Users\x213212\AppData\Local\Android\Sdk\ndk\26.1.10909125\toolchains\llvm\prebuilt\windows-x86_64

image

image
正常編譯成宮是不會有任何訊息這邊要把這些註解
image

app

package com.example.secchat

import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.secchat.databinding.ActivityInputBinding
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import retrofit2.Call
import retrofit2.Callback

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.UUID
import okhttp3.Response
import java.security.cert.X509Certificate
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager


class InputActivity : AppCompatActivity() {

    private lateinit var binding: ActivityInputBinding
    private lateinit var apiService: ApiService
    // 靜態加載 secchat 庫
    companion object {
        // 靜態加載 secchat 庫
        init {
            System.loadLibrary("secchat")
        }

    }

    object ApiClient {
        private const val TAG = "ApiClient"
        private const val API_URL = "https://172.22.100.167:8443"

        private fun getUnsafeOkHttpClient(): OkHttpClient {
            try {
                val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
                    override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}
                    override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {}
                    override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
                })

                val sslContext = SSLContext.getInstance("SSL")
                sslContext.init(null, trustAllCerts, java.security.SecureRandom())

                val sslSocketFactory = sslContext.socketFactory
                return OkHttpClient.Builder()
                    .sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
                    .hostnameVerifier { _, _ -> true }
                    .build()
            } catch (e: Exception) {
                throw RuntimeException(e)
            }
        }

        fun sendCredentials(username: String?, password: String?) {
            val client = getUnsafeOkHttpClient()

            val json = """{"username": "$username", "password": "$password"}"""
            val mediaType = "application/json; charset=utf-8".toMediaType()
            val body = json.toRequestBody(mediaType)

            val request = Request.Builder()
                .url(API_URL)
                .post(body)
                .build()

            Thread {
                try {
                    val response: Response = client.newCall(request).execute()
                    if (response.isSuccessful) {
                        Log.i(TAG, "Response: ${response.body?.string()}")
                    } else {
                        Log.e(TAG, "Request failed: ${response.code}")
                    }
                } catch (e: Exception) {
                    Log.e(TAG, "Error sending request", e)
                }
            }.start()
        }
    }


    // 聲明 native 方法
    external fun stringFromJNI(): String
    external fun sub(a: Int, b: Int): Int
    external fun bcf(input: String): String
    external fun fla(x: Int, y: Int): String
    // 使用 UUID 類生成有效的 UUID
    private fun generateRandomUserId(): String {
        return UUID.randomUUID().toString()
    }

    override fun onCreate(savedInstanceState: Bundle?) {

        val message = stringFromJNI()
        Log.d("JNI_MESSAGE", message)
        super.onCreate(savedInstanceState)


        binding = ActivityInputBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 初始化 Retrofit
        val retrofit = Retrofit.Builder()
            .baseUrl("http://192.168.30.100:8080") // 替换为你的API服务器地址
            .addConverterFactory(GsonConverterFactory.create())
            .build()

        apiService = retrofit.create(ApiService::class.java)

        // 设置按钮点击事件
        binding.btnSubmit.setOnClickListener {
            // 調用 native 方法
            val message = stringFromJNI()
            Log.d("JNI_MESSAGE", message)

            val subValue = sub(10, 3)
            Log.d("JNI_MESSAGE", "sub(10,3) = $subValue")

            val bcfValue = bcf("Hi")
            Log.d("JNI_MESSAGE", "bcf(\"Hi\") = $bcfValue")

            val flaValue = fla(2, 5)
            Log.d("JNI_MESSAGE", "fla(2,5) = $flaValue")

        }
    }

jni

#include <jni.h>
#include <string>
#include <sstream>
#include <cstdio>  // for snprintf

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_secchat_InputActivity_stringFromJNI(JNIEnv *env, jobject thiz) {
    std::string message = "Hello from C++!";
    return env->NewStringUTF(message.c_str());
}

extern "C" JNIEXPORT jint JNICALL
Java_com_example_secchat_InputActivity_sub(JNIEnv *env, jobject thiz, jint a, jint b) {
    return a - b;
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_secchat_InputActivity_bcf(JNIEnv *env, jobject thiz, jstring input) {
    const char* inputStr = env->GetStringUTFChars(input, nullptr);
    if (!inputStr) {
        return env->NewStringUTF("Invalid input");
    }
    std::string result = std::string("BCF: ") + inputStr;
    env->ReleaseStringUTFChars(input, inputStr);
    return env->NewStringUTF(result.c_str());
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_secchat_InputActivity_fla(JNIEnv *env, jobject thiz, jint x, jint y) {
    int sum = x + y;
    char buffer[128];
    if (sum < 5) {
        snprintf(buffer, sizeof(buffer), "x = %d, y = %d, x + y 小於 5", x, y);
    } else if (sum == 5) {
        snprintf(buffer, sizeof(buffer), "x = %d, y = %d, x + y 等於 5", x, y);
    } else {
        snprintf(buffer, sizeof(buffer), "x = %d, y = %d, x + y 大於 5", x, y);
    }
    return env->NewStringUTF(buffer);
}

image
image