Initial commit

This commit is contained in:
恍兮惚兮 2024-02-07 20:59:24 +08:00
commit d458abbd3f
506 changed files with 71664 additions and 0 deletions

17
.gitattributes vendored Normal file
View File

@ -0,0 +1,17 @@
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
/build/
/builds/
/out/
*.vs/
*.user
*.aps
.vscode

39
CMakeLists.txt Normal file
View File

@ -0,0 +1,39 @@
cmake_minimum_required(VERSION 3.16)
project(LunaHook)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
add_compile_options(
/std:c++17
/MP
/wd4018
/wd4819
/wd4244
/wd4267
/DVERSION="${VERSION}"
/DUNICODE
/D_UNICODE
)
if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
set(bitappendix "64")
else()
set(bitappendix "32")
endif()
set(CMAKE_FINAL_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/builds/${CMAKE_BUILD_TYPE}_x${bitappendix})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY $<1:${CMAKE_FINAL_OUTPUT_DIRECTORY}>)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY $<1:${CMAKE_FINAL_OUTPUT_DIRECTORY}>)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_FINAL_OUTPUT_DIRECTORY}>)
include_directories(.)
include(libs/libs.cmake)
include_directories(include)
add_subdirectory(include)
add_subdirectory(LunaHook)
add_subdirectory(LunaHost)

674
LICENSE Normal file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
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) <year> <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 <http://www.gnu.org/licenses/>.
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:
<program> Copyright (C) <year> <name of author>
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
<http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

2
Lang/Lang.h Normal file
View File

@ -0,0 +1,2 @@
#include"en.h"

54
Lang/en.h Normal file
View File

@ -0,0 +1,54 @@

#define ALREADY_INJECTED L"already injected"
#define NEED_32_BIT L"architecture mismatch: only x86 can inject this process"
#define NEED_64_BIT L"architecture mismatch: only x64 can inject this process"
#define INJECT_FAILED L"couldn't inject"
#define LAUNCH_FAILED L"couldn't launch"
#define INVALID_CODE L"invalid code"
#define INVALID_CODEPAGE L"couldn't convert text (invalid codepage?)"
#define PIPE_CONNECTED u8"pipe connected"
#define INSERTING_HOOK u8"inserting hook: %s"
#define REMOVING_HOOK u8"removing hook: %s"
#define TOO_MANY_HOOKS u8"too many hooks: can't insert"
#define HOOK_SEARCH_STARTING u8"starting hook search"
#define HOOK_SEARCH_INITIALIZING u8"initializing hook search (%f%%)"
#define NOT_ENOUGH_TEXT u8"not enough text to search accurately"
#define HOOK_SEARCH_INITIALIZED u8"initialized hook search with %zd hooks"
#define MAKE_GAME_PROCESS_TEXT u8"please click around in the game to force it to process text during the next %d seconds"
#define HOOK_SEARCH_FINISHED u8"hook search finished, %d results found"
#define OUT_OF_RECORDS_RETRY u8"out of search records, please retry if results are poor (default record count increased)"
#define FUNC_MISSING u8"function not present"
#define MODULE_MISSING u8"module not present"
#define GARBAGE_MEMORY u8"memory inline constantly changing, useless to read"
#define SEND_ERROR u8"Send ERROR (likely an unstable/incorrect H-code) in %s"
#define READ_ERROR u8"Reader ERROR (likely an incorrect R-code) in %s"
#define SearchForHooks_ERROR u8"SearchForHooks ERROR: out of memory, retrying to allocate %d"
#define ResultsNum u8"%d results processed"
#define HIJACK_ERROR u8"Hijack ERROR"
#define COULD_NOT_FIND u8"could not find text"
#define CONSOLE L"Console"
#define InvalidLength u8"something went very wrong (invalid length %d at hook address %I64d)"
#define InsertHookFailed u8"failed to insert hook %s"
#define Match_Error u8"ERROR happened when matching engine %s "
#define Attach_Error u8"ERROR happened when attaching engine %s ERROR"
#define Attach_Continue u8"Attach engine %s success and continue"
#define MatchedEngine u8"Matched engine %s"
#define ConfirmStop "Confirmed engine %s, stop checking"
#define Attach_Stop "Attach engine %s success and stop"
#define ProcessRange "hijacking process located from 0x%p to 0x%p"
#define WarningDummy "WARNING injected process is very small, possibly a dummy!"
#define HijackERROR "Hijack ERROR"
#define WndSelectProcess L"SelectProcess"
#define WndLunaHostGui L"LunaHost Gui"
#define NotifyInvalidHookCode L"Invalid HookCode"
#define BtnSelectProcess L"Select Process"
#define BtnAttach L"Attach"
#define BtnRefresh L"Refresh"
#define BtnToClipboard L"to clipboard"
#define BtnInsertUserHook L"Insert UserHook"
#define LblFlushDelay L"flushDelay"
#define LblCodePage L"CodePage"
#define MenuCopyHookCode L"CopyHookCode"
#define MenuRemoveHook L"RemoveHook"
#define MenuDetachProcess L"DetachProcess"
#define DefaultFont L"Arial"

35
LunaHook/CMakeLists.txt Normal file
View File

@ -0,0 +1,35 @@
include_directories(. util engines)
if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
set(enginessrc TYPEMOON ENTERGRAM AGES7 mono Godot Renpy 5pb IG LightVN V8 pchooks Artemis KiriKiri YOX PPSSPP CMVS Suika2 )
set(enginepath "engine64")
set(collector "enginecollection64.cpp")
else()
set(enginessrc ScrPlayer SYSD KISS IGScript Jellyfish BKEngine Overflow SRPGStudio Suika2 FVP LCScript Ohgetsu RPGMakerRGSS3 ONScripterru OVERDRIVE HXP Palette Purple Ruf RUNE Tarte Tomato Sakuradog Troy VitaminSoft Unknown TSSystem Xbangbang Anisetta Nijyuei Interheart LovaGame Giga Jisatu101 lua51 EntisGLS Ciel ACTGS TerraLunar PPSSPP jukujojidai PCSX2 VanillawareGC cef V8 mono pchooks PONScripter Bishop sakanagl Renpy Lightvn KiriKiri SideB BGI Bootup morning shyakunage Regista NNNConfig Eushully Majiro littlecheese Elf Silkys CMVS Wolf Circus1 Circus2 Cotopha Artemis CatSystem Atelier Tenco QLIE Pal AIL2 NeXAS LunaSoft Unicorn Rejet Interlude AdobeAir Retouch Malie Live Nexton Lucifen Waffle TinkerBell SystemAoi Yuris Nitroplus2 Bruns EME RRE Candy Speed ApricoT Triangle AB2Try MBLMED GameMaker DxLib CodeX Minori Sprite RpgmXP Eagls Debonosu C4 WillPlus Tanuki GXP AOS Mink YukaSystem2 sakusesu Exp Syuntada Pensil Anim hibiki Nitroplus Reallive Siglus Taskforce2 RUGP IronGameSystem Anex86 ShinyDaysGame MarineHeart ShinaRio CaramelBox UnisonShift UnisonShift2 Escude Ryokucha Alice Footy2 utawarerumono System4x Abalone Abel 5pb HorkEye XUSE Leaf Nekopack AXL AGS AdobeFlash10 FocasLens Tamamo Ages3ResT)
set(enginepath "engine32")
set(collector "enginecollection32.cpp")
endif()
string(REPLACE ";" ".cpp;${enginepath}/" enginessrc "${enginessrc}")
message("${enginessrc}")
set(enginessrc "${enginepath}/${enginessrc}.cpp")
message("${enginessrc}")
set_source_files_properties(${enginessrc} PROPERTIES SOURCE_ENCODING "UTF-8")
set(texthook_src
main.cc
texthook.cc
hookfinder.cc
${enginessrc}
${collector}
enginecontrol.cpp
embed_util.cc
hijackfuns.cc
)
add_subdirectory(util)
add_subdirectory(engines)
add_library(LunaHook MODULE ${texthook_src} resource.rc)
target_precompile_headers(LunaHook REUSE_FROM pch)
set_target_properties(LunaHook PROPERTIES OUTPUT_NAME "LunaHook${bitappendix}")
target_link_libraries(LunaHook pch minhook commonengine utils ${YY_Thunks_for_WinXP} ${Detours})

138
LunaHook/NoEngine.h Normal file
View File

@ -0,0 +1,138 @@
#include"engine.h"
class NoEngine:public ENGINE{
public:
bool attach_function(){
ConsoleOutput("IGNORE %s",getenginename());
//ConsoleOutput("IGNORE engine");
return true;
}
};
class oldSystem40ini:public NoEngine{
public:
oldSystem40ini(){
// jichi 1/19/2015: Disable inserting Lstr for System40
// See: http://sakuradite.com/topic/618
check_by=CHECK_BY::FILE;
check_by_target=L"System40.ini";
};
};
// class RPGMakerRGSS3:public NoEngine{
// public:
// RPGMakerRGSS3(){
// // jichi 6/7/2015: RPGMaker v3
// check_by=CHECK_BY::FILE;
// check_by_target=L"*.rgss3a";
// };
// };
// class FVP:public NoEngine{
// public:
// FVP(){
// // 7/28/2015 jichi: Favorite games
// check_by=CHECK_BY::FILE;
// check_by_target=L"*.hcb";
// };
// };
class AdvPlayerHD:public NoEngine{
public:
AdvPlayerHD(){
// supposed to be WillPlus
check_by=CHECK_BY::FILE_ANY;
check_by_target=check_by_list{L"AdvHD.exe",L"AdvHD.dll"};
};
};
class DPM:public NoEngine{
public:
DPM(){
// jichi 4/30/2015: Skip games made from らすこう, such as とある人妻のネトラレ事情
// It has garbage from lstrlenW. Correct text is supposed to be in TabbedTextOutA.
check_by=CHECK_BY::FILE;
check_by_target=L"data_cg.dpm";
};
};
class Escude_ignore:public NoEngine{
public:
Escude_ignore(){
// jichi 3/19/2014: Escude game
// Example: bgm.bin gfx.bin maou.bin script.bin snd.bin voc.bin
check_by=CHECK_BY::FILE_ANY;
check_by_target=check_by_list{L"gfx.bin",L"snd.bin",L"voc.bin"};
};
};
class Chartreux:public NoEngine{
public:
Chartreux(){
// jichi 12/28/2014: "Chartreux Inc." in Copyright.
// Sublimary brands include Rosebleu, MORE, etc.
// GetGlyphOutlineA already works.
check_by=CHECK_BY::RESOURCE_STR;
check_by_target=L"Chartreux";
};
};
class lcsebody:public NoEngine{
public:
lcsebody(){
check_by=CHECK_BY::CUSTOM;
// jichi 3/19/2014: LC-ScriptEngine, GetGlyphOutlineA
check_by_target=[](){
return (wcsstr(processName, L"lcsebody") || !wcsncmp(processName, L"lcsebo~", 7) || Util::CheckFile(L"lcsebody*"));
};
};
};
// class FVP2:public NoEngine{
// public:
// FVP2(){
// check_by=CHECK_BY::CUSTOM;
// // jichi 3/19/2014: LC-ScriptEngine, GetGlyphOutlineA
// check_by_target=[](){
// wchar_t str[MAX_PATH];
// DWORD i;
// for (i = 0; processName[i]; i++) {
// str[i] = processName[i];
// if (processName[i] == L'.')
// break;
// }
// *(DWORD *)(str + i + 1) = 0x630068; //.hcb
// *(DWORD *)(str + i + 3) = 0x62;
// // jichi 10/3/2013: such like アトリエかぐや
// return (Util::CheckFile(str));
// };
// };
// };
//if (Util::CheckFile(L"AGERC.DLL")) { // jichi 3/17/2014: Eushully, AGE.EXE
// ConsoleOutput("IGNORE Eushully");
// return true;
//}
//if (Util::CheckFile(L"*\\Managed\\UnityEngine.dll")) { // jichi 12/3/2013: Unity (BALDRSKY ZERO)
// ConsoleOutput("IGNORE Unity");
// return true;
//}
//if (Util::CheckFile(L"bsz_Data\\Managed\\UnityEngine.dll") || Util::CheckFile(L"bsz2_Data\\Managed\\UnityEngine.dll")) {
// ConsoleOutput("IGNORE Unity");
// return true;
//}

233
LunaHook/embed_util.cc Normal file
View File

@ -0,0 +1,233 @@
#include"embed_util.h"
#include"MinHook.h"
#include"stringutils.h"
#include"main.h"
#include"detours.h"
#include"hijackfuns.h"
#include"winevent.hpp"
#include"defs.h"
DynamicShiftJISCodec *dynamiccodec=new DynamicShiftJISCodec(932);
std::wstring cast_a2w(HookParam hp,void*data ,size_t len){
if(hp.type&CODEC_UTF16)
return std::wstring((wchar_t*)(data),len/2);
return StringToWideString(std::string((char*)data,len),hp.codepage?hp.codepage:embedsharedmem->codepage).value();
}
void cast_back(HookParam hp,void*data ,size_t *len,std::wstring trans,bool normal){
if(hp.type&CODEC_UTF16){
wcscpy((wchar_t*)data,trans.c_str());
*len=trans.size()*2;
}
else{
std::string astr;
if(hp.type&EMBED_DYNA_SJIS&&!normal){
astr=dynamiccodec->encodeSTD(trans,0);
}
else{
astr=WideStringToString(trans,hp.codepage?hp.codepage:embedsharedmem->codepage);
}
strcpy((char*)data,astr.c_str());
*len=astr.size();
}
}
struct FunctionInfo {
const char *name; // for debugging purpose
uintptr_t *oldFunction,
newFunction;
bool attached ;
uintptr_t addr;
explicit FunctionInfo(const uintptr_t _addr=0,const char *name = "", uintptr_t *oldFunction = nullptr, uintptr_t newFunction = 0,
bool attached = false )
: name(name), oldFunction(oldFunction), newFunction(newFunction)
, attached(attached),addr(_addr)
{}
};
std::unordered_map<uintptr_t, FunctionInfo> funcs; // attached functions
std::vector<uintptr_t > replacedfuns; // attached functions
bool _1f()
{
#define ADD_FUN(_f) funcs[F_##_f] = FunctionInfo((uintptr_t)_f,#_f, (uintptr_t *)&Hijack::old##_f, (uintptr_t)Hijack::new##_f);
ADD_FUN(CreateFontA)
ADD_FUN(CreateFontW)
ADD_FUN(CreateFontIndirectA)
ADD_FUN(CreateFontIndirectW)
ADD_FUN(GetGlyphOutlineA)
ADD_FUN(GetGlyphOutlineW)
ADD_FUN(GetTextExtentPoint32A)
ADD_FUN(GetTextExtentPoint32W)
ADD_FUN(GetTextExtentExPointA)
ADD_FUN(GetTextExtentExPointW)
//ADD_FUN(GetCharABCWidthsA)
//ADD_FUN(GetCharABCWidthsW)
ADD_FUN(TextOutA)
ADD_FUN(TextOutW)
ADD_FUN(ExtTextOutA)
ADD_FUN(ExtTextOutW)
ADD_FUN(DrawTextA)
ADD_FUN(DrawTextW)
ADD_FUN(DrawTextExA)
ADD_FUN(DrawTextExW)
ADD_FUN(CharNextA)
//ADD_FUN(CharNextW)
//ADD_FUN(CharNextExA)
//ADD_FUN(CharNextExW)
ADD_FUN(CharPrevA)
//ADD_FUN(CharPrevW)
ADD_FUN(MultiByteToWideChar)
ADD_FUN(WideCharToMultiByte)
#undef ADD_FUN
return 0;
}
extern bool DetourAttachedUserAddr;
extern bool hostconnected;
bool _1=_1f();
void ReplaceFunction(PVOID* oldf,PVOID newf){
RemoveHook((uintptr_t)*oldf);
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach((PVOID*)oldf, (PVOID)newf);
DetourTransactionCommit();
}
void attachFunction(uintptr_t _hook_font_flag)
{
for(auto & _func:funcs){
if(_func.first&_hook_font_flag){
if(_func.second.attached)continue;
_func.second.attached = true;
*_func.second.oldFunction=_func.second.addr;
replacedfuns.push_back(_func.first);
ReplaceFunction((PVOID*)_func.second.oldFunction,(PVOID)_func.second.newFunction);
}
}
}
void detachall( )
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
for(auto _flag:replacedfuns){
auto info=funcs.at(_flag);
DetourDetach((PVOID*)info.oldFunction, (PVOID)info.newFunction);
}
DetourTransactionCommit();
}
void solvefont(HookParam hp){
if(hp.hook_font){
attachFunction(hp.hook_font);
}
if(hp.hook_font&F_MultiByteToWideChar)
disable_mbwc=true;
if(hp.hook_font&F_WideCharToMultiByte)
disable_wcmb=true;
if (auto current_patch_fun = patch_fun.exchange(nullptr)){
current_patch_fun();
DetourAttachedUserAddr=true;
}
}
static std::wstring alwaysInsertSpacesSTD(const std::wstring& text)
{
std::wstring ret;
for(auto c: text) {
ret.push_back(c);
if (c >= 32) // ignore non-printable characters
ret.push_back(L' '); // or insert \u3000 if needed
}
return ret;
}
bool charEncodableSTD(const wchar_t& ch, UINT codepage)
{
if (ch <= 127) // ignore ascii characters
return true;
std::wstring s ;
s.push_back(ch);
return StringToWideString(WideStringToString(s, codepage), codepage).value() == s;
}
static std::wstring insertSpacesAfterUnencodableSTD(const std::wstring& text, HookParam hp)
{
std::wstring ret;
for(const wchar_t & c: text) {
ret.push_back(c);
if (!charEncodableSTD(c, hp.codepage?hp.codepage:embedsharedmem->codepage))
ret.push_back(L' ');
}
return ret;
}
std::wstring adjustSpacesSTD(const std::wstring& text,HookParam hp)
{
switch (embedsharedmem->spaceadjustpolicy)
{
case 0: return text;
case 1:return alwaysInsertSpacesSTD(text);
case 2:return insertSpacesAfterUnencodableSTD(text, hp);
default:return text;
}
}
bool isPauseKeyPressed()
{
return WinKey::isKeyControlPressed()
|| WinKey::isKeyShiftPressed() && !WinKey::isKeyReturnPressed();
}
inline UINT64 djb2_n2(const unsigned char * str, size_t len, UINT64 hash = 5381)
{
int i=0;
while (len--){
hash = ((hash << 5) + hash) + (*str++); // hash * 33 + c
}
return hash;
}
std::unordered_map<std::wstring,std::wstring>translatecache;
bool check_is_thread_selected(const ThreadParam& tp){
for(int i=0;i<10;i++)
if(embedsharedmem->use[i])
if((embedsharedmem->addr[i]==tp.addr)&&(embedsharedmem->ctx1[i]==tp.ctx)&&(embedsharedmem->ctx2[i]==tp.ctx2))
return true;
return false;
}
bool check_embed_able(const ThreadParam& tp){
return hostconnected&&check_is_thread_selected(tp)&&(isPauseKeyPressed()==false);
}
bool waitforevent(UINT32 timems,const ThreadParam& tp,const std::wstring &origin){
char eventname[1000];
sprintf(eventname,LUNA_EMBED_notify_event,GetCurrentProcessId(),djb2_n2((const unsigned char*)(origin.c_str()),origin.size()*2));
auto event=win_event(eventname);
while(timems){
if(check_embed_able(tp)==false)return false;
auto sleepstep=min(100,timems);
if(event.wait(sleepstep))return true;
timems-=sleepstep;
}
return false;
}
bool TextHook::waitfornotify(TextOutput_T* buffer,void*data ,size_t*len,ThreadParam tp){
auto origin=cast_a2w(hp,data,*len);
if(origin.size()>1000)return false;
if(hp.newlineseperator)strReplace(origin,hp.newlineseperator,L"\n");
cast_back(hp,data,len,origin,true);
TextOutput(tp, buffer, *len);
std::wstring translate;
if(translatecache.find(origin)!=translatecache.end()){
translate=translatecache.at(origin);
}
else{
if(waitforevent(embedsharedmem->waittime,tp,origin)==false)return false;
translate=embedsharedmem->text;
if((translate.size()==0)||(translate==origin))return false;
translatecache.insert(std::make_pair(origin,translate));
}
if(hp.newlineseperator)strReplace(translate,L"\n",hp.newlineseperator);
translate=adjustSpacesSTD(translate,hp);
if(embedsharedmem->keeprawtext)translate=origin+L" "+translate;
solvefont(hp);
cast_back(hp,data,len,translate,false);
return true;
}

27
LunaHook/embed_util.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef __LUNA_EMBED_ENGINE_H
#define __LUNA_EMBED_ENGINE_H
#include"types.h"
#include "texthook.h"
#include"dyncodec/dynsjiscodec.h"
extern EmbedSharedMem *embedsharedmem;
extern DynamicShiftJISCodec *dynamiccodec ;
namespace WinKey {
inline bool isKeyPressed(int vk) { return ::GetKeyState(vk) & 0xf0; }
inline bool isKeyToggled(int vk) { return ::GetKeyState(vk) & 0x0f; }
inline bool isKeyReturnPressed() { return isKeyPressed(VK_RETURN); }
inline bool isKeyControlPressed() { return isKeyPressed(VK_CONTROL); }
inline bool isKeyShiftPressed() { return isKeyPressed(VK_SHIFT); }
inline bool isKeyAltPressed() { return isKeyPressed(VK_MENU); }
}
namespace Engine{
enum TextRole { UnknownRole = 0, ScenarioRole, NameRole, OtherRole,
ChoiceRole = OtherRole, HistoryRole = OtherRole,
RoleCount };
}
inline std::atomic<void(*)()> patch_fun = nullptr;
void ReplaceFunction(PVOID* oldf,PVOID newf);
bool check_embed_able(const ThreadParam& tp);
#endif

48
LunaHook/engine.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef __LUNA_ENGINE_H
#define __LUNA_ENGINE_H
#include"stackoffset.hpp"
#include"stringutils.h"
#include"stringfilters.h"
#include "util/util.h"
#include "ithsys/ithsys.h"
#include"pchooks/pchooks.h"
#include "disasm/disasm.h"
#include"main.h"
#include"const.h"
extern WCHAR* processName, processPath[MAX_PATH],processName_lower[MAX_PATH]; // cached
extern uintptr_t processStartAddress, processStopAddress;
extern uintptr_t processStartAddress, processStopAddress;
class ENGINE{
public:
const char* enginename;
bool dontstop;//dont stop even if attached a engine
bool is_engine_certain; //stop when match a engine ,even if not attached
enum class CHECK_BY {
ALL_TRUE,
FILE, FILE_ALL,FILE_ANY,
RESOURCE_STR,
CUSTOM,
};
CHECK_BY check_by;
// const wchar_t* check_by_single;
// std::vector<const wchar_t*>check_by_list;
// std::function<bool()>check_by_custom_function;
typedef std::function<bool()> check_by_custom_function;
typedef std::vector<const wchar_t*> check_by_list;
typedef const wchar_t* check_by_single;
std::variant<check_by_single,check_by_list,check_by_custom_function>check_by_target;
//virtual bool check_by_target(){return false;};
virtual bool attach_function()=0;
virtual const char* getenginename(){
if(enginename)return enginename;
return typeid(*this).name()+6;
}
ENGINE():enginename(nullptr),dontstop(false),is_engine_certain(true),check_by(CHECK_BY::ALL_TRUE){};
bool check_function();
};
#endif

613
LunaHook/engine32/5pb.cpp Normal file
View File

@ -0,0 +1,613 @@
#include"5pb.h"
#include"mages/mages.hpp"
/** jichi 12/2/2014 5pb
*
* Sample game: [140924] CROSS<EFBFBD>CHANNEL FINAL COMPLETE<EFBFBD> * See: http://sakuradite.com/topic/528
*
* Debugging method: insert breakpoint.
* The first matched function cannot extract prelude text.
* The second matched function can extract anything but contains garbage.
*
* Function for scenario:
* 0016d90e cc int3
* 0016d90f cc int3
* 0016d910 8b15 782b6e06 mov edx,dword ptr ds:[0x66e2b78] ; .00b43bfe
* 0016d916 8a0a mov cl,byte ptr ds:[edx] ; jichi: hook here
* 0016d918 33c0 xor eax,eax
* 0016d91a 84c9 test cl,cl
* 0016d91c 74 41 je short .0016d95f
* 0016d91e 8bff mov edi,edi
* 0016d920 80f9 25 cmp cl,0x25
* 0016d923 75 11 jnz short .0016d936
* 0016d925 8a4a 01 mov cl,byte ptr ds:[edx+0x1]
* 0016d928 42 inc edx
* 0016d929 80f9 4e cmp cl,0x4e
* 0016d92c 74 05 je short .0016d933
* 0016d92e 80f9 6e cmp cl,0x6e
* 0016d931 75 26 jnz short .0016d959
* 0016d933 42 inc edx
* 0016d934 eb 23 jmp short .0016d959
* 0016d936 80f9 81 cmp cl,0x81
* 0016d939 72 05 jb short .0016d940
* 0016d93b 80f9 9f cmp cl,0x9f
* 0016d93e 76 0a jbe short .0016d94a
* 0016d940 80f9 e0 cmp cl,0xe0
* 0016d943 72 0c jb short .0016d951
* 0016d945 80f9 fc cmp cl,0xfc
* 0016d948 77 07 ja short .0016d951
* 0016d94a b9 02000000 mov ecx,0x2
* 0016d94f eb 05 jmp short .0016d956
* 0016d951 b9 01000000 mov ecx,0x1
* 0016d956 40 inc eax
* 0016d957 03d1 add edx,ecx
* 0016d959 8a0a mov cl,byte ptr ds:[edx]
* 0016d95b 84c9 test cl,cl
* 0016d95d ^75 c1 jnz short .0016d920
* 0016d95f c3 retn
*
* Function for everything:
* 001e9a76 8bff mov edi,edi
* 001e9a78 55 push ebp
* 001e9a79 8bec mov ebp,esp
* 001e9a7b 51 push ecx
* 001e9a7c 8365 fc 00 and dword ptr ss:[ebp-0x4],0x0
* 001e9a80 53 push ebx
* 001e9a81 8b5d 10 mov ebx,dword ptr ss:[ebp+0x10]
* 001e9a84 85db test ebx,ebx
* 001e9a86 75 07 jnz short .001e9a8f
* 001e9a88 33c0 xor eax,eax
* 001e9a8a e9 9a000000 jmp .001e9b29
* 001e9a8f 56 push esi
* 001e9a90 83fb 04 cmp ebx,0x4
* 001e9a93 72 75 jb short .001e9b0a
* 001e9a95 8d73 fc lea esi,dword ptr ds:[ebx-0x4]
* 001e9a98 85f6 test esi,esi
* 001e9a9a 74 6e je short .001e9b0a
* 001e9a9c 8b4d 0c mov ecx,dword ptr ss:[ebp+0xc]
* 001e9a9f 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
* 001e9aa2 8a10 mov dl,byte ptr ds:[eax]
* 001e9aa4 83c0 04 add eax,0x4
* 001e9aa7 83c1 04 add ecx,0x4
* 001e9aaa 84d2 test dl,dl
* 001e9aac 74 52 je short .001e9b00
* 001e9aae 3a51 fc cmp dl,byte ptr ds:[ecx-0x4]
* 001e9ab1 75 4d jnz short .001e9b00
* 001e9ab3 8a50 fd mov dl,byte ptr ds:[eax-0x3]
* 001e9ab6 84d2 test dl,dl
* 001e9ab8 74 3c je short .001e9af6
* 001e9aba 3a51 fd cmp dl,byte ptr ds:[ecx-0x3]
* 001e9abd 75 37 jnz short .001e9af6
* 001e9abf 8a50 fe mov dl,byte ptr ds:[eax-0x2]
* 001e9ac2 84d2 test dl,dl
* 001e9ac4 74 26 je short .001e9aec
* 001e9ac6 3a51 fe cmp dl,byte ptr ds:[ecx-0x2]
* 001e9ac9 75 21 jnz short .001e9aec
* 001e9acb 8a50 ff mov dl,byte ptr ds:[eax-0x1]
* 001e9ace 84d2 test dl,dl
* 001e9ad0 74 10 je short .001e9ae2
* 001e9ad2 3a51 ff cmp dl,byte ptr ds:[ecx-0x1]
* 001e9ad5 75 0b jnz short .001e9ae2
* 001e9ad7 8345 fc 04 add dword ptr ss:[ebp-0x4],0x4
* 001e9adb 3975 fc cmp dword ptr ss:[ebp-0x4],esi
* 001e9ade ^72 c2 jb short .001e9aa2
* 001e9ae0 eb 2e jmp short .001e9b10
* 001e9ae2 0fb640 ff movzx eax,byte ptr ds:[eax-0x1]
* 001e9ae6 0fb649 ff movzx ecx,byte ptr ds:[ecx-0x1]
* 001e9aea eb 46 jmp short .001e9b32
* 001e9aec 0fb640 fe movzx eax,byte ptr ds:[eax-0x2]
* 001e9af0 0fb649 fe movzx ecx,byte ptr ds:[ecx-0x2]
* 001e9af4 eb 3c jmp short .001e9b32
* 001e9af6 0fb640 fd movzx eax,byte ptr ds:[eax-0x3]
* 001e9afa 0fb649 fd movzx ecx,byte ptr ds:[ecx-0x3]
* 001e9afe eb 32 jmp short .001e9b32
* 001e9b00 0fb640 fc movzx eax,byte ptr ds:[eax-0x4]
* 001e9b04 0fb649 fc movzx ecx,byte ptr ds:[ecx-0x4]
* 001e9b08 eb 28 jmp short .001e9b32
* 001e9b0a 8b4d 0c mov ecx,dword ptr ss:[ebp+0xc]
* 001e9b0d 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
* 001e9b10 8b75 fc mov esi,dword ptr ss:[ebp-0x4]
* 001e9b13 eb 0d jmp short .001e9b22
* 001e9b15 8a10 mov dl,byte ptr ds:[eax] ; jichi: here, word by word
* 001e9b17 84d2 test dl,dl
* 001e9b19 74 11 je short .001e9b2c
* 001e9b1b 3a11 cmp dl,byte ptr ds:[ecx]
* 001e9b1d 75 0d jnz short .001e9b2c
* 001e9b1f 40 inc eax
* 001e9b20 46 inc esi
* 001e9b21 41 inc ecx
* 001e9b22 3bf3 cmp esi,ebx
* 001e9b24 ^72 ef jb short .001e9b15
* 001e9b26 33c0 xor eax,eax
* 001e9b28 5e pop esi
* 001e9b29 5b pop ebx
* 001e9b2a c9 leave
* 001e9b2b c3 retn
*/
namespace { // unnamed
// Characters to ignore: [%0-9A-Z]
bool Insert5pbHook1()
{
const BYTE bytes[] = {
0xcc, // 0016d90e cc int3
0xcc, // 0016d90f cc int3
0x8b,0x15, XX4, // 0016d910 8b15 782b6e06 mov edx,dword ptr ds:[0x66e2b78] ; .00b43bfe
0x8a,0x0a, // 0016d916 8a0a mov cl,byte ptr ds:[edx] ; jichi: hook here
0x33,0xc0, // 0016d918 33c0 xor eax,eax
0x84,0xc9 // 0016d91a 84c9 test cl,cl
};
enum { addr_offset = 0x0016d916 - 0x0016d90e };
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
//GROWL_DWORD3(addr+addr_offset, processStartAddress,processStopAddress);
if (!addr) {
ConsoleOutput("5pb1: pattern not found");
return false;
}
HookParam hp;
hp.address = addr + addr_offset;
hp.offset=get_reg(regs::edx);
hp.type = USING_STRING;
ConsoleOutput("INSERT 5pb1");
// GDI functions are not used by 5pb games anyway.
//ConsoleOutput("5pb: disable GDI hooks");
//
return NewHook(hp, "5pb1");
}
// Characters to ignore: [%@A-z]
inline bool _5pb2garbage_ch(char c)
{
return c == '%' || c == '@' || c >= 'A' && c <= 'z';
}
// 001e9b15 8a10 mov dl,byte ptr ds:[eax] ; jichi: here, word by word
void SpecialHook5pb2(hook_stack* stack, HookParam*, uintptr_t* data, uintptr_t* split, size_t* len)
{
static DWORD lasttext;
DWORD text = stack->eax;
if (lasttext == text)
return;
BYTE c = *(BYTE*)text;
if (!c)
return;
BYTE size = ::LeadByteTable[c]; // 1, 2, or 3
if (size == 1 && _5pb2garbage_ch(*(LPCSTR)text))
return;
lasttext = text;
*data = text;
*len = size;
}
bool Insert5pbHook2()
{
const BYTE bytes[] = {
0x8a,0x10, // 001e9b15 8a10 mov dl,byte ptr ds:[eax] ; jichi: here, word by word
0x84,0xd2, // 001e9b17 84d2 test dl,dl
0x74,0x11 // 001e9b19 74 11 je short .001e9b2c
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
//GROWL_DWORD3(addr, processStartAddress,processStopAddress);
if (!addr) {
ConsoleOutput("5pb2: pattern not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.type = USING_STRING;
hp.text_fun = SpecialHook5pb2;
ConsoleOutput("INSERT 5pb2");
// GDI functions are not used by 5pb games anyway.
//ConsoleOutput("5pb: disable GDI hooks");
//
return NewHook(hp, "5pb2");
}
/** jichi 2/2/2015: New 5pb hook
* Sample game: Hyperdimension.Neptunia.ReBirth1
*
* Debugging method: hardware breakpoint and find function in msvc110
* Then, backtrack the function stack to find proper function.
*
* Hooked function: 558BEC56FF750C8BF1FF75088D460850
*
* 0025A12E CC INT3
* 0025A12F CC INT3
* 0025A130 55 PUSH EBP
* 0025A131 8BEC MOV EBP,ESP
* 0025A133 56 PUSH ESI
* 0025A134 FF75 0C PUSH DWORD PTR SS:[EBP+0xC]
* 0025A137 8BF1 MOV ESI,ECX
* 0025A139 FF75 08 PUSH DWORD PTR SS:[EBP+0x8]
* 0025A13C 8D46 08 LEA EAX,DWORD PTR DS:[ESI+0x8]
* 0025A13F 50 PUSH EAX
* 0025A140 E8 DB100100 CALL .0026B220
* 0025A145 8B8E 988D0000 MOV ECX,DWORD PTR DS:[ESI+0x8D98]
* 0025A14B 8988 80020000 MOV DWORD PTR DS:[EAX+0x280],ECX
* 0025A151 8B8E A08D0000 MOV ECX,DWORD PTR DS:[ESI+0x8DA0]
* 0025A157 8988 88020000 MOV DWORD PTR DS:[EAX+0x288],ECX
* 0025A15D 8B8E A88D0000 MOV ECX,DWORD PTR DS:[ESI+0x8DA8]
* 0025A163 8988 90020000 MOV DWORD PTR DS:[EAX+0x290],ECX
* 0025A169 8B8E B08D0000 MOV ECX,DWORD PTR DS:[ESI+0x8DB0]
* 0025A16F 8988 98020000 MOV DWORD PTR DS:[EAX+0x298],ECX
* 0025A175 83C4 0C ADD ESP,0xC
* 0025A178 8D8E 188B0000 LEA ECX,DWORD PTR DS:[ESI+0x8B18]
* 0025A17E E8 DDD8FEFF CALL .00247A60
* 0025A183 5E POP ESI
* 0025A184 5D POP EBP
* 0025A185 C2 0800 RETN 0x8
* 0025A188 CC INT3
* 0025A189 CC INT3
*
* Runtime stack, text in arg1, and name in arg2:
*
* 0015F93C 00252330 RETURN to .00252330 from .0025A130
* 0015F940 181D0D4C ASCII "That's my line! I won't let any of you
* take the title of True Goddess!"
* 0015F944 0B8B4D20 ASCII " White Heart "
* 0015F948 0B8B5528
* 0015F94C 0B8B5524
* 0015F950 /0015F980
* 0015F954 |0026000F RETURN to .0026000F from .002521D0
*
*
* Another candidate funciton for backup usage.
* Previous text in arg1.
* Current text in arg2.
* Current name in arg3.
*
* 0026B21C CC INT3
* 0026B21D CC INT3
* 0026B21E CC INT3
* 0026B21F CC INT3
* 0026B220 55 PUSH EBP
* 0026B221 8BEC MOV EBP,ESP
* 0026B223 81EC A0020000 SUB ESP,0x2A0
* 0026B229 BA A0020000 MOV EDX,0x2A0
* 0026B22E 53 PUSH EBX
* 0026B22F 8B5D 08 MOV EBX,DWORD PTR SS:[EBP+0x8]
* 0026B232 56 PUSH ESI
* 0026B233 57 PUSH EDI
* 0026B234 8D041A LEA EAX,DWORD PTR DS:[EDX+EBX]
* 0026B237 B9 A8000000 MOV ECX,0xA8
* 0026B23C 8BF3 MOV ESI,EBX
* 0026B23E 8DBD 60FDFFFF LEA EDI,DWORD PTR SS:[EBP-0x2A0]
* 0026B244 F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
* 0026B246 B9 A8000000 MOV ECX,0xA8
* 0026B24B 8BF0 MOV ESI,EAX
* 0026B24D 8BFB MOV EDI,EBX
* 0026B24F F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
* 0026B251 81C2 A0020000 ADD EDX,0x2A0
* 0026B257 B9 A8000000 MOV ECX,0xA8
* 0026B25C 8DB5 60FDFFFF LEA ESI,DWORD PTR SS:[EBP-0x2A0]
* 0026B262 8BF8 MOV EDI,EAX
* 0026B264 F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
* 0026B266 81FA 40830000 CMP EDX,0x8340
* 0026B26C ^7C C6 JL SHORT .0026B234
* 0026B26E 8BCB MOV ECX,EBX
* 0026B270 E8 EBC7FDFF CALL .00247A60
* 0026B275 FF75 0C PUSH DWORD PTR SS:[EBP+0xC]
* 0026B278 8B35 D8525000 MOV ESI,DWORD PTR DS:[0x5052D8] ; msvcr110.sprintf
* 0026B27E 68 805C5000 PUSH .00505C80 ; ASCII "%s"
* 0026B283 53 PUSH EBX
* 0026B284 FFD6 CALL ESI
* 0026B286 FF75 10 PUSH DWORD PTR SS:[EBP+0x10]
* 0026B289 8D83 00020000 LEA EAX,DWORD PTR DS:[EBX+0x200]
* 0026B28F 68 805C5000 PUSH .00505C80 ; ASCII "%s"
* 0026B294 50 PUSH EAX
* 0026B295 FFD6 CALL ESI
* 0026B297 83C4 18 ADD ESP,0x18
* 0026B29A 8BC3 MOV EAX,EBX
* 0026B29C 5F POP EDI
* 0026B29D 5E POP ESI
* 0026B29E 5B POP EBX
* 0026B29F 8BE5 MOV ESP,EBP
* 0026B2A1 5D POP EBP
* 0026B2A2 C3 RETN
* 0026B2A3 CC INT3
* 0026B2A4 CC INT3
* 0026B2A5 CC INT3
* 0026B2A6 CC INT3
*/
void SpecialHook5pb3(hook_stack* stack, HookParam *hp, uintptr_t* data, uintptr_t* split, size_t* len)
{
int index=0;
// Text in arg1, name in arg2
if (LPCSTR text = (LPCSTR)stack->stack[index+1])
if (*text) {
if (index) // trim spaces in character name
while (*text == ' ') text++;
size_t sz = ::strlen(text);
if (index)
while (sz && text[sz - 1] == ' ') sz--;
*data = (DWORD)text;
*len = sz;
*split = FIXED_SPLIT_VALUE << index;
}
}
bool Insert5pbHook3()
{
const BYTE bytes[] = { // function starts
0x55, // 0025A130 55 PUSH EBP
0x8b,0xec, // 0025A131 8BEC MOV EBP,ESP
0x56, // 0025A133 56 PUSH ESI
0xff,0x75, 0x0c, // 0025A134 FF75 0C PUSH DWORD PTR SS:[EBP+0xC]
0x8b,0xf1, // 0025A137 8BF1 MOV ESI,ECX
0xff,0x75, 0x08, // 0025A139 FF75 08 PUSH DWORD PTR SS:[EBP+0x8]
0x8d,0x46, 0x08, // 0025A13C 8D46 08 LEA EAX,DWORD PTR DS:[ESI+0x8]
0x50, // 0025A13F 50 PUSH EAX
0xe8 // 0025A140 E8 DB100100 CALL .0026B220
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
//GROWL_DWORD3(addr, processStartAddress,processStopAddress);
if (!addr) {
ConsoleOutput("5pb2: pattern not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.type = USING_STRING | NO_CONTEXT;
hp.text_fun = SpecialHook5pb3;
hp.filter_fun = NewLineCharToSpaceFilterA; // replace '\n' by ' '
ConsoleOutput("INSERT 5pb3");
// GDI functions are not used by 5pb games anyway.
//ConsoleOutput("5pb: disable GDI hooks");
//
return NewHook(hp, "5pb3");
}
} // unnamed namespace
bool Insert5pbHook()
{
bool ok = Insert5pbHook1();
ok = Insert5pbHook2() || ok;
ok = Insert5pbHook3() || ok;
return ok;
}
bool Insert5pbHookex() {
//祝姬
const BYTE bytes[] = {
0x0F,0xB6,0xC2, 0x35,0xC5 ,0x9D ,0x1C ,0x81
};
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (addr == 0)return false;
const BYTE start[] = {
0x55,0x8b,0xec,0x83,0xe4
};
addr = reverseFindBytes(start, sizeof(start), addr - 0x40, addr);
if (addr == 0)return false;
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::ecx);
hp.type = CODEC_UTF16;
return NewHook(hp, "5pb");
}
bool InsertStuffScriptHook()
{
// BOOL GetTextExtentPoint32(
// _In_ HDC hdc,
// _In_ LPCTSTR lpString,
// _In_ int c,
// _Out_ LPSIZE lpSize
// );
HookParam hp;
hp.address = (DWORD)::GetTextExtentPoint32A;
hp.offset=get_stack(2); // arg2 lpString
hp.split = get_reg(regs::esp);
hp.type = USING_STRING | USING_SPLIT;
ConsoleOutput("INSERT StuffScriptEngine");
return NewHook(hp, "StuffScriptEngine");
//RegisterEngine(ENGINE_STUFFSCRIPT);
}
bool StuffScript2Filter(LPVOID data, size_t *size, HookParam *)
{
auto text = reinterpret_cast<LPSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
if (text[0] == '-') {
StringFilter(text, len, "-/-", 3);
StringFilterBetween(text, len, "-", 1, "-", 1);
}
StringCharReplacer(text, len, "_n_r", 4, '\n');
StringCharReplacer(text, len, "_r", 2, ' ');
StringFilter(text, len, "\\n", 2);
StringFilter(text, len, "_n", 2);
return true;
}
bool InsertStuffScript2Hook()
{
/*
* Sample games:
* https://vndb.org/r41537
* https://vndb.org/r41539
*/
const BYTE bytes[] = {
0x0F, XX, XX4, // jne tokyobabel.exe+3D4E8
0xB9, XX4, // mov ecx,tokyobabel.exe+54EAC
0x8D, 0x85, XX4, // lea eax,[ebp+tokyobabel.exe+59B968]
0x8A, 0x10, // mov dl,[eax] <-- hook here
0x3A, 0x11, // cmp dl,[ecx]
0x75, 0x1A, // jne tokyobabel.exe+3D1D7
0x84, 0xD2, // test dl,dl
0x74, 0x12, // je tokyobabel.exe+3D1D3
0x8A, 0x50, 0x01, // mov dl,[eax+01]
0x3A, 0x51, 0x01, // cmp dl,[ecx+01]
0x75, 0x0E, // jne tokyobabel.exe+3D1D7
0x83, 0xC0, 0x02, // add eax,02
0x83, 0xC1, 0x02, // add ecx,02
0x84, 0xD2, // test dl,dl
0x75, 0xE4, // jne Agreement.exe+4F538
0x33, 0xC0, // xor eax,eax
0xEB, 0x05, // jmp Agreement.exe+4F55D
0x1B, 0xC0, // sbb eax,eax
0x83, 0xD8, 0xFF, // sbb eax,-01
XX2, // cmp eax,edi
0x0F, 0x84, XX4 // je tokyobabel.exe+3D4E8
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr)
return false;
HookParam hp;
hp.address = addr + 0x11;
hp.offset=get_reg(regs::eax);
hp.index = 0;
hp.type = USING_STRING | NO_CONTEXT;
hp.filter_fun = StuffScript2Filter;
ConsoleOutput("INSERT StuffScript2");
return NewHook(hp, "StuffScript2");
}
bool StuffScript_attach_function() {
auto _=InsertStuffScriptHook();
_|=InsertStuffScript2Hook();
return _;
}
bool _5pb::attach_function() {
bool b1 = Insert5pbHook();
bool b2 = Insert5pbHookex();
bool b3=mages::MAGES();
bool sf=StuffScript_attach_function();
return b1 || b2 || b3||sf;
}
bool KaleidoFilter(LPVOID data, size_t* size, HookParam*)
{
auto text = reinterpret_cast<LPSTR>(data);
auto len = reinterpret_cast<size_t*>(size);
// Unofficial eng TL with garbage newline spaces
StringCharReplacer(text, len, " \\n ", 4, ' ');
StringCharReplacer(text, len, " \\n", 3, ' ');
StringCharReplacer(text, len, "\\n", 2, ' ');
StringCharReplacer(text, len, "\xEF\xBC\x9F", 3, '?');
return true;
}
bool InsertKaleidoHook()
{
/*
* Sample games:
* https://vndb.org/v29889
*/
const BYTE bytes[] = {
0xFF, 0x75, 0xD4, // push [ebp-2C]
0xE8, XX4, // call 5toubun.exe+1DD0
0x83, 0xC4, 0x0C, // add esp,0C
0x8A, 0xC3, // mov al,bl
0x8B, 0x4D, 0xF4, // mov ecx,[ebp-0C]
0x64, 0x89, 0x0D, XX4, // mov fs:[00000000],ecx
0x59 // pop ecx << hook here
};
enum { addr_offset = sizeof(bytes) - 1 };
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("Kaleido: pattern not found");
return false;
}
HookParam hp;
hp.address = addr + addr_offset;
hp.offset=get_reg(regs::esi);
hp.index = 0;
hp.split =get_stack(3);
hp.split_index = 0;
hp.type = USING_STRING | USING_SPLIT;
hp.filter_fun = KaleidoFilter;
ConsoleOutput(" INSERT Kaleido");
return NewHook(hp, "Kaleido");
}
namespace
{ //ANONYMOUS;CODE 官中
bool __1() {
BYTE bytes[] = {
0x8d,0x45,0xf4,0x64,0xA3,0x00,0x00,0x00,0x00,0x8b,0xf1,0x8a,0x46,0x2c,0x8b,0x55,0x08,0x84,0xc0,0x74,0x04,0x32,0xc0
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (!addr) return false;
addr = MemDbg::findEnclosingAlignedFunction(addr);
if (addr == 0)return false;
HookParam hp;
hp.address = addr;
hp.offset=get_stack(1);
hp.type = USING_STRING | CODEC_UTF8 | EMBED_ABLE | EMBED_BEFORE_SIMPLE | EMBED_AFTER_NEW;
hp.newlineseperator = L"\\n";
return NewHook(hp, "5bp");
}
bool __() {
BYTE sig1[] = {
0x81,0xFE,0xF0,0x00,0x00,0x00
};
BYTE sig2[] = {
0x81,0xFE,0xF8,0x00,0x00,0x00
};
BYTE sig3[] = {
0x81,0xFE,0xFC,0x00,0x00,0x00
};
BYTE sig4[] = {
0x81,0xFE,0xFE,0x00,0x00,0x00
};
BYTE sig5[] = {
0x81,0xFE,0x80,0x00,0x00,0x00
};
BYTE sig6[] = {
0x81,0xFE,0xE0,0x00,0x00,0x00
};
std::unordered_map<uintptr_t, int>addr_hit;
for (auto sigsz : std::vector<std::pair<BYTE*, int>>{ {sig1,sizeof(sig1)},{sig2,sizeof(sig2)},{sig3,sizeof(sig3)},{sig4,sizeof(sig4)},{sig5,sizeof(sig5)},{sig6,sizeof(sig6)} }) {
for (auto addr : Util::SearchMemory(sigsz.first, sigsz.second, PAGE_EXECUTE, processStartAddress, processStopAddress)) {
addr = MemDbg::findEnclosingAlignedFunction(addr);
if (addr == 0)continue;
if (addr_hit.find(addr) == addr_hit.end()) {
addr_hit[addr] = 1;
}
else addr_hit[addr] += 1;
}
}
DWORD addr = 0; int m = 0;
for (auto _ : addr_hit) {
if (_.second > m) {
m = _.second;
addr = _.first;
}
}
if(!addr)return false;
HookParam hp;
hp.address = addr;
hp.offset=get_stack(1);
hp.type = USING_STRING | CODEC_UTF8;
hp.filter_fun = [](LPVOID data, size_t* size, HookParam*) {
auto text = reinterpret_cast<LPSTR>(data);
auto len = reinterpret_cast<size_t*>(size);
StringCharReplacer(text, len, "\\n", 2, '\n');
return true;
};
return NewHook(hp, "5bp");
}
} // namespace name
bool _5pb_2::attach_function() {
bool ___1 = __1() || __();
return InsertKaleidoHook() || ___1;
}

23
LunaHook/engine32/5pb.h Normal file
View File

@ -0,0 +1,23 @@
#include"engine.h"
class _5pb:public ENGINE{
public:
_5pb(){
is_engine_certain=false;
check_by=CHECK_BY::FILE_ANY;
check_by_target=check_by_list{ L"data\\*.cpk",L"*.cpk",L"*.mpk",L"USRDIR\\*.mpk"};
};
bool attach_function();
};
class _5pb_2:public ENGINE{
public:
_5pb_2(){
check_by=CHECK_BY::FILE;
check_by_target=L"windata/script_body.bin";
is_engine_certain=false;
};
bool attach_function();
};

View File

@ -0,0 +1,82 @@
#include"AB2Try.h"
/********************************************************************************************
AkabeiSoft2Try hook:
Game folder contains YaneSDK.dll. Maybe we should call the engine Yane(<EFBFBD> = roof)?
This engine is based on .NET framework. This really makes it troublesome to locate a
valid hook address. The problem is that the engine file merely contains bytecode for
the CLR. Real meaningful object code is generated dynamically and the address is randomized.
Therefore the easiest method is to brute force search whole address space. While it's not necessary
to completely search the whole address space, since non-executable pages can be excluded first.
The generated code sections do not belong to any module(exe/dll), hence they do not have
a section name. So we can also exclude executable pages from all modules. At last, the code
section should be long(>0x2000). The remain address space should be several MBs in size and
can be examined in reasonable time(less than 0.1s for P8400 Win7x64).
Characteristic sequence is 0F B7 44 50 0C, stands for movzx eax, word ptr [edx*2 + eax + C].
Obviously this instruction extracts one unicode character from a string.
A main shortcoming is that the code is not generated if it hasn't been used yet.
So if you are in title screen this approach will fail.
********************************************************************************************/
namespace { // unnamed
typedef struct _NSTRING
{
PVOID vfTable;
DWORD lenWithNull;
DWORD lenWithoutNull;
WCHAR str[1];
} NSTRING;
// qsort correctly identifies overflow.
int cmp(const void * a, const void * b)
{ return *(int*)a - *(int*)b; }
void SpecialHookAB2Try(hook_stack* stack, HookParam *, uintptr_t *data, uintptr_t *split, size_t *len)
{
//DWORD test = *(DWORD*)(esp_base - 0x10);
DWORD edx = stack->edx;
if (edx != 0)
return;
//NSTRING *s = *(NSTRING **)(esp_base - 8);
if (const NSTRING *s = (NSTRING *)stack->eax) {
*len = s->lenWithoutNull << 1;
*data = (DWORD)s->str;
//*split = 0;
*split = FIXED_SPLIT_VALUE; // 8/3/2014 jichi: change to single threads
}
}
bool FindCharacteristInstruction()
{
const BYTE bytes[] = { 0x0F, 0xB7, 0x44, 0x50, 0x0C, 0x89 };
for (auto addr : Util::SearchMemory(bytes, sizeof(bytes), PAGE_EXECUTE_READWRITE))
{
//GROWL_DWORD(addr);
HookParam hp;
hp.address = addr;
hp.text_fun = SpecialHookAB2Try;
hp.type = USING_STRING | NO_CONTEXT | CODEC_UTF16;
ConsoleOutput("INSERT AB2Try");
//ConsoleOutput("Please adjust text speed to fastest/immediate.");
//RegisterEngineType(ENGINE_AB2T);
return NewHook(hp, "AB2Try");
}
return false;
}
} // unnamed namespace
bool InsertAB2TryHook()
{
bool ret = FindCharacteristInstruction();
if (ret)
ConsoleOutput("AB2Try: found characteristic sequence");
else
ConsoleOutput("AB2Try: cannot find characteristic sequence. Make sure you have start the game and have seen some text on the screen.");
return ret;
}
bool AB2Try::attach_function() {
return InsertAB2TryHook();
}

View File

@ -0,0 +1,11 @@
#include"engine.h"
class AB2Try:public ENGINE{
public:
AB2Try(){
check_by=CHECK_BY::FILE;
check_by_target=L"Yanesdk.dll";
};
bool attach_function();
};

View File

@ -0,0 +1,25 @@
#include"ACTGS.h"
bool ACTGS::attach_function() {
const BYTE bytes[] = {
0x0F,0xBE,0xD0,
0x83,0xFA,0x20,
0x74,XX,
0x83,0xfa,0x09,
0x75,XX
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) return false;
addr = findfuncstart(addr);
if (!addr) return false;
HookParam hp;
hp.address = addr;
hp.offset=get_stack(2);
hp.type = USING_STRING;
hp.filter_fun = all_ascii_Filter;
return NewHook(hp, "ACTGS");
}

11
LunaHook/engine32/ACTGS.h Normal file
View File

@ -0,0 +1,11 @@
#include"engine.h"
class ACTGS:public ENGINE{
public:
ACTGS(){
check_by=CHECK_BY::RESOURCE_STR;
check_by_target=L"ACTRESS Game System";
};
bool attach_function();
};

90
LunaHook/engine32/AGS.cpp Normal file
View File

@ -0,0 +1,90 @@
#include"AGS.h"
bool InsertAGSHook()
{
const BYTE bytes1[] = {
/*.text:0043E3A0 55 push ebp
.text : 0043E3A1 8B EC mov ebp, esp
.text : 0043E3A3 83 EC 38 sub esp, 38h
.text : 0043E3A6 53 push ebx
.text : 0043E3A7 56 push esi
.text : 0043E3A8 8B F1 mov esi, ecx*/
0x55,
0x8b,0xec,
0x83,0xec,0x38,0x53,0x56,0x8b,0xf1
};
ULONG addr = MemDbg::findBytes(bytes1, sizeof(bytes1), processStartAddress, processStopAddress);
if (!addr) {
return false;
}
const BYTE bytes2[] = {
/* .text:0043E95E FF 75 08 push[ebp + arg_0]
.text:0043E961 8B CE mov ecx, esi
.text : 0043E963 E8 38 FA FF FF call sub_43E3A0*/
0xff,0x75,0x08,
0x8b,0xce
};
bool ok = false;
auto addrs = findrelativecall(bytes2, sizeof(bytes2), addr, processStartAddress, processStopAddress);
for(auto addr :addrs){
addr = findfuncstart(addr);
if (!addr)continue;
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::eax);
hp.type = USING_STRING;
ConsoleOutput("INSERT HOOK_AGS %p",addr);
ok |= NewHook(hp, "HOOK_AGS");
}
return ok;
}
namespace{
bool hook2(){
//誘惑女教師~熟れた蜜の味~
auto entry=Util::FindImportEntry(processStartAddress,(DWORD)TextOutA);
if(entry==0)return false;
BYTE bytes[]={0xFF,0x15,XX4};
memcpy(bytes+2,&entry,4);
for (auto addr : Util::SearchMemory(bytes, sizeof(bytes), PAGE_EXECUTE, processStartAddress, processStopAddress) ) {
auto funcaddr = findfuncstart(addr,0x1000);
ConsoleOutput("funcaddr %p",funcaddr);
if (!funcaddr) continue;
BYTE sig1[]={0x68,0x00,0x80,0x00,0x00,0x6a,0x00};
BYTE sig2[]={0x2D,0xC0,0x00,0x00,0x00,0xC1,0xE0,0x08};
BYTE sig3[]={0x83,0xC0,0x80,0xC1,0xE0,0x08};
BYTE sig4[]={0x3C,0xA0,0x0F,0xB6,0xC0};
int found=0;
for(auto sigsz:std::vector<std::pair<BYTE*,int>>{{sig1,sizeof(sig1)},{sig2,sizeof(sig2)},{sig3,sizeof(sig3)},{sig4,sizeof(sig4)}}){
auto fd= MemDbg::findBytes(sigsz.first, sigsz.second, funcaddr, addr);
ConsoleOutput("%p",fd);
if(fd)found+=1;
}
if(found==4){
HookParam hp;
hp.address = funcaddr;
hp.type = DATA_INDIRECT;
hp.offset=get_stack(1);
hp.index=0;
return NewHook(hp, "AGS");
}
}
return false;
}
}
bool AGS::attach_function() {
return InsertAGSHook()||hook2();
}

12
LunaHook/engine32/AGS.h Normal file
View File

@ -0,0 +1,12 @@
#include"engine.h"
class AGS:public ENGINE{
public:
AGS(){
check_by=CHECK_BY::FILE_ANY;
check_by_target=check_by_list{L"voice/*.pk",L"sound/*.pk",L"misc/*.pk"};
is_engine_certain=false;
};
bool attach_function();
};

View File

@ -0,0 +1,58 @@
#include"AIL2.h"
bool InsertAIL2Hook() {
auto findalign = [](uintptr_t addr1) {
const BYTE pattern[] = { 0x90,0x90,0x83,0xec };
return reverseFindBytes(pattern, sizeof(pattern), processStartAddress, addr1) + 2;
};
bool succ=false;
BYTE bytes1[] = {
// .text:0042E5DF 3C 66 cmp al, 66h; 'f'
//.text:0042E5E1 74 57 jz short loc_42E63A
//.text : 0042E5E1
//.text : 0042E5E3 3C 70 cmp al, 70h; 'p'
//.text:0042E5E5 74 4C jz short loc_42E633
//.text : 0042E5E5
//.text : 0042E5E7 3C 73 cmp al, 73h; 's'
//.text:0042E5E9 74 37 jz short loc_42E622
0x3c,0x66,
0x74,XX,
0x3c,0x70,
0x74,XX,
0x3c,0x73,
0x74,XX
};
auto addr1 = MemDbg::findBytes(bytes1, sizeof(bytes1), processStartAddress, processStopAddress);
if (addr1 == 0) return false;
addr1 = findalign(addr1);
if (addr1 == 0) return false;
ConsoleOutput("AIL1 %p", addr1);
HookParam hp;
hp.address = addr1;
hp.codepage = 932;
hp.offset=get_stack(3);
hp.type = USING_STRING;
succ|=NewHook(hp, "AIL1");
BYTE bytes[] = { //if ( v12 != 32 && v12 != 33088 )
0x3d,0x40,0x81,0x00,0x00,0x0f
};
addr1 = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (addr1 == 0) return succ;
addr1 = MemDbg::findEnclosingAlignedFunction(addr1);
if (addr1 == 0) return succ;
hp = {};
hp.address = addr1;
hp.codepage = 932;
hp.offset=get_stack(4);
hp.type = USING_STRING | USING_SPLIT;
hp.split_index = 0;
succ|=NewHook(hp, "AIL2");
return succ;
}
bool AIL2::attach_function() {
//アイル
return InsertAIL2Hook();
}

11
LunaHook/engine32/AIL2.h Normal file
View File

@ -0,0 +1,11 @@
#include"engine.h"
class AIL2:public ENGINE{
public:
AIL2(){
check_by=CHECK_BY::FILE;
check_by_target=L"Gall*.dat";
};
bool attach_function();
};

279
LunaHook/engine32/AOS.cpp Normal file
View File

@ -0,0 +1,279 @@
#include"AOS.h"
/**
* jichi 4/1/2014: Insert AOS hook
* About <EFBFBD>: http://erogetrailers.com/brand/165
* About AOS: http://asmodean.reverse.net/pages/exaos.html
*
* Sample games:
*
* [140228] [Sugar Pot] <EFBFBD> V1.00 H-CODE by <EFBFBD>쿿
* - /HB8*0@3C2F0:<EFBFBD>.exe
* - /HBC*0@3C190:<EFBFBD>.exe
*
* [120224] [Sugar Pot] <EFBFBD>
*
* LiLiM games
*
* /HB8*0@3C2F0:<EFBFBD>
* - addr: 246512 = 0x3c2f0
* - length_offset: 1
* - module: 1814017450
* - off: 8
* - type: 72 = 0x48
*
* 00e3c2ed cc int3
* 00e3c2ee cc int3
* 00e3c2ef cc int3
* 00e3c2f0 /$ 51 push ecx ; jichi: hook here, function starts
* 00e3c2f1 |. a1 0c64eb00 mov eax,dword ptr ds:[0xeb640c]
* 00e3c2f6 |. 8b0d 7846eb00 mov ecx,dword ptr ds:[0xeb4678]
* 00e3c2fc |. 53 push ebx
* 00e3c2fd |. 55 push ebp
* 00e3c2fe |. 8b6c24 10 mov ebp,dword ptr ss:[esp+0x10]
* 00e3c302 |. 56 push esi
* 00e3c303 |. 8b35 c446eb00 mov esi,dword ptr ds:[0xeb46c4]
* 00e3c309 |. 57 push edi
* 00e3c30a |. 0fb63d c746eb00 movzx edi,byte ptr ds:[0xeb46c7]
* 00e3c311 |. 81e6 ffffff00 and esi,0xffffff
* 00e3c317 |. 894424 18 mov dword ptr ss:[esp+0x18],eax
* 00e3c31b |. 85ff test edi,edi
* 00e3c31d |. 74 6b je short <EFBFBD>00e3c38a
* 00e3c31f |. 8bd9 mov ebx,ecx
* 00e3c321 |. 85db test ebx,ebx
* 00e3c323 |. 74 17 je short <EFBFBD>00e3c33c
* 00e3c325 |. 8b4b 28 mov ecx,dword ptr ds:[ebx+0x28]
* 00e3c328 |. 56 push esi ; /color
* 00e3c329 |. 51 push ecx ; |hdc
* 00e3c32a |. ff15 3c40e800 call dword ptr ds:[<&gdi32.SetTextColor>>; \settextcolor
* 00e3c330 |. 89b3 c8000000 mov dword ptr ds:[ebx+0xc8],esi
* 00e3c336 |. 8b0d 7846eb00 mov ecx,dword ptr ds:[0xeb4678]
* 00e3c33c |> 0fbf55 1c movsx edx,word ptr ss:[ebp+0x1c]
* 00e3c340 |. 0fbf45 0a movsx eax,word ptr ss:[ebp+0xa]
* 00e3c344 |. 0fbf75 1a movsx esi,word ptr ss:[ebp+0x1a]
* 00e3c348 |. 03d7 add edx,edi
* 00e3c34a |. 03c2 add eax,edx
* 00e3c34c |. 0fbf55 08 movsx edx,word ptr ss:[ebp+0x8]
* 00e3c350 |. 03f7 add esi,edi
* 00e3c352 |. 03d6 add edx,esi
* 00e3c354 |. 85c9 test ecx,ecx
* 00e3c356 |. 74 32 je short <EFBFBD>00e3c38a
*/
bool InsertAOS1Hook()
{
// jichi 4/2/2014: The starting of this function is different from ヂ<>ツキ
// So, use a pattern in the middle of the function instead.
//
//const BYTE bytes[] = {
// 0x51, // 00e3c2f0 /$ 51 push ecx ; jichi: hook here, function begins
// 0xa1, 0x0c,0x64,0xeb,0x00, // 00e3c2f1 |. a1 0c64eb00 mov eax,dword ptr ds:[0xeb640c]
// 0x8b,0x0d, 0x78,0x46,0xeb,0x00, // 00e3c2f6 |. 8b0d 7846eb00 mov ecx,dword ptr ds:[0xeb4678]
// 0x53, // 00e3c2fc |. 53 push ebx
// 0x55, // 00e3c2fd |. 55 push ebp
// 0x8b,0x6c,0x24, 0x10, // 00e3c2fe |. 8b6c24 10 mov ebp,dword ptr ss:[esp+0x10]
// 0x56, // 00e3c302 |. 56 push esi
// 0x8b,0x35, 0xc4,0x46,0xeb,0x00, // 00e3c303 |. 8b35 c446eb00 mov esi,dword ptr ds:[0xeb46c4]
// 0x57, // 00e3c309 |. 57 push edi
// 0x0f,0xb6,0x3d, 0xc7,0x46,0xeb,0x00, // 00e3c30a |. 0fb63d c746eb00 movzx edi,byte ptr ds:[0xeb46c7]
// 0x81,0xe6, 0xff,0xff,0xff,0x00 // 00e3c311 |. 81e6 ffffff00 and esi,0xffffff
//};
//enum { addr_offset = 0 };
const BYTE bytes[] = {
0x0f,0xbf,0x55, 0x1c, // 00e3c33c |> 0fbf55 1c movsx edx,word ptr ss:[ebp+0x1c]
0x0f,0xbf,0x45, 0x0a, // 00e3c340 |. 0fbf45 0a movsx eax,word ptr ss:[ebp+0xa]
0x0f,0xbf,0x75, 0x1a, // 00e3c344 |. 0fbf75 1a movsx esi,word ptr ss:[ebp+0x1a]
0x03,0xd7, // 00e3c348 |. 03d7 add edx,edi
0x03,0xc2, // 00e3c34a |. 03c2 add eax,edx
0x0f,0xbf,0x55, 0x08, // 00e3c34c |. 0fbf55 08 movsx edx,word ptr ss:[ebp+0x8]
0x03,0xf7, // 00e3c350 |. 03f7 add esi,edi
0x03,0xd6, // 00e3c352 |. 03d6 add edx,esi
0x85,0xc9 // 00e3c354 |. 85c9 test ecx,ecx
};
enum { addr_offset = 0x00e3c2f0 - 0x00e3c33c }; // distance to the beginning of the function, which is 0x51 (push ecx)
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
//GROWL(reladdr);
if (!addr) {
ConsoleOutput("AOS1: pattern not found");
return false;
}
addr += addr_offset;
//GROWL(addr);
enum { push_ecx = 0x51 }; // beginning of the function
if (*(BYTE *)addr != push_ecx) {
ConsoleOutput("AOS1: beginning of the function not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_stack(2);
hp.type = DATA_INDIRECT;
ConsoleOutput("INSERT AOS1");
return NewHook(hp, "AOS1");
}
bool InsertAOS2Hook()
{
const BYTE bytes[] = {
0x51, // 00C4E7E0 /$ 51 PUSH ECX ; mireado: hook here, function begins
0x33,0xc0, // 00C4E7E1 |. 33C0 XOR EAX,EAX
0x53, // 00C4E7E3 |. 53 PUSH EBX
0x55, // 00C4E7E4 |. 55 PUSH EBP
0x8b,0x2d//, XX4, // 00C4E7E5 |. 8B2D 40A3CF00 MOV EBP,DWORD PTR DS:[0CFA340] ; mireado: some time changing 40A3CF00 => 40A3C000
//0x89,0x07, // 00C4E7EB |. 8907 MOV DWORD PTR DS:[EDI],EAX
//0x89,0x47, 0x04 // 00C4E7ED |. 8947 04 MOV DWORD PTR DS:[EDI+4],EAX
//0x56, // 00C4E7F0 |. 56 PUSH ESI
//0x8b,0x75, 0x44 // 00C4E7F1 |. 8B75 44 MOV ESI,DWORD PTR SS:[EBP+44]
};
enum { addr_offset = 0 }; // distance to the beginning of the function, which is 0x51 (push ecx)
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
//GROWL(reladdr);
if (!addr) {
ConsoleOutput("AOS2: pattern not found");
return false;
}
addr += addr_offset;
//GROWL(addr);
enum { push_ecx = 0x51 }; // beginning of the function
if (*(BYTE *)addr != push_ecx) {
ConsoleOutput("AOS2: beginning of the function not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_stack(2);
hp.type = DATA_INDIRECT;
ConsoleOutput("INSERT AOS2");
return NewHook(hp, "AOS2");
}
bool InsertAOSHook()
{ return InsertAOS1Hook() || InsertAOS2Hook();}
namespace{
DWORD calladdr(DWORD addr){
if(addr==0)return 0;
BYTE callop[] = { 0xe8 };
addr = reverseFindBytes(callop, sizeof(callop), addr - 0x20, addr);
if (addr == 0)return 0;
auto calladdr = *(int*)((char*)addr + 1);
ConsoleOutput("calladdr %p", calladdr);
addr = calladdr + addr + 5;
ConsoleOutput("funcaddr %p", addr);
if (*(BYTE*)((BYTE*)addr - 1) != 0xcc)return 0;
return addr;
}
DWORD lastcall(){
auto entry=Util::FindImportEntry(processStartAddress,(DWORD)TextOutA);
if(entry==0)return 0;
BYTE bytes[]={0xFF,0x15,XX4};
memcpy(bytes+2,&entry,4);
auto addr = reverseFindBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if(addr==0)return 0;
addr = MemDbg::findEnclosingAlignedFunction(addr);
return addr;
}
}
regs mov_reg_ebpoffset(int reg) {
switch (reg) {
case 0x4B:
return regs::ebx;
case 0x48:
return regs::eax;
case 0x49:
return regs::ecx;
case 0x4a:
return regs::edx;
case 0x4c:
return regs::ebp;
case 0x4d:
return regs::esp;
case 0x4e:
return regs::esi;
case 0x4f:
return regs::edi;
default:
return regs::invalid;
}
}
bool AOS_EX() {
BYTE aos_shared_bytes1[] = {
0x3c,XX,
0x74,XX,
0x3c,XX,
0x74,XX,
0x3c,XX,
0x74,XX,
0x3c,XX,
0x74,XX,
0x3c,XX,
0x74,XX,
};
BYTE aos_shared_bytes2[] = {
0x80,0xfb,XX,
0x74,XX,
0x80,0xfb,XX,
0x74,XX,
0x80,0xfb,XX,
0x74,XX,
0x80,0xfb,XX,
0x74,XX
};
std::vector<DWORD>addrs;
addrs.push_back(calladdr(MemDbg::findBytes(aos_shared_bytes1, sizeof(aos_shared_bytes1), processStartAddress, processStopAddress)));
addrs.push_back(calladdr(MemDbg::findBytes(aos_shared_bytes2, sizeof(aos_shared_bytes2), processStartAddress, processStopAddress)));
addrs.push_back(lastcall());
for(auto addr: addrs){
if (addr == 0)continue;
auto reg = mov_reg_ebpoffset(*(BYTE*)((BYTE*)addr + 5));
int off;
if (reg!=regs::invalid){
//usercall
off=get_reg(reg);
}
else if(((*(WORD*)addr))==0xec83) {
//姫様LOVEライフ
//也是usercall但是第二个参数是栈上。
off=get_stack(1);
}
else{
//螺旋遡行のディストピア -The infinite set of alternative version- 官方中文
BYTE sig[]={0x89,0x55,0xFC};
if(MemDbg::findBytes(sig, sizeof(sig), addr, addr+0x20)){
off=get_reg(regs::edx);
}
else{
//cdecl;
off=get_stack(2);
}
}
HookParam hp;
hp.address = addr;
hp.offset = off;
hp.type = NO_CONTEXT | DATA_INDIRECT;
hp.index = 0;
return NewHook(hp, "AOS_EX");
}
return false;
}
bool AOS::attach_function() {
bool b1=InsertAOSHook();
bool b3=AOS_EX();
return b1||b3;
}

11
LunaHook/engine32/AOS.h Normal file
View File

@ -0,0 +1,11 @@
#include"engine.h"
class AOS:public ENGINE{
public:
AOS(){
check_by=CHECK_BY::FILE;
check_by_target=L"*.aos";
};
bool attach_function();
};

46
LunaHook/engine32/AXL.cpp Normal file
View File

@ -0,0 +1,46 @@
#include"AXL.h"
bool InsertAXLHook() {
//キミの声がきこえる
BYTE bytes[] = {
0x0f,0x95,0xc2,0x33,0xc0,0xB9,0x41,0x00,0x00,0x00
};
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (addr == 0)return false;
addr = findfuncstart(addr,0x1000);
if (addr == 0)return false;
HookParam hp;
hp.address = addr ;
hp.offset = get_stack(4);
hp.type = USING_STRING;
return NewHook(hp, "AXL");
}
namespace{
bool hook2(){
//剣乙女ノア
//Maria天使のキスと悪魔の花嫁
BYTE bytes[] = {
0x55,0x8b,0xec,
0x56,
0x8b,0xf0,
0x3b,0x9e,0x8c,0xf8,0x00,0x00,
0x57
};
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (addr == 0)return false;
HookParam hp;
hp.address = addr ;
hp.offset=get_stack(1);
hp.split=get_reg(regs::eax);
hp.type=USING_SPLIT;
return NewHook(hp, "TAILWIND");
}
}
bool AXL::attach_function() {
return InsertAXLHook()||hook2();
}

12
LunaHook/engine32/AXL.h Normal file
View File

@ -0,0 +1,12 @@
#include"engine.h"
class AXL:public ENGINE{
public:
AXL(){
check_by=CHECK_BY::FILE;
check_by_target=L"script.arc";
is_engine_certain=false;
};
bool attach_function();
};

View File

@ -0,0 +1,21 @@
#include"Abalone.h"
bool AbaloneHook() {
BYTE bytes[] = {
0x8B,0x44,0x24,XX,
0x80,0x38,0x00,
0x74
};
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
ConsoleOutput("AbaloneHook %p", addr);
if (addr == 0)return false;
HookParam hp;
hp.address = addr+4;
hp.offset=get_reg(regs::eax);
hp.type = DATA_INDIRECT;
hp.index = 0;
return NewHook(hp, "AbaloneHook");
}
bool Abalone::attach_function() {
return AbaloneHook();
}

View File

@ -0,0 +1,12 @@
#include"engine.h"
class Abalone:public ENGINE{
public:
Abalone(){
check_by=CHECK_BY::FILE;
check_by_target=L"Archive.dat";
is_engine_certain=false;
};
bool attach_function();
};

424
LunaHook/engine32/Abel.cpp Normal file
View File

@ -0,0 +1,424 @@
#include"Abel.h"
/********************************************************************************************
AbelSoftware hook:
The game folder usually is made up many no extended name files(file name doesn't have '.').
And these files have common prefix which is the game name, and 2 digit in order.
********************************************************************************************/
/** 7/31/2015
* Sample game <EFBFBD> * Hooked address: 0x4413b0
*
* GDI functions are cached: TextOutA and GetTextExtentPoint32A
*
* 004413AB 90 NOP
* 004413AC 90 NOP
* 004413AD 90 NOP
* 004413AE 90 NOP
* 004413AF 90 NOP
* 004413B0 6A FF PUSH -0x1 ; jichi: text in arg1, but text painted character by character
* 004413B2 68 D0714900 PUSH .004971D0
* 004413B7 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
* 004413BD 50 PUSH EAX
* 004413BE 64:8925 00000000 MOV DWORD PTR FS:[0],ESP
* 004413C5 83EC 4C SUB ESP,0x4C
* 004413C8 A1 C00B4B00 MOV EAX,DWORD PTR DS:[0x4B0BC0]
* 004413CD 53 PUSH EBX
* 004413CE 55 PUSH EBP
* 004413CF 56 PUSH ESI
* 004413D0 57 PUSH EDI
* 004413D1 8BF1 MOV ESI,ECX
* 004413D3 894424 48 MOV DWORD PTR SS:[ESP+0x48],EAX
* 004413D7 894424 4C MOV DWORD PTR SS:[ESP+0x4C],EAX
* 004413DB 894424 58 MOV DWORD PTR SS:[ESP+0x58],EAX
* 004413DF 8B4424 6C MOV EAX,DWORD PTR SS:[ESP+0x6C]
* 004413E3 33DB XOR EBX,EBX
* 004413E5 50 PUSH EAX
* 004413E6 8D4C24 4C LEA ECX,DWORD PTR SS:[ESP+0x4C]
* 004413EA 895C24 68 MOV DWORD PTR SS:[ESP+0x68],EBX
* 004413EE E8 74520400 CALL .00486667
* 004413F3 8B4C24 78 MOV ECX,DWORD PTR SS:[ESP+0x78]
* 004413F7 51 PUSH ECX
* 004413F8 8D4C24 50 LEA ECX,DWORD PTR SS:[ESP+0x50]
* 004413FC E8 66520400 CALL .00486667
* 00441401 8B5424 7C MOV EDX,DWORD PTR SS:[ESP+0x7C]
* 00441405 8D4C24 58 LEA ECX,DWORD PTR SS:[ESP+0x58]
* 00441409 52 PUSH EDX
* 0044140A E8 58520400 CALL .00486667
* 0044140F 8B4424 70 MOV EAX,DWORD PTR SS:[ESP+0x70]
* 00441413 894424 50 MOV DWORD PTR SS:[ESP+0x50],EAX
* 00441417 8B4424 74 MOV EAX,DWORD PTR SS:[ESP+0x74]
* 0044141B 83F8 FF CMP EAX,-0x1
* 0044141E 75 06 JNZ SHORT .00441426
* 00441420 895C24 54 MOV DWORD PTR SS:[ESP+0x54],EBX
* 00441424 EB 2E JMP SHORT .00441454
* 00441426 8BC8 MOV ECX,EAX
* 00441428 33D2 XOR EDX,EDX
* 0044142A 81E1 FF000000 AND ECX,0xFF
* 00441430 8AD4 MOV DL,AH
* 00441432 81C9 00FFFFFF OR ECX,0xFFFFFF00
* 00441438 81E2 FF000000 AND EDX,0xFF
* 0044143E C1E1 08 SHL ECX,0x8
* 00441441 0BCA OR ECX,EDX
* 00441443 C1E8 10 SHR EAX,0x10
* 00441446 C1E1 08 SHL ECX,0x8
* 00441449 25 FF000000 AND EAX,0xFF
* 0044144E 0BC8 OR ECX,EAX
* 00441450 894C24 54 MOV DWORD PTR SS:[ESP+0x54],ECX
* 00441454 8B4424 48 MOV EAX,DWORD PTR SS:[ESP+0x48]
* 00441458 3958 F8 CMP DWORD PTR DS:[EAX-0x8],EBX
* 0044145B 0F84 7A030000 JE .004417DB
* 00441461 8B8E 08020000 MOV ECX,DWORD PTR DS:[ESI+0x208]
* 00441467 83F9 20 CMP ECX,0x20
* 0044146A 0F8D 35030000 JGE .004417A5
* 00441470 0FBE00 MOVSX EAX,BYTE PTR DS:[EAX]
* 00441473 83E8 09 SUB EAX,0x9
* 00441476 0F84 29030000 JE .004417A5
* 0044147C 48 DEC EAX
* 0044147D 0F84 0A030000 JE .0044178D
* 00441483 83E8 03 SUB EAX,0x3
* 00441486 0F84 19030000 JE .004417A5
* 0044148C 8BBE 38010000 MOV EDI,DWORD PTR DS:[ESI+0x138]
* 00441492 68 80C84A00 PUSH .004AC880
* 00441497 8BCF MOV ECX,EDI
* 00441499 E8 E2E9FDFF CALL .0041FE80
* 0044149E 3BC3 CMP EAX,EBX
* 004414A0 7D 0F JGE SHORT .004414B1
* 004414A2 53 PUSH EBX
* 004414A3 53 PUSH EBX
* 004414A4 53 PUSH EBX
* 004414A5 53 PUSH EBX
* 004414A6 8D4C24 48 LEA ECX,DWORD PTR SS:[ESP+0x48]
* 004414AA E8 916DFDFF CALL .00418240
* 004414AF EB 06 JMP SHORT .004414B7
* 004414B1 8B4F 24 MOV ECX,DWORD PTR DS:[EDI+0x24]
* 004414B4 8B0481 MOV EAX,DWORD PTR DS:[ECX+EAX*4]
* 004414B7 8B48 04 MOV ECX,DWORD PTR DS:[EAX+0x4]
* 004414BA 8B10 MOV EDX,DWORD PTR DS:[EAX]
* 004414BC 894C24 24 MOV DWORD PTR SS:[ESP+0x24],ECX
* 004414C0 895424 20 MOV DWORD PTR SS:[ESP+0x20],EDX
* 004414C4 8B50 08 MOV EDX,DWORD PTR DS:[EAX+0x8]
* 004414C7 8B40 0C MOV EAX,DWORD PTR DS:[EAX+0xC]
* 004414CA 8D4C24 10 LEA ECX,DWORD PTR SS:[ESP+0x10]
* 004414CE 895424 28 MOV DWORD PTR SS:[ESP+0x28],EDX
* 004414D2 51 PUSH ECX
* 004414D3 8BCE MOV ECX,ESI
* 004414D5 894424 30 MOV DWORD PTR SS:[ESP+0x30],EAX
* 004414D9 E8 52F3FFFF CALL .00440830
* 004414DE 8B5424 50 MOV EDX,DWORD PTR SS:[ESP+0x50]
* 004414E2 33C9 XOR ECX,ECX
* 004414E4 894C24 78 MOV DWORD PTR SS:[ESP+0x78],ECX
* 004414E8 B8 B0B64900 MOV EAX,.0049B6B0
* 004414ED 3B10 CMP EDX,DWORD PTR DS:[EAX]
* 004414EF 7E 0B JLE SHORT .004414FC
* 004414F1 83C0 04 ADD EAX,0x4
* 004414F4 41 INC ECX
* 004414F5 3D C0B64900 CMP EAX,.0049B6C0
* 004414FA ^72 F1 JB SHORT .004414ED
* 004414FC 8B5424 48 MOV EDX,DWORD PTR SS:[ESP+0x48]
* 00441500 8D4424 18 LEA EAX,DWORD PTR SS:[ESP+0x18]
* 00441504 894C24 78 MOV DWORD PTR SS:[ESP+0x78],ECX
* 00441508 8B4C8E 3C MOV ECX,DWORD PTR DS:[ESI+ECX*4+0x3C]
* 0044150C 52 PUSH EDX
* 0044150D 50 PUSH EAX
* 0044150E E8 3D34FCFF CALL .00404950
* 00441513 8B46 38 MOV EAX,DWORD PTR DS:[ESI+0x38]
* 00441516 895C24 70 MOV DWORD PTR SS:[ESP+0x70],EBX
* 0044151A 3BC3 CMP EAX,EBX
* 0044151C 0F84 F9000000 JE .0044161B
* 00441522 8B50 08 MOV EDX,DWORD PTR DS:[EAX+0x8]
* 00441525 8B4E 78 MOV ECX,DWORD PTR DS:[ESI+0x78]
* 00441528 3BCA CMP ECX,EDX
* 0044152A 0F8D EB000000 JGE .0044161B
* 00441530 8B50 04 MOV EDX,DWORD PTR DS:[EAX+0x4]
* 00441533 8B4424 10 MOV EAX,DWORD PTR SS:[ESP+0x10]
* 00441537 8B7E 74 MOV EDI,DWORD PTR DS:[ESI+0x74]
* 0044153A 8B2C8A MOV EBP,DWORD PTR DS:[EDX+ECX*4]
* 0044153D 8B4C24 18 MOV ECX,DWORD PTR SS:[ESP+0x18]
* 00441541 897C24 7C MOV DWORD PTR SS:[ESP+0x7C],EDI
* 00441545 8B55 00 MOV EDX,DWORD PTR SS:[EBP]
* 00441548 8D1C01 LEA EBX,DWORD PTR DS:[ECX+EAX]
* 0044154B 8BCD MOV ECX,EBP
* 0044154D FF52 08 CALL DWORD PTR DS:[EDX+0x8]
* 00441550 3BF8 CMP EDI,EAX
* 00441552 0F8D C3000000 JGE .0044161B
* 00441558 EB 04 JMP SHORT .0044155E
* 0044155A 8B7C24 7C MOV EDI,DWORD PTR SS:[ESP+0x7C]
* 0044155E 8B45 00 MOV EAX,DWORD PTR SS:[EBP]
* 00441561 57 PUSH EDI
* 00441562 8BCD MOV ECX,EBP
* 00441564 FF50 04 CALL DWORD PTR DS:[EAX+0x4]
* 00441567 8BF8 MOV EDI,EAX
* 00441569 8BCF MOV ECX,EDI
* 0044156B 8B17 MOV EDX,DWORD PTR DS:[EDI]
* 0044156D FF52 0C CALL DWORD PTR DS:[EDX+0xC]
* 00441570 85C0 TEST EAX,EAX
* 00441572 0F84 A3000000 JE .0044161B
* 00441578 8B07 MOV EAX,DWORD PTR DS:[EDI]
* 0044157A 8D4C24 6C LEA ECX,DWORD PTR SS:[ESP+0x6C]
* 0044157E 51 PUSH ECX
* 0044157F 8BCF MOV ECX,EDI
* 00441581 FF50 10 CALL DWORD PTR DS:[EAX+0x10]
* 00441584 8B5424 6C MOV EDX,DWORD PTR SS:[ESP+0x6C]
* 00441588 8B4C24 78 MOV ECX,DWORD PTR SS:[ESP+0x78]
* 0044158C 8D4424 30 LEA EAX,DWORD PTR SS:[ESP+0x30]
* 00441590 52 PUSH EDX
* 00441591 8B4C8E 3C MOV ECX,DWORD PTR DS:[ESI+ECX*4+0x3C]
* 00441595 50 PUSH EAX
* 00441596 C64424 6C 01 MOV BYTE PTR SS:[ESP+0x6C],0x1
* 0044159B E8 B033FCFF CALL .00404950
* 004415A0 8B10 MOV EDX,DWORD PTR DS:[EAX]
* 004415A2 8B86 E4030000 MOV EAX,DWORD PTR DS:[ESI+0x3E4]
* 004415A8 03DA ADD EBX,EDX
* 004415AA 8B5424 6C MOV EDX,DWORD PTR SS:[ESP+0x6C]
* 004415AE 52 PUSH EDX
* 004415AF 50 PUSH EAX
* 004415B0 E8 BB020000 CALL .00441870
* 004415B5 83C4 08 ADD ESP,0x8
* 004415B8 85C0 TEST EAX,EAX
* 004415BA 74 08 JE SHORT .004415C4
* 004415BC 3B5C24 28 CMP EBX,DWORD PTR SS:[ESP+0x28]
* 004415C0 7F 43 JG SHORT .00441605
* 004415C2 EB 18 JMP SHORT .004415DC
* 004415C4 8B4C24 6C MOV ECX,DWORD PTR SS:[ESP+0x6C]
* 004415C8 8B86 E0030000 MOV EAX,DWORD PTR DS:[ESI+0x3E0]
* 004415CE 51 PUSH ECX
* 004415CF 50 PUSH EAX
* 004415D0 E8 9B020000 CALL .00441870
* 004415D5 83C4 08 ADD ESP,0x8
* 004415D8 85C0 TEST EAX,EAX
* 004415DA 74 31 JE SHORT .0044160D
* 004415DC 8D4C24 6C LEA ECX,DWORD PTR SS:[ESP+0x6C]
* 004415E0 C64424 64 00 MOV BYTE PTR SS:[ESP+0x64],0x0
* 004415E5 E8 404F0400 CALL .0048652A
* 004415EA 8B7C24 7C MOV EDI,DWORD PTR SS:[ESP+0x7C]
* 004415EE 8B55 00 MOV EDX,DWORD PTR SS:[EBP]
* 004415F1 47 INC EDI
* 004415F2 8BCD MOV ECX,EBP
* 004415F4 897C24 7C MOV DWORD PTR SS:[ESP+0x7C],EDI
* 004415F8 FF52 08 CALL DWORD PTR DS:[EDX+0x8]
* 004415FB 3BF8 CMP EDI,EAX
* 004415FD ^0F8C 57FFFFFF JL .0044155A
* 00441603 EB 16 JMP SHORT .0044161B
* 00441605 C74424 70 010000>MOV DWORD PTR SS:[ESP+0x70],0x1
* 0044160D 8D4C24 6C LEA ECX,DWORD PTR SS:[ESP+0x6C]
* 00441611 C64424 64 00 MOV BYTE PTR SS:[ESP+0x64],0x0
* 00441616 E8 0F4F0400 CALL .0048652A
* 0044161B 8B4424 10 MOV EAX,DWORD PTR SS:[ESP+0x10]
* 0044161F 8B4C24 18 MOV ECX,DWORD PTR SS:[ESP+0x18]
* 00441623 03C8 ADD ECX,EAX
* 00441625 8B4424 28 MOV EAX,DWORD PTR SS:[ESP+0x28]
* 00441629 3BC8 CMP ECX,EAX
* 0044162B 7E 18 JLE SHORT .00441645
* 0044162D 8B5424 48 MOV EDX,DWORD PTR SS:[ESP+0x48]
* 00441631 8B86 E0030000 MOV EAX,DWORD PTR DS:[ESI+0x3E0]
* 00441637 52 PUSH EDX
* 00441638 50 PUSH EAX
* 00441639 E8 32020000 CALL .00441870
* 0044163E 83C4 08 ADD ESP,0x8
* 00441641 85C0 TEST EAX,EAX
* 00441643 74 08 JE SHORT .0044164D
* 00441645 8B4424 70 MOV EAX,DWORD PTR SS:[ESP+0x70]
* 00441649 85C0 TEST EAX,EAX
* 0044164B 74 3F JE SHORT .0044168C
* 0044164D 8B8E 08020000 MOV ECX,DWORD PTR DS:[ESI+0x208]
* 00441653 41 INC ECX
* 00441654 8BC1 MOV EAX,ECX
* 00441656 898E 08020000 MOV DWORD PTR DS:[ESI+0x208],ECX
* 0044165C 83F8 20 CMP EAX,0x20
* 0044165F 0F8D 40010000 JGE .004417A5
* 00441665 83EC 10 SUB ESP,0x10
* 00441668 8B15 D0B04A00 MOV EDX,DWORD PTR DS:[0x4AB0D0]
* 0044166E 8BDC MOV EBX,ESP
* 00441670 33C0 XOR EAX,EAX
* 00441672 8B3D D4B04A00 MOV EDI,DWORD PTR DS:[0x4AB0D4]
* 00441678 33C9 XOR ECX,ECX
* 0044167A 8903 MOV DWORD PTR DS:[EBX],EAX
* 0044167C 894B 04 MOV DWORD PTR DS:[EBX+0x4],ECX
* 0044167F 8BCE MOV ECX,ESI
* 00441681 8953 08 MOV DWORD PTR DS:[EBX+0x8],EDX
* 00441684 897B 0C MOV DWORD PTR DS:[EBX+0xC],EDI
* 00441687 E8 7418FCFF CALL .00402F00
* 0044168C 8B86 08020000 MOV EAX,DWORD PTR DS:[ESI+0x208]
* 00441692 6A 00 PUSH 0x0
* 00441694 8D0CC5 00000000 LEA ECX,DWORD PTR DS:[EAX*8]
* 0044169B 2BC8 SUB ECX,EAX
* 0044169D 8B948E 78040000 MOV EDX,DWORD PTR DS:[ESI+ECX*4+0x478]
* 004416A4 8DAC8E 70040000 LEA EBP,DWORD PTR DS:[ESI+ECX*4+0x470]
* 004416AB 52 PUSH EDX
* 004416AC 8BCD MOV ECX,EBP
* 004416AE E8 7D8A0000 CALL .0044A130
* 004416B3 8BD8 MOV EBX,EAX
* 004416B5 8D4424 48 LEA EAX,DWORD PTR SS:[ESP+0x48]
* 004416B9 50 PUSH EAX
* 004416BA 8D7B 08 LEA EDI,DWORD PTR DS:[EBX+0x8]
* 004416BD 8BCF MOV ECX,EDI
* 004416BF E8 534F0400 CALL .00486617
* 004416C4 8D4C24 4C LEA ECX,DWORD PTR SS:[ESP+0x4C]
* 004416C8 51 PUSH ECX
* 004416C9 8D4F 04 LEA ECX,DWORD PTR DS:[EDI+0x4]
* 004416CC E8 464F0400 CALL .00486617
* 004416D1 8B5424 50 MOV EDX,DWORD PTR SS:[ESP+0x50]
* 004416D5 8D4C24 58 LEA ECX,DWORD PTR SS:[ESP+0x58]
* 004416D9 8957 08 MOV DWORD PTR DS:[EDI+0x8],EDX
* 004416DC 8B4424 54 MOV EAX,DWORD PTR SS:[ESP+0x54]
* 004416E0 51 PUSH ECX
* 004416E1 8D4F 10 LEA ECX,DWORD PTR DS:[EDI+0x10]
* 004416E4 8947 0C MOV DWORD PTR DS:[EDI+0xC],EAX
* 004416E7 E8 2B4F0400 CALL .00486617
* 004416EC 8B45 08 MOV EAX,DWORD PTR SS:[EBP+0x8]
* 004416EF 85C0 TEST EAX,EAX
* 004416F1 74 04 JE SHORT .004416F7
* 004416F3 8918 MOV DWORD PTR DS:[EAX],EBX
* 004416F5 EB 03 JMP SHORT .004416FA
* 004416F7 895D 04 MOV DWORD PTR SS:[EBP+0x4],EBX
* 004416FA 83EC 10 SUB ESP,0x10
* 004416FD 895D 08 MOV DWORD PTR SS:[EBP+0x8],EBX
* 00441700 8B4424 20 MOV EAX,DWORD PTR SS:[ESP+0x20]
* 00441704 8B5424 28 MOV EDX,DWORD PTR SS:[ESP+0x28]
* 00441708 8B7C24 2C MOV EDI,DWORD PTR SS:[ESP+0x2C]
* 0044170C 8BDC MOV EBX,ESP
* 0044170E 8D4C02 02 LEA ECX,DWORD PTR DS:[EDX+EAX+0x2]
* 00441712 8B5424 24 MOV EDX,DWORD PTR SS:[ESP+0x24]
* 00441716 8903 MOV DWORD PTR DS:[EBX],EAX
* 00441718 8D7C3A 02 LEA EDI,DWORD PTR DS:[EDX+EDI+0x2]
* 0044171C 8953 04 MOV DWORD PTR DS:[EBX+0x4],EDX
* 0044171F 894B 08 MOV DWORD PTR DS:[EBX+0x8],ECX
* 00441722 8BCE MOV ECX,ESI
* 00441724 897B 0C MOV DWORD PTR DS:[EBX+0xC],EDI
* 00441727 E8 D417FCFF CALL .00402F00
* 0044172C 8B4424 4C MOV EAX,DWORD PTR SS:[ESP+0x4C]
* 00441730 8B48 F8 MOV ECX,DWORD PTR DS:[EAX-0x8]
* 00441733 85C9 TEST ECX,ECX
* 00441735 74 6E JE SHORT .004417A5
* 00441737 8B4E 3C MOV ECX,DWORD PTR DS:[ESI+0x3C]
* 0044173A 50 PUSH EAX
* 0044173B 8D4424 24 LEA EAX,DWORD PTR SS:[ESP+0x24]
* 0044173F 50 PUSH EAX
* 00441740 E8 0B32FCFF CALL .00404950
* 00441745 8B5C24 20 MOV EBX,DWORD PTR SS:[ESP+0x20]
* 00441749 8B4C24 18 MOV ECX,DWORD PTR SS:[ESP+0x18]
* 0044174D 8B7C24 24 MOV EDI,DWORD PTR SS:[ESP+0x24]
* 00441751 8BC3 MOV EAX,EBX
* 00441753 2BC1 SUB EAX,ECX
* 00441755 8BCF MOV ECX,EDI
* 00441757 99 CDQ
* 00441758 2BC2 SUB EAX,EDX
* 0044175A 8B5424 14 MOV EDX,DWORD PTR SS:[ESP+0x14]
* 0044175E F7D9 NEG ECX
* 00441760 D1F8 SAR EAX,1
* 00441762 03CA ADD ECX,EDX
* 00441764 8B5424 10 MOV EDX,DWORD PTR SS:[ESP+0x10]
* 00441768 F7D8 NEG EAX
* 0044176A 03C2 ADD EAX,EDX
* 0044176C 83EC 10 SUB ESP,0x10
* 0044176F 8D7C39 02 LEA EDI,DWORD PTR DS:[ECX+EDI+0x2]
* 00441773 8D5418 02 LEA EDX,DWORD PTR DS:[EAX+EBX+0x2]
* 00441777 8BDC MOV EBX,ESP
* 00441779 8903 MOV DWORD PTR DS:[EBX],EAX
* 0044177B 894B 04 MOV DWORD PTR DS:[EBX+0x4],ECX
* 0044177E 8BCE MOV ECX,ESI
* 00441780 8953 08 MOV DWORD PTR DS:[EBX+0x8],EDX
* 00441783 897B 0C MOV DWORD PTR DS:[EBX+0xC],EDI
* 00441786 E8 7517FCFF CALL .00402F00
* 0044178B EB 18 JMP SHORT .004417A5
* 0044178D 8D41 29 LEA EAX,DWORD PTR DS:[ECX+0x29]
* 00441790 8D14C5 00000000 LEA EDX,DWORD PTR DS:[EAX*8]
* 00441797 2BD0 SUB EDX,EAX
* 00441799 391C96 CMP DWORD PTR DS:[ESI+EDX*4],EBX
* 0044179C 74 07 JE SHORT .004417A5
* 0044179E 41 INC ECX
* 0044179F 898E 08020000 MOV DWORD PTR DS:[ESI+0x208],ECX
* 004417A5 8B86 E8020000 MOV EAX,DWORD PTR DS:[ESI+0x2E8]
* 004417AB 33DB XOR EBX,EBX
* 004417AD 3BC3 CMP EAX,EBX
* 004417AF 74 2A JE SHORT .004417DB
* 004417B1 399E C8030000 CMP DWORD PTR DS:[ESI+0x3C8],EBX
* 004417B7 75 22 JNZ SHORT .004417DB
* 004417B9 8B86 C4030000 MOV EAX,DWORD PTR DS:[ESI+0x3C4]
* 004417BF 8BCE MOV ECX,ESI
* 004417C1 50 PUSH EAX
* 004417C2 E8 89040000 CALL .00441C50
* 004417C7 3B86 3C020000 CMP EAX,DWORD PTR DS:[ESI+0x23C]
* 004417CD 74 06 JE SHORT .004417D5
* 004417CF 8986 38020000 MOV DWORD PTR DS:[ESI+0x238],EAX
* 004417D5 8986 3C020000 MOV DWORD PTR DS:[ESI+0x23C],EAX
* 004417DB 399E 30020000 CMP DWORD PTR DS:[ESI+0x230],EBX
* 004417E1 75 3C JNZ SHORT .0044181F
* 004417E3 8BCE MOV ECX,ESI
* 004417E5 E8 C6040000 CALL .00441CB0
* 004417EA 85C0 TEST EAX,EAX
* 004417EC 75 31 JNZ SHORT .0044181F
* 004417EE 399E 18020000 CMP DWORD PTR DS:[ESI+0x218],EBX
* 004417F4 74 29 JE SHORT .0044181F
* 004417F6 83BE C4020000 64 CMP DWORD PTR DS:[ESI+0x2C4],0x64
* 004417FD 74 20 JE SHORT .0044181F
* 004417FF 8B86 08020000 MOV EAX,DWORD PTR DS:[ESI+0x208]
* 00441805 83F8 20 CMP EAX,0x20
* 00441808 7D 1D JGE SHORT .00441827
* 0044180A 83C0 29 ADD EAX,0x29
* 0044180D 8D0CC5 00000000 LEA ECX,DWORD PTR DS:[EAX*8]
* 00441814 2BC8 SUB ECX,EAX
* 00441816 391C8E CMP DWORD PTR DS:[ESI+ECX*4],EBX
* 00441819 74 0C JE SHORT .00441827
* 0044181B 6A 01 PUSH 0x1
* 0044181D EB 01 JMP SHORT .00441820
* 0044181F 53 PUSH EBX
* 00441820 8BCE MOV ECX,ESI
* 00441822 E8 49C5FEFF CALL .0042DD70
* 00441827 8D4C24 58 LEA ECX,DWORD PTR SS:[ESP+0x58]
* 0044182B C74424 64 030000>MOV DWORD PTR SS:[ESP+0x64],0x3
* 00441833 E8 F24C0400 CALL .0048652A
* 00441838 8D4C24 4C LEA ECX,DWORD PTR SS:[ESP+0x4C]
* 0044183C C64424 64 02 MOV BYTE PTR SS:[ESP+0x64],0x2
* 00441841 E8 E44C0400 CALL .0048652A
* 00441846 8D4C24 48 LEA ECX,DWORD PTR SS:[ESP+0x48]
* 0044184A C74424 64 FFFFFF>MOV DWORD PTR SS:[ESP+0x64],-0x1
* 00441852 E8 D34C0400 CALL .0048652A
* 00441857 8B4C24 5C MOV ECX,DWORD PTR SS:[ESP+0x5C]
* 0044185B 5F POP EDI
* 0044185C 5E POP ESI
* 0044185D 5D POP EBP
* 0044185E 64:890D 00000000 MOV DWORD PTR FS:[0],ECX
* 00441865 5B POP EBX
* 00441866 83C4 58 ADD ESP,0x58
* 00441869 C2 1400 RETN 0x14
* 0044186C 90 NOP
* 0044186D 90 NOP
* 0044186E 90 NOP
* 0044186F 90 NOP
*
* Another sample game: <EFBFBD>
*/
bool InsertAbelHook()
{
// jichi: If this pattern failed again, try the following pattern instead:
// 004413D3 894424 48 MOV DWORD PTR SS:[ESP+0x48],EAX
// 004413D7 894424 4C MOV DWORD PTR SS:[ESP+0x4C],EAX
// 004413DB 894424 58 MOV DWORD PTR SS:[ESP+0x58],EAX
const DWORD character[] = {0xc981d48a, 0xffffff00};
if (DWORD j = SearchPattern(processStartAddress, processStopAddress - processStartAddress, character, sizeof(character))) {
j += processStartAddress;
for (DWORD i = j - 0x100; j > i; j--)
if (*(WORD *)j == 0xff6a) {
HookParam hp;
hp.address = j;
hp.offset=get_stack(1);
hp.type = USING_STRING|NO_CONTEXT;
ConsoleOutput("INSERT Abel");
//GROWL_DWORD(hp.address);
//RegisterEngineType(ENGINE_ABEL);
return NewHook(hp, "Abel");
}
}
ConsoleOutput("Abel: failed");
return false;
}
bool Abel::attach_function() {
return InsertAbelHook();
}

54
LunaHook/engine32/Abel.h Normal file
View File

@ -0,0 +1,54 @@
#include"engine.h"
class Abel:public ENGINE{
public:
Abel(){
check_by=CHECK_BY::CUSTOM;
check_by_target=[](){
// jichi 8/24/2013: Move into functions
// Artikash 6/15/2018: Removed this detection for Abel Software games. IthGetFileInfo no longer works correctly
//static BYTE static_file_info[0x1000];
//if (IthGetFileInfo(L"*01", static_file_info))
// if (*(DWORD*)static_file_info == 0) {
// STATUS_INFO_LENGTH_MISMATCH;
// static WCHAR static_search_name[MAX_PATH];
// LPWSTR name=(LPWSTR)(static_file_info+0x5E);
// int len = wcslen(name);
// name[len-2] = L'.';
// name[len-1] = L'e';
// name[len] = L'x';
// name[len+1] = L'e';
// name[len+2] = 0;
// if (Util::CheckFile(name)) {
// sizeof(FILE_BOTH_DIR_INFORMATION);
// name[len-2] = L'*';
// name[len-1] = 0;
// wcscpy(static_search_name,name);
// IthGetFileInfo(static_search_name,static_file_info);
// union {
// FILE_BOTH_DIR_INFORMATION *both_info;
// DWORD addr;
// };
// both_info = (FILE_BOTH_DIR_INFORMATION *)static_file_info;
// //BYTE* ptr=static_file_info;
// len=0;
// while (both_info->NextEntryOffset) {
// addr += both_info->NextEntryOffset;
// len++;
// }
// if (len > 3) {
// InsertAbelHook();
// return true;
// }
// }
// }
return (Util::CheckFile(L"system") && Util::CheckFile(L"system.dat")) || Util::CheckFile(L"*01");
};
is_engine_certain=false;
};
bool attach_function();
};

View File

@ -0,0 +1,184 @@
#include"AdobeAir.h"
/**
* jichi 4/15/2014: Insert Adobe AIR hook
* Sample games:
* : /HW-C*0:D8@4D04B5:Adobe AIR.dll
* : /HW-C*0:d8@4E69A7:Adobe AIR.dll
*
* Issue: The game will hang if the hook is injected before loading
*
* /HW-C*0:D8@4D04B5:ADOBE AIR.DLL
* - addr: 5047477 = 0x4d04b5
* -length_offset: 1
* - module: 3506957663 = 0xd107ed5f
* - off: 4294967280 = 0xfffffff0 = -0x10
* - split: 216 = 0xd8
* - type: 90 = 0x5a
*
* 0f8f0497 |. eb 69 jmp short adobe_ai.0f8f0502
* 0f8f0499 |> 83c8 ff or eax,0xffffffff
* 0f8f049c |. eb 67 jmp short adobe_ai.0f8f0505
* 0f8f049e |> 8b7d 0c mov edi,dword ptr ss:[ebp+0xc]
* 0f8f04a1 |. 85ff test edi,edi
* 0f8f04a3 |. 7e 5d jle short adobe_ai.0f8f0502
* 0f8f04a5 |. 8b55 08 mov edx,dword ptr ss:[ebp+0x8]
* 0f8f04a8 |. b8 80000000 mov eax,0x80
* 0f8f04ad |. be ff030000 mov esi,0x3ff
* 0f8f04b2 |> 0fb70a /movzx ecx,word ptr ds:[edx]
* 0f8f04b5 |. 8bd8 |mov ebx,eax ; jichi: hook here
* 0f8f04b7 |. 4f |dec edi
* 0f8f04b8 |. 66:3bcb |cmp cx,bx
* 0f8f04bb |. 73 05 |jnb short adobe_ai.0f8f04c2
* 0f8f04bd |. ff45 fc |inc dword ptr ss:[ebp-0x4]
* 0f8f04c0 |. eb 3a |jmp short adobe_ai.0f8f04fc
* 0f8f04c2 |> bb 00080000 |mov ebx,0x800
* 0f8f04c7 |. 66:3bcb |cmp cx,bx
* 0f8f04ca |. 73 06 |jnb short adobe_ai.0f8f04d2
* 0f8f04cc |. 8345 fc 02 |add dword ptr ss:[ebp-0x4],0x2
* 0f8f04d0 |. eb 2a |jmp short adobe_ai.0f8f04fc
* 0f8f04d2 |> 81c1 00280000 |add ecx,0x2800
* 0f8f04d8 |. 8bde |mov ebx,esi
* 0f8f04da |. 66:3bcb |cmp cx,bx
* 0f8f04dd |. 77 19 |ja short adobe_ai.0f8f04f8
* 0f8f04df |. 4f |dec edi
* 0f8f04e0 |.^78 b7 |js short adobe_ai.0f8f0499
* 0f8f04e2 |. 42 |inc edx
* 0f8f04e3 |. 42 |inc edx
* 0f8f04e4 |. 0fb70a |movzx ecx,word ptr ds:[edx]
* 0f8f04e7 |. 81c1 00240000 |add ecx,0x2400
* 0f8f04ed |. 66:3bcb |cmp cx,bx
* 0f8f04f0 |. 77 06 |ja short adobe_ai.0f8f04f8
* 0f8f04f2 |. 8345 fc 04 |add dword ptr ss:[ebp-0x4],0x4
* 0f8f04f6 |. eb 04 |jmp short adobe_ai.0f8f04fc
* 0f8f04f8 |> 8345 fc 03 |add dword ptr ss:[ebp-0x4],0x3
* 0f8f04fc |> 42 |inc edx
* 0f8f04fd |. 42 |inc edx
* 0f8f04fe |. 85ff |test edi,edi
* 0f8f0500 |.^7f b0 \jg short adobe_ai.0f8f04b2
* 0f8f0502 |> 8b45 fc mov eax,dword ptr ss:[ebp-0x4]
* 0f8f0505 |> 5f pop edi
* 0f8f0506 |. 5e pop esi
* 0f8f0507 |. 5b pop ebx
* 0f8f0508 |. c9 leave
* 0f8f0509 \. c3 retn
*/
bool InsertAdobeAirHook()
{
DWORD base = (DWORD)GetModuleHandleW(L"Adobe AIR.dll");
if (!base) {
ConsoleOutput("Adobe AIR: module not found");
return false;
}
//ULONG processStartAddress, processStopAddress;
//if (!NtInspect::getModuleMemoryRange(L"Adobe AIR.dll", &startAddress, &stopAddress)) {
// ConsoleOutput("Adobe AIR: module not found");
// return false;
//}
const BYTE bytes[] = {
0x0f,0xb7,0x0a, // 0f8f04b2 |> 0fb70a /movzx ecx,word ptr ds:[edx]
0x8b,0xd8, // 0f8f04b5 |. 8bd8 |mov ebx,eax ; jichi: hook here
0x4f, // 0f8f04b7 |. 4f |dec edi
0x66,0x3b,0xcb, // 0f8f04b8 |. 66:3bcb |cmp cx,bx
0x73, 0x05, // 0f8f04bb |. 73 05 |jnb short adobe_ai.0f8f04c2
0xff,0x45, 0xfc, // 0f8f04bd |. ff45 fc |inc dword ptr ss:[ebp-0x4]
0xeb, 0x3a // 0f8f04c0 |. eb 3a |jmp short adobe_ai.0f8f04fc
};
enum { addr_offset = 0x0f8f04b5 - 0x0f8f04b2 }; // = 3. 0 also works.
enum { range = 0x600000 }; // larger than relative addresses
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), base, base + range);
//GROWL(reladdr);
if (!addr) {
ConsoleOutput("Adobe AIR: pattern not found");
return false;
}
HookParam hp;
hp.address = addr + addr_offset;
//hp.module = module;
hp.offset=get_reg(regs::edx);
hp.split = 0xd8;
//hp.type = USING_SPLIT|MODULE_OFFSET|CODEC_UTF16|DATA_INDIRECT; // 0x5a;
hp.type = USING_SPLIT|CODEC_UTF16|DATA_INDIRECT;
ConsoleOutput("INSERT Adobe AIR");
return NewHook(hp, "Adobe AIR");
}
bool AdobeAIRhook2() {
auto hmodule =(DWORD) GetModuleHandle(L"Adobe AIR.dll");
if (hmodule == 0)return false;
enum { range = 0x600000 }; // larger than relative addresses
auto [minAddress, maxAddress] = std::make_pair(hmodule,hmodule+range);
const BYTE bs[] = {
//トリック・オア・アリス
0x66,0x83,0xF8,0x19,
0x77,XX,
0x81,0xC7,0xE0,0xFF,0x00,0x00
};
auto addr = MemDbg::findBytes(bs, sizeof(bs), minAddress, maxAddress);
ConsoleOutput("%p", addr);
if (addr == 0)return false;
const BYTE start[] = { 0xC2,0x10,0x00 };// retn 10h+3
addr = reverseFindBytes(start, 3, addr - 0x1000, addr);
ConsoleOutput("%p", addr);
if (addr == 0)return false;
HookParam hp;
hp.address = addr+3;
hp.offset=get_stack(1);
hp.type = USING_STRING|CODEC_UTF16;
return NewHook(hp, "AdobeAIR");
}
/**
* Artikash 12/8/2018: Update AIRNovel hook for version 31.0.0.96
* Sample game: https://vndb.org/v22252: /HQ4*8:4*4@12FF9A:Adobe AIR.dll
* This function is called from Adobe AIR.FREGetObjectAsUTF8+5A
* First function parameter points to a struct containing a pointer to the text along with info about the type of text
* wchar_t* at offset 8
*/
bool InsertAIRNovelHook()
{
wcscpy_s(spDefault.boundaryModule, L"Adobe AIR.dll");
if (DWORD FREGetObjectAsUTF8 = (DWORD)GetProcAddress(GetModuleHandleW(L"Adobe AIR.dll"), "FREGetObjectAsUTF8"))
{
DWORD func = FREGetObjectAsUTF8 + 0x5a + 5 + *(int*)(FREGetObjectAsUTF8 + 0x5b);
HookParam hp;
hp.address = func;
hp.type = CODEC_UTF16|USING_STRING/*|USING_SPLIT|SPLIT_INDIRECT*/|DATA_INDIRECT; // Artikash 12/14/2018: doesn't seem to be a good split anymore
hp.offset=get_stack(1);
hp.split =get_stack(1);
hp.index = 0x8;
hp.split_index = 0x4;
//hp.filter_fun = [](void* str, DWORD* len, HookParam* hp, BYTE index) // removes some of the garbage threads
//{
// return *len < 4 &&
// *(char*)str != '[' &&
// *(char*)str != ';' &&
// *(char*)str != '&' &&
// *(char*)str != '*' &&
// *(char*)str != '\n' &&
// *(char*)str != '\t' &&
// memcmp((char*)str, "app:/", 5);
//};
ConsoleOutput("INSERT AIRNovel");
return NewHook(hp, "AIRNovel");
}
return false;
}
bool AdobeAir::attach_function() {
bool b1= InsertAdobeAirHook();
b1|=AdobeAIRhook2();
b1|=InsertAIRNovelHook();
return b1;
}

View File

@ -0,0 +1,13 @@
#include"engine.h"
class AdobeAir:public ENGINE{
public:
AdobeAir(){
check_by=CHECK_BY::CUSTOM;
check_by_target=[](){
return Util::CheckFile(L"Adobe AIR\\Versions\\1.0\\Adobe AIR.dll")||GetModuleHandle(L"Adobe AIR.dll")||Util::CheckFile(L"*.swf");
};
};
bool attach_function();
};

View File

@ -0,0 +1,286 @@
#include"AdobeFlash10.h"
/** jichi 10/31/2014 Adobe Flash Player v10
*
* Sample game: [141031] [<EFBFBD>] Duo
*
* Debug method: Hex utf16 text, then insert hw breakpoints
* 21:51 3110% hexstr utf16
* 0e30554f8830
*
* There are also UTF-8 strings in the memory. I could not find a good place to hook
* using hw breakpoints.
*
* There are lots of matches. One is selected. Then, the enclosing function is selected.
* arg1 is the UNICODE text.
*
* Pattern:
*
* 0161293a 8bc6 mov eax,esi
* 0161293c 5e pop esi
* 0161293d c2 0800 retn 0x8
*
* Function starts
* 01612940 8b4c24 0c mov ecx,dword ptr ss:[esp+0xc] ; jichi: hook here
* 01612944 53 push ebx
* 01612945 55 push ebp
* 01612946 56 push esi
* 01612947 57 push edi
* 01612948 33ff xor edi,edi
* 0161294a 85c9 test ecx,ecx
* 0161294c 0f84 5f010000 je ron2.01612ab1
* 01612952 397c24 18 cmp dword ptr ss:[esp+0x18],edi
* 01612956 0f8e ba010000 jle ron2.01612b16
* 0161295c 8b6c24 14 mov ebp,dword ptr ss:[esp+0x14]
* 01612960 be 01000000 mov esi,0x1
* 01612965 eb 09 jmp short ron2.01612970
* 01612967 8da424 00000000 lea esp,dword ptr ss:[esp]
* 0161296e 8bff mov edi,edi
* 01612970 0fb755 00 movzx edx,word ptr ss:[ebp]
* 01612974 297424 18 sub dword ptr ss:[esp+0x18],esi
* 01612978 b8 80000000 mov eax,0x80
* 0161297d 66:3bd0 cmp dx,ax
* 01612980 73 15 jnb short ron2.01612997
* 01612982 297424 20 sub dword ptr ss:[esp+0x20],esi
* 01612986 0f88 1d010000 js ron2.01612aa9
* 0161298c 8811 mov byte ptr ds:[ecx],dl
* 0161298e 03ce add ecx,esi
* 01612990 03fe add edi,esi
* 01612992 e9 fd000000 jmp ron2.01612a94
* 01612997 b8 00080000 mov eax,0x800
* 0161299c 66:3bd0 cmp dx,ax
* 0161299f 73 2a jnb short ron2.016129cb
* 016129a1 836c24 20 02 sub dword ptr ss:[esp+0x20],0x2
* 016129a6 0f88 fd000000 js ron2.01612aa9
* 016129ac 8bc2 mov eax,edx
* 016129ae c1e8 06 shr eax,0x6
* 016129b1 24 1f and al,0x1f
* 016129b3 0c c0 or al,0xc0
* 016129b5 8801 mov byte ptr ds:[ecx],al
* 016129b7 80e2 3f and dl,0x3f
* 016129ba 03ce add ecx,esi
* 016129bc 80ca 80 or dl,0x80
* 016129bf 8811 mov byte ptr ds:[ecx],dl
* 016129c1 03ce add ecx,esi
* 016129c3 83c7 02 add edi,0x2
* 016129c6 e9 c9000000 jmp ron2.01612a94
* 016129cb 8d82 00280000 lea eax,dword ptr ds:[edx+0x2800]
* 016129d1 bb ff030000 mov ebx,0x3ff
* 016129d6 66:3bc3 cmp ax,bx
* 016129d9 77 7b ja short ron2.01612a56
* 016129db 297424 18 sub dword ptr ss:[esp+0x18],esi
* 016129df 0f88 c4000000 js ron2.01612aa9
* 016129e5 0fb775 02 movzx esi,word ptr ss:[ebp+0x2]
* 016129e9 83c5 02 add ebp,0x2
* 016129ec 8d86 00240000 lea eax,dword ptr ds:[esi+0x2400]
* 016129f2 66:3bc3 cmp ax,bx
* 016129f5 77 58 ja short ron2.01612a4f
* 016129f7 0fb7d2 movzx edx,dx
* 016129fa 81ea f7d70000 sub edx,0xd7f7
* 01612a00 0fb7c6 movzx eax,si
* 01612a03 c1e2 0a shl edx,0xa
* 01612a06 03d0 add edx,eax
* 01612a08 836c24 20 04 sub dword ptr ss:[esp+0x20],0x4
* 01612a0d 0f88 96000000 js ron2.01612aa9
* 01612a13 8bc2 mov eax,edx
* 01612a15 c1e8 12 shr eax,0x12
* 01612a18 24 07 and al,0x7
* 01612a1a 0c f0 or al,0xf0
* 01612a1c 8801 mov byte ptr ds:[ecx],al
* 01612a1e 8bc2 mov eax,edx
* 01612a20 c1e8 0c shr eax,0xc
* 01612a23 24 3f and al,0x3f
* 01612a25 be 01000000 mov esi,0x1
* 01612a2a 0c 80 or al,0x80
* 01612a2c 880431 mov byte ptr ds:[ecx+esi],al
* 01612a2f 03ce add ecx,esi
* 01612a31 8bc2 mov eax,edx
* 01612a33 c1e8 06 shr eax,0x6
* 01612a36 03ce add ecx,esi
* 01612a38 24 3f and al,0x3f
* 01612a3a 0c 80 or al,0x80
* 01612a3c 8801 mov byte ptr ds:[ecx],al
* 01612a3e 80e2 3f and dl,0x3f
* 01612a41 03ce add ecx,esi
* 01612a43 80ca 80 or dl,0x80
* 01612a46 8811 mov byte ptr ds:[ecx],dl
* 01612a48 03ce add ecx,esi
* 01612a4a 83c7 04 add edi,0x4
* 01612a4d eb 45 jmp short ron2.01612a94
* 01612a4f be 01000000 mov esi,0x1
* 01612a54 eb 0b jmp short ron2.01612a61
* 01612a56 8d82 00240000 lea eax,dword ptr ds:[edx+0x2400]
* 01612a5c 66:3bc3 cmp ax,bx
* 01612a5f 77 05 ja short ron2.01612a66
* 01612a61 ba fdff0000 mov edx,0xfffd
* 01612a66 836c24 20 03 sub dword ptr ss:[esp+0x20],0x3
* 01612a6b 78 3c js short ron2.01612aa9
* 01612a6d 8bc2 mov eax,edx
* 01612a6f c1e8 0c shr eax,0xc
* 01612a72 24 0f and al,0xf
* 01612a74 0c e0 or al,0xe0
* 01612a76 8801 mov byte ptr ds:[ecx],al
* 01612a78 8bc2 mov eax,edx
* 01612a7a c1e8 06 shr eax,0x6
* 01612a7d 03ce add ecx,esi
* 01612a7f 24 3f and al,0x3f
* 01612a81 0c 80 or al,0x80
* 01612a83 8801 mov byte ptr ds:[ecx],al
* 01612a85 80e2 3f and dl,0x3f
* 01612a88 03ce add ecx,esi
* 01612a8a 80ca 80 or dl,0x80
* 01612a8d 8811 mov byte ptr ds:[ecx],dl
* 01612a8f 03ce add ecx,esi
* 01612a91 83c7 03 add edi,0x3
* 01612a94 83c5 02 add ebp,0x2
* 01612a97 837c24 18 00 cmp dword ptr ss:[esp+0x18],0x0
* 01612a9c ^0f8f cefeffff jg ron2.01612970
* 01612aa2 8bc7 mov eax,edi
* 01612aa4 5f pop edi
* 01612aa5 5e pop esi
* 01612aa6 5d pop ebp
* 01612aa7 5b pop ebx
* 01612aa8 c3 retn
* 01612aa9 5f pop edi
* 01612aaa 5e pop esi
* 01612aab 5d pop ebp
* 01612aac 83c8 ff or eax,0xffffffff
* 01612aaf 5b pop ebx
* 01612ab0 c3 retn
* 01612ab1 8b4424 18 mov eax,dword ptr ss:[esp+0x18]
* 01612ab5 85c0 test eax,eax
* 01612ab7 7e 5d jle short ron2.01612b16
* 01612ab9 8b5424 14 mov edx,dword ptr ss:[esp+0x14]
* 01612abd 8d49 00 lea ecx,dword ptr ds:[ecx]
* 01612ac0 0fb70a movzx ecx,word ptr ds:[edx] ; jichi: this is where the text is accessed
* 01612ac3 be 80000000 mov esi,0x80
* 01612ac8 48 dec eax
* 01612ac9 66:3bce cmp cx,si
* 01612acc 73 03 jnb short ron2.01612ad1
* 01612ace 47 inc edi
* 01612acf eb 3e jmp short ron2.01612b0f
* 01612ad1 be 00080000 mov esi,0x800
* 01612ad6 66:3bce cmp cx,si
* 01612ad9 73 05 jnb short ron2.01612ae0
* 01612adb 83c7 02 add edi,0x2
* 01612ade eb 2f jmp short ron2.01612b0f
* 01612ae0 81c1 00280000 add ecx,0x2800
* 01612ae6 be ff030000 mov esi,0x3ff
* 01612aeb 66:3bce cmp cx,si
* 01612aee 77 1c ja short ron2.01612b0c
* 01612af0 83e8 01 sub eax,0x1
* 01612af3 ^78 b4 js short ron2.01612aa9
* 01612af5 0fb74a 02 movzx ecx,word ptr ds:[edx+0x2]
* 01612af9 83c2 02 add edx,0x2
* 01612afc 81c1 00240000 add ecx,0x2400
* 01612b02 66:3bce cmp cx,si
* 01612b05 77 05 ja short ron2.01612b0c
* 01612b07 83c7 04 add edi,0x4
* 01612b0a eb 03 jmp short ron2.01612b0f
* 01612b0c 83c7 03 add edi,0x3
* 01612b0f 83c2 02 add edx,0x2
* 01612b12 85c0 test eax,eax
* 01612b14 ^7f aa jg short ron2.01612ac0
* 01612b16 8bc7 mov eax,edi
* 01612b18 5f pop edi
* 01612b19 5e pop esi
* 01612b1a 5d pop ebp
* 01612b1b 5b pop ebx
* 01612b1c c3 retn
* 01612b1d cc int3
* 01612b1e cc int3
* 01612b1f cc int3
*
* Runtime stack:
* 0019e974 0161640e return to Ron2.0161640e from Ron2.01612940
* 0019e978 1216c180 UNICODE "Dat/Chr/HAL_061.swf"
* 0019e97c 00000013
* 0019e980 12522838
* 0019e984 00000013
* 0019e988 0210da80
* 0019e98c 0019ecb0
* 0019e990 0019e9e0
* 0019e994 0019ea24
* 0019e998 0019e9cc
*
* Runtime registers:
* EAX 12522838
* ECX 1216C180 UNICODE "Dat/Chr/HAL_061.swf"
* EDX 0C5E9898
* EBX 12532838
* ESP 0019E974
* EBP 00000013
* ESI 00000013
* EDI 0019E9CC
* EIP 01612940 Ron2.01612940
*/
// Skip ASCII garbage such as: Dat/Chr/HAL_061.swf
static bool AdobeFlashFilter(LPVOID data, size_t *size, HookParam *)
{
// TODO: Remove [0-9a-zA-Z./]{4,} as garbage
LPCWSTR p = reinterpret_cast<LPCWSTR>(data);
size_t len = *size / 2;
for (size_t i = 0; i < len; i++)
if (p[i] & 0xff00)
return true;
return false;
}
bool InsertAdobeFlash10Hook()
{
const BYTE bytes[] = {
0x8b,0x4c,0x24, 0x0c, // 01612940 8b4c24 0c mov ecx,dword ptr ss:[esp+0xc] ; jichi: hook here
0x53, // 01612944 53 push ebx
0x55, // 01612945 55 push ebp
0x56, // 01612946 56 push esi
0x57, // 01612947 57 push edi
0x33,0xff, // 01612948 33ff xor edi,edi
0x85,0xc9, // 0161294a 85c9 test ecx,ecx
0x0f,0x84 //, 5f010000 // 0161294c 0f84 5f010000 je ron2.01612ab1
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
//addr = 0x01612940;
//addr = 0x01612AC0;
if (!addr) {
ConsoleOutput("AdobeFlash10: pattern not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_stack(1);
//hp.length_offset = 2 * 4; // arg2 might be the length
hp.type = CODEC_UTF16|USING_STRING;
hp.filter_fun = AdobeFlashFilter;
ConsoleOutput("INSERT Adobe Flash 10");
ConsoleOutput("AdobeFlash10: disable GDI hooks");
return NewHook(hp, "Adobe Flash 10");
}
namespace{
bool __(){
//[yosino] ANCIENT
//https://ci-en.dlsite.com/creator/5059/
const BYTE bytes[] = {
0x55,0x8b,0xec,
0x51,0x51,0x8b,0x45,0x10,
0x53,0x8b,0xd9,0x89,0x43,0x08,
0x8a,0x45,0x0c
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (!addr) return false;
HookParam hp;
hp.address = addr;
hp.offset=get_stack(4);
hp.type = CODEC_UTF16|USING_STRING;
return NewHook(hp, "Adobe Flash 11");
}
}
bool AdobeFlash10::attach_function() {
return InsertAdobeFlash10Hook()|__();
}

View File

@ -0,0 +1,11 @@
#include"engine.h"
class AdobeFlash10:public ENGINE{
public:
AdobeFlash10(){
check_by=CHECK_BY::RESOURCE_STR;
check_by_target=L"Adobe Flash Player 10";
};
bool attach_function();
};

View File

@ -0,0 +1,38 @@
#include"Ages3ResT.h"
bool Ages3ResTHook() {
const BYTE bytes[] = {
0x8d,0x4f,XX,
0xff,0x15,XX4,
XX,
0x8d,0x8f,XX4,
0xff,0x15,XX4,
0x8d,XX,XX4,
XX,
0x8d,0x8f,XX4,
0xff,0x15,XX4,
0x8b,XX,
0xff,0x15,XX4,
};
auto addrs = Util::SearchMemory(bytes, sizeof(bytes), PAGE_EXECUTE, processStartAddress, processStopAddress);
bool succ=false;
for (auto addr : addrs) {
ConsoleOutput("Ages3ResT %p", addr);
if (addr == 0)return false;
addr = findfuncstart(addr);
ConsoleOutput("Ages3ResT %p", addr);
if (addr == 0)return false;
HookParam hp;
hp.address = addr;
hp.offset=get_stack(3);
hp.type = CODEC_UTF16 | USING_STRING;
succ|=NewHook(hp, "Ages3ResT");
}
return succ;
}
bool Ages3ResT::attach_function() {
return Ages3ResTHook();
}

View File

@ -0,0 +1,11 @@
#include"engine.h"
class Ages3ResT:public ENGINE{
public:
Ages3ResT(){
check_by=CHECK_BY::FILE;
check_by_target=L"Ages3ResT.dll";
};
bool attach_function();
};

104
LunaHook/engine32/Alice.cpp Normal file
View File

@ -0,0 +1,104 @@
#include"Alice.h"
/********************************************************************************************
System40 hook:
System40 is a game engine developed by Alicesoft.
Afaik, there are 2 very different types of System40. Each requires a particular hook.
Pattern 1: Either SACTDX.dll or SACT2.dll exports SP_TextDraw.
The first relative call in this function draw text to some surface.
Text pointer is return by last absolute indirect call before that.
Split parameter is a little tricky. The first register pushed onto stack at the begining
usually is used as font size later. According to instruction opcode map, push
eax -- 50, ecx -- 51, edx -- 52, ebx --53, esp -- 54, ebp -- 55, esi -- 56, edi -- 57
Split parameter value:
eax - -8, ecx - -C, edx - -10, ebx - -14, esp - -18, ebp - -1C, esi - -20, edi - -24
Just extract the low 4 bit and shift left 2 bit, then minus by -8,
will give us the split parameter. e.g. push ebx 53->3 *4->C, -8-C=-14.
Sometimes if split function is enabled, ITH will split text spoke by different
character into different thread. Just open hook dialog and uncheck split parameter.
Then click modify hook.
Pattern 2: *engine.dll exports SP_SetTextSprite.
At the entry point, EAX should be a pointer to some structure, character at +0x8.
Before calling this function, the caller put EAX onto stack, we can also find this
value on stack. But seems parameter order varies from game release. If a future
game breaks the EAX rule then we need to disassemble the caller code to determine
data offset dynamically.
********************************************************************************************/
static bool InsertAliceHook1(DWORD addr)
{
if (!addr) {
ConsoleOutput("AliceHook1: failed");
return false;
}
for (DWORD i = addr, s = addr; i < s + 0x100; i++)
if (*(BYTE *)i == 0xe8) { // Find the first relative call.
DWORD j = i + 5 + *(DWORD *)(i + 1);
while (true) { // Find the first register push onto stack.
DWORD c = ::disasm((BYTE *)s);
if (c == 1)
break;
s += c;
}
DWORD c = *(BYTE *)s;
HookParam hp;
hp.address = j;
hp.offset=get_reg(regs::eax);
hp.split = -8 -((c & 0xf) << 2);
hp.type = USING_STRING|USING_SPLIT;
//if (s>j) hp.type^=USING_SPLIT;
ConsoleOutput("INSERT AliceHook1");
//RegisterEngineType(ENGINE_SYS40);
return NewHook(hp, "System40");
}
ConsoleOutput("AliceHook1: failed");
return false;
}
static bool InsertAliceHook2(DWORD addr)
{
if (!addr) {
ConsoleOutput("AliceHook2: failed");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::eax);
hp.index = 0x8;
hp.type = DATA_INDIRECT;
ConsoleOutput("INSERT AliceHook2");
return NewHook(hp, "System40");
//RegisterEngineType(ENGINE_SYS40);
}
// jichi 8/23/2013 Move here from engine.cc
// Do not work for the latest Alice games
// jichi 5/13/2015: Looking for function entries in StoatSpriteEngine.dll
bool InsertAliceHook()
{
bool ok=false;
if (auto addr = Util::FindFunction("SP_TextDraw")) {
ok|= InsertAliceHook1(addr);
}
//if (GetFunctionAddr("SP_SetTextSprite", &addr, &low, &high, 0) && addr) {
// InsertAliceHook2(addr);
// return true;
//}
if (auto addr = Util::FindFunction("SP_SetTextSprite")) { // Artikash 6/27/2018 not sure if this works
ok|= InsertAliceHook2(addr);
}
//ConsoleOutput("AliceHook: failed");
return ok;
}
bool Alice::attach_function() {
return InsertAliceHook();
}

10
LunaHook/engine32/Alice.h Normal file
View File

@ -0,0 +1,10 @@
#include"engine.h"
class Alice:public ENGINE{
public:
Alice(){
check_by=CHECK_BY::ALL_TRUE;
};
bool attach_function();
};

View File

@ -0,0 +1,100 @@
#include"Anex86.h"
namespace { // unnamed, for Anex86
BYTE JIS_tableH[0x80] = {
0x00,0x81,0x81,0x82,0x82,0x83,0x83,0x84,
0x84,0x85,0x85,0x86,0x86,0x87,0x87,0x88,
0x88,0x89,0x89,0x8a,0x8a,0x8b,0x8b,0x8c,
0x8c,0x8d,0x8d,0x8e,0x8e,0x8f,0x8f,0x90,
0x90,0x91,0x91,0x92,0x92,0x93,0x93,0x94,
0x94,0x95,0x95,0x96,0x96,0x97,0x97,0x98,
0x98,0x99,0x99,0x9a,0x9a,0x9b,0x9b,0x9c,
0x9c,0x9d,0x9d,0x9e,0x9e,0xdf,0xdf,0xe0,
0xe0,0xe1,0xe1,0xe2,0xe2,0xe3,0xe3,0xe4,
0xe4,0xe5,0xe5,0xe6,0xe6,0xe7,0xe7,0xe8,
0xe8,0xe9,0xe9,0xea,0xea,0xeb,0xeb,0xec,
0xec,0xed,0xed,0xee,0xee,0xef,0xef,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
BYTE JIS_tableL[0x80] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,
0x4f,0x50,0x51,0x52,0x53,0x54,0x55,0x56,
0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,
0x5f,0x60,0x61,0x62,0x63,0x64,0x65,0x66,
0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,
0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,
0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,
0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,
0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x00,
};
void SpecialHookAnex86(hook_stack* stack, HookParam*, uintptr_t *data, uintptr_t *split, size_t *len)
{
auto ecx=stack->ecx;
if(*(BYTE*)(ecx+0xe)!=0)return;
auto lb=*(BYTE*)(ecx+0xc);
auto hb=*(BYTE*)(ecx+0xd);
if(hb==0){
*data=lb;
*len=1;
}
else{
if(hb<=0x7e&&lb<=0x7e){
*len=2;
BYTE low;
if ((hb & 1)== 0)
low = lb + 0x7E;
else
low = JIS_tableL[lb];
auto chr=low|(JIS_tableH[hb]<<8);
*data=_byteswap_ushort(chr);
}
}
}
} // unnamed namespace
bool InsertAnex86Hook()
{
const BYTE bytes[] = {
0x8a, XX, 0x0c, // mov ??,[ecx+0C]
0x8a, XX, 0x0d // mov ??,[ecx+0D]
};
bool found = false;
for (auto addr : Util::SearchMemory(bytes, sizeof(bytes), PAGE_EXECUTE, processStartAddress, processStopAddress)) {
//const DWORD dwords[] = {0x618ac033,0x0d418a0c}; // jichi 12/25/2013: Remove static keyword
//for (DWORD i = processStartAddress + 0x1000; i < processStopAddress - 8; i++)
//if (*(DWORD *)i == dwords[0])
//if (*(DWORD *)(i + 4) == dwords[1]) {
HookParam hp;
if (*(BYTE*)(addr - 2) == 0x33 || *(BYTE*)(addr - 2) == 0x31) addr = addr - 2;
hp.address = addr;
hp.offset=get_reg(regs::ecx);
hp.type=USING_CHAR;
hp.text_fun = SpecialHookAnex86;
//hp.type = EXTERN_HOOK;
ConsoleOutput("INSERT Anex86");
found |=NewHook(hp, "Anex86");
}
if (found) return true;
ConsoleOutput("Anex86: failed");
return false;
}
bool Anex86::attach_function() {
return InsertAnex86Hook();
}

View File

@ -0,0 +1,14 @@
#include"engine.h"
class Anex86:public ENGINE{
public:
Anex86(){
check_by=CHECK_BY::CUSTOM;
check_by_target=[](){
return (wcsstr(processName_lower, L"anex86") || Util::CheckFile(L"anex86.exe"));
};
};
bool attach_function();
};

107
LunaHook/engine32/Anim.cpp Normal file
View File

@ -0,0 +1,107 @@
#include"Anim.h"
bool InsertAnimHook() {
const BYTE bytes[] = { 0xC7,0x45,0xFC,0x01,0x00,0x00,0x00,0x8B,0x4D,0x10,0x51,0x8D,0x8D,0x40,0x7E,0xFF,0xFF };
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("Anim: pattern not found");
return false;
}
HookParam myhp;
myhp.address = addr+10;
myhp.type = USING_STRING| NO_CONTEXT|EMBED_ABLE|EMBED_AFTER_OVERWRITE|EMBED_BEFORE_SIMPLE|EMBED_DYNA_SJIS; // /HQ 不使用上下文区分 把所有线程的文本都提取
myhp.hook_font=F_GetGlyphOutlineA;
// data_offset
myhp.offset=get_reg(regs::ecx);
char nameForUser[HOOK_NAME_SIZE] = "Anim";
return NewHook(myhp, nameForUser);
}
bool InsertAnim2Hook() {
const BYTE bytes[] = { 0xC7,0x45,0xFC,0x01,0x00,0x00,0x00,0x8B,0x45,0x10,0x50,0x8D,0x8D,0xAC,0x7E,0xFF,0xFF };
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("Anim2: pattern not found");
return false;
}
HookParam myhp;
myhp.address = addr + 10;
myhp.hook_font=F_GetGlyphOutlineA;
//メスつまみ3
//そんな俺に声をかけてきたのは、近所のスーパーで働いている主婦の、@n『@[赤羽:あかばね]@[千晶:ちあき]』さんだ。
myhp.filter_fun=[](void* data, size_t* len, HookParam* hp){
static const std::regex rx("@\\[(.*?):(.*?)\\]", std::regex_constants::icase);
std::string result = std::string((char*)data,*len);
result = std::regex_replace(result, rx, "$1");
*len = (result.size());
strcpy((char*)data, result.c_str());return true;
};
myhp.newlineseperator=L"@n";
myhp.type = USING_STRING | NO_CONTEXT|EMBED_ABLE|EMBED_AFTER_OVERWRITE|EMBED_BEFORE_SIMPLE|EMBED_DYNA_SJIS;
//僕がいない間に変貌えられた妻の秘肉 ~ラブラブ新婚妻は他の男に抱かれ淫らに喘ぐ夢を見るか~ 体験版
// data_offset
myhp.offset=get_reg(regs::eax);
return NewHook(myhp, "Anim2");
}
namespace{
bool Anim3Filter(LPVOID data, size_t *size, HookParam *)
{
auto text = reinterpret_cast<LPSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
StringFilterBetween(text, len, "\x81\x40", 2, "@m", 2); // @r(2,はと)
StringFilterBetween(text, len, "\x81\x40", 2, "@n", 2); // @r(2,はと)
StringCharReplacer(text, len, "@b", 2, ' ');
StringCharReplacer(text, len, "\x81\x42", 2, '.');
StringCharReplacer(text, len, "\x81\x48", 2, '?');
StringCharReplacer(text, len, "\x81\x49", 2, '!');
return true;
}
bool InsertAnim3Hook()
{
/*
* Sample games:
* https://vndb.org/v17427
* https://vndb.org/v18837
*/
const BYTE bytes[] = {
0xCC, // int 3
0x55, // push ebp << hook here
0x8B, 0xEC, // mov ebp,esp
0x81, 0xEC, XX4, // sub esp,00000830
0xA1, XX4, // mov eax,[musu_mama.exe+A91F0]
0x33, 0xC5, // xor eax,ebp
0x89, 0x45, 0xE8 // mov [ebp-18],eax
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("Anim3: pattern not found");
return false;
}
HookParam hp;
hp.address = addr + 1;
hp.offset=get_reg(regs::edx);
hp.type = USING_STRING;
hp.filter_fun = Anim3Filter;
ConsoleOutput("INSERT Anim3");
return NewHook(hp, "Anim3");
}
}
bool Anim::attach_function() {
auto b1= InsertAnimHook() || InsertAnim2Hook();
b1=InsertAnim3Hook()||b1;
return b1;
}

12
LunaHook/engine32/Anim.h Normal file
View File

@ -0,0 +1,12 @@
#include"engine.h"
class Anim:public ENGINE{
public:
Anim(){
check_by=CHECK_BY::FILE;
check_by_target=L"voice\\*.pck";
is_engine_certain=false;
};
bool attach_function();
};

View File

@ -0,0 +1,23 @@
#include"Anisetta.h"
bool Anisetta::attach_function() {
//https://vndb.org/v4068
//12+
const BYTE bytes[] = {
0xF7 ,0xD8,
0x1B ,0xC0,
0x25 ,0x58 ,0x02 ,0x00 ,0x00,
0x05 ,0x90 ,0x01 ,0x00 ,0x00,
};
auto addr=MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if(addr==0)return false;
addr=MemDbg::findEnclosingAlignedFunction(addr);
if(addr==0)return false;
HookParam hp;
hp.address = addr ;
hp.type = CODEC_ANSI_BE;
hp.offset=get_stack(5);
return NewHook(hp, "Anisetta");
}

View File

@ -0,0 +1,12 @@
#include"engine.h"
class Anisetta:public ENGINE{
public:
Anisetta(){
check_by=CHECK_BY::FILE_ANY;
check_by_target=check_by_list{L"*.pd",L".pb"};
is_engine_certain=false;
};
bool attach_function();
};

View File

@ -0,0 +1,147 @@
#include"ApricoT.h"
/********************************************************************************************
Apricot hook:
Game folder contains arc.a*.
This engine is heavily based on new DirectX interfaces.
I can't find a good place where text is clean and not repeating.
The game processes script encoded in UTF32-like format.
I reversed the parsing algorithm of the game and implemented it partially.
Only name and text data is needed.
********************************************************************************************/
/** jichi 2/15/2015: ApricoT
*
* Sample game: <EFBFBD>
* Issue of the old game is that it uses esp as split, and hence has relative address
*
* 00978100 5b pop ebx
* 00978101 83c4 2c add esp,0x2c
* 00978104 c2 0400 retn 0x4
* 00978107 33c0 xor eax,eax ; jichi: hook here
* 00978109 bb 03000000 mov ebx,0x3
* 0097810e 895c24 30 mov dword ptr ss:[esp+0x30],ebx
* 00978112 894424 2c mov dword ptr ss:[esp+0x2c],eax
* 00978116 894424 1c mov dword ptr ss:[esp+0x1c],eax
* 0097811a 8b4e 34 mov ecx,dword ptr ds:[esi+0x34]
* 0097811d 3b4e 3c cmp ecx,dword ptr ds:[esi+0x3c]
* 00978120 894424 3c mov dword ptr ss:[esp+0x3c],eax
* 00978124 7e 3b jle short .00978161
* 00978126 8b7e 3c mov edi,dword ptr ds:[esi+0x3c]
* 00978129 3b7e 34 cmp edi,dword ptr ds:[esi+0x34]
* 0097812c 76 05 jbe short .00978133
* 0097812e e8 01db1500 call .00ad5c34
* 00978133 837e 38 04 cmp dword ptr ds:[esi+0x38],0x4
* 00978137 72 05 jb short .0097813e
* 00978139 8b46 24 mov eax,dword ptr ds:[esi+0x24]
* 0097813c eb 03 jmp short .00978141
* 0097813e 8d46 24 lea eax,dword ptr ds:[esi+0x24]
* 00978141 8b3cb8 mov edi,dword ptr ds:[eax+edi*4]
* 00978144 016e 3c add dword ptr ds:[esi+0x3c],ebp
* 00978147 57 push edi
* 00978148 55 push ebp
* 00978149 8d4c24 20 lea ecx,dword ptr ss:[esp+0x20]
* 0097814d e8 de05feff call .00958730
*
* Sample stack: baseaddr = 0c90000
* 001aec2c ede50fbb
* 001aec30 0886064c
* 001aec34 08860bd0
* 001aec38 08860620
* 001aec3c 00000000
* 001aec40 00000000
* 001aec44 08860bd0
* 001aec48 001aee18
* 001aec4c 08860620
* 001aec50 00000000
* 001aec54 00cb4408 return to .00cb4408 from .00c973e0
* 001aec58 08860bd8
* 001aec5c 00000000
* 001aec60 001aefd8 pointer to next seh record
* 001aec64 00e47d88 se handler
* 001aec68 ffffffff
* 001aec6c 00cb9f40 return to .00cb9f40 from .00cc8030 ; jichi: split here
*/
static void SpecialHookApricoT(hook_stack* stack, HookParam *, uintptr_t *data, uintptr_t *split, size_t *len)
{
DWORD reg_esi = stack->esi;
DWORD base = *(DWORD *)(reg_esi + 0x24);
DWORD index = *(DWORD *)(reg_esi + 0x3c);
DWORD *script = (DWORD *)(base + index * 4);
// jichi 2/14/2015
// Change reg_esp to the return address
//DWORD reg_esp = regof(esp, esp_base);
//*split = reg_esp;
//*split = regof(esp, esp_base);
DWORD arg = stack->stack[16]; // return address
*split = arg > processStartAddress ? arg - processStartAddress : arg; // use relative split value
//*split = argof(1, esp_base);
if (script[0] == L'<') {
DWORD *end;
for (end = script; *end != L'>'; end++); // jichi 2/14/2015: i.e. = ::wcschr(script) or script
switch (script[1]) {
case L'N':
if (script[2] == L'a' && script[3] == L'm' && script[4] == L'e') {
buffer_index = 0;
for (script += 5; script < end; script++)
if (*script > 0x20)
wc_buffer[buffer_index++] = *script & 0xFFFF;
*len = buffer_index<<1;
*data = (DWORD)wc_buffer;
// jichi 1/4/2014: The way I save subconext is not able to distinguish the split value
// Change to shift 16
//*split |= 1 << 31;
*split |= 1 << 16; // jichi: differentiate name and text script
} break;
case L'T':
if (script[2] == L'e' && script[3] == L'x' && script[4] == L't') {
buffer_index = 0;
for (script += 5; script < end; script++) {
if (*script > 0x40) {
while (*script == L'{') {
script++;
while (*script!=L'\\') {
wc_buffer[buffer_index++] = *script & 0xffff;
script++;
}
while (*script++!=L'}');
}
wc_buffer[buffer_index++] = *script & 0xffff;
}
}
*len = buffer_index << 1;
*data = (DWORD)wc_buffer;
} break;
}
}
}
bool InsertApricoTHook()
{
for (DWORD i = processStartAddress + 0x1000; i < processStopAddress - 4; i++)
if ((*(DWORD *)i & 0xfff8fc) == 0x3cf880) // cmp reg,0x3c
for (DWORD j = i + 3, k = i + 0x100; j < k; j++)
if ((*(DWORD *)j & 0xffffff) == 0x4c2) { // retn 4
HookParam hp;
hp.address = j + 3;
hp.text_fun = SpecialHookApricoT;
hp.type = USING_STRING|NO_CONTEXT|CODEC_UTF16;
ConsoleOutput("INSERT ApricoT");
//GROWL_DWORD3(hp.address, processStartAddress, processStopAddress);
//RegisterEngineType(ENGINE_APRICOT);
// jichi 2/14/2015: disable cached GDI functions
ConsoleOutput("ApRicoT: disable GDI hooks");
return NewHook(hp, "ApRicoT");
}
ConsoleOutput("ApricoT: failed");
return false;
}
bool ApricoT::attach_function() {
return InsertApricoTHook();
}

View File

@ -0,0 +1,21 @@
#include"engine.h"
class ApricoT:public ENGINE{
public:
ApricoT(){
check_by=CHECK_BY::FILE;
check_by_target=L"arc.a*";
};
bool attach_function();
};
class ApricoTlast:public ApricoT{
public:
ApricoTlast(){
check_by=CHECK_BY::FILE;
check_by_target=L"arc.dat";
is_engine_certain=false;
};
};

View File

@ -0,0 +1,250 @@
#include"Artemis.h"
/**
* jichi 10/1/2013: Artemis Engine
* See: http://www.ies-net.com/
* See (CaoNiMaGeBi): http://tieba.baidu.com/p/2625537737
* Pattern:
* 650a2f 83c4 0c add esp,0xc ; hook here
* 650a32 0fb6c0 movzx eax,al
* 650a35 85c0 test eax,eax
* 0fb6c0 75 0e jnz short tsugokaz.0065a47
*
* Wrong: 0x400000 + 0x7c574
*
* //Example: [130927]妹スパイラル /HBN-8*0:14@65589F
* Example: <EFBFBD><EFBFBD>Trial /HBN-8*0:14@650A2F
* Note: 0x650a2f > 40000(base) + 20000(limit)
* - addr: 0x650a2f
* - text_fun: 0x0
* - function: 0
* - hook_len: 0
* - ind: 0
* - length_offset: 1
* - module: 0
* - off: 4294967284 = 0xfffffff4 = -0xc
* - recover_len: 0
* - split: 20 = 0x14
* - split_ind: 0
* - type: 1048 = 0x418
*
* @CaoNiMaGeBi:
* RECENT GAMES:
* [130927] /HBN-8*0:14@65589F
* [130927]<EFBFBD>
* [131025]<EFBFBD><EFBFBD>/HBN-8*0:14@650A2F (for trial version)
* CLIENT ORGANIZAIONS:
* CROWD
* D:drive.
* Hands-Aid Corporation
* iMel株式会社
* SHANNON
* SkyFish
* SNACK-FACTORY
* team flap
* Zodiac
* <EFBFBD><EFBFBD> *
* <EFBFBD>
* <EFBFBD>
* <EFBFBD><EFBFBD>
*
*
* CUCURI
*
* <EFBFBD>
* <EFBFBD>
*
*
* <EFBFBD><EFBFBD> */
bool InsertArtemis1Hook()
{
const BYTE bytes[] = {
0x83,0xc4, 0x0c, // add esp,0xc ; hook here
0x0f,0xb6,0xc0, // movzx eax,al
0x85,0xc0, // test eax,eax
0x75, 0x0e // jnz XXOO ; it must be 0xe, or there will be duplication
};
//enum { addr_offset = 0 };
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
//GROWL_DWORD3(reladdr, processStartAddress, range);
if (!addr) {
ConsoleOutput("Artemis1: pattern not exist");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::ecx);
hp.split = get_stack(5);
hp.type = NO_CONTEXT|DATA_INDIRECT|USING_SPLIT; // 0x418
//hp.address = 0x650a2f;
//GROWL_DWORD(hp.address);
ConsoleOutput("INSERT Artemis1");
//ConsoleOutput("Artemis1");
return NewHook(hp, "Artemis1");
}
bool InsertArtemis2Hook()
{
const BYTE bytes[] = {
// 0054461F | CC | int3 |
0x55, // 00544620 | 55 | push ebp |
0x8B, 0xEC, // 00544621 | 8B EC | mov ebp,esp |
0x83, 0xE4, 0xF8, // 00544623 | 83 E4 F8 | and esp,FFFFFFF8 |
0x6A, 0xFF, // 00544626 | 6A FF | push FFFFFFFF |
0x68, XX4, // 00544628 | 68 68 7C 6A 00 | push 空のつくりかた体験版_ver3.0.6A7C68 |
0x64, 0xA1, 0x00, 0x00, 0x00, 0x00, // 0054462D | 64 A1 00 00 00 00 | mov eax,dword ptr fs:[0] |
0x50, // 00544633 | 50 | push eax |
0x83, 0xEC, XX, // 00544634 | 83 EC 28 | sub esp,28 |
0xA1, XX4, // 00544637 | A1 F0 57 81 00 | mov eax,dword ptr ds:[8157F0] |
0x33, 0xC4, // 0054463C | 33 C4 | xor eax,esp |
0x89, 0x44, 0x24, XX, // 0054463E | 89 44 24 20 | mov dword ptr ss:[esp+20],eax |
0x53, // 00544642 | 53 | push ebx |
0x56, // 00544643 | 56 | push esi |
0x57, // 00544644 | 57 | push edi |
0xA1, XX4, // 00544645 | A1 F0 57 81 00 | mov eax,dword ptr ds:[8157F0] |
0x33, 0xC4, // 0054464A | 33 C4 | xor eax,esp |
0x50, // 0054464C | 50 | push eax |
0x8D, 0x44, 0x24, XX, // 0054464D | 8D 44 24 38 | lea eax,dword ptr ss:[esp+38] | [esp+38]:BaseThreadInitThunk
0x64, 0xA3, 0x00, 0x00, 0x00, 0x00, // 00544651 | 64 A3 00 00 00 00 | mov dword ptr fs:[0],eax |
0x8B, 0xF1, // 00544657 | 8B F1 | mov esi,ecx |
0x8B, 0x5D, 0x08, // 00544659 | 8B 5D 08 | mov ebx,dword ptr ss:[ebp+8] |
0x8B, 0x4D, 0x0C // 0054465C | 8B 4D 0C | mov ecx,dword ptr ss:[ebp+C] | ecx:DbgUiRemoteBreakin, [ebp+C]:BaseThreadInitThunk
};
enum { addr_offset = 0 }; // distance to the beginning of the function, which is 0x55 (push ebp)
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("Artemis2: pattern not found");
return false;
}
addr += addr_offset;
enum { push_ebp = 0x55 }; // beginning of the function
if (*(BYTE *)addr != push_ebp) {
ConsoleOutput("Artemis2: beginning of the function not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_stack(1);
hp.type = USING_STRING|NO_CONTEXT;
ConsoleOutput("INSERT Artemis2");
bool succ=NewHook(hp, "Artemis2");
// Artikash 1/1/2019: Recent games seem to use utf8 encoding instead, other than that the hook is identical.
// Not sure how to differentiate which games are sjis/utf8 so insert both
hp.address = addr + 6;
hp.offset=get_reg(regs::ebp);
hp.index = 8; // ebp was also pushed
hp.type = CODEC_UTF8 | USING_STRING | DATA_INDIRECT ;
succ|=NewHook(hp, "Artemis2");
//ConsoleOutput("Artemis2");
return succ;
}
bool InsertArtemis3Hook()
{
const BYTE bytes[] = {
0x55, // 005FD780 | 55 | push ebp |
0x8B, 0xEC, // 005FD781 | 8BEC | mov ebp,esp |
0x83, 0xE4, 0xF8, // 005FD783 | 83E4 F8 | and esp,FFFFFFF8 |
0x83, 0xEC, 0x3C, // 005FD786 | 83EC 3C | sub esp,3C |
0xA1, XX4, // 005FD789 | A1 6C908600 | mov eax,dword ptr ds:[86906C] |
0x33, 0xC4, // 005FD78E | 33C4 | xor eax,esp |
0x89, 0x44, 0x24, 0x38, // 005FD790 | 894424 38 | mov dword ptr ss:[esp+38],eax |
0x53, // 005FD794 | 53 | push ebx |
0x56, // 005FD795 | 56 | push esi |
0x8B, 0xC1, // 005FD796 | 8BC1 | mov eax,ecx |
0xC7, 0x44, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00, // 005FD798 | C74424 14 00000000 | mov dword ptr ss:[esp+14],0 |
0x8B, 0x4D, 0x0C, // 005FD7A0 | 8B4D 0C | mov ecx,dword ptr ss:[ebp+C] |
0x33, 0xF6, // 005FD7A3 | 33F6 | xor esi,esi |
0x57, // 005FD7A5 | 57 | push edi |
0x8B, 0x7D, 0x08, // 005FD7A6 | 8B7D 08 | mov edi,dword ptr ss:[ebp+8] |
0x89, 0x44, 0x24, 0x14, // 005FD7A9 | 894424 14 | mov dword ptr ss:[esp+14],eax |
0x89, 0x4C, 0x24, 0x28, // 005FD7AD | 894C24 28 | mov dword ptr ss:[esp+28],ecx |
0x80, 0x3F, 0x00, // 005FD7B1 | 803F 00 | cmp byte ptr ds:[edi],0 |
0x0F, 0x84, XX4, // 005FD7B4 | 0F84 88040000 | je ヘンタイ・プリズンsplit 1.5FDC42 |
0x83, 0xB8, XX4, 0x00, // 005FD7BA | 83B8 74030000 00 | cmp dword ptr ds:[eax+374],0 |
0x8B, 0xDF, // 005FD7C1 | 8BDF | mov ebx,edi |
};
enum { addr_offset = 0 }; // distance to the beginning of the function, which is 0x55 (push ebp)
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("Artemis3: pattern not found");
return false;
}
addr += addr_offset;
enum { push_ebp = 0x55 }; // beginning of the function
if (*(BYTE *)addr != push_ebp) {
ConsoleOutput("Artemis3: beginning of the function not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_stack(1);
hp.type = USING_STRING| EMBED_ABLE|CODEC_UTF8|EMBED_BEFORE_SIMPLE|EMBED_AFTER_NEW;
return NewHook(hp, "EmbedArtemis");
}
namespace{
bool a4(){
//高慢な奥さんは好きですか?~傲慢人妻教師の堕とし方~
auto entryA=Util::FindImportEntry(processStartAddress,(DWORD)GetGlyphOutlineA);
auto entryW=Util::FindImportEntry(processStartAddress,(DWORD)GetGlyphOutlineW);
std::vector<uint64_t> addrs;
BYTE bytes[]={0xFF,0x15,XX4};
for(DWORD entry:{entryA,entryW})
if(entry) {
memcpy(bytes+2,&entry,4);
auto addrs_ = Util::SearchMemory(bytes, sizeof(bytes), PAGE_EXECUTE, processStartAddress, processStopAddress);
addrs.insert(addrs.end(), addrs_.begin(), addrs_.end());
}
bool ok=false;
for (auto addr : addrs) {
auto funcaddr = MemDbg::findEnclosingAlignedFunction(addr);
if (!funcaddr) continue;
BYTE sig1[]={0x81,XX,0x00,0x00,0x10,0x00};
BYTE sig2[]={0x68,0x00,0x02,0x00,0x00,0x68,0x00,0x02,0x00,0x00};
BYTE sig3[]={XX,0x80,0x00,0x00,0x00,0x0f,0x95,0xc1};
BYTE sig4[]={0xC1,XX,0x18};
int found=0;
for(auto sigsz:std::vector<std::pair<BYTE*,int>>{{sig1,sizeof(sig1)},{sig2,sizeof(sig2)},{sig3,sizeof(sig3)},{sig4,sizeof(sig4)}}){
auto fd= MemDbg::findBytes(sigsz.first, sigsz.second, funcaddr, addr);
if(fd)found+=1;
}
if(found==4){
{
HookParam hp;
hp.address = funcaddr;
hp.type = CODEC_ANSI_BE;
hp.offset=get_stack(2);
ok|=NewHook(hp, "Artemis4A");
}
{
HookParam hp;
hp.address = funcaddr+5;
hp.type = CODEC_UTF16;
hp.offset=get_stack(2);
ok|=NewHook(hp, "Artemis4W");
}
return ok;
}
}
return false;
}
}
bool Artemis::attach_function() {
return InsertArtemis1Hook() || InsertArtemis2Hook() || InsertArtemis3Hook()||a4();
}

View File

@ -0,0 +1,11 @@
#include"engine.h"
class Artemis:public ENGINE{
public:
Artemis(){
check_by=CHECK_BY::FILE;
check_by_target=L"*.pfs";
};
bool attach_function();
};

View File

@ -0,0 +1,248 @@
#include"Atelier.h"
/********************************************************************************************
AtelierKaguya hook:
Game folder contains message.dat. Used by AtelierKaguya games.
Usually has font caching issue with TextOutA.
Game engine uses EBP to set up stack frame so we can easily trace back.
Keep step out until it's in main game module. We notice that either register or
stack contains string pointer before call instruction. But it's not quite stable.
In-depth analysis of the called function indicates that there's a loop traverses
the string one character by one. We can set a hook there.
This search process is too complex so I just make use of some characteristic
instruction(add esi,0x40) to locate the right point.
********************************************************************************************/
bool InsertAtelierHook()
{
PcHooks::hookOtherPcFunctions(); // lstrlenA gives good hook too
//SafeFillRange(processName, &base, &size);
//size=size-base;
//DWORD sig = 0x40c683; // add esi,0x40
//i=processStartAddress+SearchPattern(processStartAddress,processStopAddress-processStartAddress,&sig,3);
DWORD i;
for (i = processStartAddress; i < processStopAddress - 4; i++) {
DWORD sig = *(DWORD *)i & 0xffffff;
if (0x40c683 == sig) // add esi,0x40
break;
}
if (i < processStopAddress - 4)
for (DWORD j=i-0x200; i>j; i--)
if (*(DWORD *)i == 0xff6acccc) { // find the function entry
HookParam hp;
hp.address = i+2;
hp.offset=get_stack(2);
hp.split = get_reg(regs::esp);
hp.type = USING_SPLIT;
ConsoleOutput("INSERT Aterlier KAGUYA");
//RegisterEngineType(ENGINE_ATELIER);
return NewHook(hp, "Atelier KAGUYA");
}
ConsoleOutput("Aterlier: failed");
return false;
//ConsoleOutput("Unknown Atelier KAGUYA engine.");
}
bool InsertAtelierKaguya2Hook()
{
/*
* Sample games:
* https://vndb.org/v22713
* https://vndb.org/v31685
* https://vndb.org/v37081
*/
const BYTE bytes[] = {
0x51, // push ecx << hook here
0x50, // push eax
0xE8, XX4, // call Start.exe+114307
0x83, 0xC4, 0x08, // add esp,08
0x85, 0xC0, // test eax,eax
0x78, 0xA1 // js Start.exe+48947
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("Atelier KAGUYA2: pattern not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::eax);
hp.type = USING_STRING|EMBED_AFTER_OVERWRITE|EMBED_BEFORE_SIMPLE|EMBED_ABLE|EMBED_DYNA_SJIS;
hp.hook_font=F_TextOutA;
hp.filter_fun = NewLineCharToSpaceFilterA;
ConsoleOutput("INSERT Atelier KAGUYA2");
return NewHook(hp, "Atelier KAGUYA2");
}
bool InsertAtelierKaguya3Hook()
{
/*
* Sample games:
* https://vndb.org/v10082
*/
const BYTE bytes[] = {
0x55, // push ebp << hook here
0x8B, 0xEC, // mov ebp,esp
0x6A, 0xFF, // push -01
0x68, 0x80, 0xB9, 0x4D, 0x00, // push Start.exe+DB980
0x64, 0xA1, XX4, // mov eax,fs:[00000000]
0x50, // push eax
0x51, // push ecx
0x81, 0xEC, 0xAC, 0x00, 0x00, 0x00 // sub esp,000000AC
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("Atelier KAGUYA3: pattern not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::eax);
hp.type = USING_STRING;
hp.filter_fun = NewLineCharToSpaceFilterA;
ConsoleOutput("INSERT Atelier KAGUYA3");
return NewHook(hp, "Atelier KAGUYA3");
}
bool InsertAtelierKaguya4Hook()
{
/*
* Sample games:
* https://vndb.org/v14705
*/
const BYTE bytes[] = {
0xE8, 0x90, 0xA8, 0xFF, 0xFF, // call Start.exe+18380
0x89, 0x45, 0xF8, // mov [ebp-08],eax
0x8B, 0x4D, 0x10, // mov ecx,[ebp+10]
0x51, // push ecx
0x8B, 0x55, 0x0C, // mov edx,[ebp+0C]
0x52, // push edx
0x8B, 0x45, 0x08, // mov eax,[ebp+08]
0x50 // push eax << hook here
};
enum { addr_offset = sizeof(bytes) - 1 };
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("Atelier KAGUYA4: pattern not found");
return false;
}
HookParam hp;
hp.address = addr + addr_offset;
hp.offset=get_reg(regs::eax);
hp.type = USING_STRING;
hp.filter_fun = NewLineCharToSpaceFilterA;
ConsoleOutput("INSERT Atelier KAGUYA4");
return NewHook(hp, "Atelier KAGUYA4");
}
bool InsertAtelierKaguya5Hook()
{
/*
* Sample games:
* https://vndb.org/v11224
*/
const BYTE bytes[] = {
0xC2, 0x04, 0x00, // ret 0004
0x55, // push ebp << hook here
0x8B, 0xEC, // mov ebp,esp
0x6A, 0xFF, // push -01
0x68, XX4, // push Start.exe+DA680
0x64, 0xA1, 0x00, 0x00, 0x00, 0x00, // mov eax,fs:[00000000]
0x50, // push eax
0x51, // push ecx
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("Atelier KAGUYA5: pattern not found");
return false;
}
HookParam hp;
hp.address = addr + 3;
hp.offset=get_reg(regs::eax);
hp.type = USING_STRING;
hp.filter_fun = NewLineCharToSpaceFilterA;
ConsoleOutput("INSERT Atelier KAGUYA5");
return NewHook(hp, "Atelier KAGUYA5");
}
bool InsertAtelierKaguyaX()
{
//エロティ課 誘惑研修はじまるよ~ しごいちゃうから覚悟なさい!
const BYTE bytes[] = {
0x3D,0xF0,0x41,0x00,0x00,
0x75
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) return false;
addr = findfuncstart(addr,0x1000);
if (!addr) return false;
HookParam hp;
hp.address = addr;
hp.offset=get_stack(1);
hp.type = USING_STRING;
return NewHook(hp, "Atelier KAGUYA3");
}
bool Atelier::attach_function() {
return InsertAtelierHook() || InsertAtelierKaguya2Hook() ||InsertAtelierKaguyaX()|| InsertAtelierKaguya3Hook() || InsertAtelierKaguya4Hook() || InsertAtelierKaguya5Hook();
}
bool Atelier2attach_function(){
//https://vndb.org/v304
//ダンジョンクルセイダーズTALES OF DEMON EATER
const BYTE bytes[] = {
0x83 ,0xFE ,0x34 ,
0xF6 ,XX ,
0x88 ,XX,0x24 ,0x29 ,
0x7D
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) return false;
HookParam hp;
hp.address = addr+sizeof(bytes)-1;
hp.offset = get_stack(10);
return NewHook(hp, "Atelier KAGUYA3");
}
bool Atelier2attach_function2(){
//https://vndb.org/v7264
//禁断の病棟 特殊精神科医 遊佐惣介の診察記録
auto addr=MemDbg::findCallerAddressAfterInt3((ULONG)TextOutA,processStartAddress,processStopAddress);
if(addr==0)return 0;
HookParam hp;
hp.address = addr;
hp.offset=get_stack(3);
hp.type=USING_STRING|DATA_INDIRECT;
return NewHook(hp, "Atelier KAGUYA");
}
bool Atelier2::attach_function(){
return Atelier2attach_function()||Atelier2attach_function2();
}

View File

@ -0,0 +1,24 @@
#include"engine.h"
class Atelier:public ENGINE{
public:
Atelier(){
check_by=CHECK_BY::FILE;
check_by_target=L"message.dat";
};
bool attach_function();
};
class Atelier2:public ENGINE{
public:
Atelier2(){
check_by=CHECK_BY::CUSTOM;
check_by_target=[](){
return (Util::CheckFile(L"*.ARC")&&Util::CheckFile(L"*.ARI"))||
(Util::CheckFile(L"ARC\\*.ARC")&&Util::CheckFile(L"ARC\\*.ARI"));
};
};
bool attach_function();
};

1542
LunaHook/engine32/BGI.cpp Normal file

File diff suppressed because it is too large Load Diff

11
LunaHook/engine32/BGI.h Normal file
View File

@ -0,0 +1,11 @@
#include"engine.h"
class BGI:public ENGINE{
public:
BGI(){
check_by=CHECK_BY::FILE_ANY;
check_by_target=check_by_list{L"bgi.*",L"sysgrp.arc"};
};
bool attach_function();
};

View File

@ -0,0 +1,59 @@
#include"BKEngine.h"
//https://bke.bakery.moe/download.html
namespace{
bool _1(){
BYTE sig[]={0x64,0xa3,0x00,0x00,0x00,0x00,0x8b,0xf1,0x8b,0x45,0x08,0x0f,0x57,0xc0,0xc7,0x06,0x02,0x00,0x00,0x00};
auto addr=MemDbg::findBytes(sig, sizeof(sig), processStartAddress, processStopAddress);
if(addr==0)return 0;
addr=MemDbg::findEnclosingAlignedFunction(addr);
if(addr==0)return 0;
HookParam hp;
hp.address = addr;
hp.type = CODEC_UTF16|DATA_INDIRECT;
hp.index=0;
hp.offset=get_stack(1);
return NewHook(hp, "BKEngine1");
}
bool _2(){
BYTE sig[]={0xb8,0xff,0x00,0x00,0x00,0x66,0x3b,0x06,0x1b,0xc0,0xf7,0xd8,0x40};
auto addr=MemDbg::findBytes(sig, sizeof(sig), processStartAddress, processStopAddress);
if(addr==0)return 0;
addr=MemDbg::findEnclosingAlignedFunction(addr);
if(addr==0)return 0;
HookParam hp;
hp.address = addr;
hp.type = CODEC_UTF16|DATA_INDIRECT|NO_CONTEXT;
hp.index=0;
hp.offset=get_stack(1);
return NewHook(hp, "BKEngine2");
}
bool _3(){
BYTE sig[]={0x6a,0xff,0x6a,0x00,0x56};
std::unordered_map<DWORD,int>mp;
DWORD maxaddr=0;int maxi=0;
for(auto addr:Util::SearchMemory(sig, sizeof(sig),PAGE_EXECUTE, processStartAddress, processStopAddress)){
addr=MemDbg::findEnclosingAlignedFunction(addr);
if(addr==0)continue;
if(mp.find(addr)==mp.end())mp[addr]=0;
mp[addr]+=1;
if(mp[addr]>maxi){maxi=mp[addr];maxaddr=addr;}
}
if(maxaddr==0)return 0;
HookParam hp;
hp.address = maxaddr;
hp.type = CODEC_UTF16|USING_STRING;
hp.offset=get_reg(regs::edx);
return NewHook(hp, "BKEngine3");
}
}
bool BKEngine::attach_function() {
bool ok= _1();
ok=_2()||ok;
ok=_3()||ok;
return ok;
}

View File

@ -0,0 +1,11 @@
#include"engine.h"
class BKEngine:public ENGINE{
public:
BKEngine(){
is_engine_certain=false;
check_by=CHECK_BY::FILE;
check_by_target=L"*.bkarc";
};
bool attach_function();
};

View File

@ -0,0 +1,67 @@
#include"Bishop.h"
bool bishopmbcjmstojis()
{
//特別授業
const BYTE bytes[] = {
//unsigned int __cdecl _mbcjmstojis(unsigned int C)
0x55,0x8b,0xec,
0x8b,0x45,0x08, //mov eax, [ebp+C]
0x81, 0x3D,XX4, 0xA4 ,0x03 ,0x00 ,0x00, //cmp dword_4A1F0C, 3A4h //if ( dword_4A1F0C == 932 )
XX2,
0xa9,0x00,0x00,0xff,0xff //if ( (C & 0xFFFF0000) != 0 )
};
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (!addr) return false;
HookParam hp;
hp.address = addr ;
hp.offset=get_stack(2);
hp.type = USING_SPLIT|USING_STRING;
return NewHook(hp, "bishop");
}
bool Bishop::attach_function() {
return bishopmbcjmstojis();
}
bool Bishop2::attach_function(){
//三射面談~連鎖する恥辱・調教の学園~
//特別授業3SLG
auto entry=Util::FindImportEntry(processStartAddress,(DWORD)GetGlyphOutlineW);
if(entry==0)return false;
bool ok=false;
for(auto addr:Util::SearchMemory(&entry, 4, PAGE_EXECUTE, processStartAddress, processStopAddress)){
addr = MemDbg::findEnclosingAlignedFunction(addr);
if (!addr) continue;
auto xrefs=findxref_reverse_checkcallop(addr,max(processStartAddress,addr-0x100000),min(processStopAddress,addr+0x100000),0xe8);
for(auto addrx:xrefs){
//ConsoleOutput("xref %p",addrx);
const BYTE aligned [] = {0xCC,0xCC};
auto addrx1 = reverseFindBytes(aligned, sizeof(aligned), addrx-0x200, addrx);
//ConsoleOutput("Aligned %p",addrx1);
if (!addrx1) continue;
addrx1+=2;
BYTE __1[]={0xDC,0x0D,XX,XX,XX,0x00};
auto _1 = MemDbg::findBytes(__1, 6, addrx-0x30, addrx);
//ConsoleOutput("sig %p",_1);
if(_1==0 )continue;
BYTE checkthiscall[]={0x8B,0xF9};//mov edi, ecx
auto _3 = MemDbg::findBytes(checkthiscall,2, addrx1, addrx);
HookParam hp;
hp.address = addrx1;
if(_3)
hp.offset=get_stack(3);
else
hp.offset=get_stack(4);
hp.type = CODEC_UTF16;
ok=NewHook(hp, "Bishop2");
}
}
return ok;
}

View File

@ -0,0 +1,24 @@
#include"engine.h"
class Bishop:public ENGINE{
public:
Bishop(){
check_by=CHECK_BY::FILE;
check_by_target=L"GRAPHICS\\PACK.PK";
is_engine_certain=false;
};
bool attach_function();
};
class Bishop2:public ENGINE{
public:
Bishop2(){
check_by=CHECK_BY::FILE;
check_by_target=L"*.bsa";
is_engine_certain=false;
};
bool attach_function();
};

View File

@ -0,0 +1,259 @@
#include"Bootup.h"
/**
* jichi 5/22/2015: Insert Bootup hook
* Sample games:
* - [090709] [PIL] 西
* - [110318] [Daisy2] <EFBFBD> * - [110329] [PIL/SLASH]
* - [150527] [Daisy2] <EFBFBD> *
* Properties
* - There is Bootup.dat existing in the game folder.
* - lstrlenW can find text repeating once
* - GetCharABCWidthsW and TextOutW can find cached text that missing characters
* GetCharABCWidthsA and TextOutA for old games.
* - There is only one TextOut (W for new and A for old).
*
* Logic:
* + GDI hook
* - Hook to the caller of TextOut
* + Lstr hook
* - Find last (second) caller of the first GetCharABCWidths after int3
* - Find the lstrlen function in this caller, and hook to it
*
* Full text is in arg1, shifted one by one.
* Character to paint is also in arg3
*
* All Bootup games are slightly different
* - <EFBFBD>西: text in both lstrlenA and caller of TextOutA
* But I didn't find correct lstrlenA to hook. BootupLstrA find nothing for 西 and name for <EFBFBD>
* - : text in both lstrlenW and TextOutW, but lstrlenW has repetition
* Caller of TextOutW the same as that of TextOutA
* - <EFBFBD> text in both lstrlenW and TextOutW. But TextOutW's name has repetition
* Caller of TextOutW different
*
* Here's the beginning of caller of TextOutW in <EFBFBD>
* 00B61ADD CC INT3
* 00B61ADE CC INT3
* 00B61ADF CC INT3
* 00B61AE0 55 PUSH EBP
* 00B61AE1 8BEC MOV EBP,ESP
* 00B61AE3 81EC 98000000 SUB ESP,0x98
* 00B61AE9 53 PUSH EBX
* 00B61AEA 56 PUSH ESI
* 00B61AEB 57 PUSH EDI
* 00B61AEC 8BF2 MOV ESI,EDX
* 00B61AEE 8BF9 MOV EDI,ECX
* 00B61AF0 8975 D8 MOV DWORD PTR SS:[EBP-0x28],ESI
* 00B61AF3 897D E0 MOV DWORD PTR SS:[EBP-0x20],EDI
* 00B61AF6 E8 A5FEFFFF CALL .00B619A0
* 00B61AFB 8BD8 MOV EBX,EAX
* 00B61AFD 895D CC MOV DWORD PTR SS:[EBP-0x34],EBX
* 00B61B00 66:833B 00 CMP WORD PTR DS:[EBX],0x0
* 00B61B04 0F85 0B020000 JNZ .00B61D15
* 00B61B0A B8 00010000 MOV EAX,0x100
* 00B61B0F 66:8933 MOV WORD PTR DS:[EBX],SI
* 00B61B12 66:3BF0 CMP SI,AX
* 00B61B15 72 26 JB SHORT .00B61B3D
* 00B61B17 8B47 3C MOV EAX,DWORD PTR DS:[EDI+0x3C]
* 00B61B1A 85C0 TEST EAX,EAX
* 00B61B1C 74 1F JE SHORT .00B61B3D
* 00B61B1E 8B57 44 MOV EDX,DWORD PTR DS:[EDI+0x44]
* 00B61B21 85D2 TEST EDX,EDX
* 00B61B23 7E 18 JLE SHORT .00B61B3D
* 00B61B25 33C9 XOR ECX,ECX
* 00B61B27 85D2 TEST EDX,EDX
* 00B61B29 7E 12 JLE SHORT .00B61B3D
* 00B61B2B 8B47 40 MOV EAX,DWORD PTR DS:[EDI+0x40]
* 00B61B2E 8BFF MOV EDI,EDI
* 00B61B30 66:3930 CMP WORD PTR DS:[EAX],SI
* 00B61B33 74 6F JE SHORT .00B61BA4
* 00B61B35 41 INC ECX
* 00B61B36 83C0 02 ADD EAX,0x2
* 00B61B39 3BCA CMP ECX,EDX
* 00B61B3B ^7C F3 JL SHORT .00B61B30
* 00B61B3D 33C0 XOR EAX,EAX
* 00B61B3F 66:8945 9E MOV WORD PTR SS:[EBP-0x62],AX
* 00B61B43 8B47 04 MOV EAX,DWORD PTR DS:[EDI+0x4]
* 00B61B46 0FAF47 1C IMUL EAX,DWORD PTR DS:[EDI+0x1C]
* 00B61B4A 0FAF47 1C IMUL EAX,DWORD PTR DS:[EDI+0x1C]
* 00B61B4E 0FAF47 18 IMUL EAX,DWORD PTR DS:[EDI+0x18]
* 00B61B52 50 PUSH EAX
* 00B61B53 6A 00 PUSH 0x0
* 00B61B55 FF77 14 PUSH DWORD PTR DS:[EDI+0x14]
* 00B61B58 66:8975 9C MOV WORD PTR SS:[EBP-0x64],SI
* 00B61B5C E8 2FC20200 CALL .00B8DD90
* 00B61B61 83C4 0C ADD ESP,0xC
* 00B61B64 8D45 9C LEA EAX,DWORD PTR SS:[EBP-0x64]
* 00B61B67 6A 01 PUSH 0x1
* 00B61B69 50 PUSH EAX
* 00B61B6A 6A 00 PUSH 0x0
* 00B61B6C 6A 00 PUSH 0x0
* 00B61B6E FF77 10 PUSH DWORD PTR DS:[EDI+0x10]
* 00B61B71 FF15 8820BB00 CALL DWORD PTR DS:[0xBB2088] ; gdi32.TextOutW
* 00B61B77 8B47 1C MOV EAX,DWORD PTR DS:[EDI+0x1C]
* 00B61B7A 8B57 14 MOV EDX,DWORD PTR DS:[EDI+0x14]
* 00B61B7D 8B7F 04 MOV EDI,DWORD PTR DS:[EDI+0x4]
* 00B61B80 8B73 0C MOV ESI,DWORD PTR DS:[EBX+0xC]
* 00B61B83 0FAFF8 IMUL EDI,EAX
* 00B61B86 48 DEC EAX
* 00B61B87 8975 C4 MOV DWORD PTR SS:[EBP-0x3C],ESI
* 00B61B8A 897D C8 MOV DWORD PTR SS:[EBP-0x38],EDI
*
* TextOutW's caller for
* 0113183E CC INT3
* 0113183F CC INT3
* 01131840 55 PUSH EBP
* 01131841 8BEC MOV EBP,ESP
* 01131843 83EC 74 SUB ESP,0x74
* 01131846 53 PUSH EBX
* 01131847 56 PUSH ESI
* 01131848 8B75 08 MOV ESI,DWORD PTR SS:[EBP+0x8]
* 0113184B 57 PUSH EDI
* 0113184C 8B7D 0C MOV EDI,DWORD PTR SS:[EBP+0xC]
* 0113184F 8BCF MOV ECX,EDI
* 01131851 8BD6 MOV EDX,ESI
* 01131853 E8 A8FEFFFF CALL .01131700
* 01131858 8BD8 MOV EBX,EAX
* 0113185A 66:833B 00 CMP WORD PTR DS:[EBX],0x0
* 0113185E 895D 90 MOV DWORD PTR SS:[EBP-0x70],EBX
* 01131861 0F85 700F0000 JNZ .011327D7
* 01131867 B8 00010000 MOV EAX,0x100
* 0113186C 66:893B MOV WORD PTR DS:[EBX],DI
* 0113186F 66:3BF8 CMP DI,AX
* 01131872 72 2E JB SHORT .011318A2
* 01131874 8B56 3C MOV EDX,DWORD PTR DS:[ESI+0x3C]
* 01131877 85D2 TEST EDX,EDX
* 01131879 74 27 JE SHORT .011318A2
* 0113187B 8B46 44 MOV EAX,DWORD PTR DS:[ESI+0x44]
* 0113187E 85C0 TEST EAX,EAX
* 01131880 7E 20 JLE SHORT .011318A2
* 01131882 33FF XOR EDI,EDI
* 01131884 85C0 TEST EAX,EAX
* 01131886 7E 1A JLE SHORT .011318A2
* 01131888 8B46 40 MOV EAX,DWORD PTR DS:[ESI+0x40]
* 0113188B EB 03 JMP SHORT .01131890
* 0113188D 8D49 00 LEA ECX,DWORD PTR DS:[ECX]
* 01131890 66:8B4D 0C MOV CX,WORD PTR SS:[EBP+0xC]
* 01131894 66:3908 CMP WORD PTR DS:[EAX],CX
* 01131897 74 74 JE SHORT .0113190D
* 01131899 47 INC EDI
* 0113189A 83C0 02 ADD EAX,0x2
* 0113189D 3B7E 44 CMP EDI,DWORD PTR DS:[ESI+0x44]
* 011318A0 ^7C EE JL SHORT .01131890
* 011318A2 66:8B45 0C MOV AX,WORD PTR SS:[EBP+0xC]
* 011318A6 66:8945 8C MOV WORD PTR SS:[EBP-0x74],AX
* 011318AA 8B46 1C MOV EAX,DWORD PTR DS:[ESI+0x1C]
* 011318AD 0FAFC0 IMUL EAX,EAX
* 011318B0 0FAF46 18 IMUL EAX,DWORD PTR DS:[ESI+0x18]
* 011318B4 0FAF46 04 IMUL EAX,DWORD PTR DS:[ESI+0x4]
* 011318B8 8B56 14 MOV EDX,DWORD PTR DS:[ESI+0x14]
* 011318BB 33C9 XOR ECX,ECX
* 011318BD 50 PUSH EAX
* 011318BE 51 PUSH ECX
* 011318BF 52 PUSH EDX
* 011318C0 66:894D 8E MOV WORD PTR SS:[EBP-0x72],CX
* 011318C4 E8 87060200 CALL .01151F50
* 011318C9 8B4E 10 MOV ECX,DWORD PTR DS:[ESI+0x10]
* 011318CC 83C4 0C ADD ESP,0xC
* 011318CF 6A 01 PUSH 0x1
* 011318D1 8D45 8C LEA EAX,DWORD PTR SS:[EBP-0x74]
* 011318D4 50 PUSH EAX
* 011318D5 6A 00 PUSH 0x0
* 011318D7 6A 00 PUSH 0x0
* 011318D9 51 PUSH ECX
* 011318DA FF15 38101701 CALL DWORD PTR DS:[0x1171038] ; gdi32.TextOutW
* 011318E0 8B4E 1C MOV ECX,DWORD PTR DS:[ESI+0x1C]
* 011318E3 8B46 04 MOV EAX,DWORD PTR DS:[ESI+0x4]
* 011318E6 8B56 14 MOV EDX,DWORD PTR DS:[ESI+0x14]
* 011318E9 0FAFC1 IMUL EAX,ECX
* 011318EC 8B7B 0C MOV EDI,DWORD PTR DS:[EBX+0xC]
*/
namespace { // unnamed
bool BootupGDIHook(hook_stack* stack, HookParam *hp)
{
DWORD arg2 = stack->stack[2];
if ((arg2 & 0xffff0000)) { // if arg2 high bits are there, this is new Bootup game
hp->type |= DATA_INDIRECT;
hp->offset = get_stack(3);
hp->split = get_reg(regs::ebx);
}
return false; // run once and stop hooking
}
bool InsertBootupGDIHook()
{
bool widechar = true;
ULONG addr = MemDbg::findCallerAddressAfterInt3((ULONG)TextOutW, processStartAddress, processStopAddress);
if (!addr) {
addr = MemDbg::findCallerAddressAfterInt3((ULONG)TextOutA, processStartAddress, processStopAddress);
widechar = false;
}
if (!addr) {
ConsoleOutput("BootupGDI: failed to find TextOut");
return false;
}
HookParam hp;
hp.address = addr;
hp.type = USING_SPLIT|NO_CONTEXT|USING_CHAR; // use NO_CONTEXT to get rid of floating reladdr
hp.type |= widechar ? CODEC_UTF16 : CODEC_ANSI_BE; // use context as split is sufficient, but will produce floating split
hp.offset=get_stack(2); // arg2, character in arg2, could be modified by hook
if (widechar)
hp.split = get_reg(regs::edx);
else
hp.split = get_stack(1);
hp.hook_fun = BootupGDIHook; // adjust hook parameter at runtime
ConsoleOutput("INSERT BootupGDI");
ConsoleOutput("BootupGDI: disable GDI hooks");
return NewHook(hp, widechar ? "BootupW" : "BootupA");
}
bool InsertBootupLstrHook() // for character name
{
bool widechar = true;
ULONG addr = MemDbg::findLastCallerAddressAfterInt3((ULONG)GetCharABCWidthsW, processStartAddress, processStopAddress);
if (!addr) {
// Do not hook to lstrlenA, which causes text extraction to stop
//addr = MemDbg::findLastCallerAddressAfterInt3((ULONG)GetCharABCWidthsA, processStartAddress, processStopAddress);
//widechar = false;
}
if (!addr) {
ConsoleOutput("BootupLstr: failed to find GetCharABCWidths");
return false;
}
//GROWL_DWORD2(addr, processStartAddress);
//enum { range = 0x200 }; // 0x012A2CCB - 0x12A2CB0 = 0x1b
addr = MemDbg::findCallAddress(widechar ? (ULONG)::lstrlenW : (ULONG)::lstrlenA,
processStartAddress, processStopAddress,
addr - processStartAddress); //, range); // no range
if (!addr) {
ConsoleOutput("BootupLstr: failed to find lstrlen");
return false;
}
HookParam hp;
hp.address = addr;
hp.type = widechar ? (USING_STRING|CODEC_UTF16) : USING_STRING; // use context as split is sufficient, but will produce floating split
//hp.type = CODEC_UTF16|NO_CONTEXT|USING_SPLIT; // use text address as split
//hp.split = 0;
ConsoleOutput("INSERT BootupLstr");
return NewHook(hp, widechar ? "BootupLstrW" : "BootupLstrA");
}
} // unnamed namespace
bool InsertBootupHook()
{
bool ret = InsertBootupGDIHook();
InsertBootupLstrHook();
return ret;
}
bool Bootup::attach_function() {
return InsertBootupHook();
}

View File

@ -0,0 +1,13 @@
#include"engine.h"
class Bootup:public ENGINE{
public:
Bootup(){
check_by=CHECK_BY::FILE;
check_by_target=L"Bootup.dat";
is_engine_certain=false;
// lstrlenW can also find text with repetition though
};
bool attach_function();
};

View File

@ -0,0 +1,80 @@
#include"Bruns.h"
bool InsertBrunsHook()
{
bool success=false;
if (Util::CheckFile(L"libscr.dll")) {
HookParam hp;
hp.offset=get_stack(1);
hp.type = CODEC_UTF16;
//?push_back@?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QAEXG@Z
if (Util::CheckFile(L"msvcp90.dll"))
hp.address = (DWORD)GetProcAddress(GetModuleHandleW(L"msvcp90.dll"), "?push_back@?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QAEXG@Z");
else if (Util::CheckFile(L"msvcp80.dll"))
hp.address = (DWORD)GetProcAddress(GetModuleHandleW(L"msvcp80.dll"), "?push_back@?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QAEXG@Z");
else if (Util::CheckFile(L"msvcp100.dll")) // jichi 8/17/2013: MSVCRT 10.0 and 11.0
hp.address = (DWORD)GetProcAddress(GetModuleHandleW(L"msvcp100.dll"), "?push_back@?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QAEXG@Z");
else if (Util::CheckFile(L"msvcp110.dll"))
hp.address = (DWORD)GetProcAddress(GetModuleHandleW(L"msvcp110.dll"), "?push_back@?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QAEXG@Z");
if (hp.address) {
ConsoleOutput("INSERT Brus#1");
success|=NewHook(hp, "Bruns");
}
}
//else
// jichi 12/21/2013: Keep both bruns hooks
// The first one does not work for games like 「オーク・キングダマモン娘繁殖<E7B981>豚人王<E78E8B>anymore.
{
union {
DWORD i;
DWORD *id;
WORD *iw;
BYTE *ib;
};
DWORD k = processStopAddress - 4;
for (i = processStartAddress + 0x1000; i < k; i++) {
if (*id != 0xff) //cmp reg,0xff
continue;
i += 4;
if (*iw != 0x8f0f)
continue;//jg
i += 2;
i += *id + 4;
for (DWORD j = i + 0x40; i < j; i++) {
if (*ib != 0xe8)
continue;
i++;
DWORD t = i + 4 + *id;
if (t > processStartAddress && t <processStopAddress) {
i = t;
for (j = i + 0x80; i < j; i++) {
if (*ib != 0xe8)
continue;
i++;
t = i + 4 + *id;
if (t > processStartAddress && t <processStopAddress) {
HookParam hp;
hp.address = t;
hp.offset=get_stack(1);
hp.type = CODEC_UTF16|DATA_INDIRECT;
ConsoleOutput("INSERT Brus#2");
return NewHook(hp, "Bruns2");
}
}
k = i; //Terminate outer loop.
break; //Terminate inner loop.
}
}
}
}
//ConsoleOutput("Unknown Bruns engine.");
ConsoleOutput("Brus: failed");
return success;
}
bool Bruns::attach_function() {
return InsertBrunsHook();
}

29
LunaHook/engine32/Bruns.h Normal file
View File

@ -0,0 +1,29 @@
#include"engine.h"
class Bruns:public ENGINE{
public:
Bruns(){
check_by=CHECK_BY::CUSTOM;
is_engine_certain=false;
check_by_target=[](){
return Util::CheckFile(L"args.txt")||(wcsstr(processName_lower, L"bruns") || Util::CheckFile(L"bruns.exe"));
};
//check_by=CHECK_BY::FILE;
//check_by_target=L"args.txt";
//if (Util::CheckFile(L"libscr.dll")) { // already checked
// InsertBrunsHook();
// return true;
//}
// jichi 10/12/2013: Sample args.txt:
// See: http://tieba.baidu.com/p/2631413816
// -workdir
// .
// -loadpath
// .
// am.cfg
};
bool attach_function();
};

29
LunaHook/engine32/C4.cpp Normal file
View File

@ -0,0 +1,29 @@
#include"C4.h"
/********************************************************************************************
C4 hook: (Contributed by Stomp)
Game folder contains C4.EXE or XEX.EXE.
********************************************************************************************/
bool InsertC4Hook()
{
const BYTE bytes[] = { 0x8a, 0x10, 0x40, 0x80, 0xfa, 0x5f, 0x88, 0x15 };
//enum { addr_offset = 0 };
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (!addr) {
ConsoleOutput("C4: pattern not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::eax);
hp.type = DATA_INDIRECT|NO_CONTEXT;
ConsoleOutput("INSERT C4");
//RegisterEngineType(ENGINE_C4);
return NewHook(hp, "C4");
}
bool C4::attach_function() {
return InsertC4Hook();
}

11
LunaHook/engine32/C4.h Normal file
View File

@ -0,0 +1,11 @@
#include"engine.h"
class C4:public ENGINE{
public:
C4(){
check_by=CHECK_BY::FILE_ANY;
check_by_target=check_by_list{L"C4.EXE",L"XEX.EXE"};
};
bool attach_function();
};

1527
LunaHook/engine32/CMVS.cpp Normal file

File diff suppressed because it is too large Load Diff

18
LunaHook/engine32/CMVS.h Normal file
View File

@ -0,0 +1,18 @@
#include"engine.h"
class CMVS:public ENGINE{
public:
CMVS(){
check_by=CHECK_BY::FILE;
check_by_target=L"data\\pack\\*.cpz";
// jichi 8/19/2013: DO NOT WORK for games like「ハピメア」
//if (wcsstr(str,L"cmvs32") || wcsstr(str,L"cmvs64")) {
// InsertCMVSHook();
// return true;
//}
};
bool attach_function();
};

215
LunaHook/engine32/Candy.cpp Normal file
View File

@ -0,0 +1,215 @@
#include"Candy.h"
/********************************************************************************************
CandySoft hook:
Game folder contains many *.fpk. Engine name is SystemC.
I haven't seen this engine in other company/brand.
AGTH /X3 will hook lstrlenA. One thread is the exactly result we want.
But the function call is difficult to located programmatically.
I find a equivalent points which is more easy to search.
The script processing function needs to find 0x5B'[',
so there should a instruction like cmp reg,5B
Find this position and navigate to function entry.
The first parameter is the string pointer.
This approach works fine with game later than <EFBFBD>
But the original <EFBFBD>is quite different. I handle this case separately.
********************************************************************************************/
namespace { // unnamed Candy
// jichi 8/23/2013: split into two different engines
//if (_wcsicmp(processName, L"systemc.exe")==0)
// Process name is "SystemC.exe"
bool InsertCandyHook1()
{
for (DWORD i = processStartAddress + 0x1000; i < processStopAddress - 4; i++)
if ((*(DWORD *)i&0xffffff) == 0x24f980) // cmp cl,24
for (DWORD j = i, k = i - 0x100; j > k; j--)
if (*(DWORD *)j == 0xc0330a8a) { // mov cl,[edx]; xor eax,eax
HookParam hp;
hp.address = j;
hp.offset=get_reg(regs::edx);
hp.type = USING_STRING;
ConsoleOutput("INSERT SystemC#1");
//RegisterEngineType(ENGINE_CANDY);
return NewHook(hp, "SystemC");
}
ConsoleOutput("CandyHook1: failed");
return false;
}
// jichi 8/23/2013: Process name is NOT "SystemC.exe"
bool InsertCandyHook2()
{
for (DWORD i = processStartAddress + 0x1000; i < processStopAddress - 4 ;i++)
if (*(WORD *)i == 0x5b3c || // cmp al,0x5b
(*(DWORD *)i & 0xfff8fc) == 0x5bf880) // cmp reg,0x5B
for (DWORD j = i, k = i - 0x100; j > k; j--)
if ((*(DWORD *)j & 0xffff) == 0x8b55) { // push ebp, mov ebp,esp, sub esp,*
HookParam hp;
hp.address = j;
if(((*(BYTE *)(j+3)))==0x51) //push ecx ,thiscall
hp.offset=get_reg(regs::ecx); //アイドルクリニック恋の薬でHな処方
else
hp.offset=get_stack(1); // jichi: text in arg1
hp.type = USING_STRING;
//RegisterEngineType(ENGINE_CANDY);
return NewHook(hp, "SystemC");
}
ConsoleOutput("CandyHook2: failed");
return false;
}
/** jichi 10/2/2013: CHECKPOINT
*
* [5/31/2013] Hもお勉強も<EFBFBD><EFBFBD>
* base = 0xf20000
* + : /HSN-4@104A48:ANEBU.EXE
* - off: 4294967288 = 0xfffffff8 = -8
, - type: 1025 = 0x401
* + : /HSN-4@104FDD:ANEBU.EXE
* - off: 4294967288 = 0xfffffff8 = -8
* - type: 1089 = 0x441
*/
//bool InsertCandyHook3()
//{
// return false; // CHECKPOINT
// const BYTE ins[] = {
// 0x83,0xc4, 0x0c, // add esp,0xc ; hook here
// 0x0f,0xb6,0xc0, // movzx eax,al
// 0x85,0xc0, // test eax,eax
// 0x75, 0x0e // jnz XXOO ; it must be 0xe, or there will be duplication
// };
// enum { addr_offset = 0 };
// ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
// ULONG reladdr = SearchPattern(processStartAddress, range, ins, sizeof(ins));
// reladdr = 0x104a48;
// GROWL_DWORD(processStartAddress);
// //GROWL_DWORD3(reladdr, processStartAddress, range);
// if (!reladdr)
// return false;
//
// HookParam hp;
// hp.address = processStartAddress + reladdr + addr_offset;
// hp.offset=get_reg(regs::eax);
// hp.type = USING_STRING|NO_CONTEXT;
// NewHook(hp, "Candy");
// return true;
//}
} // unnamed Candy
namespace{
bool candy3(){
//お母さんは俺専用!~あなたの初めてを…母さんが貰ってア・ゲ・ル~
//茉莉子さん家の性事情 ~伯母さんは僕のモノ~
const BYTE bytes[] = {
0x24, //XX||XX2
0x75
};
for (auto addr : Util::SearchMemory(bytes, sizeof(bytes), PAGE_EXECUTE)){
ConsoleOutput("%x",addr);
if((*(BYTE*)(addr-1) ==0x3c)||((*(BYTE*)(addr-2) ==0x83)&&(*(BYTE*)(addr-1) ==0xf9))){
addr=MemDbg::findEnclosingAlignedFunction(addr);
if(addr==0)continue;
ConsoleOutput("!%x",addr);
HookParam hp;
hp.type = USING_STRING;
if(*(BYTE*)addr==0x55)
hp.offset=get_stack(1);
else if(*(BYTE*)addr==0x56)
hp.offset=get_reg(regs::eax);
else
continue;
hp.address = addr;
return NewHook(hp, "candy3");
}
}
return false;
}
bool InsertCandyHook3()
{
/*
* Sample games:
* https://vndb.org/v24878
*/
const BYTE bytes[] = {
0xCC, // int 3
0x55, // push ebp << hook here
0x8B, 0xEC, // mov ebp,esp
0x6A, 0xFF, // push -01
0x68, XX4, // push iinari-omnibus.exe+C4366
0x64, 0xA1, 0x00, 0x00, 0x00, 0x00, // mov eax,fs:[00000000]
0x50, // push eax
0x83, 0xEC, 0x74, // sub esp,74
0x53, // push ebx
0x56, // push esi
0x57 // push edi
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("SystemC#3: pattern not found");
return false;
}
HookParam hp;
hp.address = addr + 1;
hp.offset=get_stack(4);
hp.type = USING_STRING | CODEC_UTF16;
ConsoleOutput("INSERT SystemC#3");
return NewHook(hp, "SystemC#3");
}
}
// jichi 10/2/2013: Add new candy hook
bool InsertCandyHook()
{
PcHooks::hookOtherPcFunctions();
//if (0 == _wcsicmp(processName, L"systemc.exe"))
if (Util::CheckFile(L"SystemC.exe"))
return InsertCandyHook1()||candy3();
else{
//return InsertCandyHook2();
bool b2 = InsertCandyHook2(),
b3 = InsertCandyHook3();
return b2 || b3;
}
}
bool Candy::attach_function() {
return InsertCandyHook();
}
bool WillowSoft::attach_function(){
//お母さんがいっぱい!!限定ママBOX
const BYTE bytes[] = {
0xF7 ,0xC2 ,0x00 ,0x00 ,0xFF ,0x00,
XX2,
0xF7 ,0xC2 ,0x00 ,0x00 ,0x00 ,0xFF ,
XX2
};
auto addr=MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if(addr==0)return false;
addr=MemDbg::findEnclosingAlignedFunction(addr);
if(addr==0)return false;
HookParam hp;
hp.type = USING_STRING;
hp.offset=get_stack(2);
hp.type |= DATA_INDIRECT;
hp.index = 0;
hp.address = addr;
return NewHook(hp, "WillowSoft");
}

24
LunaHook/engine32/Candy.h Normal file
View File

@ -0,0 +1,24 @@
#include"engine.h"
class Candy:public ENGINE{
public:
Candy(){
check_by=CHECK_BY::FILE_ANY;
check_by_target=check_by_list{L"*.fpk",L"data\\*.fpk"};
is_engine_certain=false;
};
bool attach_function();
};
class WillowSoft:public ENGINE{
public:
WillowSoft(){
check_by=CHECK_BY::FILE;
check_by_target=L"Selene.dll";
is_engine_certain=false;
};
bool attach_function();
};

View File

@ -0,0 +1,123 @@
#include"CaramelBox.h"
static void SpecialHookCaramelBox(hook_stack* stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t *len)
{
DWORD reg_ecx = *(DWORD*)(stack->base + hp->offset);
BYTE *ptr = (BYTE *)reg_ecx;
buffer_index = 0;
while (ptr[0])
if (ptr[0] == 0x28) { // Furigana format: (Kanji,Furi)
ptr++;
while (ptr[0]!=0x2c) //Copy Kanji
text_buffer[buffer_index++] = *ptr++;
while (ptr[0]!=0x29) // Skip Furi
ptr++;
ptr++;
} else if (ptr[0] == 0x5c)
ptr +=2;
else {
text_buffer[buffer_index++] = ptr[0];
if (LeadByteTable[ptr[0]] == 2) {
ptr++;
text_buffer[buffer_index++] = ptr[0];
}
ptr++;
}
*len = buffer_index;
*data = (DWORD)text_buffer;
*split = 0; // 8/3/2014 jichi: use return address as split
}
// jichi 10/1/2013: Change return type to bool
bool InsertCaramelBoxHook()
{
union { DWORD i; BYTE* pb; WORD* pw; DWORD *pd; };
DWORD reg = -1;
for (i = processStartAddress + 0x1000; i < processStopAddress - 4; i++) {
if (*pd == 0x7ff3d) // cmp eax, 7ff
reg = 0;
else if ((*pd & 0xfffff8fc) == 0x07fff880) // cmp reg, 7ff
reg = pb[1] & 0x7;
if (reg == -1)
continue;
DWORD flag = 0;
if (*(pb - 6) == 3) { //add reg, [ebp+$disp_32]
if (*(pb - 5) == (0x85 | (reg << 3)))
flag = 1;
} else if (*(pb - 3) == 3) { // add reg, [ebp+$disp_8]
if (*(pb - 2) == (0x45 | (reg << 3)))
flag = 1;
} else if (*(pb - 2) == 3) { // add reg, reg
if (((*(pb - 1) >> 3) & 7)== reg)
flag = 1;
}
reg = -1;
if (flag) {
for (DWORD j = i, k = i - 0x100; j > k; j--) {
if ((*(DWORD *)j & 0xffff00ff) == 0x1000b8) { // mov eax,10??
HookParam hp;
hp.address = j & ~0xf;
hp.text_fun = SpecialHookCaramelBox;
hp.type = USING_STRING;
for (i &= ~0xffff; i < processStopAddress - 4; i++)
if (pb[0] == 0xe8) {
pb++;
if (pd[0] + i + 4 == hp.address) {
pb += 4;
if ((pd[0] & 0xffffff) == 0x04c483)
hp.offset=get_stack(1);
else hp.offset=get_reg(regs::ecx);
break;
}
}
if (hp.offset == 0) {
ConsoleOutput("CaramelBox: failed, zero off");
return false;
}
ConsoleOutput("INSERT CaramelBox");
//RegisterEngineType(ENGINE_CARAMEL);
return NewHook(hp, "CaramelBox");
}
}
}
}
ConsoleOutput("CaramelBox: failed");
return false;
//_unknown_engine:
//ConsoleOutput("Unknown CarmelBox engine.");
}
bool CaramelBox::attach_function() {
return InsertCaramelBoxHook();
}
bool CaramelBoxMilkAji::attach_function(){
//雨芳恋歌
//https://vndb.org/v6663
BYTE bytes[] = {
0x33,0xD2,
0xB9,0x8A,0x02,0x00,0x00,
0xF7,0xF1,
0x6B,0xC0,0x44,
0x6B,0xC0,0x03
};
auto addr=MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if(addr==0)return false;
addr=MemDbg::findEnclosingAlignedFunction(addr);
if(addr==0)return false;
HookParam hp;
hp.address = addr;
hp.type = USING_STRING;
hp.offset=get_stack(1);
return NewHook(hp, "CaramelBox");
}

View File

@ -0,0 +1,36 @@
#include"engine.h"
class CaramelBox:public ENGINE{
public:
CaramelBox(){
check_by=CHECK_BY::CUSTOM;
check_by_target=[](){
auto str=std::wstring( processName_lower);
DWORD len = str.size();
// jichi 8/10/2013: Since *.bin is common, move CaramelBox to the end
str[len - 3] = L'b';
str[len - 2] = L'i';
str[len - 1] = L'n';
str[len] = 0;
return (Util::CheckFile(str.c_str()) || Util::CheckFile(L"trial.bin"));
};
is_engine_certain=false;
};
bool attach_function();
};
class CaramelBoxMilkAji:public ENGINE{
public:
CaramelBoxMilkAji(){
check_by=CHECK_BY::FILE;
check_by_target=L"SdActiRc.dll";
is_engine_certain=false;
};
bool attach_function();
};

View File

@ -0,0 +1,799 @@
#include"CatSystem.h"
#include"embed_util.h"
#include"dyncodec/dynsjis.h"
// jichi 5/10/2014
// See also: http://bbs.sumisora.org/read.php?tid=11044704&fpage=2
//
// Old engine: グリザイアの迷宮
// 0053cc4e cc int3
// 0053cc4f cc int3
// 0053cc50 6a ff push -0x1 ; jichi: hook here
// 0053cc52 68 6b486000 push .0060486b
// 0053cc57 64:a1 00000000 mov eax,dword ptr fs:[0]
// 0053cc5d 50 push eax
// 0053cc5e 81ec 24020000 sub esp,0x224
// 0053cc64 a1 f8647600 mov eax,dword ptr ds:[0x7664f8]
// 0053cc69 33c4 xor eax,esp
// 0053cc6b 898424 20020000 mov dword ptr ss:[esp+0x220],eax
// 0053cc72 53 push ebx
// 0053cc73 55 push ebp
// 0053cc74 56 push esi
// 0053cc75 57 push edi
//
// Stack:
// 0544e974 0053d593 return to .0053d593 from .0053cc50
// 0544e978 045cc820
// 0544e97c 00008dc5 : jichi: text
// 0544e980 00000016
// 0544e984 0452f2e4
// 0544e988 00000000
// 0544e98c 00000001
// 0544e990 0544ea94
// 0544e994 04513840
// 0544e998 0452f2b8
// 0544e99c 04577638
// 0544e9a0 04620450
// 0544e9a4 00000080
// 0544e9a8 00000080
// 0544e9ac 004914f3 return to .004914f3 from .0055c692
//
// Registers:
// edx 0
// ebx 00000016
//
//
// New engine: イノセントガール
// Stack:
// 051ae508 0054e9d1 return to .0054e9d1 from .0054e310
// 051ae50c 04361650
// 051ae510 00008ca9 ; jichi: text
// 051ae514 0000001a
// 051ae518 04343864
// 051ae51c 00000000
// 051ae520 00000001
// 051ae524 051ae62c
// 051ae528 041edc20
// 051ae52c 04343830
// 051ae530 0434a8b0
// 051ae534 0434a7f0
// 051ae538 00000080
// 051ae53c 00000080
// 051ae540 3f560000
// 051ae544 437f8000
// 051ae548 4433e000
// 051ae54c 16f60c00
// 051ae550 051ae650
// 051ae554 042c4c20
// 051ae558 0000002c
// 051ae55c 00439bc5 return to .00439bc5 from .0043af60
//
// Registers & stack:
// Scenario:
// eax 04361650
// ecx 04357640
// edx 04343864
// ebx 0000001a
// esp 051ae508
// ebp 00008169
// esi 04357640
// edi 051ae62c
// eip 0054e310 .0054e310
//
// 051ae508 0054e9d1 return to .0054e9d1 from .0054e310
// 051ae50c 04361650
// 051ae510 00008169
// 051ae514 0000001a
// 051ae518 04343864
// 051ae51c 00000000
// 051ae520 00000001
// 051ae524 051ae62c
// 051ae528 041edc20
// 051ae52c 04343830
// 051ae530 0434a8b0
// 051ae534 0434a7f0
// 051ae538 00000080
// 051ae53c 00000080
// 051ae540 3f560000
// 051ae544 437f8000
// 051ae548 4433e000
// 051ae54c 16f60c00
// 051ae550 051ae650
// 051ae554 042c4c20
// 051ae558 0000002c
//
// Name:
//
// eax 04362430
// ecx 17025230
// edx 0430b6e4
// ebx 0000001a
// esp 051ae508
// ebp 00008179
// esi 17025230
// edi 051ae62c
// eip 0054e310 .0054e310
//
// 051ae508 0054e9d1 return to .0054e9d1 from .0054e310
// 051ae50c 04362430
// 051ae510 00008179
// 051ae514 0000001a
// 051ae518 0430b6e4
// 051ae51c 00000000
// 051ae520 00000001
// 051ae524 051ae62c
// 051ae528 041edae0
// 051ae52c 0430b6b0
// 051ae530 0434a790
// 051ae534 0434a910
// 051ae538 00000080
// 051ae53c 00000080
// 051ae540 3efa0000
// 051ae544 4483f000
// 051ae548 44322000
// 051ae54c 16f60aa0
// 051ae550 051ae650
// 051ae554 042c4c20
// 051ae558 0000002c
static void SpecialHookCatSystem3(hook_stack* stack, HookParam *, uintptr_t *data, uintptr_t *split, size_t *len)
{
//DWORD ch = *data = *(DWORD *)(esp_base + hp->offset); // arg2
DWORD ch = *data = stack->stack[2];
*len = LeadByteTable[(ch >> 8) & 0xff]; // CODEC_ANSI_BE
*split = stack->edx >> 16;
}
bool InsertCatSystemHook()
{
//DWORD search=0x95EB60F;
//DWORD j,i=SearchPattern(processStartAddress,processStopAddress-processStartAddress,&search,4);
//if (i==0) return;
//i+=processStartAddress;
//for (j=i-0x100;i>j;i--)
// if (*(DWORD*)i==0xcccccccc) break;
//if (i==j) return;
//hp.address=i+4;
//hp.offset=get_reg(regs::eax);
//hp.index=4;
//hp.type =CODEC_ANSI_BE|DATA_INDIRECT|USING_SPLIT|SPLIT_INDIRECT;
//hp.length_offset=1;
enum { beg = 0xff6acccc }; // jichi 7/12/2014: beginning of the function
enum { addr_offset = 2 }; // skip two leading 0xcc
ULONG addr = MemDbg::findCallerAddress((ULONG)::GetTextMetricsA, beg, processStartAddress, processStopAddress);
if (!addr) {
ConsoleOutput("CatSystem2: pattern not exist");
return false;
}
HookParam hp;
hp.address = addr + addr_offset; // skip 1 push?
hp.offset=get_stack(2); // text character is in arg2
// jichi 12/23/2014: Modify split for new catsystem
bool newEngine = Util::CheckFile(L"cs2conf.dll");
if (newEngine) {
//hp.text_fun = SpecialHookCatSystem3; // type not needed
//NewHook(hp, "CatSystem3");
//ConsoleOutput("INSERT CatSystem3");
hp.type = CODEC_ANSI_BE|USING_SPLIT;
hp.split = get_reg(regs::esi);
ConsoleOutput("INSERT CatSystem3new");
return NewHook(hp, "CatSystem3new");
} else {
hp.type = CODEC_ANSI_BE|USING_SPLIT;
hp.split = get_reg(regs::edx);
ConsoleOutput("INSERT CatSystem2");
return NewHook(hp, "CatSystem2");
}
}
bool InsertCatSystem2Hook()
{
/*
* Sample games:
* https://vndb.org/v26987
*/
const BYTE bytes[] = {
0x38, 0x08, // cmp [eax],cl
0x0F, 0x84, XX4, // je cs2.exe+23E490
0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, XX4, // nop word ptr [eax+eax+00000000]
0x4F, // dec edi
0xC7, 0x85, XX4, XX4, // mov [ebp-000005A0],00000000
0x33, 0xF6, // xor esi,esi
0xC7, 0x85, XX4, XX4, // mov [ebp-0000057C],00000000
0x85, 0xFF // test edi,edi
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("CatSystem2new: pattern not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::eax);
hp.codepage = 65001;
hp.type = USING_STRING;
ConsoleOutput("INSERT CatSystem2new");
return NewHook(hp, "CatSystem2new");
}
namespace { // unnamed
namespace Patch {
namespace Private {
// String in ecx
// bool __fastcall isLeadByteChar(const char *s, DWORD edx)
// bool isLeadByteChar(hook_stack*s,void* data, size_t* len,uintptr_t*role)
// {
// auto pc=(CHAR*)s->ecx;
// s->eax=(bool)((pc)&&dynsjis::isleadbyte(*pc));
// return false;
// //return dynsjis::isleadstr(s); // no idea why this will cause Grisaia3 to hang
// //return ::IsDBCSLeadByte(HIBYTE(testChar));
// }
bool isLeadByteChar(char* s)
{
return s && dynsjis::isleadchar(*s);
//return dynsjis::isleadstr(s); // no idea why this will cause Grisaia3 to hang
//return ::IsDBCSLeadByte(HIBYTE(testChar));
}
} // namespace Private
/**
* Sample game:
*
* This function is found by searching the following instruction:
* 00511C8E 3C 81 CMP AL,0x81
*
* This function is very similar to that in LC-ScriptEngine.
*
* Return 1 if the first byte in arg1 is leading byte else 0.
*
* 00511C7C CC INT3
* 00511C7D CC INT3
* 00511C7E CC INT3
* 00511C7F CC INT3
* 00511C80 8B4C24 04 MOV ECX,DWORD PTR SS:[ESP+0x4]
* 00511C84 85C9 TEST ECX,ECX
* 00511C86 74 2F JE SHORT .00511CB7
* 00511C88 8A01 MOV AL,BYTE PTR DS:[ECX]
* 00511C8A 84C0 TEST AL,AL
* 00511C8C 74 29 JE SHORT .00511CB7
* 00511C8E 3C 81 CMP AL,0x81
* 00511C90 72 04 JB SHORT .00511C96
* 00511C92 3C 9F CMP AL,0x9F
* 00511C94 76 08 JBE SHORT .00511C9E
* 00511C96 3C E0 CMP AL,0xE0
* 00511C98 72 1D JB SHORT .00511CB7
* 00511C9A 3C EF CMP AL,0xEF
* 00511C9C 77 19 JA SHORT .00511CB7
* 00511C9E 8A41 01 MOV AL,BYTE PTR DS:[ECX+0x1]
* 00511CA1 3C 40 CMP AL,0x40
* 00511CA3 72 04 JB SHORT .00511CA9
* 00511CA5 3C 7E CMP AL,0x7E
* 00511CA7 76 08 JBE SHORT .00511CB1
* 00511CA9 3C 80 CMP AL,0x80
* 00511CAB 72 0A JB SHORT .00511CB7
* 00511CAD 3C FC CMP AL,0xFC
* 00511CAF 77 06 JA SHORT .00511CB7
* 00511CB1 B8 01000000 MOV EAX,0x1
* 00511CB6 C3 RETN
* 00511CB7 33C0 XOR EAX,EAX
* 00511CB9 C3 RETN
* 00511CBA CC INT3
* 00511CBB CC INT3
* 00511CBC CC INT3
* 00511CBD CC INT3
*
* Sample game: Grisaia3
* 0050747F CC INT3
* 00507480 8B4C24 04 MOV ECX,DWORD PTR SS:[ESP+0x4] ; jichi: text in arg1
* 00507484 85C9 TEST ECX,ECX
* 00507486 74 2F JE SHORT .005074B7
* 00507488 8A01 MOV AL,BYTE PTR DS:[ECX]
* 0050748A 84C0 TEST AL,AL
* 0050748C 74 29 JE SHORT .005074B7
* 0050748E 3C 81 CMP AL,0x81
* 00507490 72 04 JB SHORT .00507496
* 00507492 3C 9F CMP AL,0x9F
* 00507494 76 08 JBE SHORT .0050749E
* 00507496 3C E0 CMP AL,0xE0
* 00507498 72 1D JB SHORT .005074B7
* 0050749A 3C EF CMP AL,0xEF
* 0050749C 77 19 JA SHORT .005074B7
* 0050749E 8A41 01 MOV AL,BYTE PTR DS:[ECX+0x1]
* 005074A1 3C 40 CMP AL,0x40
* 005074A3 72 04 JB SHORT .005074A9
* 005074A5 3C 7E CMP AL,0x7E
* 005074A7 76 08 JBE SHORT .005074B1
* 005074A9 3C 80 CMP AL,0x80
* 005074AB 72 0A JB SHORT .005074B7
* 005074AD 3C FC CMP AL,0xFC
* 005074AF 77 06 JA SHORT .005074B7
* 005074B1 B8 01000000 MOV EAX,0x1
* 005074B6 C3 RETN
* 005074B7 33C0 XOR EAX,EAX
* 005074B9 C3 RETN
* 005074BA CC INT3
* 005074BB CC INT3
* 005074BC CC INT3
* 005074BD CC INT3
*
* Sample game: Grisaia1
* 0041488A CC INT3
* 0041488B CC INT3
* 0041488C CC INT3
* 0041488D CC INT3
* 0041488E CC INT3
* 0041488F CC INT3
* 00414890 85C9 TEST ECX,ECX ; jichi: text in ecx
* 00414892 74 2F JE SHORT Grisaia.004148C3
* 00414894 8A01 MOV AL,BYTE PTR DS:[ECX]
* 00414896 84C0 TEST AL,AL
* 00414898 74 29 JE SHORT Grisaia.004148C3
* 0041489A 3C 81 CMP AL,0x81
* 0041489C 72 04 JB SHORT Grisaia.004148A2
* 0041489E 3C 9F CMP AL,0x9F
* 004148A0 76 08 JBE SHORT Grisaia.004148AA
* 004148A2 3C E0 CMP AL,0xE0
* 004148A4 72 1D JB SHORT Grisaia.004148C3
* 004148A6 3C EF CMP AL,0xEF
* 004148A8 77 19 JA SHORT Grisaia.004148C3
* 004148AA 8A41 01 MOV AL,BYTE PTR DS:[ECX+0x1]
* 004148AD 3C 40 CMP AL,0x40
* 004148AF 72 04 JB SHORT Grisaia.004148B5
* 004148B1 3C 7E CMP AL,0x7E
* 004148B3 76 08 JBE SHORT Grisaia.004148BD
* 004148B5 3C 80 CMP AL,0x80
* 004148B7 72 0A JB SHORT Grisaia.004148C3
* 004148B9 3C FC CMP AL,0xFC
* 004148BB 77 06 JA SHORT Grisaia.004148C3
* 004148BD B8 01000000 MOV EAX,0x1
* 004148C2 C3 RETN
* 004148C3 33C0 XOR EAX,EAX
* 004148C5 C3 RETN
* 004148C6 CC INT3
* 004148C7 CC INT3
* 004148C8 CC INT3
*/
ULONG patchEncoding(ULONG startAddress, ULONG stopAddress)
{
const uint8_t bytes[] = {
0x74, 0x29, // 00511c8c 74 29 je short .00511cb7
0x3c, 0x81 // 00511c8e 3c 81 cmp al,0x81
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
if (!addr)
return false;
addr = MemDbg::findEnclosingAlignedFunction(addr);
if (!addr)
return false;
for (auto p = addr; p - addr < 20; p += ::disasm((LPCVOID)p))
if (*(WORD *)p == 0xc985)// 00414890 85C9 TEST ECX,ECX ; jichi: text in ecx
return addr;//winhook::replace_fun(p, (ULONG)Private::isLeadByteChar);
return 0;
}
} // namespace Patch
/**
* Sample game:
*
* Example prefix to skip:
* 03751294 81 40 5C 70 63 81 75 83 7B 83 4E 82 CC 8E AF 82  \pc
*
* 033CF370 5C 6E 81 40 5C 70 63 8C 4A 82 E8 95 D4 82 BB 82 \n \pc繰り返そ
* 033CF380 A4 81 41 96 7B 93 96 82 C9 81 41 82 B1 82 CC 8B
* 033CF390 47 90 DF 82 CD 81 41 83 8D 83 4E 82 C8 82 B1 82 G節は
* 033CF3A0 C6 82 AA 82 C8 82 A2 81 42 00 AA 82 C8 82 A2 81 .
* 033CF3B0 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 B...............
* 033CF3C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* 033CF3D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* 033CF3E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* 033CF3F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* 033CF400 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
*
* Sample choice texts:
*
* str 155
*
* 0 op01
*
* 1 select_go_tar
*/
template <typename strT>
strT ltrim(strT text)
{
strT lastText = nullptr;
while (*text && text != lastText) {
lastText = text;
if (text[0] == 0x20)
text++;
if ((UINT8)text[0] == 0x81 && (UINT8)text[1] == 0x40) // skip space \u3000 (0x8140 in sjis)
text += 2;
if (text[0] == '\\') {
text++;
while (::islower(text[0]) || text[0] == '@')
text++;
}
}
while ((signed char)text[0] > 0 && text[0] != '[') // skip all leading ascii characters except "[" needed for ruby
text++;
return text;
}
// Remove trailing '\@'
size_t rtrim(LPCSTR text)
{
size_t size = ::strlen(text);
while (size >= 2 && text[size - 2] == '\\' && (UINT8)text[size - 1] <= 127)
size -= 2;
return size;
}
namespace ScenarioHook {
namespace Private {
bool isOtherText(LPCSTR text)
{
/* Sample game: ゆきこいめると */
return ::strcmp(text, "\x91\x49\x91\xf0\x8e\x88") == 0; /* 選択肢 */
}
/**
* Sample game:
*
* Sample ecx:
*
* 03283A88 24 00 CD 02 76 16 02 00 24 00 CD 02 58 00 CD 02 $.v.$.X.
* 03283A98 BD 2D 01 00 1C 1C 49 03 14 65 06 00 14 65 06 00 -.Ie.e.
* this is ID, this is the same ID: 0x066514
* 03283AA8 80 64 06 00 20 8C 06 00 24 00 6C 0D 00 00 10 00 €d. .$.l....
* this is ID: 0x066480
* 03283AB8 C8 F1 C2 00 21 00 00 00 48 A9 75 00 E8 A9 96 00 .!...Hゥu.
* 03283AC8 00 00 00 00 48 80 4F 03 00 00 00 00 CC CC CC CC ....H€O....
* 03283AD8 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
*/
//struct ClassArgument // for ecx
//{
// DWORD unknown[7],
// split1, // 0x20 - 9
// split2; // 0x20
// // split1 - split2 is always 0x94
// DWORD split() const { return split1 - split2; } //
//};
static bool containsNamePunct_(const char *text)
{
static const char *puncts[] = {
"\x81\x41" /* 、 */
, "\x81\x43" /* */
, "\x81\x42" /* 。 */
//, "\x81\x48" /* */
, "\x81\x49" /* */
, "\x81\x63" /* … */
, "\x81\x64" /* ‥ */
//, "\x81\x79" /* 【 */
//, "\x81\x7a" /* 】 */
, "\x81\x75" /* 「 */
, "\x81\x76" /* 」 */
, "\x81\x77" /* 『 */
, "\x81\x78" /* 』 */
//, "\x81\x69" /* */
//, "\x81\x6a" /* */
//, "\x81\x6f" /* */
//, "\x81\x70" /* */
//, "\x81\x71" /* 〈 */
//, "\x81\x72" /* 〉 */
, "\x81\x6d" /* */
, "\x81\x6e" /* */
//, "\x81\x83", /* */
//, "\x81\x84", /* */
, "\x81\x65" /* */
, "\x81\x66" /* */
, "\x81\x67" /* “ */
, "\x81\x68" /* ” */
};
for (size_t i = 0; i < sizeof(puncts)/sizeof(*puncts); i++)
if (::strstr(text, puncts[i]))
return true;
if (::strstr(text, "\x81\x48") /* */
&& !::strstr(text, "\x81\x48\x81\x48\x81\x48")) /* */
return true;
return false;
}
bool guessIsNameText(const char *text, size_t size)
{
enum { MaximumNameSize = 0x10 };
if (!size)
size = ::strlen(text);
return size < MaximumNameSize && !containsNamePunct_(text);
}
LPSTR trimmedText;size_t trimmedSize;
bool hookBefore(hook_stack*s,void* data, size_t* len,uintptr_t*role)
{
//static std::unordered_set<uint64_t> hashes_;
auto text = (LPSTR)s->eax; // arg1
if (!text || !*text || all_ascii(text))
return false;
// Alternatively, if do not skip ascii chars, edx is always 0x4ef74 for Japanese texts
//if (s->edx != 0x4ef74)
// return true;
trimmedText = ltrim(text);
if (!trimmedText || !*trimmedText)
return false;
trimmedSize = rtrim(trimmedText);
* role = Engine::OtherRole;
//DOUT(QString::fromLocal8Bit((LPCSTR)s->esi));
//auto splitText = (LPCSTR)s->esi;
//if (::strcmp(splitText, "MES_SETNAME")) // This is for scenario text with voice
//if (::strcmp(splitText, "MES_SETFACE"))
//if (::strcmp(splitText, "pcm")) // first scenario or history without text
// return true;
//auto retaddr = s->stack[1]; // caller
//auto retaddr = s->stack[13]; // parent caller
//auto split = *(DWORD *)s->esi;
//auto split = s->esi - s->eax;
//DOUT(split);
//auto self = (ClassArgument *)s->ecx;
//auto split = self->split();
//enum { sig = 0 };
auto self = s->ecx;
if (!Engine::isAddressWritable(self)) // old cs2 game such as Grisaia
self = s->stack[2]; // arg1
ULONG groupId = self;
if (Engine::isAddressWritable(self))
groupId = *(DWORD *)(self + 0x20);
{
static ULONG minimumGroupId_ = -1; // I assume scenario thread to have minimum groupId
//if (session_.addText(groupId, Engine::hashCharArray(text))) {
if (groupId <= minimumGroupId_) {
minimumGroupId_ = groupId;
*role = Engine::ScenarioRole;
if (isOtherText(text))
*role = Engine::OtherRole;
else if (::isdigit(text[0]))
*role = Engine::ChoiceRole;
else if (trimmedText == text && !trimmedText[trimmedSize] // no prefix and suffix
&& guessIsNameText(trimmedText, trimmedSize))
*role = Engine::NameRole;
}
}
std::string oldData(trimmedText, trimmedSize);
strcpy((char*)data,oldData.c_str());
*len=oldData.size();
return true;
}
void hookafter(hook_stack*s,void* data, size_t len){
auto newData =std::string((char*)data,len);
if (trimmedText[trimmedSize])
newData.append(trimmedText + trimmedSize);
::strcpy(trimmedText, newData.c_str());
}
} // namespace Private
/**
* Sample game:
*
* Debugging message:
* - Hook to GetGlyphOutlineA
* - Find "MES_SHOW" address on the stack
* Alternatively, find the address of "fes.int/flow.fes" immediately after the game is launched
* - Use hardware breakpoint to find out when "MES_SHOW" is overridden
* Only stop when text is written by valid scenario text.
*
* 00503ADE CC INT3
* 00503ADF CC INT3
* 00503AE0 8B4424 0C MOV EAX,DWORD PTR SS:[ESP+0xC]
* 00503AE4 8B4C24 04 MOV ECX,DWORD PTR SS:[ESP+0x4]
* 00503AE8 56 PUSH ESI
* 00503AE9 FF30 PUSH DWORD PTR DS:[EAX]
* 00503AEB E8 102F1600 CALL Hatsumir.00666A00 ; jichi: text in eax after this call
* 00503AF0 BE 18058900 MOV ESI,Hatsumir.00890518 ; ASCII "fes.int/flow.fes"
* 00503AF5 8BC8 MOV ECX,EAX ; jichi: esi is the target location
* 00503AF7 2BF0 SUB ESI,EAX
* 00503AF9 8DA424 00000000 LEA ESP,DWORD PTR SS:[ESP]
* 00503B00 8A11 MOV DL,BYTE PTR DS:[ECX]
* 00503B02 8D49 01 LEA ECX,DWORD PTR DS:[ECX+0x1]
* 00503B05 88540E FF MOV BYTE PTR DS:[ESI+ECX-0x1],DL ; jichi: target location modified here
* 00503B09 84D2 TEST DL,DL
* 00503B0B ^75 F3 JNZ SHORT Hatsumir.00503B00
* 00503B0D 8B4C24 0C MOV ECX,DWORD PTR SS:[ESP+0xC]
* 00503B11 50 PUSH EAX
* 00503B12 68 18058900 PUSH Hatsumir.00890518 ; ASCII "fes.int/flow.fes"
* 00503B17 8B89 B4000000 MOV ECX,DWORD PTR DS:[ECX+0xB4]
* 00503B1D E8 EE030B00 CALL Hatsumir.005B3F10
* 00503B22 B8 02000000 MOV EAX,0x2
* 00503B27 5E POP ESI
* 00503B28 C2 1000 RETN 0x10
* 00503B2B CC INT3
* 00503B2C CC INT3
* 00503B2D CC INT3
* 00503B2E CC INT3
*
* EAX 0353B1A0 ; jichi: text here
* ECX 00D86D08
* EDX 0004EF74
* EBX 00012DB2
* ESP 0525EBAC
* EBP 0525ED6C
* ESI 00D86D08
* EDI 00000000
* EIP 00503AF0 Hatsumir.00503AF0
*
* 0525EBAC 00D86D08
* 0525EBB0 0066998E RETURN to Hatsumir.0066998E
* 0525EBB4 00D86D08
* 0525EBB8 00B16188
* 0525EBBC 035527D8
* 0525EBC0 0525EBE4
* 0525EBC4 00B16188
* 0525EBC8 00D86D08
* 0525EBCC 0525F62B ASCII "ript.kcs"
* 0525EBD0 00000004
* 0525EBD4 00000116
* 0525EBD8 00000003
* 0525EBDC 00000003
* 0525EBE0 00665C08 RETURN to Hatsumir.00665C08
* 0525EBE4 CCCCCCCC
* 0525EBE8 0525F620 ASCII "kcs.int/sscript.kcs"
* 0525EBEC 00694D94 Hatsumir.00694D94
* 0525EBF0 004B278F RETURN to Hatsumir.004B278F from Hatsumir.00666CA0
* 0525EBF4 B3307379
* 0525EBF8 0525ED04
* 0525EBFC 00B16188
* 0525EC00 0525ED04
* 0525EC04 00B16188
* 0525EC08 00CC5440
* 0525EC0C 02368938
* 0525EC10 0069448C ASCII "%s/%s"
* 0525EC14 00B45B18 ASCII "kcs.int"
* 0525EC18 00000001
* 0525EC1C 023741E0
* 0525EC20 0000000A
* 0525EC24 0049DBB3 RETURN to Hatsumir.0049DBB3 from Hatsumir.00605A84
* 0525EC28 72637373
* 0525EC2C 2E747069
* 0525EC30 0073636B Hatsumir.0073636B
* 0525EC34 0525ED04
* 0525EC38 0053ECDE RETURN to Hatsumir.0053ECDE from Hatsumir.004970C0
* 0525EC3C 0525EC80
* 0525EC40 023D9FB8
*
* Alternative ruby hook:
* It will hook to the beginning of the Ruby processing function, which is not better than the current approach.
* http://lab.aralgood.com/index.php?mid=board_lecture&search_target=title_content&search_keyword=CS&document_srl=1993027
*
* Sample game: Grisaia3
*
* 004B00CB CC INT3
* 004B00CC CC INT3
* 004B00CD CC INT3
* 004B00CE CC INT3
* 004B00CF CC INT3
* 004B00D0 8B4424 0C MOV EAX,DWORD PTR SS:[ESP+0xC]
* 004B00D4 8B08 MOV ECX,DWORD PTR DS:[EAX]
* 004B00D6 56 PUSH ESI
* 004B00D7 51 PUSH ECX
* 004B00D8 8B4C24 0C MOV ECX,DWORD PTR SS:[ESP+0xC]
* 004B00DC E8 7F191300 CALL .005E1A60
* 004B00E1 BE D0E87B00 MOV ESI,.007BE8D0
* 004B00E6 8BC8 MOV ECX,EAX
* 004B00E8 2BF0 SUB ESI,EAX
* 004B00EA 8D9B 00000000 LEA EBX,DWORD PTR DS:[EBX]
* 004B00F0 8A11 MOV DL,BYTE PTR DS:[ECX]
* 004B00F2 88140E MOV BYTE PTR DS:[ESI+ECX],DL
* 004B00F5 41 INC ECX
* 004B00F6 84D2 TEST DL,DL
* 004B00F8 ^75 F6 JNZ SHORT .004B00F0
* 004B00FA 8B5424 0C MOV EDX,DWORD PTR SS:[ESP+0xC]
* 004B00FE 8B8A B4000000 MOV ECX,DWORD PTR DS:[EDX+0xB4]
* 004B0104 50 PUSH EAX
* 004B0105 68 D0E87B00 PUSH .007BE8D0
* 004B010A E8 818D0600 CALL .00518E90
* 004B010F B8 02000000 MOV EAX,0x2
* 004B0114 5E POP ESI
* 004B0115 C2 1000 RETN 0x10
* 004B0118 CC INT3
* 004B0119 CC INT3
* 004B011A CC INT3
* 004B011B CC INT3
* 004B011C CC INT3
*
* Sample game: Grisaia1
* 00498579 CC INT3
* 0049857A CC INT3
* 0049857B CC INT3
* 0049857C CC INT3
* 0049857D CC INT3
* 0049857E CC INT3
* 0049857F CC INT3
* 00498580 8B4424 0C MOV EAX,DWORD PTR SS:[ESP+0xC]
* 00498584 8B08 MOV ECX,DWORD PTR DS:[EAX] ; jichi: ecx is no longer a pointer
* 00498586 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+0x4]
* 0049858A 56 PUSH ESI
* 0049858B E8 10920500 CALL Grisaia.004F17A0
* 00498590 BE D89C7600 MOV ESI,Grisaia.00769CD8 ; ASCII "bgm01"
* 00498595 8BC8 MOV ECX,EAX
* 00498597 2BF0 SUB ESI,EAX
* 00498599 8DA424 00000000 LEA ESP,DWORD PTR SS:[ESP]
* 004985A0 8A11 MOV DL,BYTE PTR DS:[ECX]
* 004985A2 88140E MOV BYTE PTR DS:[ESI+ECX],DL
* 004985A5 41 INC ECX
* 004985A6 84D2 TEST DL,DL
* 004985A8 ^75 F6 JNZ SHORT Grisaia.004985A0
* 004985AA 8B4C24 0C MOV ECX,DWORD PTR SS:[ESP+0xC]
* 004985AE 8B91 B4000000 MOV EDX,DWORD PTR DS:[ECX+0xB4]
* 004985B4 50 PUSH EAX
* 004985B5 68 D89C7600 PUSH Grisaia.00769CD8 ; ASCII "bgm01"
* 004985BA 52 PUSH EDX
* 004985BB E8 701C0600 CALL Grisaia.004FA230
* 004985C0 B8 02000000 MOV EAX,0x2
* 004985C5 5E POP ESI
* 004985C6 C2 1000 RETN 0x10
* 004985C9 CC INT3
* 004985CA CC INT3
* 004985CB CC INT3
* 004985CC CC INT3
* 004985CD CC INT3
*/
bool attach(ULONG startAddress, ULONG stopAddress)
{
const uint8_t bytes[] = {
0xe8, XX4, // 004b00dc e8 7f191300 call .005e1a60 ; jichi: hook after here
0xbe, XX4, // 004b00e1 be d0e87b00 mov esi,.007be8d0
0x8b,0xc8, // 004b00e6 8bc8 mov ecx,eax
0x2b,0xf0 // 004b00e8 2bf0 sub esi,eax
//XX2, XX, 0x00,0x00,0x00 // 004b00ea 8d9b 00000000 lea ebx,dword ptr ds:[ebx]
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
if(addr==0)return false;
HookParam hp;
hp.address=addr+5;
hp.type=USING_STRING|EMBED_ABLE|EMBED_DYNA_SJIS;
hp.hook_before=Private::hookBefore;
hp.hook_after=Private::hookafter;
hp.hook_font=F_GetGlyphOutlineA;
hp.filter_fun=[](void* data, size_t* len, HookParam* hp){
static std::regex rx(R"(\[(.+?)/.+\])");
auto _=std::regex_replace(std::string((char*)data,*len), rx, "$1");
strcpy((char*)data,_.c_str());*len=_.size();return true;
};
static ULONG p;
p=Patch::patchEncoding(startAddress, stopAddress) ;
if(p){
hp.type|= EMBED_DYNA_SJIS;
hp.hook_font=F_GetGlyphOutlineA;
patch_fun=[](){
ReplaceFunction((PVOID*)&p, (PVOID)(ULONG)Patch::Private::isLeadByteChar);
};
}
return NewHook(hp,"EmbedCS2");
}
}
} // namespace ScenarioHook
bool CatSystem::attach_function() {
auto embed=ScenarioHook::attach(processStartAddress,processStopAddress);
return InsertCatSystemHook()||InsertCatSystem2Hook()||embed;
}

View File

@ -0,0 +1,12 @@
#include"engine.h"
class CatSystem:public ENGINE{
public:
CatSystem(){
check_by=CHECK_BY::FILE;
check_by_target=L"*.int";
is_engine_certain=false;
};
bool attach_function();
};

View File

@ -0,0 +1,49 @@
#include"Ciel.h"
bool CielFilter(LPVOID data, size_t *size, HookParam *)
{
auto text = reinterpret_cast<LPSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
if (*len == 1) return false;
//StringCharReplacer(text, len, "^n", 2, ' ');
return true;
}
bool InsertCielHook()
{
/*
* Sample games:
* https://vndb.org/r26480
* https://vndb.org/v1648
* https://vndb.org/v10392
*/
const BYTE bytes[] = {
0x50, // push eax << hook here
0xE8, XX4, // call FaultA.exe+81032
0x83, 0xC4, 0x04, // add esp,04
0x85, 0xC0, // test eax,eax
0x74, 0x32, // je FaultA.exe+41FA6
0x81, 0x7C, 0x24, 0x10, XX4 // cmp [esp+10],000003FE
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) return false;
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::edi);
hp.index = 0;
hp.type = DATA_INDIRECT;
hp.filter_fun = CielFilter;
return NewHook(hp, "Ciel");
}
bool Ciel::attach_function() {
return InsertCielHook();
}

11
LunaHook/engine32/Ciel.h Normal file
View File

@ -0,0 +1,11 @@
#include"engine.h"
class Ciel:public ENGINE{
public:
Ciel(){
check_by=CHECK_BY::FILE;
check_by_target=L"sys/kidoku.dat";
};
bool attach_function();
};

View File

@ -0,0 +1,45 @@
#include"Circus1.h"
/********************************************************************************************
CIRCUS hook:
Game folder contains advdata folder. Used by CIRCUS games.
Usually has font caching issues. But trace back from GetGlyphOutline gives a hook
which generate repetition.
If we study circus engine follow Freaka's video, we can easily discover that
in the game main module there is a static buffer, which is filled by new text before
it's drawing to screen. By setting a hardware breakpoint there we can locate the
function filling the buffer. But we don't have to set hardware breakpoint to search
the hook address if we know some characteristic instruction(cmp al,0x24) around there.
********************************************************************************************/
bool InsertCircusHook1() // jichi 10/2/2013: Change return type to bool
{
for (DWORD i = processStartAddress + 0x1000; i < processStopAddress - 4; i++)
if (*(WORD *)i == 0xa3c) //cmp al, 0xA; je
for (DWORD j = i; j < i + 0x100; j++) {
BYTE c = *(BYTE *)j;
if (c == 0xc3)
break;
if (c == 0xe8) {
DWORD k = *(DWORD *)(j+1)+j+5;
if (k > processStartAddress && k < processStopAddress) {
HookParam hp;
hp.address = k;
hp.offset=get_stack(3);
hp.split =get_reg(regs::esp);
hp.type = DATA_INDIRECT|USING_SPLIT;
ConsoleOutput("INSERT CIRCUS#1");
//RegisterEngineType(ENGINE_CIRCUS);
return NewHook(hp, "Circus1");
}
}
}
//break;
//ConsoleOutput("Unknown CIRCUS engine");
ConsoleOutput("CIRCUS1: failed");
return false;
}
bool Circus1::attach_function() {
return InsertCircusHook1();
}

View File

@ -0,0 +1,11 @@
#include"engine.h"
class Circus1:public ENGINE{
public:
Circus1(){
check_by=CHECK_BY::FILE;
check_by_target=L"AdvData\\DAT\\NAMES.DAT";
};
bool attach_function();
};

View File

@ -0,0 +1,401 @@
#include"Circus2.h"
#include"embed_util.h"
namespace{
bool filter(void* data, size_t* len, HookParam* hp){
if (strstr((char*)data,"@i")||strstr((char*)data,"@y"))return false;
//{てんきゅう/天穹}
if(strstr((char*)data,"\x81\x6f")&&strstr((char*)data,"\x81\x5e")&&strstr((char*)data,"\x81\x70")){
StringFilter((char*)data, len, "\x81\x70", 2);
StringFilterBetween((char*)data,len, "\x81\x6f", 2, "\x81\x5e", 2);
}
return true;
};
}
/**
* jichi 6/5/2014: Sample function from DC3 at 0x4201d0
* 004201ce cc int3
* 004201cf cc int3
* 004201d0 /$ 8b4c24 08 mov ecx,dword ptr ss:[esp+0x8]
* 004201d4 |. 8a01 mov al,byte ptr ds:[ecx]
* 004201d6 |. 84c0 test al,al
* 004201d8 |. 74 1c je short dc3.004201f6
* 004201da |. 8b5424 04 mov edx,dword ptr ss:[esp+0x4]
* 004201de |. 8bff mov edi,edi
* 004201e0 |> 3c 24 /cmp al,0x24
* 004201e2 |. 75 05 |jnz short dc3.004201e9
* 004201e4 |. 83c1 02 |add ecx,0x2
* 004201e7 |. eb 04 |jmp short dc3.004201ed
* 004201e9 |> 8802 |mov byte ptr ds:[edx],al
* 004201eb |. 42 |inc edx
* 004201ec |. 41 |inc ecx
* 004201ed |> 8a01 |mov al,byte ptr ds:[ecx]
* 004201ef |. 84c0 |test al,al
* 004201f1 |.^75 ed \jnz short dc3.004201e0
* 004201f3 |. 8802 mov byte ptr ds:[edx],al
* 004201f5 |. c3 retn
* 004201f6 |> 8b4424 04 mov eax,dword ptr ss:[esp+0x4]
* 004201fa |. c600 00 mov byte ptr ds:[eax],0x0
* 004201fd \. c3 retn
*/
bool InsertCircusHook2() // jichi 10/2/2013: Change return type to bool
{
for (DWORD i = processStartAddress + 0x1000; i < processStopAddress -4; i++)
if ((*(DWORD *)i & 0xffffff) == 0x75243c) { // cmp al, 24; je
if (DWORD j = SafeFindEnclosingAlignedFunction(i, 0x80)) {
HookParam hp;
hp.address = j;
hp.offset=get_stack(2);
//hp.filter_fun = CharNewLineFilter; // \n\s* is used to remove new line
hp.type = USING_STRING;
//GROWL_DWORD(hp.address); // jichi 6/5/2014: 0x4201d0 for DC3
//RegisterEngineType(ENGINE_CIRCUS);
return NewHook(hp, "Circus");
}
break;
}
//ConsoleOutput("Unknown CIRCUS engine.");
ConsoleOutput("CIRCUS: failed");
return false;
}
namespace{
bool c2(){
//D.C.III Dream Daysダ・カーポIIIドリームデイズ
auto entry=Util::FindImportEntry(processStartAddress,(DWORD)GetGlyphOutlineA);
DWORD funcaddr=0;
if(entry==0)return false;
for (auto addr : Util::SearchMemory(&entry, 4, PAGE_EXECUTE, processStartAddress, processStopAddress) ) {
DWORD _=0xCCCCCCCC;
funcaddr=reverseFindBytes((BYTE*)&_,4,addr-0x1000,addr);
//funcaddr=MemDbg::findEnclosingAlignedFunction(addr,0x1000);ConsoleOutput("%p",funcaddr);
}
if(funcaddr==0)return false;
funcaddr+=4;
HookParam hp;
hp.address = funcaddr;
hp.offset=get_stack(2);
hp.type = USING_STRING;//|EMBED_ABLE|EMBED_BEFORE_SIMPLE|EMBED_AFTER_NEW|EMBED_DYNA_SJIS;
//hp.hook_font=F_GetGlyphOutlineA;
//it will split a long to many lines
hp.filter_fun=filter;
return NewHook(hp, "Circus2");
}
}
namespace { // unnamed
// Skip leading tags such as @K and @c5
template <typename strT>
strT ltrim(strT s)
{
if (s && *s == '@')
while ((signed char)*++s > 0);
return s;
}
namespace ScenarioHook {
namespace Private {
DWORD nameReturnAddress_,
scenarioReturnAddress_;
/**
* Sample game: DC3, function: 0x4201d0
*
* IDA: sub_4201D0 proc near
* - arg_0 = dword ptr 4
* - arg_4 = dword ptr 8
*
* Observations:
* - arg1: LPVOID, pointed to unknown object
* - arg2: LPCSTR, the actual text
*
* Example runtime stack:
* 0012F15C 0040C208 RETURN to .0040C208 from .00420460
* 0012F160 0012F7CC ; jichi: unknown stck
* 0012F164 0012F174 ; jichi: text
* 0012F168 0012F6CC
* 0012F16C 0012F7CC
* 0012F170 0012F7CC
*/
void hookafter(hook_stack*s,void* data, size_t len){
auto newData =std::string((char*)data,len);
LPCSTR text = (LPCSTR)s->stack[2], // arg2
trimmedText = ltrim(text);
if (trimmedText != text)
newData.insert(0,std::string(text, trimmedText - text));
auto ss=new char[newData.size()+1];
strcpy(ss,newData.c_str());
s->stack[2] =(ULONG)ss; // reset arg2
}
bool hookBefore(hook_stack*s,void* data, size_t* len,uintptr_t*role)
{
LPCSTR text = (LPCSTR)s->stack[2], // arg2
trimmedText = ltrim(text);
if (!trimmedText || !*trimmedText)
return false;
auto retaddr = s->stack[0]; // retaddr
* role = retaddr == scenarioReturnAddress_ ? Engine::ScenarioRole :
retaddr == nameReturnAddress_ ? Engine::NameRole :
Engine::OtherRole;
//s->ebx? Engine::OtherRole : // other threads ebx is not zero
//// 004201e4 |. 83c1 02 |add ecx,0x2
//// 004201e7 |. eb 04 |jmp short dc3.004201ed
//*(BYTE *)(retaddr + 3) == 0xe9 // old name
//? Engine::NameRole : // retaddr+3 is jmp
//Engine::ScenarioRole;
std::string oldData = trimmedText;
strcpy((char*)data,oldData.c_str());
*len=oldData.size();
return true;
}
// Alternatively, using the following pattern bytes also works:
//
// 3c24750583c102eb0488024241
//
// 004201e0 |> 3c 24 /cmp al,0x24
// 004201e2 |. 75 05 |jnz short dc3.004201e9
// 004201e4 |. 83c1 02 |add ecx,0x2
// 004201e7 |. eb 04 |jmp short dc3.004201ed
// 004201e9 |> 8802 |mov byte ptr ds:[edx],al
// 004201eb |. 42 |inc edx
// 004201ec |. 41 |inc ecx
ULONG findFunctionAddress(ULONG startAddress, ULONG stopAddress) // find the function to hook
{
//return 0x4201d0; // DC3 function address
for (ULONG i = startAddress + 0x1000; i < stopAddress -4; i++)
// * 004201e0 |> 3c 24 /cmp al,0x24
// * 004201e2 |. 75 05 |jnz short dc3.004201e9
if ((*(ULONG *)i & 0xffffff) == 0x75243c) { // cmp al, 24; je
enum { range = 0x80 }; // the range is small, since it is a small function
if (ULONG addr = MemDbg::findEnclosingAlignedFunction(i, range))
return addr;
}
return 0;
}
} // namespace Private
/**
* jichi 6/5/2014: Sample function from DC3 at 0x4201d0
*
* Sample game: DC3PP
* 0042CE1E 68 E0F0B700 PUSH .00B7F0E0
* 0042CE23 A3 0C824800 MOV DWORD PTR DS:[0x48820C],EAX
* 0042CE28 E8 A352FFFF CALL .004220D0 ; jichi: name thread
* 0042CE2D C705 08024D00 01>MOV DWORD PTR DS:[0x4D0208],0x1
* 0042CE37 EB 52 JMP SHORT .0042CE8B
* 0042CE39 392D 08024D00 CMP DWORD PTR DS:[0x4D0208],EBP
* 0042CE3F 74 08 JE SHORT .0042CE49
* 0042CE41 392D 205BB900 CMP DWORD PTR DS:[0xB95B20],EBP
* 0042CE47 74 07 JE SHORT .0042CE50
* 0042CE49 C605 E0F0B700 00 MOV BYTE PTR DS:[0xB7F0E0],0x0
* 0042CE50 8D5424 40 LEA EDX,DWORD PTR SS:[ESP+0x40]
* 0042CE54 52 PUSH EDX
* 0042CE55 68 30B5BA00 PUSH .00BAB530
* 0042CE5A 892D 08024D00 MOV DWORD PTR DS:[0x4D0208],EBP
* 0042CE60 E8 6B52FFFF CALL .004220D0 ; jichi: scenario thread
* 0042CE65 C705 A0814800 FF>MOV DWORD PTR DS:[0x4881A0],-0x1
* 0042CE6F 892D 2C824800 MOV DWORD PTR DS:[0x48822C],EBP
*
* Sample game:
*
* 004201ce cc int3
* 004201cf cc int3
* 004201d0 /$ 8b4c24 08 mov ecx,dword ptr ss:[esp+0x8]
* 004201d4 |. 8a01 mov al,byte ptr ds:[ecx]
* 004201d6 |. 84c0 test al,al
* 004201d8 |. 74 1c je short dc3.004201f6
* 004201da |. 8b5424 04 mov edx,dword ptr ss:[esp+0x4]
* 004201de |. 8bff mov edi,edi
* 004201e0 |> 3c 24 /cmp al,0x24
* 004201e2 |. 75 05 |jnz short dc3.004201e9
* 004201e4 |. 83c1 02 |add ecx,0x2
* 004201e7 |. eb 04 |jmp short dc3.004201ed
* 004201e9 |> 8802 |mov byte ptr ds:[edx],al
* 004201eb |. 42 |inc edx
* 004201ec |. 41 |inc ecx
* 004201ed |> 8a01 |mov al,byte ptr ds:[ecx]
* 004201ef |. 84c0 |test al,al
* 004201f1 |.^75 ed \jnz short dc3.004201e0
* 004201f3 |. 8802 mov byte ptr ds:[edx],al
* 004201f5 |. c3 retn
* 004201f6 |> 8b4424 04 mov eax,dword ptr ss:[esp+0x4]
* 004201fa |. c600 00 mov byte ptr ds:[eax],0x0
* 004201fd \. c3 retn
*
* Sample registers:
* EAX 0012F998
* ECX 000000DB
* EDX 00000059
* EBX 00000000 ; ebx is zero for name/scenario thread
* ESP 0012F96C
* EBP 00000003
* ESI 00000025
* EDI 000000DB
* EIP 022C0000
*
* EAX 0012F174
* ECX 0012F7CC
* EDX FDFBF80C
* EBX 0012F6CC
* ESP 0012F15C
* EBP 0012F5CC
* ESI 800000DB
* EDI 00000001
* EIP 00420460 .00420460
*
* EAX 0012F174
* ECX 0012F7CC
* EDX FDFBF7DF
* EBX 0012F6CC
* ESP 0012F15C
* EBP 0012F5CC
* ESI 00000108
* EDI 00000001
* EIP 00420460 .00420460
*
* 0042DC5D 52 PUSH EDX
* 0042DC5E 68 E038AC00 PUSH .00AC38E0 ; ASCII "Ami"
* 0042DC63 E8 F827FFFF CALL .00420460 ; jichi: name thread
* 0042DC68 83C4 08 ADD ESP,0x8
* 0042DC6B E9 48000000 JMP .0042DCB8
* 0042DC70 83FD 58 CMP EBP,0x58
* 0042DC73 74 07 JE SHORT .0042DC7C
* 0042DC75 C605 E038AC00 00 MOV BYTE PTR DS:[0xAC38E0],0x0
* 0042DC7C 8D4424 20 LEA EAX,DWORD PTR SS:[ESP+0x20]
* 0042DC80 50 PUSH EAX
* 0042DC81 68 0808AF00 PUSH .00AF0808
* 0042DC86 E8 D527FFFF CALL .00420460 ; jichi: scenario thread
* 0042DC8B 83C4 08 ADD ESP,0x8
* 0042DC8E 33C0 XOR EAX,EAX
* 0042DC90 C705 D0DF4700 FF>MOV DWORD PTR DS:[0x47DFD0],-0x1
* 0042DC9A A3 0CE04700 MOV DWORD PTR DS:[0x47E00C],EAX
* 0042DC9F A3 940EB200 MOV DWORD PTR DS:[0xB20E94],EAX
* 0042DCA4 A3 2C65AC00 MOV DWORD PTR DS:[0xAC652C],EAX
* 0042DCA9 C705 50F9AC00 59>MOV DWORD PTR DS:[0xACF950],0x59
* 0042DCB3 A3 3C70AE00 MOV DWORD PTR DS:[0xAE703C],EAX
*/
bool attach(ULONG startAddress, ULONG stopAddress)
{
ULONG addr = Private::findFunctionAddress(startAddress, stopAddress);
if (!addr)
return false;
// Find the nearest two callers (distance within 100)
ULONG lastCall = 0;
auto fun = [&lastCall](ULONG call) -> bool {
// scenario: 0x42b78c
// name: 0x42b754
if (call - lastCall < 100) {
Private::scenarioReturnAddress_ = call + 5;
Private::nameReturnAddress_ = lastCall + 5;
return false; // found target
}
lastCall = call;
return true; // replace all functions
};
MemDbg::iterNearCallAddress(fun, addr, startAddress, stopAddress);
if (!Private::scenarioReturnAddress_ && lastCall) {
Private::scenarioReturnAddress_ = lastCall + 5;
}
HookParam hp;
hp.address=addr;
hp.filter_fun=filter;
hp.hook_before=Private::hookBefore;
hp.hook_after=Private::hookafter;
hp.hook_font=F_GetGlyphOutlineA;
hp.type=USING_STRING|EMBED_ABLE|NO_CONTEXT|EMBED_DYNA_SJIS;
return NewHook(hp,"EmbedCircus");
}
} // namespace ScenarioHook
} // unnamed namespace
bool InsertCircusHook3()
{
/*
* Sample games:
* https://vndb.org/v20218
*/
const BYTE bytes[] = {
0xCC, // int 3
0x81, 0xEC, XX4, // sub esp,000004E0 << hook here
0xA1, XX4, // mov eax,[DSIF.EXE+AD288]
0x33, 0xC4, // xor eax,esp
0x89, 0x84, 0x24, XX4, // mov [esp+000004DC],eax
0x8B, 0x84, 0x24, XX4, // mov eax,[esp+000004E4]
0x53, // push ebx
0x55, // push ebp
0x56, // push esi
0x8B, 0xB4, 0x24, XX4 // mov esi,[esp+000004F4]
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
return false;
}
HookParam hp;
hp.address = addr + 1;
hp.offset=get_reg(regs::esi);
hp.split = get_reg(regs::ecx);
hp.type = USING_STRING | USING_SPLIT;
return NewHook(hp, "Circus3");
}
bool CircusFilter(LPVOID data, size_t *size, HookParam *)
{
auto text = reinterpret_cast<LPSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
//ConsoleOutput("debug:Circus: -%.*s-", *len, text);
if (*len <= 1 || cpp_strnstr(text, "\\", *len) || (text[0] == '&' && text[1] == 'n'))
return false;
CharReplacer(text, len, '\n', ' ');
return true;
}
bool InsertCircusHook4()
{
/*
* Sample games:
* https://vndb.org/r46909
*/
const BYTE bytes[] = {
0x83, 0xF8, 0xFF, // cmp eax,-01 << hook here
0x0F, 0x84, XX4, // je DST.exe+1BCF0
0x8B, 0x0D, XX4 // mov ecx,[DST.exe+A41F0]
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::edx);
hp.split =get_stack(4); //arg4
hp.padding = 0x40;
hp.type = USING_STRING | USING_SPLIT;
hp.filter_fun = CircusFilter;
return NewHook(hp, "Circus4");
}
bool Circus2::attach_function() {
bool ch2=InsertCircusHook2();
bool _1= ch2||c2();
bool _2=ch2|| InsertCircusHook3() || InsertCircusHook4();
bool embed=ScenarioHook::attach(processStartAddress,processStopAddress);
return _1||embed||_2;
}

View File

@ -0,0 +1,11 @@
#include"engine.h"
class Circus2:public ENGINE{
public:
Circus2(){
check_by=CHECK_BY::FILE;
check_by_target=L"AdvData\\GRP\\NAMES.DAT";
};
bool attach_function();
};

View File

@ -0,0 +1,79 @@
#include"CodeX.h"
bool CodeXFilter(LPVOID data, size_t *size, HookParam *)
{
auto text = reinterpret_cast<LPSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
StringCharReplacer(text, len, "^n", 2, ' ');
//|晒[さら]
std::string result = std::string((char*)data,*len);
result = std::regex_replace(result, std::regex("\\|(.+?)\\[(.+?)\\]"), "$1");
*len = (result.size());
strcpy((char*)data, result.c_str());return true;
return true;
}
bool InsertCodeXHook()
{
/*
* Sample games:
* https://vndb.org/v41664
* https://vndb.org/v36122
*/
const BYTE bytes[] = {
0x83, 0xC4, 0x08, // add esp,08 << hook here
0x8D, 0x85, XX4, // lea eax,[ebp-00000218]
0x50, // push eax
0x68, XX4, // push ???????????!.exe+10A76C
0x85, 0xF6, // test esi,esi
0x74, 0x4F, // je ???????????!.exe+2A95B
0xFF, 0x15, XX4, // call dword ptr [???????????!.exe+C8140]
0x8B, 0x85, XX4 // mov eax,[ebp-00000220] << alternative hook here
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("CodeX: pattern not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::eax);
hp.index = 0;
hp.type = USING_STRING;
hp.filter_fun = CodeXFilter;
ConsoleOutput("INSERT CodeX");
return NewHook(hp, "CodeX");
}
namespace{
bool hook(){
//霞外籠逗留記
BYTE _[]={0x90,0x90,0x68,0x64,0x7B,0x4C,0x00}; //aHdL db 'hd{L',0
ULONG addr = MemDbg::findBytes(_, sizeof(_), processStartAddress, processStopAddress);
if(addr==0)return false;
addr+=2;
BYTE bytes[]={0x68,XX4};
memcpy(bytes+1,&addr,4);
auto addrs = Util::SearchMemory(bytes, sizeof(bytes), PAGE_EXECUTE, processStartAddress, processStopAddress);
bool succ=false;
for(auto adr:addrs){
adr=MemDbg::findEnclosingAlignedFunction(adr);
if(adr==0)continue;
HookParam hp;
hp.address = adr;
hp.offset=get_stack(1);
hp.type = CODEC_ANSI_BE;
succ|=NewHook(hp, "CodeX");
}
return succ;
}
}
bool CodeX::attach_function() {
return InsertCodeXHook()||hook();
}

12
LunaHook/engine32/CodeX.h Normal file
View File

@ -0,0 +1,12 @@
#include"engine.h"
class CodeX:public ENGINE{
public:
CodeX(){
check_by=CHECK_BY::FILE;
check_by_target=L"*.xfl";
is_engine_certain=false;
};
bool attach_function();
};

View File

@ -0,0 +1,667 @@
#include"Cotopha.h"
#include"embed_util.h"
#define s2_mov_ecx_edi 0xcf8b
namespace { // unnamed
namespace ScenarioHook {
namespace Private {
/**
* Sample game: 使 (old type)
*
* - Name
*
* EAX 00000000
* ECX 04A4C058
* EDX 00713FD8 .00713FD8
* EBX 17F90130
* ESP 0012EBBC
* EBP 0020C5A8
* ESI 04A4B678
* EDI 04A4C058
* EIP 005C2E20 .005C2E20
*
* 0012EBBC 0055D210 RETURN to .0055D210
* 0012EBC0 17F90130
* 0012EBC4 04A4B678
* 0012EBC8 00000000
* 0012EBCC 0020C5A8
* 0012EBD0 00000000 ; jichi: used to identify name
* 0012EBD4 00000000
* 0012EBD8 04A4B678
* 0012EBDC 00000000
* 0012EBE0 0020C5A8
* 0012EBE4 00000000
* 0012EBE8 0055C58F RETURN to .0055C58F from .0046CD30
* 0012EBEC 0012EC54
* 0012EBF0 0055C5A3 RETURN to .0055C5A3 from .0055D180
* 0012EBF4 04A4C058
* 0012EBF8 04A4B678
*
* - Scenario
*
* EAX 00000000
* ECX 04A4CC30
* EDX 00713FD8 .00713FD8
* EBX 17F90170
* ESP 0012EBBC
* EBP 00000015
* ESI 04A4C250
* EDI 04A4CC30
* EIP 005C2E20 .005C2E20
*
* 0012EBBC 0055D210 RETURN to .0055D210
* 0012EBC0 17F90170
* 0012EBC4 04A4C250
* 0012EBC8 0000001E ; jichi: old game arg3 is 1e
* 0012EBCC 00000015
* 0012EBD0 00000002
* 0012EBD4 00000002
* 0012EBD8 04A4C250
* 0012EBDC 0000001E
* 0012EBE0 00000015
* 0012EBE4 00000000
* 0012EBE8 0055C58F RETURN to .0055C58F from .0046CD30
* 0012EBEC 0012EC54
* 0012EBF0 0055C5A3 RETURN to .0055C5A3 from .0055D180
*
* Caller of the scenario/name thread:
* 0055D207 8BCF MOV ECX,EDI
* 0055D209 897C24 34 MOV DWORD PTR SS:[ESP+0x34],EDI
* 0055D20D FF52 14 CALL DWORD PTR DS:[EDX+0x14] ; jichi: called here
* 0055D210 8BCF MOV ECX,EDI ; jichi: retaddr is here
* 0055D212 894424 18 MOV DWORD PTR SS:[ESP+0x18],EAX
* 0055D216 E8 456D0600 CALL .005C3F60
* 0055D21B 33C9 XOR ECX,ECX
* 0055D21D 894424 14 MOV DWORD PTR SS:[ESP+0x14],EAX
* 0055D221 3BC1 CMP EAX,ECX
* 0055D223 76 06 JBE SHORT .0055D22B
*
* Sample game: (very old type)
*
* - Name:
*
* EAX 0A4106C0 ASCII "ゥa"
* ECX 0012F594
* EDX 0058032C ASCII "pgM"
* EBX 00000000
* ESP 0012F4F4
* EBP 00000003
* ESI 0012F618
* EDI 0012F594
* EIP 004D52B0 .004D52B0
*
* 0012F4F4 004DBFF2 RETURN to .004DBFF2
* 0012F4F8 0A4106C0 ASCII "ゥa"
* 0012F4FC 0012F698
* 0012F500 0012F618
* 0012F504 0296EA58
* 0012F508 00000000 ; jichi: used to identify name
* 0012F50C 0A40EC00
* 0012F510 00000000
* 0012F514 000000F9
* 0012F518 00005DC8
* 0012F51C 00580304 ASCII "PgM"
* 0012F520 D90A0DDD
* 0012F524 00000018
* 0012F528 00000000
*
* - Scenario:
*
* EAX 00000000
* ECX 01B69134
* EDX 0058032C ASCII "pgM"
* EBX 09E82E88
* ESP 0012F548
* EBP 00000016
* ESI 01B68A70
* EDI 01B69134
* EIP 004D52B0 .004D52B0
*
* 0012F548 004B5210 RETURN to .004B5210
* 0012F54C 09E82E88
* 0012F550 01B68A70
* 0012F554 00000018
* 0012F558 00000016
* 0012F55C 00000009
* 0012F560 01B69134
* 0012F564 01B68A70
* 0012F568 00000018
* 0012F56C 00000016
* 0012F570 00000000
* 0012F574 004B459F RETURN to .004B459F from .0040DE50
* 0012F578 0012F5E0
* 0012F57C 004B45B3 RETURN to .004B45B3 from .004B5180
* 0012F580 09E82E88
* 0012F584 00000000
* 0012F588 0012FC78
* 0012F58C 00000000
* 0012F590 01B68A70
* 0012F594 005655D0 .005655D0
* 0012F598 0057BB80 .0057BB80
* 0012F59C 0A419628
*
* Caller of the name/scenario thread
*
* 004B517D 90 NOP
* 004B517E 90 NOP
* 004B517F 90 NOP
* 004B5180 83EC 1C SUB ESP,0x1C
* 004B5183 53 PUSH EBX
* 004B5184 55 PUSH EBP
* 004B5185 8B5C24 28 MOV EBX,DWORD PTR SS:[ESP+0x28]
* 004B5189 56 PUSH ESI
* 004B518A 8BF1 MOV ESI,ECX
* 004B518C 57 PUSH EDI
* 004B518D 8B86 A0050000 MOV EAX,DWORD PTR DS:[ESI+0x5A0]
* 004B5193 85C0 TEST EAX,EAX
* 004B5195 74 63 JE SHORT .004B51FA
* 004B5197 53 PUSH EBX
* 004B5198 8D8E C4060000 LEA ECX,DWORD PTR DS:[ESI+0x6C4]
* 004B519E E8 3DFD0100 CALL .004D4EE0
* 004B51A3 8BF8 MOV EDI,EAX
* 004B51A5 8D86 D4060000 LEA EAX,DWORD PTR DS:[ESI+0x6D4]
* 004B51AB 8B8E EC060000 MOV ECX,DWORD PTR DS:[ESI+0x6EC]
* 004B51B1 8BAE F0060000 MOV EBP,DWORD PTR DS:[ESI+0x6F0]
* 004B51B7 8B10 MOV EDX,DWORD PTR DS:[EAX]
* 004B51B9 895424 1C MOV DWORD PTR SS:[ESP+0x1C],EDX
* 004B51BD 8B50 04 MOV EDX,DWORD PTR DS:[EAX+0x4]
* 004B51C0 895424 20 MOV DWORD PTR SS:[ESP+0x20],EDX
* 004B51C4 8B50 08 MOV EDX,DWORD PTR DS:[EAX+0x8]
* 004B51C7 8B40 0C MOV EAX,DWORD PTR DS:[EAX+0xC]
* 004B51CA 894424 28 MOV DWORD PTR SS:[ESP+0x28],EAX
* 004B51CE 8BC2 MOV EAX,EDX
* 004B51D0 2BC1 SUB EAX,ECX
* 004B51D2 3BF8 CMP EDI,EAX
* 004B51D4 7F 24 JG SHORT .004B51FA
* 004B51D6 83BE A0050000 03 CMP DWORD PTR DS:[ESI+0x5A0],0x3
* 004B51DD 75 0B JNZ SHORT .004B51EA
* 004B51DF 2BC7 SUB EAX,EDI
* 004B51E1 99 CDQ
* 004B51E2 2BC2 SUB EAX,EDX
* 004B51E4 D1F8 SAR EAX,1
* 004B51E6 03C8 ADD ECX,EAX
* 004B51E8 EB 04 JMP SHORT .004B51EE
* 004B51EA 2BD7 SUB EDX,EDI
* 004B51EC 8BCA MOV ECX,EDX
* 004B51EE 898E EC060000 MOV DWORD PTR DS:[ESI+0x6EC],ECX
* 004B51F4 89AE F0060000 MOV DWORD PTR DS:[ESI+0x6F0],EBP
* 004B51FA 8B96 C4060000 MOV EDX,DWORD PTR DS:[ESI+0x6C4]
* 004B5200 8DBE C4060000 LEA EDI,DWORD PTR DS:[ESI+0x6C4]
* 004B5206 53 PUSH EBX
* 004B5207 8BCF MOV ECX,EDI
* 004B5209 897C24 14 MOV DWORD PTR SS:[ESP+0x14],EDI
* 004B520D FF52 10 CALL DWORD PTR DS:[EDX+0x10] ; jichi: called here
* 004B5210 8BCF MOV ECX,EDI ; jichi: retaddr is here
* 004B5212 894424 18 MOV DWORD PTR SS:[ESP+0x18],EAX
* 004B5216 E8 85120200 CALL .004D64A0
* 004B521B 33ED XOR EBP,EBP
* 004B521D 894424 14 MOV DWORD PTR SS:[ESP+0x14],EAX
* 004B5221 3BC5 CMP EAX,EBP
* 004B5223 76 06 JBE SHORT .004B522B
* 004B5225 89AE A0050000 MOV DWORD PTR DS:[ESI+0x5A0],EBP
* 004B522B 85C0 TEST EAX,EAX
* 004B522D 896C24 30 MOV DWORD PTR SS:[ESP+0x30],EBP
* 004B5231 76 68 JBE SHORT .004B529B
* 004B5233 55 PUSH EBP
* 004B5234 8BCF MOV ECX,EDI
* 004B5236 E8 75120200 CALL .004D64B0
* 004B523B 85C0 TEST EAX,EAX
* 004B523D 74 4F JE SHORT .004B528E
* 004B523F 50 PUSH EAX
* 004B5240 8BCE MOV ECX,ESI
* 004B5242 E8 69000000 CALL .004B52B0
* 004B5247 8BD8 MOV EBX,EAX
* 004B5249 85DB TEST EBX,EBX
* 004B524B 74 41 JE SHORT .004B528E
* 004B524D 8B86 C0060000 MOV EAX,DWORD PTR DS:[ESI+0x6C0]
* 004B5253 8B8E B0060000 MOV ECX,DWORD PTR DS:[ESI+0x6B0]
* 004B5259 8BAE 30070000 MOV EBP,DWORD PTR DS:[ESI+0x730]
* 004B525F 8DBE 28070000 LEA EDI,DWORD PTR DS:[ESI+0x728]
* 004B5265 03C8 ADD ECX,EAX
* 004B5267 6A 00 PUSH 0x0
* 004B5269 8D55 01 LEA EDX,DWORD PTR SS:[EBP+0x1]
* 004B526C 898E C0060000 MOV DWORD PTR DS:[ESI+0x6C0],ECX
* 004B5272 52 PUSH EDX
* 004B5273 8BCF MOV ECX,EDI
* 004B5275 8983 C0000000 MOV DWORD PTR DS:[EBX+0xC0],EAX
* 004B527B E8 8003F8FF CALL .00435600
* 004B5280 8B47 04 MOV EAX,DWORD PTR DS:[EDI+0x4]
* 004B5283 8B7C24 10 MOV EDI,DWORD PTR SS:[ESP+0x10]
* 004B5287 891CA8 MOV DWORD PTR DS:[EAX+EBP*4],EBX
* 004B528A 8B6C24 30 MOV EBP,DWORD PTR SS:[ESP+0x30]
* 004B528E 8B4424 14 MOV EAX,DWORD PTR SS:[ESP+0x14]
* 004B5292 45 INC EBP
* 004B5293 3BE8 CMP EBP,EAX
* 004B5295 896C24 30 MOV DWORD PTR SS:[ESP+0x30],EBP
* 004B5299 ^72 98 JB SHORT .004B5233
* 004B529B 8BCF MOV ECX,EDI
* 004B529D E8 2E120200 CALL .004D64D0
* 004B52A2 8B4424 18 MOV EAX,DWORD PTR SS:[ESP+0x18]
* 004B52A6 5F POP EDI
* 004B52A7 5E POP ESI
* 004B52A8 5D POP EBP
* 004B52A9 5B POP EBX
* 004B52AA 83C4 1C ADD ESP,0x1C
* 004B52AD C2 0400 RETN 0x4
* 004B52B0 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
* 004B52B6 6A FF PUSH -0x1
* 004B52B8 68 A1F15200 PUSH .0052F1A1
* 004B52BD 50 PUSH EAX
* 004B52BE 64:8925 00000000 MOV DWORD PTR FS:[0],ESP
* 004B52C5 81EC CC000000 SUB ESP,0xCC
* 004B52CB 56 PUSH ESI
* 004B52CC 8BF1 MOV ESI,ECX
* 004B52CE 8B8C24 E0000000 MOV ECX,DWORD PTR SS:[ESP+0xE0]
* 004B52D5 57 PUSH EDI
* 004B52D6 85C9 TEST ECX,ECX
* 004B52D8 75 07 JNZ SHORT .004B52E1
* 004B52DA 33C0 XOR EAX,EAX
* 004B52DC E9 55060000 JMP .004B5936
* 004B52E1 8B79 14 MOV EDI,DWORD PTR DS:[ECX+0x14]
* 004B52E4 85FF TEST EDI,EDI
* 004B52E6 897C24 18 MOV DWORD PTR SS:[ESP+0x18],EDI
* 004B52EA 75 07 JNZ SHORT .004B52F3
* 004B52EC 33C0 XOR EAX,EAX
* 004B52EE E9 43060000 JMP .004B5936
* 004B52F3 8A86 AA060000 MOV AL,BYTE PTR DS:[ESI+0x6AA]
* 004B52F9 84C0 TEST AL,AL
* 004B52FB 74 51 JE SHORT .004B534E
* 004B52FD 8B01 MOV EAX,DWORD PTR DS:[ECX]
* 004B52FF 8D5424 08 LEA EDX,DWORD PTR SS:[ESP+0x8]
* 004B5303 52 PUSH EDX
* 004B5304 FF50 34 CALL DWORD PTR DS:[EAX+0x34]
* 004B5307 8D86 D4060000 LEA EAX,DWORD PTR DS:[ESI+0x6D4]
* 004B530D 8B8E D4060000 MOV ECX,DWORD PTR DS:[ESI+0x6D4]
* 004B5313 894C24 48 MOV DWORD PTR SS:[ESP+0x48],ECX
* 004B5317 8B50 04 MOV EDX,DWORD PTR DS:[EAX+0x4]
* 004B531A 895424 4C MOV DWORD PTR SS:[ESP+0x4C],EDX
* 004B531E 8B48 08 MOV ECX,DWORD PTR DS:[EAX+0x8]
* 004B5321 894C24 50 MOV DWORD PTR SS:[ESP+0x50],ECX
* 004B5325 8A8E 14070000 MOV CL,BYTE PTR DS:[ESI+0x714]
* 004B532B 8B40 0C MOV EAX,DWORD PTR DS:[EAX+0xC]
* 004B532E 84C9 TEST CL,CL
* 004B5330 75 0D JNZ SHORT .004B533F
* 004B5332 394424 0C CMP DWORD PTR SS:[ESP+0xC],EAX
* 004B5336 7E 16 JLE SHORT .004B534E
* 004B5338 33C0 XOR EAX,EAX
* 004B533A E9 F7050000 JMP .004B5936
*
* Sample game: (new type), 0x54bd80
* Name:
* 0012EB5C 004DACB0 RETURN to .004DACB0
* 0012EB60 05067E40
* 0012EB64 0000001E ; jichi: new game arg2 is 1e
* 0012EB68 0012ECA8
* 0012EB6C 008D3E48
* 0012EB70 004512DB RETURN to .004512DB from .00450FE0
* 0012EB74 0000001E
* 0012EB78 00000025
* 0012EB7C 0012ECA8
* 0012EB80 008D3E48
* 0012EB84 0000001E
* 0012EB88 004DA1CB RETURN to .004DA1CB from .00451280
* 0012EB8C 004DA1DF RETURN to .004DA1DF from .004DAC20 ; jichi: 004DAC20 is a better place to hook to
* 0012EB90 05067E40
* 0012EB94 5D9C7C59
* 0012EB98 00000000
* 0012EB9C 008D3E48
* 0012EBA0 00000000
* 0012EBA4 00000000
* 0012EBA8 1600C8C8
* 0012EBAC 006835B4 .006835B4
* 0012EBB0 1621BBF0 UNICODE "\h:\f;MsgFont:\s:\c;E6ADFA:\v:"
* 0012EBB4 00000025
*
* 0012EB5C 004DACB0 RETURN to .004DACB0
* 0012EB60 05000420
* 0012EB64 0000001E
* 0012EB68 0012ECA8
* 0012EB6C 008D3E48
* 0012EB70 004512DB RETURN to .004512DB from .00450FE0
* 0012EB74 0000001E
* 0012EB78 00000022
* 0012EB7C 0012ECA8
* 0012EB80 008D3E48
* 0012EB84 0000001E
* 0012EB88 004DA1CB RETURN to .004DA1CB from .00451280
* 0012EB8C 004DA1DF RETURN to .004DA1DF from .004DAC20
* 0012EB90 05000420
* 0012EB94 5D9C7C59
* 0012EB98 00000000
* 0012EB9C 008D3E48
* 0012EBA0 00000000
* 0012EBA4 00000000
* 0012EBA8 05000C90
* 0012EBAC 006835B4 .006835B4
* 0012EBB0 05000F40 UNICODE "\h:\f;MsgFont:\s:\c;DAD4FF:\v:"
* 0012EBB4 00000022
* 0012EBB8 00000034
* 0012EBBC 00000022
* 0012EBC0 FFFFFFFF
* 0012EBC4 7C00FFFF
* 0012EBC8 78000000
* 0012EBCC F8000001
* 0012EBD0 00000000
* 0012EBD4 58001384
* 0012EBD8 28000000
* 0012EBDC 28000000
* 0012EBE0 00000048
* 0012EBE4 00655A28 .00655A28
* 0012EBE8 05000420
* 0012EBEC 00000004
* 0012EBF0 00000007
* 0012EBF4 00210030
* 0012EBF8 00000000
* 0012EBFC 00DAD4FF
* 0012EC00 0012EC98
* 0012EC04 00000001
*
* EAX 0054BD80 .0054BD80
* ECX 008D4848
* EDX 0069E80C .0069E80C
* EBX 05067E40
* ESP 0012EB5C
* EBP 0012ECA8
* ESI 008D3E48
* EDI 0000001E
* EIP 0054BD80 .0054BD80
*
* 004DAC98 89AE 300A0000 MOV DWORD PTR DS:[ESI+0xA30],EBP
* 004DAC9E 8B96 000A0000 MOV EDX,DWORD PTR DS:[ESI+0xA00]
* 004DACA4 8B42 14 MOV EAX,DWORD PTR DS:[EDX+0x14]
* 004DACA7 8D8E 000A0000 LEA ECX,DWORD PTR DS:[ESI+0xA00]
* 004DACAD 53 PUSH EBX
* 004DACAE FFD0 CALL EAX ; jichi: called here
* 004DACB0 8B8E 100A0000 MOV ECX,DWORD PTR DS:[ESI+0xA10]
* 004DACB6 894424 14 MOV DWORD PTR SS:[ESP+0x14],EAX
* 004DACBA 8B41 08 MOV EAX,DWORD PTR DS:[ECX+0x8]
* 004DACBD 33FF XOR EDI,EDI
* 004DACBF 3BC7 CMP EAX,EDI
* 004DACC1 894424 10 MOV DWORD PTR SS:[ESP+0x10],EAX
*
* ecx:
* 01814848 0C E8 69 00 60 C7 F8 13 00 00 00 00 00 00 00 00 i읠ᏸ....
* 01814858 28 3E 81 01 00 00 00 00 00 00 00 00 80 01 00 00 Ɓ....ƀ. ; jichi: 810 is the width and 26 the height to paint
* 01814868 26 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 &..ÿ....
* 01814878 00 00 00 00 26 00 00 00 00 00 00 00 00 00 00 00 ..&.....
* 01814888 06 00 00 00 03 00 00 00 28 5A 65 00 98 3D 81 01 ..e㶘Ɓ
* 01814898 2C 00 00 00 43 00 00 00 00 01 01 00 BA C1 1E 77 ,.C.Ā
* 018148A8 35 FC 1C 77 20 FF 1C 77 90 16 38 0B 64 D5 68 00 h
* 018148B8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........
* 018148C8 7E 31 00 00 4C 03 00 00 00 00 00 00 00 00 00 00 .͌.....
* 018148D8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........
* 018148E8 00 00 00 00 00 00 F0 3F 00 00 00 00 00 00 F0 3F ......
* 018148F8 00 00 00 00 00 00 00 00 94 C3 67 00 00 00 00 00 ....g..
*
* 01814848 0C E8 69 00 58 EC E4 03 00 00 00 00 00 00 00 00 iϤ....
* 01814858 28 3E 81 01 00 00 00 00 00 00 00 00 80 01 00 00 Ɓ....ƀ.
* 01814868 26 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 &..ÿ....
* 01814878 00 00 00 00 26 00 00 00 00 00 00 00 00 00 00 00 ..&.....
* 01814888 06 00 00 00 03 00 00 00 28 5A 65 00 98 3D 81 01 ..e㶘Ɓ
* 01814898 2C 00 00 00 43 00 00 00 00 01 01 00 BA C1 1E 77 ,.C.Ā
* 018148A8 35 FC 1C 77 20 FF 1C 77 90 16 38 0B 64 D5 68 00 h
* 018148B8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........
* 018148C8 4B 4F 00 00 4C 03 00 00 00 00 00 00 00 00 00 00 .͌.....
* 018148D8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........
* 018148E8 00 00 00 00 00 00 F0 3F 00 00 00 00 00 00 F0 3F ......
* 018148F8 00 00 00 00 00 00 00 00 94 C3 67 00 00 00 00 00 ....g..
*
* Scenario:
* EAX 0054BD80 .0054BD80
* ECX 008D3C50
* EDX 0069E80C .0069E80C
* EBX 1621C280
* ESP 0012EB5C
* EBP 0012ECA8
* ESI 008D3250
* EDI 0000001E
* EIP 0054BD80 .0054BD80
*
* 0012EB5C 004DACB0 RETURN to .004DACB0
* 0012EB60 1621C280
* 0012EB64 0000001E
* 0012EB68 0012ECA8
* 0012EB6C 008D3250
* 0012EB70 004512DB RETURN to .004512DB from .00450FE0
* 0012EB74 0000001E
* 0012EB78 00000041
* 0012EB7C 0012ECA8
* 0012EB80 008D3250
* 0012EB84 0000001E
* 0012EB88 004DA1CB RETURN to .004DA1CB from .00451280
* 0012EB8C 004DA1DF RETURN to .004DA1DF from .004DAC20
* 0012EB90 1621C280
*
* 0012EB5C 004DACB0 RETURN to .004DACB0
* 0012EB60 050003B8
* 0012EB64 0000001E
* 0012EB68 0012ECA8
* 0012EB6C 008D3250
* 0012EB70 004512DB RETURN to .004512DB from .00450FE0
* 0012EB74 0000001E
* 0012EB78 00000034
* 0012EB7C 0012ECA8
* 0012EB80 008D3250
* 0012EB84 0000001E
* 0012EB88 004DA1CB RETURN to .004DA1CB from .00451280
* 0012EB8C 004DA1DF RETURN to .004DA1DF from .004DAC20
* 0012EB90 050003B8
* 0012EB94 5D9C7C59
* 0012EB98 00000000
* 0012EB9C 008D3250
* 0012EBA0 00000000
* 0012EBA4 00000000
* 0012EBA8 05007A68 UNICODE "38"
* 0012EBAC 006835B4 .006835B4
* 0012EBB0 0500E910 UNICODE "\h:\f;MsgFont:\s:\c;DAD4FF:\v:"
* 0012EBB4 00000034
* 0012EBB8 0000004F
* 0012EBBC 00000034
* 0012EBC0 FFFFFFFF
* 0012EBC4 7C00FFFF
* 0012EBC8 78000000
* 0012EBCC F8000001
* 0012EBD0 00000000
* 0012EBD4 58001384
* 0012EBD8 28000000
* 0012EBDC 28000000
* 0012EBE0 00000040
* 0012EBE4 00655A28 .00655A28
* 0012EBE8 050003B8
*
* ecx:
* 01813C50 0C E8 69 00 80 E9 F8 13 00 00 00 00 00 00 00 00 i....
* 01813C60 30 32 81 01 00 00 00 00 00 00 00 00 84 03 00 00 Ɓ....΄. ; jichi: 384 is the width and 76 the height to paint
* 01813C70 76 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 v..ÿ....
* 01813C80 00 00 00 00 26 00 00 00 00 00 00 00 00 00 00 00 ..&.....
* 01813C90 06 00 00 00 03 00 00 00 28 5A 65 00 A0 31 81 01 ..eㆠƁ
* 01813CA0 2C 00 00 00 43 00 00 00 00 01 01 00 BA C1 1E 77 ,.C.Ā
* 01813CB0 35 FC 1C 77 20 FF 1C 77 20 24 34 0B 64 D5 68 00 h
* 01813CC0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........
* 01813CD0 7E 31 00 00 50 03 00 00 00 00 00 00 00 00 00 00 .͐.....
* 01813CE0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........
* 01813CF0 00 00 00 00 00 00 F0 3F 00 00 00 00 00 00 F0 3F ......
*
* 01813C50 0C E8 69 00 10 C4 E4 03 00 00 00 00 00 00 00 00 i쐐Ϥ....
* 01813C60 30 32 81 01 00 00 00 00 00 00 00 00 84 03 00 00 Ɓ....΄.
* 01813C70 76 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 v..ÿ....
* 01813C80 00 00 00 00 26 00 00 00 00 00 00 00 00 00 00 00 ..&.....
* 01813C90 06 00 00 00 03 00 00 00 28 5A 65 00 A0 31 81 01 ..eㆠƁ
* 01813CA0 2C 00 00 00 43 00 00 00 00 01 01 00 BA C1 1E 77 ,.C.Ā
* 01813CB0 35 FC 1C 77 20 FF 1C 77 20 24 34 0B 64 D5 68 00 h
*/
bool attachCaller(ULONG addr);
size_t textSize_;
bool hookBefore(hook_stack*s,void* data, size_t* len,uintptr_t*role)
{
static std::wstring text_; // persistent storage, which makes this function not thread-safe
textSize_ = 0;
auto text = (LPCWSTR)s->stack[1]; // arg1
if (!text || !*text)
return false;
if (::wcscmp(text, L"----/--/-- --:--") == 0)
return false;
textSize_ = ::wcslen(text);
if (s->stack[1] == s->stack[13]) // for new games
attachCaller(s->stack[12]);
else if (s->stack[1] == s->stack[14]) // for old games
attachCaller(s->stack[13]);
//else // very old or very new games
auto retaddr = s->stack[0];
//int textStackIndex = -1;
* role = Engine::OtherRole;
if (s->stack[2] < 0x100) { // new game, this value is mostly 0x1e
//if (s->stack[1] == s->stack[13])
// textStackIndex = 13;
// 004DACA7 8D8E 000A0000 LEA ECX,DWORD PTR DS:[ESI+0xA00]
// 004DACAD 53 PUSH EBX
// 004DACAE FFD0 CALL EAX ; jichi: called here
// 004DACB0 8B8E 100A0000 MOV ECX,DWORD PTR DS:[ESI+0xA10]
// 004DACB6 894424 14 MOV DWORD PTR SS:[ESP+0x14],EAX
// 004DACBA 8B41 08 MOV EAX,DWORD PTR DS:[ECX+0x8]
// 004DACBD 33FF XOR EDI,EDI
//if (*(WORD *)retaddr == 0x8e8b) { // 004DACB0 8B8E 100A0000 MOV ECX,DWORD PTR DS:[ESI+0xA10]
*role = Engine::ScenarioRole;
enum : wchar_t { w_open = 0x3010, w_close = 0x3011 }; /* 【】 */
if (text[0] == w_open && text[::wcslen(text) - 1] == w_close)
*role = Engine::NameRole;
} else if (s->stack[3] < 0x100 // for old game
|| *(WORD *)retaddr == s2_mov_ecx_edi && *(WORD *)(retaddr - 5) == 0x52ff) { // for very old game
// Sample game: お兄ちゃん、右手の使用を禁止します! (old type)
// 0055D207 8BCF MOV ECX,EDI
// 0055D209 897C24 34 MOV DWORD PTR SS:[ESP+0x34],EDI
// 0055D20D FF52 14 CALL DWORD PTR DS:[EDX+0x14] ; jichi: called here
// 0055D210 8BCF MOV ECX,EDI ; jichi: retaddr is here
// 0055D212 894424 18 MOV DWORD PTR SS:[ESP+0x18],EAX
// Sample game: キスと魔王と紅茶 (old type)
// name:
// 004DBFEC 50 PUSH EAX
// 004DBFED 8BCF MOV ECX,EDI
// 004DBFEF FF52 10 CALL DWORD PTR DS:[EDX+0x10] ; jichi: called here
// 004DBFF2 8B7424 7C MOV ESI,DWORD PTR SS:[ESP+0x7C]
// 004DBFF6 33DB XOR EBX,EBX
// 004DBFF8 3BF3 CMP ESI,EBX
// 004DBFFA 74 4B JE SHORT .004DC047
// 004DBFFC 8BCF MOV ECX,EDI
// 004DBFFE E8 9DA4FFFF CALL .004D64A0
// 004DC003 8BE8 MOV EBP,EAX
// 004DC005 891E MOV DWORD PTR DS:[ESI],EBX
// 004DC007 85ED TEST EBP,EBP
//
// Scenario:
// 004B5207 8BCF MOV ECX,EDI
// 004B5209 897C24 14 MOV DWORD PTR SS:[ESP+0x14],EDI
// 004B520D FF52 10 CALL DWORD PTR DS:[EDX+0x10] ; jichi: called here
// 004B5210 8BCF MOV ECX,EDI
// 004B5212 894424 18 MOV DWORD PTR SS:[ESP+0x18],EAX
// 004B5216 E8 85120200 CALL .004D64A0
// 004B521B 33ED XOR EBP,EBP
*role = s->stack[5] == 0 ? Engine::NameRole : Engine::ScenarioRole;
}
wcscpy((LPWSTR)data,text);
*len=wcslen(text)*2;
return true;
}
bool hookAfterCaller(hook_stack*s,void* data, size_t* len,uintptr_t*role)
{
if (textSize_)
s->eax = textSize_;
return false;
}
bool attachCaller(ULONG addr)
{
static std::unordered_set<ULONG> addresses_;
if (addresses_.find(addr) != addresses_.end())
return false;
addresses_.insert(addr);
HookParam hp;
hp.type=HOOK_EMPTY|EMBED_ABLE;
hp.hook_before=hookAfterCaller;
return true;
}
} // namespace Private
} // namespace ScenarioHook
} // unnamed namespace
bool InsertCotophaHook1()
{
enum : DWORD { ins = 0xec8b55 }; // mov ebp,esp, sub esp,* ; jichi 7/12/2014
ULONG addr = MemDbg::findCallerAddress((ULONG)::GetTextMetricsA, ins, processStartAddress, processStopAddress);
if (!addr) {
ConsoleOutput("Cotopha: pattern not exist");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_stack(1);
hp.split = get_reg(regs::ebp);
hp.type = CODEC_UTF16|USING_SPLIT|USING_STRING|EMBED_ABLE|EMBED_AFTER_NEW;
hp.hook_before=ScenarioHook::Private::hookBefore;
ConsoleOutput("INSERT Cotopha");
//RegisterEngineType(ENGINE_COTOPHA);
return NewHook(hp, "Cotopha");
}
bool InsertCotophaHook2()
{
if (void* addr = GetProcAddress(GetModuleHandleW(NULL), "eslHeapFree"))
{
HookParam hp;
hp.address = (uintptr_t)addr;
hp.offset=get_stack(2);
hp.type = CODEC_UTF16 | USING_STRING;
hp.filter_fun = [](void* data, size_t* len, HookParam*)
{
if(*len > VNR_TEXT_CAPACITY*2)return false;
return std::wstring_view((wchar_t*)data, *len / sizeof(wchar_t)).find(L'\\') != std::wstring_view::npos;
};
ConsoleOutput("INSERT Cotopha 2");
return NewHook(hp, "Cotopha2");
}
return false;
}
bool InsertCotophaHook3() {
const BYTE bytes[] = { 0x8B,0x75,0xB8,0x8B,0xCE,0x50,0xC6,0x45,0xFC,0x01,0xE8 };
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("Cotopha3: Cotopha3 not found");
return false;
}
HookParam myhp;
myhp.address = addr;
myhp.type = CODEC_UTF16 | USING_STRING | NO_CONTEXT;
myhp.offset=get_reg(regs::eax);
char nameForUser[HOOK_NAME_SIZE] = "Cotopha3_EWideString";
return NewHook(myhp, nameForUser);
}
bool InsertCotophaHook()
{
InsertCotophaHook1();
return InsertCotophaHook3() || InsertCotophaHook2();
}
bool Cotopha::attach_function() {
return InsertCotophaHook();
}

View File

@ -0,0 +1,11 @@
#include"engine.h"
class Cotopha:public ENGINE{
public:
Cotopha(){
check_by=CHECK_BY::FILE_ANY;
check_by_target=check_by_list{L"*.noa",L"data\\*.noa"};
};
bool attach_function();
};

View File

@ -0,0 +1,176 @@
#include"Debonosu.h"
namespace { // unnamed
int _type;
void SpecialHookDebonosuScenario(hook_stack* stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t *len)
{
DWORD retn = stack->retaddr;
if (*(WORD *)retn == 0xc483){ // add esp, $ old Debonosu game
hp->offset = get_stack(1);
_type=1;
}
else{ // new Debonosu game
hp->offset = get_reg(regs::eax);
_type=2;
}
//hp->type ^= EXTERN_HOOK;
hp->text_fun = nullptr;
*data = *(DWORD*)(stack->base + hp->offset);
*len = ::strlen((char*)*data);
*split = FIXED_SPLIT_VALUE;
}
void hook_after(hook_stack*s,void* data, size_t len){
static std::string ts;
ts=std::string((LPSTR)data,len);
if(_type==1){
s->stack[1]=(DWORD)ts.c_str();
}
else{
s->ecx=(DWORD)ts.c_str();
}
}
bool InsertDebonosuScenarioHook()
{
DWORD addr = Util::FindImportEntry(processStartAddress, (DWORD)lstrcatA);
if (!addr) {
ConsoleOutput("Debonosu: lstrcatA is not called");
return false;
}
DWORD search = 0x15ff | (addr << 16); // jichi 10/20/2014: call dword ptr ds
addr >>= 16;
for (DWORD i = processStartAddress; i < processStopAddress - 4; i++)
if (*(DWORD *)i == search &&
*(WORD *)(i + 4) == addr && // call dword ptr lstrcatA
*(BYTE *)(i - 5) == 0x68) { // push $
DWORD push = *(DWORD *)(i - 4);
for (DWORD j = i + 6, k = j + 0x10; j < k; j++)
if (*(BYTE *)j == 0xb8 &&
*(DWORD *)(j + 1) == push)
if (DWORD hook_addr = SafeFindEnclosingAlignedFunction(i, 0x200)) {
HookParam hp;
hp.address = hook_addr;
hp.text_fun = SpecialHookDebonosuScenario;
//hp.type = USING_STRING;
hp.hook_after=hook_after;
hp.hook_font=F_MultiByteToWideChar|F_GetTextExtentPoint32A;
hp.type = USING_STRING|NO_CONTEXT|USING_SPLIT|FIXING_SPLIT|EMBED_ABLE|EMBED_BEFORE_SIMPLE|EMBED_DYNA_SJIS; // there is only one thread
hp.filter_fun=[](void* data, size_t* len, HookParam* hp){
auto text = reinterpret_cast<LPSTR>(data);
std::string str = text;
str = str.substr(0, *len);
std::regex reg1("\\{(.*?)/(.*?)\\}");
std::string result1 = std::regex_replace(str, reg1, "$1");
*len = result1.size();
strcpy(text, result1.c_str());
return true;
};
ConsoleOutput("INSERT Debonosu");
return NewHook(hp, "Debonosu");
}
}
ConsoleOutput("Debonosu: failed");
//ConsoleOutput("Unknown Debonosu engine.");
return false;
}
void SpecialHookDebonosuName(hook_stack* stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t *len)
{
DWORD text = stack->ecx;
if (!text)
return;
*data = text;
*len = ::strlen((LPCSTR)text);
*split = FIXED_SPLIT_VALUE << 1;
}
bool InsertDebonosuNameHook()
{
const BYTE bytes[] = {
// 0032f659 32c0 xor al,al
// 0032f65b 5b pop ebx
// 0032f65c 8be5 mov esp,ebp
// 0032f65e 5d pop ebp
// 0032f65f c3 retn
0x55, // 0032f660 55 push ebp ; jichi: name text in ecx, which could be zero though
0x8b,0xec, // 0032f661 8bec mov ebp,esp
0x81,0xec, XX4, // 0032f663 81ec 2c080000 sub esp,0x82c
0x8b,0x45, 0x08, // 0032f669 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
0x53, // 0032f66c 53 push ebx
0x56, // 0032f66d 56 push esi
0x8b,0xf1, // 0032f66e 8bf1 mov esi,ecx
0x85,0xc0, // 0032f670 85c0 test eax,eax
0x8d,0x4d, 0xf0, // 0032f672 8d4d f0 lea ecx,dword ptr ss:[ebp-0x10]
0x0f,0x45,0xc8, // 0032f675 0f45c8 cmovne ecx,eax
0x57 // 0032f678 57 push edi
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (!addr) {
ConsoleOutput("DebonosuName: pattern NOT FOUND");
return false;
}
HookParam hp;
hp.address = addr;
//hp.text_fun = SpecialHookDebonosuName;
hp.offset=get_reg(regs::ecx);
//hp.type = USING_STRING;
hp.type = USING_STRING|NO_CONTEXT|USING_SPLIT|EMBED_ABLE|EMBED_BEFORE_SIMPLE|EMBED_AFTER_NEW; //|FIXING_SPLIT; // there is only one thread
ConsoleOutput("INSERT DebonosuName");
return NewHook(hp, "DebonosuName");
}
} // unnamed namespace
bool attach(ULONG startAddress, ULONG stopAddress)
{
ULONG addr = 0;
{
const char *msg = "D3DFont::Draw";
if (addr = MemDbg::findBytes(msg, ::strlen(msg+1), startAddress, stopAddress))
addr = MemDbg::findPushAddress(addr, startAddress, stopAddress);
}
if (!addr) {
const uint8_t bytes[] = {
0x50, // 0010fb80 50 push eax
0xff,0x75, 0x14, // 0010fb81 ff75 14 push dword ptr ss:[ebp+0x14]
0x8b,0xce, // 0010fb84 8bce mov ecx,esi
0xff,0x75, 0x10 // 0010fb86 ff75 10 push dword ptr ss:[ebp+0x10]
};
addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
}
if (!addr) {
return false;
}
//addr = MemDbg::findEnclosingAlignedFunction(addr); // This might not work as the address is not always aligned
addr = MemDbg::findEnclosingFunctionAfterInt3(addr);
if (!addr) {
return false;
}
HookParam hp;
hp.address = addr;
//hp.text_fun = SpecialHookDebonosuName;
hp.offset=20;
//hp.type = USING_STRING;
hp.type = USING_STRING|NO_CONTEXT; //|FIXING_SPLIT; // there is only one thread
return NewHook(hp, "Debonosu2");
}
bool InsertDebonosuHook()
{
bool ok = InsertDebonosuScenarioHook();
if (ok)
InsertDebonosuNameHook();
return ok;
}
bool Debonosu::attach_function() {
// 1/1/2016 jich: skip izumo4 from studio ego that is not supported by debonosu
if (Util::CheckFile(L"*izumo4*.exe")) {
PcHooks::hookOtherPcFunctions();
return true;
}
return InsertDebonosuHook();
}

View File

@ -0,0 +1,16 @@
#include"engine.h"
class Debonosu:public ENGINE{
public:
Debonosu(){
check_by=CHECK_BY::CUSTOM;
check_by_target=[](){
//神楽創世記-久遠-
//官方中英版bmp.pak在语言目录里。
auto paks={L"bmp.pak",L"EN\\bmp.pak",L"ZHCN\\bmp.pak",L"ZHTW\\bmp.pak"};
return (std::any_of(paks.begin(),paks.end(),Util::CheckFile) && Util::CheckFile(L"dsetup.dll"))||(Util::SearchResourceString(L"でぼの巣製作所"));
};
};
bool attach_function();
};

View File

@ -0,0 +1,50 @@
#include"DxLib.h"
bool DxLibFilter(LPVOID data, size_t* size, HookParam*)
{
auto text = reinterpret_cast<LPSTR>(data);
auto len = reinterpret_cast<size_t*>(size);
StringCharReplacer(text, len, "%N", 2, ' ');
StringFilter(text, len, "%K", 2);
StringFilter(text, len, "%P", 2);
return true;
}
bool InsertDxLibHook()
{
/*
* Sample games:
* https://vndb.org/v7849
* https://vndb.org/v10231
*/
const BYTE bytes[] = {
0xF7, 0xC6, XX4, // test esi,00000003 << hook here
0x75, XX, // jne BookofShadows.exe+15FE54
0x8B, 0xD9, // mov ebx,ecx
0xC1, 0xE9, 0x02, // shr ecx,02
0x75, XX, // jne BookofShadows.exe+15FEAE
0xEB, XX // jmp BookofShadows.exe+15FE76
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("DxLib: pattern not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset =get_reg(regs::esi);
hp.type = USING_STRING;
hp.filter_fun = DxLibFilter;
ConsoleOutput(" INSERT DxLib");
return NewHook(hp, "DxLib");
}
bool DxLib::attach_function() {
return InsertDxLibHook();
}

12
LunaHook/engine32/DxLib.h Normal file
View File

@ -0,0 +1,12 @@
#include"engine.h"
class DxLib:public ENGINE{
public:
DxLib(){
check_by=CHECK_BY::FILE;
check_by_target=L"*.bcx";
is_engine_certain=false;
};
bool attach_function();
};

41
LunaHook/engine32/EME.cpp Normal file
View File

@ -0,0 +1,41 @@
#include"EME.h"
/********************************************************************************************
EMEHook hook: (Contributed by Freaka)
EmonEngine is used by LoveJuice company and TakeOut. Earlier builds were apparently
called Runrun engine. String parsing varies a lot depending on the font settings and
speed setting. E.g. without antialiasing (which very early versions did not have)
uses TextOutA, fast speed triggers different functions then slow/normal. The user can
set his own name and some odd control characters are used (0x09 for line break, 0x0D
for paragraph end) which is parsed and put together on-the-fly while playing so script
can't be read directly.
********************************************************************************************/
bool InsertEMEHook()
{
ULONG addr = MemDbg::findCallAddress((ULONG)::IsDBCSLeadByte, processStartAddress, processStopAddress);
// no needed as first call to IsDBCSLeadByte is correct, but sig could be used for further verification
//WORD sig = 0x51C3;
//while (c && (*(WORD*)(c-2)!=sig))
//{
// //-0x1000 as FindCallOrJmpAbs always uses an offset of 0x1000
// c = Util::FindCallOrJmpAbs((DWORD)IsDBCSLeadByte,processStopAddress-c-0x1000+4,c-0x1000+4,false);
//}
if (!addr) {
ConsoleOutput("EME: pattern does not exist");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::eax);
hp.type = NO_CONTEXT|DATA_INDIRECT|USING_STRING;
ConsoleOutput("INSERT EmonEngine");
//ConsoleOutput("EmonEngine, hook will only work with text speed set to slow or normal!");
//else ConsoleOutput("Unknown EmonEngine engine");
return NewHook(hp, "EmonEngine");
}
bool EME::attach_function() {
return InsertEMEHook();
}

11
LunaHook/engine32/EME.h Normal file
View File

@ -0,0 +1,11 @@
#include"engine.h"
class EME:public ENGINE{
public:
EME(){
check_by=CHECK_BY::FILE;
check_by_target=L"emecfg.ecf";
};
bool attach_function();
};

View File

@ -0,0 +1,31 @@
#include"Eagls.h"
/** jichi 7/26/2014: E.A.G.L.S engine for TechArts games (SQUEEZ, May-Be Soft)
* Sample games: [May-Be Soft] <EFBFBD><EFBFBD> * Should also work for SQUEEZ's
*
* Two functions calls to GetGlyphOutlineA are responsible for painting.
* - 0x4094ef
* - 0x409e35
* However, by default, one of the thread is like: scenario namename scenario
* The other thread have infinite loop.
*/
bool InsertEaglsHook()
{
// Modify the split for GetGlyphOutlineA
HookParam hp;
hp.address = (DWORD)::GetGlyphOutlineA;
hp.type = CODEC_ANSI_BE|USING_SPLIT; // the only difference is the split value
hp.offset = get_stack(2);
hp.split = get_stack(4);
//hp.split = arg7_lpmat2;
ConsoleOutput("INSERT EAGLS");
return NewHook(hp, "EAGLS");
}
bool Eagls::attach_function() {
return InsertEaglsHook();
}

11
LunaHook/engine32/Eagls.h Normal file
View File

@ -0,0 +1,11 @@
#include"engine.h"
class Eagls:public ENGINE{
public:
Eagls(){
check_by=CHECK_BY::FILE;
check_by_target=L"EAGLS.dll";
};
bool attach_function();
};

404
LunaHook/engine32/Elf.cpp Normal file
View File

@ -0,0 +1,404 @@
#include"Elf.h"
/**
* jichi 6/1/2014:
* Observations from 4
* - Scenario: arg1 + 4*5 is 0, arg1+0xc is address of the text
* - Character: arg1 + 4*10 is 0, arg1+0xc is text
*/
static inline size_t _elf_strlen(LPCSTR p) // limit search address which might be bad
{
//CC_ASSERT(p);
for (size_t i = 0; i < VNR_TEXT_CAPACITY; i++)
if (!*p++)
return i;
return 0; // when len >= VNR_TEXT_CAPACITY
}
static void SpecialHookElf(hook_stack* stack, HookParam *, uintptr_t *data, uintptr_t *split, size_t *len)
{
//DWORD arg1 = *(DWORD *)(esp_base + 0x4);
DWORD arg1 = stack->stack[1];
DWORD arg2_scene = arg1 + 4*5,
arg2_chara = arg1 + 4*10;
DWORD text; //= 0; // This variable will be killed
if (*(DWORD *)arg2_scene == 0) {
text = *(DWORD *)(arg2_scene + 4*3);
if (!text || ::IsBadReadPtr((LPCVOID)text, 1)) // Text from scenario could be bad when open backlog while the character is speaking
return;
*split = 1;
} else if (*(DWORD *)arg2_chara == 0) {
text = arg2_chara + 4*3;
*split = 2;
} else
return;
//if (text && text < MemDbg::UserMemoryStopAddress) {
*len = _elf_strlen((LPCSTR)text); // in case the text is bad but still readable
//*len = ::strlen((LPCSTR)text);
*data = text;
}
/**
* jichi 5/31/2014: elf's
* Type1: SEXヂ<EFBFBD> trial, reladdr = 0x2f0f0, 2 parameters
* Type2: 4, reladdr = 0x2f9b0, 3 parameters
*
* IDA: sub_42F9B0 proc near ; bp-based frame
* var_8 = dword ptr -8
* var_4 = byte ptr -4
* var_3 = word ptr -3
* arg_0 = dword ptr 8
* arg_4 = dword ptr 0Ch
* arg_8 = dword ptr 10h
*
* Call graph (Type2):
* 0x2f9b0 ; hook here
* > 0x666a0 ; called multiple time
* > TextOutA ; there are two TextOutA, the second is the right one
*
* Function starts (Type1), pattern offset: 0xc
* - 012ef0f0 /$ 55 push ebp ; jichi: hook
* - 012ef0f1 |. 8bec mov ebp,esp
* - 012ef0f3 |. 83ec 10 sub esp,0x10
* - 012ef0f6 |. 837d 0c 00 cmp dword ptr ss:[ebp+0xc],0x0
* - 012ef0fa |. 53 push ebx
* - 012ef0fb |. 56 push esi
* - 012ef0fc |. 75 0f jnz short stt_tria.012ef10d ; jicchi: pattern starts
* - 012ef0fe |. 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
* - 012ef101 |. 8b48 04 mov ecx,dword ptr ds:[eax+0x4]
* - 012ef104 |. 8b91 90000000 mov edx,dword ptr ds:[ecx+0x90] ; jichi: pattern stops
* - 012ef10a |. 8955 0c mov dword ptr ss:[ebp+0xc],edx
* - 012ef10d |> 8b4d 08 mov ecx,dword ptr ss:[ebp+0x8]
* - 012ef110 |. 8b51 04 mov edx,dword ptr ds:[ecx+0x4]
* - 012ef113 |. 33c0 xor eax,eax
* - 012ef115 |. c645 f8 00 mov byte ptr ss:[ebp-0x8],0x0
* - 012ef119 |. 66:8945 f9 mov word ptr ss:[ebp-0x7],ax
* - 012ef11d |. 8b82 b0000000 mov eax,dword ptr ds:[edx+0xb0]
* - 012ef123 |. 8945 f4 mov dword ptr ss:[ebp-0xc],eax
* - 012ef126 |. 33db xor ebx,ebx
* - 012ef128 |> 8b4f 20 /mov ecx,dword ptr ds:[edi+0x20]
* - 012ef12b |. 83f9 10 |cmp ecx,0x10
*
* Function starts (Type2), pattern offset: 0x10
* - 0093f9b0 /$ 55 push ebp ; jichi: hook here
* - 0093f9b1 |. 8bec mov ebp,esp
* - 0093f9b3 |. 83ec 08 sub esp,0x8
* - 0093f9b6 |. 837d 10 00 cmp dword ptr ss:[ebp+0x10],0x0
* - 0093f9ba |. 53 push ebx
* - 0093f9bb |. 8b5d 0c mov ebx,dword ptr ss:[ebp+0xc]
* - 0093f9be |. 56 push esi
* - 0093f9bf |. 57 push edi
* - 0093f9c0 |. 75 0f jnz short silkys.0093f9d1 ; jichi: pattern starts
* - 0093f9c2 |. 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
* - 0093f9c5 |. 8b48 04 mov ecx,dword ptr ds:[eax+0x4]
* - 0093f9c8 |. 8b91 90000000 mov edx,dword ptr ds:[ecx+0x90] ; jichi: pattern stops
* - 0093f9ce |. 8955 10 mov dword ptr ss:[ebp+0x10],edx
* - 0093f9d1 |> 33c0 xor eax,eax
* - 0093f9d3 |. c645 fc 00 mov byte ptr ss:[ebp-0x4],0x0
* - 0093f9d7 |. 66:8945 fd mov word ptr ss:[ebp-0x3],ax
* - 0093f9db |. 33ff xor edi,edi
* - 0093f9dd |> 8b53 20 /mov edx,dword ptr ds:[ebx+0x20]
* - 0093f9e0 |. 8d4b 0c |lea ecx,dword ptr ds:[ebx+0xc]
* - 0093f9e3 |. 83fa 10 |cmp edx,0x10
*/
bool InsertElfHook()
{
const BYTE bytes[] = {
//0x55, // 0093f9b0 /$ 55 push ebp ; jichi: hook here
//0x8b,0xec, // 0093f9b1 |. 8bec mov ebp,esp
//0x83,0xec, 0x08, // 0093f9b3 |. 83ec 08 sub esp,0x8
//0x83,0x7d, 0x10, 0x00, // 0093f9b6 |. 837d 10 00 cmp dword ptr ss:[ebp+0x10],0x0
//0x53, // 0093f9ba |. 53 push ebx
//0x8b,0x5d, 0x0c, // 0093f9bb |. 8b5d 0c mov ebx,dword ptr ss:[ebp+0xc]
//0x56, // 0093f9be |. 56 push esi
//0x57, // 0093f9bf |. 57 push edi
0x75, 0x0f, // 0093f9c0 |. 75 0f jnz short silkys.0093f9d1
0x8b,0x45, 0x08, // 0093f9c2 |. 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
0x8b,0x48, 0x04, // 0093f9c5 |. 8b48 04 mov ecx,dword ptr ds:[eax+0x4]
0x8b,0x91, 0x90,0x00,0x00,0x00 // 0093f9c8 |. 8b91 90000000 mov edx,dword ptr ds:[ecx+0x90]
};
//enum { addr_offset = 0xc };
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
//GROWL_DWORD(addr);
//addr = 0x42f170; // 愛姉妹4 Trial
//reladdr = 0x2f9b0; // 愛姉妹4
//reladdr = 0x2f0f0; // SEXヂ<58>ーチャー剛史 trial
if (!addr) {
ConsoleOutput("Elf: pattern not found");
return false;
}
enum : BYTE { push_ebp = 0x55 };
for (int i = 0; i < 0x20; i++, addr--) // value of i is supposed to be 0xc or 0x10
if (*(BYTE *)addr == push_ebp) { // beginning of the function
HookParam hp;
hp.address = addr;
hp.text_fun = SpecialHookElf;
hp.type = USING_STRING|NO_CONTEXT; // = 9
ConsoleOutput("INSERT Elf");
return NewHook(hp, "Elf");
}
ConsoleOutput("Elf: function not found");
return false;
}
namespace{
bool __(){
const BYTE bytes[] = {
//姫騎士オリヴィア ~へ、変態、この変態男!少しは恥を知りなさい!
//女系家族III秘密HIMITSU卑蜜
//ベロちゅー!~コスプレメイドをエロメロしちゃう魔法の舌戯~
0x0F,0xB7,XX,XX4, //v11 == 30081 // movzx edx, ds:word_4C285C //word_4C285C dw 7581h
};
for (auto addr : Util::SearchMemory(bytes, sizeof(bytes), PAGE_EXECUTE, processStartAddress, processStopAddress)) {
BYTE reg=*(BYTE*)(addr+2);
if((reg!=0x05)&&(reg!=0x0d)&&(reg!=0x1d)&&(reg!=0x15))continue;
int word_4C285C_addr=*(int*)(addr+3);
if(word_4C285C_addr<processStartAddress || word_4C285C_addr>processStopAddress)continue;
int word_4C285C=*(int*)word_4C285C_addr;
if((word_4C285C)!=0x7581)continue;
addr = findfuncstart(addr, 0x200);
if (addr == 0)continue;
HookParam hp;
hp.address = addr;
hp.offset=get_stack(1);
hp.type = USING_STRING;
return NewHook(hp, "aiwin6");
}
return false;
}
}
#include"embed_util.h"
namespace { // unnamed
namespace ScenarioHook {
namespace Private {
struct TextArgument
{
DWORD _unknown1[5];
DWORD scenarioFlag; // +4*5, 0 if it is scenario
DWORD _unknown2[2];
LPCSTR scenarioText; // +4*5+4*3, could be bad address though
DWORD _unknown3;
DWORD nameFlag; // +4*10, 0 if it is name
DWORD _unknown4[2];
char nameText[1]; // +4*10+4*3, could be bad address though
};
std::string data_;
TextArgument *scenarioArg_,
*nameArg_;
LPCSTR scenarioText_;
enum { MaxNameSize = 100 };
char nameText_[MaxNameSize + 1];
bool hookBefore(hook_stack*s,void* data, size_t* len,uintptr_t*role)
{
auto arg = (TextArgument *)s->stack[0]; // arg1 on the top of the stack
// Scenario
if (arg->scenarioFlag == 0) {
* role = Engine::ScenarioRole ;
// Text from scenario could be bad when open backlog while the character is speaking
auto text = arg->scenarioText;
if (!Engine::isAddressReadable(text))
return 0;
strcpy((LPSTR)data,text);*len=strlen(text);return 1;
// data_ = q->dispatchTextASTD(text, role, sig);
// scenarioArg_ = arg;
// scenarioText_ = arg->scenarioText;
// arg->scenarioText = (LPCSTR)data_.c_str();
} else if (arg->nameFlag == 0) {
* role = Engine::NameRole;
auto text = arg->nameText;
strcpy((LPSTR)data,text);*len=strlen(text);return 1;
// ::memcpy(text, newData.constData(), qMin(oldData.size(), newData.size()));
//int left = oldData.size() - newData.size();
//if (left > 0)
// ::memset(text + oldData.size() - left, 0, left);
}
return 0;
}
void hookafter1(hook_stack*s,void* data1, size_t len){
auto newData=std::string((char*)data1,len);
auto arg = (TextArgument *)s->stack[0]; // arg1 on the top of the stack
// Scenario
if (arg->scenarioFlag == 0) {
auto text = arg->scenarioText;
if (!Engine::isAddressReadable(text))
return ;
data_ = newData;
scenarioArg_ = arg;
scenarioText_ = arg->scenarioText;
arg->scenarioText = (LPCSTR)data_.c_str();
} else if (arg->nameFlag == 0) {
auto text = arg->nameText;
std::string oldData=text;
::memcpy(text, newData.c_str(), min(oldData.size(), newData.size()));
int left = oldData.size() - newData.size();
if (left > 0)
::memset(text + oldData.size() - left, 0, left);
}
}
bool hookAfter(hook_stack*s,void* data, size_t* len,uintptr_t*role)
{
if (scenarioArg_) {
scenarioArg_->scenarioText = scenarioText_;
scenarioArg_ = nullptr;
}
if (nameArg_) {
::strcpy(nameArg_->nameText, nameText_);
nameArg_ = nullptr;
}
return 0;
}
} // namespace Private
/**
* jichi 5/31/2014: elf's
* Type1: SEXティーチャー剛史 trial, reladdr = 0x2f0f0, 2 parameters
* Type2: 4, reladdr = 0x2f9b0, 3 parameters
*
* The hooked function is the caller of the caller of TextOutA.
*/
bool attach(ULONG startAddress, ULONG stopAddress)
{
const uint8_t bytes[] = {
//0x55, // 0093f9b0 /$ 55 push ebp ; jichi: hook here
//0x8b,0xec, // 0093f9b1 |. 8bec mov ebp,esp
//0x83,0xec, 0x08, // 0093f9b3 |. 83ec 08 sub esp,0x8
//0x83,0x7d, 0x10, 0x00, // 0093f9b6 |. 837d 10 00 cmp dword ptr ss:[ebp+0x10],0x0
//0x53, // 0093f9ba |. 53 push ebx
//0x8b,0x5d, 0x0c, // 0093f9bb |. 8b5d 0c mov ebx,dword ptr ss:[ebp+0xc]
//0x56, // 0093f9be |. 56 push esi
//0x57, // 0093f9bf |. 57 push edi
0x75, 0x0f, // 0093f9c0 |. 75 0f jnz short silkys.0093f9d1
0x8b,0x45, 0x08, // 0093f9c2 |. 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
0x8b,0x48, 0x04, // 0093f9c5 |. 8b48 04 mov ecx,dword ptr ds:[eax+0x4]
0x8b,0x91, 0x90,0x00,0x00,0x00 // 0093f9c8 |. 8b91 90000000 mov edx,dword ptr ds:[ecx+0x90]
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
if (!addr)
return false;
addr = MemDbg::findEnclosingAlignedFunction(addr);
if (!addr)
return false;
int count = 0;
auto fun = [&count](ULONG addr) -> bool {
bool succ=false;
HookParam hp;
hp.address=addr;
hp.hook_before=Private::hookBefore;
hp.hook_after=Private::hookafter1;
hp.type=USING_STRING|EMBED_ABLE|EMBED_DYNA_SJIS;
hp.hook_font=F_TextOutA;
succ|=NewHook(hp,"EmbedElf");
hp.address=addr+5;
hp.hook_before=Private::hookAfter;
succ|=NewHook(hp,"EmbedElf");
count+=1;
return succ; // replace all functions
};
MemDbg::iterNearCallAddress(fun, addr, startAddress, stopAddress);
return count;
//lastCaller = MemDbg::findEnclosingAlignedFunction(lastCaller);
//Private::attached_ = false;
//return winhook::hook_before(lastCaller, [=](winhook::hook_stack *s) -> bool {
// if (Private::attached_)
// return true;
// Private::attached_ = true;
// if (ULONG addr = MemDbg::findEnclosingAlignedFunction(s->stack[0])) {
// DOUT("dynamic pattern found");
// Private::oldHookFun = (Private::hook_fun_t)winhook::replace_fun(addr, (ULONG)Private::newHookFun);
// }
// return true;
//});
}
} // namespace ScenarioHook
} // unnamed namespace
bool Elf::attach_function() {
auto _1= InsertElfHook()||__();
return ScenarioHook::attach(processStartAddress,processStopAddress)||_1;
}
bool isshiftjisX(WORD w){
return (((BYTE)(w))<=0xfc)&& (((BYTE)(w))>=0x80);
}
void SpecialHookElf2(hook_stack* stack, HookParam *, uintptr_t *data, uintptr_t *split, size_t *len)
{
static DWORD lasttext;
DWORD eax = stack->eax;
DWORD edx = stack->edx;
*data = *(WORD*)(eax+edx);
if(isshiftjisX(*data)==false){
*len=0;
return;
}
*len = 2;
*split=stack->stack[1];
}
bool Elf2attach_function() {
//这个有好多乱码
//[エルフ]あしたの雪之丞 DVD Special Edition
const uint8_t bytes[] = {
0x53,
0x8a,0x1c,0x02,
0x8b,0x54,0x24,0x08,
0x03,0xc2
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (!addr)
return false;
HookParam hp;
hp.address=addr+1;
hp.text_fun = SpecialHookElf2;
hp.type=NO_CONTEXT;
return NewHook(hp,"Elf");
}
bool elf2(){
//[エルフ]あしたの雪之丞 DVD Special Edition
//勝 あしたの雪之丞2
const uint8_t bytes[] = {
0x66,0x8b,0x8e,XX4,
0x66,0x8b,0x96,XX4,
0x66,0x01,0x8e,XX4,
0x66,0x89,0x96,XX4,
0x8b,0x06,
0x6a,0x00,
0x8b,0xce,
0xff,0x50,0x08,
0x84,0xc0
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (!addr)
return false;
HookParam hp;
hp.address=addr+sizeof(bytes);
hp.type=NO_CONTEXT|USING_STRING;
hp.offset=get_reg(regs::ebx);
return NewHook(hp,"Elf");
}
bool Elf2::attach_function(){
return elf2()||Elf2attach_function();
}

23
LunaHook/engine32/Elf.h Normal file
View File

@ -0,0 +1,23 @@
#include"engine.h"
class Elf:public ENGINE{
public:
Elf(){
check_by=CHECK_BY::FILE_ALL;
check_by_target=check_by_list{L"data.arc",L"effect.arc",L"mes.arc"};
//Util::CheckFile(L"Silkys.exe") || // It might or might not have Silkys.exe
// data, effect, layer, mes, music
};
bool attach_function();
};
class Elf2:public ENGINE{
public:
Elf2(){
check_by=CHECK_BY::FILE_ALL;
check_by_target=check_by_list{L"data.arc",L"Ai5win.exe",L"mes.arc"};
};
bool attach_function();
};

View File

@ -0,0 +1,32 @@
#include"EntisGLS.h"
bool EntisGLS::attach_function() {
//それは舞い散る桜のように-完全版-
//int __thiscall sub_4BB5D0(_BYTE *this, LPCWCH lpWideCharStr)
const uint8_t bytes1[]={
0x66,0x83,0xF9,0x41 ,
0x72,0x06,
0x66,0x83,0xF9,0x5a ,
0x76,0x0C,
0x66,0x83,0xF9,0x61 ,
0x72,0x12,
0x66,0x83,0xF9,0x7a ,
0x77,0x0c
};
auto addr=MemDbg::findBytes(bytes1, sizeof(bytes1), processStartAddress, processStopAddress);
if (!addr) return false;
addr=MemDbg::findEnclosingAlignedFunction(addr);
if (!addr) return false;
HookParam hp;
hp.address = addr ;
hp.offset=get_stack(1);
hp.hook_font=F_GetGlyphOutlineW;
hp.type = USING_STRING|CODEC_UTF16|EMBED_ABLE|EMBED_BEFORE_SIMPLE|EMBED_AFTER_NEW;
return NewHook(hp, "EntisGLS");
}

View File

@ -0,0 +1,12 @@
#include"engine.h"
class EntisGLS:public ENGINE{
public:
EntisGLS(){
check_by=CHECK_BY::FILE;
check_by_target= L"Data\\*.dat";
is_engine_certain=false;
};
bool attach_function();
};

View File

@ -0,0 +1,265 @@
#include"Escude.h"
#include"embed_util.h"
/** jichi 7/23/2015 Escude
* Sample game: Re;Lord <EFBFBD><EFBFBD><EFBFBD><EFBFBD> * See: http://capita.tistory.com/m/post/210
*
* ENCODEKOR,FORCEFONT(5),HOOK(0x0042CB40,TRANS([[ESP+0x4]+0x20],PTRCHEAT,PTRBACKUP,SAFE),RETNPOS(SOURCE)),FONT(Malgun Gothic,-13)
*
* GDI functions: TextOutA, GetTextExtentPoint32A
* It requires changing function to MS Gothic using configure.exe
*
* Text in arg1 + 0x20
*
* 0042CB3C CC INT3
* 0042CB3D CC INT3
* 0042CB3E CC INT3
* 0042CB3F CC INT3
* 0042CB40 56 PUSH ESI
* 0042CB41 8B7424 08 MOV ESI,DWORD PTR SS:[ESP+0x8]
* 0042CB45 8B06 MOV EAX,DWORD PTR DS:[ESI]
* 0042CB47 50 PUSH EAX
* 0042CB48 E8 53FC0A00 CALL .004DC7A0
* 0042CB4D 8B56 04 MOV EDX,DWORD PTR DS:[ESI+0x4]
* 0042CB50 83C4 04 ADD ESP,0x4
* 0042CB53 5E POP ESI
* 0042CB54 85D2 TEST EDX,EDX
* 0042CB56 74 7E JE SHORT .0042CBD6
* 0042CB58 85C0 TEST EAX,EAX
* 0042CB5A 74 07 JE SHORT .0042CB63
* 0042CB5C 8B08 MOV ECX,DWORD PTR DS:[EAX]
* 0042CB5E 8B49 04 MOV ECX,DWORD PTR DS:[ECX+0x4]
* 0042CB61 EB 02 JMP SHORT .0042CB65
* 0042CB63 33C9 XOR ECX,ECX
* 0042CB65 890A MOV DWORD PTR DS:[EDX],ECX
* 0042CB67 85C0 TEST EAX,EAX
* 0042CB69 74 07 JE SHORT .0042CB72
* 0042CB6B 8B08 MOV ECX,DWORD PTR DS:[EAX]
* 0042CB6D 8B49 08 MOV ECX,DWORD PTR DS:[ECX+0x8]
* 0042CB70 EB 02 JMP SHORT .0042CB74
* 0042CB72 33C9 XOR ECX,ECX
* 0042CB74 894A 04 MOV DWORD PTR DS:[EDX+0x4],ECX
* 0042CB77 85C0 TEST EAX,EAX
* 0042CB79 74 08 JE SHORT .0042CB83
* 0042CB7B 8B08 MOV ECX,DWORD PTR DS:[EAX]
* 0042CB7D 0FB749 0E MOVZX ECX,WORD PTR DS:[ECX+0xE]
* 0042CB81 EB 02 JMP SHORT .0042CB85
* 0042CB83 33C9 XOR ECX,ECX
* 0042CB85 0FB7C9 MOVZX ECX,CX
* 0042CB88 894A 08 MOV DWORD PTR DS:[EDX+0x8],ECX
* 0042CB8B 85C0 TEST EAX,EAX
* 0042CB8D 74 19 JE SHORT .0042CBA8
* 0042CB8F 8B08 MOV ECX,DWORD PTR DS:[EAX]
* 0042CB91 8379 04 00 CMP DWORD PTR DS:[ECX+0x4],0x0
* 0042CB95 76 11 JBE SHORT .0042CBA8
* 0042CB97 8B49 08 MOV ECX,DWORD PTR DS:[ECX+0x8]
* 0042CB9A 85C9 TEST ECX,ECX
* 0042CB9C 76 0A JBE SHORT .0042CBA8
* 0042CB9E 49 DEC ECX
* 0042CB9F 0FAF48 0C IMUL ECX,DWORD PTR DS:[EAX+0xC]
* 0042CBA3 0348 04 ADD ECX,DWORD PTR DS:[EAX+0x4]
* 0042CBA6 EB 02 JMP SHORT .0042CBAA
* 0042CBA8 33C9 XOR ECX,ECX
* 0042CBAA 894A 0C MOV DWORD PTR DS:[EDX+0xC],ECX
* 0042CBAD 85C0 TEST EAX,EAX
* 0042CBAF 74 16 JE SHORT .0042CBC7
* 0042CBB1 8B48 0C MOV ECX,DWORD PTR DS:[EAX+0xC]
* 0042CBB4 F7D9 NEG ECX
* 0042CBB6 894A 10 MOV DWORD PTR DS:[EDX+0x10],ECX
* 0042CBB9 8B00 MOV EAX,DWORD PTR DS:[EAX]
* 0042CBBB 83C0 28 ADD EAX,0x28
* 0042CBBE 8942 14 MOV DWORD PTR DS:[EDX+0x14],EAX
* 0042CBC1 B8 01000000 MOV EAX,0x1
* 0042CBC6 C3 RETN
* 0042CBC7 33C9 XOR ECX,ECX
* 0042CBC9 F7D9 NEG ECX
* 0042CBCB 894A 10 MOV DWORD PTR DS:[EDX+0x10],ECX
* 0042CBCE 8B00 MOV EAX,DWORD PTR DS:[EAX]
* 0042CBD0 83C0 28 ADD EAX,0x28
* 0042CBD3 8942 14 MOV DWORD PTR DS:[EDX+0x14],EAX
* 0042CBD6 B8 01000000 MOV EAX,0x1
* 0042CBDB C3 RETN
* 0042CBDC CC INT3
* 0042CBDD CC INT3
* 0042CBDE CC INT3
* 0042CBDF CC INT3
* 0042CBE0 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+0x4]
* 0042CBE4 8B48 10 MOV ECX,DWORD PTR DS:[EAX+0x10]
* 0042CBE7 8B50 0C MOV EDX,DWORD PTR DS:[EAX+0xC]
* 0042CBEA 51 PUSH ECX
* 0042CBEB 8B48 08 MOV ECX,DWORD PTR DS:[EAX+0x8]
* 0042CBEE 52 PUSH EDX
* 0042CBEF 8B50 04 MOV EDX,DWORD PTR DS:[EAX+0x4]
* 0042CBF2 8B00 MOV EAX,DWORD PTR DS:[EAX]
* 0042CBF4 51 PUSH ECX
* 0042CBF5 52 PUSH EDX
* 0042CBF6 50 PUSH EAX
* 0042CBF7 E8 E4FD0A00 CALL .004DC9E0
* 0042CBFC 83C4 14 ADD ESP,0x14
* 0042CBFF C3 RETN
* 0042CC00 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+0x4]
* 0042CC04 8B48 10 MOV ECX,DWORD PTR DS:[EAX+0x10]
* 0042CC07 8B50 0C MOV EDX,DWORD PTR DS:[EAX+0xC]
* 0042CC0A 51 PUSH ECX
* 0042CC0B 8B48 08 MOV ECX,DWORD PTR DS:[EAX+0x8]
* 0042CC0E 52 PUSH EDX
* 0042CC0F 8B50 04 MOV EDX,DWORD PTR DS:[EAX+0x4]
* 0042CC12 8B00 MOV EAX,DWORD PTR DS:[EAX]
* 0042CC14 51 PUSH ECX
* 0042CC15 52 PUSH EDX
* 0042CC16 50 PUSH EAX
* 0042CC17 E8 C4FF0A00 CALL .004DCBE0
* 0042CC1C 83C4 14 ADD ESP,0x14
* 0042CC1F C3 RETN
* 0042CC20 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+0x4]
* 0042CC24 8B08 MOV ECX,DWORD PTR DS:[EAX]
* 0042CC26 894C24 04 MOV DWORD PTR SS:[ESP+0x4],ECX
* 0042CC2A E9 71FB0A00 JMP .004DC7A0
* 0042CC2F CC INT3
* 0042CC30 56 PUSH ESI
* 0042CC31 8B7424 08 MOV ESI,DWORD PTR SS:[ESP+0x8]
* 0042CC35 8B06 MOV EAX,DWORD PTR DS:[ESI]
* 0042CC37 50 PUSH EAX
* 0042CC38 E8 63FB0A00 CALL .004DC7A0
* 0042CC3D D946 0C FLD DWORD PTR DS:[ESI+0xC]
* 0042CC40 D91C24 FSTP DWORD PTR SS:[ESP]
* 0042CC43 83EC 08 SUB ESP,0x8
* 0042CC46 D946 08 FLD DWORD PTR DS:[ESI+0x8]
* 0042CC49 D95C24 04 FSTP DWORD PTR SS:[ESP+0x4]
* 0042CC4D D946 04 FLD DWORD PTR DS:[ESI+0x4]
* 0042CC50 D91C24 FSTP DWORD PTR SS:[ESP]
* 0042CC53 50 PUSH EAX
* 0042CC54 E8 27680400 CALL .00473480
* 0042CC59 83C4 10 ADD ESP,0x10
* 0042CC5C B8 01000000 MOV EAX,0x1
* 0042CC61 5E POP ESI
* 0042CC62 C3 RETN
* 0042CC63 CC INT3
* 0042CC64 CC INT3
* 0042CC65 CC INT3
* 0042CC66 CC INT3
* 0042CC67 CC INT3
* 0042CC68 CC INT3
* 0042CC69 CC INT3 *
*/
namespace { // unnamed
/**
* Handle new lines and ruby.
*
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD> * <EFBFBD><EFBFBD> <r> <EFBFBD> *
* <EFBFBD><EFBFBD><EFBFBD>r><ruby text='<EFBFBD>></ruby><EFBFBD> *
* <EFBFBD><EFBFBD>ruby text='<EFBFBD>></ruby><EFBFBD><EFBFBD>r><EFBFBD><EFBFBD> *
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <r> <EFBFBD><EFBFBD>r><ruby text='<EFBFBD>></ruby><EFBFBD><EFBFBD>ruby text='<EFBFBD>></ruby><EFBFBD><EFBFBD>r><EFBFBD>
*/
bool EscudeFilter(LPVOID data, size_t *size, HookParam *)
{
auto text = reinterpret_cast<LPSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
StringCharReplacer(text, len, "<r>", 3, '\n');
if (cpp_strnstr(text, "<ruby", *len)) {
StringFilter(text, len, "</ruby>", 7);
StringFilterBetween(text, len, "<ruby", 5, "'>", 2);
}
return true;
}
LPCSTR _escudeltrim(LPCSTR text)
{
if (text && *text == '<')
for (auto p = text; (signed char)*p > 0; p++)
if (*p == '>')
return p + 1;
return text;
}
void SpecialHookEscude(hook_stack* stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t*len)
{
DWORD arg1 = stack->stack[1];
if (!arg1 || (LONG)arg1 == -1 || ::IsBadWritePtr((LPVOID)arg1, 4)) // this is indispensable
return;
LPCSTR text = (LPCSTR)*(DWORD *)(arg1 + 0x20);
if (!text || ::IsBadWritePtr((LPVOID)text, 1) || !*text) // this is indispensable
return;
text = _escudeltrim(text);
if (!text)
return;
*data = (DWORD)text;
*len = ::strlen(text);
*split = *(DWORD *)arg1;
}
struct HookArgument
{
ULONG split;
//ULONG unknown1[3];
//LPCSTR text1; // 0x10 only for old games
ULONG unknown[7];
LPCSTR text; // 0x20
bool isValid() const { return Engine::isAddressWritable(text) && *text; }
Engine::TextRole role() const
{
if (split >= 0xff)
return Engine::OtherRole;
static ULONG maxSplit_ = 0;
if (split > maxSplit_)
maxSplit_ = split;
if (split == maxSplit_)
return Engine::ScenarioRole;
return Engine::NameRole; // scenario role is larger than name role
}
};
LPCSTR trimmedText;
bool hook_before(hook_stack*s,void* data, size_t* len,uintptr_t*role){
auto arg = (HookArgument *)s->stack[1];
if ((long)arg == -1 || !Engine::isAddressWritable(arg) || !arg->isValid())
return false;
trimmedText = _escudeltrim(arg->text);
* role = arg->role();
strcpy((char*)data,trimmedText);
*len=strlen(trimmedText);
return true;
}
void hook_after(hook_stack*s,void* data, size_t len){
static std::string data_;
data_=std::string((char*)data,len);
auto arg = (HookArgument *)s->stack[1];
if(trimmedText!=arg->text)
data_.insert(0,std::string(arg->text, trimmedText - arg->text));
arg->text=data_.c_str();
}
} // unnamed namespace
bool InsertEscudeHook()
{
const BYTE bytes[] = {
0x76, 0x0a, // 0042cb9c 76 0a jbe short .0042cba8
0x49, // 0042cb9e 49 dec ecx
0x0f,0xaf,0x48, 0x0c // 0042cb9f 0faf48 0c imul ecx,dword ptr ds:[eax+0xc]
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
//GROWL(addr);
if (!addr) {
ConsoleOutput("Escude: pattern not found");
return false;
}
addr = MemDbg::findEnclosingAlignedFunction(addr);
if (!addr) {
ConsoleOutput("Escude: enclosing function not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.hook_before=hook_before;
hp.hook_after=hook_after;
hp.hook_font=F_TextOutA|F_GetTextExtentPoint32A;
hp.text_fun = SpecialHookEscude;
hp.filter_fun = EscudeFilter;
hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT|EMBED_ABLE|EMBED_DYNA_SJIS; // NO_CONTEXT as this function is only called by one caller anyway
hp.newlineseperator=L"<r>";
ConsoleOutput("INSERT Escude");
return NewHook(hp, "Escude");
}
bool Escude::attach_function() {
return InsertEscudeHook();
}

View File

@ -0,0 +1,11 @@
#include"engine.h"
class Escude:public ENGINE{
public:
Escude(){
check_by=CHECK_BY::FILE_ALL;
check_by_target=check_by_list{L"configure.cfg",L"gfx.bin"};
}
bool attach_function();
};

View File

@ -0,0 +1,522 @@
#include"Eushully.h"
/** jichi 6/1/2014 Eushully
* Insert to the last GetTextExtentPoint32A
*
* ATCode:
* http://capita.tistory.com/m/post/255
*
* Binary:
* {AGE.EXE!0x000113C3(89 C2 C1 E2 04 29 C2 E8 BD 25 20 00 52 89 D1 59), AGE.EXE!0x00012A47(E8 40 0F 20 00 90 90 90 90), AGE.EXE!0x0001DF07(55 8B EC 83 EC 08 56 EB 07 E8 32 5A 1F 00 EB F0), AGE.EXE!0x002137CE(90 90 90 90 90 C2 04 00 53 8B 1A 83 FB 6E 74 14 81 FB 96 01 00 00 74 1B 83 FB 6F 74 25 83 FB 72 74 27 EB 2C 8B 5A 10 89 1F 83 C7 04 B8 05 00 00 00 EB 1F 8B 5A 10 89 1F 83 C7 04 B8 07 00 00 00 EB 10 B8 03 00 00 00 EB 09 B8 01 00 00 00 EB 02 31 C0 5B C3 60 89 E5 83 EC 18 E8 7E 01 00 00 8B 55 F8 83 3A 00 75 31 8B 45 FC 8B 4C 30 E8 89 CA C1 E2 04 29 CA 8D 0C D6 8B 1C 08 51 8B 4C 08 FC 8B 7D F4 89 DA E8 7E FF FF FF 85 C0 74 0A 83 F8 01 74 09 8D 14 82 EB ED 89 EC 61 C3 C7 07 00 00 00 00 8B 75 F4 8B 7D F0 52 8B 06 85 C0 74 17 8D 04 81 8A 10 80 FA FF 74 08 F6 D2 88 17 40 47 EB F1 83 C6 04 EB E3 8B 55 F0 52 8B 02 E8 2F FF FF FF 8B 12 39 D0 74 C1 8B 55 F8 C7 02 01 00 00 00 8B 4D E4 8B 45 FC 8D 04 08 8B 55 F8 89 42 04 58 89 42 08 89 5A 0C 8B 45 FC 8B 4C 08 FC 8B 45 F4 8B 00 89 42 10 8D 04 81 89 42 14 8B 72 0C 8B 7D EC B9 08 00 00 00 F3 A5 8B 5D E8 8B 7A 14 8B 75 F0 31 C9 52 8A 06 84 C0 74 0F F6 D0 8A 14 39 88 14 19 88 04 39 41 46 EB EB 5A 8B 04 39 89 04 19 31 C0 F7 D0 89 04 39 83 C1 04 89 4A 18 8B 7A 0C 8B 42 10 31 C9 BB 6E 00 00 00 89 1F 89 4F 04 89 4F 08 C7 47 0C 02 00 00 00 83 C3 04 89 5F 14 89 4F 18 89 4F 1C 89 EC 61 C3 60 89 E5 83 EC 18 E8 59 00 00 00 8B 5D F8 83 3B 01 75 2E 31 C9 89 0B 8B 7B 0C 8B 75 EC 8D 49 08 F3 A5 8B 7B 14 8B 75 E8 8B 4B 18 F3 A4 8B 43 04 8B 53 08 89 10 8D 7B 04 31 C0 B9 40 01 00 00 F3 AB 89 EC 61 C3 8B 8C D6 A8 D7 05 00 8B 01 3D 96 01 00 00 74 07 83 F8 6E 74 02 EB 07 E8 7A FE FF FF 8B 01 C3 60 C7 45 FC A8 D7 05 00 EB 03 58 EB 05 E8 F8 FF FF FF 2D BD 39 21 00 03 80 D4 02 00 00 B9 00 01 00 00 8D 80 00 40 01 00 89 45 F8 8D 04 01 89 45 F4 8D 04 01 89 45 F0 8D 04 01 89 45 EC 8D 04 01 89 45 E8 61 C3)}
*
* #1 other text AGE.EXE!0x000113C3(89 C2 C1 E2 04 29 C2 E8 BD 25 20 00 52 89 D1 59)
* #2 scenario AGE.EXE!0x00012A47(E8 40 0F 20 00 90 90 90 90)
*
* 0041130B 8B96 9CA30A00 MOV EDX,DWORD PTR DS:[ESI+0xAA39C]
* 00411311 81A6 CCA90A00 FF>AND DWORD PTR DS:[ESI+0xAA9CC],0xF7FFFFF>
* 0041131B 33C0 XOR EAX,EAX
* 0041131D 50 PUSH EAX
* 0041131E 8986 1C160000 MOV DWORD PTR DS:[ESI+0x161C],EAX
* 00411324 8986 78EB0500 MOV DWORD PTR DS:[ESI+0x5EB78],EAX
* 0041132A 8B42 0C MOV EAX,DWORD PTR DS:[EDX+0xC]
* 0041132D 68 F4536100 PUSH .006153F4 ; ASCII "message:ReadTextSkip"
* 00411332 8D8E 9CA30A00 LEA ECX,DWORD PTR DS:[ESI+0xAA39C]
* 00411338 FFD0 CALL EAX
* 0041133A 8B96 9CA30A00 MOV EDX,DWORD PTR DS:[ESI+0xAA39C]
* 00411340 8B42 04 MOV EAX,DWORD PTR DS:[EDX+0x4]
* 00411343 68 4C606100 PUSH .0061604C ; ASCII "set:CancelMesSkipOnClick"
* 00411348 8D8E 9CA30A00 LEA ECX,DWORD PTR DS:[ESI+0xAA39C]
* 0041134E FFD0 CALL EAX
* 00411350 83F8 02 CMP EAX,0x2
* 00411353 75 1A JNZ SHORT .0041136F
* 00411355 68 34606100 PUSH .00616034 ; ASCII "CALLBACK_SETTING.BIN"
* 0041135A 8BCE MOV ECX,ESI
* 0041135C E8 7FFBFFFF CALL .00410EE0
* 00411361 5F POP EDI
* 00411362 5E POP ESI
* 00411363 5B POP EBX
* 00411364 C3 RETN
* 00411365 C786 18770700 01>MOV DWORD PTR DS:[ESI+0x77718],0x1
* 0041136F 83BE 6C780700 00 CMP DWORD PTR DS:[ESI+0x7786C],0x0
* 00411376 75 45 JNZ SHORT .004113BD
* 00411378 F603 40 TEST BYTE PTR DS:[EBX],0x40
* 0041137B 75 40 JNZ SHORT .004113BD
* 0041137D 81A6 CCA90A00 FF>AND DWORD PTR DS:[ESI+0xAA9CC],0xF7FFFFF>
* 00411387 33DB XOR EBX,EBX
* 00411389 8DBE B0780700 LEA EDI,DWORD PTR DS:[ESI+0x778B0]
* 0041138F 90 NOP
* 00411390 8B07 MOV EAX,DWORD PTR DS:[EDI]
* 00411392 85C0 TEST EAX,EAX
* 00411394 74 1E JE SHORT .004113B4
* 00411396 8B8F E4D5F8FF MOV ECX,DWORD PTR DS:[EDI+0xFFF8D5E4]
* 0041139C 8B57 0C MOV EDX,DWORD PTR DS:[EDI+0xC]
* 0041139F 51 PUSH ECX
* 004113A0 52 PUSH EDX
* 004113A1 50 PUSH EAX
* 004113A2 53 PUSH EBX
* 004113A3 8D8E 04480100 LEA ECX,DWORD PTR DS:[ESI+0x14804]
* 004113A9 E8 42840900 CALL .004A97F0
* 004113AE C707 00000000 MOV DWORD PTR DS:[EDI],0x0
* 004113B4 43 INC EBX
* 004113B5 83C7 04 ADD EDI,0x4
* 004113B8 83FB 03 CMP EBX,0x3
* 004113BB ^7C D3 JL SHORT .00411390
* 004113BD 8B86 90D70500 MOV EAX,DWORD PTR DS:[ESI+0x5D790]
* 004113C3 8BC8 MOV ECX,EAX ; jichi: #1 hook here
* 004113C5 C1E1 04 SHL ECX,0x4
* 004113C8 2BC8 SUB ECX,EAX
* 004113CA 8B94CE A8D70500 MOV EDX,DWORD PTR DS:[ESI+ECX*8+0x5D7A8]
* 004113D1 8B02 MOV EAX,DWORD PTR DS:[EDX]
* 004113D3 85C0 TEST EAX,EAX
* //004113C3 89C2 MOV EDX,EAX
* //004113C5 C1E2 04 SHL EDX,0x4
* //004113C8 29C2 SUB EDX,EAX
* //004113CA E8 BD252000 CALL .0061398C
* //004113CF 52 PUSH EDX
* //004113D0 89D1 MOV ECX,EDX
* //004113D2 59 POP ECX
* 004113D5 78 35 JS SHORT .0041140C
* 004113D7 3D 00040000 CMP EAX,0x400
* 004113DC 7D 2E JGE SHORT .0041140C
* 004113DE 8B8486 244F0A00 MOV EAX,DWORD PTR DS:[ESI+EAX*4+0xA4F24]
* 004113E5 8BCE MOV ECX,ESI
* 004113E7 FFD0 CALL EAX
* 004113E9 8B86 90D70500 MOV EAX,DWORD PTR DS:[ESI+0x5D790]
* 004113EF 8BC8 MOV ECX,EAX
* 004113F1 C1E1 04 SHL ECX,0x4
* 004113F4 2BC8 SUB ECX,EAX
* 004113F6 8B94CE 04D80500 MOV EDX,DWORD PTR DS:[ESI+ECX*8+0x5D804]
* 004113FD 8D04CE LEA EAX,DWORD PTR DS:[ESI+ECX*8]
* 00411400 03D2 ADD EDX,EDX
* 00411402 03D2 ADD EDX,EDX
* 00411404 0190 A8D70500 ADD DWORD PTR DS:[EAX+0x5D7A8],EDX
* 0041140A EB 07 JMP SHORT .00411413
* 0041140C 8BCE MOV ECX,ESI
* 0041140E E8 7D6C0000 CALL .00418090
* 00411413 8B86 9CA30A00 MOV EAX,DWORD PTR DS:[ESI+0xAA39C]
* 00411419 8B50 04 MOV EDX,DWORD PTR DS:[EAX+0x4]
* 0041141C 8D8E 9CA30A00 LEA ECX,DWORD PTR DS:[ESI+0xAA39C]
* 00411422 68 4C606100 PUSH .0061604C ; ASCII "set:CancelMesSkipOnClick"
* 00411427 FFD2 CALL EDX
* 00411429 85C0 TEST EAX,EAX
* 0041142B ^0F85 30FFFFFF JNZ .00411361
* 00411431 3986 D8C90000 CMP DWORD PTR DS:[ESI+0xC9D8],EAX
* 00411437 ^0F84 24FFFFFF JE .00411361
* 0041143D 8B86 D0A90A00 MOV EAX,DWORD PTR DS:[ESI+0xAA9D0]
* 00411443 A8 10 TEST AL,0x10
* 00411445 0F84 84000000 JE .004114CF
* 0041144B 83E0 EF AND EAX,0xFFFFFFEF
* 0041144E 83BE 10770700 00 CMP DWORD PTR DS:[ESI+0x77710],0x0
* 00411455 8986 D0A90A00 MOV DWORD PTR DS:[ESI+0xAA9D0],EAX
* 0041145B ^0F85 00FFFFFF JNZ .00411361
* 00411461 8B86 ECC90000 MOV EAX,DWORD PTR DS:[ESI+0xC9EC]
* 00411467 8DBE 3C550000 LEA EDI,DWORD PTR DS:[ESI+0x553C]
* 0041146D 85C0 TEST EAX,EAX
* 0041146F ^0F88 ECFEFFFF JS .00411361
* 00411475 3987 08040000 CMP DWORD PTR DS:[EDI+0x408],EAX
* 0041147B ^0F8E E0FEFFFF JLE .00411361
* 00411481 8BCE MOV ECX,ESI
* 00411483 E8 A86AFFFF CALL .00407F30
* 00411488 6A 00 PUSH 0x0
* 0041148A 8BCE MOV ECX,ESI
* 0041148C E8 EF3CFFFF CALL .00405180
* 00411491 8B86 90D70500 MOV EAX,DWORD PTR DS:[ESI+0x5D790]
* 00411497 8BC8 MOV ECX,EAX
* 00411499 C1E1 04 SHL ECX,0x4
* 0041149C 2BC8 SUB ECX,EAX
* 0041149E 8D34CE LEA ESI,DWORD PTR DS:[ESI+ECX*8]
* 004114A1 8BCF MOV ECX,EDI
* 004114A3 E8 0839FFFF CALL .00404DB0
* 004114A8 8B96 A4D70500 MOV EDX,DWORD PTR DS:[ESI+0x5D7A4]
* 004114AE 8D0482 LEA EAX,DWORD PTR DS:[EDX+EAX*4]
* 004114B1 8986 A8D70500 MOV DWORD PTR DS:[ESI+0x5D7A8],EAX
* 004114B7 C787 B0740000 FF>MOV DWORD PTR DS:[EDI+0x74B0],-0x1
*
* 00412953 53 PUSH EBX
* 00412954 FF15 B8406100 CALL DWORD PTR DS:[0x6140B8] ; kernel32.Sleep
* 0041295A 53 PUSH EBX
* 0041295B 53 PUSH EBX
* 0041295C 53 PUSH EBX
* 0041295D 53 PUSH EBX
* 0041295E 8D8D 34F8FFFF LEA ECX,DWORD PTR SS:[EBP-0x7CC]
* 00412964 51 PUSH ECX
* 00412965 FF15 AC436100 CALL DWORD PTR DS:[0x6143AC] ; user32.PeekMessageA
* 0041296B 85C0 TEST EAX,EAX
* 0041296D ^0F85 5DF3FFFF JNZ .00411CD0
* 00412973 ^E9 D8F3FFFF JMP .00411D50
* 00412978 A9 00000020 TEST EAX,0x20000000
* 0041297D 74 0C JE SHORT .0041298B
* 0041297F 8BCE MOV ECX,ESI
* 00412981 E8 3A63FFFF CALL .00408CC0
* 00412986 ^E9 C5F3FFFF JMP .00411D50
* 0041298B 85C0 TEST EAX,EAX
* 0041298D 79 14 JNS SHORT .004129A3
* 0041298F 8BCE MOV ECX,ESI
* 00412991 E8 AAEBFFFF CALL .00411540
* 00412996 6A 02 PUSH 0x2
* 00412998 FF15 B8406100 CALL DWORD PTR DS:[0x6140B8] ; kernel32.Sleep
* 0041299E ^E9 ADF3FFFF JMP .00411D50
* 004129A3 A8 01 TEST AL,0x1
* 004129A5 74 25 JE SHORT .004129CC
* 004129A7 8D8E D08D0600 LEA ECX,DWORD PTR DS:[ESI+0x68DD0]
* 004129AD E8 CEF30300 CALL .00451D80
* 004129B2 8985 ACF8FFFF MOV DWORD PTR SS:[EBP-0x754],EAX
* 004129B8 3BC3 CMP EAX,EBX
* 004129BA ^0F8C 90F3FFFF JL .00411D50
* 004129C0 83A6 CCA90A00 FE AND DWORD PTR DS:[ESI+0xAA9CC],0xFFFFFFF>
* 004129C7 ^E9 84F3FFFF JMP .00411D50
* 004129CC A8 20 TEST AL,0x20
* 004129CE 74 3C JE SHORT .00412A0C
* 004129D0 8D8E 5C8E0600 LEA ECX,DWORD PTR DS:[ESI+0x68E5C]
* 004129D6 E8 A5F30300 CALL .00451D80
* 004129DB 8985 ACF8FFFF MOV DWORD PTR SS:[EBP-0x754],EAX
* 004129E1 3BC3 CMP EAX,EBX
* 004129E3 ^0F8C 67F3FFFF JL .00411D50
* 004129E9 83A6 CCA90A00 DF AND DWORD PTR DS:[ESI+0xAA9CC],0xFFFFFFD>
* 004129F0 8D8E 5C8E0600 LEA ECX,DWORD PTR DS:[ESI+0x68E5C]
* 004129F6 E8 45EE0300 CALL .00451840
* 004129FB 50 PUSH EAX
* 004129FC 8D8E 5C8E0600 LEA ECX,DWORD PTR DS:[ESI+0x68E5C]
* 00412A02 E8 39F30300 CALL .00451D40
* 00412A07 ^E9 44F3FFFF JMP .00411D50
* 00412A0C A9 00000010 TEST EAX,0x10000000
* 00412A11 74 14 JE SHORT .00412A27
* 00412A13 8BCE MOV ECX,ESI
* 00412A15 E8 A664FFFF CALL .00408EC0
* 00412A1A 6A 02 PUSH 0x2
* 00412A1C FF15 B8406100 CALL DWORD PTR DS:[0x6140B8] ; kernel32.Sleep
* 00412A22 ^E9 29F3FFFF JMP .00411D50
* 00412A27 A9 00008000 TEST EAX,0x800000
* 00412A2C 74 0C JE SHORT .00412A3A
* 00412A2E 8BCE MOV ECX,ESI
* 00412A30 E8 6B66FFFF CALL .004090A0
* 00412A35 ^E9 16F3FFFF JMP .00411D50
* 00412A3A 8B86 90D70500 MOV EAX,DWORD PTR DS:[ESI+0x5D790]
* 00412A40 8BD0 MOV EDX,EAX
* 00412A42 C1E2 04 SHL EDX,0x4
* 00412A45 2BD0 SUB EDX,EAX
* 00412A47 8B84D6 A8D70500 MOV EAX,DWORD PTR DS:[ESI+EDX*8+0x5D7A8] ; jichi: #2 hook here
* //00412A47 E8 400F2000 CALL .0061398C
* 00412A4E 8B00 MOV EAX,DWORD PTR DS:[EAX]
* 00412A50 3BC3 CMP EAX,EBX
* 00412A52 7C 37 JL SHORT .00412A8B
* 00412A54 3D 00040000 CMP EAX,0x400
* 00412A59 7D 30 JGE SHORT .00412A8B
* 00412A5B 8BCE MOV ECX,ESI
* 00412A5D 8B9486 244F0A00 MOV EDX,DWORD PTR DS:[ESI+EAX*4+0xA4F24]
* 00412A64 FFD2 CALL EDX
* 00412A66 8B86 90D70500 MOV EAX,DWORD PTR DS:[ESI+0x5D790]
* 00412A6C 8BC8 MOV ECX,EAX
* 00412A6E C1E1 04 SHL ECX,0x4
* 00412A71 2BC8 SUB ECX,EAX
* 00412A73 8D04CE LEA EAX,DWORD PTR DS:[ESI+ECX*8]
* 00412A76 8B90 04D80500 MOV EDX,DWORD PTR DS:[EAX+0x5D804]
* 00412A7C 03D2 ADD EDX,EDX
* 00412A7E 03D2 ADD EDX,EDX
* 00412A80 0190 A8D70500 ADD DWORD PTR DS:[EAX+0x5D7A8],EDX
* 00412A86 ^E9 C5F2FFFF JMP .00411D50
* 00412A8B 8BCE MOV ECX,ESI
* 00412A8D E8 FE550000 CALL .00418090
* 00412A92 ^E9 B9F2FFFF JMP .00411D50
* 00412A97 C785 A4F8FFFF 01>MOV DWORD PTR SS:[EBP-0x75C],0x1
* 00412AA1 C745 FC FFFFFFFF MOV DWORD PTR SS:[EBP-0x4],-0x1
* 00412AA8 B8 E02D4100 MOV EAX,.00412DE0
* 00412AAD C3 RETN
* 00412AAE 8B85 14F8FFFF MOV EAX,DWORD PTR SS:[EBP-0x7EC]
* 00412AB4 50 PUSH EAX
* 00412AB5 8B8D 10F8FFFF MOV ECX,DWORD PTR SS:[EBP-0x7F0]
*
* Patched code:
*
* 0041DF07 55 PUSH EBP
* 0041DF08 8BEC MOV EBP,ESP
* 0041DF0A 83EC 08 SUB ESP,0x8
* 0041DF0D 56 PUSH ESI
* 0041DF0E EB 07 JMP SHORT .0041DF17
* 0041DF10 E8 325A1F00 CALL .00613947
* 0041DF15 ^EB F0 JMP SHORT .0041DF07
*
* 006137CE 90 NOP
* 006137CF 90 NOP
* 006137D0 90 NOP
* 006137D1 90 NOP
* 006137D2 90 NOP
* 006137D3 C2 0400 RETN 0x4
* 006137D6 53 PUSH EBX
* 006137D7 8B1A MOV EBX,DWORD PTR DS:[EDX]
* 006137D9 83FB 6E CMP EBX,0x6E
* 006137DC 74 14 JE SHORT .006137F2
* 006137DE 81FB 96010000 CMP EBX,0x196
* 006137E4 74 1B JE SHORT .00613801
* 006137E6 83FB 6F CMP EBX,0x6F
* 006137E9 74 25 JE SHORT .00613810
* 006137EB 83FB 72 CMP EBX,0x72
* 006137EE 74 27 JE SHORT .00613817
* 006137F0 EB 2C JMP SHORT .0061381E
* 006137F2 8B5A 10 MOV EBX,DWORD PTR DS:[EDX+0x10]
* 006137F5 891F MOV DWORD PTR DS:[EDI],EBX
* 006137F7 83C7 04 ADD EDI,0x4
* 006137FA B8 05000000 MOV EAX,0x5
* 006137FF EB 1F JMP SHORT .00613820
* 00613801 8B5A 10 MOV EBX,DWORD PTR DS:[EDX+0x10]
* 00613804 891F MOV DWORD PTR DS:[EDI],EBX
* 00613806 83C7 04 ADD EDI,0x4
* 00613809 B8 07000000 MOV EAX,0x7
* 0061380E EB 10 JMP SHORT .00613820
* 00613810 B8 03000000 MOV EAX,0x3
* 00613815 EB 09 JMP SHORT .00613820
* 00613817 B8 01000000 MOV EAX,0x1
* 0061381C EB 02 JMP SHORT .00613820
* 0061381E 31C0 XOR EAX,EAX
* 00613820 5B POP EBX
* 00613821 C3 RETN
* 00613822 60 PUSHAD ; jichi: the translate function for hookpoint #2
* 00613823 89E5 MOV EBP,ESP
* 00613825 83EC 18 SUB ESP,0x18 ; reserve 18 local variables
* 00613828 E8 7E010000 CALL .006139AB
* 0061382D 8B55 F8 MOV EDX,DWORD PTR SS:[EBP-0x8]
* 00613830 833A 00 CMP DWORD PTR DS:[EDX],0x0
* 00613833 75 31 JNZ SHORT .00613866
* 00613835 8B45 FC MOV EAX,DWORD PTR SS:[EBP-0x4]
* 00613838 8B4C30 E8 MOV ECX,DWORD PTR DS:[EAX+ESI-0x18]
* 0061383C 89CA MOV EDX,ECX
* 0061383E C1E2 04 SHL EDX,0x4
* 00613841 29CA SUB EDX,ECX
* 00613843 8D0CD6 LEA ECX,DWORD PTR DS:[ESI+EDX*8]
* 00613846 8B1C08 MOV EBX,DWORD PTR DS:[EAX+ECX]
* 00613849 51 PUSH ECX
* 0061384A 8B4C08 FC MOV ECX,DWORD PTR DS:[EAX+ECX-0x4]
* 0061384E 8B7D F4 MOV EDI,DWORD PTR SS:[EBP-0xC]
* 00613851 89DA MOV EDX,EBX
* 00613853 E8 7EFFFFFF CALL .006137D6
* 00613858 85C0 TEST EAX,EAX
* 0061385A 74 0A JE SHORT .00613866
* 0061385C 83F8 01 CMP EAX,0x1
* 0061385F 74 09 JE SHORT .0061386A
* 00613861 8D1482 LEA EDX,DWORD PTR DS:[EDX+EAX*4]
* 00613864 ^EB ED JMP SHORT .00613853
* 00613866 89EC MOV ESP,EBP
* 00613868 61 POPAD
* 00613869 C3 RETN
* 0061386A C707 00000000 MOV DWORD PTR DS:[EDI],0x0
* 00613870 8B75 F4 MOV ESI,DWORD PTR SS:[EBP-0xC]
* 00613873 8B7D F0 MOV EDI,DWORD PTR SS:[EBP-0x10]
* 00613876 52 PUSH EDX
* 00613877 8B06 MOV EAX,DWORD PTR DS:[ESI]
* 00613879 85C0 TEST EAX,EAX
* 0061387B 74 17 JE SHORT .00613894
* 0061387D 8D0481 LEA EAX,DWORD PTR DS:[ECX+EAX*4]
* 00613880 8A10 MOV DL,BYTE PTR DS:[EAX]
* 00613882 80FA FF CMP DL,0xFF
* 00613885 74 08 JE SHORT .0061388F
* 00613887 F6D2 NOT DL
* 00613889 8817 MOV BYTE PTR DS:[EDI],DL
* 0061388B 40 INC EAX
* 0061388C 47 INC EDI
* 0061388D ^EB F1 JMP SHORT .00613880
* 0061388F 83C6 04 ADD ESI,0x4
* 00613892 ^EB E3 JMP SHORT .00613877
* 00613894 8B55 F0 MOV EDX,DWORD PTR SS:[EBP-0x10]
* 00613897 52 PUSH EDX
* 00613898 8B02 MOV EAX,DWORD PTR DS:[EDX]
* 0061389A E8 2FFFFFFF CALL .006137CE
* 0061389F 8B12 MOV EDX,DWORD PTR DS:[EDX]
* 006138A1 39D0 CMP EAX,EDX
* 006138A3 ^74 C1 JE SHORT .00613866
* 006138A5 8B55 F8 MOV EDX,DWORD PTR SS:[EBP-0x8]
* 006138A8 C702 01000000 MOV DWORD PTR DS:[EDX],0x1
* 006138AE 8B4D E4 MOV ECX,DWORD PTR SS:[EBP-0x1C]
* 006138B1 8B45 FC MOV EAX,DWORD PTR SS:[EBP-0x4]
* 006138B4 8D0408 LEA EAX,DWORD PTR DS:[EAX+ECX]
* 006138B7 8B55 F8 MOV EDX,DWORD PTR SS:[EBP-0x8]
* 006138BA 8942 04 MOV DWORD PTR DS:[EDX+0x4],EAX
* 006138BD 58 POP EAX
* 006138BE 8942 08 MOV DWORD PTR DS:[EDX+0x8],EAX
* 006138C1 895A 0C MOV DWORD PTR DS:[EDX+0xC],EBX
* 006138C4 8B45 FC MOV EAX,DWORD PTR SS:[EBP-0x4]
* 006138C7 8B4C08 FC MOV ECX,DWORD PTR DS:[EAX+ECX-0x4]
* 006138CB 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-0xC]
* 006138CE 8B00 MOV EAX,DWORD PTR DS:[EAX]
* 006138D0 8942 10 MOV DWORD PTR DS:[EDX+0x10],EAX
* 006138D3 8D0481 LEA EAX,DWORD PTR DS:[ECX+EAX*4]
* 006138D6 8942 14 MOV DWORD PTR DS:[EDX+0x14],EAX
* 006138D9 8B72 0C MOV ESI,DWORD PTR DS:[EDX+0xC]
* 006138DC 8B7D EC MOV EDI,DWORD PTR SS:[EBP-0x14]
* 006138DF B9 08000000 MOV ECX,0x8
* 006138E4 F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
* 006138E6 8B5D E8 MOV EBX,DWORD PTR SS:[EBP-0x18]
* 006138E9 8B7A 14 MOV EDI,DWORD PTR DS:[EDX+0x14]
* 006138EC 8B75 F0 MOV ESI,DWORD PTR SS:[EBP-0x10]
* 006138EF 31C9 XOR ECX,ECX
* 006138F1 52 PUSH EDX
* 006138F2 8A06 MOV AL,BYTE PTR DS:[ESI]
* 006138F4 84C0 TEST AL,AL
* 006138F6 74 0F JE SHORT .00613907
* 006138F8 F6D0 NOT AL
* 006138FA 8A1439 MOV DL,BYTE PTR DS:[ECX+EDI]
* 006138FD 881419 MOV BYTE PTR DS:[ECX+EBX],DL
* 00613900 880439 MOV BYTE PTR DS:[ECX+EDI],AL
* 00613903 41 INC ECX
* 00613904 46 INC ESI
* 00613905 ^EB EB JMP SHORT .006138F2
* 00613907 5A POP EDX
* 00613908 8B0439 MOV EAX,DWORD PTR DS:[ECX+EDI]
* 0061390B 890419 MOV DWORD PTR DS:[ECX+EBX],EAX
* 0061390E 31C0 XOR EAX,EAX
* 00613910 F7D0 NOT EAX
* 00613912 890439 MOV DWORD PTR DS:[ECX+EDI],EAX
* 00613915 83C1 04 ADD ECX,0x4
* 00613918 894A 18 MOV DWORD PTR DS:[EDX+0x18],ECX
* 0061391B 8B7A 0C MOV EDI,DWORD PTR DS:[EDX+0xC]
* 0061391E 8B42 10 MOV EAX,DWORD PTR DS:[EDX+0x10]
* 00613921 31C9 XOR ECX,ECX
* 00613923 BB 6E000000 MOV EBX,0x6E
* 00613928 891F MOV DWORD PTR DS:[EDI],EBX
* 0061392A 894F 04 MOV DWORD PTR DS:[EDI+0x4],ECX
* 0061392D 894F 08 MOV DWORD PTR DS:[EDI+0x8],ECX
* 00613930 C747 0C 02000000 MOV DWORD PTR DS:[EDI+0xC],0x2
* 00613937 83C3 04 ADD EBX,0x4
* 0061393A 895F 14 MOV DWORD PTR DS:[EDI+0x14],EBX
* 0061393D 894F 18 MOV DWORD PTR DS:[EDI+0x18],ECX
* 00613940 894F 1C MOV DWORD PTR DS:[EDI+0x1C],ECX
* 00613943 89EC MOV ESP,EBP
* 00613945 61 POPAD
* 00613946 C3 RETN
* 00613947 60 PUSHAD
* 00613948 89E5 MOV EBP,ESP
* 0061394A 83EC 18 SUB ESP,0x18
* 0061394D E8 59000000 CALL .006139AB
* 00613952 8B5D F8 MOV EBX,DWORD PTR SS:[EBP-0x8]
* 00613955 833B 01 CMP DWORD PTR DS:[EBX],0x1
* 00613958 75 2E JNZ SHORT .00613988
* 0061395A 31C9 XOR ECX,ECX
* 0061395C 890B MOV DWORD PTR DS:[EBX],ECX
* 0061395E 8B7B 0C MOV EDI,DWORD PTR DS:[EBX+0xC]
* 00613961 8B75 EC MOV ESI,DWORD PTR SS:[EBP-0x14]
* 00613964 8D49 08 LEA ECX,DWORD PTR DS:[ECX+0x8]
* 00613967 F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
* 00613969 8B7B 14 MOV EDI,DWORD PTR DS:[EBX+0x14]
* 0061396C 8B75 E8 MOV ESI,DWORD PTR SS:[EBP-0x18]
* 0061396F 8B4B 18 MOV ECX,DWORD PTR DS:[EBX+0x18]
* 00613972 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
* 00613974 8B43 04 MOV EAX,DWORD PTR DS:[EBX+0x4]
* 00613977 8B53 08 MOV EDX,DWORD PTR DS:[EBX+0x8]
* 0061397A 8910 MOV DWORD PTR DS:[EAX],EDX
* 0061397C 8D7B 04 LEA EDI,DWORD PTR DS:[EBX+0x4]
* 0061397F 31C0 XOR EAX,EAX
* 00613981 B9 40010000 MOV ECX,0x140
* 00613986 F3:AB REP STOS DWORD PTR ES:[EDI]
* 00613988 89EC MOV ESP,EBP
* 0061398A 61 POPAD
* 0061398B C3 RETN
* 0061398C 8B8CD6 A8D70500 MOV ECX,DWORD PTR DS:[ESI+EDX*8+0x5D7A8] ; jichi: #2 hook jumped here, execute the original instruction first
* 00613993 8B01 MOV EAX,DWORD PTR DS:[ECX] ; get dword split in ecx
* 00613995 3D 96010000 CMP EAX,0x196
* 0061399A 74 07 JE SHORT .006139A3 ; translate if split is 0x196 or 0x6e
* 0061399C 83F8 6E CMP EAX,0x6E
* 0061399F 74 02 JE SHORT .006139A3
* 006139A1 EB 07 JMP SHORT .006139AA
* 006139A3 E8 7AFEFFFF CALL .00613822
* 006139A8 8B01 MOV EAX,DWORD PTR DS:[ECX]
* 006139AA C3 RETN
* 006139AB 60 PUSHAD
* 006139AC C745 FC A8D70500 MOV DWORD PTR SS:[EBP-0x4],0x5D7A8
* 006139B3 EB 03 JMP SHORT .006139B8
* 006139B5 58 POP EAX
* 006139B6 EB 05 JMP SHORT .006139BD
* 006139B8 E8 F8FFFFFF CALL .006139B5
* 006139BD 2D BD392100 SUB EAX,0x2139BD
* 006139C2 0380 D4020000 ADD EAX,DWORD PTR DS:[EAX+0x2D4]
* 006139C8 B9 00010000 MOV ECX,0x100
* 006139CD 8D80 00400100 LEA EAX,DWORD PTR DS:[EAX+0x14000]
* 006139D3 8945 F8 MOV DWORD PTR SS:[EBP-0x8],EAX
* 006139D6 8D0401 LEA EAX,DWORD PTR DS:[ECX+EAX]
* 006139D9 8945 F4 MOV DWORD PTR SS:[EBP-0xC],EAX
* 006139DC 8D0401 LEA EAX,DWORD PTR DS:[ECX+EAX]
* 006139DF 8945 F0 MOV DWORD PTR SS:[EBP-0x10],EAX
* 006139E2 8D0401 LEA EAX,DWORD PTR DS:[ECX+EAX]
* 006139E5 8945 EC MOV DWORD PTR SS:[EBP-0x14],EAX
* 006139E8 8D0401 LEA EAX,DWORD PTR DS:[ECX+EAX]
* 006139EB 8945 E8 MOV DWORD PTR SS:[EBP-0x18],EAX
* 006139EE 61 POPAD
* 006139EF C3 RETN
* 006139F0 0000 ADD BYTE PTR DS:[EAX],AL
* 006139F2 0000 ADD BYTE PTR DS:[EAX],AL
* 006139F4 0000 ADD BYTE PTR DS:[EAX],AL
*/
bool InsertEushullyHook()
{
/*
ULONG addr = MemDbg::findLastCallerAddressAfterInt3((DWORD)::GetTextExtentPoint32A, processStartAddress, processStopAddress);
//GROWL_DWORD(addr);
if (!addr) {
ConsoleOutput("Eushully: failed");
return false;
}
*/
ULONG lastCaller = 0,
lastCall = 0;
auto fun = [&lastCaller, &lastCall](ULONG caller, ULONG call) -> bool {
lastCaller = caller;
lastCall = call;
return true; // find last caller && call
};
MemDbg::iterCallerAddressAfterInt3(fun, (ULONG)::GetTextExtentPoint32A, processStartAddress, processStopAddress);
if (!lastCaller)
return false;
//OtherHook
ULONG thisCaller = 0,
thisCall = 0,
prevCall = 0;
auto fun2 = [&thisCaller, &thisCall, &prevCall](ULONG caller, ULONG call) -> bool {
if (call - prevCall == 133) { // 0x0046e1f8 - 0x0046e173 = 133
thisCaller = caller;
thisCall = call;
return false; // stop iteration
}
prevCall = call;
return true; // continue iteration
};
MemDbg::iterCallerAddressAfterInt3(fun2, (ULONG)::GetGlyphOutlineA, processStartAddress, processStopAddress);
// BOOL GetTextExtentPoint32(
// _In_ HDC hdc,
// _In_ LPCTSTR lpString,
// _In_ int c,
// _Out_ LPSIZE lpSize
// );
enum stack { // current stack
//retaddr = 0 // esp[0] is the return address since this is the beginning of the function
arg1_hdc = 4 * 1 // 0x4
, arg2_lpString = 4 * 2 // 0x8
, arg3_lc = 4 * 3 // 0xc
, arg4_lpSize = 4 * 4 // 0x10
};
{
enum : DWORD { sig = 0x550010c2 };
enum { fun_offset = 3 };
for (auto addr = lastCaller; addr < lastCall; addr++)
if (*(DWORD *)addr == sig) {
lastCaller = addr + fun_offset;
break;
}
}
HookParam hp;
hp.address = lastCaller;
hp.type = USING_STRING|FIXING_SPLIT|EMBED_ABLE|EMBED_BEFORE_SIMPLE|EMBED_AFTER_NEW|EMBED_DYNA_SJIS; // merging all threads
hp.offset = arg2_lpString; // arg2 = 0x4 * 2
hp.hook_font=F_MultiByteToWideChar|F_GetTextExtentPoint32A|F_GetGlyphOutlineA|F_CreateFontA;
ConsoleOutput("INSERT Eushully");
bool succ=NewHook(hp, "ARCGameEngine");
if(thisCaller){
hp.address = thisCall;
hp.offset=get_stack(6);
succ|=NewHook(hp, "ARCGameEngine_other");
}
return succ;
}
bool Eushully::attach_function() {
return InsertEushullyHook();
}

View File

@ -0,0 +1,11 @@
#include"engine.h"
class Eushully:public ENGINE{
public:
Eushully(){
check_by=CHECK_BY::FILE;
check_by_target=L"AGERC.DLL";// 6/1/2014 jichi: Eushully, AGE.EXE
};
bool attach_function();
};

229
LunaHook/engine32/Exp.cpp Normal file
View File

@ -0,0 +1,229 @@
#include"Exp.h"
/** jichi 9/8/2014 EXP, http://www.exp-inc.jp
* Maker: EXP, 5pb
* Sample game: <EFBFBD>
*
* There are three matched memory addresses with SHIFT-JIS.
* The middle one is used as it is aligned with zeros.
* The memory address is fixed.
*
* There are three functions found using hardware breakpoints.
* The last one is used as the first two are looped.
*
* reladdr = 0x138020
*
* baseaddr = 0x00120000
*
* 0025801d cc int3
* 0025801e cc int3
* 0025801f cc int3
* 00258020 55 push ebp ; jichi: hook here
* 00258021 8bec mov ebp,esp
* 00258023 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
* 00258026 83ec 08 sub esp,0x8
* 00258029 85c0 test eax,eax
* 0025802b 0f84 d8000000 je .00258109
* 00258031 837d 10 00 cmp dword ptr ss:[ebp+0x10],0x0
* 00258035 0f84 ce000000 je .00258109
* 0025803b 8b10 mov edx,dword ptr ds:[eax] ; jichi: edx is the text
* 0025803d 8b45 0c mov eax,dword ptr ss:[ebp+0xc]
* 00258040 53 push ebx
* 00258041 56 push esi
* 00258042 c745 f8 00000000 mov dword ptr ss:[ebp-0x8],0x0
* 00258049 8945 fc mov dword ptr ss:[ebp-0x4],eax
* 0025804c 57 push edi
* 0025804d 8d49 00 lea ecx,dword ptr ds:[ecx]
* 00258050 8a0a mov cl,byte ptr ds:[edx] jichi: text in accessed in edx
* 00258052 8a45 14 mov al,byte ptr ss:[ebp+0x14]
* 00258055 3ac1 cmp al,cl
* 00258057 74 7a je short .002580d3
* 00258059 8b7d 10 mov edi,dword ptr ss:[ebp+0x10]
* 0025805c 8b5d fc mov ebx,dword ptr ss:[ebp-0x4]
* 0025805f 33f6 xor esi,esi
* 00258061 8bc2 mov eax,edx
* 00258063 80f9 81 cmp cl,0x81
* 00258066 72 05 jb short .0025806d
* 00258068 80f9 9f cmp cl,0x9f
* 0025806b 76 0a jbe short .00258077
* 0025806d 80f9 e0 cmp cl,0xe0
* 00258070 72 1d jb short .0025808f
* 00258072 80f9 fc cmp cl,0xfc
* 00258075 77 18 ja short .0025808f
* 00258077 8b45 fc mov eax,dword ptr ss:[ebp-0x4]
* 0025807a 85c0 test eax,eax
* 0025807c 74 05 je short .00258083
* 0025807e 8808 mov byte ptr ds:[eax],cl
* 00258080 8d58 01 lea ebx,dword ptr ds:[eax+0x1]
* 00258083 8b7d 10 mov edi,dword ptr ss:[ebp+0x10]
* 00258086 8d42 01 lea eax,dword ptr ds:[edx+0x1]
* 00258089 be 01000000 mov esi,0x1
* 0025808e 4f dec edi
* 0025808f 85ff test edi,edi
* 00258091 74 36 je short .002580c9
* 00258093 85db test ebx,ebx
* 00258095 74 04 je short .0025809b
* 00258097 8a08 mov cl,byte ptr ds:[eax]
* 00258099 880b mov byte ptr ds:[ebx],cl
* 0025809b 46 inc esi
* 0025809c 33c0 xor eax,eax
* 0025809e 66:3bc6 cmp ax,si
* 002580a1 7f 47 jg short .002580ea
* 002580a3 0fbfce movsx ecx,si
* 002580a6 03d1 add edx,ecx
* 002580a8 3945 fc cmp dword ptr ss:[ebp-0x4],eax
* 002580ab 74 03 je short .002580b0
* 002580ad 014d fc add dword ptr ss:[ebp-0x4],ecx
* 002580b0 294d 10 sub dword ptr ss:[ebp+0x10],ecx
* 002580b3 014d f8 add dword ptr ss:[ebp-0x8],ecx
* 002580b6 8a0a mov cl,byte ptr ds:[edx]
* 002580b8 80f9 0a cmp cl,0xa
* 002580bb 74 20 je short .002580dd
* 002580bd 80f9 0d cmp cl,0xd
* 002580c0 74 1b je short .002580dd
* 002580c2 3945 10 cmp dword ptr ss:[ebp+0x10],eax
* 002580c5 ^75 89 jnz short .00258050
* 002580c7 eb 21 jmp short .002580ea
* 002580c9 85db test ebx,ebx
* 002580cb 74 1d je short .002580ea
* 002580cd c643 ff 00 mov byte ptr ds:[ebx-0x1],0x0
* 002580d1 eb 17 jmp short .002580ea
* 002580d3 84c0 test al,al
* 002580d5 74 13 je short .002580ea
* 002580d7 42 inc edx
* 002580d8 ff45 f8 inc dword ptr ss:[ebp-0x8]
* 002580db eb 0d jmp short .002580ea
* 002580dd 8a42 01 mov al,byte ptr ds:[edx+0x1]
* 002580e0 42 inc edx
* 002580e1 3c 0a cmp al,0xa
* 002580e3 74 04 je short .002580e9
* 002580e5 3c 0d cmp al,0xd
* 002580e7 75 01 jnz short .002580ea
* 002580e9 42 inc edx
* 002580ea 8b45 fc mov eax,dword ptr ss:[ebp-0x4]
* 002580ed 5f pop edi
* 002580ee 5e pop esi
* 002580ef 5b pop ebx
* 002580f0 85c0 test eax,eax
* 002580f2 74 09 je short .002580fd
* 002580f4 837d 10 00 cmp dword ptr ss:[ebp+0x10],0x0
* 002580f8 74 03 je short .002580fd
* 002580fa c600 00 mov byte ptr ds:[eax],0x0
* 002580fd 8b4d 08 mov ecx,dword ptr ss:[ebp+0x8]
* 00258100 8b45 f8 mov eax,dword ptr ss:[ebp-0x8]
* 00258103 8911 mov dword ptr ds:[ecx],edx
* 00258105 8be5 mov esp,ebp
* 00258107 5d pop ebp
* 00258108 c3 retn
* 00258109 33c0 xor eax,eax
* 0025810b 8be5 mov esp,ebp
* 0025810d 5d pop ebp
* 0025810e c3 retn
* 0025810f cc int3
*
* Stack:
* 0f14f87c 00279177 return to .00279177 from .00258020
* 0f14f880 0f14f8b0 ; arg1 address of the text's pointer
* 0f14f884 0f14f8c0 ; arg2 pointed to zero, maybe a buffer
* 0f14f888 00000047 ; arg3 it is zero if no text, this value might be text size + 1
* 0f14f88c ffffff80 ; constant, used as split
* 0f14f890 005768c8 .005768c8
* 0f14f894 02924340 ; text is at 02924350
* 0f14f898 00000001 ; this might also be a good split
* 0f14f89c 1b520020
* 0f14f8a0 00000000
* 0f14f8a4 00000000
* 0f14f8a8 029245fc
* 0f14f8ac 0004bfd3
* 0f14f8b0 0f14fae0
* 0f14f8b4 00000000
* 0f14f8b8 00000000
* 0f14f8bc 02924340
* 0f14f8c0 00000000
*
* Registers:
* eax 0f14f8c0 ; floating at runtime
* ecx 0f14f8b0; floating at runtime
* edx 00000000
* ebx 0f14fae0; floating at runtime
* esp 0f14f87c; floating at runtime
* ebp 0f14facc; floating at runtime
* esi 00000047
* edi 02924340 ; text is in 02924350
* eip 00258020 .00258020
*
* Memory access pattern:
* For long sentences, it first render the first line, then the second line, and so on.
* So, the second line is a subtext of the entire dialog.
*/
static void SpecialHookExp(hook_stack* stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t*len)
{
static DWORD lasttext;
// 00258020 55 push ebp ; jichi: hook here
// 00258021 8bec mov ebp,esp
// 00258023 8b45 08 mov eax,dword ptr ss:[ebp+0x8] ; jichi: move arg1 to eax
// 00258029 85c0 test eax,eax ; check if text is null
// 0025802b 0f84 d8000000 je .00258109
// 00258031 837d 10 00 cmp dword ptr ss:[ebp+0x10],0x0 ; jichi: compare 0 with arg3, which is size+1
// 00258035 0f84 ce000000 je .00258109
// 0025803b 8b10 mov edx,dword ptr ds:[eax] ; move text address to edx
DWORD arg1 = stack->stack[1], // mov eax,dword ptr ss:[ebp+0x8]
arg3 = stack->stack[3]; // size - 1
if (arg1 && arg3)
if (DWORD text = *(DWORD *)arg1)
if (!(text > lasttext && text < lasttext + VNR_TEXT_CAPACITY)) { // text is not a subtext of lastText
*data = lasttext = text; // mov edx,dword ptr ds:[eax]
//*len = arg3 - 1; // the last char is the '\0', so -1, but this value is not reliable
*len = ::strlen((LPCSTR)text);
// Registers are not used as split as all of them are floating at runtime
//*split = argof(4, esp_base); // arg4, always -8, this will merge all threads and result in repetition
*split = stack->stack[7]; // reduce repetition, but still have sub-text repeat
}
}
bool InsertExpHook()
{
const BYTE bytes[] = {
0x55, // 00258020 55 push ebp ; jichi: hook here, function starts, text in [arg1], size+1 in arg3
0x8b,0xec, // 00258021 8bec mov ebp,esp
0x8b,0x45, 0x08, // 00258023 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
0x83,0xec, 0x08, // 00258026 83ec 08 sub esp,0x8
0x85,0xc0, // 00258029 85c0 test eax,eax
0x0f,0x84, XX4, // 0025802b 0f84 d8000000 je .00258109
0x83,0x7d, 0x10, 0x00, // 00258031 837d 10 00 cmp dword ptr ss:[ebp+0x10],0x0
0x0f,0x84, XX4, // 00258035 0f84 ce000000 je .00258109
0x8b,0x10, // 0025803b 8b10 mov edx,dword ptr ds:[eax] ; jichi: edx is the text
0x8b,0x45, 0x0c, // 0025803d 8b45 0c mov eax,dword ptr ss:[ebp+0xc]
0x53, // 00258040 53 push ebx
0x56, // 00258041 56 push esi
0xc7,0x45, 0xf8, 0x00,0x00,0x00,0x00, // 00258042 c745 f8 00000000 mov dword ptr ss:[ebp-0x8],0x0
0x89,0x45, 0xfc, // 00258049 8945 fc mov dword ptr ss:[ebp-0x4],eax
0x57, // 0025804c 57 push edi
0x8d,0x49, 0x00, // 0025804d 8d49 00 lea ecx,dword ptr ds:[ecx]
0x8a,0x0a // 00258050 8a0a mov cl,byte ptr ds:[edx] ; jichi: text accessed in edx
};
enum { addr_offset = 0 };
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
//GROWL_DWORD(addr);
if (!addr) {
ConsoleOutput("EXP: pattern not found");
return false;
}
HookParam hp;
hp.address = addr + addr_offset;
hp.type = NO_CONTEXT|USING_STRING; // NO_CONTEXT to get rid of floating address
hp.text_fun = SpecialHookExp;
ConsoleOutput("INSERT EXP");
ConsoleOutput("EXP: disable GDI hooks"); // There are no GDI functions hooked though
return NewHook(hp, "EXP"); // FIXME: text displayed line by line
}
bool Exp::attach_function() {
return InsertExpHook();
}

11
LunaHook/engine32/Exp.h Normal file
View File

@ -0,0 +1,11 @@
#include"engine.h"
class Exp:public ENGINE{
public:
Exp(){
check_by=CHECK_BY::FILE;
check_by_target=L"model\\*.hed";
};
bool attach_function();
};

533
LunaHook/engine32/FVP.cpp Normal file
View File

@ -0,0 +1,533 @@
#include"engine32/FVP.h"
namespace { // unnamed
namespace ScenarioHook {
namespace Private {
/**
* FIXME: Scenario/name/history text cannot be distinguished
*
* Sample game:
*
* Scenario:
*
* 0012FD44 0043CB56 RETURN to .0043CB56 from .00433610
* 0012FD48 0B711390
* 0012FD4C 024FE43C
* 0012FD50 02541120
* 0012FD54 024FEC50
* 0012FD58 00000000
* 0012FD5C 024FE43C
* 0012FD60 0044598E RETURN to .0044598E
* 0012FD64 024FE53C
* 0012FD68 00000001
* 0012FD6C 024FE43C
*
* EAX 0000000E
* ECX 01B99750
* EDX 0B711391
* EBX 01E7047C
* ESP 0012FD44
* EBP 01B99750
* ESI 0B711390
* EDI 024FE53C
* EIP 00433610 .00433610
*
* ecx:
* 01B99750 F4 D8 45 00 A8 D5 45 00 A0 2B 8E 0A 00 00 00 00 E.E.+....
* 01B99760 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* 01B99770 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* 01B99780 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
*
* [ecx+8]
* 0A8E2BA0 B0 51 A6 63 C0 83 4C 04 15 00 00 00 03 00 00 00 Qヲcタキ......
* 0A8E2BB0 00 00 00 0C 02 00 00 00 00 00 00 00 00 00 00 00 ...............
* 0A8E2BC0 00 04 00 00 80 00 00 00 00 00 00 00 00 00 00 00 ...€...........
* 0A8E2BD0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
*
* 0012FD44 0043CB56 RETURN to .0043CB56 from .00433610
* 0012FD48 0B6CE660
* 0012FD4C 024FE43C
* 0012FD50 02541120
* 0012FD54 024FEC50
* 0012FD58 00000000
* 0012FD5C 024FE43C
* 0012FD60 0044598E RETURN to .0044598E
* 0012FD64 024FE53C
* 0012FD68 00000001
* 0012FD6C 024FE43C
* 0012FD70 00597669 d3dx9_31.00597669
* 0012FD74 00000000
* 0012FD78 004454D2 RETURN to .004454D2
* 0012FD7C 01E7047C
* 0012FD80 0043F67F RETURN to .0043F67F from .00445440
* 0012FD84 76F32EB2 user32.PeekMessageA
* 0012FD88 76F52B5A user32.TranslateAcceleratorA
* 0012FD8C 76F366E3 user32.IsIconic
*
* 0B6D9118 06 06 07 07 07 07 08 08 07 08 09 0A 0A 08 09 09 .....
* 0B6D9128 37 5F 7C 3B E8 B7 02 00 D8 FF 61 02 30 8C 70 0B 7_|;.a0
* 0B6D9138 35 5E 75 31 EF B7 02 08 98 7C 58 02 20 2F B9 01 5^u1X /
* 0B6D9148 0B 00 00 00 C0 D0 E0 F0 A8 9A C7 23 00 00 00 8D ...#...
* 0B6D9158 81 40 82 BB 82 CC 83 79 81 5B 83 57 82 AA 82 CF  
* 0B6D9168 82 E7 82 CF 82 E7 82 C6 97 AC 82 B3 82 EA 82 E9
* 0B6D9178 81 42 00 00 00 00 00 00 B2 9A C7 23 00 00 00 8D ......#...
*
* 0B6D9188 81 40 82 BB 82 CC 83 79 81 5B 83 57 82 AA 82 CF  
* 0B6D9198 82 E7 82 CF 82 E7 82 C6 97 AC 82 B3 82 EA 82 E9
* 0B6D91A8 81 42 00 00 00 00 00 00 B4 9A C7 23 00 00 00 80 ......#...€
* 0B6D91B8 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ...............
* 0B6D91C8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* 0B6D91D8 00 00 00 00 00 00 00 00 BE 9A C7 23 00 00 00 80 ........#...€
* 0B6D91E8 1A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ...............
* 0B6D91F8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* 0B6D9208 00 00 00 00 00 00 00 00 C0 9A C7 23 00 00 00 80 ........#...€
* 0B6D9218 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ...............
* 0B6D9228 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* 0B6D9238 00 00 00 00 00 00 00 00 CA 9A C7 23 00 00 00 80 ........#...€
* 0B6D9248 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &...............
* 0B6D9258 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* 0B6D9268 00 00 00 00 00 00 00 00 CC 9A C7 23 00 00 00 80 ........#...€
*
* History:
*
* 0012FD44 0043CB56 RETURN to .0043CB56 from .00433610
* 0012FD48 0B7113D8
* 0012FD4C 024FE43C
* 0012FD50 02541120
* 0012FD54 024FEC50
* 0012FD58 00000000
* 0012FD5C 024FE43C
* 0012FD60 0044598E RETURN to .0044598E
* 0012FD64 024FE5CC
* 0012FD68 00000001
* 0012FD6C 024FE43C
*
* 0B6D9118 06 06 07 07 07 07 08 08 07 08 09 0A 0A 08 09 09 .....
* 0B6D9128 37 5F 7C 3B E8 B7 02 00 D8 FF 61 02 30 8C 70 0B 7_|;.a0
* 0B6D9138 35 5E 75 31 EF B7 02 08 98 7C 58 02 20 2F B9 01 5^u1X /
* 0B6D9148 0B 00 00 00 C0 D0 E0 F0 A8 9A C7 23 00 00 00 8D ...#...
* 0B6D9158 81 40 82 BB 82 CC 83 79 81 5B 83 57 82 AA 82 CF  
* 0B6D9168 82 E7 82 CF 82 E7 82 C6 97 AC 82 B3 82 EA 82 E9
* 0B6D9178 81 42 00 00 00 00 00 00 B2 9A C7 23 00 00 00 8D ......#...
* 0B6D9188 81 40 82 BB 82 CC 83 79 81 5B 83 57 82 AA 82 CF  
* 0B6D9198 82 E7 82 CF 82 E7 82 C6 97 AC 82 B3 82 EA 82 E9
* 0B6D91A8 81 42 00 00 00 00 00 00 B4 9A C7 23 00 00 00 8A ......#...
* 0B6D91B8 01 00 40 81 BB 82 CC 82 79 83 5B 81 57 83 AA 82 .@Μ
* 0B6D91C8 CF 82 E7 82 CF 82 E7 82 C6 82 AC 97 B3 82 EA 82
* 0B6D91D8 E9 82 42 81 7E 00 00 00 BE 9A C7 23 00 00 00 8D B×...#...
* 0B6D91E8 81 40 82 BB 82 CC 83 79 81 5B 83 57 82 AA 82 CF  
* 0B6D91F8 82 E7 82 CF 82 E7 82 C6 97 AC 82 B3 82 EA 82 E9
* 0B6D9208 81 42 00 00 00 00 00 00 C0 9A C7 23 00 00 00 8D ......#...
*
* 0B6D9218 81 40 82 BB 82 CC 83 79 81 5B 83 57 82 AA 82 CF  
* 0B6D9228 82 E7 82 CF 82 E7 82 C6 97 AC 82 B3 82 EA 82 E9
* 0B6D9238 81 42 00 00 00 00 00 00 CA 9A C7 23 00 00 00 80 ......#...€
* 0B6D9248 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 &...............
* 0B6D9258 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* 0B6D9268 00 00 00 00 00 00 00 00 CC 9A C7 23 00 00 00 80 ........#...€
*
* ecx:
* 02536A88 F4 D8 45 00 A8 D5 45 00 80 39 2F 04 00 00 00 00 E.E.€9/....
* 02536A98 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* 02536AA8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* 02536AB8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
*
* [ecx+8]
* 042F3980 B0 51 A6 63 A0 1A E2 09 15 00 00 00 03 00 00 00 Qヲc......
* 042F3990 00 00 00 0C 02 00 00 00 00 00 00 00 00 00 00 00 ...............
* 042F39A0 00 04 00 00 80 00 00 00 00 00 00 00 00 00 00 00 ...€...........
* 042F39B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* 042F39C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
*
* EAX 0000000E
* ECX 02537740
* EDX 0B7113D9
* EBX 01E7047C
* ESP 0012FD44
* EBP 02537740
* ESI 0B7113D8
* EDI 024FE5CC
* EIP 00433610 .00433610
*
* 0012FD44 0043CB56 RETURN to .0043CB56 from .00433610
* 0012FD48 0B6CEA20
* 0012FD4C 024FE43C
* 0012FD50 02541120
* 0012FD54 024FEC50
* 0012FD58 00000000
* 0012FD5C 024FE43C
* 0012FD60 0044598E RETURN to .0044598E
* 0012FD64 024FE5CC
* 0012FD68 00000001
* 0012FD6C 024FE43C
* 0012FD70 005A44DE d3dx9_31.005A44DE
* 0012FD74 00000000
* 0012FD78 004454D2 RETURN to .004454D2
* 0012FD7C 01E7047C
* 0012FD80 0043F67F RETURN to .0043F67F from .00445440
* 0012FD84 76F32EB2 user32.PeekMessageA
* 0012FD88 76F52B5A user32.TranslateAcceleratorA
* 0012FD8C 76F366E3 user32.IsIconic
*
* Config message:
*
* 0012FD44 0043CB56 RETURN to .0043CB56 from .00433610
* 0012FD48 026A1180
* 0012FD4C 02508B94
* 0012FD50 02541120
* 0012FD54 025093A8
* 0012FD58 00000000
* 0012FD5C 02508B94
* 0012FD60 0044598E RETURN to .0044598E
* 0012FD64 02508BA4
* 0012FD68 00000001
* 0012FD6C 02508B94
* 0012FD70 005AC45E d3dx9_31.005AC45E
* 0012FD74 00000000
* 0012FD78 004454D2 RETURN to .004454D2
* 0012FD7C 01E7047C
* 0012FD80 0043F67F RETURN to .0043F67F from .00445440
* 0012FD84 76F32EB2 user32.PeekMessageA
* 0012FD88 76F52B5A user32.TranslateAcceleratorA
* 0012FD8C 76F366E3 user32.IsIconic
*
* EAX 0000001E
* ECX 0253A4F8
* EDX 026A1181
* EBX 01E7047C
* ESP 0012FD44
* EBP 0253A4F8
* ESI 026A1180
* EDI 02508BA4
* EIP 00433610 .00433610
*
* ecx:
* 0253A4F8 F4 D8 45 00 A8 D5 45 00 00 D4 2F 04 00 00 00 00 E.E../....
* 0253A508 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* 0253A518 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
* 0253A528 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
*
* [ecx+8]
* 042FD400 B0 51 A6 63 C0 18 E2 09 15 00 00 00 03 00 00 00 Qヲcタ......
* 042FD410 00 00 00 0C 02 00 00 00 00 00 00 00 00 00 00 00 ...............
* 042FD420 00 02 00 00 20 00 00 00 00 00 00 00 00 00 00 00 ... ...........
* 042FD430 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
*
* 026A1160 25 07 4F 11 08 00 10 FE 0C 0A 1D 0C 01 1A 05 04 %O....
* 026A1170 04 01 00 00 0C 01 07 90 11 08 00 0F 7C 05 0E 1F ....|
*
* 026A1180 83 81 83 62 83 5A 81 5B 83 57 91 AC 93 78 83 54
* 026A1190 83 93 83 76 83 8B 83 65 83 4C 83 58 83 67 00 03 .
* 026A11A0 7B 00 03 85 00 0F 7C 05 03 6F 00 06 54 11 08 00 {.|o.T.
*
*/
// bool hookBefore(winhook::hook_stack *s)
// {
// static std::string data_; // persistent storage, which makes this function not thread-safe
// LPCSTR text = (LPCSTR)s->stack[1]; // arg1
// if (!text || !*text)
// return true;
// //auto role = Engine::OtherRole;
// //if (text[-2] == 0 && text[-3] == 0 && text[-4] == 0) // 234 should be zero for text on the heap?
// // role = Engine::ScenarioRole;
// auto role = Engine::ScenarioRole;
// auto retaddr = s->stack[0]; // retaddr, there is only one retaddr anyway
// //auto split = s->ecx;
// //if (Engine::isAddressReadable(split))
// // split = *(DWORD *)(split + 8);
// auto sig = Engine::hashThreadSignature(role, retaddr);
// data_ = EngineController::instance()->dispatchTextASTD(text, role, sig);
// s->stack[1] = (ULONG)data_.c_str(); // reset arg1
// return true;
// }
} // namespace Private
/** jichi 7/28/2015
* Sample game:
* Text can also be extracted in both GetGlyphOutlineA and lstrlenA
* See also: http://capita.tistory.com/m/post/267
*
* 0043360E CC INT3
* 0043360F CC INT3
* 00433610 83EC 0C SUB ESP,0xC
* 00433613 55 PUSH EBP
* 00433614 56 PUSH ESI
* 00433615 57 PUSH EDI
* 00433616 8BF9 MOV EDI,ECX
* 00433618 8D4424 0C LEA EAX,DWORD PTR SS:[ESP+0xC]
* 0043361C 8DB7 74050000 LEA ESI,DWORD PTR DS:[EDI+0x574]
* 00433622 50 PUSH EAX
* 00433623 8BCE MOV ECX,ESI
* 00433625 897C24 18 MOV DWORD PTR SS:[ESP+0x18],EDI
* 00433629 C74424 10 010000>MOV DWORD PTR SS:[ESP+0x10],0x1
* 00433631 E8 8AEFFFFF CALL .004325C0
* 00433636 8D8F 90050000 LEA ECX,DWORD PTR DS:[EDI+0x590]
* 0043363C 51 PUSH ECX
* 0043363D 8D8F B8050000 LEA ECX,DWORD PTR DS:[EDI+0x5B8]
* 00433643 E8 E8EFFFFF CALL .00432630
* 00433648 8B6C24 1C MOV EBP,DWORD PTR SS:[ESP+0x1C]
* 0043364C 8A45 00 MOV AL,BYTE PTR SS:[EBP]
* 0043364F 84C0 TEST AL,AL
* 00433651 0F84 8C000000 JE .004336E3
* 00433657 53 PUSH EBX
* 00433658 EB 06 JMP SHORT .00433660
* 0043365A 8D9B 00000000 LEA EBX,DWORD PTR DS:[EBX]
* 00433660 66:0FB6D0 MOVZX DX,AL
* 00433664 0FB7DA MOVZX EBX,DX
* 00433667 0FB7C3 MOVZX EAX,BX
* 0043366A 50 PUSH EAX
* 0043366B 895C24 24 MOV DWORD PTR SS:[ESP+0x24],EBX
* 0043366F 45 INC EBP
* 00433670 E8 DA4D0100 CALL .0044844F
* 00433675 83C4 04 ADD ESP,0x4
* 00433678 85C0 TEST EAX,EAX
* 0043367A 74 13 JE SHORT .0043368F
* 0043367C 66:0FB64D 00 MOVZX CX,BYTE PTR SS:[EBP]
* 00433681 C1E3 08 SHL EBX,0x8
* 00433684 66:0BD9 OR BX,CX
* 00433687 0FB7DB MOVZX EBX,BX
* 0043368A 895C24 20 MOV DWORD PTR SS:[ESP+0x20],EBX
* 0043368E 45 INC EBP
* 0043368F 8B4E 0C MOV ECX,DWORD PTR DS:[ESI+0xC]
* 00433692 85C9 TEST ECX,ECX
* 00433694 75 04 JNZ SHORT .0043369A
* 00433696 33C0 XOR EAX,EAX
* 00433698 EB 07 JMP SHORT .004336A1
* 0043369A 8B46 14 MOV EAX,DWORD PTR DS:[ESI+0x14]
* 0043369D 2BC1 SUB EAX,ECX
* 0043369F D1F8 SAR EAX,1
* 004336A1 8B7E 10 MOV EDI,DWORD PTR DS:[ESI+0x10]
* 004336A4 8BD7 MOV EDX,EDI
* 004336A6 2BD1 SUB EDX,ECX
* 004336A8 D1FA SAR EDX,1
* 004336AA 3BD0 CMP EDX,EAX
* 004336AC 73 0B JNB SHORT .004336B9
* 004336AE 66:891F MOV WORD PTR DS:[EDI],BX
* 004336B1 83C7 02 ADD EDI,0x2
* 004336B4 897E 10 MOV DWORD PTR DS:[ESI+0x10],EDI
* 004336B7 EB 1E JMP SHORT .004336D7
* 004336B9 3BCF CMP ECX,EDI
* 004336BB 76 05 JBE SHORT .004336C2
* 004336BD E8 644A0100 CALL .00448126
* 004336C2 8B06 MOV EAX,DWORD PTR DS:[ESI]
* 004336C4 8D4C24 20 LEA ECX,DWORD PTR SS:[ESP+0x20]
* 004336C8 51 PUSH ECX
* 004336C9 57 PUSH EDI
* 004336CA 50 PUSH EAX
* 004336CB 8D5424 1C LEA EDX,DWORD PTR SS:[ESP+0x1C]
* 004336CF 52 PUSH EDX
* 004336D0 8BCE MOV ECX,ESI
* 004336D2 E8 F9E8FFFF CALL .00431FD0
* 004336D7 8A45 00 MOV AL,BYTE PTR SS:[EBP]
* 004336DA 84C0 TEST AL,AL
* 004336DC ^75 82 JNZ SHORT .00433660
* 004336DE 8B7C24 18 MOV EDI,DWORD PTR SS:[ESP+0x18]
* 004336E2 5B POP EBX
* 004336E3 8D4424 1C LEA EAX,DWORD PTR SS:[ESP+0x1C]
* 004336E7 50 PUSH EAX
* 004336E8 8BCE MOV ECX,ESI
* 004336EA C74424 20 7E0000>MOV DWORD PTR SS:[ESP+0x20],0x7E
* 004336F2 E8 C9EEFFFF CALL .004325C0
* 004336F7 6A 01 PUSH 0x1
* 004336F9 6A 00 PUSH 0x0
* 004336FB 6A 00 PUSH 0x0
* 004336FD 8BCF MOV ECX,EDI
* 004336FF E8 5CF4FFFF CALL .00432B60
* 00433704 5F POP EDI
* 00433705 5E POP ESI
* 00433706 5D POP EBP
* 00433707 83C4 0C ADD ESP,0xC
* 0043370A C2 0400 RETN 0x4
* 0043370D CC INT3
* 0043370E CC INT3
* 0043370F CC INT3
*
* Sample game:
* 0042EAAD CC INT3
* 0042EAAE CC INT3
* 0042EAAF CC INT3
* 0042EAB0 83EC 0C SUB ESP,0xC
* 0042EAB3 55 PUSH EBP
* 0042EAB4 56 PUSH ESI
* 0042EAB5 57 PUSH EDI
* 0042EAB6 8BF9 MOV EDI,ECX
* 0042EAB8 8D4424 0C LEA EAX,DWORD PTR SS:[ESP+0xC]
* 0042EABC 8DB7 A4000000 LEA ESI,DWORD PTR DS:[EDI+0xA4]
* 0042EAC2 50 PUSH EAX
* 0042EAC3 8BCE MOV ECX,ESI
* 0042EAC5 897C24 18 MOV DWORD PTR SS:[ESP+0x18],EDI
* 0042EAC9 C74424 10 010000>MOV DWORD PTR SS:[ESP+0x10],0x1
* 0042EAD1 E8 5AF2FFFF CALL .0042DD30
* 0042EAD6 8D8F B8000000 LEA ECX,DWORD PTR DS:[EDI+0xB8]
* 0042EADC 51 PUSH ECX
* 0042EADD 8D8F E0000000 LEA ECX,DWORD PTR DS:[EDI+0xE0]
* 0042EAE3 E8 B8F2FFFF CALL .0042DDA0
* 0042EAE8 8B6C24 1C MOV EBP,DWORD PTR SS:[ESP+0x1C]
* 0042EAEC 8A45 00 MOV AL,BYTE PTR SS:[EBP]
* 0042EAEF 84C0 TEST AL,AL
* 0042EAF1 0F84 96000000 JE .0042EB8D
* 0042EAF7 53 PUSH EBX
* 0042EAF8 EB 06 JMP SHORT .0042EB00
* 0042EAFA 8D9B 00000000 LEA EBX,DWORD PTR DS:[EBX]
* 0042EB00 66:0FB6D0 MOVZX DX,AL
* 0042EB04 0FB7DA MOVZX EBX,DX
* 0042EB07 0FB7C3 MOVZX EAX,BX
* 0042EB0A 50 PUSH EAX
* 0042EB0B 895C24 24 MOV DWORD PTR SS:[ESP+0x24],EBX
* 0042EB0F 83C5 01 ADD EBP,0x1
* 0042EB12 E8 22430100 CALL .00442E39
* 0042EB17 83C4 04 ADD ESP,0x4
* 0042EB1A 85C0 TEST EAX,EAX
* 0042EB1C 74 11 JE SHORT .0042EB2F
* 0042EB1E 33C9 XOR ECX,ECX
* 0042EB20 8AEB MOV CH,BL
* 0042EB22 83C5 01 ADD EBP,0x1
* 0042EB25 8A4D FF MOV CL,BYTE PTR SS:[EBP-0x1]
* 0042EB28 0FB7D9 MOVZX EBX,CX
* 0042EB2B 895C24 20 MOV DWORD PTR SS:[ESP+0x20],EBX
* 0042EB2F 8B56 04 MOV EDX,DWORD PTR DS:[ESI+0x4]
* 0042EB32 85D2 TEST EDX,EDX
* 0042EB34 75 04 JNZ SHORT .0042EB3A
* 0042EB36 33C9 XOR ECX,ECX
* 0042EB38 EB 07 JMP SHORT .0042EB41
* 0042EB3A 8B4E 08 MOV ECX,DWORD PTR DS:[ESI+0x8]
* 0042EB3D 2BCA SUB ECX,EDX
* 0042EB3F D1F9 SAR ECX,1
* 0042EB41 85D2 TEST EDX,EDX
* 0042EB43 74 19 JE SHORT .0042EB5E
* 0042EB45 8B46 0C MOV EAX,DWORD PTR DS:[ESI+0xC]
* 0042EB48 2BC2 SUB EAX,EDX
* 0042EB4A D1F8 SAR EAX,1
* 0042EB4C 3BC8 CMP ECX,EAX
* 0042EB4E 73 0E JNB SHORT .0042EB5E
* 0042EB50 8B46 08 MOV EAX,DWORD PTR DS:[ESI+0x8]
* 0042EB53 66:8918 MOV WORD PTR DS:[EAX],BX
* 0042EB56 83C0 02 ADD EAX,0x2
* 0042EB59 8946 08 MOV DWORD PTR DS:[ESI+0x8],EAX
* 0042EB5C EB 23 JMP SHORT .0042EB81
* 0042EB5E 8B7E 08 MOV EDI,DWORD PTR DS:[ESI+0x8]
* 0042EB61 3BD7 CMP EDX,EDI
* 0042EB63 76 05 JBE SHORT .0042EB6A
* 0042EB65 E8 6E420100 CALL .00442DD8
* 0042EB6A 8D5424 20 LEA EDX,DWORD PTR SS:[ESP+0x20]
* 0042EB6E 52 PUSH EDX
* 0042EB6F 57 PUSH EDI
* 0042EB70 56 PUSH ESI
* 0042EB71 8D4424 1C LEA EAX,DWORD PTR SS:[ESP+0x1C]
* 0042EB75 50 PUSH EAX
* 0042EB76 8BCE MOV ECX,ESI
* 0042EB78 E8 83ECFFFF CALL .0042D800
* 0042EB7D 8B7C24 18 MOV EDI,DWORD PTR SS:[ESP+0x18]
* 0042EB81 8A45 00 MOV AL,BYTE PTR SS:[EBP]
* 0042EB84 84C0 TEST AL,AL
* 0042EB86 ^0F85 74FFFFFF JNZ .0042EB00
* 0042EB8C 5B POP EBX
* 0042EB8D 8D4C24 1C LEA ECX,DWORD PTR SS:[ESP+0x1C]
* 0042EB91 51 PUSH ECX
* 0042EB92 8BCE MOV ECX,ESI
* 0042EB94 C74424 20 7E0000>MOV DWORD PTR SS:[ESP+0x20],0x7E
* 0042EB9C E8 8FF1FFFF CALL .0042DD30
* 0042EBA1 6A 01 PUSH 0x1
* 0042EBA3 6A 00 PUSH 0x0
* 0042EBA5 6A 00 PUSH 0x0
* 0042EBA7 8BCF MOV ECX,EDI
* 0042EBA9 E8 72F4FFFF CALL .0042E020
* 0042EBAE 5F POP EDI
* 0042EBAF 5E POP ESI
* 0042EBB0 5D POP EBP
* 0042EBB1 83C4 0C ADD ESP,0xC
* 0042EBB4 C2 0400 RETN 0x4
* 0042EBB7 CC INT3
* 0042EBB8 CC INT3
* 0042EBB9 CC INT3
* 0042EBBA CC INT3
* 0042EBBB CC INT3
* 0042EBBC CC INT3
*/
bool attach(ULONG startAddress, ULONG stopAddress)
{
const uint8_t bytes[] = {
0x53, // 00433657 53 push ebx
0xeb, 0x06, // 00433658 eb 06 jmp short .00433660
0x8d,0x9b, 0x00,0x00,0x00,0x00 // 0043365a 8d9b 00000000 lea ebx,dword ptr ds:[ebx]
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
if (!addr)
return false;
// 0042EAAD CC INT3
// 0042EAAE CC INT3
// 0042EAAF CC INT3
// 0042EAB0 83EC 0C SUB ESP,0xC
// 0042EAB3 55 PUSH EBP
// 0042EAB4 56 PUSH ESI
//
// 00433657 - 00433610 = 71, function not aligned
addr = MemDbg::findEnclosingFunctionBeforeDword(0x550cec83, addr, MemDbg::MaximumFunctionSize, 1); // step = 1
//addr = MemDbg::findEnclosingAlignedFunction(addr); // does not work
//addr = MemDbg::findEnclosingFunctionAfterInt3(addr); // does not work as there is not enough int3
if (!addr)
return false;
HookParam hp;
hp.address=addr;
hp.offset=get_stack(1);
hp.type=USING_STRING|EMBED_ABLE|EMBED_AFTER_NEW|EMBED_BEFORE_SIMPLE|EMBED_DYNA_SJIS;
hp.hook_font=F_DrawTextA|F_GetGlyphOutlineA;
hp.filter_fun=[](void* data, size_t* len, HookParam* hp){
static std::regex rx("\\[.+\\|(.+?)\\]");
auto x= std::regex_replace(std::string((LPSTR)data,*len), rx, "$1");
strcpy((LPSTR)data,x.c_str());
*len=x.size();return true;
};
return NewHook(hp,"EmbedFVP");
}
} // namespace ScenarioHook
} // unnamed namespace
/** Public class */
bool FVP::attach_function()
{
ULONG startAddress, stopAddress;
if (!ScenarioHook::attach(processStartAddress, processStopAddress))
return false;
// HijackManager::instance()->attachFunction((ULONG)::GetGlyphOutlineA); // for new game: 紅い瞳に映るセカイ
// HijackManager::instance()->attachFunction((ULONG)::DrawTextA); // for old game: 星空のメモリア
//HijackManager::instance()->attachFunction((ULONG)::CreateFontA);
return true;
}
/**
* Get rid of ruby. Examples:
* [|]
*/
//QString FVPEngine::rubyCreate(const QString &rb, const QString &rt)
//{
// static QString fmt = "[%2|%1]";
// return fmt.arg(rb, rt);
//}
//
//// Remove furigana in scenario thread.
//QString FVPEngine::rubyRemove(const QString &text)
//{
// if (!text.contains('|'))
// return text;
// static QRegExp rx("\\[.+\\|(.+)\\]");
// if (!rx.isMinimal())
// rx.setMinimal(true);
// return QString(text).replace(rx, "\\1");
//}
// std::wstring FVPEngine::rubyRemove(const std::wstring& text)
// {
// if (text.find(L'|') == std::wstring::npos)
// return text;
// static std::wregex rx(L"\\[.+\\|(.+?)\\]");
// return std::regex_replace(text, rx, L"$1");
// }
// EOF

11
LunaHook/engine32/FVP.h Normal file
View File

@ -0,0 +1,11 @@
#include"engine.h"
class FVP:public ENGINE{
public:
FVP(){
is_engine_certain=false;
check_by=CHECK_BY::FILE;
check_by_target=L"*.hcb";
};
bool attach_function();
};

View File

@ -0,0 +1,147 @@
#include"FocasLens.h"
/** jichi 2/6/2015 FocasLens (Touhou)
* Sample game: [141227] [FocasLens] <EFBFBD>
*
* Debugging method:
* 1. Find first matched text, which has stable address
* 2. Insert WRITE hw break point
* 3. Find where the text is assigned
*
* The game also invokes GDI functions (GetGlyphOutlineA), where the access is cached and looped.
*
* Issues:
* - This hook cannot find name thread
* - Selected character name is hard-coded to the thread
*
* 001faaed cc int3
* 001faaee cc int3
* 001faaef cc int3
* 001faaf0 55 push ebp
* 001faaf1 8bec mov ebp,esp
* 001faaf3 51 push ecx
* 001faaf4 53 push ebx
* 001faaf5 56 push esi
* 001faaf6 57 push edi
* 001faaf7 8bf0 mov esi,eax
* 001faaf9 e8 98281500 call .0034d396
* 001faafe 50 push eax
* 001faaff a1 b08bb100 mov eax,dword ptr ds:[0xb18bb0]
* 001fab04 03c6 add eax,esi
* 001fab06 50 push eax
* 001fab07 e8 9b241500 call .0034cfa7
* 001fab0c 8b0d e88bb100 mov ecx,dword ptr ds:[0xb18be8]
* 001fab12 8b3d b08bb100 mov edi,dword ptr ds:[0xb18bb0]
* 001fab18 83c1 f7 add ecx,-0x9
* 001fab1b 83c4 08 add esp,0x8
* 001fab1e 8bd8 mov ebx,eax
* 001fab20 390d ec8bb100 cmp dword ptr ds:[0xb18bec],ecx
* 001fab26 7c 65 jl short .001fab8d
* 001fab28 803c37 20 cmp byte ptr ds:[edi+esi],0x20
* 001fab2c 74 41 je short .001fab6f
* 001fab2e 803c37 81 cmp byte ptr ds:[edi+esi],0x81
* 001fab32 75 4d jnz short .001fab81
* 001fab34 807c37 01 42 cmp byte ptr ds:[edi+esi+0x1],0x42
* 001fab39 74 34 je short .001fab6f
* 001fab3b 803c37 81 cmp byte ptr ds:[edi+esi],0x81
* 001fab3f 75 40 jnz short .001fab81
* 001fab41 807c37 01 41 cmp byte ptr ds:[edi+esi+0x1],0x41
* 001fab46 74 27 je short .001fab6f
* 001fab48 803c37 81 cmp byte ptr ds:[edi+esi],0x81
* 001fab4c 75 33 jnz short .001fab81
* 001fab4e 807c37 01 48 cmp byte ptr ds:[edi+esi+0x1],0x48
* 001fab53 74 1a je short .001fab6f
* 001fab55 803c37 81 cmp byte ptr ds:[edi+esi],0x81
* 001fab59 75 26 jnz short .001fab81
* 001fab5b 807c37 01 49 cmp byte ptr ds:[edi+esi+0x1],0x49
* 001fab60 74 0d je short .001fab6f
* 001fab62 803c37 81 cmp byte ptr ds:[edi+esi],0x81
* 001fab66 75 19 jnz short .001fab81
* 001fab68 807c37 01 40 cmp byte ptr ds:[edi+esi+0x1],0x40
* 001fab6d 75 12 jnz short .001fab81
* 001fab6f 803d c58bb100 00 cmp byte ptr ds:[0xb18bc5],0x0
* 001fab76 75 09 jnz short .001fab81
* 001fab78 c605 c58bb100 01 mov byte ptr ds:[0xb18bc5],0x1
* 001fab7f eb 0c jmp short .001fab8d
* 001fab81 e8 7a000000 call .001fac00
* 001fab86 c605 c58bb100 00 mov byte ptr ds:[0xb18bc5],0x0
* 001fab8d 8b0d e48bb100 mov ecx,dword ptr ds:[0xb18be4]
* 001fab93 33c0 xor eax,eax
* 001fab95 85db test ebx,ebx
* 001fab97 7e 2b jle short .001fabc4
* 001fab99 8d1437 lea edx,dword ptr ds:[edi+esi]
* 001fab9c 8b35 ec8bb100 mov esi,dword ptr ds:[0xb18bec]
* 001faba2 8955 fc mov dword ptr ss:[ebp-0x4],edx
* 001faba5 8bd1 mov edx,ecx
* 001faba7 0faf15 e88bb100 imul edx,dword ptr ds:[0xb18be8]
* 001fabae 0315 bc8bb100 add edx,dword ptr ds:[0xb18bbc] ; .00b180f8
* 001fabb4 03f2 add esi,edx
* 001fabb6 8b55 fc mov edx,dword ptr ss:[ebp-0x4]
* 001fabb9 8a1402 mov dl,byte ptr ds:[edx+eax]
* 001fabbc 881406 mov byte ptr ds:[esi+eax],dl ; jichi: text is in dl in byte
* 001fabbf 40 inc eax
* 001fabc0 3bc3 cmp eax,ebx
* 001fabc2 ^7c f2 jl short .001fabb6
* 001fabc4 0faf0d e88bb100 imul ecx,dword ptr ds:[0xb18be8]
* 001fabcb 030d bc8bb100 add ecx,dword ptr ds:[0xb18bbc] ; .00b180f8
* 001fabd1 a1 ec8bb100 mov eax,dword ptr ds:[0xb18bec]
* 001fabd6 03fb add edi,ebx
* 001fabd8 893d b08bb100 mov dword ptr ds:[0xb18bb0],edi
* 001fabde 5f pop edi
* 001fabdf 03c8 add ecx,eax
* 001fabe1 03c3 add eax,ebx
* 001fabe3 5e pop esi
* 001fabe4 c60419 00 mov byte ptr ds:[ecx+ebx],0x0
* 001fabe8 a3 ec8bb100 mov dword ptr ds:[0xb18bec],eax
* 001fabed 5b pop ebx
* 001fabee 8be5 mov esp,ebp
* 001fabf0 5d pop ebp
* 001fabf1 c3 retn
* 001fabf2 cc int3
* 001fabf3 cc int3
* 001fabf4 cc int3
* 001fabf5 cc int3
* 001fabf6 cc int3
* 001fabf7 cc int3
*/
static void SpecialHookFocasLens(hook_stack* stack, HookParam *, uintptr_t *data, uintptr_t *split, size_t*len)
{
DWORD addr = (DWORD)stack->base + get_reg(regs::edx);
if (*(char *)addr) {
*data = addr;
*len = 1;
*split = FIXED_SPLIT_VALUE;
}
}
bool InsertFocasLensHook()
{
const BYTE bytes[] = {
0x8a,0x14,0x02, // 001fabb9 8a1402 mov dl,byte ptr ds:[edx+eax]
0x88,0x14,0x06, // 001fabbc 881406 mov byte ptr ds:[esi+eax],dl ; jichi: text is in dl in byte
0x40, // 001fabbf 40 inc eax
0x3b,0xc3 // 001fabc0 3bc3 cmp eax,ebx
};
enum { addr_offset = 0x001fabbc - 0x001fabb9 };
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
//GROWL(addr);
if (!addr) {
ConsoleOutput("FocasLens: pattern not found");
return false;
}
HookParam hp;
hp.address = addr + addr_offset;
hp.text_fun = SpecialHookFocasLens; // use special hook to force byte access
hp.type = USING_STRING|USING_SPLIT|FIXING_SPLIT|NO_CONTEXT; // no context to get rid of relative function address
ConsoleOutput("INSERT FocasLens");
// GDI functions are kept in case the font is not cached
//
return NewHook(hp, "FocasLens");
}
bool FocasLens::attach_function() {
return InsertFocasLensHook();
}

Some files were not shown because too many files have changed in this diff Show More