diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f6adf54
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.vs/
+.vscode/
+bin/
+obj/
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/README.md b/README.md
new file mode 100644
index 0000000..f011008
--- /dev/null
+++ b/README.md
@@ -0,0 +1,36 @@
+# Navicat Keygen
+
+[中文版README](README.zh-CN.md)
+
+This repository will tell you how Navicat offline activation works.
+
+Previous code is archived in [`windows-archived`](https://github.com/DoubleLabyrinth/navicat-keygen/tree/windows-archived) branch for the reason that previos code contains 3rd binary libraries and it gets quite big :-(.
+
+When you git-clone this repo, please add `--single-branch` flag so that archived branch won't be cloned to your computer, which saves your time and disk.
+
+```console
+$ git clone -b windows --single-branch https://github.com/DoubleLabyrinth/navicat-keygen.git
+```
+
+## 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?
+
+For Windows users, see [here](doc/how-to-use.windows.md).
+
+For Linux users, see [here](doc/how-to-use.linux.md).
+
+## 4. Contributor
+
+* Deltafox79
+
+* dragonflylee
+
+* zenuo
+
diff --git a/README.zh-CN.md b/README.zh-CN.md
new file mode 100644
index 0000000..b4498b3
--- /dev/null
+++ b/README.zh-CN.md
@@ -0,0 +1,34 @@
+# Navicat Keygen
+
+这份repo将会告诉你Navicat是怎么完成离线激活的。
+
+由于历史代码包含第三方二进制库,且大小较大,故决定将该项目的历史代码归档到 [`windows-archived`](https://github.com/DoubleLabyrinth/navicat-keygen/tree/windows-archived) 分支。
+
+当你clone该仓库的时候,请使用 `--single-branch` 选项,以此避免clone到 `windows-archived` 分支,并且还可以节省你的时间和磁盘空间。
+
+```console
+$ git clone -b windows --single-branch https://github.com/DoubleLabyrinth/navicat-keygen.git
+```
+
+## 1. 注册机是怎么工作的?
+
+见[这里](doc/how-does-it-work.zh-CN.md)。
+
+## 2. 如何编译?
+
+见[这里](doc/how-to-build.zh-CN.md)。
+
+## 3. 如何使用这个注册机?
+
+针对 Windows 用户,请见[这里](doc/how-to-use.windows.zh-CN.md)。
+
+针对 Linux 用户,请见[这里](doc/how-to-use.linux.zh-CN.md)。
+
+## 4. 贡献者
+
+* Deltafox79
+
+* dragonflylee
+
+* zenuo
+
diff --git a/bash/navicat-keygen.sh b/bash/navicat-keygen.sh
new file mode 100644
index 0000000..5ad32d8
--- /dev/null
+++ b/bash/navicat-keygen.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+cd `dirname "$0"`
+navicat_root=`pwd`
+
+# Wine environment variables
+WINEDIR="wine"
+export LANG="en_US.UTF-8"
+export PATH="$navicat_root/$WINEDIR/bin":"$navicat_root":"$navicat_root/$WINEDIR/drive_c/windows":"$PATH"
+export LD_LIBRARY_PATH="$navicat_root/$WINEDIR/lib":"$navicat_root/lib":"$LD_LIBRARY_PATH"
+export WINEDLLPATH="$navicat_root/$WINEDIR/lib/wine"
+export WINELOADER="$navicat_root/$WINEDIR/bin/wine64"
+export WINESERVER="$navicat_root/$WINEDIR/bin/wineserver"
+export WINEPREFIX="$HOME/.navicat64"
+
+exec "${WINELOADER:-wine}" "navicat-keygen.exe" "-text" "RegPrivateKey.pem"
diff --git a/bash/navicat-patcher.sh b/bash/navicat-patcher.sh
new file mode 100644
index 0000000..3f190da
--- /dev/null
+++ b/bash/navicat-patcher.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+cd `dirname "$0"`
+navicat_root=`pwd`
+
+# Wine environment variables
+WINEDIR="wine"
+export LANG="en_US.UTF-8"
+export PATH="$navicat_root/$WINEDIR/bin":"$navicat_root":"$navicat_root/$WINEDIR/drive_c/windows":"$PATH"
+export LD_LIBRARY_PATH="$navicat_root/$WINEDIR/lib":"$navicat_root/lib":"$LD_LIBRARY_PATH"
+export WINEDLLPATH="$navicat_root/$WINEDIR/lib/wine"
+export WINELOADER="$navicat_root/$WINEDIR/bin/wine64"
+export WINESERVER="$navicat_root/$WINEDIR/bin/wineserver"
+export WINEPREFIX="$HOME/.navicat64"
+
+# 将斜线替换为反斜线
+navicat_root_back_slash=${navicat_root//\//\\}
+# 前缀
+prefix='Z:\'
+# 后缀
+suffix='\Navicat'
+# wine环境中的navicat路径
+navicat_path="$prefix$navicat_root_back_slash$suffix"
+
+# wine执行navicat-patcher.exe
+exec "${WINELOADER:-wine}" "navicat-patcher.exe" "$navicat_path"
\ No newline at end of file
diff --git a/common/Exception.hpp b/common/Exception.hpp
new file mode 100644
index 0000000..a2b5557
--- /dev/null
+++ b/common/Exception.hpp
@@ -0,0 +1,66 @@
+#pragma once
+#include
+#include "xstring.hpp"
+#include
+
+namespace nkg {
+
+ class Exception {
+ private:
+
+ PCTSTR _SourceFile;
+ SIZE_T _SourceLine;
+ PCTSTR _Message;
+ std::vector _Hints;
+
+ public:
+
+ Exception(PCTSTR SourceFile, SIZE_T SourceLine, PCTSTR CustomMessage) noexcept :
+ _SourceFile(SourceFile),
+ _SourceLine(SourceLine),
+ _Message(CustomMessage) {}
+
+ [[nodiscard]]
+ auto File() const noexcept {
+ return _SourceFile;
+ }
+
+ [[nodiscard]]
+ auto Line() const noexcept {
+ return _SourceLine;
+ }
+
+ [[nodiscard]]
+ auto Message() const noexcept {
+ return _Message;
+ }
+
+ auto& AddHint(const std::xstring& Hint) {
+ _Hints.emplace_back(Hint);
+ return *this;
+ }
+
+ [[nodiscard]]
+ const auto& Hints() const noexcept {
+ return _Hints;
+ }
+
+ [[nodiscard]]
+ virtual bool HasErrorCode() const noexcept {
+ return false;
+ }
+
+ [[nodiscard]]
+ virtual ULONG_PTR ErrorCode() const noexcept {
+ return 0;
+ }
+
+ [[nodiscard]]
+ virtual PCTSTR ErrorString() const noexcept {
+ return nullptr;
+ }
+
+ virtual ~Exception() = default;
+ };
+
+}
diff --git a/common/ExceptionOpenssl.hpp b/common/ExceptionOpenssl.hpp
new file mode 100644
index 0000000..5151748
--- /dev/null
+++ b/common/ExceptionOpenssl.hpp
@@ -0,0 +1,44 @@
+#pragma once
+#include "Exception.hpp"
+#include
+
+namespace nkg {
+
+ class OpensslError final : public Exception {
+ private:
+
+ unsigned long _ErrorCode;
+ std::xstring _ErrorString;
+
+ public:
+
+ OpensslError(PCTSTR SourceFile, SIZE_T SourceLine, unsigned long OpensslErrorCode, PCTSTR CustomMessage) noexcept :
+ Exception(SourceFile, SourceLine, CustomMessage),
+ _ErrorCode(OpensslErrorCode)
+ {
+ static bool CryptoStringsLoaded = false;
+ if (CryptoStringsLoaded == false) {
+ ERR_load_crypto_strings();
+ CryptoStringsLoaded = true;
+ }
+
+ _ErrorString = std::xstring(std::xstring_extension{}, ERR_reason_error_string(_ErrorCode), CP_UTF8);
+ }
+
+ [[nodiscard]]
+ virtual bool HasErrorCode() const noexcept override {
+ return true;
+ }
+
+ [[nodiscard]]
+ virtual ULONG_PTR ErrorCode() const noexcept override {
+ return _ErrorCode;
+ }
+
+ [[nodiscard]]
+ virtual PCTSTR ErrorString() const noexcept override {
+ return _ErrorString.c_str();
+ }
+ };
+
+}
diff --git a/common/ExceptionUser.hpp b/common/ExceptionUser.hpp
new file mode 100644
index 0000000..00a85f1
--- /dev/null
+++ b/common/ExceptionUser.hpp
@@ -0,0 +1,28 @@
+#pragma once
+#include "Exception.hpp"
+
+namespace nkg {
+
+ class UserAbortionError final : public Exception {
+ public:
+
+ UserAbortionError(PCTSTR SourceFile, SIZE_T SourceLine, PCTSTR CustomMessage) noexcept :
+ Exception(SourceFile, SourceLine, CustomMessage) {}
+
+ [[nodiscard]]
+ virtual bool HasErrorCode() const noexcept override {
+ return false;
+ }
+
+ [[nodiscard]]
+ virtual ULONG_PTR ErrorCode() const noexcept override {
+ return 0;
+ }
+
+ [[nodiscard]]
+ virtual PCTSTR ErrorString() const noexcept override {
+ return nullptr;
+ }
+ };
+
+}
\ No newline at end of file
diff --git a/common/ExceptionWin32.hpp b/common/ExceptionWin32.hpp
new file mode 100644
index 0000000..4510d9f
--- /dev/null
+++ b/common/ExceptionWin32.hpp
@@ -0,0 +1,49 @@
+#pragma once
+#include "Exception.hpp"
+
+namespace nkg {
+
+ class Win32Error final : public Exception {
+ private:
+
+ DWORD _ErrorCode;
+ std::xstring _ErrorString;
+
+ public:
+
+ Win32Error(PCTSTR SourceFile, SIZE_T SourceLine, DWORD Win32ErrorCode, PCTSTR CustomMessage) noexcept :
+ Exception(SourceFile, SourceLine, CustomMessage),
+ _ErrorCode(Win32ErrorCode)
+ {
+ PTSTR Text = NULL;
+ FormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ NULL,
+ Win32ErrorCode,
+ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
+ reinterpret_cast(&Text),
+ 0,
+ NULL
+ );
+ if (Text) {
+ _ErrorString = Text;
+ LocalFree(Text);
+ }
+ }
+
+ [[nodiscard]]
+ virtual bool HasErrorCode() const noexcept override {
+ return true;
+ }
+
+ [[nodiscard]]
+ virtual ULONG_PTR ErrorCode() const noexcept override {
+ return _ErrorCode;
+ }
+
+ [[nodiscard]]
+ virtual PCTSTR ErrorString() const noexcept override {
+ return _ErrorString.c_str();
+ }
+ };
+}
diff --git a/common/RSACipher.hpp b/common/RSACipher.hpp
new file mode 100644
index 0000000..e01c028
--- /dev/null
+++ b/common/RSACipher.hpp
@@ -0,0 +1,261 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include "Exception.hpp"
+#include "ExceptionOpenssl.hpp"
+#include "ResourceOwned.hpp"
+#include "ResourceTraitsOpenssl.hpp"
+
+#undef NKG_CURRENT_SOURCE_FILE
+#undef NKG_CURRENT_SOURCE_LINE
+#define NKG_CURRENT_SOURCE_FILE() TEXT(".\\common\\RSACipher.hpp")
+#define NKG_CURRENT_SOURCE_LINE() __LINE__
+
+namespace nkg {
+
+ enum class RSAKeyType {
+ PrivateKey,
+ PublicKey
+ };
+
+ enum class RSAKeyFormat {
+ PEM,
+ PKCS1
+ };
+
+ class RSACipher final : private ResourceOwned {
+ 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 Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("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 Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("PEM_write_bio_RSA_PUBKEY failed."));
+ }
+ }
+
+ if constexpr (__Format == RSAKeyFormat::PKCS1) {
+ if (PEM_write_bio_RSAPublicKey(lpBIO, lpRSA) == 0) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("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 Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("PEM_read_bio_RSAPrivateKey failed."))
+ .AddHint(TEXT("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 Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("PEM_read_bio_RSA_PUBKEY failed."))
+ .AddHint(TEXT("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 Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("PEM_read_bio_RSAPublicKey failed."))
+ .AddHint(TEXT("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() : ResourceOwned(RSA_new()) {
+ if (IsValid() == false) {
+ throw OpensslError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), TEXT("RSA_new failed."));
+ }
+ }
+
+ [[nodiscard]]
+ size_t Bits() const {
+ if (Get()->n == nullptr) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("RSA modulus has not been set."));
+ } else {
+ return BN_num_bits(Get()->n);
+ }
+ }
+
+ void GenerateKey(int bits, unsigned int e = RSA_F4) {
+ ResourceOwned bn_e(BN_new());
+
+ if (bn_e.IsValid() == false) {
+ throw OpensslError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), TEXT("BN_new failed."));
+ }
+
+ if (!BN_set_word(bn_e, e)) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("BN_set_word failed."));
+ }
+
+ if (!RSA_generate_key_ex(Get(), bits, bn_e, nullptr)) {
+ throw OpensslError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), TEXT("RSA_generate_key_ex failed."));
+ }
+ }
+
+ template
+ void ExportKeyToFile(const std::xstring& FileName) const {
+ ResourceOwned BioFile(BIO_new_file(FileName.explicit_string(CP_UTF8).c_str(), "w"));
+
+ if (BioFile.IsValid() == false) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("BIO_new_file failed."));
+ }
+
+ _WriteRSAToBIO<__Type, __Format>(Get(), BioFile);
+ }
+
+ template
+ [[nodiscard]]
+ std::string ExportKeyString() const {
+ ResourceOwned BioMemory(BIO_new(BIO_s_mem()));
+ long StringLength;
+ const char* StringChars = nullptr;
+
+ if (BioMemory.IsValid() == false) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("BIO_new failed."));
+ }
+
+ _WriteRSAToBIO<__Type, __Format>(Get(), BioMemory);
+
+ StringLength = BIO_get_mem_data(BioMemory.Get(), &StringChars);
+
+ return std::string(StringChars, StringLength);
+ }
+
+ template
+ void ImportKeyFromFile(const std::xstring& FileName) {
+ ResourceOwned BioFile(BIO_new_file(FileName.explicit_string(CP_UTF8).c_str(), "r"));
+
+ if (BioFile.IsValid() == false) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("BIO_new_file failed."));
+ }
+
+ TakeOver(_ReadRSAFromBIO<__Type, __Format>(BioFile));
+ }
+
+ template
+ void ImportKeyString(const std::string& KeyString) {
+ ResourceOwned BioMemory(BIO_new(BIO_s_mem()));
+
+ if (BioMemory.IsValid() == false) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("BIO_new failed."));
+ }
+
+ if (BIO_puts(BioMemory.Get(), KeyString.c_str()) <= 0) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("BIO_puts failed."));
+ }
+
+ TakeOver(_ReadRSAFromBIO<__Type, __Format>(BioMemory));
+ }
+
+ template
+ size_t Encrypt(const void* lpFrom, size_t cbFrom, void* lpTo, int Padding) const {
+ int BytesWritten;
+
+ if (cbFrom > INT_MAX) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("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 OpensslError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), TEXT("RSA_private_encrypt failed."));
+ }
+ } else {
+ BytesWritten = RSA_public_encrypt(
+ static_cast(cbFrom),
+ reinterpret_cast(lpFrom),
+ reinterpret_cast(lpTo),
+ Get(),
+ Padding
+ );
+
+ if (BytesWritten == -1) {
+ throw OpensslError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), TEXT("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 > INT_MAX) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("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 OpensslError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), TEXT("RSA_private_decrypt failed."))
+ .AddHint(TEXT("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 OpensslError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), TEXT("RSA_public_decrypt failed."))
+ .AddHint(TEXT("Are your sure you DO provide a correct public key?"));
+ }
+ }
+
+ return BytesWritten;
+ }
+ };
+
+}
diff --git a/common/ResourceOwned.hpp b/common/ResourceOwned.hpp
new file mode 100644
index 0000000..49e16c5
--- /dev/null
+++ b/common/ResourceOwned.hpp
@@ -0,0 +1,313 @@
+#pragma once
+#include
+#include
+
+template
+class ResourceOwned {
+private:
+
+ using __HandleType = typename __ResourceTraits::HandleType;
+ static_assert(std::is_pod_v<__HandleType>);
+
+ __HandleType _Handle;
+ __LambdaReleasor _Releasor;
+
+public:
+
+ ResourceOwned(__LambdaReleasor&& Releasor) noexcept :
+ _Handle(__ResourceTraits::InvalidValue),
+ _Releasor(std::forward<__LambdaReleasor>(Releasor)) {}
+
+ ResourceOwned(const __HandleType& Handle, __LambdaReleasor&& Releasor) noexcept :
+ _Handle(Handle),
+ _Releasor(std::forward<__LambdaReleasor>(Releasor)) {}
+
+ //
+ // Construct from custom releasor.
+ // `_Handle` will be set to an invalid value.
+ //
+ ResourceOwned(__ResourceTraits, __LambdaReleasor&& Releasor) noexcept :
+ _Handle(__ResourceTraits::InvalidValue),
+ _Releasor(std::forward<__LambdaReleasor>(Releasor)) {}
+
+ //
+ // Construct from handle given and custom releasor.
+ //
+ ResourceOwned(__ResourceTraits, const __HandleType& Handle, __LambdaReleasor&& Releasor) noexcept :
+ _Handle(Handle),
+ _Releasor(std::forward<__LambdaReleasor>(Releasor)) {}
+
+ //
+ // ResourceOwned doesn't allow to copy.
+ // Because it takes `_Handle` exclusively.
+ //
+ ResourceOwned(const ResourceOwned<__ResourceTraits, __LambdaReleasor>& Other) = delete;
+
+ //
+ // ResourceOwned allows to move.
+ //
+ ResourceOwned(ResourceOwned<__ResourceTraits, __LambdaReleasor>&& Other) noexcept :
+ _Handle(Other._Handle),
+ _Releasor(std::move(Other._Releasor))
+ {
+ Other._Handle = __ResourceTraits::InvalidValue;
+ }
+
+ //
+ // ResourceOwned doesn't allow to copy.
+ // Because it takes `_Handle` exclusively.
+ //
+ ResourceOwned<__ResourceTraits, __LambdaReleasor>& operator=(const ResourceOwned<__ResourceTraits, __LambdaReleasor>& Other) = delete;
+
+ //
+ // ResourceOwned allows to move.
+ //
+ ResourceOwned<__ResourceTraits, __LambdaReleasor>& operator=(ResourceOwned<__ResourceTraits, __LambdaReleasor>&& Other) noexcept {
+ _Handle = Other._Handle;
+ _Releasor = std::move(Other._Releasor);
+ Other._Handle = __ResourceTraits::InvalidValue;
+ return *this;
+ }
+
+ [[nodiscard]]
+ operator const __HandleType&() const noexcept { // NOLINT: Allow implicit conversion.
+ return _Handle;
+ }
+
+ template, typename = std::enable_if_t<__IsPointer>>
+ [[nodiscard]]
+ __AsType As() const noexcept {
+ return reinterpret_cast<__AsType>(_Handle);
+ }
+
+ template, typename = typename std::enable_if_t<__IsPointer>>
+ [[nodiscard]]
+ __HandleType operator->() const noexcept {
+ return _Handle;
+ }
+
+ [[nodiscard]]
+ bool IsValid() const noexcept {
+ return __ResourceTraits::IsValid(_Handle);
+ }
+
+ [[nodiscard]]
+ const __HandleType& Get() const noexcept {
+ return _Handle;
+ }
+
+ template
+ [[nodiscard]]
+ __ReturnType GetAddressOf() noexcept {
+ return reinterpret_cast<__ReturnType>(&_Handle);
+ }
+
+ void TakeOver(const __HandleType& Handle) {
+ if (__ResourceTraits::IsValid(_Handle) == true) {
+ _Releasor(_Handle);
+ }
+ _Handle = Handle;
+ }
+
+ void Discard() noexcept {
+ _Handle = __ResourceTraits::InvalidValue;
+ }
+
+ [[nodiscard]]
+ __HandleType Transfer() noexcept {
+ __HandleType t = _Handle;
+ _Handle = __ResourceTraits::InvalidValue;
+ return t;
+ }
+
+ void Release() {
+ if (__ResourceTraits::IsValid(_Handle)) {
+ _Releasor(_Handle);
+ _Handle = __ResourceTraits::InvalidValue;
+ }
+ }
+
+ ~ResourceOwned() {
+ if (__ResourceTraits::IsValid(_Handle)) {
+ _Releasor(_Handle);
+ _Handle = __ResourceTraits::InvalidValue;
+ }
+ }
+};
+
+template
+class ResourceOwned<__ResourceTraits, void> {
+private:
+
+ using __HandleType = typename __ResourceTraits::HandleType;
+ static_assert(std::is_pod_v<__HandleType>);
+
+ __HandleType _Handle;
+
+public:
+
+ ResourceOwned() noexcept :
+ _Handle(__ResourceTraits::InvalidValue) {}
+
+ ResourceOwned(const __HandleType& Handle) noexcept :
+ _Handle(Handle) {}
+
+ //
+ // Construct from custom releasor.
+ // `_Handle` will be set to an invalid value.
+ //
+ explicit ResourceOwned(__ResourceTraits) noexcept :
+ _Handle(__ResourceTraits::InvalidValue) {}
+
+ //
+ // Construct from handle given and custom releasor.
+ //
+ ResourceOwned(__ResourceTraits, const __HandleType& Handle) noexcept :
+ _Handle(Handle) {}
+
+ //
+ // ResourceOwned doesn't allow to copy.
+ // Because it takes `_Handle` exclusively.
+ //
+ ResourceOwned(const ResourceOwned<__ResourceTraits, void>& Other) = delete;
+
+ //
+ // ResourceOwned allows to move.
+ //
+ ResourceOwned(ResourceOwned<__ResourceTraits, void>&& Other) noexcept :
+ _Handle(Other._Handle) {
+ Other._Handle = __ResourceTraits::InvalidValue;
+ }
+
+ //
+ // ResourceOwned doesn't allow to copy.
+ // Because it takes `_Handle` exclusively.
+ //
+ ResourceOwned<__ResourceTraits, void>& operator=(const ResourceOwned<__ResourceTraits, void>& Other) = delete;
+
+ //
+ // ResourceOwned allows to move.
+ //
+ ResourceOwned<__ResourceTraits, void>& operator=(ResourceOwned<__ResourceTraits, void>&& Other) noexcept {
+ _Handle = Other._Handle;
+ Other._Handle = __ResourceTraits::InvalidValue;
+ return *this;
+ }
+
+ [[nodiscard]]
+ operator const __HandleType&() const noexcept { // NOLINT: Allow implicit conversion.
+ return _Handle;
+ }
+
+ template, typename = typename std::enable_if_t<__IsPointer>>
+ [[nodiscard]]
+ __AsType As() const noexcept {
+ return reinterpret_cast<__AsType>(_Handle);
+ }
+
+ template, typename = typename std::enable_if_t<__IsPointer>>
+ [[nodiscard]]
+ __HandleType operator->() const noexcept {
+ return _Handle;
+ }
+
+ [[nodiscard]]
+ bool IsValid() const noexcept {
+ return __ResourceTraits::IsValid(_Handle);
+ }
+
+ [[nodiscard]]
+ const __HandleType& Get() const noexcept {
+ return _Handle;
+ }
+
+ template
+ [[nodiscard]]
+ __ReturnType GetAddressOf() noexcept {
+ return reinterpret_cast<__ReturnType>(&_Handle);
+ }
+
+ void TakeOver(const __HandleType& Handle) {
+ if (__ResourceTraits::IsValid(_Handle) == true) {
+ __ResourceTraits::Releasor(_Handle);
+ }
+ _Handle = Handle;
+ }
+
+ void Discard() noexcept {
+ _Handle = __ResourceTraits::InvalidValue;
+ }
+
+ [[nodiscard]]
+ __HandleType Transfer() noexcept {
+ __HandleType t = _Handle;
+ _Handle = __ResourceTraits::InvalidValue;
+ return t;
+ }
+
+ void Release() {
+ if (__ResourceTraits::IsValid(_Handle)) {
+ __ResourceTraits::Releasor(_Handle);
+ _Handle = __ResourceTraits::InvalidValue;
+ }
+ }
+
+ ~ResourceOwned() {
+ if (__ResourceTraits::IsValid(_Handle)) {
+ __ResourceTraits::Releasor(_Handle);
+ _Handle = __ResourceTraits::InvalidValue;
+ }
+ }
+};
+
+template
+ResourceOwned(__ResourceTraits) ->
+ ResourceOwned<__ResourceTraits, void>;
+
+template
+ResourceOwned(__ResourceTraits, __ArgType&&) ->
+ ResourceOwned<
+ __ResourceTraits,
+ std::conditional_t<
+ std::is_same_v>, typename __ResourceTraits::HandleType> == false,
+ std::remove_reference_t<__ArgType>,
+ void
+ >
+ >;
+
+template
+ResourceOwned(__ResourceTraits, const typename __ResourceTraits::HandleType&, __LambdaReleasor&&) ->
+ ResourceOwned<__ResourceTraits, std::remove_reference_t<__LambdaReleasor>>;
+
+template
+struct CppObjectTraits {
+ using HandleType = __ClassType*;
+
+ static inline const HandleType InvalidValue = nullptr;
+
+ [[nodiscard]]
+ static bool IsValid(const HandleType& Handle) noexcept {
+ return Handle != InvalidValue;
+ }
+
+ static void Releasor(const HandleType& Handle) {
+ delete Handle;
+ }
+};
+
+template
+struct CppDynamicArrayTraits {
+ using HandleType = __ClassType*;
+
+ static inline const HandleType InvalidValue = nullptr;
+
+ [[nodiscard]]
+ static bool IsValid(const HandleType& Handle) noexcept {
+ return Handle != InvalidValue;
+ }
+
+ static void Releasor(const HandleType& Handle) {
+ delete[] Handle;
+ }
+};
+
diff --git a/common/ResourceTraitsOpenssl.hpp b/common/ResourceTraitsOpenssl.hpp
new file mode 100644
index 0000000..8def6dd
--- /dev/null
+++ b/common/ResourceTraitsOpenssl.hpp
@@ -0,0 +1,64 @@
+#pragma once
+#include
+#include
+
+struct OpensslBIOTraits {
+ using HandleType = BIO*;
+
+ static inline const HandleType InvalidValue = nullptr;
+
+ [[nodiscard]]
+ static bool IsValid(const HandleType& Handle) noexcept {
+ return Handle != InvalidValue;
+ }
+
+ static void Releasor(const HandleType& Handle) noexcept {
+ BIO_free(Handle);
+ }
+};
+
+struct OpensslBIOChainTraits {
+ using HandleType = BIO*;
+
+ static inline const HandleType InvalidValue = nullptr;
+
+ [[nodiscard]]
+ static bool IsValid(const HandleType& Handle) noexcept {
+ return Handle != InvalidValue;
+ }
+
+ static void Releasor(const HandleType& Handle) noexcept {
+ BIO_free_all(Handle);
+ }
+};
+
+struct OpensslBNTraits {
+ using HandleType = BIGNUM*;
+
+ static inline const HandleType InvalidValue = nullptr;
+
+ [[nodiscard]]
+ static bool IsValid(const HandleType& Handle) noexcept {
+ return Handle != InvalidValue;
+ }
+
+ static void Releasor(const HandleType& Handle) noexcept {
+ BN_free(Handle);
+ }
+};
+
+struct OpensslRSATraits {
+ using HandleType = RSA*;
+
+ static inline const HandleType InvalidValue = nullptr;
+
+ [[nodiscard]]
+ static bool IsValid(const HandleType& Handle) noexcept {
+ return Handle != InvalidValue;
+ }
+
+ static void Releasor(const HandleType& Handle) noexcept {
+ RSA_free(Handle);
+ }
+};
+
diff --git a/common/ResourceTraitsWin32.hpp b/common/ResourceTraitsWin32.hpp
new file mode 100644
index 0000000..0aef903
--- /dev/null
+++ b/common/ResourceTraitsWin32.hpp
@@ -0,0 +1,47 @@
+#pragma once
+#include
+
+struct GenericHandleTraits {
+ using HandleType = HANDLE;
+
+ static inline const HandleType InvalidValue = NULL;
+
+ [[nodiscard]]
+ static bool IsValid(const HandleType& Handle) noexcept {
+ return Handle != InvalidValue;
+ }
+
+ static void Releasor(const HandleType& Handle) noexcept {
+ CloseHandle(Handle);
+ }
+};
+
+struct FileHandleTraits {
+ using HandleType = HANDLE;
+
+ static inline const HandleType InvalidValue = INVALID_HANDLE_VALUE;
+
+ [[nodiscard]]
+ static bool IsValid(const HandleType& Handle) noexcept {
+ return Handle != InvalidValue;
+ }
+
+ static void Releasor(const HandleType& Handle) noexcept {
+ CloseHandle(Handle);
+ }
+};
+
+struct MapViewHandleTraits {
+ using HandleType = PVOID;
+
+ static inline const HandleType InvalidValue = NULL;
+
+ [[nodiscard]]
+ static bool IsValid(const HandleType& Handle) noexcept {
+ return Handle != InvalidValue;
+ }
+
+ static void Releasor(const HandleType& Handle) noexcept {
+ UnmapViewOfFile(Handle);
+ }
+};
diff --git a/common/bytearray.hpp b/common/bytearray.hpp
new file mode 100644
index 0000000..2b25308
--- /dev/null
+++ b/common/bytearray.hpp
@@ -0,0 +1,9 @@
+#pragma once
+#include
+#include
+
+namespace std {
+
+ using bytearray = vector;
+
+}
diff --git a/common/common.vcxitems b/common/common.vcxitems
new file mode 100644
index 0000000..1afa056
--- /dev/null
+++ b/common/common.vcxitems
@@ -0,0 +1,28 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ {290c5d5e-52d2-4fef-909f-27fd95ae0af9}
+
+
+
+ %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/common.vcxitems.user b/common/common.vcxitems.user
new file mode 100644
index 0000000..966b4ff
--- /dev/null
+++ b/common/common.vcxitems.user
@@ -0,0 +1,6 @@
+
+
+
+ true
+
+
\ No newline at end of file
diff --git a/common/xstring.hpp b/common/xstring.hpp
new file mode 100644
index 0000000..300a8e5
--- /dev/null
+++ b/common/xstring.hpp
@@ -0,0 +1,288 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace std {
+
+ struct xstring_extension {};
+
+#if defined(_UNICODE) || defined(UNICODE)
+ class xstring final : public wstring {
+ public:
+
+ using wstring::wstring;
+ using wstring::operator=;
+
+ xstring(const wstring& wstr) : wstring(wstr) {}
+
+ xstring(wstring&& wstr) : wstring(std::move(wstr)) {}
+
+ xstring(xstring_extension, const string& str, DWORD CodePage = CP_ACP) {
+ auto len = MultiByteToWideChar(CodePage, 0, str.c_str(), -1, NULL, 0);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ resize(static_cast(len) - 1);
+
+ len = MultiByteToWideChar(CodePage, 0, str.c_str(), -1, data(), len);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+ }
+
+ xstring(xstring_extension, const char* lpstr, DWORD CodePage = CP_ACP) {
+ auto len = MultiByteToWideChar(CodePage, 0, lpstr, -1, NULL, 0);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ resize(static_cast(len) - 1);
+
+ len = MultiByteToWideChar(CodePage, 0, lpstr, -1, data(), len);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+ }
+#else
+ class xstring final : public string {
+ public:
+
+ using string::string;
+ using string::operator=;
+
+ xstring(const string& str) : string(str) {}
+
+ xstring(string&& str) : string(std::move(str)) {}
+
+ xstring(xstring_extension, const string& str, DWORD CodePage = CP_ACP) {
+ if (CodePage == CP_ACP || CodePage == GetACP()) {
+ assign(str);
+ } else {
+ std::wstring wstr;
+
+ auto len = MultiByteToWideChar(CodePage, 0, str.c_str(), -1, NULL, 0);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ wstr.resize(len - 1);
+
+ len = MultiByteToWideChar(CodePage, 0, str.c_str(), -1, wstr.data(), len);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ resize(len - 1);
+
+ len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, data(), len, NULL, NULL);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+ }
+ }
+
+ xstring(xstring_extension, const char* lpstr, DWORD CodePage = CP_ACP) {
+ if (CodePage == CP_ACP || CodePage == GetACP()) {
+ assign(str);
+ } else {
+ std::wstring wstr;
+
+ auto len = MultiByteToWideChar(CodePage, 0, lpstr, -1, NULL, 0);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ wstr.resize(len - 1);
+
+ len = MultiByteToWideChar(CodePage, 0, lpstr, -1, wstr.data(), len);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ resize(len - 1);
+
+ len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, data(), len, NULL, NULL);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+ }
+ }
+
+ xstring(xstring_extension, const wstring& wstr) {
+ auto len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ resize(len - 1);
+
+ len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, data(), len, NULL, NULL);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+ }
+
+ xstring(xstring_extension, const wchar_t* lpwstr) {
+ auto len = WideCharToMultiByte(CP_ACP, 0, lpwstr, -1, NULL, 0, NULL, NULL);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ resize(len - 1);
+
+ len = WideCharToMultiByte(CP_ACP, 0, lpwstr, -1, data(), len, NULL, NULL);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+ }
+#endif
+
+ std::string explicit_string(DWORD CodePage = CP_ACP) const {
+#if defined(_UNICODE) || defined(UNICODE)
+ std::string str;
+
+ auto len = WideCharToMultiByte(CodePage, 0, c_str(), -1, NULL, 0, NULL, NULL);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ str.resize(static_cast(len) - 1);
+
+ len = WideCharToMultiByte(CodePage, 0, c_str(), -1, str.data(), len, NULL, NULL);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ return str;
+#else
+ if (CodePage == CP_ACP || CodePage == GetACP()) {
+ return *this;
+ } else {
+ std::string str;
+ std::wstring wstr;
+
+ auto len = MultiByteToWideChar(CP_ACP, 0, c_str(), -1, NULL, 0);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ wstr.resize(len - 1);
+
+ len = MultiByteToWideChar(CP_ACP, 0, c_str(), -1, wstr.data(), len);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ len = WideCharToMultiByte(CodePage, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ str.resize(len - 1);
+
+ len = WideCharToMultiByte(CodePage, 0, wstr.c_str(), -1, str.data(), len, NULL, NULL);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ return str;
+ }
+#endif
+ }
+
+ std::wstring explicit_wstring() const {
+#if defined(_UNICODE) || defined(UNICODE)
+ return *this;
+#else
+ std::wstring wstr;
+
+ auto len = MultiByteToWideChar(CP_ACP, 0, c_str(), -1, NULL, 0);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ wstr.resize(len - 1);
+
+ len = MultiByteToWideChar(CP_ACP, 0, c_str(), -1, wstr.data(), len);
+ if (len == 0) {
+ auto err = GetLastError();
+ throw std::system_error(err, std::system_category());
+ }
+
+ return wstr;
+#endif
+ }
+
+ template
+ static xstring format(const xstring& Format, __Ts&&... Args) {
+ xstring s;
+
+ auto len = _sctprintf(Format.c_str(), std::forward<__Ts>(Args)...);
+ if (len == -1) {
+ throw std::invalid_argument("_sctprintf failed.");
+ }
+
+ s.resize(len);
+
+ _sntprintf_s(s.data(), s.length() + 1, _TRUNCATE, Format.c_str(), std::forward<__Ts>(Args)...);
+
+ return s;
+ }
+
+ template
+ static xstring format(PCTSTR lpszFormat, __Ts&& ... Args) {
+ xstring s;
+
+ auto len = _sctprintf(lpszFormat, std::forward<__Ts>(Args)...);
+ if (len == -1) {
+ throw std::invalid_argument("_sctprintf failed.");
+ }
+
+ s.resize(len);
+
+ _sntprintf_s(s.data(), s.length() + 1, _TRUNCATE, lpszFormat, std::forward<__Ts>(Args)...);
+
+ return s;
+ }
+ };
+
+}
diff --git a/doc/how-does-it-work.md b/doc/how-does-it-work.md
new file mode 100644
index 0000000..c4ec785
--- /dev/null
+++ b/doc/how-does-it-work.md
@@ -0,0 +1,284 @@
+# 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 | |0x47 |0x4A | |
+ |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..dcd63f1
--- /dev/null
+++ b/doc/how-does-it-work.zh-CN.md
@@ -0,0 +1,282 @@
+# 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 | |0x47 |0x4A | |
+ |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..23c89b1
--- /dev/null
+++ b/doc/how-to-build.md
@@ -0,0 +1,52 @@
+# Navicat Keygen - How to build?
+
+[中文版](how-to-build.zh-CN.md)
+
+## 1. Prerequisites
+
+1. Please make sure that you have __Visual Studio 2019__ or the higher. Because this is a VS2019 project.
+
+2. Please make sure you have installed `vcpkg` and the following libraries:
+
+ * `capstone[x86]:x64-windows-static`
+ * `capstone[x86]:x86-windows-static`
+ * `openssl-windows:x64-windows-static`
+ * `openssl-windows:x86-windows-static`
+ * `rapidjson:x64-windows-static`
+ * `rapidjson:x86-windows-static`
+
+ is installed.
+
+ You can install them by:
+
+ ```console
+ $ vcpkg install capstone[x86]:x64-windows-static
+ $ vcpkg install capstone[x86]:x86-windows-static
+ $ vcpkg install openssl-windows:x64-windows-static
+ $ vcpkg install openssl-windows:x86-windows-static
+ $ vcpkg install rapidjson:x64-windows-static
+ $ vcpkg install rapidjson:x86-windows-static
+ ```
+
+3. Your `vcpkg` has been integrated into your __Visual Studio__, which means you have run
+
+ ```console
+ $ vcpkg integrate install
+ ```
+
+ successfully.
+
+## 2. Build
+
+1. Open this project in __Visual Studio__.
+
+2. Select `Release` configuration.
+
+3. Select `Win32` to build keygen/patcher for 32-bits Navicat.
+
+ Or select `x64` to build keygen/patcher for 64-bits Navicat.
+
+4. Select __Build > Build Solution__.
+
+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..6e378e4
--- /dev/null
+++ b/doc/how-to-build.zh-CN.md
@@ -0,0 +1,46 @@
+# Navicat Keygen - 如何编译?
+
+## 1. 前提条件
+
+1. 请确保你有 __Visual Studio 2019__ 或者更高版本。因为这是一个VS2019项目。
+
+2. 请确保你安装了 `vcpkg` 以及下面几个库:
+
+ * `capstone[x86]:x64-windows-static`
+ * `capstone[x86]:x86-windows-static`
+ * `openssl-windows:x64-windows-static`
+ * `openssl-windows:x86-windows-static`
+ * `rapidjson:x64-windows-static`
+ * `rapidjson:x86-windows-static`
+
+ 你可以通过下面的命令来安装它们:
+
+ ```console
+ $ vcpkg install capstone[x86]:x64-windows-static
+ $ vcpkg install capstone[x86]:x86-windows-static
+ $ vcpkg install openssl-windows:x64-windows-static
+ $ vcpkg install openssl-windows:x86-windows-static
+ $ vcpkg install rapidjson:x64-windows-static
+ $ vcpkg install rapidjson:x86-windows-static
+ ```
+
+3. 你的 `vcpkg` 已经和你的 __Visual Studio__ 集成了,即你曾成功运行了:
+
+ ```console
+ $ vcpkg integrate install
+ ```
+
+## 2. 编译
+
+1. 在 __Visual Studio__ 打开这个项目。
+
+2. 选择 `Release` 配置。
+
+3. 选择 `Win32` 来生成供32位Navicat使用的keygen/patcher。
+
+ 或者选择 `x64` 来生成供64位Navicat使用的keygen/patcher。
+
+4. 选择 __生成 > 生成解决方案__。
+
+生成完成后,你会在 `bin/` 文件夹下看到编译后的keygen/patcher。
+
diff --git a/doc/how-to-use.linux.md b/doc/how-to-use.linux.md
new file mode 100644
index 0000000..1df1ad2
--- /dev/null
+++ b/doc/how-to-use.linux.md
@@ -0,0 +1,257 @@
+# Navicat Keygen - How to use? (Linux)
+
+[中文版](how-to-use.linux.zh-CN.md)
+
+* Maintainer(s): zenuo, DoubleLabyrinth
+
+---
+
+> You can download [Screen recoding](image/Screen_recording.mp4) for references.
+
+1. Switch to the path you extracted the installation package. Run Navicat, for initialization:
+
+ ```console
+ $ cd ~/navicat121_premium_en_x64 && \
+ ./start_navicat
+ ```
+
+ When running for the first time, you will be prompted with the following two windows, click "Cancel" to:
+
+ 
+
+ 
+
+ Until the `Registration` window appears, select `Trial`, close Navicat after loading is complete, and execute `Step 2`:
+
+ 
+
+2. Download the latest release [from here](https://github.com/DoubleLabyrinth/navicat-keygen/releases), and extract:
+
+ > The 64-bit executable file is downloaded here. If you use 32-bit, please download the corresponding version.
+
+ ```console
+ $ curl -O -L https://github.com/DoubleLabyrinth/navicat-keygen/releases/download/v3.1/navicat-keygen-for-x64.zip && \
+ unzip navicat-keygen-for-x64.zip
+ ```
+
+3. Download `navicat-pacther.sh` and `navicat-keygen.sh`:
+
+ ```console
+ $ curl -O -L https://raw.githubusercontent.com/DoubleLabyrinth/navicat-keygen/windows/bash/navicat-patcher.sh && \
+ chmod +x navicat-patcher.sh && \
+ curl -O -L https://raw.githubusercontent.com/DoubleLabyrinth/navicat-keygen/windows/bash/navicat-keygen.sh && \
+ chmod +x navicat-keygen.sh
+ ```
+
+4. Use `navicat-patcher.exe` to replace __Navicat Activation Public Key__ that is stored in `navicat.exe` or `libcc.dll`.
+
+ > Please turn off `Navicat` when performing this step.
+
+ ```console
+ $ ./navicat-patcher.sh
+ ```
+
+ It has been tested on __Navicat Premium 12.1.22 Simplified Chinese version__. The following is an example of output:
+
+ ```
+ ***************************************************
+ * Navicat Patcher by @DoubleLabyrinth *
+ * Version: 4.0 *
+ ***************************************************
+
+ Press Enter to continue or Ctrl + C to abort.
+
+ [+] Try to open Navicat.exe ... Ok!
+ [+] Try to open libcc.dll ... Ok!
+
+ [+] PatchSolution0 ...... Ready to apply
+ [*] Patch offset = +0x029bccd8
+ [+] PatchSolution1 ...... Ready to apply
+ [*] [0] Patch offset = +0x02206c00
+ [*] [1] Patch offset = +0x0074c489
+ [*] [2] Patch offset = +0x02206910
+ [*] [3] Patch offset = +0x0074c46f
+ [*] [4] Patch offset = +0x02206904
+ [-] PatchSolution2 ...... Omitted
+ [+] PatchSolution3 ...... Ready to apply
+ [*] [ 0] Instruction RVA = 0x016539c8, Patch Offset = +0x023e64d4
+ [*] [ 1] Instruction RVA = 0x01653a1f, Patch Offset = +0x01652e23
+ [*] [ 2] Instruction RVA = 0x01653a25, Patch Offset = +0x01652e28
+ [*] [ 3] Instruction RVA = 0x01653a8c, Patch Offset = +0x01652e8e
+ ...
+ ...
+ ...
+ [*] [108] Instruction RVA = 0x016604e1, Patch Offset = +0x023e66d8
+ [*] [109] Instruction RVA = 0x01660518, Patch Offset = +0x0165f91c
+ [*] [110] Instruction RVA = 0x0166051e, Patch Offset = +0x0165f921
+
+ [*] PatchSolution0 is suppressed in order to keep digital signature valid.
+
+ [*] Generating new RSA private key, it may take a long time...
+ [*] Your RSA public key:
+ -----BEGIN PUBLIC KEY-----
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1hV66HgU4LrKXWW6O7bK
+ AN6ZTr5W+Mq8ClTQ+Pc+BdhLu6rww55kVq7OXKGpvx0G4eTafYMGrrBETgDSTaMq
+ Bx+8bZbGBWh2LtNfqU+xUrpHHBSz0ByBc3iTEzzthJl+Fzf8suDX2lWYIc/Ym/eW
+ YtxdJ7xOzLb68z4N0zVmA0jFX2FOm75DRYgKqy4SGixapfucL9dVaWVLTUdbrVdj
+ 4LX78t4t5ykbYoThrat4yuLvj/BxLaQ6ivKD+ScfHdtCoY+NA5jmBoUfBq3Q1SXB
+ iNaoXctbi0/H3MiPu0cRojryAocooF89yFm5/mNnzWGAYPr6DvBI8CDTZmjaQ4oC
+ aQIDAQAB
+ -----END PUBLIC KEY-----
+
+ *******************************************************
+ * PatchSolution1 *
+ *******************************************************
+ [*] Previous:
+ +0x0000000002206c00 44 37 35 31 32 35 42 37 30 37 36 37 42 39 34 31 D75125B70767B941
+ +0x0000000002206c10 34 35 42 34 37 43 31 43 42 33 43 30 37 35 35 45 45B47C1CB3C0755E
+ +0x0000000002206c20 37 43 43 42 38 38 32 35 43 35 44 43 45 30 43 35 7CCB8825C5DCE0C5
+ ...
+ ...
+ [*] After:
+ +0x0000000002206c00 33 43 32 39 30 39 35 38 33 34 38 41 42 43 35 39 3C290958348ABC59
+ +0x0000000002206c10 36 44 39 30 43 45 45 38 31 36 42 36 39 38 34 44 6D90CEE816B6984D
+ +0x0000000002206c20 35 32 35 34 37 45 30 32 34 31 42 36 42 43 31 41 52547E0241B6BC1A
+ ...
+ ...
+
+ [*] Previous:
+ +0x000000000074c480 fe ea bc 01 ....
+ [*] After:
+ +0x000000000074c480 08 00 00 00 ....
+
+ [*] Previous:
+ +0x0000000002206910 45 31 43 45 44 30 39 42 39 43 32 31 38 36 42 46 E1CED09B9C2186BF
+ +0x0000000002206920 37 31 41 37 30 43 30 46 45 32 46 31 45 30 41 45 71A70C0FE2F1E0AE
+ +0x0000000002206930 46 33 42 44 36 42 37 35 32 37 37 41 41 42 32 30 F3BD6B75277AAB20
+ ...
+ ...
+ [*] After:
+ +0x0000000002206910 41 33 39 42 41 36 43 34 31 36 33 32 35 30 46 45 A39BA6C4163250FE
+ +0x0000000002206920 42 32 41 39 31 41 34 32 46 44 42 46 30 41 32 31 B2A91A42FDBF0A21
+ +0x0000000002206930 33 34 46 34 36 44 43 45 34 30 42 46 41 42 33 35 34F46DCE40BFAB35
+ ...
+ ...
+
+ [*] Previous:
+ +0x000000000074c460 59 Y
+ +0x000000000074c470 08 01 00 ...
+ [*] After:
+ +0x000000000074c460 06 .
+ +0x000000000074c470 00 00 00 ...
+
+ [*] Previous:
+ +0x0000000002206900 39 32 39 33 33 92933
+ [*] After:
+ +0x0000000002206900 42 34 34 33 38 B4438
+
+ *******************************************************
+ * PatchSolution3 *
+ *******************************************************
+ [*] +023e64d4: 4d 49 49 ---> 4d 49 49
+ [*] +01652e23: 42 49 ---> 42 49
+ [*] +01652e28: 6a ---> 6a
+ ...
+ ...
+ ...
+ [*] +023e66d8: 77 49 44 41 ---> 51 49 44 41
+ [*] +0165f91c: 51 41 ---> 51 41
+ [*] +0165f921: 42 ---> 42
+
+ [*] New RSA-2048 private key has been saved to
+ C:\Users\DoubleSine\github.com\navicat-keygen\bin\x64-Release\RegPrivateKey.pem
+
+ *******************************************************
+ * PATCH HAS BEEN DONE SUCCESSFULLY! *
+ * HAVE FUN AND ENJOY~ *
+ *******************************************************
+ ```
+
+5. Then use `navicat-keygen.exe` to generate __snKey__ and __Activation Code__
+
+ ```console
+ $ ./navicat-keygen.sh
+ ```
+
+ You will be asked to select Navicat product, language and input major version number. After that an randomly generated __snKey__ will be given.
+
+ ```
+ 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)> 1
+
+ (Input major version number, range: 0 ~ 15, default: 12)> 12
+
+ Serial number:
+ NAVO-2ORP-IN5A-GQEE
+
+ 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.
+
+ ```
+ Your name: DoubleLabyrinth
+ Your organization: DoubleLabyrinth
+ Input request code (in Base64), input empty line to end:
+ ```
+
+ After that, you will be asked to input the request code. Now __DO NOT CLOSE KEYGEN__.
+
+6. __Set up__ a invalid proxy. Find and click `Registration`. Fill `Registration Key` by __snKey__ that the keygen gave and click `Activate`.
+
+7. Online activation will failed and Navicat will ask you do `Manual Activation`, just choose it.
+
+8. Copy your request code and paste it in the keygen. Input empty line to tell the keygen that your input ends.
+
+ ```
+ Your name: DoubleLabyrinth
+ Your organization: DoubleLabyrinth
+
+ Input request code (in Base64), input empty line to end:
+ t2U+0yfE2FfnbjyhCXa0lglZOHu9Ntc3qyGiPbR6xb1QoU63/9BVfdaCq0blwVycXPyT/Vqw5joIKdM5oCRR/afCPM7iRcyhQMAnvqwc+AOKCqayVV+SqKLvtR/AbREI12w++PQ6Ewfs4A8PgB8OJ9G0jKt6Q/iJRblqi2WWw9mwy+YHcYYh3UAfygTnyj/xl+MzRymbY0lkus+6LPtpDecVsFFhM7F32Ee1QPwISko7bAkHOtkt+joPfYDdn9PDGZ4HEmeLvH6UqZCXkzgaAfynB7cQZFEkId8FsW2NGkbpM7wB2Hi3fNFgOIjutTprixTdbpFKn4w6gGc28ve23A==
+
+ Request Info:
+ {"K":"NAVO2ORPIN5AGQEE", "DI":"R91j6WyMhxHznAKSxxxx", "P":"WIN"}
+
+ Response Info:
+ {"K":"NAVO2ORPIN5AGQEE","DI":"R91j6WyMhxHznAKSxxxx","N":"DoubleLabyrinth","O":"DoubleLabyrinth","T":1547826060}
+
+ License:
+ lRF18o+ZhBphyN0U5kFLHtAAGGXuvhqOcxNuvAk4dJcGeR0ISuw74mQvAfdNjv0T
+ I5NZFzqIJvrzM0XeR88q+3kmZkECuxwwWHP3zzDPhPiylcTV4DoGZ1tfoViUSYQc
+ LgXG0Fl7koZeP61YOKQ8GfX+Xk2ZTM64bYaF7NlhonM+GQUJCCF2JThmrP921t2p
+ b/E5pV6fLOYMM13881ZQcQcltMNVDZn4lzgzKRFFxCQFaTl6fJMHZdYVmICQTHtI
+ sNaym0zduc8/cv34mgJ+7NseXmsEPCdjrZ59wgfPsLhZLXqtfxi5hGWw4NMa3Sb2
+ UI8dzqFzRp/hSDEM0mEqiA==
+ ```
+
+9. 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. Don't forget to close the proxy that we just set up.
+
diff --git a/doc/how-to-use.linux.zh-CN.md b/doc/how-to-use.linux.zh-CN.md
new file mode 100644
index 0000000..1327af6
--- /dev/null
+++ b/doc/how-to-use.linux.zh-CN.md
@@ -0,0 +1,255 @@
+# Navicat Keygen - 如何使用这个注册机? (Linux)
+
+* 维护者:zenuo, DoubleLabyrinth
+
+---
+
+> 可下载[录屏文件](image/Screen_recording.mp4)参考
+
+1. 切换到解压安装包的路径,本示例解压到了 `家目录`,运行Navicat,使其初始化环境:
+
+ ```console
+ $ cd ~/navicat121_premium_en_x64 && \
+ ./start_navicat
+ ```
+
+ 首次启动时,会提示如下两个窗口,点击“Cancel”即可:
+
+ 
+
+ 
+
+ 直至出现 `Registration` 窗口,选择 `Trial`,待加载完成后关闭Navicat,执行 `步骤2`:
+
+ 
+
+2. [从这里](https://github.com/DoubleLabyrinth/navicat-keygen/releases)下载最新的release,并且解压:
+
+ > 此处下载的是64位的可执行文件,若您使用32位,请下载对应版本
+
+ ```console
+ $ curl -O -L https://github.com/DoubleLabyrinth/navicat-keygen/releases/download/v3.1/navicat-keygen-for-x64.zip && \
+ unzip navicat-keygen-for-x64.zip
+ ```
+
+3. 下载 `navicat-pacther.sh` 和 `navicat-keygen.sh`:
+
+ ```console
+ $ curl -O -L https://raw.githubusercontent.com/DoubleLabyrinth/navicat-keygen/windows/bash/navicat-patcher.sh && \
+ chmod +x navicat-patcher.sh && \
+ curl -O -L https://raw.githubusercontent.com/DoubleLabyrinth/navicat-keygen/windows/bash/navicat-keygen.sh && \
+ chmod +x navicat-keygen.sh
+ ```
+
+4. 使用 `navicat-patcher.exe` 替换掉 `navicat.exe` 和 `libcc.dll` 里的Navicat激活公钥。
+
+ > 执行此步骤时,请将Navicat关闭
+
+ ```console
+ $ ./navicat-patcher.sh
+ ```
+
+ __Navicat Premium 12.1.22 简体中文版已通过测试__。下面将是一份样例输出:
+
+ ```
+ ***************************************************
+ * Navicat Patcher by @DoubleLabyrinth *
+ * Version: 4.0 *
+ ***************************************************
+
+ Press Enter to continue or Ctrl + C to abort.
+
+ [+] Try to open Navicat.exe ... Ok!
+ [+] Try to open libcc.dll ... Ok!
+
+ [+] PatchSolution0 ...... Ready to apply
+ [*] Patch offset = +0x029bccd8
+ [+] PatchSolution1 ...... Ready to apply
+ [*] [0] Patch offset = +0x02206c00
+ [*] [1] Patch offset = +0x0074c489
+ [*] [2] Patch offset = +0x02206910
+ [*] [3] Patch offset = +0x0074c46f
+ [*] [4] Patch offset = +0x02206904
+ [-] PatchSolution2 ...... Omitted
+ [+] PatchSolution3 ...... Ready to apply
+ [*] [ 0] Instruction RVA = 0x016539c8, Patch Offset = +0x023e64d4
+ [*] [ 1] Instruction RVA = 0x01653a1f, Patch Offset = +0x01652e23
+ [*] [ 2] Instruction RVA = 0x01653a25, Patch Offset = +0x01652e28
+ [*] [ 3] Instruction RVA = 0x01653a8c, Patch Offset = +0x01652e8e
+ ...
+ ...
+ ...
+ [*] [108] Instruction RVA = 0x016604e1, Patch Offset = +0x023e66d8
+ [*] [109] Instruction RVA = 0x01660518, Patch Offset = +0x0165f91c
+ [*] [110] Instruction RVA = 0x0166051e, Patch Offset = +0x0165f921
+
+ [*] PatchSolution0 is suppressed in order to keep digital signature valid.
+
+ [*] Generating new RSA private key, it may take a long time...
+ [*] Your RSA public key:
+ -----BEGIN PUBLIC KEY-----
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1hV66HgU4LrKXWW6O7bK
+ AN6ZTr5W+Mq8ClTQ+Pc+BdhLu6rww55kVq7OXKGpvx0G4eTafYMGrrBETgDSTaMq
+ Bx+8bZbGBWh2LtNfqU+xUrpHHBSz0ByBc3iTEzzthJl+Fzf8suDX2lWYIc/Ym/eW
+ YtxdJ7xOzLb68z4N0zVmA0jFX2FOm75DRYgKqy4SGixapfucL9dVaWVLTUdbrVdj
+ 4LX78t4t5ykbYoThrat4yuLvj/BxLaQ6ivKD+ScfHdtCoY+NA5jmBoUfBq3Q1SXB
+ iNaoXctbi0/H3MiPu0cRojryAocooF89yFm5/mNnzWGAYPr6DvBI8CDTZmjaQ4oC
+ aQIDAQAB
+ -----END PUBLIC KEY-----
+
+ *******************************************************
+ * PatchSolution1 *
+ *******************************************************
+ [*] Previous:
+ +0x0000000002206c00 44 37 35 31 32 35 42 37 30 37 36 37 42 39 34 31 D75125B70767B941
+ +0x0000000002206c10 34 35 42 34 37 43 31 43 42 33 43 30 37 35 35 45 45B47C1CB3C0755E
+ +0x0000000002206c20 37 43 43 42 38 38 32 35 43 35 44 43 45 30 43 35 7CCB8825C5DCE0C5
+ ...
+ ...
+ [*] After:
+ +0x0000000002206c00 33 43 32 39 30 39 35 38 33 34 38 41 42 43 35 39 3C290958348ABC59
+ +0x0000000002206c10 36 44 39 30 43 45 45 38 31 36 42 36 39 38 34 44 6D90CEE816B6984D
+ +0x0000000002206c20 35 32 35 34 37 45 30 32 34 31 42 36 42 43 31 41 52547E0241B6BC1A
+ ...
+ ...
+
+ [*] Previous:
+ +0x000000000074c480 fe ea bc 01 ....
+ [*] After:
+ +0x000000000074c480 08 00 00 00 ....
+
+ [*] Previous:
+ +0x0000000002206910 45 31 43 45 44 30 39 42 39 43 32 31 38 36 42 46 E1CED09B9C2186BF
+ +0x0000000002206920 37 31 41 37 30 43 30 46 45 32 46 31 45 30 41 45 71A70C0FE2F1E0AE
+ +0x0000000002206930 46 33 42 44 36 42 37 35 32 37 37 41 41 42 32 30 F3BD6B75277AAB20
+ ...
+ ...
+ [*] After:
+ +0x0000000002206910 41 33 39 42 41 36 43 34 31 36 33 32 35 30 46 45 A39BA6C4163250FE
+ +0x0000000002206920 42 32 41 39 31 41 34 32 46 44 42 46 30 41 32 31 B2A91A42FDBF0A21
+ +0x0000000002206930 33 34 46 34 36 44 43 45 34 30 42 46 41 42 33 35 34F46DCE40BFAB35
+ ...
+ ...
+
+ [*] Previous:
+ +0x000000000074c460 59 Y
+ +0x000000000074c470 08 01 00 ...
+ [*] After:
+ +0x000000000074c460 06 .
+ +0x000000000074c470 00 00 00 ...
+
+ [*] Previous:
+ +0x0000000002206900 39 32 39 33 33 92933
+ [*] After:
+ +0x0000000002206900 42 34 34 33 38 B4438
+
+ *******************************************************
+ * PatchSolution3 *
+ *******************************************************
+ [*] +023e64d4: 4d 49 49 ---> 4d 49 49
+ [*] +01652e23: 42 49 ---> 42 49
+ [*] +01652e28: 6a ---> 6a
+ ...
+ ...
+ ...
+ [*] +023e66d8: 77 49 44 41 ---> 51 49 44 41
+ [*] +0165f91c: 51 41 ---> 51 41
+ [*] +0165f921: 42 ---> 42
+
+ [*] New RSA-2048 private key has been saved to
+ C:\Users\DoubleSine\github.com\navicat-keygen\bin\x64-Release\RegPrivateKey.pem
+
+ *******************************************************
+ * PATCH HAS BEEN DONE SUCCESSFULLY! *
+ * HAVE FUN AND ENJOY~ *
+ *******************************************************
+ ```
+
+5. 接下来使用`navicat-keygen.exe`来生成序列号和激活码
+
+ ```console
+ $ ./navicat-keygen.sh
+ ```
+
+ 你会被要求选择Navicat产品类别、语言以及输入主版本号。之后会随机生成一个序列号。
+
+ ```
+ 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)> 1
+
+ (Input major version number, range: 0 ~ 15, default: 12)> 12
+
+ Serial number:
+ NAVO-2ORP-IN5A-GQEE
+
+ Your name:
+ ```
+
+ 你可以使用这个序列号暂时激活Navicat。
+
+ 接下来你会被要求输入`用户名`和`组织名`;请随便填写,但不要太长。
+
+ ```
+ Your name: DoubleLabyrinth
+ Your organization: DoubleLabyrinth
+ Input request code (in Base64), input empty line to end:
+ ```
+
+ 之后你会被要求填入请求码。注意 __不要关闭命令行__.
+
+6. 配置一个不存在的`代理`。找到`注册`窗口,并填入keygen给你的序列号。然后点击`激活`按钮。
+
+7. 在线激活失败,这时候Navicat会询问你是否`手动激活`,直接选吧。
+
+8. 在`手动激活`窗口你会得到一个请求码,复制它并把它粘贴到keygen里。最后别忘了连按至少两下回车结束输入。
+
+ ```
+ Your name: DoubleLabyrinth
+ Your organization: DoubleLabyrinth
+
+ Input request code (in Base64), input empty line to end:
+ t2U+0yfE2FfnbjyhCXa0lglZOHu9Ntc3qyGiPbR6xb1QoU63/9BVfdaCq0blwVycXPyT/Vqw5joIKdM5oCRR/afCPM7iRcyhQMAnvqwc+AOKCqayVV+SqKLvtR/AbREI12w++PQ6Ewfs4A8PgB8OJ9G0jKt6Q/iJRblqi2WWw9mwy+YHcYYh3UAfygTnyj/xl+MzRymbY0lkus+6LPtpDecVsFFhM7F32Ee1QPwISko7bAkHOtkt+joPfYDdn9PDGZ4HEmeLvH6UqZCXkzgaAfynB7cQZFEkId8FsW2NGkbpM7wB2Hi3fNFgOIjutTprixTdbpFKn4w6gGc28ve23A==
+
+ Request Info:
+ {"K":"NAVO2ORPIN5AGQEE", "DI":"R91j6WyMhxHznAKSxxxx", "P":"WIN"}
+
+ Response Info:
+ {"K":"NAVO2ORPIN5AGQEE","DI":"R91j6WyMhxHznAKSxxxx","N":"DoubleLabyrinth","O":"DoubleLabyrinth","T":1547826060}
+
+ License:
+ lRF18o+ZhBphyN0U5kFLHtAAGGXuvhqOcxNuvAk4dJcGeR0ISuw74mQvAfdNjv0T
+ I5NZFzqIJvrzM0XeR88q+3kmZkECuxwwWHP3zzDPhPiylcTV4DoGZ1tfoViUSYQc
+ LgXG0Fl7koZeP61YOKQ8GfX+Xk2ZTM64bYaF7NlhonM+GQUJCCF2JThmrP921t2p
+ b/E5pV6fLOYMM13881ZQcQcltMNVDZn4lzgzKRFFxCQFaTl6fJMHZdYVmICQTHtI
+ sNaym0zduc8/cv34mgJ+7NseXmsEPCdjrZ59wgfPsLhZLXqtfxi5hGWw4NMa3Sb2
+ UI8dzqFzRp/hSDEM0mEqiA==
+ ```
+
+9. 如果不出意外,你会得到一个看似用Base64编码的激活码。直接复制它,并把它粘贴到Navicat的`手动激活`窗口,最后点`激活`按钮。如果没什么意外的话应该能成功激活。别忘了关闭我们刚刚设置的不存在的代理哦。
+
diff --git a/doc/how-to-use.windows.md b/doc/how-to-use.windows.md
new file mode 100644
index 0000000..ceb2a5f
--- /dev/null
+++ b/doc/how-to-use.windows.md
@@ -0,0 +1,256 @@
+# Navicat Keygen - How to use? (Windows)
+
+[中文版](how-to-use.windows.zh-CN.md)
+
+1. Download the latest release [from here](https://github.com/DoubleLabyrinth/navicat-keygen/releases).
+
+2. Use `navicat-patcher.exe` to replace __Navicat Activation Public Key__ that is stored in `navicat.exe` or `libcc.dll`.
+
+ ```
+ navicat-patcher.exe [-dry-run] [RSA-2048 PEM File Path]
+ ```
+
+ * `[-dry-run]` Run patcher without applying any patches.
+
+ __This parameter is optional.__
+
+ * ``: The full path to Navicat installation folder.
+
+ __This parameter must be specified.__
+
+ * `[RSA-2048 PEM File Path]`: The full path or relative path to a RSA-2048 private key file.
+
+ __This parameter is optional.__ If not specified, `navicat-patcher.exe` will generate a new RSA-2048 private key file `RegPrivateKey.pem` at current directory.
+
+ __Example: (in cmd.exe)__
+
+ ```
+ navicat-patcher.exe "C:\Program Files\PremiumSoft\Navicat Premium 12"
+ ```
+
+ It has been tested on __Navicat Premium 12.1.22 Simplified Chinese version__. The following is an example of output.
+
+ ```
+ ***************************************************
+ * Navicat Patcher by @DoubleLabyrinth *
+ * Version: 4.0 *
+ ***************************************************
+
+ Press Enter to continue or Ctrl + C to abort.
+
+ [+] Try to open Navicat.exe ... Ok!
+ [+] Try to open libcc.dll ... Ok!
+
+ [+] PatchSolution0 ...... Ready to apply
+ [*] Patch offset = +0x029bccd8
+ [+] PatchSolution1 ...... Ready to apply
+ [*] [0] Patch offset = +0x02206c00
+ [*] [1] Patch offset = +0x0074c489
+ [*] [2] Patch offset = +0x02206910
+ [*] [3] Patch offset = +0x0074c46f
+ [*] [4] Patch offset = +0x02206904
+ [-] PatchSolution2 ...... Omitted
+ [+] PatchSolution3 ...... Ready to apply
+ [*] [ 0] Instruction RVA = 0x016539c8, Patch Offset = +0x023e64d4
+ [*] [ 1] Instruction RVA = 0x01653a1f, Patch Offset = +0x01652e23
+ [*] [ 2] Instruction RVA = 0x01653a25, Patch Offset = +0x01652e28
+ [*] [ 3] Instruction RVA = 0x01653a8c, Patch Offset = +0x01652e8e
+ ...
+ ...
+ ...
+ [*] [108] Instruction RVA = 0x016604e1, Patch Offset = +0x023e66d8
+ [*] [109] Instruction RVA = 0x01660518, Patch Offset = +0x0165f91c
+ [*] [110] Instruction RVA = 0x0166051e, Patch Offset = +0x0165f921
+
+ [*] PatchSolution0 is suppressed in order to keep digital signature valid.
+
+ [*] Generating new RSA private key, it may take a long time...
+ [*] Your RSA public key:
+ -----BEGIN PUBLIC KEY-----
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1hV66HgU4LrKXWW6O7bK
+ AN6ZTr5W+Mq8ClTQ+Pc+BdhLu6rww55kVq7OXKGpvx0G4eTafYMGrrBETgDSTaMq
+ Bx+8bZbGBWh2LtNfqU+xUrpHHBSz0ByBc3iTEzzthJl+Fzf8suDX2lWYIc/Ym/eW
+ YtxdJ7xOzLb68z4N0zVmA0jFX2FOm75DRYgKqy4SGixapfucL9dVaWVLTUdbrVdj
+ 4LX78t4t5ykbYoThrat4yuLvj/BxLaQ6ivKD+ScfHdtCoY+NA5jmBoUfBq3Q1SXB
+ iNaoXctbi0/H3MiPu0cRojryAocooF89yFm5/mNnzWGAYPr6DvBI8CDTZmjaQ4oC
+ aQIDAQAB
+ -----END PUBLIC KEY-----
+
+ *******************************************************
+ * PatchSolution1 *
+ *******************************************************
+ [*] Previous:
+ +0x0000000002206c00 44 37 35 31 32 35 42 37 30 37 36 37 42 39 34 31 D75125B70767B941
+ +0x0000000002206c10 34 35 42 34 37 43 31 43 42 33 43 30 37 35 35 45 45B47C1CB3C0755E
+ +0x0000000002206c20 37 43 43 42 38 38 32 35 43 35 44 43 45 30 43 35 7CCB8825C5DCE0C5
+ ...
+ ...
+ [*] After:
+ +0x0000000002206c00 33 43 32 39 30 39 35 38 33 34 38 41 42 43 35 39 3C290958348ABC59
+ +0x0000000002206c10 36 44 39 30 43 45 45 38 31 36 42 36 39 38 34 44 6D90CEE816B6984D
+ +0x0000000002206c20 35 32 35 34 37 45 30 32 34 31 42 36 42 43 31 41 52547E0241B6BC1A
+ ...
+ ...
+
+ [*] Previous:
+ +0x000000000074c480 fe ea bc 01 ....
+ [*] After:
+ +0x000000000074c480 08 00 00 00 ....
+
+ [*] Previous:
+ +0x0000000002206910 45 31 43 45 44 30 39 42 39 43 32 31 38 36 42 46 E1CED09B9C2186BF
+ +0x0000000002206920 37 31 41 37 30 43 30 46 45 32 46 31 45 30 41 45 71A70C0FE2F1E0AE
+ +0x0000000002206930 46 33 42 44 36 42 37 35 32 37 37 41 41 42 32 30 F3BD6B75277AAB20
+ ...
+ ...
+ [*] After:
+ +0x0000000002206910 41 33 39 42 41 36 43 34 31 36 33 32 35 30 46 45 A39BA6C4163250FE
+ +0x0000000002206920 42 32 41 39 31 41 34 32 46 44 42 46 30 41 32 31 B2A91A42FDBF0A21
+ +0x0000000002206930 33 34 46 34 36 44 43 45 34 30 42 46 41 42 33 35 34F46DCE40BFAB35
+ ...
+ ...
+
+ [*] Previous:
+ +0x000000000074c460 59 Y
+ +0x000000000074c470 08 01 00 ...
+ [*] After:
+ +0x000000000074c460 06 .
+ +0x000000000074c470 00 00 00 ...
+
+ [*] Previous:
+ +0x0000000002206900 39 32 39 33 33 92933
+ [*] After:
+ +0x0000000002206900 42 34 34 33 38 B4438
+
+ *******************************************************
+ * PatchSolution3 *
+ *******************************************************
+ [*] +023e64d4: 4d 49 49 ---> 4d 49 49
+ [*] +01652e23: 42 49 ---> 42 49
+ [*] +01652e28: 6a ---> 6a
+ ...
+ ...
+ ...
+ [*] +023e66d8: 77 49 44 41 ---> 51 49 44 41
+ [*] +0165f91c: 51 41 ---> 51 41
+ [*] +0165f921: 42 ---> 42
+
+ [*] New RSA-2048 private key has been saved to
+ C:\Users\DoubleSine\github.com\navicat-keygen\bin\x64-Release\RegPrivateKey.pem
+
+ *******************************************************
+ * PATCH HAS BEEN DONE SUCCESSFULLY! *
+ * HAVE FUN AND ENJOY~ *
+ *******************************************************
+ ```
+
+3. Then use `navicat-keygen.exe` to generate __snKey__ and __Activation Code__
+
+ ```
+ navicat-keygen.exe <-bin|-text> [-adv]
+ ```
+
+ * `<-bin|-text>`: Must be `-bin` or `-text`.
+
+ If `-bin` is specified, `navicat-keygen.exe` will finally generate `license_file`. It is used for Navicat old activation method only.
+
+ If `-text` is specified, `navicat-keygen.exe` will finally generate a Base64-style string which is __Activation Code__. It is used for Navicat new activation method.
+
+ __This parameter must be specified.__
+
+ * `[-adv]`: Enable advanced mode.
+
+ __This parameter is optional.__ If specified, `navicat-keygen.exe` will ask you input Navicat product ID number, language signature numbers. It is for future use generally.
+
+ * ``: The full path or relative path to a RSA-2048 private key file.
+
+ __This parameter must be specified.__
+
+ __Example: (in cmd.exe)__
+
+ ```console
+ navicat-keygen.exe -text .\RegPrivateKey.pem
+ ```
+
+ You will be asked to select Navicat product, language and input major version number. After that an randomly generated __snKey__ will be given.
+
+ ```
+ 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)> 1
+
+ (Input major version number, range: 0 ~ 15, default: 12)> 12
+
+ Serial number:
+ NAVO-2ORP-IN5A-GQEE
+
+ 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.
+
+ ```
+ Your name: DoubleLabyrinth
+ Your organization: DoubleLabyrinth
+ Input request code (in Base64), input empty line to end:
+ ```
+
+ After that, you will be asked to input the request code. Now __DO NOT CLOSE KEYGEN__.
+
+4. __Disconnect your network__ and open Navicat. Find and click `Registration`. Fill `Registration Key` by __snKey__ that the keygen gave and click `Activate`.
+
+5. Generally online activation will failed and Navicat will ask you do `Manual Activation`, just choose it.
+
+6. Copy your request code and paste it in the keygen. Input empty line to tell the keygen that your input ends.
+
+ ```
+ Your name: DoubleLabyrinth
+ Your organization: DoubleLabyrinth
+
+ Input request code (in Base64), input empty line to end:
+ t2U+0yfE2FfnbjyhCXa0lglZOHu9Ntc3qyGiPbR6xb1QoU63/9BVfdaCq0blwVycXPyT/Vqw5joIKdM5oCRR/afCPM7iRcyhQMAnvqwc+AOKCqayVV+SqKLvtR/AbREI12w++PQ6Ewfs4A8PgB8OJ9G0jKt6Q/iJRblqi2WWw9mwy+YHcYYh3UAfygTnyj/xl+MzRymbY0lkus+6LPtpDecVsFFhM7F32Ee1QPwISko7bAkHOtkt+joPfYDdn9PDGZ4HEmeLvH6UqZCXkzgaAfynB7cQZFEkId8FsW2NGkbpM7wB2Hi3fNFgOIjutTprixTdbpFKn4w6gGc28ve23A==
+
+ Request Info:
+ {"K":"NAVO2ORPIN5AGQEE", "DI":"R91j6WyMhxHznAKSxxxx", "P":"WIN"}
+
+ Response Info:
+ {"K":"NAVO2ORPIN5AGQEE","DI":"R91j6WyMhxHznAKSxxxx","N":"DoubleLabyrinth","O":"DoubleLabyrinth","T":1547826060}
+
+ License:
+ lRF18o+ZhBphyN0U5kFLHtAAGGXuvhqOcxNuvAk4dJcGeR0ISuw74mQvAfdNjv0T
+ I5NZFzqIJvrzM0XeR88q+3kmZkECuxwwWHP3zzDPhPiylcTV4DoGZ1tfoViUSYQc
+ LgXG0Fl7koZeP61YOKQ8GfX+Xk2ZTM64bYaF7NlhonM+GQUJCCF2JThmrP921t2p
+ b/E5pV6fLOYMM13881ZQcQcltMNVDZn4lzgzKRFFxCQFaTl6fJMHZdYVmICQTHtI
+ sNaym0zduc8/cv34mgJ+7NseXmsEPCdjrZ59wgfPsLhZLXqtfxi5hGWw4NMa3Sb2
+ UI8dzqFzRp/hSDEM0mEqiA==
+ ```
+
+7. 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.
+
diff --git a/doc/how-to-use.windows.zh-CN.md b/doc/how-to-use.windows.zh-CN.md
new file mode 100644
index 0000000..b486498
--- /dev/null
+++ b/doc/how-to-use.windows.zh-CN.md
@@ -0,0 +1,254 @@
+# Navicat Keygen - 如何使用这个注册机? (Windows)
+
+1. [从这里](https://github.com/DoubleLabyrinth/navicat-keygen/releases)下载最新的release。
+
+2. 使用`navicat-patcher.exe`替换掉`navicat.exe`和`libcc.dll`里的Navicat激活公钥。
+
+ ```
+ navicat-patcher.exe [-dry-run] [RSA-2048 PEM File Path]
+ ```
+
+ * `[-dry-run]`: 运行patcher但不对Navicat程序做任何修改。
+
+ __这个参数是可选的。__
+
+ * ``: Navicat的完整安装路径。
+
+ __这个参数必须指定。__
+
+ * `[RSA-2048 PEM File Path]`: RSA-2048私钥文件的完整路径或相对路径。
+
+ __这个参数是可选的。__ 如果未指定,`navicat-patcher.exe`将会在当前目录生成一个新的RSA-2048私钥文件。
+
+ __例如:(在cmd.exe中)__
+
+ ```
+ navicat-patcher.exe "C:\Program Files\PremiumSoft\Navicat Premium 12"
+ ```
+
+ __Navicat Premium 12.1.22 简体中文版已通过测试__。下面将是一份样例输出:
+
+ ```
+ ***************************************************
+ * Navicat Patcher by @DoubleLabyrinth *
+ * Version: 4.0 *
+ ***************************************************
+
+ Press Enter to continue or Ctrl + C to abort.
+
+ [+] Try to open Navicat.exe ... Ok!
+ [+] Try to open libcc.dll ... Ok!
+
+ [+] PatchSolution0 ...... Ready to apply
+ [*] Patch offset = +0x029bccd8
+ [+] PatchSolution1 ...... Ready to apply
+ [*] [0] Patch offset = +0x02206c00
+ [*] [1] Patch offset = +0x0074c489
+ [*] [2] Patch offset = +0x02206910
+ [*] [3] Patch offset = +0x0074c46f
+ [*] [4] Patch offset = +0x02206904
+ [-] PatchSolution2 ...... Omitted
+ [+] PatchSolution3 ...... Ready to apply
+ [*] [ 0] Instruction RVA = 0x016539c8, Patch Offset = +0x023e64d4
+ [*] [ 1] Instruction RVA = 0x01653a1f, Patch Offset = +0x01652e23
+ [*] [ 2] Instruction RVA = 0x01653a25, Patch Offset = +0x01652e28
+ [*] [ 3] Instruction RVA = 0x01653a8c, Patch Offset = +0x01652e8e
+ ...
+ ...
+ ...
+ [*] [108] Instruction RVA = 0x016604e1, Patch Offset = +0x023e66d8
+ [*] [109] Instruction RVA = 0x01660518, Patch Offset = +0x0165f91c
+ [*] [110] Instruction RVA = 0x0166051e, Patch Offset = +0x0165f921
+
+ [*] PatchSolution0 is suppressed in order to keep digital signature valid.
+
+ [*] Generating new RSA private key, it may take a long time...
+ [*] Your RSA public key:
+ -----BEGIN PUBLIC KEY-----
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1hV66HgU4LrKXWW6O7bK
+ AN6ZTr5W+Mq8ClTQ+Pc+BdhLu6rww55kVq7OXKGpvx0G4eTafYMGrrBETgDSTaMq
+ Bx+8bZbGBWh2LtNfqU+xUrpHHBSz0ByBc3iTEzzthJl+Fzf8suDX2lWYIc/Ym/eW
+ YtxdJ7xOzLb68z4N0zVmA0jFX2FOm75DRYgKqy4SGixapfucL9dVaWVLTUdbrVdj
+ 4LX78t4t5ykbYoThrat4yuLvj/BxLaQ6ivKD+ScfHdtCoY+NA5jmBoUfBq3Q1SXB
+ iNaoXctbi0/H3MiPu0cRojryAocooF89yFm5/mNnzWGAYPr6DvBI8CDTZmjaQ4oC
+ aQIDAQAB
+ -----END PUBLIC KEY-----
+
+ *******************************************************
+ * PatchSolution1 *
+ *******************************************************
+ [*] Previous:
+ +0x0000000002206c00 44 37 35 31 32 35 42 37 30 37 36 37 42 39 34 31 D75125B70767B941
+ +0x0000000002206c10 34 35 42 34 37 43 31 43 42 33 43 30 37 35 35 45 45B47C1CB3C0755E
+ +0x0000000002206c20 37 43 43 42 38 38 32 35 43 35 44 43 45 30 43 35 7CCB8825C5DCE0C5
+ ...
+ ...
+ [*] After:
+ +0x0000000002206c00 33 43 32 39 30 39 35 38 33 34 38 41 42 43 35 39 3C290958348ABC59
+ +0x0000000002206c10 36 44 39 30 43 45 45 38 31 36 42 36 39 38 34 44 6D90CEE816B6984D
+ +0x0000000002206c20 35 32 35 34 37 45 30 32 34 31 42 36 42 43 31 41 52547E0241B6BC1A
+ ...
+ ...
+
+ [*] Previous:
+ +0x000000000074c480 fe ea bc 01 ....
+ [*] After:
+ +0x000000000074c480 08 00 00 00 ....
+
+ [*] Previous:
+ +0x0000000002206910 45 31 43 45 44 30 39 42 39 43 32 31 38 36 42 46 E1CED09B9C2186BF
+ +0x0000000002206920 37 31 41 37 30 43 30 46 45 32 46 31 45 30 41 45 71A70C0FE2F1E0AE
+ +0x0000000002206930 46 33 42 44 36 42 37 35 32 37 37 41 41 42 32 30 F3BD6B75277AAB20
+ ...
+ ...
+ [*] After:
+ +0x0000000002206910 41 33 39 42 41 36 43 34 31 36 33 32 35 30 46 45 A39BA6C4163250FE
+ +0x0000000002206920 42 32 41 39 31 41 34 32 46 44 42 46 30 41 32 31 B2A91A42FDBF0A21
+ +0x0000000002206930 33 34 46 34 36 44 43 45 34 30 42 46 41 42 33 35 34F46DCE40BFAB35
+ ...
+ ...
+
+ [*] Previous:
+ +0x000000000074c460 59 Y
+ +0x000000000074c470 08 01 00 ...
+ [*] After:
+ +0x000000000074c460 06 .
+ +0x000000000074c470 00 00 00 ...
+
+ [*] Previous:
+ +0x0000000002206900 39 32 39 33 33 92933
+ [*] After:
+ +0x0000000002206900 42 34 34 33 38 B4438
+
+ *******************************************************
+ * PatchSolution3 *
+ *******************************************************
+ [*] +023e64d4: 4d 49 49 ---> 4d 49 49
+ [*] +01652e23: 42 49 ---> 42 49
+ [*] +01652e28: 6a ---> 6a
+ ...
+ ...
+ ...
+ [*] +023e66d8: 77 49 44 41 ---> 51 49 44 41
+ [*] +0165f91c: 51 41 ---> 51 41
+ [*] +0165f921: 42 ---> 42
+
+ [*] New RSA-2048 private key has been saved to
+ C:\Users\DoubleSine\github.com\navicat-keygen\bin\x64-Release\RegPrivateKey.pem
+
+ *******************************************************
+ * PATCH HAS BEEN DONE SUCCESSFULLY! *
+ * HAVE FUN AND ENJOY~ *
+ *******************************************************
+ ```
+
+3. 接下来使用`navicat-keygen.exe`来生成序列号和激活码
+
+ ```
+ navicat-keygen.exe <-bin|-text> [-adv]
+ ```
+
+ * `<-bin|-text>`: 必须是`-bin`或`-text`。
+
+ 如果指定了`-bin`,`navicat-keygen.exe`最终将生成`license_file`文件。这个选项是给Navicat旧激活方式使用的。
+
+ 如果指定了`-text`,`navicat-keygen.exe`最终将生成Base64样式的激活码。这个选项是给Navicat新激活方式使用的。
+
+ __这个参数必须指定。__
+
+ * `[-adv]`: 开启高级模式。
+
+ __这个参数是可选的。__ 如果指定了这个参数,`navicat-keygen.exe`将会要求你手工填写产品ID号、语言标识号。这个选项一般是给以后用的。
+
+ * ``: RSA-2048私钥文件的完整路径或相对路径。
+
+ __这个参数必须指定。__
+
+ __例如:(在cmd.exe中)__
+
+ ```console
+ navicat-keygen.exe -text .\RegPrivateKey.pem
+ ```
+
+ 你会被要求选择Navicat产品类别、语言以及输入主版本号。之后会随机生成一个序列号。
+
+ ```
+ 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)> 1
+
+ (Input major version number, range: 0 ~ 15, default: 12)> 12
+
+ Serial number:
+ NAVO-2ORP-IN5A-GQEE
+
+ Your name:
+ ```
+
+ 你可以使用这个序列号暂时激活Navicat。
+
+ 接下来你会被要求输入`用户名`和`组织名`;请随便填写,但不要太长。
+
+ ```
+ Your name: DoubleLabyrinth
+ Your organization: DoubleLabyrinth
+ Input request code (in Base64), input empty line to end:
+ ```
+
+ 之后你会被要求填入请求码。注意 __不要关闭命令行__.
+
+4. __断开网络__ 并打开Navicat。找到`注册`窗口,并填入keygen给你的序列号。然后点击`激活`按钮。
+
+5. 一般来说在线激活肯定会失败,这时候Navicat会询问你是否`手动激活`,直接选吧。
+
+6. 在`手动激活`窗口你会得到一个请求码,复制它并把它粘贴到keygen里。最后别忘了连按至少两下回车结束输入。
+
+ ```
+ Your name: DoubleLabyrinth
+ Your organization: DoubleLabyrinth
+
+ Input request code (in Base64), input empty line to end:
+ t2U+0yfE2FfnbjyhCXa0lglZOHu9Ntc3qyGiPbR6xb1QoU63/9BVfdaCq0blwVycXPyT/Vqw5joIKdM5oCRR/afCPM7iRcyhQMAnvqwc+AOKCqayVV+SqKLvtR/AbREI12w++PQ6Ewfs4A8PgB8OJ9G0jKt6Q/iJRblqi2WWw9mwy+YHcYYh3UAfygTnyj/xl+MzRymbY0lkus+6LPtpDecVsFFhM7F32Ee1QPwISko7bAkHOtkt+joPfYDdn9PDGZ4HEmeLvH6UqZCXkzgaAfynB7cQZFEkId8FsW2NGkbpM7wB2Hi3fNFgOIjutTprixTdbpFKn4w6gGc28ve23A==
+
+ Request Info:
+ {"K":"NAVO2ORPIN5AGQEE", "DI":"R91j6WyMhxHznAKSxxxx", "P":"WIN"}
+
+ Response Info:
+ {"K":"NAVO2ORPIN5AGQEE","DI":"R91j6WyMhxHznAKSxxxx","N":"DoubleLabyrinth","O":"DoubleLabyrinth","T":1547826060}
+
+ License:
+ lRF18o+ZhBphyN0U5kFLHtAAGGXuvhqOcxNuvAk4dJcGeR0ISuw74mQvAfdNjv0T
+ I5NZFzqIJvrzM0XeR88q+3kmZkECuxwwWHP3zzDPhPiylcTV4DoGZ1tfoViUSYQc
+ LgXG0Fl7koZeP61YOKQ8GfX+Xk2ZTM64bYaF7NlhonM+GQUJCCF2JThmrP921t2p
+ b/E5pV6fLOYMM13881ZQcQcltMNVDZn4lzgzKRFFxCQFaTl6fJMHZdYVmICQTHtI
+ sNaym0zduc8/cv34mgJ+7NseXmsEPCdjrZ59wgfPsLhZLXqtfxi5hGWw4NMa3Sb2
+ UI8dzqFzRp/hSDEM0mEqiA==
+ ```
+
+ 4. 如果不出意外,你会得到一个看似用Base64编码的激活码。直接复制它,并把它粘贴到Navicat的`手动激活`窗口,最后点`激活`按钮。如果没什么意外的话应该能成功激活。
+
diff --git a/doc/image/Screen_recording.mp4 b/doc/image/Screen_recording.mp4
new file mode 100644
index 0000000..0ba72c4
Binary files /dev/null and b/doc/image/Screen_recording.mp4 differ
diff --git a/doc/image/Screenshot_2019-04-30_12-31-33.png b/doc/image/Screenshot_2019-04-30_12-31-33.png
new file mode 100644
index 0000000..483c947
Binary files /dev/null and b/doc/image/Screenshot_2019-04-30_12-31-33.png differ
diff --git a/doc/image/Screenshot_2019-04-30_12-31-52.png b/doc/image/Screenshot_2019-04-30_12-31-52.png
new file mode 100644
index 0000000..fa7b822
Binary files /dev/null and b/doc/image/Screenshot_2019-04-30_12-31-52.png differ
diff --git a/doc/image/Screenshot_2019-04-30_12-32-43.png b/doc/image/Screenshot_2019-04-30_12-32-43.png
new file mode 100644
index 0000000..fc3a9be
Binary files /dev/null and b/doc/image/Screenshot_2019-04-30_12-32-43.png differ
diff --git a/navicat-keygen.sln b/navicat-keygen.sln
new file mode 100644
index 0000000..9ae8039
--- /dev/null
+++ b/navicat-keygen.sln
@@ -0,0 +1,48 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29215.179
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "navicat-keygen", "navicat-keygen\navicat-keygen.vcxproj", "{BA201764-EECF-4DA8-BCAD-7CFBA301FC9C}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "navicat-patcher", "navicat-patcher\navicat-patcher.vcxproj", "{7EE03EF3-D58D-4B53-913D-4A4DC8A29E34}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "common\common.vcxitems", "{290C5D5E-52D2-4FEF-909F-27FD95AE0AF9}"
+EndProject
+Global
+ GlobalSection(SharedMSBuildProjectFiles) = preSolution
+ common\common.vcxitems*{290c5d5e-52d2-4fef-909f-27fd95ae0af9}*SharedItemsImports = 9
+ common\common.vcxitems*{7ee03ef3-d58d-4b53-913d-4a4dc8a29e34}*SharedItemsImports = 4
+ common\common.vcxitems*{ba201764-eecf-4da8-bcad-7cfba301fc9c}*SharedItemsImports = 4
+ EndGlobalSection
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BA201764-EECF-4DA8-BCAD-7CFBA301FC9C}.Debug|x64.ActiveCfg = Debug|x64
+ {BA201764-EECF-4DA8-BCAD-7CFBA301FC9C}.Debug|x64.Build.0 = Debug|x64
+ {BA201764-EECF-4DA8-BCAD-7CFBA301FC9C}.Debug|x86.ActiveCfg = Debug|Win32
+ {BA201764-EECF-4DA8-BCAD-7CFBA301FC9C}.Debug|x86.Build.0 = Debug|Win32
+ {BA201764-EECF-4DA8-BCAD-7CFBA301FC9C}.Release|x64.ActiveCfg = Release|x64
+ {BA201764-EECF-4DA8-BCAD-7CFBA301FC9C}.Release|x64.Build.0 = Release|x64
+ {BA201764-EECF-4DA8-BCAD-7CFBA301FC9C}.Release|x86.ActiveCfg = Release|Win32
+ {BA201764-EECF-4DA8-BCAD-7CFBA301FC9C}.Release|x86.Build.0 = Release|Win32
+ {7EE03EF3-D58D-4B53-913D-4A4DC8A29E34}.Debug|x64.ActiveCfg = Debug|x64
+ {7EE03EF3-D58D-4B53-913D-4A4DC8A29E34}.Debug|x64.Build.0 = Debug|x64
+ {7EE03EF3-D58D-4B53-913D-4A4DC8A29E34}.Debug|x86.ActiveCfg = Debug|Win32
+ {7EE03EF3-D58D-4B53-913D-4A4DC8A29E34}.Debug|x86.Build.0 = Debug|Win32
+ {7EE03EF3-D58D-4B53-913D-4A4DC8A29E34}.Release|x64.ActiveCfg = Release|x64
+ {7EE03EF3-D58D-4B53-913D-4A4DC8A29E34}.Release|x64.Build.0 = Release|x64
+ {7EE03EF3-D58D-4B53-913D-4A4DC8A29E34}.Release|x86.ActiveCfg = Release|Win32
+ {7EE03EF3-D58D-4B53-913D-4A4DC8A29E34}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {CB1D6B65-6AEA-44E4-8698-0AEBDBC63CD2}
+ EndGlobalSection
+EndGlobal
diff --git a/navicat-keygen/Base64.hpp b/navicat-keygen/Base64.hpp
new file mode 100644
index 0000000..9a50e13
--- /dev/null
+++ b/navicat-keygen/Base64.hpp
@@ -0,0 +1,92 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+
+#pragma comment(lib, "Crypt32")
+
+#undef NKG_CURRENT_SOURCE_FILE
+#undef NKG_CURRENT_SOURCE_LINE
+#define NKG_CURRENT_SOURCE_FILE() TEXT(".\\navicat-keygen\\Base64.hpp")
+#define NKG_CURRENT_SOURCE_LINE() __LINE__
+
+namespace nkg {
+
+ std::xstring Base64Encode(const std::bytearray& Bytes) {
+ if (Bytes.empty()) {
+ return std::xstring();
+ } else {
+ DWORD cchBase64String = 0;
+ std::xstring Base64String;
+
+ auto bResult = CryptBinaryToString(
+ Bytes.data(),
+ static_cast(Bytes.size()),
+ CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
+ NULL,
+ &cchBase64String
+ );
+ if (bResult == FALSE) {
+ throw Win32Error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), GetLastError(), TEXT("CryptBinaryToString failed."));
+ }
+
+ Base64String.resize(cchBase64String - 1);
+
+ bResult = CryptBinaryToString(
+ Bytes.data(),
+ static_cast(Bytes.size()),
+ CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
+ Base64String.data(),
+ &cchBase64String
+ );
+ if (bResult == FALSE) {
+ throw Win32Error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), GetLastError(), TEXT("CryptBinaryToString failed."));
+ }
+
+ return Base64String;
+ }
+ }
+
+ std::bytearray Base64Decode(const std::xstring& Base64String) {
+ if (Base64String.empty()) {
+ return std::bytearray();
+ } else {
+ DWORD cbBytes = 0;
+ std::bytearray Bytes;
+
+ auto bResult = CryptStringToBinary(
+ Base64String.c_str(),
+ NULL,
+ CRYPT_STRING_BASE64,
+ NULL,
+ &cbBytes,
+ NULL,
+ NULL
+ );
+ if (bResult == FALSE) {
+ throw Win32Error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), GetLastError(), TEXT("CryptStringToBinary failed."))
+ .AddHint(TEXT("Are you sure it is a Base64 string?"));
+ }
+
+ Bytes.resize(cbBytes);
+
+ bResult = CryptStringToBinary(
+ Base64String.c_str(),
+ NULL,
+ CRYPT_STRING_BASE64,
+ Bytes.data(),
+ &cbBytes,
+ NULL,
+ NULL
+ );
+
+ if (bResult == FALSE) {
+ throw Win32Error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), GetLastError(), TEXT("CryptStringToBinary failed."));
+ }
+
+ return Bytes;
+ }
+ }
+}
diff --git a/navicat-keygen/CollectInformation.cpp b/navicat-keygen/CollectInformation.cpp
new file mode 100644
index 0000000..b92b7a1
--- /dev/null
+++ b/navicat-keygen/CollectInformation.cpp
@@ -0,0 +1,152 @@
+#include "SerialNumberGenerator.hpp"
+#include
+#include
+
+#undef NKG_CURRENT_SOURCE_FILE
+#undef NKG_CURRENT_SOURCE_LINE
+#define NKG_CURRENT_SOURCE_FILE() TEXT(".\\navicat-keygen\\CollectInformation.cpp")
+#define NKG_CURRENT_SOURCE_LINE() __LINE__
+
+namespace std {
+#if defined(_UNICODE) || defined(UNICODE)
+ static auto& xcin = wcin;
+ static auto& xcout = wcout;
+ static auto& xcerr = wcerr;
+#else
+ static auto& xcin = cin;
+ static auto& xcout = cout;
+ static auto& xcerr = cerr;
+#endif
+}
+
+namespace nkg {
+
+ [[nodiscard]]
+ static int ReadInt(int MinVal, int MaxVal, PCTSTR lpszPrompt, PCTSTR lpszErrorMessage) {
+ int val;
+ std::xstring s;
+ while (true) {
+ std::xcout << lpszPrompt;
+ if (!std::getline(std::xcin, s)) {
+ throw UserAbortionError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Abort."));
+ }
+
+ if (s.empty())
+ continue;
+
+ try {
+ val = std::stoi(s, nullptr, 0);
+ if (MinVal <= val && val <= MaxVal) {
+ return val;
+ } else {
+ throw std::invalid_argument("");
+ }
+ } catch (std::invalid_argument&) {
+ std::xcout << lpszErrorMessage << std::endl;
+ }
+ }
+ }
+
+ [[nodiscard]]
+ static int ReadInt(int MinVal, int MaxVal, int DefaultVal, PCTSTR lpszPrompt, PCTSTR lpszErrorMessage) {
+ int val;
+ std::xstring s;
+ while (true) {
+ std::xcout << lpszPrompt;
+ if (!std::getline(std::xcin, s)) {
+ throw UserAbortionError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("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("");
+ }
+ } catch (std::invalid_argument&) {
+ std::xcout << lpszErrorMessage << std::endl;
+ }
+ }
+ }
+
+ [[nodiscard]]
+ SerialNumberGenerator CollectInformationNormal() {
+ SerialNumberGenerator Generator;
+
+ std::xcout << TEXT("[*] Select Navicat product:") << std::endl;
+ std::xcout << TEXT(" 0. DataModeler") << std::endl;
+ std::xcout << TEXT(" 1. Premium") << std::endl;
+ std::xcout << TEXT(" 2. MySQL") << std::endl;
+ std::xcout << TEXT(" 3. PostgreSQL") << std::endl;
+ std::xcout << TEXT(" 4. Oracle") << std::endl;
+ std::xcout << TEXT(" 5. SQLServer") << std::endl;
+ std::xcout << TEXT(" 6. SQLite") << std::endl;
+ std::xcout << TEXT(" 7. MariaDB") << std::endl;
+ std::xcout << TEXT(" 8. MongoDB") << std::endl;
+ std::xcout << TEXT(" 9. ReportViewer") << std::endl;
+ std::xcout << std::endl;
+ Generator.SetProductSignature(
+ static_cast(ReadInt(0, 9, TEXT("(Input index)> "), TEXT("Invalid index.")))
+ );
+
+ std::xcout << std::endl;
+ std::xcout << TEXT("[*] Select product language:") << std::endl;
+ std::xcout << TEXT(" 0. English") << std::endl;
+ std::xcout << TEXT(" 1. Simplified Chinese") << std::endl;
+ std::xcout << TEXT(" 2. Traditional Chinese") << std::endl;
+ std::xcout << TEXT(" 3. Japanese") << std::endl;
+ std::xcout << TEXT(" 4. Polish") << std::endl;
+ std::xcout << TEXT(" 5. Spanish") << std::endl;
+ std::xcout << TEXT(" 6. French") << std::endl;
+ std::xcout << TEXT(" 7. German") << std::endl;
+ std::xcout << TEXT(" 8. Korean") << std::endl;
+ std::xcout << TEXT(" 9. Russian") << std::endl;
+ std::xcout << TEXT(" 10. Portuguese") << std::endl;
+ std::xcout << std::endl;
+ Generator.SetLanguageSignature(
+ static_cast(ReadInt(0, 10, TEXT("(Input index)> "), TEXT("Invalid index.")))
+ );
+
+ std::xcout << std::endl;
+ std::xcout << TEXT("[*] Input major version number:") << std::endl;
+ Generator.SetVersion(
+ static_cast(ReadInt(0, 15, 12, TEXT("(range: 0 ~ 15, default: 12)> "), TEXT("Invalid number.")))
+ );
+
+ std::xcout << std::endl;
+ return Generator;
+ }
+
+ [[nodiscard]]
+ SerialNumberGenerator CollectInformationAdvanced() {
+ SerialNumberGenerator Generator;
+
+ std::xcout << TEXT("[*] Navicat Product Signature:") << std::endl;
+ Generator.SetProductSignature(
+ static_cast(ReadInt(0x00, 0xff, TEXT("(range: 0x00 ~ 0xFF)> "), TEXT("Invalid number.")))
+ );
+
+ std::xcout << std::endl;
+ std::xcout << TEXT("[*] Navicat Language Signature 0:") << std::endl;
+ auto s1 = static_cast(ReadInt(0x00, 0xff, TEXT("(range: 0x00 ~ 0xFF)> "), TEXT("Invalid number.")));
+ std::xcout << std::endl;
+ std::xcout << TEXT("[*] Navicat Language Signature 1:") << std::endl;
+ auto s2 = static_cast(ReadInt(0x00, 0xff, TEXT("(range: 0x00 ~ 0xFF)> "), TEXT("Invalid number.")));
+ Generator.SetLanguageSignature(s1, s2);
+
+ std::xcout << std::endl;
+ std::xcout << TEXT("[*] Input major version number:") << std::endl;
+ Generator.SetVersion(
+ static_cast(ReadInt(0, 15, 12, TEXT("(range: 0 ~ 15, default: 12)> "), TEXT("Invalid number.")))
+ );
+
+ std::xcout << std::endl;
+ return Generator;
+ }
+}
+
diff --git a/navicat-keygen/GenerateLicense.cpp b/navicat-keygen/GenerateLicense.cpp
new file mode 100644
index 0000000..75e4505
--- /dev/null
+++ b/navicat-keygen/GenerateLicense.cpp
@@ -0,0 +1,241 @@
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include "Base64.hpp"
+#include "SerialNumberGenerator.hpp"
+
+#include
+#include
+#include
+#include
+#include
+
+#undef NKG_CURRENT_SOURCE_FILE
+#undef NKG_CURRENT_SOURCE_LINE
+#define NKG_CURRENT_SOURCE_FILE() TEXT(".\\navicat-keygen\\GenerateLicense.cpp")
+#define NKG_CURRENT_SOURCE_LINE() __LINE__
+
+namespace std {
+#if defined(_UNICODE) || defined(UNICODE)
+ static auto & xcin = wcin;
+ static auto& xcout = wcout;
+ static auto& xcerr = wcerr;
+#else
+ static auto& xcin = cin;
+ static auto& xcout = cout;
+ static auto& xcerr = cerr;
+#endif
+}
+
+namespace nkg {
+
+ void GenerateLicenseText(const RSACipher& Cipher, const SerialNumberGenerator& Generator) {
+ std::xstring username;
+ std::xstring organization;
+ std::string utf8username;
+ std::string utf8organization;
+
+ std::xstring b64RequestCode;
+ std::bytearray RequestCode;
+ std::string utf8RequestInfo;
+ std::string utf8ResponseInfo;
+ std::bytearray ResponseCode;
+ std::xstring b64ResponseCode;
+
+ std::xcout << TEXT("[*] Your name: ");
+ if (!std::getline(std::xcin, username)) {
+ throw UserAbortionError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Abort."));
+ } else {
+ utf8username = username.explicit_string(CP_UTF8);
+ }
+
+ std::xcout << TEXT("[*] Your organization: ");
+ if (!std::getline(std::xcin, organization)) {
+ throw UserAbortionError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Abort."));
+ } else {
+ utf8organization = organization.explicit_string(CP_UTF8);
+ }
+
+ std::xcout << TEXT("[*] Input request code in Base64: (Input empty line to end)") << std::endl;
+ while (true) {
+ std::xstring temp;
+ if (!std::getline(std::xcin, temp)) {
+ throw UserAbortionError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Abort."));
+ }
+
+ if (temp.empty()) {
+ break;
+ }
+
+ b64RequestCode.append(temp);
+ }
+
+ RequestCode = Base64Decode(b64RequestCode);
+
+ 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::xcout << TEXT("[*] Request Info:") << std::endl;
+ std::xcout << std::xstring(std::xstring_extension{}, utf8RequestInfo, CP_UTF8) << std::endl;
+ std::xcout << 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 Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Response info is too long."));
+ }
+
+ utf8ResponseInfo.assign(buffer.GetString(), buffer.GetSize());
+
+ std::xcout << TEXT("[*] Response Info:") << std::endl;
+ std::xcout << std::xstring(std::xstring_extension{}, utf8ResponseInfo, CP_UTF8) << std::endl;
+ std::xcout << std::endl;
+
+ ResponseCode.resize((Cipher.Bits() + 7) / 8);
+ Cipher.Encrypt(utf8ResponseInfo.data(), utf8ResponseInfo.size(), ResponseCode.data(), RSA_PKCS1_PADDING);
+ b64ResponseCode = Base64Encode(ResponseCode);
+
+ std::xcout << TEXT("[*] Activation Code:") << std::endl;
+ std::xcout << b64ResponseCode << std::endl;
+ std::xcout << std::endl;
+ }
+
+ void GenerateLicenseBinary(const RSACipher& Cipher, const SerialNumberGenerator& Generator) {
+ ResourceOwned hLicenseFile(FileHandleTraits{});
+
+ std::string utf8SerialNumber = Generator.GetSerialNumberShort().explicit_string(CP_UTF8);
+
+ std::xstring username;
+ std::xstring organization;
+ std::string utf8username;
+ std::string utf8organization;
+
+ std::string utf8ResponseInfo;
+ std::bytearray ResponseCode;
+
+ std::xcout << TEXT("[*] Your name: ");
+ if (!std::getline(std::xcin, username)) {
+ throw UserAbortionError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Abort."));
+ } else {
+ utf8username = username.explicit_string(CP_UTF8);
+ }
+
+ std::xcout << TEXT("[*] Your organization: ");
+ if (!std::getline(std::xcin, organization)) {
+ throw UserAbortionError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Abort."));
+ } else {
+ utf8organization = organization.explicit_string(CP_UTF8);
+ }
+
+ 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 Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Response info is too long."));
+ }
+
+ utf8ResponseInfo.assign(buffer.GetString(), buffer.GetSize());
+
+ std::xcout << TEXT("[*] Response Info:") << std::endl;
+ std::xcout << std::xstring(std::xstring_extension{}, utf8ResponseInfo, CP_UTF8) << std::endl;
+ std::xcout << std::endl;
+
+ ResponseCode.resize((Cipher.Bits() + 7) / 8);
+ Cipher.Encrypt(utf8ResponseInfo.data(), utf8ResponseInfo.size(), ResponseCode.data(), RSA_PKCS1_PADDING);
+
+ hLicenseFile.TakeOver(
+ CreateFile(
+ TEXT("license_file"),
+ GENERIC_WRITE,
+ 0,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL
+ )
+ );
+ if (hLicenseFile.IsValid() == false) {
+ throw Win32Error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), GetLastError(), TEXT("CreateFile failed."));
+ }
+
+ DWORD NumberOfBytesWritten;
+ if (!WriteFile(hLicenseFile, ResponseCode.data(), static_cast(ResponseCode.size()), &NumberOfBytesWritten, NULL)) {
+ throw Win32Error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), GetLastError(), TEXT("WriteFile failed."));
+ }
+
+ std::xcout << TEXT("[+] 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..9febb25
--- /dev/null
+++ b/navicat-keygen/SerialNumberGenerator.cpp
@@ -0,0 +1,195 @@
+#include "SerialNumberGenerator.hpp"
+#include
+#include
+#include
+#include
+
+#pragma comment(lib, "Advapi32")
+
+#undef NKG_CURRENT_SOURCE_FILE
+#undef NKG_CURRENT_SOURCE_LINE
+#define NKG_CURRENT_SOURCE_FILE() TEXT(".\\navicat-keygen\\NavicatKeygen.cpp")
+#define NKG_CURRENT_SOURCE_LINE() __LINE__
+
+namespace std {
+#if defined(_UNICODE) || defined(UNICODE)
+ static auto & xcin = wcin;
+ static auto& xcout = wcout;
+ static auto& xcerr = wcerr;
+#else
+ static auto& xcin = cin;
+ static auto& xcout = cout;
+ static auto& xcerr = cerr;
+#endif
+}
+
+namespace nkg {
+
+ SerialNumberGenerator::SerialNumberGenerator() noexcept :
+ _Data{ 0x68 , 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32 } {}
+
+ void SerialNumberGenerator::SetLanguageSignature(NavicatLanguage Language) noexcept {
+ switch (Language) {
+ case NavicatLanguage::English:
+ _Data[5] = 0xAC; // Must be 0xAC for English version.
+ _Data[6] = 0x88; // Must be 0x88 for English version.
+ break;
+ case NavicatLanguage::SimplifiedChinese:
+ _Data[5] = 0xCE; // Must be 0xCE for Simplified Chinese version.
+ _Data[6] = 0x32; // Must be 0x32 for Simplified Chinese version.
+ break;
+ case NavicatLanguage::TraditionalChinese:
+ _Data[5] = 0xAA; // Must be 0xAA for Traditional Chinese version.
+ _Data[6] = 0x99; // Must be 0x99 for Traditional Chinese version.
+ break;
+ case NavicatLanguage::Japanese:
+ _Data[5] = 0xAD; // Must be 0xAD for Japanese version. Discoverer: @dragonflylee
+ _Data[6] = 0x82; // Must be 0x82 for Japanese version. Discoverer: @dragonflylee
+ break;
+ case NavicatLanguage::Polish:
+ _Data[5] = 0xBB; // Must be 0xBB for Polish version. Discoverer: @dragonflylee
+ _Data[6] = 0x55; // Must be 0x55 for Polish version. Discoverer: @dragonflylee
+ break;
+ case NavicatLanguage::Spanish:
+ _Data[5] = 0xAE; // Must be 0xAE for Spanish version. Discoverer: @dragonflylee
+ _Data[6] = 0x10; // Must be 0x10 for Spanish version. Discoverer: @dragonflylee
+ break;
+ case NavicatLanguage::French:
+ _Data[5] = 0xFA; // Must be 0xFA for French version. Discoverer: @Deltafox79
+ _Data[6] = 0x20; // Must be 0x20 for French version. Discoverer: @Deltafox79
+ break;
+ case NavicatLanguage::German:
+ _Data[5] = 0xB1; // Must be 0xB1 for German version. Discoverer: @dragonflylee
+ _Data[6] = 0x60; // Must be 0x60 for German version. Discoverer: @dragonflylee
+ break;
+ case NavicatLanguage::Korean:
+ _Data[5] = 0xB5; // Must be 0xB5 for Korean version. Discoverer: @dragonflylee
+ _Data[6] = 0x60; // Must be 0x60 for Korean version. Discoverer: @dragonflylee
+ break;
+ case NavicatLanguage::Russian:
+ _Data[5] = 0xEE; // Must be 0xB5 for Russian version. Discoverer: @dragonflylee
+ _Data[6] = 0x16; // Must be 0x60 for Russian version. Discoverer: @dragonflylee
+ break;
+ case NavicatLanguage::Portuguese:
+ _Data[5] = 0xCD; // Must be 0xCD for Portuguese version. Discoverer: @dragonflylee
+ _Data[6] = 0x49; // Must be 0x49 for Portuguese version. Discoverer: @dragonflylee
+ break;
+ default:
+ break;
+ }
+ }
+
+ void SerialNumberGenerator::SetLanguageSignature(BYTE LanguageSignature0, BYTE LanguageSignature1) noexcept {
+ _Data[5] = LanguageSignature0;
+ _Data[6] = LanguageSignature1;
+ }
+
+ void SerialNumberGenerator::SetProductSignature(NavicatProductType ProductType) noexcept {
+ switch (ProductType) {
+ case NavicatProductType::DataModeler:
+ _Data[7] = 0x47;
+ break;
+ case NavicatProductType::Premium:
+ _Data[7] = 0x65;
+ break;
+ case NavicatProductType::MySQL:
+ _Data[7] = 0x68;
+ break;
+ case NavicatProductType::PostgreSQL:
+ _Data[7] = 0x6C;
+ break;
+ case NavicatProductType::Oracle:
+ _Data[7] = 0x70;
+ break;
+ case NavicatProductType::SQLServer:
+ _Data[7] = 0x74;
+ break;
+ case NavicatProductType::SQLite:
+ _Data[7] = 0x78;
+ break;
+ case NavicatProductType::MariaDB:
+ _Data[7] = 0x7C;
+ break;
+ case NavicatProductType::MongoDB:
+ _Data[7] = 0x80;
+ break;
+ case NavicatProductType::ReportViewer:
+ _Data[7] = 0xb;
+ default:
+ break;
+ }
+ }
+
+ void SerialNumberGenerator::SetProductSignature(BYTE ProductSignature) noexcept {
+ _Data[7] = ProductSignature;
+ }
+
+ void SerialNumberGenerator::SetVersion(BYTE Version) {
+ if (Version < 0x10) {
+ _Data[8] = static_cast(Version << 4);
+ } else {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid version for Navicat."));
+ }
+ }
+
+ void SerialNumberGenerator::Generate() {
+ static const TCHAR EncodeTable[] = TEXT("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567");
+
+ RtlGenRandom(_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(_Data + 2),
+ reinterpret_cast(_Data + 2),
+ &schedule,
+ DES_ENCRYPT
+ );
+
+ _SerialNumberShort.resize(16);
+
+ _SerialNumberShort[0] = EncodeTable[_Data[0] >> 3];
+ _SerialNumberShort[1] = EncodeTable[(_Data[0] & 0x07) << 2 | _Data[1] >> 6];
+ _SerialNumberShort[2] = EncodeTable[_Data[1] >> 1 & 0x1F];
+ _SerialNumberShort[3] = EncodeTable[(_Data[1] & 0x1) << 4 | _Data[2] >> 4];
+ _SerialNumberShort[4] = EncodeTable[(_Data[2] & 0xF) << 1 | _Data[3] >> 7];
+ _SerialNumberShort[5] = EncodeTable[_Data[3] >> 2 & 0x1F];
+ _SerialNumberShort[6] = EncodeTable[_Data[3] << 3 & 0x1F | _Data[4] >> 5];
+ _SerialNumberShort[7] = EncodeTable[_Data[4] & 0x1F];
+
+ _SerialNumberShort[8] = EncodeTable[_Data[5] >> 3];
+ _SerialNumberShort[9] = EncodeTable[(_Data[5] & 0x07) << 2 | _Data[6] >> 6];
+ _SerialNumberShort[10] = EncodeTable[_Data[6] >> 1 & 0x1F];
+ _SerialNumberShort[11] = EncodeTable[(_Data[6] & 0x1) << 4 | _Data[7] >> 4];
+ _SerialNumberShort[12] = EncodeTable[(_Data[7] & 0xF) << 1 | _Data[8] >> 7];
+ _SerialNumberShort[13] = EncodeTable[_Data[8] >> 2 & 0x1F];
+ _SerialNumberShort[14] = EncodeTable[_Data[8] << 3 & 0x1F | _Data[9] >> 5];
+ _SerialNumberShort[15] = EncodeTable[_Data[9] & 0x1F];
+
+ _SerialNumberLong = std::xstring::format(
+ TEXT("%.4s-%.4s-%.4s-%.4s"),
+ _SerialNumberShort.c_str() + 0,
+ _SerialNumberShort.c_str() + 4,
+ _SerialNumberShort.c_str() + 8,
+ _SerialNumberShort.c_str() + 12
+ );
+ }
+
+ [[nodiscard]]
+ const std::xstring& SerialNumberGenerator::GetSerialNumberShort() const noexcept {
+ return _SerialNumberShort;
+ }
+
+ [[nodiscard]]
+ const std::xstring& SerialNumberGenerator::GetSerialNumberLong() const noexcept {
+ return _SerialNumberLong;
+ }
+
+ void SerialNumberGenerator::ShowInConsole() const {
+ std::xcout << TEXT("[*] Serial number:") << std::endl;
+ std::xcout << _SerialNumberLong << std::endl;
+ std::xcout << std::endl;
+ }
+}
+
diff --git a/navicat-keygen/SerialNumberGenerator.hpp b/navicat-keygen/SerialNumberGenerator.hpp
new file mode 100644
index 0000000..8289f2b
--- /dev/null
+++ b/navicat-keygen/SerialNumberGenerator.hpp
@@ -0,0 +1,64 @@
+#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:
+
+ BYTE _Data[10];
+ std::xstring _SerialNumberShort;
+ std::xstring _SerialNumberLong;
+
+ public:
+
+ SerialNumberGenerator() noexcept;
+
+ void SetLanguageSignature(NavicatLanguage Language) noexcept;
+ void SetLanguageSignature(BYTE LanguageSignature0, BYTE LanguageSignature1) noexcept;
+
+ void SetProductSignature(NavicatProductType ProductType) noexcept;
+ void SetProductSignature(BYTE ProductSignature) noexcept;
+
+ void SetVersion(BYTE Version);
+
+ void Generate();
+
+ [[nodiscard]]
+ const std::xstring& GetSerialNumberShort() const noexcept;
+ [[nodiscard]]
+ const std::xstring& GetSerialNumberLong() const noexcept;
+
+ void ShowInConsole() const;
+ };
+
+}
+
diff --git a/navicat-keygen/_tmain.cpp b/navicat-keygen/_tmain.cpp
new file mode 100644
index 0000000..de1dec9
--- /dev/null
+++ b/navicat-keygen/_tmain.cpp
@@ -0,0 +1,117 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include "SerialNumberGenerator.hpp"
+
+#undef NKG_CURRENT_SOURCE_FILE
+#undef NKG_CURRENT_SOURCE_LINE
+#define NKG_CURRENT_SOURCE_FILE() TEXT(".\\navicat-keygen\\_tmain.cpp")
+#define NKG_CURRENT_SOURCE_LINE() __LINE__
+
+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() {
+ _putts(TEXT("***************************************************"));
+ _putts(TEXT("* Navicat Keygen by @DoubleLabyrinth *"));
+ _putts(TEXT("* Version: 4.0 *"));
+ _putts(TEXT("***************************************************"));
+ _putts(TEXT(""));
+}
+
+static void Help() {
+ _putts(TEXT("Usage:"));
+ _putts(TEXT(" navicat-keygen.exe <-bin|-text> [-adv] "));
+ _putts(TEXT(""));
+ _putts(TEXT(" <-bin|-text> Specify \"-bin\" to generate \"license_file\" used by Navicat 11."));
+ _putts(TEXT(" Specify \"-text\" to generate base64-encoded activation code."));
+ _putts(TEXT(" This parameter must be specified."));
+ _putts(TEXT(""));
+ _putts(TEXT(" [-adv] Enable advance mode."));
+ _putts(TEXT(" This parameter is optional."));
+ _putts(TEXT(""));
+ _putts(TEXT(" A path to an RSA-2048 private key file."));
+ _putts(TEXT(" This parameter must be specified."));
+ _putts(TEXT(""));
+ _putts(TEXT("Example:"));
+ _putts(TEXT(" navicat-keygen.exe -text .\\RegPrivateKey.pem"));
+}
+
+int _tmain(int argc, PTSTR argv[]) {
+ Welcome();
+
+ if (argc == 3 || argc == 4) {
+ nkg::fnCollectInformation* lpfnCollectInformation = nullptr;
+ nkg::fnGenerateLicense* lpfnGenerateLicense = nullptr;
+
+ if (_tcsicmp(argv[1], TEXT("-bin")) == 0) {
+ lpfnGenerateLicense = nkg::GenerateLicenseBinary;
+ } else if (_tcsicmp(argv[1], TEXT("-text")) == 0) {
+ lpfnGenerateLicense = nkg::GenerateLicenseText;
+ } else {
+ Help();
+ return -1;
+ }
+
+ if (argc == 4) {
+ if (_tcsicmp(argv[2], TEXT("-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 nkg::Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("RSA key length mismatches."))
+ .AddHint(TEXT("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 (nkg::UserAbortionError&) {
+ return -1;
+ } catch (nkg::Exception& e) {
+ _tprintf_s(TEXT("[-] %s:%zu ->\n"), e.File(), e.Line());
+ _tprintf_s(TEXT(" %s\n"), e.Message());
+
+ if (e.HasErrorCode()) {
+ _tprintf_s(TEXT(" %s (0x%zx)\n"), e.ErrorString(), e.ErrorCode());
+ }
+
+ for (auto& Hint : e.Hints()) {
+ _tprintf_s(TEXT(" Hints: %s\n"), Hint.c_str());
+ }
+
+ return -1;
+ } catch (std::exception& e) {
+ _tprintf_s(TEXT("[-] %hs\n"), e.what());
+ return -1;
+ }
+ } else {
+ Help();
+ return -1;
+ }
+}
diff --git a/navicat-keygen/navicat-keygen.vcxproj b/navicat-keygen/navicat-keygen.vcxproj
new file mode 100644
index 0000000..7ab4edd
--- /dev/null
+++ b/navicat-keygen/navicat-keygen.vcxproj
@@ -0,0 +1,181 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 16.0
+ {BA201764-EECF-4DA8-BCAD-7CFBA301FC9C}
+ Win32Proj
+ navicatkeygen
+ 10.0
+ x86-windows-static
+ x64-windows-static
+
+
+
+ Application
+ true
+ v142
+ Unicode
+ false
+
+
+ Application
+ false
+ v142
+ true
+ Unicode
+ false
+
+
+ Application
+ true
+ v142
+ Unicode
+ false
+
+
+ Application
+ false
+ v142
+ true
+ Unicode
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)bin\$(Platform)-$(Configuration)\
+ $(SolutionDir)obj\$(Platform)-$(Configuration)\$(ProjectName)\
+
+
+ true
+ $(SolutionDir)bin\$(Platform)-$(Configuration)\
+ $(SolutionDir)obj\$(Platform)-$(Configuration)\$(ProjectName)\
+
+
+ false
+ $(SolutionDir)bin\$(Platform)-$(Configuration)\
+ $(SolutionDir)obj\$(Platform)-$(Configuration)\$(ProjectName)\
+
+
+ false
+ $(SolutionDir)bin\$(Platform)-$(Configuration)\
+ $(SolutionDir)obj\$(Platform)-$(Configuration)\$(ProjectName)\
+
+
+
+ Level3
+ Disabled
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ stdcpp17
+ MultiThreadedDebug
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ Disabled
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ stdcpp17
+ MultiThreadedDebug
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ stdcpp17
+ MultiThreaded
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ stdcpp17
+ MultiThreaded
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/navicat-keygen/navicat-keygen.vcxproj.filters b/navicat-keygen/navicat-keygen.vcxproj.filters
new file mode 100644
index 0000000..3a267c9
--- /dev/null
+++ b/navicat-keygen/navicat-keygen.vcxproj.filters
@@ -0,0 +1,39 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ 源文件
+
+
+ 源文件
+
+
+ 源文件
+
+
+ 源文件
+
+
+
+
+ 头文件
+
+
+ 头文件
+
+
+
\ No newline at end of file
diff --git a/navicat-keygen/navicat-keygen.vcxproj.user b/navicat-keygen/navicat-keygen.vcxproj.user
new file mode 100644
index 0000000..966b4ff
--- /dev/null
+++ b/navicat-keygen/navicat-keygen.vcxproj.user
@@ -0,0 +1,6 @@
+
+
+
+ true
+
+
\ No newline at end of file
diff --git a/navicat-patcher/CapstoneDisassembler.cpp b/navicat-patcher/CapstoneDisassembler.cpp
new file mode 100644
index 0000000..d9a9602
--- /dev/null
+++ b/navicat-patcher/CapstoneDisassembler.cpp
@@ -0,0 +1,94 @@
+#include "CapstoneDisassembler.hpp"
+
+#undef NKG_CURRENT_SOURCE_FILE
+#undef NKG_CURRENT_SOURCE_LINE
+#define NKG_CURRENT_SOURCE_FILE() TEXT(".\\navicat-patcher\\CapstoneDisassembler.cpp")
+#define NKG_CURRENT_SOURCE_LINE() __LINE__
+
+namespace nkg {
+
+ CapstoneDisassembler::CapstoneDisassembler(const CapstoneEngine& Engine) :
+ ResourceOwned(cs_malloc(Engine)),
+ _Engine(Engine),
+ _CurrentState{},
+ _NextState{},
+ _lpCurrentInsn(nullptr)
+ {
+ if (IsValid() == false) {
+ throw CapstoneError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), cs_errno(Engine), TEXT("cs_malloc failed."));
+ }
+ }
+
+ CapstoneDisassembler::CapstoneDisassembler(CapstoneDisassembler&& Other) noexcept :
+ ResourceOwned(static_cast&&>(Other)),
+ _Engine(Other._Engine),
+ _CurrentState(Other._CurrentState),
+ _NextState(Other._NextState),
+ _lpCurrentInsn(Other._lpCurrentInsn) {}
+
+ CapstoneDisassembler& CapstoneDisassembler::SetContext(const CapstoneContext& Ctx) noexcept {
+ _lpCurrentInsn = nullptr;
+
+ _CurrentState.lpMachineCode = nullptr;
+ _CurrentState.cbMachineCode = 0;
+ _CurrentState.Address = 0;
+
+ _NextState = Ctx;
+
+ return *this;
+ }
+
+ [[nodiscard]]
+ const CapstoneContext& CapstoneDisassembler::GetContext() const noexcept {
+ return _NextState;
+ }
+
+ [[nodiscard]]
+ bool CapstoneDisassembler::Next() noexcept {
+ bool bSucceed;
+ CapstoneContext backup = _NextState;
+
+ bSucceed = cs_disasm_iter(_Engine.Get(), reinterpret_cast(&_NextState.lpMachineCode), &_NextState.cbMachineCode, &_NextState.Address, Get());
+ if (bSucceed) {
+ if (_lpCurrentInsn == nullptr) {
+ _lpCurrentInsn = Get();
+ }
+
+ _CurrentState = backup;
+ } else {
+ _lpCurrentInsn = nullptr;
+ }
+
+ return bSucceed;
+ }
+
+ [[nodiscard]]
+ const cs_insn* CapstoneDisassembler::GetInstruction() const noexcept {
+ return _lpCurrentInsn;
+ }
+
+ [[nodiscard]]
+ const CapstoneContext& CapstoneDisassembler::GetInstructionContext() const noexcept {
+ return _CurrentState;
+ }
+
+ CapstoneEngine::CapstoneEngine(cs_arch ArchType, cs_mode Mode) {
+ auto err = cs_open(ArchType, Mode, GetAddressOf());
+ if (err != CS_ERR_OK) {
+ throw CapstoneError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, TEXT("cs_open failed."));
+ }
+ }
+
+ void CapstoneEngine::Option(cs_opt_type Type, size_t Value) {
+ auto err = cs_option(Get(), Type, Value);
+ if (err != CS_ERR_OK) {
+ throw CapstoneError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, TEXT("cs_option failed."));
+ }
+ }
+
+ [[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..11bd01e
--- /dev/null
+++ b/navicat-patcher/CapstoneDisassembler.hpp
@@ -0,0 +1,59 @@
+#pragma once
+#include "ExceptionCapstone.hpp"
+#include
+#include "ResourceTraitsCapstone.hpp"
+
+namespace nkg {
+
+ struct CapstoneContext {
+ const void* lpMachineCode;
+ size_t cbMachineCode;
+ uint64_t Address;
+ };
+
+ class CapstoneEngine;
+
+ class CapstoneDisassembler : private ResourceOwned {
+ friend class CapstoneEngine;
+ private:
+
+ const CapstoneEngine& _Engine;
+ CapstoneContext _CurrentState;
+ CapstoneContext _NextState;
+ cs_insn* _lpCurrentInsn;
+
+ CapstoneDisassembler(const CapstoneEngine& Engine);
+
+ public:
+
+ CapstoneDisassembler(CapstoneDisassembler&& Other) noexcept;
+
+ 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 ResourceOwned {
+ friend class CapstoneDisassembler;
+ public:
+
+ CapstoneEngine(cs_arch ArchType, cs_mode Mode);
+
+ void Option(cs_opt_type Type, size_t Value);
+
+ [[nodiscard]]
+ CapstoneDisassembler CreateDisassembler() const;
+ };
+
+}
+
diff --git a/navicat-patcher/ExceptionCapstone.hpp b/navicat-patcher/ExceptionCapstone.hpp
new file mode 100644
index 0000000..b75e0cc
--- /dev/null
+++ b/navicat-patcher/ExceptionCapstone.hpp
@@ -0,0 +1,36 @@
+#pragma once
+#include
+#include
+
+namespace nkg {
+
+ class CapstoneError final : public Exception {
+ private:
+
+ cs_err _ErrorCode;
+ std::xstring _ErrorString;
+
+ public:
+
+ CapstoneError(PCTSTR SourceFile, SIZE_T SourceLine, cs_err CapstoneErrorCode, PCTSTR CustomMessage) noexcept :
+ Exception(SourceFile, SourceLine, CustomMessage),
+ _ErrorCode(CapstoneErrorCode),
+ _ErrorString(std::xstring_extension{}, cs_strerror(CapstoneErrorCode), CP_UTF8) {}
+
+ [[nodiscard]]
+ virtual bool HasErrorCode() const noexcept override {
+ return true;
+ }
+
+ [[nodiscard]]
+ virtual ULONG_PTR ErrorCode() const noexcept override {
+ return _ErrorCode;
+ }
+
+ [[nodiscard]]
+ virtual PCTSTR ErrorString() const noexcept override {
+ return _ErrorString.c_str();
+ }
+ };
+
+}
diff --git a/navicat-patcher/ImageInterpreter.cpp b/navicat-patcher/ImageInterpreter.cpp
new file mode 100644
index 0000000..c9a9a33
--- /dev/null
+++ b/navicat-patcher/ImageInterpreter.cpp
@@ -0,0 +1,284 @@
+#include "ImageInterpreter.hpp"
+
+#undef NKG_CURRENT_SOURCE_FILE
+#undef NKG_CURRENT_SOURCE_LINE
+#define NKG_CURRENT_SOURCE_FILE() TEXT(".\\navicat-patcher\\ImageInterpreter.cpp")
+#define NKG_CURRENT_SOURCE_LINE() __LINE__
+
+namespace nkg {
+
+ ImageInterpreter::ImageInterpreter() :
+ _DosHeader(nullptr),
+ _NtHeaders(nullptr),
+ _SectionHeaderTable(nullptr),
+ _VsFixedFileInfo(nullptr) {}
+
+ [[nodiscard]]
+ ImageInterpreter ImageInterpreter::ParseImage(PVOID ImageBase, bool DisableRelocationParsing) {
+ ImageInterpreter NewImage;
+
+ NewImage._DosHeader = reinterpret_cast(ImageBase);
+ if (NewImage._DosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (DOS signature check failure)"))
+ .AddHint(TEXT("Are you sure you DO provide a valid WinPE file?"));
+ }
+
+ NewImage._NtHeaders = reinterpret_cast(
+ reinterpret_cast(ImageBase) + NewImage._DosHeader->e_lfanew
+ );
+ if (NewImage._NtHeaders->Signature != IMAGE_NT_SIGNATURE) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (NT signature check failure)"))
+ .AddHint(TEXT("Are you sure you DO provide a valid WinPE file?"));
+ }
+
+#if defined(_M_AMD64)
+ if (NewImage._NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (Optional header magic check failure)"))
+ .AddHint(TEXT("Are you sure you DO provide a valid 64-bits WinPE file?"));
+ }
+ if (NewImage._NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (Machine check failure)"))
+ .AddHint(TEXT("Are you sure you DO provide a valid 64-bits WinPE file?"));
+ }
+#elif defined(_M_IX86)
+ if (NewImage._NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (Optional header magic check failure)"))
+ .AddHint(TEXT("Are you sure you DO provide a valid 32-bits WinPE file?"));
+ }
+ if (NewImage._NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Invalid Image. (Machine check failure)"))
+ .AddHint(TEXT("Are you sure you DO provide a valid 32-bits WinPE file?"));
+ }
+#else
+#error "Unsupported architecture."
+#endif
+
+ NewImage._SectionHeaderTable = reinterpret_cast(
+ reinterpret_cast(&NewImage._NtHeaders->OptionalHeader) + NewImage._NtHeaders->FileHeader.SizeOfOptionalHeader
+ );
+
+ for (WORD i = 0; i < NewImage._NtHeaders->FileHeader.NumberOfSections; ++i) {
+ uint64_t SectionName = *reinterpret_cast(NewImage._SectionHeaderTable[i].Name);
+
+ if (NewImage._SectionNameTable.find(SectionName) == NewImage._SectionNameTable.end()) {
+ NewImage._SectionNameTable[SectionName] = i;
+ }
+
+ NewImage._SectionAddressTable[NewImage._SectionHeaderTable[i].VirtualAddress] = i;
+ NewImage._SectionOffsetTable[NewImage._SectionHeaderTable[i].PointerToRawData] = i;
+ }
+
+ if (!DisableRelocationParsing && NewImage._NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress != 0) {
+ auto RelocTableRva = NewImage._NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
+ auto RelocTable = NewImage.RvaToPointer(RelocTableRva);
+
+ while (RelocTable->VirtualAddress != 0) {
+ uintptr_t Rva = RelocTable->VirtualAddress;
+ PWORD RelocItems = reinterpret_cast(RelocTable + 1);
+ DWORD RelocItemsCount = (RelocTable->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
+
+ for (DWORD i = 0; i < RelocItemsCount; ++i) {
+ auto RelocType = RelocItems[i] >> 12;
+
+ switch (RelocType) {
+ case IMAGE_REL_BASED_ABSOLUTE:
+ break;
+ case IMAGE_REL_BASED_HIGH:
+ case IMAGE_REL_BASED_LOW:
+ case IMAGE_REL_BASED_HIGHADJ:
+ NewImage._RelocationAddressTable[Rva + (RelocItems[i] & 0x0fff)] = 2;
+ break;
+ case IMAGE_REL_BASED_HIGHLOW:
+ NewImage._RelocationAddressTable[Rva + (RelocItems[i] & 0x0fff)] = 4;
+ break;
+#if defined(IMAGE_REL_BASED_DIR64)
+ case IMAGE_REL_BASED_DIR64:
+ NewImage._RelocationAddressTable[Rva + (RelocItems[i] & 0x0fff)] = 8;
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+
+ RelocTable = reinterpret_cast(&RelocItems[RelocItemsCount]);
+ }
+ }
+
+ if (NewImage._NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress) {
+ uintptr_t ResourceRva = NewImage._NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
+
+ auto ResourceTypeTable =
+ NewImage.RvaToPointer(ResourceRva);
+ auto ResourceTypeNameEntries =
+ reinterpret_cast(ResourceTypeTable + 1);
+ auto ResourceTypeIdEntries =
+ ResourceTypeNameEntries + ResourceTypeTable->NumberOfNamedEntries;
+ bool VS_FII_Ok = false;
+
+ for (WORD i = 0; i < ResourceTypeTable->NumberOfIdEntries && !VS_FII_Ok; ++i) {
+ if (ResourceTypeIdEntries[i].Id == reinterpret_cast(RT_VERSION) && ResourceTypeIdEntries[i].DataIsDirectory) {
+ auto ResourceNameTable =
+ NewImage.RvaToPointer(ResourceRva + ResourceTypeIdEntries[i].OffsetToDirectory);
+ auto ResourceNameNameEntries =
+ reinterpret_cast(ResourceNameTable + 1);
+ auto ResourceNameIdEntries =
+ ResourceNameNameEntries + ResourceNameTable->NumberOfNamedEntries;
+
+ for (WORD j = 0; j < ResourceNameTable->NumberOfIdEntries && !VS_FII_Ok; ++j) {
+ if (ResourceNameIdEntries[j].Id == VS_VERSION_INFO && ResourceNameIdEntries[j].DataIsDirectory) {
+ auto ResourceLangTable =
+ NewImage.RvaToPointer(ResourceRva + ResourceNameIdEntries[j].OffsetToDirectory);
+ auto ResourceLangNameEntries =
+ reinterpret_cast(ResourceLangTable + 1);
+ auto ResourceLangIdEntries =
+ ResourceLangNameEntries + ResourceLangTable->NumberOfNamedEntries;
+
+ for (WORD k = 0; k < ResourceLangTable->NumberOfIdEntries && !VS_FII_Ok; ++k) {
+ if (ResourceLangIdEntries[k].Id == MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL) && !ResourceLangIdEntries[k].DataIsDirectory) {
+ auto ResourceDataEntry =
+ NewImage.RvaToPointer(ResourceRva + ResourceLangIdEntries[k].OffsetToData);
+
+ auto VsVersionInfo = NewImage.RvaToPointer(ResourceDataEntry->OffsetToData);
+ auto VsVersionInfoszKey = reinterpret_cast(VsVersionInfo + 6);
+ if (_wcsicmp(VsVersionInfoszKey, L"VS_VERSION_INFO") == 0) {
+ auto p = reinterpret_cast(VsVersionInfoszKey + _countof(L"VS_VERSION_INFO"));
+ while (NewImage.PointerToRva(p) % sizeof(DWORD)) {
+ ++p;
+ }
+
+ if (reinterpret_cast(p)->dwSignature == VS_FFI_SIGNATURE) {
+ NewImage._VsFixedFileInfo = reinterpret_cast(p);
+ VS_FII_Ok = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return NewImage;
+ }
+
+ [[nodiscard]]
+ PIMAGE_DOS_HEADER ImageInterpreter::ImageDosHeader() const noexcept {
+ return _DosHeader;
+ }
+
+ [[nodiscard]]
+ PIMAGE_NT_HEADERS ImageInterpreter::ImageNtHeaders() const noexcept {
+ return _NtHeaders;
+ }
+
+ [[nodiscard]]
+ PIMAGE_SECTION_HEADER ImageInterpreter::ImageSectionTable() const noexcept {
+ return _SectionHeaderTable;
+ }
+
+ [[nodiscard]]
+ PIMAGE_SECTION_HEADER ImageInterpreter::ImageSectionHeader(PCSTR lpszSectionName) const {
+ uint64_t NameValue = 0;
+
+ for (int i = 0; i < sizeof(NameValue) && lpszSectionName[i]; ++i)
+ reinterpret_cast(&NameValue)[i] = lpszSectionName[i];
+
+ auto it = _SectionNameTable.find(NameValue);
+
+ if (it == _SectionNameTable.end()) {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Target section header is not found."))
+ .AddHint(std::xstring::format(TEXT("lpszSectionName = %s"), lpszSectionName));
+ }
+
+ return &_SectionHeaderTable[it->second];
+ }
+
+ [[nodiscard]]
+ PIMAGE_SECTION_HEADER ImageInterpreter::ImageSectionHeader(uintptr_t Rva) const {
+ auto it = _SectionAddressTable.upper_bound(Rva);
+ if (it != _SectionAddressTable.begin()) {
+ --it;
+ }
+
+ auto SectionHeader = &_SectionHeaderTable[it->second];
+ uintptr_t SectionRvaBegin = SectionHeader->VirtualAddress;
+ uintptr_t SectionRvaEnd = SectionRvaBegin + SectionHeader->Misc.VirtualSize;
+
+ if (SectionRvaBegin <= Rva && Rva < SectionRvaEnd) {
+ return SectionHeader;
+ } else {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Target section header is not found."))
+ .AddHint(std::xstring::format(TEXT("Rva = 0x%zx"), Rva));
+ }
+ }
+
+ [[nodiscard]]
+ uintptr_t ImageInterpreter::RvaToFileOffset(uintptr_t Rva) const {
+ auto SectionHeader = ImageSectionHeader(Rva);
+ return SectionHeader->PointerToRawData + (Rva - static_cast(SectionHeader->VirtualAddress));
+ }
+
+ [[nodiscard]]
+ uintptr_t ImageInterpreter::FileOffsetToRva(uintptr_t FileOffset) const {
+ auto it = _SectionOffsetTable.upper_bound(FileOffset);
+ if (it != _SectionOffsetTable.begin()) {
+ --it;
+ }
+
+ auto SectionHeader = &_SectionHeaderTable[it->second];
+ uintptr_t SectionFileOffsetBegin = SectionHeader->PointerToRawData;
+ uintptr_t SectionFileOffsetEnd = SectionFileOffsetBegin + SectionHeader->SizeOfRawData;
+
+ if (SectionFileOffsetBegin <= FileOffset && FileOffset < SectionFileOffsetEnd) {
+ return SectionHeader->VirtualAddress + (FileOffset - SectionHeader->PointerToRawData);
+ } else {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Target section header is not found."))
+ .AddHint(std::xstring::format(TEXT("FileOffset = 0x%zx"), FileOffset));
+ }
+ }
+
+ [[nodiscard]]
+ bool ImageInterpreter::IsRvaRangeInRelocTable(uintptr_t Rva, size_t Size) const {
+ auto it = _RelocationAddressTable.upper_bound(Rva);
+ if (it != _RelocationAddressTable.begin()) {
+ --it;
+ }
+
+ return it->first <= Rva && Rva < it->first + it->second;
+ }
+
+ DWORD ImageInterpreter::ImageFileMajorVersion() const {
+ if (_VsFixedFileInfo) {
+ return _VsFixedFileInfo->dwFileVersionMS;
+ } else {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Image does not have version info."));
+ }
+ }
+
+ DWORD ImageInterpreter::ImageFileMinorVersion() const {
+ if (_VsFixedFileInfo) {
+ return _VsFixedFileInfo->dwFileVersionLS;
+ } else {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Image does not have version info."));
+ }
+ }
+
+ DWORD ImageInterpreter::ImageProductMajorVersion() const {
+ if (_VsFixedFileInfo) {
+ return _VsFixedFileInfo->dwProductVersionMS;
+ } else {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Image does not have version info."));
+ }
+ }
+
+ DWORD ImageInterpreter::ImageProductMinorVersion() const {
+ if (_VsFixedFileInfo) {
+ return _VsFixedFileInfo->dwProductVersionLS;
+ } else {
+ throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Image does not have version info."));
+ }
+ }
+}
+
diff --git a/navicat-patcher/ImageInterpreter.hpp b/navicat-patcher/ImageInterpreter.hpp
new file mode 100644
index 0000000..c4a01ff
--- /dev/null
+++ b/navicat-patcher/ImageInterpreter.hpp
@@ -0,0 +1,173 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include