├── .clang-format
├── .gitignore
├── .gitmodules
├── .idea
├── .gitignore
├── ELangPatcher.iml
├── misc.xml
├── modules.xml
└── vcs.xml
├── CMakeLists.txt
├── LICENSE
├── LibELangPatch
├── CMakeLists.txt
├── CallProxyStubWithEcxGen.cpp
├── CdeclPushAndCallGen.cpp
├── CodeGenHelper.h
├── ELangBulkPushGen.cpp
├── ELangInitFnGen.cpp
├── ELangLoaderInitGen.cpp
├── ResolveCallDllFunctionGen.cpp
├── VArgsProxyGen.cpp
├── WndHandlerGen.cpp
└── include
│ ├── CallProxyStubWithEcxGen.h
│ ├── CdeclPushAndCallGen.h
│ ├── ELangBulkPushGen.h
│ ├── ELangInitFnGen.h
│ ├── ELangLoaderInitGen.h
│ ├── ResolveCallDllFunctionGen.h
│ ├── VArgsProxyGen.h
│ └── WndHandlerGen.h
├── README.MD
├── assets
└── screenshot.webp
├── build_vs2022.cmd
└── src
├── ELangPatchFile.cpp
├── ELangPatchFile.h
├── ELangPatcher.cpp
├── ELangPatcher.h
├── ELangPatcher
├── ELangPatcherImpl.h
├── PatchAddFakeEWndStub.cpp
├── PatchDllResolveAndCall.cpp
├── PatchELangLoaderInitStub.cpp
├── PatchEWndUltimate.cpp
├── PatchEWndV02.cpp
├── PatchKernelInvokeCall.cpp
├── PatchLoadWndCall.cpp
├── PatchProxyStub.cpp
├── PatchSuspiciousCallWithParam.cpp
└── PatchWndEventHandlerMain.cpp
├── PEParser.h
├── SearchMatcher.h
├── main.cpp
└── test.cpp
/.clang-format:
--------------------------------------------------------------------------------
1 | # Generated from CLion C/C++ Code Style settings
2 | BasedOnStyle: LLVM
3 | AccessModifierOffset: -4
4 | AlignAfterOpenBracket: Align
5 | AlignConsecutiveAssignments: None
6 | AlignOperands: Align
7 | AllowAllArgumentsOnNextLine: false
8 | AllowAllConstructorInitializersOnNextLine: false
9 | AllowAllParametersOfDeclarationOnNextLine: false
10 | AllowShortBlocksOnASingleLine: Always
11 | AllowShortCaseLabelsOnASingleLine: false
12 | AllowShortFunctionsOnASingleLine: All
13 | AllowShortIfStatementsOnASingleLine: Always
14 | AllowShortLambdasOnASingleLine: All
15 | AllowShortLoopsOnASingleLine: true
16 | AlwaysBreakAfterReturnType: None
17 | AlwaysBreakTemplateDeclarations: Yes
18 | BreakBeforeBraces: Custom
19 | BraceWrapping:
20 | AfterCaseLabel: false
21 | AfterClass: false
22 | AfterControlStatement: Never
23 | AfterEnum: false
24 | AfterFunction: false
25 | AfterNamespace: false
26 | AfterUnion: false
27 | BeforeCatch: false
28 | BeforeElse: false
29 | IndentBraces: false
30 | SplitEmptyFunction: false
31 | SplitEmptyRecord: true
32 | BreakBeforeBinaryOperators: None
33 | BreakBeforeTernaryOperators: true
34 | BreakConstructorInitializers: BeforeColon
35 | BreakInheritanceList: BeforeColon
36 | ColumnLimit: 0
37 | CompactNamespaces: false
38 | ContinuationIndentWidth: 8
39 | IndentCaseLabels: true
40 | IndentPPDirectives: None
41 | IndentWidth: 4
42 | KeepEmptyLinesAtTheStartOfBlocks: true
43 | MaxEmptyLinesToKeep: 2
44 | NamespaceIndentation: All
45 | ObjCSpaceAfterProperty: false
46 | ObjCSpaceBeforeProtocolList: true
47 | PointerAlignment: Right
48 | ReflowComments: false
49 | SpaceAfterCStyleCast: true
50 | SpaceAfterLogicalNot: false
51 | SpaceAfterTemplateKeyword: false
52 | SpaceBeforeAssignmentOperators: true
53 | SpaceBeforeCpp11BracedList: false
54 | SpaceBeforeCtorInitializerColon: true
55 | SpaceBeforeInheritanceColon: true
56 | SpaceBeforeParens: ControlStatements
57 | SpaceBeforeRangeBasedForLoopColon: false
58 | SpaceInEmptyParentheses: false
59 | SpacesBeforeTrailingComments: 0
60 | SpacesInAngles: false
61 | SpacesInCStyleCastParentheses: false
62 | SpacesInContainerLiterals: false
63 | SpacesInParentheses: false
64 | SpacesInSquareBrackets: false
65 | TabWidth: 4
66 | UseTab: Never
67 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### C++ template
2 | # Prerequisites
3 | *.d
4 |
5 | # Compiled Object files
6 | *.slo
7 | *.lo
8 | *.o
9 | *.obj
10 |
11 | # Precompiled Headers
12 | *.gch
13 | *.pch
14 |
15 | # Compiled Dynamic libraries
16 | *.so
17 | *.dylib
18 | *.dll
19 |
20 | # Fortran module files
21 | *.mod
22 | *.smod
23 |
24 | # Compiled Static libraries
25 | *.lai
26 | *.la
27 | *.a
28 | *.lib
29 |
30 | # Executables
31 | *.exe
32 | *.out
33 | *.app
34 |
35 | ### CLion template
36 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
37 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
38 |
39 | # User-specific stuff
40 | .idea/**/workspace.xml
41 | .idea/**/tasks.xml
42 | .idea/**/usage.statistics.xml
43 | .idea/**/dictionaries
44 | .idea/**/shelf
45 |
46 | # AWS User-specific
47 | .idea/**/aws.xml
48 |
49 | # Generated files
50 | .idea/**/contentModel.xml
51 |
52 | # Sensitive or high-churn files
53 | .idea/**/dataSources/
54 | .idea/**/dataSources.ids
55 | .idea/**/dataSources.local.xml
56 | .idea/**/sqlDataSources.xml
57 | .idea/**/dynamic.xml
58 | .idea/**/uiDesigner.xml
59 | .idea/**/dbnavigator.xml
60 |
61 | # Gradle
62 | .idea/**/gradle.xml
63 | .idea/**/libraries
64 |
65 | # Gradle and Maven with auto-import
66 | # When using Gradle or Maven with auto-import, you should exclude module files,
67 | # since they will be recreated, and may cause churn. Uncomment if using
68 | # auto-import.
69 | # .idea/artifacts
70 | # .idea/compiler.xml
71 | # .idea/jarRepositories.xml
72 | # .idea/modules.xml
73 | # .idea/*.iml
74 | # .idea/modules
75 | # *.iml
76 | # *.ipr
77 |
78 | # CMake
79 | cmake-build-*/
80 |
81 | # Mongo Explorer plugin
82 | .idea/**/mongoSettings.xml
83 |
84 | # File-based project format
85 | *.iws
86 |
87 | # IntelliJ
88 | out/
89 |
90 | # mpeltonen/sbt-idea plugin
91 | .idea_modules/
92 |
93 | # JIRA plugin
94 | atlassian-ide-plugin.xml
95 |
96 | # Cursive Clojure plugin
97 | .idea/replstate.xml
98 |
99 | # SonarLint plugin
100 | .idea/sonarlint/
101 |
102 | # Crashlytics plugin (for Android Studio and IntelliJ)
103 | com_crashlytics_export_strings.xml
104 | crashlytics.properties
105 | crashlytics-build.properties
106 | fabric.properties
107 |
108 | # Editor-based Rest Client
109 | .idea/httpRequests
110 |
111 | # Android studio 3.1+ serialized cache file
112 | .idea/caches/build_file_checksums.ser
113 |
114 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "vendor/xbyak"]
2 | path = vendor/xbyak
3 | url = https://github.com/herumi/xbyak.git
4 | [submodule "vendor/cxxopts"]
5 | path = vendor/cxxopts
6 | url = https://github.com/jarro2783/cxxopts.git
7 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/.idea/ELangPatcher.iml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.27)
2 | project(ELangPatcher VERSION 0.1.1)
3 |
4 | set(CMAKE_CXX_STANDARD 20)
5 | add_subdirectory(LibELangPatch)
6 |
7 | add_executable(ELangPatcherTest src/test.cpp)
8 | target_link_libraries(ELangPatcherTest LibELangPatch)
9 |
10 | add_executable(ELangPatcher
11 | src/ELangPatcher.cpp
12 | src/main.cpp
13 | src/ELangPatchFile.cpp
14 | src/ELangPatcher/PatchEWndV02.cpp
15 | src/ELangPatcher/PatchEWndV02.cpp
16 | src/ELangPatcher/PatchEWndUltimate.cpp
17 | src/ELangPatcher/PatchEWndUltimate.cpp
18 | src/ELangPatcher/PatchWndEventHandlerMain.cpp
19 | src/ELangPatcher/PatchKernelInvokeCall.cpp
20 | src/ELangPatcher/PatchKernelInvokeCall.cpp
21 | src/ELangPatcher/PatchProxyStub.cpp
22 | src/ELangPatcher/PatchAddFakeEWndStub.cpp
23 | src/ELangPatcher/PatchELangLoaderInitStub.cpp
24 | src/ELangPatcher/PatchDllResolveAndCall.cpp
25 | src/ELangPatcher/PatchLoadWndCall.cpp
26 | src/ELangPatcher/PatchSuspiciousCallWithParam.cpp
27 | )
28 | target_include_directories(ELangPatcher PRIVATE vendor/cxxopts/include)
29 | target_compile_definitions(ELangPatcher PRIVATE
30 | WIN32_LEAN_AND_MEAN=1
31 | ELANG_PATCHER_VERSION="${PROJECT_VERSION}"
32 | )
33 | target_link_libraries(ELangPatcher LibELangPatch)
34 |
35 | if(MSVC)
36 | target_compile_options(ELangPatcher PRIVATE "/MP")
37 | endif()
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 爱飞的猫
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LibELangPatch/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.27)
2 | project(LibELangPatcher)
3 |
4 | add_library(LibELangPatch STATIC
5 | WndHandlerGen.cpp
6 | ELangInitFnGen.cpp
7 | CallProxyStubWithEcxGen.cpp
8 | VArgsProxyGen.cpp
9 | ELangLoaderInitGen.cpp
10 | ResolveCallDllFunctionGen.cpp
11 | ELangBulkPushGen.cpp
12 | CdeclPushAndCallGen.cpp
13 | CdeclPushAndCallGen.cpp
14 | )
15 |
16 | target_compile_definitions(LibELangPatch PRIVATE XBYAK32=1)
17 | target_include_directories(LibELangPatch
18 | PRIVATE ../vendor/xbyak/xbyak
19 | PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
20 |
21 | if(MSVC)
22 | target_compile_options(LibELangPatch PRIVATE "/MP")
23 | endif()
24 |
--------------------------------------------------------------------------------
/LibELangPatch/CallProxyStubWithEcxGen.cpp:
--------------------------------------------------------------------------------
1 | #include "include/CallProxyStubWithEcxGen.h"
2 |
3 | #include "CodeGenHelper.h"
4 |
5 | class CallProxyStubWithEcxGen : public CodeGenHelper {
6 | public:
7 | explicit CallProxyStubWithEcxGen(bool is_cdecl, int arg_count, int pre_junk_len, int post_junk_len, uint32_t ecx_value, uint32_t call_delta) {
8 | if (is_cdecl) {
9 | generate_cdecl(arg_count, pre_junk_len, post_junk_len, ecx_value, call_delta);
10 | } else {
11 | generate_stdcall(pre_junk_len, post_junk_len, ecx_value, call_delta);
12 | }
13 | }
14 |
15 | void generate_cdecl(int arg_count, int pre_junk_len, int post_junk_len, uint32_t ecx_value, uint32_t call_delta) {
16 | int max_junk_per_inst{};
17 | if (arg_count <= 2) {
18 | max_junk_per_inst = 1;
19 | } else if (arg_count <= 4) {
20 | max_junk_per_inst = 2;
21 | } else if (arg_count <= 6) {
22 | max_junk_per_inst = 3;
23 | } else {
24 | max_junk_per_inst = 4;
25 | }
26 |
27 | auto regs = shuffled({eax, ecx, edx});
28 |
29 | int prefix_junk_left {pre_junk_len - arg_count * 3 - 2/* mov reg, esp */};
30 | auto junk_start = rand_int(0, std::min(max_junk_per_inst, prefix_junk_left));
31 | getJunkInstByLen(junk_start, {edx, eax, ecx});
32 | prefix_junk_left -= junk_start;
33 |
34 | auto reg_stack = pop_last_item(regs);
35 | mov(reg_stack, esp);
36 |
37 | int esp_offset = 4 * arg_count;
38 | for(int i = 0; i < arg_count; i++) {
39 | auto junk_this_round = rand_int(0, std::min(max_junk_per_inst, prefix_junk_left));
40 | getJunkInstByLen(junk_this_round, regs);
41 | prefix_junk_left -= junk_this_round;
42 |
43 | push(dword[reg_stack + esp_offset]);
44 | esp_offset -= 4;
45 | }
46 | fillWithJunk(prefix_junk_left, {eax, ecx, edx});
47 |
48 | post_junk_len -= 1;
49 | auto slide_junk_len = rand_int(0, post_junk_len);
50 | post_junk_len -= slide_junk_len;
51 |
52 | mov(ecx, ecx_value);
53 | fillWithJunkSlideInst(slide_junk_len, {eax, edx});
54 | db(0xE8);
55 | dd(call_delta - slide_junk_len);
56 | ret();
57 |
58 | if (post_junk_len != 0) {
59 | std::vector dummy(post_junk_len);
60 | std::generate(dummy.begin(), dummy.end(), mt_);
61 | db(dummy.data(), dummy.size());
62 | }
63 | }
64 |
65 | void generate_stdcall(int pre_junk_len, int post_junk_len, uint32_t ecx_value, uint32_t call_delta) {
66 | fillWithJunk(pre_junk_len, {eax, ecx, edx});
67 | auto slide_junk_len = rand_int(0, post_junk_len);
68 | post_junk_len -= slide_junk_len;
69 |
70 | mov(ecx, ecx_value);
71 | fillWithJunkSlideInst(slide_junk_len, {eax, edx});
72 | db(0xE9);
73 | dd(call_delta - slide_junk_len);
74 |
75 | if (post_junk_len != 0) {
76 | std::vector dummy(post_junk_len);
77 | std::generate(dummy.begin(), dummy.end(), mt_);
78 | db(dummy.data(), dummy.size());
79 | }
80 | }
81 | };
82 |
83 | std::vector GenerateCallProxyStubWithEcx(int pre_junk_len, int post_junk_len, uint32_t ecx_value, uint32_t call_delta) {
84 | return CallProxyStubWithEcxGen{false, 0, pre_junk_len, post_junk_len, ecx_value, call_delta}.vec();
85 | }
86 |
87 | std::vector GenerateCallProxyStubWithEcxCdecl(int arg_count, int pre_junk_len, int post_junk_len, uint32_t ecx_value, uint32_t call_delta) {
88 | return CallProxyStubWithEcxGen{true, arg_count, pre_junk_len, post_junk_len, ecx_value, call_delta}.vec();
89 | }
90 |
--------------------------------------------------------------------------------
/LibELangPatch/CdeclPushAndCallGen.cpp:
--------------------------------------------------------------------------------
1 | #include "CdeclPushAndCallGen.h"
2 | #include "CodeGenHelper.h"
3 |
4 | class CdeclPushAndCallGen : public CodeGenHelper {
5 | public:
6 | explicit CdeclPushAndCallGen(uint32_t push_value, uint32_t call_delta, uint32_t ret_delta) {
7 | auto regs = shuffled({eax, ecx, edx});
8 | fillWithJunkSlideInst(rand_int(2, 5), regs);
9 |
10 | auto reg_ret_delta = pop_last_item(regs);
11 | IntGenerator int_gen_ret_delta{rand_int(2, 5), ret_delta};
12 | while(!int_gen_ret_delta.generate_step(*this, reg_ret_delta)) {
13 | maybeGenJunk(regs);
14 | }
15 | add(dword[esp], reg_ret_delta);
16 | regs = shuffled({eax, ecx, edx});
17 |
18 | std::optional reg_push_const = std::nullopt;
19 | std::optional reg_call_addr = std::nullopt;
20 |
21 | IntGenerator int_gen_push_value{rand_int(2, 5), push_value};
22 | IntGenerator int_gen_call_delta{rand_int(2, 5), call_delta - ret_delta};
23 |
24 | bool xchg_done{false};
25 | bool target_addr_fixed{false};
26 | ExecItem fns_decode{};
27 | ExecItem::value_type fn_exchange_stack_value = [&]() {
28 | if (!int_gen_push_value.done()) {
29 | fns_decode.emplace_back(fn_exchange_stack_value);
30 | return;
31 | }
32 | genJunk(regs);
33 | xchg(*reg_push_const, dword[esp]);
34 | xchg_done = true;
35 | fns_decode.emplace_back([&]() {
36 | genJunk(regs);
37 | push(*reg_push_const);
38 | });
39 | };
40 | ExecItem::value_type fn_increment_ret_addr = [&]() {
41 | if (!int_gen_call_delta.done()) {
42 | fns_decode.emplace_back(fn_increment_ret_addr);
43 | return;
44 | }
45 |
46 | genJunk(regs);
47 | if (xchg_done) {
48 | add(*reg_call_addr, *reg_push_const);
49 | } else {
50 | add(*reg_call_addr, dword[esp]);
51 | }
52 | target_addr_fixed = true;
53 | };
54 | ExecItem::value_type fn_decode_call_delta = [&]() {
55 | maybeGenJunk(regs);
56 | if (!reg_push_const) {
57 | reg_push_const = std::make_optional(pop_last_item(regs));
58 | }
59 | if (!int_gen_push_value.generate_step(*this, *reg_push_const)) {
60 | fns_decode.emplace_back(fn_decode_call_delta);
61 | }
62 | };
63 | ExecItem::value_type fn_decode_push_value = [&]() {
64 | maybeGenJunk(regs);
65 | if (!reg_call_addr) {
66 | reg_call_addr = std::make_optional(pop_last_item(regs));
67 | }
68 | if (!int_gen_call_delta.generate_step(*this, *reg_call_addr)) {
69 | fns_decode.emplace_back(fn_decode_push_value);
70 | }
71 | };
72 | fns_decode.emplace_back(fn_decode_call_delta);
73 | fns_decode.emplace_back(fn_decode_push_value);
74 | fns_decode.emplace_back(fn_exchange_stack_value);
75 | fns_decode.emplace_back(fn_increment_ret_addr);
76 | shuffle_exec_2(fns_decode);
77 | regs.push_back(*reg_push_const);
78 |
79 | genJunk(regs);
80 | test(*reg_call_addr, *reg_call_addr);
81 | jz("junk");
82 | genJunk(regs);
83 | jmp(*reg_call_addr);
84 | regs = shuffled({eax, ecx, edx, esi, edi, ebx});
85 | fillWithJunkSlideInst(rand_int(5, 10), regs);
86 | L("junk");
87 | fillWithJunkSlideInst(rand_int(10, 15), regs);
88 | jnz("junk");
89 | fillWithJunkSlideInst(rand_int(2, 5), regs);
90 | }
91 | };
92 |
93 | std::vector GenerateCdeclPushAndCall(uint32_t push_value, uint32_t call_delta, uint32_t ret_delta) {
94 | return CdeclPushAndCallGen{push_value, call_delta, ret_delta}.vec();
95 | }
96 |
--------------------------------------------------------------------------------
/LibELangPatch/CodeGenHelper.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include "../vendor/xbyak/xbyak/xbyak.h"
10 |
11 | #ifdef min
12 | #undef min
13 | #endif
14 | #ifdef max
15 | #undef max
16 | #endif
17 |
18 | class IntGenerator {
19 | public:
20 | enum class OpCode {
21 | XOR = 0,
22 | ADD,
23 | SUB,
24 | ROR,
25 | ROL,
26 | NEG,
27 | NOT,
28 |
29 | MOV = 0x10,
30 | };
31 |
32 | inline IntGenerator(int steps, uint32_t value) {
33 | std::uniform_int_distribution<> dist_u32_bits(1, 31);
34 | std::uniform_int_distribution<> dist(0, 6);
35 | std::mt19937 mt(std::random_device{}());
36 | auto next_opcode = [&]() { return static_cast(dist(mt)); };
37 |
38 | auto v{value};
39 | for (int i = 0; i < steps; i++) {
40 | auto opcode = next_opcode();
41 | auto operand = mt();
42 | if (mt() & 1) {
43 | operand &= 0x7F;
44 | }
45 | switch (opcode) {
46 | case OpCode::XOR:
47 | v ^= operand;
48 | break;
49 | case OpCode::ADD:
50 | v -= operand;
51 | break;
52 | case OpCode::SUB:
53 | v += operand;
54 | break;
55 | case OpCode::ROR:
56 | operand = dist_u32_bits(mt);
57 | v = std::rotl(v, static_cast(operand));
58 | break;
59 | case OpCode::ROL:
60 | operand = dist_u32_bits(mt);
61 | v = std::rotr(v, static_cast(operand));
62 | break;
63 | case OpCode::NEG:
64 | operand = 0;
65 | v = -v;
66 | break;
67 | case OpCode::NOT:
68 | operand = 0;
69 | v = ~v;
70 | break;
71 |
72 | // special: do nothing
73 | case OpCode::MOV:
74 | break;
75 | }
76 | steps_.emplace(opcode, operand);
77 | }
78 | steps_.emplace(OpCode::MOV, v);
79 | }
80 | inline bool generate_step(Xbyak::CodeGenerator &code, const Xbyak::Operand &op) {
81 | if (steps_.empty()) {
82 | return true;
83 | }
84 |
85 | auto [opcode, operand] = steps_.top();
86 | steps_.pop();
87 |
88 | switch (opcode) {
89 | case OpCode::XOR:
90 | code.xor_(op, operand);
91 | break;
92 | case OpCode::ADD:
93 | code.add(op, operand);
94 | break;
95 | case OpCode::SUB:
96 | code.sub(op, operand);
97 | break;
98 | case OpCode::ROR:
99 | code.ror(op, static_cast(operand));
100 | break;
101 | case OpCode::ROL:
102 | code.rol(op, static_cast(operand));
103 | break;
104 | case OpCode::NEG:
105 | code.neg(op);
106 | break;
107 | case OpCode::NOT:
108 | code.not_(op);
109 | break;
110 | case OpCode::MOV:
111 | code.mov(op, operand);
112 | break;
113 | }
114 |
115 | return steps_.empty();
116 | }
117 |
118 | inline bool done() {
119 | return steps_.empty();
120 | }
121 |
122 | private:
123 | std::stack> steps_{};
124 | };
125 |
126 | class CodeGenHelper : public Xbyak::CodeGenerator {
127 | public:
128 | [[nodiscard]] inline std::vector vec() {
129 | ready();
130 | return {getCode(), getCurr()};
131 | }
132 |
133 | protected:
134 | typedef std::vector> ExecItem;
135 | typedef Xbyak::Reg32 Reg32;
136 | typedef std::vector Reg32List;
137 |
138 | std::mt19937 mt_{std::random_device{}()};
139 | template
140 | inline std::vector shuffled(std::vector items) {
141 | std::shuffle(items.begin(), items.end(), mt_);
142 | return items;
143 | }
144 | template
145 | inline void shuffle(T &items) {
146 | std::shuffle(items.begin(), items.end(), mt_);
147 | }
148 |
149 | inline void shuffle_exec_2(ExecItem &items) {
150 | while (!items.empty()) {
151 | std::shuffle(items.begin(), items.end(), mt_);
152 | auto next_fn = items.back();
153 | items.pop_back();
154 | next_fn();
155 | }
156 | }
157 | inline void set_zero(Xbyak::Operand ®) {
158 | if (reg.getKind() == Xbyak::Operand::REG) {
159 | pick_exec({
160 | [&]() { xor_(reg, reg); },
161 | [&]() { mov(reg, 0); },
162 | [&]() { and_(reg, 0); },
163 | });
164 | } else {
165 | pick_exec({
166 | [&]() { mov(reg, 0); },
167 | [&]() { and_(reg, 0); },
168 | });
169 | }
170 | }
171 |
172 | inline void shuffle_exec(std::vector> items) {
173 | while (!items.empty()) {
174 | std::shuffle(items.begin(), items.end(), mt_);
175 | auto next_fn = items.back();
176 | items.pop_back();
177 | next_fn(&items);
178 | }
179 | }
180 | inline void shuffle_exec(std::vector> items) {
181 | std::shuffle(items.begin(), items.end(), mt_);
182 | for (auto &fn: items) {
183 | fn();
184 | }
185 | }
186 | inline void pick_exec(const std::vector> &items) {
187 | if (!items.empty()) {
188 | std::uniform_int_distribution<> distr(0, int(items.size() - 1));
189 | auto &it = *(items.begin() + distr(mt_));
190 | it();
191 | }
192 | }
193 | template
194 | inline T pick_random_item(std::vector items) {
195 | std::uniform_int_distribution<> distr(0, int(items.size() - 1));
196 | return items.at(distr(mt_));
197 | }
198 | std::uniform_int_distribution<> dist_bool_{0, 1};
199 | inline bool next_bool() {
200 | return dist_bool_(mt_) == 1;
201 | }
202 | template
203 | inline T rand_int() {
204 | return static_cast(mt_());
205 | }
206 | template
207 | inline T rand_int(T min_value, T max_value) {
208 | std::uniform_int_distribution dist(min_value, max_value);
209 | return static_cast(dist(mt_));
210 | }
211 | inline void getJunkInstByLen(size_t len, const std::vector ®s) {
212 | std::uniform_int_distribution<> dist_register(0, static_cast(regs.size()) - 1);
213 | auto rand_reg = [&]() { return regs[dist_register(mt_)]; };
214 | std::uniform_int_distribution dis_signed_byte(0, 0xFF);
215 | auto rand_signed_byte = [&]() { return static_cast(static_cast(static_cast(dis_signed_byte(mt_)))); };
216 | std::uniform_int_distribution dist_lea_shift(0, 3);// 1/2/4/8
217 | auto rand_lea_multiplier = [&]() { return 1 << dist_lea_shift(mt_); };
218 |
219 | auto ro_regs = shuffled({eax, ebx, ecx, edx, ebp, esp, esi, edi});
220 | std::uniform_int_distribution<> dist_ro_register(0, static_cast(ro_regs.size()) - 1);
221 | auto rand_ro_reg = [&]() { return ro_regs[dist_ro_register(mt_)]; };
222 |
223 | switch (len) {
224 | case 1:
225 | pick_exec({
226 | [&]() { inc(rand_reg()); },
227 | [&]() { dec(rand_reg()); },
228 | [&]() { nop(); },
229 | });
230 | break;
231 |
232 | case 2:
233 | pick_exec({
234 | [&]() { or_(rand_reg(), rand_ro_reg()); },
235 | [&]() { xor_(rand_reg(), rand_ro_reg()); },
236 | [&]() { and_(rand_reg(), rand_ro_reg()); },
237 | [&]() { add(rand_reg(), rand_ro_reg()); },
238 | [&]() { sub(rand_reg(), rand_ro_reg()); },
239 | [&]() { adc(rand_reg(), rand_ro_reg()); },
240 | [&]() { test(rand_reg(), rand_ro_reg()); },
241 | [&]() { cmp(rand_reg(), rand_ro_reg()); },
242 | });
243 | break;
244 |
245 | case 3:
246 | pick_exec({
247 | [&]() { or_(rand_reg(), rand_signed_byte()); },
248 | [&]() { xor_(rand_reg(), rand_signed_byte()); },
249 | [&]() { and_(rand_reg(), rand_signed_byte()); },
250 | [&]() { add(rand_reg(), rand_signed_byte()); },
251 | [&]() { sub(rand_reg(), rand_signed_byte()); },
252 | [&]() { adc(rand_reg(), rand_signed_byte()); },
253 | });
254 | break;
255 |
256 | case 4:
257 | pick_exec({
258 | [&]() {
259 | Xbyak::Reg32 index_reg;
260 | do {
261 | index_reg = rand_ro_reg();
262 | } while (index_reg == esp);
263 | lea(rand_reg(), ptr[rand_ro_reg() + index_reg * rand_lea_multiplier() + rand_signed_byte()]);
264 | },
265 | [&]() { lea(rand_reg(), ptr[rand_ro_reg() + rand_signed_byte()]); },
266 | });
267 | break;
268 |
269 | case 5:
270 | pick_exec({
271 | [&]() { mov(rand_reg(), mt_()); },
272 | });
273 | break;
274 |
275 | case 0:
276 | default:
277 | break;
278 | }
279 | }
280 | inline void genJunk(const std::vector ®s) {
281 | if (!regs.empty()) {
282 | std::uniform_int_distribution<> dist_len(1, 4);
283 | getJunkInstByLen(dist_len(mt_), regs);
284 | }
285 | }
286 | inline void fillWithJunkSlideInst(size_t size, const std::vector ®s) {
287 | int junk_code_left = static_cast(size);
288 | while (junk_code_left > 0) {
289 | auto this_round = rand_int(1, std::min(4, junk_code_left));
290 | getJunkInstByLen(this_round, regs);
291 | junk_code_left -= this_round;
292 | }
293 | }
294 | inline void fillWithJunk(size_t size, const std::vector ®s) {
295 | // too short to have anything meaningful
296 | if (size <= 20) {
297 | fillWithJunkSlideInst(size, regs);
298 | return;
299 | }
300 |
301 | auto end_label = Xbyak::Label{};
302 | std::uniform_int_distribution dist_u8(0, 0xff);
303 | auto gen_byte = [&]() { return static_cast(dist_u8(mt_)); };
304 |
305 | std::vector buffer(size);
306 | std::generate(buffer.begin(), buffer.end(), gen_byte);
307 |
308 | int junk_inst_len = rand_int(6, 8);
309 |
310 | int junk_padding = static_cast(size) - 5 - 4 - 1 - junk_inst_len;
311 | int padding_start = rand_int(2, junk_padding - 4);
312 | int padding_end = junk_padding - padding_start;
313 |
314 | call(end_label);
315 | db(buffer.data(), padding_start);
316 | L(end_label);
317 |
318 | add(dword[esp], static_cast(size) - 5);
319 | fillWithJunkSlideInst(junk_inst_len, regs);
320 | ret();
321 |
322 | fillWithJunkSlideInst(padding_end, regs);
323 | }
324 |
325 | inline void maybeGenJunk(const std::vector ®s) {
326 | if (next_bool()) {
327 | genJunk(regs);
328 | }
329 | }
330 | };
331 |
332 |
333 | template
334 | std::optional find_and_remove_item(std::vector &list, F fn) {
335 | std::optional result{};
336 | auto it = std::find_if(list.begin(), list.end(), fn);
337 | if (it != list.end()) {
338 | result = std::make_optional(std::move(*it));
339 | list.erase(it);
340 | }
341 | return result;
342 | }
343 |
344 | template
345 | inline T pop_last_item(std::vector &list) {
346 | T result{};
347 | result = std::move(list.back());
348 | list.pop_back();
349 | return result;
350 | }
351 |
--------------------------------------------------------------------------------
/LibELangPatch/ELangBulkPushGen.cpp:
--------------------------------------------------------------------------------
1 | #include "ELangBulkPushGen.h"
2 | #include "CodeGenHelper.h"
3 |
4 | class BulkPushInstruction : public CodeGenHelper {
5 | public:
6 | explicit BulkPushInstruction(uint32_t ret_delta, std::vector values_to_push, uint32_t ebx_delta_to_ret_addr, uint32_t call_delta_to_ret_addr) {
7 | auto regs = Reg32List{eax, ecx, edx};
8 | shuffle(regs);
9 |
10 | fillWithJunkSlideInst(rand_int(2, 4), regs);
11 | {
12 | auto reg_stack_offset = pop_last_item(regs);
13 | auto reg_arg_count_mul_4 = pop_last_item(regs);
14 |
15 | IntGenerator int_gen_stack_delta{rand_int(2, 5), ret_delta};
16 | IntGenerator int_gen_arg_count{rand_int(2, 5), static_cast(values_to_push.size() - 1) * 4};
17 |
18 | while (!int_gen_stack_delta.done() || !int_gen_arg_count.done()) {
19 | pick_exec({
20 | [&]() { int_gen_stack_delta.generate_step(*this, reg_stack_offset); },
21 | [&]() { int_gen_arg_count.generate_step(*this, reg_arg_count_mul_4); },
22 | });
23 | }
24 |
25 | add(dword[esp], reg_stack_offset);
26 | regs.push_back(reg_stack_offset);
27 | genJunk(regs);
28 |
29 | sub(esp, reg_arg_count_mul_4);
30 | regs.push_back(reg_arg_count_mul_4);
31 | genJunk(regs);
32 | }
33 |
34 | regs = shuffled({eax, ecx, edx});
35 | std::vector>> int_gens_idle{};
36 | std::vector>> int_gens_working{};
37 |
38 | auto arg_count = values_to_push.size();
39 | for (auto i = 1; i < arg_count; i++) {
40 | int offset = (static_cast(arg_count) - i - 1) * 4;
41 | int_gens_idle.emplace_back(offset, std::make_shared(rand_int(2, 5), values_to_push[i]));
42 | }
43 | shuffle(int_gens_idle);
44 | int_gens_idle.insert(int_gens_idle.begin(), {-4, std::make_shared(rand_int(2, 5), values_to_push[0])});
45 |
46 | while (!regs.empty()) {
47 | auto temp_reg = pop_last_item(regs);
48 | auto [op, int_gen] = pop_last_item(int_gens_idle);
49 | int_gens_working.emplace_back(op, temp_reg, int_gen);
50 | }
51 |
52 | Reg32 reg_last_value{};
53 | while (!int_gens_working.empty()) {
54 | auto idx = rand_int(0, int(int_gens_working.size() - 1));
55 | auto [esp_offset, reg_temp, int_gen] = int_gens_working[idx];
56 | int_gen->generate_step(*this, reg_temp);
57 |
58 | if (int_gen->done()) {
59 | int_gens_working.erase(int_gens_working.begin() + idx);
60 |
61 | // Don't bother
62 | if (esp_offset == -4) {
63 | reg_last_value = reg_temp;
64 | continue;
65 | }
66 |
67 | mov(dword[esp + esp_offset], reg_temp);
68 |
69 | regs.push_back(reg_temp);
70 | shuffle(regs);
71 | genJunk(regs);
72 | reg_temp = pop_last_item(regs);
73 |
74 | if (!int_gens_idle.empty()) {
75 | auto [op_next, int_gen_next] = pop_last_item(int_gens_idle);
76 | int_gens_working.emplace_back(op_next, reg_temp, int_gen_next);
77 | }
78 | }
79 | }
80 |
81 | regs = shuffled({eax, ecx, edx});
82 | find_and_remove_item(regs, [&](auto &r) { return r == reg_last_value; });
83 | genJunk(regs);
84 | xchg(reg_last_value, dword[esp + (arg_count * 4 - 4)]);
85 | genJunk(regs);
86 |
87 | if (ebx_delta_to_ret_addr && call_delta_to_ret_addr) {
88 | auto reg_temp_delta_to_ret = pop_last_item(regs);
89 | IntGenerator ebx_gen{rand_int(2, 5), ebx_delta_to_ret_addr};
90 | while(!ebx_gen.done()){
91 | ebx_gen.generate_step(*this, reg_temp_delta_to_ret);
92 | genJunk(regs);
93 | }
94 | lea(ebx, dword[reg_last_value + reg_temp_delta_to_ret]);
95 | regs.push_back(reg_temp_delta_to_ret);
96 | shuffle(regs);
97 | genJunk(regs);
98 |
99 | push(reg_last_value);
100 | regs.push_back(reg_last_value);
101 | shuffle(regs);
102 | genJunk(regs);
103 |
104 | reg_last_value = pop_last_item(regs);
105 | lea(reg_last_value, dword[ebx + (call_delta_to_ret_addr - ebx_delta_to_ret_addr)]);
106 | }
107 |
108 | genJunk(regs);
109 | if (next_bool()) {
110 | test(reg_last_value, reg_last_value);
111 | jz("out");
112 | fillWithJunkSlideInst(rand_int(2, 5), regs);
113 | }
114 | pick_exec({
115 | [&]() { jmp(reg_last_value); },
116 | [&]() {
117 | push(reg_last_value);
118 | genJunk({eax, ecx, edx});
119 | ret();
120 | },
121 | });
122 | fillWithJunkSlideInst(rand_int(5, 10), {eax, ecx, edx, esi, edi, ebx});
123 | L("out");
124 | }
125 | };
126 |
127 | std::vector GenerateBulkPushInstruction(uint32_t ret_delta, std::vector values_to_push) {
128 | return BulkPushInstruction{ret_delta, std::move(values_to_push), 0, 0}.vec();
129 | }
130 | std::vector GenerateBulkPushInstructionWithEBXCall(uint32_t ret_delta, std::vector values_to_push, uint32_t ebx_delta_to_ret_addr, uint32_t call_delta_to_ret_addr) {
131 | return BulkPushInstruction{ret_delta, std::move(values_to_push), ebx_delta_to_ret_addr, call_delta_to_ret_addr}.vec();
132 | }
133 |
--------------------------------------------------------------------------------
/LibELangPatch/ELangInitFnGen.cpp:
--------------------------------------------------------------------------------
1 | #include "include/ELangInitFnGen.h"
2 | #include "CodeGenHelper.h"
3 |
4 | class InitHandlerGen : public CodeGenHelper {
5 | public:
6 | explicit InitHandlerGen(uint32_t offset_process_heap, uint32_t offset_has_ole, uint32_t *header_data) {
7 | auto ebx_offset = static_cast(mt_());
8 | if (next_bool()) {
9 | ebx_offset &= 0x7F;
10 | }
11 | // ebx_offset &= 0b1111'1100;
12 | fillWithJunk((mt_() & 0b1111) | 1, {ecx, edx, esi, edi});
13 |
14 | auto regs = shuffled({ecx, edx, esi, edi});
15 |
16 | auto reg_temp_heap = pop_last_item(regs);
17 | auto new_ebx = pop_last_item(regs);
18 |
19 | shuffle_exec({
20 | [&]() {
21 | genJunk(regs);
22 | mov(reg_temp_heap, eax);
23 | regs.push_back(eax);
24 | },
25 | [&]() {
26 | genJunk(regs);
27 | mov(new_ebx, ebx);
28 | regs.push_back(ebx);
29 | },
30 | });
31 |
32 | auto mov_to_value = [&](const Xbyak::Operand &op, uint32_t imm) {
33 | if (imm == 0) {
34 | pick_exec({
35 | [&]() { mov(op, imm); },
36 | [&]() { and_(op, imm); },
37 | });
38 | } else {
39 | mov(op, imm);
40 | }
41 | };
42 |
43 | lea(new_ebx, ptr[new_ebx + ebx_offset]);
44 | shuffle_exec({
45 | [&]() { genJunk(regs); },
46 | [&]() { genJunk(regs); },
47 | [&]() { genJunk(regs); },
48 | [&]() { mov_to_value(dword[new_ebx + (0xC4 - ebx_offset)], header_data[0]); },
49 | [&]() { mov_to_value(dword[new_ebx + (0xC8 - ebx_offset)], header_data[1]); },
50 | [&]() { mov_to_value(dword[new_ebx + (0xCC - ebx_offset)], header_data[2] + 1); },
51 | [&]() {
52 | mov(dword[new_ebx + (offset_process_heap - ebx_offset)], reg_temp_heap);
53 | regs.push_back(reg_temp_heap);
54 | },
55 | });
56 |
57 | regs = shuffled({eax, ebx, edx, ecx, esi, edi});
58 | std::erase_if(regs, [&](auto &r) { return r == new_ebx; });
59 |
60 | Xbyak::Reg32 reg_jump_offset = *find_and_remove_item(regs, [&](auto &r) { return r != esi && r != edi && r != ebx; });
61 | std::shuffle(regs.begin(), regs.end(), mt_);
62 |
63 | maybeGenJunk(regs);
64 | cmp(dword[new_ebx + (offset_has_ole - ebx_offset)], 0);
65 | setne(reg_jump_offset.cvt8());
66 | maybeGenJunk(regs);
67 |
68 | shuffle_exec({
69 | [&](void *) { genJunk(regs); },
70 | [&](void *) { genJunk(regs); },
71 | [&](void *p_vec) {
72 | pick_exec({
73 | [&]() { neg(reg_jump_offset.cvt8()); },
74 | [&]() { neg(reg_jump_offset); },
75 | });
76 | auto &vec = *reinterpret_cast> *>(p_vec);
77 | vec.emplace_back([&](void *p_vec) {
78 | and_(reg_jump_offset, 0x12);
79 |
80 | auto &vec = *reinterpret_cast> *>(p_vec);
81 | vec.emplace_back([&](void *) { add(reg_jump_offset, 0x2F); });
82 | });
83 | },
84 | [&](void *) {
85 | if (new_ebx == ebx) {
86 | sub(ebx, ebx_offset);
87 | } else {
88 | lea(ebx, dword[new_ebx - ebx_offset]);
89 | regs.push_back(new_ebx);
90 | }
91 | std::erase_if(regs, [&](auto &r) { return r == ebx; });
92 | },
93 | });
94 | maybeGenJunk(regs);
95 | add(dword[esp], reg_jump_offset);
96 | regs.push_back(reg_jump_offset);
97 | maybeGenJunk(regs);
98 | ret();
99 |
100 | std::vector junk_padding((mt_() & 0b1111) | 1);
101 | std::generate(junk_padding.begin(), junk_padding.end(), mt_);
102 | db(junk_padding.data(), junk_padding.size());
103 | }
104 | };
105 |
106 | std::vector GenerateELangInitSnippet(uint32_t offset_process_heap, uint32_t offset_has_ole, uint32_t *header_data) {
107 | return InitHandlerGen{offset_process_heap, offset_has_ole, header_data}.vec();
108 | }
109 |
--------------------------------------------------------------------------------
/LibELangPatch/ELangLoaderInitGen.cpp:
--------------------------------------------------------------------------------
1 | #include "ELangLoaderInitGen.h"
2 | #include "CodeGenHelper.h"
3 |
4 | class ELangLoaderInitGen : public CodeGenHelper {
5 | public:
6 | explicit ELangLoaderInitGen(std::optional call_delta) {
7 | auto regs = shuffled({eax, edx, ecx});
8 | fillWithJunkSlideInst(rand_int(1, 5), regs);
9 | shuffle_exec({
10 | [&]() { cld(); genJunk(regs); },
11 | [&]() { fninit(); genJunk(regs); },
12 | });
13 | fillWithJunkSlideInst(rand_int(1, 5), regs);
14 |
15 | bool use_ret_trick{false};
16 | if (call_delta) {
17 | use_ret_trick = next_bool();
18 | auto reg_ret_addr = pop_last_item(regs);
19 | mov(reg_ret_addr, dword[esp]);
20 | genJunk(regs);
21 | if (use_ret_trick) {
22 | add(dword[esp], 3);
23 | genJunk(regs);
24 | }
25 | auto delta_signed = static_cast(*call_delta);
26 | if (delta_signed > 0) {
27 | add(reg_ret_addr, delta_signed);
28 | } else {
29 | sub(reg_ret_addr, -delta_signed);
30 | }
31 | genJunk(regs);
32 |
33 | if (use_ret_trick) {
34 | jmp(reg_ret_addr);
35 | } else {
36 | call(reg_ret_addr);
37 | }
38 | }
39 |
40 | regs = shuffled({eax, edx, ecx});
41 | if (!use_ret_trick) {
42 | genJunk(regs);
43 | add(dword[esp], 3);
44 | }
45 | genJunk(regs);
46 | ret();
47 |
48 | std::vector junk(rand_int(4, 10));
49 | std::generate(junk.begin(), junk.end(), mt_);
50 | db(junk.data(), junk.size());
51 | }
52 | };
53 |
54 | std::vector GenerateELangLoaderInit(std::optional call_delta) {
55 | return ELangLoaderInitGen{call_delta}.vec();
56 | }
--------------------------------------------------------------------------------
/LibELangPatch/ResolveCallDllFunctionGen.cpp:
--------------------------------------------------------------------------------
1 | #include "CodeGenHelper.h"
2 | #include "VArgsProxyGen.h"
3 |
4 | class ResolveCallDllFunctionGen : public CodeGenHelper {
5 | public:
6 | explicit ResolveCallDllFunctionGen(uint32_t call_delta) {
7 | Reg32List regs({eax, ecx, edx});
8 | shuffle(regs);
9 | auto reg_to_save = pop_last_item(regs);
10 | if (reg_to_save != eax) {
11 | fillWithJunkSlideInst(rand_int(2, 5), {edx, ecx});
12 | mov(reg_to_save, eax);
13 | }
14 |
15 | int fn_offset = 0;
16 | fillWithJunkSlideInst(rand_int(2, 5), regs);
17 |
18 | Reg32 reg_fn_call{};
19 | shuffle_exec({
20 | [&]() {
21 | push(reg_to_save);
22 | fn_offset += 4;
23 | regs.push_back(reg_to_save);
24 | shuffle(regs);
25 | fillWithJunkSlideInst(rand_int(2, 7), regs);
26 | },
27 | [&]() {
28 | reg_fn_call = pop_last_item(regs);
29 | mov(reg_fn_call, dword[esp + fn_offset]);
30 | pick_exec({
31 | [&]() { add(reg_fn_call, call_delta); },
32 | [&]() {
33 | auto reg_fn_call_new = pop_last_item(regs);
34 | lea(reg_fn_call_new, dword[reg_fn_call + call_delta]);
35 | regs.push_back(reg_fn_call);
36 | shuffle(regs);
37 | reg_fn_call = reg_fn_call_new;
38 | },
39 | });
40 | },
41 | });
42 | call(reg_fn_call);
43 | regs = {eax, ecx, edx};
44 | shuffle(regs);
45 |
46 | auto reg_next_fn_addr = pop_last_item(regs);
47 | if (reg_next_fn_addr != eax) {
48 | genJunk({ecx, edx});
49 | mov(reg_next_fn_addr, eax);
50 | }
51 | genJunk(regs);
52 |
53 | int stack_delta = 8;
54 | if (next_bool()) {
55 | stack_delta -= 4;
56 | pop(pick_random_item(regs));
57 | }
58 | Xbyak::Label lb_junk;
59 | pick_exec({
60 | [&]() {
61 | add(esp, stack_delta);
62 | jz(lb_junk);
63 | genJunk(regs);
64 | jmp(reg_next_fn_addr);
65 | },
66 | [&]() {
67 | if (stack_delta - 4 != 0) {
68 | add(esp, stack_delta - 4);
69 | jz(lb_junk);
70 | genJunk(regs);
71 | }
72 | mov(dword[esp], reg_next_fn_addr);
73 | genJunk({eax, ecx, edx});
74 | ret();
75 | },
76 | });
77 | L(lb_junk);
78 | fillWithJunkSlideInst(rand_int(10, 20), {eax, ecx, edx});
79 | jnz(lb_junk);
80 | ret(static_cast(mt_() & 0x3C));
81 | }
82 | };
83 |
84 |
85 | std::vector GenerateResolveCallDllFunction(uint32_t call_delta) {
86 | return ResolveCallDllFunctionGen{call_delta}.vec();
87 | }
88 |
--------------------------------------------------------------------------------
/LibELangPatch/VArgsProxyGen.cpp:
--------------------------------------------------------------------------------
1 | #include "VArgsProxyGen.h"
2 | #include "CodeGenHelper.h"
3 |
4 | class VArgsProxyGen : public CodeGenHelper {
5 | public:
6 | explicit VArgsProxyGen() {
7 | Reg32List regs({eax, ebx, ecx, edx});
8 | shuffle(regs);
9 |
10 | auto reg_stack_tracker = pop_last_item(regs);
11 | auto reg_zero = *find_and_remove_item(regs, [&](auto &r) { return r != ebx; });
12 | auto reg_next_func = pop_last_item(regs);
13 | auto reg_stack_offset = (std::uniform_int_distribution<>(0, 0x7C - 0x14)(mt_) & 0x7C);
14 |
15 | fillWithJunkSlideInst(rand_int(2, 4), {ecx, edx});
16 |
17 | shuffle_exec({
18 | [&]() {
19 | if (reg_next_func != ebx) {
20 | mov(reg_next_func, ebx);
21 | }
22 | },
23 | [&]() { set_zero(reg_zero); },
24 | });
25 |
26 | int stack_offset = 0;
27 | bool stack_reg_set{false};
28 | for (int i = 0; i < 3; i++) {
29 | pick_exec({
30 | [&]() { push(reg_zero); },
31 | [&]() { push(0); },
32 | });
33 | if (stack_reg_set) continue;
34 |
35 | stack_offset += 4;
36 | if (next_bool()) {
37 | stack_reg_set = true;
38 | lea(reg_stack_tracker, dword[esp + (stack_offset + reg_stack_offset + 8)]);
39 | }
40 | }
41 | regs.push_back(reg_zero);
42 | shuffle(regs);
43 |
44 | if (!stack_reg_set) {
45 | lea(reg_stack_tracker, dword[esp + (stack_offset + reg_stack_offset + 8)]);
46 | }
47 | genJunk(regs);
48 | auto reg_param_value = pop_last_item(regs);
49 | lea(reg_param_value, ptr[reg_stack_tracker - reg_stack_offset]);
50 | genJunk(regs);
51 | push(reg_param_value);
52 | genJunk(regs);
53 | push(dword[reg_stack_tracker - 4 - reg_stack_offset]);
54 | genJunk(regs);
55 | pick_exec({
56 | [&]() { sub(reg_stack_tracker, (0x14 + reg_stack_offset)); },
57 | [&]() { lea(reg_stack_tracker, ptr[reg_stack_tracker - (0x14 + reg_stack_offset)]); },
58 | });
59 | genJunk(regs);
60 | push(reg_stack_tracker);
61 | regs.push_back(reg_stack_tracker);
62 | if (reg_next_func != ebx) {
63 | mov(ebx, reg_next_func);
64 | }
65 | genJunk(regs);
66 | call(reg_next_func);
67 |
68 | regs = {ecx, edx, eax};
69 | genJunk(regs);
70 | add(esp, 0x0C);
71 | while (!regs.empty()) {
72 | genJunk(regs);
73 | pop(pop_last_item(regs));
74 | }
75 | ret();
76 | }
77 | };
78 |
79 |
80 | std::vector GenerateVArgsProxyCode() {
81 | return VArgsProxyGen{}.vec();
82 | }
83 |
--------------------------------------------------------------------------------
/LibELangPatch/WndHandlerGen.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "CodeGenHelper.h"
5 | #include "include/WndHandlerGen.h"
6 |
7 | class WndHandlerGen : public CodeGenHelper {
8 | public:
9 | explicit WndHandlerGen(uint32_t call_inst_address, uint32_t call_inst_delta) : call_inst_address_(call_inst_address), call_inst_delta_(call_inst_delta) {
10 | inLocalLabel();
11 |
12 | enterProc();
13 |
14 | // backup "this" ctx.
15 | mov(ptr_local_this_, ecx);
16 | resolveNextWndHandler();
17 |
18 | exitProc();
19 |
20 | outLocalLabel();
21 | ready();
22 | }
23 |
24 | private:
25 | uint32_t call_inst_address_{};
26 | uint32_t call_inst_delta_{};
27 | std::vector backup_regs_{};
28 | Xbyak::Address ptr_local_this_ = dword[ebp - 4];
29 | Xbyak::Address ptr_arg_1_ = dword[ebp + 0x0C];
30 | Xbyak::Address ptr_arg_ret_addr_ = dword[ebp + 0x04];
31 | inline void initBackupRegs() {
32 | backup_regs_ = {edi, esi, ebx};
33 |
34 | if (next_bool()) backup_regs_.push_back(edx);
35 | if (next_bool()) backup_regs_.push_back(ecx);
36 |
37 | std::shuffle(backup_regs_.begin(), backup_regs_.end(), mt_);
38 | }
39 | inline void enterProc() {
40 | push(esp);
41 | mov(ebp, esp);
42 |
43 | auto locals_size = (mt_() & 0b1'1100) + 12;
44 | sub(esp, locals_size);
45 | auto local_select = std::uniform_int_distribution<>(1, (locals_size) >> 2)(mt_) << 2;
46 | ptr_local_this_ = dword[ebp - local_select];
47 |
48 | initBackupRegs();
49 | for (auto reg: backup_regs_) {
50 | push(reg);
51 | }
52 | }
53 | inline void exitProc() {
54 | L("prepare_exit");
55 | for (auto it = backup_regs_.crbegin(); it != backup_regs_.crend(); ++it) {
56 | maybeGenJunk({*it});
57 | pop(*it);
58 | }
59 | genJunk({ecx, edx});
60 | mov(esp, ebp);
61 | genJunk({ecx, edx});
62 | pop(ebp);
63 | genJunk({ecx, edx});
64 | add(esp, 4);
65 | genJunk({ecx, edx});
66 | ret(4);
67 | }
68 | inline void resolveNextWndHandler() {
69 | std::vector regs_for_access = {eax, ebx, edx, esi, edi};
70 | shuffle(regs_for_access);
71 | auto reg_temp = pop_last_item(regs_for_access);
72 | auto reg_temp_2 = pop_last_item(regs_for_access);
73 |
74 | // 00418810 | 55 | push ebp
75 | // 0041883A | E8 B19AFFFF | call 测试2_5.1_静态编译_q1.4122F0
76 | // (0041883A - 00418810) + 0xFFFF9AB1 + 5 === (getSize() + ?)
77 | mov(reg_temp_2, ptr_arg_ret_addr_);
78 | genJunk(regs_for_access);
79 | add(reg_temp_2, call_inst_delta_);
80 |
81 | mov(reg_temp, ptr_arg_1_);
82 |
83 | for (int i = 0x0C; i >= 0; i -= 4) {
84 | maybeGenJunk(regs_for_access);
85 | push(dword[reg_temp + i]);
86 | }
87 | maybeGenJunk(regs_for_access);
88 | call(reg_temp_2);
89 |
90 | pick_exec({
91 | [&]() { test(eax, eax); },
92 | [&]() { cmp(eax, 0); },
93 | });
94 | je("prepare_exit");
95 |
96 | prepareAndCallNextHandler();
97 | saveAndAssignHandlerResult();
98 | }
99 |
100 | inline void prepareAndCallNextHandler() {
101 | auto regs = shuffled({eax, ecx, edx, esi, ebx});
102 |
103 | auto reg_next_handler = regs[0];
104 | auto reg_arg_count = regs[1];
105 | auto reg_list_begin = regs[2];
106 | auto reg_list_end = regs[3];
107 | auto reg_arg_1 = regs[4];
108 |
109 | if (reg_next_handler != eax) {
110 | mov(reg_next_handler, eax);
111 | }
112 | mov(reg_arg_1, ptr_arg_1_);
113 | shuffle_exec({
114 | [&](void *) { lea(reg_list_begin, dword[reg_arg_1 + 0x10]); },
115 | [&](void *) { mov(reg_arg_count, dword[reg_arg_1 + 0x0C]); },
116 | });
117 | lea(reg_list_end, dword[reg_list_begin + reg_arg_count * 4]);
118 | cmp(reg_list_begin, reg_list_end);
119 | je("lb_end_push_args");
120 | /**/ maybeGenJunk({reg_arg_1, reg_arg_count});
121 | L("lb_push_next_arg");
122 | bool added{false};
123 | /**/ shuffle_exec({
124 | [&](void *) { push(dword[reg_list_begin - (added ? 4 : 0)]); },
125 | [&](void *) { maybeGenJunk({reg_arg_1, reg_arg_count}); },
126 | [&](void *) { add(reg_list_begin, 4); added = true; },
127 | });
128 | /**/ cmp(reg_list_begin, reg_list_end);
129 | /**/ jne("lb_push_next_arg", T_SHORT);
130 | /**/ maybeGenJunk({reg_arg_1, reg_arg_count});
131 | L("lb_end_push_args");
132 | call(reg_next_handler);
133 | }
134 | inline void saveAndAssignHandlerResult() {
135 | auto regs = shuffled({eax, ecx, edx, ebx, esi, edi});
136 |
137 | Xbyak::Reg32 reg_handler_result = *find_and_remove_item(regs, [&](auto ®) {
138 | return reg != ebx;
139 | });
140 | Xbyak::Reg32 reg_flag = *find_and_remove_item(regs, [&](auto ®) {
141 | return reg != esi && reg != edi;
142 | });
143 |
144 | auto reg_temp = regs[0];
145 | auto reg_temp_const = regs[1];
146 |
147 | if (reg_handler_result != eax) {
148 | mov(reg_handler_result, eax);
149 | }
150 |
151 | pick_exec({
152 | [&]() { test(ebx, ebx); },
153 | [&]() { cmp(ebx, 0); },
154 | });
155 | setne(reg_flag.cvt8());
156 | jz("skip_set_extra_flags");
157 | shuffle_exec({
158 | [&]() { mov(reg_temp, ptr_arg_1_); },
159 | [&]() { movzx(reg_flag, reg_flag.cvt8()); },
160 | });
161 |
162 | shuffle_exec({
163 | [&]() { mov(dword[reg_temp + reg_flag * 8 + 0x20], reg_handler_result); },
164 | [&]() { mov(dword[reg_temp + 0x24], reg_flag); },
165 | });
166 |
167 | L("skip_set_extra_flags");
168 | genJunk({reg_temp_const, reg_flag, esi, edi});
169 | shuffle_exec({
170 | [&]() { mov(reg_temp, ptr_local_this_); },
171 | [&]() { xor_(reg_temp_const, reg_temp_const); },
172 | });
173 | mov(dword[reg_temp + 0x1F0], reg_temp_const /* 0 */);
174 | pick_exec({
175 | [&]() { mov(eax, 1); },
176 | [&]() { lea(eax, ptr[reg_temp_const + 1]); },
177 | [&]() { mov(eax, reg_temp_const); inc(eax); },
178 | });
179 | }
180 | };
181 |
182 | std::vector GenerateWndHandlerCode(uint32_t call_inst_address, uint32_t call_inst_delta) {
183 | return WndHandlerGen{call_inst_address, call_inst_delta}.vec();
184 | }
185 |
--------------------------------------------------------------------------------
/LibELangPatch/include/CallProxyStubWithEcxGen.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | /**
7 | * @desc Replace the following function
8 | * 15 bytes => junk nop
9 | * mov ecx, ...
10 | * call => jmp
11 | * 00411540 | 8B4424 0C | mov eax,dword ptr ss:[esp+C]
12 | * 00411544 | 8B4C24 08 | mov ecx,dword ptr ss:[esp+8]
13 | * 00411548 | 8B5424 04 | mov edx,dword ptr ss:[esp+4]
14 | * 0041154C | 50 | push eax
15 | * 0041154D | 51 | push ecx
16 | * 0041154E | 52 | push edx
17 | * 0041154F | B9 08F64A00 | mov ecx, exe.4AF608 <- `ecx_value`
18 | * 00411554 | E8 87C7FFFF | call exe.40DCE0 <- `call_delta`
19 | * 00411559 | C2 0C00 | ret C
20 | * @param pre_junk_len The number of bytes until mov ecx. e.g. `15`.
21 | * @param post_junk_len The number of bytes available afterwards, .e.g `3`.
22 | * @param ecx_value
23 | * @param call_delta
24 | * @return
25 | */
26 | std::vector GenerateCallProxyStubWithEcx(int pre_junk_len, int post_junk_len, uint32_t ecx_value, uint32_t call_delta);
27 |
28 | std::vector GenerateCallProxyStubWithEcxCdecl(int arg_count, int pre_junk_len, int post_junk_len, uint32_t ecx_value, uint32_t call_delta);
29 |
--------------------------------------------------------------------------------
/LibELangPatch/include/CdeclPushAndCallGen.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | /**
7 | * 00401545 | 68 48000152 | push 52010048 <- this should not be relocatable
8 | * 0040154A | E8 11000000 | call exe.401560 <- call_delta = 0x11
9 | * 0040154F | 83C4 04 | add esp,4 <- stack_adjustment
10 | */
11 | std::vector GenerateCdeclPushAndCall(uint32_t push_value, uint32_t call_delta, uint32_t ret_delta);
12 |
--------------------------------------------------------------------------------
/LibELangPatch/include/ELangBulkPushGen.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | std::vector GenerateBulkPushInstruction(uint32_t ret_delta, std::vector values_to_push);
7 | std::vector GenerateBulkPushInstructionWithEBXCall(
8 | uint32_t ret_delta, std::vector values_to_push,
9 | uint32_t ebx_delta_to_ret_addr, uint32_t call_delta_to_ret_addr);
10 |
--------------------------------------------------------------------------------
/LibELangPatch/include/ELangInitFnGen.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | /**
7 | * 0043659F | 8983 18040000 | mov dword ptr ds:[ebx+418],eax | <-- start address offset_process_heap: 0x418
8 | * 004365A5 | A1 A0E64700 | mov eax,dword ptr ds:[47E6A0] | <-- header_data[0]
9 | * (inst_address=0x004365A5, wnd_data_address=0x47E6A0)
10 | * 004365AA | 8983 C4000000 | mov dword ptr ds:[ebx+C4],eax |
11 | * 004365B0 | 8B0D A4E64700 | mov ecx,dword ptr ds:[47E6A4] | <-- header_data[1]
12 | * 004365B6 | 8B83 10040000 | mov eax,dword ptr ds:[ebx+410] | <-- offset_has_ole: 0x410
13 | * 004365BC | 898B C8000000 | mov dword ptr ds:[ebx+C8],ecx |
14 | * 004365C2 | 8B15 A8E64700 | mov edx,dword ptr ds:[47E6A8] | <-- header_data[2]
15 | * 004365C8 | 42 | inc edx |
16 | * 004365C9 | 85C0 | test eax,eax |
17 | * 004365CB | 8993 CC000000 | mov dword ptr ds:[ebx+CC],edx | <-- end address (size = 44)
18 | * @param offset_process_heap
19 | * @param offset_has_ole
20 | * @param header_data
21 | * @return
22 | */
23 | std::vector GenerateELangInitSnippet(uint32_t offset_process_heap, uint32_t offset_has_ole, uint32_t* header_data);
24 |
--------------------------------------------------------------------------------
/LibELangPatch/include/ELangLoaderInitGen.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | /**
8 | * 0040116D | FC | cld <-- addr start
9 | * 0040116E | DBE3 | fninit
10 | * 00401170 | E8 ECFFFFFF | call exe.401161 <-- call_delta = 0x0xFFFFFFEC
11 | * @param call_delta This can be `{}` if the call is empty.
12 | * @return
13 | */
14 | std::vector GenerateELangLoaderInit(std::optional call_delta);
15 |
--------------------------------------------------------------------------------
/LibELangPatch/include/ResolveCallDllFunctionGen.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | /**
7 | * .text:00419280 50 | push eax
8 | * .text:00419281 E8 DA FF FF FF | call sub_419260 <-- call_delta: 0xFFFFFFDA
9 | * .text:00419286 83 C4 04 | add esp, 4
10 | * .text:00419289 FF E0 | jmp eax
11 | * @param call_delta
12 | * @return
13 | */
14 | std::vector GenerateResolveCallDllFunction(uint32_t call_delta);
15 |
--------------------------------------------------------------------------------
/LibELangPatch/include/VArgsProxyGen.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | /**
7 | * 00418C90 | 8D4424 08 | lea eax,dword ptr ss:[esp+8] |
8 | * 00418C94 | 83EC 0C | sub esp,C |
9 | * 00418C97 | 50 | push eax |
10 | * 00418C98 | FF7424 14 | push dword ptr ss:[esp+14] |
11 | * 00418C9C | 33C0 | xor eax,eax |
12 | * 00418C9E | 894424 08 | mov dword ptr ss:[esp+8],eax |
13 | * 00418CA2 | 894424 0C | mov dword ptr ss:[esp+C],eax |
14 | * 00418CA6 | 894424 10 | mov dword ptr ss:[esp+10],eax |
15 | * 00418CAA | 8D5424 08 | lea edx,dword ptr ss:[esp+8] |
16 | * 00418CAE | 52 | push edx |
17 | * 00418CAF | FFD3 | call ebx | <-- core: call to the function
18 | * 00418CB1 | 8B4424 0C | mov eax,dword ptr ss:[esp+C] |
19 | * 00418CB5 | 8B5424 10 | mov edx,dword ptr ss:[esp+10] |
20 | * 00418CB9 | 8B4C24 14 | mov ecx,dword ptr ss:[esp+14] |
21 | * 00418CBD | 83C4 18 | add esp,18 |
22 | * 00418CC0 | C3 | ret |
23 | */
24 | std::vector GenerateVArgsProxyCode();
25 |
--------------------------------------------------------------------------------
/LibELangPatch/include/WndHandlerGen.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | /**
7 | * 0041883A | E8 B19AFFFF | call exe.4122F0 <-- CALL to get event handler
8 | * 0041887D | FF55 FC | call dword ptr ss:[ebp-4] <-- ELang Button Event Handler call
9 | * call_inst_address: 0x0041883A
10 | * call_inst_delta: 0xFFFF9AB1
11 | * @param call_inst_address
12 | * @param call_inst_delta
13 | * @return
14 | */
15 | std::vector GenerateWndHandlerCode(uint32_t call_inst_address, uint32_t call_inst_delta);
16 |
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | # ELangPatcher
2 |
3 | 针对易语言静态编译的代码进行轻微混淆处理,避免被插件一键识别部分关键函数。处理过的内容参考下方。
4 |
5 | 注意该轻微膨胀/混淆只能用来对抗现有的“一键识别”工具,不能和加密壳的效果比。
6 |
7 | 如果你能拿到这类工具的源码缝缝补补,应该也能让它重新识别。
8 |
9 | 
10 |
11 | ## 原理
12 |
13 | 对已知的部分特征进行魔改,大部分时候都是对简单操作进行膨胀,然后开辟新的内存空间跳转过去。
14 |
15 | 因为生成代码计算绝对地址比较麻烦,所以依赖 CALL 指令自动入栈的返回地址来计算正确跳转位置。
16 |
17 | ## 处理过的特征
18 |
19 | 只处理了一小部分特征。手动整起来太麻烦了,感觉不如利用特征码自动标记然后上加密壳批量处理了。
20 |
21 | - 对抗 [查找易语言按钮事件] 插件
22 | - 对抗 [EWnd v0.2] 插件的一键分析
23 | - 对抗 [EWnd Ultimate] 插件的一键分析
24 | - 对抗 [E-Debug] 程序/插件的一键分析
25 | - 对抗 [E-Decompiler] 插件的一键分析
26 | - 对抗 [易语言逆向分析助手] 的窗体信息分析
27 | - 对抗易语言初始化入口识别(`cld; fninit; call xxxx`)
28 | - 对抗控件处理事件识别(重写了个简单的,混淆程度不高 `call dword[ebp - 4]`)
29 | - 处理了找到的一些乱七八糟的特征…
30 |
31 | [EWnd Ultimate]: https://www.52pojie.cn/thread-1466188-1-1.html
32 | [EWnd v0.2]: https://www.52pojie.cn/thread-396634-1-1.html
33 | [E-Debug]: https://www.52pojie.cn/thread-1527446-1-1.html
34 | [E-Decompiler]: https://www.52pojie.cn/thread-1684608-1-1.html
35 | [易语言逆向分析助手]: https://www.52pojie.cn/thread-1586374-1-1.html
36 | [查找易语言按钮事件]: https://www.52pojie.cn/thread-1393607-1-1.html
37 |
38 | ## 从源码构建
39 |
40 | 首先确保安装有 [VS 2022]、[CMake]、[Git for Windows] 这三个程序。CMake 安装时需要选择将 `cmake.exe` 注册到系统。
41 |
42 | [VS 2022]: https://visualstudio.microsoft.com/zh-hans/downloads/
43 | [CMake]: https://cmake.org/download/#latest
44 | [Git for Windows]: https://github.com/git-for-windows/git/releases/latest
45 |
46 | ```bat
47 | :: 克隆仓库
48 | git clone https://github.com/FlyingRainyCats/ELangPatcher.git
49 | cd ELangPatcher
50 |
51 | :: 更新子模组
52 | git submodule update --init --recursive
53 |
54 | :: 开始构建
55 | cmake -Bcmake-build-vs2022 -G "Visual Studio 17 2022" -A Win32
56 | cmake --build cmake-build-vs2022 --config Release
57 | ```
58 |
59 | ## 集成到易语言
60 |
61 | 1. 打开易语言目录;
62 | 2. 打开该目录下的 `tools` 目录;
63 | 3. 将 `ELangPatcher.exe` 放入该目录;
64 | 4. 打开 `link.ini` 配置文件;
65 | 5. 找到结尾的 `post_link_action` 区域,并添加新的操作。
66 | 1. 静态编译后自动处理,参考添加 `post_link_action1="$(E_TOOLS)\ELangPatcher.exe" "$(TARGET)"`;
67 | 2. 如果有自动加壳,你需要调整序号,让 `ELangPatcher.exe` 先执行;
68 |
69 |
70 | ## 碎碎念
71 |
72 | - 易语言的编译顺序太“稳定”了,可以通过定位目标函数附近的函数来快速定位。
73 | - 没处理过特征的函数依然可以手动检索特征码找到。
74 | - 再做下去感觉需要自动化识别函数、反编译、然后随机混淆/膨胀了。
75 | - 要考虑的东西太多了,不适合我。
76 |
77 | ## 致谢
78 |
79 | - [fjqisba 老师的易语言逆向专栏]
80 | - [易语言程序分析笔记 - 看雪/PlaneJun]
81 |
82 | [fjqisba 老师的易语言逆向专栏]: https://fjqisba.github.io/categories/%E6%98%93%E8%AF%AD%E8%A8%80%E9%80%86%E5%90%91/
83 | [易语言程序分析笔记 - 看雪/PlaneJun]: https://bbs.kanxue.com/thread-274503.htm
84 |
--------------------------------------------------------------------------------
/assets/screenshot.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlyingRainyCats/ELangPatcher/fc351df0d979c440918aecda6485ff80d334a70a/assets/screenshot.webp
--------------------------------------------------------------------------------
/build_vs2022.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 | :: git submodule update --init --recursive
3 |
4 | cmake -Bcmake-build-vs2022 -G "Visual Studio 17 2022" -A Win32
5 | cmake --build cmake-build-vs2022 --config Release
6 | pause
7 |
--------------------------------------------------------------------------------
/src/ELangPatchFile.cpp:
--------------------------------------------------------------------------------
1 | #include "ELangPatchFile.h"
2 | #include "ELangPatcher.h"
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | namespace fs = std::filesystem;
9 |
10 | template
11 | const char *get_stream_error(const T &file) {
12 | if (file.bad()) {
13 | return "Unrecoverable I/O error occurred.";
14 | } else if (file.eof()) {
15 | return "End-of-File reached.";
16 | } else if (file.fail()) {
17 | return "Non-fatal I/O error occurred.";
18 | } else if (file.good()) {
19 | return "No error occurred.";
20 | }
21 | return "Unknown error.";
22 | }
23 |
24 | bool ELangPatchFile(const fs::path &file_path, const std::u8string &suffix, bool backup, bool fake_stub) {
25 | if (!fs::exists(file_path)) {
26 | fprintf(stderr, " ERR: file does not exist.\n");
27 | return false;
28 | }
29 |
30 | fs::path output_path{file_path};
31 | auto output_file_name = output_path.stem().u8string() + suffix + output_path.extension().u8string();
32 | output_path.replace_filename(output_file_name);
33 |
34 | if (backup && fs::exists(output_path)) {
35 | auto bak_file{output_path};
36 | bak_file.replace_extension(output_path.extension().u8string() + u8".bak");
37 |
38 | if (!fs::exists(bak_file)) {
39 | std::error_code ec_backup{};
40 | fs::copy_file(file_path, bak_file, ec_backup);
41 | if (ec_backup) {
42 | fprintf(stderr, " ERR: backup failed: %s\n", ec_backup.message().c_str());
43 | return false;
44 | }
45 | }
46 | }
47 |
48 | auto file_size = static_cast(fs::file_size(file_path));
49 | std::vector exe_data(file_size);
50 | exe_data.reserve(file_size * 2 + 0x4000);
51 | {
52 | std::ifstream ifs(file_path, std::ifstream::binary);
53 | if (!ifs.is_open()) {
54 | fprintf(stderr, " ERR: could not open file for read: %s\n", get_stream_error(ifs));
55 | return false;
56 | }
57 | ifs.read(reinterpret_cast(exe_data.data()), file_size);
58 | }
59 |
60 | auto patcher = MakeELangPatcher(exe_data);
61 | patcher->PatchAll();
62 | if (fake_stub) patcher->MiscAddFakeEWndStub();
63 |
64 | {
65 | std::ofstream ofs(output_path, std::ifstream::binary);
66 | if (!ofs.is_open()) {
67 | fprintf(stderr, " ERR: could not open file for write: %s\n", get_stream_error(ofs));
68 | return false;
69 | }
70 | ofs.write(reinterpret_cast(exe_data.data()), static_cast(exe_data.size()));
71 | }
72 | return true;
73 | }
74 |
--------------------------------------------------------------------------------
/src/ELangPatchFile.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | bool ELangPatchFile(const std::filesystem::path &file_path, const std::u8string& suffix, bool backup, bool fake_stub);
7 |
--------------------------------------------------------------------------------
/src/ELangPatcher.cpp:
--------------------------------------------------------------------------------
1 | #include "ELangPatcher.h"
2 | #include "ELangPatcher/ELangPatcherImpl.h"
3 |
4 | std::unique_ptr MakeELangPatcher(std::vector& exe_data) {
5 | return std::make_unique(exe_data);
6 | }
7 |
--------------------------------------------------------------------------------
/src/ELangPatcher.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | #include "SearchMatcher.h"
5 |
6 | class ELangPatcher {
7 | public:
8 | virtual ~ELangPatcher() = default;
9 |
10 | virtual void PatchDllFunctionInvokeCall() = 0;
11 | virtual void PatchEWndV02() = 0;
12 | virtual void PatchEWndUltimate() = 0;
13 | virtual void PatchWndEventHandlerMain() = 0;
14 | virtual void PatchKernelInvokeCall() = 0;
15 | virtual void PatchProxyStub() = 0;
16 | virtual void PatchLoadWndCall() = 0;
17 | virtual void PatchSuspiciousCallWithParam() = 0;
18 | virtual void PatchELangLoaderInitStub() = 0;
19 |
20 | virtual void MiscAddFakeEWndStub() = 0;
21 |
22 | inline void PatchAll() {
23 | PatchDllFunctionInvokeCall();
24 | PatchEWndV02();
25 | PatchEWndUltimate();
26 | PatchWndEventHandlerMain();
27 | PatchKernelInvokeCall();
28 | PatchProxyStub();
29 | PatchLoadWndCall();
30 | PatchSuspiciousCallWithParam();
31 | PatchELangLoaderInitStub();
32 | }
33 | };
34 |
35 | std::unique_ptr MakeELangPatcher(std::vector& exe_data);
36 |
--------------------------------------------------------------------------------
/src/ELangPatcher/ELangPatcherImpl.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../ELangPatcher.h"
4 | #include "../PEParser.h"
5 | #include "../SearchMatcher.h"
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | class ELangPatcherImpl : public ELangPatcher {
12 | typedef ELang::PatternSearch::PatternSegment PatternSegment;
13 | typedef ELang::PatternSearch::SearchMatcher SearchMatcher;
14 | typedef FlyingRainyCats::PEParser::PEParser PEParser;
15 |
16 | public:
17 | explicit ELangPatcherImpl(std::vector& data): data_(data), pe_(data_), mt_(std::random_device{}()) {}
18 |
19 | void PatchDllFunctionInvokeCall() override;
20 | void PatchEWndV02() override;
21 | void PatchEWndUltimate() override;
22 | void PatchWndEventHandlerMain() override;
23 | void PatchKernelInvokeCall() override;
24 | void PatchProxyStub() override;
25 | void PatchLoadWndCall() override;
26 | void PatchELangLoaderInitStub() override;
27 | void PatchSuspiciousCallWithParam() override;
28 | void MiscAddFakeEWndStub() override;
29 |
30 | private:
31 | std::vector& data_;
32 | PEParser pe_;
33 | std::mt19937 mt_;
34 | std::vector> code_caves_;
35 |
36 | inline int rand_int(int min, int max) {
37 | return std::uniform_int_distribution<>(min, max)(mt_);
38 | }
39 |
40 | inline uint32_t read_u32 (size_t offset) {
41 | return *reinterpret_cast(&data_[offset]);
42 | };
43 |
44 | inline void write_jmp(uint32_t foa_inst_offset, uint32_t foa_target_pos, uint8_t opcode = 0xE9) {
45 | auto rva_inst = pe_.FOAtoRVA(foa_inst_offset);
46 | auto rva_target = pe_.FOAtoRVA(foa_target_pos);
47 | data_[foa_inst_offset] = opcode;
48 | *reinterpret_cast(&data_.at(foa_inst_offset + 1)) = rva_target - (rva_inst + 5);
49 | }
50 |
51 | inline void write_call(uint32_t foa_inst_offset, uint32_t foa_target_pos) {
52 | write_jmp(foa_inst_offset, foa_target_pos, 0xE8);
53 | }
54 | };
55 |
--------------------------------------------------------------------------------
/src/ELangPatcher/PatchAddFakeEWndStub.cpp:
--------------------------------------------------------------------------------
1 | #include "ELangPatcherImpl.h"
2 |
3 | void ELangPatcherImpl::MiscAddFakeEWndStub() {
4 | std::vector junk_data_inner = {
5 | // start of function
6 | 0x55, 0x8B, 0xEC,
7 | // junk 1
8 | 0xe8,
9 | 0x50, 0x64, 0x89, 0x25, 0x00, 0x00, 0x00, 0x00, 0x81, 0xec, 0xac, 0x01, 0x00, 0x00, 0x53, 0x56, 0x57,
10 | // junk 2
11 | 0xea,
12 | 0x8b, 0x44, 0x24, 0x0c, 0x8b, 0x4c, 0x24, 0x08, 0x8b, 0x54, 0x24, 0x04, 0x50, 0x51, 0x52, 0xb9,
13 | // junk 3
14 | 0xe9,
15 | 0x83, 0xec, 0x0c, 0x33, 0xc0, 0x56, 0x8b, 0x74, 0x24, 0x1c, 0x57, 0x8b, 0x7c, 0x24, 0x18, 0xc7, 0x07, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x4e, 0x14, 0x85, 0xc9, 0x74, 0x13, 0x50, 0x8b, 0x46, 0x0c, 0x50, 0x68, 0xd6, 0x07, 0x00, 0x00,
16 | // junk 4
17 | 0x01, 0x52, 0xE8, 0x11, 0x00, 0x00, 0x00,
18 | // end of function
19 | 0x8B, 0xE5, 0x5D, 0xC2, 0x0C, 0x00};
20 |
21 | auto prefix_len = rand_int(0x10, 0x20);
22 | auto suffix_len = rand_int(0x10, 0x20);
23 | auto payload_len = prefix_len + junk_data_inner.size() + suffix_len;
24 | fprintf(stderr, " INFO: [MiscAddFakeEWndStub] add stub (len=%d bytes)\n", static_cast(payload_len));
25 | std::generate_n(pe_.ExpandTextSection(payload_len), payload_len, mt_);
26 | }
27 |
--------------------------------------------------------------------------------
/src/ELangPatcher/PatchDllResolveAndCall.cpp:
--------------------------------------------------------------------------------
1 | #include "ResolveCallDllFunctionGen.h"
2 | #include "ELangPatcherImpl.h"
3 |
4 | void ELangPatcherImpl::PatchDllFunctionInvokeCall() {
5 | ELang::PatternSearch::SearchMatcher pattern{{
6 | {0x50, 0xE8},
7 | PatternSegment::Skip(4), // call_delta
8 | {0x83, 0xC4, 0x04, 0xFF, 0xE0},
9 | }};
10 |
11 | for (auto it = data_.begin(); (it = pattern.search(it, data_.end())) != data_.end(); it += pattern.size()) {
12 | auto offset = std::distance(data_.begin(), it);
13 | auto call_delta = read_u32(offset + pattern.offset_at_item(1));
14 | fprintf(stderr, " INFO: [PatchDllFunctionInvokeCall] found (offset=0x%08x, call_delta=0x%08x)\n", static_cast(offset), call_delta);
15 |
16 | auto padding_beg = rand_int(2, 7);
17 | auto padding_end = rand_int(2, 7);
18 | auto snippet = GenerateResolveCallDllFunction(call_delta + 1);
19 |
20 | auto ptr_output = pe_.ExpandTextSection(padding_beg + snippet.size() + padding_end);
21 | it = data_.begin() + offset;
22 |
23 | std::generate_n(ptr_output, padding_beg, mt_);
24 | std::copy(snippet.cbegin(), snippet.cend(), ptr_output + padding_beg);
25 | std::generate_n(ptr_output + padding_beg + snippet.size(), padding_end, mt_);
26 |
27 | std::generate_n(it, pattern.size(), mt_);
28 | write_call(offset, ptr_output + padding_beg - data_.data());
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/ELangPatcher/PatchELangLoaderInitStub.cpp:
--------------------------------------------------------------------------------
1 | #include "ELangLoaderInitGen.h"
2 | #include "ELangPatcherImpl.h"
3 |
4 | void ELangPatcherImpl::PatchELangLoaderInitStub() {
5 | ELang::PatternSearch::SearchMatcher pattern{{
6 | {0xFC, 0xDB, 0xE3, 0xE8, 0xEC},
7 | }};
8 |
9 | auto it = data_.begin();
10 | while ((it = pattern.search(it, data_.end())) != data_.end()) {
11 | auto offset = std::distance(data_.begin(), it);
12 | fprintf(stderr, " INFO: [PatchELangLoaderInitStub] found (offset=0x%08tx)\n", offset);
13 |
14 | const auto call_delta = *reinterpret_cast(data_.data() + offset + 4);
15 |
16 | // Generate call inst
17 | auto snippet = GenerateELangLoaderInit({call_delta + 3});
18 | auto p_snippet_out = pe_.ExpandTextSection(snippet.size());
19 | it = data_.begin() + offset;
20 | std::copy(snippet.cbegin(), snippet.cend(), p_snippet_out);
21 |
22 | // Write call inst + 3 byte junk
23 | auto offset_snippet = p_snippet_out - data_.data();
24 | write_call(offset, offset_snippet);
25 | std::generate_n(it + 5, 3, mt_);
26 |
27 | it += pattern.size();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/ELangPatcher/PatchEWndUltimate.cpp:
--------------------------------------------------------------------------------
1 | #include "ELangInitFnGen.h"
2 | #include "ELangPatcherImpl.h"
3 |
4 | #include
5 |
6 | void ELangPatcherImpl::PatchEWndUltimate() {
7 | ELang::PatternSearch::SearchMatcher pattern{{
8 | {0x89, 0x83},
9 | PatternSegment::Skip(4),// offset: heap
10 | {0xA1},
11 | PatternSegment::Skip(4),// wnd_data_offset
12 | {0x89, 0x83, 0xC4, 0x00, 0x00, 0x00, 0x8B, 0x0D},
13 | PatternSegment::Skip(4),
14 | {0x8B, 0x83},
15 | PatternSegment::Skip(4),// offset: ole_enabled
16 | {0x89, 0x8B, 0xC8, 0x00, 0x00, 0x00, 0x8B, 0x15},
17 | PatternSegment::Skip(4),
18 | {0x42, 0x85, 0xC0, 0x89, 0x93, 0xCC, 0x00, 0x00, 0x00},
19 | }};
20 |
21 | std::array data_elang_header{};
22 | constexpr size_t kPostCallJunkLen = 0x2F;
23 |
24 | auto it = data_.begin();
25 | while ((it = pattern.search(it, data_.end())) != data_.end()) {
26 | auto offset = std::distance(data_.begin(), it);
27 | auto heap_offset = read_u32(offset + pattern.offset_at_item(1));
28 | auto has_ole_offset = read_u32(offset + pattern.offset_at_item(7));
29 | auto wnd_data_address = read_u32(offset + pattern.offset_at_item(3));
30 | auto wnd_data_offset = static_cast(pe_.RVAtoFOA(wnd_data_address));
31 | fprintf(stderr, " INFO: [PatchEWndUltimate] found (offset=0x%08tx, data=0x%08x, wnd_data_offset=0x%08x)\n", offset, wnd_data_address, wnd_data_offset);
32 |
33 | std::copy_n(data_.begin() + wnd_data_offset, data_elang_header.size(), data_elang_header.begin());
34 |
35 | // Inject our new header (junk + code)
36 | size_t pre_stub_junk_len = rand_int(0x04, 0x20);
37 | size_t post_stub_junk_len = rand_int(0x04, 0x20);
38 | auto snippet = GenerateELangInitSnippet(heap_offset, has_ole_offset, reinterpret_cast(data_elang_header.data()));
39 | auto injected_code_ptr = pe_.ExpandTextSection(pre_stub_junk_len + snippet.size() + post_stub_junk_len);
40 | it = data_.begin() + offset;
41 |
42 | // Write junk + stub + junk
43 | std::generate_n(injected_code_ptr, pre_stub_junk_len, mt_);
44 | std::copy_n(snippet.begin(), snippet.size(), injected_code_ptr + pre_stub_junk_len);
45 | std::generate_n(injected_code_ptr + pre_stub_junk_len + snippet.size(), post_stub_junk_len, mt_);
46 |
47 | auto offset_stub = injected_code_ptr + pre_stub_junk_len - data_.data();
48 | fprintf(stderr, " - stub added: 0x%08x (file offset: %08x)\n", static_cast(pe_.FOAtoRVA(offset_stub)), static_cast(offset_stub));
49 | write_call(offset, offset_stub);
50 |
51 | // Stack frame adjustment to bypass some sig matching
52 | {
53 | uint32_t len = std::uniform_int_distribution(0x04, 0xFF)(mt_) & 0xFC;
54 | auto p_stack_offset = reinterpret_cast(data_.data() + offset - (0x00436A9F - 0x00436A88) + 2);
55 | *p_stack_offset += len;
56 | }
57 |
58 | // Write junk to where the header data was and post call to our stub
59 | std::generate_n(data_.begin() + wnd_data_offset, data_elang_header.size(), mt_);
60 | std::generate_n(it + 5, kPostCallJunkLen, mt_);
61 |
62 | it += pattern.size();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/ELangPatcher/PatchEWndV02.cpp:
--------------------------------------------------------------------------------
1 | #include "CallProxyStubWithEcxGen.h"
2 | #include "ELangPatcherImpl.h"
3 |
4 | void ELangPatcherImpl::PatchEWndV02() {
5 | ELang::PatternSearch::SearchMatcher pattern{{
6 | {0x8B, 0x44, 0x24, 0x0C, 0x8B, 0x4C, 0x24, 0x08, 0x8B, 0x54, 0x24, 0x04, 0x50, 0x51, 0x52, 0xB9},
7 | PatternSegment::Skip(4),
8 | {0xE8},
9 | PatternSegment::Skip(4),
10 | {0xC2, 0x0C, 0x00},
11 | }};
12 |
13 | auto it = data_.begin();
14 | while ((it = pattern.search(it, data_.end())) != data_.end()) {
15 | auto offset = std::distance(data_.begin(), it);
16 | auto ecx_value = read_u32(offset + pattern.offset_at_item(1));
17 | auto call_delta = read_u32(offset + pattern.offset_at_item(3));
18 | fprintf(stderr, " INFO: [PatchEWndV02] found (offset=0x%08x, ecx=0x%08x, call_delta=0x%08x)\n", static_cast(offset), ecx_value, call_delta);
19 |
20 | auto snippet = GenerateCallProxyStubWithEcx(15, 3, ecx_value, call_delta);
21 | std::copy(snippet.cbegin(), snippet.cend(), it);
22 | it += pattern.size();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/ELangPatcher/PatchKernelInvokeCall.cpp:
--------------------------------------------------------------------------------
1 | #include "ELangPatcherImpl.h"
2 | #include "VArgsProxyGen.h"
3 |
4 | void ELangPatcherImpl::PatchKernelInvokeCall() {
5 | std::vector patterns{
6 | ELang::PatternSearch::SearchMatcher{{
7 | {0x8D, 0x54, 0x24, 0x08, 0x83, 0xEC, 0x0C, 0x52, 0xFF, 0x74, 0x24, 0x14, 0xC7, 0x44, 0x24, 0x08, 0x00, 0x00, 0x00, 0x00, 0xC7, 0x44, 0x24, 0x0C, 0x00, 0x00, 0x00, 0x00, 0xC7, 0x44, 0x24, 0x10, 0x00, 0x00, 0x00, 0x00, 0x8D, 0x54, 0x24, 0x08, 0x52, 0xFF, 0xD3, 0x8B, 0x44, 0x24, 0x0C, 0x8B, 0x54, 0x24, 0x10, 0x8B, 0x4C, 0x24, 0x14, 0x83, 0xC4, 0x18, 0xC3},
8 | }},
9 | ELang::PatternSearch::SearchMatcher{{
10 | {0x8D, 0x44, 0x24, 0x08, 0x83, 0xEC, 0x0C, 0x50, 0xFF, 0x74, 0x24, 0x14, 0x33, 0xC0, 0x89, 0x44, 0x24, 0x08, 0x89, 0x44, 0x24, 0x0C, 0x89, 0x44, 0x24, 0x10, 0x8D, 0x54, 0x24, 0x08, 0x52, 0xFF, 0xD3, 0x8B, 0x44, 0x24, 0x0C, 0x8B, 0x54, 0x24, 0x10, 0x8B, 0x4C, 0x24, 0x14, 0x83, 0xC4, 0x18, 0xC3},
11 | }},
12 | };
13 |
14 | int pattern_id{0};
15 | for (auto &pattern: patterns) {
16 | auto pattern_size = pattern.size();
17 |
18 | for (auto it = data_.begin(); (it = pattern.search(it, data_.end())) != data_.end(); it += pattern_size) {
19 | auto offset = std::distance(data_.begin(), it);
20 |
21 | auto padding_beg = rand_int(2, 7);
22 | auto padding_end = rand_int(2, 7);
23 | auto snippet = GenerateVArgsProxyCode();
24 |
25 | auto ptr_output = pe_.ExpandTextSection(padding_beg + snippet.size() + padding_end);
26 | it = data_.begin() + offset;
27 |
28 | fprintf(stderr, " INFO: [PatchKernelInvokeCall#%d] found (offset=0x%08tx, len=%04x, replace_len=%04x)\n", pattern_id, offset, static_cast(pattern_size), static_cast(snippet.size()));
29 |
30 | std::generate_n(ptr_output, padding_beg, mt_);
31 | std::copy(snippet.cbegin(), snippet.cend(), ptr_output + padding_beg);
32 | std::generate_n(ptr_output + padding_beg + snippet.size(), padding_end, mt_);
33 |
34 | std::generate_n(it, pattern.size(), mt_);
35 | write_jmp(offset, ptr_output + padding_beg - data_.data());
36 | }
37 | pattern_id++;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/ELangPatcher/PatchLoadWndCall.cpp:
--------------------------------------------------------------------------------
1 | #include "ELangBulkPushGen.h"
2 | #include "ELangPatcherImpl.h"
3 |
4 | void ELangPatcherImpl::PatchLoadWndCall() {
5 | // @formatter:off
6 | std::vector patterns{
7 | ELang::PatternSearch::SearchMatcher{{
8 | {0x68}, PatternSegment::Skip(4),
9 | {0x6A}, PatternSegment::Skip(1),
10 | {0x68}, PatternSegment::Skip(4),
11 | {0x68}, PatternSegment::Skip(4),
12 | {0x68}, PatternSegment::Skip(4),
13 | {0x68}, PatternSegment::Skip(4),
14 | {0x68}, PatternSegment::Skip(4),
15 | {0x68}, PatternSegment::Skip(4),
16 | {0x68}, PatternSegment::Skip(4),
17 | {0x68, 0x03, 0x00, 0x00, 0x00},// 3 args
18 | {0xBB}, PatternSegment::Skip(4),
19 | {0xE8}, PatternSegment::Skip(4),
20 | }},
21 | ELang::PatternSearch::SearchMatcher{{
22 | {0x68}, PatternSegment::Skip(4),
23 | {0x6A}, PatternSegment::Skip(1),
24 | {0x68}, PatternSegment::Skip(4),
25 | {0x6A}, PatternSegment::Skip(1),
26 | {0x6A}, PatternSegment::Skip(1),
27 | {0x6A}, PatternSegment::Skip(1),
28 | {0x68}, PatternSegment::Skip(4),
29 | {0x68}, PatternSegment::Skip(4),
30 | {0x68}, PatternSegment::Skip(4),
31 | {0x68, 0x03, 0x00, 0x00, 0x00},// 3 args
32 | {0xBB}, PatternSegment::Skip(4), // mov ebx, ???
33 | {0xE8}, PatternSegment::Skip(4), // call ???
34 | }},
35 | };
36 |
37 | std::vector values{};
38 | values.reserve(10);
39 |
40 | for (auto &pattern: patterns) {
41 | for (auto it = data_.begin(); (it = pattern.search(it, data_.end())) != data_.end(); it += pattern.size()) {
42 | auto offset = std::distance(data_.begin(), it);
43 | auto ebx = read_u32(offset + pattern.offset_at_item(20));
44 | auto call_delta = read_u32(offset + pattern.offset_at_item(22));
45 |
46 | values.resize(0);
47 | for (int i = 1; i < 18; i += 2) {
48 | auto len = pattern.size_at_item(i);
49 | if (len == 4) {
50 | values.push_back(read_u32(offset + pattern.offset_at_item(i)));
51 | } else {
52 | values.push_back(data_[offset + pattern.offset_at_item(i)]);
53 | }
54 | }
55 | values.push_back(3);
56 |
57 | // Might be data pointer, skip...
58 | if (values[0] != 0x80000002 || (values[3] != 0x10001 && values[3] != 0) || values[6] != 0x10001) {
59 | continue;
60 | }
61 |
62 | fprintf(stderr, " INFO: [PatchLoadWndCall] found (offset=0x%08x, ebx=0x%08x, call_delta=0x%08x)\n", static_cast(offset), ebx, call_delta);
63 |
64 | auto padding_beg = rand_int(2, 7);
65 | auto padding_end = rand_int(2, 7);
66 | auto ret_addr_foa = offset + pattern.size();
67 | auto ret_addr_rva = pe_.FOAtoRVA(ret_addr_foa);
68 | auto snippet = GenerateBulkPushInstructionWithEBXCall(pattern.size() - 5, values, ebx - ret_addr_rva, call_delta);
69 |
70 | auto ptr_output = pe_.ExpandTextSection(padding_beg + snippet.size() + padding_end);
71 | it = data_.begin() + offset;
72 |
73 | // write new snippet
74 | std::generate_n(ptr_output, padding_beg, mt_);
75 | std::copy(snippet.cbegin(), snippet.cend(), ptr_output + padding_beg);
76 | std::generate_n(ptr_output + padding_beg + snippet.size(), padding_end, mt_);
77 |
78 | // write our call to new fn
79 | std::generate_n(it, pattern.size(), mt_);
80 | write_call(offset, ptr_output + padding_beg - data_.data());
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/ELangPatcher/PatchProxyStub.cpp:
--------------------------------------------------------------------------------
1 | #include "CallProxyStubWithEcxGen.h"
2 | #include "ELangPatcherImpl.h"
3 |
4 | class ProxyStubContainer {
5 | public:
6 | ProxyStubContainer(const char *name, int arg_count, std::initializer_list pattern_segments) : name_(name) {
7 | pattern_ = {pattern_segments};
8 | arg_count_ = {arg_count};
9 | beg_padding_ = static_cast(pattern_.offset_at_item(1)) - 1;
10 | end_padding_ = static_cast(pattern_.size()) - static_cast(pattern_.offset_at_item(pattern_.pattern_count() - 1));
11 | }
12 |
13 | const char *name_{};
14 | ELang::PatternSearch::SearchMatcher pattern_{};
15 | int arg_count_{};
16 | int beg_padding_{};
17 | int end_padding_{};
18 | };
19 |
20 | void ELangPatcherImpl::PatchProxyStub() {
21 | std::vector patterns{
22 | {"ELibInvokeCall", 1,
23 | {
24 | {0x55, 0x8B, 0xEC, 0x8B, 0x45, 0x08, 0x50, 0xB9},
25 | PatternSegment::Skip(4),
26 | {0xE8},
27 | PatternSegment::Skip(4),
28 | {0x5D, 0xC3},
29 | }},
30 |
31 | {"LoadInitWindow", 4,
32 | {
33 | {0x55, 0x8B, 0xEC, 0x8B, 0x45, 0x14, 0x50, 0x8B, 0x4D, 0x10, 0x51, 0x8B, 0x55, 0x0C, 0x52, 0x8B, 0x45, 0x08, 0x50, 0xB9},
34 | PatternSegment::Skip(4),
35 | {0xE8},
36 | PatternSegment::Skip(4),
37 | {0x5D, 0xC3},
38 | }},
39 |
40 | {"UnknownCtrlRelated", 6,
41 | {
42 | {0x55, 0x8B, 0xEC, 0x8B, 0x45, 0x1C, 0x50, 0x8B, 0x4D, 0x18, 0x51, 0x8B, 0x55, 0x14, 0x52, 0x8B, 0x45, 0x10, 0x50, 0x8B, 0x4D, 0x0C, 0x51, 0x8B, 0x55, 0x08, 0x52, 0xB9},
43 | PatternSegment::Skip(4),
44 | {0xE8},
45 | PatternSegment::Skip(4),
46 | {0x5D, 0xC3},
47 | }},
48 | };
49 |
50 | for (const auto& item: patterns) {
51 | auto &pattern = item.pattern_;
52 | auto beg_padding = item.beg_padding_;
53 | auto end_padding = item.end_padding_;
54 | auto arg_count = item.arg_count_;
55 | const auto *name = item.name_;
56 |
57 | int count{0};
58 | for (auto it = data_.begin(); (it = pattern.search(it, data_.end())) != data_.end(); it += pattern.size()) {
59 | auto offset = std::distance(data_.begin(), it);
60 | auto ecx_value = read_u32(offset + pattern.offset_at_item(1));
61 | auto call_delta = read_u32(offset + pattern.offset_at_item(3));
62 | fprintf(stderr, " INFO: [%s#%d] found (offset=0x%08x, args=[%d], ecx=0x%08x, call_delta=0x%08x)\n", name, count, static_cast(offset), arg_count, ecx_value, call_delta);
63 |
64 | auto snippet = GenerateCallProxyStubWithEcxCdecl(arg_count, beg_padding, end_padding, ecx_value, call_delta);
65 | std::copy(snippet.cbegin(), snippet.cend(), it);
66 | count++;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/ELangPatcher/PatchSuspiciousCallWithParam.cpp:
--------------------------------------------------------------------------------
1 | #include "CdeclPushAndCallGen.h"
2 | #include "ELangPatcherImpl.h"
3 | #include "ResolveCallDllFunctionGen.h"
4 |
5 | #include
6 |
7 | void ELangPatcherImpl::PatchSuspiciousCallWithParam() {
8 | ELang::PatternSearch::SearchMatcher pattern{{
9 | {0x68},// push imm32
10 | PatternSegment::Skip(4),// push_value
11 | {0xE8},// call $+????
12 | PatternSegment::Skip(4),
13 | }};
14 |
15 | constexpr std::array kSigJumpIndirect{0xFF, 0x25};
16 | constexpr std::array kSigBalanceStack{0x83, 0xC4}; // add esp, ??
17 |
18 | for (auto it = data_.begin(); (it = pattern.search(it, data_.end())) != data_.end(); it += pattern.size()) {
19 | auto offset = std::distance(data_.begin(), it);
20 | auto push_value = read_u32(offset + pattern.offset_at_item(1));
21 | auto call_delta = read_u32(offset + pattern.offset_at_item(3));
22 |
23 | auto offset_to_call_target = offset + 10 + call_delta;
24 | if (offset_to_call_target >= data_.size() + 1) continue;
25 | if (!std::equal(kSigJumpIndirect.cbegin(), kSigJumpIndirect.cend(), &data_[offset_to_call_target])) continue;
26 | if ((push_value & 0xFFFF0000) != 0 && !std::equal(kSigBalanceStack.cbegin(), kSigBalanceStack.cend(), &data_[offset + pattern.size()])) {
27 | continue;
28 | }
29 |
30 | fprintf(stderr, " INFO: [PatchSuspiciousCallWithParam] found (offset=0x%08x, push_value=0x%08x, call_delta=0x%08x)\n", static_cast(offset), push_value, call_delta);
31 |
32 | auto padding_beg = rand_int(2, 7);
33 | auto padding_end = rand_int(2, 7);
34 | auto snippet = GenerateCdeclPushAndCall(push_value, call_delta + 5, 5);
35 |
36 | auto ptr_output = pe_.ExpandTextSection(padding_beg + snippet.size() + padding_end);
37 | it = data_.begin() + offset;
38 |
39 | std::generate_n(ptr_output, padding_beg, mt_);
40 | std::copy(snippet.cbegin(), snippet.cend(), ptr_output + padding_beg);
41 | std::generate_n(ptr_output + padding_beg + snippet.size(), padding_end, mt_);
42 |
43 | std::generate_n(it, pattern.size(), mt_);
44 | write_call(offset, ptr_output + padding_beg - data_.data());
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/ELangPatcher/PatchWndEventHandlerMain.cpp:
--------------------------------------------------------------------------------
1 | #include "ELangPatcherImpl.h"
2 | #include "WndHandlerGen.h"
3 |
4 | void ELangPatcherImpl::PatchWndEventHandlerMain() {
5 | ELang::PatternSearch::SearchMatcher pattern{{
6 | {0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x18, 0x53, 0x56, 0x57, 0x89, 0x4D, 0xE8, 0x8B, 0x45, 0x08,
7 | 0x8B, 0x48, 0x0C, 0x51, 0x8B, 0x55, 0x08, 0x8B, 0x42, 0x08, 0x50, 0x8B, 0x4D, 0x08, 0x8B,
8 | 0x51, 0x04, 0x52, 0x8B, 0x45, 0x08, 0x8B, 0x08, 0x51, 0x8B, 0x4D, 0xE8},
9 | {0xE8},
10 | PatternSegment::Skip(4),
11 | }};
12 | ELang::PatternSearch::SearchMatcher pattern_fn_end{
13 | {0x8B, 0xE5, 0x5D, 0xC2, 0x04, 0x00},
14 | };
15 |
16 | constexpr size_t kMaxSearchFunctionSize = 0x200;
17 |
18 | for (auto it = data_.begin(); (it = pattern.search(it, data_.end())) != data_.end(); it += pattern.size()) {
19 | auto offset = std::distance(data_.begin(), it);
20 | auto call_foa = static_cast(offset + pattern.offset_at_item(1));
21 | auto call_rva = static_cast(pe_.FOAtoRVA(call_foa));
22 | auto call_inst_delta = read_u32(offset + pattern.offset_at_item(2));
23 |
24 | auto it_fn_end_search_last = std::min(it + kMaxSearchFunctionSize, data_.end());
25 | auto it_fn_end = pattern_fn_end.search(it, it_fn_end_search_last);
26 | if (it_fn_end == it_fn_end_search_last) {
27 | continue;
28 | }
29 | auto offset_fn_end = std::distance(data_.begin(), it_fn_end);
30 | fprintf(stderr, " INFO: [PatchWndEventHandlerMain] found (fn_end=%08tx, offset=0x%08tx, inst=0x%08x(p: 0x%08x), delta=0x%08x)\n", offset_fn_end, offset, call_foa, call_rva, call_inst_delta);
31 |
32 | auto function_size = static_cast(offset_fn_end + pattern_fn_end.size() - offset);
33 |
34 | auto padding_beg = rand_int(2, 7);
35 | auto padding_end = rand_int(2, 7);
36 | auto snippet = GenerateWndHandlerCode(static_cast(call_rva), call_inst_delta + pattern.size_at_item(0));
37 |
38 | auto ptr_output = pe_.ExpandTextSection(padding_beg + snippet.size() + padding_end);
39 | it = data_.begin() + offset;
40 |
41 | std::generate_n(ptr_output, padding_beg, mt_);
42 | std::copy(snippet.cbegin(), snippet.cend(), ptr_output + padding_beg);
43 | std::generate_n(ptr_output + padding_beg + snippet.size(), padding_end, mt_);
44 |
45 | std::generate_n(it, function_size, mt_);
46 | write_call(offset, ptr_output + padding_beg - data_.data());
47 | code_caves_.emplace_back(offset + 5, function_size - 5);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/PEParser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include
5 |
6 | #if !NDEBUG
7 | #include
8 | #include
9 | #endif
10 |
11 | #ifdef min
12 | #undef min
13 | #endif
14 | #ifdef max
15 | #undef max
16 | #endif
17 |
18 | namespace FlyingRainyCats {
19 | namespace helper {
20 | inline uint32_t round_up_to_section_size(uint32_t size) {
21 | if (size % 0x1000 != 0) {
22 | size += 0x1000 - (size % 0x1000);
23 | }
24 | return size;
25 | }
26 | }// namespace helper
27 |
28 | namespace PEParser {
29 | template
30 | class PEParser {
31 | public:
32 | using INNER_IMAGE_NT_HEADERS = typename std::conditional_t;
33 | using INNER_PIMAGE_NT_HEADERS = typename std::conditional_t;
34 | using INNER_PIMAGE_DOS_HEADER = PIMAGE_DOS_HEADER;
35 | using INNER_PIMAGE_OPTIONAL_HEADER = typename std::conditional_t;
36 |
37 | std::vector &exe_data_;
38 | inline explicit PEParser(std::vector &exe_data) : exe_data_(exe_data) {}
39 |
40 | inline INNER_PIMAGE_NT_HEADERS GetNtHeader() const {
41 | return INNER_PIMAGE_NT_HEADERS(exe_data_.data() + INNER_PIMAGE_DOS_HEADER(exe_data_.data())->e_lfanew);
42 | }
43 |
44 | inline INNER_PIMAGE_OPTIONAL_HEADER GetNtOptionalHeader() const {
45 | return &GetNtHeader()->OptionalHeader;
46 | }
47 |
48 | [[nodiscard]] inline uint32_t RVAtoFOA(DWORD address) const {
49 | auto p_nt_header = GetNtHeader();
50 | auto p_file_header = &p_nt_header->FileHeader;
51 | auto section_count = std::size_t(p_file_header->NumberOfSections);
52 |
53 | address -= p_nt_header->OptionalHeader.ImageBase;
54 |
55 | auto section = (PIMAGE_SECTION_HEADER) ((uint8_t *) (p_nt_header) + offsetof(IMAGE_NT_HEADERS, OptionalHeader) + p_nt_header->FileHeader.SizeOfOptionalHeader);
56 |
57 | for (std::size_t i = 0; i < section_count; i++) {
58 | if ((section->VirtualAddress <= address) && (address < (section->VirtualAddress + section->Misc.VirtualSize))) {
59 | return section->PointerToRawData + (address - section->VirtualAddress);
60 | }
61 | section++;
62 | }
63 |
64 | return 0;
65 | }
66 |
67 | [[nodiscard]] inline uintptr_t FOAtoRVA(const DWORD address) const {
68 | auto p_nt_header = INNER_PIMAGE_NT_HEADERS((uint8_t *) (exe_data_.data()) + PIMAGE_DOS_HEADER(exe_data_.data())->e_lfanew);
69 | auto p_file_header = &p_nt_header->FileHeader;
70 | auto section_count = std::size_t(p_file_header->NumberOfSections);
71 |
72 | auto section = (PIMAGE_SECTION_HEADER) ((uint8_t *) (p_nt_header) + offsetof(INNER_IMAGE_NT_HEADERS, OptionalHeader) + p_nt_header->FileHeader.SizeOfOptionalHeader);
73 |
74 | for (std::size_t i = 0; i < section_count; i++) {
75 | if ((section->PointerToRawData <= address) && (address < (section->PointerToRawData + section->SizeOfRawData))) {
76 | return p_nt_header->OptionalHeader.ImageBase + address - section->PointerToRawData + section->VirtualAddress;
77 | }
78 | section++;
79 | }
80 |
81 | return 0;
82 | }
83 |
84 | inline uint8_t *ExpandTextSection(uint32_t size) {
85 | auto p_nt_header = INNER_PIMAGE_NT_HEADERS((uint8_t *) (exe_data_.data()) + PIMAGE_DOS_HEADER(exe_data_.data())->e_lfanew);
86 | auto p_file_header = &p_nt_header->FileHeader;
87 | auto section_count = std::size_t(p_file_header->NumberOfSections);
88 |
89 | auto section = (PIMAGE_SECTION_HEADER) ((uint8_t *) (p_nt_header) + offsetof(INNER_IMAGE_NT_HEADERS, OptionalHeader) + p_nt_header->FileHeader.SizeOfOptionalHeader);
90 |
91 | auto &image_size = GetNtOptionalHeader()->SizeOfImage;
92 |
93 | for (std::size_t i = 0; i < section_count; i++) {
94 | if (strcmp((char *) section->Name, ".text") == 0) {
95 | if (section->Misc.VirtualSize + size < section->SizeOfRawData) {
96 | auto offset = section->Misc.VirtualSize;
97 | section->Misc.VirtualSize += size;
98 | return &exe_data_.at(section->PointerToRawData + offset);
99 | }
100 | } else if (strcmp((char *) section->Name, ".txt2") == 0) {
101 | exe_data_.resize(exe_data_.size() + size);
102 | auto offset = section->SizeOfRawData;
103 | section->SizeOfRawData += size;
104 | image_size -= section->Misc.VirtualSize;
105 | section->Misc.VirtualSize = helper::round_up_to_section_size(section->SizeOfRawData);
106 | image_size += section->Misc.VirtualSize;
107 | return &exe_data_.at(section->PointerToRawData + offset);
108 | }
109 | section++;
110 | }
111 |
112 | auto last_section = §ion[-1];
113 | p_file_header->NumberOfSections++;
114 |
115 | memset(section, 0, sizeof(*section));
116 | memcpy(section->Name, ".txt2", 6);
117 | section->PointerToRawData = exe_data_.size();
118 | section->SizeOfRawData = size;
119 | section->Misc.VirtualSize = helper::round_up_to_section_size(size);
120 | image_size = helper::round_up_to_section_size(image_size) + section->Misc.VirtualSize;
121 | section->VirtualAddress = helper::round_up_to_section_size(last_section->VirtualAddress + last_section->Misc.VirtualSize);
122 | section->Characteristics = IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_CODE;
123 | exe_data_.resize(exe_data_.size() + size);
124 |
125 | return &exe_data_.at(exe_data_.size() - size);
126 | }
127 | };
128 | }// namespace PEParser
129 | }// namespace FlyingRainyCats
130 |
--------------------------------------------------------------------------------
/src/SearchMatcher.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | namespace ELang::PatternSearch {
8 | enum class Type {
9 | Match = 0,
10 | Skip,
11 | };
12 |
13 | struct PatternSegment {
14 | Type type_;
15 | size_t len_;
16 | std::vector data_{};
17 |
18 | explicit PatternSegment(size_t seek_len) : type_(Type::Skip), len_(seek_len) {}
19 | PatternSegment(std::initializer_list data) : type_(Type::Match) {
20 | len_ = data.size();
21 | data_ = {data.begin(), data.end()};
22 | }
23 | PatternSegment(size_t data_len, const void *data) : type_(Type::Match), len_(data_len) {
24 | auto p_data = reinterpret_cast(data);
25 | data_ = {p_data, p_data + data_len};
26 | }
27 |
28 | static PatternSegment Skip(size_t n) {
29 | return PatternSegment(n);
30 | }
31 | };
32 |
33 | class SearchMatcher {
34 | public:
35 | SearchMatcher(std::initializer_list pattern) {
36 | pattern_.insert(pattern_.end(), pattern.begin(), pattern.end());
37 | for (auto &segment: pattern) {
38 | len_ += segment.len_;
39 | }
40 | }
41 |
42 | [[nodiscard]] size_t pattern_count() const {
43 | return pattern_.size();
44 | }
45 |
46 | [[nodiscard]] size_t offset_at_item(size_t idx) const {
47 | size_t offset = 0;
48 | for (size_t i = 0; i < idx; ++i) {
49 | offset += pattern_[i].len_;
50 | }
51 | return offset;
52 | }
53 |
54 | [[nodiscard]] size_t size_at_item(size_t idx) const {
55 | return pattern_[idx].len_;
56 | }
57 |
58 | template
59 | bool matches(It first) const {
60 | for (const auto &segment: pattern_) {
61 | switch (segment.type_) {
62 | case Type::Match:
63 | if (!std::equal(segment.data_.cbegin(), segment.data_.cend(), first)) {
64 | return false;
65 | }
66 | break;
67 | case Type::Skip:
68 | break;
69 | }
70 | first += segment.len_;
71 | }
72 |
73 | return true;
74 | }
75 |
76 | template
77 | It search(It first, It last) const {
78 | It search_end = last - len_;
79 | while (first < search_end) {
80 | if (matches(first)) {
81 | return first;
82 | }
83 | first++;
84 | }
85 |
86 | return last;
87 | }
88 |
89 | [[nodiscard]] ptrdiff_t size() const {
90 | return static_cast(len_);
91 | }
92 |
93 | private:
94 | std::vector pattern_{};
95 | size_t len_{0};
96 | };
97 |
98 | }// namespace ELang::PatternSearch
99 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include "ELangPatchFile.h"
2 | #include "ELangPatcher.h"
3 |
4 | #include
5 | #include
6 |
7 | #include
8 |
9 | #include
10 | #include
11 | #include
12 |
13 | namespace fs = std::filesystem;
14 |
15 | std::string ConvertWideToMultibyte(UINT CodePage, const std::wstring &wstr) {
16 | if (wstr.empty()) return {};
17 | int sizeNeeded = WideCharToMultiByte(CodePage, 0, wstr.data(), -1, nullptr, 0, nullptr, nullptr);
18 | std::string result(sizeNeeded, 0);
19 | WideCharToMultiByte(CodePage, 0, wstr.data(), -1, &result[0], sizeNeeded, nullptr, nullptr);
20 | return result;
21 | }
22 |
23 | bool checkCallingFromE() {
24 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
25 |
26 | PROCESSENTRY32 pe{};
27 | pe.dwSize = sizeof(PROCESSENTRY32);
28 |
29 | DWORD pid = GetCurrentProcessId();
30 | DWORD ppid{};
31 | std::vector elang_pids{};
32 | elang_pids.reserve(8);
33 | if (Process32First(hSnapshot, &pe)) {
34 | do {
35 | if (pe.th32ProcessID == pid) {
36 | ppid = pe.th32ParentProcessID;
37 | } else if (_stricmp(pe.szExeFile, "e.exe") == 0) {
38 | elang_pids.push_back(pe.th32ProcessID);
39 | }
40 | } while (Process32Next(hSnapshot, &pe));
41 | }
42 | CloseHandle(hSnapshot);
43 |
44 | return std::find(elang_pids.cbegin(), elang_pids.cend(), ppid) != elang_pids.cend();
45 | }
46 |
47 | int main_unicode(int argc, wchar_t *argv[]) {
48 | cxxopts::Options options("ELang-Patcher", "ELang AntiEWnd by FlyingRainyCats (爱飞的猫 @52pojie.cn)");
49 | options.add_options() //
50 | ("b,backup", "Enable backup", cxxopts::value()->default_value("true")) //
51 | ("fake-stub", "Insert fake stub", cxxopts::value()->default_value("true"))//
52 | ("suffix", "Write to a different file with suffix, if specified.",
53 | cxxopts::value()->default_value(""))//
54 | ("h,help", "Show help") //
55 | ("files", "The file(s) to process", cxxopts::value>());
56 |
57 | options.parse_positional({"files"});
58 |
59 | std::vector args_utf8(argc);
60 | std::vector argv_utf8(argc);
61 | for (int i = 0; i < argc; i++) {
62 | args_utf8[i] = ConvertWideToMultibyte(CP_UTF8, argv[i]);
63 | argv_utf8[i] = args_utf8[i].data();
64 | }
65 | auto result = options.parse(argc, argv_utf8.data());
66 | if (result.count("help")) {
67 | fputs(options.help().c_str(), stderr);
68 | return 0;
69 | }
70 | bool useGBK = checkCallingFromE();
71 | fprintf(stderr, "ELang Patcher v%s by FlyingRainyCats (%s @52pojie.cn)\n", ELANG_PATCHER_VERSION, useGBK ? "\xB0\xAE\xB7\xC9\xB5\xC4\xC3\xA8" : "爱飞的猫");
72 | const auto file_count = result.count("files");
73 | if (file_count == 0) {
74 | fprintf(stderr, "ERROR: no input files specified\n");
75 | return 999;
76 | }
77 | auto files = result["files"].as>();
78 | auto output_suffix_str = result["suffix"].as();
79 | std::u8string output_suffix{output_suffix_str.cbegin(), output_suffix_str.cend()};
80 |
81 | auto error_count{0};
82 | auto fake_stub = result["fake-stub"].as();
83 | auto backup = result["backup"].as();
84 | for (auto &file_path: files) {
85 | std::u8string temp_path(file_path.cbegin(), file_path.cend());
86 | fs::path exe_path{temp_path};
87 |
88 | std::string exe_path_str{};
89 | if (useGBK) {
90 | exe_path_str = ConvertWideToMultibyte(936, exe_path.wstring());
91 | } else {
92 | exe_path_str = exe_path.string();
93 | }
94 | fprintf(stderr, "INFO: processing: %s\n", exe_path_str.c_str());
95 |
96 | if (!ELangPatchFile(exe_path, output_suffix, backup, fake_stub)) {
97 | error_count++;
98 | }
99 | }
100 | return error_count;
101 | }
102 |
103 | int main() {
104 | setlocale(LC_ALL, ".UTF8");
105 |
106 | int argc{0};
107 | auto argv = CommandLineToArgvW(GetCommandLineW(), &argc);
108 | return main_unicode(argc, argv);
109 | }
110 |
--------------------------------------------------------------------------------
/src/test.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "../LibELangPatch/include/CallProxyStubWithEcxGen.h"
6 | #include "../LibELangPatch/include/ELangInitFnGen.h"
7 | #include "../LibELangPatch/include/WndHandlerGen.h"
8 | #include "CdeclPushAndCallGen.h"
9 | #include "ELangBulkPushGen.h"
10 |
11 | void print_shellcode(const std::vector &code) {
12 | for (auto bytecode: code) {
13 | printf("%02x ", bytecode);
14 | }
15 | printf("\n");
16 | }
17 |
18 | int main() {
19 | auto wnd_handler_code = GenerateWndHandlerCode(0x0041883A, 0xFFFF9AB1);
20 | printf("WndHandler: %zu bytes\n", wnd_handler_code.size());
21 | print_shellcode(wnd_handler_code);
22 |
23 | uint32_t data[3]{0, 1, 2};
24 | auto elang_init_code = GenerateELangInitSnippet(0x004365A5, 0x47E6A0, data);
25 | printf("ELangInitSnippet: %zu bytes\n", elang_init_code.size());
26 | print_shellcode(elang_init_code);
27 |
28 | auto elang_wnd_proc_stub = GenerateCallProxyStubWithEcx(15, 3, 0x4AF608, 0xFFFFC787);
29 | printf("ELangWndProcStub: %zu bytes\n", elang_wnd_proc_stub.size());
30 | print_shellcode(elang_wnd_proc_stub);
31 |
32 | auto elang_bulkPushInstruction = GenerateBulkPushInstruction(0x00401036 - 0x00401007, {0x80000002, 0, 1, 0x10001, 0x6010000, 0x52010001, 0x10001, 0x601000E, 0x5201000F, 3});
33 | printf("elang_bulkPushInstruction: %zu bytes\n", elang_bulkPushInstruction.size());
34 | print_shellcode(elang_bulkPushInstruction);
35 |
36 | // uint32_t push_value, uint32_t call_delta, uint32_t ret_delta
37 | auto elang_GenerateCdeclPushAndCall = GenerateCdeclPushAndCall(0x52010048, 0x11 + 5, 5);
38 | printf("elang_GenerateCdeclPushAndCall: %zu bytes\n", elang_GenerateCdeclPushAndCall.size());
39 | print_shellcode(elang_GenerateCdeclPushAndCall);
40 | return 0;
41 | }
42 |
--------------------------------------------------------------------------------