DexPatcher ช่วยให้นักพัฒนาสามารถแก้ไข APK โดยใช้ Java สิ่งนี้มีข้อดีหลายประการ และการใช้ DexPatcher นั้นง่ายกว่าวิธี Smali แบบคลาสสิกมาก
คุณอาจเคยเห็นหรือติดตั้งแอปพลิเคชั่นที่ถูกดัดแปลง ไม่ว่าจะเป็นตัวเรียกเลขหมายที่ได้รับแพตช์สำหรับความละเอียดของคุณ หรือเวอร์ชัน WhatsApp แบบกำหนดเองพร้อมคุณสมบัติเพิ่มเติม นักพัฒนาทำเช่นนั้นได้อย่างไร? หลายครั้งที่ซอร์สโค้ดของแอปพลิเคชันไม่พร้อมใช้งาน ดังนั้นมันทำงานอย่างไร เราจะเห็นสิ่งนั้นก่อน จากนั้นจึงมาดูเครื่องมือใหม่ที่มีจุดมุ่งหมายเพื่อทำให้กระบวนการง่ายขึ้นมาก และสุดท้ายก็เปรียบเทียบกับเฟรมเวิร์ก Xposed ยอดนิยมเพื่อดูว่ามันแตกต่างกันอย่างไร
คุณอาจเคยได้ยินเกี่ยวกับวิธีแก้ไข APK ตามปกติ นักพัฒนาซอฟต์แวร์เสียบปลั๊กตัวเองเข้ากับ เมทริกซ์ เริ่มมองเห็นทุกสิ่งใน Smali และได้รับความสามารถในการแก้ไขสิ่งต่าง ๆ โดยใช้พลังของ แหล่งที่มา. การโทรศัพท์ก็เพียงพอแล้วที่จะพาพวกเขาออกไปเมื่อเสร็จแล้ว เมื่อถึงจุดนี้พวกเขาก็พร้อมที่จะแชร์ APK ใหม่ที่สดใส
จริงจังกว่านี้… มาเริ่มกันตั้งแต่ต้นเลย หากคุณไม่คุ้นเคยกับการดัดแปลงแอปพลิเคชัน Android คุณอาจสงสัยว่า smali คืออะไร นักพัฒนามักจะใช้ภาษาการเขียนโปรแกรม Java เพื่อเขียนโค้ดแอพ Android จากนั้นโปรแกรม (คอมไพลเลอร์) จะ "แปล" โค้ดนั้นเป็นรูปแบบอื่นที่เหมาะกับอุปกรณ์ของคุณ ส่งผลให้ได้ไฟล์ .dex อยู่ภายในแพ็คเกจแอปพลิเคชัน (หรือ APK)
เมื่อถึงจุดนั้น คุณจะไม่สามารถเข้าถึงซอร์สโค้ดต้นฉบับได้อีกต่อไป (เว้นแต่คุณจะเป็นนักพัฒนาหรือแอปพลิเคชันนั้นเป็นโอเพ่นซอร์ส) อย่างไรก็ตาม สิ่งที่คุณมีคือ APK เนื่องจากเป็นสิ่งที่ติดตั้งบนอุปกรณ์ของคุณ จากนั้น คุณจะได้รับไฟล์ dex (โดยปกติจะเป็น class.dex) จากนั้นลองแปลกลับเป็นรูปแบบที่คุณเข้าใจได้ นั่นคือที่มาของ smali ซึ่งเป็นคำแปลที่อ่านง่ายแต่น่าเชื่อถือ คุณสามารถก้าวไปอีกขั้นหนึ่งแล้วแปลกลับเป็น Java แม้ว่ากระบวนการนั้นจะไม่น่าเชื่อถือเพียงพอ แต่คุณจะได้รับ ผลลัพธ์ที่เข้าใจได้ แต่มีโอกาสที่คุณจะแปลอย่างอื่นไม่ได้อีกครั้ง เนื่องจากรายละเอียดบางอย่างจะหายไป ระหว่างทาง. กล่าวอีกนัยหนึ่ง การปรับเปลี่ยนใด ๆ ที่คุณอาจทำนั้นไร้ประโยชน์เพราะคุณจะไม่สามารถเปลี่ยนมันกลับเป็น APK อีกครั้งเพื่อติดตั้งมันบนอุปกรณ์ของคุณได้… อย่างน้อยก็โดยไม่ต้องใช้ความพยายามมากนัก
smali/baksmali จริงๆ แล้วเป็นแอสเซมเบลอร์/ดิสเซมเบลอร์สำหรับรูปแบบ dex นั่นคือสิ่งที่ความหมายในภาษาไอซ์แลนด์อย่างแท้จริง อย่างไรก็ตาม เรามักจะใช้รูปแบบที่ smali เข้าใจเมื่อเราพูดว่า 'Smali' (ให้คิดซะว่าเป็นคำสั่ง) การกำหนดรายละเอียดเล็กๆ น้อยๆ ทุกๆ อย่าง แม้ว่ามนุษย์จะไม่จำเป็นทั้งหมดก็ตาม ดังนั้นจึงละเอียดมากกว่า ชวา) โปรดทราบว่าคำอธิบายข้างต้นนั้นเรียบง่ายเล็กน้อย แต่ควรเป็นการเปรียบเทียบที่ใกล้เคียงแต่ยังคงเข้าใจได้ง่าย
นักพัฒนาจะต้องทำอะไรเพื่อแก้ไขแอป (โดยไม่ต้องเข้าถึงแหล่งที่มา) แล้ว? กระบวนการมีมากหรือน้อยดังนี้:
- รับ APK (จากเว็บหรือจากอุปกรณ์)
- ใช้บางอย่างเช่น apktool.apk เพื่อถอดรหัส APK เป็น Smali (apktool.apk ใช้ smali/baksmali แต่ช่วยให้ถอดรหัสและสร้าง APK ใหม่ได้ง่ายขึ้นมาก และยังดูแลทรัพยากรการถอดรหัสเช่นไฟล์ XML ด้วย)
- แยก class.dex ออกจาก APK แล้วใช้ เดกซ์2จาร์ และสุดท้ายคือตัวถอดรหัส Java เพื่อรับโค้ด Java (ไม่สมบูรณ์ ใช้งานไม่ได้บ่อย แต่ส่วนใหญ่เข้าใจได้) (นี่เป็นทางเลือก แต่อาจมีประโยชน์ได้เนื่องจาก Smali เข้าใจยากกว่ามาก)
- ระบุสิ่งที่จะแก้ไข
- แก้ไขจริงโดยแก้ไขโค้ด Smali โดยตรง
- อีกทางหนึ่ง เขียนการแก้ไขใน Java คอมไพล์ แยกส่วนอีกครั้งเป็น Smali จากนั้นคัดลอกโค้ด Smali ที่เป็นผลลัพธ์ไปทับ
- เสร็จแล้วก็ใช้เลย apktool.apk อีกครั้งเพื่อสร้าง APK ใหม่
- ลงนาม APK (เพื่อยืนยันตัวตนของผู้แต่ง ต้องลงนามแพ็คเกจทั้งหมด) และสุดท้ายจึงทำการติดตั้ง
การเขียนโค้ด Smali ค่อนข้างยากและมีแนวโน้มที่จะเกิดข้อผิดพลาด การเปลี่ยนแปลงเล็กๆ น้อยๆ สามารถทำได้ใน Smali แต่การเพิ่มคุณสมบัติใหม่ๆ เข้าไปนั้นเป็นเรื่องที่ท้าทายยิ่งกว่า นอกจากนี้ คุณจะไม่มีข้อผิดพลาดด้านเวลาคอมไพล์ ดังนั้นแม้แต่การพิมพ์ผิดก็อาจตรวจพบได้เฉพาะตอนรันไทม์เท่านั้น การขยายและการแชร์แพตช์ Smali อาจเป็นปัญหาได้เช่นกัน เนื่องจากส่วนต่างมักจะมีความเฉพาะเจาะจงกับ APK เวอร์ชันใดเวอร์ชันหนึ่งโดยเฉพาะ แม้ว่าจะมีเครื่องมือบางอย่างเพื่อทำให้กระบวนการบางส่วนที่อธิบายข้างต้นง่ายขึ้น (คุณธรรมสิบสตูดิโอ อยู่ในใจ) ก็ยังน่าเบื่อได้
DexPatcher โดย XDA Senior Member แลนชอน มีเป้าหมายเพื่อแก้ไขปัญหาเหล่านี้ โดยทำให้กระบวนการง่ายขึ้น และช่วยให้นักพัฒนาหลีกเลี่ยงการติดต่อกับ Smali ได้อย่างสมบูรณ์ นักพัฒนาซอฟต์แวร์สามารถเขียนแพตช์ใน Java เพียงอย่างเดียวและให้ DexPatcher จัดการทุกอย่างแทน
นี่เป็นข้อได้เปรียบหลักในการมีไฟล์แพตช์ที่อ่านและจัดการได้ง่าย การแพตช์ APK โดยทั่วไปจะสะดวกยิ่งขึ้น เราจะเห็นตัวอย่างเต็มเกี่ยวกับวิธีใช้ DexPatcher ในอีกสักครู่ แต่ต่อไปนี้เป็นภาพรวมโดยย่อเกี่ยวกับสิ่งที่นำเสนอก่อน:
- โอเพ่นซอร์ส.
- ข้ามแพลตฟอร์ม: ควรทำงานบน Linux, Mac และ Windows
- ไฟล์แพทช์: การแก้ไขที่คุณทำจะอยู่ในไฟล์แพทช์ Java ที่คุณสามารถแชร์ได้อย่างอิสระ
- Java: ไม่ใช่ Smali
คุณยังได้รับประโยชน์จากการตรวจสอบข้อผิดพลาดตามเวลาบิลด์ด้วย ดังนั้นจุดบกพร่องจึงปรากฏขึ้นตั้งแต่ช่วงต้นของวงจรการพัฒนา การคอมไพล์ Java ให้การตรวจสอบเวลาการคอมไพล์ตามปกติ (พร้อมการเข้าถึงสัญลักษณ์ APK ดั้งเดิม) และ DexPatcher บังคับใช้ ความเข้ากันได้ของแหล่งที่มาและแพตช์เมื่อทำการแพตช์ การให้ข้อมูลที่เป็นประโยชน์ และคำเตือนเมื่อดูเหมือนว่าคุณกำลังทำอะไรบางอย่าง ถูกกฎหมายแต่คาว
นอกจากนั้น DexPatcher ยังมาพร้อมกับชุดของ สคริปต์ตัวช่วย (ใช้ได้เฉพาะบน Linux แม้ว่าสามารถย้ายไปยังแพลตฟอร์มอื่นได้เช่นกัน) สิ่งเหล่านี้ดูแลการตั้งค่าพื้นที่ทำงาน แยกคลาสและทรัพยากรของ APK เป้าหมาย แยกคลาสเป็น Java ( ตัวถอดรหัส CFR Java ใช้สำหรับอย่างหลัง) และในที่สุดก็สร้างและลงนาม APK ที่ได้รับแพตช์เมื่อคุณทำเสร็จแล้ว
ลองมาดูตัวอย่าง (บน Linux):
ติดตั้งสคริปต์ DexPatcher
$# Make a directory where we can test stuff out and enter it.$ mkdir xda-test
$cd xda-test
$ git clone https://github.com/Lanchon/DexPatcher-scripts.git dexpatcher # Clone the DexPatcher helper scripts repo.
$cd dexpatcher
$ chmod +x dxp-* # Not necessary, but for clarity: we need to make sure the files we'll call later are executable.
กำหนดค่าสคริปต์ DexPatcher
เปิด dxp.config ในโปรแกรมแก้ไขข้อความที่คุณชื่นชอบ และตรวจสอบให้แน่ใจว่าได้เปลี่ยนตัวแปรที่จำเป็นเพื่อให้เหมาะกับระบบของคุณ คุณจะต้องเปลี่ยนบรรทัดต่อไปนี้เพื่อชี้ไปที่ตำแหน่งการติดตั้ง Android SDK ของคุณแทน:
dxp_android_sdk_dir=(~/android/sdk)
(DexPatcher จะเลือกเวอร์ชันแพลตฟอร์มสูงสุดที่มีอยู่โดยอัตโนมัติ นอกจากนี้ คุณยังสามารถแก้ไขตัวเลือกการกำหนดค่าอื่นๆ เพื่อให้ใช้เครื่องมือบางอย่างในเวอร์ชันของคุณเองแทนค่าเริ่มต้นที่รวมไว้ได้)
เพื่อความสะดวกในการเข้าถึงเราสามารถเพิ่ม เดกซ์แพตเชอร์ ไดเรกทอรีของเรา เส้นทางหรือแม้แต่เชื่อมโยงส่วนต่างๆ เข้าด้วยกัน dxp-* สคริปต์ไปยังตำแหน่งที่มีอยู่ในของคุณแล้ว เส้นทาง, เช่น ~/bin:
export PATH=$PWD:$PATH
ปรับเปลี่ยนแอปพลิเคชัน
สำหรับตัวอย่างนี้ เราจะใช้แอปพลิเคชันโอเพ่นซอร์สที่เรียบง่าย แน่นอนว่าการแพตช์ซอร์สโค้ดโดยตรงอาจทำได้ในกรณีนี้ แต่นั่นไม่สนุกเลย!
เราจะนำแอปพลิเคชัน "Get ID" โดย basil2style ซึ่งเป็นแอปพลิเคชันที่แสดงรายละเอียดบางอย่างเกี่ยวกับอุปกรณ์ของคุณ เป้าหมายของเราคือการแก้ไขปุ่ม "คัดลอก" สำหรับ "รหัสอุปกรณ์" และให้ปุ่มแชร์รหัสนี้แทน:
- ก่อนอื่น มาดาวน์โหลด APK ที่เราจะแก้ไขกันก่อน: รับบัตรประจำตัว.
- ถอดรหัสแอปพลิเคชัน
- สร้างคีย์การลงนามที่เราจะใช้ในภายหลังเพื่อลงนาม APK
เรายังสามารถทำได้ทั้งหมดผ่านเชลล์ โดยใช้สคริปต์ตัวช่วย:
$cd dexpatcher # Go to our working directory.$ curl -O https://f-droid.org/repo/makeinfo.com.getid_1.apk # Download the APK.
$ dxp-setup-for-apk makeinfo.com.getid_1.apk # Unpack and decompile the APK.
$cd makeinfo.com.getid_1 # Go to the newly created directory where everything is unpacked/decompiled to.
$ dxp-create-keystore # Create the APK signing key. Press 6 times (or fill out the info), then "yes".
คุณจะสังเกตเห็นไดเร็กทอรีที่แตกต่างกันสองสามรายการในนั้น:
- ถอดรหัส: คุณจะพบทรัพยากรและ Smali ที่นี่ตามที่ถอดรหัสโดย apktool.apk.
- src: ไดเร็กทอรีว่างเปล่า นี่คือที่ที่เราจะวางไฟล์แพทช์ของเรา
- src-cfr: ที่นี่คือที่ อ้างอิง ถอดรหัสแอป (พร้อมกับข้อผิดพลาด) เหมาะที่จะพิจารณาเพื่อตัดสินใจว่าจะเปลี่ยนแปลงอะไร (คุณอาจต้องใช้ทรัพยากรและ ID จากไดเร็กทอรีถอดรหัสด้านบน แต่ไม่ใช่สำหรับตัวอย่างนี้)
- src-cfr-nodecode.src: เช่นเดียวกับข้างต้น แต่มีเพียงสตับที่ว่างเปล่า (ไม่มีรหัส มีเพียงโครงกระดูก) คุณสามารถใช้ไฟล์เหล่านี้เป็นพื้นฐานสำหรับแพทช์ของคุณได้ดังที่เราจะได้เห็นในอีกสักครู่
ดังที่เราได้กล่าวไปแล้ว เราต้องการเปลี่ยนปุ่ม "คัดลอก" ID อุปกรณ์เพื่อแชร์ข้อความ ID แทน หากเราดูซอร์สโค้ดเราจะสังเกตเห็นว่าปุ่มคัดลอกรหัสอุปกรณ์ (อุปกรณ์_คัดลอก) เมื่อคลิก เหตุการณ์ได้รับการจัดการโดยคลาสที่ไม่ระบุชื่อใน src-cfr/makeinfo/com/getid/MainActivity.java. แม้ว่าเราจะแก้ไขมันได้ที่นี่ แต่โดยปกติแล้วจะดีกว่าหากหาวิธีอื่น เนื่องจากคลาสที่ไม่ระบุตัวตนมีชื่อเป็นตัวเลข (MainClassName$SomeNumber, เช่น. กิจกรรมหลัก$3) ซึ่งอาจเปลี่ยนแปลงอย่างคาดเดาไม่ได้ระหว่างเวอร์ชันต่างๆ
แต่เราจะลงทะเบียนชั้นเรียนของเราเองสำหรับกิจกรรมโดยแก้ไข กิจกรรมหลัก ระดับ. ก่อนอื่น เรามาคัดลอกเวอร์ชัน "โครงกระดูก" กันก่อน src-cfr-nocode/makeinfo/com/getid/MainActivity.java ถึง src/makeinfo/com/getid/MainActivity.java (จำไว้ src คือที่ที่แพทช์ของเราจะอาศัยอยู่) (คุณสามารถคัดลอกเวอร์ชันพร้อมโค้ดเต็มได้หากต้องการ นี่เป็นเรื่องของรสนิยมเท่านั้น)
ตอนนี้เราสามารถแก้ไขได้ดังนี้:
- เพิ่มการนำเข้าที่จำเป็นสำหรับคำอธิบายประกอบ DexPatcher:
importlanchon.dexpatcher.annotation.*;
- เพิ่มแท็กเพื่อระบุว่าเรากำลังแก้ไขชั้นเรียน นอกจากนี้เรายังตั้งค่าการดำเนินการเริ่มต้นสำหรับสมาชิกของคลาสแพตช์ด้วย ไม่สนใจซึ่งหมายความว่าสมาชิกจะอยู่ที่นั่นเพื่อให้โค้ดของเราอ้างอิงในระหว่างการคอมไพล์ Java แต่ DexPatcher จะถูกละเลย
@DexEdit(defaultAction=DexAction.IGNORE)publicclassMainActivity
// The reference to ActionBarActivity will be satisfied by symbols
// extracted from the app when we build the patch.
extendsActionBarActivity{
- นอกจากนี้ ให้เพิ่มเนื้อหาว่างให้กับตัวสร้างและ ในการสร้าง วิธีการ เช่นเดียวกับวิธีการอื่นๆ ทั้งหมดที่เราวางแผนจะใช้ (โปรดจำไว้ว่าวิธีการเหล่านั้นจะถูกเพิกเฉยเมื่อมีการใช้โปรแกรมแก้ไขของเราจริง ๆ - เราเพียงเพิ่มวิธีการเหล่านั้นเพื่อให้เราสามารถอ้างอิงถึงได้ที่นี่ หากจำเป็น) คุณยังสามารถเพิ่มไฟล์ พื้นเมือง คำหลักแทน
- ในตอนนี้เราสามารถสร้างแพตช์ได้แล้ว หากคุณสงสัย:
$ dxp-make # Output: `patched.apk`.
ค่อนข้างง่ายใช่มั้ย? ไปกันต่อ -- เรายังทำไม่เสร็จเลย - มาแก้ไขกัน ในการสร้าง ตอนนี้เพื่อกำหนดของตัวเอง OnClickListener เพื่อให้เราสามารถแชร์ ID อุปกรณ์แทนการคัดลอกไปยังคลิปบอร์ด:
// Rename the target method so that we can still call it (the original)// if needed.@DexEdit(target="onCreate")protectedvoidsource_onCreate(Bundlevar1){}// Add our new custom method.@Override@DexAddprotectedvoidonCreate(Bundlevar1){// Call the original method:source_onCreate(var1);// Replace the text and handler:device_copy.setText("Share");device_copy.setOnClickListener(newDeviceCopyOnClick());}// Note that we don't use an anonymous class to avoid nameclashing with// MainActivity$1, which already exists.// We also could've defined a nested MainActivity.Patch class and used// an anonymous class in MainActivity.Patch.onCreate(), and then called// MainActivity.Patch.onCreate() from MainActivity.onCreate().@DexAddclassDeviceCopyOnClickimplementsView.OnClickListener{@OverridepublicvoidonClick(Viewobject){if(MainActivity.this.val){Intentintent=newIntent(Intent.ACTION_SEND);intent.setType("text/plain");intent.putExtra(Intent.EXTRA_SUBJECT,"Device ID");intent.putExtra(Intent.EXTRA_TEXT,device.getText().toString());startActivity(Intent.createChooser(intent,"Share Device ID"));}else{Toast.makeText(MainActivity.this.getApplicationContext(),"Nothing to Share",0).show();}}}
- ดูเหมือนว่าเราจะเสร็จแล้ว! แพทช์เต็มควรมีลักษณะเช่นนี้ นี้. ตอนนี้เราสามารถสร้าง APK ที่ได้รับการแพตช์แล้วและติดตั้งได้:
$ dxp-make$ adb install patched.apk
- มาดูผลลัพธ์กัน:
(ขอบคุณ Lanchon ที่ช่วยในเรื่องโค้ดตัวอย่าง!)
Xposed ได้รับความนิยมอย่างมาก และด้วยเหตุผลที่ดี มันทำให้การสร้าง การแชร์ และติดตั้งม็อดง่ายขึ้นมากสำหรับนักพัฒนาและผู้ใช้ มีความแตกต่างเล็กน้อยระหว่าง DexPatcher และ Xposed ที่อาจทำให้บางคนชอบอย่างใดอย่างหนึ่งมากกว่าสิ่งอื่น:
- Xposed ทำสิ่งมหัศจรรย์ด้วยการเชื่อมโยงวิธีการต่าง ๆ ในขณะรันไทม์ และอนุญาตให้นักพัฒนาทำอะไรบางอย่างก่อน หลัง หรือแทนวิธีการใดก็ได้ ในทางกลับกัน DexPatcher จะแก้ไขทุกอย่างก่อนรันไทม์และสร้าง APK แบบสแตนด์อโลนที่ได้รับการแก้ไข -- การรันโค้ดก่อน, หลังหรือแทนเมธอดยังคงเป็นไปได้ และคุณมีอะไรเพิ่มเติมจริงๆ เสรีภาพ.
- การผลิต APK แบบสแตนด์อโลนหมายความว่าไม่ต้องอาศัยเฟรมเวิร์กภายนอกใดๆ นอกจากนี้ยังหมายความว่าไม่จำเป็นต้องรูทในการแก้ไขแอปของผู้ใช้
- เนื่องจากคุณสร้าง APK ใหม่ด้วย DexPatcher จึงจะมีการเซ็นชื่อแตกต่างออกไป ซึ่งหมายความว่าผู้ใช้ไม่สามารถรับการอัปเดตอย่างเป็นทางการจากผู้เขียนต้นฉบับ และอาจทำให้เกิดปัญหาบางอย่างกับแอป เช่น Google Apps หากตรวจสอบลายเซ็นแล้ว
- ซอร์สโค้ดของแพทช์ทั้งโมดูลและ DexPatcher สามารถแจกจ่ายและแก้ไขได้อย่างง่ายดาย พวกเขายังมีความคล้ายคลึงกันมากมายหากคุณคุ้นเคยบ้าง
เราได้พูดคุยเกี่ยวกับ DexPatcher มามากพอแล้ว ถึงเวลาที่คุณต้องลองดูแล้ว ดังนั้นตรงไปที่ หัวข้อฟอรัม DexPatcher เพื่อเริ่มต้นทันที!