diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..08b4f8f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+*/.DS_Store
+.vscode/*
+bin/*
+navicat-patcher/Elf64Crafter.hpp
+navicat-patcher/Elf64Crafter.cpp
+navicat-patcher/Elf64InterpreterAmd64.hpp
+navicat-patcher/Elf64InterpreterAmd64.cpp
+RegPrivateKey.pem
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..9d2bb69
--- /dev/null
+++ b/LICENSE
@@ -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.
+
+ {one line to give the program's name and a brief idea of what it does.}
+ Copyright (C) 2017 {name of author}
+
+ 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:
+
+ navicat-keygen Copyright (C) 2017 Double Helix
+ 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
+.
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3b5f791
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,99 @@
+CC = g++
+UNAME = $(shell uname)
+
+ifeq ($(UNAME),Darwin)
+ OPENSSL_INCLUDE_PATH = /usr/local/opt/openssl@1.1/include
+ OPENSSL_LIB_PATH = /usr/local/opt/openssl@1.1/lib
+else
+ OPENSSL_INCLUDE_PATH =
+ OPENSSL_LIB_PATH =
+endif
+CAPSTONE_INCLUDE_PATH =
+CAPSTONE_LIB_PATH =
+KEYSTONE_INCLUDE_PATH =
+KEYSTONE_LIB_PATH =
+RAPIDJSON_INCLUDE_PATH =
+
+OUTPUT_DIR = ./bin/
+COMMON_DIR = ./common/
+PATCHER_DIR = ./navicat-patcher/
+KEYGEN_DIR = ./navicat-keygen/
+
+COMMON_HEADER = \
+$(COMMON_DIR)Exception.hpp \
+$(COMMON_DIR)ExceptionGeneric.hpp \
+$(COMMON_DIR)ExceptionOpenssl.hpp \
+$(COMMON_DIR)ExceptionSystem.hpp \
+$(COMMON_DIR)ResourceTraitsOpenssl.hpp \
+$(COMMON_DIR)ResourceWrapper.hpp \
+$(COMMON_DIR)RSACipher.hpp
+
+PATCHER_HEADER = \
+$(PATCHER_DIR)CapstoneDisassembler.hpp \
+$(PATCHER_DIR)KeystoneAssembler.hpp \
+$(PATCHER_DIR)Elf64Interpreter.hpp \
+$(PATCHER_DIR)ExceptionCapstone.hpp \
+$(PATCHER_DIR)ExceptionKeystone.hpp \
+$(PATCHER_DIR)MemoryAccess.hpp \
+$(PATCHER_DIR)Misc.hpp \
+$(PATCHER_DIR)PatchSolutions.hpp \
+$(PATCHER_DIR)ResourceTraitsCapstone.hpp \
+$(PATCHER_DIR)ResourceTraitsKeystone.hpp \
+$(PATCHER_DIR)ResourceTraitsUnix.hpp
+
+PATCHER_SOURCE = \
+$(PATCHER_DIR)CapstoneDisassembler.cpp \
+$(PATCHER_DIR)KeystoneAssembler.cpp \
+$(PATCHER_DIR)Elf64Interpreter.cpp \
+$(PATCHER_DIR)Misc.cpp \
+$(PATCHER_DIR)PatchSolution.cpp \
+$(PATCHER_DIR)PatchSolution0.cpp \
+$(PATCHER_DIR)main.cpp
+
+PATCHER_BINARY = $(OUTPUT_DIR)navicat-patcher
+
+KEYGEN_HEADER = \
+$(KEYGEN_DIR)Base32.hpp \
+$(KEYGEN_DIR)Base64.hpp \
+$(KEYGEN_DIR)SerialNumberGenerator.hpp
+
+KEYGEN_SOURCE = \
+$(KEYGEN_DIR)CollectInformation.cpp \
+$(KEYGEN_DIR)GenerateLicense.cpp \
+$(KEYGEN_DIR)main.cpp \
+$(KEYGEN_DIR)SerialNumberGenerator.cpp
+
+KEYGEN_BINARY = $(OUTPUT_DIR)navicat-keygen
+
+patcher: $(PATCHER_HEADER) $(PATCHER_SOURCE)
+ @if [ ! -d $(OUTPUT_DIR) ]; then mkdir -p $(OUTPUT_DIR); fi
+ $(CC) -std=c++17 -O2 \
+-I$(COMMON_DIR) \
+$(if $(OPENSSL_INCLUDE_PATH),-I$(OPENSSL_INCLUDE_PATH),) $(if $(OPENSSL_LIB_PATH),-L$(OPENSSL_LIB_PATH),) \
+$(if $(CAPSTONE_INCLUDE_PATH),-I$(CAPSTONE_INCLUDE_PATH),) $(if $(CAPSTONE_LIB_PATH),-L$(CAPSTONE_LIB_PATH),) \
+$(if $(KEYSTONE_INCLUDE_PATH),-I$(KEYSTONE_INCLUDE_PATH),) $(if $(KEYSTONE_LIB_PATH),-L$(KEYSTONE_LIB_PATH),) \
+$(PATCHER_SOURCE) -o $(PATCHER_BINARY) -lcrypto -lcapstone -lkeystone
+ @echo
+
+keygen: $(KEYGEM_HEADER) $(KEYGEN_SOURCE)
+ @if [ ! -d $(OUTPUT_DIR) ]; then mkdir -p $(OUTPUT_DIR); fi
+ $(CC) -std=c++17 -O2 \
+-I$(COMMON_DIR) \
+$(if $(OPENSSL_INCLUDE_PATH),-I$(OPENSSL_INCLUDE_PATH),) $(if $(OPENSSL_LIB_PATH),-L$(OPENSSL_LIB_PATH),) \
+$(if $(RAPIDJSON_INCLUDE_PATH),-I$(RAPIDJSON_INCLUDE_PATH),) \
+$(KEYGEN_SOURCE) -o $(KEYGEN_BINARY) -lcrypto
+
+all: patcher keygen
+ @echo 'Done.'
+
+.PHONY: all
+
+clean:
+ifeq ($(wildcard $(PATCHER_BINARY)), $(PATCHER_BINARY))
+ rm $(PATCHER_BINARY)
+endif
+
+ifeq ($(wildcard $(KEYGEN_BINARY)), $(KEYGEN_BINARY))
+ rm $(KEYGEN_BINARY)
+endif
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b5695ab
--- /dev/null
+++ b/README.md
@@ -0,0 +1,276 @@
+# Navicat Keygen
+
+[中文版README](README.zh-CN.md)
+
+## 1. How does it work?
+
+see [here](doc/how-does-it-work.md).
+
+## 2. How to build
+
+see [here](doc/how-to-build.md).
+
+## 3. How to Use
+
+1. Download navicat from official website.
+
+ And you will get a AppImage file. For example, `navicat15-premium-en.AppImage`.
+
+ I assume that the AppImage file is in `~/Desktop` folder.
+
+2. Extract all files in the AppImage to a directory. For example:
+
+ ```console
+ $ mkdir ~/Desktop/navicat15-premium-en
+ $ sudo mount -o loop ~/Desktop/navicat15-premium-en.AppImage ~/Desktop/navicat15-premium-en
+ $ cp -r ~/Desktop/navicat15-premium-en ~/Desktop/navicat15-premium-en-patched
+ $ sudo umount ~/Desktop/navicat15-premium-en
+ $ rm -rf ~/Desktop/navicat15-premium-en
+ ```
+
+3. [Build keygen and patcher.](doc/how-to-build.md)
+
+4. Use `navicat-patcher` to replace the official public key.
+
+ ```
+ Usage:
+ navicat-patcher [--dry-run] [RSA-2048 Private Key File]
+
+ [--dry-run] Run patcher without applying any patches.
+ This parameter is optional.
+
+ Path to a directory where Navicat locates
+ This parameter must be specified.
+
+ [RSA-2048 Private Key File] Path to a PEM-format RSA-2048 private key file.
+ This parameter is optional.
+ ```
+
+ __Example:__
+
+ ```console
+ $ ./bin/navicat-patcher ~/Desktop/navicat15-premium-en-patched
+ ```
+
+ It has been tested on __Navicat Premium 15.0.3 English For Linux__ version.
+
+ The following is an example of output:
+
+ ```console
+ **********************************************************
+ * Navicat Patcher (Linux) by @DoubleLabyrinth *
+ * Version: 1.0 *
+ **********************************************************
+
+ Press ENTER to continue or Ctrl + C to abort.
+
+ [+] Try to open libcc.so ... Ok!
+
+ [+] PatchSolution0 ...... Ready to apply
+ RefSegment = 1
+ MachineCodeRva = 0x0000000001413e10
+ PatchMarkOffset = +0x00000000029ecf40
+
+ [*] Generating new RSA private key, it may take a long time...
+ [*] Your RSA private key:
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIEowIBAAKCAQEArRsg1+6JZxZNMhGyuM8d+Ue/ky9LSv/XyKh+wppQMS5wx7QE
+ XFcdDgaByNZeLMenh8sgungahWbPo/5jmkDuuHHrVMU748q2JLL1E3nFraPZqoRD
+ ...
+ ...
+ B1Z5AoGBAK8cWMvNYf1pfQ9w6nD4gc3NgRVYLctxFLmkGylqrzs8faoLLBkFq3iI
+ s2vdYwF//wuN2aq8JHldGriyb6xkDjdqiEk+0c98LmyKNmEVt8XghjrZuUrn8dA0
+ 0hfInLdRpaB7b+UeIQavw9yLH0ilijAcMkGzzom7vdqDPizoLpXQ
+ -----END RSA PRIVATE KEY-----
+ [*] Your RSA public key:
+ -----BEGIN PUBLIC KEY-----
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArRsg1+6JZxZNMhGyuM8d
+ +Ue/ky9LSv/XyKh+wppQMS5wx7QEXFcdDgaByNZeLMenh8sgungahWbPo/5jmkDu
+ ...
+ ...
+ GrVJ3o8aDm35EzGymp4ON+A0fdAkweqKV6FqxEJqLWIDRYh+Z01JXUZIrKmnCkgf
+ QQIDAQAB
+ -----END PUBLIC KEY-----
+
+ *******************************************************
+ * PatchSolution0 *
+ *******************************************************
+ [*] Previous:
+ +0x0000000000000070 01 00 00 00 05 00 00 00 ........
+ +0x0000000000000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ +0x0000000000000090 00 00 00 00 00 00 00 00 40 cf 9e 02 00 00 00 00 ........@.......
+ +0x00000000000000a0 40 cf 9e 02 00 00 00 00 00 10 00 00 00 00 00 00 @...............
+ [*] After:
+ +0x0000000000000070 01 00 00 00 05 00 00 00 ........
+ +0x0000000000000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ +0x0000000000000090 00 00 00 00 00 00 00 00 d0 d0 9e 02 00 00 00 00 ................
+ +0x00000000000000a0 d0 d0 9e 02 00 00 00 00 00 10 00 00 00 00 00 00 ................
+
+ [*] Previous:
+ +0x00000000029ecf40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ +0x00000000029ecf50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ +0x00000000029ecf60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ ...
+ ...
+ +0x00000000029ed0c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ [*] After:
+ +0x00000000029ecf40 ef be ad de 4d 49 49 42 49 6a 41 4e 42 67 6b 71 ....MIIBIjANBgkq
+ +0x00000000029ecf50 68 6b 69 47 39 77 30 42 41 51 45 46 41 41 4f 43 hkiG9w0BAQEFAAOC
+ +0x00000000029ecf60 41 51 38 41 4d 49 49 42 43 67 4b 43 41 51 45 41 AQ8AMIIBCgKCAQEA
+ ...
+ ...
+ ...
+ +0x00000000029ed0c0 43 6b 67 66 51 51 49 44 41 51 41 42 ad de ef be CkgfQQIDAQAB....
+
+ [*] Previous:
+ +0x0000000001413e10 44 0f b6 24 18 48 8b 44 24 28 8b 50 f8 85 d2 79 D..$.H.D$(.P...y
+ +0x0000000001413e20 6f o
+ [*] After:
+ +0x0000000001413e10 45 31 e4 48 8d 05 2a 91 5d 01 90 90 90 90 90 90 E1.H..*.].......
+ +0x0000000001413e20 90 .
+
+ [*] New RSA-2048 private key has been saved to
+ /home/doublesine/github.com/navicat-keygen/RegPrivateKey.pem
+
+ *******************************************************
+ * PATCH HAS BEEN DONE SUCCESSFULLY! *
+ * HAVE FUN AND ENJOY~ *
+ *******************************************************
+ ```
+
+5. Repack files into a new AppImage:
+
+ __Example:__
+
+ ```console
+ $ wget 'https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage'
+ $ chmod +x appimagetool-x86_64.AppImage
+ $ ./appimagetool-x86_64.AppImage ~/Desktop/navicat15-premium-en-patched ~/Desktop/navicat15-premium-en-patched.AppImage
+ ```
+
+6. Run the newly-generated AppImage:
+
+ ```console
+ $ chmod +x ~/Desktop/navicat15-premium-en-patched.AppImage
+ $ ~/Desktop/navicat15-premium-en-patched.AppImage
+ ```
+
+7. Use `navicat-keygen` to generate __snKey__ and __Activation Code__.
+
+ ```
+ Usage:
+ navicat-keygen <--bin|--text> [--adv]
+
+ <--bin|--text> Specify "-bin" to generate "license_file" used by Navicat 11.
+ Specify "-text" to generate base64-encoded activation code.
+ This parameter must be specified.
+
+ [--adv] Enable advance mode.
+ This parameter is optional.
+
+ A path to an RSA-2048 private key file.
+ This parameter must be specified.
+ ```
+
+ __Example:__
+
+ ```console
+ $ ./bin/navicat-keygen --text ./RegPrivateKey.pem
+ ```
+
+ You will be asked to select Navicat language and give major version number. After that an randomly generated __snKey__ will be given.
+
+ ```console
+ $ ./bin/navicat-keygen --text ./RegPrivateKey.pem
+ **********************************************************
+ * Navicat Keygen (Linux) by @DoubleLabyrinth *
+ * Version: 1.0 *
+ **********************************************************
+
+ [*] Select Navicat product:
+ 0. DataModeler
+ 1. Premium
+ 2. MySQL
+ 3. PostgreSQL
+ 4. Oracle
+ 5. SQLServer
+ 6. SQLite
+ 7. MariaDB
+ 8. MongoDB
+ 9. ReportViewer
+
+ (Input index)> 1
+
+ [*] Select product language:
+ 0. English
+ 1. Simplified Chinese
+ 2. Traditional Chinese
+ 3. Japanese
+ 4. Polish
+ 5. Spanish
+ 6. French
+ 7. German
+ 8. Korean
+ 9. Russian
+ 10. Portuguese
+
+ (Input index)> 0
+
+ [*] Input major version number:
+ (range: 0 ~ 15, default: 12)> 15
+
+ [*] Serial number:
+ NAVM-RTVJ-EO42-IODD
+
+ [*] Your name:
+ ```
+
+ You can use this __snKey__ to activate your Navicat preliminarily.
+
+ Then you will be asked to input `Your name` and `Your organization`. Just set them whatever you want, but not too long.
+
+ ```console
+ [*] Your name: DoubleLabyrinth
+ [*] Your organization: DoubleLabyrinth
+
+ [*] Input request code in Base64: (Double press ENTER to end)
+ ```
+
+ After that, you will be asked to input request code. Now __DO NOT CLOSE KEYGEN__.
+
+8. __Disconnect your network__. Find and click `Registration`.
+
+ Fill license key by __Serial number__ that the keygen gave and click `Activate`.
+
+9. Generally online activation will fail and Navicat will ask you do `Manual Activation`, just choose it.
+
+10. Copy your request code and paste it in the keygen. Input empty line to tell the keygen that your input ends.
+
+ ```console
+ [*] Input request code in Base64: (Double press ENTER to end)
+ OaGPC3MNjJ/pINbajFzLRkrV2OaSXYLr2tNLDW0fIthPOJQFXr84OOroCY1XN8R2xl2j7epZ182PL6q+BRaSC6hnHev/cZwhq/4LFNcLu0T0D/QUhEEBJl4QzFr8TlFSYI1qhWGLIxkGZggA8vMLMb/sLHYn9QebBigvleP9dNCS4sO82bilFrKFUtq3ch8r7V3mbcbXJCfLhXgrHRvT2FV/s1BFuZzuWZUujxlp37U6Y2PFD8fQgsgBUwrxYbF0XxnXKbCmvtgh2yaB3w9YnQLoDiipKp7io1IxEFMYHCpjmfTGk4WU01mSbdi2OS/wm9pq2Y62xvwawsq1WQJoMg==
+
+ [*] Request Info:
+ {"K":"NAVMRTVJEO42IODD", "DI":"4A12F84C6A088104D23E", "P":"linux"}
+
+ [*] Response Info:
+ {"K":"NAVMRTVJEO42IODD","DI":"4A12F84C6A088104D23E","N":"DoubleLabyrinth","O":"DoubleLabyrinth","T":1575543648}
+
+ [*] Activation Code:
+ i45HIr7T1g69Cm9g3bN1DBpM/Zio8idBw3LOFGXFQjXj0nPfy9yRGuxaUBQkWXSOWa5EAv7S9Z1sljlkZP6cKdfDGYsBb/4N1W5Oj1qogzNtRo5LGwKe9Re3zPY3SO8RXACfpNaKjdjpoOQa9GjQ/igDVH8r1k+Oc7nEnRPZBm0w9aJIM9kS42lbjynVuOJMZIotZbk1NloCodNyRQw3vEEP7kq6bRZsQFp2qF/mr+hIPH8lo/WF3hh+2NivdrzmrKKhPnoqSgSsEttL9a6ueGOP7Io3j2lAFqb9hEj1uC3tPRpYcBpTZX7GAloAENSasFwMdBIdszifDrRW42wzXw==
+ ```
+
+11. Finally, you will get __Activation Code__ which looks like a Base64 string.
+
+ Just copy it and paste it in Navicat `Manual Activation` window, then click `Activate`.
+
+ If nothing wrong, activation should be done successfully.
+
+12. Clean up:
+
+ ```console
+ $ rm ~/Desktop/navicat15-premium-en.AppImage
+ $ rm -rf ~/Desktop/navicat15-premium-en-patched
+ $ mv ~/Desktop/navicat15-premium-en-patched.AppImage ~/Desktop/navicat15-premium-en.AppImage
+ ```
+
diff --git a/README.zh-CN.md b/README.zh-CN.md
new file mode 100644
index 0000000..e2f69d0
--- /dev/null
+++ b/README.zh-CN.md
@@ -0,0 +1,272 @@
+# Navicat Keygen
+
+## 1. 它是怎么工作的?
+
+见[这里](doc/how-does-it-work.zh-CN.md).
+
+## 2. 如何编译?
+
+见[这里](doc/how-to-build.zh-CN.md).
+
+## 3. 如何使用?
+
+1. 从官方网站下载navicat。
+
+ 你会得到一个AppImage文件。例如 `navicat15-premium-en.AppImage`。
+
+ 我假定这个AppImage文件在 `~/Desktop` 文件夹下。
+
+2. 提取AppImage文件里的所有文件到一个文件夹。例如:
+
+ ```console
+ $ mkdir ~/Desktop/navicat15-premium-en
+ $ sudo mount -o loop ~/Desktop/navicat15-premium-en.AppImage ~/Desktop/navicat15-premium-en
+ $ cp -r ~/Desktop/navicat15-premium-en ~/Desktop/navicat15-premium-en-patched
+ $ sudo umount ~/Desktop/navicat15-premium-en
+ $ rm -rf ~/Desktop/navicat15-premium-en
+ ```
+
+3. [编译patcher和keygen。](doc/how-to-build.zh-CN.md)
+
+4. 使用 `navicat-patcher` 替换官方公钥。
+
+ ```
+ Usage:
+ navicat-patcher [--dry-run] [RSA-2048 Private Key File]
+
+ [--dry-run] Run patcher without applying any patches.
+ This parameter is optional.
+
+ Path to a directory where Navicat locates
+ This parameter must be specified.
+
+ [RSA-2048 Private Key File] Path to a PEM-format RSA-2048 private key file.
+ This parameter is optional.
+ ```
+
+ __例如:__
+
+ ```console
+ $ ./bin/navicat-patcher ~/Desktop/navicat15-premium-en-patched
+ ```
+
+ __Navicat Premium 15.0.3 Linux 英文版__ 已经通过测试。
+
+ 下面是一份样例输出:
+
+ ```console
+ **********************************************************
+ * Navicat Patcher (Linux) by @DoubleLabyrinth *
+ * Version: 1.0 *
+ **********************************************************
+
+ Press ENTER to continue or Ctrl + C to abort.
+
+ [+] Try to open libcc.so ... Ok!
+
+ [+] PatchSolution0 ...... Ready to apply
+ RefSegment = 1
+ MachineCodeRva = 0x0000000001413e10
+ PatchMarkOffset = +0x00000000029ecf40
+
+ [*] Generating new RSA private key, it may take a long time...
+ [*] Your RSA private key:
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIEowIBAAKCAQEArRsg1+6JZxZNMhGyuM8d+Ue/ky9LSv/XyKh+wppQMS5wx7QE
+ XFcdDgaByNZeLMenh8sgungahWbPo/5jmkDuuHHrVMU748q2JLL1E3nFraPZqoRD
+ ...
+ ...
+ B1Z5AoGBAK8cWMvNYf1pfQ9w6nD4gc3NgRVYLctxFLmkGylqrzs8faoLLBkFq3iI
+ s2vdYwF//wuN2aq8JHldGriyb6xkDjdqiEk+0c98LmyKNmEVt8XghjrZuUrn8dA0
+ 0hfInLdRpaB7b+UeIQavw9yLH0ilijAcMkGzzom7vdqDPizoLpXQ
+ -----END RSA PRIVATE KEY-----
+ [*] Your RSA public key:
+ -----BEGIN PUBLIC KEY-----
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArRsg1+6JZxZNMhGyuM8d
+ +Ue/ky9LSv/XyKh+wppQMS5wx7QEXFcdDgaByNZeLMenh8sgungahWbPo/5jmkDu
+ ...
+ ...
+ GrVJ3o8aDm35EzGymp4ON+A0fdAkweqKV6FqxEJqLWIDRYh+Z01JXUZIrKmnCkgf
+ QQIDAQAB
+ -----END PUBLIC KEY-----
+
+ *******************************************************
+ * PatchSolution0 *
+ *******************************************************
+ [*] Previous:
+ +0x0000000000000070 01 00 00 00 05 00 00 00 ........
+ +0x0000000000000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ +0x0000000000000090 00 00 00 00 00 00 00 00 40 cf 9e 02 00 00 00 00 ........@.......
+ +0x00000000000000a0 40 cf 9e 02 00 00 00 00 00 10 00 00 00 00 00 00 @...............
+ [*] After:
+ +0x0000000000000070 01 00 00 00 05 00 00 00 ........
+ +0x0000000000000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ +0x0000000000000090 00 00 00 00 00 00 00 00 d0 d0 9e 02 00 00 00 00 ................
+ +0x00000000000000a0 d0 d0 9e 02 00 00 00 00 00 10 00 00 00 00 00 00 ................
+
+ [*] Previous:
+ +0x00000000029ecf40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ +0x00000000029ecf50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ +0x00000000029ecf60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ ...
+ ...
+ +0x00000000029ed0c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ [*] After:
+ +0x00000000029ecf40 ef be ad de 4d 49 49 42 49 6a 41 4e 42 67 6b 71 ....MIIBIjANBgkq
+ +0x00000000029ecf50 68 6b 69 47 39 77 30 42 41 51 45 46 41 41 4f 43 hkiG9w0BAQEFAAOC
+ +0x00000000029ecf60 41 51 38 41 4d 49 49 42 43 67 4b 43 41 51 45 41 AQ8AMIIBCgKCAQEA
+ ...
+ ...
+ ...
+ +0x00000000029ed0c0 43 6b 67 66 51 51 49 44 41 51 41 42 ad de ef be CkgfQQIDAQAB....
+
+ [*] Previous:
+ +0x0000000001413e10 44 0f b6 24 18 48 8b 44 24 28 8b 50 f8 85 d2 79 D..$.H.D$(.P...y
+ +0x0000000001413e20 6f o
+ [*] After:
+ +0x0000000001413e10 45 31 e4 48 8d 05 2a 91 5d 01 90 90 90 90 90 90 E1.H..*.].......
+ +0x0000000001413e20 90 .
+
+ [*] New RSA-2048 private key has been saved to
+ /home/doublesine/github.com/navicat-keygen/RegPrivateKey.pem
+
+ *******************************************************
+ * PATCH HAS BEEN DONE SUCCESSFULLY! *
+ * HAVE FUN AND ENJOY~ *
+ *******************************************************
+ ```
+
+5. 将文件重新打包成AppImage:
+
+ __例如:__
+
+ ```console
+ $ wget 'https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage'
+ $ chmod +x appimagetool-x86_64.AppImage
+ $ ./appimagetool-x86_64.AppImage ~/Desktop/navicat15-premium-en-patched ~/Desktop/navicat15-premium-en-patched.AppImage
+ ```
+
+6. 运行刚生成的AppImage:
+
+ ```console
+ $ chmod +x ~/Desktop/navicat15-premium-en-patched.AppImage
+ $ ~/Desktop/navicat15-premium-en-patched.AppImage
+ ```
+
+7. 使用 `navicat-keygen` 来生成 __序列号__ 和 __激活码__。
+
+ ```
+ Usage:
+ navicat-keygen <--bin|--text> [--adv]
+
+ <--bin|--text> Specify "--bin" to generate "license_file" used by Navicat 11.
+ Specify "--text" to generate base64-encoded activation code.
+ This parameter must be specified.
+
+ [--adv] Enable advance mode.
+ This parameter is optional.
+
+ A path to an RSA-2048 private key file.
+ This parameter must be specified.
+ ```
+
+ __例如:__
+
+ ```console
+ $ ./bin/navicat-keygen --text ./RegPrivateKey.pem
+ ```
+
+ 你会被要求选择Navicat产品类别、Navicat语言版本和填写主版本号。之后一个随机生成的 __序列号__ 将会给出。
+
+ ```console
+ $ ./bin/navicat-keygen --text ./RegPrivateKey.pem
+ **********************************************************
+ * Navicat Keygen (Linux) by @DoubleLabyrinth *
+ * Version: 1.0 *
+ **********************************************************
+
+ [*] Select Navicat product:
+ 0. DataModeler
+ 1. Premium
+ 2. MySQL
+ 3. PostgreSQL
+ 4. Oracle
+ 5. SQLServer
+ 6. SQLite
+ 7. MariaDB
+ 8. MongoDB
+ 9. ReportViewer
+
+ (Input index)> 1
+
+ [*] Select product language:
+ 0. English
+ 1. Simplified Chinese
+ 2. Traditional Chinese
+ 3. Japanese
+ 4. Polish
+ 5. Spanish
+ 6. French
+ 7. German
+ 8. Korean
+ 9. Russian
+ 10. Portuguese
+
+ (Input index)> 0
+
+ [*] Input major version number:
+ (range: 0 ~ 15, default: 12)> 15
+
+ [*] Serial number:
+ NAVM-RTVJ-EO42-IODD
+
+ [*] Your name:
+ ```
+
+ 你可以使用这个 __序列号__ 来暂时激活Navicat。
+
+ 之后你会被要求填写 __用户名__ 和 __组织名__。你可以随意填写,但别太长。
+
+ ```console
+ [*] Your name: DoubleLabyrinth
+ [*] Your organization: DoubleLabyrinth
+
+ [*] Input request code in Base64: (Double press ENTER to end)
+ ```
+
+ 之后你会被要求填写请求码。__注意不要关闭keygen。__
+
+8. __断开网络__. 找到注册窗口,填写keygen给你的 __序列号__,然后点击 `激活`。
+
+9. 通常在线激活会失败,所以在弹出的提示中选择 `手动激活`。
+
+10. 复制 __请求码__ 到keygen,连按两次回车结束。
+
+ ```console
+ [*] Input request code in Base64: (Double press ENTER to end)
+ OaGPC3MNjJ/pINbajFzLRkrV2OaSXYLr2tNLDW0fIthPOJQFXr84OOroCY1XN8R2xl2j7epZ182PL6q+BRaSC6hnHev/cZwhq/4LFNcLu0T0D/QUhEEBJl4QzFr8TlFSYI1qhWGLIxkGZggA8vMLMb/sLHYn9QebBigvleP9dNCS4sO82bilFrKFUtq3ch8r7V3mbcbXJCfLhXgrHRvT2FV/s1BFuZzuWZUujxlp37U6Y2PFD8fQgsgBUwrxYbF0XxnXKbCmvtgh2yaB3w9YnQLoDiipKp7io1IxEFMYHCpjmfTGk4WU01mSbdi2OS/wm9pq2Y62xvwawsq1WQJoMg==
+
+ [*] Request Info:
+ {"K":"NAVMRTVJEO42IODD", "DI":"4A12F84C6A088104D23E", "P":"linux"}
+
+ [*] Response Info:
+ {"K":"NAVMRTVJEO42IODD","DI":"4A12F84C6A088104D23E","N":"DoubleLabyrinth","O":"DoubleLabyrinth","T":1575543648}
+
+ [*] Activation Code:
+ i45HIr7T1g69Cm9g3bN1DBpM/Zio8idBw3LOFGXFQjXj0nPfy9yRGuxaUBQkWXSOWa5EAv7S9Z1sljlkZP6cKdfDGYsBb/4N1W5Oj1qogzNtRo5LGwKe9Re3zPY3SO8RXACfpNaKjdjpoOQa9GjQ/igDVH8r1k+Oc7nEnRPZBm0w9aJIM9kS42lbjynVuOJMZIotZbk1NloCodNyRQw3vEEP7kq6bRZsQFp2qF/mr+hIPH8lo/WF3hh+2NivdrzmrKKhPnoqSgSsEttL9a6ueGOP7Io3j2lAFqb9hEj1uC3tPRpYcBpTZX7GAloAENSasFwMdBIdszifDrRW42wzXw==
+ ```
+
+11. 最终你会得到一个base64编码的 __激活码__。
+
+ 将之复制到 `手动激活` 的窗口,然后点击 `激活`。
+
+ 如果没有什么意外,应该可以成功激活。
+
+12. 最后的清理:
+
+ ```console
+ $ rm ~/Desktop/navicat15-premium-en.AppImage
+ $ rm -rf ~/Desktop/navicat15-premium-en-patched
+ $ mv ~/Desktop/navicat15-premium-en-patched.AppImage ~/Desktop/navicat15-premium-en.AppImage
+ ```
+
diff --git a/common/Exception.hpp b/common/Exception.hpp
new file mode 100644
index 0000000..01db81c
--- /dev/null
+++ b/common/Exception.hpp
@@ -0,0 +1,124 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace ARL {
+
+ class Exception : public std::exception {
+ private:
+
+ const char* m_SourceFile;
+ const size_t m_SourceLine;
+ std::string m_Message;
+ std::vector m_Hints;
+
+ public:
+
+ template
+ Exception(const char* SourceFile, size_t SourceLine, const char* Format, __ArgTypes&&... Args) noexcept :
+ m_SourceFile(SourceFile),
+ m_SourceLine(SourceLine)
+ {
+ if constexpr (sizeof...(Args) == 0) {
+ m_Message.assign(Format);
+ } else {
+ int l;
+
+ l = snprintf(nullptr, 0, Format, std::forward<__ArgTypes>(Args)...);
+ if (l < 0) {
+ std::terminate();
+ }
+
+ m_Message.resize(l + 1);
+
+ l = snprintf(m_Message.data(), m_Message.length(), Format, std::forward<__ArgTypes>(Args)...);
+ if (l < 0) {
+ std::terminate();
+ }
+
+ while (m_Message.back() == '\x00') {
+ m_Message.pop_back();
+ }
+ }
+ }
+
+ [[nodiscard]]
+ auto ExceptionFile() const noexcept {
+ return m_SourceFile;
+ }
+
+ [[nodiscard]]
+ auto ExceptionLine() const noexcept {
+ return m_SourceLine;
+ }
+
+ [[nodiscard]]
+ auto ExceptionMessage() const noexcept {
+ return m_Message.c_str();
+ }
+
+ template
+ auto& PushHint(__HintType&& Hint) noexcept { // if an exception is thrown, just suppress and terminate.
+ m_Hints.emplace_back(std::forward<__HintType>(Hint));
+ return *this;
+ }
+
+ template
+ auto& PushFormatHint(const char* Format, __ArgTypes&&... Args) noexcept { // if an exception is thrown, just suppress and terminate.
+ int l;
+ std::string s;
+
+ l = snprintf(nullptr, 0, Format, std::forward<__ArgTypes>(Args)...);
+ if (l < 0) {
+ std::terminate();
+ }
+
+ s.resize(l + 1);
+
+ l = snprintf(s.data(), s.length(), Format, std::forward<__ArgTypes>(Args)...);
+ if (l < 0) {
+ std::terminate();
+ }
+
+ while (s.back() == '\x00') {
+ s.pop_back();
+ }
+
+ m_Hints.emplace_back(std::move(s));
+
+ return *this;
+ }
+
+ [[nodiscard]]
+ const auto& Hints() const noexcept {
+ return m_Hints;
+ }
+
+ [[nodiscard]]
+ virtual bool HasErrorCode() const noexcept {
+ return false;
+ }
+
+ [[nodiscard]]
+ virtual intptr_t ErrorCode() const noexcept {
+ return 0;
+ }
+
+ [[nodiscard]]
+ virtual const char* ErrorString() const noexcept {
+ return nullptr;
+ }
+
+ [[nodiscard]]
+ // NOLINTNEXTLINE: mark "virtual" explicitly for more readability
+ virtual const char* what() const noexcept override {
+ return ExceptionMessage();
+ }
+ };
+
+}
+
diff --git a/common/ExceptionGeneric.hpp b/common/ExceptionGeneric.hpp
new file mode 100644
index 0000000..82dd5f2
--- /dev/null
+++ b/common/ExceptionGeneric.hpp
@@ -0,0 +1,29 @@
+#pragma once
+#include "Exception.hpp"
+
+namespace ARL {
+
+#pragma push_macro("DECLARE_NEW_EXCEPTION")
+#undef DECLARE_NEW_EXCEPTION
+
+#define DECLARE_NEW_EXCEPTION(name) \
+ class name final : public Exception { \
+ public: \
+ template \
+ name(const char* SourceFile, size_t SourceLine, const char* Format, __ArgTypes&&... Args) noexcept : \
+ Exception(SourceFile, SourceLine, Format, std::forward<__ArgTypes>(Args)...) {} \
+ }
+
+ DECLARE_NEW_EXCEPTION(AssertionError);
+ DECLARE_NEW_EXCEPTION(EOFError);
+ DECLARE_NEW_EXCEPTION(IndexError);
+ DECLARE_NEW_EXCEPTION(KeyError);
+ DECLARE_NEW_EXCEPTION(NotImplementedError);
+ DECLARE_NEW_EXCEPTION(OverflowError);
+ DECLARE_NEW_EXCEPTION(ValueError);
+
+#undef DECLARE_NEW_EXCEPTION
+#pragma pop_macro("DECLARE_NEW_EXCEPTION")
+
+}
+
diff --git a/common/ExceptionOpenssl.hpp b/common/ExceptionOpenssl.hpp
new file mode 100644
index 0000000..6ef1db8
--- /dev/null
+++ b/common/ExceptionOpenssl.hpp
@@ -0,0 +1,45 @@
+#pragma once
+#include "Exception.hpp"
+#include
+
+namespace ARL {
+
+ class OpensslError final : public Exception {
+ private:
+
+ unsigned long m_ErrorCode;
+
+ public:
+
+ template
+ OpensslError(const char* SourceFile, size_t SourceLine, unsigned long ErrorCode, const char* Format, __ArgTypes&&... Args) noexcept :
+ Exception(SourceFile, SourceLine, Format, std::forward<__ArgTypes>(Args)...),
+ m_ErrorCode(ErrorCode) {}
+
+ [[nodiscard]]
+ // NOLINTNEXTLINE: mark "virtual" explicitly for more readability
+ virtual bool HasErrorCode() const noexcept override {
+ return true;
+ }
+
+ [[nodiscard]]
+ // NOLINTNEXTLINE: mark "virtual" explicitly for more readability
+ virtual intptr_t ErrorCode() const noexcept override {
+ return m_ErrorCode;
+ }
+
+ [[nodiscard]]
+ // NOLINTNEXTLINE: mark "virtual" explicitly for more readability
+ virtual const char* ErrorString() const noexcept override {
+ static bool loaded = false;
+ if (loaded == false) {
+ ERR_load_crypto_strings();
+ loaded = true;
+ }
+ return ERR_reason_error_string(m_ErrorCode);
+ }
+ };
+
+}
+
+
diff --git a/common/ExceptionSystem.hpp b/common/ExceptionSystem.hpp
new file mode 100644
index 0000000..397c2ec
--- /dev/null
+++ b/common/ExceptionSystem.hpp
@@ -0,0 +1,38 @@
+#pragma once
+#include "Exception.hpp"
+#include
+
+namespace ARL {
+
+ class SystemError final : public Exception {
+ private:
+
+ int m_ErrorCode;
+
+ public:
+
+ template
+ SystemError(const char* SourceFile, size_t SourceLine, int ErrorCode, const char* Format, __ArgTypes&&... Args) noexcept :
+ Exception(SourceFile, SourceLine, Format, std::forward<__ArgTypes>(Args)...),
+ m_ErrorCode(ErrorCode) {}
+
+ [[nodiscard]]
+ // NOLINTNEXTLINE: mark "virtual" explicitly for more readability
+ virtual bool HasErrorCode() const noexcept override {
+ return true;
+ }
+
+ [[nodiscard]]
+ // NOLINTNEXTLINE: mark "virtual" explicitly for more readability
+ virtual intptr_t ErrorCode() const noexcept override {
+ return m_ErrorCode;
+ }
+
+ [[nodiscard]]
+ // NOLINTNEXTLINE: mark "virtual" explicitly for more readability
+ virtual const char* ErrorString() const noexcept override {
+ return strerror(m_ErrorCode);
+ }
+ };
+
+}
\ No newline at end of file
diff --git a/common/RSACipher.hpp b/common/RSACipher.hpp
new file mode 100644
index 0000000..3c3790f
--- /dev/null
+++ b/common/RSACipher.hpp
@@ -0,0 +1,274 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+#include
+#include "Exception.hpp"
+#include "ExceptionOpenssl.hpp"
+#include "ResourceWrapper.hpp"
+#include "ResourceTraitsOpenssl.hpp"
+
+namespace nkg {
+
+ enum class RSAKeyType {
+ PrivateKey,
+ PublicKey
+ };
+
+ enum class RSAKeyFormat {
+ PEM,
+ PKCS1
+ };
+
+ class RSACipher final : private ARL::ResourceWrapper {
+ private:
+
+ template
+ static void _WriteRSAToBIO(RSA* lpRSA, BIO* lpBIO) {
+ if constexpr (__Type == RSAKeyType::PrivateKey) {
+ if (PEM_write_bio_RSAPrivateKey(lpBIO, lpRSA, nullptr, nullptr, 0, nullptr, nullptr) == 0) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "PEM_write_bio_RSAPrivateKey failed.");
+ }
+ }
+
+ if constexpr (__Type == RSAKeyType::PublicKey) {
+ if constexpr (__Format == RSAKeyFormat::PEM) {
+ if (PEM_write_bio_RSA_PUBKEY(lpBIO, lpRSA) == 0) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "PEM_write_bio_RSA_PUBKEY failed.");
+ }
+ }
+
+ if constexpr (__Format == RSAKeyFormat::PKCS1) {
+ if (PEM_write_bio_RSAPublicKey(lpBIO, lpRSA) == 0) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "PEM_write_bio_RSAPublicKey failed.");
+ }
+ }
+
+ static_assert(__Format == RSAKeyFormat::PEM || __Format == RSAKeyFormat::PKCS1);
+ }
+
+ static_assert(__Type == RSAKeyType::PrivateKey || __Type == RSAKeyType::PublicKey);
+ }
+
+ template
+ [[nodiscard]]
+ static RSA* _ReadRSAFromBIO(BIO* lpBIO) {
+ RSA* lpRSA;
+
+ if constexpr (__Type == RSAKeyType::PrivateKey) {
+ lpRSA = PEM_read_bio_RSAPrivateKey(lpBIO, nullptr, nullptr, nullptr);
+ if (lpRSA == nullptr) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "PEM_read_bio_RSAPrivateKey failed.")
+ .PushHint("Are you sure that you DO provide a valid RSA private key file?");
+ }
+ }
+
+ if constexpr (__Type == RSAKeyType::PublicKey) {
+ if constexpr (__Format == RSAKeyFormat::PEM) {
+ lpRSA = PEM_read_bio_RSA_PUBKEY(lpBIO, nullptr, nullptr, nullptr);
+ if (lpRSA == nullptr) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "PEM_read_bio_RSA_PUBKEY failed.")
+ .PushHint("Are you sure that you DO provide a valid RSA public key file with PEM format?");
+ }
+ }
+
+ if constexpr (__Format == RSAKeyFormat::PKCS1) {
+ lpRSA = PEM_read_bio_RSAPublicKey(lpBIO, nullptr, nullptr, nullptr);
+ if (lpRSA == nullptr) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "PEM_read_bio_RSAPublicKey failed.")
+ .PushHint("Are you sure that you DO provide a valid RSA public key file with PKCS1 format?");
+ }
+ }
+
+ static_assert(__Format == RSAKeyFormat::PEM || __Format == RSAKeyFormat::PKCS1);
+ }
+
+ static_assert(__Type == RSAKeyType::PrivateKey || __Type == RSAKeyType::PublicKey);
+
+ return lpRSA;
+ }
+
+ public:
+
+ RSACipher() : ARL::ResourceWrapper(RSA_new()) {
+ if (IsValid() == false) {
+ throw ARL::OpensslError(__BASE_FILE__, __LINE__, ERR_get_error(), "RSA_new failed.");
+ }
+ }
+
+ [[nodiscard]]
+ size_t Bits() const {
+#if (OPENSSL_VERSION_NUMBER & 0xffff0000) == 0x10000000 // openssl 1.0.x
+ if (Get()->n == nullptr) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "RSA modulus has not been set.");
+ } else {
+ return BN_num_bits(Get()->n);
+ }
+#elif (OPENSSL_VERSION_NUMBER & 0xffff0000) == 0x10100000 // openssl 1.1.x
+ return RSA_bits(Get());
+#else
+#error "Unexpected openssl version!"
+#endif
+ }
+
+ void GenerateKey(int bits, unsigned int e = RSA_F4) {
+ ARL::ResourceWrapper bn_e{ ARL::ResourceTraits::OpensslBIGNUM{} };
+
+ bn_e.TakeOver(BN_new());
+ if (bn_e.IsValid() == false) {
+ throw ARL::OpensslError(__BASE_FILE__, __LINE__, ERR_get_error(), "BN_new failed.");
+ }
+
+ if (!BN_set_word(bn_e, e)) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "BN_set_word failed.");
+ }
+
+ if (!RSA_generate_key_ex(Get(), bits, bn_e, nullptr)) {
+ throw ARL::OpensslError(__BASE_FILE__, __LINE__, ERR_get_error(), "RSA_generate_key_ex failed.");
+ }
+ }
+
+ template
+ void ExportKeyToFile(std::string_view FileName) const {
+ ARL::ResourceWrapper KeyFile{ ARL::ResourceTraits::OpensslBIO{} };
+
+ KeyFile.TakeOver(BIO_new_file(FileName.data(), "w"));
+ if (KeyFile.IsValid() == false) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "BIO_new_file failed.");
+ }
+
+ _WriteRSAToBIO<__Type, __Format>(Get(), KeyFile);
+ }
+
+ template
+ [[nodiscard]]
+ std::string ExportKeyString() const {
+ ARL::ResourceWrapper TempMemory{ ARL::ResourceTraits::OpensslBIO{} };
+ const char* lpsz = nullptr;
+
+ TempMemory.TakeOver(BIO_new(BIO_s_mem()));
+ if (TempMemory.IsValid() == false) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "BIO_new failed.");
+ }
+
+ _WriteRSAToBIO<__Type, __Format>(Get(), TempMemory);
+
+ auto l = BIO_get_mem_data(TempMemory.Get(), &lpsz);
+
+ std::string KeyString(lpsz, l);
+ while (KeyString.back() == '\n' || KeyString.back() == '\r') {
+ KeyString.pop_back();
+ }
+
+ return KeyString;
+ }
+
+ template
+ void ImportKeyFromFile(std::string_view FileName) {
+ ARL::ResourceWrapper KeyFile{ ARL::ResourceTraits::OpensslBIO{} };
+
+ KeyFile.TakeOver(BIO_new_file(FileName.data(), "r"));
+ if (KeyFile.IsValid() == false) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "BIO_new_file failed.");
+ }
+
+ ReleaseAndTakeOver(_ReadRSAFromBIO<__Type, __Format>(KeyFile));
+ }
+
+ template
+ void ImportKeyString(std::string_view KeyString) {
+ ARL::ResourceWrapper TempMemory{ ARL::ResourceTraits::OpensslBIO{} };
+
+ TempMemory.TakeOver(BIO_new(BIO_s_mem()));
+ if (TempMemory.IsValid() == false) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "BIO_new failed.");
+ }
+
+ if (BIO_puts(TempMemory.Get(), KeyString.data()) <= 0) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "BIO_puts failed.");
+ }
+
+ TakeOver(_ReadRSAFromBIO<__Type, __Format>(TempMemory));
+ }
+
+ template
+ size_t Encrypt(const void* lpFrom, size_t cbFrom, void* lpTo, int Padding) const {
+ int BytesWritten;
+
+ if (cbFrom > static_cast(INT_MAX)) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "Length overflowed.");
+ }
+
+ if constexpr (__Type == RSAKeyType::PrivateKey) {
+ BytesWritten = RSA_private_encrypt(
+ static_cast(cbFrom),
+ reinterpret_cast(lpFrom),
+ reinterpret_cast(lpTo),
+ Get(),
+ Padding
+ );
+
+ if (BytesWritten == -1) {
+ throw ARL::OpensslError(__BASE_FILE__, __LINE__, ERR_get_error(), "RSA_private_encrypt failed.");
+ }
+ } else {
+ BytesWritten = RSA_public_encrypt(
+ static_cast(cbFrom),
+ reinterpret_cast(lpFrom),
+ reinterpret_cast(lpTo),
+ Get(),
+ Padding
+ );
+
+ if (BytesWritten == -1) {
+ throw ARL::OpensslError(__BASE_FILE__, __LINE__, ERR_get_error(), "RSA_public_encrypt failed.");
+ }
+ }
+
+ return BytesWritten;
+ }
+
+ template
+ size_t Decrypt(const void* lpFrom, size_t cbFrom, void* lpTo, int Padding) const {
+ int BytesWritten;
+
+ if (cbFrom > static_cast(INT_MAX)) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "Length overflowed.");
+ }
+
+ if constexpr (__Type == RSAKeyType::PrivateKey) {
+ BytesWritten = RSA_private_decrypt(
+ static_cast(cbFrom),
+ reinterpret_cast(lpFrom),
+ reinterpret_cast(lpTo),
+ Get(),
+ Padding
+ );
+
+ if (BytesWritten == -1) {
+ throw ARL::OpensslError(__BASE_FILE__, __LINE__, ERR_get_error(), "RSA_private_decrypt failed.")
+ .PushHint("Are your sure you DO provide a correct private key?");
+ }
+ } else {
+ BytesWritten = RSA_public_decrypt(
+ static_cast(cbFrom),
+ reinterpret_cast(lpFrom),
+ reinterpret_cast(lpTo),
+ Get(),
+ Padding
+ );
+
+ if (BytesWritten == -1) {
+ throw ARL::OpensslError(__BASE_FILE__, __LINE__, ERR_get_error(), "RSA_public_decrypt failed.")
+ .PushHint("Are your sure you DO provide a correct public key?");
+ }
+ }
+
+ return BytesWritten;
+ }
+ };
+
+}
+
diff --git a/common/ResourceTraitsOpenssl.hpp b/common/ResourceTraitsOpenssl.hpp
new file mode 100644
index 0000000..1782eb5
--- /dev/null
+++ b/common/ResourceTraitsOpenssl.hpp
@@ -0,0 +1,68 @@
+#pragma once
+#include
+#include
+
+namespace ARL::ResourceTraits {
+
+ struct OpensslBIO {
+ using HandleType = BIO*;
+
+ static inline const HandleType InvalidValue = nullptr;
+
+ [[nodiscard]]
+ static bool IsValid(const HandleType& Handle) noexcept {
+ return Handle != InvalidValue;
+ }
+
+ static void Release(const HandleType& Handle) noexcept {
+ BIO_free(Handle);
+ }
+ };
+
+ struct OpensslBIOChain {
+ using HandleType = BIO*;
+
+ static inline const HandleType InvalidValue = nullptr;
+
+ [[nodiscard]]
+ static bool IsValid(const HandleType& Handle) noexcept {
+ return Handle != InvalidValue;
+ }
+
+ static void Release(const HandleType& Handle) noexcept {
+ BIO_free_all(Handle);
+ }
+ };
+
+ struct OpensslBIGNUM {
+ using HandleType = BIGNUM*;
+
+ static inline const HandleType InvalidValue = nullptr;
+
+ [[nodiscard]]
+ static bool IsValid(const HandleType& Handle) noexcept {
+ return Handle != InvalidValue;
+ }
+
+ static void Release(const HandleType& Handle) noexcept {
+ BN_free(Handle);
+ }
+ };
+
+ struct OpensslRSA {
+ using HandleType = RSA*;
+
+ static inline const HandleType InvalidValue = nullptr;
+
+ [[nodiscard]]
+ static bool IsValid(const HandleType& Handle) noexcept {
+ return Handle != InvalidValue;
+ }
+
+ static void Release(const HandleType& Handle) noexcept {
+ RSA_free(Handle);
+ }
+ };
+
+}
+
diff --git a/common/ResourceWrapper.hpp b/common/ResourceWrapper.hpp
new file mode 100644
index 0000000..cbb077e
--- /dev/null
+++ b/common/ResourceWrapper.hpp
@@ -0,0 +1,375 @@
+#pragma once
+#include
+#include
+#include
+
+namespace ARL {
+
+ template
+ class ResourceWrapper {
+ public:
+
+ using HandleType = typename __ResourceTraits::HandleType;
+
+ static_assert(
+ std::is_trivial_v && std::is_standard_layout_v,
+ "HandleType must be a POD type."
+ );
+
+ private:
+
+ HandleType m_Handle;
+
+ public:
+
+ ResourceWrapper() noexcept :
+ m_Handle(__ResourceTraits::InvalidValue) {}
+
+ ResourceWrapper(const HandleType& Handle) noexcept :
+ m_Handle(Handle) {}
+
+ ResourceWrapper(__ResourceTraits) noexcept :
+ m_Handle(__ResourceTraits::InvalidValue) {}
+
+ ResourceWrapper(__ResourceTraits, const HandleType& Handle) noexcept :
+ m_Handle(Handle) {}
+
+ ResourceWrapper(const ResourceWrapper& Other) = delete;
+
+ ResourceWrapper(ResourceWrapper&& Other) noexcept :
+ m_Handle(std::move(Other.m_Handle))
+ {
+ Other.m_Handle = __ResourceTraits::InvalidValue;
+ }
+
+ ResourceWrapper& operator=(const ResourceWrapper& Other) = delete;
+
+ ResourceWrapper& operator=(ResourceWrapper&& Other) noexcept {
+ if (this != std::addressof(Other)) {
+ if (IsValid()) {
+ __ResourceTraits::Release(m_Handle);
+ }
+
+ m_Handle = std::move(Other.m_Handle);
+ Other.m_Handle = __ResourceTraits::InvalidValue;
+ }
+
+ return *this;
+ }
+
+ [[nodiscard]]
+ operator HandleType() const noexcept { // NOLINT: Allow implicit conversion.
+ return m_Handle;
+ }
+
+ template
+ [[nodiscard]]
+ __AsType As() const noexcept {
+ return reinterpret_cast<__AsType>(m_Handle);
+ }
+
+ template>
+ [[nodiscard]]
+ std::enable_if_t<__Enable, HandleType> operator->() const noexcept {
+ return m_Handle;
+ }
+
+ [[nodiscard]]
+ bool IsValid() const noexcept {
+ return __ResourceTraits::IsValid(m_Handle);
+ }
+
+ [[nodiscard]]
+ HandleType Get() const noexcept {
+ return m_Handle;
+ }
+
+ [[nodiscard]]
+ HandleType* GetAddressOf() noexcept {
+ return &m_Handle;
+ }
+
+ template
+ [[nodiscard]]
+ __ReturnType GetAddressOfAs() noexcept {
+ return reinterpret_cast<__ReturnType>(&m_Handle);
+ }
+
+ void TakeOver(const HandleType& Handle) {
+ if (IsValid() == false) {
+ m_Handle = Handle;
+ } else {
+ throw std::runtime_error("ResourceWrapper is already in use.");
+ }
+ }
+
+ void Discard() noexcept {
+ m_Handle = __ResourceTraits::InvalidValue;
+ }
+
+ [[nodiscard]]
+ HandleType Transfer() noexcept {
+ auto t = m_Handle;
+ m_Handle = __ResourceTraits::InvalidValue;
+ return t;
+ }
+
+ template
+ [[nodiscard]]
+ __ReturnType TransferAs() noexcept {
+ static_assert(
+ std::is_trivial_v<__ReturnType> && std::is_standard_layout_v<__ReturnType>,
+ "__ReturnType should also be a POD type, just like HandleType."
+ );
+
+ auto t = reinterpret_cast<__ReturnType>(m_Handle);
+ m_Handle = __ResourceTraits::InvalidValue;
+ return t;
+ }
+
+ void Release() {
+ if (IsValid()) {
+ __ResourceTraits::Release(m_Handle);
+ m_Handle = __ResourceTraits::InvalidValue;
+ }
+ }
+
+ void ReleaseAndTakeOver(const HandleType& Handle) {
+ if (IsValid()) {
+ __ResourceTraits::Release(m_Handle);
+ }
+
+ m_Handle = Handle;
+ }
+
+ [[nodiscard]]
+ HandleType* ReleaseAndGetAddressOf() {
+ if (IsValid()) {
+ __ResourceTraits::Release(m_Handle);
+ m_Handle = __ResourceTraits::InvalidValue;
+ }
+
+ return GetAddressOf();
+ }
+
+ template
+ __ReturnType ReleaseAndGetAddressOfAs() {
+ if (IsValid()) {
+ __ResourceTraits::Release(m_Handle);
+ m_Handle = __ResourceTraits::InvalidValue;
+ }
+
+ return GetAddressOfAs<__ReturnType>();
+ }
+
+ ~ResourceWrapper() {
+ Release();
+ }
+ };
+
+ template
+ class ResourceWrapperEx {
+ public:
+
+ using HandleType = typename __ResourceTraits::HandleType;
+ using DeleterType = __DeleterType;
+
+ static_assert(
+ std::is_trivial_v && std::is_standard_layout_v,
+ "HandleType must be a POD type."
+ );
+
+ private:
+
+ HandleType m_Handle;
+ DeleterType m_Deleter;
+
+ public:
+
+ template
+ ResourceWrapperEx(__ResourceTraits, __DeleterArgType&& Deleter) noexcept :
+ m_Handle(__ResourceTraits::InvalidValue),
+ m_Deleter(std::forward<__DeleterArgType>(Deleter)) {}
+
+ template
+ ResourceWrapperEx(__ResourceTraits, const HandleType& Handle, __DeleterArgType&& Deleter) noexcept :
+ m_Handle(Handle),
+ m_Deleter(std::forward<__DeleterArgType>(Deleter)) {}
+
+ ResourceWrapperEx(const ResourceWrapperEx& Other) = delete;
+
+ ResourceWrapperEx(ResourceWrapperEx&& Other) noexcept :
+ m_Handle(std::move(Other.m_Handle)),
+ m_Deleter(std::move(Other.m_Deleter))
+ {
+ Other.m_Handle = __ResourceTraits::InvalidValue;
+ }
+
+ ResourceWrapperEx& operator=(const ResourceWrapperEx& Other) = delete;
+
+ ResourceWrapperEx& operator=(ResourceWrapperEx&& Other) noexcept {
+ if (this != std::addressof(Other)) {
+ if (IsValid()) {
+ m_Deleter(m_Handle);
+ }
+
+ m_Handle = std::move(Other.m_Handle);
+ m_Deleter = std::move(Other.m_Deleter);
+ Other.m_Handle = __ResourceTraits::InvalidValue;
+ }
+
+ return *this;
+ }
+
+ [[nodiscard]]
+ operator HandleType() const noexcept { // NOLINT: Allow implicit conversion.
+ return m_Handle;
+ }
+
+ template
+ [[nodiscard]]
+ __AsType As() const noexcept {
+ return reinterpret_cast<__AsType>(m_Handle);
+ }
+
+ template>
+ [[nodiscard]]
+ std::enable_if_t<__Enable, HandleType> operator->() const noexcept {
+ return m_Handle;
+ }
+
+ [[nodiscard]]
+ bool IsValid() const noexcept {
+ return __ResourceTraits::IsValid(m_Handle);
+ }
+
+ [[nodiscard]]
+ HandleType Get() const noexcept {
+ return m_Handle;
+ }
+
+ [[nodiscard]]
+ HandleType* GetAddressOf() noexcept {
+ return &m_Handle;
+ }
+
+ template
+ [[nodiscard]]
+ __ReturnType GetAddressOfAs() noexcept {
+ return reinterpret_cast<__ReturnType>(&m_Handle);
+ }
+
+ void TakeOver(const HandleType& Handle) {
+ if (IsValid() == false) {
+ m_Handle = Handle;
+ } else {
+ throw std::runtime_error("ResourceWrapperEx is already in use.");
+ }
+ }
+
+ void Discard() noexcept {
+ m_Handle = __ResourceTraits::InvalidValue;
+ }
+
+ [[nodiscard]]
+ HandleType Transfer() noexcept {
+ auto t = m_Handle;
+ m_Handle = __ResourceTraits::InvalidValue;
+ return t;
+ }
+
+ template
+ [[nodiscard]]
+ __ReturnType TransferAs() noexcept {
+ static_assert(std::is_trivial_v<__ReturnType> && std::is_standard_layout_v<__ReturnType>);
+
+ auto t = reinterpret_cast<__ReturnType>(m_Handle);
+ m_Handle = __ResourceTraits::InvalidValue;
+ return t;
+ }
+
+ void Release() {
+ if (IsValid()) {
+ m_Deleter(m_Handle);
+ m_Handle = __ResourceTraits::InvalidValue;
+ }
+ }
+
+ void ReleaseAndTakeOver(const HandleType& Handle) {
+ if (IsValid()) {
+ __ResourceTraits::Release(m_Handle);
+ }
+
+ m_Handle = Handle;
+ }
+
+ [[nodiscard]]
+ HandleType* ReleaseAndGetAddressOf() {
+ if (IsValid()) {
+ m_Deleter(m_Handle);
+ m_Handle = __ResourceTraits::InvalidValue;
+ }
+
+ return GetAddressOf();
+ }
+
+ template
+ __ReturnType ReleaseAndGetAddressOfAs() {
+ if (IsValid()) {
+ m_Deleter(m_Handle);
+ m_Handle = __ResourceTraits::InvalidValue;
+ }
+
+ return GetAddressOfAs<__ReturnType>();
+ }
+
+ ~ResourceWrapperEx() {
+ Release();
+ }
+ };
+
+ template
+ ResourceWrapperEx(__ResourceTraits, __DeleterArgType&& Deleter) ->
+ ResourceWrapperEx<__ResourceTraits, std::remove_reference_t<__DeleterArgType>>;
+
+ template
+ ResourceWrapperEx(__ResourceTraits, const typename __ResourceTraits::HandleType& Handle, __DeleterArgType&& Deleter) ->
+ ResourceWrapperEx<__ResourceTraits, std::remove_reference_t<__DeleterArgType>>;
+
+ namespace ResourceTraits {
+
+ template
+ struct CppObject {
+ using HandleType = __ClassType*;
+
+ static inline const HandleType InvalidValue = nullptr;
+
+ [[nodiscard]]
+ static bool IsValid(const HandleType& Handle) noexcept {
+ return Handle != InvalidValue;
+ }
+
+ static void Release(const HandleType& Handle) {
+ delete Handle;
+ }
+ };
+
+ template
+ struct CppArray {
+ using HandleType = __ElementType*;
+
+ static inline const HandleType InvalidValue = nullptr;
+
+ [[nodiscard]]
+ static bool IsValid(const HandleType& Handle) noexcept {
+ return Handle != InvalidValue;
+ }
+
+ static void Release(const HandleType& Handle) {
+ delete[] Handle;
+ }
+ };
+
+ }
+}
+
diff --git a/doc/how-does-it-work.md b/doc/how-does-it-work.md
new file mode 100644
index 0000000..526f526
--- /dev/null
+++ b/doc/how-does-it-work.md
@@ -0,0 +1,283 @@
+# Navicat Keygen - How does it work?
+
+[中文版](how-does-it-work.zh-CN.md)
+
+## 1. Keyword Explanation.
+
+* __Navicat Activation Public Key__
+
+ It is a __RSA-2048__ public key that Navicat used to encrypt or decrypt offline activation information.
+
+ It is stored in __navicat.exe__ as a kind of resource called __RCData__. The resource name is `"ACTIVATIONPUBKEY"`. You can see it by a software called [___Resource Hacker___](http://www.angusj.com/resourcehacker/). The public key is
+
+ ```
+ -----BEGIN PUBLIC KEY-----
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I
+ qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv
+ a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF
+ R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2
+ WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt
+ YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ
+ awIDAQAB
+ -----END PUBLIC KEY-----
+ ```
+
+ If you have the corresponding private key, you can tell me. I would be very appreciated for your generous.
+
+ __NOTICE:__
+
+ Start from __Navicat Premium 12.0.25__, Navicat do not load this public key from resource in `navicat.exe`. Instead, the public key is stored in `libcc.dll` and has been encrypted. To avoid being replaced easily, the public key is split into 5 parts:
+
+ The following content is discovered from `libcc.dll` of __Navicat Premium x64 12.0.25 Simplified Chinese version__. The SHA256 value of `libcc.dll` is `607e0a84c75966b00f3d12fa833e91d159e4f51ac51b6ba66f98d0c3cbefdce0`.
+
+ I __DO NOT__ guarantee that offset values are absolutely correct in other versions. But __char strings__ and __immediate values__ are highly possible to be found.
+
+ 1. At file offset `+0x01A12090` in `libcc.dll`, stored as __char string__:
+
+ ```
+ "D75125B70767B94145B47C1CB3C0755E
+ 7CCB8825C5DCE0C58ACF944E08280140
+ 9A02472FAFFD1CD77864BB821AE36766
+ FEEDE6A24F12662954168BFA314BD950
+ 32B9D82445355ED7BC0B880887D650F5"
+ ```
+
+ 2. At file offset `+0x0059D799` in `libcc.dll`, stored as __immediate value__ in a instruction:
+
+ ```
+ 0xFE 0xEA 0xBC 0x01
+ ```
+
+ In decimal: `29158142`
+
+ 3. At file offset `+0x01A11DA0` in `libcc.dll`, stored as __char string__:
+
+ ```
+ "E1CED09B9C2186BF71A70C0FE2F1E0AE
+ F3BD6B75277AAB20DFAF3D110F75912B
+ FB63AC50EC4C48689D1502715243A79F
+ 39FF2DE2BF15CE438FF885745ED54573
+ 850E8A9F40EE2FF505EB7476F95ADB78
+ 3B28CA374FAC4632892AB82FB3BF4715
+ FCFE6E82D03731FC3762B6AAC3DF1C3B
+ C646FE9CD3C62663A97EE72DB932A301
+ 312B4A7633100C8CC357262C39A2B3A6
+ 4B224F5276D5EDBDF0804DC3AC4B8351
+ 62BB1969EAEBADC43D2511D6E0239287
+ 81B167A48273B953378D3D2080CC0677
+ 7E8A2364F0234B81064C5C739A8DA28D
+ C5889072BF37685CBC94C2D31D0179AD
+ 86D8E3AA8090D4F0B281BE37E0143746
+ E6049CCC06899401264FA471C016A96C
+ 79815B55BBC26B43052609D9D175FBCD
+ E455392F10E51EC162F51CF732E6BB39
+ 1F56BBFD8D957DF3D4C55B71CEFD54B1
+ 9C16D458757373E698D7E693A8FC3981
+ 5A8BF03BA05EA8C8778D38F9873D62B4
+ 460F41ACF997C30E7C3AF025FA171B5F
+ 5AD4D6B15E95C27F6B35AD61875E5505
+ 449B4E"
+ ```
+
+ 4. At file offset `+0x0059D77F` in `libcc.dll`, stored as __immediate value__ in a instruction:
+
+ ```
+ 0x59 0x08 0x01 0x00
+ ```
+
+ In decimal: `67673`
+
+ 5. At file offset `+ 0x1A11D8C` in `libcc.dll`, stored as __char string__:
+
+ ```
+ "92933"
+ ```
+
+ Then output encrypted public key with format `"%s%d%s%d%s"`. The order is the same as it lists:
+
+ ```
+ D75125B70767B94145B47C1CB3C0755E7CCB8825C5DCE0C58ACF944E082801409A02472FAFFD1CD77864BB821AE36766FEEDE6A24F12662954168BFA314BD95032B9D82445355ED7BC0B880887D650F529158142E1CED09B9C2186BF71A70C0FE2F1E0AEF3BD6B75277AAB20DFAF3D110F75912BFB63AC50EC4C48689D1502715243A79F39FF2DE2BF15CE438FF885745ED54573850E8A9F40EE2FF505EB7476F95ADB783B28CA374FAC4632892AB82FB3BF4715FCFE6E82D03731FC3762B6AAC3DF1C3BC646FE9CD3C62663A97EE72DB932A301312B4A7633100C8CC357262C39A2B3A64B224F5276D5EDBDF0804DC3AC4B835162BB1969EAEBADC43D2511D6E023928781B167A48273B953378D3D2080CC06777E8A2364F0234B81064C5C739A8DA28DC5889072BF37685CBC94C2D31D0179AD86D8E3AA8090D4F0B281BE37E0143746E6049CCC06899401264FA471C016A96C79815B55BBC26B43052609D9D175FBCDE455392F10E51EC162F51CF732E6BB391F56BBFD8D957DF3D4C55B71CEFD54B19C16D458757373E698D7E693A8FC39815A8BF03BA05EA8C8778D38F9873D62B4460F41ACF997C30E7C3AF025FA171B5F5AD4D6B15E95C27F6B35AD61875E5505449B4E6767392933
+ ```
+
+ This encrypted public key can be decrypted by my another repo: [how-does-navicat-encrypt-password](https://github.com/DoubleLabyrinth/how-does-navicat-encrypt-password), while the key used is `b'23970790'`.
+
+ Example:
+
+ ```cmd
+ E:\GitHub>git clone https://github.com/DoubleLabyrinth/how-does-navicat-encrypt-password.git
+ ...
+ E:\GitHub>cd how-does-navicat-encrypt-password\python3
+ E:\GitHub\how-does-navicat-encrypt-password\python3>python
+ Python 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)] on win32
+ Type "help", "copyright", "credits" or "license" for more information.
+ >>> from NavicatCrypto import *
+ >>> cipher = Navicat11Crypto(b'23970790')
+ >>> print(cipher.DecryptString('D75125B70767B94145B47C1CB3C0755E7CCB8825C5DCE0C58ACF944E082801409A02472FAFFD1CD77864BB821AE36766FEEDE6A24F12662954168BFA314BD95032B9D82445355ED7BC0B880887D650F529158142E1CED09B9C2186BF71A70C0FE2F1E0AEF3BD6B75277AAB20DFAF3D110F75912BFB63AC50EC4C48689D1502715243A79F39FF2DE2BF15CE438FF885745ED54573850E8A9F40EE2FF505EB7476F95ADB783B28CA374FAC4632892AB82FB3BF4715FCFE6E82D03731FC3762B6AAC3DF1C3BC646FE9CD3C62663A97EE72DB932A301312B4A7633100C8CC357262C39A2B3A64B224F5276D5EDBDF0804DC3AC4B835162BB1969EAEBADC43D2511D6E023928781B167A48273B953378D3D2080CC06777E8A2364F0234B81064C5C739A8DA28DC5889072BF37685CBC94C2D31D0179AD86D8E3AA8090D4F0B281BE37E0143746E6049CCC06899401264FA471C016A96C79815B55BBC26B43052609D9D175FBCDE455392F10E51EC162F51CF732E6BB391F56BBFD8D957DF3D4C55B71CEFD54B19C16D458757373E698D7E693A8FC39815A8BF03BA05EA8C8778D38F9873D62B4460F41ACF997C30E7C3AF025FA171B5F5AD4D6B15E95C27F6B35AD61875E5505449B4E6767392933'))
+ -----BEGIN PUBLIC KEY-----
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I
+ qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv
+ a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF
+ R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2
+ WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt
+ YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ
+ awIDAQAB
+ -----END PUBLIC KEY-----
+ ```
+
+ __NOTICE:__
+
+ Start from __Navicat Premium 12.1.11__, Navicat do not load the public key through the method I talked before. Of course, the public key is still stored in `libcc.dll`. When Navicat starts, it encrypts the public key by an 8-bytes-long XOR key and stores the ciphertext in static area. When verifing __Activation Code__, Navicat will regenerate the 8-bytes-long XOR key and decrypts the ciphertext in static area to get the public key.
+
+ In `libcc.dll`, x64 version, you can find some instructions that looks like:
+
+ ```asm
+ xor eax, 'M'
+ mov byte_xxxxxx, al
+ ...
+ xor eax, 'I'
+ mov byte_xxxxxx, al
+ ...
+ xor eax, 'I'
+ mov byte_xxxxxx, al
+ ...
+ xor eax, 'B'
+ mov byte_xxxxxx, al
+ ...
+ xor eax, 'I'
+ mov byte_xxxxxx, al
+ ...
+ xor eax, 'j'
+ mov byte_xxxxxx, al
+ ...
+ ...
+ ```
+
+* __Request Code__
+
+ It is a Base64 string that represents 256-bytes-long data, while the 256-bytes-long data is the cipher text of __Offline Activation Request Information__ encrypted by __Navicat Activation Public Key__.
+
+* __Offline Activation Request Information__
+
+ It is just a JSON-style ASCII string which contains 3 items. They are `"K"`, `"DI"` and `"P"` respectively, which represent __snKey__, __DeviceIdentifier__ (related with your machine), __Platform__ (OS Type).
+
+ Like
+
+ ```
+ {"K": "xxxxxxxxxxxxxxxx", "DI": "yyyyyyyyyyyyy", "P": "WIN8"}
+ ```
+
+* __Activation Code__
+
+ It is a Base64 string that represents 256-bytes-long data, while the 256-bytes-long data is the cipher text of the __Offline Activation Response Information__ encrypted by __Navicat Activation Private Key__. So far, we don't know the official activation private key and we have to replace it in `navicat.exe` and `libcc.dll`.
+
+* __Offline Activation Response Information__
+
+ Just like __Offline Activation Request Information__, it is also a JSON-style ASCII string. But it contains 5 items. They are `"K"`, `"N"`, `"O"`, `"T"` and `"DI"` respectively.
+
+ `"K"` and `"DI"` has the same meaning that is mentioned in __Offline Activation Request Information__ and must be the same with the corresponding items in __Offline Activation Request Information__.
+
+ `"N"`, `"O"`, `"T"` represent __Name__, __Organization__, __Timestamp__ respectively. __Name__ and __Organization__ are UTF-8 strings and the type of __Timestamp__ can be string or integer. (Thanks for discoveries from @Wizr, issue #10)
+
+ `"T"` can be omitted.
+
+* __snKey__
+
+ It is a 4-block-long string, while every block is 4-chars-long.
+
+ __snKey__ is generated by 10-bytes-long data. In order to explain it easily, I use __uint8_t data[10]__ to represent the 10-bytes-long data.
+
+ 1. __data[0]__ and __data[1]__ must be `0x68` and `0x2A` respectively.
+
+ These two bytes are Naivcat signature number.
+
+ 2. __data[2]__, __data[3]__ and __data[4]__ can be any byte. Just set them whatever you want.
+
+ 3. __data[5]__ and __data[6]__ are product language signatures.
+
+ | Language | data[5] | data[6] | Discoverer |
+ |------------|:---------:|:---------:|-----------------|
+ | English | 0xAC | 0x88 | |
+ | 简体中文 | 0xCE | 0x32 | |
+ | 繁體中文 | 0xAA | 0x99 | |
+ | 日本語 | 0xAD | 0x82 | @dragonflylee |
+ | Polski | 0xBB | 0x55 | @dragonflylee |
+ | Español | 0xAE | 0x10 | @dragonflylee |
+ | Français | 0xFA | 0x20 | @Deltafox79 |
+ | Deutsch | 0xB1 | 0x60 | @dragonflylee |
+ | 한국어 | 0xB5 | 0x60 | @dragonflylee |
+ | Русский | 0xEE | 0x16 | @dragonflylee |
+ | Português | 0xCD | 0x49 | @dragonflylee |
+
+ 4. __data[7]__ is Navicat product ID. (Thanks @dragonflylee and @Deltafox79)
+
+ |Product Name |Enterprise|Standard|Educational|Essentials|
+ |----------------------|:--------:|:------:|:---------:|:--------:|
+ |Navicat Report Viewer |0x0B | | | |
+ |Navicat Data Modeler 3| |0x84 |0x85 | |
+ |Navicat Premium |0x65 | |0x66 |0x67 |
+ |Navicat MySQL |0x68 |0x69 |0x6A |0x6B |
+ |Navicat PostgreSQL |0x6C |0x6D |0x6E |0x6F |
+ |Navicat Oracle |0x70 |0x71 |0x72 |0x73 |
+ |Navicat SQL Server |0x74 |0x75 |0x76 |0x77 |
+ |Navicat SQLite |0x78 |0x79 |0x7A |0x7B |
+ |Navicat MariaDB |0x7C |0x7D |0x7E |0x7F |
+ |Navicat MongoDB |0x80 |0x81 |0x82 | |
+
+ 5. High 4 bits of __data[8]__ represents __major version number__.
+
+ Low 4 bits is unknown, but we can use it to delay activation deadline. Possible values are `0000` or `0001`.
+
+ __Example:__
+
+ For __Navicat 12 x64__: High 4 bits must be `1100`, which is the binary of number `12`.
+ For __Navicat 11 x64__: High 4 bits must be `1011`, which is the binary of number `11`.
+
+ 6. __data[9]__ is unknown, but you can set it by `0xFD`, `0xFC` or `0xFB` if you want to use __not-for-resale license__.
+
+ According to symbol information in __Navicat 12 for Mac x64__ version:
+
+ * `0xFB` is __Not-For-Resale-30-days__ license.
+ * `0xFC` is __Not-For-Resale-90-days__ license.
+ * `0xFD` is __Not-For-Resale-365-days__ license.
+ * `0xFE` is __Not-For-Resale__ license.
+ * `0xFF` is __Site__ license.
+
+ After `uint8_t data[10]` is ready, Navicat uses __DES__ with __ECB mode__ to encrypt the last 8 bytes of `uint8_t data[10]` which are from __data[2]__ to __data[9]__.
+
+ The DES key is:
+
+ ```cpp
+ unsigned char DESKey = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 };
+ ```
+
+ Then use Base32 to encode `uint8_t data[10]` whose encode table is
+
+ ```cpp
+ char EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+ ```
+
+ After encoding, you will get a 16-char-long string starting with `"NAV"`.
+
+ Finally, divide the 16-char-long string to four 4-chars-long blocks and join them with `"-"` then you will get __snKey__.
+
+## 2. Activation Process
+
+1. Check whether __snKey__ that user inputs is valid.
+
+2. After user clicks `Activate`, Navicat will start online activation first. If fails, user can choose offline activation.
+
+3. Navicat will use the __snKey__ that user inputs and some information collected from user's machine to generate __Offline Activation Request Information__. Then Navicat will encrypt it by __Navicat Activation Public Key__ and return a Base64-encoded string as __Request Code__.
+
+4. In legal way, the __Request Code__ should be sent to Navicat official activation server by a Internet-accessible computer. And Navicat official activation server will return a legal __Activation Code__.
+
+ But now, we use keygen to play the official activation server's role.
+
+ 1. According to the __Request Code__, get `"DI"` value and `"K"` value.
+
+ 2. Fill __Offline Activation Response Information__ with `"K"` value, name, organization name, `"DI"` value and, if need, `"T"` value.
+
+ 3. Encrypt __Offline Activation Response Information__ by __Navicat Activation Private Key__ and you will get 256-byte-long data.
+
+ 4. Encode the 256-byte-long data by Base64. The result is __Activation Code__.
+
+ 5. After user input __Activation Code__, offline activation is done successfully.
diff --git a/doc/how-does-it-work.zh-CN.md b/doc/how-does-it-work.zh-CN.md
new file mode 100644
index 0000000..e8567b0
--- /dev/null
+++ b/doc/how-does-it-work.zh-CN.md
@@ -0,0 +1,281 @@
+# Navicat Keygen - 注册机是怎么工作的?
+
+## 1. 关键词解释.
+
+* __Navicat激活公钥__
+
+ 这是一个2048位的RSA公钥,Navicat使用这个公钥来完成相关激活信息的加密和解密。
+
+ 这个公钥被作为 __RCData__ 类型的资源储存在 __navicat.exe__ 当中。资源名为`"ACTIVATIONPUBKEY"`。你可以使用一个叫[Resource Hacker](http://www.angusj.com/resourcehacker/)的软件来查看它。这个公钥的具体内容为:
+
+ ```
+ -----BEGIN PUBLIC KEY-----
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I
+ qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv
+ a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF
+ R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2
+ WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt
+ YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ
+ awIDAQAB
+ -----END PUBLIC KEY-----
+ ```
+
+ 如果您有相应的私钥并乐意公开的话欢迎联系我,我将非常感谢您的慷慨。
+
+ __注意:__
+
+ 从 __Navicat Premium 12.0.25__ 开始,Navicat不再从`navicat.exe`的资源中加载私钥。事实上,公钥转为从`libcc.dll`中加载,并且已经被加密。与此同时,为了防止被轻松地替换,加密的公钥被分到5个地方储存:
+
+ 以下内容是从 __Navicat Premium x64 12.0.25 简体中文版__ 的`libcc.dll`中发现的,`libcc.dll`的SHA256值为`607e0a84c75966b00f3d12fa833e91d159e4f51ac51b6ba66f98d0c3cbefdce0`。我不保证在Navicat的其他版本中相关偏移量和下述的相同,但相关的 __字符串__ 以及 __立即数__ 是很可能找得到的。
+
+ 1. 在`libcc.dll`中,文件偏移量`+0x01A12090`的地方,储存了加密公钥的第一部分,以 __字符串__ 的形式储存:
+
+ ```
+ "D75125B70767B94145B47C1CB3C0755E
+ 7CCB8825C5DCE0C58ACF944E08280140
+ 9A02472FAFFD1CD77864BB821AE36766
+ FEEDE6A24F12662954168BFA314BD950
+ 32B9D82445355ED7BC0B880887D650F5"
+ ```
+
+ 2. 在`libcc.dll`中,文件偏移量`+0x0059D799`的地方,储存了加密公钥的第二部分,以 __立即数__ 的形式储存在一条指令中:
+
+ ```
+ 0xFE 0xEA 0xBC 0x01
+ ```
+
+ 相应的十进制值为: `29158142`
+
+ 3. 在`libcc.dll`中,文件偏移量`+0x01A11DA0`的地方,储存了加密公钥的第三部分,以 __字符串__ 的形式储存:
+
+ ```
+ "E1CED09B9C2186BF71A70C0FE2F1E0AE
+ F3BD6B75277AAB20DFAF3D110F75912B
+ FB63AC50EC4C48689D1502715243A79F
+ 39FF2DE2BF15CE438FF885745ED54573
+ 850E8A9F40EE2FF505EB7476F95ADB78
+ 3B28CA374FAC4632892AB82FB3BF4715
+ FCFE6E82D03731FC3762B6AAC3DF1C3B
+ C646FE9CD3C62663A97EE72DB932A301
+ 312B4A7633100C8CC357262C39A2B3A6
+ 4B224F5276D5EDBDF0804DC3AC4B8351
+ 62BB1969EAEBADC43D2511D6E0239287
+ 81B167A48273B953378D3D2080CC0677
+ 7E8A2364F0234B81064C5C739A8DA28D
+ C5889072BF37685CBC94C2D31D0179AD
+ 86D8E3AA8090D4F0B281BE37E0143746
+ E6049CCC06899401264FA471C016A96C
+ 79815B55BBC26B43052609D9D175FBCD
+ E455392F10E51EC162F51CF732E6BB39
+ 1F56BBFD8D957DF3D4C55B71CEFD54B1
+ 9C16D458757373E698D7E693A8FC3981
+ 5A8BF03BA05EA8C8778D38F9873D62B4
+ 460F41ACF997C30E7C3AF025FA171B5F
+ 5AD4D6B15E95C27F6B35AD61875E5505
+ 449B4E"
+ ```
+
+ 4. 在`libcc.dll`中,文件偏移量`+0x0059D77F`的地方,储存了加密公钥的第四部分,以 __立即数__ 的形式储存在一条指令中:
+
+ ```
+ 0x59 0x08 0x01 0x00
+ ```
+
+ 相应的十进制值为: `67673`
+
+ 5. 在`libcc.dll`中,文件偏移量`+0x01A11D8C`的地方,储存了加密公钥的第五部分,以 __字符串__ 的形式储存:
+
+ ```
+ "92933"
+ ```
+
+ 这五部分按照`"%s%d%s%d%s"`的形式输出则为加密的公钥,顺序和上述的顺序相同,具体的输出为:
+
+ ```
+
+ D75125B70767B94145B47C1CB3C0755E7CCB8825C5DCE0C58ACF944E082801409A02472FAFFD1CD77864BB821AE36766FEEDE6A24F12662954168BFA314BD95032B9D82445355ED7BC0B880887D650F529158142E1CED09B9C2186BF71A70C0FE2F1E0AEF3BD6B75277AAB20DFAF3D110F75912BFB63AC50EC4C48689D1502715243A79F39FF2DE2BF15CE438FF885745ED54573850E8A9F40EE2FF505EB7476F95ADB783B28CA374FAC4632892AB82FB3BF4715FCFE6E82D03731FC3762B6AAC3DF1C3BC646FE9CD3C62663A97EE72DB932A301312B4A7633100C8CC357262C39A2B3A64B224F5276D5EDBDF0804DC3AC4B835162BB1969EAEBADC43D2511D6E023928781B167A48273B953378D3D2080CC06777E8A2364F0234B81064C5C739A8DA28DC5889072BF37685CBC94C2D31D0179AD86D8E3AA8090D4F0B281BE37E0143746E6049CCC06899401264FA471C016A96C79815B55BBC26B43052609D9D175FBCDE455392F10E51EC162F51CF732E6BB391F56BBFD8D957DF3D4C55B71CEFD54B19C16D458757373E698D7E693A8FC39815A8BF03BA05EA8C8778D38F9873D62B4460F41ACF997C30E7C3AF025FA171B5F5AD4D6B15E95C27F6B35AD61875E5505449B4E6767392933
+
+ ```
+
+ 这个加密的公钥可以用我的另外一个repo([how-does-navicat-encrypt-password](https://github.com/DoubleLabyrinth/how-does-navicat-encrypt-password))解密,其中密钥为`b'23970790'`。
+
+ 例如:
+
+ ```cmd
+ E:\GitHub>git clone https://github.com/DoubleLabyrinth/how-does-navicat-encrypt-password.git
+ ...
+ E:\GitHub>cd how-does-navicat-encrypt-password\python3
+ E:\GitHub\how-does-navicat-encrypt-password\python3>python
+ Python 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)] on win32
+ Type "help", "copyright", "credits" or "license" for more information.
+ >>> from NavicatCrypto import *
+ >>> cipher = Navicat11Crypto(b'23970790')
+ >>> print(cipher.DecryptString('D75125B70767B94145B47C1CB3C0755E7CCB8825C5DCE0C58ACF944E082801409A02472FAFFD1CD77864BB821AE36766FEEDE6A24F12662954168BFA314BD95032B9D82445355ED7BC0B880887D650F529158142E1CED09B9C2186BF71A70C0FE2F1E0AEF3BD6B75277AAB20DFAF3D110F75912BFB63AC50EC4C48689D1502715243A79F39FF2DE2BF15CE438FF885745ED54573850E8A9F40EE2FF505EB7476F95ADB783B28CA374FAC4632892AB82FB3BF4715FCFE6E82D03731FC3762B6AAC3DF1C3BC646FE9CD3C62663A97EE72DB932A301312B4A7633100C8CC357262C39A2B3A64B224F5276D5EDBDF0804DC3AC4B835162BB1969EAEBADC43D2511D6E023928781B167A48273B953378D3D2080CC06777E8A2364F0234B81064C5C739A8DA28DC5889072BF37685CBC94C2D31D0179AD86D8E3AA8090D4F0B281BE37E0143746E6049CCC06899401264FA471C016A96C79815B55BBC26B43052609D9D175FBCDE455392F10E51EC162F51CF732E6BB391F56BBFD8D957DF3D4C55B71CEFD54B19C16D458757373E698D7E693A8FC39815A8BF03BA05EA8C8778D38F9873D62B4460F41ACF997C30E7C3AF025FA171B5F5AD4D6B15E95C27F6B35AD61875E5505449B4E6767392933'))
+ -----BEGIN PUBLIC KEY-----
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I
+ qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv
+ a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF
+ R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2
+ WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt
+ YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ
+ awIDAQAB
+ -----END PUBLIC KEY-----
+ ```
+
+ __注意:__
+
+ 从 __Navicat Premium 12.1.11__ 开始,Navicat不再用上面说的方法加载密钥。当然密钥还是储存在`libcc.dll`文件中。当Navicat启动时,它会用8字节长的XOR密钥来加密公钥,并储存到一个静态数据区中。当验证 __激活码__ 时,Navicat会重新生成一样的8字节XOR密钥,并解密在静态储存区中的密文,从而获取公钥。
+
+ 在`libcc.dll`,x64版本中,你会看到如下的几条指令:
+
+ ```asm
+ xor eax, 'M'
+ mov byte_xxxxxx, al
+ ...
+ xor eax, 'I'
+ mov byte_xxxxxx, al
+ ...
+ xor eax, 'I'
+ mov byte_xxxxxx, al
+ ...
+ xor eax, 'B'
+ mov byte_xxxxxx, al
+ ...
+ xor eax, 'I'
+ mov byte_xxxxxx, al
+ ...
+ xor eax, 'j'
+ mov byte_xxxxxx, al
+ ...
+ ...
+ ```
+
+* __请求码__
+
+ 这是一个Base64编码的字符串,代表的是长度为256字节的数据。这256字节的数据是 __离线激活信息__ 用 __Navicat激活公钥__ 加密的密文。
+
+* __离线激活请求信息__
+
+ 这是一个JSON风格的字符串。它包含了3个Key:`"K"`、`"DI"`和`"P"`,分别代表 __序列号__、__设备识别码__(与你的电脑硬件信息相关)和 __平台__ (其实就是操作系统类型)。
+
+ 例如:
+
+ ```
+ {"K": "xxxxxxxxxxxxxxxx", "DI": "yyyyyyyyyyyyy", "P": "WIN8"}
+ ```
+
+* __激活码__
+
+ 这是一个Base64编码的字符串,代表的是长度为256字节的数据。这256字节的数据是 __离线激活回复信息__ 用 __Navicat激活私钥__ 加密的密文。目前我们不知道官方的 __Navicat激活私钥__,所以我们得替换掉软件里的公钥。
+
+* __离线激活回复信息__
+
+ 和 __离线激活请求信息__ 一样,它也是一个JSON风格的字符串。但是它包含5个Key,分别为`"K"`、`"N"`、`"O"`、`"T"` 和 `"DI"`.
+
+ `"K"` 和 `"DI"` 的意义与 __离线激活请求信息__ 中的相同,且Value必须与 __离线激活请求信息__ 中的相同。
+
+ `"N"`、`"O"`、`"T"` 分别代表 __注册名__、__组织__、__授权时间__。
+
+ __注册名__ 和 __组织__ 的值类型为UTF-8编码的字符串。__授权时间__ 的值类型可以为字符串或整数(感谢@Wizr在issue #10中的报告)。
+
+ `"T"` 可以被省略。
+
+* __序列号__
+
+ 这是一个被分为了4个部分的字符串,其中每个部分都是4个字符长。
+
+ __序列号__ 是通过10个字节的数据来生成的。为了表达方便,我用 __uint8_t data[10]__ 来表示这10个字节。
+
+ 1. __data[0]__ 和 __data[1]__ 必须分别为 `0x68` 和 `0x2A`。
+
+ 这两个字节为Navicat的标志数。
+
+ 2. __data[2]__、__data[3]__ 和 __data[4]__ 可以是任意字节,你想设成什么都行。
+
+ 3. __data[5]__ 和 __data[6]__ 是Navicat的语言标志,值如下:
+
+ | 语言类型 | data[5] | data[6] | 发现者 |
+ |------------|:---------:|:---------:|-----------------|
+ | English | 0xAC | 0x88 | |
+ | 简体中文 | 0xCE | 0x32 | |
+ | 繁體中文 | 0xAA | 0x99 | |
+ | 日本語 | 0xAD | 0x82 | @dragonflylee |
+ | Polski | 0xBB | 0x55 | @dragonflylee |
+ | Español | 0xAE | 0x10 | @dragonflylee |
+ | Français | 0xFA | 0x20 | @Deltafox79 |
+ | Deutsch | 0xB1 | 0x60 | @dragonflylee |
+ | 한국어 | 0xB5 | 0x60 | @dragonflylee |
+ | Русский | 0xEE | 0x16 | @dragonflylee |
+ | Português | 0xCD | 0x49 | @dragonflylee |
+
+ 4. __data[7]__ 是Navicat产品ID。(感谢 @dragonflylee 和 @Deltafox79提供的数据)
+
+ |产品名 |Enterprise|Standard|Educational|Essentials|
+ |----------------------|:--------:|:------:|:---------:|:--------:|
+ |Navicat Report Viewer |0x0B | | | |
+ |Navicat Data Modeler 3| |0x84 |0x85 | |
+ |Navicat Premium |0x65 | |0x66 |0x67 |
+ |Navicat MySQL |0x68 |0x69 |0x6A |0x6B |
+ |Navicat PostgreSQL |0x6C |0x6D |0x6E |0x6F |
+ |Navicat Oracle |0x70 |0x71 |0x72 |0x73 |
+ |Navicat SQL Server |0x74 |0x75 |0x76 |0x77 |
+ |Navicat SQLite |0x78 |0x79 |0x7A |0x7B |
+ |Navicat MariaDB |0x7C |0x7D |0x7E |0x7F |
+ |Navicat MongoDB |0x80 |0x81 |0x82 | |
+
+ 5. __data[8]__ 的高4位代表 __版本号__。低4位未知,但可以用来延长激活期限,可取的值有`0000`和`0001`。
+
+ 例如:
+
+ 对于 __Navicat 12__: 高4位必须是`1100`,为`12`的二进制形式。
+ 对于 __Navicat 11__: 高4位必须是`1011`,为`11`的二进制形式。
+
+ 6. __data[9]__ 目前暂未知,但如果你想要 __not-for-resale license__ 的话可以设成`0xFD`、`0xFC`或`0xFB`。
+
+ 根据 __Navicat 12 for Mac x64__ 版本残留的符号信息可知:
+
+ * `0xFB`是 __Not-For-Resale-30-days__ license.
+ * `0xFC`是 __Not-For-Resale-90-days__ license.
+ * `0xFD`是 __Not-For-Resale-365-days__ license.
+ * `0xFE`是 __Not-For-Resale__ license.
+ * `0xFF`是 __Site__ license.
+
+ 之后Navicat使用 __ECB__ 模式的 __DES__ 算法来加密 __data[10]__ 的后8字节,也就是 __data[2]__ 到 __data[9]__ 的部分。
+
+ 相应的DES密钥为:
+
+ ```cpp
+ unsigned char DESKey = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 };
+ ```
+
+ 之后使用Base32编码 __data[10]__,其中编码表改为:
+
+ ```cpp
+ char EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+ ```
+
+ 编码之后你应该会得到一个16字节长的字符串,并且以"NAV"打头。
+
+ 将16字节的字符串分成4个4字节的小块,然后用`"-"`连接就可以得到 __序列号__。
+
+## 2. 激活过程
+
+ 1. 检查用户输入的 __序列号__ 是否合法。
+
+ 2. 在用户点击了`激活`按钮之后,Navicat会先尝试在线激活。如果失败,用户可以选择离线激活。
+
+ 3. Navicat会使用用户输入的 __序列号__ 以及从用户电脑收集来的信息生成 __离线激活请求信息__,然后用 __Navicat激活公钥__ 加密,并将密文用Base64编码,最后得到 __请求码__。
+
+ 4. 正常流程下,__请求码__ 应该通过可联网的电脑发送给Navicat的官方激活服务器。之后Navicat的官方激活服务器会返回一个合法的 __激活码__。
+
+ 但现在我们使用注册机来扮演官方激活服务器的角色,只是Navicat软件里的激活公钥得换成自己的公钥:
+
+ 1. 根据 __请求码__, 获得`"DI"`值和`"K"`值。
+
+ 2. 用`"K"`值、用户名、组织名和`"DI"`值填写 __离线激活回复信息__。
+
+ 3. 用自己的2048位RSA私钥加密 __离线激活回复信息__,你将会得到256字节的密文。
+
+ 4. 用Base64编码这256字节的密文,就可以得到 __激活码__。
+
+ 5. 在Navicat软件中填入 __激活码__ 即可完成离线激活。
diff --git a/doc/how-to-build.md b/doc/how-to-build.md
new file mode 100644
index 0000000..9da6758
--- /dev/null
+++ b/doc/how-to-build.md
@@ -0,0 +1,43 @@
+# Navicat Keygen - How to build?
+
+[中文版](how-to-build.zh-CN.md)
+
+## 1. Prerequisites
+
+1. Please make sure you have installed following libraries:
+
+ * `capstone`
+ * `keystone`
+ * `rapidjson`
+
+ If you use Ubuntu, you can install them by:
+
+ ```console
+ # install capstone
+ $ sudo apt-get install libcapstone-dev
+
+ # install keystone
+ $ sudo apt-get install cmake
+ $ git clone https://github.com/keystone-engine/keystone.git
+ $ cd keystone
+ $ mkdir build
+ $ cd build
+ $ ../make-share.sh
+ $ sudo make install
+ $ sudo ldconfig
+
+ # install rapidjson
+ $ sudo apt-get install rapidjson-dev
+ ```
+
+2. Your gcc supports C++17 feature.
+
+## 2. Build
+
+```console
+$ git clone -b linux --single-branch https://github.com/DoubleLabyrinth/navicat-keygen.git
+$ cd navicat-keygen
+$ make all
+```
+
+You will see executable files in `bin/` directory.
diff --git a/doc/how-to-build.zh-CN.md b/doc/how-to-build.zh-CN.md
new file mode 100644
index 0000000..e19be62
--- /dev/null
+++ b/doc/how-to-build.zh-CN.md
@@ -0,0 +1,41 @@
+# Navicat Keygen - 如何编译?
+
+## 1. 前提条件
+
+1. 请确保你安装了下面几个库:
+
+ * `capstone`
+ * `keystone`
+ * `rapidjson`
+
+ 你可以通过下面的命令来安装它们:
+
+ ```console
+ # install capstone
+ $ sudo apt-get install libcapstone-dev
+
+ # install keystone
+ $ sudo apt-get install cmake
+ $ git clone https://github.com/keystone-engine/keystone.git
+ $ cd keystone
+ $ mkdir build
+ $ cd build
+ $ ../make-share.sh
+ $ sudo make install
+ $ sudo ldconfig
+
+ # install rapidjson
+ $ sudo apt-get install rapidjson-dev
+ ```
+
+2. 你的gcc支持C++17特性。
+
+## 2. 编译
+
+```console
+$ git clone -b linux --single-branch https://github.com/DoubleLabyrinth/navicat-keygen.git
+$ cd navicat-keygen
+$ make all
+```
+
+生成完成后,你会在 `bin/` 文件夹下看到编译后的keygen/patcher。
diff --git a/navicat-keygen/Base32.hpp b/navicat-keygen/Base32.hpp
new file mode 100644
index 0000000..cdaeaee
--- /dev/null
+++ b/navicat-keygen/Base32.hpp
@@ -0,0 +1,134 @@
+#pragma once
+#include
+#include
+#include
+
+[[nodiscard]]
+inline std::string base32_encode(const void* lpBinary, size_t cbBinary) {
+ static const std::string::value_type Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+ static constexpr std::string::value_type PaddingChar = '=';
+
+ std::string szBase32;
+
+ if (auto pbBinary = reinterpret_cast(lpBinary); cbBinary) {
+ szBase32.reserve((cbBinary * 8 + 4) / 5);
+
+ uint8_t Idx = 0;
+ uint8_t BitsLeft = 8;
+ for (size_t i = 0; i < cbBinary;) {
+ if (BitsLeft < 5) {
+ Idx = pbBinary[i] << (5 - BitsLeft);
+
+ ++i;
+ if (i != cbBinary) {
+ Idx |= pbBinary[i] >> (3 + BitsLeft);
+ }
+
+ Idx &= 0x1F;
+ BitsLeft += 3;
+ } else {
+ Idx = pbBinary[i] >> (BitsLeft - 5);
+
+ Idx &= 0x1F;
+ BitsLeft -= 5;
+ }
+
+ szBase32.append(1, Alphabet[Idx]);
+
+ if (BitsLeft == 0) {
+ BitsLeft = 8;
+ ++i;
+ }
+ }
+
+ if (szBase32.length() % 8) {
+ size_t Padding = 8 - szBase32.length() % 8;
+ szBase32.append(Padding, PaddingChar);
+ }
+ }
+
+ return szBase32;
+}
+
+[[nodiscard]]
+inline std::string base32_encode(const std::vector& Binary) {
+ return base32_encode(Binary.data(), Binary.size());
+}
+
+[[nodiscard]]
+inline std::string base32_encode(const std::initializer_list& Binary) {
+ return base32_encode(Binary.begin(), Binary.size());
+}
+
+[[nodiscard]]
+inline std::vector base32_decode(std::string_view szBase32) {
+ static constexpr std::string::value_type PaddingChar = '=';
+
+ std::vector Binary;
+
+ if (szBase32.length()) {
+ Binary.reserve((szBase32.length() * 5 + 7) / 8);
+
+ uint8_t Byte = 0;
+ uint8_t BitsNeed = 8;
+ for (size_t i = 0; i < szBase32.length(); ++i) {
+ uint8_t Idx;
+ if ('A' <= szBase32[i] && szBase32[i] <= 'Z') {
+ Idx = szBase32[i] - 'A';
+ } else if ('a' <= szBase32[i] && szBase32[i] <= 'z') {
+ Idx = szBase32[i] - 'a';
+ } else if ('2' <= szBase32[i] && szBase32[i] <= '7') {
+ Idx = szBase32[i] - '2' + 26;
+ } else if (szBase32[i] == PaddingChar) {
+ for (size_t j = i + 1; j < szBase32.length(); ++j) {
+ if (szBase32[j] != PaddingChar) {
+ throw std::invalid_argument("base32_decode: invalid padding schema.");
+ }
+ }
+
+ break;
+ } else {
+ throw std::invalid_argument("base32_decode: non-Base32 character is detected.");
+ }
+
+ if (BitsNeed >= 5) {
+ Byte |= Idx;
+
+ BitsNeed -= 5;
+ Byte <<= BitsNeed;
+ } else {
+ Byte |= Idx >> (5 - BitsNeed);
+ Binary.push_back(Byte);
+
+ BitsNeed += 3;
+ Byte = Idx << BitsNeed;
+ if (BitsNeed > 5) {
+ Byte >>= BitsNeed - 5;
+ }
+ }
+ }
+
+ switch (BitsNeed) {
+ case 1:
+ case 2:
+ case 3:
+ throw std::invalid_argument("base32_decode: base32 string is corrupted.");
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ if (Byte != 0) {
+ throw std::invalid_argument("base32_decode: base32 string is corrupted.");
+ }
+ break;
+ case 0:
+ case 8:
+ break;
+ default:
+ __builtin_unreachable();
+ }
+ }
+
+ return Binary;
+}
+
diff --git a/navicat-keygen/Base64.hpp b/navicat-keygen/Base64.hpp
new file mode 100644
index 0000000..7ce0359
--- /dev/null
+++ b/navicat-keygen/Base64.hpp
@@ -0,0 +1,134 @@
+#pragma once
+#include
+#include
+#include
+
+[[nodiscard]]
+inline std::string base64_encode(const void* lpBinary, size_t cbBinary) {
+ static const std::string::value_type Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ static constexpr std::string::value_type PaddingChar = '=';
+
+ std::string szBase64;
+
+ if (auto pbBinary = reinterpret_cast(lpBinary); cbBinary) {
+ szBase64.reserve((cbBinary * 8 + 5) / 6);
+
+ uint8_t Idx = 0;
+ uint8_t BitsLeft = 8;
+ for (size_t i = 0; i < cbBinary;) {
+ if (BitsLeft < 6) {
+ Idx = pbBinary[i] << (6 - BitsLeft);
+
+ ++i;
+ if (i != cbBinary) {
+ Idx |= pbBinary[i] >> (2 + BitsLeft);
+ }
+
+ Idx &= 0x3F;
+ BitsLeft += 2;
+ } else {
+ Idx = pbBinary[i] >> (BitsLeft - 6);
+
+ Idx &= 0x3F;
+ BitsLeft -= 6;
+ }
+
+ szBase64.append(1, Alphabet[Idx]);
+
+ if (BitsLeft == 0) {
+ BitsLeft = 8;
+ ++i;
+ }
+ }
+
+ if (szBase64.length() % 4) {
+ size_t Padding = 4 - szBase64.length() % 4;
+ szBase64.append(Padding, PaddingChar);
+ }
+ }
+
+ return szBase64;
+}
+
+[[nodiscard]]
+inline std::string base64_encode(const std::vector& Binary) {
+ return base64_encode(Binary.data(), Binary.size());
+}
+
+[[nodiscard]]
+inline std::string base64_encode(const std::initializer_list& Binary) {
+ return base64_encode(Binary.begin(), Binary.size());
+}
+
+[[nodiscard]]
+inline std::vector base64_decode(std::string_view szBase64) {
+ static constexpr std::string::value_type PaddingChar = '=';
+
+ std::vector Binary;
+
+ if (szBase64.length()) {
+ Binary.reserve((szBase64.length() * 6 + 7) / 8);
+
+ uint8_t Byte = 0;
+ uint8_t BitsNeed = 8;
+ for (size_t i = 0; i < szBase64.length(); ++i) {
+ uint8_t Idx;
+ if ('A' <= szBase64[i] && szBase64[i] <= 'Z') {
+ Idx = szBase64[i] - 'A';
+ } else if ('a' <= szBase64[i] && szBase64[i] <= 'z') {
+ Idx = szBase64[i] - 'a' + 26;
+ } else if ('0' <= szBase64[i] && szBase64[i] <= '9') {
+ Idx = szBase64[i] - '0' + 26 + 26;
+ } else if (szBase64[i] == '+') {
+ Idx = 26 + 26 + 10;
+ } else if (szBase64[i] == '/') {
+ Idx = 26 + 26 + 10 + 1;
+ } else if (szBase64[i] == PaddingChar) {
+ for (size_t j = i + 1; j < szBase64.length(); ++j) {
+ if (szBase64[j] != PaddingChar) {
+ throw std::invalid_argument("base64_decode: invalid padding schema.");
+ }
+ }
+
+ break;
+ } else {
+ throw std::invalid_argument("base64_decode: non-Base64 character is detected.");
+ }
+
+ if (BitsNeed >= 6) {
+ Byte |= Idx;
+
+ BitsNeed -= 6;
+ Byte <<= BitsNeed;
+ } else {
+ Byte |= Idx >> (6 - BitsNeed);
+ Binary.push_back(Byte);
+
+ BitsNeed += 2;
+ Byte = Idx << BitsNeed;
+ if (BitsNeed > 6) {
+ Byte >>= BitsNeed - 6;
+ }
+ }
+ }
+
+ switch (BitsNeed) {
+ case 2:
+ throw std::invalid_argument("base64_decode: base64 string is corrupted.");
+ case 4:
+ case 6:
+ if (Byte != 0) {
+ throw std::invalid_argument("base64_decode: base64 string is corrupted.");
+ }
+ break;
+ case 0:
+ case 8:
+ break;
+ default:
+ __builtin_unreachable();
+ }
+ }
+
+ return Binary;
+}
+
diff --git a/navicat-keygen/CollectInformation.cpp b/navicat-keygen/CollectInformation.cpp
new file mode 100644
index 0000000..fd6b43e
--- /dev/null
+++ b/navicat-keygen/CollectInformation.cpp
@@ -0,0 +1,137 @@
+#include "SerialNumberGenerator.hpp"
+#include "ExceptionGeneric.hpp"
+#include
+
+namespace nkg {
+
+ [[nodiscard]]
+ static int ReadInt(int MinVal, int MaxVal, const char* lpszPrompt, const char* lpszErrorMessage) {
+ int val;
+ std::string s;
+ while (true) {
+ std::cout << lpszPrompt;
+ if (!std::getline(std::cin, s)) {
+ throw ARL::EOFError(__BASE_FILE__, __LINE__, "Abort.");
+ }
+
+ if (s.empty())
+ continue;
+
+ try {
+ val = std::stoi(s, nullptr, 0);
+ if (MinVal <= val && val <= MaxVal) {
+ return val;
+ } else {
+ throw std::invalid_argument("Out of range.");
+ }
+ } catch (std::invalid_argument&) {
+ std::cout << lpszErrorMessage << std::endl;
+ }
+ }
+ }
+
+ [[nodiscard]]
+ static int ReadInt(int MinVal, int MaxVal, int DefaultVal, const char* lpszPrompt, const char* lpszErrorMessage) {
+ int val;
+ std::string s;
+ while (true) {
+ std::cout << lpszPrompt;
+ if (!std::getline(std::cin, s)) {
+ throw ARL::EOFError(__BASE_FILE__, __LINE__, "Abort.");
+ }
+
+ if (s.empty()) {
+ return DefaultVal;
+ }
+
+ try {
+ val = std::stoi(s, nullptr, 0);
+ if (MinVal <= val && val <= MaxVal) {
+ return val;
+ } else {
+ throw std::invalid_argument("Out of range.");
+ }
+ } catch (std::invalid_argument&) {
+ std::cout << lpszErrorMessage << std::endl;
+ }
+ }
+ }
+
+ [[nodiscard]]
+ SerialNumberGenerator CollectInformationNormal() {
+ SerialNumberGenerator Generator;
+
+ std::cout << "[*] Select Navicat product:" << std::endl;
+ std::cout << " 0. DataModeler" << std::endl;
+ std::cout << " 1. Premium" << std::endl;
+ std::cout << " 2. MySQL" << std::endl;
+ std::cout << " 3. PostgreSQL" << std::endl;
+ std::cout << " 4. Oracle" << std::endl;
+ std::cout << " 5. SQLServer" << std::endl;
+ std::cout << " 6. SQLite" << std::endl;
+ std::cout << " 7. MariaDB" << std::endl;
+ std::cout << " 8. MongoDB" << std::endl;
+ std::cout << " 9. ReportViewer" << std::endl;
+ std::cout << std::endl;
+ Generator.SetProductSignature(
+ static_cast(ReadInt(0, 9, "(Input index)> ", "Invalid index."))
+ );
+
+ std::cout << std::endl;
+ std::cout << "[*] Select product language:" << std::endl;
+ std::cout << " 0. English" << std::endl;
+ std::cout << " 1. Simplified Chinese" << std::endl;
+ std::cout << " 2. Traditional Chinese" << std::endl;
+ std::cout << " 3. Japanese" << std::endl;
+ std::cout << " 4. Polish" << std::endl;
+ std::cout << " 5. Spanish" << std::endl;
+ std::cout << " 6. French" << std::endl;
+ std::cout << " 7. German" << std::endl;
+ std::cout << " 8. Korean" << std::endl;
+ std::cout << " 9. Russian" << std::endl;
+ std::cout << " 10. Portuguese" << std::endl;
+ std::cout << std::endl;
+ Generator.SetLanguageSignature(
+ static_cast(ReadInt(0, 10, "(Input index)> ", "Invalid index."))
+ );
+
+ std::cout << std::endl;
+ std::cout << "[*] Input major version number:" << std::endl;
+ Generator.SetVersion(
+ static_cast(ReadInt(0, 15, 12, "(range: 0 ~ 15, default: 12)> ", "Invalid number."))
+ );
+
+ std::cout << std::endl;
+ return Generator;
+ }
+
+ [[nodiscard]]
+ SerialNumberGenerator CollectInformationAdvanced() {
+ SerialNumberGenerator Generator;
+
+ std::cout << "[*] Navicat Product Signature:" << std::endl;
+ Generator.SetProductSignature(
+ static_cast(ReadInt(0x00, 0xff, "(range: 0x00 ~ 0xFF)> ", "Invalid number."))
+ );
+
+ std::cout << std::endl;
+ std::cout << "[*] Navicat Language Signature 0:" << std::endl;
+ auto s1 = static_cast(ReadInt(0x00, 0xff, "(range: 0x00 ~ 0xFF)> ", "Invalid number."));
+
+ std::cout << std::endl;
+ std::cout << "[*] Navicat Language Signature 1:" << std::endl;
+ auto s2 = static_cast(ReadInt(0x00, 0xff, "(range: 0x00 ~ 0xFF)> ", "Invalid number."));
+
+ Generator.SetLanguageSignature(s1, s2);
+
+ std::cout << std::endl;
+ std::cout << "[*] Input major version number:" << std::endl;
+ Generator.SetVersion(
+ static_cast(ReadInt(0, 15, 12, "(range: 0 ~ 15, default: 12)> ", "Invalid number."))
+ );
+
+ std::cout << std::endl;
+ return Generator;
+ }
+}
+
diff --git a/navicat-keygen/GenerateLicense.cpp b/navicat-keygen/GenerateLicense.cpp
new file mode 100644
index 0000000..3408b7d
--- /dev/null
+++ b/navicat-keygen/GenerateLicense.cpp
@@ -0,0 +1,205 @@
+#include "Exception.hpp"
+#include "ExceptionGeneric.hpp"
+#include "ResourceWrapper.hpp"
+#include "ResourceTraitsOpenssl.hpp"
+#include "RSACipher.hpp"
+#include "Base64.hpp"
+#include "SerialNumberGenerator.hpp"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace nkg {
+
+ void GenerateLicenseText(const RSACipher& Cipher, const SerialNumberGenerator& Generator) {
+ std::string utf8username;
+ std::string utf8organization;
+
+ std::string b64RequestCode;
+ std::vector RequestCode;
+ std::string utf8RequestInfo;
+ std::string utf8ResponseInfo;
+ std::vector ResponseCode;
+ std::string b64ResponseCode;
+
+ std::cout << "[*] Your name: ";
+ if (!std::getline(std::cin, utf8username)) {
+ throw ARL::EOFError(__BASE_FILE__, __LINE__, "Abort.");
+ }
+
+ std::cout << "[*] Your organization: ";
+ if (!std::getline(std::cin, utf8organization)) {
+ throw ARL::EOFError(__BASE_FILE__, __LINE__, "Abort.");
+ }
+
+ std::cout << std::endl;
+
+ std::cout << "[*] Input request code in Base64: (Double press ENTER to end)" << std::endl;
+ while (true) {
+ std::string temp;
+ if (!std::getline(std::cin, temp)) {
+ throw ARL::EOFError(__BASE_FILE__, __LINE__, "Abort.");
+ }
+
+ if (temp.empty()) {
+ break;
+ }
+
+ b64RequestCode.append(temp);
+ }
+
+ RequestCode = base64_decode(b64RequestCode);
+ if (RequestCode.size() != 256) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Broken request code. %zu", RequestCode.size());
+ }
+
+ utf8RequestInfo.resize((Cipher.Bits() + 7) / 8);
+ Cipher.Decrypt(RequestCode.data(), RequestCode.size(), utf8RequestInfo.data(), RSA_PKCS1_PADDING);
+ while (utf8RequestInfo.back() == '\x00') {
+ utf8RequestInfo.pop_back();
+ }
+
+ std::cout << "[*] Request Info:" << std::endl;
+ std::cout << utf8RequestInfo << std::endl;
+ std::cout << std::endl;
+
+ rapidjson::Document json;
+ rapidjson::Value N_Key;
+ rapidjson::Value N_Value;
+ rapidjson::Value O_Key;
+ rapidjson::Value O_Value;
+ rapidjson::Value T_Key;
+ rapidjson::Value T_Value;
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer writer(buffer);
+
+ //
+ // Begin to parse
+ //
+ json.Parse(utf8RequestInfo.c_str());
+ //
+ // Remove "Platform" info
+ //
+ json.RemoveMember("P");
+ //
+ // Set "Name" info
+ //
+ N_Key.SetString("N", 1);
+ N_Value.SetString(utf8username.c_str(), static_cast(utf8username.length()));
+ //
+ // Set "Organization" info
+ //
+ O_Key.SetString("O", 1);
+ O_Value.SetString(utf8organization.c_str(), static_cast(utf8organization.length()));
+ //
+ // Set "Time" info
+ //
+ T_Key.SetString("T", 1);
+ T_Value.SetUint(static_cast(std::time(nullptr)));
+ //
+ // Add "Name", "Organization" and "Time"
+ //
+ json.AddMember(N_Key, N_Value, json.GetAllocator());
+ json.AddMember(O_Key, O_Value, json.GetAllocator());
+ json.AddMember(T_Key, T_Value, json.GetAllocator());
+
+ json.Accept(writer);
+ if (buffer.GetSize() > 240) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "Response info is too long.");
+ }
+
+ utf8ResponseInfo.assign(buffer.GetString(), buffer.GetSize());
+
+ std::cout << "[*] Response Info:" << std::endl;
+ std::cout << utf8ResponseInfo << std::endl;
+ std::cout << std::endl;
+
+ ResponseCode.resize((Cipher.Bits() + 7) / 8);
+ Cipher.Encrypt(utf8ResponseInfo.data(), utf8ResponseInfo.size(), ResponseCode.data(), RSA_PKCS1_PADDING);
+
+ b64ResponseCode = base64_encode(ResponseCode);
+
+ std::cout << "[*] Activation Code:" << std::endl;
+ std::cout << b64ResponseCode << std::endl;
+ std::cout << std::endl;
+ }
+
+ void GenerateLicenseBinary(const RSACipher& Cipher, const SerialNumberGenerator& Generator) {
+ ARL::ResourceWrapper LicenseFile{ ARL::ResourceTraits::OpensslBIO{} };
+
+ std::string utf8SerialNumber = Generator.GetSerialNumberShort();
+ std::string utf8username;
+ std::string utf8organization;
+
+ std::string utf8ResponseInfo;
+ std::vector ResponseCode;
+
+ std::cout << "[*] Your name: ";
+ if (!std::getline(std::cin, utf8username)) {
+ throw ARL::EOFError(__BASE_FILE__, __LINE__, "Abort.");
+ }
+
+ std::cout << "[*] Your organization: ";
+ if (!std::getline(std::cin, utf8organization)) {
+ throw ARL::EOFError(__BASE_FILE__, __LINE__, "Abort.");
+ }
+
+ std::cout << std::endl;
+
+ rapidjson::Document json;
+ rapidjson::Value N_Key;
+ rapidjson::Value N_Value;
+ rapidjson::Value O_Key;
+ rapidjson::Value O_Value;
+ rapidjson::Value T_Key;
+ rapidjson::Value T_Value;
+ rapidjson::Value K_Key;
+ rapidjson::Value K_Value;
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer writer(buffer);
+
+ json.Parse("{}");
+ K_Key.SetString("K", 1);
+ K_Value.SetString(utf8SerialNumber.c_str(), static_cast(utf8SerialNumber.length()));
+ N_Key.SetString("N", 1);
+ N_Value.SetString(utf8username.c_str(), static_cast(utf8username.length()));
+ O_Key.SetString("O", 1);
+ O_Value.SetString(utf8organization.c_str(), static_cast(utf8organization.length()));
+ T_Key.SetString("T", 1);
+ T_Value.SetUint(static_cast(std::time(nullptr)));
+
+ json.AddMember(K_Key, K_Value, json.GetAllocator());
+ json.AddMember(N_Key, N_Value, json.GetAllocator());
+ json.AddMember(O_Key, O_Value, json.GetAllocator());
+ json.AddMember(T_Key, T_Value, json.GetAllocator());
+
+ json.Accept(writer);
+ if (buffer.GetSize() > 240) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "Response info is too long.");
+ }
+
+ utf8ResponseInfo.assign(buffer.GetString(), buffer.GetSize());
+
+ std::cout << "[*] Response Info:" << std::endl;
+ std::cout << utf8ResponseInfo << std::endl;
+ std::cout << std::endl;
+
+ ResponseCode.resize((Cipher.Bits() + 7) / 8);
+ Cipher.Encrypt(utf8ResponseInfo.data(), utf8ResponseInfo.size(), ResponseCode.data(), RSA_PKCS1_PADDING);
+
+ LicenseFile.TakeOver(BIO_new_file("license_file", "w"));
+ if (LicenseFile.IsValid() == false) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "BIO_new_file failed.");
+ }
+
+ if (BIO_write(LicenseFile, ResponseCode.data(), ResponseCode.size()) != ResponseCode.size()) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "BIO_write failed.");
+ }
+
+ std::cout << "[+] license_file has been generated." << std::endl;
+ }
+}
+
diff --git a/navicat-keygen/SerialNumberGenerator.cpp b/navicat-keygen/SerialNumberGenerator.cpp
new file mode 100644
index 0000000..2797076
--- /dev/null
+++ b/navicat-keygen/SerialNumberGenerator.cpp
@@ -0,0 +1,183 @@
+#include "SerialNumberGenerator.hpp"
+#include "Exception.hpp"
+#include
+#include
+#include
+#include
+#include "Base32.hpp"
+
+namespace nkg {
+
+ SerialNumberGenerator::SerialNumberGenerator() noexcept :
+ m_Data{ 0x68 , 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32 } {}
+
+ void SerialNumberGenerator::SetLanguageSignature(NavicatLanguage Language) noexcept {
+ switch (Language) {
+ case NavicatLanguage::English:
+ m_Data[5] = 0xAC; // Must be 0xAC for English version.
+ m_Data[6] = 0x88; // Must be 0x88 for English version.
+ break;
+ case NavicatLanguage::SimplifiedChinese:
+ m_Data[5] = 0xCE; // Must be 0xCE for Simplified Chinese version.
+ m_Data[6] = 0x32; // Must be 0x32 for Simplified Chinese version.
+ break;
+ case NavicatLanguage::TraditionalChinese:
+ m_Data[5] = 0xAA; // Must be 0xAA for Traditional Chinese version.
+ m_Data[6] = 0x99; // Must be 0x99 for Traditional Chinese version.
+ break;
+ case NavicatLanguage::Japanese:
+ m_Data[5] = 0xAD; // Must be 0xAD for Japanese version. Discoverer: @dragonflylee
+ m_Data[6] = 0x82; // Must be 0x82 for Japanese version. Discoverer: @dragonflylee
+ break;
+ case NavicatLanguage::Polish:
+ m_Data[5] = 0xBB; // Must be 0xBB for Polish version. Discoverer: @dragonflylee
+ m_Data[6] = 0x55; // Must be 0x55 for Polish version. Discoverer: @dragonflylee
+ break;
+ case NavicatLanguage::Spanish:
+ m_Data[5] = 0xAE; // Must be 0xAE for Spanish version. Discoverer: @dragonflylee
+ m_Data[6] = 0x10; // Must be 0x10 for Spanish version. Discoverer: @dragonflylee
+ break;
+ case NavicatLanguage::French:
+ m_Data[5] = 0xFA; // Must be 0xFA for French version. Discoverer: @Deltafox79
+ m_Data[6] = 0x20; // Must be 0x20 for French version. Discoverer: @Deltafox79
+ break;
+ case NavicatLanguage::German:
+ m_Data[5] = 0xB1; // Must be 0xB1 for German version. Discoverer: @dragonflylee
+ m_Data[6] = 0x60; // Must be 0x60 for German version. Discoverer: @dragonflylee
+ break;
+ case NavicatLanguage::Korean:
+ m_Data[5] = 0xB5; // Must be 0xB5 for Korean version. Discoverer: @dragonflylee
+ m_Data[6] = 0x60; // Must be 0x60 for Korean version. Discoverer: @dragonflylee
+ break;
+ case NavicatLanguage::Russian:
+ m_Data[5] = 0xEE; // Must be 0xB5 for Russian version. Discoverer: @dragonflylee
+ m_Data[6] = 0x16; // Must be 0x60 for Russian version. Discoverer: @dragonflylee
+ break;
+ case NavicatLanguage::Portuguese:
+ m_Data[5] = 0xCD; // Must be 0xCD for Portuguese version. Discoverer: @dragonflylee
+ m_Data[6] = 0x49; // Must be 0x49 for Portuguese version. Discoverer: @dragonflylee
+ break;
+ default:
+ break;
+ }
+ }
+
+ void SerialNumberGenerator::SetLanguageSignature(uint8_t LanguageSignature0, uint8_t LanguageSignature1) noexcept {
+ m_Data[5] = LanguageSignature0;
+ m_Data[6] = LanguageSignature1;
+ }
+
+ void SerialNumberGenerator::SetProductSignature(NavicatProductType ProductType) noexcept {
+ switch (ProductType) {
+ case NavicatProductType::DataModeler:
+ m_Data[7] = 0x84;
+ break;
+ case NavicatProductType::Premium:
+ m_Data[7] = 0x65;
+ break;
+ case NavicatProductType::MySQL:
+ m_Data[7] = 0x68;
+ break;
+ case NavicatProductType::PostgreSQL:
+ m_Data[7] = 0x6C;
+ break;
+ case NavicatProductType::Oracle:
+ m_Data[7] = 0x70;
+ break;
+ case NavicatProductType::SQLServer:
+ m_Data[7] = 0x74;
+ break;
+ case NavicatProductType::SQLite:
+ m_Data[7] = 0x78;
+ break;
+ case NavicatProductType::MariaDB:
+ m_Data[7] = 0x7C;
+ break;
+ case NavicatProductType::MongoDB:
+ m_Data[7] = 0x80;
+ break;
+ case NavicatProductType::ReportViewer:
+ m_Data[7] = 0x0b;
+ default:
+ break;
+ }
+ }
+
+ void SerialNumberGenerator::SetProductSignature(uint8_t ProductSignature) noexcept {
+ m_Data[7] = ProductSignature;
+ }
+
+ void SerialNumberGenerator::SetVersion(uint8_t Version) {
+ if (Version < 0x10) {
+ m_Data[8] = static_cast(Version << 4);
+ } else {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "Invalid version for Navicat.");
+ }
+ }
+
+ void SerialNumberGenerator::Generate() {
+ static auto translator = [](char c) {
+#ifdef __APPLE__
+ //static const char StandardBase32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+ //static const char NavicatBase32[] = "ABCDEFGH8JKLMN9PQRSTUVWXYZ234567";
+ switch (c) {
+ case 'I':
+ return '8';
+ case 'O':
+ return '9';
+ default:
+ return c;
+ }
+#else
+ //static const char StandardBase32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+ //static const char NavicatBase32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+ return c;
+#endif
+ };
+
+ RAND_bytes(m_Data + 2, 3);
+
+ const_DES_cblock key = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 };
+ DES_key_schedule schedule;
+ DES_set_key_unchecked(&key, &schedule);
+ DES_ecb_encrypt(
+ reinterpret_cast(m_Data + 2),
+ reinterpret_cast(m_Data + 2),
+ &schedule,
+ DES_ENCRYPT
+ );
+
+
+ m_SerialNumberShort = base32_encode(m_Data, sizeof(m_Data));
+ std::transform(m_SerialNumberShort.begin(), m_SerialNumberShort.end(), m_SerialNumberShort.begin(), translator);
+
+ m_SerialNumberLong.resize(20);
+ snprintf(m_SerialNumberLong.data(), m_SerialNumberLong.length(),
+ "%.4s-%.4s-%.4s-%.4s",
+ m_SerialNumberShort.c_str() + 0,
+ m_SerialNumberShort.c_str() + 4,
+ m_SerialNumberShort.c_str() + 8,
+ m_SerialNumberShort.c_str() + 12
+ );
+ while (m_SerialNumberLong.back() == '\x00') {
+ m_SerialNumberLong.pop_back();
+ }
+ }
+
+ [[nodiscard]]
+ const std::string& SerialNumberGenerator::GetSerialNumberShort() const noexcept {
+ return m_SerialNumberShort;
+ }
+
+ [[nodiscard]]
+ const std::string& SerialNumberGenerator::GetSerialNumberLong() const noexcept {
+ return m_SerialNumberLong;
+ }
+
+ void SerialNumberGenerator::ShowInConsole() const {
+ std::cout << "[*] Serial number:" << std::endl;
+ std::cout << m_SerialNumberLong << std::endl;
+ std::cout << std::endl;
+ }
+}
+
diff --git a/navicat-keygen/SerialNumberGenerator.hpp b/navicat-keygen/SerialNumberGenerator.hpp
new file mode 100644
index 0000000..75f9554
--- /dev/null
+++ b/navicat-keygen/SerialNumberGenerator.hpp
@@ -0,0 +1,65 @@
+#pragma once
+#include
+#include
+
+namespace nkg {
+
+ enum class NavicatLanguage {
+ English,
+ SimplifiedChinese,
+ TraditionalChinese,
+ Japanese,
+ Polish,
+ Spanish,
+ French,
+ German,
+ Korean,
+ Russian,
+ Portuguese
+ };
+
+ enum class NavicatProductType {
+ DataModeler,
+ Premium,
+ MySQL,
+ PostgreSQL,
+ Oracle,
+ SQLServer,
+ SQLite,
+ MariaDB,
+ MongoDB,
+ ReportViewer
+ };
+
+ class SerialNumberGenerator {
+ private:
+
+ uint8_t m_Data[10];
+ std::string m_SerialNumberShort;
+ std::string m_SerialNumberLong;
+
+ public:
+
+ SerialNumberGenerator() noexcept;
+
+ void SetLanguageSignature(NavicatLanguage Language) noexcept;
+ void SetLanguageSignature(uint8_t LanguageSignature0, uint8_t LanguageSignature1) noexcept;
+
+ void SetProductSignature(NavicatProductType ProductType) noexcept;
+ void SetProductSignature(uint8_t ProductSignature) noexcept;
+
+ void SetVersion(uint8_t Version);
+
+ void Generate();
+
+ [[nodiscard]]
+ const std::string& GetSerialNumberShort() const noexcept;
+
+ [[nodiscard]]
+ const std::string& GetSerialNumberLong() const noexcept;
+
+ void ShowInConsole() const;
+ };
+
+}
+
diff --git a/navicat-keygen/main.cpp b/navicat-keygen/main.cpp
new file mode 100644
index 0000000..893c2b4
--- /dev/null
+++ b/navicat-keygen/main.cpp
@@ -0,0 +1,112 @@
+#include
+#include
+#include "Exception.hpp"
+#include "ExceptionGeneric.hpp"
+#include "RSACipher.hpp"
+#include "SerialNumberGenerator.hpp"
+
+namespace nkg {
+ using fnCollectInformation = SerialNumberGenerator();
+ using fnGenerateLicense = void(const RSACipher& Cipher, const SerialNumberGenerator& Generator);
+
+ SerialNumberGenerator CollectInformationNormal();
+ SerialNumberGenerator CollectInformationAdvanced();
+ void GenerateLicenseText(const RSACipher& Cipher, const SerialNumberGenerator& Generator);
+ void GenerateLicenseBinary(const RSACipher& Cipher, const SerialNumberGenerator& Generator);
+}
+
+static void Welcome() {
+ puts("**********************************************************");
+ puts("* Navicat Keygen (Linux) by @DoubleLabyrinth *");
+ puts("* Version: 1.0 *");
+ puts("**********************************************************");
+ puts("");
+}
+
+static void Help() {
+ puts("Usage:");
+ puts(" navicat-keygen <--bin|--text> [--adv] ");
+ puts("");
+ puts(" <--bin|--text> Specify \"--bin\" to generate \"license_file\" used by Navicat 11.");
+ puts(" Specify \"--text\" to generate base64-encoded activation code.");
+ puts(" This parameter must be specified.");
+ puts("");
+ puts(" [--adv] Enable advance mode.");
+ puts(" This parameter is optional.");
+ puts("");
+ puts(" A path to an RSA-2048 private key file.");
+ puts(" This parameter must be specified.");
+ puts("");
+ puts("Example:");
+ puts(" ./navicat-keygen --text ./RegPrivateKey.pem");
+}
+
+int main(int argc, const char* argv[]) {
+ Welcome();
+
+ if (argc == 3 || argc == 4) {
+ nkg::fnCollectInformation* lpfnCollectInformation = nullptr;
+ nkg::fnGenerateLicense* lpfnGenerateLicense = nullptr;
+
+ if (strcasecmp(argv[1], "--bin") == 0) {
+ lpfnGenerateLicense = nkg::GenerateLicenseBinary;
+ } else if (strcasecmp(argv[1], "--text") == 0) {
+ lpfnGenerateLicense = nkg::GenerateLicenseText;
+ } else {
+ Help();
+ return -1;
+ }
+
+ if (argc == 4) {
+ if (strcasecmp(argv[2], "--adv") == 0) {
+ lpfnCollectInformation = nkg::CollectInformationAdvanced;
+ } else {
+ Help();
+ return -1;
+ }
+ } else {
+ lpfnCollectInformation = nkg::CollectInformationNormal;
+ }
+
+ try {
+ nkg::RSACipher Cipher;
+
+ Cipher.ImportKeyFromFile(argv[argc - 1]);
+ if (Cipher.Bits() != 2048) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "RSA key length mismatches.")
+ .PushHint("You must provide an RSA key whose modulus length is 2048 bits.");
+ }
+
+ auto Generator = lpfnCollectInformation();
+
+ Generator.Generate();
+ Generator.ShowInConsole();
+
+ lpfnGenerateLicense(Cipher, Generator);
+
+ return 0;
+ } catch (ARL::EOFError&) {
+ return ECANCELED;
+ } catch (ARL::Exception& e) {
+ printf("[-] %s:%zu ->\n", e.ExceptionFile(), e.ExceptionLine());
+ printf(" %s\n", e.ExceptionMessage());
+
+ if (e.HasErrorCode()) {
+ printf(" %s (0x%zx)\n", e.ErrorString(), e.ErrorCode());
+ }
+
+ for (const auto& Hint : e.Hints()) {
+ printf(" Hints: %s\n", Hint.c_str());
+ }
+
+ return -1;
+ } catch (std::exception& e) {
+ printf("[-] %s\n", e.what());
+ return -1;
+ }
+ } else {
+ Help();
+ return -1;
+ }
+}
+
diff --git a/navicat-patcher/CapstoneDisassembler.cpp b/navicat-patcher/CapstoneDisassembler.cpp
new file mode 100644
index 0000000..fa38a5a
--- /dev/null
+++ b/navicat-patcher/CapstoneDisassembler.cpp
@@ -0,0 +1,95 @@
+#include "CapstoneDisassembler.hpp"
+
+namespace nkg {
+
+ CapstoneDisassembler::CapstoneDisassembler(const CapstoneEngine& Engine) :
+ ARL::ResourceWrapper(cs_malloc(Engine)),
+ m_Engine(Engine),
+ m_CurrentState{},
+ m_NextState{},
+ m_lpCurrentInsn(nullptr)
+ {
+ if (IsValid() == false) {
+ throw ARL::CapstoneError(__BASE_FILE__, __LINE__, cs_errno(Engine), "cs_malloc failed.");
+ }
+ }
+
+ CapstoneDisassembler& CapstoneDisassembler::SetContext(const CapstoneContext& Ctx) noexcept {
+ m_lpCurrentInsn = nullptr;
+
+ m_CurrentState.lpMachineCode = nullptr;
+ m_CurrentState.cbMachineCode = 0;
+ m_CurrentState.Address = 0;
+
+ m_NextState = Ctx;
+
+ return *this;
+ }
+
+ [[nodiscard]]
+ const CapstoneContext& CapstoneDisassembler::GetContext() const noexcept {
+ return m_NextState;
+ }
+
+ [[nodiscard]]
+ bool CapstoneDisassembler::Next() noexcept {
+ bool bSucceed;
+ CapstoneContext backup = m_NextState;
+
+ bSucceed = cs_disasm_iter(m_Engine.Get(), reinterpret_cast(&m_NextState.lpMachineCode), &m_NextState.cbMachineCode, &m_NextState.Address, Get());
+ if (bSucceed) {
+ if (m_lpCurrentInsn == nullptr) {
+ m_lpCurrentInsn = Get();
+ }
+
+ m_CurrentState = backup;
+ } else {
+ m_lpCurrentInsn = nullptr;
+ }
+
+ return bSucceed;
+ }
+
+ [[nodiscard]]
+ const cs_insn* CapstoneDisassembler::GetInstruction() const noexcept {
+ return m_lpCurrentInsn;
+ }
+
+ [[nodiscard]]
+ const CapstoneContext& CapstoneDisassembler::GetInstructionContext() const noexcept {
+ return m_CurrentState;
+ }
+
+ CapstoneEngine::CapstoneEngine(cs_arch ArchType, cs_mode Mode) {
+ auto err = cs_open(ArchType, Mode, GetAddressOf());
+ if (err != CS_ERR_OK) {
+ throw ARL::CapstoneError(__BASE_FILE__, __LINE__, err, "cs_open failed.");
+ }
+ }
+
+ void CapstoneEngine::Option(cs_opt_type Type, cs_opt_value Value) {
+ auto err = cs_option(Get(), Type, Value);
+ if (err != CS_ERR_OK) {
+ throw ARL::CapstoneError(__BASE_FILE__, __LINE__, err, "cs_option failed.");
+ }
+ }
+
+ const char* CapstoneEngine::GetGroupName(unsigned int group_id) const noexcept {
+ return cs_group_name(Get(), group_id);
+ }
+
+ const char* CapstoneEngine::GetInstructionName(unsigned int instruction_id) const noexcept {
+ return cs_insn_name(Get(), instruction_id);
+ }
+
+ const char* CapstoneEngine::GetRegisterName(unsigned int register_id) const noexcept {
+ return cs_reg_name(Get(), register_id);
+ }
+
+ [[nodiscard]]
+ CapstoneDisassembler CapstoneEngine::CreateDisassembler() const {
+ return CapstoneDisassembler(*this);
+ }
+
+}
+
diff --git a/navicat-patcher/CapstoneDisassembler.hpp b/navicat-patcher/CapstoneDisassembler.hpp
new file mode 100644
index 0000000..97bfc84
--- /dev/null
+++ b/navicat-patcher/CapstoneDisassembler.hpp
@@ -0,0 +1,64 @@
+#pragma once
+#include
+#include "ExceptionCapstone.hpp"
+#include "ResourceWrapper.hpp"
+#include "ResourceTraitsCapstone.hpp"
+
+namespace nkg {
+
+ struct CapstoneContext {
+ const void* lpMachineCode;
+ size_t cbMachineCode;
+ uint64_t Address;
+ };
+
+ class CapstoneEngine;
+
+ class CapstoneDisassembler : private ARL::ResourceWrapper {
+ friend class CapstoneEngine;
+ private:
+
+ const CapstoneEngine& m_Engine;
+ CapstoneContext m_CurrentState;
+ CapstoneContext m_NextState;
+ cs_insn* m_lpCurrentInsn;
+
+ CapstoneDisassembler(const CapstoneEngine& Engine);
+
+ public:
+
+ CapstoneDisassembler& SetContext(const CapstoneContext& Ctx) noexcept;
+
+ [[nodiscard]]
+ const CapstoneContext& GetContext() const noexcept;
+
+ [[nodiscard]]
+ bool Next() noexcept;
+
+ [[nodiscard]]
+ const cs_insn* GetInstruction() const noexcept;
+
+ [[nodiscard]]
+ const CapstoneContext& GetInstructionContext() const noexcept;
+ };
+
+ class CapstoneEngine : private ARL::ResourceWrapper {
+ friend class CapstoneDisassembler;
+ public:
+
+ CapstoneEngine(cs_arch ArchType, cs_mode Mode);
+
+ void Option(cs_opt_type Type, cs_opt_value Value);
+
+ const char* GetGroupName(unsigned int group_id) const noexcept;
+
+ const char* GetInstructionName(unsigned int instruction_id) const noexcept;
+
+ const char* GetRegisterName(unsigned int register_id) const noexcept;
+
+ [[nodiscard]]
+ CapstoneDisassembler CreateDisassembler() const;
+ };
+
+}
+
diff --git a/navicat-patcher/Elf64Interpreter.cpp b/navicat-patcher/Elf64Interpreter.cpp
new file mode 100644
index 0000000..c409ce3
--- /dev/null
+++ b/navicat-patcher/Elf64Interpreter.cpp
@@ -0,0 +1,406 @@
+#include "Elf64Interpreter.hpp"
+#include "Exception.hpp"
+#include "ExceptionGeneric.hpp"
+#include
+#include
+
+namespace nkg {
+
+ [[nodiscard]]
+ Elf64Interpreter Elf64Interpreter::Parse(const void* lpImage, size_t cbImage) {
+ Elf64Interpreter Interpreter;
+
+ //
+ // Checking ELF header
+ //
+
+ Interpreter.m_ElfSize = cbImage;
+ Interpreter.m_lpElfHdr = reinterpret_cast(lpImage);
+ if (ARL::AddressIsInRangeEx(Interpreter.m_lpElfHdr, sizeof(Elf64_Ehdr), lpImage, cbImage) == false) {
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "Bad ELF file: image is corrupted.");
+ }
+
+ if (memcmp(Interpreter.m_lpElfHdr->e_ident, ELFMAG, SELFMAG) != 0) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: header magic check failure.");
+ }
+
+ if (Interpreter.m_lpElfHdr->e_ident[EI_CLASS] != ELFCLASS64) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Unsupported ELF file: not ELF64 image.");
+ }
+
+ switch (Interpreter.m_lpElfHdr->e_ident[EI_DATA]) {
+ case ELFDATA2LSB:
+ if (__BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__) {
+ throw ARL::NotImplementedError(__BASE_FILE__, __LINE__, "Unsupported ELF file: unsupported endian.");
+ }
+ break;
+ case ELFDATA2MSB:
+ if (__BYTE_ORDER__ != __ORDER_BIG_ENDIAN__) {
+ throw ARL::NotImplementedError(__BASE_FILE__, __LINE__, "Unsupported ELF file: unsupported endian.");
+ }
+ break;
+ default:
+ throw ARL::Exception(__BASE_FILE__, __LINE__, "Bad ELF file: unknown endian.");
+ }
+
+ if (Interpreter.m_lpElfHdr->e_ident[EI_VERSION] != EV_CURRENT) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Ehdr::e_ident[EI_VERSION] check failure.");
+ }
+
+ // Interpreter.m_lpElfHdr->e_ident[EI_OSABI]
+ // Interpreter.m_lpElfHdr->e_ident[EI_ABIVERSION]
+
+ for (int i = EI_PAD; i < sizeof(Interpreter.m_lpElfHdr->e_ident); ++i) {
+ if (Interpreter.m_lpElfHdr->e_ident[i] != 0) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Ehdr::e_ident padding contains non-zero byte(s).");
+ }
+ }
+
+ if (Interpreter.m_lpElfHdr->e_version != EV_CURRENT) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Ehdr::e_version check failure.");
+ }
+
+ if (Interpreter.m_lpElfHdr->e_ehsize != sizeof(Elf64_Ehdr)) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Ehdr::e_ehsize check failure.");
+ }
+
+ if (Interpreter.m_lpElfHdr->e_phoff && Interpreter.m_lpElfHdr->e_phentsize && Interpreter.m_lpElfHdr->e_phnum) {
+ if (Interpreter.m_lpElfHdr->e_phentsize != sizeof(Elf64_Phdr)) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Ehdr::e_phentsize check failure.");
+ }
+
+ Interpreter.m_lpElfProgramHdr =
+ ARL::AddressOffsetWithCast(lpImage, Interpreter.m_lpElfHdr->e_phoff);
+
+ auto a1 = Interpreter.m_lpElfProgramHdr;
+ auto a2 = Interpreter.m_lpElfProgramHdr + Interpreter.m_lpElfHdr->e_phnum;
+ if (a1 < a2) {
+ if (ARL::AddressIsInRangeEx(a1, a2, lpImage, cbImage) == false) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: image is corrupted.");
+ }
+ } else {
+ throw ARL::OverflowError(__BASE_FILE__, __LINE__, "Bad ELF file: program header table overflowed.");
+ }
+ } else if (Interpreter.m_lpElfHdr->e_phoff == 0 && Interpreter.m_lpElfHdr->e_phentsize == 0 && Interpreter.m_lpElfHdr->e_phnum == 0) {
+ Interpreter.m_lpElfProgramHdr = nullptr;
+ } else {
+ throw ARL::ValueError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Ehdr::e_ph* check failure.");
+ }
+
+ if (Interpreter.m_lpElfHdr->e_shoff && Interpreter.m_lpElfHdr->e_shentsize && Interpreter.m_lpElfHdr->e_shnum) {
+ if (Interpreter.m_lpElfHdr->e_shentsize != sizeof(Elf64_Shdr)) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Ehdr::e_shentsize check failure.");
+ }
+
+ Interpreter.m_lpElfSectionHdr =
+ ARL::AddressOffsetWithCast(lpImage, Interpreter.m_lpElfHdr->e_shoff);
+
+ auto b1 = Interpreter.m_lpElfSectionHdr;
+ auto b2 = Interpreter.m_lpElfSectionHdr + Interpreter.m_lpElfHdr->e_shnum;
+ if (b1 < b2) {
+ if (ARL::AddressIsInRangeEx(b1, b2, lpImage, cbImage) == false) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: image is corrupted.");
+ }
+ } else {
+ throw ARL::OverflowError(__BASE_FILE__, __LINE__, "Bad ELF file: section header table overflowed.");
+ }
+ } else if (Interpreter.m_lpElfHdr->e_shoff == 0 && Interpreter.m_lpElfHdr->e_shentsize == 0 && Interpreter.m_lpElfHdr->e_shnum == 0) {
+ Interpreter.m_lpElfSectionHdr = nullptr;
+ } else {
+ throw ARL::ValueError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Ehdr::e_sh* check failure.");
+ }
+
+ if (Interpreter.m_lpElfHdr->e_shstrndx != SHN_UNDEF) {
+ if (Interpreter.m_lpElfHdr->e_shstrndx >= Interpreter.m_lpElfHdr->e_shnum) {
+ throw ARL::IndexError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Ehdr::e_shstrndx is out of range.");
+ }
+ }
+
+ //
+ // Checking program header table and section header table
+ //
+
+ if (Interpreter.m_lpElfProgramHdr && Interpreter.m_lpElfSectionHdr) {
+ auto a1 = Interpreter.m_lpElfProgramHdr;
+ auto a2 = Interpreter.m_lpElfProgramHdr + Interpreter.m_lpElfHdr->e_phnum;
+ auto b1 = Interpreter.m_lpElfSectionHdr;
+ auto b2 = Interpreter.m_lpElfSectionHdr + Interpreter.m_lpElfHdr->e_shnum;
+ bool NotOverlapped =
+ (ARL::AddressDelta(a1, b1) < 0 && ARL::AddressDelta(a2, b1) <= 0) ||
+ (ARL::AddressDelta(b1, a1) < 0 && ARL::AddressDelta(b2, a1) <= 0);
+ if (NotOverlapped == false) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: program header table and section header table overlap.");
+ }
+ }
+
+ //
+ // Parsing program header
+ //
+ {
+ for (decltype(Elf64_Ehdr::e_phnum) i = 0; i < Interpreter.m_lpElfHdr->e_phnum; ++i) {
+ const auto& proghdr = Interpreter.m_lpElfProgramHdr[i];
+
+ if (ARL::AddressIsInRangeEx(ARL::AddressOffset(lpImage, proghdr.p_offset), proghdr.p_filesz, lpImage, cbImage) == false) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: image is corrupted.");
+ }
+
+ if (auto p_align = proghdr.p_align; p_align) {
+ // align must be a power of 2
+ if ((p_align & (p_align - 1)) != 0) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Phdr[%u]: p_align is not a power of 2.", i);
+ }
+
+ if (proghdr.p_offset % p_align != proghdr.p_vaddr % p_align) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Phdr[%u]: p_offset !== p_vaddr (mod p_align).", i);
+ }
+ }
+
+ // todo
+ }
+ }
+
+ //
+ // Parsing section header
+ //
+ {
+ const Elf64_Shdr* sechdr_shstrtab;
+ const char* secview_shstrtab;
+ if (Interpreter.m_lpElfHdr->e_shstrndx != SHN_UNDEF) {
+ sechdr_shstrtab = &Interpreter.m_lpElfSectionHdr[Interpreter.m_lpElfHdr->e_shstrndx];
+ secview_shstrtab = ARL::AddressOffsetWithCast(lpImage, sechdr_shstrtab->sh_offset);
+
+ if (sechdr_shstrtab->sh_type != SHT_STRTAB) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: sechdr_shstrtab->sh_type != SHT_STRTAB.");
+ }
+
+ if (ARL::AddressIsInRangeEx(secview_shstrtab, sechdr_shstrtab->sh_size, lpImage, cbImage) == false) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: image is corrupted.");
+ }
+ } else {
+ sechdr_shstrtab = nullptr;
+ secview_shstrtab = nullptr;
+ }
+
+ for (decltype(Elf64_Ehdr::e_shnum) i = 0; i < Interpreter.m_lpElfHdr->e_shnum; ++i) {
+ auto& sechdr = Interpreter.m_lpElfSectionHdr[i];
+
+ //
+ // checking sh_type
+ //
+ switch (sechdr.sh_type) {
+ case SHT_SYMTAB:
+ if (sechdr.sh_entsize != sizeof(Elf64_Sym)) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_entsize != sizeof(Elf64_Dyn).", i);
+ }
+ break;
+ case SHT_RELA:
+ if (sechdr.sh_entsize != sizeof(Elf64_Rela)) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_entsize != sizeof(Elf64_Rela).", i);
+ }
+ break;
+ case SHT_DYNAMIC:
+ if (sechdr.sh_entsize != sizeof(Elf64_Dyn)) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_entsize != sizeof(Elf64_Dyn).", i);
+ }
+ break;
+ case SHT_REL:
+ if (sechdr.sh_entsize != sizeof(Elf64_Rel)) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_entsize != sizeof(Elf64_Rel).", i);
+ }
+ break;
+ case SHT_DYNSYM:
+ if (sechdr.sh_entsize != sizeof(Elf64_Sym)) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_entsize != sizeof(Elf64_Dyn).", i);
+ }
+ break;
+ default:
+ break;
+ }
+
+ //
+ // checking sh_link and sh_info
+ //
+ switch (sechdr.sh_type) {
+ case SHT_DYNAMIC:
+ if (sechdr.sh_link == SHN_UNDEF) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_link == SHN_UNDEF.", i);
+ }
+
+ if (sechdr.sh_link < Interpreter.m_lpElfHdr->e_shnum) {
+ if (Interpreter.m_lpElfSectionHdr[sechdr.sh_link].sh_type != SHT_STRTAB) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: incorrect value of sh_link.", i);
+ }
+ } else {
+ throw ARL::IndexError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_link is out of range.", i);
+ }
+
+ if (sechdr.sh_info != 0) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_info != 0.", i);
+ }
+ break;
+ case SHT_HASH:
+ if (sechdr.sh_link == SHN_UNDEF) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_link == SHN_UNDEF.", i);
+ }
+
+ if (sechdr.sh_link < Interpreter.m_lpElfHdr->e_shnum) {
+ if (Interpreter.m_lpElfSectionHdr[sechdr.sh_link].sh_type != SHT_SYMTAB && Interpreter.m_lpElfSectionHdr[sechdr.sh_link].sh_type != SHT_DYNSYM) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: incorrect value of sh_link.", i);
+ }
+ } else {
+ throw ARL::IndexError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_link is out of range.", i);
+ }
+
+ if (sechdr.sh_info != 0) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_info != 0.", i);
+ }
+ break;
+ case SHT_RELA:
+ case SHT_REL:
+ if (sechdr.sh_link == SHN_UNDEF) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_link == SHN_UNDEF.", i);
+ }
+
+ if (sechdr.sh_link < Interpreter.m_lpElfHdr->e_shnum) {
+ if (Interpreter.m_lpElfSectionHdr[sechdr.sh_link].sh_type != SHT_SYMTAB && Interpreter.m_lpElfSectionHdr[sechdr.sh_link].sh_type != SHT_DYNSYM) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: incorrect value of sh_link.", i);
+ }
+ } else {
+ throw ARL::IndexError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_link is out of range.", i);
+ }
+
+ if (sechdr.sh_flags & SHF_INFO_LINK) {
+ if (sechdr.sh_info == SHN_UNDEF) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_info == SHN_UNDEF.", i);
+ }
+
+ if (sechdr.sh_info >= Interpreter.m_lpElfHdr->e_shnum) {
+ throw ARL::IndexError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_info is out of range.", i);
+ }
+ } else {
+ if (sechdr.sh_info != 0) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_info != 0.", i);
+ }
+ }
+ break;
+ case SHT_SYMTAB:
+ case SHT_DYNSYM:
+ if (sechdr.sh_link == SHN_UNDEF) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_link == SHN_UNDEF.", i);
+ }
+
+ if (sechdr.sh_link < Interpreter.m_lpElfHdr->e_shnum) {
+ if (Interpreter.m_lpElfSectionHdr[sechdr.sh_link].sh_type != SHT_STRTAB) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: incorrect value of sh_link.", i);
+ }
+ } else {
+ throw ARL::IndexError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_link is out of range.", i);
+ }
+
+ // todo: check sh_info
+ break;
+ default:
+ break;
+ }
+
+ if (sechdr.sh_type != SHT_NOBITS) {
+ if (ARL::AddressIsInRangeEx(ARL::AddressOffset(lpImage, sechdr.sh_offset), sechdr.sh_size, lpImage, cbImage) == false) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: image is corrupted.", i);
+ }
+ }
+
+ if (sechdr.sh_addr) {
+ if (sechdr.sh_addralign && sechdr.sh_addr % sechdr.sh_addralign != 0) {
+ throw ARL::AssertionError(__BASE_FILE__, __LINE__, "Bad ELF file: Elf64_Shdr[%u]: sh_addr is not aligned to sh_addralign.", i);
+ }
+
+ Interpreter.m_SectionRvaMap.emplace(
+ std::make_pair(
+ sechdr.sh_addr,
+ Interpreter.m_lpElfSectionHdr + i
+ )
+ );
+ }
+
+ if (sechdr.sh_type != SHT_NOBITS) {
+ Interpreter.m_SectionOffsetMap.emplace(
+ std::make_pair(
+ sechdr.sh_offset,
+ Interpreter.m_lpElfSectionHdr + i
+ )
+ );
+ }
+
+ if (secview_shstrtab) {
+ Interpreter.m_SectionNameMap.emplace(
+ std::make_pair(
+ std::string(ARL::AddressOffset(secview_shstrtab, sechdr.sh_name)),
+ Interpreter.m_lpElfSectionHdr + i
+ )
+ );
+ }
+
+ // todo
+ }
+ }
+
+ return Interpreter;
+ }
+
+ size_t Elf64Interpreter::ElfSize() const noexcept {
+ return m_ElfSize;
+ }
+
+ [[nodiscard]]
+ const Elf64_Phdr* Elf64Interpreter::ElfProgramHeader(size_t Idx) const {
+ if (Idx < m_lpElfHdr->e_phnum) {
+ return m_lpElfProgramHdr + Idx;
+ } else {
+ throw ARL::IndexError(__BASE_FILE__, __LINE__, "Elf64Interpreter: Idx is out of range.");
+ }
+ }
+
+ [[nodiscard]]
+ const Elf64_Shdr* Elf64Interpreter::ElfSectionHeader(size_t Idx) const {
+ if (Idx < m_lpElfHdr->e_shnum) {
+ return m_lpElfSectionHdr + Idx;
+ } else {
+ throw ARL::IndexError(__BASE_FILE__, __LINE__, "Elf64Interpreter: Idx is out of range.");
+ }
+ }
+
+ [[nodiscard]]
+ const Elf64_Shdr* Elf64Interpreter::ElfSectionHeader(std::string_view SectionName) const {
+ auto it = m_SectionNameMap.find(std::string(SectionName));
+ if (it != m_SectionNameMap.end()) {
+ return it->second;
+ } else {
+ throw ARL::KeyError(__BASE_FILE__, __LINE__, "Elf64Interpreter: section %s is not found.", SectionName.data());
+ }
+ }
+
+ [[nodiscard]]
+ Elf64_Off Elf64Interpreter::ConvertRvaToOffset(Elf64_Addr Rva) const {
+ auto it = m_SectionRvaMap.upper_bound(Rva);
+ if (it != m_SectionRvaMap.begin()) {
+ --it;
+ if (it->second->sh_addr <= Rva && Rva < it->second->sh_addr + it->second->sh_size) {
+ return it->second->sh_offset + (Rva - it->second->sh_addr);
+ }
+ }
+ throw ARL::KeyError(__BASE_FILE__, __LINE__, "Elf64Interpreter: Invalid RVA.");
+ }
+
+ [[nodiscard]]
+ Elf64_Addr Elf64Interpreter::ConvertOffsetToRva(Elf64_Off Offset) const {
+ auto it = m_SectionOffsetMap.upper_bound(Offset);
+ if (it != m_SectionOffsetMap.begin()) {
+ --it;
+ if (it->second->sh_offset <= Offset && Offset < it->second->sh_offset + it->second->sh_size) {
+ return it->second->sh_addr + (Offset - it->second->sh_offset);
+ }
+ }
+ throw ARL::KeyError(__BASE_FILE__, __LINE__, "Elf64Interpreter: Invalid Offset.");
+ }
+}
+
diff --git a/navicat-patcher/Elf64Interpreter.hpp b/navicat-patcher/Elf64Interpreter.hpp
new file mode 100644
index 0000000..f363a7f
--- /dev/null
+++ b/navicat-patcher/Elf64Interpreter.hpp
@@ -0,0 +1,193 @@
+#pragma once
+#include
+#include
+#ifdef __APPLE__
+ #include
+#else
+ #include
+#endif
+#include
+#include
+#include