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
NF->getBasicBlockList().splice(NF->begin(), F->getBasicBlockList());
NF->splice(NF->begin(), F);
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;
}
#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
正常編譯成宮是不會有任何訊息這邊要把這些註解
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")
}
}
#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);
}