From 3336ab8365458138b57fb9dadf2e0933b797150e Mon Sep 17 00:00:00 2001 From: *Nix Fanboy <63163893+nix-enthusiast@users.noreply.github.com> Date: Sat, 19 Oct 2024 19:40:28 +0300 Subject: [PATCH] Big update! - Source code added - Readme, gitignore, banner and the license are updated --- .gitignore | 30 +- .idea/.gitignore | 8 + .idea/modules.xml | 8 + .idea/unildd.iml | 11 + .idea/vcs.xml | 6 + Cargo.toml | 26 + LICENSE | 532 +------------------ README.md | 53 +- header/unildd.h | 162 ++++++ media/banner/UniLDD Banner.png | Bin 125369 -> 123169 bytes media/banner/init | 1 - media/emblems/UniLDD-%100.png | Bin media/emblems/UniLDD-%1000.png | Bin 3449 -> 0 bytes media/emblems/UniLDD-%200.png | Bin 1371 -> 0 bytes media/emblems/UniLDD-%300.png | Bin 1615 -> 0 bytes media/emblems/UniLDD-%400.png | Bin media/emblems/UniLDD-%500.png | Bin 2066 -> 0 bytes media/emblems/UniLDD-%600.png | Bin 2301 -> 0 bytes media/emblems/UniLDD-%700.png | Bin 2508 -> 0 bytes media/emblems/UniLDD-%800.png | Bin 2816 -> 0 bytes media/emblems/UniLDD-%900.png | Bin 3104 -> 0 bytes media/emblems/init | 1 - src/archive.rs | 43 ++ src/coff.rs | 40 ++ src/debug.rs | 37 ++ src/elf.rs | 133 +++++ src/impls.rs | 152 ++++++ src/lib.rs | 286 +++++++++++ src/mach.rs | 206 ++++++++ src/pe.rs | 88 ++++ src/structs.rs | 111 ++++ src/types.rs | 897 +++++++++++++++++++++++++++++++++ 32 files changed, 2320 insertions(+), 511 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/modules.xml create mode 100644 .idea/unildd.iml create mode 100644 .idea/vcs.xml create mode 100644 Cargo.toml create mode 100644 header/unildd.h mode change 100644 => 100755 media/banner/UniLDD Banner.png delete mode 100644 media/banner/init mode change 100644 => 100755 media/emblems/UniLDD-%100.png delete mode 100644 media/emblems/UniLDD-%1000.png delete mode 100644 media/emblems/UniLDD-%200.png delete mode 100644 media/emblems/UniLDD-%300.png mode change 100644 => 100755 media/emblems/UniLDD-%400.png delete mode 100644 media/emblems/UniLDD-%500.png delete mode 100644 media/emblems/UniLDD-%600.png delete mode 100644 media/emblems/UniLDD-%700.png delete mode 100644 media/emblems/UniLDD-%800.png delete mode 100644 media/emblems/UniLDD-%900.png delete mode 100644 media/emblems/init create mode 100755 src/archive.rs create mode 100755 src/coff.rs create mode 100644 src/debug.rs create mode 100755 src/elf.rs create mode 100755 src/impls.rs create mode 100755 src/lib.rs create mode 100755 src/mach.rs create mode 100755 src/pe.rs create mode 100755 src/structs.rs create mode 100755 src/types.rs diff --git a/.gitignore b/.gitignore index 8b809f4..f457933 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,30 @@ /target -/.vscode -/tests Cargo.lock + +# This part is taken from GitHub's macOS gitignore template +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..e1980a7 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/unildd.iml b/.idea/unildd.iml new file mode 100644 index 0000000..cf84ae4 --- /dev/null +++ b/.idea/unildd.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..62a5b3c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "unildd" +version = "0.1.0" +edition = "2021" +authors = ["nix-enthusiast"] +license = "BSD-3" + +[dependencies] +# Object parsing library +goblin = { version = "0.8.2"} + +# For creating static HashMaps +phf = { version = "0.11.2", features = ["macros"] } + +# Coloring +owo-colors = "4.1.0" + +# For old Windows CMD +anstream = "0.6.15" + +[lib] +name = "unildd" +crate-type = ["cdylib", "staticlib"] + +[profile.release] +strip = true diff --git a/LICENSE b/LICENSE index 8000a6f..5b0f50e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,504 +1,28 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -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 this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -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 -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser 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 Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "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 -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY 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 -LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey 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 library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random - Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! +BSD 3-Clause License + +Copyright (c) 2024, nix-enthusiast + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md index d851e66..08bd7c4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,51 @@ -# unildd -UniLDD - A Portable Object Parsing Library +![banner](media/banner/UniLDD%20Banner.png) + +### UniLDD is designed to bring parsing objects to any language (has a C FFI library). -Currently Work In Progress +### ⭐️ Features: + - Detailed information! Some of them are: + - Name of the OS (Windows, macOS, Linux, etc.) + - File type (Core dump, shared library, executable, etc.) + - ISA type (X86_64, ARM64, RISC-V, etc.) + - CPU Subtype[^1] + - Name of the linker[^2] + - Which libraries are linked against + - Parses without loading objects. Therefore, you can even parse shady objects like malwares![^3] + - Error codes and explanations to make error handling easier. + - A Basic and built-in logger to get real-time information. + +### Installation +Basically: +- Clone the git repository: + + `git clone https://github.com/nix-enthusiast/unildd.git` + +- Go into the git repository: + + `cd unildd` + +- Compile build with cargo: + + `cargo build --release` + +- Put the output files to desired destination: + + `cp target/release/{libunildd.so,libunildd.a} /my/amazing/project/` + +- Also do not forget to copy the header file: + + `cp header/unildd.h /my/amazing/project/` + +### License +This library is licensed with [BSD-3 Clause License](https://choosealicense.com/licenses/bsd-3-clause/) + +The resources used to make this library are cited as comments in the respective source files which they were used. + +### 🎉 Thanks to: + - [m4b](https://github.com/m4b) for the [goblin](https://github.com/m4b/goblin) crate which this library gets its power by! + +[^1]: CPU subtype is a macOS-only feature which tells what kind of CPU model the code is optimized for. + +[^2]: It has some caveats. See the wiki (currently work-in-progress) for further details. + +[^3]: That doesn't mean I am liable for any damages done by this project and files you parsed. Take your own risk! diff --git a/header/unildd.h b/header/unildd.h new file mode 100644 index 0000000..b32d657 --- /dev/null +++ b/header/unildd.h @@ -0,0 +1,162 @@ +#include +#include +#include +#include + +/** + * + * An error struct for making error handling easy. + * + * ## Error Codes + * - \>0: Magic number of the unknown object (as `i64` (or `ìnt64_t)) + * - -1: Binary is corrupted + * - -2: Unknown/Bad magic number + * - -3: Error at reading and interpreting bytes + * - -4: I/O Error at parsing the object + * - -5: Buffer is too short to hold + * - -6: Unknown error[^1] + * - -7: Unimplemented executable format + * + * [^1]: All errors thrown by goblin crate and my code are covered. Because of matching goblin's [`Error`](goblin::error::Error) is non-exhaustive, I included non-exhaustive path too. + * + */ +typedef struct ParsingError { + int64_t code; + char *explanation; +} ParsingError; + +/** + * A C-compatible vector for `Vec`. + */ +typedef struct CharVec { + uintptr_t capacity; + uintptr_t length; + char **vec; +} CharVec; + +/** + * + * A struct contains detailed information about the object. + * + * It contains some information even the object is an erroneous one to make error handling more verbose. + * + * If the error occurs on parsing: + * - A file: `file_name` and `member_name` + * - A Muti Architecture Mach-O file: `file_name`, `member_name` and `executable_format` + * - An archive: `file_name`, `member_name` and `file_type` + * + * fields will be filled correctly and the rest will be: + * - null (the fields which are string) + * - blank (`member_name` and `libraries`) + * - `false` (`is_64` and `is_stripped`). + * + */ +typedef struct ULDDObj { + /** + * The name of the object. + * + * Objects inside Muti Architecture Mach-O files will be named as "n. file" due to they don't have file names. + */ + char *file_name; + /** + * The location of objects in recursive files. + * + * This field is empty if the object is not in a recursive file (Like: Archives and Muti Architecture Mach-O files). + * + * The names in the vector is sorted as outer to inner. + */ + struct CharVec member_name; + /** + * The type of the executable format of the object. + */ + char *executable_format; + /** + * The field is true if the object is 64 bit otherwise it is 32 bit or the object is an erroneous one. + */ + bool is_64; + /** + * The name of the OS it was compiled for. + */ + char *os_type; + /** + * The type of the object. + */ + char *file_type; + /** + * The field is true if the object was stripped from debug symbols otherwise it is not stripped or the object is an erroneous one . + */ + bool is_stripped; + /** + * The ISA (CPU Architecture) the object compiled for. + */ + char *cpu_type; + /** + * The specific CPU model the object compiled for. + * + * macOS only field. It is null pointer in other executable formats. + */ + char *cpu_subtype; + /** + * The name/version of the linker. + * + * ELF/PE only field. It is null pointer in other executable formats. + * + * It returns the version of the linker in PE files. + */ + char *interpreter; + /** + * A vector of libraries linked against the object. + * + * It is blank in COFF files because they are mostly PE object files therefore they don't have linked libraries against them. + */ + struct CharVec libraries; +} ULDDObj; + +/** + * A struct packs (empty or filled) error and (successfully or not) read object. + */ +typedef struct ULDDObjResult { + struct ParsingError error; + struct ULDDObj obj; +} ULDDObjResult; + +/** + * A C-compatible vector for [`ULDDObjResult`]. + */ +typedef struct ULDDObjResultVec { + uintptr_t capacity; + uintptr_t length; + struct ULDDObjResult *vec; +} ULDDObjResultVec; + +/** + * + * Parses the given buffer and returns a vector of parsed binaries. + * + * # Safety + * + * This function is null pointer-safe. If the file name is an invalid UTF-8 string and/or buffer pointer is a null pointer it will panic. + * + * Since the function returns a [`ULDDObjResultVec`] created by rust it has to be [deallocated](free_obj) by rust if it is done by other languages errors may occur. + * + */ +struct ULDDObjResultVec read_obj(const char *file_name, + const uint8_t *buffer, + uintptr_t buffer_size, + bool debugging); + +/** + * + * # Safety + * + * This function is designed for deallocating [`ULDDObjResultVec`] created by rust. Trying to deallocating [`ULDDObjResultVec`] created by other languages may result with errors. + * + * It is null pointer-safe. + * + * ## Error codes: + * - 0: No errors + * - 1: `vec` field of [`ULDDObjResultVec`] is a null pointer + * + */ +uint8_t free_obj(struct ULDDObjResultVec obj, + bool debugging); diff --git a/media/banner/UniLDD Banner.png b/media/banner/UniLDD Banner.png old mode 100644 new mode 100755 index d48035902bc41a9d2c1224cfd7f0e9da13ea5743..e1ccc6268928e9a1e96eb5ff94cdb5f4613be991 GIT binary patch delta 3524 zcmcInc~nzZ8h-2n2gdbX*XT1_W6|L6!&QjM>f9Iui}Hm}c^mEfz!Z$0!gp>@6P zq2sw}DcdU1HxrS++9q6bTi>q!(o1=rx+IY)aYw3@UvtsmJp2nTS{F$NGT$OzUs(tyd9i)83c~VvCBZ z6qcU>w*^x-AqeUpb#R{tBXE0=A*XuQntQRfrM}I>A8*AI@`na`6U0YOGxnEc zr_*nXikQ!OJ&6iqvOk6>0i|B1^2O9|GcmK*qwJnFz$@j2(zN8OojDZ#23NMnJ3!-B zJk=DDyvMAzRUDU@DacY7ahv0C`U}r+ExySvuKIIxz{!XM1T6Vg zPNLn|(D3N9nM{*4+XzTv^a0OeTrV>gMiG4z856Mux|krKKo%_kt*yn^X~ z4A>d*Kx4r@Kb!*VQIxPvyffQCjx0svPrNIA>5!xdl2ao8Qq%!4fil0Q-Z z2#ALoc^8)`kFKa7!PvdJ&>{5;s`MNywkda&7qGe4e|)YPPp`E^eE)rR!-B}OcONTR zx2kvTa9ToAJ->9vhZkm^_XY@BDrG&1W)<9T?E-f%nA@(DQbysq8vqTMjTD#-_xCx8 zoXe#W?=S@QecDaonyFL^rXA3 zLxoXPIQO+!74~V9uv`tv7(g!Ac=uzUtG_^nweByy&j+dwp%2auF`FB z(bv@SX8SdEr74(2)b;ViZjtA+PZ2@Xl$kqfJTNAzhp7*pV7u4>l6qXu&#)@^;f3XT z<+UMVLQ2_eV$%;HJTwG20e$)u?mqG1mV(lXeb%)?n?IxtcY|-jtBeP(Amzn0m3FP1 z4kbj8J&6#>#NkBkgLn`fV>MFcl4ye;Q%Frignu!TRAP@%X(~hR3elN9jtClo@Ur~ToBAqOJ8J0bsRhfE!u0l%#aFa(o0gInF@R5>r z+ET5B&gGVv=uagko!55ORc=y^+lZ8C#e@C%$;2jo`uVr+Ue3-w-U9@iw6WTr0~VvU z65hU`+qI`h5%ug`JvkqT)9>+)JXN9ISlZ_&jXgn|$74AHXFx0Rd*EY=`s+pr8XEd} zUql`a!~?Y)@ZiAk25j|tKxH*Q?v1^jFD%C=RZij~Y5w(TuX#O#!tM?gIU;KSg-MiI(xOeVlZm zBAWv1jv%Whs9Zn;Ot6a1ta>a75?+)Rx(r16TaY%~wb_@MmlVANHM4B!)4^A@Pju#t zSCkYF@`&Xf$&FOFR2Pg_u`N*L+htG030-HbQ)7t;L)w-^M$A*=mnSP3ZRIpv^R!s{{o!WD)4^QwkkQz2T~m8G zUFj2LoAH6Z`}OkvKQ7nRcH51#H^=47GkzjyQ$^p`hUST%bNcQ!=-jUtmyG_d%^#27 zk|?Qwr=r?Ol6r({?5Bw4ve3X%}fNzsXjP3EYwTt6H%MbxXqX8G)2_8w*JV8Z2ghH z>AK{CqD+nJu;#f`>_hW{FK|@PYFvW|PnLCv;54L7frb`iy{fdo$`E`-+u@7wEv)JO zM##`L7rMHPrz_~;2z*@{lQGb0)FJDS%%b?$6!o4uga~k zoC-snT=1YMW86e~&p?pS9a!^xDmJrm!Pj#2IXimBt5iMQv_kl8qS$sh2iYorlWlGl z{sl#ioZ!`p2C62f|8tX*i^WJ9+@Vs)vgw|hpdUsIRr_vWf{Gxv7od0Wb#3%_gIDWZ z@sFPt!Rmj&u$`PhNcLPvkqf-ttQ#6P#4+xyEixRFEc+BGxPL!a3a(f2rzQ7i$s}Z% z!OyeSoXriSmT z$xUrBs3~G*QWO@Eo|9Omjy}-kI{G~an1k{RtRVFSCdntm8xSQZI`MC?N}2-FdbQq! zN~;_}EIAnYralmG9XY8u3Y8%LsRy%vfsM=Oh*lP7yp!N9h%~-8fd3;ASYcQbQy-Xd t$um|FrYl_vUIh0HU7Q`gu-*WDO$+Z`v4;jUqEWbtxUAmjoVSW~_%{lz9Pa=C delta 6828 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 D=L~@# diff --git a/media/emblems/UniLDD-%200.png b/media/emblems/UniLDD-%200.png deleted file mode 100644 index 4d8c74543a15e10c8e59bd231e97793e97183182..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1371 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

kCsXMsNCHFjHH~Uh#@zUAFj`yYdKyK%Rcwm0 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 M{DOTOe4=jr7kP&87XSbN diff --git a/media/emblems/UniLDD-%800.png b/media/emblems/UniLDD-%800.png deleted file mode 100644 index 446170391faf0560adf2983db3db29d2629cf250..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2816 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 diff --git a/media/emblems/init b/media/emblems/init deleted file mode 100644 index 8b13789..0000000 --- a/media/emblems/init +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/archive.rs b/src/archive.rs new file mode 100755 index 0000000..8d45f0d --- /dev/null +++ b/src/archive.rs @@ -0,0 +1,43 @@ +use crate::{ + debug::{debug_objects, find_error_type, merge_members}, + parse_objects, + structs::{CharVec, Debugging, ParsingError, StringPtr, ULDDObj, ULDDObjResult}, +}; +use goblin::archive::Archive; + +pub(crate) fn parse_archive<'a>( + file_name: &'a str, + buffer: &'a [u8], + archive: Archive<'a>, + member_names: &mut Vec<&'a str>, + objects: &mut Vec, + debugging: bool, +) { + for member in archive.members() { + member_names.push(file_name); + let member_buffer = match archive.extract(member, buffer) { + Ok(buf) => buf, + Err(error) => { + Debugging::Error(format!("Error while extracting the bytes of the member named '{}' from buffer of the file named '{}'{}\nDetails:\n{}", + member, + file_name, + merge_members(member_names), + error)).print(debugging); + return objects.push(ULDDObjResult { + error: ParsingError { + code: find_error_type(&error), + explanation: StringPtr::from(error.to_string()).0, + }, + obj: ULDDObj { + file_name: StringPtr::from(file_name).0, + member_name: CharVec::from(member_names), + file_type: StringPtr::from("Archive").0, + ..Default::default() + }, + }); + } + }; + debug_objects(file_name, member_names, "an archive file", debugging); + parse_objects(member, member_buffer, member_names, objects, debugging); + } +} diff --git a/src/coff.rs b/src/coff.rs new file mode 100755 index 0000000..701645c --- /dev/null +++ b/src/coff.rs @@ -0,0 +1,40 @@ +use crate::{ + debug::debug_objects, + structs::{CharVec, ParsingError, StringPtr, ULDDObj, ULDDObjResult}, + types::PE_ARCH, +}; +use goblin::pe::{ + characteristic::{IMAGE_FILE_32BIT_MACHINE, IMAGE_FILE_DEBUG_STRIPPED}, + Coff, +}; +use std::ptr::null_mut; + +pub(crate) fn parse_coff( + file_name: &str, + coff: Coff, + member_names: &mut Vec<&str>, + debugging: bool, +) -> ULDDObjResult { + // Thanks to developers of goblin for making me to find out that I can "bitwise and" characteristics and wanted characteristics to find out if the COFF file has the one we want + let is_64 = coff.header.characteristics & IMAGE_FILE_32BIT_MACHINE != IMAGE_FILE_32BIT_MACHINE; + let is_stripped = + coff.header.characteristics & IMAGE_FILE_DEBUG_STRIPPED == IMAGE_FILE_DEBUG_STRIPPED; + let cpu_type = StringPtr::from(PE_ARCH.get(&coff.header.machine)).0; + debug_objects(file_name, member_names, "a COFF binary", debugging); + ULDDObjResult { + error: ParsingError::default(), + obj: ULDDObj { + file_name: StringPtr::from(file_name).0, + member_name: CharVec::from(member_names), + executable_format: StringPtr::from("COFF").0, + is_64, + os_type: StringPtr::from("Windows").0, + file_type: StringPtr::from("Windows object file").0, + is_stripped, + cpu_type, + cpu_subtype: null_mut(), + interpreter: null_mut(), + libraries: CharVec::default(), + }, + } +} diff --git a/src/debug.rs b/src/debug.rs new file mode 100644 index 0000000..0e00ab0 --- /dev/null +++ b/src/debug.rs @@ -0,0 +1,37 @@ +use goblin::error::Error as ObjectError; + +use crate::structs::Debugging; + +pub(crate) fn find_error_type(error: &ObjectError) -> i64 { + match error { + ObjectError::Malformed(_) => -1, + ObjectError::BadMagic(_) => -2, + ObjectError::Scroll(_) => -3, + ObjectError::BufferTooShort(_, _) => -4, + ObjectError::IO(_) => -5, + _ => -6, + } +} + +pub(crate) fn merge_members(member_names: &mut [&str]) -> String { + if !member_names.is_empty() { + format!(" (Member of: {})", member_names.join(" -> ")) + } else { + String::new() + } +} + +pub(crate) fn debug_objects( + file_name: &str, + member_names: &mut [&str], + object_name: &str, + debugging: bool, +) { + Debugging::Info(format!( + "The binary named '{}'{} is {}", + file_name, + merge_members(member_names), + object_name + )) + .print(debugging) +} diff --git a/src/elf.rs b/src/elf.rs new file mode 100755 index 0000000..51e9f38 --- /dev/null +++ b/src/elf.rs @@ -0,0 +1,133 @@ +use std::ptr::null_mut; + +use crate::{ + debug::debug_objects, + structs::{CharVec, ParsingError, StringPtr, ULDDObj, ULDDObjResult}, + types::{ElfFileType, ElfOS, E_MACHINE, E_TYPE}, +}; +use goblin::elf::Elf; + +fn find_os_from_strtab_elf(elf: &Elf<'_>, pat: &[&str]) -> bool { + [ + elf.strtab.to_vec().unwrap_or(vec![""]), + elf.shdr_strtab.to_vec().unwrap_or(vec![""]), + elf.dynstrtab.to_vec().unwrap_or(vec![""]), + ] + .iter() + .flatten() + .any(|s| pat.iter().any(|i| s.to_lowercase().contains(i))) +} + +fn find_os_elf(elf: &Elf<'_>, os_abi: u8) -> (ElfOS, *mut i8) { + let os = { + match os_abi { + 0x00 => match true { + _ if find_os_from_strtab_elf(elf, &["fbsd"]) => ElfOS::FreeBSD, + _ if find_os_from_strtab_elf(elf, &["openbsd"]) => ElfOS::OpenBSD, + _ if find_os_from_strtab_elf(elf, &["musl", "glibc", "linux"]) => ElfOS::Linux, + _ if find_os_from_strtab_elf(elf, &["android"]) => ElfOS::Android, + _ if find_os_from_strtab_elf(elf, &["netbsd"]) => ElfOS::NetBSD, + _ if find_os_from_strtab_elf(elf, &["solaris"]) => ElfOS::Solaris, + _ if find_os_from_strtab_elf(elf, &["illumos"]) => ElfOS::Illumos, + _ if elf.interpreter.is_some_and(|v| v.contains("Loader.so")) => ElfOS::SerenityOS, + _ => return (ElfOS::Undefined, null_mut()), + }, + 0x01 => ElfOS::HPUX, + 0x02 => ElfOS::NetBSD, + 0x03 => ElfOS::Linux, + 0x04 => ElfOS::GNUHurd, + 0x06 => { + if find_os_from_strtab_elf(elf, &["illumos"]) { + ElfOS::Illumos + } else { + ElfOS::Solaris + } + } + 0x07 => ElfOS::AIXMonterey, + 0x08 => ElfOS::IRIX, + 0x09 => ElfOS::FreeBSD, + 0x10 => ElfOS::FenixOS, + 0x11 => ElfOS::CloudABI, + 0x12 => ElfOS::OpenVOS, + 0x0A => ElfOS::Tru64, + 0x0B => ElfOS::NovellModesto, + 0x0C => ElfOS::OpenBSD, + 0x0D => ElfOS::OpenVMS, + 0x0E => ElfOS::NonStopKernel, + 0x0F => ElfOS::AROS, + _ => return (ElfOS::Undefined, null_mut()), + } + }; + + (os, StringPtr::from(os.to_string()).0) +} + +fn find_linux_vdso(e_machine: u16, bit_type: bool) -> Option<&'static str> { + match e_machine { + 0x3E => Some("linux-vdso.so.1"), + 0x03 => Some("linux-vdso.so.1"), + 0x2A => Some("linux-gate.so.1"), + 0x16 => { + if bit_type { + Some("linux-vdso64.so.1") + } else { + Some("linux-vdso32.so.1") + } + } + 0xF3 => Some("linux-vdso.so.1"), + 0x15 => Some("linux-vdso64.so.1"), + 0x14 => Some("linux-vdso32.so.1"), + 0x08 => Some("linux-vdso.so.1"), + 0x32 => Some("linux-gate.so.1"), + 0x28 => Some("linux-vdso.so.1"), + 0xB7 => Some("linux-vdso.so.1"), + _ => None, + } +} + +fn convert_libraries_into_char_vec(elf: &mut Elf, os_abi: u8) -> CharVec { + let mut vector = std::mem::take(&mut elf.libraries); + if let (Some(vdso), ElfOS::Linux) = ( + find_linux_vdso(elf.header.e_machine, elf.is_64), + find_os_elf(elf, os_abi).0, + ) { + vector.push(vdso) + } + + CharVec::from(vector) +} + +pub(crate) fn parse_elf( + file_name: &str, + elf: Elf, + os_abi: u8, + member_names: &mut Vec<&str>, + debugging: bool, +) -> ULDDObjResult { + let mut elf = elf; + let cpu_type = StringPtr::from(E_MACHINE.get(&elf.header.e_machine)).0; + let file_type = match E_TYPE.get(&elf.header.e_type) { + _ if elf.header.e_type == 0x03 && elf.interpreter.is_some() => { + StringPtr::from(ElfFileType::Executable.to_string()).0 + } + rest => StringPtr::from(rest).0, + }; + let interpreter = StringPtr::from(elf.interpreter).0; + debug_objects(file_name, member_names, "an ELF binary", debugging); + ULDDObjResult { + error: ParsingError::default(), + obj: ULDDObj { + file_name: StringPtr::from(file_name).0, + member_name: CharVec::from(member_names), + executable_format: StringPtr::from("ELF").0, + is_64: elf.is_64, + os_type: find_os_elf(&elf, os_abi).1, + file_type, + is_stripped: elf.syms.is_empty(), + cpu_type, + cpu_subtype: null_mut(), + interpreter, + libraries: convert_libraries_into_char_vec(&mut elf, os_abi), + }, + } +} diff --git a/src/impls.rs b/src/impls.rs new file mode 100755 index 0000000..9e4b11f --- /dev/null +++ b/src/impls.rs @@ -0,0 +1,152 @@ +use crate::{ + structs::{CharVec, Debugging, ParsingError, StringPtr, ULDDObj}, + ULDDObjResult, ULDDObjResultVec, +}; +use anstream::{eprintln as a_eprintln, println as a_println}; +use owo_colors::OwoColorize; +use std::{fmt::Display, mem::ManuallyDrop, ptr::null_mut, ffi::{c_char, CString}}; + +impl From> for CharVec { + fn from(value: Vec<*mut c_char>) -> Self { + CharVec { + capacity: value.capacity(), + length: value.len(), + vec: if value.is_empty() { + null_mut() + } else { + ManuallyDrop::new(value).as_mut_ptr() + }, + } + } +} + +impl From> for ULDDObjResultVec { + fn from(value: Vec) -> Self { + ULDDObjResultVec { + capacity: value.capacity(), + length: value.len(), + vec: if value.is_empty() { + null_mut() + } else { + ManuallyDrop::new(value).as_mut_ptr() + }, + } + } +} + +impl Default for CharVec { + fn default() -> Self { + Self { + capacity: 0, + length: 0, + vec: null_mut(), + } + } +} + +impl From> for CharVec { + fn from(val: Vec<&str>) -> Self { + let vector: Vec<*mut c_char> = val + .into_iter() + .map(|item| unsafe { + CString::from_vec_unchecked(item.to_string().into_bytes()).into_raw() + }) + .collect(); + + CharVec::from(vector) + } +} + +impl From<&mut Vec<&str>> for CharVec { + fn from(val: &mut Vec<&str>) -> Self { + let vector: Vec<*mut c_char> = std::mem::take(val) + .into_iter() + .map(|item| unsafe { + CString::from_vec_unchecked(item.to_string().into_bytes()).into_raw() + }) + .collect(); + + CharVec::from(vector) + } +} + +impl From for StringPtr { + fn from(value: String) -> Self { + let mut value = value; + value.push('\0'); + let c_string = match CString::from_vec_with_nul(value.into_bytes()) { + Ok(string) => string, + Err(error) => { + Debugging::Fatal("converting the string into a C string".to_owned()).print(true); + panic!("{}", error) + } + }; + StringPtr(c_string.into_raw()) + } +} + +impl From<&str> for StringPtr { + fn from(value: &str) -> Self { + StringPtr::from(value.to_owned()) + } +} + +impl From> for StringPtr +where + T: Display, +{ + fn from(value: Option) -> Self { + let Some(t) = value else { + return StringPtr(null_mut()); + }; + StringPtr::from(t.to_string()) + } +} + +impl Default for ParsingError { + fn default() -> Self { + Self { + code: 0, + explanation: null_mut(), + } + } +} + +impl Default for ULDDObj { + fn default() -> Self { + Self { + file_name: null_mut(), + member_name: Default::default(), + executable_format: null_mut(), + is_64: false, + os_type: null_mut(), + file_type: null_mut(), + is_stripped: false, + cpu_type: null_mut(), + cpu_subtype: null_mut(), + interpreter: null_mut(), + libraries: Default::default(), + } + } +} + +impl Debugging { + pub(crate) fn print(self, debugging: bool) { + if debugging { + match self { + Debugging::Info(msg) => { + a_println!("{} {}", "[INFO]".yellow().bold(), msg); + } + Debugging::Affirmative(msg) => { + a_println!("{} {}", "[OK]".green().bold(), msg); + } + Debugging::Error(msg) => { + a_eprintln!("{} {}", "[ERROR]".red().bold(), msg); + } + Debugging::Fatal(msg) => { + a_eprintln!("{} Library got a fatal error while {}. Panic function will halt the library and provide a stacktrace.", "[FATAL]".red().bold(), msg); + } + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100755 index 0000000..c8ee01a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,286 @@ +#![doc( + html_favicon_url = "https://github.com/nix-enthusiast/unildd/blob/main/media/emblems/UniLDD-%25100.png?raw=true" +)] +#![doc( + html_logo_url = "https://github.com/nix-enthusiast/unildd/blob/main/media/emblems/UniLDD-%25400.png?raw=true" +)] +#![doc(html_playground_url = "https://play.rust-lang.org/")] +//! +//! ![banner](https://github.com/nix-enthusiast/unildd/blob/main/media/banner/UniLDD%20Banner.png?raw=true) +//! +//! ### UniLDD is designed for bringing parsing objects to any language (has a C FFI library). +//! +//! ### ⭐️ Features: +//! - Detailed information! Some of them are: +//! - Name of the OS +//! - File type (Core dump, shared library, executable, etc.) +//! - ISA type (X86_64, Aarch64, RISC-V, etc.) +//! - CPU Subtype[^1] +//! - Name of the linker[^2] +//! - Which libraries are linked +//! - Parses without loading objects. Therefore, you can even parse shady objects like malwares![^3] +//! - Error codes and explanations to make error handling easier. +//! - A Basic and built-in logger to get real-time information. +//! +//! [^1]: CPU subtype is a macOS-only feature which tells what kind of CPU model the code is optimized for. +//! +//! [^2]: It has some caveats. See [`ULDDObj`] for further details. +//! +//! [^3]: That doesn't mean I am liable for any damages done by this project and files you parsed. Take your own risk! +//! +use archive::parse_archive; +use coff::parse_coff; +use debug::{find_error_type, merge_members}; +use elf::parse_elf; +use goblin::Object; +use mach::parse_mach; +use owo_colors::OwoColorize; +use pe::parse_pe; +use std::ffi::{c_char, CStr, CString}; +use structs::{ + CharVec, Debugging, ParsingError, StringPtr, ULDDObj, ULDDObjResult, ULDDObjResultVec, +}; +#[doc(hidden)] +pub mod archive; +#[doc(hidden)] +pub mod coff; +#[doc(hidden)] +pub mod debug; +#[doc(hidden)] +pub mod elf; +#[doc(hidden)] +pub mod impls; +#[doc(hidden)] +pub mod mach; +#[doc(hidden)] +pub mod pe; + +pub mod structs; +pub mod types; + +fn parse_objects<'a>( + file_name: &'a str, + buffer: &'a [u8], + member_names: &mut Vec<&'a str>, + objects: &mut Vec, + debugging: bool, +) { + match Object::parse(buffer) { + Ok(Object::Archive(archive)) => { + parse_archive(file_name, buffer, archive, member_names, objects, debugging) + } + + Ok(Object::Mach(mach)) => { + parse_mach(file_name, buffer, mach, member_names, objects, debugging) + } + + Ok(Object::Elf(elf)) => { + objects.push(parse_elf( + file_name, + elf, + buffer[0x7], + member_names, + debugging, + )); + } + + Ok(Object::PE(pe)) => objects.push(parse_pe(file_name, pe, member_names, debugging)), + + Ok(Object::COFF(coff)) => { + objects.push(parse_coff(file_name, coff, member_names, debugging)); + } + + Ok(Object::Unknown(magic_number)) => { + let msg = format!( + "The binary named '{}'{} has a unknown magic number (in big-endian): {}", + file_name, + merge_members(member_names), + format!("{:02X?}", magic_number.to_be_bytes()).replace(['[', ']', ','], "") + ); + Debugging::Error(msg.to_owned()).print(debugging); + objects.push(ULDDObjResult { + error: ParsingError { + code: magic_number as i64, + explanation: StringPtr::from(msg).0, + }, + obj: ULDDObj { + file_name: StringPtr::from(file_name).0, + member_name: CharVec::from(member_names), + ..Default::default() + }, + }) + } + + Ok(_) => { + let msg = format!( + "The executable format of the file named '{}'{} is not yet implemented", + file_name, + merge_members(member_names), + ); + Debugging::Error(msg.to_string()).print(debugging); + Debugging::Info(format!( + "First 16 bytes of the file named '{}' are {}", + file_name, + format!("{:02X?}", &buffer[0..17]).replace(['[', ']', ','], "") + )) + .print(debugging); + + objects.push(ULDDObjResult { + error: ParsingError { + code: -7, + explanation: StringPtr::from(msg).0, + }, + obj: ULDDObj { + file_name: StringPtr::from(file_name).0, + member_name: CharVec::from(member_names), + ..Default::default() + }, + }) + } + + Err(error) => { + Debugging::Error(format!( + "Error while parsing the bytes of the given file named '{}'{}\nDetails:\n{}", + file_name, + merge_members(member_names), + error + )) + .print(debugging); + + objects.push(ULDDObjResult { + error: ParsingError { + code: find_error_type(&error), + explanation: StringPtr::from(error.to_string()).0, + }, + obj: ULDDObj { + file_name: StringPtr::from(file_name).0, + member_name: CharVec::from(member_names), + ..Default::default() + }, + }) + } + }; +} + +/// +/// Parses the given buffer and returns a vector of parsed binaries. +/// +/// # Safety +/// +/// This function is null pointer-safe. If the file name is an invalid UTF-8 string and/or buffer pointer is a null pointer it will panic. +/// +/// Since the function returns a [`ULDDObjResultVec`] created by rust it has to be [deallocated](free_obj) by rust if it is done by other languages errors may occur. +/// +#[no_mangle] +pub unsafe extern "C" fn read_obj( + file_name: *const c_char, + buffer: *const u8, + buffer_size: usize, + debugging: bool, +) -> ULDDObjResultVec { + let (buf, f_name) = unsafe { + let s = match CStr::from_ptr(file_name).to_str() { + Ok(string_slice) => string_slice, + Err(error) => { + Debugging::Fatal("converting the C string to a &str".to_owned()).print(true); + panic!("{}", error) + } + }; + let b = std::slice::from_raw_parts(buffer, buffer_size); + (b, s) + }; + + let mut objects = vec![]; + parse_objects(f_name, buf, &mut vec![], &mut objects, debugging); + let (total, success, failed): (usize, usize, usize) = { + let t = objects.len(); + let (mut s, mut f) = (0, 0); + objects.iter().for_each(|o| { + if o.error.code != 0 { + f += 1; + } else { + s += 1 + } + }); + (t, s, f) + }; + + Debugging::Affirmative(format!( + "{} binaries from the file(s) are parsed. Success/Fail rate of parsing(s) is {}/{}", + total, + success.green(), + failed.red() + )) + .print(debugging); + + ULDDObjResultVec::from(objects) +} + +unsafe fn drop_c_string(ptr: *mut i8) { + if !ptr.is_null() { + let _ = CString::from_raw(ptr); + } +} + +/// +/// # Safety +/// +/// This function is designed for deallocating [`ULDDObjResultVec`] created by rust. Trying to deallocating [`ULDDObjResultVec`] created by other languages may result with errors. +/// +/// It is null pointer-safe. +/// +/// ## Error codes: +/// - 0: No errors +/// - 1: `vec` field of [`ULDDObjResultVec`] is a null pointer +/// +#[no_mangle] +pub unsafe extern "C" fn free_obj(obj: ULDDObjResultVec, debugging: bool) -> u8 { + if obj.vec.is_null() { + Debugging::Error("Given object vector is invalid".to_owned()).print(debugging); + + Debugging::Error("Deallocation(s) is failed".to_owned()).print(debugging); + + return 1; + }; + + let object_vector = Vec::from_raw_parts(obj.vec, obj.length, obj.capacity); + for (index, object) in object_vector.into_iter().enumerate() { + Debugging::Info(format!("{}. object is being deallocated", index + 1)).print(debugging); + + let o = object.obj; + drop_c_string(object.error.explanation); + drop_c_string(o.file_name); + drop_c_string(o.executable_format); + drop_c_string(o.os_type); + drop_c_string(o.file_type); + drop_c_string(o.cpu_type); + drop_c_string(o.cpu_subtype); + drop_c_string(o.interpreter); + if !o.member_name.vec.is_null() { + let member_names = Vec::from_raw_parts( + o.member_name.vec, + o.member_name.length, + o.member_name.capacity, + ); + for name in member_names { + drop_c_string(name) + } + }; + if !o.libraries.vec.is_null() { + let libraries = + Vec::from_raw_parts(o.libraries.vec, o.libraries.length, o.libraries.capacity); + for library in libraries { + drop_c_string(library) + } + }; + Debugging::Affirmative(format!("{}. object is deallocated", index + 1)).print(debugging); + } + + Debugging::Affirmative(format!( + "Deallocation(s) is successful. {} object(s) is freed.", + obj.length + )) + .print(debugging); + + 0 +} diff --git a/src/mach.rs b/src/mach.rs new file mode 100755 index 0000000..ec57d7b --- /dev/null +++ b/src/mach.rs @@ -0,0 +1,206 @@ +use crate::{ + debug::{debug_objects, find_error_type, merge_members}, + structs::{CharVec, Debugging, ParsingError, StringPtr, ULDDObj, ULDDObjResult}, + types::{ + MachOCpuType, MachOOs, MACH_O_ARM_CPU_SUBTYPE, MACH_O_CPUTYPE, MACH_O_FILE_TYPE, + MACH_O_X86_CPU_SUBTYPE, + }, +}; +use goblin::mach::{load_command::CommandVariant::BuildVersion, Mach, MachO}; +use std::ptr::null_mut; + +fn find_os_mach(mach: &MachO<'_>) -> *mut i8 { + for lc in &mach.load_commands { + if let BuildVersion(build_version) = lc.command { + let os = match build_version.platform { + 0x01 => MachOOs::MacOS, + 0x02 => MachOOs::IOS, + 0x03 => MachOOs::AppleTVBox, + 0x04 => MachOOs::AppleWatch, + 0x05 => MachOOs::BridgeOS, + 0x06 => MachOOs::MacCatalyst, + 0x07 => MachOOs::IOSSimulator, + 0x08 => MachOOs::AppleTVSimulator, + 0x09 => MachOOs::AppleWatchSimulator, + 0x0A => MachOOs::DriverKit, + 0x0B => MachOOs::AppleVisionPro, + 0x0C => MachOOs::AppleVisionProSimulator, + _ => return null_mut(), + }; + return StringPtr::from(os.to_string()).0; + } + } + + null_mut() +} + +/* +I will leave it there because I may use later + +fn decode_further(mach: &MachO<'_>) { + for lc in &mach.load_commands { + if let BuildVersion(build_version) = lc.command { + let os = match build_version.platform { + 0x01 => MachOOs::MacOS, + 0x02 => MachOOs::IOS, + 0x03 => MachOOs::AppleTVBox, + 0x04 => MachOOs::AppleWatch, + 0x05 => MachOOs::BridgeOS, + 0x06 => MachOOs::MacCatalyst, + 0x07 => MachOOs::IOSSimulator, + 0x08 => MachOOs::AppleTVSimulator, + 0x09 => MachOOs::AppleWatchSimulator, + 0x0A => MachOOs::DriverKit, + 0x0B => MachOOs::AppleVisionPro, + 0x0C => MachOOs::AppleVisionProSimulator, + _ => MachOOs::Undefined, + }; + let os_ver = { + let [_, x, y, z] = build_version.minos.to_be_bytes(); + format!("{x}.{y}.{z}") + }; + let sdk_ver = { + let [_, x, y, z] = build_version.sdk.to_be_bytes(); + format!("{x}.{y}.{z}") + }; + let tool_type = match build_version.ntools { + 0x1 => "Clang", + 0x2 => "Swift", + 0x3 => "Linked with ld", + _ => "Unknown" + }; + } + } +} +*/ + +pub(crate) fn parse_mach<'a>( + file_name: &'a str, + buffer: &[u8], + mach: Mach, + member_names: &mut Vec<&'a str>, + objects: &mut Vec, + debugging: bool, +) { + match mach { + Mach::Fat(fat) => { + debug_objects( + file_name, + member_names, + "a multi architecture Mach-O", + debugging, + ); + let fat_arches = match fat.arches() { + Ok(arches) => arches, + Err(error) => { + Debugging::Error(format!("Error while reading the multi architecture Mach-O binary named '{}'{}\nDetails:\n{}", + file_name, + merge_members(member_names), + error)).print(debugging); + + return objects.push(ULDDObjResult { + error: ParsingError { + code: find_error_type(&error), + explanation: StringPtr::from(error.to_string()).0, + }, + obj: ULDDObj { + file_name: StringPtr::from(file_name).0, + member_name: CharVec::from(member_names), + executable_format: StringPtr::from("Mach-O").0, + ..Default::default() + }, + }); + } + }; + + for (index, arch) in fat_arches.iter().enumerate() { + match MachO::parse(buffer, arch.offset as usize) { + Ok(mach_o) => { + member_names.push(file_name); + objects.push(parse_mach_o( + &format!("{}. file", index + 1), + member_names, + mach_o, + debugging, + )) + } + Err(error) => { + Debugging::Error(format!("Error while processing the multi architecture Mach-O binary named '{}'{}\nDetails:\n{}", file_name, merge_members(member_names), + error)).print(debugging); + objects.push(ULDDObjResult { + error: ParsingError { + code: find_error_type(&error), + explanation: StringPtr::from(error.to_string()).0, + }, + obj: ULDDObj { + file_name: StringPtr::from(file_name).0, + member_name: CharVec::from(member_names.clone()), + executable_format: StringPtr::from("Mach-O").0, + ..Default::default() + }, + }) + } + } + } + } + Mach::Binary(binary) => { + objects.push(parse_mach_o(file_name, member_names, binary, debugging)) + } + } +} + +fn parse_mach_o( + file_name: &str, + member_names: &mut Vec<&str>, + mach_o: MachO, + debugging: bool, +) -> ULDDObjResult { + let mut mach_o = mach_o; + let file_type = StringPtr::from(MACH_O_FILE_TYPE.get(&mach_o.header.filetype)).0; + let (cpu_type, cpu_subtype) = { + if let Some(mach_o_cpu_type) = MACH_O_CPUTYPE.get(&mach_o.header.cputype) { + let mach_o_cpu_subtype = { + match mach_o_cpu_type { + MachOCpuType::ARM | MachOCpuType::ARM64 => { + StringPtr::from(MACH_O_ARM_CPU_SUBTYPE.get(&mach_o.header.cpusubtype)).0 + } + MachOCpuType::X86 | MachOCpuType::X86_64 => { + StringPtr::from(MACH_O_X86_CPU_SUBTYPE.get(&mach_o.header.cpusubtype)).0 + } + _ => null_mut(), + } + }; + ( + StringPtr::from(mach_o_cpu_type.to_string()).0, + mach_o_cpu_subtype, + ) + } else { + (null_mut(), null_mut()) + } + }; + + let is_stripped = !mach_o + .symbols + .as_ref() + .is_some_and(|v| v.iter().any(|s| s.is_ok_and(|(x, _)| x.contains("debug")))); + + mach_o.libs.retain(|lib| lib != &"self"); + debug_objects(file_name, member_names, "a Mach-O binary", debugging); + + ULDDObjResult { + error: ParsingError::default(), + obj: ULDDObj { + file_name: StringPtr::from(file_name).0, + member_name: CharVec::from(member_names), + executable_format: StringPtr::from("Mach-O").0, + is_64: mach_o.is_64, + os_type: find_os_mach(&mach_o), + file_type, + is_stripped, + cpu_type, + cpu_subtype, + interpreter: null_mut(), + libraries: CharVec::from(mach_o.libs), + }, + } +} diff --git a/src/pe.rs b/src/pe.rs new file mode 100755 index 0000000..37ad564 --- /dev/null +++ b/src/pe.rs @@ -0,0 +1,88 @@ +use crate::{ + debug::debug_objects, + structs::{CharVec, ParsingError, StringPtr, ULDDObj, ULDDObjResult}, + types::{PeOS, PeSubsystem, PE_ARCH, PE_SUBSYSTEM}, +}; +use goblin::pe::{characteristic::IMAGE_FILE_DEBUG_STRIPPED, PE}; +use std::ptr::null_mut; + +fn find_os_pe(pe: &PE<'_>) -> *mut i8 { + let Some(optional_header) = pe + .header + .optional_header + .and_then(|h| PE_SUBSYSTEM.get(&h.windows_fields.subsystem)) + else { + return null_mut(); + }; + + let os = match optional_header { + PeSubsystem::Xbox => PeOS::Xbox, + PeSubsystem::EFIApplication + | PeSubsystem::EFIBootServiceDriver + | PeSubsystem::EFIRom + | PeSubsystem::EFIRuntimeDriver => PeOS::UEFI, + PeSubsystem::WindowsCUI + | PeSubsystem::WindowsGUI + | PeSubsystem::Native + | PeSubsystem::OS2CUI + | PeSubsystem::PosixCUI + | PeSubsystem::NativeWindows + | PeSubsystem::WindowsCEGUI + | PeSubsystem::WindowsBootApplication => PeOS::Windows, + PeSubsystem::Unknown => return null_mut(), + }; + + StringPtr::from(os.to_string()).0 +} + +pub(crate) fn parse_pe( + file_name: &str, + pe: PE, + member_names: &mut Vec<&str>, + debugging: bool, +) -> ULDDObjResult { + let is_stripped = pe.header.coff_header.characteristics & IMAGE_FILE_DEBUG_STRIPPED + == IMAGE_FILE_DEBUG_STRIPPED; + let cpu_type = StringPtr::from(PE_ARCH.get(&pe.header.coff_header.machine)).0; + let file_type = pe + .header + .optional_header + .and_then(|h| PE_SUBSYSTEM.get(&h.windows_fields.subsystem)); + let interpreter = { + if let Some(optional_header) = pe.header.optional_header { + let linker_major_version = optional_header + .windows_fields + .major_operating_system_version; + let linker_minor_version = optional_header + .windows_fields + .minor_operating_system_version; + let linker_version = format!("{}.{}", linker_major_version, linker_minor_version); + StringPtr::from(linker_version).0 + } else { + null_mut() + } + }; + let executable_format = if pe.is_64 { + debug_objects(file_name, member_names, "a PE32+ binary", debugging); + StringPtr::from("PE32+").0 + } else { + debug_objects(file_name, member_names, "a PE32 binary", debugging); + StringPtr::from("PE32").0 + }; + ULDDObjResult { + error: ParsingError::default(), + obj: ULDDObj { + file_name: StringPtr::from(file_name).0, + member_name: CharVec::from(member_names), + executable_format, + is_64: pe.is_64, + os_type: find_os_pe(&pe), + file_type: StringPtr::from(file_type).0, + is_stripped, + cpu_type, + cpu_subtype: null_mut(), + interpreter, + libraries: CharVec::from(pe.libraries), + }, + } +} diff --git a/src/structs.rs b/src/structs.rs new file mode 100755 index 0000000..e41ad1b --- /dev/null +++ b/src/structs.rs @@ -0,0 +1,111 @@ +use std::ffi::c_char; + +/// A C-compatible vector for `Vec`. +#[repr(C)] +pub struct CharVec { + pub capacity: usize, + pub length: usize, + pub vec: *mut *mut c_char, +} + +/// +/// An error struct for making error handling easy. +/// +/// ## Error Codes +/// - \>0: Magic number of the unknown object (as `i64` (or `ìnt64_t)) +/// - -1: Binary is corrupted +/// - -2: Unknown/Bad magic number +/// - -3: Error at reading and interpreting bytes +/// - -4: I/O Error at parsing the object +/// - -5: Buffer is too short to hold +/// - -6: Unknown error[^1] +/// - -7: Unimplemented executable format +/// +/// [^1]: All errors thrown by goblin crate and my code are covered. Because of matching goblin's [`Error`](goblin::error::Error) is non-exhaustive, I included non-exhaustive path too. +/// +#[repr(C)] +pub struct ParsingError { + pub code: i64, + pub explanation: *mut c_char, +} + +/// +/// A struct contains detailed information about the object. +/// +/// It contains some information even the object is an erroneous one to make error handling more verbose. +/// +/// If the error occurs on parsing: +/// - A file: `file_name` and `member_name` +/// - A Muti Architecture Mach-O file: `file_name`, `member_name` and `executable_format` +/// - An archive: `file_name`, `member_name` and `file_type` +/// +/// fields will be filled correctly and the rest will be: +/// - null (the fields which are string) +/// - blank (`member_name` and `libraries`) +/// - `false` (`is_64` and `is_stripped`). +/// +#[repr(C)] +pub struct ULDDObj { + /// The name of the object. + /// + /// Objects inside Muti Architecture Mach-O files will be named as "n. file" due to they don't have file names. + pub file_name: *mut c_char, + /// The location of objects in recursive files. + /// + /// This field is empty if the object is not in a recursive file (Like: Archives and Muti Architecture Mach-O files). + /// + /// The names in the vector is sorted as outer to inner. + pub member_name: CharVec, + /// The type of the executable format of the object. + pub executable_format: *mut c_char, + /// The field is true if the object is 64 bit otherwise it is 32 bit or the object is an erroneous one. + pub is_64: bool, + /// The name of the OS it was compiled for. + pub os_type: *mut c_char, + /// The type of the object. + pub file_type: *mut c_char, + /// The field is true if the object was stripped from debug symbols otherwise it is not stripped or the object is an erroneous one . + pub is_stripped: bool, + /// The ISA (CPU Architecture) the object compiled for. + pub cpu_type: *mut c_char, + /// The specific CPU model the object compiled for. + /// + /// macOS only field. It is null pointer in other executable formats. + pub cpu_subtype: *mut c_char, + /// The name/version of the linker. + /// + /// ELF/PE only field. It is null pointer in other executable formats. + /// + /// It returns the version of the linker in PE files. + pub interpreter: *mut c_char, + /// A vector of libraries linked against the object. + /// + /// It is blank in COFF files because they are mostly PE object files therefore they don't have linked libraries against them. + pub libraries: CharVec, +} + +/// A struct packs (empty or filled) error and (successfully or not) read object. +#[repr(C)] +pub struct ULDDObjResult { + pub error: ParsingError, + pub obj: ULDDObj, +} + +/// A C-compatible vector for [`ULDDObjResult`]. +#[repr(C)] +pub struct ULDDObjResultVec { + pub capacity: usize, + pub length: usize, + pub vec: *mut ULDDObjResult, +} + +#[doc(hidden)] +pub struct StringPtr(pub *mut i8); + +#[doc(hidden)] +pub(crate) enum Debugging { + Info(String), + Affirmative(String), + Error(String), + Fatal(String), +} diff --git a/src/types.rs b/src/types.rs new file mode 100755 index 0000000..c14c7d8 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,897 @@ +use phf::phf_map; +use std::fmt::{Display, Formatter, Result as FmtResult}; + +/* +Source: +https://en.wikipedia.org/wiki/Executable_and_Linkable_Format +https://en.wikipedia.org/wiki/Cell_(processor) +https://en.wikipedia.org/wiki/ETRAX_CRIS +https://developer.fedoraproject.org/deployment/secondary_architectures/s390.html#:~:text=s390%20is%2031%2Dbit%2Daddress,known%20as%20IBM%20System%20z. +https://www.infineon.com/cms/en/product/microcontroller/ +*/ + +/// MP Stands for Microprocessor +/// BPF Stands for Berkeley Packet Filter +#[allow(clippy::upper_case_acronyms)] +#[derive(Debug)] +pub enum ElfInstructionSet { + Undefined, + WE32100, + SPARC, + X86, + M68k, + M88k, + IntelMCU, + I860, + MIPS, + IBMSystem370, + MIPSRS3000Le, + FutureUse, + HPPPRISC, + I960, + PPC, + PPC64, + S390, + S390x, + // Wikipedia or Google doesn't provide healthy info about what IBM SPU/SPC is + IBMSPUSPC, + NECV800, + FR20, + RH32, + MotorolaRCE, + Arm32, + DigitalAlpha, + SuperH, + SPARCVersion9, + SiemensTriCore, + ArgonautRISCCore, + H8300, + H8300H, + H8S, + H8500, + IA64, + StanfordMIPSX, + MotorolaColdFire, + MotorolaM68HC12, + FujitsuMMA, + SiemensPCP, + SonyCellCPU, + DensoNDR1, + MotorolaStarCore, + ToyotaME16, + STMicroelectronicsST100, + AdvancedLogicCorpTinyJ, + X86_64, + SonyDSP, + PDP10, + PDP11, + SiemensFX66, + STMicroelectronicsST9Plus, + STMicroelectronicsST7, + MC68HC16, + MC68HC11, + MC68HC08, + MC68HC05, + SiliconGraphicsSVx, + STMicroelectronicsST19, + DigitalVAX, + ETRAXCRIS, + InfineonTechnologiesMP32, + Element14DSP64, + LSILogicDSP16, + TMS320C6000, + MCSTElbrusE2k, + Arm64, + ZilogZ80, + RISCV, + BPF, + WDC65C816, +} + +// Source: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format +pub const E_MACHINE: phf::Map = phf_map! { + 0x00_u16 => ElfInstructionSet::Undefined, + 0x01_u16 => ElfInstructionSet::WE32100, + 0x02_u16 => ElfInstructionSet::SPARC, + 0x03_u16 => ElfInstructionSet::X86, + 0x04_u16 => ElfInstructionSet::M68k, + 0x05_u16 => ElfInstructionSet::M88k, + 0x06_u16 => ElfInstructionSet::IntelMCU, + 0x07_u16 => ElfInstructionSet::I860, + 0x08_u16 => ElfInstructionSet::MIPS, + 0x09_u16 => ElfInstructionSet::IBMSystem370, + 0x0A_u16 => ElfInstructionSet::MIPSRS3000Le, + 0x0B_u16 => ElfInstructionSet::FutureUse, + 0x0C_u16 => ElfInstructionSet::FutureUse, + 0x0D_u16 => ElfInstructionSet::FutureUse, + 0x0E_u16 => ElfInstructionSet::FutureUse, + 0x0F_u16 => ElfInstructionSet::HPPPRISC, + 0x13_u16 => ElfInstructionSet::I960, + 0x14_u16 => ElfInstructionSet::PPC, + 0x15_u16 => ElfInstructionSet::PPC64, + 0x16_u16 => ElfInstructionSet::S390, + 0x17_u16 => ElfInstructionSet::S390x, + 0x18_u16 => ElfInstructionSet::FutureUse, + 0x19_u16 => ElfInstructionSet::FutureUse, + 0x20_u16 => ElfInstructionSet::FutureUse, + 0x21_u16 => ElfInstructionSet::FutureUse, + 0x22_u16 => ElfInstructionSet::FutureUse, + 0x23_u16 => ElfInstructionSet::FutureUse, + 0x24_u16 => ElfInstructionSet::NECV800, + 0x25_u16 => ElfInstructionSet::FR20, + 0x26_u16 => ElfInstructionSet::RH32, + 0x27_u16 => ElfInstructionSet::MotorolaRCE, + 0x28_u16 => ElfInstructionSet::Arm32, + 0x29_u16 => ElfInstructionSet::DigitalAlpha, + 0x2A_u16 => ElfInstructionSet::SuperH, + 0x2B_u16 => ElfInstructionSet::SPARCVersion9, + 0x2C_u16 => ElfInstructionSet::SiemensTriCore, + 0x2D_u16 => ElfInstructionSet::ArgonautRISCCore, + 0x2E_u16 => ElfInstructionSet::H8300, + 0x2F_u16 => ElfInstructionSet::H8300H, + 0x30_u16 => ElfInstructionSet::H8S, + 0x31_u16 => ElfInstructionSet::H8500, + 0x32_u16 => ElfInstructionSet::IA64, + 0x33_u16 => ElfInstructionSet::StanfordMIPSX, + 0x34_u16 => ElfInstructionSet::MotorolaColdFire, + 0x35_u16 => ElfInstructionSet::MotorolaM68HC12, + 0x36_u16 => ElfInstructionSet::FujitsuMMA, + 0x37_u16 => ElfInstructionSet::SiemensPCP, + 0x38_u16 => ElfInstructionSet::SonyCellCPU, + 0x39_u16 => ElfInstructionSet::DensoNDR1, + 0x3A_u16 => ElfInstructionSet::MotorolaStarCore, + 0x3B_u16 => ElfInstructionSet::ToyotaME16, + 0x3C_u16 => ElfInstructionSet::STMicroelectronicsST100, + 0x3D_u16 => ElfInstructionSet::AdvancedLogicCorpTinyJ, + 0x3E_u16 => ElfInstructionSet::X86_64, + 0x3F_u16 => ElfInstructionSet::SonyDSP, + 0x40_u16 => ElfInstructionSet::PDP10, + 0x41_u16 => ElfInstructionSet::PDP11, + 0x42_u16 => ElfInstructionSet::SiemensFX66, + 0x43_u16 => ElfInstructionSet::STMicroelectronicsST9Plus, + 0x44_u16 => ElfInstructionSet::STMicroelectronicsST7, + 0x45_u16 => ElfInstructionSet::MC68HC16, + 0x46_u16 => ElfInstructionSet::MC68HC11, + 0x47_u16 => ElfInstructionSet::MC68HC08, + 0x48_u16 => ElfInstructionSet::MC68HC05, + 0x49_u16 => ElfInstructionSet::SiliconGraphicsSVx, + 0x4A_u16 => ElfInstructionSet::STMicroelectronicsST19, + 0x4B_u16 => ElfInstructionSet::DigitalVAX, + 0x4C_u16 => ElfInstructionSet::ETRAXCRIS, + 0x4D_u16 => ElfInstructionSet::InfineonTechnologiesMP32, + 0x4E_u16 => ElfInstructionSet::Element14DSP64, + 0x4F_u16 => ElfInstructionSet::LSILogicDSP16, + 0x8C_u16 => ElfInstructionSet::TMS320C6000, + 0xAF_u16 => ElfInstructionSet::MCSTElbrusE2k, + 0xB7_u16 => ElfInstructionSet::Arm64, + 0xDC_u16 => ElfInstructionSet::ZilogZ80, + 0xF3_u16 => ElfInstructionSet::RISCV, + 0xF7_u16 => ElfInstructionSet::BPF, + 0x101_u16 => ElfInstructionSet::WDC65C816, +}; + +// Source: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format +#[allow(clippy::upper_case_acronyms)] +#[derive(Clone, Copy, Debug)] +pub enum ElfOS { + SystemV, + HPUX, + NetBSD, + Linux, + GNUHurd, + Solaris, + AIXMonterey, + IRIX, + FreeBSD, + Tru64, + NovellModesto, + OpenBSD, + OpenVMS, + NonStopKernel, + AROS, + FenixOS, + CloudABI, + OpenVOS, + Illumos, + SerenityOS, + Android, + Undefined, +} + +/* +Source: + https://en.wikipedia.org/wiki/Executable_and_Linkable_Format + https://stackoverflow.com/a/49248689 +*/ +#[derive(Debug)] +pub enum ElfFileType { + Undefined, + Relocatable, + Executable, + SharedObject, + CoreFile, + OsSpecific, + ProcessorSpecific, +} + +// Source: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format +pub const E_TYPE: phf::Map = phf_map! { + 0x00_u16 => ElfFileType::Undefined, + 0x01_u16 => ElfFileType::Relocatable, + 0x02_u16 => ElfFileType::Executable, + 0x03_u16 => ElfFileType::SharedObject, + 0x04_u16 => ElfFileType::CoreFile, + 0xFE00_u16 => ElfFileType::OsSpecific, + 0xFEFF_u16 => ElfFileType::OsSpecific, + 0xFF00_u16 => ElfFileType::ProcessorSpecific, + 0xFFFF_u16 => ElfFileType::ProcessorSpecific, +}; + +// Source: https://en.wikipedia.org/wiki/Mach-O +#[derive(Debug)] +pub enum MachOCpuType { + VAX, + ROMP, + NS32032, + NS32332, + MC680x0, + X86, + MIPS, + NS32352, + MC98000, + HPPA, + ARM, + MC88000, + SPARC, + I860Be, + I860Le, + RS6000, + PPC, + ARM64, + X86_64, + Unknown, +} + +/* +Source: + https://en.wikipedia.org/wiki/Mach-O + https://www.jviotti.com/2021/07/23/a-deep-dive-on-macos-universal-binaries.html +*/ +pub const MACH_O_CPUTYPE: phf::Map = phf_map! { + 0x01_u32 => MachOCpuType::VAX, + 0x02_u32 => MachOCpuType::ROMP, + 0x04_u32 => MachOCpuType::NS32032, + 0x05_u32 => MachOCpuType::NS32332, + 0x06_u32 => MachOCpuType::MC680x0, + 0x07_u32 => MachOCpuType::X86, + 0x08_u32 => MachOCpuType::MIPS, + 0x09_u32 => MachOCpuType::NS32352, + 0x0A_u32 => MachOCpuType::MC98000, + 0x0B_u32 => MachOCpuType::HPPA, + 0x0C_u32 => MachOCpuType::ARM, + 0x0D_u32 => MachOCpuType::MC88000, + 0x0E_u32 => MachOCpuType::SPARC, + 0x0F_u32 => MachOCpuType::I860Be, + 0x10_u32 => MachOCpuType::I860Le, + 0x11_u32 => MachOCpuType::RS6000, + 0x12_u32 => MachOCpuType::PPC, + 0x1000007_u32 => MachOCpuType::X86_64, + 0x100000C_u32 => MachOCpuType::ARM64, +}; + +// Source: https://en.wikipedia.org/wiki/Mach-O +#[derive(Debug)] +pub enum MachOArmSubType { + All, + A500ArchOrNewer, + A500OrNewer, + A440OrNewer, + M4OrNewer, + V4TOrNewer, + V6OrNewer, + V5TEJOrNewer, + XScaleOrNewer, + V7OrNewer, + V7FCortexA9OrNewer, + V7SSwiftOrNewer, + V7KKirkwood40OrNewer, + V8OrNewer, + V6MOrNewer, + V7MOrNewer, + V7EMOrNewer, + Unknown, +} + +// Source: https://en.wikipedia.org/wiki/Mach-O +pub const MACH_O_ARM_CPU_SUBTYPE: phf::Map = phf_map! { + 0x00_u32 => MachOArmSubType::All, + 0x01_u32 => MachOArmSubType::A500ArchOrNewer, + 0x02_u32 => MachOArmSubType::A500OrNewer, + 0x03_u32 => MachOArmSubType::A440OrNewer, + 0x04_u32 => MachOArmSubType::M4OrNewer, + 0x05_u32 => MachOArmSubType::V4TOrNewer, + 0x06_u32 => MachOArmSubType::V6OrNewer, + 0x07_u32 => MachOArmSubType::V5TEJOrNewer, + 0x08_u32 => MachOArmSubType::XScaleOrNewer, + 0x09_u32 => MachOArmSubType::V7OrNewer, + 0x0A_u32 => MachOArmSubType::V7FCortexA9OrNewer, + 0x0B_u32 => MachOArmSubType::V7SSwiftOrNewer, + 0x0C_u32 => MachOArmSubType::V7KKirkwood40OrNewer, + 0x0D_u32 => MachOArmSubType::V8OrNewer, + 0x0E_u32 => MachOArmSubType::V6MOrNewer, + 0x0F_u32 => MachOArmSubType::V7MOrNewer, + 0x10_u32 => MachOArmSubType::V7EMOrNewer, +}; + +// Source: https://en.wikipedia.org/wiki/Mach-O +#[derive(Debug)] +pub enum MachOX86SubType { + All, + I486OrNewer, + I486SXOrNewer, + PentiumM5OrNewer, + CeleronOrNewer, + CeleronMobile, + Pentium3OrNewer, + Pentium3MOrNewer, + Pentium3XeonOrNewer, + Pentium4OrNewer, + ItaniumOrNewer, + Itanium2OrNewer, + XeonOrNewer, + XeonMPOrNewer, + Undefined, +} + +// Source: https://en.wikipedia.org/wiki/Mach-O +pub const MACH_O_X86_CPU_SUBTYPE: phf::Map = phf_map! { + 0x03_u32 => MachOX86SubType::All, + 0x04_u32 => MachOX86SubType::I486OrNewer, + 0x84_u32 => MachOX86SubType::I486SXOrNewer, + 0x56_u32 => MachOX86SubType::PentiumM5OrNewer, + 0x67_u32 => MachOX86SubType::CeleronOrNewer, + 0x77_u32 => MachOX86SubType::CeleronMobile, + 0x08_u32 => MachOX86SubType::Pentium3OrNewer, + 0x18_u32 => MachOX86SubType::Pentium3MOrNewer, + 0x28_u32 => MachOX86SubType::Pentium3XeonOrNewer, + 0x0A_u32 => MachOX86SubType::Pentium4OrNewer, + 0x0B_u32 => MachOX86SubType::ItaniumOrNewer, + 0x1B_u32 => MachOX86SubType::Itanium2OrNewer, + 0x0C_u32 => MachOX86SubType::XeonOrNewer, + 0x1C_u32 => MachOX86SubType::XeonMPOrNewer, +}; + +#[derive(Debug)] +pub enum MachOCpuSubType { + Arm(MachOArmSubType), + X86(MachOX86SubType), +} + +// Source: https://en.wikipedia.org/wiki/Mach-O +#[derive(Debug)] +pub enum MachOOs { + MacOS, + IOS, + AppleTVBox, + AppleWatch, + BridgeOS, + MacCatalyst, + IOSSimulator, + AppleTVSimulator, + AppleWatchSimulator, + DriverKit, + AppleVisionPro, + AppleVisionProSimulator, + Undefined, +} + +// Source: https://en.wikipedia.org/wiki/Mach-O +pub const MACH_O_OS: phf::Map = phf_map! { + 0x1_u32 => MachOOs::MacOS, + 0x2_u32 => MachOOs::IOS, + 0x3_u32 => MachOOs::AppleTVBox, + 0x4_u32 => MachOOs::AppleWatch, + 0x5_u32 => MachOOs::BridgeOS, + 0x6_u32 => MachOOs::MacCatalyst, + 0x7_u32 => MachOOs::IOSSimulator, + 0x8_u32 => MachOOs::AppleTVSimulator, + 0x9_u32 => MachOOs::AppleWatchSimulator, + 0xA_u32 => MachOOs::DriverKit, + 0xB_u32 => MachOOs::AppleVisionPro, + 0xC_u32 => MachOOs::AppleVisionProSimulator, +}; + +// Source: https://en.wikipedia.org/wiki/Mach-O +#[derive(Debug)] +pub enum MachOFileType { + RelocatableObjectFile, + DemandPagedExecutableFile, + FixedVMSharedLibraryFile, + CoreFile, + PreloadedExecutableFile, + DynamicallyBoundSharedLibraryFile, + DynamicLinkEditor, + DynamicallyBoundBundleFile, + SharedLibraryStub, + CompanionFileWithDebugSections, + X86_64Kexts, + ComposedFile, + Undefined, +} + +// Source: https://en.wikipedia.org/wiki/Mach-O +pub const MACH_O_FILE_TYPE: phf::Map = phf_map! { + 0x1_u32 => MachOFileType::RelocatableObjectFile, + 0x2_u32 => MachOFileType::DemandPagedExecutableFile, + 0x3_u32 => MachOFileType::FixedVMSharedLibraryFile, + 0x4_u32 => MachOFileType::CoreFile, + 0x5_u32 => MachOFileType::PreloadedExecutableFile, + 0x6_u32 => MachOFileType::DynamicallyBoundSharedLibraryFile, + 0x7_u32 => MachOFileType::DynamicLinkEditor, + 0x8_u32 => MachOFileType::DynamicallyBoundBundleFile, + 0x9_u32 => MachOFileType::SharedLibraryStub, + 0xA_u32 => MachOFileType::CompanionFileWithDebugSections, + 0xB_u32 => MachOFileType::X86_64Kexts, + 0xC_u32 => MachOFileType::ComposedFile, +}; + +// Source: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format +#[derive(Debug)] +pub enum PeArch { + Unknown, + AlphaAXP32, + AlphaAXP64, + MatsushitaAM33, + X64, + ARM, + ARM64, + ARMNT, + AXP64, + EBC, + I386, + IA64, + LoongArch32, + LoongArch64, + MitsubishiM32R, + MIPS16, + MIPSFPU, + MIPSFPU16, + PowerPC, + PowerPCFP, + MIPS, + RISCV32, + RISCV64, + RISCV128, + HitachiSH3, + HitachiSH3DSP, + HitachiSH4, + HitachiSH5, + Thumb, + MIPSWCE, +} + +// Source: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format +pub const PE_ARCH: phf::Map = phf_map! { + 0x0000_u16 => PeArch::Unknown, + 0x0184_u16 => PeArch::AlphaAXP32, + 0x0284_u16 => PeArch::AlphaAXP64, + 0x01d3_u16 => PeArch::MatsushitaAM33, + 0x8664_u16 => PeArch::X64, + 0x01c0_u16 => PeArch::ARM, + 0xaa64_u16 => PeArch::ARM64, + 0x01c4_u16 => PeArch::ARMNT, + // Look at source to learn why there is a duplicate value + //0x0284_u16 => PeArch::AXP64, + 0xebc_u16 => PeArch::EBC, + 0x014c_u16 => PeArch::I386, + 0x0200_u16 => PeArch::IA64, + 0x6232_u16 => PeArch::LoongArch32, + 0x6264_u16 => PeArch::LoongArch64, + 0x9041_u16 => PeArch::MitsubishiM32R, + 0x0266_u16 => PeArch::MIPS16, + 0x0366_u16 => PeArch::MIPSFPU, + 0x0466_u16 => PeArch::MIPSFPU16, + 0x01f0_u16 => PeArch::PowerPC, + 0x01f1_u16 => PeArch::PowerPCFP, + 0x0166_u16 => PeArch::MIPS, + 0x5032_u16 => PeArch::RISCV32, + 0x5064_u16 => PeArch::RISCV64, + 0x5128_u16 => PeArch::RISCV128, + 0x01a2_u16 => PeArch::HitachiSH3, + 0x01a3_u16 => PeArch::HitachiSH3DSP, + 0x01a6_u16 => PeArch::HitachiSH4, + 0x01a8_u16 => PeArch::HitachiSH5, + 0x01c2_u16 => PeArch::Thumb, + 0x0169_u16 => PeArch::MIPSWCE, +}; + +// Source: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format +#[derive(Debug)] +pub enum PeSubsystem { + Unknown, + Native, + WindowsGUI, + WindowsCUI, + OS2CUI, + PosixCUI, + NativeWindows, + WindowsCEGUI, + EFIApplication, + EFIBootServiceDriver, + EFIRuntimeDriver, + EFIRom, + Xbox, + WindowsBootApplication, +} + +// Source: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format +pub const PE_SUBSYSTEM: phf::Map = phf_map! { + 0x0000_u16 => PeSubsystem::Unknown, + 0x0001_u16 => PeSubsystem::Native, + 0x0002_u16 => PeSubsystem::WindowsGUI, + 0x0003_u16 => PeSubsystem::WindowsCUI, + 0x0005_u16 => PeSubsystem::OS2CUI, + 0x0007_u16 => PeSubsystem::PosixCUI, + 0x0008_u16 => PeSubsystem::NativeWindows, + 0x0009_u16 => PeSubsystem::WindowsCEGUI, + 0x000A_u16 => PeSubsystem::EFIApplication, + 0x000B_u16 => PeSubsystem::EFIBootServiceDriver, + 0x000C_u16 => PeSubsystem::EFIRuntimeDriver, + 0x000D_u16 => PeSubsystem::EFIRom, + 0x000E_u16 => PeSubsystem::Xbox, + 0x0010_u16 => PeSubsystem::WindowsBootApplication, +}; + +#[derive(Debug)] +pub enum PeOS { + Xbox, + Windows, + UEFI, + Undefined, +} + +// Source of the names: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format +impl Display for ElfOS { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + ElfOS::SystemV => write!(f, "System V"), + ElfOS::HPUX => write!(f, "HP-UX"), + ElfOS::NetBSD => write!(f, "NetBSD"), + ElfOS::Linux => write!(f, "Linux"), + ElfOS::GNUHurd => write!(f, "GNU Hurd"), + ElfOS::Solaris => write!(f, "Solaris"), + ElfOS::AIXMonterey => write!(f, "AIX (Monterey)"), + ElfOS::IRIX => write!(f, "IRIX"), + ElfOS::FreeBSD => write!(f, "FreeBSD"), + ElfOS::Tru64 => write!(f, "Tru64"), + ElfOS::NovellModesto => write!(f, "Novell Modesto"), + ElfOS::OpenBSD => write!(f, "OpenBSD"), + ElfOS::OpenVMS => write!(f, "OpenVMS"), + ElfOS::NonStopKernel => write!(f, "NonStop Kernel"), + ElfOS::AROS => write!(f, "AROS"), + ElfOS::FenixOS => write!(f, "FenixOS"), + ElfOS::CloudABI => write!(f, "Nuxi CloudABI"), + ElfOS::OpenVOS => write!(f, "OpenVOS"), + ElfOS::Illumos => write!(f, "Illumos"), + ElfOS::SerenityOS => write!(f, "SerenityOS"), + ElfOS::Android => write!(f, "Android"), + ElfOS::Undefined => write!(f, "Undefined"), + } + } +} + +// Source of the names: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format +impl Display for ElfInstructionSet { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + ElfInstructionSet::Undefined => write!(f, "Undefined"), //0x00 no specific instruction set is defined + ElfInstructionSet::WE32100 => write!(f, "AT&T WE 32100"), + ElfInstructionSet::SPARC => write!(f, "SPARC"), + ElfInstructionSet::X86 => write!(f, "x86"), + ElfInstructionSet::M68k => write!(f, "Motorola 68000 (M68k)"), + ElfInstructionSet::M88k => write!(f, "Motorola 88000 (M88k)"), + ElfInstructionSet::IntelMCU => write!(f, "Intel MCU"), + ElfInstructionSet::I860 => write!(f, "Intel 80860"), + ElfInstructionSet::MIPS => write!(f, "MIPS"), + ElfInstructionSet::IBMSystem370 => write!(f, "IBM System/370"), + ElfInstructionSet::MIPSRS3000Le => write!(f, "MIPS RS3000 (Little-endian)"), + ElfInstructionSet::FutureUse => write!(f, "Reserved for future use"), + ElfInstructionSet::HPPPRISC => write!(f, "HP PA-RISC"), + ElfInstructionSet::I960 => write!(f, "Intel 80960"), + ElfInstructionSet::PPC => write!(f, "PowerPC"), + ElfInstructionSet::PPC64 => write!(f, "PowerPC (64-Bit)"), + ElfInstructionSet::S390 => write!(f, "S390"), + ElfInstructionSet::S390x => write!(f, "S390x"), + ElfInstructionSet::NECV800 => write!(f, "NEC V800"), + ElfInstructionSet::FR20 => write!(f, "Fujitsu FR20"), + ElfInstructionSet::RH32 => write!(f, "TRW RH-32"), + ElfInstructionSet::MotorolaRCE => write!(f, "Motorola RCE"), + ElfInstructionSet::Arm32 => write!(f, "Arm (32-Bit)"), + ElfInstructionSet::DigitalAlpha => write!(f, "Digital Alpha"), + ElfInstructionSet::SuperH => write!(f, "SuperH"), + ElfInstructionSet::SPARCVersion9 => write!(f, "SPARC version 9"), + ElfInstructionSet::SiemensTriCore => write!(f, "Siemens TriCore"), + ElfInstructionSet::ArgonautRISCCore => write!(f, "Argonaut RISC Core"), + ElfInstructionSet::H8300 => write!(f, "Hitachi H8/300"), + ElfInstructionSet::H8300H => write!(f, "Hitachi H8/300H"), + ElfInstructionSet::H8S => write!(f, "Hitachi H8S"), + ElfInstructionSet::H8500 => write!(f, "Hitachi H8/500"), + ElfInstructionSet::IA64 => write!(f, "Intel Itanium"), + ElfInstructionSet::StanfordMIPSX => write!(f, "Stanford MIPS-X"), + ElfInstructionSet::MotorolaColdFire => write!(f, "Motorola ColdFire"), + ElfInstructionSet::MotorolaM68HC12 => write!(f, "Motorola M68HC12"), + ElfInstructionSet::FujitsuMMA => write!(f, "Fujitsu MMA multimedia accelerator"), + ElfInstructionSet::SiemensPCP => write!(f, "Siemens PCP"), + ElfInstructionSet::SonyCellCPU => write!(f, "Sony nCPU Embedded RISC"), + ElfInstructionSet::DensoNDR1 => write!(f, "Denso NDR1"), + ElfInstructionSet::MotorolaStarCore => write!(f, "Motorola Star*Core"), + ElfInstructionSet::ToyotaME16 => write!(f, "Toyota ME16"), + ElfInstructionSet::STMicroelectronicsST100 => { + write!(f, "STMicroelectronics ST100") + } + ElfInstructionSet::AdvancedLogicCorpTinyJ => { + write!(f, "Advanced Logic Corp. TinyJ") + } + ElfInstructionSet::X86_64 => write!(f, "x86-64"), + ElfInstructionSet::SonyDSP => write!(f, "Sony DSP processor"), + ElfInstructionSet::PDP10 => write!(f, "Digital Equipment Corp. PDP-10"), + ElfInstructionSet::PDP11 => write!(f, "Digital Equipment Corp. PDP-11"), + ElfInstructionSet::SiemensFX66 => write!(f, "Siemens FX66 microcontroller"), + ElfInstructionSet::STMicroelectronicsST9Plus => { + write!(f, "STMicroelectronics ST9+ 8/16-Bit microcontroller") + } + ElfInstructionSet::STMicroelectronicsST7 => { + write!(f, "STMicroelectronics ST7 8-Bit microcontroller") + } + ElfInstructionSet::MC68HC16 => write!(f, "Motorola MC68HC16 microcontroller"), + ElfInstructionSet::MC68HC11 => write!(f, "Motorola MC68HC11 microcontroller"), + ElfInstructionSet::MC68HC08 => write!(f, "Motorola MC68HC08 microcontroller"), + ElfInstructionSet::MC68HC05 => write!(f, "Motorola MC68HC05 microcontroller"), + ElfInstructionSet::SiliconGraphicsSVx => write!(f, "Silicon Graphics SVx"), + ElfInstructionSet::STMicroelectronicsST19 => { + write!(f, "STMicroelectronics ST19 8-Bit microcontroller") + } + ElfInstructionSet::DigitalVAX => write!(f, "Digital VAX"), + ElfInstructionSet::ETRAXCRIS => { + write!(f, "Axis Communications (32-Bit) embedded processor") + } + ElfInstructionSet::InfineonTechnologiesMP32 => { + write!(f, "Infineon Technologies (32-Bit) embedded processor") + } + ElfInstructionSet::Element14DSP64 => write!(f, "Element 14 (64-Bit) DSP processor"), + ElfInstructionSet::LSILogicDSP16 => write!(f, "LSI Logic 16-Bit DSP processor"), + ElfInstructionSet::TMS320C6000 => write!(f, "TMS320C6000 family"), + ElfInstructionSet::MCSTElbrusE2k => write!(f, "MCST Elbrus e2k"), + ElfInstructionSet::Arm64 => write!(f, "Arm (64-Bit)"), + ElfInstructionSet::ZilogZ80 => write!(f, "Zilog Z80"), + ElfInstructionSet::RISCV => write!(f, "RISC-V"), + ElfInstructionSet::BPF => write!(f, "Berkeley packet filter"), + ElfInstructionSet::IBMSPUSPC => write!(f, "IBM SPU/SPC"), + ElfInstructionSet::WDC65C816 => write!(f, "WDC 65C816"), + } + } +} + +/* +Sources: + https://en.wikipedia.org/wiki/Executable_and_Linkable_Format + https://refspecs.linuxbase.org/elf/gabi4+/ch4.intro.html +*/ +impl Display for ElfFileType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + ElfFileType::Undefined => write!(f, "Undefined"), + ElfFileType::Relocatable => write!(f, "Object file"), + ElfFileType::Executable => write!(f, "Executable"), + ElfFileType::SharedObject => write!(f, "Shared object"), + ElfFileType::CoreFile => write!(f, "Core file"), + // I couldn't get healthy info. about "OS/CPU specific" + ElfFileType::OsSpecific => write!(f, "OS-specific"), + ElfFileType::ProcessorSpecific => write!(f, "CPU-specific"), + } + } +} + +// Source: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format +impl Display for PeArch { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + PeArch::Unknown => write!(f, "Unknown"), + PeArch::AlphaAXP32 => write!(f, "Alpha AXP (32-Bit)"), + PeArch::AlphaAXP64 | PeArch::AXP64 => write!(f, "Alpha 64 (64-Bit)"), + PeArch::MatsushitaAM33 => write!(f, "Matsushita AM33"), + PeArch::X64 => write!(f, "x64"), + PeArch::ARM => write!(f, "Arm"), + PeArch::ARM64 => write!(f, "Arm (64-Bit)"), + PeArch::ARMNT => write!(f, "ARM Thumb-2"), + PeArch::EBC => write!(f, "EFI bytecode"), + PeArch::I386 => write!(f, "Intel 386"), + PeArch::IA64 => write!(f, "Intel Itanium"), + PeArch::LoongArch32 => write!(f, "LoongArch (32-Bit)"), + PeArch::LoongArch64 => write!(f, "LoongArch (64-Bit)"), + PeArch::MitsubishiM32R => write!(f, "Mitsubishi M32R"), + PeArch::MIPS16 => write!(f, "MIPS16"), + PeArch::MIPSFPU => write!(f, "MIPS with FPU"), + PeArch::MIPSFPU16 => write!(f, "MIPS16 with FPU"), + PeArch::PowerPC => write!(f, "PowerPC"), + PeArch::PowerPCFP => write!(f, "PowerPC with floating point support"), + PeArch::MIPS => write!(f, "MIPS"), + PeArch::RISCV32 => write!(f, "RISC-V (32-Bit)"), + PeArch::RISCV64 => write!(f, "RISC-V (64-Bit)"), + PeArch::RISCV128 => write!(f, "RISC-V 128-Bit"), + PeArch::HitachiSH3 => write!(f, "Hitachi SH3"), + PeArch::HitachiSH3DSP => write!(f, "Hitachi SH3 DSP"), + PeArch::HitachiSH4 => write!(f, "Hitachi SH4"), + PeArch::HitachiSH5 => write!(f, "Hitachi SH5"), + PeArch::Thumb => write!(f, "Thumb"), + PeArch::MIPSWCE => write!(f, "MIPS (Little-endian) WCE v2"), + } + } +} + +/* + Sources: + https://learn.microsoft.com/en-us/windows/win32/debug/pe-format + CLI != CUI: https://github.com/avelino/awesome-go/issues/282#issuecomment-73395067 +*/ +impl Display for PeSubsystem { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + PeSubsystem::Unknown => write!(f, "Undefined"), + PeSubsystem::Native => write!(f, "Device drivers and native windows processes"), + PeSubsystem::WindowsGUI => write!(f, "GUI application"), + PeSubsystem::WindowsCUI => write!(f, "Windows CUI application"), + PeSubsystem::OS2CUI => write!(f, "OS/2 CUI application"), + PeSubsystem::PosixCUI => write!(f, "Posix CUI application"), + PeSubsystem::NativeWindows => write!(f, "Native Win9x driver"), + PeSubsystem::WindowsCEGUI => write!(f, "Windows CE application"), + PeSubsystem::EFIApplication => write!(f, "EFI application"), + PeSubsystem::EFIBootServiceDriver => write!(f, "EFI driver with boot services"), + PeSubsystem::EFIRuntimeDriver => write!(f, "EFI driver with runtime services"), + PeSubsystem::EFIRom => write!(f, "EFI ROM image"), + PeSubsystem::Xbox => write!(f, "XBOX"), + PeSubsystem::WindowsBootApplication => write!(f, "Windows boot application."), + } + } +} + +// Source: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format +impl Display for PeOS { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + PeOS::Xbox => write!(f, "Xbox"), + PeOS::Windows => write!(f, "Windows"), + PeOS::UEFI => write!(f, "UEFI"), + PeOS::Undefined => write!(f, "Undefined"), + } + } +} + +// Source: https://en.wikipedia.org/wiki/Mach-O +impl Display for MachOArmSubType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + MachOArmSubType::All => write!(f, "All ARM processors"), + MachOArmSubType::A500ArchOrNewer => write!(f, "ARM-A500 ARCH or newer"), + MachOArmSubType::A500OrNewer => write!(f, "ARM-A500 or newer"), + MachOArmSubType::A440OrNewer => write!(f, "ARM-A440 or newer"), + MachOArmSubType::M4OrNewer => write!(f, "ARM-M4 or newer"), + MachOArmSubType::V4TOrNewer => write!(f, "ARM-V4T or newer"), + MachOArmSubType::V6OrNewer => write!(f, "ARM-V6 or newer"), + MachOArmSubType::V5TEJOrNewer => write!(f, "ARM-V5TEJ or newer"), + MachOArmSubType::XScaleOrNewer => write!(f, "ARM-XSCALE or newer"), + MachOArmSubType::V7OrNewer => write!(f, "ARM-V7 or newer"), + MachOArmSubType::V7FCortexA9OrNewer => write!(f, "ARM-V7F (Cortex A9) or newer"), + MachOArmSubType::V7SSwiftOrNewer => write!(f, "ARM-V7S (Swift) or newer"), + MachOArmSubType::V7KKirkwood40OrNewer => write!(f, "ARM-V7K (Kirkwood40) or newer"), + MachOArmSubType::V8OrNewer => write!(f, "ARM-V8 or newer"), + MachOArmSubType::V6MOrNewer => write!(f, "ARM-V6M or newer"), + MachOArmSubType::V7MOrNewer => write!(f, "ARM-V7M or newer"), + MachOArmSubType::V7EMOrNewer => write!(f, "ARM-V7EM or newer"), + MachOArmSubType::Unknown => write!(f, "Unknown"), + } + } +} + +// Source: https://en.wikipedia.org/wiki/Mach-O +impl Display for MachOX86SubType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + MachOX86SubType::All => write!(f, "All x86 processors"), + MachOX86SubType::I486OrNewer => write!(f, "486 or newer"), + MachOX86SubType::I486SXOrNewer => write!(f, "486SX or newer"), + MachOX86SubType::PentiumM5OrNewer => write!(f, "Pentium M5 or newer"), + MachOX86SubType::CeleronOrNewer => write!(f, "Celeron or newer"), + MachOX86SubType::CeleronMobile => write!(f, "Celeron Mobile"), + MachOX86SubType::Pentium3OrNewer => write!(f, "Pentium 3 or newer"), + MachOX86SubType::Pentium3MOrNewer => write!(f, "Pentium 3-M or newer"), + MachOX86SubType::Pentium3XeonOrNewer => write!(f, "Pentium 3-XEON or newer"), + MachOX86SubType::Pentium4OrNewer => write!(f, "Pentium-4 or newer"), + MachOX86SubType::ItaniumOrNewer => write!(f, "Itanium or newer"), + MachOX86SubType::Itanium2OrNewer => write!(f, "Itanium-2 or newer"), + MachOX86SubType::XeonOrNewer => write!(f, "XEON or newer"), + MachOX86SubType::XeonMPOrNewer => write!(f, "XEON-MP or newer"), + MachOX86SubType::Undefined => write!(f, "Undefined"), + } + } +} + +// Source: https://en.wikipedia.org/wiki/Mach-O +impl Display for MachOCpuSubType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + MachOCpuSubType::Arm(arm) => write!(f, "{}", arm), + MachOCpuSubType::X86(x86) => write!(f, "{}", x86), + } + } +} + +// Source: https://en.wikipedia.org/wiki/Mach-O +impl Display for MachOCpuType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + MachOCpuType::VAX => write!(f, "VAX"), + MachOCpuType::ROMP => write!(f, "ROMP"), + MachOCpuType::NS32032 => write!(f, "NS32032"), + MachOCpuType::NS32332 => write!(f, "NS32332"), + MachOCpuType::MC680x0 => write!(f, "MC680x0"), + MachOCpuType::X86 => write!(f, "x86"), + MachOCpuType::MIPS => write!(f, "MIPS"), + MachOCpuType::NS32352 => write!(f, "NS32352"), + MachOCpuType::MC98000 => write!(f, "MC98000"), + MachOCpuType::HPPA => write!(f, "HP-PA"), + MachOCpuType::ARM => write!(f, "ARM"), + MachOCpuType::MC88000 => write!(f, "MC88000"), + MachOCpuType::SPARC => write!(f, "SPARC"), + MachOCpuType::I860Be => write!(f, "i860 (Big-endian)"), + MachOCpuType::I860Le => write!(f, "i860 (Little-endian)"), + MachOCpuType::RS6000 => write!(f, "RS/6000"), + MachOCpuType::PPC => write!(f, "PowerPC"), + MachOCpuType::ARM64 => write!(f, "ARM64"), + MachOCpuType::X86_64 => write!(f, "x86_64"), + MachOCpuType::Unknown => write!(f, "Unknown"), + } + } +} + +// Source: https://en.wikipedia.org/wiki/Mach-O +impl Display for MachOFileType { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + MachOFileType::RelocatableObjectFile => write!(f, "Object file"), + MachOFileType::DemandPagedExecutableFile => write!(f, "Executable"), + MachOFileType::FixedVMSharedLibraryFile => write!(f, "Fixed VM shared library file"), + MachOFileType::CoreFile => write!(f, "Core file"), + MachOFileType::PreloadedExecutableFile => write!(f, "Preloaded executable file"), + MachOFileType::DynamicallyBoundSharedLibraryFile => write!(f, "Shared object"), + MachOFileType::DynamicLinkEditor => write!(f, "Dynamic link editor"), + MachOFileType::DynamicallyBoundBundleFile => write!(f, "Dynamically bound bundle file"), + MachOFileType::SharedLibraryStub => write!(f, "Shared library stub for static linking only, no section contents"), + MachOFileType::CompanionFileWithDebugSections => write!(f, "Companion file with only debug sections"), + MachOFileType::X86_64Kexts => write!(f, "x86_64 kext"), + MachOFileType::ComposedFile => write!(f, "File composed of other Mach-Os to be run in the same userspace sharing a single linkedit"), + MachOFileType::Undefined => write!(f, "Undefined"), + } + } +} + +// Source: https://en.wikipedia.org/wiki/Mach-O +impl Display for MachOOs { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + match self { + MachOOs::MacOS => write!(f, "MacOS"), + MachOOs::IOS => write!(f, "IOS"), + MachOOs::AppleTVBox => write!(f, "Apple TV Box"), + MachOOs::AppleWatch => write!(f, "Apple Watch"), + MachOOs::BridgeOS => write!(f, "Bridge OS"), + MachOOs::MacCatalyst => write!(f, "Mac Catalyst"), + MachOOs::IOSSimulator => write!(f, "IOS simulator"), + MachOOs::AppleTVSimulator => write!(f, "Apple TV simulator"), + MachOOs::AppleWatchSimulator => write!(f, "Apple watch simulator"), + MachOOs::DriverKit => write!(f, "Driver KIT"), + MachOOs::AppleVisionPro => write!(f, "Apple Vision Pro"), + MachOOs::AppleVisionProSimulator => write!(f, "Apple Vision Pro simulator"), + MachOOs::Undefined => write!(f, "Undefined"), + } + } +}