From 41bcc4bdd63248401bb78b2175eb57b10f0cd64c Mon Sep 17 00:00:00 2001 From: Stephen Fewer Date: Thu, 21 Apr 2011 11:41:39 +0000 Subject: [PATCH] First Commit! --- OllyHeapTrace.bdsproj | 245 ++++++ OllyHeapTraceGroup.bdsgroup | 19 + Readme.txt | 58 ++ bin/OLLYDBG.LIB | Bin 0 -> 8580 bytes bin/OllyHeapTrace.dll | Bin 0 -> 134144 bytes gpl-3.0.txt | 674 +++++++++++++++ screenshot1.gif | Bin 0 -> 16561 bytes src/OllyHeapTrace.res | Bin 0 -> 5112 bytes src/Plugin.h | 1599 +++++++++++++++++++++++++++++++++++ src/hooks.c | 194 +++++ src/hooks.h | 65 ++ src/main.c | 526 ++++++++++++ 12 files changed, 3380 insertions(+) create mode 100644 OllyHeapTrace.bdsproj create mode 100644 OllyHeapTraceGroup.bdsgroup create mode 100644 Readme.txt create mode 100644 bin/OLLYDBG.LIB create mode 100644 bin/OllyHeapTrace.dll create mode 100644 gpl-3.0.txt create mode 100644 screenshot1.gif create mode 100644 src/OllyHeapTrace.res create mode 100644 src/Plugin.h create mode 100644 src/hooks.c create mode 100644 src/hooks.h create mode 100644 src/main.c diff --git a/OllyHeapTrace.bdsproj b/OllyHeapTrace.bdsproj new file mode 100644 index 0000000..eac15f0 --- /dev/null +++ b/OllyHeapTrace.bdsproj @@ -0,0 +1,245 @@ + + + + + + + + + + + + src\main.c + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + False + 1 + 1 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + Harmony Security + OllyHeapTrace + 1.1.0.0 + + + + + + 1.0.0.0 + + + + + + + + + False + + + + + + + False + + False + + True + False + + + + + + + + + + + diff --git a/OllyHeapTraceGroup.bdsgroup b/OllyHeapTraceGroup.bdsgroup new file mode 100644 index 0000000..a213068 --- /dev/null +++ b/OllyHeapTraceGroup.bdsgroup @@ -0,0 +1,19 @@ + + + + + + + + + + + + + OllyHeapTrace.bdsproj + OllyHeapTrace.dll + + + + diff --git a/Readme.txt b/Readme.txt new file mode 100644 index 0000000..6af9a5a --- /dev/null +++ b/Readme.txt @@ -0,0 +1,58 @@ +OllyHeapTrace v1.1 (18 July 2008) + +By Stephen Fewer of Harmony Security (www.harmonysecurity.com) + +----[About]------------------------------------------------------------- +OllyHeapTrace is a plugin for OllyDbg (version 1.10) to trace the heap +operations being performed by a process. It will monitor heap +allocations and frees for multiple heaps, as well as operations such as +creating or destroying heaps and reallocations. All parameters as well +as return values are recorded and the trace is highlighted with a unique +colour for each heap being traced. + +The primary purpose of this plugin is to aid in the debugging of heap +overflows where you wish to be able to control the heap layout to +overwrite a specific structure such as a chunk header, critical section +structure or some application specific data. By tracing the heap +operations performed during actions you can control (for example opening +a connection, sending a packet, closing a connection) you can begin to +predict the heap operations and thus control the heap layout. + +----[Usage]------------------------------------------------------------- +Simply install the plugin and activate OllyHeapTrace when you wish to +begin tracing heap operations. OllyHeapTrace will automatically create +the breakpoints needed (RtlAllocateHeap, RtlFreeHeap, RtlCreateHeap, +RtlDestroyHeap, RtlReAllocateHeap, RtlSizeHeap, GetProcessHeap and +RtlInitializeCriticalSection, RtlDeleteCriticalSection) and record the +relevant information when these breakpoints are hit. To view the +heap trace select the OllyHeapTrace Log. + +Double clicking on any row in the OllyHeapTrace Log window will bring +you to the callers location in the OllyDbg disassembly window. The +recorded heap trace is highlighted with a unique colour for each heap +being traced. Right clicking on any row will give you some options such +as to view the heap chunks data or the heap itself (only a raw dump of +the memory is given, no parsing of the heap structures is performed). +You can also filter out unwanted information if you are only concerned +with a specific heap. + +OllyHeapTrace has been successfully tested on: + + Windows 2000 SP4 + + Windows XP SP3 + + Windows Server 2003 SP2 + + Windows Vista SP1 + + Windows Server 2008 SP1 + +----[License]----------------------------------------------------------- +The OllyHeapTrace source code is available under the GPLv3 license, +please see the included file gpl-3.0.txt for details. + +----[Changelog]--------------------------------------------------------- +v1.1 - 18 July 2008 ++ Bug fix in RtlSizeHeap and InitializeCriticalSection hooks. ++ Added hooks for RtlDeleteCriticalSection, RtlInitializeCriticalSection ++ Allow plugin to enable even when all hooks are not created. ++ Removed ability to see 8 bytes before heap chunk. + +v1.0 - 14 December 2007 ++ Initial release diff --git a/bin/OLLYDBG.LIB b/bin/OLLYDBG.LIB new file mode 100644 index 0000000000000000000000000000000000000000..cae83546375ae2d412eef53f99637c70d8a50cd0 GIT binary patch literal 8580 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 aWi6MnSl8PtDcQ5(Kff~S{{HjYX!JjnQiGoW literal 0 HcmV?d00001 diff --git a/bin/OllyHeapTrace.dll b/bin/OllyHeapTrace.dll new file mode 100644 index 0000000000000000000000000000000000000000..de991bffd4c4caf41ee67f9de62b115911a4fdec GIT binary patch literal 134144 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%0Dt4xzs$p`XO^K7>|e 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 Gf&UM^Wt;E- literal 0 HcmV?d00001 diff --git a/gpl-3.0.txt b/gpl-3.0.txt new file mode 100644 index 0000000..818433e --- /dev/null +++ b/gpl-3.0.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/screenshot1.gif b/screenshot1.gif new file mode 100644 index 0000000000000000000000000000000000000000..bcce6db8148ec6ce8ab01dfde5d138a081dbc0c2 GIT binary patch literal 16561 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 literal 0 HcmV?d00001 diff --git a/src/OllyHeapTrace.res b/src/OllyHeapTrace.res new file mode 100644 index 0000000000000000000000000000000000000000..0ef92f9248c1f9a3cb0a1b5476361bf1113d51d5 GIT binary patch literal 5112 zcmeHL!A=xG5Pge#5D#QcJQy!-j4{T8S(Ck(n4p3L5*4F{E7<@daaqWU2XFH^9{2?l zKE`|e_`Wza*?6|SFBUf*Y6MU|4aYm!R}P;9Dxbl(-c zI8-BXiF%XIQPGQIxxW6+DA)1EK_0)K!q4+7KI0x!8T_#k^3BAb*XKM&_Opv9?*OSz+ct574b0Wsqv+ApcTHXoz zj*sWMnrmpXd*w!)ANqA%;eOM)PA`u6`8RPWUZ)@P%<7-zy%y$b%yt54W#BH)vovr` zBfFbEHk`wrwNiw(NP1d6Zjr1S9HI}hJ4%b{J;wv$#<&ZV)S*~#FtZB%cRU!u5`$)`y zNGbJjJ>b}2Y)qCi_X!?D)-wa{5#6%>jtn~@)9_T~zyrg)=Kikjs>?&_tFm2uW^LJc z6ydVEwwbrjyfMD85+50MJ?GGAOV@js=O^biz9aSk+dQ)^Du!n(&c5}18WVS4_O1Sb pJ63OYXHpxH*DGAMLN}~#uc~|tfcy3aWgU)~H6${;qxtr)_CJ*wf13aR literal 0 HcmV?d00001 diff --git a/src/Plugin.h b/src/Plugin.h new file mode 100644 index 0000000..2883be4 --- /dev/null +++ b/src/Plugin.h @@ -0,0 +1,1599 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// OLLYDBG PLUGIN API // +// // +// Version 1.10 // +// // +// Written by Oleh Yuschuk (ollydbg@t-online.de) // +// // +// Internet: http://home.t-online.de/home/Ollydbg // +// // +// This code is distributed "as is", without warranty of any kind, expressed // +// or implied, including, but not limited to warranty of fitness for any // +// particular purpose. In no event will Oleh Yuschuk be liable to you for any // +// special, incidental, indirect, consequential or any other damages caused // +// by the use, misuse, or the inability to use of this code, including any // +// lost profits or lost savings, even if Oleh Yuschuk has been advised of the // +// possibility of such damages. // +// // +//////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////// IMPORTANT INFORMATION ///////////////////////////// + +// 1. Export all callback functions by name, NOT by ordinal! +// 2. Force byte alignment of OllyDbg structures! +// 3. Set default char type to unsigned! +// 4. Read documentation! + +// If you prefere Borland, this will force necessary settings (but, as a side +// effect, may cause plenty of warnings if other include files will be compiled +// with different options): +#ifdef __BORLANDC__ + + #pragma option -a1 // Byte alignment + #pragma option -K // Unsigned char + + // And here I check that settings are correct. Unfortunately, Microsoft C (at + // least C++ 5.0) doesn't allow for sizeof and typecasts in conditionals: + + typedef struct t_aligntest { + char a; + long b; + } t_aligntest; + + #if (sizeof(t_aligntest)!=sizeof(char)+sizeof(long)) + #error Please assure byte alignment of OllyDbg structures + #endif + #undef t_aligntest + + #if ((char)0xFF!=255) + #error Please set default char type to unsigned + #endif + +#endif + +// If you like Microsoft compiler, this will force byte alignment and verify +// that character is set to unsigned. +#ifdef _MSC_VER + + #pragma pack(1) // Force byte alignment of structures + + #ifndef _CHAR_UNSIGNED // Verify that character is unsigned + #error Please set default char type to unsigned (option /J) + #endif + + // Borland adds underscore to export automatically, whereas I don't know any + // such option for Microsoft compiler. This solution is not too elegant but + // works. + #define ODBG_Plugindata _ODBG_Plugindata + #define ODBG_Plugininit _ODBG_Plugininit + #define ODBG_Pluginmainloop _ODBG_Pluginmainloop + #define ODBG_Pluginsaveudd _ODBG_Pluginsaveudd + #define ODBG_Pluginuddrecord _ODBG_Pluginuddrecord + #define ODBG_Pluginmenu _ODBG_Pluginmenu + #define ODBG_Pluginaction _ODBG_Pluginaction + #define ODBG_Pluginshortcut _ODBG_Pluginshortcut + #define ODBG_Pluginreset _ODBG_Pluginreset + #define ODBG_Pluginclose _ODBG_Pluginclose + #define ODBG_Plugindestroy _ODBG_Plugindestroy + #define ODBG_Paused _ODBG_Paused + #define ODBG_Pausedex _ODBG_Pausedex + #define ODBG_Plugincmd _ODBG_Plugincmd + +#endif + + +//////////////////////////////////////////////////////////////////////////////// +///////////////////////////// GENERAL DECLARATIONS ///////////////////////////// + +#define PLUGIN_VERSION 110 // Version of plugin interface + +#ifdef __cplusplus + #define extc extern "C" // Assure that names are not mangled +#else + #define extc extern +#endif + +#define _export __declspec(dllexport) + +typedef unsigned char uchar; // Unsigned character (byte) +typedef unsigned short ushort; // Unsigned short +typedef unsigned int uint; // Unsigned integer +typedef unsigned long ulong; // Unsigned long + +#define TEXTLEN 256 // Maximal length of text string +#define ARGLEN 1024 // Maximal length of argument string +#define USERLEN 4096 // Maximal length of record in .udd file +#define SHORTLEN 8 // Maximal length of short name + +#define BLACK 0 // Indices of colours used by OllyDbg. In +#define BLUE 1 // syntax highlighting, use only colours +#define GREEN 2 // 0 to 15 in the least significant bits +#define CYAN 3 // of the corresponding mask byte. +#define RED 4 +#define MAGENTA 5 +#define BROWN 6 +#define LIGHTGRAY 7 +#define DARKGRAY 8 +#define LIGHTBLUE 9 +#define LIGHTGREEN 10 +#define LIGHTCYAN 11 +#define LIGHTRED 12 +#define LIGHTMAGENTA 13 +#define YELLOW 14 +#define WHITE 15 +#define MINT 16 +#define SKYBLUE 17 +#define IVORY 18 +#define GRAY 19 + +#define NCOLORS 20 // Total number of defined colours + +#define BKTRANSP 0x00 // Background colours in syntax hiliting +#define BKBLACK 0x10 +#define BKGRAY 0x20 +#define BKWHITE 0x30 +#define BKCYAN 0x40 +#define BKGREEN 0x50 +#define BKRED 0x60 +#define BKYELLOW 0x70 + +#define BLACKWHITE 0 // Colour schemes used by OllyDbg +#define BLUEGOLD 1 +#define SKYWIND 2 +#define NIGHTSTARS 3 +#define SCHEME4 4 +#define SCHEME5 5 +#define SCHEME6 6 +#define SCHEME7 7 + +#define FIXEDFONT 0 // Fonts used by OllyDbg. Variable-pitch +#define TERMINAL6 1 // fonts are placed at the end of this +#define FIXEDSYS 2 // table. +#define COURIERFONT 3 +#define LUCIDACONS 4 +#define FONT5 5 +#define FONT6 6 +#define FONT7 7 +#define MAINFONT 8 +#define SYSFONT 9 +#define INFOFONT 10 + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////// INFORMATION FUNCTIONS ///////////////////////////// + +extc void cdecl Addtolist(long addr,int highlight,char *format,...); +extc void cdecl Updatelist(void); +extc HWND cdecl Createlistwindow(void); +extc void cdecl Error(char *format,...); +extc void cdecl Message(ulong addr,char *format,...); +extc void cdecl Infoline(char *format,...); +extc void cdecl Progress(int promille,char *format,...); +extc void cdecl Flash(char *format,...); + +//////////////////////////////////////////////////////////////////////////////// +////////////////////////// DATA FORMATTING FUNCTIONS /////////////////////////// + +// Bits used in Decodeaddress(), Decoderelativeoffset() and Decodethreadname() +// to specify decoding mode: +#define ADC_DEFAULT 0x0000 // Default decoding mode +#define ADC_DIFFMOD 0x0001 // Show module only if different +#define ADC_NOMODNAME 0x0002 // Never show module name +#define ADC_VALID 0x0004 // Only decode if allocated memory +#define ADC_INMODULE 0x0008 // Only decode if in some module +#define ADC_SAMEMOD 0x0010 // Decode only address in same module +#define ADC_SYMBOL 0x0020 // Only decode if symbolic name +#define ADC_JUMP 0x0040 // Check if points to JMP/CALL command +#define ADC_OFFSET 0x0080 // Check if symbol for data +#define ADC_STRING 0x0100 // Check if pointer to ASCII or UNICODE +#define ADC_ENTRY 0x0200 // Check if entry to subroutine +#define ADC_UPPERCASE 0x0400 // First letter in uppercase if possible +#define ADC_WIDEFORM 0x0800 // Extended form of decoded name +#define ADC_NONTRIVIAL 0x1000 // Name + non-zero offset +#define ADC_DYNAMIC 0x2000 // JMP/CALL to dynamically loaded name + +#define PLAINASCII 0x01 // Plain ASCII character +#define DIACRITICAL 0x02 // Diacritical character +#define RAREASCII 0x10 // Rare ASCII character + +extc int cdecl Decodeaddress(ulong addr,ulong base,int addrmode, + char *symb,int nsymb,char *comment); +extc int cdecl Decoderelativeoffset(ulong addr,int addrmode, + char *symb,int nsymb); +extc int cdecl Decodecharacter(char *s,uint c); +extc int cdecl Printfloat4(char *s,float f); +extc int cdecl Printfloat8(char *s,double d); +extc int cdecl Printfloat10(char *s,long double ext); +extc int cdecl Print3dnow(char *s,uchar *f); +extc int cdecl Printsse(char *s,char *f); +extc ulong cdecl Followcall(ulong addr); +extc int cdecl IstextA(char c); +extc int cdecl IstextW(wchar_t w); +extc int cdecl Stringtotext(char *data,int ndata,char *text,int ntext); + +//////////////////////////////////////////////////////////////////////////////// +///////////////////////////// DATA INPUT FUNCTIONS ///////////////////////////// + +#define MAXCMDSIZE 16 // Maximal length of 80x86 command +#define NSEQ 8 // Max length of command sequence +#define NMODELS 8 // Number of assembler search models + +// Note that each of dialog functions decodes only subset of listed flags. +#define DIA_ASKGLOBAL 0x0001 // Display checkbox "Global search" +#define DIA_HEXONLY 0x0002 // Hexadecimal format only +#define DIA_ALIGNED 0x0004 // Display checkbox "Aligned search" +#define DIA_DEFHEX 0x0000 // On startup, cursor in hex control +#define DIA_DEFASCII 0x0010 // On startup, cursor in ASCII control +#define DIA_DEFUNICODE 0x0020 // On startup, cursor in UNICODE control +#define DIA_SEARCH 0x0040 // Is a search dialog +#define DIA_HISTORY 0x0100 // Allows previous hex strings + +// Status of animation or trace. +#define ANIMATE_OFF 0 // No animation +#define ANIMATE_IN 1 // Animate into +#define ANIMATE_OVER 2 // Animate over +#define ANIMATE_RET 3 // Execute till RET +#define ANIMATE_SKPRET 4 // Skip RET instruction +#define ANIMATE_USER 5 // Execute till user code +#define ANIMATE_TRIN 6 // Run trace in +#define ANIMATE_TROVER 7 // Run trace over +#define ANIMATE_STOP 8 // Gracefully stop animation + +typedef struct t_hexstr { // String used for hex/text search + int n; // String length + uchar data[TEXTLEN]; // Data + uchar mask[TEXTLEN]; // Mask, 0 bits are masked +} t_hexstr; + +typedef struct t_asmmodel { // Model to search for assembler command + uchar code[MAXCMDSIZE]; // Binary code + uchar mask[MAXCMDSIZE]; // Mask for binary code (0: bit ignored) + int length; // Length of code, bytes (0: empty) + int jmpsize; // Offset size if relative jump + int jmpoffset; // Offset relative to IP + int jmppos; // Position of jump offset in command +} t_asmmodel; + +typedef struct t_extmodel { // Model for extended command search + char code[MAXCMDSIZE]; // Binary code + char mask[MAXCMDSIZE]; // Mask for binary code (0: bit ignored) + int length; // Length of code, bytes (0: empty) + int jmpsize; // Offset size if relative jump + int jmpoffset; // Offset relative to IP + int jmppos; // Position of jump offset in command + int isany; // Count for ANY's argument + int cmdoffset; // Offset of command in the source + char ramask[MAXCMDSIZE]; // Mask for pseudoregister RA + char rbmask[MAXCMDSIZE]; // Mask for pseudoregister RB +} t_extmodel; + +extc int cdecl Getlong(char *title,ulong *data,int datasize, + char letter,int mode); +extc int cdecl Getlongxy(char *title,ulong *data,int datasize, + char letter,int mode,int x,int y); +extc int cdecl Getregxy(char *title,ulong *data,char letter,int x,int y); +extc int cdecl Getline(char *title,ulong *data); +extc int cdecl Getlinexy(char *title,ulong *data,int x,int y); +extc int cdecl Getfloat10(char *title,long double *fdata, + uchar *tag,char letter,int mode); +extc int cdecl Getfloat10xy(char *title,long double *fdata, + char *tag,char letter,int mode,int x,int y); +extc int cdecl Getfloat(char *title,void *fdata,int size, + char letter,int mode); +extc int cdecl Getfloatxy(char *title,void *fdata,int size, + char letter,int mode,int x,int y); +extc void cdecl Getasmfindmodel(t_asmmodel model[NMODELS], + char letter,int searchall); +extc void cdecl Getasmfindmodelxy(t_asmmodel model[NMODELS], + char letter,int searchall,int x,int y); +extc int cdecl Gettext(char *title,char *text, + char letter,int type,int fontindex); +extc int cdecl Gettextxy(char *title,char *text,char letter, + int type,int fontindex,int x,int y); +extc int cdecl Gethexstring(char *title,t_hexstr *hs, + int mode,int fontindex,char letter); +extc int cdecl Gethexstringxy(char *title,t_hexstr *hs,int mode, + int fontindex,char letter,int x,int y); +extc int cdecl Getmmx(char *title,uchar *data,int mode); +extc int cdecl Getmmxxy(char *title,char *data,int mode,int x,int y); +extc int cdecl Get3dnow(char *title,uchar *data,int mode); +extc int cdecl Get3dnowxy(char *title,char *data,int mode,int x,int y); +extc int cdecl Browsefilename(char *title,char *name,char *defext, + int getarguments); +extc int cdecl OpenEXEfile(char *path,int dropped); +extc int cdecl Attachtoactiveprocess(int newprocessid); +extc void cdecl Animate(int animation); + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////// SORTED DATA FUNCTIONS ///////////////////////////// + +#define NBAR 17 // Max allowed number of segments in bar + +#define BAR_PRESSED 0x01 // Bar segment pressed, used internally +#define BAR_DISABLED 0x02 // Bar segment disabled +#define BAR_NOSORT 0x04 // Flat bar column, supports no sorting +#define BAR_NORESIZE 0x08 // Bar column cannot be resized +#define BAR_BUTTON 0x10 // Segment sends WM_USER_BAR +#define BAR_SHIFTSEL 0x20 // Selection shifted 1/2 char to left + +#define CAPT_FREE 0 // Bar and data are not captured + +#define TABLE_DIR 0x0001 // Bottom-to-top table +#define TABLE_COPYMENU 0x0002 // Attach copy item +#define TABLE_SORTMENU 0x0004 // Attach sort menu +#define TABLE_APPMENU 0x0010 // Attach appearance menu +#define TABLE_WIDECOL 0x0020 // Attach wide columns menu item +#define TABLE_USERAPP 0x0040 // Attach user-processed appearance menu +#define TABLE_USERDEF 0x0080 // User-drawn table +#define TABLE_NOHSCR 0x0100 // Table contains no horizontal scroll +#define TABLE_SAVEPOS 0x0200 // Save position & appearance to .ini +#define TABLE_CPU 0x0400 // Table belongs to CPU window +#define TABLE_FASTSEL 0x0800 // Update when selection changes +#define TABLE_COLSEL 0x1000 // Column-wide selection +#define TABLE_SAVEAPP 0x2000 // Save multiinstance appearance to .ini +#define TABLE_HILMENU 0x4000 // Attach Syntax highlighting menu +#define TABLE_ONTOP 0x8000 // Attach Always on top menu + +#define DRAW_NORMAL 0x0000 // Normal plain text +#define DRAW_GRAY 0x0001 // Grayed text +#define DRAW_HILITE 0x0002 // Highlighted text +#define DRAW_UL 0x0004 // Underlined text +#define DRAW_SELECT 0x0008 // Selected background +#define DRAW_EIP 0x0010 // Inverted normal text/background +#define DRAW_BREAK 0x0020 // Breakpoint background +#define DRAW_GRAPH 0x0040 // Graphical element +#define DRAW_DIRECT 0x0080 // Direct text colour index (mask only) +#define DRAW_MASK 0x0080 // Use mask to set individual colors +#define DRAW_EXTSEL 0x0100 // Extend mask till end of column +#define DRAW_UNICODE 0x0200 // Text in UNICODE +#define DRAW_TOP 0x0400 // Draw upper half of text +#define DRAW_BOTTOM 0x0800 // Draw lower half of text + +// Symbolic names for graphical characters. Any other character is displayed +// as space. Use only characters in range [1..0x7F]! +#define D_SPACE 'N' // Space +#define D_SEP ' ' // Thin separating line +#define D_BEGIN 'B' // Begin of procedure or scope +#define D_BODY 'I' // Body of procedure or scope +#define D_ENTRY 'J' // Loop entry point +#define D_LEAF 'K' // Intermediate leaf on a tree +#define D_END 'E' // End of procedure or scope +#define D_SINGLE 'S' // Single-line scope +#define D_ENDBEG 'T' // End and begin of stack scope +#define D_POINT '.' // Point +#define D_JMPUP 'U' // Jump upstairs +#define D_JMPOUT '<' // Jump to same location or out of module +#define D_JMPDN 'D' // Jump downstairs +#define D_PATHUP 'u' // Jump path upstairs (highlighted) +#define D_GRAYUP 'v' // Jump path upstairs (grayed) +#define D_PATH 'i' // Jump path through text (highlighted) +#define D_GRAYPATH 'j' // Jump path through text (grayed) +#define D_PATHDN 'd' // Jump path downstairs (highlighted) +#define D_GRAYDN 'e' // Jump path downstairs (grayed) +#define D_PATHUPEND 'r' // End of path upstairs (highlighted) +#define D_GRAYUPEND 's' // End of path upstairs (grayed) +#define D_PATHDNEND 'f' // End of path downstairs (highlighted) +#define D_GRAYDNEND 'g' // End of path downstairs (grayed) +#define D_SWTOP 't' // Start of switch +#define D_SWBODY 'b' // Switch body +#define D_CASE 'c' // Intermediate switch case +#define D_LASTCASE 'l' // Last switch case + +// Please note: Although types here contain mostly unique bit assignments, it's +// not really necessary. Same bits, except for reserved general types, can be +// freely shared between different types of sorted data. +// General item types: +#define TY_NEW 0x00000001 // Item is new +#define TY_CONFIRMED 0x00000002 // Item still exists +#define TY_MAIN 0x00000004 // Main item (thread or module) +#define TY_INVALID 0x00000008 // Invalid type (item does not exist) +#define TY_SELECTED 0x80000000 // Reserved for multiple selection +// Module-specific types: +#define TY_REPORTED 0x00000010 // Stop on module was reported +// Reference-specific types: +#define TY_REFERENCE 0x00000020 // Item is a real reference +#define TY_ORIGIN 0x00000040 // Item is a search origin +// Breakpoint-specific types: +#define TY_STOPAN 0x00000080 // Stop animation if TY_ONESHOT +#define TY_SET 0x00000100 // Code INT3 is in memory +#define TY_ACTIVE 0x00000200 // Permanent breakpoint +#define TY_DISABLED 0x00000400 // Permanent disabled breakpoint +#define TY_ONESHOT 0x00000800 // Temporary stop +#define TY_TEMP 0x00001000 // Temporary breakpoint +#define TY_KEEPCODE 0x00002000 // Set and keep command code +#define TY_KEEPCOND 0x00004000 // Keep condition unchanged (0: remove) +#define TY_NOUPDATE 0x00008000 // Don't redraw breakpoint window +#define TY_RTRACE 0x00010000 // Pseudotype of run trace breakpoint +// Namelist-specific types: +#define TY_EXPORT 0x00010000 // Exported name +#define TY_IMPORT 0x00020000 // Imported name +#define TY_LIBRARY 0x00040000 // Name extracted from object file +#define TY_LABEL 0x00080000 // User-defined name +#define TY_ANYNAME 0x000F0000 // Any of the namelist flags above +#define TY_KNOWN 0x00100000 // Name of known function +// Memory-specific types: +#define TY_DEFHEAP 0x00020000 // Contains default heap +#define TY_HEAP 0x00040000 // Contains non-default heap +#define TY_SFX 0x00080000 // Contains self-extractor +#define TY_CODE 0x00100000 // Contains image of code section +#define TY_DATA 0x00200000 // Contains image of data section +#define TY_IMPDATA 0x00400000 // Memory block includes import data +#define TY_EXPDATA 0x00800000 // Memory block includes export data +#define TY_RSRC 0x01000000 // Memory block includes resources +#define TY_RELOC 0x02000000 // Memory block includes relocation data +#define TY_STACK 0x04000000 // Contains stack of some thread +#define TY_THREAD 0x08000000 // Contains data block of some thread +#define TY_HEADER 0x10000000 // COFF header +#define TY_ANYMEM 0x1FFE0000 // Any of the memory flags above +#define TY_GUARDED 0x20000000 // NT only: guarded memory block +// Procedure data-specific types: +#define TY_PURE 0x00004000 // No side effects except in stack +#define TY_PASCAL 0x00010000 // Procedure ends with RET nnn +#define TY_C 0x00020000 // ADD ESP,nnn after call to procedure +#define TY_NOTENTRY 0x00100000 // Not necessarily entry point +// Switch data-specific types. +#define TY_CHARSW 0x00100000 // ASCII switch +#define TY_WMSW 0x00200000 // Window message switch +#define TY_EXCEPTSW 0x00400000 // Exception switch +// Stack walk data-specific types. +#define TY_RELIABLE 0x01000000 // Reliable call +#define TY_GUESSED 0x02000000 // Not a real entry, just guessed +#define TY_BELONGS 0x04000000 // Not a real entry, just belongs to proc +// Call tree-specific types. +#define TY_RECURSIVE 0x00000100 // Routine calls self +#define TY_TERMINAL 0x00000200 // Leaf function, doesn't call others +#define TY_SYSTEM 0x00000400 // Function resides in system DLL +#define TY_DIRECT 0x00000800 // Called directly +#define TY_NODATA 0x00001000 // Not analyzed or outside procedure +#define TY_DUMMY 0x00002000 // Consists of single RET command +#define TY_NOSIDE 0x00004000 // No side effects except in stack + +typedef struct t_scheme { // Color scheme + char *name; // Name of the scheme + int textcolor; // Colour used to draw text + int hitextcolor; // Colour used to draw highlited text + int lowcolor; // Colour used to draw auxiliary text + int bkcolor; // Colour used to draw backgrounds + int selbkcolor; // Colour used for selecting background + int linecolor; // Colour used for separating lines + int auxcolor; // Colour used for auxiliary objects + int condbkcolor; // Colour used for background of cond brk +} t_scheme; + +typedef struct t_schemeopt { // Color scheme, alternative form + char *name; // Name of the scheme + int color[8]; // Colours used as in t_scheme +} t_schemeopt; + +typedef struct t_bar { + int nbar; // Number of active columns + int font; // Font used for bar segments + int dx[NBAR]; // Actual widths of columns, pixels + int defdx[NBAR]; // Default widths of columns, chars + char *name[NBAR]; // Column names (may be NULL) + uchar mode[NBAR]; // Combination of BAR_xxx bits + int captured; // One of CAPT_xxx, set to CAPT_FREE + int active; // Info about how mouse is captured + int prevx; // Previous mouse coordinate +} t_bar; + +typedef struct t_sortheader { // Header of sorted data field + ulong addr; // Base address of the entry + ulong size; // Size address of the entry + ulong type; // Entry type, TY_xxx +} t_sortheader; + +typedef int SORTFUNC(const t_sortheader *,const t_sortheader *,const int); +typedef int DRAWFUNC(char *,char *,int *,t_sortheader *,int); +typedef void DESTFUNC(t_sortheader *); + +#define AUTOARRANGE ((SORTFUNC *)1) // Autoarrangeable sorted data + +typedef struct t_sorted { // Descriptor of sorted table + char name[MAX_PATH]; // Name of table, as appears in error + int n; // Actual number of entries + int nmax; // Maximal number of entries + int selected; // Index of selected entry or -1 + ulong seladdr; // Base address of selected entry + int itemsize; // Size of single entry + ulong version; // Unique version of table + void *data; // Entries, sorted by address + SORTFUNC *sortfunc; // Function which sorts data or NULL + DESTFUNC *destfunc; // Destructor function or NULL + int sort; // Sorting criterium (column) + int sorted; // Whether indexes are sorted + int *index; // Indexes, sorted by criterium + int suppresserr; // Suppress multiple overflow errors +} t_sorted; + +typedef struct t_table { // Window with sorted data and bar + HWND hw; // Handle of window or NULL + t_sorted data; // Sorted data + t_bar bar; // Description of bar + int showbar; // Bar: 1-displayed, 0-hidden, -1-absent + short hscroll; // Horiz. scroll: 1-displayed, 0-hidden + short colsel; // Column in TABLE_COLSEL window + int mode; // Combination of bits TABLE_xxx + int font; // Font used by window + short scheme; // Colour scheme used by window + short hilite; // Syntax highlighting used by window + int offset; // First displayed row + int xshift; // Shift in X direction, pixels + DRAWFUNC *drawfunc; // Function which decodes table fields +} t_table; + +extc int cdecl Createsorteddata(t_sorted *sd,char *name,int itemsize, + int nmax,SORTFUNC *sortfunc,DESTFUNC *destfunc); +extc void cdecl Destroysorteddata(t_sorted *sd); +extc void cdecl *Addsorteddata(t_sorted *sd,void *item); +extc void cdecl Deletesorteddata(t_sorted *sd,ulong addr); +extc void cdecl Deletesorteddatarange(t_sorted *sd,ulong addr0,ulong addr1); +extc int cdecl Deletenonconfirmedsorteddata(t_sorted *sd); +extc void* cdecl Findsorteddata(t_sorted *sd,ulong addr); +extc void* cdecl Findsorteddatarange(t_sorted *sd,ulong addr0,ulong addr1); +extc int cdecl Findsorteddataindex(t_sorted *sd,ulong addr0,ulong addr1); +extc int cdecl Sortsorteddata(t_sorted *sd,int sort); +extc void* cdecl Getsortedbyselection(t_sorted *sd,int index); +extc void cdecl Defaultbar(t_bar *pb); +extc int cdecl Tablefunction(t_table *pt, + HWND hw,UINT msg,WPARAM wp,LPARAM lp); +extc void cdecl Painttable(HWND hw,t_table *pt,DRAWFUNC getline); +extc int cdecl Gettableselectionxy(t_table *pt,int column,int *px,int *py); +extc void cdecl Selectandscroll(t_table *pt,int index,int mode); + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// NAME FUNCTIONS //////////////////////////////// + +// Types of names used in name functions. Note that higher-priority types have +// smaller identifiers! +#define NM_NONAME 0x00 // Undefined name +#define NM_ANYNAME 0xFF // Name of any type +// Names saved in the data file of module they appear. +#define NM_PLUGCMD 0x30 // Plugin commands to execute at break +#define NM_LABEL 0x31 // User-defined label +#define NM_EXPORT 0x32 // Exported (global) name +#define NM_IMPORT 0x33 // Imported name +#define NM_LIBRARY 0x34 // Name from library or object file +#define NM_CONST 0x35 // User-defined constant +#define NM_COMMENT 0x36 // User-defined comment +#define NM_LIBCOMM 0x37 // Comment from library or object file +#define NM_BREAK 0x38 // Condition related with breakpoint +#define NM_ARG 0x39 // Arguments decoded by analyzer +#define NM_ANALYSE 0x3A // Comment added by analyzer +#define NM_BREAKEXPR 0x3B // Expression related with breakpoint +#define NM_BREAKEXPL 0x3C // Explanation related with breakpoint +#define NM_ASSUME 0x3D // Assume function with known arguments +#define NM_STRUCT 0x3E // Code structure decoded by analyzer +#define NM_CASE 0x3F // Case description decoded by analyzer +// Names saved in the data file of main module. +#define NM_INSPECT 0x40 // Several last inspect expressions +#define NM_WATCH 0x41 // Watch expressions +#define NM_ASM 0x42 // Several last assembled strings +#define NM_FINDASM 0x43 // Several last find assembler strings +#define NM_LASTWATCH 0x48 // Several last watch expressions +#define NM_SOURCE 0x49 // Several last source search strings +#define NM_REFTXT 0x4A // Several last ref text search strings +#define NM_GOTO 0x4B // Several last expressions to follow +#define NM_GOTODUMP 0x4C // Several expressions to follow in Dump +#define NM_TRPAUSE 0x4D // Several expressions to pause trace +// Pseudonames. +#define NM_IMCALL 0xFE // Intermodular call + +#define NMHISTORY 0x40 // Converts NM_xxx to type of init list + +extc int cdecl Insertname(ulong addr,int type,char *name); +extc int cdecl Quickinsertname(ulong addr,int type,char *name); +extc void cdecl Mergequicknames(void); +extc void cdecl Discardquicknames(void); +extc int cdecl Findname(ulong addr,int type,char *name); +extc int cdecl Decodename(ulong addr,int type,char *name); +extc ulong cdecl Findnextname(char *name); +extc int cdecl Findlabel(ulong addr,char *name); +extc void cdecl Deletenamerange(ulong addr0,ulong addr1,int type); +extc int cdecl Findlabelbyname(char *name,ulong *addr, + ulong addr0,ulong addr1); +extc ulong cdecl Findimportbyname(char *name,ulong addr0,ulong addr1); +extc int cdecl Demanglename(char *name,int type,char *undecorated); +extc int cdecl Findsymbolicname(ulong addr,char *fname); + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////// DISASSEMBLY FUNCTIONS ///////////////////////////// + +#define REG_EAX 0 // Indexes of general-purpose registers +#define REG_ECX 1 // in t_reg. +#define REG_EDX 2 +#define REG_EBX 3 +#define REG_ESP 4 +#define REG_EBP 5 +#define REG_ESI 6 +#define REG_EDI 7 + +#define SEG_UNDEF -1 +#define SEG_ES 0 // Indexes of segment/selector registers +#define SEG_CS 1 // in t_reg. +#define SEG_SS 2 +#define SEG_DS 3 +#define SEG_FS 4 +#define SEG_GS 5 + +// Selected items in register window. +#define RS_NONE 0x0000 // No selection +#define RS_INT 0x0010 // General-purpose 32-bit registers +#define RS_EIP 0x0020 // EIP (instruction pointer) +#define RS_FLG 0x0030 // 1-bit decoded flags +#define RS_SEG 0x0040 // Segment (selector) registers +#define RS_EFL 0x0050 // 32-bit flag register +#define RS_TAG 0x0060 // FPU register tag +#define RS_FPU 0x0070 // 80-bit FPU registers +#define RS_FST 0x0080 // FPU status +#define RS_FCO 0x0090 // FPU condition bits +#define RS_FER 0x00A0 // FPU error bits +#define RS_FCW 0x00B0 // FPU control word +#define RS_FPR 0x00C0 // FPU precision fields +#define RS_FEM 0x00D0 // FPU error mask bits +#define RS_MMX 0x00E0 // MMX registers +#define RS_3DN 0x00F0 // 3DNow! registers +#define RS_SSE 0x0100 // SSE registers +#define RS_CSR 0x0110 // SSE MXCSR register +#define RS_CSB 0x0120 // SSE MXCSR bits +#define RS_CPR 0x0130 // SSE rounding control +#define RS_ERR 0x0140 // Last thread error + +#define RS_GROUP 0x01F0 // Mask to extract group of registers +#define RS_INDEX 0x000F // Mask to extract index of register + +#define NREGSTACK 32 // Length of stack trace buffer +#define MAXCALSIZE 8 // Max length of CALL without prefixes + +#define INT3 0xCC // Code of 1-byte breakpoint +#define NOP 0x90 // Code of 1-byte NOP command +#define TRAPFLAG 0x00000100 // Trap flag in CPU flag register + +#define C_TYPEMASK 0xF0 // Mask for command type +#define C_CMD 0x00 // Ordinary instruction +#define C_PSH 0x10 // PUSH instruction +#define C_POP 0x20 // POP instruction +#define C_MMX 0x30 // MMX instruction +#define C_FLT 0x40 // FPU instruction +#define C_JMP 0x50 // JUMP instruction +#define C_JMC 0x60 // Conditional JUMP instruction +#define C_CAL 0x70 // CALL instruction +#define C_RET 0x80 // RET instruction +#define C_FLG 0x90 // Changes system flags +#define C_RTF 0xA0 // C_JMP and C_FLG simultaneously +#define C_REP 0xB0 // Instruction with REPxx prefix +#define C_PRI 0xC0 // Privileged instruction +#define C_SSE 0xD0 // SSE instruction +#define C_NOW 0xE0 // 3DNow! instruction +#define C_BAD 0xF0 // Unrecognized command +#define C_RARE 0x08 // Rare command, seldom used in programs +#define C_SIZEMASK 0x07 // MMX data size or special flag +#define C_EXPL 0x01 // (non-MMX) Specify explicit memory size + +#define C_DANGER95 0x01 // Command is dangerous under Win95/98 +#define C_DANGER 0x03 // Command is dangerous everywhere +#define C_DANGERLOCK 0x07 // Dangerous with LOCK prefix + +#define DEC_TYPEMASK 0x1F // Type of memory byte +#define DEC_UNKNOWN 0x00 // Unknown type +#define DEC_BYTE 0x01 // Accessed as byte +#define DEC_WORD 0x02 // Accessed as short +#define DEC_NEXTDATA 0x03 // Subsequent byte of data +#define DEC_DWORD 0x04 // Accessed as long +#define DEC_FLOAT4 0x05 // Accessed as float +#define DEC_FWORD 0x06 // Accessed as descriptor/long pointer +#define DEC_FLOAT8 0x07 // Accessed as double +#define DEC_QWORD 0x08 // Accessed as 8-byte integer +#define DEC_FLOAT10 0x09 // Accessed as long double +#define DEC_TBYTE 0x0A // Accessed as 10-byte integer +#define DEC_STRING 0x0B // Zero-terminated ASCII string +#define DEC_UNICODE 0x0C // Zero-terminated UNICODE string +#define DEC_3DNOW 0x0D // Accessed as 3Dnow operand +#define DEC_SSE 0x0E // Accessed as SSE operand +#define DEC_TEXT 0x10 // For use in t_result only +#define DEC_BYTESW 0x11 // Accessed as byte index to switch +#define DEC_NEXTCODE 0x13 // Subsequent byte of command +#define DEC_COMMAND 0x1D // First byte of command +#define DEC_JMPDEST 0x1E // Jump destination +#define DEC_CALLDEST 0x1F // Call (and maybe jump) destination +#define DEC_PROCMASK 0x60 // Procedure analysis +#define DEC_PROC 0x20 // Start of procedure +#define DEC_PBODY 0x40 // Body of procedure +#define DEC_PEND 0x60 // End of procedure +#define DEC_CHECKED 0x80 // Byte was analysed +#define DEC_SIGNED 0x100 // For use in t_result only + +#define DISASM_SIZE 0 // Determine command size only +#define DISASM_DATA 1 // Determine size and analysis data +#define DISASM_TRACE 2 // Trace integer registers +#define DISASM_FILE 3 // Disassembly, no symbols/registers +#define DISASM_CODE 4 // Disassembly, registers undefined +#define DISASM_ALL 5 // Complete disassembly +#define DISASM_RTRACE 6 // Disassemble with run-trace registers + +#define DISASM_MODE 0x0000000F // Mask to extract disassembling mode +#define DISASM_HILITE 0x000F0000 // Mask to extract highlighting mode +#define DISASM_HLSHIFT 16 // Shift to extract highlighting mode + +// Warnings issued by Disasm(): +#define DAW_FARADDR 0x0001 // Command is a far jump, call or return +#define DAW_SEGMENT 0x0002 // Command loads segment register +#define DAW_PRIV 0x0004 // Privileged command +#define DAW_IO 0x0008 // I/O command +#define DAW_SHIFT 0x0010 // Shift constant out of range 1..31 +#define DAW_PREFIX 0x0020 // Superfluous prefix +#define DAW_LOCK 0x0040 // Command has LOCK prefix +#define DAW_STACK 0x0080 // Unaligned stack operation +#define DAW_DANGER95 0x1000 // May mess up Win95/98 if executed +#define DAW_DANGEROUS 0x3000 // May mess up any OS if executed + +#define RST_INVALID 0 // Register undefined +#define RST_VALUE 1 // Register contains regdata +#define RST_VFIXUP 2 // Reg contains regdata that is fixup +#define RST_INDIRECT 3 // Register contains [regdata] + +#define NREGSTACK 32 // Length of stack trace buffer + +typedef struct t_reg { // Excerpt from context + int modified; // Some regs modified, update context + int modifiedbyuser; // Among modified, some modified by user + int singlestep; // Type of single step, SS_xxx + ulong r[8]; // EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI + ulong ip; // Instruction pointer (EIP) + ulong flags; // Flags + int top; // Index of top-of-stack + long double f[8]; // Float registers, f[top] - top of stack + char tag[8]; // Float tags (0x3 - empty register) + ulong fst; // FPU status word + ulong fcw; // FPU control word + ulong s[6]; // Segment registers ES,CS,SS,DS,FS,GS + ulong base[6]; // Segment bases + ulong limit[6]; // Segment limits + char big[6]; // Default size (0-16, 1-32 bit) + ulong dr6; // Debug register DR6 + ulong threadid; // ID of thread that owns registers + ulong lasterror; // Last thread error or 0xFFFFFFFF + int ssevalid; // Whether SSE registers valid + int ssemodified; // Whether SSE registers modified + char ssereg[8][16]; // SSE registers + ulong mxcsr; // SSE control and status register + int selected; // Reports selected register to plugin + ulong drlin[4]; // Debug registers DR0..DR3 + ulong dr7; // Debug register DR7 +} t_reg; + +typedef struct t_operand { // Full decription of command's operand + char optype; // DEC_xxx (mem) or DECR_xxx (reg,const) + char opsize; // Size of operand + char regscale[8]; // Scales of registers + char seg; // Segment register + ulong opconst; // Constant +} t_operand; + +typedef struct t_disasm { // Results of disassembling + ulong ip; // Instrucion pointer + char dump[TEXTLEN]; // Hexadecimal dump of the command + char result[TEXTLEN]; // Disassembled command + char comment[TEXTLEN]; // Brief comment + char opinfo[3][TEXTLEN]; // Comments to command's operands + int cmdtype; // One of C_xxx + int memtype; // Type of addressed variable in memory + int nprefix; // Number of prefixes + int indexed; // Address contains register(s) + ulong jmpconst; // Constant jump address + ulong jmptable; // Possible address of switch table + ulong adrconst; // Constant part of address + ulong immconst; // Immediate constant + int zeroconst; // Whether contains zero constant + int fixupoffset; // Possible offset of 32-bit fixups + int fixupsize; // Possible total size of fixups or 0 + ulong jmpaddr; // Destination of jump/call/return + int condition; // 0xFF:unconditional, 0:false, 1:true + int error; // Error while disassembling command + int warnings; // Combination of DAW_xxx + int optype[3]; // Type of operand (extended set DEC_xxx) + int opsize[3]; // Size of operand, bytes + int opgood[3]; // Whether address and data valid + ulong opaddr[3]; // Address if memory, index if register + ulong opdata[3]; // Actual value (only integer operands) + t_operand op[3]; // Full description of operand + ulong regdata[8]; // Registers after command is executed + int regstatus[8]; // Status of registers, one of RST_xxx + ulong addrdata; // Traced memory address + int addrstatus; // Status of addrdata, one of RST_xxx + ulong regstack[NREGSTACK]; // Stack tracing buffer + int rststatus[NREGSTACK]; // Status of stack items + int nregstack; // Number of items in stack trace buffer + ulong reserved[29]; // Reserved for plugin compatibility +} t_disasm; + +extc ulong cdecl Disasm(uchar *src,ulong srcsize,ulong srcip,uchar *srcdec, + t_disasm *disasm,int disasmmode,ulong threadid); +extc ulong cdecl Disassembleback(uchar *block,ulong base,ulong size, + ulong ip,int n,int usedec); +extc ulong cdecl Disassembleforward(uchar *block,ulong base,ulong size, + ulong ip,int n,int usedec); +extc int cdecl Issuspicious(char *cmd,ulong size,ulong ip, + ulong threadid,t_reg *preg,char *s); +extc int cdecl Isfilling(ulong offset,char *data,ulong size,ulong align); + +//////////////////////////////////////////////////////////////////////////////// +////////////////////////////// ASSEMBLY FUNCTIONS ////////////////////////////// + +extc int cdecl Assemble(char *cmd,ulong ip,t_asmmodel *model,int attempt, + int constsize,char *errtext); +extc int cdecl Checkcondition(int code,ulong flags); + +//////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// EXPRESSIONS ////////////////////////////////// + +typedef struct t_result { // Result of expression's evaluation + int type; // Type of expression, DEC(R)_xxx + int dtype; // Type of data, DEC_xxx + union { + uchar data[10]; // Binary form of expression's value + ulong u; // Value as unsigned integer + long l; // Value as signed integer + long double f; }; // Value as 80-bit float + union { + char value[TEXTLEN]; // ASCII form of expression's value + wchar_t wvalue[TEXTLEN/2]; }; // UNICODE form of expression's value + ulong lvaddr; // Address of lvalue or NULL +} t_result; + +extc int cdecl Expression(t_result *result,char *expression,int a,int b, + uchar *data,ulong database,ulong datasize,ulong threadid); + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// THREAD FUNCTIONS /////////////////////////////// + +typedef struct t_thread { // Information about active threads + ulong threadid; // Thread identifier + ulong dummy; // Always 1 + ulong type; // Service information, TY_xxx + HANDLE thread; // Thread handle + ulong datablock; // Per-thread data block + ulong entry; // Thread entry point + ulong stacktop; // Working variable of Listmemory() + ulong stackbottom; // Working variable of Listmemory() + CONTEXT context; // Actual context of the thread + t_reg reg; // Actual contents of registers + int regvalid; // Whether reg is valid + t_reg oldreg; // Previous contents of registers + int oldregvalid; // Whether oldreg is valid + int suspendcount; // Suspension count (may be negative) + long usertime; // Time in user mode, 1/10th ms, or -1 + long systime; // Time in system mode, 1/10th ms, or -1 + ulong reserved[16]; // Reserved for future compatibility +} t_thread; + +extc HWND cdecl Createthreadwindow(void); +extc t_thread* cdecl Findthread(ulong threadid); +extc int cdecl Decodethreadname(char *s,ulong threadid,int mode); +extc ulong cdecl Getcputhreadid(void); +extc ulong cdecl Runsinglethread(ulong threadid); +extc void cdecl Restoreallthreads(void); + + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// MEMORY FUNCTIONS /////////////////////////////// + +// Mode bits recognized by Readmemory() and Writememory(). +#define MM_RESTORE 0x01 // Restore or remove INT3 breakpoints +#define MM_SILENT 0x02 // Don't display error message +#define MM_DELANAL 0x04 // Delete analysis from the memory + +#define MM_RESILENT (MM_RESTORE|MM_SILENT) + +typedef struct t_memory { // Memory block descriptor + ulong base; // Base address of memory block + ulong size; // Size of block + ulong type; // Service information, TY_xxx + ulong owner; // Address of owner of the memory + ulong initaccess; // Initial read/write access + ulong access; // Actual status and read/write access + ulong threadid; // Block belongs to this thread or 0 + char sect[SHORTLEN]; // Name of module section + uchar *copy; // Copy used in CPU window or NULL + ulong reserved[8]; // Reserved for plugin compatibility +} t_memory; + +typedef struct t_heap { // Heap block descriptor + ulong base; // Base address of heap block + ulong size; // Size of heap block + ulong type; // Service information, TY_xxx + ulong parent; // Handle of heap descriptor block +} t_heap; + +extc int cdecl Listmemory(void); +extc t_memory* cdecl Findmemory(ulong addr); +extc int cdecl Guardmemory(ulong base,ulong size,int guard); +extc void cdecl Havecopyofmemory(uchar *copy,ulong base,ulong size); +extc ulong cdecl Readmemory(void *buf,ulong addr,ulong size,int mode); +extc ulong cdecl Writememory(void *buf,ulong addr,ulong size,int mode); +extc ulong cdecl Readcommand(ulong ip,char *cmd); + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// MODULE FUNCTIONS /////////////////////////////// + +#define NVERS 32 // Max allowed length of file version + +// Types of recognized jumps. +#define JT_JUMP 0 // Unconditional jump +#define JT_COND 1 // Conditional jump +#define JT_SWITCH 2 // Jump via switch table + +typedef struct t_ahint { // User-supplied hint for analysis + ulong addr; // Hint address + ulong size; // Hint size + ulong type; // Hint type, bits from DEC_TYPEMASK +} t_ahint; + +typedef struct t_stringtable { // Pointers to string resources + ulong name; // Name of block of strings + ulong language; // Language identifier + ulong addr; // Address of block in memory + ulong size; // Size of block in memory +} t_stringtable; + +typedef struct t_fixup { + ulong base; // Address of fixup + ulong size; // Size of fixup (usually 2 or 4 bytes) +} t_fixup; + +typedef struct t_symvar { // Symbolic variable from debug data + int next; // Index of next variable in chain or -1 + ushort kind; // Kind of variable + union { + ulong type; // Type of variable + ulong regs; }; // Registers in optvar + union { + ulong addr; // Address or description of registers + long offset; }; // Offset for EBP-relative data + ulong size; // Size of variable or optvar data + int optvar; // Index of optvar chain or -1 + ulong nameaddr; // NM_DEBUG address of var's name +} t_symvar; + +typedef struct t_jdest { // Element of jump data + char type; // Type of jump, one of JT_xxx + ulong from; // Jump source + ulong to; // Jump destination +} t_jdest; + +typedef struct t_module { // Executable module descriptor + ulong base; // Base address of module + ulong size; // Size occupied by module + ulong type; // Service information, TY_xxx + ulong codebase; // Base address of module code block + ulong codesize; // Size of module code block + ulong resbase; // Base address of resources + ulong ressize; // Size of resources + t_stringtable *stringtable; // Pointers to string resources or NULL + int nstringtable; // Actual number of used stringtable + int maxstringtable; // Actual number of allocated stringtable + ulong entry; // Address of or NULL + ulong database; // Base address of module data block + ulong idatatable; // Base address of import data table + ulong idatabase; // Base address of import data block + ulong edatatable; // Base address of export data table + ulong edatasize; // Size of export data table + ulong reloctable; // Base address of relocation table + ulong relocsize; // Size of relocation table + char name[SHORTLEN]; // Short name of the module + char path[MAX_PATH]; // Full name of the module + int nsect; // Number of sections in the module + IMAGE_SECTION_HEADER *sect; // Copy of section headers from file + ulong headersize; // Total size of headers in executable + ulong fixupbase; // Base of image in executable file + int nfixup; // Number of fixups in executable + t_fixup *fixup; // Extracted fixups or NULL + char *codedec; // Decoded code features or NULL + ulong codecrc; // Code CRC for actual decoding + char *hittrace; // Hit tracing data or NULL + char *hittracecopy; // Copy of INT3-substituted code + char *datadec; // Decoded data features or NULL + t_table namelist; // List of module names + t_symvar *symvar; // Descriptions of symbolic variables + int nsymvar; // Actual number of elements in symvar + int maxsymvar; // Maximal number of elements in symvar + char *globaltypes; // Global types from debug info + ulong mainentry; // Address of WinMain() etc. in dbg data + ulong realsfxentry; // Entry of packed code or NULL + int updatenamelist; // Request to update namelist + ulong origcodesize; // Original size of module code block + ulong sfxbase; // Base of memory block with SFX + ulong sfxsize; // Size of memory block with SFX + int issystemdll; // Whether system DLL + int processed; // 0: not processed, 1: good, -1: bad + int dbghelpsym; // 1: symbols loaded by dbghelp.dll + char version[NVERS]; // Version of executable file + t_jdest *jddata; // Recognized jumps within the module + int njddata; // Number of recognized jumps + ulong reserved[15]; // Reserved for plugin compatibility +} t_module; + +extc t_module* cdecl Findmodule(ulong addr); +extc t_fixup* cdecl Findfixup(t_module *pmod,ulong addr); +extc uchar* cdecl Finddecode(ulong addr,ulong *psize); +extc ulong cdecl Findfileoffset(t_module *pmod,ulong addr); +extc int cdecl Decoderange(ulong addr,ulong size,char *s); +extc int cdecl Analysecode(t_module *pm); + +//////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////// DUMP ///////////////////////////////////// + +// Standard dump types. +#define DU_ESCAPABLE 0x20000L // Create escapable dump window +#define DU_BACKUP 0x10000L // Bit indicates that backup is displayed +#define DU_TYPE 0x0F000L // Mask for dump type +#define DU_COUNT 0x00FF0L // Mask for number of items/line +#define DU_SIZE 0x0000FL // Mask for size of single item + +#define DU_UNDEF 0x00000L // Undefined dump type +#define DU_HEXTEXT 0x01000L // Hexadecimal dump with ASCII text +#define DU_TEXT 0x02000L // Character dump +#define DU_UNICODE 0x03000L // Unicode dump +#define DU_INT 0x04000L // Integer signed dump +#define DU_UINT 0x05000L // Integer unsigned dump +#define DU_IHEX 0x06000L // Integer hexadecimal dump +#define DU_FLOAT 0x07000L // Floating-point dump +#define DU_ADDR 0x08000L // Address dump +#define DU_DISASM 0x09000L // Disassembly +#define DU_HEXUNI 0x0A000L // Hexadecimal dump with UNICODE text +#define DU_ADRASC 0x0B000L // Address dump with ASCII text +#define DU_ADRUNI 0x0C000L // Address dump with UNICODE text +#define DU_SPEC 0x0D000L // Special decoding + +// Standard menu types. +#define MT_BACKUP 0x0001 // Backup, Undo +#define MT_COPY 0x0002 // Copy to clipboard +#define MT_EDIT 0x0004 // Edit, Modify, Assemble +#define MT_SEARCH 0x0008 // Search, Next +#define MT_DHISTORY 0x0010 // Previous, Next in history + +typedef ulong SPECFUNC(char *,ulong,ulong,ulong,t_disasm *,int); + +typedef struct t_dump { // Current status of dump window + t_table table; // Treat dump window as custom table + int dimmed; // Draw in lowcolor if nonzero + ulong threadid; // Use decoding and registers if not 0 + int dumptype; // Current dump type, DU_xxx+count+size + SPECFUNC *specdump; // Decoder of DU_SPEC dump types + int menutype; // Standard menues, MT_xxx + int itemwidth; // Length of displayed item, characters + int showstackframes; // Show stack frames in address dump + int showstacklocals; // Show names of locals in stack + int commentmode; // 0: comment, 1: source, 2: profile + char filename[MAX_PATH]; // Name of displayed or backup file + ulong base; // Start of memory block or file + ulong size; // Size of memory block or file + ulong addr; // Address of first displayed byte + ulong lastaddr; // Address of last displayed byte + 1 + ulong sel0; // Address of first selected byte + ulong sel1; // Last selected byte (not included!) + ulong startsel; // Start of last selection + int captured; // Mouse is captured by dump + ulong reladdr; // Addresses relative to this + char relname[SHORTLEN]; // Symbol for relative zero address base + uchar *filecopy; // Copy of the file or NULL + uchar *backup; // Old backup of memory/file or NULL + int runtraceoffset; // Offset back in run trace + ulong reserved[8]; // Reserved for the future extentions +} t_dump; + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// WINDOW FUNCTIONS /////////////////////////////// + +#define WM_USER_MENU (WM_USER+101) // Activate context-sensitive menu +#define WM_USER_SCR (WM_USER+102) // Redraw scroll(s) +#define WM_USER_SAVE (WM_USER+103) // Save data to disk +#define WM_USER_VABS (WM_USER+104) // Scroll contents of window by lines +#define WM_USER_VREL (WM_USER+105) // Scroll contents of window by percent +#define WM_USER_VBYTE (WM_USER+106) // Scroll contents of window by bytes +#define WM_USER_STS (WM_USER+107) // Start selection in window +#define WM_USER_CNTS (WM_USER+108) // Continue selection in window +#define WM_USER_CHGS (WM_USER+109) // Move single-line selection +#define WM_USER_BAR (WM_USER+110) // Message from bar segment as button +#define WM_USER_DBLCLK (WM_USER+111) // Doubleclick in column +#define WM_USER_SIZE (WM_USER+112) // Resize children in CPU window +#define WM_USER_FOCUS (WM_USER+113) // Set focus to child of CPU window +#define WM_USER_FILE (WM_USER+114) // Change state of file dump +#define WM_USER_HERE (WM_USER+115) // Query presence list +#define WM_USER_CHALL (WM_USER+116) // Redraw (almost) everything +#define WM_USER_CHMEM (WM_USER+117) // Range of debuggee's memory changed +#define WM_USER_CHREG (WM_USER+118) // Debuggee's register(s) changed +#define WM_USER_CHNAM (WM_USER+119) // Redraw name tables +#define WM_USER_MOUSE (WM_USER+120) // Check mouse coordinates +#define WM_USER_KEY (WM_USER+121) // Emulate WM_KEYDOWN +#define WM_USER_SYSKEY (WM_USER+122) // Emulate WM_SYSKEYDOWN + +// Constants used for scrolling and selection. +#define MAXTRACK 16384 // Maximal scroll of user-drawn table +#define MOVETOP 0x7FFFFFFFL // Move selection to top of table +#define MOVEBOTTOM 0x7FFFFFFEL // Move selection to bottom of table + +#define CONT_BROADCAST 0x0000 // Continue sending msg to other windows +#define STOP_BROADCAST 0x1234 // Stop sending message to other windows + +// Dumpbackup() actions. +#define BKUP_CREATE 1 // Create or update backup copy +#define BKUP_VIEWDATA 2 // View original data +#define BKUP_VIEWCOPY 3 // View backup copy +#define BKUP_LOADCOPY 4 // Read backup copy from file +#define BKUP_SAVEDATA 5 // Save original data to file +#define BKUP_SAVECOPY 6 // Save backup copy to file +#define BKUP_DELETE 7 // Delete backup copy + +extc int cdecl Registerotclass(char *classname, + char *iconname,WNDPROC classproc); +extc HWND cdecl Newtablewindow(t_table *pt,int nlines,int maxcolumns, + char *winclass,char *wintitle); +extc HWND cdecl Quicktablewindow(t_table *pt,int nlines,int maxcolumns, + char *winclass,char *wintitle); +extc HWND cdecl Createdumpwindow(char *name,ulong base,ulong size, + ulong addr,int type,SPECFUNC *specdump); +extc void cdecl Setdumptype(t_dump *pd,int dumptype); +extc void cdecl Dumpbackup(t_dump *pd,int action); +extc int cdecl Broadcast(UINT msg,WPARAM wp,LPARAM lp); + +//////////////////////////////////////////////////////////////////////////////// +////////////////////////// DATA CONVERSION FUNCTIONS /////////////////////////// + +extc ulong cdecl Compress(uchar *bufin,ulong nbufin, + uchar *bufout,ulong nbufout); +extc ulong cdecl Getoriginaldatasize(char *bufin,ulong nbufin); +extc ulong cdecl Decompress(uchar *bufin,ulong nbufin, + uchar *bufout,ulong nbufout); +extc ulong cdecl Calculatecrc(uchar *copy,ulong base,ulong size, + t_module *pmod,ulong fixupoffset); + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////// REFERENCES AND SEARCH ///////////////////////////// + +typedef struct t_ref { // Description of reference + ulong addr; // Address of reference + ulong size; // 1: single command, otherwise size + ulong type; // Type of reference, TY_xxx + ulong dest; // Destination of call +} t_ref; + +extc int cdecl Findreferences(ulong base,ulong size,ulong addr0,ulong addr1, + ulong origin,int recurseonjump,char *title); +extc int cdecl Findstrings(ulong base,ulong size,ulong origin,char *title); +extc int cdecl Findalldllcalls(t_dump *pd,ulong origin,char *title); +extc int cdecl Findallcommands(t_dump *pd,t_asmmodel *model, + ulong origin,char *title); +extc int cdecl Findallsequences(t_dump *pd,t_extmodel model[NSEQ][NMODELS], + ulong origin,char *title); +extc ulong cdecl Walkreference(int dir); +extc ulong cdecl Walkreferenceex(int dir,ulong *size); + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////// BREAKPOINT AND TRACE FUNCTIONS //////////////////////// + +#define MEMBP_READ 0x0001 // Memory breakpoint on read +#define MEMBP_WRITE 0x0002 // Memory breakpoint on write +#define MEMBP_SFX 0x1000 // Skip self-extraction + +// Bits of module hit trace buffer. +#define TR_HIT 0x01 // Command was executed during trace +#define TR_SET 0x02 // Code INT3 is in memory, copy valid +#define TR_RTSKIP 0x20 // Disable run trace on command +#define TR_RTRACE 0x40 // Force run trace on command +#define TR_TRACE 0x80 // Command is included in trace + +#define HB_FREE 0 // Breakpoint is not used +#define HB_CODE 1 // Active on command execution +#define HB_ACCESS 2 // Active on read/write access +#define HB_WRITE 3 // Active on write access +#define HB_IO 4 // Active on port I/O +#define HB_ONESHOT 5 // One-shot on command execution +#define HB_STOPAN 6 // One-shot on command and stop +#define HB_TEMP 7 // Temporary on command execution + +// Parameter mode of function Modifyhittrace(). +#define ATR_ADD 1 // Add trace to buffer +#define ATR_ADDPROC 2 // Add only recognized procedures +#define ATR_RESET 3 // Mark range as not traced +#define ATR_REMOVE 4 // Remove range and breakpoints +#define ATR_REMOVEALL 5 // Destroy range and breakpoints +#define ATR_RESTORE 6 // Restore breakpoints +#define ATR_RTRADD 7 // Add trace and force run trace +#define ATR_RTRJUMPS 8 // Add trace and run trace jumps only +#define ATR_RTRENTRY 9 // Add trace and run trace entries only +#define ATR_RTREMOVE 10 // Remove trace from range +#define ATR_RTSKIP 11 // Skip when run tracing + +// Breakpoint conditions (first byte of NM_BREAKEXPR associated with +// breakpoint). Strange settings of bits COND_NOBREAK and COND_BRKALWAYS are +// for backward compatibility with version 1.0. If both bits are 0, program +// pauses when condition is met. If both bits are set, COND_NOBREAK has higher +// priority. +#define COND_NOBREAK 0x01 // Don't break on this breakpoint +#define COND_LOGTRUE 0x02 // Log expression if condition true +#define COND_LOGALWAYS 0x04 // Log expression each pass +#define COND_ARGTRUE 0x08 // Log arguments if condition true +#define COND_ARGALWAYS 0x10 // Log arguments each pass +#define COND_BRKALWAYS 0x20 // Always break on this breakpoint +#define COND_MASK 0x3F // Mask for conditional break type bits +#define COND_FILLING 0x40 // Used to assure that byte is non-zero + +typedef struct t_bpoint { // Description of INT3 breakpoint + ulong addr; // Address of breakpoint + ulong dummy; // Always 1 + ulong type; // Type of breakpoint, TY_xxx + char cmd; // Old value of command + ulong passcount; // Actual pass count +} t_bpoint; + +typedef struct t_hardbpoint { // Description of hardware breakpoint + ulong addr; // Base address of hardware breakpoint + int size; // Size of hardware breakpoint + int type; // Type of breakpoint, one of HB_xxx + ulong reserved[4]; // Reserved for the future +} t_hardbpoint; + +extc int cdecl Setbreakpoint(ulong addr,ulong type,uchar cmd); +extc int cdecl Setbreakpointext(ulong addr,ulong type,char cmd, + ulong passcount); +extc int cdecl Manualbreakpoint(ulong addr, + int key,int shiftkey,ulong nametype,int font); +extc void cdecl Deletebreakpoints(ulong addr0,ulong addr1,int silent); +extc ulong cdecl Getbreakpointtype(ulong addr); +extc ulong cdecl Getbreakpointtypecount(ulong addr,ulong *passcount); +extc ulong cdecl Getnextbreakpoint(ulong addr,ulong *type,int *cmd); +extc void cdecl Tempbreakpoint(ulong addr,int mode); +extc int cdecl Hardbreakpoints(int closeondelete); +extc int cdecl Sethardwarebreakpoint(ulong addr,int size,int type); +extc int cdecl Deletehardwarebreakpoint(int index); +extc int cdecl Deletehardwarebreakbyaddr(ulong addr); +extc int cdecl Setmembreakpoint(int type,ulong addr,ulong size); +extc uchar* cdecl Findhittrace(ulong addr,uchar **ptracecopy,ulong *psize); +extc int cdecl Modifyhittrace(ulong addr0,ulong addr1,int mode); +extc ulong cdecl Isretaddr(ulong retaddr,ulong *procaddr); +extc HWND cdecl Creatertracewindow(void); +extc void cdecl Settracecondition(char *cond,int onsuspicious, + ulong in0,ulong in1,ulong out0,ulong out1); +extc void cdecl Settracecount(ulong count); +extc void cdecl Settracepauseoncommands(char *cmdset); +extc int cdecl Startruntrace(t_reg *preg); +extc void cdecl Deleteruntrace(void); +extc int cdecl Runtracesize(void); +extc int cdecl Findprevruntraceip(ulong ip,int startback); +extc int cdecl Findnextruntraceip(ulong ip,int startback); +extc int cdecl Getruntraceregisters(int nback,t_reg *preg, + t_reg *pold,char *cmd,char *comment); +extc int cdecl Getruntraceprofile(ulong addr,ulong size,ulong *profile); +extc void cdecl Scrollruntracewindow(int back); +extc HWND cdecl Createprofilewindow(ulong base,ulong size); + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// CODE INJECTION //////////////////////////////// + +typedef int INJECTANSWER(char *data,ulong datasize,ulong parm2); + +typedef struct t_inject { // Description of injected code + ulong codesize; // Size of code, including INT3 + char *code; // Pointer to code + int stacksize; // Stack size to save + int datatype; // 0: in/out, 1: in, 2: out +} t_inject; + +extc int cdecl Injectcode(ulong threadid,t_inject *inject,char *data, + ulong datasize,ulong parm1,ulong parm2, + INJECTANSWER *answerfunc); + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////// CPU-SPECIFIC FUNCTIONS //////////////////////////// + +// Setcpu modes. +#define CPU_ASMHIST 0x00001 // Add change to Disassembler history +#define CPU_ASMCENTER 0x00004 // Make address in the middle of window +#define CPU_ASMFOCUS 0x00008 // Move focus to Disassembler +#define CPU_DUMPHIST 0x00010 // Add change to Dump history +#define CPU_DUMPFIRST 0x00020 // Make address the first byte in Dump +#define CPU_DUMPFOCUS 0x00080 // Move focus to Dump +#define CPU_REGAUTO 0x00100 // Automatically switch to FPU/MMX/3DNow! +#define CPU_RUNTRACE 0x00200 // Show run trace data at offset asmaddr +#define CPU_STACKFOCUS 0x00800 // Move focus to Stack +#define CPU_NOCREATE 0x04000 // Don't create CPU window if absent +#define CPU_REDRAW 0x08000 // Redraw CPU window immediately +#define CPU_NOFOCUS 0x10000 // Don't assign focus to main window + +extc void cdecl Setcpu(ulong threadid,ulong asmaddr, + ulong dumpaddr,ulong stackaddr,int mode); +extc void cdecl Setdisasm(ulong asmaddr,ulong selsize,int mode); +extc void cdecl Redrawdisassembler(void); +extc void cdecl Getdisassemblerrange(ulong *pbase,ulong *psize); +extc ulong cdecl Findprocbegin(ulong addr); +extc ulong cdecl Findprocend(ulong addr); +extc ulong cdecl Findprevproc(ulong addr); +extc ulong cdecl Findnextproc(ulong addr); +extc int cdecl Getproclimits(ulong addr,ulong *start,ulong *end); +extc void cdecl Sendshortcut(int where,ulong addr, + int msg,int ctrl,int shift,int vkcode); + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// PROCESS CONTROL //////////////////////////////// + +// Parameter stepmode in call to Go(). +#define STEP_SAME 0 // Same action as on previous call +#define STEP_RUN 1 // Run program +#define STEP_OVER 2 // Step over +#define STEP_IN 3 // Step in +#define STEP_SKIP 4 // Skip sequence + +typedef enum t_status { // Thread/process status + STAT_NONE=0, // Thread/process is empty + STAT_STOPPED, // Thread/process suspended + STAT_EVENT, // Processing debug event, process paused + STAT_RUNNING, // Thread/process running + STAT_FINISHED, // Process finished + STAT_CLOSING // Process is requested to terminate +} t_status; + +extc t_status cdecl Getstatus(void); +extc int cdecl Go(ulong threadid,ulong tilladdr,int stepmode, + int givechance,int backupregs); +extc int cdecl Suspendprocess(int processevents); + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////// DECODING OF ARGUMENTS ///////////////////////////// + +#define DASC_TEST 0 // Test whether a string +#define DASC_NOHEX 1 // Test, print nothing if not a string +#define DASC_ASCII 2 // Force ASCII +#define DASC_PASCAL 3 // Force Pascal + +extc uchar* cdecl Findknownfunction(ulong addr,int direct, + int level,char *fname); +extc int cdecl Decodeknownargument(ulong addr,uchar *arg,ulong value, + int valid,char *s,char *mask,uchar *pset[]); +extc char cdecl *Findunknownfunction(ulong ip,char *code,char *dec, + ulong size,char *fname); +extc int cdecl Decodeascii(ulong value,char *s,int len,int mode); +extc int cdecl Decodeunicode(ulong value,char *s,int len); + +//////////////////////////////////////////////////////////////////////////////// +///////////////////////////// SOURCE CODE SUPPORT ////////////////////////////// + +#define FIRST_CODE_LINE 0xFFFFFFFF // First available source line + +typedef struct t_sourceline { // Source line descriptor + ulong addr; // Start address of corresponding code + ulong size; // Code size + ulong srcname; // Index of source name (as NM_DEBUG) + ulong line; // 0-based line number +} t_sourceline; + +extc HWND cdecl Showsourcefromaddress(ulong addr,int show); +extc int cdecl Getresourcestring(t_module *pm,ulong id,char *s); +extc t_sourceline* cdecl Getlinefromaddress(ulong addr); +extc ulong cdecl Getaddressfromline(ulong addr0,ulong addr1, + char *path,ulong line); +extc int cdecl Getsourcefilelimits(ulong nameaddr, + ulong *addr0,ulong *addr1); +extc int cdecl Decodefullvarname(t_module *pmod,t_symvar *psym, + int offset,char *name); +extc int cdecl Getbprelname(t_module *pmod,ulong addr,long offset, + char *s,int nsymb); + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// WATCH SUPPORT ///////////////////////////////// + +extc HWND cdecl Createwatchwindow(void); +extc int cdecl Deletewatch(int indexone); +extc int cdecl Insertwatch(int indexone,char *text); +extc int cdecl Getwatch(int indexone,char *text); + +//////////////////////////////////////////////////////////////////////////////// +////////////////////////// WINDOWS-SPECIFIC FUNCTIONS ////////////////////////// + +#define MAXNEST 32 // Max allowed code structure nesting + +typedef struct t_window { // Description of window + ulong hwnd; // Window's handle + ulong dummy; // Must be 1 + ulong type; // Type of window, TY_xxx + ulong parenthw; // Handle of parent or 0 + ulong winproc; // Address of WinProc or 0 + ulong threadid; // ID of the owning thread + ulong exstyle; // Extended style + ulong style; // Style + ulong id; // Identifier + ulong classproc; // Address of default (class) WinProc + int child; // Index of next child + int level; // Level in genealogy (0: topmost) + int sibling; // Index of next sibling + int byparent; // Index when sorted by parent + char title[TEXTLEN]; // Window's title + char classname[TEXTLEN]; // Class name + char tree[MAXNEST]; // Tree display +} t_window; + +extc HWND cdecl Createwinwindow(void); + + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// PATCHES //////////////////////////////////// + +typedef struct t_patch { + ulong addr; // Base address of patch in memory + ulong size; // Size of patch, bytes + ulong type; // Type of patch, set of TY_xxx + char orig[TEXTLEN]; // Original code + char mod[TEXTLEN]; // Patched code +} t_patch; + +extc HWND cdecl Createpatchwindow(void); + + +//////////////////////////////////////////////////////////////////////////////// +////////////////////////// PLUGIN-SPECIFIC FUNCTIONS /////////////////////////// + +// Parameters of Plugingetvalue(). +#define VAL_HINST 1 // Current program instance +#define VAL_HWMAIN 2 // Handle of the main window +#define VAL_HWCLIENT 3 // Handle of the MDI client window +#define VAL_NCOLORS 4 // Number of common colors +#define VAL_COLORS 5 // RGB values of common colors +#define VAL_BRUSHES 6 // Handles of common color brushes +#define VAL_PENS 7 // Handles of common color pens +#define VAL_NFONTS 8 // Number of common fonts +#define VAL_FONTS 9 // Handles of common fonts +#define VAL_FONTNAMES 10 // Internal font names +#define VAL_FONTWIDTHS 11 // Average widths of common fonts +#define VAL_FONTHEIGHTS 12 // Average heigths of common fonts +#define VAL_NFIXFONTS 13 // Actual number of fixed-pitch fonts +#define VAL_DEFFONT 14 // Index of default font +#define VAL_NSCHEMES 15 // Number of color schemes +#define VAL_SCHEMES 16 // Color schemes +#define VAL_DEFSCHEME 17 // Index of default colour scheme +#define VAL_DEFHSCROLL 18 // Default horizontal scroll +#define VAL_RESTOREWINDOWPOS 19 // Restore window positions from .ini +#define VAL_HPROCESS 20 // Handle of Debuggee +#define VAL_PROCESSID 21 // Process ID of Debuggee +#define VAL_HMAINTHREAD 22 // Handle of main thread +#define VAL_MAINTHREADID 23 // Thread ID of main thread +#define VAL_MAINBASE 24 // Base of main module in the process +#define VAL_PROCESSNAME 25 // Name of the active process +#define VAL_EXEFILENAME 26 // Name of the main debugged file +#define VAL_CURRENTDIR 27 // Current directory for debugged process +#define VAL_SYSTEMDIR 28 // Windows system directory +#define VAL_DECODEANYIP 29 // Decode registers dependless on EIP +#define VAL_PASCALSTRINGS 30 // Decode Pascal-style string constants +#define VAL_ONLYASCII 31 // Only printable ASCII chars in dump +#define VAL_DIACRITICALS 32 // Allow diacritical symbols in strings +#define VAL_GLOBALSEARCH 33 // Search from the beginning of block +#define VAL_ALIGNEDSEARCH 34 // Search aligned to item's size +#define VAL_IGNORECASE 35 // Ignore case in string search +#define VAL_SEARCHMARGIN 36 // Floating search allows error margin +#define VAL_KEEPSELSIZE 37 // Keep size of hex edit selection +#define VAL_MMXDISPLAY 38 // MMX display mode in dialog +#define VAL_WINDOWFONT 39 // Use calling window's font in dialog +#define VAL_TABSTOPS 40 // Distance between tab stops +#define VAL_MODULES 41 // Table of modules (.EXE and .DLL) +#define VAL_MEMORY 42 // Table of allocated memory blocks +#define VAL_THREADS 43 // Table of active threads +#define VAL_BREAKPOINTS 44 // Table of active breakpoints +#define VAL_REFERENCES 45 // Table with found references +#define VAL_SOURCELIST 46 // Table of source files +#define VAL_WATCHES 47 // Table of watches +#define VAL_CPUFEATURES 50 // CPU feature bits +#define VAL_TRACEFILE 51 // Handle of run trace log file +#define VAL_ALIGNDIALOGS 52 // Whether to align dialogs +#define VAL_CPUDASM 53 // Dump descriptor of CPU Disassembler +#define VAL_CPUDDUMP 54 // Dump descriptor of CPU Dump +#define VAL_CPUDSTACK 55 // Dump descriptor of CPU Stack +#define VAL_APIHELP 56 // Name of selected API help file +#define VAL_HARDBP 57 // Whether hardware breakpoints enabled +#define VAL_PATCHES 58 // Table of patches +#define VAL_HINTS 59 // Sorted data with analysis hints + +extc int cdecl Registerpluginclass(char *classname,char *iconname, + HINSTANCE dllinst,WNDPROC classproc); +extc void cdecl Unregisterpluginclass(char *classname); +extc int cdecl Pluginwriteinttoini(HINSTANCE dllinst,char *key,int value); +extc int cdecl Pluginwritestringtoini(HINSTANCE dllinst,char *key,char *s); +extc int cdecl Pluginreadintfromini(HINSTANCE dllinst,char *key,int def); +extc int cdecl Pluginreadstringfromini(HINSTANCE dllinst,char *key, + char *s,char *def); +extc int cdecl Pluginsaverecord(ulong tag,ulong size,void *data); +extc int cdecl Plugingetvalue(int type); + +//////////////////////////////////////////////////////////////////////////////// +////////////////////// EXPORTED PLUGIN CALLBACK FUNCTIONS ////////////////////// + +// Origins of standard OllyDbg windows as passed to plugin. In parenthesis is +// the type of item you get in ODBG_Pluginmenu(), ODBG_Pluginaction() and +// ODBG_Pluginshortcut(). Note that this item can be NULL! +#define PM_MAIN 0 // Main window (NULL) +#define PM_DUMP 10 // Any Dump window (t_dump*) +#define PM_MODULES 11 // Modules window (t_module*) +#define PM_MEMORY 12 // Memory window (t_memory*) +#define PM_THREADS 13 // Threads window (t_thread*) +#define PM_BREAKPOINTS 14 // Breakpoints window (t_bpoint*) +#define PM_REFERENCES 15 // References window (t_ref*) +#define PM_RTRACE 16 // Run trace window (int*) +#define PM_WATCHES 17 // Watches window (1-based index) +#define PM_WINDOWS 18 // Windows window (t_window*) +#define PM_DISASM 31 // CPU Disassembler (t_dump*) +#define PM_CPUDUMP 32 // CPU Dump (t_dump*) +#define PM_CPUSTACK 33 // CPU Stack (t_dump*) +#define PM_CPUREGS 34 // CPU Registers (t_reg*) + +// Reasons why debugged application was paused, as a first argument in call to +// ODBG_Paused(), ODBG_Pausedex() and ODBG_Plugincmd(). +#define PP_MAIN 0x0003 // Mask to extract main reason +#define PP_EVENT 0x0000 // Paused on debugging event +#define PP_PAUSE 0x0001 // Paused on user's request +#define PP_TERMINATED 0x0002 // Application terminated +// Extended reasons in ODBG_Pausedex(). +#define PP_BYPROGRAM 0x0004 // Debugging event caused by program +#define PP_INT3BREAK 0x0010 // INT3 breakpoint +#define PP_MEMBREAK 0x0020 // Memory breakpoint +#define PP_HWBREAK 0x0040 // Hardware breakpoint +#define PP_SINGLESTEP 0x0080 // Single-step trap +#define PP_EXCEPTION 0x0100 // Exception, like division by 0 +#define PP_ACCESS 0x0200 // Access violation +#define PP_GUARDED 0x0400 // Guarded page + +// Record tags in .udd files. +#define MI_SIGNATURE 0x00646F4DL // Module info signature +#define MI_VERSION 0x7265560AL // OllyDbg version +#define MI_FILENAME 0x6C69460AL // Record with full name of executable +#define MI_FILESIZE 0x7A69530AL // Record with file size +#define MI_TIMESTAMP 0x7473540AL // Record with timestamp file data +#define MI_SFXENTRY 0x6566530AL // Real entry of SFX-able module +#define MI_PATCH 0x7461500AL // Record with patch data +#define MI_USER 0x0073550AL // Record with user-defined label/comment +#define MI_PROCDATA 0x6372500AL // Record with procedure data +#define MI_SWDATA 0x6977530AL // Record with switch data +#define MI_CALLFINT 0x6966430AL // Record with internal call +#define MI_CALLFMOD 0x6D66430AL // Record with intermodular call +#define MI_CALLFABS 0x6166430AL // Record with absolute call +#define MI_INT3BREAK 0x7470420AL // Record with breakpoint data +#define MI_INT3BRKC 0x6370420AL // Record with checked breakpoint data +#define MI_HDWRBREAK 0x7262480AL // Record with hardware breakpoint data +#define MI_JDDATA 0x74644A0AL // Record with all module jump data +#define MI_ANALYSIS 0x616E410AL // Record with analysis data +#define MI_ANALPACK 0x636E410AL // Record with compressed analysis data +#define MI_AHINT 0x7468410AL // Record with analysis hint data +#define MI_TRACE 0x6172540AL // Record with trace data +#define MI_TRACEPACK 0x6372540AL // Record with compressed trace data +#define MI_CODECRC 0x7263430AL // Record with CRC of code for analysis +#define MI_SAVEAREA 0x6176530AL // Record with general-purpose save area +#define MI_END 0x646E450AL // End of module info data +// Tags reserved for 3rd-party plugins. +#define MI_WINJUG 0x67754A0AL // WindowJuggler by EsseEmme +#define MI_WINJU1 0x31754A0AL +#define MI_WINJU2 0x32754A0AL +#define MI_WINJU3 0x33754A0AL +#define MI_APPST 0x73614F0AL // OllyAppStarter by Homunculus + +// Prototypes for plugin callback functions. +extc int _export cdecl ODBG_Plugindata(char shortname[32]); +extc int _export cdecl ODBG_Plugininit(int ollydbgversion,HWND hw, + ulong *features); +extc void _export cdecl ODBG_Pluginmainloop(DEBUG_EVENT *debugevent); +extc void _export cdecl ODBG_Pluginsaveudd(t_module *pmod,int ismainmodule); +extc int _export cdecl ODBG_Pluginuddrecord(t_module *pmod,int ismainmodule, + ulong tag,ulong size,void *data); +extc int _export cdecl ODBG_Pluginmenu(int origin,char data[4096],void *item); +extc void _export cdecl ODBG_Pluginaction(int origin,int action,void *item); +extc int _export cdecl ODBG_Pluginshortcut( + int origin,int ctrl,int alt,int shift,int key, + void *item); +extc void _export cdecl ODBG_Pluginreset(void); +extc int _export cdecl ODBG_Pluginclose(void); +extc void _export cdecl ODBG_Plugindestroy(void); +extc int _export cdecl ODBG_Paused(int reason,t_reg *reg); +extc int _export cdecl ODBG_Pausedex(int reasonex,int dummy,t_reg *reg, + DEBUG_EVENT *debugevent); +extc int _export cdecl ODBG_Plugincmd(int reason,t_reg *reg,char *cmd); + diff --git a/src/hooks.c b/src/hooks.c new file mode 100644 index 0000000..317aeda --- /dev/null +++ b/src/hooks.c @@ -0,0 +1,194 @@ +//--------------------------------------------------------------------------- +// OllyHeapTrace - A Heap Tracer plugin for OllyDbg +// By Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// Copyright (c) 2008 Stephen Fewer of Harmony Security +//--------------------------------------------------------------------------- +#include +//#include + +#include "hooks.h" + +extern DWORD dwProcessHeap; + +struct HEAPFLAGS +{ + DWORD dwValue; + const char * cpName; +}; +//--------------------------------------------------------------------------- +struct HEAPFLAGS flags[] = { + + { HEAP_GENERATE_EXCEPTIONS, "HEAP_GENERATE_EXCEPTIONS" }, + { HEAP_NO_SERIALIZE, "HEAP_NO_SERIALIZE" }, + { HEAP_ZERO_MEMORY, "HEAP_ZERO_MEMORY" }, + + { NULL, NULL } +}; +//--------------------------------------------------------------------------- +VOID ResolveHeapFlags( DWORD dwFlags, char * cpOutput ) +{ + int iCount = 0, i = 0; + memset( cpOutput, 0, MAX_PATH ); + + while( flags[i].cpName != NULL ) + { + if( dwFlags & flags[i].dwValue == flags[i].dwValue ) + { + if( iCount > 0 ) + strcat( cpOutput, " | " ); + strcat( cpOutput, flags[i].cpName ); + iCount++; + } + i++; + } + + if( iCount == 0 ) + strcat( cpOutput, "0" ); +} +//--------------------------------------------------------------------------- +BOOL DefaultDWORD_Return( LPLOGDATA pLogData, t_reg * pRegisters ) +{ + snprintf( pLogData->cReturnMessage, BUFFER_SIZE, "0x%.8X", pRegisters->r[REG_EAX] ); + return TRUE; +} +//--------------------------------------------------------------------------- +BOOL DefaultINT_Return( LPLOGDATA pLogData, t_reg * pRegisters ) +{ + snprintf( pLogData->cReturnMessage, BUFFER_SIZE, "%d", pRegisters->r[REG_EAX] ); + return TRUE; +} +//--------------------------------------------------------------------------- +BOOL DefaultBOOL_Return( LPLOGDATA pLogData, t_reg * pRegisters ) +{ + snprintf( pLogData->cReturnMessage, BUFFER_SIZE, "%s", (pRegisters->r[REG_EAX] ? "TRUE" : "FALSE" ) ); + return TRUE; +} +//--------------------------------------------------------------------------- +BOOL RtlInitializeCriticalSection_Call( LPLOGDATA pLogData, t_reg * pRegisters ) +{ + DWORD dwParameter; + Readmemory( &dwParameter, pRegisters->r[REG_ESP]+4, 4, MM_SILENT ); + snprintf( pLogData->cMessage, BUFFER_SIZE, "RtlInitializeCriticalSection( 0x%.8X )", dwParameter ); + return TRUE; +} +//--------------------------------------------------------------------------- +BOOL RtlDeleteCriticalSection_Call( LPLOGDATA pLogData, t_reg * pRegisters ) +{ + DWORD dwParameter; + Readmemory( &dwParameter, pRegisters->r[REG_ESP]+4, 4, MM_SILENT ); + snprintf( pLogData->cMessage, BUFFER_SIZE, "RtlDeleteCriticalSection( 0x%.8X )", dwParameter ); + return TRUE; +} +//--------------------------------------------------------------------------- +BOOL RtlAllocateHeap_Call( LPLOGDATA pLogData, t_reg * pRegisters ) +{ + char cFlagsOutput[MAX_PATH]; + DWORD dwParameters[3]; + Readmemory( &dwParameters, pRegisters->r[REG_ESP]+4, 12, MM_SILENT ); + ResolveHeapFlags( dwParameters[1], (char *)&cFlagsOutput ); + if( dwProcessHeap != NULL && dwParameters[0] == dwProcessHeap ) + snprintf( pLogData->cMessage, BUFFER_SIZE, "RtlAllocateHeap( GetProcessHeap(), %s, %d )", cFlagsOutput, dwParameters[2] ); + else + snprintf( pLogData->cMessage, BUFFER_SIZE, "RtlAllocateHeap( 0x%.8X, %s, %d )", dwParameters[0], cFlagsOutput, dwParameters[2] ); + pLogData->dwHeap = dwParameters[0]; + pLogData->dwHeapBlockSize = dwParameters[2]; + return TRUE; +} +//--------------------------------------------------------------------------- +BOOL RtlAllocateHeap_Return( LPLOGDATA pLogData, t_reg * pRegisters ) +{ + pLogData->dwHeapBlock = pRegisters->r[REG_EAX]; + snprintf( pLogData->cReturnMessage, BUFFER_SIZE, "0x%.8X", pLogData->dwHeapBlock ); + return TRUE; +} +//--------------------------------------------------------------------------- +BOOL RtlReAllocateHeap_Call( LPLOGDATA pLogData, t_reg * pRegisters ) +{ + char cFlagsOutput[MAX_PATH]; + DWORD dwParameters[4]; + Readmemory( &dwParameters, pRegisters->r[REG_ESP]+4, 16, MM_SILENT ); + ResolveHeapFlags( dwParameters[1], (char *)&cFlagsOutput ); + if( dwProcessHeap != NULL && dwParameters[0] == dwProcessHeap ) + snprintf( pLogData->cMessage, BUFFER_SIZE, "RtlReAllocateHeap( GetProcessHeap(), %s, 0x%.8X, %d )", cFlagsOutput, dwParameters[2], dwParameters[3] ); + else + snprintf( pLogData->cMessage, BUFFER_SIZE, "RtlReAllocateHeap( 0x%.8X, %s, 0x%.8X, %d )", dwParameters[0], cFlagsOutput, dwParameters[2], dwParameters[3] ); + pLogData->dwHeap = dwParameters[0]; + pLogData->dwHeapBlockSize = dwParameters[3]; + pLogData->dwHeapBlock = dwParameters[2]; + return TRUE; +} +//--------------------------------------------------------------------------- +BOOL RtlFreeHeap_Call( LPLOGDATA pLogData, t_reg * pRegisters ) +{ + char cFlagsOutput[MAX_PATH]; + DWORD dwParameters[3]; + Readmemory( &dwParameters, pRegisters->r[REG_ESP]+4, 12, MM_SILENT ); + ResolveHeapFlags( dwParameters[1], (char *)&cFlagsOutput ); + if( dwProcessHeap != NULL && dwParameters[0] == dwProcessHeap ) + snprintf( pLogData->cMessage, BUFFER_SIZE, "RtlFreeHeap( GetProcessHeap(), %s, 0x%.8X )", cFlagsOutput, dwParameters[2] ); + else + snprintf( pLogData->cMessage, BUFFER_SIZE, "RtlFreeHeap( 0x%.8X, %s, 0x%.8X )", dwParameters[0], cFlagsOutput, dwParameters[2] ); + pLogData->dwHeap = dwParameters[0]; + pLogData->dwHeapBlock = dwParameters[2]; + return TRUE; +} +//--------------------------------------------------------------------------- +BOOL RtlCreateHeap_Call( LPLOGDATA pLogData, t_reg * pRegisters ) +{ + char cFlagsOutput[MAX_PATH]; + DWORD dwParameters[3]; + Readmemory( &dwParameters, pRegisters->r[REG_ESP]+4, 12, MM_SILENT ); + ResolveHeapFlags( dwParameters[0], (char *)&cFlagsOutput ); + snprintf( pLogData->cMessage, BUFFER_SIZE, "RtlCreateHeap( %s, %d, %d )", cFlagsOutput, dwParameters[1], dwParameters[2] ); + return TRUE; +} +//--------------------------------------------------------------------------- +BOOL RtlCreateHeap_Return( LPLOGDATA pLogData, t_reg * pRegisters ) +{ + snprintf( pLogData->cReturnMessage, BUFFER_SIZE, "0x%.8X", pRegisters->r[REG_EAX] ); + pLogData->dwHeap = pRegisters->r[REG_EAX]; + return TRUE; +} +//--------------------------------------------------------------------------- +BOOL GetProcessHeap_Call( LPLOGDATA pLogData, t_reg * pRegisters ) +{ + snprintf( pLogData->cMessage, BUFFER_SIZE, "GetProcessHeap()" ); + return TRUE; +} +//--------------------------------------------------------------------------- +BOOL GetProcessHeap_Return( LPLOGDATA pLogData, t_reg * pRegisters ) +{ + dwProcessHeap = pRegisters->r[REG_EAX]; + snprintf( pLogData->cReturnMessage, BUFFER_SIZE, "0x%.8X", dwProcessHeap ); + pLogData->dwHeap = dwProcessHeap; + return TRUE; +} +//--------------------------------------------------------------------------- +BOOL RtlDestroyHeap_Call( LPLOGDATA pLogData, t_reg * pRegisters ) +{ + DWORD dwParameter; + Readmemory( &dwParameter, pRegisters->r[REG_ESP]+4, 4, MM_SILENT ); + if( dwProcessHeap != NULL && dwParameter == dwProcessHeap ) + snprintf( pLogData->cMessage, BUFFER_SIZE, "RtlDestroyHeap( GetProcessHeap() )" ); + else + snprintf( pLogData->cMessage, BUFFER_SIZE, "RtlDestroyHeap( 0x%.8X )", dwParameter ); + pLogData->dwHeap = dwParameter; + return TRUE; +} +//--------------------------------------------------------------------------- +BOOL RtlSizeHeap_Call( LPLOGDATA pLogData, t_reg * pRegisters ) +{ + char cFlagsOutput[MAX_PATH]; + DWORD dwParameters[3]; + Readmemory( &dwParameters, pRegisters->r[REG_ESP]+4, 12, MM_SILENT ); + ResolveHeapFlags( dwParameters[1], (char *)&cFlagsOutput ); + if( dwProcessHeap != NULL && dwParameters[0] == dwProcessHeap ) + snprintf( pLogData->cMessage, BUFFER_SIZE, "RtlSizeHeap( GetProcessHeap(), %s, 0x%.8X )", cFlagsOutput, dwParameters[2] ); + else + snprintf( pLogData->cMessage, BUFFER_SIZE, "RtlSizeHeap( 0x%.8X, %s, 0x%.8X )", dwParameters[0], cFlagsOutput, dwParameters[2] ); + pLogData->dwHeap = dwParameters[0]; + pLogData->dwHeapBlock = dwParameters[2]; + return TRUE; +} +//--------------------------------------------------------------------------- + diff --git a/src/hooks.h b/src/hooks.h new file mode 100644 index 0000000..546f4c7 --- /dev/null +++ b/src/hooks.h @@ -0,0 +1,65 @@ +//--------------------------------------------------------------------------- +// OllyHeapTrace - A Heap Tracer plugin for OllyDbg +// By Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// Copyright (c) 2008 Stephen Fewer of Harmony Security +//--------------------------------------------------------------------------- +#ifndef HOOKS_H +#define HOOKS_h + +#define WIN32_LEAN_AND_MEAN +#include + +#include "Plugin.h" + +#pragma nopackwarning + +#define BUFFER_SIZE 256 + +typedef struct _LOGDATA +{ + DWORD dwAddress; + DWORD dwSize; + DWORD dwType; + + DWORD dwCallerAddress; + DWORD dwThreadId; + DWORD dwHeap; + DWORD dwHeapBlock; + DWORD dwHeapBlockSize; + char cMessage[BUFFER_SIZE]; + char cReturnMessage[BUFFER_SIZE]; + BOOL bReturnMessageSet; + int iHookIndex; + +} LOGDATA, * LPLOGDATA; + +typedef BOOL (* HOOK_FUNC)( LPLOGDATA pLogData, t_reg * pRegisters ); + +struct HOOK +{ + const char * cpModuleName; + const char * cpFunctionName; + DWORD dwFunctionAddress; + HOOK_FUNC handle_call; + HOOK_FUNC handle_return; +}; + +BOOL DefaultDWORD_Return( LPLOGDATA, t_reg * ); +BOOL DefaultBOOL_Return( LPLOGDATA, t_reg * ); +BOOL DefaultINT_Return( LPLOGDATA, t_reg * ); + +BOOL RtlInitializeCriticalSection_Call( LPLOGDATA, t_reg * ); +BOOL RtlDeleteCriticalSection_Call( LPLOGDATA, t_reg * ); + +BOOL RtlAllocateHeap_Call( LPLOGDATA , t_reg * ); +BOOL RtlAllocateHeap_Return( LPLOGDATA , t_reg * ); +BOOL RtlReAllocateHeap_Call( LPLOGDATA , t_reg * ); +BOOL RtlFreeHeap_Call( LPLOGDATA , t_reg * ); +BOOL RtlCreateHeap_Call( LPLOGDATA , t_reg * ); +BOOL RtlCreateHeap_Return( LPLOGDATA, t_reg * ); +BOOL GetProcessHeap_Call( LPLOGDATA , t_reg * ); +BOOL GetProcessHeap_Return( LPLOGDATA, t_reg * ); +BOOL RtlDestroyHeap_Call( LPLOGDATA, t_reg * ); +BOOL RtlSizeHeap_Call( LPLOGDATA, t_reg * ); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..88feac6 --- /dev/null +++ b/src/main.c @@ -0,0 +1,526 @@ +//--------------------------------------------------------------------------- +// OllyHeapTrace - A Heap Tracer plugin for OllyDbg +// By Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// Copyright (c) 2008 Stephen Fewer of Harmony Security +//--------------------------------------------------------------------------- +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +#include "hooks.h" +#pragma nopackwarning +//--------------------------------------------------------------------------- +#pragma link ".\\bin\\OllyDbg.lib" +//--------------------------------------------------------------------------- +#define OLLYHT_NAME "OllyHeapTrace" +#define OLLYHT_VERSION "1.1" +#define OLLYHT_ABOUT "By Stephen Fewer of Harmony Security (www.harmonysecurity.com)" + +struct COLORS +{ + BYTE bColor; + DWORD dwHeap; +}; + +struct COLORS colors[NCOLORS-1] = {0}; +//--------------------------------------------------------------------------- +struct HOOK hooks[] = { + { "ntdll", "RtlAllocateHeap", NULL, RtlAllocateHeap_Call, RtlAllocateHeap_Return }, + { "ntdll", "RtlFreeHeap", NULL, RtlFreeHeap_Call, DefaultBOOL_Return }, + { "ntdll", "RtlCreateHeap", NULL, RtlCreateHeap_Call, RtlCreateHeap_Return }, + { "ntdll", "RtlDestroyHeap", NULL, RtlDestroyHeap_Call, DefaultBOOL_Return }, + { "ntdll", "RtlReAllocateHeap", NULL, RtlReAllocateHeap_Call, RtlAllocateHeap_Return }, + { "ntdll", "RtlSizeHeap", NULL, RtlSizeHeap_Call, DefaultINT_Return }, + { "ntdll", "RtlInitializeCriticalSection", NULL, RtlInitializeCriticalSection_Call, NULL }, + { "ntdll", "RtlDeleteCriticalSection", NULL, RtlDeleteCriticalSection_Call, NULL }, + + { "kernel32", "GetProcessHeap", NULL, GetProcessHeap_Call, GetProcessHeap_Return }, + + { NULL, NULL, NULL, NULL, NULL } +}; +//--------------------------------------------------------------------------- +HINSTANCE hDll = NULL; +HANDLE hOllyWindow = NULL; +volatile BOOL bEnabled = FALSE; +char cLogWindowClass[32] = { 0 }; +t_table logtable = { 0 }; + +volatile DWORD dwLogIndex = 0; +//DWORD dwIgnoreHeaps[MAX_PATH] = {0}; +DWORD dwProcessHeap = NULL; +//--------------------------------------------------------------------------- +int WINAPI DllEntryPoint( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved ) +{ + if( dwReason == DLL_PROCESS_ATTACH ) + hDll = hInstance; + return 1; +} +//--------------------------------------------------------------------------- +int _export cdecl ODBG_Plugindata( char cShortname[32] ) +{ + strcpy( cShortname, OLLYHT_NAME ); + return PLUGIN_VERSION; +} +//--------------------------------------------------------------------------- +BYTE GetColor( DWORD dwHeap ) +{ + int i; + for( i=0 ; idwHeap ); + + if( iColumn == 0 ) + { + *pSelect = DRAW_GRAY; + i = Decodeaddress( pLogData->dwCallerAddress, 0, ADC_VALID, cpBuffer, BUFFER_SIZE, NULL ); + if( i == 0 ) + i = snprintf( cpBuffer, BUFFER_SIZE, "0x%.8X", pLogData->dwCallerAddress ); + } + else if( iColumn == 1 ) + { + *pSelect = DRAW_GRAY; + i = snprintf( cpBuffer, BUFFER_SIZE, "0x%.8X", pLogData->dwThreadId ); + } + else if( iColumn == 2 ) + { + i = snprintf( cpBuffer, BUFFER_SIZE, "%s", pLogData->cMessage ); + *pSelect = DRAW_MASK; + memset( pMask, DRAW_DIRECT|bColor, i ); + } + else if( iColumn == 3 ) + { + if( strlen( pLogData->cReturnMessage ) > 0 ) + { + i = snprintf( cpBuffer, BUFFER_SIZE, "%s", pLogData->cReturnMessage ); + *pSelect = DRAW_MASK; + memset( pMask, DRAW_DIRECT|bColor, i ); + } + } + return i; +} +//--------------------------------------------------------------------------- +void CreateLogWindow( void ) +{ + if( logtable.bar.nbar == 0 ) + { + logtable.bar.name[0] = "Caller"; + logtable.bar.defdx[0] = 20; + logtable.bar.mode[0] = BAR_NOSORT; + + logtable.bar.name[1] = "Thread"; + logtable.bar.defdx[1] = 12; + logtable.bar.mode[1] = BAR_NOSORT; + + logtable.bar.name[2] = "Function Call"; + logtable.bar.defdx[2] = 64; + logtable.bar.mode[2] = BAR_NOSORT; + + logtable.bar.name[3] = "Return Value"; + logtable.bar.defdx[3] = 16; + logtable.bar.mode[3] = BAR_NOSORT; + + logtable.bar.nbar = 4; + logtable.mode = TABLE_COPYMENU|TABLE_APPMENU|TABLE_SAVEPOS|TABLE_ONTOP; + logtable.drawfunc = LogWindowGetText; + } + Quicktablewindow( &logtable, 15, logtable.bar.nbar, cLogWindowClass, "OllyHeapTrace - Log" ); +} +//--------------------------------------------------------------------------- +VOID HandleRightClick( HWND hw ) +{ + LPLOGDATA pLogData; + HMENU hMenu; + int i; + char cBuffer[BUFFER_SIZE]; + + hMenu = CreatePopupMenu(); + pLogData = (LPLOGDATA)Getsortedbyselection( &(logtable.data), logtable.data.selected ); + if( hMenu != NULL && pLogData != NULL ) + { + if( pLogData->dwHeap != NULL ) + { + snprintf( cBuffer, BUFFER_SIZE, "Delete trace for heap 0x%.8X", pLogData->dwHeap ); + AppendMenu( hMenu, MF_STRING, 1, cBuffer ); + + snprintf( cBuffer, BUFFER_SIZE, "View dump of heap 0x%.8X", pLogData->dwHeap ); + AppendMenu( hMenu, MF_STRING, 2, cBuffer ); + + if( pLogData->dwHeapBlock != NULL ) + { + snprintf( cBuffer, BUFFER_SIZE, "View dump of heap block 0x%.8X", pLogData->dwHeapBlock ); + AppendMenu( hMenu, MF_STRING, 3, cBuffer ); + } + } + + } + i = Tablefunction( &logtable, hw, WM_USER_MENU, 0, (LPARAM)hMenu ); + if( hMenu != NULL ) + DestroyMenu( hMenu ); + + if( i == 1 ) + { + DWORD dwHeap = pLogData->dwHeap; + pLogData = (LPLOGDATA)logtable.data.data; + for( i=0 ; idwHeap, 4096, 0, 0x01101, NULL ); + } + else if( i == 3 && pLogData->dwHeapBlock != NULL ) + { + if( pLogData->dwHeapBlockSize == NULL ) + pLogData->dwHeapBlockSize = 4096; + Createdumpwindow( "OllyHeapTrace - Dump Heap Block", pLogData->dwHeapBlock, pLogData->dwHeapBlockSize, 0, 0x01101, NULL ); + //Createdumpwindow( "OllyHeapTrace - Dump Heap Block", pLogData->dwHeapBlock - 8, pLogData->dwHeapBlockSize + 8, 0, 0x01101, NULL ); + } +} +//--------------------------------------------------------------------------- +LRESULT CALLBACK LogWindowProc( HWND hw,UINT msg,WPARAM wp,LPARAM lp) +{ + LPLOGDATA pLogData; + + switch( msg ) + { + case WM_DESTROY: + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + case WM_HSCROLL: + case WM_VSCROLL: + case WM_TIMER: + case WM_SYSKEYDOWN: + case WM_USER_SCR: + case WM_USER_VABS: + case WM_USER_VREL: + case WM_USER_VBYTE: + case WM_USER_STS: + case WM_USER_CNTS: + case WM_USER_CHGS: + case WM_KEYDOWN: + return Tablefunction( &logtable, hw, msg, wp, lp ); + case WM_USER_MENU: + HandleRightClick( hw ); + return 0; + case WM_USER_DBLCLK: + pLogData = (LPLOGDATA)Getsortedbyselection( &(logtable.data), logtable.data.selected ); + if ( pLogData != NULL ) + Setcpu( 0, pLogData->dwCallerAddress, 0, 0, CPU_ASMHIST|CPU_ASMCENTER|CPU_ASMFOCUS ); + return 1; + case WM_USER_CHALL: + case WM_USER_CHMEM: + InvalidateRect( hw, NULL, FALSE ); + return 0; + case WM_PAINT: + Painttable( hw, &logtable, LogWindowGetText ); + return 0; + default: break; + } + return DefMDIChildProc( hw, msg, wp, lp ); +} +//--------------------------------------------------------------------------- +int _export cdecl ODBG_Plugininit( int iOllyVersion, HWND hWindow, DWORD * features ) +{ + int i; + if( iOllyVersion < PLUGIN_VERSION ) + return -1; + + hOllyWindow = hWindow; + + bEnabled = FALSE; + + if( Createsorteddata( &(logtable.data), NULL, sizeof(LOGDATA), 64, NULL, NULL ) != 0 ) + return -1; + + if( Registerpluginclass( cLogWindowClass, NULL, hDll, LogWindowProc ) < 0 ) + { + Destroysorteddata( &(logtable.data) ); + return -1; + } + + for( i=0 ; idata.data; + for( i=0 ; idata.n ; i++ ) + { + if( strnicmp( cpName, m[i].name, SHORTLEN ) == 0 ) + return &m[i]; + } + return NULL; +} +//--------------------------------------------------------------------------- +VOID CreateBreakpoint( t_module * m, const char * cpName, DWORD * pAddress ) +{ + if( Findlabelbyname( (char *)cpName, pAddress, m->codebase, (m->codebase + m->codesize) ) != NM_NONAME ) + { + if( Setbreakpoint( *pAddress, TY_ACTIVE, 0 ) == 0 ) + return; + } + /* + *pAddress = Findimportbyname( cpName, 0, 0x80000000 ); + if( *pAddress != 0 ) + { + if( Setbreakpoint( *pAddress, TY_ACTIVE, 0 ) == 0 ) + return; + } + */ + //*pAddress = NULL; + + Addtolist( 0, 1, "%s: Failed to create breakpoint for %s.", OLLYHT_NAME, cpName ); + //RaiseException( 1, 0, 0, NULL ); +} +//--------------------------------------------------------------------------- +void DisableBreakpoints( BOOL bDisable ) +{ + int i = 0; + + while( hooks[i].cpModuleName != NULL ) + { + if( hooks[i].dwFunctionAddress != NULL ) + { + if( bDisable ) + Setbreakpoint( hooks[i].dwFunctionAddress, TY_DISABLED, 0 ); + hooks[i].dwFunctionAddress = NULL; + } + i++; + } +} +//--------------------------------------------------------------------------- +BOOL EnableBreakpoints( void ) +{ + BOOL bSuccess; + t_module * m; + int i = 0; + t_table * modtable = (t_table *)Plugingetvalue( VAL_MODULES ); + + __try + { + while( hooks[i].cpModuleName != NULL ) + { + // fix case insensitive search!!! + m = FindModule( modtable, hooks[i].cpModuleName ); + if( m == NULL ) + { + Addtolist( 0, 1, "%s: Failed to find module %s.", OLLYHT_NAME, hooks[i].cpModuleName ); + RaiseException( 2, 0, 0, NULL ); + } + CreateBreakpoint( m, hooks[i].cpFunctionName, &hooks[i].dwFunctionAddress ); + i++; + } + bSuccess = TRUE; + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + //PEXCEPTION_POINTERS x = GetExceptionInformation(); + //if( x->ExceptionCode == 1 ) + //{ + DisableBreakpoints( TRUE ); + bSuccess = FALSE; + Addtolist( 0, 1, "%s failed to enable required breakpoints.", OLLYHT_NAME ); + // Addtolist( 0, 1, " Breakpoint for %s failed.", w->ExceptionInformation[0] ); + //} + } + return bSuccess; +} +//--------------------------------------------------------------------------- +void _export cdecl ODBG_Plugindestroy( void ) +{ + //int i; + //LPLOGDATA pLogData; + bEnabled = FALSE; + + Unregisterpluginclass( cLogWindowClass ); + + /*pLogData = (LPLOGDATA)logtable.data.data; + for( i=0 ; iip == hooks[i].dwFunctionAddress ) + { + pLogData = (LPLOGDATA)malloc( sizeof(LOGDATA) ); + memset( pLogData, 0, sizeof(LOGDATA) ); + pLogData->dwAddress = dwLogIndex++; + pLogData->dwSize = 1; + + pLogData->iHookIndex = i; + pLogData->dwThreadId = pDebugEvent->dwThreadId; + + Readmemory( &pLogData->dwCallerAddress, pRegisters->r[REG_ESP], 4, MM_SILENT ); + + if( hooks[i].handle_call != NULL ) + hooks[i].handle_call( pLogData, pRegisters ); + + if( hooks[i].handle_return != NULL ) + Setbreakpoint( pLogData->dwCallerAddress, TY_ONESHOT, 0 );//TY_ONESHOT//TY_ACTIVE + + Addsorteddata( &(logtable.data), pLogData ); + bFound = TRUE; + break; + } + i++; + } + + if( !bFound ) + { + pLogData = (LPLOGDATA)logtable.data.data; + for( i=0 ; iip == pLogData[i].dwCallerAddress && !pLogData[i].bReturnMessageSet ) + { + if( hooks[pLogData[i].iHookIndex].handle_return != NULL ) + pLogData[i].bReturnMessageSet = hooks[pLogData[i].iHookIndex].handle_return( &pLogData[i], pRegisters ); + bFound = TRUE; + break; + } + } + } + + if( bFound ) + { + Go( 0, 0, STEP_RUN, 1, 1 ); + return 1; + } + + return 0; +} +//--------------------------------------------------------------------------- +