การรายงานข้อมูลโดยใช้ Frida

ฟังก์ชันการรายงานข้อมูลโดยใช้ Frida นั้นมีพื้นฐานมาจากฟังก์ชันการคงอยู่ (persistence) คุณสามารถใช้สคริปต์ Frida ที่คุณเขียนขึ้นเพื่อดักจับข้อมูลจากการเรียกใช้เมธอดโดยอัตโนมัติ และรายงานข้อมูลผ่านเมธอดที่เรากำหนดไว้โดยเฉพาะ ฟังก์ชันการรายงานข้อมูลช่วยให้คุณสามารถอัปโหลดข้อมูลที่สคริปต์ดักจับได้โดยตรงไปยัง Redis หรือ HTTP interface ภายนอกได้อย่างง่ายดาย คุณสามารถรับข้อมูลที่รายงานผ่าน Redis หรือ HTTP interface ได้ ในขณะเดียวกัน เพื่อเพิ่มประสิทธิภาพของเครือข่ายให้สูงสุด เรายังรองรับการรายงานข้อมูลหลังจากการบีบอัด (zlib)

ข้อควรสนใจ

ตั้งแต่เวอร์ชัน 9.0 เป็นต้นไป Frida 17.x ที่เราใช้ภายในจะต้องการให้คุณแพ็ก frida-java-bridge เข้าไปในสคริปต์ด้วยตนเอง มิฉะนั้นจะเกิดข้อผิดพลาดเกี่ยวกับ Java not defined การเปลี่ยนแปลงนี้เป็นการเปลี่ยนแปลงอย่างเป็นทางการของ Frida ตามคำอธิบายการเปลี่ยนแปลงอย่างเป็นทางการ คุณจะต้องสร้างโปรเจกต์ Node.js ใหม่และนำเข้า java bridge สำหรับรายละเอียดเพิ่มเติม โปรดอ้างอิง https://github.com/oleavr/frida-agent-example หรือใช้ tools/frida_script_generate.py ที่เรามีให้เพื่อห่อหุ้มสคริปต์ js เดิมอีกครั้ง

การเขียนสคริปต์รายงาน

ขั้นตอนแรก คุณต้องแก้ไขสคริปต์ Frida ของคุณ โดยปกติแล้วสคริปต์ Frida จะมีฟังก์ชันเช่น send และ log ที่ช่วยให้คุณส่งข้อมูลออกไปภายนอกได้ สำหรับ FIRERPA คุณต้องใช้เมธอดที่กำหนดไว้โดยเฉพาะเพื่อส่งข้อมูลออกไป ด้านล่างนี้เป็นตัวอย่างโค้ดเทมเพลตที่เราใช้สำหรับการดักจับทราฟฟิกของ OkHttp ซึ่งเป็นเพียงสคริปต์สาธิตเท่านั้น คุณอาจไม่สามารถใช้งานสคริปต์นี้ได้ตามปกติ สคริปต์ด้านล่างนี้ไม่มีความแตกต่างจากสคริปต์ทั่วไปมากนัก ความแตกต่างเพียงอย่างเดียวคือการใช้เมธอด emit ซึ่งเป็นเมธอดที่มีมาในตัวของ FIRERPA คุณสามารถใช้เมธอดนี้เพื่อส่งข้อมูลไปยังภายนอกได้อย่างสะดวกและเป็นระบบ

Java.perform(function() {
        Java.use("com.android.okhttp.internal.http.HttpEngine").getResponse.implementation = function() {
                var response = this.getResponse()
                var data = {}
                data["url"] = response._request.value._url.value._url.value
                data["body"] = response.body().string()
                emit("report_data", JSON.stringify(data))
                return response
        }
})

เมธอด emit มีพารามิเตอร์สองตัวคือ emit(name, content) โดยที่ name หมายถึงประเภทของข้อมูล หากที่อยู่การรายงานที่คุณตั้งค่าไว้เป็น Redis ชื่อนี้จะหมายถึงชื่อคิวของ Redis คุณควรพยายามใช้คำอธิบายภาษาอังกฤษที่แม่นยำที่สุด เช่น product_info เป็นต้น ส่วน content หมายถึงเนื้อหาของข้อมูลนั้นๆ ซึ่งรองรับเฉพาะประเภทสตริง (string) และอาร์เรย์ไบต์ (bytes) เท่านั้น ในตัวอย่างข้างต้น เราได้แปลงข้อมูลเป็นสตริง JSON ก่อนส่ง

ตอนนี้คุณได้เข้าใจรูปแบบและข้อกำหนดในการเรียกใช้ที่จำเป็นสำหรับการเขียนสคริปต์แล้ว ซึ่งก็คือวิธีการส่งข้อมูลที่ Hook ได้ไปยังภายนอก คุณยังต้องอ่านเนื้อหาต่อไปเพื่อทำความเข้าใจวิธีการกำหนดค่า ปลายทางของการรายงานข้อมูล

ปลายทางของการรายงานข้อมูล

ปลายทางของการรายงานข้อมูล หมายถึงสถานที่ที่ข้อมูลที่คุณ emit ในสคริปต์ควรจะถูกส่งไป ประเภทของปลายทางที่รองรับได้แก่ HTTP interface, คิว Redis, และคิว RabbitMQ ซึ่งมีความแตกต่างกันเล็กน้อย

โดยปกติแล้ว หากคุณไม่จำเป็นต้องสนใจข้อมูลเกี่ยวกับแหล่งที่มาของข้อมูล เช่น ไม่จำเป็นต้องจับคู่ ข้อมูล กับ เครื่องต้นทาง เราขอแนะนำให้ใช้คิว Redis ในทางกลับกัน คุณควรใช้ HTTP หรือ MQTT (v5) เนื่องจากคุณสมบัติของโปรโตคอลทั้งสองนี้ เราสามารถแนบเมตาดาต้าเพิ่มเติมเข้าไปในโปรโตคอลได้ ซึ่งจะช่วยให้คุณสามารถจับคู่อุปกรณ์ได้อย่างแม่นยำยิ่งขึ้น

เมตาดาต้าที่รายงาน

สำหรับข้อมูลที่รายงานไปยัง HTTP interface และปลายทาง RabbitMQ นอกจากจะรวมข้อมูลที่รายงานเดิมแล้ว คุณยังสามารถรับเมตาดาต้าเกี่ยวกับอุปกรณ์และสคริปต์ผ่านเลเยอร์โปรโตคอลได้อีกด้วย เมตาดาต้าที่แนบมาในโปรโตคอลแสดงดังตารางด้านล่าง

ฟิลด์คำอธิบาย
applicationชื่อแพ็กเกจของแอปพลิเคชัน (เช่น com.android.settings)
deviceID อุปกรณ์ (เช่น 67b2a3d7-5004-ea2a-0d44-194de6ede8de)
encodeการเข้ารหัสข้อมูล (เช่น none)
nameชื่อข้อมูล (เช่น report_data)
scriptID สคริปต์ (เช่น 7c52530d)
sequenceลำดับการรายงาน (เช่น 30)
timestampเวลาที่รายงาน (เช่น 1740023596914)
userID แอปพลิเคชันที่โคลน (เช่น 0)

ข้อควรสนใจ

เนื่องจากคุณสมบัติของโปรโตคอล Redis ข้อมูลที่รายงานไปยังปลายทาง Redis จะไม่รวมเมตาดาต้าใดๆ ที่กล่าวมาข้างต้น

device คือ ID ที่ไม่ซ้ำกันของอุปกรณ์ คุณสามารถค้นหาได้ในแถบข้อมูลของเดสก์ท็อประยะไกล โดยปกติแล้ว ID นี้จะไม่ซ้ำกันและคงที่ คุณสามารถใช้ ID นี้เพื่อระบุอุปกรณ์และสร้างความสัมพันธ์ได้ encode คือการเข้ารหัสข้อมูล ซึ่งรองรับ none และ zlib หากการเข้ารหัสเป็น zlib คุณจะต้องใช้ zlib เพื่อคลายการบีบอัดเนื้อหาข้อมูล name ใช้เพื่อระบุประเภทของข้อมูลที่รายงานนี้ ซึ่งเป็นพารามิเตอร์ตัวแรกที่คุณใช้ในเมธอด emit sequence หมายถึงดัชนีของข้อมูลที่รายงาน โดยเริ่มจาก 0 และจะเพิ่มขึ้นทุกครั้งที่มีการรายงาน คุณสามารถใช้ฟิลด์นี้เพื่อจัดเรียงข้อมูลหรือตรวจสอบว่ามีการรายงานข้อมูลสูญหายหรือไม่

พารามิเตอร์เพิ่มเติมในลิงก์การรายงาน

เมื่อคุณสร้างลิงก์การรายงาน คุณสามารถระบุพารามิเตอร์ ID แบบไดนามิกบางอย่างในลิงก์ได้ โดยใช้รูปแบบตัวยึดตำแหน่งตัวแปร ${name} เพื่อแทรกเข้าไปในส่วนต่างๆ ของลิงก์ ตัวอย่างเช่น http://192.168.1.2/report/${device_id} ตัวแปรที่รองรับมีดังนี้

ชื่อคำอธิบายชื่อ
device_idID ที่ไม่ซ้ำกันของอุปกรณ์
device_id_shortID ที่ไม่ซ้ำกันของอุปกรณ์ (ID อุปกรณ์แบบสั้นที่เข้ารหัสด้วย BASE62)
android_idAndroid ID
serialnoro.serialno

การรายงานไปยัง HTTP

คุณต้องเขียนบริการ HTTP เพื่อรับข้อมูลที่รายงานด้วยตนเอง HTTP interface สำหรับการรายงานข้อมูลจะต้อง implement เมธอด POST FIRERPA จะส่งข้อมูลผ่าน POST ไปยัง interface และจะเข้ารหัสแต่ละฟิลด์ในเมตาดาต้าเป็นพารามิเตอร์คิวรีของ HTTP ซึ่งคุณสามารถดึงข้อมูลและประมวลผลได้ สำหรับเนื้อหาข้อมูล จะถูกแนบมาใน body ของคำขอ POST FIRERPA จะทำการลองใหม่ 3 ครั้งโดยอัตโนมัติเมื่อได้รับรหัสสถานะ 502, 503, 504 หากแบ็กเอนด์ของคุณรับและประมวลผลข้อมูลที่รายงานอย่างถูกต้อง ควรตอบกลับด้วยข้อความธรรมดา OK หรือ SUCCESS และตั้งค่ารหัสสถานะเป็น 200 เพื่อแสดงว่าการประมวลผลสำเร็จ

ข้อควรสนใจ

คำขอ HTTP เป็นแบบมัลติเธรด ข้อความที่แบ็กเอนด์ได้รับอาจไม่เป็นไปตามลำดับการรายงาน (sequence)
ตัวอย่างลิงก์การรายงาน http://192.168.1.2/report/${device_id}?serialno=${serialno}
หากต้องการการยืนยันตัวตนด้วยรหัสผ่าน http://user:password@192.168.1.2/report/${device_id}?serialno=${serialno}

การรายงานไปยัง MQTT

สำหรับการรายงานไปยัง MQTT คุณควรดึงเมตาดาต้าที่รายงานจาก UserProperty รองรับ TLS, ชื่อผู้ใช้และรหัสผ่าน, และการตรวจสอบใบรับรองทางเดียว (ตรวจสอบใบรับรองเซิร์ฟเวอร์) แต่ไม่รองรับการตรวจสอบสองทาง

ตัวอย่างลิงก์การรายงาน mqtt://test.mosquitto.org:1883/script/${device_id}/report
หากต้องการการยืนยันตัวตนด้วยรหัสผ่าน mqtt://rw:readwrite@test.mosquitto.org:1884/script/${device_id}/report

หากต้องการการตรวจสอบทางเดียว mqtts://test.mosquitto.org:8883/script/${device_id}/report?verify=true&ca=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVBekNDQXV1Z0F3SUJBZ0lVQlkxaGxDR3ZkajROaEJYa1ovdUxVWk5JTEF3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd2daQXhDekFKQmdOVkJBWVRBa2RDTVJjd0ZRWURWUVFJREE1VmJtbDBaV1FnUzJsdVoyUnZiVEVPTUF3RwpBMVVFQnd3RlJHVnlZbmt4RWpBUUJnTlZCQW9NQ1UxdmMzRjFhWFIwYnpFTE1Ba0dBMVVFQ3d3Q1EwRXhGakFVCkJnTlZCQU1NRFcxdmMzRjFhWFIwYnk1dmNtY3hIekFkQmdrcWhraUc5dzBCQ1FFV0VISnZaMlZ5UUdGMFkyaHYKYnk1dmNtY3dIaGNOTWpBd05qQTVNVEV3TmpNNVdoY05NekF3TmpBM01URXdOak01V2pDQmtERUxNQWtHQTFVRQpCaE1DUjBJeEZ6QVZCZ05WQkFnTURsVnVhWFJsWkNCTGFXNW5aRzl0TVE0d0RBWURWUVFIREFWRVpYSmllVEVTCk1CQUdBMVVFQ2d3SlRXOXpjWFZwZEhSdk1Rc3dDUVlEVlFRTERBSkRRVEVXTUJRR0ExVUVBd3dOYlc5emNYVnAKZEhSdkxtOXlaekVmTUIwR0NTcUdTSWIzRFFFSkFSWVFjbTluWlhKQVlYUmphRzl2TG05eVp6Q0NBU0l3RFFZSgpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNRTBIS21JemZUT3drS0xUM1RISGUrT2JkaXphbVBnClVabUQ2NFRmM3pKZE5lWUdZbjRDRVhieVA2ZnkzdFdjOFMyYm9XNmR6ckg4U2RGZjl1bzMyMEdKQTlCN1UxRlcKVGUzeGRhL0xtM0pGZmFIamtXdzdqQndjYXVRWmpwR0lOSGFwSFJscGlDWnNxdUF0aE9neFc5U2dEZ1lsR3pFQQpzMDZwa0VGaU13K3FEZkxvL3N4RktCNnZRbEZla01lQ3ltakxDYk53UEp5cXloRm1QV3dpby9QRE1ydUJUelBICjNjaW9CbnJKV0tYYzNPalhkTEdGSk9majdwUDBqL2RyMkxINzJlU3Z2M1BRUUZsOTBDWlBGaHJDVWNSSFNTeG8KRTZ5akdPZG56N2Y2UHZlTElCNTc0a1FPUnd0OGVQbjB5aWRyVEMxaWN0aWtFRDNuSFloTVVPVUNBd0VBQWFOVApNRkV3SFFZRFZSME9CQllFRlBWVjZ4QlVGUGlHS0R5bzVWMytIYmg0TjlZU01COEdBMVVkSXdRWU1CYUFGUFZWCjZ4QlVGUGlHS0R5bzVWMytIYmg0TjlZU01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUwKQlFBRGdnRUJBR2E5a1MyMU43MFRoTTYvSGo5RDdtYlZ4S0xCalZXZTJUUHNHZmJsM3JFRGZaK09LUloyajZBQwo2cjdqYjRUWk8zZHpGMnA2ZGdicmxVNzFZLzRLMFRkeklqUmozY1EzS1NtNDFKdlVRMGhaL2MwNGlHRGcveFdmCitwcDU4bmZQQVl3dWVycnVQTldtbFN0V0FYZjBVVHFSdGc0aFFEV0J1VUZESlR1V3V1QnZFWHVkejc0ZWgvd0sKc013ZnUxSEZ2ank1WjBpTURVOFBVRGVwalZvbE9DdWU5YXNobFM0RUI1SUVDZFNSMlRJdG5BSWlJd2lteDgzOQpMZFVkUnVkYWZNdTVUNVhtYTE4Mk9DMC91L3hSbEVtK3R2S0dHbWZGY04wcGlxVmw4T3JTUEJnSWxiKzFJS0pFCm0vWHJpV3IvQ3E0aC9KZkI3TlRzZXpWc2xna0Jhb1U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K

ในลิงก์ข้างต้น ข้อมูลที่เกี่ยวข้องจะถูกส่งไปยัง script/${device_id}/report คุณสามารถ subscribe ข้อความเหล่านี้ได้โดยใช้คำสั่งเช่น mosquitto_sub -L mqtt://test.mosquitto.org:1883/script/+/report

การรายงานไปยัง Redis

การรายงานไปยัง Redis นั้นค่อนข้างง่าย เนื่องจากไม่มีการแนบเมตาดาต้าใดๆ จึงไม่สามารถแยกแยะแหล่งที่มาของข้อมูลได้โดยตรง คุณอาจต้องใช้วิธีการแก้ไขสคริปต์ที่ inject เข้าไปแบบไดนามิกเพื่อทำสิ่งนี้ สำหรับการรายงานไปยัง Redis FIRERPA จะนำเนื้อหาข้อมูลไปใส่ในคิวโดยตรงผ่านคำสั่ง LPUSH ตัวอย่างเช่น ในสคริปต์ตัวอย่างข้างต้น ข้อมูลที่รายงานจะถูกใส่เข้าไปในคิว report_data

ข้อควรสนใจ

รองรับเฉพาะบริการ Redis ในโหมด standalone เท่านั้น ไม่รองรับคลัสเตอร์ Redis

ข้อควรสนใจ

นอกจากฟิลด์รหัสผ่านแล้ว โปรดอย่าใส่ตัวยึดตำแหน่งตัวแปรในลิงก์ Redis ลิงก์นี้เป็นลิงก์มาตรฐานที่ไลบรารี Redis รองรับ การแก้ไขส่วนอื่นๆ อาจทำให้ไม่สามารถแยกวิเคราะห์ได้อย่างถูกต้อง
ตัวอย่างลิงก์การรายงาน redis://1.2.3.4/0
หากต้องการการยืนยันตัวตนด้วยรหัสผ่าน redis://:password@1.2.3.4/0

TLS + รหัสผ่าน

rediss://:password@1.2.3.4/0?ssl_ca_data=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURUVENDQWpXZ0F3SUJBZ0lVUFIvcmcxK0x2aU5tYzNsc0...

การ Inject สคริปต์รายงาน

แน่นอนว่า การได้รับอินสแตนซ์ของแอปเป็นขั้นตอนแรก คุณสามารถใช้การเรียกใช้ข้างต้นเพื่อรับตัวแปร app ซึ่งหมายถึงอินสแตนซ์ของแอปพลิเคชันที่คุณต้องการ inject จากนั้นคุณจะต้องใช้ตัวแปรนี้เพื่อดำเนินการ inject หรือ detach ต่อไป

app = d.application("com.android.settings")

ใช้ interface ต่อไปนี้เพื่อ inject สคริปต์รายงานข้างต้นเข้าไปในแอปพลิเคชัน เมื่อข้อมูลถูกดักจับ ข้อมูลจะถูกส่งไปยังคิว report_data ของ Redis ในตัวอย่างนี้ อุปกรณ์ของคุณจะต้องสามารถเข้าถึงบริการที่เกี่ยวข้องบน 192.168.1.10 ได้โดยตรง มิฉะนั้นจะไม่สามารถรายงานข้อมูลได้

app.attach_script(script, emit="redis://192.168.1.10/0")

คุณยังสามารถตั้งค่าแบบนี้ได้ ซึ่งข้อมูลของคุณจะถูกส่งไปยัง HTTP interface แทนที่จะเป็น Redis (รองรับ https)

app.attach_script(script, emit="http://192.168.1.10/dataReport")

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

app.attach_script(..., encode=DataEncode.DATA_ENCODE_ZLIB)

การคลายการบีบอัดข้อมูลที่รายงาน

โดยค่าเริ่มต้น ข้อมูลที่รายงานจะไม่มีการบีบอัดใดๆ หากคุณตั้งค่าการบีบอัดข้อมูลที่รายงาน คุณจะต้องใช้การเข้ารหัส zlib ที่ฝั่งผู้รับเพื่อคลายการบีบอัดข้อมูลที่รายงาน คุณสามารถใช้เมธอด decompress ของไลบรารี zlib ที่เป็นทางการของ Python เพื่อคลายการบีบอัดข้อมูลที่รายงานได้อย่างสะดวก

zlib.decompress(data)

การลบสคริปต์รายงาน

กระบวนการลบสคริปต์รายงานนั้นง่ายมาก เหมือนกับการใช้งานในสคริปต์แบบคงอยู่ (persistence script)

app.detach_script()