一、人臉識別簡介與應用場景
二、人臉識別框架分析
三、人臉識別的攻擊面
四、攻擊流程分析
五、總結
一、人臉識別簡介與應用場景
1.1、什么是人臉識別
人臉識別,是基於人的臉部特征信息進行身份識別的一種生物識別技術。用攝像機或攝像頭采集含有人臉的圖像或視頻流,並自動在圖像中檢測和跟蹤人臉,進而對檢測到的人臉進行臉部識別的一系列相關技術,通常也叫做人像識別、面部識別。
1.2、人臉識別發展歷程
早在20世紀50年代,認知科學家就已着手對人臉識別展開研究。
20世紀60年代,人臉識別工程化應用研究正式開啟。當時的方法主要利用了人臉的幾何結構,通過分析人臉器官特征點及其之間的拓撲關系進行辨識。這種方法簡單直觀,但是一旦人臉姿態、表情發生變化,則精度嚴重下降。
1991年,著名的“特征臉”方法第一次將主成分分析和統計特征技術引入人臉識別,在實用效果上取得了長足的進步。這一思路也在后續研究中得到進一步發揚光大。
21世紀的前十年,隨着機器學習理論的發展,學者們相繼探索出了基於遺傳算法、支持向量機(Support Vector Machine, SVM)、boosting、流形學習以及核方法等進行人臉識別。
2013年,MSRA的研究者首度嘗試了10萬規模的大訓練數據,並基於高維LBP特征和Joint Bayesian方法在LFW上獲得了95.17%的精度。這一結果表明:大訓練數據集對於有效提升非受限環境下的人臉識別很重要。然而,以上所有這些經典方法,都難以處理大規模數據集的訓練場景。
2014年前后,隨着大數據和深度學習的發展,神經網絡重受矚目,並在圖像分類、手寫體識別、語音識別等應用中獲得了遠超經典方法的結果。香港中文大學的Sun Yi等人提出將卷積神經網絡應用到人臉識別上,采用20萬訓練數據,在LFW上第一次得到超過人類水平的識別精度,這是人臉識別發展歷史上的一座里程碑。
自此之后,研究者們不斷改進網絡結構,同時擴大訓練樣本規模,將LFW上的識別精度推到99.5%以上。不斷在訓練數據擴充、新模型設計及度量學習等方面投入更多的精力,如今大規模人臉識別己走入實用。
1.3、應用場景
人臉驗證產品我們應該都很熟悉了,不少人都在用人臉進行解鎖手機或者刷臉支付、人臉注冊登錄等,不過這只是其中的一小部分,還有應用於金融、泛安防、零售等行業場景,滿足身份核驗、人臉考勤、閘機通行等業務需求,概括來說,人臉識別實現了一件事,確定實際人臉與目標人臉的相似度,粗略可分為:人臉1:1比對、人臉1:N檢測,按照類型分類大致如圖1-1所示:

圖1-1
1.4、產品介紹
該產品主要功能包含人臉檢測與屬性分析、人臉對比、人臉搜索、活體檢測等能力。靈活應用於金融、泛安防、零售等行業場景,滿足身份核驗、人臉考勤、閘機通行等業務需求。如圖1-2所示:

圖1-2
二、人臉識別框架分析
2.1、整體框架
一般來說人臉識別分為三步,采集數據、人臉檢測、結果驗證,流程大致如圖2-1所示:

圖2-1
人臉識別是身份認證的第一步,因為首先我要確認這個人是真人,而不是視頻、照片、面具等欺詐盜用行為,所以身份認證/安防的核心技術在於活體檢測、人臉比對、人臉搜索;主要用於:線上遠程認證場景(金融開戶、刷臉注冊、刷臉登錄等)、線下無人值守場景(智慧交通、人臉門禁、刷臉取款、刷臉支付等)。
三、人臉識別的攻擊面
3.1、人臉識別技術
人臉識別主要分為人臉檢測(face detecTIon)、特征提取(feature extracTIon)和人臉識別(face recogniTIon)三個過程。這期間隨着硬件的升級發展,分為不同有類型,如圖3-1所示:

圖3-1
3D人臉模型比2D人臉模型有更強的描述能力,能更好的表達出真實人臉,所以基於3D數據的人臉識別不管識別准確率還是活體檢測准確率都有很大的提高。
3.2、常見攻擊面
業務場景不同,攻擊方式也不同,難易度也不同,本次攻擊的目標是用於金融APP登錄注冊時用到的人臉識別。
目前大部分人臉識別產品防止照片攻擊基本都有活體檢測(點頭、張嘴等動作)機制,但是這種用戶體驗不好,在些基礎上又出現一種靜默活體檢測的方式,這種方式相對來說用戶體驗更佳。
目前常見的攻擊方式如圖3-2、3-3、3-4所示:

圖3-2

圖3-3

圖3-4
四、攻擊流程分析
4.1、攻擊方案嘗試
第一次嘗試是在手機中打開一張靜態照片來模擬攻擊,AI能識別出圖片中的人臉,但是被檢測出不是真人,“人臉驗證失敗”,結果如圖4-1所示:

圖 4-1
第二次嘗,通過第一次的嘗試后我想應該是AI有檢測人臉是否有動態的特征我再次將一個視頻放在手機中播放來欺騙AI,但是最終還是沒有成功。
經過兩次嘗試后,還是不能過掉AI活體檢測,我最終決定分析APP識別人臉的整個過程和邏輯,是否能反向推導出識別模型,然后再構造出一張對抗性圖像或視頻數據來達到攻擊模型的目的。攻擊場景如圖4-2所示

圖 4-2
對於離線模型其實還有一種方式是直接修改程序邏輯達到攻擊的目的。
4.2、攻擊方案實施
4.2.1、模型初始化流程分析
打開攝像頭,畫出人臉識別框
if(v4_5 != null) { Camera.CameraInfo v6_2 = new Camera.CameraInfo(); Camera.getCameraInfo(v4_5.x, v6_2); WindowManager v7 = v4_5.getWindowManager(); g.a(v7, "windowManager"); Display v7_1 = v7.getDefaultDisplay(); g.a(v7_1, "windowManager.defaultDisplay"); int v7_2 = v7_1.getRotation(); int v1 = 0; if(v7_2 != 0) { if(v7_2 == 1) { v1 = 90; } else if(v7_2 == 2) { v1 = 180; } else if(v7_2 == 3) { v1 = 270; } } int v6_3 = v6_2.facing == 1 ? 360 - (v6_2.orientation + v1) % 360 : v6_2.orientation - v1 + 360 % 360; Camera v4_6 = v4_5.w; if(v4_6 != null) { v4_6.setDisplayOrientation(v6_3); return; } g.a(); throw null; }
模型加載
public final int loadModel(AssetManager arg18) { if(arg18 != null) { JSONArray v2 = new JSONArray(new BufferedReader(new InputStreamReader(arg18.open("live/config.json"))).readLine()); ArrayList v1 = new ArrayList(); int v3 = v2.length(); int v4; for(v4 = 0; v4 < v3; ++v4) { JSONObject v5 = v2.getJSONObject(v4); g.a(v5, "jsonArray.getJSONObject(i)"); ModelConfig v6 = new ModelConfig(0f, 0f, 0f, 0, 0, null, false, 0x7F, null); String v7 = v5.optString("name"); g.a(v7, "config.optString(\"name\")"); v6.setName(v7); v6.setWidth(v5.optInt("width")); v6.setHeight(v5.optInt("height")); v6.setScale(((float)v5.optDouble("scale"))); v6.setShift_x(((float)v5.optDouble("shift_x"))); v6.setShift_y(((float)v5.optDouble("shift_y"))); v6.setOrg_resize(v5.optBoolean("org_resize")); v1.add(v6); } if(v1.isEmpty()) { Log.e("Live", "parse model config failed"); return -1; } return this.nativeLoadModel(arg18, v1); } g.a("assetManager"); throw null; }
模型初始化
從模型加載可以看到最終會調用nativeLoadModel來初始化模型 .text:0000BB30 ; _DWORD __fastcall FaceDetector::LoadModel(FaceDetector *__hidden this, AAssetManager *mgr) .text:0000BB30 EXPORT _ZN12FaceDetector9LoadModelEP13AAssetManager .text:0000BB30 _ZN12FaceDetector9LoadModelEP13AAssetManager .text:0000BB30 ; __unwind { .text:0000BB30 B0 B5 PUSH {R4,R5,R7,LR} .text:0000BB32 02 AF ADD R7, SP, #8 .text:0000BB34 05 46 MOV R5, R0 .text:0000BB36 5C 30 ADDS R0, #0x5C ; '\' .text:0000BB38 60 F9 8F 0A VLD1.32 {D16-D17}, [R0] .text:0000BB3C 0C 46 MOV R4, R1 .text:0000BB3E 68 20 MOVS R0, #0x68 ; 'h' .text:0000BB40 29 46 MOV R1, R5 .text:0000BB42 17 4A LDR R2, =(aDetectionDetec - 0xBB50) .text:0000BB44 41 F9 80 0A VST1.32 {D16-D17}, [R1],R0 .text:0000BB48 05 F1 0C 00 ADD.W R0, R5, #0xC .text:0000BB4C 7A 44 ADD R2, PC ; "detection/detection.param" .text:0000BB4E 61 F9 8F 0A VLD1.32 {D16-D17}, [R1] .text:0000BB52 21 46 MOV R1, R4 ; mgr .text:0000BB54 40 F9 8F 0A VST1.32 {D16-D17}, [R0] .text:0000BB58 28 46 MOV R0, R5 ; int .text:0000BB5A 0C F0 AD FF BL sub_18AB8 .text:0000BB5E 50 B1 CBZ R0, loc_BB76 .text:0000BB60 10 49 LDR R1, =(aEngine - 0xBB6C) ; "Engine" .text:0000BB62 03 46 MOV R3, R0 .text:0000BB64 10 4A LDR R2, =(aFacedetectorLo - 0xBB6E) .text:0000BB66 06 20 MOVS R0, #6 ; prio .text:0000BB68 79 44 ADD R1, PC ; "Engine" .text:0000BB6A 7A 44 ADD R2, PC ; "FaceDetector load param failed. %d" .text:0000BB6C FE F7 EE E8 BLX __android_log_print .text:0000BB70 4F F0 FF 30 MOV.W R0, #0xFFFFFFFF .text:0000BB74 B0 BD POP {R4,R5,R7,PC} .text:0000BB76 .text:0000BB76 loc_BB76 .text:0000BB76 0D 4A LDR R2, =(aDetectionDetec_0 - 0xBB80) ; "detection/detection.bin" .text:0000BB78 28 46 MOV R0, R5 ; int .text:0000BB7A 21 46 MOV R1, R4 ; mgr .text:0000BB7C 7A 44 ADD R2, PC ; "detection/detection.bin" .text:0000BB7E 0D F0 63 F8 BL sub_18C48 .text:0000BB82 50 B1 CBZ R0, loc_BB9A .text:0000BB84 0A 49 LDR R1, =(aEngine - 0xBB90) ; "Engine" .text:0000BB86 03 46 MOV R3, R0 .text:0000BB88 0A 4A LDR R2, =(aFacedetectorLo_0 - 0xBB92) .text:0000BB8A 06 20 MOVS R0, #6 ; prio .text:0000BB8C 79 44 ADD R1, PC ; "Engine" .text:0000BB8E 7A 44 ADD R2, PC .text:0000BB90 FE F7 DC E8 BLX __android_log_print .text:0000BB94 6F F0 01 00 MOV R0, #0xFFFFFFFE .text:0000BB98 B0 BD POP {R4,R5,R7,PC} .text:0000BB9A 00 20 MOVS R0, #0 .text:0000BB9C B0 BD POP {R4,R5,R7,PC} //從二進制文件中載入模型並解析 .text:00016A64 CA 48 LDR R0, =(__sF_ptr - 0x16A6A) .text:00016A66 78 44 ADD R0, PC ; __sF_ptr .text:00016A68 01 68 LDR R1, [R0] ; __sF .text:00016A6A CA 48 LDR R0, =(aInvalidLayerCo - 0x16A76) ; "invalid layer_count or blob_count\n" .text:00016A6C 01 F1 A8 03 ADD.W R3, R1, #0xA8 .text:00016A70 22 21 MOVS R1, #0x22 ; '"' .text:00016A72 78 44 ADD R0, PC ; "invalid layer_count or blob_count\n" .text:00016A74 22 E0 B loc_16ABC .text:00016A76 .text:00016A76 loc_16A76 ; CODE XREF: sub_169E4+36↑j .text:00016A76 C8 48 LDR R0, =(__sF_ptr - 0x16A7C) .text:00016A78 78 44 ADD R0, PC ; __sF_ptr .text:00016A7A 01 68 LDR R1, [R0] ; __sF .text:00016A7C C7 48 LDR R0, =(aParseMagicFail - 0x16A88) ; "parse magic failed\n" .text:00016A7E 01 F1 A8 03 ADD.W R3, R1, #0xA8 .text:00016A82 13 21 MOVS R1, #0x13 .text:00016A84 78 44 ADD R0, PC ; "parse magic failed\n" .text:00016A86 19 E0 B loc_16ABC .text:00016A88 .text:00016A88 C5 48 LDR R0, =(__sF_ptr - 0x16A8E) .text:00016A8A 78 44 ADD R0, PC ; __sF_ptr .text:00016A8C 01 68 LDR R1, [R0] ; __sF .text:00016A8E C5 48 LDR R0, =(aParamIsTooOldP - 0x16A9A) ; "param is too old, please regenerate\n" .text:00016A90 01 F1 A8 03 ADD.W R3, R1, #0xA8 .text:00016A94 24 21 MOVS R1, #0x24 ; '$' .text:00016A96 78 44 ADD R0, PC ; "param is too old, please regenerate\n" .text:00016A98 10 E0 B loc_16ABC .text:00016A9A .text:00016A9A loc_16A9A ; CODE XREF: sub_169E4+5E↑j .text:00016A9A C3 48 LDR R0, =(__sF_ptr - 0x16AA0) .text:00016A9C 78 44 ADD R0, PC ; __sF_ptr .text:00016A9E 01 68 LDR R1, [R0] ; __sF .text:00016AA0 C2 48 LDR R0, =(aParseLayerCoun - 0x16AAC) ; "parse layer_count failed\n" .text:00016AA2 01 F1 A8 03 ADD.W R3, R1, #0xA8 .text:00016AA6 19 21 MOVS R1, #0x19 .text:00016AA8 78 44 ADD R0, PC ; "parse layer_count failed\n" .text:00016AAA 07 E0 B loc_16ABC
以上是初始化過程,主要邏輯是讀取模型解析出網絡的layer層數及blob數,遍歷所有的layer,解析每個layer層的類型(layer_type)、名稱(layer_name)、輸入數(bottom_count)和輸出數(top_count),設置layer參數:layer的類型、名字、輸入和輸出。
4.2.2、模型推理流程
獲取人臉數據
@Override // f.l.j.a.a public final Object c_onPreviewFrame(Object arg13) { b v3; r.d(arg13); MainActivity v13 = this.j.e; v13.G = true; c v1 = v13.u; if(v1 != null) { byte[] Facedata = this.k; int v5 = v13.y; int v6 = v13.z; int v7 = v13.A; if(Facedata != null) { List v13_1 = v1.a.detect(Facedata, v5, v6, v7);// 識別人臉方法 if((v13_1.isEmpty() ^ 1) == 0) { v3 = new b(); } else { long v10 = System.currentTimeMillis(); FaceBox v13_2 = (FaceBox)v13_1.get(0); v13_2.setConfidence(v1.b.a(Facedata, v5, v6, v7, v13_2)); v3 = new b(v13_2, System.currentTimeMillis() - v10, true); } MainActivity v13_3 = this.j.e; v3.h = v13_3.v; Rect v6_1 = new Rect(((int)(((float)v3.b) * v13_3.D)), ((int)(((float)v3.c) * v13_3.E)), ((int)(((float)v3.d) * v13_3.D)), ((int)(((float)v3.e) * v13_3.E))); a v13_4 = MainActivity.a(this.j.e); v3.b = v6_1.left; v3.a(4); v3.c = v6_1.top; v3.a(9); v3.d = v6_1.right; v3.a(6); v3.e = v6_1.bottom; v3.a(1); v13_4.a(v3); Log.d("MainActivity", "threshold:" + v3.h + ", confidence: " + v3.f); MainActivity.a(this.j.e).p.postInvalidate(); this.j.e.G = false; return j.a; } g.a("yuv"); throw null; } //調用到Native方法nativeDetectYuv public final List detect(byte[] arg3, int arg4, int arg5, int arg6) { if(arg3 != null) { if(arg4 * arg5 * 3 / 2 == arg3.length) { return this.nativeDetectYuv(arg3, arg4, arg5, arg6); } throw new IllegalArgumentException("Invalid yuv data"); } g.a("yuv"); throw null; }
數據處理
手機攝像頭獲取的數據是yuv420sp格式,AI模型要求圖像輸入格式BGR,所以需要做格式轉換。 .text:0000AC60 F0 B5 PUSH {R4-R7,LR} .text:0000AC62 03 AF ADD R7, SP, #0xC .text:0000AC64 2D E9 00 0F PUSH.W {R8-R11} .text:0000AC68 99 B0 SUB SP, SP, #0x64 .text:0000AC6A 0D 46 MOV R5, R1 .text:0000AC6C 5B 49 LDR R1, =(__stack_chk_guard_ptr - 0xAC78) .text:0000AC6E 00 26 MOVS R6, #0 .text:0000AC70 0D F1 28 0B ADD.W R11, SP, #0x80+var_58 .text:0000AC74 79 44 ADD R1, PC ; __stack_chk_guard_ptr .text:0000AC76 99 46 MOV R9, R3 .text:0000AC78 14 46 MOV R4, R2 .text:0000AC7A 00 23 MOVS R3, #0 ; int .text:0000AC7C D1 F8 00 A0 LDR.W R10, [R1] ; __stack_chk_guard .text:0000AC80 DA F8 00 10 LDR.W R1, [R10] .text:0000AC84 18 91 STR R1, [SP,#0x80+var_20] .text:0000AC86 CD E9 00 06 STRD.W R0, R6, [SP,#0x80+var_80] ; void * .text:0000AC8A 02 EB D2 70 ADD.W R0, R2, R2,LSR#31 .text:0000AC8E 02 EB 60 01 ADD.W R1, R2, R0,ASR#1 ; int .text:0000AC92 58 46 MOV R0, R11 ; this .text:0000AC94 2A 46 MOV R2, R5 ; int .text:0000AC96 FF F7 4E E8 BLX j__ZN2cv3MatC2EiiiPvj ; cv::Mat::Mat(int,int,int,void *,uint) .text:0000AC9A D7 F8 08 80 LDR.W R8, [R7,#arg_0] .text:0000AC9E 40 46 MOV R0, R8 ; this .text:0000ACA0 21 46 MOV R1, R4 ; int .text:0000ACA2 2A 46 MOV R2, R5 ; int .text:0000ACA4 10 23 MOVS R3, #0x10 ; int .text:0000ACA6 FF F7 7C E8 BLX j__ZN2cv3Mat6createEiii ; cv::Mat::create(int,int,int) .text:0000ACAA 4B 4D LDR R5, =0x1010000 .text:0000ACAC CD E9 08 66 STRD.W R6, R6, [SP,#0x80+var_60] .text:0000ACB0 05 F1 80 74 ADD.W R4, R5, #0x1000000 .text:0000ACB4 CD E9 06 5B STRD.W R5, R11, [SP,#0x80+var_68] .text:0000ACB8 CD E9 04 66 STRD.W R6, R6, [SP,#0x80+var_70] .text:0000ACBC CD E9 02 48 STRD.W R4, R8, [SP,#0x80+var_78] .text:0000ACC0 06 A8 ADD R0, SP, #0x80+var_68 .text:0000ACC2 02 A9 ADD R1, SP, #0x80+var_78 .text:0000ACC4 5D 22 MOVS R2, #0x5D ; ']' .text:0000ACC6 00 23 MOVS R3, #0 .text:0000ACC8 FF F7 3A E8 BLX _ZN2cv8cvtColorERKNS_11_InputArrayERKNS_12_OutputArrayEii ; cv::cvtColor(cv::_InputArray const&,cv::_OutputArray const&,int,int) .text:0000ACCC A9 F1 02 00 SUB.W R0, R9, #2 ; switch 7 cases .text:0000ACD0 06 28 CMP R0, #6 .text:0000ACD2 64 D8 BHI def_ACD4 ; jumptable 0000ACD4 default case .text:0000ACD4 DF E8 00 F0 TBB.W [PC,R0] ; switch jump
創建一個模型對象,設置輸入;提取中間節點的運算結果
.text:0000BBB8 F0 B5 PUSH {R4-R7,LR} .text:0000BBBA 03 AF ADD R7, SP, #0xC .text:0000BBBC 2D E9 00 0B PUSH.W {R8,R9,R11} .text:0000BBC0 2D ED 0A 8B VPUSH {D8-D12} .text:0000BBC4 AA B0 SUB SP, SP, #0xA8 .text:0000BBC6 05 46 MOV R5, R0 .text:0000BBC8 96 48 LDR R0, =(__stack_chk_guard_ptr - 0xBBD2) .text:0000BBCA 14 46 MOV R4, R2 .text:0000BBCC 0E 46 MOV R6, R1 .text:0000BBCE 78 44 ADD R0, PC ; __stack_chk_guard_ptr .text:0000BBD0 D0 F8 00 80 LDR.W R8, [R0] ; __stack_chk_guard .text:0000BBD4 D8 F8 00 00 LDR.W R0, [R8] .text:0000BBD8 29 90 STR R0, [SP,#0xE8+var_44] .text:0000BBDA 91 ED 02 0A VLDR S0, [R1,#8] .text:0000BBDE 91 ED 03 1A VLDR S2, [R1,#0xC] .text:0000BBE2 B8 EE C0 8A VCVT.F32.S32 S16, S0 .text:0000BBE6 B8 EE C1 9A VCVT.F32.S32 S18, S2 .text:0000BBEA 95 ED 10 0A VLDR S0, [R5,#0x40] .text:0000BBEE B8 EE C0 CA VCVT.F32.S32 S24, S0 .text:0000BBF2 89 EE 08 BA VDIV.F32 S22, S18, S16 .text:0000BBF6 B1 EE CB AA VSQRT.F32 S20, S22 .text:0000BBFA B0 EE 4A 0A VMOV.F32 S0, S20 .text:0000BBFE B4 EE CA AA VCMPE.F32 S20, S20 .text:0000BC02 F1 EE 10 FA VMRS APSR_nzcv, FPSCR .text:0000BC06 05 D7 BVC loc_BC14 .text:0000BC08 1B EE 10 0A VMOV R0, S22 ; x .text:0000BC0C FE F7 B8 E9 BLX sqrtf .text:0000BC10 00 EE 10 0A VMOV S0, R0 .text:0000BC14 .text:0000BC14 20 EE 0C 0A VMUL.F32 S0, S0, S24 .text:0000BC18 95 ED 10 1A VLDR S2, [R5,#0x40] .text:0000BC1C B4 EE CA AA VCMPE.F32 S20, S20 .text:0000BC20 B8 EE C1 CA VCVT.F32.S32 S24, S2 .text:0000BC24 F1 EE 10 FA VMRS APSR_nzcv, FPSCR .text:0000BC28 BD EE C0 0A VCVT.S32.F32 S0, S0 .text:0000BC2C 10 EE 10 9A VMOV R9, S0 .text:0000BC30 05 D7 BVC loc_BC3E .text:0000BC32 1B EE 10 0A VMOV R0, S22 ; x .text:0000BC36 FE F7 A4 E9 BLX sqrtf .text:0000BC3A 0A EE 10 0A VMOV S20, R0 .text:0000BC3E .text:0000BC3E 8C EE 0A 0A VDIV.F32 S0, S24, S20 .text:0000BC42 D6 E9 02 03 LDRD.W R0, R3, [R6,#8] .text:0000BC46 31 69 LDR R1, [R6,#0x10] .text:0000BC48 1F AE ADD R6, SP, #0xE8+var_6C .text:0000BC4A 00 22 MOVS R2, #0 .text:0000BC4C BD EE C0 0A VCVT.S32.F32 S0, S0 .text:0000BC50 03 92 STR R2, [SP,#0xE8+var_DC] .text:0000BC52 CD E9 00 09 STRD.W R0, R9, [SP,#0xE8+var_E8] .text:0000BC56 30 46 MOV R0, R6 .text:0000BC58 02 22 MOVS R2, #2 .text:0000BC5A 8D ED 02 0A VSTR S0, [SP,#0xE8+var_E0] .text:0000BC5E 05 F0 C5 F9 BL sub_10FEC ; from_pixels_resize .text:0000BC62 05 F1 7C 01 ADD.W R1, R5, #0x7C ; '|' .text:0000BC66 30 46 MOV R0, R6 .text:0000BC68 00 22 MOVS R2, #0 .text:0000BC6A 01 F0 B1 FC BL sub_D5D0 ; substract_mean_normalize .text:0000BC6E 14 A8 ADD R0, SP, #0xE8+var_98 .text:0000BC70 29 46 MOV R1, R5 .text:0000BC72 0D F0 29 F8 BL sub_18CC8 ; create_extractor .text:0000BC76 D5 F8 88 10 LDR.W R1, [R5,#0x88] .text:0000BC7A 14 A8 ADD R0, SP, #0xE8+var_98 .text:0000BC7C 0E F0 2C FB BL sub_1A2D8 ; set_num_threads .text:0000BC80 28 46 MOV R0, R5 .text:0000BC82 10 F8 44 1F LDRB.W R1, [R0,#0x44]! .text:0000BC86 C9 07 LSLS R1, R1, #0x1F .text:0000BC88 0C BF ITE EQ .text:0000BC8A 41 1C ADDEQ R1, R0, #1 .text:0000BC8C E9 6C LDRNE R1, [R5,#0x4C] .text:0000BC8E 14 A8 ADD R0, SP, #0xE8+var_98 .text:0000BC90 1F AA ADD R2, SP, #0xE8+var_6C .text:0000BC92 0E F0 27 FB BL sub_1A2E4 ; input .text:0000BC96 0A A8 ADD R0, SP, #0xE8+var_C0 .text:0000BC98 C0 EF 50 00 VMOV.I32 Q8, #0 .text:0000BC9C 00 F1 10 01 ADD.W R1, R0, #0x10 .text:0000BCA0 41 F9 CF 0A VST1.64 {D16-D17}, [R1] .text:0000BCA4 24 21 MOVS R1, #0x24 ; '$' .text:0000BCA6 40 F9 C1 0A VST1.64 {D16-D17}, [R0],R1 .text:0000BCAA 00 21 MOVS R1, #0 .text:0000BCAC 01 60 STR R1, [R0] .text:0000BCAE 28 46 MOV R0, R5 .text:0000BCB0 12 91 STR R1, [SP,#0xE8+var_A0] .text:0000BCB2 10 F8 50 1F LDRB.W R1, [R0,#0x50]! .text:0000BCB6 C9 07 LSLS R1, R1, #0x1F .text:0000BCB8 0C BF ITE EQ .text:0000BCBA 41 1C ADDEQ R1, R0, #1 .text:0000BCBC A9 6D LDRNE R1, [R5,#0x58] .text:0000BCBE 14 A8 ADD R0, SP, #0xE8+var_98 .text:0000BCC0 0A AA ADD R2, SP, #0xE8+var_C0 .text:0000BCC2 0E F0 82 FB BL sub_1A3CA ; extract .text:0000BCC6 B7 EE 00 AA VMOV.F32 S20, #1.0 .text:0000BCCA 0D F1 10 09 ADD.W R9, SP, #0xE8+var_D8 .text:0000BCCE B6 EE 00 BA VMOV.F32 S22, #0.5 .text:0000BCD2 20 68 LDR R0, [R4] .text:0000BCD4 BF EE 00 CA VMOV.F32 S24, #-1.0 .text:0000BCD8 00 26 MOVS R6, #0 .text:0000BCDA 60 60 STR R0, [R4,#4]
4.2.3、Patch關鍵點過檢測
通過調試分析找到兩個關鍵點,Patch這兩個點就能過掉檢測:
人臉檢測:
.text:CC0CC6C8 BF EE 00 CA VMOV.F32 S24, #-1.0 .text:CC0CC6CC 00 26 MOVS R6, #0 .text:CC0CC6CE 60 60 STR R0, [R4,#4] .text:CC0CC6D0 5C E0 B loc_CC0CC78C .text:CC0CC6D2 // box.confidence = confidence;//人臉分數 .text:CC0CC6D2 .text:CC0CC6D2 10 9A LDR R2, [SP,#0xE8+var_A8] .text:CC0CC6D4 0C 99 LDR R1, [SP,#0xE8+var_B8] .text:CC0CC6D6 0A 98 LDR R0, [SP,#0xE8+var_C0] .text:CC0CC6D8 72 43 MULS R2, R6 .text:CC0CC6DA ; 66: if ( v11[1] >= *(float *)(a1 + 120) ) .text:CC0CC6DA 95 ED 1E 1A VLDR S2, [R5,#0x78] .text:CC0CC6DE 02 FB 01 00 MLA.W R0, R2, R1, R0 .text:CC0CC6E2 90 ED 01 0A VLDR S0, [R0,#4] .text:CC0CC6E6 B4 EE C1 0A VCMPE.F32 S0, S2 .text:CC0CC6EA F1 EE 10 FA VMRS APSR_nzcv, FPSCR .text:CC0CC6EE 4C D4 BMI loc_CC0CC78A ; 判斷是否為人臉,Patch點1
Patch檢測是否為人臉,將其Patch永遠為真
.text:0000C72C 9B ED 1E 0A VLDR S0, [R11,#0x78] .text:0000C730 B4 EE C0 DA VCMPE.F32 S26, S0 .text:0000C734 F1 EE 10 FA VMRS APSR_nzcv, FPSCR .text:0000C738 4C DC BGT loc_C7D4 ; 大於
活體檢測:
.text:CC20D9CA DA F8 30 00 LDR.W R0, [R10,#0x30] .text:CC20D9CE 83 42 CMP R3, R0 .text:CC20D9D0 FF F6 32 AF BLT.W loc_CC20D838 .text:CC20D9D4 00 EE 10 0A VMOV S0, R0 ; 活體分數Patch點2 .text:CC20D9D8 B8 EE C0 0A VCVT.F32.S32 S0, S0 .text:CC20D9DC 09 98 LDR R0, [SP,#0x178+var_154] ; 存放活體分數空間 .text:CC20D9DE 88 EE 00 0A VDIV.F32 S0, S16, S0 ; 活體分數 .text:CC20D9E2 80 ED 00 0A VSTR S0, [R0] .text:CC20D9E6 4D 98 LDR R0, [SP,#0x178+var_44] .text:CC20D9E8 06 99 LDR R1, [SP,#0x178+var_160] .text:CC20D9EA 09 68 LDR R1, [R1] .text:CC20D9EC 08 1A SUBS R0, R1, R0
Patch檢測是否為活體,將其Patch永遠為真的值
.text:0000DA40 09 99 LDR R1, [SP,#0x178+var_154] .text:0000DA42 19 48 LDR R0, =0x3F7B3380 ; patch值 .text:0000DA44 08 60 STR R0, [R1] ; 存放空間 .text:0000DA46 4D 99 LDR R1, [SP,#0x178+var_44] .text:0000DA48 06 9A LDR R2, [SP,#0x178+var_160] .text:0000DA4A 12 68 LDR R2, [R2] .text:0000DA4C 51 1A SUBS R1, R2, R1 .text:0000DA4E 01 BF ITTTT EQ .text:0000DA50 4E B0 ADDEQ SP, SP, #0x138 .text:0000DA52 BD EC 08 8B VPOPEQ {D8-D11} .text:0000DA56 01 B0 ADDEQ SP, SP, #4 .text:0000DA58 BD E8 00 0F POPEQ.W {R8-R11}
patch完后我們再用手機中的同一張靜態照來試試是否能驗證成功?效果如圖4-3所示:

圖4-3
攻擊成功,完美通過。
4.2.3、構造特征過檢測
上面通過Patch的方式來過掉檢測看起來是很好,但是、但是、但是,當相同的AI模型放在服務器端時,Patch這種方式就失效了,通過前面對模型進行逆向分析,獲取模型內部的一些特征數據,所以需要根據分析出來的模型特征構造出一張對抗性圖像或視頻數據送給服務器端達到攻擊模型的目的。
上面分析活體檢測時得到的活體特征,再根據YUV420數據格式將其構造到數據體中,得到一張人眼無法觀看只有機器能識別的圖像數據,如圖4-4、4-5所示:

圖4-4

圖4-5
通過hook攝像頭讀取方法可以直接將構造好的數據傳送給服務器端模型,效果如圖4-6所示:

圖4-6
這種方式是不改變目標AI學習系統的情況下,通過構造特定輸入樣本以完成欺騙目標系統的攻擊。我用一張A4紙打印了一張非真人臉圖做測試,在沒有真人臉的情況下成功通過驗證。這其中的根本原因,在於模型沒有學到完美的判別規則。雖然圖片識別系統一直試圖在計算機上模仿人類肉眼視覺功能,但由於人類肉眼視覺機理過於復雜,兩個系統在判別物體時依賴的判斷規則存在一定差異。因此人類肉眼的判別規則和模型實際學到的判別規則之間的差距,就給了攻擊者逃脫模型檢測提供可趁之機。
五、總結
本文主要是最近自己學習人臉識別的一點總結,與小伙伴們分享,有不對的地方請指正。
主要分為攻擊模型計算邏輯與攻擊模型本身兩種方式,不同的業務場景攻擊方案與難易成度不同。
模型計算邏輯攻擊:
這種攻擊方式只適用於離線模型,分析模型計算邏輯進行關鍵點Patch達到強制繞過驗證的目的。
攻擊模型:
攻擊模型和其他攻擊不同,攻擊模型主要發生在構造對抗性數據的時候,之后該對抗性數據就如正常數據一樣輸入機器學習模型並得到欺騙的識別結果。在構造對抗性數據的過程中,攻擊者並不知道AI所使用的算法和參數,但攻擊者仍能與AI系統有所交互,比如可以通過傳入任意輸入觀察輸出,判斷輸出。或者攻擊者可以通過逆向分析推導掌握AI模型參數信息。
歡迎關注公眾號:

