├── .gitignore ├── .gitmodules ├── Detours ├── Detours.vcxproj └── Detours.vcxproj.filters ├── Dummy ├── Dummy.cpp ├── Dummy.vcxproj └── Dummy.vcxproj.filters ├── GenerateRelease.cmd ├── LICENSE.txt ├── PEDoll.sln ├── PEDollController ├── BlobFormatters │ ├── FmtHex.cs │ ├── FmtRaw.cs │ ├── FmtText.cs │ ├── FmtX86.cs │ ├── IBlobFormatter.cs │ └── Util.cs ├── Commands │ ├── CmdBreak.cs │ ├── CmdDump.cs │ ├── CmdEnd.cs │ ├── CmdEval.cs │ ├── CmdExit.cs │ ├── CmdHelp.cs │ ├── CmdHook.cs │ ├── CmdKill.cs │ ├── CmdListen.cs │ ├── CmdLoad.cs │ ├── CmdLoaddll.cs │ ├── CmdPs.cs │ ├── CmdRem.cs │ ├── CmdShell.cs │ ├── CmdTarget.cs │ ├── CmdUnhook.cs │ ├── CmdVerdict.cs │ ├── CommandLine.cs │ ├── ICommand.cs │ ├── Util.cs │ └── cmdDoll.cs ├── FDlgAbout.Designer.cs ├── FDlgAbout.cs ├── FDlgAbout.resx ├── FDlgAbout.zh-CN.resx ├── FDlgAddHook.Designer.cs ├── FDlgAddHook.cs ├── FDlgAddHook.resx ├── FDlgAddHook.zh-CN.resx ├── FDlgBrowsePID.Designer.cs ├── FDlgBrowsePID.cs ├── FDlgBrowsePID.resx ├── FDlgBrowsePID.zh-CN.resx ├── FMain.Designer.cs ├── FMain.cs ├── FMain.resx ├── FMain.zh-CN.resx ├── FSplash.Designer.cs ├── FSplash.cs ├── FSplash.resx ├── Logger.cs ├── PEDollController.csproj ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Resources.zh-CN.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Puppet │ ├── Puppet.cs │ └── Util.cs ├── Resources │ ├── CREDITS.txt │ ├── Controller.ico │ ├── Controller.png │ ├── Controller.svg │ └── Splash.png ├── Threads │ ├── AsyncDataProvider.cs │ ├── BlockingQueue.cs │ ├── Client.cs │ ├── CmdEngine.cs │ ├── EvalEngine.cs │ ├── EvalEngineContext.cs │ ├── Gui.cs │ └── Listener.cs ├── app.config └── packages.config ├── PEDollMonitor ├── Doll.cpp ├── Doll.h ├── GetFileVersion.cpp ├── GetFileVersion.h ├── PEDollMonitor.cpp ├── PEDollMonitor.h ├── PEDollMonitor.rc ├── PEDollMonitor.vcxproj ├── PEDollMonitor.vcxproj.filters ├── Proc.cpp ├── Proc.h ├── SetPrivilege.cpp ├── SetPrivilege.h ├── ThreadPuppet.cpp ├── framework.h ├── pch.cpp ├── pch.h └── resource.h ├── README.md ├── README.zh-CN.md ├── Scripts ├── API │ ├── CreateFile.txt │ ├── CreateProcess.txt │ ├── DeleteFile.txt │ ├── ExitWindowsEx.txt │ ├── LoadLibraryEx.txt │ ├── MessageBox.txt │ ├── OpenProcess.txt │ ├── RegCreateKeyEx.txt │ ├── RegOpenKeyEx.txt │ ├── RegSetKeyValue.txt │ ├── SetFileAttributes.txt │ ├── WriteFile.txt │ ├── WriteProcessMemory.txt │ ├── recv.txt │ ├── recvfrom.txt │ ├── send.txt │ └── sendto.txt ├── Break_on_MessageBox.txt ├── Break_on_MessageBox_64.txt ├── HTTP.txt ├── MBR_Ransom.txt ├── MBR_Ransom_64.txt ├── Registry.txt ├── Remote_Trojan.txt ├── TCP.txt └── UDP.txt ├── libDoll ├── BoyerMoore.cpp ├── BoyerMoore.h ├── Hook.cpp ├── Hook.h ├── HookStub.cpp ├── HookStub.h ├── HookStub_x64.asm ├── HookStub_x86.asm ├── Thread.cpp ├── Thread.h ├── ThreadPuppet.cpp ├── dllmain.cpp ├── framework.h ├── libDoll.h ├── libDoll.rc ├── libDoll.vcxproj ├── libDoll.vcxproj.filters ├── pch.cpp ├── pch.h └── resource.h └── libPuppet ├── PuppetClientTCP.cpp ├── PuppetClientTCP.h ├── framework.h ├── libPuppet.cpp ├── libPuppet.h ├── libPuppet.vcxproj ├── libPuppet.vcxproj.filters ├── pch.cpp └── pch.h /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Detours/repo"] 2 | path = Detours/repo 3 | url = https://github.com/microsoft/Detours 4 | -------------------------------------------------------------------------------- /Detours/Detours.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 头文件 20 | 21 | 22 | 头文件 23 | 24 | 25 | 26 | 27 | 源文件 28 | 29 | 30 | 源文件 31 | 32 | 33 | 源文件 34 | 35 | 36 | 源文件 37 | 38 | 39 | 源文件 40 | 41 | 42 | 源文件 43 | 44 | 45 | 源文件 46 | 47 | 48 | -------------------------------------------------------------------------------- /Dummy/Dummy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | // FIXME: Debug build of this dummy does not work 7 | #define DETOUR_PAYLOAD 8 | 9 | #ifdef DETOUR_PAYLOAD 10 | 11 | // Include a Detours payload section for easlier debugging 12 | #include "../Detours/repo/src/detours.h" 13 | 14 | # pragma pack(push, 1) 15 | 16 | struct PAYLOAD_SERVER_INFO 17 | { 18 | DETOUR_SECTION_HEADER header; 19 | DETOUR_SECTION_RECORD record; 20 | char data[32]; 21 | }; 22 | 23 | # pragma pack(pop) 24 | 25 | #pragma data_seg(".detour") 26 | 27 | static PAYLOAD_SERVER_INFO payload = { 28 | DETOUR_SECTION_HEADER_DECLARE(sizeof(PAYLOAD_SERVER_INFO)), 29 | { 30 | (sizeof(PAYLOAD_SERVER_INFO) - sizeof(DETOUR_SECTION_HEADER)), 31 | 0, 32 | { 0xa2062469, 0x2b45, 0x496d, { 0x8f, 0xe9, 0x7e, 0x89, 0x4e, 0xd7, 0x22, 0x70 } } 33 | }, 34 | "127.0.0.1" 35 | }; 36 | #pragma data_seg() 37 | 38 | extern "C" int DollDllHelloWorld(); 39 | 40 | #endif // DETOUR_PAYLOAD 41 | 42 | int main() 43 | { 44 | cout << "sizeof(UINT_PTR) = " << sizeof(UINT_PTR) << endl; 45 | cout << "&payload = " << &payload << endl; 46 | 47 | # ifdef DETOUR_PAYLOAD 48 | DollDllHelloWorld(); 49 | # endif // DETOUR_PAYLOAD 50 | 51 | getchar(); 52 | 53 | int ret = WinExec("ver", SW_SHOW); 54 | cout << "ret = " << ret << endl; 55 | 56 | return 0; 57 | } 58 | 59 | -------------------------------------------------------------------------------- /Dummy/Dummy.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 源文件 20 | 21 | 22 | -------------------------------------------------------------------------------- /GenerateRelease.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | 4 | set "RELEASEDIR=%tmp%\PEDollRelease" 5 | 6 | 7 | :main 8 | pushd %~dp0 9 | msbuild -ver >nul 2>nul 10 | if %ERRORLEVEL% neq 0 ( 11 | echo Run this batch file from a Developer Command Prompt! 12 | goto :eof 13 | ) 14 | 15 | if exist "%RELEASEDIR%" ( 16 | rd /s /q "%RELEASEDIR%" 17 | ) 18 | 19 | md %RELEASEDIR% 20 | 21 | ::call :buildAll Debug 22 | call :buildAll Release 23 | 24 | start explorer %RELEASEDIR% 25 | popd 26 | goto :eof 27 | 28 | 29 | :: call :buildAll Debug 30 | :buildAll 31 | call :buildController %1 ^ 32 | && call :buildMonitor x86 %1 ^ 33 | && call :buildMonitor x64 %1 34 | goto :eof 35 | 36 | 37 | :: call :buildController Debug 38 | :buildController 39 | set PLATFORMDIR=PEDollController\bin 40 | 41 | msbuild PEDoll.sln -t:PEDollController -p:Platform="Any CPU";Configuration=%1 42 | if %ERRORLEVEL% neq 0 ( 43 | goto :eof 44 | ) 45 | 46 | if not exist "%RELEASEDIR%\%1" ( 47 | md "%RELEASEDIR%\%1" 48 | ) 49 | 50 | xcopy /e %PLATFORMDIR%\%1 "%RELEASEDIR%\%1\" 51 | xcopy /e /i Scripts "%RELEASEDIR%\%1\Scripts" 52 | 53 | :: Generate x64 API script (Scripts\API\*64.txt) from x86 scripts 54 | pushd "%RELEASEDIR%\%1\Scripts\API" 55 | powershell -Command "ls *.txt | foreach { (cat $_.FullName -Raw) -Replace ' --stack=\d+','' > ($_.BaseName + '64.txt') }" 56 | popd 57 | goto :eof 58 | 59 | 60 | :: call :buildMonitor x64 Debug 61 | :buildMonitor 62 | 63 | if %1 equ x86 ( 64 | set PLATFORMDIR=. 65 | ) else ( 66 | set PLATFORMDIR=x64 67 | ) 68 | 69 | msbuild PEDoll.sln -t:PEDollMonitor,libDoll -p:Platform=%1;Configuration=%2 70 | if %ERRORLEVEL% neq 0 ( 71 | goto :eof 72 | ) 73 | 74 | if not exist "%RELEASEDIR%\%2" ( 75 | md "%RELEASEDIR%\%2" 76 | ) 77 | 78 | md "%RELEASEDIR%\%2\Monitor_%1" 79 | 80 | copy %PLATFORMDIR%\%2\PEDollMonitor.exe "%RELEASEDIR%\%2\Monitor_%1\" 81 | copy %PLATFORMDIR%\%2\PEDollMonitor.pdb "%RELEASEDIR%\%2\Monitor_%1\" 82 | copy %PLATFORMDIR%\%2\libDoll.dll "%RELEASEDIR%\%2\Monitor_%1\" 83 | copy %PLATFORMDIR%\%2\libDoll.pdb "%RELEASEDIR%\%2\Monitor_%1\" 84 | 85 | goto :eof -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright © 2020 Eric Zhang (EZForever), https://github.com/EZForever 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | 9 | -------------------------------------------------------------------------------- /PEDoll.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29613.14 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libDoll", "libDoll\libDoll.vcxproj", "{2EFB5550-99ED-473C-9132-CA2A04042D15}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libPuppet", "libPuppet\libPuppet.vcxproj", "{781B8BAD-43A7-42D1-B9B6-678943F3DFA4}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PEDollController", "PEDollController\PEDollController.csproj", "{EEEC10F7-451A-43AF-B230-DAE937660EAF}" 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dummy", "Dummy\Dummy.vcxproj", "{AF9DEB43-3AFE-4E77-8BEC-D4A0CC7074FB}" 13 | EndProject 14 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Detours", "Detours\Detours.vcxproj", "{C8D2C05E-499B-4E6E-9813-EDDEAFC02931}" 15 | EndProject 16 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PEDollMonitor", "PEDollMonitor\PEDollMonitor.vcxproj", "{E137C13A-D394-4500-AADE-0A1443CBB1F9}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Debug|x64 = Debug|x64 22 | Debug|x86 = Debug|x86 23 | Release|Any CPU = Release|Any CPU 24 | Release|x64 = Release|x64 25 | Release|x86 = Release|x86 26 | EndGlobalSection 27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 28 | {2EFB5550-99ED-473C-9132-CA2A04042D15}.Debug|Any CPU.ActiveCfg = Debug|Win32 29 | {2EFB5550-99ED-473C-9132-CA2A04042D15}.Debug|x64.ActiveCfg = Debug|x64 30 | {2EFB5550-99ED-473C-9132-CA2A04042D15}.Debug|x64.Build.0 = Debug|x64 31 | {2EFB5550-99ED-473C-9132-CA2A04042D15}.Debug|x86.ActiveCfg = Debug|Win32 32 | {2EFB5550-99ED-473C-9132-CA2A04042D15}.Debug|x86.Build.0 = Debug|Win32 33 | {2EFB5550-99ED-473C-9132-CA2A04042D15}.Release|Any CPU.ActiveCfg = Release|Win32 34 | {2EFB5550-99ED-473C-9132-CA2A04042D15}.Release|x64.ActiveCfg = Release|x64 35 | {2EFB5550-99ED-473C-9132-CA2A04042D15}.Release|x64.Build.0 = Release|x64 36 | {2EFB5550-99ED-473C-9132-CA2A04042D15}.Release|x86.ActiveCfg = Release|Win32 37 | {2EFB5550-99ED-473C-9132-CA2A04042D15}.Release|x86.Build.0 = Release|Win32 38 | {781B8BAD-43A7-42D1-B9B6-678943F3DFA4}.Debug|Any CPU.ActiveCfg = Debug|Win32 39 | {781B8BAD-43A7-42D1-B9B6-678943F3DFA4}.Debug|x64.ActiveCfg = Debug|x64 40 | {781B8BAD-43A7-42D1-B9B6-678943F3DFA4}.Debug|x64.Build.0 = Debug|x64 41 | {781B8BAD-43A7-42D1-B9B6-678943F3DFA4}.Debug|x86.ActiveCfg = Debug|Win32 42 | {781B8BAD-43A7-42D1-B9B6-678943F3DFA4}.Debug|x86.Build.0 = Debug|Win32 43 | {781B8BAD-43A7-42D1-B9B6-678943F3DFA4}.Release|Any CPU.ActiveCfg = Release|Win32 44 | {781B8BAD-43A7-42D1-B9B6-678943F3DFA4}.Release|x64.ActiveCfg = Release|x64 45 | {781B8BAD-43A7-42D1-B9B6-678943F3DFA4}.Release|x64.Build.0 = Release|x64 46 | {781B8BAD-43A7-42D1-B9B6-678943F3DFA4}.Release|x86.ActiveCfg = Release|Win32 47 | {781B8BAD-43A7-42D1-B9B6-678943F3DFA4}.Release|x86.Build.0 = Release|Win32 48 | {EEEC10F7-451A-43AF-B230-DAE937660EAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {EEEC10F7-451A-43AF-B230-DAE937660EAF}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {EEEC10F7-451A-43AF-B230-DAE937660EAF}.Debug|x64.ActiveCfg = Debug|Any CPU 51 | {EEEC10F7-451A-43AF-B230-DAE937660EAF}.Debug|x64.Build.0 = Debug|Any CPU 52 | {EEEC10F7-451A-43AF-B230-DAE937660EAF}.Debug|x86.ActiveCfg = Debug|Any CPU 53 | {EEEC10F7-451A-43AF-B230-DAE937660EAF}.Debug|x86.Build.0 = Debug|Any CPU 54 | {EEEC10F7-451A-43AF-B230-DAE937660EAF}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {EEEC10F7-451A-43AF-B230-DAE937660EAF}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {EEEC10F7-451A-43AF-B230-DAE937660EAF}.Release|x64.ActiveCfg = Release|Any CPU 57 | {EEEC10F7-451A-43AF-B230-DAE937660EAF}.Release|x64.Build.0 = Release|Any CPU 58 | {EEEC10F7-451A-43AF-B230-DAE937660EAF}.Release|x86.ActiveCfg = Release|Any CPU 59 | {EEEC10F7-451A-43AF-B230-DAE937660EAF}.Release|x86.Build.0 = Release|Any CPU 60 | {AF9DEB43-3AFE-4E77-8BEC-D4A0CC7074FB}.Debug|Any CPU.ActiveCfg = Debug|Win32 61 | {AF9DEB43-3AFE-4E77-8BEC-D4A0CC7074FB}.Debug|x64.ActiveCfg = Debug|x64 62 | {AF9DEB43-3AFE-4E77-8BEC-D4A0CC7074FB}.Debug|x64.Build.0 = Debug|x64 63 | {AF9DEB43-3AFE-4E77-8BEC-D4A0CC7074FB}.Debug|x86.ActiveCfg = Debug|Win32 64 | {AF9DEB43-3AFE-4E77-8BEC-D4A0CC7074FB}.Debug|x86.Build.0 = Debug|Win32 65 | {AF9DEB43-3AFE-4E77-8BEC-D4A0CC7074FB}.Release|Any CPU.ActiveCfg = Release|Win32 66 | {AF9DEB43-3AFE-4E77-8BEC-D4A0CC7074FB}.Release|x64.ActiveCfg = Release|x64 67 | {AF9DEB43-3AFE-4E77-8BEC-D4A0CC7074FB}.Release|x64.Build.0 = Release|x64 68 | {AF9DEB43-3AFE-4E77-8BEC-D4A0CC7074FB}.Release|x86.ActiveCfg = Release|Win32 69 | {AF9DEB43-3AFE-4E77-8BEC-D4A0CC7074FB}.Release|x86.Build.0 = Release|Win32 70 | {C8D2C05E-499B-4E6E-9813-EDDEAFC02931}.Debug|Any CPU.ActiveCfg = Debug|Win32 71 | {C8D2C05E-499B-4E6E-9813-EDDEAFC02931}.Debug|x64.ActiveCfg = Debug|x64 72 | {C8D2C05E-499B-4E6E-9813-EDDEAFC02931}.Debug|x64.Build.0 = Debug|x64 73 | {C8D2C05E-499B-4E6E-9813-EDDEAFC02931}.Debug|x86.ActiveCfg = Debug|Win32 74 | {C8D2C05E-499B-4E6E-9813-EDDEAFC02931}.Debug|x86.Build.0 = Debug|Win32 75 | {C8D2C05E-499B-4E6E-9813-EDDEAFC02931}.Release|Any CPU.ActiveCfg = Release|Win32 76 | {C8D2C05E-499B-4E6E-9813-EDDEAFC02931}.Release|x64.ActiveCfg = Release|x64 77 | {C8D2C05E-499B-4E6E-9813-EDDEAFC02931}.Release|x64.Build.0 = Release|x64 78 | {C8D2C05E-499B-4E6E-9813-EDDEAFC02931}.Release|x86.ActiveCfg = Release|Win32 79 | {C8D2C05E-499B-4E6E-9813-EDDEAFC02931}.Release|x86.Build.0 = Release|Win32 80 | {E137C13A-D394-4500-AADE-0A1443CBB1F9}.Debug|Any CPU.ActiveCfg = Debug|Win32 81 | {E137C13A-D394-4500-AADE-0A1443CBB1F9}.Debug|x64.ActiveCfg = Debug|x64 82 | {E137C13A-D394-4500-AADE-0A1443CBB1F9}.Debug|x64.Build.0 = Debug|x64 83 | {E137C13A-D394-4500-AADE-0A1443CBB1F9}.Debug|x86.ActiveCfg = Debug|Win32 84 | {E137C13A-D394-4500-AADE-0A1443CBB1F9}.Debug|x86.Build.0 = Debug|Win32 85 | {E137C13A-D394-4500-AADE-0A1443CBB1F9}.Release|Any CPU.ActiveCfg = Release|Win32 86 | {E137C13A-D394-4500-AADE-0A1443CBB1F9}.Release|x64.ActiveCfg = Release|x64 87 | {E137C13A-D394-4500-AADE-0A1443CBB1F9}.Release|x64.Build.0 = Release|x64 88 | {E137C13A-D394-4500-AADE-0A1443CBB1F9}.Release|x86.ActiveCfg = Release|Win32 89 | {E137C13A-D394-4500-AADE-0A1443CBB1F9}.Release|x86.Build.0 = Release|Win32 90 | EndGlobalSection 91 | GlobalSection(SolutionProperties) = preSolution 92 | HideSolutionNode = FALSE 93 | EndGlobalSection 94 | GlobalSection(ExtensibilityGlobals) = postSolution 95 | SolutionGuid = {BC9C3864-6F12-46BE-B9E1-27E9381BAE2F} 96 | EndGlobalSection 97 | EndGlobal 98 | -------------------------------------------------------------------------------- /PEDollController/BlobFormatters/FmtHex.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace PEDollController.BlobFormatters 5 | { 6 | class FmtHex : IBlobFormatter 7 | { 8 | string PieceToRaw(byte[] blob, int offset) 9 | { 10 | int pieceLen = Math.Min(blob.Length - offset, 16); 11 | 12 | // Similar to FmtRaw.ToScreen(), but convert any control characters (not just '\r') to '.'. 13 | StringBuilder ret = new StringBuilder(); 14 | 15 | for(int i = 0; i < pieceLen; i++) 16 | { 17 | char c = (char)blob[offset + i]; 18 | // Excluded characters: 19 | // 1. Non-ASCII characters 20 | // 2. Control characters 21 | ret.Append((blob[offset + i] > 0x7f || Char.IsControl(c)) ? '.' : c); 22 | } 23 | 24 | return ret.ToString(); 25 | } 26 | 27 | string PieceToHex(byte[] blob, ref int offset) 28 | { 29 | int pieceLen = Math.Min(blob.Length - offset, 8); 30 | string ret = (pieceLen > 0) ? BitConverter.ToString(blob, offset, pieceLen) : String.Empty; 31 | ret = ret.PadRight(3 * 8 - 1).Replace('-', ' '); 32 | 33 | offset += pieceLen; 34 | return ret; 35 | } 36 | 37 | public string ToScreen(byte[] blob) 38 | { 39 | StringBuilder ret = new StringBuilder(); 40 | 41 | int offset = 0; 42 | while (offset < blob.Length) 43 | { 44 | // Offset 45 | ret.Append(offset.ToString("x8")); 46 | ret.Append(" "); 47 | 48 | // Divide a line of 16 bytes as two pieces of 8 bytes 49 | int offsetNew = offset; 50 | ret.Append(PieceToHex(blob, ref offsetNew)); 51 | ret.Append(" "); 52 | ret.Append(PieceToHex(blob, ref offsetNew)); 53 | ret.Append(" "); 54 | 55 | // ASCII 56 | ret.Append(PieceToRaw(blob, offset)); 57 | ret.Append(Environment.NewLine); 58 | 59 | offset = offsetNew; 60 | } 61 | 62 | // Blob size at the last line 63 | ret.Append(offset.ToString("x8")); 64 | ret.Append(Environment.NewLine); 65 | 66 | return ret.ToString(); 67 | } 68 | 69 | public byte[] ToFile(byte[] blob) 70 | { 71 | return Encoding.UTF8.GetBytes(ToScreen(blob)); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /PEDollController/BlobFormatters/FmtRaw.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace PEDollController.BlobFormatters 5 | { 6 | class FmtRaw : IBlobFormatter 7 | { 8 | public string ToScreen(byte[] blob) 9 | { 10 | StringBuilder ret = new StringBuilder(); 11 | 12 | foreach (byte b in blob) 13 | { 14 | char c = (char)b; 15 | // Excluded characters: 16 | // 1. Non-ASCII characters 17 | // 2. '\r' (Will cause characters being overwritten if displayed on console) 18 | // 3. Other non-whitespace control characters 19 | if (b > 0x7f || c == '\r' || (Char.IsControl(c) && !Char.IsWhiteSpace(c))) 20 | ret.Append('.'); 21 | else 22 | ret.Append(c); 23 | } 24 | 25 | return ret.ToString(); 26 | } 27 | 28 | public byte[] ToFile(byte[] blob) 29 | { 30 | return blob; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /PEDollController/BlobFormatters/FmtText.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace PEDollController.BlobFormatters 5 | { 6 | class FmtText : IBlobFormatter 7 | { 8 | Encoding encoding; 9 | 10 | public FmtText(Encoding encoding) 11 | { 12 | this.encoding = encoding; 13 | } 14 | 15 | public string ToScreen(byte[] blob) 16 | { 17 | try 18 | { 19 | return encoding.GetString(blob); 20 | } 21 | catch(Exception e) 22 | { 23 | if (e is ArgumentException || e is DecoderFallbackException) 24 | throw new ArgumentException(e.Message); 25 | else 26 | throw; 27 | } 28 | } 29 | 30 | public byte[] ToFile(byte[] blob) 31 | { 32 | return Encoding.UTF8.GetBytes(ToScreen(blob)); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /PEDollController/BlobFormatters/FmtX86.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | using Gee.External.Capstone; 5 | using Gee.External.Capstone.X86; 6 | 7 | namespace PEDollController.BlobFormatters 8 | { 9 | class FmtX86 : IBlobFormatter 10 | { 11 | CapstoneX86Disassembler disassembler; 12 | 13 | public FmtX86(X86DisassembleMode mode) 14 | { 15 | disassembler = CapstoneDisassembler.CreateX86Disassembler(mode); 16 | disassembler.DisassembleSyntax = DisassembleSyntax.Intel; 17 | 18 | // Represent invalid instructions as "db 0x.." 19 | disassembler.EnableSkipDataMode = true; 20 | disassembler.SkipDataInstructionMnemonic = "db"; 21 | } 22 | 23 | ~FmtX86() 24 | { 25 | if (disassembler != null) 26 | disassembler.Dispose(); 27 | } 28 | 29 | public string ToScreen(byte[] blob) 30 | { 31 | StringBuilder ret = new StringBuilder(); 32 | X86Instruction[] instructions = disassembler.Disassemble(blob, 0); 33 | 34 | int maxbytes = 0; 35 | foreach (X86Instruction instruction in instructions) 36 | { 37 | maxbytes = Math.Max(maxbytes, instruction.Bytes.Length); 38 | 39 | // About 98.5% of IA32 instructions are shorter than 8 bytes 40 | // Reference: Fig.2 from https://www.strchr.com/x86_machine_code_statistics 41 | if (maxbytes > 8) 42 | { 43 | maxbytes = 8; 44 | break; 45 | } 46 | } 47 | 48 | foreach (X86Instruction instruction in instructions) 49 | { 50 | int bytesLen = instruction.Bytes.Length; 51 | string bytesStr; 52 | if (bytesLen > maxbytes) 53 | { 54 | bytesStr = BitConverter.ToString(instruction.Bytes, 0, maxbytes - 1) + " .."; 55 | } 56 | else 57 | { 58 | bytesStr = BitConverter.ToString(instruction.Bytes).PadRight(3 * maxbytes - 1); 59 | } 60 | bytesStr = bytesStr.Replace("-", " "); 61 | 62 | // if (!instruction.IsSkippedData) { ...} 63 | //ret.Append(String.Format("{0:x8} {1} {2}\t{3}", instruction.Address, bytesStr, instruction.Mnemonic, instruction.Operand)); 64 | ret.Append(instruction.Address.ToString("x8")); 65 | ret.Append(" "); 66 | ret.Append(bytesStr); 67 | ret.Append(" "); 68 | ret.Append(instruction.Mnemonic); 69 | ret.Append('\t'); 70 | ret.Append(instruction.Operand); 71 | ret.Append(Environment.NewLine); 72 | } 73 | 74 | return ret.ToString(); 75 | } 76 | 77 | public byte[] ToFile(byte[] blob) 78 | { 79 | return Encoding.UTF8.GetBytes(ToScreen(blob)); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /PEDollController/BlobFormatters/IBlobFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PEDollController.BlobFormatters 4 | { 5 | interface IBlobFormatter 6 | { 7 | string ToScreen(byte[] blob); 8 | 9 | byte[] ToFile(byte[] blob); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /PEDollController/BlobFormatters/Util.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | 5 | using Gee.External.Capstone.X86; 6 | 7 | namespace PEDollController.BlobFormatters 8 | { 9 | static class Util 10 | { 11 | public static readonly Dictionary Formatters = new Dictionary() 12 | { 13 | { "hex", new FmtHex() }, 14 | { "raw", new FmtRaw() }, 15 | 16 | { "ansi", new FmtText(Encoding.Default) }, 17 | { "unicode", new FmtText(Encoding.Unicode) }, 18 | { "utf8", new FmtText(Encoding.UTF8) }, 19 | 20 | { "8086", new FmtX86(X86DisassembleMode.Bit16) }, 21 | { "x86", new FmtX86(X86DisassembleMode.Bit32) }, 22 | { "x64", new FmtX86(X86DisassembleMode.Bit64) }, 23 | }; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /PEDollController/Commands/CmdBreak.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace PEDollController.Commands 5 | { 6 | 7 | // Command "break": Suspend/Resume all threads. 8 | // break 9 | 10 | class CmdBreak : ICommand 11 | { 12 | public string HelpResId() => "Commands.Help.Break"; 13 | public string HelpShortResId() => "Commands.HelpShort.Break"; 14 | 15 | public Dictionary Parse(string cmd) 16 | { 17 | // Ignores any arguments 18 | 19 | return new Dictionary() 20 | { 21 | { "verb", "break" } 22 | }; 23 | } 24 | 25 | public void Invoke(Dictionary options) 26 | { 27 | Threads.Client client = Threads.CmdEngine.theInstance.GetTargetClient(false); 28 | 29 | // Does not allow suspending a hooked process 30 | if(client.hookOep != 0) 31 | throw new ArgumentException(Program.GetResourceString("Threads.CmdEngine.TargetNotApplicable")); 32 | 33 | // Send CMD_BREAK 34 | client.Send(Puppet.Util.Serialize(new Puppet.PACKET_CMD_BREAK(0))); 35 | 36 | // Expect ACK(0) 37 | Puppet.PACKET_ACK pktAck; 38 | pktAck = Puppet.Util.Deserialize(client.Expect(Puppet.PACKET_TYPE.ACK)); 39 | if (pktAck.status != 0) 40 | throw new ArgumentException(Util.Win32ErrorToMessage((int)pktAck.status)); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /PEDollController/Commands/CmdDump.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Collections.Generic; 5 | 6 | using Mono.Options; 7 | 8 | namespace PEDollController.Commands 9 | { 10 | 11 | // Command "rem": Comment. Do nothing. 12 | // rem [anything]... 13 | // #[anything]... 14 | 15 | class CmdDump : ICommand 16 | { 17 | public string HelpResId() => "Commands.Help.Dump"; 18 | public string HelpShortResId() => "Commands.HelpShort.Dump"; 19 | 20 | public Dictionary Parse(string cmd) 21 | { 22 | int id = -1; 23 | string format = BlobFormatters.Util.Formatters.Keys.First(); 24 | string save = null; 25 | 26 | OptionSet options = new OptionSet() 27 | { 28 | { 29 | "format=", 30 | x => 31 | { 32 | if(!BlobFormatters.Util.Formatters.ContainsKey(x)) 33 | throw new ArgumentException("format"); 34 | format = x; 35 | } 36 | }, 37 | { 38 | "save=", 39 | x => 40 | { 41 | save = Util.RemoveQuotes(x); 42 | } 43 | }, 44 | { 45 | "<>", 46 | (uint x) => 47 | { 48 | if(x >= Threads.CmdEngine.theInstance.dumps.Count) 49 | throw new ArgumentException("id"); 50 | id = (int)x; 51 | } 52 | } 53 | }; 54 | Util.ParseOptions(cmd, options); 55 | 56 | return new Dictionary() 57 | { 58 | { "verb", "dump" }, 59 | { "id", id }, 60 | { "format", format }, 61 | { "save", save } 62 | }; 63 | } 64 | 65 | public void Invoke(Dictionary options) 66 | { 67 | int id = (int)options["id"]; 68 | string format = (string)options["format"]; 69 | string save = (string)options["save"]; 70 | 71 | if(id < 0) 72 | { 73 | Logger.I(Program.GetResourceString("Commands.Dump.Header")); 74 | 75 | for (int i = 0; i < Threads.CmdEngine.theInstance.dumps.Count; i++) 76 | { 77 | Threads.DumpEntry entry = Threads.CmdEngine.theInstance.dumps[i]; 78 | 79 | Logger.I(Program.GetResourceString("Commands.Dump.Format", 80 | i, 81 | entry.Data.Length, 82 | entry.Source 83 | )); 84 | } 85 | return; 86 | } 87 | 88 | BlobFormatters.IBlobFormatter formatter = BlobFormatters.Util.Formatters[format]; 89 | Threads.DumpEntry dump = Threads.CmdEngine.theInstance.dumps[id]; 90 | 91 | if(save == null) 92 | { 93 | Logger.I(Program.GetResourceString("Commands.Dump.Title", id, dump.Source, dump.Data.Length, format)); 94 | 95 | string formatted = formatter.ToScreen(dump.Data); 96 | Logger.I(formatted); 97 | Threads.Gui.theInstance.InvokeOn((FMain Me) => 98 | { 99 | Me.txtDumpContent.Text = formatted; 100 | }); 101 | } 102 | else 103 | { 104 | BinaryWriter writer = null; 105 | try 106 | { 107 | writer = new BinaryWriter(File.Open(save, FileMode.Create)); 108 | writer.Write(formatter.ToFile(dump.Data)); 109 | writer.Close(); 110 | } 111 | catch (Exception e) 112 | { 113 | throw new ArgumentException(Program.GetResourceString("Commands.IOError", e.GetType().Name, e.Message)); 114 | } 115 | finally 116 | { 117 | if (writer != null) 118 | writer.Dispose(); 119 | } 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /PEDollController/Commands/CmdEnd.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace PEDollController.Commands 5 | { 6 | 7 | // Command "end": Ends the client, stopping its process. 8 | // end 9 | 10 | class CmdEnd : ICommand 11 | { 12 | public string HelpResId() => "Commands.Help.End"; 13 | public string HelpShortResId() => "Commands.HelpShort.End"; 14 | 15 | public Dictionary Parse(string cmd) 16 | { 17 | // Ignores any arguments 18 | 19 | return new Dictionary() 20 | { 21 | { "verb", "end" } 22 | }; 23 | } 24 | 25 | public void Invoke(Dictionary options) 26 | { 27 | Threads.Client client = Threads.CmdEngine.theInstance.GetTargetClient(); 28 | 29 | // Send CMD_END 30 | client.Send(Puppet.Util.Serialize(new Puppet.PACKET_CMD_END(0))); 31 | 32 | // Expect ACK 33 | client.Expect(Puppet.PACKET_TYPE.ACK); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /PEDollController/Commands/CmdEval.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Mono.Options; 5 | 6 | namespace PEDollController.Commands 7 | { 8 | 9 | // Command "eval": Manually call EvalEngine. 10 | // eval EXPR 11 | 12 | class CmdEval : ICommand 13 | { 14 | 15 | static string RemoveQuotes(string x) 16 | { 17 | return (x[0] == '"' && x[x.Length - 1] == '"') ? x.Substring(1, x.Length - 2) : x; 18 | } 19 | 20 | // ---------- 21 | 22 | public string HelpResId() => "Commands.Help.Eval"; 23 | public string HelpShortResId() => "Commands.HelpShort.Eval"; 24 | 25 | public Dictionary Parse(string cmd) 26 | { 27 | string expr = null; 28 | 29 | OptionSet options = new OptionSet() 30 | { 31 | { "<>", x => expr = x } 32 | }; 33 | Util.ParseOptions(cmd, options); 34 | 35 | if (expr == null) 36 | throw new ArgumentException("expr"); 37 | 38 | return new Dictionary() 39 | { 40 | { "verb", "eval" }, 41 | { "expr", RemoveQuotes(expr) } 42 | }; 43 | } 44 | 45 | public void Invoke(Dictionary options) 46 | { 47 | Threads.Client client = Threads.CmdEngine.theInstance.GetTargetClient(false); 48 | if(client.hookOep == 0) 49 | throw new ArgumentException(Program.GetResourceString("Threads.CmdEngine.TargetNotApplicable")); 50 | 51 | string expr = (string)options["expr"]; 52 | string result = Program.GetResourceString( 53 | "Threads.Client.Eval", 54 | expr, 55 | Threads.EvalEngine.EvalString(client, expr) 56 | ); 57 | Logger.I(result); 58 | Threads.Gui.theInstance.InvokeOn((FMain Me) => Me.txtHookedResults.Text += (result + Environment.NewLine)); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /PEDollController/Commands/CmdExit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace PEDollController.Commands 5 | { 6 | // Command "exit": Quit Controller. 7 | // exit 8 | 9 | class CmdExit : ICommand 10 | { 11 | public string HelpResId() => "Commands.Help.Exit"; 12 | public string HelpShortResId() => "Commands.HelpShort.Exit"; 13 | 14 | public Dictionary Parse(string cmd) 15 | { 16 | // Ignores any arguments 17 | 18 | return new Dictionary() 19 | { 20 | { "verb", "exit" } 21 | }; 22 | } 23 | 24 | public void Invoke(Dictionary options) 25 | { 26 | Threads.CmdEngine.theInstance.stopTaskEvent.Set(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /PEDollController/Commands/CmdHelp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Mono.Options; 5 | 6 | namespace PEDollController.Commands 7 | { 8 | 9 | // Command "help": Show help message for all commands / specified command 10 | // help [command] 11 | 12 | class CmdHelp : ICommand 13 | { 14 | public string HelpResId() => "Commands.Help.Help"; 15 | public string HelpShortResId() => "Commands.HelpShort.Help"; 16 | 17 | public Dictionary Parse(string cmd) 18 | { 19 | string command = null; 20 | 21 | OptionSet options = new OptionSet() 22 | { 23 | { "<>", x => command = x } 24 | }; 25 | Util.ParseOptions(cmd, options); 26 | 27 | return new Dictionary() 28 | { 29 | { "verb", "help" }, 30 | { "command", command } 31 | }; 32 | } 33 | 34 | public void Invoke(Dictionary options) 35 | { 36 | string command = (string)options["command"]; 37 | if (String.IsNullOrEmpty(command)) 38 | ShowHelpScreen(); 39 | else if(Util.Commands.ContainsKey(command)) 40 | Logger.I(Program.GetResourceString(Util.Commands[command].HelpResId())); 41 | else 42 | throw new ArgumentException(Program.GetResourceString("Commands.Unknown", command)); 43 | } 44 | 45 | void ShowHelpScreen() 46 | { 47 | Logger.I(Program.GetResourceString("Commands.HelpShort.Header")); 48 | 49 | // A sorted list of help messages is more readable 50 | SortedSet commandNames = new SortedSet(Util.Commands.Keys); 51 | foreach (string commandName in commandNames) 52 | Logger.I(Program.GetResourceString(Util.Commands[commandName].HelpShortResId())); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /PEDollController/Commands/CmdKill.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Mono.Options; 5 | 6 | namespace PEDollController.Commands 7 | { 8 | 9 | // Command "kill": `kill`/`killall` equivalent. 10 | // kill PID 11 | // kill --all NAME 12 | 13 | class CmdKill : ICommand 14 | { 15 | public string HelpResId() => "Commands.Help.Kill"; 16 | public string HelpShortResId() => "Commands.HelpShort.Kill"; 17 | 18 | public Dictionary Parse(string cmd) 19 | { 20 | bool killAll = false; 21 | string extras; 22 | 23 | OptionSet options = new OptionSet() 24 | { 25 | { "all", x => killAll = (x != null) } 26 | }; 27 | extras = String.Join(" ", Util.ParseOptions(cmd, options)); 28 | 29 | string name = null; 30 | int pid = 0; 31 | 32 | if (killAll) 33 | { 34 | name = extras; 35 | } 36 | else 37 | { 38 | try 39 | { 40 | pid = Convert.ToInt32(extras); 41 | } 42 | catch (Exception e) 43 | { 44 | throw new ArgumentException(e.Message); 45 | } 46 | } 47 | 48 | return new Dictionary() 49 | { 50 | { "verb", "kill" }, 51 | { "killAll", killAll }, 52 | { "name", name }, 53 | { "pid", pid } 54 | }; 55 | } 56 | 57 | public void Invoke(Dictionary options) 58 | { 59 | Threads.Client client = Threads.CmdEngine.theInstance.GetTargetClient(true); 60 | 61 | // Send CMD_KILL 62 | Puppet.PACKET_CMD_KILL pktKill = new Puppet.PACKET_CMD_KILL(0); 63 | pktKill.pid = (UInt32)(int)options["pid"]; 64 | client.Send(Puppet.Util.Serialize(pktKill)); 65 | 66 | if ((bool)options["killAll"]) 67 | client.Send(Puppet.Util.SerializeString((string)options["name"])); 68 | 69 | // Expect ACK(0) 70 | Puppet.PACKET_ACK pktAck; 71 | pktAck = Puppet.Util.Deserialize(client.Expect(Puppet.PACKET_TYPE.ACK)); 72 | if (pktAck.status != 0) 73 | throw new ArgumentException(Util.Win32ErrorToMessage((int)pktAck.status)); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /PEDollController/Commands/CmdListen.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | using System.Collections.Generic; 5 | 6 | using Mono.Options; 7 | 8 | namespace PEDollController.Commands 9 | { 10 | // Command "listen": Listen for clients. 11 | // listen [--ipv6] [PORT] 12 | 13 | class CmdListen : ICommand 14 | { 15 | public string HelpResId() => "Commands.Help.Listen"; 16 | public string HelpShortResId() => "Commands.HelpShort.Listen"; 17 | 18 | public Dictionary Parse(string cmd) 19 | { 20 | bool ipv6 = false; 21 | int port = Puppet.Util.DEFAULT_PORT; 22 | 23 | OptionSet options = new OptionSet() 24 | { 25 | { "ipv6", x => ipv6 = (x != null) }, 26 | { "<>", (ushort x) => port = x } 27 | }; 28 | Util.ParseOptions(cmd, options); 29 | 30 | return new Dictionary() 31 | { 32 | { "verb", "listen" }, 33 | { "ipv6", ipv6 }, 34 | { "port", port } 35 | }; 36 | } 37 | 38 | public void Invoke(Dictionary options) 39 | { 40 | if (Threads.Listener.theInstance != null) 41 | throw new ArgumentException(Program.GetResourceString("Commands.Listen.AlreadyStarted")); 42 | 43 | bool ipv6 = (bool)options["ipv6"]; 44 | int port = (int)options["port"]; 45 | 46 | try 47 | { 48 | Threads.Listener.CreateInstance(ipv6, port); 49 | } 50 | catch(Exception e) 51 | { 52 | throw new ArgumentException(e.ToString()); 53 | } 54 | 55 | Logger.I(Program.GetResourceString("Commands.Listen.AvailableAddresses")); 56 | 57 | List addresses = new List(Dns.GetHostAddresses(Dns.GetHostName())); 58 | addresses.Add(IPAddress.Loopback); 59 | addresses.Add(IPAddress.IPv6Loopback); 60 | foreach(IPAddress address in addresses) 61 | { 62 | if (address.AddressFamily != (ipv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork)) 63 | continue; 64 | 65 | if(port == Puppet.Util.DEFAULT_PORT) 66 | Logger.I(address.ToString()); 67 | else 68 | Logger.I(ipv6 ? "[{0}]:{1}" : "{0}:{1}", address.ToString(), port); 69 | } 70 | 71 | Threads.Gui.theInstance.InvokeOn((FMain Me) => 72 | { 73 | Me.pnlListenerStart.Enabled = false; 74 | }); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /PEDollController/Commands/CmdLoad.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | using System.Collections.Generic; 6 | 7 | namespace PEDollController.Commands 8 | { 9 | 10 | // Command "load": Load commands from a script. 11 | // load 12 | // load "" 13 | 14 | class CmdLoad : ICommand 15 | { 16 | public string HelpResId() => "Commands.Help.Load"; 17 | public string HelpShortResId() => "Commands.HelpShort.Load"; 18 | 19 | public Dictionary Parse(string cmd) 20 | { 21 | List args = Commandline.ToArgs(cmd); 22 | string script = args[0]; 23 | string arguments = String.Join(" ", args.Skip(1)); 24 | 25 | if (script.StartsWith("\"") && script.EndsWith("\"")) 26 | { 27 | // If embraced with double-quotes, treat the path as a qualified path and no more guessing 28 | script = script.Trim('"'); 29 | try 30 | { 31 | script = Path.GetFullPath(script); 32 | } 33 | catch(Exception e) 34 | { 35 | if (e is ArgumentException || e is NotSupportedException || e is PathTooLongException) 36 | throw new ArgumentException("script"); 37 | else 38 | throw; // Not expected to be `catch`ed 39 | } 40 | } 41 | else 42 | { 43 | // Otherwise the input is treated as a plain file name 44 | foreach(char c in Path.GetInvalidFileNameChars()) 45 | { 46 | if (script.Contains(c)) 47 | throw new ArgumentException("script"); 48 | } 49 | 50 | if (!Path.HasExtension(script)) 51 | script = Path.ChangeExtension(script, "txt"); 52 | 53 | string selfPath = Path.GetDirectoryName(Application.ExecutablePath); 54 | string possiblePath = Path.Combine(selfPath, "Scripts", script); 55 | if (File.Exists(possiblePath)) 56 | script = possiblePath; // $self\Scripts\$script 57 | else 58 | script = Path.Combine(selfPath, "Scripts", "API", script); // $self\Scripts\API\$script 59 | } 60 | 61 | return new Dictionary() 62 | { 63 | { "verb", "load" }, 64 | { "script", script }, 65 | { "arguments", arguments } 66 | }; 67 | } 68 | 69 | public void Invoke(Dictionary options) 70 | { 71 | string script = (string)options["script"]; 72 | string arguments = (string)options["arguments"]; 73 | 74 | StreamReader reader = null; 75 | try 76 | { 77 | reader = new StreamReader(script); 78 | 79 | string line; 80 | while ((line = reader.ReadLine()) != null) 81 | { 82 | if (line.EndsWith("*")) 83 | line = line.TrimEnd('*') + arguments; 84 | Threads.CmdEngine.theInstance.AddCommand(line); 85 | } 86 | 87 | reader.Close(); 88 | } 89 | catch (Exception e) 90 | { 91 | throw new ArgumentException(Program.GetResourceString("Commands.IOError", e.GetType().Name, e.Message)); 92 | } 93 | finally 94 | { 95 | if (reader != null) 96 | reader.Dispose(); 97 | } 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /PEDollController/Commands/CmdLoaddll.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Mono.Options; 5 | 6 | namespace PEDollController.Commands 7 | { 8 | 9 | // Command "loaddll": Call LoadLibrary() in a new thread. 10 | // loaddll MODULE 11 | 12 | class CmdLoadDll : ICommand 13 | { 14 | public string HelpResId() => "Commands.Help.LoadDll"; 15 | public string HelpShortResId() => "Commands.HelpShort.LoadDll"; 16 | 17 | public Dictionary Parse(string cmd) 18 | { 19 | string module = null; 20 | 21 | OptionSet options = new OptionSet() 22 | { 23 | { "<>", x => module = Util.RemoveQuotes(x) } 24 | }; 25 | Util.ParseOptions(cmd, options); 26 | 27 | if (module == null) 28 | throw new ArgumentException("module"); 29 | 30 | return new Dictionary() 31 | { 32 | { "verb", "loaddll" }, 33 | { "module", module } 34 | }; 35 | } 36 | 37 | public void Invoke(Dictionary options) 38 | { 39 | Threads.Client client = Threads.CmdEngine.theInstance.GetTargetClient(false); 40 | 41 | // Send CMD_LOADDLL packets 42 | client.Send(Puppet.Util.Serialize(new Puppet.PACKET_CMD_LOADDLL(0))); 43 | client.Send(Puppet.Util.SerializeString((string)options["module"])); 44 | 45 | // Expect ACK(0) 46 | Puppet.PACKET_ACK pktAck; 47 | pktAck = Puppet.Util.Deserialize(client.Expect(Puppet.PACKET_TYPE.ACK)); 48 | if (pktAck.status != 0) 49 | throw new ArgumentException(Util.Win32ErrorToMessage((int)pktAck.status)); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /PEDollController/Commands/CmdPs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | using System.Collections.Generic; 4 | 5 | namespace PEDollController.Commands 6 | { 7 | 8 | // Command "ps": `ps` equivalent. 9 | // ps 10 | 11 | class CmdPs : ICommand 12 | { 13 | public string HelpResId() => "Commands.Help.Ps"; 14 | public string HelpShortResId() => "Commands.HelpShort.Ps"; 15 | 16 | public Dictionary Parse(string cmd) 17 | { 18 | // Ignores any arguments 19 | 20 | return new Dictionary() 21 | { 22 | { "verb", "ps" } 23 | }; 24 | } 25 | 26 | public void Invoke(Dictionary options) 27 | { 28 | Threads.Client client = Threads.CmdEngine.theInstance.GetTargetClient(true); 29 | 30 | // Send CMD_PS 31 | client.Send(Puppet.Util.Serialize(new Puppet.PACKET_CMD_PS(0))); 32 | 33 | // Expect ACK(0) 34 | Puppet.PACKET_ACK pktAck; 35 | pktAck = Puppet.Util.Deserialize(client.Expect(Puppet.PACKET_TYPE.ACK)); 36 | if (pktAck.status != 0) 37 | throw new ArgumentException(Util.Win32ErrorToMessage((int)pktAck.status)); 38 | 39 | Logger.I(Program.GetResourceString("Commands.Ps.Header")); 40 | 41 | Threads.Gui.theInstance.InvokeOn((FDlgBrowsePID Me) => 42 | { 43 | Me.lstPs.Items.Clear(); 44 | }); 45 | 46 | // Obtain entries 47 | Puppet.PACKET_INTEGER pktInt; 48 | while(true) 49 | { 50 | pktInt = Puppet.Util.Deserialize(client.Expect(Puppet.PACKET_TYPE.INTEGER)); 51 | if ((Int64)pktInt.data == -1) 52 | break; 53 | 54 | string name = Puppet.Util.DeserializeString(client.Expect(Puppet.PACKET_TYPE.STRING)); 55 | Logger.I(Program.GetResourceString("Commands.Ps.Format", pktInt.data, name)); 56 | 57 | Threads.Gui.theInstance.InvokeOn((FDlgBrowsePID Me) => 58 | { 59 | Me.lstPs.Items.Add(new ListViewItem(new string[] { 60 | pktInt.data.ToString(), 61 | name 62 | })); 63 | }); 64 | } 65 | 66 | Threads.Gui.theInstance.InvokeOn((FDlgBrowsePID Me) => 67 | { 68 | Me.lstPs.Items[0].Selected = true; 69 | Me.UseWaitCursor = false; 70 | }); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /PEDollController/Commands/CmdRem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace PEDollController.Commands 5 | { 6 | 7 | // Command "rem": Comment. Do nothing. 8 | // rem [anything]... 9 | // #[anything]... 10 | 11 | class CmdRem : ICommand 12 | { 13 | public string HelpResId() => "Commands.Help.Rem"; 14 | public string HelpShortResId() => "Commands.HelpShort.Rem"; 15 | 16 | public Dictionary Parse(string cmd) 17 | { 18 | // Ignores any arguments 19 | 20 | return new Dictionary() 21 | { 22 | { "verb", "rem" } 23 | }; 24 | } 25 | 26 | public void Invoke(Dictionary options) 27 | { 28 | // Do nothing 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /PEDollController/Commands/CmdShell.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace PEDollController.Commands 5 | { 6 | 7 | // Command "shell": `system()` equivalent. 8 | // shell [ARGUMENTS ...] 9 | 10 | class CmdShell : ICommand 11 | { 12 | public string HelpResId() => "Commands.Help.Shell"; 13 | public string HelpShortResId() => "Commands.HelpShort.Shell"; 14 | 15 | public Dictionary Parse(string cmd) 16 | { 17 | string arguments = String.Join(" ", Commandline.ToArgs(cmd)); 18 | 19 | return new Dictionary() 20 | { 21 | { "verb", "shell" }, 22 | { "arguments", arguments } 23 | }; 24 | } 25 | 26 | public void Invoke(Dictionary options) 27 | { 28 | Threads.Client client = Threads.CmdEngine.theInstance.GetTargetClient(true); 29 | 30 | // Send CMD_SHELL 31 | client.Send(Puppet.Util.Serialize(new Puppet.PACKET_CMD_SHELL(0))); 32 | 33 | // Send arguments 34 | client.Send(Puppet.Util.SerializeString((string)options["arguments"])); 35 | 36 | // Expect ACK(0) 37 | Puppet.PACKET_ACK pktAck; 38 | pktAck = Puppet.Util.Deserialize(client.Expect(Puppet.PACKET_TYPE.ACK)); 39 | if (pktAck.status != 0) 40 | throw new ArgumentException(Util.Win32ErrorToMessage((int)pktAck.status)); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /PEDollController/Commands/CmdTarget.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | using System.Collections.Generic; 4 | 5 | using Mono.Options; 6 | 7 | namespace PEDollController.Commands 8 | { 9 | 10 | // Command "target": Shows/switches target client. 11 | // target[ID] 12 | // target --lastDoll 13 | // target --lastMonitor 14 | 15 | class CmdTarget : ICommand 16 | { 17 | public string HelpResId() => "Commands.Help.Target"; 18 | public string HelpShortResId() => "Commands.HelpShort.Target"; 19 | 20 | public Dictionary Parse(string cmd) 21 | { 22 | int target = -1; 23 | bool lastDoll = false; 24 | bool lastMonitor = false; 25 | 26 | OptionSet options = new OptionSet() 27 | { 28 | { "doll", x => lastDoll = (x != null) }, 29 | { "monitor", x => lastMonitor = (x != null) }, 30 | { "<>", (int x) => target = x } 31 | }; 32 | Util.ParseOptions(cmd, options); 33 | 34 | if (lastDoll && lastMonitor) 35 | throw new ArgumentException(); 36 | 37 | return new Dictionary() 38 | { 39 | { "verb", "target" }, 40 | { "target", target }, 41 | { "lastDoll", lastDoll }, 42 | { "lastMonitor", lastMonitor } 43 | }; 44 | } 45 | 46 | public void Invoke(Dictionary options) 47 | { 48 | int target = (int)options["target"]; 49 | bool lastDoll = (bool)options["lastDoll"]; 50 | bool lastMonitor = (bool)options["lastMonitor"]; 51 | 52 | if(target == -1 && !lastDoll && !lastMonitor) 53 | { 54 | Logger.I(Program.GetResourceString("Commands.Target.Header")); 55 | 56 | for(int i = 0; i < Threads.Client.theInstances.Count; i++) 57 | { 58 | Threads.Client instance = Threads.Client.theInstances[i]; 59 | 60 | Logger.I(Program.GetResourceString("Commands.Target.Format", 61 | (i == Threads.CmdEngine.theInstance.target) ? '*' : ' ', 62 | i, 63 | instance.clientName, 64 | instance.GetTypeString(), 65 | instance.GetStatusString(), 66 | instance.pid, 67 | instance.bits 68 | )); 69 | } 70 | return; 71 | } 72 | 73 | if(lastDoll) 74 | target = Threads.CmdEngine.theInstance.targetLastDoll; 75 | else if(lastMonitor) 76 | target = Threads.CmdEngine.theInstance.targetLastMonitor; 77 | 78 | if(target < 0 || target >= Threads.Client.theInstances.Count || Threads.Client.theInstances[target].isDead) 79 | throw new ArgumentException(Program.GetResourceString("Threads.CmdEngine.TargetNotAvailable")); 80 | 81 | int targetLast = Threads.CmdEngine.theInstance.target; 82 | Threads.CmdEngine.theInstance.target = target; 83 | 84 | Threads.Client clientLast = Threads.Client.theInstances[targetLast]; 85 | if (clientLast.isMonitor) 86 | Threads.CmdEngine.theInstance.targetLastMonitor = clientLast.isDead ? target : targetLast; 87 | else 88 | Threads.CmdEngine.theInstance.targetLastDoll = clientLast.isDead ? target : targetLast; 89 | 90 | Threads.Client client = Threads.Client.theInstances[target]; 91 | 92 | Logger.I(Program.GetResourceString("Commands.Target.CurrentTarget", 93 | target, 94 | client.clientName, 95 | client.GetTypeString(), 96 | client.GetStatusString() 97 | )); 98 | 99 | Threads.Gui.theInstance.InvokeOn((FMain Me) => Me.RefreshGuiTargets()); 100 | } 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /PEDollController/Commands/CmdUnhook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | 5 | using Mono.Options; 6 | 7 | namespace PEDollController.Commands 8 | { 9 | 10 | // Command "unhook": Uninstalls a hook. 11 | // unhook 0xOEP 12 | 13 | class CmdUnhook : ICommand 14 | { 15 | public string HelpResId() => "Commands.Help.Unhook"; 16 | public string HelpShortResId() => "Commands.HelpShort.Unhook"; 17 | 18 | public Dictionary Parse(string cmd) 19 | { 20 | int id = -1; 21 | 22 | OptionSet options = new OptionSet() 23 | { 24 | { "<>", (uint x) => id = (int)x } 25 | }; 26 | Util.ParseOptions(cmd, options); 27 | 28 | return new Dictionary() 29 | { 30 | { "verb", "unhook" }, 31 | { "id", id } 32 | }; 33 | } 34 | 35 | public void Invoke(Dictionary options) 36 | { 37 | int id = (int)options["id"]; 38 | Threads.Client client = Threads.CmdEngine.theInstance.GetTargetClient(false); 39 | 40 | if(id >= client.hooks.Count || client.hooks[id].name == null) 41 | throw new ArgumentException(Program.GetResourceString("Commands.Unhook.NotFound")); 42 | 43 | Threads.HookEntry entry = client.hooks[id]; 44 | // If client is under the current hook, cancel the operation with TargetNotApplicable 45 | if (client.hookOep == entry.oep) 46 | throw new ArgumentException(Program.GetResourceString("Threads.CmdEngine.TargetNotApplicable")); 47 | 48 | // Prepare & send CMD_UNHOOK packets 49 | client.Send(Puppet.Util.Serialize(new Puppet.PACKET_CMD_UNHOOK(0))); 50 | client.Send(Puppet.Util.Serialize(new Puppet.PACKET_INTEGER(entry.oep))); 51 | 52 | // Expect ACK(0) 53 | Puppet.PACKET_ACK pktAck; 54 | pktAck = Puppet.Util.Deserialize(client.Expect(Puppet.PACKET_TYPE.ACK)); 55 | if (pktAck.status != 0) 56 | throw new ArgumentException(Util.Win32ErrorToMessage((int)pktAck.status)); 57 | 58 | // Remove entry from client's hooks 59 | //client.hooks.Remove(entry); // This will cause following hooks' IDs change 60 | client.hooks[id] = new Threads.HookEntry(); 61 | 62 | Logger.I(Program.GetResourceString("Commands.Unhook.Uninstalled", id, entry.name)); 63 | Threads.Gui.theInstance.InvokeOn((FMain Me) => Me.RefreshGuiHooks()); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /PEDollController/Commands/CmdVerdict.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Mono.Options; 5 | 6 | namespace PEDollController.Commands 7 | { 8 | 9 | // Command "verdict": Verdicts a activated hook. 10 | // verdict {approve|reject|terminate} 11 | 12 | class CmdVerdict : ICommand 13 | { 14 | static readonly string[] verdicts = { 15 | "approve", "reject", "terminate" 16 | }; 17 | 18 | // ---------- 19 | 20 | public string HelpResId() => "Commands.Help.Verdict"; 21 | public string HelpShortResId() => "Commands.HelpShort.Verdict"; 22 | 23 | public Dictionary Parse(string cmd) 24 | { 25 | string verdict = null; 26 | 27 | OptionSet options = new OptionSet() 28 | { 29 | { 30 | "<>", 31 | x => { 32 | if(Array.IndexOf(verdicts, x) < 0) 33 | throw new ArgumentException("verdict"); 34 | else 35 | verdict = x; 36 | } 37 | } 38 | }; 39 | Util.ParseOptions(cmd, options); 40 | 41 | if (verdict == null) 42 | throw new ArgumentException("verdict"); 43 | 44 | return new Dictionary() 45 | { 46 | { "verb", "verdict" }, 47 | { "verdict", verdict } 48 | }; 49 | } 50 | 51 | public void Invoke(Dictionary options) 52 | { 53 | string verdict = (string)options["verdict"]; 54 | 55 | Threads.Client client = Threads.CmdEngine.theInstance.GetTargetClient(false); 56 | if (client.hookOep == 0 || (client.hookPhase != 0 && verdict == "reject")) 57 | throw new ArgumentException(Program.GetResourceString("Threads.CmdEngine.TargetNotApplicable")); 58 | 59 | client.SendVerdict(verdict); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /PEDollController/Commands/CommandLine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | 5 | namespace PEDollController.Commands 6 | { 7 | public static class Commandline 8 | { 9 | 10 | // Commandline.ToArgv() provides an simple implemention of CommandLineToArgvW() 11 | // https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw 12 | // However, this implemention allows parens outside of quotes 13 | 14 | // Commandline.ToArgs() Removes argv[0] from result, make it easlier for Mono.Options to parse 15 | 16 | static Dictionary PairingCharacters = new Dictionary() 17 | { 18 | { '"', '"' }, 19 | { '\'', '\'' }, 20 | { '(', ')' }, 21 | { '[', ']' }, 22 | { '{', '}' } 23 | }; 24 | 25 | public static List ToArgv(string cmd, bool skipArgv0 = false) 26 | { 27 | List ret = new List(); 28 | StringBuilder builder = new StringBuilder(); 29 | 30 | Stack pairingStack = new Stack(); 31 | bool isEscaped = false; 32 | foreach (char c in cmd) 33 | { 34 | char stackTop = (pairingStack.Count == 0) ? (char)0 : pairingStack.Peek(); 35 | 36 | if (stackTop == '"' || stackTop == '\'') 37 | { 38 | // If inside a pair of quotes, consider following rules: 39 | // 1. (Escape rule) Character after '\\' is ignored (i.e. has no effect on rule appliance); 40 | // 2. No match rule except quotes may apply 41 | if (isEscaped) 42 | { 43 | isEscaped = false; 44 | } 45 | else 46 | { 47 | if (c == stackTop) 48 | pairingStack.Pop(); 49 | else if (c == '\\') 50 | isEscaped = true; 51 | } 52 | } 53 | else 54 | { 55 | // Otherwise, consider standard rules: 56 | // 1. (Match rule) If `c` is a left-hand pairing character, push it onto the stack; 57 | // 2. (Match rule) If `c` is a right-hand pairing character, 58 | // pop the stack if `c` matches the stack top, fail otherwise 59 | // 3. (Separate rule) If `c` is a whitespace character and stack is empty, 60 | // ignore `c` and cut the string to be a piece of argv[] 61 | if (PairingCharacters.ContainsKey(c)) 62 | { 63 | pairingStack.Push(c); 64 | } 65 | else if (PairingCharacters.ContainsValue(c)) 66 | { 67 | if (c == PairingCharacters[stackTop]) 68 | pairingStack.Pop(); 69 | else 70 | throw new ArgumentException(Program.GetResourceString("Commands.ParensMismatch")); 71 | } 72 | else if (Char.IsWhiteSpace(c) && pairingStack.Count == 0) 73 | { 74 | if (builder.Length > 0) 75 | { 76 | // Ignore argv[0] if required 77 | if (skipArgv0) 78 | skipArgv0 = false; 79 | else 80 | ret.Add(builder.ToString()); 81 | 82 | builder.Clear(); 83 | } 84 | continue; // Ignore `c` 85 | } 86 | } 87 | builder.Append(c); // Add `c` to current piece 88 | } 89 | 90 | // Add the leftover piece in builder 91 | if (builder.Length > 0 && !(skipArgv0 && ret.Count == 0)) 92 | ret.Add(builder.ToString()); 93 | 94 | // For now, if still in paren-matching state or escaped state, the string is incomplete and should fail 95 | if (isEscaped || pairingStack.Count != 0) 96 | throw new ArgumentException(Program.GetResourceString("Commands.Incomplete")); 97 | 98 | return ret; 99 | } 100 | 101 | public static List ToArgs(string cmd) 102 | { 103 | return ToArgv(cmd, true); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /PEDollController/Commands/ICommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace PEDollController.Commands 5 | { 6 | interface ICommand 7 | { 8 | // Resource ID of full help message (`help cmd`) 9 | string HelpResId(); 10 | 11 | // Resource ID of shortened (one-line) help message (`help`) 12 | string HelpShortResId(); 13 | 14 | // Parses a line of command to its options, throws ArgumentException on error 15 | Dictionary Parse(string cmd); 16 | 17 | // Invokes the command with given options, throws ArgumentException on error 18 | // Options are guaranteed to be sanitized but not verifyed 19 | void Invoke(Dictionary options); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PEDollController/Commands/Util.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | 5 | using Mono.Options; 6 | 7 | namespace PEDollController.Commands 8 | { 9 | static class Util 10 | { 11 | public static readonly Dictionary Commands = new Dictionary() 12 | { 13 | { "ps", new CmdPs() }, 14 | { "rem", new CmdRem() }, 15 | { "end", new CmdEnd() }, 16 | { "help", new CmdHelp() }, 17 | { "load", new CmdLoad() }, 18 | { "exit", new CmdExit() }, 19 | { "doll", new CmdDoll() }, 20 | { "kill", new CmdKill() }, 21 | { "hook", new CmdHook() }, 22 | { "eval", new CmdEval() }, 23 | { "dump", new CmdDump() }, 24 | { "shell", new CmdShell() }, 25 | { "break", new CmdBreak() }, 26 | { "listen", new CmdListen() }, 27 | { "target", new CmdTarget() }, 28 | { "unhook", new CmdUnhook() }, 29 | { "loaddll", new CmdLoadDll() }, 30 | { "verdict", new CmdVerdict() }, 31 | }; 32 | 33 | public static void Invoke(string cmd) 34 | { 35 | // Remove prefix whitespaces 36 | cmd = cmd.Trim(); 37 | 38 | string[] cmdExplode = cmd.Split(' '); 39 | string cmdVerb = cmdExplode[0]; // cmdExplode will have element as long as cmd is not null 40 | 41 | // Any command that's empty or start with a # is considered as a comment 42 | if (String.IsNullOrEmpty(cmdVerb) || cmdVerb.StartsWith("#")) 43 | cmdVerb = "rem"; 44 | 45 | if (!Commands.ContainsKey(cmdVerb)) 46 | throw new ArgumentException(Program.GetResourceString("Commands.Unknown", cmdVerb)); 47 | 48 | Dictionary options; 49 | try 50 | { 51 | options = Commands[cmdVerb].Parse(cmd); 52 | } 53 | catch (ArgumentException e) 54 | { 55 | throw new ArgumentException(Program.GetResourceString("Commands.Invalid", cmd, cmdVerb), e.Message); 56 | } 57 | 58 | Commands[cmdVerb].Invoke(options); 59 | } 60 | 61 | public static List ParseOptions(string cmd, OptionSet options) 62 | { 63 | try 64 | { 65 | return options.Parse(Commandline.ToArgs(cmd)); 66 | } 67 | catch (OptionException e) 68 | { 69 | throw new ArgumentException(e.Message); 70 | } 71 | } 72 | 73 | public static string RemoveQuotes(string x, bool doUnescape = false) 74 | { 75 | if (!x.StartsWith("\"") || !x.EndsWith("\"")) 76 | return x; 77 | 78 | string content = x.Substring(1, x.Length - 2); 79 | if (!doUnescape) 80 | return content; 81 | 82 | StringBuilder builder = new StringBuilder(); 83 | bool isEscaped = false; 84 | foreach(char c in content) 85 | { 86 | if (!isEscaped && c == '\\') 87 | isEscaped = true; 88 | else 89 | builder.Append(c); 90 | } 91 | 92 | if (isEscaped) 93 | throw new ArgumentException(Program.GetResourceString("Commands.Incomplete")); 94 | 95 | return builder.ToString(); 96 | } 97 | 98 | public static string Win32ErrorToMessage(int code) 99 | { 100 | return Program.GetResourceString("Commands.Win32Error", code, new System.ComponentModel.Win32Exception(code).Message); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /PEDollController/Commands/cmdDoll.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Mono.Options; 5 | 6 | namespace PEDollController.Commands 7 | { 8 | 9 | // Command "doll": Creates a new Doll client. 10 | // doll CMDLINE 11 | // doll --attach PID 12 | 13 | class CmdDoll : ICommand 14 | { 15 | public string HelpResId() => "Commands.Help.Doll"; 16 | public string HelpShortResId() => "Commands.HelpShort.Doll"; 17 | 18 | public Dictionary Parse(string cmd) 19 | { 20 | bool attach = false; 21 | string extras; 22 | 23 | OptionSet options = new OptionSet() 24 | { 25 | { "attach", x => attach = (x != null) } 26 | }; 27 | extras = String.Join(" ", Util.ParseOptions(cmd, options)); 28 | 29 | string cmdline = null; 30 | int pid = 0; 31 | 32 | if(attach) 33 | { 34 | try 35 | { 36 | pid = Convert.ToInt32(extras); 37 | } 38 | catch(Exception e) 39 | { 40 | throw new ArgumentException(e.Message); 41 | } 42 | } 43 | else 44 | { 45 | cmdline = extras; 46 | } 47 | 48 | return new Dictionary() 49 | { 50 | { "verb", "doll" }, 51 | { "attach", attach }, 52 | { "cmdline", cmdline }, 53 | { "pid", pid } 54 | }; 55 | } 56 | 57 | public void Invoke(Dictionary options) 58 | { 59 | Threads.Client client = Threads.CmdEngine.theInstance.GetTargetClient(true); 60 | 61 | // Send CMD_DOLL 62 | Puppet.PACKET_CMD_DOLL pktDoll = new Puppet.PACKET_CMD_DOLL(0); 63 | pktDoll.pid = (UInt32)(int)options["pid"]; 64 | client.Send(Puppet.Util.Serialize(pktDoll)); 65 | 66 | if(!(bool)options["attach"]) 67 | client.Send(Puppet.Util.SerializeString((string)options["cmdline"])); 68 | 69 | // Expect ACK(0) 70 | Puppet.PACKET_ACK pktAck; 71 | pktAck = Puppet.Util.Deserialize(client.Expect(Puppet.PACKET_TYPE.ACK)); 72 | if (pktAck.status != 0) 73 | throw new ArgumentException(Util.Win32ErrorToMessage((int)pktAck.status)); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /PEDollController/FDlgAbout.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace PEDollController 2 | { 3 | partial class FDlgAbout 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FDlgAbout)); 32 | this.picIcon = new System.Windows.Forms.PictureBox(); 33 | this.lblBanner = new System.Windows.Forms.Label(); 34 | this.lblLine1 = new System.Windows.Forms.Label(); 35 | this.lblLine2 = new System.Windows.Forms.Label(); 36 | this.txtCredits = new System.Windows.Forms.TextBox(); 37 | this.btnOK = new System.Windows.Forms.Button(); 38 | ((System.ComponentModel.ISupportInitialize)(this.picIcon)).BeginInit(); 39 | this.SuspendLayout(); 40 | // 41 | // picIcon 42 | // 43 | resources.ApplyResources(this.picIcon, "picIcon"); 44 | this.picIcon.Image = global::PEDollController.Properties.Resources.Controller; 45 | this.picIcon.Name = "picIcon"; 46 | this.picIcon.TabStop = false; 47 | // 48 | // lblBanner 49 | // 50 | resources.ApplyResources(this.lblBanner, "lblBanner"); 51 | this.lblBanner.Name = "lblBanner"; 52 | // 53 | // lblLine1 54 | // 55 | resources.ApplyResources(this.lblLine1, "lblLine1"); 56 | this.lblLine1.Name = "lblLine1"; 57 | // 58 | // lblLine2 59 | // 60 | resources.ApplyResources(this.lblLine2, "lblLine2"); 61 | this.lblLine2.Name = "lblLine2"; 62 | // 63 | // txtCredits 64 | // 65 | resources.ApplyResources(this.txtCredits, "txtCredits"); 66 | this.txtCredits.Name = "txtCredits"; 67 | this.txtCredits.ReadOnly = true; 68 | // 69 | // btnOK 70 | // 71 | resources.ApplyResources(this.btnOK, "btnOK"); 72 | this.btnOK.Name = "btnOK"; 73 | this.btnOK.UseVisualStyleBackColor = true; 74 | this.btnOK.Click += new System.EventHandler(this.btnOK_Click); 75 | // 76 | // FDlgAbout 77 | // 78 | this.AcceptButton = this.btnOK; 79 | resources.ApplyResources(this, "$this"); 80 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 81 | this.Controls.Add(this.btnOK); 82 | this.Controls.Add(this.txtCredits); 83 | this.Controls.Add(this.lblLine2); 84 | this.Controls.Add(this.lblLine1); 85 | this.Controls.Add(this.lblBanner); 86 | this.Controls.Add(this.picIcon); 87 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; 88 | this.Name = "FDlgAbout"; 89 | this.ShowInTaskbar = false; 90 | ((System.ComponentModel.ISupportInitialize)(this.picIcon)).EndInit(); 91 | this.ResumeLayout(false); 92 | this.PerformLayout(); 93 | 94 | } 95 | 96 | #endregion 97 | 98 | private System.Windows.Forms.PictureBox picIcon; 99 | private System.Windows.Forms.Label lblBanner; 100 | private System.Windows.Forms.Label lblLine1; 101 | private System.Windows.Forms.Label lblLine2; 102 | private System.Windows.Forms.TextBox txtCredits; 103 | private System.Windows.Forms.Button btnOK; 104 | } 105 | } -------------------------------------------------------------------------------- /PEDollController/FDlgAbout.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace PEDollController 5 | { 6 | public partial class FDlgAbout : Form 7 | { 8 | public FDlgAbout() 9 | { 10 | InitializeComponent(); 11 | lblBanner.Text = Program.GetResourceString("UI.Gui.Banner", Program.GetVersionString()); 12 | } 13 | 14 | private void btnOK_Click(object sender, EventArgs e) 15 | { 16 | this.Hide(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /PEDollController/FDlgAbout.zh-CN.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | 216, 20 123 | 124 | 125 | Win32/Win64 程序行为分析器 126 | 127 | 128 | 关于 PEDoll 129 | 130 | -------------------------------------------------------------------------------- /PEDollController/FDlgBrowsePID.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace PEDollController 2 | { 3 | partial class FDlgBrowsePID 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FDlgBrowsePID)); 32 | this.lstPs = new System.Windows.Forms.ListView(); 33 | this.clmPID = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); 34 | this.clmName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); 35 | this.btnCancel = new System.Windows.Forms.Button(); 36 | this.btnOK = new System.Windows.Forms.Button(); 37 | this.SuspendLayout(); 38 | // 39 | // lstPs 40 | // 41 | resources.ApplyResources(this.lstPs, "lstPs"); 42 | this.lstPs.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { 43 | this.clmPID, 44 | this.clmName}); 45 | this.lstPs.FullRowSelect = true; 46 | this.lstPs.GridLines = true; 47 | this.lstPs.HideSelection = false; 48 | this.lstPs.MultiSelect = false; 49 | this.lstPs.Name = "lstPs"; 50 | this.lstPs.UseCompatibleStateImageBehavior = false; 51 | this.lstPs.View = System.Windows.Forms.View.Details; 52 | // 53 | // clmPID 54 | // 55 | resources.ApplyResources(this.clmPID, "clmPID"); 56 | // 57 | // clmName 58 | // 59 | resources.ApplyResources(this.clmName, "clmName"); 60 | // 61 | // btnCancel 62 | // 63 | resources.ApplyResources(this.btnCancel, "btnCancel"); 64 | this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; 65 | this.btnCancel.Name = "btnCancel"; 66 | this.btnCancel.UseVisualStyleBackColor = true; 67 | this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); 68 | // 69 | // btnOK 70 | // 71 | resources.ApplyResources(this.btnOK, "btnOK"); 72 | this.btnOK.Name = "btnOK"; 73 | this.btnOK.UseVisualStyleBackColor = true; 74 | this.btnOK.Click += new System.EventHandler(this.btnOK_Click); 75 | // 76 | // FDlgBrowsePID 77 | // 78 | this.AcceptButton = this.btnOK; 79 | resources.ApplyResources(this, "$this"); 80 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 81 | this.CancelButton = this.btnCancel; 82 | this.Controls.Add(this.btnOK); 83 | this.Controls.Add(this.btnCancel); 84 | this.Controls.Add(this.lstPs); 85 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; 86 | this.Name = "FDlgBrowsePID"; 87 | this.ShowInTaskbar = false; 88 | this.VisibleChanged += new System.EventHandler(this.FDlgBrowsePID_VisibleChanged); 89 | this.ResumeLayout(false); 90 | 91 | } 92 | 93 | #endregion 94 | 95 | internal System.Windows.Forms.ListView lstPs; 96 | private System.Windows.Forms.ColumnHeader clmPID; 97 | private System.Windows.Forms.ColumnHeader clmName; 98 | private System.Windows.Forms.Button btnCancel; 99 | private System.Windows.Forms.Button btnOK; 100 | } 101 | } -------------------------------------------------------------------------------- /PEDollController/FDlgBrowsePID.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace PEDollController 5 | { 6 | public partial class FDlgBrowsePID : Form 7 | { 8 | public int Result; 9 | 10 | public FDlgBrowsePID() 11 | { 12 | InitializeComponent(); 13 | } 14 | 15 | private void FDlgBrowsePID_VisibleChanged(object sender, EventArgs e) 16 | { 17 | // Ignore the dialog hiding 18 | if (!this.Visible) 19 | return; 20 | 21 | // -1 means user cancelled the selection 22 | Result = -1; 23 | 24 | // Wait for `ps` to initialize the list 25 | this.UseWaitCursor = true; 26 | Threads.CmdEngine.theInstance.AddCommand("ps"); 27 | } 28 | 29 | private void btnOK_Click(object sender, EventArgs e) 30 | { 31 | this.Result = Convert.ToInt32(lstPs.SelectedItems[0].Text); 32 | this.Hide(); 33 | } 34 | 35 | private void btnCancel_Click(object sender, EventArgs e) 36 | { 37 | this.Hide(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /PEDollController/FDlgBrowsePID.zh-CN.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 进程名 122 | 123 | 124 | 取消(&C) 125 | 126 | 127 | 确定(&O) 128 | 129 | 130 | 选择一个PID 131 | 132 | -------------------------------------------------------------------------------- /PEDollController/FSplash.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace PEDollController 2 | { 3 | partial class FSplash 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.components = new System.ComponentModel.Container(); 32 | this.tmrClose = new System.Windows.Forms.Timer(this.components); 33 | this.picSplash = new System.Windows.Forms.PictureBox(); 34 | this.lblBanner = new System.Windows.Forms.Label(); 35 | this.lblCopyright = new System.Windows.Forms.Label(); 36 | ((System.ComponentModel.ISupportInitialize)(this.picSplash)).BeginInit(); 37 | this.SuspendLayout(); 38 | // 39 | // tmrClose 40 | // 41 | this.tmrClose.Enabled = true; 42 | this.tmrClose.Interval = 1200; 43 | this.tmrClose.Tick += new System.EventHandler(this.tmrClose_Tick); 44 | // 45 | // picSplash 46 | // 47 | this.picSplash.Image = global::PEDollController.Properties.Resources.Splash; 48 | this.picSplash.Location = new System.Drawing.Point(0, 0); 49 | this.picSplash.Name = "picSplash"; 50 | this.picSplash.Size = new System.Drawing.Size(640, 260); 51 | this.picSplash.TabIndex = 0; 52 | this.picSplash.TabStop = false; 53 | this.picSplash.UseWaitCursor = true; 54 | // 55 | // lblBanner 56 | // 57 | this.lblBanner.AutoSize = true; 58 | this.lblBanner.Font = new System.Drawing.Font("微软雅黑", 16.2F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); 59 | this.lblBanner.ForeColor = System.Drawing.Color.White; 60 | this.lblBanner.Location = new System.Drawing.Point(12, 263); 61 | this.lblBanner.Name = "lblBanner"; 62 | this.lblBanner.Size = new System.Drawing.Size(284, 37); 63 | this.lblBanner.TabIndex = 1; 64 | this.lblBanner.Text = "PEDoll v1.0.0 Indev"; 65 | this.lblBanner.UseWaitCursor = true; 66 | // 67 | // lblCopyright 68 | // 69 | this.lblCopyright.AutoSize = true; 70 | this.lblCopyright.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); 71 | this.lblCopyright.ForeColor = System.Drawing.Color.White; 72 | this.lblCopyright.Location = new System.Drawing.Point(16, 300); 73 | this.lblCopyright.Name = "lblCopyright"; 74 | this.lblCopyright.Size = new System.Drawing.Size(362, 20); 75 | this.lblCopyright.TabIndex = 2; 76 | this.lblCopyright.Text = "Copyright © 2020 EZForever. All rights reserved."; 77 | this.lblCopyright.UseWaitCursor = true; 78 | // 79 | // FSplash 80 | // 81 | this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F); 82 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 83 | this.BackColor = System.Drawing.Color.Black; 84 | this.ClientSize = new System.Drawing.Size(640, 335); 85 | this.ControlBox = false; 86 | this.Controls.Add(this.lblCopyright); 87 | this.Controls.Add(this.lblBanner); 88 | this.Controls.Add(this.picSplash); 89 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; 90 | this.Name = "FSplash"; 91 | this.ShowInTaskbar = false; 92 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 93 | this.Text = "FSplash"; 94 | this.UseWaitCursor = true; 95 | ((System.ComponentModel.ISupportInitialize)(this.picSplash)).EndInit(); 96 | this.ResumeLayout(false); 97 | this.PerformLayout(); 98 | 99 | } 100 | 101 | #endregion 102 | 103 | private System.Windows.Forms.Timer tmrClose; 104 | private System.Windows.Forms.PictureBox picSplash; 105 | private System.Windows.Forms.Label lblBanner; 106 | private System.Windows.Forms.Label lblCopyright; 107 | } 108 | } -------------------------------------------------------------------------------- /PEDollController/FSplash.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace PEDollController 5 | { 6 | public partial class FSplash : Form 7 | { 8 | public FSplash() 9 | { 10 | InitializeComponent(); 11 | lblBanner.Text = Program.GetResourceString("UI.Gui.Banner", Program.GetVersionString()); 12 | } 13 | 14 | private void tmrClose_Tick(object sender, EventArgs e) 15 | { 16 | this.Close(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /PEDollController/FSplash.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 17, 17 122 | 123 | 124 | True 125 | 126 | 127 | True 128 | 129 | 130 | True 131 | 132 | 133 | True 134 | 135 | -------------------------------------------------------------------------------- /PEDollController/Logger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PEDollController 4 | { 5 | static class Logger 6 | { 7 | // Info, Notice, Highlight, Warning, Error 8 | static readonly ConsoleColor colorI = ConsoleColor.Gray; 9 | static readonly ConsoleColor colorN = ConsoleColor.Green; 10 | static readonly ConsoleColor colorH = ConsoleColor.White; 11 | static readonly ConsoleColor colorW = ConsoleColor.Yellow; 12 | static readonly ConsoleColor colorE = ConsoleColor.Red; 13 | 14 | public static void Write(ConsoleColor color, string msg, object[] args = null) 15 | { 16 | Console.ForegroundColor = color; 17 | if (args == null) 18 | Console.WriteLine(msg); 19 | else 20 | Console.WriteLine(msg, args); 21 | Console.ResetColor(); 22 | } 23 | 24 | public static void I(string msg) => Write(colorI, msg); 25 | public static void I(string msg, params object[] args) => Write(colorI, msg, args); 26 | 27 | public static void N(string msg) => Write(colorN, msg); 28 | public static void N(string msg, params object[] args) => Write(colorN, msg, args); 29 | 30 | public static void H(string msg) => Write(colorH, msg); 31 | public static void H(string msg, params object[] args) => Write(colorH, msg, args); 32 | 33 | public static void W(string msg) => Write(colorW, msg); 34 | public static void W(string msg, params object[] args) => Write(colorW, msg, args); 35 | 36 | public static void E(string msg) => Write(colorE, msg); 37 | public static void E(string msg, params object[] args) => Write(colorE, msg, args); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /PEDollController/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Reflection; 4 | using System.Windows.Forms; 5 | using System.Collections.Generic; 6 | 7 | using PEDollController.Threads; 8 | 9 | namespace PEDollController 10 | { 11 | static class Program 12 | { 13 | #region GetResourceString() & GetVersionString() 14 | 15 | public static string GetResourceString(string resId) 16 | { 17 | string ret = Properties.Resources.ResourceManager.GetString(resId); 18 | if (ret == null) 19 | ret = String.Format("[ Missing \"{0}\" ]", resId); 20 | return ret; 21 | } 22 | 23 | public static string GetResourceString(string resId, params object[] args) 24 | { 25 | return String.Format(GetResourceString(resId), args); 26 | } 27 | 28 | public static string GetVersionString() 29 | { 30 | Version ver = Assembly.GetExecutingAssembly().GetName().Version; 31 | 32 | // For versions between release tags, a "*" is appended after the version string 33 | string ret = ver.ToString(3); 34 | if (ver.Revision != 0) 35 | ret += "*"; 36 | return ret; 37 | } 38 | 39 | #endregion 40 | 41 | #region TabPage.My{Show|Hide}() 42 | 43 | // NOTE: Always MyHide() before MyShow()! Or the indexes recorded will be messed up 44 | 45 | // > 46 | static Dictionary> tabLounge = new Dictionary>(); 47 | 48 | public static void MyShow(this TabPage page) 49 | { 50 | if (!tabLounge.ContainsKey(page)) 51 | return; 52 | //throw new InvalidOperationException(); 53 | 54 | TabControl parent = tabLounge[page].Item1; 55 | parent.TabPages.Insert(tabLounge[page].Item2, page); 56 | tabLounge.Remove(page); 57 | } 58 | 59 | public static void MyHide(this TabPage page) 60 | { 61 | TabControl parent = page.Parent as TabControl; 62 | if (parent == null) 63 | return; 64 | //throw new InvalidOperationException(); 65 | 66 | tabLounge.Add(page, new Tuple(parent, parent.TabPages.IndexOf(page))); 67 | parent.TabPages.Remove(page); 68 | } 69 | 70 | #endregion 71 | 72 | public static event Action OnProgramEnd; 73 | 74 | //[STAThread] 75 | static void Main() 76 | { 77 | Console.ResetColor(); 78 | Logger.H(GetResourceString("UI.Cli.Banner", GetVersionString())); 79 | 80 | // Initialize CmdEngine, which receives and processes user commands 81 | CmdEngine.theTask.Start(); 82 | 83 | // Initialize GUI 84 | // NOTE: In order to let OLE dialogs able to work, GUI must run on a dedicated STA thread 85 | Gui.theThread.SetApartmentState(ApartmentState.STA); 86 | Gui.theThread.Start(); 87 | 88 | // Wait for CmdEngine to finish 89 | CmdEngine.theTask.Wait(); 90 | 91 | // Then finialize anything 92 | OnProgramEnd(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /PEDollController/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("PEDollController")] 9 | [assembly: AssemblyDescription("Program behavior analyzer for Win32/Win64")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("PEDoll")] 13 | [assembly: AssemblyCopyright("Copyright (C) 2020 EZForever. All rights reserved.")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("eeec10f7-451a-43af-b230-dae937660eaf")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.1.0")] 36 | [assembly: AssemblyFileVersion("1.0.1.0")] 37 | -------------------------------------------------------------------------------- /PEDollController/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace PEDollController.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.4.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /PEDollController/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PEDollController/Puppet/Util.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace PEDollController.Puppet 6 | { 7 | 8 | static class Util 9 | { 10 | // Credit of (S|Des)erialize: https://stackoverflow.com/a/3278956 11 | 12 | public static readonly int DEFAULT_PORT = 31415; 13 | 14 | public static byte[] Serialize(T obj) 15 | { 16 | int size = Marshal.SizeOf(typeof(T)); 17 | byte[] data = new byte[size]; 18 | 19 | IntPtr pData = Marshal.AllocHGlobal(size); 20 | Marshal.StructureToPtr(obj, pData, true); 21 | Marshal.Copy(pData, data, 0, size); 22 | Marshal.FreeHGlobal(pData); 23 | 24 | return data; 25 | } 26 | 27 | public static byte[] SerializeString(string str) 28 | { 29 | PACKET_STRING obj = new PACKET_STRING(0); 30 | int strSize = sizeof(char) * (str.Length + 1); 31 | obj.header.size += (UInt32)strSize; 32 | 33 | byte[] pktHeader = Serialize(obj); 34 | byte[] strBytes = Encoding.Unicode.GetBytes(str); 35 | 36 | byte[] data = new byte[obj.header.size]; 37 | pktHeader.CopyTo(data, 0); 38 | strBytes.CopyTo(data, pktHeader.Length); 39 | return data; 40 | } 41 | 42 | public static byte[] SerializeBinary(byte[] bin) 43 | { 44 | PACKET_BINARY obj = new PACKET_BINARY(0); 45 | obj.header.size += (UInt32)bin.Length; 46 | byte[] pktHeader = Serialize(obj); 47 | byte[] data = new byte[obj.header.size]; 48 | 49 | pktHeader.CopyTo(data, 0); 50 | bin.CopyTo(data, pktHeader.Length); 51 | return data; 52 | } 53 | 54 | public static T Deserialize(byte[] data) 55 | { 56 | int size = Marshal.SizeOf(typeof(T)); 57 | T obj; 58 | 59 | IntPtr pData = Marshal.AllocHGlobal(size); 60 | Marshal.Copy(data, 0, pData, size); 61 | obj = (T)Marshal.PtrToStructure(pData, typeof(T)); 62 | Marshal.FreeHGlobal(pData); 63 | 64 | return obj; 65 | } 66 | 67 | public static string DeserializeString(byte[] data) 68 | { 69 | int offset = Marshal.SizeOf(typeof(PACKET_STRING)); 70 | return Encoding.Unicode.GetString(data, offset, data.Length - offset - 2); // remove L'\0' 71 | } 72 | 73 | public static byte[] DeserializeBinary(byte[] data) 74 | { 75 | int size = data.Length - Marshal.SizeOf(typeof(PACKET_BINARY)); 76 | byte[] bin = new byte[size]; 77 | Array.Copy(data, Marshal.SizeOf(typeof(PACKET_BINARY)), bin, 0, size); 78 | return bin; 79 | } 80 | 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /PEDollController/Resources/CREDITS.txt: -------------------------------------------------------------------------------- 1 | *** Controller icon credit *** 2 | "puppet" icon by HeadsOfBirds from the Noun Project (https://thenounproject.com/). CC BY 3.0. 3 | 4 | *** Splash image credit *** 5 | Copyright © 2016 DBinary from 52pojie (https://52pojie.cn/). Used with permission. 6 | 7 | *** Open source libraries' licenses *** 8 | Detours (the MIT License): 9 | https://github.com/microsoft/Detours/blob/master/LICENSE.md 10 | 11 | Mono.Options (the MIT License): 12 | https://github.com/xamarin/XamarinComponents/blob/master/XPlat/Mono.Options/License.md 13 | 14 | Capstone.NET (the MIT License): 15 | https://github.com/9ee1/Capstone.NET/blob/master/LICENSE 16 | 17 | *** PEDoll license (the MIT License) *** 18 | Copyright © 2020 Eric Zhang (EZForever), https://github.com/EZForever 19 | 20 | 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: 21 | 22 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 23 | 24 | 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. 25 | 26 | -------------------------------------------------------------------------------- /PEDollController/Resources/Controller.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EZForever/PEDoll/893c853d6bda6614b72077707928ead73e6f78df/PEDollController/Resources/Controller.ico -------------------------------------------------------------------------------- /PEDollController/Resources/Controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EZForever/PEDoll/893c853d6bda6614b72077707928ead73e6f78df/PEDollController/Resources/Controller.png -------------------------------------------------------------------------------- /PEDollController/Resources/Controller.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /PEDollController/Resources/Splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EZForever/PEDoll/893c853d6bda6614b72077707928ead73e6f78df/PEDollController/Resources/Splash.png -------------------------------------------------------------------------------- /PEDollController/Threads/AsyncDataProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Collections.Generic; 4 | 5 | namespace PEDollController.Threads 6 | { 7 | 8 | // AsyncDataProvider converts any infinite blocking data fetch operation into a series of await-able Tasks 9 | // Idea from: https://stackoverflow.com/a/54443866 10 | 11 | /* 12 | * Usage example: 13 | * AsyncDataProvider provider = new AsyncDataProvider(Console.ReadLine); 14 | * ... 15 | * while(true) 16 | * { 17 | * Task task = provider.Get(); 18 | * int idx = Task.WaitAny(task, ...); 19 | * 20 | * if(idx == 0) 21 | * { 22 | * string line = task.Result; 23 | * ... 24 | * } 25 | * else 26 | * { 27 | * .... 28 | * } 29 | * } 30 | */ 31 | 32 | class AsyncDataProvider 33 | { 34 | 35 | IEnumerator> getResultEnumerator; 36 | 37 | static IEnumerator> LoopEnumerator(Func func) 38 | { 39 | while (true) 40 | { 41 | Task task = new Task(func); 42 | task.Start(); 43 | yield return task; 44 | } 45 | } 46 | 47 | public AsyncDataProvider(Func getResult) 48 | { 49 | getResultEnumerator = LoopEnumerator(getResult); 50 | getResultEnumerator.MoveNext(); 51 | } 52 | 53 | public Task Get() 54 | { 55 | if (getResultEnumerator.Current.IsCompleted) 56 | getResultEnumerator.MoveNext(); 57 | 58 | return getResultEnumerator.Current; 59 | } 60 | 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /PEDollController/Threads/BlockingQueue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Collections.Generic; 4 | 5 | namespace PEDollController.Threads 6 | { 7 | 8 | // BlockingQueue provides blocking version of Enqueue() & Dequeue(), implementing basic thread safety 9 | // Idea from: https://stackoverflow.com/a/530228 10 | 11 | static class BlockingQueue 12 | { 13 | public static void BlockingEnqueue(this Queue queue, T item) 14 | { 15 | lock(queue) 16 | { 17 | queue.Enqueue(item); 18 | // If just recovered from empty state, send a pulse to the blocked GetCommand() thread 19 | if (queue.Count == 1) 20 | Monitor.PulseAll(queue); 21 | } 22 | } 23 | 24 | public static T BlockingDequeue(this Queue queue) 25 | { 26 | lock(queue) 27 | { 28 | if (queue.Count == 0) 29 | Monitor.Wait(queue); 30 | return queue.Dequeue(); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /PEDollController/Threads/CmdEngine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Threading; 4 | using System.Windows.Forms; 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | 8 | namespace PEDollController.Threads 9 | { 10 | struct DumpEntry 11 | { 12 | public string Source; 13 | public byte[] Data; 14 | } 15 | 16 | class CmdEngine 17 | { 18 | public static CmdEngine theInstance = new CmdEngine(); 19 | public static Task theTask = new Task(theInstance.TaskMain); 20 | 21 | // ---------- 22 | 23 | Queue cmdQueue; 24 | AsyncDataProvider cmdProvider; 25 | 26 | // Stop engine & all tasks created by CmdEngine (Listener, Client*) 27 | public ManualResetEvent stopTaskEvent; 28 | public Task stopTaskAsync; 29 | 30 | public int target = -1; 31 | public int targetLastDoll = -1; 32 | public int targetLastMonitor = -1; 33 | 34 | public List dumps; 35 | 36 | CmdEngine() 37 | { 38 | dumps = new List(); 39 | cmdQueue = new Queue(); 40 | cmdProvider = new AsyncDataProvider(cmdQueue.BlockingDequeue); 41 | 42 | stopTaskEvent = new ManualResetEvent(false); 43 | stopTaskAsync = new Task(() => stopTaskEvent.WaitOne()); 44 | stopTaskAsync.Start(); 45 | 46 | // Register to Program's stop event 47 | Program.OnProgramEnd += Program_OnProgramEnd; 48 | 49 | // Register Ctrl-C event, so that when we received a Ctrl-C from the console, we stop the engine 50 | Console.CancelKeyPress += Console_CancelKeyPress; 51 | } 52 | 53 | ~CmdEngine() 54 | { 55 | Console.CancelKeyPress -= Console_CancelKeyPress; 56 | Program.OnProgramEnd -= Program_OnProgramEnd; 57 | } 58 | 59 | void TaskMain() 60 | { 61 | while(true) 62 | { 63 | // XXX: Also wait for commands from Console.ReadLine()? 64 | Task taskCmd = cmdProvider.Get(); 65 | int idx = Task.WaitAny(stopTaskAsync, taskCmd); 66 | 67 | if (idx == 0) 68 | { 69 | // stopTaskAsync triggered 70 | break; 71 | } 72 | else 73 | { 74 | string cmd = taskCmd.Result; 75 | 76 | Logger.H(Program.GetResourceString("UI.Cli.Format", cmd)); 77 | OnCommand(cmd); 78 | } 79 | } 80 | } 81 | 82 | void OnCommand(string cmd) 83 | { 84 | try 85 | { 86 | Commands.Util.Invoke(cmd); 87 | } 88 | catch(ArgumentException e) 89 | { 90 | Logger.E(e.Message); // NOTE: e.ParamName is followed 91 | } 92 | } 93 | 94 | void Program_OnProgramEnd() 95 | { 96 | stopTaskEvent.Set(); 97 | } 98 | 99 | void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs args) 100 | { 101 | // Do not just stop the process 102 | args.Cancel = true; 103 | 104 | Logger.H(Program.GetResourceString("UI.Cli.CtrlC")); 105 | Console.ReadKey(); 106 | 107 | // Tell TaskMain() about ending task 108 | stopTaskEvent.Set(); 109 | } 110 | 111 | public void AddCommand(string cmd) 112 | { 113 | cmdQueue.BlockingEnqueue(cmd); 114 | } 115 | 116 | public Client GetTargetClient() 117 | { 118 | if (target < 0 || Client.theInstances[target].isDead) 119 | throw new ArgumentException(Program.GetResourceString("Threads.CmdEngine.TargetNotAvailable")); 120 | 121 | return Client.theInstances[target]; 122 | } 123 | 124 | public Client GetTargetClient(bool isMonitor) 125 | { 126 | Client client = GetTargetClient(); 127 | if (client.isMonitor != isMonitor) 128 | throw new ArgumentException(Program.GetResourceString("Threads.CmdEngine.TargetNotApplicable")); 129 | 130 | return client; 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /PEDollController/Threads/EvalEngineContext.cs: -------------------------------------------------------------------------------- 1 | // NOTE: This file will not get compiled into the assembly, but embedded into the resources. 2 | // It is meant to be compiled by the EvalEngine at runtime. 3 | 4 | // NOTE: The default C# compiler .NET Framework provides (Microsoft.CSharp.CSharpCodeProvider) only support up to C# 5. 5 | // Any newer features will cause compile errors in this file. 6 | 7 | using System; 8 | using System.Linq; 9 | using System.Collections.Generic; 10 | 11 | #if CLIENT_X64 12 | 13 | // x64 client 14 | using word = System.Int64; 15 | using uword = System.UInt64; 16 | 17 | #else 18 | 19 | // x86 client or in IDE 20 | using word = System.Int32; 21 | using uword = System.UInt32; 22 | 23 | #endif // CLIENT_X64 24 | 25 | namespace EvalEngineScope 26 | { 27 | public class Context 28 | { 29 | // Import delegates 30 | readonly Func _getContext; 31 | readonly Func _str; 32 | readonly Func _wstr; 33 | readonly Func _ctx; 34 | readonly Func _mem; 35 | readonly Func _poi; 36 | readonly Func _arg; 37 | readonly Func _dump; 38 | 39 | // Methods 40 | public string str(uword ptr) { return _str(ptr); } 41 | public string wstr(uword ptr) { return _wstr(ptr); } 42 | public string ctx(string key) { return _ctx(key); } 43 | public byte[] mem(uword ptr, uint len) { return _mem(ptr, len); } 44 | public uword poi(uword ptr) { return (uword)_poi(ptr); } 45 | public uword arg(uint index) { return (uword)_arg(index); } 46 | public int dump(byte[] blob) { return _dump(blob); } 47 | 48 | // Readonly register fields 49 | public uword ax { get { return (uword)_getContext(0); } } 50 | public uword cx { get { return (uword)_getContext(1); } } 51 | public uword dx { get { return (uword)_getContext(2); } } 52 | public uword bx { get { return (uword)_getContext(3); } } 53 | public uword sp { get { return (uword)_getContext(4); } } 54 | public uword bp { get { return (uword)_getContext(5); } } 55 | public uword si { get { return (uword)_getContext(6); } } 56 | public uword di { get { return (uword)_getContext(7); } } 57 | 58 | # if CLIENT_X64 59 | public uword r8 { get { return (uword)_getContext(8); } } 60 | public uword r9 { get { return (uword)_getContext(9); } } 61 | # endif 62 | 63 | public Context(Delegate[] imports) 64 | { 65 | _getContext = (Func)imports[0]; 66 | _str = (Func)imports[1]; 67 | _wstr = (Func)imports[2]; 68 | _ctx = (Func)imports[3]; 69 | _mem = (Func)imports[4]; 70 | _poi = (Func)imports[5]; 71 | _arg = (Func)imports[6]; 72 | _dump = (Func)imports[7]; 73 | } 74 | 75 | public object[] Invoke() 76 | { 77 | return new object[] { /* expr */ }; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /PEDollController/Threads/Gui.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Windows.Forms; 5 | 6 | namespace PEDollController.Threads 7 | { 8 | class Gui 9 | { 10 | public static Gui theInstance = new Gui(); 11 | public static Thread theThread = new Thread(theInstance.TaskMain); 12 | 13 | // ---------- 14 | 15 | FMain winMain; 16 | public FDlgAbout dlgAbout; 17 | public FDlgAddHook dlgAddHook; 18 | public FDlgBrowsePID dlgBrowsePID; 19 | 20 | Gui() 21 | { 22 | // These must happen before the first Form class 23 | Application.EnableVisualStyles(); 24 | Application.SetCompatibleTextRenderingDefault(false); 25 | 26 | Program.OnProgramEnd += Program_OnProgramEnd; 27 | winMain = new FMain(); 28 | dlgAbout = new FDlgAbout(); 29 | dlgAddHook = new FDlgAddHook(); 30 | dlgBrowsePID = new FDlgBrowsePID(); 31 | } 32 | 33 | ~Gui() 34 | { 35 | Program.OnProgramEnd -= Program_OnProgramEnd; 36 | } 37 | 38 | void TaskMain() 39 | { 40 | Application.Run(new FSplash()); 41 | Application.Run(winMain); 42 | } 43 | 44 | void Program_OnProgramEnd() 45 | { 46 | Action
actionCloseForm = new Action((Form Me) => Me.Close()); 47 | 48 | foreach (Form frm in Application.OpenForms.Cast().ToArray()) 49 | frm.Invoke(actionCloseForm, frm); 50 | } 51 | 52 | // Prototype: void InvokeOnProc(T Me) / (T Me) => { ... } 53 | // Threads.Gui.InvokeOn((FMain Me) => { ... }); 54 | // NOTE: Nothing will happen if the selected form is not visible 55 | public void InvokeOn(Action method) where T : Form 56 | { 57 | foreach (T frm in Application.OpenForms.Cast().Where(frm => frm is T)) 58 | frm.Invoke(method, frm); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /PEDollController/Threads/Listener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | using System.Threading.Tasks; 5 | 6 | namespace PEDollController.Threads 7 | { 8 | class Listener 9 | { 10 | // The Listener instance will be created by CmdEngine 11 | public static Listener theInstance; 12 | public static Task theTask; 13 | 14 | public static void CreateInstance(bool ipv6, int port) 15 | { 16 | try 17 | { 18 | theInstance = new Listener(ipv6, port); 19 | theTask = new Task(theInstance.TaskMain); 20 | theTask.Start(); 21 | } 22 | catch 23 | { 24 | theInstance = null; 25 | theTask = null; 26 | throw; 27 | } 28 | } 29 | 30 | // ---------- 31 | 32 | public TcpListener listener; 33 | public bool ipv6; 34 | public int port; 35 | 36 | Listener(bool ipv6, int port) 37 | { 38 | this.ipv6 = ipv6; 39 | this.port = port; 40 | 41 | // IPv6Any without IPv6Only will listen on both v4 & v6 interfaces 42 | listener = new TcpListener(ipv6 ? IPAddress.IPv6Any : IPAddress.Any, port); 43 | listener.Start(); 44 | } 45 | 46 | void TaskMain() 47 | { 48 | while(true) 49 | { 50 | Task taskTcp = listener.AcceptTcpClientAsync(); 51 | int idx = Task.WaitAny(CmdEngine.theInstance.stopTaskAsync, taskTcp); 52 | 53 | if(idx == 0) 54 | { 55 | // stopTaskAsync triggered 56 | listener.Stop(); 57 | break; 58 | } 59 | else 60 | { 61 | Client.CreateInstance(taskTcp.Result); 62 | } 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /PEDollController/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /PEDollController/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /PEDollMonitor/Doll.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "PEDollMonitor.h" 3 | 4 | #include "Doll.h" 5 | #include "../libPuppet/libPuppet.h" 6 | #include "../Detours/repo/src/detours.h" 7 | 8 | uint32_t MonDollAttach(uint32_t pid) 9 | { 10 | uint32_t ret = 0; 11 | 12 | HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 13 | if (!hProc) 14 | { 15 | return GetLastError(); 16 | } 17 | // CreateRemoteThread() requires so many permissions 18 | 19 | size_t libDollPathSize = sizeof(wchar_t) * (wcslen(ctx.libDollPath) + 1); 20 | LPVOID libDollPath = NULL; 21 | HANDLE hTNew = NULL; 22 | if ((libDollPath = VirtualAllocEx(hProc, NULL, libDollPathSize, MEM_COMMIT, PAGE_READWRITE)) 23 | && WriteProcessMemory(hProc, libDollPath, ctx.libDollPath, libDollPathSize, (SIZE_T*)&libDollPathSize) 24 | && DetourCopyPayloadToProcess(hProc, Puppet::PAYLOAD_SERVER_INFO, ctx.serverInfo, (DWORD)(strlen(ctx.serverInfo) + 1)) 25 | && (hTNew = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryW, libDollPath, 0, NULL))) 26 | { 27 | CloseHandle(hTNew); 28 | } 29 | else 30 | { 31 | ret = GetLastError(); 32 | } 33 | 34 | // That VirtualAllocEx()'ed memory region is leaked, but whatever 35 | 36 | CloseHandle(hProc); 37 | return ret; 38 | } 39 | 40 | uint32_t MonDollLaunch(wchar_t* path) 41 | { 42 | uint32_t ret = 0; 43 | 44 | size_t libDollPathSize = wcslen(ctx.libDollPath); 45 | char* libDollPath = new char[libDollPathSize + 1]; 46 | wcstombs_s(&libDollPathSize, libDollPath, libDollPathSize + 1, ctx.libDollPath, libDollPathSize); 47 | 48 | STARTUPINFOW si = { sizeof(STARTUPINFOW) }; 49 | PROCESS_INFORMATION pi = { 0 }; 50 | if (!DetourCreateProcessWithDllEx(NULL, path, NULL, NULL, FALSE, CREATE_SUSPENDED | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &si, &pi, libDollPath, NULL)) 51 | { 52 | ret = GetLastError(); 53 | delete[] libDollPath; 54 | return ret; 55 | } 56 | delete[] libDollPath; 57 | 58 | if (!DetourCopyPayloadToProcess(pi.hProcess, Puppet::PAYLOAD_SERVER_INFO, ctx.serverInfo, (DWORD)(strlen(ctx.serverInfo) + 1))) 59 | { 60 | ret = GetLastError(); 61 | TerminateProcess(pi.hProcess, 9); 62 | } 63 | else 64 | { 65 | ResumeThread(pi.hThread); 66 | } 67 | 68 | CloseHandle(pi.hProcess); 69 | CloseHandle(pi.hThread); 70 | return ret; 71 | } -------------------------------------------------------------------------------- /PEDollMonitor/Doll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "pch.h" 3 | #include "PEDollMonitor.h" 4 | 5 | uint32_t MonDollAttach(uint32_t pid); 6 | 7 | uint32_t MonDollLaunch(wchar_t* path); 8 | 9 | -------------------------------------------------------------------------------- /PEDollMonitor/GetFileVersion.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "GetFileVersion.h" 4 | 5 | BOOL GetFileVersion(HMODULE hModule, std::string& str) 6 | { 7 | char* moduleName = new char[MAX_PATH]; 8 | if (!GetModuleFileNameA(hModule, moduleName, MAX_PATH)) 9 | { 10 | delete[] moduleName; 11 | return FALSE; 12 | } 13 | 14 | DWORD handle; 15 | DWORD verInfoSize = GetFileVersionInfoSizeA(moduleName, &handle); 16 | if (!verInfoSize) 17 | { 18 | delete[] moduleName; 19 | return FALSE; 20 | } 21 | 22 | char* verInfo = new char[verInfoSize]; 23 | if (!GetFileVersionInfoA(moduleName, 0, verInfoSize, verInfo)) 24 | { 25 | delete[] moduleName; 26 | delete[] verInfo; 27 | return FALSE; 28 | } 29 | delete[] moduleName; 30 | 31 | VS_FIXEDFILEINFO* fileInfo; 32 | UINT fileInfoSize = sizeof(VS_FIXEDFILEINFO); 33 | if (!VerQueryValueA(verInfo, "\\", (LPVOID*)&fileInfo, &fileInfoSize)) 34 | { 35 | delete[] verInfo; 36 | return FALSE; 37 | } 38 | 39 | std::stringstream builder; 40 | builder << (fileInfo->dwFileVersionMS >> 16) 41 | << '.' 42 | << (fileInfo->dwFileVersionMS & 0xffff) 43 | << '.' 44 | << (fileInfo->dwFileVersionLS >> 16); 45 | //<< '.' 46 | //<< (fileInfo->dwFileVersionLS & 0xffff); 47 | 48 | // For versions between release tags, a "*" is appended after the version string 49 | if (fileInfo->dwFileVersionLS & 0xffff) 50 | builder << '*'; 51 | 52 | builder >> str; 53 | 54 | delete[] verInfo; 55 | return TRUE; 56 | } -------------------------------------------------------------------------------- /PEDollMonitor/GetFileVersion.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "pch.h" 3 | 4 | // Get the given module's version string in the format of "x.x.x" 5 | BOOL GetFileVersion(HMODULE hModule, std::string &str); 6 | 7 | -------------------------------------------------------------------------------- /PEDollMonitor/PEDollMonitor.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "PEDollMonitor.h" 3 | 4 | #include "../libPuppet/libPuppet.h" 5 | #include "../libPuppet/PuppetClientTCP.h" 6 | #include "SetPrivilege.h" 7 | #include "GetFileVersion.h" 8 | 9 | void __cdecl TPuppet(void*); 10 | 11 | MONITOR_CTX ctx; 12 | 13 | void isr_sigint(int signalId) 14 | { 15 | // FIXME: TerminateThread() will cause huge resource leaks 16 | // Should inform TPuppet 17 | //TerminateThread(ctx.hTPuppet, 0); 18 | 19 | if(ctx.hTPuppet != INVALID_HANDLE_VALUE) 20 | CloseHandle(ctx.hTPuppet); 21 | 22 | if(ctx.puppet) 23 | delete ctx.puppet; 24 | 25 | if(ctx.serverInfo) 26 | delete[] ctx.serverInfo; 27 | 28 | if(ctx.libDollPath) 29 | delete[] ctx.libDollPath; 30 | 31 | exit(signalId == SIGINT ? 0 : 1); 32 | } 33 | 34 | void MonPanic(const char* msg) 35 | { 36 | std::cerr << msg << std::endl; 37 | isr_sigint(SIGTERM); 38 | } 39 | 40 | /* 41 | // stdout is set to multi-byte mode after the first call to std::cout 42 | // Using std::wcout or std::wcerr may cause trouble 43 | void MonPanic(const wchar_t* msg) 44 | { 45 | std::wcerr << msg << std::endl; 46 | isr_sigint(SIGTERM); 47 | } 48 | */ 49 | 50 | int main(int argc, char* argv[]) 51 | { 52 | std::string version; 53 | if (!GetFileVersion(NULL, version)) 54 | version = "???"; 55 | 56 | std::cout << "PEDoll Monitor v" << version << " InDev" << std::endl << std::endl; 57 | 58 | // Pre-initialize all contexts for isr_sigint() 59 | ctx.puppet = NULL; 60 | ctx.hTPuppet = INVALID_HANDLE_VALUE; 61 | ctx.serverInfo = NULL; 62 | ctx.libDollPath = NULL; 63 | 64 | if (!SetPrivilege(GetCurrentProcess(), SE_DEBUG_NAME, TRUE)) 65 | { 66 | std::cerr << "main(): SetPrivilege(SE_DEBUG_NAME, TRUE) failed, GetLastError() = " << GetLastError() << std::endl; 67 | MonPanic("main(): Debug privilege not held"); 68 | } 69 | 70 | ctx.libDollPath = new wchar_t[MAX_PATH]; 71 | wchar_t* pFilePart; 72 | if (!SearchPathW(NULL, L"libDoll.dll", NULL, MAX_PATH, ctx.libDollPath, &pFilePart)) 73 | { 74 | std::cerr << "main(): SearchPathW() failed, GetLastError() = " << GetLastError() << std::endl; 75 | MonPanic("main(): libDoll.dll not found"); 76 | } 77 | // SearchPathW() is, actually, not designed for searching a DLL for LoadLibrary() 78 | // See https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw#security-remarks 79 | 80 | const char* serverInfo = NULL; 81 | std::string serverInfoInput; 82 | if (argc > 1) 83 | { 84 | // PEDollMonitor.exe $serverInfo 85 | serverInfo = argv[1]; 86 | } 87 | else 88 | { 89 | std::cout << "Input connection string [127.0.0.1]: "; 90 | std::getline(std::cin, serverInfoInput); 91 | serverInfo = serverInfoInput.empty() ? "127.0.0.1" : serverInfoInput.c_str(); 92 | } 93 | 94 | size_t serverInfoLen = strlen(serverInfo); 95 | ctx.serverInfo = new char[serverInfoLen + 1]; 96 | strcpy_s(ctx.serverInfo, serverInfoLen + 1, serverInfo); 97 | 98 | uintptr_t hTPuppet = _beginthread(TPuppet, 0, NULL); 99 | if (hTPuppet == 0 || hTPuppet == -1) // these status means error occurred 100 | { 101 | MonPanic("main(): _beginthread(TPuppet) failed"); 102 | } 103 | ctx.hTPuppet = (HANDLE)hTPuppet; 104 | 105 | // Register SIGINT (Ctrl-C) handler 106 | signal(SIGINT, isr_sigint); 107 | 108 | std::cout << "Initialization complete, press Ctrl-C to stop" << std::endl; 109 | 110 | // Suspend current thread 111 | Sleep(INFINITE); 112 | 113 | return 42; // Not reached 114 | } 115 | 116 | -------------------------------------------------------------------------------- /PEDollMonitor/PEDollMonitor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "pch.h" 3 | 4 | #include "../libPuppet/libPuppet.h" 5 | 6 | struct MONITOR_CTX { 7 | Puppet::IPuppet* puppet; 8 | HANDLE hTPuppet; 9 | char* serverInfo; 10 | wchar_t* libDollPath; 11 | }; 12 | 13 | // Called on a fatal error 14 | void MonPanic(const char* msg); 15 | //void MonPanic(const wchar_t* msg); 16 | 17 | // Global context 18 | extern MONITOR_CTX ctx; 19 | 20 | -------------------------------------------------------------------------------- /PEDollMonitor/PEDollMonitor.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EZForever/PEDoll/893c853d6bda6614b72077707928ead73e6f78df/PEDollMonitor/PEDollMonitor.rc -------------------------------------------------------------------------------- /PEDollMonitor/PEDollMonitor.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 源文件 20 | 21 | 22 | 源文件 23 | 24 | 25 | 源文件 26 | 27 | 28 | 源文件 29 | 30 | 31 | 源文件 32 | 33 | 34 | 源文件 35 | 36 | 37 | 源文件 38 | 39 | 40 | 41 | 42 | 头文件 43 | 44 | 45 | 头文件 46 | 47 | 48 | 头文件 49 | 50 | 51 | 头文件 52 | 53 | 54 | 头文件 55 | 56 | 57 | 头文件 58 | 59 | 60 | 头文件 61 | 62 | 63 | 头文件 64 | 65 | 66 | 67 | 68 | 资源文件 69 | 70 | 71 | -------------------------------------------------------------------------------- /PEDollMonitor/Proc.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "PEDollMonitor.h" 3 | 4 | #include "Proc.h" 5 | #include "../libPuppet/libPuppet.h" 6 | 7 | uint32_t MonProcPs(std::vector &entry) 8 | { 9 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 10 | if (hSnapshot == INVALID_HANDLE_VALUE) 11 | return GetLastError(); 12 | 13 | PROCESSENTRY32W procEntry; 14 | procEntry.dwSize = sizeof(PROCESSENTRY32W); 15 | if (!Process32FirstW(hSnapshot, &procEntry)) 16 | { 17 | CloseHandle(hSnapshot); 18 | return GetLastError(); 19 | } 20 | 21 | do { 22 | entry.push_back(new Puppet::PACKET_INTEGER(procEntry.th32ProcessID)); 23 | entry.push_back(Puppet::PacketAllocString(procEntry.szExeFile)); 24 | } while (Process32NextW(hSnapshot, &procEntry)); 25 | 26 | entry.push_back(new Puppet::PACKET_INTEGER(-1)); 27 | 28 | CloseHandle(hSnapshot); 29 | return 0; 30 | } 31 | 32 | uint32_t MonProcShell(wchar_t* args) 33 | { 34 | // Obtain the value of $COMSPEC, and fail if it does not exist 35 | wchar_t* comSpec = new wchar_t[MAX_PATH]; 36 | size_t comSpecSize = MAX_PATH; 37 | if (_wgetenv_s(&comSpecSize, comSpec, comSpecSize, L"COMSPEC")) 38 | { 39 | delete[] comSpec; 40 | return GetLastError(); 41 | } 42 | 43 | STARTUPINFOW si = { sizeof(STARTUPINFOW) }; 44 | PROCESS_INFORMATION pi = { 0 }; 45 | if (CreateProcessW(comSpec, args, NULL, NULL, FALSE, CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &si, &pi)) 46 | { 47 | delete[] comSpec; 48 | CloseHandle(pi.hProcess); 49 | CloseHandle(pi.hThread); 50 | return 0; 51 | } 52 | else 53 | { 54 | delete[] comSpec; 55 | return GetLastError(); 56 | } 57 | } 58 | 59 | uint32_t MonProcKillByPID(uint32_t pid) 60 | { 61 | HANDLE hProc = OpenProcess(PROCESS_TERMINATE, FALSE, pid); 62 | if (!hProc) 63 | return GetLastError(); 64 | 65 | uint32_t ret = 0; 66 | if (!TerminateProcess(hProc, 9)) 67 | ret = GetLastError(); 68 | 69 | CloseHandle(hProc); 70 | return ret; 71 | } 72 | 73 | uint32_t MonProcKillByName(wchar_t* name) 74 | { 75 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 76 | if (hSnapshot == INVALID_HANDLE_VALUE) 77 | return GetLastError(); 78 | 79 | uint32_t ret = 0; 80 | PROCESSENTRY32W procEntry; 81 | procEntry.dwSize = sizeof(PROCESSENTRY32W); 82 | if (!Process32FirstW(hSnapshot, &procEntry)) 83 | { 84 | ret = GetLastError(); 85 | CloseHandle(hSnapshot); 86 | return ret; 87 | } 88 | 89 | do { 90 | if (_wcsicmp(procEntry.szExeFile, name)) 91 | continue; 92 | 93 | ret = MonProcKillByPID(procEntry.th32ProcessID); 94 | if (ret) 95 | break; 96 | } while (Process32NextW(hSnapshot, &procEntry)); 97 | 98 | CloseHandle(hSnapshot); 99 | return ret; 100 | } -------------------------------------------------------------------------------- /PEDollMonitor/Proc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "pch.h" 3 | #include "PEDollMonitor.h" 4 | 5 | // Implements CMD_PS, lists all processes running into `entry` 6 | uint32_t MonProcPs(std::vector& entry); 7 | 8 | // Implements CMD_SHELL, executes "$COMSPEC $args" 9 | // Returns 0 on success, or GetLastError() values 10 | uint32_t MonProcShell(wchar_t* args); 11 | 12 | // Implements CMD_KILL on a PID 13 | uint32_t MonProcKillByPID(uint32_t pid); 14 | 15 | // Implements CMD_KILL on a process name 16 | uint32_t MonProcKillByName(wchar_t* name); 17 | -------------------------------------------------------------------------------- /PEDollMonitor/SetPrivilege.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "SetPrivilege.h" 4 | 5 | // Modified from https://docs.microsoft.com/en-us/windows/win32/secauthz/enabling-and-disabling-privileges-in-c-- 6 | // CC BY 4.0 7 | 8 | BOOL SetPrivilege(HANDLE hProcess, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) 9 | { 10 | HANDLE hToken; 11 | if (!OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken)) 12 | return FALSE; 13 | 14 | LUID luid; 15 | if (!LookupPrivilegeValue(NULL, lpszPrivilege, &luid)) 16 | { 17 | CloseHandle(hToken); 18 | return FALSE; 19 | } 20 | 21 | TOKEN_PRIVILEGES tp; 22 | tp.PrivilegeCount = 1; 23 | tp.Privileges[0].Luid = luid; 24 | tp.Privileges[0].Attributes = bEnablePrivilege ? SE_PRIVILEGE_ENABLED : 0; 25 | 26 | if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) 27 | { 28 | CloseHandle(hToken); 29 | return FALSE; 30 | } 31 | 32 | CloseHandle(hToken); 33 | return TRUE; 34 | } -------------------------------------------------------------------------------- /PEDollMonitor/SetPrivilege.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "pch.h" 3 | 4 | // Enable or disable the given privilege on a process 5 | BOOL SetPrivilege(HANDLE hProcess, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege); 6 | 7 | -------------------------------------------------------------------------------- /PEDollMonitor/ThreadPuppet.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "PEDollMonitor.h" 3 | 4 | #include "../libPuppet/libPuppet.h" 5 | #include "../libPuppet/PuppetClientTCP.h" 6 | #include "Proc.h" 7 | #include "Doll.h" 8 | 9 | // These are directly copied from libDoll/ThreadPuppet.cpp 10 | 11 | inline void TPuppetSendAck(uint32_t status) 12 | { 13 | ctx.puppet->send(Puppet::PACKET_ACK(status)); 14 | } 15 | 16 | inline void TPuppetSendInteger(uint64_t data) 17 | { 18 | ctx.puppet->send(Puppet::PACKET_INTEGER(data)); 19 | } 20 | 21 | template 22 | inline TPacket* TPuppetExpect() 23 | { 24 | Puppet::PACKET* packet = ctx.puppet->recv(); 25 | if (packet->type != type) 26 | throw std::runtime_error("TPuppetExpect(): Packet type mismatch"); 27 | return (TPacket*)packet; 28 | } 29 | 30 | void TPuppetOnRecv(Puppet::PACKET* packet) 31 | { 32 | switch (packet->type) 33 | { 34 | case Puppet::PACKET_TYPE::CMD_END: 35 | { 36 | TPuppetSendAck(0); 37 | MonPanic("TPuppetOnRecv(): CMD_END received"); 38 | break; 39 | } 40 | case Puppet::PACKET_TYPE::CMD_DOLL: 41 | { 42 | uint32_t pid = ((Puppet::PACKET_CMD_DOLL*)packet)->pid; 43 | uint32_t ret; 44 | if (pid) 45 | { 46 | ret = MonDollAttach(pid); 47 | } 48 | else 49 | { 50 | auto pktString = TPuppetExpect(); 51 | ret = MonDollLaunch(pktString->data); 52 | Puppet::PacketFree(pktString); 53 | } 54 | TPuppetSendAck(ret); 55 | break; 56 | } 57 | case Puppet::PACKET_TYPE::CMD_PS: 58 | { 59 | std::vector entry; 60 | TPuppetSendAck(MonProcPs(entry)); 61 | for (auto iter = entry.cbegin(); iter != entry.cend(); iter++) 62 | { 63 | ctx.puppet->send(**iter); 64 | Puppet::PacketFree(*iter); 65 | } 66 | break; 67 | } 68 | case Puppet::PACKET_TYPE::CMD_SHELL: 69 | { 70 | auto pktString = TPuppetExpect(); 71 | TPuppetSendAck(MonProcShell(pktString->data)); 72 | Puppet::PacketFree(pktString); 73 | break; 74 | } 75 | case Puppet::PACKET_TYPE::CMD_KILL: 76 | { 77 | uint32_t pid = ((Puppet::PACKET_CMD_KILL*)packet)->pid; 78 | uint32_t ret; 79 | if (pid) 80 | { 81 | ret = MonProcKillByPID(pid); 82 | } 83 | else 84 | { 85 | auto pktString = TPuppetExpect(); 86 | ret = MonProcKillByName(pktString->data); 87 | Puppet::PacketFree(pktString); 88 | } 89 | TPuppetSendAck(ret); 90 | break; 91 | } 92 | default: 93 | // A unknown packet to Doll being sent 94 | TPuppetSendAck(-1); 95 | } 96 | } 97 | 98 | void __cdecl TPuppet(void* arg) 99 | { 100 | // Initialize ctx.puppet 101 | try { 102 | ctx.puppet = Puppet::ClientTCPInitialize(ctx.serverInfo); 103 | } 104 | catch (const std::runtime_error & e) { 105 | MonPanic(e.what()); 106 | } 107 | if (!ctx.puppet) 108 | MonPanic("TPuppet(): ClientTCPInitialize() failed"); 109 | 110 | // Prepare MSG_ONLINE packet & current process name 111 | Puppet::PACKET_MSG_ONLINE packetOnline; 112 | packetOnline.isMonitor = 1; 113 | packetOnline.bits = sizeof(UINT_PTR) * 8; 114 | packetOnline.pid = GetCurrentProcessId(); 115 | 116 | DWORD baseNameSize = MAX_COMPUTERNAME_LENGTH + 1; 117 | wchar_t* baseName = new wchar_t[baseNameSize]; 118 | GetComputerNameW(baseName, &baseNameSize); 119 | Puppet::PACKET_STRING* packetString = Puppet::PacketAllocString(baseName); 120 | delete[] baseName; 121 | 122 | // Send the packet & wait for reply 123 | try { 124 | ctx.puppet->send(packetOnline); 125 | ctx.puppet->send(*packetString); 126 | auto packetAck = TPuppetExpect(); 127 | Puppet::PacketFree(packetAck); 128 | } 129 | catch (const std::runtime_error & e) { 130 | MonPanic(e.what()); 131 | } 132 | Puppet::PacketFree(packetString); 133 | 134 | // Main loop 135 | Puppet::PACKET* packet = NULL; 136 | try { 137 | while (true) 138 | { 139 | packet = ctx.puppet->recv(); 140 | TPuppetOnRecv(packet); 141 | Puppet::PacketFree(packet); 142 | packet = NULL; 143 | } 144 | } 145 | catch (const std::runtime_error & e) { 146 | MonPanic(e.what()); 147 | } 148 | } -------------------------------------------------------------------------------- /PEDollMonitor/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | -------------------------------------------------------------------------------- /PEDollMonitor/pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | -------------------------------------------------------------------------------- /PEDollMonitor/pch.h: -------------------------------------------------------------------------------- 1 | #ifndef PCH_H 2 | #define PCH_H 3 | 4 | #include "framework.h" 5 | 6 | #endif //PCH_H 7 | -------------------------------------------------------------------------------- /PEDollMonitor/resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EZForever/PEDoll/893c853d6bda6614b72077707928ead73e6f78df/PEDollMonitor/resource.h -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PEDoll 2 | *Program behavior analyzer for Win32/Win64* 3 | 4 | English | [简体中文](README.zh-CN.md) 5 | 6 | ## Overview 7 | **PEDoll** is a parody of @matrixcascade's [PeDoll](https://github.com/matrixcascade/PeDoll) (the "original PeDoll"), aimed to support the x64 platform, multiple clients and multi-threaded applications. 8 | 9 | PEDoll consists of three parts: 10 | 11 | - The **Controller** accepts incoming client connections and provide a CLI/GUI for user to manage clients and hooks on them. 12 | - The **Monitor** clients runs on a target machine (either the same physical machine the Controller is running on, or a VM), with its main purpose to create Doll clients by either launching a new process, or attaching to existing ones. 13 | - The **Doll** clients are processes injected by the *libDoll* module (`libDoll.dll`). libDoll communicates with the Controller, installs inline hooks into the process and respond to the Controller's commands in case of a hook has activated. 14 | 15 | These parts communicate by the *Puppet Protocol*, a simple, TCP-based scheme of packets. 16 | 17 | PEDoll works by installing hooks on Windows APIs or inner procedures into a Doll process, wait for hooks to activate, then examine the hooks' context (function arguments, memory buffers, etc.) and give out verdicts on whether the process can continue execution. 18 | 19 | You can get a prebulit PEDoll package from [the "Releases" page](https://github.com/EZForever/PEDoll/releases), or compile it yourself with instructions below. 20 | 21 | ## Features 22 | 23 | Comparing to the original PeDoll: 24 | 25 | - **Full x64 support:** PEDoll is designed with x64 concepts in mind. You can set up hooks and examine results just like on a x86 client. 26 | - **Arbitrary number of API/pattern-based hooks:** Due to the change to hooking mechanisms, PEDoll no longer require the API being hooked to be supported by libDoll. Also applies to pattern-based hooks ("binary hooks" as in original PeDoll). 27 | - **Enhanced context expressions:** Examine contexts with C# expressions! Lambda expressions, LINQ extensions, all in one command. 28 | - **Multiple clients:** Because one Controller per Doll can be annoying. 29 | 30 | ## System requirements 31 | 32 | - Controller: Windows 7 SP1 or above, with .NET Framework 4.5 installed (pre-installed on Windows 8 or above). 33 | - Monitor and libDoll: Windows Vista or above. 34 | 35 | ## Usage 36 | 37 | [The project's wiki provides a simple example.][wiki/example] 38 | 39 | More & detailed information on commands, expressions, etc. will be available on [the project's wiki][wiki]. 40 | 41 | ## Building 42 | 43 | The IDE of choice is Visual Studio Community 2019 with workloads `Desktop development with C++` (for building the Monitor and libDoll) and `.NET desktop development` (for building the Controller). 44 | 45 | This project utilizes [Detours](https://github.com/microsoft/Detours) and includes it as a git submodule. In order to build the Monitor and libDoll, you need to obtain it by either use `git clone --recursive https://github.com/EZForever/PEDoll.git` while cloning this repo, or run `git submodule update --init --recursive` afterwards. 46 | 47 | Do not expect all features in Controller to work when debugging; important files like Monitor executables and scripts will be missing. The file `GenerateRelease.cmd`, when run under a Visual Studio Developer Command Prompt, will build the whole solution, place all files accordingly and process all API scripts to support x64. *Always* use the build generated by `GenerateRelease.cmd` for normal usage. 48 | 49 | More information on implementation details will be available on [the project's wiki][wiki]. 50 | 51 | ## FAQ 52 | 53 | See ["FAQ" on project wiki][wiki/faq]. 54 | 55 | ## License 56 | 57 | [The MIT License.](LICENSE.txt) 58 | 59 | [wiki]: https://github.com/EZForever/PEDoll/wiki 60 | [wiki/faq]: https://github.com/EZForever/PEDoll/wiki/FAQ 61 | [wiki/example]: https://github.com/EZForever/PEDoll/wiki/Simple-Example 62 | 63 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # PEDoll 2 | *Win32/Win64 程序行为分析器* 3 | 4 | [English](README.md) | 简体中文 5 | 6 | ## 概述 7 | **PEDoll**是@matrixcascade的作品 [PeDoll](https://github.com/matrixcascade/PeDoll)(下文称“原版PeDoll”)的一个翻版,其设计目标是支持x64、多客户端以及多线程程序。 8 | 9 | PEDoll由三部分组成: 10 | 11 | - **主控端(Controller)** 接受客户端的连接请求,并提供命令行及图形界面供用户对客户端及钩子进行管理。 12 | - **监控端(Monitor)** 运行在目标机器上(主控端所在的机器,或一台虚拟机),主要目的是通过启动新进程或附加到进程的方式,创建新受控端。 13 | - **受控端(Doll)** 是指被注入了*受控端模块*(*libDoll*,`libDoll.dll`)的进程。受控端模块与主控端进行通信、安装内联钩子并当钩子被激活时相应主控端的命令。 14 | 15 | 这三部分通过基于TCP协议的*Puppet Protocol*相互通信。 16 | 17 | PEDoll的工作原理是对受控端进程所使用的Windows API或内部过程安装钩子。待钩子被激活后,即可读取钩子执行的上下文(如函数参数、内存缓冲区等),并给出进程能否继续执行的判定。 18 | 19 | 您可以在[“Releases”页面](https://github.com/EZForever/PEDoll/releases)下载编译好的PEDoll软件包,也可以按照下文中的说明自行编译。 20 | 21 | ## 特性 22 | 23 | 与原版PeDoll相比: 24 | 25 | - **完整的x64支持:** x64支持是PEDoll的设计初衷之一。在功能使用上,x64客户端进程与x86客户端几乎没有任何区别。 26 | - **任意数量的API钩子/特征码钩子:** 挂钩机制的改变使得API钩子不再需要受控端模块支持。特征码钩子(原版PeDoll的“二进制钩子”)同理。 27 | - **更强大的上下文读取能力:** PEDoll支持使用C#表达式读取钩子的上下文。Lambda表达式、LINQ扩展,只需要一个命令。 28 | - **多客户端支持:** 给每一个受控端单独开一个主控端会很麻烦。 29 | 30 | ## 系统需求 31 | 32 | - 主控端:Windows 7 SP1及以上版本,安装有.NET Framework 4.5(Windows 8 及以上版本自带)。 33 | - 监控端及受控端:Windows Vista及以上版本。 34 | 35 | ## 使用方法 36 | 37 | [项目wiki提供了一个简单的例子。][wiki/example] 38 | 39 | 有关命令、表达式等的更多信息,请参阅[项目wiki][wiki]。 40 | 41 | ## 开发 42 | 43 | 开发使用的IDE是Visual Studio 2019社区版,需要`使用C++的桌面开发`(用于开发监控端及受控端)和`.NET桌面开发`(用于开发主控端)两个工作负载。 44 | 45 | 这个项目使用了[Detours](https://github.com/microsoft/Detours),并将其作为git submodule包含在项目中。在编译监控端及受控端之前必须获取这个库。可以在clone本项目时使用`git clone --recursive https://github.com/EZForever/PEDoll.git`命令,或者在之后运行`git submodule update --init --recursive`命令。 46 | 47 | 调试运行主控端时,部分功能可能无法正常工作,原因是缺少了如监控端程序或脚本等的关键文件。在Visual Studio命令提示符下运行`GenerateRelease.cmd`,可以自动编译整个解决方案、把编译生成的文件放在合适的地方,并生成用于支持x64的API脚本。日常使用PEDoll时,请使用`GenerateRelease.cmd`构建的软件包。 48 | 49 | 有关具体实现等的更多信息,请参阅[项目wiki][wiki]。 50 | 51 | ## 常见问题 52 | 53 | 参见[项目wiki上的FAQ页面][wiki/faq]。 54 | 55 | ## 开源许可证 56 | 57 | [The MIT License.](LICENSE.txt) 58 | 59 | [wiki]: https://github.com/EZForever/PEDoll/wiki/Home.zh-CN 60 | [wiki/faq]: https://github.com/EZForever/PEDoll/wiki/FAQ.zh-CN 61 | [wiki/example]: https://github.com/EZForever/PEDoll/wiki/Simple-Example.zh-CN 62 | 63 | -------------------------------------------------------------------------------- /Scripts/API/CreateFile.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | hook kernel32!CreateFileA --stack=28 --before --echo="lpFileName = {str(arg(0))}" * 3 | hook kernel32!CreateFileW --stack=28 --before --echo="lpFileName = {wstr(arg(0))}" * 4 | -------------------------------------------------------------------------------- /Scripts/API/CreateProcess.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | hook kernel32!CreateProcessA --stack=40 --before --echo="lpApplicationName = {str(arg(0))}" --echo="lpCommandLine = {str(arg(1))}" * 3 | hook kernel32!CreateProcessW --stack=40 --before --echo="lpApplicationName = {wstr(arg(0))}" --echo="lpCommandLine = {wstr(arg(1))}" * 4 | -------------------------------------------------------------------------------- /Scripts/API/DeleteFile.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | hook kernel32!DeleteFileA --stack=4 --before --echo="lpFileName = {str(arg(0))}" * 3 | hook kernel32!DeleteFileW --stack=4 --before --echo="lpFileName = {str(arg(0))}" * 4 | -------------------------------------------------------------------------------- /Scripts/API/ExitWindowsEx.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | hook user32!ExitWindowsEx --stack=8 --before * 3 | -------------------------------------------------------------------------------- /Scripts/API/LoadLibraryEx.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | hook kernel32!LoadLibraryExA --stack=12 --before --echo="lpLibFileName = {str(arg(0))}" * 3 | hook kernel32!LoadLibraryExW --stack=12 --before --echo="lpLibFileName = {wstr(arg(0))}" * 4 | -------------------------------------------------------------------------------- /Scripts/API/MessageBox.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | hook user32!MessageBox --stack=16 --before --echo="lpText = {str(arg(1))}" --echo="lpCaption = {str(arg(2))}" * 3 | hook user32!MessageBoxA --stack=16 --before --echo="lpText = {str(arg(1))}" --echo="lpCaption = {str(arg(2))}" * 4 | hook user32!MessageBoxW --stack=16 --before --echo="lpText = {wstr(arg(1))}" --echo="lpCaption = {wstr(arg(2))}" * 5 | -------------------------------------------------------------------------------- /Scripts/API/OpenProcess.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | hook OpenProcess --stack=12 --before --echo="dwProcessId = {(int)arg(2)}" * 3 | -------------------------------------------------------------------------------- /Scripts/API/RegCreateKeyEx.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | hook advapi32!RegCreateKeyExA --stack=36 --before * 3 | hook advapi32!RegCreateKeyExW --stack=36 --before * 4 | -------------------------------------------------------------------------------- /Scripts/API/RegOpenKeyEx.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | hook advapi32!RegOpenKeyExA --stack=20 --before --echo="hKey = {arg(0)}" --echo="lpSubKey = {str(arg(1))}" * 3 | hook advapi32!RegOpenKeyExW --stack=20 --before --echo="hKey = {arg(0)}" --echo="lpSubKey = {wstr(arg(1))}" * 4 | -------------------------------------------------------------------------------- /Scripts/API/RegSetKeyValue.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | hook advapi32!RegSetKeyValueA --stack=24 --before --echo="hKey = {arg(0)}" --echo="lpSubKey = {str(arg(1))}" --echo="lpValueName = {str(arg(2))}" --echo="dwType = {(int)arg(3)}" --dump={arg(4)},{(int)arg(5)} * 3 | hook advapi32!RegSetKeyValueA --stack=24 --before --echo="hKey = {arg(0)}" --echo="lpSubKey = {wstr(arg(1))}" --echo="lpValueName = {wstr(arg(2))}" --echo="dwType = {(int)arg(3)}" --dump={arg(4)},{(int)arg(5)} * 4 | -------------------------------------------------------------------------------- /Scripts/API/SetFileAttributes.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | hook kernel32!SetFileAttributesA --stack=8 --before --echo="lpFileName = {str(arg(0))}" --echo="dwFileAttributes = {(uint)arg(1)}" * 3 | hook kernel32!SetFileAttributesW --stack=8 --before --echo="lpFileName = {wstr(arg(0))}" --echo="dwFileAttributes = {(uint)arg(1)}" * 4 | -------------------------------------------------------------------------------- /Scripts/API/WriteFile.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | hook kernel32!WriteFile --stack=20 --before --dump={arg(1)},{(int)arg(2)} * 3 | -------------------------------------------------------------------------------- /Scripts/API/WriteProcessMemory.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | hook kernel32!WriteProcessMemory --stack=20 --before --dump={arg(2)},{arg(3)} * 3 | -------------------------------------------------------------------------------- /Scripts/API/recv.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | # --dump={arg(1)},{(int)arg(2)} 3 | hook ws2_32!recv --stack=16 --before * 4 | -------------------------------------------------------------------------------- /Scripts/API/recvfrom.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | # --dump={arg(1)},{(int)arg(2)} 3 | hook ws2_32!recvfrom --stack=24 --before * 4 | -------------------------------------------------------------------------------- /Scripts/API/send.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | hook ws2_32!send --stack=16 --before --dump={arg(1)},{(int)arg(2)} * 3 | -------------------------------------------------------------------------------- /Scripts/API/sendto.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | hook ws2_32!sendto --stack=24 --before --dump={arg(1)},{(int)arg(2)} * 3 | -------------------------------------------------------------------------------- /Scripts/Break_on_MessageBox.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | load MessageBox --after --verdict=approve 3 | -------------------------------------------------------------------------------- /Scripts/Break_on_MessageBox_64.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | load MessageBox64 --after --verdict=approve 3 | -------------------------------------------------------------------------------- /Scripts/HTTP.txt: -------------------------------------------------------------------------------- 1 | # TODO -------------------------------------------------------------------------------- /Scripts/MBR_Ransom.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | load CreateFile --verdict=approve --after --verdict=approve 3 | load WriteFile --verdict=reject --after --verdict=approve 4 | load DeleteFile --verdict=reject --after --verdict=approve 5 | load CreateProcess --verdict=reject --after --verdict=approve 6 | load ExitWindowsEx --verdict=reject --after --verdict=approve 7 | -------------------------------------------------------------------------------- /Scripts/MBR_Ransom_64.txt: -------------------------------------------------------------------------------- 1 | target --doll 2 | load CreateFile64 --verdict=approve --after --verdict=approve 3 | load WriteFile64 --verdict=reject --after --verdict=approve 4 | load DeleteFile64 --verdict=reject --after --verdict=approve 5 | load CreateProcess64 --verdict=reject --after --verdict=approve 6 | load ExitWindowsEx64 --verdict=reject --after --verdict=approve 7 | -------------------------------------------------------------------------------- /Scripts/Registry.txt: -------------------------------------------------------------------------------- 1 | # TODO (ctx dictionary: RegCreateKeyEx, RegOpenKeyEx, RegSetValueEx, RegQueryValueEx) -------------------------------------------------------------------------------- /Scripts/Remote_Trojan.txt: -------------------------------------------------------------------------------- 1 | # TODO (load {CreateFile, Registry, TCP, UDP}) -------------------------------------------------------------------------------- /Scripts/TCP.txt: -------------------------------------------------------------------------------- 1 | # TODO (ctx dictionary: send, recv) -------------------------------------------------------------------------------- /Scripts/UDP.txt: -------------------------------------------------------------------------------- 1 | # TODO (ctx dictionary: sendto, recvfrom) -------------------------------------------------------------------------------- /libDoll/BoyerMoore.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "BoyerMoore.h" 3 | 4 | // Inspired by Wikepedia 5 | // https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm#Implementations 6 | 7 | #ifndef max 8 | # define max(a, b) ((a > b) ? a : b) 9 | #endif 10 | 11 | inline bool BoyerMoore::IsPrefix(const char* data, size_t dataLen, ptrdiff_t pos) 12 | { 13 | return !memcmp(data, data + pos, dataLen - pos); 14 | } 15 | 16 | inline size_t BoyerMoore::SuffixLength(const char* data, size_t dataLen, ptrdiff_t pos) 17 | { 18 | size_t i = 0; 19 | while (data[pos - i] != data[(dataLen - 1) - i] && (ptrdiff_t)i < pos) 20 | i++; 21 | 22 | return i; 23 | } 24 | 25 | void BoyerMoore::MakeDelta1() 26 | { 27 | delta1 = new ptrdiff_t[256]; 28 | 29 | for (size_t i = 0; i < 256; i++) 30 | delta1[i] = patternLen; 31 | 32 | for (size_t i = 0; i < patternLen - 2; i++) 33 | delta1[(unsigned char)pattern[i]] = (patternLen - 1) - i; 34 | // Disable sign extension by explicitly converting to an shorter unsigned type beforehand 35 | } 36 | 37 | void BoyerMoore::MakeDelta2() 38 | { 39 | delta2 = new ptrdiff_t[patternLen]; 40 | 41 | size_t iLastPrefix = patternLen - 1; 42 | for (ptrdiff_t i = patternLen - 1; i >= 0; i--) 43 | { 44 | if (IsPrefix(pattern, patternLen, i + 1)) 45 | iLastPrefix = i; 46 | delta2[i] = iLastPrefix + ((patternLen - 1) - i); 47 | } 48 | 49 | for (size_t i = 0; i < patternLen - 1; i++) 50 | { 51 | size_t suffixLen = SuffixLength(pattern, patternLen, i); 52 | if (pattern[i - suffixLen] != pattern[(patternLen - 1) - suffixLen]) 53 | delta2[(patternLen - 1) - suffixLen] = (patternLen - 1) - i + suffixLen; 54 | } 55 | } 56 | 57 | BoyerMoore::BoyerMoore(const char* pattern, size_t patternLen) 58 | { 59 | delta1 = delta2 = NULL; 60 | 61 | this->pattern = pattern; 62 | this->patternLen = patternLen; 63 | 64 | // My implemention of the algorithm does not work with patternLen == 1 65 | if (patternLen >= 2) 66 | { 67 | MakeDelta1(); 68 | MakeDelta2(); 69 | } 70 | } 71 | 72 | BoyerMoore::~BoyerMoore() 73 | { 74 | if (delta1) 75 | delete[] delta1; 76 | if (delta2) 77 | delete[] delta2; 78 | } 79 | 80 | const char* BoyerMoore::search(const char* haystack, size_t haystackLen) 81 | { 82 | // For the case the algorithm does not work, just use memchr() from stdlib 83 | if (patternLen < 2) 84 | return (const char*)memchr(haystack, pattern[0], haystackLen); 85 | 86 | size_t i = patternLen - 1; 87 | while (i < haystackLen) 88 | { 89 | ptrdiff_t j = patternLen - 1; 90 | while (j >= 0 && haystack[i] == pattern[j]) 91 | { 92 | i--; 93 | j--; 94 | } 95 | 96 | if (j < 0) 97 | return haystack + (i + 1); 98 | i += max(delta1[haystack[i]], delta2[j]); 99 | } 100 | 101 | return NULL; 102 | } 103 | 104 | -------------------------------------------------------------------------------- /libDoll/BoyerMoore.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // The Boyer-Moore binary searcher 4 | class BoyerMoore final 5 | { 6 | private: 7 | 8 | const char* pattern; 9 | size_t patternLen; 10 | 11 | ptrdiff_t* delta1; 12 | ptrdiff_t* delta2; 13 | 14 | // Copying an instance is not allowed 15 | BoyerMoore(const BoyerMoore& x) = delete; 16 | BoyerMoore& operator=(BoyerMoore& x) = delete; 17 | 18 | static inline bool IsPrefix(const char* data, size_t dataLen, ptrdiff_t pos); 19 | static inline size_t SuffixLength(const char* data, size_t dataLen, ptrdiff_t pos); 20 | 21 | void MakeDelta1(); 22 | void MakeDelta2(); 23 | 24 | 25 | public: 26 | 27 | // Construct a search context using the given pattern 28 | // NOTE: the pattern is NOT saved inside the object and must stay available during the searching process 29 | BoyerMoore(const char* pattern, size_t patternLen); 30 | ~BoyerMoore(); 31 | 32 | // Search for the first occurance of pattern in haystack 33 | // Returns the location in haystack of found, or NULL otherwise 34 | const char* search(const char* haystack, size_t haystackLen); 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /libDoll/Hook.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "libDoll.h" 3 | #include "HookStub.h" 4 | #include "Hook.h" 5 | 6 | #include "../Detours/repo/src/detours.h" 7 | 8 | void DollHookAllocBeforeStub(char* &pBefore, DWORD &pBeforeProtect, UINT_PTR hookOEP, char* target) 9 | { 10 | // NOTE: VirtualProtect() works on memory pages, not bytes 11 | // If protection above is set to PAGE_EXECUTE_READ, the memory allocation below will fail 12 | // due to no write permission to the memory page 13 | 14 | pBefore = new char[HookStubBefore_len]; 15 | memcpy(pBefore, &HookStubBefore, HookStubBefore_len); 16 | *(UINT_PTR*)(pBefore + HookStubBefore_HookOEPOffset) = hookOEP; 17 | *(UINT_PTR*)(pBefore + HookStubBefore_AddrOffset) = (UINT_PTR)target; 18 | VirtualProtect(pBefore, HookStubBefore_len, PAGE_EXECUTE_READWRITE, &pBeforeProtect); 19 | } 20 | 21 | void DollHookFreeBeforeStub(char* &pBefore, DWORD& pBeforeProtect) 22 | { 23 | VirtualProtect(pBefore, HookStubBefore_len, pBeforeProtect, &pBeforeProtect); 24 | delete[] pBefore; 25 | pBefore = NULL; 26 | } 27 | 28 | // DetourUpdateThread(GetCurrentThread()) is not necessary 29 | // but a call to DetourUpdateThread() for each thread is required 30 | void DollHookUpdateAllThreads(std::set &updatedThreads) 31 | { 32 | if (!updatedThreads.empty()) 33 | return; 34 | 35 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); 36 | if (hSnapshot == INVALID_HANDLE_VALUE) 37 | return; 38 | 39 | DWORD procSelf = GetCurrentProcessId(); 40 | DWORD thSelf = ctx.pRealGetCurrentThreadId(); 41 | THREADENTRY32 thEntry; 42 | thEntry.dwSize = sizeof(THREADENTRY32); 43 | 44 | if (!Thread32First(hSnapshot, &thEntry)) 45 | { 46 | CloseHandle(hSnapshot); 47 | return; 48 | } 49 | 50 | do 51 | { 52 | // th32ProcessID of CreateToolhelp32Snapshot() only works on modules 53 | // So skip any threads owned by other processes in order not to freeze the whole system 54 | if (thEntry.th32OwnerProcessID != procSelf) 55 | continue; 56 | 57 | DWORD thIter = thEntry.th32ThreadID; 58 | if (thIter == thSelf) 59 | continue; 60 | 61 | HANDLE hThIter = OpenThread(THREAD_SUSPEND_RESUME, FALSE, thIter); 62 | if (!hThIter) 63 | continue; 64 | 65 | DetourUpdateThread(hThIter); 66 | updatedThreads.emplace(hThIter); 67 | } while (Thread32Next(hSnapshot, &thEntry)); 68 | 69 | CloseHandle(hSnapshot); 70 | } 71 | 72 | void DollHookEndUpdateAllThreads(std::set& updatedThreads) 73 | { 74 | for (auto iter = updatedThreads.begin(); iter != updatedThreads.end(); iter++) 75 | { 76 | CloseHandle(*iter); 77 | } 78 | updatedThreads.clear(); 79 | } 80 | 81 | bool DollHookIsHappened() 82 | { 83 | if (TryEnterCriticalSection(&ctx.lockHook)) 84 | { 85 | LeaveCriticalSection(&ctx.lockHook); 86 | return false; 87 | } 88 | return true; 89 | } 90 | 91 | void DollHookAdd(UINT_PTR hookOEP, UINT_PTR denySPOffset, UINT_PTR denyAX) 92 | { 93 | if (ctx.dollHooks.find(hookOEP) != ctx.dollHooks.end()) 94 | return; 95 | 96 | DetourTransactionBegin(); 97 | 98 | std::set updatedThreads; 99 | DollHookUpdateAllThreads(updatedThreads); 100 | 101 | LIBDOLL_HOOK* hook = new LIBDOLL_HOOK; 102 | 103 | DollHookAllocBeforeStub(hook->pBeforeA, hook->pBeforeAProtect, hookOEP, &HookStubA); 104 | DollHookAllocBeforeStub(hook->pBeforeB, hook->pBeforeBProtect, hookOEP, &HookStubB); 105 | DollHookAllocBeforeStub(hook->pBeforeDeny, hook->pBeforeDenyProtect, hookOEP, &HookStubOnDeny); 106 | 107 | hook->denySPOffset = denySPOffset; 108 | hook->denyAX = denyAX; 109 | 110 | ctx.dollHooks.emplace(std::make_pair(hookOEP, hook)); 111 | 112 | hook->pTrampoline = hookOEP; 113 | DetourAttach((void**)&hook->pTrampoline, hook->pBeforeA); 114 | 115 | if (hookOEP == (UINT_PTR)GetCurrentThreadId) 116 | ctx.pRealGetCurrentThreadId = (GET_CURRENT_THREAD_ID)hook->pTrampoline; 117 | 118 | DetourTransactionCommit(); 119 | DollHookEndUpdateAllThreads(updatedThreads); 120 | } 121 | 122 | void DollHookRemove(UINT_PTR hookOEP, bool removeEntry) 123 | { 124 | auto hookIter = ctx.dollHooks.find(hookOEP); 125 | if (hookIter == ctx.dollHooks.end()) 126 | return; 127 | 128 | DetourTransactionBegin(); 129 | 130 | std::set updatedThreads; 131 | DollHookUpdateAllThreads(updatedThreads); 132 | 133 | LIBDOLL_HOOK* hook = hookIter->second; 134 | 135 | DetourDetach((void**)&hook->pTrampoline, hook->pBeforeA); 136 | 137 | if (hookOEP == (UINT_PTR)GetCurrentThreadId) 138 | ctx.pRealGetCurrentThreadId = GetCurrentThreadId; 139 | 140 | if(removeEntry) 141 | ctx.dollHooks.erase(hookIter); 142 | 143 | DollHookFreeBeforeStub(hook->pBeforeDeny, hook->pBeforeDenyProtect); 144 | DollHookFreeBeforeStub(hook->pBeforeB, hook->pBeforeBProtect); 145 | DollHookFreeBeforeStub(hook->pBeforeA, hook->pBeforeAProtect); 146 | 147 | delete hook; 148 | 149 | DetourTransactionCommit(); 150 | DollHookEndUpdateAllThreads(updatedThreads); 151 | } -------------------------------------------------------------------------------- /libDoll/Hook.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "pch.h" 3 | #include "libDoll.h" 4 | 5 | bool DollHookIsHappened(); 6 | void DollHookAdd(UINT_PTR hookOEP, UINT_PTR denySPOffset = 0, UINT_PTR denyAX = 0); 7 | void DollHookRemove(UINT_PTR hookOEP, bool removeEntry = true); 8 | 9 | -------------------------------------------------------------------------------- /libDoll/HookStub.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "HookStub.h" 3 | #include "libDoll.h" 4 | #include "Thread.h" 5 | 6 | #include "../Detours/repo/src/detours.h" 7 | 8 | extern "C" UINT_PTR DollThreadIsCurrent() 9 | { 10 | // Avoid hook on GetCurrentThreadId() to cause endless loop 11 | return (UINT_PTR)(ctx.dollThreads.find(ctx.pRealGetCurrentThreadId()) != ctx.dollThreads.end()); 12 | } 13 | 14 | extern "C" UINT_PTR DollHookGetCurrent(UINT_PTR* context) 15 | { 16 | return (UINT_PTR)(ctx.dollHooks.find(*context)->second); 17 | } 18 | 19 | extern "C" void DollOnHook(UINT_PTR* context) 20 | { 21 | // Procedure: 22 | // register current thread 23 | // EnterCriticalSection 24 | // hookOriginalSP = context[1]; 25 | // "Before..." operations 26 | // Set context[0] & context[1] based on the reply 27 | // If reply is Deny, set hookDenySPOffset & hookDenyReturn 28 | 29 | // Replies are implemented by modifying HookOEP & OriginalSP 30 | // Allow: HookOEP to pTrampoline, OriginalSP to pBeforeB 31 | // Deny: HookOEP to HookStubOnDeny, OriginalSP to pBeforeB 32 | // Terminate: HookOEP to DebugBreak / __fastfail, OriginalSP unchanged (This way the call stack is like called DebugBreak() by hand) 33 | 34 | DollThreadRegisterCurrent(); 35 | 36 | EnterCriticalSection(&ctx.lockHook); 37 | 38 | LIBDOLL_HOOK* hook = (LIBDOLL_HOOK*)DollHookGetCurrent(context); 39 | hook->context = context; 40 | hook->originalSP = context[1]; 41 | 42 | // FIXME: MSG_ONHOOK should not really be sent at here, since TPuppet may send other packets at the same time and cause data corruption 43 | // Replied ACK is received & processed by TPuppetOnRecv() 44 | ctx.puppet->send(Puppet::PACKET_MSG_ONHOOK(0)); 45 | ctx.puppet->send(Puppet::PACKET_INTEGER(context[0])); 46 | 47 | ctx.waitingHookOEP = context[0]; 48 | WaitForSingleObject(ctx.hEvtHookVerdict, INFINITE); 49 | 50 | if (hook->verdict == 0) 51 | { 52 | // Approved 53 | context[0] = hook->pTrampoline; 54 | context[1] = (UINT_PTR)hook->pBeforeB; 55 | } 56 | else if (hook->verdict == 1) 57 | { 58 | // Rejected 59 | // Parameters are set by TPuppetOnRecv*() 60 | context[0] = (UINT_PTR)hook->pBeforeDeny; 61 | context[1] = (UINT_PTR)hook->pBeforeB; // Comment this line to disable "after" phase on a rejected "before" phase 62 | } 63 | else 64 | { 65 | // Terminate 66 | context[0] = (UINT_PTR)DebugBreak; 67 | } 68 | } 69 | 70 | extern "C" void DollOnAfterHook(UINT_PTR* context) 71 | { 72 | // Procedure: 73 | // "After..." operations 74 | // If prompted to Terminate, overwrite hookOriginalSP with DebugBreak / __fastfail 75 | // since it is not possible to do it "the pretty way" // TODO: Revise this sentence 76 | // context[0] = hookOriginalSP; 77 | // LeaveCriticalSection 78 | // unregister current thread 79 | 80 | LIBDOLL_HOOK* hook = (LIBDOLL_HOOK*)DollHookGetCurrent(context); 81 | hook->context = context; 82 | 83 | // FIXME: MSG_ONHOOK should not really be sent at here, since TPuppet may send other packets at the same time and cause data corruption 84 | // Replied ACK is received & processed by TPuppetOnRecv() 85 | ctx.puppet->send(Puppet::PACKET_MSG_ONHOOK(1)); 86 | ctx.puppet->send(Puppet::PACKET_INTEGER(context[0])); 87 | 88 | ctx.waitingHookOEP = context[0]; 89 | WaitForSingleObject(ctx.hEvtHookVerdict, INFINITE); 90 | 91 | if (hook->verdict == 0) 92 | { 93 | // Approved / Continue 94 | context[0] = hook->originalSP; 95 | } 96 | else 97 | { 98 | // Terminate 99 | context[0] = (UINT_PTR)DebugBreak; 100 | } 101 | 102 | LeaveCriticalSection(&ctx.lockHook); 103 | 104 | DollThreadUnregisterCurrent(); 105 | } 106 | 107 | extern "C" void DollOnEPHook(UINT_PTR* context) 108 | { 109 | // Unhook first 110 | DetourTransactionBegin(); 111 | DetourDetach(&ctx.pEP, &HookStubEP); 112 | DetourTransactionCommit(); 113 | 114 | // By now the trampoline is destroyed by Detours, so we need to jump (again) to EP manually 115 | *context = (UINT_PTR)ctx.pEP; 116 | 117 | // Hooks may exist after returning from WaitForSingleObject() 118 | DollThreadRegisterCurrent(); 119 | 120 | // Wait for approval 121 | WaitForSingleObject(ctx.hEvtEP, INFINITE); 122 | 123 | // By now the EP event is useless and can be destroyed 124 | CloseHandle(ctx.hEvtEP); 125 | ctx.hEvtEP = INVALID_HANDLE_VALUE; 126 | 127 | DollThreadUnregisterCurrent(); 128 | } 129 | 130 | -------------------------------------------------------------------------------- /libDoll/HookStub.h: -------------------------------------------------------------------------------- 1 | // HookStub.h 2 | // Platform-independent declarations for C++-Assembly interop 3 | // Although platform-independent, they're not code-independent; I/O must follow the commented rules strictly 4 | #pragma once 5 | #include "libDoll.h" 6 | 7 | extern "C" { 8 | 9 | // They are not "char"s; they are pieces of machine code 10 | 11 | extern char HookStubBefore; 12 | extern char HookStubA; 13 | extern char HookStubB; 14 | extern char HookStubOnDeny; 15 | extern char HookStubEP; 16 | 17 | extern const UINT_PTR HookStubBefore_len; 18 | extern const UINT_PTR HookStubBefore_HookOEPOffset; 19 | extern const UINT_PTR HookStubBefore_AddrOffset; 20 | extern const UINT_PTR pushad_count; 21 | 22 | UINT_PTR DollThreadIsCurrent(); 23 | 24 | // context[0] = [in]HookOEP 25 | UINT_PTR DollHookGetCurrent(UINT_PTR* context); 26 | 27 | // context[0] = [inout]HookOEP, context[1] = [inout]returnAddr 28 | // context[n] for n > 1 should not be accessed directly 29 | // context[-1] = [in]{e|r}ax, context[-2] = [in]{e|r}bx, etc 30 | void DollOnHook(UINT_PTR* context); 31 | 32 | // Same as DollOnHook() but context[0] = [out]returnAddr and context[1] is now gone 33 | void DollOnAfterHook(UINT_PTR* context); 34 | 35 | // context[0] = [out]returnAddr 36 | void DollOnEPHook(UINT_PTR* context); 37 | 38 | } 39 | 40 | -------------------------------------------------------------------------------- /libDoll/HookStub_x64.asm: -------------------------------------------------------------------------------- 1 | ; This file is translated from HookStub_x86.asm 2 | ; All the comments are identital to the x86 counterpart. 3 | extern DollThreadIsCurrent:byte, DollHookGetCurrent:byte 4 | extern DollOnHook:byte, DollOnAfterHook:byte, DollOnEPHook:byte 5 | 6 | public HookStubBefore, HookStubA, HookStubB, HookStubOnDeny, HookStubEP 7 | public HookStubBefore_len, HookStubBefore_HookOEPOffset, HookStubBefore_AddrOffset 8 | public pushad_count 9 | 10 | .code 11 | 12 | ; Machine's word size, in bytes 13 | ; Could have used @WordSize (https://docs.microsoft.com/en-us/cpp/assembler/masm/at-wordsize) but it is not supported in x64 14 | WORDSZ equ 8 15 | 16 | ; Registers saved by the pushall macro, as a macro for usage in assembly code 17 | ; Currently: rax, rcx, rdx, rbx, rbp, rsp, rdi, rsi, r8, r9, rflags, (garbage) 18 | PUSHAD_CNT equ 12 19 | 20 | ; Size of register shadow space 21 | ; NOTE: The `sub/add rsp, 8 * 4`s around calling a function are MANDATORY 22 | ; That's the "shadow space" left for the caller function 23 | ; Under Debug it's safe to remove them, but under Release this space may get utilized 24 | ; See https://stackoverflow.com/a/30194393 for details 25 | SHADOWSZ equ WORDSZ * 4 26 | 27 | ; pushad/popad are not supported on x64 :( 28 | 29 | ; NOTE: this macro corrupts rax 30 | pushall macro 31 | push rax 32 | mov rax, rsp 33 | lea rax, [rax + WORDSZ] ; lea instead of add to avoid flag corruption 34 | ; rax == original rsp 35 | push rcx 36 | push rdx 37 | push rbx 38 | push rax ; original rsp 39 | push rbp 40 | push rsi 41 | push rdi 42 | push r8 43 | push r9 44 | pushfq 45 | push rax 46 | ; Stack alignment is also MANDATORY when it comes to optimized STL functions 47 | ; pushall must maintain an even number of push operations, so here's a garbage value 48 | endm 49 | 50 | popall macro 51 | pop rax ; the garbage value 52 | popfq 53 | pop r9 54 | pop r8 55 | pop rdi 56 | pop rsi 57 | pop rbp 58 | pop rax ; original rsp 59 | pop rbx 60 | pop rdx 61 | pop rcx 62 | ; rax == original rsp 63 | ; stack == (original rax), (red zone...) 64 | xchg rax, [rsp] 65 | ; swap(rax, [rsp]) 66 | ; stack == (original rsp), (red zone...) 67 | pop rsp 68 | endm 69 | 70 | ; push for 64-bit immediates is not supported on x64 too :( 71 | 72 | pushimm64 macro x 73 | push rax 74 | mov rax, x 75 | ; rax == x 76 | ; stack == (original rax), (red zone...) 77 | xchg rax, [rsp] 78 | ; swap(rax, [rsp]) 79 | ; stack == (x), (red zone...) 80 | endm 81 | 82 | HookStubBefore: 83 | pushimm64 0CCCCCCCCCCCCCCCCh ; HookOEP placeholder 84 | pushimm64 0CCCCCCCCCCCCCCCCh ; Address pointer placeholder 85 | ret 86 | HookStubBefore_end: 87 | 88 | HookStubA: 89 | pushall 90 | 91 | sub rsp, SHADOWSZ 92 | lea rax, DollThreadIsCurrent 93 | call rax 94 | add rsp, SHADOWSZ 95 | 96 | lea rcx, [rsp + WORDSZ * PUSHAD_CNT] 97 | 98 | test rax, rax 99 | jnz __HookStubA_isDoll 100 | 101 | sub rsp, SHADOWSZ 102 | lea rax, DollOnHook 103 | call rax 104 | add rsp, SHADOWSZ 105 | 106 | popall 107 | 108 | ret 109 | 110 | __HookStubA_isDoll: 111 | 112 | sub rsp, SHADOWSZ 113 | lea rax, DollHookGetCurrent 114 | call rax 115 | add rsp, SHADOWSZ 116 | 117 | mov rdx, [rax + WORDSZ * 0] ; offset LIBDOLL_HOOK::pTrampoline 118 | 119 | mov [rsp + WORDSZ * PUSHAD_CNT], rdx 120 | 121 | popall 122 | 123 | ret 124 | 125 | HookStubB: 126 | pushall 127 | 128 | mov rcx, rsp 129 | add rcx, WORDSZ * PUSHAD_CNT 130 | 131 | sub rsp, SHADOWSZ 132 | lea rax, DollOnAfterHook 133 | call rax 134 | add rsp, SHADOWSZ 135 | 136 | popall 137 | 138 | ret 139 | 140 | HookStubOnDeny: 141 | pushall 142 | 143 | lea rcx, [rsp + WORDSZ * PUSHAD_CNT] 144 | 145 | sub rsp, SHADOWSZ 146 | lea rax, DollHookGetCurrent 147 | call rax 148 | add rsp, SHADOWSZ 149 | 150 | mov rdx, [rax + WORDSZ * 1] ; offset LIBDOLL_HOOK::denySPOffset 151 | 152 | add [rsp + WORDSZ * 7], rdx ; offset pushad::rsp 153 | 154 | lea rcx, [rsp + WORDSZ * (PUSHAD_CNT + 1)] ; &(return addr) 155 | 156 | mov rsi, [rcx] 157 | 158 | mov [rcx + rdx], rsi 159 | 160 | mov rdx, [rax + WORDSZ * 2] ; offset LIBDOLL_HOOK::denyAX 161 | 162 | mov [rsp + WORDSZ * 11], rdx ; offset pushad::eax 163 | 164 | popall 165 | 166 | add rsp, WORDSZ 167 | 168 | ret 169 | 170 | HookStubEP: 171 | push rax 172 | 173 | pushall 174 | 175 | lea rcx, [rsp + WORDSZ * PUSHAD_CNT] 176 | 177 | sub rsp, SHADOWSZ 178 | lea rax, DollOnEPHook 179 | call rax 180 | add rsp, SHADOWSZ 181 | 182 | popall 183 | 184 | ret 185 | 186 | .const 187 | 188 | HookStubBefore_len \ 189 | dq HookStubBefore_end - HookStubBefore 190 | 191 | ; Offset to HookOEP placeholder 192 | HookStubBefore_HookOEPOffset \ 193 | dq 3 194 | 195 | ; Offset to address pointer placeholder 196 | HookStubBefore_AddrOffset \ 197 | dq 18 198 | 199 | ; Registers saved by the pushad/pushall instruction 200 | pushad_count \ 201 | dq PUSHAD_CNT 202 | 203 | end -------------------------------------------------------------------------------- /libDoll/Thread.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "pch.h" 3 | #include "libDoll.h" 4 | 5 | #include "Thread.h" 6 | 7 | void DollThreadRegisterCurrent() 8 | { 9 | ctx.dollThreads.emplace(ctx.pRealGetCurrentThreadId()); 10 | } 11 | 12 | void DollThreadUnregisterCurrent() 13 | { 14 | ctx.dollThreads.erase(ctx.pRealGetCurrentThreadId()); 15 | } 16 | 17 | void DollThreadPanic(const char* msg) 18 | { 19 | FatalAppExitA(0, msg); 20 | } 21 | 22 | void DollThreadPanic(const wchar_t* msg) 23 | { 24 | FatalAppExitW(0, msg); 25 | } 26 | 27 | void DollThreadSuspendAll(bool skipDollThreads) 28 | { 29 | if (!ctx.suspendedThreads.empty()) 30 | return; 31 | 32 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); 33 | if (hSnapshot == INVALID_HANDLE_VALUE) 34 | return; 35 | 36 | DWORD procSelf = GetCurrentProcessId(); 37 | DWORD thSelf = ctx.pRealGetCurrentThreadId(); 38 | THREADENTRY32 thEntry; 39 | thEntry.dwSize = sizeof(THREADENTRY32); 40 | 41 | if (!Thread32First(hSnapshot, &thEntry)) 42 | { 43 | CloseHandle(hSnapshot); 44 | return; 45 | } 46 | 47 | do 48 | { 49 | // th32ProcessID of CreateToolhelp32Snapshot() only works on modules 50 | // So skip any threads owned by other processes in order not to freeze the whole system 51 | if (thEntry.th32OwnerProcessID != procSelf) 52 | continue; 53 | 54 | DWORD thIter = thEntry.th32ThreadID; 55 | if(thIter == thSelf || (skipDollThreads && (ctx.dollThreads.find(thIter) != ctx.dollThreads.end()))) 56 | continue; 57 | 58 | HANDLE hThIter = OpenThread(THREAD_SUSPEND_RESUME, FALSE, thIter); 59 | if (!hThIter) 60 | continue; 61 | 62 | SuspendThread(hThIter); 63 | ctx.suspendedThreads.emplace(hThIter); 64 | } while (Thread32Next(hSnapshot, &thEntry)); 65 | 66 | CloseHandle(hSnapshot); 67 | } 68 | 69 | void DollThreadResumeAll() 70 | { 71 | for (auto iter = ctx.suspendedThreads.begin(); iter != ctx.suspendedThreads.end(); iter++) 72 | { 73 | ResumeThread(*iter); 74 | CloseHandle(*iter); 75 | } 76 | ctx.suspendedThreads.clear(); 77 | } 78 | 79 | -------------------------------------------------------------------------------- /libDoll/Thread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "pch.h" 3 | #include "libDoll.h" 4 | 5 | // Register current thread as a libDoll thread 6 | // libDoll threads will not be affected by hooks, i.e. will follow the hook but sliently continues 7 | void DollThreadRegisterCurrent(); 8 | 9 | // Unregister current thread 10 | void DollThreadUnregisterCurrent(); 11 | 12 | // Terminate the attached process after a textual message 13 | // Called when an unrecoverable error has happened 14 | void DollThreadPanic(const char* msg); 15 | void DollThreadPanic(const wchar_t* msg); 16 | 17 | // Suspend all threads in current process, exclude the current one, and optionally libDoll ones 18 | void DollThreadSuspendAll(bool skipDollThreads); 19 | 20 | // Resume all threads suspended by DollThreadSuspendAll() 21 | void DollThreadResumeAll(); 22 | 23 | -------------------------------------------------------------------------------- /libDoll/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "libDoll.h" 3 | 4 | #include "../Detours/repo/src/detours.h" 5 | #include "HookStub.h" 6 | #include "Thread.h" 7 | #include "Hook.h" 8 | 9 | void __cdecl TPuppet(void*); 10 | 11 | LIBDOLL_CTX ctx; 12 | 13 | char* DollDllFindServerInfo() 14 | { 15 | HMODULE hIter = DetourEnumerateModules(NULL); 16 | char* payload; 17 | DWORD payloadSize; 18 | 19 | while(hIter) 20 | { 21 | payload = (char*)DetourFindPayload(hIter, Puppet::PAYLOAD_SERVER_INFO, &payloadSize); 22 | if (payload) 23 | return payload; 24 | hIter = DetourEnumerateModules(hIter); 25 | } 26 | 27 | return NULL; 28 | } 29 | 30 | BOOL DollDllAttach(BOOL isInjectedByDetours) 31 | { 32 | // This will get updated if DetourAttach() / DetourDetach() happened on GetCurrentThreadId 33 | // Initialize this before usage in DollThreadSuspendAll() 34 | ctx.pRealGetCurrentThreadId = GetCurrentThreadId; 35 | 36 | if (isInjectedByDetours) 37 | { 38 | // Set up a one-time hook at the entry point of the main executable 39 | // NOTE: Try to DollThreadSuspendAll() now will just suspend DLL loader threads on Windows 10, thus breaking the first "break" 40 | // NOTE: DetourCreateProcessWithDllEx() will erase the original IAT, so we'll always be the first DLL initializing 41 | 42 | // Get & store EP 43 | MODULEINFO mi; 44 | GetModuleInformation(GetCurrentProcess(), GetModuleHandle(NULL), &mi, sizeof(MODULEINFO)); 45 | ctx.pEP = mi.EntryPoint; 46 | 47 | // Initialize the EP event 48 | ctx.hEvtEP = CreateEvent(NULL, TRUE, FALSE, NULL); 49 | 50 | // Set the actual hook 51 | DetourTransactionBegin(); 52 | DetourAttach(&ctx.pEP, &HookStubEP); 53 | DetourTransactionCommit(); 54 | } 55 | else 56 | { 57 | // Suspend any victim thread, in case of attaching 58 | // There should be no libDoll threads for now 59 | DollThreadSuspendAll(false); 60 | 61 | // Invalidate states meant for other situations 62 | ctx.pEP = NULL; 63 | ctx.hEvtEP = INVALID_HANDLE_VALUE; 64 | } 65 | 66 | // Initialize all the global contexts 67 | 68 | // Fetch server infomation stored by Monitor 69 | // NOTE: This pointer points to a static area in another module, do not "delete[] pServerInfo;" 70 | char* pServerInfo = DollDllFindServerInfo(); 71 | if (!pServerInfo) 72 | { 73 | DollThreadPanic(L"DollDllAttach(): No server information found"); 74 | return FALSE; 75 | } 76 | 77 | // The event object handle for informing hooked thread 78 | ctx.hEvtHookVerdict = CreateEvent(NULL, FALSE, FALSE, NULL); 79 | 80 | // The global lock for hooks 81 | InitializeCriticalSection(&ctx.lockHook); 82 | 83 | // _beginthread() returns a (uintptr_t)HANDLE to the created thread 84 | // i.e. the return value of CreateThread() 85 | 86 | // ThreadPuppet(TPuppet) establishes the connection to Controller 87 | uintptr_t hTPuppet = _beginthread(TPuppet, 0, pServerInfo); 88 | if (hTPuppet == 0 || hTPuppet == -1) // these status means error occurred 89 | { 90 | DollThreadPanic(L"DollDllAttach(): _beginthread(ThreadPuppet) failed"); 91 | return FALSE; 92 | } 93 | ctx.hTPuppet = (HANDLE)hTPuppet; 94 | 95 | return TRUE; 96 | } 97 | 98 | BOOL DollDllDetach() 99 | { 100 | // Register current thread to avoid hook happen in unhook process 101 | DollThreadRegisterCurrent(); 102 | 103 | // Clean up 104 | DollThreadSuspendAll(false); 105 | 106 | // Free all hooks but leave all entries, avoiding concurrent modification 107 | for (auto iter = ctx.dollHooks.cbegin(); iter != ctx.dollHooks.cend(); iter++) 108 | DollHookRemove(iter->first, false); 109 | ctx.dollHooks.clear(); 110 | 111 | // FIXME: TerminateThread() will cause huge resource leaks 112 | // Should inform TPuppet about DLL detachment 113 | TerminateThread(ctx.hTPuppet, 0); 114 | 115 | // For now no hook should exist, unregister self 116 | DollThreadUnregisterCurrent(); 117 | 118 | CloseHandle(ctx.hTPuppet); 119 | 120 | DeleteCriticalSection(&ctx.lockHook); 121 | 122 | CloseHandle(ctx.hEvtHookVerdict); 123 | 124 | DollThreadResumeAll(); 125 | return TRUE; 126 | } 127 | 128 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 129 | { 130 | switch (ul_reason_for_call) 131 | { 132 | case DLL_PROCESS_ATTACH: 133 | { 134 | // Restore IAT modified by inject procedure 135 | // It is here, not in DollDllAttach(), because IAT must be restored before any API call 136 | // If any error occurs, the injection might be done in ways other than DetourCreateProcessWithDllEx() 137 | BOOL isInjectedByDetours = DetourRestoreAfterWith(); 138 | 139 | // XXX: DisableThreadLibraryCalls() will interfere with a statically-linked CRT 140 | // https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-disablethreadlibrarycalls#remarks 141 | //DisableThreadLibraryCalls(hModule); 142 | 143 | return DollDllAttach(isInjectedByDetours); 144 | } 145 | case DLL_PROCESS_DETACH: 146 | { 147 | return DollDllDetach(); 148 | } 149 | case DLL_THREAD_ATTACH: 150 | case DLL_THREAD_DETACH: 151 | break; 152 | } 153 | return TRUE; 154 | } 155 | 156 | // DetourCreateProcessWithDllEx() requires at least 1 export function (ordinal #1) 157 | extern "C" int __declspec(dllexport) DollDllHelloWorld() 158 | { 159 | return 42; 160 | } -------------------------------------------------------------------------------- /libDoll/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include -------------------------------------------------------------------------------- /libDoll/libDoll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "pch.h" 3 | 4 | #include "../libPuppet/libPuppet.h" 5 | 6 | // UINT_PTR represents the machine's native word size as a unsigned integer type 7 | // See https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types#uint_ptr 8 | 9 | // The prototype of GetCurrentThreadId() 10 | typedef DWORD (__stdcall *GET_CURRENT_THREAD_ID)(); 11 | 12 | #pragma pack(push, 1) 13 | 14 | // The context of an active hook 15 | // This struct is not code-independent; will be visited by assembly code 16 | struct LIBDOLL_HOOK { 17 | 18 | // Members being used in assembly level 19 | 20 | UINT_PTR pTrampoline; 21 | UINT_PTR denySPOffset; 22 | UINT_PTR denyAX; 23 | UINT_PTR originalSP; 24 | 25 | // Dynamically allocated HookStubBefore* stubs 26 | 27 | char* pBeforeA; 28 | char* pBeforeB; 29 | char* pBeforeDeny; 30 | DWORD pBeforeAProtect; 31 | DWORD pBeforeBProtect; 32 | DWORD pBeforeDenyProtect; 33 | 34 | // Interop with TPuppet 35 | 36 | UINT_PTR* context; 37 | uint32_t verdict; 38 | 39 | }; 40 | 41 | #pragma pack(pop) 42 | 43 | struct LIBDOLL_CTX { 44 | 45 | Puppet::IPuppet* puppet; 46 | HANDLE hTPuppet; 47 | 48 | std::set dollThreads; 49 | std::set suspendedThreads; 50 | 51 | std::map dollHooks; 52 | HANDLE hEvtHookVerdict; 53 | UINT_PTR waitingHookOEP; 54 | CRITICAL_SECTION lockHook; 55 | 56 | void* pEP; 57 | HANDLE hEvtEP; 58 | 59 | GET_CURRENT_THREAD_ID pRealGetCurrentThreadId; 60 | 61 | }; 62 | 63 | // Global context 64 | extern LIBDOLL_CTX ctx; 65 | 66 | -------------------------------------------------------------------------------- /libDoll/libDoll.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EZForever/PEDoll/893c853d6bda6614b72077707928ead73e6f78df/libDoll/libDoll.rc -------------------------------------------------------------------------------- /libDoll/libDoll.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 头文件 20 | 21 | 22 | 头文件 23 | 24 | 25 | 头文件 26 | 27 | 28 | 头文件 29 | 30 | 31 | 头文件 32 | 33 | 34 | 头文件 35 | 36 | 37 | 头文件 38 | 39 | 40 | 头文件 41 | 42 | 43 | 44 | 45 | 源文件 46 | 47 | 48 | 源文件 49 | 50 | 51 | 源文件 52 | 53 | 54 | 源文件 55 | 56 | 57 | 源文件 58 | 59 | 60 | 源文件 61 | 62 | 63 | 源文件 64 | 65 | 66 | 67 | 68 | 源文件 69 | 70 | 71 | 源文件 72 | 73 | 74 | 75 | 76 | 资源文件 77 | 78 | 79 | -------------------------------------------------------------------------------- /libDoll/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: 与预编译标头对应的源文件 2 | 3 | #include "pch.h" 4 | 5 | // 当使用预编译的头时,需要使用此源文件,编译才能成功。 6 | -------------------------------------------------------------------------------- /libDoll/pch.h: -------------------------------------------------------------------------------- 1 | #ifndef PCH_H 2 | #define PCH_H 3 | 4 | #include "framework.h" 5 | 6 | #endif //PCH_H 7 | -------------------------------------------------------------------------------- /libDoll/resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EZForever/PEDoll/893c853d6bda6614b72077707928ead73e6f78df/libDoll/resource.h -------------------------------------------------------------------------------- /libPuppet/PuppetClientTCP.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "PuppetClientTCP.h" 3 | 4 | namespace Puppet { 5 | 6 | # define ASSERT(expr, msg) assert((expr), "PuppetClientTCP::" msg) 7 | 8 | void PuppetClientTCP::assert(bool expr, const char* msg) { 9 | if (expr) 10 | return; 11 | 12 | lastError = WSAGetLastError(); 13 | throw std::runtime_error(msg); 14 | } 15 | 16 | PuppetClientTCP::PuppetClientTCP(int port, const char* host, bool ipv6) 17 | : clientSocket(INVALID_SOCKET) 18 | { 19 | int ret; 20 | 21 | // According to MSDN, multiple calls to WSAStartup() is fine, as long as keep balance with WSACleanup() 22 | // Calling WSAStartup() inside of DllMain() is not recommended though, 23 | // since WSAStartup() may load other DLLs and may cause deadlocks 24 | ret = WSAStartup(MAKEWORD(2, 2), &wsa); 25 | ASSERT(!ret, "(): WSAStartup() failed"); 26 | 27 | clientSocket = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0); 28 | ASSERT(clientSocket != INVALID_SOCKET, "(): socket() failed"); 29 | 30 | if (ipv6) 31 | { 32 | SOCKADDR_IN6* a = new SOCKADDR_IN6{ 0 }; 33 | a->sin6_family = AF_INET6; 34 | a->sin6_port = htons(port); 35 | 36 | if (!host) 37 | host = "::1"; 38 | ret = inet_pton(AF_INET6, host, &a->sin6_addr); 39 | ASSERT(ret, "(): Invalid host"); 40 | 41 | addr = (SOCKADDR*)a; 42 | addrSize = sizeof(*a); 43 | } 44 | else 45 | { 46 | SOCKADDR_IN* a = new SOCKADDR_IN{ 0 }; 47 | a->sin_family = AF_INET; 48 | a->sin_port = htons(port); 49 | 50 | if (!host) 51 | host = "127.0.0.1"; 52 | ret = inet_pton(AF_INET, host, &a->sin_addr); 53 | ASSERT(ret, "(): Invalid host"); 54 | 55 | addr = (SOCKADDR*)a; 56 | addrSize = sizeof(*a); 57 | } 58 | 59 | ret = ::connect(clientSocket, addr, addrSize); // Blocks 60 | ASSERT(!ret, "connect(): connect() failed"); 61 | } 62 | 63 | PuppetClientTCP::~PuppetClientTCP() 64 | { 65 | if (clientSocket != INVALID_SOCKET) { 66 | shutdown(clientSocket, SD_BOTH); 67 | closesocket(clientSocket); 68 | } 69 | 70 | delete addr; // XXX: Will type confusion cause something here? 71 | WSACleanup(); 72 | 73 | // __declspec(nothrow) is default on destructors - warning C2497 74 | //ASSERT(!WSACleanup(), "~(): WSACleanup() failed"); 75 | } 76 | 77 | void PuppetClientTCP::send(const PACKET& packet) 78 | { 79 | ASSERT(clientSocket != INVALID_SOCKET, "send(): Connection not established"); 80 | 81 | int ret; 82 | 83 | ret = ::send(clientSocket, (const char*)&packet, packet.size, 0); 84 | ASSERT(ret == packet.size, "send(): send() failed"); 85 | } 86 | 87 | PACKET* PuppetClientTCP::recv() 88 | { 89 | ASSERT(clientSocket != INVALID_SOCKET, "recv(): Connection not established"); 90 | 91 | int ret; 92 | 93 | // Get packet size first 94 | 95 | decltype(PACKET::size) size; 96 | ret = ::recv(clientSocket, (char*)&size, sizeof(size), 0); // Blocks 97 | ASSERT(ret == sizeof(size), "recv(): recv() failed"); 98 | 99 | // Then get rest of the data 100 | 101 | char* packet = new char[size]; 102 | ((PACKET*)packet)->size = size; 103 | 104 | ret = ::recv(clientSocket, packet + sizeof(size), size - sizeof(size), 0); 105 | ASSERT(ret == size - sizeof(size), "recv(): recv() failed"); 106 | 107 | return (PACKET*)packet; // XXX: Will type confusion cause something here? 108 | } 109 | 110 | 111 | PuppetClientTCP* ClientTCPInitialize(const char* serverInfo) 112 | { 113 | size_t serverInfoSize = strlen(serverInfo); 114 | char* str = new char[serverInfoSize + 1]; 115 | strcpy_s(str, serverInfoSize + 1, serverInfo); 116 | 117 | int port = DEFAULT_PORT; 118 | bool ipv6 = false; 119 | char* pSpr = strrchr(str, '.'); 120 | if (pSpr) 121 | { 122 | // If '.' is found, this must be an IPv4 address 123 | pSpr = strrchr(str, ':'); 124 | if (pSpr) 125 | { 126 | // serverInfo == L"$host:$port" 127 | *pSpr++ = 0; 128 | port = strtol(pSpr, NULL, 10); 129 | } 130 | // Otherwise serverInfo == L"$host" 131 | } 132 | else 133 | { 134 | ipv6 = true; 135 | pSpr = strrchr(str, ']'); 136 | if (pSpr) 137 | { 138 | // serverInfo == L"[$v6host]:$port" 139 | *pSpr++ = 0; // Remove ']' 140 | memmove(str, str + 1, strlen(str) - 1); // Remove '[' 141 | *pSpr++ = 0; // Remove ':' 142 | port = strtol(pSpr, NULL, 10); 143 | } 144 | // Otherwise serverInfo == L"$v6host" 145 | } 146 | 147 | PuppetClientTCP* puppet = NULL; 148 | try { 149 | puppet = new Puppet::PuppetClientTCP(port, str, ipv6); 150 | } 151 | catch (const std::runtime_error &) { 152 | delete puppet; 153 | puppet = NULL; 154 | throw; 155 | } 156 | 157 | delete[] str; 158 | return puppet; 159 | } 160 | 161 | } -------------------------------------------------------------------------------- /libPuppet/PuppetClientTCP.h: -------------------------------------------------------------------------------- 1 | // PuppetClientTCP.h 2 | // Forward TCP Client for Puppet Protocol 3 | #pragma once 4 | #include "pch.h" 5 | #include 6 | #include // For some reason IPv6 things are here 7 | #include "libPuppet.h" 8 | 9 | namespace Puppet { 10 | 11 | // Forward TCP Client for Puppet Protocol 12 | class PuppetClientTCP : public IPuppet 13 | { 14 | private: 15 | WSADATA wsa; 16 | SOCKADDR* addr; 17 | int addrSize; 18 | SOCKET clientSocket; 19 | 20 | void assert(bool expr, const char* msg); 21 | 22 | public: 23 | PuppetClientTCP(int port, const char* host = NULL, bool ipv6 = false); 24 | ~PuppetClientTCP(); 25 | 26 | // IPuppet implemention 27 | 28 | void send(const PACKET& packet); 29 | PACKET* recv(); 30 | }; 31 | 32 | // Construct a PuppetClientTCP instance from a serverInfo string 33 | // e.g. "127.0.0.1", "127.0.0.1:12345", "::1", "[::1]:12345" 34 | PuppetClientTCP* ClientTCPInitialize(const char* serverInfo); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /libPuppet/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | -------------------------------------------------------------------------------- /libPuppet/libPuppet.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "libPuppet.h" 3 | 4 | namespace Puppet { 5 | 6 | const GUID PAYLOAD_SERVER_INFO = { 0xa2062469, 0x2b45, 0x496d, { 0x8f, 0xe9, 0x7e, 0x89, 0x4e, 0xd7, 0x22, 0x70 } }; 7 | 8 | const int DEFAULT_PORT = 31415; 9 | 10 | PACKET_STRING* PacketAllocString(const wchar_t* data) 11 | { 12 | uint32_t packetSize = (uint32_t)(sizeof(PACKET_STRING) + sizeof(wchar_t) * (wcslen(data) + 1)); 13 | PACKET_STRING* packet = (PACKET_STRING*)new char[packetSize]; 14 | new (packet) PACKET_STRING; // Call constructor to initialize header 15 | packet->size = packetSize; 16 | wcscpy_s(packet->data, wcslen(data) + 1, data); 17 | return packet; 18 | } 19 | 20 | PACKET_BINARY* PacketAllocBinary(const unsigned char* data, uint32_t size) 21 | { 22 | uint32_t packetSize = (uint32_t)(sizeof(PACKET_BINARY) + size); 23 | PACKET_BINARY* packet = (PACKET_BINARY*)new char[packetSize]; 24 | new (packet) PACKET_BINARY; // Call constructor to initialize header 25 | packet->size = packetSize; 26 | memcpy(packet->data, data, size); 27 | return packet; 28 | } 29 | 30 | void PacketFree(PACKET* packet) 31 | { 32 | delete[] (char*)packet; 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /libPuppet/libPuppet.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 头文件 20 | 21 | 22 | 头文件 23 | 24 | 25 | 头文件 26 | 27 | 28 | 头文件 29 | 30 | 31 | 32 | 33 | 源文件 34 | 35 | 36 | 源文件 37 | 38 | 39 | 源文件 40 | 41 | 42 | -------------------------------------------------------------------------------- /libPuppet/pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | -------------------------------------------------------------------------------- /libPuppet/pch.h: -------------------------------------------------------------------------------- 1 | #ifndef PCH_H 2 | #define PCH_H 3 | 4 | #include "framework.h" 5 | 6 | #endif //PCH_H 7 | --------------------------------------------------------------------------------