การตรวจสอบ bytecode ของ dalvik - dex2oat

Sep 24 2020

ฉันมีอุปกรณ์แอพที่ระดับ bytecode และได้รับข้อผิดพลาดการตรวจสอบต่อไปนี้จาก dex2oat:

2020-09-23 19:39:04.005 4864-4864/? W/dex2oat: Verification error in int a.d.cg.b(byte[], int, int)
2020-09-23 19:39:04.005 4864-4864/? W/dex2oat: int a.d.cg.b(byte[], int, int): [0x25] 
2020-09-23 19:39:04.005 4864-4864/? W/dex2oat: int a.d.cg.b(byte[], int, int): [0x27] 
2020-09-23 19:39:04.005 4864-4864/? W/dex2oat: int a.d.cg.b(byte[], int, int) failed to verify: int a.d.cg.b(byte[], int, int): [0x2C] monitor-exit on non-object (Undefined)

การแสดง smali สำหรับวิธีนี้มีดังต่อไปนี้:

.method b([BII)I
    .locals 4

    move-object/from16 v2, p0

    move-object/from16 v3, p1

    move/from16 p0, p2

    move/from16 p1, p3

    iget-object v0, v2, La/d/cg;->a:La/d/bj;

    iget-object v0, v0, La/d/bj;->p:Ljava/io/InputStream;

    if-eqz v0, :cond_0

    const-string p2, "La/d/cg;->b([BII)I->3"

    invoke-static/range {p2 .. p2}, Lde/tracer/Tracer;->trace(Ljava/lang/String;)V

    iget-object v0, v2, La/d/cg;->a:La/d/bj;

    iget-object v0, v0, La/d/bj;->p:Ljava/io/InputStream;

    check-cast v0, La/d/cn;

    iget-object v1, v0, La/d/cn;->b:Ljava/lang/Object;

    monitor-enter v1

    :try_start_0
    invoke-virtual {v0, v3, p0, p1}, La/d/cn;->b([BII)I

    iget-object v0, v0, La/d/cn;->b:Ljava/lang/Object;

    invoke-virtual {v0}, Ljava/lang/Object;->notify()V

    monitor-exit v1

    :cond_0
    const-string p2, "La/d/cg;->b([BII)I->12"

    invoke-static/range {p2 .. p2}, Lde/tracer/Tracer;->trace(Ljava/lang/String;)V

    return p1

    :catchall_0
    move-exception v0

    monitor-exit v1
    :try_end_0
    .catchall {:try_start_0 .. :try_end_0} :catchall_0

    throw v0
.end method

เวอร์ชันที่ไม่มีเครื่องมือไม่มีการเรียกใช้ตัวติดตามและคำสั่งสตริง const ที่กำหนดไว้ก่อนหน้านี้ซึ่งกำหนดสตริงการติดตาม นอกจากนี้คำแนะนำในการย้ายสี่ครั้งแรกยังไม่มีอยู่ในเวอร์ชันดั้งเดิม ใช้เพื่อรับการลงทะเบียนฟรีที่ "ตอนท้าย" ฉันยังใส่คำอธิบายประกอบไฟล์ smali พร้อมข้อมูลประเภทการลงทะเบียนและผลลัพธ์เป็นดังต่อไปนี้ (เฉพาะส่วนที่น่าสนใจที่อธิบายโดยข้อผิดพลาดการตรวจสอบ):

    #@1b
    #v0=(Reference,La/d/cn;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
    monitor-enter v1
    #v0=(Reference,La/d/cn;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);

    #@1c
    :try_start_1c
    #v0=(Reference,La/d/cn;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
    invoke-virtual {v0, v3, p0, p1}, La/d/cn;->b([BII)I
    #v0=(Reference,La/d/cn;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);

    #@1f
    #v0=(Reference,La/d/cn;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
    iget-object v0, v0, La/d/cn;->b:Ljava/lang/Object;
    #v0=(Reference,Ljava/lang/Object;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);

    #@21
    #v0=(Reference,Ljava/lang/Object;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
    invoke-virtual {v0}, Ljava/lang/Object;->notify()V
    #v0=(Reference,Ljava/lang/Object;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);

    #@24
    #v0=(Reference,Ljava/lang/Object;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
    monitor-exit v1
    #v0=(Reference,Ljava/lang/Object;);v1=(Reference,Ljava/lang/Object;);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);

    #@25
    :cond_25
    #v0=(Reference,Ljava/lang/Object;):merge{0xc:(Reference,Ljava/io/InputStream;),0x24:(Reference,Ljava/lang/Object;)}
    #v1=(Conflicted):merge{0xc:(Uninit),0x24:(Reference,Ljava/lang/Object;)}
    #v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);
    #p2=(Conflicted):merge{0xc:(Integer),0x24:(Reference,Ljava/lang/String;)}
    #p3=(Integer);
    const-string p2, "La/d/cg;->b([BII)I->12"
    #v0=(Reference,Ljava/lang/Object;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);

    #@27
    #v0=(Reference,Ljava/lang/Object;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
    invoke-static/range {p2 .. p2}, Lde/tracer/Tracer;->trace(Ljava/lang/String;)V
    #v0=(Reference,Ljava/lang/Object;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);

    #@2a
    #v0=(Reference,Ljava/lang/Object;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);
    return p1
    #v0=(Reference,Ljava/lang/Object;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Reference,Ljava/lang/String;);p3=(Integer);

    #@2b
    :catchall_2b
    #v0=(Reference,Ljava/lang/Object;):merge{0xc:(Reference,Ljava/io/InputStream;),0x1b:(Reference,La/d/cn;),0x1c:(Reference,La/d/cn;),0x1f:(Reference,Ljava/lang/Object;),0x21:(Reference,Ljava/lang/Object;),0x24:(Reference,Ljava/lang/Object;),0x25:(Reference,Ljava/lang/Object;),0x2b:(Reference,Ljava/lang/Throwable;)}
    #v1=(Conflicted):merge{0xc:(Uninit),0x1b:(Reference,Ljava/lang/Object;),0x1c:(Reference,Ljava/lang/Object;),0x1f:(Reference,Ljava/lang/Object;),0x21:(Reference,Ljava/lang/Object;),0x24:(Reference,Ljava/lang/Object;),0x25:(Conflicted),0x2b:(Conflicted)}
    #v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);
    #p2=(Conflicted):merge{0xc:(Integer),0x1b:(Reference,Ljava/lang/String;),0x1c:(Reference,Ljava/lang/String;),0x1f:(Reference,Ljava/lang/String;),0x21:(Reference,Ljava/lang/String;),0x24:(Reference,Ljava/lang/String;),0x25:(Reference,Ljava/lang/String;),0x2b:(Conflicted)}
    #p3=(Integer);
    move-exception v0
    #v0=(Reference,Ljava/lang/Throwable;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Conflicted);p3=(Integer);

    #@2c
    #v0=(Reference,Ljava/lang/Throwable;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Conflicted);p3=(Integer);
    monitor-exit v1
    #v0=(Reference,Ljava/lang/Throwable;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Conflicted);p3=(Integer);
    :try_end_2d
    .catchall {:try_start_1c .. :try_end_2d} :catchall_2b

    #@2d
    #v0=(Reference,Ljava/lang/Throwable;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Conflicted);p3=(Integer);
    throw v0
    #v0=(Reference,Ljava/lang/Throwable;);v1=(Conflicted);v2=(Reference,La/d/cg;);v3=(Reference,[B);p0=(Integer);p1=(Integer);p2=(Conflicted);p3=(Integer);
.end method

เมื่อดูที่ตำแหน่ง [0x2C] ฉันสังเกตเพียงว่า v1 อยู่ในสถานะที่ขัดแย้งกันและการผสานที่อธิบายไว้ที่ [0x2B] กำลังบอกฉันว่า Uninit ถูกรวมเข้ากับประเภทการอ้างอิง ฉันถือว่านี่เป็นปัญหาและทำให้เกิดข้อผิดพลาดในการตรวจสอบ (https://android.googlesource.com/platform/art/+/master/runtime/verifier/register_line.cc#367). อย่างไรก็ตามเมื่อพิจารณาไฟล์ smali ดั้งเดิมที่แนบมาพร้อมกับข้อมูลประเภทการลงทะเบียนฉันสังเกตว่า v1 ไม่เคยอยู่ในสถานะที่ขัดแย้งกัน ยิ่งไปกว่านั้นสิ่งที่แปลก - อย่างน้อยสำหรับฉัน - เครื่องมือของฉันไม่เคยแตะ register v1 ดังนั้นความขัดแย้งนี้จะเกิดขึ้นได้อย่างไร?

คำตอบ

1 JesusFreke Sep 24 2020 at 01:48

ปัญหาคือเมื่อเพิ่มการเรียกไปยังฟังก์ชันการติดตามในบล็อกลองคุณกำลังเพิ่มขอบจากตำแหน่งนั้นไปยังตัวจัดการข้อยกเว้นที่รับทั้งหมด

คำแนะนำบางอย่างสามารถโยนข้อยกเว้นและบางคำสั่งไม่ได้ เช่นคำแนะนำในการส่งคืนไม่สามารถทำให้เกิดข้อยกเว้นได้ในขณะที่การเรียกใช้คำแนะนำสามารถทำได้ ดังนั้นสำหรับคำสั่งใด ๆ ในบล็อกลองที่สามารถขว้างได้ขอบจะถูกเพิ่มให้กับตัวจัดการข้อยกเว้นสำหรับบล็อกลองนั้น

ในวิธีการดั้งเดิมเงื่อนไขที่อยู่ใกล้กับจุดเริ่มต้นของวิธีการ ( if-eqz v0, :cond_0) จะข้ามไปที่คำสั่ง return โดยตรงดังนั้นจึงไม่มีข้อได้เปรียบสำหรับตัวจัดการข้อยกเว้นเนื่องจากไม่สามารถโยนข้อยกเว้นได้ ดังนั้นวิธีเดียวที่จะไปยังตัวจัดการข้อยกเว้นนั้นคือผ่านเส้นทางการดำเนินการที่v1ได้ตั้งค่าไว้

อย่างไรก็ตามด้วยการเพิ่มคำสั่งเรียกใช้คุณได้เพิ่ม edge จากตรงนั้นไปยังตัวจัดการข้อยกเว้นเพื่อให้ตอนนี้มีพา ธ การดำเนินการไปยังตัวจัดการข้อยกเว้นซึ่งv1ไม่ได้ตั้งค่า

โดยพื้นฐานแล้วให้พิจารณากรณีที่v0เป็นโมฆะตามเงื่อนไข (ดังนั้นจึงใช้การกระโดด) จากนั้นฟังก์ชันการติดตามจะแสดงข้อยกเว้น ตัวจัดการข้อยกเว้นจะถูกเรียกใช้ แต่v1ยังไม่ได้ตั้งค่า