├── .gitignore
├── LICENSE.txt
├── Project.txt
├── README.md
├── README_LGKDZ.txt
├── README_ozip.md
├── README_simg2img.txt
├── README_unpayload.md
├── about_pycrypto.md
├── clean_cache.py
├── dz.py
├── gpt.py
├── image2chunks.py
├── install_requirements.py
├── kdz.py
├── main.py
├── make.py
├── ofp_libextract.py
├── ozipdecrypt.py
├── payload_dumper.py
├── pic
├── 1.png
├── 2.png
├── 3.png
├── 4.png
├── 5.png
├── 6.png
├── README.txt
└── home.png
├── requirements.txt
├── rimg2sdat.py
├── sdat2img.py
├── simg2img.py
├── undz.py
├── unkdz.py
└── update_metadata_pb2.py
/.gitignore:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | # 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。
3 | ################################################################################
4 | __pycache__
5 | .git
6 | .vs
7 | META-INF
8 | output
9 | rom
10 | Imgextractor.exe
11 | build
12 | dist
13 | main.spec
14 | testcode.py
15 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/Project.txt:
--------------------------------------------------------------------------------
1 | oppoozip:https://github.com/tahirtaous/ozip2zip
2 | extract_android_ota_payload:https://github.com/cyxx/extract_android_ota_payload
3 | sdat2img:https://github.com/xpirt/sdat2img
4 | rimg2sdat:https://github.com/jazchen/rimg2sdat
5 | LGKDZ:https://github.com/randomstuffpaul/kdztools
6 | PayloadDumperOnDocker:https://github.com/matze19999/PayloadDumperOnDocker
7 | simg2img:https://code.google.com/p/simg2img
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Rom处理工具(Python)
2 |
3 | 此项目使用Python语言,一键解包安卓ROM的system.img
4 |
5 | 由于各大手机厂商基本上已经封闭了ROM的下载和获取,后继对于ROM解包支持可能会难以维护
6 |
7 | 目前主要支持安卓10及以下的ROM解包(以及安卓10及以上标准的动态分区/AB分区解包支持)
8 |
9 |
10 | 支持格式:
11 |
12 | > .new.dat. .new.dat.br .img .tar.md5 .ozip .kdz .dz .bin .zip .tar
13 |
14 | 其中,ozip,new.dat(.br),img,payload.bin,以及部分zip可以一键解包出system.
15 |
16 | 同时,还可以嗅探某些ROM的底层(魅族 魅蓝note5/6 嗅探底层成功)
17 |
18 | 支持几乎除了安卓10动态分区外的所有卡刷包,以及三星,LG线刷包,ozip解密等
19 |
20 | 特点:将众多开源项目涵盖在了一个项目中,方便ROM的解包操作及寻找开源项目
21 |
22 | (若main处理不了,你还可以用其它的,不过会略微麻烦就是了)
23 |
24 |
25 |
26 | 测试结果:→[前往观赏测试图](pic)
27 |
28 | 可以正常识别目前我见到的卡刷包
29 |
30 | oppo ozip解密解包功能正常(仅部分机型)(含.new.dat.br)
31 |
32 | 三星官方tar.md5解包system正常
33 |
34 | MIUI线刷包卡刷包解包正常
35 |
36 | 魅族 new.dat解包正常
37 |
38 | 360 普通打包方式解包正常
39 |
40 | Google AB payload.bin解包正常
41 |
42 | LG KDZ解包正常
43 |
44 | (暂未添加DZ解包)
45 |
46 | 食用步骤:
47 |
48 | Clone该项目:
49 |
50 | ```
51 | git clone --depth=1 https://hub.fastgit.org/AEnjoy/unpackandroidrom.git
52 | ```
53 |
54 | 安装依赖:
55 |
56 | ```shell
57 | python3 install_requirements.py
58 | # or
59 | pip install -r requirements.txt
60 | ```
61 |
62 | 运行:
63 |
64 | ```shell
65 | python3 main.py
66 | ```
67 |
68 | (提交bug -_-||,无情嘲讽)
69 |
70 | 可选操作:
71 |
72 | 定期执行-清理缓存
73 |
74 | ```shell
75 | python clean_cache.py
76 | ```
77 |
78 | 定期执行-项目同步
79 |
80 | ```shell
81 | git pull
82 | ```
83 |
84 | 
85 |
86 | 本项目引用的项目(文件)列表及来源:
87 |
88 | oppoozip:https://github.com/tahirtaous/ozip2zip
89 | ~~extract_android_ota_payload:https://github.com/cyxx/extract_android_ota_payload~~
90 | sdat2img:https://github.com/xpirt/sdat2img
91 | rimg2sdat:https://github.com/jazchen/rimg2sdat
92 | LGKDZ:https://github.com/randomstuffpaul/kdztools
93 | PayloadDumperOnDocker:https://github.com/matze19999/PayloadDumperOnDocker
94 | simg2img:https://code.google.com/p/simg2img
95 |
96 | 运行环境需求:
97 |
98 | Python2.7/Python3.6+
99 |
100 | 运行依赖包含在requirements.txt文件中,你可以运行install_requirements.py 一键安装依赖
101 |
102 | Changes:
103 |
104 | ```
105 | 2.2.4→2.2.5:2024-7-7 17:35:59
106 | 1.Upgrade Docs.
107 | 2.Fix TypeError thanks to @WangBoee
108 |
109 | 2.2.3→2.2.4:2021-8-22 23:09:49
110 | 1.旧版本号忘记更新了,现在更新回来
111 | 2.修复三星线刷包解包失败问题(Thanks to CoolApkUser:名字嘛随机取一个)
112 | 3.修复三星线刷包识别可能失败的问题
113 | 4.清理代码中的垃圾部分
114 | 5.make.py Update:减小打包后的文件体积
115 | 6.make.py Update:根据系统,系统架构自动重命名压缩包文件
116 | 7.修复MIUI线刷包解包后可能会出现错误的问题
117 | 8.解决识别部分ROM后产生的垃圾文件未自动清理的问题
118 | 9.默认自动转换动态分区super.img文件
119 |
120 | 2.2.2→2.2.3:2021-8-22 19:09:49
121 | 1.修复MIUI线刷包解包后可能会出现错误的问题
122 | 2.解决识别部分ROM后产生的垃圾文件未自动清理的问题
123 | 3.默认自动转换动态分区super.img文件
124 |
125 | 2.2.2→2.2.3:2021-8-20 01:25:48
126 | 1.旧版本号忘记更新了,现在更新回来
127 | 2.修复三星线刷包解包失败问题(Thanks to CoolApkUser:名字嘛随机取一个)
128 | 3.修复三星线刷包识别可能失败的问题
129 | 4.清理代码中的垃圾部分
130 | 5.make.py Update:减小打包后的文件体积
131 | 6.make.py Update:根据系统,系统架构自动重命名压缩包文件
132 |
133 | 2.2.1→2.2.2:2021-8-19
134 | 1.Bug Fixed.
135 |
136 | 2.1→2.2.1:on 1 Jul
137 | 1.增加了Linux arm64的编译
138 | 2.增加了有关pycrypto的readme
139 | 3.现在没有pycrypto依赖也可以运行项目
140 | 4.make.py支持手动编译,方便跨平台
141 |
142 | 2→2.1:2020-8-16 13:19:14
143 | 1.修复了一个小bug
144 |
145 | 1→2:2020-8-15 21:58:28
146 | 1.更新lz4,brotli解包代码
147 | 2.更新依赖(可能需要重新运行install_requirements.py)
148 | 3.LG DZ解包支持(之前只支持查看)
149 | 4.MIUI线刷包解包
150 | ```
151 |
152 | 根据上游开源,本项目开源许可协议为GNU/GPL3
--------------------------------------------------------------------------------
/README_LGKDZ.txt:
--------------------------------------------------------------------------------
1 | -------------------------------------------
2 |
3 | LGE KDZ Utilities
4 | originally
5 | Copyright 2013 "IOMonster" (thecubed on XDA and GitHub)
6 | Copyright 2016 Elliott Mitchell
7 |
8 | -------------------------------------------
9 |
10 | These two scripts will allow you to extract both KDZ files and DZ files
11 |
12 | Run unkdz.py or undz.py with the -h option to get more options.
13 |
14 | -l or --list
15 | Lists all files contained in the archive
16 |
17 | -x or --extract
18 | Extract all portions or as chunk-files. See -h or below for
19 | more detail.
20 |
21 | -c or --chunks
22 | (undz-only) Extract archive as chunks, unless a specific
23 | chunk number is give, all chunks will be extracted.
24 |
25 | -s ID or --single ID
26 | Extract a single slice/partition by ID (can be found with
27 | --list). With undz multiple IDs can be given and the whole
28 | slice/partition will be extracted
29 |
30 | -i or --image
31 | (undz-only) Extract who archive as a disk image
32 |
33 | -d DIR or --dir DIR
34 | Set directory instead of the default "[kdz|dz]extracted"
35 | directory in the current path
36 |
37 | -f FILE or --file FILE
38 | File to operate on
39 |
40 | A sample workflow can look like:
41 |
42 | $ unkdz -f H90120j_00_0712.kdz -l
43 | [+] KDZ Partition List (format v2)
44 | =========================================
45 | 0 : H90120j_00.dz (1988019978 bytes)
46 | 1 : LGUP_c.dll (2875560 bytes)
47 | 2 : LGUP_c.dylib (1170000 bytes)
48 | $ unkdz -f H90120j_00_0712.kdz -x
49 | [+] Extracting all partitions from v2 file!
50 |
51 | [+] Extracting H90120e_00.dz to kdzextracted/H90120j_00.dz
52 | [+] Extracting LGUP_c.dll to kdzextracted/LGUP_c.dll
53 | [+] Extracting LGUP_c.dylib to kdzextracted/LGUP_c.dylib
54 | $ undz -f kdzextracted/H90120j_00.dz -l
55 | [!] Warning: Chunk is part of "BackupGPT", but starts in front of slice?!
56 | [+] DZ Partition List
57 | =========================================
58 | 0/ 1 : PrimaryGPT_0.bin (5199 bytes)
59 | -1/?? : _unallocated_0_8371200 ()
60 | 1/ 2 : modem_16384.bin (42902300 bytes)
61 | 2/?? : spare1 ()
62 | 3/ 3 : pmic_196608.bin (11065 bytes)
63 | 4/ 4 : sbl1_197632.bin (290373 bytes)
64 | 5/ 5 : tz_199680.bin (258129 bytes)
65 | 6/ 6 : sdi_201728.bin (14684 bytes)
66 | 7/ 7 : hyp_202752.bin (23677 bytes)
67 | 8/ 8 : rpm_203776.bin (111265 bytes)
68 | 9/ 9 : aboot_204800.bin (407367 bytes)
69 | 10/10 : sbl1bak_208896.bin (290373 bytes)
70 | 11/11 : pmicbak_210944.bin (11065 bytes)
71 | 12/12 : hypbak_211968.bin (23677 bytes)
72 | 13/13 : tzbak_212992.bin (258129 bytes)
73 | 14/14 : rpmbak_215040.bin (111265 bytes)
74 | 15/15 : abootbak_216064.bin (407367 bytes)
75 | 16/16 : sdibak_220160.bin (14684 bytes)
76 | 17/?? : limits ()
77 | 18/?? : apdp ()
78 | 19/?? : msadp ()
79 | 20/?? : dpo ()
80 | 21/?? : spare2 ()
81 | 22/?? : persistent ()
82 | 23/?? : devinfo ()
83 | 24/?? : spare3 ()
84 | 25/?? : misc ()
85 | 26/17 : persist_278528.bin (23385 bytes)
86 | 27/?? : modemst1 ()
87 | 28/?? : modemst2 ()
88 | 29/?? : fsg ()
89 | 30/?? : fsc ()
90 | 31/?? : ssd ()
91 | 32/?? : keystore ()
92 | 33/?? : DDR ()
93 | 34/18 : sec_360448.bin (2603 bytes)
94 | 35/?? : encrypt ()
95 | 36/?? : eksst ()
96 | 37/19 : rct_363520.bin (2317 bytes)
97 | 38/?? : spare4 ()
98 | 39/20 : laf_376832.bin (19533292 bytes)
99 | 40/21 : boot_475136.bin (16694149 bytes)
100 | 41/22 : recovery_557056.bin (17891387 bytes)
101 | 42/?? : drm ()
102 | 43/?? : sns ()
103 | 44/?? : mpt ()
104 | 45/23 : raw_resources_737280.bin (888299 bytes)
105 | 46/24 : raw_resourcesbak_745472.bin (888299 bytes)
106 | 47/25 : factory_753664.bin (2317 bytes)
107 | 48/?? : spare5 ()
108 | 49/?? : fota ()
109 | 50/?? : fau ()
110 | 51/26 : system_901120.bin (25371453 bytes)
111 | 51/27 : system_1165448.bin (60652649 bytes)
112 | 51/28 : system_1429456.bin (65098099 bytes)
113 | 51/29 : system_1689736.bin (2313 bytes)
114 | 51/30 : system_1693784.bin (52573309 bytes)
115 | 51/31 : system_1953744.bin (64109275 bytes)
116 | 51/32 : system_2214024.bin (2313 bytes)
117 | 51/33 : system_2218072.bin (75157338 bytes)
118 | 51/34 : system_2478032.bin (56945821 bytes)
119 | 51/35 : system_2738312.bin (2313 bytes)
120 | 51/36 : system_2742360.bin (68310797 bytes)
121 | 51/37 : system_3002320.bin (54648076 bytes)
122 | 51/38 : system_3262600.bin (2313 bytes)
123 | 51/39 : system_3266648.bin (59937725 bytes)
124 | 51/40 : system_3526608.bin (66799033 bytes)
125 | 51/41 : system_3788752.bin (61032997 bytes)
126 | 51/42 : system_4050896.bin (88105175 bytes)
127 | 51/43 : system_4313040.bin (57305742 bytes)
128 | 51/44 : system_4575184.bin (59805583 bytes)
129 | 51/45 : system_4837328.bin (53946948 bytes)
130 | 51/46 : system_5099472.bin (62892749 bytes)
131 | 51/47 : system_5361616.bin (82165315 bytes)
132 | 51/48 : system_5623760.bin (68205296 bytes)
133 | 51/49 : system_5885904.bin (60609918 bytes)
134 | 51/50 : system_6148048.bin (80451673 bytes)
135 | 51/51 : system_6410192.bin (73034607 bytes)
136 | 51/52 : system_6672336.bin (59097445 bytes)
137 | 51/53 : system_6934480.bin (61310637 bytes)
138 | 51/54 : system_7196624.bin (58278602 bytes)
139 | 51/55 : system_7456904.bin (2313 bytes)
140 | 51/56 : system_7460952.bin (55951601 bytes)
141 | 51/57 : system_7720912.bin (64319230 bytes)
142 | 51/58 : system_7981192.bin (2313 bytes)
143 | 51/59 : system_7985240.bin (66376379 bytes)
144 | 51/60 : system_8245200.bin (62336405 bytes)
145 | 51/61 : system_8507344.bin (26426046 bytes)
146 | 51/62 : system_8765440.bin (2314 bytes)
147 | 51/63 : system_9027584.bin (2314 bytes)
148 | 51/64 : system_9289728.bin (2314 bytes)
149 | 51/65 : system_9551872.bin (2317 bytes)
150 | 51/66 : system_9554936.bin (35634261 bytes)
151 | 52/?? : cache ()
152 | 53/?? : userdata ()
153 | 54/?? : grow ()
154 | 55/67 : BackupGPT_122141696.bin (5174 bytes)
155 | $ undz -f kdzextracted/H90120j_00.dz -x
156 | [!] Warning: Chunk is part of "BackupGPT", but starts in front of slice?!
157 | [+] Extracting all chunkfiles!
158 |
159 | [+] Extracting PrimaryGPT_0.bin to PrimaryGPT_0.bin.chunk
160 | [+] Extracting modem_16384.bin to modem_16384.bin.chunk
161 | [+] Extracting pmic_196608.bin to pmic_196608.bin.chunk
162 | [+] Extracting sbl1_197632.bin to sbl1_197632.bin.chunk
163 | [+] Extracting tz_199680.bin to tz_199680.bin.chunk
164 | [+] Extracting sdi_201728.bin to sdi_201728.bin.chunk
165 | [+] Extracting hyp_202752.bin to hyp_202752.bin.chunk
166 | [+] Extracting rpm_203776.bin to rpm_203776.bin.chunk
167 | [+] Extracting aboot_204800.bin to aboot_204800.bin.chunk
168 | [+] Extracting sbl1bak_208896.bin to sbl1bak_208896.bin.chunk
169 | [+] Extracting pmicbak_210944.bin to pmicbak_210944.bin.chunk
170 | [+] Extracting hypbak_211968.bin to hypbak_211968.bin.chunk
171 | [+] Extracting tzbak_212992.bin to tzbak_212992.bin.chunk
172 | [+] Extracting rpmbak_215040.bin to rpmbak_215040.bin.chunk
173 | [+] Extracting abootbak_216064.bin to abootbak_216064.bin.chunk
174 | [+] Extracting sdibak_220160.bin to sdibak_220160.bin.chunk
175 | [+] Extracting persist_278528.bin to persist_278528.bin.chunk
176 | [+] Extracting sec_360448.bin to sec_360448.bin.chunk
177 | [+] Extracting rct_363520.bin to rct_363520.bin.chunk
178 | [+] Extracting laf_376832.bin to laf_376832.bin.chunk
179 | [+] Extracting boot_475136.bin to boot_475136.bin.chunk
180 | [+] Extracting recovery_557056.bin to recovery_557056.bin.chunk
181 | [+] Extracting raw_resources_737280.bin to raw_resources_737280.bin.chunk
182 | [+] Extracting raw_resourcesbak_745472.bin to raw_resourcesbak_745472.bin.chunk
183 | [+] Extracting factory_753664.bin to factory_753664.bin.chunk
184 | [+] Extracting system_901120.bin to system_901120.bin.chunk
185 | [+] Extracting system_1165448.bin to system_1165448.bin.chunk
186 | [+] Extracting system_1429456.bin to system_1429456.bin.chunk
187 | [+] Extracting system_1689736.bin to system_1689736.bin.chunk
188 | [+] Extracting system_1693784.bin to system_1693784.bin.chunk
189 | [+] Extracting system_1953744.bin to system_1953744.bin.chunk
190 | [+] Extracting system_2214024.bin to system_2214024.bin.chunk
191 | [+] Extracting system_2218072.bin to system_2218072.bin.chunk
192 | [+] Extracting system_2478032.bin to system_2478032.bin.chunk
193 | [+] Extracting system_2738312.bin to system_2738312.bin.chunk
194 | [+] Extracting system_2742360.bin to system_2742360.bin.chunk
195 | [+] Extracting system_3002320.bin to system_3002320.bin.chunk
196 | [+] Extracting system_3262600.bin to system_3262600.bin.chunk
197 | [+] Extracting system_3266648.bin to system_3266648.bin.chunk
198 | [+] Extracting system_3526608.bin to system_3526608.bin.chunk
199 | [+] Extracting system_3788752.bin to system_3788752.bin.chunk
200 | [+] Extracting system_4050896.bin to system_4050896.bin.chunk
201 | [+] Extracting system_4313040.bin to system_4313040.bin.chunk
202 | [+] Extracting system_4575184.bin to system_4575184.bin.chunk
203 | [+] Extracting system_4837328.bin to system_4837328.bin.chunk
204 | [+] Extracting system_5099472.bin to system_5099472.bin.chunk
205 | [+] Extracting system_5361616.bin to system_5361616.bin.chunk
206 | [+] Extracting system_5623760.bin to system_5623760.bin.chunk
207 | [+] Extracting system_5885904.bin to system_5885904.bin.chunk
208 | [+] Extracting system_6148048.bin to system_6148048.bin.chunk
209 | [+] Extracting system_6410192.bin to system_6410192.bin.chunk
210 | [+] Extracting system_6672336.bin to system_6672336.bin.chunk
211 | [+] Extracting system_6934480.bin to system_6934480.bin.chunk
212 | [+] Extracting system_7196624.bin to system_7196624.bin.chunk
213 | [+] Extracting system_7456904.bin to system_7456904.bin.chunk
214 | [+] Extracting system_7460952.bin to system_7460952.bin.chunk
215 | [+] Extracting system_7720912.bin to system_7720912.bin.chunk
216 | [+] Extracting system_7981192.bin to system_7981192.bin.chunk
217 | [+] Extracting system_7985240.bin to system_7985240.bin.chunk
218 | [+] Extracting system_8245200.bin to system_8245200.bin.chunk
219 | [+] Extracting system_8507344.bin to system_8507344.bin.chunk
220 | [+] Extracting system_8765440.bin to system_8765440.bin.chunk
221 | [+] Extracting system_9027584.bin to system_9027584.bin.chunk
222 | [+] Extracting system_9289728.bin to system_9289728.bin.chunk
223 | [+] Extracting system_9551872.bin to system_9551872.bin.chunk
224 | [+] Extracting system_9554936.bin to system_9554936.bin.chunk
225 | [+] Extracting BackupGPT_122141696.bin to BackupGPT_122141696.bin.chunk
226 | $ rm dzextracted/system_[0-9]*[0-9].bin.chunk
227 | $ undz -f kdzextracted/H90120j_00.dz -s 51
228 | [!] Warning: Chunk is part of "BackupGPT", but starts in front of slice?!
229 | [+] Extracting single slice^Wpartition!
230 |
231 | [+] Extracting system_901120.bin to system.image
232 | [+] Extracting system_1165448.bin to system.image
233 | [+] Extracting system_1429456.bin to system.image
234 | [+] Extracting system_1689736.bin to system.image
235 | [+] Extracting system_1693784.bin to system.image
236 | [+] Extracting system_1953744.bin to system.image
237 | [+] Extracting system_2214024.bin to system.image
238 | [+] Extracting system_2218072.bin to system.image
239 | [+] Extracting system_2478032.bin to system.image
240 | [+] Extracting system_2738312.bin to system.image
241 | [+] Extracting system_2742360.bin to system.image
242 | [+] Extracting system_3002320.bin to system.image
243 | [+] Extracting system_3262600.bin to system.image
244 | [+] Extracting system_3266648.bin to system.image
245 | [+] Extracting system_3526608.bin to system.image
246 | [+] Extracting system_3788752.bin to system.image
247 | [+] Extracting system_4050896.bin to system.image
248 | [+] Extracting system_4313040.bin to system.image
249 | [+] Extracting system_4575184.bin to system.image
250 | [+] Extracting system_4837328.bin to system.image
251 | [+] Extracting system_5099472.bin to system.image
252 | [+] Extracting system_5361616.bin to system.image
253 | [+] Extracting system_5623760.bin to system.image
254 | [+] Extracting system_5885904.bin to system.image
255 | [+] Extracting system_6148048.bin to system.image
256 | [+] Extracting system_6410192.bin to system.image
257 | [+] Extracting system_6672336.bin to system.image
258 | [+] Extracting system_6934480.bin to system.image
259 | [+] Extracting system_7196624.bin to system.image
260 | [+] Extracting system_7456904.bin to system.image
261 | [+] Extracting system_7460952.bin to system.image
262 | [+] Extracting system_7720912.bin to system.image
263 | [+] Extracting system_7981192.bin to system.image
264 | [+] Extracting system_7985240.bin to system.image
265 | [+] Extracting system_8245200.bin to system.image
266 | [+] Extracting system_8507344.bin to system.image
267 | [+] Extracting system_8765440.bin to system.image
268 | [+] Extracting system_9027584.bin to system.image
269 | [+] Extracting system_9289728.bin to system.image
270 | [+] Extracting system_9551872.bin to system.image
271 | [+] Extracting system_9554936.bin to system.image
272 | $
273 |
274 | The theory was at this point you can modify out/system.image to your liking.
275 | For this particular device (LG H901), system.image is an EXT4 filesystem. Many
276 | tools exist for modifying EXT4 filesystems and these should work fine.
277 |
278 | The next step would be reconstructing the file. There are three steps, turning
279 | the files into chunks, merging them together into a DZ file, and then merging
280 | everything back into a KDZ file. The first step has some quirks.
281 |
282 | "image2chunks.py" currently has 3 strategies for breaking image files into
283 | chunks. At of this writing the prefered strategy is to make use of ext2simg
284 | from the Android image utilities. This produces results that differ somewhat
285 | from LG's images, but is believed to produce sane results. The differences
286 | have me wondering whether LG's images are either unsafe, or else relying on
287 | special knowledge of the hardware (is the eMMC certain to give back zero blocks
288 | for TRIMmed areas?).
289 |
290 | The next two strategies are utilizing support for SEEK_DATA and SEEK_HOLE, or
291 | probing for the presence of holes. Operating System support for
292 | SEEK_DATA/SEEK_HOLE is decent, though to my knowledge no versions of Windows
293 | include this. This seems a bit of a cheat since it is relying on knowledge of
294 | which areas of the image haven't been written to. Probing marks an awful lot
295 | of areas as holes, which leaves me uncomfortable believing the results to be
296 | sane. As such I reccommend the first if available (ext2simg is known to work
297 | for Linux, but I'm unsure Windows binaries are available).
298 |
299 | WARNING: It has been found there is some additional unknown verification
300 | mechanism in LGE's tools. Due to this mechanism currently the generated KDZ
301 | files haven't been shown to work. There are some guesses as to where the
302 | mechanism is, but as of now not enough is known to work around it.
303 |
304 | $ image2chunks --ext4 dzextracted/system.image
305 | [+] Compressing system.image to system_901120.bin (0 empty blocks)
306 | [+] Compressing system.image to system_1163264.bin (8 empty blocks)
307 | [+] Compressing system.image to system_1425408.bin (8 empty blocks)
308 | [+] Compressing system.image to system_1687552.bin (8 empty blocks)
309 | [+] Compressing system.image to system_1949696.bin (8 empty blocks)
310 | [+] Compressing system.image to system_2211840.bin (8 empty blocks)
311 | [+] Compressing system.image to system_2473984.bin (8 empty blocks)
312 | [+] Compressing system.image to system_2736128.bin (8 empty blocks)
313 | [+] Compressing system.image to system_2998272.bin (8 empty blocks)
314 | [+] Compressing system.image to system_3260416.bin (8 empty blocks)
315 | [+] Compressing system.image to system_3522560.bin (8 empty blocks)
316 | [+] Compressing system.image to system_3784704.bin (8 empty blocks)
317 | [+] Compressing system.image to system_4046848.bin (8 empty blocks)
318 | [+] Compressing system.image to system_4308992.bin (8 empty blocks)
319 | [+] Compressing system.image to system_4571136.bin (8 empty blocks)
320 | [+] Compressing system.image to system_4833280.bin (8 empty blocks)
321 | [+] Compressing system.image to system_5095424.bin (8 empty blocks)
322 | [+] Compressing system.image to system_5357568.bin (8 empty blocks)
323 | [+] Compressing system.image to system_5619712.bin (8 empty blocks)
324 | [+] Compressing system.image to system_5881856.bin (8 empty blocks)
325 | [+] Compressing system.image to system_6144000.bin (8 empty blocks)
326 | [+] Compressing system.image to system_6406144.bin (8 empty blocks)
327 | [+] Compressing system.image to system_6668288.bin (8 empty blocks)
328 | [+] Compressing system.image to system_6930432.bin (8 empty blocks)
329 | [+] Compressing system.image to system_7192576.bin (8 empty blocks)
330 | [+] Compressing system.image to system_7454720.bin (8 empty blocks)
331 | [+] Compressing system.image to system_7716864.bin (8 empty blocks)
332 | [+] Compressing system.image to system_7979008.bin (8 empty blocks)
333 | [+] Compressing system.image to system_8241152.bin (8 empty blocks)
334 | [+] Compressing system.image to system_8503296.bin (150056 empty blocks)
335 | [+] Compressing system.image to system_8765440.bin (258096 empty blocks)
336 | [+] Compressing system.image to system_9027584.bin (258096 empty blocks)
337 | [+] Compressing system.image to system_9289728.bin (258096 empty blocks)
338 | [+] Compressing system.image to system_9551872.bin (8 empty blocks)
339 | [+] done
340 |
341 | $ mkdz -f kdzextracted/H90120j_00.dz -m
342 | [+] Writing 60 chunks to H90120j_00.dz:
343 |
344 | [+] Writing PrimaryGPT_0.bin.chunk to H90120j_00.dz (5711 bytes)
345 | [+] Writing modem_16384.bin.chunk to H90120j_00.dz (42902812 bytes)
346 | [+] Writing pmic_196608.bin.chunk to H90120j_00.dz (11577 bytes)
347 | [+] Writing sbl1_197632.bin.chunk to H90120j_00.dz (290885 bytes)
348 | [+] Writing tz_199680.bin.chunk to H90120j_00.dz (258641 bytes)
349 | [+] Writing sdi_201728.bin.chunk to H90120j_00.dz (15196 bytes)
350 | [+] Writing hyp_202752.bin.chunk to H90120j_00.dz (24189 bytes)
351 | [+] Writing rpm_203776.bin.chunk to H90120j_00.dz (111777 bytes)
352 | [+] Writing aboot_204800.bin.chunk to H90120j_00.dz (407879 bytes)
353 | [+] Writing sbl1bak_208896.bin.chunk to H90120j_00.dz (290885 bytes)
354 | [+] Writing pmicbak_210944.bin.chunk to H90120j_00.dz (11577 bytes)
355 | [+] Writing hypbak_211968.bin.chunk to H90120j_00.dz (24189 bytes)
356 | [+] Writing tzbak_212992.bin.chunk to H90120j_00.dz (258641 bytes)
357 | [+] Writing rpmbak_215040.bin.chunk to H90120j_00.dz (111777 bytes)
358 | [+] Writing abootbak_216064.bin.chunk to H90120j_00.dz (407879 bytes)
359 | [+] Writing sdibak_220160.bin.chunk to H90120j_00.dz (15196 bytes)
360 | [+] Writing persist_278528.bin.chunk to H90120j_00.dz (23897 bytes)
361 | [+] Writing sec_360448.bin.chunk to H90120j_00.dz (3115 bytes)
362 | [+] Writing rct_363520.bin.chunk to H90120j_00.dz (2829 bytes)
363 | [+] Writing laf_376832.bin.chunk to H90120j_00.dz (19533804 bytes)
364 | [+] Writing boot_475136.bin.chunk to H90120j_00.dz (16694661 bytes)
365 | [+] Writing recovery_557056.bin.chunk to H90120j_00.dz (17891899 bytes)
366 | [+] Writing raw_resources_737280.bin.chunk to H90120j_00.dz (888811 bytes)
367 | [+] Writing raw_resourcesbak_745472.bin.chunk to H90120j_00.dz (888811 bytes)
368 | [+] Writing factory_753664.bin.chunk to H90120j_00.dz (2829 bytes)
369 | [+] Writing system_901120.bin.chunk to H90120j_00.dz (25368238 bytes)
370 | [+] Writing system_1163264.bin.chunk to H90120j_00.dz (60662497 bytes)
371 | [+] Writing system_1425408.bin.chunk to H90120j_00.dz (65117233 bytes)
372 | [+] Writing system_1687552.bin.chunk to H90120j_00.dz (52590332 bytes)
373 | [+] Writing system_1949696.bin.chunk to H90120j_00.dz (64116886 bytes)
374 | [+] Writing system_2211840.bin.chunk to H90120j_00.dz (75176824 bytes)
375 | [+] Writing system_2473984.bin.chunk to H90120j_00.dz (56952617 bytes)
376 | [+] Writing system_2736128.bin.chunk to H90120j_00.dz (68318938 bytes)
377 | [+] Writing system_2998272.bin.chunk to H90120j_00.dz (54652864 bytes)
378 | [+] Writing system_3260416.bin.chunk to H90120j_00.dz (59959919 bytes)
379 | [+] Writing system_3522560.bin.chunk to H90120j_00.dz (66803716 bytes)
380 | [+] Writing system_3784704.bin.chunk to H90120j_00.dz (61048842 bytes)
381 | [+] Writing system_4046848.bin.chunk to H90120j_00.dz (88113814 bytes)
382 | [+] Writing system_4308992.bin.chunk to H90120j_00.dz (57314201 bytes)
383 | [+] Writing system_4571136.bin.chunk to H90120j_00.dz (59818074 bytes)
384 | [+] Writing system_4833280.bin.chunk to H90120j_00.dz (53953971 bytes)
385 | [+] Writing system_5095424.bin.chunk to H90120j_00.dz (62896913 bytes)
386 | [+] Writing system_5357568.bin.chunk to H90120j_00.dz (82167175 bytes)
387 | [+] Writing system_5619712.bin.chunk to H90120j_00.dz (68191722 bytes)
388 | [+] Writing system_5881856.bin.chunk to H90120j_00.dz (60612219 bytes)
389 | [+] Writing system_6144000.bin.chunk to H90120j_00.dz (80457934 bytes)
390 | [+] Writing system_6406144.bin.chunk to H90120j_00.dz (73042775 bytes)
391 | [+] Writing system_6668288.bin.chunk to H90120j_00.dz (59107610 bytes)
392 | [+] Writing system_6930432.bin.chunk to H90120j_00.dz (61329475 bytes)
393 | [+] Writing system_7192576.bin.chunk to H90120j_00.dz (58290368 bytes)
394 | [+] Writing system_7454720.bin.chunk to H90120j_00.dz (55970470 bytes)
395 | [+] Writing system_7716864.bin.chunk to H90120j_00.dz (64312923 bytes)
396 | [+] Writing system_7979008.bin.chunk to H90120j_00.dz (66396113 bytes)
397 | [+] Writing system_8241152.bin.chunk to H90120j_00.dz (62351646 bytes)
398 | [+] Writing system_8503296.bin.chunk to H90120j_00.dz (26438033 bytes)
399 | [+] Writing system_8765440.bin.chunk to H90120j_00.dz (9577 bytes)
400 | [+] Writing system_9027584.bin.chunk to H90120j_00.dz (9577 bytes)
401 | [+] Writing system_9289728.bin.chunk to H90120j_00.dz (9577 bytes)
402 | [+] Writing system_9551872.bin.chunk to H90120j_00.dz (35643326 bytes)
403 | [+] Writing BackupGPT_122141696.bin.chunk to H90120j_00.dz (5686 bytes)
404 | $ mkkdz -f myH901_20j.kdz -m
405 | [+] Writing LGUP_c.dll to output file myH901_20j.kdz
406 | [+] Writing LGUP_c.dylib to output file myH901_20j.kdz
407 | [+] Writing H90120j_00.dz to output file myH901_20j.kdz
408 |
409 | [+] Writing headers to myH901_20j.kdz
410 | [+] Done!
411 | $
412 |
413 | At this point myH901_20j.kdz should be installable using LG's tools. If the
414 | image was unmodified, and image2chunks.py was used with its --sparse option,
415 | it should be identical to the original file. This second statement relies on
416 | behavior of the OS and may not work precisely on all systems. Exactly
417 | identical files have only been shown on Linux with system.image being unpacked
418 | onto a EXT4 FS (dzextracted being on EXT4). EXT2/3 will also work if mounted
419 | using the EXT4's backwards compatibility mode, rather than the native
420 | implementation of EXT2/3. Most other flavors of Unix should get sane output,
421 | but not as likely to be identical.
422 |
423 | There is a value in the chunk headers referred to as "trimCount" in the code,
424 | as well as in the .params files (these are simply text files) generated for
425 | extracted slices. My suspicion is this is this is a count of blocks to be
426 | TRIMed prior to writing the data from the DZ file. There are some oddities
427 | though, notably several slices/partitions are marked as being wiped by a
428 | large value on the prior chunk. I'm unsure what this means, perhaps LG's tools
429 | ignore super large wipes.
430 |
431 | There is also the quirk of the backup GPT's chunk extending near the front of
432 | the "grow" slice/partition. My suspicion is this is a weakness of the tools LG
433 | is using to generate the files. Perhaps they don't understand the concept of
434 | chunks which don't start at a slice/partition boundary, they also may not
435 | correctly handle the case of chunks which *begin* with a hole.
436 |
437 | Lastly, I'm concerned about the number of unknowns in the DZ header. Several
438 | look to be harmless to simply copy the value from an existing file, but others
439 | are totally unknown to me at this time. Two fields look to be date codes of
440 | some flavor (easy, simply copy). I worry more of these need to be regenerated,
441 | but I've got no idea what to put in new files.
442 |
443 | WARNING: As of this writing it appears the fear of the unknowns was valid.
444 | According to reports it appears there is an additional verification mechanism
445 | which needs to be worked around. At the moment I'm guessing "unknown1" (saved
446 | in dzextracted/.dz.params) is a MD5 of some portion of the image.
447 | Unfortunately without knowing which portion, it cannot be adjusted. Similarly
448 | "unknown3" could be a CRC-32 of the same area. My fear is "unknown1" could be
449 | a keyed hash at which point fixing is impossible unless we discover what/where
450 | the key is.
451 |
452 | One piece of good news is I'm pretty confident unpacking mostly works
453 | correctly. The one quirk is newer LGE devices (the G5) the DZ format is
454 | somewhat modified and unpacking doesn't yet meet my expectations.
455 |
456 |
457 | -------------------------------------------
458 |
459 | Permission was granted to rerelease this software as long as attribution was
460 | given. This version is distributed under the terms of the GNU Public License
461 | version 3.
462 |
463 | -------------------------------------------
464 |
465 |
--------------------------------------------------------------------------------
/README_ozip.md:
--------------------------------------------------------------------------------
1 | - ozip2zip
2 | Convert Oppo ozip firmware file to zip files
3 |
4 |
5 | Welcome to Androidiya - Please Subscribe if you haven't
6 |
7 | Please subscribe for more https://www.youtube.com/channel/UC0dMbs2KSS5GvBMRoyQZ4Ow/featured?disable_polymer=1
8 |
9 | I am Tahir, I just found this code online which worked for me. I spend several housr to find a solution to convert Oppo firmware from ozip ro zip file b ut only this code worked for me on Linux. I didn't write this code and I don't know how wrote this code. I am just sharing it because it works.
10 |
11 | I was able to convert Oppo F5 firmware to zip file with this.
12 |
13 | **How to use it**
14 |
15 | Open terminal and use thse commands to Install Python
16 | ```
17 | sudo apt install python3
18 | sudo apt install python3-pip
19 | pip3 install pycrypto
20 | ```
21 | Now create a new directory and move .ozip firmware file, ozipdecrypt.py, and ofp_libextract.py in this same folder.
22 |
23 | Run
24 | ```
25 | ./ozipdecrypt.py *.ozip
26 | ```
27 | or
28 | ```
29 | ./ozipdecrypt.py firmwarefilename.ozip
30 | ```
31 | It will take a minute ot two to convert, depends on the file size.
32 |
33 | **With this code you can convert**
34 |
35 | Oppo,Realme
36 |
37 | [ stock recovery ozip --> custom recovery flashable to zip ]
38 |
39 | Supported devices list for .ozip to .zip
40 |
41 | OPPO:-
42 | - A77
43 | - R11
44 | - R11s
45 | - R11s Plus
46 | - R9s
47 | - R9s Plus
48 | - FindX
49 | - FindX
50 | - K1
51 | - Reno
52 | - K3
53 | - A9
54 | - Reno 10x zoom PCCM00
55 | - A1
56 | - A83t
57 | - R17 Pro
58 |
59 | REALME:-
60 | - Realme 2 = Rename the .ozip file to .zip and flash via recovery
61 | - Realme C1 = Rename the .ozip file to .zip and flash via recovery
62 | - Realme 1
63 | - Realme C2
64 | - Realme 2 pro
65 | - Realme U1 RMX1831
66 | - Realme 3 RMX1825EX
67 | - Realme 3 Pro
68 | - Realme X
69 | - Realme X2
70 | - Realme 5
71 | - Realme 5 = Rename the .ozip file to .zip and flash via recovery
72 |
--------------------------------------------------------------------------------
/README_simg2img.txt:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # FILE: simg2img.py
5 | #
6 | # USAGE: ./simg2img.py system.img
7 | #
8 | # DESCRIPTION:
9 | #
10 | # AUTHOR: Karl Zheng
11 | # COMPANY: Meizu
12 | # CREATED: 2011年10月18日 15时25分15秒 CST
13 | # REVISION: ---
14 | # MODIFY: AEnjoy
15 | #===============================================================================
16 | GNU GENERAL PUBLIC LICENSE
17 | Version 2, June 1991
18 |
19 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 | Everyone is permitted to copy and distribute verbatim copies
22 | of this license document, but changing it is not allowed.
23 |
24 | Preamble
25 |
26 | The licenses for most software are designed to take away your
27 | freedom to share and change it. By contrast, the GNU General Public
28 | License is intended to guarantee your freedom to share and change free
29 | software--to make sure the software is free for all its users. This
30 | General Public License applies to most of the Free Software
31 | Foundation's software and to any other program whose authors commit to
32 | using it. (Some other Free Software Foundation software is covered by
33 | the GNU Lesser General Public License instead.) You can apply it to
34 | your programs, too.
35 |
36 | When we speak of free software, we are referring to freedom, not
37 | price. Our General Public Licenses are designed to make sure that you
38 | have the freedom to distribute copies of free software (and charge for
39 | this service if you wish), that you receive source code or can get it
40 | if you want it, that you can change the software or use pieces of it
41 | in new free programs; and that you know you can do these things.
42 |
43 | To protect your rights, we need to make restrictions that forbid
44 | anyone to deny you these rights or to ask you to surrender the rights.
45 | These restrictions translate to certain responsibilities for you if you
46 | distribute copies of the software, or if you modify it.
47 |
48 | For example, if you distribute copies of such a program, whether
49 | gratis or for a fee, you must give the recipients all the rights that
50 | you have. You must make sure that they, too, receive or can get the
51 | source code. And you must show them these terms so they know their
52 | rights.
53 |
54 | We protect your rights with two steps: (1) copyright the software, and
55 | (2) offer you this license which gives you legal permission to copy,
56 | distribute and/or modify the software.
57 |
58 | Also, for each author's protection and ours, we want to make certain
59 | that everyone understands that there is no warranty for this free
60 | software. If the software is modified by someone else and passed on, we
61 | want its recipients to know that what they have is not the original, so
62 | that any problems introduced by others will not reflect on the original
63 | authors' reputations.
64 |
65 | Finally, any free program is threatened constantly by software
66 | patents. We wish to avoid the danger that redistributors of a free
67 | program will individually obtain patent licenses, in effect making the
68 | program proprietary. To prevent this, we have made it clear that any
69 | patent must be licensed for everyone's free use or not licensed at all.
70 |
71 | The precise terms and conditions for copying, distribution and
72 | modification follow.
73 |
74 | GNU GENERAL PUBLIC LICENSE
75 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
76 |
77 | 0. This License applies to any program or other work which contains
78 | a notice placed by the copyright holder saying it may be distributed
79 | under the terms of this General Public License. The "Program", below,
80 | refers to any such program or work, and a "work based on the Program"
81 | means either the Program or any derivative work under copyright law:
82 | that is to say, a work containing the Program or a portion of it,
83 | either verbatim or with modifications and/or translated into another
84 | language. (Hereinafter, translation is included without limitation in
85 | the term "modification".) Each licensee is addressed as "you".
86 |
87 | Activities other than copying, distribution and modification are not
88 | covered by this License; they are outside its scope. The act of
89 | running the Program is not restricted, and the output from the Program
90 | is covered only if its contents constitute a work based on the
91 | Program (independent of having been made by running the Program).
92 | Whether that is true depends on what the Program does.
93 |
94 | 1. You may copy and distribute verbatim copies of the Program's
95 | source code as you receive it, in any medium, provided that you
96 | conspicuously and appropriately publish on each copy an appropriate
97 | copyright notice and disclaimer of warranty; keep intact all the
98 | notices that refer to this License and to the absence of any warranty;
99 | and give any other recipients of the Program a copy of this License
100 | along with the Program.
101 |
102 | You may charge a fee for the physical act of transferring a copy, and
103 | you may at your option offer warranty protection in exchange for a fee.
104 |
105 | 2. You may modify your copy or copies of the Program or any portion
106 | of it, thus forming a work based on the Program, and copy and
107 | distribute such modifications or work under the terms of Section 1
108 | above, provided that you also meet all of these conditions:
109 |
110 | a) You must cause the modified files to carry prominent notices
111 | stating that you changed the files and the date of any change.
112 |
113 | b) You must cause any work that you distribute or publish, that in
114 | whole or in part contains or is derived from the Program or any
115 | part thereof, to be licensed as a whole at no charge to all third
116 | parties under the terms of this License.
117 |
118 | c) If the modified program normally reads commands interactively
119 | when run, you must cause it, when started running for such
120 | interactive use in the most ordinary way, to print or display an
121 | announcement including an appropriate copyright notice and a
122 | notice that there is no warranty (or else, saying that you provide
123 | a warranty) and that users may redistribute the program under
124 | these conditions, and telling the user how to view a copy of this
125 | License. (Exception: if the Program itself is interactive but
126 | does not normally print such an announcement, your work based on
127 | the Program is not required to print an announcement.)
128 |
129 | These requirements apply to the modified work as a whole. If
130 | identifiable sections of that work are not derived from the Program,
131 | and can be reasonably considered independent and separate works in
132 | themselves, then this License, and its terms, do not apply to those
133 | sections when you distribute them as separate works. But when you
134 | distribute the same sections as part of a whole which is a work based
135 | on the Program, the distribution of the whole must be on the terms of
136 | this License, whose permissions for other licensees extend to the
137 | entire whole, and thus to each and every part regardless of who wrote it.
138 |
139 | Thus, it is not the intent of this section to claim rights or contest
140 | your rights to work written entirely by you; rather, the intent is to
141 | exercise the right to control the distribution of derivative or
142 | collective works based on the Program.
143 |
144 | In addition, mere aggregation of another work not based on the Program
145 | with the Program (or with a work based on the Program) on a volume of
146 | a storage or distribution medium does not bring the other work under
147 | the scope of this License.
148 |
149 | 3. You may copy and distribute the Program (or a work based on it,
150 | under Section 2) in object code or executable form under the terms of
151 | Sections 1 and 2 above provided that you also do one of the following:
152 |
153 | a) Accompany it with the complete corresponding machine-readable
154 | source code, which must be distributed under the terms of Sections
155 | 1 and 2 above on a medium customarily used for software interchange; or,
156 |
157 | b) Accompany it with a written offer, valid for at least three
158 | years, to give any third party, for a charge no more than your
159 | cost of physically performing source distribution, a complete
160 | machine-readable copy of the corresponding source code, to be
161 | distributed under the terms of Sections 1 and 2 above on a medium
162 | customarily used for software interchange; or,
163 |
164 | c) Accompany it with the information you received as to the offer
165 | to distribute corresponding source code. (This alternative is
166 | allowed only for noncommercial distribution and only if you
167 | received the program in object code or executable form with such
168 | an offer, in accord with Subsection b above.)
169 |
170 | The source code for a work means the preferred form of the work for
171 | making modifications to it. For an executable work, complete source
172 | code means all the source code for all modules it contains, plus any
173 | associated interface definition files, plus the scripts used to
174 | control compilation and installation of the executable. However, as a
175 | special exception, the source code distributed need not include
176 | anything that is normally distributed (in either source or binary
177 | form) with the major components (compiler, kernel, and so on) of the
178 | operating system on which the executable runs, unless that component
179 | itself accompanies the executable.
180 |
181 | If distribution of executable or object code is made by offering
182 | access to copy from a designated place, then offering equivalent
183 | access to copy the source code from the same place counts as
184 | distribution of the source code, even though third parties are not
185 | compelled to copy the source along with the object code.
186 |
187 | 4. You may not copy, modify, sublicense, or distribute the Program
188 | except as expressly provided under this License. Any attempt
189 | otherwise to copy, modify, sublicense or distribute the Program is
190 | void, and will automatically terminate your rights under this License.
191 | However, parties who have received copies, or rights, from you under
192 | this License will not have their licenses terminated so long as such
193 | parties remain in full compliance.
194 |
195 | 5. You are not required to accept this License, since you have not
196 | signed it. However, nothing else grants you permission to modify or
197 | distribute the Program or its derivative works. These actions are
198 | prohibited by law if you do not accept this License. Therefore, by
199 | modifying or distributing the Program (or any work based on the
200 | Program), you indicate your acceptance of this License to do so, and
201 | all its terms and conditions for copying, distributing or modifying
202 | the Program or works based on it.
203 |
204 | 6. Each time you redistribute the Program (or any work based on the
205 | Program), the recipient automatically receives a license from the
206 | original licensor to copy, distribute or modify the Program subject to
207 | these terms and conditions. You may not impose any further
208 | restrictions on the recipients' exercise of the rights granted herein.
209 | You are not responsible for enforcing compliance by third parties to
210 | this License.
211 |
212 | 7. If, as a consequence of a court judgment or allegation of patent
213 | infringement or for any other reason (not limited to patent issues),
214 | conditions are imposed on you (whether by court order, agreement or
215 | otherwise) that contradict the conditions of this License, they do not
216 | excuse you from the conditions of this License. If you cannot
217 | distribute so as to satisfy simultaneously your obligations under this
218 | License and any other pertinent obligations, then as a consequence you
219 | may not distribute the Program at all. For example, if a patent
220 | license would not permit royalty-free redistribution of the Program by
221 | all those who receive copies directly or indirectly through you, then
222 | the only way you could satisfy both it and this License would be to
223 | refrain entirely from distribution of the Program.
224 |
225 | If any portion of this section is held invalid or unenforceable under
226 | any particular circumstance, the balance of the section is intended to
227 | apply and the section as a whole is intended to apply in other
228 | circumstances.
229 |
230 | It is not the purpose of this section to induce you to infringe any
231 | patents or other property right claims or to contest validity of any
232 | such claims; this section has the sole purpose of protecting the
233 | integrity of the free software distribution system, which is
234 | implemented by public license practices. Many people have made
235 | generous contributions to the wide range of software distributed
236 | through that system in reliance on consistent application of that
237 | system; it is up to the author/donor to decide if he or she is willing
238 | to distribute software through any other system and a licensee cannot
239 | impose that choice.
240 |
241 | This section is intended to make thoroughly clear what is believed to
242 | be a consequence of the rest of this License.
243 |
244 | 8. If the distribution and/or use of the Program is restricted in
245 | certain countries either by patents or by copyrighted interfaces, the
246 | original copyright holder who places the Program under this License
247 | may add an explicit geographical distribution limitation excluding
248 | those countries, so that distribution is permitted only in or among
249 | countries not thus excluded. In such case, this License incorporates
250 | the limitation as if written in the body of this License.
251 |
252 | 9. The Free Software Foundation may publish revised and/or new versions
253 | of the General Public License from time to time. Such new versions will
254 | be similar in spirit to the present version, but may differ in detail to
255 | address new problems or concerns.
256 |
257 | Each version is given a distinguishing version number. If the Program
258 | specifies a version number of this License which applies to it and "any
259 | later version", you have the option of following the terms and conditions
260 | either of that version or of any later version published by the Free
261 | Software Foundation. If the Program does not specify a version number of
262 | this License, you may choose any version ever published by the Free Software
263 | Foundation.
264 |
265 | 10. If you wish to incorporate parts of the Program into other free
266 | programs whose distribution conditions are different, write to the author
267 | to ask for permission. For software which is copyrighted by the Free
268 | Software Foundation, write to the Free Software Foundation; we sometimes
269 | make exceptions for this. Our decision will be guided by the two goals
270 | of preserving the free status of all derivatives of our free software and
271 | of promoting the sharing and reuse of software generally.
272 |
273 | NO WARRANTY
274 |
275 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
276 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
277 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
278 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
279 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
280 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
281 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
282 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
283 | REPAIR OR CORRECTION.
284 |
285 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
286 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
287 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
288 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
289 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
290 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
291 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
292 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
293 | POSSIBILITY OF SUCH DAMAGES.
294 |
295 | END OF TERMS AND CONDITIONS
296 |
297 | How to Apply These Terms to Your New Programs
298 |
299 | If you develop a new program, and you want it to be of the greatest
300 | possible use to the public, the best way to achieve this is to make it
301 | free software which everyone can redistribute and change under these terms.
302 |
303 | To do so, attach the following notices to the program. It is safest
304 | to attach them to the start of each source file to most effectively
305 | convey the exclusion of warranty; and each file should have at least
306 | the "copyright" line and a pointer to where the full notice is found.
307 |
308 |
309 | Copyright (C)
310 |
311 | This program is free software; you can redistribute it and/or modify
312 | it under the terms of the GNU General Public License as published by
313 | the Free Software Foundation; either version 2 of the License, or
314 | (at your option) any later version.
315 |
316 | This program is distributed in the hope that it will be useful,
317 | but WITHOUT ANY WARRANTY; without even the implied warranty of
318 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
319 | GNU General Public License for more details.
320 |
321 | You should have received a copy of the GNU General Public License along
322 | with this program; if not, write to the Free Software Foundation, Inc.,
323 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
324 |
325 | Also add information on how to contact you by electronic and paper mail.
326 |
327 | If the program is interactive, make it output a short notice like this
328 | when it starts in an interactive mode:
329 |
330 | Gnomovision version 69, Copyright (C) year name of author
331 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
332 | This is free software, and you are welcome to redistribute it
333 | under certain conditions; type `show c' for details.
334 |
335 | The hypothetical commands `show w' and `show c' should show the appropriate
336 | parts of the General Public License. Of course, the commands you use may
337 | be called something other than `show w' and `show c'; they could even be
338 | mouse-clicks or menu items--whatever suits your program.
339 |
340 | You should also get your employer (if you work as a programmer) or your
341 | school, if any, to sign a "copyright disclaimer" for the program, if
342 | necessary. Here is a sample; alter the names:
343 |
344 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
345 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
346 |
347 | , 1 April 1989
348 | Ty Coon, President of Vice
349 |
350 | This General Public License does not permit incorporating your program into
351 | proprietary programs. If your program is a subroutine library, you may
352 | consider it more useful to permit linking proprietary applications with the
353 | library. If this is what you want to do, use the GNU Lesser General
354 | Public License instead of this License.
--------------------------------------------------------------------------------
/README_unpayload.md:
--------------------------------------------------------------------------------
1 | # extract_android_ota_payload.py
2 |
3 | Extract Android firmware images from an OTA payload.bin file.
4 |
5 | With the introduction of the A/B system update, the OTA file format changed.
6 | This tool allows to extract and decompress the firmware images packed using the 'brillo' toolset.
7 |
8 | Incremental firmware images are not supported (source_copy, source_bsdiff operations).
9 |
10 | ## Usage
11 |
12 | ```
13 | $ extract_android_ota_payload.py [target_dir]
14 | : file extracted from the OTA zip file or the OTA zip file
15 | : output directory for the extracted file
16 | ```
17 |
18 | ## Example
19 |
20 | ```
21 | $ python extract_android_ota_payload.py marlin-ota-opm4.171019.021.d1-fd6998a5.zip /tmp/
22 | Extracting 'boot.img'
23 | Extracting 'system.img'
24 | Extracting 'vendor.img'
25 | ...
26 | Extracting 'modem.img'
27 | ```
28 |
29 | ## Dependencies
30 |
31 | ```
32 | python-protobuf
33 | ```
34 |
--------------------------------------------------------------------------------
/about_pycrypto.md:
--------------------------------------------------------------------------------
1 | # pycrypto依赖可能遇到的安装问题
2 |
3 | ## 1.error: Microsoft Visual C++ 14.0 is required. Get it with "Build Tools for Visual Studio"
4 |
5 | 下载Build Tools for Visual Studio:
6 |
7 | https://visualstudio.microsoft.com/downloads/
8 |
9 | ## 2.winrand.c ... ..\pyconfig.h(59): fatal error C1083: 无法打开包括文件: “io.h”: No such file or directory
10 |
11 | 安装完整Visual Studio,选择c/c++桌面开发
12 |
13 | ## 3.其它问题
14 |
15 | ### 1.提交issue给我
16 |
17 | ### 2.安装pycryptodome
18 |
19 | ### 3.不能定义Crypto?No Module named Crypto
20 |
21 | > #### 找到你的python包的安装目录,
22 | >
23 | > #### lib\site-packages把crypto改为Crypto
24 |
25 | # 替代方案
26 |
27 | 安装pycryptodome
28 |
29 | 打开requirements.txt,将pycrypto更改为pycryptodome
30 |
31 |
--------------------------------------------------------------------------------
/clean_cache.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import os,platform
4 | print('开始清理缓存')
5 | os.rmdir('__pycache__')
6 | if platform.system()=='Windows':
7 | os.system('rd /s /q META-INF output rom system system_')
8 | os.system('del /f /s /q *.img payload.bin system.new.dat.br system.transfer.list system.new.dat')
9 | if platform.system()=='Linux':
10 | os.system('rm -rf META-INF output rom system system_')
11 | os.system('rm -rf *.img ')
12 | os.system('rm payload.bin system.new.dat.br system.transfer.list system.new.dat')
--------------------------------------------------------------------------------
/dz.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | """
4 | Copyright (C) 2016 Elliott Mitchell
5 | Copyright (C) 2013 IOMonster (thecubed on XDA)
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program. If not, see .
19 | """
20 |
21 | from __future__ import print_function
22 | import sys
23 | from struct import Struct
24 | from collections import OrderedDict
25 |
26 |
27 | class DZStruct(object):
28 | """
29 | Common class for DZ file structures
30 | """
31 |
32 | # Length of the headers in DZ files
33 | _dz_length = 512
34 |
35 | def __init__(self, classy):
36 | """
37 | Common initializations for all DZ structures
38 | """
39 |
40 |
41 | # Generate the struct for .unpack()
42 | try:
43 | classy._dzstruct
44 |
45 | except AttributeError:
46 | classy._dz_struct = Struct("<" + "".join([x[0] for x in classy._dz_format_dict.values()]))
47 |
48 | # Sanity check
49 | if self._dz_struct.size != self._dz_length:
50 | print("[!] Internal error! Chunk format wrong! (computed={:d}, specified={:d})".format(self._dz_struct.size, self._dz_length), file=sys.stderr)
51 | sys.exit(-1)
52 |
53 | # Generate list of items that can be collapsed (truncated)
54 | try:
55 | classy._dz_collapsibles
56 |
57 | except AttributeError:
58 | classy._dz_collapsibles = [n for n, (y, p) in classy._dz_format_dict.items() if p]
59 |
60 |
61 | def packdict(self, din):
62 | """
63 | Pack all the fields from the dict into a returned buffer
64 | """
65 |
66 | dout = dict()
67 |
68 | # pad any string keys that need padding
69 | for k in self._dz_format_dict.keys():
70 | if self._dz_format_dict[k][0][-1] == 's':
71 | l = int(self._dz_format_dict[k][0][:-1])
72 | dout[k] = (din[k] if k in din else b"").ljust(l, b'\x00')
73 | elif not k in din and k in self._dz_collapsibles:
74 | dout[k] = 0
75 | else:
76 | dout[k] = din[k]
77 |
78 | dout['header'] = self._dz_header
79 |
80 | values = [dout[k] for k in self._dz_format_dict.keys()]
81 | buffer = self._dz_struct.pack(*values)
82 |
83 | return buffer
84 |
85 |
86 | def unpackdict(self, buffer):
87 | """
88 | Unpack data in buffer into a returned dictionary, return None
89 | if magic number/header is absent
90 | """
91 |
92 | d = dict(zip(
93 | self._dz_format_dict.keys(),
94 | self._dz_struct.unpack(buffer)
95 | ))
96 |
97 | if d['header'] != self._dz_header:
98 | return None
99 |
100 | return d
101 |
102 |
103 |
104 | class DZChunk(DZStruct):
105 | """
106 | Representation of an individual file chunk from a LGE DZ file
107 | """
108 |
109 | _dz_area = "chunk"
110 | _dz_header = b"\x30\x12\x95\x78"
111 |
112 | # Format string dict
113 | # itemName is the new dict key for the data to be stored under
114 | # formatString is the Python formatstring for struct.unpack()
115 | # collapse: boolean that controls whether extra \x00 's should be stripped
116 | # for integer types collapse set to True means that the value should always be zero
117 | # Example:
118 | # ('itemName', ('formatString', collapse))
119 | _dz_format_dict = OrderedDict([
120 | ('header', ('4s', False)), # magic number
121 | ('sliceName', ('32s', True)), # name of our slice
122 | ('chunkName', ('64s', True)), # name of our chunk
123 | ('targetSize', ('I', False)), # bytes of target area
124 | ('dataSize', ('I', False)), # amount of compressed
125 | ('md5', ('16s', False)), # MD5 of target image
126 | ('targetAddr', ('I', False)), # first block to write
127 | ('trimCount', ('I', False)), # blocks to TRIM before
128 | ('dev', ('I', False)), # flash device Id
129 | ('crc32', ('I', False)), # CRC32 of target image
130 | ('pad', ('372s', True)), # currently always zero
131 | ])
132 |
133 | def __init__(self):
134 | """
135 | Initializer for DZChunk, gets DZStruct to fill remaining values
136 | """
137 | super(DZChunk, self).__init__(DZChunk)
138 |
139 |
140 |
141 | class DZFile(DZStruct):
142 | """
143 | Representation of the data parsed from a LGE DZ file
144 | """
145 |
146 | _dz_area = "file"
147 | _dz_header = b"\x32\x96\x18\x74"
148 |
149 | # Format string dict
150 | # itemName is the new dict key for the data to be stored under
151 | # formatString is the Python formatstring for struct.unpack()
152 | # collapse: boolean that controls whether extra \x00 's should be stripped
153 | # for integer types collapse set to True means that the value should always be zero
154 | # Example:
155 | # ('itemName', ('formatString', collapse))
156 | _dz_format_dict = OrderedDict([
157 | ('header', ('4s', False)), # magic number
158 | ('formatMajor', ('I', False)), # always 2 in LE
159 | ('formatMinor', ('I', False)), # always 1 in LE
160 | ('reserved0', ('I', True)), # format patchlevel?
161 | ('device', ('32s', True)),
162 | ('version', ('144s', True)), # "factoryversion"
163 | ('chunkCount', ('I', False)),
164 | ('md5', ('16s', False)), # MD5 of chunk headers
165 | ('unknown0', ('I', False)), # 256?
166 | ('reserved1', ('I', True)), # currently always zero
167 | ('reserved4', ('H', True)), # currently always zero
168 | ('unknown1', ('16s', False)), # unknown, MD5 of thing?
169 | ('unknown2', ('50s', True)), # A##-M##-C##-U##-0 ?
170 | ('buildType', ('20s', True)), # "user"???
171 | ('unknown3', ('4s', False)), # version code? CRC?
172 | ('androidVer', ('10s', True)), # Android ver, optional
173 | #anti-rollback minimum date? absent from Lollipop, "122142720" on all other V10
174 | ('oldDateCode', ('10s', True)), # prior firmware date?
175 | ('reserved5', ('I', False)), # currently always zero
176 | ('unknown4', ('I', False)), # sometimes 256?
177 | ('unknown5', ('I', False)), # ???
178 | ('unknown6', ('64s', False)), # ???
179 | ('unknown7', ('32s', False)), # ???
180 | ('unknown8', ('8s', False)), # ???
181 | ('pad', ('64s', True)), # currently always zero
182 | ])
183 |
184 | def __init__(self):
185 | """
186 | Initializer for DZFile, gets DZStruct to fill remaining values
187 | """
188 | super(DZFile, self).__init__(DZFile)
189 |
190 |
191 |
192 | if __name__ == "__main__":
193 | print("Sorry, this file is an internal library and doesn't do anything interesting by", file=sys.stderr)
194 | print("itself.", file=sys.stderr)
195 | sys.exit(1)
196 |
197 |
--------------------------------------------------------------------------------
/gpt.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | """
4 | Copyright (C) 2016 Elliott Mitchell
5 |
6 | This program is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | """
19 |
20 | from __future__ import print_function
21 | import os
22 | import sys
23 | import io
24 | from collections import OrderedDict
25 | from struct import Struct
26 | from uuid import UUID
27 | from binascii import crc32
28 |
29 |
30 | verbose = lambda msg: None
31 |
32 |
33 | class NoGPT(Exception):
34 | def __init__(self, errmsg):
35 | self.errmsg = errmsg
36 | def __str__(self):
37 | return self.errmsg
38 |
39 |
40 | class GPTSlice(object):
41 | """
42 | Class for handling invidual slices^Wpartitions of a GUID table
43 | """
44 |
45 | _gpt_slice_length = 128 # this is *default*!
46 |
47 | # Format string dict
48 | # itemName is the new dict key for the data to be stored under
49 | # formatString is the Python formatstring for struct.unpack()
50 | # Example:
51 | # ('itemName', ('formatString'))
52 | _gpt_slice_fmt = OrderedDict([
53 | ('type', ('16s')),
54 | ('uuid', ('16s')),
55 | ('startLBA', ('Q')),
56 | ('endLBA', ('Q')),
57 | ('flags', ('Q')),
58 | ('name', ('72s'))
59 | ])
60 |
61 | # Generate the formatstring for struct.unpack()
62 | _gpt_struct = Struct("<" + "".join([x for x in _gpt_slice_fmt.values()]))
63 |
64 | def display(self, idx):
65 | """
66 | Display the data for this slice of a GPT
67 | """
68 |
69 | if self.type == UUID(int=0):
70 | verbose("Name: ")
71 | return None
72 |
73 | verbose("Name({:d}): \"{:s}\" start={:d} end={:d} count={:d}".format(idx, self.name, self.startLBA, self.endLBA, self.endLBA-self.startLBA+1))
74 | verbose("typ={:s} id={:s}".format(str(self.type), str(self.uuid)))
75 |
76 | def __init__(self, buf):
77 | """
78 | Initialize the GPTSlice class
79 | """
80 |
81 | data = dict(zip(
82 | self._gpt_slice_fmt.keys(),
83 | self._gpt_struct.unpack(buf)
84 | ))
85 |
86 | self.type = UUID(bytes=data['type'])
87 | self.uuid = UUID(bytes=data['uuid'])
88 | self.startLBA = data['startLBA']
89 | self.endLBA = data['endLBA']
90 | self.flags = data['flags']
91 | self.name = data['name'].decode("utf16").rstrip('\x00')
92 |
93 |
94 |
95 | class GPT(object):
96 | """
97 | Class for handling of GUID Partition Tables
98 | (https://en.wikipedia.org/wiki/GUID_Partition_Table)
99 | """
100 |
101 | _gpt_header = b"EFI PART"
102 | _gpt_size = 0x5C # default, can be overridden by headerSize
103 |
104 | # Format string dict
105 | # itemName is the new dict key for the data to be stored under
106 | # formatString is the Python formatstring for struct.unpack()
107 | # Example:
108 | # ('itemName', ('formatString'))
109 | _gpt_head_fmt = OrderedDict([
110 | ('header', ('8s')), # magic number
111 | ('revision', ('I')), # actually 2 shorts, Struct...
112 | ('headerSize', ('I')),
113 | ('crc32', ('I')),
114 | ('reserved', ('I')),
115 | ('myLBA', ('Q')),
116 | ('altLBA', ('Q')),
117 | ('dataStartLBA',('Q')),
118 | ('dataEndLBA', ('Q')),
119 | ('uuid', ('16s')),
120 | ('entryStart', ('Q')),
121 | ('entryCount', ('I')),
122 | ('entrySize', ('I')),
123 | ('entryCrc32', ('I')),
124 | ])
125 |
126 | # Generate the formatstring for struct.unpack()
127 | _gpt_struct = Struct("<" + "".join([x for x in _gpt_head_fmt.values()]))
128 |
129 |
130 |
131 | def display(self):
132 | """
133 | Display the data in the particular GPT
134 | """
135 |
136 | verbose("")
137 |
138 | verbose("block size is {:d} bytes (shift {:d})".format(1<>self.shiftLBA)
148 | if endEntry < self.dataStartLBA:
149 | verbose("Note: {:d} unused slice entry blocks before first usable block".format(self.dataStartLBA - endEntry))
150 | else:
151 | if self.entryStart != self.dataEndLBA+1:
152 | verbose("Note: {:d} unused slice entry blocks after last usable block".format(self.entryStart-self.dataEndLBA-1))
153 | endEntry = self.entryStart + ((self.entrySize * self.entryCount + (1<>self.shiftLBA)
154 | if endEntry < self.myLBA-1:
155 | verbose("Note: {:d} unused blocks between GPT header and entry table".format(self.myLBA-endEntry+1))
156 |
157 | current = self.dataStartLBA
158 | idx = 1
159 | for slice in self.slices:
160 | if slice.type != UUID(int=0):
161 | if slice.startLBA != current:
162 | verbose("Note: non-contiguous ({:d} unused)".format(slice.startLBA-current))
163 | current = slice.endLBA + 1
164 | slice.display(idx)
165 | idx += 1
166 | current-=1
167 | if self.dataEndLBA != current:
168 | verbose("Note: empty LBAs at end ({:d} unused)".format(self.dataEndLBA-current))
169 |
170 |
171 | def tryParseHeader(self, buf):
172 | """
173 | Try to parse a buffer as a GPT header, return None on failure
174 | """
175 |
176 | if len(buf) < self._gpt_size:
177 | raise NoGPT("Failed to locate GPT")
178 |
179 | data = dict(zip(
180 | self._gpt_head_fmt.keys(),
181 | self._gpt_struct.unpack(buf[0:self._gpt_size])
182 | ))
183 |
184 | if data['header'] != self._gpt_header:
185 | return None
186 |
187 | tmp = data['crc32']
188 | data['crc32'] = 0
189 |
190 | crc = crc32(self._gpt_struct.pack(*[data[k] for k in self._gpt_head_fmt.keys()]))
191 |
192 | data['crc32'] = tmp
193 |
194 | # just in case future ones are larger
195 | crc = crc32(buf[self._gpt_size:data['headerSize']], crc)
196 | crc &= 0xFFFFFFFF
197 |
198 | if crc != data['crc32']:
199 | verbose("Warning: Found GPT candidate with bad CRC")
200 | return None
201 |
202 | return data
203 |
204 |
205 |
206 | def __init__(self, buf, type=None, lbaMinShift=9, lbaMaxShift=16):
207 | """
208 | Initialize the GPT class
209 | """
210 |
211 | # sanity checking
212 | if self._gpt_struct.size != self._gpt_size:
213 | raise NoGPT("GPT format string wrong!")
214 |
215 | # we assume we're searching, start with the bottom end
216 | shiftLBA = lbaMinShift
217 | lbaSize = 1<>16 != 1:
268 | raise NoGPT("Error: GPT major version isn't 1")
269 | elif self.revision&0xFFFF != 0:
270 | verbose("Warning: Newer GPT revision")
271 |
272 | # these tests were against our version and may well fail
273 | elif self.reserved != 0:
274 | verbose("Warning: Reserved area non-zero")
275 |
276 | # this is an error according to the specs
277 | if (self.myLBA != 1) and (self.altLBA != 1):
278 | raise NoGPT("Error: No GPT at LBA 1 ?!")
279 |
280 | # this is an error according to the specs
281 | if self.entrySize & (self.entrySize-1):
282 | raise NoGPT("Error: entry size is not a power of 2")
283 |
284 | if self.myLBA == 1:
285 | sliceAddr = self.entryStart<
5 |
6 | This program is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | """
19 |
20 | from __future__ import print_function
21 | import os
22 | import sys
23 | import subprocess
24 | import io
25 | import zlib
26 | import argparse
27 | import hashlib
28 | from collections import OrderedDict
29 | from binascii import crc32
30 |
31 | # our tools are in "libexec"
32 | sys.path.append(os.path.join(sys.path[0], "libexec"))
33 |
34 | import dz
35 |
36 | # compatibility, Python 3 has SEEK_HOLE/SEEK_DATA, Python 2 does not
37 | SEEK_HOLE = io.SEEK_HOLE if hasattr(io, "SEEK_HOLE") else 4
38 | SEEK_DATA = io.SEEK_DATA if hasattr(io, "SEEK_DATA") else 3
39 |
40 | class EXT4SparseChunk(dz.DZStruct):
41 | """
42 | Class for handling chunk from Android sparse image format file
43 | """
44 |
45 | # Known length of chunk
46 | _dz_length = 12
47 |
48 | # Doesn't include magic number, this should match output, I hope
49 | _dz_header = b""
50 |
51 | # Format dictionary
52 | _dz_format_dict = OrderedDict([
53 | ('header', ('0s', False)), # magic number (none)
54 | ('type', ('H', False)), # type value
55 | ('reserved1', ('H', True)), # reserved
56 | ('chunkCount', ('I', False)), # blocks in output
57 | ('totalSize', ('I', False)), # bytes of chunk input
58 | ])
59 |
60 | # Chunk types
61 | typeRaw = 0xCAC1
62 | typeFill = 0xCAC2
63 | typeDontCare = 0xCAC3
64 | typeCrc32 = 0xCAC4
65 |
66 | def __init__(self, head, buf, pipe, blockShift, readSize):
67 | """
68 | Initializer for EXT4SparseChunk, gets DZStruct to fill values
69 | """
70 | super(EXT4SparseChunk, self).__init__(EXT4SparseChunk)
71 |
72 | values = self.unpackdict(buf[:self._dz_length])
73 |
74 | self.type = values['type']
75 | self.blocks = values['chunkCount']
76 |
77 | self.remaining = self.blocks << blockShift
78 |
79 | self.readSize = readSize
80 |
81 | self.pipe = pipe
82 |
83 | self.head = head
84 |
85 | if self.type == self.typeRaw:
86 | # would such a case suggest zero-padding?
87 | if self.remaining != values['totalSize'] - len(buf):
88 | print("[!] Error encountered raw chunk with incorrect amount of payload", file=sys.stderr)
89 | sys.exit(64)
90 | elif self.type == self.typeFill:
91 | buf = self.pipe.read(values['totalSize'] - len(buf))
92 | self.buffer = b""
93 | while len(self.buffer) < readSize:
94 | self.buffer += buf
95 |
96 | def __del__(self):
97 | """
98 | Destructor for EXT4SparseChunk, notably read remaining data,
99 | if any is left behind
100 | """
101 |
102 | if self.type == self.typeRaw or self.type == self.typeFill:
103 | while self.remaining > 0:
104 | if self.remaining < (1< 0:
211 | print("[!] Warning: Output format from ext2simg more recent than this utility", file=sys.stderr)
212 |
213 | # Extra fields to ignore
214 | if values['headerSize'] > self._dz_length:
215 | self.child.stdout.read(values['headerSize'] - self._dz_length)
216 |
217 | # How large the chunks are
218 | self.chunkHSize = values['chunkHSize']
219 | self.chunkCount = values['totalChunks']
220 |
221 | # CRC32 of image
222 | self.origCrc = values['imageCRC32']
223 | self.crc = crc32(b"")
224 |
225 | # Block size of the device, power of 2, minimum of 4K
226 | size = values['blockSize']
227 | self.blockSize = size
228 |
229 | if size & (size-1):
230 | print("[!] Error: Block size specified in sparse header is not a power of 2", file=sys.stderr)
231 | sys.exit(1)
232 |
233 | result = 0
234 | shift = 32
235 | while shift > 0:
236 | if (size>>shift)>0:
237 | size>>=shift
238 | result+=shift
239 | shift>>=1
240 | self.blockShift = result
241 |
242 | def __del__(self):
243 | """
244 | Destructor for Image2Chunks, notably kills child if needed
245 | """
246 |
247 | if hasattr(self, 'child') and self.child != None:
248 | # normal exit
249 | if self.chunkCount <= 0:
250 | if self.child.poll() != None:
251 | if self.child.returncode > 0:
252 | print("[!] Warning: Return code of {:d} from ext2simg!".format(self.child.returncode), file=sys.stderr)
253 | elif self.child.returncode < 0:
254 | print("[!] Warning ext2simg killed by signal {:d}!".format(-self.child.returncode), file=sys.stderr)
255 | # otherwise 0 exit, no problem
256 |
257 | # hasn't terminated, trouble...
258 | else:
259 | self.child.terminate()
260 | if self.child.wait(10) == None:
261 | self.child.kill()
262 | print("[!] Warning forced to terminate ext2simg", file=sys.stderr)
263 | # abnormal condition of some sort
264 | else:
265 | self.child.terminate()
266 | if self.child.wait(10) == None:
267 | self.child.kill()
268 |
269 | def __iter__(self):
270 | """
271 | The __iter__ method for doing loops
272 | """
273 | return self
274 |
275 | def __next__(self):
276 | """
277 | Retrieve the next parsed chunk
278 | """
279 |
280 | try:
281 | # The previous chunk object *MUST* be destroyed *NOW*
282 | self.last.__del__()
283 | except AttributeError:
284 | # doesn't exist yet
285 | pass
286 |
287 | if self.chunkCount <= 0:
288 | self.crc = self.crc & 0xFFFFFFFF
289 | # apparently 0 is used for none computed
290 | if self.crc != self.origCrc and self.origCrc != 0:
291 | print("[!] CRC mismatch, computed={:08X}, original={:08X}".format(self.crc, self.origCrc))
292 | sys.exit(4)
293 |
294 | raise StopIteration
295 |
296 | self.chunkCount -= 1
297 |
298 | buf = self.child.stdout.read(self.chunkHSize)
299 |
300 | if len(buf) != self.chunkHSize:
301 | print("[!] Attempting to read chunk header got short read", file=sys.stderr)
302 | sys.exit(2)
303 |
304 | self.last = EXT4SparseChunk(self, buf, self.child.stdout, self.blockShift, self.readSize)
305 | return self.last
306 |
307 | # Python 2 compatibility
308 | next = __next__
309 |
310 |
311 | class Image2Chunks(dz.DZChunk):
312 | """
313 | Class for transforming a single file from a raw image into chunk files
314 | """
315 |
316 | def openFiles(self, name):
317 | """
318 | Opens the files, provide an error message if one doesn't exist
319 | """
320 |
321 | try:
322 | role = "parameter"
323 | self.paramsFile = io.open(name + ".params", "rt")
324 | role = "image"
325 | self.file = io.FileIO(name, "rb")
326 |
327 | except IOError:
328 | print("[!] Failed opening {:s} file for {:s}".format(role, name), file=sys.stderr)
329 | sys.exit(1)
330 |
331 |
332 |
333 |
334 | def loadParams(self, name):
335 | """
336 | Loads the .params file for image, saves off key values
337 | """
338 |
339 |
340 | params = dict()
341 | line = self.paramsFile.readline()
342 | while len(line) > 0:
343 | line.lstrip()
344 | line = line.partition("#")[0]
345 | if len(line) == 0:
346 | line = self.paramsFile.readline()
347 | continue
348 | parts = line.partition("=")
349 | if len(parts[1]) == 0:
350 | print("[!] Bad line in {:s}'s parameter file".format(name), file=sys.stderr)
351 | var = parts[0].rstrip()
352 | # currently we only have integers in the file
353 | val = int(parts[2].strip())
354 | params[var] = val
355 | line = self.paramsFile.readline()
356 |
357 | self.paramsFile.close()
358 | del self.paramsFile
359 |
360 | if 'phantom' in params and params['phantom']:
361 | print("[!] {:s} is a phantom slice, skipping!".format(name))
362 | return False
363 |
364 | for k in 'blockShift', 'startLBA', 'endLBA', 'lastWipe', 'dev':
365 | if k not in params:
366 | print("Parameter value \"{:s}\" is missing, unable to continue".format(k))
367 | sys.exit(1)
368 |
369 | self.blockShift = params['blockShift']
370 | self.blockSize = 1 << self.blockShift
371 | self.startLBA = params['startLBA']
372 | self.endLBA = params['endLBA']
373 | self.lastWipe = params['lastWipe']
374 | self.dev = params['dev']
375 |
376 | return True
377 |
378 |
379 | def makeChunksHoles(self, name):
380 | """
381 | Generate one or more .chunks files for the named file
382 | """
383 |
384 | os.chdir(os.path.dirname(name))
385 | name = os.path.basename(name)
386 | baseName = name.rpartition(".")[0] + "_"
387 | sliceName = name.rpartition(".")[0].encode("utf8")
388 |
389 | current = 0
390 | targetAddr = self.startLBA
391 | eof = self.file.seek(0, io.SEEK_END)
392 | self.file.seek(0, io.SEEK_SET)
393 |
394 | while current < eof:
395 | hole = (self.file.seek(current, SEEK_HOLE) + self.blockSize-1) & ~(self.blockSize-1)
396 | # Python's handling of this condition is suboptimal
397 | try:
398 | next = self.file.seek(hole, SEEK_DATA) & ~(self.blockSize-1)
399 | trimCount = (next - current) >> self.blockShift
400 | except IOError:
401 | next = eof
402 | trimCount = self.lastWipe - targetAddr
403 |
404 | # Watch out for chunks >4GB (too big!)
405 | # Also, try not to test the limits of LG's tools...
406 | if (hole - current) >= 1<<27:
407 | hole = current + (1<<27)
408 | next = hole
409 | trimCount = (next - current) >> self.blockShift
410 |
411 | md5 = hashlib.md5()
412 | crc = crc32(b"")
413 | zobj = zlib.compressobj(1)
414 | self.file.seek(current, io.SEEK_SET)
415 |
416 | chunkName = baseName + str(targetAddr) + ".bin"
417 | out = io.FileIO(chunkName + ".chunk", "wb")
418 |
419 | print("[+] Compressing {:s} to {:s} ({:d} empty blocks)".format(name, chunkName, (next - hole) >> self.blockShift))
420 |
421 | chunkName = chunkName.encode("utf8")
422 | out.seek(self._dz_length, io.SEEK_SET)
423 | zlen = 0
424 |
425 | for b in range((hole - current) >> self.blockShift):
426 | buf = self.file.read(self.blockSize)
427 | md5.update(buf)
428 | crc = crc32(buf, crc)
429 | zdata = zobj.compress(buf)
430 | zlen += len(zdata)
431 | out.write(zdata)
432 |
433 | zdata = zobj.flush(zlib.Z_FINISH)
434 | zlen += len(zdata)
435 | out.write(zdata)
436 | md5 = md5.digest()
437 |
438 |
439 | out.seek(0, io.SEEK_SET)
440 |
441 | values = {
442 | 'sliceName': sliceName,
443 | 'chunkName': chunkName,
444 | 'targetSize': hole - current,
445 | 'dataSize': zlen,
446 | 'md5': md5,
447 | 'targetAddr': targetAddr,
448 | 'trimCount': trimCount,
449 | 'crc32': crc & 0xFFFFFFFF,
450 | 'dev': self.dev,
451 | }
452 |
453 | header = self.packdict(values)
454 | out.write(header)
455 | out.close()
456 |
457 | current = next
458 | targetAddr = self.startLBA + (current >> self.blockShift)
459 |
460 | print("[+] done\n")
461 |
462 |
463 | def makeChunksEXT4FS(self, name):
464 | """
465 | Generate one or more .chunks files assuming an EXT4 FS
466 | """
467 |
468 | os.chdir(os.path.dirname(name))
469 | name = os.path.basename(name)
470 | baseName = name.rpartition(".")[0] + "_"
471 | sliceName = name.rpartition(".")[0].encode("utf8")
472 |
473 | # Check length before closing
474 | eof = self.file.seek(0, io.SEEK_END)
475 |
476 | # Alas, ext2simg can't take the image as stdin
477 | self.file.close()
478 |
479 | sparse = EXT4SparseFile(name, 1<> self.blockShift)
517 |
518 | print("({:d} empty blocks)".format(nl.trimCount - dataBlocks))
519 |
520 | nl.trimCount = 0
521 |
522 | for chunk in sparse:
523 | if chunk.type == EXT4SparseChunk.typeRaw or chunk.type == EXT4SparseChunk.typeFill:
524 | if nl.trimCount:
525 | complete()
526 | dataBlocks = chunk.remaining >> self.blockShift
527 | nl.trimCount += dataBlocks
528 |
529 | nl.md5 = hashlib.md5()
530 | crc = crc32(b"")
531 | zobj = zlib.compressobj(1)
532 |
533 | chunkName = baseName + str(nl.targetAddr) + ".bin"
534 | out = io.FileIO(chunkName + ".chunk", "wb")
535 |
536 | sys.stdout.write("[+] Compressing {:s} to {:s} ".format(name, chunkName))
537 |
538 | chunkName = chunkName.encode("utf8")
539 | out.seek(self._dz_length, io.SEEK_SET)
540 | nl.zlen = 0
541 |
542 | for buf in chunk:
543 | nl.md5.update(buf)
544 | crc = crc32(buf, crc)
545 | zdata = zobj.compress(buf)
546 | nl.zlen += len(zdata)
547 | out.write(zdata)
548 |
549 | elif chunk.type == EXT4SparseChunk.typeDontCare:
550 | # check for EOF, lastWipe overrides
551 | if sparse.chunkCount == 0:
552 | nl.trimCount = self.lastWipe - nl.targetAddr
553 | else:
554 | nl.trimCount += chunk.remaining>>self.blockShift
555 | complete()
556 |
557 | elif chunk.type == EXT4SparseChunk.typeCrc32:
558 | pass
559 | else:
560 | print("[!] Error: unknown chunk, type=0x{:04X}".format(chunk.type), file=sys.stderr)
561 | sys.exit(64)
562 |
563 | if nl.trimCount:
564 | nl.trimCount = self.lastWipe - nl.targetAddr
565 | complete()
566 |
567 | print("[+] done\n")
568 |
569 |
570 | def makeChunksProbe(self, name):
571 | """
572 | Generate one or more .chunks files for the named file
573 | """
574 |
575 | os.chdir(os.path.dirname(name))
576 | name = os.path.basename(name)
577 | baseName = name.rpartition(".")[0] + "_"
578 | sliceName = name.rpartition(".")[0].encode("utf8")
579 |
580 | current = 0
581 | targetAddr = self.startLBA
582 | eof = self.file.seek(0, io.SEEK_END)
583 | self.file.seek(0, io.SEEK_SET)
584 |
585 |
586 |
587 | # emulate characteristics of LG's tool, always find a block at start
588 | readSize = self.blockSize << 10
589 |
590 | md5 = hashlib.md5()
591 | crc = crc32(b"")
592 | zobj = zlib.compressobj(1)
593 | self.file.seek(current, io.SEEK_SET)
594 |
595 | chunkName = baseName + str(targetAddr) + ".bin"
596 | out = io.FileIO(chunkName + ".chunk", "wb")
597 |
598 | sys.stdout.write("[+] Compressing {:s} to {:s} ".format(name, chunkName))
599 |
600 | chunkName = chunkName.encode("utf8")
601 | out.seek(self._dz_length, io.SEEK_SET)
602 | zlen = 0
603 |
604 | wipeData = readSize
605 | dataCount = readSize
606 |
607 |
608 |
609 | buf = self.file.read(readSize)
610 |
611 | while len(buf.lstrip(b'\x00')) == 0 and current < eof:
612 | md5.update(buf)
613 | crc = crc32(buf, crc)
614 | zdata = zobj.compress(buf)
615 | zlen += len(zdata)
616 | out.write(zdata)
617 | wipeData += readSize
618 | dataCount += readSize
619 | current += readSize
620 |
621 | buf = self.file.read(readSize)
622 |
623 |
624 |
625 | while current < eof:
626 |
627 |
628 |
629 |
630 | while len(buf.lstrip(b'\x00')) != 0 and current < eof:
631 | md5.update(buf)
632 | crc = crc32(buf, crc)
633 | zdata = zobj.compress(buf)
634 | zlen += len(zdata)
635 | out.write(zdata)
636 | wipeData += readSize
637 | dataCount += readSize
638 | current += readSize
639 |
640 | buf = self.file.read(readSize)
641 |
642 | zdata = zobj.flush(zlib.Z_FINISH)
643 | zlen += len(zdata)
644 | out.write(zdata)
645 | md5 = md5.digest()
646 |
647 |
648 |
649 | while len(buf.lstrip(b'\x00')) == 0 and current < eof:
650 | wipeData += readSize
651 | current += readSize
652 | buf = self.file.read(readSize)
653 |
654 |
655 | print("({:d} empty blocks)".format((wipeData - dataCount) >> self.blockShift))
656 |
657 |
658 |
659 |
660 |
661 | out.seek(0, io.SEEK_SET)
662 |
663 | values = {
664 | 'sliceName': sliceName,
665 | 'chunkName': chunkName,
666 | 'targetSize': dataCount,
667 | 'dataSize': zlen,
668 | 'md5': md5,
669 | 'targetAddr': targetAddr,
670 | 'trimCount': wipeData >> self.blockShift,
671 | 'crc32': crc & 0xFFFFFFFF,
672 | 'dev': self.dev,
673 | }
674 |
675 | header = self.packdict(values)
676 | out.write(header)
677 | out.close()
678 |
679 |
680 |
681 |
682 | targetAddr = self.startLBA + (current >> self.blockShift)
683 |
684 |
685 | if current < eof:
686 |
687 |
688 | md5 = hashlib.md5()
689 | crc = crc32(b"")
690 | zobj = zlib.compressobj(1)
691 |
692 | chunkName = baseName + str(targetAddr) + ".bin"
693 | out = io.FileIO(chunkName + ".chunk", "wb")
694 |
695 | sys.stdout.write("[+] Compressing {:s} to {:s} ".format(name, chunkName))
696 |
697 | chunkName = chunkName.encode("utf8")
698 | out.seek(self._dz_length, io.SEEK_SET)
699 | zlen = 0
700 |
701 | wipeData = readSize
702 | dataCount = readSize
703 |
704 |
705 |
706 |
707 |
708 |
709 | print("[+] done\n")
710 |
711 |
712 | def __init__(self, name, strategy):
713 | """
714 | Initializer for Image2Chunks class, takes filename as arg
715 | """
716 |
717 | super(Image2Chunks, self).__init__()
718 |
719 | self.openFiles(name)
720 |
721 | if self.loadParams(name):
722 | if strategy == 0:
723 | self.makeChunksEXT4FS(name)
724 | elif strategy == 1:
725 | self.makeChunksHoles(name)
726 | elif strategy == 2:
727 | self.makeChunksProbe(name)
728 | elif strategy == None:
729 | print("[!] No strategy specified, one *must* be specified before filename(s)", file=sys.stderr)
730 | sys.exit(1)
731 | else:
732 | print("[!] Internal error!", file=sys.stderr)
733 | sys.exit(127)
734 |
735 |
736 | def help(progname):
737 | print("usage: {:s} [-h | --help] [-e | --ext4 | -s | --sparse | -p | --probe] \n".format(progname))
738 | print("DZ Chunking program by Elliott Mitchell\n")
739 | print("optional arguments:")
740 | print(" -h | --help show this help message and exit")
741 | print(" -e | --ext4 use Android's sparse EXT4 dump utility (recommended)")
742 | print(" -s | --sparse use SEEK_DATA/SEEK_HOLE (not available on all OSes)")
743 | print(" -p | --probe probe for holes (safe)")
744 | sys.exit(0)
745 |
746 |
747 | if __name__ == "__main__":
748 |
749 | progname = sys.argv[0]
750 | del sys.argv[0]
751 |
752 | basedir = os.open(".", os.O_DIRECTORY)
753 |
754 | # no default strategy, ext2simg is reasonable, but worrisome if non-FS
755 | strategy = None
756 |
757 | if len(sys.argv) <= 0:
758 | help(progname)
759 |
760 | for arg in sys.argv:
761 |
762 | if arg[0] == "-":
763 | if arg == "-e" or arg == "--ext4":
764 | strategy = 0
765 | elif arg == "-s" or arg == "--sparse":
766 | strategy = 1
767 | elif arg == "-p" or arg == "--probe":
768 | strategy = 2
769 | elif arg == "-h" or arg == "--help":
770 | help(progname)
771 |
772 | elif arg[0] == "-":
773 | print('[!] Unknown option "{:s}"'.format(arg))
774 | sys.exit(1)
775 |
776 | continue
777 |
778 | Image2Chunks(arg, strategy)
779 |
780 | os.fchdir(basedir)
781 |
782 |
--------------------------------------------------------------------------------
/install_requirements.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | '''
4 | 执行此文件将自动安装requirements.txt中的依赖
5 | '''
6 | import os,platform
7 | f=open('requirements.txt')
8 | for i in f:
9 | if i[0]=='#':
10 | pass
11 | else:
12 | os.system('pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple '+i)
13 | f.close()
14 | if platform.system()=='Linux':print('请手动安装brotli')
15 |
16 |
--------------------------------------------------------------------------------
/kdz.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | """
4 | Copyright (C) 2016 Elliott Mitchell
5 | Copyright (C) 2013 IOMonster (thecubed on XDA)
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program. If not, see .
19 | """
20 |
21 | from __future__ import print_function
22 | import sys
23 | from struct import Struct
24 | from collections import OrderedDict
25 | import dz
26 |
27 |
28 | class KDZFile(dz.DZStruct):
29 | """
30 | LGE KDZ File tools
31 | """
32 |
33 |
34 | _dz_length = 272
35 | _dz_header = b"\x28\x05\x00\x00"b"\x24\x38\x22\x25"
36 |
37 | # Format string dict
38 | # itemName is the new dict key for the data to be stored under
39 | # formatString is the Python formatstring for struct.unpack()
40 | # collapse is boolean that controls whether extra \x00 's should be stripped
41 | # Example:
42 | # ('itemName', ('formatString', collapse))
43 | _dz_format_dict = OrderedDict([
44 | ('name', ('256s', True)),
45 | ('length', ('Q', False)),
46 | ('offset', ('Q', False)),
47 | ])
48 |
49 |
50 | def __init__(self):
51 | """
52 | Initializer for KDZFile, gets DZStruct to fill remaining values
53 | """
54 | super(KDZFile, self).__init__(KDZFile)
55 |
56 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | # adbshellpy_libunpakrom
4 | # By : AEnjoy
5 | # Version : 2.2.5
6 | # last Update: 2024-7-7 17:38:01
7 | try:import sys,os,zipfile,urllib.request,tarfile,argparse,platform,lz4.frame,glob2,undz,unkdz,sdat2img,payload_dumper,simg2img
8 | except ImportError:
9 | print('E:请执行install_requirements.py后再执行main!')
10 | input('按Enter键退出')
11 | exit(1)
12 | ozipmodelerror=0
13 | try:import ozipdecrypt
14 | except ImportError:
15 | print('W:pycrypto依赖未安装,oppo ozip解包将不可用')
16 | ozipmodelerror=1
17 |
18 | quiet=0 #0询问 1安静
19 |
20 | def get_saminfo(filename='AP_G9650ZCS6CSK2_CL17051143_QB26966166_REV01_user_low_ship_MULTI_CERT_meta_OS9.tar.md5',l=-1):
21 | """
22 | get samsumg rom info
23 | :return: last line or None for empty file
24 | """
25 | try:
26 | filesize = os.path.getsize(filename)
27 | if filesize == 0:
28 | return None
29 | else:
30 | with open(filename, 'rb') as fp:
31 | offset = -8
32 | while -offset < filesize:
33 | fp.seek(offset, 2)
34 | lines = fp.readlines()
35 | if len(lines) >= 2:
36 | return lines[l]
37 | else:
38 | offset *= 2
39 | fp.seek(0)
40 | lines = fp.readlines()
41 | print('b')
42 | return lines[l]
43 | except FileNotFoundError:
44 | print(filename + ' 未找到!')
45 | return None
46 | class rominformation:
47 | '''
48 | brotli=None
49 | newdat=None
50 | olnyimg=None
51 | onlyfolder=None
52 | ozip=None
53 | androidVersion=
54 | flag:
55 | 1.无效文件路径
56 | 2.不支持格式
57 | 3.线刷包找到
58 | 4.卡刷包找到 ROM
59 | 5.卡刷包找到 flashable
60 | '''
61 | type=None #1功能性卡刷包(如opengapps) 2ROM卡刷包 3线刷包
62 | flag=0
63 | olnyimg,onlyfolder,brotil,newdat,samsumgodinfile,ozip,lgkd,lgkdz,abflag,miuitar=False,False,False,False,False,False,False,False,False,False
64 | def __init__(self,file=''):
65 | print('正在处理ROM信息...该过程需要1s-2min分钟不等')
66 | '''获取ROM信息 输入的文件可以是线刷包,也可以是卡刷包'''
67 | size=os.path.getsize(file)
68 | if os.path.exists(file)==False or size==0:
69 | print('E:请选择一个正确的文件!!!')
70 | self.flag=1#无效文件路径
71 | return
72 | self.file=file
73 | if file.find('payload.bin')>-1:
74 | self.abflag=True
75 | self.flag=3
76 | print('发现A/B(System As Root)更新文件(安卓10动态分区)')
77 | return
78 | if file.find('.ozip') > -1 and zipfile.is_zipfile(file)==True:
79 | with open(file,'rb') as fr:
80 | magic=fr.read(12)
81 | if magic==b"OPPOENCRYPT!" or magic[:2]==b"PK":
82 | self.ozip=True
83 | print('发现OPPO OZIP! 需要解密后才能读取ROM信息')
84 | fr.close()
85 | return
86 | print('这个ROM可能不是OPPO OZIP?!')
87 | if file.find('.tar.md5') > -1 and tarfile.is_tarfile(file):
88 | self.samsumgodinfile=True
89 | a=str(get_saminfo(file))
90 | if a:
91 | a=a.replace("b'",'')
92 | a=a.replace(".tar\\n'",'')
93 | li=a.split(' ')
94 | a=li[2].split('_')
95 | print('ROM类型:'+a[0]+'\n版本:'+a[1]+'\n发行标志:'+a[5]+'\n固件类型:offical')
96 | print('发现三星odin线刷文件!')
97 | print('W:只有ROM类型为AP才支持解包出系统镜像')
98 | return
99 | print('Maybe:发现三星odin线刷文件?!')
100 | if file.find('.tgz') > -1 and tarfile.is_tarfile(file):
101 | #MIUI
102 | tar = tarfile.open(file, "r:gz")
103 | l=tar.getnames()
104 | for a in l:
105 | if a.find('system.img')>-1 :
106 | self.flag=3
107 | self.miuitar=True
108 | print('Maybe:MIUI 线刷包找到')
109 | return
110 | elif a.find('super.img')>-1:
111 | self.flag=3
112 | self.miuitar=True
113 | self.super=True
114 | print('Maybe:MIUI 线刷包找到')
115 | return
116 | if zipfile.is_zipfile(file)==False:
117 | print('E:不支持的格式!!!!')
118 | self.flag=2
119 | return
120 | self.file=file
121 | z=zipfile.ZipFile(file)
122 | self.l=z.namelist()
123 | self.flag=4
124 | #z.close()
125 | if 'system.img' in self.l:
126 | self.olnyimg=True
127 |
128 | if 'system/framework/framework.jar' in self.l:
129 | self.onlyfolder=True
130 |
131 | if 'system.new.dat.br' in self.l and 'system.transfer.list' in self.l:
132 | self.brotil=True
133 |
134 | if 'system.new.dat' in self.l and 'system.transfer.list' in self.l:
135 | self.newdat=True
136 |
137 | if 'system.transfer.list' in self.l:
138 | z.extract('system.transfer.list')
139 | f = open('system.transfer.list', 'r')
140 | v = int(f.readline())
141 | f.close()
142 | if v == 1:
143 | print('Android Lollipop 5.0 检测到!\n')
144 | self.androidVersion='Lollipop 5.0 API 21'
145 | elif v == 2:
146 | print('Android Lollipop 5.1 检测到!\n')
147 | self.androidVersion='Lollipop 5.1 API 22'
148 | elif v == 3:
149 | print('Android Marshmallow 6.x 检测到!\n')
150 | self.androidVersion='Marshmallow 6.x API 23'
151 | elif v == 4:
152 | print('Android Nougat 7.x / Oreo 8.x 或更高版本检测到!\n')
153 | self.androidVersion='Nougat 7.x or higher API 24+'
154 | os.remove('system.transfer.list')
155 | if 'payload.bin' in self.l:
156 | self.abflag=True
157 | self.flag=4
158 | print('发现A/B(System As Root)更新文件(安卓10动态分区)')
159 |
160 | if 'META-INF/com/android/metadata' in self.l:
161 | z.extract('META-INF/com/android/metadata')
162 | f=open('META-INF/com/android/metadata', encoding='UTF-8')
163 | l=[]
164 | for i in f:l.append(i.strip())
165 | f.close()
166 | os.remove('META-INF/com/android/metadata')
167 | for i in l:
168 | x=i.split('=')
169 | if x[0]=='post-build':
170 | text=x[1]
171 | self.info=text.split('/')
172 | if len(self.info)==6:
173 | print('ROM制造商:'+self.info[0]+'\n手机代号:'+self.info[1]+'\n版本:'+self.info[2]+'\nAndroid开发版本:'+self.info[3]+'\n固件版本:'+self.info[4]+'\n发行标志:'+self.info[5])
174 | z.close()
175 | return
176 | else:
177 | print('您的设备指纹可能已经被修改,无法获取ROM信息!!!')
178 | else:
179 | print('metadata文件不存在?!')
180 | z.close()
181 |
182 |
183 | for names in self.l:#prop获取Android版本
184 | if names.find('build.prop') > -1:
185 | try:z.extract(names)
186 | except:pass
187 | if os.path.exists(names):
188 | f=open(names, encoding='UTF-8')
189 | l=[]
190 | for i in f:l.append(i.strip())
191 | f.close()
192 | os.remove(names)
193 | for i in l:
194 | x=i.split('=')
195 | if x[0]=='ro.build.fingerprint':#Android 指纹库
196 | text=x[1]
197 | self.info=text.split('/')
198 | if len(self.info)==6:
199 | print('ROM制造商:'+self.info[0]+'\n手机代号:'+self.info[1]+'\n版本:'+self.info[2]+'\nAndroid开发版本:'+self.info[3]+'\n固件版本:'+self.info[4]+'\n发行标志:'+self.info[5])
200 | z.close()
201 | return
202 | else:
203 | print('您的设备指纹可能已经被修改,无法获取ROM信息!!!')
204 |
205 | if 'META-INF/com/google/android/updater-script' in self.l:
206 | z.extract('META-INF/com/google/android/updater-script')
207 | f=open('META-INF/com/google/android/updater-script', encoding='UTF-8')
208 | l=[]
209 | for i in f:l.append(i.strip())
210 | f.close()
211 | os.remove('META-INF/com/google/android/updater-script')
212 | for i in l:
213 | if 'ui_print("Target:' in i:
214 | i=i.replace('ui_print("Target:','')
215 | i=i.replace('");','')
216 | i=i.replace(' ','')
217 | self.info=i.split('/')
218 | if len(self.info)==6:
219 | print('ROM制造商:'+self.info[0]+'\n手机代号:'+self.info[1]+'\n版本:'+self.info[2]+'\nAndroid开发版本:'+self.info[3]+'\n固件版本:'+self.info[4]+'\n发行标志:'+self.info[5])
220 | z.close()
221 | return
222 | if (i.find('update-binary') > -1 and i.find('ummy') > -1) or i.find('#MAGISK') > -1:
223 | self.flag=5
224 | print('发现该压缩包为功能性卡刷包!(Magisk/oepngapps/ak2/ak3/etc.')
225 | z.close()
226 | return
227 | print('W:无法从updater-script获取ROM信息!!')
228 | if zipfile.is_zipfile(file)==False:
229 | if file.find('.kdz') > -1:
230 | print('Maybe:发现LG .kdz文件!\n正在测试是否为 .kdz文件...')
231 | if lg_kd_kdz(file).islgkdzfile():
232 | self.lgkdz=True
233 | self.flag=3
234 | self.type=3
235 | print('发现LG .kdz文件!')
236 | return
237 | else:
238 | print('这个文件可能不是LG .kdz文件?')
239 | self.flag=2
240 | return
241 | if file.find('.dz') > -1:
242 | print('Maybe:发现LG .dz文件!\n正在测试是否为 .dz文件...')
243 | if lg_kd_kdz(file).islgdzfile():
244 | self.lgkd=True
245 | self.flag=3
246 | self.type=3
247 | print('发现LG .dz文件!')
248 | else:
249 | print('这个文件可能不是LG .dz文件?')
250 | self.flag=2
251 | return
252 | print('无效不可读格式?')
253 | self.flag=2
254 | return
255 | z.close()
256 |
257 | class lg_kd_kdz():
258 | def __init__(self,file):
259 | """
260 | GitHub Paper:https://github.com/randomstuffpaul/kdztools
261 | Copyright (C) 2016 Elliott Mitchell
262 | Copyright (C) 2013 IOMonster (thecubed on XDA)
263 | This program is free software: you can redistribute it and/or modify
264 | it under the terms of the GNU General Public License as published by
265 | the Free Software Foundation, either version 3 of the License, or
266 | (at your option) any later version.
267 | This program is distributed in the hope that it will be useful,
268 | but WITHOUT ANY WARRANTY; without even the implied warranty of
269 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
270 | GNU General Public License for more details.
271 | You should have received a copy of the GNU General Public License
272 | along with this program. If not, see .
273 | file 输入的文件(kdz/kd)
274 | """
275 | if os.path.exists(file)==False:
276 | print('E:无效文件路径!')
277 | return
278 | self.file=file
279 | def islgkdzfile(self):
280 | kdz=unkdz.KDZFileTools()
281 | kdz.kdzfile=self.file
282 | try:
283 | kdz.openFile(self.file)
284 | l=kdz.getPartitions()
285 | if len(l) !=0:return True
286 | else:return False
287 | except:return False
288 |
289 | def islgdzfile(self):
290 | dz=undz.DZFileTools()
291 | try:
292 | dz.dz_file=undz.UNDZFile(self.file)
293 | dz.cmdListPartitions()
294 | return True
295 | except:return False
296 | #dz.
297 | def unpackkdz(self):#all
298 | kdz=unkdz.KDZFileTools()
299 | kdz.kdzfile=self.file
300 | kdz.openFile(self.file)
301 | kdz.outdir=os.getcwd()
302 | kdz.partList=kdz.getPartitions()
303 | kdz.cmdExtractAll()
304 | def listkdz(self):#all
305 | kdz=unkdz.KDZFileTools()
306 | kdz.kdzfile=self.file
307 | kdz.openFile(self.file)
308 | kdz.cmdListPartitions()
309 | def unpackdz(self,mode=1):
310 | '''
311 | mode:
312 | 1 USE COMMAND TO EXTRACT
313 | 2 USE PYTHON FUNCTION
314 | '''
315 | if mode==2:
316 | dz=undz.DZFileTools()
317 | dz.mainbyclass(self.file,'rom')
318 | elif mode==1:
319 | os.system('python undz.py -f %s -i -d rom'%self.file)
320 |
321 | class unpackrom():
322 | global quiet
323 | file=''
324 | unpacktodir=1
325 | def __init__(self,file,rominfo,unpacktodir=1,check=0):
326 | '''file:inputfile unpacktodir 0/1 0:Only run onec ;1 only to system dir check:lib 0/1'''
327 | self.rominfo=rominfo
328 | if file!='':self.file=rominfo.file
329 | else:self.file=file
330 | self.unpacktodir=unpacktodir
331 | if check==1:
332 | print('正在安装依赖...')
333 | import install_requirements
334 | a=input('ROM解析完成.是否解包?y/n>>>')
335 | if a=='y':
336 | if rominfo.abflag==True and zipfile.is_zipfile(self.file)==True:self.unzip()
337 | if rominfo.miuitar==True:self.miui_tar()
338 | if rominfo.samsumgodinfile==True:self.samsumg_tar()
339 | if rominfo.lgkdz==True:self.lg_kdz()
340 | if rominfo.lgkd==True:self.lg_dz()
341 | if rominfo.flag==5 or rominfo.flag==4:self.unzip()
342 | if rominfo.ozip==True:self.oppo_ozip()
343 | if rominfo.abflag==True:self.abunpack()#.bin
344 | elif a=='n':print('操作已取消')
345 | def miui_tar(self):
346 | if quiet==0:
347 | if input('是否解包XiaomiTarFile?y/n>>>')=='n':return
348 | tar=tarfile.open(self.file)
349 | tar.extractall(path='rom')
350 | tar.close()
351 | print('Done.')
352 | items = os.listdir('rom')
353 | dir='rom/'
354 | if len(items)==1:
355 | dir=dir+items[0]+'/'
356 | items = os.listdir(dir)
357 | for i in items:
358 | if i.find('images')>-1:
359 | dir=dir+i+'/'
360 | if os.path.exists(dir+'super.img'):
361 | print('MIUI:发现线刷包动态分区镜像.')
362 | imgfile = dir+'super.img'
363 | if os.path.exists(dir+'system.img'):
364 | imgfile = dir+'system.img'
365 | self.file=imgfile
366 | self.imgunpack()
367 |
368 | def samsumg_tar(self):
369 | if quiet==0:
370 | if input('是否解包SamsungTarFile?y/n>>>')=='n':return
371 | tar=tarfile.open(self.file)
372 | tar.extractall(path='rom')
373 | tar.close()
374 | #lz4File
375 | lz4file = glob2.glob('rom/*.lz4')
376 | for a in lz4file:
377 | #a:input_file b:output_file char
378 | b=a.replace('.img.lz4','.img')
379 | with open(a, 'rb') as infile:
380 | data=infile.read()
381 | newdata = lz4.frame.decompress(data)
382 | outfile = open(b, 'wb')
383 | outfile.write(newdata)
384 | outfile.close()
385 | infile.close()
386 | if os.path.exists('rom/super.img') and self.unpacktodir==1:
387 | print('W:动态分区解包有待测试')
388 | self.file='rom/super.img'
389 | self.imgunpack()
390 | if os.path.exists('rom/system.img') and self.unpacktodir==1:
391 | self.file='rom/system.img'
392 | self.imgunpack()
393 | if os.path.exists('rom/system.img.ext4') and self.unpacktodir==1:
394 | self.file='rom/system.img.ext4'
395 | self.imgunpack()
396 | def lg_kdz(self):
397 | if quiet==0:
398 | if input('是否解包LG KDZ?y/n>>>')=='n':return
399 | print('当a,b同时为y时,将列出kdz文件分区表并解包,否则当a=y,b=n时,将仅列出kdz内文件列表')
400 | a=input('a:是否仅列出.kdz分区列表?(默认n)y/n')
401 | b=input('b:是否解包kdz全部文件(默认y)?y/n')
402 | c=input('c:欲解包的分区序号:(默认全部Enter)[暂不可用]')
403 | elif quiet==1:
404 | a,b='y','y'
405 | if a=='y' and b=='y':lg_kd_kdz(self.file).unpackkdz()
406 | elif a=='y' and b=='n':lg_kd_kdz(self.file).listkdz()
407 | def lg_dz(self):
408 | if quiet==0:
409 | if input('是否解包LG DZ?y/n>>>')=='n':return
410 | a=int(input('请选择解包模式:1.command 2.func:'))
411 | elif quiet==1:
412 | a=1
413 | lg_kd_kdz(self.file).unpackdz(a)
414 |
415 | def oppo_ozip(self):
416 | global ozipmodelerror
417 | if quiet==0:
418 | if input('是否解密oppo ozip?y/n>>>')=='n':return
419 | if ozipmodelerror==1:
420 | print('''
421 | E:pycrypto模块错误!不支持oppo ozip解包!
422 | 有关pycrypto安装信息,请浏览:
423 | https://github.com/AEnjoy/unpackandroidrom/blob/master/about_pycrypto.md
424 | https://hub.fastgit.org/AEnjoy/unpackandroidrom/blob/master/about_pycrypto.md
425 | ''')
426 | return
427 | ozipdecrypt.main(self.file)
428 | self.file=self.file.replace('.ozip','.zip')
429 | z=zipfile.ZipFile(self.file)
430 | if 'system.new.dat.br' in z.namelist() and 'system.transfer.list' in z.namelist():self.rominfo.brotil=True
431 | else:self.rominfo.newdat=True
432 | rominformation(self.file)
433 | self.unzip()
434 | def unzip(self):#system.img
435 | if self.rominfo.flag==1:
436 | print('无效格式!!!')
437 | sys.exit(1)
438 | if quiet==0:
439 | if input('是否解包卡刷包zip文件?y/n>>>')=='n':
440 | print('取消.')
441 | sys.exit(0)
442 | if self.rominfo.flag==5:
443 | z=zipfile.ZipFile(self.file)
444 | z.extractall(path='flashable')
445 | z.close()
446 | print('功能性卡刷包解包完成.输出目录:flashable')
447 | print('Done.')
448 | sys.exit(0)
449 | if self.rominfo.abflag==True and zipfile.is_zipfile(self.file)==True:
450 | z=zipfile.ZipFile(self.file)
451 | z.extract('payload.bin')
452 | z.close()
453 | self.file='payload.bin'
454 | self.abunpack()
455 | print('Done.')
456 | sys.exit(0)
457 | if self.rominfo.onlyfolder==True:
458 | z=zipfile.ZipFile(self.file)
459 | for name in z.namelist() :
460 | if name.find('system')==0:
461 | z.extract(name)
462 | z.close()
463 | print('Done.')
464 | sys.exit(0)
465 | if self.rominfo.olnyimg==True:
466 | z=zipfile.ZipFile(self.file)
467 | z.extract('system.img')
468 | z.close()
469 | if self.unpacktodir==1:
470 | self.file='system.img'
471 | self.imgunpack()
472 | print('Done.')
473 | sys.exit(0)
474 | if self.rominfo.brotil==True:
475 | z=zipfile.ZipFile(self.file)
476 | z.extract('system.transfer.list')
477 | z.extract('system.new.dat.br')
478 | z.close()
479 | self.brotli()
480 | self.newdatunpack()
481 | if self.unpacktodir==1:
482 | self.file='system.img'
483 | self.imgunpack()
484 | print('Done.')
485 | sys.exit(0)
486 | if self.rominfo.newdat==True:
487 | z=zipfile.ZipFile(self.file)
488 | z.extract('system.transfer.list')
489 | z.extract('system.new.dat')
490 | z.close()
491 | self.newdatunpack()
492 | if self.unpacktodir==1:
493 | self.file='system.img'
494 | self.imgunpack()
495 | print('Done.')
496 | sys.exit(0)
497 | if self.unpacktodir==0:
498 | print('Done! 输出的到的目录: /')
499 | return
500 | else:pass
501 |
502 | def abunpack(self):
503 | if quiet==0:
504 | if input('是否解密payload.bin?y/n>>>')=='n':return
505 | if os.path.exists('output')==False:os.mkdir('output')
506 | payload_dumper.main(self.file)
507 | try:os.remove(self.file)
508 | except:pass
509 | if self.unpacktodir==1:
510 | if os.path.exists('output/system.img'):
511 | self.file='output/system.img'
512 | self.imgunpack()
513 | if os.path.exists('output/system_a.img'):
514 | self.file='output/system_a.img'
515 | self.imgunpack()
516 | if os.path.exists('output/surper.img'):
517 | self.file='output/surper.img'
518 | print('W:动态分区有待测试!')
519 | self.imgunpack()
520 | def imgunpack(self,flag=1):
521 | '''flag: 1mount 2unmount Linux'''
522 | if quiet==0:
523 | if input('是否解包.img?y/n>>>')=='n':return
524 | if self.file.find('super.img')>-1:
525 | print('''W:暂不支持解包动态分区super.img文件!
526 | 正在转换super.img...以便手动解包
527 | ''')
528 | a=''
529 | simg2img.main(self.file,a)
530 | print('输出的文件:'+a)
531 | return
532 | if platform.system()=='Linux':
533 | if flag==1:
534 | os.system('mkdir android-system-img')
535 | os.system('sudo mount %s android-system-img'%self.file)
536 | print('Done!: 挂载镜像到文件夹 android-system-img')
537 | if flag==2:
538 | os.system('sudo umount android-system-img')
539 | os.system('e2fsck -p -f '+self.file)
540 | os.system('resize2fs -M '+self.file)
541 | print('Done!: 保存的镜像 '+self.file)
542 | if platform.system()=='Windows':
543 | url='https://hub.fastgit.org/AEnjoy/adbshellpy/raw/master/Imgextractor.exe'
544 | if os.path.exists('Imgextractor.exe')==False:
545 | try:urllib.request.urlretrieve(url,'Imgextractor.exe')
546 | except:
547 | print('E:下载失败!')
548 | return
549 | os.system('Imgextractor '+self.file)
550 | print('Done!')
551 |
552 | def newdatunpack(self,TRANSFER_LIST_FILE='system.transfer.list', NEW_DATA_FILE='system.new.dat', OUTPUT_IMAGE_FILE='system.img'):
553 | #====================================================
554 | # FILE: sdat2img.py
555 | # AUTHORS: xpirt - luxi78 - howellzhu
556 | # DATE: 2018-10-27 10:33:21 CEST
557 | # Chinese: 神郭
558 | #====================================================
559 | if quiet==0:
560 | if input('是否转换.new.dat?y/n>>>')=='n':return
561 | sdat2img.main(TRANSFER_LIST_FILE,NEW_DATA_FILE,OUTPUT_IMAGE_FILE,0)
562 |
563 | def brotli(self,INPUT_FILE='system.new.dat.br',OUTPUT_FILE='system.new.dat',flag=1):
564 | import brotli as b
565 | if quiet==0:
566 | if input('是否转换.new.dat.br?y/n>>>')=='n':return
567 | if flag==1:
568 | with open(INPUT_FILE, 'rb') as infile:
569 | data = infile.read()
570 | outfile = open(OUTPUT_FILE, 'wb')
571 | data = b.decompress(data)
572 | outfile.write(data)
573 | outfile.close()
574 | infile.close()
575 | if flag==2:
576 | with open(INPUT_FILE, 'rb') as infile:
577 | data = infile.read()
578 | outfile = open(OUTPUT_FILE, 'wb')
579 | data = b.compress(data)
580 | outfile.write(data)
581 | outfile.close()
582 | infile.close()
583 | print('参数无效!')
584 |
585 | def parseArgs():
586 | parser = argparse.ArgumentParser(description='System一键解包工具')
587 | parser.add_argument('-f', '--file', help='欲解包的ROM文件', action='store', required=False, dest='file')
588 | parser.add_argument("-t", "--type", type=str, choices=['kdz', 'dz', 'samsumgodin','abota','flashable','ozip','miuitar','newdat','newdatbr'], help="强制指定输入的文件ROM的类型", required=False)
589 | parser.add_argument('-q', '--quiet', help='安静模式 不询问yes', action='store_true', required=False, dest='quit')
590 | parser.add_argument('-v', '--version', help='显示程序版本信息', action='store_true', required=False, dest='version')
591 | return parser.parse_args()
592 | def main(args=None):
593 | global quiet
594 | if os.path.exists('rom')==False:os.mkdir('rom')
595 | if args.file:rom=rominformation(args.file)
596 | if args.version:
597 | print('Android ROM Unpack Tool \r\n 安卓ROM解包工具 \r\n Version:2.2.4 \r\n BuildDate: 2021-8-22 19:09:49')
598 | sys.exit(0)
599 | else:
600 | file=input('请选择一个处理的ROM>>>')
601 | if file=='':
602 | print('E:请选择一个ROM文件!')
603 | exit(1)
604 | elif os.path.exists(file):rom=rominformation(file)
605 | else:
606 | print('E:请选择一个ROM文件!')
607 | exit(1)
608 | if args.type=='kdz':rom.lgkdz=True
609 | elif args.type=='dz':rom.lgkd=True
610 | elif args.type=='samsumgodin':rom.samsumgodinfile=True
611 | elif args.type=='abota':rom.abflag=True
612 | elif args.type=='ozip':rom.ozip=True
613 | elif args.type=='flashable':rom.flag=5
614 | elif args.type=='miuitar':
615 | rom.miuitar=True
616 | rom.flag=3
617 | elif args.type=='newdat':rom.newdat=True
618 | elif args.type=='newdatbr':rom.brotil=True
619 | if args.quit:quiet=1
620 | unpackrom(args.file,rom)
621 | if __name__ == '__main__':
622 | print('''
623 | **********************************libunpakrom*****************************************
624 | * Android ROM 智能解包工具箱 版本2.2.4 *
625 | * 支持市面上绝大部分Android手机的ROM解包,未来更新后还将支持ROM打包等操作 *
626 | * 功能: *
627 | * ①OPPO OZIP解密 *
628 | * ②Android O+ A/B分区(System As Root) payload.bin 解包 *
629 | * ③Android Q+ 真(虚拟)动态分区payload.bin解包 *
630 | * ④Android L+ .new.dat, .new.dat.br 转换img *
631 | * ⑤Android L+ 分区.img解包 *
632 | * ⑥常规解包,卡刷包解包. *
633 | * ⑦部分ROM卡刷包支持直接读取ROM信息 *
634 | * ⑧Samsung odin .tar.md5 文件解包/获取ROM信息 *
635 | * (仅官方.tar.md5文件支持) 解包.lz4→.img *
636 | * ⑨LG KDZ / DZ 文件解包 *
637 | * ⑩.tar线刷包解包 *
638 | * 支持文件格式: *
639 | * .img/.zip/.tar/.tar.gz/.tar.md5/.new.dat/.new.dat.br/ *
640 | * .kdz/.dz/.ozip/payload.bin/.tgz *
641 | * 项目地址: https://github.com/AEnjoy/unpackandroidrom *
642 | * *
643 | **********************************libunpakrom*****************************************
644 | ''')
645 | args=parseArgs()
646 | main(args)
647 |
--------------------------------------------------------------------------------
/make.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | # make.py
4 | # By : 神郭
5 | import os,zipfile,platform
6 | from shutil import copy,copytree
7 | def main():
8 | print('''make:
9 | [1]:下载Python库Lib依赖 [2]:安装pyinstaller [3]:同步源代码(需要先安装git)
10 | [4]:编译可执行文件 [5]:清理工作目录 [6]:Exit
11 | ''')
12 | a=int(input('>>>'))
13 | if a==1:
14 | l=['lz4','ConfigParser','protobuf','brotli','pycryptodome',
15 | 'docopt','Crypto','zstandard','google','checker','glob2']
16 | for i in l:os.system('pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple '+i)
17 | main()
18 | return
19 | if a==2:
20 | os.system('pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple pyinstaller')
21 | main()
22 | return
23 | if a==3:
24 | os.system('git pull')
25 | main()
26 | return
27 | if a==4:
28 | os.system('pyinstaller -F main.py')
29 | copy('Project.txt', 'dist'+os.sep+'Project.txt')
30 | copy('about_pycrypto.md', 'dist'+os.sep+'about_pycrypto.md')
31 | copy('README.md', 'dist'+os.sep+'README.md')
32 | copy('requirements.txt', 'dist'+os.sep+'requirements.txt')
33 | copy('README_LGKDZ.txt', 'dist'+os.sep+'README_LGKDZ.txt')
34 | copy('README_ozip.md', 'dist'+os.sep+'README_ozip.md')
35 | copy('README_unpayload.md', 'dist'+os.sep+'README_unpayload.md')
36 | copy('README_simg2img.txt', 'dist'+os.sep+'README_simg2img.txt')
37 | copytree('pic', 'dist'+os.sep+'pic')
38 | z = zipfile.ZipFile(platform.system()+'_'+platform.machine()+'.zip', 'w',compression=zipfile.ZIP_DEFLATED,allowZip64=True)
39 | for d in os.listdir('dist'):
40 | z.write('dist'+os.sep+d)
41 | for c in os.listdir('dist'+os.sep+'pic'):
42 | z.write('dist'+os.sep+'pic'+os.sep+c)
43 | z.close()
44 | main()
45 | return
46 | if a==5:
47 | l1=['main.spec','main.zip']
48 | l2=['__pycache__','build','dist','logs','temp']
49 | for i in l1:
50 | try:os.remove(i)
51 | except:pass
52 | for i in l2:
53 | try:os.rmdir(i)
54 | except:pass
55 | main()
56 | return
57 | if a==6:exit()
58 | if __name__ == '__main__':
59 | main()
--------------------------------------------------------------------------------
/ofp_libextract.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | # (c) B.Kerler, MIT license
4 | import os
5 | import sys
6 | from struct import unpack
7 | from struct import unpack
8 |
9 | if len(sys.argv)<2:
10 | print("Usage: ./ofp_libextract.py [Filename.ofp]")
11 | exit(0)
12 |
13 | filename=sys.argv[1]
14 | filesize=os.stat(filename).st_size
15 |
16 | def elfcalcsize(pos):
17 | rf.seek(pos)
18 | data=rf.read(0x200)
19 | if data[:4]!=b"\x7FELF":
20 | print("没有 ELF 发现.")
21 | return 0
22 | sectionheaderoffset=unpack("0):
32 | rf.seek(pos)
33 | data=rf.read(0x20004)
34 | idx=data.find(b"\x7FELF")
35 | if idx!=-1:
36 | length=elfcalcsize(pos+idx)
37 | if length>0x50000 and length<0x200000:
38 | return [(pos+idx),length]
39 | if length>0x200000:
40 | return []
41 | pos-=0x20000
42 | return []
43 |
44 | def seekfordecryptstring(rf):
45 | pos=0
46 | area=[]
47 | old=0
48 | i=0
49 | while(posold:
53 | old=percent
54 | print(f"{percent}% scanned.")
55 | data=rf.read(0x20004)
56 | idx=data.find(b"OPPOENCRYPT!\x00Decrypt failed!")
57 | if idx!=-1:
58 | print(f"Found a possible candidate at: {hex(dinfo[0])}")
59 | dinfo=reverseseekforelf(rf,pos+idx)
60 | if dinfo!=[]:
61 | i += 1
62 | print(f"Extracting possible candidate: {hex(dinfo[0])}, Length:{hex(dinfo[1])} as {str(i)}.elf")
63 | rf.seek(dinfo[0])
64 | data=rf.read(dinfo[1])
65 | with open(f"{str(i)}.elf","wb") as wf:
66 | wf.write(data)
67 | pos+=0x20000
68 | return area
69 |
70 |
71 | def seekforrecovery(rf):
72 | pos=0
73 | area=[]
74 | old=0
75 | i=0
76 | found=0
77 | while(posold:
81 | old=percent
82 | print(f"{percent}% scanned.")
83 | data=rf.read(0x20004)
84 | idx=0
85 | idx=data.find(b"\x1F\x8B\x08\x00\x00\x00\x00\x00\x00",idx)
86 | if idx!=-1:
87 | found+=1
88 | if found>1:
89 | print(f"Found a possible candidate at: {hex(pos+idx)}")
90 | pos=pos+idx
91 | pos2=pos
92 | while(pos2old:
96 | old=percent
97 | print(f"{percent}% scanned.")
98 | data=rf.read(0x20004)
99 | idx2=data.find(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
100 | if idx2!=-1:
101 | print(f"Extracting recovery: {hex(pos)}, Length:{hex(pos2+idx2-pos)} as recovery.cpio.gz")
102 | rf.seek(pos)
103 | data=rf.read(pos2+idx2-pos)
104 | with open(f"recovery{str(found-1)}.cpio.gz","wb") as wf:
105 | wf.write(data)
106 | break;
107 | pos2+=0x20000
108 | pos+=0x20000
109 | return area
110 |
111 | with open(filename,'rb') as rf:
112 | #info=seekfordecryptstring(rf)
113 | seekforrecovery(rf)
114 | print("完成.")
115 |
--------------------------------------------------------------------------------
/ozipdecrypt.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #(c) B. Kerler 2017-2019, licensed under MIT license
4 |
5 | import os
6 | import sys, stat
7 | import shutil
8 | import binascii
9 | from Crypto.Cipher import AES
10 | from zipfile import ZipFile
11 |
12 | keys=[
13 | "2143DCCB21513E39E1DCAFD41ACEDBD7",
14 | "2D23CCBBA1563519CE23C1C4AA1E3412", #A77
15 | "1E38C1B72D522E29E0D4ACD50ACFDCD6",
16 | "D7DBCE1AD4AFDCE1393E5121CBDC4321", #R11s, Plus
17 | "D6DCCF0AD5ACD4E0292E522DB7C1381E", #R9s, Plus, R11
18 | "12341EAAC4C123CE193556A1BBCC232D",
19 | "D4D2CD61D4AFDCE13B5E01221BD14D20", #FindX
20 | "261CC7131D7C1481294E532DB752381E", #FindX
21 | "172B3E14E46F3CE13E2B5121CBDC4321", #Realme 1
22 | "ACAC1E13A72531AE4A1B22BB31C1CC22", #Realme C2
23 | "2132321EA2CA86621A11241ABA512722", #Realme C2
24 | "1CA21E12271335AE33AB81B2A7B14622", #Realme 2 pro
25 | "acaa1e12a71431ce4a1b21bba1c1c6a2", #Realme U1 RMX1831
26 | "acac1e13a72531ae4a1b22bb31c1cc22", #Realme 3 RMX1825EX
27 | "1c4c1ea3a12531ae491b21bb31613c11", #Realme 3 Pro, Realme X
28 | "1c4a11a3a12589ae441a23bb31517733", #Realme X2
29 | "1C4A11A3A22513AE541B53BB31513121", #Realme 5
30 | "D4D2CE11D4AFDCE13B3E0121CBD14D20", #K1
31 | "ACAC1E13A12531AE4A1B21BB31C13C21", #Reno, K3
32 | "ACAC1E13A72431AE4A1B22BBA1C1C6A2", #A9
33 | "1c4c1ea3a12531ae4a1b21bb31c13c21", #Reno 10x zoom PCCM00
34 | "D6EECF0AE5ACD4E0E9FE522DE7CE381E", #mnkey
35 | "D6ECCF0AE5ACD4E0E92E522DE7C1381E", #mkey
36 | "D6DCCF0AD5ACD4E0292E522DB7C1381E", #realkey
37 | "D7DCCE1AD4AFDCE2393E5161CBDC4321", #testkey
38 | "D7DBCE2AD4ADDCE1393E5521CBDC4321", #utilkey
39 | "12cac11211aac3aea2658690122c1e81", #A1,A83t
40 | "2442CE821A4F352E33AE81B22BC1462E", #R17 Pro
41 | ]
42 |
43 |
44 | def keytest(data):
45 | for key in keys:
46 | ctx=AES.new(binascii.unhexlify(key),AES.MODE_ECB)
47 | dat=ctx.decrypt(data)
48 | if (dat[0:4]==b'\x50\x4B\x03\x04'):
49 | print ("找到正确的 AES key: "+key)
50 | return binascii.unhexlify(key)
51 | elif (dat[0:4]==b'\x41\x4E\x44\x52'):
52 | print ("找到正确的 AES key: "+key)
53 | return binascii.unhexlify(key)
54 | return -1
55 |
56 | def del_rw(action, name, exc):
57 | os.chmod(name, stat.S_IWRITE)
58 | os.remove(name)
59 |
60 | def rmrf(path):
61 | if os.path.exists(path):
62 | if os.path.isfile(path):
63 | del_rw("",path,"")
64 | else:
65 | shutil.rmtree(path, onerror=del_rw)
66 |
67 | def main(file='None'):
68 | print ("ozipdecrypt 0.5 (c) B.Kerler 2017-2019")
69 | if (len(sys.argv)!=2) and os.path.exists(file)==False:
70 | print ("usage: ozipdecrypt.py [*.ozip]")
71 | exit(1)
72 | try:file_input=sys.argv[1]
73 | except IndexError:file_input=file
74 |
75 | with open(file_input,'rb') as fr:
76 | magic=fr.read(12)
77 | if (magic==b"OPPOENCRYPT!"):
78 | pk=False
79 | elif magic[:2]==b"PK":
80 | pk=True
81 | else:
82 | print ("ozip has unknown magic, OPPOENCRYPT! expected !")
83 | exit(1)
84 |
85 | if pk==False:
86 | fr.seek(0x1050)
87 | data=fr.read(16)
88 | key=keytest(data)
89 | if (key==-1):
90 | print("未知的AES密钥,请先从Recovery获取解密密钥!!")
91 | exit(1)
92 | ctx=AES.new(key,AES.MODE_ECB)
93 | filename=file_input[:-4]+"zip"
94 | with open(filename,'wb') as wf:
95 | fr.seek(0x1050)
96 | print("Decrypting...")
97 | while (True):
98 | data=fr.read(16)
99 | if len(data)==0:
100 | break
101 | wf.write(ctx.decrypt(data))
102 | data = fr.read(0x4000)
103 | if len(data)==0:
104 | break
105 | wf.write(data)
106 | print("DONE!!")
107 | else:
108 | testkey=True
109 | with ZipFile(file_input,'r') as zipObj:
110 | if os.path.exists('temp'):
111 | rmrf('temp')
112 | os.mkdir('temp')
113 | if os.path.exists('out'):
114 | rmrf('out')
115 | os.mkdir('out')
116 | print("Extracting "+file_input)
117 | zipObj.extractall('temp')
118 | for r, d, f in os.walk('temp'):
119 | for file in f:
120 | rfilename=os.path.join(r, file)
121 | wfilename = os.path.join("out", rfilename[rfilename.rfind('/') + 1:])
122 | with open(rfilename,'rb') as rr:
123 | magic = rr.read(12)
124 | if (magic == b"OPPOENCRYPT!"):
125 | if testkey==True:
126 | with open(os.path.join("temp","boot.img"),"rb") as rt:
127 | rt.seek(0x1050)
128 | data=rt.read(16)
129 | key=keytest(data)
130 | if (key==-1):
131 | print("未知的AES密钥,请先从Recovery获取反向密钥!!")
132 | exit(1)
133 | testkey=False
134 | with open(wfilename,'wb') as wf:
135 | rr.seek(0x10)
136 | dsize=int(rr.read(0x10).replace(b"\x00",b"").decode('utf-8'),10)
137 | rr.seek(0x1050)
138 | print("Decrypting "+rfilename)
139 | flen=os.stat(rfilename).st_size-0x1050
140 |
141 | ctx = AES.new(key, AES.MODE_ECB)
142 | while (dsize>0):
143 | if flen>0x4000:
144 | size=0x4000
145 | else:
146 | size=flen
147 | data = rr.read(size)
148 | if dsizeI', x)[0]
18 |
19 | def u64(x):
20 | return struct.unpack('>Q', x)[0]
21 |
22 | def verify_contiguous(exts):
23 | blocks = 0
24 |
25 | for ext in exts:
26 | if ext.start_block != blocks:
27 | return False
28 |
29 | blocks += ext.num_blocks
30 |
31 | return True
32 |
33 | def data_for_op(op):
34 | global data_offset,p
35 | p.seek(data_offset + op.data_offset)
36 | data = p.read(op.data_length)
37 |
38 | # assert hashlib.sha256(data).digest() == op.data_sha256_hash, 'operation data hash mismatch'
39 |
40 | if op.type == op.REPLACE_XZ:
41 | dec = lzma.LZMADecompressor()
42 | data = dec.decompress(data)
43 | elif op.type == op.REPLACE_BZ:
44 | dec = bz2.BZ2Decompressor()
45 | data = dec.decompress(data)
46 |
47 | return data
48 |
49 | def dump_part(part):
50 | print(part.partition_name)
51 |
52 | out_file = open('output/%s.img' % part.partition_name, 'wb')
53 | h = hashlib.sha256()
54 |
55 | for op in part.operations:
56 | data = data_for_op(op)
57 | h.update(data)
58 | out_file.write(data)
59 |
60 | # assert h.digest() == part.new_partition_info.hash, 'partition hash mismatch'
61 | def parse(p):
62 | global data_offset
63 | magic = p.read(4)
64 | assert magic == b'CrAU'
65 |
66 | file_format_version = u64(p.read(8))
67 | assert file_format_version == 2
68 |
69 | manifest_size = u64(p.read(8))
70 |
71 | metadata_signature_size = 0
72 |
73 | if file_format_version > 1:
74 | metadata_signature_size = u32(p.read(4))
75 |
76 | manifest = p.read(manifest_size)
77 | metadata_signature = p.read(metadata_signature_size)
78 |
79 | data_offset = p.tell()
80 |
81 | dam = um.DeltaArchiveManifest()
82 | dam.ParseFromString(manifest)
83 |
84 | for part in dam.partitions:
85 | # for op in part.operations:
86 | # assert op.type in (op.REPLACE, op.REPLACE_BZ, op.REPLACE_XZ), \
87 | # 'unsupported op'
88 |
89 | # extents = flatten([op.dst_extents for op in part.operations])
90 | # assert verify_contiguous(extents), 'operations do not span full image'
91 |
92 | dump_part(part)
93 | if __name__ == "__main__":
94 | p = open(sys.argv[1], 'rb')
95 | parse(p)
96 |
97 | def main(file):
98 | global p
99 | p = open(file, 'rb')
100 | parse(p)
101 |
--------------------------------------------------------------------------------
/pic/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AEnjoy/unpackandroidrom/473f292d813136700550fe657f7d6a41a10077f0/pic/1.png
--------------------------------------------------------------------------------
/pic/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AEnjoy/unpackandroidrom/473f292d813136700550fe657f7d6a41a10077f0/pic/2.png
--------------------------------------------------------------------------------
/pic/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AEnjoy/unpackandroidrom/473f292d813136700550fe657f7d6a41a10077f0/pic/3.png
--------------------------------------------------------------------------------
/pic/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AEnjoy/unpackandroidrom/473f292d813136700550fe657f7d6a41a10077f0/pic/4.png
--------------------------------------------------------------------------------
/pic/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AEnjoy/unpackandroidrom/473f292d813136700550fe657f7d6a41a10077f0/pic/5.png
--------------------------------------------------------------------------------
/pic/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AEnjoy/unpackandroidrom/473f292d813136700550fe657f7d6a41a10077f0/pic/6.png
--------------------------------------------------------------------------------
/pic/README.txt:
--------------------------------------------------------------------------------
1 | 测试图
--------------------------------------------------------------------------------
/pic/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AEnjoy/unpackandroidrom/473f292d813136700550fe657f7d6a41a10077f0/pic/home.png
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | #该文件用于记录此项目所需依赖.
2 | ConfigParser
3 | protobuf
4 | brotli
5 | pycryptodome
6 | docopt
7 | #stat 已弃用
8 | Crypto
9 | zstandard
10 | google
11 | lz4
12 | glob2
13 | #pyinstaller
14 |
--------------------------------------------------------------------------------
/rimg2sdat.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | #===================================
5 | # File: rimg2sdat.py
6 | # Authors: jazchen
7 | # Date: 2018.07.05 17:39 CST
8 | #===================================
9 |
10 | """
11 | version == 1: Android Lollipop 5.0
12 | version == 2: Android Lollipop 5.1
13 | version == 3: Android Marshmallow 6.x
14 | version == 4: Android Nougat 7.x / Oreo 8.x
15 | """
16 |
17 | from __future__ import print_function
18 |
19 | import os
20 | import sys
21 | import struct
22 | import time
23 |
24 |
25 | def GetRawImageFileHandle(filepath, image_size):
26 | image_name = os.path.basename(filepath)
27 | file_handle = open(filepath, 'rb')
28 |
29 | file_handle.seek(0x438)
30 | magic = struct.unpack(" 0 :
63 | streams = [(range_list[i], range_list[i+1]) for i in range(0, len(range_list), 2)]
64 | commands = []
65 | cmd_range = []
66 |
67 | counter = limit
68 | for begin, end in streams:
69 | length = end - begin
70 |
71 | while (length > 0) :
72 | if length > counter :
73 | cmd_range.append(begin)
74 | cmd_range.append(begin + counter)
75 | begin = begin + counter
76 | length -= counter
77 | counter = 0
78 | else:
79 | cmd_range.append(begin)
80 | cmd_range.append(end)
81 | counter -= length
82 | length = 0
83 |
84 | if counter == 0 :
85 | commands.append(tuple(cmd_range))
86 | cmd_range = []
87 | counter = limit
88 | else:
89 | if len(cmd_range) > 0:
90 | commands.append(tuple(cmd_range))
91 |
92 | return commands
93 | else:
94 | return [tuple(range_list)]
95 |
96 |
97 | def WriteTransfers(prefix, version, rangeSet, write_blocks, total_blocks, nolimit=False):
98 |
99 | rangeSet['erase'] = []
100 |
101 | if version <= 2 :
102 | rangeSet['erase'].append(0)
103 | rangeSet['erase'].append(total_blocks)
104 |
105 | elif version >= 3 :
106 | range_zero = tuple(rangeSet['zero'])
107 | del rangeSet['zero'][:]
108 |
109 | for i in range(0, len(range_zero), 2):
110 | if (range_zero[i+1] - range_zero[i]) > 1024 :
111 | rangeSet['erase'].append(range_zero[i])
112 | rangeSet['erase'].append(range_zero[i+1])
113 | else:
114 | rangeSet['zero'].append(range_zero[i])
115 | rangeSet['zero'].append(range_zero[i+1])
116 | else:
117 | del range_zero
118 |
119 |
120 | if version == 4 and nolimit == False :
121 | rangeSet['new'] = SplitRangeSet(rangeSet['new'], 1024)
122 | rangeSet['zero'] = SplitRangeSet(rangeSet['zero'], 1024)
123 | else:
124 | rangeSet['new'] = SplitRangeSet(rangeSet['new'])
125 | rangeSet['zero'] = SplitRangeSet(rangeSet['zero'])
126 |
127 |
128 | with open(prefix + '.transfer.list', "w") as f:
129 | f.write(str(version) +'\n')
130 | f.write(str(write_blocks) +'\n')
131 |
132 | if version >= 2:
133 | f.write('0\n0\n')
134 |
135 | if len(rangeSet['erase']) > 0:
136 | f.write('erase ' + str(len(rangeSet['erase'])) + ',' + ','.join(map(str,rangeSet['erase'])) +'\n')
137 |
138 | for cmd_range in rangeSet['new']:
139 | f.write('new ' + str(len(cmd_range)) + ',' + ','.join(map(str, cmd_range)) +'\n')
140 |
141 | for cmd_range in rangeSet['zero']:
142 | f.write('zero ' + str(len(cmd_range)) + ',' + ','.join(map(str, cmd_range)) +'\n')
143 |
144 |
145 | def CompressDatFile(in_name, out_name):
146 | import imp
147 |
148 | read_size = 2 ** 24
149 | bc = brotli.Compressor(quality=6, lgwin=24)
150 |
151 | # check which one installed
152 | if imp.find_module('brotli')[0] == None :
153 | # brotlipy package
154 | def compress(uncompress):
155 | return bc.compress(uncompress)
156 | else:
157 | # brotli module
158 | def compress(uncompress):
159 | return bc.process(uncompress)
160 |
161 | with open(out_name, 'wb') as out_file:
162 | with open(in_name, 'rb') as in_file:
163 | while True:
164 | data = in_file.read(read_size)
165 | if not data:
166 | break
167 |
168 | out_file.write(compress(data))
169 |
170 | out_file.write(bc.finish())
171 |
172 |
173 | def Compute(args):
174 | sTime = time.time()
175 |
176 | if args.sha1 == True:
177 | sha1sum = sha1()
178 |
179 | block_size = 4096
180 | image_file = args.image
181 | image_size = os.path.getsize(image_file)
182 |
183 | prefix = args.outdir + os.sep + args.prefix
184 |
185 | fh = GetRawImageFileHandle(image_file, image_size)
186 | fh.seek(0, 0)
187 |
188 | block_count = int(image_size / block_size)
189 | ref_zero = '\0'.encode('utf-8') * block_size
190 |
191 | rangeSet = {'new': [], 'zero': []}
192 | zero_range = 0
193 | write_blocks = 0
194 | last_command = ''
195 |
196 | dat = open(prefix + '.new.dat', 'wb')
197 |
198 | for bindex in range(block_count):
199 | data = fh.read(block_size)
200 |
201 | if data == ref_zero:
202 | command = 'zero'
203 | zero_range += 1
204 | else:
205 | command = 'new'
206 |
207 | # if zero_block range < 128, merge to 'new' command
208 | if 0 < zero_range and zero_range < 128 :
209 | for r in range(zero_range):
210 | if args.sha1 == True:
211 | sha1sum.update(ref_zero)
212 | dat.write(ref_zero)
213 | write_blocks += zero_range
214 | last_command = 'new'
215 | rangeSet['new'].pop()
216 | rangeSet['zero'].pop()
217 |
218 | if args.sha1 == True:
219 | sha1sum.update(data)
220 | dat.write(data)
221 | zero_range = 0
222 | write_blocks += 1
223 |
224 | if command != last_command :
225 | last_command = command
226 |
227 | if bindex == 0:
228 | rangeSet['new'].append(bindex)
229 | else:
230 | rangeSet['new'].append(bindex)
231 | rangeSet['zero'].append(bindex)
232 | else:
233 | rangeSet[command].append(bindex+1)
234 |
235 | dat.close()
236 | fh.close()
237 |
238 | WriteTransfers(prefix, args.version, rangeSet, write_blocks, block_count, args.nolimit)
239 |
240 | tTime = time.time() - sTime
241 | print('')
242 | print(' 转换为分卷的Android数据镜像已完成, 使用 %d seconds' % tTime)
243 |
244 | if args.sha1 == True:
245 | print('\n Get sha1 checksums: ' + sha1sum.hexdigest())
246 |
247 | if args.compress == True:
248 | sTime = time.time()
249 | CompressDatFile(prefix + '.new.dat', prefix + '.new.dat.br')
250 | tTime = time.time() - sTime
251 | print('\n 压缩完成, 使用 %d seconds' % tTime)
252 |
253 | print('')
254 |
255 |
256 | def main(args):
257 | if not os.path.isfile(args.image):
258 | print('raw_image_file 不存在: %s' % args.image)
259 | sys.exit(2)
260 |
261 | if not os.path.isdir(args.outdir):
262 | print('outdir 不存在: %s' % args.outdir)
263 | sys.exit(2)
264 |
265 | if args.sha1 == True:
266 | global sha1
267 | from hashlib import sha1
268 |
269 | if args.compress == True:
270 | if args.version == 4:
271 | global brotli
272 | try:
273 | import brotli
274 | except ImportError:
275 | print('Please install brotli (brotli or brotlipy), use:')
276 | print(' pip install brotli\n Or\n pip%d.%d install brotli' % (sys.version_info.major, sys.version_info.minor))
277 | sys.exit(1)
278 | else:
279 | print('仅版本4支持压缩选项')
280 | sys.exit(1)
281 |
282 | Compute(args)
283 |
284 |
285 | from argparse import ArgumentParser
286 |
287 | parser = ArgumentParser(usage='%(prog)s [OPTION] ')
288 | parser.add_argument('-o', dest='outdir', metavar='outdir', default=".", help='输出目录,默认:当前目录')
289 | parser.add_argument('-p', dest='prefix', metavar='prefix', default="system", help='分卷数据映像的名称(prefix.new.dat),默认: system')
290 | parser.add_argument('-v', dest='version', metavar='version', default=4, type=int, choices=range(1,5), help='transfer 列出版本号, default: 4')
291 | parser.add_argument('-c', dest='compress', action='store_true', default=False, help='使用brotli将new.dat文件压缩为new.dat.br文件。 (警告!仅支持Android 8.1)')
292 | parser.add_argument('-nl', dest='nolimit', action='store_true', default=False, help='当版本为4时,单个命令中没有限制1024个块')
293 | parser.add_argument('-sha1', dest='sha1', action='store_true', default=False, help='获取分卷数据镜像的sha1校验和')
294 | parser.add_argument('image', metavar='raw_image_file', help='输入 system 原镜像文件')
295 |
296 |
297 | if __name__ == '__main__':
298 | # as cli
299 | if len(sys.argv) == 1:
300 | parser.print_help()
301 | sys.exit()
302 | else:
303 | args = parser.parse_args()
304 |
305 | main(args)
306 |
307 | else:
308 | # as module
309 | def process(image, option=''):
310 | options = str(option).split()
311 | options.append(image)
312 |
313 | args = parser.parse_args(options)
314 | main(args)
315 |
--------------------------------------------------------------------------------
/sdat2img.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #====================================================
4 | # FILE: sdat2img.py
5 | # AUTHORS: xpirt - luxi78 - howellzhu
6 | # DATE: 2018-10-27 10:33:21 CEST
7 | # Chinese: 神郭
8 | #====================================================
9 | '''
10 | 相对于原版的改进:
11 | 1.中文
12 | 2.Python3
13 | 3.如若当前路径下存在system.new.dat和system.transfer.list,则不需要输入命令直接运行
14 | 例:
15 | ./sdat2img.py
16 | ./sdat2img.py system.transfer.list system.new.dat
17 | ./sdat2img.py system.transfer.list system.new.dat system.img
18 | '''
19 | from __future__ import print_function
20 | import sys, os, errno
21 |
22 | def main(TRANSFER_LIST_FILE, NEW_DATA_FILE, OUTPUT_IMAGE_FILE,quiet=1):#quiet:0:enable 1:disable
23 | __version__ = '1.2'
24 |
25 | if sys.hexversion < 0x02070000:
26 | print >> sys.stderr, "需要Python 2.7或更新的版本."
27 | try:
28 | input = raw_input
29 | except NameError: pass
30 | input('按 ENTER 退出...')
31 | sys.exit(1)
32 | else:
33 | if quiet==1:
34 | print('sdat2img Version: {}\n'.format(__version__))
35 |
36 | def rangeset(src):
37 | src_set = src.split(',')
38 | num_set = [int(item) for item in src_set]
39 | if len(num_set) != num_set[0]+1:
40 | print('以下数据分析到RangeSet时出错:\n {}'.format(src), file=sys.stderr)
41 | sys.exit(1)
42 |
43 | return tuple ([ (num_set[i], num_set[i+1]) for i in range(1, len(num_set), 2) ])
44 |
45 | def parse_transfer_list_file(path):
46 | trans_list = open(TRANSFER_LIST_FILE, 'r')
47 |
48 | # First line in transfer list is the version number
49 | version = int(trans_list.readline())
50 |
51 | # Second line in transfer list is the total number of blocks we expect to write
52 | new_blocks = int(trans_list.readline())
53 |
54 | if version >= 2:
55 | # Third line is how many stash entries are needed simultaneously
56 | trans_list.readline()
57 | # Fourth line is the maximum number of blocks that will be stashed simultaneously
58 | trans_list.readline()
59 |
60 | # Subsequent lines are all individual transfer commands
61 | commands = []
62 | for line in trans_list:
63 | line = line.split(' ')
64 | cmd = line[0]
65 | if cmd in ['erase', 'new', 'zero']:
66 | commands.append([cmd, rangeset(line[1])])
67 | else:
68 | # Skip lines starting with numbers, they are not commands anyway
69 | if not cmd[0].isdigit():
70 | print('命令 "{}" 无效.'.format(cmd), file=sys.stderr)
71 | trans_list.close()
72 | sys.exit(1)
73 |
74 | trans_list.close()
75 | return version, new_blocks, commands
76 |
77 | BLOCK_SIZE = 4096
78 |
79 | version, new_blocks, commands = parse_transfer_list_file(TRANSFER_LIST_FILE)
80 | if quiet==1:
81 | if version == 1:
82 | print('Android Lollipop 5.0 检测到!\n')
83 | elif version == 2:
84 | print('Android Lollipop 5.1 检测到!\n')
85 | elif version == 3:
86 | print('Android Marshmallow 6.x 检测到!\n')
87 | elif version == 4:
88 | print('Android Nougat 7.x / Oreo 8.x 检测到!\n')
89 | else:
90 | print('Unknown Android version!\n')
91 |
92 | # Don't clobber existing files to avoid accidental data loss
93 | try:
94 | output_img = open(OUTPUT_IMAGE_FILE, 'wb')
95 | except IOError as e:
96 | if e.errno == errno.EEXIST:
97 | print('错误: 输出的文件 "{}" 已经存在'.format(e.filename), file=sys.stderr)
98 | print('移动它, 重命名它, 或选择一个不同的文件名.', file=sys.stderr)
99 | sys.exit(e.errno)
100 | else:
101 | raise
102 |
103 | new_data_file = open(NEW_DATA_FILE, 'rb')
104 | all_block_sets = [i for command in commands for i in command[1]]
105 | max_file_size = max(pair[1] for pair in all_block_sets)*BLOCK_SIZE
106 |
107 | for command in commands:
108 | if command[0] == 'new':
109 | for block in command[1]:
110 | begin = block[0]
111 | end = block[1]
112 | block_count = end - begin
113 | if quiet==1:
114 | print('复制 {} 区段到位置 {}...'.format(block_count, begin))
115 | # Position output file
116 | output_img.seek(begin*BLOCK_SIZE)
117 |
118 | # Copy one block at a time
119 | while(block_count > 0):
120 | output_img.write(new_data_file.read(BLOCK_SIZE))
121 | block_count -= 1
122 | else:
123 | if quiet==1:
124 | print('跳过命令 {}...'.format(command[0]))
125 |
126 | # Make file larger if necessary
127 | if(output_img.tell() < max_file_size):
128 | output_img.truncate(max_file_size)
129 |
130 | output_img.close()
131 | new_data_file.close()
132 | print('完成! 输出的镜像文件: {}'.format(os.path.realpath(output_img.name)))
133 |
134 | if __name__ == '__main__':
135 | try:
136 | TRANSFER_LIST_FILE = str(sys.argv[1])
137 | NEW_DATA_FILE = str(sys.argv[2])
138 | except IndexError:
139 | print('\n用法: sdat2img.py [ ] [system_img]\n')
140 | print(' : transfer list 文件')
141 | print(' : system new dat 文件')
142 | print(' [system_img]: 输出 system 镜像\n\n')
143 | print('Visit xda thread for more information.\n')
144 | try:
145 | input = raw_input
146 | except NameError: pass
147 | if os.path.exists('system.new.dat') and os.path.exists('system.transfer.list'):
148 | NEW_DATA_FILE='system.new.dat'
149 | TRANSFER_LIST_FILE='system.transfer.list'
150 | else:
151 | input('按 ENTER 退出...')
152 | sys.exit()
153 | try:
154 | OUTPUT_IMAGE_FILE = str(sys.argv[3])
155 | except IndexError:
156 | OUTPUT_IMAGE_FILE = 'system.img'
157 | if sys.hexversion < 0x03000000:
158 | import time
159 | print('Warning:For better Chinese support, please use Python 3 or above.\n')
160 | print('The program will continue after 3s.\n')
161 | time.sleep(3)
162 | main(TRANSFER_LIST_FILE, NEW_DATA_FILE, OUTPUT_IMAGE_FILE)
163 |
--------------------------------------------------------------------------------
/simg2img.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # FILE: simg2img.py
5 | #
6 | # USAGE: ./simg2img.py system.img
7 | #
8 | # DESCRIPTION:
9 | #
10 | # AUTHOR: Karl Zheng
11 | # COMPANY: Meizu
12 | # CREATED: 2011年10月18日 15时25分15秒 CST
13 | # REVISION: ---
14 | # MODIFY: AEnjoy
15 | #===============================================================================
16 |
17 | from __future__ import print_function
18 | import os
19 | import sys
20 | import struct
21 |
22 | EXT4_FILE_HEADER_MAGIC = 0xED26FF3A
23 | EXT4_CHUNK_HEADER_SIZE = 12
24 |
25 | class ext4_file_header(object):
26 | def __init__(self, buf):
27 | (self.magic,
28 | self.major,
29 | self.minor,
30 | self.file_header_size,
31 | self.chunk_header_size,
32 | self.block_size,
33 | self.total_blocks,
34 | self.total_chunks,
35 | self.crc32) = struct.unpack(' 0:
65 | chunk_header = ext4_chunk_header(ifd.read(EXT4_CHUNK_HEADER_SIZE))
66 | sector_size = (chunk_header.chunk_size * file_header.block_size) >> 9
67 | #print("ct:%X, cs:%X, ts:%X, ss:%X"%(chunk_header.type, chunk_header.chunk_size, chunk_header.total_size, sector_size))
68 |
69 | if chunk_header.type == 0xCAC1: # raw type
70 | data = ifd.read(chunk_header.total_size - EXT4_CHUNK_HEADER_SIZE)
71 | len_data = len(data)
72 | if len_data != (sector_size << 9):
73 | print("len data:%d, sector_size:%d" % (len_data, sector_size << 9))
74 | sys.exit(4)
75 | else:
76 | print("len data:%d, sector_size:%d" % (len_data, sector_size << 9))
77 | ofd.write(data)
78 | print("raw_chunk ")
79 | print("write raw data in %d size %d \n" % (sector_base, sector_size))
80 | print("output len:%x" % (output_len + len_data))
81 | else:
82 | len_data = sector_size << 9
83 | if chunk_header.type == 0xCAC2: # TYPE_FILL
84 | print("fill_chunk \n")
85 | print("chunk_size:%x" % chunk_header.chunk_size)
86 | print("output len:%x" % (output_len + len_data))
87 | else:
88 | if chunk_header.type == 0xCAC3: # TYPE_DONT_CARE
89 | print("none chunk at chunk:%d" % (file_header.total_chunks - total_chunks))
90 | print("data_size:0x%x, chunk_size:%d, block_size:%d" % (len_data,
91 | chunk_header.chunk_size, file_header.block_size))
92 | else:
93 | print("unknown type!!")
94 | print("output len:%x" % (output_len + len_data))
95 |
96 | ofd.write(struct.pack("B", 0) * len_data)
97 | output_len += len_data
98 | sector_base += sector_size
99 |
100 | total_chunks -= 1
101 | print("remain chunks = %d "% total_chunks)
102 |
103 | print("write done")
104 | return 0
105 |
106 | if __name__ == "__main__":
107 | if len(sys.argv) == 2 and os.path.exists(sys.argv[1]):
108 | main(sys.argv[1])
109 | else:
110 | print("required existing input file")
111 | sys.exit(1)
112 |
113 |
--------------------------------------------------------------------------------
/unkdz.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | """
5 | Copyright (C) 2016 Elliott Mitchell
6 | Copyright (C) 2013 IOMonster (thecubed on XDA)
7 |
8 | This program is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | This program is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with this program. If not, see .
20 | """
21 |
22 | from __future__ import print_function
23 | import os
24 | import argparse
25 | import sys
26 | from binascii import b2a_hex
27 |
28 |
29 | import kdz
30 |
31 |
32 | class KDZFileTools(kdz.KDZFile):
33 | """
34 | LGE KDZ File tools
35 | """
36 |
37 | # Setup variables
38 | partitions = []
39 | outdir = "kdzextracted"
40 | infile = None
41 |
42 | kdz_header = {
43 | b"\x28\x05\x00\x00"b"\x34\x31\x25\x80": 0,
44 | b"\x18\x05\x00\x00"b"\x32\x79\x44\x50": 1,
45 | kdz.KDZFile._dz_header: 2,
46 | }
47 |
48 |
49 | def readKDZHeader(self):
50 | """
51 | Reads the KDZ header, and returns a single kdz_item
52 | in the form as defined by self._dz_format_dict
53 | """
54 |
55 | # Read a whole DZ header
56 | buf = self.infile.read(self._dz_length)
57 |
58 | # "Make the item"
59 | # Create a new dict using the keys from the format string
60 | # and the format string itself
61 | # and apply the format to the buffer
62 | kdz_item = dict(zip(
63 | self._dz_format_dict.keys(),
64 | self._dz_struct.unpack(buf)
65 | ))
66 |
67 | # Collapse (truncate) each key's value if it's listed as collapsible
68 | for key in self._dz_collapsibles:
69 | if type(kdz_item[key]) is str or type(kdz_item[key]) is bytes:
70 | kdz_item[key] = kdz_item[key].rstrip(b'\x00')
71 | if b'\x00' in kdz_item[key]:
72 | print("[!] Warning:发现多余的数据"+key, file=sys.stderr)
73 | #sys.exit(1)
74 | elif type(kdz_item[key]) is int:
75 | if kdz_item[key] != 0:
76 | print('[!] Error: 空间 "'+key+'" is 没有0 ('+b2a_hex(kdz_item[key])+')', file=sys.stderr)
77 | sys.exit(1)
78 | else:
79 | print("[!] Error: 内部错误", file=sys.stderr)
80 | sys.exit(-1)
81 |
82 | return kdz_item
83 |
84 | def getPartitions(self):
85 | """
86 | Returns the list of partitions from a KDZ file containing multiple segments
87 | """
88 |
89 | # Setup initial values
90 | last = False
91 | cont = not last
92 | self.dataStart = 1<<63
93 |
94 | while cont:
95 |
96 | # Read the current KDZ header
97 | kdz_sub = self.readKDZHeader()
98 |
99 | # Add it to our list
100 | self.partitions.append(kdz_sub)
101 |
102 | # Update start of data, if needed
103 | if kdz_sub['offset'] < self.dataStart:
104 | self.dataStart = kdz_sub['offset']
105 |
106 | # Was it the last one?
107 | cont = not last
108 |
109 | # Check for end of headers
110 | nextchar = self.infile.read(1)
111 | # Is this the last KDZ header? (ctrl-C, how appropos)
112 | if nextchar == b'\x03':
113 | last = True
114 | # Alternative, immediate end
115 | elif nextchar == b'\x00':
116 | cont = False
117 | # Rewind file pointer 1 byte
118 | else:
119 | self.infile.seek(-1, os.SEEK_CUR)
120 |
121 | # Record where headers end
122 | self.headerEnd = self.infile.tell()
123 |
124 | # Paranoia check for an updated file format
125 | buf = self.infile.read(self.dataStart - self.headerEnd - 1)
126 | if len(buf.lstrip(b'\x00')) > 0:
127 | print("[!] Warning: 标头和有效负载之间的数据! (offsets {:d} to {:d})".format(self.headerEnd, self.dataStart), file=sys.stderr)
128 | self.hasExtra = True
129 |
130 | # Make partition list
131 | return [(x['name'],x['length']) for x in self.partitions]
132 |
133 | def extractPartition(self,index):
134 | """
135 | Extracts a partition from a KDZ file
136 | """
137 |
138 | currentPartition = self.partitions[index]
139 |
140 | # Seek to the beginning of the compressed data in the specified partition
141 | self.infile.seek(currentPartition['offset'], os.SEEK_SET)
142 |
143 | # Ensure that the output directory exists
144 | if not os.path.exists(self.outdir):
145 | os.makedirs(self.outdir)
146 |
147 | # Open the new file for writing
148 | outfile = open(os.path.join(self.outdir,currentPartition['name'].decode("utf8")), 'wb')
149 |
150 | # Use 1024 byte chunks
151 | chunkSize = 1024
152 |
153 | # uncomment to prevent runaways
154 | #for x in xrange(10):
155 |
156 | while True:
157 |
158 | # Read file in 1024 byte chunks
159 | outfile.write(self.infile.read(chunkSize))
160 |
161 | # If the output file + chunkSize would be larger than the input data
162 | if outfile.tell() + chunkSize >= currentPartition['length']:
163 | # Change the chunk size to be the difference between the length of the input and the current length of the output
164 | outfile.write(self.infile.read(currentPartition['length'] - outfile.tell() ))
165 | # Prevent runaways!
166 | break
167 |
168 | # Close the file
169 | outfile.close()
170 |
171 | def saveExtra(self):
172 | """
173 | Save the extra data that has appeared between headers&files
174 | """
175 |
176 | try:
177 | if not self.hasExtra:
178 | return
179 | except AttributeError:
180 | return
181 |
182 | filename = os.path.join(self.outdir, "kdz_extras.bin")
183 |
184 | extra = open(filename, "wb")
185 |
186 | print("[+] 提取额外的数据到" + filename)
187 |
188 | self.infile.seek(self.headerEnd, os.SEEK_SET)
189 |
190 | total = self.dataStart - self.headerEnd
191 | while total > 0:
192 | count = 4096 if 4096 < total else total
193 |
194 | buf = self.infile.read(count)
195 | extra.write(buf)
196 |
197 | total -= count
198 |
199 | extra.close()
200 |
201 | def saveParams(self):
202 | """
203 | Save the parameters for creating a compatible file
204 | """
205 |
206 | params = open(os.path.join(self.outdir, ".kdz.params"), "wt")
207 | params.write('# saved parameters from the file "{:s}"\n'.format(self.kdzfile))
208 | params.write("version={:d}\n".format(self.header_type))
209 | params.write("# note, this is actually quite fluid, dataStart just needs to be large enough\n")
210 | params.write("# for headers not to overwrite data; roughly 16 bytes for overhead plus 272\n")
211 | params.write("# bytes per file should be sufficient (but not match original)\n")
212 | params.write("dataStart={:d}\n".format(self.dataStart))
213 | params.write("# embedded files\n")
214 |
215 | out = []
216 | i = 0
217 | for p in self.partitions:
218 | out.append({'name': p['name'], 'data': p['offset'], 'header': i})
219 | i += 1
220 |
221 | out.sort(key=lambda p: p['data'])
222 |
223 | i = 0
224 | for p in out:
225 | params.write("payload{:d}={:s}\n".format(i, p['name'].decode("utf8")))
226 | params.write("payload{:d}head={:d}\n".format(i, p['header']))
227 | i += 1
228 |
229 | params.close()
230 |
231 | def parseArgs(self):
232 | # Parse arguments
233 | parser = argparse.ArgumentParser(description='LG KDZ File Extractor最初由IOMonster提供')
234 | parser.add_argument('-f', '--file', help='要读的KDZ文件', action='store', required=True, dest='kdzfile')
235 | group = parser.add_mutually_exclusive_group(required=True)
236 | group.add_argument('-l', '--list', help='列出分区', action='store_true', dest='listOnly')
237 | group.add_argument('-x', '--extract', help='解压所有分区', action='store_true', dest='extractAll')
238 | group.add_argument('-s', '--single', help='通过ID解压单个分区', action='store', dest='extractID', type=int)
239 | parser.add_argument('-d', '--dir', '-o', '--out', help='输出目录', action='store', dest='outdir')
240 | #1.kdzfile 2.listOnly 3.extractAll 4.extractID(int) 5.outdir
241 | return parser.parse_args()
242 |
243 | def openFile(self, kdzfile):
244 | # Open the file
245 | try:
246 | self.infile = open(kdzfile, "rb")
247 | except IOError as err:
248 | print(err, file=sys.stderr)
249 | sys.exit(1)
250 |
251 | # Get length of whole file
252 | self.infile.seek(0, os.SEEK_END)
253 | # os.seek() doesn't return current position?!
254 | self.kdz_length = self.infile.tell()
255 | self.infile.seek(0, os.SEEK_SET)
256 |
257 | # Verify KDZ header
258 | verify_header = self.infile.read(8)
259 |
260 | if verify_header not in self.kdz_header:
261 | print("[!] Error: 不支持的KDZ文件格式.")
262 | print('[ ] Received header "{:s}".'.format(" ".join(b2a_hex(n) for n in verify_header)))
263 | sys.exit(1)
264 |
265 | self.header_type = self.kdz_header[verify_header]
266 |
267 |
268 | def cmdExtractSingle(self, partID):
269 | print("[+] 从中提取单个分区 v{:d} file!\n".format(self.header_type))
270 | print("[+] 提取 " + str(self.partList[partID][0]) + " to " + os.path.join(self.outdir,self.partList[partID][0].decode("utf8")))
271 | self.extractPartition(partID)
272 |
273 | def cmdExtractAll(self):
274 | print("[+] 从 v{:d} 提取所有分区文件!\n".format(self.header_type))
275 | for part in enumerate(self.partList):
276 | print("[+] 提取 " + part[1][0].decode("utf8") + " to " + os.path.join(self.outdir,part[1][0].decode("utf8")))
277 | self.extractPartition(part[0])
278 | self.saveExtra()
279 | self.saveParams()
280 |
281 | def cmdListPartitions(self):
282 | print("[+] KDZ 分区列表 (format v{:d})\n=========================================".format(self.header_type))
283 | for part in enumerate(self.partList):
284 | print("{:2d} : {:s} ({:d} bytes)".format(part[0], part[1][0].decode("utf8"), part[1][1]))
285 |
286 | def main(self):
287 | args = self.parseArgs()
288 | self.kdzfile = args.kdzfile
289 | self.openFile(args.kdzfile)
290 | self.partList = self.getPartitions()
291 |
292 | if args.outdir:
293 | self.outdir = args.outdir
294 |
295 | if args.listOnly:
296 | self.cmdListPartitions()
297 |
298 | elif args.extractID != None:
299 | if args.extractID >= 0 and args.extractID < len(self.partList):
300 | self.cmdExtractSingle(args.extractID)
301 | else:
302 | print("[!] 片段 {:d} 超出范围!".format(args.extractID), file=sys.stderr)
303 |
304 | elif args.extractAll:
305 | self.cmdExtractAll()
306 |
307 | def mainbyclass(self,kdzfile,outdir=os.getcwd(),extractID=None,listOnly=False,extractAll=True):#extractID(int) import 初始化
308 | #if init==False:return #通过命令行自动运行的使用main()初始化
309 | self.kdzfile=kdzfile
310 | self.openFile(kdzfile)
311 | self.partList = self.getPartitions()
312 | self.outdir = outdir
313 | if listOnly:self.cmdListPartitions()
314 | elif extractID != None:
315 | if extractID >= 0 and extractID < len(self.partList):
316 | self.cmdExtractSingle(extractID)
317 | else:
318 | print("[!] 片段 {:d} 超出范围!".format(extractID), file=sys.stderr)
319 | elif extractAll:
320 | self.cmdExtractAll()
321 |
322 |
323 |
324 | if __name__ == "__main__":
325 | kdztools = KDZFileTools()
326 | kdztools.main() #命令行执行初始化
327 |
328 |
--------------------------------------------------------------------------------