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

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

```{attention}
ตั้งแต่เวอร์ชัน 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 คุณสามารถใช้เมธอดนี้เพื่อส่งข้อมูลไปยังภายนอกได้อย่างสะดวกและเป็นระบบ

```js
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) |
| device | ID อุปกรณ์ (เช่น 67b2a3d7-5004-ea2a-0d44-194de6ede8de) |
| encode | การเข้ารหัสข้อมูล (เช่น none) |
| name | ชื่อข้อมูล (เช่น report_data) |
| script | ID สคริปต์ (เช่น 7c52530d) |
| sequence | ลำดับการรายงาน (เช่น 30) |
| timestamp | เวลาที่รายงาน (เช่น 1740023596914) |
| user | ID แอปพลิเคชันที่โคลน (เช่น 0) |

```{attention}
เนื่องจากคุณสมบัติของโปรโตคอล 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_id | ID ที่ไม่ซ้ำกันของอุปกรณ์ |
| device_id_short | ID ที่ไม่ซ้ำกันของอุปกรณ์ (ID อุปกรณ์แบบสั้นที่เข้ารหัสด้วย BASE62) |
| android_id | Android ID |
| serialno | ro.serialno |


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

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

```{attention}
คำขอ 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`

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

```{attention}
นอกจากฟิลด์รหัสผ่านแล้ว โปรดอย่าใส่ตัวยึดตำแหน่งตัวแปรในลิงก์ 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 ต่อไป

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

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

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

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

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

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

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

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

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

```python
zlib.decompress(data)
```

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

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

```python
app.detach_script()
```