├── .gdbinit
├── .gitignore
├── LICENSE
├── README.md
├── angelheap
├── README.md
├── angelheap.py
├── command_wrapper.py
├── gdbinit.py
└── utils.py
├── pwndbg
├── README.md
├── angelheap.py
├── commands
│ ├── angelheap.py
│ └── pwngdb.py
└── pwngdb.py
└── pwngdb.py
/.gdbinit:
--------------------------------------------------------------------------------
1 | source ~/peda/peda.py
2 | source ~/Pwngdb/pwngdb.py
3 | source ~/Pwngdb/angelheap/gdbinit.py
4 |
5 | define hook-run
6 | python
7 | import angelheap
8 | angelheap.init_angelheap()
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 |
27 | # PyInstaller
28 | # Usually these files are written by a python script from a template
29 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
30 | *.manifest
31 | *.spec
32 |
33 | # Installer logs
34 | pip-log.txt
35 | pip-delete-this-directory.txt
36 |
37 | # Unit test / coverage reports
38 | htmlcov/
39 | .tox/
40 | .coverage
41 | .coverage.*
42 | .cache
43 | nosetests.xml
44 | coverage.xml
45 | *,cover
46 | .hypothesis/
47 |
48 | # Translations
49 | *.mo
50 | *.pot
51 |
52 | # Django stuff:
53 | *.log
54 |
55 | # Sphinx documentation
56 | docs/_build/
57 |
58 | # PyBuilder
59 | target/
60 |
61 | #Ipython Notebook
62 | .ipynb_checkpoints
63 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
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 | {one line to give the program's name and a brief idea of what it does.}
635 | Copyright (C) {year} {name of author}
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 | {project} Copyright (C) {year} {fullname}
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pwngdb
2 |
3 | GDB for pwn.
4 |
5 | ## Install
6 |
7 | ### install
8 | cd ~/
9 | git clone https://github.com/scwuaptx/Pwngdb.git
10 | cp ~/Pwngdb/.gdbinit ~/
11 |
12 | If you dont want to use gdb-peda , you can modify the gdbinit to remove it.
13 |
14 | ### pwndbg
15 |
16 | If you only want to install with pwndbg, see [pwndbg/README.md](pwndbg/README.md)
17 |
18 | ### Heapinfo
19 |
20 | If you want to use the feature of heapinfo and tracemalloc , you need to install libc debug file (libc6-dbg & libc6-dbg:i386 for debian package)
21 |
22 | ## Features
23 |
24 | + `libc` : Print the base address of libc
25 | + `ld` : Print the base address of ld
26 | + `codebase` : Print the base of code segment
27 | + `heap` : Print the base of heap
28 | + `got` : Print the Global Offset Table infomation
29 | + `dyn` : Print the Dynamic section infomation
30 | + `findcall` : Find some function call
31 | + `bcall` : Set the breakpoint at some function call
32 | + `tls` : Print the thread local storage address
33 | + `at` : Attach by process name
34 | + `findsyscall` : Find the syscall
35 | + `fmtarg` : Calculate the index of format string
36 | + You need to stop on printf which has vulnerability.
37 | + `force` : Calculate the nb in the house of force.
38 | + `heapinfo` : Print some infomation of heap
39 | + heapinfo (Address of arena)
40 | + default is the arena of current thread
41 | + If tcache is enable, it would show infomation of tcache entry
42 | + `heapinfoall` : Print some infomation of heap (all threads)
43 | + `arenainfo` : Print some infomation of all arena
44 | + `chunkinfo`: Print the infomation of chunk
45 | + chunkinfo (Address of victim)
46 | + `chunkptr` : Print the infomation of chunk
47 | + chunkptr (Address of user ptr)
48 | + `mergeinfo` : Print the infomation of merge
49 | + mergeinfo (Address of victim)
50 | + `printfastbin` : Print some infomation of fastbin
51 | + `tracemalloc on` : Trace the malloc and free and detect some error .
52 | + You need to run the process first than `tracemalloc on`, it will record all of the malloc and free.
53 | + You can set the `DEBUG` in pwngdb.py , than it will print all of the malloc and free infomation such as the screeshot.
54 | + `parseheap` : Parse heap layout
55 | + `magic` : Print useful variable and function in glibc
56 | + `fp` : show FILE structure
57 | + fp (Address of FILE)
58 | + `fpchain`: show linked list of FILE
59 | + `orange` : Test `house of orange` condition in the `_IO_flush_lockp`
60 | + orange (Address of FILE)
61 | + glibc version <= 2.23
62 |
63 |
64 | ## Screenshot
65 |
66 | + Chunkinfo
67 |
68 | 
69 | + Mergeinfo
70 |
71 | 
72 | + Heapinfo
73 |
74 | 
75 | + Heapinfoall
76 |
77 | 
78 |
79 | + parseheap
80 |
81 | 
82 |
83 | + tracemalloc
84 |
85 | 
86 |
87 | + magic
88 | 
89 |
--------------------------------------------------------------------------------
/angelheap/README.md:
--------------------------------------------------------------------------------
1 | angelheap
2 | ===============
3 | All the heap features (`heapinfo`, `tracemalloc`...) have been seperated from Pwngdb and integrated into an independent module, so everyone can use these features by adding the following lines into their own `.gdbinit`:
4 | ```
5 | source ~/Pwngdb/angelheap/gdbinit.py
6 |
7 | define hook-run
8 | python
9 | import angelheap
10 | angelheap.init_angelheap()
11 | end
12 | end
13 | ```
14 |
--------------------------------------------------------------------------------
/angelheap/angelheap.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | #!/usr/bin/env python3
3 | # -*- coding: utf-8 -*-
4 |
5 | import gdb
6 | import subprocess
7 | import re
8 | import copy
9 | import struct
10 | import os
11 | # main_arena
12 | main_arena = 0
13 | main_arena_off = 0
14 |
15 | # thread
16 | thread_arena = 0
17 | enable_thread = False
18 | tcache_enable = False
19 | tcache = None
20 | tcache_max_bin = 0
21 | tcache_counts_size = 1
22 |
23 | # chunks
24 | top = {}
25 | fastbinsize = 13
26 | fastbin = []
27 | fastchunk = [] #save fastchunk address for chunkinfo check
28 | tcache_entry = []
29 | tcache_count = []
30 | all_tcache_entry = [] #save tcache address for chunkinfo check
31 | last_remainder = {}
32 | unsortbin = []
33 | smallbin = {} #{size:bin}
34 | largebin = {}
35 | system_mem = 0x21000
36 |
37 | # chunk recording
38 | freememoryarea = {} #using in parse
39 | allocmemoryarea = {}
40 | freerecord = {} # using in trace
41 |
42 | # setting for tracing memory allocation
43 | enable_reveal_ptr = False
44 | tracelargebin = True
45 | inmemalign = False
46 | inrealloc = False
47 | print_overlap = True
48 | DEBUG = True #debug msg (free and malloc) if you want
49 |
50 | # breakpoints for tracing
51 | mallocbp = None
52 | freebp = None
53 | memalignbp = None
54 | reallocbp = None
55 |
56 | # architecture setting
57 | capsize = 0
58 | word = ""
59 | arch = ""
60 | libc_version = 0
61 |
62 | #condition
63 | corruptbin = False
64 |
65 | def u32(data,fmt="--------------------------------------------------------------------------------------<\033[37m")
109 | msg = "\033[33mmalloc(0x%x)\033[37m" % self.arg
110 | print("%-40s = 0x%x \033[31m overlap detected !! (0x%x)\033[37m" % (msg,chunk["addr"]+capsize*2,overlap["addr"]))
111 | print("\033[34m>--------------------------------------------------------------------------------------<\033[37m")
112 | else :
113 | print("\033[31moverlap detected !! (0x%x)\033[37m" % overlap["addr"])
114 | del allocmemoryarea[hex(overlap["addr"])]
115 | else :
116 | if DEBUG:
117 | msg = "\033[33mmalloc(0x%x)\033[37m" % self.arg
118 | print("%-40s = 0x%x" % (msg,chunk["addr"] + capsize*2))
119 | allocmemoryarea[hex(chunk["addr"])] = copy.deepcopy((chunk["addr"],chunk["addr"]+chunk["size"],chunk))
120 | if hex(chunk["addr"]) in freerecord :
121 | freechunktuple = freerecord[hex(chunk["addr"])]
122 | freechunk = freechunktuple[2]
123 | splitchunk = {}
124 | del freerecord[hex(chunk["addr"])]
125 | if chunk["size"] != freechunk["size"] :
126 | splitchunk["addr"] = chunk["addr"] + chunk["size"]
127 | splitchunk["size"] = freechunk["size"] - chunk["size"]
128 | freerecord[hex(splitchunk["addr"])] = copy.deepcopy((splitchunk["addr"],splitchunk["addr"]+splitchunk["size"],splitchunk))
129 | if self.arg >= 128*capsize :
130 | Malloc_consolidate()
131 |
132 | class Malloc_Bp_handler(gdb.Breakpoint):
133 | def stop(self):
134 | if len(arch) == 0 :
135 | getarch()
136 | if arch == "x86-64":
137 | reg = "$rsi"
138 | arg = int(gdb.execute("info register " + reg,to_string=True).split()[1].strip(),16)
139 | else :
140 | # for _int_malloc in x86's glibc (unbuntu 14.04 & 16.04), size is stored in edx
141 | reg = "$edx"
142 | arg = int(gdb.execute("info register " + reg,to_string=True).split()[1].strip(),16)
143 | Malloc_bp_ret(arg)
144 | return False
145 |
146 | class Free_bp_ret(gdb.FinishBreakpoint):
147 | def __init__(self):
148 | gdb.FinishBreakpoint.__init__(self,gdb.newest_frame(),internal=True)
149 | self.silent = True
150 |
151 | def stop(self):
152 | Malloc_consolidate()
153 | return False
154 |
155 | class Free_Bp_handler(gdb.Breakpoint):
156 |
157 | def stop(self):
158 | global allocmemoryarea
159 | global freerecord
160 | global inmemalign
161 | global inrealloc
162 | get_top_lastremainder()
163 |
164 | if len(arch) == 0 :
165 | getarch()
166 | if arch == "x86-64":
167 | reg = "$rsi"
168 | result = int(gdb.execute("info register " + reg,to_string=True).split()[1].strip(),16) + 0x10
169 | else :
170 | # for _int_free in x86's glibc (unbuntu 14.04 & 16.04), chunk address is stored in edx
171 | reg = "$edx"
172 | result = int(gdb.execute("info register " + reg,to_string=True).split()[1].strip(),16) + 0x8
173 | chunk = {}
174 | if inmemalign or inrealloc:
175 | Update_alloca()
176 | inmemalign = False
177 | inrealloc = False
178 | prevfreed = False
179 | chunk["addr"] = result - capsize*2
180 |
181 | cmd = "x/" +word + hex(chunk["addr"] + capsize)
182 | size = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
183 | chunk["size"] = size & 0xfffffffffffffff8
184 | if (size & 1) == 0 :
185 | prevfreed = True
186 | # overlap,status = check_overlap(chunk["addr"],chunk["size"],freememoryarea)
187 | overlap,status = check_overlap(chunk["addr"],chunk["size"],freerecord)
188 | if overlap and status == "error" :
189 | if DEBUG :
190 | msg = "\033[32mfree(0x%x)\033[37m (size = 0x%x)" % (result,chunk["size"])
191 | print("\033[34m>--------------------------------------------------------------------------------------<\033[37m")
192 | print("%-25s \033[31m double free detected !! (0x%x(size:0x%x))\033[37m" % (msg,overlap["addr"],overlap["size"]))
193 | print("\033[34m>--------------------------------------------------------------------------------------<\033[37m",end="")
194 | else :
195 | print("\033[31mdouble free detected !! (0x%x)\033[37m" % overlap["addr"])
196 | del freerecord[hex(overlap["addr"])]
197 | else :
198 | if DEBUG :
199 | msg = "\033[32mfree(0x%x)\033[37m" % result
200 | print("%-40s (size = 0x%x)" % (msg,chunk["size"]),end="")
201 |
202 | if chunk["size"] <= 0x80 :
203 | freerecord[hex(chunk["addr"])] = copy.deepcopy((chunk["addr"],chunk["addr"]+chunk["size"],chunk))
204 | if DEBUG :
205 | print("")
206 | if hex(chunk["addr"]) in allocmemoryarea :
207 | del allocmemoryarea[hex(chunk["addr"])]
208 | return False
209 |
210 | prevchunk = {}
211 | if prevfreed :
212 | cmd = "x/" +word + hex(chunk["addr"])
213 | prevchunk["size"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16) & 0xfffffffffffffff8
214 | prevchunk["addr"] = chunk["addr"] - prevchunk["size"]
215 | if hex(prevchunk["addr"]) not in freerecord :
216 | print("\033[31m confuse in prevchunk 0x%x" % prevchunk["addr"])
217 | else :
218 | prevchunk["size"] += chunk["size"]
219 | del freerecord[hex(prevchunk["addr"])]
220 |
221 | nextchunk = {}
222 | nextchunk["addr"] = chunk["addr"] + chunk["size"]
223 |
224 | if nextchunk["addr"] == top["addr"] :
225 | if hex(chunk["addr"]) in allocmemoryarea :
226 | del allocmemoryarea[hex(chunk["addr"])]
227 | Free_bp_ret()
228 | if DEBUG :
229 | print("")
230 | return False
231 |
232 | cmd = "x/" + word + hex(nextchunk["addr"] + capsize)
233 | nextchunk["size"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16) & 0xfffffffffffffff8
234 | cmd = "x/" + word + hex(nextchunk["addr"] + nextchunk["size"] + capsize)
235 | nextinused = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16) & 1
236 |
237 | if nextinused == 0 and prevfreed: #next chunk is freed
238 | if hex(nextchunk["addr"]) not in freerecord :
239 | print("\033[31m confuse in nextchunk 0x%x" % nextchunk["addr"])
240 | else :
241 | prevchunk["size"] += nextchunk["size"]
242 | del freerecord[hex(nextchunk["addr"])]
243 | if nextinused == 0 and not prevfreed:
244 | if hex(nextchunk["addr"]) not in freerecord :
245 | print("\033[31m confuse in nextchunk 0x%x" % nextchunk["addr"])
246 | else :
247 | chunk["size"] += nextchunk["size"]
248 | del freerecord[hex(nextchunk["addr"])]
249 | if prevfreed :
250 | if hex(chunk["addr"]) in allocmemoryarea :
251 | del allocmemoryarea[hex(chunk["addr"])]
252 | chunk = prevchunk
253 |
254 | if DEBUG :
255 | print("")
256 | freerecord[hex(chunk["addr"])] = copy.deepcopy((chunk["addr"],chunk["addr"]+chunk["size"],chunk))
257 | if hex(chunk["addr"]) in allocmemoryarea :
258 | del allocmemoryarea[hex(chunk["addr"])]
259 | if chunk["size"] > 65536 :
260 | Malloc_consolidate()
261 | return False
262 |
263 | class Memalign_Bp_handler(gdb.Breakpoint):
264 | def stop(self):
265 | global inmemalign
266 | inmemalign = True
267 | return False
268 |
269 | class Realloc_Bp_handler(gdb.Breakpoint):
270 | def stop(self):
271 | global inrealloc
272 | inrealloc = True
273 | return False
274 |
275 | def Update_alloca():
276 | global allocmemoryarea
277 | if capsize == 0:
278 | getarch()
279 | for addr,(start,end,chunk) in allocmemoryarea.items():
280 | cmd = "x/" + word + hex(chunk["addr"] + capsize*1)
281 | cursize = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16) & 0xfffffffffffffff8
282 |
283 | if cursize != chunk["size"]:
284 | chunk["size"] = cursize
285 | allocmemoryarea[hex(chunk["addr"])]= copy.deepcopy((start,start + cursize,chunk))
286 |
287 |
288 | def Malloc_consolidate(): #merge fastbin when malloc a large chunk or free a very large chunk
289 | global fastbin
290 | global freerecord
291 |
292 | if capsize == 0 :
293 | getarch()
294 | freerecord = {}
295 | if not get_heap_info():
296 | print("Can't find heap info")
297 | return
298 | freerecord = copy.deepcopy(freememoryarea)
299 |
300 | def getarch():
301 | global capsize
302 | global word
303 | global arch
304 |
305 | data = gdb.execute('show arch',to_string = True)
306 | tmp = re.search("currently.*",data)
307 | if tmp :
308 | info = tmp.group()
309 | if "x86-64" in info:
310 | capsize = 8
311 | word = "gx "
312 | arch = "x86-64"
313 | return "x86-64"
314 | elif "aarch64" in info :
315 | capsize = 8
316 | word = "gx "
317 | arch = "aarch64"
318 | return "aarch64"
319 | elif "arm" in info :
320 | capsize = 4
321 | word = "wx "
322 | arch = "arm"
323 | return "arm"
324 | else :
325 | word = "wx "
326 | capsize = 4
327 | arch = "i386"
328 | return "i386"
329 | else :
330 | return "error"
331 |
332 | def infoprocmap():
333 | """ Use gdb command 'info proc map' to get the memory mapping """
334 | """ Notice: No permission info """
335 | resp = gdb.execute("info proc map", to_string=True).split("\n")
336 | resp = '\n'.join(resp[i] for i in range(4, len(resp))).strip().split("\n")
337 | infomap = ""
338 | for l in resp:
339 | line = ""
340 | first = True
341 | for sep in l.split(" "):
342 | if len(sep) != 0:
343 | if first: # start address
344 | line += sep + "-"
345 | first = False
346 | else:
347 | line += sep + " "
348 | line = line.strip() + "\n"
349 | infomap += line
350 | return infomap
351 |
352 | def procmap():
353 | data = gdb.execute('info proc exe',to_string = True)
354 | pid = re.search('process.*',data)
355 | if pid :
356 | pid = pid.group()
357 | pid = pid.split()[1]
358 | fpath = "/proc/" + pid + "/maps"
359 | if os.path.isfile(fpath): # if file exist, read memory mapping directly from file
360 | maps = open(fpath)
361 | infomap = maps.read()
362 | maps.close()
363 | return infomap
364 | else: # if file doesn't exist, use 'info proc map' to get the memory mapping
365 | return infoprocmap()
366 | else :
367 | return "error"
368 |
369 | def libcbase():
370 | infomap = procmap()
371 | data = re.search(r".*libc-.*\.so",infomap)
372 | if data :
373 | libcaddr = data.group().split("-")[0]
374 | return int(libcaddr,16)
375 | else :
376 | return 0
377 |
378 | def get_libc_version():
379 | global libc_version
380 | try :
381 | libc_version = float(gdb.execute("x/s __libc_version",to_string = True).split()[2].strip("\""))
382 | except :
383 | print("Can not get libc version")
384 |
385 |
386 | def getoff(sym):
387 | libc = libcbase()
388 | if type(sym) is int :
389 | return sym-libc
390 | else :
391 | try :
392 | data = gdb.execute("x/x " + sym ,to_string=True)
393 | if "No symbol" in data:
394 | return 0
395 | else :
396 | data = re.search("0x.*[0-9a-f] ",data)
397 | data = data.group()
398 | symaddr = int(data[:-1] ,16)
399 | return symaddr-libc
400 | except :
401 | return 0
402 |
403 |
404 | def set_thread_arena():
405 | global thread_arena
406 | global main_arena
407 | global enable_thread
408 | if capsize == 0 :
409 | arch = getarch()
410 | try :
411 | data = gdb.execute("x/" + word +"&thread_arena",to_string=True)
412 | except :
413 | return
414 | enable_thread = True
415 | if "main_arena" in data :
416 | thread_arena = main_arena
417 | return
418 | thread_arena = int(data.split(":")[1].strip(),16)
419 |
420 | def set_main_arena():
421 | global main_arena
422 | global main_arena_off
423 |
424 | offset = getoff("&main_arena")
425 | if offset == 0: # no main_arena symbol
426 | print("Cannot get main_arena's symbol address. Make sure you install libc debug file (libc6-dbg & libc6-dbg:i386 for debian package).")
427 | return
428 | libc = libcbase()
429 | arch = getarch()
430 | main_arena_off = offset
431 | main_arena = libc + main_arena_off
432 |
433 | def check_overlap(addr,size,data = None):
434 | if data :
435 | for key,(start,end,chunk) in data.items() :
436 | if (addr >= start and addr < end) or ((addr+size) > start and (addr+size) < end ) or ((addr < start) and ((addr + size) >= end)):
437 | return chunk,"error"
438 | else :
439 | for key,(start,end,chunk) in freememoryarea.items() :
440 | if (addr >= start and addr < end) or ((addr+size) > start and (addr+size) < end ) or ((addr < start) and ((addr + size) >= end)):
441 | return chunk,"freed"
442 | for key,(start,end,chunk) in allocmemoryarea.items() :
443 | if (addr >= start and addr < end) or ((addr+size) > start and (addr+size) < end ) or ((addr < start) and ((addr + size) >= end)) :
444 | return chunk,"inused"
445 | return None,None
446 |
447 | def get_top_lastremainder(arena=None):
448 | global fastbinsize
449 | global top
450 | global last_remainder
451 | if not arena :
452 | arena = main_arena
453 | chunk = {}
454 | if capsize == 0 :
455 | arch = getarch()
456 | #get top
457 | cmd = "x/" + word + "&((struct malloc_state *)" + hex(arena) + ").top"
458 | chunk["addr"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
459 | chunk["size"] = 0
460 | if chunk["addr"] :
461 | cmd = "x/" + word + hex(chunk["addr"]+capsize*1)
462 | try :
463 | chunk["size"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16) & 0xfffffffffffffff8
464 | if chunk["size"] > system_mem :
465 | chunk["memerror"] = "top is broken ?"
466 | except :
467 | chunk["memerror"] = "invaild memory"
468 | top = copy.deepcopy(chunk)
469 | #get last_remainder
470 | chunk = {}
471 | cmd = "x/" + word + "&((struct malloc_state *)" + hex(arena) + ").last_remainder"
472 | chunk["addr"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
473 | chunk["size"] = 0
474 | if chunk["addr"] :
475 | cmd = "x/" + word + hex(chunk["addr"]+capsize*1)
476 | try :
477 | chunk["size"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16) & 0xfffffffffffffff8
478 | except :
479 | chunk["memerror"] = "invaild memory"
480 | last_remainder = copy.deepcopy(chunk)
481 |
482 |
483 | def reveal_ptr(addr_of_entry,entry):
484 | return (addr_of_entry >> 12) ^ entry
485 |
486 | def get_fast_bin(arena=None):
487 | global fastbin
488 | global fastchunk
489 | global fastbinsize
490 | global freememoryarea
491 | if not arena :
492 | arena = main_arena
493 | fastbin = []
494 | fastchunk = []
495 | #freememoryarea = []
496 | if capsize == 0 :
497 | arch = getarch()
498 | cmd = "x/" + word + "&((struct malloc_state *)" + hex(arena) + ").fastbinsY"
499 | fastbinsY = int(gdb.execute(cmd,to_string=True).split(":")[0].split()[0].strip(),16)
500 | for i in range(fastbinsize-3):
501 | fastbin.append([])
502 | chunk = {}
503 | is_overlap = (None,None)
504 | cmd = "x/" + word + hex(fastbinsY + i*capsize)
505 | chunk["addr"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
506 |
507 | while chunk["addr"] and not is_overlap[0]:
508 | cmd = "x/" + word + hex(chunk["addr"]+capsize*1)
509 | try :
510 | chunk["size"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16) & 0xfffffffffffffff8
511 | except :
512 | chunk["memerror"] = "invaild memory"
513 | break
514 | is_overlap = check_overlap(chunk["addr"], (capsize*2)*(i+2))
515 | chunk["overlap"] = is_overlap
516 | freememoryarea[hex(chunk["addr"])] = copy.deepcopy((chunk["addr"],chunk["addr"] + (capsize*2)*(i+2) ,chunk))
517 | fastbin[i].append(copy.deepcopy(chunk))
518 | fastchunk.append(chunk["addr"])
519 | cmd = "x/" + word + hex(chunk["addr"]+capsize*2)
520 | ref_chunk_addr = chunk["addr"]+capsize*2
521 | chunk = {}
522 | chunk["addr"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
523 | if chunk["addr"] and enable_reveal_ptr :
524 | chunk["addr"] = reveal_ptr(ref_chunk_addr,chunk["addr"])
525 |
526 | if not is_overlap[0]:
527 | chunk["size"] = 0
528 | chunk["overlap"] = None
529 | fastbin[i].append(copy.deepcopy(chunk))
530 |
531 | def get_curthread():
532 | cmd = "thread"
533 | thread_id = int(gdb.execute(cmd,to_string=True).split("thread is")[1].split()[0].strip())
534 | return thread_id
535 |
536 | def get_all_threads():
537 | cmd = "info threads"
538 | all_threads = [int(line.split()[0].strip()) for line in gdb.execute(cmd, to_string=True).replace("*", "").split("\n")[1:-1]]
539 | return all_threads
540 |
541 | def thread_cmd_execute(thread_id,thread_cmd):
542 | cmd = "thread apply %d %s" % (thread_id,thread_cmd)
543 | result = gdb.execute(cmd,to_string=True)
544 | return result
545 |
546 | def get_tcache():
547 | global tcache
548 | global tcache_enable
549 | global tcache_max_bin
550 | global tcache_counts_size
551 | if capsize == 0 :
552 | arch = getarch()
553 | try :
554 | tcache_max_bin = int(gdb.execute("x/" + word + " &mp_.tcache_bins",to_string=True).split(":")[1].strip(),16)
555 | try :
556 | tcache_enable = True
557 | tcache = int(gdb.execute("x/" + word + "&tcache",to_string=True).split(":")[1].strip(),16)
558 | tps_size = int(gdb.execute("p sizeof(*tcache)",to_string=True).split("=")[1].strip())
559 | if capsize == 4 :
560 | if tps_size > 0x140:
561 | tcache_counts_size = 2
562 | else :
563 | if tps_size > 0x240:
564 | tcache_counts_size = 2
565 | except :
566 | heapbase = get_heapbase()
567 | if heapbase != 0 :
568 | cmd = "x/" + word + hex(heapbase + capsize*1)
569 | f_size = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
570 | while(f_size == 0):
571 | heapbase += capsize*2
572 | cmd = "x/" + word + hex(heapbase + capsize*1)
573 | f_size = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
574 | tcache = heapbase + capsize*2
575 | if capsize == 4 :
576 | if (f_size & ~7) - 0x10 > 0x140:
577 | tcache_counts_size = 2
578 | else :
579 | if (f_size & ~7) - 0x10 > 0x240:
580 | tcache_counts_size = 2
581 | else :
582 | tcache = 0
583 | except :
584 | tcache_enable = False
585 | tcache = 0
586 |
587 | def get_tcache_count() :
588 | global tcache_count
589 | tcache_count = []
590 | if not tcache_enable :
591 | return
592 | if capsize == 0 :
593 | arch = getarch()
594 | count_size = int(tcache_max_bin * tcache_counts_size / capsize)
595 | for i in range(count_size):
596 | cmd = "x/" + word + hex(tcache + i*capsize)
597 | c = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
598 | for j in range(int(capsize / tcache_counts_size)):
599 | tcache_count.append((c >> j * 8*tcache_counts_size) & 0xff)
600 |
601 |
602 |
603 |
604 |
605 | def get_tcache_entry():
606 | global tcache_entry
607 | get_tcache()
608 | if not tcache_enable :
609 | return
610 | tcache_entry = []
611 | get_tcache_count()
612 | if capsize == 0 :
613 | arch = getarch()
614 | if tcache and tcache_max_bin :
615 | entry_start = tcache + tcache_max_bin * tcache_counts_size
616 | for i in range(tcache_max_bin):
617 | tcache_entry.append([])
618 | chunk = {}
619 | is_overlap = (None,None)
620 | cmd = "x/" + word + hex(entry_start + i*capsize)
621 | entry = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
622 | while entry and not is_overlap[0] :
623 | chunk["addr"] = entry - capsize*2
624 | if ((entry & -capsize) != entry) and enable_reveal_ptr :
625 | chunk["memerror"] = "unaligned tcache chunk"
626 | tcache_entry[i].append(copy.deepcopy(chunk))
627 | break
628 |
629 | cmd = "x/" + word + hex(chunk["addr"] + capsize)
630 | try :
631 | chunk["size"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16) & 0xfffffffffffffff8
632 | except :
633 | chunk["memerror"] = "invaild memory"
634 | tcache_entry[i].append(copy.deepcopy(chunk))
635 | break
636 | # is_overlap = check_overlap(chunk["addr"],capsize*2*(i+2))
637 | is_overlap = check_overlap(chunk["addr"],chunk["size"])
638 |
639 | chunk["overlap"] = is_overlap
640 | freememoryarea[hex(chunk["addr"])] = copy.deepcopy((chunk["addr"],chunk["addr"] + chunk["size"] ,chunk))
641 | tcache_entry[i].append(copy.deepcopy(chunk))
642 | all_tcache_entry.append(chunk["addr"])
643 | cmd = "x/" + word + hex(chunk["addr"]+capsize*2)
644 | entry = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
645 | if entry and enable_reveal_ptr :
646 | entry = reveal_ptr(chunk["addr"]+capsize*2,entry)
647 |
648 | chunk = {}
649 |
650 | def trace_normal_bin(chunkhead,arena=None):
651 | global freememoryarea
652 | if not arena :
653 | arena = main_arena
654 | libc = libcbase()
655 | bins = []
656 | if capsize == 0 :
657 | arch = getarch()
658 | if chunkhead["addr"] == 0 : # main_arena not initial
659 | return None
660 | chunk = {}
661 | cmd = "x/" + word + hex(chunkhead["addr"] + capsize*2) #fd
662 | chunk["addr"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16) #get fd chunk
663 | if (chunk["addr"] == chunkhead["addr"]) : #no chunk in the bin
664 | if (chunkhead["addr"] > arena) :
665 | return bins
666 | else :
667 | try :
668 | cmd = "x/" + word + hex(chunk["addr"]+capsize*1)
669 | chunk["size"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16) & 0xfffffffffffffff8
670 | is_overlap = check_overlap(chunk["addr"],chunk["size"])
671 | chunk["overlap"] = is_overlap
672 | chunk["memerror"] = "\033[31mbad fd (" + hex(chunk["addr"]) + ")\033[37m"
673 | except :
674 | chunk["memerror"] = "invaild memory"
675 | bins.append(copy.deepcopy(chunk))
676 | return bins
677 | else :
678 | try :
679 | cmd = "x/" + word + hex(chunkhead["addr"]+capsize*3)
680 | bk = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
681 | cmd = "x/" + word + hex(bk+capsize*2)
682 | bk_fd = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
683 | if bk_fd != chunkhead["addr"]:
684 | chunkhead["memerror"] = "\033[31mdoubly linked list corruption {0} != {1} and \033[36m{2}\033[31m is broken".format(hex(chunkhead["addr"]),hex(bk_fd),hex(chunkhead["addr"]))
685 | bins.append(copy.deepcopy(chunkhead))
686 | return bins
687 | fd = chunkhead["addr"]
688 | chunkhead = {}
689 | chunkhead["addr"] = bk #bins addr
690 | chunk["addr"] = fd #first chunk
691 | except :
692 | chunkhead["memerror"] = "invaild memory"
693 | bins.append(copy.deepcopy(chunkhead))
694 | return bins
695 | while chunk["addr"] != chunkhead["addr"] :
696 | try :
697 | cmd = "x/" + word + hex(chunk["addr"])
698 | chunk["prev_size"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16) & 0xfffffffffffffff8
699 | cmd = "x/" + word + hex(chunk["addr"]+capsize*1)
700 | chunk["size"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16) & 0xfffffffffffffff8
701 | except :
702 | chunk["memerror"] = "invaild memory"
703 | break
704 | try :
705 | cmd = "x/" + word + hex(chunk["addr"]+capsize*2)
706 | fd = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
707 | if fd == chunk["addr"] :
708 | chunk["memerror"] = "\033[31mbad fd (" + hex(fd) + ")\033[37m"
709 | bins.append(copy.deepcopy(chunk))
710 | break
711 | cmd = "x/" + word + hex(fd + capsize*3)
712 | fd_bk = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
713 | if chunk["addr"] != fd_bk :
714 | chunk["memerror"] = "\033[31mdoubly linked list corruption {0} != {1} and \033[36m{2}\033[31m or \033[36m{3}\033[31m is broken".format(hex(chunk["addr"]),hex(fd_bk),hex(fd),hex(chunk["addr"]))
715 | bins.append(copy.deepcopy(chunk))
716 | break
717 | except :
718 | chunk["memerror"] = "invaild memory"
719 | bins.append(copy.deepcopy(chunk))
720 | break
721 | is_overlap = check_overlap(chunk["addr"],chunk["size"])
722 | chunk["overlap"] = is_overlap
723 | freememoryarea[hex(chunk["addr"])] = copy.deepcopy((chunk["addr"],chunk["addr"] + chunk["size"] ,chunk))
724 | bins.append(copy.deepcopy(chunk))
725 | cmd = "x/" + word + hex(chunk["addr"]+capsize*2) #find next
726 | chunk = {}
727 | chunk["addr"] = fd
728 | return bins
729 |
730 | def get_unsortbin(arena=None):
731 | global unsortbin
732 | if not arena :
733 | arena = main_arena
734 | unsortbin = []
735 | if capsize == 0 :
736 | arch = getarch()
737 | chunkhead = {}
738 | cmd = "x/" + word + "&((struct malloc_state *)" + hex(arena) + ").bins"
739 | chunkhead["addr"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
740 | unsortbin = trace_normal_bin(chunkhead,arena)
741 |
742 | def get_smallbin(arena=None):
743 | global smallbin
744 | if not arena :
745 | arena = main_arena
746 | smallbin = {}
747 | if capsize == 0 :
748 | arch = getarch()
749 | max_smallbin_size = 512*int(capsize/4)
750 | cmd = "x/" + word + "&((struct malloc_state *)" + hex(arena) + ").bins"
751 | bins_addr = int(gdb.execute(cmd,to_string=True).split(":")[0].split()[0].strip(),16)
752 | if tcache_enable :
753 | cursize = 8
754 | else :
755 | cursize = capsize
756 | for size in range(capsize*4,max_smallbin_size,cursize*2):
757 | chunkhead = {}
758 | if tcache_enable and capsize == 4 :
759 | idx = int((size/(cursize*2)))
760 | else :
761 | idx = int((size/(cursize*2))) - 1
762 | cmd = "x/" + word + hex(bins_addr + idx*capsize*2) # calc the smallbin index
763 | chunkhead["addr"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
764 | try :
765 | bins = trace_normal_bin(chunkhead,arena)
766 | except:
767 | corruptbin = True
768 | bins = None
769 | if bins and len(bins) > 0 :
770 | smallbin[hex(size)] = copy.deepcopy(bins)
771 |
772 | def largbin_index(size):
773 | if capsize == 0 :
774 | arch = getarch()
775 | if capsize == 8 :
776 | if (size >> 6) <= 48 :
777 | idx = 48 + (size >> 6)
778 | elif (size >> 9) <= 20 :
779 | idx = 91 + (size >> 9)
780 | elif (size >> 12) <= 10:
781 | idx = 110 + (size >> 12)
782 | elif (size >> 15) <= 4 :
783 | idx = 119 + (size >> 15)
784 | elif (size >> 18) <= 2:
785 | idx = 124 + (size >> 18)
786 | else :
787 | idx = 126
788 | else :
789 | if (size >> 6) <= 38 :
790 | idx = 56 + (size >> 6)
791 | elif (size >> 9) <= 20 :
792 | idx = 91 + (size >> 9)
793 | elif (size >> 12) <= 10:
794 | idx = 110 + (size >> 12)
795 | elif (size >> 15) <= 4 :
796 | idx = 119 + (size >> 15)
797 | elif (size >> 18) <= 2:
798 | idx = 124 + (size >> 18)
799 | else :
800 | idx = 126
801 | return idx
802 |
803 | def get_largebin(arena=None):
804 | global largebin
805 | global corruptbin
806 | if not arena :
807 | arena = main_arena
808 | largebin = {}
809 | if capsize == 0 :
810 | arch = getarch()
811 | min_largebin = 512*int(capsize/4)
812 | cmd = "x/" + word + "&((struct malloc_state *)" + hex(arena) + ").bins"
813 | bins_addr = int(gdb.execute(cmd,to_string=True).split(":")[0].split()[0].strip(),16)
814 | for idx in range(64,128):
815 | chunkhead = {}
816 | cmd = "x/" + word + hex(bins_addr + idx*capsize*2 - 2*capsize) # calc the largbin index
817 | chunkhead["addr"] = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
818 | try :
819 | bins = trace_normal_bin(chunkhead,arena)
820 | except :
821 | corruptbin = True
822 | bins = None
823 | if bins and len(bins) > 0 :
824 | largebin[idx] = copy.deepcopy(bins)
825 |
826 | def get_system_mem(arena=None):
827 | global system_mem
828 | if not arena :
829 | arena = main_arena
830 | if capsize == 0 :
831 | arch = getarch()
832 | cmd = "x/" + word + "&((struct malloc_state *)" + hex(arena) + ").system_mem"
833 | system_mem = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
834 |
835 |
836 | def get_heap_info(arena=None):
837 | global main_arena
838 | global thread_arena
839 | global freememoryarea
840 | global top
841 | global enable_reveal_ptr
842 | global tcache_enable
843 | global tcache
844 |
845 | top = {}
846 | freememoryarea = {}
847 | corruptbin = False
848 | get_libc_version()
849 | if libc_version > 2.31 :
850 | enable_reveal_ptr = True
851 | if arena :
852 | get_tcache_entry()
853 | get_system_mem(arena)
854 | get_unsortbin(arena)
855 | get_smallbin(arena)
856 | if tracelargebin :
857 | get_largebin(arena)
858 | get_fast_bin(arena)
859 | get_top_lastremainder(arena)
860 |
861 | return True
862 |
863 |
864 | set_main_arena()
865 | set_thread_arena()
866 | if thread_arena and enable_thread :
867 | get_tcache_entry()
868 | get_system_mem(thread_arena)
869 | get_unsortbin(thread_arena)
870 | get_smallbin(thread_arena)
871 | if tracelargebin :
872 | get_largebin(thread_arena)
873 | get_fast_bin(thread_arena)
874 | get_top_lastremainder(thread_arena)
875 |
876 | return True
877 |
878 | elif main_arena and not enable_thread:
879 | get_tcache_entry()
880 | get_system_mem()
881 | get_unsortbin()
882 | get_smallbin()
883 | if tracelargebin :
884 | get_largebin()
885 | get_fast_bin()
886 | get_top_lastremainder()
887 |
888 | return True
889 | return False
890 |
891 |
892 | def get_reg(reg):
893 | cmd = "info register " + reg
894 | result = int(gdb.execute(cmd,to_string=True).split()[1].strip(),16)
895 | return result
896 |
897 | def trace_malloc():
898 | global mallocbp
899 | global freebp
900 | global memalignbp
901 | global reallocbp
902 |
903 | mallocbp = Malloc_Bp_handler("*" + "_int_malloc")
904 | freebp = Free_Bp_handler("*" + "_int_free")
905 | memalignbp = Memalign_Bp_handler("*" + "_int_memalign")
906 | reallocbp = Realloc_Bp_handler("*" + "_int_realloc")
907 | if not get_heap_info() :
908 | print("Can't find heap info")
909 | return
910 |
911 | def dis_trace_malloc():
912 | global mallocbp
913 | global freebp
914 | global memalignbp
915 | global reallocbp
916 |
917 | if mallocbp :
918 | mallocbp.delete()
919 | mallocbp = None
920 | if freebp :
921 | freebp.delete()
922 | freebp = None
923 | if memalignbp :
924 | memalignbp.delete()
925 | memalignbp = None
926 | if reallocbp :
927 | reallocbp.delete()
928 | reallocbp = None
929 |
930 | def find_overlap(chunk,bins):
931 | is_overlap = False
932 | count = 0
933 | for current in bins :
934 | if chunk["addr"] == current["addr"] :
935 | count += 1
936 | if count > 1 :
937 | is_overlap = True
938 | return is_overlap
939 |
940 | def unlinkable(chunkaddr,fd = None ,bk = None):
941 | if capsize == 0 :
942 | arch = getarch()
943 | try :
944 | cmd = "x/" + word + hex(chunkaddr + capsize)
945 | chunk_size = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16) & 0xfffffffffffffff8
946 | cmd = "x/" + word + hex(chunkaddr + chunk_size)
947 | next_prev_size = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
948 | if not fd :
949 | cmd = "x/" + word + hex(chunkaddr + capsize*2)
950 | fd = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
951 | if not bk :
952 | cmd = "x/" + word + hex(chunkaddr + capsize*3)
953 | bk = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
954 | cmd = "x/" + word + hex(fd + capsize*3)
955 | fd_bk = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
956 | cmd = "x/" + word + hex(bk + capsize*2)
957 | bk_fd = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
958 | if chunk_size != next_prev_size :
959 | print("\033[32mUnlinkable :\033[1;31m False (corrupted size chunksize(0x%x) != prev_size(0x%x)) ) \033[37m " % (chunk_size,next_prev_size))
960 | elif (chunkaddr == fd_bk ) and (chunkaddr == bk_fd) :
961 | print("\033[32mUnlinkable :\033[1;33m True\033[37m")
962 | print("\033[32mResult of unlink :\033[37m")
963 | print("\033[32m \033[1;34m FD->bk (\033[1;33m*0x%x\033[1;34m) = BK (\033[1;37m0x%x ->\033[1;33m 0x%x\033[1;34m)\033[37m " % (fd+capsize*3,fd_bk,bk))
964 | print("\033[32m \033[1;34m BK->fd (\033[1;33m*0x%x\033[1;34m) = FD (\033[1;37m0x%x ->\033[1;33m 0x%x\033[1;34m)\033[37m " % (bk+capsize*2,bk_fd,fd))
965 | else :
966 | if chunkaddr != fd_bk :
967 | print("\033[32mUnlinkable :\033[1;31m False (FD->bk(0x%x) != (0x%x)) \033[37m " % (fd_bk,chunkaddr))
968 | else :
969 | print("\033[32mUnlinkable :\033[1;31m False (BK->fd(0x%x) != (0x%x)) \033[37m " % (bk_fd,chunkaddr))
970 | except :
971 | print("\033[32mUnlinkable :\033[1;31m False (FD or BK is corruption) \033[37m ")
972 |
973 | def freeable(victim):
974 | global fastchunk
975 | global system_mem
976 | if capsize == 0 :
977 | arch = getarch()
978 | chunkaddr = victim
979 | try :
980 | if not get_heap_info() :
981 | print("Can't find heap info")
982 | return
983 | cmd = "x/" + word + hex(chunkaddr)
984 | prev_size = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
985 | cmd = "x/" + word + hex(chunkaddr + capsize*1)
986 | size = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
987 | cmd = "x/" + word + hex(chunkaddr + capsize*2)
988 | fd = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
989 | cmd = "x/" + word + hex(chunkaddr + capsize*3)
990 | bk = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
991 | prev_inuse = size & 1
992 | is_mmapd = (size >> 1) & 1
993 | non_main_arena = (size >> 2) & 1
994 | size = size & 0xfffffffffffffff8
995 | if is_mmapd :
996 | block = chunkaddr - prev_size
997 | total_size = prev_size + size
998 | if ((block | total_size) & (0xfff)) != 0 :
999 | print("\033[32mFreeable :\033[1;31m False -> Invalid pointer (((chunkaddr(0x%x) - prev_size(0x%x))|(prev_size(0x%x) + size(0x%x)))) & 0xfff != 0 \033[37m" % (chunkaddr,prev_size,prev_size,size))
1000 | return
1001 | else :
1002 | if chunkaddr > (2**(capsize*8) - (size & 0xfffffffffffffff8)):
1003 | print("\033[32mFreeable :\033[1;31m False -> Invalid pointer chunkaddr (0x%x) > -size (0x%x)\033[37m" % (chunkaddr,(2**(capsize*8) - (size & 0xfffffffffffffff8))))
1004 | return
1005 | if (chunkaddr & (capsize*2 - 1)) != 0 :
1006 | print("\033[32mFreeable :\033[1;31m False -> Invalid pointer misaligned chunkaddr (0x%x) & (0x%x) != 0\033[37m" % (chunkaddr,(capsize*2 - 1)))
1007 | return
1008 | if (size < capsize*4) :
1009 | print("\033[32mFreeable :\033[1;31m False -> Chunkaddr (0x%x) invalid size (size(0x%x) < 0x%x )\033[37m" % (chunkaddr,size,capsize*4))
1010 | return
1011 | if (size & (capsize)) !=0 :
1012 | print("\033[32mFreeable :\033[1;31m False -> Chunkaddr (0x%x) invalid size (size(0x%x) & 0x%x != 0 )\033[37m" % (chunkaddr,size,capsize))
1013 | return
1014 | cmd = "x/" + word + hex(chunkaddr + size + capsize)
1015 | nextsize = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1016 | nextchunk = chunkaddr + size
1017 | status = nextsize & 1
1018 | if size <= capsize*0x10 : #fastbin
1019 | if nextsize < capsize*4 :
1020 | print("\033[32mFreeable :\033[1;31m False -> Chunkaddr (0x%x) invalid next size (size(0x%x) < 0x%x )\033[37m" % (chunkaddr,size,capsize*4))
1021 | return
1022 | if nextsize >= system_mem :
1023 | print("\033[32mFreeable :\033[1;31m False -> Chunkaddr (0x%x) invalid next size (size(0x%x) > system_mem(0x%x) )\033[37m" % (chunkaddr,size,system_mem))
1024 | return
1025 | old = fastbin[int(size/0x10)-2][0]["addr"]
1026 | if chunkaddr == old :
1027 | print("\033[32mFreeable :\033[1;31m false -> Double free chunkaddr(0x%x) == 0x%x )\033[37m" % (chunkaddr,old))
1028 | return
1029 | else :
1030 | if chunkaddr == top["addr"]:
1031 | print("\033[32mFreeable :\033[1;31m False -> Free top chunkaddr(0x%x) == 0x%x )\033[37m" % (chunkaddr,top["addr"]))
1032 | return
1033 | cmd = "x/" + word + hex(top["addr"] + capsize)
1034 | topsize = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1035 | if nextchunk >= top["addr"] + topsize :
1036 | print("\033[32mFreeable :\033[1;31m False -> Out of top chunkaddr(0x%x) > 0x%x )\033[37m" % (chunkaddr,top["addr"] + topsize))
1037 | return
1038 | if status == 0 :
1039 | print("\033[32mFreeable :\033[1;31m false -> Double free chunkaddr(0x%x) inused bit is not seted )\033[37m" % (chunkaddr))
1040 | return
1041 | if nextsize < capsize*4 :
1042 | print("\033[32mFreeable :\033[1;31m False -> Chunkaddr (0x%x) invalid next size (size(0x%x) < 0x%x )\033[37m" % (chunkaddr,size,capsize*4))
1043 | return
1044 | if nextsize >= system_mem :
1045 | print("\033[32mFreeable :\033[1;31m False -> Chunkaddr (0x%x) invalid next size (size(0x%x) > system_mem(0x%x) )\033[37m" % (chunkaddr,size,system_mem))
1046 | return
1047 | if not prev_inuse :
1048 | cmd = "x/" + word + hex(chunkaddr - prev_size + capsize)
1049 | prev_chunk_size = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16) & 0xfffffffffffffff8
1050 | if prev_size != prev_chunk_size :
1051 | print("\033[32mFreeable :\033[1;31m False -> p->size(0x%x) != next->prevsize(0x%x) \033[37m" % (prev_chunk_size,prev_size))
1052 | return
1053 |
1054 | if len(unsortbin) > 0 :
1055 | bck = unsortbin[0]["addr"]
1056 | cmd = "x/" + word + hex(bck + capsize*2)
1057 | fwd = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1058 | cmd = "x/" + word + hex(fwd + capsize*3)
1059 | bk = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1060 | if bk != bck :
1061 | print("\033[32mFreeable :\033[1;31m False -> Corrupted unsorted chunkaddr fwd->bk(0x%x) != bck(0x%x) )\033[37m" % (bk,bck))
1062 | return
1063 | print("\033[32mFreeable :\033[1;33m True\033[37m")
1064 | except :
1065 | print("Can't access memory")
1066 |
1067 | def get_heapbase():
1068 | if (main_arena and not enable_thread) or thread_arena == main_arena :
1069 | heapbase = int(gdb.execute("x/" + word + " &mp_.sbrk_base",to_string=True).split(":")[1].strip(),16)
1070 | elif thread_arena :
1071 | arena_size = int(gdb.execute("p sizeof(main_arena)",to_string=True).split("=")[1].strip(),16)
1072 | heapbase = (thread_arena + arena_size + 0xf) & ~0xf
1073 | else :
1074 | return None
1075 | return heapbase
1076 |
1077 | def chunkinfo(victim):
1078 | global fastchunk
1079 | if capsize == 0 :
1080 | arch = getarch()
1081 | chunkaddr = victim
1082 | try :
1083 | if not get_heap_info() :
1084 | print("Can't find heap info")
1085 | return
1086 | cmd = "x/" + word + hex(chunkaddr)
1087 | prev_size = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1088 | cmd = "x/" + word + hex(chunkaddr + capsize*1)
1089 | size = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1090 | cmd = "x/" + word + hex(chunkaddr + capsize*2)
1091 | fd = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1092 | cmd = "x/" + word + hex(chunkaddr + capsize*3)
1093 | bk = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1094 | cmd = "x/" + word + hex(chunkaddr + (size & 0xfffffffffffffff8) + capsize)
1095 | nextsize = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1096 | status = nextsize & 1
1097 | print("==================================")
1098 | print(" Chunk info ")
1099 | print("==================================")
1100 | if status:
1101 | if chunkaddr in fastchunk :
1102 | print("\033[1;32mStatus : \033[1;34m Freed (fast) \033[37m")
1103 | elif chunkaddr in all_tcache_entry:
1104 | print("\033[1;32mStatus : \033[1;34m Freed (tcache) \033[37m")
1105 | else :
1106 | print("\033[1;32mStatus : \033[31m Used \033[37m")
1107 | else :
1108 | print("\033[1;32mStatus : \033[1;34m Freed \033[37m")
1109 | unlinkable(chunkaddr,fd,bk)
1110 | freeable(chunkaddr)
1111 | print("\033[32mprev_size :\033[37m 0x%x " % prev_size)
1112 | print("\033[32msize :\033[37m 0x%x " % (size & 0xfffffffffffffff8))
1113 | print("\033[32mprev_inused :\033[37m %x " % (size & 1) )
1114 | print("\033[32mis_mmap :\033[37m %x " % (size & 2) )
1115 | print("\033[32mnon_mainarea :\033[37m %x " % (size & 4) )
1116 | if not status :
1117 | print("\033[32mfd :\033[37m 0x%x " % fd)
1118 | print("\033[32mbk :\033[37m 0x%x " % bk)
1119 | if size >= 512*(capsize/4) :
1120 | cmd = "x/" + word + hex(chunkaddr + capsize*4)
1121 | fd_nextsize = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1122 | cmd = "x/" + word + hex(chunkaddr + capsize*5)
1123 | bk_nextsize = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1124 | print("\033[32mfd_nextsize :\033[37m 0x%x " % fd_nextsize)
1125 | print("\033[32mbk_nextsize :\033[37m 0x%x " % bk_nextsize)
1126 | except :
1127 | print("Can't access memory")
1128 |
1129 | def freeptr(ptr):
1130 | if capsize == 0 :
1131 | arch = getarch()
1132 | freeable(ptr-capsize*2)
1133 |
1134 | def chunkptr(ptr):
1135 | if capsize == 0 :
1136 | arch = getarch()
1137 | chunkinfo(ptr-capsize*2)
1138 |
1139 |
1140 |
1141 |
1142 |
1143 | def mergeinfo(victim):
1144 | global fastchunk
1145 | if capsize == 0 :
1146 | arch = getarch()
1147 | chunkaddr = victim
1148 | try :
1149 | if not get_heap_info():
1150 | print("Can't find heap info")
1151 | return
1152 | print("==================================")
1153 | print(" Merge info ")
1154 | print("==================================")
1155 | cmd = "x/" + word + hex(chunkaddr)
1156 | prev_size = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1157 | cmd = "x/" + word + hex(chunkaddr + capsize*1)
1158 | size = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1159 | cmd = "x/" + word + hex(chunkaddr + (size & 0xfffffffffffffff8) + capsize)
1160 | nextsize = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1161 | status = nextsize & 1
1162 | if status :
1163 | if chunkaddr in fastchunk :
1164 | print("The chunk is freed")
1165 | else :
1166 | if (size & 0xfffffffffffffff8) <= 0x80 :
1167 | print("The chunk will be a\033[32m fastchunk\033[37m")
1168 | else :
1169 | prev_status = size & 1
1170 | next_chunk = chunkaddr + (size & 0xfffffffffffffff8)
1171 | cmd = "x/" + word + hex(next_chunk + (nextsize & 0xfffffffffffffff8) + capsize)
1172 | if next_chunk != top["addr"] :
1173 | next_nextsize = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1174 | next_status = next_nextsize & 1
1175 | if not prev_status: #if prev chunk is freed
1176 | prev_chunk = chunkaddr - prev_size
1177 | if next_chunk == top["addr"] : #if next chunk is top
1178 | print("The chunk will merge into top , top will be \033[1;33m0x%x\033[37m " % prev_chunk)
1179 | print("\033[32mUnlink info : \033[1;33m0x%x\033[37m" % prev_chunk)
1180 | unlinkable(prev_chunk)
1181 | elif not next_status : #if next chunk is freed
1182 | print("The chunk and \033[1;33m0x%x\033[0m will merge into \033[1;33m0x%x\033[37m" % (next_chunk,prev_chunk))
1183 | print("\033[32mUnlink info : \033[1;33m0x%x\033[37m" % prev_chunk)
1184 | unlinkable(prev_chunk)
1185 | print("\033[32mUnlink info : \033[1;33m0x%x\033[37m" % next_chunk)
1186 | unlinkable(next_chunk)
1187 | else :
1188 | print("The chunk will merge into \033[1;33m0x%x\033[37m" % prev_chunk)
1189 | print("\033[32mUnlink info : \033[1;33m0x%x\033[37m" % prev_chunk)
1190 | unlinkable(prev_chunk)
1191 | else :
1192 | if next_chunk == top["addr"] : #if next chunk is top
1193 | print("The chunk will merge into top , top will be \033[1;34m0x%x\033[37m" % chunkaddr)
1194 | elif not next_status : #if next chunk is freed
1195 | print("The chunk will merge with \033[1;33m0x%x\033[37m" % next_chunk)
1196 | print("\033[32mUnlink info : \033[1;33m0x%x\033[37m" % next_chunk)
1197 | unlinkable(next_chunk)
1198 | else :
1199 | print("The chunk will not merge with other")
1200 | else :
1201 | print("The chunk is freed")
1202 | except :
1203 | print("Can't access memory")
1204 |
1205 | def force(target):
1206 | if capsize == 0 :
1207 | arch = getarch()
1208 | if not get_heap_info():
1209 | print("Can't find heap info")
1210 | return
1211 | if target % capsize != 0 :
1212 | print("Not alignment")
1213 | else :
1214 | nb = target - top["addr"] - capsize*2
1215 | print("nb = %d" % nb)
1216 |
1217 | def putfastbin(arena=None):
1218 | if capsize == 0 :
1219 | arch = getarch()
1220 |
1221 | if not get_heap_info(arena):
1222 | print("Can't find heap info")
1223 | return False
1224 | for i,bins in enumerate(fastbin) :
1225 | cursize = (capsize*2)*(i+2)
1226 | print("\033[32m(0x%02x) fastbin[%d]:\033[37m " % (cursize,i),end = "")
1227 | for chunk in bins :
1228 | if "memerror" in chunk :
1229 | print("\033[31m0x%x (%s)\033[37m" % (chunk["addr"],chunk["memerror"]),end = "")
1230 | elif chunk["size"] != cursize and chunk["addr"] != 0 :
1231 | print("\033[36m0x%x (size error (0x%x))\033[37m" % (chunk["addr"],chunk["size"]),end = "")
1232 | elif chunk["overlap"] and chunk["overlap"][0]:
1233 | print("\033[31m0x%x (overlap chunk with \033[36m0x%x(%s)\033[31m )\033[37m" % (chunk["addr"],chunk["overlap"][0]["addr"],chunk["overlap"][1]),end = "")
1234 | elif chunk == bins[0] :
1235 | print("\033[34m0x%x\033[37m" % chunk["addr"],end = "")
1236 | else :
1237 | if print_overlap :
1238 | if find_overlap(chunk,bins):
1239 | print("\033[31m0x%x\033[37m" % chunk["addr"],end ="")
1240 | else :
1241 | print("0x%x" % chunk["addr"],end = "")
1242 | else :
1243 | print("0x%x" % chunk["addr"],end = "")
1244 | if chunk != bins[-1]:
1245 | print(" --> ",end = "")
1246 | print("")
1247 | return True
1248 |
1249 | def put_tcache():
1250 | if not tcache_enable :
1251 | return
1252 | for i,entry in enumerate(tcache_entry):
1253 | if capsize == 4 :
1254 | cursize = (8*2)*(i+1)
1255 | else :
1256 | cursize = (8*2)*(i+2)
1257 | if len(tcache_entry[i]) > 0 :
1258 | print("\033[33;1m(0x%02x) tcache_entry[%d]\033[32m(%d)\033[33;1m:\033[37m " % (cursize,i,tcache_count[i]),end = "")
1259 | elif tcache_count[i] > 0:
1260 | print("\033[33;1m(0x%02x) tcache_entry[%d]\033[31;1m(%d)\033[33;1m:\033[37m 0\n" % (cursize,i,tcache_count[i]),end = "")
1261 | for chunk in entry :
1262 | if "memerror" in chunk :
1263 | print("\033[31m0x%x (%s)\033[37m" % (chunk["addr"]+capsize*2,chunk["memerror"]),end = "")
1264 | elif chunk["overlap"] and chunk["overlap"][0]:
1265 | print("\033[31m0x%x (overlap chunk with \033[36m0x%x(%s)\033[31m )\033[37m" % (chunk["addr"]+capsize*2,chunk["overlap"][0]["addr"],chunk["overlap"][1]),end = "")
1266 | elif chunk == entry[0] :
1267 | print("\033[34m0x%x\033[37m" % (chunk["addr"]+capsize*2),end = "")
1268 | else :
1269 | if print_overlap :
1270 | if find_overlap(chunk,entry):
1271 | print("\033[31m0x%x\033[37m" % chunk["addr"],end ="")
1272 | else :
1273 | print("0x%x" % (chunk["addr"] + capsize*2),end = "")
1274 | else :
1275 | print("0x%x" % (chunk["addr"] + capsize*2),end = "")
1276 | if chunk != entry[-1]:
1277 | print(" --> ",end = "")
1278 | if len(tcache_entry[i]) > 0 :
1279 | print("")
1280 |
1281 | return True
1282 |
1283 |
1284 |
1285 | def putheapinfo(arena=None):
1286 | if capsize == 0 :
1287 | arch = getarch()
1288 | if not putfastbin(arena) :
1289 | return
1290 | if "memerror" in top :
1291 | print("\033[35m %20s:\033[31m 0x%x \033[33m(size : 0x%x)\033[31m (%s)\033[37m " % ("top",top["addr"],top["size"],top["memerror"]))
1292 | else :
1293 | print("\033[35m %20s:\033[34m 0x%x \033[33m(size : 0x%x)\033[37m " % ("top",top["addr"],top["size"]))
1294 |
1295 | print("\033[35m %20s:\033[34m 0x%x \033[33m(size : 0x%x)\033[37m " % ("last_remainder",last_remainder["addr"],last_remainder["size"]))
1296 | if unsortbin and len(unsortbin) > 0 :
1297 | print("\033[35m %20s:\033[37m " % "unsortbin",end="")
1298 | for chunk in unsortbin :
1299 | if "memerror" in chunk :
1300 | print("\033[31m0x%x (%s)\033[37m" % (chunk["addr"],chunk["memerror"]),end = "")
1301 | elif chunk["overlap"] and chunk["overlap"][0]:
1302 | print("\033[31m0x%x (overlap chunk with \033[36m0x%x(%s)\033[31m )\033[37m" % (chunk["addr"],chunk["overlap"][0]["addr"],chunk["overlap"][1]),end = "")
1303 | elif chunk == unsortbin[-1]:
1304 | print("\033[34m0x%x\033[37m \33[33m(size : 0x%x)\033[37m" % (chunk["addr"],chunk["size"]),end = "")
1305 | else :
1306 | print("0x%x \33[33m(size : 0x%x)\033[37m" % (chunk["addr"],chunk["size"]),end = "")
1307 | if chunk != unsortbin[-1]:
1308 | print(" <--> ",end = "")
1309 | print("")
1310 | else :
1311 | print("\033[35m %20s:\033[37m 0x%x" % ("unsortbin",0)) #no chunk in unsortbin
1312 | for size,bins in smallbin.items() :
1313 | if tcache_enable :
1314 | cur_size = 8
1315 | else :
1316 | cur_size = capsize
1317 | idx = int((int(size,16)/(cur_size*2)))-2
1318 | print("\033[33m(0x%03x) %s[%2d]:\033[37m " % (int(size,16),"smallbin",idx),end="")
1319 | for chunk in bins :
1320 | if "memerror" in chunk :
1321 | print("\033[31m0x%x (%s)\033[37m" % (chunk["addr"],chunk["memerror"]),end = "")
1322 | elif chunk["size"] != int(size,16) :
1323 | print("\033[36m0x%x (size error (0x%x))\033[37m" % (chunk["addr"],chunk["size"]),end = "")
1324 | elif chunk["overlap"] and chunk["overlap"][0]:
1325 | print("\033[31m0x%x (overlap chunk with \033[36m0x%x(%s)\033[31m )\033[37m" % (chunk["addr"],chunk["overlap"][0]["addr"],chunk["overlap"][1]),end = "")
1326 | elif chunk == bins[-1]:
1327 | print("\033[34m0x%x\033[37m" % chunk["addr"],end = "")
1328 | else :
1329 | print("0x%x " % chunk["addr"],end = "")
1330 | if chunk != bins[-1]:
1331 | print(" <--> ",end = "")
1332 | print("")
1333 | for idx,bins in largebin.items():
1334 | print("\033[33m %15s[%2d]:\033[37m " % ("largebin",idx-64),end="")
1335 | for chunk in bins :
1336 | if "memerror" in chunk :
1337 | print("\033[31m0x%x (%s)\033[37m" % (chunk["addr"],chunk["memerror"]),end = "")
1338 | elif chunk["overlap"] and chunk["overlap"][0]:
1339 | print("\033[31m0x%x (overlap chunk with \033[36m0x%x(%s)\033[31m )\033[37m" % (chunk["addr"],chunk["overlap"][0]["addr"],chunk["overlap"][1]),end = "")
1340 | elif largbin_index(chunk["size"]) != idx :
1341 | print("\033[31m0x%x (incorrect bin size :\033[36m 0x%x\033[31m)\033[37m" % (chunk["addr"],chunk["size"]),end = "")
1342 | elif chunk == bins[-1]:
1343 | print("\033[34m0x%x\033[37m \33[33m(size : 0x%x)\033[37m" % (chunk["addr"],chunk["size"]),end = "")
1344 | else :
1345 | print("0x%x \33[33m(size : 0x%x)\033[37m" % (chunk["addr"],chunk["size"]),end = "")
1346 | if chunk != bins[-1]:
1347 | print(" <--> ",end = "")
1348 | print("")
1349 | if not arena :
1350 | put_tcache()
1351 | if corruptbin :
1352 | print("\033[31m Some bins is corrupted !\033[37m")
1353 |
1354 | def putarenainfo():
1355 | set_main_arena()
1356 | if capsize == 0 :
1357 | arch = getarch()
1358 | cur_arena = 0
1359 | if main_arena :
1360 | try :
1361 | count = 0
1362 | print(" Main Arena ".center(50,"="))
1363 | putheapinfo(main_arena)
1364 | cmd = "x/" + word + "&main_arena.next"
1365 | cur_arena = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1366 | while cur_arena != main_arena :
1367 | count +=1
1368 | print((" Arena " + str(count) + " ").center(50,"="))
1369 | putheapinfo(cur_arena)
1370 | cur_arena = int(gdb.execute("x/" + word + hex(cur_arena+nextoff),to_string=True).split(":")[1].strip(),16)
1371 | except :
1372 | print("Memory Error (heap)")
1373 | else :
1374 | print("Can't find heap info ")
1375 |
1376 | def putheapinfoall():
1377 | cur_thread_id = get_curthread()
1378 | all_threads = get_all_threads()
1379 | for thread_id in all_threads:
1380 | if thread_id == cur_thread_id :
1381 | print("\033[33;1m"+(" Thread " + str(thread_id) + " ").center(50,"=") + "\033[0m",end="")
1382 | else :
1383 | print((" Thread " + str(thread_id) + " ").center(50,"="),end="")
1384 | result = thread_cmd_execute(thread_id,"heapinfo")
1385 | print(result.split("):")[1],end="")
1386 |
1387 |
1388 | def putinused():
1389 | print("\033[33m %s:\033[37m " % "inused ",end="")
1390 | for addr,(start,end,chunk) in allocmemoryarea.items() :
1391 | print("0x%x," % (chunk["addr"]),end="")
1392 | print("")
1393 |
1394 |
1395 | def parse_heap(arena=None):
1396 | if capsize == 0 :
1397 | arch = getarch()
1398 | if not get_heap_info(arena):
1399 | print("can't find heap info")
1400 | return
1401 |
1402 | hb = get_heapbase()
1403 | chunkaddr = hb
1404 | if not chunkaddr:
1405 | print("Can't find heap")
1406 | return
1407 | print('\033[1;33m{:<20}{:<20}{:<21}{:<20}{:<18}{:<18}\033[0m'.format('addr', 'prev', 'size', 'status', 'fd', 'bk'))
1408 | while chunkaddr != top["addr"] :
1409 | try :
1410 | cmd = "x/" + word + hex(chunkaddr)
1411 | prev_size = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1412 | cmd = "x/" + word + hex(chunkaddr + capsize*1)
1413 | size = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1414 | cmd = "x/" + word + hex(chunkaddr + capsize*2)
1415 | if size == 0 and chunkaddr == hb :
1416 | chunkaddr += capsize*2
1417 | continue
1418 | fd = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1419 | cmd = "x/" + word + hex(chunkaddr + capsize*3)
1420 | bk = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1421 | cmd = "x/" + word + hex(chunkaddr + (size & 0xfffffffffffffff8) + capsize)
1422 | nextsize = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
1423 | status = nextsize & 1
1424 | size = size & 0xfffffffffffffff8
1425 | if size == 0 :
1426 | print("\033[31mCorrupt ?! \033[0m(size == 0) (0x%x)" % chunkaddr)
1427 | break
1428 | if status :
1429 | if chunkaddr in fastchunk or chunkaddr in all_tcache_entry:
1430 | msg = "\033[1;34m Freed \033[0m"
1431 | print('0x{:<18x}0x{:<18x}0x{:<18x}{:<16}{:>18}{:>18}'.format(chunkaddr, prev_size, size, msg, hex(fd), "None"))
1432 | else :
1433 | msg = "\033[31m Used \033[0m"
1434 | print('0x{:<18x}0x{:<18x}0x{:<18x}{:<16}{:>18}{:>18}'.format(chunkaddr, prev_size, size, msg, "None", "None"))
1435 | else :
1436 | msg = "\033[1;34m Freed \033[0m"
1437 | print('0x{:<18x}0x{:<18x}0x{:<18x}{:<16}{:>18}{:>18}'.format(chunkaddr, prev_size, size, msg, hex(fd), hex(bk)))
1438 | chunkaddr = chunkaddr + (size & 0xfffffffffffffff8)
1439 |
1440 | if chunkaddr > top["addr"] :
1441 | print("\033[31mCorrupt ?!\033[0m")
1442 | break
1443 | except :
1444 | print("Corrupt ?!")
1445 | break
1446 |
1447 | def fastbin_idx(size):
1448 | if capsize == 0 :
1449 | arch = getarch()
1450 | if capsize == 8 :
1451 | return (size >> 4) - 2
1452 | else:
1453 | return (size >> 3) - 2
1454 |
1455 | def fake_fast(addr,size):
1456 | if not get_heap_info():
1457 | print("Can't find heap info")
1458 | return
1459 | result = []
1460 | idx = fastbin_idx(size)
1461 | chunk_size = size & 0xfffffffffffffff8
1462 | start = addr - chunk_size
1463 | chunk_data = gdb.selected_inferior().read_memory(start, chunk_size)
1464 | for offset in range(chunk_size-4):
1465 | fake_size = u32(chunk_data[offset:offset+4])
1466 | if fastbin_idx(fake_size) == idx :
1467 | if ((fake_size & 2 == 2) and (fake_size & 4 == 4)) or (fake_size & 4 == 0) :
1468 | padding = addr - (start+offset-capsize) - capsize*2
1469 | result.append((start+offset-capsize,padding))
1470 | return result
1471 |
1472 | def get_fake_fast(addr,size = None):
1473 | if capsize == 0 :
1474 | arch = getarch()
1475 | fast_max = int(gdb.execute("x/" + word + "&global_max_fast",to_string=True).split(":")[1].strip(),16)
1476 | if not fast_max :
1477 | fast_max = capsize*0x10
1478 | if size :
1479 | chunk_list = fake_fast(addr,size)
1480 | for fakechunk in chunk_list :
1481 | if len(chunk_list) > 0 :
1482 | print("\033[1;33mfake chunk : \033[1;0m0x{:<12x}\033[1;33m padding :\033[1;0m {:<8d}".format(fakechunk[0],fakechunk[1]))
1483 | else :
1484 | for i in range(int(fast_max/(capsize*2)-1)):
1485 | size = capsize*2*2 + i*capsize*2
1486 | chunk_list = fake_fast(addr,size)
1487 | if len(chunk_list) > 0 :
1488 | print("-- size : %s --" % hex(size))
1489 | for fakechunk in chunk_list :
1490 | print("\033[1;33mfake chunk :\033[1;0m 0x{:<12x}\033[1;33m padding :\033[1;0m {:<8d}".format(fakechunk[0],fakechunk[1]))
1491 |
--------------------------------------------------------------------------------
/angelheap/command_wrapper.py:
--------------------------------------------------------------------------------
1 | import gdb
2 | import angelheap
3 |
4 | from utils import *
5 |
6 | angelheap_cmd = None
7 |
8 | class AngelHeapCmd(object):
9 | commands = []
10 | def __init__(self):
11 | # list all commands
12 | self.commands = [cmd for cmd in dir(self) if callable(getattr(self, cmd)) ]
13 |
14 | def tracemalloc(self,*arg):
15 | """ Trace the malloc and free and detect some error """
16 | (option,) = normalize_argv(arg,1)
17 | if option == "on":
18 | try :
19 | angelheap.trace_malloc()
20 | except :
21 | print("Can't create Breakpoint")
22 | else :
23 | angelheap.dis_trace_malloc()
24 |
25 | def heapinfo(self,*arg):
26 | """ Print some information of heap """
27 | (arena,) = normalize_argv(arg,1)
28 | angelheap.putheapinfo(arena)
29 |
30 | def heapinfoall(self):
31 | """ Print some information of multiheap """
32 | angelheap.putheapinfoall()
33 |
34 | def arenainfo(self):
35 | """ Print all arena info """
36 | angelheap.putarenainfo()
37 |
38 | def chunkinfo(self,*arg):
39 | """ Print chunk information of victim"""
40 | (victim,) = normalize_argv(arg,1)
41 | angelheap.chunkinfo(victim)
42 |
43 | def free(self,*arg):
44 | """ Print chunk is freeable """
45 | (victim,) = normalize_argv(arg,1)
46 | angelheap.freeptr(victim)
47 |
48 | def chunkptr(self,*arg):
49 | """ Print chunk information of user ptr"""
50 | (ptr,) = normalize_argv(arg,1)
51 | angelheap.chunkptr(ptr)
52 |
53 | def mergeinfo(self,*arg):
54 | """ Print merge information of victim"""
55 | (victim,) = normalize_argv(arg,1)
56 | angelheap.mergeinfo(victim)
57 |
58 | def force(self,*arg):
59 | """ Calculate the nb in the house of force """
60 | (target,) = normalize_argv(arg,1)
61 | angelheap.force(target)
62 |
63 | def printfastbin(self):
64 | """ Print the fastbin """
65 | angelheap.putfastbin()
66 |
67 | def inused(self):
68 | """ Print the inuse chunk """
69 | angelheap.putinused()
70 |
71 | def parseheap(self):
72 | """ Parse heap """
73 | angelheap.parse_heap()
74 |
75 | def fakefast(self,*arg):
76 | (addr,size) = normalize_argv(arg,2)
77 | angelheap.get_fake_fast(addr,size)
78 |
79 | class AngelHeapCmdWrapper(gdb.Command):
80 | """ angelheap command wrapper """
81 | def __init__(self):
82 | super(AngelHeapCmdWrapper,self).__init__("angelheap",gdb.COMMAND_USER)
83 |
84 | def try_eval(self, expr):
85 | try:
86 | return gdb.parse_and_eval(expr)
87 | except:
88 | #print("Unable to parse expression: {}".format(expr))
89 | return expr
90 |
91 | def eval_argv(self, expressions):
92 | """ Leave command alone, let GDB parse and evaluate arguments """
93 | return [expressions[0]] + [ self.try_eval(expr) for expr in expressions[1:] ]
94 |
95 | def invoke(self,args,from_tty):
96 | global angelheap_cmd
97 | self.dont_repeat()
98 | expressions = gdb.string_to_argv(args)
99 | arg = self.eval_argv(expressions)
100 | if len(arg) > 0 :
101 | cmd = arg[0]
102 |
103 | if cmd in angelheap_cmd.commands :
104 | func = getattr(angelheap_cmd,cmd)
105 | func(*arg[1:])
106 | else :
107 | print("Unknown command")
108 | else :
109 | print("Unknow command")
110 |
111 | return
112 |
113 | class Alias(gdb.Command):
114 | """ angelheap Alias """
115 |
116 | def __init__(self,alias,command):
117 | self.command = command
118 | super(Alias, self).__init__(alias,gdb.COMMAND_NONE)
119 |
120 | def invoke(self,args,from_tty):
121 | self.dont_repeat()
122 | gdb.execute("%s %s" % (self.command,args))
123 |
124 |
--------------------------------------------------------------------------------
/angelheap/gdbinit.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from os import path
3 |
4 | directory, file = path.split(__file__)
5 | directory = path.expanduser(directory)
6 | directory = path.abspath(directory)
7 |
8 | sys.path.append(directory)
9 |
10 | import command_wrapper
11 | import angelheap
12 |
13 | command_wrapper.angelheap_cmd = command_wrapper.AngelHeapCmd()
14 | command_wrapper.AngelHeapCmdWrapper()
15 | for cmd in command_wrapper.angelheap_cmd.commands:
16 | command_wrapper.Alias(cmd, "angelheap %s" % cmd)
17 |
--------------------------------------------------------------------------------
/angelheap/utils.py:
--------------------------------------------------------------------------------
1 |
2 | def to_int(val):
3 | """
4 | Convert a string to int number
5 | from https://github.com/longld/peda
6 | """
7 | try:
8 | return int(str(val), 0)
9 | except:
10 | return None
11 |
12 | def normalize_argv(args, size=0):
13 | """
14 | Normalize argv to list with predefined length
15 | from https://github.com/longld/peda
16 | """
17 | args = list(args)
18 | for (idx, val) in enumerate(args):
19 | if to_int(val) is not None:
20 | args[idx] = to_int(val)
21 | if size and idx == size:
22 | return args[:idx]
23 |
24 | if size == 0:
25 | return args
26 | for i in range(len(args), size):
27 | args += [None]
28 | return args
29 |
--------------------------------------------------------------------------------
/pwndbg/README.md:
--------------------------------------------------------------------------------
1 | # Pwngdb ❤️ pwndbg
2 |
3 | Below instruction is tested based on Pwndbg: `2024.02.14 build: 90dc42e5`.
4 |
5 | ## How to install
6 |
7 | 1. Put `pwngdb.py` and `angelheap.py` into `/path/to/pwndbg/pwndbg/`
8 |
9 | 2. Put `commands/pwngdb.py` and `commands/angelheap.py` into `/path/to/pwndbg/pwndbg/commands/`
10 |
11 | 3. Add `import pwndbg.commands.pwngdb` and `import pwndbg.commands.angelheap` into `/path/to/pwndbg/pwndbg/__init__.py`
12 |
13 | You can use these commands to install it:
14 |
15 | ```shell
16 | #!/bin/bash
17 | # You need to change the `/path/to/pwdbg` to your pwndbg location
18 |
19 | pwndbg='/path/to/pwndbg'
20 |
21 | cp pwngdb.py $pwndbg/pwndbg/pwngdb.py
22 | cp angelheap.py $pwndbg/pwndbg/angelheap.py
23 |
24 | cp commands/pwngdb.py $pwndbg/pwndbg/commands/pwngdb.py
25 | cp commands/angelheap.py $pwndbg/pwndbg/commands/angelheap.py
26 |
27 | sed -i -e '/config_mod.init_params()/a import pwndbg.commands.pwngdb' $pwndbg/pwndbg/__init__.py
28 | sed -i -e '/config_mod.init_params()/a import pwndbg.commands.angelheap' $pwndbg/pwndbg/__init__.py
29 | ```
30 |
31 | ## Note
32 |
33 | To avoid the conflict with pwndbg, some commands will be different or be removed.
34 |
35 | 1. `got` will be renamed to `objdump_got`
36 |
37 | 2. `canary` will be removed since pwndbg already has `canary` command
38 |
39 | 3. `tls` will be renamed to `pwngdb_tls`
40 |
41 | ## TODO
42 |
43 | - [ ] Use more pwndbg API if possible instead of using `gdb.execute` (see [developer notes of pwndbg](https://github.com/pwndbg/pwndbg/blob/dev/DEVELOPING.md))
44 |
--------------------------------------------------------------------------------
/pwndbg/angelheap.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 |
3 | # !/usr/bin/env python3
4 | # -*- coding: utf-8 -*-
5 | """
6 | Pwngdb by angelboy
7 |
8 | https://github.com/scwuaptx/Pwngdb
9 | """
10 |
11 | import gdb
12 | import subprocess
13 | import re
14 | import copy
15 | import struct
16 | import os
17 |
18 | import pwndbg.gdblib.arch
19 | import pwndbg.gdblib.vmmap
20 |
21 | # main_arena
22 | main_arena = 0
23 | main_arena_off = 0
24 |
25 | # thread
26 | thread_arena = 0
27 | enable_thread = False
28 | tcache_enable = False
29 | tcache = None
30 | tcache_max_bin = 0
31 | tcache_counts_size = 1
32 |
33 | # chunks
34 | top = {}
35 | fastbinsize = 13
36 | fastbin = []
37 | fastchunk = [] # save fastchunk address for chunkinfo check
38 | tcache_entry = []
39 | tcache_count = []
40 | all_tcache_entry = [] # save tcache address for chunkinfo check
41 | last_remainder = {}
42 | unsortbin = []
43 | smallbin = {} # {size:bin}
44 | largebin = {}
45 | system_mem = 0x21000
46 |
47 | # chunk recording
48 | freememoryarea = {} # using in parse
49 | allocmemoryarea = {}
50 | freerecord = {} # using in trace
51 |
52 | # setting for tracing memory allocation
53 | enable_reveal_ptr = False
54 | tracelargebin = True
55 | inmemalign = False
56 | inrealloc = False
57 | print_overlap = True
58 | DEBUG = True # debug msg (free and malloc) if you want
59 |
60 | # breakpoints for tracing
61 | mallocbp = None
62 | freebp = None
63 | memalignbp = None
64 | reallocbp = None
65 |
66 | # architecture setting
67 | capsize = 0
68 | word = ""
69 | arch = ""
70 | libc_version = 0
71 |
72 | # condition
73 | corruptbin = False
74 |
75 |
76 | def u32(data, fmt="--------------------------------------------------------------------------------------<\033[37m")
123 | msg = "\033[33mmalloc(0x%x)\033[37m" % self.arg
124 | print("%-40s = 0x%x \033[31m overlap detected !! (0x%x)\033[37m" % (
125 | msg, chunk["addr"] + capsize * 2, overlap["addr"]))
126 | print(
127 | "\033[34m>--------------------------------------------------------------------------------------<\033[37m")
128 | else:
129 | print("\033[31moverlap detected !! (0x%x)\033[37m" % overlap["addr"])
130 | del allocmemoryarea[hex(overlap["addr"])]
131 | else:
132 | if DEBUG:
133 | msg = "\033[33mmalloc(0x%x)\033[37m" % self.arg
134 | print("%-40s = 0x%x" % (msg, chunk["addr"] + capsize * 2))
135 | allocmemoryarea[hex(chunk["addr"])] = copy.deepcopy((chunk["addr"], chunk["addr"] + chunk["size"], chunk))
136 | if hex(chunk["addr"]) in freerecord:
137 | freechunktuple = freerecord[hex(chunk["addr"])]
138 | freechunk = freechunktuple[2]
139 | splitchunk = {}
140 | del freerecord[hex(chunk["addr"])]
141 | if chunk["size"] != freechunk["size"]:
142 | splitchunk["addr"] = chunk["addr"] + chunk["size"]
143 | splitchunk["size"] = freechunk["size"] - chunk["size"]
144 | freerecord[hex(splitchunk["addr"])] = copy.deepcopy(
145 | (splitchunk["addr"], splitchunk["addr"] + splitchunk["size"], splitchunk))
146 | if self.arg >= 128 * capsize:
147 | Malloc_consolidate()
148 |
149 |
150 | class Malloc_Bp_handler(gdb.Breakpoint):
151 | def stop(self):
152 | if len(arch) == 0:
153 | getarch()
154 | if arch == "x86-64":
155 | reg = "$rsi"
156 | arg = int(gdb.execute("info register " + reg, to_string=True).split()[1].strip(), 16)
157 | else:
158 | # for _int_malloc in x86's glibc (unbuntu 14.04 & 16.04), size is stored in edx
159 | reg = "$edx"
160 | arg = int(gdb.execute("info register " + reg, to_string=True).split()[1].strip(), 16)
161 | Malloc_bp_ret(arg)
162 | return False
163 |
164 |
165 | class Free_bp_ret(gdb.FinishBreakpoint):
166 | def __init__(self):
167 | gdb.FinishBreakpoint.__init__(self, gdb.newest_frame(), internal=True)
168 | self.silent = True
169 |
170 | def stop(self):
171 | Malloc_consolidate()
172 | return False
173 |
174 |
175 | class Free_Bp_handler(gdb.Breakpoint):
176 |
177 | def stop(self):
178 | global allocmemoryarea
179 | global freerecord
180 | global inmemalign
181 | global inrealloc
182 | get_top_lastremainder()
183 |
184 | if len(arch) == 0:
185 | getarch()
186 | if arch == "x86-64":
187 | reg = "$rsi"
188 | result = int(gdb.execute("info register " + reg, to_string=True).split()[1].strip(), 16) + 0x10
189 | else:
190 | # for _int_free in x86's glibc (unbuntu 14.04 & 16.04), chunk address is stored in edx
191 | reg = "$edx"
192 | result = int(gdb.execute("info register " + reg, to_string=True).split()[1].strip(), 16) + 0x8
193 | chunk = {}
194 | if inmemalign or inrealloc:
195 | Update_alloca()
196 | inmemalign = False
197 | inrealloc = False
198 | prevfreed = False
199 | chunk["addr"] = result - capsize * 2
200 |
201 | cmd = "x/" + word + hex(chunk["addr"] + capsize)
202 | size = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
203 | chunk["size"] = size & 0xfffffffffffffff8
204 | if (size & 1) == 0:
205 | prevfreed = True
206 | # overlap,status = check_overlap(chunk["addr"],chunk["size"],freememoryarea)
207 | overlap, status = check_overlap(chunk["addr"], chunk["size"], freerecord)
208 | if overlap and status == "error":
209 | if DEBUG:
210 | msg = "\033[32mfree(0x%x)\033[37m (size = 0x%x)" % (result, chunk["size"])
211 | print(
212 | "\033[34m>--------------------------------------------------------------------------------------<\033[37m")
213 | print("%-25s \033[31m double free detected !! (0x%x(size:0x%x))\033[37m" % (
214 | msg, overlap["addr"], overlap["size"]))
215 | print(
216 | "\033[34m>--------------------------------------------------------------------------------------<\033[37m",
217 | end="")
218 | else:
219 | print("\033[31mdouble free detected !! (0x%x)\033[37m" % overlap["addr"])
220 | del freerecord[hex(overlap["addr"])]
221 | else:
222 | if DEBUG:
223 | msg = "\033[32mfree(0x%x)\033[37m" % result
224 | print("%-40s (size = 0x%x)" % (msg, chunk["size"]), end="")
225 |
226 | if chunk["size"] <= 0x80:
227 | freerecord[hex(chunk["addr"])] = copy.deepcopy((chunk["addr"], chunk["addr"] + chunk["size"], chunk))
228 | if DEBUG:
229 | print("")
230 | if hex(chunk["addr"]) in allocmemoryarea:
231 | del allocmemoryarea[hex(chunk["addr"])]
232 | return False
233 |
234 | prevchunk = {}
235 | if prevfreed:
236 | cmd = "x/" + word + hex(chunk["addr"])
237 | prevchunk["size"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16) & 0xfffffffffffffff8
238 | prevchunk["addr"] = chunk["addr"] - prevchunk["size"]
239 | if hex(prevchunk["addr"]) not in freerecord:
240 | print("\033[31m confuse in prevchunk 0x%x" % prevchunk["addr"])
241 | else:
242 | prevchunk["size"] += chunk["size"]
243 | del freerecord[hex(prevchunk["addr"])]
244 |
245 | nextchunk = {}
246 | nextchunk["addr"] = chunk["addr"] + chunk["size"]
247 |
248 | if nextchunk["addr"] == top["addr"]:
249 | if hex(chunk["addr"]) in allocmemoryarea:
250 | del allocmemoryarea[hex(chunk["addr"])]
251 | Free_bp_ret()
252 | if DEBUG:
253 | print("")
254 | return False
255 |
256 | cmd = "x/" + word + hex(nextchunk["addr"] + capsize)
257 | nextchunk["size"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16) & 0xfffffffffffffff8
258 | cmd = "x/" + word + hex(nextchunk["addr"] + nextchunk["size"] + capsize)
259 | nextinused = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16) & 1
260 |
261 | if nextinused == 0 and prevfreed: # next chunk is freed
262 | if hex(nextchunk["addr"]) not in freerecord:
263 | print("\033[31m confuse in nextchunk 0x%x" % nextchunk["addr"])
264 | else:
265 | prevchunk["size"] += nextchunk["size"]
266 | del freerecord[hex(nextchunk["addr"])]
267 | if nextinused == 0 and not prevfreed:
268 | if hex(nextchunk["addr"]) not in freerecord:
269 | print("\033[31m confuse in nextchunk 0x%x" % nextchunk["addr"])
270 | else:
271 | chunk["size"] += nextchunk["size"]
272 | del freerecord[hex(nextchunk["addr"])]
273 | if prevfreed:
274 | if hex(chunk["addr"]) in allocmemoryarea:
275 | del allocmemoryarea[hex(chunk["addr"])]
276 | chunk = prevchunk
277 |
278 | if DEBUG:
279 | print("")
280 | freerecord[hex(chunk["addr"])] = copy.deepcopy((chunk["addr"], chunk["addr"] + chunk["size"], chunk))
281 | if hex(chunk["addr"]) in allocmemoryarea:
282 | del allocmemoryarea[hex(chunk["addr"])]
283 | if chunk["size"] > 65536:
284 | Malloc_consolidate()
285 | return False
286 |
287 |
288 | class Memalign_Bp_handler(gdb.Breakpoint):
289 | def stop(self):
290 | global inmemalign
291 | inmemalign = True
292 | return False
293 |
294 |
295 | class Realloc_Bp_handler(gdb.Breakpoint):
296 | def stop(self):
297 | global inrealloc
298 | inrealloc = True
299 | return False
300 |
301 |
302 | def Update_alloca():
303 | global allocmemoryarea
304 | if capsize == 0:
305 | getarch()
306 | for addr, (start, end, chunk) in allocmemoryarea.items():
307 | cmd = "x/" + word + hex(chunk["addr"] + capsize * 1)
308 | cursize = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16) & 0xfffffffffffffff8
309 |
310 | if cursize != chunk["size"]:
311 | chunk["size"] = cursize
312 | allocmemoryarea[hex(chunk["addr"])] = copy.deepcopy((start, start + cursize, chunk))
313 |
314 |
315 | def Malloc_consolidate(): # merge fastbin when malloc a large chunk or free a very large chunk
316 | global fastbin
317 | global freerecord
318 |
319 | if capsize == 0:
320 | getarch()
321 | freerecord = {}
322 | if not get_heap_info():
323 | print("Can't find heap info")
324 | return
325 | freerecord = copy.deepcopy(freememoryarea)
326 |
327 |
328 | def getarch():
329 | global capsize
330 | global word
331 | global arch
332 | capsize = pwndbg.gdblib.arch.ptrsize
333 | word = "gx " if capsize == 8 else "x "
334 | arch = pwndbg.gdblib.arch.current
335 | return arch
336 |
337 |
338 | def libcbase():
339 | for p in pwndbg.gdblib.vmmap.get():
340 | if re.search(r".*libc-.*", p.objfile):
341 | return p.start
342 | return 0
343 |
344 |
345 | def get_libc_version():
346 | global libc_version
347 | try:
348 | libc_version = float(gdb.execute("x/s __libc_version", to_string=True).split()[2].strip("\""))
349 | except:
350 | print("Can not get libc version")
351 |
352 |
353 | def getoff(sym):
354 | libc = libcbase()
355 | if type(sym) is int:
356 | return sym - libc
357 | else:
358 | try:
359 | data = gdb.execute("x/x " + sym, to_string=True)
360 | if "No symbol" in data:
361 | return 0
362 | else:
363 | data = re.search("0x.*[0-9a-f] ", data)
364 | data = data.group()
365 | symaddr = int(data[:-1], 16)
366 | return symaddr - libc
367 | except:
368 | return 0
369 |
370 |
371 | def set_thread_arena():
372 | global thread_arena
373 | global main_arena
374 | global enable_thread
375 | if capsize == 0:
376 | arch = getarch()
377 | try:
378 | data = gdb.execute("x/" + word + "&thread_arena", to_string=True)
379 | except:
380 | return
381 | enable_thread = True
382 | if "main_arena" in data:
383 | thread_arena = main_arena
384 | return
385 | thread_arena = int(data.split(":")[1].strip(), 16)
386 |
387 |
388 | def set_main_arena():
389 | global main_arena
390 | global main_arena_off
391 |
392 | offset = getoff("&main_arena")
393 | if offset == 0: # no main_arena symbol
394 | print(
395 | "Cannot get main_arena's symbol address. Make sure you install libc debug file (libc6-dbg & libc6-dbg:i386 for debian package).")
396 | return
397 | libc = libcbase()
398 | arch = getarch()
399 | main_arena_off = offset
400 | main_arena = libc + main_arena_off
401 |
402 |
403 | def check_overlap(addr, size, data=None):
404 | if data:
405 | for key, (start, end, chunk) in data.items():
406 | if (addr >= start and addr < end) or ((addr + size) > start and (addr + size) < end) or (
407 | (addr < start) and ((addr + size) >= end)):
408 | return chunk, "error"
409 | else:
410 | for key, (start, end, chunk) in freememoryarea.items():
411 | if (addr >= start and addr < end) or ((addr + size) > start and (addr + size) < end) or (
412 | (addr < start) and ((addr + size) >= end)):
413 | return chunk, "freed"
414 | for key, (start, end, chunk) in allocmemoryarea.items():
415 | if (addr >= start and addr < end) or ((addr + size) > start and (addr + size) < end) or (
416 | (addr < start) and ((addr + size) >= end)):
417 | return chunk, "inused"
418 | return None, None
419 |
420 |
421 | def get_top_lastremainder(arena=None):
422 | global fastbinsize
423 | global top
424 | global last_remainder
425 | if not arena:
426 | arena = main_arena
427 | chunk = {}
428 | if capsize == 0:
429 | arch = getarch()
430 | # get top
431 | cmd = "x/" + word + "&((struct malloc_state *)" + hex(arena) + ").top"
432 | chunk["addr"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
433 | chunk["size"] = 0
434 | if chunk["addr"]:
435 | cmd = "x/" + word + hex(chunk["addr"] + capsize * 1)
436 | try:
437 | chunk["size"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16) & 0xfffffffffffffff8
438 | if chunk["size"] > system_mem:
439 | chunk["memerror"] = "top is broken ?"
440 | except:
441 | chunk["memerror"] = "invaild memory"
442 | top = copy.deepcopy(chunk)
443 | # get last_remainder
444 | chunk = {}
445 | cmd = "x/" + word + "&((struct malloc_state *)" + hex(arena) + ").last_remainder"
446 | chunk["addr"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
447 | chunk["size"] = 0
448 | if chunk["addr"]:
449 | cmd = "x/" + word + hex(chunk["addr"] + capsize * 1)
450 | try:
451 | chunk["size"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16) & 0xfffffffffffffff8
452 | except:
453 | chunk["memerror"] = "invaild memory"
454 | last_remainder = copy.deepcopy(chunk)
455 |
456 |
457 | def reveal_ptr(addr_of_entry, entry):
458 | return (addr_of_entry >> 12) ^ entry
459 |
460 |
461 | def get_fast_bin(arena=None):
462 | global fastbin
463 | global fastchunk
464 | global fastbinsize
465 | global freememoryarea
466 | if not arena:
467 | arena = main_arena
468 | fastbin = []
469 | fastchunk = []
470 | # freememoryarea = []
471 | if capsize == 0:
472 | arch = getarch()
473 | cmd = "x/" + word + "&((struct malloc_state *)" + hex(arena) + ").fastbinsY"
474 | fastbinsY = int(gdb.execute(cmd, to_string=True).split(":")[0].split()[0].strip(), 16)
475 | for i in range(fastbinsize - 3):
476 | fastbin.append([])
477 | chunk = {}
478 | is_overlap = (None, None)
479 | cmd = "x/" + word + hex(fastbinsY + i * capsize)
480 | chunk["addr"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
481 |
482 | while chunk["addr"] and not is_overlap[0]:
483 | cmd = "x/" + word + hex(chunk["addr"] + capsize * 1)
484 | try:
485 | chunk["size"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16) & 0xfffffffffffffff8
486 | except:
487 | chunk["memerror"] = "invaild memory"
488 | break
489 | is_overlap = check_overlap(chunk["addr"], (capsize * 2) * (i + 2))
490 | chunk["overlap"] = is_overlap
491 | freememoryarea[hex(chunk["addr"])] = copy.deepcopy(
492 | (chunk["addr"], chunk["addr"] + (capsize * 2) * (i + 2), chunk))
493 | fastbin[i].append(copy.deepcopy(chunk))
494 | fastchunk.append(chunk["addr"])
495 | cmd = "x/" + word + hex(chunk["addr"] + capsize * 2)
496 | ref_chunk_addr = chunk["addr"] + capsize * 2
497 | chunk = {}
498 | chunk["addr"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
499 | if chunk["addr"] and enable_reveal_ptr:
500 | chunk["addr"] = reveal_ptr(ref_chunk_addr, chunk["addr"])
501 |
502 | if not is_overlap[0]:
503 | chunk["size"] = 0
504 | chunk["overlap"] = None
505 | fastbin[i].append(copy.deepcopy(chunk))
506 |
507 |
508 | def get_curthread():
509 | cmd = "thread"
510 | thread_id = int(gdb.execute(cmd, to_string=True).split("thread is")[1].split()[0].strip())
511 | return thread_id
512 |
513 |
514 | def get_all_threads():
515 | cmd = "info threads"
516 | all_threads = [int(line.split()[0].strip()) for line in
517 | gdb.execute(cmd, to_string=True).replace("*", "").split("\n")[1:-1]]
518 | return all_threads
519 |
520 |
521 | def thread_cmd_execute(thread_id, thread_cmd):
522 | cmd = "thread apply %d %s" % (thread_id, thread_cmd)
523 | result = gdb.execute(cmd, to_string=True)
524 | return result
525 |
526 |
527 | def get_tcache():
528 | global tcache
529 | global tcache_enable
530 | global tcache_max_bin
531 | global tcache_counts_size
532 | if capsize == 0:
533 | arch = getarch()
534 | try:
535 | tcache_max_bin = int(gdb.execute("x/" + word + " &mp_.tcache_bins", to_string=True).split(":")[1].strip(), 16)
536 | try:
537 | tcache_enable = True
538 | tcache = int(gdb.execute("x/" + word + "&tcache", to_string=True).split(":")[1].strip(), 16)
539 | tps_size = int(gdb.execute("p sizeof(*tcache)", to_string=True).split("=")[1].strip())
540 | if capsize == 4:
541 | if tps_size > 0x140:
542 | tcache_counts_size = 2
543 | else:
544 | if tps_size > 0x240:
545 | tcache_counts_size = 2
546 | except:
547 | heapbase = get_heapbase()
548 | if heapbase != 0:
549 | cmd = "x/" + word + hex(heapbase + capsize * 1)
550 | f_size = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
551 | while (f_size == 0):
552 | heapbase += capsize * 2
553 | cmd = "x/" + word + hex(heapbase + capsize * 1)
554 | f_size = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
555 | tcache = heapbase + capsize * 2
556 | if capsize == 4:
557 | if (f_size & ~7) - 0x10 > 0x140:
558 | tcache_counts_size = 2
559 | else:
560 | if (f_size & ~7) - 0x10 > 0x240:
561 | tcache_counts_size = 2
562 | else:
563 | tcache = 0
564 | except:
565 | tcache_enable = False
566 | tcache = 0
567 |
568 |
569 | def get_tcache_count():
570 | global tcache_count
571 | tcache_count = []
572 | if not tcache_enable:
573 | return
574 | if capsize == 0:
575 | arch = getarch()
576 | count_size = int(tcache_max_bin * tcache_counts_size / capsize)
577 | for i in range(count_size):
578 | cmd = "x/" + word + hex(tcache + i * capsize)
579 | c = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
580 | for j in range(int(capsize / tcache_counts_size)):
581 | tcache_count.append((c >> j * 8 * tcache_counts_size) & 0xff)
582 |
583 |
584 | def get_tcache_entry():
585 | global tcache_entry
586 | get_tcache()
587 | if not tcache_enable:
588 | return
589 | tcache_entry = []
590 | get_tcache_count()
591 | if capsize == 0:
592 | arch = getarch()
593 | if tcache and tcache_max_bin:
594 | entry_start = tcache + tcache_max_bin * tcache_counts_size
595 | for i in range(tcache_max_bin):
596 | tcache_entry.append([])
597 | chunk = {}
598 | is_overlap = (None, None)
599 | cmd = "x/" + word + hex(entry_start + i * capsize)
600 | entry = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
601 | while entry and not is_overlap[0]:
602 | chunk["addr"] = entry - capsize * 2
603 | if ((entry & -capsize) != entry) and enable_reveal_ptr:
604 | chunk["memerror"] = "unaligned tcache chunk"
605 | tcache_entry[i].append(copy.deepcopy(chunk))
606 | break
607 |
608 | cmd = "x/" + word + hex(chunk["addr"] + capsize)
609 | try:
610 | chunk["size"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16) & 0xfffffffffffffff8
611 | except:
612 | chunk["memerror"] = "invaild memory"
613 | tcache_entry[i].append(copy.deepcopy(chunk))
614 | break
615 | # is_overlap = check_overlap(chunk["addr"],capsize*2*(i+2))
616 | is_overlap = check_overlap(chunk["addr"], chunk["size"])
617 |
618 | chunk["overlap"] = is_overlap
619 | freememoryarea[hex(chunk["addr"])] = copy.deepcopy(
620 | (chunk["addr"], chunk["addr"] + chunk["size"], chunk))
621 | tcache_entry[i].append(copy.deepcopy(chunk))
622 | all_tcache_entry.append(chunk["addr"])
623 | cmd = "x/" + word + hex(chunk["addr"] + capsize * 2)
624 | entry = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
625 | if entry and enable_reveal_ptr:
626 | entry = reveal_ptr(chunk["addr"] + capsize * 2, entry)
627 |
628 | chunk = {}
629 |
630 |
631 | def trace_normal_bin(chunkhead, arena=None):
632 | global freememoryarea
633 | if not arena:
634 | arena = main_arena
635 | libc = libcbase()
636 | bins = []
637 | if capsize == 0:
638 | arch = getarch()
639 | if chunkhead["addr"] == 0: # main_arena not initial
640 | return None
641 | chunk = {}
642 | cmd = "x/" + word + hex(chunkhead["addr"] + capsize * 2) # fd
643 | chunk["addr"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16) # get fd chunk
644 | if (chunk["addr"] == chunkhead["addr"]): # no chunk in the bin
645 | if (chunkhead["addr"] > arena):
646 | return bins
647 | else:
648 | try:
649 | cmd = "x/" + word + hex(chunk["addr"] + capsize * 1)
650 | chunk["size"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16) & 0xfffffffffffffff8
651 | is_overlap = check_overlap(chunk["addr"], chunk["size"])
652 | chunk["overlap"] = is_overlap
653 | chunk["memerror"] = "\033[31mbad fd (" + hex(chunk["addr"]) + ")\033[37m"
654 | except:
655 | chunk["memerror"] = "invaild memory"
656 | bins.append(copy.deepcopy(chunk))
657 | return bins
658 | else:
659 | try:
660 | cmd = "x/" + word + hex(chunkhead["addr"] + capsize * 3)
661 | bk = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
662 | cmd = "x/" + word + hex(bk + capsize * 2)
663 | bk_fd = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
664 | if bk_fd != chunkhead["addr"]:
665 | chunkhead[
666 | "memerror"] = "\033[31mdoubly linked list corruption {0} != {1} and \033[36m{2}\033[31m is broken".format(
667 | hex(chunkhead["addr"]), hex(bk_fd), hex(chunkhead["addr"]))
668 | bins.append(copy.deepcopy(chunkhead))
669 | return bins
670 | fd = chunkhead["addr"]
671 | chunkhead = {}
672 | chunkhead["addr"] = bk # bins addr
673 | chunk["addr"] = fd # first chunk
674 | except:
675 | chunkhead["memerror"] = "invaild memory"
676 | bins.append(copy.deepcopy(chunkhead))
677 | return bins
678 | while chunk["addr"] != chunkhead["addr"]:
679 | try:
680 | cmd = "x/" + word + hex(chunk["addr"])
681 | chunk["prev_size"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(),
682 | 16) & 0xfffffffffffffff8
683 | cmd = "x/" + word + hex(chunk["addr"] + capsize * 1)
684 | chunk["size"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16) & 0xfffffffffffffff8
685 | except:
686 | chunk["memerror"] = "invaild memory"
687 | break
688 | try:
689 | cmd = "x/" + word + hex(chunk["addr"] + capsize * 2)
690 | fd = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
691 | if fd == chunk["addr"]:
692 | chunk["memerror"] = "\033[31mbad fd (" + hex(fd) + ")\033[37m"
693 | bins.append(copy.deepcopy(chunk))
694 | break
695 | cmd = "x/" + word + hex(fd + capsize * 3)
696 | fd_bk = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
697 | if chunk["addr"] != fd_bk:
698 | chunk[
699 | "memerror"] = "\033[31mdoubly linked list corruption {0} != {1} and \033[36m{2}\033[31m or \033[36m{3}\033[31m is broken".format(
700 | hex(chunk["addr"]), hex(fd_bk), hex(fd), hex(chunk["addr"]))
701 | bins.append(copy.deepcopy(chunk))
702 | break
703 | except:
704 | chunk["memerror"] = "invaild memory"
705 | bins.append(copy.deepcopy(chunk))
706 | break
707 | is_overlap = check_overlap(chunk["addr"], chunk["size"])
708 | chunk["overlap"] = is_overlap
709 | freememoryarea[hex(chunk["addr"])] = copy.deepcopy((chunk["addr"], chunk["addr"] + chunk["size"], chunk))
710 | bins.append(copy.deepcopy(chunk))
711 | cmd = "x/" + word + hex(chunk["addr"] + capsize * 2) # find next
712 | chunk = {}
713 | chunk["addr"] = fd
714 | return bins
715 |
716 |
717 | def get_unsortbin(arena=None):
718 | global unsortbin
719 | if not arena:
720 | arena = main_arena
721 | unsortbin = []
722 | if capsize == 0:
723 | arch = getarch()
724 | chunkhead = {}
725 | cmd = "x/" + word + "&((struct malloc_state *)" + hex(arena) + ").bins"
726 | chunkhead["addr"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
727 | unsortbin = trace_normal_bin(chunkhead, arena)
728 |
729 |
730 | def get_smallbin(arena=None):
731 | global smallbin
732 | if not arena:
733 | arena = main_arena
734 | smallbin = {}
735 | if capsize == 0:
736 | arch = getarch()
737 | max_smallbin_size = 512 * int(capsize / 4)
738 | cmd = "x/" + word + "&((struct malloc_state *)" + hex(arena) + ").bins"
739 | bins_addr = int(gdb.execute(cmd, to_string=True).split(":")[0].split()[0].strip(), 16)
740 | if tcache_enable:
741 | cursize = 8
742 | else:
743 | cursize = capsize
744 | for size in range(capsize * 4, max_smallbin_size, cursize * 2):
745 | chunkhead = {}
746 | if tcache_enable and capsize == 4:
747 | idx = int((size / (cursize * 2)))
748 | else:
749 | idx = int((size / (cursize * 2))) - 1
750 | cmd = "x/" + word + hex(bins_addr + idx * capsize * 2) # calc the smallbin index
751 | chunkhead["addr"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
752 | try:
753 | bins = trace_normal_bin(chunkhead, arena)
754 | except:
755 | corruptbin = True
756 | bins = None
757 | if bins and len(bins) > 0:
758 | smallbin[hex(size)] = copy.deepcopy(bins)
759 |
760 |
761 | def largbin_index(size):
762 | if capsize == 0:
763 | arch = getarch()
764 | if capsize == 8:
765 | if (size >> 6) <= 48:
766 | idx = 48 + (size >> 6)
767 | elif (size >> 9) <= 20:
768 | idx = 91 + (size >> 9)
769 | elif (size >> 12) <= 10:
770 | idx = 110 + (size >> 12)
771 | elif (size >> 15) <= 4:
772 | idx = 119 + (size >> 15)
773 | elif (size >> 18) <= 2:
774 | idx = 124 + (size >> 18)
775 | else:
776 | idx = 126
777 | else:
778 | if (size >> 6) <= 38:
779 | idx = 56 + (size >> 6)
780 | elif (size >> 9) <= 20:
781 | idx = 91 + (size >> 9)
782 | elif (size >> 12) <= 10:
783 | idx = 110 + (size >> 12)
784 | elif (size >> 15) <= 4:
785 | idx = 119 + (size >> 15)
786 | elif (size >> 18) <= 2:
787 | idx = 124 + (size >> 18)
788 | else:
789 | idx = 126
790 | return idx
791 |
792 |
793 | def get_largebin(arena=None):
794 | global largebin
795 | global corruptbin
796 | if not arena:
797 | arena = main_arena
798 | largebin = {}
799 | if capsize == 0:
800 | arch = getarch()
801 | min_largebin = 512 * int(capsize / 4)
802 | cmd = "x/" + word + "&((struct malloc_state *)" + hex(arena) + ").bins"
803 | bins_addr = int(gdb.execute(cmd, to_string=True).split(":")[0].split()[0].strip(), 16)
804 | for idx in range(64, 128):
805 | chunkhead = {}
806 | cmd = "x/" + word + hex(bins_addr + idx * capsize * 2 - 2 * capsize) # calc the largbin index
807 | chunkhead["addr"] = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
808 | try:
809 | bins = trace_normal_bin(chunkhead, arena)
810 | except:
811 | corruptbin = True
812 | bins = None
813 | if bins and len(bins) > 0:
814 | largebin[idx] = copy.deepcopy(bins)
815 |
816 |
817 | def get_system_mem(arena=None):
818 | global system_mem
819 | if not arena:
820 | arena = main_arena
821 | if capsize == 0:
822 | arch = getarch()
823 | cmd = "x/" + word + "&((struct malloc_state *)" + hex(arena) + ").system_mem"
824 | system_mem = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
825 |
826 |
827 | def get_heap_info(arena=None):
828 | global main_arena
829 | global thread_arena
830 | global freememoryarea
831 | global top
832 | global enable_reveal_ptr
833 | global tcache_enable
834 | global tcache
835 |
836 | top = {}
837 | freememoryarea = {}
838 | corruptbin = False
839 | get_libc_version()
840 | if libc_version > 2.31:
841 | enable_reveal_ptr = True
842 | if arena:
843 | get_tcache_entry()
844 | get_system_mem(arena)
845 | get_unsortbin(arena)
846 | get_smallbin(arena)
847 | if tracelargebin:
848 | get_largebin(arena)
849 | get_fast_bin(arena)
850 | get_top_lastremainder(arena)
851 |
852 | return True
853 |
854 | set_main_arena()
855 | set_thread_arena()
856 | if thread_arena and enable_thread:
857 | get_tcache_entry()
858 | get_system_mem(thread_arena)
859 | get_unsortbin(thread_arena)
860 | get_smallbin(thread_arena)
861 | if tracelargebin:
862 | get_largebin(thread_arena)
863 | get_fast_bin(thread_arena)
864 | get_top_lastremainder(thread_arena)
865 |
866 | return True
867 |
868 | elif main_arena and not enable_thread:
869 | get_tcache_entry()
870 | get_system_mem()
871 | get_unsortbin()
872 | get_smallbin()
873 | if tracelargebin:
874 | get_largebin()
875 | get_fast_bin()
876 | get_top_lastremainder()
877 |
878 | return True
879 | return False
880 |
881 |
882 | def trace_malloc():
883 | global mallocbp
884 | global freebp
885 | global memalignbp
886 | global reallocbp
887 |
888 | mallocbp = Malloc_Bp_handler("*" + "_int_malloc")
889 | freebp = Free_Bp_handler("*" + "_int_free")
890 | memalignbp = Memalign_Bp_handler("*" + "_int_memalign")
891 | reallocbp = Realloc_Bp_handler("*" + "_int_realloc")
892 | if not get_heap_info():
893 | print("Can't find heap info")
894 | return
895 |
896 |
897 | def dis_trace_malloc():
898 | global mallocbp
899 | global freebp
900 | global memalignbp
901 | global reallocbp
902 |
903 | if mallocbp:
904 | mallocbp.delete()
905 | mallocbp = None
906 | if freebp:
907 | freebp.delete()
908 | freebp = None
909 | if memalignbp:
910 | memalignbp.delete()
911 | memalignbp = None
912 | if reallocbp:
913 | reallocbp.delete()
914 | reallocbp = None
915 |
916 |
917 | def find_overlap(chunk, bins):
918 | is_overlap = False
919 | count = 0
920 | for current in bins:
921 | if chunk["addr"] == current["addr"]:
922 | count += 1
923 | if count > 1:
924 | is_overlap = True
925 | return is_overlap
926 |
927 |
928 | def unlinkable(chunkaddr, fd=None, bk=None):
929 | if capsize == 0:
930 | arch = getarch()
931 | try:
932 | cmd = "x/" + word + hex(chunkaddr + capsize)
933 | chunk_size = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16) & 0xfffffffffffffff8
934 | cmd = "x/" + word + hex(chunkaddr + chunk_size)
935 | next_prev_size = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
936 | if not fd:
937 | cmd = "x/" + word + hex(chunkaddr + capsize * 2)
938 | fd = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
939 | if not bk:
940 | cmd = "x/" + word + hex(chunkaddr + capsize * 3)
941 | bk = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
942 | cmd = "x/" + word + hex(fd + capsize * 3)
943 | fd_bk = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
944 | cmd = "x/" + word + hex(bk + capsize * 2)
945 | bk_fd = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
946 | if chunk_size != next_prev_size:
947 | print(
948 | "\033[32mUnlinkable :\033[1;31m False (corrupted size chunksize(0x%x) != prev_size(0x%x)) ) \033[37m " % (
949 | chunk_size, next_prev_size))
950 | elif (chunkaddr == fd_bk) and (chunkaddr == bk_fd):
951 | print("\033[32mUnlinkable :\033[1;33m True\033[37m")
952 | print("\033[32mResult of unlink :\033[37m")
953 | print(
954 | "\033[32m \033[1;34m FD->bk (\033[1;33m*0x%x\033[1;34m) = BK (\033[1;37m0x%x ->\033[1;33m 0x%x\033[1;34m)\033[37m " % (
955 | fd + capsize * 3, fd_bk, bk))
956 | print(
957 | "\033[32m \033[1;34m BK->fd (\033[1;33m*0x%x\033[1;34m) = FD (\033[1;37m0x%x ->\033[1;33m 0x%x\033[1;34m)\033[37m " % (
958 | bk + capsize * 2, bk_fd, fd))
959 | else:
960 | if chunkaddr != fd_bk:
961 | print("\033[32mUnlinkable :\033[1;31m False (FD->bk(0x%x) != (0x%x)) \033[37m " % (fd_bk, chunkaddr))
962 | else:
963 | print("\033[32mUnlinkable :\033[1;31m False (BK->fd(0x%x) != (0x%x)) \033[37m " % (bk_fd, chunkaddr))
964 | except:
965 | print("\033[32mUnlinkable :\033[1;31m False (FD or BK is corruption) \033[37m ")
966 |
967 |
968 | def freeable(victim):
969 | global fastchunk
970 | global system_mem
971 | if capsize == 0:
972 | arch = getarch()
973 | chunkaddr = victim
974 | try:
975 | if not get_heap_info():
976 | print("Can't find heap info")
977 | return
978 | cmd = "x/" + word + hex(chunkaddr)
979 | prev_size = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
980 | cmd = "x/" + word + hex(chunkaddr + capsize * 1)
981 | size = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
982 | cmd = "x/" + word + hex(chunkaddr + capsize * 2)
983 | fd = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
984 | cmd = "x/" + word + hex(chunkaddr + capsize * 3)
985 | bk = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
986 | prev_inuse = size & 1
987 | is_mmapd = (size >> 1) & 1
988 | non_main_arena = (size >> 2) & 1
989 | size = size & 0xfffffffffffffff8
990 | if is_mmapd:
991 | block = chunkaddr - prev_size
992 | total_size = prev_size + size
993 | if ((block | total_size) & (0xfff)) != 0:
994 | print(
995 | "\033[32mFreeable :\033[1;31m False -> Invalid pointer (((chunkaddr(0x%x) - prev_size(0x%x))|(prev_size(0x%x) + size(0x%x)))) & 0xfff != 0 \033[37m" % (
996 | chunkaddr, prev_size, prev_size, size))
997 | return
998 | else:
999 | if chunkaddr > (2 ** (capsize * 8) - (size & 0xfffffffffffffff8)):
1000 | print(
1001 | "\033[32mFreeable :\033[1;31m False -> Invalid pointer chunkaddr (0x%x) > -size (0x%x)\033[37m" % (
1002 | chunkaddr, (2 ** (capsize * 8) - (size & 0xfffffffffffffff8))))
1003 | return
1004 | if (chunkaddr & (capsize * 2 - 1)) != 0:
1005 | print(
1006 | "\033[32mFreeable :\033[1;31m False -> Invalid pointer misaligned chunkaddr (0x%x) & (0x%x) != 0\033[37m" % (
1007 | chunkaddr, (capsize * 2 - 1)))
1008 | return
1009 | if (size < capsize * 4):
1010 | print(
1011 | "\033[32mFreeable :\033[1;31m False -> Chunkaddr (0x%x) invalid size (size(0x%x) < 0x%x )\033[37m" % (
1012 | chunkaddr, size, capsize * 4))
1013 | return
1014 | if (size & (capsize)) != 0:
1015 | print(
1016 | "\033[32mFreeable :\033[1;31m False -> Chunkaddr (0x%x) invalid size (size(0x%x) & 0x%x != 0 )\033[37m" % (
1017 | chunkaddr, size, capsize))
1018 | return
1019 | cmd = "x/" + word + hex(chunkaddr + size + capsize)
1020 | nextsize = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1021 | nextchunk = chunkaddr + size
1022 | status = nextsize & 1
1023 | if size <= capsize * 0x10: # fastbin
1024 | if nextsize < capsize * 4:
1025 | print(
1026 | "\033[32mFreeable :\033[1;31m False -> Chunkaddr (0x%x) invalid next size (size(0x%x) < 0x%x )\033[37m" % (
1027 | chunkaddr, size, capsize * 4))
1028 | return
1029 | if nextsize >= system_mem:
1030 | print(
1031 | "\033[32mFreeable :\033[1;31m False -> Chunkaddr (0x%x) invalid next size (size(0x%x) > system_mem(0x%x) )\033[37m" % (
1032 | chunkaddr, size, system_mem))
1033 | return
1034 | old = fastbin[int(size / 0x10) - 2][0]["addr"]
1035 | if chunkaddr == old:
1036 | print("\033[32mFreeable :\033[1;31m false -> Double free chunkaddr(0x%x) == 0x%x )\033[37m" % (
1037 | chunkaddr, old))
1038 | return
1039 | else:
1040 | if chunkaddr == top["addr"]:
1041 | print("\033[32mFreeable :\033[1;31m False -> Free top chunkaddr(0x%x) == 0x%x )\033[37m" % (
1042 | chunkaddr, top["addr"]))
1043 | return
1044 | cmd = "x/" + word + hex(top["addr"] + capsize)
1045 | topsize = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1046 | if nextchunk >= top["addr"] + topsize:
1047 | print("\033[32mFreeable :\033[1;31m False -> Out of top chunkaddr(0x%x) > 0x%x )\033[37m" % (
1048 | chunkaddr, top["addr"] + topsize))
1049 | return
1050 | if status == 0:
1051 | print(
1052 | "\033[32mFreeable :\033[1;31m false -> Double free chunkaddr(0x%x) inused bit is not seted )\033[37m" % (
1053 | chunkaddr))
1054 | return
1055 | if nextsize < capsize * 4:
1056 | print(
1057 | "\033[32mFreeable :\033[1;31m False -> Chunkaddr (0x%x) invalid next size (size(0x%x) < 0x%x )\033[37m" % (
1058 | chunkaddr, size, capsize * 4))
1059 | return
1060 | if nextsize >= system_mem:
1061 | print(
1062 | "\033[32mFreeable :\033[1;31m False -> Chunkaddr (0x%x) invalid next size (size(0x%x) > system_mem(0x%x) )\033[37m" % (
1063 | chunkaddr, size, system_mem))
1064 | return
1065 | if not prev_inuse:
1066 | cmd = "x/" + word + hex(chunkaddr - prev_size + capsize)
1067 | prev_chunk_size = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(),
1068 | 16) & 0xfffffffffffffff8
1069 | if prev_size != prev_chunk_size:
1070 | print("\033[32mFreeable :\033[1;31m False -> p->size(0x%x) != next->prevsize(0x%x) \033[37m" % (
1071 | prev_chunk_size, prev_size))
1072 | return
1073 |
1074 | if len(unsortbin) > 0:
1075 | bck = unsortbin[0]["addr"]
1076 | cmd = "x/" + word + hex(bck + capsize * 2)
1077 | fwd = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1078 | cmd = "x/" + word + hex(fwd + capsize * 3)
1079 | bk = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1080 | if bk != bck:
1081 | print(
1082 | "\033[32mFreeable :\033[1;31m False -> Corrupted unsorted chunkaddr fwd->bk(0x%x) != bck(0x%x) )\033[37m" % (
1083 | bk, bck))
1084 | return
1085 | print("\033[32mFreeable :\033[1;33m True\033[37m")
1086 | except:
1087 | print("Can't access memory")
1088 |
1089 |
1090 | def get_heapbase():
1091 | if (main_arena and not enable_thread) or thread_arena == main_arena:
1092 | heapbase = int(gdb.execute("x/" + word + " &mp_.sbrk_base", to_string=True).split(":")[1].strip(), 16)
1093 | elif thread_arena:
1094 | arena_size = int(gdb.execute("p sizeof(main_arena)", to_string=True).split("=")[1].strip(), 16)
1095 | heapbase = (thread_arena + arena_size + 0xf) & ~0xf
1096 | else:
1097 | return None
1098 | return heapbase
1099 |
1100 |
1101 | def chunkinfo(victim):
1102 | global fastchunk
1103 | if capsize == 0:
1104 | arch = getarch()
1105 | chunkaddr = victim
1106 | try:
1107 | if not get_heap_info():
1108 | print("Can't find heap info")
1109 | return
1110 | cmd = "x/" + word + hex(chunkaddr)
1111 | prev_size = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1112 | cmd = "x/" + word + hex(chunkaddr + capsize * 1)
1113 | size = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1114 | cmd = "x/" + word + hex(chunkaddr + capsize * 2)
1115 | fd = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1116 | cmd = "x/" + word + hex(chunkaddr + capsize * 3)
1117 | bk = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1118 | cmd = "x/" + word + hex(chunkaddr + (size & 0xfffffffffffffff8) + capsize)
1119 | nextsize = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1120 | status = nextsize & 1
1121 | print("==================================")
1122 | print(" Chunk info ")
1123 | print("==================================")
1124 | if status:
1125 | if chunkaddr in fastchunk:
1126 | print("\033[1;32mStatus : \033[1;34m Freed (fast) \033[37m")
1127 | elif chunkaddr in all_tcache_entry:
1128 | print("\033[1;32mStatus : \033[1;34m Freed (tcache) \033[37m")
1129 | else:
1130 | print("\033[1;32mStatus : \033[31m Used \033[37m")
1131 | else:
1132 | print("\033[1;32mStatus : \033[1;34m Freed \033[37m")
1133 | unlinkable(chunkaddr, fd, bk)
1134 | freeable(chunkaddr)
1135 | print("\033[32mprev_size :\033[37m 0x%x " % prev_size)
1136 | print("\033[32msize :\033[37m 0x%x " % (size & 0xfffffffffffffff8))
1137 | print("\033[32mprev_inused :\033[37m %x " % (size & 1))
1138 | print("\033[32mis_mmap :\033[37m %x " % (size & 2))
1139 | print("\033[32mnon_mainarea :\033[37m %x " % (size & 4))
1140 | if not status:
1141 | print("\033[32mfd :\033[37m 0x%x " % fd)
1142 | print("\033[32mbk :\033[37m 0x%x " % bk)
1143 | if size >= 512 * (capsize / 4):
1144 | cmd = "x/" + word + hex(chunkaddr + capsize * 4)
1145 | fd_nextsize = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1146 | cmd = "x/" + word + hex(chunkaddr + capsize * 5)
1147 | bk_nextsize = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1148 | print("\033[32mfd_nextsize :\033[37m 0x%x " % fd_nextsize)
1149 | print("\033[32mbk_nextsize :\033[37m 0x%x " % bk_nextsize)
1150 | except:
1151 | print("Can't access memory")
1152 |
1153 |
1154 | def freeptr(ptr):
1155 | if capsize == 0:
1156 | arch = getarch()
1157 | freeable(ptr - capsize * 2)
1158 |
1159 |
1160 | def chunkptr(ptr):
1161 | if capsize == 0:
1162 | arch = getarch()
1163 | chunkinfo(ptr - capsize * 2)
1164 |
1165 |
1166 | def mergeinfo(victim):
1167 | global fastchunk
1168 | if capsize == 0:
1169 | arch = getarch()
1170 | chunkaddr = victim
1171 | try:
1172 | if not get_heap_info():
1173 | print("Can't find heap info")
1174 | return
1175 | print("==================================")
1176 | print(" Merge info ")
1177 | print("==================================")
1178 | cmd = "x/" + word + hex(chunkaddr)
1179 | prev_size = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1180 | cmd = "x/" + word + hex(chunkaddr + capsize * 1)
1181 | size = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1182 | cmd = "x/" + word + hex(chunkaddr + (size & 0xfffffffffffffff8) + capsize)
1183 | nextsize = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1184 | status = nextsize & 1
1185 | if status:
1186 | if chunkaddr in fastchunk:
1187 | print("The chunk is freed")
1188 | else:
1189 | if (size & 0xfffffffffffffff8) <= 0x80:
1190 | print("The chunk will be a\033[32m fastchunk\033[37m")
1191 | else:
1192 | prev_status = size & 1
1193 | next_chunk = chunkaddr + (size & 0xfffffffffffffff8)
1194 | cmd = "x/" + word + hex(next_chunk + (nextsize & 0xfffffffffffffff8) + capsize)
1195 | if next_chunk != top["addr"]:
1196 | next_nextsize = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1197 | next_status = next_nextsize & 1
1198 | if not prev_status: # if prev chunk is freed
1199 | prev_chunk = chunkaddr - prev_size
1200 | if next_chunk == top["addr"]: # if next chunk is top
1201 | print("The chunk will merge into top , top will be \033[1;33m0x%x\033[37m " % prev_chunk)
1202 | print("\033[32mUnlink info : \033[1;33m0x%x\033[37m" % prev_chunk)
1203 | unlinkable(prev_chunk)
1204 | elif not next_status: # if next chunk is freed
1205 | print("The chunk and \033[1;33m0x%x\033[0m will merge into \033[1;33m0x%x\033[37m" % (
1206 | next_chunk, prev_chunk))
1207 | print("\033[32mUnlink info : \033[1;33m0x%x\033[37m" % prev_chunk)
1208 | unlinkable(prev_chunk)
1209 | print("\033[32mUnlink info : \033[1;33m0x%x\033[37m" % next_chunk)
1210 | unlinkable(next_chunk)
1211 | else:
1212 | print("The chunk will merge into \033[1;33m0x%x\033[37m" % prev_chunk)
1213 | print("\033[32mUnlink info : \033[1;33m0x%x\033[37m" % prev_chunk)
1214 | unlinkable(prev_chunk)
1215 | else:
1216 | if next_chunk == top["addr"]: # if next chunk is top
1217 | print("The chunk will merge into top , top will be \033[1;34m0x%x\033[37m" % chunkaddr)
1218 | elif not next_status: # if next chunk is freed
1219 | print("The chunk will merge with \033[1;33m0x%x\033[37m" % next_chunk)
1220 | print("\033[32mUnlink info : \033[1;33m0x%x\033[37m" % next_chunk)
1221 | unlinkable(next_chunk)
1222 | else:
1223 | print("The chunk will not merge with other")
1224 | else:
1225 | print("The chunk is freed")
1226 | except:
1227 | print("Can't access memory")
1228 |
1229 |
1230 | def force(target):
1231 | if capsize == 0:
1232 | arch = getarch()
1233 | if not get_heap_info():
1234 | print("Can't find heap info")
1235 | return
1236 | if target % capsize != 0:
1237 | print("Not alignment")
1238 | else:
1239 | nb = target - top["addr"] - capsize * 2
1240 | print("nb = %d" % nb)
1241 |
1242 |
1243 | def putfastbin(arena=None):
1244 | if capsize == 0:
1245 | arch = getarch()
1246 |
1247 | if not get_heap_info(arena):
1248 | print("Can't find heap info")
1249 | return False
1250 | for i, bins in enumerate(fastbin):
1251 | cursize = (capsize * 2) * (i + 2)
1252 | print("\033[32m(0x%02x) fastbin[%d]:\033[37m " % (cursize, i), end="")
1253 | for chunk in bins:
1254 | if "memerror" in chunk:
1255 | print("\033[31m0x%x (%s)\033[37m" % (chunk["addr"], chunk["memerror"]), end="")
1256 | elif chunk["size"] != cursize and chunk["addr"] != 0:
1257 | print("\033[36m0x%x (size error (0x%x))\033[37m" % (chunk["addr"], chunk["size"]), end="")
1258 | elif chunk["overlap"] and chunk["overlap"][0]:
1259 | print("\033[31m0x%x (overlap chunk with \033[36m0x%x(%s)\033[31m )\033[37m" % (
1260 | chunk["addr"], chunk["overlap"][0]["addr"], chunk["overlap"][1]), end="")
1261 | elif chunk == bins[0]:
1262 | print("\033[34m0x%x\033[37m" % chunk["addr"], end="")
1263 | else:
1264 | if print_overlap:
1265 | if find_overlap(chunk, bins):
1266 | print("\033[31m0x%x\033[37m" % chunk["addr"], end="")
1267 | else:
1268 | print("0x%x" % chunk["addr"], end="")
1269 | else:
1270 | print("0x%x" % chunk["addr"], end="")
1271 | if chunk != bins[-1]:
1272 | print(" --> ", end="")
1273 | print("")
1274 | return True
1275 |
1276 |
1277 | def put_tcache():
1278 | if not tcache_enable:
1279 | return
1280 | for i, entry in enumerate(tcache_entry):
1281 | if capsize == 4:
1282 | cursize = (8 * 2) * (i + 1)
1283 | else:
1284 | cursize = (8 * 2) * (i + 2)
1285 | if len(tcache_entry[i]) > 0:
1286 | print(
1287 | "\033[33;1m(0x%02x) tcache_entry[%d]\033[32m(%d)\033[33;1m:\033[37m " % (cursize, i, tcache_count[i]),
1288 | end="")
1289 | elif tcache_count[i] > 0:
1290 | print("\033[33;1m(0x%02x) tcache_entry[%d]\033[31;1m(%d)\033[33;1m:\033[37m 0\n" % (
1291 | cursize, i, tcache_count[i]), end="")
1292 | for chunk in entry:
1293 | if "memerror" in chunk:
1294 | print("\033[31m0x%x (%s)\033[37m" % (chunk["addr"] + capsize * 2, chunk["memerror"]), end="")
1295 | elif chunk["overlap"] and chunk["overlap"][0]:
1296 | print("\033[31m0x%x (overlap chunk with \033[36m0x%x(%s)\033[31m )\033[37m" % (
1297 | chunk["addr"] + capsize * 2, chunk["overlap"][0]["addr"], chunk["overlap"][1]), end="")
1298 | elif chunk == entry[0]:
1299 | print("\033[34m0x%x\033[37m" % (chunk["addr"] + capsize * 2), end="")
1300 | else:
1301 | if print_overlap:
1302 | if find_overlap(chunk, entry):
1303 | print("\033[31m0x%x\033[37m" % chunk["addr"], end="")
1304 | else:
1305 | print("0x%x" % (chunk["addr"] + capsize * 2), end="")
1306 | else:
1307 | print("0x%x" % (chunk["addr"] + capsize * 2), end="")
1308 | if chunk != entry[-1]:
1309 | print(" --> ", end="")
1310 | if len(tcache_entry[i]) > 0:
1311 | print("")
1312 |
1313 | return True
1314 |
1315 |
1316 | def putheapinfo(arena=None):
1317 | if capsize == 0:
1318 | arch = getarch()
1319 | if not putfastbin(arena):
1320 | return
1321 | if "memerror" in top:
1322 | print("\033[35m %20s:\033[31m 0x%x \033[33m(size : 0x%x)\033[31m (%s)\033[37m " % (
1323 | "top", top["addr"], top["size"], top["memerror"]))
1324 | else:
1325 | print("\033[35m %20s:\033[34m 0x%x \033[33m(size : 0x%x)\033[37m " % ("top", top["addr"], top["size"]))
1326 |
1327 | print("\033[35m %20s:\033[34m 0x%x \033[33m(size : 0x%x)\033[37m " % (
1328 | "last_remainder", last_remainder["addr"], last_remainder["size"]))
1329 | if unsortbin and len(unsortbin) > 0:
1330 | print("\033[35m %20s:\033[37m " % "unsortbin", end="")
1331 | for chunk in unsortbin:
1332 | if "memerror" in chunk:
1333 | print("\033[31m0x%x (%s)\033[37m" % (chunk["addr"], chunk["memerror"]), end="")
1334 | elif chunk["overlap"] and chunk["overlap"][0]:
1335 | print("\033[31m0x%x (overlap chunk with \033[36m0x%x(%s)\033[31m )\033[37m" % (
1336 | chunk["addr"], chunk["overlap"][0]["addr"], chunk["overlap"][1]), end="")
1337 | elif chunk == unsortbin[-1]:
1338 | print("\033[34m0x%x\033[37m \33[33m(size : 0x%x)\033[37m" % (chunk["addr"], chunk["size"]), end="")
1339 | else:
1340 | print("0x%x \33[33m(size : 0x%x)\033[37m" % (chunk["addr"], chunk["size"]), end="")
1341 | if chunk != unsortbin[-1]:
1342 | print(" <--> ", end="")
1343 | print("")
1344 | else:
1345 | print("\033[35m %20s:\033[37m 0x%x" % ("unsortbin", 0)) # no chunk in unsortbin
1346 | for size, bins in smallbin.items():
1347 | if tcache_enable:
1348 | cur_size = 8
1349 | else:
1350 | cur_size = capsize
1351 | idx = int((int(size, 16) / (cur_size * 2))) - 2
1352 | print("\033[33m(0x%03x) %s[%2d]:\033[37m " % (int(size, 16), "smallbin", idx), end="")
1353 | for chunk in bins:
1354 | if "memerror" in chunk:
1355 | print("\033[31m0x%x (%s)\033[37m" % (chunk["addr"], chunk["memerror"]), end="")
1356 | elif chunk["size"] != int(size, 16):
1357 | print("\033[36m0x%x (size error (0x%x))\033[37m" % (chunk["addr"], chunk["size"]), end="")
1358 | elif chunk["overlap"] and chunk["overlap"][0]:
1359 | print("\033[31m0x%x (overlap chunk with \033[36m0x%x(%s)\033[31m )\033[37m" % (
1360 | chunk["addr"], chunk["overlap"][0]["addr"], chunk["overlap"][1]), end="")
1361 | elif chunk == bins[-1]:
1362 | print("\033[34m0x%x\033[37m" % chunk["addr"], end="")
1363 | else:
1364 | print("0x%x " % chunk["addr"], end="")
1365 | if chunk != bins[-1]:
1366 | print(" <--> ", end="")
1367 | print("")
1368 | for idx, bins in largebin.items():
1369 | print("\033[33m %15s[%2d]:\033[37m " % ("largebin", idx - 64), end="")
1370 | for chunk in bins:
1371 | if "memerror" in chunk:
1372 | print("\033[31m0x%x (%s)\033[37m" % (chunk["addr"], chunk["memerror"]), end="")
1373 | elif chunk["overlap"] and chunk["overlap"][0]:
1374 | print("\033[31m0x%x (overlap chunk with \033[36m0x%x(%s)\033[31m )\033[37m" % (
1375 | chunk["addr"], chunk["overlap"][0]["addr"], chunk["overlap"][1]), end="")
1376 | elif largbin_index(chunk["size"]) != idx:
1377 | print(
1378 | "\033[31m0x%x (incorrect bin size :\033[36m 0x%x\033[31m)\033[37m" % (chunk["addr"], chunk["size"]),
1379 | end="")
1380 | elif chunk == bins[-1]:
1381 | print("\033[34m0x%x\033[37m \33[33m(size : 0x%x)\033[37m" % (chunk["addr"], chunk["size"]), end="")
1382 | else:
1383 | print("0x%x \33[33m(size : 0x%x)\033[37m" % (chunk["addr"], chunk["size"]), end="")
1384 | if chunk != bins[-1]:
1385 | print(" <--> ", end="")
1386 | print("")
1387 | if not arena:
1388 | put_tcache()
1389 | if corruptbin:
1390 | print("\033[31m Some bins is corrupted !\033[37m")
1391 |
1392 |
1393 | def putarenainfo():
1394 | set_main_arena()
1395 | if capsize == 0:
1396 | arch = getarch()
1397 | cur_arena = 0
1398 | if main_arena:
1399 | try:
1400 | count = 0
1401 | print(" Main Arena ".center(50, "="))
1402 | putheapinfo(main_arena)
1403 | cmd = "x/" + word + "&main_arena.next"
1404 | cur_arena = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1405 | while cur_arena != main_arena:
1406 | count += 1
1407 | print((" Arena " + str(count) + " ").center(50, "="))
1408 | putheapinfo(cur_arena)
1409 | cur_arena = int(
1410 | gdb.execute("x/" + word + hex(cur_arena + nextoff), to_string=True).split(":")[1].strip(), 16)
1411 | except:
1412 | print("Memory Error (heap)")
1413 | else:
1414 | print("Can't find heap info ")
1415 |
1416 |
1417 | def putheapinfoall():
1418 | cur_thread_id = get_curthread()
1419 | all_threads = get_all_threads()
1420 | for thread_id in all_threads:
1421 | if thread_id == cur_thread_id:
1422 | print("\033[33;1m" + (" Thread " + str(thread_id) + " ").center(50, "=") + "\033[0m", end="")
1423 | else:
1424 | print((" Thread " + str(thread_id) + " ").center(50, "="), end="")
1425 | result = thread_cmd_execute(thread_id, "heapinfo")
1426 | print(result.split("):")[1], end="")
1427 |
1428 |
1429 | def putinused():
1430 | print("\033[33m %s:\033[37m " % "inused ", end="")
1431 | for addr, (start, end, chunk) in allocmemoryarea.items():
1432 | print("0x%x," % (chunk["addr"]), end="")
1433 | print("")
1434 |
1435 |
1436 | def parse_heap(arena=None):
1437 | if capsize == 0:
1438 | arch = getarch()
1439 | if not get_heap_info(arena):
1440 | print("can't find heap info")
1441 | return
1442 |
1443 | hb = get_heapbase()
1444 | chunkaddr = hb
1445 | if not chunkaddr:
1446 | print("Can't find heap")
1447 | return
1448 | print('\033[1;33m{:<20}{:<20}{:<21}{:<20}{:<18}{:<18}\033[0m'.format('addr', 'prev', 'size', 'status', 'fd', 'bk'))
1449 | while chunkaddr != top["addr"]:
1450 | try:
1451 | cmd = "x/" + word + hex(chunkaddr)
1452 | prev_size = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1453 | cmd = "x/" + word + hex(chunkaddr + capsize * 1)
1454 | size = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1455 | cmd = "x/" + word + hex(chunkaddr + capsize * 2)
1456 | if size == 0 and chunkaddr == hb:
1457 | chunkaddr += capsize * 2
1458 | continue
1459 | fd = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1460 | cmd = "x/" + word + hex(chunkaddr + capsize * 3)
1461 | bk = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1462 | cmd = "x/" + word + hex(chunkaddr + (size & 0xfffffffffffffff8) + capsize)
1463 | nextsize = int(gdb.execute(cmd, to_string=True).split(":")[1].strip(), 16)
1464 | status = nextsize & 1
1465 | size = size & 0xfffffffffffffff8
1466 | if size == 0:
1467 | print("\033[31mCorrupt ?! \033[0m(size == 0) (0x%x)" % chunkaddr)
1468 | break
1469 | if status:
1470 | if chunkaddr in fastchunk or chunkaddr in all_tcache_entry:
1471 | msg = "\033[1;34m Freed \033[0m"
1472 | print(
1473 | '0x{:<18x}0x{:<18x}0x{:<18x}{:<16}{:>18}{:>18}'.format(chunkaddr, prev_size, size, msg, hex(fd),
1474 | "None"))
1475 | else:
1476 | msg = "\033[31m Used \033[0m"
1477 | print(
1478 | '0x{:<18x}0x{:<18x}0x{:<18x}{:<16}{:>18}{:>18}'.format(chunkaddr, prev_size, size, msg, "None",
1479 | "None"))
1480 | else:
1481 | msg = "\033[1;34m Freed \033[0m"
1482 | print('0x{:<18x}0x{:<18x}0x{:<18x}{:<16}{:>18}{:>18}'.format(chunkaddr, prev_size, size, msg, hex(fd),
1483 | hex(bk)))
1484 | chunkaddr = chunkaddr + (size & 0xfffffffffffffff8)
1485 |
1486 | if chunkaddr > top["addr"]:
1487 | print("\033[31mCorrupt ?!\033[0m")
1488 | break
1489 | except:
1490 | print("Corrupt ?!")
1491 | break
1492 |
1493 |
1494 | def fastbin_idx(size):
1495 | if capsize == 0:
1496 | arch = getarch()
1497 | if capsize == 8:
1498 | return (size >> 4) - 2
1499 | else:
1500 | return (size >> 3) - 2
1501 |
1502 |
1503 | def fake_fast(addr, size):
1504 | if not get_heap_info():
1505 | print("Can't find heap info")
1506 | return
1507 | result = []
1508 | idx = fastbin_idx(size)
1509 | chunk_size = size & 0xfffffffffffffff8
1510 | start = addr - chunk_size
1511 | chunk_data = gdb.selected_inferior().read_memory(start, chunk_size)
1512 | for offset in range(chunk_size - 4):
1513 | fake_size = u32(chunk_data[offset:offset + 4])
1514 | if fastbin_idx(fake_size) == idx:
1515 | if ((fake_size & 2 == 2) and (fake_size & 4 == 4)) or (fake_size & 4 == 0):
1516 | padding = addr - (start + offset - capsize) - capsize * 2
1517 | result.append((start + offset - capsize, padding))
1518 | return result
1519 |
1520 |
1521 | def get_fake_fast(addr, size=None):
1522 | if capsize == 0:
1523 | arch = getarch()
1524 | fast_max = int(gdb.execute("x/" + word + "&global_max_fast", to_string=True).split(":")[1].strip(), 16)
1525 | if not fast_max:
1526 | fast_max = capsize * 0x10
1527 | if size:
1528 | chunk_list = fake_fast(addr, size)
1529 | for fakechunk in chunk_list:
1530 | if len(chunk_list) > 0:
1531 | print("\033[1;33mfake chunk : \033[1;0m0x{:<12x}\033[1;33m padding :\033[1;0m {:<8d}".format(
1532 | fakechunk[0], fakechunk[1]))
1533 | else:
1534 | for i in range(int(fast_max / (capsize * 2) - 1)):
1535 | size = capsize * 2 * 2 + i * capsize * 2
1536 | chunk_list = fake_fast(addr, size)
1537 | if len(chunk_list) > 0:
1538 | print("-- size : %s --" % hex(size))
1539 | for fakechunk in chunk_list:
1540 | print("\033[1;33mfake chunk :\033[1;0m 0x{:<12x}\033[1;33m padding :\033[1;0m {:<8d}".format(
1541 | fakechunk[0], fakechunk[1]))
1542 |
--------------------------------------------------------------------------------
/pwndbg/commands/angelheap.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | Pwngdb by angelboy
5 |
6 | https://github.com/scwuaptx/Pwngdb
7 | """
8 |
9 | import argparse
10 |
11 | import gdb
12 | import pwndbg.commands
13 | import pwndbg.angelheap as angelheap
14 |
15 | # initialize angelheap when process starts
16 | gdb.execute('''define hook-run
17 | python
18 | import pwndbg.angelheap as angelheap
19 | angelheap.init_angelheap()
20 | end
21 | end
22 | ''')
23 |
24 | parser = argparse.ArgumentParser()
25 | parser.description = "Trace the malloc and free and detect some error."
26 | parser.add_argument("option", nargs='?', type=str, default="on", help="on or off")
27 | @pwndbg.commands.ArgparsedCommand(parser)
28 | def tracemalloc(option="on"):
29 | if option == "on":
30 | try:
31 | angelheap.trace_malloc()
32 | except:
33 | print("Can't create Breakpoint")
34 | else:
35 | angelheap.dis_trace_malloc()
36 |
37 |
38 | parser = argparse.ArgumentParser()
39 | parser.description = "Print some information of heap."
40 | parser.add_argument("arena", nargs='?', type=int, default=None, help="Address of arena")
41 | @pwndbg.commands.ArgparsedCommand(parser)
42 | @pwndbg.commands.OnlyWhenRunning
43 | def heapinfo(arena=None):
44 | angelheap.putheapinfo(arena)
45 |
46 |
47 | @pwndbg.commands.ArgparsedCommand("Print some information of multiheap.")
48 | @pwndbg.commands.OnlyWhenRunning
49 | def heapinfoall():
50 | angelheap.putheapinfoall()
51 |
52 |
53 | @pwndbg.commands.ArgparsedCommand("Print all arena info.")
54 | @pwndbg.commands.OnlyWhenRunning
55 | def arenainfo():
56 | angelheap.putarenainfo()
57 |
58 |
59 | parser = argparse.ArgumentParser()
60 | parser.description = "Print chunk information of victim."
61 | parser.add_argument("victim", nargs='?', type=int, help="Address of victim.")
62 | @pwndbg.commands.ArgparsedCommand(parser)
63 | @pwndbg.commands.OnlyWhenRunning
64 | def chunkinfo(victim):
65 | angelheap.chunkinfo(victim)
66 |
67 |
68 | parser = argparse.ArgumentParser()
69 | parser.description = "Print chunk is freeable."
70 | parser.add_argument("ptr", nargs='?', type=int, help="Address of user ptr.")
71 | @pwndbg.commands.ArgparsedCommand(parser)
72 | @pwndbg.commands.OnlyWhenRunning
73 | def free(ptr):
74 | angelheap.freeptr(ptr)
75 |
76 |
77 | parser = argparse.ArgumentParser()
78 | parser.description = "Print chunk information of user ptr."
79 | parser.add_argument("ptr", nargs='?', type=int, help="Address of user ptr.")
80 | @pwndbg.commands.ArgparsedCommand(parser)
81 | @pwndbg.commands.OnlyWhenRunning
82 | def chunkptr(ptr):
83 | angelheap.chunkptr(ptr)
84 |
85 |
86 | parser = argparse.ArgumentParser()
87 | parser.description = "Print merge information of victim."
88 | parser.add_argument("victim", nargs='?', type=int, help="Address of victim.")
89 | @pwndbg.commands.ArgparsedCommand(parser)
90 | @pwndbg.commands.OnlyWhenRunning
91 | def mergeinfo(victim):
92 | angelheap.mergeinfo(victim)
93 |
94 |
95 | parser = argparse.ArgumentParser()
96 | parser.description = "Calculate the nb in the house of force."
97 | parser.add_argument("target", nargs='?', type=int, help="Address of target you want to calculate.")
98 | @pwndbg.commands.ArgparsedCommand(parser)
99 | @pwndbg.commands.OnlyWhenRunning
100 | def force(target):
101 | angelheap.force(target)
102 |
103 |
104 | @pwndbg.commands.ArgparsedCommand("Print the fastbin.")
105 | @pwndbg.commands.OnlyWhenRunning
106 | def printfastbin():
107 | angelheap.putfastbin()
108 |
109 |
110 | @pwndbg.commands.ArgparsedCommand("Print the inuse chunk.")
111 | @pwndbg.commands.OnlyWhenRunning
112 | def inused():
113 | angelheap.putinused()
114 |
115 |
116 | @pwndbg.commands.ArgparsedCommand("Parse heap.")
117 | @pwndbg.commands.OnlyWhenRunning
118 | def parseheap():
119 | angelheap.parse_heap()
120 |
121 |
122 | parser = argparse.ArgumentParser()
123 | parser.description = "Get fake fast chunks information."
124 | parser.add_argument("addr", nargs='?', type=int, help="Address of the fake chunk.")
125 | parser.add_argument("size", nargs='?', type=int, help="Size of the fake chunk.")
126 | @pwndbg.commands.ArgparsedCommand(parser)
127 | @pwndbg.commands.OnlyWhenRunning
128 | def fakefast(addr, size):
129 | angelheap.get_fake_fast(addr, size)
130 |
--------------------------------------------------------------------------------
/pwndbg/commands/pwngdb.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | Pwngdb by angelboy
5 |
6 | https://github.com/scwuaptx/Pwngdb
7 | """
8 | from __future__ import absolute_import
9 | from __future__ import division
10 | from __future__ import print_function
11 | from __future__ import unicode_literals
12 |
13 | import subprocess
14 | import argparse
15 |
16 | import gdb
17 |
18 | import pwndbg.commands
19 | import pwndbg.gdblib.arch
20 | import pwndbg.gdblib.proc
21 | import pwndbg.search
22 | import pwndbg.gdblib.regs
23 | import pwndbg.gdblib.symbol
24 | import pwndbg.gdblib.memory
25 | import pwndbg.pwngdb as pwngdb
26 |
27 |
28 |
29 | parser = argparse.ArgumentParser()
30 | parser.description = "Automatically attach process by filename."
31 | parser.add_argument("processname", nargs='?', type=str, default=None, help="Process name")
32 | @pwndbg.commands.ArgparsedCommand(parser)
33 | def at(processname=None):
34 | if processname is None:
35 | processname = pwndbg.gdblib.proc.exe
36 | try :
37 | pidlist = map(int, subprocess.check_output('pidof $(basename {})'.format(processname), shell=True).decode('utf8').split())
38 |
39 | for pid in pidlist:
40 | if pid == pwndbg.gdblib.proc.pid:
41 | continue
42 | print('attaching to {} ...'.format(processname))
43 | gdb.execute("attach {}".format(pid))
44 | pwngdb.getheapbase()
45 | pwngdb.libcbase()
46 | pwngdb.codeaddr()
47 | pwngdb.ldbase()
48 | return
49 |
50 | print("already attached on {}".format(pwndbg.gdblib.proc.pid))
51 | except:
52 | print("no such process:", processname)
53 |
54 |
55 | @pwndbg.commands.ArgparsedCommand("Get libc base.")
56 | @pwndbg.commands.OnlyWhenRunning
57 | def libc():
58 | print("\033[34m" + "libc : " + "\033[37m" + hex(pwngdb.libcbase()))
59 |
60 |
61 | @pwndbg.commands.ArgparsedCommand("Get heapbase.")
62 | @pwndbg.commands.OnlyWhenRunning
63 | def heapbase():
64 | heapbase_addr = pwngdb.getheapbase()
65 | if heapbase_addr :
66 | print("\033[34m" + "heapbase : " + "\033[37m" + hex(heapbase_addr))
67 | else :
68 | print("heap not found")
69 |
70 |
71 | @pwndbg.commands.ArgparsedCommand("Get ld.so base.")
72 | @pwndbg.commands.OnlyWhenRunning
73 | def ld():
74 | print("\033[34m" + "ld : " + "\033[37m" + hex(pwngdb.ldbase()))
75 |
76 |
77 | @pwndbg.commands.ArgparsedCommand("Get text base.")
78 | @pwndbg.commands.OnlyWhenRunning
79 | def codebase():
80 | codebs = pwngdb.codeaddr()[0]
81 | print("\033[34m" + "codebase : " + "\033[37m" + hex(codebs))
82 |
83 |
84 | @pwndbg.commands.ArgparsedCommand("Get tls base.")
85 | @pwndbg.commands.OnlyWhenRunning
86 | def pwngdb_tls():
87 | tlsaddr = pwngdb.gettls()
88 | if tlsaddr != -1:
89 | print("\033[34m" + "tls : " + "\033[37m" + hex(tlsaddr))
90 | else:
91 | print("cannot get tls")
92 |
93 |
94 | parser = argparse.ArgumentParser()
95 | parser.description = "Calculate the index of format string."
96 | parser.add_argument("addr", nargs='?', type=int, help="Address of the target")
97 | @pwndbg.commands.ArgparsedCommand(parser)
98 | @pwndbg.commands.OnlyWhenRunning
99 | def fmtarg(addr):
100 | if pwndbg.gdblib.arch.current == "i386":
101 | reg = "esp"
102 | elif pwndbg.gdblib.arch.current == "x86-64":
103 | reg = "rsp"
104 | else:
105 | print("arch not support")
106 | return
107 | start = pwndbg.gdblib.regs[reg]
108 | idx = (addr - start) / (pwndbg.gdblib.arch.ptrsize) + 6
109 | print("The index of format argument : %d" % idx)
110 |
111 |
112 | parser = argparse.ArgumentParser()
113 | parser.description = "Calculate the offset of libc."
114 | parser.add_argument("symbol", nargs='?', type=str, help="A symbol or an address")
115 | @pwndbg.commands.ArgparsedCommand(parser)
116 | @pwndbg.commands.OnlyWhenRunning
117 | def off(symbol):
118 | symaddr = pwngdb.getoff(symbol)
119 | if symaddr == -1 :
120 | print("symbol not found")
121 | return
122 |
123 | if type(symbol) is int :
124 | print("\033[34m" + hex(symbol) + " : " + "\033[37m" + hex(symaddr))
125 | else :
126 | print("\033[34m" + symbol + " : " + "\033[37m" + hex(symaddr))
127 |
128 |
129 | parser = argparse.ArgumentParser()
130 | parser.description = "Find the syscall gadget."
131 | parser.add_argument("mapping_name", nargs='?', type=str, default=None, help="Mapping to search [e.g. libc]")
132 | @pwndbg.commands.ArgparsedCommand(parser)
133 | @pwndbg.commands.OnlyWhenRunning
134 | def findsyscall(mapping_name=None):
135 | if mapping_name is None:
136 | mapping_name = pwndbg.gdblib.proc.exe
137 | arch = pwndbg.gdblib.arch.current
138 |
139 | if arch == "x86-64" :
140 | gdb.execute("search -e -x 0f05 {}".format(mapping_name))
141 | elif arch == "i386":
142 | gdb.execute("search -e -x cd80 {}".format(mapping_name))
143 | elif arch == "arm":
144 | gdb.execute("search -e -x 00df80bc {}".format(mapping_name))
145 | elif arch == "aarch64":
146 | gdb.execute("search -e -x 010000d4 {}".format(mapping_name))
147 | else :
148 | print("arch not support")
149 |
150 |
151 | parser = argparse.ArgumentParser()
152 | parser.description = "Find some function call."
153 | parser.add_argument("symbol", nargs='?', type=str, help="A symbol of a function")
154 | @pwndbg.commands.ArgparsedCommand(parser)
155 | @pwndbg.commands.OnlyWithFile
156 | def findcall(symbol):
157 | call = pwngdb.searchcall(symbol)
158 | print(call) if call != -1 else print("symbol not found")
159 |
160 |
161 | @pwndbg.commands.ArgparsedCommand("Print the GOT table by objdump.")
162 | @pwndbg.commands.OnlyWithFile
163 | def objdump_got():
164 | cmd = "objdump -R {} {}".format("--demangle" if pwngdb.iscplus() else "", pwndbg.gdblib.proc.exe)
165 | print(subprocess.check_output(cmd, shell=True)[:-2].decode("utf8").strip())
166 |
167 | @pwndbg.commands.ArgparsedCommand("Print dynamic section.")
168 | @pwndbg.commands.OnlyWithFile
169 | def dyn():
170 | print(subprocess.check_output("readelf -d {}".format(pwndbg.gdblib.proc.exe), shell=True).decode("utf8").strip())
171 |
172 |
173 | @pwndbg.commands.ArgparsedCommand("Print usefual variables or function in glibc.")
174 | @pwndbg.commands.OnlyWhenRunning
175 | def magic():
176 | print("========== function ==========")
177 | for f in pwngdb.magic_function :
178 | print("\033[34m" + f + ":" + "\033[33m" +hex(pwngdb.getoff(f)))
179 | print("\033[00m========== variables ==========")
180 | for v in pwngdb.magic_variable:
181 | addr = pwndbg.symbol.address(v)
182 | if addr is None:
183 | print("\033[34m" + v + ":" + "\033[33m" + "not found")
184 | offset = addr - pwngdb.libcbase()
185 | pad = 36 - len(v) - len(hex(offset)) - 2
186 | print("\033[34m%s\033[33m(%s)\033[37m%s: \033[37m0x%016x" % (v, hex(offset), ' ' *pad, addr))
187 |
188 |
189 | parser = argparse.ArgumentParser()
190 | parser.description = "Show FILE structure."
191 | parser.add_argument("addr", nargs='?', type=int, help="Address of the target")
192 | @pwndbg.commands.ArgparsedCommand(parser)
193 | @pwndbg.commands.OnlyWhenRunning
194 | def fp(addr):
195 | pwngdb.showfp(addr)
196 |
197 |
198 | @pwndbg.commands.ArgparsedCommand("Show FILE chain.")
199 | @pwndbg.commands.OnlyWithFile
200 | def fpchain():
201 | pwngdb.showfpchain()
202 |
203 |
204 | parser = argparse.ArgumentParser()
205 | parser.description = "Test house of orange."
206 | parser.add_argument("addr", nargs='?', type=int, help="Address of the target")
207 | @pwndbg.commands.ArgparsedCommand(parser)
208 | @pwndbg.commands.OnlyWhenRunning
209 | def orange(addr):
210 | pwngdb.testorange(addr)
211 |
212 | parser = argparse.ArgumentParser()
213 | parser.description = "Set the breakpoint at some function call."
214 | parser.add_argument("symbol", nargs='?', type=str, help="A symbol of a function")
215 | @pwndbg.commands.ArgparsedCommand(parser)
216 | @pwndbg.commands.OnlyWithFile
217 | def bcall(symbol):
218 | call = pwngdb.searchcall(symbol)
219 | if call == -1:
220 | print("symbol not found")
221 | return
222 | codebase = pwngdb.codeaddr()[0] if pwngdb.ispie() else 0
223 | for callbase in call.split('\n'):
224 | addr = int(callbase.split(':')[0], 16) + codebase
225 | gdb.execute("b *{}".format(hex(addr)))
226 |
227 |
--------------------------------------------------------------------------------
/pwndbg/pwngdb.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | """
4 | Pwngdb by angelboy
5 |
6 | https://github.com/scwuaptx/Pwngdb
7 | """
8 | from __future__ import absolute_import
9 | from __future__ import division
10 | from __future__ import print_function
11 | from __future__ import unicode_literals
12 |
13 | import re
14 | import struct
15 | import subprocess
16 |
17 | import gdb
18 |
19 | import pwndbg.gdblib.arch
20 | import pwndbg.gdblib.proc
21 | import pwndbg.search
22 | import pwndbg.gdblib.symbol
23 | import pwndbg.gdblib.memory
24 | import pwndbg.gdblib.vmmap
25 |
26 | magic_variable = ["__malloc_hook", "__free_hook", "__realloc_hook", "stdin", "stdout", "_IO_list_all",
27 | "__after_morecore_hook"]
28 | magic_function = ["system", "execve", "open", "read", "write", "gets", "setcontext+0x35"]
29 |
30 |
31 | def to_int(val):
32 | try:
33 | return int(str(val), 0)
34 | except:
35 | return val
36 |
37 |
38 | def procmap():
39 | data = gdb.execute("info proc exe", to_string=True)
40 | pid = re.search("process.*", data)
41 | if pid:
42 | pid = pid.group().split()[1]
43 | with open("/proc/{}/maps".format(pid), "r") as maps:
44 | return maps.read()
45 | else:
46 | return "error"
47 |
48 |
49 | def libcbase():
50 | for p in pwndbg.gdblib.vmmap.get():
51 | if re.search(r".*libc-.*", p.objfile):
52 | libcaddr = p.start
53 | gdb.execute("set $libc={}".format(hex(libcaddr)))
54 | return libcaddr
55 | return 0
56 |
57 |
58 | def getheapbase():
59 | for p in pwndbg.gdblib.vmmap.get():
60 | if re.search(r".*heap\]", p.objfile):
61 | heapbase = p.start
62 | gdb.execute("set $heap={}".format(hex(heapbase)))
63 | return heapbase
64 | return 0
65 |
66 |
67 | def ldbase():
68 | for p in pwndbg.gdblib.vmmap.get():
69 | if re.search(r".*ld.*\.so", p.objfile):
70 | ldbase = p.start
71 | gdb.execute("set $ld={}".format(hex(ldbase)))
72 | return ldbase
73 | return 0
74 |
75 |
76 | def codeaddr(): # ret (start, end)
77 | pat = ".*" + pwndbg.gdblib.proc.exe
78 | if pwndbg.gdblib.vmmap.get() and re.search(pat, pwndbg.gdblib.vmmap.get()[0].objfile):
79 | codebaseaddr = pwndbg.gdblib.vmmap.get()[0].start
80 | codeend = pwndbg.gdblib.vmmap.get()[0].end
81 | gdb.execute("set $code=%s" % hex(codebaseaddr))
82 | return (codebaseaddr, codeend)
83 | else:
84 | return (0, 0)
85 |
86 |
87 | def gettls():
88 | arch = pwndbg.gdblib.arch.current
89 |
90 | if arch == "i386":
91 | vsysaddr = gdb.execute("info functions __kernel_vsyscall", to_string=True).split("\n")[-2].split()[0].strip()
92 | value = struct.pack(" ", end="")
176 | chain_addr = int(gdb.parse_and_eval("&((struct _IO_FILE_plus *)" + hex(chain) + ").file._chain"))
177 | chain = pwndbg.gdblib.memory.read(chain_addr, pwndbg.gdblib.arch.ptrsize)
178 | chain = int.from_bytes(chain, byteorder=pwndbg.gdblib.arch.endian)
179 | print("0x%x" % chain, end="")
180 | print()
181 | except:
182 | print("Chain is corrupted")
183 |
184 |
185 | def testorange(addr):
186 | result = True
187 | mode_addr = int(gdb.parse_and_eval("&((struct _IO_FILE_plus *)" + hex(addr) + ").file._mode"))
188 | mode = pwndbg.gdblib.memory.read(mode_addr, pwndbg.gdblib.arch.ptrsize)
189 | mode = int.from_bytes(mode, byteorder=pwndbg.gdblib.arch.endian) & 0xffffffff
190 | write_ptr_address = int(gdb.parse_and_eval("&((struct _IO_FILE_plus *)" + hex(addr) + ").file._IO_write_ptr"))
191 | write_ptr = pwndbg.gdblib.memory.read(write_ptr_address, pwndbg.gdblib.arch.ptrsize)
192 | write_ptr = int.from_bytes(write_ptr, byteorder=pwndbg.gdblib.arch.endian)
193 | write_base_addr = int(gdb.parse_and_eval("&((struct _IO_FILE_plus *)" + hex(addr) + ").file._IO_write_base"))
194 | write_base = pwndbg.gdblib.memory.read(write_base_addr, pwndbg.gdblib.arch.ptrsize)
195 | write_base = int.from_bytes(write_base, byteorder=pwndbg.gdblib.arch.endian)
196 | if mode < 0x80000000 and mode != 0:
197 | try:
198 | wide_data_addr = int(gdb.parse_and_eval("&((struct _IO_FILE_plus *)" + hex(addr) + ").file._wide_data"))
199 | wide_data = pwndbg.gdblib.memory.read(wide_data_addr, pwndbg.gdblib.arch.ptrsize)
200 | wide_data = int.from_bytes(wide_data, byteorder=pwndbg.gdblib.arch.endian)
201 | w_write_ptr_addr = int(
202 | gdb.parse_and_eval("&((struct _IO_wide_data *)" + hex(wide_data) + ")._IO_write_ptr"))
203 | w_write_ptr = pwndbg.gdblib.memory.read(w_write_ptr_addr, pwndbg.gdblib.arch.ptrsize)
204 | w_write_ptr = int.from_bytes(w_write_ptr, byteorder=pwndbg.gdblib.arch.endian)
205 | w_write_base_addr = int(
206 | gdb.parse_and_eval("&((struct _IO_wide_data *)" + hex(wide_data) + ")._IO_write_base"))
207 | w_write_base = pwndbg.gdblib.memory.read(w_write_base_addr, pwndbg.gdblib.arch.ptrsize)
208 | w_write_base = int.from_bytes(w_write_base, byteorder=pwndbg.gdblib.arch.endian)
209 | if w_write_ptr <= w_write_base:
210 | print("\033[;1;31m_wide_data->_IO_write_ptr(0x%x) < _wide_data->_IO_write_base(0x%x)\033[1;37m" % (
211 | w_write_ptr, w_write_base))
212 | result = False
213 | except:
214 | print("\033;1;31mCan't access wide_data\033[1;37m")
215 | result = False
216 | else:
217 | if write_ptr <= write_base:
218 | print("\033[;1;31m_IO_write_ptr(0x%x) < _IO_write_base(0x%x)\033[1;37m" % (write_ptr, write_base))
219 | result = False
220 | if result:
221 | print("Result : \033[34mTrue\033[37m")
222 | overflow_addr = int(gdb.parse_and_eval("&((struct _IO_FILE_plus *)" + hex(addr) + ").vtable.__overflow"))
223 | overflow = pwndbg.gdblib.memory.read(overflow_addr, pwndbg.gdblib.arch.ptrsize)
224 | overflow = int.from_bytes(overflow, byteorder=pwndbg.gdblib.arch.endian)
225 | print("Func : \033[33m 0x%x\033[1;37m" % overflow)
226 | else:
227 | print("Result : \033[31mFalse\033[1;37m")
228 |
--------------------------------------------------------------------------------
/pwngdb.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | import gdb
3 | import subprocess
4 | import re
5 | import copy
6 | from os import path
7 |
8 | directory, file = path.split(__file__)
9 | directory = path.expanduser(directory)
10 | directory = path.abspath(directory)
11 | #sys.path.append(directory)
12 |
13 | # arch
14 | capsize = 0
15 | word = ""
16 | arch = ""
17 | magic_variable = ["__malloc_hook","__free_hook","__realloc_hook","stdin","stdout","_IO_list_all","__after_morecore_hook"]
18 | magic_function = ["system","execve","open","read","write","gets","setcontext+0x35"]
19 |
20 |
21 | def to_int(val):
22 | """
23 | Convert a string to int number
24 | from https://github.com/longld/peda
25 | """
26 | try:
27 | return int(str(val), 0)
28 | except:
29 | return None
30 |
31 | def normalize_argv(args, size=0):
32 | """
33 | Normalize argv to list with predefined length
34 | from https://github.com/longld/peda
35 | """
36 | args = list(args)
37 | for (idx, val) in enumerate(args):
38 | if to_int(val) is not None:
39 | args[idx] = to_int(val)
40 | if size and idx == size:
41 | return args[:idx]
42 |
43 | if size == 0:
44 | return args
45 | for i in range(len(args), size):
46 | args += [None]
47 | return args
48 |
49 | class PwnCmd(object):
50 | commands = []
51 | prevbp = []
52 | bpoff = []
53 | def __init__(self):
54 | # list all commands
55 | self.commands = [cmd for cmd in dir(self) if callable(getattr(self, cmd)) ]
56 |
57 | def libc(self):
58 | """ Get libc base """
59 | libcbs = libcbase()
60 |
61 | print("\033[34m" + "libc : " + "\033[37m" + hex(libcbs))
62 |
63 | def heapbase(self):
64 | """ Get heapbase """
65 | heapbase = getheapbase()
66 | if heapbase :
67 | print("\033[34m" + "heapbase : " + "\033[37m" + hex(heapbase))
68 | else :
69 | print("heap not found")
70 |
71 |
72 | def ld(self):
73 | """ Get ld.so base """
74 | print("\033[34m" + "ld : " + "\033[37m" + hex(ldbase()))
75 |
76 | def codebase(self):
77 | """ Get text base """
78 | codebs = codeaddr()[0]
79 | print("\033[34m" + "codebase : " + "\033[37m" + hex(codebs))
80 |
81 | def tls(self):
82 | """ Get tls base """
83 | print("\033[34m" + "tls : " + "\033[37m" + hex(gettls()))
84 |
85 | def canary(self):
86 | """ Get canary value """
87 | print("\033[34m" + "canary : " + "\033[37m" + hex(getcanary()))
88 |
89 | def fmtarg(self,*arg):
90 | (addr,) = normalize_argv(arg,1)
91 | getfmtarg(addr)
92 |
93 | def off(self,*arg) :
94 | """ Calculate the offset of libc """
95 | #(sym,)= normalize_argv(arg,1)
96 | (sym,) = normalize_argv(arg,1)
97 | symaddr = getoff(sym)
98 | if symaddr == 0 :
99 | print("Not found the symbol")
100 | else :
101 | if type(sym) is int :
102 | print("\033[34m" + hex(sym) + ":" + "\033[37m" +hex(symaddr))
103 | else :
104 | print("\033[34m" + sym + ":" + "\033[37m" +hex(symaddr))
105 |
106 | def fp(self,*arg):
107 | """ show FILE structure """
108 | (addr,) = normalize_argv(arg,1)
109 | showfp(addr)
110 |
111 | def fpchain(self):
112 | """ show FILE chain """
113 | showfpchain()
114 |
115 | def orange(self,*arg):
116 | """ test house of orange """
117 | (addr,) = normalize_argv(arg,1)
118 | if addr :
119 | testorange(addr)
120 | else :
121 | print("You need to specifiy an address")
122 |
123 | def fsop(self,*arg):
124 | """ test fsop """
125 | (addr,) = normalize_argv(arg,1)
126 | testfsop(addr)
127 |
128 | def magic(self):
129 | """ Print usefual variables or function in glibc """
130 | getarch()
131 |
132 | try :
133 | print("========== function ==========")
134 | for f in magic_function :
135 | print("\033[34m" + f + ":" + "\033[33m" +hex(getoff(f)))
136 | print("\033[00m========== variables ==========")
137 | for v in magic_variable :
138 | cmd = "x/" + word + "&" +v
139 | content = gdb.execute(cmd,to_string=True).split(":")[1].strip()
140 | offset = hex(getoff("&"+ v))
141 | pad = 36 - len(v) - len(offset) - 2
142 | print("\033[34m%s\033[33m(%s)\033[37m%s: \033[37m%s" % (v, offset, ' ' *pad, content))
143 | except :
144 | print("You need run the program first")
145 |
146 |
147 | def findsyscall(self):
148 | """ find the syscall gadget"""
149 | arch = getarch()
150 | start,end = codeaddr()
151 | if arch == "x86-64" :
152 | gdb.execute("find 0x050f " + hex(start) + " " + hex(end) )
153 | elif arch == "i386":
154 | gdb.execute("find 0x80cd " + hex(start) + " " + hex(end) )
155 | elif arch == "arm":
156 | gdb.execute("find 0xbc80df00 " + hex(start) + " " + hex(end) )
157 | elif arch == "aarch64":
158 | gdb.execute("find 0xd4000001 " + hex(start) + " " + hex(end) )
159 | else :
160 | print("error")
161 |
162 | def got(self):
163 | """ Print the got table """
164 | processname = getprocname()
165 | if processname :
166 | cmd = "objdump -R "
167 | if iscplus :
168 | cmd += "--demangle "
169 | cmd += "\"" + processname + "\""
170 | got = subprocess.check_output(cmd,shell=True)[:-2].decode('utf8')
171 | print(got)
172 | else :
173 | print("No current process or executable file specified." )
174 |
175 | def dyn(self):
176 | """ Print dynamic section """
177 | processname = getprocname()
178 | if processname :
179 | dyn = subprocess.check_output("readelf -d \"" + processname + "\"",shell=True).decode('utf8')
180 | print(dyn)
181 | else :
182 | print("No current process or executable file specified." )
183 |
184 | def rop(self):
185 | """ ROPgadget """
186 | procname = getprocname()
187 | if procname :
188 | subprocess.call("ROPgadget --binary \"" + procname +"\"",shell=True)
189 | else :
190 | print("No current process or executable file specified." )
191 |
192 | def findcall(self,*arg):
193 | """ Find some function call """
194 | (sym,)= normalize_argv(arg,1)
195 | output = searchcall(sym)
196 | print(output)
197 |
198 | def at(self,*arg):
199 | """ Attach by processname """
200 | (processname,) = normalize_argv(arg,1)
201 | if not processname :
202 | processname = getprocname(relative=True)
203 | if not processname :
204 | print("Attaching program: ")
205 | print("No executable file specified.")
206 | print("Use the \"file\" or \"exec-file\" command.")
207 | return
208 | try :
209 | print("Attaching to %s ..." % processname)
210 | pidlist = subprocess.check_output("pidof " + processname,shell=True).decode('utf8').split()
211 | gdb.execute("attach " + pidlist[0])
212 | getheapbase()
213 | libcbase()
214 | codeaddr()
215 | ldbase()
216 | except :
217 | print( "No such process" )
218 |
219 | def bcall(self,*arg):
220 | """ Set the breakpoint at some function call """
221 | (sym,)= normalize_argv(arg,1)
222 | call = searchcall(sym)
223 | if "not found" in call :
224 | print("symbol not found")
225 | else :
226 | if ispie():
227 | codebaseaddr,codeend = codeaddr()
228 | for callbase in call.split('\n')[:-1]:
229 | addr = int(callbase.split(':')[0],16) + codebaseaddr
230 | cmd = "b*" + hex(addr)
231 | print(gdb.execute(cmd,to_string=True))
232 | else:
233 | for callbase in call.split('\n')[:-1]:
234 | addr = int(callbase.split(':')[0],16)
235 | cmd = "b*" + hex(addr)
236 | print(gdb.execute(cmd,to_string=True))
237 |
238 | def boff(self,*arg):
239 | """ Set the breakpoint at some offset from base address """
240 | (sym,) = normalize_argv(arg,1)
241 | codebaseaddr,codeend = codeaddr()
242 | if sym not in self.bpoff:
243 | self.bpoff.append(sym)
244 | cmd = "b*" + hex(codebaseaddr + sym)
245 | x = gdb.execute(cmd,to_string=True)
246 | y = x.rstrip().split("\n")[-1].split()[1]
247 | self.prevbp.append(y)
248 | print(x.rstrip())
249 |
250 | def tboff(self,*arg):
251 | """ Set temporary breakpoint at some offset from base address """
252 | (sym,) = normalize_argv(arg,1)
253 | codebaseaddr,codeend = codeaddr()
254 | cmd = "tb*" + hex(codebaseaddr + sym)
255 | print(gdb.execute(cmd,to_string=True))
256 |
257 | def atboff(self,*arg):
258 | """ Attach and set breakpoints accordingly """
259 | (sym,) = normalize_argv(arg,1)
260 | cmd = "attach " + str(sym)
261 | print(gdb.execute(cmd,to_string=True))
262 | x = len(self.prevbp)
263 | while x > 0:
264 | i = self.prevbp.pop(0)
265 | cmd = "del " + i
266 | gdb.execute(cmd,to_string=True)
267 | x -= 1
268 | for i in self.bpoff:
269 | self.boff(hex(i))
270 |
271 | def doff(self,*arg):
272 | """ Delete the breakpoint using breakpoint number at some offset from base address """
273 | (sym,) = normalize_argv(arg,1)
274 | if str(sym) not in self.prevbp:
275 | return
276 | codebaseaddr,codeend = codeaddr()
277 | cmd = "i b " + str(sym)
278 | x = gdb.execute(cmd,to_string=True)
279 | y = int(x.rstrip().split("\n")[1].split()[4], 16) - codebaseaddr
280 | cmd = "del " + str(sym)
281 | print(gdb.execute(cmd,to_string=True).rstrip())
282 | self.bpoff.remove(y)
283 | self.prevbp.remove(str(sym))
284 |
285 | def xo(self,*arg):
286 | """ Examine at offset from base address """
287 | (_,arg1,) = normalize_argv(arg,2)
288 | cmd = "x" + arg[0] + " "
289 | if arg1:
290 | codebaseaddr,_ = codeaddr()
291 | cmd += hex(codebaseaddr + arg1)
292 | print(gdb.execute(cmd,to_string=True)[:-1])
293 |
294 | class PwngdbCmd(gdb.Command):
295 | """ Pwngdb command wrapper """
296 | def __init__(self):
297 | super(PwngdbCmd,self).__init__("pwngdb",gdb.COMMAND_USER)
298 |
299 | def try_eval(self, expr):
300 | try:
301 | return gdb.parse_and_eval(expr)
302 | except:
303 | #print("Unable to parse expression: {}".format(expr))
304 | return expr
305 |
306 | def eval_argv(self, expressions):
307 | """ Leave command alone, let GDB parse and evaluate arguments """
308 | return [expressions[0]] + [ self.try_eval(expr) for expr in expressions[1:] ]
309 |
310 | def invoke(self,args,from_tty):
311 | self.dont_repeat()
312 | # Don't eval expression in PwngdbCmd commands
313 | #expressions = gdb.string_to_argv(args)
314 | #arg = self.eval_argv(expressions)
315 | arg = args.split()
316 | if len(arg) > 0 :
317 | cmd = arg[0]
318 | if cmd in pwncmd.commands :
319 | func = getattr(pwncmd,cmd)
320 | func(*arg[1:])
321 | else :
322 | print("Unknown command")
323 | else :
324 | print("Unknown command")
325 |
326 | return
327 |
328 | class PwngdbAlias(gdb.Command):
329 | """ Pwngdb Alias """
330 |
331 | def __init__(self,alias,command):
332 | self.command = command
333 | super(PwngdbAlias,self).__init__(alias,gdb.COMMAND_NONE)
334 |
335 | def invoke(self,args,from_tty):
336 | self.dont_repeat()
337 | gdb.execute("%s %s" % (self.command,args))
338 |
339 | def getarch():
340 | global capsize
341 | global word
342 | global arch
343 | data = gdb.execute('show arch',to_string = True)
344 | tmp = re.search("currently.*",data)
345 | if tmp :
346 | info = tmp.group()
347 | if "x86-64" in info:
348 | capsize = 8
349 | word = "gx "
350 | arch = "x86-64"
351 | return "x86-64"
352 | elif "aarch64" in info :
353 | capsize = 8
354 | word = "gx "
355 | arch = "aarch64"
356 | return "aarch64"
357 | elif "arm" in info :
358 | capsize = 4
359 | word = "wx "
360 | arch = "arm"
361 | return "arm"
362 | else :
363 | word = "wx "
364 | capsize = 4
365 | arch = "i386"
366 | return "i386"
367 | else :
368 | return "error"
369 |
370 | def procmap():
371 | data = gdb.execute('info proc exe',to_string = True)
372 | pid = re.search('process.*',data)
373 | if pid :
374 | pid = pid.group()
375 | pid = pid.split()[1]
376 | maps = open("/proc/" + pid + "/maps","r")
377 | infomap = maps.read()
378 | maps.close()
379 | return infomap
380 | else :
381 | return "error"
382 |
383 | def iscplus():
384 | name = getprocname()
385 | data = subprocess.check_output("readelf -s " + name,shell=True).decode('utf8')
386 | if "CXX" in data :
387 | return True
388 | else :
389 | return False
390 |
391 | def getprocname(relative=False):
392 | procname = None
393 | try:
394 | data = gdb.execute("info proc exe",to_string=True)
395 | procname = re.search("exe.*",data).group().split("=")[1][2:-1]
396 | except:
397 | data = gdb.execute("info files",to_string=True)
398 | if data:
399 | procname = re.search('Symbols from "(.*)"',data).group(1)
400 | if procname and relative :
401 | return procname.split("/")[-1]
402 | return procname
403 |
404 | def libcbase():
405 | infomap = procmap()
406 | data = re.search(r".*libc.*\.so",infomap)
407 | if data :
408 | libcaddr = data.group().split("-")[0]
409 | gdb.execute("set $libc=%s" % hex(int(libcaddr,16)))
410 | return int(libcaddr,16)
411 | else :
412 | return 0
413 |
414 | def ldbase():
415 | infomap = procmap()
416 | data = re.search(r".*ld.*\.so",infomap)
417 | if data :
418 | ldaddr = data.group().split("-")[0]
419 | gdb.execute("set $ld=%s" % hex(int(ldaddr,16)))
420 | return int(ldaddr,16)
421 | else :
422 | return 0
423 |
424 | def getheapbase():
425 | infomap = procmap()
426 | data = re.search(r".*heap\]",infomap)
427 | if data :
428 | heapbase = data.group().split("-")[0]
429 | gdb.execute("set $heap=%s" % hex(int(heapbase,16)))
430 | return int(heapbase,16)
431 | else :
432 | return 0
433 |
434 | def codeaddr(): # ret (start,end)
435 | infomap = procmap()
436 | procname = getprocname()
437 | pat = ".*" + procname
438 | data = re.findall(pat,infomap)
439 | if data :
440 | codebaseaddr = data[0].split("-")[0]
441 | codeend = data[0].split("-")[1].split()[0]
442 | gdb.execute("set $code=%s" % hex(int(codebaseaddr,16)))
443 | return (int(codebaseaddr,16),int(codeend,16))
444 | else :
445 | return (0,0)
446 |
447 | def gettls():
448 | arch = getarch()
449 | if arch == "i386" :
450 | vsysaddr = gdb.execute("info functions __kernel_vsyscall",to_string=True).split("\n")[-2].split()[0].strip()
451 | sysinfo= gdb.execute("find " + vsysaddr,to_string=True).split("\n")[2]
452 | match = re.search(r"0x[0-9a-z]{8}",sysinfo)
453 | if match :
454 | tlsaddr = int(match.group(),16) - 0x10
455 | else:
456 | return "error"
457 | return tlsaddr
458 | elif arch == "x86-64" :
459 | gdb.execute("call (int)arch_prctl(0x1003,$rsp-8)",to_string=True)
460 | data = gdb.execute("x/xg $rsp-8",to_string=True)
461 | return int(data.split(":")[1].strip(),16)
462 | else:
463 | return "error"
464 |
465 | def getcanary():
466 | arch = getarch()
467 | tlsaddr = gettls()
468 | if arch == "i386" :
469 | offset = 0x14
470 | result = gdb.execute("x/xw " + hex(tlsaddr + offset),to_string=True).split(":")[1].strip()
471 | return int(result ,16)
472 | elif arch == "x86-64" :
473 | offset = 0x28
474 | result = gdb.execute("x/xg " + hex(tlsaddr + offset),to_string=True).split(":")[1].strip()
475 | return int(result,16)
476 | else :
477 | return "error"
478 |
479 | def getoff(sym):
480 | libc = libcbase()
481 | if type(sym) is int :
482 | return sym-libc
483 | else :
484 | try :
485 | data = gdb.execute("x/x " + sym ,to_string=True)
486 | if "No symbol" in data:
487 | return 0
488 | else :
489 | data = re.search("0x.*[0-9a-f] ",data)
490 | data = data.group()
491 | symaddr = int(data[:-1] ,16)
492 | return symaddr-libc
493 | except :
494 | return 0
495 |
496 | def searchcall(sym):
497 | procname = getprocname()
498 | cmd = "objdump -d -M intel "
499 | if iscplus :
500 | cmd += "--demangle "
501 | cmd += "\"" + procname + "\""
502 | try :
503 | call = subprocess.check_output(cmd
504 | + "| grep \"call.*" + sym + "@plt>\"" ,shell=True).decode('utf8')
505 | return call
506 | except :
507 | return "symbol not found"
508 |
509 | def ispie():
510 | procname = getprocname()
511 | result = subprocess.check_output("readelf -h " + "\"" + procname +"\"",shell=True).decode('utf8')
512 | if re.search("DYN",result):
513 | return True
514 | else:
515 | return False
516 |
517 | def get_reg(reg):
518 | cmd = "info register " + reg
519 | result = int(gdb.execute(cmd,to_string=True).split()[1].strip(),16)
520 | return result
521 |
522 | def showfp(addr):
523 | if addr :
524 | cmd = "p *(struct _IO_FILE_plus *)" + hex(addr)
525 | try :
526 | result = gdb.execute(cmd)
527 | except :
528 | print("Can't not access 0x%x" % addr)
529 | else :
530 | print("You need to specify an address")
531 |
532 | def showfpchain():
533 | getarch()
534 | cmd = "x/" + word + "&_IO_list_all"
535 | head = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
536 | print("\033[32mfpchain:\033[1;37m ",end = "")
537 | chain = head
538 | print("0x%x" % chain,end = "")
539 | try :
540 | while chain != 0 :
541 | print(" --> ",end = "")
542 | cmd = "x/" + word + "&((struct _IO_FILE_plus *)" + hex(chain) +").file._chain"
543 | chain = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
544 | print("0x%x" % chain,end = "")
545 | print("")
546 | except :
547 | print("Chain is corrupted")
548 |
549 | def testorange(addr):
550 | getarch()
551 | result = True
552 | cmd = "x/" + word + "&((struct _IO_FILE_plus *)" + hex(addr) + ").file._mode"
553 | mode = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16) & 0xffffffff
554 | cmd = "x/" + word + "&((struct _IO_FILE_plus *)" + hex(addr) + ").file._IO_write_ptr"
555 | write_ptr = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
556 | cmd = "x/" + word + "&((struct _IO_FILE_plus *)" + hex(addr) + ").file._IO_write_base"
557 | write_base = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
558 | if mode < 0x80000000 and mode != 0:
559 | try :
560 | cmd = "x/" + word + "&((struct _IO_FILE_plus *)" + hex(addr) + ").file._wide_data"
561 | wide_data = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
562 | cmd = "x/" + word + "&((struct _IO_wide_data *)" + hex(wide_data) + ")._IO_write_ptr"
563 | w_write_ptr = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
564 | cmd = "x/" + word + "&((struct _IO_wide_data *)" + hex(wide_data) + ")._IO_write_base"
565 | w_write_base = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
566 | if w_write_ptr <= w_write_base :
567 | print("\033[;1;31m_wide_data->_IO_write_ptr(0x%x) < _wide_data->_IO_write_base(0x%x)\033[1;37m" % (w_write_ptr,w_write_base))
568 | result = False
569 | except :
570 | print("\033;1;31mCan't access wide_data\033[1;37m")
571 | result = False
572 | else :
573 | if write_ptr <= write_base :
574 | print("\033[;1;31m_IO_write_ptr(0x%x) < _IO_write_base(0x%x)\033[1;37m" % (write_ptr,write_base))
575 | result = False
576 | if result :
577 | print("Result : \033[34mTrue\033[37m")
578 | cmd = "x/" + word + "&((struct _IO_FILE_plus *)" + hex(addr) + ").vtable.__overflow"
579 | overflow = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
580 | print("Func : \033[33m 0x%x\033[1;37m" % overflow)
581 | else :
582 | print("Result : \033[31mFalse\033[1;37m")
583 |
584 | def testfsop(addr=None):
585 | getarch()
586 | if addr :
587 | cmd = "x/" + word + hex(addr)
588 | else :
589 | cmd = "x/" + word + "&_IO_list_all"
590 | head = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
591 | chain = head
592 | print("---------- fp : 0x%x ----------" % chain)
593 | testorange(chain)
594 | try :
595 | while chain != 0 :
596 | cmd = "x/" + word + "&((struct _IO_FILE_plus *)" + hex(chain) +").file._chain"
597 | chain = int(gdb.execute(cmd,to_string=True).split(":")[1].strip(),16)
598 | if chain != 0 :
599 | print("---------- fp : 0x%x ----------" % chain)
600 | testorange(chain)
601 | except :
602 | print("Chain is corrupted")
603 |
604 | def getfmtarg(addr):
605 | if capsize == 0 :
606 | getarch()
607 | if arch == "i386" :
608 | start = get_reg("esp")
609 | idx = (addr- start)/4
610 | print(r"The index of format argument : %d (\"\%%%d$p\")" % (idx,idx - 1))
611 | elif arch == "x86-64" :
612 | start = get_reg("rsp")
613 | idx = (addr - start)/8 + 6
614 | print(r"The index of format argument : %d (\"\%%%d$p\")" % (idx,idx - 1))
615 | else :
616 | print("Not support the arch")
617 |
618 | pwncmd = PwnCmd()
619 | PwngdbCmd()
620 | for cmd in pwncmd.commands :
621 | PwngdbAlias(cmd,"pwngdb %s" % cmd)
622 |
623 | gdb.execute("set print asm-demangle on")
624 |
625 |
--------------------------------------------------------------------------------