diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/LEEME.txt b/LEEME.txt
new file mode 100644
index 0000000..1aae667
--- /dev/null
+++ b/LEEME.txt
@@ -0,0 +1,170 @@
+**-Unhide-**
+ http://www.unhide-forensics.info
+
+Unhide es una herramienta forense que permite descubrir procesos y puertos TCP/UDP ocultos
+por rootkits / LKMs o cualquier otra tecnica de ocultacion.
+
+
+// Unhide (unhide-linux o unhide-posix)
+// -------------------------------------
+
+Permite identificar procesos que hayan sido ocultados. Implementa seis tecnicas:
+
+1- Comparacion de la informacion obtenida por /bin/ps frente a los directorios en /proc
+
+2- Comparacion de la informacin obtenida de /bin/ps contra la estructura de directorios de /proc
+ SOLO para la versión "unhide-linux"
+
+3- Comparacion de la informacion visible por /bin/ps frente a la que se puede obtener
+ utilizando diversas sycalls del sistema (syscall scanning).
+
+4- Busqueda de incoherencias entre el resultado de /bin/ps y la informacion obtenida en /proc y syscall scanning (Reverse scanning)
+ SOLO para la versión "unhide-linux"
+
+5- Ocupacion por fuerta bruta del espacio de PIDs disponibles en el sistema (PIDs bruteforcing)
+ SOLO para la versión "unhide-linux"
+
+6- Escaneo 'rapido' de informacion usando /proc procfs y syscalls
+ SOLO para la versión "unhide-linux". Es un test notablemente más rápido que usar de forma independiente los test 1/2/3 pero
+ mas propenso a dar falsos positivos
+
+// Unhide_rb
+// ---------
+
+Es un port en el lenguaje de programación C de unhide.rb, proyecto alternativo a Unhide
+Como el original, es tan solo una aproximación ligera de Unhide
+
+- El hace solo tres test (kill, opendir y chdir)
+- Tan solo ejecuta /bin/ps al iniciar el escaneo y para el check doble
+- Los tests realizados son mucho menos fiables (por ejemplo usar los valores de retorno en vez de errno)
+- Los procesos son identificados tan solo por su ejecutable (unhide-linux también usa cmdline y 'sleeping kernel process')
+- No obstante, incorpora unos pocos métodos anti-fallos, (fallos al usar popen por ejemplo)
+- No tiene capacidad de logging
+
+Es bastante rápido, unas 80 veces más rápido que usar 'unhide-linux quick reverse'
+
+// Unhide-TCP
+
+Permite identificar puertos TCP/UDP que esten a la escucha pero no aparezcan listados
+en /bin/netstat o sbin/ss, usa dos métodos:
+- Fuerza bruta sobre todo el rango de puertos TCP/UDP disponibles y comparándolos con la salida de los comandos SS/netstat
+- Probando todos los puertos TCP/UDP que no lista netstat
+
+// Ficheros
+
+unhide-linux.c --> Procesos ocultos, Linux 2.6.x
+unhide-linux.h
+
+unhide-tcp.c --> Puertos tcp/udp ocultos
+unhide-tcp-fast.c
+unhide-tcp.h
+
+unhide-output.c --> Rutinas de uso para Unhide
+unhide-output.h
+
+
+
+unhide_rb.c --> un port en C de unhide.rb (una versión muy simplificada de unhide-linux en Ruby)
+
+unhide-posix.c --> Procesos ocultos, Sistemas Unix (*BSD, solaris, linux 2.2, linux 2.4) No incorpora
+ PIDs bruteforcing, Necesita mas testing. Atención: Esta es una versión desactualizada de Unhide solo para sistemas antiguos
+
+changelog -- El log de cambios de Unhide
+
+COPYING -- Fichero de licencia, GNU GPL V3
+
+LISEZ-MOI.TXT -- Versión francesa de este fichero
+
+NEWS -- Novedades relacionadas con las versiones
+
+README.txt -- Versión inglesa de este fichero
+
+sanity.sh -- Fichero para realizar tests de funcionamiento
+
+TODO -- Cosas pendientes de hacer (¿Algún voluntario?)
+
+man/unhide.8 -- Página man en inglés
+
+man/unhide-tcp.8 -- Página man de unhide-tcp en inglés
+
+man/fr/unhide.8 -- Página man en Francés de unhide
+
+man/fr/unhide-tcp.8 -- Página man en Francés de unhide-tcp
+
+man/es/unhide.8 -- Página man de unhide en Español
+
+man/es/unhide-tcp.8 --Página man de unhide-tcp en Español
+
+
+// Compilación
+
+Para compilar Unhide es necesario:
+ glibc-devel
+ glibc-static-devel
+
+Y las siguientes dependencias:
+- unhide-tcp para linux :
+ iproute2
+ net-tools (para netstat)
+ lsof
+ psmisc (para fuser)
+- unhide-tcp para freeBSD :
+ sockstat
+ lsof
+ netstat
+
+unhide-linux, unhide-posix, unhide_rb :
+ procps
+
+Si estás usando un kernel de Linux > = 2.6
+ gcc -Wall -O2 --static -pthread unhide-linux*.c unhide-output.c -o unhide-linux
+ gcc -Wall -O2 --static unhide_rb.c -o unhide_rb
+ gcc -Wall -O2 --static unhide-tcp.c unhide-tcp-fast.c unhide-output.c -o unhide-tcp
+ ln -s unhide unhide-linux
+
+Si no,(Linux < 2.6, *BSD, Solaris and other Unix)
+ gcc --static unhide-posix.c -o unhide-posix
+ ln -s unhide unhide-posix
+
+// Utilización
+
+TIENE QUE SER root para usar unhide
+
+
+Ejemplos:
+
+ # ./unhide-linux -vo quick reverse
+ # ./unhide-linux -vom procall sys
+ # ./unhide_rb
+
+ # ./unhide-tcp -flov
+ # ./unhide-tcp -flovs
+
+
+// Licencia
+
+GPL V.3 (http://www.gnu.org/licenses/gpl-3.0.html)
+
+// Agradecimientos
+
+A. Ramos (aramosf@unsec.net) Por aportar algunas expresiones regulares
+
+unspawn (unspawn@rootshell.be) Soporte en CentOS
+
+Martin Bowers (Martin.Bowers@freescale.com) Soporte en CentOS
+
+Lorenzo Martinez (lorenzo@lorenzomartinez.homeip.net) Por aportar varias ideas y betatesting
+
+Francois Marier (francois@debian.org) Por crear las paginas man y dar soporte en Debian
+
+Johan Walles (johan.walles@gmail.com) Por encontrar y solucionar un importante fallo del tipo "condicion de carrera"
+
+Jan Iven (jan.iven@cern.ch) Por sus magníficas mejoras, nuevos tests y bugfixing
+
+P. Gouin (pg.bug.cvs.pgn@free.fr) Por su increible trabajo 'fixeando' bugs y mejorando el rendimiento
+
+François Boisson por su idea de un doble control en el test 'brute'
+
+Leandro Lucarella (leandro.lucarella@sociomantic.com) por el modo de escaneo rápido y la re-escritura de unhide-tcp
+
+Nikos Ntarmos (ntarmos@ceid.upatras.gr) Por su inestimable labor ayudando a portar Unhide a FreeBSD y por hacer el empaquetado para FreeBSD.
diff --git a/LISEZ-MOI.TXT b/LISEZ-MOI.TXT
new file mode 100644
index 0000000..4412ebc
--- /dev/null
+++ b/LISEZ-MOI.TXT
@@ -0,0 +1,184 @@
+**-Unhide-**
+ http://www.unhide-forensics.info
+
+Unhide est un outil d'investigation dont le rôle est de détecter les processus et les
+flux TCP/UDP cachés par les rootkits / LKM ou par d''autres techniques de masquage.
+
+Le paquet comprend quatre utilitaires : unhide-posix, unhide-linux, unhide_rb et unhide-tcp.
+
+// unhide (unhide-posix, unhide-linux)
+// -----------------------------------
+
+Détection de processus cachés. Il met en œuvre six techniques principales
+
+1 - Comparaison de /proc avec la sortie de /bin/ps.
+
+2 - Comparaison des informations recueillies par le parcours de l'arborescence du
+ système de fichiers procfs avec les informations issues de /bin/ps .
+ Cette technique n'est disponible qu'avec la version unhide-linux.
+
+3 - Comparaison des informations collectées depuis des appels système avec les
+ informations issues de /bin/ps(syscall scanning).
+
+4 - Scan complet de l'espace des ID de processus par force brute (PIDs bruteforcing).
+ Cette technique n'est disponible qu'avec la version unhide-linux.
+
+5 - Comparaison de la sortie de /bin/ps avec /proc, le parcours de procfs et les
+ appels systèmes.
+ Recherche inverse afin vérifiez que tous les processus affichés par /bin/ps
+ existent réellement.
+ Cette technique n'est disponible qu'avec la version unhide-linux.
+
+6 - Comparaison rapide des informations recueillies dans /proc, par le parcours
+ de procfs et par lesappels systèmes avec la sortie de /bin/ps.
+ cette technique est environ 20 fois plus rapide que les 3 premières réunies
+ mais peut éventuellement donner davantage de faux positifs.
+ Cette technique n'est disponible qu'avec la version unhide-linux.
+
+// Unhide_rb
+// ---------
+
+C'est un portage en langage C de l'utilitaire unhide_rb.
+Comme l'original, il est grossièrement équivalent à "unhide-linux quick reverse" :
+- il effectue trois tests de moins (kill, opendir and chdir),
+- il lance /bin/ps seulement un fois au démarrage et une fois pour la double vérification,
+- ses tests sont moins précis (P.ex. : test de la valeur de retour au lieu de errno),
+- les processus sont uniquement identifiés par le lien sur leur exécutable (unhide-linux utilise
+ aussi la copie de la ligne de commande et le nom des "processus noyau dormant"),
+- il y a peu de protection contre les erreurs (échec de fopen ou popen par exemple),
+- il ne sait pas générer un fichier journal.
+Il est très rapide, environ 80 fois plus que "unhide-linux quick reverse"
+
+// unhide-TCP
+// ----------
+
+Sert à identifier les ports TCP ou UDP qui sont en écoute mais qui ne sont pas
+visibles par la commande /sbin/ss (ou /bin/netstat).
+Deux techniques sont employées :
+- Celle de la force brute (passage en revue de tous les ports TCP/UDP possibles)
+ et comparaison avec la sortie de SS/netstat.
+- Test de tous les ports TCP/UDP non listés par netstat.
+
+// Fichiers
+// --------
+
+unhide-linux.c -- Recherche des processus cachés, pour les systèmes Linux >= 2.6
+unhide-linux.h -- Header pour unhide-linux
+
+unhide-tcp.c -- Recherche des ports TCP/UDP cachés (ss ou netstat)
+unhide-tcp-fast.c -- Recherche des ports TCP/UDP cachés (recherche rapide)
+unhide-tcp.h -- Header pour unhide-tcp
+
+unhide_rb.c -- Portage en C de unhide.rb (une version très allégée de unhide-linux en ruby)
+
+unhide-posix.c -- Recherche des processus cachés, pour les systèmes Unix génériques (*BSD,
+ Solaris, Linux 2.2 / 2.4)
+ Il ne met en œuvre que les techniques 1 et 3. Besoin de plus de tests
+ Avertissement: Cette version est quelque peu obsolète, et peut générer
+ des faux positifs. Utilisez unhide-linux.c si c'est possible'.
+
+unhide-output.c -- Routines de sortie utilisés par les autres modules de unhide
+unhide-output.h -- Header de unhide-output
+
+changelog -- liste des évolutions apportées à unhide
+
+COPYING -- Fichier de Licence, GNU GPL V3
+
+LEEME.txt -- Version espagnole de ce fichier
+
+LISEZ-MOI.TXT -- Ce fichier
+
+NEWS -- Notes de version
+
+README.txt -- Version anglaise de ce fichier
+
+sanity.sh -- Fichier de test de unhide-linux
+
+TODO -- Liste des évolutions envisagées (des volontaires ?)
+
+man/unhide.8 -- man page en anglais de unhide
+
+man/unhide-tcp.8 -- man page en anglais de unhide-tcp
+
+man/es/unhide.8 -- man page en espagnol de unhide
+
+man/es/unhide-tcp.8 -- man page en espagnol de unhide-tcp
+
+man/fr/unhide.8 -- man page en français de unhide
+
+man/fr/unhide-tcp.8 -- man page en français de unhide-tcp
+
+
+// Compilation
+// -----------
+
+Prérequis de build
+ glibc-devel
+ glibc-static-devel
+
+Prérequis d'utilisation
+- unhide-tcp under linux :
+ iproute2
+ net-tools (for netstat)
+ lsof
+ psmisc (for fuser)
+- unhide-tcp under freeBSD :
+ sockstat
+ lsof
+ netstat
+unhide-linux, unhide-posix, unhide_rb :
+ procps
+
+
+
+Si vous utilisez un noyau Linux >= 2.6
+ gcc -Wall -O2 --static -pthread unhide-linux*.c unhide-output.c -o unhide-linux
+ gcc -Wall -O2 --static unhide_rb.c -o unhide_rb
+ gcc -Wall -O2 --static unhide-tcp.c unhide-tcp-fast.c unhide-output.c -o unhide-tcp
+ ln -s unhide unhide-linux
+
+Sinon (Linux < 2.6, *BSD, Solaris, etc.)
+ gcc --static unhide-posix.c -o unhide-posix
+ ln -s unhide unhide-posix
+
+// Utilisation
+// -----------
+Vous DEVEZ être root pour utiliser unhide
+
+Exemples:
+ # ./unhide-linux -vo quick reverse
+ # ./unhide-linux -vom procall sys
+ # ./unhide_rb
+
+ # ./unhide-tcp -flov
+ # ./unhide-tcp -flovs
+
+// Licence
+
+GPL V.3 (http://www.gnu.org/licenses/gpl-3.0.html)
+
+
+// Remerciement
+// ------------
+
+A. Ramos (aramosf@unsec.net) pour certaines expressions rationnelles
+
+unspawn (unspawn@rootshell.be) support CentOS
+
+Martin Bowers (Martin.Bowers@freescale.com) soutien CentOS
+
+Lorenzo Martinez (lorenzo@lorenzomartinez.homeip.net) pour ses idées d'amélioration et le betatesting
+
+François Marier (francois@debian.org) Auteur des pages de manuel et le support Debian
+
+Johan Walles (johan.walles@gmail.com) Identification et correction d'un bug très désagréable de concurrence critique (race condition)
+
+Jan Iven (jan.iven@cern.ch) En raison de ses grandes améliorations, de nouveaux tests et de corrections de bugs
+
+P. Gouin (patrick-g@users.sourceforge.net) En raison de son travail incroyable correction des bugs et d'amélioration des performances
+
+François Boisson pour l'idée de la double vérification dans le test "brute".
+
+Leandro Lucarella (leandro.lucarella@sociomantic.com) pour la méthode rapide de balayage et son travail de factorisation de unhide-tcp
+
+Nikos Ntarmos (ntarmos@ceid.upatras.gr) pour son aide inestimable pour le portage de unhide-tcp sur FreeBSD.
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..29aae87
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,130 @@
+Changes since 20121229 :
+**********************
+
+BUG FIXES
+ - include in unhide-output.h, some old gcc/glibc need it.
+
+SUPPORT FOR PORTING
+ - On non Linux OS, ss is not used by default by unhide-tcp.
+ This way, FreeBSD guys should be able to package without patching unhide source :)
+ - On FreeBSD, use sockstat instead of fuser.
+
+MISCELLANOUS
+
+ - The unhide files in the tarball are again contained in a directory (unhide-YYYYMMDD)
+ - The name of the tarball uses again a '-' not a '_'.
+ - Help packagers: in unhide-posix.c, unhide-output.c, unhide-tcp.c, OS specific
+ command are put between #ifdef instead of beeing commented.
+ - Correct banner of unhide-posix.
+ - Update manpages.
+ - Add build/use require list in readme files
+
+
+
+Changes since 20110113 :
+**********************
+
+IMPORTANT
+
+ - unhide-linux26.c was renamed to unhide-linux.c
+ - unhide.c was renamed to unhide-posix.c
+ - The log file of unhide-linux is renamed 'unhide-linux_AAAA-MM-DD.log'
+ - The log file of unhide-tcp is named 'unhide-tcp_AAAA-MM-DD.log'
+ - By default, unhide-tcp now use /sbin/ss from iproute2 package, to use
+ netstat as before '-n' option must be given on command line.
+ - Display is more verbose and multi-lines for hidden processes (unhide-linux).
+ - If asked to (-l and/or -f), display is more verbose and multi-lines for hidden ports (unhide-tcp).
+ - sysinfo test is no more called as part of compound quick and sys tests as it may give false positives.
+ It could still be run using the checksysinfo, checksysinfo2 or checksysinfo3 command line parameter.
+
+NEW FEATURES
+
+ - Major enhancement of unhide-tcp :
+ * Add capability to output a log file (unhide-tcp_AAA-MM-DD.log)
+ * Add capability to output more information (via lsof and/or fuser) on hidden port if available
+ * Add verbose mode (disabled by default) to display warning
+ * Add a new method (via option '-s') very fast on system with huge number of opened ports
+ * Make a double check of port access to avoid false positive (previous single check
+ version is available as unhide-tcp-simple-check.c if needed).
+ - Add a quick port in C language of unhide.rb (unhide_rb.c) and guess what ...
+ it's 40 times faster than original ruby unhide.rb
+ unhide_rb doesn't take any option.
+ - Add "-d" option for doing a double check in brute test, this reduce false positives.
+ - Add "-o" option as synonym of "-f".
+ - For found hidden processes, display the user and the working directory
+ as extracted from the process environment. Note that it doesn't work well
+ for kernel processes/threads nor for deamons.
+ - For found hidden processes, display cmdline, exe link and internal command name.
+
+MISCELLANOUS
+
+ - Add french and spanish man page for unhide-tcp
+ - Update english manpage of unhide-tcp to reflect changes
+ - Minor corrections in french manpage of unhide
+ - Display copyright and license information in start banners.
+ - Make message from sysinfo tests more clear.
+ - Add a NEWS file :)
+ - Update README.txt, LISEZ-MOI.txt and LEEME.txt to clarify difference between
+ unhide-posix and unhide-linux.
+ - Remove sysinfo test from quick and sys compound tests as it may give false positive.
+ sysinfo test still can be used via the checksysinfo[2|3] command line parameters.
+
+BUG FIXES
+
+ - Suppress pedantic compilation warnings (glibc >=2.3, gcc >=4.6).
+ - Correct the number of processes displayed for /proc counting in sysinfo test.
+
+Changes since 20100819 :
+**********************
+
+NEW FEATURES
+
+ - Add spanish man page
+ - Add additional check to checkopendir when -m is specified.
+ - Add a option (-f) to create a log file.
+ - Add checkopendir test (also called by procfs and procall compound test)
+ - Also do opendir() test in reverse and quick tests.
+ - Add alternate sysinfo test (via -r option or checksysinfo2 test name)
+ - Make the output of hidden process on one line to facilitate parsing
+ - Display wchan if there is no cmdline and no exe link (sleeping kernel threads)
+ - Add -V version to show version and exit.
+ - The -v option can now be given more than once on command line : management of several verbosity level.
+ - Now several tests can be simultaneously entered on the command line.
+ - Add all elementary tests to the command line test list
+ - Add procall compound test command line args.
+ - Check for our own spawn ps process in reverse test to avoid false positive.
+ - Enhanced fake process detection in reverse test.
+
+BUG FIXES
+
+ - Correct warning message in additional check of checkchdir.
+ - Close log file only if it is open.
+ - Correct the value returned by unhide
+ - Add the misssing new lines in most of the warnings (thanks to gordy for the report).
+ - Check the return of fgets in checkallreverse(), check of feof seems not to be
+ very reliable for a pipe, we sometime got the last line 2 times (thanks to gordy for the report).
+ - Correct an initialized fd use, that gcc don't report when -O2 isn't given on command line
+
+DEVELOPER ISSUES
+
+ - Minor readability when generating program info for display
+ - Factorize (f)printf to stdout & log.
+ - Add a preliminary testsuite for unhide (sanity.sh)
+ - Use printbadpid() in checkallnoprocps() as in other tests.
+ - Also check it in checksysinfo & checksysinfo2
+ - Simplify and clarify test checksysinfo()
+ - Redo args parsing : Manage multiple args on command line and several verbosity levels.
+ - Add a tests table to allow new command line parsing.
+ - Correct a copy/past "typo", in checkps
+ - Minor optimizations of printf & sprintf calls.
+
+MISCELLANOUS
+
+ - Add a NEWS file
+ - Add GPL disclaimer to source files
+ - Add french LISEZ-MOI.txt file
+ - Add reference to new unhide site in version string
+ - Add a warning about the generic version of unhide in README.txt (thanks to gordy for the report)
+ - Modify man page to add the -V option, correct typos and clarify quick test.
+ - Add -O2 option to compiling command line in README.txt
+ - Add a TODO file
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..ec7f5b7
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,164 @@
+**-Unhide-**
+ http://www.unhide-forensics.info
+
+Unhide is a forensic tool to find hidden processes and TCP/UDP ports by rootkits / LKMs
+or by another hiding technique.
+
+// Unhide (unhide-linux or unhide-posix)
+// -------------------------------------
+
+Detecting hidden processes. Implements six main techniques
+
+1- Compare /proc vs /bin/ps output
+
+2- Compare info gathered from /bin/ps with info gathered by walking thru the procfs. ONLY for unhide-linux version
+
+3- Compare info gathered from /bin/ps with info gathered from syscalls (syscall scanning).
+
+4- Full PIDs space ocupation (PIDs bruteforcing). ONLY for unhide-linux version
+
+5- Compare /bin/ps output vs /proc, procfs walking and syscall. ONLY for unhide-linux version
+ Reverse search, verify that all thread seen by ps are also seen in the kernel.
+
+6- Quick compare /proc, procfs walking and syscall vs /bin/ps output. ONLY for unhide-linux version
+ It's about 20 times faster than tests 1+2+3 but maybe give more false positives.
+
+// Unhide_rb
+// ---------
+
+It's a back port in C language of the ruby unhide.rb
+As the original unhide.rb, it is roughly equivalent to "unhide-linux quick reverse" :
+- it makes three tests less (kill, opendir and chdir),
+- it only run /bin/ps once at start and once for the double check,
+- also, its tests are less accurate (e.g.. testing return value instead of errno),
+- processes are only identified by their exe link (unhide-linux also use cmdline and
+ "sleeping kernel process" name),
+- there's little protection against failures (failed fopen or popen by example),
+- there's no logging capability.
+It is very quick, about 80 times quicker than "unhide-linux quick reverse"
+
+// Unhide-TCP
+// ----------
+
+Identify TCP/UDP ports that are listening but not listed in sbin/ss or /bin/netstat.
+It use two methods:
+- brute force of all TCP/UDP ports availables and compare with SS/netstat output.
+- probe of all TCP/UDP ports not reported by netstat.
+
+// Files
+// -----
+
+unhide-linux.c -- Hidden processes, for Linux >= 2.6
+unhide-linux.h
+
+unhide-tcp.c -- Hidden TCP/UDP Ports
+unhide-tcp-fast.c
+unhide-tcp.h
+
+unhide-output.c -- Common routines of unhide tools
+unhide-output.h
+
+unhide_rb.c -- C port of unhide.rb (a very light version of unhide-linux in ruby)
+
+unhide-posix.c -- Hidden processes, for generic Unix systems (*BSD, Solaris, linux 2.2 / 2.4)
+ It doesn't implement PIDs brute forcing check yet. Needs more testing
+ Warning : This version is somewhat outdated and may generate false positive.
+ Prefer unhide-linux.c if you can use it.
+
+changelog -- As the name implied log of the change to unhide
+
+COPYING -- License file, GNU GPL V3
+
+LEEME.txt -- Spanish version of this file
+
+LISEZ-MOI.TXT -- French version of this file
+
+NEWS -- Release notes
+
+README.txt -- This file
+
+sanity.sh -- unhide-linux testsuite file
+
+TODO -- Evolutions to do (any volunteers ?)
+
+man/unhide.8 -- English man page of unhide
+
+man/unhide-tcp.8 -- English man page of unhide-tcp
+
+man/fr/unhide.8 -- French man page of unhide
+
+man/fr/unhide-tcp.8 -- French man page of unhide-tcp
+
+// Compiling
+// ---------
+
+Build requires
+ glibc-devel
+ glibc-static-devel
+
+Require
+- unhide-tcp under linux :
+ iproute2
+ net-tools (for netstat)
+ lsof
+ psmisc (for fuser)
+- unhide-tcp under freeBSD :
+ sockstat
+ lsof
+ netstat
+
+unhide-linux, unhide-posix, unhide_rb :
+ procps
+
+
+If you ARE using a Linux kernel >= 2.6
+ gcc -Wall -O2 --static -pthread unhide-linux*.c unhide-output.c -o unhide-linux
+ gcc -Wall -O2 --static unhide_rb.c -o unhide_rb
+ gcc -Wall -O2 --static unhide-tcp.c unhide-tcp-fast.c unhide-output.c -o unhide-tcp
+ ln -s unhide unhide-linux
+
+Else (Linux < 2.6, *BSD, Solaris and other Unice)
+ gcc --static unhide-posix.c -o unhide-posix
+ ln -s unhide unhide-posix
+
+// Using
+// -----
+You MUST be root to use unhide-linux and unhide-tcp.
+
+Examples:
+ # ./unhide-linux -vo quick reverse
+ # ./unhide-linux -vom procall sys
+ # ./unhide_rb
+
+ # ./unhide-tcp -flov
+ # ./unhide-tcp -flovs
+
+// License
+// -------
+
+GPL V.3 (http://www.gnu.org/licenses/gpl-3.0.html)
+
+// Greets
+// ------
+
+A. Ramos (aramosf@unsec.net) for some regexps
+
+unspawn (unspawn@rootshell.be) CentOS support
+
+Martin Bowers (Martin.Bowers@freescale.com) CentOS support
+
+Lorenzo Martinez (lorenzo@lorenzomartinez.homeip.net) Some ideas to improve and betatesting
+
+Francois Marier (francois@debian.org) Author of the man pages and Debian support
+
+Johan Walles (johan.walles@gmail.com) Find and fix a very nasty race condition bug
+
+Jan Iven (jan.iven@cern.ch) Because of his great improvements, new tests and bugfixing
+
+P. Gouin (patrick-g@users.sourceforge.net) Because of his incredible work fixing bugs and improving the performance
+
+François Boisson for his idea of a double check in brute test
+
+Leandro Lucarella (leandro.lucarella@sociomantic.com) for the fast scan method and his factorization work for unhide-tcp
+
+Nikos Ntarmos (ntarmos@ceid.upatras.gr) for its invaluable help in the FreeBSD port of unhide-tcp and for packaging unhide on FreeBSD.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..e4a1ed8
--- /dev/null
+++ b/TODO
@@ -0,0 +1,25 @@
+[TODO]
+- Integrate -m in other test,
+- Try to factorize the code,
+- More optimizations,
+- Add a mail sending option,
+- Beautify the source (add comments, function headers, etc.),
+- Add a version number beside date of release,
+- Localize (have you already used gettext ?), create man pages in other language
+- Add an install script or use autotools/cmake/something else.
+- Upgrade the generic version of unhide with some of the enhancements of the
+ linux26 version.
+- Others ...
+
+
+[DONE]
+- Make a meta proc test that involves all /proc test (proc and procfs)
+- Add more verbosity level,
+- Sanitize the exit code,
+- Make a better command line parsing (without getopt:),
+- Put it in a CVS/SVN/Git repo, unhide is on sourceforge.
+- Create a TODO file :), you're reading it
+- Add an option (-x for expert ?) which allows to run subtest individually.
+ Done without the use of a special option
+- Add a option (-f ) to create a log file, (not everybody use unhide via RKH)
+- Add a test script to reliably test new version (preliminary version).
diff --git a/changelog b/changelog
new file mode 100644
index 0000000..eca8707
--- /dev/null
+++ b/changelog
@@ -0,0 +1,370 @@
+2013-05-26
+ unhide-posix.c
+ - Transform 'ret' in global variable to avoid warnings
+ (note: ret variable was added to avoid warnings with some over pedantic
+ version of glibc and is otherwise useless).
+
+2013-05-24
+ unhide-tcp.8 (spanish version), LEEME.txt
+ - update according to english version.
+
+2013-03-03
+ unhide-posix.c
+ - Bugfix : Correct app name in banner of unhide-posix.
+
+ unhide-tcp.c
+ - Continue to simplify packager job:
+ * on FreeBSD use sockstat instead of fuser, which doesn't show info on internet socket
+ on this system.
+
+ README.txt, LISEZ-MOI.txt
+ - Add list of build-requires and use-requires
+
+ unhide-tcp.8 (french and english version)
+ - Add notes upon FreeBSD.
+
+2013-02-03
+ unhide-output.h
+ - Bugfix : include , some old glibc need it
+
+ unhide-posix.c, unhide-output.c, unhide-tcp.c
+ - Simplify packager job:
+ * put OS specific command between #ifdef (they were previously commented),
+ * don't use ss by default in unhide-tcp if OS is not linux,
+ * on FreeBSD use sockstat instead of fuser, which doesn't show info on internet socket
+ on this system.
+
+ make_tarball.sh
+ - Change '_' to '-' in the name of the tarball
+ - Make sure that unhide files are in a unhide-YYYYMMDD directory.
+
+2012-12-29
+ Promote unhide-tcp-double_check.c as official version of unhide-tcp. Old version
+ is still available as unhide-tcp-simple-check.c
+
+ unhide-linux, unhide-posix, unhide-tcp, unhide-tcp-simple-check, unhide_rb :
+ - update date of the version for official release.
+
+
+2012-12-18
+ unhide-linux, unhide-posix, unhide-tcp, unhide_rb :
+ - update date of the version
+ unhide-tcp :
+ - Suppress 1 warning with some over pedantic version of glibc.
+
+2012-12-12
+ unhide-linux :
+ - In unhide-linux-syscall, transform ret in global variable to avoid warning
+ (note ret variable was added to avoid warning with some over pedantic version of glibc
+ ans is otherwise useless).
+ Correct sched_getaffinity test in checkallnoprocps (it tested ret instead of errno).
+ unhide-tcp :
+ - Avoid to display the banner twice.
+ unhide_rb :
+ - Suppress warning.
+
+2012-12-07
+ unhide-linux :
+ - Remove sysinfo from quick and sys test as it may give false positive.
+
+ unhide-tcp :
+ - Nice ourself to -20 to limit race condition while probing ports.
+
+2012-10-07
+ unhide-linux :
+ - Go back to multi-lines output in printbadpid in order to display more known
+ information about the process.
+
+2012-10-03
+ unhide-linux :
+ - Fix the name displayed for kernel thread (we used /proc/PID/wchan instead of
+ /proc/PID/comm).
+
+2012-09-05
+ unhide-linux, unhide-tcp :
+ - Add test to verify we're run by root.
+
+2012-09-02
+ unhide-linux :
+ - Remove useless calls to feof().
+ - Split unhide-linux.c in 5 files :
+ * unhide-linux-bruteforce.c
+ * unhide-linux.c
+ * unhide-linux-compound.c
+ * unhide-linux-procfs.c
+ * unhide-linux-syscall.c
+ - Add option '-o' as synonym for '-f'
+ - Add a parse_arg() function which use getopt_long().
+ - For found hidden processes, display the user and the working directory
+ as extracted from the process environment.
+
+2012-08-31
+ unhide-linux :
+ - Use unhide-output routines for display and log.
+ - Change logfile filename to 'unhide-linux_AAAA-MM-DD.log'
+ - Add header file for unhide-linux
+
+2012-08-22
+ unhide-tcp :
+ - Change the default tools to be ss instead of netstat.
+ - Replace option '-s' (use ss) by option '-n' (use netstat).
+ - Change option '-q' in '-s' with the same effect
+
+2012-06-03
+ unhide-tcp :
+ - Thanks to a patch of Leandro Lucarella and additional work from
+ the unhide team, a major rewriting was done :
+ * Factorization & clean-up of the code
+ * Split the code in 4 files : unhide-tcp.c, unhide-fast.c, unhide-output.c
+ & unhide.h
+ * Add a new method for scanning ports via option '-q'
+ - Add a option '-s' to use ss command instead of nestat.
+ - Use getopt_long() to parse options and then add long option strings.
+ - Change logfile filename to 'unhide-tcp_AAAA-MM-DD.log'
+ - Many minor bug fixes (mainly display ones)
+
+2012-03-18
+ unhide-linux26.c, unhide-posix.c, unhide-tcp.c :
+ - Change copyright attribution.
+
+ unhide_rb.c :
+ - Add banner display at start.
+
+ unhide-linux26.c :
+ - Change reserved process reserved for kernel from 299 to 300 for brute test.
+ - Add "-d" option for doing a double check in brute test, this reduce false positive number.
+ Thanks to François Boisson for the idea.
+ - Change log file name to unhide-linux.log
+
+ Documentation changes :
+ - Add example section in manpages.
+ - Indicate in bug section of manpages, the potential problem with sysinfo test.
+
+2012-03-17
+ Important changes :
+ - Rename unhide-linux26.c to unhide-linux.c and unhide.c to unhide-posix.c.
+ - Update readme files and manpages to reflect the renaming
+ - Add unhide_rb description to readme files.
+
+2012-03-11
+ unhide-linux26.c :
+ - Correct the number of processes displayed for /proc counting in sysinfo test.
+
+ unhide.c :
+ - Correct banner (POSIX -> UNIX).
+
+ Documentation changes :
+ - Update README.txt, LISEZ-MOI.txt and LEEME.txt to clarify difference between
+ unhide and unhide-linux26.
+
+2012-03-10
+ unhide-linux26.c :
+ - Fix pedantic compilation warnings reported when using recent version of glibc.
+ - Change report messages of checksysinfoX tests to make them clearer.
+ - Update banner to indicate this version is for system using Linux >= 2.6
+
+ unhide.c :
+ - Update banner to indicate this is legacy version of unhide for system using
+ Linux < 2.6 or other UNIX system.
+ - Fix compilation warnings
+
+2011-10-31
+ unhide-linux26.c :
+ - Add copyright and license output.
+
+ unhide-tcp.c :
+ - Add copyright and license output.
+ - Add -v, -V, -h, -l, -f, -o command line options.
+ - Add the capability to output fuser (-f) and/or lsof (-l) output for hidden port.
+ - Add the capability to create a log file (-o). File name is unhide-tcp.log
+
+ Documentation changes :
+ - Add a french manpage for unhide-tcp.
+ - Complete english manpage of unhide-tcp to reflect changes.
+ - Minor corrections in french manpage of unhide.
+ - Change compile command of unhide-tcp in README.txt, LISEZ-MOI.txt and LEEME.txt.
+ - Add info on unhide_rb in README.txt, LISEZ-MOI.txt and LEEME.txt.
+ - Update NEWS file.
+
+2011-02-08
+ Documentation changes :
+ - Add a NEWS file
+
+2011-01-13
+ All files :
+ - Replace reference to SourceForge with reference to new unhide web site in version string
+
+ man pages :
+ - Add spanish man pages
+
+2010-11-21
+ unhide-linux26.c :
+ Development changes :
+ - Minor readability when generating program info for display
+
+2010-11-21
+ unhide-linux26.c :
+ User visible changes :
+ - Add additional check to checkopendir when -m is specified.
+ - Correct warning message in additional check of checkchdir.
+ - Add sourceForge project URL in header
+
+ unhide.c :
+ - Add GPL disclaimer.
+
+ unhide-tcp.c :
+ - Add GPL disclaimer.
+
+ Documentation changes :
+ changelog :
+ - Fix an omission in 2010-11-14 Internal changes
+
+ man pages : Development changes :
+
+ - update french and english man pages wrt '-m' option and checkopendir
+
+ Development changes :
+ - Correct message of test#1 of sanity.sh
+ - Use procall in test#2 of sanity.sh instead of proc
+
+2010-11-14
+ unhide-linux26.c :
+ User visible changes :
+ - Add ending time to log file.
+ - Add execution header to log file.
+ - Change date format to ISO 8601 one's in log file.
+ - Add warning, when selected, to log file.
+ - Update english and french man page to reflect the add of '-f' option.
+
+ Internal changes
+ - Close log file only if it is open.
+ - Factorize (f)printf to stdout & log.
+
+ Documentation changes :
+ README.txt & LISEZ-MOI.TXT
+ - Minor clarifications.
+ - Add description of all the files included in unhide
+
+ Development changes :
+ - Add a preliminary testsuite for unhide (sanity.sh)
+
+2010-11-09
+ unhide-linux26.c :
+ User visible changes :
+ - Add a option (-f) to create a log file.
+
+2010-10-16
+ Documentation changes :
+ LEEME.txt :
+ Correct compilation instruction.
+ Add reference to sourceforge site.
+
+ README.txt
+ Add reference to sourceforge site.
+ Correct typo.
+
+ LISEZ-MOI.TXT
+ Ajout du fichier
+
+2010-09-23
+ unhide-linux26.c :
+ User visible changes :
+ - Add reference to sourceforge path to version string
+
+ Documentation changes :
+ - Update man page to reflect all the change made so far.
+
+2010-09-23
+ unhide-linux26.c :
+ User visible changes :
+ - Add checkopendir test (also called by procfs and procall compound test)
+ - Also do opendir() test in reverse and quick tests.
+ - Add alternate sysinfo test (via -r option or checksysinfo2 test name)
+ It's a reorganised checksysinfo() to put uncritical instructions out of the critical part
+ It might (or not) work better on kernel patched for RT, preemption or latency.
+ - Make the output of hidden process on one line to facilitate parsing
+ - Display wchan if there is no cmdline and no exe link (sleeping kernel threads)
+ - Add -V version to show version and exit.
+ - The -v option can now be given more than once on command line.
+ - Correct the value returned by unhide
+ - Add the misssing new lines in most of the warnings (thanks to gordy for the report).
+ - Completely redo args parsing : now several tests can be simultaneously
+ entered on the command line.
+ - Add all elementary tests to the command line test list
+ - Add procall compound test command line args.
+
+ Internal changes
+ - Use printbadpid() in checkallnoprocps() as in other tests.
+ - Check the return of fgets in checkallreverse(), check of feof seems not to be
+ very reliable for a pipe, we sometime got the last line 2 times (thanks to gordy for the report).
+ - Also check it in checksysinfo & checksysinfo2
+ - Simplify and clarify test checksysinfo()
+ - Check for our own spawn ps process in reverse test to avoid false positive.
+ - Enhanced fake process detection in reverse test.
+ - Add a tests table to allow new command line parsing.
+ - Add management of several verbosity level.
+ - Correct a copy/past "typo", in checkps
+ - Correct an initialized fd use, that gcc don't report when -O2 isn't given on command line
+ - Minor optimizations of printf & sprintf calls.
+
+ Documentation changes :
+ - Add a warning about the generic version of unhide in README.txt (thanks to gordy for the report)
+ - Modify man page to add the -V option, correct typos and clarify quick test.
+ - Add -O2 option to compiling command line in README.txt
+ - Add a TODO file
+
+2010-08-19
+ unhide-linux26.c :
+ - Add GPL v3 Disclaimer
+ - Add new test 'procfs' (via readdir & chdir)
+ - Add new test 'reverse'
+ - Add new test 'quick'
+ - Add option verbose (-v) to allow warning display
+ - Add option morecheck (-m), only affect procfs test for now
+ - Add option help (-h)
+ - Displace usage in usage() function
+ - Add Changelog file (this file)
+ - Rewamp command line parsing in main()
+ - Change checkps() parameter to allow more scalability
+ - Minor optimization in brute(), we tried to create 300 more processes than available.
+ - Minor optimization : avoid to test our own PID
+ - Update the man page and README.txt to reflect changes.
+
+2010-02-01
+ unhide-linux26.c :
+ - Threads Brute Force added
+ - Add needed stuff (includes, defines, ...) to eliminate compilation warning. (Thanks to J. Walles)
+ - Correct a typo in checkps() where fich_tmp is used in place of fich_pgid (Thanks to P. Gouin)
+ - Corrected several FD leaks where files or pipes are read and closed even if they have failed to open. (Thanks to W. Doekes & P. Gouin)
+ - Add warning messages if file or pipe fails to open (compatible with rkhunter use of unhide) (Thanks to W. Doekes & P. Gouin)
+ - Add warning messages if a test is skipped (compatible with rkhunter use of unhide). (Thanks to P. Gouin)
+ - Correct removing of leading spaces which tests one char too far for end of string in checkps(). (Thanks to P. Gouin)
+ - Close fd in get_max_pid(). (Thanks to P. Gouin)
+ - Close cmd_file in printbadpid(). (Thanks to P. Gouin)
+ - Add display of test name in checkallnoprocps(). (Thanks to P. Gouin)
+ - Close fich_processo in checksysinfo() (Thanks to W. Doekes)
+ - Avoid potential buffer overflow in checksysinfo() (Thanks to W. Doekes)
+ - Correct allpids[] initialization in brute() (Thanks to W. Doekes)
+ - Modify brute as modifying allpid from within the forked process may have undefined results (Linux vfork() man page) (Thanks to P. Gouin)
+ - Add return to main() (Thanks to W. Doekes)
+ - Optimizations (Thanks to P. Gouin)
+
+2009-08-10 (BETA)
+-Improved maxpid routine (Thanks to Jan Iven)
+-Improved false positives detection (Thanks to Jan Iven)
+-Kill() syscall added (Thanks to Jan Iven)
+-Fixed sched_getaffinity() bug (Thanks to Jan Iven)
+-Some minor bug fixes
+
+2008-05-19
+-Fixed a race condition bug that showed false positives (Thanks to Johan Walles)
+-Added manpages (Thanks to Francois Marier)
+
+02-11-2007
+-Minor bugfixes
+-License added
+-sysinfo() syscall added
+
+28-12-2005
+-Initial Release
+
diff --git a/make_tarball.sh b/make_tarball.sh
new file mode 100755
index 0000000..8ecee54
--- /dev/null
+++ b/make_tarball.sh
@@ -0,0 +1,27 @@
+#! /bin/sh
+TAR_DATE=`date +%Y%m%d`
+echo $TAR_DATE
+TAR_FILE="unhide-$TAR_DATE"
+echo $TAR_FILE
+
+if [ -e "../$TAR_FILE" ]; then
+ echo "../$TAR_FILE already exists, do you want to delete it and continue [yN] ?"
+ read DEL_DIR
+ if [ $DEL_DIR == "Y" -o $DEL_DIR == "y" ]; then
+ if [ -d "../$TAR_FILE" ]; then
+ echo "\rm -rf ../$TAR_FILE"
+ else
+ echo "\rm -f ../$TAR_FILE"
+ fi
+ else
+ exit 1
+ fi
+else
+ echo "../$TAR_FILE n'existe pas"
+fi
+mkdir -p ../$TAR_FILE/man/es ../$TAR_FILE/man/fr
+for FILE in `cat tar_list.txt`; do
+ cp $FILE ../$TAR_FILE/$FILE
+done
+tar -czvf $TAR_FILE.tgz ../$TAR_FILE
+mv $TAR_FILE.tgz ../$TAR_FILE
diff --git a/man/es/unhide-tcp.8 b/man/es/unhide-tcp.8
new file mode 100644
index 0000000..9a993c3
--- /dev/null
+++ b/man/es/unhide-tcp.8
@@ -0,0 +1,79 @@
+.TH "UNHIDE-TCP" "8" "August 2012" "Administration commands" ""
+.SH "NOMBRE"
+unhide\-tcp \(em Herramienta forense para localizar puertos TCP/UDP ocultos
+.SH "SYNOPSIS"
+.PP
+\fBunhide\-tcp [opciones]\fR
+.SH "DESCRIPCIÓN"
+.PP
+\fBunhide\-tcp\fR es una herramienta forense capaz de identificar puertos
+TCP/UDP que están a la escucha pero no aparecen listados en /sbin/ss (o alternativamente
+/bin/netstat) haciendo fuerza bruta en todo el espacio de puertos TCP/UDP disponibles
+.br
+Nota1 : Tanto en FreeBSD como en OpenBSD el comando netstat será siempre la opción elegida
+puesto que iproute2 no está disponible. Además en FreeBSD se usará sockstat en lugar de fuser
+
+Nota2: Si el comando iproute2 no se encuentra disponible en el sistema la opción \-n o \-s DEBE estar
+entre los flags con los que es llamado unhide\-tcp
+.PP
+.SH "OPCIONES"
+.TP
+\fB\-h \-\-help\fR
+Muestra la ayuda
+.TP
+\fB\-\-brief\fR
+No muestra mensajes de error. Este es el comportamiento por defecto
+.TP
+\fB\-f \-\-fuser\fR
+Muestra la salida del comando fuser (si se encuentra en el sistema) del puerto oculto
+Para FreeBSD, en lugar de comando fuser, muestra la salida del comando sockstat del puerto oculto
+.TP
+\fB\-l \-\-lsof\fR
+Muestra la salida del comando lsof (si se encuentra en el sistema) del puerto oculto
+.TP
+\fB\-n \-\-netstat\fR
+Emplea /bin/netstat en vez de /sbin/ss. En sistemas con muchos puertos abiertos usar esta opción
+puede provocar que el test resulte excesivamente lento
+.TP
+\fB\-s \-\-server\fR
+Usa un método de escaneo muy rápido. En sistemas con muchos puertos abiertos este test es
+cientos de veces más rápido que si se usa el comando ss y miles de veces más rápido que si se usa
+el comando netstat
+.TP
+\fB\-o \-\-log\fR
+Genera un fichero de log (unhide\-tcp\-AAAA\-MM\-DD.log) en el directorio donde se ejecuta el comando
+.TP
+\fB\-V \-\-version\fR
+Muestra la versión y sale
+.TP
+\fB\-v \-\-verbose\fR
+Muestra mucha información así como los mensajes de advertencia. Esta opción puede usarse varias veces
+.PP
+.SS "Exit status:"
+.TP
+0
+Si no se ha encontrado ningún puerto oculto,
+.TP
+4
+Si uno o varios puertos TCP ocultos son localizados,
+.TP
+8
+Si uno o varios puertos UDP ocultos son encontrados
+.TP
+12
+Si uno o varios puertos TCP y UDP ocultos son encontrados
+.PP
+.SH "VÉASE TAMBIÉN"
+.PP
+unhide (8).
+.SH "AUTOR"
+.PP
+Este manual ha sido creado por Francois Marier francois@debian.org y Patrick Gouin.
+Se concede permiso para ser copiado, distribuido y modificado bajo los términos de la licencia
+GNU, versión 3 o versiones posteriores publicadas por la Free Software Foundation
+.SH "LICENCIA"
+License GPLv3+: GNU GPL version 3 or later .
+.br
+License GPLv3+: GNU GPL version 3 o posterior .
+Este es software libre, vd es libre de modificar y redistribuir las modificaciones.
+Este software no provee ninguna garantía .
diff --git a/man/es/unhide.8 b/man/es/unhide.8
new file mode 100644
index 0000000..41f4d05
--- /dev/null
+++ b/man/es/unhide.8
@@ -0,0 +1,196 @@
+.TH "UNHIDE" "8" "Marzo 2012" "Comandos de administración" ""
+.SH "NOMBRE"
+unhide \(em Herramienta forense para descubrir procesos ocultos
+.SH "SINOPSIS"
+.PP
+\fBunhide\-linux\fR [\fIOPTIONS\fR] \fITEST_LIST\fR
+.br
+\fBunhide\-posix\fR \fIproc | sys\fR
+.SH "DESCRIPCIÓN"
+.PP
+\fBunhide\fR es una herramienta forense para detectar procesos ocultos en sistemas Unix que hayan sido ocultados mediante rookits / módulos en el Kernel o cualquier otra técnica de ocultación. Implementa seis técnicas de detección
+.PP
+.SH "OPCIONES"
+.PP
+Opciones sólo están disponibles para \fBunhide\-linux\fR no para \fBunhide\-posix\fR.
+.TP
+\fB\-d\fR
+Realiza una prueba doble control en test 'brute' para reducir la aparición de falsos positivos.
+.TP
+\fB\-f\fR
+Crea un fichero de log (unhide\-linux.log) en el directorio de ejecución.
+.TP
+\fB\-h\fR
+Muestra la ayuda
+.TP
+\fB\-m\fR
+Realiza múltiples tests añadidos, desde la versión 2012\-03\-17, esta opción solo es válida en los tests 'procfs', 'procall', 'checkopendir' y 'checkchdir'
+.br
+Implica \-v
+.TP
+\fB\-r\fR
+Emplea una versión alternativa del test sysinfo
+.TP
+\fB\-V\fR
+Muestra la versión y sale
+.TP
+\fB\-v\fR
+Fuerza la salida debug con los mensajes de error (se puede repetir varias veces \-vv)
+.PP
+.PP
+.SH "TEST_LIST"
+.PP
+Los checks consisten en uno o mas de los siguientes tests
+.br
+Los tests estandar son en realidad agrupaciones de varios tests elementales
+.PP
+\fBTests estandar :\fR
+.PP
+El test \fIbrute\fR consiste en hacer fuerza bruta sobre todo el espacio de identificadores de procesos (PIDS)
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIproc\fR consiste en comparar el directorio /proc con la salida del comando /bin/ps
+.PP
+El test \fIprocall\fR combina los tests proc y procfs
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIprocfs\fR consiste en comparar la información obtenida por /bin/ps con los datos obtenidos al recorrer procfs
+.br
+Con la opción \fB\-m\fR este test realiza tests adicionales, para mas información consultar \fIcheckchdir\fR
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIquick\fR combina los tests proc, procfs y sys en una forma 'rápida' de ejecución, es hasta 20 veces mas rápido que otros test pero también puede ofrecer mas falsos positivos
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIreverse\fR consiste en verificar que todos los procesos e hilos que son vistos por /bin/ps existen realmente buscandolos en procfs y por system calls. El objetivo es averiguar si se ha modificado /bin/ps para hacer creer que existen en ejecución algunos programas que realmente no lo están
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIsys\fR consiste en comparar la información obtenida de /bin/ps contra algunas system calls
+.PP
+\fBTests elementales :\fR
+.PP
+El test \fIcheckbrute\fR consiste en hacer fuerza bruta contra todo el espacio de procesos (PIDS) del sistema
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIcheckchdir\fR consiste en comparar la información obtenida por /bin/ps y compararla haciendo chdir() en procfs
+.br
+Con la opción \fB\-m\fR también se comprueba que el hilo aparece en la lista "leader process"
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIcheckgetaffinity\fR consiste en comparar la información obtenida por /bin/ps con el resultado de la system call sched_getaffinity()
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIcheckgetparam\fR consiste en comparar la información obtenida por /bin/ps con el resultado de la system call sched_getparam()
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIcheckgetpgid\fR consiste en comparar la información obtenida por /bin/ps con el resultado de la system call getpgid()
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIcheckgetprio\fR consiste en comparar la información obtenida por /bin/ps con el resultado de la system call getpriority()
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIcheckRRgetinterval\fR consiste en comparar la información obtenida por /bin/ps con el resultado de la system call sched_rr_get_interval()
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIcheckgetsched\fR consiste en comparar la información obtenida por /bin/ps con el resultado de la system call sched_getscheduler()
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIcheckgetsid\fR consiste en comparar la información obtenida por /bin/ps con el resultado de la system call getsid()
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIcheckkill\fR consiste en comparar la información obtenida por /bin/ps con el resultado de la system call kill()
+.br
+Nota: ningún proceso es 'matado' con este test
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIchecknoprocps\fR consiste en comparar el resultado de la información obtenida usando cada una de las system calls buscando diferencias entre si. No se emplea /bin/ps o /proc
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIcheckopendir\fR consiste en comparar la información obtenida por /bin/ps con el resultado de hacer opendir() sobre procfs
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIcheckproc\fR consiste en comparar la información obtenida por /bin/ps con los datos de /proc
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIcheckquick\fR combina los tests proc, procfs y sys en una forma 'rápida' de ejecución, es hasta 20 veces mas rápido que otros test pero también puede ofrecer mas falsos positivos
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIcheckreaddir\fR consiste en comparar la información obtenida por /bin/ps con el resultado de hacer readdir() en /proc y /proc/pid/task
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIcheckreverse\fR consiste en verificar que todos los procesos e hilos que son vistos por /bin/ps existen realmente buscandolos en procfs y por system calls. El objetivo es averiguar si se ha modificado /bin/ps para hacer creer que existen en ejecución algunos programas que realmente no lo están
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIchecksysinfo\fR consiste en comparar el número de procesos contabilizados por /bin/ps contra el número de procesos que indica la syscall sysinfo()
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.PP
+El test \fIchecksysinfo2\fR es una versión alternativa de checksysinfo, se presupone que funciona mejor en kernels parcheados para RT, preempt o latency y también con kernels que no usen el planificador estandar
+.br
+Este test está implicito cuando se ejecuta con la opción \fB\-r\fR
+.br
+Esta técnica solo está disponible con la versión unhide\-linux.
+.SS "Valor regresado:"
+.TP
+0
+si todo OK,
+.TP
+1
+si se ha localizado un proceso/hilo oculto o falso
+.PP
+.SH "EJEMPLOS"
+.TP
+Un test excepcionalmente rápido :
+unhide quick
+.TP
+Test rápido :
+unhide quick reverse
+.TP
+Estándar test :
+unhide sys proc
+.TP
+Un test completo :
+unhide \-m \-d sys procall brute reverse
+.SH "FALLOS"
+.PP
+Puedes reportar fallos de \fBunhide\fR en el 'bug tracker' de Sourceforge (http://sourceforge.net/projects/unhide/)
+.br
+Con las últimas versiones de kernel de Linux (> 2.6.33), el test sysinfo puede reportar falsos positivos.
+Puede ser debido a la optimización en el scheduler, el uso de cgroup o incluso el uso de systemd.
+El uso del patch PREEMPT\-RT amplifica la probabilidad de que se de ese problema.
+Esto es actualmente objeto de investigación.
+.SH "VÉASE TAMBIÉN"
+.PP
+unhide\-tcp (8).
+.SH "AUTOR"
+.PP
+Este manual ha sido creado por Francois Marier francois@debian.org y Patrick Gouin.
+Se concede permiso para ser copiado, distribuido y modificado bajo los términos de la licencia
+GNU, versión 3 o versiones posteriores publicadas por la Free Software Foundation
+
+.SH "LICENSE"
+License GPLv3+: GNU GPL version 3 o posterior .
+.br
+Este es software libre, vd es libre de modificar y redistribuir las modificaciones.
+Este software no provee ninguna garantía .
diff --git a/man/fr/unhide-tcp.8 b/man/fr/unhide-tcp.8
new file mode 100644
index 0000000..ff05a05
--- /dev/null
+++ b/man/fr/unhide-tcp.8
@@ -0,0 +1,79 @@
+.TH "UNHIDE-TCP" "8" "Août 2012" "Commandes d'administration"
+.SH "NOM"
+unhide-tcp \(em outil d'investigation post-mortem pour trouver des ports TCP/UDP cachés
+.SH "SYNOPSIS"
+.PP
+\fBunhide-tcp [options]\fR
+.SH "DESCRIPTION"
+.PP
+\fBunhide-tcp\fR est un outil d'investigation post-mortem qui identifie les ports
+TCP/UDP qui sont à l'écoute mais qui ne sont pas listés par /sbin/ss (ou
+alternativement par /bin/netstat) en utilisant la force brute : ouverture de
+tous les ports TCP/UDP existants.
+.br
+Note1 : sur FreeBSD et OPENBSD, netstat est systématiquement utilisé iproute2 n'existant pas
+sur ces systèmes. De plus sur FreeBSD, sockstat est utilisé à la place de fuser.
+Note2 : si iproute2 n'est pas installé sur le système, une des option -n ou - s
+DOIT être utilisée sur la ligne de commande.
+.PP
+.SH "OPTIONS"
+.TP
+\fB\-h\fR
+Affiche l'aide.
+.TP
+\fB\--brief\fR
+N'affiche pas les messages d'avertissement, c'est le comportement par défaut.
+.TP
+\fB\-f --fuser\fR
+Affiche la sortie de fuser (si elle est disponible) pour les ports cachés.
+Sur FreeBSD, affiche, à la place, la sortie de sockstat pour les ports cachés.
+.TP
+\fB\-l --lsof\fR
+Affiche la sortie de lsof (si elle est disponible) pour les ports cachés.
+.TP
+\fB\-n --netstat\fR
+Utilise /bin/netstat au lieu de /sbin/ss. Sur les systèmes où un grand nombre de ports sont ouverts,
+cela peut ralentir le test de façon dramatique.
+.TP
+\fB\-o --log\fR
+Enregistre les sorties dans un fichier de log (unhide-tcp-AAAA-MM-JJ.log) situé dans le répertoire courant.
+.TP
+\fB\-s --server\fR
+Utilise une stratégie d'analyse très rapide. Sur un système avec un très grand nombre de ports ouverts,
+c'est des centaines de fois plus rapide que la méthode ss et des dizaines de milliers de fois plus rapide que
+la méthode netstat.
+.TP
+\fB\-V --version\fR
+Affiche la version et sort
+.TP
+\fB\-v --verbose\fR
+Affichage prolixe, affiche les message d'avertissement (par défaut : ne pas afficher).
+.PP
+.SS "Exit status:"
+.TP
+0
+si aucun port caché n'est trouvé,
+.TP
+4
+si un ou plusieurs port(s) TCP caché(s) est(sont) trouvé(s),
+.TP
+8
+si un ou plusieurs port(s) UDP caché(s) est(sont) trouvé(s),
+.TP
+12
+si des ports TCP et UDP cachés sont trouvés.
+.PP
+.SH "VOIR AUSSI"
+.PP
+unhide (8).
+.SH "AUTEUR"
+.PP
+Cette page de manuel a été écrite par Patrick Gouin (patrick-g@users.sourceforge.net).
+Permission vous est donnée de copier, distribuer et/ou modifier ce document sous
+les termes de la GNU General Public License, Version 3 ou toute
+version ultérieure publiée par la Free Software Foundation.
+.SH LICENCE
+Licence GPLv3: GNU GPL version 3 ou version ultérieure .
+.br
+Ce logiciel est libre : vous êtes libre de le modifier et le redistribuer.
+Il n'y a AUCUNE GARANTIE, dans les limites permises par la loi.
diff --git a/man/fr/unhide.8 b/man/fr/unhide.8
new file mode 100644
index 0000000..83cdd6c
--- /dev/null
+++ b/man/fr/unhide.8
@@ -0,0 +1,239 @@
+.TH "unhide" "8" "Mars 2012" "Commandes d'administration"
+.SH "NOM"
+unhide \(em outil d'investigation post\-mortem pour trouver des processus cachés
+.SH "SYNOPSIS"
+.PP
+\fBunhide\-linux\fR [\fIOPTIONS\fR] \fITEST_LIST\fR
+.br
+\fBunhide\-posix\fR \fIproc | sys\fR
+.SH "DESCRIPTION"
+.PP
+\fBunhide\fR est un outil d'investigation pour trouver les processus cachés par
+des rootkits, des modules du noyau Linux ou par d'autres techniques. Il
+détecte les processus cachés en utilisant six techniques principales.
+.PP
+.SH "OPTIONS"
+.PP
+Les options sont uniquement disponibles pour \fBunhide-linux\fR pas pour \fBunhide-posix\fR.
+.TP
+\fB \-d\fR
+Effectue un double contrôle dans le test 'brute' pour diminuer l'occurence des faux positifs.
+.TP
+\fB \-f\fR
+Enregistre les sorties dans un fichier de log (unhide-linux.log) situé dans le répertoire courant.
+.TP
+\fB \-h\fR
+Affichage de l'aide.
+.TP
+\fB \-m\fR
+Exécute des contrôles supplémentaires. Pour la version 2012\-03\-17, cette option n'a
+d''effet pour les tests procfs, procall, checkopendir et checkchdir.
+.br
+Elle implique l'option \-v.
+.TP
+\fB \-r\fR
+Utilise une version alternative du test sysinfo lors du lancement d'un test standard.
+.TP
+\fB \-V\fR
+Affiche la version et sort.
+.TP
+\fB \-v\fR
+Affichage prolixe, affiche les message d'avertissement (par défaut : ne pas afficher).
+Cette option peut être répétée plus d'une fois.
+.PP
+.PP
+.SH "TEST_LIST"
+.PP
+Les vérifications à faire consiste en un ou plusieurs des tests suivants.
+.br
+Les tests standard sont l'agrégation d'un ou plusieurs test(s) élémentaire(s).
+.PP
+\fBTests Standards :\fR
+.PP
+La technique \fIbrute\fR consiste en un scan de tous les ID de processus par
+force brute.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIproc\fR consiste à comparer le contenu de /proc avec la
+sortie de /bin/ps.
+.PP
+La technique \fIprocall\fR combine les tests proc et procfs.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIprocfs\fR consiste à comparer les informations recueillies par
+le parcours de l'arborescence du système de fichiers procfs avec les informations
+issues de /bin/ps
+.br
+Avec l'option \fB\-m\fR, ce test effectue des contrôles plus approfondis, voir le
+test \fIcheckchdir\fR.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIquick\fR combine les techniques proc, procfs et sys d'une façon rapide.
+Elle est environ 20 fois plus rapide, mais peut donner davantage de faux positifs.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIreverse\fR consiste à vérifier que tous les threads vus par /bin/ps
+sont également vus dans le procfs et par les appels système. C'est une recherche
+inversée. Elle est destiné à vérifier qu'un rootkit n'a pas tué un outil de sécurité
+(IDS ou autre) et modifié /bin/ps pour lui faire afficher un faux processus à la place.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIsys\fR consiste à comparer les résultats des appels des fonctions systèmes
+avec les informations recueillies à partir de /bin/ps.
+.PP
+\fBTests Elémentaires :\fR
+.PP
+La technique \fIcheckbrute\fR en un scan de tous les ID de processus par
+force brute.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIcheckchdir\fR consiste à comparer les informations recueillies en
+parcourant le système de fichiers procfs à l'aide de la fonction chdir() avec les informations
+obtenues avec /bin/ps.
+.br
+Avec l'option \fB\-m\fR, elle vérifie également que les threads apparaîssent dans la
+liste des threads de leur processus principal
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIcheckgetaffinity\fR consiste à comparer les résultat de
+l'appel à la fonction système sched_getaffinity() avec les informations recueillies
+à partir de /bin/ps.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIcheckgetparam\fR consiste à comparer les résultats de
+l'appel à la fonction système sched_getparam() avec les informations
+recueillies à partir de /bin/ps.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIcheckgetpgid\fR consiste à comparer les résultats de l'appel à la
+fonction système getpgid() avec les informations recueillies à partir de /bin/ps.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIcheckgetprio\fR consiste à comparer les résultats de l'appel à la
+fonction système getpriority() avec les informations recueillies à partir de /bin/ps.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIcheckRRgetinterval\fR consiste à comparer les résultats de l'appel
+à la fonction système sched_rr_get_interval() avec les informations
+recueillies à partir de /bin/ps.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIcheckgetsched\fR consiste à comparer les résultats de l'appel à la
+fonction système sched_getscheduler() avec les informations recueillies à partir de /bin/ps.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIcheckgetsid\fR consiste à comparer les résultats de l'appel à la
+fonction système getsid() avec les informations recueillies à partir de /bin/ps.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIcheckkill\fR consiste à comparer les résultats de l'appel à la
+fonction système kill() avec les informations recueillies à partir de /bin/ps.
+.br
+Note: aucun processus n'est réellement tué par ce test.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIchecknoprocps\fR consiste à comparer les résultats des appels
+de chacune des fonctions du système entre eux. Aucune comparaison n'est faite avec
+le contenu de /proc ou la sortie de /bin/ps.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIcheckopendir\fR consiste à comparer les informations recueillies en
+parcourant le système de fichiers procfs à l'aide de la fonction opendir() avec les informations
+recueillies à partir de /bin/ps.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIcheckproc\fR consiste à comparer le contenu de /proc avec la
+sortie de /bin/ps.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIcheckquick\fR combine les technique proc, procfs et sys d'une façon
+rapide. Il est environ 20 fois plus rapide, mais peut donner davantage de faux positifs.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIcheckreaddir\fR consiste à comparer les informations recueillies en
+parcourant le système de fichiers procfs (/proc et /proc/PID/task) à l'aide de la fonction
+readdir() avec les informations recueillies à partir de /bin/ps.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIcheckreverse\fR consiste à vérifier que tous les threads
+vus par ps sont également vus dans procfs et par les appels système. Il est destiné
+à vérifier qu'un rootkit n'a pas tué un outil de sécurité (IDS ou autre) et
+modifié /bin/ps pour lui faire afficher un faux processus à la place.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIchecksysinfo\fR consiste à comparer le nombre des processus obtenu
+à partir de l'appel système sysinfo() avec le nombre de processus vu par /bin/ps.
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.PP
+La technique \fIchecksysinfo2\fR est une version alternative du test checksysinfo.
+Il peut (ou pas) fonctionner mieux sur un noyau modifié pour le temps réel, la préemption,
+la latence basse ou un noyau qui n'utilise pas le scheduler standard.
+.br
+Il est invoqué par les tests standard lorsqu'on utilise l'option \fB\-r\fR
+.br
+Cette technique n'est disponible qu'avec la version unhide\-linux.
+.SS "Code de retour"
+.TP
+0
+si OK,
+.TP
+1
+si un thread caché ou faux est trouvé.
+.PP
+.SH "EXEMPLES"
+.TP
+Test le plus rapide :
+unhide quick
+.TP
+Test rapide :
+unhide quick reverse
+.TP
+Test standard :
+unhide sys proc
+.TP
+Test le plus complet :
+unhide -m -d sys procall brute reverse
+.SH "BUGS"
+.PP
+Rapportez les bugs de \fBunhide\fR sur le bug tracker de sourceforge (http://sourceforge.net/projects/unhide/)
+.br
+Avec les versions récentes du noyau Linux (> 2.6.33), le test sysinfo peut indiquer de faux positifs.
+Ça peut être dû à l'optimisation dans le scheduleur, l'utilisation des cgroup ou même l'utilisation de systemd.
+L'utilisation du patch PREEMPT-RT amplifie l'apparition du problème.
+Ce problème est en cours d'investigation.
+.SH "VOIR AUSSI"
+.PP
+unhide\-tcp (8).
+.SH "AUTEUR"
+.PP
+Cette page de manuel a été écrite par Patrick Gouin (patrick\-g@users.sourceforge.net).
+Permission vous est donnée de copier, distribuer et/ou modifier ce document sous
+les termes de la GNU General Public License, Version 3 ou toute
+version ultérieure publiée par la Free Software Foundation.
+.SH "LICENCE"
+Licence GPLv3: GNU GPL version 3 ou version ultérieure .
+.br
+Ce logiciel est libre : vous êtes libre de le modifier et le redistribuer.
+Il n'y a AUCUNE GARANTIE, dans les limites permises par la loi.
diff --git a/man/unhide-tcp.8 b/man/unhide-tcp.8
new file mode 100644
index 0000000..899334f
--- /dev/null
+++ b/man/unhide-tcp.8
@@ -0,0 +1,80 @@
+.TH "UNHIDE-TCP" "8" "August 2012" "Administration commands"
+.SH "NAME"
+unhide-tcp \(em forensic tool to find hidden TCP/UDP ports
+.SH "SYNOPSIS"
+.PP
+\fBunhide-tcp [options]\fR
+.SH "DESCRIPTION"
+.PP
+\fBunhide-tcp\fR is a forensic tool that identifies TCP/UDP
+ports that are listening but are not listed by /sbin/ss (or
+alternatively by /bin/netstat) through brute forcing of all
+TCP/UDP ports available.
+.br
+Note1 : On FreeBSD ans OpenBSD, netstat is allways used as iproute2 doesn't exist
+on these OS. In addition, on FreeBSD, sockstat is used instead of fuser.
+Note2 : If iproute2 is not available on the system, option -n or -s SHOULD be
+given on the command line.
+.PP
+.SH "OPTIONS"
+.TP
+\fB\-h --help\fR
+Display help
+.TP
+\fB\--brief\fR
+Don't display warning messages, that's the default behavior.
+.TP
+\fB\-f --fuser\fR
+Display fuser output (if available) for the hidden port
+On FreeBSD, instead of fuser command, displays the output of the sockstat command for the hidden port.
+.TP
+\fB\-l --lsof\fR
+Display lsof output (if available) for the hidden port
+.TP
+\fB\-n --netstat\fR
+Use /bin/netstat instead of /sbin/ss. On system with many opened ports, this can
+slow down the test dramatically.
+.TP
+\fB\-s --server\fR
+Use a very quick strategy of scanning. On system with a lot of opened ports,
+it is hundreds times faster than ss method and ten thousands times faster than
+netstat method.
+.TP
+\fB\-o --log\fR
+Write a log file (unhide-tcp-AAAA-MM-DD.log) in the current directory.
+.TP
+\fB\-V --version\fR
+Show version and exit
+.TP
+\fB\-v --verbose\fR
+Be verbose, display warning message (default : don't display).
+This option may be repeated more than once.
+.PP
+.SS "Exit status:"
+.TP
+0
+if no hidden port is found,
+.TP
+4
+if one or more hidden TCP port(s) is(are) found,
+.TP
+8
+if one or more hidden UDP port(s) is(are) found,
+.TP
+12
+if one or more hidden TCP and UDP ports are found.
+.PP
+.SH "SEE ALSO"
+.PP
+unhide (8).
+.SH "AUTHOR"
+.PP
+This manual page was written by Francois Marier francois@debian.org and Patrick Gouin.
+Permission is granted to copy, distribute and/or modify this document under
+the terms of the GNU General Public License, Version 3 or any
+later version published by the Free Software Foundation.
+.SH LICENSE
+License GPLv3+: GNU GPL version 3 or later .
+.br
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
diff --git a/man/unhide.8 b/man/unhide.8
new file mode 100644
index 0000000..31f8ed4
--- /dev/null
+++ b/man/unhide.8
@@ -0,0 +1,241 @@
+.TH "UNHIDE" "8" "March 2012" "Administration commands"
+.SH "NAME"
+unhide \(em forensic tool to find hidden processes
+.SH "SYNOPSIS"
+.PP
+\fBunhide\fR [\fIOPTIONS\fR] \fITEST_LIST\fR
+.br
+\fBunhide\-posix\fR \fIproc | sys\fR
+.SH "DESCRIPTION"
+.PP
+\fBunhide\fR is a forensic tool to find processes hidden by
+rootkits, Linux kernel modules or by other techniques. It
+detects hidden processes using six techniques.
+.PP
+.SH "OPTIONS"
+.PP
+Options are only available for \fBunhide-linux\fR not for \fBunhide-posix\fR.
+.TP
+\fB\-d\fR
+Do a double check in brute test to avoid false positive.
+.TP
+\fB\-f\fR
+Write a log file (unhide-linux.log) in the current directory.
+.TP
+\fB\-h\fR
+Display help
+.TP
+\fB\-m\fR
+Do more checks. As of 2012\-03\-17 version, this option has only
+effect for the procfs, procall, checkopendir and checkchdir tests.
+.br
+Implies -v
+.TP
+\fB\-r\fR
+Use alternate version of sysinfo check in standard tests
+.TP
+\fB\-V\fR
+Show version and exit
+.TP
+\fB\-v\fR
+Be verbose, display warning message (default : don't display).
+This option may be repeated more than once.
+.PP
+.PP
+.SH "TEST_LIST"
+.PP
+The checks to do consist of one or more of the following tests.
+.br
+The standard tests are the aggregation of one or more elementary test(s).
+.PP
+\fBStandard tests :\fR
+.PP
+The \fIbrute\fR technique consists of bruteforcing the all
+process IDs.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIproc\fR technique consists of comparing /proc with the
+output of /bin/ps.
+.PP
+The \fIprocall\fR technique combinates proc and procfs tests.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIprocfs\fR technique consists of comparing information
+gathered from /bin/ps with information gathered by walking in the procfs.
+.br
+With \fB-m\fR option, this test makes more checks, see \fIcheckchdir\fR test.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIquick\fR technique combines the proc, procfs and sys techniques in a
+quick way. It's about 20 times faster but may give more false positives.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIreverse\fR technique consists of verifying that all threads
+seen by ps are also seen in procfs and by system calls. It is intended to
+verify that a rootkit has not killed a security tool (IDS or other) and
+make ps showing a fake process instead.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIsys\fR technique consists of comparing information
+gathered from /bin/ps with information gathered from system
+calls.
+.PP
+\fBElementary tests :\fR
+.PP
+The \fIcheckbrute\fR technique consists of bruteforcing the all
+process IDs.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIcheckchdir\fR technique consists of comparing information
+gathered from /bin/ps with information gathered by making chdir() in the procfs.
+.br
+With the \fB-m\fR option, it also verify that the thread appears in its
+"leader process" threads list.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIcheckgetaffinity\fR technique consists of comparing information
+gathered from /bin/ps with the result of call to the sched_getaffinity()
+system function.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIcheckgetparam\fR technique consists of comparing information
+gathered from /bin/ps with the result of call to the sched_getparam()
+system function.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIcheckgetpgid\fR technique consists of comparing information
+gathered from /bin/ps with the result of call to the getpgid()
+system function.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIcheckgetprio\fR technique consists of comparing information
+gathered from /bin/ps with the result of call to the getpriority()
+system function.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIcheckRRgetinterval\fR technique consists of comparing information
+gathered from /bin/ps with the result of call to the sched_rr_get_interval()
+system function.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIcheckgetsched\fR technique consists of comparing information
+gathered from /bin/ps with the result of call to the sched_getscheduler()
+system function.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIcheckgetsid\fR technique consists of comparing information
+gathered from /bin/ps with the result of call to the getsid()
+system function.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIcheckkill\fR technique consists of comparing information
+gathered from /bin/ps with the result of call to the kill()
+system function.
+.br
+Note : no process is really killed by this test.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIchecknoprocps\fR technique consists of comparing the result of the call
+to each of the system functions. No comparison is done against /proc or the
+output of ps.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIcheckopendir\fR technique consists of comparing information
+gathered from /bin/ps with information gathered by making opendir() in the procfs.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIcheckproc\fR technique consists of comparing /proc with the
+output of /bin/ps.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIcheckquick\fR technique combines the proc, procfs and sys techniques in a
+quick way. It's about 20 times faster but may give more false positives.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIcheckreaddir\fR technique consists of comparing information
+gathered from /bin/ps with information gathered by making readdir() in /proc and
+/proc/pid/task.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIcheckreverse\fR technique consists of verifying that all threads
+seen by ps are also seen in procfs and by system calls. It is intended to
+verify that a rootkit has not killed a security tool (IDS or other) and
+make ps showing a fake process instead.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIchecksysinfo\fR technique consists of comparing the number of process
+seen by /bin/ps with information obtained from sysinfo() system call.
+.br
+This technique is only available with version unhide\-linux.
+.PP
+The \fIchecksysinfo2\fR technique is an alternate version of checksysinfo test.
+It might (or not) work better on kernel patched for RT, preempt or latency and
+with kernel that don't use the standard scheduler.
+.br
+It's also invoked by standard tests when using the \fB-r\fR option
+.br
+This technique is only available with version unhide\-linux.
+.SS "Exit status:"
+.TP
+0
+if OK,
+.TP
+1
+if a hidden or fake thread is found.
+.PP
+.SH "EXAMPLES"
+.TP
+Quicker test:
+unhide quick
+.TP
+Quick test:
+unhide quick reverse
+.TP
+Standard test:
+unhide sys proc
+.TP
+Deeper test:
+unhide -m -d sys procall brute reverse
+.SH "BUGS"
+.PP
+Report \fBunhide\fR bugs on the bug tracker on sourceforge (http://sourceforge.net/projects/unhide/)
+.br
+With recent versions of Linux kernel (> 2.6.33), the sysinfo test may report false positives.
+It may be due to optimization in the scheduler, the use of cgroup or even the use of systemd.
+The use of the PREEMPT-RT patch amplifies the occurence of the problem.
+This is currently under investigation.
+.SH "SEE ALSO"
+.PP
+unhide-tcp (8).
+.SH "AUTHOR"
+.PP
+This manual page was written by Francois Marier francois@debian.org and Patrick Gouin.
+Permission is granted to copy, distribute and/or modify this document under
+the terms of the GNU General Public License, Version 3 or any
+later version published by the Free Software Foundation.
+.SH LICENSE
+License GPLv3+: GNU GPL version 3 or later .
+.br
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
diff --git a/sanity-tcp.sh b/sanity-tcp.sh
new file mode 100755
index 0000000..c9f30d0
--- /dev/null
+++ b/sanity-tcp.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+# sanity.sh -- a growing testsuite for unhide-tcp.
+#
+# Copyright (C) 2010 Patrick Gouin.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Original Author: Patrick Gouin
+# BSD portability: Nikos Ntarmos
+
+if [ "x`/usr/bin/env uname`" == "xLinux" ]; then
+ ONFREEBSD=0
+ CHECKER=ss
+else
+ ONFREEBSD=1
+ CHECKER=netstat
+fi
+
+# remove pre-existing local ss
+rm -f ./$CHECKER
+
+#test 0
+# Don't call CHECKER : let all ports appear hidden
+cat <./$CHECKER
+#!/bin/sh
+
+false
+EOF
+chmod 754 ./$CHECKER
+PATH=.:$PATH ./unhide-tcp -fl
+# PATH=.:$PATH ./unhide-tcp
+#PATH=.:$PATH ./unhide-tcp-double_check
+
+# remove pre-existing local $CHECKER
+rm -f ./$CHECKER
+
+#test 1
+# Call $CHECKER : let cups port appears hidden
+cat <./$CHECKER
+#!/bin/sh
+
+set -e
+
+# echo "Le 1er paramètre est : \$1" >&2
+# echo "Le 2ème paramètre est : \$2" >&2
+# echo "Le 3ème paramètre est : \$3" >&2
+# echo "Le 4ème paramètre est : \$4" >&2
+
+if [ $ONFREEBSD -eq 1 ]
+then
+ /usr/bin/netstat \$@ | grep -v 631
+ exit
+elif [ "\$4" != ":631" ]
+then
+ # appelle le véritable ss
+ /sbin/ss \$@
+else
+ echo "Le 4ème paramètre est : \$4" >&2
+fi
+
+EOF
+chmod 754 ./$CHECKER
+PATH=.:$PATH ./unhide-tcp -fl
+# PATH=.:$PATH ./unhide-tcp-double_check -fl
+
+# remove pre-existing local CHECKER
+#rm -f ./$CHECKER
diff --git a/sanity.sh b/sanity.sh
new file mode 100755
index 0000000..50a5a5a
--- /dev/null
+++ b/sanity.sh
@@ -0,0 +1,122 @@
+#! /bin/sh
+
+# sanity.sh -- a growing testsuite for unhide.
+#
+# Copyright (C) 2010 Patrick Gouin.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Original Author: Patrick Gouin
+
+# remove pre-existing local ps
+rm -f ./ps
+
+#test 0
+# Call ps, but add a faked process.
+cat <./ps
+#! /bin/bash
+
+/bin/ps "\$@"
+echo 65535 my_false_proc
+EOF
+chmod 754 ./ps
+PATH=.:$PATH ./unhide-linux -v checksysinfo checksysinfo2
+
+# remove pre-existing local ps
+rm -f ./ps
+# test2
+# Don't call ps : let all processes appear hidden
+cat <./ps
+#! /bin/bash
+
+false
+EOF
+chmod 754 ./ps
+PATH=.:$PATH ./unhide-linux procall
+
+# remove pre-existing local ps
+rm -f ./ps
+
+# test 1
+# Call ps, but hide the last line of output
+cat <./ps
+#! /bin/bash
+
+/bin/ps "\$@" | head -n-1
+EOF
+chmod 754 ./ps
+PATH=.:$PATH ./unhide-linux sys
+
+
+# remove pre-existing local ps
+rm -f ./ps
+# test2
+# Don't call ps : let all processes appear hidden
+cat <./ps
+#! /bin/bash
+
+false
+EOF
+chmod 754 ./ps
+PATH=.:$PATH ./unhide-linux procall
+
+# remove pre-existing local ps
+rm -f ./ps
+#test 3
+# Call ps, but add a faked process.
+cat <./ps
+#! /bin/bash
+
+/bin/ps "\$@"
+echo 65535 my_false_proc
+EOF
+chmod 754 ./ps
+PATH=.:$PATH ./unhide-linux reverse
+
diff --git a/tar_list.txt b/tar_list.txt
new file mode 100644
index 0000000..8661b50
--- /dev/null
+++ b/tar_list.txt
@@ -0,0 +1,32 @@
+changelog
+COPYING
+LEEME.txt
+LISEZ-MOI.TXT
+make_tarball.sh
+man/es/unhide.8
+man/es/unhide-tcp.8
+man/fr/unhide.8
+man/fr/unhide-tcp.8
+man/unhide.8
+man/unhide-tcp.8
+NEWS
+README.txt
+sanity.sh
+sanity-tcp.sh
+tar_list.txt
+TODO
+unhide-linux-bruteforce.c
+unhide-linux.c
+unhide-linux-compound.c
+unhide-linux.h
+unhide-linux-procfs.c
+unhide-linux-syscall.c
+unhide-output.c
+unhide-output.h
+unhide-posix.c
+unhide_rb.c
+unhide-tcp.c
+unhide-tcp-simple-check.c
+unhide-tcp-fast.c
+unhide-tcp.h
+
diff --git a/unhide-linux-bruteforce.c b/unhide-linux-bruteforce.c
new file mode 100644
index 0000000..7eee9e8
--- /dev/null
+++ b/unhide-linux-bruteforce.c
@@ -0,0 +1,217 @@
+/*
+ http://sourceforge.net/projects/unhide/
+*/
+
+/*
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+// Needed for unistd.h to declare getpgid() and others
+#define _XOPEN_SOURCE 500
+
+// Needed for sched.h to declare sched_getaffinity()
+#define _GNU_SOURCE
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "unhide-output.h"
+#include "unhide-linux.h"
+
+/*
+ * Minimalist thread function for brute test.
+ * Set tid with the pid of the created thread.
+ */
+void *funcionThread (void *parametro)
+{
+
+ tid = (pid_t) syscall (SYS_gettid);
+ return(&tid) ;
+};
+
+/*
+ * Brute force the pid space via vfork and
+ * pthread_create/pthread_join. All pid which
+ * can't be obtained are check against ps output
+ */
+void brute(void)
+{
+ int i=0;
+ int allpids[maxpid] ;
+ int allpids2[maxpid] ;
+ int x;
+ int y;
+ int z;
+
+ msgln(unlog, 0, "[*]Starting scanning using brute force against PIDS with fork()\n") ;
+
+ // PID under 301 are reserved for kernel
+ for(x=0; x < 301; x++)
+ {
+ allpids[x] = 0 ;
+ allpids2[x] = 0 ;
+ }
+
+ for(z=301; z < maxpid; z++)
+ {
+ allpids[z] = z ;
+ allpids2[z] = z ;
+ }
+
+ for (i=301; i < maxpid; i++)
+ {
+ int vpid;
+ int status;
+
+ errno= 0 ;
+
+ if ((vpid = vfork()) == 0)
+ {
+ _exit(0);
+ }
+
+ if (0 == errno)
+ {
+ allpids[vpid] = 0;
+ waitpid(vpid, &status, 0);
+ }
+ }
+
+ if(FALSE == brutesimplecheck) // Do the scan a second time
+ {
+// printf("DOING double check ...\n") ;
+ for (i=301; i < maxpid; i++)
+ {
+ int vpid;
+ int status;
+ errno= 0 ;
+
+ if ((vpid = vfork()) == 0)
+ {
+ _exit(0);
+ }
+
+ if (0 == errno)
+ {
+ allpids2[vpid] = 0;
+ waitpid(vpid, &status, 0);
+ }
+ }
+ }
+ /* processes that quit at this point in time create false positives */
+
+ for(y=0; y < maxpid; y++)
+ {
+ if ((allpids[y] != 0) && ((TRUE == brutesimplecheck) || (allpids2[y] != 0)))
+ {
+// printf("Check PID : %d\n", y);
+ if(!checkps(allpids[y],PS_PROC | PS_THREAD | PS_MORE) )
+ {
+ printbadpid(allpids[y]);
+ }
+ }
+ }
+
+ msgln(unlog, 0, "[*]Starting scanning using brute force against PIDS with pthread functions\n") ;
+
+ // PID under 301 are reserved for kernel
+ for(x=0; x < 301; x++)
+ {
+ allpids[x] = 0 ;
+ allpids2[x] = 0 ;
+ }
+
+
+ for(z=301; z < maxpid; z++)
+ {
+ allpids[z] = z ;
+ allpids2[z] = z ;
+ }
+
+
+ for (i=301; i < maxpid ; i++)
+ {
+ void *status;
+ errno= 0 ;
+ pthread_t idHilo;
+ int error;
+
+ error = pthread_create (&idHilo, NULL, funcionThread, NULL);
+ if (error != 0)
+ {
+ die(unlog, "Error: Cannot create thread ! Exiting.");
+ }
+
+ error = pthread_join(idHilo, &status);
+ if (error != 0)
+ {
+ die(unlog, "Error : Cannot join thread ! Exiting.");
+ }
+ allpids[tid] = 0;
+
+ }
+
+ if(FALSE == brutesimplecheck) // Do the scan a second time
+ {
+// printf("DOING double check ...\n") ;
+ for (i=301; i < maxpid ; i++) {
+ void *status;
+ errno= 0 ;
+ pthread_t idHilo;
+ int error;
+
+ error = pthread_create (&idHilo, NULL, funcionThread, NULL);
+ if (error != 0)
+ {
+ die(unlog, "Error: Cannot create thread ! Exiting.");
+ }
+
+ error = pthread_join(idHilo, &status);
+ if (error != 0)
+ {
+ die(unlog, "Error : Cannot join thread ! Exiting.");
+ }
+ allpids2[tid] = 0;
+ }
+ }
+
+ /* processes that quit at this point in time create false positives */
+
+ for(y=0; y < maxpid; y++)
+ {
+ if ((allpids[y] != 0) && ((TRUE == brutesimplecheck) || (allpids2[y] != 0)))
+ {
+ if(!checkps(allpids[y],PS_PROC | PS_THREAD | PS_MORE) )
+ {
+ printbadpid(allpids[y]);
+ }
+ }
+ }
+}
diff --git a/unhide-linux-compound.c b/unhide-linux-compound.c
new file mode 100644
index 0000000..795efe4
--- /dev/null
+++ b/unhide-linux-compound.c
@@ -0,0 +1,366 @@
+/*
+ http://sourceforge.net/projects/unhide/
+*/
+
+/*
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+// Needed for unistd.h to declare getpgid() and others
+#define _XOPEN_SOURCE 500
+
+// Needed for sched.h to declare sched_getaffinity()
+#define _GNU_SOURCE
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "unhide-output.h"
+#include "unhide-linux.h"
+
+/*
+ * Compare the various system calls against each other,
+ * and with fs function in /proc, finally check ps output
+ */
+void checkallquick(void)
+{
+
+ int ret;
+ int syspids;
+ struct timespec tp;
+ struct sched_param param;
+ cpu_set_t mask;
+ int found=0;
+ int found_killbefore=0;
+ int found_killafter=0;
+ char directory[100], *pathpt;
+ struct stat buffer;
+ int statusproc, statusdir, backtodir ;
+ char curdir[PATH_MAX] ;
+ DIR *dir_fd;
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through comparison of results of system calls, proc, dir and ps\n") ;
+
+ // get the path where Unhide is ran from.
+ if (NULL == (pathpt = getcwd(curdir, PATH_MAX)))
+ {
+ warnln(verbose, unlog, "Can't get current directory, test aborted.") ;
+ return;
+ }
+
+ sprintf(directory,"/proc/");
+
+ for ( syspids = 1; syspids <= maxpid; syspids++ )
+ {
+ // avoid ourselves
+ if (syspids == mypid)
+ {
+ continue;
+ }
+ // printf("syspid = %d\n", syspids); //DEBUG
+
+ found=0;
+ found_killbefore=0;
+ found_killafter=0;
+
+ errno=0;
+ ret = kill(syspids, 0);
+ if (errno == 0) found_killbefore=1;
+
+ errno= 0 ;
+ ret = getpriority(PRIO_PROCESS, syspids);
+ if (errno == 0) found++;
+
+ errno= 0 ;
+ ret = getpgid(syspids);
+ if (errno == 0) found++;
+
+ errno= 0 ;
+ ret = getsid(syspids);
+ if (errno == 0) found++;
+
+ errno= 0 ;
+ ret = sched_getaffinity(syspids, sizeof(cpu_set_t), &mask);
+ if (ret == 0) found++;
+
+ errno= 0 ;
+ ret = sched_getparam(syspids, ¶m);
+ if (errno == 0) found++;
+
+ errno= 0 ;
+ ret = sched_getscheduler(syspids);
+ if (errno == 0) found++;
+
+ errno=0;
+ ret = sched_rr_get_interval(syspids, &tp);
+ if (errno == 0) found++;
+
+ sprintf(&directory[6],"%d",syspids);
+
+ statusproc = stat(directory, &buffer) ;
+ if (statusproc == 0)
+ {
+ found++;
+ }
+
+ statusdir = chdir(directory) ;
+ if (statusdir == 0)
+ {
+ found++;
+ if (-1 == (backtodir = chdir(curdir)))
+ {
+ warnln(verbose, unlog, "Can't go back to unhide directory, test aborted.") ;
+ return;
+ }
+ }
+
+ dir_fd = opendir(directory) ;
+ if (NULL != dir_fd)
+ {
+ found++;
+ closedir(dir_fd);
+ }
+
+ // Avoid checkps call if nobody sees anything
+ if ((0 != found) || (0 != found_killbefore))
+ {
+ if(checkps(syspids,PS_PROC | PS_THREAD))
+ {
+ found++;
+ }
+ }
+
+ errno=0;
+ ret = kill(syspids, 0);
+ if (errno == 0) found_killafter=1;
+
+
+ /* these should all agree, except if a process went or came in the middle */
+ if (found_killbefore == found_killafter)
+ {
+ if ( ! ((found_killbefore == 0 && found == 0) ||
+ (found_killbefore == 1 && found == 11)) )
+ {
+ printbadpid(syspids);
+ }
+ } /* else: unreliable */
+ else
+ {
+ errno = 0 ;
+ warnln(verbose, unlog, "syscall comparison test skipped for PID %d.", syspids) ;
+ }
+ }
+}
+
+/*
+ * Check that all processes seen by ps are also seen by
+ * fs function in /proc and by syscall
+ */
+void checkallreverse(void)
+{
+
+ int ret;
+ int syspids;
+ struct timespec tp;
+ struct sched_param param;
+ cpu_set_t mask;
+ int not_seen=0;
+ int found_killbefore=0;
+ int found_killafter=0;
+ FILE *fich_tmp;
+ char command[50];
+ char read_line[1024];
+ char lwp[7];
+ int index;
+ char directory[100];
+ struct stat buffer;
+ int statusproc, statusdir, backtodir;
+ char curdir[PATH_MAX], *pathpt ;
+ DIR *dir_fd;
+
+ msgln(unlog, 0, "[*]Searching for Fake processes by verifying that all threads seen by ps are also seen by others\n") ;
+
+ sprintf(command,REVERSE) ;
+
+ fich_tmp=popen (command, "r") ;
+ if (fich_tmp == NULL)
+ {
+ warnln(verbose, unlog, "Couldn't run command: %s, test aborted", command) ;
+ return;
+ }
+ // get the path where Unhide is ran from.
+ if (NULL == (pathpt = getcwd(curdir, PATH_MAX)))
+ {
+ warnln(verbose, unlog, "Can't get current directory, test aborted") ;
+ return;
+ }
+
+ strcpy(directory,"/proc/");
+
+ while (NULL != fgets(read_line, 1024, fich_tmp))
+ {
+ char* curline = read_line;
+
+ read_line[1023] = 0;
+ read_line[strlen(read_line)-1] = 0;
+
+// printf("read_line = %s\n", read_line); // DEBUG
+ while( *curline == ' ' && curline <= read_line+1023)
+ {
+ curline++;
+ }
+
+ // get LWP
+ index=0;
+ while( isdigit(*curline) && curline <= read_line+1023)
+ {
+ lwp[index++] = *curline;
+ curline++;
+ }
+ lwp[index] = 0; // terminate string
+
+ syspids = -1;
+ syspids = atol(lwp);
+ if (-1 == syspids) continue ; // something went wrong
+
+ // avoid ourselves
+ if (syspids == mypid)
+ {
+ continue;
+ }
+
+ not_seen=0;
+ found_killbefore=0;
+ found_killafter=0;
+
+ errno=0;
+ ret = kill(syspids, 0);
+ if (errno == 0) found_killbefore=1;
+
+ strcpy(&directory[6],lwp);
+
+ statusproc = stat(directory, &buffer) ;
+ if (statusproc != 0)
+ {
+ not_seen++;
+ }
+
+ statusdir = chdir(directory) ;
+ if (statusdir != 0)
+ {
+ not_seen++;
+ }
+ else
+ {
+ if (-1 == (backtodir = chdir(curdir)))
+ {
+ warnln(verbose, unlog, "Can't go back to unhide directory, test aborted") ;
+ return;
+ }
+ }
+
+ dir_fd = opendir(directory) ;
+ if (NULL == dir_fd)
+ {
+ not_seen++;
+ }
+ else
+ {
+ closedir(dir_fd);
+ }
+
+ errno= 0 ;
+ ret = getpriority(PRIO_PROCESS, syspids);
+ if (errno != 0) not_seen++;
+
+ errno= 0 ;
+ ret = getpgid(syspids);
+ if (errno != 0) not_seen++;
+
+ errno= 0 ;
+ ret = getsid(syspids);
+ if (errno != 0) not_seen++;
+
+ errno= 0 ;
+ ret = sched_getaffinity(syspids, sizeof(cpu_set_t), &mask);
+ if (ret != 0) not_seen++;
+
+ errno= 0 ;
+ ret = sched_getparam(syspids, ¶m);
+ if (errno != 0) not_seen++;
+
+ errno= 0 ;
+ ret = sched_getscheduler(syspids);
+ if (errno != 0) not_seen++;
+
+ errno=0;
+ ret = sched_rr_get_interval(syspids, &tp);
+ if (errno != 0) not_seen++;
+
+ errno=0;
+ ret = kill(syspids, 0);
+ if (errno == 0) found_killafter=1;
+
+// printf("FK_bef = %d FK_aft = %d not_seen = %d\n",found_killbefore, found_killafter, not_seen); //DEBUG
+ /* these should all agree, except if a process went or came in the middle */
+ if (found_killbefore == found_killafter)
+ {
+ if (found_killafter == 1)
+ {
+ if (0 != not_seen)
+ {
+ if (NULL == strstr(curline, REVERSE)) // avoid our spawn ps
+ {
+ // printbadpid should NOT be used here : we are looking for faked process
+ msgln(unlog, 0, "Found FAKE PID: %i\tCommand = %s not seen by %d sys fonc", syspids, curline, not_seen) ;
+ found_HP = 1;
+ }
+ }
+ }
+ else
+ {
+ if (NULL == strstr(curline, REVERSE)) // avoid our spawned ps
+ {
+ // printbadpid should NOT be used here : we are looking for faked process
+ msgln(unlog, 0, "Found FAKE PID: %i\tCommand = %s not seen by %d sys fonc", syspids, curline, not_seen + 2) ;
+ found_HP = 1;
+ }
+ }
+ } /* else: unreliable */
+ else
+ {
+ errno = 0 ;
+ warnln(verbose, unlog, "reverse test skipped for PID %d", syspids) ;
+ }
+ }
+
+ if (fich_tmp != NULL)
+ pclose(fich_tmp);
+}
diff --git a/unhide-linux-procfs.c b/unhide-linux-procfs.c
new file mode 100644
index 0000000..d2d7eaf
--- /dev/null
+++ b/unhide-linux-procfs.c
@@ -0,0 +1,438 @@
+/*
+ http://sourceforge.net/projects/unhide/
+*/
+
+/*
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+// Needed for unistd.h to declare getpgid() and others
+#define _XOPEN_SOURCE 500
+
+// Needed for sched.h to declare sched_getaffinity()
+#define _GNU_SOURCE
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "unhide-output.h"
+#include "unhide-linux.h"
+
+
+/*
+ * Check all the PID stat() see in /proc.
+ */
+void checkproc(void)
+{
+
+ int procpids ;
+ int statusprocbefore, statusprocafter;
+ struct stat buffer;
+ char directory[100] ;
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through /proc stat scanning\n") ;
+
+ sprintf(directory,"/proc/");
+
+ for ( procpids = 1; procpids <= maxpid; procpids = procpids +1 )
+ {
+ // avoid ourselves
+ if (procpids == mypid)
+ {
+ continue;
+ }
+ sprintf(&directory[6],"%d",procpids);
+
+ statusprocbefore = stat(directory, &buffer) ;
+ if (statusprocbefore != 0)
+ {
+ continue;
+ }
+
+ if(checkps(procpids,PS_PROC | PS_THREAD))
+ {
+ continue;
+ }
+
+ statusprocafter = stat(directory, &buffer) ;
+ if (statusprocafter != 0)
+ {
+ continue;
+ }
+
+ printbadpid(procpids);
+ }
+}
+
+/*
+ * Check all the pid that chdir() see in /proc.
+ */
+void checkchdir(void)
+{
+
+ int procpids ;
+ int statusdir, backtodir;
+ char curdir[PATH_MAX], *pathpt ;
+ char directory[100] ;
+// char scratch[PATH_MAX] ; // DEBUG
+// int count = 0; //DEBUG
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through /proc chdir scanning\n") ;
+
+ // get the path where Unhide is ran from.
+ if (NULL == (pathpt = getcwd(curdir, PATH_MAX)))
+ {
+ warnln(verbose, unlog, "Can't get current directory, test aborted") ;
+ return;
+ }
+
+ sprintf(directory,"/proc/");
+
+ for ( procpids = 1; procpids <= maxpid; procpids = procpids +1 )
+ {
+ // avoid ourselves
+ if (procpids == mypid)
+ {
+ continue;
+ }
+
+ sprintf(&directory[6],"%d",procpids);
+ statusdir = chdir(directory) ;
+ // the directory doesn't exist continue with the next one
+ if (statusdir != 0)
+ {
+ continue;
+ }
+ if (morecheck == TRUE)
+ {
+ // find process group ID (the master thread) by reading the status file of the current dir
+ FILE* fich_tmp ;
+ int found_tgid = FALSE;
+ char line[128] ;
+ char* tmp_pids = line;
+ char* end_pid;
+ char new_directory[100];
+
+// printf("directory = '%s'\n", directory); // DEBUG
+// getcwd(scratch, PATH_MAX); // DEBUG
+// printf("CWD = '%s'\n", scratch); // DEBUG
+
+ // we are in the /proc/pid directory
+ fich_tmp=fopen("status", "r") ;
+ if (NULL == fich_tmp)
+ {
+ warnln(verbose, unlog, "can't open status file for process: %d", procpids) ;
+ continue ; // next process
+ }
+ while ((FALSE == found_tgid) && (NULL != fgets (line, 128, fich_tmp)))
+ {
+ line[127] = 0;
+ if (0 == strncmp (line, "Tgid:", 5))
+ {
+ found_tgid = TRUE;
+ }
+ }
+ fclose(fich_tmp);
+
+ if (TRUE == found_tgid)
+ {
+ tmp_pids = line + 5;
+ while( ((*tmp_pids == ' ') || (*tmp_pids == '\t')) && (tmp_pids <= line+127))
+ {
+ tmp_pids++;
+ }
+// printf("tmp_pids2 = '%s'\n", tmp_pids); // DEBUG
+ end_pid = tmp_pids;
+ while( isdigit(*end_pid) && end_pid <= line+127)
+ {
+ end_pid++;
+ }
+ *end_pid = 0; // remove \n
+// if the number of threads is < to about 40 % of the number of processes,
+// the next "optimising" test actually produce a slower executable.
+// if(procpids != atoi(tmp_pids))
+ { // if the thread isn't the master thread (process)
+// count++; // DEBUG
+ sprintf(new_directory,"/proc/%s/task/%d", tmp_pids, procpids) ;
+// printf("new_dir = %s\n", new_directory); // DEBUG
+ statusdir = chdir(new_directory) ;
+ if (statusdir != 0)
+ {
+ // the thread is not listed in the master thread task directory
+ errno = 0 ;
+ warnln(1, unlog, "Thread %d said it's in group %s but isn't listed in %s", procpids, tmp_pids, new_directory) ;
+ }
+ }
+ }
+ else
+ {
+ errno = 0 ;
+ warnln(1, unlog, "Can't find TGID in status file for process: %d", procpids) ;
+ }
+ }
+
+ // unlock the proc directory so it can disappear if it's a transitory process
+ if (-1 == (backtodir = chdir(curdir)))
+ {
+ warnln(verbose, unlog, "Can't go back to unhide directory, test aborted") ;
+ return;
+ }
+
+ if(checkps(procpids, PS_PROC | PS_THREAD))
+ {
+ continue;
+ }
+
+ // Avoid false positive on short life process/thread
+ statusdir = chdir(directory) ;
+ if (statusdir != 0)
+ {
+ continue;
+ }
+
+ printbadpid(procpids);
+ }
+ // go back to our path
+ if (-1 == (backtodir = chdir(curdir)))
+ {
+ warnln(verbose, unlog, "Can't go back to unhide directory, test aborted") ;
+ return;
+ }
+// printf("Passages = %d\n", count); // DEBUG
+}
+
+/*
+ * Check all the pid that opendir() see in /proc.
+ */
+void checkopendir(void)
+{
+
+ int procpids ;
+ DIR *statusdir;
+// char curdir[PATH_MAX] ;
+ char directory[100] ;
+// char scratch[PATH_MAX] ; // DEBUG
+// int count = 0; //DEBUG
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through /proc opendir scanning\n") ;
+
+ sprintf(directory,"/proc/");
+
+ for ( procpids = 1; procpids <= maxpid; procpids = procpids +1 )
+ {
+ // avoid ourselves
+ if (procpids == mypid)
+ {
+ continue;
+ }
+
+ sprintf(&directory[6],"%d",procpids);
+ statusdir = opendir(directory) ;
+ // the directory doesn't exist continue with the next one
+ if (statusdir == NULL)
+ continue;
+
+ if (morecheck == TRUE)
+ {
+ // find process group ID (the master thread) by reading the status file of the current dir
+ FILE* fich_tmp ;
+ int found_tgid = FALSE;
+ char line[128] ;
+ char* tmp_pids = line;
+ char* end_pid;
+ char new_directory[100] ;
+ DIR* statdir;
+
+// printf("directory = '%s'\n", directory); // DEBUG
+// getcwd(scratch, PATH_MAX); // DEBUG
+// printf("CWD = '%s'\n", scratch); // DEBUG
+
+ snprintf(line, 128, "%s/status", directory);
+// printf("STATUS_FILE : %s\n", line);
+ fich_tmp=fopen(line, "r") ;
+ if (NULL == fich_tmp)
+ {
+ msgln(unlog, 0, "Can't open status file for process: %d", procpids) ;
+ continue ; // next process
+ }
+ while ((FALSE == found_tgid) && (NULL != fgets (line, 128, fich_tmp)))
+ {
+ line[127] = 0;
+ if (0 == strncmp (line, "Tgid:", 5))
+ {
+ found_tgid = TRUE;
+ }
+ }
+ fclose(fich_tmp);
+
+ if (TRUE == found_tgid)
+ {
+ tmp_pids = line + 5;
+ while( ((*tmp_pids == ' ') || (*tmp_pids == '\t')) && (tmp_pids <= line+127))
+ {
+ tmp_pids++;
+ }
+// printf("tmp_pids2 = '%s'\n", tmp_pids); // DEBUG
+ end_pid = tmp_pids;
+ while( isdigit(*end_pid) && end_pid <= line+127)
+ {
+ end_pid++;
+ }
+ *end_pid = 0; // remove \n
+// if the number of threads is < to about 40 % of the number of processes,
+// the next "optimising" test actually produce a slower executable.
+// if(procpids != atoi(tmp_pids))
+ { // if the thread isn't the master thread (process)
+// count++; // DEBUG
+ sprintf(new_directory,"/proc/%s/task/%d", tmp_pids, procpids) ;
+// printf("new_dir = %s\n", new_directory); // DEBUG
+// errno = 0;
+ statdir = opendir(new_directory) ;
+ if (NULL == statdir)
+ {
+ // the thread is not listed in the master thread task directory
+// printf("opendir failed : %s)\n", strerror(errno)) ;
+ errno = 0 ;
+ warnln(1, unlog, "Thread %d said it's in group %s but isn't listed in %s", procpids, tmp_pids, new_directory) ;
+ }
+ else
+ {
+ closedir(statdir);
+ }
+ }
+ }
+ else
+ {
+ errno = 0 ;
+ warnln(1, unlog, "Can't find TGID in status file for process: %d", procpids) ;
+ }
+ }
+
+ // unlock the proc directory so it can disappear if it's a transitory process
+ closedir(statusdir);
+
+ if(checkps(procpids, PS_PROC | PS_THREAD)) {
+ continue;
+ }
+
+ // Avoid false positive on short life process/thread
+ statusdir = opendir(directory) ;
+ if (statusdir == NULL) {
+ continue;
+ }
+ // unlock dir & free descriptor
+ closedir(statusdir);
+
+ printbadpid(procpids);
+ }
+// printf("Passages = %d\n", count); // DEBUG
+}
+
+/*
+ * Check all the pid that readdir() see in all /proc/pid/task.
+ */
+void checkreaddir(void)
+{
+
+ int procpids ;
+ DIR *procdir, *taskdir;
+ struct dirent *dir, *dirproc;
+ char task[100] ;
+
+ msgln(unlog, 0, "[*]Searching for Hidden thread through /proc/pid/task readdir scanning\n") ;
+
+ procdir = opendir("/proc");
+ if (NULL == procdir)
+ {
+ warnln(verbose, unlog, "Cannot open /proc directory ! Exiting test.") ;
+ return ;
+ }
+
+ sprintf(task, "/proc/") ;
+
+ while ((dirproc = readdir(procdir)))
+ {
+ // As of Linux kernel 2.6 :
+ // readdir directly in /proc only see process, not thread
+ // because procfs voluntary hides threads to readdir
+ char *directory ;
+
+ directory = dirproc->d_name;
+ if(!isdigit(*directory))
+ {
+ // not a process directory of /proc
+ continue;
+ }
+// sprintf(currentproc, "%d", directory);
+
+ sprintf(&task[6], "%s/task", directory) ;
+// printf("task : %s", task) ; // DEBUG
+ taskdir = opendir(task);
+ if (NULL == taskdir)
+ {
+ warnln(verbose, unlog, "Cannot open %s directory ! ! Skipping process %s.", task, directory) ;
+ continue ;
+ }
+
+ while ((dir = readdir(taskdir)))
+ {
+ char *tmp_d_name ;
+ tmp_d_name = dir->d_name;
+// printf(" thread : %s\n",tmp_d_name) ; // DEBUG
+ if (!strcmp(tmp_d_name, ".") || !strcmp(tmp_d_name, "..")) // skip parent and current dir
+ continue;
+ if(!isdigit(*tmp_d_name))
+ {
+ errno = 0 ;
+ warnln(verbose, unlog, "Not a thread ID (%s) in %s.", tmp_d_name, task) ;
+ continue;
+ }
+ else if (0 != strcmp(tmp_d_name, directory)) { // thread ID is not the process ID
+// printf("thread : %s\n",tmp_d_name) ; // DEBUG
+ procpids = atoi(tmp_d_name) ;
+ if(checkps(procpids,PS_THREAD)) {
+ continue;
+ }
+ printbadpid(atoi(tmp_d_name));
+ }
+ else {
+// printf("process : %s\n",tmp_d_name) ; // DEBUG
+ procpids = atoi(tmp_d_name) ;
+ if(checkps(procpids,PS_PROC)) {
+ continue;
+ }
+ printbadpid(atoi(tmp_d_name));
+ }
+ }
+ closedir(taskdir);
+ }
+ closedir(procdir) ;
+}
+
diff --git a/unhide-linux-syscall.c b/unhide-linux-syscall.c
new file mode 100644
index 0000000..0493c77
--- /dev/null
+++ b/unhide-linux-syscall.c
@@ -0,0 +1,752 @@
+/*
+ http://sourceforge.net/projects/unhide/
+*/
+
+/*
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+// Needed for unistd.h to declare getpgid() and others
+#define _XOPEN_SOURCE 500
+
+// Needed for sched.h to declare sched_getaffinity()
+#define _GNU_SOURCE
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "unhide-output.h"
+#include "unhide-linux.h"
+
+// Shut up some warnings with over pedantic version of glibc
+int ret;
+
+/*
+ * Check all the pid that getpriority() see.
+ */
+void checkgetpriority(void)
+{
+ int syspids ;
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through getpriority() scanning\n") ;
+ for ( syspids = 1; syspids <= maxpid; syspids = syspids +1 )
+ {
+ int which = PRIO_PROCESS;
+ // int ret;
+
+ errno= 0 ;
+ // avoid ourselves
+ if (syspids == mypid)
+ {
+ continue;
+ }
+
+ ret = getpriority(which, syspids);
+ if ( errno != 0)
+ {
+ continue;
+ }
+
+ if(checkps(syspids,PS_PROC | PS_THREAD))
+ {
+ continue;
+ }
+
+ errno=0;
+ ret = getpriority(which, syspids);
+ if ( errno != 0)
+ {
+ continue;
+ }
+ printbadpid(syspids);
+ }
+}
+
+/*
+ * Check all the pid that getpgid() see.
+ */
+void checkgetpgid(void)
+{
+ int syspids ;
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through getpgid() scanning\n") ;
+ for ( syspids = 1; syspids <= maxpid; syspids = syspids +1 )
+ {
+ // int ret;
+
+ errno= 0 ;
+ // avoid ourselves
+ if (syspids == mypid)
+ {
+ continue;
+ }
+
+ ret = getpgid(syspids);
+ if ( errno != 0 )
+ {
+ continue;
+ }
+
+ if(checkps(syspids,PS_PROC | PS_THREAD))
+ {
+ continue;
+ }
+
+ errno=0;
+ ret = getpgid(syspids);
+ if ( errno != 0 )
+ {
+ continue;
+ }
+ printbadpid(syspids);
+ }
+}
+
+/*
+ * Check all the pid that getsid() see.
+ */
+void checkgetsid(void)
+{
+ int syspids ;
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through getsid() scanning\n") ;
+ for ( syspids = 1; syspids <= maxpid; syspids = syspids +1 )
+ {
+ // int ret;
+
+ errno= 0 ;
+ // avoid ourselves
+ if (syspids == mypid)
+ {
+ continue;
+ }
+
+ ret = getsid(syspids);
+ if ( errno != 0)
+ {
+ continue;
+ }
+ if(checkps(syspids,PS_PROC | PS_THREAD))
+ {
+ continue;
+ }
+ errno=0;
+ ret = getsid(syspids);
+ if ( errno != 0)
+ {
+ continue;
+ }
+ printbadpid(syspids);
+ }
+}
+
+/*
+ * Check all the pid that sched_getaffinity() see.
+ */
+void checksched_getaffinity(void)
+{
+
+ int syspids;
+ cpu_set_t mask;
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through sched_getaffinity() scanning\n") ;
+ for ( syspids = 1; syspids <= maxpid; syspids = syspids +1 )
+ {
+ // int ret;
+
+ errno= 0 ;
+ // avoid ourselves
+ if (syspids == mypid)
+ {
+ continue;
+ }
+
+ ret = sched_getaffinity(syspids, sizeof(cpu_set_t), &mask);
+ if (errno != 0)
+ {
+ continue;
+ }
+ if (checkps(syspids,PS_PROC | PS_THREAD))
+ {
+ continue;
+ }
+ errno=0;
+ ret = sched_getaffinity(syspids, sizeof(cpu_set_t), &mask);
+ if (errno != 0)
+ {
+ continue;
+ }
+ printbadpid(syspids);
+ }
+}
+
+/*
+ * Check all the pid that sched_getparam() see.
+ */
+void checksched_getparam(void)
+{
+
+ int syspids;
+ struct sched_param param;
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through sched_getparam() scanning\n") ;
+ for ( syspids = 1; syspids <= maxpid; syspids = syspids +1 )
+ {
+ // int ret;
+
+ errno= 0 ;
+ // avoid ourselves
+ if (syspids == mypid) {
+ continue;
+ }
+
+ ret = sched_getparam(syspids, ¶m);
+ if ( errno != 0)
+ {
+ continue;
+ }
+
+ if(checkps(syspids,PS_PROC | PS_THREAD))
+ {
+ continue;
+ }
+
+ errno=0;
+ ret = sched_getparam(syspids, ¶m);
+ if ( errno != 0)
+ {
+ continue;
+ }
+ printbadpid(syspids);
+ }
+}
+
+/*
+ * Check all the pid that sched_getscheduler() see.
+ */
+void checksched_getscheduler(void)
+{
+ int syspids ;
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through sched_getscheduler() scanning\n") ;
+ for ( syspids = 1; syspids <= maxpid; syspids = syspids +1 )
+ {
+ // int ret;
+
+ errno= 0 ;
+ // avoid ourselves
+ if (syspids == mypid) {
+ continue;
+ }
+
+ ret = sched_getscheduler(syspids);
+ if ( errno != 0)
+ {
+ continue;
+ }
+
+ if(checkps(syspids,PS_PROC | PS_THREAD))
+ {
+ continue;
+ }
+
+ errno=0;
+ ret = sched_getscheduler(syspids);
+ if ( errno != 0)
+ {
+ continue;
+ }
+ printbadpid(syspids);
+ }
+}
+
+/*
+ * Check all the pid that sched_rr_get_interval() see.
+ */
+void checksched_rr_get_interval(void)
+{
+
+ int syspids;
+ struct timespec tp;
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through sched_rr_get_interval() scanning\n") ;
+ for ( syspids = 1; syspids <= maxpid; syspids = syspids +1 )
+ {
+ // int ret;
+
+ errno= 0 ;
+ // avoid ourselves
+ if (syspids == mypid)
+ {
+ continue;
+ }
+
+ ret = sched_rr_get_interval(syspids, &tp);
+ if ( errno != 0)
+ {
+ continue;
+ }
+
+ if(checkps(syspids,PS_PROC | PS_THREAD))
+ {
+ continue;
+ }
+
+ errno=0;
+ ret = sched_rr_get_interval(syspids, &tp);
+ if ( errno != 0)
+ {
+ continue;
+ }
+ printbadpid(syspids);
+ }
+}
+
+/*
+ * Check all the pid that kill() see.
+ */
+void checkkill(void)
+{
+ int syspids;
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through kill(..,0) scanning\n") ;
+ for ( syspids = 1; syspids <= maxpid; syspids = syspids +1 )
+ {
+ // int ret;
+
+ errno= 0 ;
+ // avoid ourselves
+ if (syspids == mypid)
+ {
+ continue;
+ }
+
+ ret = kill(syspids, 0);
+ if ( errno != 0)
+ {
+ continue;
+ }
+
+ if(checkps(syspids,PS_PROC | PS_THREAD))
+ {
+ continue;
+ }
+
+ errno= 0 ;
+ ret = kill(syspids, 0);
+ if ( errno != 0)
+ {
+ continue;
+ }
+ printbadpid(syspids);
+ }
+}
+
+/*
+ * Compare the various system calls against each other,
+ * without invoking 'ps' or looking at /proc
+ */
+void checkallnoprocps(void)
+{
+ // int ret;
+ int syspids;
+ struct timespec tp;
+ struct sched_param param;
+ cpu_set_t mask;
+ int found=0;
+ int found_killbefore=0;
+ int found_killafter=0;
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through comparison of results of system calls\n") ;
+ for ( syspids = 1; syspids <= maxpid; syspids++ )
+ {
+ // avoid ourselves
+ if (syspids == mypid)
+ {
+ continue;
+ }
+
+ found=0;
+ found_killbefore=0;
+ found_killafter=0;
+
+ errno=0;
+ ret = kill(syspids, 0);
+ if (errno == 0) found_killbefore=1;
+
+ errno= 0 ;
+ ret = getpriority(PRIO_PROCESS, syspids);
+ if (errno == 0) found++;
+
+ errno= 0 ;
+ ret = getpgid(syspids);
+ if (errno == 0) found++;
+
+ errno= 0 ;
+ ret = getsid(syspids);
+ if (errno == 0) found++;
+
+ errno= 0 ;
+ ret = sched_getaffinity(syspids, sizeof(cpu_set_t), &mask);
+ if (errno == 0) found++;
+
+ errno= 0 ;
+ ret = sched_getparam(syspids, ¶m);
+ if (errno == 0) found++;
+
+ errno= 0 ;
+ ret = sched_getscheduler(syspids);
+ if (errno == 0) found++;
+
+ errno=0;
+ ret = sched_rr_get_interval(syspids, &tp);
+ if (errno == 0) found++;
+
+ errno=0;
+ ret = kill(syspids, 0);
+ if (errno == 0) found_killafter=1;
+
+
+ /* these should all agree, except if a process went or came in the middle */
+ if (found_killbefore == found_killafter)
+ {
+ if ( ! ((found_killbefore == 0 && found == 0) ||
+ (found_killbefore == 1 && found == 7)) )
+ {
+ printbadpid(syspids);
+ }
+ } /* else: unreliable */
+ else
+ {
+ errno = 0 ;
+ warnln(verbose, unlog, "syscall comparison test skipped for PID %d.", syspids) ;
+ }
+ }
+}
+
+void checksysinfo(void)
+{
+
+ struct sysinfo info;
+ int contador=0;
+ int resultado_antes=0;
+ int resultado_despues=0;
+ int resultado = 0;
+ int ocultos=0;
+ char buffer[500];
+
+ FILE *fich_proceso ;
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through sysinfo() scanning\n") ;
+
+ buffer[499] = '\0';
+
+ sysinfo(&info);
+ resultado = resultado_antes=info.procs;
+
+ fich_proceso=popen (SYS_COMMAND, "r") ;
+ if (fich_proceso == NULL)
+ {
+ warnln(verbose, unlog, "Couldn't run command: %s, test aborted", SYS_COMMAND) ;
+ return;
+ }
+
+ while (NULL != fgets(buffer, 499, fich_proceso))
+ {
+ contador++;
+ if(verbose)
+ {
+ sysinfo(&info);
+ if (resultado != info.procs)
+ {
+ msgln(unlog, 1, "\tWARNING : info.procs changed during test : %d (was %d)",info.procs,resultado) ;
+ resultado = info.procs;
+ }
+ if (verbose >=2)
+ {
+ buffer[strlen(buffer)-1] = 0; // get rid of \n
+ snprintf(scratch, 1000, "\"%s\"",buffer) ;
+ msgln(unlog, 1, scratch) ;
+ }
+ }
+ }
+ pclose(fich_proceso);
+
+ sysinfo(&info);
+ resultado_despues=info.procs;
+ if(verbose >= 1) {
+ if (resultado != resultado_despues) {
+ msgln(unlog, 1, "\tWARNING : info.procs changed during test : %d (was %d)",resultado_despues,resultado) ;
+ }
+ }
+
+
+ if (resultado_antes == resultado_despues) /* otherwise intermittent activity.. */
+ {
+ // We add one as ps sees itself and not sysinfo.
+ ocultos=resultado_despues + 1 - contador ;
+
+ if (ocultos != 0) {
+ msgln(unlog, 1, "%i HIDDEN Processes Found\tsysinfo.procs reports %d processes and ps sees %d processes",abs(ocultos), resultado_despues,contador-1) ;
+ found_HP = 1;
+ }
+ }
+ else
+ {
+ errno = 0 ;
+ warnln(verbose, unlog, "sysinfo test skipped due to intermittent activity") ;
+ }
+
+}
+
+
+/*
+ * Compare the number of processes reported by sysinfo
+ * with the number of processes seen by ps
+ * Alternate version.
+ */
+void checksysinfo2()
+{
+
+ struct sysinfo info;
+ int contador=0;
+ int resultado_antes=0;
+ int resultado_despues=0;
+ int resultado = 0;
+ int ocultos=0;
+ char buffer[500];
+
+ FILE *fich_proceso ;
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through sysinfo() scanning\n") ;
+
+ buffer[499] = '\0';
+
+ fich_proceso=popen (SYS_COMMAND, "r") ;
+ if (fich_proceso == NULL)
+ {
+ warnln(verbose, unlog, "Couldn't run command: %s, test aborted", SYS_COMMAND) ;
+ return;
+ }
+
+ sysinfo(&info);
+ resultado = resultado_antes = info.procs;
+
+ while (NULL != fgets(buffer, 499, fich_proceso))
+ {
+ contador++;
+ if(verbose)
+ {
+ sysinfo(&info); // DEBUG
+ if (resultado != info.procs)
+ { // DEBUG
+ msgln(unlog, 1, "\tWARNING : info.procs changed during test : %d (was %d)",info.procs,resultado) ;
+ resultado = info.procs; // DEBUG
+ }
+ if (verbose >=2)
+ {
+ buffer[strlen(buffer)-1] = 0; // get rid of \n
+ snprintf(scratch, 1000, "\"%s\"",buffer) ;
+ msgln(unlog, 1, scratch) ;
+ }
+ }
+ }
+
+ sysinfo(&info);
+ resultado_despues=info.procs;
+ if(verbose >= 1)
+ {
+ if (resultado != resultado_despues)
+ {
+ msgln(unlog, 1, "\tWARNING : info.procs changed during test : %d (was %d)", resultado_despues, resultado) ;
+ }
+ }
+
+ pclose(fich_proceso);
+
+ if (resultado_antes == resultado_despues) /* otherwise intermittent activity.. */
+ {
+ ocultos=resultado_despues - contador;
+ if (ocultos != 0)
+ {
+ msgln(unlog, 1, "%i HIDDEN Processes Found\tsysinfo.procs reports %d processes and ps sees %d processes", abs(ocultos), resultado_despues,contador) ;
+ found_HP = 1;
+ }
+ }
+ else
+ {
+ errno = 0 ;
+ warnln(verbose, unlog, "sysinfo test skipped due to intermittent activity") ;
+ }
+
+}
+
+/*
+ * Compare the number of processes reported by sysinfo
+ * with the number of processes seen by ps
+ * minimal version.
+ */
+void checksysinfo3()
+{
+
+ struct sysinfo info;
+ int contador=0;
+ int resultado_antes=0;
+ int resultado_despues=0;
+ int ocultos=0;
+ char buffer[500];
+
+ FILE *fich_proceso ;
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through sysinfo() scanning\n") ;
+
+ buffer[499] = '\0';
+
+ if (NULL != (fich_proceso=popen (SYS_COMMAND, "r")))
+ {
+
+ sysinfo(&info);
+ resultado_antes = info.procs;
+
+ while (NULL != fgets(buffer, 499, fich_proceso))
+ {
+ contador++;
+ }
+
+ sysinfo(&info);
+ resultado_despues=info.procs;
+ pclose(fich_proceso);
+
+ if (resultado_antes == resultado_despues) /* otherwise intermittent activity.. */
+ {
+ ocultos=resultado_despues - contador;
+ if (ocultos != 0)
+ {
+ msgln(unlog, 1, "%i HIDDEN Processes Found\tsysinfo.procs reports %d processes and ps sees %d processes", abs(ocultos), resultado_despues,contador) ;
+ found_HP = 1;
+ }
+ }
+ else
+ {
+ errno = 0 ;
+ warnln(verbose, unlog, "sysinfo test skipped due to intermittent activity") ;
+ }
+ }
+ else
+ {
+ warnln(verbose, unlog, "Couldn't run command: %s, test aborted", SYS_COMMAND) ;
+ return;
+ }
+
+}
+
+
+char big_buffer[32768*6+1] ;
+/*
+ * Compare the number of processes reported by sysinfo
+ * with the number of processes seen by ps
+ * unbuffered version.
+ * In fact there is no way to accelerate sysinfo test with
+ * the procps version of ps, as it always sorts its output.
+ * Therefore its outpout is only available when all processing
+ * is finished.
+ */
+void checksysinfo4()
+{
+
+ struct sysinfo info;
+ int contador=0;
+ int resultado_antes=0;
+ int resultado_despues=0;
+ int ocultos=0;
+// char buffer[500];
+ ssize_t read_size, avail ;
+ char *buf_pt ;
+ int fd ;
+
+ FILE *fich_proceso ;
+
+ msgln(unlog, 0, "[*]Searching for Hidden processes through sysinfo() scanning\n") ;
+
+// buffer[499] = '\0';
+
+ buf_pt = big_buffer ;
+ read_size = 0 ;
+ avail = 32768*6 ;
+ if (NULL != (fich_proceso=popen (SYS_COMMAND, "r")))
+ {
+ fd = fileno(fich_proceso) ;
+ sysinfo(&info);
+ resultado_antes = info.procs;
+
+ while ((read_size = read(fd, buf_pt, avail)))
+ {
+ buf_pt += read_size ;
+ avail -= read_size ;
+ printf("%d\n", (int)read_size) ;
+ }
+
+ *buf_pt = 0 ;
+
+ sysinfo(&info);
+ resultado_despues=info.procs;
+ pclose(fich_proceso);
+
+ buf_pt = big_buffer ;
+ while (*buf_pt)
+ {
+ if (*buf_pt == '\n')
+ contador++ ;
+ buf_pt++ ;
+ }
+
+ if (resultado_antes == resultado_despues) /* otherwise intermittent activity.. */
+ {
+ ocultos=resultado_despues - contador;
+ if (ocultos != 0)
+ {
+ msgln(unlog, 1, "%i HIDDEN Processes Found\tsysinfo.procs reports %d processes and ps sees %d processes", abs(ocultos), resultado_despues,contador) ;
+ found_HP = 1;
+ }
+ }
+ else
+ {
+ errno = 0 ;
+ warnln(verbose, unlog, "sysinfo test skipped due to intermittent activity") ;
+ }
+ }
+ else
+ {
+ warnln(verbose, unlog, "Couldn't run command: %s, test aborted", SYS_COMMAND) ;
+ return;
+ }
+
+}
+
diff --git a/unhide-linux.c b/unhide-linux.c
new file mode 100644
index 0000000..1aca7dd
--- /dev/null
+++ b/unhide-linux.c
@@ -0,0 +1,795 @@
+/*
+ http://sourceforge.net/projects/unhide/
+*/
+
+/*
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+// Needed for unistd.h to declare getpgid() and others
+#define _XOPEN_SOURCE 500
+
+// Needed for sched.h to declare sched_getaffinity()
+#define _GNU_SOURCE
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "unhide-output.h"
+#include "unhide-linux.h"
+
+
+// header
+const char header[] =
+ "Unhide 20130526\n"
+ "Copyright © 2013 Yago Jesus & Patrick Gouin\n"
+ "License GPLv3+ : GNU GPL version 3 or later\n"
+ "http://www.unhide-forensics.info\n\n"
+ "NOTE : This version of unhide is for systems using Linux >= 2.6 \n\n";
+
+// defauly sysctl kernel.pid_max
+int maxpid = 32768;
+
+// Threads id for sync
+int tid ;
+
+// our own PID
+pid_t mypid ;
+
+// options
+int verbose = 0;
+int morecheck = FALSE;
+int RTsys = FALSE;
+int brutesimplecheck = TRUE;
+
+// Found hidden proccess flag
+int found_HP = 0;
+
+// For logging to file
+int logtofile;
+FILE *unlog;
+
+// Temporary string for output
+char used_options[1000];
+
+// Temporary string for output
+char scratch[1000];
+
+// table of test to perform
+struct tab_test_t tab_test[MAX_TESTNUM];
+
+
+/*
+ * Get the maximum number of process on this system.
+ */
+void get_max_pid(int* newmaxpid)
+{
+ char path[]= "/proc/sys/kernel/pid_max";
+ pid_t tmppid = 0;
+ FILE* fd= fopen(path,"r");
+ if(!fd)
+ {
+ warnln(1, unlog, "Cannot read current maximum PID. Using default value %d", * newmaxpid) ;
+ return;
+ }
+
+
+ if((fscanf(fd, "%d", &tmppid) != 1) || tmppid < 1)
+ {
+ msgln(unlog, 0, "Warning : Cannot get current maximum PID, error parsing %s format. Using default value %d", path, * newmaxpid) ;
+ return;
+ }
+ else
+ {
+ *newmaxpid = tmppid;
+ }
+ fclose(fd) ;
+}
+
+/*
+ * Verify if ps see a given pid.
+ */
+int checkps(int tmppid, int checks)
+{
+
+ int ok = 0;
+ char pids[30];
+
+ char compare[100];
+ char command[60];
+
+
+ FILE *fich_tmp ;
+
+// printf("in --> checkps\n"); // DEBUG
+
+// The compare string is the same for all test
+ sprintf(compare,"%i\n",tmppid);
+
+ if (PS_PROC == (checks & PS_PROC))
+ {
+ sprintf(command,COMMAND,tmppid) ;
+
+ fich_tmp=popen (command, "r") ;
+ if (fich_tmp == NULL)
+ {
+ warnln(verbose, unlog, "Couldn't run command: %s while ps checking pid %d", command, tmppid) ;
+ return(0);
+ }
+
+ {
+ char* tmp_pids = pids;
+
+ if (NULL != fgets(pids, 30, fich_tmp))
+ {
+ pids[29] = 0;
+
+// printf("pids = %s\n", pids); // DEBUG
+ while( *tmp_pids == ' ' && tmp_pids <= pids+29)
+ {
+ tmp_pids++;
+ }
+
+ if (strncmp(tmp_pids, compare, 30) == 0) {ok = 1;}
+ }
+ }
+
+ if (NULL != fich_tmp)
+ pclose(fich_tmp);
+
+ if (1 == ok) return(ok) ; // pid is found, no need to go further
+ }
+
+ if (PS_THREAD == (checks & PS_THREAD))
+ {
+ FILE *fich_thread ;
+
+ fich_thread=popen (THREADS, "r") ;
+ if (NULL == fich_thread)
+ {
+ warnln(verbose, unlog, "Couldn't run command: %s while ps checking pid %d", THREADS, tmppid) ;
+ return(0);
+ }
+
+ while ((NULL != fgets(pids, 30, fich_thread)) && ok == 0)
+ {
+ char* tmp_pids = pids;
+
+ pids[29] = 0;
+
+ while( *tmp_pids == ' ' && tmp_pids <= pids+29)
+ {
+ tmp_pids++;
+ }
+
+ if (strncmp(tmp_pids, compare, 30) == 0) {ok = 1;}
+ }
+ if (fich_thread != NULL)
+ pclose(fich_thread);
+
+ if (1 == ok) return(ok) ; // thread is found, no need to go further
+ }
+
+ if (PS_MORE == (checks & PS_MORE))
+ {
+
+ FILE *fich_session ;
+
+ sprintf(command,SESSION,tmppid) ;
+
+ fich_session=popen (command, "r") ;
+ if (fich_session == NULL)
+ {
+ warnln(verbose, unlog, "Couldn't run command: %s while ps checking pid %d", command, tmppid) ;
+ return(0);
+ }
+
+
+ while ((NULL != fgets(pids, 30, fich_session)) && ok == 0)
+ {
+ char* tmp_pids = pids;
+
+ pids[29] = 0;
+
+ while( *tmp_pids == ' ' && tmp_pids <= pids+29)
+ {
+ tmp_pids++;
+ }
+
+ if (strncmp(tmp_pids, compare, 30) == 0)
+ {
+ ok = 1;
+ }
+ }
+
+ pclose(fich_session);
+
+ if (1 == ok)
+ return(ok) ; // session is found, no need to go further
+
+ FILE *fich_pgid ;
+
+ fich_pgid=popen (PGID, "r") ;
+ if (NULL == fich_pgid)
+ {
+ warnln(verbose, unlog, "Couldn't run command: %s while ps checking pid %d", PGID, tmppid) ;
+ return(0);
+ }
+
+ while ((NULL != fgets(pids, 30, fich_pgid)) && ok == 0)
+ {
+ char* tmp_pids = pids;
+
+ pids[29] = 0;
+
+ while( *tmp_pids == ' ' && tmp_pids <= pids+29)
+ {
+ tmp_pids++;
+ }
+
+ if (strncmp(tmp_pids, compare, 30) == 0)
+ {
+ ok = 1;
+ }
+ }
+
+ pclose(fich_pgid);
+
+ }
+ return ok;
+}
+
+/*
+ * Display hidden process and possibly some information on it.
+ */
+void printbadpid (int tmppid)
+{
+
+ int statuscmd ;
+ char cmd[100] ;
+ struct stat buffer;
+ FILE *cmdfile ;
+ char cmdcont[1000], fmtstart[128];
+ int cmdok = 0, cmdok2 = 0;
+
+ found_HP = 1;
+ sprintf(fmtstart,"Found HIDDEN PID: %i", tmppid) ;
+ msgln(unlog, 0, "%s", fmtstart) ;
+
+ sprintf(cmd,"/proc/%i/cmdline",tmppid);
+
+ statuscmd = stat(cmd, &buffer);
+// statuscmd = 0 ; // DEBUG
+
+ if (statuscmd == 0)
+ {
+ cmdfile=fopen (cmd, "r") ;
+ if (cmdfile != NULL)
+ {
+ while ((NULL != fgets (cmdcont, 1000, cmdfile)) && 0 == cmdok)
+ {
+ cmdok++ ;
+ msgln(unlog, 0, "\tCmdline: \"%s\"", cmdcont) ;
+ }
+ fclose(cmdfile);
+ }
+ }
+ if (0 == cmdok)
+ {
+ msgln(unlog, 0, "\tCmdline: \"\"") ;
+ }
+
+ { // try to readlink the exe
+ ssize_t length ;
+
+ sprintf(cmd,"/proc/%i/exe",tmppid);
+ statuscmd = lstat(cmd, &buffer);
+// printf("%s",cmd) ; //DEBUG
+// printf("\tstatuscmd : %d\n",statuscmd) ; //DEBUG
+ if (statuscmd == 0)
+ {
+ length = readlink(cmd, cmdcont, 1000) ;
+// printf("\tLength : %0d\n",(int)length) ; //DEBUG
+ if (-1 != length)
+ {
+ cmdcont[length] = 0; // terminate the string
+ cmdok++;
+ msgln(unlog, 0, "\tExecutable: \"%s\"", cmdcont) ;
+ }
+ else
+ {
+ msgln(unlog, 0, "\tExecutable: \"\"") ;
+
+ }
+ }
+ else
+ {
+ msgln(unlog, 0, "\tExecutable: \"\"") ;
+ }
+ }
+ { // read internal command name
+ sprintf(cmd,"/proc/%i/comm",tmppid);
+ statuscmd = stat(cmd, &buffer);
+ if (statuscmd == 0)
+ {
+ cmdfile=fopen (cmd, "r") ;
+ if (cmdfile != NULL)
+ {
+// printf("\tCmdFile : %s\n",cmd) ; //DEBUG
+ while ((NULL != fgets (cmdcont, 1000, cmdfile)) && 0 == cmdok2)
+ {
+ cmdok2++;
+// printf("\tLastChar : %x\n",cmdcont[strlen(cmdcont)]) ; //DEBUG
+ if (cmdcont[strlen(cmdcont)-1] == '\n')
+ {
+ cmdcont[strlen(cmdcont)-1] = 0 ; // get rid of newline
+ }
+ if (0 == cmdok) // it is a kthreed : add brackets
+ {
+ msgln(unlog, 0, "\tCommand: \"[%s]\"", cmdcont) ;
+ }
+ else
+ {
+ msgln(unlog, 0, "\tCommand: \"%s\"", cmdcont) ;
+ }
+
+ }
+ fclose(cmdfile);
+ }
+ else
+ {
+ msgln(unlog, 0, "\tCommand: \"can't read file\"") ;
+ }
+ }
+ else
+ {
+ msgln(unlog, 0, "\t\" ... maybe a transitory process\"") ;
+ }
+ }
+ // try to print some useful info about the hidden process
+ // does not work well for kernel processes/threads and deamons
+ {
+ FILE *fich_tmp ;
+
+ sprintf(cmd,"/proc/%i/environ",tmppid);
+ statuscmd = stat(cmd, &buffer);
+ if (statuscmd == 0)
+ {
+ sprintf(cmd,"cat /proc/%i/environ | tr \"\\0\" \"\\n\" | grep -w 'USER'",tmppid) ;
+ // printf(cmd) ;
+ fich_tmp=popen (cmd, "r") ;
+ if (fich_tmp == NULL)
+ {
+ warnln(verbose, unlog, "\tCouldn't read USER for pid %d", tmppid) ;
+ }
+
+ if (NULL != fgets(cmdcont, 30, fich_tmp))
+ {
+ cmdcont[strlen(cmdcont)-1] = 0 ; // get rid of newline
+ msgln(unlog, 0, "\t$%s", cmdcont) ;
+ }
+ else
+ {
+ msgln(unlog, 0, "\t$USER=", cmdcont) ;
+ }
+ pclose(fich_tmp);
+
+ sprintf(cmd,"cat /proc/%i/environ | tr \"\\0\" \"\\n\" | grep -w 'PWD'",tmppid) ;
+ // printf(cmd) ;
+ fich_tmp=popen (cmd, "r") ;
+ if (fich_tmp == NULL)
+ {
+ warnln(verbose, unlog, "\tCouldn't read PWD for pid %d", tmppid) ;
+ }
+
+ if (NULL != fgets(cmdcont, 30, fich_tmp))
+ {
+ cmdcont[strlen(cmdcont)-1] = 0 ; // get rid of newline
+ msgln(unlog, 0, "\t$%s", cmdcont) ;
+ }
+ else
+ {
+ msgln(unlog, 0, "\t$PWD=", cmdcont) ;
+ }
+ pclose(fich_tmp);
+
+ // printf("Done !\n");
+ }
+ }
+ printf("\n");
+}
+
+
+/*
+ * Display short help
+ */
+void usage(char * command)
+{
+
+ printf("Usage: %s [options] test_list\n\n", command);
+ printf("Option :\n");
+ printf(" -V Show version and exit\n");
+ printf(" -v verbose\n");
+ printf(" -h display this help\n");
+ printf(" -m more checks (available only with procfs, checkopendir & checkchdir commands)\n");
+ printf(" -r use alternate sysinfo test in meta-test\n");
+ printf(" -f log result into unhide-linux.log file\n");
+ printf(" -o same as '-f'\n");
+ printf(" -d do a double check in brute test\n");
+ printf("Test_list :\n");
+ printf(" Test_list is one or more of the following\n");
+ printf(" Standard tests :\n");
+ printf(" brute\n");
+ printf(" proc\n");
+ printf(" procall\n");
+ printf(" procfs\n");
+ printf(" quick\n");
+ printf(" reverse\n");
+ printf(" sys\n");
+ printf(" Elementary tests :\n");
+ printf(" checkbrute\n");
+ printf(" checkchdir\n");
+ printf(" checkgetaffinity\n");
+ printf(" checkgetparam\n");
+ printf(" checkgetpgid\n");
+ printf(" checkgetprio\n");
+ printf(" checkRRgetinterval\n");
+ printf(" checkgetsched\n");
+ printf(" checkgetsid\n");
+ printf(" checkkill\n");
+ printf(" checknoprocps\n");
+ printf(" checkopendir\n");
+ printf(" checkproc\n");
+ printf(" checkquick\n");
+ printf(" checkreaddir\n");
+ printf(" checkreverse\n");
+ printf(" checksysinfo\n");
+ printf(" checksysinfo2\n");
+ printf(" checksysinfo3\n");
+}
+
+/*
+ * Parse command line arguments (exiting if requested by any option).
+ */
+void parse_args(int argc, char **argv)
+{
+ int c = 0;
+ int index = 0;
+
+ static struct option long_options[] =
+ {
+ /* These options set a flag. */
+ {"brute-doublecheck", no_argument, &brutesimplecheck, 0},
+ {"alt-sysinfo", no_argument, &RTsys, 1},
+ {"log", no_argument, &logtofile, 1},
+ /* These options don't set a flag.
+ We distinguish them by their indices. */
+ {"morecheck", no_argument, 0, 'm'},
+ {"verbose", no_argument, 0, 'v'},
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'V'},
+ {0, 0, 0, 0}
+ };
+
+ for(;;) // until there's no more option
+ {
+ /* getopt_long stores the option index here. */
+ int option_index = 0;
+
+ c = getopt_long (argc, argv, "dformhvV",
+ long_options, &option_index);
+
+ /* Detect the end of the options. */
+ if (c == -1)
+ break;
+
+ switch(c)
+ {
+ case 0 : // flag long options
+ if (long_options[option_index].flag != 0) //if this option set a flag
+ {
+ break; // nothing to do
+ }
+ printf ("option %s", long_options[option_index].name);
+ if (optarg) // if there's an argument
+ {
+ printf (" with arg %s", optarg);
+ }
+ printf ("\n");
+ break ;
+ case 'd' :
+ brutesimplecheck = FALSE;
+ break ;
+ case 'h' :
+ usage(argv[0]) ;
+ exit (0) ;
+ break ;
+ case 'f' :
+ logtofile = 1;
+ break;
+ case 'o' :
+ logtofile = 1 ;
+ break ;
+ case 'm' :
+ morecheck = TRUE;
+ verbose = TRUE;
+ break ;
+ case 'r' :
+ RTsys = TRUE;
+ break ;
+ case 'v' :
+ verbose++ ; ;
+ break ;
+ case 'V' :
+ exit (0) ;
+ break ;
+ case '?' : // invalid option
+ exit (2) ;
+ break ;
+ default : // something very nasty happened
+ exit(-1) ;
+ break ;
+ }
+
+ }
+
+ // generate options string for logging
+ strncpy(used_options, "Used options: ", 1000);
+ if (verbose)
+ strncat(used_options, "verbose ", 1000-1-strlen(used_options));
+ if (!brutesimplecheck)
+ strncat(used_options, "brutesimplecheck ", 1000-1-strlen(used_options));
+ if (morecheck)
+ strncat(used_options, "morecheck ", 1000-1-strlen(used_options));
+ if (RTsys)
+ strncat(used_options, "RTsys ", 1000-1-strlen(used_options));
+ if (logtofile)
+ strncat(used_options, "logtofile ", 1000-1-strlen(used_options));
+
+ // Process list of tests to do
+ for (index = optind; index < argc; index++)
+ {
+ if ((strcmp(argv[index], "proc") == 0) ||
+ (strcmp(argv[index], "checkproc") == 0))
+ {
+ tab_test[TST_PROC].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "procfs") == 0)
+ {
+ tab_test[TST_CHDIR].todo = TRUE;
+ tab_test[TST_OPENDIR].todo = TRUE;
+ tab_test[TST_READDIR].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "procall") == 0)
+ {
+ tab_test[TST_PROC].todo = TRUE;
+ tab_test[TST_CHDIR].todo = TRUE;
+ tab_test[TST_OPENDIR].todo = TRUE;
+ tab_test[TST_READDIR].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "sys") == 0)
+ {
+ tab_test[TST_KILL].todo = TRUE;
+ tab_test[TST_NOPROCPS].todo = TRUE;
+ tab_test[TST_GETPRIO].todo = TRUE;
+ tab_test[TST_GETPGID].todo = TRUE;
+ tab_test[TST_GETSID].todo = TRUE;
+ tab_test[TST_GETAFF].todo = TRUE;
+ tab_test[TST_GETPARM].todo = TRUE;
+ tab_test[TST_GETSCHED].todo = TRUE;
+ tab_test[TST_RR_INT].todo = TRUE;
+/* Remove sysinfo test from sys compound test as it give FP in some case
+ if (TRUE == RTsys)
+ {
+ tab_test[TST_SYS_INFO2].todo = TRUE;
+ }
+ else
+ {
+ tab_test[TST_SYS_INFO].todo = TRUE;
+ }
+*/
+ }
+ else if (strcmp(argv[index], "quick") == 0)
+ {
+ tab_test[TST_QUICKONLY].todo = TRUE;
+/* Remove sysinfo test from quick compound test as it give FP in some case
+ if (TRUE == RTsys)
+ {
+ tab_test[TST_SYS_INFO2].todo = TRUE;
+ }
+ else
+ {
+ tab_test[TST_SYS_INFO].todo = TRUE;
+ }
+*/
+ }
+ else if ((strcmp(argv[index], "brute") == 0) ||
+ (strcmp(argv[index], "checkbrute") == 0))
+ {
+ tab_test[TST_BRUTE].todo = TRUE;
+ }
+ else if ((strcmp(argv[index], "reverse") == 0) ||
+ (strcmp(argv[index], "checkreverse") == 0))
+ {
+ tab_test[TST_REVERSE].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "opendir") == 0)
+ {
+ tab_test[TST_OPENDIR].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "checkquick") == 0)
+ {
+ tab_test[TST_QUICKONLY].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "checksysinfo") == 0)
+ {
+ tab_test[TST_SYS_INFO].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "checksysinfo2") == 0)
+ {
+ tab_test[TST_SYS_INFO2].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "checksysinfo3") == 0)
+ {
+ tab_test[TST_SYS_INFO3].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "checkchdir") == 0)
+ {
+ tab_test[TST_CHDIR].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "checkreaddir") == 0)
+ {
+ tab_test[TST_READDIR].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "checkopendir") == 0)
+ {
+ tab_test[TST_OPENDIR].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "checkkill") == 0)
+ {
+ tab_test[TST_KILL].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "checknoprocps") == 0)
+ {
+ tab_test[TST_NOPROCPS].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "checkgetprio") == 0)
+ {
+ tab_test[TST_GETPRIO].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "checkgetpgid") == 0)
+ {
+ tab_test[TST_GETPGID].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "checkgetsid") == 0)
+ {
+ tab_test[TST_GETSID].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "checkgetaffinity") == 0)
+ {
+ tab_test[TST_GETAFF].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "checkgetparam") == 0)
+ {
+ tab_test[TST_GETPARM].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "checkgetsched") == 0)
+ {
+ tab_test[TST_GETSCHED].todo = TRUE;
+ }
+ else if (strcmp(argv[index], "checkRRgetinterval") == 0)
+ {
+ tab_test[TST_RR_INT].todo = TRUE;
+ }
+ else
+ {
+ printf("Unknown argument\n") ; usage(argv[0]); exit(0);
+ }
+ }
+
+
+}
+
+
+int main (int argc, char *argv[])
+{
+int i;
+
+ printf(header) ;
+ if(getuid() != 0){
+ die(unlog, "You must be root to run %s !", argv[0]) ;
+ }
+
+ // Initialize the table of test to perform.
+ // ---------------------------------------
+ for (i=0 ; i.
+*/
+
+
+// External commands
+// =================
+// we are looking only for real process not thread and only one by one
+#define COMMAND "ps --no-header -p %i o pid"
+// we are looking for session ID one by one
+#define SESSION "ps --no-header -s %i o sess"
+// We are looking for group ID one by one
+// but ps can't select by pgid
+#define PGID "ps --no-header -eL o pgid"
+// We are looking for all processes even threads
+#define THREADS "ps --no-header -eL o lwp"
+// for sysinfo scanning, fall back to old command, as --no-header seems to create
+// an extra process/thread
+// #define SYS_COMMAND "ps -eL o lwp"
+#define SYS_COMMAND "ps --no-header -eL o lwp"
+// an extra process/thread
+#define REVERSE "ps --no-header -eL o lwp,cmd"
+
+// Masks for the checks to do in checkps
+// =====================================
+#define PS_PROC 0x00000001
+#define PS_THREAD 0x00000002
+#define PS_MORE 0x00000004
+
+// Test numbers
+// ============
+// note that checkps can't be call alone.
+enum test_num {
+ // Individual test
+ TST_NONE = 0,
+ TST_VERSION,
+ TST_PROC,
+ TST_CHDIR,
+ TST_OPENDIR,
+ TST_READDIR,
+ TST_GETPRIO,
+ TST_GETPGID,
+ TST_GETSID,
+ TST_GETAFF,
+ TST_GETPARM,
+ TST_GETSCHED,
+ TST_RR_INT,
+ TST_KILL,
+ TST_NOPROCPS,
+ TST_BRUTE,
+ TST_REVERSE,
+ TST_QUICKONLY,
+ TST_SYS_INFO,
+ TST_SYS_INFO2,
+ TST_SYS_INFO3,
+ // meta test
+ TST_DIR,
+ TST_SYS,
+ TST_QUICK,
+ TST_PROCALL,
+ // MAX number, should be the last of enum.
+ MAX_TESTNUM
+};
+
+// boolean values
+// ==============
+#define FALSE 0
+#define TRUE 1
+
+// Structure of the table of tests
+// ===============================
+struct tab_test_t {
+ int todo;
+ void (*func)(void);
+} ;
+
+
+// Default sysctl kernel.pid_max
+extern int maxpid ;
+
+// Threads id for sync
+extern int tid ;
+
+// our own PID
+extern pid_t mypid ;
+
+// options
+extern int verbose ;
+extern int morecheck ;
+extern int RTsys ;
+extern int brutesimplecheck ;
+
+// Found hidden proccess flag
+extern int found_HP ;
+
+// Temporary string for output
+extern char used_options[1000];
+
+// For logging to file
+extern int logtofile;
+extern FILE *unlog;
+
+// Temporary string for output
+extern char scratch[1000];
+
+extern struct tab_test_t tab_test[MAX_TESTNUM];
+
+// prototypes
+// ==========
+// unhide-linux-bruteforce.c
+extern void *funcionThread (void *parametro) ;
+extern void brute(void) ;
+
+// unhide-linux.c
+extern void get_max_pid(int* newmaxpid) ;
+extern int checkps(int tmppid, int checks) ;
+extern void printbadpid (int tmppid) ;
+extern void usage(char * command) ;
+extern void parse_args(int argc, char **argv) ;
+
+// unhide-linux-procfs.c
+extern void checkproc(void) ;
+extern void checkchdir(void) ;
+extern void checkopendir(void) ;
+extern void checkreaddir(void) ;
+
+// unhide-linux-syscall.c
+extern void checkgetpriority(void) ;
+extern void checkgetpgid(void) ;
+extern void checkgetsid(void) ;
+extern void checksched_getaffinity(void) ;
+extern void checksched_getparam(void) ;
+extern void checksched_getscheduler(void) ;
+extern void checksched_rr_get_interval(void) ;
+extern void checkkill(void) ;
+extern void checkallnoprocps(void) ;
+extern void checksysinfo(void) ;
+extern void checksysinfo2(void) ;
+extern void checksysinfo3(void) ;
+extern void checksysinfo4(void) ;
+
+// unhide-linux-compound.c
+extern void checkallquick(void) ;
+extern void checkallreverse(void) ;
diff --git a/unhide-output.c b/unhide-output.c
new file mode 100644
index 0000000..284a5e6
--- /dev/null
+++ b/unhide-output.c
@@ -0,0 +1,193 @@
+/*
+ http://www.unhide-forensics.info
+*/
+
+/*
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include
+#include
+#include
+#include
+#ifdef __linux__
+ #include
+#else
+ #include
+#endif
+#include
+#include
+
+#include "unhide-output.h"
+
+/*
+ * Print a message to a file stream (and log the message if necessary).
+ */
+void vfmsg(FILE * unlog, FILE* fp, const char* fmt, va_list ap)
+{
+ char buf[BUFSIZ];
+
+ vsnprintf(buf, BUFSIZ, fmt, ap);
+ fputs(buf, fp);
+
+ if (NULL != unlog)
+ fputs(buf, unlog);
+}
+
+
+/*
+ * Print a message to a stdout (and log the message if necessary), appending \n.
+ */
+void msgln(FILE * unlog, int indent, const char* fmt, ...)
+{
+ char buf[BUFSIZ];
+ va_list ap;
+
+ if(1 == indent)
+ {
+ strncpy(buf, "\t", BUFSIZ);
+ strncat(buf, fmt, BUFSIZ-strlen(buf));
+ }
+ else
+ {
+ strncpy(buf, fmt, BUFSIZ);
+ }
+ strncat(buf, "\n", BUFSIZ-1-strlen(buf));
+
+ va_start(ap, fmt);
+ vfmsg(unlog, stdout, buf, ap);
+ va_end(ap);
+}
+
+
+/*
+ * Print a warning message to a stderr (and log the message if necessary),
+ * appending \n, only if in verbose mode.
+ *
+ * If errno is not 0, then information about the last error is printed too.
+ */
+void warnln(int verbose, FILE * unlog, const char* fmt, ...)
+{
+ char buf[BUFSIZ];
+ va_list ap;
+ int e = errno; /* save it in case some other function fails */
+
+ if (!verbose)
+ {
+ return;
+ }
+
+ strncpy(buf, "Warning : ", BUFSIZ);
+ strncat(buf, fmt, BUFSIZ-1-strlen(buf));
+ if (e != 0)
+ {
+ strncat(buf, " [", BUFSIZ-1-strlen(buf));
+ strncat(buf, strerror(e), BUFSIZ-1-strlen(buf));
+ strncat(buf, "]", BUFSIZ-1-strlen(buf));
+ }
+ strncat(buf, "\n", BUFSIZ-1-strlen(buf));
+
+ va_start(ap, fmt);
+ vfmsg(unlog, stderr, buf, ap);
+ va_end(ap);
+}
+
+
+/*
+ * Print an error to stderr and exit with code 1.
+ *
+ * If errno is not 0, then information about the last error is printed too.
+ */
+void die(FILE * unlog, const char* fmt, ...)
+{
+ va_list ap;
+ char buf[BUFSIZ];
+ int e = errno; /* save it in case some other function fails */
+
+ strncpy(buf, "Error : ", BUFSIZ);
+ strncat(buf, fmt, BUFSIZ-1-strlen(buf));
+ if (e != 0)
+ {
+ strncat(buf, " [", BUFSIZ-1-strlen(buf));
+ strncat(buf, strerror(e), BUFSIZ-1-strlen(buf));
+ strncat(buf, "]", BUFSIZ-1-strlen(buf));
+ }
+ strncat(buf, "\n", BUFSIZ-1-strlen(buf));
+
+ va_start(ap, fmt);
+ vfmsg(unlog, stderr, buf, ap);
+ va_end(ap);
+
+ exit(1);
+}
+
+/*
+ * Initialize and write a header to the log file.
+ */
+FILE *init_log(int logtofile, const char *header, const char *basename)
+{
+ FILE *fh ;
+ char filename[PATH_MAX] ;
+ time_t scantime;
+ struct tm *tmPtr;
+ char cad[80];
+
+ if (0 == logtofile)
+ {
+ return(NULL);
+ }
+
+ scantime = time(NULL);
+ tmPtr = localtime(&scantime);
+ sprintf(filename, "%s_%4d-%02d-%02d.%s", basename, tmPtr->tm_year+1900, tmPtr->tm_mon + 1, tmPtr->tm_mday, "log" );
+
+ fh = fopen(filename, "w");
+
+ if (NULL == fh)
+ {
+ logtofile = 0; // inhibit write to log file
+ warnln(1, NULL, "Unable to open log file (%s)!", filename) ;
+ return(NULL) ;
+ }
+
+ fputs(header, fh);
+
+ strftime( cad, 80, "%H:%M:%S, %F", tmPtr );
+
+ fprintf(fh, "\n%s scan starting at: %s\n", basename, cad) ;
+ return(fh);
+}
+
+/* Write a footer and close the log file. */
+void close_log(FILE *fh, const char *basename)
+{
+
+ if (NULL == fh)
+ {
+ return ;
+ }
+
+ time_t scantime;
+ char cad[80];
+ struct tm *tmPtr;
+
+ scantime = time(NULL);
+ tmPtr = localtime(&scantime);
+ strftime( cad, 80, "%H:%M:%S, %F", tmPtr );
+
+ fprintf(fh, "%s scan ending at: %s\n", basename, cad );
+ fclose(fh);
+}
+
+
diff --git a/unhide-output.h b/unhide-output.h
new file mode 100644
index 0000000..b992c01
--- /dev/null
+++ b/unhide-output.h
@@ -0,0 +1,48 @@
+/*
+ http://www.unhide-forensics.info
+*/
+
+/*
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include
+
+/*
+ * Globals
+ */
+
+
+/*
+ * unhide-output
+ */
+
+// Print a message to a file stream (and log the message if necessary).
+extern void vfmsg(FILE * unlog, FILE* fp, const char* fmt, va_list ap) ;
+
+// Print a message to a stdout (and log the message if necessary), appending \n.
+extern void msgln(FILE * unlog, int indent, const char* fmt, ...) ;
+
+// Print a warning message to a stderr (and log the message if necessary),
+extern void warnln(int verbose, FILE * unlog, const char* fmt, ...) ;
+
+// Print an error to stderr and exit with code 1.
+extern void die(FILE * unlog, const char* fmt, ...) ;
+
+// Initialize and write a header to the log file.
+extern FILE *init_log(int logtofile, const char *header, const char *basename) ;
+
+// Write a footer and close a log file.
+extern void close_log(FILE *fh, const char *basename) ;
+
diff --git a/unhide-posix.c b/unhide-posix.c
new file mode 100644
index 0000000..471efdd
--- /dev/null
+++ b/unhide-posix.c
@@ -0,0 +1,264 @@
+/*
+ http://www.unhide-forensics.info
+*/
+
+/*
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+// Needed for unistd.h to declare getpgid() and others
+#define _XOPEN_SOURCE 500
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+#ifdef __linux__
+ // Linux
+ #define COMMAND "ps -eLf | awk '{ print $2 }' | grep -v PID"
+// Old Linux (without threads)
+// #define COMMAND "ps -ax | awk '{ print $1 }' | grep -v PID"
+// CentOS / RHEL linux (thanks unspawn@rootshell.be and Martin.Bowers@freescale.com )
+// #define COMMAND "ps -emf --no-headers| awk '{ print $2 }'"
+#else
+ #ifdef __OpenBSD__
+ //OpenBSD
+ #define COMMAND "ps -axk | awk '{ print $1 }' | grep -v PID"
+ #else
+ #if defined(sun) || defined(__sun)
+ # if defined(__SVR4) || defined(__svr4__)
+ /* Solaris */
+ #define COMMAND "ps -elf | awk '{ print $4 }' | grep -v PID"
+ # else
+ /* SunOS */
+ # endif
+ #else
+ #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ //FreeBSD
+ #define COMMAND "ps -axH | awk '{ print $1 }' | grep -v PID"
+ #else
+ //default : unknown OS
+ #define COMMAND "ps -ax | awk '{ print $1 }' | grep -v PID"
+ #endif
+ #endif
+ #endif
+#endif
+
+
+
+int maxpid= 999999;
+// Temporary string for output
+char scratch[1000];
+
+// Shut up some warnings with over pedantic version of glibc
+int ret;
+
+
+void checkps(int tmppid) {
+
+ int ok = 0;
+ char pids[30];
+ char compare[100];
+
+ FILE *fich_tmp ;
+
+ fich_tmp=popen (COMMAND, "r") ;
+
+
+ while (!feof(fich_tmp) && ok == 0) {
+
+ fgets(pids, 30, fich_tmp);
+
+ sprintf(compare,"%i\n",tmppid);
+
+ if (strcmp(pids, compare) == 0) {ok = 1;}
+
+
+ }
+
+ pclose(fich_tmp);
+
+ if ( ok == 0 ) {
+
+ int statuscmd ;
+ char cmd[100] ;
+
+ struct stat buffer;
+
+ printf ("Found HIDDEN PID: %i\n", tmppid) ;
+
+ sprintf(cmd,"/proc/%i/cmdline",tmppid);
+
+ statuscmd = stat(cmd, &buffer);
+
+ if (statuscmd == 0) {
+
+ FILE *cmdfile ;
+ char cmdcont[1000];
+
+ cmdfile=fopen (cmd, "r") ;
+
+
+ while (!feof (cmdfile)) {
+
+ fgets (cmdcont, 1000, cmdfile);
+ printf ("Command: %s\n\n", cmdcont);
+
+ }
+ }
+
+ }
+
+}
+
+void checkproc() {
+
+ int procpids ;
+
+ int statusproc;
+ struct stat buffer;
+
+ printf ("[*]Searching for Hidden processes through /proc scanning\n\n") ;
+
+ for ( procpids = 1; procpids <= maxpid; procpids = procpids +1 ) {
+
+ char directory[100] ;
+
+
+ sprintf(directory,"/proc/%d",procpids);
+
+
+ statusproc = stat(directory, &buffer) ;
+
+ if (statusproc == 0) {
+
+ checkps(procpids);
+
+ }
+
+ }
+}
+
+void checkgetpriority() {
+
+ int syspids ;
+
+ printf ("[*]Searching for Hidden processes through getpriority() scanning\n\n") ;
+
+
+ for ( syspids = 1; syspids <= maxpid; syspids = syspids +1 ) {
+
+ int which = PRIO_PROCESS;
+
+// int ret;
+
+ errno= 0 ;
+
+ ret = getpriority(which, syspids);
+
+ if ( errno == 0) {
+
+ checkps(syspids);
+ }
+ }
+}
+
+void checkgetpgid() {
+
+ int syspids ;
+
+ printf ("[*]Searching for Hidden processes through getpgid() scanning\n\n") ;
+
+
+
+ for ( syspids = 1; syspids <= maxpid; syspids = syspids +1 ) {
+
+// int ret;
+
+ errno= 0 ;
+
+ ret = getpgid(syspids);
+
+ if ( errno == 0) {
+
+ checkps(syspids);
+ }
+ }
+}
+
+
+void checkgetsid() {
+
+ int syspids ;
+
+
+ printf ("[*]Searching for Hidden processes through getsid() scanning\n\n") ;
+
+
+ for ( syspids = 1; syspids <= maxpid; syspids = syspids +1 ) {
+
+// int ret;
+
+ errno= 0 ;
+
+ ret = getsid(syspids);
+
+ if ( errno == 0) {
+
+ checkps(syspids);
+ }
+ }
+}
+
+
+
+int main (int argc, char *argv[]) {
+
+ strncpy(scratch,"Unhide-posix 20130526\n", 1000) ;
+ strncat(scratch, "Copyright © 2013 Yago Jesus & Patrick Gouin\n", 1000);
+ strncat(scratch, "License GPLv3+ : GNU GPL version 3 or later\n", 1000);
+ strncat(scratch, "http://www.unhide-forensics.info\n\n", 1000);
+ strncat(scratch, "NOTE : This is legacy version of unhide, it is intended\n\
+ for systems using Linux < 2.6 or other UNIX systems\n\n", 1000);
+ fputs(scratch, stdout);
+
+
+ if(argc != 2) {
+
+ printf("usage: %s proc | sys\n\n", argv[0]);
+ exit (1);
+
+ }
+
+ if (strcmp(argv[1], "proc") == 0) {checkproc();}
+
+ else if (strcmp(argv[1], "sys") == 0) {
+ checkgetpriority();
+ checkgetpgid() ;
+ checkgetsid();
+
+ }
+
+ else {
+ printf("usage: %s proc | sys\n\n", argv[0]);
+ exit (1);
+ }
+ return(0) ;
+}
diff --git a/unhide-tcp-fast.c b/unhide-tcp-fast.c
new file mode 100644
index 0000000..256114e
--- /dev/null
+++ b/unhide-tcp-fast.c
@@ -0,0 +1,218 @@
+/*
+ http://www.unhide-forensics.info
+*/
+
+/*
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+// #include
+
+#include "unhide-output.h"
+#include "unhide-tcp.h"
+
+/*
+ * These are simplified hash set to store the ports that:
+ *
+ * - are visible to netstat
+ * - are found to be hidden by us
+ * - we want to check
+ *
+ * A value of 0 means the port is NOT in the set, and a value != 0 means
+ * otherwise.
+ */
+
+static char netstat_ports[65536];
+static char hidden_ports[65536];
+static char check_ports[65536];
+
+/* Fill netstat_ports with the ports netstat see as used for protocol proto. */
+static void get_netstat_ports(enum Proto proto)
+{
+ FILE *fp;
+ int port;
+
+ if (TCP == proto)
+ {
+ fp=popen (tcpcommand1, "r");
+ }
+ else
+ {
+ fp=popen (udpcommand1, "r");
+ }
+
+ if (fp == NULL)
+ {
+ die(unlog, "popen failed to open netstat to get the ports list");
+ }
+
+ memset(netstat_ports, 0, sizeof(netstat_ports));
+
+ errno = 0;
+ while (!feof(fp))
+ {
+ if (fscanf(fp, "%i\n", &port) == EOF && errno != 0)
+ {
+ die(unlog, "fscanf failed to parse int");
+ }
+
+ netstat_ports[port] = 1;
+ }
+
+ pclose(fp);
+}
+
+
+/*
+ * Check a list of ports against what netstat report as used ports.
+ *
+ * All ports that are not reported as used by netstat are opened, binded and
+ * put in listen state (for the TCP proto). If any of that operations fail with
+ * an EADDRINUSE, it's reported as a port hidden to netstat.
+ */
+static void check(enum Proto proto)
+{
+ int i;
+ int protocol;
+
+ if (proto == TCP)
+ protocol = SOCK_STREAM;
+ else if (proto == UDP)
+ protocol = SOCK_DGRAM;
+ else
+ abort();
+
+ memset(hidden_ports, 0, sizeof(hidden_ports));
+ hidden_found = 0;
+
+ get_netstat_ports(proto);
+ for (i = 0; i < 65536; i++)
+ {
+ int fd;
+ int reuseaddr;
+ struct sockaddr_in addr;
+
+ /*
+ * skip if is not a port to check or is already visible to
+ * netstat
+ */
+ if (!check_ports[i] || netstat_ports[i])
+ {
+ continue;
+ }
+
+ fd = socket(AF_INET, protocol, 0);
+ if (fd == -1)
+ {
+ die(unlog, "socket creation failed");
+ }
+
+ reuseaddr = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
+ sizeof(reuseaddr)) != 0)
+ {
+ die(unlog, "setsockopt can't set SO_REUSEADDR");
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = htons(i);
+
+ /*
+ * if we can't bind or listen because the address is used, the
+ * port is asumed to be used and added to the hidden_ports list
+ * because we only check for ports not visible by netstat.
+ * If we can bind them, we remove them from the check_ports
+ * list so we don't try to check them again if a new pass is
+ * performed in the future.
+ */
+ errno = 0;
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) != 0 ||
+ (proto == TCP && listen(fd, 1) != 0))
+ {
+ if (errno == EADDRINUSE)
+ {
+ hidden_ports[i] = 1;
+ hidden_found++;
+ }
+ else
+ {
+ warnln(verbose, unlog, "bind failed, maybe you are not root?");
+ check_ports[i] = 0;
+ }
+ }
+ else
+ {
+ check_ports[i] = 0;
+ }
+
+ close(fd);
+ }
+}
+
+
+/*
+ * Print ports not visible to netstat but that are being used.
+ *
+ * The check for hidden ports is retried to minimize false positives, see
+ * comments inside the function for details.
+ */
+void print_hidden_ports(enum Proto proto)
+{
+ /* reset the list of ports to check (we start wanting to check all of
+ * them) and the list of hidden ports (none is hidden until we prove
+ * otherwise)
+ */
+ memset(check_ports, 1, sizeof(check_ports));
+ memset(hidden_ports, 0, sizeof(hidden_ports));
+
+ /*
+ * Double-check to minimize false positives.
+ *
+ * For very short lived connections we have a race condition between
+ * getting the output from netstat and trying to open the port
+ * ourselves. To minize this problem we check again the ports reported
+ * as hidden. If in the next run of netstat those ports are not present
+ * anymore, is fairly safe to asume they were false positives.
+ */
+ check(proto);
+ if (hidden_found)
+ {
+ memcpy(check_ports, hidden_ports, sizeof(hidden_ports));
+ check(proto);
+ }
+
+ if (hidden_found)
+ {
+ int i;
+ for (i = 0; i < 65536; i++)
+ {
+ if (hidden_ports[i])
+ {
+ print_port(proto, i);
+ }
+ }
+ }
+}
+
+
diff --git a/unhide-tcp-simple-check.c b/unhide-tcp-simple-check.c
new file mode 100644
index 0000000..a6c11b1
--- /dev/null
+++ b/unhide-tcp-simple-check.c
@@ -0,0 +1,499 @@
+/*
+ http://www.unhide-forensics.info
+*/
+
+/*
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "unhide-output.h"
+#include "unhide-tcp.h"
+
+// header
+const char header[] =
+ "Unhide-tcp 20121229\n"
+ "Copyright © 2012 Yago Jesus & Patrick Gouin\n"
+ "License GPLv3+ : GNU GPL version 3 or later\n"
+ "http://www.unhide-forensics.info\n";
+
+// options
+int verbose = 0;
+int use_fuser = 0;
+int use_lsof = 0;
+int use_ss = 1; // use ss by default
+int use_quick = 0;
+
+char checker[10] = "ss" ;
+
+// Temporary string for output
+char scratch[1000];
+
+// Temporary string for output
+char used_options[1000] = "";
+
+// For logging to file
+int logtofile = 0;
+FILE *unlog;
+
+// Global hidden port counter, used only to set the exit code of the program
+int hidden_found;
+
+/* thx aramosf@unsec.net for the nice regexp! */
+
+// Linux
+char tcpcommand1[]= "netstat -tan | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+char udpcommand1[]= "netstat -uan | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+
+// Alternative commands, needs iproute2
+char tcpcommand2[]= "ss -tan sport = :%d | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+char udpcommand2[]= "ss -uan sport = :%d | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+
+// fuser commands
+char fuserTCPcommand[]= "fuser -v -n tcp %d 2>&1" ;
+char fuserUDPcommand[]= "fuser -v -n udp %d 2>&1" ;
+
+// lsof commands
+char lsofTCPcommand[]= "lsof +c 0 -iTCP:%d" ;
+char lsofUDPcommand[]= "lsof +c 0 -iUDP:%d" ;
+
+// OpenBSD
+// char tcpcommand[]= "netstat -an -p tcp | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+// char udpcommand[]= "netstat -an -p udp| sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+
+
+// Solaris
+// char tcpcommand[]= "netstat -an -P tcp | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+// char udpcommand[]= "netstat -an -P udp| sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+
+
+
+/*
+ * Run a command to get more information about a port.
+ */
+static void print_info(const char *prog_name, const char *command_fmt, int port)
+{
+
+ char buffer[1000];
+ FILE* fp;
+
+ sprintf(buffer, command_fmt, port);
+ fp = popen(buffer, "r") ;
+
+ if (NULL == fp)
+ {
+ warnln(verbose, unlog, "Couldn't run command: %s", buffer) ;
+ return ;
+ }
+
+ msgln(unlog, 1, "%s reports :", prog_name) ;
+
+ while (NULL != fgets(buffer, 1000, fp))
+ {
+ msgln(unlog, 1, buffer) ;
+ }
+
+ pclose(fp);
+}
+
+/* Print a port, optionally querying info about it via lsof or fuser. */
+void print_port(enum Proto proto, int port)
+{
+ msgln(unlog, 0, "\nFound Hidden port that not appears in %s: %i", checker, port) ;
+ if (1 == use_fuser)
+ {
+ if (TCP == proto)
+ {
+ print_info("fuser", fuserTCPcommand, port);
+ }
+ else
+ {
+ print_info("fuser", fuserUDPcommand, port);
+ }
+ }
+ if (1 == use_lsof)
+ {
+ if (TCP == proto)
+ {
+ print_info("lsof", lsofTCPcommand, port);
+ }
+ else
+ {
+ print_info("lsof", lsofUDPcommand, port);
+ }
+ }
+}
+
+
+/*
+ * Check if port is seen by netstat.
+ *
+ * If not, report it and optionnally run lsof and/or fuser
+ * to show more info.
+ */
+void checkoneport(int port, char command[], enum Proto proto)
+{
+ int ok = 0;
+ char ports[30];
+ char compare[100];
+
+ FILE *fich_tmp ;
+
+ if (NULL != (fich_tmp=popen (command, "r")))
+ {
+ sprintf(compare,"%i\n",port);
+ while ((NULL != fgets(ports, 30, fich_tmp)) && ok == 0) {
+ if (strcmp(ports, compare) == 0) {ok = 1;}
+ }
+ pclose(fich_tmp);
+ }
+ else
+ {
+ die(unlog, "Couldn't execute command : %s while checking port %d", command, port) ;
+ }
+// test
+// ok = 0 ;
+ if ( ok == 0 )
+ {
+ hidden_found++;
+ print_port(proto, port) ;
+ }
+}
+
+/*
+ * Check all TCP ports one by one.
+ */
+static void print_hidden_TCP_ports_1_by_1(enum Proto proto)
+{
+ int i ;
+ char tcpcommand[512] ;
+
+ hidden_found = 0 ;
+ for (i =1; i <= 65535; i++)
+ {
+ int socket_desc;
+ struct sockaddr_in address;
+
+ if ( -1 != (socket_desc=socket(AF_INET,SOCK_STREAM,0)))
+ {
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = INADDR_ANY;
+ address.sin_port = htons(i);
+ errno= 0 ;
+ if ( -1 != bind(socket_desc,(struct sockaddr *)&address,sizeof(address)))
+ {
+ listen(socket_desc,1);
+ if ( EADDRINUSE == errno ) // port is listened by another process
+ {
+ close(socket_desc);
+ if (1 == use_ss)
+ {
+ sprintf(tcpcommand, tcpcommand2, i) ;
+ }
+ else
+ {
+ strncpy(tcpcommand, tcpcommand1, 512) ;
+ }
+ checkoneport(i, tcpcommand, TCP);
+ }
+ else
+ {
+ close(socket_desc);
+ }
+ }
+ else
+ {
+ if (EADDRINUSE == errno) //port is in use by another process
+ {
+ close(socket_desc);
+ if (1 == use_ss)
+ {
+ sprintf(tcpcommand, tcpcommand2, i) ;
+ }
+ else
+ {
+ strncpy(tcpcommand, tcpcommand1, 512) ;
+ }
+ checkoneport(i, tcpcommand, TCP);
+ }
+ else
+ {
+ close(socket_desc);
+ warnln(verbose, unlog, "can't bind to socket while checking port %d", i) ;
+ }
+ }
+ }
+ else
+ {
+ warnln(verbose, unlog, "can't create socket while checking port %d/tcp", i) ;
+ }
+ }
+}
+
+/*
+ * Check all UDP ports one by one.
+ */
+static void print_hidden_UDP_ports_1_by_1(enum Proto proto)
+{
+ int u ;
+ char udpcommand[512] ;
+
+ hidden_found = 0 ;
+
+ for (u = 1; u <= 65535; u++)
+ {
+ int socket_desc;
+ struct sockaddr_in address;
+
+ if ( -1 != (socket_desc=socket(AF_INET,SOCK_DGRAM,0)))
+ {
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = INADDR_ANY;
+ address.sin_port = htons(u);
+ errno= 0 ;
+ if ( 0 != bind(socket_desc,(struct sockaddr *)&address,sizeof(address)))
+ {
+ if ( EADDRINUSE == errno ) //port is in use by another process
+ {
+ close(socket_desc);
+ if (1 == use_ss)
+ {
+ sprintf(udpcommand, udpcommand2, u) ;
+ }
+ else
+ {
+ strncpy(udpcommand, udpcommand1, 512) ;
+ }
+ checkoneport(u, udpcommand, UDP);
+ }
+ else // other error
+ {
+ close(socket_desc);
+ warnln(verbose, unlog, "can't bind to socket while checking port %d", u) ;
+ }
+ }
+ else // port is available
+ {
+ close(socket_desc);
+ }
+ }
+ else
+ {
+ warnln(verbose, unlog, "can't create socket while checking port %d/udp", u) ;
+ }
+ }
+}
+
+
+/*
+ * Print usage.
+ */
+void usage(char * command) {
+
+ printf("Usage: %s [options] \n\n", command);
+ printf("Options :\n");
+ printf(" -V Show version and exit\n");
+ printf(" -v verbose\n");
+ printf(" -h display this help\n");
+ printf(" -f show fuser output for hidden ports\n");
+ printf(" -l show lsof output for hidden ports\n");
+ printf(" -o log result into unhide-tcp.log file\n");
+ printf(" -s use very quick version for server with lot of opened ports\n");
+ printf(" -n use netstat instead of ss\n");
+}
+
+/*
+ * Parse command line arguments (exiting if requested by any option).
+ */
+void parse_args(int argc, char **argv)
+{
+ int c = 0;
+
+ static struct option long_options[] =
+ {
+ /* These options set a flag. */
+ {"verbose", no_argument, &verbose, 1},
+ {"brief", no_argument, &verbose, 0},
+ {"fuser", no_argument, &use_fuser, 1},
+ {"lsof", no_argument, &use_lsof, 1},
+ {"log", no_argument, &logtofile, 1},
+ {"netstat", no_argument, &use_ss, 0},
+ {"server", no_argument, &use_quick, 1},
+ /* These options don't set a flag.
+ We distinguish them by their indices. */
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'V'},
+ {0, 0, 0, 0}
+ };
+
+ for(;;) // until there's no more option
+ {
+ /* getopt_long stores the option index here. */
+ int option_index = 0;
+
+ c = getopt_long (argc, argv, "Vvhflosn",
+ long_options, &option_index);
+
+ /* Detect the end of the options. */
+ if (c == -1)
+ break;
+
+ switch(c)
+ {
+ case 0 : // flag long options
+ if (long_options[option_index].flag != 0) //if this option set a flag
+ {
+ break; // nothing to do
+ }
+ printf ("option %s", long_options[option_index].name);
+ if (optarg) // if there's an argument
+ {
+ printf (" with arg %s", optarg);
+ }
+ printf ("\n");
+ break ;
+ case 'f' :
+ use_fuser = 1 ;
+ break ;
+ case 'h' :
+ usage(argv[0]) ;
+ exit (0) ;
+ break ;
+ case 'l' :
+ use_lsof = 1 ;
+ break;
+ case 'o' :
+ logtofile = 1 ;
+ break ;
+ case 's' :
+ use_quick = 1 ;
+ break ;
+ case 'n' :
+ use_ss = 0 ;
+ break ;
+ case 'v' :
+ verbose++ ; ;
+ break ;
+ case 'V' :
+ exit (0) ;
+ break ;
+ case '?' : // invalid option
+ exit (2) ;
+ break ;
+ default : // something very nasty happened
+ exit(-1) ;
+ break ;
+ }
+
+ }
+
+ // generate options string for logging
+ strncpy(used_options, "Used options: ", 1000);
+ if (verbose)
+ strncat(used_options, "verbose ", 1000-1-strlen(used_options));
+ if (use_lsof)
+ strncat(used_options, "use_lsof ", 1000-1-strlen(used_options));
+ if (use_fuser)
+ strncat(used_options, "use_fuser ", 1000-1-strlen(used_options));
+ if (!use_ss)
+ strncat(used_options, "use_netscape ", 1000-1-strlen(used_options));
+ if (use_quick)
+ strncat(used_options, "use_quick ", 1000-1-strlen(used_options));
+ if (logtofile)
+ strncat(used_options, "logtofile ", 1000-1-strlen(used_options));
+}
+
+/*
+ * Look for TCP and UDP ports that are hidden to netstat.
+ *
+ * Returns 0 if none is found, 1 if there is some internal error, 4 if TCP
+ * hidden ports were found, 8 if UDP hidden ports were found or 12 (4 & 8) if
+ * both were found.
+ */
+int main(int argc, char **argv)
+{
+ int ret_code = 0;
+
+ printf(header) ;
+ if(getuid() != 0){
+ die(unlog, "You must be root to run %s !", argv[0]) ;
+ }
+
+ parse_args(argc, argv) ;
+
+ if (1 == logtofile)
+ {
+ unlog = init_log(logtofile, header, "unhide-tcp") ;
+ }
+ msgln(unlog, 0, used_options) ;
+
+ if (1 == use_ss)
+ {
+ strncpy(checker, "ss", 10);
+ }
+ else
+ {
+ strncpy(checker, "netstat", 10);
+ }
+
+ setpriority(PRIO_PROCESS,0,-20); /* reduce risk of race condition - may fail, dont care */
+
+ msgln(unlog, 0, "[*]Starting TCP checking") ;
+ if (1 == use_quick)
+ {
+ print_hidden_ports(TCP);
+ }
+ else
+ {
+ print_hidden_TCP_ports_1_by_1(TCP) ;
+ }
+ if (hidden_found)
+ {
+ ret_code += 4;
+ }
+
+ msgln(unlog, 0, "[*]Starting UDP checking") ;
+ if (1 == use_quick)
+ {
+ print_hidden_ports(UDP);
+ }
+ else
+ {
+ print_hidden_UDP_ports_1_by_1(UDP) ;
+ }
+ if (hidden_found)
+ {
+ ret_code += 8;
+ }
+
+ if (logtofile == 1)
+ {
+ close_log(unlog, "unhide-tcp") ;
+ }
+ return(ret_code);
+
+}
+
diff --git a/unhide-tcp.c b/unhide-tcp.c
new file mode 100644
index 0000000..3d3cf19
--- /dev/null
+++ b/unhide-tcp.c
@@ -0,0 +1,554 @@
+/*
+ http://www.unhide-forensics.info
+*/
+
+/*
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "unhide-output.h"
+#include "unhide-tcp.h"
+
+// header
+const char header[] =
+ "Unhide-tcp 20130526\n"
+ "Copyright © 2013 Yago Jesus & Patrick Gouin\n"
+ "License GPLv3+ : GNU GPL version 3 or later\n"
+ "http://www.unhide-forensics.info\n";
+
+// options
+int verbose = 0;
+int use_fuser = 0;
+int use_lsof = 0;
+#ifdef __linux__
+ int use_ss = 1; // on Linux use ss by default
+#else
+ int use_ss = 0; // else don't use ss by default
+#endif
+int use_quick = 0;
+
+char checker[10] = "ss" ;
+
+// Temporary string for output
+char scratch[1000];
+
+// Temporary string for output
+char used_options[1000] = "";
+
+// For logging to file
+int logtofile = 0;
+FILE *unlog;
+
+// Global hidden port counter, used only to set the exit code of the program
+int hidden_found;
+
+
+/* thx aramosf@unsec.net for the nice regexp! */
+
+// Default commands for Linux, needs iproute2
+char tcpcommand2[]= "ss -tan sport = :%d | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+char udpcommand2[]= "ss -uan sport = :%d | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+
+// fuser commands
+// for FreeBSD, use sockstat as fuser equivalent.
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ // FreeBSD
+ char fuserTCPcommand[]= "sockstat -46 -p %d -P tcp" ;
+ char fuserUDPcommand[]= "sockstat -46 -p %d -P udp" ;
+#else
+ char fuserTCPcommand[]= "fuser -v -n tcp %d 2>&1" ;
+ char fuserUDPcommand[]= "fuser -v -n udp %d 2>&1" ;
+#endif
+
+// lsof commands
+char lsofTCPcommand[]= "lsof +c 0 -iTCP:%d" ;
+char lsofUDPcommand[]= "lsof +c 0 -iUDP:%d" ;
+
+#ifdef __OpenBSD__
+ // OpenBSD
+ char tcpcommand1[]= "netstat -an -p tcp | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+ char udpcommand1[]= "netstat -an -p udp| sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ // FreeBSD
+ char tcpcommand1[]= "netstat -an -p tcp | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+ char udpcommand1[]= "netstat -an -p udp| sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+#elif (defined(sun) || defined(__sun)) && (defined(__SVR4) || defined(__svr4__))
+ // Solaris
+ char tcpcommand1[]= "netstat -an -P tcp | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+ char udpcommand1[]= "netstat -an -P udp| sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+#else
+ // Linux / default
+ char tcpcommand1[]= "netstat -tan | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+ char udpcommand1[]= "netstat -uan | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ;
+#endif
+
+
+
+/*
+ * Run a command to get more information about a port.
+ */
+static void print_info(const char *prog_name, const char *command_fmt, int port)
+{
+
+ char buffer[1000];
+ FILE* fp;
+
+ sprintf(buffer, command_fmt, port);
+ fp = popen(buffer, "r") ;
+
+ if (NULL == fp)
+ {
+ warnln(verbose, unlog, "Couldn't run command: %s", buffer) ;
+ return ;
+ }
+
+ msgln(unlog, 1, "%s reports :", prog_name) ;
+
+ while (NULL != fgets(buffer, 1000, fp))
+ {
+ msgln(unlog, 1, buffer) ;
+ }
+
+ pclose(fp);
+}
+
+/* Print a port, optionally querying info about it via lsof or fuser. */
+void print_port(enum Proto proto, int port)
+{
+ msgln(unlog, 0, "\nFound Hidden port that not appears in %s: %i", checker, port) ;
+ if (1 == use_fuser)
+ {
+ if (TCP == proto)
+ {
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ print_info("sockstat", fuserTCPcommand, port);
+#else
+ print_info("fuser", fuserTCPcommand, port);
+#endif
+ }
+ else
+ {
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ print_info("sockstat", fuserUDPcommand, port);
+#else
+ print_info("fuser", fuserUDPcommand, port);
+#endif
+ }
+ }
+ if (1 == use_lsof)
+ {
+ if (TCP == proto)
+ {
+ print_info("lsof", lsofTCPcommand, port);
+ }
+ else
+ {
+ print_info("lsof", lsofUDPcommand, port);
+ }
+ }
+}
+
+
+/*
+ * Check if port is seen by netstat.
+ *
+ * If not, report it and optionnally run lsof and/or fuser
+ * to show more info.
+ */
+int checkoneport(int port, char command[], enum Proto proto)
+{
+ int ok = 0;
+ char ports[30];
+ char compare[100];
+
+ FILE *fich_tmp ;
+
+ if (NULL != (fich_tmp=popen (command, "r")))
+ {
+ sprintf(compare,"%i\n",port);
+ while ((NULL != fgets(ports, 30, fich_tmp)) && ok == 0) {
+ if (strcmp(ports, compare) == 0) {ok = 1;}
+ }
+ pclose(fich_tmp);
+ }
+ else
+ {
+ die(unlog, "Couldn't execute command : %s while checking port %d", command, port) ;
+ }
+ return(ok) ;
+}
+
+/*
+ * Check all TCP ports one by one.
+ */
+static void print_hidden_TCP_ports_1_by_1(enum Proto proto)
+{
+ int i ;
+ char tcpcommand[512] ;
+
+ hidden_found = 0 ;
+ for (i =1; i <= 65535; i++)
+ {
+ int socket_desc;
+ struct sockaddr_in address;
+
+ if ( -1 != (socket_desc=socket(AF_INET,SOCK_STREAM,0)))
+ {
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = INADDR_ANY;
+ address.sin_port = htons(i);
+ errno= 0 ;
+ if ( -1 != bind(socket_desc,(struct sockaddr *)&address,sizeof(address)))
+ {
+ listen(socket_desc,1);
+ if ( EADDRINUSE == errno ) // port is listened by another process
+ {
+ if (1 == use_ss)
+ {
+ sprintf(tcpcommand, tcpcommand2, i) ;
+ }
+ else
+ {
+ strncpy(tcpcommand, tcpcommand1, 512) ;
+ }
+ if (0 == checkoneport(i, tcpcommand, TCP))
+ {
+ // test again
+ listen(socket_desc,1);
+ if ( EADDRINUSE == errno ) // port is still listened by another process
+ {
+ hidden_found++;
+ print_port(proto, i) ;
+ }
+ }
+ close(socket_desc);
+ }
+ else
+ {
+ close(socket_desc);
+ }
+ }
+ else
+ {
+ if (EADDRINUSE == errno) //port is in use by another process
+ {
+ if (1 == use_ss)
+ {
+ sprintf(tcpcommand, tcpcommand2, i) ;
+ }
+ else
+ {
+ strncpy(tcpcommand, tcpcommand1, 512) ;
+ }
+ if (0 == checkoneport(i, tcpcommand, TCP))
+ {
+ // test again
+ if ( -1 == bind(socket_desc,(struct sockaddr *)&address,sizeof(address)))
+ {
+ if ( EADDRINUSE == errno ) // port is still used by another process
+ {
+ hidden_found++;
+ print_port(proto, i) ;
+ }
+ else
+ {
+ warnln(verbose, unlog, "can't bind to socket while checking port %d", i) ;
+ }
+ close(socket_desc);
+ }
+ }
+ else
+ {
+ close(socket_desc);
+ }
+ }
+ }
+ }
+ else
+ {
+ warnln(verbose, unlog, "can't create socket while checking port %d/tcp", i) ;
+ }
+ }
+}
+
+/*
+ * Check all UDP ports one by one.
+ */
+static void print_hidden_UDP_ports_1_by_1(enum Proto proto)
+{
+ int u ;
+ char udpcommand[512] ;
+
+ hidden_found = 0 ;
+
+ for (u = 1; u <= 65535; u++)
+ {
+ int socket_desc;
+ struct sockaddr_in address;
+
+ if ( -1 != (socket_desc=socket(AF_INET,SOCK_DGRAM,0)))
+ {
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = INADDR_ANY;
+ address.sin_port = htons(u);
+ errno= 0 ;
+ if ( 0 != bind(socket_desc,(struct sockaddr *)&address,sizeof(address)))
+ {
+ if ( EADDRINUSE == errno ) //port is in use by another process
+ {
+ if (1 == use_ss)
+ {
+ sprintf(udpcommand, udpcommand2, u) ;
+ }
+ else
+ {
+ strncpy(udpcommand, udpcommand1, 512) ;
+ }
+ ;
+ if (0 == checkoneport(u, udpcommand, UDP))
+ {
+ // test again
+ if ( 0 != bind(socket_desc,(struct sockaddr *)&address,sizeof(address))) // port is still in use by another process
+ {
+ if ( EADDRINUSE == errno ) //port is in use by another process
+ {
+ hidden_found++;
+ print_port(proto, u) ;
+ }
+ }
+ }
+ close(socket_desc);
+ }
+ else // other error
+ {
+ close(socket_desc);
+ warnln(verbose, unlog, "can't bind to socket while checking port %d", u) ;
+ }
+ }
+ else // port is available
+ {
+ close(socket_desc);
+ }
+ }
+ else
+ {
+ warnln(verbose, unlog, "can't create socket while checking port %d/udp", u) ;
+ }
+ }
+}
+
+
+/*
+ * Print usage.
+ */
+void usage(char * command) {
+
+ printf("Usage: %s [options] \n\n", command);
+ printf("Options :\n");
+ printf(" -V Show version and exit\n");
+ printf(" -v verbose\n");
+ printf(" -h display this help\n");
+ printf(" -f show fuser output for hidden ports\n");
+ printf(" -l show lsof output for hidden ports\n");
+ printf(" -o log result into unhide-tcp.log file\n");
+ printf(" -s use very quick version for server with lot of opened ports\n");
+ printf(" -n use netstat instead of ss\n");
+}
+
+/*
+ * Parse command line arguments (exiting if requested by any option).
+ */
+void parse_args(int argc, char **argv)
+{
+ int c = 0;
+
+ static struct option long_options[] =
+ {
+ /* These options set a flag. */
+ {"verbose", no_argument, &verbose, 1},
+ {"brief", no_argument, &verbose, 0},
+ {"fuser", no_argument, &use_fuser, 1},
+ {"lsof", no_argument, &use_lsof, 1},
+ {"log", no_argument, &logtofile, 1},
+ {"netstat", no_argument, &use_ss, 0},
+ {"server", no_argument, &use_quick, 1},
+ /* These options don't set a flag.
+ We distinguish them by their indices. */
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'V'},
+ {0, 0, 0, 0}
+ };
+
+ for(;;) // until there's no more option
+ {
+ /* getopt_long stores the option index here. */
+ int option_index = 0;
+
+ c = getopt_long (argc, argv, "Vvhflosn",
+ long_options, &option_index);
+
+ /* Detect the end of the options. */
+ if (c == -1)
+ break;
+
+ switch(c)
+ {
+ case 0 : // flag long options
+ if (long_options[option_index].flag != 0) //if this option set a flag
+ {
+ break; // nothing to do
+ }
+ printf ("option %s", long_options[option_index].name);
+ if (optarg) // if there's an argument
+ {
+ printf (" with arg %s", optarg);
+ }
+ printf ("\n");
+ break ;
+ case 'f' :
+ use_fuser = 1 ;
+ break ;
+ case 'h' :
+ usage(argv[0]) ;
+ exit (0) ;
+ break ;
+ case 'l' :
+ use_lsof = 1 ;
+ break;
+ case 'o' :
+ logtofile = 1 ;
+ break ;
+ case 's' :
+ use_quick = 1 ;
+ break ;
+ case 'n' :
+ use_ss = 0 ;
+ break ;
+ case 'v' :
+ verbose++ ; ;
+ break ;
+ case 'V' :
+ exit (0) ;
+ break ;
+ case '?' : // invalid option
+ exit (2) ;
+ break ;
+ default : // something very nasty happened
+ exit(-1) ;
+ break ;
+ }
+
+ }
+
+ // generate options string for logging
+ strncpy(used_options, "Used options: ", 1000);
+ if (verbose)
+ strncat(used_options, "verbose ", 1000-1-strlen(used_options));
+ if (use_lsof)
+ strncat(used_options, "use_lsof ", 1000-1-strlen(used_options));
+ if (use_fuser)
+ strncat(used_options, "use_fuser ", 1000-1-strlen(used_options));
+ if (!use_ss)
+ strncat(used_options, "use_netscape ", 1000-1-strlen(used_options));
+ if (use_quick)
+ strncat(used_options, "use_quick ", 1000-1-strlen(used_options));
+ if (logtofile)
+ strncat(used_options, "logtofile ", 1000-1-strlen(used_options));
+}
+
+/*
+ * Look for TCP and UDP ports that are hidden to netstat.
+ *
+ * Returns 0 if none is found, 1 if there is some internal error, 4 if TCP
+ * hidden ports were found, 8 if UDP hidden ports were found or 12 (4 & 8) if
+ * both were found.
+ */
+int main(int argc, char **argv)
+{
+ int ret_code = 0;
+
+ printf(header) ;
+
+ if(getuid() != 0){
+ die(unlog, "You must be root to run %s !", argv[0]) ;
+ }
+
+ parse_args(argc, argv) ;
+
+ if (1 == logtofile)
+ {
+ unlog = init_log(logtofile, header, "unhide-tcp") ;
+ }
+ msgln(unlog, 0, used_options) ;
+
+ if (1 == use_ss)
+ {
+ strncpy(checker, "ss", 10);
+ }
+ else
+ {
+ strncpy(checker, "netstat", 10);
+ }
+
+ setpriority(PRIO_PROCESS,0,-20); /* reduce risk of race condition - may fail, dont care */
+
+ msgln(unlog, 0, "[*]Starting TCP checking") ;
+ if (1 == use_quick)
+ {
+ print_hidden_ports(TCP);
+ }
+ else
+ {
+ print_hidden_TCP_ports_1_by_1(TCP) ;
+ }
+ if (hidden_found)
+ {
+ ret_code += 4;
+ }
+
+ msgln(unlog, 0, "[*]Starting UDP checking") ;
+ if (1 == use_quick)
+ {
+ print_hidden_ports(UDP);
+ }
+ else
+ {
+ print_hidden_UDP_ports_1_by_1(UDP) ;
+ }
+ if (hidden_found)
+ {
+ ret_code += 8;
+ }
+
+ if (logtofile == 1)
+ {
+ close_log(unlog, "unhide-tcp") ;
+ }
+ return(ret_code);
+
+}
+
diff --git a/unhide-tcp.h b/unhide-tcp.h
new file mode 100644
index 0000000..250a1e3
--- /dev/null
+++ b/unhide-tcp.h
@@ -0,0 +1,56 @@
+/*
+ http://www.unhide-forensics.info
+*/
+
+/*
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+
+/*
+ * Globals
+ */
+
+enum Proto
+{
+ TCP = 0,
+ UDP = 1
+};
+
+
+/*
+ * unhide-tcp
+ */
+// options
+extern int verbose ;
+extern int use_fuser ;
+extern int use_lsof ;
+extern int logtofile ;
+extern FILE *unlog ;
+extern int hidden_found;
+extern char tcpcommand1[] ;
+extern char udpcommand1[] ;
+
+/* Print a port, optionally querying info about it via lsof or fuser. */
+extern void print_port(enum Proto proto, int port);
+
+
+
+/*
+ * unhide-tcp-fast
+ */
+/*
+ * Print ports not visible to netstat but that are being used.
+ */
+void print_hidden_ports(enum Proto proto);
diff --git a/unhide_rb.c b/unhide_rb.c
new file mode 100644
index 0000000..76f846f
--- /dev/null
+++ b/unhide_rb.c
@@ -0,0 +1,590 @@
+/*
+ http://sourceforge.net/projects/unhide/
+*/
+
+/*
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+// Needed for unistd.h to declare getpgid() and others
+#define _XOPEN_SOURCE 500
+
+// Needed for sched.h to declare sched_getaffinity()
+#define _GNU_SOURCE
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define UNHIDE_RB "ps axhHo lwp,cmd"
+
+// boolean value
+#define FALSE 0
+#define TRUE 1
+#define UNKNOWN -1
+
+// sysctl kernel.pid_max
+int maxpid = 32768;
+
+// Temporary string for output
+char scratch[1000] ;
+char cmdcont[1000] ;
+
+unsigned int proc_parent_pids[65536] ;
+
+char *proc_tasks[65536];
+char *ps_pids[65536];
+char *messages_pids[65536];
+char message[1000] ;
+char description[1000] ;
+int ps_count = 0 ;
+
+const char *pid_detectors[] =
+{
+ "ps", "/proc ", "/proc_tasks ", "/proc_parent", "getsid()", "getpgid()",
+ "getpriority()", "sched_getparam()",
+ "sched_getaffinity()", "sched_getscheduler()", "sched_rr_get_interval()"
+} ;
+
+enum n_detector
+{
+ N_PS,
+ N_PROC,
+ N_PROC_TASK,
+ N_PROC_PARENT,
+ N_GETSID,
+ N_GET_PGID,
+ N_GETPRIORITY,
+ N_SCHED_GETPARAM,
+ N_SCHED_GETAFFINITY,
+ N_SCHED_GETSCHEDULER,
+ N_SCHED_RR_GET_INTERVAL
+} ;
+
+void setup(int phase)
+{
+ // setup part of unhide.rb
+ // -----------------------
+
+ DIR *procdir, *taskdir;
+ struct dirent *dirtask, *dirproc;
+ char mypath[512] = "/proc/" ;
+ sprintf(mypath, "/proc/") ;
+
+ procdir = opendir(mypath);
+ while ((dirproc = readdir(procdir)))
+ {
+ // As of Linux kernel 2.6 :
+ // readdir directly in /proc only see process, not thread
+ // because procfs voluntary hides threads to readdir
+ char *directory ;
+
+ if (dirproc->d_type != DT_DIR)
+ {
+ // not a directory or not a process directory
+ continue ;
+ }
+ directory = dirproc->d_name;
+ if(!isdigit(*directory))
+ {
+ // not a process directory of /proc
+ continue;
+ }
+ else
+ {
+ FILE* fich_tmp ;
+ char line[128] ;
+ int tmp_pid ;
+// proc_parent_pids
+ sprintf(&mypath[6],"%s/status",directory);
+// printf("%s\n",mypath);
+ if(NULL != (fich_tmp=fopen(mypath, "r")))
+ {
+ while (!feof (fich_tmp))
+ {
+
+ if (NULL != fgets (line, 128, fich_tmp))
+ {
+ line[127] = 0;
+ if (0 == strncmp (line, "ppid:", 5))
+ {
+ tmp_pid = strtol(line+5, NULL, 10);
+ proc_parent_pids[tmp_pid] = tmp_pid ;
+ }
+ }
+ }
+ fclose(fich_tmp);
+ }
+// proc_tasks
+ sprintf(&mypath[6],"%s/task/", directory) ;
+ taskdir = opendir(mypath);
+ while ((dirtask = readdir(taskdir)))
+ {
+ char *directory ;
+
+ directory = dirtask->d_name;
+ if(!isdigit(*directory))
+ {
+ // not a process directory of /proc
+ continue;
+ }
+ else
+ {
+// FILE* fich_tmp ;
+// char line[128] ;
+// int tmp_pid ;
+ size_t length ;
+ char myexe[512] ;
+
+ sprintf(myexe,"%s%s/exe",mypath,directory);
+// printf("%s\n",myexe);
+
+ length = readlink(myexe, cmdcont, 1000) ;
+ if (-1 != length)
+ {
+ if (2 == phase)
+ {
+ cmdcont[length] = 0; // terminate the string
+ proc_tasks[strtol(directory, NULL, 10)] = malloc(length+1) ;
+ strcpy(proc_tasks[strtol(directory, NULL, 10)],cmdcont) ;
+// printf("proc_tasks[%d] = %s\n",strtol(directory, NULL, 10), proc_tasks[strtol(directory, NULL, 10)]) ;
+ }
+ else
+ {
+ proc_tasks[strtol(directory, NULL, 10)] = (char *)1L ;
+ }
+ }
+ }
+ }
+ closedir(taskdir) ;
+ }
+ mypath[6] = 0 ;
+ }
+ closedir(procdir);
+// ps_pids
+
+ FILE *fich_tmp ;
+ char myline[1000] ;
+ int mypid ;
+// char ch_pid[30] ;
+
+ fich_tmp=popen (UNHIDE_RB, "r") ;
+
+ while (!feof(fich_tmp))
+ {
+ if (NULL != fgets(myline, 1000, fich_tmp))
+ {
+ myline[999] = 0;
+ if (myline[0] != '\n')
+ {
+ mypid = strtol(myline, NULL,10) ;
+// printf("line+5 = --%s--\n", myline+5); // DEBUG
+ if (NULL == strstr(myline+5,UNHIDE_RB))
+ {
+ ps_count++ ;
+ if (2 == phase)
+ {
+ char *my_end ;
+/*
+ int my_len ;
+ my_len = strlen(myline);
+ my_end=myline ;
+ if (1 == mypid)
+ while (my_end < myline+my_len)
+ {
+ printf(" %2x",*my_end);
+ my_end++;
+ }
+*/
+ my_end = myline + strlen(myline) -1 ;
+// printf("line = --%s--\n", myline); // DEBUG
+ while (' ' == *my_end || '\n' == *my_end || '\r' == *my_end || '\t' == *my_end)
+ {
+ *my_end = 0 ;
+ my_end-- ;
+ }
+// printf("line = --%s--\n", myline); // DEBUG
+ ps_pids[mypid] = malloc(strlen(myline+5)+1) ;
+// printf("ps_pids[%d] = %p for %d bytes\n", mypid,ps_pids[mypid],strlen(myline+5)+1);
+ strcpy(ps_pids[mypid],myline+5) ;
+// printf("pid = %d\t", mypid); // DEBUG
+// printf("cmd = --%s--\n",ps_pids[mypid]);
+ }
+ else
+ {
+ ps_pids[mypid] = (char *)1L ;
+ }
+ }
+ }
+ }
+ }
+
+ if (fich_tmp != NULL)
+ pclose(fich_tmp);
+// end of unhide_rb setup
+
+}
+
+int get_suspicious_pids(int pid_num)
+{
+ int pid_min, pid_max, my_pid ;
+ int pid_exists[N_SCHED_RR_GET_INTERVAL+1];
+ char mypath[50] ;
+ struct stat buffer;
+// FILE *cmdfile ;
+ char proc_exe[512] ;
+ int statuscmd, length;
+ struct sched_param param;
+ cpu_set_t mask;
+ struct timespec tp;
+ int found_p = FALSE ;
+
+
+ if (pid_num == -1)
+ {
+ char path[]= "/proc/sys/kernel/pid_max";
+ pid_t tmppid = 0;
+ FILE* fd= fopen(path,"r");
+ if(!fd)
+ {
+ snprintf(scratch, 1000, "[*] Error: cannot get current maximum PID: %s\n", strerror(errno));
+ fputs(scratch, stdout);
+ }
+ else if((fscanf(fd, "%d", &tmppid) != 1) || tmppid < 1)
+ {
+ snprintf(scratch, 1000, "[*] cannot get current maximum PID: Error parsing %s format\n", path);
+ fputs(scratch, stdout);
+ } else
+ {
+ maxpid = tmppid;
+ }
+ fclose(fd) ;
+
+ pid_min = 1 ;
+ pid_max = maxpid ;
+ }
+ else
+ pid_min = pid_max = pid_num ;
+
+ for (my_pid = pid_min ; my_pid <= pid_max; my_pid++)
+ {
+// printf("pid_min = %d, pid_max = %d, my_pid = %d\n", pid_min, pid_max, my_pid) ;
+ // ps
+ if (ps_pids[my_pid] != NULL)
+ pid_exists[N_PS] = TRUE ;
+ else
+ pid_exists[N_PS] = FALSE ;
+ // proc
+ sprintf(mypath,"/proc/%d",my_pid);
+ statuscmd = stat(mypath, &buffer) ;
+ if ((statuscmd == 0) && S_ISDIR(buffer.st_mode))
+ {
+ pid_exists[N_PROC] = TRUE ;
+ strcat(mypath,"/exe") ;
+ length = readlink(mypath, cmdcont, 1000) ;
+ if (-1 != length)
+ {
+ cmdcont[length] = 0; // terminate the string
+// printf("cmdcont(proc_exe) = %s\n", cmdcont) ; //DEBUG
+ strcpy(proc_exe,cmdcont) ;
+ }
+ else
+ {
+ strcpy(proc_exe,"unknown exe") ;
+ }
+ }
+ else
+ {
+ pid_exists[N_PROC] = FALSE ;
+ }
+ // proc/#/task
+ if (proc_tasks[my_pid] != NULL)
+ pid_exists[N_PROC_TASK] = TRUE ;
+ else
+ pid_exists[N_PROC_TASK] = FALSE ;
+
+ // proc_parent
+ if (proc_parent_pids[my_pid] != 0)
+ pid_exists[N_PROC_PARENT] = TRUE ;
+ else
+ pid_exists[N_PROC_PARENT] = UNKNOWN ;
+ // getsid()
+ if (-1 != getsid(my_pid))
+ {
+ pid_exists[N_GETSID] = TRUE ;
+ }
+ else
+ {
+ pid_exists[N_GETSID] = FALSE ;
+ }
+ // getpgid()
+ if (-1 != getpgid(my_pid))
+ {
+ pid_exists[N_GET_PGID] = TRUE ;
+ }
+ else
+ {
+ pid_exists[N_GET_PGID] = FALSE ;
+ }
+ // getpriority()
+ if (-1 != getpriority(PRIO_PROCESS, my_pid))
+ {
+ pid_exists[N_GETPRIORITY] = TRUE ;
+ }
+ else
+ {
+ pid_exists[N_GETPRIORITY] = FALSE ;
+ }
+ // sched_getparam()
+ if (-1 != sched_getparam(my_pid, ¶m))
+ {
+ pid_exists[N_SCHED_GETPARAM] = TRUE ;
+ }
+ else
+ {
+ pid_exists[N_SCHED_GETPARAM] = FALSE ;
+ }
+ // sched_getaffinity()
+ if (-1 != sched_getaffinity(my_pid, sizeof(cpu_set_t), &mask))
+ {
+ pid_exists[N_SCHED_GETAFFINITY] = TRUE ;
+ }
+ else
+ {
+ pid_exists[N_SCHED_GETAFFINITY] = FALSE ;
+ }
+ // sched_getscheduler()
+ if (-1 != sched_getscheduler(my_pid))
+ {
+ pid_exists[N_SCHED_GETSCHEDULER] = TRUE ;
+ }
+ else
+ {
+ pid_exists[N_SCHED_GETSCHEDULER] = FALSE ;
+ }
+ // sched_rr_get_interval()
+ if (-1 != sched_rr_get_interval(my_pid, &tp))
+ {
+ pid_exists[N_SCHED_RR_GET_INTERVAL] = TRUE ;
+ }
+ else
+ {
+ pid_exists[N_SCHED_RR_GET_INTERVAL] = FALSE ;
+ }
+
+ int suspicious = FALSE ;
+ int existence_consensus = UNKNOWN ;
+ int index ;
+ for (index = 0; index <= N_SCHED_RR_GET_INTERVAL; index++)
+ {
+ if (UNKNOWN == existence_consensus)
+ {
+ existence_consensus = pid_exists[index] ;
+ }
+ if (UNKNOWN == pid_exists[index])
+ continue ;
+ if (FALSE == existence_consensus)
+ {
+ if (TRUE == pid_exists[index])
+ suspicious = TRUE ;
+ break ;
+ }
+ else
+ {
+ if (FALSE == pid_exists[index])
+ suspicious = TRUE ;
+ break ;
+ }
+
+ }
+
+// if (1 == my_pid) suspicious = TRUE ; //DEBUG
+ if(TRUE == suspicious)
+ {
+ found_p = TRUE ;
+ sprintf(message, "Suspicious PID %5d:", my_pid) ;
+ int index ;
+ for (index = 0; index <= N_SCHED_RR_GET_INTERVAL; index++)
+ {
+ if (UNKNOWN == pid_exists[index])
+ continue;
+ description[0] = 0 ;
+ if (-1 != pid_num)
+ {
+ if(N_PS == index)
+ {
+ if (NULL != ps_pids[my_pid])
+ strcpy(description, ps_pids[my_pid]) ;
+ }
+ else if (N_PROC_TASK == index)
+ {
+ if (NULL != proc_tasks[my_pid])
+ {
+// printf("proc_tasks[%d] = %s", my_pid, proc_tasks[my_pid]) ; // DEBBUG
+ strcpy(description, proc_tasks[my_pid]) ;
+ }
+ }
+ else if (N_PROC == index)
+ {
+// printf("proc_exe = %s\n", proc_exe) ; // DEBBUG
+ strcpy(description, proc_exe) ;
+ }
+ }
+ sprintf(scratch, "\n %s %s%s", (pid_exists[index] ? "Seen by" : "Not seen by"), pid_detectors[index], description) ;
+ strcat(message, scratch) ;
+ }
+// puts(message) ; //DEBUG
+ if (-1 == pid_num)
+ {
+ messages_pids[my_pid] = malloc(strlen(message)+1) ;
+ strcpy(messages_pids[my_pid], message) ;
+ }
+ }
+ }
+ return(found_p);
+}
+
+
+int main (int argc, char *argv[])
+{
+/*
+ time_t scantime1, scantime2;
+ char cad[80];
+ struct tm *tmPtr;
+ double duree ;
+*/
+ int i ;
+ int found_something = FALSE ;
+ int phase1_ko = FALSE ;
+
+ strncpy(scratch,"Unhide_rb 20130526\n", 1000) ;
+
+ strncat(scratch, "Copyright © 2013 Yago Jesus & Patrick Gouin\n", 1000);
+ strncat(scratch, "License GPLv3+ : GNU GPL version 3 or later\n", 1000);
+ strncat(scratch, "http://www.unhide-forensics.info\n\n", 1000);
+ strncat(scratch, "NOTE : This version of unhide_rb is for systems using Linux >= 2.6 \n\n", 1000);
+ fputs(scratch, stdout);
+
+// printf(header) ;
+ if(getuid() != 0){
+ printf("You must be root to run %s !\n", argv[0]) ;
+ }
+
+
+/*
+ scantime1 = time(NULL);
+ tmPtr = localtime(&scantime1);
+ strftime( cad, 80, "%H:%M.%S, %F", tmPtr );
+ printf("Unhide_rb scan starting at: %s\n", cad );
+*/
+ puts ("Scanning for hidden processes...") ;
+
+// initializing memory pointers
+ for (i = 0 ; i < maxpid; i++)
+ {
+ ps_pids[i] = NULL ;
+ proc_tasks[i] = NULL ;
+ messages_pids[i] = NULL ;
+ }
+
+ setup(1);
+ struct sysinfo info;
+ sysinfo(&info);
+ if (ps_count != info.procs)
+ {
+ puts("ps and sysinfo() process count mismatch:\n") ;
+ printf(" ps: %d processes\n", ps_count) ;
+ printf(" sysinfo(): %d processes\n", info.procs) ;
+ }
+
+ phase1_ko = get_suspicious_pids(-1) ;
+// re-initializing memory pointers (were used as boolean in setup() phase 1)
+ for (i = 0 ; i < maxpid; i++)
+ {
+ ps_pids[i] = NULL ;
+ proc_tasks[i] = NULL ;
+ }
+ if (TRUE == phase1_ko)
+ {
+ setup(2);
+ for (i=1; i