From 28398805141435462ed6be0d7e362392c83d26aa Mon Sep 17 00:00:00 2001 From: gmh5225 <2315157@qq.com> Date: Fri, 27 May 2022 19:43:08 +0800 Subject: [PATCH] Upload --- .clang-format | 1 + .gitmodules | 3 + CMakeLists.txt | 52 +++++++ LICENSE | 20 ++- ObfuscateField.cpp | 13 ++ ObfuscateField.h | 220 +++++++++++++++++++++++++++++ README.md | 68 ++++++++- build-release.bat | 8 ++ images/UnknownField-gui.png | Bin 0 -> 29423 bytes images/UnknownField_after.png | Bin 0 -> 3347 bytes images/UnknownField_before.png | Bin 0 -> 13748 bytes sdk/UnknownFieldSDK.h | 6 + test/test.cpp | 49 +++++++ test/test_UTF16_LE.cpp | Bin 0 -> 1380 bytes test/test_p.cpp | 20 +++ test/test_p/test_p.sln | 31 ++++ test/test_p/test_p.vcxproj | 147 +++++++++++++++++++ test/test_p/test_p.vcxproj.filters | 22 +++ tool/CMakeLists.txt | 12 ++ tool/cli/CMakeLists.txt | 13 ++ tool/cli/UnknownField-cli.cpp | 22 +++ tool/gui/.gitignore | 73 ++++++++++ tool/gui/NewWorld.ico | Bin 0 -> 4286 bytes tool/gui/gui.pro | 43 ++++++ tool/gui/main.cpp | 26 ++++ tool/gui/mainwindow.cpp | 126 +++++++++++++++++ tool/gui/mainwindow.h | 45 ++++++ tool/gui/mainwindow.ui | 83 +++++++++++ 28 files changed, 1098 insertions(+), 5 deletions(-) create mode 100644 .clang-format create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 ObfuscateField.cpp create mode 100644 ObfuscateField.h create mode 100644 build-release.bat create mode 100644 images/UnknownField-gui.png create mode 100644 images/UnknownField_after.png create mode 100644 images/UnknownField_before.png create mode 100644 sdk/UnknownFieldSDK.h create mode 100644 test/test.cpp create mode 100644 test/test_UTF16_LE.cpp create mode 100644 test/test_p.cpp create mode 100644 test/test_p/test_p.sln create mode 100644 test/test_p/test_p.vcxproj create mode 100644 test/test_p/test_p.vcxproj.filters create mode 100644 tool/CMakeLists.txt create mode 100644 tool/cli/CMakeLists.txt create mode 100644 tool/cli/UnknownField-cli.cpp create mode 100644 tool/gui/.gitignore create mode 100644 tool/gui/NewWorld.ico create mode 100644 tool/gui/gui.pro create mode 100644 tool/gui/main.cpp create mode 100644 tool/gui/mainwindow.cpp create mode 100644 tool/gui/mainwindow.h create mode 100644 tool/gui/mainwindow.ui diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..9b3aa8b --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: LLVM diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..70cba51 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tool/gui/QDarkStyleSheet"] + path = tool/gui/QDarkStyleSheet + url = https://github.com/gmh5225/QDarkStyleSheet diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8c9895b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,52 @@ +project(UnknownField-project) + +find_package(LLVM REQUIRED CONFIG) +include_directories(${LLVM_INCLUDE_DIRS}) +link_directories(${LLVM_LIBRARY_DIRS}) +add_definitions(${LLVM_DEFINITIONS}) + +find_package(Clang REQUIRED CONFIG) +include_directories(${CLANG_INCLUDE_DIRS}) +link_directories(${CLANG_LIBRARY_DIRS}) +add_definitions(${CLANG_DEFINITIONS}) + +list(APPEND CMAKE_MODULE_PATH ${LLVM_CMAKE_DIR}) +include(AddLLVM) +list(APPEND CMAKE_MODULE_PATH ${CLANG_CMAKE_DIR}) +include(AddClang) + +if(DEBUG_MODE) +add_definitions(-DDEBUG_MODE) +endif() + +set(CMAKE_CXX_FLAGS_RELEASE "/MT") + +add_library(UnknownField-lib STATIC +sdk/UnknownFieldSDK.h +ObfuscateField.h +ObfuscateField.cpp +) + +target_link_libraries(UnknownField-lib + PRIVATE + clangAST + clangASTMatchers + clangBasic + clangDynamicASTMatchers + clangFrontend + clangTooling + clangSerialization + LLVMLineEditor + LLVMSupport + LLVMFrontendOpenMP +) + +add_subdirectory(tool) + + + + + + + + diff --git a/LICENSE b/LICENSE index 2071b23..8acf865 100644 --- a/LICENSE +++ b/LICENSE @@ -1,9 +1,21 @@ MIT License -Copyright (c) +Copyright (c) 2022 gmh -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ObfuscateField.cpp b/ObfuscateField.cpp new file mode 100644 index 0000000..d48f0f4 --- /dev/null +++ b/ObfuscateField.cpp @@ -0,0 +1,13 @@ +#include "ObfuscateField.h" + +std::map> + GlobalFieldNodeVectorMap; +std::map> + GlobalClassFieldDeclStringVectorMap; +std::map GlobalSDKUnknownFieldProtectionEnabledMap; + +llvm::cl::OptionCategory + UnknownFieldOptionCategory("UnknownField OptionCategory"); + +cl::opt GlobalObfucated{"g", cl::desc("Enable global obfucation"), cl::init(false), + cl::cat(UnknownFieldOptionCategory)}; diff --git a/ObfuscateField.h b/ObfuscateField.h new file mode 100644 index 0000000..86fcc5a --- /dev/null +++ b/ObfuscateField.h @@ -0,0 +1,220 @@ +#ifndef _OBFUSCATE_FIELD_H +#define _OBFUSCATE_FIELD_H + +#include +#include +#include +#include +#include +#include + +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Lex/MacroArgs.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Refactoring.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace clang; +using namespace clang::ast_matchers; +using namespace clang::driver; +using namespace clang::tooling; + +#define UnknownFieldProtectionTagName "UnknownFieldProtection" + +extern std::map> + GlobalFieldNodeVectorMap; +extern std::map> + GlobalClassFieldDeclStringVectorMap; +extern std::map GlobalSDKUnknownFieldProtectionEnabledMap; + +extern llvm::cl::OptionCategory UnknownFieldOptionCategory; +extern cl::opt GlobalObfucated; + +class ObfuscateFieldDeclHandler : public MatchFinder::MatchCallback { +public: + ObfuscateFieldDeclHandler(Rewriter &Rewrite) : Rewrite(Rewrite) {} + + virtual void run(const MatchFinder::MatchResult &Result) { + if (auto FDNode = + Result.Nodes.getNodeAs("ObfuscateField")) { + auto FieldStr = Rewrite.getRewrittenText(FDNode->getSourceRange()); + if (FieldStr.empty()) { + // Empty strings are not required + return; + } + + auto QualifiedNameStr = FDNode->getQualifiedNameAsString(); + auto ClassNameStr = QualifiedNameStr.substr( + 0, FDNode->getQualifiedNameAsString().length() - + FDNode->getNameAsString().length() - 2); + + if (FDNode->hasInClassInitializer()) { + // Filter field that in class initializer + GlobalSDKUnknownFieldProtectionEnabledMap[ClassNameStr] = false; + outs() << "Error: Found field(" << FieldStr << ")" + << " that in class(" << ClassNameStr << ")" + << " initializer we don't support!\n "; + exit(-1); + return; + } + + // Save sth that used later. + GlobalFieldNodeVectorMap[ClassNameStr].push_back(FDNode); + GlobalClassFieldDeclStringVectorMap[ClassNameStr].push_back(FieldStr); + } + } + +private: + Rewriter &Rewrite; +}; + +class ObfuscateFieldASTConsumer : public ASTConsumer { +public: + ObfuscateFieldASTConsumer(Rewriter &R) : HandlerForObfuscateFieldDecl(R) { + // Only match our main file.So we should use 'isExpansionInMainFile()' + Matcher.addMatcher( + traverse(TK_IgnoreUnlessSpelledInSource, + fieldDecl(isExpansionInMainFile()).bind("ObfuscateField")), + &HandlerForObfuscateFieldDecl); + } + + void HandleTranslationUnit(ASTContext &Context) override { + Matcher.matchAST(Context); + } + +private: + ObfuscateFieldDeclHandler HandlerForObfuscateFieldDecl; + MatchFinder Matcher; +}; + +class PreprocessorPPCallback : public PPCallbacks { +public: + /// Called by Preprocessor::HandleMacroExpandedIdentifier when a + /// macro invocation is found. + virtual void MacroExpands(const Token &MacroNameTok, + const MacroDefinition &MD, SourceRange Range, + const MacroArgs *Args) { + + if (GlobalObfucated) { + // This is a global tag + return; + } + + auto IdentifierInfo = MacroNameTok.getIdentifierInfo(); + if (!IdentifierInfo) { + return; + } + + if (!Args) { + return; + } + + // We only care about our markers + if (IdentifierInfo->getName().compare(UnknownFieldProtectionTagName) != 0) { + return; + } + + auto UnexpArgument0 = Args->getUnexpArgument(0); + if (!UnexpArgument0) { + return; + } + + auto Arg0IdentifierInfo = UnexpArgument0->getIdentifierInfo(); + if (!Arg0IdentifierInfo) { + return; + } + + StringRef ClassName = Arg0IdentifierInfo->getName(); + GlobalSDKUnknownFieldProtectionEnabledMap[ClassName.str()] = true; + outs() << "Scanning " << UnknownFieldProtectionTagName << " in class(" + << ClassName << ").\n"; + } +}; + +class ObfuscateFieldFrontendAction : public ASTFrontendAction { +private: + auto GenerateRandomKey() { + std::random_device RD; + // Generate seed + std::mt19937 G(RD()); + return G; + } + + bool EnableObfuscated(std::string ClassName) { + if (GlobalObfucated) { + // This is a global tag + return true; + } + + // Find class name that need to be protected. + if (GlobalSDKUnknownFieldProtectionEnabledMap.find(ClassName) != + GlobalSDKUnknownFieldProtectionEnabledMap.end()) { + if (GlobalSDKUnknownFieldProtectionEnabledMap[ClassName]) { + return true; + } + } + + return false; + } + +public: + ObfuscateFieldFrontendAction() {} + void EndSourceFileAction() override { + + // For Field + { + // Traverse map + for (auto &Map : GlobalClassFieldDeclStringVectorMap) { + if (!EnableObfuscated(Map.first)) { + continue; + } + + // Shuffle it + std::shuffle(Map.second.begin(), Map.second.end(), GenerateRandomKey()); + + // Replace it + size_t Count = Map.second.size(); + for (size_t i = 0; i < Count; ++i) { + if (Map.second[i].empty()) { + // Empty strings are not required + continue; + } + auto FDNode = GlobalFieldNodeVectorMap[Map.first][i]; + TheRewriter.ReplaceText(FDNode->getSourceRange(), + Map.second[i].c_str()); + } + } + } + +#ifdef DEBUG_MODE + TheRewriter.getEditBuffer(TheRewriter.getSourceMgr().getMainFileID()) + .write(llvm::outs()); +#else + TheRewriter.overwriteChangedFiles(); +#endif + } + + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef file) override { + // Add PPCallbacks + CI.getPreprocessor().addPPCallbacks( + std::make_unique()); + + // New ASTConsumer + TheRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts()); + return std::make_unique(TheRewriter); + } + +private: + Rewriter TheRewriter; +}; + +#endif diff --git a/README.md b/README.md index 3aec0e5..b30f6a2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,69 @@ # UnknownField +UnknownField is a tool based clang that obfuscating the order of fields to protect your C/C++ game or code. -UnknownField is a tool based clang that obfuscating the order of fields to protect your C/C++ game or code. \ No newline at end of file +![image](images/UnknownField-gui.png) + +## Before +![image](images/UnknownField_before.png) + +## After +![image](images/UnknownField_after.png) + +## Usage +``` +UnknownField-cli.exe +Usage: UnknownField-cli.exe [options] [... ] + +Optional arguments: +-- -I - External include directory +-g - Enable global obfucation +``` + +## Example commands: +```bash +UnknownField-cli.exe test.cpp +UnknownField-cli.exe test.cpp -g +UnknownField-cli.exe test.cpp -- -IE:\External\Directory +UnknownField-cli.exe test.cpp -- -IE:\External\Directory1 -IE:\External\Directory2 +``` + +## Example SDK: +```C++ +#include "sdk/UnknownFieldSDK.h" +#include +class UnknownFieldProtection(MyClassX) { +public: + MyClassX(); + ~MyClassX(); +private: + UCHAR name[300]; + DWORD mp; + DWORD maxmp; + DWORD hp; + DWORD maxhp; + unsigned char level; +}; +``` + +## Usage Dependency +- Visual Studio with SDK10 (without this you could not include windows.h in your file) + +## Build Dependency +- [llvm-msvc](https://github.com/NewWorldComingSoon/llvm-msvc-build/releases) + +## Build +``` +git clone --recurse-submodules https://github.com/NewWorldComingSoon/UnknownField.git +cd UnknownField +mkdir build +cd build +set LLVM-MSVC-BIN=E:\llvm\llvm-msvc-bin -> You need to replace your path. +cmake ../ -DLLVM_DIR=%LLVM-MSVC-BIN%\lib\cmake\llvm -DClang_DIR=%LLVM-MSVC-BIN%\lib\cmake\clang +cmake --build . --config Release -- -m +``` + +## TODO +- Obfuscating the order of virtual functions. + +## Note +This project is currently still a demo. diff --git a/build-release.bat b/build-release.bat new file mode 100644 index 0000000..0d6e970 --- /dev/null +++ b/build-release.bat @@ -0,0 +1,8 @@ +cd ./ +mkdir build-release +cd build-release +cmake ../ -DLLVM_DIR=E:\llvm\llvm-msvc-bin\lib\cmake\llvm -DClang_DIR=E:\llvm\llvm-msvc-bin\lib\cmake\clang +cd ../ + + + diff --git a/images/UnknownField-gui.png b/images/UnknownField-gui.png new file mode 100644 index 0000000000000000000000000000000000000000..d08d0c1c2c5a4ce14b53b6222fcd36a292174302 GIT binary patch literal 29423 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 NqMVv+zO;$&{{a=Ya((~+ literal 0 HcmV?d00001 diff --git a/images/UnknownField_after.png b/images/UnknownField_after.png new file mode 100644 index 0000000000000000000000000000000000000000..a73fcd162f36df71ac9190eef0d917d75393955c GIT binary patch literal 3347 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 literal 0 HcmV?d00001 diff --git a/images/UnknownField_before.png b/images/UnknownField_before.png new file mode 100644 index 0000000000000000000000000000000000000000..f614cea4ca968f5e5310ad87d36860ec64b2c2b3 GIT binary patch literal 13748 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

2^1+u;cra0xeH%O8ugTR?xtwG5 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 + +class UnknownFieldProtection(MyClassX) { +public: + MyClassX(); + ~MyClassX(); +private: + UCHAR name[300]; + DWORD mp; + DWORD maxmp; + DWORD hp; + DWORD maxhp; + unsigned char level; +}; + +class MyClassX2 { +public: + MyClassX2(); + ~MyClassX2(); + +private: + char name2[300]; + int mp2; + int maxmp2; + int hp2; + int maxhp2; + unsigned char level2; +}; + +class MyClassX3 { +public: + MyClassX3(); + ~MyClassX3(); + +private: + char name3[300]; + int mp3; + int maxmp3; + int hp3; + int maxhp3; + unsigned char level3; +}; + +int myfunct(int a, int b) { + int c; + c = a + b; + return c; +} diff --git a/test/test_UTF16_LE.cpp b/test/test_UTF16_LE.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fbb346a6146f83359685a19dbce787047360c35b GIT binary patch literal 1380 zcmb7^+e*Vg6h-%|g8xwTrJ|@LZ{9#GB8VtTQ4k-}wA#>%B}r?G=%=e|V$)1xdLv9G zIs4qQraeF3TME>bueSQCtD%xMwa2xui1!wI6}`oJ(>wPI{2O@Jb+3x9@N5#Xp>_Sw zbgYLYdp(Jb65rFr*Z7;79#tZ0&Z=>%kl2`tKKFpIWLor|BWfSf&1@&U8oiE@}t$Nv;mKo~_&!2;AUR!LOs< zOvTf1j^11lIj#5UD5C3E_6Fxvf<==aL!R1L2CVkDlh5&an9^1<091%EV6gtHiX`^x*Xv&-?EI|6&bq=8QUJKF?JU+ zcIPrKQVqo-d&bySmt&kp*o?CiAL)g8V5VCPtJLmk7t-ll&6*x-6RU51otYp$>xAX8 Yc38EX9`c`xsTESqc>hh8&t%5@0u}Jb5&!@I literal 0 HcmV?d00001 diff --git a/test/test_p.cpp b/test/test_p.cpp new file mode 100644 index 0000000..d7caa46 --- /dev/null +++ b/test/test_p.cpp @@ -0,0 +1,20 @@ +#include +#include "../sdk/UnknownFieldSDK.h" + +static unsigned int gv = 0; +class C1 { +public: + C1() { gv = 1; } +}; + +class UnknownFieldProtection(C2) { +public: + C1 c; + unsigned int gv_ = gv; +}; + +int main() { + C2 c2; + printf("c2.gv=%d\n", c2.gv_); + return 0; +} diff --git a/test/test_p/test_p.sln b/test/test_p/test_p.sln new file mode 100644 index 0000000..ed036df --- /dev/null +++ b/test/test_p/test_p.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32228.430 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_p", "test_p.vcxproj", "{EEA027DD-C602-4EFB-9B3A-7FE8D8F2B39C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EEA027DD-C602-4EFB-9B3A-7FE8D8F2B39C}.Debug|x64.ActiveCfg = Debug|x64 + {EEA027DD-C602-4EFB-9B3A-7FE8D8F2B39C}.Debug|x64.Build.0 = Debug|x64 + {EEA027DD-C602-4EFB-9B3A-7FE8D8F2B39C}.Debug|x86.ActiveCfg = Debug|Win32 + {EEA027DD-C602-4EFB-9B3A-7FE8D8F2B39C}.Debug|x86.Build.0 = Debug|Win32 + {EEA027DD-C602-4EFB-9B3A-7FE8D8F2B39C}.Release|x64.ActiveCfg = Release|x64 + {EEA027DD-C602-4EFB-9B3A-7FE8D8F2B39C}.Release|x64.Build.0 = Release|x64 + {EEA027DD-C602-4EFB-9B3A-7FE8D8F2B39C}.Release|x86.ActiveCfg = Release|Win32 + {EEA027DD-C602-4EFB-9B3A-7FE8D8F2B39C}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FEA3B910-2314-4E79-87E6-7E78B8D4C2A6} + EndGlobalSection +EndGlobal diff --git a/test/test_p/test_p.vcxproj b/test/test_p/test_p.vcxproj new file mode 100644 index 0000000..986893c --- /dev/null +++ b/test/test_p/test_p.vcxproj @@ -0,0 +1,147 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {eea027dd-c602-4efb-9b3a-7fe8d8f2b39c} + testp + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/test/test_p/test_p.vcxproj.filters b/test/test_p/test_p.vcxproj.filters new file mode 100644 index 0000000..374e35b --- /dev/null +++ b/test/test_p/test_p.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/tool/CMakeLists.txt b/tool/CMakeLists.txt new file mode 100644 index 0000000..c7c34dc --- /dev/null +++ b/tool/CMakeLists.txt @@ -0,0 +1,12 @@ + +add_subdirectory(cli) +#add_subdirectory(gui) + + + + + + + + + diff --git a/tool/cli/CMakeLists.txt b/tool/cli/CMakeLists.txt new file mode 100644 index 0000000..ea1500c --- /dev/null +++ b/tool/cli/CMakeLists.txt @@ -0,0 +1,13 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../) + +set(CMAKE_CXX_FLAGS_RELEASE "/MT") + +add_executable(UnknownField-cli + UnknownField-cli.cpp + ) +target_link_libraries(UnknownField-cli + PRIVATE + UnknownField-lib + ) diff --git a/tool/cli/UnknownField-cli.cpp b/tool/cli/UnknownField-cli.cpp new file mode 100644 index 0000000..017735e --- /dev/null +++ b/tool/cli/UnknownField-cli.cpp @@ -0,0 +1,22 @@ + +#include "ObfuscateField.h" + +int main(int argc, const char **argv) { + Expected eOptParser = + clang::tooling::CommonOptionsParser::create( + argc, argv, UnknownFieldOptionCategory, llvm::cl::OneOrMore, nullptr, + false); + if (auto E = eOptParser.takeError()) { + errs() << "Problem constructing CommonOptionsParser " + << toString(std::move(E)) << '\n'; + return EXIT_FAILURE; + } + + clang::tooling::ClangTool Tool(eOptParser->getCompilations(), + eOptParser->getSourcePathList()); + int Ret = Tool.run( + clang::tooling::newFrontendActionFactory() + .get()); + outs() << "UnknownField finished!\n"; + return Ret; +} diff --git a/tool/gui/.gitignore b/tool/gui/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/tool/gui/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/tool/gui/NewWorld.ico b/tool/gui/NewWorld.ico new file mode 100644 index 0000000000000000000000000000000000000000..d22e4d195365e9f27716d6909c6e58f3417d0c89 GIT binary patch literal 4286 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 +#include +#include + +int main(int argc, char *argv[]) { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif + + QApplication a(argc, argv); + MainWindow w; + + QFile f(":/qdarkstyle/dark/style.qss"); + if (!f.exists()) { + printf("Unable to set stylesheet, file not found\n"); + } else { + f.open(QFile::ReadOnly | QFile::Text); + QTextStream ts(&f); + qApp->setStyleSheet(ts.readAll()); + } + + w.show(); + return a.exec(); +} diff --git a/tool/gui/mainwindow.cpp b/tool/gui/mainwindow.cpp new file mode 100644 index 0000000..b107fbb --- /dev/null +++ b/tool/gui/mainwindow.cpp @@ -0,0 +1,126 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include +#include +#include +#include +#include + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), ui(new Ui::MainWindow), mQStringFilename("") { + ui->setupUi(this); + + setDefaultDisposition(); + setDefaultComponent(); + + // signal-slot + connect(mQPushButtonRun, SIGNAL(clicked()), this, SLOT(qPushButtonRunSlot())); +} + +MainWindow::~MainWindow() { delete ui; } + +void MainWindow::setDefaultComponent() { + + mQLabelExternalInclude = new QLabel("External include directory"); + ui->verticalLayout_frame_top->addWidget(mQLabelExternalInclude); + mQTextEditExternalInclude = new QTextEdit(); + ui->verticalLayout_frame_top->addWidget(mQTextEditExternalInclude); + + mQLabelSourceFile = new QLabel("Source file"); + ui->verticalLayout_frame_middle->addWidget(mQLabelSourceFile); + + mQTextEditSourceFile = new QTextEdit(); + QFont qFont("Courier", 8); + qFont.setStyleHint(QFont::Monospace); + qFont.setBold(true); + qFont.setFixedPitch(true); + mQTextEditSourceFile->setFont(qFont); + ui->verticalLayout_frame_middle->addWidget(mQTextEditSourceFile); + + mQPushButtonRun = new QPushButton("Run"); + ui->verticalLayout_frame_middle->addWidget(mQPushButtonRun); + + mQLabelLog = new QLabel("Log"); + ui->verticalLayout_frame_bottom->addWidget(mQLabelLog); + mQTextEditLog = new QTextEdit(); + mQTextEditLog->setReadOnly(true); + mQTextEditLog->setUndoRedoEnabled(false); + ui->verticalLayout_frame_bottom->addWidget(mQTextEditLog); +} + +void MainWindow::setDefaultDisposition() { + int iHeight0 = ui->splitter->widget(0)->size().height(); + int iHeight1 = ui->splitter->widget(1)->size().height(); + int iHeight2 = ui->splitter->widget(2)->size().height(); + int iTotalHeight = iHeight0 + iHeight1 + iHeight2; + + QList qListSizes; + // 20% + qListSizes.append(iTotalHeight * 20 / 100); + // 70% + qListSizes.append(iTotalHeight * 70 / 100); + // 10% + qListSizes.append(iTotalHeight * 10 / 100); + ui->splitter->setSizes(qListSizes); +} + +void MainWindow::readSourceFileIntoTextEdit(QString filename) { + // clean content first + mQTextEditSourceFile->setText(QString("")); + + QFile qFile(filename); + if (!qFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + QMessageBox::information(nullptr, QString("Error"), + QString("Open file failed!")); + return; + } + + QTextStream qTextStream(&qFile); + while (!qTextStream.atEnd()) { + QString qsLine = qTextStream.readLine(); + mQTextEditSourceFile->append(qsLine); + } +} + +void MainWindow::qPushButtonRunSlot() { + if (mQStringFilename.length() == 0) { + return; + } + + QProcess qProcess; + QString qsCmd = "UnknownField-cli.exe "; + qsCmd += mQStringFilename; + QString qsExternalInclude = mQTextEditExternalInclude->toPlainText(); + if (qsExternalInclude.length() != 0) { + QStringList qsList = + qsExternalInclude.split(QRegExp("[\n|;]"), QString::SkipEmptyParts); + qsCmd += " --"; + for (int i = 0; i < qsList.size(); ++i) { + QString qsInc = qsList[i].trimmed(); + if (qsInc.length() > 0) { + qsCmd += " -I"; + qsCmd += "\""; + qsCmd += qsInc; + qsCmd += "\""; + } + } + } + qProcess.start(qsCmd); + qProcess.waitForFinished(); + mQTextEditLog->append(qProcess.readAllStandardError()); + mQTextEditLog->append(qProcess.readAllStandardOutput()); + readSourceFileIntoTextEdit(mQStringFilename); +} + +void MainWindow::on_actionOpen_triggered() { + QString qsFilename = QFileDialog::getOpenFileName( + this, "Open file", 0, "Source file (*.h *.hpp *.c *.cpp *.cc)"); + if (qsFilename.length() == 0) { + return; + } + qsFilename = QDir::toNativeSeparators(qsFilename); + // Set mQStringFilename + mQStringFilename = qsFilename; + + readSourceFileIntoTextEdit(qsFilename); +} diff --git a/tool/gui/mainwindow.h b/tool/gui/mainwindow.h new file mode 100644 index 0000000..e59f0d5 --- /dev/null +++ b/tool/gui/mainwindow.h @@ -0,0 +1,45 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { +class MainWindow; +} +QT_END_NAMESPACE + +class MainWindow : public QMainWindow { + Q_OBJECT +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +public: + void setDefaultDisposition(); + void setDefaultComponent(); + void readSourceFileIntoTextEdit(QString filename); +private slots: + void on_actionOpen_triggered(); + void qPushButtonRunSlot(); + +private: + Ui::MainWindow *ui; + + QString mQStringFilename; + + QLabel *mQLabelExternalInclude; + QTextEdit *mQTextEditExternalInclude; + + QLabel *mQLabelSourceFile; + QTextEdit *mQTextEditSourceFile; + QPushButton *mQPushButtonRun; + + QLabel *mQLabelLog; + QTextEdit *mQTextEditLog; +}; +#endif // MAINWINDOW_H diff --git a/tool/gui/mainwindow.ui b/tool/gui/mainwindow.ui new file mode 100644 index 0000000..175dd57 --- /dev/null +++ b/tool/gui/mainwindow.ui @@ -0,0 +1,83 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + UnknownField + + + + + + + Qt::Vertical + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + + + 0 + 0 + 800 + 21 + + + + + File + + + + + + + + + Open + + + F3 + + + + + +