# Security SDK 使用說明文件 ## 目錄 - [SDK 簡介](#SDK-簡介) - [功能模組使用方式](#功能模組使用方式) - [建立 SDK 實例](#建立-SDK-實例) - [裝置資訊](#裝置資訊) - [模擬器辨識](#模擬器辨識) - [IMU追蹤](#IMU追蹤) - [觸控資料](#觸控資料) - [Callback 機制說明](#Callback-機制說明) - [附錄](#附錄) <br/> ## SDK 簡介 Security SDK 提供跨平台的感測器資料收集功能,主要應用於裝置行為識別與異常使用者偵測。SDK 採模組化設計,支援透過 Lua 或 C++ 調用,回傳資料為 C++ 結構(struct)格式。 目前支援 Android 平台,包含以下三大功能模組: - **裝置資訊收集**:回傳設備的硬體、系統與網路特徵(如 model、CPU Info、RAM、fingerprint 等)。 - **IMU 追蹤(Inertial Measurement Unit)**:包含陀螺儀(Gyroscope)與加速度計,可追蹤角速度、角度變化與加速度。 - **觸控資料紀錄**:定時收集點擊座標與時間戳,用於分析使用者的操作樣態。 > 下圖為 SDK 整體模組架構: ![SecuritySDK_Overview.drawio](https://hackmd.io/_uploads/S1Wspdluxg.png) <br/> ## 功能模組使用方式 > 平台實作都在預編好的 `libsecuritysdk.so`。 使用者只需要 include 下列檔案,以及加入 SecuritySDK.cpp 即可,其他實作都已經編入 .so。 #### SDK介面檔案: - `ISecuritySDK.h`:定義功能操作的純虛擬介面 - `SecuritySDKTypes.h`:定義共用資料結構 - `SecuritySDK.h`:統一入口,負責實例建立與平台綁定 - `SecuritySDK.cpp`:因為內含 `cocos2d::Director::getInstance()->getScheduler()->performFunctionInCocosThread(...)`,避免把 cocos2d 拉進 .so 而使體積暴增,所以放在外面。 #### SDK平台檔案 - `SecuritySDKImplAndroid.h` - `SecuritySDKImplIOS.h` #### 模組功能檔案 - `DeviceInfo/DeviceInfo.h` - `Emulator/EmulatorDetector.h` - `Emulator/IEmulatorDetectionCondition.h` - `Emulator/JsonEmulatorCondition.h` - `IMU/IMUData.h` - `IMU/IMUSensor.h` - `TouchEvent/TouchEventCollector.h` - `TouchEvent/TouchPointData.h` ### 建立 SDK 實例 請於初始化階段建立並註冊 `SecuritySDK` 實例,建議如下使用: #### C++ ```cpp #include "securitysdk/SecuritySDK.h" auto securitySDK = SecuritySDK::getInstance(); securitySDK->RegisterSecuritySDK(); // 使用方法:檢測模擬器 securitysdk::DeviceInfo info = securitySDK->getDeviceInfo(); ``` #### Lua 在C++使用`RegisterLua(lua_State* L);` 註冊 Lua 綁定類別與方法 ```cpp SecuritySDK::getInstance()->RegisterLua(L); ``` Lua 端初始化與使用 ```lua -- 獲得 C++ 層單例 self.sdk = Inanna.GetSecuritySDK() -- 初始化平台原生實作(Android / iOS) self.sdk:RegisterSecuritySDK() -- 獲得DeviceInfo local info = self.sdk:getDeviceInfo() ``` 所有功能請以 `securitySDK` 作為入口。`RegisterSecuritySDK()` 會根據平台自動建立對應的實作類別 <br/> ### 裝置資訊 可使用下列方法獲取裝置資訊: ```cpp DeviceInfo info = securitySDK->getDeviceInfo(); ``` #### DeviceInfo回傳結構: ```cpp struct DeviceInfo { std::string ID; std::string model; std::string osVersion; std::string cpuInfo; std::string fingerprint; std::string localIpAddress; std::string networkType; std::string macAddress; std::string kernelABIInfo; int totalMemoryMb; int availableMemoryMb; std::string buildInfo; int screenWidth; int screenHeight; }; ``` #### DeviceInfo結構說明: | 欄位名稱 | 型別 | 說明 | | ------------------- | ------------- | ---------------------- | | `ID` | `std::string` | 裝置唯一識別碼(如 Android ID) | | `model` | `std::string` | 裝置型號 | | `osVersion` | `std::string` | 作業系統版本 | | `cpuInfo` | `std::string` | CPU 架構與型號 | | `fingerprint` | `std::string` | 系統建置指紋 | | `localIpAddress` | `std::string` | 裝置的內網 IP 位址 | | `networkType` | `std::string` | 網路類型(如 Wi-Fi、LTE) | | `macAddress` | `std::string` | 裝置 MAC 位址 | | `kernelABIInfo` | `std::string` | 核心 ABI 描述(如 arm64-v8a) | | `totalMemoryMb` | `int` | 總記憶體(MB) | | `availableMemoryMb` | `int` | 可用記憶體(MB) | | `buildInfo` | `std::string` | 系統 build 資訊 | | `screenWidth` | `int` | 螢幕寬度(像素,橫向解析度) | | `screenHeight` | `int` | 螢幕高度(像素,縱向解析度) | --- <br/> ### 模擬器辨識 模擬器偵測邏輯使用 JsonEmulatorCondition 實作,支援多層邏輯巢狀與字串比對操作,條件配置格式如下: ![image](https://hackmd.io/_uploads/H1YjQmBIlg.png) #### Function 說明 - `setEmulatorDetectionCondition(const std::string& jsonConfig)` - **參數:** - `jsonConfig`:JSON 設定字串(而非檔案路徑) - **功能:** 載入指定的模擬器判斷條件(JSON 格式字串),供後續判斷使用。 - **回傳值:** `bool` — 成功載入為 `true`,載入失敗為 `false` - `reloadEmulatorDetectionCondition()` ⚠️ **已棄用** - **功能:** 重新載入上次設定的 JSON 條件內容(已棄用,不建議使用)。 - **回傳值:** `bool` — 目前固定回傳 `false` - `getEmulatorDetectionDescription()` - **功能:** 回傳目前載入的條件來源描述。 - **回傳值:** `std::string` — 條件說明文字 - `isEmulator()` - **功能:** 執行模擬器判斷,根據目前條件返回結果。若未設定條件,預設為 false。 - **回傳值:** `bool` — 為模擬器則回傳 `true`,否則回傳 `false` - `getTriggerRules()` - **功能**: 回傳觸發目前判定為模擬器的條件規則 - **回傳值**: `std::vector<TriggeredRule>` - 字串描述被觸發的規則 #### 資料結構說明 TriggeredRule | 欄位名稱 | 型別 | 說明 | |----------------|----------------|------------------------------| | `field` | `std::string` | 檢測欄位名稱 | | `operation` | `std::string` | 邏輯運算類型 | | `actualValue` | `std::string` | 實際值 | | `expectedValue` | `std::string` | 期望值 | | `description` | `std::string` | 規則描述 | #### 使用方法 1. **準備 JSON 配置字串**:將模擬器檢測的 JSON 配置準備為字串格式 2. **呼叫 `setEmulatorDetectionCondition(jsonConfig)`** 設定條件(傳入 JSON 字串而非檔案路徑) 3. **呼叫 `isEmulator()`** 檢查是否為模擬器 詳見附錄提供在 `AppDelegate.cpp` 中初始化 `SecuritySDK` 和調用 `JsonEmulator` 的完整範例 #### 支援操作 (Operation) | 操作名稱 | 說明 | | ----------------------- | ------------ | | `equals` | 精確匹配 | | `contains` | 包含字串 | | `starts_with` | 前綴比對 | | `ends_with` | 後綴比對 | | `regex` | 通配符比對(\*, ?) | | `less_than` | 小於 | | `greater_than` | 大於 | | `less_than_or_equal` | 小於等於 | | `greater_than_or_equal` | 大於等於 | #### 詳細模擬器檢測文件 - [模擬器檢測系統使用說明](https://hackmd.io/ppgD4A8_SeGneP9uSo9YvQ) - [JSON 模擬器檢測條件系統](https://hackmd.io/4A9gr2pqS-m1sJliby8nMA?view) --- <br/> ### 觸控資料 #### Function 說明 - `startDetectTouch(const luabridge::LuaRef cb, float durationSeconds, float intervalMs = 0.1f)` - **參數:** - `cb`:Lua callback 函式,會在指定時間結束時觸發 - `durationSeconds`:資料收集持續的時間(秒) - `intervalMs`:間隔秒數(毫秒) - **功能:** 啟動觸控資料收集,期間內會記錄所有點擊座標與時間。 - **回傳值:** `void` - `stopDetectTouch(const luabridge::LuaRef cb)` - **參數:** - `cb`:Lua callback 函式,會在停止後立即觸發 - **功能:** 中止正在進行中的觸控資料收集流程。 - **回傳值:** `void` - `exportTouchData()` - **參數:** 無 - **功能:** 將目前已收集到的觸控點資料導出為列表。 - **回傳值:** `std::vector<TouchExportData>` — 一組觸控點資料 #### 資料結構說明 TouchExportData | 欄位名稱 | 型別 | 說明 | |----------------|----------------|------------------------------| | `x` | `float` | 點擊座標 X | | `y` | `float` | 點擊座標 Y | | `timestamp` | `uint64_t` | 點擊時間 | | `hasRapidClicks` | `bool` | 是否快速點擊 | | `hasIntegerCoordinates` | `bool` | 是否點在整數座標 | > 每筆點擊紀錄代表一次觸控事件(如點擊螢幕)。 #### 使用順序建議: 1. **開始收集觸控資料** 呼叫 `startDetectTouch(callback, durationSeconds)` 啟動收集。SDK 將於背景中記錄每次點擊的 `(x, y)` 與對應時間戳。 2. **等待結束通知(或手動停止)** 當收集時間結束,或你主動呼叫 `stopDetectTouch(callback)` 時,SDK 將觸發 callback 通知資料已蒐集完成。 3. **導出觸控資料** 在 callback 觸發後立即呼叫 `exportTouchData()`,即可取得已收集的 `TouchExportData` 列表,包含所有點擊事件。 --- <br/> ### IMU追蹤 #### Function 說明 - `startIMUTracking(const luabridge::LuaRef cb, float durationSeconds, float sampleIntervalMs = 100.0f)` - **參數:** - `cb`:Lua callback 函式,會在追蹤時間結束時觸發 - `durationSeconds`:資料收集持續的時間(秒) - `sampleIntervalMs`:取樣區間(豪秒) - **功能:** 啟動 IMU 資料收集,並在指定時間結束時觸發 callback。 - **回傳值:** `void` - `stopIMUTracking(const luabridge::LuaRef cb)` - **參數:** - `cb`:Lua callback 函式,會在停止後立即觸發 - **功能:** 中止正在進行中的 IMU 收集流程。 - **回傳值:** `void` - `exportIMUData()` - **參數:** 無 - **功能:** 將當前緩衝區中的 IMU 資料導出為列表 - **回傳值:** `std::vector<GyroExportData>` — 一組已收集的 IMU 資料點 --- #### 資料結構說明 IMUExportData | 欄位名稱 | 型別 | 說明 | | ----------------------- | ---------- | --------------------- | | `timestamp` | `uint64_t` | 資料結束時的時間戳(單位:ns) | | `interval` | `float` | 此筆資料涵蓋的時間區段(秒) | | `deltaAngles[3]` | `std::array<float, 3>` | X, Y, Z 軸的角度總變化量 | | `avgAngularVelocity[3]` | `std::array<float, 3>` | X, Y, Z 軸的平均角速度 | | `avgAccelVelocity[3]` | `std::array<float, 3>` | X, Y, Z 軸的平均加速度(保留欄位) | | `sampleCount` | `size_t` | 該筆統計所使用的樣本數 | #### 使用順序建議: 1. **開始收集 IMU 資料** 呼叫 `startIMUTracking(callback, durationSeconds)` 啟動收集。SDK 將每 10ms 取樣一次角速度,並計算累積角度與統計值。 2. **等待結束通知(或手動停止)** 當收集時間結束(或你主動呼叫 `stopIMUTracking(callback)` 時),SDK 將觸發 callback 通知資料已準備完成。 3. **導出資料** 在 callback 觸發後呼叫 `exportIMUData()`,可取得一組 `IMUExportData` 統計結果,包含時間戳、平均角速度與樣本數等。 <br/> <br/> ## Callback 機制說明 本 SDK 所有支援 callback 的資料收集 API,皆要求傳入合法的 Lua function 作為 callback。 若未傳入合法函式(`luabridge::LuaRef`),對應的主功能(例如開始或停止收集)將不會執行。 --- ### 支援 callback 的 API 一覽 | 函式名稱 | 觸發時機 | |------------------------------|------------------| | `startIMUTracking(const luabridge::LuaRef cb, float durationSeconds)` | 時間結束後觸發 | | `stopIMUTracking(const luabridge::LuaRef cb)` | 停止後立即觸發 | | `startDetectTouch(const luabridge::LuaRef cb, float durationSeconds)` | 時間結束後觸發 | | `stopDetectTouch(const luabridge::LuaRef cb)` | 停止後立即觸發 | --- ### 行為說明 - 所有 callback 使用 `luabridge::LuaRef` 傳入,必須為有效的 Lua function。 - 若未傳入合法 callback,SDK 會記錄錯誤並**跳過主功能執行**。 - 所有 callback 都會被 `try-catch` 包裝,避免 Lua/C++ 層錯誤導致崩潰。 --- ### 使用範例(Lua) ```lua local function handleGyroDetectionComplete() print("陀螺儀偵測完成!時間到了") end SecuritySDK.startGyroTracking(handleGyroDetectionComplete, 10) ``` <br/> <br/> ## 附錄: ### 公開 Interface 定義(ISecuritySDK.h) ```cpp #ifndef ISECURITYSDK_H #define ISECURITYSDK_H #include "SecuritySDKTypes.h" #include <memory> #include <string> #if defined(ANDROID) #include <jni.h> #endif namespace luabridge { class LuaRef; } namespace securitysdk{ struct ISecuritySDK { virtual ~ISecuritySDK() {}; virtual DeviceInfo getDeviceInfo(std::string *outStringFormat = nullptr) = 0; virtual void startIMUTracking(const luabridge::LuaRef cb, float durationSeconds, float sampleIntervalSecond = 0.1f) = 0; virtual void stopIMUTracking(const luabridge::LuaRef cb) = 0; virtual std::vector<IMUExportData> exportIMUData() = 0; virtual void startDetectTouch(const luabridge::LuaRef cb, float durationSeconds, uint64_t intervalMs = 100) = 0; virtual void stopDetectTouch(const luabridge::LuaRef cb) = 0; virtual std::vector<TouchExportData> exportTouchData() = 0; //-------------------------------- // EmulatorDetection //-------------------------------- // 設定EmulatorDetection, 如果jsonConfig為空,則使用預設的模擬器檢測配置 virtual bool setEmulatorDetectionCondition(const std::string& jsonConfig = "") = 0; // 重啟EmulatorDetection(deprecated) virtual bool reloadEmulatorDetectionCondition() = 0; // 取得EmulatorDetection的描述 virtual std::string getEmulatorDetectionDescription() = 0; // 取得EmulatorDetection的觸發規則 virtual std::vector<TriggeredRule> getTriggeredRules() = 0; // 清除EmulatorDetection的觸發規則 virtual void clearTriggeredRules() = 0; // 檢查是否為模擬器 virtual bool isEmulator() = 0; }; typedef std::shared_ptr<ISecuritySDK> ISecuritySDKPtr; } // namespace securitysdk #endif // ISECURITYSDK_H ``` ### 公開 struct 定義(SecuritySDK.h) ```cpp #pragma once #include <string> #include "scripting/lua-bindings/manual/LuaBasicConversions.h" #include "scripting/lua-bindings/manual/CCLuaEngine.h" #include "ISecuritySDK.h" #if ( CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID ) #include "SecuritySDKImplAndroid.h" #elif ( CC_TARGET_PLATFORM == CC_PLATFORM_IOS ) #include "SecuritySDKImplIOS.h" #endif class SecuritySDK { public: static SecuritySDK* getInstance(); SecuritySDK(); virtual ~SecuritySDK(); void RegisterLua(lua_State* L); void RegisterSecuritySDK(); void UnregisterSecuritySDK(); int m_testInt = 640; securitysdk::DeviceInfo getDeviceInfo(); void startIMUTracking(const luabridge::LuaRef cb, float durationSeconds, float sampleIntervalSecond = 0.1f); void stopIMUTracking(const luabridge::LuaRef cb); std::vector<securitysdk::IMUExportData> exportIMUData(); void startDetectTouch(const luabridge::LuaRef cb, float durationSeconds, uint64_t intervalMs = 100); void stopDetectTouch(const luabridge::LuaRef cb); std::vector<securitysdk::TouchExportData> exportTouchData(); //-------------------------------- // EmulatorDetection //-------------------------------- // 設定EmulatorDetection, 如果jsonConfigPath為空,則使用預設的模擬器檢測配置 bool setEmulatorDetectionCondition(const std::string& jsonConfig = ""); // 重啟EmulatorDetection, // 目前JsonEmulatorCondition可以通過setEmulatorDetectionCondition直接修改,如果是有需要動態調整模擬器檢測的可以使用reloadEmulatorDetectionCondition bool reloadEmulatorDetectionCondition(); // 取得EmulatorDetection的描述 std::string getEmulatorDetectionDescription(); // 取得EmulatorDetection的觸發規則 std::vector<securitysdk::TriggeredRule> getTriggeredRules(); // 清除EmulatorDetection的觸發規則 void clearTriggeredRules(); // 檢查是否為模擬器 bool isEmulator(); void startDetectTouchLua(luabridge::LuaRef cb, luabridge::LuaRef durationRef, luabridge::LuaRef intervalRef); luabridge::LuaRef exportTouchDataLua(lua_State* L); void startIMUTrackingLua(luabridge::LuaRef cb, luabridge::LuaRef durationRef, luabridge::LuaRef intervalRef); luabridge::LuaRef exportIMUDataLua(lua_State* L); luabridge::LuaRef getTriggeredRulesLua(lua_State* L); protected: static SecuritySDK* _instance; securitysdk::ISecuritySDKPtr m_securitySDKNative; std::vector<securitysdk::IMUExportData> m_IMUExportData = {}; std::vector<securitysdk::TouchExportData> m_touchExportData = {}; std::vector<securitysdk::TriggeredRule> m_triggeredRules = {}; securitysdk::DeviceInfo m_deviceInfo = {}; }; ``` ### 公開 struct 定義(SecuritySDKTypes.h) ```cpp #ifndef SECURITYSDKTYPES_H #define SECURITYSDKTYPES_H #include <string> #include <vector> #include <cstdint> #include <array> namespace securitysdk { struct DeviceInfo { std::string ID; std::string model; std::string osVersion; std::string cpuInfo; std::string fingerprint; std::string localIpAddress; std::string networkType; std::string macAddress; std::string kernelABIInfo; int totalMemoryMb; int availableMemoryMb; std::string buildInfo; int screenWidth; int screenHeight; // 移除用於模擬器檢測的欄位 }; struct TouchExportData { float x; float y; uint64_t timestamp; bool hasRapidClicks = false; bool hasIntegerCoordinates = false; }; struct IMUExportData { uint64_t timestamp = 0; float interval = 0.0f; std::array<float, 3> deltaAngles{}; std::array<float, 3> avgAngularVelocity{}; std::array<float, 3> avgAccelVelocity{}; size_t sampleCount = 0; }; struct TriggeredRule { std::string field; // 檢測欄位 std::string operation; // 操作類型 std::string actualValue; // 實際值 std::string expectedValue; // 期望值 std::string description; // 規則描述 }; } // namespace securitysdk #endif // SECURITYSDKTYPES_H ``` ### 模擬器檢測 JSON 配置範例 #### 預設 Android 配置範例 ```json { "logic": "OR", "rules": [ { "field": "brand", "operation": "equals", "values": [ "generic" ], "caseSensitive": false }, { "field": "model", "operation": "contains", "values": [ "android sdk", "sdk", "emulator", "generic" ], "caseSensitive": false }, { "field": "product", "operation": "contains", "values": [ "sdk", "emulator", "generic" ], "caseSensitive": false }, { "field": "device", "operation": "contains", "values": [ "generic", "sdk" ], "caseSensitive": false }, { "field": "hardware", "operation": "contains", "values": [ "goldfish", "ranchu", "vbox86" ], "caseSensitive": false }, { "field": "board", "operation": "contains", "values": [ "goldfish", "ranchu", "vbox86" ], "caseSensitive": false }, { "field": "host", "operation": "contains", "values": [ "ubuntu" ], "caseSensitive": false }, { "field": "localIpAddress", "operation": "starts_with", "values": [ "172." ], "caseSensitive": false }, { "field": "kernelABIInfo", "operation": "contains", "values": [ "x86", "x86_64" ], "caseSensitive": false }, { "field": "totalMemoryMb", "operation": "less_than", "values": [ "2048" ], "caseSensitive": false } ], "groups": [ { "logic": "AND", "rules": [ { "field": "brand", "operation": "equals", "values": [ "generic" ], "caseSensitive": false }, { "field": "model", "operation": "starts_with", "values": [ "android sdk" ], "caseSensitive": false } ] }, { "logic": "OR", "rules": [ { "field": "hardware", "operation": "regex", "values": [ "*goldfish*", "*ranchu*", "*vbox*" ], "caseSensitive": false }, { "field": "board", "operation": "regex", "values": [ "*goldfish*", "*ranchu*", "*vbox*" ], "caseSensitive": false } ] } ] } ``` #### 預設 iOS 配置範例 >[!Warning] 現在規劃 >目前尚無 iOS 模擬器的參考範例,僅先行建立功能,待未來實際捕捉到 iOS 模擬器資料後(階段二),再完善其相關 JSON 文件。 ```json {} ``` ### AppDelegate.cpp 參考範例 ```cpp #include "AppDelegate.h" #include "securitysdk/ISecuritySDK.h" #include "securitysdk/SecuritySDKFactory.h" #include "cocos2d.h" bool AppDelegate::applicationDidFinishLaunching() { // === SecuritySDK 模擬器檢測初始化 === try { // 創建 SecuritySDK 實例並設定為全域實例 auto securitySDK = createSecuritySDK(); if (securitySDK) { // 將實例設定為全域實例,供 Lua 綁定使用 extern ISecuritySDK* g_sdk; g_sdk = securitySDK; // 嘗試從多個位置讀取 JSON 配置檔案 std::string jsonConfig = ""; auto fileUtils = cocos2d::FileUtils::getInstance(); std::vector<std::string> configPaths = { "emulator_detection_config.json", "res/emulator_detection_config.json", "src/emulator_detection_config.json", "assets/emulator_detection_config.json" }; bool configLoaded = false; for (const auto& configPath : configPaths) { std::string fullPath = fileUtils->fullPathForFilename(configPath); if (fileUtils->isFileExist(fullPath)) { jsonConfig = fileUtils->getStringFromFile(fullPath); cocos2d::log("SecuritySDK: JSON 配置檔案讀取成功 - %s", configPath.c_str()); configLoaded = true; break; } } if (!configLoaded) { // 使用內建的預設配置 jsonConfig = R"({ "logic": "OR", "groups": [ { "logic": "AND", "rules": [ { "field": "manufacturer", "operation": "contains", "values": ["google", "genymotion", "android_x86"], "caseSensitive": false }, { "field": "model", "operation": "contains", "values": ["sdk", "emulator", "google_sdk"], "caseSensitive": false } ] } ] })"; cocos2d::log("SecuritySDK: 使用內建預設配置"); } // 設定模擬器檢測配置(使用 JSON 字串) if (securitySDK->setEmulatorDetectionCondition(jsonConfig)) { cocos2d::log("SecuritySDK: 模擬器檢測配置載入成功"); // 執行模擬器檢測 bool isEmulator = securitySDK->isEmulator(); cocos2d::log("SecuritySDK: 模擬器檢測結果 - %s", isEmulator ? "檢測到模擬器" : "真實裝置"); // 取得觸發的規則 auto triggeredRules = securitySDK->getTriggeredRules(); if (!triggeredRules.empty()) { cocos2d::log("SecuritySDK: 觸發的檢測規則:"); for (const auto& rule : triggeredRules) { cocos2d::log(" - %s %s (實際值: %s, 期望值: %s)", rule.field.c_str(), rule.operation.c_str(), rule.actualValue.c_str(), rule.expectedValue.c_str()); } } } else { cocos2d::log("SecuritySDK: 模擬器檢測配置載入失敗"); } } else { cocos2d::log("SecuritySDK: 創建 SecuritySDK 實例失敗"); } } catch (const std::exception& e) { cocos2d::log("SecuritySDK: 初始化異常 - %s", e.what()); } // === SecuritySDK 初始化完成 === // ... 其他初始化程式碼 ... } ```