C++11新增预定义的宏

C++11中新增了4个预定义的宏,他们分别为__STDC____STDC_HOSTED____STDC_VERSION____STDC_ISO_10646__

  1. __STDC__用于指示编译器是否支持ISO标准C语言,如果支持ISO标准C语言则_STDC__定义为1,否为定义为0。这个宏在不同的编译器上可能有不同的定义,甚至有未定义的情况。例如在GCC上,编译并输出该宏的值为1,而在Visual Studio C++上,默认情况下该宏处于未定义状态。

  2. __STDC_HOSTED__用于指示宿主环境是否具有标准C库的完整功能,如果具有标准C库的完整功能则__STDC_HOSTED__定义为1,否为定义为0。

  3. __STDC_VERSION__用于定义C标准的版本号,但是标准文档中并没有明确规定其实现,所以在很多编译器中这个宏处于未定义状态。

  4. __STDC_ISO_10646__用于指示wchar_t是否使用Unicode,如果使用Unicode那么wchar_t展开为yyyymmL的形式。

编译运行下面这段代码用于检测这些宏的定义状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
cout << "__cplusplus is " << __cplusplus << endl;
cout << "__DATE__ is " << __DATE__ << endl;
cout << "__FILE__ is " << __FILE__ << endl;
cout << "__LINE__ is " << __LINE__ << endl;
#ifdef __STDC_HOSTED__
cout << "__STDC_HOSTED__ is " << __STDC_HOSTED__ << endl;
#else
cout << "__STDC_HOSTED__ is not defined" << endl;
#endif
cout << "__TIME__ is " << __TIME__ << endl;
#ifdef __STDC__
cout << "__STDC__ is " << __STDC__ << endl;
#else
cout << "__STDC__ is not defined" << endl;
#endif
#ifdef __STDC_MB_MIGHT_NEQ_WC__
cout << "__STDC_MB_MIGHT_NEQ_WC__ is " << __STDC_MB_MIGHT_NEQ_WC__ << endl;
#else
cout << "__STDC_MB_MIGHT_NEQ_WC__ is not defined" << endl;
#endif
#ifdef __STDC_VERSION__
cout << "__STDC_VERSION__ is " << __STDC_VERSION__ << endl;
#else
cout << "__STDC_VERSION__ is not defined" << endl;
#endif
#ifdef __STDC_ISO_10646__
cout << "__STDC_ISO_10646__ is " << __STDC_ISO_10646__ << endl;
#else
cout << "__STDC_ISO_10646__ is not defined" << endl;
#endif
#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
cout << "__STDCPP_DEFAULT_NEW_ALIGNMENT__ is " << __STDCPP_DEFAULT_NEW_ALIGNMENT__ << endl;
#else
cout << "__STDCPP_DEFAULT_NEW_ALIGNMENT__ is not defined" << endl;
#endif
#ifdef __STDCPP_STRICT_POINTER_SAFETY__
cout << "__STDCPP_STRICT_POINTER_SAFETY__ is " << __STDCPP_STRICT_POINTER_SAFETY__ << endl;
#else
cout << "__STDCPP_STRICT_POINTER_SAFETY__ is not defined" << endl;
#endif
#ifdef __STDCPP_THREADS__
cout << "__STDCPP_THREADS__ is " << __STDCPP_THREADS__ << endl;
#else
cout << "__STDCPP_THREADS__ is not defined" << endl;
#endif
}

Tips

在Windows中编译GCC

最近心血来潮想体验下C++2a的标准,但是mingw中的GCC最新版本是8.2。于是乎就产生了编译thunk下最新代码。

要在Windows上编译GCC没有在linux上方便,但是也是可以完成的。首先我们需要一个mingw的环境。自带mingw环境的软件有很多,这里我比较推荐MSYS2,因为这个环境更新的比较快。下载安装好了以后,运行MSYS2,会弹出类似linux终端窗口。这里我们首先下载需要的开发编译环境:

pacman -S –needed mingw-w64-i686-toolchain mingw-w64-x86_64-toolchain

下载安装好了开发环境后,可以下载GCC源代码:

mkdir gcc-latest
cd gcc-latest
git clone git://gcc.gnu.org/git/gcc.git

源代码比较大(2个多G),下载需要一点耐心。
源代码下载完成后,创建编译目录:

mkdir build

在开始编译之前,有一个坑需要注意下,我们需要将usr/bin/下的makeinfo改名。不知道为什么,最新的makeinfo对gcc的texi文件不兼容会导致编译失败。这个操作之后就可以开始配置了。

../gcc/configure –enable-languages=c,c++ –disable-multilib –disable-bootstrap –disable-werror –disable-nls –prefix=/mingw64/gcc-latest –build=x86_64-w64-mingw32 –host=x86_64-w64-mingw32 –target=x86_64-w64-mingw32 –with-native-system-header-dir=/mingw64/x86_64-w64-mingw32/include –with-arch=x86-64 MAKEINFO=missing
export LIBRARY_PATH=/mingw64/x86_64-w64-mingw32/lib

配置的选项比较多,我们可以参考GCC文档进行参照。这里的配置是我尝试后感觉必须加入的,否则编译的时候会出相应的问题。可以说是编译GCC血泪史了。
再然后就可以开始编译了:

make -j 16

使用多线程编译,编译速度会快不少。编译好了以后就可以安装了,注意安装的时候可能会遇到失败,需要注释build/gcc/makefile中,对应s-tm-texi的相关代码。

make install

最后检查gcc版本号:

gcc -v

注意目前最新的版本号为9.0.1。

Tips

在循环中使用CComPtr需要特别注意

CComPtr是ATL里提供给我们管理COM接口的智能指针类,使用它能够让我们无需关心接口的引用释放。例如:

1
2
CComPtr<IShellFolder> pDesktop;
SHGetDesktopFolder(&pDesktop);

但是这个类在循环中使用的时候要特别注意一下,例如:

1
2
3
4
5
6
7
8
9
10
11
// 没有问题
for (...) {
CComPtr<IShellFolder> pDesktop;
SHGetDesktopFolder(&pDesktop);
}

// 有问题,接口没有调用Release,内存泄露
CComPtr<IShellFolder> pDesktop;
for (...) {
SHGetDesktopFolder(&pDesktop);
}

其根本原因是,CComPtr的&操作符重载的时候没有做释放操作,只有Debug版本的assert来提醒程序员这样使用的问题。

1
2
3
4
5
T** operator&() throw()
{
ATLASSERT(p==NULL);
return &p;
}

所以我们需要手动调用Release

1
2
3
4
5
6
CComPtr<IShellFolder> pDesktop;
for (...) {
SHGetDesktopFolder(&pDesktop);
......
pDesktop.Release();
}

注意,这里是调用CComPtr的Release成员函数,而不是其保护的接口对象的Release函数。

另外一个解决方案就是使用<comdef.h>里的_com_ptr_t,这个类对于上述情况做了更加合理的处理。

1
2
3
4
5
6
Interface** operator&() throw()
{
_Release();
m_pInterface = NULL;
return &m_pInterface;
}

可以看到这个类里,重载&操作符的函数会先调用Release,然后再取地址避免了内存泄漏的问题。

Tips

给程序内部菜单增加指定的explorer菜单

为了将explorer的右键菜单项的某个菜单增加到我们程序内部的菜单,我们需要做以下几件事情:

  1. 获得指定文件的IShellFolder
  2. 获得指定文件的IContextMenu
  3. 创建菜单A,并且把IContextMenu的内容填充到菜单A
  4. 查询菜单A,找到我们想要的菜单项
  5. 取出我们想要的菜单项的内容,填充到我们想要真正弹出的菜单B
  6. 弹出菜单B
  7. 用IContextMenu响应用户对菜单的选择

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
CComPtr<IShellFolder> GetParentFolder(LPCWSTR szFolder)
{
CComPtr<IShellFolder> pDesktop;
SHGetDesktopFolder(&pDesktop);
if (NULL == pDesktop)
{
return NULL;
}

ULONG pchEaten = 0;
LPITEMIDLIST pidl = NULL;
DWORD dwAttributes = 0;
HRESULT hr = pDesktop->ParseDisplayName(NULL, NULL, (LPTSTR)szFolder, NULL, &pidl, NULL);
if (S_OK != hr)
{
return NULL;
}

CComPtr<IShellFolder> pParentFolder = NULL;
hr = pDesktop->BindToObject(pidl, NULL, IID_IShellFolder, (void**)&pParentFolder);
if (S_OK != hr)
{
CoTaskMemFree(pidl);
return NULL;
}

CoTaskMemFree(pidl);

return pParentFolder;
}

std::wstring GetDirectory(LPCWSTR szFile)
{
WCHAR szDrive[_MAX_DRIVE];
WCHAR szDir[_MAX_DIR];
WCHAR szFName[_MAX_FNAME];
WCHAR szExt[_MAX_EXT];
_wsplitpath_s(szFile, szDrive, szDir, szFName, szExt);

std::wstring strResult = szDrive;
strResult += szDir;
return strResult;
}

std::wstring GetFileNameWithExt(LPCWSTR szFile)
{
WCHAR szDrive[_MAX_DRIVE];
WCHAR szDir[_MAX_DIR];
WCHAR szFName[_MAX_FNAME];
WCHAR szExt[_MAX_EXT];
_wsplitpath_s(szFile, szDrive, szDir, szFName, szExt);

std::wstring strResult = szFName;
strResult += szExt;
return strResult;
}
void qtmenutest::ShowContextMenu(const QPoint &pos)
{
m_pContextMenu.Release();
QMenu contextMenu(tr("Context menu"), this);

std::wstring strFilePath = L"d:\\1.jpg";
CComPtr<IShellFolder> pParentFolder = GetParentFolder(GetDirectory(strFilePath.c_str()).c_str());
if (NULL == pParentFolder)
{
return;
}

std::wstring strFile = GetFileNameWithExt(strFilePath.c_str());
ULONG pchEaten = 0;
LPITEMIDLIST pidl = NULL;
DWORD dwAttributes = 0;
HRESULT hr = pParentFolder->ParseDisplayName(WId(), NULL, (LPWSTR)strFile.c_str(), &pchEaten, &pidl, &dwAttributes);
if (S_OK != hr)
{
return;
}


UINT refReversed = 0;
hr = pParentFolder->GetUIObjectOf(WId(), 1, (LPCITEMIDLIST *)&pidl, IID_IContextMenu, &refReversed, (void **)&m_pContextMenu);
if (S_OK != hr)
{
CoTaskMemFree(pidl);
return;
}

HMENU hMenu = CreatePopupMenu();
if (hMenu == NULL) {
CoTaskMemFree(pidl);
return;
}

m_pContextMenu->QueryContextMenu(hMenu, 0, 100, 200, CMF_EXPLORE | CMF_NORMAL);
int nMenuCount = GetMenuItemCount(hMenu);
HMENU hSubMenu = NULL;
WCHAR szMenuText[MAX_PATH];
for (int i = 0; i < nMenuCount; i++) {
GetMenuStringW(hMenu, i, szMenuText, MAX_PATH, MF_BYPOSITION);
if (wcsstr(szMenuText, L"some_ui_text")) {
hSubMenu = GetSubMenu(hMenu, i);
break;
}
}

QMenu *subMenu = contextMenu.addMenu(QString::fromWCharArray(szMenuText));
int nSubMenuCount = GetMenuItemCount(hSubMenu);
for (int i = 0; i < nSubMenuCount; i++) {
GetMenuStringW(hSubMenu, i, szMenuText, MAX_PATH, MF_BYPOSITION);
int nCmdId = GetMenuItemID(hSubMenu, i);
QAction *curAct = subMenu->addAction(QString::fromWCharArray(szMenuText));
if (curAct) {
curAct->setData(nCmdId);
}
}

QAction *selAct = contextMenu.exec(mapToGlobal(pos));
if (selAct) {
int nSelId = selAct->data().toInt();
if (nSelId) {
CMINVOKECOMMANDINFO info = {0};
info.cbSize = sizeof(CMINVOKECOMMANDINFOEX);
info.lpVerb = MAKEINTRESOURCEA(nSelId - 100);
m_pContextMenu->InvokeCommand(&info);
}
}

DestroyMenu(hMenu);
CoTaskMemFree(pidl);
return;
}

Tips

0cchext插件更新 1.0.19.1

最近把windbg插件0cchext升级到1.0.19.1,完善了autocmd命令,并且增加了accessmask,oledata和cppexcr命令。

autocmd更新:

现在支持全局自动运行命令,区分应用层调试和内核调试,并且区分了普通调试和DUMP分析,配置文件依然是插件同目录下的autocmd.ini。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[all]
? 88 * 66

[kernel]
!process 0 0 explorer.exe

[kernel dump]
!analyze -v

[notepad.exe]
.sympath+ c:\notepad_pdb
~*k

[calc.exe]
.sympath+ c:\calc_pdb
~*k

[calc.exe dump]
.excr

在[all]区间的命令,会在所有情况下执行;[kernel]区间的命令会在内核调试的情况下执行;[kernel dump]区间的命令会在内核调试dump的情况下执行;[app.exe]区间是在调试某exe的时候执行;最后[app.exe dump]命令会在调试指定exe的dump的时候执行。

accessmask命令:

这个命令很简单,就是查询权限标志的,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
0:000> !accessmask process 0x1fffff
Access mask: 0x1fffff

Generic rights:
STANDARD_RIGHTS_READ (0x20000)
STANDARD_RIGHTS_WRITE (0x20000)
STANDARD_RIGHTS_EXECUTE (0x20000)
STANDARD_RIGHTS_REQUIRED (0xf0000)
STANDARD_RIGHTS_ALL (0x1f0000)
READ_CONTROL (0x20000)
DELETE (0x10000)
SYNCHRONIZE (0x100000)
WRITE_DAC (0x40000)
WRITE_OWNER (0x80000)

Specific rights:
PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
PROCESS_SUSPEND_RESUME (0x800)
PROCESS_QUERY_INFORMATION (0x400)
PROCESS_SET_INFORMATION (0x200)
PROCESS_SET_QUOTA (0x100)
PROCESS_CREATE_PROCESS (0x80)
PROCESS_DUP_HANDLE (0x40)
PROCESS_VM_WRITE (0x20)
PROCESS_VM_READ (0x10)
PROCESS_VM_OPERATION (0x8)
PROCESS_CREATE_THREAD (0x2)
PROCESS_TERMINATE (0x1)
PROCESS_ALL_ACCESS (0x1fffff)

其中第一个参数是对象类型,比如process,thread,file;第二个参数则是具体要查询的值。

oledata命令:

这个命令是方便我们当前线程查询com和ole相关结构的命令,不需要参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
0:000> !oledata
dt combase!tagSOleTlsData 0x0000019370ad0360
dx (combase!tagSOleTlsData *)0x0000019370ad0360
0:000> dt combase!tagSOleTlsData 0x0000019370ad0360
+0x000 pvThreadBase : (null)
+0x008 pSmAllocator : (null)
+0x010 dwApartmentID : 0x1e3d4
+0x014 dwFlags : 0x81
+0x018 TlsMapIndex : 0n0
+0x020 ppTlsSlot : 0x00000018`66fc9758 -> 0x00000193`70ad0360 Void
+0x028 cComInits : 3
+0x02c cOleInits : 0
+0x030 cCalls : 0
...

另外可以看调试COM的一个tip来简单了解以下这个命令的用途

cppexcrname命令:

这个命令用于查询C++ 异常名

1
2
3
4
5
6
7
8
9
10
0:000> .exr -1
ExceptionAddress: 74e61812 (KERNELBASE!RaiseException+0x00000062)
ExceptionCode: e06d7363 (C++ EH exception)
ExceptionFlags: 00000001
NumberParameters: 3
Parameter[0]: 19930520
Parameter[1]: 006ff46c
Parameter[2]: 00372294
0:000> !cppexcrname
Exception name: [email protected]@@

这个的详情可以参考C++异常的参数分析(0xE06D7363)

Tips

不要使用GetFullPathName获得相对路径的全路径

上周末在网上闲逛,看到一篇介绍Path*相关API的文章,发现文章中推荐了GetFullPathName这个API,因为他能方便的获得相对路径对于当前工作目录的全路径。然而,我对于这个推荐API是坚决反对的。如果所有工程代码都是一个人开发,这种情况下调用这个API,我认为尚可理解,但是如果对于多人维护的大型工程,就不要使用它了。原因很简单,这个API是根据当前工作目录去确定绝对路径的,然而你无法确定当前工作目录是否是你认为的工作目录。即使在调用GetFullPathName之前,检查了这个路径,我认为也是不可信的。因为在检查过后,也有可能在其他线程中改变当前工作目录,要知道这个变量是全局唯一的。

所以,以下代码是不可取的:

1
2
3
4
5
WCHAR lpBuffer[MAX_PATH];
LPWSTR lpFname = NULL;

SetCurrentDirectoryW(L"C:\\some\\thing\\dir");
GetFullPathNameW(L"..\\other\\dir", MAX_PATH, lpBuffer, &lpFname);

其实微软为我们提供挺好的API来代替他,比如PathCanonicalize以及PathCombine(实际上有更安全的API,比如 PathCchCanonicalize和PathCchCombine,只不过需要高版本的系统)。

所以,以下代码是正确的:

1
2
3
4
WCHAR lpBuffer[MAX_PATH];
WCHAR lpSrc[MAX_PATH] = L"C:\\some\\thing\\dir";
wcscat_s(lpSrc, L"..\\other\\dir");
PathCanonicalizeW(lpBuffer, lpSrc);
1
2
WCHAR lpBuffer[MAX_PATH];
PathCombineW(lpBuffer, L"C:\\some\\thing\\dir", L"..\\other\\dir");

最后在介绍一个实用的函数:PathCompactPath,这个函数把路径缩写为指定的像素长度:

1
2
3
4
WCHAR buffer[MAX_PATH] = L"C:\\some\\thing\\very\\long\\long\\long\\long\\long\\path";
PathCompactPathW(NULL, buffer, 200);

// buffer="C:\some\thing\very...\path"

Tips

关于TokenLinkedToken的一点记录2

内核原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
0: kd> !process 0 1 explorer.exe
PROCESS ffffc306d0c34580
SessionId: 11 Cid: 44e0 Peb: 006fc000 ParentCid: 5758
DirBase: 2b1d00002 ObjectTable: ffff8b8e568c7040 HandleCount: 29648.
Image: explorer.exe
VadRoot ffffc306ddf5fca0 Vads 1625 Clone 0 Private 403154. Modified 771103. Locked 50.
DeviceMap ffff8b8e3147ed30
Token ffff8b8e4937f940
ElapsedTime 06:29:46.426
UserTime 00:00:48.921
KernelTime 00:00:53.250
QuotaPoolUsage[PagedPool] 2123416
QuotaPoolUsage[NonPagedPool] 225280
Working Set Sizes (now,min,max) (56547, 50, 345) (226188KB, 200KB, 1380KB)
PeakWorkingSetSize 462611
VirtualSize 2103798 Mb
PeakVirtualSize 2104556 Mb
PageFaultCount 2358568
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 426963

0: kd> !token ffff8b8e4937f940
_TOKEN 0xffff8b8e4937f940
TS Session ID: 0xb
User: S-1-5-21-3854333306-943506906-3328512208-1001
User Groups:
00 S-1-5-21-3854333306-943506906-3328512208-513
Attributes - Mandatory Default Enabled
01 S-1-1-0
Attributes - Mandatory Default Enabled
02 S-1-5-114
Attributes - DenyOnly
03 S-1-5-21-3854333306-943506906-3328512208-1002
Attributes - Mandatory Default Enabled
04 S-1-5-32-544
Attributes - DenyOnly
05 S-1-5-32-559
Attributes - Mandatory Default Enabled
06 S-1-5-32-545
Attributes - Mandatory Default Enabled
07 S-1-5-4
Attributes - Mandatory Default Enabled
08 S-1-2-1
Attributes - Mandatory Default Enabled
09 S-1-5-11
Attributes - Mandatory Default Enabled
10 S-1-5-15
Attributes - Mandatory Default Enabled
11 S-1-5-113
Attributes - Mandatory Default Enabled
12 S-1-5-5-0-4234506195
Attributes - Mandatory Default Enabled LogonId
13 S-1-2-0
Attributes - Mandatory Default Enabled
14 S-1-5-64-10
Attributes - Mandatory Default Enabled
15 S-1-16-8192
Attributes - GroupIntegrity GroupIntegrityEnabled
Primary Group: S-1-5-21-3854333306-943506906-3328512208-513
Privs:
19 0x000000013 SeShutdownPrivilege Attributes - Enabled
23 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default
25 0x000000019 SeUndockPrivilege Attributes -
33 0x000000021 SeIncreaseWorkingSetPrivilege Attributes -
34 0x000000022 SeTimeZonePrivilege Attributes -
Authentication ID: (0,fc65706c)
Impersonation Level: Anonymous
TokenType: Primary
Source: User32 TokenFlags: 0x2a00 ( Token in use )
Token ID: fc664685 ParentToken ID: fc65706f
Modified ID: (0, fe4096af)
RestrictedSidCount: 0 RestrictedSids: 0x0000000000000000
OriginatingLogonSession: 3e7
PackageSid: (null)
CapabilityCount: 0 Capabilities: 0x0000000000000000
LowboxNumberEntry: 0x0000000000000000
Security Attributes:


0: kd> dt _TOKEN 0xffff8b8e4937f940
nt!_TOKEN
+0x000 TokenSource : _TOKEN_SOURCE
+0x010 TokenId : _LUID
+0x018 AuthenticationId : _LUID
+0x020 ParentTokenId : _LUID
+0x028 ExpirationTime : _LARGE_INTEGER 0x7fffffff`ffffffff
+0x030 TokenLock : 0xffffc306`c7b5dd40 _ERESOURCE
+0x038 ModifiedId : _LUID
+0x040 Privileges : _SEP_TOKEN_PRIVILEGES
+0x058 AuditPolicy : _SEP_AUDIT_POLICY
+0x078 SessionId : 0xb
+0x07c UserAndGroupCount : 0x11
+0x080 RestrictedSidCount : 0
+0x084 VariableLength : 0x228
+0x088 DynamicCharged : 0x1000
+0x08c DynamicAvailable : 0
+0x090 DefaultOwnerIndex : 0
+0x098 UserAndGroups : 0xffff8b8e`4937fdd0 _SID_AND_ATTRIBUTES
+0x0a0 RestrictedSids : (null)
+0x0a8 PrimaryGroup : 0xffff8b8e`2b2a3b10 Void
+0x0b0 DynamicPart : 0xffff8b8e`2b2a3b10 -> 0x501
+0x0b8 DefaultDacl : 0xffff8b8e`2b2a3b2c _ACL
+0x0c0 TokenType : 1 ( TokenPrimary )
+0x0c4 ImpersonationLevel : 0 ( SecurityAnonymous )
+0x0c8 TokenFlags : 0x2a00
+0x0cc TokenInUse : 0x1 ''
+0x0d0 IntegrityLevelIndex : 0x10
+0x0d4 MandatoryPolicy : 3
+0x0d8 LogonSession : 0xffff8b8e`143fc870 _SEP_LOGON_SESSION_REFERENCES
+0x0e0 OriginatingLogonSession : _LUID
+0x0e8 SidHash : _SID_AND_ATTRIBUTES_HASH
+0x1f8 RestrictedSidHash : _SID_AND_ATTRIBUTES_HASH
+0x308 pSecurityAttributes : 0xffff8b8e`0c3b7f30 _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION
+0x310 Package : (null)
+0x318 Capabilities : (null)
+0x320 CapabilityCount : 0
+0x328 CapabilitiesHash : _SID_AND_ATTRIBUTES_HASH
+0x438 LowboxNumberEntry : (null)
+0x440 LowboxHandlesEntry : (null)
+0x448 pClaimAttributes : (null)
+0x450 TrustLevelSid : (null)
+0x458 TrustLinkedToken : (null)
+0x460 IntegrityLevelSidValue : (null)
+0x468 TokenSidValues : (null)
+0x470 IndexEntry : 0xffff8b8e`349cd270 _SEP_LUID_TO_INDEX_MAP_ENTRY
+0x478 DiagnosticInfo : (null)
+0x480 BnoIsolationHandlesEntry : (null)
+0x488 SessionObject : 0xffffc306`cd464140 Void
+0x490 VariablePart : 0xffff8b8e`4937fee0

0: kd> dt 0xffff8b8e`143fc870 _SEP_LOGON_SESSION_REFERENCES
nt!_SEP_LOGON_SESSION_REFERENCES
+0x000 Next : (null)
+0x008 LogonId : _LUID
+0x010 BuddyLogonId : _LUID
+0x018 ReferenceCount : 0n1799
+0x020 Flags : 0xd
+0x028 pDeviceMap : 0xffff8b8e`3147ed30 _DEVICE_MAP
+0x030 Token : 0xffff8b8e`20c65060 Void
+0x038 AccountName : _UNICODE_STRING "win"
+0x048 AuthorityName : _UNICODE_STRING "DESKTOP-GJGV2E2"
+0x058 CachedHandlesTable : _SEP_CACHED_HANDLES_TABLE
+0x068 SharedDataLock : _EX_PUSH_LOCK
+0x070 SharedClaimAttributes : (null)
+0x078 SharedSidValues : (null)
+0x080 RevocationBlock : _OB_HANDLE_REVOCATION_BLOCK
+0x0a0 ServerSilo : (null)
+0x0a8 SiblingAuthId : _LUID
+0x0b0 TokenList : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ]


0: kd> dt 0xffff8b8e`143fc870 _SEP_LOGON_SESSION_REFERENCES BuddyLogonId.
nt!_SEP_LOGON_SESSION_REFERENCES
+0x010 BuddyLogonId :
+0x000 LowPart : 0xfc657047
+0x004 HighPart : 0n0

0: kd> ? 0xfc657047&0xf
Evaluate expression: 7 = 00000000`00000007

0: kd> dq nt!SepLogonSessions L1
fffff802`45a744a0 ffff8b8e`0b0020d0
0: kd> dq ffff8b8e`0b0020d0+8*7 L1
ffff8b8e`0b002108 ffff8b8e`367ef010

0: kd> dt ffff8b8e`367ef010 _SEP_LOGON_SESSION_REFERENCES
nt!_SEP_LOGON_SESSION_REFERENCES
+0x000 Next : 0xffff8b8e`593ba230 _SEP_LOGON_SESSION_REFERENCES
+0x008 LogonId : _LUID
+0x010 BuddyLogonId : _LUID
+0x018 ReferenceCount : 0n56
+0x020 Flags : 0xa
+0x028 pDeviceMap : 0xffff8b8e`0e5e0890 _DEVICE_MAP
+0x030 Token : 0xffff8b8e`15b56940 Void
+0x038 AccountName : _UNICODE_STRING "win"
+0x048 AuthorityName : _UNICODE_STRING "DESKTOP-GJGV2E2"
+0x058 CachedHandlesTable : _SEP_CACHED_HANDLES_TABLE
+0x068 SharedDataLock : _EX_PUSH_LOCK
+0x070 SharedClaimAttributes : (null)
+0x078 SharedSidValues : (null)
+0x080 RevocationBlock : _OB_HANDLE_REVOCATION_BLOCK
+0x0a0 ServerSilo : (null)
+0x0a8 SiblingAuthId : _LUID
+0x0b0 TokenList : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ]

0: kd> !token 0xffff8b8e`15b56940
_TOKEN 0xffff8b8e15b56940
TS Session ID: 0xb
User: S-1-5-21-3854333306-943506906-3328512208-1001
User Groups:
00 S-1-5-21-3854333306-943506906-3328512208-513
Attributes - Mandatory Default Enabled
01 S-1-1-0
Attributes - Mandatory Default Enabled
02 S-1-5-114
Attributes - Mandatory Default Enabled
03 S-1-5-21-3854333306-943506906-3328512208-1002
Attributes - Mandatory Default Enabled
04 S-1-5-32-544
Attributes - Mandatory Default Enabled Owner
05 S-1-5-32-559
Attributes - Mandatory Default Enabled
06 S-1-5-32-545
Attributes - Mandatory Default Enabled
07 S-1-5-4
Attributes - Mandatory Default Enabled
08 S-1-2-1
Attributes - Mandatory Default Enabled
09 S-1-5-11
Attributes - Mandatory Default Enabled
10 S-1-5-15
Attributes - Mandatory Default Enabled
11 S-1-5-113
Attributes - Mandatory Default Enabled
12 S-1-5-5-0-4234506195
Attributes - Mandatory Default Enabled LogonId
13 S-1-2-0
Attributes - Mandatory Default Enabled
14 S-1-5-64-10
Attributes - Mandatory Default Enabled
15 S-1-16-12288
Attributes - GroupIntegrity GroupIntegrityEnabled
Primary Group: S-1-5-21-3854333306-943506906-3328512208-513
Privs:
05 0x000000005 SeIncreaseQuotaPrivilege Attributes -
08 0x000000008 SeSecurityPrivilege Attributes -
09 0x000000009 SeTakeOwnershipPrivilege Attributes -
10 0x00000000a SeLoadDriverPrivilege Attributes -
11 0x00000000b SeSystemProfilePrivilege Attributes -
12 0x00000000c SeSystemtimePrivilege Attributes -
13 0x00000000d SeProfileSingleProcessPrivilege Attributes -
14 0x00000000e SeIncreaseBasePriorityPrivilege Attributes -
15 0x00000000f SeCreatePagefilePrivilege Attributes -
17 0x000000011 SeBackupPrivilege Attributes -
18 0x000000012 SeRestorePrivilege Attributes -
19 0x000000013 SeShutdownPrivilege Attributes -
20 0x000000014 SeDebugPrivilege Attributes -
22 0x000000016 SeSystemEnvironmentPrivilege Attributes -
23 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default
24 0x000000018 SeRemoteShutdownPrivilege Attributes -
25 0x000000019 SeUndockPrivilege Attributes -
28 0x00000001c SeManageVolumePrivilege Attributes -
29 0x00000001d SeImpersonatePrivilege Attributes - Enabled Default
30 0x00000001e SeCreateGlobalPrivilege Attributes - Enabled Default
33 0x000000021 SeIncreaseWorkingSetPrivilege Attributes -
34 0x000000022 SeTimeZonePrivilege Attributes -
35 0x000000023 SeCreateSymbolicLinkPrivilege Attributes -
36 0x000000024 SeDelegateSessionUserImpersonatePrivilege Attributes -
Authentication ID: (0,fc657047)
Impersonation Level: Anonymous
TokenType: Primary
Source: User32 TokenFlags: 0x2020 ( Token NOT in use )
Token ID: fc657079 ParentToken ID: 0
Modified ID: (0, fc65706b)
RestrictedSidCount: 0 RestrictedSids: 0x0000000000000000
OriginatingLogonSession: 3e7
PackageSid: (null)
CapabilityCount: 0 Capabilities: 0x0000000000000000
LowboxNumberEntry: 0x0000000000000000
Security Attributes:

Tips

关于TokenLinkedToken的一点记录

我们用GetTokenInformation可以获得一个TokenLinkedToken,简单的说就是要获得与我们进程token关联的token。

接下来就有趣了,如果当你的进程是一个提权的管理员权限的进程,那么你获得的token会是一个标准用户进程的token,也就是一个提权之前进程。那么这有什么用呢?比如我们的子程序需要运行其他开发者开发的插件,而我们不想给予他们过高的权限,那么这个就有用了。当然,如果你更谨慎一些,你希望给予他更低的权限,那就得实用CreateRestrictedToken来创建一个新的token了。

聪明的程序员看到这里肯定就会想,既然管理员权限下的进程获得的TokenLinkedToken是一个标准用户权限的token,那么标准用户权限环境下的进程能不能获得一个管理员权限的TokenLinkedToken呢?没错,答案是可以。更聪明的程序员肯定会惊讶,那这个不是安全漏洞么?答案是并不是,因为虽然可以获得一个管理员权限的token,但是这个token只是一个IDENTIFY level token,这是一个token的_SECURITY_IMPERSONATION_LEVEL,不同的模仿等级,对应于不同的功能。比如SecurityIdentification,这个等级就只能用来查询token的信息。比方说有外部一个进程访问我们的进程,我们可以让他提供token验证其身份。但是外部进程为了防止我们用他的token干坏事,所以只给我们一个IDENTIFY level token,这样一来,我们就只能验证身份而无法做其他事情了。

我们真的没办法通过TokenLinkedToken获得可以使用的管理员身份的token了么?也不是,我们确实有办法获得能够使用的管理员身份的token。但是有个前提,我们的进程必须有SeTcbPrivilege权限。那这不也是个安全漏洞么?不是,因为SeTcbPrivilege是SYSTEM用户的权限,简单的说,这个用户的权限比管理员还要高。那这玩意不是也没什么用么?也有用,当你想在系统服务中启动一个管理员身份的进程的时候,可以先获得标准用户权限的token,然后获得其TokenLinkedToken,最后CreateProcessAsUser来创建进程。

TokenLinkedToken

Tips

tensorflow入门 —— mnist

自从有tensorflow这样的平台工具横空出世,机器学习的代码编写逐渐变的平民化了。我们不需要太多的数学理论知识就能够完成一些机器学习的项目。比如在使用tensorflow的时候,我们只需要定义好损失函数,tensorflow会自动的帮我们完成反向传播改善参数。我们所需要做的就是合理利用tensorflow创建模型。

当然,我并不是在鼓动初学者跳过数学原理部分,而是认为,如果没有数学基础,入门就死磕原理容易产生挫败感导致放弃。这样就不如先接触简单的项目,在有了一定的体会后,再回头去看看数学原理的东西,这样会更容易接受。比如,当能用tensorflow完成对mnist的训练之后,再去理解反向传播这四个公式,应该会更有感觉。

$$\begin{align} \delta^L &= \nabla_a C \odot \sigma'(z^L) \tag{1} \\ \delta^l &= ((w^{l+1})^T \delta^{l+1}) \odot \sigma'(z^l) \tag{2}\\ \frac{\partial C}{\partial b^l_j} &= \delta^l_j \tag{3}\\ \frac{\partial C}{\partial w^l_{jk}} &= a^{l-1}_k \delta^l_j \tag{4} \end{align}$$

接下来相信看看这份代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# -*- coding: utf-8 -*-
import tensorflow as tf
import matplotlib.pyplot as plt
from tensorflow.examples.tutorials.mnist import input_data

# step1
mnist = input_data.read_data_sets(".", one_hot=True)

# step2
INPUT_PARAMETERS = 784
L1_PARAMETERS = 300
W1 = tf.Variable(tf.truncated_normal([INPUT_PARAMETERS, L1_PARAMETERS], stddev=0.1))
b1 = tf.Variable(tf.truncated_normal([L1_PARAMETERS], stddev=0.1))
W2 = tf.Variable(tf.truncated_normal([L1_PARAMETERS, 10], stddev=0.1))
b2 = tf.Variable(tf.truncated_normal([10], stddev=0.1))

# step3
x = tf.placeholder(tf.float32, [None, INPUT_PARAMETERS])
y_ = tf.placeholder(tf.float32, [None, 10])

# step4
hidden1 = tf.nn.sigmoid(tf.matmul(x, W1) + b1)
y = tf.matmul(hidden1, W2) + b2

# step5
loss = (tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
logits=y, labels=y_)))
train_step = tf.train.GradientDescentOptimizer(0.3).minimize(loss)

# step6
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

# step7
loss_array = []
accuracy_array = []
with tf.Session() as sess:
tf.global_variables_initializer().run()
for i in range(10000):
batch_xs, batch_ys = mnist.train.next_batch(100)
_, step_loss = sess.run([train_step, loss], feed_dict={x: batch_xs, y_: batch_ys})
if i % 500 == 0:
step_accuracy = accuracy.eval({x: mnist.test.images, y_: mnist.test.labels})
loss_array.append(step_loss)
accuracy_array.append(step_accuracy)
print(step_loss, step_accuracy)

loss_array.append(step_loss)
accuracy_array.append(step_accuracy)
print(step_loss, step_accuracy)

plt.plot([i*500 for i in range(len(loss_array))], loss_array, 'b-',
[i*500 for i in range(len(accuracy_array))], accuracy_array, 'r-')

step1:read_data_sets是tf提供读取mnist数据的函数,运行时会有函数过时的警告提示,但是不用管它,因为用起来完全没有问题。one_hot参数在这里很重要,他将实际的标签值转化为一个向量,例如,将标签值3,转化成表示0-9是否置位的向量:\( \left(\begin{array}{cccccccccc}0;0;0;1;0;0;0;0;0;0;\end{array}\right)^T \)

step2:创建权重和偏置,创建方法有很多,我用的是期望为0,标准差为0.1的随机分布。

step3:创建输入层和标签的placeholder,大概的意思是占住空间,以方便后续数据喂给模型。

step4:实际上是向前传播 \( z = w^{T}x\,+\,b\),\( a = sigmoid(z)\),其中sigmoid是\( \frac{1}{1+\exp(-z)}\)。这里可能有一个疑问,第二个隐藏层到输出层,没有使用sigmoid函数。原因继续往下看就知道了。

step5:计算损失函数,调用了tf的sigmoid_cross_entropy_with_logits,这个函数把交叉熵和sigmoid的计算放在了一起,所以上面不需要去计算sigmoid了。这样做的好处很明显,就是方便我们修改从最后一个隐藏层到输出层的激活函数,比如将sigmoid_cross_entropy_with_logits替换为softmax_cross_entropy_with_logits,那么我们最后一个激活函数就变成了softmax。最后设置学习率并且把我们的损失函数传给梯度下降类的最小化函数中,tf就会自动的帮我们优化参数,从而最小化损失值了。

step6:使用测试数据去计算模型的识别准确率

step7:最后一步,我们将数据分为小份,随着迭代,逐步喂给模型。然后记录损失和准确率的变化,并做图。

这里我们为了简单,没有使用softmax,dropout和正则化等优化方法,所以识别率只达到了90%,不过作为一个入门来说已经够了。

2018-07-07-tensorflow-mnist

machinelearning

机器学习练习题1

掐指一算,已经有三个月没有更新blog了。因为最近一直在学习机器学习的内容,所以没空也没有新鲜的技术值得写下来分享。还好经过一段时间的积累(学习线性代数和概率论),机器学习这块内容也算是入门了。这是机器学习的第一个习题,线性回归。用最直白的话来说,就是用函数去拟合数据分布,从而达到预测新数据的效果。需要的知识是矩阵的计算,最小二乘法以及求偏微分。

关键的公式只有两个:
$$\begin{align} Cost(\theta) = \frac{1}{2m}\,\sum^{m}_{i=1}\,(h_\theta(x^{i})-y^{i})^2 \tag{1}\\ \theta_{j} = \theta_{j}-\alpha\frac{1}{m}\,\sum^{m}_{i=1}(h_\theta(x^{i})-y^{i})\frac{\partial{h_\theta(x^{i})}}{\partial{\theta_{j}}}\tag{2} \end{align}$$

那么最后需要确定的只剩下一个,我们希望用什么样的曲线去拟合样本,这个就需要经验和尝试了。这里的练习只需要用直线来拟合就足够了: \( h_\theta(x) = \theta^{T}x = \theta_{0}+\theta_{1}x_{1} \) .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt

data = np.loadtxt('ex1data1.txt', delimiter=',');

# 分离样本数据输入X和输出Y
X = np.concatenate((np.ones((len(data),1)), data[:,0].reshape((len(data),1))), axis=1)
theta = np.random.randn(2,1)
Y = data[:,1].reshape((len(data),1))

# 为了加速计算,把除法优化位乘法
alpha = 0.01
div_m = 1 / len(data);

for loop_count in range(1000):
Y1 = X.dot(theta)
# 根据公式计算损失
cost = ((Y-Y1)**2).sum() * 0.5 * div_m;
print(cost)
# 更新参数
for i in range(2):
theta[i, 0] = theta[i, 0] - alpha * div_m * (np.diagflat(X[:, i]) * (Y1 - Y)).sum()

# 最后把拟合直线画出来
Xl = np.linspace(0, 30, 100)
Yl = Xl * theta[1, 0] + theta[0, 0]

plt.plot(data[:,0],data[:,1],'x', Xl, Yl, 'r')

2018-06-23-machine-learning-ex1

machinelearning