├── M2TeamCommonLibrary
├── M2MessageDialogResource.rc
├── M2MessageDialogResource.h
├── M2.SDK.h
├── M2TeamCommonLibrary.vcxitems
├── M2.Base.cpp
├── M2Win32GUIHelpers.h
├── M2TeamCommonLibrary.vcxitems.filters
├── M2Win32GUIHelpers.cpp
├── M2.Base.h
├── M2.NSudo.h
└── M2WindowsHelpers.cpp
├── M2TeamCommonLibrary.sln
├── LICENSE
├── .gitattributes
└── .gitignore
/M2TeamCommonLibrary/M2MessageDialogResource.rc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M2TeamArchived/M2TeamCommonLibrary/HEAD/M2TeamCommonLibrary/M2MessageDialogResource.rc
--------------------------------------------------------------------------------
/M2TeamCommonLibrary/M2MessageDialogResource.h:
--------------------------------------------------------------------------------
1 | /*
2 | * PROJECT: M2-Team Common Library
3 | * FILE: M2MessageDialogResource.h
4 | * PURPOSE: Definition for the message dialog resource
5 | *
6 | * LICENSE: The MIT License
7 | *
8 | * DEVELOPER: Mouri_Naruto (Mouri_Naruto AT Outlook.com)
9 | */
10 |
11 | #define IDD_MESSAGE_DIALOG 105
12 | #define IDC_MESSAGE_DIALOG_EDIT 1003
13 |
--------------------------------------------------------------------------------
/M2TeamCommonLibrary.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28307.329
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "M2TeamCommonLibrary", "M2TeamCommonLibrary\M2TeamCommonLibrary.vcxitems", "{956CAD19-1F11-4656-8664-6837AA67CD87}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documents", "Documents", "{AF6177FD-314C-44B4-A3D0-552F08FD3545}"
9 | ProjectSection(SolutionItems) = preProject
10 | LICENSE = LICENSE
11 | EndProjectSection
12 | EndProject
13 | Global
14 | GlobalSection(SolutionProperties) = preSolution
15 | HideSolutionNode = FALSE
16 | EndGlobalSection
17 | GlobalSection(ExtensibilityGlobals) = postSolution
18 | SolutionGuid = {0FF7B3A9-96BE-4CEF-BB1A-104BBE844B1D}
19 | EndGlobalSection
20 | EndGlobal
21 |
--------------------------------------------------------------------------------
/M2TeamCommonLibrary/M2.SDK.h:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | 项目:M2-SDK
3 | 描述:M2-SDK 包含用头文件
4 | 文件名:M2.SDK.h
5 | 基于项目:无
6 | 许可协议:看顶层目录的 License.txt
7 | 建议的Windows SDK版本:10.0.10586及以后
8 |
9 | Project: M2-SDK
10 | Description: Header file for including M2-SDK
11 | Filename: M2.SDK.h
12 | License: See License.txt in the top level directory
13 | Recommend Minimum Windows SDK Version: 10.0.10586
14 | ******************************************************************************/
15 |
16 | #pragma once
17 |
18 | #ifndef _M2_SDK_
19 | #define _M2_SDK_
20 |
21 | /*
22 | Windows API 基本定义
23 | Base Windows API Definitions
24 | */
25 | #include "MINT.h"
26 |
27 | /*
28 | M2-SDK 基本定义
29 | Base M2-SDK Definitions
30 | */
31 | #include "M2.Base.h"
32 |
33 | /*
34 | NSudo库
35 | NSudo Library
36 | */
37 | #include "M2.NSudo.h"
38 |
39 | #endif
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) M2-Team and Contributors. All rights reserved.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/M2TeamCommonLibrary/M2TeamCommonLibrary.vcxitems:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
5 | true
6 | {956cad19-1f11-4656-8664-6837aa67cd87}
7 |
8 |
9 |
10 | %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/M2TeamCommonLibrary/M2.Base.cpp:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | 项目:M2-SDK
3 | 描述:M2-SDK 基本定义实现
4 | 文件名:M2.Base.cpp
5 | 许可协议:看顶层目录的 License.txt
6 | 建议的最低 Windows SDK 版本:10.0.10586
7 | 提示:无
8 |
9 | Project: M2-SDK
10 | Description: Implemention of Base M2-SDK Definitions
11 | Filename: M2.Base.cpp
12 | License: See License.txt in the top level directory
13 | Recommend Minimum Windows SDK Version: 10.0.10586
14 | Tips: N/A
15 | ******************************************************************************/
16 |
17 | #include "M2.Windows.h" // Windows API 基本定义
18 | #include "M2.Base.h" // M2-SDK 基本定义
19 |
20 | namespace M2
21 | {
22 | // 创建事件对象, 不内联考虑到大量使用本函数时实现函数复用以节约空间
23 | NTSTATUS WINAPI M2CreateEvent(
24 | _Out_ PHANDLE phEvent,
25 | _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,
26 | _In_ BOOL bManualReset,
27 | _In_ BOOL bInitialState,
28 | _In_opt_ LPCWSTR lpName)
29 | {
30 | OBJECT_ATTRIBUTES ObjectAttributes;
31 | UNICODE_STRING NtFileName;
32 |
33 | M2InitObjectAttributes(ObjectAttributes);
34 |
35 | if (lpEventAttributes &&
36 | lpEventAttributes->nLength == sizeof(SECURITY_ATTRIBUTES))
37 | {
38 | if (lpEventAttributes->bInheritHandle)
39 | ObjectAttributes.Attributes = OBJ_INHERIT;
40 | ObjectAttributes.SecurityDescriptor =
41 | lpEventAttributes->lpSecurityDescriptor;
42 | }
43 |
44 | if (lpName)
45 | {
46 | M2InitUnicodeString(NtFileName, (PWSTR)lpName);
47 | ObjectAttributes.ObjectName = &NtFileName;
48 | }
49 |
50 | return NtCreateEvent(
51 | phEvent,
52 | EVENT_ALL_ACCESS,
53 | &ObjectAttributes,
54 | bManualReset ? NotificationEvent : SynchronizationEvent,
55 | (BOOLEAN)bInitialState);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/M2TeamCommonLibrary/M2Win32GUIHelpers.h:
--------------------------------------------------------------------------------
1 | /*
2 | * PROJECT: M2-Team Common Library
3 | * FILE: M2Win32GUIHelpers.h
4 | * PURPOSE: Definition for the Win32 desktop GUI helper functions
5 | *
6 | * LICENSE: The MIT License
7 | *
8 | * DEVELOPER: Mouri_Naruto (Mouri_Naruto AT Outlook.com)
9 | */
10 |
11 | #pragma once
12 |
13 | #ifndef _M2_WIN32_GUI_HELPERS_
14 | #define _M2_WIN32_GUI_HELPERS_
15 |
16 | #include "M2WindowsHelpers.h"
17 |
18 | /**
19 | * Creates and shows the message dialog.
20 | *
21 | * @param hInstance A handle to the module which contains the message dialog
22 | * resource. If this parameter is nullptr, then the current
23 | * executable is used.
24 | * @param hWndParent A handle to the window that owns the message dialog.
25 | * @param lpIconName Pointer that references the icon to be displayed in the
26 | * message dialog. If this parameter is nullptr or the
27 | * hInstance parameter is nullptr, no icon will be displayed.
28 | * This parameter must be an integer resource identifier
29 | * passed to the MAKEINTRESOURCE macro.
30 | * @param lpTitle Pointer to the string to be used for the message dialog
31 | * title. This parameter is a null-terminated, Unicode string.
32 | * @param lpContent Pointer to the string to be used for the message dialog
33 | * content. This parameter is a null-terminated, Unicode
34 | * string.
35 | * @return If the function succeeds, the return value is the value of the
36 | * nResult parameter specified in the call to the EndDialog function
37 | * used to terminate the message dialog. If the function fails because
38 | * the hWndParent parameter is invalid, the return value is zero. The
39 | * function returns zero in this case for compatibility with previous
40 | * versions of Windows. If the function fails for any other reason, the
41 | * return value is –1. To get extended error information, call
42 | * GetLastError.
43 | */
44 | INT_PTR WINAPI M2MessageDialog(
45 | _In_opt_ HINSTANCE hInstance,
46 | _In_opt_ HWND hWndParent,
47 | _In_opt_ LPCWSTR lpIconName,
48 | _In_ LPCWSTR lpTitle,
49 | _In_ LPCWSTR lpContent);
50 |
51 | #endif // _M2_WIN32_GUI_HELPERS_
52 |
--------------------------------------------------------------------------------
/M2TeamCommonLibrary/M2TeamCommonLibrary.vcxitems.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {c3376011-3a37-453f-8b61-e247c38d599a}
6 |
7 |
8 | {78390f38-88c8-4b68-8465-c8bab293c8a9}
9 |
10 |
11 | {3591d6a3-24d5-4139-a102-8b037e3e6ad9}
12 |
13 |
14 | {3c38a529-bf06-4caa-bd26-27c27c375f63}
15 |
16 |
17 |
18 |
19 | M2Win32GUIHelpers
20 |
21 |
22 | LegacyImplementations
23 |
24 |
25 | M2WindowsHelpers
26 |
27 |
28 |
29 |
30 | M2Win32GUIHelpers
31 |
32 |
33 | M2Win32GUIHelpers
34 |
35 |
36 | MINT
37 |
38 |
39 | LegacyImplementations
40 |
41 |
42 | LegacyImplementations
43 |
44 |
45 | LegacyImplementations
46 |
47 |
48 | M2WindowsHelpers
49 |
50 |
51 |
52 |
53 | M2Win32GUIHelpers
54 |
55 |
56 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/M2TeamCommonLibrary/M2Win32GUIHelpers.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * PROJECT: M2-Team Common Library
3 | * FILE: M2Win32GUIHelpers.cpp
4 | * PURPOSE: Implementation for the Win32 desktop GUI helper functions
5 | *
6 | * LICENSE: The MIT License
7 | *
8 | * DEVELOPER: Mouri_Naruto (Mouri_Naruto AT Outlook.com)
9 | */
10 |
11 | #include "stdafx.h"
12 |
13 | #include
14 |
15 | #include "M2Win32GUIHelpers.h"
16 | #include "M2MessageDialogResource.h"
17 |
18 | /**
19 | * The parameter struct of the message dialog.
20 | */
21 | struct DIALOG_BOX_PARAM
22 | {
23 | HINSTANCE hInstance;
24 | LPCWSTR lpIconName;
25 | LPCWSTR lpTitle;
26 | LPCWSTR lpContent;
27 | };
28 |
29 | /**
30 | * The callback function used of the message dialog.
31 | *
32 | * @param hwndDlg A handle to the message dialog.
33 | * @param uMsg The message.
34 | * @param wParam Additional message-specific information.
35 | * @param lParam Additional message-specific information.
36 | * @return Typically, the dialog box procedure should return TRUE if it
37 | * processed the message, and FALSE if it did not. If the dialog box
38 | * procedure returns FALSE, the dialog manager performs the default
39 | * dialog operation in response to the message.
40 | */
41 | INT_PTR CALLBACK M2MessageDialogDialogCallBack(
42 | _In_ HWND hwndDlg,
43 | _In_ UINT uMsg,
44 | _In_ WPARAM wParam,
45 | _In_ LPARAM lParam)
46 | {
47 | UNREFERENCED_PARAMETER(lParam);
48 |
49 | if (WM_INITDIALOG == uMsg)
50 | {
51 | HICON hIcon = reinterpret_cast(LoadImageW(
52 | reinterpret_cast(lParam)->hInstance,
53 | reinterpret_cast(lParam)->lpIconName,
54 | IMAGE_ICON,
55 | 256,
56 | 256,
57 | LR_SHARED));
58 | if (nullptr != hIcon)
59 | {
60 | SendMessageW(
61 | hwndDlg,
62 | WM_SETICON,
63 | ICON_SMALL,
64 | reinterpret_cast(hIcon));
65 | SendMessageW(
66 | hwndDlg,
67 | WM_SETICON,
68 | ICON_BIG,
69 | reinterpret_cast(hIcon));
70 | }
71 |
72 | SetWindowTextW(
73 | hwndDlg,
74 | reinterpret_cast(lParam)->lpTitle);
75 | SetWindowTextW(
76 | GetDlgItem(hwndDlg, IDC_MESSAGE_DIALOG_EDIT),
77 | reinterpret_cast(lParam)->lpContent);
78 |
79 | return (INT_PTR)TRUE;
80 | }
81 | else if (
82 | (WM_CLOSE == uMsg) ||
83 | (WM_COMMAND == uMsg && IDOK == LOWORD(wParam)))
84 | {
85 | EndDialog(hwndDlg, 0);
86 | }
87 |
88 | return FALSE;
89 | }
90 |
91 | /**
92 | * Creates and shows the message dialog.
93 | *
94 | * @param hInstance A handle to the module which contains the message dialog
95 | * resource. If this parameter is nullptr, then the current
96 | * executable is used.
97 | * @param hWndParent A handle to the window that owns the message dialog.
98 | * @param lpIconName Pointer that references the icon to be displayed in the
99 | * message dialog. If this parameter is nullptr or the
100 | * hInstance parameter is nullptr, no icon will be displayed.
101 | * This parameter must be an integer resource identifier
102 | * passed to the MAKEINTRESOURCE macro.
103 | * @param lpTitle Pointer to the string to be used for the message dialog
104 | * title. This parameter is a null-terminated, Unicode string.
105 | * @param lpContent Pointer to the string to be used for the message dialog
106 | * content. This parameter is a null-terminated, Unicode
107 | * string.
108 | * @return If the function succeeds, the return value is the value of the
109 | * nResult parameter specified in the call to the EndDialog function
110 | * used to terminate the message dialog. If the function fails because
111 | * the hWndParent parameter is invalid, the return value is zero. The
112 | * function returns zero in this case for compatibility with previous
113 | * versions of Windows. If the function fails for any other reason, the
114 | * return value is –1. To get extended error information, call
115 | * GetLastError.
116 | */
117 | INT_PTR WINAPI M2MessageDialog(
118 | _In_opt_ HINSTANCE hInstance,
119 | _In_opt_ HWND hWndParent,
120 | _In_opt_ LPCWSTR lpIconName,
121 | _In_ LPCWSTR lpTitle,
122 | _In_ LPCWSTR lpContent)
123 | {
124 | DIALOG_BOX_PARAM Param = { hInstance, lpIconName,lpTitle,lpContent };
125 |
126 | M2EnablePerMonitorDialogScaling();
127 |
128 | return DialogBoxParamW(
129 | hInstance,
130 | MAKEINTRESOURCEW(IDD_MESSAGE_DIALOG),
131 | hWndParent,
132 | M2MessageDialogDialogCallBack,
133 | reinterpret_cast(&Param));
134 | }
135 |
136 |
137 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 | *.VC.VC.opendb
85 |
86 | # Visual Studio profiler
87 | *.psess
88 | *.vsp
89 | *.vspx
90 | *.sap
91 |
92 | # TFS 2012 Local Workspace
93 | $tf/
94 |
95 | # Guidance Automation Toolkit
96 | *.gpState
97 |
98 | # ReSharper is a .NET coding add-in
99 | _ReSharper*/
100 | *.[Rr]e[Ss]harper
101 | *.DotSettings.user
102 |
103 | # JustCode is a .NET coding add-in
104 | .JustCode
105 |
106 | # TeamCity is a build add-in
107 | _TeamCity*
108 |
109 | # DotCover is a Code Coverage Tool
110 | *.dotCover
111 |
112 | # NCrunch
113 | _NCrunch_*
114 | .*crunch*.local.xml
115 | nCrunchTemp_*
116 |
117 | # MightyMoose
118 | *.mm.*
119 | AutoTest.Net/
120 |
121 | # Web workbench (sass)
122 | .sass-cache/
123 |
124 | # Installshield output folder
125 | [Ee]xpress/
126 |
127 | # DocProject is a documentation generator add-in
128 | DocProject/buildhelp/
129 | DocProject/Help/*.HxT
130 | DocProject/Help/*.HxC
131 | DocProject/Help/*.hhc
132 | DocProject/Help/*.hhk
133 | DocProject/Help/*.hhp
134 | DocProject/Help/Html2
135 | DocProject/Help/html
136 |
137 | # Click-Once directory
138 | publish/
139 |
140 | # Publish Web Output
141 | *.[Pp]ublish.xml
142 | *.azurePubxml
143 | # TODO: Comment the next line if you want to checkin your web deploy settings
144 | # but database connection strings (with potential passwords) will be unencrypted
145 | *.pubxml
146 | *.publishproj
147 |
148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
149 | # checkin your Azure Web App publish settings, but sensitive information contained
150 | # in these scripts will be unencrypted
151 | PublishScripts/
152 |
153 | # NuGet Packages
154 | *.nupkg
155 | # The packages folder can be ignored because of Package Restore
156 | **/packages/*
157 | # except build/, which is used as an MSBuild target.
158 | !**/packages/build/
159 | # Uncomment if necessary however generally it will be regenerated when needed
160 | #!**/packages/repositories.config
161 | # NuGet v3's project.json files produces more ignoreable files
162 | *.nuget.props
163 | *.nuget.targets
164 |
165 | # Microsoft Azure Build Output
166 | csx/
167 | *.build.csdef
168 |
169 | # Microsoft Azure Emulator
170 | ecf/
171 | rcf/
172 |
173 | # Windows Store app package directories and files
174 | AppPackages/
175 | BundleArtifacts/
176 | Package.StoreAssociation.xml
177 | _pkginfo.txt
178 |
179 | # Visual Studio cache files
180 | # files ending in .cache can be ignored
181 | *.[Cc]ache
182 | # but keep track of directories ending in .cache
183 | !*.[Cc]ache/
184 |
185 | # Others
186 | ClientBin/
187 | ~$*
188 | *~
189 | *.dbmdl
190 | *.dbproj.schemaview
191 | *.pfx
192 | *.publishsettings
193 | node_modules/
194 | orleans.codegen.cs
195 |
196 | # Since there are multiple workflows, uncomment next line to ignore bower_components
197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
198 | #bower_components/
199 |
200 | # RIA/Silverlight projects
201 | Generated_Code/
202 |
203 | # Backup & report files from converting an old project file
204 | # to a newer Visual Studio version. Backup files are not needed,
205 | # because we have git ;-)
206 | _UpgradeReport_Files/
207 | Backup*/
208 | UpgradeLog*.XML
209 | UpgradeLog*.htm
210 |
211 | # SQL Server files
212 | *.mdf
213 | *.ldf
214 |
215 | # Business Intelligence projects
216 | *.rdl.data
217 | *.bim.layout
218 | *.bim_*.settings
219 |
220 | # Microsoft Fakes
221 | FakesAssemblies/
222 |
223 | # GhostDoc plugin setting file
224 | *.GhostDoc.xml
225 |
226 | # Node.js Tools for Visual Studio
227 | .ntvs_analysis.dat
228 |
229 | # Visual Studio 6 build log
230 | *.plg
231 |
232 | # Visual Studio 6 workspace options file
233 | *.opt
234 |
235 | # Visual Studio LightSwitch build output
236 | **/*.HTMLClient/GeneratedArtifacts
237 | **/*.DesktopClient/GeneratedArtifacts
238 | **/*.DesktopClient/ModelManifest.xml
239 | **/*.Server/GeneratedArtifacts
240 | **/*.Server/ModelManifest.xml
241 | _Pvt_Extensions
242 |
243 | # Paket dependency manager
244 | .paket/paket.exe
245 | paket-files/
246 |
247 | # FAKE - F# Make
248 | .fake/
249 |
250 | # JetBrains Rider
251 | .idea/
252 | *.sln.iml
253 |
--------------------------------------------------------------------------------
/M2TeamCommonLibrary/M2.Base.h:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | 项目:M2-SDK
3 | 描述:M2-SDK 基本定义
4 | 文件名:M2.Base.h
5 | 许可协议:看顶层目录的 License.txt
6 | 建议的最低 Windows SDK 版本:10.0.10586
7 | 提示:无
8 |
9 | Project: M2-SDK
10 | Description: Base M2-SDK Definitions
11 | Filename: M2.Base.h
12 | License: See License.txt in the top level directory
13 | Recommend Minimum Windows SDK Version: 10.0.10586
14 | Tips: N/A
15 | ******************************************************************************/
16 |
17 | #pragma once
18 |
19 | #ifndef _M2_BASE_
20 | #define _M2_BASE_
21 |
22 | #ifdef __cplusplus
23 |
24 | namespace M2
25 | {
26 | // 初始化OBJECT_ATTRIBUTES结构
27 | FORCEINLINE void M2InitObjectAttributes(
28 | _Out_ OBJECT_ATTRIBUTES& ObjectAttributes,
29 | _In_ PUNICODE_STRING ObjectName = nullptr,
30 | _In_ ULONG Attributes = 0,
31 | _In_ HANDLE RootDirectory = nullptr,
32 | _In_ PVOID SecurityDescriptor = nullptr,
33 | _In_ PVOID SecurityQualityOfService = nullptr)
34 | {
35 | ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
36 | ObjectAttributes.RootDirectory = RootDirectory;
37 | ObjectAttributes.ObjectName = ObjectName;
38 | ObjectAttributes.Attributes = Attributes;
39 | ObjectAttributes.SecurityDescriptor = SecurityDescriptor;
40 | ObjectAttributes.SecurityQualityOfService = SecurityQualityOfService;
41 | }
42 |
43 | // 初始化SECURITY_QUALITY_OF_SERVICE结构
44 | FORCEINLINE void M2InitSecurityQuailtyOfService(
45 | _Out_ SECURITY_QUALITY_OF_SERVICE& SecurityQuailtyOfService,
46 | _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
47 | _In_ SECURITY_CONTEXT_TRACKING_MODE ContextTrackingMode,
48 | _In_ BOOLEAN EffectiveOnly)
49 | {
50 | SecurityQuailtyOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
51 | SecurityQuailtyOfService.ImpersonationLevel = ImpersonationLevel;
52 | SecurityQuailtyOfService.ContextTrackingMode = ContextTrackingMode;
53 | SecurityQuailtyOfService.EffectiveOnly = EffectiveOnly;
54 | }
55 |
56 | // 初始化CLIENT_ID结构
57 | FORCEINLINE void M2InitClientID(
58 | _Out_ CLIENT_ID& ClientID,
59 | _In_opt_ DWORD ProcessID,
60 | _In_opt_ DWORD ThreadID)
61 | {
62 | ClientID.UniqueProcess = UlongToHandle(ProcessID);
63 | ClientID.UniqueThread = UlongToHandle(ThreadID);
64 | }
65 |
66 | // 获取KUSER_SHARED_DATA结构
67 | FORCEINLINE PKUSER_SHARED_DATA M2GetKUserSharedData()
68 | {
69 | return ((PKUSER_SHARED_DATA const)0x7ffe0000);
70 | }
71 |
72 | // 获取当前系统会话号
73 | FORCEINLINE DWORD M2GetCurrentSessionID()
74 | {
75 | return M2GetKUserSharedData()->ActiveConsoleId;
76 | }
77 |
78 | // GetLastError()的未公开内联实现
79 | FORCEINLINE DWORD M2GetLastError()
80 | {
81 | return NtCurrentTeb()->LastErrorValue;
82 | }
83 |
84 | // SetLastError()的未公开内联实现
85 | FORCEINLINE VOID M2SetLastError(_In_ DWORD dwErrCode)
86 | {
87 | if (NtCurrentTeb()->LastErrorValue != dwErrCode)
88 | NtCurrentTeb()->LastErrorValue = dwErrCode;
89 | }
90 |
91 | // 在默认堆上分配内存
92 | FORCEINLINE PVOID M2HeapAlloc(
93 | _In_ SIZE_T Size)
94 | {
95 | return RtlAllocateHeap(RtlProcessHeap(), 0, Size);
96 | }
97 |
98 | // 在默认堆上释放内存
99 | FORCEINLINE VOID M2HeapFree(
100 | _In_ PVOID BaseAddress)
101 | {
102 | RtlFreeHeap(RtlProcessHeap(), 0, BaseAddress);
103 | }
104 |
105 | // 分配初始化为零的内存
106 | FORCEINLINE PVOID M2AllocZeroedMemory(
107 | _In_ size_t Size)
108 | {
109 | return RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, Size);
110 | }
111 |
112 | // 初始化UNICODE_STRING结构
113 | template
114 | FORCEINLINE void M2InitUnicodeString(
115 | _Out_ UNICODE_STRING &Destination,
116 | _In_opt_ StringType Source)
117 | {
118 | Destination.Length =
119 | (USHORT)(Source ? (wcslen(Source) * sizeof(WCHAR)) : 0);
120 | Destination.MaximumLength =
121 | (USHORT)(Source ? ((wcslen(Source) + 1) * sizeof(WCHAR)) : 0);
122 | Destination.Buffer =
123 | (PWCH)(Source ? Source : nullptr);
124 | }
125 |
126 | // 初始化STRING结构
127 | template
128 | FORCEINLINE void M2InitString(
129 | _Out_ STRING &Destination,
130 | _In_opt_ StringType Source)
131 | {
132 | Destination.Length =
133 | (USHORT)(Source ? (strlen(Source) * sizeof(CHAR)) : 0);
134 | Destination.MaximumLength =
135 | (USHORT)(Source ? ((strlen(Source) + 1) * sizeof(CHAR)) : 0);
136 | Destination.Buffer =
137 | (PCHAR)(Source ? Source : nullptr);
138 | }
139 |
140 | // 通过直接访问PEB结构获取当前进程模块,以替代GetModuleHandleW(NULL)
141 | FORCEINLINE HMODULE M2GetCurrentModuleHandle()
142 | {
143 | return reinterpret_cast(NtCurrentPeb()->ImageBaseAddress);
144 | }
145 |
146 | // 加载特定模块到当前进程内存
147 | FORCEINLINE NTSTATUS M2LoadModule(
148 | _Out_ PVOID &ModuleHandle,
149 | _In_ LPCWSTR ModuleFileName)
150 | {
151 | UNICODE_STRING usDllName;
152 | M2InitUnicodeString(usDllName, ModuleFileName);
153 | return LdrLoadDll(nullptr, nullptr, &usDllName, &ModuleHandle);
154 | }
155 |
156 | // 释放已加载到内存的特定模块或减少引用计数
157 | FORCEINLINE NTSTATUS M2FreeModule(
158 | _Out_ PVOID ModuleHandle)
159 | {
160 | return LdrUnloadDll(ModuleHandle);
161 | }
162 |
163 | // 获取已加载到内存的特定模块的句柄
164 | FORCEINLINE NTSTATUS M2GetModuleHandle(
165 | _Out_ PVOID &ModuleHandle,
166 | _In_ LPCWSTR ModuleFileName)
167 | {
168 | UNICODE_STRING usDllName;
169 | M2InitUnicodeString(usDllName, ModuleFileName);
170 | return LdrGetDllHandleEx(
171 | 0, nullptr, nullptr, &usDllName, &ModuleHandle);
172 | }
173 |
174 | // 获取特定模块的特定导出函数地址
175 | template
176 | FORCEINLINE NTSTATUS M2GetProcedureAddress(
177 | _Out_ ProcedureType &ProcedureAddress,
178 | _In_ PVOID ModuleHandle,
179 | _In_ LPCSTR ProcedureName)
180 | {
181 | ANSI_STRING asProcedureName;
182 | M2InitString(asProcedureName, ProcedureName);
183 | return LdrGetProcedureAddress(
184 | ModuleHandle,
185 | &asProcedureName,
186 | 0,
187 | (PVOID*)(&ProcedureAddress));
188 | }
189 |
190 | // 获取特定模块的特定导出函数地址
191 | template
192 | FORCEINLINE NTSTATUS M2GetProcedureAddress(
193 | _Out_ ProcedureType &ProcedureAddress,
194 | _In_ PVOID ModuleHandle,
195 | _In_ ULONG ProcedureNumber)
196 | {
197 | return LdrGetProcedureAddress(
198 | ModuleHandle,
199 | nullptr,
200 | ProcedureNumber,
201 | (PVOID*)(&ProcedureAddress));
202 | }
203 |
204 | // 创建事件对象, 不内联考虑到大量使用本函数时实现函数复用以节约空间
205 | NTSTATUS WINAPI M2CreateEvent(
206 | _Out_ PHANDLE phEvent,
207 | _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,
208 | _In_ BOOL bManualReset,
209 | _In_ BOOL bInitialState,
210 | _In_opt_ LPCWSTR lpName);
211 |
212 | // 按范围取值
213 | template
214 | inline T M2GetValueByRange(T Value, T Min, T Max)
215 | {
216 | return ((Value > Min) ? ((Value > Max) ? Max : Value) : Min);
217 | }
218 |
219 | // 在默认堆上分配内存
220 | template
221 | FORCEINLINE NTSTATUS M2HeapAlloc(
222 | _In_ SIZE_T Size,
223 | _Out_ PtrType &BaseAddress)
224 | {
225 | BaseAddress = (PtrType)RtlAllocateHeap(RtlProcessHeap(), 0, Size);
226 | return (BaseAddress ? STATUS_SUCCESS : STATUS_NO_MEMORY);
227 | }
228 |
229 | // 内存指针模板类
230 | template class CPtr
231 | {
232 | public:
233 | // 分配内存
234 | bool Alloc(_In_ size_t Size)
235 | {
236 | if (m_Ptr) this->Free();
237 | m_Ptr = malloc(Size);
238 | return (m_Ptr != nullptr);
239 | }
240 |
241 | // 释放内存
242 | void Free()
243 | {
244 | free(m_Ptr);
245 | m_Ptr = nullptr;
246 | }
247 |
248 | // 获取内存指针
249 | operator PtrType() const
250 | {
251 | return (PtrType)m_Ptr;
252 | }
253 |
254 | // 获取内存指针(->运算符)
255 | PtrType operator->() const
256 | {
257 | return (PtrType)m_Ptr;
258 | }
259 |
260 | // 设置内存指针
261 | CPtr& operator=(_In_ PtrType Ptr)
262 | {
263 | if (Ptr != m_Ptr) // 如果值相同返回自身,否则赋新值
264 | {
265 | if (m_Ptr) this->Free(); // 如果内存已分配则释放
266 | m_Ptr = Ptr; // 设置内存指针
267 | }
268 | return *this; // 返回自身
269 | }
270 |
271 | // 退出时释放内存
272 | ~CPtr()
273 | {
274 | if (m_Ptr) this->Free();
275 | }
276 |
277 | private:
278 | //指针内部变量
279 | void *m_Ptr = nullptr;
280 | };
281 |
282 | // 忽略未调用参数警告
283 | template void UnReferencedParameter(const T&) {}
284 |
285 |
286 |
287 | //*************************************************************************
288 | // Windows 10未文档化DPI支持相关定义
289 | // Windows 10 DPI Support Definations
290 | //*************************************************************************
291 | #if _MSC_VER >= 1200
292 | #pragma warning(push)
293 | // 从“type of expression”到“type required”的不安全转换(等级 3)
294 | #pragma warning(disable:4191)
295 | #endif
296 |
297 | typedef INT(WINAPI *PFN_EnablePerMonitorDialogScaling)();
298 | typedef BOOL(WINAPI *PFN_EnableChildWindowDpiMessage)(HWND, BOOL);
299 | typedef BOOL(WINAPI *PFN_NtUserEnableChildWindowDpiMessage)(HWND, BOOL);
300 |
301 | /*
302 | EnablePerMonitorDialogScaling函数为指定对话框启用Per-Monitor DPI Aware支
303 | 持。
304 | The EnablePerMonitorDialogScaling function enables the Per-Monitor DPI
305 | Aware for the specified dialog.
306 |
307 | 你需要在Windows 10 Threshold 1 及以后的版本使用该函数。
308 | You need to use this function in Windows 10 Threshold 1 or later.
309 | */
310 | FORCEINLINE INT EnablePerMonitorDialogScaling()
311 | {
312 | PVOID pDllHandle = nullptr;
313 | PFN_EnablePerMonitorDialogScaling pFunc = nullptr;
314 |
315 | if (!NT_SUCCESS(M2GetModuleHandle(pDllHandle, L"user32.dll")))
316 | return -1;
317 | if (!NT_SUCCESS(M2GetProcedureAddress(pFunc, pDllHandle, 2577)))
318 | return -1;
319 |
320 | return pFunc();
321 | }
322 |
323 | /*
324 | EnableChildWindowDpiMessage函数启用指定子窗口的DPI消息。
325 | The EnableChildWindowDpiMessage function enables the dpi messages from the
326 | specified child window.
327 |
328 | 你需要在Windows 10 Threshold 1 和 Windows Threshold 2使用该函数。Windows
329 | 10 Redstone 1 及以后的版本需要使用NtUserEnableChildWindowDpiMessage。
330 | You need to use this function in Windows 10 Threshold 1 and Windows 10
331 | Threshold 2. You need to use NtUserEnableChildWindowDpiMessage in Windows
332 | 10 Redstone 1 or later.
333 | */
334 | FORCEINLINE BOOL EnableChildWindowDpiMessage(
335 | _In_ HWND hWnd,
336 | _In_ BOOL bEnable)
337 | {
338 | PVOID pDllHandle = nullptr;
339 | PFN_EnableChildWindowDpiMessage pFunc = nullptr;
340 |
341 | if (!NT_SUCCESS(M2GetModuleHandle(pDllHandle, L"user32.dll")))
342 | return -1;
343 | if (!NT_SUCCESS(M2GetProcedureAddress(
344 | pFunc, pDllHandle, "EnableChildWindowDpiMessage")))
345 | return -1;
346 |
347 | return pFunc(hWnd, bEnable);
348 | }
349 |
350 | /*
351 | NtUserEnableChildWindowDpiMessage函数启用指定子窗口的DPI消息。
352 | The NtUserEnableChildWindowDpiMessage function enables the dpi messages
353 | from the specified child window.
354 |
355 | 你需要在Windows 10 Redstone 1 及以后的版本使用该函数。
356 | You need to use this function in Windows 10 Redstone 1 or later.
357 | */
358 | FORCEINLINE BOOL NtUserEnableChildWindowDpiMessage(
359 | _In_ HWND hWnd,
360 | _In_ BOOL bEnable)
361 | {
362 | PVOID pDllHandle = nullptr;
363 | PFN_NtUserEnableChildWindowDpiMessage pFunc = nullptr;
364 |
365 | if (!NT_SUCCESS(M2GetModuleHandle(pDllHandle, L"win32u.dll")))
366 | return -1;
367 | if (!NT_SUCCESS(M2GetProcedureAddress(
368 | pFunc, pDllHandle, "NtUserEnableChildWindowDpiMessage")))
369 | return -1;
370 |
371 | return (pFunc ? pFunc(hWnd, bEnable) : -1);
372 | }
373 |
374 | #if _MSC_VER >= 1200
375 | #pragma warning(pop)
376 | #endif
377 |
378 | //*************************************************************************
379 | // COM对象模板
380 | // COM Object Template
381 | //*************************************************************************
382 | #if _MSC_VER >= 1200
383 | #pragma warning(push)
384 | #pragma warning(disable:4820) // 字节填充添加在数据成员后(等级 4)
385 | #endif
386 |
387 | #define COM_INTERFACE_ENTRY(Interface) \
388 | if (__uuidof(Interface) == riid) \
389 | { \
390 | *ppvObject = (Interface*)this; \
391 | AddRef(); \
392 | return S_OK; \
393 | }
394 |
395 | #define COM_INTERFACE_MAP_BEGIN \
396 | FORCEINLINE HRESULT InternalQueryInterface( \
397 | REFIID riid, \
398 | void __RPC_FAR *__RPC_FAR *ppvObject) \
399 | {
400 |
401 | #define COM_INTERFACE_MAP_END \
402 | COM_INTERFACE_ENTRY(IUnknown); \
403 | return E_NOINTERFACE; \
404 | }
405 |
406 | // 单线程COM对象模板类
407 | template
408 | class CComObject : public Interface
409 | {
410 | private:
411 | ULONG m_ulRef;
412 |
413 | public:
414 | // 构造函数
415 | CComObject() :m_ulRef(1)
416 | {
417 | }
418 |
419 | // 析构函数
420 | virtual ~CComObject()
421 | {
422 | }
423 |
424 | // 查询接口
425 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(
426 | REFIID riid,
427 | void __RPC_FAR *__RPC_FAR *ppvObject)
428 | {
429 | return ((BaseClass*)this)->InternalQueryInterface(riid, ppvObject);
430 | }
431 |
432 | // 增加引用计数
433 | virtual ULONG STDMETHODCALLTYPE AddRef()
434 | {
435 | return InterlockedIncrement(&m_ulRef);
436 | }
437 |
438 | // 释放引用计数
439 | virtual ULONG STDMETHODCALLTYPE Release()
440 | {
441 | ULONG dwRet = InterlockedDecrement(&m_ulRef);
442 |
443 | // 如果释放计数后等于0释放自身
444 | if (dwRet == 0) delete (BaseClass*)this;
445 |
446 | // 否则返回当前计数
447 | return dwRet;
448 | }
449 | };
450 |
451 | #if _MSC_VER >= 1200
452 | #pragma warning(pop)
453 | #endif
454 |
455 | }
456 |
457 | #endif
458 |
459 | #endif
460 |
--------------------------------------------------------------------------------
/M2TeamCommonLibrary/M2.NSudo.h:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | 项目:M2-SDK
3 | 描述:NSudo库
4 | 文件名:M2.NSudo.h
5 | 基于项目:无
6 | 许可协议:看顶层目录的 License.txt
7 | 建议的Windows SDK版本:10.0.10586及以后
8 |
9 | Project: M2-SDK
10 | Description: NSudo Library
11 | Filename: M2.NSudo.h
12 | License: See License.txt in the top level directory
13 | Recommend Minimum Windows SDK Version: 10.0.10586
14 | ******************************************************************************/
15 |
16 | #pragma once
17 |
18 | #ifndef _M2_NSUDO_
19 | #define _M2_NSUDO_
20 |
21 | // 为编译通过而禁用的微软.Net Framework SDK存在的警告
22 | #if _MSC_VER >= 1200
23 | #pragma warning(push)
24 | // 字节填充添加在数据成员后(等级 4)
25 | #pragma warning(disable:4820)
26 | // 不带范围的枚举的前向声明必须具有基础类型(假定为 int)(等级 4)
27 | #pragma warning(disable:4471)
28 | #endif
29 |
30 | #include
31 | #import "mscorlib.tlb" raw_interfaces_only \
32 | high_property_prefixes("_get","_put","_putref") \
33 | rename("ReportEvent", "InteropServices_ReportEvent")
34 |
35 | #if _MSC_VER >= 1200
36 | #pragma warning(pop)
37 | #endif
38 |
39 | #ifdef __cplusplus
40 | extern "C" {
41 | using namespace M2;
42 | #endif
43 |
44 | /*
45 | SuDuplicateToken函数通过现有的访问令牌创建一个主令牌或模仿令牌。
46 | The SuDuplicateToken function creates a primary token or an impersonation
47 | token via an existing access token.
48 |
49 | 该函数是DuplicateTokenEx API的一个等价实现。
50 | This function is an equivalent implementation of DuplicateTokenEx API.
51 | */
52 | static NTSTATUS WINAPI SuDuplicateToken(
53 | _In_ HANDLE hExistingToken,
54 | _In_ DWORD dwDesiredAccess,
55 | _In_opt_ LPSECURITY_ATTRIBUTES lpTokenAttributes,
56 | _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
57 | _In_ TOKEN_TYPE TokenType,
58 | _Outptr_ PHANDLE phNewToken)
59 | {
60 | SECURITY_QUALITY_OF_SERVICE SQOS;
61 | OBJECT_ATTRIBUTES ObjectAttributes;
62 |
63 | M2InitSecurityQuailtyOfService(
64 | SQOS, ImpersonationLevel, FALSE, FALSE);
65 | M2InitObjectAttributes(
66 | ObjectAttributes, nullptr, 0, nullptr, nullptr, &SQOS);
67 |
68 | if (lpTokenAttributes &&
69 | lpTokenAttributes->nLength == sizeof(SECURITY_ATTRIBUTES))
70 | {
71 | ObjectAttributes.Attributes =
72 | (ULONG)(lpTokenAttributes->bInheritHandle ? OBJ_INHERIT : 0);
73 | ObjectAttributes.SecurityDescriptor =
74 | lpTokenAttributes->lpSecurityDescriptor;
75 | }
76 |
77 | return NtDuplicateToken(
78 | hExistingToken,
79 | dwDesiredAccess,
80 | &ObjectAttributes,
81 | FALSE,
82 | TokenType,
83 | phNewToken);
84 | }
85 |
86 | /*
87 | SuOpenProcess函数打开一个存在的本机进程对象。
88 | The SuOpenProcess function opens an existing local process object.
89 |
90 | 该函数是OpenProcess API的一个等价实现。
91 | This function is an equivalent implementation of OpenProcess API.
92 | */
93 | static NTSTATUS WINAPI SuOpenProcess(
94 | _Out_ PHANDLE phProcess,
95 | _In_ DWORD dwDesiredAccess,
96 | _In_ BOOL bInheritHandle,
97 | _In_ DWORD dwProcessId)
98 | {
99 | OBJECT_ATTRIBUTES ObjectAttributes;
100 | CLIENT_ID ClientID;
101 |
102 | M2InitClientID(ClientID, dwProcessId, 0);
103 | M2InitObjectAttributes(ObjectAttributes);
104 |
105 | ObjectAttributes.Attributes =
106 | (ULONG)(bInheritHandle ? OBJ_INHERIT : 0);
107 |
108 | return NtOpenProcess(
109 | phProcess, dwDesiredAccess, &ObjectAttributes, &ClientID);
110 | }
111 |
112 | /*
113 | SuOpenProcessToken函数根据进程ID打开一个进程的关联令牌。
114 | The SuOpenProcessToken function opens the access token associated with a
115 | process via ProcessID.
116 | */
117 | static NTSTATUS WINAPI SuOpenProcessToken(
118 | _In_ DWORD dwProcessId,
119 | _In_ DWORD DesiredAccess,
120 | _Outptr_ PHANDLE TokenHandle)
121 | {
122 | NTSTATUS status = STATUS_SUCCESS;
123 | HANDLE hProcess = nullptr;
124 |
125 | status = SuOpenProcess(
126 | &hProcess, MAXIMUM_ALLOWED, FALSE, dwProcessId);
127 | if (NT_SUCCESS(status))
128 | {
129 | status = NtOpenProcessToken(
130 | hProcess, DesiredAccess, TokenHandle);
131 | NtClose(hProcess);
132 | }
133 |
134 | return status;
135 | }
136 |
137 | /*
138 | SuOpenSessionToken函数根据已登陆的用户的会话ID获取主访问令牌。您需要在
139 | LocalSystem账户且开启SE_TCB_NAME特权的访问令牌上下文下调用该函数。
140 | The SuOpenSessionToken function obtains the primary access token of the
141 | logged-on user specified by the session ID. To call this function
142 | successfully, the calling application must be running within the context
143 | of the LocalSystem account and have the SE_TCB_NAME privilege.
144 |
145 | 该函数是WTSQueryUserToken API的一个等价实现。
146 | This function is an equivalent implementation of WTSQueryUserToken API.
147 | */
148 | static HRESULT WINAPI SuOpenSessionToken(
149 | _In_ ULONG SessionId,
150 | _Out_ PHANDLE phToken)
151 | {
152 | WINSTATIONUSERTOKEN WSUT = { 0 };
153 | DWORD ReturnLength = 0;
154 |
155 | // 初始化 LastError
156 | M2SetLastError(ERROR_SUCCESS);
157 |
158 | // 获取线程令牌
159 | if (WinStationQueryInformationW(
160 | SERVERNAME_CURRENT,
161 | SessionId,
162 | WinStationUserToken,
163 | &WSUT,
164 | sizeof(WINSTATIONUSERTOKEN),
165 | &ReturnLength))
166 | {
167 | // 如果执行成功则返回令牌句柄
168 | *phToken = WSUT.UserToken;
169 | }
170 |
171 | return __HRESULT_FROM_WIN32(M2GetLastError());
172 | }
173 |
174 | /*
175 | SuStartService函数通过服务名启动服务并返回服务状态。
176 | The SuStartService function starts a service and return service status via
177 | service name.
178 | */
179 | static HRESULT WINAPI SuStartService(
180 | _In_ LPCWSTR lpServiceName,
181 | _Out_ LPSERVICE_STATUS_PROCESS lpServiceStatus)
182 | {
183 | SC_HANDLE hSCM = nullptr;
184 | SC_HANDLE hService = nullptr;
185 | DWORD nBytesNeeded = 0;
186 | DWORD nOldCheckPoint = 0;
187 | ULONGLONG nCurrentTick = 0;
188 | ULONGLONG nLastTick = 0;
189 | bool bStartServiceWCalled = false;
190 | bool bSleepCalled = false;
191 | bool bFinished = false;
192 | bool bSucceed = false;
193 |
194 | // 初始化 LastError
195 | M2SetLastError(ERROR_SUCCESS);
196 |
197 | hSCM = OpenSCManagerW(
198 | nullptr,
199 | nullptr,
200 | SC_MANAGER_CONNECT);
201 | if (!hSCM) goto FuncEnd;
202 |
203 | hService = OpenServiceW(
204 | hSCM,
205 | lpServiceName,
206 | SERVICE_QUERY_STATUS | SERVICE_START);
207 | if (!hService) goto FuncEnd;
208 |
209 | while (QueryServiceStatusEx(
210 | hService,
211 | SC_STATUS_PROCESS_INFO,
212 | (LPBYTE)lpServiceStatus,
213 | sizeof(SERVICE_STATUS_PROCESS),
214 | &nBytesNeeded))
215 | {
216 | switch (lpServiceStatus->dwCurrentState)
217 | {
218 | case SERVICE_STOPPED:
219 | if (!bStartServiceWCalled)
220 | {
221 | bStartServiceWCalled = true;
222 | bFinished = (!StartServiceW(hService, 0, nullptr));
223 | }
224 | else bFinished = true;
225 | break;
226 | case SERVICE_STOP_PENDING:
227 | case SERVICE_START_PENDING:
228 | nCurrentTick = NtGetTickCount64();
229 |
230 | if (!bSleepCalled)
231 | {
232 | nLastTick = nCurrentTick;
233 | nOldCheckPoint = lpServiceStatus->dwCheckPoint;
234 |
235 | bSleepCalled = true;
236 |
237 | // 等待250ms(借鉴.Net服务操作类的实现)
238 | LARGE_INTEGER Interval;
239 | Interval.QuadPart = 250LL * -10000LL;
240 | NtDelayExecution(FALSE, &Interval);
241 | }
242 | else
243 | {
244 | // 如果校验点增加则继续循环,否则检测是否超时
245 | if (lpServiceStatus->dwCheckPoint > nOldCheckPoint)
246 | {
247 | bSleepCalled = false;
248 | }
249 | else
250 | {
251 | ULONGLONG nDiff = nCurrentTick - nLastTick;
252 | if (nDiff > lpServiceStatus->dwWaitHint)
253 | {
254 | M2SetLastError(ERROR_TIMEOUT);
255 | bFinished = true;
256 | }
257 | else
258 | {
259 | // 未超时则继续循环
260 | bSleepCalled = false;
261 | }
262 | }
263 | }
264 | break;
265 | default:
266 | bSucceed = true;
267 | bFinished = true;
268 | break;
269 | }
270 |
271 | if (bFinished) break;
272 | }
273 |
274 | // 如果服务启动失败则清空状态信息
275 | if (!bSucceed)
276 | memset(lpServiceStatus, 0, sizeof(SERVICE_STATUS_PROCESS));
277 |
278 | FuncEnd:
279 | if (hService) CloseServiceHandle(hService);
280 | if (hSCM) CloseServiceHandle(hSCM);
281 | return __HRESULT_FROM_WIN32(M2GetLastError());
282 | }
283 |
284 | /*
285 | SuOpenServiceProcessToken函数根据服务名打开一个服务进程的关联令牌。
286 | The SuOpenServiceProcessToken function opens the access token associated
287 | with a service process via service name.
288 | */
289 | static HRESULT WINAPI SuOpenServiceProcessToken(
290 | _In_ LPCWSTR lpServiceName,
291 | _In_ DWORD DesiredAccess,
292 | _Outptr_ PHANDLE TokenHandle)
293 | {
294 | HRESULT hr = S_OK;
295 | NTSTATUS status = STATUS_SUCCESS;
296 | SERVICE_STATUS_PROCESS ssStatus;
297 |
298 | hr = SuStartService(lpServiceName, &ssStatus);
299 | if (SUCCEEDED(hr))
300 | {
301 | status = SuOpenProcessToken(
302 | ssStatus.dwProcessId, DesiredAccess, TokenHandle);
303 | hr = __HRESULT_FROM_WIN32(RtlNtStatusToDosError(status));
304 | }
305 |
306 | return hr;
307 | }
308 |
309 | /*
310 | SuOpenCurrentProcessToken函数打开当前进程的关联令牌。
311 | The SuOpenCurrentProcessToken function opens the access token associated
312 | with current process.
313 | */
314 | static NTSTATUS WINAPI SuOpenCurrentProcessToken(
315 | _Out_ PHANDLE phProcessToken,
316 | _In_ DWORD DesiredAccess)
317 | {
318 | return NtOpenProcessToken(
319 | NtCurrentProcess(), DesiredAccess, phProcessToken);
320 | }
321 |
322 |
323 | /*
324 | SuGetCurrentProcessSessionID获取当前进程的会话ID。
325 | The SuGetCurrentProcessSessionID function obtains the Session ID of the
326 | current process.
327 | */
328 | static NTSTATUS SuGetCurrentProcessSessionID(PDWORD SessionID)
329 | {
330 | NTSTATUS status = STATUS_SUCCESS;
331 | HANDLE hToken = INVALID_HANDLE_VALUE;
332 | DWORD ReturnLength = 0;
333 |
334 | status = SuOpenCurrentProcessToken(&hToken, MAXIMUM_ALLOWED);
335 | if (NT_SUCCESS(status))
336 | {
337 | status = NtQueryInformationToken(
338 | hToken,
339 | TokenSessionId,
340 | SessionID,
341 | sizeof(DWORD),
342 | &ReturnLength);
343 | }
344 |
345 | return status;
346 | }
347 |
348 | /*
349 | SuSetThreadToken函数给线程分配一个模拟令牌。该函数还可以使一个线程停止使用
350 | 模拟令牌。
351 | The SuSetThreadToken function assigns an impersonation token to a thread.
352 | The function can also cause a thread to stop using an impersonation token.
353 |
354 | 该函数是SetThreadToken API的一个等价实现。
355 | This function is an equivalent implementation of SetThreadToken API.
356 | */
357 | static NTSTATUS WINAPI SuSetThreadToken(
358 | _In_opt_ PHANDLE phThread,
359 | _In_ HANDLE hToken)
360 | {
361 | return NtSetInformationThread(
362 | (phThread != nullptr) ? *phThread : NtCurrentThread(),
363 | ThreadImpersonationToken,
364 | &hToken,
365 | sizeof(HANDLE));
366 | }
367 |
368 | /*
369 | SuSetCurrentThreadToken函数给当前线程分配一个模拟令牌。该函数还可以使当前线
370 | 程停止使用模拟令牌。
371 | The SuSetCurrentThreadToken function assigns an impersonation token to the
372 | current thread. The function can also cause the current thread to stop
373 | using an impersonation token.
374 | */
375 | static NTSTATUS WINAPI SuSetCurrentThreadToken(
376 | _In_ HANDLE hToken)
377 | {
378 | return SuSetThreadToken(nullptr, hToken);
379 | }
380 |
381 | /*
382 | SuRevertToSelf函数终止客户端应用程序模拟。
383 | The SuRevertToSelf function terminates the impersonation of a client
384 | application.
385 |
386 | 该函数是RevertToSelf API的一个等价实现。
387 | This function is an equivalent implementation of RevertToSelf API.
388 | */
389 | static NTSTATUS WINAPI SuRevertToSelf()
390 | {
391 | return SuSetCurrentThreadToken(nullptr);
392 | }
393 |
394 | /*
395 | SuSetTokenPrivileges函数启用或禁用指定的访问令牌特权。启用或禁用一个访问令
396 | 牌的特权需要TOKEN_ADJUST_PRIVILEGES访问权限。
397 | The SuSetTokenPrivileges function enables or disables privileges in the
398 | specified access token. Enabling or disabling privileges in an access
399 | token requires TOKEN_ADJUST_PRIVILEGES access.
400 | */
401 | static NTSTATUS WINAPI SuSetTokenPrivileges(
402 | _In_ HANDLE TokenHandle,
403 | _In_opt_ PTOKEN_PRIVILEGES NewState)
404 | {
405 | return NtAdjustPrivilegesToken(
406 | TokenHandle, FALSE, NewState, 0, nullptr, nullptr);
407 | }
408 |
409 | /*
410 | 访问令牌特权定义
411 | The definitions of the Token Privileges
412 | */
413 | typedef enum _TOKEN_PRIVILEGES_LIST
414 | {
415 | SeMinWellKnownPrivilege = 2,
416 | SeCreateTokenPrivilege = 2,
417 | SeAssignPrimaryTokenPrivilege,
418 | SeLockMemoryPrivilege,
419 | SeIncreaseQuotaPrivilege,
420 | SeMachineAccountPrivilege,
421 | SeTcbPrivilege,
422 | SeSecurityPrivilege,
423 | SeTakeOwnershipPrivilege,
424 | SeLoadDriverPrivilege,
425 | SeSystemProfilePrivilege,
426 | SeSystemtimePrivilege,
427 | SeProfileSingleProcessPrivilege,
428 | SeIncreaseBasePriorityPrivilege,
429 | SeCreatePagefilePrivilege,
430 | SeCreatePermanentPrivilege,
431 | SeBackupPrivilege,
432 | SeRestorePrivilege,
433 | SeShutdownPrivilege,
434 | SeDebugPrivilege,
435 | SeAuditPrivilege,
436 | SeSystemEnvironmentPrivilege,
437 | SeChangeNotifyPrivilege,
438 | SeRemoteShutdownPrivilege,
439 | SeUndockPrivilege,
440 | SeSyncAgentPrivilege,
441 | SeEnableDelegationPrivilege,
442 | SeManageVolumePrivilege,
443 | SeImpersonatePrivilege,
444 | SeCreateGlobalPrivilege,
445 | SeTrustedCredManAccessPrivilege,
446 | SeRelabelPrivilege,
447 | SeIncreaseWorkingSetPrivilege,
448 | SeTimeZonePrivilege,
449 | SeCreateSymbolicLinkPrivilege,
450 | SeMaxWellKnownPrivilege = SeCreateSymbolicLinkPrivilege
451 | } TOKEN_PRIVILEGES_LIST, *PTOKEN_PRIVILEGES_LIST;
452 |
453 | /*
454 | 访问令牌完整性级别定义
455 | The definitions of the Token Integrity Levels
456 | */
457 | typedef enum _TOKEN_INTEGRITY_LEVELS_LIST
458 | {
459 | // S-1-16-0
460 | UntrustedLevel = SECURITY_MANDATORY_UNTRUSTED_RID,
461 |
462 | // S-1-16-4096
463 | LowLevel = SECURITY_MANDATORY_LOW_RID,
464 |
465 | // S-1-16-8192
466 | MediumLevel = SECURITY_MANDATORY_MEDIUM_RID,
467 |
468 | // S-1-16-8448
469 | MediumPlusLevel = SECURITY_MANDATORY_MEDIUM_PLUS_RID,
470 |
471 | // S-1-16-12288
472 | HighLevel = SECURITY_MANDATORY_HIGH_RID,
473 |
474 | // S-1-16-16384
475 | SystemLevel = SECURITY_MANDATORY_SYSTEM_RID,
476 |
477 | // S-1-16-20480
478 | ProtectedLevel = SECURITY_MANDATORY_PROTECTED_PROCESS_RID
479 | } TOKEN_INTEGRITY_LEVELS_LIST, *PTOKEN_INTEGRITY_LEVELS_LIST;
480 |
481 | /*
482 | SuSetTokenPrivilege函数启用或禁用指定的访问令牌的指定特权。启用或禁用一个访
483 | 问令牌的特权需要TOKEN_ADJUST_PRIVILEGES访问权限。
484 | The SuSetTokenPrivilege function enables or disables the specified
485 | privilege in the specified access token. Enabling or disabling privileges
486 | in an access token requires TOKEN_ADJUST_PRIVILEGES access.
487 | */
488 | static NTSTATUS WINAPI SuSetTokenPrivilege(
489 | _In_ HANDLE hExistingToken,
490 | _In_ TOKEN_PRIVILEGES_LIST Privilege,
491 | _In_ bool bEnable)
492 | {
493 | TOKEN_PRIVILEGES TP;
494 | TP.PrivilegeCount = 1;
495 | TP.Privileges[0].Luid.LowPart = Privilege;
496 | TP.Privileges[0].Attributes = (DWORD)(bEnable ? SE_PRIVILEGE_ENABLED : 0);
497 |
498 | return SuSetTokenPrivileges(hExistingToken, &TP);
499 | }
500 |
501 | /*
502 | SuSetTokenAllPrivileges函数启用或禁用指定的访问令牌的所有特权。启用或禁用一
503 | 个访问令牌的特权需要TOKEN_ADJUST_PRIVILEGES访问权限。
504 | The SuSetTokenAllPrivileges function enables or disables all privileges in
505 | the specified access token. Enabling or disabling privileges in an access
506 | token requires TOKEN_ADJUST_PRIVILEGES access.
507 | */
508 | static NTSTATUS WINAPI SuSetTokenAllPrivileges(
509 | _In_ HANDLE hExistingToken,
510 | _In_ bool bEnable)
511 | {
512 | NTSTATUS status = STATUS_SUCCESS;
513 | PTOKEN_PRIVILEGES pTPs = nullptr;
514 | DWORD Length = 0;
515 |
516 | // 获取特权信息大小
517 | NtQueryInformationToken(
518 | hExistingToken, TokenPrivileges, nullptr, 0, &Length);
519 |
520 | // 分配内存
521 | status = M2HeapAlloc(Length, pTPs);
522 | if (NT_SUCCESS(status))
523 | {
524 | // 获取特权信息
525 | status = NtQueryInformationToken(
526 | hExistingToken,
527 | TokenPrivileges,
528 | pTPs,
529 | Length,
530 | &Length);
531 | if (NT_SUCCESS(status))
532 | {
533 | // 设置特权信息
534 | for (DWORD i = 0; i < pTPs->PrivilegeCount; i++)
535 | pTPs->Privileges[i].Attributes =
536 | (DWORD)(bEnable ? SE_PRIVILEGE_ENABLED : 0);
537 |
538 | // 开启全部特权
539 | status = SuSetTokenPrivileges(hExistingToken, pTPs);
540 | }
541 |
542 | // 释放内存
543 | M2HeapFree(pTPs);
544 | }
545 |
546 | return status;
547 | }
548 |
549 | // sizeof(SID_IDENTIFIER_AUTHORITY)
550 | const SIZE_T SIA_Length = sizeof(SID_IDENTIFIER_AUTHORITY);
551 |
552 | // SECURITY_NT_AUTHORITY
553 | static SID_IDENTIFIER_AUTHORITY SIA_NT = SECURITY_NT_AUTHORITY;
554 |
555 | // SECURITY_WORLD_SID_AUTHORITY
556 | static SID_IDENTIFIER_AUTHORITY SIA_World = SECURITY_WORLD_SID_AUTHORITY;
557 |
558 | // SECURITY_APP_PACKAGE_AUTHORITY
559 | static SID_IDENTIFIER_AUTHORITY SIA_App = SECURITY_APP_PACKAGE_AUTHORITY;
560 |
561 | // SECURITY_MANDATORY_LABEL_AUTHORITY
562 | static SID_IDENTIFIER_AUTHORITY SIA_IL = SECURITY_MANDATORY_LABEL_AUTHORITY;
563 |
564 | /*
565 | SuSetTokenIntegrityLevel函数为指定的访问令牌设置完整性标签。
566 | The SuSetTokenIntegrityLevel function sets the integrity level for the
567 | specified access token.
568 | */
569 | static NTSTATUS WINAPI SuSetTokenIntegrityLevel(
570 | _In_ HANDLE TokenHandle,
571 | _In_ TOKEN_INTEGRITY_LEVELS_LIST IL)
572 | {
573 | NTSTATUS status = STATUS_SUCCESS;
574 | TOKEN_MANDATORY_LABEL TML;
575 |
576 | // 初始化SID
577 | status = RtlAllocateAndInitializeSid(
578 | &SIA_IL, 1, IL, 0, 0, 0, 0, 0, 0, 0, &TML.Label.Sid);
579 | if (NT_SUCCESS(status))
580 | {
581 | // 初始化TOKEN_MANDATORY_LABEL
582 | TML.Label.Attributes = SE_GROUP_INTEGRITY;
583 |
584 | // 设置令牌对象
585 | status = NtSetInformationToken(
586 | TokenHandle, TokenIntegrityLevel, &TML, sizeof(TML));
587 |
588 | // 释放SID
589 | RtlFreeSid(TML.Label.Sid);
590 | }
591 |
592 | return status;
593 | }
594 |
595 | /*
596 | SuIsLogonSid函数判断指定的SID是否为登录SID。
597 | The SuIsLogonSid function determines whether the specified SID is a logon
598 | SID.
599 | */
600 | static bool WINAPI SuIsLogonSid(
601 | _In_ PSID pSid)
602 | {
603 | // 获取pSid的SID_IDENTIFIER_AUTHORITY结构
604 | PSID_IDENTIFIER_AUTHORITY pSidAuth = RtlIdentifierAuthoritySid(pSid);
605 |
606 | // 如果不符合SID_IDENTIFIER_AUTHORITY结构长度,则返回false
607 | if (memcmp(pSidAuth, &SIA_NT, SIA_Length)) return false;
608 |
609 | // 判断SID是否属于Logon SID
610 | return (*RtlSubAuthorityCountSid(pSid) == SECURITY_LOGON_IDS_RID_COUNT
611 | && *RtlSubAuthoritySid(pSid, 0) == SECURITY_LOGON_IDS_RID);
612 | }
613 |
614 | /*
615 | SuSetKernelObjectIntegrityLevel函数为指定的内核对象设置完整性标签。
616 | The SuSetKernelObjectIntegrityLevel function sets the integrity level for
617 | the specified kernel object.
618 | */
619 | static NTSTATUS WINAPI SuSetKernelObjectIntegrityLevel(
620 | _In_ HANDLE Object,
621 | _In_ TOKEN_INTEGRITY_LEVELS_LIST IL)
622 | {
623 | const size_t AclLength = 88;
624 | NTSTATUS status = STATUS_SUCCESS;
625 | PSID pSID = nullptr;
626 | PACL pAcl = nullptr;
627 | SECURITY_DESCRIPTOR SD;
628 | HANDLE hNewHandle = nullptr;
629 |
630 | // 复制句柄
631 | status = NtDuplicateObject(
632 | NtCurrentProcess(),
633 | Object,
634 | NtCurrentProcess(),
635 | &hNewHandle,
636 | DIRECTORY_ALL_ACCESS,
637 | 0,
638 | 0);
639 | if (!NT_SUCCESS(status)) goto FuncEnd;
640 |
641 | //初始化SID
642 | status = RtlAllocateAndInitializeSid(
643 | &SIA_IL, 1, IL, 0, 0, 0, 0, 0, 0, 0, &pSID);
644 | if (!NT_SUCCESS(status)) goto FuncEnd;
645 |
646 | //分配ACL结构内存
647 | status = M2HeapAlloc(AclLength, pAcl);
648 | if (!NT_SUCCESS(status)) goto FuncEnd;
649 |
650 | // 创建SD
651 | status = RtlCreateSecurityDescriptor(
652 | &SD, SECURITY_DESCRIPTOR_REVISION);
653 | if (!NT_SUCCESS(status)) goto FuncEnd;
654 |
655 | // 创建ACL
656 | status = RtlCreateAcl(pAcl, AclLength, ACL_REVISION);
657 | if (!NT_SUCCESS(status)) goto FuncEnd;
658 |
659 | // 添加完整性ACE
660 | status = RtlAddMandatoryAce(
661 | pAcl, ACL_REVISION, 0, pSID,
662 | SYSTEM_MANDATORY_LABEL_ACE_TYPE, OBJECT_TYPE_CREATE);
663 | if (!NT_SUCCESS(status)) goto FuncEnd;
664 |
665 | // 设置SACL
666 | status = RtlSetSaclSecurityDescriptor(&SD, TRUE, pAcl, FALSE);
667 | if (!NT_SUCCESS(status)) goto FuncEnd;
668 |
669 | // 设置内核对象
670 | status = NtSetSecurityObject(
671 | hNewHandle, LABEL_SECURITY_INFORMATION, &SD);
672 |
673 | FuncEnd:
674 | M2HeapFree(pAcl);
675 | RtlFreeSid(pSID);
676 | NtClose(hNewHandle);
677 |
678 | return status;
679 | }
680 |
681 | /*
682 | SuCreateLUAToken函数从一个现有的访问令牌创建一个新的LUA访问令牌。
683 | The SuCreateLUAToken function creates a new LUA access token from an
684 | existing access token.
685 | */
686 | static NTSTATUS WINAPI SuCreateLUAToken(
687 | _Out_ PHANDLE TokenHandle,
688 | _In_ HANDLE ExistingTokenHandle)
689 | {
690 | NTSTATUS status = STATUS_SUCCESS;
691 | DWORD Length = 0;
692 | BOOL EnableTokenVirtualization = TRUE;
693 | TOKEN_OWNER Owner = { 0 };
694 | TOKEN_DEFAULT_DACL NewTokenDacl = { 0 };
695 | PTOKEN_USER pTokenUser = nullptr;
696 | PTOKEN_DEFAULT_DACL pTokenDacl = nullptr;
697 | PSID pAdminSid = nullptr;
698 | PACCESS_ALLOWED_ACE pTempAce = nullptr;
699 |
700 | //创建受限令牌
701 | status = NtFilterToken(
702 | ExistingTokenHandle, LUA_TOKEN,
703 | nullptr, nullptr, nullptr, TokenHandle);
704 | if (!NT_SUCCESS(status)) goto FuncEnd;
705 |
706 | // 设置令牌完整性
707 | status = SuSetTokenIntegrityLevel(
708 | *TokenHandle, TOKEN_INTEGRITY_LEVELS_LIST::MediumLevel);
709 | if (!NT_SUCCESS(status)) goto FuncEnd;
710 |
711 | // 获取令牌对应的用户账户SID信息大小
712 | status = NtQueryInformationToken(
713 | *TokenHandle, TokenUser, nullptr, 0, &Length);
714 | if (status != STATUS_BUFFER_TOO_SMALL) goto FuncEnd;
715 |
716 | // 为令牌对应的用户账户SID信息分配内存
717 | status = M2HeapAlloc(Length, pTokenUser);
718 | if (!NT_SUCCESS(status)) goto FuncEnd;
719 |
720 | // 获取令牌对应的用户账户SID信息
721 | status = NtQueryInformationToken(
722 | *TokenHandle, TokenUser, pTokenUser, Length, &Length);
723 | if (!NT_SUCCESS(status)) goto FuncEnd;
724 |
725 | // 设置令牌Owner为当前用户
726 | Owner.Owner = pTokenUser->User.Sid;
727 | status = NtSetInformationToken(
728 | *TokenHandle, TokenOwner, &Owner, sizeof(TOKEN_OWNER));
729 | if (!NT_SUCCESS(status)) goto FuncEnd;
730 |
731 | // 获取令牌的DACL信息大小
732 | status = NtQueryInformationToken(
733 | *TokenHandle, TokenDefaultDacl, nullptr, 0, &Length);
734 | if (status != STATUS_BUFFER_TOO_SMALL) goto FuncEnd;
735 |
736 | // 为令牌的DACL信息分配内存
737 | status = M2HeapAlloc(Length, pTokenDacl);
738 | if (!NT_SUCCESS(status)) goto FuncEnd;
739 |
740 | // 获取令牌的DACL信息
741 | status = NtQueryInformationToken(
742 | *TokenHandle, TokenDefaultDacl, pTokenDacl, Length, &Length);
743 | if (!NT_SUCCESS(status)) goto FuncEnd;
744 |
745 | // 获取管理员组SID
746 | status = RtlAllocateAndInitializeSid(
747 | &SIA_NT, 2,
748 | SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
749 | 0, 0, 0, 0, 0, 0, &pAdminSid);
750 | if (!NT_SUCCESS(status)) goto FuncEnd;
751 |
752 | // 计算新ACL大小
753 | Length = pTokenDacl->DefaultDacl->AclSize;
754 | Length += RtlLengthSid(pTokenUser->User.Sid);
755 | Length += sizeof(ACCESS_ALLOWED_ACE);
756 |
757 | // 分配ACL结构内存
758 | status = M2HeapAlloc(Length, NewTokenDacl.DefaultDacl);
759 | if (!NT_SUCCESS(status)) goto FuncEnd;
760 |
761 | // 创建ACL
762 | status = RtlCreateAcl(
763 | NewTokenDacl.DefaultDacl,
764 | Length, pTokenDacl->DefaultDacl->AclRevision);
765 | if (!NT_SUCCESS(status)) goto FuncEnd;
766 |
767 | // 添加ACE
768 | status = RtlAddAccessAllowedAce(
769 | NewTokenDacl.DefaultDacl,
770 | pTokenDacl->DefaultDacl->AclRevision,
771 | GENERIC_ALL,
772 | pTokenUser->User.Sid);
773 | if (!NT_SUCCESS(status)) goto FuncEnd;
774 |
775 | // 复制ACE
776 | for (ULONG i = 0;
777 | NT_SUCCESS(RtlGetAce(pTokenDacl->DefaultDacl, i, (PVOID*)&pTempAce));
778 | ++i)
779 | {
780 | if (RtlEqualSid(pAdminSid, &pTempAce->SidStart)) continue;
781 |
782 | RtlAddAce(
783 | NewTokenDacl.DefaultDacl,
784 | pTokenDacl->DefaultDacl->AclRevision, 0,
785 | pTempAce, pTempAce->Header.AceSize);
786 | }
787 |
788 | // 设置令牌DACL
789 | Length += sizeof(TOKEN_DEFAULT_DACL);
790 | status = NtSetInformationToken(
791 | *TokenHandle, TokenDefaultDacl, &NewTokenDacl, Length);
792 | if (!NT_SUCCESS(status)) goto FuncEnd;
793 |
794 | // 开启LUA虚拟化
795 | status = NtSetInformationToken(
796 | *TokenHandle,
797 | TokenVirtualizationEnabled,
798 | &EnableTokenVirtualization,
799 | sizeof(BOOL));
800 | if (!NT_SUCCESS(status)) goto FuncEnd;
801 |
802 | FuncEnd: // 扫尾
803 |
804 | if (NewTokenDacl.DefaultDacl) M2HeapFree(NewTokenDacl.DefaultDacl);
805 | if (pAdminSid) RtlFreeSid(pAdminSid);
806 | if (pTokenDacl) M2HeapFree(pTokenDacl);
807 | if (pTokenUser) M2HeapFree(pTokenUser);
808 | if (!NT_SUCCESS(status))
809 | {
810 | NtClose(*TokenHandle);
811 | *TokenHandle = INVALID_HANDLE_VALUE;
812 | }
813 |
814 | return status;
815 | }
816 |
817 | /*
818 | 内部使用的AppContainer对象列表
819 | The list of the AppContainer Objects for Internal use.
820 | */
821 | const enum SuAppContainerHandleList
822 | {
823 | RootDirectory, // 主目录对象
824 | RpcDirectory, // RPC目录对象
825 | GlobalSymbolicLink, // Global符号链接
826 | LocalSymbolicLink, // Local符号链接
827 | SessionSymbolicLink, // Session符号链接
828 | NamedPipe //命名管道
829 | };
830 |
831 | /*
832 | SuBuildAppContainerSecurityDescriptor函数从为创建一个新的AppContainer访问令
833 | 牌构建一个新的安全标识符结构。
834 | The SuBuildAppContainerSecurityDescriptor function builds a new Security
835 | Descriptor struct for creating a new AppContainer access token.
836 | */
837 | NTSTATUS WINAPI SuBuildAppContainerSecurityDescriptor(
838 | _In_ PSECURITY_DESCRIPTOR ExistingSecurityDescriptor,
839 | _In_ PSID SandBoxSid,
840 | _In_ PSID UserSid,
841 | _In_ bool IsRpcControl,
842 | _Out_ PSECURITY_DESCRIPTOR *NewSecurityDescriptor)
843 | {
844 | NTSTATUS status = STATUS_SUCCESS;
845 | DWORD ReturnLength = 0;
846 | BOOLEAN DaclPresent = FALSE;
847 | BOOLEAN DaclDefaulted = FALSE;
848 | PACL pAcl = nullptr;
849 | PACL pNewAcl = nullptr;
850 | PSID AdminSid = nullptr;
851 | PSID RestrictedSid = nullptr;
852 | PSID WorldSid = nullptr;
853 | bool bUserSidExist = false;
854 | PACCESS_ALLOWED_ACE pTempAce = nullptr;
855 |
856 | //生成受限组SID结构
857 | status = RtlAllocateAndInitializeSid(
858 | &SIA_NT, 1, SECURITY_RESTRICTED_CODE_RID,
859 | 0, 0, 0, 0, 0, 0, 0, &RestrictedSid);
860 | if (!NT_SUCCESS(status)) goto FuncEnd;
861 |
862 | //生成管理员组SID结构
863 | status = RtlAllocateAndInitializeSid(
864 | &SIA_NT, 2, SECURITY_BUILTIN_DOMAIN_RID,
865 | DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdminSid);
866 | if (!NT_SUCCESS(status)) goto FuncEnd;
867 |
868 | //生成Everyone组SID结构
869 | status = RtlAllocateAndInitializeSid(
870 | &SIA_World, 1, SECURITY_WORLD_RID,
871 | 0, 0, 0, 0, 0, 0, 0, &WorldSid);
872 | if (!NT_SUCCESS(status)) goto FuncEnd;
873 |
874 | //获取现有对象的ACL
875 | status = RtlGetDaclSecurityDescriptor(
876 | ExistingSecurityDescriptor, &DaclPresent, &pAcl, &DaclDefaulted);
877 | if (!NT_SUCCESS(status)) goto FuncEnd;
878 |
879 | //计算新ACL大小
880 | ReturnLength = pAcl->AclSize;
881 | ReturnLength += RtlLengthSid(SandBoxSid) * 2;
882 | ReturnLength += RtlLengthSid(UserSid) * 2;
883 | ReturnLength += RtlLengthSid(RestrictedSid);
884 | ReturnLength += RtlLengthSid(AdminSid);
885 | ReturnLength += RtlLengthSid(WorldSid);
886 | ReturnLength += sizeof(ACCESS_ALLOWED_ACE) * 7;
887 |
888 | //分配ACL结构内存
889 | status = M2HeapAlloc(ReturnLength, pNewAcl);
890 | if (!NT_SUCCESS(status)) goto FuncEnd;
891 |
892 | //创建ACL
893 | status = RtlCreateAcl(pNewAcl, ReturnLength, pAcl->AclRevision);
894 | if (!NT_SUCCESS(status)) goto FuncEnd;
895 |
896 | //复制ACE
897 | for (ULONG i = 0; NT_SUCCESS(RtlGetAce(pAcl, i, (PVOID*)&pTempAce)); i++)
898 | {
899 | //检测登陆SID并对权限做出修改
900 | if (SuIsLogonSid(&pTempAce->SidStart)
901 | && !(pTempAce->Header.AceFlags & INHERIT_ONLY_ACE))
902 | {
903 | pTempAce->Mask = DIRECTORY_ALL_ACCESS;
904 | }
905 |
906 | //如果不是是rpc句柄则跳过管理员和Everyone的SID添加
907 | if (!IsRpcControl
908 | && (RtlEqualSid(&pTempAce->SidStart, AdminSid)
909 | || RtlEqualSid(&pTempAce->SidStart, RestrictedSid)
910 | || RtlEqualSid(&pTempAce->SidStart, WorldSid))) continue;
911 |
912 | //如果是用户SID存在则标记
913 | if (RtlEqualSid(&pTempAce->SidStart, UserSid))
914 | bUserSidExist = true;
915 |
916 | //添加ACE
917 | RtlAddAce(pNewAcl, pAcl->AclRevision, 0,
918 | pTempAce, pTempAce->Header.AceSize);
919 | }
920 |
921 | //添加ACE(特殊) - 沙盒SID
922 | status = RtlAddAccessAllowedAce(
923 | pNewAcl,
924 | pAcl->AclRevision,
925 | DIRECTORY_ALL_ACCESS,
926 | SandBoxSid);
927 | if (!NT_SUCCESS(status)) goto FuncEnd;
928 |
929 | //添加ACE(InheritNone) - 沙盒SID
930 | status = RtlAddAccessAllowedAceEx(
931 | pNewAcl,
932 | pAcl->AclRevision,
933 | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE,
934 | GENERIC_ALL,
935 | SandBoxSid);
936 | if (!NT_SUCCESS(status)) goto FuncEnd;
937 |
938 | if (!bUserSidExist)
939 | {
940 | //添加ACE(特殊) - 用户SID
941 | status = RtlAddAccessAllowedAce(
942 | pNewAcl,
943 | pAcl->AclRevision,
944 | DIRECTORY_ALL_ACCESS,
945 | UserSid);
946 | if (!NT_SUCCESS(status)) goto FuncEnd;
947 |
948 | //添加ACE(InheritNone) - 用户SID
949 | status = RtlAddAccessAllowedAceEx(
950 | pNewAcl,
951 | pAcl->AclRevision,
952 | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE,
953 | GENERIC_ALL,
954 | UserSid);
955 | if (!NT_SUCCESS(status)) goto FuncEnd;
956 | }
957 |
958 | if (IsRpcControl)
959 | {
960 | //添加ACE(InheritNone) - 管理员SID
961 | status = RtlAddAccessAllowedAceEx(
962 | pNewAcl,
963 | pAcl->AclRevision,
964 | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE,
965 | GENERIC_ALL,
966 | AdminSid);
967 | if (!NT_SUCCESS(status)) goto FuncEnd;
968 |
969 | //添加ACE(InheritNone) - 受限SID
970 | status = RtlAddAccessAllowedAceEx(
971 | pNewAcl,
972 | pAcl->AclRevision,
973 | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE,
974 | GENERIC_READ | GENERIC_EXECUTE,
975 | RestrictedSid);
976 | if (!NT_SUCCESS(status)) goto FuncEnd;
977 |
978 | //添加ACE(InheritNone) - Everyone SID
979 | status = RtlAddAccessAllowedAceEx(
980 | pNewAcl,
981 | pAcl->AclRevision,
982 | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE,
983 | GENERIC_READ | GENERIC_EXECUTE,
984 | WorldSid);
985 | if (!NT_SUCCESS(status)) goto FuncEnd;
986 | }
987 |
988 | //分配SD结构内存
989 | status = M2HeapAlloc(
990 | sizeof(SECURITY_DESCRIPTOR), *NewSecurityDescriptor);
991 | if (!NT_SUCCESS(status)) goto FuncEnd;
992 |
993 | //创建SD
994 | status = RtlCreateSecurityDescriptor(
995 | *NewSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
996 | if (!NT_SUCCESS(status)) goto FuncEnd;
997 |
998 | //设置SD
999 | status = RtlSetDaclSecurityDescriptor(
1000 | *NewSecurityDescriptor, DaclPresent, pNewAcl, DaclDefaulted);
1001 | if (!NT_SUCCESS(status)) goto FuncEnd;
1002 |
1003 | FuncEnd:
1004 | RtlFreeSid(WorldSid);
1005 | RtlFreeSid(AdminSid);
1006 | RtlFreeSid(RestrictedSid);
1007 | return status;
1008 | }
1009 |
1010 | #define PIPE_ALL_ACCESS (SYNCHRONIZE \
1011 | | STANDARD_RIGHTS_REQUIRED \
1012 | | PIPE_ACCESS_INBOUND \
1013 | | PIPE_ACCESS_OUTBOUND \
1014 | | PIPE_ACCESS_DUPLEX)
1015 |
1016 | /*
1017 | SuCreateAppContainerToken函数从一个现有的访问令牌创建一个新的AppContainer访
1018 | 问令牌。
1019 | The SuCreateAppContainerToken function creates a new AppContainer access
1020 | token from an existing access token.
1021 | */
1022 | NTSTATUS WINAPI SuCreateAppContainerToken(
1023 | _Out_ PHANDLE TokenHandle,
1024 | _In_ HANDLE ExistingTokenHandle,
1025 | _In_ PSECURITY_CAPABILITIES SecurityCapabilities)
1026 | {
1027 | NTSTATUS status = STATUS_SUCCESS;
1028 | PVOID pNTDLL = nullptr;
1029 | UNICODE_STRING usNTDLL = { 0 };
1030 | ANSI_STRING asFuncName = { 0 };
1031 | decltype(NtCreateLowBoxToken) *pNtCreateLowBoxToken = nullptr;
1032 | decltype(NtCreateDirectoryObjectEx) *pNtCreateDirectoryObjectEx = nullptr;
1033 | DWORD ReturnLength = 0;
1034 | DWORD TokenSessionID = 0;
1035 | wchar_t Buffer[MAX_PATH];
1036 | UNICODE_STRING usBNO = RTL_CONSTANT_STRING(L"\\BaseNamedObjects");
1037 | OBJECT_ATTRIBUTES ObjectAttributes;
1038 | UNICODE_STRING usACNO = { 0 };
1039 | UNICODE_STRING usRpcControl = RTL_CONSTANT_STRING(L"\\RPC Control");
1040 | UNICODE_STRING usRpcControl2 = RTL_CONSTANT_STRING(L"RPC Control");
1041 | UNICODE_STRING usRootDirectory = { 0 };
1042 | UNICODE_STRING usGlobal = RTL_CONSTANT_STRING(L"Global");
1043 | UNICODE_STRING usLocal = RTL_CONSTANT_STRING(L"Local");
1044 | UNICODE_STRING usSession = RTL_CONSTANT_STRING(L"Session");
1045 | UNICODE_STRING usBNO1 = RTL_CONSTANT_STRING(L"\\BaseNamedObjects");
1046 | PACCESS_ALLOWED_ACE pTempAce = nullptr;
1047 | UNICODE_STRING usNamedPipe = { 0 };
1048 | IO_STATUS_BLOCK IoStatusBlock;
1049 | UNICODE_STRING usAppContainerSID = { 0 };
1050 | HANDLE hBaseNamedObjects = nullptr;
1051 | PSECURITY_DESCRIPTOR pSD = nullptr;
1052 | PTOKEN_USER pTokenUser = nullptr;
1053 | PSECURITY_DESCRIPTOR pDirectorySD = nullptr;
1054 | PSECURITY_DESCRIPTOR pRpcControlSD = nullptr;
1055 | HANDLE hAppContainerNamedObjects = nullptr;
1056 | HANDLE hRpcControl = nullptr;
1057 | HANDLE HandleList[6] = { nullptr };
1058 |
1059 | // 获取ntdll.dll地址
1060 | M2InitUnicodeString(usNTDLL, L"ntdll.dll");
1061 | status = LdrGetDllHandleEx(0, nullptr, nullptr, &usNTDLL, &pNTDLL);
1062 | if (!NT_SUCCESS(status)) goto FuncEnd;
1063 |
1064 | // 获取NtCreateLowBoxToken地址
1065 | M2InitString(asFuncName, "NtCreateLowBoxToken");
1066 | status = LdrGetProcedureAddress(
1067 | pNTDLL, &asFuncName, 0,
1068 | reinterpret_cast(&pNtCreateLowBoxToken));
1069 | if (!NT_SUCCESS(status)) goto FuncEnd;
1070 |
1071 | // 获取NtCreateDirectoryObjectEx地址
1072 | M2InitString(asFuncName, "NtCreateDirectoryObjectEx");
1073 | status = LdrGetProcedureAddress(
1074 | pNTDLL, &asFuncName, 0,
1075 | reinterpret_cast(&pNtCreateDirectoryObjectEx));
1076 | if (!NT_SUCCESS(status)) goto FuncEnd;
1077 |
1078 | // 获取令牌会话ID
1079 | status = NtQueryInformationToken(
1080 | ExistingTokenHandle,
1081 | TokenSessionId,
1082 | &TokenSessionID,
1083 | sizeof(DWORD),
1084 | &ReturnLength);
1085 | if (!NT_SUCCESS(status)) goto FuncEnd;
1086 |
1087 | // 把SID转换为Unicode字符串
1088 | status = RtlConvertSidToUnicodeString(
1089 | &usAppContainerSID, SecurityCapabilities->AppContainerSid, TRUE);
1090 | if (!NT_SUCCESS(status)) goto FuncEnd;
1091 |
1092 | // 如果SessionID不为0,则生成对应会话的路径
1093 | if (TokenSessionID)
1094 | {
1095 | StringCbPrintfW(
1096 | Buffer, sizeof(Buffer),
1097 | L"\\Sessions\\%ld\\BaseNamedObjects", TokenSessionID);
1098 |
1099 | M2InitUnicodeString(usBNO, Buffer);
1100 | }
1101 |
1102 | // 初始化用于打开BaseNamedObjects目录对象的OBJECT_ATTRIBUTES结构
1103 | M2InitObjectAttributes(ObjectAttributes, &usBNO);
1104 |
1105 | // 打开BaseNamedObjects目录对象
1106 | status = NtOpenDirectoryObject(
1107 | &hBaseNamedObjects,
1108 | READ_CONTROL | DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
1109 | &ObjectAttributes);
1110 | if (!NT_SUCCESS(status)) goto FuncEnd;
1111 |
1112 | // 获取BaseNamedObjects目录安全标识符信息大小
1113 | NtQuerySecurityObject(
1114 | hBaseNamedObjects, DACL_SECURITY_INFORMATION,
1115 | nullptr, 0, &ReturnLength);
1116 |
1117 | // 为安全标识符分配内存
1118 | status = M2HeapAlloc(ReturnLength, pSD);
1119 | if (!NT_SUCCESS(status)) goto FuncEnd;
1120 |
1121 | // 获取BaseNamedObjects目录安全标识符信息
1122 | status = NtQuerySecurityObject(
1123 | hBaseNamedObjects, DACL_SECURITY_INFORMATION,
1124 | pSD, ReturnLength, &ReturnLength);
1125 | if (!NT_SUCCESS(status)) goto FuncEnd;
1126 |
1127 | // 获取令牌用户信息大小
1128 | status = NtQueryInformationToken(
1129 | ExistingTokenHandle, TokenUser,
1130 | nullptr, 0, &ReturnLength);
1131 | if (status != STATUS_BUFFER_TOO_SMALL) goto FuncEnd;
1132 |
1133 | // 为令牌用户信息分配内存
1134 | status = M2HeapAlloc(ReturnLength, pTokenUser);
1135 | if (!NT_SUCCESS(status)) goto FuncEnd;
1136 |
1137 | // 获取令牌用户信息
1138 | status = NtQueryInformationToken(
1139 | ExistingTokenHandle, TokenUser,
1140 | pTokenUser, ReturnLength, &ReturnLength);
1141 | if (!NT_SUCCESS(status)) goto FuncEnd;
1142 |
1143 | // 创建AppContainer对象目录安全标识符
1144 | status = SuBuildAppContainerSecurityDescriptor(
1145 | pSD,
1146 | SecurityCapabilities->AppContainerSid,
1147 | pTokenUser->User.Sid,
1148 | false,
1149 | &pDirectorySD);
1150 | if (!NT_SUCCESS(status)) goto FuncEnd;
1151 |
1152 | // 创建AppContainer RPC对象目录安全标识符
1153 | status = SuBuildAppContainerSecurityDescriptor(
1154 | pSD,
1155 | SecurityCapabilities->AppContainerSid,
1156 | pTokenUser->User.Sid,
1157 | true,
1158 | &pRpcControlSD);
1159 | if (!NT_SUCCESS(status)) goto FuncEnd;
1160 |
1161 | // 初始化AppContainerNamedObjects对象目录路径字符串
1162 | StringCbPrintfW(
1163 | Buffer, sizeof(Buffer),
1164 | L"\\Sessions\\%ld\\AppContainerNamedObjects", TokenSessionID);
1165 |
1166 | // 初始化AppContainerNamedObjects对象目录路径UNICODE_STRING结构
1167 | M2InitUnicodeString(usACNO, Buffer);
1168 |
1169 | // 初始化用于打开AppContainerNamedObjects目录对象的OBJECT_ATTRIBUTES结构
1170 | M2InitObjectAttributes(ObjectAttributes, &usACNO);
1171 |
1172 | // 打开AppContainerNamedObjects目录对象
1173 | status = NtOpenDirectoryObject(
1174 | &hAppContainerNamedObjects,
1175 | DIRECTORY_QUERY | DIRECTORY_TRAVERSE |
1176 | DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_SUBDIRECTORY,
1177 | &ObjectAttributes);
1178 | if (!NT_SUCCESS(status)) goto FuncEnd;
1179 |
1180 | // 初始化用于创建AppContainer目录对象的OBJECT_ATTRIBUTES结构
1181 | M2InitObjectAttributes(
1182 | ObjectAttributes,
1183 | &usAppContainerSID,
1184 | OBJ_INHERIT | OBJ_OPENIF,
1185 | hAppContainerNamedObjects,
1186 | pDirectorySD);
1187 |
1188 | // 创建AppContainer目录对象
1189 | status = pNtCreateDirectoryObjectEx(
1190 | &HandleList[SuAppContainerHandleList::RootDirectory],
1191 | DIRECTORY_QUERY | DIRECTORY_TRAVERSE |
1192 | DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_SUBDIRECTORY,
1193 | &ObjectAttributes,
1194 | hBaseNamedObjects,
1195 | 1);
1196 | if (!NT_SUCCESS(status)) goto FuncEnd;
1197 |
1198 | // 设置AppContainer目录对象完整性标签为低
1199 | status = SuSetKernelObjectIntegrityLevel(
1200 | HandleList[SuAppContainerHandleList::RootDirectory],
1201 | TOKEN_INTEGRITY_LEVELS_LIST::LowLevel);
1202 | if (!NT_SUCCESS(status)) goto FuncEnd;
1203 |
1204 | // 初始化用于打开RPC Control目录对象的OBJECT_ATTRIBUTES结构
1205 | M2InitObjectAttributes(ObjectAttributes, &usRpcControl);
1206 |
1207 | // 打开RPC Control目录对象
1208 | status = NtOpenDirectoryObject(
1209 | &hRpcControl,
1210 | DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
1211 | &ObjectAttributes);
1212 | if (!NT_SUCCESS(status)) goto FuncEnd;
1213 |
1214 | // 初始化用于创建AppContainer RPC Control目录对象的OBJECT_ATTRIBUTES结构
1215 | M2InitObjectAttributes(
1216 | ObjectAttributes,
1217 | &usRpcControl2,
1218 | OBJ_INHERIT | OBJ_OPENIF,
1219 | HandleList[SuAppContainerHandleList::RootDirectory],
1220 | pRpcControlSD);
1221 |
1222 | // 创建AppContainer RPC Control目录对象
1223 | status = pNtCreateDirectoryObjectEx(
1224 | &HandleList[SuAppContainerHandleList::RpcDirectory],
1225 | DIRECTORY_QUERY | DIRECTORY_TRAVERSE |
1226 | DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_SUBDIRECTORY,
1227 | &ObjectAttributes,
1228 | hRpcControl,
1229 | 1);
1230 | if (!NT_SUCCESS(status)) goto FuncEnd;
1231 |
1232 | // 设置AppContainer RPC Control目录对象完整性标签为低
1233 | status = SuSetKernelObjectIntegrityLevel(
1234 | HandleList[SuAppContainerHandleList::RpcDirectory],
1235 | TOKEN_INTEGRITY_LEVELS_LIST::LowLevel);
1236 | if (!NT_SUCCESS(status)) goto FuncEnd;
1237 |
1238 | // 初始化AppContainer目录对象字符串
1239 | StringCbPrintfW(
1240 | Buffer, sizeof(Buffer),
1241 | L"\\Sessions\\%d\\AppContainerNamedObjects\\%ws",
1242 | TokenSessionID,
1243 | usAppContainerSID.Buffer, usAppContainerSID.Length);
1244 |
1245 | // 初始化AppContainer目录对象的UNICODE_STRING结构
1246 | M2InitUnicodeString(usRootDirectory, Buffer);
1247 |
1248 | // 初始化用于创建Global符号链接对象的OBJECT_ATTRIBUTES结构
1249 | M2InitObjectAttributes(
1250 | ObjectAttributes,
1251 | &usGlobal,
1252 | OBJ_INHERIT | OBJ_OPENIF,
1253 | HandleList[SuAppContainerHandleList::RootDirectory],
1254 | pDirectorySD);
1255 |
1256 | // 在AppContainer目录对象下创建Global符号链接对象
1257 | status = NtCreateSymbolicLinkObject(
1258 | &HandleList[SuAppContainerHandleList::GlobalSymbolicLink],
1259 | SYMBOLIC_LINK_ALL_ACCESS,
1260 | &ObjectAttributes,
1261 | &usBNO1);
1262 | if (!NT_SUCCESS(status)) goto FuncEnd;
1263 |
1264 | // 初始化用于创建Local符号链接对象的OBJECT_ATTRIBUTES结构
1265 | M2InitObjectAttributes(
1266 | ObjectAttributes,
1267 | &usLocal,
1268 | OBJ_INHERIT | OBJ_OPENIF,
1269 | HandleList[SuAppContainerHandleList::RootDirectory],
1270 | pDirectorySD);
1271 |
1272 | // 在AppContainer目录对象下创建Local符号链接对象
1273 | status = NtCreateSymbolicLinkObject(
1274 | &HandleList[SuAppContainerHandleList::LocalSymbolicLink],
1275 | SYMBOLIC_LINK_ALL_ACCESS,
1276 | &ObjectAttributes,
1277 | &usRootDirectory);
1278 | if (!NT_SUCCESS(status)) goto FuncEnd;
1279 |
1280 | // 初始化用于创建Session符号链接对象的OBJECT_ATTRIBUTES结构
1281 | M2InitObjectAttributes(
1282 | ObjectAttributes,
1283 | &usSession,
1284 | OBJ_INHERIT | OBJ_OPENIF,
1285 | HandleList[SuAppContainerHandleList::RootDirectory],
1286 | pDirectorySD);
1287 |
1288 | // 在AppContainer目录对象下创建Session符号链接对象
1289 | status = NtCreateSymbolicLinkObject(
1290 | &HandleList[SuAppContainerHandleList::SessionSymbolicLink],
1291 | SYMBOLIC_LINK_ALL_ACCESS,
1292 | &ObjectAttributes,
1293 | &usRootDirectory);
1294 | if (!NT_SUCCESS(status)) goto FuncEnd;
1295 |
1296 | // 初始化AppContainer命名管道路径字符串
1297 | StringCbPrintfW(
1298 | Buffer, sizeof(Buffer),
1299 | L"\\Device\\NamedPipe\\Sessions\\%d\\AppContainerNamedObjects\\%ws",
1300 | TokenSessionID,
1301 | usAppContainerSID.Buffer, usAppContainerSID.Length);
1302 |
1303 | for (ULONG i = 0;
1304 | NT_SUCCESS(RtlGetAce(
1305 | ((SECURITY_DESCRIPTOR*)pDirectorySD)->Dacl,
1306 | i,
1307 | (PVOID*)&pTempAce));
1308 | ++i)
1309 | {
1310 | DWORD LowMask = LOWORD(pTempAce->Mask);
1311 |
1312 | // 清零pTempAce->Mask低16位
1313 | pTempAce->Mask &= ~0xFFFF;
1314 |
1315 | if (FILE_CREATE_PIPE_INSTANCE == (LowMask & FILE_CREATE_PIPE_INSTANCE))
1316 | pTempAce->Mask |= SYNCHRONIZE | FILE_WRITE_DATA;
1317 |
1318 | if (FILE_READ_EA == (LowMask & FILE_READ_EA))
1319 | pTempAce->Mask |= SYNCHRONIZE | FILE_CREATE_PIPE_INSTANCE;
1320 | }
1321 |
1322 | // 初始化AppContainer命名管道UNICODE_STRING结构
1323 | M2InitUnicodeString(usNamedPipe, Buffer);
1324 |
1325 | // 初始化创建AppContainer命名管道的OBJECT_ATTRIBUTES结构
1326 | M2InitObjectAttributes(
1327 | ObjectAttributes,
1328 | &usNamedPipe,
1329 | OBJ_INHERIT | OBJ_CASE_INSENSITIVE,
1330 | nullptr,
1331 | pDirectorySD,
1332 | nullptr);
1333 |
1334 | // 创建AppContainer命名管道
1335 | status = NtCreateFile(
1336 | &HandleList[SuAppContainerHandleList::NamedPipe],
1337 | PIPE_ALL_ACCESS,
1338 | &ObjectAttributes,
1339 | &IoStatusBlock,
1340 | nullptr,
1341 | FILE_ATTRIBUTE_NORMAL,
1342 | FILE_SHARE_READ | FILE_SHARE_WRITE,
1343 | FILE_OPEN_IF,
1344 | FILE_DIRECTORY_FILE,
1345 | nullptr,
1346 | 0);
1347 | if (!NT_SUCCESS(status)) goto FuncEnd;
1348 |
1349 | // 初始化用于创建AppContainer令牌对象的OBJECT_ATTRIBUTES结构
1350 | M2InitObjectAttributes(ObjectAttributes);
1351 |
1352 | // 创建AppContainer令牌
1353 | status = pNtCreateLowBoxToken(
1354 | TokenHandle,
1355 | ExistingTokenHandle,
1356 | MAXIMUM_ALLOWED,
1357 | &ObjectAttributes,
1358 | SecurityCapabilities->AppContainerSid,
1359 | SecurityCapabilities->CapabilityCount,
1360 | SecurityCapabilities->Capabilities,
1361 | 6,
1362 | HandleList);
1363 |
1364 | FuncEnd: // 结束处理
1365 |
1366 | for (size_t i = 0; i < 6; ++i) NtClose(HandleList[i]);
1367 | NtClose(hRpcControl);
1368 | NtClose(hAppContainerNamedObjects);
1369 | M2HeapFree(pRpcControlSD);
1370 | M2HeapFree(pDirectorySD);
1371 | M2HeapFree(pTokenUser);
1372 | M2HeapFree(pSD);
1373 | NtClose(hBaseNamedObjects);
1374 | RtlFreeUnicodeString(&usAppContainerSID);
1375 |
1376 | return status;
1377 | }
1378 |
1379 | /*
1380 | SuGenerateRandomAppContainerSid函数生成一个随机AppContainer SID
1381 | The SuGenerateRandomAppContainerSid function generates a random AppContainer
1382 | SID.
1383 | */
1384 | void WINAPI SuGenerateRandomAppContainerSid(
1385 | _Out_ PSID *RandomAppContainerSid)
1386 | {
1387 | LARGE_INTEGER PerfCounter, PerfFrequency;
1388 |
1389 | // 获取性能计数器数值
1390 | NtQueryPerformanceCounter(&PerfCounter, &PerfFrequency);
1391 |
1392 | //生成种子
1393 | ULONG seed = (ULONG)(PerfCounter.QuadPart - PerfFrequency.QuadPart);
1394 |
1395 | RtlAllocateAndInitializeSid(
1396 | &SIA_App,
1397 | SECURITY_APP_PACKAGE_RID_COUNT,
1398 | SECURITY_APP_PACKAGE_BASE_RID,
1399 | (DWORD)RtlRandomEx(&seed),
1400 | (DWORD)RtlRandomEx(&seed),
1401 | (DWORD)RtlRandomEx(&seed),
1402 | (DWORD)RtlRandomEx(&seed),
1403 | (DWORD)RtlRandomEx(&seed),
1404 | (DWORD)RtlRandomEx(&seed),
1405 | (DWORD)RtlRandomEx(&seed),
1406 | RandomAppContainerSid);
1407 | }
1408 |
1409 | /*
1410 | SuGenerateAppContainerCapabilities函数生成一个AppContainer能力列表.你应该调
1411 | 用M2HeapFree释放你生成的能力列表。
1412 | The SuGenerateAppContainerCapabilities function generates an AppContainer
1413 | capabilities list. You should call M2HeapFree to free the memory the list
1414 | which you generated.
1415 | */
1416 | NTSTATUS WINAPI SuGenerateAppContainerCapabilities(
1417 | _Out_ PSID_AND_ATTRIBUTES *Capabilities,
1418 | _In_ DWORD *CapabilitiyRIDs,
1419 | _In_ DWORD CapabilityCount)
1420 | {
1421 | NTSTATUS status = STATUS_SUCCESS;
1422 |
1423 | //设置参数及分配内存
1424 | status = M2HeapAlloc(
1425 | CapabilityCount * sizeof(SID_AND_ATTRIBUTES), *Capabilities);
1426 | if (!NT_SUCCESS(status)) goto Error;
1427 |
1428 | //获取能力SID
1429 | for (DWORD i = 0; i < CapabilityCount; i++)
1430 | {
1431 | (*Capabilities)[i].Attributes = SE_GROUP_ENABLED;
1432 | status = RtlAllocateAndInitializeSid(
1433 | &SIA_App,
1434 | SECURITY_BUILTIN_CAPABILITY_RID_COUNT,
1435 | SECURITY_CAPABILITY_BASE_RID, CapabilitiyRIDs[i],
1436 | 0, 0, 0, 0, 0, 0,
1437 | &(*Capabilities)[i].Sid);
1438 | if (!NT_SUCCESS(status)) goto Error;
1439 | }
1440 |
1441 | return status;
1442 |
1443 | Error: // 错误处理
1444 |
1445 | if (*Capabilities)
1446 | {
1447 | for (DWORD i = 0; i < CapabilityCount; i++)
1448 | if ((*Capabilities)[i].Sid) RtlFreeSid((*Capabilities)[i].Sid);
1449 |
1450 | M2HeapFree(*Capabilities);
1451 | *Capabilities = nullptr;
1452 | }
1453 |
1454 | return status;
1455 | }
1456 |
1457 | /*
1458 | SuCLRExecuteAssembly函数执行指定的.Net程序集。入口方法格式为:
1459 | The SuCLRExecuteAssembly function executes the specified .Net Assembly.
1460 | The format of the entry method:
1461 |
1462 | static int pwzMethodName(String pwzArgument);
1463 | */
1464 | static HRESULT WINAPI SuCLRExecuteAssembly(
1465 | _In_ LPCWSTR pwzVersion,
1466 | _In_ LPCWSTR pwzAssemblyPath,
1467 | _In_ LPCWSTR pwzTypeName,
1468 | _In_ LPCWSTR pwzMethodName,
1469 | _In_ LPCWSTR pwzArgument)
1470 | {
1471 | HRESULT hr = E_NOTIMPL;
1472 |
1473 | ICLRMetaHost *pMetaHost = nullptr;
1474 | ICLRRuntimeInfo *pRuntimeInfo = nullptr;
1475 |
1476 | // ICorRuntimeHost和ICLRRuntimeHost是CLR 4.0支持的两个CLR宿主接口
1477 | // 以下是使用.Net v2.0提供的ICLRRuntimeHost接口以支持CLR 2.0新功能的示例
1478 | // ICLRRuntimeHost不支持加载.NET v1.x运行时.
1479 | ICLRRuntimeHost *pClrRuntimeHost = nullptr;
1480 |
1481 | // The static method in the .NET class to invoke.
1482 | DWORD pReturnValue = 0;
1483 |
1484 | //
1485 | // 加载并启动.NET运行时.
1486 | //
1487 |
1488 | wprintf(L"Load and start the .NET runtime %s \n", pwzVersion);
1489 |
1490 | PVOID pDllModule = nullptr;
1491 |
1492 | NTSTATUS status = STATUS_SUCCESS;
1493 | CLRCreateInstanceFnPtr pCLRCreateInstance = nullptr;
1494 |
1495 | status = M2LoadModule(pDllModule, L"mscoree.dll");
1496 | if (!NT_SUCCESS(status))
1497 | {
1498 | hr = __HRESULT_FROM_WIN32(RtlNtStatusToDosError(status));
1499 | goto Cleanup;
1500 | }
1501 |
1502 | status = M2GetProcedureAddress(
1503 | pCLRCreateInstance, pDllModule, "CLRCreateInstance");
1504 | if (!NT_SUCCESS(status))
1505 | {
1506 | hr = __HRESULT_FROM_WIN32(RtlNtStatusToDosError(status));
1507 | goto Cleanup;
1508 | }
1509 |
1510 | hr = pCLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
1511 | if (FAILED(hr))
1512 | {
1513 | wprintf(L"CLRCreateInstance failed w/hr 0x%08lx\n", hr);
1514 | goto Cleanup;
1515 | }
1516 |
1517 | // 获取对应CLR版本的ICLRRuntimeInfo接口
1518 | hr = pMetaHost->GetRuntime(pwzVersion, IID_PPV_ARGS(&pRuntimeInfo));
1519 | if (FAILED(hr))
1520 | {
1521 | wprintf(L"ICLRMetaHost::GetRuntime failed w/hr 0x%08lx\n", hr);
1522 | goto Cleanup;
1523 | }
1524 |
1525 | // 检测特定版本的运行时是否可以加载入当前进程
1526 | BOOL fLoadable = FALSE;
1527 | hr = pRuntimeInfo->IsLoadable(&fLoadable);
1528 | if (FAILED(hr))
1529 | {
1530 | wprintf(L"ICLRRuntimeInfo::IsLoadable failed w/hr 0x%08lx\n", hr);
1531 | goto Cleanup;
1532 | }
1533 |
1534 | if (!fLoadable)
1535 | {
1536 | wprintf(L".NET runtime %s cannot be loaded\n", pwzVersion);
1537 | goto Cleanup;
1538 | }
1539 |
1540 | // 加载特定版本CLR到当前进程,并获取ICLRRuntimeHost接口
1541 | hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost,
1542 | IID_PPV_ARGS(&pClrRuntimeHost));
1543 | if (FAILED(hr))
1544 | {
1545 | wprintf(L"ICLRRuntimeInfo::GetInterface failed w/hr 0x%08lx\n", hr);
1546 | goto Cleanup;
1547 | }
1548 |
1549 | // 启动CLR.
1550 | hr = pClrRuntimeHost->Start();
1551 | if (FAILED(hr))
1552 | {
1553 | wprintf(L"CLR failed to start w/hr 0x%08lx\n", hr);
1554 | goto Cleanup;
1555 | }
1556 |
1557 | wprintf(L"Load the assembly %s\n", pwzAssemblyPath);
1558 |
1559 | // 调用pwzAssemblyPath程序集pwzTypeName类的方法并在pReturnValue返回运行结果
1560 | // 方法格式为 static int pwzMethodName(String pwzArgument)
1561 | hr = pClrRuntimeHost->ExecuteInDefaultAppDomain(
1562 | pwzAssemblyPath,
1563 | pwzTypeName,
1564 | pwzMethodName,
1565 | pwzArgument,
1566 | &pReturnValue);
1567 | if (FAILED(hr))
1568 | {
1569 | wprintf(L"Failed to call %s w/hr 0x%08lx\n", pwzMethodName, hr);
1570 | goto Cleanup;
1571 | }
1572 |
1573 | // Print the call result of the static method.
1574 | wprintf(L"Call %s.%s(\"%s\") => %d\n", pwzTypeName, pwzMethodName,
1575 | pwzArgument, (int)pReturnValue);
1576 |
1577 | Cleanup:
1578 |
1579 | if (pMetaHost)
1580 | {
1581 | pMetaHost->Release();
1582 | pMetaHost = nullptr;
1583 | }
1584 | if (pRuntimeInfo)
1585 | {
1586 | pRuntimeInfo->Release();
1587 | pRuntimeInfo = nullptr;
1588 | }
1589 | if (pClrRuntimeHost)
1590 | {
1591 | pClrRuntimeHost->Release();
1592 | pClrRuntimeHost = nullptr;
1593 | }
1594 |
1595 | if (pDllModule)
1596 | {
1597 | M2FreeModule(pDllModule);
1598 | pDllModule = nullptr;
1599 | }
1600 |
1601 | return hr;
1602 | }
1603 |
1604 | /*
1605 | SuCreateProcess函数创建一个新进程和对应的主线程
1606 | The SuCreateProcess function creates a new process and its primary thread.
1607 | */
1608 | static HRESULT WINAPI SuCreateProcess(
1609 | _In_opt_ HANDLE hToken,
1610 | _In_opt_ LPCWSTR lpApplicationName,
1611 | _Inout_opt_ LPWSTR lpCommandLine,
1612 | _In_ DWORD dwCreationFlags,
1613 | _In_opt_ LPVOID lpEnvironment,
1614 | _In_opt_ LPCWSTR lpCurrentDirectory,
1615 | _In_ LPSTARTUPINFOW lpStartupInfo,
1616 | _Out_ LPPROCESS_INFORMATION lpProcessInformation)
1617 | {
1618 | HRESULT hr = S_OK;
1619 |
1620 | if (!CreateProcessAsUserW(
1621 | hToken,
1622 | lpApplicationName,
1623 | lpCommandLine,
1624 | nullptr,
1625 | nullptr,
1626 | FALSE,
1627 | dwCreationFlags,
1628 | lpEnvironment,
1629 | lpCurrentDirectory,
1630 | lpStartupInfo,
1631 | lpProcessInformation))
1632 | {
1633 | if (!CreateProcessWithTokenW(
1634 | hToken,
1635 | LOGON_WITH_PROFILE,
1636 | lpApplicationName,
1637 | lpCommandLine,
1638 | dwCreationFlags,
1639 | lpEnvironment,
1640 | lpCurrentDirectory,
1641 | lpStartupInfo,
1642 | lpProcessInformation))
1643 | {
1644 | hr = __HRESULT_FROM_WIN32(M2GetLastError());
1645 | }
1646 | }
1647 |
1648 | return hr;
1649 | }
1650 |
1651 | class CSuProcessSnapshot
1652 | {
1653 | public:
1654 | /*
1655 | 初始化进程快照
1656 | Initialize the Process Snapshot
1657 | */
1658 | CSuProcessSnapshot(
1659 | _Out_ PNTSTATUS Status)
1660 | {
1661 | *Status = this->Refresh();
1662 | }
1663 |
1664 | /*
1665 | 反初始化进程快照
1666 | Uninitialize the Process Snapshot
1667 | */
1668 | ~CSuProcessSnapshot()
1669 | {
1670 | if (lpBuffer) M2HeapFree(lpBuffer);
1671 | }
1672 |
1673 | /*
1674 | 刷新进程快照
1675 | Refresh the Process Snapshot
1676 | */
1677 | NTSTATUS Refresh()
1678 | {
1679 | NTSTATUS status = STATUS_SUCCESS;
1680 | DWORD dwLength = 0;
1681 |
1682 | do
1683 | {
1684 | // 获取进程信息大小
1685 | status = NtQuerySystemInformation(
1686 | SystemProcessInformation,
1687 | nullptr,
1688 | 0,
1689 | &dwLength);
1690 | if (status != STATUS_INFO_LENGTH_MISMATCH) break;
1691 |
1692 | // 为令牌信息分配内存,如果失败则返回
1693 | status = M2HeapAlloc(
1694 | dwLength,
1695 | lpBuffer);
1696 | if (!NT_SUCCESS(status)) break;
1697 |
1698 | // 获取进程信息
1699 | status = NtQuerySystemInformation(
1700 | SystemProcessInformation,
1701 | lpBuffer,
1702 | dwLength,
1703 | &dwLength);
1704 |
1705 | // 设置遍历开始地址
1706 | pTemp = (ULONG_PTR)(PVOID)lpBuffer;
1707 |
1708 | } while (false);
1709 |
1710 | return status;
1711 | }
1712 |
1713 | /*
1714 | 遍历进程快照
1715 | Enumerate the Process Snapshot
1716 | */
1717 | bool Next(
1718 | _Out_ PSYSTEM_PROCESS_INFORMATION *pSPI)
1719 | {
1720 | *pSPI = (PSYSTEM_PROCESS_INFORMATION)pTemp;
1721 |
1722 | // 如果*pSPI=0或下个结构偏移=0时则pTemp=0,否则pTemp=下个结构地址
1723 | if (!*pSPI || !(*pSPI)->NextEntryOffset) pTemp = 0;
1724 | else pTemp += (*pSPI)->NextEntryOffset;
1725 |
1726 | // 返回执行结果
1727 | return (*pSPI != nullptr);
1728 | }
1729 |
1730 | private:
1731 | PVOID lpBuffer;
1732 | ULONG_PTR pTemp = 0;
1733 | };
1734 |
1735 |
1736 | #if _MSC_VER >= 1200
1737 | #pragma warning(push)
1738 | #pragma warning(disable:4355) // "this": 用于基成员初始值设定项列表
1739 | #endif
1740 |
1741 | /*
1742 | 进程列表遍历迭代器
1743 | Iterator for enumerate the process list
1744 |
1745 | 用法 Usage
1746 | for (auto pSPI : CM2EnumProcess(status)) { }
1747 |
1748 | status 是初始化遍历返回值(可选)
1749 | status is the return value for initialization (Optional)
1750 | */
1751 | class CM2EnumProcess
1752 | {
1753 | public:
1754 | class CM2EnumProcessIterator
1755 | {
1756 | private:
1757 | CM2EnumProcess* m_EnumProcess;
1758 |
1759 | public:
1760 | FORCEINLINE CM2EnumProcessIterator(
1761 | _In_ CM2EnumProcess* FindFile) :
1762 | m_EnumProcess(FindFile)
1763 | {
1764 |
1765 | }
1766 |
1767 | FORCEINLINE ~CM2EnumProcessIterator()
1768 | {
1769 |
1770 | }
1771 |
1772 | FORCEINLINE void operator++()
1773 | {
1774 | // 如果pSPI和下个结构偏移都存在,则继续循环,否则清零
1775 | if (m_EnumProcess->pSPI && m_EnumProcess->pSPI->NextEntryOffset)
1776 | {
1777 | ULONG_PTR NextSPI = reinterpret_cast(m_EnumProcess->pSPI);
1778 | NextSPI += m_EnumProcess->pSPI->NextEntryOffset;
1779 | m_EnumProcess->pSPI = reinterpret_cast(NextSPI);
1780 | }
1781 | else
1782 | {
1783 | m_EnumProcess->pSPI = nullptr;
1784 | }
1785 | }
1786 |
1787 | // 根据迭代器循环特性,使用不等于操作符遍历目录
1788 | FORCEINLINE bool operator!=(const CM2EnumProcessIterator& Item)
1789 | {
1790 | UNREFERENCED_PARAMETER(Item);
1791 | return (m_EnumProcess->pSPI != nullptr);
1792 | }
1793 |
1794 | FORCEINLINE PSYSTEM_PROCESS_INFORMATION operator*()
1795 | {
1796 | return m_EnumProcess->pSPI;
1797 | }
1798 | };
1799 |
1800 | private:
1801 | CM2EnumProcessIterator Iterator;
1802 | PVOID lpBuffer;
1803 | PSYSTEM_PROCESS_INFORMATION pSPI;
1804 |
1805 | public:
1806 | // 初始化文件遍历, 不内联考虑到大量使用本迭代器时实现函数复用以节约空间
1807 | DECLSPEC_NOINLINE CM2EnumProcess(
1808 | _Out_ NTSTATUS* InitStatus = nullptr) :
1809 | Iterator(this),
1810 | lpBuffer(nullptr),
1811 | pSPI(nullptr)
1812 |
1813 | {
1814 | NTSTATUS status = STATUS_SUCCESS;
1815 | DWORD dwLength = 0;
1816 |
1817 | do
1818 | {
1819 | // 获取进程信息大小
1820 | status = NtQuerySystemInformation(
1821 | SystemProcessInformation,
1822 | nullptr,
1823 | 0,
1824 | &dwLength);
1825 | if (status != STATUS_INFO_LENGTH_MISMATCH) break;
1826 |
1827 | // 为令牌信息分配内存,如果失败则返回
1828 | status = M2HeapAlloc(
1829 | dwLength,
1830 | lpBuffer);
1831 | if (!NT_SUCCESS(status)) break;
1832 |
1833 | // 获取进程信息
1834 | status = NtQuerySystemInformation(
1835 | SystemProcessInformation,
1836 | lpBuffer,
1837 | dwLength,
1838 | &dwLength);
1839 | if (!NT_SUCCESS(status)) break;
1840 |
1841 | // 设置遍历开始地址
1842 | pSPI = reinterpret_cast(lpBuffer);
1843 |
1844 | } while (false);
1845 |
1846 | if (InitStatus) *InitStatus = status;
1847 | }
1848 |
1849 | FORCEINLINE ~CM2EnumProcess()
1850 | {
1851 | if (lpBuffer) M2HeapFree(lpBuffer);
1852 | }
1853 |
1854 | FORCEINLINE CM2EnumProcessIterator& begin()
1855 | {
1856 | return Iterator;
1857 | }
1858 |
1859 | FORCEINLINE CM2EnumProcessIterator& end()
1860 | {
1861 | return Iterator;
1862 | }
1863 | };
1864 |
1865 | #if _MSC_VER >= 1200
1866 | #pragma warning(pop)
1867 | #endif
1868 |
1869 | /*
1870 | SuGetSystemTokenCopy函数获取一个当前会话SYSTEM用户令牌的副本。
1871 | The SuGetSystemTokenCopy function obtains a copy of current session SYSTEM
1872 | user token.
1873 | */
1874 | static NTSTATUS WINAPI SuGetSystemTokenCopy(
1875 | _In_ DWORD dwDesiredAccess,
1876 | _In_opt_ LPSECURITY_ATTRIBUTES lpTokenAttributes,
1877 | _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
1878 | _In_ TOKEN_TYPE TokenType,
1879 | _Outptr_ PHANDLE phToken)
1880 | {
1881 | NTSTATUS status = STATUS_SUCCESS;
1882 | DWORD dwWinLogonPID = (DWORD)-1;
1883 | DWORD dwSessionID = (DWORD)-1;
1884 | HANDLE hProcessToken = nullptr;
1885 |
1886 | do
1887 | {
1888 | // 获取当前进程令牌会话ID
1889 | status = SuGetCurrentProcessSessionID(&dwSessionID);
1890 | if (!NT_SUCCESS(status)) break;
1891 |
1892 | // 遍历进程寻找winlogon进程并获取PID
1893 | for (auto pSPI : CM2EnumProcess(&status))
1894 | {
1895 | if (pSPI->SessionId != dwSessionID) continue;
1896 | if (pSPI->ImageName.Buffer == nullptr) continue;
1897 |
1898 | if (wcscmp(L"winlogon.exe", pSPI->ImageName.Buffer) == 0)
1899 | {
1900 | dwWinLogonPID = HandleToUlong(pSPI->UniqueProcessId);
1901 | break;
1902 | }
1903 | }
1904 |
1905 | // 如果初始化进程遍历失败,则返回错误
1906 | if (!NT_SUCCESS(status)) break;
1907 |
1908 | // 如果没找到进程,则返回错误
1909 | if (dwWinLogonPID == -1)
1910 | {
1911 | status = STATUS_NOT_FOUND;
1912 | break;
1913 | }
1914 |
1915 | // 获取当前会话winlogon进程令牌
1916 | status = SuOpenProcessToken(
1917 | dwWinLogonPID, MAXIMUM_ALLOWED, &hProcessToken);
1918 | if (!NT_SUCCESS(status)) break;
1919 |
1920 | // 复制令牌
1921 | status = SuDuplicateToken(
1922 | hProcessToken,
1923 | dwDesiredAccess,
1924 | lpTokenAttributes,
1925 | ImpersonationLevel,
1926 | TokenType,
1927 | phToken);
1928 | if (!NT_SUCCESS(status)) break;
1929 |
1930 | } while (false);
1931 |
1932 | NtClose(hProcessToken);
1933 |
1934 | return status;
1935 | }
1936 |
1937 | /*
1938 | SuGetServiceProcessTokenCopy函数根据服务名获取一个服务进程令牌的副本。
1939 | The SuGetServiceProcessTokenCopy function obtains a copy of service process
1940 | token via service name.
1941 | */
1942 | static HRESULT WINAPI SuGetServiceProcessTokenCopy(
1943 | _In_ LPCWSTR lpServiceName,
1944 | _In_ DWORD dwDesiredAccess,
1945 | _In_opt_ LPSECURITY_ATTRIBUTES lpTokenAttributes,
1946 | _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
1947 | _In_ TOKEN_TYPE TokenType,
1948 | _Outptr_ PHANDLE phToken)
1949 | {
1950 | HRESULT hr = S_OK;
1951 | NTSTATUS status = STATUS_SUCCESS;
1952 | HANDLE hToken = nullptr;
1953 |
1954 | // 打开服务进程令牌
1955 | hr = SuOpenServiceProcessToken(
1956 | lpServiceName, MAXIMUM_ALLOWED, &hToken);
1957 | if (SUCCEEDED(hr))
1958 | {
1959 | // 复制令牌
1960 | status = SuDuplicateToken(
1961 | hToken,
1962 | dwDesiredAccess,
1963 | lpTokenAttributes,
1964 | ImpersonationLevel,
1965 | TokenType,
1966 | phToken);
1967 | hr = __HRESULT_FROM_WIN32(RtlNtStatusToDosError(status));
1968 |
1969 | NtClose(hToken);
1970 | }
1971 |
1972 | return hr;
1973 | }
1974 |
1975 | /*
1976 | SuGetSessionTokenCopy函数根据服务名获取一个服务进程令牌的副本。
1977 | The SuGetSessionTokenCopy function obtains a copy of Session token via
1978 | Session ID.
1979 | */
1980 | static HRESULT WINAPI SuGetSessionTokenCopy(
1981 | _In_ DWORD dwSessionID,
1982 | _In_ DWORD dwDesiredAccess,
1983 | _In_opt_ LPSECURITY_ATTRIBUTES lpTokenAttributes,
1984 | _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
1985 | _In_ TOKEN_TYPE TokenType,
1986 | _Outptr_ PHANDLE phToken)
1987 | {
1988 | HRESULT hr = S_OK;
1989 | NTSTATUS status = STATUS_SUCCESS;
1990 | HANDLE hToken = nullptr;
1991 |
1992 | // 打开会话令牌
1993 | hr = SuOpenSessionToken(dwSessionID, &hToken);
1994 | if (SUCCEEDED(hr))
1995 | {
1996 | // 复制令牌
1997 | status = SuDuplicateToken(
1998 | hToken,
1999 | dwDesiredAccess,
2000 | lpTokenAttributes,
2001 | ImpersonationLevel,
2002 | TokenType,
2003 | phToken);
2004 | hr = __HRESULT_FROM_WIN32(RtlNtStatusToDosError(status));
2005 |
2006 | NtClose(hToken);
2007 | }
2008 |
2009 | return hr;
2010 | }
2011 |
2012 | /*
2013 | SuGetProcessTokenCopy函数根据进程ID获取一个进程令牌的副本。
2014 | The SuGetProcessTokenCopy function obtains a copy of process token via
2015 | Process ID.
2016 | */
2017 | static NTSTATUS WINAPI SuGetProcessTokenCopy(
2018 | _In_ DWORD dwProcessID,
2019 | _In_ DWORD dwDesiredAccess,
2020 | _In_opt_ LPSECURITY_ATTRIBUTES lpTokenAttributes,
2021 | _In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
2022 | _In_ TOKEN_TYPE TokenType,
2023 | _Outptr_ PHANDLE phToken)
2024 | {
2025 | NTSTATUS status = STATUS_SUCCESS;
2026 | HANDLE hToken = nullptr;
2027 |
2028 | // 打开进程令牌
2029 | status = SuOpenProcessToken(dwProcessID, MAXIMUM_ALLOWED, &hToken);
2030 | if (NT_SUCCESS(status))
2031 | {
2032 | // 复制令牌
2033 | status = SuDuplicateToken(
2034 | hToken,
2035 | dwDesiredAccess,
2036 | lpTokenAttributes,
2037 | ImpersonationLevel,
2038 | TokenType,
2039 | phToken);
2040 |
2041 | NtClose(hToken);
2042 | }
2043 |
2044 | return status;
2045 | }
2046 |
2047 | /*
2048 | SuImpersonateAsSystem函数给当前线程分配一个SYSTEM用户模拟令牌。该函数还可以
2049 | 使当前线程停止使用模拟令牌。
2050 | The SuImpersonateAsSystem function assigns an SYSTEM user impersonation
2051 | token to the current thread. The function can also cause the current thread
2052 | to stop using an impersonation token.
2053 | */
2054 | static NTSTATUS WINAPI SuImpersonateAsSystem()
2055 | {
2056 | NTSTATUS status = STATUS_SUCCESS;
2057 | HANDLE hToken = nullptr;
2058 |
2059 | // 获取当前会话SYSTEM用户令牌副本
2060 | status = SuGetSystemTokenCopy(
2061 | MAXIMUM_ALLOWED,
2062 | nullptr,
2063 | SecurityImpersonation,
2064 | TokenImpersonation,
2065 | &hToken);
2066 | if (NT_SUCCESS(status))
2067 | {
2068 | // 启用令牌全部特权
2069 | status = SuSetTokenAllPrivileges(hToken, true);
2070 | if (NT_SUCCESS(status))
2071 | {
2072 | // 模拟令牌
2073 | status = SuSetCurrentThreadToken(hToken);
2074 | }
2075 |
2076 | NtClose(hToken);
2077 | }
2078 |
2079 | return status;
2080 | }
2081 |
2082 | #ifdef __cplusplus
2083 | }
2084 | #endif
2085 |
2086 | #endif // !_M2_NSUDO_
2087 |
--------------------------------------------------------------------------------
/M2TeamCommonLibrary/M2WindowsHelpers.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * PROJECT: M2-Team Common Library
3 | * FILE: M2WindowsHelpers.cpp
4 | * PURPOSE: Implementation for the Windows helper functions
5 | *
6 | * LICENSE: The MIT License
7 | *
8 | * DEVELOPER: Mouri_Naruto (Mouri_Naruto AT Outlook.com)
9 | */
10 |
11 | #include "stdafx.h"
12 |
13 | #include "M2WindowsHelpers.h"
14 |
15 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
16 | #include
17 | #endif
18 |
19 | #include
20 | #include
21 |
22 | #ifdef __cplusplus_winrt
23 | #include
24 | #include
25 | #include
26 |
27 | #include
28 | #include
29 |
30 | #include
31 |
32 | using Microsoft::WRL::ComPtr;
33 | using Microsoft::WRL::MakeAndInitialize;
34 | using Microsoft::WRL::RuntimeClass;
35 | using Microsoft::WRL::RuntimeClassFlags;
36 | using Microsoft::WRL::RuntimeClassType;
37 | #endif
38 |
39 | #pragma region Error
40 |
41 | /**
42 | * Retrieves the calling thread's last-error code value. The last-error code is
43 | * maintained on a per-thread basis. Multiple threads do not overwrite each
44 | * other's last-error code.
45 | *
46 | * @return The calling thread's last-error code which is converted to an
47 | * HRESULT value.
48 | */
49 | HRESULT M2GetLastHRESULTError()
50 | {
51 | return HRESULT_FROM_WIN32(GetLastError());
52 | }
53 |
54 | /**
55 | * Retrieves the calling thread's last-error code value if you can be sure that
56 | * the last call was failed. The last-error code is maintained on a per-thread
57 | * basis. Multiple threads do not overwrite each other's last-error code.
58 | *
59 | * @return The calling thread's last-error code.
60 | */
61 | DWORD M2GetLastErrorKnownFailedCall()
62 | {
63 | DWORD LastError = GetLastError();
64 | return (LastError != ERROR_SUCCESS) ? LastError : ERROR_FUNCTION_FAILED;
65 | }
66 |
67 | /**
68 | * Retrieves the calling thread's last-error code value if you can be sure that
69 | * the last call was failed. The last-error code is maintained on a per-thread
70 | * basis. Multiple threads do not overwrite each other's last-error code.
71 | *
72 | * @return The calling thread's last-error code which is converted to an
73 | * HRESULT value.
74 | */
75 | HRESULT M2GetLastHRESULTErrorKnownFailedCall()
76 | {
77 | return HRESULT_FROM_WIN32(M2GetLastErrorKnownFailedCall());
78 | }
79 |
80 | #ifdef __cplusplus_winrt
81 |
82 | /**
83 | * Throw the appropriate Platform::Exception for the given HRESULT.
84 | *
85 | * @param hr The error HRESULT that is represented by the exception.
86 | * @return This function does not return a value, but will throw
87 | * Platform::Exception.
88 | */
89 | __declspec(noreturn) void M2ThrowPlatformException(HRESULT hr)
90 | {
91 | throw Platform::Exception::CreateException(hr);
92 | }
93 |
94 | /**
95 | * Throw the appropriate Platform::Exception for the given HRESULT.
96 | *
97 | * @param hr The error HRESULT that is represented by the exception.
98 | * @return This function does not return a value, but will throw
99 | * Platform::Exception if it is a failed HRESULT value.
100 | */
101 | void M2ThrowPlatformExceptionIfFailed(HRESULT hr)
102 | {
103 | if (FAILED(hr))
104 | {
105 | M2ThrowPlatformException(hr);
106 | }
107 | }
108 |
109 | /**
110 | * Convert C++/CX exceptions in the callable code into HRESULTs.
111 | *
112 | * @return The function will return HRESULT.
113 | */
114 | HRESULT M2ThrownPlatformExceptionToHResult()
115 | {
116 | try
117 | {
118 | throw;
119 | }
120 | catch (Platform::Exception ^ ex)
121 | {
122 | return ex->HResult;
123 | }
124 | catch (std::bad_alloc const&)
125 | {
126 | return E_OUTOFMEMORY;
127 | }
128 | catch (...)
129 | {
130 | return E_UNEXPECTED;
131 | }
132 | }
133 |
134 | #endif
135 |
136 | #pragma endregion
137 |
138 | #pragma region String
139 |
140 | /**
141 | * Converts from the UTF-8 string to the UTF-16 string.
142 | *
143 | * @param UTF8String The UTF-8 string you want to convert.
144 | * @return A converted UTF-16 string.
145 | */
146 | std::wstring M2MakeUTF16String(const std::string& UTF8String)
147 | {
148 | std::wstring UTF16String;
149 |
150 | int UTF16StringLength = MultiByteToWideChar(
151 | CP_UTF8,
152 | 0,
153 | UTF8String.data(),
154 | (int)UTF8String.size(),
155 | nullptr,
156 | 0);
157 | if (UTF16StringLength > 0)
158 | {
159 | UTF16String.resize(UTF16StringLength);
160 | MultiByteToWideChar(
161 | CP_UTF8,
162 | 0,
163 | UTF8String.data(),
164 | (int)UTF8String.size(),
165 | &UTF16String[0],
166 | UTF16StringLength);
167 | }
168 |
169 | return UTF16String;
170 | }
171 |
172 | /**
173 | * Converts from the UTF-16 string to the UTF-8 string.
174 | *
175 | * @param UTF16String The UTF-16 string you want to convert.
176 | * @return A converted UTF-8 string.
177 | */
178 | std::string M2MakeUTF8String(const std::wstring& UTF16String)
179 | {
180 | std::string UTF8String;
181 |
182 | int UTF8StringLength = WideCharToMultiByte(
183 | CP_UTF8,
184 | 0,
185 | UTF16String.data(),
186 | (int)UTF16String.size(),
187 | nullptr,
188 | 0,
189 | nullptr,
190 | nullptr);
191 | if (UTF8StringLength > 0)
192 | {
193 | UTF8String.resize(UTF8StringLength);
194 | WideCharToMultiByte(
195 | CP_UTF8,
196 | 0,
197 | UTF16String.data(),
198 | (int)UTF16String.size(),
199 | &UTF8String[0],
200 | UTF8StringLength,
201 | nullptr,
202 | nullptr);
203 | }
204 |
205 | return UTF8String;
206 | }
207 |
208 | /**
209 | * Write formatted data to a string.
210 | *
211 | * @param Format Format-control string.
212 | * @param ... Optional arguments to be formatted.
213 | * @return A formatted string if successful, "N/A" otherwise.
214 | */
215 | std::wstring M2FormatString(
216 | _In_z_ _Printf_format_string_ wchar_t const* const Format,
217 | ...)
218 | {
219 | // Check the argument list.
220 | if (nullptr != Format)
221 | {
222 | va_list ArgList = nullptr;
223 | va_start(ArgList, Format);
224 |
225 | // Get the length of the format result.
226 | size_t nLength = static_cast(_vscwprintf(Format, ArgList)) + 1;
227 |
228 | // Allocate for the format result.
229 | std::wstring Buffer(nLength + 1, L'\0');
230 |
231 | // Format the string.
232 | int nWritten = _vsnwprintf_s(
233 | &Buffer[0],
234 | Buffer.size(),
235 | nLength,
236 | Format,
237 | ArgList);
238 |
239 | va_end(ArgList);
240 |
241 | if (nWritten > 0)
242 | {
243 | // If succeed, resize to fit and return result.
244 | Buffer.resize(nWritten);
245 | return Buffer;
246 | }
247 | }
248 |
249 | // If failed, return "N/A".
250 | return L"N/A";
251 | }
252 |
253 | /**
254 | * Parses a command line string and returns an array of the command line
255 | * arguments, along with a count of such arguments, in a way that is similar to
256 | * the standard C run-time.
257 | *
258 | * @param CommandLine A string that contains the full command line. If this
259 | * parameter is an empty string the function returns an
260 | * array with only one empty string.
261 | * @return An array of the command line arguments, along with a count of such
262 | * arguments.
263 | */
264 | std::vector M2SpiltCommandLine(
265 | const std::wstring& CommandLine)
266 | {
267 | // Initialize the SplitArguments.
268 | std::vector SplitArguments;
269 |
270 | wchar_t c = L'\0';
271 | int copy_character; /* 1 = copy char to *args */
272 | unsigned numslash; /* num of backslashes seen */
273 |
274 | std::wstring Buffer;
275 | Buffer.reserve(CommandLine.size());
276 |
277 | /* first scan the program name, copy it, and count the bytes */
278 | wchar_t* p = const_cast(CommandLine.c_str());
279 |
280 | // A quoted program name is handled here. The handling is much simpler than
281 | // for other arguments. Basically, whatever lies between the leading
282 | // double-quote and next one, or a terminal null character is simply
283 | // accepted. Fancier handling is not required because the program name must
284 | // be a legal NTFS/HPFS file name. Note that the double-quote characters are
285 | // not copied, nor do they contribute to character_count.
286 | bool InQuotes = false;
287 | do
288 | {
289 | if (*p == '"')
290 | {
291 | InQuotes = !InQuotes;
292 | c = *p++;
293 | continue;
294 | }
295 |
296 | // Copy character into argument:
297 | Buffer.push_back(*p);
298 |
299 | c = *p++;
300 | } while (c != '\0' && (InQuotes || (c != ' ' && c != '\t')));
301 |
302 | if (c == '\0')
303 | {
304 | p--;
305 | }
306 | else
307 | {
308 | Buffer.resize(Buffer.size() - 1);
309 | }
310 |
311 | // Save te argument.
312 | SplitArguments.push_back(Buffer);
313 |
314 | InQuotes = false;
315 |
316 | // Loop on each argument
317 | for (;;)
318 | {
319 | if (*p)
320 | {
321 | while (*p == ' ' || *p == '\t')
322 | ++p;
323 | }
324 |
325 | // End of arguments
326 | if (*p == '\0')
327 | break;
328 |
329 | // Initialize the argument buffer.
330 | Buffer.clear();
331 |
332 | // Loop through scanning one argument:
333 | for (;;)
334 | {
335 | copy_character = 1;
336 |
337 | // Rules: 2N backslashes + " ==> N backslashes and begin/end quote
338 | // 2N + 1 backslashes + " ==> N backslashes + literal " N
339 | // backslashes ==> N backslashes
340 | numslash = 0;
341 |
342 | while (*p == '\\')
343 | {
344 | // Count number of backslashes for use below
345 | ++p;
346 | ++numslash;
347 | }
348 |
349 | if (*p == '"')
350 | {
351 | // if 2N backslashes before, start/end quote, otherwise copy
352 | // literally:
353 | if (numslash % 2 == 0)
354 | {
355 | if (InQuotes && p[1] == '"')
356 | {
357 | p++; // Double quote inside quoted string
358 | }
359 | else
360 | {
361 | // Skip first quote char and copy second:
362 | copy_character = 0; // Don't copy quote
363 | InQuotes = !InQuotes;
364 | }
365 | }
366 |
367 | numslash /= 2;
368 | }
369 |
370 | // Copy slashes:
371 | while (numslash--)
372 | {
373 | Buffer.push_back(L'\\');
374 | }
375 |
376 | // If at end of arg, break loop:
377 | if (*p == '\0' || (!InQuotes && (*p == ' ' || *p == '\t')))
378 | break;
379 |
380 | // Copy character into argument:
381 | if (copy_character)
382 | {
383 | Buffer.push_back(*p);
384 | }
385 |
386 | ++p;
387 | }
388 |
389 | // Save te argument.
390 | SplitArguments.push_back(Buffer);
391 | }
392 |
393 | return SplitArguments;
394 | }
395 |
396 | /**
397 | * Parses a command line string and get more friendly result.
398 | *
399 | * @param CommandLine A string that contains the full command line. If this
400 | * parameter is an empty string the function returns an
401 | * array with only one empty string.
402 | * @param OptionPrefixes One or more of the prefixes of option we want to use.
403 | * @param OptionParameterSeparators One or more of the separators of option we
404 | * want to use.
405 | * @param ApplicationName The application name.
406 | * @param OptionsAndParameters The options and parameters.
407 | * @param UnresolvedCommandLine The unresolved command line.
408 | */
409 | void M2SpiltCommandLineEx(
410 | const std::wstring & CommandLine,
411 | const std::vector & OptionPrefixes,
412 | const std::vector & OptionParameterSeparators,
413 | std::wstring & ApplicationName,
414 | std::map & OptionsAndParameters,
415 | std::wstring & UnresolvedCommandLine)
416 | {
417 | ApplicationName.clear();
418 | OptionsAndParameters.clear();
419 | UnresolvedCommandLine.clear();
420 |
421 | size_t arg_size = 0;
422 | for (auto& SplitArgument : M2SpiltCommandLine(CommandLine))
423 | {
424 | // We need to process the application name at the beginning.
425 | if (ApplicationName.empty())
426 | {
427 | // For getting the unresolved command line, we need to cumulate
428 | // length which including spaces.
429 | arg_size += SplitArgument.size() + 1;
430 |
431 | // Save
432 | ApplicationName = SplitArgument;
433 | }
434 | else
435 | {
436 | bool IsOption = false;
437 | size_t OptionPrefixLength = 0;
438 |
439 | for (auto& OptionPrefix : OptionPrefixes)
440 | {
441 | if (0 == _wcsnicmp(
442 | SplitArgument.c_str(),
443 | OptionPrefix.c_str(),
444 | OptionPrefix.size()))
445 | {
446 | IsOption = true;
447 | OptionPrefixLength = OptionPrefix.size();
448 | }
449 | }
450 |
451 | if (IsOption)
452 | {
453 | // For getting the unresolved command line, we need to cumulate
454 | // length which including spaces.
455 | arg_size += SplitArgument.size() + 1;
456 |
457 | // Get the option name and parameter.
458 |
459 | wchar_t* OptionStart = &SplitArgument[0] + OptionPrefixLength;
460 | wchar_t* ParameterStart = nullptr;
461 |
462 | for (auto& OptionParameterSeparator
463 | : OptionParameterSeparators)
464 | {
465 | wchar_t* Result = wcsstr(
466 | OptionStart,
467 | OptionParameterSeparator.c_str());
468 | if (nullptr == Result)
469 | {
470 | continue;
471 | }
472 |
473 | Result[0] = L'\0';
474 | ParameterStart = Result + OptionParameterSeparator.size();
475 |
476 | break;
477 | }
478 |
479 | // Save
480 | OptionsAndParameters[(OptionStart ? OptionStart : L"")] =
481 | (ParameterStart ? ParameterStart : L"");
482 | }
483 | else
484 | {
485 | // Get the approximate location of the unresolved command line.
486 | // We use "(arg_size - 1)" to ensure that the program path
487 | // without quotes can also correctly parse.
488 | wchar_t* search_start =
489 | const_cast(CommandLine.c_str()) + (arg_size - 1);
490 |
491 | // Get the unresolved command line. Search for the beginning of
492 | // the first parameter delimiter called space and exclude the
493 | // first space by adding 1 to the result.
494 | wchar_t* command = wcsstr(search_start, L" ") + 1;
495 |
496 | // Omit the space. (Thanks to wzzw.)
497 | while (command && *command == L' ')
498 | {
499 | ++command;
500 | }
501 |
502 | // Save
503 | if (command)
504 | {
505 | UnresolvedCommandLine = command;
506 | }
507 |
508 | break;
509 | }
510 | }
511 | }
512 | }
513 |
514 | #ifdef CPPWINRT_VERSION
515 |
516 | /**
517 | * Finds a sub string from a source string.
518 | *
519 | * @param SourceString The source string.
520 | * @param SubString The sub string.
521 | * @param IgnoreCase Determines whether to ignore case.
522 | * @return Returns true if successful, or false otherwise.
523 | */
524 | bool M2FindSubString(
525 | winrt::hstring const& SourceString,
526 | winrt::hstring const& SubString,
527 | bool IgnoreCase)
528 | {
529 | return (::FindNLSStringEx(
530 | nullptr,
531 | (IgnoreCase ? NORM_IGNORECASE : 0) | FIND_FROMSTART,
532 | SourceString.c_str(),
533 | SourceString.size(),
534 | SubString.c_str(),
535 | SubString.size(),
536 | nullptr,
537 | nullptr,
538 | nullptr,
539 | 0) >= 0);
540 | }
541 |
542 | /**
543 | * Converts a numeric value into a string that represents the number expressed
544 | * as a size value in byte, bytes, kibibytes, mebibytes, gibibytes, tebibytes,
545 | * pebibytes or exbibytes, depending on the size.
546 | *
547 | * @param ByteSize The numeric byte size value to be converted.
548 | * @return Returns a winrt::hstring object which represents the converted
549 | * string.
550 | */
551 | winrt::hstring M2ConvertByteSizeToString(
552 | uint64_t ByteSize)
553 | {
554 | const wchar_t* Systems[] =
555 | {
556 | L"Byte",
557 | L"Bytes",
558 | L"KiB",
559 | L"MiB",
560 | L"GiB",
561 | L"TiB",
562 | L"PiB",
563 | L"EiB"
564 | };
565 |
566 | size_t nSystem = 0;
567 | double result = static_cast(ByteSize);
568 |
569 | if (ByteSize > 1)
570 | {
571 | for (
572 | nSystem = 1;
573 | nSystem < sizeof(Systems) / sizeof(*Systems);
574 | ++nSystem)
575 | {
576 | if (1024.0 > result)
577 | break;
578 |
579 | result /= 1024.0;
580 | }
581 |
582 | result = static_cast(result * 100) / 100.0;
583 | }
584 |
585 | return winrt::to_hstring(result) + L" " + Systems[nSystem];
586 | }
587 |
588 | #endif
589 |
590 | #ifdef __cplusplus_winrt
591 |
592 | /**
593 | * Converts from the C++/CX string to the UTF-16 string.
594 | *
595 | * @param PlatformString The C++/CX string you want to convert.
596 | * @return The return value is the UTF-16 string.
597 | */
598 | std::wstring M2MakeUTF16String(Platform::String^ PlatformString)
599 | {
600 | return std::wstring(PlatformString->Data(), PlatformString->Length());
601 | }
602 |
603 | /**
604 | * Converts from the C++/CX string to the UTF-8 string.
605 | *
606 | * @param PlatformString The C++/CX string you want to convert.
607 | * @return The return value is the UTF-8 string.
608 | */
609 | std::string M2MakeUTF8String(Platform::String^ PlatformString)
610 | {
611 | std::string UTF8String;
612 |
613 | int UTF8StringLength = WideCharToMultiByte(
614 | CP_UTF8,
615 | 0,
616 | PlatformString->Data(),
617 | static_cast(PlatformString->Length()),
618 | nullptr,
619 | 0,
620 | nullptr,
621 | nullptr);
622 | if (UTF8StringLength > 0)
623 | {
624 | UTF8String.resize(UTF8StringLength);
625 | WideCharToMultiByte(
626 | CP_UTF8,
627 | 0,
628 | PlatformString->Data(),
629 | static_cast(PlatformString->Length()),
630 | &UTF8String[0],
631 | UTF8StringLength,
632 | nullptr,
633 | nullptr);
634 | }
635 |
636 | return UTF8String;
637 | }
638 |
639 | /**
640 | * Converts from the UTF-8 string to the C++/CX string.
641 | *
642 | * @param UTF16String The UTF-16 string you want to convert.
643 | * @return The return value is the C++/CX string.
644 | */
645 | Platform::String^ M2MakeCXString(const std::wstring& UTF16String)
646 | {
647 | return ref new Platform::String(
648 | UTF16String.c_str(), static_cast(UTF16String.size()));
649 | }
650 |
651 | /**
652 | * Finds a sub string from a source string.
653 | *
654 | * @param SourceString The source string.
655 | * @param SubString The sub string.
656 | * @param IgnoreCase Determines whether to ignore case.
657 | * @return Returns true if successful, or false otherwise.
658 | */
659 | bool M2FindSubString(
660 | Platform::String^ SourceString,
661 | Platform::String^ SubString,
662 | bool IgnoreCase)
663 | {
664 | return (::FindNLSStringEx(
665 | nullptr,
666 | (IgnoreCase ? NORM_IGNORECASE : 0) | FIND_FROMSTART,
667 | SourceString->Data(),
668 | SourceString->Length(),
669 | SubString->Data(),
670 | SubString->Length(),
671 | nullptr,
672 | nullptr,
673 | nullptr,
674 | 0) >= 0);
675 | }
676 |
677 | /**
678 | * Converts a numeric value into a string that represents the number expressed
679 | * as a size value in byte, bytes, kibibytes, mebibytes, gibibytes, tebibytes,
680 | * pebibytes or exbibytes, depending on the size.
681 | *
682 | * @param ByteSize The numeric byte size value to be converted.
683 | * @return Returns a Platform::String object which represents the converted
684 | * string.
685 | */
686 | Platform::String ^ M2ConvertByteSizeToString(uint64 ByteSize)
687 | {
688 | double result = static_cast(ByteSize);
689 |
690 | if (0.0 == result)
691 | {
692 | return L"0 Byte";
693 | }
694 |
695 | const wchar_t* Systems[] =
696 | {
697 | L"Bytes",
698 | L"KiB",
699 | L"MiB",
700 | L"GiB",
701 | L"TiB",
702 | L"PiB",
703 | L"EiB"
704 | };
705 |
706 | size_t nSystem = 0;
707 | for (; nSystem < sizeof(Systems) / sizeof(*Systems); ++nSystem)
708 | {
709 | if (1024.0 > result)
710 | break;
711 |
712 | result /= 1024.0;
713 | }
714 |
715 | Platform::String^ ByteSizeString =
716 | (static_cast(result * 100) / 100.0).ToString();
717 |
718 | return ByteSizeString + Platform::StringReference(Systems[nSystem]);
719 | }
720 |
721 | #endif
722 |
723 | #pragma endregion
724 |
725 | #pragma region Performance
726 |
727 | /**
728 | * Retrieves the number of milliseconds that have elapsed since the system was
729 | * started.
730 | *
731 | * @return The number of milliseconds.
732 | */
733 | ULONGLONG M2GetTickCount()
734 | {
735 | LARGE_INTEGER Frequency = { 0 }, PerformanceCount = { 0 };
736 |
737 | if (QueryPerformanceFrequency(&Frequency))
738 | {
739 | if (QueryPerformanceCounter(&PerformanceCount))
740 | {
741 | return (PerformanceCount.QuadPart * 1000 / Frequency.QuadPart);
742 | }
743 | }
744 |
745 | return GetTickCount64();
746 | }
747 |
748 | #pragma endregion
749 |
750 | #pragma region Thread
751 |
752 | /**
753 | * Creates a thread to execute within the virtual address space of the calling
754 | * process.
755 | *
756 | * @param lpThreadAttributes A pointer to a SECURITY_ATTRIBUTES structure that
757 | * determines whether the returned handle can be
758 | * inherited by child processes.
759 | * @param dwStackSize The initial size of the stack, in bytes.
760 | * @param lpStartAddress A pointer to the application-defined function to be
761 | * executed by the thread.
762 | * @param lpParameter A pointer to a variable to be passed to the thread.
763 | * @param dwCreationFlags The flags that control the creation of the thread.
764 | * @param lpThreadId A pointer to a variable that receives the thread
765 | * identifier.
766 | * @return HRESULT. If the function succeeds, the return value is S_OK.
767 | * @remark For more information, see CreateThread.
768 | */
769 | HRESULT M2CreateThread(
770 | _Out_ PHANDLE lpThreadHandle,
771 | _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
772 | _In_ SIZE_T dwStackSize,
773 | _In_ LPTHREAD_START_ROUTINE lpStartAddress,
774 | _In_opt_ __drv_aliasesMem LPVOID lpParameter,
775 | _In_ DWORD dwCreationFlags,
776 | _Out_opt_ LPDWORD lpThreadId)
777 | {
778 | // sanity check for lpThreadId
779 | assert(sizeof(DWORD) == sizeof(unsigned));
780 |
781 | typedef unsigned(__stdcall * routine_type)(void*);
782 |
783 | // _beginthreadex calls CreateThread which will set the last error
784 | // value before it returns.
785 | *lpThreadHandle = reinterpret_cast(_beginthreadex(
786 | lpThreadAttributes,
787 | static_cast(dwStackSize),
788 | reinterpret_cast(lpStartAddress),
789 | lpParameter,
790 | dwCreationFlags,
791 | reinterpret_cast(lpThreadId)));
792 |
793 | return (*lpThreadHandle) ? S_OK : M2GetLastHRESULTErrorKnownFailedCall();
794 | }
795 |
796 | /**
797 | * Retrieves the number of logical processors in the current group.
798 | *
799 | * @return The number of logical processors in the current group.
800 | */
801 | DWORD M2GetNumberOfHardwareThreads()
802 | {
803 | SYSTEM_INFO SystemInfo = { 0 };
804 | GetNativeSystemInfo(&SystemInfo);
805 | return SystemInfo.dwNumberOfProcessors;
806 | }
807 |
808 | #pragma endregion
809 |
810 | #pragma region Memory
811 |
812 | /**
813 | * Allocates a block of memory from a heap. The allocated memory is not
814 | * movable.
815 | *
816 | * @param lpNewMem A pointer to the allocated memory block.
817 | * @param hHeap A handle to the heap from which the memory will be allocated.
818 | * @param dwFlags The heap allocation options.
819 | * @param dwBytes The number of bytes to be allocated.
820 | * @return HRESULT. If the function succeeds, the return value is S_OK.
821 | * @remark For more information, see HeapAlloc.
822 | */
823 | HRESULT M2HeapAlloc(
824 | _Out_ PVOID* lpNewMem,
825 | _In_ HANDLE hHeap,
826 | _In_ DWORD dwFlags,
827 | _In_ SIZE_T dwBytes)
828 | {
829 | *lpNewMem = HeapAlloc(hHeap, dwFlags, dwBytes);
830 | return *lpNewMem ? S_OK : __HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
831 | }
832 |
833 | /**
834 | * Reallocates a block of memory from a heap. This function enables you to
835 | * resize a memory block and change other memory block properties. The
836 | * allocated memory is not movable.
837 | *
838 | * @param lpNewMem A pointer to the allocated memory block.
839 | * @param hHeap A handle to the heap from which the memory is to be
840 | * reallocated.
841 | * @param dwFlags The heap reallocation options.
842 | * @param lpMem A pointer to the block of memory that the function reallocates.
843 | * @param dwBytes The new size of the memory block, in bytes.
844 | * @return HRESULT. If the function succeeds, the return value is S_OK. If the
845 | * function fails, the original memory is not freed, and the original
846 | * handle and pointer are still valid.
847 | * @remark For more information, see HeapReAlloc.
848 | */
849 | HRESULT M2HeapReAlloc(
850 | _Out_ PVOID* lpNewMem,
851 | _Inout_ HANDLE hHeap,
852 | _In_ DWORD dwFlags,
853 | _In_ LPVOID lpMem,
854 | _In_ SIZE_T dwBytes)
855 | {
856 | *lpNewMem = HeapReAlloc(hHeap, dwFlags, lpMem, dwBytes);
857 | return *lpNewMem ? S_OK : __HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
858 | }
859 |
860 | /**
861 | * Frees a memory block allocated from a heap by the M2HeapAlloc and
862 | * M2HeapReAlloc function.
863 | *
864 | * @param hHeap A handle to the heap whose memory block is to be freed.
865 | * @param dwFlags The heap free options.
866 | * @param lpMem A pointer to the memory block to be freed.
867 | * @return HRESULT. If the function succeeds, the return value is S_OK.
868 | * @remark For more information, see HeapFree.
869 | */
870 | HRESULT M2HeapFree(
871 | _Inout_ HANDLE hHeap,
872 | _In_ DWORD dwFlags,
873 | _In_ LPVOID lpMem)
874 | {
875 | if (!HeapFree(hHeap, dwFlags, lpMem))
876 | return M2GetLastHRESULTErrorKnownFailedCall();
877 |
878 | return S_OK;
879 | }
880 |
881 | /**
882 | * Allocates a block of memory from the default heap of the calling process.
883 | * The allocated memory will be initialized to zero. The allocated memory is
884 | * not movable.
885 | *
886 | * @param AllocatedMemoryBlock A pointer to the allocated memory block.
887 | * @param MemoryBlockSize The number of bytes to be allocated.
888 | * @return HRESULT. If the function succeeds, the return value is S_OK.
889 | */
890 | HRESULT M2AllocMemory(
891 | _Out_ PVOID* AllocatedMemoryBlock,
892 | _In_ SIZE_T MemoryBlockSize)
893 | {
894 | return M2HeapAlloc(
895 | AllocatedMemoryBlock,
896 | GetProcessHeap(),
897 | HEAP_ZERO_MEMORY,
898 | MemoryBlockSize);
899 | }
900 |
901 | /**
902 | * Reallocates a block of memory from the default heap of the calling process.
903 | * If the reallocation request is for a larger size, the additional region of
904 | * memory beyond the original size be initialized to zero. This function
905 | * enables you to resize a memory block and change other memory block
906 | * properties. The allocated memory is not movable.
907 | *
908 | * @param NewAllocatedMemoryBlock A pointer to the allocated memory block.
909 | * @param OldAllocatedMemoryBlock A pointer to the block of memory that the
910 | * function reallocates. This pointer is
911 | * returned by an earlier call to the
912 | * M2AllocMemory or M2ReAllocMemory function.
913 | * @param NewMemoryBlockSize The new size of the memory block, in bytes. A
914 | * memory block's size can be increased or decreased
915 | * by using this function.
916 | * @return HRESULT. If the function succeeds, the return value is S_OK. If the
917 | * function fails, the original memory is not freed, and the original
918 | * handle and pointer are still valid.
919 | */
920 | HRESULT M2ReAllocMemory(
921 | _Out_ PVOID* NewAllocatedMemoryBlock,
922 | _In_ PVOID OldAllocatedMemoryBlock,
923 | _In_ SIZE_T NewMemoryBlockSize)
924 | {
925 | return M2HeapReAlloc(
926 | NewAllocatedMemoryBlock,
927 | GetProcessHeap(),
928 | HEAP_ZERO_MEMORY,
929 | OldAllocatedMemoryBlock,
930 | NewMemoryBlockSize);
931 | }
932 |
933 | /**
934 | * Frees a memory block allocated from a heap by the M2AllocMemory and
935 | * M2ReAllocMemory function.
936 | *
937 | * @param AllocatedMemoryBlock A pointer to the memory block to be freed. This
938 | * pointer is returned by the M2AllocMemory or M2ReAllocMemory function. If
939 | * this pointer is nullptr, the behavior is undefined.
940 | * @return HRESULT. If the function succeeds, the return value is S_OK.
941 | */
942 | HRESULT M2FreeMemory(
943 | _In_ PVOID AllocatedMemoryBlock)
944 | {
945 | return M2HeapFree(GetProcessHeap(), 0, AllocatedMemoryBlock);
946 | }
947 |
948 | #pragma endregion
949 |
950 | #pragma region AccessToken
951 |
952 | /**
953 | * Enables or disables privileges in the specified access token. Enabling or
954 | * disabling privileges in an access token requires TOKEN_ADJUST_PRIVILEGES
955 | * access.
956 | *
957 | * @param TokenHandle A handle to the access token that contains the privileges
958 | * to be modified. The handle must have
959 | * TOKEN_ADJUST_PRIVILEGES access to the token. If the
960 | * PreviousState parameter is not NULL, the handle must also
961 | * have TOKEN_QUERY access.
962 | * @param DisableAllPrivileges Specifies whether the function disables all of
963 | * the token's privileges. If this value is TRUE,
964 | * the function disables all privileges and ignores
965 | * the NewState parameter. If it is FALSE, the
966 | * function modifies privileges based on the
967 | * information pointed to by the NewState
968 | * parameter.
969 | * @param NewState A pointer to a TOKEN_PRIVILEGES structure that specifies an
970 | * array of privileges and their attributes. If
971 | * DisableAllPrivileges is TRUE, the function ignores this
972 | * parameter.
973 | * @param BufferLength Specifies the size, in bytes, of the buffer pointed to
974 | * by the PreviousState parameter. This parameter can be
975 | * zero if the PreviousState parameter is NULL.
976 | * @param PreviousState A pointer to a buffer that the function fills with a
977 | * TOKEN_PRIVILEGES structure that contains the previous
978 | * state of any privileges that the function modifies.
979 | * This parameter can be NULL.
980 | * @param ReturnLength A pointer to a variable that receives the required size,
981 | * in bytes, of the buffer pointed to by the PreviousState
982 | * parameter. This parameter can be NULL if PreviousState
983 | * is NULL.
984 | * @return HRESULT. If the function succeeds, the return value is S_OK.
985 | * @remark For more information, see AdjustTokenPrivileges.
986 | */
987 | HRESULT M2AdjustTokenPrivileges(
988 | _In_ HANDLE TokenHandle,
989 | _In_ BOOL DisableAllPrivileges,
990 | _In_opt_ PTOKEN_PRIVILEGES NewState,
991 | _In_ DWORD BufferLength,
992 | _Out_opt_ PTOKEN_PRIVILEGES PreviousState,
993 | _Out_opt_ PDWORD ReturnLength)
994 | {
995 | BOOL Result = AdjustTokenPrivileges(
996 | TokenHandle,
997 | DisableAllPrivileges,
998 | NewState,
999 | BufferLength,
1000 | PreviousState,
1001 | ReturnLength);
1002 |
1003 | DWORD LastError = GetLastError();
1004 |
1005 | if (!Result && LastError == ERROR_SUCCESS)
1006 | LastError = ERROR_FUNCTION_FAILED;
1007 |
1008 | return HRESULT_FROM_WIN32(LastError);
1009 | }
1010 |
1011 | /**
1012 | * Retrieves a specified type of information about an access token. The calling
1013 | * process must have appropriate access rights to obtain the information.
1014 | *
1015 | * @param TokenHandle A handle to an access token from which information is
1016 | * retrieved.
1017 | * @param TokenInformationClass Specifies a value from the
1018 | * TOKEN_INFORMATION_CLASS enumerated type to
1019 | * identify the type of information the function
1020 | * retrieves.
1021 | * @param TokenInformation A pointer to a buffer the function fills with the
1022 | * requested information.
1023 | * @param TokenInformationLength Specifies the size, in bytes, of the buffer
1024 | * pointed to by the TokenInformation parameter.
1025 | * @param ReturnLength A pointer to a variable that receives the number of
1026 | * bytes needed for the buffer pointed to by the
1027 | * TokenInformation parameter.
1028 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1029 | * @remark For more information, see GetTokenInformation.
1030 | */
1031 | HRESULT M2GetTokenInformation(
1032 | _In_ HANDLE TokenHandle,
1033 | _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
1034 | _Out_opt_ LPVOID TokenInformation,
1035 | _In_ DWORD TokenInformationLength,
1036 | _Out_ PDWORD ReturnLength)
1037 | {
1038 | if (GetTokenInformation(
1039 | TokenHandle,
1040 | TokenInformationClass,
1041 | TokenInformation,
1042 | TokenInformationLength,
1043 | ReturnLength))
1044 | {
1045 | return S_OK;
1046 | }
1047 |
1048 | return M2GetLastHRESULTErrorKnownFailedCall();
1049 | }
1050 |
1051 | /**
1052 | * Opens the access token associated with a process.
1053 | *
1054 | * @param TokenHandle A pointer to a handle that identifies the newly opened
1055 | * access token when the function returns.
1056 | * @param TokenSource The source information of access token associated with a
1057 | * process.
1058 | * @param DesiredAccess Specifies an access mask that specifies the requested
1059 | * types of access to the access token. These requested
1060 | * access types are compared with the discretionary access
1061 | * control list (DACL) of the token to determine which
1062 | * accesses are granted or denied.
1063 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1064 | */
1065 | HRESULT M2OpenProcessToken(
1066 | _Out_ PHANDLE TokenHandle,
1067 | _In_ PM2_PROCESS_ACCESS_TOKEN_SOURCE TokenSource,
1068 | _In_ DWORD DesiredAccess)
1069 | {
1070 | *TokenHandle = INVALID_HANDLE_VALUE;
1071 |
1072 | if (!TokenSource)
1073 | return __HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
1074 |
1075 | HANDLE ProcessHandle = INVALID_HANDLE_VALUE;
1076 |
1077 | switch (TokenSource->Type)
1078 | {
1079 | case M2_PROCESS_TOKEN_SOURCE_TYPE::Current:
1080 | ProcessHandle = GetCurrentProcess();
1081 | break;
1082 | case M2_PROCESS_TOKEN_SOURCE_TYPE::Handle:
1083 | ProcessHandle = TokenSource->ProcessHandle;
1084 | break;
1085 | case M2_PROCESS_TOKEN_SOURCE_TYPE::ProcessId:
1086 | ProcessHandle = OpenProcess(
1087 | MAXIMUM_ALLOWED, FALSE, TokenSource->ProcessId);
1088 | if (!ProcessHandle)
1089 | return M2GetLastHRESULTErrorKnownFailedCall();
1090 | break;
1091 | default:
1092 | return __HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
1093 | }
1094 |
1095 | if (OpenProcessToken(ProcessHandle, DesiredAccess, TokenHandle))
1096 | return S_OK;
1097 |
1098 | return M2GetLastHRESULTErrorKnownFailedCall();
1099 | }
1100 |
1101 | /**
1102 | * Retrieves a specified type of information about an access token. The calling
1103 | * process must have appropriate access rights to obtain the information.
1104 | *
1105 | * @param OutputInformation A pointer to a buffer the function fills with the
1106 | * requested information. When you have finished using
1107 | * the information, free it by calling the
1108 | * M2FreeMemory function. You should also set the
1109 | * pointer to NULL.
1110 | * @param TokenHandle A handle to an access token from which information is
1111 | * retrieved.
1112 | * @param TokenInformationClass Specifies a value from the
1113 | * TOKEN_INFORMATION_CLASS enumerated type to
1114 | * identify the type of information the function
1115 | * retrieves.
1116 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1117 | * @remark For more information, see GetTokenInformation.
1118 | */
1119 | HRESULT M2GetTokenInformation(
1120 | _Out_ PVOID* OutputInformation,
1121 | _In_ HANDLE TokenHandle,
1122 | _In_ TOKEN_INFORMATION_CLASS TokenInformationClass)
1123 | {
1124 | *OutputInformation = nullptr;
1125 |
1126 | DWORD Length = 0;
1127 |
1128 | HRESULT hr = M2GetTokenInformation(
1129 | TokenHandle,
1130 | TokenInformationClass,
1131 | nullptr,
1132 | 0,
1133 | &Length);
1134 | if (hr == __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
1135 | {
1136 | hr = M2AllocMemory(OutputInformation, Length);
1137 | if (SUCCEEDED(hr))
1138 | {
1139 | hr = M2GetTokenInformation(
1140 | TokenHandle,
1141 | TokenInformationClass,
1142 | *OutputInformation,
1143 | Length,
1144 | &Length);
1145 | if (FAILED(hr))
1146 | {
1147 | hr = M2FreeMemory(*OutputInformation);
1148 | }
1149 | }
1150 | }
1151 |
1152 | return hr;
1153 | }
1154 |
1155 | #pragma endregion
1156 |
1157 | #pragma region COM
1158 |
1159 | /**
1160 | * Creates a single uninitialized object of the class associated with a
1161 | * specified CLSID.
1162 | *
1163 | * @param lpszCLSID The string representation of the CLSID.
1164 | * @param pUnkOuter If NULL, indicates that the object is not being created as
1165 | * part of an aggregate. If non-NULL, pointer to the aggregate
1166 | * object's IUnknown interface (the controlling IUnknown).
1167 | * @param dwClsContext Context in which the code that manages the newly created
1168 | * object will run. The values are taken from the
1169 | * enumeration CLSCTX.
1170 | * @param lpszIID A pointer to the string representation of the IID.
1171 | * @param ppv Address of pointer variable that receives the interface pointer
1172 | * requested in riid. Upon successful return, *ppv contains the
1173 | * requested interface pointer. Upon failure, *ppv contains NULL.
1174 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1175 | * @remark For more information, see CoCreateInstance.
1176 | */
1177 | HRESULT M2CoCreateInstance(
1178 | _In_ LPCWSTR lpszCLSID,
1179 | _In_opt_ LPUNKNOWN pUnkOuter,
1180 | _In_ DWORD dwClsContext,
1181 | _In_ LPCWSTR lpszIID,
1182 | _Out_ LPVOID* ppv)
1183 | {
1184 | CLSID clsid;
1185 | IID iid;
1186 |
1187 | HRESULT hr = CLSIDFromString(lpszCLSID, &clsid);
1188 | if (SUCCEEDED(hr))
1189 | {
1190 | hr = IIDFromString(lpszIID, &iid);
1191 | if (SUCCEEDED(hr))
1192 | {
1193 | hr = CoCreateInstance(clsid, pUnkOuter, dwClsContext, iid, ppv);
1194 | }
1195 | }
1196 |
1197 | return hr;
1198 | }
1199 |
1200 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
1201 |
1202 | /**
1203 | * Determines whether the interface id have the correct interface name.
1204 | *
1205 | * @param InterfaceID A pointer to the string representation of the IID.
1206 | * @param InterfaceName A pointer to the interface name string.
1207 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1208 | */
1209 | HRESULT M2CoCheckInterfaceName(
1210 | _In_ LPCWSTR InterfaceID,
1211 | _In_ LPCWSTR InterfaceName)
1212 | {
1213 | HKEY hKey = nullptr;
1214 | HRESULT hr = M2RegCreateKey(
1215 | HKEY_CLASSES_ROOT,
1216 | (std::wstring(L"Interface\\") + InterfaceID).c_str(),
1217 | 0,
1218 | nullptr,
1219 | 0,
1220 | KEY_READ,
1221 | nullptr,
1222 | &hKey,
1223 | nullptr);
1224 | if (SUCCEEDED(hr))
1225 | {
1226 | wchar_t* InterfaceTypeName = nullptr;
1227 | hr = M2RegQueryStringValue(&InterfaceTypeName, hKey, nullptr);
1228 | if (SUCCEEDED(hr))
1229 | {
1230 | if (0 != _wcsicmp(InterfaceTypeName, InterfaceName))
1231 | {
1232 | hr = E_NOINTERFACE;
1233 | }
1234 | }
1235 |
1236 | RegCloseKey(hKey);
1237 | }
1238 |
1239 | return hr;
1240 | }
1241 |
1242 | #endif
1243 |
1244 | #ifdef CPPWINRT_VERSION
1245 |
1246 | /**
1247 | * Creates a GUID, a unique 128-bit integer used for CLSIDs and interface
1248 | * identifiers.
1249 | *
1250 | * @return The function will return GUID struct.
1251 | */
1252 | GUID M2CreateGuid()
1253 | {
1254 | GUID guid = { 0 };
1255 | winrt::check_hresult(CoCreateGuid(&guid));
1256 | return guid;
1257 | }
1258 |
1259 | #endif
1260 |
1261 | #ifdef __cplusplus_winrt
1262 |
1263 | /**
1264 | * Retrieves the raw pointer from the provided IBuffer object.
1265 | *
1266 | * @param Buffer The IBuffer object you want to retrieve the raw pointer.
1267 | * @return If the function succeeds, the return value is the raw pointer from
1268 | * the provided IBuffer object. If the function fails, the return value
1269 | * is nullptr.
1270 | * @remark The lifetime of the returned buffer is controlled by the lifetime of
1271 | * the buffer object that's passed to this method. When the buffer has
1272 | * been released, the pointer becomes invalid and must not be used.
1273 | */
1274 | byte* M2GetPointer(Windows::Storage::Streams::IBuffer^ Buffer)
1275 | {
1276 | byte* pBuffer = nullptr;
1277 | Windows::Storage::Streams::IBufferByteAccess* pBufferByteAccess = nullptr;
1278 | IInspectable* pBufferABIObject = M2GetInspectable(Buffer);
1279 | if (SUCCEEDED(pBufferABIObject->QueryInterface(&pBufferByteAccess)))
1280 | {
1281 | pBufferByteAccess->Buffer(&pBuffer);
1282 | pBufferByteAccess->Release();
1283 | }
1284 |
1285 | return pBuffer;
1286 | }
1287 |
1288 | class BufferReference : public RuntimeClass<
1289 | RuntimeClassFlags,
1290 | ABI::Windows::Storage::Streams::IBuffer,
1291 | Windows::Storage::Streams::IBufferByteAccess>
1292 | {
1293 | private:
1294 | UINT32 m_Capacity;
1295 | UINT32 m_Length;
1296 | byte* m_Pointer;
1297 |
1298 | public:
1299 | virtual ~BufferReference()
1300 | {
1301 | }
1302 |
1303 | STDMETHODIMP RuntimeClassInitialize(
1304 | byte* Pointer, UINT32 Capacity)
1305 | {
1306 | m_Capacity = Capacity;
1307 | m_Length = Capacity;
1308 | m_Pointer = Pointer;
1309 | return S_OK;
1310 | }
1311 |
1312 | // IBufferByteAccess::Buffer
1313 | STDMETHODIMP Buffer(byte** value)
1314 | {
1315 | *value = m_Pointer;
1316 | return S_OK;
1317 | }
1318 |
1319 | // IBuffer::get_Capacity
1320 | STDMETHODIMP get_Capacity(UINT32* value)
1321 | {
1322 | *value = m_Capacity;
1323 | return S_OK;
1324 | }
1325 |
1326 | // IBuffer::get_Length
1327 | STDMETHODIMP get_Length(UINT32* value)
1328 | {
1329 | *value = m_Length;
1330 | return S_OK;
1331 | }
1332 |
1333 | // IBuffer::put_Length
1334 | STDMETHODIMP put_Length(UINT32 value)
1335 | {
1336 | if (value > m_Capacity)
1337 | return E_INVALIDARG;
1338 | m_Length = value;
1339 | return S_OK;
1340 | }
1341 | };
1342 |
1343 | // Retrieves the IBuffer object from the provided raw pointer.
1344 | // Parameters:
1345 | // Pointer: The raw pointer you want to retrieve the IBuffer object.
1346 | // Capacity: The size of raw pointer you want to retrieve the IBuffer object.
1347 | // Return value:
1348 | // If the function succeeds, the return value is the IBuffer object from the
1349 | // provided raw pointer. If the function fails, the return value is nullptr.
1350 | // Warning:
1351 | // The lifetime of the returned IBuffer object is controlled by the lifetime
1352 | // of the raw pointer that's passed to this method. When the raw pointer has
1353 | // been released, the IBuffer object becomes invalid and must not be used.
1354 | Windows::Storage::Streams::IBuffer^ M2MakeIBuffer(
1355 | byte* Pointer,
1356 | UINT32 Capacity)
1357 | {
1358 | using Windows::Storage::Streams::IBuffer;
1359 |
1360 | IBuffer^ buffer = nullptr;
1361 |
1362 | ComPtr bufferReference;
1363 | if (SUCCEEDED(MakeAndInitialize(
1364 | &bufferReference, Pointer, Capacity)))
1365 | {
1366 | buffer = reinterpret_cast(bufferReference.Get());
1367 | }
1368 |
1369 | return buffer;
1370 | }
1371 |
1372 | /**
1373 | * Creates a GUID, a unique 128-bit integer used for CLSIDs and interface
1374 | * identifiers.
1375 | *
1376 | * @return The function will return Platform::Guid object.
1377 | */
1378 | Platform::Guid M2CreateGuid()
1379 | {
1380 | GUID guid = { 0 };
1381 | M2ThrowPlatformExceptionIfFailed(CoCreateGuid(&guid));
1382 | return Platform::Guid(guid);
1383 | }
1384 |
1385 | #endif
1386 |
1387 | #pragma endregion
1388 |
1389 | #pragma region File
1390 |
1391 | /**
1392 | * Retrieves file system attributes for a specified file or directory.
1393 | *
1394 | * @param FileHandle A handle to the file that contains the information to be
1395 | * retrieved. This handle should not be a pipe handle.
1396 | * @param FileAttributes The attributes of the specified file or directory.
1397 | * For a list of attribute values and their descriptions,
1398 | * see File Attribute Constants. If the function fails,
1399 | * the return value is INVALID_FILE_ATTRIBUTES.
1400 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1401 | */
1402 | HRESULT M2GetFileAttributes(
1403 | _In_ HANDLE FileHandle,
1404 | _Out_ PDWORD FileAttributes)
1405 | {
1406 | FILE_BASIC_INFO BasicInfo;
1407 |
1408 | if (GetFileInformationByHandleEx(
1409 | FileHandle,
1410 | FileBasicInfo,
1411 | &BasicInfo,
1412 | sizeof(FILE_BASIC_INFO)))
1413 | {
1414 | *FileAttributes = BasicInfo.FileAttributes;
1415 | return S_OK;
1416 | }
1417 | else
1418 | {
1419 | *FileAttributes = INVALID_FILE_ATTRIBUTES;
1420 | return M2GetLastHRESULTErrorKnownFailedCall();
1421 | }
1422 | }
1423 |
1424 | /**
1425 | * Sets the attributes for a file or directory.
1426 | *
1427 | * @param FileHandle A handle to the file for which to change information. This
1428 | * handle must be opened with the appropriate permissions for
1429 | * the requested change. This handle should not be a pipe
1430 | * handle.
1431 | * @param FileAttributes The file attributes to set for the file. This
1432 | * parameter can be one or more values, combined using
1433 | * the bitwise - OR operator. However, all other values
1434 | * override FILE_ATTRIBUTE_NORMAL. For more information,
1435 | * see the SetFileAttributes function.
1436 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1437 | */
1438 | HRESULT M2SetFileAttributes(
1439 | _In_ HANDLE FileHandle,
1440 | _In_ DWORD FileAttributes)
1441 | {
1442 | FILE_BASIC_INFO BasicInfo = { 0 };
1443 | BasicInfo.FileAttributes =
1444 | FileAttributes & (
1445 | FILE_SHARE_READ |
1446 | FILE_SHARE_WRITE |
1447 | FILE_SHARE_DELETE |
1448 | FILE_ATTRIBUTE_ARCHIVE |
1449 | FILE_ATTRIBUTE_TEMPORARY |
1450 | FILE_ATTRIBUTE_OFFLINE |
1451 | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED |
1452 | FILE_ATTRIBUTE_NO_SCRUB_DATA) |
1453 | FILE_ATTRIBUTE_NORMAL;
1454 |
1455 | if (SetFileInformationByHandle(
1456 | FileHandle,
1457 | FileBasicInfo,
1458 | &BasicInfo,
1459 | sizeof(FILE_BASIC_INFO)))
1460 | {
1461 | return S_OK;
1462 | }
1463 | else
1464 | {
1465 | return M2GetLastHRESULTErrorKnownFailedCall();
1466 | }
1467 | }
1468 |
1469 | /**
1470 | * Retrieves the size of the specified file.
1471 | *
1472 | * @param FileHandle A handle to the file that contains the information to be
1473 | * retrieved. This handle should not be a pipe handle.
1474 | * @param FileSize A pointer to a ULONGLONG value that receives the file size,
1475 | * in bytes.
1476 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1477 | * @remark The way to get a file handle for this operation:
1478 | * HANDLE hFile = CreateFileW(
1479 | * lpFileName,
1480 | * GENERIC_READ | SYNCHRONIZE,
1481 | * FILE_SHARE_READ,
1482 | * nullptr,
1483 | * OPEN_EXISTING,
1484 | * FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
1485 | * nullptr);
1486 | */
1487 | HRESULT M2GetFileSize(
1488 | _In_ HANDLE FileHandle,
1489 | _Out_ PULONGLONG FileSize)
1490 | {
1491 | FILE_STANDARD_INFO StandardInfo;
1492 |
1493 | if (GetFileInformationByHandleEx(
1494 | FileHandle,
1495 | FileStandardInfo,
1496 | &StandardInfo,
1497 | sizeof(FILE_STANDARD_INFO)))
1498 | {
1499 | *FileSize = static_cast(
1500 | StandardInfo.EndOfFile.QuadPart);
1501 | return S_OK;
1502 | }
1503 | else
1504 | {
1505 | *FileSize = 0;
1506 | return M2GetLastHRESULTErrorKnownFailedCall();
1507 | }
1508 | }
1509 |
1510 | /**
1511 | * Retrieves the amount of space that is allocated for the file.
1512 | *
1513 | * @param FileHandle A handle to the file that contains the information to be
1514 | * retrieved. This handle should not be a pipe handle.
1515 | * @param AllocationSize A pointer to a ULONGLONG value that receives the
1516 | * amount of space that is allocated for the file, in
1517 | * bytes.
1518 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1519 | * @remark The way to get a file handle for this operation:
1520 | * HANDLE hFile = CreateFileW(
1521 | * lpFileName,
1522 | * GENERIC_READ | SYNCHRONIZE,
1523 | * FILE_SHARE_READ,
1524 | * nullptr,
1525 | * OPEN_EXISTING,
1526 | * FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
1527 | * nullptr);
1528 | */
1529 | HRESULT M2GetFileAllocationSize(
1530 | _In_ HANDLE FileHandle,
1531 | _Out_ PULONGLONG AllocationSize)
1532 | {
1533 | FILE_STANDARD_INFO StandardInfo;
1534 |
1535 | if (GetFileInformationByHandleEx(
1536 | FileHandle,
1537 | FileStandardInfo,
1538 | &StandardInfo,
1539 | sizeof(FILE_STANDARD_INFO)))
1540 | {
1541 | *AllocationSize = static_cast(
1542 | StandardInfo.AllocationSize.QuadPart);
1543 | return S_OK;
1544 | }
1545 | else
1546 | {
1547 | *AllocationSize = 0;
1548 | return M2GetLastHRESULTErrorKnownFailedCall();
1549 | }
1550 | }
1551 |
1552 | /**
1553 | * Deletes an existing file.
1554 | *
1555 | * @param FileHandle The handle of the file to be deleted.. This handle must be
1556 | * opened with the appropriate permissions for the requested
1557 | * change. This handle should not be a pipe handle.
1558 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1559 | * @remark The way to get a file handle for this operation:
1560 | * HANDLE hFile = CreateFileW(
1561 | * lpFileName,
1562 | * SYNCHRONIZE | DELETE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
1563 | * FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1564 | * nullptr,
1565 | * OPEN_EXISTING,
1566 | * FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
1567 | * nullptr);
1568 | */
1569 | HRESULT M2DeleteFile(
1570 | _In_ HANDLE FileHandle)
1571 | {
1572 | FILE_DISPOSITION_INFO DispostionInfo;
1573 | DispostionInfo.DeleteFile = TRUE;
1574 |
1575 | if (SetFileInformationByHandle(
1576 | FileHandle,
1577 | FileDispositionInfo,
1578 | &DispostionInfo,
1579 | sizeof(FILE_DISPOSITION_INFO)))
1580 | {
1581 | return S_OK;
1582 | }
1583 | else
1584 | {
1585 | return M2GetLastHRESULTErrorKnownFailedCall();
1586 | }
1587 | }
1588 |
1589 | /**
1590 | * Deletes an existing file, even the file have the readonly attribute.
1591 | *
1592 | * @param FileHandle The handle of the file to be deleted.. This handle must be
1593 | * opened with the appropriate permissions for the requested
1594 | * change. This handle should not be a pipe handle.
1595 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1596 | * @remark The way to get a file handle for this operation:
1597 | * HANDLE hFile = CreateFileW(
1598 | * lpFileName,
1599 | * SYNCHRONIZE | DELETE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
1600 | * FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1601 | * nullptr,
1602 | * OPEN_EXISTING,
1603 | * FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
1604 | * nullptr);
1605 | */
1606 | HRESULT M2DeleteFileIgnoreReadonlyAttribute(
1607 | _In_ HANDLE FileHandle)
1608 | {
1609 | HRESULT hr = S_OK;
1610 | DWORD OldAttribute = 0;
1611 |
1612 | // Save old attributes.
1613 | hr = M2GetFileAttributes(
1614 | FileHandle,
1615 | &OldAttribute);
1616 | if (!SUCCEEDED(hr)) return hr;
1617 |
1618 | // Remove readonly attribute.
1619 | hr = M2SetFileAttributes(
1620 | FileHandle,
1621 | OldAttribute & (-1 ^ FILE_ATTRIBUTE_READONLY));
1622 | if (!SUCCEEDED(hr)) return hr;
1623 |
1624 | // Delete the file.
1625 | hr = M2DeleteFile(FileHandle);
1626 | if (!SUCCEEDED(hr))
1627 | {
1628 | // Restore attributes if failed.
1629 | hr = M2SetFileAttributes(
1630 | FileHandle,
1631 | OldAttribute);
1632 | }
1633 |
1634 | return hr;
1635 | }
1636 |
1637 | #pragma endregion
1638 |
1639 | #pragma region Module
1640 |
1641 | /**
1642 | * Retrieves the address of an exported function or variable from the specified
1643 | * dynamic-link library (DLL).
1644 | *
1645 | * @param lpProcAddress The address of the exported function or variable.
1646 | * @param hModule A handle to the DLL module that contains the function or
1647 | * variable. The LoadLibrary, LoadLibraryEx, LoadPackagedLibrary
1648 | * or GetModuleHandle function returns this handle. This
1649 | * function does not retrieve addresses from modules that were
1650 | * loaded using the LOAD_LIBRARY_AS_DATAFILE flag. For more
1651 | * information, see LoadLibraryEx.
1652 | * @param lpProcName The function or variable name, or the function's ordinal
1653 | * value. If this parameter is an ordinal value, it must be
1654 | * in the low-order word; the high-order word must be zero.
1655 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1656 | */
1657 | HRESULT M2GetProcAddress(
1658 | _Out_ FARPROC* lpProcAddress,
1659 | _In_ HMODULE hModule,
1660 | _In_ LPCSTR lpProcName)
1661 | {
1662 | *lpProcAddress = GetProcAddress(hModule, lpProcName);
1663 | return (!*lpProcAddress) ? S_OK : M2GetLastHRESULTErrorKnownFailedCall();
1664 | }
1665 |
1666 | /**
1667 | * Retrieves the path of the executable file of the current process.
1668 | *
1669 | * @return If the function succeeds, the return value is the path of the
1670 | * executable file of the current process. If the function fails, the
1671 | * return value is an empty string.
1672 | */
1673 | std::wstring M2GetCurrentProcessModulePath()
1674 | {
1675 | std::wstring result(MAX_PATH, L'\0');
1676 | GetModuleFileNameW(nullptr, &result[0], (DWORD)(result.capacity()));
1677 | result.resize(wcslen(result.c_str()));
1678 | return result;
1679 | }
1680 |
1681 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
1682 |
1683 | /**
1684 | * Loads the specified module with the optimization of the mitigation of DLL
1685 | * preloading attacks into the address space of the calling process safely. The
1686 | * specified module may cause other modules to be loaded.
1687 | *
1688 | * @param phLibModule A handle to the loaded module.
1689 | * @param lpLibFileName A string that specifies the file name of the module to
1690 | * load.
1691 | * @param hFile This parameter is reserved for future use. It must be NULL.
1692 | * @param dwFlags The action to be taken when loading the module.
1693 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1694 | * @remark For more information, see LoadLibraryEx.
1695 | */
1696 | HRESULT M2LoadLibrary(
1697 | _Out_ HMODULE* phLibModule,
1698 | _In_ LPCWSTR lpLibFileName,
1699 | _Reserved_ HANDLE hFile,
1700 | _In_ DWORD dwFlags)
1701 | {
1702 | *phLibModule = LoadLibraryExW(lpLibFileName, hFile, dwFlags);
1703 | return *phLibModule ? S_OK : M2GetLastHRESULTErrorKnownFailedCall();
1704 | }
1705 |
1706 | /**
1707 | * Obtain the best matching resource with the specified type and name in the
1708 | * specified module.
1709 | *
1710 | * @param lpResourceInfo The resource info which contains the pointer and size.
1711 | * @param hModule A handle to the module whose portable executable file or an
1712 | * accompanying MUI file contains the resource. If this
1713 | * parameter is NULL, the function searches the module used to
1714 | * create the current process.
1715 | * @param lpType The resource type. Alternately, rather than a pointer, this
1716 | * parameter can be MAKEINTRESOURCE(ID), where ID is the integer
1717 | * identifier of the given resource type.
1718 | * @param lpName The name of the resource. Alternately, rather than a pointer,
1719 | * this parameter can be MAKEINTRESOURCE(ID), where ID is the
1720 | * integer identifier of the resource.
1721 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1722 | */
1723 | HRESULT M2LoadResource(
1724 | _Out_ PM2_RESOURCE_INFO lpResourceInfo,
1725 | _In_opt_ HMODULE hModule,
1726 | _In_ LPCWSTR lpType,
1727 | _In_ LPCWSTR lpName)
1728 | {
1729 | if (!lpResourceInfo)
1730 | return E_INVALIDARG;
1731 |
1732 | lpResourceInfo->Size = 0;
1733 | lpResourceInfo->Pointer = nullptr;
1734 |
1735 | HRSRC ResourceFind = FindResourceExW(
1736 | hModule, lpType, lpName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
1737 | if (!ResourceFind)
1738 | return M2GetLastHRESULTErrorKnownFailedCall();
1739 |
1740 | lpResourceInfo->Size = SizeofResource(hModule, ResourceFind);
1741 |
1742 | HGLOBAL ResourceLoad = LoadResource(hModule, ResourceFind);
1743 | if (!ResourceLoad)
1744 | return M2GetLastHRESULTErrorKnownFailedCall();
1745 |
1746 | lpResourceInfo->Pointer = LockResource(ResourceLoad);
1747 |
1748 | return S_OK;
1749 | }
1750 |
1751 | /**
1752 | * Loads the specified module with the optimization of the mitigation of DLL
1753 | * preloading attacks into the address space of the calling process safely. The
1754 | * specified module may cause other modules to be loaded.
1755 | *
1756 | * @param ModuleHandle If the function succeeds, this parameter's value is a
1757 | * handle to the loaded module. You should read the
1758 | * documentation about LoadLibraryEx API for further
1759 | * information.
1760 | * @param LibraryFileName A string that specifies the file name of the module
1761 | * to load. You should read the documentation about
1762 | * LoadLibraryEx API for further information.
1763 | * @param Flags The action to be taken when loading the module. You should read
1764 | * the documentation about LoadLibraryEx API for further
1765 | * information.
1766 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1767 | */
1768 | HRESULT M2LoadLibraryEx(
1769 | _Out_ HMODULE* ModuleHandle,
1770 | _In_ LPCWSTR LibraryFileName,
1771 | _In_ DWORD Flags)
1772 | {
1773 | HRESULT hr = M2LoadLibrary(ModuleHandle, LibraryFileName, nullptr, Flags);
1774 | if (SUCCEEDED(hr))
1775 | {
1776 | if ((Flags & LOAD_LIBRARY_SEARCH_SYSTEM32) &&
1777 | (hr == __HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)))
1778 | {
1779 | if (!wcschr(LibraryFileName, L'\\'))
1780 | {
1781 | std::wstring SystemDirectoryPath;
1782 | hr = M2GetSystemDirectory(SystemDirectoryPath);
1783 | if (SUCCEEDED(hr))
1784 | {
1785 | hr = M2LoadLibrary(
1786 | ModuleHandle,
1787 | (SystemDirectoryPath + LibraryFileName).c_str(),
1788 | nullptr,
1789 | Flags);
1790 | }
1791 | }
1792 | }
1793 | }
1794 |
1795 | return hr;
1796 | }
1797 |
1798 | #endif
1799 |
1800 | #pragma endregion
1801 |
1802 | #pragma region Registry
1803 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
1804 |
1805 | /**
1806 | * Creates the specified registry key. If the key already exists, the function
1807 | * opens it. Note that key names are not case sensitive.
1808 | *
1809 | * @param hKey A handle to an open registry key.
1810 | * @param lpSubKey The name of a subkey that this function opens or creates
1811 | * @param Reserved This parameter is reserved and must be zero.
1812 | * @param lpClass The user-defined class type of this key.
1813 | * @param dwOptions This parameter can be one of the following values:
1814 | * REG_OPTION_BACKUP_RESTORE, REG_OPTION_CREATE_LINK,
1815 | * REG_OPTION_NON_VOLATILE, REG_OPTION_VOLATILE.
1816 | * @param samDesired A mask that specifies the access rights for the key to be
1817 | * created.
1818 | * @param lpSecurityAttributes A pointer to a SECURITY_ATTRIBUTES structure
1819 | * that determines whether the returned handle can
1820 | * be inherited by child processes.
1821 | * @param phkResult A pointer to a variable that receives a handle to the
1822 | * opened or created key.
1823 | * @param lpdwDisposition A pointer to a variable that receives one of the
1824 | * following disposition values.
1825 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1826 | * @remark For more information, see RegCreateKeyEx.
1827 | */
1828 | HRESULT M2RegCreateKey(
1829 | _In_ HKEY hKey,
1830 | _In_ LPCWSTR lpSubKey,
1831 | _Reserved_ DWORD Reserved,
1832 | _In_opt_ LPWSTR lpClass,
1833 | _In_ DWORD dwOptions,
1834 | _In_ REGSAM samDesired,
1835 | _In_opt_ CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes,
1836 | _Out_ PHKEY phkResult,
1837 | _Out_opt_ LPDWORD lpdwDisposition)
1838 | {
1839 | return HRESULT_FROM_WIN32(RegCreateKeyExW(
1840 | hKey,
1841 | lpSubKey,
1842 | Reserved,
1843 | lpClass,
1844 | dwOptions,
1845 | samDesired,
1846 | lpSecurityAttributes,
1847 | phkResult,
1848 | lpdwDisposition));
1849 | }
1850 |
1851 | /**
1852 | * Retrieves the type and data for the specified value name associated with an
1853 | * open registry key.
1854 | *
1855 | * @param hKey A handle to an open registry key.
1856 | * @param lpValueName The name of the registry value.
1857 | * @param lpReserved This parameter is reserved and must be NULL.
1858 | * @param lpType A pointer to a variable that receives a code indicating the
1859 | * type of data stored in the specified value.
1860 | * @param lpData A pointer to a buffer that receives the value's data.
1861 | * @param lpcbData A pointer to a variable that specifies the size of the
1862 | * buffer pointed to by the lpData parameter, in bytes.
1863 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1864 | * @remark For more information, see RegQueryValueEx.
1865 | */
1866 | HRESULT M2RegQueryValue(
1867 | _In_ HKEY hKey,
1868 | _In_opt_ LPCWSTR lpValueName,
1869 | _Reserved_ LPDWORD lpReserved,
1870 | _Out_opt_ LPDWORD lpType,
1871 | _Out_opt_ LPBYTE lpData,
1872 | _Inout_opt_ LPDWORD lpcbData)
1873 | {
1874 | return HRESULT_FROM_WIN32(RegQueryValueExW(
1875 | hKey,
1876 | lpValueName,
1877 | lpReserved,
1878 | lpType,
1879 | lpData,
1880 | lpcbData));
1881 | }
1882 |
1883 | /**
1884 | * Retrieves the type and data for the specified value name associated with an
1885 | * open registry key.
1886 | *
1887 | * @param hKey A handle to an open registry key.
1888 | * @param lpValueName The name of the value to be set.
1889 | * @param Reserved This parameter is reserved and must be zero.
1890 | * @param dwType The type of data pointed to by the lpData parameter.
1891 | * @param lpData The data to be stored.
1892 | * @param cbData The size of the information pointed to by the lpData
1893 | * parameter, in bytes.
1894 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1895 | * @remark For more information, see RegSetValueEx.
1896 | */
1897 | HRESULT M2RegSetValue(
1898 | _In_ HKEY hKey,
1899 | _In_opt_ LPCWSTR lpValueName,
1900 | _Reserved_ DWORD Reserved,
1901 | _In_ DWORD dwType,
1902 | _In_opt_ CONST BYTE* lpData,
1903 | _In_ DWORD cbData)
1904 | {
1905 | return HRESULT_FROM_WIN32(RegSetValueExW(
1906 | hKey,
1907 | lpValueName,
1908 | Reserved,
1909 | dwType,
1910 | lpData,
1911 | cbData));
1912 | }
1913 |
1914 | /**
1915 | * Retrieves the string type data for the specified value name associated with
1916 | * an open registry key.
1917 | *
1918 | * @param hKey A handle to an open registry key.
1919 | * @param lpValueName The name of the registry value.
1920 | * @param lpData A pointer to a buffer that receives the value's data. When you
1921 | * have finished using the information, free it by calling the
1922 | * M2FreeMemory function. You should also set the pointer to
1923 | * NULL.
1924 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1925 | * @remark For more information, see RegQueryValueEx.
1926 | */
1927 | HRESULT M2RegQueryStringValue(
1928 | _Out_ LPWSTR* lpData,
1929 | _In_ HKEY hKey,
1930 | _In_opt_ LPCWSTR lpValueName)
1931 | {
1932 | *lpData = nullptr;
1933 |
1934 | DWORD cbData = 0;
1935 | HRESULT hr = M2RegQueryValue(
1936 | hKey,
1937 | lpValueName,
1938 | nullptr,
1939 | nullptr,
1940 | nullptr,
1941 | &cbData);
1942 | if (SUCCEEDED(hr))
1943 | {
1944 | hr = M2AllocMemory(reinterpret_cast(lpData), cbData);
1945 | if (SUCCEEDED(hr))
1946 | {
1947 | DWORD Type = 0;
1948 | hr = M2RegQueryValue(
1949 | hKey,
1950 | lpValueName,
1951 | nullptr,
1952 | &Type,
1953 | reinterpret_cast(*lpData),
1954 | &cbData);
1955 | if (SUCCEEDED(hr) && REG_SZ != Type)
1956 | hr = __HRESULT_FROM_WIN32(ERROR_ILLEGAL_ELEMENT_ADDRESS);
1957 |
1958 | if (FAILED(hr))
1959 | hr = M2FreeMemory(*lpData);
1960 | }
1961 | }
1962 |
1963 | return hr;
1964 | }
1965 |
1966 | #endif
1967 | #pragma endregion
1968 |
1969 | #pragma region Service
1970 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
1971 |
1972 | /**
1973 | * Starts a service if not started and retrieves the current status of the
1974 | * specified service.
1975 | *
1976 | * @param lpServiceName The name of the service to be started. This is the name
1977 | * specified by the lpServiceName parameter of the
1978 | * CreateService function when the service object was
1979 | * created, not the service display name that is shown by
1980 | * user interface applications to identify the service.
1981 | * The maximum string length is 256 characters. The
1982 | * service control manager database preserves the case of
1983 | * the characters, but service name comparisons are always
1984 | * case insensitive. Forward-slash (/) and backslash ()
1985 | * are invalid service name characters.
1986 | * @param lpServiceStatus Contains process status information for a service.
1987 | * @return HRESULT. If the function succeeds, the return value is S_OK.
1988 | */
1989 | HRESULT M2StartService(
1990 | _In_ LPCWSTR lpServiceName,
1991 | _Out_ LPSERVICE_STATUS_PROCESS lpServiceStatus)
1992 | {
1993 | memset(lpServiceStatus, 0, sizeof(LPSERVICE_STATUS_PROCESS));
1994 |
1995 | M2::CServiceHandle hSCM;
1996 | M2::CServiceHandle hService;
1997 |
1998 | DWORD nBytesNeeded = 0;
1999 | DWORD nOldCheckPoint = 0;
2000 | ULONGLONG nLastTick = 0;
2001 | bool bStartServiceWCalled = false;
2002 |
2003 | hSCM = OpenSCManagerW(
2004 | nullptr,
2005 | nullptr,
2006 | SC_MANAGER_CONNECT);
2007 | if (!hSCM)
2008 | return M2GetLastHRESULTErrorKnownFailedCall();
2009 |
2010 | hService = OpenServiceW(
2011 | hSCM,
2012 | lpServiceName,
2013 | SERVICE_QUERY_STATUS | SERVICE_START);
2014 | if (!hService)
2015 | return M2GetLastHRESULTErrorKnownFailedCall();
2016 |
2017 | while (QueryServiceStatusEx(
2018 | hService,
2019 | SC_STATUS_PROCESS_INFO,
2020 | reinterpret_cast(lpServiceStatus),
2021 | sizeof(SERVICE_STATUS_PROCESS),
2022 | &nBytesNeeded))
2023 | {
2024 | if (SERVICE_STOPPED == lpServiceStatus->dwCurrentState)
2025 | {
2026 | // Failed if the service had stopped again.
2027 | if (bStartServiceWCalled)
2028 | return E_FAIL;
2029 |
2030 | if (!StartServiceW(hService, 0, nullptr))
2031 | return M2GetLastHRESULTErrorKnownFailedCall();
2032 |
2033 | bStartServiceWCalled = true;
2034 | }
2035 | else if (
2036 | SERVICE_STOP_PENDING == lpServiceStatus->dwCurrentState ||
2037 | SERVICE_START_PENDING == lpServiceStatus->dwCurrentState)
2038 | {
2039 | ULONGLONG nCurrentTick = GetTickCount64();
2040 |
2041 | if (!nLastTick)
2042 | {
2043 | nLastTick = nCurrentTick;
2044 | nOldCheckPoint = lpServiceStatus->dwCheckPoint;
2045 |
2046 | // Same as the .Net System.ServiceProcess, wait 250ms.
2047 | SleepEx(250, FALSE);
2048 | }
2049 | else
2050 | {
2051 | // Check the timeout if the checkpoint is not increased.
2052 | if (lpServiceStatus->dwCheckPoint <= nOldCheckPoint)
2053 | {
2054 | ULONGLONG nDiff = nCurrentTick - nLastTick;
2055 | if (nDiff > lpServiceStatus->dwWaitHint)
2056 | {
2057 | return __HRESULT_FROM_WIN32(ERROR_TIMEOUT);
2058 | }
2059 | }
2060 |
2061 | // Continue looping.
2062 | nLastTick = 0;
2063 | }
2064 | }
2065 | else
2066 | {
2067 | break;
2068 | }
2069 | }
2070 |
2071 | return S_OK;
2072 | }
2073 |
2074 | #endif
2075 | #pragma endregion
2076 |
2077 | #pragma region Environment
2078 |
2079 | /**
2080 | * Expands environment-variable strings and replaces them with the values
2081 | * defined for the current user.
2082 | *
2083 | * @param ExpandedString The expanded string.
2084 | * @param VariableName The environment-variable string you need to expand.
2085 | * @return HRESULT. If the function succeeds, the return value is S_OK.
2086 | */
2087 | HRESULT M2ExpandEnvironmentStrings(
2088 | std::wstring& ExpandedString,
2089 | const std::wstring& VariableName)
2090 | {
2091 | HRESULT hr = S_OK;
2092 |
2093 | do
2094 | {
2095 | DWORD Length = ExpandEnvironmentStringsW(
2096 | VariableName.c_str(),
2097 | nullptr,
2098 | 0);
2099 | if (0 == Length)
2100 | {
2101 | hr = M2GetLastHRESULTErrorKnownFailedCall();
2102 | break;
2103 | }
2104 |
2105 | ExpandedString.resize(Length - 1);
2106 |
2107 | Length = ExpandEnvironmentStringsW(
2108 | VariableName.c_str(),
2109 | &ExpandedString[0],
2110 | static_cast(ExpandedString.size() + 1));
2111 | if (0 == Length)
2112 | {
2113 | hr = M2GetLastHRESULTErrorKnownFailedCall();
2114 | break;
2115 | }
2116 | if (ExpandedString.size() != Length - 1)
2117 | {
2118 | hr = E_UNEXPECTED;
2119 | break;
2120 | }
2121 |
2122 | } while (false);
2123 |
2124 | if (FAILED(hr))
2125 | {
2126 | ExpandedString.clear();
2127 | }
2128 |
2129 | return hr;
2130 | }
2131 |
2132 | /**
2133 | * Retrieves the path of the system directory.
2134 | *
2135 | * @param SystemFolderPath The string of the path of the system directory.
2136 | * @return HRESULT. If the function succeeds, the return value is S_OK.
2137 | */
2138 | HRESULT M2GetSystemDirectory(
2139 | std::wstring& SystemFolderPath)
2140 | {
2141 | HRESULT hr = S_OK;
2142 |
2143 | do
2144 | {
2145 | UINT Length = GetSystemDirectoryW(
2146 | nullptr,
2147 | 0);
2148 | if (0 == Length)
2149 | {
2150 | hr = M2GetLastHRESULTErrorKnownFailedCall();
2151 | break;
2152 | }
2153 |
2154 | SystemFolderPath.resize(Length - 1);
2155 |
2156 | Length = GetSystemDirectoryW(
2157 | &SystemFolderPath[0],
2158 | static_cast(Length));
2159 | if (0 == Length)
2160 | {
2161 | hr = M2GetLastHRESULTErrorKnownFailedCall();
2162 | break;
2163 | }
2164 | if (SystemFolderPath.size() != Length)
2165 | {
2166 | hr = E_UNEXPECTED;
2167 | break;
2168 | }
2169 |
2170 | } while (false);
2171 |
2172 | if (FAILED(hr))
2173 | {
2174 | SystemFolderPath.clear();
2175 | }
2176 |
2177 | return hr;
2178 | }
2179 |
2180 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
2181 |
2182 | /**
2183 | * Retrieves the path of the shared Windows directory on a multi-user system.
2184 | *
2185 | * @param WindowsFolderPath The string of the path of the shared Windows
2186 | * directory on a multi-user system.
2187 | * @return HRESULT. If the function succeeds, the return value is S_OK.
2188 | */
2189 | HRESULT M2GetWindowsDirectory(
2190 | std::wstring& WindowsFolderPath)
2191 | {
2192 | HRESULT hr = S_OK;
2193 |
2194 | do
2195 | {
2196 | UINT Length = GetSystemWindowsDirectoryW(
2197 | nullptr,
2198 | 0);
2199 | if (0 == Length)
2200 | {
2201 | hr = M2GetLastHRESULTErrorKnownFailedCall();
2202 | break;
2203 | }
2204 |
2205 | WindowsFolderPath.resize(Length - 1);
2206 |
2207 | Length = GetSystemWindowsDirectoryW(
2208 | &WindowsFolderPath[0],
2209 | static_cast(Length));
2210 | if (0 == Length)
2211 | {
2212 | hr = M2GetLastHRESULTErrorKnownFailedCall();
2213 | break;
2214 | }
2215 | if (WindowsFolderPath.size() != Length)
2216 | {
2217 | hr = E_UNEXPECTED;
2218 | break;
2219 | }
2220 |
2221 | } while (false);
2222 |
2223 | if (FAILED(hr))
2224 | {
2225 | WindowsFolderPath.clear();
2226 | }
2227 |
2228 | return hr;
2229 | }
2230 |
2231 | /**
2232 | * Enables the Per-Monitor DPI Aware for the specified dialog using the
2233 | * internal API from Windows.
2234 | *
2235 | * @return INT. If failed. returns -1.
2236 | * @remarks You need to use this function in Windows 10 Threshold 1 or Windows
2237 | * 10 Threshold 2.
2238 | */
2239 | INT M2EnablePerMonitorDialogScaling()
2240 | {
2241 | // Fix for Windows Vista and Server 2008.
2242 | if (!IsWindowsVersionOrGreater(10, 0, 0)) return -1;
2243 |
2244 | typedef INT(WINAPI * PFN_EnablePerMonitorDialogScaling)();
2245 |
2246 | HMODULE hModule = nullptr;
2247 | PFN_EnablePerMonitorDialogScaling pFunc = nullptr;
2248 |
2249 | hModule = GetModuleHandleW(L"user32.dll");
2250 | if (!hModule) return -1;
2251 |
2252 | if (FAILED(M2GetProcAddress(
2253 | pFunc, hModule, reinterpret_cast(2577))))
2254 | return -1;
2255 |
2256 | return pFunc();
2257 | }
2258 |
2259 | /**
2260 | * Queries the dots per inch (dpi) of a display.
2261 | *
2262 | * @param hmonitor Handle of the monitor being queried.
2263 | * @param dpiType The type of DPI being queried. Possible values are from the
2264 | * MONITOR_DPI_TYPE enumeration.
2265 | * @param dpiX The value of the DPI along the X axis. This value always refers
2266 | * to the horizontal edge, even when the screen is rotated.
2267 | * @param dpiY The value of the DPI along the Y axis. This value always refers
2268 | * to the vertical edge, even when the screen is rotated.
2269 | * @return HRESULT. If the function succeeds, the return value is S_OK.
2270 | */
2271 | HRESULT M2GetDpiForMonitor(
2272 | _In_ HMONITOR hmonitor,
2273 | _In_ MONITOR_DPI_TYPE dpiType,
2274 | _Out_ UINT* dpiX,
2275 | _Out_ UINT* dpiY)
2276 | {
2277 | HMODULE hModule = nullptr;
2278 | HRESULT hr = M2LoadLibraryEx(
2279 | &hModule,
2280 | L"SHCore.dll",
2281 | LOAD_LIBRARY_SEARCH_SYSTEM32);
2282 | if (SUCCEEDED(hr))
2283 | {
2284 | decltype(GetDpiForMonitor)* pFunc = nullptr;
2285 | hr = M2GetProcAddress(pFunc, hModule, "GetDpiForMonitor");
2286 | if (SUCCEEDED(hr))
2287 | {
2288 | hr = pFunc(hmonitor, dpiType, dpiX, dpiY);
2289 | }
2290 |
2291 | FreeLibrary(hModule);
2292 | }
2293 |
2294 | return hr;
2295 | }
2296 |
2297 | #endif
2298 |
2299 | #pragma endregion
2300 |
2301 | #pragma region WinRT
2302 |
2303 | #ifdef CPPWINRT_VERSION
2304 |
2305 | /**
2306 | * Execute function on the UI thread with normal priority.
2307 | *
2308 | * @param agileCallback The function you want to execute.
2309 | * @return The return value is Windows::Foundation::IAsyncAction^.
2310 | */
2311 | winrt::IAsyncAction M2ExecuteOnUIThread(
2312 | winrt::DispatchedHandler const& agileCallback)
2313 | {
2314 | using winrt::Windows::ApplicationModel::Core::CoreApplication;
2315 | using winrt::Windows::UI::Core::CoreDispatcherPriority;
2316 |
2317 | return CoreApplication::MainView().CoreWindow().Dispatcher().RunAsync(
2318 | CoreDispatcherPriority::Normal, agileCallback);
2319 | }
2320 |
2321 | namespace M2
2322 | {
2323 | void NotifyPropertyChangedBase::RaisePropertyChanged(
2324 | std::wstring_view const& PropertyName)
2325 | {
2326 | this->m_PropertyChanged(
2327 | *this, winrt::PropertyChangedEventArgs(PropertyName));
2328 | }
2329 |
2330 | winrt::event_token NotifyPropertyChangedBase::PropertyChanged(
2331 | winrt::PropertyChangedEventHandler const& value)
2332 | {
2333 | return this->m_PropertyChanged.add(value);
2334 | }
2335 |
2336 | void NotifyPropertyChangedBase::PropertyChanged(
2337 | winrt::event_token const& token)
2338 | {
2339 | this->m_PropertyChanged.remove(token);
2340 | }
2341 | }
2342 |
2343 | #endif
2344 |
2345 | #ifdef __cplusplus_winrt
2346 |
2347 | /**
2348 | * Handle the completed asynchronous call.
2349 | *
2350 | * @param Async The completed asynchronous call you want to handle.
2351 | * @return Return the HRESULT determined by the asynchronous call.
2352 | */
2353 | HRESULT M2AsyncHandleCompleted(Platform::Object^ Async)
2354 | {
2355 | HRESULT hr = S_OK;
2356 | ABI::Windows::Foundation::IAsyncInfo* asyncInfo = nullptr;
2357 |
2358 | hr = M2GetInspectable(Async)->QueryInterface(&asyncInfo);
2359 | if (SUCCEEDED(hr))
2360 | {
2361 | // Get the error code.
2362 | AsyncStatus asyncStatus;
2363 | hr = asyncInfo->get_Status(&asyncStatus);
2364 | if (SUCCEEDED(hr))
2365 | {
2366 | if (AsyncStatus::Completed == asyncStatus)
2367 | {
2368 | // Just return S_OK if succeeded.
2369 | hr = S_OK;
2370 | }
2371 | else if (AsyncStatus::Started == asyncStatus)
2372 | {
2373 | // Cancel the asynchronous call and return error code if
2374 | // the status is still Started, the timeout interval has
2375 | // been elapsed.
2376 | hr = asyncInfo->Cancel();
2377 | if (SUCCEEDED(hr)) hr = __HRESULT_FROM_WIN32(ERROR_TIMEOUT);
2378 | }
2379 | else if (AsyncStatus::Canceled == asyncStatus)
2380 | {
2381 | // If the status is Cancelled, return the error code.
2382 | hr = E_ABORT;
2383 | }
2384 | else
2385 | {
2386 | HRESULT hrTemp;
2387 |
2388 | // If the status is other value, return the error code.
2389 | hr = asyncInfo->get_ErrorCode(&hrTemp);
2390 | if (SUCCEEDED(hr)) hr = hrTemp;
2391 | }
2392 | }
2393 |
2394 | asyncInfo->Release();
2395 | }
2396 |
2397 | return hr;
2398 | }
2399 |
2400 | /**
2401 | * Execute function on the UI thread with normal priority.
2402 | *
2403 | * @param agileCallback The function you want to execute.
2404 | * @return The return value is Windows::Foundation::IAsyncAction^.
2405 | */
2406 | Windows::Foundation::IAsyncAction^ M2ExecuteOnUIThread(
2407 | Windows::UI::Core::DispatchedHandler^ agileCallback)
2408 | {
2409 | using Windows::ApplicationModel::Core::CoreApplication;
2410 | using Windows::UI::Core::CoreDispatcherPriority;
2411 |
2412 | return CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
2413 | CoreDispatcherPriority::Normal, agileCallback);
2414 | }
2415 |
2416 | #endif
2417 |
2418 | #pragma endregion
2419 |
2420 |
--------------------------------------------------------------------------------