diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/.nojekyll @@ -0,0 +1 @@ + diff --git a/404.html b/404.html new file mode 100644 index 00000000..df57ce76 --- /dev/null +++ b/404.html @@ -0,0 +1 @@ + 404: Page not found | Rayanfam Blog
Home 404: Page not found
404: Page not found
Cancel
diff --git a/CNAME b/CNAME new file mode 100644 index 00000000..c0c1e182 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +rayanfam.com \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 00000000..b2ab751c --- /dev/null +++ b/app.js @@ -0,0 +1 @@ +/* Registering Service Worker */ if('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js'); }; diff --git a/archives/index.html b/archives/index.html new file mode 100644 index 00000000..94568e90 --- /dev/null +++ b/archives/index.html @@ -0,0 +1 @@ + Archives | Rayanfam Blog
Home Archives
Archives
Cancel

Archives

202320222020201920182017
diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 00000000..70184888 --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1,9 @@ +/*! + * The styles for Jekyll theme Chirpy + * + * Chirpy v5.1.0 (https://github.com/cotes2020/jekyll-theme-chirpy) + * © 2019 Cotes Chung + * MIT Licensed + */#search-results a,h5,h4,h3,h2,h1{color:var(--heading-color);font-weight:400;font-family:'Lato', 'Microsoft Yahei', sans-serif}#core-wrapper h5,#core-wrapper h4,#core-wrapper h3,#core-wrapper h2{margin-top:2.5rem;margin-bottom:1.25rem}#core-wrapper h5:focus,#core-wrapper h4:focus,#core-wrapper h3:focus,#core-wrapper h2:focus{outline:none}h5 .anchor,h4 .anchor,h3 .anchor,h2 .anchor{font-size:80%}@media (hover: hover){h5 .anchor,h4 .anchor,h3 .anchor,h2 .anchor{visibility:hidden;opacity:0;transition:opacity 0.25s ease-in, visibility 0s ease-in 0.25s}h5:hover .anchor,h4:hover .anchor,h3:hover .anchor,h2:hover .anchor{visibility:visible;opacity:1;transition:opacity 0.25s ease-in, visibility 0s ease-in 0s}}.post-tag:hover,.tag:hover{background:var(--tag-hover);transition:background 0.35s ease-in-out}.table-wrapper>table tbody tr td,.table-wrapper>table thead th{padding:0.4rem 1rem;font-size:95%;white-space:nowrap}#page-category a:hover,#page-tag a:hover,.license-wrapper>a:hover,#post-list .post-preview a:hover,#search-results a:hover,#topbar #breadcrumb a:hover,.post-content a:not(.img-link):hover,.post-meta a:hover,.post a:hover code,#access-lastmod a:hover,footer a:hover{color:#d2603a !important;border-bottom:1px solid #d2603a;text-decoration:none}#search-results a,#search-hints .post-tag,a{color:var(--link-color)}.post-tail-wrapper .post-meta a:not(:hover),.post-content a:not(.img-link){border-bottom:1px solid var(--link-underline-color)}#sidebar .sidebar-bottom .mode-toggle>i,#sidebar .sidebar-bottom a,#sidebar .nav-item:not(.active)>a,#sidebar .site-title a{transition:color 0.35s ease-in-out}#sidebar .sidebar-bottom .icon-border,.post a.img-link,i.far,i.fas,.code-header{user-select:none}#page-category ul>li>a,#page-tag ul>li>a,#core-wrapper .categories a:not(:hover),#core-wrapper #tags a:not(:hover),#core-wrapper #archives a:not(:hover),#search-results a,#access-lastmod a{border-bottom:none}.share-wrapper .share-icons>i,#search-cancel,.code-header button{cursor:pointer}#related-posts em,#post-list .post-preview .post-meta em,.post-meta em{font-style:normal}.post-content a.img-link+em,img[data-src]+em{display:block;text-align:center;font-style:normal;font-size:80%;padding:0;color:#6d6c6c}#sidebar .sidebar-bottom .mode-toggle,#sidebar a{color:rgba(117,117,117,0.9);user-select:none}@media (prefers-color-scheme: light){html:not([data-mode]),html [data-mode=light]{--highlight-bg-color: #f7f7f7;--highlighter-rouge-color: #2f2f2f;--highlight-lineno-color: #c2c6cc;--inline-code-bg: #f3f3f3;--code-header-text-color: #a3a3b1;--code-header-muted-color: #ebebeb;--code-header-icon-color: #d1d1d1;--clipboard-checked-color: #43c743}html:not([data-mode]) .highlight .hll,html [data-mode=light] .highlight .hll{background-color:#ffffcc}html:not([data-mode]) .highlight .c,html [data-mode=light] .highlight .c{color:#999988;font-style:italic}html:not([data-mode]) .highlight .err,html [data-mode=light] .highlight .err{color:#a61717;background-color:#e3d2d2}html:not([data-mode]) .highlight .k,html [data-mode=light] .highlight .k{color:#000000;font-weight:bold}html:not([data-mode]) .highlight .o,html [data-mode=light] .highlight .o{color:#000000;font-weight:bold}html:not([data-mode]) .highlight .cm,html [data-mode=light] .highlight .cm{color:#999988;font-style:italic}html:not([data-mode]) .highlight .cp,html [data-mode=light] .highlight .cp{color:#999999;font-weight:bold;font-style:italic}html:not([data-mode]) .highlight .c1,html [data-mode=light] .highlight .c1{color:#999988;font-style:italic}html:not([data-mode]) .highlight .cs,html [data-mode=light] .highlight .cs{color:#999999;font-weight:bold;font-style:italic}html:not([data-mode]) .highlight .gd,html [data-mode=light] .highlight .gd{color:#d01040;background-color:#ffdddd}html:not([data-mode]) .highlight .ge,html [data-mode=light] .highlight .ge{color:#000000;font-style:italic}html:not([data-mode]) .highlight .gr,html [data-mode=light] .highlight .gr{color:#aa0000}html:not([data-mode]) .highlight .gh,html [data-mode=light] .highlight .gh{color:#999999}html:not([data-mode]) .highlight .gi,html [data-mode=light] .highlight .gi{color:#008080;background-color:#ddffdd}html:not([data-mode]) .highlight .go,html [data-mode=light] .highlight .go{color:#888888}html:not([data-mode]) .highlight .gp,html [data-mode=light] .highlight .gp{color:#555555}html:not([data-mode]) .highlight .gs,html [data-mode=light] .highlight .gs{font-weight:bold}html:not([data-mode]) .highlight .gu,html [data-mode=light] .highlight .gu{color:#aaaaaa}html:not([data-mode]) .highlight .gt,html [data-mode=light] .highlight .gt{color:#aa0000}html:not([data-mode]) .highlight .kc,html [data-mode=light] .highlight .kc{color:#000000;font-weight:bold}html:not([data-mode]) .highlight .kd,html [data-mode=light] .highlight .kd{color:#000000;font-weight:bold}html:not([data-mode]) .highlight .kn,html [data-mode=light] .highlight .kn{color:#000000;font-weight:bold}html:not([data-mode]) .highlight .kp,html [data-mode=light] .highlight .kp{color:#000000;font-weight:bold}html:not([data-mode]) .highlight .kr,html [data-mode=light] .highlight .kr{color:#000000;font-weight:bold}html:not([data-mode]) .highlight .kt,html [data-mode=light] .highlight .kt{color:#445588;font-weight:bold}html:not([data-mode]) .highlight .m,html [data-mode=light] .highlight .m{color:#009999}html:not([data-mode]) .highlight .s,html [data-mode=light] .highlight .s{color:#d01040}html:not([data-mode]) .highlight .na,html [data-mode=light] .highlight .na{color:#008080}html:not([data-mode]) .highlight .nb,html [data-mode=light] .highlight .nb{color:#0086b3}html:not([data-mode]) .highlight .nc,html [data-mode=light] .highlight .nc{color:#445588;font-weight:bold}html:not([data-mode]) .highlight .no,html [data-mode=light] .highlight .no{color:#008080}html:not([data-mode]) .highlight .nd,html [data-mode=light] .highlight .nd{color:#3c5d5d;font-weight:bold}html:not([data-mode]) .highlight .ni,html [data-mode=light] .highlight .ni{color:#800080}html:not([data-mode]) .highlight .ne,html [data-mode=light] .highlight .ne{color:#990000;font-weight:bold}html:not([data-mode]) .highlight .nf,html [data-mode=light] .highlight .nf{color:#990000;font-weight:bold}html:not([data-mode]) .highlight .nl,html [data-mode=light] .highlight .nl{color:#990000;font-weight:bold}html:not([data-mode]) .highlight .nn,html [data-mode=light] .highlight .nn{color:#555555}html:not([data-mode]) .highlight .nt,html [data-mode=light] .highlight .nt{color:#000080}html:not([data-mode]) .highlight .nv,html [data-mode=light] .highlight .nv{color:#008080}html:not([data-mode]) .highlight .ow,html [data-mode=light] .highlight .ow{color:#000000;font-weight:bold}html:not([data-mode]) .highlight .w,html [data-mode=light] .highlight .w{color:#bbbbbb}html:not([data-mode]) .highlight .mf,html [data-mode=light] .highlight .mf{color:#009999}html:not([data-mode]) .highlight .mh,html [data-mode=light] .highlight .mh{color:#009999}html:not([data-mode]) .highlight .mi,html [data-mode=light] .highlight .mi{color:#009999}html:not([data-mode]) .highlight .mo,html [data-mode=light] .highlight .mo{color:#009999}html:not([data-mode]) .highlight .sb,html [data-mode=light] .highlight .sb{color:#d01040}html:not([data-mode]) .highlight .sc,html [data-mode=light] .highlight .sc{color:#d01040}html:not([data-mode]) .highlight .sd,html [data-mode=light] .highlight .sd{color:#d01040}html:not([data-mode]) .highlight .s2,html [data-mode=light] .highlight .s2{color:#d01040}html:not([data-mode]) .highlight .se,html [data-mode=light] .highlight .se{color:#d01040}html:not([data-mode]) .highlight .sh,html [data-mode=light] .highlight .sh{color:#d01040}html:not([data-mode]) .highlight .si,html [data-mode=light] .highlight .si{color:#d01040}html:not([data-mode]) .highlight .sx,html [data-mode=light] .highlight .sx{color:#d01040}html:not([data-mode]) .highlight .sr,html [data-mode=light] .highlight .sr{color:#009926}html:not([data-mode]) .highlight .s1,html [data-mode=light] .highlight .s1{color:#d01040}html:not([data-mode]) .highlight .ss,html [data-mode=light] .highlight .ss{color:#990073}html:not([data-mode]) .highlight .bp,html [data-mode=light] .highlight .bp{color:#999999}html:not([data-mode]) .highlight .vc,html [data-mode=light] .highlight .vc{color:#008080}html:not([data-mode]) .highlight .vg,html [data-mode=light] .highlight .vg{color:#008080}html:not([data-mode]) .highlight .vi,html [data-mode=light] .highlight .vi{color:#008080}html:not([data-mode]) .highlight .il,html [data-mode=light] .highlight .il{color:#009999}html:not([data-mode]) [class^=prompt-],html [data-mode=light] [class^=prompt-]{--inline-code-bg: #fbfafa;--highlighter-rouge-color: rgb(82 82 82)}html[data-mode=dark]{--highlight-bg-color: #252525;--highlighter-rouge-color: #de6b18;--highlight-lineno-color: #6c6c6d;--inline-code-bg: #272822;--code-header-text-color: #6a6a6a;--code-header-muted-color: rgb(60 60 60);--code-header-icon-color: rgb(86 86 86);--clipboard-checked-color: #2bcc2b;--filepath-text-color: #bdbdbd}html[data-mode=dark] .highlight pre{background-color:var(--highlight-bg-color)}html[data-mode=dark] .highlight .hll{background-color:var(--highlight-bg-color)}html[data-mode=dark] .highlight .c{color:#75715e}html[data-mode=dark] .highlight .err{color:#960050;background-color:#1e0010}html[data-mode=dark] .highlight .k{color:#66d9ef}html[data-mode=dark] .highlight .l{color:#ae81ff}html[data-mode=dark] .highlight .n{color:#f8f8f2}html[data-mode=dark] .highlight .o{color:#f92672}html[data-mode=dark] .highlight .p{color:#f8f8f2}html[data-mode=dark] .highlight .cm{color:#75715e}html[data-mode=dark] .highlight .cp{color:#75715e}html[data-mode=dark] .highlight .c1{color:#75715e}html[data-mode=dark] .highlight .cs{color:#75715e}html[data-mode=dark] .highlight .ge{color:inherit;font-style:italic}html[data-mode=dark] .highlight .gs{font-weight:bold}html[data-mode=dark] .highlight .kc{color:#66d9ef}html[data-mode=dark] .highlight .kd{color:#66d9ef}html[data-mode=dark] .highlight .kn{color:#f92672}html[data-mode=dark] .highlight .kp{color:#66d9ef}html[data-mode=dark] .highlight .kr{color:#66d9ef}html[data-mode=dark] .highlight .kt{color:#66d9ef}html[data-mode=dark] .highlight .ld{color:#e6db74}html[data-mode=dark] .highlight .m{color:#ae81ff}html[data-mode=dark] .highlight .s{color:#e6db74}html[data-mode=dark] .highlight .na{color:#a6e22e}html[data-mode=dark] .highlight .nb{color:#f8f8f2}html[data-mode=dark] .highlight .nc{color:#a6e22e}html[data-mode=dark] .highlight .no{color:#66d9ef}html[data-mode=dark] .highlight .nd{color:#a6e22e}html[data-mode=dark] .highlight .ni{color:#f8f8f2}html[data-mode=dark] .highlight .ne{color:#a6e22e}html[data-mode=dark] .highlight .nf{color:#a6e22e}html[data-mode=dark] .highlight .nl{color:#f8f8f2}html[data-mode=dark] .highlight .nn{color:#f8f8f2}html[data-mode=dark] .highlight .nx{color:#a6e22e}html[data-mode=dark] .highlight .py{color:#f8f8f2}html[data-mode=dark] .highlight .nt{color:#f92672}html[data-mode=dark] .highlight .nv{color:#f8f8f2}html[data-mode=dark] .highlight .ow{color:#f92672}html[data-mode=dark] .highlight .w{color:#f8f8f2}html[data-mode=dark] .highlight .mf{color:#ae81ff}html[data-mode=dark] .highlight .mh{color:#ae81ff}html[data-mode=dark] .highlight .mi{color:#ae81ff}html[data-mode=dark] .highlight .mo{color:#ae81ff}html[data-mode=dark] .highlight .sb{color:#e6db74}html[data-mode=dark] .highlight .sc{color:#e6db74}html[data-mode=dark] .highlight .sd{color:#e6db74}html[data-mode=dark] .highlight .s2{color:#e6db74}html[data-mode=dark] .highlight .se{color:#ae81ff}html[data-mode=dark] .highlight .sh{color:#e6db74}html[data-mode=dark] .highlight .si{color:#e6db74}html[data-mode=dark] .highlight .sx{color:#e6db74}html[data-mode=dark] .highlight .sr{color:#e6db74}html[data-mode=dark] .highlight .s1{color:#e6db74}html[data-mode=dark] .highlight .ss{color:#e6db74}html[data-mode=dark] .highlight .bp{color:#f8f8f2}html[data-mode=dark] .highlight .vc{color:#f8f8f2}html[data-mode=dark] .highlight .vg{color:#f8f8f2}html[data-mode=dark] .highlight .vi{color:#f8f8f2}html[data-mode=dark] .highlight .il{color:#ae81ff}html[data-mode=dark] .highlight .gu{color:#75715e}html[data-mode=dark] .highlight .gd{color:#f92672;background-color:#561c08}html[data-mode=dark] .highlight .gi{color:#a6e22e;background-color:#0b5858}html[data-mode=dark] .highlight .gp{color:#818c96}html[data-mode=dark] pre{color:#bfbfbf}}@media (prefers-color-scheme: dark){html:not([data-mode]),html[data-mode=dark]{--highlight-bg-color: #252525;--highlighter-rouge-color: #de6b18;--highlight-lineno-color: #6c6c6d;--inline-code-bg: #272822;--code-header-text-color: #6a6a6a;--code-header-muted-color: rgb(60 60 60);--code-header-icon-color: rgb(86 86 86);--clipboard-checked-color: #2bcc2b;--filepath-text-color: #bdbdbd}html:not([data-mode]) .highlight pre,html[data-mode=dark] .highlight pre{background-color:var(--highlight-bg-color)}html:not([data-mode]) .highlight .hll,html[data-mode=dark] .highlight .hll{background-color:var(--highlight-bg-color)}html:not([data-mode]) .highlight .c,html[data-mode=dark] .highlight .c{color:#75715e}html:not([data-mode]) .highlight .err,html[data-mode=dark] .highlight .err{color:#960050;background-color:#1e0010}html:not([data-mode]) .highlight .k,html[data-mode=dark] .highlight .k{color:#66d9ef}html:not([data-mode]) .highlight .l,html[data-mode=dark] .highlight .l{color:#ae81ff}html:not([data-mode]) .highlight .n,html[data-mode=dark] .highlight .n{color:#f8f8f2}html:not([data-mode]) .highlight .o,html[data-mode=dark] .highlight .o{color:#f92672}html:not([data-mode]) .highlight .p,html[data-mode=dark] .highlight .p{color:#f8f8f2}html:not([data-mode]) .highlight .cm,html[data-mode=dark] .highlight .cm{color:#75715e}html:not([data-mode]) .highlight .cp,html[data-mode=dark] .highlight .cp{color:#75715e}html:not([data-mode]) .highlight .c1,html[data-mode=dark] .highlight .c1{color:#75715e}html:not([data-mode]) .highlight .cs,html[data-mode=dark] .highlight .cs{color:#75715e}html:not([data-mode]) .highlight .ge,html[data-mode=dark] .highlight .ge{color:inherit;font-style:italic}html:not([data-mode]) .highlight .gs,html[data-mode=dark] .highlight .gs{font-weight:bold}html:not([data-mode]) .highlight .kc,html[data-mode=dark] .highlight .kc{color:#66d9ef}html:not([data-mode]) .highlight .kd,html[data-mode=dark] .highlight .kd{color:#66d9ef}html:not([data-mode]) .highlight .kn,html[data-mode=dark] .highlight .kn{color:#f92672}html:not([data-mode]) .highlight .kp,html[data-mode=dark] .highlight .kp{color:#66d9ef}html:not([data-mode]) .highlight .kr,html[data-mode=dark] .highlight .kr{color:#66d9ef}html:not([data-mode]) .highlight .kt,html[data-mode=dark] .highlight .kt{color:#66d9ef}html:not([data-mode]) .highlight .ld,html[data-mode=dark] .highlight .ld{color:#e6db74}html:not([data-mode]) .highlight .m,html[data-mode=dark] .highlight .m{color:#ae81ff}html:not([data-mode]) .highlight .s,html[data-mode=dark] .highlight .s{color:#e6db74}html:not([data-mode]) .highlight .na,html[data-mode=dark] .highlight .na{color:#a6e22e}html:not([data-mode]) .highlight .nb,html[data-mode=dark] .highlight .nb{color:#f8f8f2}html:not([data-mode]) .highlight .nc,html[data-mode=dark] .highlight .nc{color:#a6e22e}html:not([data-mode]) .highlight .no,html[data-mode=dark] .highlight .no{color:#66d9ef}html:not([data-mode]) .highlight .nd,html[data-mode=dark] .highlight .nd{color:#a6e22e}html:not([data-mode]) .highlight .ni,html[data-mode=dark] .highlight .ni{color:#f8f8f2}html:not([data-mode]) .highlight .ne,html[data-mode=dark] .highlight .ne{color:#a6e22e}html:not([data-mode]) .highlight .nf,html[data-mode=dark] .highlight .nf{color:#a6e22e}html:not([data-mode]) .highlight .nl,html[data-mode=dark] .highlight .nl{color:#f8f8f2}html:not([data-mode]) .highlight .nn,html[data-mode=dark] .highlight .nn{color:#f8f8f2}html:not([data-mode]) .highlight .nx,html[data-mode=dark] .highlight .nx{color:#a6e22e}html:not([data-mode]) .highlight .py,html[data-mode=dark] .highlight .py{color:#f8f8f2}html:not([data-mode]) .highlight .nt,html[data-mode=dark] .highlight .nt{color:#f92672}html:not([data-mode]) .highlight .nv,html[data-mode=dark] .highlight .nv{color:#f8f8f2}html:not([data-mode]) .highlight .ow,html[data-mode=dark] .highlight .ow{color:#f92672}html:not([data-mode]) .highlight .w,html[data-mode=dark] .highlight .w{color:#f8f8f2}html:not([data-mode]) .highlight .mf,html[data-mode=dark] .highlight .mf{color:#ae81ff}html:not([data-mode]) .highlight .mh,html[data-mode=dark] .highlight .mh{color:#ae81ff}html:not([data-mode]) .highlight .mi,html[data-mode=dark] .highlight .mi{color:#ae81ff}html:not([data-mode]) .highlight .mo,html[data-mode=dark] .highlight .mo{color:#ae81ff}html:not([data-mode]) .highlight .sb,html[data-mode=dark] .highlight .sb{color:#e6db74}html:not([data-mode]) .highlight .sc,html[data-mode=dark] .highlight .sc{color:#e6db74}html:not([data-mode]) .highlight .sd,html[data-mode=dark] .highlight .sd{color:#e6db74}html:not([data-mode]) .highlight .s2,html[data-mode=dark] .highlight .s2{color:#e6db74}html:not([data-mode]) .highlight .se,html[data-mode=dark] .highlight .se{color:#ae81ff}html:not([data-mode]) .highlight .sh,html[data-mode=dark] .highlight .sh{color:#e6db74}html:not([data-mode]) .highlight .si,html[data-mode=dark] .highlight .si{color:#e6db74}html:not([data-mode]) .highlight .sx,html[data-mode=dark] .highlight .sx{color:#e6db74}html:not([data-mode]) .highlight .sr,html[data-mode=dark] .highlight .sr{color:#e6db74}html:not([data-mode]) .highlight .s1,html[data-mode=dark] .highlight .s1{color:#e6db74}html:not([data-mode]) .highlight .ss,html[data-mode=dark] .highlight .ss{color:#e6db74}html:not([data-mode]) .highlight .bp,html[data-mode=dark] .highlight .bp{color:#f8f8f2}html:not([data-mode]) .highlight .vc,html[data-mode=dark] .highlight .vc{color:#f8f8f2}html:not([data-mode]) .highlight .vg,html[data-mode=dark] .highlight .vg{color:#f8f8f2}html:not([data-mode]) .highlight .vi,html[data-mode=dark] .highlight .vi{color:#f8f8f2}html:not([data-mode]) .highlight .il,html[data-mode=dark] .highlight .il{color:#ae81ff}html:not([data-mode]) .highlight .gu,html[data-mode=dark] .highlight .gu{color:#75715e}html:not([data-mode]) .highlight .gd,html[data-mode=dark] .highlight .gd{color:#f92672;background-color:#561c08}html:not([data-mode]) .highlight .gi,html[data-mode=dark] .highlight .gi{color:#a6e22e;background-color:#0b5858}html:not([data-mode]) .highlight .gp,html[data-mode=dark] .highlight .gp{color:#818c96}html:not([data-mode]) pre,html[data-mode=dark] pre{color:#bfbfbf}html[data-mode=light]{--highlight-bg-color: #f7f7f7;--highlighter-rouge-color: #2f2f2f;--highlight-lineno-color: #c2c6cc;--inline-code-bg: #f3f3f3;--code-header-text-color: #a3a3b1;--code-header-muted-color: #ebebeb;--code-header-icon-color: #d1d1d1;--clipboard-checked-color: #43c743}html[data-mode=light] .highlight .hll{background-color:#ffffcc}html[data-mode=light] .highlight .c{color:#999988;font-style:italic}html[data-mode=light] .highlight .err{color:#a61717;background-color:#e3d2d2}html[data-mode=light] .highlight .k{color:#000000;font-weight:bold}html[data-mode=light] .highlight .o{color:#000000;font-weight:bold}html[data-mode=light] .highlight .cm{color:#999988;font-style:italic}html[data-mode=light] .highlight .cp{color:#999999;font-weight:bold;font-style:italic}html[data-mode=light] .highlight .c1{color:#999988;font-style:italic}html[data-mode=light] .highlight .cs{color:#999999;font-weight:bold;font-style:italic}html[data-mode=light] .highlight .gd{color:#d01040;background-color:#ffdddd}html[data-mode=light] .highlight .ge{color:#000000;font-style:italic}html[data-mode=light] .highlight .gr{color:#aa0000}html[data-mode=light] .highlight .gh{color:#999999}html[data-mode=light] .highlight .gi{color:#008080;background-color:#ddffdd}html[data-mode=light] .highlight .go{color:#888888}html[data-mode=light] .highlight .gp{color:#555555}html[data-mode=light] .highlight .gs{font-weight:bold}html[data-mode=light] .highlight .gu{color:#aaaaaa}html[data-mode=light] .highlight .gt{color:#aa0000}html[data-mode=light] .highlight .kc{color:#000000;font-weight:bold}html[data-mode=light] .highlight .kd{color:#000000;font-weight:bold}html[data-mode=light] .highlight .kn{color:#000000;font-weight:bold}html[data-mode=light] .highlight .kp{color:#000000;font-weight:bold}html[data-mode=light] .highlight .kr{color:#000000;font-weight:bold}html[data-mode=light] .highlight .kt{color:#445588;font-weight:bold}html[data-mode=light] .highlight .m{color:#009999}html[data-mode=light] .highlight .s{color:#d01040}html[data-mode=light] .highlight .na{color:#008080}html[data-mode=light] .highlight .nb{color:#0086b3}html[data-mode=light] .highlight .nc{color:#445588;font-weight:bold}html[data-mode=light] .highlight .no{color:#008080}html[data-mode=light] .highlight .nd{color:#3c5d5d;font-weight:bold}html[data-mode=light] .highlight .ni{color:#800080}html[data-mode=light] .highlight .ne{color:#990000;font-weight:bold}html[data-mode=light] .highlight .nf{color:#990000;font-weight:bold}html[data-mode=light] .highlight .nl{color:#990000;font-weight:bold}html[data-mode=light] .highlight .nn{color:#555555}html[data-mode=light] .highlight .nt{color:#000080}html[data-mode=light] .highlight .nv{color:#008080}html[data-mode=light] .highlight .ow{color:#000000;font-weight:bold}html[data-mode=light] .highlight .w{color:#bbbbbb}html[data-mode=light] .highlight .mf{color:#009999}html[data-mode=light] .highlight .mh{color:#009999}html[data-mode=light] .highlight .mi{color:#009999}html[data-mode=light] .highlight .mo{color:#009999}html[data-mode=light] .highlight .sb{color:#d01040}html[data-mode=light] .highlight .sc{color:#d01040}html[data-mode=light] .highlight .sd{color:#d01040}html[data-mode=light] .highlight .s2{color:#d01040}html[data-mode=light] .highlight .se{color:#d01040}html[data-mode=light] .highlight .sh{color:#d01040}html[data-mode=light] .highlight .si{color:#d01040}html[data-mode=light] .highlight .sx{color:#d01040}html[data-mode=light] .highlight .sr{color:#009926}html[data-mode=light] .highlight .s1{color:#d01040}html[data-mode=light] .highlight .ss{color:#990073}html[data-mode=light] .highlight .bp{color:#999999}html[data-mode=light] .highlight .vc{color:#008080}html[data-mode=light] .highlight .vg{color:#008080}html[data-mode=light] .highlight .vi{color:#008080}html[data-mode=light] .highlight .il{color:#009999}html[data-mode=light] [class^=prompt-]{--inline-code-bg: #fbfafa;--highlighter-rouge-color: rgb(82 82 82)}}figure.highlight,.highlight,.highlighter-rouge{background:var(--highlight-bg-color)}.highlight,.highlighter-rouge{border-radius:6px}td.rouge-code{padding-left:1rem;padding-right:1.5rem}.highlighter-rouge{color:var(--highlighter-rouge-color);margin-top:0.5rem;margin-bottom:1.2em}.highlight{overflow:auto;padding-top:0.5rem;padding-bottom:1rem}.highlight pre{margin-bottom:0;font-size:.85rem;line-height:1.4rem;word-wrap:normal}.highlight table td pre{overflow:visible;word-break:normal}.highlight .lineno{padding-right:0.5rem;min-width:2.2rem;text-align:right;color:var(--highlight-lineno-color);-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.highlight .gp{user-select:none}code{-webkit-hyphens:none;-ms-hyphens:none;-moz-hyphens:none;hyphens:none}code.highlighter-rouge{font-size:.85rem;padding:3px 5px;border-radius:4px;background-color:var(--inline-code-bg)}code.filepath{background-color:inherit;color:var(--filepath-text-color);font-weight:600;padding:0}a>code.highlighter-rouge{padding-bottom:0;color:inherit}a:hover>code.highlighter-rouge{border-bottom:none}blockquote code{color:inherit}.highlight>code{color:transparent}td.rouge-code a{color:inherit !important;border-bottom:none !important;pointer-events:none}div[class^='highlighter-rouge'] pre.lineno,div.language-plaintext.highlighter-rouge pre.lineno,div.language-console.highlighter-rouge pre.lineno,div.language-terminal.highlighter-rouge pre.lineno,div.nolineno pre.lineno{display:none}div[class^='highlighter-rouge'] td.rouge-code,div.language-plaintext.highlighter-rouge td.rouge-code,div.language-console.highlighter-rouge td.rouge-code,div.language-terminal.highlighter-rouge td.rouge-code,div.nolineno td.rouge-code{padding-left:1.5rem}.code-header{border-top-left-radius:6px;border-top-right-radius:6px;display:flex;justify-content:space-between;align-items:center;height:2.25rem}.code-header::before{content:"";display:inline-block;margin-left:1rem;width:.75rem;height:.75rem;border-radius:50%;background-color:var(--code-header-muted-color);box-shadow:1.25rem 0 0 var(--code-header-muted-color),2.5rem 0 0 var(--code-header-muted-color)}.code-header span i{font-size:1rem;margin-right:0.4rem;color:var(--code-header-icon-color)}.code-header span i.small{font-size:70%}[file] .code-header span>i{position:relative;top:1px}.code-header span::after{content:attr(data-label-text);font-size:0.85rem;font-weight:600;color:var(--code-header-text-color)}.code-header button{border:1px solid transparent;border-radius:6px;height:2.25rem;width:2.25rem;padding:0;background-color:inherit}.code-header button i{color:var(--code-header-icon-color)}.code-header button[timeout]:hover{border-color:var(--clipboard-checked-color)}.code-header button[timeout] i{color:var(--clipboard-checked-color)}.code-header button:not([timeout]):hover{background-color:rgba(128,128,128,0.37)}.code-header button:not([timeout]):hover i{color:white}.code-header button:focus{outline:none}@media all and (max-width: 576px){.post-content>div[class^='language-']{margin-left:-1.25rem;margin-right:-1.25rem;border-radius:0}.post-content>div[class^='language-'] .highlight{padding-left:0.25rem}.post-content>div[class^='language-'] .code-header{border-radius:0;padding-left:0.4rem;padding-right:0.5rem}}html{font-size:16px}@media (prefers-color-scheme: light){html:not([data-mode]),html [data-mode=light]{--body-bg: #fafafa;--mask-bg: #c1c3c5;--main-wrapper-bg: white;--main-border-color: #f3f3f3;--text-color: #34343c;--text-muted-color: gray;--heading-color: black;--blockquote-border-color: #eee;--blockquote-text-color: #9a9a9a;--link-color: #2a408e;--link-underline-color: #dee2e6;--button-bg: #fff;--btn-border-color: #e9ecef;--btn-backtotop-color: #686868;--btn-backtotop-border-color: #f1f1f1;--btn-box-shadow: #eaeaea;--checkbox-color: #c5c5c5;--checkbox-checked-color: #07a8f7;--sidebar-bg: #eeeeee;--sidebar-muted-color: #a2a19f;--sidebar-active-color: #424242;--nav-cursor-color: #757575;--sidebar-btn-bg: white;--topbar-text-color: rgb(78, 78, 78);--topbar-wrapper-bg: white;--search-wrapper-bg: rgb(245 245 245 / 50%);--search-wrapper-border-color: rgb(245 245 245);--search-tag-bg: #f8f9fa;--search-icon-color: #c2c6cc;--input-focus-border-color: var(--btn-border-color);--post-list-text-color: dimgray;--btn-patinator-text-color: #555555;--btn-paginator-hover-color: var(--sidebar-bg);--btn-paginator-border-color: var(--sidebar-bg);--btn-text-color: #676666;--pin-bg: #f5f5f5;--pin-color: #999fa4;--btn-share-hover-color: var(--link-color);--card-border-color: #f1f1f1;--card-box-shadow: rgba(234, 234, 234, 0.7686274509803922);--label-color: #616161;--relate-post-date: rgba(30, 55, 70, 0.4);--footnote-target-bg: lightcyan;--tag-bg: rgba(0, 0, 0, 0.075);--tag-border: #dee2e6;--tag-shadow: var(--btn-border-color);--tag-hover: rgb(222, 226, 230);--tb-odd-bg: #fbfcfd;--tb-border-color: #eaeaea;--dash-color: silver;--preview-img-bg: radial-gradient(circle, rgb(255 255 255) 0%, rgb(249 249 249) 100%);--kbd-wrap-color: #bdbdbd;--kbd-text-color: var(--text-color);--kbd-bg-color: white;--prompt-text-color: rgb(46 46 46 / 77%);--prompt-tip-bg: rgb(123 247 144 / 20%);--prompt-tip-icon-color: #03b303;--prompt-info-bg: #e1f5fe;--prompt-info-icon-color: #0070cb;--prompt-warning-bg: rgb(255 243 205);--prompt-warning-icon-color: #ef9c03;--prompt-danger-bg: rgb(248 215 218 / 56%);--prompt-danger-icon-color: #df3c30;--categories-hover-bg: var(--btn-border-color);--categories-icon-hover-color: darkslategray;--timeline-color: rgba(0, 0, 0, 0.075);--timeline-node-bg: #c2c6cc;--timeline-year-dot-color: #ffffff}html:not([data-mode]) [class^=prompt-],html [data-mode=light] [class^=prompt-]{--link-underline-color: rgb(219 216 216)}html[data-mode=dark]{--body-bg: var(--main-wrapper-bg);--mask-bg: rgb(68, 69, 70);--main-wrapper-bg: rgb(27, 27, 30);--main-border-color: rgb(44, 45, 45);--text-color: rgb(175, 176, 177);--text-muted-color: rgb(107, 116, 124);--heading-color: #cccccc;--blockquote-border-color: rgb(66, 66, 66);--blockquote-text-color: rgb(117, 117, 117);--link-color: rgb(138, 180, 248);--link-underline-color: rgb(82, 108, 150);--button-bg: rgb(39, 40, 43);--btn-border-color: rgb(63, 65, 68);--btn-backtotop-color: var(--text-color);--btn-backtotop-border-color: var(--btn-border-color);--btn-box-shadow: var(--main-wrapper-bg);--card-header-bg: rgb(51, 50, 50);--label-color: rgb(108, 117, 125);--checkbox-color: rgb(118 120 121);--checkbox-checked-color: var(--link-color);--sidebar-bg: radial-gradient(circle, #242424 0%, #1d1f27 100%);--sidebar-muted-color: #6d6c6b;--sidebar-active-color: rgb(255 255 255 / 80%);--nav-cursor-color: rgb(183, 182, 182);--sidebar-btn-bg: rgb(117 116 116 / 20%);--topbar-text-color: var(--text-color);--topbar-wrapper-bg: rgb(39, 40, 43);--search-wrapper-bg: rgb(34, 34, 39);--search-wrapper-border-color: rgb(34, 34, 39);--search-icon-color: rgb(100, 102, 105);--input-focus-border-color: rgb(112, 114, 115);--post-list-text-color: rgb(175, 176, 177);--btn-patinator-text-color: var(--text-color);--btn-paginator-hover-color: rgb(64, 65, 66);--btn-paginator-border-color: var(--btn-border-color);--btn-text-color: var(--text-color);--pin-bg: rgb(34 35 37);--pin-color: inherit;--toc-highlight: rgb(116, 178, 243);--tag-bg: rgb(41, 40, 40);--tag-hover: rgb(43, 56, 62);--tb-odd-bg: rgba(42, 47, 53, 0.52);--tb-even-bg: rgb(31, 31, 34);--tb-border-color: var(--tb-odd-bg);--footnote-target-bg: rgb(63, 81, 181);--btn-share-color: #6c757d;--btn-share-hover-color: #bfc1ca;--relate-post-date: var(--text-muted-color);--card-bg: rgb(39, 40, 43);--card-border-color: rgb(53, 53, 60);--card-box-shadow: var(--main-wrapper-bg);--preview-img-bg: radial-gradient(circle, rgb(22 22 24) 0%, rgb(32 32 32) 100%);--kbd-wrap-color: #6a6a6a;--kbd-text-color: #d3d3d3;--kbd-bg-color: #242424;--prompt-text-color: rgb(216 212 212 / 75%);--prompt-tip-bg: rgba(77, 187, 95, 0.2);--prompt-tip-icon-color: rgb(5 223 5 / 68%);--prompt-info-bg: rgb(7 59 104 / 80%);--prompt-info-icon-color: #0075d1;--prompt-warning-bg: rgb(90 69 3 / 95%);--prompt-warning-icon-color: rgb(255 165 0 / 80%);--prompt-danger-bg: rgb(86 28 8 / 80%);--prompt-danger-icon-color: #cd0202;--tag-border: rgb(59, 79, 88);--tag-shadow: rgb(32, 33, 33);--search-tag-bg: var(--tag-bg);--dash-color: rgb(63, 65, 68);--categories-border: rgb(64, 66, 69);--categories-hover-bg: rgb(73, 75, 76);--categories-icon-hover-color: white;--timeline-node-bg: rgb(150, 152, 156);--timeline-color: rgb(63, 65, 68);--timeline-year-dot-color: var(--timeline-color);color-scheme:dark}html[data-mode=dark] .post img[data-src]{filter:brightness(95%)}html[data-mode=dark] hr{border-color:var(--main-border-color)}html[data-mode=dark] nav[data-toggle=toc] .nav-link.active,html[data-mode=dark] nav[data-toggle=toc] .nav-link.active:focus,html[data-mode=dark] nav[data-toggle=toc] .nav-link.active:hover,html[data-mode=dark] nav[data-toggle=toc] .nav>li>a:focus,html[data-mode=dark] nav[data-toggle=toc] .nav>li>a:hover{color:var(--toc-highlight) !important;border-left-color:var(--toc-highlight) !important}html[data-mode=dark] .categories.card,html[data-mode=dark] .list-group-item{background-color:var(--card-bg)}html[data-mode=dark] .categories .card-header{background-color:var(--card-header-bg)}html[data-mode=dark] .categories .list-group-item{border-left:none;border-right:none;padding-left:2rem;border-color:var(--categories-border)}html[data-mode=dark] .categories .list-group-item:last-child{border-bottom-color:var(--card-bg)}html[data-mode=dark] #archives li:nth-child(odd){background-image:linear-gradient(to left, #1a1a1e, #27272d, #27272d, #27272d, #1a1a1e)}html[data-mode=dark] #disqus_thread{color-scheme:none}}@media (prefers-color-scheme: dark){html:not([data-mode]),html[data-mode=dark]{--body-bg: var(--main-wrapper-bg);--mask-bg: rgb(68, 69, 70);--main-wrapper-bg: rgb(27, 27, 30);--main-border-color: rgb(44, 45, 45);--text-color: rgb(175, 176, 177);--text-muted-color: rgb(107, 116, 124);--heading-color: #cccccc;--blockquote-border-color: rgb(66, 66, 66);--blockquote-text-color: rgb(117, 117, 117);--link-color: rgb(138, 180, 248);--link-underline-color: rgb(82, 108, 150);--button-bg: rgb(39, 40, 43);--btn-border-color: rgb(63, 65, 68);--btn-backtotop-color: var(--text-color);--btn-backtotop-border-color: var(--btn-border-color);--btn-box-shadow: var(--main-wrapper-bg);--card-header-bg: rgb(51, 50, 50);--label-color: rgb(108, 117, 125);--checkbox-color: rgb(118 120 121);--checkbox-checked-color: var(--link-color);--sidebar-bg: radial-gradient(circle, #242424 0%, #1d1f27 100%);--sidebar-muted-color: #6d6c6b;--sidebar-active-color: rgb(255 255 255 / 80%);--nav-cursor-color: rgb(183, 182, 182);--sidebar-btn-bg: rgb(117 116 116 / 20%);--topbar-text-color: var(--text-color);--topbar-wrapper-bg: rgb(39, 40, 43);--search-wrapper-bg: rgb(34, 34, 39);--search-wrapper-border-color: rgb(34, 34, 39);--search-icon-color: rgb(100, 102, 105);--input-focus-border-color: rgb(112, 114, 115);--post-list-text-color: rgb(175, 176, 177);--btn-patinator-text-color: var(--text-color);--btn-paginator-hover-color: rgb(64, 65, 66);--btn-paginator-border-color: var(--btn-border-color);--btn-text-color: var(--text-color);--pin-bg: rgb(34 35 37);--pin-color: inherit;--toc-highlight: rgb(116, 178, 243);--tag-bg: rgb(41, 40, 40);--tag-hover: rgb(43, 56, 62);--tb-odd-bg: rgba(42, 47, 53, 0.52);--tb-even-bg: rgb(31, 31, 34);--tb-border-color: var(--tb-odd-bg);--footnote-target-bg: rgb(63, 81, 181);--btn-share-color: #6c757d;--btn-share-hover-color: #bfc1ca;--relate-post-date: var(--text-muted-color);--card-bg: rgb(39, 40, 43);--card-border-color: rgb(53, 53, 60);--card-box-shadow: var(--main-wrapper-bg);--preview-img-bg: radial-gradient(circle, rgb(22 22 24) 0%, rgb(32 32 32) 100%);--kbd-wrap-color: #6a6a6a;--kbd-text-color: #d3d3d3;--kbd-bg-color: #242424;--prompt-text-color: rgb(216 212 212 / 75%);--prompt-tip-bg: rgba(77, 187, 95, 0.2);--prompt-tip-icon-color: rgb(5 223 5 / 68%);--prompt-info-bg: rgb(7 59 104 / 80%);--prompt-info-icon-color: #0075d1;--prompt-warning-bg: rgb(90 69 3 / 95%);--prompt-warning-icon-color: rgb(255 165 0 / 80%);--prompt-danger-bg: rgb(86 28 8 / 80%);--prompt-danger-icon-color: #cd0202;--tag-border: rgb(59, 79, 88);--tag-shadow: rgb(32, 33, 33);--search-tag-bg: var(--tag-bg);--dash-color: rgb(63, 65, 68);--categories-border: rgb(64, 66, 69);--categories-hover-bg: rgb(73, 75, 76);--categories-icon-hover-color: white;--timeline-node-bg: rgb(150, 152, 156);--timeline-color: rgb(63, 65, 68);--timeline-year-dot-color: var(--timeline-color);color-scheme:dark}html:not([data-mode]) .post img[data-src],html[data-mode=dark] .post img[data-src]{filter:brightness(95%)}html:not([data-mode]) hr,html[data-mode=dark] hr{border-color:var(--main-border-color)}html:not([data-mode]) nav[data-toggle=toc] .nav-link.active,html:not([data-mode]) nav[data-toggle=toc] .nav-link.active:focus,html:not([data-mode]) nav[data-toggle=toc] .nav-link.active:hover,html:not([data-mode]) nav[data-toggle=toc] .nav>li>a:focus,html:not([data-mode]) nav[data-toggle=toc] .nav>li>a:hover,html[data-mode=dark] nav[data-toggle=toc] .nav-link.active,html[data-mode=dark] nav[data-toggle=toc] .nav-link.active:focus,html[data-mode=dark] nav[data-toggle=toc] .nav-link.active:hover,html[data-mode=dark] nav[data-toggle=toc] .nav>li>a:focus,html[data-mode=dark] nav[data-toggle=toc] .nav>li>a:hover{color:var(--toc-highlight) !important;border-left-color:var(--toc-highlight) !important}html:not([data-mode]) .categories.card,html:not([data-mode]) .list-group-item,html[data-mode=dark] .categories.card,html[data-mode=dark] .list-group-item{background-color:var(--card-bg)}html:not([data-mode]) .categories .card-header,html[data-mode=dark] .categories .card-header{background-color:var(--card-header-bg)}html:not([data-mode]) .categories .list-group-item,html[data-mode=dark] .categories .list-group-item{border-left:none;border-right:none;padding-left:2rem;border-color:var(--categories-border)}html:not([data-mode]) .categories .list-group-item:last-child,html[data-mode=dark] .categories .list-group-item:last-child{border-bottom-color:var(--card-bg)}html:not([data-mode]) #archives li:nth-child(odd),html[data-mode=dark] #archives li:nth-child(odd){background-image:linear-gradient(to left, #1a1a1e, #27272d, #27272d, #27272d, #1a1a1e)}html:not([data-mode]) #disqus_thread,html[data-mode=dark] #disqus_thread{color-scheme:none}html[data-mode=light]{--body-bg: #fafafa;--mask-bg: #c1c3c5;--main-wrapper-bg: white;--main-border-color: #f3f3f3;--text-color: #34343c;--text-muted-color: gray;--heading-color: black;--blockquote-border-color: #eee;--blockquote-text-color: #9a9a9a;--link-color: #2a408e;--link-underline-color: #dee2e6;--button-bg: #fff;--btn-border-color: #e9ecef;--btn-backtotop-color: #686868;--btn-backtotop-border-color: #f1f1f1;--btn-box-shadow: #eaeaea;--checkbox-color: #c5c5c5;--checkbox-checked-color: #07a8f7;--sidebar-bg: #eeeeee;--sidebar-muted-color: #a2a19f;--sidebar-active-color: #424242;--nav-cursor-color: #757575;--sidebar-btn-bg: white;--topbar-text-color: rgb(78, 78, 78);--topbar-wrapper-bg: white;--search-wrapper-bg: rgb(245 245 245 / 50%);--search-wrapper-border-color: rgb(245 245 245);--search-tag-bg: #f8f9fa;--search-icon-color: #c2c6cc;--input-focus-border-color: var(--btn-border-color);--post-list-text-color: dimgray;--btn-patinator-text-color: #555555;--btn-paginator-hover-color: var(--sidebar-bg);--btn-paginator-border-color: var(--sidebar-bg);--btn-text-color: #676666;--pin-bg: #f5f5f5;--pin-color: #999fa4;--btn-share-hover-color: var(--link-color);--card-border-color: #f1f1f1;--card-box-shadow: rgba(234, 234, 234, 0.7686274509803922);--label-color: #616161;--relate-post-date: rgba(30, 55, 70, 0.4);--footnote-target-bg: lightcyan;--tag-bg: rgba(0, 0, 0, 0.075);--tag-border: #dee2e6;--tag-shadow: var(--btn-border-color);--tag-hover: rgb(222, 226, 230);--tb-odd-bg: #fbfcfd;--tb-border-color: #eaeaea;--dash-color: silver;--preview-img-bg: radial-gradient(circle, rgb(255 255 255) 0%, rgb(249 249 249) 100%);--kbd-wrap-color: #bdbdbd;--kbd-text-color: var(--text-color);--kbd-bg-color: white;--prompt-text-color: rgb(46 46 46 / 77%);--prompt-tip-bg: rgb(123 247 144 / 20%);--prompt-tip-icon-color: #03b303;--prompt-info-bg: #e1f5fe;--prompt-info-icon-color: #0070cb;--prompt-warning-bg: rgb(255 243 205);--prompt-warning-icon-color: #ef9c03;--prompt-danger-bg: rgb(248 215 218 / 56%);--prompt-danger-icon-color: #df3c30;--categories-hover-bg: var(--btn-border-color);--categories-icon-hover-color: darkslategray;--timeline-color: rgba(0, 0, 0, 0.075);--timeline-node-bg: #c2c6cc;--timeline-year-dot-color: #ffffff}html[data-mode=light] [class^=prompt-]{--link-underline-color: rgb(219 216 216)}}body{line-height:1.75rem;background:var(--body-bg);color:var(--text-color);-webkit-font-smoothing:antialiased;font-family:'Source Sans Pro', 'Microsoft Yahei', sans-serif}h1{font-size:1.9rem}h2{font-size:1.5rem}h3{font-size:1.2rem}h4{font-size:1.15rem}h5{font-size:1.1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:1rem}img{max-width:100%;height:auto}blockquote{border-left:5px solid var(--blockquote-border-color);padding-left:1rem;color:var(--blockquote-text-color)}blockquote[class^="prompt-"]{display:flex;border-left:0;border-radius:6px;padding:0.75rem 1.2rem;color:var(--prompt-text-color)}blockquote[class^="prompt-"]::before{margin-right:1rem;font-family:"Font Awesome 5 Free";text-align:center;width:1.25rem}blockquote[class^="prompt-"] p:last-child{margin-bottom:0rem}blockquote.prompt-tip{background-color:var(--prompt-tip-bg)}blockquote.prompt-tip::before{content:"";color:var(--prompt-tip-icon-color);font-weight:400}blockquote.prompt-info{background-color:var(--prompt-info-bg)}blockquote.prompt-info::before{content:"";color:var(--prompt-info-icon-color);font-weight:900}blockquote.prompt-warning{background-color:var(--prompt-warning-bg)}blockquote.prompt-warning::before{content:"";color:var(--prompt-warning-icon-color);font-weight:900}blockquote.prompt-danger{background-color:var(--prompt-danger-bg)}blockquote.prompt-danger::before{content:"";color:var(--prompt-danger-icon-color);font-weight:900}kbd{font-family:inherit;display:inline-block;vertical-align:middle;line-height:1.3rem;min-width:1.75rem;text-align:center;margin:0 0.3rem;padding-top:0.1rem;color:var(--kbd-text-color);background-color:var(--kbd-bg-color);border-radius:0.25rem;border:solid 1px var(--kbd-wrap-color);box-shadow:inset 0 -2px 0 var(--kbd-wrap-color)}footer{position:absolute;bottom:0;padding:0 1rem;height:5rem;font-size:0.8rem}footer>div.d-flex{line-height:1.2rem;width:95%;max-width:1045px;border-top:1px solid var(--main-border-color);margin-bottom:1rem}footer>div.d-flex>div{width:350px}footer a:link{text-decoration:none}footer a:hover{text-decoration:none}footer .footer-right{text-align:right}@keyframes fade-in{from{opacity:0}to{opacity:1}}img[data-src]{margin:0.5rem 0}img[data-src][data-loaded=true]{animation:fade-in linear 0.5s}img.left[data-src]{float:left;margin:0.75rem 1rem 1rem 0}img.right[data-src]{float:right;margin:0.75rem 0 1rem 1rem}img.shadow[data-src]{filter:drop-shadow(2px 4px 6px rgba(0,0,0,0.08));box-shadow:none !important}.access{top:2rem;transition:top 0.2s ease-in-out;margin-right:1.5rem;margin-top:3rem;margin-bottom:4rem}.access:only-child{position:-webkit-sticky;position:sticky}.access>div{padding-left:1rem;border-left:1px solid var(--main-border-color)}.access>div:not(:last-child){margin-bottom:4rem}.access .post-content{font-size:0.9rem}#panel-wrapper .panel-heading{color:var(--label-color);font-size:inherit;font-weight:600}#panel-wrapper .post-tag{display:inline-block;line-height:1rem;font-size:0.85rem;background:none;border:1px solid var(--btn-border-color);border-radius:0.8rem;padding:0.3rem 0.5rem;margin:0 0.35rem 0.5rem 0}#panel-wrapper .post-tag:hover{background-color:#2a408e;border-color:#2a408e;color:#fff;transition:none}[data-topbar-visible=true] #panel-wrapper>div{top:6rem}#access-lastmod li{height:1.8rem;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;list-style:none}#access-lastmod a{color:inherit}.footnotes>ol{padding-left:2rem;margin-top:0.5rem}.footnotes>ol>li:not(:last-child){margin-bottom:0.3rem}.footnotes>ol>li>p{margin-left:0.25em;margin-top:0;margin-bottom:0}.footnotes>ol>li:target:not([scroll-focus]),.footnotes>ol>li[scroll-focus=true]>p{background-color:var(--footnote-target-bg);width:fit-content;-webkit-transition:background-color 1.5s ease-in-out;transition:background-color 1.5s ease-in-out}a.footnote{margin-left:1px;margin-right:1px;padding-left:2px;padding-right:2px;border-bottom-style:none !important;-webkit-transition:background-color 1.5s ease-in-out;transition:background-color 1.5s ease-in-out}sup:target:not([scroll-focus]),sup[scroll-focus=true]>a.footnote{background-color:var(--footnote-target-bg)}a.reversefootnote{font-size:0.6rem;line-height:1;position:relative;bottom:0.25em;margin-left:0.25em;border-bottom-style:none !important}.table-wrapper{overflow-x:auto;margin-bottom:1.5rem}.table-wrapper>table{min-width:100%;overflow-x:auto;border-spacing:0}.table-wrapper>table thead{border-bottom:solid 2px rgba(210,215,217,0.75)}.table-wrapper>table tbody tr{border-bottom:1px solid var(--tb-border-color)}.table-wrapper>table tbody tr:nth-child(2n){background-color:var(--tb-even-bg)}.table-wrapper>table tbody tr:nth-child(2n + 1){background-color:var(--tb-odd-bg)}.post h1{margin-top:3rem;margin-bottom:1.5rem}.post a.popup{cursor:zoom-in}.post a.popup>img[data-src]:not(.normal):not(.left):not(.right){position:relative;left:50%;-webkit-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translateX(-50%)}.pageviews .fa-spinner{font-size:80%}.post-meta{font-size:0.85rem;word-spacing:1px}.post-meta a:not(:last-child){margin-right:2px}.post-content{font-size:1.08rem;line-height:1.8;margin-top:2rem;overflow-wrap:break-word;word-wrap:break-word}.post-content ul .task-list-item[hide-bullet]{list-style-type:none}.post-content ul .task-list-item[hide-bullet]>i{margin:0 0.4rem 0.2rem -1.4rem;vertical-align:middle;color:var(--checkbox-color)}.post-content ul .task-list-item[hide-bullet]>i.checked{color:var(--checkbox-checked-color)}.post-content ul input[type=checkbox]{margin:0 0.5rem 0.2rem -1.3rem;vertical-align:middle}.post-content>ol,.post-content>ul{padding-left:2rem}.post-content>ol li ol,.post-content>ol li ul,.post-content>ul li ol,.post-content>ul li ul{padding-left:2rem;margin-top:0.3rem}.post-content>ol li{padding-left:0.25em}.post-content dl>dd{margin-left:1rem}.post-tag{display:inline-block;min-width:2rem;text-align:center;background:var(--tag-bg);border-radius:0.3rem;padding:0 0.4rem;color:inherit;line-height:1.3rem}.post-tag:not(:last-child){margin-right:0.2rem}.post-tag:hover{border-bottom:none;text-decoration:none;color:#d2603a}.btn-lang{border:1px solid !important;padding:1px 3px;border-radius:3px;color:var(--link-color)}.btn-lang:focus{box-shadow:none}.loaded{display:block !important}.d-flex.loaded{display:flex !important}.unloaded{display:none !important}.visible{visibility:visible !important}.hidden{visibility:hidden !important}.flex-grow-1{-ms-flex-positive:1 !important;flex-grow:1 !important}.btn-box-shadow{box-shadow:0 0 8px 0 var(--btn-box-shadow) !important}.no-text-decoration{text-decoration:none}.tooltip-inner{font-size:0.7rem;max-width:220px;text-align:left}.disabled{color:#cec4c4;pointer-events:auto;cursor:not-allowed}.hide-border-bottom{border-bottom:none !important}.input-focus{box-shadow:none;border-color:var(--input-focus-border-color) !important;background:center !important;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out}figure .mfp-title{text-align:center;padding-right:0;margin-top:0.5rem}.mermaid{text-align:center}#sidebar{padding-left:0;padding-right:0;position:fixed;top:0;left:0;height:100%;overflow-y:auto;width:260px;z-index:99;background:var(--sidebar-bg);-ms-overflow-style:none;scrollbar-width:none}#sidebar::-webkit-scrollbar{display:none}#sidebar a:hover{text-decoration:none;color:var(--sidebar-active-color) !important}#sidebar #avatar>a{display:block;width:6rem;height:6rem;border-radius:50%;border:2px solid rgba(222,222,222,0.7);overflow:hidden;transform:translateZ(0);-webkit-transition:border-color 0.35s ease-in-out;-moz-transition:border-color 0.35s ease-in-out;transition:border-color 0.35s ease-in-out}#sidebar #avatar>a:hover{border-color:white}#sidebar #avatar img{width:100%;height:100%;-webkit-transition:transform 0.5s;-moz-transition:transform 0.5s;transition:transform 0.5s}#sidebar #avatar img:hover{-ms-transform:scale(1.2);-moz-transform:scale(1.2);-webkit-transform:scale(1.2);transform:scale(1.2)}#sidebar .site-title a{font-weight:900;font-size:1.5rem;letter-spacing:0.5px;color:#868585}#sidebar .site-subtitle{font-size:95%;color:var(--sidebar-muted-color);line-height:1.2rem;word-spacing:1px;margin:0.5rem 1.5rem 0.5rem 1.5rem;min-height:3rem;user-select:none}#sidebar .nav-link{border-radius:0;font-size:0.95rem;font-weight:600;letter-spacing:1px;display:table-cell;vertical-align:middle}#sidebar .nav-item{text-align:center;display:table;height:3rem}#sidebar .nav-item.active .nav-link{color:var(--sidebar-active-color)}#sidebar ul{height:21rem;margin-bottom:2rem;padding-left:0}#sidebar ul li{width:100%}#sidebar ul li:last-child a{position:relative;left:1px;width:100%}#sidebar ul li:last-child::after{display:table;visibility:hidden;content:"";position:relative;right:1px;width:2px;height:1.6rem;border-radius:1px;background-color:var(--nav-cursor-color);pointer-events:none}#sidebar ul>li.active:nth-child(1)~li:last-child::after,#sidebar ul>li.nav-item:nth-child(1):hover~li:last-child::after{top:-17.3rem;visibility:visible}#sidebar ul>li.active:nth-child(2)~li:last-child::after,#sidebar ul>li.nav-item:nth-child(2):hover~li:last-child::after{top:-14.3rem;visibility:visible}#sidebar ul>li.active:nth-child(3)~li:last-child::after,#sidebar ul>li.nav-item:nth-child(3):hover~li:last-child::after{top:-11.3rem;visibility:visible}#sidebar ul>li.active:nth-child(4)~li:last-child::after,#sidebar ul>li.nav-item:nth-child(4):hover~li:last-child::after{top:-8.3rem;visibility:visible}#sidebar ul>li.active:nth-child(5)~li:last-child::after,#sidebar ul>li.nav-item:nth-child(5):hover~li:last-child::after{top:-5.3rem;visibility:visible}#sidebar ul>li.active:nth-child(6)~li:last-child::after,#sidebar ul>li.nav-item:nth-child(6):hover~li:last-child::after{top:-2.3rem;visibility:visible}#sidebar ul>li.active:nth-child(7):last-child::after,#sidebar ul>li.nav-item:nth-child(7):last-child:hover::after{top:.7rem;visibility:visible}#sidebar .sidebar-bottom{margin-bottom:2.1rem;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem}#sidebar .sidebar-bottom .mode-toggle,#sidebar .sidebar-bottom a{width:2.4rem;text-align:center}#sidebar .sidebar-bottom i{font-size:1.2rem;line-height:1.75rem}#sidebar .sidebar-bottom .mode-toggle{padding:0;border:0;margin-bottom:1px;background-color:transparent}#sidebar .sidebar-bottom .mode-toggle:hover>i{color:var(--sidebar-active-color)}#sidebar .sidebar-bottom .icon-border{background-color:var(--sidebar-muted-color);content:"";width:3px;height:3px;border-radius:50%}@media (hover: hover){#sidebar ul>li:last-child::after{-webkit-transition:top 0.5s ease;-moz-transition:top 0.5s ease;-o-transition:top 0.5s ease;transition:top 0.5s ease}}.profile-wrapper{margin-top:2rem;width:100%}#search-result-wrapper{display:none;height:100%;overflow:auto}#search-result-wrapper .post-content{margin-top:2rem}#topbar-wrapper{height:3rem;position:fixed;top:0;left:260px;right:0;transition:top 0.2s ease-in-out;z-index:50;border-bottom:1px solid rgba(0,0,0,0.07);background-color:var(--topbar-wrapper-bg)}[data-topbar-visible=false] #topbar-wrapper{top:-3rem}#topbar i{color:#999}#topbar #breadcrumb{font-size:1rem;color:gray;padding-left:0.5rem}#topbar #breadcrumb span:not(:last-child)::after{content:"›";padding:0 0.3rem}#sidebar-trigger,#search-trigger{display:none}#search-wrapper{display:flex;width:85%;border-radius:1rem;border:1px solid var(--search-wrapper-border-color);background:var(--search-wrapper-bg);padding:0 0.5rem}#search-wrapper i{z-index:2;font-size:0.9rem;color:var(--search-icon-color)}#search-cancel{color:var(--link-color);margin-left:1rem;display:none}#search-input{background:center;border:0;border-radius:0;padding:0.18rem 0.3rem;color:var(--text-color);height:auto}#search-input:focus{box-shadow:none;background:center}#search-input.form-control:focus::-webkit-input-placeholder{opacity:0.6}#search-input.form-control:focus::-moz-placeholder{opacity:0.6}#search-input.form-control:focus:-ms-input-placeholder{opacity:0.6}#search-input.form-control:focus::placeholder{opacity:0.6}#search-hints{padding:0 1rem}#search-hints h4{margin-bottom:1.5rem}#search-hints .post-tag{display:inline-block;line-height:1rem;font-size:1rem;background:var(--search-tag-bg);border:none;padding:0.5rem;margin:0 1.25rem 1rem 0}#search-hints .post-tag::before{content:"#";color:var(--text-muted-color);padding-right:0.2rem}#search-results{padding-bottom:6rem}#search-results a{font-size:1.4rem;line-height:2.5rem}#search-results>div{width:100%}#search-results>div:not(:last-child){margin-bottom:1rem}#search-results>div i{color:#818182;margin-right:0.15rem;font-size:80%}#search-results>div>p{overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical}#topbar-title{display:none;font-size:1.1rem;font-weight:600;font-family:sans-serif;color:var(--topbar-text-color);text-align:center;width:70%;overflow:hidden;text-overflow:ellipsis;word-break:keep-all;white-space:nowrap}#core-wrapper{min-height:calc(100vh - 3rem - 5rem - 35rem) !important}#mask{display:none;position:fixed;top:0;right:0;bottom:0;left:0;height:100%;width:100%;z-index:1}[sidebar-display] #mask{display:block !important}#main-wrapper{background-color:var(--main-wrapper-bg);position:relative;min-height:100vh;padding-bottom:5rem;padding-left:0;padding-right:0}#main .row:first-child>div:nth-child(1),#main .row:first-child>div:nth-child(2){margin-top:3rem}#main .row:first-child>div:first-child{min-height:calc(100vh - 3rem - 5rem - 35rem)}#main div.row:first-of-type:last-of-type{margin-bottom:4rem}#topbar-wrapper.row,#main>.row,#search-result-wrapper>.row{margin-left:0;margin-right:0}#back-to-top{display:none;z-index:1;cursor:pointer;position:fixed;background:var(--button-bg);color:var(--btn-backtotop-color);padding:0;width:2.7em;height:2.7em;border-radius:50%;border:1px solid var(--btn-backtotop-border-color);transition:transform 0.2s ease-out;-webkit-transition:transform 0.2s ease-out}#back-to-top i{line-height:2.7em;position:relative;bottom:2px}#back-to-top:hover{transform:translate3d(0, -5px, 0);-webkit-transform:translate3d(0, -5px, 0)}@media all and (max-width: 576px){footer{height:6rem}footer>div.d-flex{width:100%;padding:1.5rem 0;margin-bottom:0.3rem;flex-wrap:wrap;-ms-flex-pack:distribute !important;justify-content:space-around !important}footer .footer-left,footer .footer-right{text-align:center}#main>div.row:first-child>div:first-child{min-height:calc(100vh - 3rem - 6rem)}#core-wrapper{min-height:calc(100vh - 3rem - 6rem - 35rem) !important}#core-wrapper h1{margin-top:2.2rem;font-size:1.75rem}#core-wrapper .post-content>blockquote[class^=prompt-]{margin-left:-1.25rem;margin-right:-1.25rem;border-radius:0}#avatar>a{width:5rem;height:5rem}.site-subtitle{margin-left:1.8rem;margin-right:1.8rem}#main-wrapper{padding-bottom:6rem}}@media all and (max-width: 849px){html,body{overflow-x:hidden}[sidebar-display] #sidebar{transform:translateX(0)}[sidebar-display] #topbar-wrapper,[sidebar-display] #main-wrapper{transform:translateX(260px)}#sidebar{-webkit-transition:transform 0.4s ease;transition:transform 0.4s ease;transform:translateX(-260px);-webkit-transform:translateX(-260px)}#sidebar .cursor{-webkit-transition:none;-moz-transition:none;transition:none}#main-wrapper{-webkit-transition:transform 0.4s ease;transition:transform 0.4s ease;padding-top:3rem}#search-result-wrapper{width:100%}#breadcrumb,#search-wrapper{display:none}#topbar-wrapper{-webkit-transition:transform 0.4s ease, top 0.2s ease;transition:transform 0.4s ease, top 0.2s ease;left:0}#main>div.row:first-child>div:nth-child(1),#main>div.row:first-child>div:nth-child(2){margin-top:0}#topbar-title,#sidebar-trigger,#search-trigger{display:block}#search-wrapper.loaded~a{margin-right:1rem}#search-input{margin-left:0;width:95%}#search-result-wrapper .post-content{letter-spacing:0}#tags{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}h1.dynamic-title{display:none}h1.dynamic-title~.post-content{margin-top:3rem}}@media all and (max-width: 849px) and (orientation: portrait){[data-topbar-visible=false] #topbar-wrapper{top:0}}@media all and (min-width: 577px) and (max-width: 1199px){footer>.d-flex>div{width:312px}}@media all and (min-width: 850px){html{overflow-y:scroll}#main-wrapper{margin-left:260px}.profile-wrapper{margin-top:3rem}#search-wrapper{width:22%;min-width:150px}#search-hints{display:none}#search-result-wrapper{margin-top:3rem}div.post-content .table-wrapper>table{min-width:70%}#back-to-top{bottom:5.5rem;right:1.2rem}#topbar-title{text-align:left}footer>div.d-flex{width:92%}}@media all and (min-width: 992px) and (max-width: 1199px){#main .col-lg-11{-webkit-box-flex:0;-ms-flex:0 0 96%;flex:0 0 96%;max-width:96%}}@media all and (min-width: 850px) and (max-width: 1199px){#sidebar{width:210px}#sidebar .site-subtitle{margin-left:1rem;margin-right:1rem}#sidebar .sidebar-bottom a,#sidebar .sidebar-bottom span{width:2rem}#sidebar .sidebar-bottom .icon-border{left:-3px}#topbar-wrapper{left:210px}#search-results>div{max-width:700px}.site-title{font-size:1.3rem;margin-left:0 !important}.site-subtitle{margin-left:1rem;margin-right:1rem;font-size:90%}#main-wrapper{margin-left:210px}#breadcrumb{width:65%;overflow:hidden;text-overflow:ellipsis;word-break:keep-all;white-space:nowrap}}@media all and (max-width: 1199px){#panel-wrapper{display:none}#topbar{padding:0}#main>div.row{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}}@media all and (min-width: 1200px){#main>div.row>div.col-xl-8{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%;padding-left:3%}#topbar{padding:0;max-width:1070px}#panel-wrapper{max-width:300px}#back-to-top{bottom:6.5rem;right:4.3rem}#search-input{-webkit-transition:all 0.3s ease-in-out;transition:all 0.3s ease-in-out}#search-results>div{width:46%}#search-results>div:nth-child(odd){margin-right:1.5rem}#search-results>div:nth-child(even){margin-left:1.5rem}#search-results>div:last-child:nth-child(odd){position:relative;right:24.3%}.post-content{font-size:1.03rem}footer>div.d-felx{width:85%}}@media all and (min-width: 1400px){#main>div.row{padding-left:calc((100% - 1150px) / 2)}#main>div.row>div.col-xl-8{max-width:850px}#search-result-wrapper{padding-right:2rem}#search-result-wrapper>div{max-width:1110px}}@media all and (min-width: 1400px) and (max-width: 1650px){#topbar{padding-right:2rem}}@media all and (min-width: 1650px){#breadcrumb{padding-left:0}#main>div.row>div.col-xl-8{padding-left:0}#main>div.row>div.col-xl-8>div:first-child{padding-left:0.55rem !important;padding-right:1.9rem !important}#main-wrapper{margin-left:350px}#panel-wrapper{margin-left:calc((100% - 1150px) / 10)}#topbar-wrapper{left:350px}#topbar{max-width:1150px}#search-wrapper{margin-right:3%}#sidebar{width:350px}#sidebar .profile-wrapper{margin-top:4rem;margin-bottom:1rem}#sidebar .profile-wrapper.text-center{text-align:left !important}#sidebar .profile-wrapper .site-subtitle,#sidebar .profile-wrapper .site-title,#sidebar .profile-wrapper #avatar{margin-left:4.5rem}#sidebar .profile-wrapper #avatar>a{width:6.2rem;height:6.2rem}#sidebar .profile-wrapper #avatar>a.mx-auto{margin-left:0 !important}#sidebar .profile-wrapper .site-title a{font-size:1.7rem;letter-spacing:1px}#sidebar .profile-wrapper .site-subtitle{word-spacing:0;margin-top:0.3rem}#sidebar ul{padding-left:2.5rem}#sidebar ul>li:last-child>a{position:static}#sidebar ul .nav-item{text-align:left}#sidebar ul .nav-item .nav-link>span{letter-spacing:2px}#sidebar ul .nav-item .nav-link>i.unloaded{display:inline-block !important}#sidebar .sidebar-bottom{padding-left:3.5rem;width:100%}#sidebar .sidebar-bottom.justify-content-center{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}#sidebar .sidebar-bottom>span,#sidebar .sidebar-bottom>button.mode-toggle,#sidebar .sidebar-bottom>a{margin-left:.15rem;margin-right:.15rem;height:2rem;margin-bottom:0.5rem}#sidebar .sidebar-bottom i{background-color:var(--sidebar-btn-bg);font-size:1rem;width:2rem;height:2rem;border-radius:50%;position:relative}#sidebar .sidebar-bottom i::before{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%)}#sidebar .sidebar-bottom .icon-border{top:0.9rem}footer>div.d-flex{width:92%;max-width:1140px}#search-result-wrapper>div{max-width:1150px}}@media all and (min-width: 1700px){#topbar-wrapper{padding-right:calc(100% - 350px - (1920px - 350px))}#topbar{max-width:calc(1150px + 20px)}#main>div.row{padding-left:calc((100% - 1150px - 2%) / 2)}#panel-wrapper{margin-left:3%}footer{padding-left:0;padding-right:calc(100% - 350px - 1180px)}#back-to-top{right:calc(100% - 1920px + 15rem)}}@media (min-width: 1920px){#main>div.row{padding-left:190px}#search-result-wrapper{padding-right:calc(100% - 350px - 1180px)}#panel-wrapper{margin-left:41px}}.pagination{color:var(--btn-patinator-text-color);font-family:'Lato', sans-serif}.pagination a:hover{text-decoration:none}.pagination .page-item .page-link{color:inherit;width:2.5rem;height:2.5rem;padding:0;display:-webkit-box;-webkit-box-pack:center;-webkit-box-align:center;border-radius:50%;border:1px solid var(--btn-paginator-border-color);background-color:var(--button-bg)}.pagination .page-item .page-link:hover{background-color:var(--btn-paginator-hover-color)}.pagination .page-item.active .page-link{background-color:var(--btn-paginator-hover-color);color:var(--btn-text-color)}.pagination .page-item.disabled{cursor:not-allowed}.pagination .page-item.disabled .page-link{color:rgba(108,117,125,0.57);border-color:var(--btn-paginator-border-color);background-color:var(--button-bg)}.pagination .page-item:first-child .page-link,.pagination .page-item:last-child .page-link{border-radius:50%}#post-list{margin-top:1rem;padding-right:0.5rem}#post-list .post-preview{padding-top:1.5rem;padding-bottom:1rem;border-bottom:1px solid var(--main-border-color)}#post-list .post-preview h1{font-size:1.4rem;margin:0}#post-list .post-preview .post-meta i{font-size:0.73rem}#post-list .post-preview .post-meta i:not(:first-child){margin-left:1.2rem}#post-list .post-preview .post-content{margin-top:0.6rem;margin-bottom:0.6rem;color:var(--post-list-text-color)}#post-list .post-preview .post-content>p{margin:0;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}#post-list .post-preview .pin>i{transform:rotate(45deg);padding-left:3px;color:var(--pin-color)}#post-list .post-preview .pin>span{display:none}@media all and (max-width: 830px){.pagination{justify-content:space-evenly}.pagination .page-item:not(:first-child):not(:last-child){display:none}}@media all and (min-width: 831px){#post-list{margin-top:1.5rem}#post-list .post-preview .post-meta .pin{background:var(--pin-bg);border-radius:5px;line-height:1.4rem;height:1.3rem;margin-top:3px;padding-left:1px;padding-right:6px}#post-list .post-preview .post-meta .pin>span{display:inline}.pagination{font-size:0.85rem}.pagination .page-item:not(:last-child){margin-right:0.7rem}.pagination .page-item .page-link{width:2rem;height:2rem}.pagination .page-index{display:none}}@media all and (max-width: 1200px){#post-list{padding-right:0}}#related-posts .card h3,h1+.post-meta em a,h1+.post-meta em,footer a{color:var(--text-color)}h1+.post-meta span+span::before{content:"\2022";padding-left:.25rem;padding-right:.25rem}img.preview-img{margin-top:3.75rem;margin-bottom:0;border-radius:6px}img.preview-img.bg[data-loaded=true]{background:var(--preview-img-bg)}.post-tail-wrapper{margin-top:6rem;border-bottom:1px double var(--main-border-color);font-size:0.85rem}.post-tags{line-height:2rem}.post-navigation{padding-top:3rem;padding-bottom:4rem}.post-navigation .btn{width:50%;position:relative;border-color:var(--btn-border-color);color:var(--link-color)}.post-navigation .btn:hover{background:#2a408e;color:#fff;border-color:#2a408e}.post-navigation .btn.disabled{width:50%;position:relative;border-color:var(--btn-border-color);pointer-events:auto;cursor:not-allowed;background:none;color:gray}.post-navigation .btn.disabled:hover{border-color:none}.post-navigation .btn.btn-outline-primary.disabled:focus{box-shadow:none}.post-navigation .btn::before{color:var(--text-muted-color);font-size:0.65rem;text-transform:uppercase;content:attr(prompt)}.post-navigation .btn:first-child{border-top-right-radius:0;border-bottom-right-radius:0;left:0.5px}.post-navigation .btn:last-child{border-top-left-radius:0;border-bottom-left-radius:0;right:0.5px}.post-navigation p{font-size:1.1rem;line-height:1.5rem;margin-top:0.3rem;white-space:normal}@keyframes fade-up{from{opacity:0;position:relative;top:2rem}to{opacity:1;position:relative;top:0}}#toc-wrapper{border-left:1px solid rgba(158,158,158,0.17);position:-webkit-sticky;position:sticky;top:4rem;transition:top 0.2s ease-in-out;animation:fade-up 0.8s}#toc li a{font-size:0.8rem}#toc li a.nav-link:not(.active){color:inherit}nav[data-toggle=toc] .nav .nav>li>a.active{font-weight:600 !important}#related-posts>h3{color:var(--label-color);font-size:1.1rem;font-weight:600}#related-posts .card{border-color:var(--card-border-color);background-color:var(--card-bg);box-shadow:0 0 5px 0 var(--card-box-shadow);-webkit-transition:all 0.3s ease-in-out;-moz-transition:all 0.3s ease-in-out;transition:all 0.3s ease-in-out}#related-posts .card:hover{-webkit-transform:translate3d(0, -3px, 0);transform:translate3d(0, -3px, 0);box-shadow:0 10px 15px -4px rgba(0,0,0,0.15)}#related-posts .timeago{color:var(--relate-post-date)}#related-posts p{font-size:0.9rem;margin-bottom:0.5rem;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}#related-posts a:hover{text-decoration:none}#related-posts ul{list-style-type:none;padding-inline-start:1.5rem}#related-posts ul>li::before{background:#c2c9d4;width:5px;height:5px;border-radius:1px;display:block;content:"";position:relative;top:1rem;right:1rem}#tail-wrapper{min-height:2rem}#tail-wrapper>div:last-of-type{margin-bottom:2rem}#tail-wrapper #disqus_thread{min-height:8.5rem}.post-tail-bottom a{color:inherit}.share-wrapper .share-icons>i:hover,.share-wrapper .share-icons a:hover>i{color:var(--btn-share-hover-color) !important}.share-wrapper{vertical-align:middle;user-select:none}.share-wrapper .share-icons{font-size:1.2rem}.share-wrapper .share-icons a:not(:last-child){margin-right:0.25rem}.share-wrapper .share-icons a:hover{text-decoration:none}.share-wrapper .share-icons>i{position:relative;bottom:1px}.share-wrapper .share-icons .fab.fa-twitter{color:var(--btn-share-color, #1da1f2)}.share-wrapper .share-icons .fab.fa-facebook-square{color:var(--btn-share-color, #425f9c)}.share-wrapper .share-icons .fab.fa-telegram{color:var(--btn-share-color, #279fd9)}.share-wrapper .share-icons .fab.fa-weibo{color:var(--btn-share-color, #e5142b)}.share-wrapper .fas.fa-link{color:var(--btn-share-color, #ababab)}.share-label{color:inherit;font-size:inherit;font-weight:400}.share-label::after{content:":"}.license-wrapper{line-height:1.2rem}.license-wrapper>a{color:var(--text-color)}.license-wrapper span:last-child{font-size:0.85rem}@media all and (max-width: 576px){.preview-img[data-src]{margin-top:2.2rem}.post-tail-bottom{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.post-tail-bottom>div:first-child{width:100%;margin-top:1rem}}@media all and (max-width: 768px){.post-content>p>img{max-width:calc(100% + 1rem)}}@media all and (max-width: 849px){.post-navigation{padding-left:0;padding-right:0;margin-left:-0.5rem;margin-right:-0.5rem}.preview-img[data-src]{max-width:100vw;border-radius:0}}.tag{border-radius:0.7em;padding:6px 8px 7px;margin-right:0.8rem;line-height:3rem;letter-spacing:0;border:1px solid var(--tag-border) !important;box-shadow:0 0 3px 0 var(--tag-shadow)}.tag span{margin-left:0.6em;font-size:0.7em;font-family:'Oswald', sans-serif}#archives ul li:first-child::before,#archives ul li::after{content:"";width:4px;left:75px;display:inline-block;float:left;position:relative;background-color:var(--timeline-color)}#archives{letter-spacing:0.03rem}#archives span.lead{font-size:1.5rem;position:relative;left:8px}#archives span.lead::after{content:"";display:block;position:relative;-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%;width:12px;height:12px;top:-26px;left:63px;border:3px solid;background-color:var(--timeline-year-dot-color);border-color:var(--timeline-node-bg);box-shadow:0 0 2px 0 #c2c6cc;z-index:1}#archives span.lead:not(:first-child){position:relative;left:4px}#archives span.lead:not(:first-child)::after{left:67px}#archives ul li{font-size:1.1rem;line-height:3rem}#archives ul li div{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#archives ul li div a{margin-left:2.5rem;position:relative;top:0.1rem}#archives ul li:nth-child(odd){background-color:var(--main-wrapper-bg, #fff);background-image:linear-gradient(to left, #fff, #fbfbfb, #fbfbfb, #fbfbfb, #fff)}#archives ul li::after{height:2.8rem;top:-1.3rem}#archives ul li:first-child::before{height:3.06rem;top:-1.61rem}#archives ul:not(:last-child)>li:last-child::after{height:3.4rem}#archives ul:last-child>li:last-child::after{display:none}#archives .date{white-space:nowrap;display:inline-block}#archives .date.month{width:1.4rem;text-align:center}#archives .date.month~a::before{content:"";display:inline-block;position:relative;-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%;width:8px;height:8px;float:left;top:1.35rem;left:69px;background-color:var(--timeline-node-bg);box-shadow:0 0 3px 0 #c2c6cc;z-index:1}#archives .date.day{font-size:85%;font-family:'Lato', sans-serif;text-align:center;margin-right:-2px;width:1.2rem;position:relative;left:-0.15rem}@media all and (max-width: 576px){#archives{margin-top:-1rem}#archives ul{letter-spacing:0}}.categories i{color:gray}.categories{margin-bottom:2rem}.categories .card-header{padding-right:12px}.categories i{font-size:86%}.categories .list-group-item{border-left:none;border-right:none;padding-left:2rem}.categories .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.category-trigger{width:1.7rem;height:1.7rem;border-radius:50%;text-align:center;color:#6c757d !important}.category-trigger:hover i{color:var(--categories-icon-hover-color)}.category-trigger i{position:relative;height:0.7rem;width:1rem;transition:transform 300ms ease}@media (hover: hover){.category-trigger:hover{background-color:var(--categories-hover-bg)}}.rotate{-ms-transform:rotate(-90deg);-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.dash{margin:0 0.5rem 0.6rem 0.5rem;border-bottom:2px dotted var(--dash-color)}#page-category ul>li,#page-tag ul>li{line-height:1.5rem;padding:0.6rem 0}#page-category ul>li::before,#page-tag ul>li::before{background:#999;width:5px;height:5px;border-radius:50%;display:block;content:"";position:relative;top:0.6rem;margin-right:0.5rem}#page-category ul>li>a,#page-tag ul>li>a{font-size:1.1rem}#page-category ul>li>span:last-child,#page-tag ul>li>span:last-child{white-space:nowrap}#page-tag h1>i{font-size:1.2rem}#page-category h1>i{font-size:1.25rem}#page-category a:hover,#page-tag a:hover,#access-lastmod a:hover{margin-bottom:-1px}@media all and (max-width: 576px){#page-category ul>li::before,#page-tag ul>li::before{margin:0 0.5rem}#page-category ul>li>a,#page-tag ul>li>a{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}} + +/*# sourceMappingURL=style.css.map */ \ No newline at end of file diff --git a/assets/css/style.css.map b/assets/css/style.css.map new file mode 100644 index 00000000..c0faa1c9 --- /dev/null +++ b/assets/css/style.css.map @@ -0,0 +1,44 @@ +{ + "version": 3, + "file": "style.css", + "sources": [ + "style.scss", + "vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.1.0/_sass/jekyll-theme-chirpy.scss", + "vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.1.0/_sass/colors/light-typography.scss", + "vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.1.0/_sass/colors/dark-typography.scss", + "vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.1.0/_sass/addon/module.scss", + "vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.1.0/_sass/addon/variables.scss", + "vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.1.0/_sass/variables-hook.scss", + "vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.1.0/_sass/addon/syntax.scss", + "vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.1.0/_sass/colors/light-syntax.scss", + "vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.1.0/_sass/colors/dark-syntax.scss", + "vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.1.0/_sass/addon/commons.scss", + "vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.1.0/_sass/layout/home.scss", + "vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.1.0/_sass/layout/post.scss", + "vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.1.0/_sass/layout/tags.scss", + "vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.1.0/_sass/layout/archives.scss", + "vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.1.0/_sass/layout/categories.scss", + "vendor/bundle/ruby/2.7.0/gems/jekyll-theme-chirpy-5.1.0/_sass/layout/category-tag.scss" + ], + "sourcesContent": [ + "/*\n If the number of TAB files has changed, the following variable is required.\n And it must be defined before `@import`.\n*/\n$tab-count: 7; // plus 1 for home tab\n\n@import \"jekyll-theme-chirpy\";\n\n/* append your custom style below */\n", + "/*!\n * The styles for Jekyll theme Chirpy\n *\n * Chirpy v5.1.0 (https://github.com/cotes2020/jekyll-theme-chirpy)\n * © 2019 Cotes Chung\n * MIT Licensed\n */\n\n@import\n \"colors/light-typography\",\n \"colors/dark-typography\",\n\n \"addon/module\",\n \"addon/variables\",\n \"variables-hook\",\n \"addon/syntax\",\n \"addon/commons\",\n\n \"layout/home\",\n \"layout/post\",\n \"layout/tags\",\n \"layout/archives\",\n \"layout/categories\",\n \"layout/category-tag\";\n", + "/*\n * The syntax light mode typography colors\n */\n\n@mixin light-scheme {\n /* Framework color */\n --body-bg: #fafafa;\n --mask-bg: #c1c3c5;\n --main-wrapper-bg: white;\n --main-border-color: #f3f3f3;\n\n /* Common color */\n --text-color: #34343c;\n --text-muted-color: gray;\n --heading-color: black;\n --blockquote-border-color: #eee;\n --blockquote-text-color: #9a9a9a;\n --link-color: #2a408e;\n --link-underline-color: #dee2e6;\n --button-bg: #fff;\n --btn-border-color: #e9ecef;\n --btn-backtotop-color: #686868;\n --btn-backtotop-border-color: #f1f1f1;\n --btn-box-shadow: #eaeaea;\n --checkbox-color: #c5c5c5;\n --checkbox-checked-color: #07a8f7;\n\n /* Sidebar */\n --sidebar-bg: #eeeeee;\n --sidebar-muted-color: #a2a19f;\n --sidebar-active-color: #424242;\n --nav-cursor-color: #757575;\n --sidebar-btn-bg: white;\n\n /* Topbar */\n --topbar-text-color: rgb(78, 78, 78);\n --topbar-wrapper-bg: white;\n --search-wrapper-bg: rgb(245 245 245 / 50%);\n --search-wrapper-border-color: rgb(245 245 245);\n --search-tag-bg: #f8f9fa;\n --search-icon-color: #c2c6cc;\n --input-focus-border-color: var(--btn-border-color);\n\n /* Home page */\n --post-list-text-color: dimgray;\n --btn-patinator-text-color: #555555;\n --btn-paginator-hover-color: var(--sidebar-bg);\n --btn-paginator-border-color: var(--sidebar-bg);\n --btn-text-color: #676666;\n --pin-bg: #f5f5f5;\n --pin-color: #999fa4;\n\n /* Posts */\n --btn-share-hover-color: var(--link-color);\n --card-border-color: #f1f1f1;\n --card-box-shadow: rgba(234, 234, 234, 0.7686274509803922);\n --label-color: #616161;\n --relate-post-date: rgba(30, 55, 70, 0.4);\n --footnote-target-bg: lightcyan;\n --tag-bg: rgba(0, 0, 0, 0.075);\n --tag-border: #dee2e6;\n --tag-shadow: var(--btn-border-color);\n --tag-hover: rgb(222, 226, 230);\n --tb-odd-bg: #fbfcfd;\n --tb-border-color: #eaeaea;\n --dash-color: silver;\n --preview-img-bg: radial-gradient(circle, rgb(255 255 255) 0%, rgb(249 249 249) 100%);\n --kbd-wrap-color: #bdbdbd;\n --kbd-text-color: var(--text-color);\n --kbd-bg-color: white;\n --prompt-text-color: rgb(46 46 46 / 77%);\n --prompt-tip-bg: rgb(123 247 144 / 20%);\n --prompt-tip-icon-color: #03b303;\n --prompt-info-bg: #e1f5fe;\n --prompt-info-icon-color: #0070cb;\n --prompt-warning-bg: rgb(255 243 205);\n --prompt-warning-icon-color: #ef9c03;\n --prompt-danger-bg: rgb(248 215 218 / 56%);\n --prompt-danger-icon-color: #df3c30;\n\n [class^=prompt-] {\n --link-underline-color: rgb(219 216 216);\n }\n\n /* Categories */\n --categories-hover-bg: var(--btn-border-color);\n --categories-icon-hover-color: darkslategray;\n\n /* Archive */\n --timeline-color: rgba(0, 0, 0, 0.075);\n --timeline-node-bg: #c2c6cc;\n --timeline-year-dot-color: #ffffff;\n\n} /* light-scheme */\n", + "/*\n * The main dark mode styles\n */\n\n@mixin dark-scheme {\n /* Framework color */\n --body-bg: var(--main-wrapper-bg);\n --mask-bg: rgb(68, 69, 70);\n --main-wrapper-bg: rgb(27, 27, 30);\n --main-border-color: rgb(44, 45, 45);\n\n /* Common color */\n --text-color: rgb(175, 176, 177);\n --text-muted-color: rgb(107, 116, 124);\n --heading-color: #cccccc;\n --blockquote-border-color: rgb(66, 66, 66);\n --blockquote-text-color: rgb(117, 117, 117);\n --link-color: rgb(138, 180, 248);\n --link-underline-color: rgb(82, 108, 150);\n --button-bg: rgb(39, 40, 43);\n --btn-border-color: rgb(63, 65, 68);\n --btn-backtotop-color: var(--text-color);\n --btn-backtotop-border-color: var(--btn-border-color);\n --btn-box-shadow: var(--main-wrapper-bg);\n --card-header-bg: rgb(51, 50, 50);\n --label-color: rgb(108, 117, 125);\n --checkbox-color: rgb(118 120 121);\n --checkbox-checked-color: var(--link-color);\n\n /* Sidebar */\n --sidebar-bg: radial-gradient(circle, #242424 0%, #1d1f27 100%);\n --sidebar-muted-color: #6d6c6b;\n --sidebar-active-color: rgb(255 255 255 / 80%);\n --nav-cursor-color: rgb(183, 182, 182);\n --sidebar-btn-bg: rgb(117 116 116 / 20%);\n\n /* Topbar */\n --topbar-text-color: var(--text-color);\n --topbar-wrapper-bg: rgb(39, 40, 43);\n --search-wrapper-bg: rgb(34, 34, 39);\n --search-wrapper-border-color: rgb(34, 34, 39);\n --search-icon-color: rgb(100, 102, 105);\n --input-focus-border-color: rgb(112, 114, 115);\n\n /* Home page */\n --post-list-text-color: rgb(175, 176, 177);\n --btn-patinator-text-color: var(--text-color);\n --btn-paginator-hover-color: rgb(64, 65, 66);\n --btn-paginator-border-color: var(--btn-border-color);\n --btn-text-color: var(--text-color);\n --pin-bg: rgb(34 35 37);\n --pin-color: inherit;\n\n /* Posts */\n --toc-highlight: rgb(116, 178, 243);\n --tag-bg: rgb(41, 40, 40);\n --tag-hover: rgb(43, 56, 62);\n --tb-odd-bg: rgba(42, 47, 53, 0.52); /* odd rows of the posts' table */\n --tb-even-bg: rgb(31, 31, 34); /* even rows of the posts' table */\n --tb-border-color: var(--tb-odd-bg);\n --footnote-target-bg: rgb(63, 81, 181);\n --btn-share-color: #6c757d;\n --btn-share-hover-color: #bfc1ca;\n --relate-post-date: var(--text-muted-color);\n --card-bg: rgb(39, 40, 43);\n --card-border-color: rgb(53, 53, 60);\n --card-box-shadow: var(--main-wrapper-bg);\n --preview-img-bg: radial-gradient(circle, rgb(22 22 24) 0%, rgb(32 32 32) 100%);\n --kbd-wrap-color: #6a6a6a;\n --kbd-text-color: #d3d3d3;\n --kbd-bg-color: #242424;\n --prompt-text-color: rgb(216 212 212 / 75%);\n --prompt-tip-bg: rgba(77, 187, 95, 0.2);\n --prompt-tip-icon-color: rgb(5 223 5 / 68%);\n --prompt-info-bg: rgb(7 59 104 / 80%);\n --prompt-info-icon-color: #0075d1;\n --prompt-warning-bg: rgb(90 69 3 / 95%);\n --prompt-warning-icon-color: rgb(255 165 0 / 80%);\n --prompt-danger-bg: rgb(86 28 8 / 80%);\n --prompt-danger-icon-color: #cd0202;\n\n /* tags */\n --tag-border: rgb(59, 79, 88);\n --tag-shadow: rgb(32, 33, 33);\n --search-tag-bg: var(--tag-bg);\n --dash-color: rgb(63, 65, 68);\n\n /* categories */\n --categories-border: rgb(64, 66, 69);\n --categories-hover-bg: rgb(73, 75, 76);\n --categories-icon-hover-color: white;\n\n /* archives */\n --timeline-node-bg: rgb(150, 152, 156);\n --timeline-color: rgb(63, 65, 68);\n --timeline-year-dot-color: var(--timeline-color);\n\n .post img[data-src] {\n filter: brightness(95%);\n }\n\n hr {\n border-color: var(--main-border-color);\n }\n\n /* posts' toc, override BS */\n nav[data-toggle=toc] .nav-link.active,\n nav[data-toggle=toc] .nav-link.active:focus,\n nav[data-toggle=toc] .nav-link.active:hover,\n nav[data-toggle=toc] .nav > li > a:focus,\n nav[data-toggle=toc] .nav > li > a:hover {\n color: var(--toc-highlight) !important;\n border-left-color: var(--toc-highlight) !important;\n }\n\n /* categories */\n .categories.card,\n .list-group-item {\n background-color: var(--card-bg);\n }\n\n .categories {\n .card-header {\n background-color: var(--card-header-bg);\n }\n\n .list-group-item {\n border-left: none;\n border-right: none;\n padding-left: 2rem;\n border-color: var(--categories-border);\n\n &:last-child {\n border-bottom-color: var(--card-bg);\n }\n }\n }\n\n #archives li:nth-child(odd) {\n background-image:\n linear-gradient(\n to left,\n rgb(26, 26, 30),\n rgb(39, 39, 45),\n rgb(39, 39, 45),\n rgb(39, 39, 45),\n rgb(26, 26, 30)\n );\n }\n\n color-scheme: dark;\n\n #disqus_thread {\n color-scheme: none;\n }\n\n} /* dark-scheme */\n", + "/*\n* Mainly scss modules, only imported to `assets/css/main.scss`\n*/\n\n/* ---------- scss placeholder --------- */\n\n%heading {\n color: var(--heading-color);\n font-weight: 400;\n font-family: 'Lato', 'Microsoft Yahei', sans-serif;\n}\n\n%section {\n #core-wrapper & {\n margin-top: 2.5rem;\n margin-bottom: 1.25rem;\n\n &:focus {\n outline: none; /* avoid outline in Safari */\n }\n }\n}\n\n%anchor {\n .anchor {\n font-size: 80%;\n }\n\n @media (hover: hover) {\n .anchor {\n visibility: hidden;\n opacity: 0;\n transition: opacity 0.25s ease-in, visibility 0s ease-in 0.25s;\n }\n\n &:hover {\n .anchor {\n visibility: visible;\n opacity: 1;\n transition: opacity 0.25s ease-in, visibility 0s ease-in 0s;\n }\n }\n }\n}\n\n%tag-hover {\n background: var(--tag-hover);\n transition: background 0.35s ease-in-out;\n}\n\n%table-cell {\n padding: 0.4rem 1rem;\n font-size: 95%;\n white-space: nowrap;\n}\n\n%link-hover {\n color: #d2603a !important;\n border-bottom: 1px solid #d2603a;\n text-decoration: none;\n}\n\n%link-color {\n color: var(--link-color);\n}\n\n%link-underline {\n border-bottom: 1px solid var(--link-underline-color);\n}\n\n%clickable-transition {\n transition: color 0.35s ease-in-out;\n}\n\n%no-cursor {\n user-select: none;\n}\n\n%no-bottom-border {\n border-bottom: none;\n}\n\n%cursor-pointer {\n cursor: pointer;\n}\n\n%normal-font-style {\n font-style: normal;\n}\n\n%img-caption {\n + em {\n display: block;\n text-align: center;\n font-style: normal;\n font-size: 80%;\n padding: 0;\n color: #6d6c6c;\n }\n}\n\n%sidebar-links {\n color: rgba(117, 117, 117, 0.9);\n user-select: none;\n}\n\n/* ---------- scss mixin --------- */\n\n@mixin no-text-decoration {\n text-decoration: none;\n}\n\n@mixin ml-mr($value) {\n margin-left: $value;\n margin-right: $value;\n}\n\n@mixin pl-pr($val) {\n padding-left: $val;\n padding-right: $val;\n}\n\n@mixin input-placeholder {\n opacity: 0.6;\n}\n\n@mixin label($font-size: 1rem, $font-weight: 600, $color: var(--label-color)) {\n color: $color;\n font-size: $font-size;\n font-weight: $font-weight;\n}\n\n@mixin align-center {\n position: relative;\n left: 50%;\n -webkit-transform: translateX(-50%);\n -ms-transform: translateX(-50%);\n transform: translateX(-50%);\n}\n\n@mixin prompt($type, $fw-icon, $icon-weight: 900) {\n &.prompt-#{$type} {\n background-color: var(--prompt-#{$type}-bg);\n\n &::before {\n content: $fw-icon;\n color: var(--prompt-#{$type}-icon-color);\n font-weight: $icon-weight;\n }\n }\n}\n", + "/*\n * The SCSS variables\n */\n\n/* sidebar */\n\n$sidebar-width: 260px !default; /* the basic width */\n$sidebar-width-small: 210px !default; /* screen width: >= 850px, <= 1199px (iPad landscape) */\n$sidebar-width-large: 350px !default; /* screen width: >= 1650px */\n\n/* tabs of sidebar */\n\n$tab-count: 5 !default; /* backward compatible (version <= 4.0.2) */\n$tab-height: 3rem !default;\n$tab-cursor-height: 1.6rem !default;\n\n$cursor-width: 2px !default; /* the cursor width of the selected tab */\n\n/* other framework sizes */\n\n$topbar-height: 3rem !default;\n\n$footer-height: 5rem !default;\n$footer-height-mobile: 6rem !default; /* screen width: <= 576px */\n\n$main-content-max-width: 1150px !default;\n\n$panel-max-width: 300px !default;\n\n$bottom-min-height: 35rem !default;\n\n/* syntax highlight */\n\n$code-font-size: 0.85rem !default;\n", + "/*\n Appending custom SCSS variables will override the default ones in `_sass/addon/variables.scsss`\n*/\n", + "/*\n* The syntax highlight.\n*/\n\n@import \"colors/light-syntax\";\n@import \"colors/dark-syntax\";\n\nhtml {\n @media (prefers-color-scheme: light) {\n &:not([data-mode]),\n [data-mode=light] {\n @include light-syntax;\n }\n\n &[data-mode=dark] {\n @include dark-syntax;\n }\n }\n\n @media (prefers-color-scheme: dark) {\n &:not([data-mode]),\n &[data-mode=dark] {\n @include dark-syntax;\n }\n\n &[data-mode=light] {\n @include light-syntax;\n }\n }\n}\n\n/* -- Codes Snippet -- */\n\n$code-radius: 6px;\n\n%code-snippet-bg {\n background: var(--highlight-bg-color);\n}\n\n%code-snippet-radius {\n border-radius: $code-radius;\n}\n\n%code-snippet-padding {\n padding-left: 1rem;\n padding-right: 1.5rem;\n}\n\n.highlighter-rouge {\n @extend %code-snippet-bg;\n @extend %code-snippet-radius;\n\n color: var(--highlighter-rouge-color);\n margin-top: 0.5rem;\n margin-bottom: 1.2em; /* Override BS Inline-code style */\n}\n\n.highlight {\n @extend %code-snippet-radius;\n @extend %code-snippet-bg;\n\n @at-root figure#{&} {\n @extend %code-snippet-bg;\n }\n\n overflow: auto;\n padding-top: 0.5rem;\n padding-bottom: 1rem;\n\n pre {\n margin-bottom: 0;\n font-size: $code-font-size;\n line-height: 1.4rem;\n word-wrap: normal; /* Fixed Safari overflow-x */\n }\n\n table {\n td pre {\n overflow: visible; /* Fixed iOS safari overflow-x */\n word-break: normal; /* Fixed iOS safari linenos code break */\n }\n }\n\n .lineno {\n padding-right: 0.5rem;\n min-width: 2.2rem;\n text-align: right;\n color: var(--highlight-lineno-color);\n -webkit-user-select: none;\n -khtml-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n -o-user-select: none;\n user-select: none;\n }\n\n /* set the dollar sign to non-selectable */\n .gp {\n user-select: none;\n }\n\n} /* .highlight */\n\ncode {\n -webkit-hyphens: none;\n -ms-hyphens: none;\n -moz-hyphens: none;\n hyphens: none;\n\n &.highlighter-rouge {\n font-size: $code-font-size;\n padding: 3px 5px;\n border-radius: 4px;\n background-color: var(--inline-code-bg);\n }\n\n &.filepath {\n background-color: inherit;\n color: var(--filepath-text-color);\n font-weight: 600;\n padding: 0;\n }\n\n a > &.highlighter-rouge {\n padding-bottom: 0; /* show link's underlinke */\n color: inherit;\n }\n\n a:hover > &.highlighter-rouge {\n border-bottom: none;\n }\n\n blockquote & {\n color: inherit;\n }\n\n .highlight > & {\n color: transparent;\n }\n}\n\ntd.rouge-code {\n @extend %code-snippet-padding;\n\n /*\n Prevent some browser extends from\n changing the URL string of code block.\n */\n a {\n color: inherit !important;\n border-bottom: none !important;\n pointer-events: none;\n }\n\n}\n\n/* Hide line numbers for default, console, and terminal code snippets */\ndiv {\n &[class^='highlighter-rouge'],\n &.language-plaintext.highlighter-rouge,\n &.language-console.highlighter-rouge,\n &.language-terminal.highlighter-rouge,\n &.nolineno {\n pre.lineno {\n display: none;\n }\n\n td.rouge-code {\n padding-left: 1.5rem;\n }\n }\n}\n\n.code-header {\n @extend %no-cursor;\n\n $code-header-height: 2.25rem;\n\n border-top-left-radius: $code-radius;\n border-top-right-radius: $code-radius;\n display: flex;\n justify-content: space-between;\n align-items: center;\n height: $code-header-height;\n\n &::before {\n $dot-size: 0.75rem;\n $dot-margin: 0.5rem;\n\n content: \"\";\n display: inline-block;\n margin-left: 1rem;\n width: $dot-size;\n height: $dot-size;\n border-radius: 50%;\n background-color: var(--code-header-muted-color);\n box-shadow:\n ($dot-size + $dot-margin) 0 0 var(--code-header-muted-color),\n ($dot-size + $dot-margin) * 2 0 0 var(--code-header-muted-color);\n }\n\n /* the label block */\n span {\n /* label icon */\n i {\n font-size: 1rem;\n margin-right: 0.4rem;\n color: var(--code-header-icon-color);\n\n &.small {\n font-size: 70%;\n }\n }\n\n @at-root [file] #{&} > i {\n position: relative;\n top: 1px; /* center the file icon */\n }\n\n /* label text */\n &::after {\n content: attr(data-label-text);\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--code-header-text-color);\n }\n }\n\n /* clipboard */\n button {\n @extend %cursor-pointer;\n\n border: 1px solid transparent;\n border-radius: $code-radius;\n height: $code-header-height;\n width: $code-header-height;\n padding: 0;\n background-color: inherit;\n\n i {\n color: var(--code-header-icon-color);\n }\n\n &[timeout] {\n &:hover {\n border-color: var(--clipboard-checked-color);\n }\n\n i {\n color: var(--clipboard-checked-color);\n }\n }\n\n &:not([timeout]):hover {\n background-color: rgba(128, 128, 128, 0.37);\n\n i {\n color: white;\n }\n }\n\n &:focus {\n outline: none;\n }\n\n }\n\n}\n\n@media all and (max-width: 576px) {\n .post-content {\n > div[class^='language-'] {\n @include ml-mr(-1.25rem);\n\n border-radius: 0;\n\n .highlight {\n padding-left: 0.25rem;\n }\n\n .code-header {\n border-radius: 0;\n padding-left: 0.4rem;\n padding-right: 0.5rem;\n }\n }\n }\n}\n", + "/*\n * The syntax light mode code snippet colors.\n */\n\n@mixin light-syntax {\n /* see: */\n .highlight .hll { background-color: #ffffcc; }\n .highlight .c { color: #999988; font-style: italic; } /* Comment */\n .highlight .err { color: #a61717; background-color: #e3d2d2; } /* Error */\n .highlight .k { color: #000000; font-weight: bold; } /* Keyword */\n .highlight .o { color: #000000; font-weight: bold; } /* Operator */\n .highlight .cm { color: #999988; font-style: italic; } /* Comment.Multiline */\n .highlight .cp { color: #999999; font-weight: bold; font-style: italic; } /* Comment.Preproc */\n .highlight .c1 { color: #999988; font-style: italic; } /* Comment.Single */\n .highlight .cs { color: #999999; font-weight: bold; font-style: italic; } /* Comment.Special */\n .highlight .gd { color: #d01040; background-color: #ffdddd; } /* Generic.Deleted */\n .highlight .ge { color: #000000; font-style: italic; } /* Generic.Emph */\n .highlight .gr { color: #aa0000; } /* Generic.Error */\n .highlight .gh { color: #999999; } /* Generic.Heading */\n .highlight .gi { color: #008080; background-color: #ddffdd; } /* Generic.Inserted */\n .highlight .go { color: #888888; } /* Generic.Output */\n .highlight .gp { color: #555555; } /* Generic.Prompt */\n .highlight .gs { font-weight: bold; } /* Generic.Strong */\n .highlight .gu { color: #aaaaaa; } /* Generic.Subheading */\n .highlight .gt { color: #aa0000; } /* Generic.Traceback */\n .highlight .kc { color: #000000; font-weight: bold; } /* Keyword.Constant */\n .highlight .kd { color: #000000; font-weight: bold; } /* Keyword.Declaration */\n .highlight .kn { color: #000000; font-weight: bold; } /* Keyword.Namespace */\n .highlight .kp { color: #000000; font-weight: bold; } /* Keyword.Pseudo */\n .highlight .kr { color: #000000; font-weight: bold; } /* Keyword.Reserved */\n .highlight .kt { color: #445588; font-weight: bold; } /* Keyword.Type */\n .highlight .m { color: #009999; } /* Literal.Number */\n .highlight .s { color: #d01040; } /* Literal.String */\n .highlight .na { color: #008080; } /* Name.Attribute */\n .highlight .nb { color: #0086b3; } /* Name.Builtin */\n .highlight .nc { color: #445588; font-weight: bold; } /* Name.Class */\n .highlight .no { color: #008080; } /* Name.Constant */\n .highlight .nd { color: #3c5d5d; font-weight: bold; } /* Name.Decorator */\n .highlight .ni { color: #800080; } /* Name.Entity */\n .highlight .ne { color: #990000; font-weight: bold; } /* Name.Exception */\n .highlight .nf { color: #990000; font-weight: bold; } /* Name.Function */\n .highlight .nl { color: #990000; font-weight: bold; } /* Name.Label */\n .highlight .nn { color: #555555; } /* Name.Namespace */\n .highlight .nt { color: #000080; } /* Name.Tag */\n .highlight .nv { color: #008080; } /* Name.Variable */\n .highlight .ow { color: #000000; font-weight: bold; } /* Operator.Word */\n .highlight .w { color: #bbbbbb; } /* Text.Whitespace */\n .highlight .mf { color: #009999; } /* Literal.Number.Float */\n .highlight .mh { color: #009999; } /* Literal.Number.Hex */\n .highlight .mi { color: #009999; } /* Literal.Number.Integer */\n .highlight .mo { color: #009999; } /* Literal.Number.Oct */\n .highlight .sb { color: #d01040; } /* Literal.String.Backtick */\n .highlight .sc { color: #d01040; } /* Literal.String.Char */\n .highlight .sd { color: #d01040; } /* Literal.String.Doc */\n .highlight .s2 { color: #d01040; } /* Literal.String.Double */\n .highlight .se { color: #d01040; } /* Literal.String.Escape */\n .highlight .sh { color: #d01040; } /* Literal.String.Heredoc */\n .highlight .si { color: #d01040; } /* Literal.String.Interpol */\n .highlight .sx { color: #d01040; } /* Literal.String.Other */\n .highlight .sr { color: #009926; } /* Literal.String.Regex */\n .highlight .s1 { color: #d01040; } /* Literal.String.Single */\n .highlight .ss { color: #990073; } /* Literal.String.Symbol */\n .highlight .bp { color: #999999; } /* Name.Builtin.Pseudo */\n .highlight .vc { color: #008080; } /* Name.Variable.Class */\n .highlight .vg { color: #008080; } /* Name.Variable.Global */\n .highlight .vi { color: #008080; } /* Name.Variable.Instance */\n .highlight .il { color: #009999; } /* Literal.Number.Integer.Long */\n\n /* --- custom light colors --- */\n --highlight-bg-color: #f7f7f7;\n --highlighter-rouge-color: #2f2f2f;\n --highlight-lineno-color: #c2c6cc;\n --inline-code-bg: #f3f3f3;\n --code-header-text-color: #a3a3b1;\n --code-header-muted-color: #ebebeb;\n --code-header-icon-color: #d1d1d1;\n --clipboard-checked-color: #43c743;\n\n [class^=prompt-] {\n --inline-code-bg: #fbfafa;\n --highlighter-rouge-color: rgb(82 82 82);\n }\n\n} /* light-syntax */\n", + "/*\n * The syntax dark mode styles.\n */\n\n@mixin dark-syntax {\n /* syntax highlight colors from https://raw.githubusercontent.com/jwarby/pygments-css/master/monokai.css */\n .highlight pre { background-color: var(--highlight-bg-color); }\n .highlight .hll { background-color: var(--highlight-bg-color); }\n .highlight .c { color: #75715e; } /* Comment */\n .highlight .err { color: #960050; background-color: #1e0010; } /* Error */\n .highlight .k { color: #66d9ef; } /* Keyword */\n .highlight .l { color: #ae81ff; } /* Literal */\n .highlight .n { color: #f8f8f2; } /* Name */\n .highlight .o { color: #f92672; } /* Operator */\n .highlight .p { color: #f8f8f2; } /* Punctuation */\n .highlight .cm { color: #75715e; } /* Comment.Multiline */\n .highlight .cp { color: #75715e; } /* Comment.Preproc */\n .highlight .c1 { color: #75715e; } /* Comment.Single */\n .highlight .cs { color: #75715e; } /* Comment.Special */\n .highlight .ge { color: inherit; font-style: italic; } /* Generic.Emph */\n .highlight .gs { font-weight: bold; } /* Generic.Strong */\n .highlight .kc { color: #66d9ef; } /* Keyword.Constant */\n .highlight .kd { color: #66d9ef; } /* Keyword.Declaration */\n .highlight .kn { color: #f92672; } /* Keyword.Namespace */\n .highlight .kp { color: #66d9ef; } /* Keyword.Pseudo */\n .highlight .kr { color: #66d9ef; } /* Keyword.Reserved */\n .highlight .kt { color: #66d9ef; } /* Keyword.Type */\n .highlight .ld { color: #e6db74; } /* Literal.Date */\n .highlight .m { color: #ae81ff; } /* Literal.Number */\n .highlight .s { color: #e6db74; } /* Literal.String */\n .highlight .na { color: #a6e22e; } /* Name.Attribute */\n .highlight .nb { color: #f8f8f2; } /* Name.Builtin */\n .highlight .nc { color: #a6e22e; } /* Name.Class */\n .highlight .no { color: #66d9ef; } /* Name.Constant */\n .highlight .nd { color: #a6e22e; } /* Name.Decorator */\n .highlight .ni { color: #f8f8f2; } /* Name.Entity */\n .highlight .ne { color: #a6e22e; } /* Name.Exception */\n .highlight .nf { color: #a6e22e; } /* Name.Function */\n .highlight .nl { color: #f8f8f2; } /* Name.Label */\n .highlight .nn { color: #f8f8f2; } /* Name.Namespace */\n .highlight .nx { color: #a6e22e; } /* Name.Other */\n .highlight .py { color: #f8f8f2; } /* Name.Property */\n .highlight .nt { color: #f92672; } /* Name.Tag */\n .highlight .nv { color: #f8f8f2; } /* Name.Variable */\n .highlight .ow { color: #f92672; } /* Operator.Word */\n .highlight .w { color: #f8f8f2; } /* Text.Whitespace */\n .highlight .mf { color: #ae81ff; } /* Literal.Number.Float */\n .highlight .mh { color: #ae81ff; } /* Literal.Number.Hex */\n .highlight .mi { color: #ae81ff; } /* Literal.Number.Integer */\n .highlight .mo { color: #ae81ff; } /* Literal.Number.Oct */\n .highlight .sb { color: #e6db74; } /* Literal.String.Backtick */\n .highlight .sc { color: #e6db74; } /* Literal.String.Char */\n .highlight .sd { color: #e6db74; } /* Literal.String.Doc */\n .highlight .s2 { color: #e6db74; } /* Literal.String.Double */\n .highlight .se { color: #ae81ff; } /* Literal.String.Escape */\n .highlight .sh { color: #e6db74; } /* Literal.String.Heredoc */\n .highlight .si { color: #e6db74; } /* Literal.String.Interpol */\n .highlight .sx { color: #e6db74; } /* Literal.String.Other */\n .highlight .sr { color: #e6db74; } /* Literal.String.Regex */\n .highlight .s1 { color: #e6db74; } /* Literal.String.Single */\n .highlight .ss { color: #e6db74; } /* Literal.String.Symbol */\n .highlight .bp { color: #f8f8f2; } /* Name.Builtin.Pseudo */\n .highlight .vc { color: #f8f8f2; } /* Name.Variable.Class */\n .highlight .vg { color: #f8f8f2; } /* Name.Variable.Global */\n .highlight .vi { color: #f8f8f2; } /* Name.Variable.Instance */\n .highlight .il { color: #ae81ff; } /* Literal.Number.Integer.Long */\n .highlight .gu { color: #75715e; } /* Generic.Subheading & Diff Unified/Comment? */\n .highlight .gd { color: #f92672; background-color: #561c08; } /* Generic.Deleted & Diff Deleted */\n .highlight .gi { color: #a6e22e; background-color: #0b5858; } /* Generic.Inserted & Diff Inserted */\n\n /* ----- custom styles ------ */\n\n --highlight-bg-color: #252525;\n --highlighter-rouge-color: #de6b18;\n --highlight-lineno-color: #6c6c6d;\n --inline-code-bg: #272822;\n --code-header-text-color: #6a6a6a;\n --code-header-muted-color: rgb(60 60 60);\n --code-header-icon-color: rgb(86 86 86);\n --clipboard-checked-color: #2bcc2b;\n --filepath-text-color: #bdbdbd;\n\n .highlight {\n .gp { color: #818c96; }\n }\n\n pre { color: #bfbfbf; } /* override Bootstrap */\n}\n", + "/*\n The common styles\n*/\n\nhtml {\n @media (prefers-color-scheme: light) {\n &:not([data-mode]),\n [data-mode=light] {\n @include light-scheme;\n }\n\n &[data-mode=dark] {\n @include dark-scheme;\n }\n }\n\n @media (prefers-color-scheme: dark) {\n &:not([data-mode]),\n &[data-mode=dark] {\n @include dark-scheme;\n }\n\n &[data-mode=light] {\n @include light-scheme;\n }\n }\n\n font-size: 16px;\n}\n\nbody {\n line-height: 1.75rem;\n background: var(--body-bg);\n color: var(--text-color);\n -webkit-font-smoothing: antialiased;\n font-family: 'Source Sans Pro', 'Microsoft Yahei', sans-serif;\n}\n\n/* --- Typography --- */\n\nh1 {\n @extend %heading;\n\n font-size: 1.9rem;\n}\n\nh2 {\n @extend %heading;\n @extend %section;\n @extend %anchor;\n\n font-size: 1.5rem;\n}\n\nh3 {\n @extend %heading;\n @extend %section;\n @extend %anchor;\n\n font-size: 1.2rem;\n}\n\nh4 {\n @extend %heading;\n @extend %section;\n @extend %anchor;\n\n font-size: 1.15rem;\n}\n\nh5 {\n @extend %heading;\n @extend %section;\n @extend %anchor;\n\n font-size: 1.1rem;\n}\n\nol,\nul {\n ol,\n ul {\n margin-bottom: 1rem;\n }\n}\n\na {\n @extend %link-color;\n}\n\nimg {\n max-width: 100%;\n height: auto;\n}\n\nblockquote {\n border-left: 5px solid var(--blockquote-border-color);\n padding-left: 1rem;\n color: var(--blockquote-text-color);\n\n &[class^=\"prompt-\"] {\n display: flex;\n border-left: 0;\n border-radius: 6px;\n padding: 0.75rem 1.2rem;\n color: var(--prompt-text-color);\n\n &::before {\n margin-right: 1rem;\n font-family: \"Font Awesome 5 Free\";\n text-align: center;\n width: 1.25rem;\n }\n\n p:last-child {\n margin-bottom: 0rem;\n }\n }\n\n @include prompt(\"tip\", \"\\f0eb\", 400);\n\n @include prompt(\"info\", \"\\f06a\");\n\n @include prompt(\"warning\", \"\\f06a\");\n\n @include prompt(\"danger\", \"\\f071\");\n}\n\nkbd {\n font-family: inherit;\n display: inline-block;\n vertical-align: middle;\n line-height: 1.3rem;\n min-width: 1.75rem;\n text-align: center;\n margin: 0 0.3rem;\n padding-top: 0.1rem;\n color: var(--kbd-text-color);\n background-color: var(--kbd-bg-color);\n border-radius: 0.25rem;\n border: solid 1px var(--kbd-wrap-color);\n box-shadow: inset 0 -2px 0 var(--kbd-wrap-color);\n}\n\nfooter {\n position: absolute;\n bottom: 0;\n padding: 0 1rem;\n height: $footer-height;\n font-size: 0.8rem;\n\n > div.d-flex {\n line-height: 1.2rem;\n width: 95%;\n max-width: 1045px;\n border-top: 1px solid var(--main-border-color);\n margin-bottom: 1rem;\n\n > div {\n width: 350px;\n }\n }\n\n a {\n @extend %text-color;\n\n &:link {\n @include no-text-decoration;\n }\n\n &:hover {\n @extend %link-hover;\n\n @include no-text-decoration;\n }\n }\n\n .footer-right {\n text-align: right;\n }\n}\n\ni { /* fontawesome icons */\n &.far,\n &.fas {\n @extend %no-cursor;\n }\n}\n\n@keyframes fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\nimg[data-src] {\n margin: 0.5rem 0;\n\n &[data-loaded=true] {\n animation: fade-in linear 0.5s;\n }\n\n &.left {\n float: left;\n margin: 0.75rem 1rem 1rem 0;\n }\n\n &.right {\n float: right;\n margin: 0.75rem 0 1rem 1rem;\n }\n\n &.shadow {\n filter: drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.08));\n box-shadow: none !important; /* cover the Bootstrap 4.6.1 styles */\n }\n\n @extend %img-caption;\n}\n\n/* --- Panels --- */\n\n.access {\n top: 2rem;\n transition: top 0.2s ease-in-out;\n margin-right: 1.5rem;\n margin-top: 3rem;\n margin-bottom: 4rem;\n\n &:only-child {\n position: -webkit-sticky; /* Safari */\n position: sticky;\n }\n\n > div {\n padding-left: 1rem;\n border-left: 1px solid var(--main-border-color);\n\n &:not(:last-child) {\n margin-bottom: 4rem;\n }\n }\n\n .post-content {\n font-size: 0.9rem;\n }\n\n}\n\n#panel-wrapper {\n /* the headings */\n .panel-heading {\n @include label(inherit);\n }\n\n .post-tag {\n display: inline-block;\n line-height: 1rem;\n font-size: 0.85rem;\n background: none;\n border: 1px solid var(--btn-border-color);\n border-radius: 0.8rem;\n padding: 0.3rem 0.5rem;\n margin: 0 0.35rem 0.5rem 0;\n\n &:hover {\n background-color: #2a408e;\n border-color: #2a408e;\n color: #fff;\n transition: none;\n }\n }\n\n [data-topbar-visible=true] & > div {\n top: 6rem;\n }\n}\n\n#access-lastmod {\n li {\n height: 1.8rem;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 1;\n -webkit-box-orient: vertical;\n list-style: none;\n }\n\n a {\n &:hover {\n @extend %link-hover;\n }\n\n @extend %no-bottom-border;\n\n color: inherit;\n }\n\n}\n\n.footnotes > ol {\n padding-left: 2rem;\n margin-top: 0.5rem;\n\n > li {\n &:not(:last-child) {\n margin-bottom: 0.3rem;\n }\n\n > p {\n margin-left: 0.25em;\n margin-top: 0;\n margin-bottom: 0;\n }\n\n /* [scroll-focus] added by `smooth-scroll.js` */\n &:target:not([scroll-focus]),\n &[scroll-focus=true] > p {\n background-color: var(--footnote-target-bg);\n width: fit-content;\n -webkit-transition: background-color 1.5s ease-in-out; /* Safari prior 6.1 */\n transition: background-color 1.5s ease-in-out;\n }\n }\n}\n\n.footnote {\n @at-root a#{&} {\n @include ml-mr(1px);\n @include pl-pr(2px);\n\n border-bottom-style: none !important;\n -webkit-transition: background-color 1.5s ease-in-out; /* Safari prior 6.1 */\n transition: background-color 1.5s ease-in-out;\n }\n\n /* [scroll-focus] added by `smooth-scroll.js` */\n @at-root sup:target:not([scroll-focus]),\n sup[scroll-focus=true] > a#{&} {\n background-color: var(--footnote-target-bg);\n }\n}\n\n.reversefootnote {\n @at-root a#{&} {\n font-size: 0.6rem;\n line-height: 1;\n position: relative;\n bottom: 0.25em;\n margin-left: 0.25em;\n border-bottom-style: none !important;\n }\n}\n\n/* --- Begin of Markdown table style --- */\n\n/* it will be created by Liquid */\n.table-wrapper {\n overflow-x: auto;\n margin-bottom: 1.5rem;\n\n > table {\n min-width: 100%;\n overflow-x: auto;\n border-spacing: 0;\n\n thead {\n border-bottom: solid 2px rgba(210, 215, 217, 0.75);\n\n th {\n @extend %table-cell;\n }\n }\n\n tbody {\n tr {\n border-bottom: 1px solid var(--tb-border-color);\n\n &:nth-child(2n) {\n background-color: var(--tb-even-bg);\n }\n\n &:nth-child(2n + 1) {\n background-color: var(--tb-odd-bg);\n }\n\n td {\n @extend %table-cell;\n }\n }\n } /* tbody */\n }/* table */\n}\n\n/* --- post --- */\n\n.post {\n h1 {\n margin-top: 3rem;\n margin-bottom: 1.5rem;\n }\n\n a {\n &.img-link {\n @extend %no-cursor;\n }\n\n /* created by `_includes/img-extra.html` */\n &.popup {\n cursor: zoom-in;\n\n > img[data-src]:not(.normal):not(.left):not(.right) {\n @include align-center;\n }\n }\n\n &:hover {\n code {\n @extend %link-hover;\n }\n }\n } /* a */\n\n}\n\n.pageviews .fa-spinner {\n font-size: 80%;\n}\n\n.post-meta {\n font-size: 0.85rem;\n word-spacing: 1px;\n\n a {\n &:not(:last-child) {\n margin-right: 2px;\n }\n\n &:hover {\n @extend %link-hover;\n }\n }\n\n em {\n @extend %normal-font-style;\n }\n}\n\n.post-content {\n font-size: 1.08rem;\n line-height: 1.8;\n margin-top: 2rem;\n overflow-wrap: break-word;\n word-wrap: break-word;\n\n a {\n &:not(.img-link) {\n @extend %link-underline;\n\n &:hover {\n @extend %link-hover;\n }\n }\n\n &.img-link {\n @extend %img-caption;\n }\n\n }\n\n ul {\n /* attribute 'hide-bullet' was added by liquid */\n .task-list-item[hide-bullet] {\n list-style-type: none;\n\n > i { /* checkbox icon */\n margin: 0 0.4rem 0.2rem -1.4rem;\n vertical-align: middle;\n color: var(--checkbox-color);\n\n &.checked {\n color: var(--checkbox-checked-color);\n }\n }\n\n }\n\n input[type=checkbox] {\n margin: 0 0.5rem 0.2rem -1.3rem;\n vertical-align: middle;\n }\n\n } /* ul */\n\n > ol,\n > ul {\n padding-left: 2rem;\n\n li {\n ol,\n ul { /* sub list */\n padding-left: 2rem;\n margin-top: 0.3rem;\n }\n }\n\n }\n\n > ol {\n li {\n padding-left: 0.25em;\n }\n }\n\n dl > dd {\n margin-left: 1rem;\n }\n\n} /* .post-content */\n\n.tag:hover {\n @extend %tag-hover;\n}\n\n.post-tag {\n display: inline-block;\n min-width: 2rem;\n text-align: center;\n background: var(--tag-bg);\n border-radius: 0.3rem;\n padding: 0 0.4rem;\n color: inherit;\n line-height: 1.3rem;\n\n &:not(:last-child) {\n margin-right: 0.2rem;\n }\n\n &:hover {\n @extend %tag-hover;\n\n border-bottom: none;\n text-decoration: none;\n color: #d2603a;\n }\n}\n\n/* --- buttons --- */\n.btn-lang {\n border: 1px solid !important;\n padding: 1px 3px;\n border-radius: 3px;\n color: var(--link-color);\n\n &:focus {\n box-shadow: none;\n }\n}\n\n/* --- Effects classes --- */\n\n.loaded {\n display: block !important;\n\n @at-root .d-flex#{&} {\n display: flex !important;\n }\n}\n\n.unloaded {\n display: none !important;\n}\n\n.visible {\n visibility: visible !important;\n}\n\n.hidden {\n visibility: hidden !important;\n}\n\n.flex-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n}\n\n.btn-box-shadow {\n box-shadow: 0 0 8px 0 var(--btn-box-shadow) !important;\n}\n\n.no-text-decoration {\n @include no-text-decoration;\n}\n\n.tooltip-inner { /* Overrided BS4 Tooltip */\n font-size: 0.7rem;\n max-width: 220px;\n text-align: left;\n}\n\n.disabled {\n color: rgb(206, 196, 196);\n pointer-events: auto;\n cursor: not-allowed;\n}\n\n.hide-border-bottom {\n border-bottom: none !important;\n}\n\n.input-focus {\n box-shadow: none;\n border-color: var(--input-focus-border-color) !important;\n background: center !important;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;\n}\n\n/* --- Overriding --- */\n\n/* magnific-popup */\nfigure .mfp-title {\n text-align: center;\n padding-right: 0;\n margin-top: 0.5rem;\n}\n\n/* mermaid */\n.mermaid {\n text-align: center;\n}\n\n/* --- sidebar layout --- */\n\n$sidebar-display: \"sidebar-display\";\n\n#sidebar {\n @include pl-pr(0);\n\n position: fixed;\n top: 0;\n left: 0;\n height: 100%;\n overflow-y: auto;\n width: $sidebar-width;\n z-index: 99;\n background: var(--sidebar-bg);\n\n /* Hide scrollbar for Chrome, Safari and Opera */\n &::-webkit-scrollbar {\n display: none;\n }\n\n /* Hide scrollbar for IE, Edge and Firefox */\n -ms-overflow-style: none; /* IE and Edge */\n scrollbar-width: none; /* Firefox */\n\n a {\n @extend %sidebar-links;\n\n &:hover {\n @include no-text-decoration;\n\n color: var(--sidebar-active-color) !important;\n }\n }\n\n #avatar {\n > a {\n display: block;\n width: 6rem;\n height: 6rem;\n border-radius: 50%;\n border: 2px solid rgba(222, 222, 222, 0.7);\n overflow: hidden;\n transform: translateZ(0); /* fixed the zoom in Safari */\n -webkit-transition: border-color 0.35s ease-in-out;\n -moz-transition: border-color 0.35s ease-in-out;\n transition: border-color 0.35s ease-in-out;\n\n &:hover {\n border-color: white;\n }\n }\n\n img {\n width: 100%;\n height: 100%;\n -webkit-transition: transform 0.5s;\n -moz-transition: transform 0.5s;\n transition: transform 0.5s;\n\n &:hover {\n -ms-transform: scale(1.2);\n -moz-transform: scale(1.2);\n -webkit-transform: scale(1.2);\n transform: scale(1.2);\n }\n }\n } /* #avatar */\n\n .site-title {\n a {\n @extend %clickable-transition;\n\n font-weight: 900;\n font-size: 1.5rem;\n letter-spacing: 0.5px;\n color: rgba(134, 133, 133, 99%);\n }\n }\n\n .site-subtitle {\n font-size: 95%;\n color: var(--sidebar-muted-color);\n line-height: 1.2rem;\n word-spacing: 1px;\n margin: 0.5rem 1.5rem 0.5rem 1.5rem;\n min-height: 3rem; /* avoid vertical shifting in multi-line words */\n user-select: none;\n }\n\n .nav-link {\n border-radius: 0;\n font-size: 0.95rem;\n font-weight: 600;\n letter-spacing: 1px;\n display: table-cell;\n vertical-align: middle;\n }\n\n .nav-item {\n text-align: center;\n display: table;\n height: $tab-height;\n\n &.active {\n .nav-link {\n color: var(--sidebar-active-color);\n }\n }\n\n &:not(.active) > a {\n @extend %clickable-transition;\n }\n }\n\n ul {\n height: $tab-height * $tab-count;\n margin-bottom: 2rem;\n padding-left: 0;\n\n li {\n width: 100%;\n\n &:last-child {\n a {\n position: relative;\n left: $cursor-width / 2;\n width: 100%;\n }\n\n &::after { /* the cursor */\n display: table;\n visibility: hidden;\n content: \"\";\n position: relative;\n right: 1px;\n width: $cursor-width;\n height: $tab-cursor-height;\n border-radius: 1px;\n background-color: var(--nav-cursor-color);\n pointer-events: none;\n }\n }\n } /* li */\n\n @mixin fix-cursor($top) {\n top: $top;\n visibility: visible;\n }\n\n @for $i from 1 through $tab-count {\n $offset: $tab-count - $i;\n $top: -$offset * $tab-height + ($tab-height - $tab-cursor-height) / 2;\n\n @if $i < $tab-count {\n > li.active:nth-child(#{$i}),\n > li.nav-item:nth-child(#{$i}):hover {\n ~ li:last-child::after {\n @include fix-cursor($top);\n }\n }\n } @else {\n > li.active:nth-child(#{$i}):last-child::after,\n > li.nav-item:nth-child(#{$i}):last-child:hover::after {\n @include fix-cursor($top);\n }\n }\n\n } /* @for */\n\n } /* ul */\n\n .sidebar-bottom {\n margin-bottom: 2.1rem;\n\n @include ml-mr(auto);\n @include pl-pr(1rem);\n\n %icon {\n width: 2.4rem;\n text-align: center;\n }\n\n a {\n @extend %icon;\n @extend %clickable-transition;\n }\n\n i {\n font-size: 1.2rem;\n line-height: 1.75rem;\n }\n\n .mode-toggle {\n padding: 0;\n border: 0;\n margin-bottom: 1px;\n background-color: transparent;\n\n @extend %icon;\n @extend %sidebar-links;\n\n > i {\n @extend %clickable-transition;\n }\n\n &:hover > i {\n color: var(--sidebar-active-color);\n }\n }\n\n .icon-border {\n @extend %no-cursor;\n\n background-color: var(--sidebar-muted-color);\n content: \"\";\n width: 3px;\n height: 3px;\n border-radius: 50%;\n }\n\n } /* .sidebar-bottom */\n\n} /* #sidebar */\n\n@media (hover: hover) {\n #sidebar ul > li:last-child::after {\n -webkit-transition: top 0.5s ease;\n -moz-transition: top 0.5s ease;\n -o-transition: top 0.5s ease;\n transition: top 0.5s ease;\n }\n}\n\n.profile-wrapper {\n margin-top: 2rem;\n width: 100%;\n}\n\n#search-result-wrapper {\n display: none;\n height: 100%;\n overflow: auto;\n\n .post-content {\n margin-top: 2rem;\n }\n}\n\n/* --- top-bar --- */\n\n#topbar-wrapper {\n height: $topbar-height;\n position: fixed;\n top: 0;\n left: $sidebar-width; /* same as sidebar width */\n right: 0;\n transition: top 0.2s ease-in-out;\n z-index: 50;\n border-bottom: 1px solid rgba(0, 0, 0, 0.07);\n background-color: var(--topbar-wrapper-bg);\n\n [data-topbar-visible=false] & {\n top: -$topbar-height; /* same as topbar height. */\n }\n}\n\n#topbar {\n i { /* icons */\n color: #999;\n }\n\n #breadcrumb {\n font-size: 1rem;\n color: gray;\n padding-left: 0.5rem;\n\n a:hover {\n @extend %link-hover;\n }\n\n span {\n &:not(:last-child) {\n &::after {\n content: \"›\";\n padding: 0 0.3rem;\n }\n }\n }\n }\n} /* #topbar */\n\n#sidebar-trigger,\n#search-trigger {\n display: none;\n}\n\n#search-wrapper {\n display: flex;\n width: 85%;\n border-radius: 1rem;\n border: 1px solid var(--search-wrapper-border-color);\n background: var(--search-wrapper-bg);\n padding: 0 0.5rem;\n\n i {\n z-index: 2;\n font-size: 0.9rem;\n color: var(--search-icon-color);\n }\n}\n\n#search-cancel { /* 'Cancel' link */\n color: var(--link-color);\n margin-left: 1rem;\n display: none;\n\n @extend %cursor-pointer;\n}\n\n#search-input {\n background: center;\n border: 0;\n border-radius: 0;\n padding: 0.18rem 0.3rem;\n color: var(--text-color);\n height: auto;\n\n &:focus {\n box-shadow: none;\n background: center;\n\n &.form-control {\n &::-webkit-input-placeholder { @include input-placeholder; }\n &::-moz-placeholder { @include input-placeholder; }\n &:-ms-input-placeholder { @include input-placeholder; }\n &::placeholder { @include input-placeholder; }\n }\n }\n}\n\n#search-hints {\n padding: 0 1rem;\n\n h4 {\n margin-bottom: 1.5rem;\n }\n\n .post-tag {\n display: inline-block;\n line-height: 1rem;\n font-size: 1rem;\n background: var(--search-tag-bg);\n border: none;\n padding: 0.5rem;\n margin: 0 1.25rem 1rem 0;\n\n &::before {\n content: \"#\";\n color: var(--text-muted-color);\n padding-right: 0.2rem;\n }\n\n @extend %link-color;\n }\n}\n\n#search-results {\n padding-bottom: 6rem;\n\n a {\n &:hover {\n @extend %link-hover;\n }\n\n @extend %link-color;\n @extend %no-bottom-border;\n @extend %heading;\n\n font-size: 1.4rem;\n line-height: 2.5rem;\n }\n\n > div {\n width: 100%;\n\n &:not(:last-child) {\n margin-bottom: 1rem;\n }\n\n i { /* icons */\n color: #818182;\n margin-right: 0.15rem;\n font-size: 80%;\n }\n\n > p {\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 3;\n -webkit-box-orient: vertical;\n }\n }\n} /* #search-results */\n\n#topbar-title {\n display: none;\n font-size: 1.1rem;\n font-weight: 600;\n font-family: sans-serif;\n color: var(--topbar-text-color);\n text-align: center;\n width: 70%;\n overflow: hidden;\n text-overflow: ellipsis;\n word-break: keep-all;\n white-space: nowrap;\n}\n\n#core-wrapper {\n min-height: calc(100vh - #{$topbar-height} - #{$footer-height} - #{$bottom-min-height}) !important;\n\n .categories,\n #tags,\n #archives {\n a:not(:hover) {\n @extend %no-bottom-border;\n }\n }\n}\n\n#mask {\n display: none;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n height: 100%;\n width: 100%;\n z-index: 1;\n\n @at-root [#{$sidebar-display}] & {\n display: block !important;\n }\n}\n\n/* --- main wrapper --- */\n\n#main-wrapper {\n background-color: var(--main-wrapper-bg);\n position: relative;\n min-height: 100vh;\n padding-bottom: $footer-height;\n\n @include pl-pr(0);\n}\n\n#main {\n .row:first-child {\n > div {\n &:nth-child(1),\n &:nth-child(2) {\n margin-top: $topbar-height; /* same as the height of topbar */\n }\n\n &:first-child {\n /* 3rem for topbar, 6rem for footer */\n min-height: calc(100vh - #{$topbar-height} - #{$footer-height} - #{$bottom-min-height});\n }\n }\n }\n\n div.row:first-of-type:last-of-type { /* alone */\n margin-bottom: 4rem;\n }\n}\n\n#topbar-wrapper.row,\n#main > .row,\n#search-result-wrapper > .row {\n @include ml-mr(0);\n}\n\n/* --- button back-to-top --- */\n\n#back-to-top {\n $size: 2.7em;\n\n display: none;\n z-index: 1;\n cursor: pointer;\n position: fixed;\n background: var(--button-bg);\n color: var(--btn-backtotop-color);\n padding: 0;\n width: $size;\n height: $size;\n border-radius: 50%;\n border: 1px solid var(--btn-backtotop-border-color);\n transition: transform 0.2s ease-out;\n -webkit-transition: transform 0.2s ease-out;\n\n i {\n line-height: $size;\n position: relative;\n bottom: 2px;\n }\n}\n\n#back-to-top:hover {\n transform: translate3d(0, -5px, 0);\n -webkit-transform: translate3d(0, -5px, 0);\n}\n\n/*\n Responsive Design:\n\n {sidebar, content, panel} >= 1120px screen width\n {sidebar, content} >= 850px screen width\n {content} <= 849px screen width\n\n*/\n\n@media all and (max-width: 576px) {\n\n $footer-height: $footer-height-mobile; /* overwrite */\n\n footer {\n height: $footer-height;\n\n > div.d-flex {\n width: 100%;\n padding: 1.5rem 0;\n margin-bottom: 0.3rem;\n flex-wrap: wrap;\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n\n .footer-left,\n .footer-right {\n text-align: center;\n }\n }\n\n #main > div.row:first-child > div:first-child {\n min-height: calc(100vh - #{$topbar-height} - #{$footer-height});\n }\n\n #core-wrapper {\n min-height: calc(100vh - #{$topbar-height} - #{$footer-height} - #{$bottom-min-height}) !important;\n\n h1 {\n margin-top: 2.2rem;\n font-size: 1.75rem;\n }\n\n .post-content {\n > blockquote[class^=prompt-] {\n @include ml-mr(-1.25rem);\n border-radius: 0;\n }\n }\n\n }\n\n #avatar > a {\n width: 5rem;\n height: 5rem;\n }\n\n .site-subtitle {\n @include ml-mr(1.8rem);\n }\n\n #main-wrapper {\n padding-bottom: $footer-height;\n }\n\n}\n\n/* hide sidebar and panel */\n@media all and (max-width: 849px) {\n @mixin slide($append: null) {\n $basic: transform 0.4s ease;\n @if $append {\n -webkit-transition: $basic, $append;\n transition: $basic, $append;\n } @else {\n -webkit-transition: $basic;\n transition: $basic;\n }\n }\n\n html,\n body {\n overflow-x: hidden;\n }\n\n [#{$sidebar-display}] {\n #sidebar {\n transform: translateX(0);\n }\n\n #topbar-wrapper,\n #main-wrapper {\n transform: translateX(#{$sidebar-width});\n }\n }\n\n #sidebar {\n @include slide;\n\n transform: translateX(-#{$sidebar-width}); /* hide */\n -webkit-transform: translateX(-#{$sidebar-width});\n\n .cursor {\n -webkit-transition: none;\n -moz-transition: none;\n transition: none;\n }\n }\n\n #main-wrapper {\n @include slide;\n\n padding-top: $topbar-height;\n }\n\n #search-result-wrapper {\n width: 100%;\n }\n\n #breadcrumb,\n #search-wrapper {\n display: none;\n }\n\n #topbar-wrapper {\n @include slide(top 0.2s ease);\n\n left: 0;\n }\n\n #main > div.row:first-child > div:nth-child(1),\n #main > div.row:first-child > div:nth-child(2) {\n margin-top: 0;\n }\n\n #topbar-title,\n #sidebar-trigger,\n #search-trigger {\n display: block;\n }\n\n #search-wrapper {\n &.loaded ~ a {\n margin-right: 1rem;\n }\n }\n\n #search-input {\n margin-left: 0;\n width: 95%;\n }\n\n #search-result-wrapper .post-content {\n letter-spacing: 0;\n }\n\n #tags {\n -webkit-box-pack: center !important;\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n\n h1.dynamic-title {\n display: none;\n\n ~ .post-content {\n margin-top: 3rem;\n }\n }\n\n} /* max-width: 849px */\n\n@media all and (max-width: 849px) and (orientation: portrait) {\n [data-topbar-visible=false] #topbar-wrapper {\n top: 0;\n }\n}\n\n/* Phone & Pad */\n@media all and (min-width: 577px) and (max-width: 1199px) {\n footer > .d-flex > div {\n width: 312px;\n }\n}\n\n/* Sidebar is visible */\n@media all and (min-width: 850px) {\n /* Solved jumping scrollbar */\n html {\n overflow-y: scroll;\n }\n\n #main-wrapper {\n margin-left: $sidebar-width;\n }\n\n .profile-wrapper {\n margin-top: 3rem;\n }\n\n #search-wrapper {\n width: 22%;\n min-width: 150px;\n }\n\n #search-hints {\n display: none;\n }\n\n #search-result-wrapper {\n margin-top: 3rem;\n }\n\n div.post-content .table-wrapper > table {\n min-width: 70%;\n }\n\n /* button 'back-to-Top' position */\n #back-to-top {\n bottom: 5.5rem;\n right: 1.2rem;\n }\n\n #topbar-title {\n text-align: left;\n }\n\n footer > div.d-flex {\n width: 92%;\n }\n\n}\n\n/* Pad horizontal */\n@media all and (min-width: 992px) and (max-width: 1199px) {\n #main .col-lg-11 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 96%;\n flex: 0 0 96%;\n max-width: 96%;\n }\n}\n\n/* Compact icons in sidebar & panel hidden */\n@media all and (min-width: 850px) and (max-width: 1199px) {\n #sidebar {\n width: $sidebar-width-small;\n\n .site-subtitle {\n margin-left: 1rem;\n margin-right: 1rem;\n }\n\n .sidebar-bottom {\n a,\n span {\n width: 2rem;\n }\n\n .icon-border {\n left: -3px;\n }\n }\n }\n\n #topbar-wrapper {\n left: 210px;\n }\n\n #search-results > div {\n max-width: 700px;\n }\n\n .site-title {\n font-size: 1.3rem;\n margin-left: 0 !important;\n }\n\n .site-subtitle {\n @include ml-mr(1rem);\n\n font-size: 90%;\n }\n\n #main-wrapper {\n margin-left: 210px;\n }\n\n #breadcrumb {\n width: 65%;\n overflow: hidden;\n text-overflow: ellipsis;\n word-break: keep-all;\n white-space: nowrap;\n }\n\n}\n\n/* panel hidden */\n@media all and (max-width: 1199px) {\n #panel-wrapper {\n display: none;\n }\n\n #topbar {\n padding: 0;\n }\n\n #main > div.row {\n -webkit-box-pack: center !important;\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n}\n\n/* --- desktop mode, both sidebar and panel are visible --- */\n\n@media all and (min-width: 1200px) {\n #main > div.row > div.col-xl-8 {\n -webkit-box-flex: 0;\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n padding-left: 3%;\n }\n\n #topbar {\n padding: 0;\n max-width: 1070px;\n }\n\n #panel-wrapper {\n max-width: $panel-max-width;\n }\n\n #back-to-top {\n bottom: 6.5rem;\n right: 4.3rem;\n }\n\n #search-input {\n -webkit-transition: all 0.3s ease-in-out;\n transition: all 0.3s ease-in-out;\n }\n\n #search-results > div {\n width: 46%;\n\n &:nth-child(odd) {\n margin-right: 1.5rem;\n }\n\n &:nth-child(even) {\n margin-left: 1.5rem;\n }\n\n &:last-child:nth-child(odd) {\n position: relative;\n right: 24.3%;\n }\n }\n\n .post-content {\n font-size: 1.03rem;\n }\n\n footer > div.d-felx {\n width: 85%;\n }\n\n}\n\n@media all and (min-width: 1400px) {\n #main > div.row {\n padding-left: calc((100% - #{$main-content-max-width}) / 2);\n\n > div.col-xl-8 {\n max-width: 850px;\n }\n }\n\n #search-result-wrapper {\n padding-right: 2rem;\n\n > div {\n max-width: 1110px;\n }\n }\n\n}\n\n@media all and (min-width: 1400px) and (max-width: 1650px) {\n #topbar {\n padding-right: 2rem;\n }\n}\n\n@media all and (min-width: 1650px) {\n #breadcrumb {\n padding-left: 0;\n }\n\n #main > div.row > div.col-xl-8 {\n padding-left: 0;\n\n > div:first-child {\n padding-left: 0.55rem !important;\n padding-right: 1.9rem !important;\n }\n }\n\n #main-wrapper {\n margin-left: $sidebar-width-large;\n }\n\n #panel-wrapper {\n margin-left: calc((100% - #{$main-content-max-width}) / 10);\n }\n\n #topbar-wrapper {\n left: $sidebar-width-large;\n }\n\n #topbar {\n max-width: #{$main-content-max-width};\n }\n\n #search-wrapper {\n margin-right: 3%;\n }\n\n #sidebar {\n width: $sidebar-width-large;\n\n .profile-wrapper {\n margin-top: 4rem;\n margin-bottom: 1rem;\n\n &.text-center {\n text-align: left !important;\n }\n\n %profile-ml {\n margin-left: 4.5rem;\n }\n\n #avatar {\n @extend %profile-ml;\n\n > a {\n width: 6.2rem;\n height: 6.2rem;\n\n &.mx-auto {\n margin-left: 0 !important;\n }\n }\n }\n\n .site-title {\n @extend %profile-ml;\n\n a {\n font-size: 1.7rem;\n letter-spacing: 1px;\n }\n }\n\n .site-subtitle {\n @extend %profile-ml;\n\n word-spacing: 0;\n margin-top: 0.3rem;\n }\n\n } /* .profile-wrapper (min-width: 1650px) */\n\n ul {\n padding-left: 2.5rem;\n\n > li:last-child {\n > a {\n position: static;\n }\n }\n\n .nav-item {\n text-align: left;\n\n .nav-link {\n > span {\n letter-spacing: 2px;\n }\n\n > i {\n &.unloaded {\n display: inline-block !important;\n }\n }\n }\n\n }\n }\n\n .sidebar-bottom {\n padding-left: 3.5rem;\n width: 100%;\n\n $icon-block-size: 2rem;\n\n &.justify-content-center {\n -webkit-box-pack: start !important;\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n\n > span,\n > button.mode-toggle,\n > a {\n @include ml-mr(0.15rem);\n\n height: $icon-block-size;\n margin-bottom: 0.5rem; /* wrap line */\n }\n\n i {\n background-color: var(--sidebar-btn-bg);\n font-size: 1rem;\n width: $icon-block-size;\n height: $icon-block-size;\n border-radius: 50%;\n position: relative;\n\n &::before {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n }\n }\n\n .icon-border {\n top: 0.9rem;\n }\n\n } /* .sidebar-bottom */\n\n } /* #sidebar */\n\n footer > div.d-flex {\n width: 92%;\n max-width: 1140px;\n }\n\n #search-result-wrapper {\n > div {\n max-width: #{$main-content-max-width};\n }\n }\n\n} /* min-width: 1650px */\n\n@media all and (min-width: 1700px) {\n #topbar-wrapper {\n /* 100% - 350px - (1920px - 350px); */\n padding-right: calc(100% - #{$sidebar-width-large} - (1920px - #{$sidebar-width-large}));\n }\n\n #topbar {\n max-width: calc(#{$main-content-max-width} + 20px);\n }\n\n #main > div.row {\n padding-left: calc((100% - #{$main-content-max-width} - 2%) / 2);\n }\n\n #panel-wrapper {\n margin-left: 3%;\n }\n\n footer {\n padding-left: 0;\n padding-right: calc(100% - #{$sidebar-width-large} - 1180px);\n }\n\n #back-to-top {\n right: calc(100% - 1920px + 15rem);\n }\n\n}\n\n@media (min-width: 1920px) {\n #main > div.row {\n padding-left: 190px;\n }\n\n #search-result-wrapper {\n padding-right: calc(100% - #{$sidebar-width-large} - 1180px);\n }\n\n #panel-wrapper {\n margin-left: 41px;\n }\n}\n", + "/*\n Style for Homepage\n*/\n\n.pagination {\n color: var(--btn-patinator-text-color);\n font-family: 'Lato', sans-serif;\n\n a:hover {\n text-decoration: none;\n }\n\n .page-item {\n .page-link {\n color: inherit;\n width: 2.5rem;\n height: 2.5rem;\n padding: 0;\n display: -webkit-box;\n -webkit-box-pack: center;\n -webkit-box-align: center;\n border-radius: 50%;\n border: 1px solid var(--btn-paginator-border-color);\n background-color: var(--button-bg);\n\n &:hover {\n background-color: var(--btn-paginator-hover-color);\n }\n }\n\n &.active {\n .page-link {\n background-color: var(--btn-paginator-hover-color);\n color: var(--btn-text-color);\n }\n }\n\n &.disabled {\n cursor: not-allowed;\n\n .page-link {\n color: rgba(108, 117, 125, 0.57);\n border-color: var(--btn-paginator-border-color);\n background-color: var(--button-bg);\n }\n }\n\n &:first-child .page-link,\n &:last-child .page-link {\n border-radius: 50%;\n }\n } // .page-item\n\n} // .pagination\n\n#post-list {\n margin-top: 1rem;\n padding-right: 0.5rem;\n\n .post-preview {\n padding-top: 1.5rem;\n padding-bottom: 1rem;\n border-bottom: 1px solid var(--main-border-color);\n\n a:hover {\n @extend %link-hover;\n }\n\n h1 {\n font-size: 1.4rem;\n margin: 0;\n }\n\n .post-meta {\n i {\n font-size: 0.73rem;\n\n &:not(:first-child) {\n margin-left: 1.2rem;\n }\n }\n\n em {\n @extend %normal-font-style;\n }\n }\n\n .post-content {\n margin-top: 0.6rem;\n margin-bottom: 0.6rem;\n color: var(--post-list-text-color);\n\n > p {\n /* Make preview shorter on the homepage */\n margin: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n }\n }\n\n .pin {\n > i {\n transform: rotate(45deg);\n padding-left: 3px;\n color: var(--pin-color);\n }\n\n > span {\n display: none;\n }\n }\n\n } // .post-preview\n} // #post-list\n\n/* Hide SideBar and TOC */\n@media all and (max-width: 830px) {\n .pagination {\n justify-content: space-evenly;\n\n .page-item {\n &:not(:first-child):not(:last-child) {\n display: none;\n }\n\n }\n\n }\n}\n\n/* Sidebar is visible */\n@media all and (min-width: 831px) {\n #post-list {\n margin-top: 1.5rem;\n\n .post-preview .post-meta {\n .pin {\n background: var(--pin-bg);\n border-radius: 5px;\n line-height: 1.4rem;\n height: 1.3rem;\n margin-top: 3px;\n padding-left: 1px;\n padding-right: 6px;\n\n > span {\n display: inline;\n }\n }\n }\n }\n\n .pagination {\n font-size: 0.85rem;\n\n .page-item {\n &:not(:last-child) {\n margin-right: 0.7rem;\n }\n\n .page-link {\n width: 2rem;\n height: 2rem;\n }\n\n }\n\n .page-index {\n display: none;\n }\n\n } // .pagination\n\n}\n\n/* Pannel hidden */\n@media all and (max-width: 1200px) {\n #post-list {\n padding-right: 0;\n }\n}\n", + "/*\n Post-specific style\n*/\n\n@mixin btn-sharing-color($light-color, $important: false) {\n @if $important {\n color: var(--btn-share-color, $light-color) !important;\n } @else {\n color: var(--btn-share-color, $light-color);\n }\n}\n\n@mixin btn-post-nav {\n width: 50%;\n position: relative;\n border-color: var(--btn-border-color);\n}\n\n@mixin dot($pl: 0.25rem, $pr: 0.25rem) {\n content: \"\\2022\";\n padding-left: $pl;\n padding-right: $pr;\n}\n\n%text-color {\n color: var(--text-color);\n}\n\nh1 + .post-meta {\n span + span::before {\n @include dot;\n }\n\n em {\n @extend %text-color;\n\n a {\n @extend %text-color;\n }\n }\n}\n\nimg.preview-img {\n margin-top: 3.75rem;\n margin-bottom: 0;\n border-radius: 6px;\n\n &.bg[data-loaded=true] {\n background: var(--preview-img-bg);\n }\n}\n\n.post-tail-wrapper {\n margin-top: 6rem;\n border-bottom: 1px double var(--main-border-color);\n font-size: 0.85rem;\n\n .post-meta a:not(:hover) {\n @extend %link-underline;\n }\n}\n\n.post-tags {\n line-height: 2rem;\n}\n\n.post-navigation {\n padding-top: 3rem;\n padding-bottom: 4rem;\n\n .btn {\n @include btn-post-nav;\n\n color: var(--link-color);\n\n &:hover {\n background: #2a408e;\n color: #fff;\n border-color: #2a408e;\n }\n\n &.disabled {\n @include btn-post-nav;\n\n pointer-events: auto;\n cursor: not-allowed;\n background: none;\n color: gray;\n\n &:hover {\n border-color: none;\n }\n }\n\n &.btn-outline-primary.disabled:focus {\n box-shadow: none;\n }\n\n &::before {\n color: var(--text-muted-color);\n font-size: 0.65rem;\n text-transform: uppercase;\n content: attr(prompt);\n }\n\n &:first-child {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n left: 0.5px;\n }\n\n &:last-child {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n right: 0.5px;\n }\n }\n\n p {\n font-size: 1.1rem;\n line-height: 1.5rem;\n margin-top: 0.3rem;\n white-space: normal;\n }\n\n} /* .post-navigation */\n\n@keyframes fade-up {\n from {\n opacity: 0;\n position: relative;\n top: 2rem;\n }\n to {\n opacity: 1;\n position: relative;\n top: 0;\n }\n}\n\n#toc-wrapper {\n border-left: 1px solid rgba(158, 158, 158, 0.17);\n position: -webkit-sticky;\n position: sticky;\n top: 4rem;\n transition: top 0.2s ease-in-out;\n animation: fade-up 0.8s;\n}\n\n#toc li a {\n font-size: 0.8rem;\n\n &.nav-link:not(.active) {\n color: inherit;\n }\n\n}\n\nnav[data-toggle=toc] {\n .nav {\n .nav > li > a.active {\n font-weight: 600 !important;\n }\n }\n}\n\n/* --- Related Posts --- */\n\n#related-posts {\n > h3 {\n @include label(1.1rem, 600);\n }\n\n em {\n @extend %normal-font-style;\n }\n\n .card {\n border-color: var(--card-border-color);\n background-color: var(--card-bg);\n box-shadow: 0 0 5px 0 var(--card-box-shadow);\n -webkit-transition: all 0.3s ease-in-out;\n -moz-transition: all 0.3s ease-in-out;\n transition: all 0.3s ease-in-out;\n\n h3 {\n @extend %text-color;\n }\n\n &:hover {\n -webkit-transform: translate3d(0, -3px, 0);\n transform: translate3d(0, -3px, 0);\n box-shadow: 0 10px 15px -4px rgba(0, 0, 0, 0.15);\n }\n }\n\n .timeago {\n color: var(--relate-post-date);\n }\n\n p {\n font-size: 0.9rem;\n margin-bottom: 0.5rem;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n }\n\n a:hover {\n text-decoration: none;\n }\n\n ul {\n list-style-type: none;\n padding-inline-start: 1.5rem;\n\n > li::before {\n background: #c2c9d4;\n width: 5px;\n height: 5px;\n border-radius: 1px;\n display: block;\n content: \"\";\n position: relative;\n top: 1rem;\n right: 1rem;\n }\n }\n}\n\n#tail-wrapper {\n min-height: 2rem;\n\n > div:last-of-type {\n margin-bottom: 2rem;\n }\n\n #disqus_thread {\n min-height: 8.5rem;\n }\n\n}\n\n.post-tail-bottom a {\n color: inherit;\n}\n\n%btn-share-hovor {\n color: var(--btn-share-hover-color) !important;\n}\n\n.share-wrapper {\n vertical-align: middle;\n user-select: none;\n\n .share-icons {\n font-size: 1.2rem;\n\n a {\n &:not(:last-child) {\n margin-right: 0.25rem;\n }\n\n &:hover {\n text-decoration: none;\n\n > i {\n @extend %btn-share-hovor;\n }\n }\n }\n\n > i {\n position: relative;\n bottom: 1px;\n\n @extend %cursor-pointer;\n\n &:hover {\n @extend %btn-share-hovor;\n }\n }\n\n .fab {\n &.fa-twitter {\n @include btn-sharing-color(rgba(29, 161, 242, 1));\n }\n\n &.fa-facebook-square {\n @include btn-sharing-color(rgb(66, 95, 156));\n }\n\n &.fa-telegram {\n @include btn-sharing-color(rgb(39, 159, 217));\n }\n\n &.fa-weibo {\n @include btn-sharing-color(rgb(229, 20, 43));\n }\n }\n\n } /* .share-icons */\n\n .fas.fa-link {\n @include btn-sharing-color(rgb(171, 171, 171));\n }\n\n} /* .share-wrapper */\n\n.share-label {\n @include label(inherit, 400, inherit);\n\n &::after {\n content: \":\";\n }\n}\n\n.license-wrapper {\n line-height: 1.2rem;\n\n > a {\n color: var(--text-color);\n\n &:hover {\n @extend %link-hover;\n }\n }\n\n span:last-child {\n font-size: 0.85rem;\n }\n\n} /* .license-wrapper */\n\n@media all and (max-width: 576px) {\n .preview-img[data-src] {\n margin-top: 2.2rem;\n }\n\n .post-tail-bottom {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n\n > div:first-child {\n width: 100%;\n margin-top: 1rem;\n }\n }\n}\n\n@media all and (max-width: 768px) {\n .post-content > p > img {\n max-width: calc(100% + 1rem);\n }\n}\n\n/* Hide SideBar and TOC */\n@media all and (max-width: 849px) {\n .post-navigation {\n padding-left: 0;\n padding-right: 0;\n margin-left: -0.5rem;\n margin-right: -0.5rem;\n }\n\n .preview-img[data-src] {\n max-width: 100vw;\n border-radius: 0;\n }\n}\n", + "/*\n Styles for Tab Tags\n*/\n\n.tag {\n border-radius: 0.7em;\n padding: 6px 8px 7px;\n margin-right: 0.8rem;\n line-height: 3rem;\n letter-spacing: 0;\n border: 1px solid var(--tag-border) !important;\n box-shadow: 0 0 3px 0 var(--tag-shadow);\n\n span {\n margin-left: 0.6em;\n font-size: 0.7em;\n font-family: 'Oswald', sans-serif;\n }\n}\n", + "/*\n Style for Archives\n*/\n\n%date-timeline {\n content: \"\";\n width: 4px;\n left: 75px;\n display: inline-block;\n float: left;\n position: relative;\n background-color: var(--timeline-color);\n}\n\n#archives {\n letter-spacing: 0.03rem;\n\n span.lead {\n font-size: 1.5rem;\n position: relative;\n left: 8px;\n\n &::after { /* Year dot */\n content: \"\";\n display: block;\n position: relative;\n -webkit-border-radius: 50%;\n -moz-border-radius: 50%;\n border-radius: 50%;\n width: 12px;\n height: 12px;\n top: -26px;\n left: 63px;\n border: 3px solid;\n background-color: var(--timeline-year-dot-color);\n border-color: var(--timeline-node-bg);\n box-shadow: 0 0 2px 0 #c2c6cc;\n z-index: 1;\n }\n\n &:not(:first-child) {\n position: relative;\n left: 4px;\n\n &::after {\n left: 67px;\n }\n }\n\n } // #archives span.lead\n\n ul {\n li {\n font-size: 1.1rem;\n line-height: 3rem;\n\n div {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n\n a {\n /* post title in Archvies */\n margin-left: 2.5rem;\n position: relative;\n top: 0.1rem;\n }\n }\n\n &:nth-child(odd) {\n background-color: var(--main-wrapper-bg, #fff);\n background-image: linear-gradient(to left, #fff, #fbfbfb, #fbfbfb, #fbfbfb, #fff);\n }\n\n &::after {\n @extend %date-timeline;\n\n height: 2.8rem;\n top: -1.3rem;\n }\n\n &:first-child::before {\n @extend %date-timeline;\n\n height: 3.06rem;\n top: -1.61rem;\n }\n }\n\n &:not(:last-child) > li:last-child::after {\n height: 3.4rem;\n }\n\n &:last-child > li:last-child::after {\n display: none;\n }\n } // #archives ul\n\n .date {\n white-space: nowrap;\n display: inline-block;\n\n &.month {\n width: 1.4rem;\n text-align: center;\n\n ~ a::before {\n /* A dot for Month and Day */\n content: \"\";\n display: inline-block;\n position: relative;\n -webkit-border-radius: 50%;\n -moz-border-radius: 50%;\n border-radius: 50%;\n width: 8px;\n height: 8px;\n float: left;\n top: 1.35rem;\n left: 69px;\n background-color: var(--timeline-node-bg);\n box-shadow: 0 0 3px 0 #c2c6cc;\n z-index: 1;\n }\n }\n\n &.day {\n font-size: 85%;\n font-family: 'Lato', sans-serif;\n text-align: center;\n margin-right: -2px;\n width: 1.2rem;\n position: relative;\n left: -0.15rem;\n }\n } // #archives .date\n\n} // #archives\n\n@media all and (max-width: 576px) {\n #archives {\n margin-top: -1rem;\n\n ul {\n letter-spacing: 0;\n }\n }\n}\n", + "/*\n Style for Tab Categories\n*/\n\n%category-icon-color {\n color: gray;\n}\n\n.categories {\n margin-bottom: 2rem;\n\n .card-header {\n padding-right: 12px;\n }\n\n i {\n @extend %category-icon-color;\n\n font-size: 86%; // fontawesome icons\n }\n\n .list-group-item {\n border-left: none;\n border-right: none;\n padding-left: 2rem;\n\n &:first-child {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n }\n\n }\n\n} // .categories\n\n.category-trigger {\n width: 1.7rem;\n height: 1.7rem;\n border-radius: 50%;\n text-align: center;\n color: #6c757d !important;\n\n &:hover {\n i {\n color: var(--categories-icon-hover-color);\n }\n }\n\n i {\n position: relative;\n height: 0.7rem;\n width: 1rem;\n transition: transform 300ms ease;\n }\n}\n\n@media (hover: hover) { // only works on desktop\n .category-trigger:hover {\n background-color: var(--categories-hover-bg);\n }\n}\n\n.rotate {\n -ms-transform: rotate(-90deg); /* IE 9 */\n -webkit-transform: rotate(-90deg); /* Safari 3-8 */\n transform: rotate(-90deg);\n}\n", + "/*\n Style for page Category and Tag\n*/\n\n.dash {\n margin: 0 0.5rem 0.6rem 0.5rem;\n border-bottom: 2px dotted var(--dash-color);\n}\n\n#page-category,\n#page-tag {\n ul > li {\n line-height: 1.5rem;\n padding: 0.6rem 0;\n\n &::before { // dot\n background: #999;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n display: block;\n content: \"\";\n position: relative;\n top: 0.6rem;\n margin-right: 0.5rem;\n }\n\n > a { /* post's title */\n @extend %no-bottom-border;\n\n font-size: 1.1rem;\n }\n\n > span:last-child {\n white-space: nowrap;\n } /* post's date */\n }\n}\n\n#page-tag h1 > i { // tag icon\n font-size: 1.2rem;\n}\n\n#page-category h1 > i {\n font-size: 1.25rem;\n}\n\n#page-category,\n#page-tag,\n#access-lastmod {\n a:hover {\n @extend %link-hover;\n\n margin-bottom: -1px; // Avoid jumping\n }\n}\n\n@media all and (max-width: 576px) {\n #page-category,\n #page-tag {\n ul > li {\n &::before {\n margin: 0 0.5rem;\n }\n\n > a {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n }\n }\n}\n" + ], + "names": [], + "mappings": "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" +} \ No newline at end of file diff --git a/assets/files/Access-Mask-Blog.zip b/assets/files/Access-Mask-Blog.zip new file mode 100644 index 00000000..c3257529 Binary files /dev/null and b/assets/files/Access-Mask-Blog.zip differ diff --git a/assets/files/Intel.x86-64.Architecture.2012011000.pdf b/assets/files/Intel.x86-64.Architecture.2012011000.pdf new file mode 100644 index 00000000..6bc9bda2 Binary files /dev/null and b/assets/files/Intel.x86-64.Architecture.2012011000.pdf differ diff --git a/assets/files/Requirements-for-Implementing-the-Microsoft-Hypervisor-Interface-1.pdf b/assets/files/Requirements-for-Implementing-the-Microsoft-Hypervisor-Interface-1.pdf new file mode 100644 index 00000000..d6c6a4ba Binary files /dev/null and b/assets/files/Requirements-for-Implementing-the-Microsoft-Hypervisor-Interface-1.pdf differ diff --git a/assets/files/VMCS.pdf b/assets/files/VMCS.pdf new file mode 100644 index 00000000..3e984577 Binary files /dev/null and b/assets/files/VMCS.pdf differ diff --git a/assets/files/lesson17.ppt b/assets/files/lesson17.ppt new file mode 100644 index 00000000..8b50ea64 Binary files /dev/null and b/assets/files/lesson17.ppt differ diff --git a/assets/images/32-64bit.jpg b/assets/images/32-64bit.jpg new file mode 100644 index 00000000..865e6797 Binary files /dev/null and b/assets/images/32-64bit.jpg differ diff --git a/assets/images/32-bit-call-gates.png b/assets/images/32-bit-call-gates.png new file mode 100644 index 00000000..cc0c8875 Binary files /dev/null and b/assets/images/32-bit-call-gates.png differ diff --git a/assets/images/64-bit-call-gates.png b/assets/images/64-bit-call-gates.png new file mode 100644 index 00000000..19da8a55 Binary files /dev/null and b/assets/images/64-bit-call-gates.png differ diff --git a/assets/images/EPT-Layout.png b/assets/images/EPT-Layout.png new file mode 100644 index 00000000..ba55a9af Binary files /dev/null and b/assets/images/EPT-Layout.png differ diff --git a/assets/images/EPT-PDE.png b/assets/images/EPT-PDE.png new file mode 100644 index 00000000..fa789d81 Binary files /dev/null and b/assets/images/EPT-PDE.png differ diff --git a/assets/images/EPT-PDPTE.png b/assets/images/EPT-PDPTE.png new file mode 100644 index 00000000..36530d54 Binary files /dev/null and b/assets/images/EPT-PDPTE.png differ diff --git a/assets/images/EPT-PML4E.png b/assets/images/EPT-PML4E.png new file mode 100644 index 00000000..366aa1cb Binary files /dev/null and b/assets/images/EPT-PML4E.png differ diff --git a/assets/images/EPT-PTE.png b/assets/images/EPT-PTE.png new file mode 100644 index 00000000..e2b50c1c Binary files /dev/null and b/assets/images/EPT-PTE.png differ diff --git a/assets/images/EPT-full-translation.png b/assets/images/EPT-full-translation.png new file mode 100644 index 00000000..8bc2ac74 Binary files /dev/null and b/assets/images/EPT-full-translation.png differ diff --git a/assets/images/EPT-support.png b/assets/images/EPT-support.png new file mode 100644 index 00000000..e3c0524d Binary files /dev/null and b/assets/images/EPT-support.png differ diff --git a/assets/images/EPT-translations.png b/assets/images/EPT-translations.png new file mode 100644 index 00000000..a9d4a9a6 Binary files /dev/null and b/assets/images/EPT-translations.png differ diff --git a/assets/images/EPTP-structure.png b/assets/images/EPTP-structure.png new file mode 100644 index 00000000..0f7ea5d3 Binary files /dev/null and b/assets/images/EPTP-structure.png differ diff --git a/assets/images/HyperDbg-v0.1-1-compressed.jpg b/assets/images/HyperDbg-v0.1-1-compressed.jpg new file mode 100644 index 00000000..e4735d6f Binary files /dev/null and b/assets/images/HyperDbg-v0.1-1-compressed.jpg differ diff --git a/assets/images/IA32-MTRRCAP.png b/assets/images/IA32-MTRRCAP.png new file mode 100644 index 00000000..f874fbd1 Binary files /dev/null and b/assets/images/IA32-MTRRCAP.png differ diff --git a/assets/images/IC_Package_Types.png b/assets/images/IC_Package_Types.png new file mode 100644 index 00000000..182afc1f Binary files /dev/null and b/assets/images/IC_Package_Types.png differ diff --git a/assets/images/INVEPT-descriptor-definition.png b/assets/images/INVEPT-descriptor-definition.png new file mode 100644 index 00000000..f3aac02a Binary files /dev/null and b/assets/images/INVEPT-descriptor-definition.png differ diff --git a/assets/images/INVVPID-descriptor-definition.png b/assets/images/INVVPID-descriptor-definition.png new file mode 100644 index 00000000..edba33c0 Binary files /dev/null and b/assets/images/INVVPID-descriptor-definition.png differ diff --git a/assets/images/IRQL-in-vmx-root.png b/assets/images/IRQL-in-vmx-root.png new file mode 100644 index 00000000..a3184be9 Binary files /dev/null and b/assets/images/IRQL-in-vmx-root.png differ diff --git a/assets/images/LoadLibraryA-windows-api.png b/assets/images/LoadLibraryA-windows-api.png new file mode 100644 index 00000000..ab3778bb Binary files /dev/null and b/assets/images/LoadLibraryA-windows-api.png differ diff --git a/assets/images/MSR-bitmap-1.png b/assets/images/MSR-bitmap-1.png new file mode 100644 index 00000000..cad78d5a Binary files /dev/null and b/assets/images/MSR-bitmap-1.png differ diff --git a/assets/images/MSR-bitmap-2.png b/assets/images/MSR-bitmap-2.png new file mode 100644 index 00000000..37b1e88e Binary files /dev/null and b/assets/images/MSR-bitmap-2.png differ diff --git a/assets/images/MTRR-windbg.png b/assets/images/MTRR-windbg.png new file mode 100644 index 00000000..17b417db Binary files /dev/null and b/assets/images/MTRR-windbg.png differ diff --git a/assets/images/Mhz-field-in-windows.png b/assets/images/Mhz-field-in-windows.png new file mode 100644 index 00000000..77c5ed9f Binary files /dev/null and b/assets/images/Mhz-field-in-windows.png differ diff --git a/assets/images/MmGetPhysicalAddress.png b/assets/images/MmGetPhysicalAddress.png new file mode 100644 index 00000000..b314e160 Binary files /dev/null and b/assets/images/MmGetPhysicalAddress.png differ diff --git a/assets/images/MmGetVirtualForPhysical-IDA.png b/assets/images/MmGetVirtualForPhysical-IDA.png new file mode 100644 index 00000000..f5696adb Binary files /dev/null and b/assets/images/MmGetVirtualForPhysical-IDA.png differ diff --git a/assets/images/MmGetVirtualForPhysical.png b/assets/images/MmGetVirtualForPhysical.png new file mode 100644 index 00000000..1934cead Binary files /dev/null and b/assets/images/MmGetVirtualForPhysical.png differ diff --git a/assets/images/NGINX-logo.png b/assets/images/NGINX-logo.png new file mode 100644 index 00000000..727db757 Binary files /dev/null and b/assets/images/NGINX-logo.png differ diff --git a/assets/images/NtOpenProcessTokenEx.png b/assets/images/NtOpenProcessTokenEx.png new file mode 100644 index 00000000..53ceae94 Binary files /dev/null and b/assets/images/NtOpenProcessTokenEx.png differ diff --git a/assets/images/ObReferenceObjectByHandle-IDA.png b/assets/images/ObReferenceObjectByHandle-IDA.png new file mode 100644 index 00000000..38cee977 Binary files /dev/null and b/assets/images/ObReferenceObjectByHandle-IDA.png differ diff --git a/assets/images/ObRegisterCallback-log.png b/assets/images/ObRegisterCallback-log.png new file mode 100644 index 00000000..eb73fe8e Binary files /dev/null and b/assets/images/ObRegisterCallback-log.png differ diff --git a/assets/images/OpenProcessToken.png b/assets/images/OpenProcessToken.png new file mode 100644 index 00000000..cb7277a0 Binary files /dev/null and b/assets/images/OpenProcessToken.png differ diff --git a/assets/images/PML5E-structure.png b/assets/images/PML5E-structure.png new file mode 100644 index 00000000..dc1ad5ab Binary files /dev/null and b/assets/images/PML5E-structure.png differ diff --git a/assets/images/PTE-in-PFN.png b/assets/images/PTE-in-PFN.png new file mode 100644 index 00000000..c13b9419 Binary files /dev/null and b/assets/images/PTE-in-PFN.png differ diff --git a/assets/images/PreOperationCallbacks-checks.png b/assets/images/PreOperationCallbacks-checks.png new file mode 100644 index 00000000..d81f4590 Binary files /dev/null and b/assets/images/PreOperationCallbacks-checks.png differ diff --git a/assets/images/PsOpenProcess-attributes-kernel.png b/assets/images/PsOpenProcess-attributes-kernel.png new file mode 100644 index 00000000..c5622591 Binary files /dev/null and b/assets/images/PsOpenProcess-attributes-kernel.png differ diff --git a/assets/images/PsOpenProcess.png b/assets/images/PsOpenProcess.png new file mode 100644 index 00000000..5b1c4fc3 Binary files /dev/null and b/assets/images/PsOpenProcess.png differ diff --git a/assets/images/Qt-designer-start.png b/assets/images/Qt-designer-start.png new file mode 100644 index 00000000..1df4698d Binary files /dev/null and b/assets/images/Qt-designer-start.png differ diff --git a/assets/images/Qt-logostrap-CMYK.png b/assets/images/Qt-logostrap-CMYK.png new file mode 100644 index 00000000..7b3af6ef Binary files /dev/null and b/assets/images/Qt-logostrap-CMYK.png differ diff --git a/assets/images/Qt-property-editor.png b/assets/images/Qt-property-editor.png new file mode 100644 index 00000000..9b9e94d0 Binary files /dev/null and b/assets/images/Qt-property-editor.png differ diff --git a/assets/images/SeDebugPrivilege.png b/assets/images/SeDebugPrivilege.png new file mode 100644 index 00000000..f6ddfe0c Binary files /dev/null and b/assets/images/SeDebugPrivilege.png differ diff --git a/assets/images/VMCS-auditor-cover-1.png b/assets/images/VMCS-auditor-cover-1.png new file mode 100644 index 00000000..1d702e05 Binary files /dev/null and b/assets/images/VMCS-auditor-cover-1.png differ diff --git a/assets/images/VMCS-auditor-cover-2.jpg b/assets/images/VMCS-auditor-cover-2.jpg new file mode 100644 index 00000000..112627ce Binary files /dev/null and b/assets/images/VMCS-auditor-cover-2.jpg differ diff --git a/assets/images/VMCS-auditor-with-RDMSR.png b/assets/images/VMCS-auditor-with-RDMSR.png new file mode 100644 index 00000000..10b7f5e5 Binary files /dev/null and b/assets/images/VMCS-auditor-with-RDMSR.png differ diff --git a/assets/images/VMCS-interruptibility-state.png b/assets/images/VMCS-interruptibility-state.png new file mode 100644 index 00000000..fadc193d Binary files /dev/null and b/assets/images/VMCS-interruptibility-state.png differ diff --git a/assets/images/VMCS-page-layout-1.jpeg b/assets/images/VMCS-page-layout-1.jpeg new file mode 100644 index 00000000..01318f8b Binary files /dev/null and b/assets/images/VMCS-page-layout-1.jpeg differ diff --git a/assets/images/VMCS-page-layout-2.jpg b/assets/images/VMCS-page-layout-2.jpg new file mode 100644 index 00000000..87ceea2b Binary files /dev/null and b/assets/images/VMCS-page-layout-2.jpg differ diff --git a/assets/images/VMXON-and-VMCS.png b/assets/images/VMXON-and-VMCS.png new file mode 100644 index 00000000..fb3b89e9 Binary files /dev/null and b/assets/images/VMXON-and-VMCS.png differ diff --git a/assets/images/VerifierObReferenceObjectByHandle.png b/assets/images/VerifierObReferenceObjectByHandle.png new file mode 100644 index 00000000..27328d70 Binary files /dev/null and b/assets/images/VerifierObReferenceObjectByHandle.png differ diff --git a/assets/images/Verilator_logo.png b/assets/images/Verilator_logo.png new file mode 100644 index 00000000..3e853106 Binary files /dev/null and b/assets/images/Verilator_logo.png differ diff --git a/assets/images/WDK-asm-file-error.png b/assets/images/WDK-asm-file-error.png new file mode 100644 index 00000000..2621abc7 Binary files /dev/null and b/assets/images/WDK-asm-file-error.png differ diff --git a/assets/images/WDK-change-driver-entry.png b/assets/images/WDK-change-driver-entry.png new file mode 100644 index 00000000..dcc6c60e Binary files /dev/null and b/assets/images/WDK-change-driver-entry.png differ diff --git a/assets/images/WPP-tracing-browse-pdb-file.png b/assets/images/WPP-tracing-browse-pdb-file.png new file mode 100644 index 00000000..b1f2d4e7 Binary files /dev/null and b/assets/images/WPP-tracing-browse-pdb-file.png differ diff --git a/assets/images/WPP-tracing-configurations.png b/assets/images/WPP-tracing-configurations.png new file mode 100644 index 00000000..8aa4a81e Binary files /dev/null and b/assets/images/WPP-tracing-configurations.png differ diff --git a/assets/images/WPP-tracing-log-session.png b/assets/images/WPP-tracing-log-session.png new file mode 100644 index 00000000..d3b29cc9 Binary files /dev/null and b/assets/images/WPP-tracing-log-session.png differ diff --git a/assets/images/WPP-tracing-providers.png b/assets/images/WPP-tracing-providers.png new file mode 100644 index 00000000..c9120967 Binary files /dev/null and b/assets/images/WPP-tracing-providers.png differ diff --git a/assets/images/WPP-tracing-results.png b/assets/images/WPP-tracing-results.png new file mode 100644 index 00000000..6be14d27 Binary files /dev/null and b/assets/images/WPP-tracing-results.png differ diff --git a/assets/images/access-rights-cover.png b/assets/images/access-rights-cover.png new file mode 100644 index 00000000..d5cec22f Binary files /dev/null and b/assets/images/access-rights-cover.png differ diff --git a/assets/images/add-asm-file.png b/assets/images/add-asm-file.png new file mode 100644 index 00000000..60257d51 Binary files /dev/null and b/assets/images/add-asm-file.png differ diff --git a/assets/images/add-cpp-file-to-driver.png b/assets/images/add-cpp-file-to-driver.png new file mode 100644 index 00000000..c3999c38 Binary files /dev/null and b/assets/images/add-cpp-file-to-driver.png differ diff --git a/assets/images/android-package-1.png b/assets/images/android-package-1.png new file mode 100644 index 00000000..37f03d7d Binary files /dev/null and b/assets/images/android-package-1.png differ diff --git a/assets/images/android-package-2.png b/assets/images/android-package-2.png new file mode 100644 index 00000000..c85a423c Binary files /dev/null and b/assets/images/android-package-2.png differ diff --git a/assets/images/anime-boy-girl-cycle.jpg b/assets/images/anime-boy-girl-cycle.jpg new file mode 100644 index 00000000..71997ebc Binary files /dev/null and b/assets/images/anime-boy-girl-cycle.jpg differ diff --git a/assets/images/anime-girl-aqua-blue.jpg b/assets/images/anime-girl-aqua-blue.jpg new file mode 100644 index 00000000..db04c975 Binary files /dev/null and b/assets/images/anime-girl-aqua-blue.jpg differ diff --git a/assets/images/anime-girl-bloom.jpg b/assets/images/anime-girl-bloom.jpg new file mode 100644 index 00000000..312ecdf0 Binary files /dev/null and b/assets/images/anime-girl-bloom.jpg differ diff --git a/assets/images/anime-girl-blue-moon.jpg b/assets/images/anime-girl-blue-moon.jpg new file mode 100644 index 00000000..388b2272 Binary files /dev/null and b/assets/images/anime-girl-blue-moon.jpg differ diff --git a/assets/images/anime-girl-butterfly.jpg b/assets/images/anime-girl-butterfly.jpg new file mode 100644 index 00000000..ed3b7e7e Binary files /dev/null and b/assets/images/anime-girl-butterfly.jpg differ diff --git a/assets/images/anime-girl-computer-animated.jpg b/assets/images/anime-girl-computer-animated.jpg new file mode 100644 index 00000000..269d49a9 Binary files /dev/null and b/assets/images/anime-girl-computer-animated.jpg differ diff --git a/assets/images/anime-girl-designing.jpg b/assets/images/anime-girl-designing.jpg new file mode 100644 index 00000000..ab04fb0c Binary files /dev/null and b/assets/images/anime-girl-designing.jpg differ diff --git a/assets/images/anime-girl-in-city.png b/assets/images/anime-girl-in-city.png new file mode 100644 index 00000000..8340a78b Binary files /dev/null and b/assets/images/anime-girl-in-city.png differ diff --git a/assets/images/anime-girl-kizuna-ai.jpg b/assets/images/anime-girl-kizuna-ai.jpg new file mode 100644 index 00000000..e0c36adc Binary files /dev/null and b/assets/images/anime-girl-kizuna-ai.jpg differ diff --git a/assets/images/anime-girl-night.jpg b/assets/images/anime-girl-night.jpg new file mode 100644 index 00000000..39f5070d Binary files /dev/null and b/assets/images/anime-girl-night.jpg differ diff --git a/assets/images/anime-girl-playing-guitar.png b/assets/images/anime-girl-playing-guitar.png new file mode 100644 index 00000000..048892b1 Binary files /dev/null and b/assets/images/anime-girl-playing-guitar.png differ diff --git a/assets/images/anime-girl-playing.jpg b/assets/images/anime-girl-playing.jpg new file mode 100644 index 00000000..ce6bf5fe Binary files /dev/null and b/assets/images/anime-girl-playing.jpg differ diff --git a/assets/images/anime-girl-reading-book.jpg b/assets/images/anime-girl-reading-book.jpg new file mode 100644 index 00000000..15ad3783 Binary files /dev/null and b/assets/images/anime-girl-reading-book.jpg differ diff --git a/assets/images/anime-girl-sitting.jpg b/assets/images/anime-girl-sitting.jpg new file mode 100644 index 00000000..bbb7e2a4 Binary files /dev/null and b/assets/images/anime-girl-sitting.jpg differ diff --git a/assets/images/anime-girl-walking-moon.jpeg b/assets/images/anime-girl-walking-moon.jpeg new file mode 100644 index 00000000..9988a388 Binary files /dev/null and b/assets/images/anime-girl-walking-moon.jpeg differ diff --git a/assets/images/anime-girl-watching-city.jpg b/assets/images/anime-girl-watching-city.jpg new file mode 100644 index 00000000..5145b622 Binary files /dev/null and b/assets/images/anime-girl-watching-city.jpg differ diff --git a/assets/images/anime-girl-white.png b/assets/images/anime-girl-white.png new file mode 100644 index 00000000..59795d77 Binary files /dev/null and b/assets/images/anime-girl-white.png differ diff --git a/assets/images/anime-girl-wind-scarf.jpg b/assets/images/anime-girl-wind-scarf.jpg new file mode 100644 index 00000000..cc1d91a5 Binary files /dev/null and b/assets/images/anime-girl-wind-scarf.jpg differ diff --git a/assets/images/anime-girls-drinking-tea.jpg b/assets/images/anime-girls-drinking-tea.jpg new file mode 100644 index 00000000..b02665fc Binary files /dev/null and b/assets/images/anime-girls-drinking-tea.jpg differ diff --git a/assets/images/anime-hvfs-part-8-1.jpg b/assets/images/anime-hvfs-part-8-1.jpg new file mode 100644 index 00000000..0bb4e174 Binary files /dev/null and b/assets/images/anime-hvfs-part-8-1.jpg differ diff --git a/assets/images/anime-moon.jpg b/assets/images/anime-moon.jpg new file mode 100644 index 00000000..9f0940c9 Binary files /dev/null and b/assets/images/anime-moon.jpg differ diff --git a/assets/images/anime-snowman.jpg b/assets/images/anime-snowman.jpg new file mode 100644 index 00000000..ef2ac2a1 Binary files /dev/null and b/assets/images/anime-snowman.jpg differ diff --git a/assets/images/anime-watching-computer.jpg b/assets/images/anime-watching-computer.jpg new file mode 100644 index 00000000..1b6e3ed0 Binary files /dev/null and b/assets/images/anime-watching-computer.jpg differ diff --git a/assets/images/aninme-girl-watching-monitor.jpg b/assets/images/aninme-girl-watching-monitor.jpg new file mode 100644 index 00000000..bee03c42 Binary files /dev/null and b/assets/images/aninme-girl-watching-monitor.jpg differ diff --git a/assets/images/anti-ransomware-1.png b/assets/images/anti-ransomware-1.png new file mode 100644 index 00000000..2d4aa9ca Binary files /dev/null and b/assets/images/anti-ransomware-1.png differ diff --git a/assets/images/anti-ransomware-2.png b/assets/images/anti-ransomware-2.png new file mode 100644 index 00000000..8d763c5c Binary files /dev/null and b/assets/images/anti-ransomware-2.png differ diff --git a/assets/images/anti-ransomware-3.png b/assets/images/anti-ransomware-3.png new file mode 100644 index 00000000..5289f706 Binary files /dev/null and b/assets/images/anti-ransomware-3.png differ diff --git a/assets/images/anti-vm-bypass.png b/assets/images/anti-vm-bypass.png new file mode 100644 index 00000000..b2485fe9 Binary files /dev/null and b/assets/images/anti-vm-bypass.png differ diff --git a/assets/images/asciinema-bind9.png b/assets/images/asciinema-bind9.png new file mode 100644 index 00000000..fe5bef93 Binary files /dev/null and b/assets/images/asciinema-bind9.png differ diff --git a/assets/images/assembly-jmp.png b/assets/images/assembly-jmp.png new file mode 100644 index 00000000..cc4d2d98 Binary files /dev/null and b/assets/images/assembly-jmp.png differ diff --git a/assets/images/assembly-table.png b/assets/images/assembly-table.png new file mode 100644 index 00000000..e3f5226b Binary files /dev/null and b/assets/images/assembly-table.png differ diff --git a/assets/images/avatar.png b/assets/images/avatar.png new file mode 100644 index 00000000..4ea72cd7 Binary files /dev/null and b/assets/images/avatar.png differ diff --git a/assets/images/backup-ca-01.jpg b/assets/images/backup-ca-01.jpg new file mode 100644 index 00000000..a5f2780d Binary files /dev/null and b/assets/images/backup-ca-01.jpg differ diff --git a/assets/images/bios-vmware.png b/assets/images/bios-vmware.png new file mode 100644 index 00000000..c3f0eee8 Binary files /dev/null and b/assets/images/bios-vmware.png differ diff --git a/assets/images/bochs-cover.png b/assets/images/bochs-cover.png new file mode 100644 index 00000000..29aea6a9 Binary files /dev/null and b/assets/images/bochs-cover.png differ diff --git a/assets/images/bochs-debugger-gui.png b/assets/images/bochs-debugger-gui.png new file mode 100644 index 00000000..32989916 Binary files /dev/null and b/assets/images/bochs-debugger-gui.png differ diff --git a/assets/images/bochs-gui.png b/assets/images/bochs-gui.png new file mode 100644 index 00000000..8d135994 Binary files /dev/null and b/assets/images/bochs-gui.png differ diff --git a/assets/images/bochs-log.gif b/assets/images/bochs-log.gif new file mode 100644 index 00000000..f6439e69 Binary files /dev/null and b/assets/images/bochs-log.gif differ diff --git a/assets/images/bochs-starting-windows.png b/assets/images/bochs-starting-windows.png new file mode 100644 index 00000000..d89cc3e8 Binary files /dev/null and b/assets/images/bochs-starting-windows.png differ diff --git a/assets/images/bram-36kb-xilinx.jpg b/assets/images/bram-36kb-xilinx.jpg new file mode 100644 index 00000000..19142a8a Binary files /dev/null and b/assets/images/bram-36kb-xilinx.jpg differ diff --git a/assets/images/break-handler.png b/assets/images/break-handler.png new file mode 100644 index 00000000..d0431cd5 Binary files /dev/null and b/assets/images/break-handler.png differ diff --git a/assets/images/build-pin-tool-2.png b/assets/images/build-pin-tool-2.png new file mode 100644 index 00000000..a1da4c35 Binary files /dev/null and b/assets/images/build-pin-tool-2.png differ diff --git a/assets/images/build-pin-tool.png b/assets/images/build-pin-tool.png new file mode 100644 index 00000000..90ea92d1 Binary files /dev/null and b/assets/images/build-pin-tool.png differ diff --git a/assets/images/calc-hex-computation.png b/assets/images/calc-hex-computation.png new file mode 100644 index 00000000..202b73d7 Binary files /dev/null and b/assets/images/calc-hex-computation.png differ diff --git a/assets/images/call-gate-structure.png b/assets/images/call-gate-structure.png new file mode 100644 index 00000000..21f2f566 Binary files /dev/null and b/assets/images/call-gate-structure.png differ diff --git a/assets/images/capture-kernel.png b/assets/images/capture-kernel.png new file mode 100644 index 00000000..0a471849 Binary files /dev/null and b/assets/images/capture-kernel.png differ diff --git a/assets/images/chameleon-1.jpg b/assets/images/chameleon-1.jpg new file mode 100644 index 00000000..a0bcbb34 Binary files /dev/null and b/assets/images/chameleon-1.jpg differ diff --git a/assets/images/chameleon-2.jpeg b/assets/images/chameleon-2.jpeg new file mode 100644 index 00000000..6bda3ce2 Binary files /dev/null and b/assets/images/chameleon-2.jpeg differ diff --git a/assets/images/change-to-x64.png b/assets/images/change-to-x64.png new file mode 100644 index 00000000..4aec866f Binary files /dev/null and b/assets/images/change-to-x64.png differ diff --git a/assets/images/chitgar-lake.jpg b/assets/images/chitgar-lake.jpg new file mode 100644 index 00000000..378c549c Binary files /dev/null and b/assets/images/chitgar-lake.jpg differ diff --git a/assets/images/cisco-sf100.jpg b/assets/images/cisco-sf100.jpg new file mode 100644 index 00000000..4ab4dbca Binary files /dev/null and b/assets/images/cisco-sf100.jpg differ diff --git a/assets/images/cmd-cover.jpg b/assets/images/cmd-cover.jpg new file mode 100644 index 00000000..1b6ad9a2 Binary files /dev/null and b/assets/images/cmd-cover.jpg differ diff --git a/assets/images/colorful-pencils.jpg b/assets/images/colorful-pencils.jpg new file mode 100644 index 00000000..f95eaaf5 Binary files /dev/null and b/assets/images/colorful-pencils.jpg differ diff --git a/assets/images/compute-handle.png b/assets/images/compute-handle.png new file mode 100644 index 00000000..bcdae37b Binary files /dev/null and b/assets/images/compute-handle.png differ diff --git a/assets/images/control-register-access.png b/assets/images/control-register-access.png new file mode 100644 index 00000000..45740667 Binary files /dev/null and b/assets/images/control-register-access.png differ diff --git a/assets/images/cpuid-handle-1.png b/assets/images/cpuid-handle-1.png new file mode 100644 index 00000000..6a8d5d5e Binary files /dev/null and b/assets/images/cpuid-handle-1.png differ diff --git a/assets/images/cpuid-handle-2.png b/assets/images/cpuid-handle-2.png new file mode 100644 index 00000000..45d74cea Binary files /dev/null and b/assets/images/cpuid-handle-2.png differ diff --git a/assets/images/cpuid-handle-3.png b/assets/images/cpuid-handle-3.png new file mode 100644 index 00000000..dacb896c Binary files /dev/null and b/assets/images/cpuid-handle-3.png differ diff --git a/assets/images/cpuid-handle-4.png b/assets/images/cpuid-handle-4.png new file mode 100644 index 00000000..15c8fc95 Binary files /dev/null and b/assets/images/cpuid-handle-4.png differ diff --git a/assets/images/create-kmdf-project.png b/assets/images/create-kmdf-project.png new file mode 100644 index 00000000..316e7740 Binary files /dev/null and b/assets/images/create-kmdf-project.png differ diff --git a/assets/images/debian-logo.jpg b/assets/images/debian-logo.jpg new file mode 100644 index 00000000..b92587fe Binary files /dev/null and b/assets/images/debian-logo.jpg differ diff --git a/assets/images/driver-signature-enforcement-boot-menu.png b/assets/images/driver-signature-enforcement-boot-menu.png new file mode 100644 index 00000000..2b1c1a84 Binary files /dev/null and b/assets/images/driver-signature-enforcement-boot-menu.png differ diff --git a/assets/images/enum-all-handles-1.png b/assets/images/enum-all-handles-1.png new file mode 100644 index 00000000..47411649 Binary files /dev/null and b/assets/images/enum-all-handles-1.png differ diff --git a/assets/images/enum-all-handles-2.png b/assets/images/enum-all-handles-2.png new file mode 100644 index 00000000..6c1c44d6 Binary files /dev/null and b/assets/images/enum-all-handles-2.png differ diff --git a/assets/images/ept-entries.png b/assets/images/ept-entries.png new file mode 100644 index 00000000..35762a9e Binary files /dev/null and b/assets/images/ept-entries.png differ diff --git a/assets/images/event-inject-and-exception-bitmap.gif b/assets/images/event-inject-and-exception-bitmap.gif new file mode 100644 index 00000000..657586ad Binary files /dev/null and b/assets/images/event-inject-and-exception-bitmap.gif differ diff --git a/assets/images/event-inject-and-exception-bitmap.png b/assets/images/event-inject-and-exception-bitmap.png new file mode 100644 index 00000000..676eb9ff Binary files /dev/null and b/assets/images/event-inject-and-exception-bitmap.png differ diff --git a/assets/images/exit-qualification-for-ept-violation.png b/assets/images/exit-qualification-for-ept-violation.png new file mode 100644 index 00000000..ee767265 Binary files /dev/null and b/assets/images/exit-qualification-for-ept-violation.png differ diff --git a/assets/images/filesystem-diagram.png b/assets/images/filesystem-diagram.png new file mode 100644 index 00000000..6c99cc9c Binary files /dev/null and b/assets/images/filesystem-diagram.png differ diff --git a/assets/images/fixed-ranges-MTRRs.png b/assets/images/fixed-ranges-MTRRs.png new file mode 100644 index 00000000..bc29c7d6 Binary files /dev/null and b/assets/images/fixed-ranges-MTRRs.png differ diff --git a/assets/images/gds-II-blinky.jpg b/assets/images/gds-II-blinky.jpg new file mode 100644 index 00000000..2031425d Binary files /dev/null and b/assets/images/gds-II-blinky.jpg differ diff --git a/assets/images/gds-II-blinky.svg b/assets/images/gds-II-blinky.svg new file mode 100644 index 00000000..b808d058 --- /dev/null +++ b/assets/images/gds-II-blinky.svg @@ -0,0 +1,14191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +A1 +B1 +A1 +A1 +Y +B1 +Y +A2 +VPWR +VGND +VPB +VNB +a21oi_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VGND +VPWR +VPB +VNB +decap_12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VPWR +VGND +VPB +VNB +decap_6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VPWR +VGND +VPB +VNB +decap_3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +X +A +X +A +X +VGND +VPWR +VPB +VNB +clkbuf_4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VGND +VPWR +X +X +X +A +VPB +VNB +clkbuf_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VPWR +VGND +X +X +X +X +X +X +B +A +A +VPB +VNB +and2_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +A +X +B +VPB +VNB +VGND +VPWR +or2_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VPWR +VGND +VPB +VNB +A +X +A +X +X +X +X +X +X +X +dlymetal6s2s_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +A +Y +Y +Y +VPB +VNB +VPWR +VGND +inv_2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VPWR +VGND +VPWR +VGND +X +X +X +A +VPB +VNB +buf_2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Y +Y +A1 +Y +Y +B1_N +Y +A1 +A2 +B1_N +A1 +VPWR +VGND +VPB +VNB +a21boi_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VGND +VPWR +VPB +VNB +decap_4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +A2 +A1 +A1 +A1 +B1 +X +X +X +X +X +X +VPWR +VGND +VPB +VNB +a21o_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Y +A +B +VPB +VNB +VGND +VPWR +nor2_4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +C1 +B1 +A2 +A1 +X +X +X +VNB +VPB +VGND +VPWR +o211a_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +A2 +B1 +A1 +A3 +A2 +A3 +X +A1 +X +X +B1 +VPB +VNB +VGND +VPWR +a31o_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +X +A_N +A_N +B +X +A_N +B +X +VGND +VPWR +VPB +VNB +and2b_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VGND +VPWR +A2 +A2 +A2 +A1 +A2 +B1 +Y +Y +VPB +VNB +o21ai_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +X +A1 +B1 +A2 +VPB +VNB +VGND +VPWR +o21a_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +C +Y +A +B +VPB +VNB +VGND +VPWR +nor3_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Y +Y +Y +A +B +VPB +VNB +VGND +VPWR +nand2_2 + + + + + + + + + + + + + + + + + + + + + + + + +VPWR +VGND +tapvpwrvgnd_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VPWR +VGND +VPB +VNB +decap_8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +B +Y +A +VPB +VNB +VGND +VPWR +nor2_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +dfxtp_1 +VPB +VPB +VNB +VNB +Q +CLK +D +VPWR +VGND + + + + + + + + + + + + + + + + + + + +VGND +VPWR +VPB +VNB +fill_2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Y +A +C +B +VPB +VNB +VGND +VPWR +nand3_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +C +A +X +C +B +B +C +B +C +D +D +VPWR +VGND +VPB +VNB +or4_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +B +X +X +A +C +X +VPWR +VGND +VPWR +VGND +VPB +VNB +and3_2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +X +C +C +A +C +B +B +D +X +X +X +X +X +VGND +VPWR +VPB +VNB +and4_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +D +C +D +Y +A +B +VPB +VNB +VGND +VPWR +nor4_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +A +A +X +X +X +X +VGND +VPWR +VGND +VPWR +VPB +VNB +clkbuf_2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VPWR +VGND +A +X +A +X +X +X +X +X +B +VPB +VNB +and2_2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +B1 +Y +Y +Y +A2 +Y +A1 +A1 +B1 +VGND +VPWR +VPB +VNB +o21ai_2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +A +A +C_N +X +A +B +B +B +VGND +VPWR +VPB +VNB +or3b_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VGND +VPWR +X +X +B +A +C +VPB +VNB +and3_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Y +Y +Y +B +A +VPB +VNB +VGND +VPWR +nand2_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +B1 +D1 +B1 +C1 +Y +A2 +A1 +D1 +D1 +D1 +C1 +C1 +C1 +B1 +B1 +Y +Y +Y +Y +A2 +A2 +VNB +VNB +VPWR +VGND +VPB +a2111oi_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +B +D_N +B +B +A +X +C +C +B +C +D_N +C +VPWR +VGND +VPB +VNB +or4b_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +X +C +C +A +C +B +B +D +X +X +X +X +X +VNB +VPB +VGND +VPWR +and4_2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +dfxtp_2 +VPB +VPB +VNB +VNB +Q +CLK +D +VPWR +VGND + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +A_N +B +X +C +A_N +X +A_N +VGND +VPWR +VPB +VNB +and3b_1 + + + + + + + + + + + + + + + + + +VPWR +VGND +VPB +VNB +fill_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +DIODE +DIODE +DIODE +DIODE +DIODE +DIODE +DIODE +DIODE +DIODE +DIODE +DIODE +DIODE +VGND +VPWR +VPB +VNB +diode_2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VGND +VPWR +_000_ +_001_ +_002_ +_003_ +_004_ +_005_ +_006_ +_007_ +_008_ +_009_ +_010_ +_011_ +_012_ +_013_ +_014_ +_015_ +_016_ +_017_ +_018_ +_019_ +_020_ +_021_ +_022_ +_023_ +_024_ +_025_ +_026_ +_027_ +_028_ +_029_ +_030_ +_031_ +_032_ +_033_ +_034_ +_035_ +_036_ +_037_ +_038_ +_039_ +_040_ +_041_ +_042_ +_043_ +_044_ +_045_ +_046_ +_047_ +_048_ +_049_ +_050_ +_051_ +_052_ +_053_ +_054_ +_055_ +_056_ +_057_ +_058_ +_059_ +_060_ +_061_ +_062_ +_063_ +_064_ +_065_ +_066_ +_067_ +_068_ +_069_ +_070_ +_071_ +_072_ +_073_ +_074_ +_075_ +_076_ +_077_ +_078_ +_079_ +_080_ +_081_ +_082_ +_083_ +_084_ +_085_ +_086_ +_087_ +_088_ +_089_ +_090_ +_091_ +_092_ +_093_ +_094_ +_095_ +_096_ +_097_ +_098_ +_099_ +_100_ +_101_ +_102_ +_103_ +_104_ +_105_ +_106_ +_107_ +_108_ +_109_ +_110_ +_111_ +_112_ +_113_ +_114_ +_115_ +_116_ +_117_ +_118_ +clock +cntReg\[0\] +cntReg\[10\] +cntReg\[11\] +cntReg\[12\] +cntReg\[13\] +cntReg\[14\] +cntReg\[15\] +cntReg\[16\] +cntReg\[17\] +cntReg\[18\] +cntReg\[19\] +cntReg\[1\] +cntReg\[20\] +cntReg\[21\] +cntReg\[22\] +cntReg\[23\] +cntReg\[24\] +cntReg\[25\] +cntReg\[26\] +cntReg\[27\] +cntReg\[28\] +cntReg\[29\] +cntReg\[2\] +cntReg\[30\] +cntReg\[31\] +cntReg\[3\] +cntReg\[4\] +cntReg\[5\] +cntReg\[6\] +cntReg\[7\] +cntReg\[8\] +cntReg\[9\] +io_led +net1 +net2 +net3 +net4 +net5 +net6 +net7 +reset +VGND +VGND +VGND +VGND +VGND +VGND +VGND +VPWR +VPWR +VPWR +VPWR +VPWR +VPWR +VPWR +clock +io_led +reset + + \ No newline at end of file diff --git a/assets/images/gre-ipsec-tunnel-transport.gif b/assets/images/gre-ipsec-tunnel-transport.gif new file mode 100644 index 00000000..04c60a0c Binary files /dev/null and b/assets/images/gre-ipsec-tunnel-transport.gif differ diff --git a/assets/images/gre-over-ipsec.jpg b/assets/images/gre-over-ipsec.jpg new file mode 100644 index 00000000..c1ab699c Binary files /dev/null and b/assets/images/gre-over-ipsec.jpg differ diff --git a/assets/images/hal-toggle.jpg b/assets/images/hal-toggle.jpg new file mode 100644 index 00000000..22fee703 Binary files /dev/null and b/assets/images/hal-toggle.jpg differ diff --git a/assets/images/hal_screenshot.jpg b/assets/images/hal_screenshot.jpg new file mode 100644 index 00000000..ba9a8c43 Binary files /dev/null and b/assets/images/hal_screenshot.jpg differ diff --git a/assets/images/hardware-design-stack.jpg b/assets/images/hardware-design-stack.jpg new file mode 100644 index 00000000..2d6c0af5 Binary files /dev/null and b/assets/images/hardware-design-stack.jpg differ diff --git a/assets/images/hello-world.jpg b/assets/images/hello-world.jpg new file mode 100644 index 00000000..5eed3c17 Binary files /dev/null and b/assets/images/hello-world.jpg differ diff --git a/assets/images/hidden-hook-example-exec.gif b/assets/images/hidden-hook-example-exec.gif new file mode 100644 index 00000000..3d2b517e Binary files /dev/null and b/assets/images/hidden-hook-example-exec.gif differ diff --git a/assets/images/hidden-hook-example-exec.png b/assets/images/hidden-hook-example-exec.png new file mode 100644 index 00000000..3d981cb9 Binary files /dev/null and b/assets/images/hidden-hook-example-exec.png differ diff --git a/assets/images/hidden-hook-example-read-write-2.png b/assets/images/hidden-hook-example-read-write-2.png new file mode 100644 index 00000000..a34f3554 Binary files /dev/null and b/assets/images/hidden-hook-example-read-write-2.png differ diff --git a/assets/images/hidden-hook-example-read-write.gif b/assets/images/hidden-hook-example-read-write.gif new file mode 100644 index 00000000..24b95245 Binary files /dev/null and b/assets/images/hidden-hook-example-read-write.gif differ diff --git a/assets/images/hidden-hook-example-read-write.png b/assets/images/hidden-hook-example-read-write.png new file mode 100644 index 00000000..657662b2 Binary files /dev/null and b/assets/images/hidden-hook-example-read-write.png differ diff --git a/assets/images/hls-testing.jpg b/assets/images/hls-testing.jpg new file mode 100644 index 00000000..39a9c147 Binary files /dev/null and b/assets/images/hls-testing.jpg differ diff --git a/assets/images/hlt-execution.png b/assets/images/hlt-execution.png new file mode 100644 index 00000000..9fd60671 Binary files /dev/null and b/assets/images/hlt-execution.png differ diff --git a/assets/images/hvfs-part7-demo1.png b/assets/images/hvfs-part7-demo1.png new file mode 100644 index 00000000..40639678 Binary files /dev/null and b/assets/images/hvfs-part7-demo1.png differ diff --git a/assets/images/hvfs-part7-demo2.png b/assets/images/hvfs-part7-demo2.png new file mode 100644 index 00000000..bf51ca6f Binary files /dev/null and b/assets/images/hvfs-part7-demo2.png differ diff --git a/assets/images/hvfs-part7-demo3.png b/assets/images/hvfs-part7-demo3.png new file mode 100644 index 00000000..07459fc2 Binary files /dev/null and b/assets/images/hvfs-part7-demo3.png differ diff --git a/assets/images/hvfs-part7-demo4.png b/assets/images/hvfs-part7-demo4.png new file mode 100644 index 00000000..098eeb3d Binary files /dev/null and b/assets/images/hvfs-part7-demo4.png differ diff --git a/assets/images/hyperdbg-and-windbg-meme-1.jpg b/assets/images/hyperdbg-and-windbg-meme-1.jpg new file mode 100644 index 00000000..769a4d2f Binary files /dev/null and b/assets/images/hyperdbg-and-windbg-meme-1.jpg differ diff --git a/assets/images/hyperdbg-and-windbg-meme-2.jpg b/assets/images/hyperdbg-and-windbg-meme-2.jpg new file mode 100644 index 00000000..51ee9402 Binary files /dev/null and b/assets/images/hyperdbg-and-windbg-meme-2.jpg differ diff --git a/assets/images/hyperdbg-cat-wallpaper.png b/assets/images/hyperdbg-cat-wallpaper.png new file mode 100644 index 00000000..c678e1f7 Binary files /dev/null and b/assets/images/hyperdbg-cat-wallpaper.png differ diff --git a/assets/images/hyperv-vmcall.png b/assets/images/hyperv-vmcall.png new file mode 100644 index 00000000..4c95a8c5 Binary files /dev/null and b/assets/images/hyperv-vmcall.png differ diff --git a/assets/images/hypervisor-from-scratch-1-cover.png b/assets/images/hypervisor-from-scratch-1-cover.png new file mode 100644 index 00000000..1ca01735 Binary files /dev/null and b/assets/images/hypervisor-from-scratch-1-cover.png differ diff --git a/assets/images/hypervisor-from-scratch-2-cover.png b/assets/images/hypervisor-from-scratch-2-cover.png new file mode 100644 index 00000000..ed8707d7 Binary files /dev/null and b/assets/images/hypervisor-from-scratch-2-cover.png differ diff --git a/assets/images/hypervisor-from-scratch-3-cover.png b/assets/images/hypervisor-from-scratch-3-cover.png new file mode 100644 index 00000000..20776f09 Binary files /dev/null and b/assets/images/hypervisor-from-scratch-3-cover.png differ diff --git a/assets/images/hypervisor-from-scratch-4-cover.png b/assets/images/hypervisor-from-scratch-4-cover.png new file mode 100644 index 00000000..a799c675 Binary files /dev/null and b/assets/images/hypervisor-from-scratch-4-cover.png differ diff --git a/assets/images/hypervisor-from-scratch-6-cover.png b/assets/images/hypervisor-from-scratch-6-cover.png new file mode 100644 index 00000000..75c3658c Binary files /dev/null and b/assets/images/hypervisor-from-scratch-6-cover.png differ diff --git a/assets/images/hypervisor-from-scratch-7-cover.png b/assets/images/hypervisor-from-scratch-7-cover.png new file mode 100644 index 00000000..b3d81f74 Binary files /dev/null and b/assets/images/hypervisor-from-scratch-7-cover.png differ diff --git a/assets/images/hypervisor-from-scratch-8-cover.png b/assets/images/hypervisor-from-scratch-8-cover.png new file mode 100644 index 00000000..6b117b15 Binary files /dev/null and b/assets/images/hypervisor-from-scratch-8-cover.png differ diff --git a/assets/images/hypervisor-from-scratch-part-5-cover.png b/assets/images/hypervisor-from-scratch-part-5-cover.png new file mode 100644 index 00000000..ce81271c Binary files /dev/null and b/assets/images/hypervisor-from-scratch-part-5-cover.png differ diff --git a/assets/images/hypervisor-loaded.png b/assets/images/hypervisor-loaded.png new file mode 100644 index 00000000..caeeae61 Binary files /dev/null and b/assets/images/hypervisor-loaded.png differ diff --git a/assets/images/hypervisor-vendor-string-ascii.png b/assets/images/hypervisor-vendor-string-ascii.png new file mode 100644 index 00000000..db582eb6 Binary files /dev/null and b/assets/images/hypervisor-vendor-string-ascii.png differ diff --git a/assets/images/hypervisor-vendor-string.png b/assets/images/hypervisor-vendor-string.png new file mode 100644 index 00000000..e50398f5 Binary files /dev/null and b/assets/images/hypervisor-vendor-string.png differ diff --git a/assets/images/ic-packages.jpg b/assets/images/ic-packages.jpg new file mode 100644 index 00000000..34dd338c Binary files /dev/null and b/assets/images/ic-packages.jpg differ diff --git a/assets/images/ic-packages.png b/assets/images/ic-packages.png new file mode 100644 index 00000000..7b1ffc84 Binary files /dev/null and b/assets/images/ic-packages.png differ diff --git a/assets/images/import-address-table.png b/assets/images/import-address-table.png new file mode 100644 index 00000000..209db963 Binary files /dev/null and b/assets/images/import-address-table.png differ diff --git a/assets/images/init-VMCS-structure.png b/assets/images/init-VMCS-structure.png new file mode 100644 index 00000000..4717d844 Binary files /dev/null and b/assets/images/init-VMCS-structure.png differ diff --git a/assets/images/intel-inside-cover.jpg b/assets/images/intel-inside-cover.jpg new file mode 100644 index 00000000..acb1a147 Binary files /dev/null and b/assets/images/intel-inside-cover.jpg differ diff --git a/assets/images/intel-x86-64-registers.jpg b/assets/images/intel-x86-64-registers.jpg new file mode 100644 index 00000000..76df90b2 Binary files /dev/null and b/assets/images/intel-x86-64-registers.jpg differ diff --git a/assets/images/ioctl-structure.png b/assets/images/ioctl-structure.png new file mode 100644 index 00000000..b1cff3fc Binary files /dev/null and b/assets/images/ioctl-structure.png differ diff --git a/assets/images/iptables-lua.png b/assets/images/iptables-lua.png new file mode 100644 index 00000000..e292c349 Binary files /dev/null and b/assets/images/iptables-lua.png differ diff --git a/assets/images/kd-DEFAULT-Mask.png b/assets/images/kd-DEFAULT-Mask.png new file mode 100644 index 00000000..f75d8af1 Binary files /dev/null and b/assets/images/kd-DEFAULT-Mask.png differ diff --git a/assets/images/kernel-debugging-1.png b/assets/images/kernel-debugging-1.png new file mode 100644 index 00000000..78efacaf Binary files /dev/null and b/assets/images/kernel-debugging-1.png differ diff --git a/assets/images/kernel-debugging-2.png b/assets/images/kernel-debugging-2.png new file mode 100644 index 00000000..2e156070 Binary files /dev/null and b/assets/images/kernel-debugging-2.png differ diff --git a/assets/images/kernel-debugging-3.png b/assets/images/kernel-debugging-3.png new file mode 100644 index 00000000..eed13a7a Binary files /dev/null and b/assets/images/kernel-debugging-3.png differ diff --git a/assets/images/kernel-debugging-4.png b/assets/images/kernel-debugging-4.png new file mode 100644 index 00000000..25a6b6df Binary files /dev/null and b/assets/images/kernel-debugging-4.png differ diff --git a/assets/images/kernel-debugging-5.png b/assets/images/kernel-debugging-5.png new file mode 100644 index 00000000..3e241f3c Binary files /dev/null and b/assets/images/kernel-debugging-5.png differ diff --git a/assets/images/kernelmode-usermode.jpg b/assets/images/kernelmode-usermode.jpg new file mode 100644 index 00000000..0390e9cf Binary files /dev/null and b/assets/images/kernelmode-usermode.jpg differ diff --git a/assets/images/kids-watching-laptop.jpg b/assets/images/kids-watching-laptop.jpg new file mode 100644 index 00000000..b34d6ce9 Binary files /dev/null and b/assets/images/kids-watching-laptop.jpg differ diff --git a/assets/images/kpcr-structure.png b/assets/images/kpcr-structure.png new file mode 100644 index 00000000..03aeafa9 Binary files /dev/null and b/assets/images/kpcr-structure.png differ diff --git a/assets/images/kprcb-structure.png b/assets/images/kprcb-structure.png new file mode 100644 index 00000000..e7591512 Binary files /dev/null and b/assets/images/kprcb-structure.png differ diff --git a/assets/images/launch-vm-return.png b/assets/images/launch-vm-return.png new file mode 100644 index 00000000..97e16b46 Binary files /dev/null and b/assets/images/launch-vm-return.png differ diff --git a/assets/images/linux-kernel-module-for-nlkb.jpg b/assets/images/linux-kernel-module-for-nlkb.jpg new file mode 100644 index 00000000..2dd79542 Binary files /dev/null and b/assets/images/linux-kernel-module-for-nlkb.jpg differ diff --git a/assets/images/linux-kernel-unified-hierarchy-cgroups-and-systemd.png b/assets/images/linux-kernel-unified-hierarchy-cgroups-and-systemd.png new file mode 100644 index 00000000..81fa3975 Binary files /dev/null and b/assets/images/linux-kernel-unified-hierarchy-cgroups-and-systemd.png differ diff --git a/assets/images/linux-tux-large.jpg b/assets/images/linux-tux-large.jpg new file mode 100644 index 00000000..679e9ff4 Binary files /dev/null and b/assets/images/linux-tux-large.jpg differ diff --git a/assets/images/m-detection-example-2.png b/assets/images/m-detection-example-2.png new file mode 100644 index 00000000..753df4a8 Binary files /dev/null and b/assets/images/m-detection-example-2.png differ diff --git a/assets/images/memory-ranges.png b/assets/images/memory-ranges.png new file mode 100644 index 00000000..e71b72da Binary files /dev/null and b/assets/images/memory-ranges.png differ diff --git a/assets/images/memory-types-in-MTRRs.png b/assets/images/memory-types-in-MTRRs.png new file mode 100644 index 00000000..3604ffa3 Binary files /dev/null and b/assets/images/memory-types-in-MTRRs.png differ diff --git a/assets/images/microsoft-active-directory.png b/assets/images/microsoft-active-directory.png new file mode 100644 index 00000000..52940bd8 Binary files /dev/null and b/assets/images/microsoft-active-directory.png differ diff --git a/assets/images/mknod-terminal.png b/assets/images/mknod-terminal.png new file mode 100644 index 00000000..3c413494 Binary files /dev/null and b/assets/images/mknod-terminal.png differ diff --git a/assets/images/mmpfn.png b/assets/images/mmpfn.png new file mode 100644 index 00000000..83307659 Binary files /dev/null and b/assets/images/mmpfn.png differ diff --git a/assets/images/modelsim-wave.jpg b/assets/images/modelsim-wave.jpg new file mode 100644 index 00000000..991a8c95 Binary files /dev/null and b/assets/images/modelsim-wave.jpg differ diff --git a/assets/images/moore1.jpg b/assets/images/moore1.jpg new file mode 100644 index 00000000..c257ef9f Binary files /dev/null and b/assets/images/moore1.jpg differ diff --git a/assets/images/moore2.jpg b/assets/images/moore2.jpg new file mode 100644 index 00000000..1593c590 Binary files /dev/null and b/assets/images/moore2.jpg differ diff --git a/assets/images/nested-virtualization-vmware-2.png b/assets/images/nested-virtualization-vmware-2.png new file mode 100644 index 00000000..1c59bbac Binary files /dev/null and b/assets/images/nested-virtualization-vmware-2.png differ diff --git a/assets/images/netlist-p1.jpg b/assets/images/netlist-p1.jpg new file mode 100644 index 00000000..15b67d0b Binary files /dev/null and b/assets/images/netlist-p1.jpg differ diff --git a/assets/images/netlist-p2.jpg b/assets/images/netlist-p2.jpg new file mode 100644 index 00000000..0d20e55e Binary files /dev/null and b/assets/images/netlist-p2.jpg differ diff --git a/assets/images/netlist-p3.jpg b/assets/images/netlist-p3.jpg new file mode 100644 index 00000000..c22185f3 Binary files /dev/null and b/assets/images/netlist-p3.jpg differ diff --git a/assets/images/netlist-p4.jpg b/assets/images/netlist-p4.jpg new file mode 100644 index 00000000..1a303a8b Binary files /dev/null and b/assets/images/netlist-p4.jpg differ diff --git a/assets/images/new-eptp-table.png b/assets/images/new-eptp-table.png new file mode 100644 index 00000000..95b5f066 Binary files /dev/null and b/assets/images/new-eptp-table.png differ diff --git a/assets/images/object-data-corruption.png b/assets/images/object-data-corruption.png new file mode 100644 index 00000000..c7521259 Binary files /dev/null and b/assets/images/object-data-corruption.png differ diff --git a/assets/images/openwrt-logo.png b/assets/images/openwrt-logo.png new file mode 100644 index 00000000..f31a65c7 Binary files /dev/null and b/assets/images/openwrt-logo.png differ diff --git a/assets/images/operation-registration-structure.png b/assets/images/operation-registration-structure.png new file mode 100644 index 00000000..37a0ff20 Binary files /dev/null and b/assets/images/operation-registration-structure.png differ diff --git a/assets/images/osr-driver-loader-gui.png b/assets/images/osr-driver-loader-gui.png new file mode 100644 index 00000000..1f4c7cb8 Binary files /dev/null and b/assets/images/osr-driver-loader-gui.png differ diff --git a/assets/images/osrdriverloader-dbgview.png b/assets/images/osrdriverloader-dbgview.png new file mode 100644 index 00000000..33619636 Binary files /dev/null and b/assets/images/osrdriverloader-dbgview.png differ diff --git a/assets/images/pfn-1-cover.png b/assets/images/pfn-1-cover.png new file mode 100644 index 00000000..99b89809 Binary files /dev/null and b/assets/images/pfn-1-cover.png differ diff --git a/assets/images/pfn-record-windbg.png b/assets/images/pfn-record-windbg.png new file mode 100644 index 00000000..de8b9903 Binary files /dev/null and b/assets/images/pfn-record-windbg.png differ diff --git a/assets/images/pfn-structure.gif b/assets/images/pfn-structure.gif new file mode 100644 index 00000000..6bcf982d Binary files /dev/null and b/assets/images/pfn-structure.gif differ diff --git a/assets/images/php-ldap-admin.png b/assets/images/php-ldap-admin.png new file mode 100644 index 00000000..923ae279 Binary files /dev/null and b/assets/images/php-ldap-admin.png differ diff --git a/assets/images/pin-based-vm-execution-controls-fields.png b/assets/images/pin-based-vm-execution-controls-fields.png new file mode 100644 index 00000000..69f1fbab Binary files /dev/null and b/assets/images/pin-based-vm-execution-controls-fields.png differ diff --git a/assets/images/pin-overview.jpg b/assets/images/pin-overview.jpg new file mode 100644 index 00000000..143bcab7 Binary files /dev/null and b/assets/images/pin-overview.jpg differ diff --git a/assets/images/pinitor-cover.png b/assets/images/pinitor-cover.png new file mode 100644 index 00000000..4bd82105 Binary files /dev/null and b/assets/images/pinitor-cover.png differ diff --git a/assets/images/pinitor-example-1.png b/assets/images/pinitor-example-1.png new file mode 100644 index 00000000..005b5e47 Binary files /dev/null and b/assets/images/pinitor-example-1.png differ diff --git a/assets/images/pinitor-example-2.png b/assets/images/pinitor-example-2.png new file mode 100644 index 00000000..d4b0cd9d Binary files /dev/null and b/assets/images/pinitor-example-2.png differ diff --git a/assets/images/pinitor-example-3.png b/assets/images/pinitor-example-3.png new file mode 100644 index 00000000..3a78a3ac Binary files /dev/null and b/assets/images/pinitor-example-3.png differ diff --git a/assets/images/primary-processor-based-vm-execution-controls-fields.png b/assets/images/primary-processor-based-vm-execution-controls-fields.png new file mode 100644 index 00000000..f28803e9 Binary files /dev/null and b/assets/images/primary-processor-based-vm-execution-controls-fields.png differ diff --git a/assets/images/processor-Mhz-value.png b/assets/images/processor-Mhz-value.png new file mode 100644 index 00000000..759b2642 Binary files /dev/null and b/assets/images/processor-Mhz-value.png differ diff --git a/assets/images/pykd-cover-2.png b/assets/images/pykd-cover-2.png new file mode 100644 index 00000000..d4010fef Binary files /dev/null and b/assets/images/pykd-cover-2.png differ diff --git a/assets/images/pykd-cover.png b/assets/images/pykd-cover.png new file mode 100644 index 00000000..2efe0440 Binary files /dev/null and b/assets/images/pykd-cover.png differ diff --git a/assets/images/query-limited-information.png b/assets/images/query-limited-information.png new file mode 100644 index 00000000..81f1aee7 Binary files /dev/null and b/assets/images/query-limited-information.png differ diff --git a/assets/images/regular-paging-structures.png b/assets/images/regular-paging-structures.png new file mode 100644 index 00000000..e98f2301 Binary files /dev/null and b/assets/images/regular-paging-structures.png differ diff --git a/assets/images/result-of-handles-from-IDA.png b/assets/images/result-of-handles-from-IDA.png new file mode 100644 index 00000000..0b0ad3a1 Binary files /dev/null and b/assets/images/result-of-handles-from-IDA.png differ diff --git a/assets/images/reversing-windows-internal-1.png b/assets/images/reversing-windows-internal-1.png new file mode 100644 index 00000000..d223870c Binary files /dev/null and b/assets/images/reversing-windows-internal-1.png differ diff --git a/assets/images/reversing-windows-internal-2.png b/assets/images/reversing-windows-internal-2.png new file mode 100644 index 00000000..ef88770f Binary files /dev/null and b/assets/images/reversing-windows-internal-2.png differ diff --git a/assets/images/reversing-windows-internal-3.png b/assets/images/reversing-windows-internal-3.png new file mode 100644 index 00000000..386a5f8c Binary files /dev/null and b/assets/images/reversing-windows-internal-3.png differ diff --git a/assets/images/reversing-windows-internals-cover.png b/assets/images/reversing-windows-internals-cover.png new file mode 100644 index 00000000..e8e53e64 Binary files /dev/null and b/assets/images/reversing-windows-internals-cover.png differ diff --git a/assets/images/ring-transitions-cover.png b/assets/images/ring-transitions-cover.png new file mode 100644 index 00000000..c8e1678b Binary files /dev/null and b/assets/images/ring-transitions-cover.png differ diff --git a/assets/images/rsp-problem-1.png b/assets/images/rsp-problem-1.png new file mode 100644 index 00000000..ade86deb Binary files /dev/null and b/assets/images/rsp-problem-1.png differ diff --git a/assets/images/running-HVFS-1.png b/assets/images/running-HVFS-1.png new file mode 100644 index 00000000..364051ca Binary files /dev/null and b/assets/images/running-HVFS-1.png differ diff --git a/assets/images/running-HVFS-2.png b/assets/images/running-HVFS-2.png new file mode 100644 index 00000000..13ce6f26 Binary files /dev/null and b/assets/images/running-HVFS-2.png differ diff --git a/assets/images/running-HVFS-3.png b/assets/images/running-HVFS-3.png new file mode 100644 index 00000000..23d5a4b7 Binary files /dev/null and b/assets/images/running-HVFS-3.png differ diff --git a/assets/images/screenshot_waveform_viewer.jpg b/assets/images/screenshot_waveform_viewer.jpg new file mode 100644 index 00000000..974aa603 Binary files /dev/null and b/assets/images/screenshot_waveform_viewer.jpg differ diff --git a/assets/images/secondary-processor-based-vm-execution-controls-fields.png b/assets/images/secondary-processor-based-vm-execution-controls-fields.png new file mode 100644 index 00000000..0cc71dba Binary files /dev/null and b/assets/images/secondary-processor-based-vm-execution-controls-fields.png differ diff --git a/assets/images/service-descriptor-table.jpg b/assets/images/service-descriptor-table.jpg new file mode 100644 index 00000000..b8b055fd Binary files /dev/null and b/assets/images/service-descriptor-table.jpg differ diff --git a/assets/images/shadow-page-tables-1.png b/assets/images/shadow-page-tables-1.png new file mode 100644 index 00000000..be5fcc75 Binary files /dev/null and b/assets/images/shadow-page-tables-1.png differ diff --git a/assets/images/squid-proxy-logo.png b/assets/images/squid-proxy-logo.png new file mode 100644 index 00000000..dc68997f Binary files /dev/null and b/assets/images/squid-proxy-logo.png differ diff --git a/assets/images/strongswan-vpn.png b/assets/images/strongswan-vpn.png new file mode 100644 index 00000000..d5a7cf7f Binary files /dev/null and b/assets/images/strongswan-vpn.png differ diff --git a/assets/images/syscall-hook-example-1.gif b/assets/images/syscall-hook-example-1.gif new file mode 100644 index 00000000..023ea1e5 Binary files /dev/null and b/assets/images/syscall-hook-example-1.gif differ diff --git a/assets/images/syscall-hook-example-2.png b/assets/images/syscall-hook-example-2.png new file mode 100644 index 00000000..a518d66b Binary files /dev/null and b/assets/images/syscall-hook-example-2.png differ diff --git a/assets/images/syscall-hook-example-3.png b/assets/images/syscall-hook-example-3.png new file mode 100644 index 00000000..2b7f0338 Binary files /dev/null and b/assets/images/syscall-hook-example-3.png differ diff --git a/assets/images/systemd-image.png b/assets/images/systemd-image.png new file mode 100644 index 00000000..39d3c714 Binary files /dev/null and b/assets/images/systemd-image.png differ diff --git a/assets/images/systemd-logo.gif b/assets/images/systemd-logo.gif new file mode 100644 index 00000000..b4b756a4 Binary files /dev/null and b/assets/images/systemd-logo.gif differ diff --git a/assets/images/terminate-vmx.png b/assets/images/terminate-vmx.png new file mode 100644 index 00000000..0557df35 Binary files /dev/null and b/assets/images/terminate-vmx.png differ diff --git a/assets/images/tutorials-cover.jpg b/assets/images/tutorials-cover.jpg new file mode 100644 index 00000000..d2d2528e Binary files /dev/null and b/assets/images/tutorials-cover.jpg differ diff --git a/assets/images/type-index.png b/assets/images/type-index.png new file mode 100644 index 00000000..11a4844e Binary files /dev/null and b/assets/images/type-index.png differ diff --git a/assets/images/type-info-callbacks-hooks.png b/assets/images/type-info-callbacks-hooks.png new file mode 100644 index 00000000..1a15588f Binary files /dev/null and b/assets/images/type-info-callbacks-hooks.png differ diff --git a/assets/images/usermode-far-call.png b/assets/images/usermode-far-call.png new file mode 100644 index 00000000..d0ff3135 Binary files /dev/null and b/assets/images/usermode-far-call.png differ diff --git a/assets/images/usermode-to-kernelmode-1.png b/assets/images/usermode-to-kernelmode-1.png new file mode 100644 index 00000000..42a647cc Binary files /dev/null and b/assets/images/usermode-to-kernelmode-1.png differ diff --git a/assets/images/usermode-to-kernelmode-2.png b/assets/images/usermode-to-kernelmode-2.png new file mode 100644 index 00000000..f518f303 Binary files /dev/null and b/assets/images/usermode-to-kernelmode-2.png differ diff --git a/assets/images/usermode-to-kernelmode-3.png b/assets/images/usermode-to-kernelmode-3.png new file mode 100644 index 00000000..047b3e6f Binary files /dev/null and b/assets/images/usermode-to-kernelmode-3.png differ diff --git a/assets/images/usermode-to-kernelmode-4.png b/assets/images/usermode-to-kernelmode-4.png new file mode 100644 index 00000000..1e581cdd Binary files /dev/null and b/assets/images/usermode-to-kernelmode-4.png differ diff --git a/assets/images/usermode-to-kernelmode-5.png b/assets/images/usermode-to-kernelmode-5.png new file mode 100644 index 00000000..da34b498 Binary files /dev/null and b/assets/images/usermode-to-kernelmode-5.png differ diff --git a/assets/images/virtualization-technology-vanderpool.png b/assets/images/virtualization-technology-vanderpool.png new file mode 100644 index 00000000..b31c48f0 Binary files /dev/null and b/assets/images/virtualization-technology-vanderpool.png differ diff --git a/assets/images/vitis-hls-diagram.png b/assets/images/vitis-hls-diagram.png new file mode 100644 index 00000000..b6c0e07b Binary files /dev/null and b/assets/images/vitis-hls-diagram.png differ diff --git a/assets/images/vitis-hls.jpg b/assets/images/vitis-hls.jpg new file mode 100644 index 00000000..88ea57c6 Binary files /dev/null and b/assets/images/vitis-hls.jpg differ diff --git a/assets/images/vivado-sim.jpg b/assets/images/vivado-sim.jpg new file mode 100644 index 00000000..40287d4d Binary files /dev/null and b/assets/images/vivado-sim.jpg differ diff --git a/assets/images/vivado-toggle-code.jpg b/assets/images/vivado-toggle-code.jpg new file mode 100644 index 00000000..b9c99090 Binary files /dev/null and b/assets/images/vivado-toggle-code.jpg differ diff --git a/assets/images/vivado-toggle-io-planning.jpg b/assets/images/vivado-toggle-io-planning.jpg new file mode 100644 index 00000000..45a57a90 Binary files /dev/null and b/assets/images/vivado-toggle-io-planning.jpg differ diff --git a/assets/images/vivado-toggle-schematic.jpg b/assets/images/vivado-toggle-schematic.jpg new file mode 100644 index 00000000..3ba2d0af Binary files /dev/null and b/assets/images/vivado-toggle-schematic.jpg differ diff --git a/assets/images/vm-detection-example-1.png b/assets/images/vm-detection-example-1.png new file mode 100644 index 00000000..39944f99 Binary files /dev/null and b/assets/images/vm-detection-example-1.png differ diff --git a/assets/images/vm-detection-example-3.png b/assets/images/vm-detection-example-3.png new file mode 100644 index 00000000..6dd2b257 Binary files /dev/null and b/assets/images/vm-detection-example-3.png differ diff --git a/assets/images/vm-detection-example-4.png b/assets/images/vm-detection-example-4.png new file mode 100644 index 00000000..8bf01be5 Binary files /dev/null and b/assets/images/vm-detection-example-4.png differ diff --git a/assets/images/vm-entry-controls-fields.png b/assets/images/vm-entry-controls-fields.png new file mode 100644 index 00000000..e683ac96 Binary files /dev/null and b/assets/images/vm-entry-controls-fields.png differ diff --git a/assets/images/vm-entry-interruption-information-fields.png b/assets/images/vm-entry-interruption-information-fields.png new file mode 100644 index 00000000..9bd15270 Binary files /dev/null and b/assets/images/vm-entry-interruption-information-fields.png differ diff --git a/assets/images/vm-error.png b/assets/images/vm-error.png new file mode 100644 index 00000000..b380e9de Binary files /dev/null and b/assets/images/vm-error.png differ diff --git a/assets/images/vm-exit-controls-fields.png b/assets/images/vm-exit-controls-fields.png new file mode 100644 index 00000000..b2865431 Binary files /dev/null and b/assets/images/vm-exit-controls-fields.png differ diff --git a/assets/images/vm-exit-interruption-information.png b/assets/images/vm-exit-interruption-information.png new file mode 100644 index 00000000..6954c974 Binary files /dev/null and b/assets/images/vm-exit-interruption-information.png differ diff --git a/assets/images/vm-properties-1.png b/assets/images/vm-properties-1.png new file mode 100644 index 00000000..4b46bb4d Binary files /dev/null and b/assets/images/vm-properties-1.png differ diff --git a/assets/images/vm-properties-2.png b/assets/images/vm-properties-2.png new file mode 100644 index 00000000..d5ade1a6 Binary files /dev/null and b/assets/images/vm-properties-2.png differ diff --git a/assets/images/vmm-life-cycle.png b/assets/images/vmm-life-cycle.png new file mode 100644 index 00000000..10c16055 Binary files /dev/null and b/assets/images/vmm-life-cycle.png differ diff --git a/assets/images/vmx-detection.png b/assets/images/vmx-detection.png new file mode 100644 index 00000000..0a6860db Binary files /dev/null and b/assets/images/vmx-detection.png differ diff --git a/assets/images/vmx-lifecycle.png b/assets/images/vmx-lifecycle.png new file mode 100644 index 00000000..808438cf Binary files /dev/null and b/assets/images/vmx-lifecycle.png differ diff --git a/assets/images/windbg-handle.png b/assets/images/windbg-handle.png new file mode 100644 index 00000000..a21785b9 Binary files /dev/null and b/assets/images/windbg-handle.png differ diff --git a/assets/images/windbg2ida.png b/assets/images/windbg2ida.png new file mode 100644 index 00000000..d5609507 Binary files /dev/null and b/assets/images/windbg2ida.png differ diff --git a/assets/images/winobj-devices.png b/assets/images/winobj-devices.png new file mode 100644 index 00000000..6fcd21ea Binary files /dev/null and b/assets/images/winobj-devices.png differ diff --git a/assets/images/zero-thread.png b/assets/images/zero-thread.png new file mode 100644 index 00000000..6997c524 Binary files /dev/null and b/assets/images/zero-thread.png differ diff --git a/assets/img/favicons/android-chrome-192x192.png b/assets/img/favicons/android-chrome-192x192.png new file mode 100644 index 00000000..4b488bc7 Binary files /dev/null and b/assets/img/favicons/android-chrome-192x192.png differ diff --git a/assets/img/favicons/android-chrome-512x512.png b/assets/img/favicons/android-chrome-512x512.png new file mode 100644 index 00000000..0725776c Binary files /dev/null and b/assets/img/favicons/android-chrome-512x512.png differ diff --git a/assets/img/favicons/apple-touch-icon.png b/assets/img/favicons/apple-touch-icon.png new file mode 100644 index 00000000..d379a822 Binary files /dev/null and b/assets/img/favicons/apple-touch-icon.png differ diff --git a/assets/img/favicons/browserconfig.xml b/assets/img/favicons/browserconfig.xml new file mode 100644 index 00000000..54217f7c --- /dev/null +++ b/assets/img/favicons/browserconfig.xml @@ -0,0 +1 @@ + #da532c diff --git a/assets/img/favicons/favicon-16x16.png b/assets/img/favicons/favicon-16x16.png new file mode 100644 index 00000000..d45cd0b5 Binary files /dev/null and b/assets/img/favicons/favicon-16x16.png differ diff --git a/assets/img/favicons/favicon-32x32.png b/assets/img/favicons/favicon-32x32.png new file mode 100644 index 00000000..ce2ae3b7 Binary files /dev/null and b/assets/img/favicons/favicon-32x32.png differ diff --git a/assets/img/favicons/favicon.ico b/assets/img/favicons/favicon.ico new file mode 100644 index 00000000..d99ec373 Binary files /dev/null and b/assets/img/favicons/favicon.ico differ diff --git a/assets/img/favicons/mstile-150x150.png b/assets/img/favicons/mstile-150x150.png new file mode 100644 index 00000000..a4393381 Binary files /dev/null and b/assets/img/favicons/mstile-150x150.png differ diff --git a/assets/img/favicons/safari-pinned-tab.svg b/assets/img/favicons/safari-pinned-tab.svg new file mode 100644 index 00000000..3a666bec --- /dev/null +++ b/assets/img/favicons/safari-pinned-tab.svg @@ -0,0 +1,83 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + diff --git a/assets/img/favicons/site.webmanifest b/assets/img/favicons/site.webmanifest new file mode 100644 index 00000000..5322a1c1 --- /dev/null +++ b/assets/img/favicons/site.webmanifest @@ -0,0 +1 @@ +{ "name": "Rayanfam Blog", "short_name": "Rayanfam Blog", "description": "We write about Windows Internals, Hypervisors, Linux, and Networks.", "icons": [ { "src": "/assets/img/favicons/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/assets/img/favicons/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" }], "start_url": "/index.html", "theme_color": "#2a1e6b", "background_color": "#ffffff", "display": "fullscreen" } diff --git a/assets/index.html b/assets/index.html new file mode 100644 index 00000000..bf99e4d3 --- /dev/null +++ b/assets/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/assets/js/data/search.json b/assets/js/data/search.json new file mode 100644 index 00000000..c1c8e27a --- /dev/null +++ b/assets/js/data/search.json @@ -0,0 +1 @@ +[ { "title": "Building Silicon Dreams: An Adventure in Hardware Design", "url": "/topics/hardware-design-stack/", "categories": "hardware", "tags": "hardware-design, HDL-languages, chisel, hls, vivado, vitis, netlist, reversing-netlist, OpenLane, OpenRAM, GDSII, Block-RAM, Verilog, VHDL, ModelSim, GTKWave, Verilator", "date": "2023-10-07 00:00:00 +0000", "snippet": "Story TimeExploring the internals of processors has long been a fascination of mine. After spending a lot of time experiencing different processor features like hypervisor and configuring different x86 MSRs, I was seeking to find a way of knowing how exactly these concepts and configurations are made and how they work on the silicon.Initially, my knowledge of hardware design was limited to a basic understanding of VHDL and Verilog gained from a microarchitecture design class at university. However, I decided to investigate and go deeper into this field and acquire more knowledge, which I am now eager to share with you.This article is neither a deep dive into the hardware design nor does it assume any prior knowledge of hardware design. Instead, it provides an overview of fundamental hardware design concepts, introduces the tools and devices used in the process, and aims to give you a foundational understanding of the hardware design industry.My journey in hardware design started when I was looking to find the actual implementation of different processors. After that, I came around two major open-source RISC-V processors. Rocket Chip Generator SonicBOOM: The Berkeley Out-of-Order MachineThese projects are one of the best implementations of RISC-V architecture in which you can use the source code to modify the microarchitecture, add/modify/remove CPU instructions, and finally synthesize the generated Verilog codes into a Xilinx FPGA or if you’re a millionaire you can give your design to TSMC to fabricate it for you :)For those who don’t know about RISC-V, it’s an architecture (just like x86 and AMD64) that mainly comes with RISC architecture. There are two fundamental architectures in computer design: 1) CISC and 2) RISC.The primary distinction between Complex Instruction Set Computer (CISC) and Reduced Instruction Set Computer (RISC) architectures lies in their approach to instruction execution. CISC, exemplified by the x86 architecture, typically employs a large and varied instruction set, often with complex addressing modes, allowing a single instruction to perform multiple operations. In contrast, RISC architectures like ARM and RISC-V utilize a simplified instruction set with fixed-length instructions, focusing on streamlined, single-cycle instruction execution.Based on these explanations, I think we can start our journey from the designing steps of a chip and finally synthesizing into an FPGA our making GDSII (more about it later) layouts.Please bear in mind, as you read the rest of this article, it is written by someone with a primary background in OS security and low-level programming, rather than being an expert hardware designer. Furthermore, this article is not an in-depth exploration but rather a high-level overview that provides technical examples and explanations to elaborate on the concepts and distinctions. I would be happy to hear if you have any feedback, corrections, and discussion points. You can use the comments section below.Table of Contents Story Time Table of Contents Introduction Analog Signals/Protocols Digital Signals/Protocols Source Code Moore’s Law &amp; Amdahl’s Law Terms Hardware Description Languages (SystemVerilog, Verilog, VHDL) Generating Hardware Using Chisel C/C++ in Hardware Design Vitis HLS Simulating Codes Simulating HDL Code Using GTKWave or ModelSim Testing Codes in Chisel Testing HDL Codes Using Verilator Synthesising HDL Codes Programming Xilinx FPGAs Using Vivado Vivado Netlists The Memory Distributed RAMs in FPGAs Block RAM (BRAM) in FPGAs Static RAM (SRAM) in ASIC Elaborating ASIC Designs Building Hardware Layout (GDSII) Reverse Engineering Netlists Conclusion ReferencesIntroductionOver the past decades, the silicon industry changed drastically reaching from 10 µm (1973) to 3 nm (2022). Shrinking chips offer advantages including increased performance, higher integration density, reduced power consumption, improved heat dissipation along economic advancements. These benefits will drive the technological companies to contribute to smaller, more powerful, and energy-efficient chips. However, it also brings challenges related to manufacturing complexity and fundamental physical limits as transistors approach atomic scales. Wavelength (µm) 10 6 3 1.5 1 0.8 0.6 0.35 0.25 0.18 0.13 0.09 0.065 0.045 0.032 0.022 0.014 0.01 0.007 0.005 0.003 0.002 Year 1971 1974 1977 1981 1984 1987 1990 1993 1996 1999 2001 2003 2005 2007 2009 2012 2014 2016 2018 2020 2022 2024 However, there are technological limitations that prohibit the shrinking of the transistors (later will be discussed as Moore’s law).Now, let’s see the hardware design from a broader view. Have you ever asked yourself why computers are 0’s and 1’s? There are plenty of scientific and engineering theories and concepts that describe why computers are using 0’s and 1’s (and using boolean algebra), but a very simple explanation is that the current technology of semiconductors works best with zero and one (high and low voltage) signals rather than analog signals. It’s always more efficient to use analog devices as long as we’re not concerned with errors! If it was not because of errors, then probably the current devices that rely on deterministic and correct results are all made of analog devices. If I don’t want to oversimplify things, I can say, that while it’s true that some functions can be efficiently performed using analog signals (e.g., audio and video processing), digital computing offers several advantages, including accuracy, precision, ease of replication, and error correction.The same is true for digital/analog protocols. Usually, it’s more efficient to send data over analog signals as you can send data at higher bitrate and longer distances. But, why there are digital signals while analog is naturally better? Simple answer, because analog has errors and you cannot guarantee the data will be correct at both sender and receiver.Here is a list of digital and analog protocols.Analog Signals/Protocols: VGA (Video Graphics Array) RCA (Radio Corporation of America) XLR (External Line Return) 3.5mm Audio Jack Component Video Composite Video S-Video (Separate Video) HDMI (High-Definition Multimedia Interface) DVI (Digital Visual Interface) - Some variants support analog signals. VGA-to-HDMI Converters (Analog to Digital conversion)Digital Signals/Protocols: USB (Universal Serial Bus) [Usually] SPI (Serial Peripheral Interface) I2C (Inter-Integrated Circuit) UART (Universal Asynchronous Receiver-Transmitter) Ethernet (IEEE 802.3) HDMI (High-Definition Multimedia Interface) - Also supports digital audio. DisplayPort PCIe (Peripheral Component Interconnect Express) SATA (Serial ATA) CAN (Controller Area Network)Having the above knowledge, let’s explore each step more comprehensively. The rest of this article covers how digital chips are designed at different levels and discusses different tools that are used at each step.Source CodeThe full source code of the codes used in this blog post is available on GitHub :[https://github.com/SinaKarvandi/hardware-design-stack]Moore’s Law &amp; Amdahl’s LawThere are two important laws in computer architecture. One is Moore’s law and the other is Amdahl’s law.Moore’s law is defined as, an observation (prediction) that the number of transistors on a microchip roughly doubles every two years, whereas its cost is halved over that same timeframe.If you have studied computer science at the university, you probably saw that professors always use a slide about Moore’s Law, seeking to justify their lessons, and declare that Moore’s Law is no longer true!Thanks to ivs for mentioning that what they really mean is that the combination of Dennard scaling and Moore’s law isn’t true anymore. Moore’s law by itself is just fine.Amdahl’s law on the other hand is a formula that gives the theoretical speedup in latency of the execution of a task at a fixed workload that can be expected of a system whose resources are improved. It states that “the overall performance improvement gained by optimizing a single part of a system is limited by the fraction of time that the improved part is actually used”.But why that is important? The fact that there are physical limitations and barriers to further expanding/shrinking transistors (both because of the power consumption and speed), makes it important to design effective and efficient hardware that is capable of performing the computation at a higher speed with less transistor consumption. As a simple example, there are multiple types of adders to perform the addition of two operands, and each of them is designed for particular purposes and is good to be used in specific operations like Ripple Carry Adder (RCA), Carry Look-Ahead Adder (CLA), Carry Save Adder (CSA), etc.Okay, but how do the programmers describe these designs? The answer is, that they use HDL languages to describe the hardware at different levels.TermsBefore exploring more hardware design concepts, let’s explain a few terms that we use in this article.FPGA (Field-Programmable Gate Array): FPGAs are versatile hardware devices that can be customized by users after manufacturing, allowing them to reconfigure the chip’s functionality to suit specific tasks or applications.ASIC (Application-Specific Integrated Circuit): ASICs are specialized integrated circuits designed for a particular application, often offering optimized performance and power efficiency, but they are not reprogrammable like FPGAs.RTL (Register-Transfer Level): RTL is a hardware description language abstraction that represents the behavior of digital circuits at a level where data movement between registers and the associated logic operations are defined, making it a crucial step in designing hardware.Netlist: A netlist is a textual or graphical representation of a circuit’s components (gates, flip-flops, etc.) and their interconnections at a lower level than RTL but higher than physical implementation.Synthesis: Synthesis is the automated process of converting a high-level hardware description into a lower-level netlist for hardware implementation, optimizing for efficiency. A code that can make hardware is called a synthesizable code.AXI: The AXI (Advanced eXtensible Interface) protocol is a widely used high-performance, on-chip communication protocol designed for efficient data transfer and interconnection of IP (Intellectual Property) blocks in System-on-Chip (SoC) and FPGA designs.Hardware Description Languages (SystemVerilog, Verilog, VHDL)Almost all of the hardware designs (like CPUs, RAMs, GPUs, etc.) are (can be) programmed using HDL or Hardware Description Languages. The most amazing thing about HDL languages is that these languages allow you to write programs that do a simple task (AND, OR, XOR, etc.) in a very short and precise slice of time and you can achieve precise control over a multitude of tasks within a fraction of a second!For example, assume that you want to write a program that adds two operands. Generally in assembly, we use an instruction like ADD and add a value to a register like ADD RAX, 0x55. There are plenty of stages that a processor will follow from fetch and decoding to executing. But in the final step, the values will be added in a very short fraction of time using an adder in the arithmetic logic unit (ALU). Imagine you have a processor/FPGA with a 200 MHz clock cycle speed and each addition of an adder took one cycle to complete. Let’s look at a simple formula:Number of Clock Cycles = Clock Frequency (Hz) * Time (seconds)Number of Clock Cycles = 200,000,000 Hz * 1 second = 200,000,000 cyclesFrom the above example, you can see that you would able to perform 200,000,000 additions in one second and you’re sure that it took only one second to complete! You can gain control of this very small and precise fraction of time (1/200,000,000 second) by using HDL programming languages.There are three famous HDL programming languages: VHDL, Verilog, and SystemVerilog.Let’s see a very simple example of a VHDL code that toggles a port to zero and one by each clock.library IEEE;use IEEE.STD_LOGIC_1164.ALL;entity Switch_Control is Port ( clock : in STD_LOGIC; -- clock input switch : out STD_LOGIC := '0' -- Initialize switch to OFF (logic low) );end Switch_Control;architecture Behavioral of Switch_Control is signal toggle : STD_LOGIC := '0'; -- Signal for toggling the switchbegin process(clock) begin if rising_edge(clock) then toggle &lt;= not toggle; -- Toggle the switch state on rising edge of the clock end if; end process; switch &lt;= toggle; -- Assign the switch state to the output portend Behavioral;The above VHDL code defines an input port “clock” and an output port “switch” and inside the architecture block, there’s a process sensitive to the rising edge of the “clock” signal. When the clock signal rises, the “toggle” signal is toggled (inverted).The above code is also written in Verilog:module Switch_Control ( input wire clock, // Clock input output reg switch = 0 // Initialize switch to OFF (logic low));reg toggle = 0; // Signal for toggling the switchalways @(posedge clock) begin toggle &lt;= ~toggle; // Toggle the switch state on rising edge of the clockendalways @(toggle) begin switch = toggle; // Assign the switch state to the output portendendmoduleThis code defines a module named Switch_Control with an input wire clock and an output register switch which is initialized to 0 (logic low). Inside the module, there’s a register named toggle initialized to 0. Effectively, this Verilog module also creates a digital switch that toggles its state on each rising edge of the clock input, and the switch output reflects this state.The SystemVerilog’s syntax is also (more or less like Verilog) with a couple of differences. Actually, SystemVerilog extends Verilog by adding several new features and enhancements.As you can see the above codes have one input and one output. The above code can be fabricated into a chip. The imaginary chip contains two pins, one for input and one for output. These pins can be made with different IC package types like DIP, SIP, etc. with different technologies.The problem with these HDL languages is that it’s so hard and frustrating to develop hardware with these languages. Even though these languages give the best control over every single part of the hardware, it can be really time-consuming to design simple hardware. Because of these reasons, hardware designers made other languages like Chisel, SystemC, and HLS that deal with higher-levels of abstractions which makes it possible to write less code; thus, speeding up and easing the development.For now, we have a very basic understanding of HDL languages and how chips are digital chips are designed, now we can follow our journey to the next steps.Generating Hardware Using ChiselPersonally, I have to admit that the Chisel language is one of the most exciting languages that I’ve ever dealt with. I really love the way they engineered a functional programming language like Scala and made a hardware generator language. The Constructing Hardware in a Scala Embedded Language (Chisel) is an open-source hardware description language (HDL) used to describe digital electronics and circuits at the register-transfer level (RTL). Think of it as a language that creates Verilog (or SystemVerilog) codes from the Scala codes. Later you can use the generated Verilog files to synthesize and program FPGAs or fabricate them in ASIC designs.Chisel adds hardware construction primitives to the Scala programming language, providing designers with the power of a modern programming language to write complex, parameterizable circuit generators that produce synthesizable Verilog.There are plenty of resources to start learning Chisel and it’s a large-scale project that is actively maintained by CHIPS Alliance and it’s used for huge projects like Rocket Chip Generator.Let’s see a very simple example of a Chisel code:import chisel3._import chisel3.util.Counterimport circt.stage.ChiselStageclass Blinky(freq: Int, startOn: Boolean = false) extends Module { val io = IO(new Bundle { val led0 = Output(Bool()) }) // Blink LED every second using Chisel built-in util.Counter val led = RegInit(startOn.B) val (_, counterWrap) = Counter(true.B, freq / 2) when(counterWrap) { led := ~led } io.led0 := led}object Main extends App { // These lines generate the Verilog output println( ChiselStage.emitSystemVerilog( new Blinky(1000), firtoolOpts = Array("-disable-all-randomization", "-strip-debug-info") ) )}This Chisel code snippet is for a digital circuit that blinks an LED at a specified frequency. Defines a Chisel module named Blinky with a parameter for the frequency and an optional parameter for the initial LED state. Inside the module, it declares an output port led0 of type Bool to drive an LED. The LED state is stored in a register named led and initialized with the value provided in the startOn parameter. When the counter wraps around, it toggles the led state.In digital designs, the clock signal is the perception of time and it’s used for synchronizing different parts of the chips. As you can see in the above code, it’s a simple LED blinker and as you can see, there is an output signal named led0. But did you notice that it didn’t specify any inputs? How can we make a blinker without knowing or being notified about the time (or more precisely without any perception of the clock signal)?The answer to this question lies in the fact that in languages like Chisel, the clock signal is masked and handled by the Chisel and it’s not exported (revealed) to the hardware designer. In other words, the generated Verilog files from the above code actually contain the clock signal (and a reset signal). But it’s actually handled by the Chisel instead of the programmer. Let’s see the generated code from the above Chisel code.module Blinky( input clock, reset, output io_led0); reg led; reg [8:0] counterWrap_c_value; always @(posedge clock) begin if (reset) begin led &lt;= 1'h0; counterWrap_c_value &lt;= 9'h0; end else begin automatic logic counterWrap = counterWrap_c_value == 9'h1F3; led &lt;= counterWrap ^ led; if (counterWrap) counterWrap_c_value &lt;= 9'h0; else counterWrap_c_value &lt;= counterWrap_c_value + 9'h1; end end // always @(posedge) assign io_led0 = led;endmoduleAs you can see, there is actually a clock and a reset signal in addition to the io_led0 output. Using Chisel, you have less control over the hardware compared to writing codes in Verilog or VHDL, but still, you have the benefit of using a high-level language like Scala (Java). Moreover, you have the ability to integrate Verilog codes into the Chisel designs and write some parts of the codes in Chisel while using Verilog for remaining parts.C/C++ in Hardware DesignWhen we talk about C/C++ in hardware design, we have to consider two different paradigms. The first approach is using C/C++ codes to program FPGA. Modern FPGAs like the Xilinx ZYNQ series come with two different units, the processing system (PS), and the FPGA programmable logic (PL). The PS part is usually an ARM processor that is connected directly to the PL parts (e.g., through AXI interface). So, you can write specific C/C++ codes compile them for a regular ARM processor, and use it in the FPGA. Usually, it’s super hard if someone wants to implement all protocols (e.g., network protocols, etc.) using the PL part. In these cases, hardware programmers make PL designs that are mainly responsible for running the computational part of a program and finally send the results back to the ARM processor and this ARM processor is responsible for synchronizing or sending/receiving data over the network. You can think of a dozen of examples how this PS part might be useful once it’s connected to the PL part. If you’re interested in this topic, you can see this tutorial as it covers a lot of exciting applications of using PL and PS together. The second approach is using C/C++ codes to design hardware or generate RTL codes. As an example, we can use Vitis HLS to generate RTL codes. High-level synthesis (HLS) is an automated design method. It starts with a high-level description of how a digital system should behave and then automatically generates the detailed hardware structure that makes that behavior possible at the register-transfer level. Vitis HLSLet’s run a simple HLS example using Xilinx Vitis. Before that, here’s a top view of how HLS works:For example, we run a simple AXI lite connection interface using HLS (from the Vitis example codes).There are three main phases, C Synthesis, C/RTL Cosimulation, and Implementation. In the first phase, the Vitis synthesizer tries to interpret the C codes, after that, we can write codes for testing our design and finally, the RTL generated code (both in Verilog and VHDL format) is ready!Here are the generated/exported AXI lite codes by Vitis HLS, and this is VHDL files and Verilog files of our design.Simulating CodesOnce you finished writing your HDL codes or generating HDL codes from the Chisel (Scala) generator, it’s time to test the code using a simulator. The simulator works like a debugger and it’s an environment to simulate the hardware behavior to test whether the designed logic works properly or contains error. First, let’s simulate HDL codes.Simulating HDL Code Using GTKWave or ModelSimFor simulating and testing VHDL and Verilog codes, we need to write a testbench. A testbench in Verilog, or VHDL is a file that is used exclusively for simulation, it’s a piece of non-synthesizable code that tries to simulate signals (input/output) to the designed logic, so you can see how your design behaves on different signals and inputs and what would be the final output.For example, for the above toggle VHDL code, we write the following testbench:library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.STD_LOGIC_ARITH.ALL;use IEEE.STD_LOGIC_UNSIGNED.ALL;entity Switch_Control_tb isend Switch_Control_tb;architecture testbench of Switch_Control_tb is -- Signals for testbench signal clock_tb : STD_LOGIC := '0'; -- Testbench clock signal signal switch_tb : STD_LOGIC; -- Testbench switch signalbegin -- Instantiate the Switch_Control DUT DUT: entity work.Switch_Control port map ( clock =&gt; clock_tb, switch =&gt; switch_tb ); -- Clock generation process (generates a clock signal) process begin while true loop clock_tb &lt;= not clock_tb; -- Toggle the clock wait for 5 ns; -- Clock period (adjust as needed) end loop; end process;end testbench;This VHDL testbench creates a clock signal and connects it to the Switch_Control while monitoring the output signal switch_tb to observe the behavior of the chip during simulation.The Verilog version of the testbench is available here.Now it’s time to test our testbench.Probably the most famous tool for testing HDL codes is ModelSim. There are also other tools like internal Vivado simulator (previously named isim) or GTKWave but in my opinion, ModelSim is better as it offers more functionalities along with a powerful scripting capability. Moreover, you don’t have to necessarily install Vivado to use ModelSim. You can just install ModelSim and test (simulate) your code.The following picture is the simulation signal of the above testbench using ModelSim:And this is the simulation result of the Vivado’s internal simulator:Keep in mind that, what you will see in the simulator environment might not necessarily behave the same once it is fabricated as a chip or is programmed into an FPGA, so there are different pre- and post-synthesis simulations plus plenty of other physical and electrical considerations that might affect the behavior of the circuit, but it usually reflects the correct behavior of what you can expect from a circuit and you can find possible design errors. By the way, it’s a program that simulates the circuit, not a physical circuit!Testing Codes in ChiselChisel is based on Scala and as it’s a high-level programming language, it gives you unlimited power of testing and writing testbenches. For example, imagine you write a code that is designing an arithmetic unit for a processor. You can generate dozens of random numbers and statements, write some Scala (or even Java) codes to compute a formula or an expression, and then provide these random values to your designed circuit, and compare the output of your circuit with the results of the formula.import chisel3._import chisel3.util._import chiseltest._import org.scalatest.flatspec.AnyFlatSpecimport scala.util.control.Breaks._class ToggleTest extends AnyFlatSpec with ChiselScalatestTester { "DUT" should "pass" in { test( new Toggle( // // Arugments to parameterize the chisel designs // ) ) { dut =&gt; // First, set an input called "start" to 0 dut.io.start.poke(0.U) // Run the circuit for one-clock dut.clock.step(1) // Perform the first test val test1 = Integer.parseInt(TestingSample.SampleFormulaFunctions(1, 2).U // Set the start to 1 dut.io.start.poke(1.U) // Set an input called "in1" to the test1 value dut.io.in1.poke(test1) // Run the circuit for one-clock dut.clock.step(1) // Check the output from "out1" with a value like 0 if (dut.io.out1.peek().litValue.toInt == 0) { print("[*] Successful\\n"); } else { print("[x] NOT successful!\\n"); } // Remove the start bit again (disable chip) dut.io.start.poke(0.U) } }}This is a Chisel testbench to test a Chisel circuit with specific input conditions and check the output to determine if the circuit behaves as expected.Initially, the start input is set to 0 using poke. The circuit is advanced by one clock cycle using dut.clock.step(1). Then, a test value is calculated using a function named TestingSample.SampleFormulaFunctions(1, 2) and converted to an integer. The start input is set to 1 to enable the circuit. The in1 input is set to the previously calculated test value using poke. The circuit is advanced by one clock cycle again. Finally, the output from out1 is checked. If it matches the expected value (0), it prints [*] Successful, otherwise [x] NOT successful!.After that, you can run your test by using sbt, and it will make a .vcd file. The VCD file is passed as an input to the GTKWave and you can see different signals and what happens in each clock cycle and the final outputs. Of course, the results can be automatically tested with Scala but if you want to debug and find possible errors, you can see the signal waves.If you want to use ModelSim with Chisel, I made an automated Python script to make necessary conversions between different formats and make Chisel results compatible with ModelSim, you can find it here, it also describes how you can use it.Testing HDL Codes Using VerilatorUp until now, we have seen how to make HDL designs and we used Chisel to generate Verilog codes. We also see how we can test the HDL and Chisel codes. Now we encounter another problem and it’s speed. Testing complicated and big projects is time-consuming, we already wrote a very simple testbench but in reality, when we encounter a big project, it would take a lot of time to simulate a circuit. In order to solve this issue, hardware designers came across the idea of converting the simulation Verilog codes into C++ and compiling the simulation codes. Now, instead of interpreting codes and generating waves, everything was running directly in an executable binary file, you can assume it is like a Verilog code that is converted to C++ code but the generated C++ code is designed based on how integrated circuits work.The tool that is responsible for this conversion is Verilator and this conversion is called “verilate”. It verilates the specified Verilog or SystemVerilog code by reading it, performing lint checks, and optionally inserting assertion checks and coverage-analysis points. It outputs single- or multithreaded .cpp and .h files, the “Verilated” code.For the demonstration, I verilated the toggle code that we wrote previously, you can see the generated CPP files here.There is also a very good tutorial if you want to start learning verilator. You can also use Verilator directly with Chisel codes.Now that we test our designed logic, it’s time to either implement the codes in FPGAs or fabricate them into chips.Synthesising HDL CodesOnce we successfully pass the tests and simulate logic, it’s time to synthesize codes. There are two major FPGA vendors that design and fabricate FPGAs Xilinx (acquired by AMD) and Intel FPGAs (previously Altera). These two giant companies provide different tools for programming their FPGAs. Altera made Quartus while Xilinx offers Vivado. In this blog post, we use Vivado as the main tool.Usually, the designs that are made for FPGAs are different from the ASICs. For example, there are plenty of considerations that make a Verilog design FPGA-efficient or ASIC-efficient. Most of the time, those codes that are generated for FPGAs use FPGA components like Block RAM (more about it later) or use FPGA IPs that make the code only synthesizable for FPGAs. The same is also true about ASIC design. Some designs might not be suitable for FPGAs and need to be fabricated in ASIC chips.Programming Xilinx FPGAs Using VivadoWe can use the Vivado Design Suite to synthesize the Verilog code that we previously wrote and finally generate a bitstream file that can program an FPGA.Another essential step for Vivado is I/O pin planning and we can connect the clock source to our design ports or connect I/O pins (e.g., a key, LED, AXI, SPI pins, etc.). It finally produces a design constraints file (XDC file) that will guide Vivado through routing the FPGA pins.For example, this is an XDC file that is used for connecting clock signals as well as some VGA I/O ports to FPGA pins.set_property PACKAGE_PIN F22 [get_ports reset]set_property IOSTANDARD LVCMOS33 [get_ports sys_clock]set_property PACKAGE_PIN AB19 [get_ports {VGA_B[3]}]set_property PACKAGE_PIN AB20 [get_ports {VGA_B[2]}]set_property PACKAGE_PIN Y20 [get_ports {VGA_B[1]}]set_property PACKAGE_PIN Y21 [get_ports {VGA_B[0]}]set_property PACKAGE_PIN AA21 [get_ports {VGA_G[3]}]...Finally, it elaborates the code, makes schematic files, and generates bitstreams. Here is the schematic view generated for our toggle example.The schematic file of the above design is available here.Vivado NetlistsBefore making the bitstream file needed for programming FPGA, the codes are converted to a lower abstraction level called netlist which is basically specific to the target FPGA components and contains the necessary components along with their interconnection to implement the actual designed logic and satisfy the design requirements.For example, assume the very first examples (Verilog code) that we made for toggling switches. We could use the following TCL commands (Vivado command-line) to generate Verilog and VHDL functional simulation netlist:For generating Verilog netlists:write_verilog -mode funcsim Verilog_Netlist_Toggle.vFor generating VHDL netlists:write_vhdl -mode funcsim VHDL_Netlist_Toggle.vhdThe final netlist is a new Verilog file (netlist file):// Copyright 1986-2022 Xilinx, Inc. All Rights Reserved.// --------------------------------------------------------------------------------// Tool Version: Vivado v.2022.2 (win64) Build 3671981 Fri Oct 14 05:00:03 MDT 2022// Date : Thu Oct 5 21:13:49 2023// Host : DESKTOP-J3QO9T8 running 64-bit major release (build 9200)// Command : write_verilog -mode funcsim Verilog_Netlist_Name.v// Design : Switch_Control// Purpose : This verilog netlist is a functional simulation representation of the design and should not be modified// or synthesized. This netlist cannot be used for SDF annotated simulation.// Device : xc7z020clg484-1// --------------------------------------------------------------------------------`timescale 1 ps / 1 ps(* NotValidForBitStream *)module Switch_Control (clock, switch); input clock; output switch; wire clock; wire clock_IBUF; wire clock_IBUF_BUFG; wire p_0_in; wire switch; wire switch_OBUF; BUFG clock_IBUF_BUFG_inst (.I(clock_IBUF), .O(clock_IBUF_BUFG)); IBUF #( .CCIO_EN("TRUE")) clock_IBUF_inst (.I(clock), .O(clock_IBUF)); OBUF switch_OBUF_inst (.I(switch_OBUF), .O(switch)); LUT1 #( .INIT(2'h1)) toggle_i_1 (.I0(switch_OBUF), .O(p_0_in)); FDRE #( .INIT(1'b0)) toggle_reg (.C(clock_IBUF_BUFG), .CE(1'b1), .D(p_0_in), .Q(switch_OBUF), .R(1'b0));endmodule`ifndef GLBL`define GLBL`timescale 1 ps / 1 psmodule glbl (); parameter ROC_WIDTH = 100000; parameter TOC_WIDTH = 0; parameter GRES_WIDTH = 10000; parameter GRES_START = 10000;//-------- STARTUP Globals -------------- wire GSR; wire GTS; wire GWE; wire PRLD; wire GRESTORE; tri1 p_up_tmp; tri (weak1, strong0) PLL_LOCKG = p_up_tmp; wire PROGB_GLBL; wire CCLKO_GLBL; wire FCSBO_GLBL; wire [3:0] DO_GLBL; wire [3:0] DI_GLBL; reg GSR_int; reg GTS_int; reg PRLD_int; reg GRESTORE_int;//-------- JTAG Globals -------------- wire JTAG_TDO_GLBL; wire JTAG_TCK_GLBL; wire JTAG_TDI_GLBL; wire JTAG_TMS_GLBL; wire JTAG_TRST_GLBL; reg JTAG_CAPTURE_GLBL; reg JTAG_RESET_GLBL; reg JTAG_SHIFT_GLBL; reg JTAG_UPDATE_GLBL; reg JTAG_RUNTEST_GLBL; reg JTAG_SEL1_GLBL = 0; reg JTAG_SEL2_GLBL = 0 ; reg JTAG_SEL3_GLBL = 0; reg JTAG_SEL4_GLBL = 0; reg JTAG_USER_TDO1_GLBL = 1'bz; reg JTAG_USER_TDO2_GLBL = 1'bz; reg JTAG_USER_TDO3_GLBL = 1'bz; reg JTAG_USER_TDO4_GLBL = 1'bz; assign (strong1, weak0) GSR = GSR_int; assign (strong1, weak0) GTS = GTS_int; assign (weak1, weak0) PRLD = PRLD_int; assign (strong1, weak0) GRESTORE = GRESTORE_int; initial begin GSR_int = 1'b1; PRLD_int = 1'b1; #(ROC_WIDTH) GSR_int = 1'b0; PRLD_int = 1'b0; end initial begin GTS_int = 1'b1; #(TOC_WIDTH) GTS_int = 1'b0; end initial begin GRESTORE_int = 1'b0; #(GRES_START); GRESTORE_int = 1'b1; #(GRES_WIDTH); GRESTORE_int = 1'b0; endendmodule`endifThe generated netlist in VHDL format is also available here. As you can see, these generated HDL netlists are elaborated in different abstraction levels and it’s quite challenging to understand the intent of this code. Even though they’re basically the same as the codes that we wrote for toggling output and do the exact same operation, but this is the eventual code that will be programmed in the FPGA.Fortunately, there are tools that can be used for reverse engineering netlists (more about it later).The MemoryOne of the fundamental components of chips is Static Random Access Memory (SRAM). You probably saw that Intel processors with more cache are offered at higher prices. The reason for that is that they need to chain the different numbers of flip-flops to form the cache. These chains of flip-flops are SRAMs and can be accessed immediately without any waiting clocks.Static RAM (SRAM) consists of flip-flops, a bistable circuit composed of four to six transistors. Once a flip-flop stores a bit, it keeps that value until the opposite value is stored in it. SRAM gives fast access to data (immediate), but it is physically relatively large. Even though SRAMs are perfect for designing memory, these components are relatively expensive. That’s why other types of RAMs (like DRAMs) are used in computers.Now let’s talk about different types of memories used in FPGAs and ASIC design.Distributed RAMs in FPGAsIn FPGAs, there are Block RAMs (BRAMs) and distributed RAMs. Distributed RAM is made up of LUTs and is used when the size of memory is small. For instance, in your code, you have a memory with a depth of 64 and a word size of 32. This one will be inferred into Distributed RAM, not BRAM.Let’s define a memory in VHDL:type ram_type is array (0 to 3) of STD_LOGIC_VECTOR(7 downto 0);signal ram: ram_type := (others =&gt; "00000000");The above code defines an array type with four elements, indexed from 0 to 3. Each element of this array is a STD_LOGIC_VECTOR type, which represents an array of bits. In this case, each element is an 8-bit wide STD_LOGIC_VECTOR (ranging from bit 7 to bit 0), effectively creating a 4x8-bit memory structure. The FPGA will infer it as a distributed RAM, not Block RAM (BRAM) because the size is relatively small.Block RAM (BRAM) in FPGAsBlock RAM (or BRAM) stands for Block Random Access Memory and is used for storing large amounts of data inside of your FPGA. BRAMs are usually external or embedded chips in the FPGA. For example, the following chip is the external design (port map) of a 36-KB BRAM used in Xilinx FPGAs.The delay for these BRAM components are 1-clock cycle means that if you want to access data bits in BRAMs, you have to request from the BRAM chip and it will be available at the rising edge of the next clock cycle.Usually, FPGAs have different formats of codes for inferring Block RAMs. For example, the following Verilog code is used for inferring BRAMs in Xilinx FPGAs where you can initialize it with particular data bits specified in the file.module rams_init_file (clk, we, addr, din, dout);input clk;input we;input [5:0] addr;input [31:0] din;output [31:0] dout;reg [31:0] ram [0:63];reg [31:0] dout;initial begin $readmemb("rams_init_file.data",ram);endalways @(posedge clk) begin if (we) ram[addr] &lt;= din; dout &lt;= ram[addr];end endmoduleThis Verilog module, rams_init_file, represents a simple RAM with a read initialization from a memory file. It responds to clock edges and writes to enable signals to update and read data from its memory array. Once you use the above code in Vivado, it automatically infers Block RAM (BRAM).You can even initialize it with custom values.library ieee;use ieee.std_logic_1164.all;use ieee.numeric_std.all;entity rams_sp_rom isport( clk : in std_logic; we : in std_logic; addr : in std_logic_vector(5 downto 0); di : in std_logic_vector(19 downto 0); do : out std_logic_vector(19 downto 0));end rams_sp_rom;architecture syn of rams_sp_rom is type ram_type is array (63 downto 0) of std_logic_vector(19 downto 0); signal RAM : ram_type := (X"0200A", X"00300", X"08101", X"04000", X"08601", X"0233A", X"00300", X"08602", X"02310", X"0203B", X"08300", X"04002", X"08201", X"00500", X"04001", X"02500", X"00340", X"00241", X"04002", X"08300", X"08201", X"00500", X"08101", X"00602", X"04003", X"0241E", X"00301", X"00102", X"02122", X"02021", X"00301", X"00102", X"02222", X"04001", X"00342", X"0232B", X"00900", X"00302", X"00102", X"04002", X"00900", X"08201", X"02023", X"00303", X"02433", X"00301", X"04004", X"00301", X"00102", X"02137", X"02036", X"00301", X"00102", X"02237", X"04004", X"00304", X"04040", X"02500", X"02500", X"02500", X"0030D", X"02341", X"08201", X"0400D"); begin process(clk) begin if rising_edge(clk) then if we = '1' then RAM(to_integer(unsigned(addr))) &lt;= di; end if; do &lt;= RAM(to_integer(unsigned(addr))); end if; end process;end syn;This Block RAM (BRAM) is a Verilog module called rams_sp_rom representing a simple synchronous read-only memory (ROM) with a 64-entry memory array. It has a clock input clk, write enable input we, address input addr, data input di, and data output do. On each rising clock edge, it either writes data to the ROM if we is high or reads data from the ROM based on the provided address, updating the do output accordingly. The memory content is initialized with preset values in the RAM signal. Same as above, Vivado infers a BRAM for the above code.As you can see, you can write HDL codes to READ/WRITE into the Block RAM memory. You can even create Read-Only Memory (ROM) by removing the modification (write) logic. Keep in mind that the logic of the Block RAM itself is also written with HDL codes, so it’s like using another HDL module with this difference that the Block RAMs are previously fabricated and physically available into the chips.Static RAM (SRAM) in ASICThere is no Block RAM (BRAM) in ASIC designs. If you want to implement a block RAM (BRAM) into an ASIC chip, you have to implement it or use custom tools for creating and simulating RAMs. One of these tools is OpenRAM. OpenRAM is an open-source framework to create the layout, netlists, timing and power models, placement and routing models, and other views necessary to use SRAMs in ASIC design.You can configure the OpenRAM and it will create different outputs from Verilog files to GDS files that can be used for fabricating SRAMs. Finally, it gives you the necessary files that are suitable for your custom technology (45 nm, 40 nm, etc.).Elaborating ASIC DesignsOnce the implementation of hardware (usually in HDL languages) is done, it’s time to use special tools to implement them into actual transistors. This task can be done with (usually commercial, expensive, and complicated) tools like Synopsys Design Compiler. These tools try to optimize designs for timing, area, power, and test and finally generate designs that can be fabricated and implemented into chips.Other than the commercial tools, there are open-source equivalent tools like Yosys that are able to synthesize and design Verilog codes.Building Hardware Layout (GDSII)Once you’ve done designing your hardware, it’s time to make it ready for fabrication. This time, the GDSII format is used to create a layout available to be used and fabricated in the industry. GDSII is an industry-standard database file format that is used to exchange data on IC layout artwork. It is a binary file format representing planar geometric shapes, text labels, and other information about the layout in hierarchical form.Once you created your final Verilog files, you can use different tools to create a standard GDSII format. As an example, the open-source OpenROAD-Project’s OpenLane can be used that perform all ASIC implementation steps from RTL all the way down to GDSII.As an example, I used the Blinky Verilog code that we generated by Chisel as an input for OpenLange and this is the picture of a chip that I made by using this project:If you want to zoom into the picture, the SVG version of this file is available here.This was the last step and we explored our journey from HDL codes to creating hardware using GDSII. Of course, there are plenty of other steps and industry considerations at this point that should be done before making the actual chip, but I think you’ve got a very high-level idea of how chips are made.Before wrapping up, let’s explore one more step, reverse engineering of netlists!Reverse Engineering NetlistsIf you remember from the previous sections where we explored the netlists of Vivado, you probably noticed that the netlists are mostly hard to understand. Fortunately, there are tools to help us with analyzing netlists and one of them is called HAL (or The Hardware Analyzer). As they mentioned in the git repo, their vision is that HAL becomes the hardware reverse-engineering equivalent of tools like IDA or Ghidra.From the main descriptions: virtually all available research on netlist analysis operates on a graph-based representation of the netlist under inspection. At its core, HAL provides exactly that: A framework to parse netlists of arbitrary sources, e.g., FPGAs or ASICs, into a graph-based netlist representation and to provide the necessary built-in tools for traversal and analysis of the included gates and nets.We can use HAL to analyze the netlist codes generated by Vivado from the previous step. First, we need to build HAL and then import the netlist file. As you can see, it visualizes the netlist view of the chips along with their input and output ports and shows the arithmetic logic behind each element (flip-flops, LUTs, Boolean Function) in the netlist. We could also use Python codes to further elaborate the netlist.One other interesting feature of HAL is its Netlist Simulator and Waveform Viewer. This tool is also able to create automated waves for the target netlist and enables the reverse engineer to simulate selected parts of the netlist in a cycle-accurate fashion.Now, what if we don’t have access to the netlist files? Or if the netlist is too complex to be interpreted? Can we physically perform the reverse engineering tasks? The answer to this question is yes!Sometimes even getting your hands on a netlist file is a hard task. In such cases, prior to reverse engineering the circuit’s netlist, an adversary will be needed to reproduce the internal netlist of the target IC. This has been a great challenge in terms of IP/IC cloning and piracy. Once the attacker gets his hand on an optical microscope, he can prepare depackaging the IC using corrosive chemicals, then delayering individual layers (metal, poly, or diffusion) using corrosive chemicals. Finally Imaging the top-view of each layer using an optical microscope or single electron microscope (SEM). The image may contain metal routing, contacts, vias, and pins in that layer.Furthermore, annotation aligns and stitches the images of different layers could be carried out to extract gate-level netlist from the annotated images.To mitigate such reverse engineering there have been a number of methods including gate camouflages, dummy contacts, netlist obfuscation, logic locking and so on. Some methods are shown below:Nevertheless, similar invasive methods like this and this could be used to get information from the hardware imaging and eventually complete reverse engineering.ConclusionThis was a very basic introduction to the digital and hardware design world and most of the introduced tools are either open-source or publicly available, however, you should keep in mind that most of the big computer maker companies probably have their own approaches to generating hardware along with their proprietary tools, so it might not reflect how the actual hardware is made in a big tech company but sure it will give you an idea of the capabilities of the hardware design and how these hardware devices can be made.In wrapping up our exploration of hardware design, we’ve taken a journey into the world of silicon. From the incredible advancements driven by Moore’s Law to the high-level and low-level description of hardware chips using different languages like VHDL, Verilog, and Chisel, testing hardware descriptions using tools like ModelSim, Verilator, and making design layouts ready to be fabricated using OpenLane or programming FPGA’s using Vivado. Whether you’re a seasoned expert or a newcomer to the field, hardware design continues to be an awesome adventure, where innovation and curiosity work hand in hand to shape the future of computing.I hope you enjoyed this blog post and I will try to make more content about chip design in the future. Make sure to use the comment section below to discuss this article and see you at the next blog posts!References[1] Jensen Huang Tells the Media That Moore’s Law is Dead (The featured image) - (https://www.techpowerup.com/299159/jensen-huang-tells-the-media-that-moores-law-is-dead)[2] 3 nm process - (https://en.wikipedia.org/wiki/3_nm_process)[3] Moore’s law - (https://en.wikipedia.org/wiki/Moore%27s_law)[4] Amdahl’s law - (https://en.wikipedia.org/wiki/Amdahl%27s_law)[5] Comparison among Different Adders - (https://www.iosrjournals.org/iosr-jvlsi/papers/vol5-issue6/Version-1/A05610106.pdf)[6] Difference between Verilog and SystemVerilog - (https://www.geeksforgeeks.org/difference-between-verilog-and-systemverilog/)[7] Verilog Example Codes - (https://verificationguide.com/verilog-examples/)[8] “Light, Thin, Short and Small”, The Development of Semiconductor Packages - (https://news.skhynix.com/light-thin-short-and-small-the-development-of-semiconductor-packages/)[9] IC packages - (https://colegiogradual.com.br/pt/plastic-leaded-chip-carrier-plcc-pllc-dummy-component-rr-z1Qd8ysk)[10] OpenROAD-Project’s OpenLane - (https://github.com/The-OpenROAD-Project/OpenLane)[11] HAL – The Hardware Analyzer - (https://github.com/emsec/hal)[12] An open-source static random access memory (SRAM) compiler - (https://github.com/VLSIDA/OpenRAM/)[13] flip-flop - (https://www.britannica.com/technology/flip-flop)[14] What is a Block RAM (BRAM) in an FPGA? Tutorial for beginners - (https://nandland.com/lesson-15-what-is-a-block-ram-bram/)[15] Block RAM - (https://docs.xilinx.com/r/en-US/am007-versal-memory/Data-Flow)[16] Initializing Block RAM From an External Data File (VHDL) - (https://docs.xilinx.com/r/en-US/ug901-vivado-synthesis/Initializing-Block-RAM-From-an-External-Data-File-Verilog)[17] OpenRAM results - (https://github.com/VLSIDA/OpenRAM/blob/stable/docs/source/results.md)[18] Chisel: A Modern Hardware Design Language - (https://github.com/chipsalliance/chisel)[19] 6.11. Incorporating Verilog Blocks - (https://chipyard.readthedocs.io/en/stable/Customization/Incorporating-Verilog-Blocks.html)[20] Design Compiler - (https://www.synopsys.com/implementation-and-signoff/rtl-synthesis-test/dc-ultra.html)[21] Generating a Netlist - (https://docs.xilinx.com/r/en-US/ug900-vivado-logic-simulation/Working-with-Simulation-Sets)[22] ModelSim-Intel FPGAs Standard Edition Software Version 18.1 - (https://www.intel.com/content/www/us/en/software-kit/750368/modelsim-intel-fpgas-standard-edition-software-version-18-1.html)[23] Netlist Simulator and Waveform Viewer - (https://github.com/emsec/hal/wiki/Netlist-Simulator-and-Waveform-Viewer)[24] Writing Test Benches - (https://alchitry.com/writing-test-benches-verilog)[25] chiseltest - (https://github.com/ucb-bar/chiseltest/blob/main/README.md)[26] Chisel3 Testing Cheat Sheet - (https://inst.eecs.berkeley.edu/~cs250/sp17/handouts/chisel-testercheatsheet.pdf)[27] Chisel Testing Intensive (Beginner Track) - Chisel Community Conference 2018 - (https://www.youtube.com/watch?v=yYosPQ9RI4I&amp;ab_channel=Chisel)[28] Verilator, the fastest Verilog/SystemVerilog simulator - (https://www.veripool.org/verilator/)[29] High-level synthesis - (https://en.wikipedia.org/wiki/High-level_synthesis)[30] Vitis HLS - (https://www.xilinx.com/products/design-tools/vitis/vitis-hls.html)[31] Krachenfels, Thilo, et al. “Automatic Extraction of Secrets from the Transistor Jungle using {Laser-Assisted}{Side-Channel} Attacks.” 30th USENIX security symposium (USENIX security 21). 2021.[32] Puschner, Endres, et al. “Red Team vs. Blue Team: A Real-World Hardware Trojan Detection Case Study Across Four Modern CMOS Technology Generations.” 2023 IEEE Symposium on Security and Privacy (SP). IEEE, 2023.[33] Rajendran, J., Sam, M., Sinanoglu, O. and Karri, R., 2013, November. Security analysis of integrated circuit camouflaging. In Proceedings of the 2013 ACM SIGSAC conference on Computer &amp; communications security (pp. 709-720)[34] Rajendran, J., Pino, Y., Sinanoglu, O., &amp; Karri, R. (2012, June). Security analysis of logic obfuscation. In Proceedings of the 49th annual design automation conference (pp. 83-89)[35] Han, Zhaokun, Muhammad Yasin, and Jeyavijayan JV Rajendran. “Does logic locking work with {EDA} tools?.” 30th USENIX Security Symposium (USENIX Security 21). 2021." }, { "title": "HyperDbg’s One Thousand and One Nights", "url": "/topics/hyperdbg-one-thousand-and-one-nights/", "categories": "debugger, debugging, hyperdbg, hypervisor", "tags": "hyperdbg, windbg, hyperdbg-vs-windbg, debugger, hyperdbg-principles, reversing-using-hyperdbg, hypervisor, using-hyperdbg-debugger", "date": "2022-06-13 00:00:00 +0000", "snippet": "This post is a different one, in that, it is more of an overview, rather than a technical post. Here, we provide a high-level summary of HyperDbg Debugger, its principles, and perspective.IntroductionHyperDbg is an open-source, hypervisor-assisted debugger that can be used to debug both user-mode and kernel-mode applications. The closest similar product available to HyperDbg is WinDbg.HyperDbg provides unique abilities, enabled by exploitation of the latest features of modern processors, to assist you in your reversing journey.The design principles employed in HyperDbg make for an OS-independent debugger with a unique architecture, offering exclusive, brand-new features.A Bit of HistoryAlmost three years ago (precisely, on 18 December 2019), we finished implementing EPT hooks and VPIDs, thus setting the cornerstones for designing a new debugger. Ever since, many of our friends have joined the project to develop an advanced, fully-practical, and usable debugger.During our Windows analysis journey, we always felt the lack of two elements that we thought would most likely make our tasks much faster and more efficient, and that was our main motive, to tackle the following setbacks: None of the current classic debuggers, (such as WinDbg) have the ability to trace read/write/executes to a large structure. Sure they have to be able to use hadware debug registers, but that’s limited. They can’t specify more than four addresses, and the size is also limited to 1, 2, and 4 Bytes. There was no support for tracing instructions from user mode to kernel mode and from kernel mode to user mode. A feature like this would allow us to trace parameters to the system calls and find the exact routines (basic blocks) executed due to our produced parameters in static analysis tools like IDA Pro, Ghidra, and Radare2.The solution to the first issue was implemented in HyperDbg and exported as a command named “!monitor”, and the secondly discussed issue was addressed with the Instrumentation Step-in or the “i”.After that, dozens of features were added to the debugger to get HyperDbg to where it is today.MotivationLet’s talk about the motivations behind HyperDbg.Classic Debuggers are Still Stuck At at their 90’s features.Debuggers are one of the essential tools used in computer sciences for a variety of purposes.Although there have been some upgrades and improvements over the course of years to the available debugging toolset, such as the Time Travel Debugging (TTD) feature that was introduced by Microsoft into their main debugger, WinDbg, people are essentially still debugging the same way they used to do back in the ’90s, with the same simple elements such as step-in and step-over. This puts a fundamental setback in the way of software programmers, reversers, and security researchers.We believe debugging, analyzing, and software profiling are inseparable elements that should all be coherently integrated into debuggers to make an enhanced experience of software development and bug finding.The intention was not to reinvent the wheel.Our motive was not to reintroduce yet another debugger with the same set of already available features, but it was fueled by the vision of how better a debugging experience can get, and the gap between that vision and the status quo. More specifically, we recognized the powerhouse, that is, the modern hardware features of newer generation processors, and their immense capacity for utilization towards providing an enriched software analysis experience, and the lack of meaningful and practical support for such features in commodity debuggers. And it was this state of mind that gave rise to the emergence of HyperDbg. We see where HyperDbg is today, only as a starting point for future milestones and improvements to provide even easier and more convenient debugging experiences.Kernel DebuggerThe central part of the HyperDbg debugger is its kernel-mode debugger called kHyperDbg.It has been almost two decades since kernel debuggers (such as WinDbg and GDB) have had any significant changes. We wanted to change that and advance the debugging experience to new horizons. Furthermore, HyperDbg delivers these improvements on rudimentary levels, compared to commonly used debuggers, in that, it enormously expands the range of privileges available to the user by shifting the debugging process from kernel-level (ring 0) to hypervisor-level (ring -1), a much-needed enhancement in our opinion, especially when it comes to kernel-debugging.As kernel level debuggers are prohibited the access to manipulate the operating system’s structures to make facilities for the debugging, HyperDbg uses an entirely separate layer to monitor and change these structures without interfering with the operating system, the hypervisor level, that resides below the kernel of the operating system in the hardware privilege rings. This makes HyperDbg blazingly fast and a highly powerful tool in terms of the flexibility, privileges, and the transparency it can provide for debugging and analysis. Owing to this fundamental transition, HyperDbg is able to deliver state-of-the-art features that make it particularly more convenient to analyze complex modern binaries that run on kernel mode of operating systems and are crucial to the security and reliability of the system. Furthermore, the highly efficient and low-level implementation, coupled with the potent script engine, allows for some tremendously powerful abilities, such as changing the flow of the applications and even the operating system using simple scripts.A few of its unique featuresIn this section, we will try and summarize some of the unique features of HyperDbg.Tremendously FasterAs described earlier, HyperDbg is incredibly fast, thanks to its low-level and efficient implementation. This brings forward new opportunities for many innovative debugging scenarios. For example, let’s imagine you want to analyze every system-calls, or get a log from a function called at a very high rate. HyperDbg allows you to do all of them with ease and high performance.Better TransparencyOne of the future goals of HyperDbg is to keep enhancing the stealth and transparency of its transparent mode. Of course, it is not possible to achieve 100% transparency, but we keep trying to make it more challenging for the anti-debugging methods to detect HyperDbg.Exporting Processor Events In DebuggerHyperDbg tends to export all system events of interest as debugger events. So many events happen in the CPU at all times. Fortunately, the majority of them are accessible via hypervisors. In HyperDbg, we export events into HyperDbg event format. Each HyperDbg event then can be used as a trigger for executing a desired action, such as breaking the debugger, executing custom assembly codes, or running a custom script engine. This standard pipeline will apply to all the current events and possible future events.HyperDbg vs. WinDbgAs one of the closest counterparts of HyperDbg, in this section, we draw a comparison between WinDbg and HyperDbg in a detailed manner.How different is HyperDbg from WinDbg?HyperDbg has an entirely different and standalone architecture. WinDbg operates on ring 0 (kernel) while HyperDbg is running on ring -1 (hypervisor); thus, HyperDbg is capable of providing unique features that are not available on WinDbg (OS-Level).Additionally, HyperDbg is not just a simple debugger. It comes with modern reverse engineering methods, powered by vt-x and other similar capabilities of modern processors to facilitate reverse engineering, analyzing, and fuzzing.WinDbg is built for development HyperDbg is built for reversing. We are not the same!Microsoft made WinDbg to build Windows and perform driver development tasks. Contrarily, HyperDbg is mainly geared towards use in reverse engineering, where one has no idea about their target debuggee. Of course, WinDbg is better at debugging drivers with the source code. However, if you wanted to understand a mechanism in which you have no access to the source code or if symbols are only partially available, then HyperDbg would give you way more features to explore your debuggee.HyperDbg is not a classic debugger.One of the main differentiating factors between HyperDbg and commodity debuggers stems from HyperDbg’s deliberation on the use of sophisticated methods and techniques by modern binaries to obfuscate their internal structures.For instance, most malware uses various anti-debugging techniques to avoid showing its malicious behavior when a debugger is around. On the other hand, classic debuggers are not suitable for analyzing the internal mechanisms that are buried deep into the complicated modules of the operating systems.HyperDbg is a hypervisor-based debugger.HyperDbg uses Intel VT-x (Ring -1) technology, while WinDbg is a kernel-based (Ring 0) debugger. Thus, HyperDbg is more privileged in terms of hardware terminology.Being a hypervisor-based debugger brings new magical features to life.Dozens of wonderful features already exist in HyperDbg that no other debuggers have. These features drastically enhance your reverse engineering journey, and we continue to add more and more features to further improve your debugging experience.HyperDbg is more transparent by nature.Generally, basing the debugger on the hypervisor layer makes HyperDbg more transparent than WinDbg. HyperDbg doesn’t use any debugging-related API, so even the operating system doesn’t have any idea that it’s being debugged! And that’s not all! HyperDbg can also hide from microarchitectural timing attacks that reveal the presence of the hypervisors. Transparency is a priority and is under active development.HyperDbg is open-source, WinDbg is not.HyperDbg is a community-driven debugger, and everyone can contribute to the project. In contrast, WinDbg is not open-source, although the source codes of some older versions have been leaked several times by now.WinDbg works in almost any architecture; HyperDbg is not.WinDbg works on multiple architectures, but so far, HyperDbg only works on x64-based systems.You can use WinDbg to debug many architectures like ARM, ARM64 (AARCH64), and AMD64 (x86_64), while HyperDbg currently only works on Intel x64-bit processors. However, it is still possible to debug x86 applications running in a system with an x64-bit processor.HyperDbg is faster, tremendously faster.HyperDbg is shipped with a vmx-root mode compatible script engine. Every check is performed on the kernel side (vmx-root mode), and in contrast to WinDbg, nothing is passed to the debugger. This makes for a substantially faster debugging process. Based on our evaluations as part of an academic publication on HyperDbg, WinDbg was able to check 6,941 conditions, while HyperDbg could check 23,214,792 in the same time period, making HyperDbg about ~3300 folds faster than WinDbg in this benchmark.For more information about the differences, please take a look at the following tweet.Here are 11 reasons why we should use #HyperDbg, the differences between HyperDbg and #WinDbg, and how HyperDbg will change our debugging/reversing journey.A thread (24 tweets) 🧵:&mdash; HyperDbg Debugger (@HyperDbg) June 4, 2022HyperDbg’s LogoHyperDbg’s logo is the Schrödinger’s cat, which is both dead and alive. It serves as a reminder that analyzing and reversing is not always a deterministic route for getting the correct answer. One will try, and one might succeed or fail, and that’s the nature of the digital world, which boils down to one of the two fundamental states: ones and zeros.ContributionHyperDbg is a large-scale project that requires a lot of time and effort from the community. Given the current number of developers and their limited time and resources, we cannot develop every part simultaneously. Therefore, new developers are warmly welcomed to join and contribute to the project. Please open discussions to discuss the HyperDbg and possible future assistance.The future worksIn the future, we want to add UEFI support to start HyperDbg before Windows. Another significant contribution would be adding Intel Processor Trace (PT) in an event and action style to the debugger and finally joining and integrating many cool projects to the HyperDbg to bring a unique debugging experience, like no one has seen before.More importantly, HyperDbg is (for the most part) operating system-independent. We want to port HyperDbg to other operating systems like Linux and OS X.ConclusionReaching the goal of a transparent, fast, and innovative debugger is not possible without the help of the community of the developers. We believe those who use HyperDbg are professional computer programmers/reversers, and almost all of them can help in this project. So, what are you waiting for? Go ahead and add your own contributions to the project!" }, { "title": "Hypervisor From Scratch – Part 8: How To Do Magic With Hypervisor!", "url": "/topics/hypervisor-from-scratch-part-8/", "categories": "cpu, hypervisor, tutorials", "tags": "hypervisor, event-injection, hidden-hook, high-irql-messaging, hyper-v-compatible, hypervisor-on-hyper-v, hypervisor-part-8, inject-interrupt, invalidate-ept, invept, invpcid, invvpid, syscall-hook, system-call-hook, vmfunc, vpid-in-hypervisor, wpp-tracing", "date": "2020-03-24 00:00:00 +0000", "snippet": "If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous practical examples on how to utilize hypervisors for reverse engineering.IntroductionHi guys,Welcome to the 8th part of the Hypervisor From Scratch. If you reach here, then you probably finished reading the 7th part, and personally, I believe the 7th part was the most challenging part to understand so hats off, you did a great job.The 8th part would be an exciting part as we’ll see lots of real-world and practical examples of solving reverse-engineering related problems with hypervisors. For example, we’ll see how hidden hooks work in the presence of hypervisor or how to create a syscall hook, and we’re eventually able to transfer messages from vmx root to OS (vmx non-root) and then into user-mode thus it gives us a valuable bunch of information about how the system works.Besides some OS-related concepts, we’ll also see some CPU related topics like VPIDs and some general information about how patches for Meltdown and Spectre works.Event injection, Exception Bitmap, and also adding support to virtualize a hyper-v machine are other titles that will be discussed.Before starting, I should give special thanks to my friend Petr Benes for his contributions to Hypervisor From Scratch, of course, Hypervisor From Scratch could never have existed without his help and to Liran Alon for great helps on fixing VPID problem, and to Gerhart for his in-depth knowledge about Hyper-V internals that makes Hypervisor From Scratch available for Hyper-V.OverviewThis part is divided into eight main sections : How to inject interrupts (Event) into the guest and Exception Bitmap Implementing hidden hooks using EPT Syscall hook Invalidating EPT caches using VPID Demonstrating a custom VMX Root-mode compatible message tracing mechanism and adding WPP Tracing to our Hypervisor We’ll add support to Hyper-V Fixing some previous design caveats Discussion (In this section we discuss the different question and approaches about various topics in this part)The full source code of this tutorial is available on GitHub :[https://github.com/SinaKarvandi/Hypervisor-From-Scratch]Table of Contents Introduction Overview Table of Contents Event Injection Vectored Events Interrupts Exceptions Exception Classifications Event Injection Fields Vectored Event Injection Exception Error Codes Exception Bitmap Monitor Trap Flag (MTF) Hidden Hooks (Simulating Hardware Debug Registers Without Any Limitation) Hidden Hooks Scenarios for Read/Write and Execute Implementing Hidden Hooks Removing Hooks From Pages An Important Note When Modifying EPT Entries System-Call Hook Finding Kernel Base Finding SSDT and Shadow SSDT Tables Get Routine Address by Syscall Number Virtual Processor ID (VPID) &amp; TLB INVVPID - Invalidate Translations Based on VPID Individual-address invalidation Single-context invalidation All-contexts invalidation Single-context invalidation, retaining global translations Important Notes For Using VPIDs INVVPID vs. INVPCID Designing A VMX Root-mode Compatible Message Tracing Concepts What’s a spinlock? Test-and-Set What do we mean by “Safe”? What is DPC? Challenges Designing A Spinlock Message Tracer Design Initialization Phase Sending Phase (Saving Buffer and adding them to pools) Reading Phase (Read buffers and send them to user-mode) Checking for new messages Sending messages to pools Receiving buffers and messages in user-mode IOCTL and managing user-mode requests User-mode notify callback Uninitialization Phase WPP Tracing Supporting to Hyper-V Enable Nested Virtualization Hyper-V’s visible behavior in nested virtualization Hyper-V Hypervisor Top-Level Functional Specification (TLFS) Out of Range MSRs Hyper-V Hypercalls (VMCALLs) Hyper-V Interface CPUID Leaves Fixing Previous Design Issues Fixing the problem with pre-allocated buffers Avoid Intercepting Accesses to CR3 Restoring IDTR, GDTR, GS Base and FS Base Let’s Test it! View WPP Tracing Messages How to test? Event Injection &amp; Exception Bitmap Demo Hidden Hooks Demo Read/Write Hooks or Hardware Debug Registers Simulation Hidden Execution Hook Syscall Hook Demo Discussion Conclusion ReferencesEvent InjectionOne of the essential parts of the hypervisors is the ability to inject events (events are Interrupts, Exceptions, NMIs, and SMIs) as if they’ve arrived normally, and the capability to monitor received interrupts and exceptions.This gives us a great ability to manage the guest operating system and unique ability to build applications, For example, if you are developing anti-cheat application, you can easily disable breakpoint and trap interrupts, and it completely disables all the features of Windbg or any other debugger as you’re the first one that is being notified about the breakpoint thus you can decide to abort the breakpoint or give it to the debugger.This is just a simple example that the attacker needs to find a way around it. You can also use event injections for reverse-engineering purposes, e.g., directly inject a breakpoint into an application that uses different anti-debugging techniques to make its code hidden.We can also implement some important features of our hypervisor like hidden hooks based on relying on event injection.Before going deep into the Event Injection, we need to know some basic processor concepts and terms used by Intel. Most of them derived from this post and this answer.Intel x86 defines two overlapping categories, vectored events (interrupts vs exceptions), and exception classes (faults vs traps vs aborts).Vectored EventsVectored Events (interrupts and exceptions) cause the processor to jump into an interrupt handler after saving much of the processor’s state (enough such that execution can continue from that point later).Exceptions and interrupts have an ID, called a vector, that determines which interrupt handler the processor jumps to. Interrupt handlers are described within the Interrupt Descriptor Table (IDT).InterruptsInterrupts occur at random times during the execution of a program, in response to signals from the hardware. System hardware uses interrupts to handle events external to the processor, such as requests to service peripheral devices. The software can also generate interrupts by executing the INT n instruction.ExceptionsExceptions occur when the processor detects an error condition while executing an instruction, such as division by zero. The processor identifies a variety of error conditions, including protection violations, page faults, and internal machine faults.Exception ClassificationsExceptions classified as faults, traps, or aborts depending on the way they reported and whether the instruction that caused the exception could be restarted without loss of program or task continuity.In summary: traps increment the instruction pointer (RIP), faults do not, and aborts ’explode’.We’ll start with the fault classification. You’ve probably heard of things called page faults (or segmentation faults if you’re from the past).A fault is just an exception type that can be corrected and allows the processor the ability to execute some fault handler to rectify an offending operation without terminating the entire operation. When a fault occurs, the system state is reverted to an earlier state before the faulting operation occurred, and the fault handler is called. After executing the fault handler, the processor returns to the faulting instruction to execute it again. That last sentence is important because that means it redoes an instruction execution to make sure the proper results are used in the following operations. This is different from how a trap is handled.A trap is an exception that is delivered immediately following the execution of a trapping instruction. In our hypervisor, we trap on various instructions, meaning that after the execution of an instruction – say rdtsc or rdtscp – a trap exception is reported to the processor. Once a trap exception is reported, control is passed to a trap handler, which will perform some operation(s). Following the execution of the trap handler, the processor returns to the instruction following the trapping instruction.An abort, however, is an exception that occurs and doesn’t always yield the location of the error. Aborts are commonly used for reporting hardware errors, or otherwise. You won’t see these very often, and if you do… Well, you’re doing something wrong. It’s important to know that all exceptions are reported on an instruction boundary – excluding aborts. An instruction boundary is quite simple: if you have the bytes 0F 31 48 C1 E2 20 which translates to the instructions,rdtscshl rdx, 20hThen the instruction boundary would be between the bytes 31 and 48. That’s because 0F 31 is the instruction opcodes for rdtsc. This way, two instructions separated by a boundary.Event Injection FieldsEvent injection is done with using interruption-information field of VMCS.The interruption-information is written into the VM-entry fields of the VMCS during VM-entry; after all the guest context has been loaded, including MSRs and Registers, it delivers the exception through the Interrupt Descriptor Table (IDT) using the vector specified in this field. The first field to configure event injection is VM-entry interruption-information field (32 bits) or VM_ENTRY_INTR_INFO in the VMCS, this field provides details about the event to be injected.The following picture shows the detail of each bit. The vector (bits 7:0) determines which entry in the IDT is used or which other event is injected or, in other words, it defines the index of Interrupt to be injected in IDT, for example, the following command (!idt) in windbg shows the IDT indexes. (note that the index is the numbers at the left).lkd&gt; !idtDumping IDT: fffff8012c05b00000: fffff80126551100 nt!KiDivideErrorFaultShadow01: fffff80126551180 nt!KiDebugTrapOrFaultShadow Stack = 0xFFFFF8012C05F9D002: fffff80126551200 nt!KiNmiInterruptShadow Stack = 0xFFFFF8012C05F7D003: fffff80126551280 nt!KiBreakpointTrapShadow04: fffff80126551300 nt!KiOverflowTrapShadow05: fffff80126551380 nt!KiBoundFaultShadow06: fffff80126551400 nt!KiInvalidOpcodeFaultShadow07: fffff80126551480 nt!KiNpxNotAvailableFaultShadow08: fffff80126551500 nt!KiDoubleFaultAbortShadow Stack = 0xFFFFF8012C05F3D009: fffff80126551580 nt!KiNpxSegmentOverrunAbortShadow0a: fffff80126551600 nt!KiInvalidTssFaultShadow0b: fffff80126551680 nt!KiSegmentNotPresentFaultShadow0c: fffff80126551700 nt!KiStackFaultShadow0d: fffff80126551780 nt!KiGeneralProtectionFaultShadow0e: fffff80126551800 nt!KiPageFaultShadow10: fffff80126551880 nt!KiFloatingErrorFaultShadow11: fffff80126551900 nt!KiAlignmentFaultShadow12: fffff80126551980 nt!KiMcheckAbortShadow Stack = 0xFFFFF8012C05F5D013: fffff80126551a80 nt!KiXmmExceptionShadow14: fffff80126551b00 nt!KiVirtualizationExceptionShadow15: fffff80126551b80 nt!KiControlProtectionFaultShadow1f: fffff80126551c00 nt!KiApcInterruptShadow20: fffff80126551c80 nt!KiSwInterruptShadow29: fffff80126551d00 nt!KiRaiseSecurityCheckFailureShadow2c: fffff80126551d80 nt!KiRaiseAssertionShadow2d: fffff80126551e00 nt!KiDebugServiceTrapShadow2f: fffff80126551f00 nt!KiDpcInterruptShadow30: fffff80126551f80 nt!KiHvInterruptShadow31: fffff80126552000 nt!KiVmbusInterrupt0Shadow32: fffff80126552080 nt!KiVmbusInterrupt1Shadow33: fffff80126552100 nt!KiVmbusInterrupt2Shadow34: fffff80126552180 nt!KiVmbusInterrupt3Shadow...The interruption type (bits 10:8) determines details of how the injection is performed.In general, a VMM should use the type hardware exception for all exceptions other than the following: Breakpoint exceptions (#BP): a VMM should use the type software exception. Overflow exceptions (#OF): a VMM should use the use type software exception. Those debug exceptions (#DB) that are generated by INT1 (a VMM should use the use type privileged software exception).For exceptions, the deliver-error-code bit (bit 11) determines whether delivery pushes an error code onthe guest stack. (we’ll talk about error-code later)The last bit is that VM entry injects an event if and only if the valid bit (bit 31) is 1. The valid bit in this field is cleared on every VM exit means that when you want to inject an event, you set this bit to inject your interrupt and the processor will automatically clear it at the next VM-Exit.The second field that controls the event injection is VM-entry exception error code.VM-entry exception error code (32 bits) or VM_ENTRY_EXCEPTION_ERROR_CODE in the VMCS: This field is used if and only if the valid bit (bit 31) and the deliver error-code bit (bit 11) are both set in the VM-entry interruption-information field.The third field that controls the event injection is VM-entry instruction length.VM-entry instruction length (32 bits) or VM_ENTRY_INSTRUCTION_LEN in the VMCS: For injection of events whose type is a software interrupt, software exception, or privileged software exception, this field is used to determine the value of RIP that is pushed on the stack.All in all, these things in VMCS control the Event Injection process: VM_ENTRY_INTR_INFO, VM_ENTRY_EXCEPTION_ERROR_CODE, VM_ENTRY_INSTRUCTION_LEN.Vectored Event InjectionIf the valid bit in the VM-entry interruption-information field is 1, VM entry causes an event to be delivered (or made pending) after all components of the guest state have been loaded (including MSRs) and after the VM-execution control fields have been established.The interruption type (which is described above) can be one of the following values.enum _INTERRUPT_TYPE{ INTERRUPT_TYPE_EXTERNAL_INTERRUPT = 0, INTERRUPT_TYPE_RESERVED = 1, INTERRUPT_TYPE_NMI = 2, INTERRUPT_TYPE_HARDWARE_EXCEPTION = 3, INTERRUPT_TYPE_SOFTWARE_INTERRUPT = 4, INTERRUPT_TYPE_PRIVILEGED_SOFTWARE_INTERRUPT = 5, INTERRUPT_TYPE_SOFTWARE_EXCEPTION = 6, INTERRUPT_TYPE_OTHER_EVENT = 7};Now it’s time to set the vector bit. The following enum is the representation of the indexes in IDT. (Look at the indexes of !idt command above).typedef enum _EXCEPTION_VECTORS{ EXCEPTION_VECTOR_DIVIDE_ERROR, EXCEPTION_VECTOR_DEBUG_BREAKPOINT, EXCEPTION_VECTOR_NMI, EXCEPTION_VECTOR_BREAKPOINT, EXCEPTION_VECTOR_OVERFLOW, EXCEPTION_VECTOR_BOUND_RANGE_EXCEEDED, EXCEPTION_VECTOR_UNDEFINED_OPCODE, EXCEPTION_VECTOR_NO_MATH_COPROCESSOR, EXCEPTION_VECTOR_DOUBLE_FAULT, EXCEPTION_VECTOR_RESERVED0, EXCEPTION_VECTOR_INVALID_TASK_SEGMENT_SELECTOR, EXCEPTION_VECTOR_SEGMENT_NOT_PRESENT, EXCEPTION_VECTOR_STACK_SEGMENT_FAULT, EXCEPTION_VECTOR_GENERAL_PROTECTION_FAULT, EXCEPTION_VECTOR_PAGE_FAULT, EXCEPTION_VECTOR_RESERVED1, EXCEPTION_VECTOR_MATH_FAULT, EXCEPTION_VECTOR_ALIGNMENT_CHECK, EXCEPTION_VECTOR_MACHINE_CHECK, EXCEPTION_VECTOR_SIMD_FLOATING_POINT_NUMERIC_ERROR, EXCEPTION_VECTOR_VIRTUAL_EXCEPTION, EXCEPTION_VECTOR_RESERVED2, EXCEPTION_VECTOR_RESERVED3, EXCEPTION_VECTOR_RESERVED4, EXCEPTION_VECTOR_RESERVED5, EXCEPTION_VECTOR_RESERVED6, EXCEPTION_VECTOR_RESERVED7, EXCEPTION_VECTOR_RESERVED8, EXCEPTION_VECTOR_RESERVED9, EXCEPTION_VECTOR_RESERVED10, EXCEPTION_VECTOR_RESERVED11, EXCEPTION_VECTOR_RESERVED12};In general, the event is delivered as if it had been generated normally, and the event is delivered using the vector in that field to select a descriptor in the IDT. Since event injection occurs after loading IDTR (IDT Register) from the guest-state area, this is the guest IDT, or in other words, the event is delivered to GUEST_IDTR_BASE and GUEST_IDTR_LIMIT.Putting the above descriptions into the implementation, we have the following function :// Injects interruption to a guestVOID EventInjectInterruption(INTERRUPT_TYPE InterruptionType, EXCEPTION_VECTORS Vector, BOOLEAN DeliverErrorCode, ULONG32 ErrorCode){ INTERRUPT_INFO Inject = { 0 }; Inject.Valid = TRUE; Inject.InterruptType = InterruptionType; Inject.Vector = Vector; Inject.DeliverCode = DeliverErrorCode; __vmx_vmwrite(VM_ENTRY_INTR_INFO, Inject.Flags); if (DeliverErrorCode) { __vmx_vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, ErrorCode); }}As an example we want to inject a #BP (breakpoint) into the guest, we can use the following code :/* Inject #BP to the guest (Event Injection) */VOID EventInjectBreakpoint(){EventInjectInterruption(INTERRUPT_TYPE_SOFTWARE_EXCEPTION, EXCEPTION_VECTOR_BREAKPOINT, FALSE, 0);UINT32 ExitInstrLength;__vmx_vmread(VM_EXIT_INSTRUCTION_LEN, &amp;ExitInstrLength);__vmx_vmwrite(VM_ENTRY_INSTRUCTION_LEN, ExitInstrLength);}Or if we want to inject a #GP(0) or general protection fault with error code 0 then we use the following code:/* Inject #GP to the guest (Event Injection) */VOID EventInjectGeneralProtection(){ EventInjectInterruption(INTERRUPT_TYPE_HARDWARE_EXCEPTION, EXCEPTION_VECTOR_GENERAL_PROTECTION_FAULT, TRUE, 0); UINT32 ExitInstrLength; __vmx_vmread(VM_EXIT_INSTRUCTION_LEN, &amp;ExitInstrLength); __vmx_vmwrite(VM_ENTRY_INSTRUCTION_LEN, ExitInstrLength);}You can write functions for other types of interrupts and exceptions. The only thing that you should consider is the InterruptionType, which is always hardware exception except for #DP, #BP, #OF, which is discussed above.Exception Error CodesYou might notice that we used VM_ENTRY_EXCEPTION_ERROR_CODE in the VMCS and 11th bit of the interruption-information field, and for some exceptions, we disabled them while for some others we set them to a specific value, so what’s that error codes?Some exceptions will push a 32-bit “error code” on to the top of the stack, which provides additional information about the error. This value must be pulled from the stack before returning control back to the currently running program. (i.e., before calling IRET for returning from interrupt).The fact that the error code must be pulled from the stack makes event injection more complicated as we have to make sure whether the Windows tries to pull error code from the stack or not, as it turns to error if we put something onto the stack that Windows doesn’t expect to pull it later or we didn’t push anything but Windows thoughts there is something in the stack that needs to be pulled.The following table shows some of these exceptions with the presence or absence of Error code, this table is derived from Intel SDM, Volume 1, CHAPTER 6 (Table 6-1. Exceptions and Interrupts). Name Vector nr. Type Mnemonic Error code? Divide-by-zero Error 0 (0x0) Fault #DE No Debug 1 (0x1) Fault/Trap #DB No Non-maskable Interrupt 2 (0x2) Interrupt - No Breakpoint 3 (0x3) Trap #BP No Overflow 4 (0x4) Trap #OF No Bound Range Exceeded 5 (0x5) Fault #BR No Invalid Opcode 6 (0x6) Fault #UD No Device Not Available 7 (0x7) Fault #NM No Double Fault 8 (0x8) Abort #DF Yes (Zero) Coprocessor Segment Overrun 9 (0x9) Fault - No Invalid TSS 10 (0xA) Fault #TS Yes Segment Not Present 11 (0xB) Fault #NP Yes Stack-Segment Fault 12 (0xC) Fault #SS Yes General Protection Fault 13 (0xD) Fault #GP Yes Page Fault 14 (0xE) Fault #PF Yes Reserved 15 (0xF) - - No x87 Floating-Point Exception 16 (0x10) Fault #MF No Alignment Check 17 (0x11) Fault #AC Yes Machine Check 18 (0x12) Abort #MC No SIMD Floating-Point Exception 19 (0x13) Fault #XM/#XF No Virtualization Exception 20 (0x14) Fault #VE No Reserved 21-29 (0x15-0x1D) - - No Security Exception 30 (0x1E) - #SX Yes Reserved 31 (0x1F) - - No Triple Fault - - - No FPU Error Interrupt IRQ 13 Interrupt #FERR No Now that we learn how to create a new events, it’s time to see how to monitor system interrupts.Exception BitmapIf you remember from MSR Bitmaps, we have a mask for each MSR that shows whether the read or write on that MSR should cause a vm-exit or not.The monitoring of exceptions uses the same method, which means that a simple mask governs it. This mask is EXCEPTION_BITMAP in VMCS.The exception bitmap is a 32-bit field that contains one bit for each exception. When an exception occurs, its vector is used to select a bit in this field. If the bit is 1, the exception causes a VM exit. If the bit is 0, the exception is delivered normally through the IDT.Now it’s up to you to decide whether you want to inject that exception back to the guest or change the state or whatever you want to do.For example, if you set the 3rd bit of the EXCEPTION_BITMAP, then whenever a breakpoint occurs somewhere (both user-mode and kernel-mode), a vm-exit with EXIT_REASON_EXCEPTION_NMI (exit reason == 0) occurs. // Set exception bitmap to hook division by zero (bit 1 of EXCEPTION_BITMAP) __vmx_vmwrite(EXCEPTION_BITMAP, 0x8); // breakpoint 3nd bitNow we can change the state of the program, then resume the guest, remember resuming the guest doesn’t cause the exception to be delivered to the guest, we have to inject an event manually if we want that the guest process the event normally. For example, we can use the function “EventInjectBreakpoint,” as mentioned earlier, to inject the exception back to the guest.The last question is how we can find the index of exception that occurred, you know we might set exception bitmap for multiple exceptions, so we have to know the exact reason why this vm-exit happened or more clearly, what exception causes this vm-exit.The following VMCS fields report us about the event, VM_EXIT_INTR_INFO VM_EXIT_INTR_ERROR_CODEThe following table shows how we can use VM_EXIT_INTR_INFO.Which is the following structure:typedef union _VMEXIT_INTERRUPT_INFO { struct { UINT32 Vector : 8; UINT32 InterruptionType : 3; UINT32 ErrorCodeValid : 1; UINT32 NmiUnblocking : 1; UINT32 Reserved : 18; UINT32 Valid : 1; }; UINT32 Flags;}VMEXIT_INTERRUPT_INFO, * PVMEXIT_INTERRUPT_INFO;And we can read the details using vmread instruction, for example, the following command shows how we can detect if breakpoint (0xcc) occurred. // read the exit reason __vmx_vmread(VM_EXIT_INTR_INFO, &amp;InterruptExit); if (InterruptExit.InterruptionType == INTERRUPT_TYPE_SOFTWARE_EXCEPTION &amp;&amp; InterruptExit.Vector == EXCEPTION_VECTOR_BREAKPOINT) { // Do whatever , e.g re-inject the breakpoint }If we want to re-inject an exception that comes with an error code (see the above table), then the error code can be read using VM_EXIT_INTR_ERROR_CODE in VMCS. After that, write the error code to VM_ENTRY_EXCEPTION_ERROR_CODE and enable the deliver-error-code of VM_ENTRY_INTR_INFO to make sure that re-injection is without any flaw.Also, keep in mind that page-fault is treated differently you can read Intel SDM for more information.But wait! Have you notice that exception bitmap are just a 32-bit field in VMCS while we have up to 256 interrupts in IDT ?!If you’re curious about this question you can read its answer in Discussion section.Monitor Trap Flag (MTF)Monitor Trap Flag or MTF is a feature that works exactly like Trap Flag in r/eflags except it’s invisible to the guest.Whenever you set this flag on CPU_BASED_VM_EXEC_CONTROL, after VMRESUME, the processor executes one instruction then a vm-exit occurs.We have to clear this flag otherwise each instruction cause a vm-exit.The following function is responsible for setting and unsetting MTF./* Set the monitor trap flag */VOID HvSetMonitorTrapFlag(BOOLEAN Set){ ULONG CpuBasedVmExecControls = 0; // Read the previous flag __vmx_vmread(CPU_BASED_VM_EXEC_CONTROL, &amp;CpuBasedVmExecControls); if (Set) { CpuBasedVmExecControls |= CPU_BASED_MONITOR_TRAP_FLAG; } else { CpuBasedVmExecControls &amp;= ~CPU_BASED_MONITOR_TRAP_FLAG; } // Set the new value __vmx_vmwrite(CPU_BASED_VM_EXEC_CONTROL, CpuBasedVmExecControls);}Setting MTF leads to a vm-exit with exit reason (EXIT_REASON_MONITOR_TRAP_FLAG), we unset the MTF in the vm-exit handler.MTF is essential in implementing hidden hooks, more details about MtfEptHookRestorePoint later in the hidden hooks section.Here’s the MTF vm-exit handler. case EXIT_REASON_MONITOR_TRAP_FLAG: { /* Monitor Trap Flag */ if (GuestState[CurrentProcessorIndex].MtfEptHookRestorePoint) { // Restore the previous state EptHandleMonitorTrapFlag(GuestState[CurrentProcessorIndex].MtfEptHookRestorePoint); // Set it to NULL GuestState[CurrentProcessorIndex].MtfEptHookRestorePoint = NULL; } else { LogError("Why MTF occured ?!"); } // Redo the instruction GuestState[CurrentProcessorIndex].IncrementRip = FALSE; // We don't need MTF anymore HvSetMonitorTrapFlag(FALSE); break; }Hidden Hooks(Simulating Hardware Debug Registers Without Any Limitation)Have you ever used hardware debugger registers ?!The debug registers allow researchers and programmers to selectively enable various debug conditions (read, write, execute) associated with a set of four debug addresses without any change in program instructions.As you know, we can set up to 4 locations to these hardware registers, and it’s the worst limitation for these registers.so what if we have a structure (let say _EPROCESS) and we want to see what function in Windows Read or Write in this structure?It’s not possible with current debug registers but we use EPT to rescue !Hidden Hooks Scenarios for Read/Write and ExecuteWe have two strategies for hidden hooks, one for Read/Write and one for Execute.For Read/Write,we unset read or write or both (based on how user wants) in the entry corresponding to the address.This means before read or write a vm-exit occurs, and an EPT Violation will notify us. In the EPT Violation handler, we log the address that tries to read or write, then we find the entry in EPT table and set both read and write (means that any read or write to the page is allowed) and also set an MTF flag.VMM resumes, and one instruction executes, or in other words, read or write is performed, then an MTF vm-exit occurs. In MTF vm-exit handler, we unset the read and write access again so any future access to that page will cause an EPT Violation.Note that all of the above scenarios happen to one core. Each core has a separate TLB and separate Monitor Trap Flag.For Execute,For execution, we use a capability in Intel processors called execute-only.Execute-only means that we can have a page with execute access enabled while read and write access is disabled.If the user wants an execution hook, then we find the entry in EPT Table and unset read and write access and set the execute access. Then we create a copy from the original page (Page A) to somewhere else (Page B) and modify the copied page (Page B) with an absolute jump to the hook function.Now, each time that any instruction attempted to execute our function, the absolute jump is performed, and our hook function is called. Each time any instruction tries to read or write to that location, an EPT Violation occurs as we unset read and write access to that page, so we can swap the original page (Page A) and also set the monitor trap flag to restore the hook after executing one instruction.Wasn’t it easy ? Review it one more time if you didn’t understand.You can also think about the different methods; for example, DdiMon creates a copy from that page and modifies the hook location by replacing one bytes (0xcc) breakpoint there. Now it intercepts each breakpoint (using Exception Bitmap) and swaps the original page. This method is much simpler to implement and more reliable, but it causes vm-exit for each hook, so it’s slower, but the first method for EPT Hooks never causes a vm-exit for execution.Vm-exits for Read and Write hooks are unavoidable.The execution hook for this part is derived from Gbps hv.Let’s dig into implementation.Implementing Hidden HooksFor hooking functions, first, we split the page into 4KB entries, as described in the previous part. Then find the entry and read that entry. We want to save the details of a hooked page so we can use it later. For read/write hooks, we unset read or write or both, while for executing hooks, we unset read/write access and set execute access and also copy the page contents into a new page and swap the entry’s physical address with the second page’s physical address (fake page’s physical address).Then we build a trampoline (explained later) and finally decide how to invalidate the TLB based on vmx-state (vmx-root or vmx non-root) and finally add the hook details to the HookedPagesList./* This function returns false in VMX Non-Root Mode if the VM is already initialized This function have to be called through a VMCALL in VMX Root Mode */BOOLEAN EptPerformPageHook(PVOID TargetAddress, PVOID HookFunction, PVOID* OrigFunction, BOOLEAN UnsetRead, BOOLEAN UnsetWrite, BOOLEAN UnsetExecute) { EPT_PML1_ENTRY ChangedEntry; INVEPT_DESCRIPTOR Descriptor; SIZE_T PhysicalAddress; PVOID VirtualTarget; PVOID TargetBuffer; PEPT_PML1_ENTRY TargetPage; PEPT_HOOKED_PAGE_DETAIL HookedPage; ULONG LogicalCoreIndex; // Check whether we are in VMX Root Mode or Not LogicalCoreIndex = KeGetCurrentProcessorIndex(); if (GuestState[LogicalCoreIndex].IsOnVmxRootMode &amp;&amp; !GuestState[LogicalCoreIndex].HasLaunched) { return FALSE; } /* Translate the page from a physical address to virtual so we can read its memory. * This function will return NULL if the physical address was not already mapped in * virtual memory. */ VirtualTarget = PAGE_ALIGN(TargetAddress); PhysicalAddress = (SIZE_T)VirtualAddressToPhysicalAddress(VirtualTarget); if (!PhysicalAddress) { LogError("Target address could not be mapped to physical memory"); return FALSE; } // Set target buffer, request buffer from pool manager , we also need to allocate new page to replace the current page ASAP TargetBuffer = PoolManagerRequestPool(SPLIT_2MB_PAGING_TO_4KB_PAGE, TRUE, sizeof(VMM_EPT_DYNAMIC_SPLIT)); if (!TargetBuffer) { LogError("There is no pre-allocated buffer available"); return FALSE; } if (!EptSplitLargePage(EptState-&gt;EptPageTable, TargetBuffer, PhysicalAddress, LogicalCoreIndex)) { LogError("Could not split page for the address : 0x%llx", PhysicalAddress); return FALSE; } // Pointer to the page entry in the page table. TargetPage = EptGetPml1Entry(EptState-&gt;EptPageTable, PhysicalAddress); // Ensure the target is valid. if (!TargetPage) { LogError("Failed to get PML1 entry of the target address"); return FALSE; } // Save the original permissions of the page ChangedEntry = *TargetPage; /* Execution is treated differently */ if (UnsetRead) ChangedEntry.ReadAccess = 0; else ChangedEntry.ReadAccess = 1; if (UnsetWrite) ChangedEntry.WriteAccess = 0; else ChangedEntry.WriteAccess = 1; /* Save the detail of hooked page to keep track of it */ HookedPage = PoolManagerRequestPool(TRACKING_HOOKED_PAGES, TRUE, sizeof(EPT_HOOKED_PAGE_DETAIL)); if (!HookedPage) { LogError("There is no pre-allocated pool for saving hooked page details"); return FALSE; } // Save the virtual address HookedPage-&gt;VirtualAddress = TargetAddress; // Save the physical address HookedPage-&gt;PhysicalBaseAddress = PhysicalAddress; // Fake page content physical address HookedPage-&gt;PhysicalBaseAddressOfFakePageContents = (SIZE_T)VirtualAddressToPhysicalAddress(&amp;HookedPage-&gt;FakePageContents[0]) / PAGE_SIZE; // Save the entry address HookedPage-&gt;EntryAddress = TargetPage; // Save the orginal entry HookedPage-&gt;OriginalEntry = *TargetPage; // If it's Execution hook then we have to set extra fields if (UnsetExecute) { // Show that entry has hidden hooks for execution HookedPage-&gt;IsExecutionHook = TRUE; // In execution hook, we have to make sure to unset read, write because // an EPT violation should occur for these cases and we can swap the original page ChangedEntry.ReadAccess = 0; ChangedEntry.WriteAccess = 0; ChangedEntry.ExecuteAccess = 1; // Also set the current pfn to fake page ChangedEntry.PageFrameNumber = HookedPage-&gt;PhysicalBaseAddressOfFakePageContents; // Copy the content to the fake page RtlCopyBytes(&amp;HookedPage-&gt;FakePageContents, VirtualTarget, PAGE_SIZE); // Create Hook if (!EptHookInstructionMemory(HookedPage, TargetAddress, HookFunction, OrigFunction)) { LogError("Could not build the hook."); return FALSE; } } // Save the modified entry HookedPage-&gt;ChangedEntry = ChangedEntry; // Add it to the list InsertHeadList(&amp;EptState-&gt;HookedPagesList, &amp;(HookedPage-&gt;PageHookList)); /***********************************************************/ // if not launched, there is no need to modify it on a safe environment if (!GuestState[LogicalCoreIndex].HasLaunched) { // Apply the hook to EPT TargetPage-&gt;Flags = ChangedEntry.Flags; } else { // Apply the hook to EPT EptSetPML1AndInvalidateTLB(TargetPage, ChangedEntry, INVEPT_SINGLE_CONTEXT); } return TRUE;}Now we need a function that creates another page and patches the original page (Page A) with an absolute jump (trampoline) that jumps another page (Page B).In (Page B) we will jump to the hooked function also this function copies the bytes that are patched to the (Page B) and save the original function for the caller to return back to the original page on (Page B).This is a simple inline hook that we use LDE (LDE64x64) as the detour function.BOOLEAN EptHookInstructionMemory(PEPT_HOOKED_PAGE_DETAIL Hook, PVOID TargetFunction, PVOID HookFunction, PVOID* OrigFunction){ SIZE_T SizeOfHookedInstructions; SIZE_T OffsetIntoPage; OffsetIntoPage = ADDRMASK_EPT_PML1_OFFSET((SIZE_T)TargetFunction); LogInfo("OffsetIntoPage: 0x%llx", OffsetIntoPage); if ((OffsetIntoPage + 13) &gt; PAGE_SIZE - 1) { LogError("Function extends past a page boundary. We just don't have the technology to solve this....."); return FALSE; } /* Determine the number of instructions necessary to overwrite using Length Disassembler Engine */ for (SizeOfHookedInstructions = 0; SizeOfHookedInstructions &lt; 13; SizeOfHookedInstructions += LDE(TargetFunction, 64)) { // Get the full size of instructions necessary to copy } LogInfo("Number of bytes of instruction mem: %d", SizeOfHookedInstructions); /* Build a trampoline */ /* Allocate some executable memory for the trampoline */ Hook-&gt;Trampoline = PoolManagerRequestPool(EXEC_TRAMPOLINE, TRUE, MAX_EXEC_TRAMPOLINE_SIZE); if (!Hook-&gt;Trampoline) { LogError("Could not allocate trampoline function buffer."); return FALSE; } /* Copy the trampoline instructions in. */ RtlCopyMemory(Hook-&gt;Trampoline, TargetFunction, SizeOfHookedInstructions); /* Add the absolute jump back to the original function. */ EptHookWriteAbsoluteJump(&amp;Hook-&gt;Trampoline[SizeOfHookedInstructions], (SIZE_T)TargetFunction + SizeOfHookedInstructions); LogInfo("Trampoline: 0x%llx", Hook-&gt;Trampoline); LogInfo("HookFunction: 0x%llx", HookFunction); /* Let the hook function call the original function */ *OrigFunction = Hook-&gt;Trampoline; /* Write the absolute jump to our shadow page memory to jump to our hook. */ EptHookWriteAbsoluteJump(&amp;Hook-&gt;FakePageContents[OffsetIntoPage], (SIZE_T)HookFunction); return TRUE;}For creating a simple absolute jump we use the following function./* Write an absolute x64 jump to an arbitrary address to a buffer. */VOID EptHookWriteAbsoluteJump(PCHAR TargetBuffer, SIZE_T TargetAddress){ /* mov r15, Target */ TargetBuffer[0] = 0x49; TargetBuffer[1] = 0xBB; /* Target */ *((PSIZE_T)&amp;TargetBuffer[2]) = TargetAddress; /* push r15 */ TargetBuffer[10] = 0x41; TargetBuffer[11] = 0x53; /* ret */ TargetBuffer[12] = 0xC3;}In the case of EPT Violations, first, we find the details of the physical address that caused this vm-exit. Then we call EptHandleHookedPage to create a log about the details then we set an MTF to restore to the hooked state after executing one instruction./* Check if this exit is due to a violation caused by a currently hooked page. Returns FALSE * if the violation was not due to a page hook. * * If the memory access attempt was RW and the page was marked executable, the page is swapped with * the original page. * * If the memory access attempt was execute and the page was marked not executable, the page is swapped with * the hooked page. */BOOLEAN EptHandlePageHookExit(VMX_EXIT_QUALIFICATION_EPT_VIOLATION ViolationQualification, UINT64 GuestPhysicalAddr){ BOOLEAN IsHandled = FALSE; PLIST_ENTRY TempList = 0; TempList = &amp;EptState-&gt;HookedPagesList; while (&amp;EptState-&gt;HookedPagesList != TempList-&gt;Flink) { TempList = TempList-&gt;Flink; PEPT_HOOKED_PAGE_DETAIL HookedEntry = CONTAINING_RECORD(TempList, EPT_HOOKED_PAGE_DETAIL, PageHookList); if (HookedEntry-&gt;PhysicalBaseAddress == PAGE_ALIGN(GuestPhysicalAddr)) { /* We found an address that match the details */ /* Returning true means that the caller should return to the ept state to the previous state when this instruction is executed by setting the Monitor Trap Flag. Return false means that nothing special for the caller to do */ if (EptHandleHookedPage(HookedEntry, ViolationQualification, GuestPhysicalAddr)) { // Next we have to save the current hooked entry to restore on the next instruction's vm-exit GuestState[KeGetCurrentProcessorNumber()].MtfEptHookRestorePoint = HookedEntry; // We have to set Monitor trap flag and give it the HookedEntry to work with HvSetMonitorTrapFlag(TRUE); } // Indicate that we handled the ept violation IsHandled = TRUE; // Get out of the loop break; } } // Redo the instruction GuestState[KeGetCurrentProcessorNumber()].IncrementRip = FALSE; return IsHandled;}Each time an EPT Violation occurs, we check whether it was because Read Access or Write Access or Execute Access violation and log GUEST_RIP, then we restore the initial flags (All read, write, and exec is allowed).BOOLEAN EptHandleHookedPage(EPT_HOOKED_PAGE_DETAIL* HookedEntryDetails, VMX_EXIT_QUALIFICATION_EPT_VIOLATION ViolationQualification, SIZE_T PhysicalAddress) { ULONG64 GuestRip; ULONG64 ExactAccessedAddress; ULONG64 AlignedVirtualAddress; ULONG64 AlignedPhysicalAddress; // Get alignment AlignedVirtualAddress = PAGE_ALIGN(HookedEntryDetails-&gt;VirtualAddress); AlignedPhysicalAddress = PAGE_ALIGN(PhysicalAddress); // Let's read the exact address that was accesses ExactAccessedAddress = AlignedVirtualAddress + PhysicalAddress - AlignedPhysicalAddress; // Reading guest's RIP __vmx_vmread(GUEST_RIP, &amp;GuestRip); if (!ViolationQualification.EptExecutable &amp;&amp; ViolationQualification.ExecuteAccess) { LogInfo("Guest RIP : 0x%llx tries to execute the page at : 0x%llx", GuestRip, ExactAccessedAddress); } else if (!ViolationQualification.EptWriteable &amp;&amp; ViolationQualification.WriteAccess) { LogInfo("Guest RIP : 0x%llx tries to write on the page at :0x%llx", GuestRip, ExactAccessedAddress); } else if (!ViolationQualification.EptReadable &amp;&amp; ViolationQualification.ReadAccess) { LogInfo("Guest RIP : 0x%llx tries to read the page at :0x%llx", GuestRip, ExactAccessedAddress); } else { // there was an unexpected ept violation return FALSE; } EptSetPML1AndInvalidateTLB(HookedEntryDetails-&gt;EntryAddress, HookedEntryDetails-&gt;OriginalEntry, INVEPT_SINGLE_CONTEXT); // Means that restore the Entry to the previous state after current instruction executed in the guest return TRUE;}That’s it! We have a working hidden hooks.Removing Hooks From PagesRemoving hooks from pages are essential to us because of two reasons; first, sometimes we need to disable the hooks, and second, when we want to turn off hypervisor, we have to remove all the hooks. Otherwise, we might encounter strange behavior.Removing hooks is simple as we saved details, including original entries in PageHookList; we have to find entries in this list and broadcast to all processors to update their TLBs and also remove that entry.The following function is for this purpose./* Remove single hook from the hooked pages list and invalidate TLB */BOOLEAN HvPerformPageUnHookSinglePage(UINT64 VirtualAddress) { PLIST_ENTRY TempList = 0; SIZE_T PhysicalAddress; PhysicalAddress = PAGE_ALIGN(VirtualAddressToPhysicalAddress(VirtualAddress)); // Should be called from vmx non-root if (GuestState[KeGetCurrentProcessorNumber()].IsOnVmxRootMode) { return FALSE; } TempList = &amp;EptState-&gt;HookedPagesList; while (&amp;EptState-&gt;HookedPagesList != TempList-&gt;Flink) { TempList = TempList-&gt;Flink; PEPT_HOOKED_PAGE_DETAIL HookedEntry = CONTAINING_RECORD(TempList, EPT_HOOKED_PAGE_DETAIL, PageHookList); if (HookedEntry-&gt;PhysicalBaseAddress == PhysicalAddress) { // Remove it in all the cores KeGenericCallDpc(HvDpcBroadcastRemoveHookAndInvalidateSingleEntry, HookedEntry-&gt;PhysicalBaseAddress); // remove the entry from the list RemoveEntryList(HookedEntry-&gt;PageHookList.Flink); return TRUE; } } // Nothing found , probably the list is not found return FALSE;}In vmx-root, we also search for the specific hook and use EptSetPML1AndInvalidateTLB to return that entry to the initial state, which is previously saved in OriginalEntry./* Remove and Invalidate Hook in TLB */// Caution : This function won't remove entries from LIST_ENTRY, just invalidate the paging, use HvPerformPageUnHookSinglePage insteadBOOLEAN EptPageUnHookSinglePage(SIZE_T PhysicalAddress) { PLIST_ENTRY TempList = 0; // Should be called from vmx-root, for calling from vmx non-root use the corresponding VMCALL if (!GuestState[KeGetCurrentProcessorNumber()].IsOnVmxRootMode) { return FALSE; } TempList = &amp;EptState-&gt;HookedPagesList; while (&amp;EptState-&gt;HookedPagesList != TempList-&gt;Flink) { TempList = TempList-&gt;Flink; PEPT_HOOKED_PAGE_DETAIL HookedEntry = CONTAINING_RECORD(TempList, EPT_HOOKED_PAGE_DETAIL, PageHookList); if (HookedEntry-&gt;PhysicalBaseAddress == PAGE_ALIGN(PhysicalAddress)) { // Undo the hook on the EPT table EptSetPML1AndInvalidateTLB(HookedEntry-&gt;EntryAddress, HookedEntry-&gt;OriginalEntry, INVEPT_SINGLE_CONTEXT); return TRUE; } } // Nothing found , probably the list is not found return FALSE;}If we want to unhook all the pages, then we use another VMCALL, there is no need to iterate through the list here as all of the hooks must be removed. Just broadcast it through all the cores./* Remove all hooks from the hooked pages list and invalidate TLB */// Should be called from Vmx Non-rootVOID HvPerformPageUnHookAllPages() { // Should be called from vmx non-root if (GuestState[KeGetCurrentProcessorNumber()].IsOnVmxRootMode) { return; } // Remove it in all the cores KeGenericCallDpc(HvDpcBroadcastRemoveHookAndInvalidateAllEntries, 0x0); // No need to remove the list as it will automatically remove by the pool uninitializer}In vmx-root we just iterate through the list and restore them to the initial state./* Remove and Invalidate Hook in TLB */// Caution : This function won't remove entries from LIST_ENTRY, just invalidate the paging, use HvPerformPageUnHookAllPages insteadVOID EptPageUnHookAllPages() { PLIST_ENTRY TempList = 0; // Should be called from vmx-root, for calling from vmx non-root use the corresponding VMCALL if (!GuestState[KeGetCurrentProcessorNumber()].IsOnVmxRootMode) { return FALSE; } TempList = &amp;EptState-&gt;HookedPagesList; while (&amp;EptState-&gt;HookedPagesList != TempList-&gt;Flink) { TempList = TempList-&gt;Flink; PEPT_HOOKED_PAGE_DETAIL HookedEntry = CONTAINING_RECORD(TempList, EPT_HOOKED_PAGE_DETAIL, PageHookList); // Undo the hook on the EPT table EptSetPML1AndInvalidateTLB(HookedEntry-&gt;EntryAddress, HookedEntry-&gt;OriginalEntry, INVEPT_SINGLE_CONTEXT); }}An Important Note When Modifying EPT EntriesOne interesting thing that I encountered during the test of my driver on the multi-core system was the fact that EPT entries should be modified in one instruction.For example, if you change the access bits of an EPT entry, bit by bit, then you probably get the error (EPT Misconfiguration) that one access bits changed and before the next access bit applies another core tries to access page table and it sometimes leads to an EPT Misconfiguration and sometimes you might not get the desired behavior.For example the following method for modifying EPT entries is wrong!HookedEntryDetails-&gt;EntryAddress-&gt;ExecuteAccess = 1;HookedEntryDetails-&gt;EntryAddress-&gt;WriteAccess = 1;HookedEntryDetails-&gt;EntryAddress-&gt;ReadAccess = 1;But the following code is correct. (Applying changes in one instruction instantly). // Apply the hook to EPT TargetPage-&gt;Flags = OriginalEntry.Flags;This is why we have the following function that acquires a spinlock that makes sure that only one entry is modified once and then invalidate that core’s TLB./* This function set the specific PML1 entry in a spinlock protected area then invalidate the TLB , this function should be called from vmx root-mode*/VOID EptSetPML1AndInvalidateTLB(PEPT_PML1_ENTRY EntryAddress, EPT_PML1_ENTRY EntryValue, INVEPT_TYPE InvalidationType){ // acquire the lock SpinlockLock(&amp;Pml1ModificationAndInvalidationLock); // set the value EntryAddress-&gt;Flags = EntryValue.Flags; // invalidate the cache if (InvalidationType == INVEPT_SINGLE_CONTEXT) { InveptSingleContext(EptState-&gt;EptPointer.Flags); } else { InveptAllContexts(); } // release the lock SpinlockUnlock(&amp;Pml1ModificationAndInvalidationLock);}The above function solves the problems of simultaneously modifying the EPT Table as we have one EPT Table for all cores.System-Call HookWhen it comes to hypervisors, we have different options for hooking system-calls. Each of these methods has its own advantages and disadvantages.Let’s review some of the methods, that we can use to hook system-calls.The first method is hooking MSR 0xc0000082 (LSTAR). This MSR is the kernel-entry for dispatching system-calls. Each time an instruction like Syscall is executed in user-mode, the processor automatically switches to kernel-mode and runs the address stored in this MSR. In Windows address of KiSystemCall64 is stored in this MSR.This means that each time an application needs to call a system-call, it executes a syscall, and now this function is responsible for finding the entries in SSDT and call. In short, SSDT is a table in Windows that stores pointer to Windows function based on a system-call number. All SSDT entries and LSTAR MSR is under the control of PatchGuard.This brings us three possibilities!First, we can change the MSR LSTAR to point to our custom function, and to make it PatchGuard compatible, we can set MSR Bitmap that if any kernel routine wants to read this MSR, then a vm-exit occurs so we can change the result. Instead of showing our custom handler, we can show the KiSystemCall64, and PatchGuard will never know that this is a fake MSR.Hooking MSR LSTAR is complicated, and updates to Meltdown make it even more complicated. In a post-meltdown system, LSTAR points to KiSystemCall64Shadow, which involves changing CR3 and execute KPTI-related instruction and Meltdown mitigation. It’s not a good idea to hook LSTAR as we have difficulties with pre-Meltdown and post-Meltdown mitigations and also as the system-state changes in this MSR so we can’t hook anything in the kernel as the kernel is not mapped on CR3.Hyperbone uses this method (even it not updated for post-meltdown systems in the time of writing this article).The second option is finding SSDT tables and change their entry to point to our custom functions, each time the PatchGuard tries to audit these entries, we can show it the not-patched listings. The only thing that we should keep in mind is to find where KiSystemCall64 tries to read that location and save that location somewhere so we can know that if the function that tries to read is syscall dispatcher our other functions (and probably PatchGuard).Implementing this method is not super-fast as we need to unset EPT Read for SSDT entry, and each time a read happens, a vm-exit occurs, so we have one vm-exit for each syscall thus it makes our computer slow!The third option is finding functions in SSDT entries and put a hidden hook on the functions that we need to hook. This way, we can catch a custom list of functions because I think hooking all system-calls is stupid!We implement the third option in this part.Another possible way is Syscall Hooking Via Extended Feature Enable Register (EFER), as described here. This method is based on disabling Syscall Enable (or SCE bit) of the EFER MSR; hence each time a Syscall is executed, a #UD exception is generated by the processor, and we can intercept #UD by using Exception Bitmap (described above) to handle these syscalls.Again it’s not a good idea because it leads to a vm-exit for each syscall; thus, it’s substantially slow but usable for experimental purposes.Also, they might be other options. Don’t hesitate to send a comment to this post and describe if you know one!Finding Kernel BaseTo find SSDT, we need to find nt!KeServiceDescriptorTable and nt!KeServiceDescriptorTableShadow, these tables are exported in x86 systems but not in x64. This makes the things much complicated as the routines to find these tables might change in future versions of Windows; thus, our Syscall hooker might have problems in future versions.First of all, we need to find the base address of ntoskrnl, and it’s the image size, this is done by using ZwQuerySystemInformation, first, we find this function by using MmGetSystemRoutineAddress.Then we allocate a memory to get the details from Windows and find the base address and module size./* Get the kernel base and Image size */PVOID SyscallHookGetKernelBase(PULONG pImageSize){ NTSTATUS status; ZWQUERYSYSTEMINFORMATION ZwQSI = 0; UNICODE_STRING routineName; PVOID pModuleBase = NULL; PSYSTEM_MODULE_INFORMATION pSystemInfoBuffer = NULL; ULONG SystemInfoBufferSize = 0; RtlInitUnicodeString(&amp;routineName, L"ZwQuerySystemInformation"); ZwQSI = (ZWQUERYSYSTEMINFORMATION)MmGetSystemRoutineAddress(&amp;routineName); if (!ZwQSI) return NULL; status = ZwQSI(SystemModuleInformation, &amp;SystemInfoBufferSize, 0, &amp;SystemInfoBufferSize); if (!SystemInfoBufferSize) { LogError("ZwQuerySystemInformation (1) failed"); return NULL; } pSystemInfoBuffer = (PSYSTEM_MODULE_INFORMATION)ExAllocatePool(NonPagedPool, SystemInfoBufferSize * 2); if (!pSystemInfoBuffer) { LogError("ExAllocatePool failed"); return NULL; } memset(pSystemInfoBuffer, 0, SystemInfoBufferSize * 2); status = ZwQSI(SystemModuleInformation, pSystemInfoBuffer, SystemInfoBufferSize * 2, &amp;SystemInfoBufferSize); if (NT_SUCCESS(status)) { pModuleBase = pSystemInfoBuffer-&gt;Module[0].ImageBase; if (pImageSize) *pImageSize = pSystemInfoBuffer-&gt;Module[0].ImageSize; } else { LogError("ZwQuerySystemInformation (2) failed"); return NULL; } ExFreePool(pSystemInfoBuffer); return pModuleBase;}Update 2: You can also use RtlPcToFileHeader instead of above method:RtlPcToFileHeader(&amp;RtlPcToFileHeader, &amp;NtoskrnlBase);Finding SSDT and Shadow SSDT TablesNow that we have the base address ntoskrnl we can search for this pattern to find nt!KeServiceDescriptorTableShadow. const unsigned char KiSystemServiceStartPattern[] = { 0x8B, 0xF8, 0xC1, 0xEF, 0x07, 0x83, 0xE7, 0x20, 0x25, 0xFF, 0x0F, 0x00, 0x00 };nt!KeServiceDescriptorTableShadow contains the nt!KiServiceTable and win32k!W32pServiceTable, which is the SSDT of Syscall function for both NT Syscalls and Win32K Syscalls./* Find SSDT address of Nt fucntions and W32Table */BOOLEAN SyscallHookFindSsdt(PUINT64 NtTable, PUINT64 Win32kTable){ ULONG kernelSize = 0; ULONG_PTR kernelBase; const unsigned char KiSystemServiceStartPattern[] = { 0x8B, 0xF8, 0xC1, 0xEF, 0x07, 0x83, 0xE7, 0x20, 0x25, 0xFF, 0x0F, 0x00, 0x00 }; const ULONG signatureSize = sizeof(KiSystemServiceStartPattern); BOOLEAN found = FALSE; LONG relativeOffset = 0; ULONG_PTR addressAfterPattern; ULONG_PTR address; SSDTStruct* shadow; PVOID ntTable; PVOID win32kTable; //x64 code kernelBase = (ULONG_PTR)SyscallHookGetKernelBase(&amp;kernelSize); if (kernelBase == 0 || kernelSize == 0) return FALSE; // Find KiSystemServiceStart ULONG KiSSSOffset; for (KiSSSOffset = 0; KiSSSOffset &lt; kernelSize - signatureSize; KiSSSOffset++) { if (RtlCompareMemory(((unsigned char*)kernelBase + KiSSSOffset), KiSystemServiceStartPattern, signatureSize) == signatureSize) { found = TRUE; break; } } if (!found) return FALSE; addressAfterPattern = kernelBase + KiSSSOffset + signatureSize; address = addressAfterPattern + 7; // Skip lea r10,[nt!KeServiceDescriptorTable] // lea r11, KeServiceDescriptorTableShadow if ((*(unsigned char*)address == 0x4c) &amp;&amp; (*(unsigned char*)(address + 1) == 0x8d) &amp;&amp; (*(unsigned char*)(address + 2) == 0x1d)) { relativeOffset = *(LONG*)(address + 3); } if (relativeOffset == 0) return FALSE; shadow = (SSDTStruct*)(address + relativeOffset + 7); ntTable = (PVOID)shadow; win32kTable = (PVOID)((ULONG_PTR)shadow + 0x20); // Offset showed in Windbg *NtTable = ntTable; *Win32kTable = win32kTable; return TRUE;}Note that nt!KeServiceDescriptorTable only contains the nt!KiServiceTable, and it doesn’t provide win32k!W32pServiceTable.Get Routine Address by Syscall NumberAfter finding the NT Syscall Table and Win32k Syscall Table, now it’s time to translate Syscall Numbers to its corresponding address.The following formula converts API Number to function address.((SSDT-&gt;pServiceTable[ApiNumber] &gt;&gt; 4) + SSDTbase);Keep in mind that NT Syscalls start from 0x0, but Win32k Syscalls start from 0x1000, so as we computer indexes based on the start of the table, we should minus the Win32k Syscalls with 0x1000.All in all, we have the following function./* Find entry from SSDT table of Nt fucntions and W32Table syscalls */PVOID SyscallHookGetFunctionAddress(INT32 ApiNumber, BOOLEAN GetFromWin32k){ SSDTStruct* SSDT; BOOLEAN Result; ULONG_PTR SSDTbase; ULONG ReadOffset; UINT64 NtTable, Win32kTable; // Read the address og SSDT Result = SyscallHookFindSsdt(&amp;NtTable, &amp;Win32kTable); if (!Result) { LogError("SSDT not found"); return 0; } if (!GetFromWin32k) { SSDT = NtTable; } else { // Win32k APIs start from 0x1000 ApiNumber = ApiNumber - 0x1000; SSDT = Win32kTable; } SSDTbase = (ULONG_PTR)SSDT-&gt;pServiceTable; if (!SSDTbase) { LogError("ServiceTable not found"); return 0; } return (PVOID)((SSDT-&gt;pServiceTable[ApiNumber] &gt;&gt; 4) + SSDTbase);}Now that we have the address of the routine that we want, now it’s time to put a hidden hook on that function, we also need their functions prototypes so we can read their arguments appropriately.The syscall hook example is demonstrated later in the (How to test?) section.Virtual Processor ID (VPID) &amp; TLBIn Intel, its explanation about VPIDs is vague, so I found a great link that explains is so much more straightforward; hence it’s better to read the details below instead of starting with SDM.The translation lookaside buffer (TLB) is a high-speed memory page cache for virtual to physical address translation. It follows the local principle to avoid time-consuming lookups for recently used pages.Host mappings are not coherent to the guest and vice versa. Each guest has it’s own address space, the mapping table cannot be re-used in another guest (or host). Therefore first-generation VMs like Intel Core 2 (VMX) flush the TLB on each VM-enter (resume) and VM-exit. But flushing the TLB is a show-stopper, it is one of the most critical components in a modern CPU.Intel engineers started to think about that. Intel Nehalem TLB entries have changed by introducing a Virtual Processor ID. So each TLB entry is tagged with this ID. The CPU does not specify VPIDs, the hypervisor allocates them, whereas the host VPID is 0. Starting with Intel Nehalem, the TLB must not be flushed. When a process tries to access a mapping where the actual VPID does not match with the TLB entry VPID a standard TLB miss occurs. Some Intel numbers show that the latency performance gain is 40% for a VM round trip transition compared to Meron, an Intel Core 2.Imagine you have two or more VMs: If you enable VPIDs, you don’t have to worry that VM1 accidentally, fetches cached memory of VM2 (or even hypervisor itself) If you don’t enable VPIDs, CPU assigns VPID=0 to all operations (VMX root &amp; VMX non-root) and flushes TLB on each transition for youA logical processor may tag some cached information with a 16-bit VPID.The VPID is 0000H in the following situations: Outside VMX operation. (e.g System Management Mode (SMM)). VMX root operation VMX non-root operation when the “enable VPID” VM-execution control is 0INVVPID - Invalidate Translations Based on VPIDIn order to support VPIDs, we have to add CPU_BASED_CTL2_ENABLE_VPID to Secondary Processor-Based VM-Execution Controls.The next step is to set a 16-bit value to VMCS’s VIRTUAL_PROCESSOR_ID field using VMWRITE instruction. This value is used as an index for the current VMCS on this core so our current VMCS’s VPID is 1.Also, as described above, 0 has special meaning and should not be used. // Set up VPID /* For all processors, we will use a VPID = 1. This allows the processor to separate caching of EPT structures away from the regular OS page translation tables in the TLB. */ __vmx_vmwrite(VIRTUAL_PROCESSOR_ID, 1);INVVPID (instruction) invalidates mappings in the translation lookaside buffers (TLBs) and paging-structure caches based on the virtual processor identifier (VPID).For the INVVPID there are 4 types that currently supported by the processors which are reported in the IA32_VMX_EPT_VPID_CAP MSR.The enumeration for these types are :typedef enum _INVVPID_ENUM{ INDIVIDUAL_ADDRESS = 0x00000000, SINGLE_CONTEXT = 0x00000001, ALL_CONTEXT = 0x00000002, SINGLE_CONTEXT_RETAINING_GLOBALS = 0x00000003}INVVPID_ENUM, *PINVVPID_ENUM;I’ll describe these types in detail later.For the implementation of INVVPID we use an assembly function like this (which executes invvpid from the RCX and RDX for x64 fast calling convention) :AsmInvept PROC PUBLIC invept rcx, oword ptr [rdx] jz @jz jc @jc xor rax, rax ret @jz: mov rax, VMX_ERROR_CODE_FAILED_WITH_STATUS ret @jc: mov rax, VMX_ERROR_CODE_FAILED retAsmInvept ENDPand then, a general purpose function for calling this assembly function :inline Invvpid(INVVPID_ENUM Type, INVVPID_DESCRIPTOR* Descriptor){ if (!Descriptor) { static INVVPID_DESCRIPTOR ZeroDescriptor = { 0 }; Descriptor = &amp;ZeroDescriptor; } return AsmInvvpid(Type, Descriptor);}For INVVPID, there is a descriptor defined below.This structure defined like this :typedef struct _INVVPID_DESCRIPTOR{ UINT64 VPID : 16; UINT64 RESERVED : 48; UINT64 LINEAR_ADDRESS;} INVVPID_DESCRIPTOR, *PINVVPID_DESCRIPTOR;The types of INVVPID is defined as below : Individual-address invalidation: If the INVVPID type is 0, the logical processor invalidates mappings for the linear address, and VPID specified in the INVVPID descriptor. In some cases, it may invalidate mappings for other linear addresses (or other VPIDs) as well.inline InvvpidIndividualAddress(UINT16 Vpid, UINT64 LinearAddress){ INVVPID_DESCRIPTOR Descriptor = { Vpid, 0, LinearAddress }; return Invvpid(INDIVIDUAL_ADDRESS, &amp;Descriptor);} Single-context invalidation: If the INVVPID type is 1, the logical processor invalidates all mappings tagged with the VPID specified in the INVVPID descriptor. In some cases, it may invalidate mappings for other VPIDs as well.inline InvvpidSingleContext(UINT16 Vpid){ INVVPID_DESCRIPTOR Descriptor = { Vpid, 0, 0 }; return Invvpid(SINGLE_CONTEXT, &amp;Descriptor);} All-contexts invalidation: If the INVVPID type is 2, the logical processor invalidates all mappings tagged with all VPIDs except VPID 0000H. In some cases, it may invalidate translations with VPID 0000H as well.inline InvvpidAllContexts(){ return Invvpid(ALL_CONTEXT, NULL);} Single-context invalidation, retaining global translations: If the INVVPID type is 3, the logical processor invalidates all mappings tagged with the VPID specified in the INVVPID descriptor except global translations. In some cases, it may invalidate global translations (and mappings with other VPIDs) as well. See the “Caching Translation Information” section in Chapter 4 of the IA-32 Intel Architecture Software Developer’s Manual, Volumes 3A for information about global translations.inline InvvpidSingleContextRetainingGlobals(UINT16 Vpid){ INVVPID_DESCRIPTOR Descriptor = { Vpid, 0, 0 }; return Invvpid(SINGLE_CONTEXT_RETAINING_GLOBALS, &amp;Descriptor);}You probably think about how VPIDs can be used in the hypervisor. We can use it instead of INVEPT, but generally, it doesn’t have any particular usage for us. I described it more in the Discussion Section. By the way, VPIDs will be used in implementing special features as it’s more flexible than INVEPT and also when we have multiple VMCS (EPTP). (Can you think about some of them?).Important Notes For Using VPIDsThere are some important things that you should know when using VPIDs.Enabling VPIDs have a side-effect of not flushing TLB on VMEntry/VMExit. You should manually flush guest TLB entries if required (By using INVEPT/INVVPID). These issues might be hidden when VPID is disabled.When VPID is disabled, VMEntry flushes the entire TLB. Thus, the hypervisor doesn’t need to explicitly invalidate TLB entries populated by the guest when performing an operation that should invalidate them (e.g., Modifying an EPT entry). When VPID is enabled, INVEPT/INVVPID should be used.An easy way for you to find these kinds of issues is indeed the issue you have, is to execute INVEPT global-context before every VMEntry to flush entire TLB while still keeping VPID enabled. If it now works, you should check where you are missing an INVEPT execution.In my experience, if you just enable VPIDs without any extra assumption, all processes start to crash one by one, and eventually, kernel crashes, and this is because we didn’t invalidate the TLB.In order to solve the problem of crashing every process, we have to invalidate TLB in the case of Mov to Cr3 thus whenever a vm-exit occurs with reason == EXIT_REASON_CR_ACCESS (28) then if it’s a Mov to Cr3 we have to invalidate TLB (INVEPT or INVVPID [Look at the Update 1 for more details]).So we edit the code like this: case TYPE_MOV_TO_CR: { switch (CrExitQualification-&gt;Fields.ControlRegister) { case 0: __vmx_vmwrite(GUEST_CR0, *RegPtr); __vmx_vmwrite(CR0_READ_SHADOW, *RegPtr); break; case 3: __vmx_vmwrite(GUEST_CR3, (*RegPtr &amp; ~(1ULL &lt;&lt; 63))); // InveptSingleContext(EptState-&gt;EptPointer.Flags); (changed, look for "Update 1" at the 8th part for more detail) InvvpidSingleContext(VPID_TAG); break; case 4: __vmx_vmwrite(GUEST_CR4, *RegPtr); __vmx_vmwrite(CR4_READ_SHADOW, *RegPtr); break; default: LogWarning("Unsupported register %d in handling control registers access", CrExitQualification-&gt;Fields.ControlRegister); break; } }Also, note that as we have a single EPTP for all cores then it’s enough to invalidate single-context otherwise we have to invalidate all-contexts.Update 1 : As Satoshi Tanda mentioned,The CR3 handler should use INVVPID instead of INVEPT because INVEPT invalidates more than needed. We want to invalid caches of GVA -&gt; HPA (combined mappings), and both instructions do this. This is why INVEPT works too, but INVEPT also invalidates caches of GPA -&gt; HPA (guest-physical mappings), which are not impacted by the guest CR3 change and can be kept without invalidation.The general guideline is, INVVPID when TLB flush emulation is needed, and INVEPT when EPT entries are changed. You can find more info on those instructions and cache types in : 28.3.1 Information That May Be Cached 28.3.3.3 Guidelines for Use of the INVVPID Instruction.so instead of InveptSingleContext we used InvvpidSingleContext.Honestly, we have some misunderstanding about handling Cr3 vm-exits, even though the above code works fine, but generally, it has some performance penalties. I’ll explain these performance problems in the “Fixing Previous Design Issues” section.You might also ask why we avoid writing the 63rd bit of the CR3. __vmx_vmwrite(GUEST_CR3, (*RegPtr &amp; ~(1ULL &lt;&lt; 63)));Bit 63 of CR3 is a new bit that is part of the PCID feature. It allows OS to change CR3 value without invalidating all TLB entries (tagged with the same EP4TA and VPID) besides those marked with global-bit.EP4TA is the value of bits 51:12 of EPTP.E.g. Windows KVA Shadowing and Linux KPTI signal this bit on CR3 mov that changes PCID between userspace PCID and kernel space PCID on user and kernel transitions.We should not write on bit 63 of CR3 on mov reg, cr3 emulation because the processor does not write and attempt to write this will cause a crash on modern Win10. INVVPID vs. INVPCIDINVPCID is not really relevant to hypervisor but in the case, if you want to know, INVPCID invalidates mappings in the translation lookaside buffers (TLBs) and paging-structure caches based on the process-context identifier (PCID).So it’s like INVVPID with the difference that it’s not specific to the hypervisor. It also has its particular contexts (currently 3), you can read more here but generally keep in mind that to reduce that overhead, a feature called Process Context ID (PCID) was introduced by Intel’s Westmere architecture and related instruction, INVPCID (invalidate PCID) with Haswell. With PCID enabled, the way the TLB is used and flushed changes. First, the TLB tags each entry with the PCID of the process that owns the entry. This allows two different mappings from the same virtual address to be stored in the TLB as long as they have a different PCID. Second, with PCID enabled, switching from one set of page tables to another doesn’t flush the TLB any more. Since each process can only use TLB entries that have the right PCID, there’s no need to flush the TLB each time.This behavior is used in Meltdown mitigation to avoid wiping out the entire TLB for the processors that support PCID.Designing A VMX Root-mode Compatible Message TracingWithout any doubt, one of the hardest parts of designing a hypervisor is sending a message from Vmx root-mode to Vmx non-root mode. This is because you have lots of limitations like you can’t access non-paged buffer, and of course, most of the NT functions are not (ANY IRQL) compatible as they might access the buffers that reside in paged pool.The things are ending here, there are plenty of other limitation to deal with.This section is inspired by Chapter 6: Kernel Mechanisms (High IRQL Synchronization) from the Windows Kernel Programming book by Pavel Yosifovich which is a really amazing book if you want to start with kernel programming.ConceptsThis section describes some of the Operating System concepts, you should know before starting.What’s a spinlock?The Spin Lock is a bit in memory that provides atomic test and modify operations. When a CPU tries to acquire a spinlock, and it’s not currently free, the CPU keeps spinning on the spinlock, busy waiting for it to be released by another CPU means that it keeps checking until another thread which acquired it first release it.Test-and-SetYou probably read about Test and Set in university. Still, in case you didn’t, in computer science, the test-and-set instruction is an instruction used to write 1 (set) to a memory location and return its old value as a single atomic (i.e., non-interruptible) operation. If multiple processes may access the same memory location, and if a process is currently performing a test-and-set, no other process may begin another test-and-set until the first process’s test-and-set is finished.What do we mean by “Safe”?The “safe” is used a lot in hypervisors. By “safe,” we mean something that works all the time and won’t cause system crash or system halt. It’s because it’s so tricky to manage codes in vmx root-mode. After all, interrupts are masked (disabled), or transfer buffer from vmx root-mode to vmx non-root mode needs extra effort, and we should be cautious and avoid executing some APIs to be safe.What is DPC?A Deferred Procedure Call (DPC) is a Windows mechanism that allows high-priority tasks (e.g., an interrupt handler) to defer required but lower-priority tasks for later execution. This permits device drivers and other low-level event consumers to perform the high-priority part of their processing quickly and schedule non-critical additional processing for execution at a lower priority.DPCs are implemented by DPC objects which are created and initialized by the kernel when a device driver or some other kernel-mode program issues requests for DPC. The DPC request is then added to the end of a DPC queue. Each processor has a separate DPC queue. DPCs have three priority levels: low, medium, and high. By default, all DPCs are set to medium priority. When Windows drops to an IRQL of Dispatch/DPC level, it checks the DPC queue for any pending DPCs and executes them until the queue is empty or some other interrupt with a higher IRQL occurs.This is the description of DPCs from MSDN:Because ISRs must execute as quickly as possible, drivers must usually postpone the completion of servicing an interrupt until after the ISR returns. Therefore, the system provides support for deferred procedure calls (DPCs), which can be queued from ISRs and which are executed at a later time and at a lower IRQL than the ISR.There are two posts about DPCs here and here, you can read them for more information.ChallengesFor example, Vmx-root mode is not a HIGH_IRQL interrupt (with discussing it in Discussion Section), but as it disables all of the interrupts, we can think like it’s a HIGH_IRQL state. The problem is that must of synchronization functions are designed to be worked on IRQL less than DISPATCH_LEVEL.Why is it problematic? Imagine you have a one-core processor, and your function requires a spinlock (let say it’s merely a buffer that needs to be accessed). The function raises the IRQL to DISPATCH_LEVEL. Now the Windows Scheduler can’t interrupt the function until it releases the spinlock and lowers the IRQL to PASSIVE_LEVEL or APC_LEVEL. During the execution of the function, a vm-exit occurs; thus, we’re in vmx root-mode now. It’s because, as I told you, vm-exit happens as if it’s a HIGH_IRQL interrupt.Now, what if we want to access that buffer in vmx root mode? Two scenarios might occur. We wait on a spinlock that was previously acquired by a thread in vmx non-root mode and this we have to wait forever. A deadlock occurs. We enter the function without looking at the lock (while there is another thread that enters the function at the same time.) so it results in a corrupted buffer and invalid data.The other limitation is in Windows design putting the thread into a waiting state cannot be done at IRQL DISPATCH_LEVEL or higher. It’s because in Windows when you acquire a spinlock it raises the IRQL to 2 – DISPATCH_LEVEL (if not already there), acquire the spinlock, perform the work and finally release the spinlock and lower IRQL back.If you look at a function like KeAcquireSpinLock and KeReleaseSpinLock, they get an IRQL in their arguments. First, KeAcquireSpinLock saves current IRQL to the parameter supplied by the user then raises the IRQL to DISPATCH_LEVEL and sets a bit. When the function finished its works with shared data, then it calls KeReleaseSpinLock and passes that old IRQL parameter so this function unsets the bit and restore the old IRQL (lowers the IRQL).Windows has 4 kinds of Spinlocks, KeAcquireSpinLock – KeReleaseSpinLock : This pair can be called at IRQL &lt;= DISPATCH_LEVEL. KeAcquireSpinLockAtDpcLevel – KeReleaseSpinLockFromDpcLevel : This pair can be call at IRQL = DISPATCH_LEVEL only, it’s more optimized if you are already in IRQL 2 as it doesn’t saves the old IRQL and it’s specially designed to work on DPC routine. KeAcquireInterruptSpinLock – KeReleaseInterruptSpinLock: Hardware based use this pair e.g in Interrupt Service Routine (ISR) or it used by drivers with an interrupt source. ExInterlockedXxx : This function raises the IRQL to HIGH_LEVEL and perform it’s task, it doesn’t need a release function as no one interrupt us on HIGH_IRQL.But unfortunately, things are more complicated when it comes to vmx root-mode. We don’t have IRQL in the vmx root-mode. It’s an operating system thing, so we can’t use any of the above functions, and things are getting worst if we want to use our message tracing mechanism between multiple cores!For these reasons, we have to design our custom spinlock.Designing A SpinlockDesigning spinlock in a multi-core system by its nature needs the hardware support for atomic operation means that hardware (most of the time processor) should guarantee that an operation is performed just by logical (hyper-threaded) core and it’s non-interruptible.There is an article here that describes different kinds of spinlock with different optimizations, also it’s implemented here.The design of this mechanism in the processor is beyond the scope of this article. We simply use an intrinsic function provided by Windows called “_interlockedbittestandset”.This makes our implementation super simple. We just need to use the following function, and it’s the responsibility of the processor to take care of everything.Update 2: We should use volatile keyword in parameters too, otherwise it’s like un-volatiling.inline BOOLEAN SpinlockTryLock(volatile LONG* Lock){ return (!(*Lock) &amp;&amp; !_interlockedbittestandset(Lock, 0));}Now we need to spin! If the above function was not successful, then we have to keep CPU checking to see when another processor releases the lock.Update 2: We should use volatile keyword in parameters too, otherwise it’s like un-volatiling.void SpinlockLock(volatile LONG* Lock){ unsigned wait = 1; while (!SpinlockTryLock(Lock)) { for (unsigned i = 0; i &lt; wait; ++i) { _mm_pause(); } // Don't call "pause" too many times. If the wait becomes too big, // clamp it to the max_wait. if (wait * 2 &gt; max_wait) { wait = max_wait; } else { wait = wait * 2; } }}If you wonder what is the _mm_pause() then it’s equal to PAUSE instruction in x86.Pause instruction is commonly used in the loop of testing spinlock, when some other thread owns the spinlock, to mitigate the tight loop.PAUSE notifies the CPU that this is a spinlock wait loop, so memory and cache accesses may be optimized. See also pause instruction in x86 for some more details about avoiding the memory-order mis-speculation when leaving the spin-loop. PAUSE may stop CPU for some time to save power. Older CPUs decode it as REP NOP, so you don’t have to check if it’s supported. Older CPUs will simply do nothing (NOP) as fast as possible.For releasing the lock, there is nothing special to do, so simply unset it without caring for any other processor as there is no other processor that wants to unset it.Update 2: We should use volatile keyword in parameters too, otherwise it’s like un-volatiling.void SpinlockUnlock(volatile LONG* Lock){ *Lock = 0;}The last step is to use a volatile variable as the lock.// Vmx-root lock for loggingvolatile LONG VmxRootLoggingLock;The “volatile” keyword tells the compiler that the value of the variable may change at any time without any action being taken by the code the compiler finds nearby. The implications of this are quite serious. There are lots of examples here if you have a problem with understanding “volatile”.Message Tracer DesignFor solving the above the challenge about deadlock, I create two message pools for saving messages. The first pool is designed to be used as storage for vmx non-root messages (buffers) and the second pool is used for vmx-root messages.We have the following structure that describes the state of each of these two pools.// Core-specific bufferstypedef struct _LOG_BUFFER_INFORMATION { UINT64 BufferStartAddress; // Start address of the buffer UINT64 BufferEndAddress; // End address of the buffer UINT64 BufferForMultipleNonImmediateMessage; // Start address of the buffer for accumulating non-immadiate messages UINT32 CurrentLengthOfNonImmBuffer; // the current size of the buffer for accumulating non-immadiate messages KSPIN_LOCK BufferLock; // SpinLock to protect access to the queue KSPIN_LOCK BufferLockForNonImmMessage; // SpinLock to protect access to the queue of non-imm messages UINT32 CurrentIndexToSend; // Current buffer index to send to user-mode UINT32 CurrentIndexToWrite; // Current buffer index to write new messages} LOG_BUFFER_INFORMATION, * PLOG_BUFFER_INFORMATION;Generally, we’ll save the buffer as illustrated below, each chunk of the message came with BUFFER_HEADER that describes that chunk.Other information for the buffer like Current Index to Write and Current to Send is saved in the above structure.A core buffer is like this , it's divided into MaximumPacketsCapacity chucks,each chunk has PacketChunkSize + sizeof(BUFFER_HEADER) size __________________________ | BUFFER_HEADER | |_________________________| | | | BODY | | (Buffer) | | size = PacketChunkSize | | | |_________________________| | BUFFER_HEADER | |_________________________| | | | BODY | | (Buffer) | | size = PacketChunkSize | | | |_________________________| | | | | | | | | | . | | . | | . | | | | | | | | | |_________________________| | BUFFER_HEADER | |_________________________| | | | BODY | | (Buffer) | | size = PacketChunkSize | | | |_________________________|The BUFFER_HEADER is defined like this,// Message buffer structuretypedef struct _BUFFER_HEADER { UINT32 OpeationNumber; // Operation ID to user-mode UINT32 BufferLength; // The actual length BOOLEAN Valid; // Determine whether the buffer was valid to send or not} BUFFER_HEADER, * PBUFFER_HEADER;We save the length of used length of the chunk and a bit which determine whether we sent it before or not.Operation Number is number, which will be sent to the user-mode to show the type of the buffer that came from the kernel. In other words, it’s a number that indicates the intention (and structure) of the buffer, so the user-mode application will know what to do with this buffer.The following Operation Numbers are currently defined :// Message area &gt;= 0x4#define OPERATION_LOG_INFO_MESSAGE 0x1#define OPERATION_LOG_WARNING_MESSAGE 0x2#define OPERATION_LOG_ERROR_MESSAGE 0x3#define OPERATION_LOG_NON_IMMEDIATE_MESSAGE 0x4Each of them shows a different type of message, and the last one shows that a bunch buffer is accumulated in this buffer. This message tracing is designed to send any kind of the buffer from both vmx root and OS to the user-mode, so it’s not limited just to sending messages, we can send buffers with custom structures and different Operation Numbers.The last thing about our message tracing is, it can be configured with the following constants, you can change them in order to have a better performance for your exclusive use.// Default buffer size#define MaximumPacketsCapacity 1000 // number of packets#define PacketChunkSize 1000 // NOTE : REMEMBER TO CHANGE IT IN USER-MODE APP TOO#define UsermodeBufferSize sizeof(UINT32) + PacketChunkSize + 1 /* Becausee of Opeation code at the start of the buffer + 1 for null-termminating */#define LogBufferSize MaximumPacketsCapacity * (PacketChunkSize + sizeof(BUFFER_HEADER))You can configure things like the maximum number of chunks in a buffer and also the size of each chunk. Setting the above variables is necessary in some cases if there is no thread to consume (read) these chunks and pools are full; it replaces the previous unread buffer. Hence, if you can’t frequently consume the pools, then it’s better to specify a higher number for MaximumPacketsCapacity so that you won’t lose anything.Initialization PhaseIn the initialization phase, we allocate space for the above structure (2 times, one for vmx non-root and one for vmx-root) and then allocate the buffers to be used as the storage for saving our messages.We have to zero them all and also KeInitializeSpinLock to initialize the spinlock. We use this spinlock only for vmx non-root, and this function makes sure that the value for the lock is unset. We do the same for our custom spinlock (VmxRootLoggingLock), just unset it.You might ask, what is the “BufferLockForNonImmMessage”, it’s another lock that will use it as optimization (see later).All in all, we have the following code./* Initialize the buffer relating to log message tracing */BOOLEAN LogInitialize() { // Initialize buffers for trace message and data messages (wee have two buffers one for vmx root and one for vmx non-root) MessageBufferInformation = ExAllocatePoolWithTag(NonPagedPool, sizeof(LOG_BUFFER_INFORMATION) * 2, POOLTAG); if (!MessageBufferInformation) { return FALSE; //STATUS_INSUFFICIENT_RESOURCES } // Zeroing the memory RtlZeroMemory(MessageBufferInformation, sizeof(LOG_BUFFER_INFORMATION) * 2); // Initialize the lock for Vmx-root mode (HIGH_IRQL Spinlock) VmxRootLoggingLock = 0; // Allocate buffer for messages and initialize the core buffer information for (int i = 0; i &lt; 2; i++) { // initialize the lock // Actually, only the 0th buffer use this spinlock but let initialize it for both but the second buffer spinlock is useless // as we use our custom spinlock. KeInitializeSpinLock(&amp;MessageBufferInformation[i].BufferLock); KeInitializeSpinLock(&amp;MessageBufferInformation[i].BufferLockForNonImmMessage); // allocate the buffer MessageBufferInformation[i].BufferStartAddress = ExAllocatePoolWithTag(NonPagedPool, LogBufferSize, POOLTAG); MessageBufferInformation[i].BufferForMultipleNonImmediateMessage = ExAllocatePoolWithTag(NonPagedPool, PacketChunkSize, POOLTAG); if (!MessageBufferInformation[i].BufferStartAddress) { return FALSE; // STATUS_INSUFFICIENT_RESOURCES } // Zeroing the buffer RtlZeroMemory(MessageBufferInformation[i].BufferStartAddress, LogBufferSize); // Set the end address MessageBufferInformation[i].BufferEndAddress = (UINT64)MessageBufferInformation[i].BufferStartAddress + LogBufferSize; }}Sending Phase (Saving Buffer and adding them to pools)In a regular Windows routine generally, we shouldn’t be on IRQL more than Dispatch Level. There is no case that our log manager needs to be used in higher IRQLs, so we don’t care about them; thus, we have two different approaches here. First, we acquire the lock (spinlock) using KeAcquireSpinLock in vmx non-root as it’s a Windows optimized way to acquire a lock and for vmx-root mode, we acquire the lock using our previously designed spinlock.As I told you above, we want to fix this problem that might a vmx-exit occurs when we acquired a lock, so it’s not possible to use the same spinlock as deadlock might happen.Now we have to see whether we are operating from vmx non-root or vmx root, based on this condition, we select our lock and the index of the buffer that we want to put our message into it.I’m not gonna explain each step, as it’s easy, it’s just managing buffer and copying data from a buffer to another buffer and also the code is well commented so you can read the code, instead, I explain tricky parts of our message tracing.After creating a header for our new message buffer, we will copy the bytes and change the information about buffer’s indexes. The last step here is to see whether any thread is waiting to receive our message or not.If there is no thread waiting for our message then nothing more to do here but if there is a thread which is IRP Pending state (I explain about it later), then we use KeInsertQueueDpc so that it will be added to our DPC Queue which will be subsequently executed by Windows in IRQL == DISPATCH_LEVEL.It means that our callback function will execute by Windows later and of course, Windows execute our function in vmx non-root so it’s safe. I’ll describe this callback and how we create a DPC later.Finally, we have to release the locks so that other threads can enter./* Save buffer to the pool */BOOLEAN LogSendBuffer(UINT32 OperationCode, PVOID Buffer, UINT32 BufferLength){ KIRQL OldIRQL; UINT32 Index; BOOLEAN IsVmxRoot; if (BufferLength &gt; PacketChunkSize - 1 || BufferLength == 0) { // We can't save this huge buffer return FALSE; } // Check that if we're in vmx root-mode IsVmxRoot = GuestState[KeGetCurrentProcessorNumber()].IsOnVmxRootMode; // Check if we're in Vmx-root, if it is then we use our customized HIGH_IRQL Spinlock, if not we use the windows spinlock if (IsVmxRoot) { // Set the index Index = 1; SpinlockLock(&amp;VmxRootLoggingLock); } else { // Set the index Index = 0; // Acquire the lock KeAcquireSpinLock(&amp;MessageBufferInformation[Index].BufferLock, &amp;OldIRQL); } // check if the buffer is filled to it's maximum index or not if (MessageBufferInformation[Index].CurrentIndexToWrite &gt; MaximumPacketsCapacity - 1) { // start from the begining MessageBufferInformation[Index].CurrentIndexToWrite = 0; } // Compute the start of the buffer header BUFFER_HEADER* Header = (BUFFER_HEADER*)((UINT64)MessageBufferInformation[Index].BufferStartAddress + (MessageBufferInformation[Index].CurrentIndexToWrite * (PacketChunkSize + sizeof(BUFFER_HEADER)))); // Set the header Header-&gt;OpeationNumber = OperationCode; Header-&gt;BufferLength = BufferLength; Header-&gt;Valid = TRUE; /* Now it's time to fill the buffer */ // compute the saving index PVOID SavingBuffer = ((UINT64)MessageBufferInformation[Index].BufferStartAddress + (MessageBufferInformation[Index].CurrentIndexToWrite * (PacketChunkSize + sizeof(BUFFER_HEADER))) + sizeof(BUFFER_HEADER)); // Copy the buffer RtlCopyBytes(SavingBuffer, Buffer, BufferLength); // Increment the next index to write MessageBufferInformation[Index].CurrentIndexToWrite = MessageBufferInformation[Index].CurrentIndexToWrite + 1; // check if there is any thread in IRP Pending state, so we can complete their request if (GlobalNotifyRecord != NULL) { /* there is some threads that needs to be completed */ // set the target pool GlobalNotifyRecord-&gt;CheckVmxRootMessagePool = IsVmxRoot; // Insert dpc to queue KeInsertQueueDpc(&amp;GlobalNotifyRecord-&gt;Dpc, GlobalNotifyRecord, NULL); // set notify routine to null GlobalNotifyRecord = NULL; } // Check if we're in Vmx-root, if it is then we use our customized HIGH_IRQL Spinlock, if not we use the windows spinlock if (IsVmxRoot) { SpinlockUnlock(&amp;VmxRootLoggingLock); } else { // Release the lock KeReleaseSpinLock(&amp;MessageBufferInformation[Index].BufferLock, OldIRQL); }}Reading Phase (Read buffers and send them to user-mode)It’s time to read the previously filled buffer! The fact that we add a DPC in the previous function “LogSendBuffer” shows that the “LogReadBuffer” is executed in vmx non-root mode so we can freely use most of the APIs (not all of them).Theoretically, we have a problem here, if we want to read a buffer from the vmx root-mode pool, then it might cause a deadlock as we acquired a vmx root-mode lock and might a vm-exit occur. Hence, we spin on this lock in vmx root mode forever, but practically there is no deadlock here. Can you guess why?It’s because our LogReadBuffer executes in DISPATCH_LEVEL so the Windows scheduler won’t interrupt us, and our function is executed without any interruption and the fact that we’re not doing anything fancy here. I mean, we’re not performing anything (like CPUID) that causes a vm-exit in our code, so practically there is nothing to cause deadlock here, but we should keep in mind that we’re not allowed to run codes that cause vmx-exit.We compute the header address based on previous information and also set the valid bit to zero so that it shows that this buffer is previously used.Then we copy the buffer to the buffer that specified in arguments also put the Operation Number on the top of the target buffer so that the future functions will know about the intention of this buffer. We can also use DbgPrint to show the messages to the kernel debugger. Using DbgPrint in DISPATCH_LEVEL (vmx non-root mode) is safe. We might need to use DbgPrint multiple times as this function has a maximum of 512 bytes by default. Even though you can change the limit number but we assume the default size is selected.Finally, we have to reset some of the information regarding buffer, clear the buffer messages (it’s not necessary to zero the buffer, but for making debug process easier, I prefer to zero the buffer), and release the locks./* return of this function shows whether the read was successfull or not (e.g FALSE shows there's no new buffer available.)*/BOOLEAN LogReadBuffer(BOOLEAN IsVmxRoot, PVOID BufferToSaveMessage, UINT32* ReturnedLength) { KIRQL OldIRQL; UINT32 Index; // Check if we're in Vmx-root, if it is then we use our customized HIGH_IRQL Spinlock, if not we use the windows spinlock if (IsVmxRoot) { // Set the index Index = 1; // Acquire the lock SpinlockLock(&amp;VmxRootLoggingLock); } else { // Set the index Index = 0; // Acquire the lock KeAcquireSpinLock(&amp;MessageBufferInformation[Index].BufferLock, &amp;OldIRQL); } // Compute the current buffer to read BUFFER_HEADER* Header = (BUFFER_HEADER*)((UINT64)MessageBufferInformation[Index].BufferStartAddress + (MessageBufferInformation[Index].CurrentIndexToSend * (PacketChunkSize + sizeof(BUFFER_HEADER)))); if (!Header-&gt;Valid) { // there is nothing to send return FALSE; } /* If we reached here, means that there is sth to send */ // First copy the header RtlCopyBytes(BufferToSaveMessage, &amp;Header-&gt;OpeationNumber, sizeof(UINT32)); // Second, save the buffer contents PVOID SendingBuffer = ((UINT64)MessageBufferInformation[Index].BufferStartAddress + (MessageBufferInformation[Index].CurrentIndexToSend * (PacketChunkSize + sizeof(BUFFER_HEADER))) + sizeof(BUFFER_HEADER)); PVOID SavingAddress = ((UINT64)BufferToSaveMessage + sizeof(UINT32)); // Because we want to pass the header of usermode header RtlCopyBytes(SavingAddress, SendingBuffer, Header-&gt;BufferLength);#if ShowMessagesOnDebugger // Means that show just messages if (Header-&gt;OpeationNumber &lt;= OPERATION_LOG_NON_IMMEDIATE_MESSAGE) { /* We're in Dpc level here so it's safe to use DbgPrint*/ // DbgPrint limitation is 512 Byte if (Header-&gt;BufferLength &gt; DbgPrintLimitation) { for (size_t i = 0; i &lt;= Header-&gt;BufferLength / DbgPrintLimitation; i++) { if (i != 0) { DbgPrint("%s", (char*)((UINT64)SendingBuffer + (DbgPrintLimitation * i) - 2)); } else { DbgPrint("%s", (char*)((UINT64)SendingBuffer + (DbgPrintLimitation * i))); } } } else { DbgPrint("%s", (char*)SendingBuffer); } }#endif // Finally, set the current index to invalid as we sent it Header-&gt;Valid = FALSE; // Set the length to show as the ReturnedByted in usermode ioctl funtion + size of header *ReturnedLength = Header-&gt;BufferLength + sizeof(UINT32); // Last step is to clear the current buffer (we can't do it once when CurrentIndexToSend is zero because // there might be multiple messages on the start of the queue that didn't read yet) // we don't free the header RtlZeroMemory(SendingBuffer, Header-&gt;BufferLength); // Check to see whether we passed the index or not if (MessageBufferInformation[Index].CurrentIndexToSend &gt; MaximumPacketsCapacity - 2) { MessageBufferInformation[Index].CurrentIndexToSend = 0; } else { // Increment the next index to read MessageBufferInformation[Index].CurrentIndexToSend = MessageBufferInformation[Index].CurrentIndexToSend + 1; } // Check if we're in Vmx-root, if it is then we use our customized HIGH_IRQL Spinlock, if not we use the windows spinlock if (IsVmxRoot) { SpinlockUnlock(&amp;VmxRootLoggingLock); } else { // Release the lock KeReleaseSpinLock(&amp;MessageBufferInformation[Index].BufferLock, OldIRQL); }}Checking for new messagesChecking for the new message is simple; we just need to check the current message index based on previous information and see if its header is valid or not. If it’s valid then it shows that we have a new message, but if it’s not valid, then some function reads the message previously, and there is no new message.For checking the new message, we even don’t need to acquire a lock because basically we don’t write anything and in our case reading doesn’t need a lock./* return of this function shows whether the read was successfull or not (e.g FALSE shows there's no new buffer available.)*/BOOLEAN LogCheckForNewMessage(BOOLEAN IsVmxRoot) { KIRQL OldIRQL; UINT32 Index; if (IsVmxRoot) { Index = 1; } else { Index = 0; } // Compute the current buffer to read BUFFER_HEADER* Header = (BUFFER_HEADER*)((UINT64)MessageBufferInformation[Index].BufferStartAddress + (MessageBufferInformation[Index].CurrentIndexToSend * (PacketChunkSize + sizeof(BUFFER_HEADER)))); if (!Header-&gt;Valid) { // there is nothing to send return FALSE; } /* If we reached here, means that there is sth to send */ return TRUE;}Sending messages to poolsPreviously, we see how to save (send) buffers and read them. Each message is a buffer of strings, so finally, we have to use “LogSendBuffer” to send our buffer, but we need to consider extra effort to send a well-formed message.va_start and va_end are used to support multiple arguments to one function, e.g like DbgPrint or printf.You can use a combination of KeQuerySystemTime, ExSystemTimeToLocalTime, and RtlTimeToTimeFields to get the current system time (see the example) then putting them together with sprintf_s.There is a particular reason why we use the sprintf-like function instead of RtlString* functions; the reason is described in the Discussion section. The next step is computing length using strnlen_s.Finally, we have a vital optimization here; logically we create two kinds of messages, one called “Immediate Message” which we will directly send it into the pool and another type is “Non-Immediate Message” which we gather the messages in another buffer and append new messages in that buffer until its capacity is full (we shouldn’t pass the PacketChunkSize limit).Using this way, we don’t send each message to the user-mode separately but instead, we send multiple messages in one buffer to the user-mode. We will gain visible performance improvement. For example with a configuration with PacketChunkSize == 1000 bytes we send 6 messages on a buffer (it’s average basically it depends on each message size) because you probably know that CPU has to do a lot to change its state from kernel-mode to user-mode and also creating new IRP Packet is a heavy task.You can also change the configuration, e.g., increase the PacketChunkSize so that more messages will hold on the temporary buffer, but generally, it delays the time you see the message.Also, we work on a buffer so we need another spinlock here.Putting it all together we have the following code :// Send string messages and tracing for logging and monitoringBOOLEAN LogSendMessageToQueue(UINT32 OperationCode, BOOLEAN IsImmediateMessage, BOOLEAN ShowCurrentSystemTime, const char* Fmt, ...){ BOOLEAN Result; va_list ArgList; size_t WrittenSize; UINT32 Index; KIRQL OldIRQL; BOOLEAN IsVmxRootMode; int SprintfResult; char LogMessage[PacketChunkSize]; char TempMessage[PacketChunkSize]; char TimeBuffer[20] = { 0 }; // Set Vmx State IsVmxRootMode = GuestState[KeGetCurrentProcessorNumber()].IsOnVmxRootMode; if (ShowCurrentSystemTime) { // It's actually not necessary to use -1 but because user-mode code might assume a null-terminated buffer so // it's better to use - 1 va_start(ArgList, Fmt); // We won't use this because we can't use in any IRQL /*Status = RtlStringCchVPrintfA(TempMessage, PacketChunkSize - 1, Fmt, ArgList);*/ SprintfResult = vsprintf_s(TempMessage, PacketChunkSize - 1, Fmt, ArgList); va_end(ArgList); // Check if the buffer passed the limit if (SprintfResult == -1) { // Probably the buffer is large that we can't store it return FALSE; } // Fill the above with timer TIME_FIELDS TimeFields; LARGE_INTEGER SystemTime, LocalTime; KeQuerySystemTime(&amp;SystemTime); ExSystemTimeToLocalTime(&amp;SystemTime, &amp;LocalTime); RtlTimeToTimeFields(&amp;LocalTime, &amp;TimeFields); // We won't use this because we can't use in any IRQL /*Status = RtlStringCchPrintfA(TimeBuffer, RTL_NUMBER_OF(TimeBuffer), "%02hd:%02hd:%02hd.%03hd", TimeFields.Hour, TimeFields.Minute, TimeFields.Second, TimeFields.Milliseconds); // Append time with previous message Status = RtlStringCchPrintfA(LogMessage, PacketChunkSize - 1, "(%s)\\t %s", TimeBuffer, TempMessage);*/ // this function probably run without error, so there is no need to check the return value sprintf_s(TimeBuffer, RTL_NUMBER_OF(TimeBuffer), "%02hd:%02hd:%02hd.%03hd", TimeFields.Hour, TimeFields.Minute, TimeFields.Second, TimeFields.Milliseconds); // Append time with previous message SprintfResult = sprintf_s(LogMessage, PacketChunkSize - 1, "(%s - core : %d - vmx-root? %s)\\t %s", TimeBuffer, KeGetCurrentProcessorNumberEx(0), IsVmxRootMode ? "yes" : "no", TempMessage); // Check if the buffer passed the limit if (SprintfResult == -1) { // Probably the buffer is large that we can't store it return FALSE; } } else { // It's actually not necessary to use -1 but because user-mode code might assume a null-terminated buffer so // it's better to use - 1 va_start(ArgList, Fmt); // We won't use this because we can't use in any IRQL /* Status = RtlStringCchVPrintfA(LogMessage, PacketChunkSize - 1, Fmt, ArgList); */ SprintfResult = vsprintf_s(LogMessage, PacketChunkSize - 1, Fmt, ArgList); va_end(ArgList); // Check if the buffer passed the limit if (SprintfResult == -1) { // Probably the buffer is large that we can't store it return FALSE; } } // Use std function because they can be run in any IRQL // RtlStringCchLengthA(LogMessage, PacketChunkSize - 1, &amp;WrittenSize); WrittenSize = strnlen_s(LogMessage, PacketChunkSize - 1); if (LogMessage[0] == '\\0') { // nothing to write DbgBreakPoint(); return FALSE; } if (IsImmediateMessage) { return LogSendBuffer(OperationCode, LogMessage, WrittenSize); } else { // Check if we're in Vmx-root, if it is then we use our customized HIGH_IRQL Spinlock, if not we use the windows spinlock if (IsVmxRootMode) { // Set the index Index = 1; SpinlockLock(&amp;VmxRootLoggingLockForNonImmBuffers); } else { // Set the index Index = 0; // Acquire the lock KeAcquireSpinLock(&amp;MessageBufferInformation[Index].BufferLockForNonImmMessage, &amp;OldIRQL); } //Set the result to True Result = TRUE; // If log message WrittenSize is above the buffer then we have to send the previous buffer if ((MessageBufferInformation[Index].CurrentLengthOfNonImmBuffer + WrittenSize) &gt; PacketChunkSize - 1 &amp;&amp; MessageBufferInformation[Index].CurrentLengthOfNonImmBuffer != 0) { // Send the previous buffer (non-immediate message) Result = LogSendBuffer(OPERATION_LOG_NON_IMMEDIATE_MESSAGE, MessageBufferInformation[Index].BufferForMultipleNonImmediateMessage, MessageBufferInformation[Index].CurrentLengthOfNonImmBuffer); // Free the immediate buffer MessageBufferInformation[Index].CurrentLengthOfNonImmBuffer = 0; RtlZeroMemory(MessageBufferInformation[Index].BufferForMultipleNonImmediateMessage, PacketChunkSize); } // We have to save the message RtlCopyBytes(MessageBufferInformation[Index].BufferForMultipleNonImmediateMessage + MessageBufferInformation[Index].CurrentLengthOfNonImmBuffer, LogMessage, WrittenSize); // add the length MessageBufferInformation[Index].CurrentLengthOfNonImmBuffer += WrittenSize; // Check if we're in Vmx-root, if it is then we use our customized HIGH_IRQL Spinlock, if not we use the windows spinlock if (IsVmxRootMode) { SpinlockUnlock(&amp;VmxRootLoggingLockForNonImmBuffers); } else { // Release the lock KeReleaseSpinLock(&amp;MessageBufferInformation[Index].BufferLockForNonImmMessage, OldIRQL); } return Result; }}Receiving buffers and messages in user-modeReceiving buffers from the user-mode is done by using an IOCTL. First, we create another thread in our user-mode application. This thread is responsible for bringing the kernel-mode buffers to the user-mode and then operate based on Operation Number. HANDLE Thread = CreateThread(NULL, 0, ThreadFunc, Handle, 0, NULL); if (Thread) { printf("[*] Thread Created successfully !!!"); }This thread executes the following function. We use IRP Pending for transferring data from kernel-mode to user-mode. IRP Pending is primarily used for transferring a packet. For example, you send an IRP packet to the kernel, and kernel marks this packet as Pending. Whenever the user-mode buffer is available to send to the user-mode, the kernel completes the IRP request, and the IOCTL function returns to the user-mode and continues the execution.It’s somehow like when you use Wait for an object. We can also use events in Windows and whenever the buffer is available the event is triggered but IRP Pending is better as it designed for the purpose of sending messages to user-mode.What we have to do is allocating a buffer for kernel-mode code and using DeviceIoControl to request the packet. When the packet from the kernel received, we process the packet and switch through the Operation Number.void ReadIrpBasedBuffer(HANDLE Device) { BOOL Status; ULONG ReturnedLength; REGISTER_EVENT RegisterEvent; UINT32 OperationCode; printf(" =============================== Kernel-Mode Logs (Driver) ===============================\\n"); RegisterEvent.hEvent = NULL; RegisterEvent.Type = IRP_BASED; char OutputBuffer[UsermodeBufferSize + 100] = { 0 }; try { while (TRUE) { ZeroMemory(OutputBuffer, UsermodeBufferSize); Sleep(200); // we're not trying to eat all of the CPU ;) Status = DeviceIoControl( Device, // Handle to device IOCTL_REGISTER_EVENT, // IO Control code &amp;RegisterEvent, // Input Buffer to driver. SIZEOF_REGISTER_EVENT * 2, // Length of input buffer in bytes. (x 2 is bcuz as the driver is x64 and has 64 bit values) OutputBuffer, // Output Buffer from driver. sizeof(OutputBuffer), // Length of output buffer in bytes. &amp;ReturnedLength, // Bytes placed in buffer. NULL // synchronous call ); if (!Status) { printf("Ioctl failed with code %d\\n", GetLastError()); break; } printf("\\n========================= Kernel Mode (Buffer) =========================\\n"); OperationCode = 0; memcpy(&amp;OperationCode, OutputBuffer, sizeof(UINT32)); printf("Returned Length : 0x%x \\n", ReturnedLength); printf("Operation Code : 0x%x \\n", OperationCode); switch (OperationCode) { case OPERATION_LOG_NON_IMMEDIATE_MESSAGE: printf("A buffer of messages (OPERATION_LOG_NON_IMMEDIATE_MESSAGE) :\\n"); printf("%s", OutputBuffer + sizeof(UINT32)); break; case OPERATION_LOG_INFO_MESSAGE: printf("Information log (OPERATION_LOG_INFO_MESSAGE) :\\n"); printf("%s", OutputBuffer + sizeof(UINT32)); break; case OPERATION_LOG_ERROR_MESSAGE: printf("Error log (OPERATION_LOG_ERROR_MESSAGE) :\\n"); printf("%s", OutputBuffer + sizeof(UINT32)); break; case OPERATION_LOG_WARNING_MESSAGE: printf("Warning log (OPERATION_LOG_WARNING_MESSAGE) :\\n"); printf("%s", OutputBuffer + sizeof(UINT32)); break; default: break; } printf("\\n========================================================================\\n"); } } catch (const std::exception&amp;) { printf("\\n Exception !\\n"); }}IOCTL and managing user-mode requestsWhen the IOCTL arrived on the kernel side, DrvDispatchIoControl from major functions is called. This function returns a pointer to the caller’s I/O stack location in the specified IRP.From the IRP Stack we can read the IOCTL code and buffers address, this time we perform necessary checks and pass the arguments to LogRegisterIrpBasedNotification./* Driver IOCTL Dispatcher*/NTSTATUS DrvDispatchIoControl(PDEVICE_OBJECT DeviceObject, PIRP Irp){ PIO_STACK_LOCATION IrpStack; PREGISTER_EVENT RegisterEvent; NTSTATUS Status; IrpStack = IoGetCurrentIrpStackLocation(Irp); switch (IrpStack-&gt;Parameters.DeviceIoControl.IoControlCode) { case IOCTL_REGISTER_EVENT: // First validate the parameters. if (IrpStack-&gt;Parameters.DeviceIoControl.InputBufferLength &lt; SIZEOF_REGISTER_EVENT || Irp-&gt;AssociatedIrp.SystemBuffer == NULL) { Status = STATUS_INVALID_PARAMETER; DbgBreakPoint(); break; } RegisterEvent = (PREGISTER_EVENT)Irp-&gt;AssociatedIrp.SystemBuffer; switch (RegisterEvent-&gt;Type) { case IRP_BASED: Status = LogRegisterIrpBasedNotification(DeviceObject, Irp); break; case EVENT_BASED: Status = LogRegisterEventBasedNotification(DeviceObject, Irp); break; default: ASSERTMSG("\\tUnknow notification type from user-mode\\n", FALSE); Status = STATUS_INVALID_PARAMETER; break; } break; default: ASSERT(FALSE); // should never hit this Status = STATUS_NOT_IMPLEMENTED; break; } if (Status != STATUS_PENDING) { Irp-&gt;IoStatus.Status = Status; Irp-&gt;IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); } return Status;}To register an IRP notification, first, we check whether any other thread is pending by checking GlobalNotifyRecord if there is any thread we complete the IRP and return to the user-mode because in our design we ignore multiple threads that request the buffers means that only one thread can read the kernel-mode buffer.Second, we initialize a custom structure that describes the state. The following structure is responsible for saving Type, DPC Object, and target buffer.typedef struct _NOTIFY_RECORD { NOTIFY_TYPE Type; union { PKEVENT Event; PIRP PendingIrp; } Message; KDPC Dpc; BOOLEAN CheckVmxRootMessagePool; // Set so that notify callback can understand where to check (Vmx root or Vmx non-root)} NOTIFY_RECORD, * PNOTIFY_RECORD;In order to fill this structure, we initialize a DPC object by calling KeInitializeDpc, this function gets the function callback that should be called later (LogNotifyUsermodeCallback) and the parameter(s) to this function (NotifyRecord).We first check the vmx non-root pools to see if anything new is available. Otherwise, we check the vmx-root mode buffer. This precedence is because vmx non-root buffers are more important. After all, we spent must of the time in VMX Root-mode, so we might see thousands of messages from vmx-root while we have fewer messages from vmx non-root. If we check the vmx root message buffer first, then we might lose some messages from vmx non-root or never find a time to process them.If any new message is available then we directly add a DPC to the queue (KeInsertQueueDpc).If there isn’t any new message available, then we simply save our Notify Record for future use, and also we mark IRP to pending state using IoMarkIrpPending and return STATUS_PENDING./* Register a new IRP Pending thread which listens for new buffers */NTSTATUS LogRegisterIrpBasedNotification(PDEVICE_OBJECT DeviceObject, PIRP Irp){ PNOTIFY_RECORD NotifyRecord; PIO_STACK_LOCATION IrpStack; KIRQL OOldIrql; PREGISTER_EVENT RegisterEvent; // check if current core has another thread with pending IRP, if no then put the current thread to pending // otherwise return and complete thread with STATUS_SUCCESS as there is another thread waiting for message if (GlobalNotifyRecord == NULL) { IrpStack = IoGetCurrentIrpStackLocation(Irp); RegisterEvent = (PREGISTER_EVENT)Irp-&gt;AssociatedIrp.SystemBuffer; // Allocate a record and save all the event context. NotifyRecord = ExAllocatePoolWithQuotaTag(NonPagedPool, sizeof(NOTIFY_RECORD), POOLTAG); if (NULL == NotifyRecord) { return STATUS_INSUFFICIENT_RESOURCES; } NotifyRecord-&gt;Type = IRP_BASED; NotifyRecord-&gt;Message.PendingIrp = Irp; KeInitializeDpc(&amp;NotifyRecord-&gt;Dpc, // Dpc LogNotifyUsermodeCallback, // DeferredRoutine NotifyRecord // DeferredContext ); IoMarkIrpPending(Irp); // check for new message (for both Vmx-root mode or Vmx non root-mode) if (LogCheckForNewMessage(FALSE)) { // check vmx root NotifyRecord-&gt;CheckVmxRootMessagePool = FALSE; // Insert dpc to queue KeInsertQueueDpc(&amp;NotifyRecord-&gt;Dpc, NotifyRecord, NULL); } else if (LogCheckForNewMessage(TRUE)) { // check vmx non-root NotifyRecord-&gt;CheckVmxRootMessagePool = TRUE; // Insert dpc to queue KeInsertQueueDpc(&amp;NotifyRecord-&gt;Dpc, NotifyRecord, NULL); } else { // Set the notify routine to the global structure GlobalNotifyRecord = NotifyRecord; } // We will return pending as we have marked the IRP pending. return STATUS_PENDING; } else { return STATUS_SUCCESS; }}Usermode notify callbackAs you see in the above codes, we add DPCs to queue in two functions (LogRegisterIrpBasedNotification and LogSendBuffer). This way, we won’t miss anything, and everything is processed as a message is generated. For example, if there is any thread waiting for the message then LogSendBuffer notifies it about the new message, if there isn’t any thread waiting for the message then LogSendBuffer can’t do anything, as long as a new thread comes to the kernel then it checks for the new message. Think about it one more time. It’s beautiful.Now it’s time to read the packets from kernel pools and send them to the user-mode.When LogNotifyUsermodeCallback is called then we sure that we’re in DISPATCH_LEVEL and vmx non-root mode.In this function, we check if the parameters sent to the kernel are valid or not. It’s because the user-mode provides them. For example, we check the IRP stack’s Parameters. DeviceIoControl. InputBufferLength and Parameters. DeviceIoControl. OutputBufferLength to make sure they are not null or check whether the SystemBuffer is null or not.Then we call LogReadBuffer with user-mode buffers, so this function will fill the user-mode buffer and adds the Operation Number in a suitable place. Also, Irp-&gt;IoStatus.Information provides the buffer length to the user-mode.The last step here is to complete the IRP, so I/O Manager sends the results to the user-mode, and the thread can continue to its normal life.The reason why we access the user-mode buffer in all processes (because DPCs might run on the random user-mode process) and why we use DPCs and don’t use other things like APCs is discussed in the Discussion section.The following code demonstrates what we talked about it above./* Complete the IRP in IRP Pending state and fill the usermode buffers with pool data */VOID LogNotifyUsermodeCallback(PKDPC Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2){ PNOTIFY_RECORD NotifyRecord; PIRP Irp; UINT32 Length; UNREFERENCED_PARAMETER(Dpc); UNREFERENCED_PARAMETER(SystemArgument1); UNREFERENCED_PARAMETER(SystemArgument2); NotifyRecord = DeferredContext; ASSERT(NotifyRecord != NULL); // can't be NULL _Analysis_assume_(NotifyRecord != NULL); switch (NotifyRecord-&gt;Type) { case IRP_BASED: Irp = NotifyRecord-&gt;Message.PendingIrp; if (Irp != NULL) { PCHAR OutBuff; // pointer to output buffer ULONG InBuffLength; // Input buffer length ULONG OutBuffLength; // Output buffer length PIO_STACK_LOCATION IrpSp; // Make suree that concurrent calls to notify function never occurs if (!(Irp-&gt;CurrentLocation &lt;= Irp-&gt;StackCount + 1)) { DbgBreakPoint(); return; } IrpSp = IoGetCurrentIrpStackLocation(Irp); InBuffLength = IrpSp-&gt;Parameters.DeviceIoControl.InputBufferLength; OutBuffLength = IrpSp-&gt;Parameters.DeviceIoControl.OutputBufferLength; if (!InBuffLength || !OutBuffLength) { Irp-&gt;IoStatus.Status = STATUS_INVALID_PARAMETER; IoCompleteRequest(Irp, IO_NO_INCREMENT); break; } // Check again that SystemBuffer is not null if (!Irp-&gt;AssociatedIrp.SystemBuffer) { // Buffer is invalid return; } OutBuff = Irp-&gt;AssociatedIrp.SystemBuffer; Length = 0; // Read Buffer might be empty (nothing to send) if (!LogReadBuffer(NotifyRecord-&gt;CheckVmxRootMessagePool, OutBuff, &amp;Length)) { // we have to return here as there is nothing to send here return; } Irp-&gt;IoStatus.Information = Length; Irp-&gt;IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); } break; case EVENT_BASED: // Signal the Event created in user-mode. KeSetEvent(NotifyRecord-&gt;Message.Event, 0, FALSE); // Dereference the object as we are done with it. ObDereferenceObject(NotifyRecord-&gt;Message.Event); break; default: ASSERT(FALSE); break; } if (NotifyRecord != NULL) { ExFreePoolWithTag(NotifyRecord, POOLTAG); }}Uninitialization PhaseNothing special, we just de-allocate the previously allocated buffers. Keep in mind that we should initialize the message tracer at the very first function of our driver so we can use it and, of course, uninitialize it at the end when we don’t have any message anymore./* Uninitialize the buffer relating to log message tracing */VOID LogUnInitialize(){ // de-allocate buffer for messages and initialize the core buffer information (for vmx-root core) for (int i = 0; i &lt; 2; i++) { // Free each buffers ExFreePoolWithTag(MessageBufferInformation[i].BufferStartAddress, POOLTAG); ExFreePoolWithTag(MessageBufferInformation[i].BufferForMultipleNonImmediateMessage, POOLTAG); } // de-allocate buffers for trace message and data messages ExFreePoolWithTag(MessageBufferInformation, POOLTAG);}WPP TracingWPP Tracing is another mechanism provided by Windows, which can be used to trace messages from both vmx non-root and vmx root-mode and in any IRQL. It is primarily intended for debugging code during development, and it’s capable of publishing events that can be consumed by applications in structured ETW events.Logging messages with WPP software tracing is similar to using Windows event logging services. The driver logs a message ID and unformatted binary data in a log file. Subsequently, a postprocessor converts the information in the log file to a human-readable form.In order to use WPP Tracing, first, we should configure our driver to use WPP Tracing as the message tracing by setting UseWPPTracing to TRUE. By default it’s FALSE.// Use WPP Tracing instead of all logging functions#define UseWPPTracing TRUEThen we go to our project’s properties and set Run Wpp Tracing to Yes and also add a custom function for sending messages by setting Function To Generate Trace Messages to HypervisorTraceLevelMessage (LEVEL,FLAGS,MSG,…).Then we need to generate a unique GUID for our driver by using Visual Studio’s Tools -&gt; Create GUID and generate one and put it into the following format.#define WPP_CONTROL_GUIDS \\ WPP_DEFINE_CONTROL_GUID( \\ HypervisorFromScratchLogger, (2AE39766,AE4B,46AB,AFC4,002DB8109721), \\ WPP_DEFINE_BIT(HVFS_LOG) /* bit 0 = 0x00000001 */ \\ WPP_DEFINE_BIT(HVFS_LOG_INFO) /* bit 1 = 0x00000002 */ \\ WPP_DEFINE_BIT(HVFS_LOG_WARNING) /* bit 2 = 0x00000004 */ \\ WPP_DEFINE_BIT(HVFS_LOG_ERROR) /* bit 3 = 0x00000008 */ \\ ) WPP_DEFINE_BIT creates some specific events for our messages that can be used in the future for masking specific events.After all the above code, we initialize the WPP Tracing by adding the following code at the very first line of the code, e.g., DriverEntry. // Initialize WPP Tracing WPP_INIT_TRACING(DriverObject, RegistryPath);At last we clean up and set WPP Tracing to off by using the following code to Driver Unload function. // Stop the tracing WPP_CLEANUP(DriverObject);For making things easy, I add the following codes to our previous message tracing code, which means that instead of sending the buffers into our custom message tracing buffer, we’ll send it to WPP Tracing buffer. if (OperationCode == OPERATION_LOG_INFO_MESSAGE) { HypervisorTraceLevelMessage( TRACE_LEVEL_INFORMATION, // ETW Level defined in evntrace.h HVFS_LOG_INFO, "%s",// Flag defined in WPP_CONTROL_GUIDS LogMessage); } else if (OperationCode == OPERATION_LOG_WARNING_MESSAGE) { HypervisorTraceLevelMessage( TRACE_LEVEL_WARNING, // ETW Level defined in evntrace.h HVFS_LOG_WARNING, "%s",// Flag defined in WPP_CONTROL_GUIDS LogMessage); } else if (OperationCode == OPERATION_LOG_ERROR_MESSAGE) { HypervisorTraceLevelMessage( TRACE_LEVEL_ERROR, // ETW Level defined in evntrace.h HVFS_LOG_ERROR, "%s",// Flag defined in WPP_CONTROL_GUIDS LogMessage); } else { HypervisorTraceLevelMessage( TRACE_LEVEL_NONE, // ETW Level defined in evntrace.h HVFS_LOG, "%s",// Flag defined in WPP_CONTROL_GUIDS LogMessage); }Also, we have to .tmh files. These files are auto-generated by the WPP framework, which contains the required code for trace messages. TMH file name should be the same as the C file, for example, if we are adding the trace message in “Driver.c” then we are supposed to include “Driver.tmh”. We used WPP Tracing APIs in two files, first Driver.c and Logging.c, so we have to include Driver.tmh and Logging.tmh and no need for these files in other project files as long as we gathered everything in one file.The WPP Tracing is complete! In order to see the messages in user-mode, we have to use another application, e.g traceview.Personally, I prefer to use my custom message tracing as WPP Tracing needs to some other application to parse the .pdb file or other files to show the messages, and I didn’t find any good example of parsing messages in an application without using another app.You can see the results of WPP Tracing later in Let’s Test it! section.Supporting to Hyper-VAs I told you in the previous parts, testing and building hypervisor for Hyper-V needs extra consideration and adding a few more lines of code to support Hyper-V nested virtualization.At the time of writing this part, Hyper-V and VMware Workstation are incompatible with each other, which means that if you run Hyper-V you can’t run VMware and a message like this will appear.VMware Workstation and Hyper-V are not compatible. Remove the Hyper-V role from the system before running VMware Workstation.The same is true for VMware, if you run VMware you can’t run Hyper-V and you need to execute a command then restart your computer to use another VMM.In order to use Hyper-V, you should run the following command (as administrator) and then restart your computer.bcdedit /set hypervisorlaunchtype auto And if you want to run VMware, you can run the following command (as administrator) and restart your computer.bcdedit /set hypervisorlaunchtype offEnable Nested VirtualizationIn part 1, there is a section that describes how to enable VMware’s nested virtualization and test your driver. For Hyper-V we have an exact same scenario, first, turn off the target VM then enable nested virtualization for the target virtual machine by running the following command on Powershell:Note that instead of PutYourVmNameHere, put the name of your virtual machine that you want to enable nested virtualization for it.Set-VMProcessor -VMName PutYourVmNameHere -ExposeVirtualizationExtensions $trueAnd if you need to disable it, you can run:Set-VMProcessor -VMName PutYourVmNameHere -ExposeVirtualizationExtensions $falseNow you need to attach your Hyper-V machine to a windbg debugger. There are many ways to do it. You can read here and here (I prefer using kdnet.exe).Now we have the testing environment, it’s time to modify our hypervisor so we can support Hyper-V.Hyper-V’s visible behavior in nested virtualizationHyper-V has some visible behavior for our hypervisor, which means that you should manage some of them that relate to us and give some of them to the Hyper-V as a top-level hypervisor to manage them, you’re confused? Let me explain it one more time.In a nested virtualization environment, you’re not directly getting the vm-exits and all other hypervisor events, instead it’s the top-level hypervisor that gets the vm-exit (in our case Hyper-V is the top-level). Top-level hypervisor calls the vm-exit handler of lower-level hypervisors (our hypervisor is a low-level hypervisor in this case.) now the lower level hypervisor manages the vm-exit (for example it injects an event (interrupt) to be delivered to the guest) after vm-exit finishes it executes VMRESUME, but this instruction won’t directly go to the guest vmx non-root. Instead, it goes to the vm-exit handler of the top-level hypervisor, and now it’s the top-level hypervisor that performs the tasks (In our example, insert event to the guest).So, even our hypervisor is not the first hypervisor that gets the event, but our hypervisor is the first to manage them.On the other hand, Windows kernel is highly integrated to Hyper-V, which means that it uses lots of Hypercalls (Vmcalls) and MSRs to contact with Hyper-V and if the Windows kernel doesn’t get the valid response from Hyper-V then it crashes or halts.As the first hypervisor to manage the vm-exits, we have to inspect vm-exit details to see if the vm-exit relates to us our refers to Hyper-V. In other words, it’s a general vm-exit, or it’s because Windows wants to talk with Hyper-V.OK, let see what we should manage and what we should not.Hyper-V Hypervisor Top-Level Functional Specification (TLFS)The Hyper-V Hypervisor Top-Level Functional Specification (TLFS) describes the hypervisor’s externally visible behavior to other operating system components. This specification is meant to be useful for guest operating system developers.If you want to research Hyper-V, you have to read the documentation about Hyper-V’s TLFS here, but we just want to support Hyper-V. Hence, there is documentation (Requirements for Implementing the Microsoft Hypervisor Interface) that describes the things we should do in order to support Hyper-V. Of course, we’re not going to implement all of them to make our hypervisor work on Hyper-V.Out of Range MSRsIn part 6, I described MSR Bitmaps, if you remember MSR bitmap support MSR index (RCX) between 0x00000000 to 0x00001FFF and 0xC0000000 to 0xC0001FFF. Windows uses other MSRs from 0x40000000 to 0x400000F0 for requesting something or reporting something to vmx-root.You might ask why they don’t use VMCALLs. Of course, they can use VMCALL, but most hypervisors do this. It’s cheaper and predates VMCALLs, and also this range is specifically designed to be used by hypervisors.The reason why it’s cheaper is the same discussion about why use int 2e and not sysenter as the cost of sending data over vmcall and allowing it from ring 0 or ring 3 and deciding things (rdmsr doesn’t need that ring check) and sending data back is greater than a simple MSR interface and can work with legacy compilers and systems too.You can find the definitions of these MSRs here.All in all, I modified our previous MSR handler (both MSR Read - RDMSR and MSR Write - WRMSR to support MSRs between 0x40000000 to 0x400000F0). All we have to do is execute RDMSR or WRMSR in vmx-root mode.You might ask, is it ok to run WRMSR or RDMSR with hardware invalid MSRs?The answer is no! but the reason why we execute it is because we’re are in a nested virtualization environment and it’s not a real vmx-root, physically we’re in vmx non-root mode if that makes sense.In other words, VMware or Hyper-V or any nested virtualization environment calls our vm-exit handler in vmx non-root and pretend that it’s in vmx-root mode, so executing WRMSR or RDMSR causes a real vm-exit to Hyper-V, and that’s how they can handle the actual vm-exit.For example RDMSR handles like this :/* Handles in the cases when RDMSR causes a Vmexit*/VOID HvHandleMsrRead(PGUEST_REGS GuestRegs){ MSR msr = { 0 }; // RDMSR. The RDMSR instruction causes a VM exit if any of the following are true: // // The "use MSR bitmaps" VM-execution control is 0. // The value of ECX is not in the ranges 00000000H - 00001FFFH and C0000000H - C0001FFFH // The value of ECX is in the range 00000000H - 00001FFFH and bit n in read bitmap for low MSRs is 1, // where n is the value of ECX. // The value of ECX is in the range C0000000H - C0001FFFH and bit n in read bitmap for high MSRs is 1, // where n is the value of ECX &amp; 00001FFFH. /* Execute WRMSR or RDMSR on behalf of the guest. Important that this can cause bug check when the guest tries to access unimplemented MSR even within the SEH block* because the below WRMSR or RDMSR raises #GP and are not protected by the SEH block (or cannot be protected either as this code run outside the thread stack region Windows requires to proceed SEH). Hypervisors typically handle this by noop-ing WRMSR and returning zero for RDMSR with non-architecturally defined MSRs. Alternatively, one can probe which MSRs should cause #GP prior to installation of a hypervisor and the hypervisor can emulate the results. */ // Check for sanity of MSR if they're valid or they're for reserved range for WRMSR and RDMSR if ((GuestRegs-&gt;rcx &lt;= 0x00001FFF) || ((0xC0000000 &lt;= GuestRegs-&gt;rcx) &amp;&amp; (GuestRegs-&gt;rcx &lt;= 0xC0001FFF)) || (GuestRegs-&gt;rcx &gt;= RESERVED_MSR_RANGE_LOW &amp;&amp; (GuestRegs-&gt;rcx &lt;= RESERVED_MSR_RANGE_HI))) { msr.Content = __readmsr(GuestRegs-&gt;rcx); } GuestRegs-&gt;rax = msr.Low; GuestRegs-&gt;rdx = msr.High;}Same checks apply to WRMSR too.Hyper-V Hypercalls (VMCALLs)VMCALL is exactly like RDMSR and WRMSR, even though running VMCALL on vmx-root mode has a known behavior (invokes an SMM monitor). Still, in our case, in a nested virtualization environment, it causes a vm-exit to Hyper-V so Hyper-V can manage the hypercall.Hyper-V has the following convention for its VMCALLs (hypercall).As we want to use our hypervisor VMCALLs, a quick and dirty fix for this problem is somehow show the vm-exit handler that our hypervisor routines should manage this VMCALL; thus we put some random hex values to r10, r11, r12 (as these registers are not used in fastcall calling convention, you can choose other registers too) thus we can check for these registers on the vm-exit handler to make sure that this VMCALL relates to our hypervisor.As some of the registers should not be changed due to the Windows x64 fastcall calling convention, we save them to restore them later.Generally, The registers RAX, RCX, RDX, R8, R9, R10, R11 are considered volatile (caller-saved) and registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 are considered nonvolatile (callee-saved). ; We change r10 to HVFS Hex ASCII and r11 to VMCALL Hex ASCII and r12 to NOHYPERV Hex ASCII so we can make sure that the calling Vmcall comes ; from our hypervisor and we're resposible for managing it, otherwise it has to be managed by Hyper-V push r10 push r11 push r12 mov r10, 48564653H ; [HVFS] mov r11, 564d43414c4cH ; [VMCALL] mov r12, 4e4f485950455256H ; [NOHYPERV] vmcall ; VmxVmcallHandler(UINT64 VmcallNumber, UINT64 OptionalParam1, UINT64 OptionalParam2, UINT64 OptionalParam3) pop r12 pop r11 pop r10 ret ; Return type is NTSTATUS and it's on RAX from the previous function, no need to change anythingFor Hyper-V VMCALLs we need to adjust RCX, RDX, R8 as demonstrated in the above picture.AsmHypervVmcall PROC vmcall ; __fastcall Vmcall(rcx = HypercallInputValue, rdx = InputParamGPA, r8 = OutputParamGPA) retAsmHypervVmcall ENDPFinally, in the vm-exit handler, we check for the VMCALL to see if our random values are store in the registers or not. If it’s on those registers, then we call our hypervisor VMCALL handler. Otherwise, we let Hyper-V do whatever it wants to its VMCALLs. case EXIT_REASON_VMCALL: { // Check if it's our routines that request the VMCALL our it relates to Hyper-V if (GuestRegs-&gt;r10 == 0x48564653 &amp;&amp; GuestRegs-&gt;r11 == 0x564d43414c4c &amp;&amp; GuestRegs-&gt;r12 == 0x4e4f485950455256) { // Then we have to manage it as it relates to us GuestRegs-&gt;rax = VmxVmcallHandler(GuestRegs-&gt;rcx, GuestRegs-&gt;rdx, GuestRegs-&gt;r8, GuestRegs-&gt;r9); } else { // Otherwise let the top-level hypervisor to manage it GuestRegs-&gt;rax = AsmHypervVmcall(GuestRegs-&gt;rcx, GuestRegs-&gt;rdx, GuestRegs-&gt;r8); } break; }Hyper-V Interface CPUID LeavesThe last step on supporting Hyper-V is managing CPUID leaves, here are some of the CPUID leaves that we have to manage them.Note that based on the document I mentioned, we have to return non “Hv#1” value. This indicates that our hypervisor does NOT conform to the Microsoft hypervisor interface. else if (RegistersState-&gt;rax == CPUID_HV_VENDOR_AND_MAX_FUNCTIONS) { // Return a maximum supported hypervisor CPUID leaf range and a vendor // ID signature as required by the spec. cpu_info[0] = HYPERV_CPUID_INTERFACE; cpu_info[1] = 'rFvH'; // "[H]yper[v]isor [Fr]o[m] [Scratch] = HvFrmScratch" cpu_info[2] = 'rcSm'; cpu_info[3] = 'hcta'; } else if (RegistersState-&gt;rax == HYPERV_CPUID_INTERFACE) { // Return our interface identifier //cpu_info[0] = 'HVFS'; // [H]yper[V]isor [F]rom [S]cratch // Return non Hv#1 value. This indicate that our hypervisor does NOT // conform to the Microsoft hypervisor interface. cpu_info[0] = '0#vH'; // Hv#0 cpu_info[1] = cpu_info[2] = cpu_info[3] = 0; }By the way, it works without the above modification about CPUID leaves, but it’s better to manage them based on TLFS.One other thing that I noticed during the development on Hyper-V was the fact that we have vm-exits because the guest executes HLT (Halt) instruction, of course, we don’t want to halt the processor so in the case of EXIT_REASON_HLT we simply ignore it.Finished! From now you can test your hypervisor on Hyper-V too : )Fixing Previous Design IssuesIn this part, we want to improve our hypervisor and fix some issues from the previous parts regarding problems and misunderstandings.Fixing the problem with pre-allocated buffersOur previous buffer pre-allocation has 2 problems, It doesn’t allow us to hook page from VMX Root mode, which means that every pool allocation should start from vmx non-root mode. In the process of allocation, we didn’t acquire spinlock so that the processor might interrupt us. Next time we want to continue our execution, there is no allocation as we allocate pools per core.To fix them, we need to design a global pool manager. You can see the pool manager code in “PoolManager.c” and “PoolManager.h”. I’m not gonna describe how it works as it’s pretty clear if you see the source code, but I’ll explain the functionality of this pool manager and how you can use its functions.In this pool manager, instead of allocating core-core specific pre-allocated buffers, we’ll use global pre-allocated buffers with ten pre-allocated buffers ready, each time one of these buffers is used we add a request to pool manager to replace another pool as soon as possible, this way we’ll never run out of pre-allocated pools.Of course, we might run out of the pre-allocated pool if ten requests arrive at the pool manager, but we don’t need such a request and, of course, between them, pool manager gets a chance to re-allocate new pools.Here the functions explanation :BOOLEAN PoolManagerInitialize();Initializes the Pool Manager and pre-allocate some pools.VOID PoolManagerUninitialize();De-allocate all the allocated poolsBOOLEAN PoolManagerCheckAndPerformAllocation();The above function tries to see whether a new pool request is available, if available, then allocates it. It should be called in PASSIVE_LEVEL (vmx non-root mode) because we want paging allocation, and also, the best place to check for it is on IOCTL handler as we call it frequently and it’s PASSIVE_LEVEL and safe.BOOLEAN PoolManagerRequestAllocation(SIZE_T Size, UINT32 Count, POOL_ALLOCATION_INTENTION Intention);If we have requested to allocate a new pool, we can call this function. It stores the requests somewhere in the memory to be allocated when it’s safe (IRQL == PASSIVE_LEVEL).POOL_ALLOCATION_INTENTION is an enum that describes why we need this pool. It’s used because we might need pools for other purposes with different sizes, so we use our pool manager without any problem.UINT64 PoolManagerRequestPool(POOL_ALLOCATION_INTENTION Intention, BOOLEAN RequestNewPool, UINT32 Size);In the vmx-root mode, if we need a safe pool address immediately we call it, it also requests a new pool if we set RequestNewPool to TRUE; thus, next time that it’s safe, the pool will be allocated.Also, you can look at the code for other explanations.Avoid Intercepting Accesses to CR3One of misunderstanding that we have from part 5 until this part was that we intercept CR3 accesses because we set CR3 load-exiting and CR3 store-exiting on the Cpu Based VM Exec Controls.In general, it’s quite unusual to intercept guest accesses to CR3 when you run it under EPT. It’s a behavior mostly done when you implementing shadow MMU (Because lack of EPT support in CPU) so not intercepting CR3 accesses is the standard behavior for any hypervisor running with EPT enabled.Intercepting CR3 access is always configurable, we have to clear bits CPU_BASED_CR3_STORE_EXITING, CPU_BASED_CR3_LOAD_EXITING, and CPU_BASED_INVLPG_EXITING in VMCS’s CPU_BASED_VM_EXEC_CONTROL.But wait, why we should clear them, we never set them !As noted in previous parts, certain VMX controls are reserved and must be set to a specific value (0 or 1), which is determined by the processor. That’s why we used the function “HvAdjustControls” and pass them an MSR (MSR_IA32_VMX_PROCBASED_CTLS, MSR_IA32_VMX_PINBASED_CTLS, MSR_IA32_VMX_EXIT_CTLS, MSR_IA32_VMX_ENTRY_CTLS) which represents these settings.Actually, there are 3 types of settings for VMCS controls. Always-flexible. These have never been reserved. Default0. These are (or have been) reserved with a default setting of 0. Default1. They are (or have been) reserved with a default setting of 1.On newer processors, if Bit 55 (IA32_VMX_BASIC) is read as 1 if any VMX controls that are default1 may be cleared to 0. This bit also reports support for the VMX capability MSRs A32_VMX_TRUE_PINBASED_CTLS, IA32_VMX_TRUE_PROCBASED_CTLS, IA32_VMX_TRUE_EXIT_CTLS, and IA32_VMX_TRUE_ENTRY_CTLS.So we have to check if our CPU supports this bit, if it supports then we have to use new A32_VMX_TRUE_PINBASED_CTLS, IA32_VMX_TRUE_PROCBASED_CTLS, IA32_VMX_TRUE_EXIT_CTLS, and IA32_VMX_TRUE_ENTRY_CTLS instead of MSR_IA32_VMX_PROCBASED_CTLS, MSR_IA32_VMX_PINBASED_CTLS, MSR_IA32_VMX_EXIT_CTLS, MSR_IA32_VMX_ENTRY_CTLS.Note that MSR_IA32_VMX_PROCBASED_CTLS2 doesn’t have another version.For this purpose, first we read the MSR_IA32_VMX_BASIC. IA32_VMX_BASIC_MSR VmxBasicMsr = { 0 }; // Reading IA32_VMX_BASIC_MSR VmxBasicMsr.All = __readmsr(MSR_IA32_VMX_BASIC);Then we check whether the 55th bit of the MSR_IA32_VMX_BASIC is set or not. If it’s set, then we use different MSR to our HvAdjustControls. CpuBasedVmExecControls = HvAdjustControls(CPU_BASED_ACTIVATE_MSR_BITMAP | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, VmxBasicMsr.Fields.VmxCapabilityHint ? MSR_IA32_VMX_TRUE_PROCBASED_CTLS : MSR_IA32_VMX_PROCBASED_CTLS); __vmx_vmwrite(CPU_BASED_VM_EXEC_CONTROL, CpuBasedVmExecControls); LogInfo("Cpu Based VM Exec Controls (Based on %s) : 0x%x", VmxBasicMsr.Fields.VmxCapabilityHint ? "MSR_IA32_VMX_TRUE_PROCBASED_CTLS" : "MSR_IA32_VMX_PROCBASED_CTLS", CpuBasedVmExecControls); SecondaryProcBasedVmExecControls = HvAdjustControls(CPU_BASED_CTL2_RDTSCP | CPU_BASED_CTL2_ENABLE_EPT | CPU_BASED_CTL2_ENABLE_INVPCID | CPU_BASED_CTL2_ENABLE_XSAVE_XRSTORS | CPU_BASED_CTL2_ENABLE_VPID, MSR_IA32_VMX_PROCBASED_CTLS2); __vmx_vmwrite(SECONDARY_VM_EXEC_CONTROL, SecondaryProcBasedVmExecControls); LogInfo("Secondary Proc Based VM Exec Controls (MSR_IA32_VMX_PROCBASED_CTLS2) : 0x%x", SecondaryProcBasedVmExecControls); __vmx_vmwrite(PIN_BASED_VM_EXEC_CONTROL, HvAdjustControls(0, VmxBasicMsr.Fields.VmxCapabilityHint ? MSR_IA32_VMX_TRUE_PINBASED_CTLS : MSR_IA32_VMX_PINBASED_CTLS)); __vmx_vmwrite(VM_EXIT_CONTROLS, HvAdjustControls(VM_EXIT_IA32E_MODE, VmxBasicMsr.Fields.VmxCapabilityHint ? MSR_IA32_VMX_TRUE_EXIT_CTLS : MSR_IA32_VMX_EXIT_CTLS)); __vmx_vmwrite(VM_ENTRY_CONTROLS, HvAdjustControls(VM_ENTRY_IA32E_MODE, VmxBasicMsr.Fields.VmxCapabilityHint ? MSR_IA32_VMX_TRUE_ENTRY_CTLS : MSR_IA32_VMX_ENTRY_CTLS));This way, we can gain better performance by disabling unnecessary vm-exits as there are countless CR3 changes for each process in Windows, and also meltdown patch brings twice cr3 changes. We no longer need to intercept them.Restoring IDTR, GDTR, GS Base and FS BaseOne of the things that we didn’t have in the previous parts was that we didn’t restore the IDTR, GDTR, GS Base, and FS Base when we want to turn off the hypervisor. We should reset GDTR/IDTR when you do vmxoff, or PatchGuard will detect them left modified.In order to restore them, before executing vmxoff in each core, the following function is called and it takes care of everything that should be restored to avoid PatchGuard errors.It read GUEST_GS_BASE and GUEST_FS_BASE from VMCS and write to restore them with WRMSR and also restore the GUEST_GDTR_BASE, GUEST_GDTR_LIMIT, and GUEST_IDTR_BASE, GUEST_IDTR_LIMIT using lgdt and lidt instructions.VOID HvRestoreRegisters(){ ULONG64 FsBase; ULONG64 GsBase; ULONG64 GdtrBase; ULONG64 GdtrLimit; ULONG64 IdtrBase; ULONG64 IdtrLimit; // Restore FS Base __vmx_vmread(GUEST_FS_BASE, &amp;FsBase); __writemsr(MSR_FS_BASE, FsBase); // Restore Gs Base __vmx_vmread(GUEST_GS_BASE, &amp;GsBase); __writemsr(MSR_GS_BASE, GsBase); // Restore GDTR __vmx_vmread(GUEST_GDTR_BASE, &amp;GdtrBase); __vmx_vmread(GUEST_GDTR_LIMIT, &amp;GdtrLimit); AsmReloadGdtr(GdtrBase, GdtrLimit); // Restore IDTR __vmx_vmread(GUEST_IDTR_BASE, &amp;IdtrBase); __vmx_vmread(GUEST_IDTR_LIMIT, &amp;IdtrLimit); AsmReloadIdtr(IdtrBase, IdtrLimit);}This is the assembly part to restore IDTR and GDTR.;------------------------------------------------------------------------; AsmReloadGdtr (PVOID GdtBase (rcx), ULONG GdtLimit (rdx) );AsmReloadGdtr PROC push rcx shl rdx, 48 push rdx lgdt fword ptr [rsp+6] ; do not try to modify stack selector with this ;) pop rax pop rax retAsmReloadGdtr ENDP;------------------------------------------------------------------------; AsmReloadIdtr (PVOID IdtBase (rcx), ULONG IdtLimit (rdx) );AsmReloadIdtr PROC push rcx shl rdx, 48 push rdx lidt fword ptr [rsp+6] pop rax pop rax retAsmReloadIdtr ENDP;------------------------------------------------------------------------Also, it’s better to unset vmx-enable bit of cr4 after executing vmxoff on each core separately. // Now that VMX is OFF, we have to unset vmx-enable bit on cr4 __writecr4(__readcr4() &amp; (~X86_CR4_VMXE));Let’s Test it!The code for our hypervisor is tested on bare-metal (physical machine), VMware’s nested virtualization and Hyper-V’s nested virtualization.View WPP Tracing MessagesTo test WPP Tracing you need an application for parsing messages, I use TraceView.TraceView is located in the tools\\&lt;Platform&gt; subdirectory of the Windows Driver Kit (WDK), where &lt;Platform&gt; represents the platform you are running the trace session on, for example, x86, x64, or arm64.There are also other applications both GUI and Command-line for this purpose, you can see a list of some of these apps here.First, open the traceview (run as administrator), go to File-&gt; Create New Log Session, and use the .pdb file generated by visual studio. PDB file contains debugging information, and for WPP Tracing, they contain GUID and format of messages.When you select your provider, then click Next.Here you can configure what kind of messages you want to see, e.g you only want to see error messages.The default configuration is to see all the messages.Finally, you’ll see the following results.How to test?Now it’s time to see what we’ve done in this part !Note: None of the below tests are active by default, you have to uncomment specific lines to see results in your hypervisor!Event Injection &amp; Exception Bitmap DemoIn order to test event injection and exception bitmap we have a scenario where we want to monitor each debug breakpoint that is triggered in a user-mode application.For this, I debugged an application with Immunity Debugger and put a breakpoint on multiple addresses. We want to intercept each breakpoint from any applications.First, uncomment the following line in Vmx.c . // Set exception bitmap to hook division by zero (bit 1 of EXCEPTION_BITMAP) __vmx_vmwrite(EXCEPTION_BITMAP, 0x8); // breakpoint 3nd bitThis will cause a vm-exit on each execution of breakpoint exception using Exception Bitmap.The following codes are responsible to handle the vm-exits for Exception Bitmap. We check to see what was the interrupt/exception that causes this vm-exit by VM_EXIT_INTR_INFO from VMCS. If it’s a SOFTWARE EXCEPTION and its a vector is BREAKPOINT then we’re sure that execution of an (int 3 or 0xcc) was the cause for this vm-exit.Now, we create a log that shows a breakpoint that happened in GUEST_RIP then re-inject the breakpoint back to the guest (Event Injection). We have to re-inject it back to the guest because the event is canceled after this vm-exit, you can check it, just remove the EventInjectBreakpoint(), and your user-mode debugger will no longer work. case EXIT_REASON_EXCEPTION_NMI: { /* Exception or non-maskable interrupt (NMI). Either: 1: Guest software caused an exception and the bit in the exception bitmap associated with exception’s vector was set to 1 2: An NMI was delivered to the logical processor and the “NMI exiting” VM-execution control was 1. VM_EXIT_INTR_INFO shows the exit infromation about event that occured and causes this exit Don't forget to read VM_EXIT_INTR_ERROR_CODE in the case of re-injectiong event */ // read the exit reason __vmx_vmread(VM_EXIT_INTR_INFO, &amp;InterruptExit); if (InterruptExit.InterruptionType == INTERRUPT_TYPE_SOFTWARE_EXCEPTION &amp;&amp; InterruptExit.Vector == EXCEPTION_VECTOR_BREAKPOINT) { ULONG64 GuestRip; // Reading guest's RIP __vmx_vmread(GUEST_RIP, &amp;GuestRip); // Send the user LogInfo("Breakpoint Hit (Process Id : 0x%x) at : %llx ", PsGetCurrentProcessId(), GuestRip); GuestState[CurrentProcessorIndex].IncrementRip = FALSE; // re-inject #BP back to the guest EventInjectBreakpoint(); } else { LogError("Not expected event occured"); } break; }To see the result as a gif, click the link below.View Example as a .gif (event-inject-and-exception-bitmap.gif)Hidden Hooks DemoHidden hooks are divided into two parts, The first part is for hidden hooks of Read/Write (It’s like simulating hardware debug registers without any limitation), and the second part is hidden hooks for execution which is an equivalent of invisible in-line hooks.In order to activate the hidden hooks test, uncomment HiddenHooksTest() from Driver.c .Note that you can simultaneously use Hidden Hooks for Read/Write, Execute or syscall hook, there is no limitation. //////////// test //////////// HiddenHooksTest(); // SyscallHookTest(); ////////////////////////////// Read/Write Hooks or Hardware Debug Registers SimulationFor testing read and write, uncomment the first line, now you’ll be notified in the case of any Read/Write from any locations to the current thread’s _ETHREAD structure (KeGetCurrentThread())./* Make examples for testing hidden hooks */VOID HiddenHooksTest(){ // Hook Test EptPageHook(KeGetCurrentThread(), NULL, NULL, TRUE, TRUE, FALSE); // EptPageHook(ExAllocatePoolWithTag, ExAllocatePoolWithTagHook, (PVOID*)&amp;ExAllocatePoolWithTagOrig, FALSE, FALSE, TRUE); // Unhook Tests //HvPerformPageUnHookSinglePage(ExAllocatePoolWithTag); //HvPerformPageUnHookAllPages(); }To see the result as a gif, click the link below.View Example as a .gif (hidden-hook-example-read-write.gif)Also, you can see the results in Windbg !Hidden Execution HookThe second scenario for hidden hooks is to inline hook the ExAllocatePoolWithTag function.This is done by uncommenting the following line./* Make examples for testing hidden hooks */VOID HiddenHooksTest(){ // Hook Test // EptPageHook(KeGetCurrentThread(), NULL, NULL, TRUE, TRUE, FALSE); EptPageHook(ExAllocatePoolWithTag, ExAllocatePoolWithTagHook, (PVOID*)&amp;ExAllocatePoolWithTagOrig, FALSE, FALSE, TRUE); // Unhook Tests //HvPerformPageUnHookSinglePage(ExAllocatePoolWithTag); //HvPerformPageUnHookAllPages(); }And also a simple fucntion that logs each ExAllocatePoolWithTag./* Hook function that HooksExAllocatePoolWithTag */PVOID ExAllocatePoolWithTagHook( POOL_TYPE PoolType, SIZE_T NumberOfBytes, ULONG Tag){ LogInfo("ExAllocatePoolWithTag Called with : Tag = 0x%x , Number Of Bytes = %d , Pool Type = %d ", Tag, NumberOfBytes, PoolType); return ExAllocatePoolWithTagOrig(PoolType, NumberOfBytes, Tag);}The hook is applied ! you can also try to use (u nt!ExAllocatePoolWithTag) and see there is no in-line hook there, so it’s completely hidden and of course PatchGuard compatible!To see the result as a gif, click the link below.View Example as a .gif (hidden-hook-example-exec.gif)Syscall Hook DemoOur scenario for testing system-call hooks is first uncommenting the following line in Driver.c . //////////// test //////////// // HiddenHooksTest(); SyscallHookTest(); ////////////////////////////// The following function first searches for API Number 0x55 (on Windows 10 1909, 0x55 represents to NtCreateFile this is not true for all versions of Windows you have to find the correct API Number for NtCreateFile based on your Windows version, a full list of system-call numbers for Nt Table is here and for Win32k Table is here).After finding the address of NtCreateFile (Syscall number 0x55) we set a hidden hook on this address./* Make examples for testing hidden hooks */VOID SyscallHookTest() { // Note that this syscall number is only valid for Windows 10 1909, you have to find the syscall number of NtCreateFile based on // Your Windows version, please visit https://j00ru.vexillium.org/syscalls/nt/64/ for finding NtCreateFile's Syscall number for your Windows. INT32 ApiNumberOfNtCreateFile = 0x0055; PVOID ApiLocationFromSSDTOfNtCreateFile = SyscallHookGetFunctionAddress(ApiNumberOfNtCreateFile, FALSE); if (!ApiLocationFromSSDTOfNtCreateFile) { LogError("Error in finding base address."); return FALSE; } if (EptPageHook(ApiLocationFromSSDTOfNtCreateFile, NtCreateFileHook, (PVOID*)&amp;NtCreateFileOrig, FALSE, FALSE, TRUE)) { LogInfo("Hook appkied to address of API Number : 0x%x at %llx\\n", ApiNumberOfNtCreateFile, ApiLocationFromSSDTOfNtCreateFile); }}For handling in-line hook, the following function is used which creates a log based on the file name and finally calls the original NtCreateFile./* Hook function that hooks NtCreateFile */NTSTATUS NtCreateFileHook( PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength){ HANDLE kFileHandle; NTSTATUS ConvertStatus; UNICODE_STRING kObjectName; ANSI_STRING FileNameA; kObjectName.Buffer = NULL; __try { ProbeForRead(FileHandle, sizeof(HANDLE), 1); ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), 1); ProbeForRead(ObjectAttributes-&gt;ObjectName, sizeof(UNICODE_STRING), 1); ProbeForRead(ObjectAttributes-&gt;ObjectName-&gt;Buffer, ObjectAttributes-&gt;ObjectName-&gt;Length, 1); kFileHandle = *FileHandle; kObjectName.Length = ObjectAttributes-&gt;ObjectName-&gt;Length; kObjectName.MaximumLength = ObjectAttributes-&gt;ObjectName-&gt;MaximumLength; kObjectName.Buffer = ExAllocatePoolWithTag(NonPagedPool, kObjectName.MaximumLength, 0xA); RtlCopyUnicodeString(&amp;kObjectName, ObjectAttributes-&gt;ObjectName); ConvertStatus = RtlUnicodeStringToAnsiString(&amp;FileNameA, ObjectAttributes-&gt;ObjectName, TRUE); LogInfo("NtCreateFile called for : %s", FileNameA.Buffer); } __except (EXCEPTION_EXECUTE_HANDLER) { } if (kObjectName.Buffer) { ExFreePoolWithTag(kObjectName.Buffer, 0xA); } return NtCreateFileOrig(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);}To see the result as a gif, click the link below.View Example as a .gif (syscall-hook-example-1.gif)Also, you can see the results in Windbg !DiscussionIt’s time to see the questions and discussions about this part, the discussion is usually about questions and experience about developing hypervisors. Thanks to Petr for making this part ready.1. What is the IRQL in VMX root-mode? Have you ever tried to use KeGetCurrentIrql() in the VMX root-mode and see the result? It returns the below results in the picture, different IRQLs.- IRQL is nothing more than Cr8 register, Cr8 register doesn’t change when VM-exit occurs, therefore, your KeGetCurrentIrql() returns the IRQL before the VM-exit happened.- In VM-root mode, there is “no IRQL”, because VMX doesn’t know such terms as IRQL (it’s Microsoft thingy) but practically speaking, HIGH_IRQL is what’s closest to the state in VMX-root mode because interrupts are disabled- Actually, IRQL requirements don’t mean much when running in the VMM context. For example, even if you enter at PASSIVE_LEVEL, you are technically at HIGH_LEVEL for all intents and purposes, as interrupts are disabled. - You can use KeGetEffectiveIrql() in VMX-root mode, and it always returns HIGH_LEVEL (that function checks if IF (Interrupt Flag) bit in EFLAGS is set and if not, it returns HIGH_LEVEL, if yes, then it returns the same value as KeGetCurrentIrql(). The EFLAGS.IF is cleared when VM-exit happened but the IF only affects hardware interrupt, and exceptions can still occur.- If you still have a problem with understanding IRQL in VMM then there are some interesting questions answered by Alex in Hyperplatform: https://github.com/tandasat/HyperPlatform/issues/3#issuecomment-231804839 that tries to explain why vmx root-mode is like HIGH_IRQL. I try to add some explanation to them.2. Is it safe for you to be context switched by the OS while in the middle of VMM mode?- Of course not. So you are at least at DISPATCH_LEVEL (As Windows schedules all threads to run at IRQLs below DISPATCH_LEVEL ).3. Is it safe for you to “wait” on an object while at VMM mode?- Of course not, you would be context switched to another thread/idle thread which would now be running as VMM Host. (Means that you wait on some objects and when another vm-exit occurs, you’re no longer in the previous thread.)4. Is it safe/OK for you to receive DPCs while in the middle of VMM mode?- Again, of course not. Another reason why you are at least at DISPATCH_LEVEL.5. Could you receive a DPC, even if you wanted to?- Nope. Receiving a DPC requires an interrupt, and IF in r/eflags is off, so Local APIC will never deliver it.6. Will you receive any Device Interrupts?- Nope, because EFLAGS.IF is off.7. Would you want to be interrupted in the middle of VMM mode?- Also nope. So you are at least at MAX_DIRQL.8. Will you receive the clock interrupt?- Nope (also why you hit a CLOCK WATCHDOG BSOD sometimes)… So you are at least at CLOCK_LEVEL.9. Will you receive IPIs?- Nope, because IF is off, so Local APIC will never send them. You also probably don’t want to be running IPI while inside the VMM host… So you are at least at IPI_LEVEL. Technically because you are not in the middle of handling an IPI, but rather you’ve disabled interrupts completely, you are at IPI_LEVEL + 1, aka HIGH_LEVEL.10. Why ExAllocatePoolWithTag doesn’t work on Vmx root-mode?- In other words, if you call, for example, ExAllocatePoolWithTag, and this is PAGED POOL, you can get unlucky and this will require page-in which requires blocking your thread, and now, some other thread will run in VMM host mode… Sure, you can get lucky and control will come back to you, but this is insane… If you request NON-PAGED POOL, it will “appear to work”… And then in one situation, a TLB flush will be required, which sends an IPI… Which can’t be delivered… And so it will hang. etc.11. Is it ok that I used Insert DPC in VMX root-mode? I used KeInsertQueueDpc (because according to MSDN this function can be called at Any Level).- Yes and no. it’s okay when you have GUARANTEED that you won’t get conflicting VM-exit that would somehow result in a recursion/deadlock, but that very depends on the use case.- For demonstration purposes, I wouldn’t mind using KeInsertQueueDpc in “real/production” environment, I would probably inject NMI from the hypervisor, and in NMI handler I would queue DPC.- It’s one more indirection, therefore it’s going to be slightly slower, but I think it’s a generally safer way… (I use it this way) however, I must note that it’s not bulletproof, as I already ran into recursive NMI injection and deadlocks in NMI handler too.- As I said, there’s no silver bullet, there always will be some dark corners when you try to communicate with the underlying OS.12. Using functions like RtlStringCchLengthA and RtlStringCchLengthA is not allowed because according to MSDN its IRQL is PASSIVE_LEVEL, so we can’t use them in VMX-Root mode? What should we do instead?- We can use sprintf (and sprintf like functions) from the C std library. it’s safe to use since it doesn’t allocate any memory. AFAIK RtlString* functions are in the PAGE section, therefore they can be paged out and if you call them from VMX-root mode when they’re paged out…. you know what happens ;)13. I was reading about VPID (INVVPID) and this seems to be unusable for hypervisors like hvpp and hyperplatform and ours? Am I right? I mean is there any special case in hypervisors that virtualize an already running system that INVVPID is preferred instead of INVEPT?- You are right, invvpid is generally useless in our cases. the only case I can think of where invvpid might be beneficial is in emulation of “invlpg” instruction, see here.- Simply said, invept will invalidate ALL EPT mappings. with invvpid, you can invalidate SPECIFIC addresses in the guest (i.e. underlying OS). I think you know how caches generally work, but I’ll try to explain anyway: with invept, you lose all cache for the guest, therefore it will take time to fill that cache again (each first memory access after INVEPT will be slow).- with invvpid, the cache is retained, but the only single address is invalidated, therefore loading of only THAT address will be slow with that said, I really can’t think of any other practical example where you’d need that, except the invlpg emulation mentioned above.14. What happens if we’re in vmx root and access an address that will cause an EPT violation?It’s like asking “what happens if we have paging disabled and access an address that will cause a page fault” EPTs are for guests, vmx-root is essentially host. EPT translation doesn’t happen when you’re in vmx root. Only regular paging. Therefore - it doesn’t matter if you access an address that will cause an EPT violation or not, what matters is whether is that address valid in vmx-root’s regular CR3 page tables.15. What if we want to cause vm-exit on exception/interrupts with IDT Index &gt; 32? Exception Bitmap is just a 32-bit field in VMCS!There are only 32 exceptions in x86 architecture. The rest are external-interrupts, which are intercepted by the pin-based control “external-interrupt exiting”. This means that you can’t select a special interrupt to cause a vm-exit, but you can configure pin-based control to cause vm-exit in the case of each interrupt.16. If several CPUs try to acquire the same spinlock at the same time, which CPU gets the spinlock first?- Normally, there is no order - the CPU with the fastest electrons wins :). The kernel does provide an alternative, called queued spinlocks that serve CPUs on a FIFO basis. These only work with IRQL DISPATCH_LEVEL. The relevant APIs are KeAcquireInStackQueuedSpinLock and KeReleaseInStackQueuedSpinLock. Check the WDK documentation for more details.17. We use DPCs to transfer messages, and because we may be executing in an arbitrary user-mode process as part DPCs, then why is our message tracing works without problem?- It works because we use METHOD_BUFFERED in our IOCTL. Generally, you have to specify that you need a buffered method in driver entry. // Establish user-buffer access method. DeviceObject-&gt;Flags |= DO_BUFFERED_IO;- But in the case of IOCTLs, you have specified this flag in IOCTL code, if you’re not familiar with METHOD_BUFFERED, it’s a way that Windows gives you a system-wide address which is valid in any process (kernel-mode) that’s why we can fill the buffer from any arbitrary process and address in Irp-&gt;AssociatedIrp.SystemBuffer in any process.- Using METHOD_BUFFERED is, of course, slower, but it solves these kinds of problems and is it’s generally safer.18. Why we didn’t use APCs instead of DPCs in message tracing?- We can use APCs instead of DPCs in our case, but using DPCs gives us a better priority as the callback is executed in DISPATCH_LEVEL as soon as possible. APCs are thread-specific means that whenever a thread runs, we have the chance that our callback is executed while DPCs are processor-specific so we can interrupt any random process, so it’s faster.- Another reason is APCs are undocumented kernel object while DPCs are documented so that’s the reason why programmers prefer to use DPCs.ConclusionWe come to the end of this part, in this part we saw some important things that can be implemented with virtualizing an already running system like hidden hooks, syscall hook, event injection, exception bitmap, and our custom VMX Root compatible message tracing, by now you should be able to use your hypervisor driver in many kinds of researches and solve your reverse-engineering problems.In the next part, we’ll look at some advanced virtualization topics like APIC Virtualization and lots of other things to make a stable and useful hypervisor.Hope you guys enjoyed it, see you in the next part.References[1] Virtual Processor IDs and TLB - (http://www.jauu.net/2011/11/13/virtual-processor-ids-and-tlb/)[2] INVVPID — Invalidate Translations Based on VPID - (https://www.felixcloutier.com/x86/invvpid)[3] INVPCID — Invalidate Process-Context Identifier - (https://www.felixcloutier.com/x86/invpcid)[4] Here’s how, and why, the Spectre and Meltdown patches will hurt performance - (https://arstechnica.com/gadgets/2018/01/heres-how-and-why-the-spectre-and-meltdown-patches-will-hurt-performance/)[5] Is vmxoff path really safe/correct? - (https://github.com/tandasat/HyperPlatform/issues/3)[6] Day 5: The VM-Exit Handler, Event Injection, Context Modifications, And CPUID Emulation - (https://revers.engineering/day-5-vmexits-interrupts-cpuid-emulation/)[7] Test-and-set - (https://en.wikipedia.org/wiki/Test-and-set)[8] _interlockedbittestandset intrinsic functions - (https://docs.microsoft.com/en-us/cpp/intrinsics/interlockedbittestandset-intrinsic-functions?view=vs-2019)[9] Spinlocks and Read-Write Locks - (https://locklessinc.com/articles/locks/)[10] PAUSE - Spin Loop Hint - (https://c9x.me/x86/html/file_module_x86_id_232.html)[11] What is the purpose of the “PAUSE” instruction in x86? - (https://stackoverflow.com/questions/12894078/what-is-the-purpose-of-the-pause-instruction-in-x86)[12] How does x86 pause instruction work in spinlock and can it be used in other scenarios? - (https://stackoverflow.com/questions/4725676/how-does-x86-pause-instruction-work-in-spinlock-and-can-it-be-used-in-other-sc)[13] Introduction to the volatile keyword - (https://www.embedded.com/introduction-to-the-volatile-keyword/)[14] Deferred Procedure Call - (https://en.wikipedia.org/wiki/Deferred_Procedure_Call)[15] Reversing DPC: KeInsertQueueDpc - (https://repnz.github.io/posts/practical-reverse-engineering/reversing-dpc-keinsertqueuedpc/)[16] Dumping DPC Queues: Adventures in HIGH_LEVEL IRQL - (https://repnz.github.io/posts/practical-reverse-engineering/dumping-dpc-queues/)[17] Vol 3C – Chapter 31 – (31.5.1 Algorithms for Determining VMX Capabilities) – (https://software.intel.com/en-us/articles/intel-sdm)[18] Vol 3D – Appendix A.2 – (RESERVED CONTROLS AND DEFAULT SETTINGS) – (https://software.intel.com/en-us/articles/intel-sdm)[19] Add WPP tracing to the Kernel Mode (Windows driver) – (http://kernelpool.blogspot.com/2018/05/add-wpp-tracing-to-kernel-mode-windows.html)[20] WPP Software Tracing – (https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/wpp-software-tracing)[21] TraceView – (https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/traceview)[22] What is the difference between Trap and Interrupt? – (https://stackoverflow.com/questions/3149175/what-is-the-difference-between-trap-and-interrupt)[23] How to disable Hyper-V in command line? – (https://stackoverflow.com/questions/30496116/how-to-disable-hyper-v-in-command-line)[24] Run Hyper-V in a Virtual Machine with Nested Virtualization – (https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/nested-virtualization)[25] Hypervisor Top-Level Functional Specification – (https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs)[26] Requirements for Implementing the Microsoft Hypervisor Interface – (https://github.com/Microsoft/Virtualization-Documentation/raw/master/tlfs/Requirements%20for%20Implementing%20the%20Microsoft%20Hypervisor%20Interface.pdf)[27] Simple Svm Hook Specification – (https://github.com/tandasat/SimpleSvmHook)[28] x86 calling conventions – (https://en.wikipedia.org/wiki/X86_calling_conventions)[29] Exceptions – (https://wiki.osdev.org/Exceptions)[30] Nt Syscall Table – (https://j00ru.vexillium.org/syscalls/nt/64/)[31] Win32k Syscall Table – (https://j00ru.vexillium.org/syscalls/win32k/64/)[32] KVA Shadow: Mitigating Meltdown on Windows – (https://msrc-blog.microsoft.com/2018/03/23/kva-shadow-mitigating-meltdown-on-windows/)[33] HyperBone - Minimalistic VT-X hypervisor with hooks – (https://github.com/DarthTon/HyperBone)[34] Syscall Hooking Via Extended Feature Enable Register (EFER) – (https://revers.engineering/syscall-hooking-via-extended-feature-enable-register-efer/)[35] xdbg64’s TitanHide – (https://github.com/dotfornet/TitanHide/)[36] System Service Descriptor Table - SSDT – (https://ired.team/miscellaneous-reversing-forensics/windows-kernel/glimpse-into-ssdt-in-windows-x64-kernel)[37] DdiMon – (https://github.com/tandasat/DdiMon)[38] Gbhv - Simple x64 Hypervisor Framework – (https://github.com/Gbps/gbhv)[39] Hook SSDT(Shadow) – (https://m0uk4.gitbook.io/notebooks/mouka/windowsinternal/ssdt-hook)[40] DetourXS – (https://github.com/DominicTobias/detourxs)[41] What is the difference between Trap and Interrupt? – (https://stackoverflow.com/questions/3149175/what-is-the-difference-between-trap-and-interrupt)" }, { "title": "Hypervisor From Scratch – Part 7: Using EPT & Page-Level Monitoring Features", "url": "/topics/hypervisor-from-scratch-part-7/", "categories": "cpu, hypervisor, tutorials", "tags": "hypervisor, 2mb-ept, 4kb-ept, ept-hook, ept-monitoring, ept-table, extended-page-table, extended-page-table-pointer, hidden-hook, hypervisor-tutorial-part-7, intel-vt-x-paging, invalidate-ept, invept, memory-type-range-register, mmu-virtualization, mtrr, second-level-address-translation, slat, vmcall", "date": "2020-01-20 00:00:00 +0000", "snippet": "If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous practical examples on how to utilize hypervisors for reverse engineering.IntroductionThis is the 7th part of the tutorial Hypervisor From Scratch, and it’s about using the Extended Page Table (EPT) in an already running system. As you might know, paging is an essential part of managing memory on modern operating systems. Hypervisors use an additional paging table; this gives us an excellent opportunity to monitor different aspects of memory (Read-Write-Execute) without modifying the operating systems page-tables. EPT is a hardware mechanism, so it’s fast, but on the other hand, we have to deal with different caching and synchronization problems.This part is highly dependent on the 4th part of the tutorial - Part 4: Address Translation Using Extended Page Table (EPT), so please read this part one more time; thus, I avoid redescribing about the basic concept relating to EPT Tables.In the 7th part, we’ll see how we can virtualize our currently running system by configuring VMCS and creating identity tables based on Memory Type Range Register (MTRR) then we use monitoring features to detect the execution of some of the Windows functions.This part is highly inspired by Simplevisor and Gbhv.The picture of this post was taken by one of my best friends Ahmad, from Khānābād Village, Aligudarz.Before starting, I should give special thanks to my friend Petr Benes for his contributions to Hypervisor From Scratch, of course, Hypervisor From Scratch could never exist without his help. I also give my regards to Alex Ionescu as he always answers my question patiently.OverviewThis part is divided into seven main sections : Implementing mechanisms to manage Vmcalls Starting with MMU Virtualization (EPT) Explaining Memory Type Range Register (MTRR) concepts Describing Page-Level Monitoring features using EPT Invalidating Translations Derived from EPT (INVEPT) Fixing some previous design caveat regarding deadlocks and synchronization problems Discussion (In this section we discuss the different question(s) and approaches about EPT)At last, I talk about some important notes you need to know in order to debug hypervisor and EPT.Guys, it’s ok if you didn’t understand some of the parts, by reading this article, you’ll get an idea, you could use EPT and over the time you’ll understand things better.The source code of this part changed drastically compared to the previous part; naming conventions are improved, so you see a much cleaner and readable code; also lots of new routines added to the code, for examples routines starting with Hv are hypervisor routines, you have to call them from IRP Major functions and avoid calling methods with Vmx prefix directly as these functions manage the operations relating to VMX Operations, functions with Asm prefix are inline-assembly functions and functions starting with Ept are those that relate to Extended Page Table (EPT). Also, functions with Vmcall prefix are for VMCALL services, and functions with Invept are related to Invalidate EPT caches.The full source code of this tutorial is available on GitHub :[https://github.com/SinaKarvandi/Hypervisor-From-Scratch]Note: Remember that hypervisors change over time because new features are added to the operating systems or new technologies are used. For example, updates to Meltdown &amp; Spectre have made a lot of changes to the hypervisors. So, if you want to use Hypervisor From Scratch in your projects, research, or whatever, you should use the HyperDbg drivers. HyperDbg is actively maintained, stable, and reliable, ensuring you avoid the errors and instability problems that can arise from using older parts of the tutorial series.Table of Contents Introduction Overview Table of Contents Implementing Functions to Manage Vmcalls Starting with MMU virtualization (EPT) Memory Type Range Register (MTRR) Building MTRR Map Fixed-Range MTRRs and PAT Virtualizing Current System’s Memory using EPT EPT Identity Mapping Setting up PML4 and PML3 entries Setting up PML2 entries EPT Violation EPT Misconfiguration Adding EPT to VMCS Monitoring Page’s RWX Activity Pre-allocating Buffers for VMX Root Mode Setting hook before Vmlaunch Setting hook after Vmlaunch Finding a Page’s entry in EPT Tables Finding PML4, PML3, PML2 entries Finding PML1 entry Splitting 2 MB Pages to 4 KB Pages Applying the Hook Handling hooked pages’ vm-exits Invalidating Translations Derived from EPT (INVEPT) Invalidating All Contexts Invalidating Single Context Broadcasting Invept to all logical cores simultaneously Fixing Previous Design Issues Support to more than 64 logical cores Synchronization problem in exiting VMX The issues relating to the Meltdown mitigation Some tips for debugging hypervisors Let’s Test it! How to test? Demo Discussion Conclusion ReferencesImplementing Functions to Manage VmcallsWe start this article by implementing functions relating to VMCALL. Intel describes Vmcall by “Call to VM monitor by causing VM exit.”.Vmcall allows guest software to call for service into an underlying VM monitor. The details of the programming interface for such calls are VMM-specific. This instruction does nothing more than cause a VM exit.In other words, whenever you execute a Vmcall instruction in Vmx non-root mode (whenever a vm-exit occurs, we are in vmx root-mode, and we stay in vmx root mode until we execute VMRESUME or VMXOFF so any other contexts is vmx non-root mode means that other drivers can use Vmcall in their contexts to request a service from our hypervisor in vmx root mode).Execution of VMCALL causes a Vm-exit (EXIT_REASON_VMCALL). As we can set registers and stack before execution of VMCALL so we can send parameters to the Vmcall handler, I mean all we need to do is designing a calling-convention so that both vmcall handler and driver which requests a service can work together perfectly.The first thing we need to implement is a function in assembly, which executes VMCALL and returns.AsmVmxVmcall PROC vmcall ; VmxVmcallHandler(UINT64 VmcallNumber, UINT64 OptionalParam1, UINT64 OptionalParam2, UINT64 OptionalParam3) ret ; Return type is NTSTATUS and it's on RAX from the previous function, no need to change anythingAsmVmxVmcall ENDPIt defines like this,extern NTSTATUS inline AsmVmxVmcall(unsigned long long VmcallNumber, unsigned long long OptionalParam1, unsigned long long OptionalParam2, unsigned long long OptionalParam3);What distinguished from the above code is that we’re not modifying anything in AsmVmxVmcall, means that if someone passes the parameters to the AsmVmxVmcall, then the parameters are in RCX, RDX, R8, R9 and rest of them into the stack, that’s because of x64 FAST CALL calling convention.Keep in mind that if you’re designing hypervisor for Linux, fast call in Linux is different from the fast-call in Windows.As we saved all the registers on vm-exit so in vm-exit handler we pass the GuestRegs-&gt;rcx, GuestRegs-&gt;rdx, GuestRegs-&gt;r8, GuestRegs-&gt;r9 to the VmxVmcallHandler, the RCX is the Vmcall Number which specifies the service we want our hypervisor to perform and RDX and R8 and R9 are optional parameters. case EXIT_REASON_VMCALL: { GuestRegs-&gt;rax = VmxVmcallHandler(GuestRegs-&gt;rcx, GuestRegs-&gt;rdx, GuestRegs-&gt;r8, GuestRegs-&gt;r9); break; }For example, we have the following services (Vmcall Numbers) for our hypervisor in this part.#define VMCALL_TEST 0x1 // Test VMCALL#define VMCALL_VMXOFF 0x2 // Call VMXOFF to turn off the hypervisor#define VMCALL_EXEC_HOOK_PAGE 0x3 // VMCALL to Hook ExecuteAccess bit of the EPT Table#define VMCALL_INVEPT_ALL_CONTEXT 0x4 // VMCALL to invalidate EPT (All Contexts)#define VMCALL_INVEPT_SINGLE_CONTEXT 0x5 // VMCALL to invalidate EPT (A Single Context)There is nothing special for VmxVmcallHandler, it’s just a simple switch case./* Main Vmcall Handler */NTSTATUS VmxVmcallHandler(UINT64 VmcallNumber, UINT64 OptionalParam1, UINT64 OptionalParam2, UINT64 OptionalParam3){ NTSTATUS VmcallStatus; BOOLEAN HookResult; VmcallStatus = STATUS_UNSUCCESSFUL; switch (VmcallNumber) { case VMCALL_TEST: { VmcallStatus = VmcallTest(OptionalParam1, OptionalParam2, OptionalParam3); break; } default: { LogWarning("Unsupported VMCALL"); VmcallStatus = STATUS_UNSUCCESSFUL; break; } } return VmcallStatus;}For testing it, I created a function called VmcallTest, it simply shows the parameters passed to Vmcall./* Test Vmcall (VMCALL_TEST) */NTSTATUS VmcallTest(UINT64 Param1, UINT64 Param2, UINT64 Param3) { LogInfo("VmcallTest called with @Param1 = 0x%llx , @Param2 = 0x%llx , @Param3 = 0x%llx", Param1, Param2, Param3); return STATUS_SUCCESS;}Finally, we can use the following piece of code and pass VMCALL_TEST as the Vmcall Number along with other optional parameters.// Check if everything is ok then return true otherwise falseAsmVmxVmcall(VMCALL_TEST, 0x22, 0x333, 0x4444);Don’t forget that the above code should bee only executed in vmx non-root mode.There is nothing more I can say about VMCALL, but for further reading (not related to our hypervisor), if you want to know what happens if you execute VMCALL in vmx root-mode, it invokes an SMM monitor. This invocation will activate the dual-monitor treatment of system-management interrupts (SMIs) and system-management mode (SMM) if it is not already active. In other words, executing Vmcall in vmx root mode causes an SMM VM exit!Read Section 34.15.2 and Section 34.15.6 in Intel SDM for more information.Starting with MMU virtualization (EPT)Let me start with differences between physical and virtual address,Physical addressing means that your program knows the real layout of RAM. When you access a variable at address 0x8746b3, that’s where it stored in the physical RAM chips.With virtual addressing, all application memory accesses go to a page table, which then maps from the virtual to the physical address. So every application has its own “private” address space, and no program can read or write to another program’s memory. EPT is a page table with a page-walk length of 4 (or in the newer versions 5). It translates guest-physical addresses to host-physical addresses.First, you have to understand that EPT maps guest physical pages to host physical pages, mapping physical addresses make hypervisors much easier to understand because you can forget about all the concepts relating to virtual memory and operating system’s memory manager. Why? That’s because you cannot allocate more physical memory. Sure, you can hot-plug RAM right into the motherboard, but let’s forget about that for now 😉 , so the RAM usually starts at 0 and usually ends at AMOUNT OF RAM + SOME MORE, where SOME MORE is some MMIO/device space.Look at the following picture (from hvpp), Memory Ranges from VMWare VM with 2 GB of RAM.Note the holes between ranges (e.g., A0000 - 100000); the ranges in the screenshot are backed by actual physical RAM, and the holes are the MMIO space.By now, you know that if you allocate or free memory, the RAM ranges are always present and what changes are the content of data in the RAM.Keep in mind, there are certainly no holes in the RAM as an electronic circuit, but it’s how BIOS maps certain physical memory ranges to the actual hardware RAM, in other words, RAM usually isn’t one contiguous address space, if you have 1 GB of RAM it’s often not one single piece of 0 … 1GB physical address space, but some parts of that space belongs to, e.g. network card, audio card, USB hub, etc.Let’s see what hypervisors like VMWare, Hyper-V, VirtualBox do with physical memory. We don’t have the same approach, but it helps you understand MMU virtualization better.In VMWare (Hyper-v, VirtualBox, etc), the VM has its own physical memory, and our PC (host) also has some physical address space. EPT exists so that you can translate the guest physical memory to host physical memory. For example, if a guest wants to read from Physical Address 0x1000, it looks into EPT, and EPT tells it that the content of the memory is on the host’s physical address 0x5000. You certainly do not want to let some guests in VMWare read physical memory on the host, so it’s VMWare’s job to setup EPTs correctly and have some chunk of physical memory dedicated to a guest.Memory Type Range Register (MTRR)By now, you have some idea about how memory (RAM) is divided into regions; these regions can be found using MTRR registers, that’s all!Now let’s explain them more precisely.Wikipedia defines MTRRs like this :Memory type range registers (MTRRs) are a set of processor supplementary capabilities control registers that provide system software with control of how accesses to memory ranges by the CPU are cached. It uses a set of programmable model-specific registers (MSRs), which are special registers provided by most modern CPUs. Possible access modes to memory ranges can be uncached, write-through, write-combining, write-protect, and write-back. In write-back mode, writes are written to the CPU’s cache, and the cache is marked dirty so that its contents are written to memory later.In old x86 architecture systems, mainly where separate chips provided the cache outside of the CPU package, this function was controlled by the chipset itself and configured through BIOS settings, when the CPU cache was moved inside the CPU, the CPUs implemented fixed-range MTRRs.Typically, the BIOS software configures the MTRRs. The operating system or executive is then free to modify the memory map using the typical page-level cacheability attributes.If you confused by reading the above sentences, let me explain it more clearly. RAM is divided into different regions, We want to read the details (Base Address, End Address, and Cache Policy) of these chunks using MTRR Registers. Cache policy is something that BIOS or Operating System sets for a particular region. For example, the operating system decides to put UC (uncached) to a region that starts from 0x1000 to 0x2000 (Physical Address) of RAM then it chooses to put WB (Writeback) to a region starting from 0x5000 to 0x7000 (Physical Address), it’s based on OS policy. If you don’t know about the different memory type caches (e.g., UC, WB), you can read here.OK, let’s see how to read these MTRRs.The availability of the MTRR feature is model-specific means that we can determine if MTRRs are supported on a processor by executing the CPUID instruction and reading the state of the MTRR flag (bit 12) in the feature information register (EDX). Still, This check is not essential as our process probably supports as it’s an old feature.What is essential for us, is an MSR called “IA32_MTRR_DEF_TYPE”. The following structure represents the IA32_MTRR_DEF_TYPE :// MSR_IA32_MTRR_DEF_TYPE typedef union _IA32_MTRR_DEF_TYPE_REGISTER{ struct { /** * [Bits 2:0] Default Memory Type. */ UINT64 DefaultMemoryType : 3; UINT64 Reserved1 : 7; /** * [Bit 10] Fixed Range MTRR Enable. */ UINT64 FixedRangeMtrrEnable : 1; /** * [Bit 11] MTRR Enable. */ UINT64 MtrrEnable : 1; UINT64 Reserved2 : 52; }; UINT64 Flags;} IA32_MTRR_DEF_TYPE_REGISTER, * PIA32_MTRR_DEF_TYPE_REGISTER;We implement a function called “EptCheckFeatures,” this function checks to see whether our processor supports basic EPT features or not; for MTRRs, we’ll check whether MTRRs are enabled or not. Having an enabled MTRR is necessary for our hypervisor. (we’ll complete this function later when we’re describing EPT.) IA32_MTRR_DEF_TYPE_REGISTER MTRRDefType; MTRRDefType.Flags = __readmsr(MSR_IA32_MTRR_DEF_TYPE); if (!MTRRDefType.MtrrEnable) { LogError("Mtrr Dynamic Ranges not supported"); return FALSE; }Building MTRR MapBefore creating a map from memory regions, It’s good to see how Windbg shows the MTRR regions and their caching policies using the “!mtrr” command.As you can see in the above picture, Windows prefers to use Fixed Range Registers (Fixed-support enabled) and variable range registers.I’ll talk about fixed range registers later in this article.In order to read MTRRs, we start by reading the VCNT value of IA32_MTRRCAP MSR (0xFE), which determines the number of variable MTRRs (Number of regions).The next step is to iterate through each MTRR variable; we read MSR_IA32_MTRR_PHYSBASE0 and MSR_IA32_MTRR_PHYSMASK0 for each range and check if the range is valid or not (based on IA32_MTRR_PHYSMASK_REGISTER.Valid bit). CurrentPhysBase.Flags = __readmsr(MSR_IA32_MTRR_PHYSBASE0 + (CurrentRegister * 2)); CurrentPhysMask.Flags = __readmsr(MSR_IA32_MTRR_PHYSMASK0 + (CurrentRegister * 2));Now we need to calculate the start address and the end address (physical) based on MSRs.The start address: // Calculate the base address in bytes Descriptor-&gt;PhysicalBaseAddress = CurrentPhysBase.PageFrameNumber * PAGE_SIZE;The end address: // Calculate the total size of the range // The lowest bit of the mask that is set to 1 specifies the size of the range _BitScanForward64(&amp;NumberOfBitsInMask, CurrentPhysMask.PageFrameNumber * PAGE_SIZE); // Size of the range in bytes + Base Address Descriptor-&gt;PhysicalEndAddress = Descriptor-&gt;PhysicalBaseAddress + ((1ULL &lt;&lt; NumberOfBitsInMask) - 1ULL);For further information about the calculation of MTRRs, you can read Intel SDM Vol 3A (11.11.3 Example Base and Mask Calculations).And finally, read the cache policy which is set by whether BIOS or operating system. // Memory Type (cacheability attributes) Descriptor-&gt;MemoryType = (UCHAR)CurrentPhysBase.Type;Putting it all together, we have the following function :/* Build MTRR Map of current physical addresses */BOOLEAN EptBuildMtrrMap(){ IA32_MTRR_CAPABILITIES_REGISTER MTRRCap; IA32_MTRR_PHYSBASE_REGISTER CurrentPhysBase; IA32_MTRR_PHYSMASK_REGISTER CurrentPhysMask; PMTRR_RANGE_DESCRIPTOR Descriptor; ULONG CurrentRegister; ULONG NumberOfBitsInMask; MTRRCap.Flags = __readmsr(MSR_IA32_MTRR_CAPABILITIES); for (CurrentRegister = 0; CurrentRegister &lt; MTRRCap.VariableRangeCount; CurrentRegister++) { // For each dynamic register pair CurrentPhysBase.Flags = __readmsr(MSR_IA32_MTRR_PHYSBASE0 + (CurrentRegister * 2)); CurrentPhysMask.Flags = __readmsr(MSR_IA32_MTRR_PHYSMASK0 + (CurrentRegister * 2)); // Is the range enabled? if (CurrentPhysMask.Valid) { // We only need to read these once because the ISA dictates that MTRRs are to be synchronized between all processors // during BIOS initialization. Descriptor = &amp;EptState-&gt;MemoryRanges[EptState-&gt;NumberOfEnabledMemoryRanges++]; // Calculate the base address in bytes Descriptor-&gt;PhysicalBaseAddress = CurrentPhysBase.PageFrameNumber * PAGE_SIZE; // Calculate the total size of the range // The lowest bit of the mask that is set to 1 specifies the size of the range _BitScanForward64(&amp;NumberOfBitsInMask, CurrentPhysMask.PageFrameNumber * PAGE_SIZE); // Size of the range in bytes + Base Address Descriptor-&gt;PhysicalEndAddress = Descriptor-&gt;PhysicalBaseAddress + ((1ULL &lt;&lt; NumberOfBitsInMask) - 1ULL); // Memory Type (cacheability attributes) Descriptor-&gt;MemoryType = (UCHAR)CurrentPhysBase.Type; if (Descriptor-&gt;MemoryType == MEMORY_TYPE_WRITE_BACK) { /* This is already our default, so no need to store this range. * Simply 'free' the range we just wrote. */ EptState-&gt;NumberOfEnabledMemoryRanges--; } LogInfo("MTRR Range: Base=0x%llx End=0x%llx Type=0x%x", Descriptor-&gt;PhysicalBaseAddress, Descriptor-&gt;PhysicalEndAddress, Descriptor-&gt;MemoryType); } } LogInfo("Total MTRR Ranges Committed: %d", EptState-&gt;NumberOfEnabledMemoryRanges); return TRUE;}Fixed-Range MTRRs and PATThe above section is enough for understanding the MTRRs for EPT. Still, I want to talk a little more about physical and virtual memory layout and caching policy (you can skip this section as it does not relate to our hypervisor).There are other MTRR registers called Fixed Range Registers as its name implies, these registers are some predefined ranges defined by the processor (you can see them in the first lines of !mtrr command in Windbg).These ranges are showed in the following table:As you can see, the start of physical RAM is defined by these fixed range registers, which are for performance and legacy reasons.Note that MTRRs should be defined contiguously; if your MTRRs are not contiguous, then the rest of the RAM is typically assumed as a hole.Keep in mind that caching policy for each region of RAM is defined by MTRRs for PHYSICAL regions and PAGE ATTRIBUTE TABLE (PAT) for virtual areas so that each page can use its own caching policy by configuring IA32_PAT MSR. This means that sometimes the caching policy specified in MTRR registers is ignored, and instead, a page-level cache policy is used. There is a table in Intel SDM that shows the precedence rules between PAT and MTRRs (Table 11-7. Effective Page-Level Memory Types for Pentium III and More Recent Processor Families).For further reading, you can read Intel SDM (Chapter 11 volume 3 A - 11.11 MEMORY TYPE RANGE REGISTERS (MTRRS) and 11.12 PAGE ATTRIBUTE TABLE (PAT)).Virtualizing Current System’s Memory using EPTAs you have some previous information from EPT (part 4), we create an EPT table for our VM. In the case of fully virtualizing memory of the current machine, there are different approaches in implementing EPT; we can either have a separate EPT table for each of the cores or an EPT table for all the cores, our approach is using one EPT for all the cores as it’s simpler to implement and manage (more details about the benefits and caveat are discussed in Discussion section).What we are trying to do is creating an EPT table that maps all of the available physical memory (we have the details of physical memory from MTRRs) to the physical address. It’s something like adding a table that maps the previous addresses to the previous address with some additional fields to control them. It’s ok if you’re confused, just read the rest of the article and things become more clear.EPT Identity MappingIn our hypervisor or all of the hypervisors that virtualize an already running system (not VMWare, VirtualBox, etc), we have a term called “Identity Mapping or 1:1 mapping”. It means that if you access guest PA (Physical Address) 0x4000, it will access host PA at 0x4000, thus, you have to map RAM’s hole as well as memory ranges to the guest.It is the same as regular page tables (you can set page tables that way as well so that virtual address 0x1234 corresponds to the physical address 0x1234);If you don’t map some physical memory and the guest access it, then you’ll get “EPT Violation”, which can be understood as the hypervisor’s page fault.In order to map everything one by one, we’ll create PML4Es, then PDPTEs, then PDEs, and finally, PEs. In cases with 2 MB of granularity, we’ll skip PEs. Of course, it’s preferred to have 4 KB granularity but keep in mind that 4GB of RAM results in one million of 4 KB pages thus having a 4 KB granularity will eat a lot of memory, besides this, setting 4 KB granularity will take quite some time which will drive you crazy if you test your hypervisor frequently.What hvpp, gbhv, and most of the other hypervisors do is initially set up 2 MB for the whole system (including RAM Ranges and MMIO holes) and then break some 2 MB pages into 4 KB pages as needed.After splitting to 4 KB pages, you can merge them back to 2 MB pages again. We do the same for our hypervisor driver, first initial with 2 MB of granularity, then split them to 4 KB whenever needed.Why we shouldn’t care about new memory allocations of Windows?Well, that’s because we mapped all of the physical memory (every possible addresses in physical RAM) using 2 MB chunks, including those which are allocated and those which are not allocated yet, so no matter if Windows allocates a new memory chunk, we already have it in our EPT table.What we want to do is creating a PML4E; then PDPTE, we’ll add that PDPTE into PML4E, then create PDE and add it to the PDPTE and finally create PE, which will point to physical address 0. Then we create another PE, that will point to address 0x1000 (if the granularity is 4 KB) or 0x200000 ( if the granularity is 2 MB ) and add it again 512 times (maximum entries in all paging tables including EPT Page tables and regular page tables are 512) then we’ll create another PDE and repeat!All in all, our hypervisor should not care about any virtual address, it’s all about physical memory.That’s enough for theory, let’s implement it!Setting up PML4 and PML3 entriesFirst of all, we have to allocate a large memory for our EPT page table and then zero it. PageTable = MmAllocateContiguousMemory((sizeof(VMM_EPT_PAGE_TABLE) / PAGE_SIZE) * PAGE_SIZE, MaxSize); if (PageTable == NULL) { LogError("Failed to allocate memory for PageTable"); return NULL; } // Zero out all entries to ensure all unused entries are marked Not Present RtlZeroMemory(PageTable, sizeof(VMM_EPT_PAGE_TABLE));We have a linked list that holds the trace of every allocated memory; we have to initialize it first so we can de-allocate our allocated pages whenever we want to turn off our hypervisor. // Initialize the dynamic split list which holds all dynamic page splits InitializeListHead(&amp;PageTable-&gt;DynamicSplitList);It’s time to initialize the first table (EPT PML4). For the initialization phase, we set all the accesses to 1 (including Read Access, Write Access, Execute Access) on all of the EPT tables.The physical address (Page Frame Number - PFN) for the PML4E is PML3’s address, and as it’s aligned and whenever the processor wants to translate it (it performs multiplication by PAGE_SIZE) so we divide it by PAGE_SIZE (4096). // Mark the first 512GB PML4 entry as present, which allows us to manage up to 512GB of discrete paging structures. PageTable-&gt;PML4[0].PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&amp;PageTable-&gt;PML3[0]) / PAGE_SIZE; PageTable-&gt;PML4[0].ReadAccess = 1; PageTable-&gt;PML4[0].WriteAccess = 1; PageTable-&gt;PML4[0].ExecuteAccess = 1;Each PML4 entry covers 512 GB of memory, so one entry is more than enough. Each table has 512 entries, so we have to fill PML3 with 512 of 1 GB entries. We’re done this by creating a template with RWX enabled and use __stosq to fill the table with this template continuously. __stosq generates a store string instruction (rep stosq) means that continuously (in our case VMM_EPT_PML3E_COUNT=512) copy something on a special location.The next step is to convert our previously allocated PML2 entries to physical addresses and fill the PML3 with those addresses. // Set up one 'template' RWX PML3 entry and copy it into each of the 512 PML3 entries // Using the same method as SimpleVisor for copying each entry using intrinsics. RWXTemplate.ReadAccess = 1; RWXTemplate.WriteAccess = 1; RWXTemplate.ExecuteAccess = 1; // Copy the template into each of the 512 PML3 entry slots __stosq((SIZE_T*)&amp;PageTable-&gt;PML3[0], RWXTemplate.Flags, VMM_EPT_PML3E_COUNT); // For each of the 512 PML3 entries for (EntryIndex = 0; EntryIndex &lt; VMM_EPT_PML3E_COUNT; EntryIndex++) { // Map the 1GB PML3 entry to 512 PML2 (2MB) entries to describe each large page. // NOTE: We do *not* manage any PML1 (4096 byte) entries and do not allocate them. PageTable-&gt;PML3[EntryIndex].PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&amp;PageTable-&gt;PML2[EntryIndex][0]) / PAGE_SIZE; }For PML2, we have the same approach, fill it with an RWX template, but this time we set LargePage to 1 (for the reason I told you above about initialization with 2 MB granularity). Exactly same as above, we use __stosq to fill these entries, this time with 512*512 entries as we have 512 entries, each of which describes 512 entries.The next step is to set up each entry’s PFN addresses. I’ll describe EptSetupPML2Entry in the next section.Note that we’re are filling entries for a 512*512 table, so we have to perform a multiplication by 512 for each EntryGroupIndex and then add it to the current PML2’s address (EntryIndex). // All PML2 entries will be RWX and 'present' PML2EntryTemplate.WriteAccess = 1; PML2EntryTemplate.ReadAccess = 1; PML2EntryTemplate.ExecuteAccess = 1; // We are using 2MB large pages, so we must mark this 1 here. PML2EntryTemplate.LargePage = 1; /* For each collection of 512 PML2 entries (512 collections * 512 entries per collection), mark it RWX using the same template above. This marks the entries as "Present" regardless of if the actual system has memory at this region or not. We will cause a fault in our EPT handler if the guest access a page outside a usable range, despite the EPT frame being present here. */ __stosq((SIZE_T*)&amp;PageTable-&gt;PML2[0], PML2EntryTemplate.Flags, VMM_EPT_PML3E_COUNT * VMM_EPT_PML2E_COUNT); // For each of the 512 collections of 512 2MB PML2 entries for (EntryGroupIndex = 0; EntryGroupIndex &lt; VMM_EPT_PML3E_COUNT; EntryGroupIndex++) { // For each 2MB PML2 entry in the collection for (EntryIndex = 0; EntryIndex &lt; VMM_EPT_PML2E_COUNT; EntryIndex++) { // Setup the memory type and frame number of the PML2 entry. EptSetupPML2Entry(&amp;PageTable-&gt;PML2[EntryGroupIndex][EntryIndex], (EntryGroupIndex * VMM_EPT_PML2E_COUNT) + EntryIndex); } }Putting it all together we have the following code:/* Allocates page maps and create identity page table */PVMM_EPT_PAGE_TABLE EptAllocateAndCreateIdentityPageTable(){ PVMM_EPT_PAGE_TABLE PageTable; EPT_PML3_POINTER RWXTemplate; EPT_PML2_ENTRY PML2EntryTemplate; SIZE_T EntryGroupIndex; SIZE_T EntryIndex; // Allocate all paging structures as 4KB aligned pages PHYSICAL_ADDRESS MaxSize; PVOID Output; // Allocate address anywhere in the OS's memory space MaxSize.QuadPart = MAXULONG64; PageTable = MmAllocateContiguousMemory((sizeof(VMM_EPT_PAGE_TABLE) / PAGE_SIZE) * PAGE_SIZE, MaxSize); if (PageTable == NULL) { LogError("Failed to allocate memory for PageTable"); return NULL; } // Zero out all entries to ensure all unused entries are marked Not Present RtlZeroMemory(PageTable, sizeof(VMM_EPT_PAGE_TABLE)); // Initialize the dynamic split list which holds all dynamic page splits InitializeListHead(&amp;PageTable-&gt;DynamicSplitList); // Mark the first 512GB PML4 entry as present, which allows us to manage up to 512GB of discrete paging structures. PageTable-&gt;PML4[0].PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&amp;PageTable-&gt;PML3[0]) / PAGE_SIZE; PageTable-&gt;PML4[0].ReadAccess = 1; PageTable-&gt;PML4[0].WriteAccess = 1; PageTable-&gt;PML4[0].ExecuteAccess = 1; /* Now mark each 1GB PML3 entry as RWX and map each to their PML2 entry */ // Ensure stack memory is cleared RWXTemplate.Flags = 0; // Set up one 'template' RWX PML3 entry and copy it into each of the 512 PML3 entries // Using the same method as SimpleVisor for copying each entry using intrinsics. RWXTemplate.ReadAccess = 1; RWXTemplate.WriteAccess = 1; RWXTemplate.ExecuteAccess = 1; // Copy the template into each of the 512 PML3 entry slots __stosq((SIZE_T*)&amp;PageTable-&gt;PML3[0], RWXTemplate.Flags, VMM_EPT_PML3E_COUNT); // For each of the 512 PML3 entries for (EntryIndex = 0; EntryIndex &lt; VMM_EPT_PML3E_COUNT; EntryIndex++) { // Map the 1GB PML3 entry to 512 PML2 (2MB) entries to describe each large page. // NOTE: We do *not* manage any PML1 (4096 byte) entries and do not allocate them. PageTable-&gt;PML3[EntryIndex].PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&amp;PageTable-&gt;PML2[EntryIndex][0]) / PAGE_SIZE; } PML2EntryTemplate.Flags = 0; // All PML2 entries will be RWX and 'present' PML2EntryTemplate.WriteAccess = 1; PML2EntryTemplate.ReadAccess = 1; PML2EntryTemplate.ExecuteAccess = 1; // We are using 2MB large pages, so we must mark this 1 here. PML2EntryTemplate.LargePage = 1; /* For each collection of 512 PML2 entries (512 collections * 512 entries per collection), mark it RWX using the same template above. This marks the entries as "Present" regardless of if the actual system has memory at this region or not. We will cause a fault in our EPT handler if the guest access a page outside a usable range, despite the EPT frame being present here. */ __stosq((SIZE_T*)&amp;PageTable-&gt;PML2[0], PML2EntryTemplate.Flags, VMM_EPT_PML3E_COUNT * VMM_EPT_PML2E_COUNT); // For each of the 512 collections of 512 2MB PML2 entries for (EntryGroupIndex = 0; EntryGroupIndex &lt; VMM_EPT_PML3E_COUNT; EntryGroupIndex++) { // For each 2MB PML2 entry in the collection for (EntryIndex = 0; EntryIndex &lt; VMM_EPT_PML2E_COUNT; EntryIndex++) { // Setup the memory type and frame number of the PML2 entry. EptSetupPML2Entry(&amp;PageTable-&gt;PML2[EntryGroupIndex][EntryIndex], (EntryGroupIndex * VMM_EPT_PML2E_COUNT) + EntryIndex); } } return PageTable;}Setting up PML2 entriesPML2 is different from the other tables; this is because, in our 2 MB design, it’s the last table, so it has to deal with MTRRs’ caching policy.First, we have to set the PageFrameNumber of our PML2 entry. This is because we’re mapping all 512 GB without any hole, I mean, we’re not trying to see just what are MTRR’s base and end address and map based on them, but we map every possible physical address within 512 GB. Think about it one more time.If you want to know more about PFNs in Windows, then you can read my blog posts Inside Windows Page Frame Number (PFN) – Part 1 and Part 2. Each of the 512 collections of 512 PML2 entries is setup here. This will, in total, identity map every physical address from 0x0 to physical address 0x8000000000 (512GB of memory) ((EntryGroupIndex * VMM_EPT_PML2E_COUNT) + EntryIndex) * 2MB is the actual physical address we're mapping */ NewEntry-&gt;PageFrameNumber = PageFrameNumber;Now it’s time to see the actual caching policy based on MTRRs. Ranges in MTRRs are not divided by 4 KB or 2 MB, and these are exact physical addresses. What we are going to do is iterating over each MTRR and see whether a special MTRR describes our current physical address or not.If none of them describe it, then we choose Write-Back (MEMORY_TYPE_WRITE_BACK) as the default caching policy; otherwise, we have to select the caching policy that is used in MTRRs.This approach will make our EPT PML2 as it’s like a real system.If we don’t choose the system-specific caching policy, then it will cause catastrophic errors. For example, some of the devices that use physical memory as the command and control mechanism go through the cache and won’t immediately respond to our requests or for APIC devices will not work in the case of real-time interrupts.The following code is responsible for finding the desired caching policy based on MTRRs. // Default memory type is always WB for performance. TargetMemoryType = MEMORY_TYPE_WRITE_BACK; // For each MTRR range for (CurrentMtrrRange = 0; CurrentMtrrRange &lt; EptState-&gt;NumberOfEnabledMemoryRanges; CurrentMtrrRange++) { // If this page's address is below or equal to the max physical address of the range if (AddressOfPage &lt;= EptState-&gt;MemoryRanges[CurrentMtrrRange].PhysicalEndAddress) { // And this page's last address is above or equal to the base physical address of the range if ((AddressOfPage + SIZE_2_MB - 1) &gt;= EptState-&gt;MemoryRanges[CurrentMtrrRange].PhysicalBaseAddress) { /* If we're here, this page fell within one of the ranges specified by the variable MTRRs Therefore, we must mark this page as the same cache type exposed by the MTRR */ TargetMemoryType = EptState-&gt;MemoryRanges[CurrentMtrrRange].MemoryType; // LogInfo("0x%X&gt; Range=%llX -&gt; %llX | Begin=%llX End=%llX", PageFrameNumber, AddressOfPage, AddressOfPage + SIZE_2_MB - 1, EptState-&gt;MemoryRanges[CurrentMtrrRange].PhysicalBaseAddress, EptState-&gt;MemoryRanges[CurrentMtrrRange].PhysicalEndAddress); // 11.11.4.1 MTRR Precedences if (TargetMemoryType == MEMORY_TYPE_UNCACHEABLE) { // If this is going to be marked uncacheable, then we stop the search as UC always takes precedent. break; } } } } // Finally, commit the memory type to the entry. NewEntry-&gt;MemoryType = TargetMemoryType;EPT ViolationIntel describes EPT Violation like this:An EPT violation occurs when there is no EPT misconfiguration, but the EPT paging structure entries disallow access using the guest-physical address.But that’s hard to understand, in short, every time one instruction tries to read a page (Read Access), or an instruction tries to write on a page (Write Access), or an instruction causes instruction fetch from a page and EPT attributes (the one we configured in the above sections) of that page doesn’t allow this, then an EPT Violation occurs.Let me explain a little bit more, imagine we have an entry in our EPT Table which is responsible for mapping physical address 0x1000. In this entry, we set Write Access to 0 (Read Access = 1 and Execute Access = 1). If any instruction tries to write on that page, for example by using (Mov [0x1000], RAX) then as the paging attributes doesn’t allow writing, so an EPT Violation occurs and now our callback is called so that we can decide to what we want to do with that page.By 0x1000, I mean a physical address. Of course, if you have the virtual address, then it gets translated to a physical.Another example, let’s assume an NT function (for example NtCreateFile) is located fffff801`80230540.nt!NtCreateFile:fffff801`80230540 4881ec88000000 sub rsp,88hfffff801`80230547 33c0 xor eax,eaxfffff801`80230549 4889442478 mov qword ptr [rsp+78h],raxIf we convert it to a physical address, then the address of NtCreateFile in physical memory is 0x3B8000, now we try to find this physical address in our EPT PTE Table. Then we set Execute Access of that entry to 0. Now, each time someone tries to call, jmp, ret, etc. to this particular page, then an EPT Violation occurs.This is the basic idea of using EPT function hooks, we talk about it in detail in Part 8.For now, first, we have to read the physical address, which caused this EPT Violation. It’s done by reading GUEST_PHYSICAL_ADDRESS using Vmread instruction. // Reading guest physical address GuestPhysicalAddr = 0; __vmx_vmread(GUEST_PHYSICAL_ADDRESS, &amp;GuestPhysicalAddr); LogInfo("Guest Physical Address : 0x%llx", GuestPhysicalAddr);The second thing that we have to read is Exit Qualification. If you remember from the previous part, Exit Qualification gives additional details about Exit Reasons.I mean, each Exit Reason might have a special Exit Qualification that has a special meaning for that special Exit Reason. (how many “special” I used in the previous sentence ?)Exit Reason can be read from VM_EXIT_REASON using Vmread instruction. ULONG ExitReason = 0; __vmx_vmread(VM_EXIT_REASON, &amp;ExitReason);In the case of EPT Violation, Exit Qualification shows that the reason why this violation occurs. For example, it indicates that EPT Violation occurs because of a data read to a physical page that its Read Access is 0 or instruction fetches (a function tries to execute instruction) from a physical page that its Execute Access is 0.The following table shows the structure of Exit Qualification and each bit’s meaning for EPT Violation.Now that we have all the details, we need to pass them to EptHandlePageHookExit, and we deal with it in the next sections./* Handle VM exits for EPT violations. Violations are thrown whenever an operation is performed on an EPT entry that does not provide permissions to access that page.*/BOOLEAN EptHandleEptViolation(ULONG ExitQualification, UINT64 GuestPhysicalAddr){ VMX_EXIT_QUALIFICATION_EPT_VIOLATION ViolationQualification; DbgBreakPoint(); ViolationQualification.Flags = ExitQualification; if (EptHandlePageHookExit(ViolationQualification, GuestPhysicalAddr)) { // Handled by page hook code. return TRUE; } LogError("Unexpected EPT violation"); DbgBreakPoint(); // Redo the instruction that caused the exception. return FALSE;}EPT MisconfigurationAnother EPT derived vm-exit is EPT Misconfiguration (EXIT_REASON_EPT_MISCONFIG).An EPT Misconfiguration occurs when, in the course of translating a physical guest address, the logical processor encounters an EPT paging-structure entry that contains an unsupported value.If you want to know more about all the reasons why EPT Misconfiguration occurs, you can see Intel SDM - Vol 3C Section 28.2.3.1.Based on my experience, I encountered EPT Misconfiguration most of the time because I clear the bit 0 of the entry (indicating that data reads are not allowed), and bit 1 is set (reporting that data writes are permitted).Also, EPT misconfigurations occur when an EPT paging-structure entry is configured with settings reserved for future functionality.It’s fatal error, let’s just break and see what we’ve done wrong !VOID EptHandleMisconfiguration(UINT64 GuestAddress){ LogInfo("EPT Misconfiguration!"); LogError("A field in the EPT paging structure was invalid, Faulting guest address : 0x%llx", GuestAddress); DbgBreakPoint(); // We can't continue now. // EPT misconfiguration is a fatal exception that will probably crash the OS if we don't get out now.}Adding EPT to VMCSOur hypervisor starts virtualizing MMU by calling EptLogicalProcessorInitialize, which sets a 64-bit value called EPTP. The following table shows the structure of EPTP. If you look at part 4, we have this table in that part too, but there is a change here, bit 7 was reserved at the time I wrote part 4, and now it has something to do with shadow stacks.EptLogicalProcessorInitialize calls EptAllocateAndCreateIdentityPageTable to allocate identity table (as described above).For performance, we let the processor know it can cache the EPT (MemoryType to MEMORY_TYPE_WRITE_BACK).We are not utilizing the ‘access’ and ‘dirty’ flag features (EnableAccessAndDirtyFlags to FALSE).As Intel mentioned, Page Walk should be the count of the tables we use (4) minus 1, so PageWalkLength = 3 indicates an EPT page-walk length of 4. It is because we’re not using just three tables with 2 MB granularity, we’ll split 2 MB pages to 4 KB granularity.The last step is to save EPTP somewhere into a global variable so we can use it later./* Initialize EPT for an individual logical processor. Creates an identity mapped page table and sets up an EPTP to be applied to the VMCS later.*/BOOLEAN EptLogicalProcessorInitialize(){ PVMM_EPT_PAGE_TABLE PageTable; EPTP EPTP; /* Allocate the identity mapped page table*/ PageTable = EptAllocateAndCreateIdentityPageTable(); if (!PageTable) { LogError("Unable to allocate memory for EPT"); return FALSE; } // Virtual address to the page table to keep track of it for later freeing EptState-&gt;EptPageTable = PageTable; EPTP.Flags = 0; // For performance, we let the processor know it can cache the EPT. EPTP.MemoryType = MEMORY_TYPE_WRITE_BACK; // We are not utilizing the 'access' and 'dirty' flag features. EPTP.EnableAccessAndDirtyFlags = FALSE; /* Bits 5:3 (1 less than the EPT page-walk length) must be 3, indicating an EPT page-walk length of 4; see Section 28.2.2 */ EPTP.PageWalkLength = 3; // The physical page number of the page table we will be using EPTP.PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&amp;PageTable-&gt;PML4) / PAGE_SIZE; // We will write the EPTP to the VMCS later EptState-&gt;EptPointer = EPTP; return TRUE;}Finally, we need to configure Vmcs with our EPTP Table, so we use vmwrite with EPT_POINTER and set it to our EPTP. // Set up EPT __vmx_vmwrite(EPT_POINTER, EptState-&gt;EptPointer.Flags);Also, don’t forget to enable EPT feature in Secondary Processor-Based VM-Execution Controls using CPU_BASED_CTL2_ENABLE_EPT; otherwise, it won’t work. SecondaryProcBasedVmExecControls = HvAdjustControls(CPU_BASED_CTL2_RDTSCP | CPU_BASED_CTL2_ENABLE_EPT | CPU_BASED_CTL2_ENABLE_INVPCID | CPU_BASED_CTL2_ENABLE_XSAVE_XRSTORS, MSR_IA32_VMX_PROCBASED_CTLS2); __vmx_vmwrite(SECONDARY_VM_EXEC_CONTROL, SecondaryProcBasedVmExecControls); LogInfo("Secondary Proc Based VM Exec Controls (MSR_IA32_VMX_PROCBASED_CTLS2) : 0x%x", SecondaryProcBasedVmExecControls);Now we have a perfect EPT Table which virtualizes MMU and now all of the translations go through the EPT.Monitoring Page’s RWX ActivityThe next important topic is the monitoring of the page’s RWX. From the above section, you saw that we put each of the Read Access, Write Access and Execute Access to 1, but to use EPT’s monitoring features, we have to set some of them to 0 so that we get EPT Violation on each of the accesses mentioned above.Using these features (setting access to 0) has its difficulties by its nature, problems relating to IRQL, splitting, absence of the ability to use NT functions, synchronization, and deadlock are some of these problems and limitations.In this section we’re trying to solve these problem.Pre-allocating Buffers for VMX Root ModeAfter executing VMLAUNCH, we shouldn’t modify EPT Tables from Vmx non-root mode; that is because if we do it, then it might (and will) causes system inconsistency.This limitation and the fact that we couldn’t use any NT function in VMX Root Mode bring us new challenges.One of these challenges is that we might need to split a 2 MB Page into 4 KB pages, of course, another Page Table (PML1) is necessary to store the details of new 4 KB pages. I mean, we have to create another Page Table (PML1), and it needs a new memory.We can’t use ExAllocatePoolTag in Vmx root-mode as it’s an NT API. (you can use it in Vmx root-mode, and you’ll see that it sometimes work and sometimes halts the system - the reason is described in the Discussion section).The solution to this problem is using a previously allocated buffer from Vmx non-root mode and use it in Vmx root mode, so this brings us the first limitation to our hypervisor which is we have to start setting hooks from vmx non-root mode because we want to pre-allocate a buffer then we pass the buffer and hook settings to Vmx root-mode using a special Vmcalls.By the way, this is not an unsolvable limitation, for example, you can allocate 100 pages from Vmx non-root mode and use them whenever you want in Vmx root-mode, and it’s not necessarily a limitation anymore but for now, let’s assume that the caller should start setting hooks from Vmx non-root mode.Honestly, I wanted to make a mechanism for running code from Vmx root mode to Vmx non-root mode using NMI events; using this approach will resolve the problem of pre-allocating buffers, but for this part, let’s use pre-allocated buffers.Hyperplatform and Hvpp use the pre-allocated buffers.In this section and next sections we’re trying to complete a function called “EptPageHook”.There is a per-core global variable called “PreAllocatedMemoryDetails” in GuestState that is defined like this:typedef struct _VMX_NON_ROOT_MODE_MEMORY_ALLOCATOR{ PVOID PreAllocatedBuffer; // As we can't use ExAllocatePoolWithTag in VMX Root mode, this holds a pre-allocated buffer address // PreAllocatedBuffer == 0 indicates that it's not previously allocated} VMX_NON_ROOT_MODE_MEMORY_ALLOCATOR, * PVMX_NON_ROOT_MODE_MEMORY_ALLOCATOR;Now that we’re trying to hook, we’ll see whether the current core has a previously pre-allocated buffer or not. If it doesn’t have a buffer, then we allocate it using ExAllocatePoolWithTag. if (GuestState[LogicalCoreIndex].PreAllocatedMemoryDetails.PreAllocatedBuffer == NULL) { PreAllocBuff = ExAllocatePoolWithTag(NonPagedPool, sizeof(VMM_EPT_DYNAMIC_SPLIT), POOLTAG); if (!PreAllocBuff) { LogError("Insufficient memory for pre-allocated buffer"); return FALSE; } // Zero out the memory RtlZeroMemory(PreAllocBuff, sizeof(VMM_EPT_DYNAMIC_SPLIT)); // Save the pre-allocated buffer GuestState[LogicalCoreIndex].PreAllocatedMemoryDetails.PreAllocatedBuffer = PreAllocBuff; }Now we have two different states if we previously configured the VMCS with EPT and we’re already in a hypervisor then we have to ask, Vmx root-mode to set the hook for us (Setting hook after Vmlaunch); otherwise, we can modify it in a regular function as we don’t execute VMLAUNCH (with EPT) yet (Setting hook before Vmlaunch).By “with EPT,” I mean if we used this EPT in our hypervisor. For example, you might configure VMCS without EPTP, then you execute VMLAUNCH, and now you decide to create an EPT Table, this way doesn’t need Vmx root-mode to modify EPT Table, we can change it from Vmx non-root mode as we didn’t use this EPT Table yet.Setting hook before VmlaunchI prefer to do everything in a function so that EptVmxRootModePageHook can be used for both Vmx root-mode and non-root mode. Still, you shouldn’t directly call this function as it needs a preparing phase (instead, you can call EptPageHook).What we have to do is calling EptVmxRootModePageHook and a HasLaunched flag that determines whether we used our EPT in our Vmx operation our not. if (EptVmxRootModePageHook(TargetFunc, HasLaunched) == TRUE) { LogInfo("[*] Hook applied (VM has not launched)"); return TRUE; }I’ll describe EptVmxRootModePageHook in the section, Applying the Hook later.Setting hook after VmlaunchIf we’re already used this EPT in our Vmx operation, then we need to ask Vmx root-mode to modify the EPT Table for us; in other words, we have to call EptVmxRootModePageHook from Vmx root-mode, so it needs Vmcall.We have some additional things to do here, as I told you, each logical core has its own set of caches relating to EPT, so we have to invalidate all the cores’ EPT Tables immediately and of course it has to be done in Vmx non-root mode as we want to use NT APIs.To call EptVmxRootModePageHook from Vmx root-mode, we’ll use Vmcall with VMCALL_EXEC_HOOK_PAGE and also sent the functions virtual address (TargetFunc) as the first parameter. if (HasLaunched) { if (AsmVmxVmcall(VMCALL_EXEC_HOOK_PAGE, TargetFunc, NULL, NULL, NULL) == STATUS_SUCCESS) { LogInfo("Hook applied from VMX Root Mode"); // Now we have to notify all the core to invalidate their EPT HvNotifyAllToInvalidateEpt(); return TRUE; } }In Vmcall handler, we just call EptVmxRootModePageHook. case VMCALL_EXEC_HOOK_PAGE: { HookResult = EptVmxRootModePageHook(OptionalParam1, TRUE); if (HookResult) { VmcallStatus = STATUS_SUCCESS; } else { VmcallStatus = STATUS_UNSUCCESSFUL; } break; }Let’s get down to invalidation part,HvNotifyAllToInvalidateEpt uses KeIpiGenericCall which broadcasts HvInvalidateEptByVmcall on all the core./* Notify all core to invalidate their EPT */VOID HvNotifyAllToInvalidateEpt(){ // Let's notify them all KeIpiGenericCall(HvInvalidateEptByVmcall, EptState-&gt;EptPointer.Flags);}As the invalidation should be within vmx root-mode (INVEPT instruction is only valid in vmx root-mode) thus HvInvalidateEptByVmcall uses Vmcall with VMCALL_INVEPT_ALL_CONTEXT and VMCALL_INVEPT_SINGLE_CONTEXT to notify vmx root-mode about invalidation./* Invalidate EPT using Vmcall (should be called from Vmx non root mode) */VOID HvInvalidateEptByVmcall(UINT64 Context){ if (Context == NULL) { // We have to invalidate all contexts AsmVmxVmcall(VMCALL_INVEPT_ALL_CONTEXT, NULL, NULL, NULL); } else { // We have to invalidate all contexts AsmVmxVmcall(VMCALL_INVEPT_SINGLE_CONTEXT, Context, NULL, NULL); }}The Vmcall handler uses InveptSingleContext and InveptAllContexts to invalidate the contexts; we’ll talk about invalidation in details later in this part (Invalidating Translations Derived from EPT (INVEPT)). case VMCALL_INVEPT_SINGLE_CONTEXT: { InveptSingleContext(OptionalParam1); VmcallStatus = STATUS_SUCCESS; break; } case VMCALL_INVEPT_ALL_CONTEXT: { InveptAllContexts(); VmcallStatus = STATUS_SUCCESS; break; }Finding a Page’s entry in EPT TablesLet’s see how we can find addresses in PML1, PML2, PML3 and PML4.Finding PML4, PML3, PML2 entriesWe want to find PML2 entry, for finding PML2, first, we have to find PML4 and PML3.We used an ordinal approach to map the physical addresses so all the physical addresses are stored in the same way so we need some definitions to find the index of the entries from tables.Here’s the definitions.// Index of the 1st paging structure (4096 byte)#define ADDRMASK_EPT_PML1_INDEX(_VAR_) ((_VAR_ &amp; 0x1FF000ULL) &gt;&gt; 12)// Index of the 2nd paging structure (2MB)#define ADDRMASK_EPT_PML2_INDEX(_VAR_) ((_VAR_ &amp; 0x3FE00000ULL) &gt;&gt; 21)// Index of the 3rd paging structure (1GB)#define ADDRMASK_EPT_PML3_INDEX(_VAR_) ((_VAR_ &amp; 0x7FC0000000ULL) &gt;&gt; 30)// Index of the 4th paging structure (512GB)#define ADDRMASK_EPT_PML4_INDEX(_VAR_) ((_VAR_ &amp; 0xFF8000000000ULL) &gt;&gt; 39)After finding the indexes, we have to find the virtual address to that index so we can modify the page table. It’s because in protected mode we can’t access physical addresses.The following code, first finds the indexes then return the virtual address from the EPT Page Table to that indexes./* Get the PML2 entry for this physical address. */PEPT_PML2_ENTRY EptGetPml2Entry(PVMM_EPT_PAGE_TABLE EptPageTable, SIZE_T PhysicalAddress){ SIZE_T Directory, DirectoryPointer, PML4Entry; PEPT_PML2_ENTRY PML2; Directory = ADDRMASK_EPT_PML2_INDEX(PhysicalAddress); DirectoryPointer = ADDRMASK_EPT_PML3_INDEX(PhysicalAddress); PML4Entry = ADDRMASK_EPT_PML4_INDEX(PhysicalAddress); // Addresses above 512GB are invalid because it is &gt; physical address bus width if (PML4Entry &gt; 0) { return NULL; } PML2 = &amp;EptPageTable-&gt;PML2[DirectoryPointer][Directory]; return PML2;}Finding PML1 entryFor PML1, we have the same approach. First, we find the PML2 the same as above. Then we check to see if the PML2 is split or not. It’s because if it’s not split before then we don’t have PML1 and it’s 3-level paging.Finally, as we saved physical addresses contiguously, so we can find the index using ADDRMASK_EPT_PML1_INDEX (as defined above) and then return the virtual address to that page entry./* Get the PML1 entry for this physical address if the page is split. Return NULL if the address is invalid or the page wasn't already split. */PEPT_PML1_ENTRY EptGetPml1Entry(PVMM_EPT_PAGE_TABLE EptPageTable, SIZE_T PhysicalAddress){ SIZE_T Directory, DirectoryPointer, PML4Entry; PEPT_PML2_ENTRY PML2; PEPT_PML1_ENTRY PML1; PEPT_PML2_POINTER PML2Pointer; Directory = ADDRMASK_EPT_PML2_INDEX(PhysicalAddress); DirectoryPointer = ADDRMASK_EPT_PML3_INDEX(PhysicalAddress); PML4Entry = ADDRMASK_EPT_PML4_INDEX(PhysicalAddress); // Addresses above 512GB are invalid because it is &gt; physical address bus width if (PML4Entry &gt; 0) { return NULL; } PML2 = &amp;EptPageTable-&gt;PML2[DirectoryPointer][Directory]; // Check to ensure the page is split if (PML2-&gt;LargePage) { return NULL; } // Conversion to get the right PageFrameNumber. // These pointers occupy the same place in the table and are directly convertable. PML2Pointer = (PEPT_PML2_POINTER)PML2; // If it is, translate to the PML1 pointer PML1 = (PEPT_PML1_ENTRY)PhysicalAddressToVirtualAddress((PVOID)(PML2Pointer-&gt;PageFrameNumber * PAGE_SIZE)); if (!PML1) { return NULL; } // Index into PML1 for that address PML1 = &amp;PML1[ADDRMASK_EPT_PML1_INDEX(PhysicalAddress)]; return PML1;}Splitting 2 MB Pages to 4 KB PagesAs you know, in all of our hypervisor parts we used 3 LEVEL paging (PML4, PML3, PML2) and our granularity is 2 MB. Having pages with 2 MB granularity is not adequate for monitoring purposes because we might get lots of unrelated violations caused by non-relevant areas.To fix these kind of problems, we use PML1 and 4 KB granularity.This is where we might need an additional buffer and as we’re in vmx root-mode, then we’ll use our previously allocated buffers.First, we get the actual entry from PML2 and check if it’s already a 4 KB defined table, if it previously split then nothing to do, we can use it. // Find the PML2 entry that's currently used TargetEntry = EptGetPml2Entry(EptPageTable, PhysicalAddress); if (!TargetEntry) { LogError("An invalid physical address passed"); return FALSE; } // If this large page is not marked a large page, that means it's a pointer already. // That page is therefore already split. if (!TargetEntry-&gt;LargePage) { return TRUE; }If not, we set PreAllocatedMemoryDetails’s PreAllocatedBuffer to null so that next time the pre-allocator allocates a new buffer for this purpose. // Free previous buffer GuestState[CoreIndex].PreAllocatedMemoryDetails.PreAllocatedBuffer = NULL;Then, we should fill the PML1 with an RWX template and then split our 2 MB page into 4 KB chunks (compute 4 KB physical addresses and fill the PageFrameNumber). // Point back to the entry in the dynamic split for easy reference for which entry that dynamic split is for. NewSplit-&gt;Entry = TargetEntry; // Make a template for RWX EntryTemplate.Flags = 0; EntryTemplate.ReadAccess = 1; EntryTemplate.WriteAccess = 1; EntryTemplate.ExecuteAccess = 1; // Copy the template into all the PML1 entries __stosq((SIZE_T*)&amp;NewSplit-&gt;PML1[0], EntryTemplate.Flags, VMM_EPT_PML1E_COUNT); // Set the page frame numbers for identity mapping. for (EntryIndex = 0; EntryIndex &lt; VMM_EPT_PML1E_COUNT; EntryIndex++) { // Convert the 2MB page frame number to the 4096 page entry number plus the offset into the frame. NewSplit-&gt;PML1[EntryIndex].PageFrameNumber = ((TargetEntry-&gt;PageFrameNumber * SIZE_2_MB) / PAGE_SIZE) + EntryIndex; }Finally, create a new PML2 entry (with LargePage = 0) and replace it with the previous PML2 entry.Also keep the track of allocated memory to de-allocate it when we want to run vmxoff. // Allocate a new pointer which will replace the 2MB entry with a pointer to 512 4096 byte entries. NewPointer.Flags = 0; NewPointer.WriteAccess = 1; NewPointer.ReadAccess = 1; NewPointer.ExecuteAccess = 1; NewPointer.PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&amp;NewSplit-&gt;PML1[0]) / PAGE_SIZE; // Add our allocation to the linked list of dynamic splits for later deallocation InsertHeadList(&amp;EptPageTable-&gt;DynamicSplitList, &amp;NewSplit-&gt;DynamicSplitList); // Now, replace the entry in the page table with our new split pointer. RtlCopyMemory(TargetEntry, &amp;NewPointer, sizeof(NewPointer));The following function represent the full code for splitting 2 MB pages to 4 KB pages./* Split 2MB (LargePage) into 4kb pages */BOOLEAN EptSplitLargePage(PVMM_EPT_PAGE_TABLE EptPageTable, PVOID PreAllocatedBuffer, SIZE_T PhysicalAddress, ULONG CoreIndex){ PVMM_EPT_DYNAMIC_SPLIT NewSplit; EPT_PML1_ENTRY EntryTemplate; SIZE_T EntryIndex; PEPT_PML2_ENTRY TargetEntry; EPT_PML2_POINTER NewPointer; // Find the PML2 entry that's currently used TargetEntry = EptGetPml2Entry(EptPageTable, PhysicalAddress); if (!TargetEntry) { LogError("An invalid physical address passed"); return FALSE; } // If this large page is not marked a large page, that means it's a pointer already. // That page is therefore already split. if (!TargetEntry-&gt;LargePage) { return TRUE; } // Free previous buffer GuestState[CoreIndex].PreAllocatedMemoryDetails.PreAllocatedBuffer = NULL; // Allocate the PML1 entries NewSplit = (PVMM_EPT_DYNAMIC_SPLIT)PreAllocatedBuffer; if (!NewSplit) { LogError("Failed to allocate dynamic split memory"); return FALSE; } RtlZeroMemory(NewSplit, sizeof(VMM_EPT_DYNAMIC_SPLIT)); // Point back to the entry in the dynamic split for easy reference for which entry that dynamic split is for. NewSplit-&gt;Entry = TargetEntry; // Make a template for RWX EntryTemplate.Flags = 0; EntryTemplate.ReadAccess = 1; EntryTemplate.WriteAccess = 1; EntryTemplate.ExecuteAccess = 1; // Copy the template into all the PML1 entries __stosq((SIZE_T*)&amp;NewSplit-&gt;PML1[0], EntryTemplate.Flags, VMM_EPT_PML1E_COUNT); // Set the page frame numbers for identity mapping. for (EntryIndex = 0; EntryIndex &lt; VMM_EPT_PML1E_COUNT; EntryIndex++) { // Convert the 2MB page frame number to the 4096 page entry number plus the offset into the frame. NewSplit-&gt;PML1[EntryIndex].PageFrameNumber = ((TargetEntry-&gt;PageFrameNumber * SIZE_2_MB) / PAGE_SIZE) + EntryIndex; } // Allocate a new pointer which will replace the 2MB entry with a pointer to 512 4096 byte entries. NewPointer.Flags = 0; NewPointer.WriteAccess = 1; NewPointer.ReadAccess = 1; NewPointer.ExecuteAccess = 1; NewPointer.PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&amp;NewSplit-&gt;PML1[0]) / PAGE_SIZE; // Add our allocation to the linked list of dynamic splits for later deallocation InsertHeadList(&amp;EptPageTable-&gt;DynamicSplitList, &amp;NewSplit-&gt;DynamicSplitList); // Now, replace the entry in the page table with our new split pointer. RtlCopyMemory(TargetEntry, &amp;NewPointer, sizeof(NewPointer)); return TRUE;}Applying the HookEptVmxRootModePageHook is one of the important parts of the EPT.First, we check to prohibit calling this function from vmx root-mode when the pre-allocated buffer isn’t available. // Check whether we are in VMX Root Mode or Not LogicalCoreIndex = KeGetCurrentProcessorIndex(); if (GuestState[LogicalCoreIndex].IsOnVmxRootMode &amp;&amp; GuestState[LogicalCoreIndex].PreAllocatedMemoryDetails.PreAllocatedBuffer == NULL &amp;&amp; HasLaunched) { return FALSE; }Then we align the address as the addresses in page tables are aligned. VirtualTarget = PAGE_ALIGN(TargetFunc); PhysicalAddress = (SIZE_T)VirtualAddressToPhysicalAddress(VirtualTarget);We’ll check about the granularity and split it if it’s a LargePage (more details at the next section - Splitting 2 MB Pages to 4 KB Pages ). // Set target buffer TargetBuffer = GuestState[LogicalCoreIndex].PreAllocatedMemoryDetails.PreAllocatedBuffer; if (!EptSplitLargePage(EptState-&gt;EptPageTable, TargetBuffer, PhysicalAddress, LogicalCoreIndex)) { LogError("Could not split page for the address : 0x%llx", PhysicalAddress); return FALSE; }Then find the PML1 entry of the requested page and as it’s already divided into 4 KB pages so PML1 is available. // Pointer to the page entry in the page table. TargetPage = EptGetPml1Entry(EptState-&gt;EptPageTable, PhysicalAddress); // Ensure the target is valid. if (!TargetPage) { LogError("Failed to get PML1 entry of the target address"); return FALSE; } // Save the original permissions of the page OriginalEntry = *TargetPage;Now, we change the attributes related to the PML1 entry, this the most interesting part of this function, for example, you can disable Write access to a 4 KB page, in our case, I disabled instruction execution (fetch) from the target page. /* * Lastly, mark the entry in the table as no execute. This will cause the next time that an instruction is * fetched from this page to cause an EPT violation exit. This will allow us to swap in the fake page with our * hook. */ OriginalEntry.ReadAccess = 1; OriginalEntry.WriteAccess = 1; OriginalEntry.ExecuteAccess = 0; // Apply the hook to EPT TargetPage-&gt;Flags = OriginalEntry.Flags;If we are in vmx root-mode then the TLB caches have to be invalidated. // Invalidate the entry in the TLB caches so it will not conflict with the actual paging structure. if (HasLaunched) { // Uncomment in order to invalidate all the contexts // LogInfo("INVEPT Results : 0x%x\\n", InveptAllContexts()); Descriptor.EptPointer = EptState-&gt;EptPointer.Flags; Descriptor.Reserved = 0; AsmInvept(1, &amp;Descriptor); }Done ! The hook is applied.Handling hooked pages’ vm-exitsFirst, we’re trying to align the Guest Physical Address (remember from the Ept Violation that we read the GUEST_PHYSICAL_ADDRESS from Vmcs). This because we’re only able to find aligned physical addresses from our EPT Table (we don’t want to iterate over them !). PhysicalAddress = PAGE_ALIGN(GuestPhysicalAddr);Now, as I described above, we find the PML1 entry relating to this physical address. We’re not looking for PML2 that’s because, if we reached here then we probably split 2 MB pages to 4 KB pages and we have PML1 instead of PML2. TargetPage = EptGetPml1Entry(EptState-&gt;EptPageTable, PhysicalAddress); // Ensure the target is valid. if (!TargetPage) { LogError("Failed to get PML1 entry for target address"); return FALSE; }Finally, we check if the violation is caused by an Execute Access (based on Exit Qualification) and the violated page has Execute Access to 0, if so, then just make the page’s entry in PML1 executable and invalidate the cache so that this modification takes effect.Don’t forget to tell our vm-exit handler to avoid skipping the current instruction (avoid adding Instruction Length to Guest RIP) and execute it one more time as the instruction didn’t execute. // If the violation was due to trying to execute a non-executable page, that means that the currently // swapped in page is our original RW page. We need to swap in the hooked executable page (fake page) if (!ViolationQualification.EptExecutable &amp;&amp; ViolationQualification.ExecuteAccess) { TargetPage-&gt;ExecuteAccess = 1; // InveptAllContexts(); INVEPT_DESCRIPTOR Descriptor; Descriptor.EptPointer = EptState-&gt;EptPointer.Flags; Descriptor.Reserved = 0; AsmInvept(1, &amp;Descriptor); // Redo the instruction GuestState[KeGetCurrentProcessorNumber()].IncrementRip = FALSE; LogInfo("Set the Execute Access of a page (PFN = 0x%llx) to 1", TargetPage-&gt;PageFrameNumber); return TRUE; }All in all, we have the following handler./* Check if this exit is due to a violation caused by a currently hooked page. Returns FALSE * if the violation was not due to a page hook. * * If the memory access attempt was RW and the page was marked executable, the page is swapped with * the original page. * * If the memory access attempt was execute and the page was marked not executable, the page is swapped with * the hooked page. */BOOLEAN EptHandlePageHookExit(VMX_EXIT_QUALIFICATION_EPT_VIOLATION ViolationQualification, UINT64 GuestPhysicalAddr){ SIZE_T PhysicalAddress; PVOID VirtualTarget; PEPT_PML1_ENTRY TargetPage; /* Translate the page from a physical address to virtual so we can read its memory. This function will return NULL if the physical address was not already mapped in virtual memory. */ PhysicalAddress = PAGE_ALIGN(GuestPhysicalAddr); if (!PhysicalAddress) { LogError("Target address could not be mapped to physical memory"); return FALSE; } TargetPage = EptGetPml1Entry(EptState-&gt;EptPageTable, PhysicalAddress); // Ensure the target is valid. if (!TargetPage) { LogError("Failed to get PML1 entry for target address"); return FALSE; } // If the violation was due to trying to execute a non-executable page, that means that the currently // swapped in page is our original RW page. We need to swap in the hooked executable page (fake page) if (!ViolationQualification.EptExecutable &amp;&amp; ViolationQualification.ExecuteAccess) { TargetPage-&gt;ExecuteAccess = 1; // InveptAllContexts(); INVEPT_DESCRIPTOR Descriptor; Descriptor.EptPointer = EptState-&gt;EptPointer.Flags; Descriptor.Reserved = 0; AsmInvept(1, &amp;Descriptor); // Redo the instruction GuestState[KeGetCurrentProcessorNumber()].IncrementRip = FALSE; LogInfo("Set the Execute Access of a page (PFN = 0x%llx) to 1", TargetPage-&gt;PageFrameNumber); return TRUE; } LogError("Invalid page swapping logic in hooked page"); return FALSE;}Invalidating Translations Derived from EPT (INVEPT)Now that we implemented EPT, there is another problem here. It’s the software’s responsibility to invalidate the caches. For example, we changed the Execute access attribute of a particular page, now we have to tell the CPU that we changed something and it has to invalidate its cache, or in another way, we get EPT Violation for Execute access of a special page and now we no longer need these EPT Violations for this page. Hence, we set the Execute Access of this page to 1; thus, we have to tell our processor that we changed something in our page table. Are you confused? Let me explain it one more time.Imagine we access the physical 0x1000, and it’ll get translated to host physical address 0x1000 (based on 1:1 mapping). Next time, if we access 0x1000, the CPU won’t send the request to the memory bus but uses cached memory instead. It’s faster. Now let’s say we changed the EPT Physical Address of a page to point to different EPT PD or change the attributes (Read, Write, Execute) of one of the EPT tables, now we have to tell the processor that your cache is invalid and that’s what exactly INVEPT performs.There is a problem here; we have to separately tell each logical core that it needs to invalidate its EPT cache. In other words, each core has to execute INVEPT on its vmx root-mode. We’ll solve these problems later in this part.There are two types of TLB Invalidation for hypervisors. VMX-specific TLB-management instructions: INVEPT - Invalidate cached Extended Page Table (EPT) mappings in the processor to synchronize address translation in virtual machines with memory-resident EPT pages. INVVPID - Invalidate cached mappings of address translation based on the Virtual Processor ID (VPID). We’ll talk about INVVPID in detail in part 8.So in case if you wouldn’t perform INVEPT after changing EPT’s structures, you would be risking that the CPU would reuse old translations.Any change to EPT structure needs INVEPT, but switching EPT (or VMCS) doesn’t require INVEPT because that translation will be “tagged” with the changed EPTP in the cache.Now we have two terms here, Single-Context and All-Context.typedef enum _INVEPT_TYPE{ SINGLE_CONTEXT = 0x00000001, ALL_CONTEXTS = 0x00000002};And we have a assembly function which generally executes the INVEPT.; Error codes : VMX_ERROR_CODE_SUCCESS = 0 VMX_ERROR_CODE_FAILED_WITH_STATUS = 1 VMX_ERROR_CODE_FAILED = 2AsmInvept PROC PUBLIC invept rcx, oword ptr [rdx] jz @jz jc @jc xor rax, rax ret @jz: mov rax, VMX_ERROR_CODE_FAILED_WITH_STATUS ret @jc: mov rax, VMX_ERROR_CODE_FAILED retAsmInvept ENDPFrom the above code, RCX describes the Type (which can be one of the all-context and single-context), and RDX is the descriptor for INVEPT.The following structure is the descriptor for INVEPT as described in Intel SDM.typedef struct _INVEPT_DESC{ EPTP EptPointer; UINT64 Reserveds;}INVEPT_DESC, * PINVEPT_DESC;We’ll use our assembly function in another function called Invept./* Invoke the Invept instruction */unsigned char Invept(UINT32 Type, INVEPT_DESC* Descriptor){ if (!Descriptor) { INVEPT_DESC ZeroDescriptor = { 0 }; Descriptor = &amp;ZeroDescriptor; } return AsmInvept(Type, Descriptor);}It’s time to see what are so called “All-Context“and “Single-Context”.Invalidating All ContextsAll-Context means that you invalidate all EPT-derived translations. (for every-VM)./* Invalidates all contexts in ept cache table */unsigned char InveptAllContexts(){ return Invept(ALL_CONTEXTS, NULL);}Note: For every-VM, I mean every VM for a particular logical core; each core can have multiple VMCSs and EPT tables and switches between them. It doesn’t relate to the EPT table on other cores.Invalidating Single ContextSingle-Context means that you invalidate all EPT-derived translations based on a single EPTP (in short: for a single VM in a logical core)./* Invalidates a single context in ept cache table */unsigned char InveptSingleContext(UINT64 EptPointer){ INVEPT_DESC Descriptor = { EptPointer, 0 }; return Invept(SINGLE_CONTEXT, &amp;Descriptor);}Broadcasting Invept to all logical cores simultaneouslyLet say you have two cores and 1 EPTP. At some point you change EPT on core one; thus you have to invalidate EPT on all cores at that point. If you remember from the previous section, we have to notify all cores to invalidate their EPT caches using something like KeIpiGenericCall, and the problem is you can’t call KeIpiGenericCall from VM-exit for apparent reasons - you shouldn’t call any NT APIs in Vm-exit. Calling this API from Vm-exit likely causes deadlock.We can get around this by modifying APIC and creating our custom IPI call routine. We’ll come across APIC virtualization in the future parts. Still, for now, if we want to change EPT for all cores, then we can call KeIpiGenericCall from regular kernel-mode (not vmx root-mode) and in that callback we perform Vmcall to tell our processor to invalidate its cache in vmx root-mode.It’s because if we don’t immediately invalidate EPT, then we might lose some EPT Violations. This is because each logical core will have a different memory view.If you remember from the above sections (EptPageHook), we’d checked whether the core is already on vmx operation (vmlaunch is executed). If it launched, then we used Vmcall to tell the processor about modifying EPT Table from the vmx root-mode. Right after returning from Vmcall, we called HvNotifyAllToInvalidateEpt to tell all the cores about new invalidation in their EPT caches (remember, we’re not on vmx root-mode anymore, we’re in vmx non-root mode so we can use NT APIs as it’s a regular kernel function). if (HasLaunched) { if (AsmVmxVmcall(VMCALL_EXEC_HOOK_PAGE, TargetFunc, NULL, NULL, NULL) == STATUS_SUCCESS) { LogInfo("Hook applied from VMX Root Mode"); // Now we have to notify all the core to invalidate their EPT HvNotifyAllToInvalidateEpt(); return TRUE; } }HvNotifyAllToInvalidateEpt, on the other hand, uses KeIpiGenericCall, and this function broadcasts HvInvalidateEptByVmcall on all the logical cores and also pass our current EPTP to this function./* Notify all core to invalidate their EPT */VOID HvNotifyAllToInvalidateEpt(){ // Let's notify them all KeIpiGenericCall(HvInvalidateEptByVmcall, EptState-&gt;EptPointer.Flags);}HvInvalidateEptByVmcall decides whether the caller needs an all-contexts invalidation or a single-context invalidation, and based on that, it calls the Vmcall with adequate Vmcall number. Note that our hypervisor doesn’t have multiple EPTPs, so it’s always a single-context Vmcall./* Invalidate EPT using Vmcall (should be called from Vmx non root mode) */VOID HvInvalidateEptByVmcall(UINT64 Context){ if (Context == NULL) { // We have to invalidate all contexts AsmVmxVmcall(VMCALL_INVEPT_ALL_CONTEXT, NULL, NULL, NULL); } else { // We have to invalidate all contexts AsmVmxVmcall(VMCALL_INVEPT_SINGLE_CONTEXT, Context, NULL, NULL); }}Finally, Vmcall handler calls InveptAllContexts or HvInvalidateEptByVmcall based on Vmcall number in vmx root-mode. case VMCALL_INVEPT_SINGLE_CONTEXT: { c(OptionalParam1); VmcallStatus = STATUS_SUCCESS; break; } case VMCALL_INVEPT_ALL_CONTEXT: { InveptAllContexts(); VmcallStatus = STATUS_SUCCESS; break; }The last thing is you can’t execute INVEPT in vmx non-root mode as it causes a Vm-exit with EXIT_REASON_INVEPT (0x32) and it doesn’t have any effect.That’s it all for INVEPT.Fixing Previous Design IssuesThe rest of the topic is nothing new. We want to improve our hypervisor and fix some issues from the previous parts and also support some new features and defeat some deadlocks and synchronization problems that exist in our previous parts.Support to more than 64 logical coresPrevious versions of Hypervisor From Scratch has the problem of not supporting more than 32 cores (32*2 logical cores). This is because we used KeSetSystemAffinityThread, and it gives a KAFFINITY as its argument, and it’s a 64 Bit long variable mask.We used KeSetSystemAffinityThread when we broadcast Vmptrld, Vmclear, VMCS Setup (Vmwrite), Vmlaunch, and Vmxoff to all cores.The best approach to run on all logical cores is letting Windows (API) execute them on each core simultaneously. This involves raising IRQL on each core.We have different options here; first, we can use KeGenericCallDpc. It’s an undocumented function which schedules CPU-specific DPCs on all CPUs.The definition of KeGenericCallDpc is as bellow.KeGenericCallDpc( _In_ PKDEFERRED_ROUTINE Routine, _In_opt_ PVOID Context);The first argument is the address of the target function, which we want to execute on each core, and context is an optional parameter to this function.In the target function, we call KeSignalCallDpcSynchronize and KeSignalCallDpcDone to avoid synchronization problems so that all the cores finish at the same time.KeSignalCallDpcSynchronize waits for all DPCs to synchronize at that point (where we call KeSignalCallDpcSynchronize).LOGICALKeSignalCallDpcSynchronize( _In_ PVOID SystemArgument2);Finally, KeSignalCallDpcDone marks the DPC as being complete.VOIDKeSignalCallDpcDone( _In_ PVOID SystemArgument1);The above two functions have to be executed as the last step (when everything completes) in the target function.Another option is using KeIpiGenericCall, this routine causes the specified function to run on all processors simultaneously, and it’s documented. I used the first approach in Hypervisor From Scratch, and these updates are applied to both the initialization phase and the Vmxoff phase.Synchronization problem in exiting VMXAs we now support more than 64 logical cores using DPCs, and as most of the functions are executed simultaneously, we have some problems with our previously designed routines. For example, in the previous parts, I used gGuestRSP and gGuestRIP to return to the former state. Using one global variable on all cores causes errors as one core might save its RIP and RSP (core 1), then other core (core 2) keeps the same data in these variables, When the first core (core 1) tries to restore the state, it’s the state of second core (core 2), and you’ll see a BSOD :D .In order to solve this problem, we have to store a per-core structure which saves the Guest RIP and Guest RSP. The following structure is used for this purpose.typedef struct _VMX_VMXOFF_STATE{ BOOLEAN IsVmxoffExecuted; // Shows whether the VMXOFF executed or not UINT64 GuestRip; // Rip address of guest to return UINT64 GuestRsp; // Rsp address of guest to return} VMX_VMXOFF_STATE, * PVMX_VMXOFF_STATE;We add the above structure to VIRTUAL_MACHINE_STATE as it’s a per-core structure.typedef struct _VIRTUAL_MACHINE_STATE{... VMX_VMXOFF_STATE VmxoffState; // Shows the vmxoff state of the guest...} VIRTUAL_MACHINE_STATE, * PVIRTUAL_MACHINE_STATE;We need to broadcast Vmxoff to all of the logical cores. This is done by using the HvTerminateVmx; this function is called once and broadcast HvDpcBroadcastTerminateGuest to all logical cores and de-allocates (free) all the EPT related tables and pre-allocated buffers./* Terminate Vmx on all logical cores. */VOID HvTerminateVmx(){ // Broadcast to terminate Vmx KeGenericCallDpc(HvDpcBroadcastTerminateGuest, 0x0); /* De-allocatee global variables */ // Free each split FOR_EACH_LIST_ENTRY(EptState-&gt;EptPageTable, DynamicSplitList, VMM_EPT_DYNAMIC_SPLIT, Split) ExFreePoolWithTag(Split, POOLTAG); FOR_EACH_LIST_ENTRY_END(); // Free Identity Page Table MmFreeContiguousMemory(EptState-&gt;EptPageTable); // Free GuestState ExFreePoolWithTag(GuestState, POOLTAG); // Free EptState ExFreePoolWithTag(EptState, POOLTAG);}HvDpcBroadcastTerminateGuest is responsible for synchronizing DPCs and calling a VMX function call VmxTerminate./* The broadcast function which terminate the guest. */VOID HvDpcBroadcastTerminateGuest(struct _KDPC* Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2){ // Terminate Vmx using Vmcall if (!VmxTerminate()) { LogError("There were an error terminating Vmx"); } // Wait for all DPCs to synchronize at this point KeSignalCallDpcSynchronize(SystemArgument2); // Mark the DPC as being complete KeSignalCallDpcDone(SystemArgument1);}VmxTerminate de-allocates per-core allocated regions like the Vmxon region, Vmcs region, Vmm Stack, and Msr Bitmap. As we implemented our Vmcall mechanism, we can use Vmcall to request a vmxoff from the vmx root mode (instead of what we’ve done in the previous version with CPUID Handler). So it executes AsmVmxVmcall with VMCALL_VMXOFF on each core, and each core will run vmxoff separately./* Broadcast to terminate VMX on all logical cores */BOOLEAN VmxTerminate(){ int CurrentCoreIndex; NTSTATUS Status; // Get the current core index CurrentCoreIndex = KeGetCurrentProcessorNumber(); LogInfo("\\tTerminating VMX on logical core %d", CurrentCoreIndex); // Execute Vmcall to to turn off vmx from Vmx root mode Status = AsmVmxVmcall(VMCALL_VMXOFF, NULL, NULL, NULL); // Free the destination memory MmFreeContiguousMemory(GuestState[CurrentCoreIndex].VmxonRegionVirtualAddress); MmFreeContiguousMemory(GuestState[CurrentCoreIndex].VmcsRegionVirtualAddress); ExFreePoolWithTag(GuestState[CurrentCoreIndex].VmmStack, POOLTAG); ExFreePoolWithTag(GuestState[CurrentCoreIndex].MsrBitmapVirtualAddress, POOLTAG); if (Status == STATUS_SUCCESS) { return TRUE; } return FALSE;}Our Vmcall handler calls VmxVmxoff, and as this function is executed under vmx root-mode, so it’s allowed to run VMXOFF. This function also saves the GuestRip and GuestRsp into the per-core VMX_VMXOFF_STATE structure. This is where we solved the problem as we’re not using a shared global variable anymore. It also sets IsVmxoffExecuted, which indicates whether the logical core is on VMX operation or it left the VMX operation by executing VMXOFF.The VmxVmxoff is implemented like this :/* Prepare and execute Vmxoff instruction */VOID VmxVmxoff(){ int CurrentProcessorIndex; UINT64 GuestRSP; // Save a pointer to guest rsp for times that we want to return to previous guest stateS UINT64 GuestRIP; // Save a pointer to guest rip for times that we want to return to previous guest state UINT64 GuestCr3; UINT64 ExitInstructionLength; // Initialize the variables ExitInstructionLength = 0; GuestRIP = 0; GuestRSP = 0; CurrentProcessorIndex = KeGetCurrentProcessorNumber(); /* According to SimpleVisor : Our callback routine may have interrupted an arbitrary user process, and therefore not a thread running with a system-wide page directory. Therefore if we return back to the original caller after turning off VMX, it will keep our current "host" CR3 value which we set on entry to the PML4 of the SYSTEM process. We want to return back with the correct value of the "guest" CR3, so that the currently executing process continues to run with its expected address space mappings. */ __vmx_vmread(GUEST_CR3, &amp;GuestCr3); __writecr3(GuestCr3); // Read guest rsp and rip __vmx_vmread(GUEST_RIP, &amp;GuestRIP); __vmx_vmread(GUEST_RSP, &amp;GuestRSP); // Read instruction length __vmx_vmread(VM_EXIT_INSTRUCTION_LEN, &amp;ExitInstructionLength); GuestRIP += ExitInstructionLength; // Set the previous registe states GuestState[CurrentProcessorIndex].VmxoffState.GuestRip = GuestRIP; GuestState[CurrentProcessorIndex].VmxoffState.GuestRsp = GuestRSP; // Notify the Vmexit handler that VMX already turned off GuestState[CurrentProcessorIndex].VmxoffState.IsVmxoffExecuted = TRUE; // Execute Vmxoff __vmx_off();}As we return to vm-exit handler, we check whether we left the VMX opeation or not. if (GuestState[CurrentProcessorIndex].VmxoffState.IsVmxoffExecuted) { return TRUE; }We also define two other functions called “HvReturnStackPointerForVmxoff” and “HvReturnInstructionPointerForVmxoff”, which find the logical core index and returns the corresponding stack pointer and RIP to return.HvReturnStackPointerForVmxoff is :/* Returns the stack pointer, to change in the case of Vmxoff */UINT64 HvReturnStackPointerForVmxoff(){ return GuestState[KeGetCurrentProcessorNumber()].VmxoffState.GuestRsp;}And HvReturnInstructionPointerForVmxoff is:/* Returns the instruction pointer, to change in the case of Vmxoff */UINT64 HvReturnInstructionPointerForVmxoff(){ return GuestState[KeGetCurrentProcessorNumber()].VmxoffState.GuestRip;}Eventually, when we detect that we left the vmx operation, instead of executing VMRESUME we’ll run AsmVmxoffHandler, this function calls the HvReturnStackPointerForVmxoff and HvReturnInstructionPointerForVmxoff and puts the value of RSP and RIP after the general-purpose registers thus when we restore the general-purpose registers, we can pop the RSP from the stack and return to the previous address (ret) and continue normal execution.AsmVmxoffHandler PROC sub rsp, 020h ; shadow space call HvReturnStackPointerForVmxoff add rsp, 020h ; remove for shadow space mov [rsp+088h], rax ; now, rax contains rsp sub rsp, 020h ; shadow space call HvReturnInstructionPointerForVmxoff add rsp, 020h ; remove for shadow space mov rdx, rsp ; save current rsp mov rbx, [rsp+088h] ; read rsp again mov rsp, rbx push rax ; push the return address as we changed the stack, we push ; it to the new stack mov rsp, rdx ; restore previous rsp sub rbx,08h ; we push sth, so we have to add (sub) +8 from previous stack ; also rbx already contains the rsp mov [rsp+088h], rbx ; move the new pointer to the current stack RestoreState: pop rax pop rcx pop rdx pop rbx pop rbp ; rsp pop rbp pop rsi pop rdi pop r8 pop r9 pop r10 pop r11 pop r12 pop r13 pop r14 pop r15 popfq pop rsp ; restore rsp ret ; jump back to where we called VmcallAsmVmxoffHandler ENDPAs you can see, we no longer have the problem of using a global variable among all the cores.The issues relating to the Meltdown mitigationAs you know, EXIT_REASON_CR_ACCESS is one of the reasons that might cause VM-Exit (Especially if you’re subject to 1-setting of CRs in your VMCS). Hypervisors used to save all the general-purpose registers every time a VM-Exit occurs and then restore it at the next VMRESUME.In the previous versions of our driver, we ignored RSP and save some trash instead of it, that’s because RSP of guest is already saved in GUEST_RSP in VMCS. After VMRESUME, it’s loaded automatically, and you know, our current RSP is invalid (it’s host RSP).After meltdown mitigation, Windows uses MOV CR3, RSP, and as we saved trash instead of RSP, then you change CR3 to an invalid value, and it silently crashes with TRIPLE FAULT VM-Exit. It won’t give you the exact error.For fixing this issue, we add the following code to HvHandleControlRegisterAccess, so each time when a vm-exit occurs, we change the RSP to the correct value. /* Because its RSP and as we didn't save RSP correctly (because of pushes) so we have make it points to the GUEST_RSP */ if (CrExitQualification-&gt;Fields.Register == 4) { __vmx_vmread(GUEST_RSP, &amp;GuestRsp); *RegPtr = GuestRsp; }Previously, this was mentioned by Alex, for more information, you can read this article.Some tips for debugging hypervisorsAlways try to test your hypervisor in a uni-core system. If it works then, you can check it on a multi-core, so when something doesn’t work on multi-core and works on uni-core, then know that it’s a synchronization problem.Don’t try to call Nt functions in Vmx root mode. Most of NT functions are not suitable to run in a high IRQL, so if you use it, it leads to weird behavior and crashes the whole or system is halted.For more information, I really recommend reading Hyperplatform’s User Document (4.4. Coding Tips).Let’s Test it!Let’s see how we can test our hypervisor,How to test?In order to test our new hypervisor, we have two scenarios, and the following codes show how we test our hypervisor, the codes for tests are available at (Ept.c and HypervisorRoutines.c).In the first scenario, we want to test page hook (EptPageHook) before executing vmlaunch, which means that Ept is initialized, and then we want to put the hook before entering VMX. (the test code is on Ept.c) ///////////////////////// Example Test ///////////////////////// EptPageHook(ExAllocatePoolWithTag, FALSE); ///////////////////////////////////////////////////////////////The above function puts hook on the execution of a page containing a function (in this case ExAllocatePoolWithTag).The second scenario is we want to test both VMCALL and EptPageHook after our hypervisor is loaded, and we’re in Vmx non-root mode (the test code is on HypervisorRoutines.c). // Check if everything is ok then return true otherwise false if (AsmVmxVmcall(VMCALL_TEST, 0x22, 0x333, 0x4444) == STATUS_SUCCESS) { ///////////////// Test Hook after Vmx is launched ///////////////// EptPageHook(ExAllocatePoolWithTag, TRUE); /////////////////////////////////////////////////////////////////// return TRUE; } else { return FALSE; }As you can see, it first tests the Vmcall using VMCALL_TEST and then puts the hook to a function (in this case ExAllocatePoolWithTag).DemoFirst, we load our hypervisor driver,For the first scenario, you can see that we successfully notified about the execution of ExAllocatePoolWith tag after vmlaunch executed, and Guest Rip is equal to the address of ExAllocatePoolWithTag and EptHandleEptViolation is responsible for handling Ept violations.In the second testing scenario, you can see that our VMCALL is successfully executed (green line), and we notified about the execution of a page, but wait, we put our Execute Access hook on ExAllocatePoolWithTag, but the Guest Rip is equal to ExFreePool, Why?It turns out that ExAllocatePoolWithTag and ExFreePool are both on the same page, and ExFreePool is executed earlier than ExAllocatePoolWithTag, so we get the execution of this function.The above test results show the importance of checking Guest Rip in the EPT violation handler. We’ll talk about it in the next part.Finally, you can see the following picture which shows whether our hook successfully applied or not.DiscussionThis part is added to answer to questions about EPT, we’ll discuss different approaches and pros and cons of them. Thanks to Petr for answering these questions.1. Why there are limitations on calling NT functions in VMX Root Mode?It is because of paging and high IRQL. The reason is what explained here for the high IRQL and as we’re in a high IRQL in Vmx root mode then some pages(paged pools) might be paged-out.The hypervisor can use a completely different address space than the NT kernel, I believe this is what regular hypervisors like Hyper-V/XEN do. They don’t use “identity EPT mapping”, therefore VA 0x10000 in VMX-root mode does not point to the same physical memory as 0x10000 in VMX non-root mode.For example, let’s pick an NT function that can be called at HIGH_IRQL (MmGetPhysicalAddress). Let’s imagine this function is on virtual address 0x1234, but this virtual address points to that function in VMX non-root, in ntoskrnl address space.The real question should be: “Why can I call some NT functions in VMX-root mode” the answer is that you set HOST_CR3 in VMCS to be the same as CR3 of the NT main System process, therefore hypervisor in vmx root-mode share the same memory view as VMX non-root mode.It is important to know this, in practice for self-virtualizing hypervisors (like hyperplatform/hvpp), you don’t care, because as I said your HOST_CR3 is the same as NT’s CR3, therefore you can touch whatever memory you want.If you happened to work on HyperV or XEN, you don’t have the same luxury. the hypervisor memory address space is not mapped at all in the virtualized OS (that’s quite the point of virtualization).2. Why we shouldn’t modify EPT in VMX Non-Root?In the ideal world, no memory of the hypervisor should be visible from the virtualized OS (you cant see XEN internals from the virtualized OS for example).in hyperplatform/hvpp, you can see the memory of the hypervisor. Why? This time it’s not because of HOST_CR3 but because of identity EPT mapping - you set EPT tables in such a way, that the virtualized OS can see even the memory of the hypervisor itself.My point is - in the ideal world you shouldn’t even see the EPT structures from within the VMX non-root mode, imagine it this way, can you modify regular page-tables from user-mode?The answer is it depends. In reality? No. Why? because the page-tables are in kernel memory that is inaccessible from the user-mode. That’s the whole point of memory protection. Could you set page tables in such a way that it would be possible to modify them from user-mode? Yes, but it doesn’t mean you should though. This is sort of a security thing.There’s one even more important reason: cachesNow you might have tried it and it worked most of the time in your case but that doesn’t mean it’s the correct approach.3. What are the advantages of having EPT table for each processor separately?When you change EPT structures and you want that change to be synced across CPUs, you have to perform IPI (KeIpiGenericCall) from within VMX root mode to flush caches on all CPUs.In an ideal world, you would call KeIpiGenericCall from VMX-root mode. but you can’t - you’ll fastly end up in a deadlock. You’d need to implement your own IPI mechanism and set correctly APIC for VMX-root mode.Now this can be done - but it would be non-trivial to implement.When you have multiple EPTs for each CPU, you don’t have to do IPIs, each core manages its own EPT.Now they won’t be 100% synced all the time, but if the EPT handler logic is the same for each core and doesn’t change over time, it doesn’t matter.ConclusionWe come to the end of this part. I believe EPT is the most important feature that can be used by researchers, security programs and game hackers as it gives a unique ability to monitor the operating system and user-mode applications. In the next part, we’ll be using EPT and implement hidden hook mechanisms, which commonly used among hypervisors. Also, we’ll improve our hypervisor by using WPP Tracing instead of using DbgPrint, event injection, and a mechanism to talk from Vmx root-mode to Vmx non-root mode and finally we’ll see how to use Virtual Processor Identifier (VPID). Feel free to use the comments below to ask questions or ask for clarification.See you guys in the next part.The 8th part is available here.References[1] Memory type range register - (https://en.wikipedia.org/wiki/Memory_type_range_register)[2] KVA Shadow: Mitigating Meltdown on Windows - (https://msrc-blog.microsoft.com/2018/03/23/kva-shadow-mitigating-meltdown-on-windows/)[3] How to Implement a software-based SMEP(Supervisor Mode Execution Protection) with Virtualization/Hypervisor Technology - (http://hypervsir.blogspot.com/2014/11/how-to-implement-software-based.html)[4] Vol 3A – Chapter 11 – (11.11.3 Example Base and Mask Calculations) - (https://software.intel.com/en-us/articles/intel-sdm)[5] x86 Paging Tutorial - (https://cirosantilli.com/x86-paging)[6] OSDev notes 2: Memory management - (http://ethv.net/workshops/osdev/notes/notes-2)[7] Vol 3A – Chapter 11 – (11.11 MEMORY TYPE RANGE REGISTERS (MTRRS)) - (https://software.intel.com/en-us/articles/intel-sdm)[8] Vol 3A – Chapter 11 – (11.12 PAGE ATTRIBUTE TABLE (PAT)) - (https://software.intel.com/en-us/articles/intel-sdm)[9] HyperPlatform User Document - (https://tandasat.github.io/HyperPlatform/userdocument/)[10] Vol 3C – Chapter 34– (34.15.2 SMM VM Exits) - (https://software.intel.com/en-us/articles/intel-sdm)[11] Vol 3C – Chapter 34– (34.15.6 Activating the Dual-Monitor Treatment) - (https://software.intel.com/en-us/articles/intel-sdm)[12] Windows Hotpatching: A Walkthrough - (https://jpassing.com/2011/05/03/windows-hotpatching-a-walkthrough/)[13] Vol 3C – Chapter 28– (28.2.3.1 EPT Misconfigurations) - (https://software.intel.com/en-us/articles/intel-sdm)[14] Vol 3C – Chapter 28– (28.2.3.2 EPT Violations) - (https://software.intel.com/en-us/articles/intel-sdm)[15] R.I.P ROP: CET Internals in Windows 20H1 - (http://windows-internals.com/cet-on-windows)[16] Inside Windows Page Frame Number (PFN) Part 1 - (https://rayanfam.com/topics/inside-windows-page-frame-number-part1)[17] Inside Windows Page Frame Number (PFN) Part 2 - (https://rayanfam.com/topics/inside-windows-page-frame-number-part2)[18] why we can access memory from non paged pool at or above DISPATCH LEVEL - (https://stackoverflow.com/questions/18764211/why-we-can-access-memory-from-non-paged-pool-at-or-above-dispatch-level)" }, { "title": "Reversing Windows Internals (Part 1) - Digging Into Handles, Callbacks & ObjectTypes", "url": "/topics/reversing-windows-internals-part1/", "categories": "tutorials, windows", "tags": "handle-creation-in-windows, handle-table-windows, handles-in-windows, how-openprocess-works, object-internals, object-type-windows, reversing-windows-internals, windows-callbacks, windows-internals-tutorial, windows-objects", "date": "2019-12-09 00:00:00 +0000", "snippet": "IntroductionWelcome to the first part of a series of posts about Exploring &amp; Reversing Windows Concepts and Internals. If you reach here then you’re probably a security researcher or a programmer and this post and similar posts can help you understand what’s going on in some parts of Windows when you use objects with different users and credentials and what you can expect from Windows and how it internally works.If you want to follow other parts of this tutorial or other tutorials, please visit here.OverviewIn this part, I’m gonna describe some internal structures and functions relating to the “Handles” and we’ll see how Windows saves these handles in its internal structures then we go to see how callbacks work. For example, when you create a handle what kind of mechanisms exists in Windows to notify you about the handle creation. After that, we’ll see how Windows internally calls these callbacks by analyzing the process of creating a handle when a user-mode application requests a handle to the kernel. At last, we’ll see how Windows saves these callbacks and check other (somehow unknown) callbacks by studying different Object Types in Windows and we’ll practically use them in our drivers.Table of Contents Introduction Overview Table of Contents Handles What is a Handle? Handles in Windows Kernel Changing Handles Access Callbacks Callbacks for processes Handle Creation Process PsOpenProcess ObOpenObjectByPointer SECURITY_SUBJECT_CONTEXT ACCESS_STATE ObpCreateHandle Object Types ObjectTypes in Windows Finding Process Object Type TypeIndex in Object Types Finding All Windows _OBJECT_TYPE(s) Finding Types which support callback Analyzing Callbacks in ObjectTypes DumpProcedure OpenProcedure CloseProcedure DeleteProcedure ParseProcedure ParseProcedureEx SecurityProcedure QueryNameProcedure OkayToCloseProcedure Using Callbacks in ObjectTypes Conclusion ReferencesWhat is a Handle?If you’re familiar with the way Windows shares its resources, then you probably know about the handles. In short, the handle is a value that the Windows kernel returns to the user-mode application (if you have needed privileges or have an account which is not denied by DACL) and this handle can be used for further action on the object.There is a tool called “handle” from the SysInternals which can be downloaded from here.The official site describes : Ever wondered which program has a particular file or directory open? Now you can find out. Handle is a utility that displays information about open handles for any process in the system. You can use it to see the programs that have a file open, or to see the object types and names of all the handles of a program.Let’s see what are the handles of processes.For example,handle windows\\systemThis command shows every handle for each process in which their handle name contains “windows\\system”. The name match is case-insensitive and the fragment specified can be anywhere in the paths you are interested in. You can imagine how this way can be used to find what process(es) are opening a specific file when you try to remove them.Finding all the handles in User-modeOne interesting thing in Windows is if you are in Integrity Level &gt;= Medium, then you can access handles to all processes, even kernel addresses of objects. Alex mentioned that KASLR is not designed to protect against processes with Medium Integrity or above. By the way, its one of the known methods to bypass KASLR (e.g when you have a write-what-where bug and don’t know where to modify then you can use one of the objects in the kernel as you have the addresses).The PoC for this way is available on GitHub (https://github.com/SinaKarvandi/Process-Magics/tree/master/EnumAllHandles) and you can see the results from the following images.Now you can see all the handles even from other processes (like system process) with an unprivileged (non-elevated UAC) user.Handles In Windows KernelLet’s see how Windows saves and manages the handles in its kernel.In process structure (nt!_EPROCESS) there is a field called “ObjectTable”.kd&gt; dt nt!_EPROCESS -y object +0x418 ObjectTable : Ptr64 _HANDLE_TABLEThe nt!_HANDLE_TABLE is like this :kd&gt; dt nt!_HANDLE_TABLE +0x000 NextHandleNeedingPool : Uint4B +0x004 ExtraInfoPages : Int4B +0x008 TableCode : Uint8B +0x010 QuotaProcess : Ptr64 _EPROCESS +0x018 HandleTableList : _LIST_ENTRY +0x028 UniqueProcessId : Uint4B +0x02c Flags : Uint4B +0x02c StrictFIFO : Pos 0, 1 Bit +0x02c EnableHandleExceptions : Pos 1, 1 Bit +0x02c Rundown : Pos 2, 1 Bit +0x02c Duplicated : Pos 3, 1 Bit +0x02c RaiseUMExceptionOnInvalidHandleClose : Pos 4, 1 Bit +0x030 HandleContentionEvent : _EX_PUSH_LOCK +0x038 HandleTableLock : _EX_PUSH_LOCK +0x040 FreeLists : [1] _HANDLE_TABLE_FREE_LIST +0x040 ActualEntry : [32] UChar +0x060 DebugInfo : Ptr64 _HANDLE_TRACE_DEBUG_INFOUsing HandleTableList, you can traverse through each handle of your target process.Each handle is defined in a structure called “nt!_HANDLE_TABLE_ENTRY “ and among these fields the most interesting one is GrantedAccessBits.kd&gt; dt nt!_HANDLE_TABLE_ENTRY +0x000 VolatileLowValue : Int8B +0x000 LowValue : Int8B +0x000 InfoTable : Ptr64 _HANDLE_TABLE_ENTRY_INFO +0x008 HighValue : Int8B +0x008 NextFreeHandleEntry : Ptr64 _HANDLE_TABLE_ENTRY +0x008 LeafHandleValue : _EXHANDLE +0x000 RefCountField : Int8B +0x000 Unlocked : Pos 0, 1 Bit +0x000 RefCnt : Pos 1, 16 Bits +0x000 Attributes : Pos 17, 3 Bits +0x000 ObjectPointerBits : Pos 20, 44 Bits +0x008 GrantedAccessBits : Pos 0, 25 Bits +0x008 NoRightsUpgrade : Pos 25, 1 Bit +0x008 Spare1 : Pos 26, 6 Bits +0x00c Spare2 : Uint4BThere is a command (!handle) in windbg which used to show the details about the handle.The following picture describes the details of each field in !handle.Changing Handles GrantedAccessIf you map (Entry) field to the _HANDLE_TABLE_ENTRY then you can see the following results.kd&gt; dt nt!_HANDLE_TABLE_ENTRY ffffa30c9aa26010 +0x000 VolatileLowValue : 0n-1981244744615788709 +0x000 LowValue : 0n-1981244744615788709 +0x000 InfoTable : 0xe4813466`e010ff5b _HANDLE_TABLE_ENTRY_INFO +0x008 HighValue : 0n2097151 +0x008 NextFreeHandleEntry : 0x00000000`001fffff _HANDLE_TABLE_ENTRY +0x008 LeafHandleValue : _EXHANDLE +0x000 RefCountField : 0n-1981244744615788709 +0x000 Unlocked : 0y1 +0x000 RefCnt : 0y0111111110101101 (0x7fad) +0x000 Attributes : 0y000 +0x000 ObjectPointerBits : 0y11100100100000010011010001100110111000000001 (0xe4813466e01) +0x008 GrantedAccessBits : 0y0000111111111111111111111 (0x1fffff) +0x008 NoRightsUpgrade : 0y0 +0x008 Spare1 : 0y000000 (0) +0x00c Spare2 : 0Note that, 0x1fffff means FULL CONTROL. You easily change the access bit, e.g using “eb”. For example when you use a command like “eb ffffa30c9aa26010+8 ee” then if you see the handle again (!handle 0x04) you can see that GrantedAccess is changed.PROCESS ffffe4813466e040 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 DirBase: 001aa002 ObjectTable: ffffa30c9aa06580 HandleCount: 2986. Image: SystemKernel handle table at ffffa30c9aa06580 with 2986 entries in use0004: Object: ffffe4813466e040 GrantedAccess: 001fffee (Protected) Entry: ffffa30c9aa26010Object: ffffe4813466e040 Type: (ffffe4813467b0c0) Process ObjectHeader: ffffe4813466e010 (new version) HandleCount: 4 PointerCount: 131188There is a good post here which describes how the above method can be used in order to bypass the restrictions that a driver can put on a special process or each process that tries to access memory of a protected-process, for example, a game with anit-cheat protection or a security software which protects its memory from being accessed by a remote process and APIs like WriteProcessMemory or ReadProcessMemory).Let’s see some other functions relating to the handles.There is a function ExEnumHandleTable which enumerates all the handle from a process by passing a pointer to process’s ObjectTable.NTKERNELAPIBOOLEANExEnumHandleTable ( __in PHANDLE_TABLE HandleTable, __in EX_ENUMERATE_HANDLE_ROUTINE EnumHandleProcedure, __in PVOID EnumParameter, __out_opt PHANDLE Handle )Also, there is a function ExpLookupHandleTableEntry which gets the handle table as its first argument (RCX) and the handle value as the second argument (RDX) and returns the _HANDLE_TABLE_ENTRY corresponding to that handle. You can use them in your driver or shellcode.PHANDLE_TABLE_ENTRYExpLookupHandleTableEntry ( IN PHANDLE_TABLE HandleTable, IN EXHANDLE tHandle )Callbacks for processesIn order to set callback whenever a handle to a process is requested or whatever relating to the handles of Threads or Processes, you can ObRegisterCallbacks function.The ObRegisterCallbacks routine registers a list of callback routines for thread, process, and desktop handle operations.The sample for using ObRegisterCallbacks is available on GitHub :[https://github.com/SinaKarvandi/misc/tree/master/ObRegisterCallbacks]If you load the above driver, you’ll get the following NTSTATUS error :“{Access Denied} A process has requested access to an object, but has not been granted those access rights.”If we look at the decompiled code from IDA and look for the error code (0xC0000022), we’ll reach to the following pseudo-code. if ( v14 ) { if ( !(unsigned int)MmVerifyCallbackFunctionCheckFlags(v14, 32i64) ) goto LABEL_23; } else if ( !v13[3] ) { break; } v15 = v13[3]; if ( v15 &amp;&amp; !(unsigned int)MmVerifyCallbackFunctionCheckFlags(v15, 32i64) ) {LABEL_23: v5 = 0xC0000022; goto LABEL_24; }It’s clear that MmVerifyCallbackFunctionCheckFlags is the guilty function, I don’t find a way to register my unsigned driver but for this let’s just patch it.0: 48 c7 c0 01 00 00 00 mov rax,0x17: c3 retThe above assembly code is enough to always return true so we need to execute the following windbg command:eb nt!MmVerifyCallbackFunctionCheckFlags 48 c7 c0 01 00 00 00 c3Update 1: As Yarden mentioned, linking in with /INTEGRITYCHECK will save you the need to patch the kernel from the debugger so instead of using the above command you can add /INTEGRITYCHECK to your linker when you’re compiling your driver.Now load the driver again. If you encounter errors like: “An instance already exists at this altitude on the volume specified”, then you have to change the following line : RtlInitUnicodeString(&amp;Altitude, L"1001");Make sure to run the following windbg command if you previously didn’t enable the debugging outputs. eb nt!kd_default_mask ff ff ff ffAfter running driver, you have to see each handle request to processes or threads with its desired access masks and the corresponding operation (e.g creating a handle or duplicate handle or whatever).Finally, we’ll get the following results :Handle Creation ProcessNow that we know some of the basic concepts from callbacks and handle tables, let’s have a comprehensive survey from user-mode OpenProcess until we come back to user-mode again so we can see how Windows creates handle and saves it on its handle table.The following functions have to be called in order to make a handle available to the user-mode in the case of opening a handle to a process.OpenProcess (user-mode) -&gt; NtOpenProcess (user-mode) -&gt; NtOpenProcess (kernel-mode) -&gt; PsOpenProcess - &gt; ObOpenObjectByPointer -&gt; ObpCreateHandleIf you remeber from the ObRegisterCallbacks, our callbacks called from “ObpCallPreOperationCallbacks” and this function is called by ObpCreateHandle. Also “ObpCallPostOperationCallbacks” is responsible for calling our post operation callbacks (we’ll see them).Let’s start with OpenProcess, here is an ordered decompiled version of OpenProcess.HANDLE __stdcall OpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId){ int result; // eax CLIENT_ID ClientId; // [rsp+20h] [rbp-48h] OBJECT_ATTRIBUTES ObjectAttributes; // [rsp+30h] [rbp-38h] HANDLE ProcessHandle; // [rsp+88h] [rbp+20h] ObjectAttributes.Length = 0x30; ObjectAttributes.RootDirectory = 0i64; ClientId = (CLIENT_ID)(unsigned __int64)(signed int)dwProcessId; ObjectAttributes.Attributes = bInheritHandle != 0 ? 2 : 0; ObjectAttributes.ObjectName = 0i64; _mm_storeu_si128((__m128i *)&amp;ObjectAttributes.SecurityDescriptor, (__m128i)0i64); result = NtOpenProcess(&amp;ProcessHandle, dwDesiredAccess, &amp;ObjectAttributes, &amp;ClientId); if ( result &gt;= 0 ) return ProcessHandle; BaseSetLastNTError(result); return 0i64;}Among the above parameters, dwProcessId and dwDesiredAccess is not in our interest as they’re more and less clear but the most interesting field here is ObjectAttributes, Microsoft explains about Object Attributes here. The structure is below, you can read about each field in MSDN.typedef struct _OBJECT_ATTRIBUTES64 { ULONG Length; ULONG64 RootDirectory; ULONG64 ObjectName; ULONG Attributes; ULONG64 SecurityDescriptor; ULONG64 SecurityQualityOfService;} OBJECT_ATTRIBUTES64;The above field has some Attributes that is interesting to us, their definition as defined in SDK :#define OBJ_INHERIT 0x00000002L#define OBJ_PERMANENT 0x00000010L#define OBJ_EXCLUSIVE 0x00000020L#define OBJ_CASE_INSENSITIVE 0x00000040L#define OBJ_OPENIF 0x00000080L#define OBJ_OPENLINK 0x00000100L#define OBJ_KERNEL_HANDLE 0x00000200L#define OBJ_FORCE_ACCESS_CHECK 0x00000400L#define OBJ_IGNORE_IMPERSONATED_DEVICEMAP 0x00000800L#define OBJ_VALID_ATTRIBUTES 0x00000FF2LWe’ll see how they affect process handle creation, later.NtOpenProcessNtOpenProcess, first checks for its previous mode (user-mode or kernel-mode) then it calls PsOpenProcess.__kernel_entry NTSYSCALLAPI NTSTATUS NtOpenProcess( PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId);PsOpenProcessThe PsOpenProcess is something like this, it’s not documented so it’s based on IDA’s decompile results:NTSTATUSFASTCALL__int64 __fastcall PsOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId, char PreviousMode, char PreviousMode2);In PsOpenProcess, it first checks whether the handle pointer resides to valid user-mode address then it limits the user-mode handles to 0x1df2. From the following picture, you can see this limitation on handles and their meanings.In the case of kernel attributes, it limits the handle to the following values.As you can see, you don’t have access to OBJ_KERNEL_HANDLE and OBJ_VALID_ATTRIBUTES in user-mode and also some undocumented values 0x11800 which is not revealed by Microsoft.In PsOpenProcess, the next check is for SeDebugPrivilege. As you might know, this is one of the powerful privileges in Windows that causes to bypass any Privilege checks and give the needed accesses directly. You might see it in tools like Mimikatz. It then passes it to the SePrivilegedServiceAuditAlarm. SePrivilegedServiceAuditAlarm is to be called whenever a privileged system service is attempted.Finally, PsOpenProcess calls ObOpenObjectByPointer.ObOpenObjectByPointerIn order to explain about ObOpenObjectByPointer, First, we have to know about two structures “ACCESS_STATE” and “SECURITY_SUBJECT_CONTEXT”.SECURITY_SUBJECT_CONTEXTThe SECURITY_SUBJECT_CONTEXT is used to capture the subject security context for access validation and auditing. It’s like dumping a special context’s token, then lock it in order to avoid any modification and finally do some privilege checks.Functions like SeCaptureSubjectContext or SeCaptureSubjectContextEx return a pointer to this structure.For example, the following code shows how this structure can be used to do some privilege checks.BOOLEAN HasPrivilege(IN PPRIVILEGE_SET Privilege){ BOOLEAN Result; SECURITY_SUBJECT_CONTEXT SubjectContext; /* Capture and lock the security subject context */ SeCaptureSubjectContext(&amp;SubjectContext); SeLockSubjectContext(&amp;SubjectContext); /* Do privilege check */ Result = SePrivilegeCheck(Privilege, &amp;SubjectContext, UserMode); /* Audit the privilege */ SePrivilegeObjectAuditAlarm(NULL, &amp;SubjectContext, 0, Privilege, Result, UserMode); /* Unlock and release the security subject context and return */ SeUnlockSubjectContext(&amp;SubjectContext); SeReleaseSubjectContext(&amp;SubjectContext); return Result;}This structure is important because as James Forshaw mentioned in 3rd part of his post about “AppLocker internals” :A Windows access check takes 4 main parameters: A SECURITY_SUBJECT_CONTEXT which identifies the caller’s access tokens. A desired access mask. A GENERIC_MAPPING structure which allows the access check to convert generic access to object-specific access rights. And most importantly, the Security Descriptor which describes the security of the resource being checked.SECURITY_SUBJECT_CONTEXT is coming from two above mentioned functions, DesiredAccess is also a parameter to ObOpenObjectByPointer and GenericMapping comes from _OBJECT_TYPE’s _OBJECT_TYPE_INITIALIZER+0x4c (I’ll describe about Object Types later in this post.) while security descriptor can be derived from the _OBJECT_HEADER’s +0x28 and object header is also the first parameter to ObOpenObjectByPointer. +0x028 SecurityDescriptor : Ptr64 VoidACCESS_STATEAs the MSDN describes, The ACCESS_STATE structure describes the state of an access in progress. It contains an object’s subject context, remaining desired access types, granted access types, and, optionally, a privilege set to indicate which privileges were used to permit the access.Now, let’s return to ObOpenObjectByPointer, This function checks whether the caller passes an access state if not then it creates a new one based on the desired access and the object type’s generic mapping by calling SepCreateAccessStateFromSubjectContext , as the name implies it receives an ACCESS_STATE from the SECURITY_SUBJECT_CONTEX.Eventually, it checks whether the the function itself (ObOpenObjectByPointer) creates ACCESS_STATE or not. If it creates, then it deletes the ACCESS_STATE and SECURITY_SUBJECT_CONTEXT using SepDeleteAccessState and SeReleaseSubjectContext.Finally, this function calls the popular ObpCreateHandle which creates the handle.ObpCreateHandleFor creating a handle, an undocumented function “ObpCreateHandle” is responsible for creating a new handle to an existing object. Generally, ObpCreateHandle creates an entry in the process’ handle table that becomes associated with the object.ObpCreateHandle defines like this: (there are some differences between current definition and WRK’s definition).__int64 __fastcall ObpCreateHandle( _OB_OPEN_REASON OpenReason, void *Object, unsigned int DesiredAccess, _ACCESS_STATE *AccessState, unsigned int ObjectPointer, unsigned int Attribute, char AccessMode, struct _OBJECT_CREATE_INFO *CreateInfo, int AccessMask2, PVOID *NewObject, PVOID *Handle);The first argument to this function is _OB_OPEN_REASON which defines like this :typedef enum _OB_OPEN_REASON{ ObCreateHandle = 0, ObOpenHandle = 1, ObDuplicateHandle = 2, ObInheritHandle = 3, ObMaxOpenReason = 4} OB_OPEN_REASON;From the above structure, you can see the cases where ObpCreateHandle might be used for.If the handle is requested from kernel-mode then “ObpKernelHandleTable” is used as the handle table and if it’s a user-mode application then it calls ObReferenceProcessHandleTable.The above function (ObReferenceProcessHandleTable), first checks whether it can acquire RundownProtect or not. When run-down protection is in effect, the driver can safely access the object without the risk that the object will be deleted before the access completes. You can imagine that if you use ExAcquireRundownProtection on this field (RundownProtect) then each attempt to create a handle by the special process will cause a 0xC000010A error (An attempt was made to access an exiting process.).This function finally returns Process-&gt;ObjectTable.In the end, ObpCreateHandle calls ExReleaseRundownProtection which releases the RundownProtect of our process.After that, ObpCreateHandle calls some undocumented Callbacks (SecurityProcedure). I’ll give a detailed explanation about these kinds of callbacks later on this topic but for now, it first checks whether SecurityProcedure is SeDefaultObjectMethod or not. SeDefaultObjectMethod is the default security method for objects. It is responsible for either retrieving, setting, and deleting the security descriptor of an object. It is not used to assign the original security descriptor to an object and as you can see if our callback fails with {buffer too small} error then it tries to call it once more so by now, you know that this callback is responsible for changing SecurityDescriptor of any object type (each object type separately using ObjectType’s SecurityProcedure).Don’t worry if things are not clear, after reading the last part (about ObjectTypes) you can return here and read it once again and sure you’ll understand it.As you can see, these object type callbacks called for each object separately and it’s not specific to a special object (e.g Process, Thread, or Desktop objects).After making SecurityDescriptor ready, it’s time to perform security checks, ObpCreateHandle calls SeAccessCheck. The SeAccessCheck routine determines whether the requested access rights can be granted to an object protected by a security descriptor and an object owner so ObpCreateHandle passes the SecurityDescriptor of object and AccessState that we have from the previous function to see if the access is granted or not.As I told you above, ObpCreateHandle calls ObpCallPreOperationCallbacks and this function is responsible for calling callbacks that are registered by ObRegisterCallbacks but in contrast with above callbacks, these callbacks are limited to some object types (e.g Process, Thread, or Desktop).This limitation is done by the following check, which checks whether object type supports callbacks and if there is any callback registered.Later, we have a section called “Finding Types which support callback”, it describes how to find these object types but keep in mind, if you set Support Callback bit of an object manually, then PatchGuard comes in and leads to a BSOD.In order to assign a handle, it first acquires a lock to HANDLE_TABLE.HandleTableLock and then search through HANDLE_TABLE.FreeLists. If there isn’t any empty place in our handle table (based on NextHandleNeedingPool index) then it tries to allocate a new handle table entry for the specified handle table using ExpAllocateHandleTableEntrySlow.Now, it’s time to compute the handle’s value.When the handle is computed, it calls ExpSetHandleExtraInfo which gets the HandleTable and Handle and sets the _HANDLE_TABLE_ENTRY_INFO to the handle entry.struct _HANDLE_TABLE_ENTRY_INFO{ unsigned int AuditMask; unsigned int MaxRelativeAccessMask;};ExpSetHandleExtraInfo is used to set AuditMask and AccessMask to the handle and you can see that it removes the handle if the above function failed (using ExpFreeHandleTableEntry).And finally, it sets the handle value : HandleAddress = ___MainHandle | 0xFFFFFFFF80000000ui64; if ( !IsKernelHandle ) HandleAddress = ___MainHandle; *_Handle = HandleAddress;As you can see, if the handle is user-mode handle then it strips the kernel address bit otherwise it’s a kernel handle and address itself is the handle.This behavior will be changed in the future if Windows starts supporting Supervisor Mode Access Prevention (SMAP) because as long as they use this protection, they won’t be able to directly write on user-mode addresses and they have to execute extra instruction for this purpose.The last step is calling ObpPostInterceptHandleCreate this function calls ObpCallPostOperationCallbacks and its responsible for calling Post Operation Callbacks.ObjectTypes in WindowsIf you’d ever used functions like ObReferenceObjectByHandle, ObReferenceObjectByPointer, ObOpenObjectByPointer or other functions then you’ve probably heard of POBJECT_TYPE.Also, creation routines for the various objects, like IoCreateDriver or PspCreateProcess, call the generic ObCreateObject and pass it a pointer to an appropriate _OBJECT_TYPE structure._OBJECT_TYPE is one of the important structures in Windows that stores the definition of different object like Process, Thread, Mutex, etc. If you want to know the difference between NT objects and non-objects you can read the article “What is POBJECT_TYPE?”.Let’s see the definition:(I dumped the structures using pdbex written by one of my best friends Petr Benes)typedef struct _OBJECT_TYPE{ /* 0x0000 */ struct _LIST_ENTRY TypeList; /* 0x0010 */ struct _UNICODE_STRING Name; /* 0x0020 */ void* DefaultObject; /* 0x0028 */ unsigned char Index; /* 0x0029 */ char Padding_1[3]; /* 0x002c */ unsigned long TotalNumberOfObjects; /* 0x0030 */ unsigned long TotalNumberOfHandles; /* 0x0034 */ unsigned long HighWaterNumberOfObjects; /* 0x0038 */ unsigned long HighWaterNumberOfHandles; /* 0x003c */ long Padding_2; /* 0x0040 */ struct _OBJECT_TYPE_INITIALIZER TypeInfo; /* 0x00b8 */ struct _EX_PUSH_LOCK TypeLock; /* 0x00c0 */ unsigned long Key; /* 0x00c4 */ long Padding_3; /* 0x00c8 */ struct _LIST_ENTRY CallbackList;} OBJECT_TYPE, *POBJECT_TYPE; /* size: 0x00d8 */Finding Process Object TypeI’ll talk about some important fields later in this post but for now, let’s see how we can find the _OBJECT_TYPE of a specific object, let say process, first find all the processes EPROCESS.kd&gt; !dml_procAddress PID Image file nameffffd689`fd06b380 4 System ffffd689`fd0ad080 44 Registry ffffd68a`01875040 24c smss.exe ffffd68a`07bf74c0 2a4 csrss.exe ffffd68a`0779c080 304 wininit.exe ffffd68a`077ae140 30c csrss.exe ffffd68a`07b21080 33c winlogon.exe ffffd68a`07b5c100 378 services.exe ffffd68a`07b5d080 380 lsass.exe ffffd68a`017a02c0 3dc svchost.exe ....I choose lsass.exe (ffffd68a07b5d080) as the target. As you might know, Windows saves each object (e.g _EPROCESS) like this : _POOL_HEADER _OBJECT_QUOTA_CHARGES (optional) _OBJECT_HANDLE_DB (optional) _OBJECT_NAME (optional) _OBJECT_CREATOR_INFO (optional) _OBJECT_HEADER object body (e.g _EPROCESS)So if we subtract sizeof(_OBJECT_HEADER) from the EPROCESS we’ll reach to the _OBJECT_HEADER of this object. (you can perform the same thing for _POOL_HEADER too.)The object header is like this :kd&gt; dt nt!_OBJECT_HEADER +0x000 PointerCount : Int8B +0x008 HandleCount : Int8B +0x008 NextToFree : Ptr64 Void +0x010 Lock : _EX_PUSH_LOCK +0x018 TypeIndex : UChar +0x019 TraceFlags : UChar +0x019 DbgRefTrace : Pos 0, 1 Bit +0x019 DbgTracePermanent : Pos 1, 1 Bit +0x01a InfoMask : UChar +0x01b Flags : UChar +0x01b NewObject : Pos 0, 1 Bit +0x01b KernelObject : Pos 1, 1 Bit +0x01b KernelOnlyAccess : Pos 2, 1 Bit +0x01b ExclusiveObject : Pos 3, 1 Bit +0x01b PermanentObject : Pos 4, 1 Bit +0x01b DefaultSecurityQuota : Pos 5, 1 Bit +0x01b SingleHandleEntry : Pos 6, 1 Bit +0x01b DeletedInline : Pos 7, 1 Bit +0x01c Reserved : Uint4B +0x020 ObjectCreateInfo : Ptr64 _OBJECT_CREATE_INFORMATION +0x020 QuotaBlockCharged : Ptr64 Void +0x028 SecurityDescriptor : Ptr64 Void +0x030 Body : _QUADAnd the sizeof is :kd&gt; ?? sizeof(nt!_OBJECT_HEADER)unsigned int64 0x38but wait, we’re in top of the object’s Body (means that EPROCESS or whatever starts at _OBJECT_HEADER+0x30) so we have to subtract -0x30 from the EPROCESS (ffffd68a`07b5d080-0x30 = ffffd68a07b5d050) .kd&gt; dt ffffd68a07b5d050 nt!_OBJECT_HEADER +0x000 PointerCount : 0n458365 +0x008 HandleCount : 0n14 +0x008 NextToFree : 0x00000000`0000000e Void +0x010 Lock : _EX_PUSH_LOCK +0x018 TypeIndex : 0x7a 'z' +0x019 TraceFlags : 0 '' +0x019 DbgRefTrace : 0y0 +0x019 DbgTracePermanent : 0y0 +0x01a InfoMask : 0x88 '' +0x01b Flags : 0 '' +0x01b NewObject : 0y0 +0x01b KernelObject : 0y0 +0x01b KernelOnlyAccess : 0y0 +0x01b ExclusiveObject : 0y0 +0x01b PermanentObject : 0y0 +0x01b DefaultSecurityQuota : 0y0 +0x01b SingleHandleEntry : 0y0 +0x01b DeletedInline : 0y0 +0x01c Reserved : 0 +0x020 ObjectCreateInfo : 0xfffff803`80468240 _OBJECT_CREATE_INFORMATION +0x020 QuotaBlockCharged : 0xfffff803`80468240 Void +0x028 SecurityDescriptor : 0xffffb082`5308716c Void +0x030 Body : _QUADYou can also confirm this by looking at the “ObjectHeader” field of “!object” command.kd&gt; !object ffffd68a`07b5d080Object: ffffd68a07b5d080 Type: (ffffd689fd09a4e0) Process ObjectHeader: ffffd68a07b5d050 (new version) HandleCount: 14 PointerCount: 458365In Windows, each object is derived from a special type and even each type (like process, thread, token) derived from another type called “type”.Let’s see.In the above code you can see there is a “Type: (ffffd689fd09a4e0) Process” if we map this to nt!_OBJECT_TYPE, we can it’s “Name” which is “Process”.kd&gt; dt nt!_OBJECT_TYPE ffffd689`fd09a4e0 +0x000 TypeList : _LIST_ENTRY [ 0xffffd689`fd09a4e0 - 0xffffd689`fd09a4e0 ] +0x010 Name : _UNICODE_STRING "Process" +0x020 DefaultObject : (null) +0x028 Index : 0x7 '' +0x02c TotalNumberOfObjects : 0x85 +0x030 TotalNumberOfHandles : 0x45b +0x034 HighWaterNumberOfObjects : 0x97 +0x038 HighWaterNumberOfHandles : 0x518 +0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER +0x0b8 TypeLock : _EX_PUSH_LOCK +0x0c0 Key : 0x636f7250 +0x0c8 CallbackList : _LIST_ENTRY [ 0xffffb082`4f982140 - 0xffffb082`4f8f5fb0 ]This _OBJECT_TYPE (process) is also mapped to a global variable called “nt!PsProcessType” (Do you remember we filled OB_OPERATION_REGISTRATION.ObjectType with PsProcessType and PsThreadType ?) and These global variables are OBJECT_TYPE**, not just single indirection pointers.kd&gt; dq nt!PsProcessType L1fffff803`8056f390 ffffd689`fd09a4e0TypeIndex in Object TypesAnother important field from _OBJECT_HEADER is TypeIndex.Let’s review this line again : +0x018 TypeIndex : 0x7a 'z'This field didn’t exist in until Windows Seven, means that before Windows Seven each _OBJECT_HEADER has a field called “Type” which was a pointer to its “_OBJECT_TYPE”, that’s the reason for (old version) and (new version) in !object’s results. You can read more about it in the article in CodeMachine.But in the newer versions of Windows (&gt; Windows 7) you can see TypeIndex. Instead of pointing directly to the OBJECT_TYPE data structure, the object header now contains an index into a new global data structure nt!ObTypeIndexTable, which is an array of pointers to the different OBJECT_TYPE structures.The following command is used to get the _OBJECT_TYPE of target index, note that in this example 0x7 is index and @$ptrsize is defined by Windbg which shows either your pointer are 8 Bytes (x64) or 4 Bytes (x86).kd&gt; dt nt!_OBJECT_TYPE poi(nt!ObTypeIndexTable+(0x7*@$ptrsize)) You can see the result here :kd&gt; dt nt!_OBJECT_TYPE poi(nt!ObTypeIndexTable+(0x7*@$ptrsize)) +0x000 TypeList : _LIST_ENTRY [ 0xffffd689`fd09a4e0 - 0xffffd689`fd09a4e0 ] +0x010 Name : _UNICODE_STRING "Process" +0x020 DefaultObject : (null) +0x028 Index : 0x7 '' +0x02c TotalNumberOfObjects : 0x85 +0x030 TotalNumberOfHandles : 0x45b +0x034 HighWaterNumberOfObjects : 0x97 +0x038 HighWaterNumberOfHandles : 0x518 +0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER +0x0b8 TypeLock : _EX_PUSH_LOCK +0x0c0 Key : 0x636f7250 +0x0c8 CallbackList : _LIST_ENTRY [ 0xffffb082`4f982140 - 0xffffb082`4f8f5fb0 ]But wait, in or example we see that (TypeIndex : 0x7a) and its index is not 0x7a! It turns out that in Windows 10 they decided to not directly point to the index (why?) of nt!ObTypeIndexTable instead you have to do some XORs in order to find the right index.Update 2 :Take a look at the following slides from NTarakanov :[http://www.powerofcommunity.net/poc2018/nikita.pdf]The reason why they XORed TypeIndex with nt!ObHeaderCookie is the fact that it’s possible to modify the nt!_OBJECT_HEADER.TypeIndex of each object (for example in the case of a pool overflow), And then it would be possible to point to other _OBJECT_TYPEs like ALPC_OBJECT and trigger this vulnerability (pool overflow) as it was possible to control the behavior of callbacks in these objects.The following picture is copied from this post which describes how he understands it by reversing nt!ObGetObjectType , you can do the same thing and it works.Keep in mind that the second member of nt!ObTypeIndexTable is “Type”. If you get the _OBJECT_HEADER of Process’s _OBJECT_TYPE itself, then you’ll reach to the following _OBJECT_TYPE .kd&gt; dt nt!_OBJECT_TYPE poi(nt!ObTypeIndexTable+((0x2)*@$ptrsize)) +0x000 TypeList : _LIST_ENTRY [ 0xffffd689`fd09acd0 - 0xffffd68a`01413750 ] +0x010 Name : _UNICODE_STRING "Type" +0x020 DefaultObject : 0xfffff803`80442780 Void +0x028 Index : 0x2 '' +0x02c TotalNumberOfObjects : 0x43 +0x030 TotalNumberOfHandles : 0 +0x034 HighWaterNumberOfObjects : 0x43 +0x038 HighWaterNumberOfHandles : 0 +0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER +0x0b8 TypeLock : _EX_PUSH_LOCK +0x0c0 Key : 0x546a624f +0x0c8 CallbackList : _LIST_ENTRY [ 0xffffd689`fd09ade8 - 0xffffd689`fd09ade8 ]Now that we know what the object type is, it’s time to dig deeper into Windows _OBJECT_TYPEs and find all of them.Finding All Windows _OBJECT_TYPE(s)The first and easiest way is using Windbg’s “!object \\ObjectTypes” or using tools like SysInternals’ WinObj.Using windbg , you’ll get the following results but the result of my tests shows that this command’s results is not complete, that’s why we need a third way to explore the kernel types. WinObj also won’t show a complete result.kd&gt; !object \\ObjectTypesObject: ffffb0824ea08700 Type: (ffffd689fd09a900) Directory ObjectHeader: ffffb0824ea086d0 (new version) HandleCount: 0 PointerCount: 69 Directory Object: ffffb0824ea07cb0 Name: ObjectTypes Hash Address Type Name ---- ------- ---- ---- 00 ffffd689fd0f5640 Type TmTm 01 ffffd689fd094560 Type Desktop ffffd689fd09a4e0 Type Process 02 ffffd689fd0f6400 Type EnergyTracker ffffd689fd0f6980 Type RegistryTransaction 03 ffffd689fd093640 Type DebugObject 04 ffffd68a014137a0 Type VRegConfigurationContext ffffd689fd0f50c0 Type TpWorkerFactory 05 ffffd689fd0f6ae0 Type Adapter ffffd689fd09a220 Type Token 06 ffffd689fd0f9900 Type DxgkSharedResource 07 ffffd689fd0934e0 Type PsSiloContextPaged 08 ffffd689fd0fa400 Type NdisCmState ffffd689fd0937a0 Type ActivityReference 09 ffffd689fd0faf00 Type PcwObject ffffd689fd0f5bc0 Type WmiGuid 11 ffffd689fd0f90c0 Type DmaAdapter ffffd689fd0f5e80 Type EtwRegistration 12 ffffd689fd0f9640 Type DxgkSharedBundleObject ffffd689fd0f5900 Type Session ffffd689fd093d20 Type RawInputManager ffffd689fd093900 Type Timer 13 ffffd689fd094820 Type Mutant 14 ffffd689fd093a60 Type IRTimer 16 ffffd689fd0fa980 Type DxgkCurrentDxgProcessObject ffffd689fd0f6c40 Type IoCompletion 17 ffffd689fd0f94e0 Type DxgkSharedProtectedSessionObject ffffd689fd0fa560 Type DxgkSharedSyncObject ffffd689fd093220 Type WindowStation ffffd689fd094ae0 Type Profile 18 ffffd689fd0f6560 Type File 20 ffffd689fd0930c0 Type Partition 21 ffffd689fd0f9e80 Type DxgkSharedKeyedMutexObject ffffd689fd0946c0 Type ActivationObject ffffd689fd093380 Type Semaphore ... ffffd689fd09ad20 Type Type ...The third method for getting objects is interpreting Windows structures manually using Windbg. Before further investigation we have to find the “Type“‘s _OBJECT_TYPE (e.g a process type itself is a “Type”), for this purpose first we have to find Process’s _OBJECT_TYPE :kd&gt; x nt!PsProcessTypefffff803`8056f390 nt!PsProcessType = &lt;no type information&gt;kd&gt; dq fffff803`8056f390 l1fffff803`8056f390 ffffd689`fd09a4e0Using !object we can find its type :kd&gt; !object ffffd689`fd09a4e0Object: ffffd689fd09a4e0 Type: (ffffd689fd09ad20) Type ObjectHeader: ffffd689fd09a4b0 (new version) HandleCount: 0 PointerCount: 2 Directory Object: ffffb0824ea08700 Name: Processyou can also find the “Type“‘s location using “!object \\ObjectTypes”.Now that we have a pointer to “Type“‘s _OBJECT_TYPE ( = ffffd689fd09ad20), it’s time to find other types, _OBJECT_TYPE itself has a “TypeList” but doesn’t seem to be a list to Object Types, you can traverse it, it won’t give a valid result. If you remember from the first part of this post, I told you the order when Windows allocates the objects (search for “ Windows saves each object” in this post and see it again.). It turns out that above the Type’s _OBJECT_HEADER, there is another optional structure, called “_OBJECT_HEADER_CREATOR_INFO”, let’s see what’s the definition of this structure.kd&gt; dt nt!_OBJECT_HEADER_CREATOR_INFO +0x000 TypeList : _LIST_ENTRY +0x010 CreatorUniqueProcess : Ptr64 Void +0x018 CreatorBackTraceIndex : Uint2B +0x01a Reserved1 : Uint2B +0x01c Reserved2 : Uint4Bkd&gt; ?? sizeof(nt!_OBJECT_HEADER_CREATOR_INFO)unsigned int64 0x20As I told you, _OBJECT_HEADER_CREATOR_INFO is above the _OBJECT_HEADER so we need a little calculation here, sizeof( _OBJECT_HEADER_CREATOR_INFO) = 0x20 and for reaching to the _OBJECT_HEADER we have to subtract the pointer (ffffd689fd09ad20) by 0x30.ffffd689fd09ad20 - 0x30 - 0x20 = FFFFD689FD09ACD0‬ =&gt; Pointer to _OBJECT_HEADER_CREATOR_INFOThe TypeList in _OBJECT_HEADER_CREATOR_INFO is _LIST_ENTRY which shows us all the Types. We’ll use Windbg to traverse through the list.kd&gt; dt FFFFD689FD09ACD0‬ nt!_OBJECT_HEADER_CREATOR_INFO -l TypeList.Flink -y TypeListTypeList.Flink at 0xffffd689`fd09acd0--------------------------------------------- +0x000 TypeList : _LIST_ENTRY [ 0xffffd689`fd09a8b0 - 0xffffd689`fd09ad20 ]TypeList.Flink at 0xffffd689`fd09a8b0--------------------------------------------- +0x000 TypeList : _LIST_ENTRY [ 0xffffd689`fd09a070 - 0xffffd689`fd09acd0 ]TypeList.Flink at 0xffffd689`fd09a070--------------------------------------------- +0x000 TypeList : _LIST_ENTRY [ 0xffffd689`fd09a1d0 - 0xffffd689`fd09a8b0 ]TypeList.Flink at 0xffffd689`fd09a1d0--------------------------------------------- +0x000 TypeList : _LIST_ENTRY [ 0xffffd689`fd09a330 - 0xffffd689`fd09a070 ]...You can see a pointer to all of the Types (of course from _OBJECT_HEADER_CREATOR_INFO). Let’s verify we found them correctly by looking at their names (Don’t forget we have to add 0x20 and ox30 to reach the start of the _OBJECT_TYPE).kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd09acd0+20+30 -y Name +0x010 Name : _UNICODE_STRING "Type"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd09a8b0+20+30 -y Name +0x010 Name : _UNICODE_STRING "Directory"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd09a070+20+30 -y Name +0x010 Name : _UNICODE_STRING "SymbolicLink"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd09a1d0+20+30 -y Name +0x010 Name : _UNICODE_STRING "Token"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd09a330+20+30 -y Name +0x010 Name : _UNICODE_STRING "Job"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd09a490+20+30 -y Name +0x010 Name : _UNICODE_STRING "Process"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0943b0+20+30 -y Name +0x010 Name : _UNICODE_STRING "Thread"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd093070+20+30 -y Name +0x010 Name : _UNICODE_STRING "Partition"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0940f0+20+30 -y Name +0x010 Name : _UNICODE_STRING "UserApcReserve"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd094930+20+30 -y Name +0x010 Name : _UNICODE_STRING "IoCompletionReserve"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd093750+20+30 -y Name +0x010 Name : _UNICODE_STRING "ActivityReference"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd093490+20+30 -y Name +0x010 Name : _UNICODE_STRING "PsSiloContextPaged"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd094250+20+30 -y Name +0x010 Name : _UNICODE_STRING "PsSiloContextNonPaged"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0935f0+20+30 -y Name +0x010 Name : _UNICODE_STRING "DebugObject"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd094eb0+20+30 -y Name +0x010 Name : _UNICODE_STRING "Event"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0947d0+20+30 -y Name +0x010 Name : _UNICODE_STRING "Mutant"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd094bf0+20+30 -y Name +0x010 Name : _UNICODE_STRING "Callback"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd093330+20+30 -y Name +0x010 Name : _UNICODE_STRING "Semaphore"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0938b0+20+30 -y Name +0x010 Name : _UNICODE_STRING "Timer"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd093a10+20+30 -y Name +0x010 Name : _UNICODE_STRING "IRTimer"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd094a90+20+30 -y Name +0x010 Name : _UNICODE_STRING "Profile"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd094d50+20+30 -y Name +0x010 Name : _UNICODE_STRING "KeyedEvent"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0931d0+20+30 -y Name +0x010 Name : _UNICODE_STRING "WindowStation"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd094510+20+30 -y Name +0x010 Name : _UNICODE_STRING "Desktop"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd093b70+20+30 -y Name +0x010 Name : _UNICODE_STRING "Composition"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd093cd0+20+30 -y Name +0x010 Name : _UNICODE_STRING "RawInputManager"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd093e30+20+30 -y Name +0x010 Name : _UNICODE_STRING "CoreMessaging"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd094670+20+30 -y Name +0x010 Name : _UNICODE_STRING "ActivationObject"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f5070+20+30 -y Name +0x010 Name : _UNICODE_STRING "TpWorkerFactory"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f6a90+20+30 -y Name +0x010 Name : _UNICODE_STRING "Adapter"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f5490+20+30 -y Name +0x010 Name : _UNICODE_STRING "Controller"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f5330+20+30 -y Name +0x010 Name : _UNICODE_STRING "Device"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f5750+20+30 -y Name +0x010 Name : _UNICODE_STRING "Driver"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f6bf0+20+30 -y Name +0x010 Name : _UNICODE_STRING "IoCompletion"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f51d0+20+30 -y Name +0x010 Name : _UNICODE_STRING "WaitCompletionPacket"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f6510+20+30 -y Name +0x010 Name : _UNICODE_STRING "File"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f55f0+20+30 -y Name +0x010 Name : _UNICODE_STRING "TmTm"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f60f0+20+30 -y Name +0x010 Name : _UNICODE_STRING "TmTx"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f6670+20+30 -y Name +0x010 Name : _UNICODE_STRING "TmRm"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f6eb0+20+30 -y Name +0x010 Name : _UNICODE_STRING "TmEn"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f6d50+20+30 -y Name +0x010 Name : _UNICODE_STRING "Section"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f58b0+20+30 -y Name +0x010 Name : _UNICODE_STRING "Session"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f5a10+20+30 -y Name +0x010 Name : _UNICODE_STRING "Key"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f6930+20+30 -y Name +0x010 Name : _UNICODE_STRING "RegistryTransaction"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f6250+20+30 -y Name +0x010 Name : _UNICODE_STRING "ALPC Port"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f63b0+20+30 -y Name +0x010 Name : _UNICODE_STRING "EnergyTracker"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f67d0+20+30 -y Name +0x010 Name : _UNICODE_STRING "PowerRequest"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f5b70+20+30 -y Name +0x010 Name : _UNICODE_STRING "WmiGuid"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f5e30+20+30 -y Name +0x010 Name : _UNICODE_STRING "EtwRegistration"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0fa0f0+20+30 -y Name +0x010 Name : _UNICODE_STRING "EtwSessionDemuxEntry"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0fabf0+20+30 -y Name +0x010 Name : _UNICODE_STRING "EtwConsumer"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f9cd0+20+30 -y Name +0x010 Name : _UNICODE_STRING "CoverageSampler"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f9070+20+30 -y Name +0x010 Name : _UNICODE_STRING "DmaAdapter"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0faeb0+20+30 -y Name +0x010 Name : _UNICODE_STRING "PcwObject"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0fad50+20+30 -y Name +0x010 Name : _UNICODE_STRING "FilterCommunicationPort"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0fa250+20+30 -y Name +0x010 Name : _UNICODE_STRING "FilterConnectionPort"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0fa3b0+20+30 -y Name +0x010 Name : _UNICODE_STRING "NdisCmState"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0fad50+20+30 -y Name +0x010 Name : _UNICODE_STRING "FilterCommunicationPort"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0fa3b0+20+30 -y Name +0x010 Name : _UNICODE_STRING "NdisCmState"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f98b0+20+30 -y Name +0x010 Name : _UNICODE_STRING "DxgkSharedResource"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f9e30+20+30 -y Name +0x010 Name : _UNICODE_STRING "DxgkSharedKeyedMutexObject"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0fa510+20+30 -y Name +0x010 Name : _UNICODE_STRING "DxgkSharedSyncObject"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0fa670+20+30 -y Name +0x010 Name : _UNICODE_STRING "DxgkSharedSwapChainObject"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0fa7d0+20+30 -y Name +0x010 Name : _UNICODE_STRING "DxgkDisplayManagerObject"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0fa930+20+30 -y Name +0x010 Name : _UNICODE_STRING "DxgkCurrentDxgProcessObject"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f9490+20+30 -y Name +0x010 Name : _UNICODE_STRING "DxgkSharedProtectedSessionObject"kd&gt; dt nt!_OBJECT_TYPE 0xffffd68a`01413750+20+30 -y Name +0x010 Name : _UNICODE_STRING "VRegConfigurationContext"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f95f0+20+30 -y Name +0x010 Name : _UNICODE_STRING "DxgkSharedBundleObject"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd09ad20+20+30 -y Name +0x010 Name : _UNICODE_STRING "--- memory read error at address 0x00000130`00000000 ---"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0f9750+20+30 -y Name +0x010 Name : _UNICODE_STRING "DxgkCompositionObject"There is also another way that you can enumerate the above list by using the following command (thanks to Bruce Dang for mentioning it):dx Debugger.Utility.Collections.FromListEntry( (*(nt!_OBJECT_TYPE **)&amp;nt!ObpTypeObjectType)-&gt;TypeList, "nt!_OBJECT_TYPE", "TypeList").Select(o =&gt; (nt!_OBJECT_HEADER*)((unsigned char *)&amp;o + 0x20)).Select( o =&gt; o-&gt;ObjectName)The above result contains more types than using “!object \\ObjectTypes” (why?) in my case, it finds 70 types while “!object \\ObjectTypes” gives only 67 types !!!Update 1: As Alex mentioned, I got more objects because I copy-pasted FilterCommunicationPort and NdisCmState twice.Update 1: Also, “!object 0 Type” is a built-in way of enumerating the creator info list.kd&gt; !object 0 TypeScanning 67 objects of type 'Type'Object: ffffbc05a907b4e0 Type: (ffffbc05a907b4e0) Type ObjectHeader: ffffbc05a907b4b0 (new version) HandleCount: 0 PointerCount: 2 Directory Object: ffff9f02dca02d90 Name: TypeOptional Headers: CreatorInfo(ffffbc05a907b4b0): Process: 0 BackTraceIndex: 58 NameInfo(ffffbc05a907b4b0)Object: ffffbc05a907b640 Type: (ffffbc05a907b4e0) Type ObjectHeader: ffffbc05a907b610 (new version) HandleCount: 0 PointerCount: 2 Directory Object: ffff9f02dca02d90 Name: DirectoryOptional Headers: CreatorInfo(ffffbc05a907b610): Process: 0 BackTraceIndex: 5a NameInfo(ffffbc05a907b610)Object: ffffbc05a907b380 Type: (ffffbc05a907b4e0) Type ObjectHeader: ffffbc05a907b350 (new version) HandleCount: 0 PointerCount: 2 Directory Object: ffff9f02dca02d90 Name: SymbolicLink...Object: ffffbc05a907b0c0 Type: (ffffbc05a907b4e0) Type ObjectHeader: ffffbc05a907b090 (new version) HandleCount: 0 PointerCount: 2 Directory Object: ffff9f02dca02d90 Name: JobOptional Headers: Now that we have the pointers to all of the types, it’s time to investigate through each _OBJECT_TYPE(s).Finding Types which support callbackIn _OBJECT_TYPE there is a structure called “_OBJECT_TYPE_INITIALIZER”. It defines like this :kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER +0x000 Length : Uint2B +0x002 ObjectTypeFlags : Uint2B +0x002 CaseInsensitive : Pos 0, 1 Bit +0x002 UnnamedObjectsOnly : Pos 1, 1 Bit +0x002 UseDefaultObject : Pos 2, 1 Bit +0x002 SecurityRequired : Pos 3, 1 Bit +0x002 MaintainHandleCount : Pos 4, 1 Bit +0x002 MaintainTypeList : Pos 5, 1 Bit +0x002 SupportsObjectCallbacks : Pos 6, 1 Bit +0x002 CacheAligned : Pos 7, 1 Bit +0x003 UseExtendedParameters : Pos 0, 1 Bit +0x003 Reserved : Pos 1, 7 Bits +0x004 ObjectTypeCode : Uint4B +0x008 InvalidAttributes : Uint4B +0x00c GenericMapping : _GENERIC_MAPPING +0x01c ValidAccessMask : Uint4B +0x020 RetainAccess : Uint4B +0x024 PoolType : _POOL_TYPE +0x028 DefaultPagedPoolCharge : Uint4B +0x02c DefaultNonPagedPoolCharge : Uint4B +0x030 DumpProcedure : Ptr64 void +0x038 OpenProcedure : Ptr64 long +0x040 CloseProcedure : Ptr64 void +0x048 DeleteProcedure : Ptr64 void +0x050 ParseProcedure : Ptr64 long +0x050 ParseProcedureEx : Ptr64 long +0x058 SecurityProcedure : Ptr64 long +0x060 QueryNameProcedure : Ptr64 long +0x068 OkayToCloseProcedure : Ptr64 unsigned char +0x070 WaitObjectFlagMask : Uint4B +0x074 WaitObjectFlagOffset : Uint2B +0x076 WaitObjectPointerOffset : Uint2BIt contains lots of important fields, for example, “SupportsObjectCallbacks” shows whether the object supports callbacks or not. _OBJECT_TYPE_INITIALIZER starts after 0x40 from the _OBJECT_TYPE so let’s find the objects that support callbacks from the _OBJECT_TYPES that we previously gathered. (Do you remember ObpCallPreOperationCallbacks when we reversed ObpCreateHandle? :) )kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd09acd0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd09a8b0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd09a070+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd09a1d0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd09a330+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd09a490+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y1kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0943b0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y1kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd093070+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0940f0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094930+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd093750+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd093490+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094250+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0935f0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094eb0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0947d0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094bf0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd093330+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0938b0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd093a10+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094a90+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094d50+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0931d0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094510+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y1kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd093b70+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd093cd0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd093e30+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094670+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f5070+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f6a90+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f5490+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f5330+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f5750+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f6bf0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f51d0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f6510+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f55f0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f60f0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f6670+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f6eb0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f6d50+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f58b0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f5a10+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f6930+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f6250+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f63b0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f67d0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f5b70+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f5e30+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fa0f0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fabf0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f9cd0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f9070+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0faeb0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fad50+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fa250+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fa3b0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fad50+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fa3b0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f98b0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f9e30+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fa510+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fa670+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fa7d0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fa930+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f9490+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd68a`01413750+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f95f0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd09ad20+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f9750+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y0You can see that only the following three _OBJECT_TYPEs support callbacks,kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094510+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y1kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd09a490+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y1kd&gt; dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0943b0+20+30+40 -b -y SupportsObjectCallbacks +0x002 SupportsObjectCallbacks : 0y1kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd094510+20+30 -y Name +0x010 Name : _UNICODE_STRING "Desktop"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd09a490+20+30 -y Name +0x010 Name : _UNICODE_STRING "Process"kd&gt; dt nt!_OBJECT_TYPE 0xffffd689`fd0943b0+20+30 -y Name +0x010 Name : _UNICODE_STRING "Thread"The result is not really surprising, previously Alex and other friends told me that Windows 10 TH2 starts supporting “ExDesktopOObjectType”.There are also another important fields in _OBJECT_TYPE_INITIALIZER for example you can find the ValidAccessMask for that object or find the related functions from DumpProcedure, OpenProcedure, CloseProcedure, DeleteProcedure, ParseProcedure, ParseProcedureEx, SecurityProcedure, QueryNameProcedure, and OkayToCloseProcedure. For example in process type we have the following functions (These are the callbacks that internally used by Microsoft and it’s not revealed to drivers) : [+0x030] DumpProcedure : 0x0 [Type: void (__cdecl*)(void *,_OBJECT_DUMP_CONTROL *)] [+0x038] OpenProcedure : 0xfffff803806b0170 [Type: long (__cdecl*)(_OB_OPEN_REASON,char,_EPROCESS *,void *,unsigned long *,unsigned long)] [+0x040] CloseProcedure : 0xfffff803806bdd50 [Type: void (__cdecl*)(_EPROCESS *,void *,unsigned __int64,unsigned __int64)] [+0x048] DeleteProcedure : 0xfffff80380664c00 [Type: void (__cdecl*)(void *)] [+0x050] ParseProcedure : 0x0 [Type: long (__cdecl*)(void *,void *,_ACCESS_STATE *,char,unsigned long,_UNICODE_STRING *,_UNICODE_STRING *,void *,_SECURITY_QUALITY_OF_SERVICE *,void * *)] [+0x050] ParseProcedureEx : 0x0 [Type: long (__cdecl*)(void *,void *,_ACCESS_STATE *,char,unsigned long,_UNICODE_STRING *,_UNICODE_STRING *,void *,_SECURITY_QUALITY_OF_SERVICE *,_OB_EXTENDED_PARSE_PARAMETERS *,void * *)] [+0x058] SecurityProcedure : 0xfffff803805cd360 [Type: long (__cdecl*)(void *,_SECURITY_OPERATION_CODE,unsigned long *,void *,unsigned long *,void * *,_POOL_TYPE,_GENERIC_MAPPING *,char)] [+0x060] QueryNameProcedure : 0x0 [Type: long (__cdecl*)(void *,unsigned char,_OBJECT_NAME_INFORMATION *,unsigned long,unsigned long *,char)] [+0x068] OkayToCloseProcedure : 0x0 [Type: unsigned char (__cdecl*)(_EPROCESS *,void *,void *,char)]kd&gt; u 0xfffff803806b0170 L1nt!PspProcessOpen:fffff803`806b0170 48895c2408 mov qword ptr [rsp+8],rbxkd&gt; u 0xfffff803806bdd50 L1nt!PspProcessClose:fffff803`806bdd50 488bc4 mov rax,rspkd&gt; u 0xfffff80380664c00 L1nt!PspProcessDelete:fffff803`80664c00 4c8bdc mov r11,rspkd&gt; u 0xfffff803805cd360 L1nt!SeDefaultObjectMethod:fffff803`805cd360 4053 push rbxAnalyzing Callbacks in ObjectTypesBy now, you’re familiar with these callbacks: DumpProcedure, OpenProcedure, CloseProcedure, DeleteProcedure, ParseProcedure, ParseProcedureEx, SecurityProcedure, QueryNameProcedure, and OkayToCloseProcedure. Now let’s see which functions attempt to call them and what is the purpose of calling them.The following definitions are the undocumented part of these callbacks. DumpProcedure: Calls from nt!ObpRemoveObjectRoutine. [Type: void (__cdecl*)(void *,_OBJECT_DUMP_CONTROL *)]int DumpProcedure_Hook( PVOID Object, OBJECT_DUMP_CONTROL* DumpControl); OpenProcedure: Calls from nt!ObpIncrementHandleCountEx and the callback target function for process is nt!PspProcessOpen. [Type: long (__cdecl*)(_OB_OPEN_REASON,char,_EPROCESS *,void *,unsigned long *,unsigned long)]int OpenProcedure_Hook( OB_OPEN_REASON OpenReason, CHAR AccessMode, PEPROCESS TargetProcess, PVOID Object, PULONG GrantedAccess, ULONG HandleCount); CloseProcedure: Calls from nt!ObCloseHandleTableEntry and the callback target function for process is nt!PspProcessClose and for file is nt!IopCloseFile. [Type: void (__cdecl*)(_EPROCESS *,void *,unsigned __int64,unsigned __int64)]int CloseProcedure_Hook( PEPROCESS Process, PVOID Object, ULONG ProcessHandleCount, ULONG SystemHandleCount); DeleteProcedure: Calls from nt!ObpRemoveObjectRoutine and the callback target function for process is nt!PspProcessDelete and for file is nt!IopDeleteFile. [Type: void (__cdecl*)(void *)]int DeleteProcedure_Hook( PVOID Object); ParseProcedure &amp; ParseProcedureEx: Calls from nt!ObpLookupObjectName and the callback target function for file is nt!IopParseFile. [Type: long (__cdecl*)(void *,void *,_ACCESS_STATE *,char,unsigned long,_UNICODE_STRING *,_UNICODE_STRING *,void *,_SECURITY_QUALITY_OF_SERVICE *,void * )] [Type: long (__cdecl)(void *,void *,_ACCESS_STATE *,char,unsigned long,_UNICODE_STRING *,_UNICODE_STRING *,void *,_SECURITY_QUALITY_OF_SERVICE *,_OB_EXTENDED_PARSE_PARAMETERS *,void * *)]int ParseProcedure_Hook( PVOID ParseObject, PVOID ObjectType, PACCESS_STATE AccessState, CHAR AccessMode, ULONG Attributes, UNICODE_STRING* CompleteName, UNICODE_STRING* RemainingName, PVOID Context, SECURITY_QUALITY_OF_SERVICE* SecurityQos, OB_EXTENDED_PARSE_PARAMETERS* ExtendedParameters, PVOID* Object); SecurityProcedure: Calls from nt!NtQuerySecurityObject and nt!ObpCreateHandle (Do you remeber? we see it on ObpCreateHandle and explained about its purpose) and the callback target function for file is nt!IopGetSetSecurityObject and for process is nt!SeDefaultObjectMethod. [Type: long (__cdecl*)(void *,_SECURITY_OPERATION_CODE,unsigned long *,void *,unsigned long *,void * *,_POOL_TYPE,_GENERIC_MAPPING *,char)]int SecurityProcedure_Hook( PVOID Object, SECURITY_OPERATION_CODE OperationCode, PULONG SecurityInformation, PVOID SecurityDescriptor, PULONG CapturedLength, PVOID* ObjectsSecurityDescriptor, POOL_TYPE PoolType, PGENERIC_MAPPING GenericMapping, CHAR Mode); QueryNameProcedure: Calls from nt!ObQueryNameStringMode and target function for file is nt!IopQueryName. [Type: long (__cdecl*)(void *,unsigned char,_OBJECT_NAME_INFORMATION *,unsigned long,unsigned long *,char)]int QueryNameProcedure_Hook( PVOID Object, UCHAR HasObjectName, POBJECT_NAME_INFORMATION ObjectNameInfo, ULONG Length, PULONG* ReturnLength, CHAR Mode); OkayToCloseProcedure: Calls from nt!ObCloseHandleTableEntry. [Type: unsigned char (__cdecl*)(_EPROCESS *,void *,void *,char)]int OkayToCloseProcedure_Hook( PEPROCESS Process, DWORD DW, HANDLE Handle, KPROCESSOR_MODE PreviousMode);Using Callbacks in ObjectTypesIt’s time to use the above information to build a driver that hooks these callbacks, there is an article here from Souhail Hammou that describes the behavior of OkayToCloseProcedure.As he describes,The function (ObpCloseHandleTableEntry) will access the OkayToCloseProcedure field and check if it’s NULL, if that’s true the function will proceed to other checks (check if the handle is protected from being closed).If the OkayToCloseProcedre field isn’t NULL, the function will proceed to call the callback function. If the callback function returns 0 the handle cannot be closed and ObpCloseHandleTableEntry will return STATUS_HANDLE_NOT_CLOSABLE. If it returns a value other than 0 we will proceed to the other checks as it happens when the OkayToCloseProcedure is NULL.Now we have to create a driver which hooks all of the non-null callback procedure and also hook OkayToCloseProcedure.Full source code of object callbacks hook is available on GitHub :[ https://github.com/SinaKarvandi/misc/tree/master/TypeInfoCallbacksHooker ]For this, first, you have to find a pointer the _OBJECT_TYPE structure of the object that you need to hook (e.g Process, File and etc.). For example, as I described above PsProcessType contains a pointer to the start of Process OBJECT_TYPE so in the case of hooking the processes callbacks you can use the following code. /* Get the Process Object Type (OBJECT_TYPE) structure */ // ProcessObjectType = ObGetObjectType(PsGetCurrentProcess()); ProcessObjectType = (PUCHAR)*PsProcessType; DbgPrint("[*] Process Object Type Structure at : %p\\n", ProcessObjectType);Also, there is another option which can be used to do the same task, You can use ObGetObjectType and pass you object as its argument. For example, you can use PsGetCurrentProcess() which is a Process object or any other object. This way is more general.For each of the callbacks, we use the following code. First, we check whether the callback is NULL or not, if it’s not null then we save the callback pointer (For future calls) then we change the address of the callback so that Windows calls our callback method first and we call the original function. /* CloseProcedure_Hook */ if (*(INT64*)(ProcessObjectType + 0x80) != NULL) { // Store the previous pointer *(INT64*)(CallbacksList + 0x10) = *(INT64*)(ProcessObjectType + 0x80); _CloseProcedure = *(INT64*)(ProcessObjectType + 0x80); // Save to pointer to new hook address *(INT64*)(ProcessObjectType + 0x80) = (INT64)CloseProcedure_Hook; DbgPrint("[*] CloseProcedure Hook Done !!\\n"); } else { DbgPrint("[*] CloseProcedure Hook Failed"); }Whenever Windows calls our callback method, we have to first show the details about the object and process that causes this callback to be invoked. Then we return the result of previous callback (which Windows sets).typedef int(*CloseProcedure)(PEPROCESS Process, PVOID Object, ULONG ProcessHandleCount, ULONG SystemHandleCount);CloseProcedure _CloseProcedure;int CloseProcedure_Hook( PEPROCESS Process, PVOID Object, ULONG ProcessHandleCount, ULONG SystemHandleCount){ DbgPrint("[*] CloseProcedure called for object : 0x%llx, Process : %s \\n", Object, (PUCHAR)Process + 0x450); return _CloseProcedure(Process, Object, ProcessHandleCount, SystemHandleCount);}Keep in mind that these modifications on callbacks are prohibited due to the presence of PatchGuard but PatchGuard won’t start in a debugged environment.Finally, the results are:ConclusionIn this post, we saw some important parts of Windows about handles, callbacks, object types and lots of other cool examples about how to use them.The details provided in this post might be changed in the future versions of Windows, these details checked on the latest Windows 10 1903 so please don’t hesitate to correct me if you are sure something is wrong or you want to add some additional details for readers or something that changes in the future.I’m not actively working on these series but I’ll try to post new parts as soon as possible.That’s it guys, hope you enjoy reading this post.References[1] Detecting Sysmon on the Victim Host - (https://ired.team/offensive-security/enumeration-and-discovery/detecting-sysmon-on-the-victim-host)[2] Sysmon - (https://docs.microsoft.com/en-us/sysinternals/downloads/sysmon)[3] Sysmon Event ID 16 - (https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=90016)[4] Handle - (https://docs.microsoft.com/en-us/sysinternals/downloads/handle)[5] libelevate - Bypass ObRegisterCallbacks via elevation- (https://github.com/notscimmy/libelevate)[6] Microsoft Windows Security - (https://www.microsoftpressstore.com/articles/article.aspx?p=2228450&amp;seqNum=3)[7] OBJECT_ATTRIBUTES structure - (https://docs.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_object_attributes)[8] Windows 7 Object Headers - (https://codemachine.com/article_objectheader.html)[9] A Light on Windows 10’s “OBJECT_HEADER-&gt;TypeIndex” - (https://medium.com/@ashabdalhalim/a-light-on-windows-10s-object-header-typeindex-value-e8f907e7073a)[10] OB_OPERATION_REGISTRATION structure - (https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_ob_operation_registration)[11] Kernel Objects - (https://computer.forensikblog.de/en/2009/04/kernel-objects.html)[12] Operating Offensively Against Sysmon - (https://www.darkoperator.com/blog/2018/10/5/operating-offensively-against-sysmon)[13] DACLs and ACEs - (https://docs.microsoft.com/en-us/windows/win32/secauthz/dacls-and-aces)[14] OkayToCloseProcedure callback kernel hook - (http://rce4fun.blogspot.com/2014/07/okaytocloseprocedure-callback-kernel_9.html)[15] Part 1: Digging deep into LoadLibrary - (https://n4r1b.netlify.com/en/posts/2019/03/part-1-digging-deep-into-loadlibrary/)[16] The Internals of AppLocker - Part 3 - Access Tokens and Access Checking - (https://tyranidslair.blogspot.com/2019/11/the-internals-of-applocker-part-3.html)" }, { "title": "Why you should not always trust MSDN: Finding Real Access Rights Needed By Handles", "url": "/topics/finding-the-real-access-rights-needed-by-handles/", "categories": "debugging, kernel-mode, programming, security, user-mode, windows", "tags": "access_mask, ida-python-script, ntopenprocesstokenex, obpreferenceobjectbyhandlewithtag, obreferenceobjectbyhandle, process_query_limited_information, real-access-rights, xrefs-ida-python", "date": "2019-06-06 00:00:00 +0000", "snippet": "IntroductionHi guys,The title of this topic is somehow weird, if you think everything in MSDN is 100% match with what Microsoft implemented in Windows (like what I used to think), you’re definitely wrong, this post shows some proofs and in the last part, I’ll give you a solution to ACCESS_RIGHTS problem.Before starting let’s talk about some backgrounds about “ACCESS_MASK”.Most of the explanations derived from here.BackgroundsThe ACCESS_MASK data type is a DWORD value that defines standard, specific, and generic rights. These rights are used in access control entries (ACEs) and are the primary means of specifying the requested or granted access to an object.A securable object is an object that can have a security descriptor. All named Windows objects are securable. Some unnamed objects, such as process and thread objects, can have security descriptors too. For most securable objects, you can specify an object’s security descriptor in the function call that creates the object. For example, you can specify a security descriptor in the CreateFile and CreateProcess functions.Each type of securable object defines its own set of specific access rights and its own mapping of generic access rights. For information about the specific and generic access rights for each type of securable object, see the overview for that type of object.Here is the list of some objects in Windows (From Windows Internals) that have security descriptor and of course ACCESS_MASK.1. Files, directories and volumes(NTFS file system)2. Devices3. Mailslots4. Named and anonymous pipes5. Jobs6. Processes7. Threads8. Events, keyed events and event pairs9. Mutexes, semaphores10. Shared memory sections11. I / O completion ports12. LPC ports13. Waitable timers14. Access tokens15. Windows stations16. Desktops17. Network shares18. ServicesThe structure of ACCESS_MASK is like this : typedef struct _ACCESS_MASK { WORD SpecificRights; BYTE StandardRights; BYTE AccessSystemAcl: 1; BYTE Reserved: 3; BYTE GenericAll: 1; BYTE GenericExecute: 1; BYTE GenericWrite: 1; BYTE GenericRead: 1; } ACCESS_MASK;For more information you can read these two pdfs.MotivationThat’s enough for theory, the reason for creating this post is during the past few days, I was reading “Reading Your Way Around UAC” which is sophisticated research about Windows Access Tokens.The most interesting part for me was where James Forshaw wrote : What’s going on? Basically, the documentation is wrong, you don’t need QueryInformation to open the process token only QueryLimitedInformation. You can disassemble NtOpenProcessTokenEx in the kernel if you don’t believe me:NTSTATUS NtOpenProcessTokenEx(HANDLE ProcessHandle, ACCESS_MASK DesiredAccess, DWORD HandleAttributes, PHANDLE TokenHandle) { EPROCESS * ProcessObject; NTSTATUS status = ObReferenceObjectByHandle( ProcessHandle, PROCESS_QUERY_LIMITED_INFORMATION, PsProcessType, &amp; ProcessObject, NULL); ...} Going back to Vista it’s always been the case that only QueryLimitedInformation was needed, contrary to the documentation. For the first time, it was so weird for me, then I ask some of my friends about the things that are implemented in contrary with documentation in MSDN, so yeah, it seems there are things in contrary with MSDN.That’s enough for me to lose my trust to Microsoft (at least for checking handles’ ACCESS_MASK ) thus I have to somehow check everything by my self but hey, it’s such a boring, isn’t it?So, I decided to write an IDA Python Plugin for this purpose but let’s see what we really need to look for.The first thing we need to know is how Windows checks for the desired access. The ObReferenceObjectByHandle function is designed for this purpose and as you can see from the MSDN: The ObReferenceObjectByHandle routine provides access validation on the object handle, and, if access can be granted, returns the corresponding pointer to the object’s body.If you search for this function in Ntoskrnl.exe using IDA Pro, you’ll see something like this :Functions to check for access rights of a handleIt seems that there are other functions for this purpose, moreover, if you look at the decompiled source of NtOpenProcessTokenEx you see that ObReferenceObjectByHandle is no longer there instead it replaced by ObpReferenceObjectByHandleWithTag so we should also add this function into our list.NtOpenProcessToken Decompiled SourceFrom the ObpReferenceObjectByHandleWithTag you can see that the contarary still remains in MSDN. If you look at the second argument to ObpReferenceObjectByHandleWithTag you can see 0x1000 and it’s, of course, PROCESS_QUERY_LIMITED_INFORMATION while the MSDN have mentioned something else.PROCESS_QUERY_LIMITED_INFORMATIONThe documentation for OpenProcessToken:OpenProcessTokenFunctions to TestActually, we just need to test the following function (from the functions available in IDA search results):CmObReferenceObjectByHandle ObReferenceObjectByHandleObpReferenceObjectByHandleWithTagObReferenceObjectByHandleWithTagOther functions are not really important to us, as I saw their XREFs, for example :VerifierObReferenceObjectByHandleAs you can see the above function doesn’t seems to valuable in our case from the XREFs results.IDA Python ScriptThe following code is the IDA Python script for finding XREFs of our target function then find the second argument from the decompiled source:#!/usr/bin/python# -*- coding: utf-8 -*-from idautils import *from idaapi import *from idc import *FunctionName = 'ObReferenceObjectByHandle'def PrintDecompiledLine(line): print 'Decompiled Line : ' + line print 'ACCESS_MASK : ' + line.split(FunctionName)[1].split(',' )[1] print ''# Finding Functionea = BeginEA()# for funcAddr in Functions(SegStart(ea), SegEnd(ea)):for funcAddr in Functions(0x140001000, 0x140909410): funcName = GetFunctionName(funcAddr) if funcName == FunctionName: print 'Function %s is located at 0x%x' % (funcName, funcAddr) print '=====================================================' ea = funcAddrfor ref in CodeRefsTo(ea, 1): print 'Function : ' + GetFunctionName(ref) print '' try: cfunc = idaapi.decompile(ref) except: print 'Failed to decompile : ' + GetFunctionName(ref) multiline = False multilinestring = '' # print cfunc for item in str(cfunc).split('\\n'): if multiline: if ')' in item and not '(' in item: multiline = False multilinestring += item.strip() PrintDecompiledLine(multilinestring) else: multilinestring += item.strip() elif FunctionName in item: if ')' in item: PrintDecompiledLine(item.strip()) else: multiline = True multilinestring = item.strip() print '-------------------------------------------------'ResultsHere is the results, you can use them if you don’t have IDA Pro:Note: If you want to use the results, you can find functions which start with (NT*) that’s because these functions have a pair in user-mode ntdll, so if your user-mode function ends to a ntdll native function then you can search for the same function and see the reall access rights.ConclusionIn this post, you saw how it might be different, the real implementation over documentation so as a conclusion if you are a security researcher, it’s always better to check the real source code (decompiled) instead of just trusting the documentation.That’s it guys, hope you enjoy it.References[1] Reading Your Way Around UAC (Part 2) - (https://tyranidslair.blogspot.com/2017/05/reading-your-way-around-uac-part-2.html)[2] ObReferenceObjectByHandle function - (https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-obreferenceobjectbyhandle)[3] Process Security and Access Rights - (https://docs.microsoft.com/en-us/windows/desktop/procthread/process-security-and-access-rights)[4] Enumerate all XefsTo a Segment in IDAPython - (https://reverseengineering.stackexchange.com/questions/3669/enumerate-all-xefsto-a-segment-in-idapython)[5] OpenProcessToken function - (https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-openprocesstoken)[6] About the ACCESS_MASK Structure - (https://blogs.msdn.microsoft.com/openspecification/2010/04/01/about-the-access_mask-structure/)[7] ACCESS_MASK - (https://docs.microsoft.com/en-us/windows/desktop/SecAuthZ/access-mask)[8] ObpReferenceProcessObjectByHandle - (http://www.codewarrior.cn/ntdoc/wrk/ob/ObpReferenceProcessObjectByHandle.htm)" }, { "title": "Hypervisor From Scratch – Part 6: Virtualizing An Already Running System", "url": "/topics/hypervisor-from-scratch-part-6/", "categories": "cpu, hypervisor, tutorials", "tags": "hypervisor, control-registers-modification, cpuid-modification, hypervisor-from-scratch, hypervisor-logging, msr-modification-detection, virtualize-all-logical-cores, virtualize-current-system, vmfunc, vmx-0-settings-and-1-settings", "date": "2019-02-25 00:00:00 +0000", "snippet": "If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous practical examples on how to utilize hypervisors for reverse engineering.IntroductionHello and welcome to the 6th part of the tutorial Hypervisor From Scratch. In this part, we’ll learn how to virtualize an already running system using our custom-made hypervisor. Like other parts, this part depends on the previous parts, so make sure to read them first.Table of contents Introduction Table of contents Overview VMX 0-settings and 1-settings VMX-Fixed Bits in CR0 and CR4 Capturing the State of the Current Machine Configuring VMCS Fields Changing IRQL on all Cores Changing the User-Mode App Getting a handle using CreateFile Using VMX Monitoring Features CR3-Target Controls Handling guest CPUID execution Prevent CPUID Timing Leakages Instructions That Cause VM-exits Conditionally Control Registers Modification Detection MSR Bitmaps Handling MSRs Read Handling MSRs Write Turning off VMX and Exit from Hypervisor VM-Exit Handler Let’s Test it! Virtualizing all the cores Changing CPUID using Hypervisor Detecting MSR Read &amp; Write (MSR Bitmap) Conclusion ReferencesOverviewIn the 6th part, we’ll see how we can virtualize our currently running system by configuring VMCS. We use monitoring features of VMX to detect the execution of important instructions like CPUID (and change the result of CPUID from user-mode and kernel-mode), detect modifications on different control registers, and describe VMX capabilities on different microarchitectures, talking about MSR Bitmaps and lots of other cool things.Before starting, I should give my special thanks to my friend Petr Benes as he always solves my problems, explains to me patiently, and gives me ideas to implement a hypervisor from scratch.The full source code of this tutorial is available on GitHub :[https://github.com/SinaKarvandi/Hypervisor-From-Scratch]Note: Remember that hypervisors change over time because new features are added to the operating systems or new technologies are used. For example, updates to Meltdown &amp; Spectre have made a lot of changes to the hypervisors. So, if you want to use Hypervisor From Scratch in your projects, research, or whatever, you should use the HyperDbg drivers. HyperDbg is actively maintained, stable, and reliable, ensuring you avoid the errors and instability problems that can arise from using older parts of the tutorial series.Please make sure to have your own lab to test your hypervisor. I tested my hypervisor on the 7th generation of Intel processors, so if you use an older processor, it might not support some features on your processor, and without a remote kernel debugger (not the local kernel debugger), you might see your system halting or BSODs without understanding the actual error.VMX 0-settings and 1-settingsIn the previous parts, we implemented a function called AdjustControl. This is an essential part of each hypervisor as you might want to run your hypervisor on many different processors with different microarchitectures. We should be aware of our processor capabilities to avoid undefined behaviors and VM-Entry errors.This works like this; first, an MSR is sent the below function, which indicates the VMCS control that needs to be modified. Then we check the corresponding MSR to understand the 1-settings and 0-settings of the control. At last, we remove the not supported bits, set those that are mandatory to be 1, and configure the control.ULONGAdjustControls(ULONG Ctl, ULONG Msr){ MSR MsrValue = {0}; MsrValue.Content = __readmsr(Msr); Ctl &amp;= MsrValue.High; /* bit == 0 in high word ==&gt; must be zero */ Ctl |= MsrValue.Low; /* bit == 1 in low word ==&gt; must be one */ return Ctl;}If you remember from the previous part, we used the above function in 4 situations. __vmx_vmwrite(CPU_BASED_VM_EXEC_CONTROL, AdjustControls(CPU_BASED_ACTIVATE_MSR_BITMAP | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, MSR_IA32_VMX_PROCBASED_CTLS)); __vmx_vmwrite(SECONDARY_VM_EXEC_CONTROL, AdjustControls(CPU_BASED_CTL2_RDTSCP | CPU_BASED_CTL2_ENABLE_INVPCID | CPU_BASED_CTL2_ENABLE_XSAVE_XRSTORS, MSR_IA32_VMX_PROCBASED_CTLS2)); __vmx_vmwrite(PIN_BASED_VM_EXEC_CONTROL, AdjustControls(0, MSR_IA32_VMX_PINBASED_CTLS)); __vmx_vmwrite(VM_EXIT_CONTROLS, AdjustControls(VM_EXIT_IA32E_MODE /* | VM_EXIT_ACK_INTR_ON_EXIT */, MSR_IA32_VMX_EXIT_CTLS)); __vmx_vmwrite(VM_ENTRY_CONTROLS, AdjustControls(VM_ENTRY_IA32E_MODE, MSR_IA32_VMX_ENTRY_CTLS));A brief look at APPENDIX A -VMX CAPABILITY REPORTING FACILITY shows the explanation about RESERVED CONTROLS AND DEFAULT SETTINGS. In Intel VMX, certain controls are reserved and must be set to a specific value (0 or 1) determined by the processor. The specific value to which a reserved control must be set is its default setting. These kinds of settings vary for each processor and microarchitecture, but in general, there are three types of classes : Always-flexible: These have never been reserved. Default0: These are (or have been) reserved with a default setting of 0. Default1: They are (or have been) reserved with a default setting of 1.Now, There are separate capability MSRs for pin-based VM-execution controls, primary processor-based VM-execution controls, VM-Entry Controls, VM-Exit Controls and secondary processor-based VM-execution controls.These MSRs are used to check the above controls: MSR_IA32_VMX_PROCBASED_CTLS MSR_IA32_VMX_PROCBASED_CTLS2 MSR_IA32_VMX_EXIT_CTLS MSR_IA32_VMX_ENTRY_CTLS MSR_IA32_VMX_PINBASED_CTLSIn all of the above MSRs, bits 31:0 indicate the allowed 0-settings of these controls. VM entry allows control X (bit X) to be 0 if bit X in the MSR is cleared to 0; if bit X in the MSR is set to 1, VM entry fails if control X is 0. Meanwhile, bits 63:32 indicate the allowed 1-settings of these controls. VM entry allows control X to be 1 if bit 32+X in the MSR is set to 1; if bit 32+X in the MSR is cleared to 0, VM entry fails if control X is 1.Although there are some exceptions, now, you should understand the purpose of AdjustControls as it first reads the MSR corresponding to the VM-execution control, then adjusts the 0-settings and 1-settings, and return the final result.I recommend seeing the result of AdjustControls specifically for MSR_IA32_VMX_PROCBASED_CTLS and MSR_IA32_VMX_PROCBASED_CTLS2 as you might unintentionally set some of the bits to 1 so, you should have a plan for handling some VM-Exits based on your specific processor.VMX-Fixed Bits in CR0 and CR4For CR0, IA32_VMX_CR0_FIXED0 MSR (index 486H) and IA32_VMX_CR0_FIXED1 MSR (index 487H) and for CR4 IA32_VMX_CR4_FIXED0 MSR (index 488H) and IA32_VMX_CR4_FIXED1 MSR (index 489H) indicate how bits in CR0 and CR4 may be set in VMX operation. If bit X is 1 in IA32_VMX_CRx_FIXED0, then that bit of CRx is fixed to 1 in VMX operation. Similarly, if bit X is 0 in IA32_VMX_CRx_FIXED1, then that bit of CRx is fixed to 0 in VMX operation. It is always the case that if bit X is 1 in IA32_VMX_CRx_FIXEDx, then that bit is also 1 in IA32_VMX_CRx_FIXED1.Capturing the State of the Current MachineIn the 5th part, we saw how to configure different VMCS fields and finally execute our instruction (HLT) under the guest context. This part is similar to the last part, with some minor changes in some VMCS attributes. Let’s review and see the differences.The first thing you need to know is that you have to create different stacks for each core as we’re going to virtualize all the cores simultaneously. These stacks will be used whenever a VM-Exit occurs. // // Allocate stack for the VM Exit Handler // UINT64 VmmStackVa = ExAllocatePoolWithTag(NonPagedPool, VMM_STACK_SIZE, POOLTAG); g_GuestState[ProcessorID].VmmStack = VmmStackVa; if (g_GuestState[ProcessorID].VmmStack == NULL) { DbgPrint("[*] Error in allocating VMM Stack\\n"); return FALSE; } RtlZeroMemory(g_GuestState[ProcessorID].VmmStack, VMM_STACK_SIZE); DbgPrint("[*] VMM Stack for logical processor %d : %llx\\n", ProcessorID, g_GuestState[ProcessorID].VmmStack);As you can see from the above code, we use VmmStack for each core separately (defined in the VIRTUAL_MACHINE_STATE structure).All the other things like clearing the VMCS state, loading VMCS, and executing VMLAUNCH are exactly the same as the previous part, so I don’t want to describe them again but see the function responsible for preparing our current core to be virtualized.VOIDVirtualizeCurrentSystem(int ProcessorID, PEPTP EPTP, PVOID GuestStack){ DbgPrint("\\n======================== Virtualizing Current System (Logical Core 0x%x) =============================\\n", ProcessorID); // // Clear the VMCS State // if (!ClearVmcsState(&amp;g_GuestState[ProcessorID])) { goto ErrorReturn; } // // Load VMCS (Set the Current VMCS) // if (!LoadVmcs(&amp;g_GuestState[ProcessorID])) { goto ErrorReturn; } DbgPrint("[*] Setting up VMCS for current system.\\n"); SetupVmcsAndVirtualizeMachine(&amp;g_GuestState[ProcessorID], EPTP, GuestStack); // // Change this hook (detect modification of MSRs using RDMSR &amp; WRMSR) // // DbgPrint("[*] Setting up MSR bitmaps.\\n"); DbgPrint("[*] Executing VMLAUNCH.\\n"); __vmx_vmlaunch(); // // if VMLAUNCH succeeds will never be here! // ULONG64 ErrorCode = 0; __vmx_vmread(VM_INSTRUCTION_ERROR, &amp;ErrorCode); __vmx_off(); DbgPrint("[*] VMLAUNCH Error : 0x%llx\\n", ErrorCode); DbgBreakPoint(); DbgPrint("\\n===================================================================\\n");ReturnWithoutError: __vmx_off(); DbgPrint("[*] VMXOFF Executed Successfully!\\n"); return TRUE; // // Return With Error //ErrorReturn: DbgPrint("[*] Fail to setup VMCS!\\n"); return FALSE;}From the above code, SetupVmcsAndVirtualizeMachine is new, so let’s see what’s inside this function.Configuring VMCS FieldsVMCS Fields are nothing new. We should configure these fields to manage the state of the virtualized core.All the VMCS fields are the same as the last part, except for the configuration of VMCS control bits: DbgPrint("[*] MSR_IA32_VMX_PROCBASED_CTLS : 0x%llx\\n", AdjustControls(CPU_BASED_ACTIVATE_MSR_BITMAP | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, MSR_IA32_VMX_PROCBASED_CTLS)); DbgPrint("[*] MSR_IA32_VMX_PROCBASED_CTLS2 : 0x%llx\\n", AdjustControls(CPU_BASED_CTL2_RDTSCP | CPU_BASED_CTL2_ENABLE_INVPCID | CPU_BASED_CTL2_ENABLE_XSAVE_XRSTORS, MSR_IA32_VMX_PROCBASED_CTLS2)); __vmx_vmwrite(CPU_BASED_VM_EXEC_CONTROL, AdjustControls(CPU_BASED_ACTIVATE_MSR_BITMAP | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS , MSR_IA32_VMX_PROCBASED_CTLS)); __vmx_vmwrite(SECONDARY_VM_EXEC_CONTROL, AdjustControls(CPU_BASED_CTL2_RDTSCP | CPU_BASED_CTL2_ENABLE_INVPCID | CPU_BASED_CTL2_ENABLE_XSAVE_XRSTORS, MSR_IA32_VMX_PROCBASED_CTLS2));As you can see, for the CPU_BASED_VM_EXEC_CONTROL, we set CPU_BASED_ACTIVATE_MSR_BITMAP; this way, we can enable the MSR BITMAP filter (described later in this part). Setting this field is somehow mandatory. As you might guess, Windows accesses lots of MSRs during a simple kernel execution, so if we don’t set this bit, then we’ll exit on each MSR access, and of course, our VMX Exit-Handler is called, hence clearing this bit to zero makes the system substantially slower.For the SECONDARY_VM_EXEC_CONTROL, we use CPU_BASED_CTL2_RDTSCP to enable RDTSCP, CPU_BASED_CTL2_ENABLE_INVPCID to enable INVPCID and the CPU_BASED_CTL2_ENABLE_XSAVE_XRSTORS to enable XSAVE and XRSTORS.It’s because I run the above code in my Windows 10 1809 and see that Windows uses INVPCID and XSAVE for its internal use (in the processors that support these features), so if you didn’t enable them before virtualizing the core, then it probably leads to error.Note that RDTSCP reads the current value of the processor’s time-stamp counter into the EDX:EAX registers and also reads the value of the IA32_TSC_AUX MSR (address C0000103H) into the ECX register. This instruction adds ordering to RDTSC and makes performance measures more accurate than RDTSC.INVPCID invalidates mappings in the translation lookaside buffers (TLBs) and paging-structure caches based on the process-context identifier (PCID), and XSAVE Performs a full or partial save of processor state components to the XSAVE area located at the memory address specified by the destination operand.Please ensure to review the final value that you put on these fields as your processor might not support all these features, so you have to implement some additional functions or ignore some of them.Nothing is left in this function except GuestStack, which is used as the GUEST_RSP. I’ll tell you what to put in this argument later.__vmx_vmwrite(GUEST_RSP, (ULONG64)GuestStack); //setup guest spOK, now the problem is from where we can start our hypervisor. I mean, how to save the state of a particular core, then execute the VMLAUNCH instruction on it, and then continue the rest of the execution.For this purpose, I’ve changed the DrvCreate routine, so you must change CreateFile from the user-mode application (I will discuss it later). In fact, DrvCreate is the function responsible for putting all the cores in the VMX state. First, it queries the core’s count and then performs the necessary initialization for each core.NTSTATUSDrvCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ DbgPrint("[*] DrvCreate Called !\\n"); // // *** Start Virtualizing Current System *** // // // Initiating EPTP and VMX // PEPTP EPTP = InitializeEptp(); InitializeVmx(); int LogicalProcessorsCount = KeQueryActiveProcessorCount(0); for (size_t i = 0; i &lt; LogicalProcessorsCount; i++) { // Launching VM for Test (in the all logical processor) int ProcessorID = i; // Allocating VMM Stack AllocateVmmStack(ProcessorID); // Allocating MSR Bit AllocateMsrBitmap(ProcessorID); RunOnProcessor(i, EPTP, VmxSaveState); DbgPrint("\\n======================================================================================================\\n", ProcessorID); } Irp-&gt;IoStatus.Status = STATUS_SUCCESS; Irp-&gt;IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS;}Our tiny driver is designed to be used in uni-core, two, three, and even all the cores. As you can see from the code below, it queries the logical processor count. int LogicalProcessorsCount = KeQueryActiveProcessorCount(0);You can edit this line to virtualize a certain number of cores or just a specific core, but the above code virtualizes all the cores by default.Changing IRQL on all CoresThere is a function called RunOnProcessor. This function takes processor ID as its first parameter, the EPTP pointer (explained in the 4th part) as the second parameter, and a particular routine called VmxSaveState as the third.RunOnProcessor sets the processor affinity to a special core, then it raises the IRQL to Dispatch-Level so the Windows Scheduler can’t kick in to change the context; thus, it runs our routine, and when it returns from VmxSaveState, the currently running core is virtualized so it can lower the IRQL to what it was before. Now Windows can continue its normal execution while it is under the hypervisor’s governance. IRQL stands for Interrupt Request Level, a Windows-specific mechanism to manage interrupts or give priority by their level, so raising IRQL means your routine will execute with higher priority than normal Windows codes (PASSIVE\\_LEVEL &amp; APC_LEVEL). For more information, you can visit here.The RunOnProcessor code is shown below.BOOLEANRunOnProcessor(ULONG ProcessorNumber, PEPTP EPTP, PFUNC Routine){ KIRQL OldIrql; KeSetSystemAffinityThread((KAFFINITY)(1 &lt;&lt; ProcessorNumber)); OldIrql = KeRaiseIrqlToDpcLevel(); Routine(ProcessorNumber, EPTP); KeLowerIrql(OldIrql); KeRevertToUserAffinityThread(); return TRUE;}VmxSaveState has to save the state and call another function, VirtualizeCurrentSystem.We have to use this function in the assembly file (VMXState.asm) as all VmxSaveState is implemented in assembly. For using a C function, in the assembly, we can write the function name and use the EXTERN keyword.The following example shows how we can use the VirtualizeCurrentSystem in the assembly file.EXTERN VirtualizeCurrentSystem:PROCThe VMXSaveState is implemented like this (in assembly):VmxSaveState PROC PUSH RAX PUSH RCX PUSH RDX PUSH RBX PUSH RBP PUSH RSI PUSH RDI PUSH R8 PUSH R9 PUSH R10 PUSH R11 PUSH R12 PUSH R13 PUSH R14 PUSH R15 SUB RSP, 28h ; It a x64 FastCall function but as long as the definition of SaveState is the same ; as VirtualizeCurrentSystem, so we RCX &amp; RDX both have a correct value ; But VirtualizeCurrentSystem also has a stack, so it's the third argument ; and according to FastCall, it should be in R8 MOV R8, RSP CALL VirtualizeCurrentSystem RETVmxSaveState ENDPIt first saves the state of all registers, subtracts the stack because of Shadow Space for fast call functions, and then puts RSP to **R8 and calls the VirtualizeCurrentSystem. RSP should be moved into the R8 (as I told you for GuestStack ) because the x64 fastcall parameter should be passed in this order: RCX, RDX, R8, R9 + Stack. This means that our third argument to this function is current RSP, and this value will be used as GUEST_RSP in the VMCS.If the above function runs without error, we should never reach to ret instruction as the state will later continue in another function called VmxRestoreState.As we can see in the VirtualizeCurrentSystem, which eventually calls SetupVmcsAndVirtualizeMachine, the GUEST_RIP is pointing to VmxRestoreState, so the first routine that executes in the current core is VmxRestoreState.This function is defined like this :VmxRestoreState PROC ADD RSP, 28h POP R15 POP R14 POP R13 POP R12 POP R11 POP R10 POP R9 POP R8 POP RDI POP RSI POP RBP POP RBX POP RDX POP RCX POP RAX RET VmxRestoreState ENDPIn the above function, first, we remove the Shadow Space and restore the registers state.When we return to RunOnProcessor, it’s time to lower the IRQL.This function will be called many times (based on our logical cores count), and eventually, all of our cores are under VMX operation, and now we are in the VMX non-root operation.Changing the User-Mode AppBased on the above assumptions, we have to make some trivial changes in our user-mode application so after loading the driver, it can be used to notify kernel-mode code to start and finally end of loading the hypervisor.Getting a handle using CreateFileAfter some checks for the vendor and presence of hypervisor, now we have to call DrvCreate from the kernel-mode, and it’s through the CreateFile user-mode function. HANDLE Handle = CreateFile("\\\\\\\\.\\\\MyHypervisorDevice", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, /// lpSecurityAttirbutes OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); /// lpTemplateFile if (Handle == INVALID_HANDLE_VALUE) { DWORD ErrNum = GetLastError(); printf("[*] CreateFile failed : %d\\n", ErrNum); return 1; }CreateFile gives us a handle that can be used in our future functions to interact with our driver. Still, whenever we close the application or call CloseHandle in the user-mode, the DrvClose is automatically called in the kernel. DrvClose turns off the hypervisor and restores the state to what it was before (not virtualized).Using VMX Monitoring FeaturesAfter configuring all the above fields, it’s time to use the monitoring features of the VMX. We’ll see how these features are unique in the case of security applications or reverse engineering tasks. As an extra resource, you can use HyperDbg Debugger. HyperDbg is a hypervisor-based debugger that allows us to use most of these VT-x features in our debugging journey.CR3-Target ControlsThe VM-execution control fields include a set of 4 CR3-target values and a CR3-target count. If you remember the VMCS fields that I presented before in the SetupVmcsAndVirtualizeMachine, you can see the following lines : __vmx_vmwrite(CR3_TARGET_COUNT, 0); __vmx_vmwrite(CR3_TARGET_VALUE0, 0); __vmx_vmwrite(CR3_TARGET_VALUE1, 0); __vmx_vmwrite(CR3_TARGET_VALUE2, 0); __vmx_vmwrite(CR3_TARGET_VALUE3, 0);Intel defines CR3-Target controls like this :An execution of MOV to CR3 in VMX non-root operation does not cause a VM exit if its source operand matches one of these values. If the CR3-target count is n, only the first n CR3-target values are considered.The implementation of using this feature is like this :BOOLEANSetTargetControls(UINT64 CR3, UINT64 Index){ // // Index starts from 0 , not 1 // if (Index &gt;= 4) { // // Not supported for more than 4 , at least for now :( // return FALSE; } UINT64 temp = 0; if (CR3 == 0) { if (g_Cr3TargetCount &lt;= 0) { // // Invalid command as g_Cr3TargetCount cannot be less than zero // s return FALSE; } else { g_Cr3TargetCount -= 1; if (Index == 0) { __vmx_vmwrite(CR3_TARGET_VALUE0, 0); } if (Index == 1) { __vmx_vmwrite(CR3_TARGET_VALUE1, 0); } if (Index == 2) { __vmx_vmwrite(CR3_TARGET_VALUE2, 0); } if (Index == 3) { __vmx_vmwrite(CR3_TARGET_VALUE3, 0); } } } else { if (Index == 0) { __vmx_vmwrite(CR3_TARGET_VALUE0, CR3); } if (Index == 1) { __vmx_vmwrite(CR3_TARGET_VALUE1, CR3); } if (Index == 2) { __vmx_vmwrite(CR3_TARGET_VALUE2, CR3); } if (Index == 3) { __vmx_vmwrite(CR3_TARGET_VALUE3, CR3); } g_Cr3TargetCount += 1; } __vmx_vmwrite(CR3_TARGET_COUNT, g_Cr3TargetCount); return TRUE;}I don’t have any good example of how this control might be helpful in a regular Windows as there are thousands of CR3 changes for each process. Still, one of my friends told me that it’s used in some special cases in scientific projects to improve the overall performance.Handling guest CPUID executionCPUID is an instruction that unconditionally causes VM-exit. As you know, CPUID is used because it allows the software to discover details of the processor. It is also used for flushing the pipeline for processors that don’t support instructions like RDTSCP, so they can use CPUID + RDTSC and use CPUID as a barrier.Whenever any software in any privilege level executes a CPUID instruction, our vm-exit handler is called, and now we can decide whatever we want to show to the software. For example, previously, I published an article “Defeating malware’s Anti-VM techniques (CPUID-Based Instructions)”. This article describes how to configure VMware Workstation in a way that changes the CPUID instruction results so that the malware with an anti-VM technique can’t understand that they’re executing in a virtualized environment. VMware Workstation (and other virtual environments) perform the same mechanism for handling CPUID. In the following example, I just passed the state of registers (state of registers before the VM-exits) to the HandleCPUID. This function decides whether the requested CPUID should have a modified result or just execute passthrough the original results.The default behavior for handling every VM-Exit (caused by execution of CPUID in VMX non-root) is to get the original result by using _cpuidex, which is the intrinsic function for CPUID. __cpuidex(CpuInfo, (INT32)state-&gt;rax, (INT32)state-&gt;rcx);As you can see, VMX non-root by itself isn’t able to execute a CPUID, and we can execute CPUID in VMX root-mode and give back the results to the VMX non-root mode.We need to check if RAX (CPUID Index) was 1 or not. It’s because there is an indicator bit that shows whether the current machine is running under a hypervisor or not. Like many other virtual machines, we set the present hypervisor bit (the constant used in this example is like hyper-v’s bit HYPERV_HYPERVISOR_PRESENT_BIT) to show that we’re running under a hypervisor.There is a second check about the hypervisor provider. We set it to ‘HVFS’ to show that our hypervisor is [H]yper[V]isor [F]rom [S]cratch. // // Check if this was CPUID 1h, which is the features request // if (state-&gt;rax == 1) { // // Set the Hypervisor Present-bit in RCX, which Intel and AMD have both // reserved for this indication // CpuInfo[2] |= HYPERV_HYPERVISOR_PRESENT_BIT; } else if (state-&gt;rax == HYPERV_CPUID_INTERFACE) { // // Return our interface identifier // CpuInfo[0] = 'HVFS'; // [H]yper[V]isor [F]rom [S]cratch }We can easily add more checks to the above code and customize our CPUID filter, for instance, changing our computer vendor string, etc.Finally, we put them into registers so that the guest has a proper result every time our routine is executed. // // Copy the values from the logical processor registers into the VP GPRs // state-&gt;rax = CpuInfo[0]; state-&gt;rbx = CpuInfo[1]; state-&gt;rcx = CpuInfo[2]; state-&gt;rdx = CpuInfo[3];Putting all the above codes together, we have the following function:BOOLEANHandleCPUID(PGUEST_REGS state){ INT32 CpuInfo[4]; ULONG Mode = 0; // // Check for the magic CPUID sequence, and check that it is coming from // Ring 0. Technically we could also check the RIP and see if this falls // in the expected function, but we may want to allow a separate "unload" // driver or code at some point // __vmx_vmread(GUEST_CS_SELECTOR, &amp;Mode); Mode = Mode &amp; RPL_MASK; if ((state-&gt;rax == 0x41414141) &amp;&amp; (state-&gt;rcx == 0x42424242) &amp;&amp; Mode == DPL_SYSTEM) { return TRUE; // Indicates we have to turn off VMX } // // Otherwise, issue the CPUID to the logical processor based on the indexes // on the VP's GPRs // __cpuidex(CpuInfo, (INT32)state-&gt;rax, (INT32)state-&gt;rcx); // // Check if this was CPUID 1h, which is the features request // if (state-&gt;rax == 1) { // // Set the Hypervisor Present-bit in RCX, which Intel and AMD have both // reserved for this indication // CpuInfo[2] |= HYPERV_HYPERVISOR_PRESENT_BIT; } else if (state-&gt;rax == HYPERV_CPUID_INTERFACE) { // // Return our interface identifier // CpuInfo[0] = 'HVFS'; // [H]yper[V]isor [F]rom [S]cratch } // // Copy the values from the logical processor registers into the VP GPRs // state-&gt;rax = CpuInfo[0]; state-&gt;rbx = CpuInfo[1]; state-&gt;rcx = CpuInfo[2]; state-&gt;rdx = CpuInfo[3]; return FALSE; // Indicates we don't have to turn off VMX}It’s somehow like instruction level hooking for CPUID. Also, you can have the same handling functions for many other important instructions by configuring the primary and secondary processor-based controls. Later we describe some of these instructions.Prevent CPUID Timing LeakagesAs an extra explanation about the hypervisor, CPUID is one of the ways that cause user-mode or kernel-mode software to detect the presence of hypervisor by using delta timing side-channel attacks. It originates from the fact that this instruction leads to an unconditional VM-exit which, in the case of a hypervisor, it takes much longer to execute in contrast with a non-virtualized machine.The description of these attacks is out of this article’s scope, but in case you’re interested, you can read a detailed explanation about these attacks in this paper.Instructions That Cause VM-exits ConditionallyHere is a list of instructions that cause VM-exits in VMX non-root operation depending on the setting of the VM-execution controls. - CLTS - ENCLS - HLT - IN, INS/INSB/INSW/INSD, OUT, OUTS/OUTSB/OUTSW/OUTSD. - INVLPG - INVPCID - LGDT, LIDT, LLDT, LTR, SGDT, SIDT, SLDT, STR - LMSW - MONITOR - MOV from CR3/CR8, MOV to CR0/1/3/4/8 - MOV DR - MWAIT - PAUSE - RDMSR, WRMSR - RDPMC - RDRAND, RDSEED - RDTSC, RDTSCP - RSM - VMREAD, VMWRITE - WBINVD - XRSTORS, XSAVESControl Registers Modification DetectionDetecting and handling Control Registers’ (CR) modifications is one of the great monitoring features provided by hypervisors.Imagine if someone exploits the Windows Kernel (or any other OSs) and wants to unset one of the control register bits (let’s say Write Protected orSMEP); then the hypervisor detects this modification and prevents further execution. Note that SMEP stands for Supervisor Mode Execution Protection. CR4.SMEP allows pages to be protected from supervisor-mode instruction fetches. If CR4.SMEP = 1, software operating in supervisor mode cannot fetch instructions from linear addresses that are accessible in user mode, and WP stands for Write Protect. CR0.WP allows pages to be protected from supervisor-mode writes. If CR0.WP = 0, supervisor-mode write accesses are allowed to linear addresses with read-only access rights; if CR0.WP = 1, they are not (User-mode write accesses are never allowed to linear addresses with read-only access rights, regardless of the value of CR0.WP).Now it’s time to implement our functions.First, let’s read the GUEST_CRs and EXIT_QUALIFICATION of the VMCS. __vmx_vmread(EXIT_QUALIFICATION , &amp;ExitQualification); __vmx_vmread(GUEST_CR0 , &amp;GuestCR0); __vmx_vmread(GUEST_CR3 , &amp;GuestCR3); __vmx_vmread(GUEST_CR4, &amp;GuestCR4);As you can see, the following picture shows how we can interpret Exit Qualifications.Note that EXIT_QUALIFICATION is somehow a general VMCS field that, in some situations like VM-exits caused by Invalid VMCS Layout, Control Register Modifications, I/O Bitmaps, and other events, gives additional information about the reason for the VM-exit.As you can see from the above picture, let’s make some variables to describe the situation based on EXIT_QUALIFICATION.Whenever a VM-Exit occurs caused by instructions like MOV CRx, REG, we have to manually modify the CRx of guest VMCS from VMX-root mode. The following code shows how to change the GUEST_CRx field of VMCS using VMWRITE. case TYPE_MOV_TO_CR: { switch (data-&gt;Fields.ControlRegister) { case 0: __vmx_vmwrite(GUEST_CR0, *RegPtr); __vmx_vmwrite(CR0_READ_SHADOW, *RegPtr); break; case 3: __vmx_vmwrite(GUEST_CR3, (*RegPtr &amp; ~(1ULL &lt;&lt; 63))); // // In the case of using EPT, the context of EPT/VPID should be // invalidated // break; case 4: __vmx_vmwrite(GUEST_CR4, *RegPtr); __vmx_vmwrite(CR4_READ_SHADOW, *RegPtr); break; default: DbgPrint("[*] Unsupported register %d\\n", data-&gt;Fields.ControlRegister); break; } } break;Otherwise, we have to read the CRx from our guest VMCS (not host Control Register as it might be different), then put it into the corresponding registers (in registers that we saved when the VM-exit handler called), then continue with VMRESUME. This way, the guest thinks as if it executed the MOV reg, CRx successfully. case TYPE_MOV_FROM_CR: { switch (data-&gt;Fields.ControlRegister) { case 0: __vmx_vmread(GUEST_CR0, RegPtr); break; case 3: __vmx_vmread(GUEST_CR3, RegPtr); break; case 4: __vmx_vmread(GUEST_CR4, RegPtr); break; default: DbgPrint("[*] Unsupported register %d\\n", data-&gt;Fields.ControlRegister); break; } }Putting it all together, we have a function like this :VOIDHandleControlRegisterAccess(PGUEST_REGS GuestState){ ULONG ExitQualification = 0; __vmx_vmread(EXIT_QUALIFICATION, &amp;ExitQualification); PMOV_CR_QUALIFICATION data = (PMOV_CR_QUALIFICATION)&amp;ExitQualification; PULONG64 RegPtr = (PULONG64)&amp;GuestState-&gt;rax + data-&gt;Fields.Register; // // Because its RSP and as we didn't save RSP correctly (because of pushes) // so we have to make it points to the GUEST_RSP // if (data-&gt;Fields.Register == 4) { INT64 RSP = 0; __vmx_vmread(GUEST_RSP, &amp;RSP); *RegPtr = RSP; } switch (data-&gt;Fields.AccessType) { case TYPE_MOV_TO_CR: { switch (data-&gt;Fields.ControlRegister) { case 0: __vmx_vmwrite(GUEST_CR0, *RegPtr); __vmx_vmwrite(CR0_READ_SHADOW, *RegPtr); break; case 3: __vmx_vmwrite(GUEST_CR3, (*RegPtr &amp; ~(1ULL &lt;&lt; 63))); // // In the case of using EPT, the context of EPT/VPID should be // invalidated // break; case 4: __vmx_vmwrite(GUEST_CR4, *RegPtr); __vmx_vmwrite(CR4_READ_SHADOW, *RegPtr); break; default: DbgPrint("[*] Unsupported register %d\\n", data-&gt;Fields.ControlRegister); break; } } break; case TYPE_MOV_FROM_CR: { switch (data-&gt;Fields.ControlRegister) { case 0: __vmx_vmread(GUEST_CR0, RegPtr); break; case 3: __vmx_vmread(GUEST_CR3, RegPtr); break; case 4: __vmx_vmread(GUEST_CR4, RegPtr); break; default: DbgPrint("[*] Unsupported register %d\\n", data-&gt;Fields.ControlRegister); break; } } break; default: DbgPrint("[*] Unsupported operation %d\\n", data-&gt;Fields.AccessType); break; }}The reason why implementing functions like HandleControlRegisterAccess is necessary is because some processors have 1-settings of some processor-based VM-execution controls like CR3-Load Exiting &amp; CR3-Store Existing, so we have to manage these kinds of VM-exits by ourselves, but if our processor can continue without these settings, it’s strongly recommended to reduce the amounts of VM-exits and avoid configuring the settings that lead to these kinds of VM-exits because modern OSs access control registers a lot; thus, it has a significant performance penalty.MSR BitmapsEverything here is based on whether you set the 28th bit of Primary Processor Based controls or not.On processors that support the 1-setting of the “use MSR bitmaps” VM-execution control, the VM-execution control fields include the 64-bit physical address of four contiguous MSR bitmaps, which are each 1-KByte in size.The definition of MSR bitmap is pretty clear in Intel SDM, so I just copied them from the original manual. After reading them, we’ll start to implement them and put them into our hypervisor. Read bitmap for low MSRs (located at the MSR-bitmap address). This contains one bit for each MSR address in the range 00000000H to 00001FFFH. The bit determines whether the execution of RDMSR applied to that MSR causes a VM-exit. Read bitmap for high MSRs (located at the MSR-bitmap address plus 1024). This contains one bit for each MSR address in the range C0000000H toC0001FFFH. The bit determines whether the execution of RDMSR applied to that MSR causes a VM-exit. Write bitmap for low MSRs (located at the MSR-bitmap address plus 2048). This contains one bit for each MSR address in the range 00000000H to 00001FFFH. The bit determines whether the execution of WRMSR applied to that MSR causes a VM-exit. Write bitmap for high MSRs (located at the MSR-bitmap address plus 3072). This contains one bit for each MSR address in the range C0000000H toC0001FFFH. The bit determines whether the execution of WRMSR applied to that MSR causes a VM-exit. OK, let’s bring the above sentences into the codes. First, we’ll write our handler for MSR VM-exits.Handling MSRs Read**In our passthrough hypervisor, if any of the RDMSR or WRMSR caused a VM-exit, we have to manually execute RDMSR or WRMSR and set the results into the registers. Because of this, we have a function to manage our RDMSRs like :VOIDHandleMSRRead(PGUEST_REGS GuestRegs){ MSR msr = {0}; // // RDMSR. The RDMSR instruction causes a VM exit if any of the following are true: // // The "use MSR bitmaps" VM-execution control is 0. // The value of ECX is not in the ranges 00000000H - 00001FFFH and C0000000H - C0001FFFH // The value of ECX is in the range 00000000H - 00001FFFH and bit n in read bitmap for low MSRs is 1, // where n is the value of ECX. // The value of ECX is in the range C0000000H - C0001FFFH and bit n in read bitmap for high MSRs is 1, // where n is the value of ECX &amp; 00001FFFH. // if (((GuestRegs-&gt;rcx &lt;= 0x00001FFF)) || ((0xC0000000 &lt;= GuestRegs-&gt;rcx) &amp;&amp; (GuestRegs-&gt;rcx &lt;= 0xC0001FFF))) { msr.Content = MSRRead((ULONG)GuestRegs-&gt;rcx); } else { msr.Content = 0; } GuestRegs-&gt;rax = msr.Low; GuestRegs-&gt;rdx = msr.High;}You can see that it just checks for the sanity of MSR and then executes the RDMSR and ultimately put the results into RAX and RDX (because a non-virtualized RDMSR does the same thing).Handling MSRs WritesThere is another function for handling WRMSR VM-exits :VOIDHandleMSRWrite(PGUEST_REGS GuestRegs){ MSR msr = {0}; // // Check for the sanity of MSR // if ((GuestRegs-&gt;rcx &lt;= 0x00001FFF) || ((0xC0000000 &lt;= GuestRegs-&gt;rcx) &amp;&amp; (GuestRegs-&gt;rcx &lt;= 0xC0001FFF))) { msr.Low = (ULONG)GuestRegs-&gt;rax; msr.High = (ULONG)GuestRegs-&gt;rdx; MSRWrite((ULONG)GuestRegs-&gt;rcx, msr.Content); }}The functionality of the function is simple. Still, one thing that is worth experimenting by yourself is to avoid setting CPU_BASED_ACTIVATE_MSR_BITMAP in CPU_BASED_VM_EXEC_CONTROL, you’ll see that all of the MSR reads and modifications will cause a VM-exit with these reasons : EXIT_REASON_MSR_READ EXIT_REASON_MSR_WRITEThis time, we have to pass everything to the above functions and log these VM-exits, so you can see what are MSRs that Windows use while running in the hypervisor. As I told you above, Windows executes a vast amount of MSR instructions, so it can make your system much slower than you can bear it.OK, let’s get back to our MSR Bitmap. We need two functions to Set bits of our MSR Bitmap.VOIDSetBit(PVOID Addr, UINT64 Bit, BOOLEAN Set){ PAGED_CODE(); UINT64 Byte = Bit / 8; UINT64 Temp = Bit % 8; UINT64 N = 7 - Temp; BYTE * Addr2 = Addr; if (Set) { Addr2[Byte] |= (1 &lt;&lt; N); } else { Addr2[Byte] &amp;= ~(1 &lt;&lt; N); }}The other function is for retrieving a particular bit.VOIDGetBit(PVOID Addr, UINT64 Bit){ UINT64 Byte = 0, K = 0; Byte = Bit / 8; K = 7 - Bit % 8; BYTE * Addr2 = Addr; return Addr2[Byte] &amp; (1 &lt;&lt; K);}Now it’s time to gather everything in one function based on the above descriptions about MSR Bitmaps. The following function first checks for the sanity of MSR; then, it changes the MSR Bitmap of the target logical core (this is why we hold both the Physical Address and the Virtual Address of MSR Bitmap, the physical address for VMCS fields, and the virtual address to ease the modification and future deallocation). If it’s a read (RDMSR) for low MSRs, then set the corresponding bit in MSR Bitmap Virtual Address, if it’s a write (WRMSR) for the low MSRs, then modify the MSR Bitmap + 2048 (as noted in Intel manual) and exact the same thing for high MSRs (between 0xC0000000 and 0xC0001FFF) but don’t forget the subtraction (0xC0000000) because 0xC000nnnn is not a valid bit.BOOLEANSetMsrBitmap(ULONG64 Msr, int ProcessID, BOOLEAN ReadDetection, BOOLEAN WriteDetection){ if (!ReadDetection &amp;&amp; !WriteDetection) { // // Invalid Command // return FALSE; } if (Msr &lt;= 0x00001FFF) { if (ReadDetection) { SetBit(g_GuestState[ProcessID].MsrBitmap, Msr, TRUE); } if (WriteDetection) { SetBit(g_GuestState[ProcessID].MsrBitmap + 2048, Msr, TRUE); } } else if ((0xC0000000 &lt;= Msr) &amp;&amp; (Msr &lt;= 0xC0001FFF)) { if (ReadDetection) { SetBit(g_GuestState[ProcessID].MsrBitmap + 1024, Msr - 0xC0000000, TRUE); } if (WriteDetection) { SetBit(g_GuestState[ProcessID].MsrBitmap + 3072, Msr - 0xC0000000, TRUE); } } else { return FALSE; } return TRUE;}Just one more thing to remember, only the above MSR ranges are currently valid in Intel processors, so even any other RDMSRs and WRMSRs cause a VM-exit. Still, the sanity check here is mandatory as the guest might send invalid MSRs and cause the whole system to crash (in VMX root-mode). In the future parts, when we learn about the event injection, we’ll simulate the physical machine’s behavior by injecting events to the guest in the case when the guest attempted to access an invalid MSR.Turning off VMX and Exit from HypervisorIt’s time to turn off our hypervisor and restore the processor state to what it was before running the hypervisor.Like how we enter hypervisor (VMLAUNCH), we have to combine our C functions with Assembly routines to save the state, execute VMXOFF, free all of our previously allocated pools, and finally restore the state.The VMXOFF part of this routine should be executed in the VMX root-mode. You can’t just execute __vmx_vmxoff in one of your driver functions and expect it turns off the hypervisor as Windows and all its drivers are currently running in VMX non-root, so executing any of the VMX instructions is like a VM-exit with one of the following reasons. EXIT_REASON_VMCLEAR EXIT_REASON_VMPTRLD EXIT_REASON_VMPTRST EXIT_REASON_VMREAD EXIT_REASON_VMRESUME EXIT_REASON_VMWRITE EXIT_REASON_VMXOFF EXIT_REASON_VMXON EXIT_REASON_VMLAUNCHFor turning off the hypervisor, it’s better to use one of our IRP Major functions. In our case, we used DrvClose as it always gets notified whenever a handle to our device is closed. If you remember from the above, we create a handle from our device using CreateFile (DrvCreate), and now it’s time to close our handle using DrvClose.NTSTATUSDrvClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ DbgPrint("[*] DrvClose Called !\\n"); // executing VMXOFF (From CPUID) on every logical processor TerminateVmx(); Irp-&gt;IoStatus.Status = STATUS_SUCCESS; Irp-&gt;IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS;}Nothing special about the above function; only the TerminateVmx is added.This function is similar to the routine of executing VMLAUNCH, except it runs VMXOFF instead.VOIDTerminateVmx(){ DbgPrint("\\n[*] Terminating VMX...\\n"); int LogicalProcessorsCount = KeQueryActiveProcessorCount(0); for (size_t i = 0; i &lt; LogicalProcessorsCount; i++) { DbgPrint("\\t\\t + Terminating VMX on processor %d\\n", i); RunOnProcessorForTerminateVMX(i); // // Free the destination memory // MmFreeContiguousMemory(PhysicalToVirtualAddress(g_GuestState[i].VmxonRegion)); MmFreeContiguousMemory(PhysicalToVirtualAddress(g_GuestState[i].VmcsRegion)); ExFreePoolWithTag(g_GuestState[i].VmmStack, POOLTAG); ExFreePoolWithTag(g_GuestState[i].MsrBitmap, POOLTAG); } DbgPrint("[*] VMX Operation turned off successfully. \\n");}As you can see, it executes RunOnProcessorForTerminateVMX on all the running logical cores. It then frees the allocated buffers for VmxonRegion, VmcsRegion, VmmStack, and MsrBitmap using MmFreeContiguousMemory and, of course, converts physicals to virtuals whenever needed.Note that you have to modify this function if you virtualized a portion of cores (not all).In RunOnProcessorForTerminateVMX, we must tell theVMX Root Operation about turning off the hypervisor. As I told you, it’s because we can’t execute any VMX instructions in the regular driver routines, and it’s pretty clear that VMX Root Operation can prevent us from this operation if there isn’t any mechanism for handling this situation or we’re not privileged enough to unload the hypervisor.There are several ways to tell our VMX Root Operation about VMXOFF, but in our case, we’ll use CPUID.By now, you definitely know that executing CPUID will cause VM-exit. Now in our CPUID exit handler routine, we manage that whenever a CPUID with RAX = 0x41414141 and RCX = 0x42424242 is executed, then we have to return true, and it shows the caller that the hypervisor needs to be off. if ((state-&gt;rax == 0x41414141) &amp;&amp; (state-&gt;rcx == 0x42424242) &amp;&amp; Mode == DPL_SYSTEM) { return TRUE; // Indicates we have to turn off VMX }There is also another check for DPL to make sure that CPUID with RAX = 0x41414141 and RCX = 0x42424242 is only executed in the system privilege level (kernel-mode). Hence, none of the user-mode applications are able to unload our hypervisor. ULONG Mode = 0; __vmx_vmread(GUEST_CS_SELECTOR, &amp;Mode); Mode = Mode &amp; RPL_MASK;Now our RunOnProcessorForTerminateVMX executes CPUID with adjusted values into registers on all cores separately.BOOLEANRunOnProcessorForTerminateVMX(ULONG ProcessorNumber){ KIRQL OldIrql; INT32 CpuInfo[4]; KeSetSystemAffinityThread((KAFFINITY)(1 &lt;&lt; ProcessorNumber)); OldIrql = KeRaiseIrqlToDpcLevel(); // // Our routine is VMXOFF // __cpuidex(CpuInfo, 0x41414141, 0x42424242); KeLowerIrql(OldIrql); KeRevertToUserAffinityThread(); return TRUE;}In the EXIT_REASON_CPUID handler, we know that if the handler returns true, then we have to turn it off, so we should think about some other things. For example, Windows expects to later continue from GUEST_RIP and needs its previous GUEST_RSP whenever the VM-exit handler returns; thus, we have to save them in some locations and use them later to restore the Windows state.Also, we have to increase GUEST_RIP because we want to restore the state after the CPUID. case EXIT_REASON_CPUID: { Status = HandleCPUID(GuestRegs); // Detect whether we have to turn off VMX or Not if (Status) { // We have to save GUEST_RIP &amp; GUEST_RSP somewhere to restore them directly ULONG ExitInstructionLength = 0; g_GuestRIP = 0; g_GuestRSP = 0; __vmx_vmread(GUEST_RIP, &amp;g_GuestRIP); __vmx_vmread(GUEST_RSP, &amp;g_GuestRSP); __vmx_vmread(VM_EXIT_INSTRUCTION_LEN, &amp;ExitInstructionLength); g_GuestRIP += ExitInstructionLength; } break; }From the 5th part, you probably know MainVmexitHandler is called from VmexitHandler (Assembly function from VMExitHandler.asm)Let’s see it in detail.First, we have to extern some previously defined variables.EXTERN g_GuestRIP:QWORDEXTERN g_GuestRSP:QWORDNow our VmexitHandler works like this, whenever a VM-exit occurs, the target logical core executes VmexitHandler as it’s defined in HOST_RIP, and our RSP is set to HOST_RSP, then we have to save all the registers. It means we must create a structure that allows us to read and modify registers in a C-like structure.typedef struct _GUEST_REGS{ ULONG64 rax; // 0x00 // NOT VALID FOR SVM ULONG64 rcx; ULONG64 rdx; // 0x10 ULONG64 rbx; ULONG64 rsp; // 0x20 // rsp is not stored here on SVM ULONG64 rbp; ULONG64 rsi; // 0x30 ULONG64 rdi; ULONG64 r8; // 0x40 ULONG64 r9; ULONG64 r10; // 0x50 ULONG64 r11; ULONG64 r12; // 0x60 ULONG64 r13; ULONG64 r14; // 0x70 ULONG64 r15;} GUEST_REGS, *PGUEST_REGS;Just push all the registers in the GUEST_REGS structure order and push the RSP as the first argument to MainVmexitHandler (Fastcall RCX), then some subtraction for Shadow Space.You can see the VmexitHandler here:VmexitHandler PROC PUSH R15 PUSH R14 PUSH R13 PUSH R12 PUSH R11 PUSH R10 PUSH R9 PUSH R8 PUSH RDI PUSH RSI PUSH RBP PUSH RBP ; RSP PUSH RBX PUSH RDX PUSH RCX PUSH RAX MOV RCX, RSP ; Fast CALL argument to PGUEST_REGS SUB RSP, 28h ; Free some space for Shadow Section CALL MainVmexitHandler ADD RSP, 28h ; Restore the state ; Check whether we have to turn off VMX or Not (the result is in RAX) CMP AL, 1 JE VmxoffHandler ; Restore the state POP RAX POP RCX POP RDX POP RBX POP RBP ; RSP POP RBP POP RSI POP RDI POP R8 POP R9 POP R10 POP R11 POP R12 POP R13 POP R14 POP R15 SUB RSP, 0100h ; to avoid error in future functions JMP VmResumeInstruction VmexitHandler ENDPFrom the above code, when we return from the MainVmexitHandler, we have to check whether the return result of MainVmexitHandler (in RAX) tells us to turn off the hypervisor or just continue.If it needs to be continued, restore the registers state and jump to our VmResumeInstruction function.VmResumeInstruction executes __vmx_vmresume and the processor sets the RIP register to GUEST_RIP.VOIDVmResumeInstruction(){ ULONG64 ErrorCode = 0; __vmx_vmresume(); // // if VMRESUME succeeds will never be here! // __vmx_vmread(VM_INSTRUCTION_ERROR, &amp;ErrorCode); __vmx_off(); DbgPrint("[*] VMRESUME Error : 0x%llx\\n", ErrorCode); // // It's such a bad error because we don't where to go // prefer to break // DbgBreakPoint();}But what if it needs to be turned off?Then based on the AL register, it jumps to another function called VmxoffHandler. This function executes the VMXOFF instruction, turns off the hypervisor (in the current logical core), and then restores the registers to their previous state as we saved them in VmexitHandler.The only thing we have to do here is changing the stack pointer to GUEST_RSP (We saved them in g_GuestRSP) and jump to the GUEST_RIP (saved in g_GuestRIP).VmxoffHandler PROC ; Turn VMXOFF VMXOFF ; Restore the state POP RAX POP RCX POP RDX POP RBX POP RBP ; RSP POP RBP POP RSI POP RDI POP R8 POP R9 POP R10 POP R11 POP R12 POP R13 POP R14 POP R15 ; Set guest RIP and RSP MOV RSP, g_GuestRSP JMP g_GuestRIPVmxoffHandler ENDPNow everything is done, we executed our normal Windows (driver) routine; I mean, start the execution after the last CPUID that was executed from RunOnProcessorForTerminateVMX but now we’re not in VMX operation.VM-Exit HandlerPutting all the above codes together, now we have to manage different kinds of VM-exits, so we need to modify our previously explained (in the 5th part) VM-exit handler; if you forget about it, please review the 5th part (VM-Exit Handler), it’s exactly the same but with different actions for various exit reasons.The first thing we need to manage is to detect every VMX instructions that are executed in VMX non-root operation; it can be done using the following code : // // 25.1.2 Instructions That Cause VM Exits Unconditionally // The following instructions cause VM exits when they are executed in VMX non-root operation: CPUID, GETSEC, // INVD, and XSETBV. This is also true of instructions introduced with VMX, which include: INVEPT, INVVPID, // VMCALL, VMCLEAR, VMLAUNCH, VMPTRLD, VMPTRST, VMRESUME, VMXOFF, and VMXON. // case EXIT_REASON_VMCLEAR: case EXIT_REASON_VMPTRLD: case EXIT_REASON_VMPTRST: case EXIT_REASON_VMREAD: case EXIT_REASON_VMRESUME: case EXIT_REASON_VMWRITE: case EXIT_REASON_VMXOFF: case EXIT_REASON_VMXON: case EXIT_REASON_VMLAUNCH: { // DbgBreakPoint(); /* DbgPrint("\\n [*] Target guest tries to execute VM Instruction ," "it probably causes a fatal error or system halt as the system might" " think it has VMX feature enabled while it's not available due to our use of hypervisor.\\n"); */ ULONG RFLAGS = 0; __vmx_vmread(GUEST_RFLAGS, &amp;RFLAGS); __vmx_vmwrite(GUEST_RFLAGS, RFLAGS | 0x1); // cf=1 indicate vm instructions fail break; }As I told you in DbgPrint, executing these kinds of VMX instructions will eventually cause BSOD because there might be some checks for the presence of a hypervisor before our hypervisor comes. Hence, the routine that executes these instructions (of course, it’s from the kernel) probably thinks it can execute these instructions. If it didn’t manage them well (which is common), you’ll see BSOD. Thus, you have to discover the cause of invoking these kinds of instructions and manually disable them.If you configured any CPU-based controls or your processor support 1-settings of any of the CR Access Exit controls, you could manage them using the following VM-exit. case EXIT_REASON_CR_ACCESS: { HandleControlRegisterAccess(GuestRegs); break; }The same thing is true for MSRs accesses. If we didn’t set any MSR Bit, every RDMSR and WRMSR cause to exit, or if we set any bits in MsrBitmap, then we have to manage them using the following function for RDMSR: case EXIT_REASON_MSR_READ: { ULONG ECX = GuestRegs-&gt;rcx &amp; 0xffffffff; // DbgPrint("[*] RDMSR (based on bitmap) : 0x%llx\\n", ECX); HandleMSRRead(GuestRegs); break; }And this code for managing WRMSR: case EXIT_REASON_MSR_WRITE: { ULONG ECX = GuestRegs-&gt;rcx &amp; 0xffffffff; DbgPrint("[*] WRMSR (based on bitmap) : 0x%llx\\n", ECX); HandleMSRWrite(GuestRegs); break; }And if you want to detect I/O instruction execution, then: case EXIT_REASON_IO_INSTRUCTION: { UINT64 RIP = 0; __vmx_vmread(GUEST_RIP, &amp;RIP); DbgPrint("[*] RIP executed IO instruction : 0x%llx\\n", RIP); DbgBreakPoint(); break; }Don’t forget to set adequate CPU-based control fields if you want to use the above functionalities.The last thing that is important for us is the CPUID Handler. It calls HandleCPUID (described above), and if the result is true, then it saves the GUEST_RSP and GUEST_RIP so that these values can be used to restore the state after VMXOFF is executed in the target core. case EXIT_REASON_CPUID: { Status = HandleCPUID(GuestRegs); // Detect whether we have to turn off VMX or Not if (Status) { // We have to save GUEST_RIP &amp; GUEST_RSP somewhere to restore them directly ULONG ExitInstructionLength = 0; g_GuestRIP = 0; g_GuestRSP = 0; __vmx_vmread(GUEST_RIP, &amp;g_GuestRIP); __vmx_vmread(GUEST_RSP, &amp;g_GuestRSP); __vmx_vmread(VM_EXIT_INSTRUCTION_LEN, &amp;ExitInstructionLength); g_GuestRIP += ExitInstructionLength; } break; }Let’s Test it!Now it’s time to test our hypervisor.Virtualizing all the coresFirst, we have to load our driver.Then our DriverEntry is called, so we have to run our user-mode application to virtualize all the cores.You can see that if you press any key or close this window, it’ll call DrvClose and restores the state (VMXOFF).The above picture shows the driver logs. At this point, all the cores are now under the hypervisor.Changing CPUID using HypervisorNow let’s test the presence of the hypervisor. For this case, I used Immunity Debugger to execute CPUID with custom EAX. You can use any other debugger or any custom application.We have to manually set the EAX to 0x40000001 or the HYPERV_CPUID_INTERFACE and then execute CPUID.As you can see, HVFS (0x48564653) is on EAX, so we successfully hooked the CPUID execution using our hypervisor.The above picture shows the HYPERV_CPUID_INTERFACE without the hypervisor.At last, we have to close the user-mode app window, so it executes VMXOFF on all cores. Let’s test the above example again.You can see that the actual results have appeared as we’re no longer under the hypervisor.Detecting MSR Read &amp; Write (MSR Bitmap)In order to test MSR Bitmaps, I create a local kernel debugger (using WinDbg). In WinDbg, we can execute rdmsr and wrmsr commands to read and write into MSRs. It’s exactly like executing RDMSR and WRMSR using a system driver.In the VirtualizeCurrentSystem function, the following line is added. SetMSRBitmap(0xc0000082, ProcessorID, TRUE, TRUE);In WinDbg Local Debugger, we executed the above commands, and in the remote debugger, we can see the result as follows,As you can see, the execution of RDMSR is detected. Our hypervisor is working perfectly!That’s it all, folks.ConclusionIn this part, we saw how we could virtualize an already running system by configuring the VMCS fields separately for each logical core. Then we used our hypervisor to change the result of the CPUID instruction and monitor every access to control registers or MSRs. After this part, our hypervisor is almost ready to be used for a practical project. The future part is about using the Extended Page Table (as described previously in the 4th part). I believe most of the exciting works in hypervisor can be performed using EPT because it has a special logging mechanism, e.g., page read/write access detection and many other cool things you’ll see in the next parts.See you in the next part.The seventh part is also available here.References[1] Vol 3C – Chapter 24 – (VIRTUAL MACHINE CONTROL STRUCTURES (https://software.intel.com/en-us/articles/intel-sdm)[2] cpu-internals (https://github.com/LordNoteworthy/cpu-internals)[3] RDTSCP — Read Time-Stamp Counter and Processor ID (https://www.felixcloutier.com/x86/rdtscp)[4] INVPCID — Invalidate Process-Context Identifier (https://www.felixcloutier.com/x86/invpcid)[5] XSAVE — Save Processor Extended States (https://www.felixcloutier.com/x86/xsave)[6] XRSTORS — Restore Processor Extended States Supervisor (https://www.felixcloutier.com/x86/xrstors)[7] What is IRQL ? (https://blogs.msdn.microsoft.com/doronh/2010/02/02/what-is-irql/)" }, { "title": "PacketScript overview: A Lua scripting engine for in-kernel packet processing", "url": "/topics/packetscript-lua-kernel/", "categories": "kernel-mode, linux, network, tutorials", "tags": "iptables, kernel, linux, lua, lunatik, packetscript, xtables, xtables-addons", "date": "2019-02-09 00:00:00 +0000", "snippet": "As I was surfing the net, trying to find a way to prototype network protocols or features in Linux. I stumbled upon PacketScript.PacketScript is the an implementation of the Lua VM inside Linux kernel. Such implementations aren’t new ,luak and lunatik have been existed for some time. However what makes PacketScript different is the ability to mangle network packets with Lua. Not just running Lua code in kernel. as a matter of fact PacketScript uses lunatik underneath as its Lua in kernel engine.PacketScript is built on existing technologies, ensuring more future maintainability. It is built on iptables infrastructure and on existing xtable-addons platform. Using xtables-addons makes PacketScript needless of kernel patching and compiling. You simply need to install the kernel module. xtables-addons also provides help for adding features into iptables command line interface.PacketScript was work of André Graf as his master thesis in University of Basel. Since its original publication of thesis and source code, It has gone unmaintained since. It is currently working and I’m not aware of any bugs but the lack of maintenance may make this project unsuitable for production usage (unless forked and maintained by yourself).Note that PacketScript compiles on linux kernel 2.x. (unless you apply the patch)Being very disappointed from the kernel version supported by PacketScript, I found a patch by OpenWrt team who has ported PacketScript to 4.x kernels! The patch is for OpenWrt but you can easily apply it to the source and build on any other distro. (I am using PacketScript on OpenWrt myself).If you want to use PacketScript on OpenWrt you just need to select it in menuconfig (Network-&gt;IPTables-&gt;ipt-mod-lua).We will use Debian stable (jessie) in this guide to learn the patching process. The same process can be used for other distros too. Let’s Start :1. Installing dependencies# apt install libxtables-dev linux-headers-$(uname -r) build-essential pkg-config2. Download xtables-addons https://sourceforge.net/projects/xtables-addons and extract files.3. Download patches$ cd xtables-addons-3.2/$ wget "https://github.com/openwrt-mirror/openwrt/raw/master/package/network/utils/xtables-addons/patches/201-fix-lua-packetscript.patch"$ wget "https://raw.githubusercontent.com/openwrt-mirror/openwrt/master/package/network/utils/xtables-addons/patches/200-add-lua-packetscript.patch"4. Apply patches$ patch -p1 &lt; 200-add-lua-packetscript.patch$ patch -p1 &lt; 201-fix-lua-packetscript.patch5. change config files (only necessary if patch fails at config files)At this stage due to difference between xtables-addons version the first patch may fail at config file, You need to change them manually and remove all patch rejects and backups.5.1. Change configsadd 'build_LUA=m' to the 'xtables-addons-1.xy/mconfig' fileadd 'obj-${build_LUA} += LUA/' to the 'xtables-addons-1.xy/extensions/Mbuild' fileadd 'obj-${build_LUA} += LUA/' to the 'xtables-addons-1.xy/extensions/Kbuild' file5.2. Remove rejects and backups$ find -name "*.rej" -delete$ find -name "*.orig" -delete6. Build &amp; Install$ make# make installNote that despite installing, the module may not be loaded, to do so:# insmod /lib/modules/$(uname -r)/extra/xt_LUA.koNote: Module may not be installed in a valid location in order to be detected by modprobe. use symlinks or change the Makefile accordingly. See this or this.Now everything should be ready:root@debian:~# iptables -I INPUT -j LUAiptables v1.6.0: LUA: --script parameter requiredTry `iptables -h' or 'iptables --help' for more information.Destination NAT (DNAT)Create a lua script like this (dnat.lua) :function process_packet(p) local ip = p:data(packet_eth):data(packet_ip) if not ip then return NF_DROP end ip:daddr():set("2.1.1.1") return XT_CONTINUEendThen push it into kernel like this:# iptables -t mangle -A POSTROUTING -d 8.8.8.8 -j LUA --script dnat.luaIt is recommended to utilize Netfilter matches and extensions and use PacketScript when they cannot do what you want for performance reasons.I am prototyping some new features in PacketScript. If my employer agreed upon open sourcing those I will create a GitHub repo and update this post with this links.No guide is available on the internet for PacketScript. I hope this post would be useful. I am glad to answer questions in comments. Let me know of your network prototyping tools." }, { "title": "Call Gates' Ring Transitioning in IA-32 Mode", "url": "/topics/call-gates-ring-transitioning-in-ia-32-mode/", "categories": "cpu, windows", "tags": "call-gate, call-gate-descriptor, changing-to-ring-1-or-2, ring-transitioning", "date": "2019-01-15 00:00:00 +0000", "snippet": "Have you ever thought how transitions between different rings performed?Well, SYSENTER &amp; SYSCALL used in modern OSs for transitioning between ring 3 to ring 0 but if there are other rings, then what’s Intel solution for entering them? The answer is call gates.The rest of this topic described how to use call gates in modern processors.80386 and its successors have 4 levels protections which isolate and protect user programs from each other and the operating system.It offers an additional type of protection on a page basis, when paging is enabled(using U/S and R/W fields) .There are 3 different types of privilege level checks when using call-gates: Current Privilege Level (CPL) Descriptor Privilege Level (DPL) Requestor Privilege Level (RPL) Current Privilege Level (CPL)CPL is stored in the selector of currently executing CS register. It represents the privilege level (PL) of the currently executing task. It is also PL in the descriptor of the code segment and designated as Task Privilege Level (TPL).Descriptor Privilege Level (DPL)It is the PL of the object which is being attempted to be accessed by the current task or in the other words, the least privilege level for the caller to use this gate.Requestor Privilege Level (RPL)It is the lowest two bits of any selector. It can be used to weaken the CPL if desired.The Effective Privilege Level (EPL) is EPL = max (CPL, RPL) (here numbers) Thus the task becomes less privileged.Note that PL refers to Privilege Level.Keep in mind that the above concepts also applies in other Intel structuresGDT EntriesGDT Stands for Global Descriptor Table. It contains some Segmentation Descriptors used in the age of memory segmentation and now it can be used to implement some segment change techniques like heavens gate.In our case we use GDT as a table for our call gate descriptor. I didn’t see any use of call gates in Windows or Linux, by the way it’s a feature that exsits in processors. (Even in AMD64 and IA32e).The GDT Entry Descriptor defined like this :typedef struct _GDT_ENTRY { UINT16 LIMIT15_0; UINT16 BASE15_0; UINT8 BASE23_16; UINT8 TYPE : 1; UINT8 SUBTYPE : 1; UINT8 Accessibility : 1; UINT8 Access : 1; UINT8 S : 1; UINT8 DPL : 2; UINT8 PRESENT : 1; UINT8 LIMIT19_16 : 4; UINT8 AVL : 1; UINT8 L : 1; UINT8 D : 1; UINT8 GRANULARITY : 1; UINT8 BASE31_24;}GDT_ENTRY, *PGDT_ENTRY;For more information about GDT, you can read this article and also this osdev topic.When the S flag is set to 0, the meaning of a GDT entry is quite different so it’s no longer a segment descriptor and now we can use this entry as a call-gate descriptor.More information about call gates implementation, later in this topic.Restricting Access to DataAssume that a task needs data from the data segment. The privilege levels are checked at the time a selector for the target segment is loaded into the data segment register. Three privilege levels enter into privilege checking mechanism. CPL RPL of the selector of the target segment DPL of the descriptor of the target segmentNote that access is allowed only if DPL ≥ Max(CPL, RPL) and A procedure can only access the data that is at the same or less privilege level.CALL &amp; JMPs (Near vs Far)The near forms of JMP and CALL transfer within the current code segment and requires only limit checking. The far forms of JMP and CALL refer to other segments and require privilege checking.The far JMP and CALL can be done in 2 ways: Without Call Gate DescriptorThe processor permits a JMP or CALL directly to another segment only if :1. DPL of the target segment = CPL of the calling segment2. Confirming bit of the target code is set and DPL of the target segment ≤ CPLConfirming Segment: These segments may be called from various privilege levels but execute at the privilege level of the calling procedure. (e.g. math library) With Call Gate DescriptorThe far pointer of the control transfer instruction uses the selector part of the pointer and selects a gate. The selector and offset fields of a gate form a pointer to the entry of a procedure.Ring TransitioningWhen CPU fetches a far-call instruction, it will use that instruction’s ‘selector’ value to look up a descriptor in the GDT (or in the current LDT).If it’s a ‘call-gate’ descriptor, and if access is allowed (i.e. if CPL ≤ DPL), then the CPU will perform a complex sequence of actions which will accomplish the requested ‘ring-transition’. CPL (Current Privilege Level) is based on least significant 2-bits in register CS (also in SS) pushes the current SS:SP register values onto a new stack-segment- copies the specified number of parameters from the old stack onto the new stack pushes the updated CS:IP register-values onto the new stack loads new values into registers CS:IP (from the call gate descriptor) and into SS:SPWhere do the new values for SS:SP come from? (They’re not found in the call-gate)They’re from a special system-segment, known as the TSS (Task State Segment). The CPU locates its TSS by referring to the value in register TR (Task Register).Return to an Outer Ring Use the far-return instruction: ‘lret’ Restores CS:IP from the current stack Restores SS:SP from the current stack Use the far-return instruction: ‘lret $n’ Restores CS:IP from the current stack Discards n parameter-bytes from that stack Restores SS:SP from that current stack Call-Gate DescriptorIntel defines a call-gate descriptor like this (in x86 environment):typedef struct CALL_GATE32 { int offset0_15 : 16; int selector : 16; union { struct { int argCount : 5; int zeros : 3; int type : 4; int sFlag : 1; int dpl : 2; int pFlag : 1; } part; unsigned __int16 all; } dummy; int offset16_31 : 16;}CALL_GATE32, *PCALL_GATE32; P = present (1 = yes, 0 = no) DPL = Descriptor Privilege Level (0, 1 ,2 ,3) Code-selector (specifies memory-segment containing procedure code) Offset (specifies the procedure’s entry-point within its code-segment) Parameter count (specifies how many parameter-values will be copied) Gate-Type (‘0x4’ means a 16-bit call-gate, ‘0xC’ means a 32-bit call-gate)The above descriptor is also available in AMD64 and it defines like the following picture.typedef struct CALL_GATE{ unsigned __int32 offset0_15 : 16; unsigned __int32 selector : 16; union { struct { unsigned __int16 ist : 3; unsigned __int16 ignored : 5; unsigned __int16 type : 5; unsigned __int16 dpl : 2; unsigned __int16 p : 1; } part; unsigned __int16 all; } dummy; unsigned __int64 offset16_63 : 48; unsigned __int32 reserved : 32;}CALL_GATE, *PCALL_GATE;Call gates facilitate controlled transfers of program control between different privilege levels. They are typically used only in operating systems or executives that use the privilege-level protection mechanism. Call gates are also useful for transferring program control between 16-bit and 32-bit code segmentsNow, let’s see different types of entries that can be used in GDT Entries (the “type” field of above descriptors). Bits 3 2 1 0 : Type of the entry 0000 - Reserved 0001 - Available 16-bit TSS 0010 - Local Descriptor Table (LDT) 0011 - Busy 16-bit TSS 0100 - 16-bit Call Gate 0101 - Task Gate 0110 - 16-bit Interrupt Gate 0111 - 16-bit Trap Gate 1000 - Reserved 1001 - Available 32-bit TSS 1010 - Reserved 1011 - Busy 32-bit TSS 1100 - 32-bit Call Gate 1101 - Reserved 1110 - 32-bit Interrupt Gate 1111 - 32-bit Trap Gate ImplementationEnough for theory, let’s see how we can use them in a real environment.The full source code is available on Github:[https://github.com/SinaKarvandi/IA32-CALL-GATES]After defining the above structures, now we need to declare a structure with the following assumptions.Selector field should be 0x8 (pointing to KGDT_R0_CODE entry of GDT). As I told you about the type it should be 0xc and the minimum ring that can invoke this call-gate is (dpl = 0x3 (user-mode)) and of course, it’s present in memory.The address should be divided into two 16 bit parts.The following function is responsible for making a Windbg command that can be executed in order to defined a call-gate. Of course you can do the same thing using a kernel mode driver.Note that address is the kernel handler function, gdtr is the result of “r gdtr” and GDTIndex is the GDT index that we want to modify.void CreateCallGateStruct(int address , int gdtr , int GDTIndex) { CALL_GATE32 callgate32 = { 0 }; callgate32.dummy.part.zeros = 0x0; callgate32.selector = 0x8; callgate32.dummy.part.type = 0xc; callgate32.dummy.part.sFlag = 0x0; callgate32.dummy.part.dpl = 0x3; callgate32.dummy.part.pFlag = 0x1; callgate32.offset0_15 = 0x0000ffff &amp; address; address = address &gt;&gt; 16; callgate32.offset16_31 = 0x0000ffff &amp; address; printf("eq %x+(0x8*0x%x) ", gdtr , GDTIndex); printf("%04x", callgate32.offset16_31); printf("%04x", callgate32.dummy.all); printf("%04x", callgate32.selector); printf("%04x", callgate32.offset0_15);}Let’s execute the above code and see the results. // Making windbg structure int DispatcherAddress= 0x8176fcd4; int gdtr = 0x80b5b000; int GdtIndex = 0x64; CreateCallGateStruct(DispatcherAddress, gdtr, GdtIndex); unsigned short callGate[3]; callGate[0] = 0x0; callGate[1] = 0x0; callGate[2] = 0x320; // Invoke the far call __asm { int 3 } __asm { call fword ptr[callGate]; }The final result is :eq 80b5b000+(0x8*0x64) ffff8176ec000008fffffcd4If you look more precisely at the above code, you can see that, the last part is the definition of callGate array. This array chooses GDT index which your call-gate descriptor is located.I choose 320 because the segment selector defines like this :15 3 2 0+--------------------------------------------------+----+--------+| Index | TI | RPL |+--------------------------------------------------+----+--------+TI = Table Indicator: 0 = GDT, 1 = LDTWe select 320 (10100’0’000), RPL (000) TI (0 because our descriptor is on GDT, not LDT), the index is 100 (0x64 or 10100 in binary).The last part is executing our FAR CALL. After the execution of this call we will be in our dispatcher address with Kernel Privilege Level. By now you should be aware of how to go to other rings (e.g ring 1,2) by modifying the above structures and the selectors.The last thing is you should handle this situation and finally return to the previous rings using lret.That’s it.ConclusionIn this topic, we see how transitions between different rings are performed in our processor, even if almost all of the modern OSs don’t use this mechanism. For more i.formation you can review the following references.References[1] Intel 64 and IA-32 Architectures Software Developer’s Manual Vol. 3A – Chapter 5 – (5.8.3 Call Gates) (https://software.intel.com/en-us/articles/intel-sdm)[2] Intel 64 and IA-32 Architectures Software Developer’s Manual Vol. 3A – Chapter 5 – (5.8.4 Accessing a Code Segment Through a Call Gate) (https://software.intel.com/en-us/articles/intel-sdm)[4] 80386DX - (https://slideplayer.com/slide/9375765)[5] Processor Privilege Levels - (https://slideplayer.com/slide/1652112)[6] The Real, Protected, Long mode assembly tutorial for PCs - (https://www.codeproject.com/Articles/45788/The-Real-Protected-Long-mode-assembly-tutorial-for)[7] Hooking the GDT - Installing Call gates - (https://github.com/David-Reguera-Garcia-Dreg/cgaty)[8] The rootkit arsenal book – (https://www.amazon.com/Rootkit-Arsenal-Escape-Evasion-Corners/dp/1598220616)[9] Bringing Call Gates Back – (http://www.alex-ionescu.com/?p=340)[10] Descriptor tables in kernel exploitation – (https://j00ru.vexillium.org/2010/01/descriptor-tables-in-kernel-exploitation-a-new-article/)" }, { "title": "Hypervisor From Scratch – Part 5: Setting up VMCS & Running Guest Code", "url": "/topics/hypervisor-from-scratch-part-5/", "categories": "cpu, hypervisor, tutorials", "tags": "hypervisor, building-vmcs, configuring-vmcs, start-virtual-machine, virtual-machine-control-structure, vmcs, vmcs-configuration, vmlaunch, vmlaunch-0x7, vmlaunch-0x8, vmlaunch-error", "date": "2018-12-16 00:00:00 +0000", "snippet": "If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous practical examples on how to utilize hypervisors for reverse engineering.IntroductionHello and welcome to the fifth part of the “Hypervisor From Scratch” tutorial series. Today we will spend our time studying different parts of Virtual Machine Control Structure (VMCS), implementing additional VMX instructions, creating a restore point, setting different VMCS control structures, and at last, we execute VMLAUNCH and enter the hardware virtualization world!Table of contents Introduction Table of contents Overview VMX Instructions VMPTRST VMCLEAR VMPTRLD Enhancing VM State Structure Preparing to launch VM Saving a return point Returning to the previous state VMLAUNCH Instruction VMX Controls VM-Execution Controls VM-entry Control Bits VM-exit Control Bits PIN-Based Execution Control Configuring VMCS Gathering machine state for VMCS Setting up VMCS Checking VMCS layout VM-Exit Handler Resume to next instruction VMRESUME Instruction Let’s Test it! Conclusion ReferencesOverviewMost of this topic is derived from Chapter 24 – (VIRTUAL MACHINE CONTROL STRUCTURES) &amp; Chapter 26 – (VM ENTRIES) available at Intel 64 and IA-32 architectures software developer’s manual (Intel SDM) combined volumes 3.This part is highly inspired by Hypervisor For Beginner.Before reading the rest of this part, make sure to read the previous parts as it gives you the necessary knowledge to understand the rest of this topic thoroughly.The full source code of this tutorial is available on GitHub :[https://github.com/SinaKarvandi/Hypervisor-From-Scratch]Note: Remember that hypervisors change over time because new features are added to the operating systems or new technologies are used. For example, updates to Meltdown &amp; Spectre have made a lot of changes to the hypervisors. So, if you want to use Hypervisor From Scratch in your projects, research, or whatever, you should use the HyperDbg drivers. HyperDbg is actively maintained, stable, and reliable, ensuring you avoid the errors and instability problems that can arise from using older parts of the tutorial series.VMX InstructionsIn part 3, we implemented VMXOFF function now let’s implement other VMX instructions function. I also make some changes in calling VMXON and VMPTRLD functions to make it more modular.VMPTRSTVMPTRST instruction stores the current-VMCS pointer into a specified memory address. The operand of this instruction is always 64 bits, and it’s always a location in memory.The following function is the implementation of VMPTRST:UINT64VmptrstInstruction(){ PHYSICAL_ADDRESS vmcspa; vmcspa.QuadPart = 0; __vmx_vmptrst((unsigned __int64 *)&amp;vmcspa); DbgPrint("[*] VMPTRST %llx\\n", vmcspa); return 0;}VMCLEARThis instruction applies to the VMCS, where the VMCS region resides at the physical address contained in the instruction operand. The instruction ensures that VMCS data for that VMCS (some of these data may be currently maintained on the processor) are copied to the VMCS region in memory. It also initializes some parts of the VMCS region (for example, it sets the launch state of that VMCS to clear).BOOLEANClearVmcsState(VIRTUAL_MACHINE_STATE * GuestState){ // Clear the state of the VMCS to inactive int status = __vmx_vmclear(&amp;GuestState-&gt;VmcsRegion); DbgPrint("[*] VMCS VMCLAEAR Status is : %d\\n", status); if (status) { // Otherwise, terminate the VMX DbgPrint("[*] VMCS failed to clear with status %d\\n", status); __vmx_off(); return FALSE; } return TRUE;}VMPTRLDThis instruction marks the current-VMCS pointer valid and loads it with the physical address in the instruction operand. The instruction fails if its operand is not properly aligned, sets unsupported physical-address bits, or is equal to the VMXON pointer. In addition, this instruction fails if the 32 bits in memory referenced by the operand do not match the VMCS revision identifier supported by the processor.BOOLEANLoadVmcs(VIRTUAL_MACHINE_STATE * GuestState){ int status = __vmx_vmptrld(&amp;GuestState-&gt;VmcsRegion); if (status) { DbgPrint("[*] VMCS failed with status %d\\n", status); return FALSE; } return TRUE;}In order to implement VMRESUME, you need to know about some VMCS fields, so the explanation of the VMRESUME instruction is left after we implement VMLAUNCH. (Later in this topic)Enhancing VM State StructureAs I told you earlier, we need a structure to save the state of our virtual machine in each core separately. The following structure is used in the newest version of our hypervisor. We will describe each field in the rest of this topic.typedef struct _VIRTUAL_MACHINE_STATE{ UINT64 VmxoRegion; // VMXON region UINT64 VmcsRegion; // VMCS region UINT64 Eptp; // Extended-Page-Table Pointer UINT64 VmmStack; // Stack for VMM in VM-Exit State UINT64 MsrBitmap; // MSR Bitmap Virtual Address UINT64 MsrBitmapPhysical; // MSR Bitmap Physical Address} VIRTUAL_MACHINE_STATE, *PVIRTUAL_MACHINE_STATE;Note that it’s not the final VIRTUAL_MACHINE_STATE structure; we’ll enhance it in the future.Preparing to launch VMIn this part, we’re just trying to enhance our hypervisor driver. In the future parts, we will add some user-mode interactions with our driver but for now, let’s start with modifying our DriverEntry as it’s the first function that executes when our driver is loaded.Besides all the preparation from part 2, we added the following lines to use our part 4 (EPT) structures: // // Initiating EPTP and VMX // PEPTP EPTP = InitializeEptp(); InitiateVmx();We also added an export to a global variable called “g_VirtualGuestMemoryAddress” that holds the address of where our guest code starts.Now let’s fill our allocated pages with \\xf4, which is the hex representation of the HLT instruction. I choose HLT because, with some special configuration (described below), it’ll cause VM-exit and return the code to the host handler; so, it would be an excellent example for this part.After that, we start creating a function called LaunchVm, which is responsible for running our virtual machine on a specific core. We will only test our hypervisor in the 0th logical processor in this part. In the future part, we’ll extend our hypervisor to virtualize the entire system.Keep in mind that every logical core has its own VMCS, and if we want our guest code to run in other logical processors, we should configure each of them separately.To run our codes in a certain logical core, we should set the affinity by using the Windows KeSetSystemAffinityThread function and choose the specific core’s VIRTUAL_MACHINE_STATE as each core has its own separate VMXON and VMCS regions.The following code describes how we can run our code in different logical cores.VOIDLaunchVm(int ProcessorID, PEPTP EPTP){ DbgPrint("\\n======================== Launching VM =============================\\n"); KAFFINITY AffinityMask; AffinityMask = MathPower(2, ProcessorID); KeSetSystemAffinityThread(AffinityMask); DbgPrint("[*]\\t\\tCurrent thread is executing in %d th logical processor.\\n", ProcessorID); PAGED_CODE();...Now that we can specify a core number and execute codes in the target core, it’s time should allocate a specific stack so that whenever a VM-exit occurs, we can save the registers and call other host functions in vmx-root mode.A quick reminder, whenever a vm-exit occurs, the host handler is called in vmx-root mode. When we run the VMRESUME instruction, the processor switches to the VMX non-root; thus, every kernel-mode driver and user-mode application are running in VMX non-root mode. Only the portion of our driver responsible for handling the host is executed in the VMX root-mode.Here we need a stack for host routines. We have two options, the first option is using the current RSP, and the second one is using a separated stack. We used a separate location for the stack instead of using the current RSP of the driver, but you can use the current stack (RSP) too.The following lines are written for allocating and zeroing the stack of our VM-exit handler. // // Allocate stack for the VM Exit Handler // UINT64 VMM_STACK_VA = ExAllocatePoolWithTag(NonPagedPool, VMM_STACK_SIZE, POOLTAG); g_GuestState[ProcessorID].VmmStack = VMM_STACK_VA; if (g_GuestState[ProcessorID].VmmStack == NULL) { DbgPrint("[*] Error in allocating VMM Stack.\\n"); return; } RtlZeroMemory(g_GuestState[ProcessorID].VmmStack, VMM_STACK_SIZE);Same as above, we’ll allocate a page for the MSR Bitmap and add it to GuestState. I’ll describe them later in this topic. // // Allocate memory for MSRBitMap // g_GuestState[ProcessorID].MsrBitmap = MmAllocateNonCachedMemory(PAGE_SIZE); // should be aligned if (g_GuestState[ProcessorID].MsrBitmap == NULL) { DbgPrint("[*] Error in allocating MSRBitMap.\\n"); return; } RtlZeroMemory(g_GuestState[ProcessorID].MsrBitmap, PAGE_SIZE); g_GuestState[ProcessorID].MsrBitmapPhysical = VirtualToPhysicalAddress(g_GuestState[ProcessorID].MsrBitmap);The next step is clearing the VMCS state and loading it as the current VMCS in the specific processor (in our case, the 0th logical processor).The ClearVmcsState and LoadVmcs functions are used as described above: // // Clear the VMCS State // if (!ClearVmcsState(&amp;g_GuestState[ProcessorID])) { goto ErrorReturn; } // // Load VMCS (Set the Current VMCS) // if (!LoadVmcs(&amp;g_GuestState[ProcessorID])) { goto ErrorReturn; }Now it’s time to set up VMCS. We will thoroughly discuss how to configure the VMCS later in this topic, but for now, assume that there is a function called SetupVmcs, which configures the VMCS structure. DbgPrint("[*] Setting up VMCS.\\n"); SetupVmcs(&amp;g_GuestState[ProcessorID], EPTP);The last step is to execute the VMLAUNCH instruction. Yet we shouldn’t forget to save the current state of the stack (RSP &amp; RBP registers). It’s because after executing the VMLAUNCH instruction, the RIP register is changed to the GUEST_RIP; thus, we need to save the previous system state so we can return to the normal system routines after returning from VM functions. If we leave the driver with the wrong RSP &amp; RBP registers, we’ll see a BSOD. For this purpose, the AsmSaveStateForVmxoff function is used.Saving a return pointFor AsmSaveStateForVmxoff, we declare two global variables called g_StackPointerForReturning, and g_BasePointerForReturning. There is no need to save the RIP register as the stack’s return address is always available. Just EXTERN it in the assembly file :EXTERN g_StackPointerForReturning:QWORDEXTERN g_BasePointerForReturning:QWORDThe implementation of AsmSaveStateForVmxoff :AsmSaveStateForVmxoff PROC PUBLIC MOV g_StackPointerForReturning, RSP MOV g_BasePointerForReturning, RBP RETAsmSaveStateForVmxoff ENDP Returning to the previous stateThat last step in our hypervisor is returning to the previous system state and turning off the hypervisor.We previously saved the system state. Now, we can restore it (RSPand RBP registers) and clear the stack position.Before that, the VMXOFF instruction is executed to turn off the hypervisor.Take a look at the following code.AsmVmxoffAndRestoreState PROC PUBLIC VMXOFF ; turn it off before existing MOV RSP, g_StackPointerForReturning MOV RBP, g_BasePointerForReturning ; make rsp point to a correct return point ADD RSP, 8 ; return True XOR RAX, RAX MOV RAX, 1 ; return section MOV RBX, [RSP+28h+8h] MOV RSI, [RSP+28h+10h] ADD RSP, 020h POP RDI RET AsmVmxoffAndRestoreState ENDP At last, we need to precisely clear the stack. Previously we called the LaunchVm function and ended up in a new RIP.To continue the execution normally, we need to clear the stack and return to the location where we called the LaunchVm function. Therefore, in the last part of the above assembly code, which is the “return section”, I used IDA Pro to see the disassembly of the LaunchVm, so we can see how this function clears the stack, and we perform the same so we can return the previous system state gracefully. Hence, the “return section” is copied from the disassemblies of the LaunchVm in IDA Pro.VMLAUNCH InstructionIt’s time to talk about the VMLAUNCH instruction.Take a look at the following code. __vmx_vmlaunch(); // // if VMLAUNCH succeeds will never be here! // ULONG64 ErrorCode = 0; __vmx_vmread(VM_INSTRUCTION_ERROR, &amp;ErrorCode); __vmx_off(); DbgPrint("[*] VMLAUNCH Error : 0x%llx\\n", ErrorCode); DbgBreakPoint();The __vmx_vmlaunch() is the intrinsic function for the VMLAUNCH instruction and __vmx_vmread is for the VMREAD instruction.As the comment describes, if VMLAUNCH succeeds, we’ll never execute the other lines. If there is an error in the state of VMCS (which is a common problem), we have to run VMREAD ** and read the error code from the **VM_INSTRUCTION_ERROR field of VMCS. It’s also necessary to run VMXOFF to turn off the hypervisor in the case of an error, and finally, we can print the error code.DbgBreakPoint is just a debug breakpoint (int 3), and it can be helpful only if we’re working on a remote kernel WinDbg Debugger. It’s clear that you can’t test it in your local debugging system because executing an int 3 in the kernel will freeze your system as long as there is no debugger to catch it, so it’s highly recommended to create a remote Kernel Debugging machine and test your codes for possible errors.You can also use VMware Workstation’s nested-virtualization to create a remote kernel debugging connection. Intel doesn’t have such a thing as “nested-virtualization” but provides some hardware facilities so vendors can support and implement nested virtualization on their own. For example, you can test your driver on VMware Workstation with nested-virtualization support (I also explained how to debug your hypervisor driver on VMware in the first part.) However, supporting Hyper-V nested virtualization needs extra things to be considered in implementing a hypervisor, so we can’t test our driver on Hyper-V nested virtualization, at least for this part. I’ll explain Hyper-V support in the 8th part.The drivers are tested on both physical machines and VMware Workstation’s nested-virtualization.Now it’s time to read some theories before digging into the configuration of the VMCS.VMX ControlsLet’s talk about different controls in VMCS that govern the guest’s behavior. We will use some of these bits in this part, and some will be used in future parts. So, don’t worry about it. Just take a look at the descriptions of these bits and be aware of them.VM-Execution ControlsIn order to control our guest features, we have to set some fields in our VMCS. The following tables represent the Primary Processor-Based VM-Execution Controls and the Secondary Processor-Based VM-Execution Controls.We define the above table like this:#define CPU_BASED_VIRTUAL_INTR_PENDING 0x00000004#define CPU_BASED_USE_TSC_OFFSETING 0x00000008#define CPU_BASED_HLT_EXITING 0x00000080#define CPU_BASED_INVLPG_EXITING 0x00000200#define CPU_BASED_MWAIT_EXITING 0x00000400#define CPU_BASED_RDPMC_EXITING 0x00000800#define CPU_BASED_RDTSC_EXITING 0x00001000#define CPU_BASED_CR3_LOAD_EXITING 0x00008000#define CPU_BASED_CR3_STORE_EXITING 0x00010000#define CPU_BASED_CR8_LOAD_EXITING 0x00080000#define CPU_BASED_CR8_STORE_EXITING 0x00100000#define CPU_BASED_TPR_SHADOW 0x00200000#define CPU_BASED_VIRTUAL_NMI_PENDING 0x00400000#define CPU_BASED_MOV_DR_EXITING 0x00800000#define CPU_BASED_UNCOND_IO_EXITING 0x01000000#define CPU_BASED_ACTIVATE_IO_BITMAP 0x02000000#define CPU_BASED_MONITOR_TRAP_FLAG 0x08000000#define CPU_BASED_ACTIVATE_MSR_BITMAP 0x10000000#define CPU_BASED_MONITOR_EXITING 0x20000000#define CPU_BASED_PAUSE_EXITING 0x40000000#define CPU_BASED_ACTIVATE_SECONDARY_CONTROLS 0x80000000In the earlier versions of VMX, there was nothing like Secondary Processor-Based VM-Execution Controls. Now, if we want to use the secondary table, we have to set the 31st bit of the first table; otherwise, it’s like the secondary table field with zeros.The definition of the above table is this (we ignore some bits, you can define them if you want to use them in your hypervisor):#define CPU_BASED_CTL2_ENABLE_EPT 0x2#define CPU_BASED_CTL2_RDTSCP 0x8#define CPU_BASED_CTL2_ENABLE_VPID 0x20#define CPU_BASED_CTL2_UNRESTRICTED_GUEST 0x80#define CPU_BASED_CTL2_ENABLE_VMFUNC 0x2000VM-entry Control BitsThe VM-entry controls constitute a 32-bit vector that governs the basic operation of VM entries.// VM-entry Control Bits #define VM_ENTRY_IA32E_MODE 0x00000200#define VM_ENTRY_SMM 0x00000400#define VM_ENTRY_DEACT_DUAL_MONITOR 0x00000800#define VM_ENTRY_LOAD_GUEST_PAT 0x00004000VM-exit Control BitsThe VM-exit controls constitute a 32-bit vector that governs the essential operation of VM-exits.// VM-exit Control Bits #define VM_EXIT_IA32E_MODE 0x00000200#define VM_EXIT_ACK_INTR_ON_EXIT 0x00008000#define VM_EXIT_SAVE_GUEST_PAT 0x00040000#define VM_EXIT_LOAD_HOST_PAT 0x00080000PIN-Based Execution ControlThe pin-based VM-execution controls constitute a 32-bit vector that governs the handling of asynchronous events (for example, interrupts). We’ll use it in the future parts, but for now, let’s define it in our hypervisor.#define PIN_BASED_VM_EXECUTION_CONTROLS_EXTERNAL_INTERRUPT 0x00000001#define PIN_BASED_VM_EXECUTION_CONTROLS_NMI_EXITING 0x00000008#define PIN_BASED_VM_EXECUTION_CONTROLS_VIRTUAL_NMI 0x00000020#define PIN_BASED_VM_EXECUTION_CONTROLS_ACTIVE_VMX_TIMER 0x00000040#define PIN_BASED_VM_EXECUTION_CONTROLS_PROCESS_POSTED_INTERRUPTS 0x00000080Configuring VMCSNow that we have a basic idea about some of the VMCS fields and controls, it’s time to configure the VMCS structure fully to make our virtualized guest ready.Gathering machine state for VMCSIn order to configure our Guest-State and Host-State, we need to have details about the current system state, e.g., Global Descriptor Table Address (GDT), Interrupt Descriptor Table (IDT) Address and read all the Segment Registers.These functions describe how all of these registers and segments can be gathered.GDT Base :GetGdtBase PROC LOCAL GDTR[10]:BYTE SGDT GDTR MOV RAX, QWORD PTR GDTR[2] RETGetGdtBase ENDPCS segment register:GetCs PROC MOV RAX, CS RETGetCs ENDPDS segment register:GetDs PROC MOV RAX, DS RETGetDs ENDPES segment register:GetEs PROC MOV RAX, ES RETGetEs ENDPSS segment register:GetSs PROC MOV RAX, SS RETGetSs ENDPFS segment register:GetFs PROC MOV RAX, FS RETGetFs ENDPGS segment register:GetGs PROC MOV RAX, GS RETGetGs ENDPLDT:GetLdtr PROC SLDT RAX RETGetLdtr ENDPTR (task register):GetTr PROC STR RAX RETGetTr ENDPInterrupt Descriptor Table:GetIdtBase PROC LOCAL IDTR[10]:BYTE SIDT IDTR MOV RAX, QWORD PTR IDTR[2] RETGetIdtBase ENDPGDT Limit:GetGdtLimit PROC LOCAL GDTR[10]:BYTE SGDT GDTR MOV AX, WORD PTR GDTR[0] RETGetGdtLimit ENDPIDT Limit:GetIdtLimit PROC LOCAL IDTR[10]:BYTE SIDT IDTR MOV AX, WORD PTR IDTR[0] RETGetIdtLimit ENDPRFLAGS:GetRflags PROC PUSHFQ POP RAX RETGetRflags ENDPSetting up VMCSLet’s get down to business (we have a long way to go).This section starts with defining a function called SetupVmcs.BOOLEANSetupVmcs(VIRTUAL_MACHINE_STATE * GuestState, PEPTP EPTP);This function is responsible for configuring all of the options related to VMCS and, of course, the Guest &amp; Host state.Configuring and modifying VMCS is done by using a special instruction called “VMWRITE”.VMWRITE writes the contents of a primary source operand (register or memory) to a specified field in a VMCS. In VMX-root operation, the instruction writes to the current VMCS. If executed in VMX non-root operation, the instruction writes to the VMCS referenced by the VMCS link pointer field in the current VMCS.The VMCS field is specified by the VMCS-field encoding contained in the register secondary source operand.The following enum contains most of the VMCS fields needed for VMWRITE &amp; VMREAD instructions. (newer processors add newer fields.)enum VMCS_FIELDS { GUEST_ES_SELECTOR = 0x00000800, GUEST_CS_SELECTOR = 0x00000802, GUEST_SS_SELECTOR = 0x00000804, GUEST_DS_SELECTOR = 0x00000806, GUEST_FS_SELECTOR = 0x00000808, GUEST_GS_SELECTOR = 0x0000080a, GUEST_LDTR_SELECTOR = 0x0000080c, GUEST_TR_SELECTOR = 0x0000080e, HOST_ES_SELECTOR = 0x00000c00, HOST_CS_SELECTOR = 0x00000c02, HOST_SS_SELECTOR = 0x00000c04, HOST_DS_SELECTOR = 0x00000c06, HOST_FS_SELECTOR = 0x00000c08, HOST_GS_SELECTOR = 0x00000c0a, HOST_TR_SELECTOR = 0x00000c0c, IO_BITMAP_A = 0x00002000, IO_BITMAP_A_HIGH = 0x00002001, IO_BITMAP_B = 0x00002002, IO_BITMAP_B_HIGH = 0x00002003, MSR_BITMAP = 0x00002004, MSR_BITMAP_HIGH = 0x00002005, VM_EXIT_MSR_STORE_ADDR = 0x00002006, VM_EXIT_MSR_STORE_ADDR_HIGH = 0x00002007, VM_EXIT_MSR_LOAD_ADDR = 0x00002008, VM_EXIT_MSR_LOAD_ADDR_HIGH = 0x00002009, VM_ENTRY_MSR_LOAD_ADDR = 0x0000200a, VM_ENTRY_MSR_LOAD_ADDR_HIGH = 0x0000200b, TSC_OFFSET = 0x00002010, TSC_OFFSET_HIGH = 0x00002011, VIRTUAL_APIC_PAGE_ADDR = 0x00002012, VIRTUAL_APIC_PAGE_ADDR_HIGH = 0x00002013, VMFUNC_CONTROLS = 0x00002018, VMFUNC_CONTROLS_HIGH = 0x00002019, EPT_POINTER = 0x0000201A, EPT_POINTER_HIGH = 0x0000201B, EPTP_LIST = 0x00002024, EPTP_LIST_HIGH = 0x00002025, GUEST_PHYSICAL_ADDRESS = 0x2400, GUEST_PHYSICAL_ADDRESS_HIGH = 0x2401, VMCS_LINK_POINTER = 0x00002800, VMCS_LINK_POINTER_HIGH = 0x00002801, GUEST_IA32_DEBUGCTL = 0x00002802, GUEST_IA32_DEBUGCTL_HIGH = 0x00002803, PIN_BASED_VM_EXEC_CONTROL = 0x00004000, CPU_BASED_VM_EXEC_CONTROL = 0x00004002, EXCEPTION_BITMAP = 0x00004004, PAGE_FAULT_ERROR_CODE_MASK = 0x00004006, PAGE_FAULT_ERROR_CODE_MATCH = 0x00004008, CR3_TARGET_COUNT = 0x0000400a, VM_EXIT_CONTROLS = 0x0000400c, VM_EXIT_MSR_STORE_COUNT = 0x0000400e, VM_EXIT_MSR_LOAD_COUNT = 0x00004010, VM_ENTRY_CONTROLS = 0x00004012, VM_ENTRY_MSR_LOAD_COUNT = 0x00004014, VM_ENTRY_INTR_INFO_FIELD = 0x00004016, VM_ENTRY_EXCEPTION_ERROR_CODE = 0x00004018, VM_ENTRY_INSTRUCTION_LEN = 0x0000401a, TPR_THRESHOLD = 0x0000401c, SECONDARY_VM_EXEC_CONTROL = 0x0000401e, VM_INSTRUCTION_ERROR = 0x00004400, VM_EXIT_REASON = 0x00004402, VM_EXIT_INTR_INFO = 0x00004404, VM_EXIT_INTR_ERROR_CODE = 0x00004406, IDT_VECTORING_INFO_FIELD = 0x00004408, IDT_VECTORING_ERROR_CODE = 0x0000440a, VM_EXIT_INSTRUCTION_LEN = 0x0000440c, VMX_INSTRUCTION_INFO = 0x0000440e, GUEST_ES_LIMIT = 0x00004800, GUEST_CS_LIMIT = 0x00004802, GUEST_SS_LIMIT = 0x00004804, GUEST_DS_LIMIT = 0x00004806, GUEST_FS_LIMIT = 0x00004808, GUEST_GS_LIMIT = 0x0000480a, GUEST_LDTR_LIMIT = 0x0000480c, GUEST_TR_LIMIT = 0x0000480e, GUEST_GDTR_LIMIT = 0x00004810, GUEST_IDTR_LIMIT = 0x00004812, GUEST_ES_AR_BYTES = 0x00004814, GUEST_CS_AR_BYTES = 0x00004816, GUEST_SS_AR_BYTES = 0x00004818, GUEST_DS_AR_BYTES = 0x0000481a, GUEST_FS_AR_BYTES = 0x0000481c, GUEST_GS_AR_BYTES = 0x0000481e, GUEST_LDTR_AR_BYTES = 0x00004820, GUEST_TR_AR_BYTES = 0x00004822, GUEST_INTERRUPTIBILITY_INFO = 0x00004824, GUEST_ACTIVITY_STATE = 0x00004826, GUEST_SM_BASE = 0x00004828, GUEST_SYSENTER_CS = 0x0000482A, HOST_IA32_SYSENTER_CS = 0x00004c00, CR0_GUEST_HOST_MASK = 0x00006000, CR4_GUEST_HOST_MASK = 0x00006002, CR0_READ_SHADOW = 0x00006004, CR4_READ_SHADOW = 0x00006006, CR3_TARGET_VALUE0 = 0x00006008, CR3_TARGET_VALUE1 = 0x0000600a, CR3_TARGET_VALUE2 = 0x0000600c, CR3_TARGET_VALUE3 = 0x0000600e, EXIT_QUALIFICATION = 0x00006400, GUEST_LINEAR_ADDRESS = 0x0000640a, GUEST_CR0 = 0x00006800, GUEST_CR3 = 0x00006802, GUEST_CR4 = 0x00006804, GUEST_ES_BASE = 0x00006806, GUEST_CS_BASE = 0x00006808, GUEST_SS_BASE = 0x0000680a, GUEST_DS_BASE = 0x0000680c, GUEST_FS_BASE = 0x0000680e, GUEST_GS_BASE = 0x00006810, GUEST_LDTR_BASE = 0x00006812, GUEST_TR_BASE = 0x00006814, GUEST_GDTR_BASE = 0x00006816, GUEST_IDTR_BASE = 0x00006818, GUEST_DR7 = 0x0000681a, GUEST_RSP = 0x0000681c, GUEST_RIP = 0x0000681e, GUEST_RFLAGS = 0x00006820, GUEST_PENDING_DBG_EXCEPTIONS = 0x00006822, GUEST_SYSENTER_ESP = 0x00006824, GUEST_SYSENTER_EIP = 0x00006826, HOST_CR0 = 0x00006c00, HOST_CR3 = 0x00006c02, HOST_CR4 = 0x00006c04, HOST_FS_BASE = 0x00006c06, HOST_GS_BASE = 0x00006c08, HOST_TR_BASE = 0x00006c0a, HOST_GDTR_BASE = 0x00006c0c, HOST_IDTR_BASE = 0x00006c0e, HOST_IA32_SYSENTER_ESP = 0x00006c10, HOST_IA32_SYSENTER_EIP = 0x00006c12, HOST_RSP = 0x00006c14, HOST_RIP = 0x00006c16,};OK, let’s continue with our configuration.The next step is configuring host Segment Registers. __vmx_vmwrite(HOST_ES_SELECTOR, GetEs() &amp; 0xF8); __vmx_vmwrite(HOST_CS_SELECTOR, GetCs() &amp; 0xF8); __vmx_vmwrite(HOST_SS_SELECTOR, GetSs() &amp; 0xF8); __vmx_vmwrite(HOST_DS_SELECTOR, GetDs() &amp; 0xF8); __vmx_vmwrite(HOST_FS_SELECTOR, GetFs() &amp; 0xF8); __vmx_vmwrite(HOST_GS_SELECTOR, GetGs() &amp; 0xF8); __vmx_vmwrite(HOST_TR_SELECTOR, GetTr() &amp; 0xF8);Keep in mind that those fields that start with “HOST_” are related to the state in which the hypervisor sets whenever a VM-exit occurs, and those which begin with “GUEST_” are related to the state in which the hypervisor sets for guest when a VMLAUNCH executed.The purpose of &amp; 0xF8 is that Intel mentioned that the three less significant bits must be cleared; otherwise, it leads to an error as the VMLAUNCH is executed with an Invalid Host State error.Next, we set the VMCS_LINK_POINTER, which should be ‘0xffffffffffffffff’. As we don’t have an additional VMCS. This field is mainly used for hypervisors that want to implement a nested-virtualization behavior (like VMware Nested Virtualization or KVM’s nVMX). // // Setting the link pointer to the required value for 4KB VMCS // __vmx_vmwrite(VMCS_LINK_POINTER, ~0ULL);The rest of this topic intends to virtualize the machine’s current state, so the guest and host configurations must be the same.Let’s configure GUEST_IA32_DEBUGCTL. This field works the same as the IA32_DEBUGCTL MSR in a physical machine, and we can use it if we want to use separate IA32_DEBUGCTL for each guest. It provides bit field controls to enable debug trace interrupts, debug trace stores, trace messages enable, single stepping on branches, last branch record recording, and control freezing of LBR stack.We don’t use it in our hypervisor, but we should configure it to the current machine’s MSR_IA32_DEBUGCTL. We use __readmsr() intrinsic to read this MSR (RDMSR) and put the value of the physical machine to the guest’s GUEST_IA32_DEBUGCTL. __vmx_vmwrite(GUEST_IA32_DEBUGCTL, __readmsr(MSR_IA32_DEBUGCTL) &amp; 0xFFFFFFFF); __vmx_vmwrite(GUEST_IA32_DEBUGCTL_HIGH, __readmsr(MSR_IA32_DEBUGCTL) &gt;&gt; 32);Note that values we put zero on them can be ignored; if you don’t modify them, it’s like you put zero on them.For example, configuring TSC is not important for our hypervisor in the current state, so we put zero on it. /* Time-stamp counter offset */ __vmx_vmwrite(TSC_OFFSET, 0); __vmx_vmwrite(TSC_OFFSET_HIGH, 0); __vmx_vmwrite(PAGE_FAULT_ERROR_CODE_MASK, 0); __vmx_vmwrite(PAGE_FAULT_ERROR_CODE_MATCH, 0); __vmx_vmwrite(VM_EXIT_MSR_STORE_COUNT, 0); __vmx_vmwrite(VM_EXIT_MSR_LOAD_COUNT, 0); __vmx_vmwrite(VM_ENTRY_MSR_LOAD_COUNT, 0); __vmx_vmwrite(VM_ENTRY_INTR_INFO_FIELD, 0);This time, we’ll configure Segment Registers based on the GDT base address for our Host (When VM-Exit occurs). GdtBase = GetGdtBase(); FillGuestSelectorData((PVOID)GdtBase, ES, GetEs()); FillGuestSelectorData((PVOID)GdtBase, CS, GetCs()); FillGuestSelectorData((PVOID)GdtBase, SS, GetSs()); FillGuestSelectorData((PVOID)GdtBase, DS, GetDs()); FillGuestSelectorData((PVOID)GdtBase, FS, GetFs()); FillGuestSelectorData((PVOID)GdtBase, GS, GetGs()); FillGuestSelectorData((PVOID)GdtBase, LDTR, GetLdtr()); FillGuestSelectorData((PVOID)GdtBase, TR, GetTr());GetGdtBase is defined above in the process of gathering information for our VMCS.FillGuestSelectorData is responsible for setting the GUEST selector, attributes, limit, and base for VMCS. It is implemented as below:VOIDFillGuestSelectorData( PVOID GdtBase, ULONG Segreg, USHORT Selector){ SEGMENT_SELECTOR SegmentSelector = {0}; ULONG AccessRights; GetSegmentDescriptor(&amp;SegmentSelector, Selector, GdtBase); AccessRights = ((PUCHAR)&amp;SegmentSelector.ATTRIBUTES)[0] + (((PUCHAR)&amp;SegmentSelector.ATTRIBUTES)[1] &lt;&lt; 12); if (!Selector) AccessRights |= 0x10000; __vmx_vmwrite(GUEST_ES_SELECTOR + Segreg * 2, Selector); __vmx_vmwrite(GUEST_ES_LIMIT + Segreg * 2, SegmentSelector.LIMIT); __vmx_vmwrite(GUEST_ES_AR_BYTES + Segreg * 2, AccessRights); __vmx_vmwrite(GUEST_ES_BASE + Segreg * 2, SegmentSelector.BASE);}The function body for GetSegmentDescriptor :BOOLEANGetSegmentDescriptor(PSEGMENT_SELECTOR SegmentSelector, USHORT Selector, PUCHAR GdtBase){ PSEGMENT_DESCRIPTOR SegDesc; if (!SegmentSelector) return FALSE; if (Selector &amp; 0x4) { return FALSE; } SegDesc = (PSEGMENT_DESCRIPTOR)((PUCHAR)GdtBase + (Selector &amp; ~0x7)); SegmentSelector-&gt;SEL = Selector; SegmentSelector-&gt;BASE = SegDesc-&gt;BASE0 | SegDesc-&gt;BASE1 &lt;&lt; 16 | SegDesc-&gt;BASE2 &lt;&lt; 24; SegmentSelector-&gt;LIMIT = SegDesc-&gt;LIMIT0 | (SegDesc-&gt;LIMIT1ATTR1 &amp; 0xf) &lt;&lt; 16; SegmentSelector-&gt;ATTRIBUTES.UCHARs = SegDesc-&gt;ATTR0 | (SegDesc-&gt;LIMIT1ATTR1 &amp; 0xf0) &lt;&lt; 4; if (!(SegDesc-&gt;ATTR0 &amp; 0x10)) { // LA_ACCESSED ULONG64 Tmp; // this is a TSS or callgate etc, save the base high part Tmp = (*(PULONG64)((PUCHAR)SegDesc + 8)); SegmentSelector-&gt;BASE = (SegmentSelector-&gt;BASE &amp; 0xffffffff) | (Tmp &lt;&lt; 32); } if (SegmentSelector-&gt;ATTRIBUTES.Fields.G) { // 4096-bit granularity is enabled for this segment, scale the limit SegmentSelector-&gt;LIMIT = (SegmentSelector-&gt;LIMIT &lt;&lt; 12) + 0xfff; } return TRUE;}Another MSR called IA32_KERNEL_GS_BASE is used to set the kernel GS base. Whenever instructions like SYSCALL are executed, and the processor enters ring 0, we need to change the current GS register, which can be done using SWAPGS instruction. This instruction copies the content of IA32_KERNEL_GS_BASE into the IA32_GS_BASE, and now it’s used in the kernel when it wants to re-enter the user-mode.MSR_FS_BASE on the other hand, doesn’t have a kernel base because it is used in 32-Bit mode while we have a 64-bit (long mode) kernel.Like the above MSR, we’ll configure the IA32_GS_BASE and IA32_FS_BASE MSRs based on the current system’s MSRs. __vmx_vmwrite(GUEST_FS_BASE, __readmsr(MSR_FS_BASE)); __vmx_vmwrite(GUEST_GS_BASE, __readmsr(MSR_GS_BASE));The GUEST_INTERRUPTIBILITY_INFO and GUEST_ACTIVITY_STATE are set to zero (we’ll describe them in the future parts). __vmx_vmwrite(GUEST_INTERRUPTIBILITY_INFO, 0); __vmx_vmwrite(GUEST_ACTIVITY_STATE, 0); //Active state Now we reach an essential part of the VMCS, and it’s the configuration of CPU_BASED_VM_EXEC_CONTROL and SECONDARY_VM_EXEC_CONTROL controls.These fields enable and disable some essential features of the guest, e.g., we can configure VMCS to cause a VM-Exit whenever execution of HLT instruction is detected (in guest). You can read the description of each bit in the VM-Execution Controls section on this topic. __vmx_vmwrite(CPU_BASED_VM_EXEC_CONTROL, AdjustControls(CPU_BASED_HLT_EXITING | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, MSR_IA32_VMX_PROCBASED_CTLS)); __vmx_vmwrite(SECONDARY_VM_EXEC_CONTROL, AdjustControls(CPU_BASED_CTL2_RDTSCP /* | CPU_BASED_CTL2_ENABLE_EPT*/, MSR_IA32_VMX_PROCBASED_CTLS2));As you can see, we set CPU_BASED_HLT_EXITING that will cause the VM-Exit on HLT and activate secondary controls using the CPU_BASED_ACTIVATE_SECONDARY_CONTROLS bit.In the secondary controls, we used CPU_BASED_CTL2_RDTSCP, and for now, comment CPU_BASED_CTL2_ENABLE_EPT because we don’t need to deal with EPT in this part. In the 7th part, I thoroughly describe about EPT.The description of PIN_BASED_VM_EXEC_CONTROL, VM_EXIT_CONTROLS*, and VM_ENTRY_CONTROLS is available above. We don’t have any special configuration for these controls in this part; hence, let us put zero on them. __vmx_vmwrite(PIN_BASED_VM_EXEC_CONTROL, AdjustControls(0, MSR_IA32_VMX_PINBASED_CTLS)); __vmx_vmwrite(VM_EXIT_CONTROLS, AdjustControls(VM_EXIT_IA32E_MODE | VM_EXIT_ACK_INTR_ON_EXIT, MSR_IA32_VMX_EXIT_CTLS)); __vmx_vmwrite(VM_ENTRY_CONTROLS, AdjustControls(VM_ENTRY_IA32E_MODE, MSR_IA32_VMX_ENTRY_CTLS));Also, the AdjustControls is a function for configuring the 0-settings and 1-settings of these fields (we will describe them in the future parts) but for now; it’s defined like this:ULONGAdjustControls(ULONG Ctl, ULONG Msr){ MSR MsrValue = {0}; MsrValue.Content = __readmsr(Msr); Ctl &amp;= MsrValue.High; /* bit == 0 in high word ==&gt; must be zero */ Ctl |= MsrValue.Low; /* bit == 1 in low word ==&gt; must be one */ return Ctl;}The next step is setting Control Registers and Debug Registers (DR7) for the guest and the host. We set them to the same values as the current machine’s state using intrinsic functions. __vmx_vmwrite(GUEST_CR0, __readcr0()); __vmx_vmwrite(GUEST_CR3, __readcr3()); __vmx_vmwrite(GUEST_CR4, __readcr4()); __vmx_vmwrite(HOST_CR0, __readcr0()); __vmx_vmwrite(HOST_CR3, __readcr3()); __vmx_vmwrite(HOST_CR4, __readcr4());The next part is setting up IDT and GDT’s Base and Limit for our guest. Generally, it’s not a good idea to use the same IDT (and GDT) for the guest and host, but in order to keep our hypervisor simple, we’ll configure them to the same value. __vmx_vmwrite(GUEST_GDTR_BASE, GetGdtBase()); __vmx_vmwrite(GUEST_IDTR_BASE, GetIdtBase()); __vmx_vmwrite(GUEST_GDTR_LIMIT, GetGdtLimit()); __vmx_vmwrite(GUEST_IDTR_LIMIT, GetIdtLimit());Next, set the RFLAGS. __vmx_vmwrite(GUEST_RFLAGS, GetRflags());If you want to use SYSENTER in your guest, you should configure the following MSRs. It’s not important to set these values in x64 Windows because Windows doesn’t support SYSENTER in x64 versions of Windows; instead, it uses SYSCALL.The same instruction works for 32-bit processes too. In 32-bit processes, Windows first changes the execution mode to long-mode (using Heaven’s Gate technique), and then executes the SYSCALL instruction. __vmx_vmwrite(GUEST_SYSENTER_CS, __readmsr(MSR_IA32_SYSENTER_CS)); __vmx_vmwrite(GUEST_SYSENTER_EIP, __readmsr(MSR_IA32_SYSENTER_EIP)); __vmx_vmwrite(GUEST_SYSENTER_ESP, __readmsr(MSR_IA32_SYSENTER_ESP)); __vmx_vmwrite(HOST_IA32_SYSENTER_CS, __readmsr(MSR_IA32_SYSENTER_CS)); __vmx_vmwrite(HOST_IA32_SYSENTER_EIP, __readmsr(MSR_IA32_SYSENTER_EIP)); __vmx_vmwrite(HOST_IA32_SYSENTER_ESP, __readmsr(MSR_IA32_SYSENTER_ESP));Don’t forget to configure HOST_FS_BASE, HOST_GS_BASE, HOST_GDTR_BASE, HOST_IDTR_BASE, HOST_TR_BASE for the host in the VMCS. GetSegmentDescriptor(&amp;SegmentSelector, GetTr(), (PUCHAR)GetGdtBase()); __vmx_vmwrite(HOST_TR_BASE, SegmentSelector.BASE); __vmx_vmwrite(HOST_FS_BASE, __readmsr(MSR_FS_BASE)); __vmx_vmwrite(HOST_GS_BASE, __readmsr(MSR_GS_BASE)); __vmx_vmwrite(HOST_GDTR_BASE, GetGdtBase()); __vmx_vmwrite(HOST_IDTR_BASE, GetIdtBase());The next important part is to set the RIP and RSP registers of the guest when a VMLAUNCH is executed. It starts with the RIP you configured in this part and RIP and RSP of the host when a VM-exit occurs. It’s pretty clear that host RIP should point to a function responsible for managing VMX events based on the VM-exit code and whether decide to execute a VMRESUME or turn off the hypervisor using VMXOFF. __vmx_vmwrite(GUEST_RSP, (ULONG64)g_VirtualGuestMemoryAddress); // setup guest sp __vmx_vmwrite(GUEST_RIP, (ULONG64)g_VirtualGuestMemoryAddress); // setup guest ip __vmx_vmwrite(HOST_RSP, ((ULONG64)GuestState-&gt;VmmStack + VMM_STACK_SIZE - 1)); __vmx_vmwrite(HOST_RIP, (ULONG64)AsmVmexitHandler);HOST_RSP points to VmmStack that we allocated before, and HOST_RIP points to AsmVmexitHandler (an assembly written function described below). GUEST_RIP points to g_VirtualGuestMemoryAddress (the global variable we configured during EPT initialization) and GUEST_RSP to the same address (g_VirtualGuestMemoryAddress) because we don’t put any instruction that uses the stack, so for a real-world example, it should point to a different writeable address.Done! Our VMCS is almost ready.Checking VMCS LayoutUnfortunately, checking VMCS Layout is not as straight as the other parts. We have to control all the checklists described in [CHAPTER 26] VM ENTRIES from Intel’s 64 and IA-32 Architectures Software Developer’s Manual, including the following sections: 26.2 CHECKS ON VMX CONTROLS AND HOST-STATE AREA 26.3 CHECKING AND LOADING GUEST STATE 26.4 LOADING MSRS 26.5 EVENT INJECTION 26.6 SPECIAL FEATURES OF VM ENTRY 26.7 VM-ENTRY FAILURES DURING OR AFTER LOADING GUEST STATE 26.8 MACHINE-CHECK EVENTS DURING VM ENTRYThe hardest part of this process is when we have no idea about the incorrect part of your VMCS layout or, on the other hand, when you miss something that eventually causes the failure.This is because Intel just gives an error number without any further details about what’s exactly wrong in n our VMCS Layout.The errors are shown below.To solve this problem, I created a user-mode application called VmcsAuditor. As its name describes, it can be a choice if you have any error and don’t have any idea about solving the problem.Remember that VmcsAuditor is a tool based on Bochs emulator support for VMX, so all the checks come from Bochs, and it’s not a 100% reliable tool that solves all the problem as we don’t know what exactly happens inside the processor. Still, it can be handy and a time saver.The source code and executable files are available on GitHub :[https://github.com/SinaKarvandi/VMCS-Auditor]Further description available here.As a better alternative, you can use Satoshi Tanda’s code for checking the guest state.VM-Exit HandlerWhen our guest software exits and gives the handle back to the host, the following VM-exit reasons might happen.#define EXIT_REASON_EXCEPTION_NMI 0#define EXIT_REASON_EXTERNAL_INTERRUPT 1#define EXIT_REASON_TRIPLE_FAULT 2#define EXIT_REASON_INIT 3#define EXIT_REASON_SIPI 4#define EXIT_REASON_IO_SMI 5#define EXIT_REASON_OTHER_SMI 6#define EXIT_REASON_PENDING_VIRT_INTR 7#define EXIT_REASON_PENDING_VIRT_NMI 8#define EXIT_REASON_TASK_SWITCH 9#define EXIT_REASON_CPUID 10#define EXIT_REASON_GETSEC 11#define EXIT_REASON_HLT 12#define EXIT_REASON_INVD 13#define EXIT_REASON_INVLPG 14#define EXIT_REASON_RDPMC 15#define EXIT_REASON_RDTSC 16#define EXIT_REASON_RSM 17#define EXIT_REASON_VMCALL 18#define EXIT_REASON_VMCLEAR 19#define EXIT_REASON_VMLAUNCH 20#define EXIT_REASON_VMPTRLD 21#define EXIT_REASON_VMPTRST 22#define EXIT_REASON_VMREAD 23#define EXIT_REASON_VMRESUME 24#define EXIT_REASON_VMWRITE 25#define EXIT_REASON_VMXOFF 26#define EXIT_REASON_VMXON 27#define EXIT_REASON_CR_ACCESS 28#define EXIT_REASON_DR_ACCESS 29#define EXIT_REASON_IO_INSTRUCTION 30#define EXIT_REASON_MSR_READ 31#define EXIT_REASON_MSR_WRITE 32#define EXIT_REASON_INVALID_GUEST_STATE 33#define EXIT_REASON_MSR_LOADING 34#define EXIT_REASON_MWAIT_INSTRUCTION 36#define EXIT_REASON_MONITOR_TRAP_FLAG 37#define EXIT_REASON_MONITOR_INSTRUCTION 39#define EXIT_REASON_PAUSE_INSTRUCTION 40#define EXIT_REASON_MCE_DURING_VMENTRY 41#define EXIT_REASON_TPR_BELOW_THRESHOLD 43#define EXIT_REASON_APIC_ACCESS 44#define EXIT_REASON_ACCESS_GDTR_OR_IDTR 46#define EXIT_REASON_ACCESS_LDTR_OR_TR 47#define EXIT_REASON_EPT_VIOLATION 48#define EXIT_REASON_EPT_MISCONFIG 49#define EXIT_REASON_INVEPT 50#define EXIT_REASON_RDTSCP 51#define EXIT_REASON_VMX_PREEMPTION_TIMER_EXPIRED 52#define EXIT_REASON_INVVPID 53#define EXIT_REASON_WBINVD 54#define EXIT_REASON_XSETBV 55#define EXIT_REASON_APIC_WRITE 56#define EXIT_REASON_RDRAND 57#define EXIT_REASON_INVPCID 58#define EXIT_REASON_RDSEED 61#define EXIT_REASON_PML_FULL 62#define EXIT_REASON_XSAVES 63#define EXIT_REASON_XRSTORS 64#define EXIT_REASON_PCOMMIT 65VMX-exit handler should be an assembly function because calling a compiled function needs some preparation and some register modification. The necessary thing in the VM-exit handler is saving the registers’ state so we can continue the guest later.I create a sample function for saving and restoring registers. In this function, we call another C function to extend the vm-exit handler.PUBLIC AsmVmexitHandlerEXTERN MainVmexitHandler:PROCEXTERN VmResumeInstruction:PROC.code _textAsmVmexitHandler PROC PUSH R15 PUSH R14 PUSH R13 PUSH R12 PUSH R11 PUSH R10 PUSH R9 PUSH R8 PUSH RDI PUSH RSI PUSH RBP PUSH RBP ; RSP PUSH RBX PUSH RDX PUSH RCX PUSH RAX MOV RCX, RSP ; GuestRegs SUB RSP, 28h CALL MainVmexitHandler ADD RSP, 28h POP RAX POP RCX POP RDX POP RBX POP RBP ; RSP POP RBP POP RSI POP RDI POP R8 POP R9 POP R10 POP R11 POP R12 POP R13 POP R14 POP R15 SUB RSP, 0100h ; to avoid error in future functions JMP VmResumeInstruction AsmVmexitHandler ENDPENDThe main VM-exit handler is a switch-case function with different decisions over the VMCS VM_EXIT_REASON and EXIT_QUALIFICATION.In this part, we’re just performing an action over EXIT_REASON_HLT and just print the result and restore the guest state normally.From the following code, you can see what event cause the VM-exit. Just keep in mind that some reasons only lead to Vm-exit if the VMCS’s control execution fields (described above) configure it. For instance, the execution of HLT instruction in guest will cause VM-exit if the 7th bit of the Primary Processor-Based VM-Execution Controls allows it.VOIDMainVmexitHandler(PGUEST_REGS GuestRegs){ ULONG ExitReason = 0; __vmx_vmread(VM_EXIT_REASON, &amp;ExitReason); ULONG ExitQualification = 0; __vmx_vmread(EXIT_QUALIFICATION, &amp;ExitQualification); DbgPrint("\\nVM_EXIT_REASION 0x%x\\n", ExitReason &amp; 0xffff); DbgPrint("\\EXIT_QUALIFICATION 0x%x\\n", ExitQualification); switch (ExitReason) { // // 25.1.2 Instructions That Cause VM Exits Unconditionally // The following instructions cause VM exits when they are executed in VMX non-root operation: CPUID, GETSEC, // INVD, and XSETBV. This is also true of instructions introduced with VMX, which include: INVEPT, INVVPID, // VMCALL, VMCLEAR, VMLAUNCH, VMPTRLD, VMPTRST, VMRESUME, VMXOFF, and VMXON. // case EXIT_REASON_VMCLEAR: case EXIT_REASON_VMPTRLD: case EXIT_REASON_VMPTRST: case EXIT_REASON_VMREAD: case EXIT_REASON_VMRESUME: case EXIT_REASON_VMWRITE: case EXIT_REASON_VMXOFF: case EXIT_REASON_VMXON: case EXIT_REASON_VMLAUNCH: { break; } case EXIT_REASON_HLT: { DbgPrint("[*] Execution of HLT detected... \\n"); // // that's enough for now ;) // AsmVmxoffAndRestoreState(); break; } case EXIT_REASON_EXCEPTION_NMI: { break; } case EXIT_REASON_CPUID: { break; } case EXIT_REASON_INVD: { break; } case EXIT_REASON_VMCALL: { break; } case EXIT_REASON_CR_ACCESS: { break; } case EXIT_REASON_MSR_READ: { break; } case EXIT_REASON_MSR_WRITE: { break; } case EXIT_REASON_EPT_VIOLATION: { break; } default: { // DbgBreakPoint(); break; } }}Resume to next instructionIf a VM-exit occurs (e.g., the guest executed a CPUID instruction), the guest RIP remains constant, and it’s up to VMM to change the Guest’s RIP or not, so if we don’t have a certain function for managing this situation, then the processor executes an infinite loop of CPUID instructions because we didn’t increment the RIP.In order to solve this problem, we have to read a VMCS field called VM_EXIT_INSTRUCTION_LEN that stores the length of the instruction that caused the VM-exit.First, we have to read the guest’s current RIP from the GUEST_RIP. Second, read the VM_EXIT_INSTRUCTION_LEN using VMREAD, and third the length of the instruction to the guest’s RIP. Now the guest will continue its execution from the next instruction, and we’re good to go.The following function is for this purpose.VOIDResumeToNextInstruction(){ PVOID ResumeRIP = NULL; PVOID CurrentRIP = NULL; ULONG ExitInstructionLength = 0; __vmx_vmread(GUEST_RIP, &amp;CurrentRIP); __vmx_vmread(VM_EXIT_INSTRUCTION_LEN, &amp;ExitInstructionLength); ResumeRIP = (PCHAR)CurrentRIP + ExitInstructionLength; __vmx_vmwrite(GUEST_RIP, (ULONG64)ResumeRIP);}VMRESUME InstructionNow that we handled the VM-exit, it’s time to continue the guest. We could continue the execution by using the VMRESUME instruction.VMRESUME is like VMLAUNCH, but it’s used in order to resume the guest.To compare these instructions, VMLAUNCH fails if the launch state of the current VMCS is not “clear”. If the instruction is successful, it sets the launch state to “launched.” VMRESUME fails if the launch state of the current VMCS is not “launched.” So it’s clear that if we executed the VMLAUNCH instruction before, we can’t use it anymore to resume the guest code, and in this condition, VMRESUME is used.The following code is the implementation of VMRESUME.VOIDVmResumeInstruction(){ __vmx_vmresume(); // if VMRESUME succeeds will never be here ! ULONG64 ErrorCode = 0; __vmx_vmread(VM_INSTRUCTION_ERROR, &amp;ErrorCode); __vmx_off(); DbgPrint("[*] VMRESUME Error : 0x%llx\\n", ErrorCode); // // It's such a bad error because we don't where to go! // prefer to break // DbgBreakPoint();}Let’s Test it!Well, we have done with the configuration, and now it’s time to run our driver using OSR Driver Loader. As always, we should disable the driver signature enforcement and run our driver.As you can see from the above picture (in the launching VM area), first, we set the current logical processor to 0. Next, we clear our VMCS status using the VMCLEAR instruction, set up our VMCS layout and execute the VMLAUNCH instruction.Now, our guest code is executed, and as we configured our VMCS to cause a VM-exit in the case of the execution of the HLT (CPU_BASED_HLT_EXITING) instruction.After running the guest, the VM-exit handler is called, then it calls the main VM-exit handler, and as the VMCS exit reason is 0xc (EXIT_REASON_HLT), we successfully detected the execution of HLT in the guest.After that, our machine state saving mechanism is executed, and we successfully turn off the hypervisor using the VMXOFF instruction and return to the first caller with a successful **(RAX = 1) status.That’s it! Wasn’t it easy?ConclusionIn this part, we got familiar with configuring the Virtual Machine Control Structure and finally ran our guest code. The future parts would be an enhancement to this configuration like entering protected-mode, interrupt injection, page modification logging, virtualizing the current machine, and so on. You can use the comments section below if you have any questions or problems.See you in the next part.The sixth part is also available here.References[1] Vol 3C - Chapter 24 – (VIRTUAL MACHINE CONTROL STRUCTURES (https://software.intel.com/en-us/articles/intel-sdm)[2] Vol 3C - Chapter 26 – (VM ENTRIES) (https://software.intel.com/en-us/articles/intel-sdm)[3] Segmentation (https://wiki.osdev.org/Segmentation)[4] x86 memory segmentation (https://en.wikipedia.org/wiki/X86_memory_segmentation)[5] VmcsAuditor – A Bochs-Based Hypervisor Layout Checker (https://rayanfam.com/topics/vmcsauditor-a-bochs-based-hypervisor-layout-checker/)[6] Rohaaan/Hypervisor For Beginners (https://github.com/rohaaan/hypervisor-for-beginners)[7] SWAPGS — Swap GS Base Register (https://www.felixcloutier.com/x86/SWAPGS.html)[8] Knockin’ on Heaven’s Gate - Dynamic Processor Mode Switching (http://rce.co/knockin-on-heavens-gate-dynamic-processor-mode-switching/)" }, { "title": "VmcsAuditor - A Bochs-Based Hypervisor Layout Checker", "url": "/topics/vmcsauditor-a-bochs-based-hypervisor-layout-checker/", "categories": "hypervisor, software", "tags": "guest-state-error, host-state-error, vmcs-auditor, vmcs-layout-checking, vmlaunch-0x7, vmlaunch-0x8, vmlaunch-error, vmresume-error, vmx-error-checking", "date": "2018-12-07 00:00:00 +0000", "snippet": "IntroductionIf you’ve ever had experience with developing your own hypervisor from scratch then you definitely faced with the problems of layout checking. What makes it hard to develop a hypervisor is the fact that Intel has a small number of errors for a huge checklist described in [CHAPTER 26] VM ENTRIES from Intel’s 64 and IA-32 Architectures Software Developer’s Manual including: 26.2 CHECKS ON VMX CONTROLS AND HOST-STATE AREA 26.3 CHECKING AND LOADING GUEST STATE 26.4 LOADING MSRS 26.5 EVENT INJECTION 26.6 SPECIAL FEATURES OF VM ENTRY 26.7 VM-ENTRY FAILURES DURING OR AFTER LOADING GUEST STATE 26.8 MACHINE-CHECK EVENTS DURING VM ENTRYYou can check these lists by your own and if you can’t understand what is the exact problem then you can use some tools like Boch Emulator which supports the Intel VMX.Unfortunately, Bochs emulator is really slow (as the nature of emulating) and sometimes have lots of problems with stability but there are lots of cool features that implemented in this emulator and one of them is VMX support. VMCS Auditor is a copy of Bochs emulator in a user-mode application. It asks you for each field of VMCS Layout and tells you what was your mistake.Keep in mind, Bochs is just an implementation of Intel’s VMX behavior and by no mean 100% precise so there might be some problems which Bochs is not able to detect.Note that even VMCS Auditor is a Windows executable file that runs in Windows, but its checks are related to CPU (not the OS) so it’s clear that you can give your VMCS Layout in other OSs like Linux or OS X an expect the same result.Source code &amp; ExecutablesThe full source code and executable file are available on Github:[https://github.com/SinaKarvandi/VMCS-Auditor]Error DetectionThe following table describes what error can occur for every vmx instructions.VMCS Auditor focuses on Error Number #7 and #8 to perform the checking lists described in Intel’s SDM as implemented in Bochs emulator.How to use?In order to use VMCS Auditor, first, you have to enter some MSRs related to Intel’s VMX (e.g IA32_VMX_PINBASED_CTL). Second VMCS Auditor asks you for other options based on your settings, newer settings are appearing and if you have any problem configuring these options then the error(s) are shown to you. If you have a problem with configuring MSRs, you have to open your Windbg in Local Kernel Debugging Mode (remember MSRs in a VM based kernel debugging might not be true for  VMX related options) then you can use rdmsr which gives you the corresponding value.For instance, we need to get MSR (0x481) then execute the following command in Windbg.rdmsr 0x481The above command’s result is illustrated below.The following MSRs representing my own system’s MSRs for VMX.lkd&gt; rdmsr 0x481msr[481] = 0000007f`00000016lkd&gt; rdmsr 0x482msr[482] = fff9fffe`0401e172lkd&gt; rdmsr 0x48bmsr[48b] = 001ffcff`00000000lkd&gt; rdmsr 0x483msr[483] = 01ffffff`00036dfflkd&gt; rdmsr 0x484msr[484] = 0003ffff`000011fflkd&gt; rdmsr 0x48cmsr[48c] = 00000f01`06334141lkd&gt; rdmsr 0x491msr[491] = 00000000`00000001lkd&gt; rdmsr 0x486msr[486] = 00000000`80000021lkd&gt; rdmsr 0x487msr[487] = 00000000`fffffffflkd&gt; rdmsr 0x488msr[488] = 00000000`00002000lkd&gt; rdmsr 0x489msr[489] = 00000000`003767ffFuture Works Adding support for config file that works with VMCS Auditor.ContributionIf you want to contribute to this project (e.g adding more checks or make VMCS Auditor more precise) then you can use the GitHub or suggest edits below in the comments.ConclusionThis tool is published in order to ease the hypervisor development, in the future part of the tutorial Hypervisor From Scratch, we’ll use this tool to test our VMCS Layout before any real-world testing.References[1] Vol 3C – 26 VM ENTRIES  (https://software.intel.com/en-us/articles/intel-sdm)[2] Bochs Emulator  (http://bochs.sourceforge.net)" }, { "title": "Start linux kernel module development!", "url": "/topics/start-linux-kernel-module-development/", "categories": "kernel-mode, linux, programming", "tags": "c, insmod, kernel, ko, linux, lkm, lsmod, make, modprobe, module", "date": "2018-10-19 00:00:00 +0000", "snippet": "Hi everyone!In this post I’m going to introduce you to the world of linux kernel module development. I am a newcomer in this field myself but I decided to document everything in this blog as I gradually learn them.To start you need some sort of virtual machine. Of course you can test kernel modules on your own system but it is very risky and you can’t really debug them effectively and must use printk and debug messages to find problems in your code.The first option is to use VirtualBox and install a linux distro in it. This way your system won’t crash and you will be safe regarding any data loss. But debugging is still not very trivial.Second option would be using QEMU and buildroot, you can easily step-debug linux kernel code or your own code. This is the recommended method.I have found a really nice linux+buildroot setup here. Plus some really nice tricks in linux kernel programming as the name also suggests.Using cirosantilli buildrootJust follow his guide on GitHub. What lies here is only some glitches I ran into while setting up the buildroot. Clone the whole repository. You are better off this way.git clone --recurse-submodules -j16 "https://github.com/cirosantilli/linux-kernel-module-cheat" Pay attention to dependencies. strangely the configure script does not check for all of them. pay attention to error messages if you encounter any and find the appropriate package to install for your distro. (most likely you will need development version of the package too)If you are using the buildroot mentioned then there is no need for this step. This is the minimal setup to get you started with kernel module development.Install dependencies for compiling kernel module (if you are on Ubuntu run this):apt-get install build-essential linux-headers-$(uname -r)Your first moduleCreate a directory and  put this piece of code into a file (like ko_example.c):#include &lt;linux/init.h&gt;#include &lt;linux/module.h&gt;#include &lt;linux/kernel.h&gt;MODULE_LICENSE("GPL");MODULE_AUTHOR("Shahriar EV");MODULE_DESCRIPTION("sample linux kernel module.");MODULE_VERSION("1.00");static int __init mylkm_init(void) { printk(KERN_INFO "HI!\\n"); return 0;}static void __exit mylkm_exit(void) { printk(KERN_INFO "BYE!\\n");}module_init(mylkm_init);module_exit(mylkm_exit); The includes at the top are pretty obvious. they are required for linux kernel programming and provide us with all the functions we are using here. The next block is module specs. self explanatory… mylkm_init is the function which is called when module is loaded. mylkm_exit is the function which is called when module is unloaded. printk is used to print stuff from kernel which then can be read via dmesg. KERN_INFO is severity level of the log message which comes after that.That is enough code for now, however we need a Makefile to compile the kernel module.Save this in a file name Makefile in the same directory as the source code:obj-m += lkm_example.oall: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesclean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) cleanNow run make and it should build successfully. To load it run (as root):insmod ko_example.koIf everything is okay, you can see “Hello World” in dmesg buffer:dmesgThe module should now be visible on lsmod output.On unloading of the kernel module, printk is called again:rmmod ko_example.koIt is very important to know that LKM developement is different from userland applications. I like to quote directly from derekmolloy.ie : A kernel module is not an application! do not execute sequentially— a kernel module registers itself to handle requests using its initialization function, which runs and then terminates. The type of requests that it can handle are defined within the module code. This is quite similar to the event-driven programming model that is commonly utilized in graphical-user interface (GUI) applications. do not have automatic cleanup — any resources that are allocated to the module must be manually released when the module is unloaded, or they may be unavailable until a system reboots. do not have printf() functions — kernel code cannot access libraries of code that is written for the Linux user space. The kernel module lives and runs in kernel space, which has its own memory address space. The interface between kernel space and user space is clearly defined and controlled. We do however have a printk() function that can output information, which can be viewed from within user space. can be interrupted — one conceptually difficult aspect of kernel modules is that they can be used by several different programs/processes at the same time. We have to carefully construct our modules so that they have a consistent and valid behavior when they are interrupted. we have to consider the impact of multiple processes accessing the module simultaneously. have a higher level of execution privilege — typically, more CPU cycles are allocated to kernel modules than to user-space programs. This sounds like an advantage, however, you have to be very careful that your module does not adversely affect the overall performance of your system. do not have floating-point support — it is kernel code that uses traps to transition from integer to floating-point mode for your user space applications. However, it is very difficult to perform these traps in kernel space. The alternative is to manually save and restore floating point operations — a task that is best avoided and left to your user-space code. http://derekmolloy.ie/writing-a-linux-kernel-module-part-1-introduction/I think that would suffice for the first try! Further topics could be actually doing some practical tasks for a kernel module; like showing some data to userland or providing some device for other programs to use.References https://blog.sourcerer.io/writing-a-simple-linux-kernel-module-d9dc3762c234 https://github.com/cirosantilli/linux-kernel-module-cheat http://derekmolloy.ie/writing-a-linux-kernel-module-part-1-introduction/ https://stackoverflow.com/questions/39652385/kernel-modules-how-can-i-modify-printk-severity-based-on-a-passed-parameter" }, { "title": "Hypervisor From Scratch – Part 4: Address Translation Using Extended Page Table (EPT)", "url": "/topics/hypervisor-from-scratch-part-4/", "categories": "cpu, hypervisor, tutorials", "tags": "hypervisor, all-context, ept, eptp, extended-page-table, extended-page-table-pointer, hypervisor-paging, invept, nested-page-tables, npt, rapid-virtualization-indexing, rvi, second-level-address-translation, single-context, slat, stage-2-page-tables", "date": "2018-10-05 00:00:00 +0000", "snippet": "If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous practical examples on how to utilize hypervisors for reverse engineering.IntroductionWelcome to the 4th part of the “Hypervisor From Scratch”. This part primarily involves translating guest addresses through Extended Page Table (EPT) and its implementation. We also see how shadow tables work and basic concepts about EPT.Table of Contents Introduction Table of Contents Overview Second Level Address Translation (SLAT) Software-assisted paging (Shadow Page Tables) Hardware-assisted paging (Extended Page Table) Extended Page Table vs. Shadow Page Table Detecting Support for EPT, NPT EPT Translation Implementing Extended Page Table (EPT) Accessed and Dirty Flags in EPTP 5-Level EPT Translation Conclusion ReferencesOverviewFirst of all, make sure to read the earlier parts before reading this topic, as these parts depend on each other. It would help if you also had a basic understanding of the paging mechanism and how page tables work. A good article is here for paging tables.Most of this topic is derived from Chapter 28 - (VMX SUPPORT FOR ADDRESS TRANSLATION) available at Intel 64 and IA-32 architectures software developer’s manual combined volumes 3.The full source code of this tutorial is available on GitHub :[https://github.com/SinaKarvandi/Hypervisor-From-Scratch]Before starting, I should give my thanks to Petr Beneš, as this part would never have been completed without his help.Note: This part tends to give you basic information about EPT. The main implementation of EPT for our hypervisor is explained in part 7. In part 7, we used the concept we learned here to implement EPT on an already virtualized system.Note: Remember that hypervisors change over time because new features are added to the operating systems or new technologies are used. For example, updates to Meltdown &amp; Spectre have made a lot of changes to the hypervisors. So, if you want to use Hypervisor From Scratch in your projects, research, or whatever, you should use the HyperDbg drivers. HyperDbg is actively maintained, stable, and reliable, ensuring you avoid the errors and instability problems that can arise from using older parts of the tutorial series.Second Level Address Translation (SLAT)Second Level Address Translation (SLAT) or nested paging is an extended layer in the paging mechanism used to map hardware-based virtualization virtual addresses into the physical memory.AMD implemented SLAT through the Rapid Virtualization Indexing (RVI) technology known as Nested Page Tables (NPT) since the introduction of its third-generation Opteron processors and microarchitecture code name Barcelona. Intel also implemented SLAT in Intel VT-x technologies since the introduction of microarchitecture code name Nehalem and it’s known as Extended Page Table (EPT) and is used in Core i9, Core i7, Core i5, and Core i3 processors.ARM processors also have an implementation known as Stage-2 page-tables.There are two methods for implementing SLAT. The first one is Shadow Page Tables, and the second one is Extended Page Tables.Software-assisted paging (Shadow Page Tables)The hypervisor uses Shadow Page Tables to keep track of the state of physical memory in which the guest thinks that it has access to physical memory, but in the real world, the hardware prevents it from accessing hardware memory.Without this prevention, the guest might control the host, which is not what is intended.In this case, VMM maintains Shadow Page Tables that map guest virtual pages directly to machine pages.By the way, using Shadow Page Table is not recommended today as it always leads to VMM traps (which result in a vast amount of VM-exits) and losses the performance due to the TLB flush on every switch. Another caveat is that there is a memory overhead due to shadow copying of guest page tables.Hardware-assisted paging (Extended Page Table)To reduce the complexity of Shadow Page Tables, avoid the excessive VM-exits, and reduce the number of TLB flushes, EPT implemented a hardware-assisted paging strategy to increase performance.According to a VMware evaluation paper: “EPT provides performance gains of up to 48% for MMU-intensive benchmarks and up to 600% for MMU-intensive microbenchmarks”.EPT implemented one more page table hierarchy to map guest virtual address to guest physical address, which is valid in the main memory.In EPT, One page table is maintained by guest OS, which is used to generate the guest’s physical address. The other page table is maintained by VMM, which maps the guest’s physical address to the host’s physical address.So for each memory access operation, EPT MMU directly gets the guest’s physical address from the guest page table and then automatically gets the host’s physical address from the VMM mapping table.Extended Page Table vs. Shadow Page TableEPT: Walk any requested address Appropriate to programs that have a large amount of page table miss when executing Less chance to exit VM (less context switch) Two-layer EPT Means each access needs to walk two tables Easier to develop Many particular registers Hardware helps guest OS to notify the VMM SPT: Only walk when SPT entry miss Appropriate to programs that would access only some addresses frequently Every access might be intercepted by VMM (many traps) One reference Fast and convenient when page hit Hard to develop Two-layer structure Complicated reverse map Permission emulation Detecting Support for EPT, NPTIf you want to see whether your system supports EPT on Intel processor or NPT on AMD processor without using assembly (CPUID), you can download coreinfo.exe from SysInternals, then run it. The last line will show you if your processor supports EPT or NPT.EPT TranslationEPT defines a layer of address translation that augments the translation of linear addresses.The extended page-table mechanism (EPT) is a feature that can be used to support the virtualization of physical memory. When EPT is in use, certain addresses that would normally be treated as physical addresses (and used to access memory) are instead treated as guest physical addresses. Guest physical addresses are translated by traversing a set of EPT paging structures to produce physical addresses that are used to access memory.EPT is used when the “enable EPT” VM-execution control is 1. It translates the guest physical addresses used in VMX non-root operation and those used by VM entry for event injection.EPT translation is exactly like regular paging translation but with some minor differences. In paging, the processor translates a virtual address to a physical address, while in EPT translation, we want to translate a guest’s physical address to a host’s physical address.If you’re familiar with paging, the 3rd control register (CR3) is the base address of the PML4 table (in an x64 processor or, more generally, it points to the root paging directory). In EPT guest is not aware of EPT translation, so it has CR3 too, but this CR3 is used to convert the guest’s virtual address to the guest’s physical address. Whenever we find our target (the guest’s physical address), the EPT mechanism treats our guest’s physical address like a virtual address and converts it to the host’s physical address. In this mechanism, EPTP is like CR3 but for EPT.Just think about the above sentence one more time!So your target physical address should be divided into four parts. The first 9 bits point to EPT PML4E (note that the PML4 base address is in EPTP). The second 9 bits indicate the EPT PDPT Entry (the base address of PDPT comes from EPT PML4E), the third 9 bits point to EPT PD Entry (the base address of PD comes from EPT PDPTE), and the last 9 bits of the guest physical address point to an entry in EPT PT table (the base address of PT comes from EPT PDE) and now the EPT PT Entry points to the host physical address of the corresponding page.You might ask, as a simple Virtual to Physical Address translation involves accessing four physical addresses, so what happens?The answer is the processor internally translates all tables’ physical addresses one by one; that’s why paging and accessing memory in a guest software is slower than hardware address translation. The following picture illustrates the operations for a guest’s virtual address to the host’s physical address.If you want to think about x86 EPT virtualization, assume, for example, that CR4.PAE = CR4.PSE = 0. The translation of a 32-bit linear address then operates as follows: Bits 31:22 of the linear address select an entry in the guest page directory located at the guest physical address in CR3. The guest’s physical address of the guest page-directory entry (PDE) is translated through EPT to determine the guest PDE’s physical address. Bits 21:12 of the linear address select an entry in the guest page table located at the guest’s physical address in the guest PDE. The guest physical address of the guest page-table entry (PTE) is translated through EPT to determine the guest PTE’s physical address. Bits 11:0 of the linear address is the offset in the page frame located at the guest’s physical address in the guest PTE. The guest’s physical address determined by this offset is translated through EPT to select the physical address to which the original linear address translates.Note that PAE stands for Physical Address Extension, which is a memory management feature for the x86 architecture that extends the address space, and PSE stands for Page Size Extension that refers to a feature of x86 processors that allows for pages larger than the standard 4 KiB size.In addition to translating a guest’s physical address to a host’s physical address, EPT specifies the privileges that software is allowed when accessing the address. Attempts at disallowed accesses are called EPT violations and cause VM-exits.Remember that an address will not translate through EPT when there is no read/write access.Implementing Extended Page Table (EPT)Now that we know some basics, let’s implement what we’ve learned before. Based on the Intel manual, we should write (VMWRITE) EPTP or Extended-Page-Table Pointer to the VMCS. The EPTP structure is described below.The above tables can be described using the following structure:// See Table 24-8. Format of Extended-Page-Table Pointertypedef union _EPTP { ULONG64 All; struct { UINT64 MemoryType : 3; // bit 2:0 (0 = Uncacheable (UC) - 6 = Write - back(WB)) UINT64 PageWalkLength : 3; // bit 5:3 (This value is 1 less than the EPT page-walk length) UINT64 DirtyAndAceessEnabled : 1; // bit 6 (Setting this control to 1 enables accessed and dirty flags for EPT) UINT64 Reserved1 : 5; // bit 11:7 UINT64 PML4Address : 36; UINT64 Reserved2 : 16; }Fields;}EPTP, *PEPTP;Like the regular paging mechanism, each entry in all EPT tables is 64-bit long. EPT PML4E, EPT PDPTE, and EPT PD are the same, but EPT PTE has some minor differences.An EPT entry is something like this:Ok, Now we should implement tables; the first table is PML4. The following table shows the format of an EPT PML4 Entry (PML4E).PML4E is a structure like this :// See Table 28-1. typedef union _EPT_PML4E { ULONG64 All; struct { UINT64 Read : 1; // bit 0 UINT64 Write : 1; // bit 1 UINT64 Execute : 1; // bit 2 UINT64 Reserved1 : 5; // bit 7:3 (Must be Zero) UINT64 Accessed : 1; // bit 8 UINT64 Ignored1 : 1; // bit 9 UINT64 ExecuteForUserMode : 1; // bit 10 UINT64 Ignored2 : 1; // bit 11 UINT64 PhysicalAddress : 36; // bit (N-1):12 or Page-Frame-Number UINT64 Reserved2 : 4; // bit 51:N UINT64 Ignored3 : 12; // bit 63:52 }Fields;}EPT_PML4E, *PEPT_PML4E;As long as we use 4-level paging, the second table is EPT Page-Directory-Pointer-Table (PDTP). The following picture illustrates the format of PDPTE:PDPTE’s structure is like this:// See Table 28-3typedef union _EPT_PDPTE { ULONG64 All; struct { UINT64 Read : 1; // bit 0 UINT64 Write : 1; // bit 1 UINT64 Execute : 1; // bit 2 UINT64 Reserved1 : 5; // bit 7:3 (Must be Zero) UINT64 Accessed : 1; // bit 8 UINT64 Ignored1 : 1; // bit 9 UINT64 ExecuteForUserMode : 1; // bit 10 UINT64 Ignored2 : 1; // bit 11 UINT64 PhysicalAddress : 36; // bit (N-1):12 or Page-Frame-Number UINT64 Reserved2 : 4; // bit 51:N UINT64 Ignored3 : 12; // bit 63:52 }Fields;}EPT_PDPTE, *PEPT_PDPTE;For the third table of paging, we should implement an EPT Page-Directory Entry (PDE) as described below:PDE’s structure:// See Table 28-5typedef union _EPT_PDE { ULONG64 All; struct { UINT64 Read : 1; // bit 0 UINT64 Write : 1; // bit 1 UINT64 Execute : 1; // bit 2 UINT64 Reserved1 : 5; // bit 7:3 (Must be Zero) UINT64 Accessed : 1; // bit 8 UINT64 Ignored1 : 1; // bit 9 UINT64 ExecuteForUserMode : 1; // bit 10 UINT64 Ignored2 : 1; // bit 11 UINT64 PhysicalAddress : 36; // bit (N-1):12 or Page-Frame-Number UINT64 Reserved2 : 4; // bit 51:N UINT64 Ignored3 : 12; // bit 63:52 }Fields;}EPT_PDE, *PEPT_PDE;The last page is EPT which is described below.PTE will be :Note that we have EPTMemoryType, IgnorePAT, DirtyFlag, and SuppressVE in addition to the above pages.// See Table 28-6typedef union _EPT_PTE { ULONG64 All; struct { UINT64 Read : 1; // bit 0 UINT64 Write : 1; // bit 1 UINT64 Execute : 1; // bit 2 UINT64 EPTMemoryType : 3; // bit 5:3 (EPT Memory type) UINT64 IgnorePAT : 1; // bit 6 UINT64 Ignored1 : 1; // bit 7 UINT64 AccessedFlag : 1; // bit 8 UINT64 DirtyFlag : 1; // bit 9 UINT64 ExecuteForUserMode : 1; // bit 10 UINT64 Ignored2 : 1; // bit 11 UINT64 PhysicalAddress : 36; // bit (N-1):12 or Page-Frame-Number UINT64 Reserved : 4; // bit 51:N UINT64 Ignored3 : 11; // bit 62:52 UINT64 SuppressVE : 1; // bit 63 }Fields;}EPT_PTE, *PEPT_PTE;There are other types of implementing page walks (2 or 3 level paging), and if you set the 7th bit of PDPTE (Maps 1 GB) or the 7th bit of PDE (Maps 2 MB) so instead of implementing 4-level paging (like what we want to do for the rest of the topic) we set those bits but keep in mind that the corresponding tables are different. These tables are described in (Table 28-4. Format of an EPT Page-Directory Entry (PDE) that Maps a 2-MByte Page) and (Table 28-2. Format of an EPT Page-Directory-Pointer-Table Entry (PDPTE) that Maps a 1-GByte Page). SimpleVisor is an example of this implementation.An important note is almost all the above structures have a 36-bit Physical Address which means our hypervisor supports only 4-level paging. It is because every page table (and every EPT Page Table) consists of 512 entries which means you need 9 bits to select an entry, and as long as we have 4 level tables, we can’t use more than 36 (4 * 9) bits. Another method with a wider address range is not implemented in all major OS like Windows or Linux. I’ll describe EPT PML5E briefly later in this topic, but we don’t implement it in our hypervisor as it’s not widespread yet!By the way, N is the physical address width supported by the processor. CPUID with 80000008H in EAX gives you the supported width in EAX bits 7:0.Let’s see the rest of the code. The following code is the InitializeEptp function which is responsible for allocating and mapping EPTP.Note that the PAGED_CODE() macro ensures that the calling thread runs at an IRQL low enough to permit paging.UINT64InitializeEptp(){ PAGED_CODE(); ...First of all, allocate EPTP and put zeros on it. // // Allocate EPTP // PEPTP EPTPointer = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, POOLTAG); if (!EPTPointer) { return NULL; } RtlZeroMemory(EPTPointer, PAGE_SIZE);Now, we need a blank page for our EPT PML4 Table. // // Allocate EPT PML4 // PEPT_PML4E EptPml4 = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, POOLTAG); if (!EptPml4) { ExFreePoolWithTag(EPTPointer, POOLTAG); return NULL; } RtlZeroMemory(EptPml4, PAGE_SIZE);And another empty page for PDPT. // // Allocate EPT Page-Directory-Pointer-Table // PEPT_PDPTE EptPdpt = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, POOLTAG); if (!EptPdpt) { ExFreePoolWithTag(EptPml4, POOLTAG); ExFreePoolWithTag(EPTPointer, POOLTAG); return NULL; } RtlZeroMemory(EptPdpt, PAGE_SIZE);Of course, it’s true about Page Directory Table. // // Allocate EPT Page-Directory // PEPT_PDE EptPd = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, POOLTAG); if (!EptPd) { ExFreePoolWithTag(EptPdpt, POOLTAG); ExFreePoolWithTag(EptPml4, POOLTAG); ExFreePoolWithTag(EPTPointer, POOLTAG); return NULL; } RtlZeroMemory(EptPd, PAGE_SIZE);The last table is a blank page for EPT Page Table. // // Allocate EPT Page-Table // PEPT_PTE EptPt = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, POOLTAG); if (!EptPt) { ExFreePoolWithTag(EptPd, POOLTAG); ExFreePoolWithTag(EptPdpt, POOLTAG); ExFreePoolWithTag(EptPml4, POOLTAG); ExFreePoolWithTag(EPTPointer, POOLTAG); return NULL; } RtlZeroMemory(EptPt, PAGE_SIZE);Now that we have all of our pages available, let’s allocate two pages (2*4096) continuously because we need one of the pages for our RIP register to start and one page for our stack (RSP register). After that, we need two EPT Page Table Entries (PTEs) with permission to execute, read, and write. The physical address should be divided by 4096 (PAGE_SIZE) because if we dived a hex number by 4096 (0x1000), 12 digits from the right (which are zeros) would disappear, and these 12 digits are for choosing between 4096 bytes.By the way, we let stack be executable. That’s because, in a regular VM, we should put RWX on all pages. After all, it’s the responsibility of internal page tables to set or clear NX bit. We need to change them from EPT tables for special purposes (e.g., intercepting instruction fetch for a special page). Changing from EPT tables will lead to EPT-Violation; this way, we can intercept these events.The actual need is two pages, but we need to build page tables inside our guest software; thus, we allocate up to 10 pages.I’ll explain about intercepting pages from EPT later in this series. // // Setup PT by allocating two pages Continuously // We allocate two pages because we need 1 page for our RIP to start and 1 page for RSP 1 + 1 = 2 // const int PagesToAllocate = 10; UINT64 GuestMemory = ExAllocatePoolWithTag(NonPagedPool, PagesToAllocate * PAGE_SIZE, POOLTAG); RtlZeroMemory(GuestMemory, PagesToAllocate * PAGE_SIZE); for (size_t i = 0; i &lt; PagesToAllocate; i++) { EptPt[i].Fields.AccessedFlag = 0; EptPt[i].Fields.DirtyFlag = 0; EptPt[i].Fields.EPTMemoryType = 6; EptPt[i].Fields.Execute = 1; EptPt[i].Fields.ExecuteForUserMode = 0; EptPt[i].Fields.IgnorePAT = 0; EptPt[i].Fields.PhysicalAddress = (VirtualToPhysicalAddress(GuestMemory + (i * PAGE_SIZE)) / PAGE_SIZE); EptPt[i].Fields.Read = 1; EptPt[i].Fields.SuppressVE = 0; EptPt[i].Fields.Write = 1; }Note: EPTMemoryType can be either 0 (for uncached memory) or 6 (writeback) memory, and as we want our memory to be cacheable, so put 6 on it.The next table is PDE. PDE should point to the PTE base address, so we just put the address of the first entry from the EPT PTE as the physical address for Page Directory Entry. // // Setting up PDE // EptPd-&gt;Fields.Accessed = 0; EptPd-&gt;Fields.Execute = 1; EptPd-&gt;Fields.ExecuteForUserMode = 0; EptPd-&gt;Fields.Ignored1 = 0; EptPd-&gt;Fields.Ignored2 = 0; EptPd-&gt;Fields.Ignored3 = 0; EptPd-&gt;Fields.PhysicalAddress = (VirtualToPhysicalAddress(EptPt) / PAGE_SIZE); EptPd-&gt;Fields.Read = 1; EptPd-&gt;Fields.Reserved1 = 0; EptPd-&gt;Fields.Reserved2 = 0; EptPd-&gt;Fields.Write = 1;The next step is mapping PDPT. PDPT Entry should point to the first entry of Page-Directory. // // Setting up PDPTE // EptPdpt-&gt;Fields.Accessed = 0; EptPdpt-&gt;Fields.Execute = 1; EptPdpt-&gt;Fields.ExecuteForUserMode = 0; EptPdpt-&gt;Fields.Ignored1 = 0; EptPdpt-&gt;Fields.Ignored2 = 0; EptPdpt-&gt;Fields.Ignored3 = 0; EptPdpt-&gt;Fields.PhysicalAddress = (VirtualToPhysicalAddress(EptPd) / PAGE_SIZE); EptPdpt-&gt;Fields.Read = 1; EptPdpt-&gt;Fields.Reserved1 = 0; EptPdpt-&gt;Fields.Reserved2 = 0; EptPdpt-&gt;Fields.Write = 1;The last step is configuring PML4E, which points to the first entry of the PTPT. // // Setting up PML4E // EptPml4-&gt;Fields.Accessed = 0; EptPml4-&gt;Fields.Execute = 1; EptPml4-&gt;Fields.ExecuteForUserMode = 0; EptPml4-&gt;Fields.Ignored1 = 0; EptPml4-&gt;Fields.Ignored2 = 0; EptPml4-&gt;Fields.Ignored3 = 0; EptPml4-&gt;Fields.PhysicalAddress = (VirtualToPhysicalAddress(EptPdpt) / PAGE_SIZE); EptPml4-&gt;Fields.Read = 1; EptPml4-&gt;Fields.Reserved1 = 0; EptPml4-&gt;Fields.Reserved2 = 0; EptPml4-&gt;Fields.Write = 1;We’ve almost done! Just set up the EPTP for our VMCS by putting 0x6 as the memory type (which is writeback), and we walk four times, so the page walk length is 4-1=3, and the PML4 address is the physical address of the first entry in the PML4 table.I’ll explain the DirtyAndAcessEnabled field later in this topic. // // Setting up EPTP // EPTPointer-&gt;Fields.DirtyAndAceessEnabled = 1; EPTPointer-&gt;Fields.MemoryType = 6; // 6 = Write-back (WB) EPTPointer-&gt;Fields.PageWalkLength = 3; // 4 (tables walked) - 1 = 3 EPTPointer-&gt;Fields.PML4Address = (VirtualToPhysicalAddress(EptPml4) / PAGE_SIZE); EPTPointer-&gt;Fields.Reserved1 = 0; EPTPointer-&gt;Fields.Reserved2 = 0;And the last step. DbgPrint("[*] Extended Page Table Pointer allocated at %llx", EPTPointer); return EPTPointer;All the above page tables should be aligned to 4KByte boundaries, but as long as we allocate &gt;= PAGE_SIZE (One PFN record) so it’s automatically 4kb-aligned.Our implementation consists of 4 tables; therefore, the full layout is like this:Accessed and Dirty Flags in EPTPIn EPTP, we’ll decide whether enable accessed and dirty flags for EPT or not using the 6th bit of the extended-page-table pointer (EPTP). Setting this flag causes processor accesses to guest paging structure entries to be treated as writes.For any EPT paging-structure entry that is used during guest-physical-address translation, bit 8 is the accessed flag. For an EPT paging-structure entry that maps a page (as opposed to referencing another EPT paging structure), bit 9 is the dirty flag.Whenever the processor uses an EPT paging-structure entry as part of the guest-physical-address translation, it sets the accessed flag in that entry (if it is not already set).Whenever there is a write to a guest physical address, the processor sets the dirty flag (if it is not already set) in the EPT paging-structure entry that identifies the final physical address for the guest’s physical address (either an EPT PTE or an EPT paging-structure entry in which bit 7 is 1).These flags are “sticky”, meaning that, once set, the processor does not clear them; only software can clear them.5-Level EPT TranslationIntel suggests a new table in translation hierarchy, called PML5 which extends the EPT into a 5-layer table, and guest operating systems can use up to 57 bits for the virtual addresses, while the classic 4-level EPT is limited to translating 48-bit guest physical addresses. None of the modern OSs use this feature yet.PML5 is also applied to both EPT and regular paging mechanisms.Translation begins by identifying a 4-KByte naturally aligned EPT PML5 table. It is located at the physical address specified in bits 51:12 of EPTP. An EPT PML5 table comprises 512 64-bit entries (EPT PML5Es). An EPT PML5E is selected using the physical address defined as follows. Bits 63:52 are all 0. Bits 51:12 are from EPTP. Bits 11:3 are bits 56:48 of the guest physical address. Bits 2:0 are all 0. Because an EPT PML5E is identified using bits 56:48 of the guest’s physical address, it controls access to a 256-TByte region of the linear address space.The only difference is we should put PML5 physical address instead of the PML4 address in EPTP.For more information about 5-layer paging, take a look at this Intel documentation.ConclusionIn this part, we see how to initialize the Extended Page Table (EPT) and map the guest’s physical address to the host’s physical address; then, we build the EPTP based on the allocated addresses.The future part would be about building the VMCS and implementing other VMX instructions and functionalities.The fifth part is also available here.Have a good time!References[1] Vol 3C - 28.2 THE EXTENDED PAGE TABLE MECHANISM (EPT) (https://software.intel.com/en-us/articles/intel-sdm)[2] Performance Evaluation of Intel EPT Hardware Assist (https://www.vmware.com/pdf/Perf_ESX_Intel-EPT-eval.pdf)[3] Second Level Address Translation (https://en.wikipedia.org/wiki/Second_Level_Address_Translation)[4] Memory Virtualization (http://www.cs.nthu.edu.tw/~ychung/slides/Virtualization/VM-Lecture-2-2-SystemVirtualizationMemory.pptx)[5] Best Practices for Paravirtualization Enhancements from Intel® Virtualization Technology: EPT and VT-d (https://software.intel.com/en-us/articles/best-practices-for-paravirtualization-enhancements-from-intel-virtualization-technology-ept-and-vt-d)[6] 5-Level Paging and 5-Level EPT (https://software.intel.com/sites/default/files/managed/2b/80/5-level_paging_white_paper.pdf)[7] Xen Summit November 2007 - Jun Nakajima (http://www-archive.xenproject.org/files/xensummit_fall07/12_JunNakajima.pdf)[8] gipervizor against rutkitov: as it works (http://developers-club.com/posts/133906/)[9] Intel SGX Explained (https://www.semanticscholar.org/paper/Intel-SGX-Explained-Costan-Devadas/2d7f3f4ca3fbb15ae04533456e5031e0d0dc845a)[10] Intel VT-x (https://github.com/tnballo/notebook/wiki/Intel-VTx)[11] Introduction to IA-32e hardware paging (https://www.triplefault.io/2017/07/introduction-to-ia-32e-hardware-paging.html)" }, { "title": "Hypervisor From Scratch – Part 3: Setting up Our First Virtual Machine", "url": "/topics/hypervisor-from-scratch-part-3/", "categories": "cpu, hypervisor, tutorials", "tags": "hypervisor, creating-vmm, initiating-vmx-operation, irp_mj_device_control, method_buffered, method_in_direct, method_niether, method_out_direct, vmcs, vmcs-region, vmm, vmx-operation, vmxon, vmxon-region", "date": "2018-09-15 00:00:00 +0000", "snippet": "If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous practical examples on how to utilize hypervisors for reverse engineering.IntroductionThis is the third part of the tutorial “Hypervisor From Scratch”. In this part, we’ll continue our journey toward learning hypervisors and how to start creating our custom VMM. In the previous part, we learned how to make WDK drivers that handle user-mode requests and enable the VMX bit in our processor. In this part, we extend our driver and add VMX functionalities to our VMM. At last, we use different VT-x instructions in the VMM.Table of Contents Introduction Table of Contents Overview Interacting with the driver from user-mode Buffer Descriptions for I/O Control Codes METHOD_BUFFERED METHOD_IN_DIRECT and METHOD_OUT_DIRECT METHOD_NIETHER IOCTL Structure IOCTL Dispatcher Per Processor Configuration Setting Affinity Converting physical and virtual addresses Check VMX support in the kernel VMXON Region Allocating VMXON Region Virtual-Machine Control Data Structures (VMCS) Initializing VMCS Region VMXOFF Instruction Testing VMM Conclusion ReferencesOverviewIn this part, we demonstrate how to interact with VMM from Windows user-mode (IOCTL Dispatcher), then we solve the problems with the affinity and running code in a particular core. Finally, we get familiar with initializing VMXON Regions and VMCS Regions, then we load our hypervisor into each core and implement our custom functions to work with hypervisor instructions and many more things related to Virtual-Machine Control Data Structures (VMCS).Some of the implementations are derived from HyperBone (Minimalistic VT-X hypervisor with hooks), HyperPlatform by Satoshi Tanda and hvpp which is amazing work by my friend Petr Beneš.The full source code of this tutorial is available on :[https://github.com/SinaKarvandi/Hypervisor-From-Scratch]Interacting with the driver from user-modeThe most important function for us in IRP MJ functions is DrvIoctlDispatcher or (IRP_MJ_DEVICE_CONTROL) Major Function, and that’s because this function can be called from user-mode with a particular IOCTL number, which means we can have a special code in our driver and implement a unique functionality corresponding this code, then by knowing the code (from user-mode) we can ask our driver to perform the request, so this way we can request a certain functionality from the kernel.Buffer Descriptions for I/O Control CodesAs explained above, IOCTL codes request a certain functionality from the kernel-mode. It’s clear that in most cases, we need to transfer a buffer (structure) to the kernel, which shows different details about our request. Thus, we need to copy the buffer from the user-mode and pass it to the kernel-mode routines.There are several methods in which Windows copies the buffer of the user-mode codes to the kernel for dispatching IOCTs. METHOD_BUFFERED METHOD_IN_DIRECT METHOD_OUT_DIRECT METHOD_NIETHERThe difference is where buffers transfer between user-mode and kernel-mode. Let’s see each of them in detail.METHOD_BUFFEREDFor METHOD_BUFFERED, the pointer to the user-mode buffer is available at Irp-&gt;AssociatedIrp.SystemBuffer, and we can put the output buffer to the same address (Irp-&gt;AssociatedIrp.SystemBuffer).This method is typically used for transferring small amounts of data per request. Most I/O control codes for device and intermediate drivers use this type as Windows copies the user-mode buffer to the kernel-mode and the kernel-mode buffer to the user-mode.METHOD_IN_DIRECT and METHOD_OUT_DIRECTFor these methods, the pointer to the user-mode buffer is available at Irp-&gt;AssociatedIrp.SystemBuffer.This type is generally used for reading or writing large amounts of data that must be transferred fast as it won’t copy the data and instead shares the pages.The METHOD_IN_DIRECT is specified if the caller pass data to the driver, and the METHOD_OUT_DIRECT is selected if the caller will receive data from the driver.METHOD_NIETHERThe input buffer address is specified by Parameters.DeviceIoControl.Type3InputBuffer in the driver’s IO_STACK_LOCATION structure, and the output buffer(to the user-mode) is specified by Irp-&gt;UserBuffer.This method is neither buffered nor direct I/O. The I/O manager does not provide any system buffers, and the IRP provides the user-mode virtual addresses of the input and output buffers without validating or mapping them.IOCTL StructureWe should specify all of the above transferring types into the following structure.There is a convention for defining IOCTLs as mentioned here. The IOCTL is a 32-bit number. The first two low bits represent the “transfer type”, which can be METHOD_OUT_DIRECT, METHOD_IN_DIRECT, METHOD_BUFFERED, or METHOD_NEITHER.The next set of bits from 2 to 13 define the “Function Code”. The high bit is referred to as the “custom bit”. This is used to determine user-defined IOCTLs versus system defined. This means that function codes 0x800 and greater are customs defined for Windows Messages.The next two bits define the access required to issue the IOCTL. This is how the I/O Manager can reject IOCTL requests if the handle has not been opened with the correct access. The access types are such as FILE_READ_DATA, FILE_WRITE_DATA, etc.The last bits represent the device type the IOCTLs are written for. The high bit again represents user-defined values.We can use the following defined macro to create our IOCTL code.#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)For example, the following IOCTL code can be defined.#define IOCTL_TEST \\ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)IOCTL DispatcherNow let’s implement our functions for dispatching IOCTL codes.Note that the PAGED_CODE() macro ensures that the calling thread runs at an IRQL low enough to permit paging, this macro is used to ensure that paging is enabled, for example, the current execution is not at DISPATCH_LEVEL. Don’t worry; we’ll discuss IRQL in detail in the future parts.The next step is to check the input buffer and the output buffer’s length. We’ll check it because we need to ensure that the user provides a buffer for the kernel and expects a buffer to be received. The following code gets the input and output buffer length from the IO_STACK_LOCATION. IrpStack = IoGetCurrentIrpStackLocation(Irp); InBufLength = IrpStack-&gt;Parameters.DeviceIoControl.InputBufferLength; OutBufLength = IrpStack-&gt;Parameters.DeviceIoControl.OutputBufferLength; if (!InBufLength || OutBufLength &lt; DataLen) { NtStatus = STATUS_INVALID_PARAMETER; goto End; }Then we have to use the switch-case through the IOCTL code. switch (IrpStack-&gt;Parameters.DeviceIoControl.IoControlCode) { case IOCTL_SIOCTL_METHOD_BUFFERED: ... break; case IOCTL_SIOCTL_METHOD_NEITHER: ... break; case IOCTL_SIOCTL_METHOD_IN_DIRECT: ... break; case IOCTL_SIOCTL_METHOD_OUT_DIRECT: ... break; default: // // The specified I/O control code is unrecognized by this driver. // NtStatus = STATUS_INVALID_DEVICE_REQUEST; DbgPrint("ERROR: unrecognized IOCTL %x\\n", IrpStack-&gt;Parameters.DeviceIoControl.IoControlCode); break; }The PrintIrpInfo is like this :VOID PrintIrpInfo(PIRP Irp){ PIO_STACK_LOCATION IrpStack; IrpStack = IoGetCurrentIrpStackLocation(Irp); PAGED_CODE(); DbgPrint("\\tIrp-&gt;AssociatedIrp.SystemBuffer = 0x%p\\n", Irp-&gt;AssociatedIrp.SystemBuffer); DbgPrint("\\tIrp-&gt;UserBuffer = 0x%p\\n", Irp-&gt;UserBuffer); DbgPrint("\\tIrpStack-&gt;Parameters.DeviceIoControl.Type3InputBuffer = 0x%p\\n", IrpStack-&gt;Parameters.DeviceIoControl.Type3InputBuffer); DbgPrint("\\tIrpStack-&gt;Parameters.DeviceIoControl.InputBufferLength = %d\\n", IrpStack-&gt;Parameters.DeviceIoControl.InputBufferLength); DbgPrint("\\tIrpStack-&gt;Parameters.DeviceIoControl.OutputBufferLength = %d\\n", IrpStack-&gt;Parameters.DeviceIoControl.OutputBufferLength); return;}If you remember from the previous part where we created a handle (HANDLE) using CreateFile, now we can use the DeviceIoControl with the previous handle and call DrvIoctlDispatcher or (IRP_MJ_DEVICE_CONTROL) along with our provided buffer in the kernel. char OutputBuffer[1000]; char InputBuffer[1000]; ULONG BytesReturned; BOOL Result; // // Performing METHOD_BUFFERED // StringCbCopy(InputBuffer, sizeof(InputBuffer), "This String is from User Application; using METHOD_BUFFERED"); printf("\\nCalling DeviceIoControl METHOD_BUFFERED:\\n"); memset(OutputBuffer, 0, sizeof(OutputBuffer)); Result = DeviceIoControl(Handle, (DWORD)IOCTL_SIOCTL_METHOD_BUFFERED, &amp;InputBuffer, (DWORD)strlen(InputBuffer) + 1, &amp;OutputBuffer, sizeof(OutputBuffer), &amp;BytesReturned, NULL); if (!Result) { printf("Error in DeviceIoControl : %d", GetLastError()); return false; } printf(" OutBuffer (%d): %s\\n", BytesReturned, OutputBuffer);For further reading, there is an old, yet great topic here which describes the different types of IOCTL dispatching.We’re done with WDK basics! It’s time to see how we can use Windows to build our VMM.Per Processor ConfigurationAffinity to a special logical processor is one of the main considerations when working with the hypervisor.In my Intel Core i7 6820HQ, I have four physical cores, and each core can run two threads simultaneously (due to the hyper-threading); thus, we have eight logical processors and, of course, eight sets of all the registers (including general purpose registers and MSR registers) and more importantly, eight sets of VMCSs and VMXON Regions, etc. so we should configure our VMM to work on eight logical processors.Setting AffinityTo get the count of logical processors, we can use KeQueryActiveProcessorCount(0). Then we should pass a KAFFINITY mask to the KeSetSystemAffinityThread, which sets the system affinity of the current thread.KAFFINITY mask can be configured using a simple power function:intMathPower(int Base, int Exponent){ int Result = 1; for (;;) { if (Exponent &amp; 1) { Result *= Base; } Exponent &gt;&gt;= 1; if (!Exponent) { break; } Base *= Base; } return Result;}After that, we should use the following code to change the affinity of the processor and run our code in all the logical cores separately: KAFFINITY AffinityMask; for (size_t i = 0; i &lt; KeQueryActiveProcessors(); i++) { AffinityMask = MathPower(2, i); KeSetSystemAffinityThread(AffinityMask); DbgPrint("====================================================="); DbgPrint("Current thread is executing in %d th logical processor.", i); // run code here }This way, we can run our codes in the different logical cores. Now, let’s see other essential functionalities we need for our hypervisor.Converting physical and virtual addressesVMXON Regions and VMCS Regions (see below) use the physical address as the operand to VMXON and VMPTRLD instructions, so we should create functions to convert Virtual Address to Physical address:UINT64VirtualToPhysicalAddress(void * Va){ return MmGetPhysicalAddress(Va).QuadPart;}And as long as we can’t directly use physical addresses for our modifications in protected-mode, we have to convert Physical addresses to Virtual addresses too.UINT64PhysicalToVirtualAddress(UINT64 Pa){ PHYSICAL_ADDRESS PhysicalAddr; PhysicalAddr.QuadPart = Pa; return MmGetVirtualForPhysical(PhysicalAddr);}Check VMX support in the kernelIn the previous part, we query about the presence of hypervisor from user-mode, but we should also consider checking about hypervisor from kernel-mode too. This reduces the possibility of getting kernel errors in the future, or there might be something that disables the hypervisor using the lock bit. By the way, the following code checks IA32_FEATURE_CONTROL MSR (MSR address 3AH) to see if the lock bit is set or not.BOOLEANIsVmxSupported(){ CPUID Data = {0}; // // Check for the VMX bit // __cpuid((int *)&amp;Data, 1); if ((Data.ecx &amp; (1 &lt;&lt; 5)) == 0) return FALSE; IA32_FEATURE_CONTROL_MSR Control = {0}; Control.All = __readmsr(MSR_IA32_FEATURE_CONTROL); // // BIOS lock check // if (Control.Fields.Lock == 0) { Control.Fields.Lock = TRUE; Control.Fields.EnableVmxon = TRUE; __writemsr(MSR_IA32_FEATURE_CONTROL, Control.All); } else if (Control.Fields.EnableVmxon == FALSE) { DbgPrint("[*] VMX locked off in BIOS"); return FALSE; } return TRUE;}The structures used in the above function are declared like this:typedef union _IA32_FEATURE_CONTROL_MSR{ ULONG64 All; struct { ULONG64 Lock : 1; // [0] ULONG64 EnableSMX : 1; // [1] ULONG64 EnableVmxon : 1; // [2] ULONG64 Reserved2 : 5; // [3-7] ULONG64 EnableLocalSENTER : 7; // [8-14] ULONG64 EnableGlobalSENTER : 1; // [15] ULONG64 Reserved3a : 16; // ULONG64 Reserved3b : 32; // [16-63] } Fields;} IA32_FEATURE_CONTROL_MSR, *PIA32_FEATURE_CONTROL_MSR;typedef struct _CPUID{ int eax; int ebx; int ecx; int edx;} CPUID, *PCPUID;VMXON RegionSeveral regions are used in the VMX to handle the virtual machine state. In this part, we will walk through the VMXON Region and the VMCS Region.Before executing VMXON, we should allocate a naturally aligned 4-KByte region of memory that our logical processor will use it to support VMX operation. This region is called the VMXON Region. The address of the VMXON Region (the VMXON pointer) is provided in an operand to VMXON instruction.A VMM should use different VMXON Regions for each logical processor; otherwise, the behavior is “undefined”.Please note that VMX operation requires that the following bits be 1 in VMX operation: CR0.PE, CR0.NE, CR0.PG, and CR4.VMXE. The restrictions on CR0.PE and CR0.PG implies that VMX operation is supported only in paged protected-mode. Therefore, the guest software cannot be run in unpaged protected-mode or in real-address mode.Now that we are configuring the hypervisor, we should have a global variable that describes the state of our virtual machine. The following structure is created for this purpose. We currently have two fields called (VMXON_REGION and VMCS_REGION), but we will add new fields and enhance this structure in the future.typedef struct _VIRTUAL_MACHINE_STATE{ UINT64 VmxonRegion; // VMXON region UINT64 VmcsRegion; // VMCS region} VIRTUAL_MACHINE_STATE, *PVIRTUAL_MACHINE_STATE;And, of course, a global variable:extern VIRTUAL_MACHINE_STATE* g_GuestState;Allocating VMXON RegionThe following function (in “Memory.c”) to allocate VMXON Region and execute VMXON instruction using the allocated region’s pointer.BOOLEANAllocateVmxonRegion(IN VIRTUAL_MACHINE_STATE * GuestState){ // at IRQL &gt; DISPATCH_LEVEL memory allocation routines don't work if (KeGetCurrentIrql() &gt; DISPATCH_LEVEL) KeRaiseIrqlToDpcLevel(); PHYSICAL_ADDRESS PhysicalMax = {0}; PhysicalMax.QuadPart = MAXULONG64; int VMXONSize = 2 * VMXON_SIZE; BYTE * Buffer = MmAllocateContiguousMemory(VMXONSize + ALIGNMENT_PAGE_SIZE, PhysicalMax); // Allocating a 4-KByte Contigous Memory region PHYSICAL_ADDRESS Highest = {0}, Lowest = {0}; Highest.QuadPart = ~0; // BYTE* Buffer = MmAllocateContiguousMemorySpecifyCache(VMXONSize + ALIGNMENT_PAGE_SIZE, Lowest, Highest, Lowest, MmNonCached); if (Buffer == NULL) { DbgPrint("[*] Error : Couldn't Allocate Buffer for VMXON Region."); return FALSE; // ntStatus = STATUS_INSUFFICIENT_RESOURCES; } UINT64 PhysicalBuffer = VirtualToPhysicalAddress(Buffer); // zero-out memory RtlSecureZeroMemory(Buffer, VMXONSize + ALIGNMENT_PAGE_SIZE); UINT64 AlignedPhysicalBuffer = (BYTE *)((ULONG_PTR)(PhysicalBuffer + ALIGNMENT_PAGE_SIZE - 1) &amp; ~(ALIGNMENT_PAGE_SIZE - 1)); UINT64 AlignedVirtualBuffer = (BYTE *)((ULONG_PTR)(Buffer + ALIGNMENT_PAGE_SIZE - 1) &amp; ~(ALIGNMENT_PAGE_SIZE - 1)); DbgPrint("[*] Virtual allocated buffer for VMXON at %llx", Buffer); DbgPrint("[*] Virtual aligned allocated buffer for VMXON at %llx", AlignedVirtualBuffer); DbgPrint("[*] Aligned physical buffer allocated for VMXON at %llx", AlignedPhysicalBuffer); // get IA32_VMX_BASIC_MSR RevisionId IA32_VMX_BASIC_MSR basic = {0}; basic.All = __readmsr(MSR_IA32_VMX_BASIC); DbgPrint("[*] MSR_IA32_VMX_BASIC (MSR 0x480) Revision Identifier %llx", basic.Fields.RevisionIdentifier); // Changing Revision Identifier *(UINT64 *)AlignedVirtualBuffer = basic.Fields.RevisionIdentifier; int Status = __vmx_on(&amp;AlignedPhysicalBuffer); if (Status) { DbgPrint("[*] VMXON failed with status %d\\n", Status); return FALSE; } g_GuestState-&gt;VmxonRegion = AlignedPhysicalBuffer; return TRUE;}Let’s explain the above function. In the above function, we used MmAllocateContiguousMemory to allocate a contiguous and aligned page. We can also use MmAllocateContiguousMemorySpecifyCache to specify the cache type for the allocated memory.You can read this link to learn about different types of memory caches.To ensure proper behavior in VMX operation, we should maintain the VMCS region and related structures in writeback cacheable memory. Alternatively, we may map any of these regions or structures with the UC (uncached) memory type. Doing so is strongly discouraged unless necessary as it will cause the performance of transitions using those structures to suffer significantly.Writeback is a storage method in which data is written into the cache every time a change occurs but is written into the corresponding location in the main memory only at specified intervals or under certain conditions. Being cachable or not cachable can be determined from the cache disable bit in paging structures (PTE) and in the Memory type range register (MTRR), which is described thoroughly in the 7th part of this series.By the way, we allocated 8192 bytes because there is no guarantee that Windows allocates the aligned memory so that we can find a piece of 4096 bytes aligned in 8196 bytes. (by aligning, I mean the physical address should be divisible by 4096 without any reminder).In my experience, the MmAllocateContiguousMemory allocation is always aligned. Maybe it is because every page in PFN is allocated by 4096 bytes, and as long as we need 4096 bytes, thus it’s aligned.If you are interested in Page Frame Number (PFN), you can read Inside Windows Page Frame Number (PFN) – Part 1 and Inside Windows Page Frame Number (PFN) – Part 2.Now we should convert the allocated memory address to its physical address and make sure it’s aligned. PHYSICAL_ADDRESS PhysicalMax = {0}; PhysicalMax.QuadPart = MAXULONG64; int VMXONSize = 2 * VMXON_SIZE; BYTE * Buffer = MmAllocateContiguousMemory(VMXONSize + ALIGNMENT_PAGE_SIZE, PhysicalMax); // Allocating a 4-KByte Contigous Memory regionMemory that MmAllocateContiguousMemory allocates is uninitialized. The kernel-mode driver must first set this memory to zero, and we use RtlSecureZeroMemory for this purpose. UINT64 PhysicalBuffer = VirtualToPhysicalAddress(Buffer); // zero-out memory RtlSecureZeroMemory(Buffer, VMXONSize + ALIGNMENT_PAGE_SIZE); UINT64 AlignedPhysicalBuffer = (BYTE *)((ULONG_PTR)(PhysicalBuffer + ALIGNMENT_PAGE_SIZE - 1) &amp; ~(ALIGNMENT_PAGE_SIZE - 1)); UINT64 AlignedVirtualBuffer = (BYTE *)((ULONG_PTR)(Buffer + ALIGNMENT_PAGE_SIZE - 1) &amp; ~(ALIGNMENT_PAGE_SIZE - 1)); DbgPrint("[*] Virtual allocated buffer for VMXON at %llx", Buffer); DbgPrint("[*] Virtual aligned allocated buffer for VMXON at %llx", AlignedVirtualBuffer); DbgPrint("[*] Aligned physical buffer allocated for VMXON at %llx", AlignedPhysicalBuffer);From Intel’s manual (24.11.5 VMXON Region ): Before executing VMXON, software should write the VMCS revision identifier to the VMXON region. (Specifically, it should write the 31-bit VMCS revision identifier to bits 30:0 of the first 4 bytes of the VMXON region; bit 31 should be cleared to 0.) It need not initialize the VMXON region in any other way. Software should use a separate region for each logical processor and should not access or modify the VMXON region of a logical processor between the execution of VMXON and VMXOFF on that logical processor. Doing otherwise may lead to unpredictable behavior.So let’s get the Revision Identifier from IA32_VMX_BASIC_MSR and write it to the VMXON Region. // get IA32_VMX_BASIC_MSR RevisionId IA32_VMX_BASIC_MSR basic = {0}; basic.All = __readmsr(MSR_IA32_VMX_BASIC); DbgPrint("[*] MSR_IA32_VMX_BASIC (MSR 0x480) Revision Identifier %llx", basic.Fields.RevisionIdentifier); // Changing Revision Identifier *(UINT64 *)AlignedVirtualBuffer = basic.Fields.RevisionIdentifier;The last part is used for executing VMXON instruction. int Status = __vmx_on(&amp;AlignedPhysicalBuffer); if (Status) { DbgPrint("[*] VMXON failed with status %d\\n", Status); return FALSE; } g_GuestState-&gt;VmxonRegion = AlignedPhysicalBuffer;__vmx_on is the intrinsic function for executing VMXON. The status code shows different meanings. Value Meaning 0 The operation succeeded. 1 The operation failed with extended status available in the VM-instruction error field of the current VMCS. 2 The operation failed without status available. If we set the VMXON Region using VMXON and it fails, then the status is equal to 1. If there isn’t any VMCS, the status is equal to 2, and if the operation was successful, the status is zero. We get errors if we execute the above code twice without executing VMXOFF.Now, the VMXON Region is ready, and we’re good to go.Virtual-Machine Control Data Structures (VMCS)A logical processor uses virtual-machine control data structure (VMCS) while it’s in VMX operation. These manage transitions into and out of VMX non-root operation (VM entries and VM exits) as well as processor behavior in VMX non-root operation. This structure is manipulated by VMCLEAR, VMPTRLD, VMREAD, and VMWRITE instructions.The above picture illustrates the lifecycle of VMX operation in the VMCS Region.Initializing VMCS RegionA VMM should use different VMCS Regions, so we need to set logical processor affinity and run our initialization routine multiple times.The location where the VMCS located is called the “VMCS Region”.VMCS Region is a 4 Kbyte (bits 11:0 must be zero) Must be aligned to the 4KB boundaryThis pointer must not set bits beyond the processor’s physical-address width (we can determine a processor’s physical-address width by executing CPUID with 80000008H in EAX. The physical-address width is returned in bits 7:0 of EAX.)There might be several VMCSs simultaneously in a processor, but just one of them is currently active, and the VMLAUNCH, VMREAD, VMRESUME, and VMWRITE instructions operate only on the current VMCS.Using VMPTRLD sets the current VMCS on a logical processor.The memory operand of the VMCLEAR instruction is also the address of a VMCS. After executing the instruction, VMCS is neither active nor current on the logical processor. If the VMCS had been current on the logical processor, the logical processor no longer has a current VMCS.VMPTRST is responsible to give the current VMCS pointer it stores the value FFFFFFFFFFFFFFFFH if there is no current VMCS.The launching state of a VMCS determines which VM-entry instruction should be used with that VMCS. The VMLAUNCH instruction requires a VMCS whose launch state is “clear”; the VMRESUME instruction requires a VMCS whose launch state is “launched”. A logical processor maintains a VMCS’s launch state in the corresponding VMCS region.If the launch state of the current VMCS is “clear”, successful execution of the VMLAUNCH instruction changes the launch state to “launched”.The memory operand of the VMCLEAR instruction is the address of a VMCS. After execution of the instruction, the launch state of that VMCS is “clear”.There are no other ways to modify the launch state of a VMCS (it cannot be modified using VMWRITE), and there is no direct way to discover it (it cannot be read using VMREAD).The following picture illustrates the contents of a VMCS Region.The following code is responsible for allocating VMCS Region :BOOLEANAllocateVmcsRegion(IN VIRTUAL_MACHINE_STATE * GuestState){ // // at IRQL &gt; DISPATCH_LEVEL memory allocation routines don't work // if (KeGetCurrentIrql() &gt; DISPATCH_LEVEL) KeRaiseIrqlToDpcLevel(); PHYSICAL_ADDRESS PhysicalMax = {0}; PhysicalMax.QuadPart = MAXULONG64; int VMCSSize = 2 * VMCS_SIZE; BYTE * Buffer = MmAllocateContiguousMemory(VMCSSize + ALIGNMENT_PAGE_SIZE, PhysicalMax); // Allocating a 4-KByte Contigous Memory region PHYSICAL_ADDRESS Highest = {0}, Lowest = {0}; Highest.QuadPart = ~0; // BYTE* Buffer = MmAllocateContiguousMemorySpecifyCache(VMXONSize + ALIGNMENT_PAGE_SIZE, Lowest, Highest, Lowest, MmNonCached); UINT64 PhysicalBuffer = VirtualToPhysicalAddress(Buffer); if (Buffer == NULL) { DbgPrint("[*] Error : Couldn't Allocate Buffer for VMCS Region."); return FALSE; // ntStatus = STATUS_INSUFFICIENT_RESOURCES; } // zero-out memory RtlSecureZeroMemory(Buffer, VMCSSize + ALIGNMENT_PAGE_SIZE); UINT64 AlignedPhysicalBuffer = (BYTE *)((ULONG_PTR)(PhysicalBuffer + ALIGNMENT_PAGE_SIZE - 1) &amp; ~(ALIGNMENT_PAGE_SIZE - 1)); UINT64 AlignedVirtualBuffer = (BYTE *)((ULONG_PTR)(Buffer + ALIGNMENT_PAGE_SIZE - 1) &amp; ~(ALIGNMENT_PAGE_SIZE - 1)); DbgPrint("[*] Virtual allocated buffer for VMCS at %llx", Buffer); DbgPrint("[*] Virtual aligned allocated buffer for VMCS at %llx", AlignedVirtualBuffer); DbgPrint("[*] Aligned physical buffer allocated for VMCS at %llx", AlignedPhysicalBuffer); // get IA32_VMX_BASIC_MSR RevisionId IA32_VMX_BASIC_MSR basic = {0}; basic.All = __readmsr(MSR_IA32_VMX_BASIC); DbgPrint("[*] MSR_IA32_VMX_BASIC (MSR 0x480) Revision Identifier %llx", basic.Fields.RevisionIdentifier); // Changing Revision Identifier *(UINT64 *)AlignedVirtualBuffer = basic.Fields.RevisionIdentifier; int Status = __vmx_vmptrld(&amp;AlignedPhysicalBuffer); if (Status) { DbgPrint("[*] VMCS failed with status %d\\n", Status); return FALSE; } g_GuestState-&gt;VmcsRegion = AlignedPhysicalBuffer; return TRUE;}The above code is exactly the same as VMXON Region except for __vmx_vmptrld instead of __vmx_on, __vmx_vmptrld is the intrinsic function for VMPTRLD instruction.In VMCS, we should find the Revision Identifier from MSR_IA32_VMX_BASIC and write it in VMCS Region before executing VMPTRLD.The MSR_IA32_VMX_BASIC is defined as below.typedef union _IA32_VMX_BASIC_MSR{ ULONG64 All; struct { ULONG32 RevisionIdentifier : 31; // [0-30] ULONG32 Reserved1 : 1; // [31] ULONG32 RegionSize : 12; // [32-43] ULONG32 RegionClear : 1; // [44] ULONG32 Reserved2 : 3; // [45-47] ULONG32 SupportedIA64 : 1; // [48] ULONG32 SupportedDualMoniter : 1; // [49] ULONG32 MemoryType : 4; // [50-53] ULONG32 VmExitReport : 1; // [54] ULONG32 VmxCapabilityHint : 1; // [55] ULONG32 Reserved3 : 8; // [56-63] } Fields;} IA32_VMX_BASIC_MSR, *PIA32_VMX_BASIC_MSR;VMXOFF InstructionAfter configuring the above regions, now it’s time to think about DrvClose when the user-mode application no longer maintains the handle to the driver. At this time, we should terminate VMX and free every memory that we allocated before.The following function is responsible for executing VMXOFF and then calling MmFreeContiguousMemory to free the allocated memory :VOIDTerminateVmx(){ DbgPrint("\\n[*] Terminating VMX...\\n"); KAFFINITY AffinityMask; for (size_t i = 0; i &lt; ProcessorCounts; i++) { AffinityMask = MathPower(2, i); KeSetSystemAffinityThread(AffinityMask); DbgPrint("\\t\\tCurrent thread is executing in %d th logical processor.", i); __vmx_off(); MmFreeContiguousMemory(PhysicalToVirtualAddress(g_GuestState[i].VmxonRegion)); MmFreeContiguousMemory(PhysicalToVirtualAddress(g_GuestState[i].VmcsRegion)); } DbgPrint("[*] VMX Operation turned off successfully. \\n");}Remember to convert VMXON and VMCS Regions to virtual addresses because MmFreeContiguousMemory accepts virtual addresses; otherwise, it leads to a BSOD.Ok, It’s almost done!Testing VMMLet’s create a test case for our code, first a function for initiating VMXON and VMCS Regions through all logical processors.VIRTUAL_MACHINE_STATE * g_GuestState;int ProcessorCounts;BOOLEANInitializeVmx(){ if (!IsVmxSupported()) { DbgPrint("[*] VMX is not supported in this machine !"); return FALSE; } ProcessorCounts = KeQueryActiveProcessorCount(0); g_GuestState = ExAllocatePoolWithTag(NonPagedPool, sizeof(VIRTUAL_MACHINE_STATE) * ProcessorCounts, POOLTAG); DbgPrint("\\n=====================================================\\n"); KAFFINITY AffinityMask; for (size_t i = 0; i &lt; ProcessorCounts; i++) { AffinityMask = MathPower(2, i); KeSetSystemAffinityThread(AffinityMask); DbgPrint("\\t\\tCurrent thread is executing in %d th logical processor.", i); // // Enabling VMX Operation // AsmEnableVmxOperation(); DbgPrint("[*] VMX Operation Enabled Successfully !"); AllocateVmxonRegion(&amp;g_GuestState[i]); AllocateVmcsRegion(&amp;g_GuestState[i]); DbgPrint("[*] VMCS Region is allocated at ===============&gt; %llx", g_GuestState[i].VmcsRegion); DbgPrint("[*] VMXON Region is allocated at ===============&gt; %llx", g_GuestState[i].VmxonRegion); DbgPrint("\\n=====================================================\\n"); } return TRUE;}The above function should be called from IRP MJ CREATE, so let’s modify our DrvCreate to :NTSTATUS DrvCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ DbgPrint("[*] DrvCreate Called !"); if (InitializeVmx()) { DbgPrint("[*] VMX Initiated Successfully."); } Irp-&gt;IoStatus.Status = STATUS_SUCCESS; Irp-&gt;IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS;}And modify DrvClose to :NTSTATUSDrvClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ DbgPrint("[*] DrvClose Called !"); // // executing VMXOFF on every logical processor // TerminateVmx(); Irp-&gt;IoStatus.Status = STATUS_SUCCESS; Irp-&gt;IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS;}Now, run the code. In the case of creating the handle (You can see that our regions were allocated successfully).And when we call CloseHandle from user mode:ConclusionIn this part, we learned about different types of IOCTL Dispatching. We saw different functions in Windows to manage our VMM, and we initialized the VMXON Regions and VMCS Regions, then terminated them.In the future, we’ll focus on Exteneded Page Table (EPT), VMCS, and different actions that can be performed in VMCS Regions to control our guest software.The fourth part is also available [here].(https://rayanfam.com/topics/hypervisor-from-scratch-part-4/).Note: Remember that hypervisors change over time because new features are added to the operating systems or new technologies are used. For example, updates to Meltdown &amp; Spectre have made a lot of changes to the hypervisors. So, if you want to use Hypervisor From Scratch in your projects, research, or whatever, you should use the HyperDbg drivers. HyperDbg is actively maintained, stable, and reliable, ensuring you avoid the errors and instability problems that can arise from using older parts of the tutorial series.References[1] Intel® 64 and IA-32 architectures software developer’s manual combined volumes 3 (https://software.intel.com/en-us/articles/intel-sdm)[2] Windows Driver Samples (https://github.com/Microsoft/Windows-driver-samples)[3] Driver Development Part 2: Introduction to Implementing IOCTLs (https://www.codeproject.com/Articles/9575/Driver-Development-Part-2-Introduction-to-Implemen)[3] Hyperplatform (https://github.com/tandasat/HyperPlatform)[4] PAGED_CODE macro (https://technet.microsoft.com/en-us/ff558773(v=vs.96))[5] HVPP (https://github.com/wbenny/hvpp)[6] HyperBone Project (https://github.com/DarthTon/HyperBone)[7] Memory Caching Types (https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ne-wdm-_memory_caching_type)[8] What is writeback cache? (https://whatis.techtarget.com/definition/write-back)[9] Buffer Descriptions for I/O Control Codes (https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/buffer-descriptions-for-i-o-control-codes)[10] Defining I/O Control Codes (https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/defining-i-o-control-codes)" }, { "title": "Using Intel's Streaming SIMD Extensions 3 (MONITOR MWAIT) As A Kernel Debugging Trick", "url": "/topics/using-intels-streaming-simd-extensions-3-monitormwait-as-a-kernel-debugging-trick/", "categories": "cpu, debugging, kernel-mode", "tags": "intel-mon-feature, intel-streaming-simd-extensions-3, intel-synchronization-agent, monitor-mwait-instructions", "date": "2018-09-08 00:00:00 +0000", "snippet": "IntroductionMONITOR and MWAIT are using when the CPU needs to be stopped executing the instruction and enter an implementation-dependent optimized state until some special event happens.MONITOR sets up an address range used to monitor write-back stores while MWAIT enables a logical processor to enter into an optimized state while waiting for a write-back store to the address range set up by MONITOR instruction.MWAIT and MONITOR may be executed only at privilege level 0, if you use these instructions in any other privilege level, then an invalid opcode exception is thrown.If the preceding MONITOR instruction did not successfully arm an address range or if the MONITOR instruction has not been executed prior to executing MWAIT, then the processor will not enter the implementation-dependent-optimized state. Execution will resume at the instruction following the MWAIT.The opcode and the instructions are shown below :0: 0f 01 c9 mwait0: 0f 01 c8 monitorCheck AvailabilityThe BIOS or any kernel-level driver or operating system can disable these instructions by using the IA32_MISC_ENABLE MSR;CPUID.01H:ECX.MONITOR[bit 3] indicates the availability of MONITOR and MWAIT in the processor.Query About DetailsTo query about the smallest and the largest line size that MONITOR supports you can use CPUID.05H:EAX[bits 15:0] and CPUID.05H:EBX.Largest[bits 15:0]. Values are returned in bytes.ImplementationFor MONITOR address should be in RAX/EAX, ECX and EDX are hints to processor about Monitor state. (We make them zeros.). MWAIT, on the other hand, should be executed after MONITOR and you can use ECX in order to config MWAIT about interrupts.If ECX[0th Bit] = 0, then MWAIT will wake on every interrupts (that’s exactly like HLT instruction) but if ECX[0th Bit] = 1 then it doesn’t wake on interrupts.As you can see in the following code, we don’t need MWAIT to wake by system interrupts so just increment the ECX.MWAIT/MONITOR in Linux Kernel Module (AT&amp;T Syntax):int init_module(void){long unsigned int address = 0xffffffff12345678; __asm__ volatile("push %%rax\\n\\t""push %%rcx\\n\\t""push %%rdx\\n\\t""xor %%rax,%%rax\\n\\t""xor %%rcx,%%rax\\n\\t""xor %%rdx,%%rax\\n\\t""movq %0,%%rax\\n\\t""MONITOR\\n\\t""xor %%rax,%%rax\\n\\t""xor %%rcx,%%rax\\n\\t""inc %%rax\\n\\t""MWAIT\\n\\t""pop %%rdx\\n\\t""pop %%rcx\\n\\t""pop %%rax\\n\\t":: "g" (address));printk("The requested location has been accessed !");return 0;}The address to monitor is stored in “address” and in our example it is 0xffffffff12345678.Using MONITOR/MWAIT To Detect ModificationsAs I told you, MONITOR/MWAIT can be used as a debugging trick, whenever we used all of our 4 debug registers then we can use our cores instead! I always use these instructions to detect whether a special range of memory (in the kernel) is modified by other processors or not. On the other hand, you have more flexibility in size rather than the debug registers but the worst thing about it is that you can notify in the case of modification but you never know what was the code that leads to this modification.As Intel describes MONITOR/MWAIT are agent synchronization instructions so they might be used in order to trigger an event to notify a kernel program.LimitationOne of the limitations for MONITOR/MWAIT is that it only wakes on modification on the write-back cache and not write-through cache so it seems Intel implemented these instructions just in L1 write-back.A good answer in StackOverflow describes the differences between write-back and write-through caches: Write-back is used for the up-to-date data is in a processor cache, and sometimes it is in main memory. If the data is in a processor cache, then that processor must stop main memory from replying to the read request, because the main memory might have a stale copy of the data. This is more complicated than write-through. Write-through can simplify the cache coherency protocol because it doesn’t need the Modify state. The Modify state records that the cache must write back the cache line before it invalidates or evicts the line. In write-through, a cache line can always be invalidated without writing back since memory already has an up-to-date copy of the line. TheThe equivalent of MWAIT and Monitor in other processors is MIPS’LL/Pause.References[1] How to Use the MONITOR and MWAIT Streaming SIMD Extensions 3 Instructions (https://software.intel.com/en-us/articles/how-to-use-the-monitor-and-mwait-streaming-simd-extensions-3-instructions)[2] MWAIT — Monitor Wait (https://www.felixcloutier.com/x86/MWAIT.html)[3] Write-back vs Write-Through (https://stackoverflow.com/questions/27087912/write-back-vs-write-through)" }, { "title": "Hypervisor From Scratch – Part 2: Entering VMX Operation", "url": "/topics/hypervisor-from-scratch-part-2/", "categories": "cpu, hypervisor, tutorials", "tags": "hypervisor, hypervisor-tutorial, creating-virtual-machine, hypervisor-tutorials, intel-vt-x-tutorial, setting-up-virtual-machine-monitor, vmm-tutorials, vmx-implementation, vmx-tutorials", "date": "2018-09-03 00:00:00 +0000", "snippet": "If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous practical examples on how to utilize hypervisors for reverse engineering.IntroductionIt’s the second part of a multiple series of a tutorial called “Hypervisor From Scratch”. First, please consider reading the first part (Basic Concepts &amp; Configure Testing Environment) before reading this part, as it contains the essential knowledge you need to know in order to understand the rest of this tutorial. In this part, we’ll talk about WDK drivers and finally start enabling VT-x.Table of Contents Introduction Table of Contents Overview IRP Major Functions What is an IRP? Configuring IRP Major Functions IRP Major Functions List Loading Driver and Checking Device Viewing Debugging Messages In DbgView Detecting Hypervisor Support Setting CR4 VMXE Bit Conclusion ReferencesOverviewIn this section, we will learn about Detecting Hypervisor Support for our processor, then we simply config the basic operations to Enable VMX, Entering VMX Operation, and we will learn more about Window Driver Kit (WDK).The source code of this tutorial is available at :[https://github.com/SinaKarvandi/Hypervisor-From-Scratch/]IRP Major FunctionsBesides our kernel-mode driver (“MyHypervisorDriver”), we’ll create a user-mode application called “MyHypervisorApp”. First of all, I should encourage you to write most of the codes (whenever it’s possible) in user-mode rather than the kernel-mode, and that’s because you might not have handled exceptions properly. Hence, it leads to BSODs, or on the other hand, running less code in kernel-mode reduces the possibility of putting some nasty kernel-mode bugs.If you remember from the previous part, we created a Windows driver. Now we want to extend our project to support more IRP Major functions.IRP Major Functions are located in a conventional Windows table created for every device. Once we register a device in Windows, we have to introduce a handler for these IRP Major Functions.That’s like every device has a table of Major Functions. Whenever a user-mode application calls any of these functions, Windows finds the corresponding function (if the device driver supports that MJ Function), then passes an IRP to the kernel driver.What is an IRP?So, what is an IRP? IRP is a structure that represents an I/O Request Packet. This packet contains many details about its caller, parameters, state of the packet, etc. We extract the caller parameters from the IRP packet.Now, we can handle the user-mode request in the kernel based on the details provided by IRP.Remember, when our functions in the kernel driver receive the IRP packet, it’s the responsibility of our code to investigate the caller and check its privileges, etc.Configuring IRP Major FunctionsAfter registering a device (explained in the previous part), we need to introduce the Major Functions of our device.The following code is responsible for configuring different IRP MJ Functions and introducing custom kernel-mode functions as the IRP handlers. if (NtStatus == STATUS_SUCCESS) { for (Index = 0; Index &lt; IRP_MJ_MAXIMUM_FUNCTION; Index++) { DriverObject-&gt;MajorFunction[Index] = DrvUnsupported; } DbgPrint("[*] Setting Devices major functions."); DriverObject-&gt;MajorFunction[IRP_MJ_CLOSE] = DrvClose; DriverObject-&gt;MajorFunction[IRP_MJ_CREATE] = DrvCreate; DriverObject-&gt;MajorFunction[IRP_MJ_DEVICE_CONTROL] = DrvIoctlDispatcher; DriverObject-&gt;MajorFunction[IRP_MJ_READ] = DrvRead; DriverObject-&gt;MajorFunction[IRP_MJ_WRITE] = DrvWrite; DriverObject-&gt;DriverUnload = DrvUnload; IoCreateSymbolicLink(&amp;DosDeviceName, &amp;DriverName); } else { DbgPrint("[*] There were some errors in creating device."); }You can see that we used “DrvUnsupported” for all functions. This function handles all MJ Functions and tells the user that it’s not supported.The main body of “DrvUnsupported” is like this:NTSTATUSDrvUnsupported(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ DbgPrint("[*] This function is not supported :( !"); Irp-&gt;IoStatus.Status = STATUS_SUCCESS; Irp-&gt;IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS;}We also introduce other Major Functions that are essential for our device. We’ll complete the implementation of some of these MJ Functions in the future parts.NTSTATUSDrvRead(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ DbgPrint("[*] Not implemented yet :( !"); Irp-&gt;IoStatus.Status = STATUS_SUCCESS; Irp-&gt;IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS;}NTSTATUSDrvWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ DbgPrint("[*] Not implemented yet :( !"); Irp-&gt;IoStatus.Status = STATUS_SUCCESS; Irp-&gt;IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS;}NTSTATUSDrvClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ DbgPrint("[*] Not implemented yet :( !"); Irp-&gt;IoStatus.Status = STATUS_SUCCESS; Irp-&gt;IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS;}Now let’s see the IRP MJ Functions list and other types of Windows Driver Kit handlers routine.IRP Major Functions ListWe can use this list of IRP Major Functions to perform different operations in a WDK driver.#define IRP_MJ_CREATE 0x00#define IRP_MJ_CREATE_NAMED_PIPE 0x01#define IRP_MJ_CLOSE 0x02#define IRP_MJ_READ 0x03#define IRP_MJ_WRITE 0x04#define IRP_MJ_QUERY_INFORMATION 0x05#define IRP_MJ_SET_INFORMATION 0x06#define IRP_MJ_QUERY_EA 0x07#define IRP_MJ_SET_EA 0x08#define IRP_MJ_FLUSH_BUFFERS 0x09#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a#define IRP_MJ_SET_VOLUME_INFORMATION 0x0b#define IRP_MJ_DIRECTORY_CONTROL 0x0c#define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d#define IRP_MJ_DEVICE_CONTROL 0x0e#define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0f#define IRP_MJ_SHUTDOWN 0x10#define IRP_MJ_LOCK_CONTROL 0x11#define IRP_MJ_CLEANUP 0x12#define IRP_MJ_CREATE_MAILSLOT 0x13#define IRP_MJ_QUERY_SECURITY 0x14#define IRP_MJ_SET_SECURITY 0x15#define IRP_MJ_POWER 0x16#define IRP_MJ_SYSTEM_CONTROL 0x17#define IRP_MJ_DEVICE_CHANGE 0x18#define IRP_MJ_QUERY_QUOTA 0x19#define IRP_MJ_SET_QUOTA 0x1a#define IRP_MJ_PNP 0x1b#define IRP_MJ_PNP_POWER IRP_MJ_PNP // Obsolete....#define IRP_MJ_MAXIMUM_FUNCTION 0x1bEvery major function will only trigger if we call its corresponding function from the user-mode. For instance, there is a function (in user-mode) called CreateFile (And all its variants like CreateFileA and CreateFileW for ASCII and Unicode), so every time we call CreateFile, the function that registered as IRP_MJ_CREATE will be called, if we call ReadFile then IRP_MJ_READ, or WriteFile then IRP_MJ_WRITE  will be triggered.You can see that Windows treats its devices like files, and everything we need to pass from user-mode to kernel-mode is available in an argument with the IRP * type and available as a buffer to the kernel IRP MJ Function handlers. Windows is responsible for copying the user-mode buffer to the kernel mode stack.Don’t worry; we use it frequently in the rest of the project, but we only support IRP_MJ_CREATE in this part and left others unimplemented for future parts.There are other terms called “IRP Minor Functions”. We left these functionalities as they’re not used in this series.Loading Driver and Checking DeviceIn order to load our driver (MyHypervisorDriver), first, download OSR Driver Loader, then run Sysinternals DbgView as administrator. Ensure that your DbgView captures the kernel (you can check by going to Capture -&gt; Capture Kernel).After that open the OSR Driver Loader (go to OsrLoader -&gt; kit-&gt; WNET -&gt; AMD64 -&gt; FRE) and open OSRLOADER.exe. Now, if you build your driver, find the .sys file (in MyHypervisorDriver\\x64\\Debug\\ should be a file named: “MyHypervisorDriver.sys”), in OSR Driver Loader, click to browse and select (MyHypervisorDriver.sys) and then click to “Register Service” after that, you see a message box that shows your driver registered successfully, you should click on “Start Service”.Please note that you should have WDK installed for your Visual Studio in order to be able to build your project.Now come back to the DbgView, you should see that your driver loaded successfully, and a message “[*] DriverEntry Called.” should appear.If there is no problem, then you’re good to go. Otherwise, you can check the next step if you have a problem with DbgView.Keep in mind that now we have registered our driver, so we can use SysInternals WinObj to see whether “MyHypervisorDevice” is available or not.Viewing Debugging Messages In DbgViewUnfortunately, for some unknown reason, I’m unable to view the result of DbgPrint(). If you can see the result, then you can skip this step but if you have a problem, perform the following steps:As I mentioned in part 1:In “regedit.exe”, add a key:HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Debug Print FilterUnder that, add a DWORD value named IHVDRIVER with a value of 0xFFFF.This method should solve the problem, but if the problem still persists, we have another option. For this purpose, we can use WinDbg to find a Windows Kernel global variable called nt!Kd\\_DEFAULT\\_Mask. This variable is responsible for showing the results in DbgView. It has a mask that I’m not aware of, so I just put a 0xffffffff into it to simply make it show everything!To do this, you need a Windows Kernel Debugging using WinDbg. In WinDbg, you can run the following command:kd&gt; eb nt!kd_Default_Mask ff ff ff ffAfter that, we should see the results and everything we’ll be ready for the next step.Remember, this is an essential step for the rest of the topic because if we can’t see any kernel messages, sure, we can’t debug it too.Detecting Hypervisor SupportDiscovering support for VMX is the first thing we should consider before enabling VT-x. This is covered in Intel Software Developer’s Manual volume 3C section 23.6 DISCOVERING SUPPORT FOR VMX.You could know the presence of VMX using CPUID if CPUID.1:ECX.VMX[bit 5] = 1, then VMX operation is supported.First, we need to know whether or not we’re running on an Intel-based processor. We can understand this using the CPUID instruction and finding the vendor string “GenuineIntel”.The following function returns the vendor string by using the CPUID instruction.std::stringGetCpuID(){ // Initialize used variables char SysType[13]; // Array consisting of 13 single bytes/characters string CpuID; // The string that will be used to add all the characters to // Starting coding in assembly language _asm { // Execute CPUID with EAX = 0 to get the CPU producer XOR EAX, EAX CPUID // MOV EBX to EAX and get the characters one by one by using shift out right bitwise operation. MOV EAX, EBX MOV SysType[0], AL MOV SysType[1], AH SHR EAX, 16 MOV SysType[2], AL MOV SysType[3], AH // Get the second part the same way but these values are stored in EDX MOV EAX, EDX MOV SysType[4], AL MOV SysType[5], AH SHR EAX, 16 MOV SysType[6], AL MOV SysType[7], AH // Get the third part MOV EAX, ECX MOV SysType[8], AL MOV SysType[9], AH SHR EAX, 16 MOV SysType[10], AL MOV SysType[11], AH MOV SysType[12], 00 } CpuID.assign(SysType, 12); return CpuID;}The last step is checking for the presence of VMX. We can check it using the following code :boolDetectVmxSupport(){ bool VMX = false; __asm { XOR EAX, EAX INC EAX CPUID BT ECX, 0x5 JC VMXSupport VMXNotSupport : JMP NopInstr VMXSupport : MOV VMX, 0x1 NopInstr : NOP } return VMX;}As you can see, it checks CPUID with EAX=1, and if the 5th (6th) bit is one, then the VMX Operation is supported. We can also perform the same thing in Kernel Driver.All in all, our main code to detect the support for VMX should be something like this:intmain(){ std::string CpuId; PrintAppearance(); CpuId = GetCpuID(); printf("[*] The CPU Vendor is : %s \\n", CpuID.c_str()); if (CpuId == "GenuineIntel") { printf("[*] The Processor virtualization technology is VT-x. \\n"); } else { printf("[*] This program is not designed to run in a non-VT-x environment !\\n"); return 1; } if (DetectVmxSupport()) { printf("[*] VMX Operation is supported by your processor .\\n"); } else { printf("[*] VMX Operation is not supported by your processor .\\n"); return 1; } HANDLE hWnd = CreateFile(L"\\\\\\\\.\\\\MyHypervisorDevice", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, /// lpSecurityAttirbutes OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); /// lpTemplateFile _getch(); return 0;}The final result:Enabling VMX OperationIf the processor supports the VMX Operation, it’s time to enable it. As I told you above, IRP_MJ_CREATE is the first function that should be used to start the operation.Before entering the VMX operation, we should enable VMX by setting CR4.VMXE[bit 13] = 1. VMX operation is then entered by executing the VMXON instruction. VMXON causes an invalid-opcode exception (#UD) if executed with CR4.VMXE = 0. Once in VMX operation, it is not possible to clear CR4.VMXE.After that, we can leave the VMX operation by executing the VMXOFF instruction and, this time, CR4.VMXE can be cleared.VMXON is also controlled by the IA32_FEATURE_CONTROL MSR (MSR address 3AH). This MSR is cleared to zero when a logical processor is reset.Let’s look at the first bit of this MSR:  Bit 0 is the lock bit. If this bit is clear, VMXON causes a general-protection (#GP) exception. If the lock bit is set, WRMSR to this MSR causes a general-protection exception; the MSR cannot be modified until a power-up reset.What does it mean? It means that we can disable the VMX feature without the ability to be enabled again. Only after a system reset, we can enable the VMX.System BIOS can use this bit to provide a setup option for BIOS to disable support for VMX. To enable VMX support in a platform, BIOS must set bit 1, bit 2, or both, as well as the lock bit.Setting CR4 VMXE BitDo you remember the previous part where I told you how to create an inline assembly in Windows Driver Kit (x64)? Now we should create some function to perform this operation in assembly.Just in Header File (in my case Source.h) declare your function:extern void inline AsmEnableVmxOperation(void);Then in the assembly file (in my case, “SourceAsm.asm”), add this function (Which sets the 13th (14th) bit of CR4).AsmEnableVmxOperation PROC PUBLIC PUSH RAX ; Save the state XOR RAX, RAX ; Clear the RAX MOV RAX, CR4 OR RAX,02000h ; Set the 14th bit MOV CR4, RAX POP RAX ; Restore the state RETAsmEnableVmxOperation ENDPAlso, declare your function in the above of SourceAsm.asm.PUBLIC AsmEnableVmxOperationThis assembly function should be called in DrvCreate:NTSTATUSDrvCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ // // Enabling VMX Operation // AsmEnableVmxOperation(); DbgPrint("[*] VMX Operation Enabled Successfully !"); Irp-&gt;IoStatus.Status = STATUS_SUCCESS; Irp-&gt;IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS;}At last, we should call the following function from the user-mode: HANDLE hWnd = CreateFile(L"\\\\\\\\.\\\\MyHypervisorDevice", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, /// lpSecurityAttirbutes OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); /// lpTemplateFileIf you see the following result, you successfully completed the second part.Important Note: Please consider that your .asm file should have a different name from your main driver file (.c file). For example, if your driver file is “Source.c”, then using the name “Source.asm” causes weird linking errors in Visual Studio. You should change the name of your .asm file to something like “SourceAsm.asm” to avoid these linker errors.ConclusionIn this part, we learned about the basic stuff we need to know to create a Windows Driver Kit program, and then we entered our virtual environment to build a cornerstone for the rest of the parts.In the third part, we’re getting deeper with Intel VT-x and making our driver even more advanced.Note: Remember that hypervisors change over time because new features are added to the operating systems or new technologies are used. For example, updates to Meltdown &amp; Spectre have made a lot of changes to the hypervisors. So, if you want to use Hypervisor From Scratch in your projects, research, or whatever, you should use the HyperDbg drivers. HyperDbg is actively maintained, stable, and reliable, ensuring you avoid the errors and instability problems that can arise from using older parts of the tutorial series.The third part is also available [here].(https://rayanfam.com/topics/hypervisor-from-scratch-part-3/).References[1] Intel® 64 and IA-32 architectures software developer’s manual combined volumes 3 (https://software.intel.com/en-us/articles/intel-sdm) [2] IRP_MJ_DEVICE_CONTROL (https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/irp-mj-device-control)[3]  Windows Driver Kit Samples (https://github.com/Microsoft/Windows-driver-samples/blob/master/general/ioctl/wdm/sys/sioctl.c)[4] Setting Up Local Kernel Debugging of a Single Computer Manually (https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-local-kernel-debugging-of-a-single-computer-manually)[5] Obtain processor manufacturer using CPUID (https://www.daniweb.com/programming/software-development/threads/112968/obtain-processor-manufacturer-using-cpuid)[6] Plug and Play Minor IRPs (https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/plug-and-play-minor-irps)[7] _FAST_IO_DISPATCH structure (https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_fast_io_dispatch)[8] Filtering IRPs and Fast I/O (https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/filtering-irps-and-fast-i-o)[9] Windows File System Filter Driver Development (https://www.apriorit.com/dev-blog/167-file-system-filter-driver)" }, { "title": "A Tour of Mount in Linux", "url": "/topics/mount-in-linux/", "categories": "linux, network, sysadmin, tutorials", "tags": "filesystem, hfs, linux, modern, mount, os, systemd, tutorial, udev, vfs", "date": "2018-08-31 00:00:00 +0000", "snippet": "I had windows 10 installed on my laptop because of serious incompatibility of ROG laptops with Linux and my desire to play some games after years of living in bash! This continued for a year and I really like windows 10 cool features like WSL and Hyper-V (with Docker!), however being in love with rolling-release distros and being fed up with windows in the past year. And the fact that I was yearning for terminal, I installed Solus OS earlier this week on a separate partition without removing windows.And now we get to the point which I wanted to listen to some music which resided on another ntfs volume. And trying to automatically mount the drive with Solus, It occurred to me that how much has really changed in hardware representation in Linux in general. Then it became to write this post about mounting in Linux and new tools for it.A little backgroundwhen you have a storage device (like a hard disk) attached to your computer, Linux kernel, being in charge of talking to hardware, detects the device and creates a file representing the device in /dev like /dev/sda. For each partition that is detected from the device’s partition table, another file is created like /dev/sda1Some files are also created in sysfs (/sys) in newer kernels and expose more advanced control and info to the user. (which are not talked about in this post)For actually being able to read/write into a device, you should mount it first. The mount command mounts a storage device or filesystem, making it accessible and attaching it to an existing directory structure.in order to mount a volume you should invoke the mount command. First, you will create a directory (should be empty)# mkdir /mnt/mydataThen you mount the volume to that:# mount /dev/sda1 /mnt/mydataTo see all mounted volumes and their options you can use mount command or findmnt (both are provided by util-linux and are available in all distros):$ findmntTARGET SOURCE FSTYPE OPTIONS/ /dev/sda4 ext4 rw,relatime,errors=remount-ro├─/sys sysfs sysfs rw,nosuid,nodev,noexec,relatime│ ├─/sys/kernel/security securityfs securityfs rw,nosuid,nodev,noexec,relatime│ ├─/sys/fs/cgroup tmpfs tmpfs ro,nosuid,nodev,noexec,mode=755│ │ ├─/sys/fs/cgroup/unified cgroup2 cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate│ │ ├─/sys/fs/cgroup/systemd cgroup cgroup rw,nosuid,nodev,noexec,relatime,xattr,name=systemd│ │ ├─/sys/fs/cgroup/net_cls,net_prio cgroup cgroup rw,nosuid,nodev,noexec,relatime,net_cls,net_prio│ │ ├─/sys/fs/cgroup/perf_event cgroup cgroup rw,nosuid,nodev,noexec,relatime,perf_event│ │ ├─/sys/fs/cgroup/pids cgroup cgroup rw,nosuid,nodev,noexec,relatime,pids│ │ ├─/sys/fs/cgroup/cpu,cpuacct cgroup cgroup rw,nosuid,nodev,noexec,relatime,cpu,cpuacct│ │ ├─/sys/fs/cgroup/blkio cgroup cgroup rw,nosuid,nodev,noexec,relatime,blkio│ │ ├─/sys/fs/cgroup/cpuset cgroup cgroup rw,nosuid,nodev,noexec,relatime,cpuset│ │ ├─/sys/fs/cgroup/freezer cgroup cgroup rw,nosuid,nodev,noexec,relatime,freezer│ │ ├─/sys/fs/cgroup/devices cgroup cgroup rw,nosuid,nodev,noexec,relatime,devices│ │ └─/sys/fs/cgroup/memory cgroup cgroup rw,nosuid,nodev,noexec,relatime,memory│ ├─/sys/firmware/efi/efivars efivarfs efivarfs rw,nosuid,nodev,noexec,relatime│ ├─/sys/fs/bpf bpf bpf rw,nosuid,nodev,noexec,relatime,mode=700│ ├─/sys/kernel/debug debugfs debugfs rw,relatime│ └─/sys/fs/fuse/connections fusectl fusectl rw,relatime├─/proc proc proc rw,nosuid,noexec,relatime│ └─/proc/sys/fs/binfmt_misc systemd-1 autofs rw,relatime,fd=36,pgrp=1,timeout=0,minproto=5,maxproto=5,direct├─/dev devtmpfs devtmpfs rw,nosuid,size=8144172k,nr_inodes=2036043,mode=755│ ├─/dev/shm tmpfs tmpfs rw│ │ └─/dev/shm none tmpfs rw,relatime│ ├─/dev/pts devpts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000│ ├─/dev/mqueue mqueue mqueue rw,relatime│ └─/dev/hugepages hugetlbfs hugetlbfs rw,relatime,pagesize=2M├─/run tmpfs tmpfs rw,nosuid,nodev,mode=755│ ├─/run/media/shahriar/Data /dev/sda2 fuseblk rw,nosuid,nodev,relatime,user_id=0,group_id=0,default_permissions,allow_other,blksize=4096│ ├─/run/user/1000 tmpfs tmpfs rw,nosuid,nodev,relatime,size=1638552k,mode=700,uid=1000,gid=1000│ │ └─/run/user/1000/gvfs gvfsd-fuse fuse.gvfsd-fuse rw,nosuid,nodev,relatime,user_id=1000,group_id=1000│ └─/run/snapd/ns tmpfs[/snapd/ns] tmpfs rw,nosuid,nodev,mode=755│ └─/run/snapd/ns/chromium.mnt nsfs[mnt:[4026532495]] nsfs rw├─/tmp tmpfs tmpfs rw,nosuid,nodev├─/snap/slack/7 /dev/loop0 squashfs ro,nodev,relatime├─/snap/sublime-text/26 /dev/loop1 squashfs ro,nodev,relatime├─/snap/core/5145 /dev/loop2 squashfs ro,nodev,relatime├─/snap/chromium/412 /dev/loop4 squashfs ro,nodev,relatime├─/snap/core/5328 /dev/loop3 squashfs ro,nodev,relatime└─/snap/telegram-desktop/270 /dev/loop5 squashfs ro,nodev,relatime$ mountsysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)proc on /proc type proc (rw,nosuid,noexec,relatime)devtmpfs on /dev type devtmpfs (rw,nosuid,size=8144172k,nr_inodes=2036043,mode=755)securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)tmpfs on /dev/shm type tmpfs (rw)devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)tmpfs on /run type tmpfs (rw,nosuid,nodev,mode=755)tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)cgroup2 on /sys/fs/cgroup/unified type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate)cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)efivarfs on /sys/firmware/efi/efivars type efivarfs (rw,nosuid,nodev,noexec,relatime)bpf on /sys/fs/bpf type bpf (rw,nosuid,nodev,noexec,relatime,mode=700)cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)/dev/sda4 on / type ext4 (rw,relatime,errors=remount-ro)mqueue on /dev/mqueue type mqueue (rw,relatime)hugetlbfs on /dev/hugepages type hugetlbfs (rw,relatime,pagesize=2M)systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=36,pgrp=1,timeout=0,minproto=5,maxproto=5,direct)debugfs on /sys/kernel/debug type debugfs (rw,relatime)tmpfs on /tmp type tmpfs (rw,nosuid,nodev)/var/lib/snapd/snaps/sublime-text_26.snap on /snap/sublime-text/26 type squashfs (ro,nodev,relatime,x-gdu.hide)/var/lib/snapd/snaps/slack_7.snap on /snap/slack/7 type squashfs (ro,nodev,relatime,x-gdu.hide)/var/lib/snapd/snaps/core_5145.snap on /snap/core/5145 type squashfs (ro,nodev,relatime,x-gdu.hide)/var/lib/snapd/snaps/chromium_412.snap on /snap/chromium/412 type squashfs (ro,nodev,relatime,x-gdu.hide)/var/lib/snapd/snaps/core_5328.snap on /snap/core/5328 type squashfs (ro,nodev,relatime,x-gdu.hide)/var/lib/snapd/snaps/telegram-desktop_270.snap on /snap/telegram-desktop/270 type squashfs (ro,nodev,relatime,x-gdu.hide)fusectl on /sys/fs/fuse/connections type fusectl (rw,relatime)tmpfs on /run/user/1000 type tmpfs (rw,nosuid,nodev,relatime,size=1638552k,mode=700,uid=1000,gid=1000)gvfsd-fuse on /run/user/1000/gvfs type fuse.gvfsd-fuse (rw,nosuid,nodev,relatime,user_id=1000,group_id=1000)tmpfs on /run/snapd/ns type tmpfs (rw,nosuid,nodev,mode=755)nsfs on /run/snapd/ns/chromium.mnt type nsfs (rw)none on /dev/shm type tmpfs (rw,relatime)/dev/sda2 on /run/media/shahriar/Data type fuseblk (rw,nosuid,nodev,relatime,user_id=0,group_id=0,default_permissions,allow_other,blksize=4096,uhelper=udisks2)For unmounting the device you should use umount:either the device containing the file system or the mount point can be specified.# umount /dev/sda1# umount /mnt/mydataA word about permissionsAll device files created in /dev are only accessible to root. Therefore in a general sense, only root can mount stuff. This is very important and you need to keep that in mind. If a filesystem has Linux permission support (linux native filesystems like ext2, ext3, ext4, btrfs, xfs, etc), they are leveraged and files on the drive are treated like other files in your linux system. But some filesystems like FAT derivatives (FAT32, exFAT, etc) are not capable of storing permission information. So by default they become only available too root unless you specify some options at mount time to allow other users. consider this example:replace xxx with uid/gid of user# mount -t vfat /dev/sda6 /media/FAT32 -o rw,uid=xxx,gid=xxxalso this neat one-liner which mounts them accessible to your user:$ sudo mount -t vfat /dev/sda6 /media/FAT32 -o rw,uid=$(id -u),gid=$(id -g)or all users…# mount -t vfat /dev/sda6 /media/FAT32 -o rw,umask=0000FilesystemsA filesystem controls how data is stored and retrieved. usually necessary options and filesystem are detected by the mount command, but you can specify them manually like this:# mount -t ntfs /dev/sda1 /mnt/mydataYou can view all filesystems supported by your kernel by reading this file:$ cat /proc/filesystemsnodev sysfsnodev rootfsnodev ramfsnodev bdevnodev procnodev cpusetnodev cgroupnodev cgroup2nodev tmpfsnodev devtmpfsnodev debugfsnodev tracefsnodev securityfsnodev sockfsnodev daxnodev bpfnodev pipefsnodev hugetlbfsnodev devpts ext3 ext4 iso9660nodev autofs xfsnodev efivarfsnodev mqueue btrfs squashfs fuseblknodev fusenodev fusectlnodev overlayThe first column signifies whether the file system is mounted on a block device. Those beginning with nodev are not mounted on a device. The second column lists the names of the file systems supported.The mount command cycles through the file systems listed here when one is not specified as an argument.To see existing filesystems on drives use lsblk command like this:$ lsblk -fNAME FSTYPE LABEL UUID MOUNTPOINTloop0 squashfs /snap/slack/7loop1 squashfs /snap/sublime-text/26loop2 squashfs /snap/core/5145loop3 squashfs /snap/core/5328loop4 squashfs /snap/chromium/412loop5 squashfs /snap/telegram-desktop/270sda ├─sda1 ├─sda2 ntfs Data DCDE47AADE477C30 /run/media/shahriar/Data├─sda3 swap 9269aa88-3a31-4299-a1ef-f1472750717f [SWAP]└─sda4 ext4 7fe5feba-8c3d-4fe9-ab0a-20776aabf441 /sdb ├─sdb1 ntfs Recovery 5436D56136D544A0 ├─sdb2 vfat 30D6-1226 ├─sdb3 └─sdb4 ntfs 560A18780A1856F9 sr0 (loop devices are apparently being used by snapd. I will write about it in another article, it’s very cool!)As I said earlier these are filesystem types supported by the kernel, you might ask so where else can a filesystem be implemented? And the answer is FUSE.Filesystem in Userspace (FUSE) is a mechanism for Unix-like operating systems that lets non-privileged users create their own file systems without editing kernel code. This is achieved by running file system code in user space, while the FUSE kernel module provides only a “bridge” to the actual kernel interfaces.However you should know that reading/writing on a FUSE filesystem involves a lot more overhead because of code providing filesystem functionality being in userland and each call to a function for that matter will traverse kernel and userland a couple of times more than a regular file operation involving a driver in kernel. Nonetheless it is a very useful feature in a lot of scenarios. (like mounting writable ntfs or a filesystem over the network)There are also a myriad of other filesystems in the Linux world, such as overlay filesystems , so I suggest you to read references at the end of the post for further reading.Once we know the basics of mounting in linux, we are going to learn how to automate that and what happens when Linux is booted regarding mounts.fstabThe fstab file can be used to define how disk partitions, various other block devices, or remote filesystems should be mounted into the filesystem.Each filesystem is described on a separate line. Fields on each line are separated by tabs or spaces. Lines starting with ‘#’ are comments. Blank lines are ignored.The mount command will use fstab, if just one of either directory or device is given, to fill in the value for the other parameter. When doing so, mount options which are listed in fstab will also be used. (user option can be set in fstab to allow non-root users to mount)All specified devices within /etc/fstab will be automatically mounted on startup and when the -a flag is used with mount, unless the noauto option is specified. Devices that are listed and not present will result in an error unless the nofail option is used.$ cat /etc/fstab# &lt;device&gt; &lt;dir&gt; &lt;type&gt; &lt;options&gt; &lt;dump&gt; &lt;fsck&gt;/dev/sda1 / ext4 noatime 0 1/dev/sda2 none swap defaults 0 0/dev/sda3 /home ext4 noatime 0 2It is very recommended to use UUID or other unique identifiers instead of relying on kernel name descriptors (sda1, sdb2, …) as they may change after reboot!UUID is the preferred method. You can find out the UUID with lsblk -f$ cat /etc/fstab# &lt;device&gt; &lt;dir&gt; &lt;type&gt; &lt;options&gt; &lt;dump&gt; &lt;fsck&gt;UUID=CBB6-24F2 /boot vfat defaults 0 2UUID=0a3407de-014b-458b-b5c1-848e92a327a3 / ext4 defaults 0 1UUID=b411dc99-f0a0-4c87-9e05-184977be8539 /home ext4 defaults 0 2UUID=f9fe0b69-a280-415d-a03a-a32752370dee none swap defaults 0 0Modernizing mountingIn the early 2000s, the first attempt to cope with this was called HAL - Hardware Abstraction Layer, which did what it said on the tin, provided a layer between device nodes and the user of the nodes, so storage devices (and other hardware) can be added and removed without rebooting this system and without rewriting the /etc/fstab file.After deprecation of HAL, everything gets replaced a dizzying number of times (DeviceKit, devfs, etc) as better approaches are discovered in a fast moving period of hardware development, udev eventually won and was the main thing for the rest of the decade.When a device is added or removed from the system, the Linux kernel notices and sends out an event. udev is a daemon that waits around listening for these events and then responding accordingly. udev runs in user space not kernel space.UdisksBuilding on top of new technologies (D-Bus, udev), udisks was created. It has modernized the mounting on userland.udisks provides: a daemon, udisksd, that implements well-defined D-Bus interfaces that can be used to query and manipulate storage devices. a command-line tool, udisksctl, that can be used to query and use the daemon The actions that a user can perform using udisks are restricted using polkit.It is the tool that handles all the work and permission checking when you simply click on the fancy usb flash drive icon that has appeared on your desktop after inserting it into your PC.We will cover the use of udisksctl for basic mounting/unmounting in this post. But it is important to know that all functionality provided here is just a wrapper around D-Bus calls which is easily programmable will be very useful in storage management automation. (like the clicking mentioned, etc)To see a list of disks attached to the system (serial numbers are replaced):$ udisksctl statusMODEL REVISION SERIAL DEVICE--------------------------------------------------------------------------Samsung SSD 860 EVO 500GB XXXXXXX XXXXXXXXXXX sda SanDisk SD8S XXXXXXX XXXXXXXXXXX sdb SlimtypeDVD A XXXXXXX XXXXXXXXXXX sr0 To see detailed info about disk:$ udiskctl dump&lt;output not shown due to length&gt;To actually mount a filesystem using this new tool:udisksctl mount -b /dev/sdb1And then to unmount:udisksctl unmount -b /dev/sdb1systemd mount unitsConsidering the merger of udev and systemd, and the ubiquity of systemd in modern Linux distros. It is recommended to ditch old-school habits and start having fun with cool new functionality made available to use.Adding entries in fstab is no longer the main way to mount a device at startup. In fact all entries in /etc/fstab are converted to systemd mount units at boot.Example systemd .mount file:[Mount]What=/dev/disk/by-uuid/9269aa88-3a31-4299-bbb1-4e528a89d222Where=/mnt/mydataType=ext4Options=defaultsImportant: Mount units must be named after the mount point directories they control.Example: the mount point /home/lennart must be configured in a unit file home-lennart.mount.So we create a file like this:# vim /etc/systemd/system/mnt-mydata.mount[Unit]Description=Mount Some of my files to empty mydata dir[Mount]What=/dev/disk/by-uuid/9269aa88-3a31-4299-bbb1-4e528a89d222Where=/mnt/mydataType=ext4Options=defaultsOf course you should signal systemd to load the unit file after you’re done editing:# systemctl daemon-reload# systemctl start mnt-mydata.mountYou can view state of the mount like other units:# systemctl status mnt-mydata.mountImportant: If you want it to be mounted on each boot, you should also include an [Install] section in the unit file__:[Unit]Description=Mount Some of my files to empty mydata dir[Mount]What=/dev/disk/by-uuid/9269aa88-3a31-4299-bbb1-4e528a89d222Where=/mnt/mydataType=ext4Options=defaults [Install]WantedBy=multi-user.targetAnd the enable the unit to be started at boot:# systemctl enable mnt-backups.mountI hope you liked this post. There are still things that were left out of this post about mounting in Linux. But I leave them up to you to explore if you are interested. Overlay filesystems, FUSE and AutoFS are useful and are recommended for reading.References https://wiki.archlinux.org/index.php/fstab https://www.freedesktop.org/wiki/Software/udisks/ https://zeth.net/2014/05/28/modern_mounting_with_udisks2.html fstab manpage → https://manpages.debian.org/stretch/mount/fstab.5.en.html https://wiki.archlinux.org/index.php/file_systems https://www.centos.org/docs/5/html/5.1/Deployment_Guide/s2-proc-filesystems.html https://wiki.archlinux.org/index.php/persistent_block_device_naming https://en.wikipedia.org/wiki/File_system" }, { "title": "Hypervisor From Scratch - Part 1: Basic Concepts & Configure Testing Environment", "url": "/topics/hypervisor-from-scratch-part-1/", "categories": "cpu, hypervisor, tutorials", "tags": "hypervisor, hypervisor-tutorial, VMX-tutorial, create-a-virtual-machine, how-to-create-virtual-machine, hypervisor-fundamentals, hypervisor-tutorials, intel-virtualization, intel-vmx, intel-vtx-tutorial, using-cpu-virtualization, vmm-implementation", "date": "2018-08-21 00:00:00 +0000", "snippet": "If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous practical examples on how to utilize hypervisors for reverse engineering.Notice: The Hypervisor From Scratch tutorial is completely revised in August 2022. Codes from all parts are updated, unnecessary details are removed, and new explanations and materials are added to the tutorial.IntroductionWelcome to the first part of a multi-part series of tutorials called “Hypervisor From Scratch”. As the name implies, this course contains technical details to create a basic Virtual Machine based on hardware virtualization. If you follow this tutorial, you’ll be able to create your own virtual environment and understand how VMWare, VirtualBox, KVM, and other virtualization software use processors’ facilities to create a virtual environment. Moreover, you can learn how the “VMM” module of the HyperDbg Debugger works internally.The full source code of this tutorial is available on GitHub :[https://github.com/SinaKarvandi/Hypervisor-From-Scratch]Table of Contents Introduction Table of Contents Overview Hypervisors and Platforms Installing Tools Configuring A Testing Environment Creating A Driver Disabling The Driver Signature Enforcement (DSE) Nested-Virtualization Hyper-V’s Nested-Virtualization VMware Workstation’s Nested-Virtualization Concepts VMX Instructions Related Work Conclusion ReferencesOverviewBoth Intel and AMD support virtualization in their modern CPUs. Intel introduced (VT-x technology) that was previously codenamed “Vanderpool” on November 13, 2005, in Pentium 4 series. The CPU flag for VT-x capability is “VMX” which stands for Virtual Machine eXtension.AMD, on the other hand, developed its first generation of virtualization extensions under the codename “Pacifica” and initially published them as AMD Secure Virtual Machine (SVM), but later marketed them under the trademark AMD Virtualization, abbreviated AMD-V.There are two types of hypervisors. The “type 1” hypervisor is called a “bare-metal hypervisor” or “native” because it runs directly on a bare-metal physical server. A type 1 hypervisor has direct access to the hardware. With a type 1 hypervisor, there is no operating system to load as the hypervisor itself has the necessary functions to manage the system boot and startup.Contrary to a type 1 hypervisor, a type 2 hypervisor loads inside an operating system, just like any other application. Because the type 2 hypervisor has to go through the operating system and is managed by the OS, the type 2 hypervisor (and its virtual machines) will run less efficiently (slower) than type 1 hypervisors.Even though most of the concepts about the virtualization in Intel and AMD processors are the same, but there are some differences between Intel VT-x and AMD-V. For example, Intel and AMD use different instruction sets for virtualization.The rest of these tutorials mainly focus on VT-x because Intel CPUs are more popular and widely used.Hypervisors and PlatformsThese concepts are platform independent, which means you can easily run the same code routine in both Linux or Windows and expect the same behavior from the CPU, but I prefer to use Windows as its more easily debuggable (at least for me.), but I try to give some examples for Linux systems whenever needed.Linux kernel manages faults like #GP and other exceptions and tries to avoid the kernel panic and keep the system up; thus, it’s better to test something like a hypervisor or any CPU-related programs. In contrast, Windows never tries to manage any unexpected exception and shows a Blue Screen Of Death whenever an unexpected exception occurs; therefore, you might get lots of BSODs while testing your hypervisor.Considering the fact that I might (and will) make mistakes like misinformation, wrong implementation, or forget about mentioning some essential explanations in these series, I should say sorry in advance, and I’ll be glad and open to every comment that tells me the mistakes in the technical details. You can use the comments below to notify me about these possible errors.That’s enough. Let’s get started!Installing ToolsFirst, we need to install Visual Studio with WDK (Windows Driver Kit).You can download the community (free) version of the Visual Studio:[https://visualstudio.microsoft.com/vs/community].After installing Visual Studio, you can get the WDK from the below link:[https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk].We’ll use WinDbg to debug our hypervisor. We can use WinDbg and WinDbg Preview to debug our hypervisor.WinDbg is available in Windows SDK:[https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/]Afterward, we should connect to debug the Windows kernel using WinDbg. You can read the following links to connect WinDbg to debug the kernel using KDNET. Setting Up KDNET Network Kernel Debugging Manually Setting Up KDNET Network Kernel Debugging AutomaticallyThe next step is downloading OSR Driver Loader. We use this tool to load our driver. You can download it using the below link:[https://www.osronline.com/article.cfm?article=157].Finally, we need to download SysInternals DebugView for printing the DbgPrint() results.Configuring A Testing EnvironmentMost of the codes in this tutorial have to run at the kernel-level, and we must set up a Linux Kernel Module or a Windows Driver for this purpose.As configuring a Virtual Machine Monitor (VMM) involves lots of assembly codes, we should be able to embed inline assemblies within our kernel project.WDK (and also user-mode applications) no longer support inline assembly in an x64 environment, so we should be able to create a simple x64 project with the support for inline assembly codes.I explained it step by step in one of my posts, so I highly recommend reading this topic to create a project with inline assembly support before continuing the rest of this part.Now it’s time to create a driver!Creating A DriverThere is a good article here if you want to start with Windows Driver Kit (WDK).For the first example, we’ll create an elementary WDK driver.In WDK, we need two essential functions, first is a Driver Entry function that works as a starting point where the driver starts its execution whenever it is loaded. The second is for Driver Unload, responsible for removing the objects used in the driver.In the driver entry, our driver needs to register a device so we can communicate with our virtual environment from the user-mode. On the other hand, I defined DrvUnload, which uses the PnP Windows driver feature, and we can easily unload our driver and remove the device, then reload and create a new one.The following code is responsible for creating a new device : RtlInitUnicodeString(&amp;DriverName, L"\\\\Device\\\\MyHypervisor"); RtlInitUnicodeString(&amp;DosDeviceName, L"\\\\DosDevices\\\\MyHypervisor"); NtStatus = IoCreateDevice(DriverObject, 0, &amp;DriverName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &amp;DeviceObject); if (NtStatus == STATUS_SUCCESS) { DriverObject-&gt;DriverUnload = DrvUnload; DeviceObject-&gt;Flags |= IO_TYPE_DEVICE; DeviceObject-&gt;Flags &amp;= (~DO_DEVICE_INITIALIZING); IoCreateSymbolicLink(&amp;DosDeviceName, &amp;DriverName); }All in all, you can compile the following file to create our first WDK driver in Visual Studio (with WDK installed).It contains a Driver Entry function which creates a device and registers the unloading routines. Whenever the driver is loaded, the DriverEntry function is called, and when we unload it, DrvUnload will be called. This function will remove the device that we registered before.#include &lt;ntddk.h&gt;#include &lt;wdf.h&gt;#include &lt;wdm.h&gt;NTSTATUSDriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath);VOIDDrvUnload(PDRIVER_OBJECT DriverObject);#pragma alloc_text(INIT, DriverEntry)#pragma alloc_text(PAGE, DrvUnload)NTSTATUSDriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath){ NTSTATUS NtStatus = STATUS_SUCCESS; PDEVICE_OBJECT DeviceObject = NULL; UNICODE_STRING DriverName, DosDeviceName; DbgPrint("DriverEntry Called."); RtlInitUnicodeString(&amp;DriverName, L"\\\\Device\\\\MyHypervisor"); RtlInitUnicodeString(&amp;DosDeviceName, L"\\\\DosDevices\\\\MyHypervisor"); NtStatus = IoCreateDevice(DriverObject, 0, &amp;DriverName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &amp;DeviceObject); if (NtStatus == STATUS_SUCCESS) { DriverObject-&gt;DriverUnload = DrvUnload; DeviceObject-&gt;Flags |= IO_TYPE_DEVICE; DeviceObject-&gt;Flags &amp;= (~DO_DEVICE_INITIALIZING); IoCreateSymbolicLink(&amp;DosDeviceName, &amp;DriverName); } return NtStatus;}VOIDDrvUnload(PDRIVER_OBJECT DriverObject){ UNICODE_STRING DosDeviceName; DbgPrint("DrvUnload Called !"); RtlInitUnicodeString(&amp;DosDeviceName, L"\\\\DosDevices\\\\MyHypervisor"); IoDeleteSymbolicLink(&amp;DosDeviceName); IoDeleteDevice(DriverObject-&gt;DeviceObject);}Starting from the next version, the source code of each driver is available at GitHub, and we’ll talk about different features in WDK drivers. Don’t worry if you still don’t have any idea about a Windows Driver. We’ll work on it later in the next part. Just make sure to set up the environment for now.Disabling The Driver Signature Enforcement (DSE)If you use Windows, you should disable Driver Signature Enforcement to load our driver. That’s because Microsoft prevents any not verified code from running in Windows Kernel (Ring 0).To do this, press and hold the shift key and restart your computer. You should see a new window. Click Advanced options. On the new Window, Click Startup Settings. Click on Restart. On the Startup Settings screen, press 7 or F7 to disable driver signature enforcement.The latest thing I remember is enabling Windows Debugging messages through the registry. This way we can get DbgPrint() results through SysInternals DebugView.Just perform the following steps:In the Regedit, add a key:HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Debug Print FilterUnder that, add a DWORD value named IHVDRIVER with a value of 0xFFFF.Reboot the machine, and it’s good to go.Nested-virtualizationWhat if you don’t have access to a separate physical machine?You can use VMware’s (or any other virtualization product) nested-virtualization.VMware Workstation’s Nested-VirtualizationIn order to set up a nested virtualization environment, make sure to enable the following features in your VM.All the drivers are tested on both physical machines and VMware’s nested virtualization.Hyper-V’s Nested-VirtualizationHyper-V differs from VMWare in many aspects. Therefore you can’t test your hypervisor on Hyper-V’s nested virtualization. In part 8, I’ll describe how to modify your hypervisor in a way that can be used in Hyper-V, so after part 8, you’ll be able to test your hypervisor on Hyper-V’s nested virtualization.ConceptsWe will frequently use some keywords in the rest of these series, and you should know about them (Most of the definitions are derived from Intel software developer’s manual, volume 3C).Virtual Machine Monitor (VMM): VMM acts as a host and has full control of the processor(s) and other platform hardware. A VMM is able to retain selective control of processor resources, physical memory, interrupt management, and I/O.Guest Software: Each virtual machine (VM) is a guest software environment.VMX Root Operation and VMX Non-root Operation: A VMM will run in VMX root operation, and guest software will run in VMX non-root operation.VMX transitions: Transitions between VMX root operation and VMX non-root operation.VM entries: Transitions into VMX non-root operation.Extended Page Table (EPT): A mechanism that uses a second layer for converting the guest’s physical address to the host’s physical address.VM exits: Transitions from VMX non-root operation to VMX root operation.Virtual machine control structure (VMCS): is a data structure in memory that exists exactly once per VM (or more precisely, one per virtual CPU) while the VMM manages it. With every change in the execution context between different VMs, the VMCS is restored for the current VM, defining the state of the VM’s virtual processor and VMM control Guest software using VMCS.The VMCS consists of six logical groups: Guest-state area: Processor state saved into the guest state area on VM exits and loaded on VM entries. Host-state area: Processor state loaded from the host state area on VM exits. VM-execution control fields: Fields controlling processor operation in VMX non-root operation. VM-exit control fields: Fields that control VM exits. VM-entry control fields: Fields that control VM entries. VM-exit information fields: Read-only fields to receive information on VM exits describing the cause and the nature of the VM exit.I found a great work illustrating the VMCS. The PDF version is also available here.Don’t worry about the fields. I’ll explain most of them clearly in the latter parts. Just remember, VMCS Structure varies between different versions of a processor.VMX InstructionsVMX introduces the following new instructions. Intel Mnemonic Description INVEPT Invalidate Translations Derived from EPT INVVPID Invalidate Translations Based on VPID VMCALL Call to VM Monitor VMCLEAR Clear Virtual-Machine Control Structure VMFUNC Invoke VM function VMLAUNCH Launch Virtual Machine VMRESUME Resume Virtual Machine VMPTRLD Load Pointer to Virtual-Machine Control Structure VMPTRST Store Pointer to Virtual-Machine Control Structure VMREAD Read Field from Virtual-Machine Control Structure VMWRITE Write Field to Virtual-Machine Control Structure VMXOFF Leave VMX Operation VMXON Enter VMX Operation VMM Life Cycle The following items summarize the life cycle of a VMM and its guest software, as well as the interactions between them: Software enters VMX operation by executing a VMXON instruction. Using VM entries, a VMM can turn guests into VMs (one at a time). The VMM affects a VM entry using instructions VMLAUNCH and VMRESUME; it regains control using VM exits. VM exits transfer control to an entry point specified by the VMM. The VMM can take action appropriate to the cause of the VM exit and then return to the VM using a VM entry. Eventually, the VMM may decide to shut itself down and leave VMX operation. It does so by executing the VMXOFF instruction. That’s enough for now!Related WorkOther hypervisor-related works and materials.Awesome virtualization (Introducing books, papers, projects, courses, CVEs, and other hypervisor hypervisor-related works) - https://github.com/Wenzel/awesome-virtualization7 Days to Virtualization: A Series on Hypervisor Development - (https://revers.engineering/7-days-to-virtualization-a-series-on-hypervisor-development/)At last, if you want to use hypervisors for debugging, researching, or reverse-engineering, you can use HyperDbg Debugger, as many innovative methods based on hypervisors are implemented in this debugger that will help you in your reversing journey.ConclusionIn this part, we study general keywords we should be aware of and create a simple testing environment for our future tests. In the next part, I will explain how to enable VMX on your machine using the driver we made above. Then we survey the rest of the virtualization, so see you in the next part.The second part is also available here.References[1] Intel® 64 and IA-32 architectures software developer’s manual combined volumes 3 (https://software.intel.com/en-us/articles/intel-sdm)[2] Hardware-assisted Virtualization (http://www.cs.cmu.edu/~412/lectures/L04_VTx.pdf)[3] Writing Windows Kernel Driver (https://resources.infosecinstitute.com/writing-a-windows-kernel-driver/)[4] What Is a Type 1 Hypervisor? – (http://www.virtualizationsoftware.com/type-1-hypervisors/)[5] Intel / AMD CPU Internals – (https://github.com/LordNoteworthy/cpu-internals)[6] Windows 10: Disable Signed Driver Enforcement – (https://ph.answers.acer.com/app/answers/detail/a_id/38288/~/windows-10%3A-disable-signed-driver-enforcement)[7] Instruction Set Mapping » VMX Instructions – (https://docs.oracle.com/cd/E36784_01/html/E36859/gntbx.html)[8] HyperDbg Documentation – (https://docs.hyperdbg.org)" }, { "title": "x64 Inline Assembly in Windows Driver Kit", "url": "/topics/inline-assembly-in-x64/", "categories": "kernel-mode, windows", "tags": "assembly-x64-visual-studio, wdk-assembly, windows-driver-kit-inline-assembly, x64-assembly-in-driver, x64-inline-assembly", "date": "2018-08-16 00:00:00 +0000", "snippet": "As my testing always interferes with running assembly directly in kernel-level and unfortunately Microsoft no longer supports x64 inline assembly through their compilers and as I always have struggle creating a simple inline assembly project so I decided to create a post to describe how to create a Windows Driver Kit project with Inline assembly to run kernel code directly in a kernel driver.This terms only applies to x64 Windows Kernel Driver, you can use _asm and asm directly in your code in x86 drivers and user-mode codes, Linux also supports x64 Inline assembly in their kernel modules.Let’s get down to business.First of all, you should have Windows WDK support in your visual studio, just follow the steps here, then after downloading Windows Driver Kit and Windows SDK then you can create a WDK Project in your visual studio like this :So I created a project named MyDriver after that you should add some files to your project. As you might know, you should create a .asm file beside your .c (code) and .h (header) files thus I created two files named (source.cpp)and the other file is source.asm.I made a simple assembly function (Masm) along with a simple driver entry function, you can add these files to your project. For example the following lines for source.asm.PUBLIC MainAsm.code _textMainAsm PROC PUBLICpush rax; do stuffs herepop raxretMainAsm ENDPMainAsm2 PROC PUBLICint 3retMainAsm2 ENDP ENDThere are some important notes here,  Your function should ends with ret, if not it never comes back from your function and as long as you are in the kernel, every error lead to a BSOD. The other important note is you are responsible for the calling convention used in your driver (for passing arguments or clearing the stack). The default calling convention in Windows x64 driver is fastcall so in most situations, all the parameters placed in the registers and you are not forced to clear anything before ret.The other file is driver entry (Source.cpp), you can use something like :#include &lt;ntddk.h&gt;#include &lt;wdf.h&gt;extern void inline MainAsm(void);extern void inline MainAsm2(void);VOID Unload(IN WDFDRIVER Driver){}NTSTATUS MyDriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath){ // NTSTATUS variable to record success or failure NTSTATUS status = STATUS_SUCCESS; // Allocate the driver configuration object WDF_DRIVER_CONFIG config; WDF_DRIVER_CONFIG_INIT( &amp;config, WDF_NO_EVENT_CALLBACK // This is a non-pnp driver. ); config.EvtDriverUnload = Unload; MainAsm(); return status;}Please note that I defined MainAsm and MainAsm2 from the previous assembly file as function name so that I can use it directly in my DriverEntry or etc.Make sure to change the architecture to x64.That’s it, build your project and you can use tools like OSR Driver Loader from osronline.com to load your driver.Almost in all current Windows versions (&lt;=Window 7) you should disable Driver Signature Enforcement, a protection for Windows to prevent non-verified drivers or rootkits to go in ring 0.Update :If you see some errors like this in your building process,Then make sure to add your driver entry in properties -&gt; Linker -&gt; All Options -&gt; search for the entry point. That should solve the problem.I also added the source of this project on my GitHub, take a look at :[https://github.com/SinaKarvandi/x64-Driver-Inline-Assembly/]" }, { "title": "Inside Windows Page Frame Number (PFN) – Part 2", "url": "/topics/inside-windows-page-frame-number-part2/", "categories": "debugging, kernel-mode, windows", "tags": "migetphysicaladdress, mivatopfn, mmavailablepages, mmgetphysicaladdress, mmgetvirtualforphysical, mmnumberofphysicalpages, mmresidentavailablepages, nt_mmpfn, page-management-in-windows, pfn, pfn-database", "date": "2018-08-07 00:00:00 +0000", "snippet": "Hey there,In the previous part, I’d explained about Page Frame Number and its importance in the OSs architecture. In this part, I’ll trace PFN more practically. I strongly recommend to read the first part, to make sure you didn’t miss anything about basic concepts.As I described in the previous part, the PFN database is located at nt!MmPFNDatabase, in the previous versions of Windows (&lt;Windows 10) it was statically located at 0xFFFFFA8000000000 but in Windows 10, it’s subject to ASLR.Converting Physical Address to Virtual Address and Virtual Address to Physical Address MmGetVirtualForPhysical (PA -&gt; VA) One of the purposes of using PFN database is for converting physical address to virtual address but in Windows, you can simply call nt!MmGetVirtualForPhysical and convert your physical address to virtual address, you can see a complete list of Memory Manager Routines in MSDN.The MmGetVirtualForPhysical is the opposite of MmGetPhysicalAddress as it converts virtual address to physical address and MmXXX before every function in Windows kernel means it’s memory-management routine.If we decompile this function using IDA Pro:So the source is simply like this:__int64 __fastcall MmGetVirtualForPhysical(unsigned __int64 a1){ return (a1 &amp; 0xFFF) + (*(_QWORD *)(48 * (a1 &gt;&gt; 12) - 6047313952760i64) &lt;&lt; 25 &gt;&gt; 16);}MmGetPhysicalAddress (VA -&gt; PA)This function is responsible for converting Virtual Address to Physical address and will eventually call MiGetPhysicalAddress so we keep investigating and if you decompile MiGetPhysicalAddress you’ll see some interesting function: nt!MiVaToPfn.MiVaToPfn, as it seems, is used for converting the Virtual address to PFN.unsigned __int64 __fastcall MiVaToPfn(__int64 a1);Let’s track a special virtual address and find its corresponding PFN Record in PFN Database.Tracking PFN RecordsAs I described in the first part, poi(nt!MmPfnDatabase)  can be used to get PFN Database Address:0: kd&gt; ? poi(nt!MmPfnDatabase)Evaluate expression: -13194139533312 = fffff400`00000000Now, let choose a kernel location to see PFN Record e.g fffff80231612000 (the location where nt module is loaded). (If you want to find user-mode addresses then you should change your context (CR3) and perform the same thing based on your virtual address.)0: kd&gt; lm m ntBrowse full module liststart end module namefffff802`31612000 fffff802`31e9b000 nt (pdb symbols)I convert the fffff80231612000 to its corresponding physical address using !vtop 0 Address.1: kd&gt; !vtop 0 fffff80231612000Amd64VtoP: Virt fffff802`31612000, pagedir 1aa000Amd64VtoP: PML4E 1aaf80Amd64VtoP: PDPE 909040Amd64VtoP: PDE 90ac58Amd64VtoP: PTE 914090Amd64VtoP: Mapped phys 2012000Virtual address fffff80231612000 translates to physical address 2012000.As you can see it maps to 2012000 and both results of physical and virtual addresses are the same.1: kd&gt; !dc 2012000# 2012000 00905a4d 00000003 00000004 0000ffff MZ..............# 2012010 000000b8 00000000 00000040 00000000 ........@.......# 2012020 00000000 00000000 00000000 00000000 ................# 2012030 00000000 00000000 00000000 00000108 ................# 2012040 0eba1f0e cd09b400 4c01b821 685421cd ........!..L.!Th# 2012050 70207369 72676f72 63206d61 6f6e6e61 is program canno# 2012060 65622074 6e757220 206e6920 20534f44 t be run in DOS # 2012070 65646f6d 0a0d0d2e 00000024 00000000 mode....$.......1: kd&gt; dc fffff802`31612000fffff802`31612000 00905a4d 00000003 00000004 0000ffff MZ..............fffff802`31612010 000000b8 00000000 00000040 00000000 ........@.......fffff802`31612020 00000000 00000000 00000000 00000000 ................fffff802`31612030 00000000 00000000 00000000 00000108 ................fffff802`31612040 0eba1f0e cd09b400 4c01b821 685421cd ...... .!..L.!Thfffff802`31612050 70207369 72676f72 63206d61 6f6e6e61 is program cannofffff802`31612060 65622074 6e757220 206e6920 20534f44 t be run in DOS fffff802`31612070 65646f6d 0a0d0d2e 00000024 00000000 mode....$.......We know that the default size of every page in Windows is 4086 Bytes so if we divide our physical address by 4096 (1000h) we can get our PFN Record Number.In our case, (2012000h/1000h) is equal to 2012, so our PFN Record Number is 2012.There is also another command called !pte which gives your PFN Number too. Let’s verify if we found the correct value.Ok, !pte and our result are the same. Now let’s find its record. First, you should get the nt!_MmPfn’s size.1: kd&gt; ?? sizeof(nt!_MmPfn)unsigned int64 0x30From the above, we have the address of nt!MmPfnDatabase (fffff400`00000000) and every record is exactly 0x30 Bytes so the final command is something like this:dt nt!_MmPfn (Address of MmPfnDatabase) + (PFN Record Number * _MmPfn size).1: kd&gt; dt nt!_MmPfn (fffff400`00000000 + (0x2012 * 0x30)) +0x000 ListEntry : _LIST_ENTRY [ 0x00000000`00000000 - 0xfffffafc`0118b090 ] +0x000 TreeNode : _RTL_BALANCED_NODE +0x000 u1 : &lt;unnamed-tag&gt; +0x008 PteAddress : 0xfffffafc`0118b090 _MMPTE +0x008 VolatilePteAddress : 0xfffffafc`0118b090 Void +0x008 PteLong : 0xfffffafc`0118b090 +0x010 OriginalPte : _MMPTE +0x018 u2 : _MIPFNBLINK +0x020 u3 : &lt;unnamed-tag&gt; +0x024 NodeBlinkLow : 0 +0x026 Unused : 0y0000 +0x026 Unused2 : 0y0000 +0x027 ViewCount : 0 '' +0x027 NodeFlinkLow : 0 '' +0x028 u4 : &lt;unnamed-tag&gt;We can also use !pfn to get the details of our PFN Record directly.1: kd&gt; !pfn 2012 PFN 00002012 at address FFFFF40000060360 flink 00000000 blink / share count 00000001 pteaddress FFFFFAFC0118B090 reference count 0001 used entry count 0000 Cached color 0 Priority 4 restore pte 00000080 containing page 000914 Active M Modified You can see, we computed the correct value previously.1: kd&gt; ?(fffff400`00000000 + (0x2012 * 0x30))Evaluate expression: -13194139139232 = fffff400`00060360You can also traverse through all the PFN Entries, just remember to get the maximum allocated pages (Physical or Virtual).These variables can help you get precise statistics about memory allocations. MmAvailablePages: Total number of available pages on the system the sum of the pages on the zeroed, free, and standby lists MmResidentAvailablePages: Total number of physical pages that would be available if every process were at its minimum working set size MmNumberOfPhysicalPages: Total number of physical pages available on the systemPFN Data StructuresThis section is derived from here, which worth reading:Free, Zero and Bad listsWe start off by discussing these states - they are the simplest to understand. Pages which can take on any of these states (A flag in the _MMPTE.u3.e1.PageLocation) are kept in their own lists of Free pages (ready to be used), Zero pages (already cleared) or Bad pages (will never be used).Active Pages: PteAddress points at a hardware PTE.If the PFN Type is set to Active, then the physical page is used by something. The most important thing to realize is that a valid physical page (frame) must be managed by a PTE.  Since that PTE record must also be accessible to the kernel, it must be mapped in the kernel’s virtual address space.When the PFN is Active, it contains 3 important pieces of information: The virtual address of the PTE that is managing this physical page (in _MMPFN.PteAddress). The Page Frame (Physical page number) of the PTE that is managing this physical page (in _MMPFN.u4.PteFrame). Note these two values provide the virtual and physical address of the PTE. The OriginalPte value (usually the prototype PTE which controls this page). When Windows installs a hardware PTE from a prototype PTE, it will copy the original prototype PTE into this field. If you want to know about prototype PTE, then there is a good article here.From the first line, you should understand how to change page attributes for your physical memory.Let’s see…The Page Table Entry should be converted to binary to see its attributes. First I get the PTE Adress values and then convert it to binary format using .formats.Note that I choose the first one because its Physical-Page Base Address is equal to 2012 (our PFN Number), the lowest 12 bits are used for attributes while bits 12 to 52 are used to show Physical Address.0x02012963 —–&gt;  10000000010010(Used for Physical Address),100101100011(used for attributes.)10000000010010 —–&gt; 0x2012The following image shows its bits position and meaning. (The last one is PTE.)Windbg !vm extensionAs I mentioned in the previous part, there is also another extension, called !vm.The Windbg documentation says: !vmThe !vm extension displays summary information about virtual memory use statistics on the target system. !vm [Flags]Parameters FlagsSpecifies what information will be displayed in the output from this command. This can be any sum of the following bits. The default is 0, which causes the display to include system-wide virtual memory statistics as well as memory statistics for each process. Bit 0 (0x1)Causes the display to omit process-specific statistics. Bit 1 (0x2)Causes the display to include memory management thread stacks. Bit 2 (0x4)(Windows XP and later) Causes the display to include terminal server memory usage. Bit 3 (0x8)(Windows XP and later) Causes the display to include the page file write log. Bit 4 (0x10)(Windows XP and later) Causes the display to include working set owner thread stacks. Bit 5 (0x20)(Windows Vista and later) Causes the display to include kernel virtual address usage.The above extension can be used to get some statistics about memory allocation and the most important field for us is PFN Array Commit.1: kd&gt; !vmPage File: \\??\\C:\\pagefile.sys Current: 1179648 Kb Free Space: 1173572 Kb Minimum: 1179648 Kb Maximum: 6291456 KbPage File: \\??\\C:\\swapfile.sys Current: 16384 Kb Free Space: 16376 Kb Minimum: 16384 Kb Maximum: 3144940 KbNo Name for Paging File Current: 8388084 Kb Free Space: 8032544 Kb Minimum: 8388084 Kb Maximum: 8388084 KbPhysical Memory: 524157 ( 2096628 Kb)Available Pages: 313062 ( 1252248 Kb)ResAvail Pages: 451681 ( 1806724 Kb)Locked IO Pages: 0 ( 0 Kb)Free System PTEs: 4294977640 (17179910560 Kb)Modified Pages: 11735 ( 46940 Kb)Modified PF Pages: 11693 ( 46772 Kb)Modified No Write Pages: 0 ( 0 Kb)NonPagedPool Usage: 0 ( 0 Kb)NonPagedPoolNx Usage: 14108 ( 56432 Kb)NonPagedPool Max: 4294967296 (17179869184 Kb)PagedPool 0: 27632 ( 110528 Kb)PagedPool 1: 2344 ( 9376 Kb)PagedPool 2: 2402 ( 9608 Kb)PagedPool 3: 2393 ( 9572 Kb)PagedPool 4: 2308 ( 9232 Kb)PagedPool Usage: 37079 ( 148316 Kb)PagedPool Maximum: 4294967296 (17179869184 Kb)Processor Commit: 728 ( 2912 Kb)Session Commit: 2947 ( 11788 Kb)Shared Commit: 24440 ( 97760 Kb)Special Pool: 0 ( 0 Kb)Kernel Stacks: 6825 ( 27300 Kb)Pages For MDLs: 1378 ( 5512 Kb)Pages For AWE: 0 ( 0 Kb)NonPagedPool Commit: 12147 ( 48588 Kb)PagedPool Commit: 37079 ( 148316 Kb)Driver Commit: 10370 ( 41480 Kb)Boot Commit: 2167 ( 8668 Kb)PFN Array Commit: 6687 ( 26748 Kb)System PageTables: 446 ( 1784 Kb)ProcessLockedFilePages: 15 ( 60 Kb)Pagefile Hash Pages: 3 ( 12 Kb)Sum System Commit: 105232 ( 420928 Kb)Total Private: 161822 ( 647288 Kb)Misc/Transient Commit: 2124 ( 8496 Kb)Committed pages: 269178 ( 1076712 Kb)Commit limit: 819069 ( 3276276 Kb) Pid ImageName Commit SharedCommit Debt 81c MsMpEng.exe 112764 Kb 7400 Kb 0 Kb... f10 userinit.exe 72 Kb 0 Kb 0 Kb 830 coredpussvr.exe 72 Kb 0 Kb 0 Kb 1f0 smss.exe 72 Kb 0 Kb 0 KbThat’s it guys, hope you enjoy reading this topic. If you have any question, then you can use the comments.Have fun surveying Windows!References[Inside Windows Page Frame Number (PFN) – Part 1] (https://rayanfam.com/topics/inside-windows-page-frame-number-part1/)  [Memory Manager Routines] (https://msdn.microsoft.com/en-us/library/windows/hardware/ff554435(v=vs.85).aspx)  [Page Frame Number Database] (https://flylib.com/books/en/4.491.1.69/1/)[Rekall and the windows PFN database] (http://blog.rekall-forensic.com/2016/05/rekall-and-windows-pfn-database.html)[Prototype PTEs] (https://www.codemachine.com/article_protopte.html)" }, { "title": "Cisco switch security features cheatsheet", "url": "/topics/cisco-switch-sec-cheatsheet/", "categories": "cisco, network, security", "tags": "cisco, defense, ios, port, port-security, security, switch, switchport", "date": "2018-07-20 00:00:00 +0000", "snippet": "Cisco switches (running IOS) have plenty of features that are critical to modern networks. Some are Cisco security features that eliminate several important attack vectors on layer 2. This is arguably the most important defense mechanism because ACLs and security mechanisms on software (layer 7) will sometimes fall short protecting the network because of the extreme complexity of communication up in this layer. So the earlier you close the holes the better!As an example security features like protected ports can effectively harden lateral movement in windows networks (Active Directory domains), also while being so dead simple compared to more advanced methods implemented on top of active directory itself.In this post I will give you the commands needed to implement some security features in a Cisco switch in a cheetsheet like manner.It is important to fully understand what each feature will do, as failing to do so and running the commands blindly may cause disruption in your network. Just look up each one and read about it. :)Reading official Cisco CCNP books is super recommended!Port Securityint INTERFACE switchport mode access switchport access vlan 123 #port security configuration starts here: switchport port-security maximum # switchport port-security aging type inactive switchport port-security aging time 5 switchport port-security violation restrict switchport port-security mac-address MAC switchport port-security mac-address stickyThese two commands show you port-security stats and make troubleshooting easier:show port-sec addressshow port-sec interface INTERFACEDHCP Snooping#(conf) ip dhcp snooping ip dhcp snooping vlan #interface INTERFACE ip dhcp snooping trust int USER-INTERFACE ip dhcp snooping limit rate #(pps)Related show command:show ip dhcp snoopingDynamic ARP Inspectionip arp inspection ip arp inspection vlan 123interface INTERFACE ip arp inspection trust interface USER-INTERFACE ip arp inspection limit rate #(pps)Related show command:show ip arp inspection vlan 123IP Source Guard It requires DHCP snooping (or static ip/mac bindings)Port based:interface INTERFACEip verify source(ip) port-security(mac)Creating manual entries:ip source binding MAC vlan # IP_ADDRESS interface INTERFACERelated show command:show ip source bindingProtected portsPorts that cannot communicate with each other directly.##private vlan edge aka protected ports : no direct traffic between those ports##interface INTERFACEswitchport protectedSpanning Tress root guardint INTERFACE spanning-tree guard root superior bpduSTP BPDU Guard with Spanning tree port-fastspanning-tree bpduguard enableStorm Controlinterface INTERFACE #(do not clip anymore – all specified traffic is dropped until end of duration [1s]) storm-conftrol broadcast level (bbp | pps | %) # # show storm-control b|m|u storm-control action ACTIONI hope you like this post.I am looking forward to improving this post using your contributions in a wiki-like manner. so if you think of any other feature which would be nice to be included in this post, please comment or email me and I will add it here. Thanks :)" }, { "title": "Inside Windows Page Frame Number (PFN) - Part 1", "url": "/topics/inside-windows-page-frame-number-part1/", "categories": "debugging, kernel-mode, windows", "tags": "freed-list, mizeropagethread, mmpfndatabase, page-frame-number, page-management-in-windows, pfn, pfn-database, pfn-in-windows, pfn_list_corrupt, standby-list, windows-mmpfn-structure, windows-paging, zero-thread", "date": "2018-07-19 00:00:00 +0000", "snippet": "Introduction (Page Frame Number)Windows and almost all the OSs use Page Frame Number Database in order to have a track of virtually allocated pages to know which page must be freed or evicted or if a page needs to be cached and etc.All of these kinds of stuff manages through a list, called Page Frame Number (PFN). A long list of explanation about the states of every physically and virtually allocated pages and its corresponding attributes.In the rest of this post, I’ll explain about Windows implementation of Page Frame Number with lots of practical examples, the following part describes basic concepts implementations, you should also read the next part in order to see how can you use or change these attributes.If you’re familiar with non-PAE mode and PAE mode systems then I should note that in a non-PAE mode every PFN structure takes 24 bytes while in a PAE mode system this size increases to 28 so if your pages are 4096 bytes then allocates about 24 bytes more to keep tracks of every page.As you can see here: In non-PAE mode 24 bytes in the PFN database represents each 4 KB page of physical memory – this is a ratio of 170:1. In PAE mode 28 bytes represents each 4 KB page of physical memory – this is a ratio of 146:1. This means that roughly 6 MB or 7 MB is needed in the PFN database to describe each 1 GB of physical memory. This might not sound like much, but if you have a 32-bit system with 16 GB of physical memory, then it requires about 112 MB of the 2 GB of kernel virtual address space just to address the RAM. This is another reason why systems with 16 GB of physical memory or more will not allow the 3GB mode (also known as IncreaseUserVA) which increases the user virtual address space to 3 GB and decreases the kernel virtual address space to 1 GB on 32-bit systems.One of the benefits of having extended pages (e.g 2MB for every page) is that it needs lower amounts of MMPFN.Before start getting deep into the PFN, please remember the term “Page” is mostly used in the operating system level concepts whereas “Frame” is used in CPU Level concepts, therefore “Page” means virtual page and “Page Frame” means physical page.PFN ListsThe Page Frame Number consists of lists that describe the state of some pages, there are Active Lists which shows an active page (e.g in working sets or etc), Standby List which means a list that previously backed in the disk and the page itself can be emptied and reused without incurring a disk IO, Modified List which shows that the page is previously modified and somehow must be written to the disk, Freed List, as the name describes, it shows a page that is no longer needed to be maintained and can be freed and finally Zero List that describes a page that is free and has all zeroes (0) in it.A great picture derived from here which shows how the PFN database lists are related to each other :These lists are used to manage memory in “page faults” state in the way that everytime a “page fault” occurs, Windows tries to find an available page form, Zero List, if the list is empty then it gets one from Freed List and zeroes the page and use it, on the other hand, if the Freed List is also empty then it goes to the Standby List and zeroes that page.The Zero Page ThreadIn Windows, there is a thread with the priority of 0 which is responsible for zeroing memory when system is idle and is the only thread in the entire system that runs at priority 0. (which is the lowest available priority because the user threads are at least 1). This thread clears the Freed List whenever is possible. Also, there is a function in Windows called RtlSecureZeroMemory() which frees a location securely but in the kernel perspective nt!KeZeroPages is responsible for freeing the pages.The following picture shows the behavior of zero-thread:Let’s find the Zero Thread!We know that it comes from system process, its priority is 0, this should be enough and nothing more is needed. First try to find System’s nt!_eprocess:!process 0 SystemNow we can see the System’s threads, the details of our target thread (zero-thread) are: THREAD ffffd4056ed00040 Cid 0004.0040 Teb: 0000000000000000 Win32Thread: 0000000000000000 WAIT: (WrFreePage) KernelMode Non-Alertable fffff8034637f148 NotificationEvent fffff80346380480 NotificationEvent Not impersonating DeviceMap ffff99832ae1b010 Owning Process ffffd4056ec56040 Image: System Attached Process N/A Image: N/A Wait Start TickCount 4910 Ticks: 4 (0:00:00:00.062) Context Switch Count 21023 IdealProcessor: 3 UserTime 00:00:00.000 KernelTime 00:00:01.109 Win32 Start Address nt!MiZeroPageThread (0xfffff80346144ed0) Stack Init ffffe700b7c14c90 Current ffffe700b7c14570 Base ffffe700b7c15000 Limit ffffe700b7c0f000 Call 0000000000000000 Priority 0 BasePriority 0 PriorityDecrement 0 IoPriority 2 PagePriority 5 Child-SP RetAddr Call Site ffffe700`b7c145b0 fffff803`46016f8a nt!KiSwapContext+0x76 ffffe700`b7c146f0 fffff803`46016951 nt!KiSwapThread+0x16a ffffe700`b7c147a0 fffff803`46014ba7 nt!KiCommitThreadWait+0x101 ffffe700`b7c14840 fffff803`461450b7 nt!KeWaitForMultipleObjects+0x217 ffffe700`b7c14920 fffff803`460bba37 nt!MiZeroPageThread+0x1e7 ffffe700`b7c14c10 fffff803`46173456 nt!PspSystemThreadStartup+0x47 ffffe700`b7c14c60 00000000`00000000 nt!KiStartSystemThread+0x16As you can see, its start address is at nt!MiZeroPageThread and its priority-level is 0 and if we see the call-stack then we can see nt!MiZeroPageThread was called previously.For more information please visit Hidden Costs of Memory Allocation.nt!_MMPFNThe Windows structure for PFN is nt!_MMPFN which you can see below :As you see, _MMPFN takes 28 bytes.PFN records are stored in the memory based on their physical address order which means you can always calculate the physical address with the help of PFN.Physical Address = PFN * page size(e.g 4096 Byte) + offsetThe address of the PFN database is located at nt!MmPfnDatabase, you can use the following example to get your PFN database address in Windbg.2: kd&gt; x nt!MmPfnDatabasefffff800`a2a76048 nt!MmPfnDatabase = &lt;no type information&gt;!memusageAnother very useful command in windbg is !memusage, this command gives almost everything about PFN and pages in your memory layout and its corresponding details (e.g files, fonts, system drivers, DLL modules, executable files including their names and their paging bits modifications).A brief sample of this command is shown below :2: kd&gt; !memusage loading PFN databaseloading (100% complete)Compiling memory usage data (99% Complete). Zeroed: 9841 ( 39364 kb) Free: 113298 ( 453192 kb) Standby: 105520 ( 422080 kb) Modified: 7923 ( 31692 kb) ModifiedNoWrite: 0 ( 0 kb) Active/Valid: 286963 ( 1147852 kb) Transition: 45 ( 180 kb) SLIST/Bad: 567 ( 2268 kb) Unknown: 0 ( 0 kb) TOTAL: 524157 ( 2096628 kb)Dangling Yes Commit: 140 ( 560 kb) Dangling No Commit: 37589 ( 150356 kb) Building kernel map Finished building kernel map (Master1 0 for 80) (Master1 0 for 580) (Master1 0 for 800) (Master1 0 for 980)Scanning PFN database - (97% complete) (Master1 0 for 7d100)Scanning PFN database - (100% complete) Usage Summary (in Kb):Control Valid Standby Dirty Shared Locked PageTables nameffffffffd 11288 0 0 0 11288 0 AWEffffd4056ec4c460 0 112 0 0 0 0 mapped_file( LeelUIsl.ttf )ffffd4056ec4c8f0 0 160 0 0 0 0 mapped_file( malgun.ttf )ffffd4056ec4d6b0 0 108 0 0 0 0 mapped_file( framd.ttf ).....ffffd4057034ecd0 328 148 0 0 0 0 mapped_file( usbport.sys )ffffd4057034f0e0 48 28 0 0 0 0 mapped_file( mouclass.sys )ffffd4057034f7d0 32 28 0 0 0 0 mapped_file( serenum.sys )ffffd405703521a0 0 20 0 0 0 0 mapped_file( swenum.sys ).....-------- 0 20 0 ----- ----- 0 session 0 0-------- 4 0 0 ----- ----- 0 session 0 ffffe700b8b45000-------- 4 0 0 ----- ----- 0 session 1 ffffe700b8ead000-------- 32520 0 84 ----- ----- 1324 process ( System ) ffffd4056ec56040-------- 2676 0 0 ----- ----- 304 process ( msdtc.exe ) ffffd405717567c0-------- 4444 0 0 ----- ----- 368 process ( WmiPrvSE.exe ) ffffd405718057c0-------- 37756 0 60 ----- ----- 1028 process ( SearchUI.exe ) ffffd405718e87c0.....-------- 8 0 0 ----- 0 ----- driver ( condrv.sys )-------- 8 0 0 ----- 0 ----- driver ( WdNisDrv.sys )-------- 52 0 0 ----- 0 ----- driver ( peauth.sys )-------- 24744 0 0 ----- 0 ----- ( PFN Database )Summary 1147852 422260 31692 129996 204428 25156 Total..... b45b 64 0 0 60 0 0 Page File Section b56b 4 0 0 4 0 0 Page File Section b7ec 84 0 0 64 0 0 Page File Section b905 12 0 0 0 0 0 Page File Section bf5c 4 0 0 0 0 0 Page File Section .....Note that !memusage takes a long time to finish its probes.What if you want to know more about these pages? The Windbg help document mentioned : Remarks You can use the !vm extension command to analyze virtual memory use. This extension is typically more useful than !memusage. For more information about memory management, see Microsoft Windows Internals, by Mark Russinovich and David Solomon. (This book may not be available in some languages and countries.) The !pfn extension command can be used to display a particular page frame entry in the PFN database.Now we want survey among these command in a more detailed perspective.That’s enough for now… I try to make another part that describes the PFN more practically, so make sure to check the blog more frequently.The second part is also published at Inside Windows Page Frame Number (PFN) – Part 2.References [Getting Physical: Extreme abuse of Intel-based Paging Systems - Part 2 - Windows] (https://www.coresecurity.com/blog/getting-physical-extreme-abuse-of-intel-based-paging-systems-part-2-windows) [Page Frame Number (PFN) database] (https://social.technet.microsoft.com/wiki/contents/articles/15259.page-frame-number-pfn-database.aspx) [Exploring Windows virtual memory management] (https://www.triplefault.io/2017/08/exploring-windows-virtual-memory.html) [Page Frame Number Database] (https://flylib.com/books/en/4.491.1.69/1/) [PDC10: Mysteries of Windows Memory Management Revealed: Part Two] (https://blogs.msdn.microsoft.com/tims/2010/10/29/pdc10-mysteries-of-windows-memory-management-revealed-part-two/)" }, { "title": "Captive portal detection with a working sample in all major OSs!", "url": "/topics/captive-portal-detection-sample/", "categories": "linux, network, sysadmin", "tags": "captive, captive-portal, covachilli, http, iptables, linux, network, nodogsplash, openwrt, router, uhttpd, wifidog", "date": "2018-07-15 00:00:00 +0000", "snippet": "Hi everyoneI’ve been working on a project which involves a developing a captive portal system from scratch. and I’m going to gradually post more of challenges we faced and the way we solved them too.But for now I’m going to talk about captive portal detection in different OSs and how we’ve implemented it. It is not a really difficult or lengthy concept but the sad thing is that it’s not very well documented or not even documented at all!Before beginning, I should note that there indeed are standard for captive portals detection and login, such as WISPr but they are not widely used as I know and they are mostly for embedded devices which do not have a browser to open login page and should be logged in automatically. We won’t have anything to do with WISPr protocol here.This setup is tested with Fedora and Ubuntu, Windows 10, Windows 8.1 and android.In order to find out whether there is a captive portal at work, the OS (or browser) should go through several steps; Firstly it should find out if internet is indeed connected or not (not disconnected completely), then it will find out that although connected to the internet, this device has limited connectivity (only DNS packets are transmitted and received). These two steps are enough for the OS to determine that it has some connection that is limited by the administrator, in some OSs this two steps are enough to detect captive portal. So making sure that only DNS packets are allowed would be enough. Firefox browser is like this and will show a bar indicating that you need to login to this network.The third step is necessary for other OSs especially Windows; all HTTP requests going out of the network should be responded with a 302 redirect.So it boils down to this: Only allowing DNS packets out of the network. Redirecting all HTTP requests with 302 (to the login page, but the page is not important in detection)Implementation on OpenWRTI will explain the steps necessary for detection in OpenWRT, using iptables and uhttpd, the default webserver on OpenWRT.IPTablesThis is really easy. Here are the rules:#start iptables for captive portal/usr/sbin/iptables -t filter -I FORWARD 1 --protocol tcp --sport 53 --jump ACCEPT/usr/sbin/iptables -t filter -I FORWARD 1 --protocol udp --sport 53 --jump ACCEPT/usr/sbin/iptables -t filter -I FORWARD 1 --protocol tcp --dport 53 --jump ACCEPT/usr/sbin/iptables -t filter -I FORWARD 1 --protocol udp --dport 53 --jump ACCEPT/usr/sbin/iptables -t filter -I FORWARD 5 -j DROP/usr/sbin/iptables -t nat -A prerouting_lan_rule --protocol tcp --dport 80 --jump DNAT --to-destination $(uci get network.lan.ipaddr):80#the line above does this, but you don't need to specify the ip manually#/usr/sbin/iptables -t nat -A prerouting_lan_rule --protocol tcp --dport 80 --jump DNAT --to-destination 192.168.100.1:80 The first four rules accept all packets with source or destination port of 53 (domain name system) The 5th rule denies all other connections the last rule redirects all web requests to our http server running on router on port 80. These rules work on OpenWRT, in order for them to work on other linuxes, you probably need to replace prerouting_lan_rule with PREROUTING. It is a very good idea to rate limit DNS packets per ip using hashlimit iptables module, As this setup has no defense against DNS tunnels and covert channels, effectively bypassing all restrictions. There sure are better ways to do this (such as only allowing outbound dport 53 and permitting established connections back, etc), but this guide focuses only on captive portal detection.HTTP RedirectThe webserver is only needed for redirect, so it can be a separate light webserver running on another port too (like 8000), you change DNAT port in iptables only. So this can be a very basic webserver.Configuring a redirect is pretty straightforward for every webserver you are using, But there wasn’t any documentation for uhttpd :/ , so I will explain redirect in uhttpd based on my experiments now.You should change /e_tc__/uhttpd/redir.json_ like this:{ "fallback": [ [ "if", [ "regex", "REQUEST_URI", [ "/*" ] ], [ "rewrite", "/" ] ] ]}It is a fallback redirect, so it will only kick in if the url does not exist. So it will not interfere with other things you will be doing on the webserver.If you have done these steps and the three requirements are satisfied, a browser will automatically open when you connect to the network with a windows system. If you happen to accidentally close that page, just open a HTTP website (or the official one http://msftconnecttest.com/redirect ) to reach the login again.Here are some links for further reading:https://en.wikipedia.org/wiki/Captive_portalhttps://www.secplicity.org/2016/08/26/lessons-defcon-2016-bypassing-captive-portals/Have fun (!) with captive portals :)" }, { "title": "Defeating malware's Anti-VM techniques (CPUID-Based Instructions)", "url": "/topics/defeating-malware-anti-vm-techniques-cpuid-based-instructions/", "categories": "debugging, hypervisor, malware", "tags": "anti-anti-vm, bypass-anti-vm, bypass-virtual-machine-detection, change-cpuid-virtualbox, change-cpuid-vmware, defeat-anti-vm", "date": "2018-06-13 00:00:00 +0000", "snippet": "[The picture of this post is taken by one of my best friends, Ahmad Ghazi in Chitgar Lake !]IntroductionYou should by now be aware of everything, cause the topic’s title clearly describes the contents of this post.As you know, almost all of the modern malware programs use some bunch of packers or protectors and using such tools cause malware to be weaponized with Anti-VM techniques which makes it impossible for reverse-engineers and analyzer to detect what’s happening inside the malware (or sometimes they implement their own methods to detect the VM presence).Reverse-engineers always prefer to run the malware inside a Virtual Machine environment to avoid their computer to be affected by malware programs and this gives them lots of features like creating a snapshot from malware’s (VM) previous state.In the rest of the post, I’m gonna show you some of the popular methods that use CPUID instruction in order to detect whether they’re running on a virtual machine or not.Why CPUID?CPUID is an instruction-level detection method and these kinds of methods are really hard to detect, as you know to trap on every execution of CPUID, you should either execute the instructions step by step(which is really slow and almost impossible) or instrument the target program. If you use instrumentation, then you might also defeat anti-instrument techniques.Let see some examples.ExamplesAs this great article in cyberbit describes:The first method: CPUID__: This instruction is executed with EAX=1 as input, the return value describes the processors features. The 31st bit of ECX on a physical machine will be equal to 0. On a guest VM it will equal to 1. The second method: “Hypervisor brand”: by calling CPUID with EAX=40000000 as input,1 the malware will get, as the return value, the virtualization vendor string in EAX, ECX, EDX. For example: Microsoft: “Microsoft HV” VMware : “VMwareVMware” The Practical ImplementationFor the first example I write the following code :#include "stdafx.h"#include &lt;iostream&gt;using namespace std;int main(){ bool IsUnderVM = false; __asm { xor eax, eax inc eax cpuid bt ecx, 0x1f jc UnderVM NotUnderVM: jmp NopInstr UnderVM: mov IsUnderVM, 0x1 NopInstr: nop } cout &lt;&lt; IsUnderVM; return 0;}The above code set eax=1 and executes the CPUID instruction, after that the result of ecx is checked to determine whether the 31st bit is set or not, the result is finally saved on “IsUnderVM” variable.Now the second example:#include "stdafx.h"#include &lt;iostream&gt;#include &lt;string&gt;using namespace std;int main(){ bool IsUnderVM = false; __asm { xor eax, eax mov eax,0x40000000 cpuid cmp ecx,0x4D566572 jne NopInstr cmp edx,0x65726177 jne NopInstr mov IsUnderVM, 0x1 NopInstr: nop } cout &lt;&lt; IsUnderVM; return 0;}The above code set eax=0x40000000 and then compares the ecx and edx against 0x4D566572 and 0x65726177 which are known and static values for VMWare products, like the first example, the result will finally save into “IsUnderVM”.Note that, this implementation of method only works on VMWare, but if you want it to work in other vendors like VirtualBox or Hyper-V, then you have to detect what is their vendor string, it’s simple but as long I currently don’t have VBox or Hyper-V VM then you should do it by yourselves if you want to test it on other vendors.Let see how it works!The following picture is the first example when running on a host machine.When it runs in a virtual environment, then the result is.The following picture is the second example when running on a host machine.When it runs in a virtual environment, then the result is.You can clearly see, the difference in execution under a virtual environment and non-virtual environment.How to defeat?Now you might ask yourselves, Is it possible to change the CPUID results of the target virtual machine from host perspective?Yep!Fortunately, all the popular virtual machine vendors give the host machine an opportunity to modify CPUID and CPU features. This is because everytime your virtual machine fetches a CPUID instruction and wants to execute it, a VM-Exit happens and now hypervisor passes the execution to VMM, so this the best time of modifying CPUID result to bypass the above Anti-VM techniques.In VMWare, you can find where your virtual machine is located, then find .vmx (config file) of your target VM then add the following line at the end of the file:cpuid.1.ecx="0---:----:----:----:----:----:----:----"Make sure to restart your VM and now the result of the first example is reversed!The bypass for the second method is really dependent on the implementation. This might not work in your case but in my case, I can easily add the following lines to the .vmx config file.cpuid.40000000.ecx="0000:0000:0000:0000:0000:0000:0000:0000"cpuid.40000000.edx="0000:0000:0000:0000:0000:0000:0000:0000"Now the second method gives the wrong results to the malware.There is a good question here, which you can read in order to perform the same thing in VirtualBox but as I don’t have VBox in my machine right now, then you should do it by yourself.The other vendors also give the same features to you so you can search for changing CPUID in your vendor and get the same results.SummaryIn this post, I described an Anti-Anti-VM technique in which you can use them to circumvent an Anti-VM malware. In the future posts, I’ll describe more about other Anti-VM techniques and the way you can defeat them, so make sure to stay connected!References [Anti-VM and Anti-Sandbox Explained] (https://www.cyberbit.com/blog/endpoint-security/anti-vm-and-anti-sandbox-explained) [Virtualbox, how to force a specific CPU to the guest] (https://superuser.com/questions/625648/virtualbox-how-to-force-a-specific-cpu-to-the-guest)" }, { "title": "PyKD Tutorial - part 2", "url": "/topics/pykd-tutorial-part2/", "categories": "debugging, kernel-mode, tutorials, user-mode", "tags": "getting-started-with-pykd, pykd-example, pykd-sample, pykd-scripts, pykd-tutorial, run-pykd-command", "date": "2018-05-26 00:00:00 +0000", "snippet": "The content of this post is the second part of PyKD Tutorials, so make sure to read this topic first, then continue reading this topic.BreakpointsBreakpoints are such useful things and can give you the power of analyzing programs in a better and easier way by using PyKD. In the API Reference they introduce setBp function in the following way :setBp( (long)offset [, (object)callback]) -&gt; breakpoint : Set software breakpoint on executiont C++ signature : class pykd::Breakpoint * __ptr64 setBp(unsigned __int64 [,class boost::python::api::object {lvalue}])setBp( (long)offset, (long)size, (int)accsessType [, (object)callback]) -&gt; breakpoint : Set hardware breakpoint C++ signature : class pykd::Breakpoint * __ptr64 setBp(unsigned __int64,unsigned __int64,unsigned long [,class boost::python::api::object {lvalue}])As you can see, setBp can give a pointer as its first argument and a python function as the second argument. Every time the pointer executes, your python function will be invoked. The second usage of setBp is for setting hardware breakpoints. Removing the all the breakpoints using pykd.removeAllBp(). Remove a breakpoint by its index removeBp(int).Searching through the memoryEven though searching for a string or a special byte is really straight in Windbg but you can also use PyKD in order to search through the memory.import pykdresult = pykd.searchMemory(0x0,0x7fffffff,"A")print(hex(result))And the result is :0:010&gt; !py c:\\users\\Sina\\desktop\\pykd-script.py0x5d6700ba0:010&gt; db 0x5d6700ba00000000`5d6700ba 41 e4 d4 aa 8f e4 0b 55-5f e4 d7 aa 8f e4 0b 55 A......U_......U00000000`5d6700ca 44 e4 d9 aa 8f e4 d6 aa-8e e4 f9 ab 8f e4 ab d3 D...............00000000`5d6700da 6f e4 f0 aa 8f e4 ab d3-6e e4 d0 ab 8f e4 ab d3 o.......n.......00000000`5d6700ea 53 e4 d7 aa 8f e4 db f8-54 e4 d7 aa 8f e4 d6 aa S.......T.......00000000`5d6700fa 18 e4 d7 aa 8f e4 ab d3-51 e4 d7 aa 8f e4 52 69 ........Q.....Ri00000000`5d67010a 63 68 d6 aa 8f e4 00 00-00 00 00 00 00 00 00 00 ch..............00000000`5d67011a 00 00 00 00 00 00 00 00-00 00 00 00 00 00 50 45 ..............PE00000000`5d67012a 00 00 64 86 06 00 01 76-e7 57 00 00 00 00 00 00 ..d....v.W......Edit a ByteThe equivalent of eb is setByte as follows:import pykdpykd.setByte(0x7fffd3d5d965,0x41)The above script is like :eb 0x7fffd3d5d965 0x41Other variants are setDWord,setDouble,setFloat.Changing XIPYou can use setIP in order to change the current RIP or EIP which is very useful in defeating with packers and protectors.import pykdpykd.setIP(0x7fffd3d5d94f)Set Symbol PathFor debugging purpose you can also set symbol path like this:pykd.setSymbolPath("srv*c:\\symbols*https://msdl.microsoft.com/download/symbols")0:003&gt; !py c:\\users\\Sina\\desktop\\pykd-script.py************* Symbol Path validation summary **************Response Time (ms) LocationDeferred srv*c:\\symbols*https://msdl.microsoft.com/download/symbolsStep and Step-out and TraceInstead of using p and t you can use its equivalent pykd.step() and pykd.stepout() and pykd.trace().Disassemble MemoryThe following example describes how to disassemble the memory at a specific address.import pykdresult = pykd.disasm(0x7fffd3d5d956)print(result)The result depends on the location. e.g :0:003&gt; !py c:\\users\\Sina\\desktop\\pykd-script.py00007fff`d3d5d956 66660f1f840000000000 nop word ptr [rax+rax]Further ReadingIn the above post, I tried to describe the main features of PyKD and how can it be used to ease the reverse engineering process, if you want to read more about PyKD API References take a look at this link, there is also a good article here worth to read." }, { "title": "PyKD Tutorial - part 1", "url": "/topics/pykd-tutorial-part1/", "categories": "debugging, kernel-mode, tutorials, user-mode, windows", "tags": "getting-started-with-pykd, pykd-example, pykd-sample, pykd-scripts, pykd-tutorial, run-pykd-command", "date": "2018-05-25 00:00:00 +0000", "snippet": "Using windbg script syntax is such annoying thing that almost all reverse engineers have problems dealing with it but automating debugging gives such a power that can’t be easily ignored. A good solution to solve this problem is using the power and simplicity of Python and Windbg together. If you aware, Windbg also supports c-like binaries as extensions so there is a praiseworthy tool called PyKD which does the hard thing and connects Python and Windbg together in a straight and usable way. The purpose of PyKD, as they mentioned, is : This project can help to automate debugging and crash dump analysis using Python. It allows one to take the best from both worlds: the expressiveness and convenience of Python with the power of WinDbg!You can download PyKD at this link.Setup PyKDTo find the main extension binary files, you should find the latest version of the Bootstrapper and download its x86 and x64 versions depending to your needs, after extracting the binary files (pykd.dll) you should load it in Windbg with the following command :.load C:\\Users\\Sina\\Desktop\\pykd\\x64\\pykd.dllIn order to see if it successfully loaded or not, you should execute the following command, if you see something like this, then you’re good to go.0:000&gt; !helpusage:!help print this text!info list installed python interpreters!select version change default version of a python interpreter!py [version] [options] [file] run python script or REPL Version: -2 : use Python2 -2.x : use Python2.x -3 : use Python3 -3.x : use Python3.x Options: -g --global : run code in the common namespace -l --local : run code in the isolated namespace -m --module : run module as the __main__ module ( see the python command line option -m ) command samples: "!py" : run REPL "!py --local" : run REPL in the isolated namespace "!py -g script.py 10 "string"" : run a script file with an argument in the commom namespace "!py -m module_name" : run a named module as the __main__!pip [version] [args] run pip package manager Version: -2 : use Python2 -2.x : use Python2.x -3 : use Python3 -3.x : use Python3.x pip command samples: "pip list" : show all installed packagies "pip install pykd" : install pykd "pip install --upgrade pykd" : upgrade pykd to the latest version "pip show pykd" : show info about pykd packageIf you saw the above command suggestions, one of the interesting commands which can be used to update the PyKD is :!pip install --upgrade pykdBut actually, I prefer to compile the latest version from its source code rather than updating or using the PyKD.dll directly. That’s enough for the setting up and starting, in the rest of the post, we’re getting started with some useful samples of using PyKD. But the right way to get PyKD is to download its latest release and then find a file named “pykd.pyd” among other DLL files then load .pyd file.Using PyKD FeaturesThis section describes the general functions of PyKD.Executing CommandThe simplest thing is using the PyKD to execute and get the command result, it can be done using the following script in which r is our command and we simply print the result. You can also assign the results to a variable and separate them using Python’s regular string function.import pykdprint pykd.dbgCommand("r")You should save the above script into a file (e.g pykd-script.py) then load it in Windbg using the following command :0:000&gt; !py c:\\users\\Sina\\desktop\\pykd-script.pyrax=0000000000000000 rbx=0000000000000010 rcx=00007fffd3d5a434rdx=0000000000000000 rsi=00007fffd3de4090 rdi=00007fffd3de4740rip=00007fffd3d8d02c rsp=0000000b419ef3b0 rbp=0000000000000000 r8=0000000b419ef3a8 r9=0000000000000000 r10=0000000000000000r11=0000000000000246 r12=0000000000000040 r13=0000000000000000r14=0000000b41a63000 r15=000001d8da130000iopl=0 nv up ei pl zr na po nccs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246ntdll!LdrInitShimEngineDynamic+0x34c:00007fff`d3d8d02c cc int 3As you see the registers’ value is shown above, I usually use these kinds of scripts with t (step in) and p (step) to simulate an instrumenting environment and check what is going on (e.g a combination of instructions and registers’ value and its corresponding memory values.) even though this operation is too slow but still usable for special cases.Getting Registers valueA better way of getting registers is using the  following sample :import pykdaddr = hex(pykd.reg("rsp"))print(addr)Continue to runThe following command is the equivalent of go in PyKD :pykd.go()Read the content of the memoryTo read the contents of a special virtual address you should use something like this :import pykdaddr = pykd.reg("rip")value = pykd.loadBytes(addr,16)print(value)The result is :0:010&gt; !py c:\\users\\Sina\\desktop\\pykd-script.py[204, 195, 204, 204, 204, 204, 204, 204, 15, 31, 132, 0, 0, 0, 0, 0]The other variants of Load functions are loadAnsiString,loadBytes,loadCStr,loadChars,loadDWords,loadDoubles and etc.Comparing MemoryThe following script returns true if the contents of memory in two virtual addresses are equal otherwise the result is false.import pykdaddr1 = 0x00007fffd3d31596addr2 = 0x00007fffd3d31597result = pykd.compareMemory(addr1,addr2,100)print(result)DetachAs the documentation suggests,pykd.detachAllProcesses() ===&gt; Detach from all process and resume all their threads&amp;pykd.detachProcess() ===&gt; Stop process debuggingFind Nearest Valid Memory LocationThe following script gives the nearest valid memory location, near to 0x0.import pykdresult = pykd.findMemoryRegion(0x0)print(hex(result[0]))The result is :0:003&gt; !py c:\\users\\Sina\\desktop\\pykd-script.py0x5d6700000:003&gt; dc 0x5d67000000000000`5d670000 00905a4d 00000003 00000004 0000ffff MZ..............00000000`5d670010 000000b8 00000000 00000040 00000000 ........@.......00000000`5d670020 00000000 00000000 00000000 00000000 ................00000000`5d670030 00000000 00000000 00000000 00000128 ............(...00000000`5d670040 0eba1f0e cd09b400 4c01b821 685421cd ........!..L.!Th00000000`5d670050 70207369 72676f72 63206d61 6f6e6e61 is program canno00000000`5d670060 65622074 6e757220 206e6920 20534f44 t be run in DOS 00000000`5d670070 65646f6d 0a0d0d2e 00000024 00000000 mode....$.......Finding Function NameIf you want to find the what function is located at a special address based on symbols, you should use findSymbol.import pykdresult = pykd.findSymbol(0x00007fffd3d5d960)print(result)The result is :0:003&gt; !py c:\\users\\Sina\\desktop\\pykd-script.pyntdll!DbgBreakPointGet Current Stack Frameimport pykdresult = pykd.getFrame()print(result)The result is :0:003&gt; !py c:\\users\\Sina\\desktop\\pykd-script.pyFrame: IP=7fffd3d5d960 Return=7fffd3d89bbb Frame Offset=b41effa70 Stack Offset=b41effa780:003&gt; dc rsp0000000b`41effa78 d3d89bbb 00007fff 00000000 00000000 ................0000000b`41effa88 00000000 00000000 00000000 00000000 ................0000000b`41effa98 00000000 00000000 00000000 00000000 ................0000000b`41effaa8 d18e3034 00007fff 00000000 00000000 40..............0000000b`41effab8 00000000 00000000 00000000 00000000 ................0000000b`41effac8 00000000 00000000 00000000 00000000 ................0000000b`41effad8 d3d31551 00007fff 00000000 00000000 Q...............0000000b`41effae8 00000000 00000000 00000000 00000000 ................pykd.getStack() also gives a list of stack frame objects.Last Exceptionimport pykdresult = pykd.getLastException()print(result)The result is :0:003&gt; !py c:\\users\\Sina\\desktop\\pykd-script.pyFirstChance= TrueExceptionCode= 0x80000003ExceptionFlags= 0x0ExceptionRecord= 0x0ExceptionAddress= 0x7fffd3d5d960Param[0]= 0x0Finding Function LocationTo get where a special function located you can use the following code : It’s like executing x KERNEL32!CreateFileW in Windbg command-line.import pykdresult = pykd.getOffset("KERNEL32!CreateFileW")print(result)The result is :0:003&gt; !py c:\\users\\Sina\\desktop\\pykd-script.py0x7fffd18f0940L0:003&gt; x kernel32!CreateFileW00007fff`d18f0940 KERNEL32!CreateFileW (&lt;no parameter info&gt;)Get System Versionimport pykdresult = pykd.getSystemVersion()print(result)example result :0:003&gt; !py c:\\users\\Sina\\desktop\\pykd-script.pyMajor Version: 10Minor Version: 0Build: 17134Description: 17134.1.amd64fre.rs4_release.180410-1804Getting Page AttributesOne of the important functions of PyKD is getting the page attributes.import pykdaddr1 = pykd.reg("rip")result = pykd.getVaProtect(addr1)print("RIP Attributes : " + str(result))addr2 = pykd.reg("rsp")result = pykd.getVaProtect(addr2)print("RSP Attributes : " + str(result))The result is :0:003&gt; !py c:\\users\\Sina\\desktop\\pykd-script.pyRIP Attributes : PageExecuteReadRSP Attributes : PageReadWriteThere is also an important function called isValid which can be used to detect whether a virtual address is valid or not.Reading and writing MSR RegisterIf you are in a kernel debugging, you could read MSR registers using pykd.rdmsr(value).import pykdresult = pykd.rdmsr(0x80000082)print(result)To write on a specific MSR you can use pykd.wrmsr(Address, Value). That’s enough for now, I’ll write the rest of this post another time in part 2, so make sure check blog more frequently. :) The second part is also published here!" }, { "title": "Active Directory Certificate Services Overview and Migration", "url": "/topics/active-directory-certificate-services-migration/", "categories": "network, sysadmin, windows", "tags": "active-directory, ad-cs, ad-ds, certificate, certificate-services, certificate-template, certificates, doamin, microsoft, network, pki, windows-server", "date": "2018-05-25 00:00:00 +0000", "snippet": "Active Directory Certificate ServicesActive Directory Certificate Services (AD CS) is a role in Windows Server which allows you to fully implement a PKI infrastructure. AD CS also provides templates and web services for issuance and management of certificates. In this guide we will cover basic concepts of AD CS and provide a method for migration of this service to another windows server .This won’t be an in depth guide because you can find the information you need in official Microsoft books, Microsoft TechNet website or other blogs. I will start with an introduction and then explain the whole migration process, because a complete working walkthrough was missing when I originally wanted to perform the migration.According to official docs, AD CS is capable of: Certification authorities (CAs). Root and subordinate CAs are used to issue certificates to users, computers, and services, and to manage certificate validity. Web enrollment. Web enrollment allows users to connect to a CA by means of a Web browser in order to request certificates and retrieve certificate revocation lists (CRLs). Online Responder. The Online Responder service decodes revocation status requests for specific certificates, evaluates the status of these certificates, and sends back a signed response containing the requested certificate status information. Network Device Enrollment Service. The Network Device Enrollment Service allows routers and other network devices that do not have domain accounts to obtain certificates.It’s noteworthy that the true power of AD CS lies in the automation made possible by extensive powershell cmdlets, “Certificate Templates” and deep integration with AD DS which lets you build complex PKI infrastructure scenarios easily and securely.AD CS can be installed in two mode: Enterprise CA Standalone CAEnterprise CA needs a domain to be present. the server on which AD CS is installed will be registered to the domain as  PKI provider (that’s why AD CS deployment in Enterprise mode needs Enterprise Admin rights on the domain). This mode enables all the fancy automation stuff and deep integration into windows and Active Directory by means of GPOs or AD CS RPC.Standalone CA is exactly what the name implies. It does not need AD DS and works on an standalone windows server perfectly. This mode is best used to issue certificates for websites or VPNs…Final Notes IIS is installed on the server if you want to publish CRLs (sure you want that in production :) ). However you can specify a network path and have another webserver on the target share (even something like nginx). Network Device Enrollment Services (NDES) can work with Cisco Simple Certificate Enrollment Protocol (SCEP) and issue certificates to network devices (routers, firewalls, etc). These certificates are usually used for VPN. the CA is imported into trusted CA Certificate store in windows computers on the domain automatically, enabling users to connect to corporate VPNs easily and securely. Even client certificate can be issued automatically and installed on workstations so there will be no need of user/pass for VPN, increasing security and user-friendliness. Smartcards can also be leveraged in an AD CS environment. User logon information, as well as certificates installed from a CA server, can be placed on a smart card. When a user needs to log on to a system, he places the smart card in a smart card reader or simply swipes it across the reader itself. The certificate is read, and the user is prompted only for a PIN, which is uniquely assigned to each user. After the PIN and the certificate are verified, the user is logged on to the domain. Encrypted File System (EFS) keys can also be integrated in CA and deployed domain-wide, making encryption and decryption secure and painless. For a guide about basic installation of AD CS and creating a template you can refer to this guide [Virtually boring] , I won’t be explaining it here again because you can easily find information about the basics of AD CS on the internet.Active Directory Certificate Services MigrationThis method was used to migrate a Windows server 2003 AD CS installation to Windows Server 2012 R2. Note that migration from Windows server 2003 to 2012 is not officially supported by Microsoft and therefore is the trickiest and the most problematic. So this guide will likely work on other versions too. Open AD CS management panel (Certificate Authority) and Backup CA by right-clicking the main entry on the tree-view.  2. Export the following registry key: HKLM\\System\\CurrentControlSet\\Services\\CertSvc\\Configuration you can use this command :reg export "HKLM\\System\\CurrentControlSet\\Services\\CertSvc\\Configuration" c:\\cabackupregistry.reg /y Remove AD CS Role from the original server (Windows Server 2003 here) Install AD CS Role on the destination server (Windows Server 2012 R2 here) note that it should have already be joined to the domain. During installation be careful to choose the option Existing Key and supply the backup files from step 1. Open Certificate Authority on destination server and choose Restore CA. (It’s right next to where you chose Backup CA in step 1) this will restore already issued or revoked certificate database and logs. Import the registry key you exported on original server to destination server. (move with USB flash drive or network share, then double click to install) Restart AD CS (reboot the server or Stop Service / Start Service on the right click menu on Certificate Authority tree-view).Now you should have successfully migrated AD CS and all your older certificates should be valid too. I advice you to test the CA by issuing a certificate from a template now that migration is finished." }, { "title": "Bochs Emulator - Debug & Instrument", "url": "/topics/bochs-emulator-debug-and-instrument/", "categories": "debugging, emulator, instrumentation", "tags": "debug-using-bochs, emulator-debugging, instrument-instructions-using-bochs, instrument-kernel-mode, instrument-linux, instrument-operating-system, instrument-os-x, instrument-windows", "date": "2018-04-27 00:00:00 +0000", "snippet": "There is also another post about configuring and building Bochs on Windows &amp; OS X if you have a problem compiling Bochs, take a look at here!In my opinion, Bochs is an amazing thing because it provides instrumentation in the lowest level of the Operating System. One of the advantages of the Boch is being able to instrument in kernel-mode, which is not available in other instrumenting tools like Intel’s pin tool.You can see how to interact with Bochs debugger here. It’s somehow like Windbg in its syntax, if you enabled the debugger feature during the compilation then after running the OS, you can press ctrl+c and it gives you a command-line interface.In the rest of the post, I’m gonna explain about instrumentation.Instrumenting in Bochs is depending on the following functions that exist in /stubs/ instrument.cc.#include "bochs.h"#if BX_INSTRUMENTATIONvoid bx_instr_init_env(void) {}void bx_instr_exit_env(void) {}void bx_instr_initialize(unsigned cpu) {}void bx_instr_exit(unsigned cpu) {}void bx_instr_reset(unsigned cpu, unsigned type) {}void bx_instr_hlt(unsigned cpu) {}void bx_instr_mwait(unsigned cpu, bx_phy_address addr, unsigned len, Bit32u flags) {}void bx_instr_debug_promt() {}void bx_instr_debug_cmd(const char *cmd) {}void bx_instr_cnear_branch_taken(unsigned cpu, bx_address branch_eip, bx_address new_eip) {}void bx_instr_cnear_branch_not_taken(unsigned cpu, bx_address branch_eip) {}void bx_instr_ucnear_branch(unsigned cpu, unsigned what, bx_address branch_eip, bx_address new_eip) {}void bx_instr_far_branch(unsigned cpu, unsigned what, Bit16u prev_cs, bx_address prev_eip, Bit16u new_cs, bx_address new_eip) {}void bx_instr_opcode(unsigned cpu, bxInstruction_c *i, const Bit8u *opcode, unsigned len, bx_bool is32, bx_bool is64) {}void bx_instr_interrupt(unsigned cpu, unsigned vector) {}void bx_instr_exception(unsigned cpu, unsigned vector, unsigned error_code) {}void bx_instr_hwinterrupt(unsigned cpu, unsigned vector, Bit16u cs, bx_address eip) {}void bx_instr_tlb_cntrl(unsigned cpu, unsigned what, bx_phy_address new_cr3) {}void bx_instr_clflush(unsigned cpu, bx_address laddr, bx_phy_address paddr) {}void bx_instr_cache_cntrl(unsigned cpu, unsigned what) {}void bx_instr_prefetch_hint(unsigned cpu, unsigned what, unsigned seg, bx_address offset) {}void bx_instr_before_execution(unsigned cpu, bxInstruction_c *i) {}void bx_instr_after_execution(unsigned cpu, bxInstruction_c *i) {}void bx_instr_repeat_iteration(unsigned cpu, bxInstruction_c *i) {}void bx_instr_inp(Bit16u addr, unsigned len) {}void bx_instr_inp2(Bit16u addr, unsigned len, unsigned val) {}void bx_instr_outp(Bit16u addr, unsigned len, unsigned val) {}void bx_instr_lin_access(unsigned cpu, bx_address lin, bx_address phy, unsigned len, unsigned memtype, unsigned rw) {}void bx_instr_phy_access(unsigned cpu, bx_address phy, unsigned len, unsigned memtype, unsigned rw) {}void bx_instr_wrmsr(unsigned cpu, unsigned addr, Bit64u value) {}void bx_instr_vmexit(unsigned cpu, Bit32u reason, Bit64u qualification) {}#endifFor using Bochs instrumentation, first, you need to configure Bochs with the following argument:./configure --enable-instrumentation="instrument/stubs"Then you can change the above file and compile your project again and run Bochs with its debugger feature then Bochs automatically sets your function as a callback to its main CPU emulation functions and every time, one of the above functions performed in the target machine, then you’ll be aware.The best reference for describing the above functions is Instrument.txt which exists under /instrument/Instrument.txt, I copied the newest version of Instrument.txt (at the time of writing this post), you can see the below file :InstrumentationTo use instrumentation features in bochs, you must compile in support for it. You should build a custom instrumentation library in a separate directory in the “instrument/” directory. To tell configure which instrumentation library you want to use, use the “–enable-instrumentation” option. The default library consists of a set of stubs, and the following are equivalent: ./configure [...] --enable-instrumentation ./configure [...] --enable-instrumentation="instrument/stubs"You could make a separate directory with your custom library, for example “instrument/myinstrument”, copy the contents of the “instrument/stubs” directory to it, then customize it. Use:./configure [...] --enable-instrumentation="instrument/myinstrument"BOCHS instrumentation callbacks void bx_instr_init_env();The callback is called when Bochs is initialized, before of reading .bochsrc. It can be used for registration of parameters in siminterface. Then when bx_instr_init() is called it can access configuration parameters defined by bx_instr_init_env(), so instrumentalization module can use additional options in .bochsrc. void bx_instr_exit_env();The callback is called each time Bochs exits. void bx_instr_initialize(unsigned cpu);The callback is called each time, when Bochs initializes the CPU object. It can be used for initialization of user’s data, dynamic memory allocation and etc. void bx_instr_exit(unsigned cpu);The callback is called each time, when Bochs destructs the CPU object. It can be used for destruction of user’s data, allocated by bx_instr_init callback. void bx_instr_reset(unsigned cpu, unsigned type);The callback is called each time, when Bochs resets the CPU object. It would be executed once at the start of simulation and each time that user presses RESET BUTTON on the simulator’s control panel. void bx_instr_hlt(unsigned cpu);The callback is called each time, when Bochs’ emulated CPU enters HALT or SHUTDOWN state. void bx_instr_mwait(unsigned cpu, bx_phy_address addr, unsigned len, Bit32u flags);The callback is called each time, when Bochs’ emulated CPU enters to the MWAIT state. The callback receives monitored memory range and MWAIT flags as a parameters. void bx_instr_cnear_branch_taken(unsigned cpu, bx_address branch_rip, bx_address new_rip);The callback is called each time, when currently executed instruction is a conditional near branch and it is taken. void bx_instr_cnear_branch_not_taken(unsigned cpu, bx_address branch_rip);The callback is called each time, when currently executed instruction is a conditional near branch and it is not taken. void bx_instr_ucnear_branch(unsigned cpu, unsigned what, bx_address branch_rip, bx_address new_rip);The callback is called each time, when currently executed instruction is an unconditional near branch (always taken). void bx_instr_far_branch(unsigned cpu, unsigned what, Bit16u prev_cs, bx_address prev_rip, Bit16u new_cs, bx_address new_rip);The callback is called each time, when currently executed instruction is an unconditional far branch (always taken).Possible operation types, passed through bx_instr_ucnear_branch and bx_instr_far_branch are: #define BX_INSTR_IS_JMP 10 #define BX_INSTR_IS_JMP_INDIRECT 11 #define BX_INSTR_IS_CALL 12 #define BX_INSTR_IS_CALL_INDIRECT 13 #define BX_INSTR_IS_RET 14 #define BX_INSTR_IS_IRET 15 #define BX_INSTR_IS_INT 16 #define BX_INSTR_IS_SYSCALL 17 #define BX_INSTR_IS_SYSRET 18 #define BX_INSTR_IS_SYSENTER 19 #define BX_INSTR_IS_SYSEXIT 20 void bx_instr_vmexit(unsigned cpu, Bit32u reason, Bit64u qualification);This callback is called right before Bochs executes a VMEXIT. void bx_instr_opcode(unsigned cpu, bxInstruction_c *i, const Bit8u *opcode, unsigned len, bx_bool is32, bx_bool is64);The callback is called each time, when Bochs completes to decode a new instruction. Through this callback function Bochs could provide an opcode of the instruction, opcode length and an execution mode (16/32/64).Note, that Bochs uses translation caches so each simulated instruction might be executed multiple times but decoded only once. void bx_instr_interrupt(unsigned cpu, unsigned vector);The callback is called each time, when Bochs simulator executes an interrupt (software interrupt, hardware interrupt or an exception). void bx_instr_exception(unsigned cpu, unsigned vector, unsigned error_code);The callback is called each time, when Bochs simulator executes an exception. void bx_instr_hwinterrupt(unsigned cpu, unsigned vector, Bit16u cs, bx_address rip);The callback is called each time, when Bochs simulator executes a hardware interrupt. void bx_instr_clflush(unsigned cpu, bx_address laddr, bx_phy_address paddr);The callback is called each time the CLFLUSH instruction is executed. void bx_instr_tlb_cntrl(unsigned cpu, unsigned what, bx_phy_address new_cr_value); void bx_instr_cache_cntrl(unsigned cpu, unsigned what);The callback is called each time, when Bochs simulator executes a cache/tlb control instruction.Possible instruction types, passed through bx_instr_tlb_cntrl are: #define BX_INSTR_MOV_CR0 10 #define BX_INSTR_MOV_CR3 11 #define BX_INSTR_MOV_CR4 12 #define BX_INSTR_TASK_SWITCH 13 #define BX_INSTR_CONTEXT_SWITCH 14 /* VMM and SMM enter/exit */ #define BX_INSTR_INVLPG 15 #define BX_INSTR_INVEPT 16 #define BX_INSTR_INVVPID 17 #define BX_INSTR_INVPCID 18The new_cr_value is provided for first 4 instruction types only and undefined for all others.Possible instruction types, passed through bx_instr_cache_cntrl are: #define BX_INSTR_INVD 10 #define BX_INSTR_WBINVD 11 void bx_instr_prefetch_hint(unsigned cpu, unsigned what, unsigned seg, bx_address offset);The callback is called each time, when Bochs simulator executes a PREFETCH instruction.Possible PREFETCH types: #define BX_INSTR_PREFETCH_NTA 00 #define BX_INSTR_PREFETCH_T0 01 #define BX_INSTR_PREFETCH_T1 02 #define BX_INSTR_PREFETCH_T2 03The seg/offset arguments indicate the address of the requested prefetch. void bx_instr_wrmsr(unsigned cpu, unsigned msr, Bit64u value);This callback is called each time when WRMSR instruction is executed. MSR number and written value passed as parameters to the callback function. void bx_instr_repeat_iteration(unsigned cpu, bxInstruction_c *i);The callback is called each time, when Bochs simulator starts a new repeat iteration. void bx_instr_before_execution(unsigned cpu, bxInstruction_c *i);The callback is called each time, when Bochs simulator starts a new instruction execution. In case of repeat instruction the callback will be called only once before the first iteration will be started. void bx_instr_after_execution(unsigned cpu, bxInstruction_c *i);The callback is called each time, when Bochs simulator finishes any instruction execution. In case of repeat instruction the callback will be called only once after all repeat iterations. void bx_instr_lin_access(unsigned cpu, bx_address lin, bx_address phy, unsigned len, unsigned memtype, unsigned rw);The callback is called each time, when Bochs simulator executes a linear memory access. Note that no page split accesses will be generated because Bochs splits page split accesses to two different memory accesses during its execution flow. The callback also will not be generated in case of direct physical memory access like page walks, SMM, VMM or SVM operations.Possible access types are: BX_READ, BX_WRITE and BX_RW.Currently the callback is not supported when repeat-speedups optimization is enabled. void bx_instr_phy_access(unsigned cpu, bx_address lin, bx_address phy, unsigned len, unsigned memtype, unsigned rw);The callback is called each time, when Bochs simulator executes a physical memory access. Physical accesses include memory accesses generated by the CPU during page walks, SMM, VMM or SVM operations. Note that no page split accesses will be generated because Bochs splits page split accesses to two different memory accesses during its execution flow.Possible access types are: BX_READ, BX_WRITE and BX_RW. void bx_instr_inp(Bit16u addr, unsigned len); void bx_instr_inp2(Bit16u addr, unsigned len, unsigned val); void bx_instr_outp(Bit16u addr, unsigned len, unsigned val);These callback functions are a feedback from various system devices.Known problems1. BX_INSTR_LIN_ACCESS doesn’t work when repeat-speedups feature is enabled.Feature requests:1. BX_INSTR_CNEAR_BRANCH_NOT_TAKEN callback should have an additional ‘not taken’ new_rip parameter.2. BX_INSTR_SMI, BX_INSTR_NMI, BX_INSTR_SIPI and other external events callbacks If you read the above description about instrument functions, then let’s have a look at some of the important ones!For debugging VMX you should use bx_instr_vmexit, but you should be sure to compile your Bochs with this feature enabled. By default it is enabled in the current version of Bochs :#define BX_SUPPORT_VMX 2bx_instr_phy_access can also help you debugging EPT (Extended Page Table) by checking physical addresses.There are also other functions like bx_instr_wrmsr which is used for detecting what kind of MSR indexes an operating system or system drivers try to use.References [Bochs User Manual] (http://bochs.sourceforge.net/doc/docbook/user/index.html) [Platform emulation with Bochs] (https://www.ibm.com/developerworks/library/l-bochs) - [PDF Version] (https://www.ibm.com/developerworks/library/l-bochs/l-bochs-pdf.pdf) [Wikipedia - Bochs] (https://en.wikipedia.org/wiki/Bochs) [Using Bochs internal debugger] (http://bochs.sourceforge.net/doc/docbook/user/internal-debugger.html)" }, { "title": "Bochs Emulator - Config & Build on Windows and OS X", "url": "/topics/bochs-config-and-build-on-windows-and-os-x/", "categories": "emulator", "tags": "bochs-emulator, bochs-os-x, bochs-windows, compile-bochs, config-bochs, install-bochs, install-windows-on-bochs", "date": "2018-04-18 00:00:00 +0000", "snippet": "IntroductionBochs is an x86 - x86-64 multiplatform emulator that provides emulation of CPU Instruction Fetching without using Hypervisor technologies like Intel’s VT-x and AMD-V.One of the benefits of emulation rather than virtualization is that you can execute instructions of old discontinued CPU structures in modern CPU or run Intel’s instructions on an AMD CPU or vice versa, more than that Bochs developers try to make Bochs devices like virtualization software devices.They provide keyboard, speaker, cdrom, disk, floppy, network device and etc.Emulation is somehow like high-level programming languages like Java when using its bytecode or .Net Framework when using MSIL.From IBM : Java technology implements just-in-time (JIT) compilation to work around some of the inefficiencies, similar to Transmeta and numerous other emulation solutions (including QEMU, another platform emulation solution similar to Bochs). But Bochs also achieves efficient emulation (up to 100 million instructions per second [MIPS] on a modern processor) while also being portable. Bochs was developed purely in the C++ language for interpreted x86 instruction execution and platform emulation. Because of instructions doesn’t directly execute on a Bochs machine, It is too much slower than CPU on virtualization so Bochs comes at the cost of performance.Even though Bochs is really slow but it gives us other benefits that worths using the emulator like Instrumenting an operating system kernel which I described in Bochs Emulator - Debug &amp; Instrument.Bochs is an open-source program and in order to use some features like instrumentation and other optimization you need to compile it by your self, more than that you can edit the source code for researching because Bochs have lots of cool features that can be manipulated in order to better optimize your needs, so I prefer to compile it rather than downloading its binaries.ok, that’s enough for introduction, let’s see how to “make” it!In the rest of topic, I’m trying to build bochs-2.9.Configure &amp; Make in OS XFirst, you need to download the project source code, you can find it here.The documentation uses the following syntax in order to build the Bochs on OS X machines./configuremakeBut it gives me the following error after running “make”:Sinas-MacBook-Pro:bochs-2.6.9 sina$ makecd iodev &amp;&amp; \\ /Library/Developer/CommandLineTools/usr/bin/make libiodev.ag++ -c -I.. -I./.. -I../instrument/stubs -I./../instrument/stubs -fpascal-strings -fno-common -Wno-four-char-constants -Wno-unknown-pragmas -Dmacintosh -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES devices.cc -o devices.o...cdrom_osx.cc:194:18: error: assigning to 'char *' from incompatible type 'const char *' if ((devname = strrchr(devpath, '/')) != NULL) { ^~~~~~~~~~~~~~~~~~~~~cdrom_osx.cc:231:15: warning: comparison between NULL and non-pointer ('io_registry_entry_t' (aka 'unsigned int') and NULL) [-Wnull-arithmetic] if (service == NULL) { ~~~~~~~ ^ ~~~~cdrom_osx.cc:291:30: warning: implicit conversion of NULL constant to 'mach_port_t' (aka 'unsigned int') [-Wnull-conversion] mach_port_t masterPort = NULL; ~~~~~~~~~~ ^~~~ 04 warnings and 1 error generated.make[1]: *** [cdrom_osx.o] Error 1make: *** [iodev/hdimage/libhdimage.a] Error 2that’s a weird error! I don’t know why this happens but I simply go to “cdrom_osx.cc” file and change the following line: if ((devname = strrchr(devpath, '/')) != NULL) {to: if ((devname = (char*)strrchr(devpath, '/')) != NULL) {And simply ignore this problem.Let’s “make” it again.Sinas-MacBook-Pro:bochs-2.6.9 sina$ ...Sinas-MacBook-Pro:bochs-2.6.9 sina$ ...carbon.cc:155:1: error: unknown type name 'CIconHandle'CIconHandle bx_cicn[BX_MAX_PIXMAPS];^carbon.cc:269:3: error: use of undeclared identifier 'GetWindowPortBounds' GetWindowPortBounds(myWindow, &amp;box); ^carbon.cc:270:13: error: use of undeclared identifier 'blackColor' BackColor(blackColor); ^carbon.cc:271:3: error: use of undeclared identifier 'EraseRect' EraseRect(&amp;box); ^carbon.cc:310:3: error: use of undeclared identifier 'GetWindowPortBounds' GetWindowPortBounds(myWindow, &amp;box); ^carbon.cc:401:10: error: use of undeclared identifier 'FindWindow' part = FindWindow(wheresMyMouse, &amp;whichWindow); ^carbon.cc:407:5: error: use of undeclared identifier 'MenuSelect' MenuSelect(wheresMyMouse); ^carbon.cc:443:7: error: use of undeclared identifier 'CreateStandardAlert' CreateStandardAlert( ^carbon.cc:450:7: error: use of undeclared identifier 'RunStandardAlert' RunStandardAlert( ^carbon.cc:474:11: error: use of undeclared identifier 'IsWindowVisible' if (IsWindowVisible(toolwin)) ^carbon.cc:488:11: error: use of undeclared identifier 'IsWindowVisible' if (IsWindowVisible(toolwin) || menubarVisible) ^carbon.cc:492:13: error: use of undeclared identifier 'IsWindowVisible' if (IsWindowVisible(toolwin)) ^carbon.cc:499:14: error: use of undeclared identifier 'IsWindowVisible' if (!IsWindowVisible(toolwin)) ^carbon.cc:535:3: error: use of undeclared identifier 'StopAlert' StopAlert(200, NULL); ^carbon.cc:543:3: error: use of undeclared identifier 'InitCursor' InitCursor(); ^carbon.cc:553:5: error: no matching function for call to 'NewAEEventHandlerUPP' NewAEEventHandlerUPP(QuitAppleEventHandler), 0, false); ^~~~~~~~~~~~~~~~~~~~/System/Library/Frameworks/CoreServices.framework/Frameworks/AE.framework/Headers/AEDataModel.h:2730:65: note: candidate function not viable: no known conversion from 'OSErr (const AppleEvent *, AppleEvent *, SInt32)' (aka 'short (const AEDesc *, AEDesc *, int)') to 'AEEventHandlerProcPtr' (aka 'short (*)(const AEDesc *, AEDesc *, void *)') for 1st argument inline AEEventHandlerUPP NewAEEve... ^carbon.cc:555:5: warning: 'ExitToShell' is deprecated: first deprecated in macOS 10.9 [-Wdeprecated-declarations] ExitToShell(); ^/System/Library/Frameworks/ApplicationServices.framework/Frameworks/HIServices.framework/Headers/Processes.h:725:6: note: 'ExitToShell' has been explicitly marked deprecated herevoid ExitToShell( void ) __attribute__ (( __noreturn__ )) AVAILABLE_MA... ^carbon.cc:575:5: error: use of undeclared identifier 'GetGWorld' GetGWorld(&amp;savePort, &amp;saveDevice); ^carbon.cc:605:56: error: use of undeclared identifier 'keepLocal' &amp;srcTileRect, disp_bpp&gt;8 ? NULL : gCTable, NULL, keepLocal, gMyBuf... ^carbon.cc:609:5: error: use of undeclared identifier 'SetGWorld' SetGWorld(gOffWorld, NULL); ^fatal error: too many errors emitted, stopping now [-ferror-limit=]1 warning and 20 errors generated.make[1]: *** [carbon.o] Error 1make: *** [gui/libgui.a] Error 2That’s too bad! 20 errors.I googled it and understand that this problem happens because the libraries that it used for it’s GUI is no longer available for OS X, so the problem can be solved using SDL instead.Now you need to download and install SDL.brew install sdlThen compile the Bochs using the following syntax../configure --with-sdlmakeEven though this problem is reported to Bochs developers but it seems none of them uses OS X, I’m sure this problem will be solved in the future versions of Bochs.Finally, it compiles without error, but actually, I want to use more features of Bochs like disasm and debug features and instrumentation and etc.I use the following syntax to compile the Bochs:./configure --enable-cpu-level=6 --enable-x86-64 --enable-vmx=2 --enable-pci --enable-usb --enable-usb-ohci --enable-debugger --enable-disasm --with-sdl --enable-instrumentation="instrument/stubs" --enable-cdrommakeIf there isn’t any problem, you can now run the Bochs.This time, when I run Bochs it gives me the following error:Sinas-MacBook-Pro:bochs-2.6.9 sina$ bochs ======================================================================== Bochs x86 Emulator 2.6.9 Built from SVN snapshot on April 9, 2017 Compiled on Apr 15 2018 at 14:58:06========================================================================00000000000i[ ] BXSHARE not set. using compile time default '/usr/local/share/bochs'00000000000i[ ] reading configuration from .bochsrc00000000000e[ ] .bochsrc:187: wrong value for parameter 'model'00000000000p[ ] &gt;&gt;PANIC&lt;&lt; .bochsrc:187: cpu directive malformed.00000000000e[SIM ] notify called, but no bxevent_callback function is registered========================================================================Bochs is exiting with the following message:[ ] .bochsrc:187: cpu directive malformed.========================================================================00000000000i[SIM ] quit_sim called with exit code 1Actually, this problem happens because the CPU architecture that selected in “.bochsrc” file is not available in the current built.You should run the following command in order to get the supported CPUs:bochs --help cpuThen I modified the following line:cpu: model=core2__penryn__t9600, count=1, ips=50000000, reset__on__triple__fault=1, ignore__bad__msrs=1, msrs="msrs.def"change the “core2_penryn_t9600” to e.g “core_duo_t2400_yonah”.Running bochs again produces such error :Sinas-MacBook-Pro:bochs-2.6.9 sina$ bochs ======================================================================== Bochs x86 Emulator 2.6.9 Built from SVN snapshot on April 9, 2017 Compiled on Apr 15 2018 at 14:58:06========================================================================00000000000i[ ] BXSHARE not set. using compile time default '/usr/local/share/bochs'00000000000i[ ] reading configuration from .bochsrc00000000000e[ ] .bochsrc:718: ataX-master/slave CHS set to 0/0/0 - autodetection enabled00000000000p[ ] &gt;&gt;PANIC&lt;&lt; .bochsrc:914: Bochs is not compiled with lowlevel sound support00000000000e[SIM ] notify called, but no bxevent_callback function is registered========================================================================Bochs is exiting with the following message:[ ] .bochsrc:914: Bochs is not compiled with lowlevel sound support========================================================================00000000000i[SIM ] quit_sim called with exit code 1It is because we didn’t configure Bochs for preparing sound device and we use “sound” option in “.bochsrc”, in order to build with sound support you can use one of the “–enable-sb16” or “–enable-es1370” in configuring and “make” again but I rather remove the following line from “.bochsrc” to build it again.sound: driver=default, waveout=/dev/dsp. wavein=, midiout=Now it runs without error.Sinas-MacBook-Pro:bochs-2.6.9 sina$ bochs ======================================================================== Bochs x86 Emulator 2.6.9 Built from SVN snapshot on April 9, 2017 Compiled on Apr 15 2018 at 14:58:06========================================================================00000000000i[ ] BXSHARE not set. using compile time default '/usr/local/share/bochs'00000000000i[ ] reading configuration from .bochsrc00000000000e[ ] .bochsrc:718: ataX-master/slave CHS set to 0/0/0 - autodetection enabled00000000000e[ ] .bochsrc:925: wrong value for parameter 'mode'00000000000e[PCSPK ] .bochsrc:925: unknown parameter for speaker ignored.------------------------------Bochs Configuration: Main Menu------------------------------This is the Bochs Configuration Interface, where you can describe themachine that you want to simulate. Bochs has already searched for aconfiguration file (typically called bochsrc.txt) and loaded it if itcould be found. When you are satisfied with the configuration, goahead and start the simulation.You can also start bochs with the -q option to skip these menus.1. Restore factory default configuration2. Read options from...3. Edit options4. Save options to...5. Restore the Bochs state from...6. Begin simulation7. Quit nowPlease choose one: [6]Building Bochs on WindowsTo build Bochs for Windows you have multiple options, like building with Cygwin or MinGW but I’d rather build Bochs with Visual Studio instead.Download (bochs-x.x.x-msvc-src.zip) latest version from here.Extract the bochs-x.x.x-msvc-src.zip go to vs2013 and open “bochs.sln”.You need to enable some of the features before building the solution. (It’s like ./configure –enable-x in OS X and Linux)Go to “config.h” and modify it like this:In order to enable debugging make sure your config file is :#define BX__GDBSTUB 1#define BX__DEBUGGER 1#define BX__DISASM 1#define BX__DEBUGGER__GUI 1I also need Instrumentation so changed the following line:#define BX__INSTRUMENTATION 1make sure disable BX_SUPPORT_HANDLERS_CHAINING_SPEEDUPS because of some incompatibility issues.#define BX__SUPPORT__HANDLERS__CHAINING__SPEEDUPS 0Then trying to build and this time it gives me the following error:unresolved external symbol ____imp____WSAStartup@8 referenced in function __main And other such errors which are linking problems.I add the following line to bxhub.cc :#pragma comment(lib,"Ws2__32.lib")That line solves the linking problems. If there isn’t any error then just build the solution.Installing Windows on BochsInstalling an operating system in Bochs is a little tricky, open bochs.exe (from what you built before.) then make sure that it starts without any problem (some systems have problem opening Bochs !).Bochs uses an image file for the purpose of its hard disk, there is a tool called bximage.exe which creates such file for you.Open bximage.exe :======================================================================== bximage Disk Image Creation / Conversion / Resize and Commit Tool for Bochs $Id: bximage.cc 13069 2017-02-12 16:51:52Z vruppert $========================================================================1. Create new floppy or hard disk image2. Convert hard disk image to other format (mode)3. Resize hard disk image4. Commit 'undoable' redolog to base image5. Disk image info0. QuitPlease choose one [0]Then choose 1 (Create new floppy or hard disk image). In the second step choose hd for device type, growing for the next question and then choose how much space you need to allocate to your emulated machine.In the last step specify a name and you’re good to go!A complete result of bximage is like :======================================================================== bximage Disk Image Creation / Conversion / Resize and Commit Tool for Bochs $Id: bximage.cc 13069 2017-02-12 16:51:52Z vruppert $========================================================================1. Create new floppy or hard disk image2. Convert hard disk image to other format (mode)3. Resize hard disk image4. Commit 'undoable' redolog to base image5. Disk image info0. QuitPlease choose one [0] 1Create imageDo you want to create a floppy disk image or a hard disk image?Please type hd or fd. [hd] hdWhat kind of image should I create?Please type flat, sparse, growing, vpc or vmware4. [flat] growingEnter the hard disk size in megabytes, between 10 and 8257535[10] 10000What should be the name of the image?[c.img] c.imgCreating hard disk image 'c.img' with CHS=20317/16/63redolog : creating redolog c.imgredolog : Standard Header : magic='Bochs Virtual HD Image', type='Redolog', subtype='Growing', version = 2.0redolog : Specific Header : #entries=32768, bitmap size=128, exent size = 524288 disk size = 10485522432The following line should appear in your bochsrc: ata0-master: type=disk, path="c.img", mode=growing(The line is stored in your windows clipboard, use CTRL-V to paste)Press any key to continueAfter creating the image file, now you need to configure your Bochs machine.Open the Bochs.exe and edit the memory option.Set the memory size (megabytes) and host allocated size (megabytes) to a value greater than 32!  I choose 1000.Then edit CPU option &gt; CPU Configuration to turion64_tyler or whatever supports x86-64, make sure to set a proper value for Emulated instructions per second (IPS). Press OK and edit Disk &amp; Boot &gt; ATA Channel &gt; First HD/CD on channel 0. In this window set Type of ATA device to disk, set path or physical device name to the c.img (the file that is created previously by bximage), Type of disk image to growing, Cylinders to 20317, heads to 16, Sectors per track to 63.Now go to another tab (Second HD/CD on channel 0) and set the type of disk to cdrom, Path or physical device name to the Windows ISO file.For the last step go to Boot Options, set Boot drive #1 to disk and Boot drive #2 to cdrom.That’s it, make sure to save your configuration to avoid doing these steps again and then press start.Select continue and don’t ask again if a prompt comes to the screen then restart the machine and you should see the Windows installation.If you see the following picture, then you’re done.By now, you should’ve configured Bochs, Let’s use more features of Bochs in the next post. (Bochs Emulator - Debug &amp; Instrument).References [Bochs User Manual] (http://bochs.sourceforge.net/doc/docbook/user/index.html) [Platform emulation with Bochs] (https://www.ibm.com/developerworks/library/l-bochs) - [PDF Version] (https://www.ibm.com/developerworks/library/l-bochs/l-bochs-pdf.pdf) [Wikipedia - Bochs] (https://en.wikipedia.org/wiki/Bochs)" }, { "title": "A partial survey among non-general purpose registers", "url": "/topics/survey-non-general-registers/", "categories": "cpu, kernel-mode, user-mode", "tags": "cpu-registers, gdtr-register, ldtr-register, non-general-registers, segment-registers, tr-register, xmm-register, ymm-registers", "date": "2018-04-07 00:00:00 +0000", "snippet": "Hi guys,In the past few days, I was searching about non-general purpose register then I saw the following pictures that give me a new idea of posting about the non-general purpose registers.These pieces of information might have some fault because of my misunderstandings about some of them, if you see any fault then please tell me in the comments!You can also download the PDF version of the above picture here!I don’t know if there are other registers but in the rest of the post, I’ll explain about the register that I know, which is shown in the above pictures. By the way, you all should know about general purpose registers like eax , ecx, ebx,edx,esi,edi,esp,eip (or their equivalent rax,rbx,rdx,rcx,rip,rsp and etc). But non-genral purpose registers are also existsFirst of all, let me explain you about segments.Actually, Segmentation is the division of memory into segments or sections, to address a segment you need to have a two-dimensional variable to point to a valid address and those variables are Base Address and Limit. Base Address contains the starting physical address where the segments reside in memory. Limit specifies the length of the segment.Segment RegistersSegment register fs, gs, cs, ss, ds, es is used for segmentation, in x86 in the real mode they used for addressing a pointer to a special region in memory like ss for stack segment or ds for the data segment.Segmentation is no longer available in Long-Mode but the modern Operating Systems still use fs and gs in x86 and x64.Model-Specific Register (MSR)In the introduction of 80386, Intel started to introduce “experimental” features which would not necessarily be present in future.From Wikipedia :The first of these were two “test registers” (T6 and T7) that allowed the 80386 to speed up virtual-to-physical address conversions. Three additional test registers followed in the 80486 (TR3–TR5) that enabled testing of the processor’s caches for code and data. None of these five registers were implemented in the subsequent Pentium processor.With the introduction of the Pentium processor, Intel provided a pair of instructions (rdmsr and wrmsr) to access current and future “model-specific registers”, as well as the CPUID instruction to determine which features are present on a particular model. Many of these registers have proven useful enough to be retained. Intel has classified these as architectural model-specific registers and has committed to their inclusion in future product lines.GDTR, LDTR, IDTR, TR and MSW RegistersGDTR: Determines the current location of Global Descriptor TableLDTR: Determines the current location of Local Descriptor TableIDTR: The location of Interrupt Descriptor Table which always point to start of nt!KiDivideErrorFault.TR: TR stands for Task Register which is a 16-bit register that holds a segment selector for TSS. This register is mainly used for task switching in hardware-level but modern Operating-Systems like Windows and Linux don’t use register because they implement their software-level task switching. LTR and STR instructions are used to read or set the value of TR Register.Control RegisterSomehow like MSR Registers, there is some older way to control CPU features.You can use these registers (cr0-cr7) to make a feature enable or disable them.There is also a cr8 register which is used by a REX prefix in long mode. CR8 is used to prioritize external interrupts and is referred to as the task-priority register (TPR).You can also visit Wikipedia which described every feature related to each bit of the Control Registers.Debug RegistersIntel and AMD also provide debug registers in order to facilitate the debugging process by providing 8 debug register.During a debugging process, we can set up to 4 pointers to cause a hardware breakpoint with different conditions (like when you want to set a breakpoint when some instruction tries to read a special location or trying to write on that pointer and etc). all these conditions are set in DR4 to DR7 and DR0 to DR3 is used to store the pointers.Extended Feature Enable Register (EFER)AMD uses EFER to enable or disable some of its features (like cr8 in Intel). EFER is a model-specific register.Floating point unit (Registers)Because of the problem of saving floating values into binary registers, there are FPU Registers (or floating point unit registers) which helps in storing a computing on float numbers.XMM RegistersXMM Registers are mainly used by Streaming SIMD Extensions. SSE originally added eight new 128-bit registers known as XMM0 through XMM7. The AMD64 added a further eight registers XMM8 through XMM15.SSE instructions are used typically in digital signal processing and graphics processing.YMM RegistersBy the rise of Advanced Vector Extensions, Intel introduced the 16 new register and new sets of instructions. AVE is an upgrade to SSE which can speed up FP operations so its main purpose is to make multimedia calculation faster.AVX uses sixteen YMM registers. Each YMM register contains: eight 32-bit single-precision floating point numbers or four 64-bit double-precision floating point numbers.I’m sure that this post will be updated ! so keep checking our blog.Thanks for reading," }, { "title": "Introduction to systemd : Basic Usage and Concepts", "url": "/topics/introduction-to-systemd-basic-usage-and-concepts/", "categories": "linux, software, sysadmin", "tags": "debate, debian, init, linux, management, sysadmin, systemd, sysvinit", "date": "2018-04-06 00:00:00 +0000", "snippet": "Hi everyoneIn this post I am going to explain some essential systemd commands and concepts. As systemd popularity grew much more and changed the linux ecosystem drastically, every sysadmin, DevOps or every casual linux user should know the basics of this init system (It’s really a load of other things too!) and be able to use systemctl, journalctl, etc in order to leverage its power.For example, by using systemd unit files you can completely get rid of using crontab as systemd replaces this functionality, you can also run the service as a temporary user or even activate a service when a connection is made to a specific port on server. as you can see, the possibilities are endless. Some of them will hopefully be described in further posts and links provided at the end of this post. ConceptsUnit FilesMost systemd configuration take place in unit files. unit files are short configuration snippets that control behavior of systmed. Unit files are of different types which will be described below:Service FileA service unit file is configuration file that shows how you like to run a specific process, like a web server application, a docker container, etc. It can be anything, even your own application.Target FileA target unit file is mechanism used for grouping different services and starting them at the same time or in another desired fashion.Timer unitsTimer units are used to run services at specific times, like crontab.Path unitsWatches a path and responds based on defined actions.Slice UnitsSlice units are used for resource management of other units. Units are assigned to a slice which controls their use of resources. By default units are assigned to a default slice. which is “system.slice” for services and scope units, “user.slice” for user sessions and “machine.slice” for containers registered with systemd-machined.Socket UnitsSocket units are used to activate another service on request to a socket. It facilitates on-demand service activation. You can setup a server which listen for ssh connection and creates a container for each user which connects to the server and connects the user to the container. only using systemd! your power is limited by your imagination :DDevice Unitsystemd wrapper for udev devices.Mount UnitsMount units are simply used to mount a filesystem automatically (or manually).A sample service unit file (OpenSSH on debian jessie):[Unit]Description=OpenBSD Secure Shell serverAfter=network.target auditd.serviceConditionPathExists=!/etc/ssh/sshd_not_to_be_run[Service]EnvironmentFile=-/etc/default/sshExecStartPre=/usr/sbin/sshd -tExecStart=/usr/sbin/sshd -D $SSHD_OPTSExecReload=/usr/sbin/sshd -tExecReload=/bin/kill -HUP $MAINPIDKillMode=processRestart=on-failure[Install]WantedBy=multi-user.targetAlias=sshd.serviceIt is rather self-explanatory so after another example (a unit file for a docker container from CoreOS):[Unit]Description=MyAppAfter=docker.serviceRequires=docker.service[Service]TimeoutStartSec=0ExecStartPre=-/usr/bin/docker kill busybox1ExecStartPre=-/usr/bin/docker rm busybox1ExecStartPre=/usr/bin/docker pull busyboxExecStart=/usr/bin/docker run --name busybox1 busybox /bin/sh -c "trap 'exit 0' INT TERM; while true; do echo Hello World; sleep 1; done"[Install]WantedBy=multi-user.target$ sudo systemctl enable /etc/systemd/system/hello.service$ sudo systemctl start hello.service systemd commandssystemctlThe most import command is probably systemctl. It is used for starting/stopping services. Here’s a good table from HighOnCoffee: systemctl stop service-name systemd stop running service systemctl start service-name systemctl start service systemctl restart service-name systemd restart running service systemctl reload service-name reloads all config files for service systemctl status service-name systemctl show if service is running systemctl enable service-name systemctl start service at boot systemctrl disable service-name systemctl - disable service at boot systemctl show service-name show systemctl service info List service dependencies with this command:# systemctl list-dependencies sshd.service Change ad-hoc runlevel with systemctl isolate command. Boot targets are somehow equivalent to SysV init runlevels: Switch to another target (in this case multi-user/runlevel 3 in old SysV): systemctl isolate multi-user.target Switch to graphical target (in this case graphical/runlevel 5 in old SysV): systemctl isolate graphical.targetjournalctlView all log entries starting from this boot:$ journalctl -bview only kernel messages from this boot:$ journalctl -k -busing -x flag attaches some additional data to the logs, -n can get an integer and show this much lines (default 10):$ journalctl -xnview all logs from a specific service:$ journalctl -b -e -u nginx.serviceThat’s All folks!systemd has sooo many features (and of course so much criticism and debate!). I will try to cover more advanced features of systemd in following posts on this blog. Here are some links that will be useful for you:https://access.redhat.com/articles/systemd-cheat-sheethttps://gist.github.com/mbodo/8f87c96ce11e91f80fbf6175412a2206https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-unitshttps://wiki.archlinux.org/index.php/Systemdand this series of articles by Lennart Poettering (core systemd developer!): [systemd-for-admins-I] (http://0pointer.net/blog/projects/systemd-for-admins-1.html) [systemd-for-admins-II] (http://0pointer.net/blog/projects/systemd-for-admins-2.html) [systemd-for-admins-II] (http://0pointer.net/blog/projects/systemd-for-admins-3.html) [systemd-for-admins-IV] (http://0pointer.net/blog/projects/systemd-for-admins-4.html) [systemd-for-admins-V] (http://0pointer.net/blog/projects/three-levels-of-off.html) [systemd-for-admins-VI] (http://0pointer.net/blog/projects/changing-roots.html) [systemd-for-admins-VII] (http://0pointer.net/blog/projects/blame-game.html) [systemd-for-admins-VIII] (http://0pointer.net/blog/projects/the-new-configuration-files.html) [systemd-for-admins-IX] (http://0pointer.net/blog/projects/on-etc-sysinit.html) [systemd-for-admins-X] (http://0pointer.net/blog/projects/instances.html) [systemd-for-admins-XI] (http://0pointer.net/blog/projects/inetd.html) [systemd-for-admins-XII] (http://0pointer.net/blog/projects/security.html) [systemd-for-admins-XIII] (http://0pointer.net/blog/projects/systemctl-journal.html) [systemd-for-admins-XIV] (http://0pointer.net/blog/projects/self-documented-boot.html) [systemd-for-admins-XV] (http://0pointer.net/blog/projects/watchdog.html) [systemd-for-admins-XVI] (http://0pointer.net/blog/projects/serial-console.html) [systemd-for-admins-XVII] (http://0pointer.net/blog/projects/journalctl.html) [systemd-for-admins-XVIII] (http://0pointer.net/blog/projects/resources.html) [systemd-for-admins-XIX] (http://0pointer.net/blog/projects/detect-virt.html) [systemd-for-admins-XX] (http://0pointer.net/blog/projects/socket-activated-containers.html)" }, { "title": "Useful Configs for NGINX", "url": "/topics/useful-configs-for-nginx/", "categories": "linux, network, sysadmin", "tags": "cache, debian, https, linux, loadbalance, nginx, opensource, php-fpm, proxy, webserver", "date": "2018-03-06 00:00:00 +0000", "snippet": "After posting the first of my linux SysAdmin quick config sample series titled “Useful Configs for squid” (which you can read here). I decided to write another post, this time about the powerful and popular web/cache server NGINX!I spent quite some time reading through nginx official docs and other blogs/websites while testing each configuration directive in different scenarios. Some of the options presented in this post do not have good or any documentation. I hope you find them useful!*** snippets are tested on nginx on Debian 8 (jessie) but they will work on other distros/OSs with minimal or no modification.Disclaimer: These configuration files are meant to be small and simple and designed to help you get an idea of what is possible with NGINX or quickly test some of its capabilities in a lab environment. although they probably work but they may be far from complete at times. So It’s up to you to research further if you want to leverage nginx in production.Connecting to PHPProbably the first thing you want to do after installing nginx is to connect it to some php interpreter to be able to run your web application. Install PHP (on debian : apt install php5 php5-fpm) change NGINX config file like this (essentially only uncomment the relevant section):location ~ \\.php$ { include snippets/fastcgi-php.conf; # With php5-cgi alone: #fastcgi_pass 127.0.0.1:9000; # With php5-fpm: fastcgi_pass unix:/var/run/php5-fpm.sock;} add index.php to index line verify socket properties in /etc/php5/fpm/pools.d/www.conf socket permissions and user must be correct (they are correct in a default Debian Jessie install) Redirect HTTP to HTTPSThere any many ways to accomplish this. Some websites advocate the use of if__($scheme … but THIS IS WRONG. it causes performance issues and also if in nginx behaves differently and you might get unexpected results. The correct way to do this is presented below, no rewrite, if , etc are needed this(see ):location / { return 301 https://$server_name$request_uri;}*** Note that since we are doing a permanent redirect (301), it will be cached by browsers so it will be a one time thing and they will connect to https port by default in subsequent visits. Nginx Reverse ProxyReverse proxy is a very popular and useful feature of nginx. It’s important that you completely understand how it works and how to use it effectively. a large number of websites and services are based on nginx reverse proxy like Netflix, CloudFlare CDN and many more!basic reverse proxy:server { listen 80; server_name rayanfam.com; location / { proxy_pass http://&lt;IP of other web server&gt;/[path of real website if not hosted on root]; }}This feature is usually utilized minimally like this:server { listen 80; server_name www.rayanfam.com devel.rayanfam.com rayanfam.com; location / { proxy_pass http://222.222.222.222:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }}I suggest you read official docs on this feature at least, there are many good articles on reverse proxying with nginx on other websites too.Forward ProxyThis is not a very used feature but for the sake of completeness and also because it is not available on other websites I will show you how to configure nginx as a forward proxy for your organization. It will do the job very well!server { listen 80; server_name _; location / { resolver 8.8.8.8; proxy_pass http://$http_host$uri$is_args$args; }}*** Do not host this on a public facing IP!IP-based BlockYou may want to deny or allow access only from a specific ip range. you can achieve this with iptables, but this is an acceptable way too:location / { allow 192.168.20.0/24; deny all; #... other directives}Custom Error PagesYou can easily customize your error page using nginx and setup fancy error pages for all types of error (GitHub is my favorite ^_^ ):# Both are mandatory. error paged should be marked as internalerror_page 403 /forbidden.html;location /forbidden.html { internal;}Log format and DestinationChanging the log format and log destination is trivial in nginx. I create a new access log format and then use it to log to syslog facility.# creating log formatlog_format mylogformat ‘$remote_addr $request’# log to a file using mylogformataccess_log /var/log/nginx/custom_access.log mylogformat;# log to syslog server using mylogformataccess_log syslog:server=192.168.10.10 mylogformat;you can view official nginx docs regarding field names for logs and support for syslog, etc.Basic Caching with NginxThis snippet is intended to give you a very rough idea of caching with nginx and the minimal configuration required to activate that. In a real server more sophisticated caching will probably be required but this will get you started on this topic.*** Caching is one of the most advanced  features of nginx, make sure to study and understand it.# First create the directory and set the required permissions#proxy_cache_path /var/cache/nginx keys_zone=CACHE:10m;server { proxy_cache CACHE; # ... other directives}HTTP Basic AuthenticationIt’s the simplest form of authentication you can have for your website or a single page. yet it is effective and secure (if your password is only known by you of course). BE CAREFUL not to put your password file in your web directory! (yes I’ve seen people do that)run this command in your shell. It's not part of nginx config:$ htpasswd -c /etc/nginx/.htpasswd shahriar# nginx config → add in desired location block auth_basic "Private Content"; auth_basic_user_file /etc/nginx/.htpasswd;some links:Official docsCommon config pitfalls (official docs)I hope you found this blog post useful… spread the word and tell your friends! also do not hesitate to comment. Have fun sysadmin-ing!" }, { "title": "Assembly Challenge : Jump to a non-relative address without using registers", "url": "/topics/assembly-challenge-jump-to-a-non-relative-address-without-using-registers/", "categories": "kernel-mode, user-mode", "tags": "far-conditional-jump, jump-without-register", "date": "2018-03-06 00:00:00 +0000", "snippet": "During developing a dispatch table for some instructions in binaries, one of the challenging problem which I faced, was changing the registers state in a way that doesn’t affect the program flow!So it might seem simple at first glance but what makes it complex is that I can’t use relative jumps or relative calls because, in some situation, I might be far away from .text segment of my binary.It causes me to explore the solutions about far jumps and far calls but actually, I realized that it can’t help me too.One of the solutions was to put the final address in the stack, then use ret in order to change xip (rip or eip).push 0xdeadbeefretIt is somehow a good solution, its fast and is really recommended, but the thing is I don’t want to change the stack state either! Even one of my friends told me that changing the above addresses of the stack doesn’t affect a regular compiler’s flow but I think it might be better, not to modify stack because I want to publish its source and it might cause the problem in abnormal programs in future.And of course another solution was using registers like :mov %eax,0xdeadbeefjmp %eaxIt’s clear that it causes nasty problems because the rest of program flow don’t know about %eax changes so it uses a wrong value and we can’t do any further modification.SolutionI solved the above problem by using the following code, in at&amp;t syntax (in x86):jmp *0f(%eip)0: .int 0x12345678The above instruction, jumps to 0x12345678 in x86 and you can see the result of compiling and disassembling it :Sinas-MBP:Desktop sina$ clang -c aa.asm -m32And to Dissemble it using objdump use the following format :Sinas-MBP:Desktop sina$ objdump -d aa.oaa.o: file format Mach-O 32-bit i386Disassembly of section __TEXT,__text:__text:0: ff 25 00 00 00 00 jmpl *06: 78 56 js 86 &lt;__text+0x5E&gt;8: 34 12 xorb $18, %alIn the case of x64 version of above code you can use the rip instead of eip and change the int to quad because you need more space for x64 addressing.jmp *0f(%rip)0: .quad 0x1234567890Sinas-MBP:Desktop sina$ clang -c aa.asmTo Dissemble it using objdump use the following format :Sinas-MBP:Desktop sina$ objdump -d aa.oaa.o: file format Mach-O 64-bit x86-64Disassembly of section __TEXT,__text:__text:0: ff 25 00 00 00 00 jmpq *(%rip)6: 90 nop7: 78 56 js 86 &lt;__text+0x5F&gt;9: 34 12 xorb $18, %alb: 00 00 addb %al, (%rax)d: 00 The above code jumps to 0x1234567890.The Problem Of Relative Conditional JumpsBy now, you might think of implementing the above jmp for j* instruction like je or jnb , but the thing is you can’t because we don’t have a conditional instruction to perform the above command.So how can we solve this ?Simply, use relative conditional jumps with a combination of above jmp, so that the conditional jump can do what it expects to base on the flags and then it can decide to jump over an instruction (in our case jump to a far address) or can perform the above jmp.# for jz addrjnz 1fjmp *0f(%rip)0: .quad addr1:Final ThoughtsThe above post derived from my question in stack overflow here, which finally answered by fuz. Even if this solution solves my problem but it is not really a good and fast way to perform this jump, and Intel advice not to use such instructions because they are really slow in CPU clock execution cycles and because I should use these instructions billions of time in a simple binary, then the normal execution becomes much slower than what I expect. So If you have any better solution, then let me know about it." }, { "title": "A first look at some aspects of Intel's "Vanderpool" initiative", "url": "/topics/intel-vanderpool-initiative-slides/", "categories": "hypervisor, kernel-mode", "tags": "hypervisor-fundamentals, intel-vt-x, virtual-machine-control-structure, vmcs", "date": "2018-01-18 00:00:00 +0000", "snippet": " A few hours ago, I was working on Intel VT-x which enables a hardware support for virtualization then I saw the following slides which gives me lots of information about Hypervisor instructions,VMM, Virtual machine control structure (VMCS) and other practical information.I don’t know about its author actually but I should give my thanks to him/her for gathering this slides.I think it worth a lot to read ,it can be downloaded here ." }, { "title": "Build a Simple Pin Tool", "url": "/topics/build-a-simple-pin-tool/", "categories": "instrumentation, user-mode", "tags": "build-tool-for-pin, compile-pin-tool, pin-tool-example, pin-tool-hello-world, pin-tool-on-windows, run-intel-pin-tool, start-with-pin-tool, use-intel-pin", "date": "2018-01-09 00:00:00 +0000", "snippet": "As I promised previously in Pinitor’s topic, I prepare a post about how to build your first pin tool.Building a pin tool is somehow difficult when they didn’t update their sample tools so that it doesn’t work in Visual Studio 2015 or newer versions. By the way, it took me more than 2 days to resolve all the errors and compile a Hello World!After compiling the first program, there where lots of new problems, so, believe me, it annoys you a lot but keep in mind if you discover a new problem in compiling, please comment about that and I add your problem and solving solution to the post.After opening the Visual Studio, I see a huge number of errors that didn’t seem to be solved without violence ;) so that I decided to use Gnu Win32 make.exe in order to compile my pin tool.Let start.Preparing EnvironmentFirst of all download pin from Intel’s official site and then download Gnu Win32 make.exe and its dependencies. In the following list, I mentioned the dependencies that I downloaded and put beside of make.exe (in the same directory) but it might be something different in your case then you should download them manually based on the error that “make” shows you when you run it.Libicon2.dllLibintl3.dllLibcharset.dllAfter solving all the errors about dependency then I realized that make.exe uses cut.exe in its compiling process so I also download cut.exe and as you might know, cut.exe has some dependencies too!Cggiconv-2.dllCygintkl-8.dllCygwin1.dllI found this dll files from the path where Cygwin GCC is installed (it is also available at CoreUtils) so you can first download Cygwin from here then you can find and copy cut.exe and its dependencies from where you installed it.Again running make causes new errors :(This time make.exe needs iconv.exe and after downloading this file make.exe seems to run correctly.You should also have c++ compiler installed previously and as I already install Visual Studio then these files are ready and I just need to provide an environment variable that points to this path and I’m good to go.In the case of providing an environment variable, I pressed Windows Key+ R (to open run.exe) then type sysdm.cpl.Open sysdm.cpl &gt; Advanced tab &gt; Environment VariableThen in User Variables for Sina (Sina is my account name.) click on path to edit it. By adding a new variable to this section you can always access to the content of the folder without changing the working directory to this path, and make.exe uses cl.exe to compile a pin tool source code into the binary module.In my visual studio, the path is as follows but it might be different in your case.C:\\Program Files(x86)\\Microsoft VisualStudio 14.0\\vc\\binand in the case when you wanna compile a 64bit pin tool then you should change this variable to :C:\\Program Files(x86)\\Microsoft VisualStudio 14.0\\vc\\bin\\Amd64Important Note: Keep in mind to restart your computer after adding a new variable or modifying one.In my case when I wanna change my context to build x64 binaries then I add \\amd64 to the path and restart the computer again.You should now be able to build your first pin tool but there are some errors that sometimes might happen because of linker problem or syntax error or header mismatching that I discuss some of them in the rest of this topicPlease note that as I said above, if you encountered new errors please comment on this post to add your problem to the following list.Problem with LinkingThe very first problem that I encountered during compiling my first application was that linker can’t find libraries of some APIs.In my case it needs kernek32.lib.I found kernel32.lib from the following path (And you know it might be different in your case.)C:\\Program Files(x86)\\Windows Kits\\10\\lib\\10.0.15063.0\\um\\x86\\kernel32.libOr in the case of 64-bit pin tool :C:\\Program Files(x86)\\Windows Kits\\10\\lib\\10.0.15063.0\\um\\x64\\kernel32.libI copied this file to the make.exe path but you can also export this path to path in environment variables as I described previously.After copying this file I added the following line to my tool’s source code after all #include(s) :#pragma comment(lib, "kernel32.lib")You by now should understand that your tool might need another .lib file to compile so you can provide more libraries for linker, same as what you did for kernel32.lib.Building A Pin ToolTo build a new pin tool you should use make as follows :In the case of ia32 or x86 architecture :make.exe obj-ia32/inscount0.dll TARGET=ia32In the case of Amd64 or x64 architecture :make.exe obj-intel64/inscount0.dllNote: If your tool source file is like “inscount0.cpp” then you should use be “inscount0.dll”, I mean its name should be same as source code except its extension which changes from .cpp to .dll.Please note as I mentioned above you should change Environment Variable in the case of x64 and x86 version.Run Pin with your ToolTo run your pin tool you should run the following command.Pin –t \\source\\tools\\ManualExamples\\inscount0.dll – cmd.exe /c dirI just instrument cmd.exe using pin by inscount0.dll tool. I also pass the dir as an argument to cmd.exe.The last thingAs I tested, pin tool has a problem with passing x64 tools to the x64 version of the pin so please don’t use the following path for running x64 tools :C:\\Users\\Sina\\Desktop\\pin.exeInstead, you should just use the following path (For x64 tools) :C:\\Users\\Sina\\Desktop\\pin\\intel64\\bin\\pin.exeThat’s it, guys,I should also give my special thanks to Sobhan Sekhavatian one of my best friends for helping me to write this post.I hope by now you built your first pin tool ;)" }, { "title": "Pinitor - An API Monitor Based on Pin", "url": "/topics/pinitor/", "categories": "instrumentation, user-mode", "tags": "api-monitor, api-pin-tool, get-native-api-windows, intel-pin, native-functions-pin-tool, windows-api-detection", "date": "2017-12-19 00:00:00 +0000", "snippet": "In the last few days, I was thinking about finding a way to discover the Native Windows APIs which malware programmers are more interested in. What I did was downloading a sample of 48k malware from VxHeaven collection and using Rohitab’s API Monitor which is a sophisticated tool in which you can find what is happening in your PE. There are also other tools that do the same thing but I almost everytime use Rohitab’s tool.After struggling with such tools, I figured out that this tool is not good enough in analyzing these samples because they actually hook everything or somehow they are known applications which some packers or protectors search for them among all the processes or if you wanna debug them, there are many nasty anti-debugging techniques that are hard to bypass so let’s get rid of all of them and use instrumentation in order to defeat these techniques.I create a pin tool which is able to have the functionality of API Monitor but based on dynamic instrumentation and Intel’s pin and it “Pinitor” which stands for Pin Monitor and actually this is a tool which detects every API calling by instrumenting the target executable.Pinitor create a call to one of its events, so that every time a new module (e.g .dll) loaded, it notifies pin about it and then search for every exported function in that module by using the EnumExportedFunctions, After that it tries to put a call in every function or in Windows Native API so everytime any of these functions called then Pinitor captures 12 arguments from the stack by default and save it in a file.The problem here is, I don’t know a way of knowing how many inputs does a special function have! As I know all API Monitor applications contain a second file (data storage) which tells them about all the functions and its arguments, so if you know any other way, please tell me about it in comments!Pinitor in its first version only works in Intel x86 systems, I’ve also built an x64 version of this tool whenever possible but for now, I think it should be a good tool for researching about binaries.In the bottom of the post, I’ve added the source code so you can change and use it for your researching but compiling a pin tool in Windows is really tricky! It takes me, more than three days to build my first pin tool, but for the future, I prepare a blog post about how to build a pin tool and the errors that might happen during the pin tool compiling process.InstallingFirst of all download pin from Intel’s website, then you can download Pinitor compiled binaries here, then you are good to go.How to useAfter downloading Pin and Pinitor then you should run pin with this tool, in the following example I demonstrate a simple example of how to use.C:\\pin\\ia32\\bin\\pin.exe -t C:\\Pintools\\Pinitor.dll -o MyOutput.txt -- c:\\Examples\\Helloworld.exeThis will create a file (“MyOutput.txt”) then add the results to this file.ExamplesImagine we built a native PE by using the following code :Example.cpp#include &lt;windows.h&gt;#include &lt;iostream&gt;using namespace std;int main(){ MessageBoxA(0,"Hello world !","Welcome",0); void * ptr1 = VirtualAlloc((void*)0x70080000, 1000, MEM_RESERVE, PAGE_READWRITE); cout &lt;&lt; ptr1; system("pause");}After that, you should run Pinitor based on how to use.And now you are able to see the result, below is a small part of the output from Pinitor :NtUserSetCursor(0x10007,0x6dfdb0,0x1,0,0,0x6dfad0,0x6dfab8,0x3aa0080,0x77559cf0,0x22c63f2e,0x5,0x23) returns 0x10003RtlFreeHeap(0x2a80000,0,0x2a92fb0,0x1,0x10007,0x6dfc34,0x6dfb6c,0x3aa0080,0x748173f0,0x567d1e5c,0x4101d,0x23) returns 0x1 returns 0x1 returns 0x1 returns 0x1 returns 0x1 returns 0x1RtlFreeHeap(0x2a80000,0,0x2a90ae8,0x1,0x10007,0x6dfc34,0x6dfb6c,0x3aa0080,0x748173f0,0x567d1e5c,0x4101d,0x23) returns 0x1RtlFreeHeap(0x2a80000,0,0x2a8eb88,0x1,0x10007,0x6dfc34,0x6dfb6c,0x3aa0080,0x748173f0,0x567d1e5c,0x4101d,0x23)RtlGetCurrentServiceSessionId(0x2a90ae8,0x2a8eb88,0x1,0x1,0x10007,0x6dfc34,0x6dfb6c,0x3aa0080,0x748173f0,0x567d1e5c,0x4101d,0x23) returns 0 returns 0x1 returns 0x1 returns 0x1VirtualAlloc(0x70080000,0x3e8,0x2000,0x1,0x10007,0x6dfc34,0x6dfb6c,0x3aa0080,0x748173f0,0x567d1e5c,0x4101d,0x23)NtAllocateVirtualMemory(0xffffffff,0x6dfe74,0,0x1,0x10007,0x6dfc34,0x6dfb6c,0x3aa0080,0x748173f0,0x567d1e5c,0x4101d,0x23)ZwAllocateVirtualMemory(0xffffffff,0x6dfe74,0,0x1,0x10007,0x6dfc34,0x6dfb6c,0x3aa0080,0x748173f0,0x567d1e5c,0x4101d,0x23) returns 0 returns 0 returns 0x70080000In this way, you can use Pinitor for your own binaries.NoteYou might have encountered that some of the results are somehow wrong, I analyze the “example.exe” by a debugger and understand that this results are because of application’s internal calls which are not defined in any exported functions in dll(s) but as long as they are calls and have return then pin automatically prints them in the results. I can filter this kind of calls but I think this calls should exist in the results so you can also filter this kind of calls and returns in AddInvokeFunctionToFile by simply checking the name content and if the name is null then you can ignore the rest of operation.ContributingWe are hardly working to create a binary analyzing and reverse engineering framework which is called Binvoke and this tool and many other tools will be added to the Binvoke Framework so any contribution and innovative idea will be appreciated.Source CodeThe source code is also available on GitHub :[ https://github.com/SinaKarvandi/Pinitor ]/* Imported Headers *//* ===================================================================== */#include "pin.H"#include &lt;iostream&gt;#include &lt;fstream&gt;#include &lt;string&gt;#include &lt;stdio.h&gt;#include &lt;string.h&gt;#include &lt;stdlib.h&gt;#include &lt;list&gt;/* ===================================================================== *//* Global Variables *//* ===================================================================== */std::ofstream TraceFile;std::list lst;/* ===================================================================== *//* Methods to enumerate exported functions from images (Module) *//* ===================================================================== */void EnumExportedFunctions(const char *, void(*callback)(char*));int Rva2Offset(unsigned int);typedef struct {unsigned char Name[8];unsigned int VirtualSize;unsigned int VirtualAddress;unsigned int SizeOfRawData;unsigned int PointerToRawData;unsigned int PointerToRelocations;unsigned int PointerToLineNumbers;unsigned short NumberOfRelocations;unsigned short NumberOfLineNumbers;unsigned int Characteristics;} sectionHeader;sectionHeader *sections;unsigned int NumberOfSections = 0;int Rva2Offset(unsigned int rva) {int i = 0;for (i = 0; i &lt; NumberOfSections; i++) { unsigned int x = sections[i].VirtualAddress + sections[i].SizeOfRawData; if (x &gt;= rva) {return sections[i].PointerToRawData + (rva + sections[i].SizeOfRawData) - x;}}return -1;}void EnumExportedFunctions(const char *szFilename, void(*callback)(char*)) {FILE *hFile = fopen(szFilename, "rb");if (hFile != NULL) {if (fgetc(hFile) == 'M' &amp;&amp; fgetc(hFile) == 'Z') {unsigned int e_lfanew = 0;unsigned int NumberOfRvaAndSizes = 0;unsigned int ExportVirtualAddress = 0;unsigned int ExportSize = 0;int i = 0;fseek(hFile, 0x3C, SEEK_SET);fread(&amp;e_lfanew, 4, 1, hFile);fseek(hFile, e_lfanew + 6, SEEK_SET);fread(&amp;NumberOfSections, 2, 1, hFile);fseek(hFile, 108, SEEK_CUR);fread(&amp;amp;NumberOfRvaAndSizes, 4, 1, hFile);if (NumberOfRvaAndSizes == 16) {fread(&amp;ExportVirtualAddress, 4, 1, hFile);fread(&amp;ExportSize, 4, 1, hFile);if (ExportVirtualAddress &gt; 0 &amp;&amp; ExportSize &gt; 0) {fseek(hFile, 120, SEEK_CUR);if (NumberOfSections &gt; 0) {sections = (sectionHeader *)malloc(NumberOfSections * sizeof(sectionHeader));for (i = 0; i &lt; NumberOfSections; i++) {fread(sections[i].Name, 8, 1, hFile);fread(&amp;sections[i].VirtualSize, 4, 1, hFile);fread(&amp;sections[i].VirtualAddress, 4, 1, hFile);fread(&amp;sections[i].SizeOfRawData, 4, 1, hFile);fread(&amp;sections[i].PointerToRawData, 4, 1, hFile);fread(&amp;sections[i].PointerToRelocations, 4, 1, hFile);fread(&amp;sections[i].PointerToLineNumbers, 4, 1, hFile);fread(&amp;sections[i].NumberOfRelocations, 2, 1, hFile);fread(&amp;sections[i].NumberOfLineNumbers, 2, 1, hFile);fread(&amp;sections[i].Characteristics, 4, 1, hFile);}unsigned int NumberOfNames = 0;unsigned int AddressOfNames = 0;int offset = Rva2Offset(ExportVirtualAddress);fseek(hFile, offset + 24, SEEK_SET);fread(&amp;NumberOfNames, 4, 1, hFile);fseek(hFile, 4, SEEK_CUR);fread(&amp;AddressOfNames, 4, 1, hFile);unsigned int namesOffset = Rva2Offset(AddressOfNames), pos = 0;fseek(hFile, namesOffset, SEEK_SET);for (i = 0; i &lt; NumberOfNames; i++) {unsigned int y = 0;fread(&amp;y, 4, 1, hFile);pos = ftell(hFile);fseek(hFile, Rva2Offset(y), SEEK_SET);char c = fgetc(hFile);int szNameLen = 0;while (c != '\\0') {c = fgetc(hFile);szNameLen++;}fseek(hFile, (-szNameLen) - 1, SEEK_CUR);char* szName = (char*)calloc(szNameLen + 1, 1);fread(szName, szNameLen, 1, hFile);callback(szName);fseek(hFile, pos, SEEK_SET);}}}}}fclose(hFile);}}/* ===================================================================== *//* It'll add exported functions into the lst *//* ===================================================================== */void mycallback(char* szName) {lst.push_back(szName);}/* ===================================================================== *//* Commandline Switches *//* ===================================================================== */// If you don't specify the the -o in command line, then the default output will be saved in pinitor.txtKNOB KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool","o", "pinitor.out", "specify trace file name");/* ===================================================================== *//* ===================================================================== *//* Showing function names and their aruments *//* ===================================================================== */// As I mentioned in the my blog at http://rayanfam.com/topics/pinitor/ I have know idea about// how to get the exact number of Arguments which is passed to the specific function except using// a data file which previously stored every functions entrance argument, so if you know any other// way then please tell me about it in my blog.VOID AddInvokeFunctionToFile(CHAR * name, ADDRINT Arg1,ADDRINT Arg2 , ADDRINT Arg3, ADDRINT Arg4, ADDRINT Arg5, ADDRINT Arg6, ADDRINT Arg7, ADDRINT Arg8, ADDRINT Arg9, ADDRINT Arg10, ADDRINT Arg11, ADDRINT Arg12){TraceFile &lt;&lt; name &lt;&lt; "(" &lt;&lt; Arg1 &lt;&lt; ","&lt;&lt; Arg2 &lt;&lt; "," &lt;&lt; Arg3 &lt;&lt; "," &lt;" &lt;&lt; endl &lt;&lt; endl;cerr &lt;&lt; "--------------------------------------------------------------------------------------------" &lt;&lt; endl;cerr &lt;&lt; endl &lt;&lt; KNOB_BASE::StringKnobSummary() &lt;&lt; endl;return -1;}/* ===================================================================== *//* Main *//* ===================================================================== */int main(int argc, char *argv[]){Usage();// Initialize pin &amp; symbol managerPIN_InitSymbols();if(PIN_Init(argc,argv)){//Prints the usagereturn Usage();}// Write to a file since cout and cerr maybe closed by the applicationTraceFile.open(KnobOutputFile.Value().c_str());TraceFile &lt;&lt; hex;TraceFile.setf(ios::showbase);// Register Image to be called to instrument functions.IMG_AddInstrumentFunction(Image, 0);PIN_AddFiniFunction(Fini, 0);// Never returnsPIN_StartProgram();return 0;}/* ===================================================================== *//* eof *//* ===================================================================== */This project is done by the contribution of my best friend Sima.Todo : Show the exact number of Arguments to functions ( I have no idea except using a list of functions with their input parameters count. If you know the other ways please tell me in comments below.) Test And Publish the Result of most used functions in virus samples Build Such tool for ELF Binaries Build Intel64 version of PinitorSpecial thanks to Mahdi Golshani, one of my friends who makes the logo." }, { "title": "Getting started with Python and Qt for cross-platform GUI apps", "url": "/topics/gui-python-qt/", "categories": "programming, software", "tags": "beginner, bitcoin, developement, developer, gui, how-to, pyqt, pyqt5, python, python3, qt", "date": "2017-12-18 00:00:00 +0000", "snippet": "Hi everyone, in this blog post I want to explain how to start developing desktop applications using Qt and Python. We will use PyQt5 module in order to connect Qt with Python. Qt was originally designed for use with C++ but it has many benefits which helped its growth and compability on all major desktop platforms (Windows, Linux, Mac OS). A link to a sample program I created is also given at the end of the post in order to help you understand the basics and maybe use it as a skeleton for your project.First we start “Qt Designer” which helps us create a GUI for our application. This step is the same regardless of which programming language you want to use for the backend and logic of your program.We drag and drop two buttons and a text label into our main window. We will also give them meaningful names: “prev_button” and “next_button”. “TextLabel” name seems fine for now.We can change item properties using the “property editor” on the right:In the panel under “Property Editor” you can also see the name “Signal/Slot Editor” which is an important functionality in Qt. but for now we won’t cover this and only stick to the very basics.Okay, you can fiddle with Qt Designer if you like, finally you should save it somewhere on your computer with the format “.ui (Designer UI Files)”.*** It’s import to know that we are using Python3 and PyQt5 in this tutorial ***Now we should open some IDE or text Editor and start working on the backend python application.we start by importing the required modules and specifying ui file for the application to use:from PyQt5 import QtCore, uic, QtWidgetsqtCreatorFile = "btc.ui" # Enter file here.Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)You should also tell python to start a GUI application!:class MyApp(QtWidgets.QMainWindow, Ui_MainWindow):def __init__(self): QtWidgets.QMainWindow.__init__(self) Ui_MainWindow.__init__(self) self.setupUi(self) #.... you application continues here ....if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) window = MyApp() window.show() sys.exit(app.exec_())It is not important of you do not understand what these lines mean at first. Gradually you will learn everything.I recommend you open the template file and continue your work from that. It is mych easier than typing all of these or copying them from here. the next important thing is connecting the buttons on the screen to some functions in our application, or else this buttons won’t do anything when pressed. so you add this line in __init__ in your main qt app class:self.prev_button.clicked.connect(self.GoPrev)It says that when user clicked “prev_button” run the function “GoPrev” the same way for this:self.next_button.clicked.connect(self.GoNext)then we define our click functions which should only take one argument which is self:def GoPrev(self): ....and do you stuff there or call other functions from there. There may also be other ways of doing this which I’m not aware of. I’m not a professional Qt developer anyway! One more thing, this is how you set the text for a label:self.label.setText("This is a useless text box")Now you know the basics and can google your way for interacting with more controls and using advanced functions of Qt. The python code and UI file should be together for the program to start.For now we won’t cover how to package it into a single executable file or handle Qt dependencies. It’s good to know this program can be run without any modification on Windows, Linux or Mac OS or any other operating system which supports Qt! How great is that?!The sample given below is an application which retrieves bitcoin price from CoinDesk API and shows it to you. In addition to the current price shown on app start, you can use Next and Previous buttons to view historical data.run it like this:_$ python3 btc.py_I hope it will be both educational and useful!The template file can also be found in the same github project folder with the name “template.py”Link:[ https://github.com/Xcess/python_qt_start ] Have fun developing everyone!Shahriar" }, { "title": "Detecting CPU Structure in .Net Framework", "url": "/topics/detecting-cpu-structure-in-net-framework/", "categories": "net-framework, programming", "tags": "detect-64-bit-os-c, get-environment-os, x86-and-x64-c", "date": "2017-11-11 00:00:00 +0000", "snippet": "The title seems simple, just getting CPU structure to detect if your program running on an AMD64 or an Intel x86 machine.Remember when you install a 32-bit operation system on a 64-bit machine then all features are 32 bit so your program supports only 32-bit processor and can’t be a 64-bit one even if your CPU is x64.By the way I search a lot across the Internet for getting the answer but almost all of them gives me , wrong information and that’s because whenever you try to get information about CPU in current PE then the result is same as current running PE structure and in almost all the situations developers set their program to support both x86 and x64 so the final EXE file will be an x86 one and you can’t correctly get CPU Type.The following code gives you the desired result : static bool is64BitProcess = (IntPtr.Size == 8); static bool is64BitOperatingSystem = is64BitProcess || InternalCheckIsWow64(); [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool IsWow64Process( [In] IntPtr hProcess, [Out] out bool wow64Process ); public static bool InternalCheckIsWow64() { if ((Environment.OSVersion.Version.Major == 5 &amp;&amp; Environment.OSVersion.Version.Minor &gt;= 1) || Environment.OSVersion.Version.Major &gt;= 6) { using (Process p = Process.GetCurrentProcess()) { bool retVal; if (!IsWow64Process(p.Handle, out retVal)) { return false; } return retVal; } } else { return false; } } public static string GetOSStructure() { if (is64BitOperatingSystem) return "x64"; return "x86"; } static void Main(string[] args) { Console.WriteLine(GetOSStructure()); Console.ReadKey(); }Remember if you build your application just for x64 machines then your CPU is always x64.That’s obvious. 😀" }, { "title": "Getting started with Windows Server command line", "url": "/topics/getting-started-with-windows-server-command-line/", "categories": "network, sysadmin, windows", "tags": "active-directory, cmd, network, powershell, server-core, windows, windows-server, windows-server-2012-r2, windows-server-core", "date": "2017-11-10 00:00:00 +0000", "snippet": "Hello everyone, In this post I am going to introduce some basic commands that are used to configure Windows Server. Of course if you are using Windows Server with GUI, you may not need these command at all (except for automation maybe). However if you work in an environment in which you configure windows network on a regular basis, you need to know some basic stuff B)OK, Imagine you have installed Windows Server Core 2012 R2 on a server and now want to set basic configuration of your server (It’s highly possible that other versions of windows are configured exactly the same (like even windows 7 etc), but It is not tested by me):Network:# ip addressnetsh interface ipv4 address name="Local Area Network" source=static address=192.168.1.10 mask=255.255.255.0 gateway=192.168.1.1# dns servernetsh interface ipv4 add dnsserver name="Local Area Connection" address=8.8.8.8Hostname:# view hostname (output of this command shows hostname)hostname# change hostnamenetdom renamecomputer WINSRV1 /NewName:WINSRV2(you should reboot after setting hostname)reboot:shutdown /r /t 0okay, now we want to join this computer to a domain:Joining to a domain:netdom join WINSRV2 /domain:lab.local /Userd:Administrator /password:Ab123456@(you should reboot after joining to domain)Installing a Role:well, windows server is of no use without any roles installed. So as an example we will install Internet Information Services (IIS):# the first method uses pkgmgr and is kinda obsolete. but if it works and is supported on your platform then no need to change... :)start /w pkgmgr /l:log.etw /u:IIS-WebServerRole;WAS-WindowsActivationService;WAS-ProcessModel;WAS-NetFxEnvironment;WAS-ConfigurationAPI# the second command uses powershell cmdlets and is newer and will be supported in future versions of windows (probably even as the main method of interacting with windows server)import-module servermanageradd-windowsfeature web-server -includeallsubfeatureEnable PowerShell Remoting:PowerShell remoting enables you to run powershell command over the network on other computer. I know many powershell or cmd command for management have -ComputerName or similar argument for specifying the destination computer, but most of them work over non-standard ports and each of them may use a different microsoft remote managemnt facility. Furthermore you may run into strange permission or other problems. So it won’t result in a smooth remote exprience.To enable PS Remoting run this command(it can be enabled with Group Policy if you have a lot of systems to manage remotely):Enable-PSRemoting –Forcethen connect to them like this:Enter-PSSession –Computername# specifying credentials be like:Enter-PSSession –Computername "WINSRV2" –Credential "LAB\\administrator"or if you want to run a command of multiple machines in parallel:Invoke-Command –name WINSRV1, WINSRV2, PC3 –ScriptBlock {Get-Process}That is the end of this post! Please feel free to comment below.Thanks" }, { "title": "Get everything from .Net Reflection", "url": "/topics/get-everything-from-net-reflection-by-two-method/", "categories": "net-framework, programming", "tags": "net-reflection, c-plugin, c-reflection, create-plugin, dll-plugin, dynamically-call-function, invoke-function", "date": "2017-10-31 00:00:00 +0000", "snippet": "In the previous posts, I’d explained about low-level languages and play around kernel-mode reversing but now let’s dig into the high-level languages. In this post, I’m gonna share a comprehensive method that took more than two weeks for me to build such small and perfect function to play with .Net framework reflection. I also share this functions in my GitHub which can be accessed through this link..Net reflection gives .net framework a high flexibility by dynamically invoking functions from .net IL files and it has good features which can be used in order to build a plugin-based application in both desktop applications and web applications.But for now, let me show you what these functions are and how we can use them.I create two methods to cover all the possibilities in which a target function can be defined, the first one is for situations where you have a function that doesn’t need any argument.It implemented as below : static object InvokeAssemblyWithoutArgumant(string Path, string MethodName, object[] ArgumantsToContructor = null) { object ret = null; System.Reflection.Assembly myDllAssembly = System.Reflection.Assembly.LoadFile(Path); if (ArgumantsToContructor == null) { foreach (Type item in myDllAssembly.GetTypes()) { ret = item.GetMethod(MethodName).Invoke(Activator.CreateInstance(item), null); } } else { foreach (Type item in myDllAssembly.GetTypes()) { ret = item.GetMethod(MethodName).Invoke(Activator.CreateInstance(item, ArgumantsToContructor), null); } } return ret; }and the second one : static object InvokeAssemblyWithArgumant(string Path, string MethodName, object[] argumantToMethod, object[] ArgumantsToContructor = null) { object ret = null; System.Reflection.Assembly myDllAssembly = System.Reflection.Assembly.LoadFile(Path); if (ArgumantsToContructor == null) { foreach (Type item in myDllAssembly.GetTypes()) { ret = item.GetMethod(MethodName).Invoke(Activator.CreateInstance(item), argumantToMethod); } } else { foreach (Type item in myDllAssembly.GetTypes()) { ret = item.GetMethod(MethodName).Invoke(Activator.CreateInstance(item, ArgumantsToContructor), argumantToMethod); } } return ret; }The second one is used for situations where you wanna pass the parameter(s) to the function.For instance …Consider you built a class library (.dll plugin) from the Visual Studio with the following syntax : public class Class1 { public Class1(string Arg1) { //Make program ready for first usage System.Windows.Forms.MessageBox.Show("Constructor Invoked !" + Arg1); } public static string MyPluginFunction1(int a, int b) { System.Windows.Forms.MessageBox.Show("I'm here in MyPluginFunction.(Args:" + a + "-" + b + ")"); // Do what to want to do as a plugin return "Successful"; } public static string MyPluginFunction2() { System.Windows.Forms.MessageBox.Show("I'm here in MyPluginFunction (without arg)."); // Do what to want to do as a plugin return "Successful"; } }If you want to call MyPluginFunction1 then you should use the second method which gives an array of object to pass to the method. //dll path (Plugin Path) string PathToPlugin = Environment.CurrentDirectory + "\\\\Plugin.dll"; // it's extension can be anything ! // you can also load assembly from a base64 string. InvokeAssemblyWithArgumant(PathToPlugin, "MyPluginFunction1", new object[] { 1, 2 }, new object[] { "Sample Arg to constructor" });If you want to call MyPluginFunction2 then you should use the first method which invokes the method directly without any arguments. //dll path (Plugin Path) string PathToPlugin = Environment.CurrentDirectory + "\\\\Plugin.dll"; // it's extension can be anything ! // you can also load assembly from a base64 string. InvokeAssemblyWithoutArgumant(PathToPlugin, "MyPluginFunction2", new object[] { "Sample Arg to constructor" });Important Note: If you have an exception like, object reference not set to an instance it is because the method name is incorrect or you invoke a non-static function within a static function or invoke a static function within a non-static function so please keep in mind the invoker function and target function should have the same state. This post is also available on Github :https://github.com/SinaKarvandi/UseAllOfDotNetReflection" }, { "title": "Fooling Windows about its internal CPU", "url": "/topics/fooling-windows-about-cpu/", "categories": "debugging, kernel-mode, windows", "tags": "change-cpu, change-kernel, edit-windows-internals", "date": "2017-10-01 00:00:00 +0000", "snippet": "In this post, I’m gonna show you how you can fool windows about its internal structure and sometimes give it wrong information about its internal capabilities or internal information which can bring you a lot of fun. (At least for me !)But don’t do that it can hurt your system actually but this post is about how to change CPU Capacity measurement of Windows and see its result in user-mode programs.There is a good article here which gives you lots of information. I recommend seeing all its structures before start reading rest of this post.Ok, As you know windows contains lots of structures which stores its internal information about the system which is running on and almost all of this information can be found by using NtQuerySystemInformation and you can see all about it on MSDN (In the future post I will give you more details about this function.)I’m gonna use nt!_KPCR and Code Machine describes it well :nt!_KPCRKPCR represents the Kernel Processor Control Region. The KPCR contains per-CPU information which is shared by the kernel and the HAL. There are as many KPCR in the system as there are CPUs.The KPCR of the current CPU is always accessible at fs:[0] on the x86 systems and gs:[0] on x64 systems. Commonly used kernel functions like PsGetCurrentProcess() and KeGetCurrentThread() retrieve information from the KPCR using the FS/GS relative accesses.The Prcb field contains an embedded KPRCB structure that represents the Kernel Processor Control Block.The debugger’s “!pcr” command displays partial contents of the PCR.You can see all Windbg gives to you from Windows symbols :lkd&gt; dt nt!_KPCR+0x000 NtTib : _NT_TIB+0x000 GdtBase : Ptr64 _KGDTENTRY64+0x008 TssBase : Ptr64 _KTSS64+0x010 UserRsp : Uint8B+0x018 Self : Ptr64 _KPCR+0x020 CurrentPrcb : Ptr64 _KPRCB+0x028 LockArray : Ptr64 _KSPIN_LOCK_QUEUE+0x030 Used_Self : Ptr64 Void+0x038 IdtBase : Ptr64 _KIDTENTRY64+0x040 Unused : [2] Uint8B+0x050 Irql : UChar+0x051 SecondLevelCacheAssociativity : UChar+0x052 ObsoleteNumber : UChar+0x053 Fill0 : UChar+0x054 Unused0 : [3] Uint4B+0x060 MajorVersion : Uint2B+0x062 MinorVersion : Uint2B+0x064 StallScaleFactor : Uint4B+0x068 Unused1 : [3] Ptr64 Void+0x080 KernelReserved : [15] Uint4B+0x0bc SecondLevelCacheSize : Uint4B+0x0c0 HalReserved : [16] Uint4B+0x100 Unused2 : Uint4B+0x108 KdVersionBlock : Ptr64 Void+0x110 Unused3 : Ptr64 Void+0x118 PcrAlign1 : [24] Uint4B+0x180 Prcb : _KPRCBThere are lots of great information about Idt , IRQL in the above structure so it must be an important one!Look at the last one, which is Prcb!Mark Russinovich in Windows Internals wrote “The PCR and PRCB contain information about the state of each processor in the system such as current IRQL, a pointer to the hardware Idt, the currently running thread, and the next thread selected to run. The kernel and the HAL use this information to perform architecture-specific and machine-specific actions.”.So all we need is seeing what we can do with such a structure. Let me show you the result of windbg symbols.kd&gt; dt nt!_KPRCB+0x000 MxCsr : Uint4B+0x004 LegacyNumber : UChar+0x005 ReservedMustBeZero : UChar+0x006 InterruptRequest : UChar+0x007 IdleHalt : UChar+0x008 CurrentThread : Ptr64 _KTHREAD+0x010 NextThread : Ptr64 _KTHREAD+0x018 IdleThread : Ptr64 _KTHREAD+0x020 NestingLevel : UChar+0x021 ClockOwner : UChar+0x022 PendingTickFlags : UChar+0x022 PendingTick : Pos 0, 1 Bit+0x022 PendingBackupTick : Pos 1, 1 Bit+0x023 IdleState : UChar+0x024 Number : Uint4B+0x028 RspBase : Uint8B+0x030 PrcbLock : Uint8B+0x038 PriorityState : Ptr64 Char+0x040 CpuType : Char+0x041 CpuID : Char+0x042 CpuStep : Uint2B+0x042 CpuStepping : UChar+0x043 CpuModel : UChar+0x044 MHz : Uint4B+0x048 HalReserved : [8] Uint8B+0x088 MinorVersion : Uint2B+0x08a MajorVersion : Uint2B+0x08c BuildType : UChar+0x08d CpuVendor : UChar+0x08e CoresPerPhysicalProcessor : UChar+0x08f LogicalProcessorsPerCore : UChar+0x090 PrcbPad04 : [6] Uint8B+0x0c0 ParentNode : Ptr64 _KNODE+0x0c8 GroupSetMember : Uint8B+0x0d0 Group : UChar+0x0d1 GroupIndex : UChar+0x0d2 PrcbPad05 : [2] UChar+0x0d4 InitialApicId : Uint4B+0x0d8 ScbOffset : Uint4B+0x0dc ApicMask : Uint4B+0x0e0 AcpiReserved : Ptr64 Void+0x0e8 CFlushSize : Uint4B+0x0ec PrcbPad10 : Uint4B+0x0f0 PrcbPad11 : [2] Uint8B+0x100 ProcessorState : _KPROCESSOR_STATE+0x6c0 PrcbPad12 : [6] Uint8B+0x6f0 LockQueue : [17] _KSPIN_LOCK_QUEUE+0x800 PPLookasideList : [16] _PP_LOOKASIDE_LIST+0x900 PPNxPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL+0x1500 PPNPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL+0x2100 PPPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL+0x2d00 PrcbPad20 : Uint8B+0x2d08 DeferredReadyListHead : _SINGLE_LIST_ENTRY+0x2d10 MmPageFaultCount : Int4B+0x2d14 MmCopyOnWriteCount : Int4B+0x2d18 MmTransitionCount : Int4B+0x2d1c MmDemandZeroCount : Int4B+0x2d20 MmPageReadCount : Int4B+0x2d24 MmPageReadIoCount : Int4B+0x2d28 MmDirtyPagesWriteCount : Int4B+0x2d2c MmDirtyWriteIoCount : Int4B+0x2d30 MmMappedPagesWriteCount : Int4B+0x2d34 MmMappedWriteIoCount : Int4B+0x2d38 KeSystemCalls : Uint4B+0x2d3c KeContextSwitches : Uint4B+0x2d40 PrcbPad40 : Uint4B+0x2d44 CcFastReadNoWait : Uint4B+0x2d48 CcFastReadWait : Uint4B+0x2d4c CcFastReadNotPossible : Uint4B+0x2d50 CcCopyReadNoWait : Uint4B+0x2d54 CcCopyReadWait : Uint4B+0x2d58 CcCopyReadNoWaitMiss : Uint4B+0x2d5c IoReadOperationCount : Int4B+0x2d60 IoWriteOperationCount : Int4B+0x2d64 IoOtherOperationCount : Int4B+0x2d68 IoReadTransferCount : _LARGE_INTEGER+0x2d70 IoWriteTransferCount : _LARGE_INTEGER+0x2d78 IoOtherTransferCount : _LARGE_INTEGER+0x2d80 PacketBarrier : Int4B+0x2d84 TargetCount : Int4B+0x2d88 IpiFrozen : Uint4B+0x2d90 IsrDpcStats : Ptr64 Void+0x2d98 DeviceInterrupts : Uint4B+0x2d9c LookasideIrpFloat : Int4B+0x2da0 InterruptLastCount : Uint4B+0x2da4 InterruptRate : Uint4B+0x2da8 LastNonHrTimerExpiration : Uint8B+0x2db0 PrcbPad41 : [20] Uint4B+0x2e00 DpcData : [2] _KDPC_DATA+0x2e50 DpcStack : Ptr64 Void+0x2e58 MaximumDpcQueueDepth : Int4B+0x2e5c DpcRequestRate : Uint4B+0x2e60 MinimumDpcRate : Uint4B+0x2e64 DpcLastCount : Uint4B+0x2e68 ThreadDpcEnable : UChar+0x2e69 QuantumEnd : UChar+0x2e6a DpcRoutineActive : UChar+0x2e6b IdleSchedule : UChar+0x2e6c DpcRequestSummary : Int4B+0x2e6c DpcRequestSlot : [2] Int2B+0x2e6c NormalDpcState : Int2B+0x2e6e ThreadDpcState : Int2B+0x2e6c DpcNormalProcessingActive : Pos 0, 1 Bit+0x2e6c DpcNormalProcessingRequested : Pos 1, 1 Bit+0x2e6c DpcNormalThreadSignal : Pos 2, 1 Bit+0x2e6c DpcNormalTimerExpiration : Pos 3, 1 Bit+0x2e6c DpcNormalDpcPresent : Pos 4, 1 Bit+0x2e6c DpcNormalLocalInterrupt : Pos 5, 1 Bit+0x2e6c DpcNormalSpare : Pos 6, 10 Bits+0x2e6c DpcThreadActive : Pos 16, 1 Bit+0x2e6c DpcThreadRequested : Pos 17, 1 Bit+0x2e6c DpcThreadSpare : Pos 18, 14 Bits+0x2e70 LastTimerHand : Uint4B+0x2e74 LastTick : Uint4B+0x2e78 ClockInterrupts : Uint4B+0x2e7c ReadyScanTick : Uint4B+0x2e80 InterruptObject : [256] Ptr64 Void+0x3680 TimerTable : _KTIMER_TABLE+0x5880 DpcGate : _KGATE+0x5898 PrcbPad52 : Ptr64 Void+0x58a0 CallDpc : _KDPC+0x58e0 ClockKeepAlive : Int4B+0x58e4 PrcbPad60 : [2] UChar+0x58e6 NmiActive : Uint2B+0x58e8 DpcWatchdogPeriod : Int4B+0x58ec DpcWatchdogCount : Int4B+0x58f0 KeSpinLockOrdering : Int4B+0x58f4 DpcWatchdogProfileCumulativeDpcThreshold : Uint4B+0x58f8 CachedPtes : Ptr64 Void+0x5900 WaitListHead : _LIST_ENTRY+0x5910 WaitLock : Uint8B+0x5918 ReadySummary : Uint4B+0x591c AffinitizedSelectionMask : Int4B+0x5920 QueueIndex : Uint4B+0x5924 PrcbPad75 : [3] Uint4B+0x5930 TimerExpirationDpc : _KDPC+0x5970 ScbQueue : _RTL_RB_TREE+0x5980 DispatcherReadyListHead : [32] _LIST_ENTRY+0x5b80 InterruptCount : Uint4B+0x5b84 KernelTime : Uint4B+0x5b88 UserTime : Uint4B+0x5b8c DpcTime : Uint4B+0x5b90 InterruptTime : Uint4B+0x5b94 AdjustDpcThreshold : Uint4B+0x5b98 DebuggerSavedIRQL : UChar+0x5b99 GroupSchedulingOverQuota : UChar+0x5b9a DeepSleep : UChar+0x5b9b PrcbPad80 : [5] UChar+0x5ba0 DpcTimeCount : Uint4B+0x5ba4 DpcTimeLimit : Uint4B+0x5ba8 PeriodicCount : Uint4B+0x5bac PeriodicBias : Uint4B+0x5bb0 AvailableTime : Uint4B+0x5bb4 KeExceptionDispatchCount : Uint4B+0x5bb8 ReadyThreadCount : Uint4B+0x5bc0 StartCycles : Uint8B+0x5bc8 TaggedCyclesStart : Uint8B+0x5bd0 TaggedCycles : [2] Uint8B+0x5be0 GenerationTarget : Uint8B+0x5be8 AffinitizedCycles : Uint8B+0x5bf0 ImportantCycles : Uint8B+0x5bf8 UnimportantCycles : Uint8B+0x5c00 ReadyQueueExpectedRunTime : Uint8B+0x5c08 PrcbPad81 : [21] Uint4B+0x5c5c DpcWatchdogProfileSingleDpcThreshold : Uint4B+0x5c60 MmSpinLockOrdering : Int4B+0x5c64 PageColor : Uint4B+0x5c68 NodeColor : Uint4B+0x5c6c NodeShiftedColor : Uint4B+0x5c70 SecondaryColorMask : Uint4B+0x5c74 PrcbPad83 : Uint4B+0x5c78 CycleTime : Uint8B+0x5c80 Cycles : [4] [2] Uint8B+0x5cc0 PrcbPad84 : [16] Uint4B+0x5d00 CcFastMdlReadNoWait : Uint4B+0x5d04 CcFastMdlReadWait : Uint4B+0x5d08 CcFastMdlReadNotPossible : Uint4B+0x5d0c CcMapDataNoWait : Uint4B+0x5d10 CcMapDataWait : Uint4B+0x5d14 CcPinMappedDataCount : Uint4B+0x5d18 CcPinReadNoWait : Uint4B+0x5d1c CcPinReadWait : Uint4B+0x5d20 CcMdlReadNoWait : Uint4B+0x5d24 CcMdlReadWait : Uint4B+0x5d28 CcLazyWriteHotSpots : Uint4B+0x5d2c CcLazyWriteIos : Uint4B+0x5d30 CcLazyWritePages : Uint4B+0x5d34 CcDataFlushes : Uint4B+0x5d38 CcDataPages : Uint4B+0x5d3c CcLostDelayedWrites : Uint4B+0x5d40 CcFastReadResourceMiss : Uint4B+0x5d44 CcCopyReadWaitMiss : Uint4B+0x5d48 CcFastMdlReadResourceMiss : Uint4B+0x5d4c CcMapDataNoWaitMiss : Uint4B+0x5d50 CcMapDataWaitMiss : Uint4B+0x5d54 CcPinReadNoWaitMiss : Uint4B+0x5d58 CcPinReadWaitMiss : Uint4B+0x5d5c CcMdlReadNoWaitMiss : Uint4B+0x5d60 CcMdlReadWaitMiss : Uint4B+0x5d64 CcReadAheadIos : Uint4B+0x5d68 MmCacheTransitionCount : Int4B+0x5d6c MmCacheReadCount : Int4B+0x5d70 MmCacheIoCount : Int4B+0x5d74 PrcbPad91 : [3] Uint4B+0x5d80 PowerState : _PROCESSOR_POWER_STATE+0x5f58 ScbList : _LIST_ENTRY+0x5f68 ForceIdleDpc : _KDPC+0x5fa8 PrcbPad92 : [18] Uint4B+0x5ff0 KeAlignmentFixupCount : Uint4B+0x5ff8 DpcWatchdogDpc : _KDPC+0x6038 DpcWatchdogTimer : _KTIMER+0x6078 Cache : [5] _CACHE_DESCRIPTOR+0x60b4 CacheCount : Uint4B+0x60b8 CachedCommit : Uint4B+0x60bc CachedResidentAvailable : Uint4B+0x60c0 HyperPte : Ptr64 Void+0x60c8 WheaInfo : Ptr64 Void+0x60d0 EtwSupport : Ptr64 Void+0x60e0 InterruptObjectPool : _SLIST_HEADER+0x60f0 HypercallPageList : _SLIST_HEADER+0x6100 HypercallCachedPages : Ptr64 Void+0x6108 VirtualApicAssist : Ptr64 Void+0x6110 StatisticsPage : Ptr64 Uint8B+0x6118 PackageProcessorSet : _KAFFINITY_EX+0x61c0 SharedReadyQueueMask : Uint8B+0x61c8 SharedReadyQueue : Ptr64 _KSHARED_READY_QUEUE+0x61d0 SharedQueueScanOwner : Uint4B+0x61d4 ScanSiblingIndex : Uint4B+0x61d8 CoreProcessorSet : Uint8B+0x61e0 ScanSiblingMask : Uint8B+0x61e8 LLCMask : Uint8B+0x61f0 CacheProcessorMask : [5] Uint8B+0x6218 ProcessorProfileControlArea : Ptr64 _PROCESSOR_PROFILE_CONTROL_AREA+0x6220 ProfileEventIndexAddress : Ptr64 Void+0x6228 DpcWatchdogProfile : Ptr64 Ptr64 Void+0x6230 DpcWatchdogProfileCurrentEmptyCapture : Ptr64 Ptr64 Void+0x6238 PrcbPad94 : [9] Uint8B+0x6280 SynchCounters : _SYNCH_COUNTERS+0x6338 PteBitCache : Uint8B+0x6340 PteBitOffset : Uint4B+0x6348 FsCounters : _FILESYSTEM_DISK_COUNTERS+0x6358 VendorString : [13] UChar+0x6365 PrcbPad100 : [2] UChar+0x6367 PendingVirtualLittle : UChar+0x6368 FeatureBits : Uint8B+0x6370 PrcbPad110 : Uint4B+0x6378 UpdateSignature : _LARGE_INTEGER+0x6380 Context : Ptr64 _CONTEXT+0x6388 ContextFlagsInit : Uint4B+0x6390 ExtendedState : Ptr64 _XSAVE_AREA+0x6398 IsrStack : Ptr64 Void+0x63a0 EntropyTimingState : _KENTROPY_TIMING_STATE+0x64f0 AbSelfIoBoostsList : _SINGLE_LIST_ENTRY+0x64f8 AbPropagateBoostsList : _SINGLE_LIST_ENTRY+0x6500 AbDpc : _KDPC+0x6540 IoIrpStackProfilerCurrent : _IOP_IRP_STACK_PROFILER+0x6594 IoIrpStackProfilerPrevious : _IOP_IRP_STACK_PROFILER+0x6600 LocalSharedReadyQueue : _KSHARED_READY_QUEUE+0x6870 TimerExpirationTrace : [16] _KTIMER_EXPIRATION_TRACE+0x6970 TimerExpirationTraceCount : Uint4B+0x6978 ExSaPageArray : Ptr64 Void+0x6980 SecureFault : _KSECURE_FAULT_INFORMATION+0x69c0 Mailbox : Ptr64 _REQUEST_MAILBOX+0x6a00 RequestMailbox : [1] _REQUEST_MAILBOXLooking at the above structure shows a lot of great information about the system processor.You can find something like Current and Next Thread _KTHREAD, all information about CPU like type and capacity and manufacture company, DPC, Cycles, User-mode and Kernel-mode times and etc.By the way, you now know that windows stores some of its internal information about CPU in nt!_PRCB and you should know find where this structure is located, I’m running an AMD64 version of Windows so it’s time to use gs:[0] in order to get the location of nt!_KPCR structure.By using the following command you can get gs:[0] values :dq gs:[0]you can also use !pcr which gives you almost the same result :)So, now we have where the _KPCR is located and now we should simply use :dt &lt;Address&gt; nt!_KPCRAnd after finding the pcrb now we should use dt in order to map the result into the _kpcrb structure. If you want to be sure that you are in a right place you can search for Vendor string as follows : It gives you kinds of hex which is equal to “GenuineIntel” and this is, of course, my CPU vendor.  After this, we know that we are in a right place so let see other fields in order to find something interesting!After studying this structure now I see something that is so familiar to me and that is “Mhz”. Can you see my CPU’s Mhz value ?!  Let me see my windows properties.  Ok, this is what we see previously, Doesn’t it ?! This value (2.71 GHz) exactly equals to 0xA98 so changing this value must be interesting.I find this value and changing it to 0x10 which is equal to 16 in decimal. So we modified the value from 0x98 to 0x10 and now we want to see if it takes effect in system information or not, so let’s view system properties again. That’s it, guys!You can see we changed windows properties and now it says that you have a 16 Mhz CPU.Of course, this was an example of what we can do with prcb and it doesn’t affect on windows but you can do many other things by modifying this structure like changing threads or block a thread but keep in mind that editing this structure in a wrong way almost always causes the Blue Screen Of Dead.Thanks for reading." }, { "title": "Simple OpenLDAP + phpLDAPadmin setup", "url": "/topics/simple-openldap-phpldapadmin-setup/", "categories": "linux, network", "tags": "debian, directory-service, ldap, openldap, phpldapadmin", "date": "2017-09-29 00:00:00 +0000", "snippet": "Hello everyone,In this blog post I’m going to show you how to setup a simple OpenLDAP server  with phpldapadmin on apache. I’m posting this because I didn’t find any up to date content on how to do this.Installing OpenLDAP and exploring it with phpLDAPadmin helps you learn LDAP structure and use cases. Also you can redirect authentication of other devices on your network to this server.These steps are run in a fresh debian wheezy (7) installation:[click on it]LDAP authentication can be used with a lot of services like Apache web authentication, FreeRADIUS, Cisco network devices, etc. which we will go over one by one in future posts.Any comments are appreciated as always." }, { "title": "GRE over IPSec in Cisco IOS", "url": "/topics/gre-over-ipsec-in-cisco-ios/", "categories": "cisco, network", "tags": "cisco, gre, ios, ipsec, network, tunnel", "date": "2017-09-17 00:00:00 +0000", "snippet": "Cisco GRE over IPSECIn the following is a sample configuration for GRE/IPSEC in Cisco IOS devices. you can tailor this to your needs (changing encryption algorithms, IP addresses etc), just remember to make sure they are the same on both sides (except for IP of course).In later posts we will discover how to do this scenario in other platforms like Cisco ASA and StrongSWAN on Linux.The best guide for learning how IPSec works: http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-77.pdfR1(config)# interface Tunnel0R1(config-if)# ip address 172.16.0.1 255.255.255.0R1(config-if)# ip mtu 1400R1(config-if)# ip tcp adjust-mss 1360R1(config-if)# tunnel source 1.1.1.10R1(config-if)# tunnel destination 2.2.2.10R1(config)# ip route 192.168.2.0 255.255.255.0 172.16.0.2R1(config)# crypto isakmp policy 1R1(config-isakmp)# encr 3desR1(config-isakmp)# hash md5R1(config-isakmp)# authentication pre-shareR1(config-isakmp)# group 2R1(config-isakmp)# lifetime 86400R1(config)# crypto isakmp key CISCO address 2.2.2.10R1(config)# crypto ipsec transform-set TS esp-3des esp-md5-hmacR1(cfg-crypto-trans)# mode transportR1(config)# crypto ipsec profile protect-greR1(ipsec-profile)# set security-association lifetime seconds 86400R1(ipsec-profile)# set transform-set TSR1(config)# interface Tunnel 0R1(config-if)# tunnel protection ipsec profile protect-greR2(config)# interface Tunnel0R2(config-if)# ip address 172.16.0.2 255.255.255.0R2(config-if)# ip mtu 1400R2(config-if)# ip tcp adjust-mss 1360R2(config-if)# tunnel source 2.2.2.10R2(config-if)# tunnel destination 1.1.1.10R2(config)# ip route 192.168.1.0 255.255.255.0 172.16.0.1R2(config)# crypto isakmp policy 1R2(config-isakmp)# encr 3desR2(config-isakmp)# hash md5R2(config-isakmp)# authentication pre-shareR2(config-isakmp)# group 2R2(config-isakmp)# lifetime 86400R2(config)# crypto isakmp key CISCO address 1.1.1.10R2(config)# crypto ipsec transform-set TS esp-3des esp-md5-hmacR2(cfg-crypto-trans)# mode transportR2(config)# crypto ipsec profile protect-greR2(ipsec-profile)# set security-association lifetime seconds 86400R2(ipsec-profile)# set transform-set TSR2(config)# interface Tunnel 0R2(config-if)# tunnel protection ipsec profile protect-gre" }, { "title": "Cisco IOS and StrongSWAN IPSEC VPN", "url": "/topics/cisco-ios-and-strongswan-ipsec-vpn/", "categories": "linux, network, sysadmin", "tags": "cisco, debian, ios, ipsec, linux, opensource, openswan, strongswan, tunnel, vpn", "date": "2017-09-17 00:00:00 +0000", "snippet": "In this blog post we will cover IPSEC tunnel between Linux StrongSWAN and Cisco IOS.The strongSWAN config file can copied exactly as is to another server with the IP of Cisco Router and the tunnel will be connected between two linux routers. That is you do not need to change right and left in config files. It will be automatically detected from interface IP address (if available of course)Cisco IOS configuration is very similar to previous post.Here are the configuration files:IOS Configuration crypto isakmp policy 1 encr aes hash sha256 authentication pre-share group 14 lifetime 14400 crypto isakmp key cisco address 20.0.0.2 crypto ipsec transform-set ts1 esp-aes esp-sha256-hmacmode tunnelcrypto map cm1 10 ipsec-isakmpset peer 20.0.0.2set transform-set ts1match address 105interface FastEthernet0/0 ip address 20.0.0.1 255.0.0.0 duplex full crypto map cm1interface FastEthernet2/0 ip address 192.168.5.1 255.255.255.0 duplex fullip route 192.168.6.0 255.255.255.0 20.0.0.2route outside 192.168.6.0 255.255.255.0 20.0.0.2access-list 105 permit ip 192.168.5.0 0.0.0.255 192.168.6.0 0.0.0.255StrongSWAN configuration/etc/ipsec.confconn site2site authby=secret esp=aes128-sha256 ike = aes128-sha256-modp2048 ikelifetime = 4h leftid=20.0.0.2 rightid=20.0.0.1 left=20.0.0.2 leftsubnet=192.168.6.0/24 right=20.0.0.1 rightsubnet=192.168.5.0/24 keyexchange=ikev1 pfs=no auto=start remote_peer_type=cisco/etc/ipsec.secret 20.0.0.2 20.0.0.1 : PSK cisco/etc/sysctl.confip_forward=1 (uncomment)# sysctl -p (run command)" }, { "title": "Exploring from User-Mode to Kernel-Mode", "url": "/topics/exploring-from-usermode-to-kernelmode/", "categories": "debugging, kernel-mode, user-mode", "tags": "debug-kernel-function, debug-native-function, kernelmode-to-usermode, trace-functions-to-kernel", "date": "2017-09-02 00:00:00 +0000", "snippet": "There were times when I want to trace instructions from User Mode and continue tracing it into Kernel mode to reverse Windows’s internal implementation with my own supplied parameters from User Mode but there were a big problem and that was, How to access User Mode when you are in a Kernel Debugger or vice versa.Even if I knew about changing debugger context to specific process but there were other problems which cause reversing kernel in this case, impossible.Yesterday one of my friends explains me about how to debug it, then we improve this method and now I wanna share my experience on my blog.Let’s get down to business…First of all, a Kernel Debugger Windbg is needed in order to continue tracing in Kernel.The first essential thing is we need to change the current process context (fs or gs registers) to be able to access user-mode space of the process which we need to trace its functions.!process 0 0 notepad.exeTo get process location then :.process /i AboveAddressWhich will notify you when the processor reaches to this process.Remember you can also view current process by simply calling !thread as shown below :Next step is to let windbg continue and it will notify you whenever it reaches to the desired process, so press g.gIt’s getting a little tricky here.Remember you just loaded the symbols of Kernel-mode modules but now we need user-mode modules’ symbol to find the functions..reloadAnd wait till all current modules symbol become loaded.After that you can see that user-mode modules loaded successfully and you get something like :lm ntSo far so good ;)In this post, I’m gonna break on NtQuerySystemInformation which is a native function of ntdll but you can also break on everything you need, depend on address and symbol files(You can also break on your modules by loading their .pdb files.).Let’s continue until the process calls the NtQuerySystemInformation …The following picture shows how it performs :Ok, everything is ready to step in or step out in windbg by the way you can’t debug like that :(Because if you continue debugging, it is possible that kernel switches its context and your breakpoint hits for the second time and you will lose everything!This problem can be solved by disabling the breakpoint so every time the kernel switches its context then windbg continues until you reach the last point where you were debugging that instruction.I always use bl in order to view and disable my breakpoints.That is it!Hope you guys have a sweet kernel exploring!" }, { "title": "Defeat Malware's Dynamic API Loading", "url": "/topics/defeat-malware-dynamic-api-loading/", "categories": "malware, user-mode", "tags": "debug-dynamic-loading, debug-loadlibrary, malware-analysis", "date": "2017-08-22 00:00:00 +0000", "snippet": "There are thousands of ways which makes malwares resist against static dissambling and static analysing. One of the known ways to circumvent against suspicious API blocking or analysing statically by AV’s, is using LoadLibrary API to dynamically load a library then use its functions and it makes a CPU Intensive task for AV’s to defeat this kind of malwares.This technique consists of using functions like LoadLibraryA or VirtualAlloc then use GetProcAdress. As msdn says, GetProcAddress : Retrieves the address of an exported function or variable from the specified dynamic-link library (DLL).So the viral routine just maps the dll in the process virtual space then finds the function which it needs from exported function the simply call the returned address from GetProcAddress. You can see a good example here.But how can we analyse such a malware ? As you can imagine by now, there is no way to breakpoint on a function which was not loaded previously, in this case the windbg’s sxe comes to play. sxe triggers a breakpoint when a special dll loaded you can use it in the following way :sxe ld:user32.dllAnd another good option is execute command instead of just a simple breakpoint.sxe -c "bp shell32!MessageBoxW" ld:user32.dllThis technique helps us to debug such viruses better and easier." }, { "title": "Lack of rechecking permissions in Android", "url": "/topics/lack-of-rechecking-permissions-in-android/", "categories": "android, pentest", "tags": "android-permissions, change-adndroid-permission, packages-xml", "date": "2017-04-11 00:00:00 +0000", "snippet": "Yesterday me and one of my friends were working on an Android Penetration testing project. After testing some kinds of exploit then we somehow get root privilege with some kinds of limitations. In the case of this exploit we can just write to any file we want and we cannot do anything more because ASLR was preventing us to do.So we just think how we could do something to violate privacy of this Android device then as we know previously, Android devices has some kinds of files that save applications signatures and package names and permissions and this file is placed in :/data/system/packages.xmlAnother juicy file which holds what kinds of groups has access to what kinds of devices is also available in :/etc/permissions/platform.xmlThen we start to edit the first file to see if it is possible to change the permissions of an special package or not, so we do like this :And then find something like this :But in the real case we just edit our application’s permissions and add another permissions to it, then restart the phone and then opened our previous application but this time with new permissions ! Unfortunately it works and has access to this new permissions and I wonder how google didn’t check permissions again ?! They store lots of signatures and use a huge number of cryptography algorithms to check integrity of files but doesn’t recheck this important stuffs. It also works on /etc/permissions/platform.xml, this file has the same affect somehow but it is not as important as previous file because this is some kind of OS settings file but packages.xml holds settings for every applications.Note : we test this commands in Android 5.1.0 but I think google has no plan or doesn’t have any plan to add a new approach for checking permissions validation on newer versions of Android so it should work on newer versions too but not tested.The need to check permissions again is theoretically needless because no one can access to edit this files but I think it should be done by google because in the case of exploits it can have bad affects in people’s privacy.That’s it guys." }, { "title": "Import Address Table (IAT) in action", "url": "/topics/import-address-table-in-action/", "categories": "debugging, user-mode", "tags": "firstthunk, forwardchain, iat, image_import_descriptor, import-address-table-address, orginalfirstthunk, timedatestamp", "date": "2017-04-11 00:00:00 +0000", "snippet": "Did you ever think about how different dll files with different versions and obviously with different addresses of functions work perfectly together ? The answer is Import Address Table (IAT).In the previous post I describe about how to get SSDT. IAT is somehow a User-Mode version of SSDT and in this post I’m gonna write about what I read and experience about IAT in action.Why IAT is important ? It is important because it gives PE executer a list of Functions pointer which normally used to jump to Windows API’s functions. That’s okay but the thing is most of packers and protectors that I see just destroy this table ! So it is important for a reverser to know about this table because if you don’t know about it, simply can’t dump a packed exe .All normal (Not packed) application make this table at first so in the future calls they can use calls or jumps to this addresses to reach to the functions and put function pointer to eip register.For example, I see this kind of calling a lot in ollydbg :Call jmp&amp;.Kernel32.ExitProcessAWhich points to IAT version of ExitProcessA in Kernel32.dll image.One of the ways to easily hook functions is to change IAT’s addresses so the new invoked functions come to your code instead of original function.Where is IAT ?!For creating IAT, process first create a import table by going to “ImageBase (Most of times 0x400000) + 0x3C” in this address you can find a pointer that you can find import table address in (the pointer + 0x80). This table consists of multiple variables in a structure which is called Image_Import_Descriptor and ends with a null Image_Import_Descriptor. The first variable of this structure is OrginalFirstThunk. This field is actually a backup variable (I explain it more in later paragraph in this post.)The second parameter is TimeDateStamp (Which probably points to time or version of building function but can be null) and the third is ForwardChain (Can be null too) and last is Name.Then process just follows the FirstThunk address and goes to dll to find the address of function which FirstThunk points to. If with any reason it can’t find the function then try it again by using OrginalFisrtThunk instead of FirstThunk and if it can’t find it again then simply the program crashes !IAT is actually in .data but if you are interfering with some kinds of packers or proctors it can be different or completely destroyed because if you dump an exe with a destroyed IAT it doesn’t work. If you find IAT in your debugger then keep in mind that all addresses separate with two null dword.I write this post because I can’t find any complete description about how IAT works in action but I simply describe how it works so if you have any problem with finding IAT then you can comment in this post.In future posts I will explain about how to build new IAT when this table is destroyed by packers or protectors." }, { "title": "Bind9 chroot on debian 8", "url": "/topics/bind9-chroot-on-debian-8/", "categories": "linux, network, software, sysadmin", "tags": "bind9-config, bind9-on-debian", "date": "2017-04-03 00:00:00 +0000", "snippet": "From Wikipedia: BIND, or named, is the most widely used Domain Name System (DNS) software on the Internet. On Unix-like operating systems it is the de facto standard.As you know chrooting a process is very beneficial for security as any compromise cannot have effect on the whole system. But be aware escaping from chroot is not impossible. and therefore should not be used as your only security measure on a production DNS resolver.Chrooting Bind is simple, however there are not good HOWTOs, the good ones are all outdated.So I made this Asciinema for “chrooting bind 9 in debian 8” (systemd)[click on it]Let me know of any inaccuracies or suggestions as usual :)Shahriar UPDATE : Thanks to Behrad Eslamifar for letting me know, This debian 8 package will also do the job if you don’t want to do it manually:   https://github.com/cvak/bind-chroot" }, { "title": "Change User-Mode application's virtual address through Kernel Debugging", "url": "/topics/change-user-mode-applications-virtual-address-through-kernel-debugging/", "categories": "debugging, kernel-mode, user-mode", "tags": "change-process-from-kernel, change-virtual-address, kernel-mode-to-user-mode, ring0-to-ring3", "date": "2017-04-02 00:00:00 +0000", "snippet": "Well, it’s somehow an odd topic but sometimes it could be really helpful in some situations.So what are the situations?Imagine sometimes you need to access windows stuffs that aren’t available from user-mode debuggers like ollydbg or through user-mode debugging (e.g memory after 0x7fffffff). In my experience I see some conditions that protectors make a sophisticated check for finding any debugger in memory and then change their approach to stop reverser from reversing the rest of the code. In such a situation you can make a virtual environment then break the machine completely and change your context to process and continue analyzing image. In this case you can overcome protection levels completely or at least overcome some protection levels. (some protectors never allow to run from a Virtual Machine or call some windows APIs to see if a kernel debugger is present or not and you should check for this stuffs first then continue debugging.)So let’s get down to business,In the following tutorial I use a VMware Virtual Machine that is ready for kernel debugging (if you don’t know how to make one pls see this link ,it describes how to do it). Then a kernel debugger (in my case Windbg) and a user-mode debugger (ollydbg).First run myfile.exe in guest machine and attach to it from guest machine by ollydbg to see any editing that made in kernel debugging takes place in myfile.exe then break the Windbg to edit memory from host machine.So I use the following command to get all the processes to see where you can find myfile.exe :kd&gt; !process 0 0And it gives you a long list of processes where you can finally find myfile.exe.PROCESS ffffe001f9652080SessionId: 1 Cid: 0da4 Peb: 7ffdf000 ParentCid: 0588DirBase: 11d6d000 ObjectTable: ffffc0013e905680 HandleCount: &lt;Data Not Accessible&gt;Image: myfile.exeSo for more details about this process you can run :kd&gt; !process ffffe001f9652080 7It should give you something like :1: kd&gt; !process ffffe001f9652080 7PROCESS ffffe001f9652080SessionId: 1 Cid: 0da4 Peb: 7ffdf000 ParentCid: 0588DirBase: 11d6d000 ObjectTable: ffffc0013e905680 HandleCount: &lt;Data Not Accessible&gt;Image: myfile.exeVadRoot ffffe001f64dda10 Vads 129 Clone 0 Private 5676. Modified 520. Locked 0.DeviceMap ffffc0013dff8c30Token ffffc0014336a8e0ElapsedTime 00:08:14.197UserTime 00:00:00.046KernelTime 00:00:00.125...then for switch to myfile.exe you should run :kd&gt; .process /p /r ffffe001f9652080Implicit process is now ffffe001f9652080.cache forcedecodeuser doneLoading User Symbols.....................Now you’re almost done ! you are in a 32 bit enviroment for myfile.exe which you can run all Windbg commands like what you run in Virtual Address (Instead of physical address.)For a sample run :kd&gt; dc 400000It gives you all the memory in myfile.exe’s base address (0x400000) which you can edit memory by something like ea command in windbg and see what’s going on after pressing g and then go to Guest Machine where you can find myfile.exe’s base address from ollydbg and see how it changed form kernel debugger.Thanks for reading" }, { "title": "How to get every detail about SSDT , GDT , IDT in a blink of an eye", "url": "/topics/how-to-get-every-details-about-ssdt-gdt-idt-in-a-blink-of-an-eye/", "categories": "debugging, kernel-mode", "tags": "gdt, global-descriptor-table, idt, interrupt-descriptor-table, ssdt, swishdbgext, system-service-descriptor-table, windbg-extension", "date": "2017-04-01 00:00:00 +0000", "snippet": "In a few days ago I was looking for something to show me the SSDT and GDT (Which is really important in malware analyzing because most of rootkits are interested in hooking and changing this stuffs.)• SSDT (System Service Descriptor Table) • GDT (Global Descriptor Table) • IDT (Interrupt Descriptor Table)They’re really important table in OSes for example SSDT is something like IAT (Import Address Table) in user-mode applications which holds pointer to exported functions of all .dll assemblies and in this case SSDT holds pointer to native windows APIs.You can imagine how an attacker can just change or hook them and start and filter your arguments every time you go through this functions. then I found something like :lkd&gt; u dwo(nt!KiServiceTable)+nt!KiServiceTable L1nt!NtMapUserPhysicalPagesScatter:fffff800‘013728b0 488bc4 mov rax,rsplkd&gt; u dwo(nt!KiServiceTable+4)+nt!KiServiceTable L1nt!NtWaitForSingleObject:fffff800‘012b83a0 4c89442418 mov [rsp+0x18],r8Which wasn’t what I really wants to, but can somehow help cause as I read in one of the articles about Patchguard bypassing,It said :” On Windows x64 kernels, nt!KeServiceDescriptorTable conveys the address of the actual dispatch table and the number of entries in the dispatch table for the native system call interface. In this case, the actual dispatch table is stored as an array of relative offsets in nt!KiServiceTable. “If you don’t know about SSDT and GDT and IDT first google about it then I want to show an amazing tools which called SwishDbgExt and it’s open source and free !available at : https://github.com/comaeio/SwishDbgExtit helps me a lot ! After compiling it from source you need to load dll like :!load C:\\\\users\\\\sina\\\\desktop\\\\SwishDbgExt.dlland then just use !ms_ssdt , and !ms_gdt and !ms_idt in Windbg to get a complete list of information about following tables.Note : Windows kernel also uses Patchguard to protect its kernel from such changes but its just security over obscurity because Kernel Drivers run in a privilege same as kernel and there are tons of article about how to bypass Patchguard and there is nothing like this protection in x86 systems because they don’t have such thing like patchguard (But I don’t know precisely if windows has any special mechanism to protect these tables in x86 machines.).So that’s it guys. There are lots of good things you can find in this Windbg extension that you can find in readme or in github page and it really worths to test." }, { "title": "Useful Configs for Squid3 Cache", "url": "/topics/useful-config-squid3/", "categories": "linux, network, software, sysadmin", "tags": "cache, debian, linux, proxy, squid, squid3", "date": "2017-03-21 00:00:00 +0000", "snippet": "Hi everyone!After searching the web so many times and testing different configurations of Squid, I have found these minimal working configs which you can use to achieve the features you want from Squid3 Cache (which is really robust and powerful btw)Read more for config…“shutdown_lifetime 3” added for quicker restart of squid service, It’s not really important.Basic caching forward proxy:http_port 3128cache_dir ufs /var/spool/squid3 100 16 256acl MYNET src 192.168.200.0/24http_access allow MYNETshutdown_lifetime 3 Transparent caching forward proxy:http_port 3128 transparentcache_dir ufs /var/spool/squid3 100 16 256acl MYNET src 192.168.200.0/24http_access allow MYNETshutdown_lifetime 3*You will also need to forward port to squid!* iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3128Note that using forward proxy compared to transparent proxying has better performance and is a better solution in general for reasons which are outside the scope of this How-To. So try to use it if you can and then use a proxy config script or Active Directory (if in a domain environment) to make users’ browsers use your proxy. Caching forward proxy with basic file authentication:http_port 3128cache_dir ufs /var/spool/squid3 100 16 256auth_param basic program /usr/lib/squid3/basic_ncsa_auth/etc/squid3/passwordsauth_param basic realm proxyacl authenticated proxy_auth REQUIREDhttp_access allow authenticatedAuthentication is not available with transparent proxy (obviously…duh) Caching forward proxy with LDAP authentication:http_port 3128cache_dir ufs /var/spool/squid3 100 16 256auth_param basic program /usr/lib/squid3/basic_ldap_auth -v 3 -b"dc=rio,dc=local" -f uid=%s 192.168.100.10auth_param basic realm proxyacl authenticated proxy_auth REQUIREDhttp_access allow authenticatedThis LDAP config can’t be used with Active Directory. That’s because unlike openldap (default config), AD DS doesn’t allow a user to bind to its ldap database without presenting a valid user (Binding DN). If you want to use this config with AD DS or a securely and properly configured OpenLDAP, you should specify the binding DN in the auth_param line using “-D” for more info visit Official Squid Guide on ActiveDirectory (which has way more than necessary info and may be a little confusing, just use the syntax from the last part of the guide, I hope it works!) Caching forward proxy with PAM authentication:http_port 3128cache_dir ufs /var/spool/squid3 100 16 256auth_param basic program /usr/lib/squid3/basic_pam_authauth_param basic realm proxyacl authenticated proxy_auth REQUIREDhttp_access allow authenticatedshutdown_lifetime 3Basically for authentication with local linux users… Caching forward proxy with RADIUS authentication:http_port 3128cache_dir ufs /var/spool/squid3 100 16 256auth_param basic program /usr/lib/squid3/basic_radius_auth -f/etc/squid3/radius-credauth_param basic realm proxyacl authenticated proxy_auth REQUIREDhttp_access allow authenticatedCaching forward proxy with domain filtering and PAM authentication:http_port 3128acl toblock dstdomain .block.rio.localcache_dir ufs /var/spool/squid3 100 16 256auth_param basic program /usr/lib/squid3/basic_pam_authauth_param basic realm proxyacl authenticated proxy_auth REQUIREDhttp_access deny toblockhttp_access allow authenticatedshutdown_lifetime 3You can use all types of access-list in Squid. Which allows for really flexible and powerful access control for your users. Squid3 not only has lots of features but also very good documentation. The main config file is around 6000 lines which only like 15 are not comments! LOL. so you get the idea… Further reading: WCCP, Delay PoolsThanks for reading this post. Any advice? please tell me in the comments!" }, { "title": "A New Anti Ransomware Idea", "url": "/topics/a-new-anti-ransomware-idea/", "categories": "ransomware, software", "tags": "anti-ransomware, anti-ransomware-with-hook, hook-for-ransomware, ransomware", "date": "2017-03-20 00:00:00 +0000", "snippet": "In the last few days, I was asked to give a new idea for creating an anti ransomware and now I wanna share my idea and source codes.The Full Source Code Is Available at : https://github.com/SinaKarvandi/Redemption-Anti-Ransomware/IntroductionIn the raise of computers in this century and as they largely used in transferring and storing sensitive data, Ransomware is a big danger which can compromise everything in a blink of an eye and causes huge loss of data or money,according to Wikipedia:Ransomware is computer malware that installs covertly on a victim’s device (e.g., computer, smartphone, wearable device) and that either mounts the cryptoviral extortion attack from cryptovirology that holds the victim’s data hostage, or mounts a cryptovirology leakware attack that threatens to publish the victim’s data, until a ransom is paid. Simple ransomware may lock the system in a way which is not difficult for a knowledgeable person to reverse, and display a message requesting payment to unlock it. More advanced malware encrypts the victim’s files, making them inaccessible, and demands a ransom payment to decrypt them. The ransomware may also encrypt the computer’s Master File Table (MFT) or the entire hard drive. Thus, ransomware is a denial-of-access attack that prevents computer users from accessing files since it is intractable to decrypt the files without the decryption key. Ransomware attacks are typically carried out using a Trojan that has a payload disguised as a legitimate file.This kind of malware is dependent on Cryptography functions and I/O activities as a part of their natures and this dependency could give us a really good resource for control and audit to prevent such this activities.The bad thing is that there is no permanent method to avoid such a malware (and it seems never be) because of billions of I/O activities that take place asynchronously in a system but being innovative can give security researchers an opportunity to evacuate a vast amount of ransomware and make it really hard and sometimes nearly impossible for bad guys to reach to their target, but as mentioned before there are many sophisticated way that could circumvent and bypass this protections. In the following article , we will show you our method of controlling ransomware which is a combination of previously invented methods and new methods to improve the detection and dismissing ransomware.AssumptionWe build our new method based on this assumptions:1. Ransomware encrypts data to be decrypt in future.Even our implementation prevents Denial of Service Attack (e.g. ransomware which encrypts data and delete the key without any plan to recover and decrypt in future.) but the nature of ransomware is that it reads data and it encrypt then deleteS or changeS the original data to cause a Denial of Access.This assumption is the base idea to prevent ransomware in the case of our implementation, we don’t see any bad behavior in just reading the data. (as it itself could bring some bypasses for instance Signed Drivers which encrypt and modify data from base cannot be prevented in our method but it is somehow impossible for ransomware to introduce themselves as a Signed Driver in Amd64 structured systems even there are some bypasses for this prevention too !).2. We assume that the users give us the highest available privilege and it is clear that Windows definitely prevents us from access the higher privilege ransomware.3. Ransomware is not a Driver Process.If the user system infected by Driver Ransomware it can’t prevented by no one as it goes to zero rop of kernel and it can even bypass Windows PatchGuard.Fortunately there is no easy way to access this rop and there are many complicated mechanism to prevent this behavior and there is no major ransomware which reaches to zero rop of kernel by now.4. We assume that the only way to access files is ReadFile and WriteFile as described in the resource section.There are tons of other ways to write to files (e.g. Native API’s which must of them implemented in ntdll.dll likeNtOpenFile(…) and NtReadFile(…) which can also be used to access files.) but the audit is just on the two major above functions and you can understand that this method could be implemented to all other functions if you have a complete list of file accessing functions but it wants time to test and improve to make a reliable anti ransomware.5. Ransomware are only interested in special kinds of file.We make more than 450 formats of extensions (that could be developed by users easily) which ransomware are more interested to encrypt, the experience shows that ransomware only encrypts special kinds of extensions because if they encrypt computer completely, it could cause an early Denial of Service which could apprise user before the encryption cycle complete and prevent ransomware from any further encryption which causes it’s mission to be unaccomplished.The extensions that we audit by default is listed below.Audit Extension.yuv, .ycbcra, .xis, .x3f, .x11, .wpd, .tex, .sxg, .stx, .st8, .st5, .srw, .srf, .sr2, .sqlitedb, .sqlite3, .sqlite, .sdf, .sda, .sd0, .s3db, .rwz, .rwl, .rdb, .rat, .raf, .qby, .qbx, .qbw, .qbr, .qba, .py, .psafe3, .plc, .plus_muhd, .pdd, .p7c, .p7b, .oth, .orf, .odm, .odf, .nyf, .nxl, .nx2, .nwb, .ns4, .ns3, .ns2, .nrw, .nop, .nk2, .nef, .ndd, .myd, .mrw, .moneywell, .mny, .mmw, .mfw, .mef, .mdc, .lua, .kpdx, .kdc, .kdbx, .kc2, .jpe, .incpas, .iiq, .ibz, .ibank, .hbk, .gry, .grey, .gray, .fhd, .fh, .ffd, .exf, .erf, .erbsql, .eml, .dxg, .drf, .dng, .dgc, .des, .der, .ddrw, .ddoc, .dcs, .dc2, .db_journal, .csl, .csh, .crw, .craw, .cib, .ce2, .ce1, .cdrw, .cdr6, .cdr5, .cdr4, .cdr3, .bpw, .bgt, .bdb, .bay, .bank, .backupdb, .backup, .back, .awg, .apj, .ait, .agdl, .ads, .adb, .acr, .ach, .accdt, .accdr, .accde, .ab4, .3pr, .3fr, .vmxf, .vmsd, .vhdx, .vhd, .vbox, .stm, .st7, .rvt, .qcow, .qed, .pif, .pdb, .pab, .ost, .ogg, .nvram, .ndf, .m4p, .m2ts, .log, .hpp, .hdd, .groups, .flvv, .edb, .dit, .dat, .cmt, .bin, .aiff, .xlk, .wad, .tlg, .st6, .st4, .say, .sas7bdat, .qbm, .qbb, .ptx, .pfx, .pef, .pat, .oil, .odc, .nsh, .nsg, .nsf, .nsd, .nd, .mos, .indd, .iif, .fpx, .fff, .fdb, .dtd, .design, .ddd, .dcr, .dac, .cr2, .cdx, .cdf, .blend, .bkp, .al, .adp, .act, .xlr, .xlam, .xla, .wps, .tga, .rw2, .r3d, .pspimage, .ps, .pct, .pcd, .m4v, .fxg, .flac, .eps, .dxb, .drw, .dot, .db3, .cpi, .cls, .cdr, .arw, .ai, .aac, .thm, .srt, .save, .safe, .rm, .pwm, .pages, .obj, .mlb, .md, .mbx, .lit, .laccdb, .kwm, .idx, .html, .flf, .dxf, .dwg, .dds, .csv, .css, .config, .cfg, .cer, .asx, .aspx, .aoi, .accdb, .7zip, .1cd, .xls, .wab, .rtf, .prf, .ppt, .oab, .msg, .mapimail, .jnt, .doc, .dbx, .contact, .n64, .m4a, .m4u, .m3u, .mid, .wma, .flv, .3g2, .mkv, .3gp, .mp4, .mov, .avi, .asf, .mpeg, .vob, .mpg, .wmv, .fla, .swf, .wav, .mp3, .qcow2, .vdi, .vmdk, .vmx, .wallet, .upk, .sav, .re4, .ltx, .litesql, .litemod, .lbf, .iwi, .forge, .das, .d3dbsp, .bsa, .bik, .asset, .apk, .gpg, .aes, .ARC, .PAQ, .tar.bz2, .tbk, .bak, .tar, .tgz, .gz, .7z, .rar, .zip, .djv, .djvu, .svg, .bmp, .png, .gif, .raw, .cgm, .jpeg, .jpg, .tif, .tiff, .NEF, .psd, .cmd, .bat, .sh, .class, .jar, .java, .rb, .asp, .cs, .brd, .sch, .dch, .dip, .pl, .vbs, .vb, .js, .asm, .pas, .cpp, .php, .ldf, .mdf, .ibd, .MYI, .MYD, .frm, .odb, .dbf, .db, .mdb, .sql, .SQLITEDB, .SQLITE3, .011, .010, .009, .008, .007, .006, .005, .004, .003, .002, .001, .pst, .onetoc2, .asc, .lay6, .lay, .ms11 (Security copy), .ms11, .sldm, .sldx, .ppsm, .ppsx, .ppam, .docb, .mml, .sxm, .otg, .odg, .uop, .potx, .potm, .pptx, .pptm, .std, .sxd, .pot, .pps, .sti, .sxi, .otp, .odp, .wb2, .123, .wks, .wk1, .xltx, .xltm, .xlsx, .xlsm, .xlsb, .slk, .xlw, .xlt, .xlm, .xlc, .dif, .stc, .sxc, .ots, .ods, .hwp, .602, .dotm, .dotx, .docm, .docx, .DOT, .3dm, .max, .3ds, .xml, .txt, .CSV, .uot, .RTF, .pdf, .XLS, .PPT, .stw, .sxw, .ott, .odt, .DOC, .pem, .p12, .csr, .crt, .keyPlease note that the users can add their own extensions on the above list.ImplementationIn a non-technical point of view, we create honeypots all over the computer and scan for interested files in directories to make a Critical Zone Table (CZT).The content of CZT should be safe from massive changes, if an application edited or deleted a content in CZT the Watchdog process immediately put that directory in Critical Mode (CM) and then searches for all the processes in Windows to find all of the available File Handles from the files which is in Critical Mode Folder (CMF), now if any process (which probably is the last one that access to directory) has any File Handle to files in CMF is attacked by Watchdog, even this a good way for prevent ransomware but it could bring us a vast amount of false positive result.So the Watchdog doesn’t suspend or Kill the process but inject itself into that process and replace all the CreateFile , OpenFile , DeleteFile and all other stuffs which could cause the loss of data in Hard Disk.So now the Watchdog is responsible for all changes in Hard Disk and Watchdog’s overrided functions is called before any changes and it do a Back up for all the deleted files and modified files that has over 50% of changes in their body in a safe place and then performs the application’s modification to avoid any error in application cycle or any false positive in result.This backup files are saved for 10 days then deleted automatically to prevent extra charges in hard disk.If any ransomware encrypts the data in CZT then the data is available by Anti Ransomware and could be backed.This method gives a lot of interesting result because the ransomware still thinks that it successfully encrypted the data and send attacker a successful message but the encrypted files are backed up in real time and that attacker might think that ransomware’s mission is accomplished but the owner could have their raw file immediately without any concern.The second methodology is Honeypot.Honeypots are a good way for detecting ransomware cuz there is no way for general ransomware to understand which file is a truly document and which is in fact a honeypot. (This can be bypass when a ransomware has a special target and just attempt to encrypt that target and nothing more which in this scenario it could be prevented by first methodology.).The approach of Honeypot methodology is to Suspend and Kill then delete every process which wants to modify Honeypots.Even this method can be bypassed but it’s still a good and precise way for detecting ransomware, because there is no reason for any process to modify the files that doesn’t interfere with them and you can be more than one hundred percent sure that it wants to do something evil.Technical PreviewEven the best method for implementing this approach is to write a customized file system driver but the lack of signing causes us to bring another solution.We use FileSystemWatcher which is .Net Framework library to detect any modification in a CZT.When anything detected no one can give us any hint that what process was responsible for that change then we use NtSystemInfo which use Windows API NTQuerySystemInformation for searching and enumerating handles form all available Windows Processes then if we find anything, inject our customized function to that process but if not detected anything then continue listening for a while and if it continues without detection the CMF flag is disappeared and shows that no process wants to change any other file in CMF.Modification KindsIn this methodology we just enumerate delete and modify as a harmful alert for Watchdog.Delete is always an alert but in the case of modification we simply compare the result before and after the modification and backup from files which has a large amount of modified content, this achieved by using SimMetric Libraries by implementing Byte similarities by Jaro–Winkler distance and Smith-Waterman string similarity algorithms and the average of this two algorithms gives us a number which 50% or more modification rings an alert for backup but less than this number is ignored.BenefitsThe best benefit for this method is it can make all process continue their cycles without any error.False Positive result will cause users to disable their anti ransomware but the main idea behind this anti ransomware is to allow system work properly even it is infected with ransomware or with applications which are for good purpose but act like ransomware or have ransomware behavior which is common.ConclusionThe method described above is a good way to prevent the majority of ransomware attacks but even there are lots of circumstances but it could prevent all massive changes in hard drive which is main idea of ransomware and it could defuse all general and massive encrypt purpose ransomware and make a real-time backup and let process continue without any false positive result that can be a reason to Denial of Service by some anti ransomware.ResourcesReadFile: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365467(v=vs.85).aspx WriteFile: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365747(v=vs.85).aspx Import Address Table: https://en.wikipedia.org/wiki/Portable_Executable#Import_Table Similarity Test: https://asecuritysite.com/forensics/simstring Jaro-Winkler: https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance Waternan: https://en.wikipedia.org/wiki/Smith%E2%80%93Waterman_algorithm Some Pictures from the UI  (Please note that it is just an idea and it doesn’t implemented to be a final heal for ransomwares, if you want to use this idea you’d better to hook native windows api by a driver.)To download source code please visit : https://github.com/SinaKarvandi/Redemption-Anti-Ransomware/" }, { "title": "Kernel Mode Debugging by Windbg", "url": "/topics/kernel-mode-debugging-by-windbg/", "categories": "debugging, kernel-mode", "tags": "debug-virtual-machine, debug-windows, debugging-kernel-mode, kernel-mode, vmware-and-windbg, vmware-debugging", "date": "2017-03-19 00:00:00 +0000", "snippet": "Hey there,Today I’m gonna show you how to make a kernel mode debugging using VMWare and Windbg and Windows.So why should you do this ?!It’s clear , everything such as Kernel Mode Driver Debugging , searching for zero days and understanding windows mechanism.There are other types of kernel debugging as described in Windows Internals by Mark Russinovich that I describe in future posts.So let’s start.First you need a Windbg and as I’m working in a x64 version of Windows so I use AMD64 version of Windbg. If you don’t know how to download and install windbg just google it :)let’s get down to business. First Start VMWare and open the OS that you want to debug.(Please note : in kernel mode debugging all U need is host machine that debugs a target machine)Then go to edit virtual machine and click to add new hardware.In this time click next and in the new window choose the “Output to named pipe” and click next.In the new window choose a name for your new serial port.This name will be used in Windbg.Now start the virtual machine and after starting you should make windows into debugging mode.There are several ways to do this and have same affects , In this case I started Run (Win Key + R) and go to msconfig.In the msconfig choose Boot from tabs and then click on Advanced Option and check Debug checkbox and Debug port and set debug port to your serial port number (In this case COM2).Note : It might be different in your computer so please check VMWare hardwares before set it.After applying the above settings, Windows asks if you want to restart computer for your actions to take place.In this time leave Windows alone and Start Windbg based on your operating system CPU.In Windbg go to File &gt; Kernel Debug…Now go to COM tab and set your serial port name (From what you choose in VMWare.)then click OK and restart the VMWare.After restarting Windows you should see something like this :Then your’re done …Now you can stop Windbg and see how it works like a charm.Please remember if you stop Windbg and make breakpoint, the target windows will stop.After doing this you can debug your Windows in Kernel Mode." }, { "title": "Hello World !", "url": "/topics/hello-world/", "categories": "social", "tags": "begining, helloworld, start", "date": "2017-03-19 00:00:00 +0000", "snippet": "This is the first post of our blog !After 5+ years of experience working with various technologies and developing skills in different computer fields including security and network, we want to share our little knowledge of this huge science to all people who love computers and computer security.As you know this year is a leap year and today is the last day of the year and tomorrow is the new year. (In Persian Calendar ;) ).so that’s like the anniversary of this blog will be every four years but I think it’s so cute &lt;3I’m MohammadSina Karvandi, a 19 years old boy who loves to learn and teach, I’m not only interested in Computer Security and Low Level Programming but also love high level programming as well.The other writer here would be one of my best friends, Shahriar Eftekhari who is into networks and network security.We will try to create innovative and new content about computer security, software and network and all the other things we find interesting!I hope that everyone who dedicates his/her time reading our blog would be glad in the end, and I should say sorry in advance if we make any mistakes writing English because It’s not our first language but believe me you can bear it :PSo we’ll be glad for every comment that tells us our mistakes in writing or technical information and we’ll give you our word to update this blog every time we can.Enjoy :)" } ] diff --git a/assets/js/data/swcache.js b/assets/js/data/swcache.js new file mode 100644 index 00000000..82a9318a --- /dev/null +++ b/assets/js/data/swcache.js @@ -0,0 +1 @@ +const resource = [ /* --- CSS --- */ '/assets/css/style.css', /* --- PWA --- */ '/app.js', '/sw.js', /* --- HTML --- */ '/index.html', '/404.html', '/tutorials/', '/tools/', '/categories/', '/tags/', '/archives/', '/contact/', /* --- Favicons & compressed JS --- */ '/assets/img/favicons/android-chrome-192x192.png', '/assets/img/favicons/android-chrome-512x512.png', '/assets/img/favicons/apple-touch-icon.png', '/assets/img/favicons/favicon-16x16.png', '/assets/img/favicons/favicon-32x32.png', '/assets/img/favicons/favicon.ico', '/assets/img/favicons/mstile-150x150.png', '/assets/img/favicons/safari-pinned-tab.svg', '/assets/js/dist/categories.min.js', '/assets/js/dist/commons.min.js', '/assets/js/dist/home.min.js', '/assets/js/dist/misc.min.js', '/assets/js/dist/page.min.js', '/assets/js/dist/post.min.js', '/assets/js/dist/pvreport.min.js' ]; /* The request url with below domain will be cached */ const allowedDomains = [ 'www.googletagmanager.com', 'www.google-analytics.com', 'rayanfam.com', 'fonts.gstatic.com', 'fonts.googleapis.com', 'cdn.jsdelivr.net', 'polyfill.io' ]; /* Requests that include the following path will be banned */ const denyUrls = [ ]; diff --git a/assets/js/dist/categories.min.js b/assets/js/dist/categories.min.js new file mode 100644 index 00000000..c369f689 --- /dev/null +++ b/assets/js/dist/categories.min.js @@ -0,0 +1,6 @@ +/*! + * Chirpy v5.1.0 (https://github.com/cotes2020/jekyll-theme-chirpy/) + * © 2019 Cotes Chung + * MIT Licensed + */ +$(function(){$(window).scroll(()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").click(()=>($("body,html").animate({scrollTop:0},800),!1))});const LocaleHelper=function(){const e=$('meta[name="prefer-datetime-locale"]'),o=0o,attrTimestamp:()=>t,attrDateFormat:()=>a,getTimestamp:e=>Number(e.attr(t)),getDateFormat:e=>e.attr(a)}}();$(function(){$(".mode-toggle").click(e=>{const o=$(e.target);let t=o.prop("tagName")==="button".toUpperCase()?o:o.parent();t.blur(),flipMode()})});const ScrollHelper=function(){const e=$("body"),o="data-topbar-visible",t=$("#topbar-wrapper").outerHeight();let a=0,r=!1,l=!1;return{hideTopbar:()=>e.attr(o,!1),showTopbar:()=>e.attr(o,!0),addScrollUpTask:()=>{a+=1,r=r||!0},popScrollUpTask:()=>--a,hasScrollUpTask:()=>0!0===r,unlockTopbar:()=>r=!1,getTopbarHeight:()=>t,orientationLocked:()=>!0===l,lockOrientation:()=>l=!0,unLockOrientation:()=>l=!1}}();$(function(){const e=$("#sidebar-trigger"),o=$("#search-trigger"),t=$("#search-cancel"),a=$("#main"),r=$("#topbar-title"),l=$("#search-wrapper"),n=$("#search-result-wrapper"),s=$("#search-results"),i=$("#search-input"),c=$("#search-hints"),d=function(){let e=0;return{block(){e=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(e)},getOffset(){return e}}}(),p={on(){e.addClass("unloaded"),r.addClass("unloaded"),o.addClass("unloaded"),l.addClass("d-flex"),t.addClass("loaded")},off(){t.removeClass("loaded"),l.removeClass("d-flex"),e.removeClass("unloaded"),r.removeClass("unloaded"),o.removeClass("unloaded")}},u=function(){let e=!1;return{on(){e||(d.block(),n.removeClass("unloaded"),a.addClass("unloaded"),e=!0)},off(){e&&(s.empty(),c.hasClass("unloaded")&&c.removeClass("unloaded"),n.addClass("unloaded"),a.removeClass("unloaded"),d.release(),i.val(""),e=!1)},isVisible(){return e}}}();function f(){return t.hasClass("loaded")}o.click(function(){p.on(),u.on(),i.focus()}),t.click(function(){p.off(),u.off()}),i.focus(function(){l.addClass("input-focus")}),i.focusout(function(){l.removeClass("input-focus")}),i.on("input",()=>{""===i.val()?f()?c.removeClass("unloaded"):u.off():(u.on(),f()&&c.addClass("unloaded"))})}),$(function(){var e=function(){const e="sidebar-display";let o=!1;const t=$("body");return{toggle(){!1===o?t.attr(e,""):t.removeAttr(e),o=!o}}}();$("#sidebar-trigger").click(e.toggle),$("#mask").click(e.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const o=$("#search-input"),t=ScrollHelper.getTopbarHeight();let e,a=0;function r(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var e=screen.orientation.type;"landscape-primary"!==e&&"landscape-secondary"!==e||r()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&r()}),$(window).scroll(()=>{e=e||!0}),setInterval(()=>{e&&(function(){var e=$(this).scrollTop();if(!(Math.abs(a-e)<=t)){if(e>a)ScrollHelper.hideTopbar(),o.is(":focus")&&o.blur();else if(e+$(window).height()<$(document).height()){if(ScrollHelper.hasScrollUpTask())return;ScrollHelper.topbarLocked()?ScrollHelper.unlockTopbar():ScrollHelper.orientationLocked()?ScrollHelper.unLockOrientation():ScrollHelper.showTopbar()}a=e}}(),e=!1)},250)}),$(function(){var o="div.post>h1:first-of-type";const t=$(o),n=$("#topbar-title");if(0!==t.length&&!t.hasClass("dynamic-title")&&!n.is(":hidden")){const s=n.text().trim();let a=t.text().trim(),r=!1,l=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(a)&&(a=a.replace(/[0-9]/g,"").trim()),t.offset().top<$(window).scrollTop()&&n.text(a);let e=new IntersectionObserver(e=>{var o,t;r?(o=$(window).scrollTop(),t=l{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").click(()=>($("body,html").animate({scrollTop:0},800),!1))});const LocaleHelper=function(){const e=$('meta[name="prefer-datetime-locale"]'),o=0o,attrTimestamp:()=>t,attrDateFormat:()=>a,getTimestamp:e=>Number(e.attr(t)),getDateFormat:e=>e.attr(a)}}();$(function(){$(".mode-toggle").click(e=>{const o=$(e.target);let t=o.prop("tagName")==="button".toUpperCase()?o:o.parent();t.blur(),flipMode()})});const ScrollHelper=function(){const e=$("body"),o="data-topbar-visible",t=$("#topbar-wrapper").outerHeight();let a=0,r=!1,l=!1;return{hideTopbar:()=>e.attr(o,!1),showTopbar:()=>e.attr(o,!0),addScrollUpTask:()=>{a+=1,r=r||!0},popScrollUpTask:()=>--a,hasScrollUpTask:()=>0!0===r,unlockTopbar:()=>r=!1,getTopbarHeight:()=>t,orientationLocked:()=>!0===l,lockOrientation:()=>l=!0,unLockOrientation:()=>l=!1}}();$(function(){const e=$("#sidebar-trigger"),o=$("#search-trigger"),t=$("#search-cancel"),a=$("#main"),r=$("#topbar-title"),l=$("#search-wrapper"),n=$("#search-result-wrapper"),s=$("#search-results"),c=$("#search-input"),i=$("#search-hints"),d=function(){let e=0;return{block(){e=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(e)},getOffset(){return e}}}(),p={on(){e.addClass("unloaded"),r.addClass("unloaded"),o.addClass("unloaded"),l.addClass("d-flex"),t.addClass("loaded")},off(){t.removeClass("loaded"),l.removeClass("d-flex"),e.removeClass("unloaded"),r.removeClass("unloaded"),o.removeClass("unloaded")}},u=function(){let e=!1;return{on(){e||(d.block(),n.removeClass("unloaded"),a.addClass("unloaded"),e=!0)},off(){e&&(s.empty(),i.hasClass("unloaded")&&i.removeClass("unloaded"),n.addClass("unloaded"),a.removeClass("unloaded"),d.release(),c.val(""),e=!1)},isVisible(){return e}}}();function f(){return t.hasClass("loaded")}o.click(function(){p.on(),u.on(),c.focus()}),t.click(function(){p.off(),u.off()}),c.focus(function(){l.addClass("input-focus")}),c.focusout(function(){l.removeClass("input-focus")}),c.on("input",()=>{""===c.val()?f()?i.removeClass("unloaded"):u.off():(u.on(),f()&&i.addClass("unloaded"))})}),$(function(){var e=function(){const e="sidebar-display";let o=!1;const t=$("body");return{toggle(){!1===o?t.attr(e,""):t.removeAttr(e),o=!o}}}();$("#sidebar-trigger").click(e.toggle),$("#mask").click(e.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const o=$("#search-input"),t=ScrollHelper.getTopbarHeight();let e,a=0;function r(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var e=screen.orientation.type;"landscape-primary"!==e&&"landscape-secondary"!==e||r()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&r()}),$(window).scroll(()=>{e=e||!0}),setInterval(()=>{e&&(function(){var e=$(this).scrollTop();if(!(Math.abs(a-e)<=t)){if(e>a)ScrollHelper.hideTopbar(),o.is(":focus")&&o.blur();else if(e+$(window).height()<$(document).height()){if(ScrollHelper.hasScrollUpTask())return;ScrollHelper.topbarLocked()?ScrollHelper.unlockTopbar():ScrollHelper.orientationLocked()?ScrollHelper.unLockOrientation():ScrollHelper.showTopbar()}a=e}}(),e=!1)},250)}),$(function(){var o="div.post>h1:first-of-type";const t=$(o),n=$("#topbar-title");if(0!==t.length&&!t.hasClass("dynamic-title")&&!n.is(":hidden")){const s=n.text().trim();let a=t.text().trim(),r=!1,l=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(a)&&(a=a.replace(/[0-9]/g,"").trim()),t.offset().top<$(window).scrollTop()&&n.text(a);let e=new IntersectionObserver(e=>{var o,t;r?(o=$(window).scrollTop(),t=l{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").click(()=>($("body,html").animate({scrollTop:0},800),!1))});const LocaleHelper=function(){const t=$('meta[name="prefer-datetime-locale"]'),e=0e,attrTimestamp:()=>o,attrDateFormat:()=>a,getTimestamp:t=>Number(t.attr(o)),getDateFormat:t=>t.attr(a)}}();$(function(){$(".mode-toggle").click(t=>{const e=$(t.target);let o=e.prop("tagName")==="button".toUpperCase()?e:e.parent();o.blur(),flipMode()})});const ScrollHelper=function(){const t=$("body"),e="data-topbar-visible",o=$("#topbar-wrapper").outerHeight();let a=0,r=!1,l=!1;return{hideTopbar:()=>t.attr(e,!1),showTopbar:()=>t.attr(e,!0),addScrollUpTask:()=>{a+=1,r=r||!0},popScrollUpTask:()=>--a,hasScrollUpTask:()=>0!0===r,unlockTopbar:()=>r=!1,getTopbarHeight:()=>o,orientationLocked:()=>!0===l,lockOrientation:()=>l=!0,unLockOrientation:()=>l=!1}}();$(function(){const t=$("#sidebar-trigger"),e=$("#search-trigger"),o=$("#search-cancel"),a=$("#main"),r=$("#topbar-title"),l=$("#search-wrapper"),n=$("#search-result-wrapper"),i=$("#search-results"),s=$("#search-input"),c=$("#search-hints"),d=function(){let t=0;return{block(){t=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(t)},getOffset(){return t}}}(),p={on(){t.addClass("unloaded"),r.addClass("unloaded"),e.addClass("unloaded"),l.addClass("d-flex"),o.addClass("loaded")},off(){o.removeClass("loaded"),l.removeClass("d-flex"),t.removeClass("unloaded"),r.removeClass("unloaded"),e.removeClass("unloaded")}},u=function(){let t=!1;return{on(){t||(d.block(),n.removeClass("unloaded"),a.addClass("unloaded"),t=!0)},off(){t&&(i.empty(),c.hasClass("unloaded")&&c.removeClass("unloaded"),n.addClass("unloaded"),a.removeClass("unloaded"),d.release(),s.val(""),t=!1)},isVisible(){return t}}}();function f(){return o.hasClass("loaded")}e.click(function(){p.on(),u.on(),s.focus()}),o.click(function(){p.off(),u.off()}),s.focus(function(){l.addClass("input-focus")}),s.focusout(function(){l.removeClass("input-focus")}),s.on("input",()=>{""===s.val()?f()?c.removeClass("unloaded"):u.off():(u.on(),f()&&c.addClass("unloaded"))})}),$(function(){var t=function(){const t="sidebar-display";let e=!1;const o=$("body");return{toggle(){!1===e?o.attr(t,""):o.removeAttr(t),e=!e}}}();$("#sidebar-trigger").click(t.toggle),$("#mask").click(t.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const e=$("#search-input"),o=ScrollHelper.getTopbarHeight();let t,a=0;function r(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var t=screen.orientation.type;"landscape-primary"!==t&&"landscape-secondary"!==t||r()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&r()}),$(window).scroll(()=>{t=t||!0}),setInterval(()=>{t&&(function(){var t=$(this).scrollTop();if(!(Math.abs(a-t)<=o)){if(t>a)ScrollHelper.hideTopbar(),e.is(":focus")&&e.blur();else if(t+$(window).height()<$(document).height()){if(ScrollHelper.hasScrollUpTask())return;ScrollHelper.topbarLocked()?ScrollHelper.unlockTopbar():ScrollHelper.orientationLocked()?ScrollHelper.unLockOrientation():ScrollHelper.showTopbar()}a=t}}(),t=!1)},250)}),$(function(){var e="div.post>h1:first-of-type";const o=$(e),n=$("#topbar-title");if(0!==o.length&&!o.hasClass("dynamic-title")&&!n.is(":hidden")){const i=n.text().trim();let a=o.text().trim(),r=!1,l=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(a)&&(a=a.replace(/[0-9]/g,"").trim()),o.offset().top<$(window).scrollTop()&&n.text(a);let t=new IntersectionObserver(t=>{var e,o;r?(e=$(window).scrollTop(),o=lt.toUpperCase())),$(this).text()!==t&&$(this).text(t)}else--o}),0===o&&void 0!==e&&clearInterval(e),o}dayjs.locale(LocaleHelper.locale()),dayjs.extend(window.dayjs_plugin_relativeTime),dayjs.extend(window.dayjs_plugin_localizedFormat),0!==o&&(t.each(function(){var t,e=$(this).attr("data-toggle");void 0!==e&&"tooltip"===e&&(t=$(this).attr("data-tooltip-df"),e=LocaleHelper.getTimestamp($(this)),t=dayjs.unix(e).format(t),$(this).attr("data-original-title",t),$(this).removeAttr("data-tooltip-df"))}),r()&&(e=setInterval(r,6e4)))}); \ No newline at end of file diff --git a/assets/js/dist/misc.min.js b/assets/js/dist/misc.min.js new file mode 100644 index 00000000..c4cb36d1 --- /dev/null +++ b/assets/js/dist/misc.min.js @@ -0,0 +1,6 @@ +/*! + * Chirpy v5.1.0 (https://github.com/cotes2020/jekyll-theme-chirpy/) + * © 2019 Cotes Chung + * MIT Licensed + */ +$(function(){$(window).scroll(()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").click(()=>($("body,html").animate({scrollTop:0},800),!1))});const LocaleHelper=function(){const e=$('meta[name="prefer-datetime-locale"]'),t=0t,attrTimestamp:()=>o,attrDateFormat:()=>a,getTimestamp:e=>Number(e.attr(o)),getDateFormat:e=>e.attr(a)}}();$(function(){$(".mode-toggle").click(e=>{const t=$(e.target);let o=t.prop("tagName")==="button".toUpperCase()?t:t.parent();o.blur(),flipMode()})});const ScrollHelper=function(){const e=$("body"),t="data-topbar-visible",o=$("#topbar-wrapper").outerHeight();let a=0,r=!1,l=!1;return{hideTopbar:()=>e.attr(t,!1),showTopbar:()=>e.attr(t,!0),addScrollUpTask:()=>{a+=1,r=r||!0},popScrollUpTask:()=>--a,hasScrollUpTask:()=>0!0===r,unlockTopbar:()=>r=!1,getTopbarHeight:()=>o,orientationLocked:()=>!0===l,lockOrientation:()=>l=!0,unLockOrientation:()=>l=!1}}();$(function(){const e=$("#sidebar-trigger"),t=$("#search-trigger"),o=$("#search-cancel"),a=$("#main"),r=$("#topbar-title"),l=$("#search-wrapper"),n=$("#search-result-wrapper"),s=$("#search-results"),i=$("#search-input"),c=$("#search-hints"),d=function(){let e=0;return{block(){e=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(e)},getOffset(){return e}}}(),p={on(){e.addClass("unloaded"),r.addClass("unloaded"),t.addClass("unloaded"),l.addClass("d-flex"),o.addClass("loaded")},off(){o.removeClass("loaded"),l.removeClass("d-flex"),e.removeClass("unloaded"),r.removeClass("unloaded"),t.removeClass("unloaded")}},u=function(){let e=!1;return{on(){e||(d.block(),n.removeClass("unloaded"),a.addClass("unloaded"),e=!0)},off(){e&&(s.empty(),c.hasClass("unloaded")&&c.removeClass("unloaded"),n.addClass("unloaded"),a.removeClass("unloaded"),d.release(),i.val(""),e=!1)},isVisible(){return e}}}();function f(){return o.hasClass("loaded")}t.click(function(){p.on(),u.on(),i.focus()}),o.click(function(){p.off(),u.off()}),i.focus(function(){l.addClass("input-focus")}),i.focusout(function(){l.removeClass("input-focus")}),i.on("input",()=>{""===i.val()?f()?c.removeClass("unloaded"):u.off():(u.on(),f()&&c.addClass("unloaded"))})}),$(function(){var e=function(){const e="sidebar-display";let t=!1;const o=$("body");return{toggle(){!1===t?o.attr(e,""):o.removeAttr(e),t=!t}}}();$("#sidebar-trigger").click(e.toggle),$("#mask").click(e.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const t=$("#search-input"),o=ScrollHelper.getTopbarHeight();let e,a=0;function r(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var e=screen.orientation.type;"landscape-primary"!==e&&"landscape-secondary"!==e||r()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&r()}),$(window).scroll(()=>{e=e||!0}),setInterval(()=>{e&&(function(){var e=$(this).scrollTop();if(!(Math.abs(a-e)<=o)){if(e>a)ScrollHelper.hideTopbar(),t.is(":focus")&&t.blur();else if(e+$(window).height()<$(document).height()){if(ScrollHelper.hasScrollUpTask())return;ScrollHelper.topbarLocked()?ScrollHelper.unlockTopbar():ScrollHelper.orientationLocked()?ScrollHelper.unLockOrientation():ScrollHelper.showTopbar()}a=e}}(),e=!1)},250)}),$(function(){var t="div.post>h1:first-of-type";const o=$(t),n=$("#topbar-title");if(0!==o.length&&!o.hasClass("dynamic-title")&&!n.is(":hidden")){const s=n.text().trim();let a=o.text().trim(),r=!1,l=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(a)&&(a=a.replace(/[0-9]/g,"").trim()),o.offset().top<$(window).scrollTop()&&n.text(a);let e=new IntersectionObserver(e=>{var t,o;r?(t=$(window).scrollTop(),o=l{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").click(()=>($("body,html").animate({scrollTop:0},800),!1))});const LocaleHelper=function(){const t=$('meta[name="prefer-datetime-locale"]'),e=0e,attrTimestamp:()=>o,attrDateFormat:()=>a,getTimestamp:t=>Number(t.attr(o)),getDateFormat:t=>t.attr(a)}}();$(function(){$(".mode-toggle").click(t=>{const e=$(t.target);let o=e.prop("tagName")==="button".toUpperCase()?e:e.parent();o.blur(),flipMode()})});const ScrollHelper=function(){const t=$("body"),e="data-topbar-visible",o=$("#topbar-wrapper").outerHeight();let a=0,r=!1,l=!1;return{hideTopbar:()=>t.attr(e,!1),showTopbar:()=>t.attr(e,!0),addScrollUpTask:()=>{a+=1,r=r||!0},popScrollUpTask:()=>--a,hasScrollUpTask:()=>0!0===r,unlockTopbar:()=>r=!1,getTopbarHeight:()=>o,orientationLocked:()=>!0===l,lockOrientation:()=>l=!0,unLockOrientation:()=>l=!1}}();$(function(){const t=$("#sidebar-trigger"),e=$("#search-trigger"),o=$("#search-cancel"),a=$("#main"),r=$("#topbar-title"),l=$("#search-wrapper"),n=$("#search-result-wrapper"),i=$("#search-results"),c=$("#search-input"),s=$("#search-hints"),d=function(){let t=0;return{block(){t=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(t)},getOffset(){return t}}}(),p={on(){t.addClass("unloaded"),r.addClass("unloaded"),e.addClass("unloaded"),l.addClass("d-flex"),o.addClass("loaded")},off(){o.removeClass("loaded"),l.removeClass("d-flex"),t.removeClass("unloaded"),r.removeClass("unloaded"),e.removeClass("unloaded")}},u=function(){let t=!1;return{on(){t||(d.block(),n.removeClass("unloaded"),a.addClass("unloaded"),t=!0)},off(){t&&(i.empty(),s.hasClass("unloaded")&&s.removeClass("unloaded"),n.addClass("unloaded"),a.removeClass("unloaded"),d.release(),c.val(""),t=!1)},isVisible(){return t}}}();function h(){return o.hasClass("loaded")}e.click(function(){p.on(),u.on(),c.focus()}),o.click(function(){p.off(),u.off()}),c.focus(function(){l.addClass("input-focus")}),c.focusout(function(){l.removeClass("input-focus")}),c.on("input",()=>{""===c.val()?h()?s.removeClass("unloaded"):u.off():(u.on(),h()&&s.addClass("unloaded"))})}),$(function(){var t=function(){const t="sidebar-display";let e=!1;const o=$("body");return{toggle(){!1===e?o.attr(t,""):o.removeAttr(t),e=!e}}}();$("#sidebar-trigger").click(t.toggle),$("#mask").click(t.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const e=$("#search-input"),o=ScrollHelper.getTopbarHeight();let t,a=0;function r(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var t=screen.orientation.type;"landscape-primary"!==t&&"landscape-secondary"!==t||r()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&r()}),$(window).scroll(()=>{t=t||!0}),setInterval(()=>{t&&(function(){var t=$(this).scrollTop();if(!(Math.abs(a-t)<=o)){if(t>a)ScrollHelper.hideTopbar(),e.is(":focus")&&e.blur();else if(t+$(window).height()<$(document).height()){if(ScrollHelper.hasScrollUpTask())return;ScrollHelper.topbarLocked()?ScrollHelper.unlockTopbar():ScrollHelper.orientationLocked()?ScrollHelper.unLockOrientation():ScrollHelper.showTopbar()}a=t}}(),t=!1)},250)}),$(function(){var e="div.post>h1:first-of-type";const o=$(e),n=$("#topbar-title");if(0!==o.length&&!o.hasClass("dynamic-title")&&!n.is(":hidden")){const i=n.text().trim();let a=o.text().trim(),r=!1,l=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(a)&&(a=a.replace(/[0-9]/g,"").trim()),o.offset().top<$(window).scrollTop()&&n.text(a);let t=new IntersectionObserver(t=>{var e,o;r?(e=$(window).scrollTop(),o=l'),$("input[type=checkbox]:not([checked])").before('')}),$(function(){var t="#main > div.row:first-child > div:first-child";if(!($(t+" img").length<=0)){var e=document.querySelectorAll(t+" img[data-src]");const o=lozad(e);o.observe(),$(t+` p > img[data-src],${t} img[data-src].preview-img`).each(function(){let t=$(this).next();var e="EM"===t.prop("tagName")?t.text():"",o=$(this).attr("data-src");$(this).wrap(``)}),$(".popup").magnificPopup({type:"image",closeOnContentClick:!0,showCloseBtn:!1,zoom:{enabled:!0,duration:300,easing:"ease-in-out"}}),$(t+" a").has("img").addClass("img-link")}}),$(function(){var t=".code-header>button";const e="timeout",r="data-title-succeed",l="data-original-title";function n(t){if($(t)[0].hasAttribute(e)){t=$(t).attr(e);if(Number(t)>Date.now())return 1}}function i(t){$(t).attr(e,Date.now()+2e3)}function c(t){$(t).removeAttr(e)}const o=new ClipboardJS(t,{target(t){let e=t.parentNode.nextElementSibling;return e.querySelector("code .rouge-code")}});$(t).tooltip({trigger:"hover",placement:"left"});const a=function(t){let e=$(t).children();return e.attr("class")}(t);o.on("success",t=>{t.clearSelection();const e=t.trigger;var o;n(e)||(function(t){let e=$(t),o=e.children();o.attr("class","fas fa-check")}(e),o=e,t=$(o).attr(r),$(o).attr(l,t).tooltip("show"),i(e),setTimeout(()=>{var t;t=e,$(t).tooltip("hide").removeAttr(l),function(t){let e=$(t),o=e.children();o.attr("class",a)}(e),c(e)},2e3))}),$("#copy-link").click(t=>{let e=$(t.target);if(!n(e)){t=window.location.href;const o=$("");$("body").append(o),o.val(t).select(),document.execCommand("copy"),o.remove();const a=e.attr(l);t=e.attr(r);e.attr(l,t).tooltip("show"),i(e),setTimeout(()=>{e.attr(l,a),c(e)},2e3)}})}),$(function(){const t=$("#topbar-title"),c="scroll-focus";$("a[href*='#']").not("[href='#']").not("[href='#0']").click(function(r){if(this.pathname.replace(/^\//,"")===location.pathname.replace(/^\//,"")&&location.hostname===this.hostname){const i=decodeURI(this.hash);let e=RegExp(/^#fnref:/).test(i),o=!e&&RegExp(/^#fn:/).test(i);var l=i.includes(":")?i.replace(/\:/g,"\\:"):i;let a=$(l);var n=t.is(":visible"),l=$(window).width()<$(window).height();if(void 0!==a){r.preventDefault(),history.pushState&&history.pushState(null,null,i);r=$(window).scrollTop();let t=a.offset().top-=8;t(a.focus(),$(`[${c}=true]`).length&&$(`[${c}=true]`).attr(c,!1),$(":target").length&&$(":target").attr(c,!1),(o||e)&&a.attr(c,!0),a.is(":focus")?!1:(a.attr("tabindex","-1"),a.focus(),void(ScrollHelper.hasScrollUpTask()&&ScrollHelper.popScrollUpTask()))))}}})}); \ No newline at end of file diff --git a/assets/js/dist/post.min.js b/assets/js/dist/post.min.js new file mode 100644 index 00000000..97f33a15 --- /dev/null +++ b/assets/js/dist/post.min.js @@ -0,0 +1,6 @@ +/*! + * Chirpy v5.1.0 (https://github.com/cotes2020/jekyll-theme-chirpy/) + * © 2019 Cotes Chung + * MIT Licensed + */ +$(function(){$(window).scroll(()=>{50<$(this).scrollTop()&&"none"===$("#sidebar-trigger").css("display")?$("#back-to-top").fadeIn():$("#back-to-top").fadeOut()}),$("#back-to-top").click(()=>($("body,html").animate({scrollTop:0},800),!1))});const LocaleHelper=function(){const t=$('meta[name="prefer-datetime-locale"]'),e=0e,attrTimestamp:()=>o,attrDateFormat:()=>a,getTimestamp:t=>Number(t.attr(o)),getDateFormat:t=>t.attr(a)}}();$(function(){$(".mode-toggle").click(t=>{const e=$(t.target);let o=e.prop("tagName")==="button".toUpperCase()?e:e.parent();o.blur(),flipMode()})});const ScrollHelper=function(){const t=$("body"),e="data-topbar-visible",o=$("#topbar-wrapper").outerHeight();let a=0,r=!1,l=!1;return{hideTopbar:()=>t.attr(e,!1),showTopbar:()=>t.attr(e,!0),addScrollUpTask:()=>{a+=1,r=r||!0},popScrollUpTask:()=>--a,hasScrollUpTask:()=>0!0===r,unlockTopbar:()=>r=!1,getTopbarHeight:()=>o,orientationLocked:()=>!0===l,lockOrientation:()=>l=!0,unLockOrientation:()=>l=!1}}();$(function(){const t=$("#sidebar-trigger"),e=$("#search-trigger"),o=$("#search-cancel"),a=$("#main"),r=$("#topbar-title"),l=$("#search-wrapper"),n=$("#search-result-wrapper"),i=$("#search-results"),c=$("#search-input"),s=$("#search-hints"),d=function(){let t=0;return{block(){t=window.scrollY,$("html,body").scrollTop(0)},release(){$("html,body").scrollTop(t)},getOffset(){return t}}}(),p={on(){t.addClass("unloaded"),r.addClass("unloaded"),e.addClass("unloaded"),l.addClass("d-flex"),o.addClass("loaded")},off(){o.removeClass("loaded"),l.removeClass("d-flex"),t.removeClass("unloaded"),r.removeClass("unloaded"),e.removeClass("unloaded")}},u=function(){let t=!1;return{on(){t||(d.block(),n.removeClass("unloaded"),a.addClass("unloaded"),t=!0)},off(){t&&(i.empty(),s.hasClass("unloaded")&&s.removeClass("unloaded"),n.addClass("unloaded"),a.removeClass("unloaded"),d.release(),c.val(""),t=!1)},isVisible(){return t}}}();function f(){return o.hasClass("loaded")}e.click(function(){p.on(),u.on(),c.focus()}),o.click(function(){p.off(),u.off()}),c.focus(function(){l.addClass("input-focus")}),c.focusout(function(){l.removeClass("input-focus")}),c.on("input",()=>{""===c.val()?f()?s.removeClass("unloaded"):u.off():(u.on(),f()&&s.addClass("unloaded"))})}),$(function(){var t=function(){const t="sidebar-display";let e=!1;const o=$("body");return{toggle(){!1===e?o.attr(t,""):o.removeAttr(t),e=!e}}}();$("#sidebar-trigger").click(t.toggle),$("#mask").click(t.toggle)}),$(function(){$('[data-toggle="tooltip"]').tooltip()}),$(function(){const e=$("#search-input"),o=ScrollHelper.getTopbarHeight();let t,a=0;function r(){0!==$(window).scrollTop()&&(ScrollHelper.lockOrientation(),ScrollHelper.hideTopbar())}screen.orientation?screen.orientation.onchange=()=>{var t=screen.orientation.type;"landscape-primary"!==t&&"landscape-secondary"!==t||r()}:$(window).on("orientationchange",()=>{$(window).width()<$(window).height()&&r()}),$(window).scroll(()=>{t=t||!0}),setInterval(()=>{t&&(function(){var t=$(this).scrollTop();if(!(Math.abs(a-t)<=o)){if(t>a)ScrollHelper.hideTopbar(),e.is(":focus")&&e.blur();else if(t+$(window).height()<$(document).height()){if(ScrollHelper.hasScrollUpTask())return;ScrollHelper.topbarLocked()?ScrollHelper.unlockTopbar():ScrollHelper.orientationLocked()?ScrollHelper.unLockOrientation():ScrollHelper.showTopbar()}a=t}}(),t=!1)},250)}),$(function(){var e="div.post>h1:first-of-type";const o=$(e),n=$("#topbar-title");if(0!==o.length&&!o.hasClass("dynamic-title")&&!n.is(":hidden")){const i=n.text().trim();let a=o.text().trim(),r=!1,l=0;($("#page-category").length||$("#page-tag").length)&&/\s/.test(a)&&(a=a.replace(/[0-9]/g,"").trim()),o.offset().top<$(window).scrollTop()&&n.text(a);let t=new IntersectionObserver(t=>{var e,o;r?(e=$(window).scrollTop(),o=l img[data-src],${t} img[data-src].preview-img`).each(function(){let t=$(this).next();var e="EM"===t.prop("tagName")?t.text():"",o=$(this).attr("data-src");$(this).wrap(``)}),$(".popup").magnificPopup({type:"image",closeOnContentClick:!0,showCloseBtn:!1,zoom:{enabled:!0,duration:300,easing:"ease-in-out"}}),$(t+" a").has("img").addClass("img-link")}}),$(function(){const a=LocaleHelper.attrTimestamp(),t=$(".timeago");let o=t.length,e=void 0;function r(){return t.each(function(){if(void 0!==$(this).attr(a)){let t=function(t){const e=dayjs(),o=dayjs.unix(LocaleHelper.getTimestamp(t));return 10t.toUpperCase())),$(this).text()!==t&&$(this).text(t)}else--o}),0===o&&void 0!==e&&clearInterval(e),o}dayjs.locale(LocaleHelper.locale()),dayjs.extend(window.dayjs_plugin_relativeTime),dayjs.extend(window.dayjs_plugin_localizedFormat),0!==o&&(t.each(function(){var t,e=$(this).attr("data-toggle");void 0!==e&&"tooltip"===e&&(t=$(this).attr("data-tooltip-df"),e=LocaleHelper.getTimestamp($(this)),t=dayjs.unix(e).format(t),$(this).attr("data-original-title",t),$(this).removeAttr("data-tooltip-df"))}),r()&&(e=setInterval(r,6e4)))}),$(function(){$("input[type=checkbox]").addClass("unloaded"),$("input[type=checkbox][checked]").before(''),$("input[type=checkbox]:not([checked])").before('')}),$(function(){var t=".code-header>button";const e="timeout",r="data-title-succeed",l="data-original-title";function n(t){if($(t)[0].hasAttribute(e)){t=$(t).attr(e);if(Number(t)>Date.now())return 1}}function i(t){$(t).attr(e,Date.now()+2e3)}function c(t){$(t).removeAttr(e)}const o=new ClipboardJS(t,{target(t){let e=t.parentNode.nextElementSibling;return e.querySelector("code .rouge-code")}});$(t).tooltip({trigger:"hover",placement:"left"});const a=function(t){let e=$(t).children();return e.attr("class")}(t);o.on("success",t=>{t.clearSelection();const e=t.trigger;var o;n(e)||(function(t){let e=$(t),o=e.children();o.attr("class","fas fa-check")}(e),o=e,t=$(o).attr(r),$(o).attr(l,t).tooltip("show"),i(e),setTimeout(()=>{var t;t=e,$(t).tooltip("hide").removeAttr(l),function(t){let e=$(t),o=e.children();o.attr("class",a)}(e),c(e)},2e3))}),$("#copy-link").click(t=>{let e=$(t.target);if(!n(e)){t=window.location.href;const o=$("");$("body").append(o),o.val(t).select(),document.execCommand("copy"),o.remove();const a=e.attr(l);t=e.attr(r);e.attr(l,t).tooltip("show"),i(e),setTimeout(()=>{e.attr(l,a),c(e)},2e3)}})}),$(function(){const t=$("#topbar-title"),c="scroll-focus";$("a[href*='#']").not("[href='#']").not("[href='#0']").click(function(r){if(this.pathname.replace(/^\//,"")===location.pathname.replace(/^\//,"")&&location.hostname===this.hostname){const i=decodeURI(this.hash);let e=RegExp(/^#fnref:/).test(i),o=!e&&RegExp(/^#fn:/).test(i);var l=i.includes(":")?i.replace(/\:/g,"\\:"):i;let a=$(l);var n=t.is(":visible"),l=$(window).width()<$(window).height();if(void 0!==a){r.preventDefault(),history.pushState&&history.pushState(null,null,i);r=$(window).scrollTop();let t=a.offset().top-=8;t(a.focus(),$(`[${c}=true]`).length&&$(`[${c}=true]`).attr(c,!1),$(":target").length&&$(":target").attr(c,!1),(o||e)&&a.attr(c,!0),a.is(":focus")?!1:(a.attr("tabindex","-1"),a.focus(),void(ScrollHelper.hasScrollUpTask()&&ScrollHelper.popScrollUpTask()))))}}})}); \ No newline at end of file diff --git a/assets/js/dist/pvreport.min.js b/assets/js/dist/pvreport.min.js new file mode 100644 index 00000000..2a8aca11 --- /dev/null +++ b/assets/js/dist/pvreport.min.js @@ -0,0 +1,6 @@ +/*! + * Chirpy v5.1.0 (https://github.com/cotes2020/jekyll-theme-chirpy/) + * © 2019 Cotes Chung + * MIT Licensed + */ +const getInitStatus=function(){let t=!1;return()=>{var e=t;return t=t||!0,e}}(),PvOpts=function(){function t(e){return $(e).attr("content")}function e(e){e=t(e);return void 0!==e&&!1!==e}return{getProxyMeta(){return t("meta[name=pv-proxy-endpoint]")},getLocalMeta(){return t("meta[name=pv-cache-path]")},hasProxyMeta(){return e("meta[name=pv-proxy-endpoint]")},hasLocalMeta(){return e("meta[name=pv-cache-path]")}}}(),PvStorage=function(){const a={KEY_PV:"pv",KEY_PV_SRC:"pv_src",KEY_CREATION:"pv_created_date"},t={LOCAL:"same-origin",PROXY:"cors"};function r(e){return localStorage.getItem(e)}function o(e,t){localStorage.setItem(e,t)}function n(e,t){o(a.KEY_PV,e),o(a.KEY_PV_SRC,t),o(a.KEY_CREATION,(new Date).toJSON())}return{keysCount(){return Object.keys(a).length},hasCache(){return null!==localStorage.getItem(a.KEY_PV)},getCache(){return JSON.parse(localStorage.getItem(a.KEY_PV))},saveLocalCache(e){n(e,t.LOCAL)},saveProxyCache(e){n(e,t.PROXY)},isExpired(){let e=new Date(r(a.KEY_CREATION));return e.setHours(e.getHours()+1),Date.now()>=e.getTime()},isFromLocal(){return r(a.KEY_PV_SRC)===t.LOCAL},isFromProxy(){return r(a.KEY_PV_SRC)===t.PROXY},newerThan(e){return PvStorage.getCache().totalsForAllResults["ga:pageviews"]>e.totalsForAllResults["ga:pageviews"]},inspectKeys(){if(localStorage.length===PvStorage.keysCount())for(let e=0;er&&countUp(r,o,a.attr("id"))):a.text((new Intl.NumberFormat).format(o))}function displayPageviews(e){if(void 0!==e){let t=getInitStatus();const a=e.rows;0<$("#post-list").length?$(".post-preview").each(function(){var e=$(this).find("a").attr("href");tacklePV(a,e,$(this).find(".pageviews"),t)}):0<$(".post").length&&(e=window.location.pathname,tacklePV(a,e,$("#pv"),t))}}function fetchProxyPageviews(){PvOpts.hasProxyMeta()&&$.ajax({type:"GET",url:PvOpts.getProxyMeta(),dataType:"jsonp",jsonpCallback:"displayPageviews",success:e=>{PvStorage.saveProxyCache(JSON.stringify(e))},error:(e,t,a)=>{console.log("Failed to load pageviews from proxy server: "+a)}})}function fetchLocalPageviews(t=!1){return fetch(PvOpts.getLocalMeta()).then(e=>e.json()).then(e=>{t&&PvStorage.isFromProxy()&&PvStorage.newerThan(e)||(displayPageviews(e),PvStorage.saveLocalCache(JSON.stringify(e)))})}$(function(){$(".pageviews").length<=0||(PvStorage.inspectKeys(),PvStorage.hasCache()?(displayPageviews(PvStorage.getCache()),PvStorage.isExpired()?PvOpts.hasLocalMeta()?fetchLocalPageviews(!0).then(fetchProxyPageviews):fetchProxyPageviews():PvStorage.isFromLocal()&&fetchProxyPageviews()):PvOpts.hasLocalMeta()?fetchLocalPageviews().then(fetchProxyPageviews):fetchProxyPageviews())}); \ No newline at end of file diff --git a/categories/android/index.html b/categories/android/index.html new file mode 100644 index 00000000..37b25478 --- /dev/null +++ b/categories/android/index.html @@ -0,0 +1 @@ + android | Rayanfam Blog
Home Categories android
Category
Cancel
diff --git a/categories/cisco/index.html b/categories/cisco/index.html new file mode 100644 index 00000000..cbde8d93 --- /dev/null +++ b/categories/cisco/index.html @@ -0,0 +1 @@ + cisco | Rayanfam Blog
Home Categories cisco
Category
Cancel
diff --git a/categories/cpu/index.html b/categories/cpu/index.html new file mode 100644 index 00000000..13561ee0 --- /dev/null +++ b/categories/cpu/index.html @@ -0,0 +1 @@ + cpu | Rayanfam Blog
Home Categories cpu
Category
Cancel
diff --git a/categories/debugger/index.html b/categories/debugger/index.html new file mode 100644 index 00000000..a7e1e5e3 --- /dev/null +++ b/categories/debugger/index.html @@ -0,0 +1 @@ + debugger | Rayanfam Blog
Home Categories debugger
Category
Cancel
diff --git a/categories/debugging/index.html b/categories/debugging/index.html new file mode 100644 index 00000000..951745a7 --- /dev/null +++ b/categories/debugging/index.html @@ -0,0 +1 @@ + debugging | Rayanfam Blog
Home Categories debugging
Category
Cancel
diff --git a/categories/emulator/index.html b/categories/emulator/index.html new file mode 100644 index 00000000..6e17a5d7 --- /dev/null +++ b/categories/emulator/index.html @@ -0,0 +1 @@ + emulator | Rayanfam Blog
Home Categories emulator
Category
Cancel
diff --git a/categories/hardware/index.html b/categories/hardware/index.html new file mode 100644 index 00000000..75d05706 --- /dev/null +++ b/categories/hardware/index.html @@ -0,0 +1 @@ + hardware | Rayanfam Blog
Home Categories hardware
Category
Cancel
diff --git a/categories/hyperdbg/index.html b/categories/hyperdbg/index.html new file mode 100644 index 00000000..e6b91905 --- /dev/null +++ b/categories/hyperdbg/index.html @@ -0,0 +1 @@ + hyperdbg | Rayanfam Blog
Home Categories hyperdbg
Category
Cancel
diff --git a/categories/hypervisor/index.html b/categories/hypervisor/index.html new file mode 100644 index 00000000..015ab953 --- /dev/null +++ b/categories/hypervisor/index.html @@ -0,0 +1 @@ + hypervisor | Rayanfam Blog
Home Categories hypervisor
Category
Cancel
diff --git a/categories/index.html b/categories/index.html new file mode 100644 index 00000000..a60f6afa --- /dev/null +++ b/categories/index.html @@ -0,0 +1 @@ + Categories | Rayanfam Blog
Home Categories
Categories
Cancel
diff --git a/categories/instrumentation/index.html b/categories/instrumentation/index.html new file mode 100644 index 00000000..22c3565a --- /dev/null +++ b/categories/instrumentation/index.html @@ -0,0 +1 @@ + instrumentation | Rayanfam Blog
Home Categories instrumentation
Category
Cancel
diff --git a/categories/kernel-mode/index.html b/categories/kernel-mode/index.html new file mode 100644 index 00000000..33359464 --- /dev/null +++ b/categories/kernel-mode/index.html @@ -0,0 +1 @@ + kernel-mode | Rayanfam Blog
Home Categories kernel-mode
Category
Cancel
diff --git a/categories/linux/index.html b/categories/linux/index.html new file mode 100644 index 00000000..24545e29 --- /dev/null +++ b/categories/linux/index.html @@ -0,0 +1 @@ + linux | Rayanfam Blog
Home Categories linux
Category
Cancel
diff --git a/categories/malware/index.html b/categories/malware/index.html new file mode 100644 index 00000000..f5b45bb0 --- /dev/null +++ b/categories/malware/index.html @@ -0,0 +1 @@ + malware | Rayanfam Blog
Home Categories malware
Category
Cancel
diff --git a/categories/net-framework/index.html b/categories/net-framework/index.html new file mode 100644 index 00000000..574b4145 --- /dev/null +++ b/categories/net-framework/index.html @@ -0,0 +1 @@ + net-framework | Rayanfam Blog
Home Categories net-framework
Category
Cancel
diff --git a/categories/network/index.html b/categories/network/index.html new file mode 100644 index 00000000..dd1c219b --- /dev/null +++ b/categories/network/index.html @@ -0,0 +1 @@ + network | Rayanfam Blog
Home Categories network
Category
Cancel
diff --git a/categories/pentest/index.html b/categories/pentest/index.html new file mode 100644 index 00000000..9fb393fc --- /dev/null +++ b/categories/pentest/index.html @@ -0,0 +1 @@ + pentest | Rayanfam Blog
Home Categories pentest
Category
Cancel
diff --git a/categories/programming/index.html b/categories/programming/index.html new file mode 100644 index 00000000..149dabba --- /dev/null +++ b/categories/programming/index.html @@ -0,0 +1 @@ + programming | Rayanfam Blog
Home Categories programming
Category
Cancel
diff --git a/categories/ransomware/index.html b/categories/ransomware/index.html new file mode 100644 index 00000000..f2a658d1 --- /dev/null +++ b/categories/ransomware/index.html @@ -0,0 +1 @@ + ransomware | Rayanfam Blog
Home Categories ransomware
Category
Cancel
diff --git a/categories/security/index.html b/categories/security/index.html new file mode 100644 index 00000000..d4f075d1 --- /dev/null +++ b/categories/security/index.html @@ -0,0 +1 @@ + security | Rayanfam Blog
Home Categories security
Category
Cancel
diff --git a/categories/social/index.html b/categories/social/index.html new file mode 100644 index 00000000..ea1b417a --- /dev/null +++ b/categories/social/index.html @@ -0,0 +1 @@ + social | Rayanfam Blog
Home Categories social
Category
Cancel
diff --git a/categories/software/index.html b/categories/software/index.html new file mode 100644 index 00000000..706f03f3 --- /dev/null +++ b/categories/software/index.html @@ -0,0 +1 @@ + software | Rayanfam Blog
Home Categories software
Category
Cancel
diff --git a/categories/sysadmin/index.html b/categories/sysadmin/index.html new file mode 100644 index 00000000..7401193a --- /dev/null +++ b/categories/sysadmin/index.html @@ -0,0 +1 @@ + sysadmin | Rayanfam Blog
Home Categories sysadmin
Category
Cancel
diff --git a/categories/tutorials/index.html b/categories/tutorials/index.html new file mode 100644 index 00000000..73e8a2c9 --- /dev/null +++ b/categories/tutorials/index.html @@ -0,0 +1 @@ + tutorials | Rayanfam Blog
Home Categories tutorials
Category
Cancel
diff --git a/categories/user-mode/index.html b/categories/user-mode/index.html new file mode 100644 index 00000000..b905afa6 --- /dev/null +++ b/categories/user-mode/index.html @@ -0,0 +1 @@ + user-mode | Rayanfam Blog
Home Categories user-mode
Category
Cancel
diff --git a/categories/windows/index.html b/categories/windows/index.html new file mode 100644 index 00000000..714c0760 --- /dev/null +++ b/categories/windows/index.html @@ -0,0 +1 @@ + windows | Rayanfam Blog
Home Categories windows
Category
Cancel
diff --git a/contact/index.html b/contact/index.html new file mode 100644 index 00000000..b73916e1 --- /dev/null +++ b/contact/index.html @@ -0,0 +1 @@ + Contact | Rayanfam Blog
Home Contact
Contact
Cancel
diff --git a/feed.xml b/feed.xml new file mode 100644 index 00000000..b8061fc7 --- /dev/null +++ b/feed.xml @@ -0,0 +1 @@ + https://rayanfam.com/Rayanfam BlogWe write about Windows Internals, Hypervisors, Linux, and Networks. 2024-07-13T13:14:59+00:00 Rayanfam Blog https://rayanfam.com/ Jekyll © 2024 Rayanfam Blog /assets/img/favicons/favicon.ico /assets/img/favicons/favicon-96x96.png Building Silicon Dreams: An Adventure in Hardware Design2023-10-07T00:00:00+00:00 2023-10-08T09:16:09+00:00 https://rayanfam.com/topics/hardware-design-stack/ {"name"=>"Sina Karvandi, Saleh Khalaj Monfared", "link"=>"https://twitter.com/Intel80x86"} Story Time Exploring the internals of processors has long been a fascination of mine. After spending a lot of time experiencing different processor features like hypervisor and configuring different x86 MSRs, I was seeking to find a way of knowing how exactly these concepts and configurations are made and how they work on the silicon. Initially, my knowledge of hardware design was limited t... HyperDbg’s One Thousand and One Nights2022-06-13T00:00:00+00:00 2024-07-13T12:45:21+00:00 https://rayanfam.com/topics/hyperdbg-one-thousand-and-one-nights/ {"name"=>"Saleh Monfared, and Sina Karvandi", "link"=>"https://twitter.com/sal3hh"} This post is a different one, in that, it is more of an overview, rather than a technical post. Here, we provide a high-level summary of HyperDbg Debugger, its principles, and perspective. Introduction HyperDbg is an open-source, hypervisor-assisted debugger that can be used to debug both user-mode and kernel-mode applications. The closest similar product available to HyperDbg is WinDbg. Hy... Hypervisor From Scratch – Part 8: How To Do Magic With Hypervisor!2020-03-24T00:00:00+00:00 2024-07-13T13:14:26+00:00 https://rayanfam.com/topics/hypervisor-from-scratch-part-8/ {"name"=>"Sina Karvandi", "link"=>"https://twitter.com/Intel80x86"} If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous pr... Hypervisor From Scratch – Part 7: Using EPT & Page-Level Monitoring Features2020-01-20T00:00:00+00:00 2024-07-13T13:14:26+00:00 https://rayanfam.com/topics/hypervisor-from-scratch-part-7/ {"name"=>"Sina Karvandi", "link"=>"https://twitter.com/Intel80x86"} If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous pr... Reversing Windows Internals (Part 1) - Digging Into Handles, Callbacks & ObjectTypes2019-12-09T00:00:00+00:00 2024-07-13T12:45:21+00:00 https://rayanfam.com/topics/reversing-windows-internals-part1/ {"name"=>"Sina Karvandi", "link"=>"https://twitter.com/Intel80x86"} Introduction Welcome to the first part of a series of posts about Exploring &amp; Reversing Windows Concepts and Internals. If you reach here then you’re probably a security researcher or a programmer and this post and similar posts can help you understand what’s going on in some parts of Windows when you use objects with different users and credentials and what you can expect from Windows a... diff --git a/index.html b/index.html new file mode 100644 index 00000000..82c2a15f --- /dev/null +++ b/index.html @@ -0,0 +1 @@ + Rayanfam Blog
Home
Rayanfam Blog
Cancel

Building Silicon Dreams: An Adventure in Hardware Design

Story Time Exploring the internals of processors has long been a fascination of mine. After spending a lot of time experiencing different processor features like hypervisor and configuring diffe...

HyperDbg’s One Thousand and One Nights

This post is a different one, in that, it is more of an overview, rather than a technical post. Here, we provide a high-level summary of HyperDbg Debugger, its principles, and perspective. Intro...

Hypervisor From Scratch – Part 8: How To Do Magic With Hypervisor!

If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and r...

Hypervisor From Scratch – Part 7: Using EPT & Page-Level Monitoring Features

If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and r...

Reversing Windows Internals (Part 1) - Digging Into Handles, Callbacks & ObjectTypes

Introduction Welcome to the first part of a series of posts about Exploring &amp; Reversing Windows Concepts and Internals. If you reach here then you’re probably a security researcher or a prog...

Why you should not always trust MSDN: Finding Real Access Rights Needed By Handles

Introduction Hi guys, The title of this topic is somehow weird, if you think everything in MSDN is 100% match with what Microsoft implemented in Windows (like what I used to think), you’re defi...

Hypervisor From Scratch – Part 6: Virtualizing An Already Running System

If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and r...

PacketScript overview: A Lua scripting engine for in-kernel packet processing

As I was surfing the net, trying to find a way to prototype network protocols or features in Linux. I stumbled upon PacketScript. PacketScript is the an implementation of the Lua VM inside Linux...

Call Gates' Ring Transitioning in IA-32 Mode

Have you ever thought how transitions between different rings performed? Well, SYSENTER &amp; SYSCALL used in modern OSs for transitioning between ring 3 to ring 0 but if there are other rings, ...

Hypervisor From Scratch – Part 5: Setting up VMCS & Running Guest Code

If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and r...

diff --git a/norobots/index.html b/norobots/index.html new file mode 100644 index 00000000..bf99e4d3 --- /dev/null +++ b/norobots/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/page2/index.html b/page2/index.html new file mode 100644 index 00000000..3ffdafb0 --- /dev/null +++ b/page2/index.html @@ -0,0 +1 @@ + Rayanfam Blog
Home
Rayanfam Blog
Cancel

VmcsAuditor - A Bochs-Based Hypervisor Layout Checker

Introduction If you’ve ever had experience with developing your own hypervisor from scratch then you definitely faced with the problems of layout checking. What makes it hard to develop a hyperv...

Start linux kernel module development!

Hi everyone! In this post I’m going to introduce you to the world of linux kernel module development. I am a newcomer in this field myself but I decided to document everything in this blog as I ...

Hypervisor From Scratch – Part 4: Address Translation Using Extended Page Table (EPT)

If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and r...

Hypervisor From Scratch – Part 3: Setting up Our First Virtual Machine

If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and r...

Using Intel's Streaming SIMD Extensions 3 (MONITOR MWAIT) As A Kernel Debugging Trick

Introduction MONITOR and MWAIT are using when the CPU needs to be stopped executing the instruction and enter an implementation-dependent optimized state until some special event happens. MONIT...

Hypervisor From Scratch – Part 2: Entering VMX Operation

If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and r...

A Tour of Mount in Linux

I had windows 10 installed on my laptop because of serious incompatibility of ROG laptops with Linux and my desire to play some games after years of living in bash! This continued for a year and ...

Hypervisor From Scratch - Part 1: Basic Concepts & Configure Testing Environment

If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and r...

x64 Inline Assembly in Windows Driver Kit

As my testing always interferes with running assembly directly in kernel-level and unfortunately Microsoft no longer supports x64 inline assembly through their compilers and as I always have stru...

Inside Windows Page Frame Number (PFN) – Part 2

Hey there, In the previous part, I’d explained about Page Frame Number and its importance in the OSs architecture. In this part, I’ll trace PFN more practically. I strongly recommend to read the...

diff --git a/page3/index.html b/page3/index.html new file mode 100644 index 00000000..c2a65c6f --- /dev/null +++ b/page3/index.html @@ -0,0 +1 @@ + Rayanfam Blog
Home
Rayanfam Blog
Cancel

Cisco switch security features cheatsheet

Cisco switches (running IOS) have plenty of features that are critical to modern networks. Some are Cisco security features that eliminate several important attack vectors on layer 2. This is arg...

Inside Windows Page Frame Number (PFN) - Part 1

Introduction (Page Frame Number) Windows and almost all the OSs use Page Frame Number Database in order to have a track of virtually allocated pages to know which page must be freed or evicted o...

Captive portal detection with a working sample in all major OSs!

Hi everyone I’ve been working on a project which involves a developing a captive portal system from scratch. and I’m going to gradually post more of challenges we faced and the way we solved them...

Defeating malware's Anti-VM techniques (CPUID-Based Instructions)

[The picture of this post is taken by one of my best friends, Ahmad Ghazi in Chitgar Lake !] Introduction You should by now be aware of everything, cause the topic’s title clearly describes the...

PyKD Tutorial - part 2

The content of this post is the second part of PyKD Tutorials, so make sure to read this topic first, then continue reading this topic. Breakpoints Breakpoints are such useful things and can gi...

PyKD Tutorial - part 1

Using windbg script syntax is such annoying thing that almost all reverse engineers have problems dealing with it but automating debugging gives such a power that can’t be easily ignored. A good ...

Active Directory Certificate Services Overview and Migration

Active Directory Certificate Services Active Directory Certificate Services (AD CS) is a role in Windows Server which allows you to fully implement a PKI infrastructure. AD CS also provides temp...

Bochs Emulator - Debug & Instrument

There is also another post about configuring and building Bochs on Windows &amp; OS X if you have a problem compiling Bochs, take a look at here! In my opinion, Bochs is an amazing thing because...

Bochs Emulator - Config & Build on Windows and OS X

Introduction Bochs is an x86 - x86-64 multiplatform emulator that provides emulation of CPU Instruction Fetching without using Hypervisor technologies like Intel’s VT-x and AMD-V. One of the be...

A partial survey among non-general purpose registers

Hi guys, In the past few days, I was searching about non-general purpose register then I saw the following pictures that give me a new idea of posting about the non-general purpose registers. T...

diff --git a/page4/index.html b/page4/index.html new file mode 100644 index 00000000..d20763aa --- /dev/null +++ b/page4/index.html @@ -0,0 +1 @@ + Rayanfam Blog
Home
Rayanfam Blog
Cancel

Introduction to systemd : Basic Usage and Concepts

Hi everyone In this post I am going to explain some essential systemd commands and concepts. As systemd popularity grew much more and changed the linux ecosystem drastically, every sysadmin, Dev...

Useful Configs for NGINX

After posting the first of my linux SysAdmin quick config sample series titled “Useful Configs for squid” (which you can read here). I decided to write another post, this time about the powerful ...

Assembly Challenge : Jump to a non-relative address without using registers

During developing a dispatch table for some instructions in binaries, one of the challenging problem which I faced, was changing the registers state in a way that doesn’t affect the program flow!...

A first look at some aspects of Intel's "Vanderpool" initiative

A few hours ago, I was working on Intel VT-x which enables a hardware support for virtualization then I saw the following slides which gives me lots of information about Hypervisor instruction...

Build a Simple Pin Tool

As I promised previously in Pinitor’s topic, I prepare a post about how to build your first pin tool. Building a pin tool is somehow difficult when they didn’t update their sample tools so that ...

Pinitor - An API Monitor Based on Pin

In the last few days, I was thinking about finding a way to discover the Native Windows APIs which malware programmers are more interested in. What I did was downloading a sample of 48k malware f...

Getting started with Python and Qt for cross-platform GUI apps

Hi everyone, in this blog post I want to explain how to start developing desktop applications using Qt and Python. We will use PyQt5 module in order to connect Qt with Python. Qt was originally d...

Detecting CPU Structure in .Net Framework

The title seems simple, just getting CPU structure to detect if your program running on an AMD64 or an Intel x86 machine. Remember when you install a 32-bit operation system on a 64-bit machine ...

Getting started with Windows Server command line

Hello everyone, In this post I am going to introduce some basic commands that are used to configure Windows Server. Of course if you are using Windows Server with GUI, you may not need these comm...

Get everything from .Net Reflection

In the previous posts, I’d explained about low-level languages and play around kernel-mode reversing but now let’s dig into the high-level languages. In this post, I’m gonna share a comprehensive...

diff --git a/page5/index.html b/page5/index.html new file mode 100644 index 00000000..70dc0ea1 --- /dev/null +++ b/page5/index.html @@ -0,0 +1 @@ + Rayanfam Blog
Home
Rayanfam Blog
Cancel

Fooling Windows about its internal CPU

In this post, I’m gonna show you how you can fool windows about its internal structure and sometimes give it wrong information about its internal capabilities or internal information which can br...

Simple OpenLDAP + phpLDAPadmin setup

Hello everyone, In this blog post I’m going to show you how to setup a simple OpenLDAP server  with phpldapadmin on apache. I’m posting this because I didn’t find any up to date content on how t...

GRE over IPSec in Cisco IOS

Cisco GRE over IPSEC In the following is a sample configuration for GRE/IPSEC in Cisco IOS devices. you can tailor this to your needs (changing encryption algorithms, IP addresses etc), just r...

Cisco IOS and StrongSWAN IPSEC VPN

In this blog post we will cover IPSEC tunnel between Linux StrongSWAN and Cisco IOS. The strongSWAN config file can copied exactly as is to another server with the IP of Cisco Router and the tun...

Exploring from User-Mode to Kernel-Mode

There were times when I want to trace instructions from User Mode and continue tracing it into Kernel mode to reverse Windows’s internal implementation with my own supplied parameters from User M...

Defeat Malware's Dynamic API Loading

There are thousands of ways which makes malwares resist against static dissambling and static analysing. One of the known ways to circumvent against suspicious API blocking or analysing staticall...

Lack of rechecking permissions in Android

Yesterday me and one of my friends were working on an Android Penetration testing project. After testing some kinds of exploit then we somehow get root privilege with some kinds of limitations. I...

Import Address Table (IAT) in action

Did you ever think about how different dll files with different versions and obviously with different addresses of functions work perfectly together ? The answer is Import Address Table (IAT). I...

Bind9 chroot on debian 8

From Wikipedia: BIND, or named, is the most widely used Domain Name System (DNS) software on the Internet. On Unix-like operating systems it is the de facto standard. As you know chrooting ...

Change User-Mode application's virtual address through Kernel Debugging

Well, it’s somehow an odd topic but sometimes it could be really helpful in some situations. So what are the situations? Imagine sometimes you need to access windows stuffs that aren’t availabl...

diff --git a/page6/index.html b/page6/index.html new file mode 100644 index 00000000..f45a14e4 --- /dev/null +++ b/page6/index.html @@ -0,0 +1 @@ + Rayanfam Blog
Home
Rayanfam Blog
Cancel

How to get every detail about SSDT , GDT , IDT in a blink of an eye

In a few days ago I was looking for something to show me the SSDT and GDT (Which is really important in malware analyzing because most of rootkits are interested in hooking and changing this stuf...

Useful Configs for Squid3 Cache

Hi everyone! After searching the web so many times and testing different configurations of Squid, I have found these minimal working configs which you can use to achieve the features you want fr...

A New Anti Ransomware Idea

In the last few days, I was asked to give a new idea for creating an anti ransomware and now I wanna share my idea and source codes. The Full Source Code Is Available at : https://github.com/Sin...

Kernel Mode Debugging by Windbg

Hey there, Today I’m gonna show you how to make a kernel mode debugging using VMWare and Windbg and Windows. So why should you do this ?! It’s clear , everything such as Kernel Mode Driver Deb...

Hello World !

This is the first post of our blog ! After 5+ years of experience working with various technologies and developing skills in different computer fields including security and network, we want to ...

diff --git a/posts/index.html b/posts/index.html new file mode 100644 index 00000000..bf99e4d3 --- /dev/null +++ b/posts/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/redirects.json b/redirects.json new file mode 100644 index 00000000..629ed190 --- /dev/null +++ b/redirects.json @@ -0,0 +1 @@ +{"/norobots/":"https://rayanfam.com/404.html","/assets/":"https://rayanfam.com/404.html","/posts/":"https://rayanfam.com/404.html"} \ No newline at end of file diff --git a/robots.txt b/robots.txt new file mode 100644 index 00000000..c4d73f91 --- /dev/null +++ b/robots.txt @@ -0,0 +1,5 @@ +User-agent: * + +Disallow: /norobots/ + +Sitemap: https://rayanfam.com/sitemap.xml diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 00000000..def23205 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,1483 @@ + + + +https://rayanfam.com/topics/hello-world/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/kernel-mode-debugging-by-windbg/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/a-new-anti-ransomware-idea/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/useful-config-squid3/ +2022-04-13T21:12:10+00:00 + + +https://rayanfam.com/topics/how-to-get-every-details-about-ssdt-gdt-idt-in-a-blink-of-an-eye/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/change-user-mode-applications-virtual-address-through-kernel-debugging/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/bind9-chroot-on-debian-8/ +2022-04-13T21:12:10+00:00 + + +https://rayanfam.com/topics/import-address-table-in-action/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/lack-of-rechecking-permissions-in-android/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/defeat-malware-dynamic-api-loading/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/exploring-from-usermode-to-kernelmode/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/cisco-ios-and-strongswan-ipsec-vpn/ +2022-04-13T21:19:17+00:00 + + +https://rayanfam.com/topics/gre-over-ipsec-in-cisco-ios/ +2022-04-13T21:12:10+00:00 + + +https://rayanfam.com/topics/simple-openldap-phpldapadmin-setup/ +2022-04-13T21:12:10+00:00 + + +https://rayanfam.com/topics/fooling-windows-about-cpu/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/get-everything-from-net-reflection-by-two-method/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/getting-started-with-windows-server-command-line/ +2022-04-13T21:12:10+00:00 + + +https://rayanfam.com/topics/detecting-cpu-structure-in-net-framework/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/gui-python-qt/ +2022-04-13T21:12:10+00:00 + + +https://rayanfam.com/topics/pinitor/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/build-a-simple-pin-tool/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/intel-vanderpool-initiative-slides/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/assembly-challenge-jump-to-a-non-relative-address-without-using-registers/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/useful-configs-for-nginx/ +2022-04-13T21:12:10+00:00 + + +https://rayanfam.com/topics/introduction-to-systemd-basic-usage-and-concepts/ +2022-04-13T21:12:10+00:00 + + +https://rayanfam.com/topics/survey-non-general-registers/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/bochs-config-and-build-on-windows-and-os-x/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/bochs-emulator-debug-and-instrument/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/active-directory-certificate-services-migration/ +2022-04-13T21:12:10+00:00 + + +https://rayanfam.com/topics/pykd-tutorial-part1/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/pykd-tutorial-part2/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/defeating-malware-anti-vm-techniques-cpuid-based-instructions/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/captive-portal-detection-sample/ +2022-04-13T21:12:10+00:00 + + +https://rayanfam.com/topics/inside-windows-page-frame-number-part1/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/cisco-switch-sec-cheatsheet/ +2022-04-13T21:12:10+00:00 + + +https://rayanfam.com/topics/inside-windows-page-frame-number-part2/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/inline-assembly-in-x64/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/hypervisor-from-scratch-part-1/ +2024-07-13T13:14:26+00:00 + + +https://rayanfam.com/topics/mount-in-linux/ +2022-04-13T21:12:10+00:00 + + +https://rayanfam.com/topics/hypervisor-from-scratch-part-2/ +2024-07-13T13:14:26+00:00 + + +https://rayanfam.com/topics/using-intels-streaming-simd-extensions-3-monitormwait-as-a-kernel-debugging-trick/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/hypervisor-from-scratch-part-3/ +2024-07-13T13:14:26+00:00 + + +https://rayanfam.com/topics/hypervisor-from-scratch-part-4/ +2024-07-13T13:14:26+00:00 + + +https://rayanfam.com/topics/start-linux-kernel-module-development/ +2022-04-13T21:12:10+00:00 + + +https://rayanfam.com/topics/vmcsauditor-a-bochs-based-hypervisor-layout-checker/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/hypervisor-from-scratch-part-5/ +2024-07-13T13:14:26+00:00 + + +https://rayanfam.com/topics/call-gates-ring-transitioning-in-ia-32-mode/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/packetscript-lua-kernel/ +2022-04-13T21:12:10+00:00 + + +https://rayanfam.com/topics/hypervisor-from-scratch-part-6/ +2024-07-13T13:14:26+00:00 + + +https://rayanfam.com/topics/finding-the-real-access-rights-needed-by-handles/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/reversing-windows-internals-part1/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/hypervisor-from-scratch-part-7/ +2024-07-13T13:14:26+00:00 + + +https://rayanfam.com/topics/hypervisor-from-scratch-part-8/ +2024-07-13T13:14:26+00:00 + + +https://rayanfam.com/topics/hyperdbg-one-thousand-and-one-nights/ +2024-07-13T12:45:21+00:00 + + +https://rayanfam.com/topics/hardware-design-stack/ +2023-10-08T09:16:09+00:00 + + +https://rayanfam.com/tutorials/ +2018-08-17T00:00:00+00:00 + + +https://rayanfam.com/tools/ +2018-08-16T00:00:00+00:00 + + +https://rayanfam.com/categories/ +2024-07-13T13:14:59+00:00 + + +https://rayanfam.com/tags/ +2024-07-13T13:14:59+00:00 + + +https://rayanfam.com/archives/ +2024-07-13T13:14:59+00:00 + + +https://rayanfam.com/contact/ +2024-07-13T13:14:59+00:00 + + +https://rayanfam.com/ + + +https://rayanfam.com/tags/begining/ + + +https://rayanfam.com/tags/helloworld/ + + +https://rayanfam.com/tags/start/ + + +https://rayanfam.com/tags/debug-virtual-machine/ + + +https://rayanfam.com/tags/debug-windows/ + + +https://rayanfam.com/tags/debugging-kernel-mode/ + + +https://rayanfam.com/tags/kernel-mode/ + + +https://rayanfam.com/tags/vmware-and-windbg/ + + +https://rayanfam.com/tags/vmware-debugging/ + + +https://rayanfam.com/tags/anti-ransomware/ + + +https://rayanfam.com/tags/anti-ransomware-with-hook/ + + +https://rayanfam.com/tags/hook-for-ransomware/ + + +https://rayanfam.com/tags/ransomware/ + + +https://rayanfam.com/tags/cache/ + + +https://rayanfam.com/tags/debian/ + + +https://rayanfam.com/tags/linux/ + + +https://rayanfam.com/tags/proxy/ + + +https://rayanfam.com/tags/squid/ + + +https://rayanfam.com/tags/squid3/ + + +https://rayanfam.com/tags/gdt/ + + +https://rayanfam.com/tags/global-descriptor-table/ + + +https://rayanfam.com/tags/idt/ + + +https://rayanfam.com/tags/interrupt-descriptor-table/ + + +https://rayanfam.com/tags/ssdt/ + + +https://rayanfam.com/tags/swishdbgext/ + + +https://rayanfam.com/tags/system-service-descriptor-table/ + + +https://rayanfam.com/tags/windbg-extension/ + + +https://rayanfam.com/tags/change-process-from-kernel/ + + +https://rayanfam.com/tags/change-virtual-address/ + + +https://rayanfam.com/tags/kernel-mode-to-user-mode/ + + +https://rayanfam.com/tags/ring0-to-ring3/ + + +https://rayanfam.com/tags/bind9-config/ + + +https://rayanfam.com/tags/bind9-on-debian/ + + +https://rayanfam.com/tags/firstthunk/ + + +https://rayanfam.com/tags/forwardchain/ + + +https://rayanfam.com/tags/iat/ + + +https://rayanfam.com/tags/image-import-descriptor/ + + +https://rayanfam.com/tags/import-address-table-address/ + + +https://rayanfam.com/tags/orginalfirstthunk/ + + +https://rayanfam.com/tags/timedatestamp/ + + +https://rayanfam.com/tags/android-permissions/ + + +https://rayanfam.com/tags/change-adndroid-permission/ + + +https://rayanfam.com/tags/packages-xml/ + + +https://rayanfam.com/tags/debug-dynamic-loading/ + + +https://rayanfam.com/tags/debug-loadlibrary/ + + +https://rayanfam.com/tags/malware-analysis/ + + +https://rayanfam.com/tags/debug-kernel-function/ + + +https://rayanfam.com/tags/debug-native-function/ + + +https://rayanfam.com/tags/kernelmode-to-usermode/ + + +https://rayanfam.com/tags/trace-functions-to-kernel/ + + +https://rayanfam.com/tags/cisco/ + + +https://rayanfam.com/tags/ios/ + + +https://rayanfam.com/tags/ipsec/ + + +https://rayanfam.com/tags/opensource/ + + +https://rayanfam.com/tags/openswan/ + + +https://rayanfam.com/tags/strongswan/ + + +https://rayanfam.com/tags/tunnel/ + + +https://rayanfam.com/tags/vpn/ + + +https://rayanfam.com/tags/gre/ + + +https://rayanfam.com/tags/network/ + + +https://rayanfam.com/tags/directory-service/ + + +https://rayanfam.com/tags/ldap/ + + +https://rayanfam.com/tags/openldap/ + + +https://rayanfam.com/tags/phpldapadmin/ + + +https://rayanfam.com/tags/change-cpu/ + + +https://rayanfam.com/tags/change-kernel/ + + +https://rayanfam.com/tags/edit-windows-internals/ + + +https://rayanfam.com/tags/net-reflection/ + + +https://rayanfam.com/tags/c-plugin/ + + +https://rayanfam.com/tags/c-reflection/ + + +https://rayanfam.com/tags/create-plugin/ + + +https://rayanfam.com/tags/dll-plugin/ + + +https://rayanfam.com/tags/dynamically-call-function/ + + +https://rayanfam.com/tags/invoke-function/ + + +https://rayanfam.com/tags/active-directory/ + + +https://rayanfam.com/tags/cmd/ + + +https://rayanfam.com/tags/powershell/ + + +https://rayanfam.com/tags/server-core/ + + +https://rayanfam.com/tags/windows/ + + +https://rayanfam.com/tags/windows-server/ + + +https://rayanfam.com/tags/windows-server-2012-r2/ + + +https://rayanfam.com/tags/windows-server-core/ + + +https://rayanfam.com/tags/detect-64-bit-os-c/ + + +https://rayanfam.com/tags/get-environment-os/ + + +https://rayanfam.com/tags/x86-and-x64-c/ + + +https://rayanfam.com/tags/beginner/ + + +https://rayanfam.com/tags/bitcoin/ + + +https://rayanfam.com/tags/developement/ + + +https://rayanfam.com/tags/developer/ + + +https://rayanfam.com/tags/gui/ + + +https://rayanfam.com/tags/how-to/ + + +https://rayanfam.com/tags/pyqt/ + + +https://rayanfam.com/tags/pyqt5/ + + +https://rayanfam.com/tags/python/ + + +https://rayanfam.com/tags/python3/ + + +https://rayanfam.com/tags/qt/ + + +https://rayanfam.com/tags/api-monitor/ + + +https://rayanfam.com/tags/api-pin-tool/ + + +https://rayanfam.com/tags/get-native-api-windows/ + + +https://rayanfam.com/tags/intel-pin/ + + +https://rayanfam.com/tags/native-functions-pin-tool/ + + +https://rayanfam.com/tags/windows-api-detection/ + + +https://rayanfam.com/tags/build-tool-for-pin/ + + +https://rayanfam.com/tags/compile-pin-tool/ + + +https://rayanfam.com/tags/pin-tool-example/ + + +https://rayanfam.com/tags/pin-tool-hello-world/ + + +https://rayanfam.com/tags/pin-tool-on-windows/ + + +https://rayanfam.com/tags/run-intel-pin-tool/ + + +https://rayanfam.com/tags/start-with-pin-tool/ + + +https://rayanfam.com/tags/use-intel-pin/ + + +https://rayanfam.com/tags/hypervisor-fundamentals/ + + +https://rayanfam.com/tags/intel-vt-x/ + + +https://rayanfam.com/tags/virtual-machine-control-structure/ + + +https://rayanfam.com/tags/vmcs/ + + +https://rayanfam.com/tags/far-conditional-jump/ + + +https://rayanfam.com/tags/jump-without-register/ + + +https://rayanfam.com/tags/https/ + + +https://rayanfam.com/tags/loadbalance/ + + +https://rayanfam.com/tags/nginx/ + + +https://rayanfam.com/tags/php-fpm/ + + +https://rayanfam.com/tags/webserver/ + + +https://rayanfam.com/tags/debate/ + + +https://rayanfam.com/tags/init/ + + +https://rayanfam.com/tags/management/ + + +https://rayanfam.com/tags/sysadmin/ + + +https://rayanfam.com/tags/systemd/ + + +https://rayanfam.com/tags/sysvinit/ + + +https://rayanfam.com/tags/cpu-registers/ + + +https://rayanfam.com/tags/gdtr-register/ + + +https://rayanfam.com/tags/ldtr-register/ + + +https://rayanfam.com/tags/non-general-registers/ + + +https://rayanfam.com/tags/segment-registers/ + + +https://rayanfam.com/tags/tr-register/ + + +https://rayanfam.com/tags/xmm-register/ + + +https://rayanfam.com/tags/ymm-registers/ + + +https://rayanfam.com/tags/bochs-emulator/ + + +https://rayanfam.com/tags/bochs-os-x/ + + +https://rayanfam.com/tags/bochs-windows/ + + +https://rayanfam.com/tags/compile-bochs/ + + +https://rayanfam.com/tags/config-bochs/ + + +https://rayanfam.com/tags/install-bochs/ + + +https://rayanfam.com/tags/install-windows-on-bochs/ + + +https://rayanfam.com/tags/debug-using-bochs/ + + +https://rayanfam.com/tags/emulator-debugging/ + + +https://rayanfam.com/tags/instrument-instructions-using-bochs/ + + +https://rayanfam.com/tags/instrument-kernel-mode/ + + +https://rayanfam.com/tags/instrument-linux/ + + +https://rayanfam.com/tags/instrument-operating-system/ + + +https://rayanfam.com/tags/instrument-os-x/ + + +https://rayanfam.com/tags/instrument-windows/ + + +https://rayanfam.com/tags/ad-cs/ + + +https://rayanfam.com/tags/ad-ds/ + + +https://rayanfam.com/tags/certificate/ + + +https://rayanfam.com/tags/certificate-services/ + + +https://rayanfam.com/tags/certificate-template/ + + +https://rayanfam.com/tags/certificates/ + + +https://rayanfam.com/tags/doamin/ + + +https://rayanfam.com/tags/microsoft/ + + +https://rayanfam.com/tags/pki/ + + +https://rayanfam.com/tags/getting-started-with-pykd/ + + +https://rayanfam.com/tags/pykd-example/ + + +https://rayanfam.com/tags/pykd-sample/ + + +https://rayanfam.com/tags/pykd-scripts/ + + +https://rayanfam.com/tags/pykd-tutorial/ + + +https://rayanfam.com/tags/run-pykd-command/ + + +https://rayanfam.com/tags/anti-anti-vm/ + + +https://rayanfam.com/tags/bypass-anti-vm/ + + +https://rayanfam.com/tags/bypass-virtual-machine-detection/ + + +https://rayanfam.com/tags/change-cpuid-virtualbox/ + + +https://rayanfam.com/tags/change-cpuid-vmware/ + + +https://rayanfam.com/tags/defeat-anti-vm/ + + +https://rayanfam.com/tags/captive/ + + +https://rayanfam.com/tags/captive-portal/ + + +https://rayanfam.com/tags/covachilli/ + + +https://rayanfam.com/tags/http/ + + +https://rayanfam.com/tags/iptables/ + + +https://rayanfam.com/tags/nodogsplash/ + + +https://rayanfam.com/tags/openwrt/ + + +https://rayanfam.com/tags/router/ + + +https://rayanfam.com/tags/uhttpd/ + + +https://rayanfam.com/tags/wifidog/ + + +https://rayanfam.com/tags/freed-list/ + + +https://rayanfam.com/tags/mizeropagethread/ + + +https://rayanfam.com/tags/mmpfndatabase/ + + +https://rayanfam.com/tags/page-frame-number/ + + +https://rayanfam.com/tags/page-management-in-windows/ + + +https://rayanfam.com/tags/pfn/ + + +https://rayanfam.com/tags/pfn-database/ + + +https://rayanfam.com/tags/pfn-in-windows/ + + +https://rayanfam.com/tags/pfn-list-corrupt/ + + +https://rayanfam.com/tags/standby-list/ + + +https://rayanfam.com/tags/windows-mmpfn-structure/ + + +https://rayanfam.com/tags/windows-paging/ + + +https://rayanfam.com/tags/zero-thread/ + + +https://rayanfam.com/tags/defense/ + + +https://rayanfam.com/tags/port/ + + +https://rayanfam.com/tags/port-security/ + + +https://rayanfam.com/tags/security/ + + +https://rayanfam.com/tags/switch/ + + +https://rayanfam.com/tags/switchport/ + + +https://rayanfam.com/tags/migetphysicaladdress/ + + +https://rayanfam.com/tags/mivatopfn/ + + +https://rayanfam.com/tags/mmavailablepages/ + + +https://rayanfam.com/tags/mmgetphysicaladdress/ + + +https://rayanfam.com/tags/mmgetvirtualforphysical/ + + +https://rayanfam.com/tags/mmnumberofphysicalpages/ + + +https://rayanfam.com/tags/mmresidentavailablepages/ + + +https://rayanfam.com/tags/nt-mmpfn/ + + +https://rayanfam.com/tags/assembly-x64-visual-studio/ + + +https://rayanfam.com/tags/wdk-assembly/ + + +https://rayanfam.com/tags/windows-driver-kit-inline-assembly/ + + +https://rayanfam.com/tags/x64-assembly-in-driver/ + + +https://rayanfam.com/tags/x64-inline-assembly/ + + +https://rayanfam.com/tags/hypervisor/ + + +https://rayanfam.com/tags/hypervisor-tutorial/ + + +https://rayanfam.com/tags/vmx-tutorial/ + + +https://rayanfam.com/tags/create-a-virtual-machine/ + + +https://rayanfam.com/tags/how-to-create-virtual-machine/ + + +https://rayanfam.com/tags/hypervisor-tutorials/ + + +https://rayanfam.com/tags/intel-virtualization/ + + +https://rayanfam.com/tags/intel-vmx/ + + +https://rayanfam.com/tags/intel-vtx-tutorial/ + + +https://rayanfam.com/tags/using-cpu-virtualization/ + + +https://rayanfam.com/tags/vmm-implementation/ + + +https://rayanfam.com/tags/filesystem/ + + +https://rayanfam.com/tags/hfs/ + + +https://rayanfam.com/tags/modern/ + + +https://rayanfam.com/tags/mount/ + + +https://rayanfam.com/tags/os/ + + +https://rayanfam.com/tags/tutorial/ + + +https://rayanfam.com/tags/udev/ + + +https://rayanfam.com/tags/vfs/ + + +https://rayanfam.com/tags/creating-virtual-machine/ + + +https://rayanfam.com/tags/intel-vt-x-tutorial/ + + +https://rayanfam.com/tags/setting-up-virtual-machine-monitor/ + + +https://rayanfam.com/tags/vmm-tutorials/ + + +https://rayanfam.com/tags/vmx-implementation/ + + +https://rayanfam.com/tags/vmx-tutorials/ + + +https://rayanfam.com/tags/intel-mon-feature/ + + +https://rayanfam.com/tags/intel-streaming-simd-extensions-3/ + + +https://rayanfam.com/tags/intel-synchronization-agent/ + + +https://rayanfam.com/tags/monitor-mwait-instructions/ + + +https://rayanfam.com/tags/creating-vmm/ + + +https://rayanfam.com/tags/initiating-vmx-operation/ + + +https://rayanfam.com/tags/irp-mj-device-control/ + + +https://rayanfam.com/tags/method-buffered/ + + +https://rayanfam.com/tags/method-in-direct/ + + +https://rayanfam.com/tags/method-niether/ + + +https://rayanfam.com/tags/method-out-direct/ + + +https://rayanfam.com/tags/vmcs-region/ + + +https://rayanfam.com/tags/vmm/ + + +https://rayanfam.com/tags/vmx-operation/ + + +https://rayanfam.com/tags/vmxon/ + + +https://rayanfam.com/tags/vmxon-region/ + + +https://rayanfam.com/tags/all-context/ + + +https://rayanfam.com/tags/ept/ + + +https://rayanfam.com/tags/eptp/ + + +https://rayanfam.com/tags/extended-page-table/ + + +https://rayanfam.com/tags/extended-page-table-pointer/ + + +https://rayanfam.com/tags/hypervisor-paging/ + + +https://rayanfam.com/tags/invept/ + + +https://rayanfam.com/tags/nested-page-tables/ + + +https://rayanfam.com/tags/npt/ + + +https://rayanfam.com/tags/rapid-virtualization-indexing/ + + +https://rayanfam.com/tags/rvi/ + + +https://rayanfam.com/tags/second-level-address-translation/ + + +https://rayanfam.com/tags/single-context/ + + +https://rayanfam.com/tags/slat/ + + +https://rayanfam.com/tags/stage-2-page-tables/ + + +https://rayanfam.com/tags/c/ + + +https://rayanfam.com/tags/insmod/ + + +https://rayanfam.com/tags/kernel/ + + +https://rayanfam.com/tags/ko/ + + +https://rayanfam.com/tags/lkm/ + + +https://rayanfam.com/tags/lsmod/ + + +https://rayanfam.com/tags/make/ + + +https://rayanfam.com/tags/modprobe/ + + +https://rayanfam.com/tags/module/ + + +https://rayanfam.com/tags/guest-state-error/ + + +https://rayanfam.com/tags/host-state-error/ + + +https://rayanfam.com/tags/vmcs-auditor/ + + +https://rayanfam.com/tags/vmcs-layout-checking/ + + +https://rayanfam.com/tags/vmlaunch-0x7/ + + +https://rayanfam.com/tags/vmlaunch-0x8/ + + +https://rayanfam.com/tags/vmlaunch-error/ + + +https://rayanfam.com/tags/vmresume-error/ + + +https://rayanfam.com/tags/vmx-error-checking/ + + +https://rayanfam.com/tags/building-vmcs/ + + +https://rayanfam.com/tags/configuring-vmcs/ + + +https://rayanfam.com/tags/start-virtual-machine/ + + +https://rayanfam.com/tags/vmcs-configuration/ + + +https://rayanfam.com/tags/vmlaunch/ + + +https://rayanfam.com/tags/call-gate/ + + +https://rayanfam.com/tags/call-gate-descriptor/ + + +https://rayanfam.com/tags/changing-to-ring-1-or-2/ + + +https://rayanfam.com/tags/ring-transitioning/ + + +https://rayanfam.com/tags/lua/ + + +https://rayanfam.com/tags/lunatik/ + + +https://rayanfam.com/tags/packetscript/ + + +https://rayanfam.com/tags/xtables/ + + +https://rayanfam.com/tags/xtables-addons/ + + +https://rayanfam.com/tags/control-registers-modification/ + + +https://rayanfam.com/tags/cpuid-modification/ + + +https://rayanfam.com/tags/hypervisor-from-scratch/ + + +https://rayanfam.com/tags/hypervisor-logging/ + + +https://rayanfam.com/tags/msr-modification-detection/ + + +https://rayanfam.com/tags/virtualize-all-logical-cores/ + + +https://rayanfam.com/tags/virtualize-current-system/ + + +https://rayanfam.com/tags/vmfunc/ + + +https://rayanfam.com/tags/vmx-0-settings-and-1-settings/ + + +https://rayanfam.com/tags/access-mask/ + + +https://rayanfam.com/tags/ida-python-script/ + + +https://rayanfam.com/tags/ntopenprocesstokenex/ + + +https://rayanfam.com/tags/obpreferenceobjectbyhandlewithtag/ + + +https://rayanfam.com/tags/obreferenceobjectbyhandle/ + + +https://rayanfam.com/tags/process-query-limited-information/ + + +https://rayanfam.com/tags/real-access-rights/ + + +https://rayanfam.com/tags/xrefs-ida-python/ + + +https://rayanfam.com/tags/handle-creation-in-windows/ + + +https://rayanfam.com/tags/handle-table-windows/ + + +https://rayanfam.com/tags/handles-in-windows/ + + +https://rayanfam.com/tags/how-openprocess-works/ + + +https://rayanfam.com/tags/object-internals/ + + +https://rayanfam.com/tags/object-type-windows/ + + +https://rayanfam.com/tags/reversing-windows-internals/ + + +https://rayanfam.com/tags/windows-callbacks/ + + +https://rayanfam.com/tags/windows-internals-tutorial/ + + +https://rayanfam.com/tags/windows-objects/ + + +https://rayanfam.com/tags/2mb-ept/ + + +https://rayanfam.com/tags/4kb-ept/ + + +https://rayanfam.com/tags/ept-hook/ + + +https://rayanfam.com/tags/ept-monitoring/ + + +https://rayanfam.com/tags/ept-table/ + + +https://rayanfam.com/tags/hidden-hook/ + + +https://rayanfam.com/tags/hypervisor-tutorial-part-7/ + + +https://rayanfam.com/tags/intel-vt-x-paging/ + + +https://rayanfam.com/tags/invalidate-ept/ + + +https://rayanfam.com/tags/memory-type-range-register/ + + +https://rayanfam.com/tags/mmu-virtualization/ + + +https://rayanfam.com/tags/mtrr/ + + +https://rayanfam.com/tags/vmcall/ + + +https://rayanfam.com/tags/event-injection/ + + +https://rayanfam.com/tags/high-irql-messaging/ + + +https://rayanfam.com/tags/hyper-v-compatible/ + + +https://rayanfam.com/tags/hypervisor-on-hyper-v/ + + +https://rayanfam.com/tags/hypervisor-part-8/ + + +https://rayanfam.com/tags/inject-interrupt/ + + +https://rayanfam.com/tags/invpcid/ + + +https://rayanfam.com/tags/invvpid/ + + +https://rayanfam.com/tags/syscall-hook/ + + +https://rayanfam.com/tags/system-call-hook/ + + +https://rayanfam.com/tags/vpid-in-hypervisor/ + + +https://rayanfam.com/tags/wpp-tracing/ + + +https://rayanfam.com/tags/hyperdbg/ + + +https://rayanfam.com/tags/windbg/ + + +https://rayanfam.com/tags/hyperdbg-vs-windbg/ + + +https://rayanfam.com/tags/debugger/ + + +https://rayanfam.com/tags/hyperdbg-principles/ + + +https://rayanfam.com/tags/reversing-using-hyperdbg/ + + +https://rayanfam.com/tags/using-hyperdbg-debugger/ + + +https://rayanfam.com/tags/hardware-design/ + + +https://rayanfam.com/tags/hdl-languages/ + + +https://rayanfam.com/tags/chisel/ + + +https://rayanfam.com/tags/hls/ + + +https://rayanfam.com/tags/vivado/ + + +https://rayanfam.com/tags/vitis/ + + +https://rayanfam.com/tags/netlist/ + + +https://rayanfam.com/tags/reversing-netlist/ + + +https://rayanfam.com/tags/openlane/ + + +https://rayanfam.com/tags/openram/ + + +https://rayanfam.com/tags/gdsii/ + + +https://rayanfam.com/tags/block-ram/ + + +https://rayanfam.com/tags/verilog/ + + +https://rayanfam.com/tags/vhdl/ + + +https://rayanfam.com/tags/modelsim/ + + +https://rayanfam.com/tags/gtkwave/ + + +https://rayanfam.com/tags/verilator/ + + +https://rayanfam.com/categories/social/ + + +https://rayanfam.com/categories/debugging/ + + +https://rayanfam.com/categories/kernel-mode/ + + +https://rayanfam.com/categories/ransomware/ + + +https://rayanfam.com/categories/software/ + + +https://rayanfam.com/categories/linux/ + + +https://rayanfam.com/categories/network/ + + +https://rayanfam.com/categories/sysadmin/ + + +https://rayanfam.com/categories/user-mode/ + + +https://rayanfam.com/categories/android/ + + +https://rayanfam.com/categories/pentest/ + + +https://rayanfam.com/categories/malware/ + + +https://rayanfam.com/categories/cisco/ + + +https://rayanfam.com/categories/windows/ + + +https://rayanfam.com/categories/net-framework/ + + +https://rayanfam.com/categories/programming/ + + +https://rayanfam.com/categories/instrumentation/ + + +https://rayanfam.com/categories/hypervisor/ + + +https://rayanfam.com/categories/cpu/ + + +https://rayanfam.com/categories/emulator/ + + +https://rayanfam.com/categories/tutorials/ + + +https://rayanfam.com/categories/security/ + + +https://rayanfam.com/categories/debugger/ + + +https://rayanfam.com/categories/hyperdbg/ + + +https://rayanfam.com/categories/hardware/ + + +https://rayanfam.com/page2/ + + +https://rayanfam.com/page3/ + + +https://rayanfam.com/page4/ + + +https://rayanfam.com/page5/ + + +https://rayanfam.com/page6/ + + +https://rayanfam.com/assets/files/Intel.x86-64.Architecture.2012011000.pdf +2024-07-13T13:14:43+00:00 + + +https://rayanfam.com/assets/files/Requirements-for-Implementing-the-Microsoft-Hypervisor-Interface-1.pdf +2024-07-13T13:14:43+00:00 + + +https://rayanfam.com/assets/files/VMCS.pdf +2024-07-13T13:14:43+00:00 + + diff --git a/sw.js b/sw.js new file mode 100644 index 00000000..9392c0fe --- /dev/null +++ b/sw.js @@ -0,0 +1 @@ +self.importScripts('/assets/js/data/swcache.js'); const cacheName = 'chirpy-20240713.1315'; function verifyDomain(url) { for (const domain of allowedDomains) { const regex = RegExp(`^http(s)?:\/\/${domain}\/`); if (regex.test(url)) { return true; } } return false; } function isExcluded(url) { for (const item of denyUrls) { if (url === item) { return true; } } return false; } self.addEventListener('install', e => { self.skipWaiting(); e.waitUntil( caches.open(cacheName).then(cache => { return cache.addAll(resource); }) ); }); self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request) .then(response => { if (response) { return response; } return fetch(event.request) .then(response => { const url = event.request.url; if (event.request.method !== 'GET' || !verifyDomain(url) || isExcluded(url)) { return response; } /* see: */ let responseToCache = response.clone(); caches.open(cacheName) .then(cache => { /* console.log('[sw] Caching new resource: ' + event.request.url); */ cache.put(event.request, responseToCache); }); return response; }); }) ); }); self.addEventListener('activate', e => { e.waitUntil( caches.keys().then(keyList => { return Promise.all( keyList.map(key => { if(key !== cacheName) { return caches.delete(key); } }) ); }) ); }); diff --git a/tags/2mb-ept/index.html b/tags/2mb-ept/index.html new file mode 100644 index 00000000..ac3da544 --- /dev/null +++ b/tags/2mb-ept/index.html @@ -0,0 +1 @@ + 2mb-ept | Rayanfam Blog
Home Tags 2mb-ept
Tag
Cancel
diff --git a/tags/4kb-ept/index.html b/tags/4kb-ept/index.html new file mode 100644 index 00000000..1dbbfb18 --- /dev/null +++ b/tags/4kb-ept/index.html @@ -0,0 +1 @@ + 4kb-ept | Rayanfam Blog
Home Tags 4kb-ept
Tag
Cancel
diff --git a/tags/access-mask/index.html b/tags/access-mask/index.html new file mode 100644 index 00000000..4b505134 --- /dev/null +++ b/tags/access-mask/index.html @@ -0,0 +1 @@ + access_mask | Rayanfam Blog
Home Tags access_mask
Tag
Cancel
diff --git a/tags/active-directory/index.html b/tags/active-directory/index.html new file mode 100644 index 00000000..85623416 --- /dev/null +++ b/tags/active-directory/index.html @@ -0,0 +1 @@ + active-directory | Rayanfam Blog
Home Tags active-directory
Tag
Cancel
diff --git a/tags/ad-cs/index.html b/tags/ad-cs/index.html new file mode 100644 index 00000000..ca157613 --- /dev/null +++ b/tags/ad-cs/index.html @@ -0,0 +1 @@ + ad-cs | Rayanfam Blog
Home Tags ad-cs
Tag
Cancel
diff --git a/tags/ad-ds/index.html b/tags/ad-ds/index.html new file mode 100644 index 00000000..6d1d6f28 --- /dev/null +++ b/tags/ad-ds/index.html @@ -0,0 +1 @@ + ad-ds | Rayanfam Blog
Home Tags ad-ds
Tag
Cancel
diff --git a/tags/all-context/index.html b/tags/all-context/index.html new file mode 100644 index 00000000..e5d320d7 --- /dev/null +++ b/tags/all-context/index.html @@ -0,0 +1 @@ + all-context | Rayanfam Blog
Home Tags all-context
Tag
Cancel
diff --git a/tags/android-permissions/index.html b/tags/android-permissions/index.html new file mode 100644 index 00000000..c5eb502e --- /dev/null +++ b/tags/android-permissions/index.html @@ -0,0 +1 @@ + android-permissions | Rayanfam Blog
Home Tags android-permissions
Tag
Cancel
diff --git a/tags/anti-anti-vm/index.html b/tags/anti-anti-vm/index.html new file mode 100644 index 00000000..9eb203d3 --- /dev/null +++ b/tags/anti-anti-vm/index.html @@ -0,0 +1 @@ + anti-anti-vm | Rayanfam Blog
Home Tags anti-anti-vm
Tag
Cancel
diff --git a/tags/anti-ransomware-with-hook/index.html b/tags/anti-ransomware-with-hook/index.html new file mode 100644 index 00000000..c97a3a80 --- /dev/null +++ b/tags/anti-ransomware-with-hook/index.html @@ -0,0 +1 @@ + anti-ransomware-with-hook | Rayanfam Blog
Home Tags anti-ransomware-with-hook
Tag
Cancel
diff --git a/tags/anti-ransomware/index.html b/tags/anti-ransomware/index.html new file mode 100644 index 00000000..ecef4af7 --- /dev/null +++ b/tags/anti-ransomware/index.html @@ -0,0 +1 @@ + anti-ransomware | Rayanfam Blog
Home Tags anti-ransomware
Tag
Cancel
diff --git a/tags/api-monitor/index.html b/tags/api-monitor/index.html new file mode 100644 index 00000000..f7d022b5 --- /dev/null +++ b/tags/api-monitor/index.html @@ -0,0 +1 @@ + api-monitor | Rayanfam Blog
Home Tags api-monitor
Tag
Cancel
diff --git a/tags/api-pin-tool/index.html b/tags/api-pin-tool/index.html new file mode 100644 index 00000000..1a0e69bf --- /dev/null +++ b/tags/api-pin-tool/index.html @@ -0,0 +1 @@ + api-pin-tool | Rayanfam Blog
Home Tags api-pin-tool
Tag
Cancel
diff --git a/tags/assembly-x64-visual-studio/index.html b/tags/assembly-x64-visual-studio/index.html new file mode 100644 index 00000000..49b6a394 --- /dev/null +++ b/tags/assembly-x64-visual-studio/index.html @@ -0,0 +1 @@ + assembly-x64-visual-studio | Rayanfam Blog
Home Tags assembly-x64-visual-studio
Tag
Cancel
diff --git a/tags/begining/index.html b/tags/begining/index.html new file mode 100644 index 00000000..3facd43a --- /dev/null +++ b/tags/begining/index.html @@ -0,0 +1 @@ + begining | Rayanfam Blog
Home Tags begining
Tag
Cancel
diff --git a/tags/beginner/index.html b/tags/beginner/index.html new file mode 100644 index 00000000..d6b3da3e --- /dev/null +++ b/tags/beginner/index.html @@ -0,0 +1 @@ + beginner | Rayanfam Blog
Home Tags beginner
Tag
Cancel
diff --git a/tags/bind9-config/index.html b/tags/bind9-config/index.html new file mode 100644 index 00000000..33cee9fc --- /dev/null +++ b/tags/bind9-config/index.html @@ -0,0 +1 @@ + bind9-config | Rayanfam Blog
Home Tags bind9-config
Tag
Cancel
diff --git a/tags/bind9-on-debian/index.html b/tags/bind9-on-debian/index.html new file mode 100644 index 00000000..46bc7988 --- /dev/null +++ b/tags/bind9-on-debian/index.html @@ -0,0 +1 @@ + bind9-on-debian | Rayanfam Blog
Home Tags bind9-on-debian
Tag
Cancel
diff --git a/tags/bitcoin/index.html b/tags/bitcoin/index.html new file mode 100644 index 00000000..fa9c7f4a --- /dev/null +++ b/tags/bitcoin/index.html @@ -0,0 +1 @@ + bitcoin | Rayanfam Blog
Home Tags bitcoin
Tag
Cancel
diff --git a/tags/block-ram/index.html b/tags/block-ram/index.html new file mode 100644 index 00000000..006770e0 --- /dev/null +++ b/tags/block-ram/index.html @@ -0,0 +1 @@ + Block-RAM | Rayanfam Blog
Home Tags Block-RAM
Tag
Cancel
diff --git a/tags/bochs-emulator/index.html b/tags/bochs-emulator/index.html new file mode 100644 index 00000000..9e9761b0 --- /dev/null +++ b/tags/bochs-emulator/index.html @@ -0,0 +1 @@ + bochs-emulator | Rayanfam Blog
Home Tags bochs-emulator
Tag
Cancel
diff --git a/tags/bochs-os-x/index.html b/tags/bochs-os-x/index.html new file mode 100644 index 00000000..730eb967 --- /dev/null +++ b/tags/bochs-os-x/index.html @@ -0,0 +1 @@ + bochs-os-x | Rayanfam Blog
Home Tags bochs-os-x
Tag
Cancel
diff --git a/tags/bochs-windows/index.html b/tags/bochs-windows/index.html new file mode 100644 index 00000000..b2badb42 --- /dev/null +++ b/tags/bochs-windows/index.html @@ -0,0 +1 @@ + bochs-windows | Rayanfam Blog
Home Tags bochs-windows
Tag
Cancel
diff --git a/tags/build-tool-for-pin/index.html b/tags/build-tool-for-pin/index.html new file mode 100644 index 00000000..3b4563b9 --- /dev/null +++ b/tags/build-tool-for-pin/index.html @@ -0,0 +1 @@ + build-tool-for-pin | Rayanfam Blog
Home Tags build-tool-for-pin
Tag
Cancel
diff --git a/tags/building-vmcs/index.html b/tags/building-vmcs/index.html new file mode 100644 index 00000000..6465df7a --- /dev/null +++ b/tags/building-vmcs/index.html @@ -0,0 +1 @@ + building-vmcs | Rayanfam Blog
Home Tags building-vmcs
Tag
Cancel
diff --git a/tags/bypass-anti-vm/index.html b/tags/bypass-anti-vm/index.html new file mode 100644 index 00000000..44d93388 --- /dev/null +++ b/tags/bypass-anti-vm/index.html @@ -0,0 +1 @@ + bypass-anti-vm | Rayanfam Blog
Home Tags bypass-anti-vm
Tag
Cancel
diff --git a/tags/bypass-virtual-machine-detection/index.html b/tags/bypass-virtual-machine-detection/index.html new file mode 100644 index 00000000..38975c29 --- /dev/null +++ b/tags/bypass-virtual-machine-detection/index.html @@ -0,0 +1 @@ + bypass-virtual-machine-detection | Rayanfam Blog
Home Tags bypass-virtual-machine-detection
Tag
Cancel
diff --git a/tags/c-plugin/index.html b/tags/c-plugin/index.html new file mode 100644 index 00000000..e7e5219a --- /dev/null +++ b/tags/c-plugin/index.html @@ -0,0 +1 @@ + c-plugin | Rayanfam Blog
Home Tags c-plugin
Tag
Cancel
diff --git a/tags/c-reflection/index.html b/tags/c-reflection/index.html new file mode 100644 index 00000000..1c96e636 --- /dev/null +++ b/tags/c-reflection/index.html @@ -0,0 +1 @@ + c-reflection | Rayanfam Blog
Home Tags c-reflection
Tag
Cancel
diff --git a/tags/c/index.html b/tags/c/index.html new file mode 100644 index 00000000..80c95df4 --- /dev/null +++ b/tags/c/index.html @@ -0,0 +1 @@ + c | Rayanfam Blog
Home Tags c
Tag
Cancel
diff --git a/tags/cache/index.html b/tags/cache/index.html new file mode 100644 index 00000000..a6358833 --- /dev/null +++ b/tags/cache/index.html @@ -0,0 +1 @@ + cache | Rayanfam Blog
Home Tags cache
Tag
Cancel
diff --git a/tags/call-gate-descriptor/index.html b/tags/call-gate-descriptor/index.html new file mode 100644 index 00000000..8362175e --- /dev/null +++ b/tags/call-gate-descriptor/index.html @@ -0,0 +1 @@ + call-gate-descriptor | Rayanfam Blog
Home Tags call-gate-descriptor
Tag
Cancel
diff --git a/tags/call-gate/index.html b/tags/call-gate/index.html new file mode 100644 index 00000000..bacef1cc --- /dev/null +++ b/tags/call-gate/index.html @@ -0,0 +1 @@ + call-gate | Rayanfam Blog
Home Tags call-gate
Tag
Cancel
diff --git a/tags/captive-portal/index.html b/tags/captive-portal/index.html new file mode 100644 index 00000000..1291d5c7 --- /dev/null +++ b/tags/captive-portal/index.html @@ -0,0 +1 @@ + captive-portal | Rayanfam Blog
Home Tags captive-portal
Tag
Cancel
diff --git a/tags/captive/index.html b/tags/captive/index.html new file mode 100644 index 00000000..ffcac03a --- /dev/null +++ b/tags/captive/index.html @@ -0,0 +1 @@ + captive | Rayanfam Blog
Home Tags captive
Tag
Cancel
diff --git a/tags/certificate-services/index.html b/tags/certificate-services/index.html new file mode 100644 index 00000000..9f78b3e7 --- /dev/null +++ b/tags/certificate-services/index.html @@ -0,0 +1 @@ + certificate-services | Rayanfam Blog
Home Tags certificate-services
Tag
Cancel
diff --git a/tags/certificate-template/index.html b/tags/certificate-template/index.html new file mode 100644 index 00000000..7a3efb4d --- /dev/null +++ b/tags/certificate-template/index.html @@ -0,0 +1 @@ + certificate-template | Rayanfam Blog
Home Tags certificate-template
Tag
Cancel
diff --git a/tags/certificate/index.html b/tags/certificate/index.html new file mode 100644 index 00000000..71538a55 --- /dev/null +++ b/tags/certificate/index.html @@ -0,0 +1 @@ + certificate | Rayanfam Blog
Home Tags certificate
Tag
Cancel
diff --git a/tags/certificates/index.html b/tags/certificates/index.html new file mode 100644 index 00000000..d22401a3 --- /dev/null +++ b/tags/certificates/index.html @@ -0,0 +1 @@ + certificates | Rayanfam Blog
Home Tags certificates
Tag
Cancel
diff --git a/tags/change-adndroid-permission/index.html b/tags/change-adndroid-permission/index.html new file mode 100644 index 00000000..1e710bb4 --- /dev/null +++ b/tags/change-adndroid-permission/index.html @@ -0,0 +1 @@ + change-adndroid-permission | Rayanfam Blog
Home Tags change-adndroid-permission
Tag
Cancel
diff --git a/tags/change-cpu/index.html b/tags/change-cpu/index.html new file mode 100644 index 00000000..7d8c9aa7 --- /dev/null +++ b/tags/change-cpu/index.html @@ -0,0 +1 @@ + change-cpu | Rayanfam Blog
Home Tags change-cpu
Tag
Cancel
diff --git a/tags/change-cpuid-virtualbox/index.html b/tags/change-cpuid-virtualbox/index.html new file mode 100644 index 00000000..dec543f4 --- /dev/null +++ b/tags/change-cpuid-virtualbox/index.html @@ -0,0 +1 @@ + change-cpuid-virtualbox | Rayanfam Blog
Home Tags change-cpuid-virtualbox
Tag
Cancel
diff --git a/tags/change-cpuid-vmware/index.html b/tags/change-cpuid-vmware/index.html new file mode 100644 index 00000000..4335d900 --- /dev/null +++ b/tags/change-cpuid-vmware/index.html @@ -0,0 +1 @@ + change-cpuid-vmware | Rayanfam Blog
Home Tags change-cpuid-vmware
Tag
Cancel
diff --git a/tags/change-kernel/index.html b/tags/change-kernel/index.html new file mode 100644 index 00000000..1078107c --- /dev/null +++ b/tags/change-kernel/index.html @@ -0,0 +1 @@ + change-kernel | Rayanfam Blog
Home Tags change-kernel
Tag
Cancel
diff --git a/tags/change-process-from-kernel/index.html b/tags/change-process-from-kernel/index.html new file mode 100644 index 00000000..d384e480 --- /dev/null +++ b/tags/change-process-from-kernel/index.html @@ -0,0 +1 @@ + change-process-from-kernel | Rayanfam Blog
Home Tags change-process-from-kernel
Tag
Cancel
diff --git a/tags/change-virtual-address/index.html b/tags/change-virtual-address/index.html new file mode 100644 index 00000000..6851b449 --- /dev/null +++ b/tags/change-virtual-address/index.html @@ -0,0 +1 @@ + change-virtual-address | Rayanfam Blog
Home Tags change-virtual-address
Tag
Cancel
diff --git a/tags/changing-to-ring-1-or-2/index.html b/tags/changing-to-ring-1-or-2/index.html new file mode 100644 index 00000000..5d04983d --- /dev/null +++ b/tags/changing-to-ring-1-or-2/index.html @@ -0,0 +1 @@ + changing-to-ring-1-or-2 | Rayanfam Blog
Home Tags changing-to-ring-1-or-2
Tag
Cancel
diff --git a/tags/chisel/index.html b/tags/chisel/index.html new file mode 100644 index 00000000..b33b6d1e --- /dev/null +++ b/tags/chisel/index.html @@ -0,0 +1 @@ + chisel | Rayanfam Blog
Home Tags chisel
Tag
Cancel
diff --git a/tags/cisco/index.html b/tags/cisco/index.html new file mode 100644 index 00000000..dc9b6edb --- /dev/null +++ b/tags/cisco/index.html @@ -0,0 +1 @@ + cisco | Rayanfam Blog
Home Tags cisco
Tag
Cancel
diff --git a/tags/cmd/index.html b/tags/cmd/index.html new file mode 100644 index 00000000..be9a7755 --- /dev/null +++ b/tags/cmd/index.html @@ -0,0 +1 @@ + cmd | Rayanfam Blog
Home Tags cmd
Tag
Cancel
diff --git a/tags/compile-bochs/index.html b/tags/compile-bochs/index.html new file mode 100644 index 00000000..9cf1622f --- /dev/null +++ b/tags/compile-bochs/index.html @@ -0,0 +1 @@ + compile-bochs | Rayanfam Blog
Home Tags compile-bochs
Tag
Cancel
diff --git a/tags/compile-pin-tool/index.html b/tags/compile-pin-tool/index.html new file mode 100644 index 00000000..dd373c0d --- /dev/null +++ b/tags/compile-pin-tool/index.html @@ -0,0 +1 @@ + compile-pin-tool | Rayanfam Blog
Home Tags compile-pin-tool
Tag
Cancel
diff --git a/tags/config-bochs/index.html b/tags/config-bochs/index.html new file mode 100644 index 00000000..cc0c7c9a --- /dev/null +++ b/tags/config-bochs/index.html @@ -0,0 +1 @@ + config-bochs | Rayanfam Blog
Home Tags config-bochs
Tag
Cancel
diff --git a/tags/configuring-vmcs/index.html b/tags/configuring-vmcs/index.html new file mode 100644 index 00000000..319676a3 --- /dev/null +++ b/tags/configuring-vmcs/index.html @@ -0,0 +1 @@ + configuring-vmcs | Rayanfam Blog
Home Tags configuring-vmcs
Tag
Cancel
diff --git a/tags/control-registers-modification/index.html b/tags/control-registers-modification/index.html new file mode 100644 index 00000000..635a425a --- /dev/null +++ b/tags/control-registers-modification/index.html @@ -0,0 +1 @@ + control-registers-modification | Rayanfam Blog
Home Tags control-registers-modification
Tag
Cancel
diff --git a/tags/covachilli/index.html b/tags/covachilli/index.html new file mode 100644 index 00000000..5e8f5280 --- /dev/null +++ b/tags/covachilli/index.html @@ -0,0 +1 @@ + covachilli | Rayanfam Blog
Home Tags covachilli
Tag
Cancel
diff --git a/tags/cpu-registers/index.html b/tags/cpu-registers/index.html new file mode 100644 index 00000000..229d9010 --- /dev/null +++ b/tags/cpu-registers/index.html @@ -0,0 +1 @@ + cpu-registers | Rayanfam Blog
Home Tags cpu-registers
Tag
Cancel
diff --git a/tags/cpuid-modification/index.html b/tags/cpuid-modification/index.html new file mode 100644 index 00000000..4c2a080e --- /dev/null +++ b/tags/cpuid-modification/index.html @@ -0,0 +1 @@ + cpuid-modification | Rayanfam Blog
Home Tags cpuid-modification
Tag
Cancel
diff --git a/tags/create-a-virtual-machine/index.html b/tags/create-a-virtual-machine/index.html new file mode 100644 index 00000000..71fab4e3 --- /dev/null +++ b/tags/create-a-virtual-machine/index.html @@ -0,0 +1 @@ + create-a-virtual-machine | Rayanfam Blog
Home Tags create-a-virtual-machine
Tag
Cancel
diff --git a/tags/create-plugin/index.html b/tags/create-plugin/index.html new file mode 100644 index 00000000..41636aa0 --- /dev/null +++ b/tags/create-plugin/index.html @@ -0,0 +1 @@ + create-plugin | Rayanfam Blog
Home Tags create-plugin
Tag
Cancel
diff --git a/tags/creating-virtual-machine/index.html b/tags/creating-virtual-machine/index.html new file mode 100644 index 00000000..13fff7d5 --- /dev/null +++ b/tags/creating-virtual-machine/index.html @@ -0,0 +1 @@ + creating-virtual-machine | Rayanfam Blog
Home Tags creating-virtual-machine
Tag
Cancel
diff --git a/tags/creating-vmm/index.html b/tags/creating-vmm/index.html new file mode 100644 index 00000000..3956868d --- /dev/null +++ b/tags/creating-vmm/index.html @@ -0,0 +1 @@ + creating-vmm | Rayanfam Blog
Home Tags creating-vmm
Tag
Cancel
diff --git a/tags/debate/index.html b/tags/debate/index.html new file mode 100644 index 00000000..d23dac98 --- /dev/null +++ b/tags/debate/index.html @@ -0,0 +1 @@ + debate | Rayanfam Blog
Home Tags debate
Tag
Cancel
diff --git a/tags/debian/index.html b/tags/debian/index.html new file mode 100644 index 00000000..9d78a6fd --- /dev/null +++ b/tags/debian/index.html @@ -0,0 +1 @@ + debian | Rayanfam Blog
Home Tags debian
Tag
Cancel
diff --git a/tags/debug-dynamic-loading/index.html b/tags/debug-dynamic-loading/index.html new file mode 100644 index 00000000..02f725b7 --- /dev/null +++ b/tags/debug-dynamic-loading/index.html @@ -0,0 +1 @@ + debug-dynamic-loading | Rayanfam Blog
Home Tags debug-dynamic-loading
Tag
Cancel
diff --git a/tags/debug-kernel-function/index.html b/tags/debug-kernel-function/index.html new file mode 100644 index 00000000..aaadc5d0 --- /dev/null +++ b/tags/debug-kernel-function/index.html @@ -0,0 +1 @@ + debug-kernel-function | Rayanfam Blog
Home Tags debug-kernel-function
Tag
Cancel
diff --git a/tags/debug-loadlibrary/index.html b/tags/debug-loadlibrary/index.html new file mode 100644 index 00000000..d4fe8bc8 --- /dev/null +++ b/tags/debug-loadlibrary/index.html @@ -0,0 +1 @@ + debug-loadlibrary | Rayanfam Blog
Home Tags debug-loadlibrary
Tag
Cancel
diff --git a/tags/debug-native-function/index.html b/tags/debug-native-function/index.html new file mode 100644 index 00000000..3f7dfc4b --- /dev/null +++ b/tags/debug-native-function/index.html @@ -0,0 +1 @@ + debug-native-function | Rayanfam Blog
Home Tags debug-native-function
Tag
Cancel
diff --git a/tags/debug-using-bochs/index.html b/tags/debug-using-bochs/index.html new file mode 100644 index 00000000..1ce2724a --- /dev/null +++ b/tags/debug-using-bochs/index.html @@ -0,0 +1 @@ + debug-using-bochs | Rayanfam Blog
Home Tags debug-using-bochs
Tag
Cancel
diff --git a/tags/debug-virtual-machine/index.html b/tags/debug-virtual-machine/index.html new file mode 100644 index 00000000..74402a41 --- /dev/null +++ b/tags/debug-virtual-machine/index.html @@ -0,0 +1 @@ + debug-virtual-machine | Rayanfam Blog
Home Tags debug-virtual-machine
Tag
Cancel
diff --git a/tags/debug-windows/index.html b/tags/debug-windows/index.html new file mode 100644 index 00000000..b0b79081 --- /dev/null +++ b/tags/debug-windows/index.html @@ -0,0 +1 @@ + debug-windows | Rayanfam Blog
Home Tags debug-windows
Tag
Cancel
diff --git a/tags/debugger/index.html b/tags/debugger/index.html new file mode 100644 index 00000000..dc78284d --- /dev/null +++ b/tags/debugger/index.html @@ -0,0 +1 @@ + debugger | Rayanfam Blog
Home Tags debugger
Tag
Cancel
diff --git a/tags/debugging-kernel-mode/index.html b/tags/debugging-kernel-mode/index.html new file mode 100644 index 00000000..c2875a56 --- /dev/null +++ b/tags/debugging-kernel-mode/index.html @@ -0,0 +1 @@ + debugging-kernel-mode | Rayanfam Blog
Home Tags debugging-kernel-mode
Tag
Cancel
diff --git a/tags/defeat-anti-vm/index.html b/tags/defeat-anti-vm/index.html new file mode 100644 index 00000000..593541b7 --- /dev/null +++ b/tags/defeat-anti-vm/index.html @@ -0,0 +1 @@ + defeat-anti-vm | Rayanfam Blog
Home Tags defeat-anti-vm
Tag
Cancel
diff --git a/tags/defense/index.html b/tags/defense/index.html new file mode 100644 index 00000000..5dee28ee --- /dev/null +++ b/tags/defense/index.html @@ -0,0 +1 @@ + defense | Rayanfam Blog
Home Tags defense
Tag
Cancel
diff --git a/tags/detect-64-bit-os-c/index.html b/tags/detect-64-bit-os-c/index.html new file mode 100644 index 00000000..3ba1cc2e --- /dev/null +++ b/tags/detect-64-bit-os-c/index.html @@ -0,0 +1 @@ + detect-64-bit-os-c | Rayanfam Blog
Home Tags detect-64-bit-os-c
Tag
Cancel
diff --git a/tags/developement/index.html b/tags/developement/index.html new file mode 100644 index 00000000..19208885 --- /dev/null +++ b/tags/developement/index.html @@ -0,0 +1 @@ + developement | Rayanfam Blog
Home Tags developement
Tag
Cancel
diff --git a/tags/developer/index.html b/tags/developer/index.html new file mode 100644 index 00000000..c457fac1 --- /dev/null +++ b/tags/developer/index.html @@ -0,0 +1 @@ + developer | Rayanfam Blog
Home Tags developer
Tag
Cancel
diff --git a/tags/directory-service/index.html b/tags/directory-service/index.html new file mode 100644 index 00000000..516b9bbd --- /dev/null +++ b/tags/directory-service/index.html @@ -0,0 +1 @@ + directory-service | Rayanfam Blog
Home Tags directory-service
Tag
Cancel
diff --git a/tags/dll-plugin/index.html b/tags/dll-plugin/index.html new file mode 100644 index 00000000..b3dd1dd4 --- /dev/null +++ b/tags/dll-plugin/index.html @@ -0,0 +1 @@ + dll-plugin | Rayanfam Blog
Home Tags dll-plugin
Tag
Cancel
diff --git a/tags/doamin/index.html b/tags/doamin/index.html new file mode 100644 index 00000000..3bbf4d82 --- /dev/null +++ b/tags/doamin/index.html @@ -0,0 +1 @@ + doamin | Rayanfam Blog
Home Tags doamin
Tag
Cancel
diff --git a/tags/dynamically-call-function/index.html b/tags/dynamically-call-function/index.html new file mode 100644 index 00000000..400ec28e --- /dev/null +++ b/tags/dynamically-call-function/index.html @@ -0,0 +1 @@ + dynamically-call-function | Rayanfam Blog
Home Tags dynamically-call-function
Tag
Cancel
diff --git a/tags/edit-windows-internals/index.html b/tags/edit-windows-internals/index.html new file mode 100644 index 00000000..37119329 --- /dev/null +++ b/tags/edit-windows-internals/index.html @@ -0,0 +1 @@ + edit-windows-internals | Rayanfam Blog
Home Tags edit-windows-internals
Tag
Cancel
diff --git a/tags/emulator-debugging/index.html b/tags/emulator-debugging/index.html new file mode 100644 index 00000000..1b4a63b8 --- /dev/null +++ b/tags/emulator-debugging/index.html @@ -0,0 +1 @@ + emulator-debugging | Rayanfam Blog
Home Tags emulator-debugging
Tag
Cancel
diff --git a/tags/ept-hook/index.html b/tags/ept-hook/index.html new file mode 100644 index 00000000..fd6589c5 --- /dev/null +++ b/tags/ept-hook/index.html @@ -0,0 +1 @@ + ept-hook | Rayanfam Blog
Home Tags ept-hook
Tag
Cancel
diff --git a/tags/ept-monitoring/index.html b/tags/ept-monitoring/index.html new file mode 100644 index 00000000..67a01cc1 --- /dev/null +++ b/tags/ept-monitoring/index.html @@ -0,0 +1 @@ + ept-monitoring | Rayanfam Blog
Home Tags ept-monitoring
Tag
Cancel
diff --git a/tags/ept-table/index.html b/tags/ept-table/index.html new file mode 100644 index 00000000..d64d7500 --- /dev/null +++ b/tags/ept-table/index.html @@ -0,0 +1 @@ + ept-table | Rayanfam Blog
Home Tags ept-table
Tag
Cancel
diff --git a/tags/ept/index.html b/tags/ept/index.html new file mode 100644 index 00000000..1d3e98b0 --- /dev/null +++ b/tags/ept/index.html @@ -0,0 +1 @@ + ept | Rayanfam Blog
Home Tags ept
Tag
Cancel
diff --git a/tags/eptp/index.html b/tags/eptp/index.html new file mode 100644 index 00000000..c68b3a7f --- /dev/null +++ b/tags/eptp/index.html @@ -0,0 +1 @@ + eptp | Rayanfam Blog
Home Tags eptp
Tag
Cancel
diff --git a/tags/event-injection/index.html b/tags/event-injection/index.html new file mode 100644 index 00000000..84418558 --- /dev/null +++ b/tags/event-injection/index.html @@ -0,0 +1 @@ + event-injection | Rayanfam Blog
Home Tags event-injection
Tag
Cancel
diff --git a/tags/extended-page-table-pointer/index.html b/tags/extended-page-table-pointer/index.html new file mode 100644 index 00000000..c16eb318 --- /dev/null +++ b/tags/extended-page-table-pointer/index.html @@ -0,0 +1 @@ + extended-page-table-pointer | Rayanfam Blog
Home Tags extended-page-table-pointer
Tag
Cancel
diff --git a/tags/extended-page-table/index.html b/tags/extended-page-table/index.html new file mode 100644 index 00000000..ec1ec56e --- /dev/null +++ b/tags/extended-page-table/index.html @@ -0,0 +1 @@ + extended-page-table | Rayanfam Blog
Home Tags extended-page-table
Tag
Cancel
diff --git a/tags/far-conditional-jump/index.html b/tags/far-conditional-jump/index.html new file mode 100644 index 00000000..8947ef42 --- /dev/null +++ b/tags/far-conditional-jump/index.html @@ -0,0 +1 @@ + far-conditional-jump | Rayanfam Blog
Home Tags far-conditional-jump
Tag
Cancel
diff --git a/tags/filesystem/index.html b/tags/filesystem/index.html new file mode 100644 index 00000000..0030a9e3 --- /dev/null +++ b/tags/filesystem/index.html @@ -0,0 +1 @@ + filesystem | Rayanfam Blog
Home Tags filesystem
Tag
Cancel
diff --git a/tags/firstthunk/index.html b/tags/firstthunk/index.html new file mode 100644 index 00000000..a2652f6f --- /dev/null +++ b/tags/firstthunk/index.html @@ -0,0 +1 @@ + firstthunk | Rayanfam Blog
Home Tags firstthunk
Tag
Cancel
diff --git a/tags/forwardchain/index.html b/tags/forwardchain/index.html new file mode 100644 index 00000000..7b2392dc --- /dev/null +++ b/tags/forwardchain/index.html @@ -0,0 +1 @@ + forwardchain | Rayanfam Blog
Home Tags forwardchain
Tag
Cancel
diff --git a/tags/freed-list/index.html b/tags/freed-list/index.html new file mode 100644 index 00000000..83f567ae --- /dev/null +++ b/tags/freed-list/index.html @@ -0,0 +1 @@ + freed-list | Rayanfam Blog
Home Tags freed-list
Tag
Cancel
diff --git a/tags/gdsii/index.html b/tags/gdsii/index.html new file mode 100644 index 00000000..85b4f74e --- /dev/null +++ b/tags/gdsii/index.html @@ -0,0 +1 @@ + GDSII | Rayanfam Blog
Home Tags GDSII
Tag
Cancel
diff --git a/tags/gdt/index.html b/tags/gdt/index.html new file mode 100644 index 00000000..724f3e29 --- /dev/null +++ b/tags/gdt/index.html @@ -0,0 +1 @@ + gdt | Rayanfam Blog
Home Tags gdt
Tag
Cancel
diff --git a/tags/gdtr-register/index.html b/tags/gdtr-register/index.html new file mode 100644 index 00000000..7e0c5aec --- /dev/null +++ b/tags/gdtr-register/index.html @@ -0,0 +1 @@ + gdtr-register | Rayanfam Blog
Home Tags gdtr-register
Tag
Cancel
diff --git a/tags/get-environment-os/index.html b/tags/get-environment-os/index.html new file mode 100644 index 00000000..b4453eb4 --- /dev/null +++ b/tags/get-environment-os/index.html @@ -0,0 +1 @@ + get-environment-os | Rayanfam Blog
Home Tags get-environment-os
Tag
Cancel
diff --git a/tags/get-native-api-windows/index.html b/tags/get-native-api-windows/index.html new file mode 100644 index 00000000..94ef0ee8 --- /dev/null +++ b/tags/get-native-api-windows/index.html @@ -0,0 +1 @@ + get-native-api-windows | Rayanfam Blog
Home Tags get-native-api-windows
Tag
Cancel
diff --git a/tags/getting-started-with-pykd/index.html b/tags/getting-started-with-pykd/index.html new file mode 100644 index 00000000..1ac27afe --- /dev/null +++ b/tags/getting-started-with-pykd/index.html @@ -0,0 +1 @@ + getting-started-with-pykd | Rayanfam Blog
Home Tags getting-started-with-pykd
Tag
Cancel
diff --git a/tags/global-descriptor-table/index.html b/tags/global-descriptor-table/index.html new file mode 100644 index 00000000..5d3fd688 --- /dev/null +++ b/tags/global-descriptor-table/index.html @@ -0,0 +1 @@ + global-descriptor-table | Rayanfam Blog
Home Tags global-descriptor-table
Tag
Cancel
diff --git a/tags/gre/index.html b/tags/gre/index.html new file mode 100644 index 00000000..1f55224f --- /dev/null +++ b/tags/gre/index.html @@ -0,0 +1 @@ + gre | Rayanfam Blog
Home Tags gre
Tag
Cancel
diff --git a/tags/gtkwave/index.html b/tags/gtkwave/index.html new file mode 100644 index 00000000..9385a8c2 --- /dev/null +++ b/tags/gtkwave/index.html @@ -0,0 +1 @@ + GTKWave | Rayanfam Blog
Home Tags GTKWave
Tag
Cancel
diff --git a/tags/guest-state-error/index.html b/tags/guest-state-error/index.html new file mode 100644 index 00000000..de292e89 --- /dev/null +++ b/tags/guest-state-error/index.html @@ -0,0 +1 @@ + guest-state-error | Rayanfam Blog
Home Tags guest-state-error
Tag
Cancel
diff --git a/tags/gui/index.html b/tags/gui/index.html new file mode 100644 index 00000000..7e020488 --- /dev/null +++ b/tags/gui/index.html @@ -0,0 +1 @@ + gui | Rayanfam Blog
Home Tags gui
Tag
Cancel
diff --git a/tags/handle-creation-in-windows/index.html b/tags/handle-creation-in-windows/index.html new file mode 100644 index 00000000..5385af24 --- /dev/null +++ b/tags/handle-creation-in-windows/index.html @@ -0,0 +1 @@ + handle-creation-in-windows | Rayanfam Blog
Home Tags handle-creation-in-windows
Tag
Cancel
diff --git a/tags/handle-table-windows/index.html b/tags/handle-table-windows/index.html new file mode 100644 index 00000000..191beb60 --- /dev/null +++ b/tags/handle-table-windows/index.html @@ -0,0 +1 @@ + handle-table-windows | Rayanfam Blog
Home Tags handle-table-windows
Tag
Cancel
diff --git a/tags/handles-in-windows/index.html b/tags/handles-in-windows/index.html new file mode 100644 index 00000000..a10a8724 --- /dev/null +++ b/tags/handles-in-windows/index.html @@ -0,0 +1 @@ + handles-in-windows | Rayanfam Blog
Home Tags handles-in-windows
Tag
Cancel
diff --git a/tags/hardware-design/index.html b/tags/hardware-design/index.html new file mode 100644 index 00000000..d1a01d33 --- /dev/null +++ b/tags/hardware-design/index.html @@ -0,0 +1 @@ + hardware-design | Rayanfam Blog
Home Tags hardware-design
Tag
Cancel
diff --git a/tags/hdl-languages/index.html b/tags/hdl-languages/index.html new file mode 100644 index 00000000..8f6c1635 --- /dev/null +++ b/tags/hdl-languages/index.html @@ -0,0 +1 @@ + HDL-languages | Rayanfam Blog
Home Tags HDL-languages
Tag
Cancel
diff --git a/tags/helloworld/index.html b/tags/helloworld/index.html new file mode 100644 index 00000000..257c54ea --- /dev/null +++ b/tags/helloworld/index.html @@ -0,0 +1 @@ + helloworld | Rayanfam Blog
Home Tags helloworld
Tag
Cancel
diff --git a/tags/hfs/index.html b/tags/hfs/index.html new file mode 100644 index 00000000..48a8b32e --- /dev/null +++ b/tags/hfs/index.html @@ -0,0 +1 @@ + hfs | Rayanfam Blog
Home Tags hfs
Tag
Cancel
diff --git a/tags/hidden-hook/index.html b/tags/hidden-hook/index.html new file mode 100644 index 00000000..edf81fa4 --- /dev/null +++ b/tags/hidden-hook/index.html @@ -0,0 +1 @@ + hidden-hook | Rayanfam Blog
Home Tags hidden-hook
Tag
Cancel
diff --git a/tags/high-irql-messaging/index.html b/tags/high-irql-messaging/index.html new file mode 100644 index 00000000..7005eeeb --- /dev/null +++ b/tags/high-irql-messaging/index.html @@ -0,0 +1 @@ + high-irql-messaging | Rayanfam Blog
Home Tags high-irql-messaging
Tag
Cancel
diff --git a/tags/hls/index.html b/tags/hls/index.html new file mode 100644 index 00000000..5dfb23e7 --- /dev/null +++ b/tags/hls/index.html @@ -0,0 +1 @@ + hls | Rayanfam Blog
Home Tags hls
Tag
Cancel
diff --git a/tags/hook-for-ransomware/index.html b/tags/hook-for-ransomware/index.html new file mode 100644 index 00000000..0e917376 --- /dev/null +++ b/tags/hook-for-ransomware/index.html @@ -0,0 +1 @@ + hook-for-ransomware | Rayanfam Blog
Home Tags hook-for-ransomware
Tag
Cancel
diff --git a/tags/host-state-error/index.html b/tags/host-state-error/index.html new file mode 100644 index 00000000..c68102c9 --- /dev/null +++ b/tags/host-state-error/index.html @@ -0,0 +1 @@ + host-state-error | Rayanfam Blog
Home Tags host-state-error
Tag
Cancel
diff --git a/tags/how-openprocess-works/index.html b/tags/how-openprocess-works/index.html new file mode 100644 index 00000000..49e261a3 --- /dev/null +++ b/tags/how-openprocess-works/index.html @@ -0,0 +1 @@ + how-openprocess-works | Rayanfam Blog
Home Tags how-openprocess-works
Tag
Cancel
diff --git a/tags/how-to-create-virtual-machine/index.html b/tags/how-to-create-virtual-machine/index.html new file mode 100644 index 00000000..18ac121e --- /dev/null +++ b/tags/how-to-create-virtual-machine/index.html @@ -0,0 +1 @@ + how-to-create-virtual-machine | Rayanfam Blog
Home Tags how-to-create-virtual-machine
Tag
Cancel
diff --git a/tags/how-to/index.html b/tags/how-to/index.html new file mode 100644 index 00000000..5be76dd8 --- /dev/null +++ b/tags/how-to/index.html @@ -0,0 +1 @@ + how-to | Rayanfam Blog
Home Tags how-to
Tag
Cancel
diff --git a/tags/http/index.html b/tags/http/index.html new file mode 100644 index 00000000..aceadfae --- /dev/null +++ b/tags/http/index.html @@ -0,0 +1 @@ + http | Rayanfam Blog
Home Tags http
Tag
Cancel
diff --git a/tags/https/index.html b/tags/https/index.html new file mode 100644 index 00000000..50721f60 --- /dev/null +++ b/tags/https/index.html @@ -0,0 +1 @@ + https | Rayanfam Blog
Home Tags https
Tag
Cancel
diff --git a/tags/hyper-v-compatible/index.html b/tags/hyper-v-compatible/index.html new file mode 100644 index 00000000..bc383431 --- /dev/null +++ b/tags/hyper-v-compatible/index.html @@ -0,0 +1 @@ + hyper-v-compatible | Rayanfam Blog
Home Tags hyper-v-compatible
Tag
Cancel
diff --git a/tags/hyperdbg-principles/index.html b/tags/hyperdbg-principles/index.html new file mode 100644 index 00000000..4ffb0fc3 --- /dev/null +++ b/tags/hyperdbg-principles/index.html @@ -0,0 +1 @@ + hyperdbg-principles | Rayanfam Blog
Home Tags hyperdbg-principles
Tag
Cancel
diff --git a/tags/hyperdbg-vs-windbg/index.html b/tags/hyperdbg-vs-windbg/index.html new file mode 100644 index 00000000..7a4e2e80 --- /dev/null +++ b/tags/hyperdbg-vs-windbg/index.html @@ -0,0 +1 @@ + hyperdbg-vs-windbg | Rayanfam Blog
Home Tags hyperdbg-vs-windbg
Tag
Cancel
diff --git a/tags/hyperdbg/index.html b/tags/hyperdbg/index.html new file mode 100644 index 00000000..84894031 --- /dev/null +++ b/tags/hyperdbg/index.html @@ -0,0 +1 @@ + hyperdbg | Rayanfam Blog
Home Tags hyperdbg
Tag
Cancel
diff --git a/tags/hypervisor-from-scratch/index.html b/tags/hypervisor-from-scratch/index.html new file mode 100644 index 00000000..8002c740 --- /dev/null +++ b/tags/hypervisor-from-scratch/index.html @@ -0,0 +1 @@ + hypervisor-from-scratch | Rayanfam Blog
Home Tags hypervisor-from-scratch
Tag
Cancel
diff --git a/tags/hypervisor-fundamentals/index.html b/tags/hypervisor-fundamentals/index.html new file mode 100644 index 00000000..06127d7f --- /dev/null +++ b/tags/hypervisor-fundamentals/index.html @@ -0,0 +1 @@ + hypervisor-fundamentals | Rayanfam Blog
Home Tags hypervisor-fundamentals
Tag
Cancel
diff --git a/tags/hypervisor-logging/index.html b/tags/hypervisor-logging/index.html new file mode 100644 index 00000000..f84f21ad --- /dev/null +++ b/tags/hypervisor-logging/index.html @@ -0,0 +1 @@ + hypervisor-logging | Rayanfam Blog
Home Tags hypervisor-logging
Tag
Cancel
diff --git a/tags/hypervisor-on-hyper-v/index.html b/tags/hypervisor-on-hyper-v/index.html new file mode 100644 index 00000000..83d7db7b --- /dev/null +++ b/tags/hypervisor-on-hyper-v/index.html @@ -0,0 +1 @@ + hypervisor-on-hyper-v | Rayanfam Blog
Home Tags hypervisor-on-hyper-v
Tag
Cancel
diff --git a/tags/hypervisor-paging/index.html b/tags/hypervisor-paging/index.html new file mode 100644 index 00000000..2783162a --- /dev/null +++ b/tags/hypervisor-paging/index.html @@ -0,0 +1 @@ + hypervisor-paging | Rayanfam Blog
Home Tags hypervisor-paging
Tag
Cancel
diff --git a/tags/hypervisor-part-8/index.html b/tags/hypervisor-part-8/index.html new file mode 100644 index 00000000..774d52fd --- /dev/null +++ b/tags/hypervisor-part-8/index.html @@ -0,0 +1 @@ + hypervisor-part-8 | Rayanfam Blog
Home Tags hypervisor-part-8
Tag
Cancel
diff --git a/tags/hypervisor-tutorial-part-7/index.html b/tags/hypervisor-tutorial-part-7/index.html new file mode 100644 index 00000000..00243f44 --- /dev/null +++ b/tags/hypervisor-tutorial-part-7/index.html @@ -0,0 +1 @@ + hypervisor-tutorial-part-7 | Rayanfam Blog
Home Tags hypervisor-tutorial-part-7
Tag
Cancel
diff --git a/tags/hypervisor-tutorial/index.html b/tags/hypervisor-tutorial/index.html new file mode 100644 index 00000000..e1bdda7b --- /dev/null +++ b/tags/hypervisor-tutorial/index.html @@ -0,0 +1 @@ + hypervisor-tutorial | Rayanfam Blog
Home Tags hypervisor-tutorial
Tag
Cancel
diff --git a/tags/hypervisor-tutorials/index.html b/tags/hypervisor-tutorials/index.html new file mode 100644 index 00000000..65ed0560 --- /dev/null +++ b/tags/hypervisor-tutorials/index.html @@ -0,0 +1 @@ + hypervisor-tutorials | Rayanfam Blog
Home Tags hypervisor-tutorials
Tag
Cancel
diff --git a/tags/hypervisor/index.html b/tags/hypervisor/index.html new file mode 100644 index 00000000..6f05dfd9 --- /dev/null +++ b/tags/hypervisor/index.html @@ -0,0 +1 @@ + hypervisor | Rayanfam Blog
Home Tags hypervisor
Tag
Cancel
diff --git a/tags/iat/index.html b/tags/iat/index.html new file mode 100644 index 00000000..f7d36fd2 --- /dev/null +++ b/tags/iat/index.html @@ -0,0 +1 @@ + iat | Rayanfam Blog
Home Tags iat
Tag
Cancel
diff --git a/tags/ida-python-script/index.html b/tags/ida-python-script/index.html new file mode 100644 index 00000000..b69496cf --- /dev/null +++ b/tags/ida-python-script/index.html @@ -0,0 +1 @@ + ida-python-script | Rayanfam Blog
Home Tags ida-python-script
Tag
Cancel
diff --git a/tags/idt/index.html b/tags/idt/index.html new file mode 100644 index 00000000..c217f9f3 --- /dev/null +++ b/tags/idt/index.html @@ -0,0 +1 @@ + idt | Rayanfam Blog
Home Tags idt
Tag
Cancel
diff --git a/tags/image-import-descriptor/index.html b/tags/image-import-descriptor/index.html new file mode 100644 index 00000000..693acc81 --- /dev/null +++ b/tags/image-import-descriptor/index.html @@ -0,0 +1 @@ + image_import_descriptor | Rayanfam Blog
Home Tags image_import_descriptor
Tag
Cancel
diff --git a/tags/import-address-table-address/index.html b/tags/import-address-table-address/index.html new file mode 100644 index 00000000..e337e175 --- /dev/null +++ b/tags/import-address-table-address/index.html @@ -0,0 +1 @@ + import-address-table-address | Rayanfam Blog
Home Tags import-address-table-address
Tag
Cancel
diff --git a/tags/index.html b/tags/index.html new file mode 100644 index 00000000..4ed6d178 --- /dev/null +++ b/tags/index.html @@ -0,0 +1 @@ + Tags | Rayanfam Blog
Home Tags
Tags
Cancel

Tags

diff --git a/tags/init/index.html b/tags/init/index.html new file mode 100644 index 00000000..c2972a8d --- /dev/null +++ b/tags/init/index.html @@ -0,0 +1 @@ + init | Rayanfam Blog
Home Tags init
Tag
Cancel
diff --git a/tags/initiating-vmx-operation/index.html b/tags/initiating-vmx-operation/index.html new file mode 100644 index 00000000..50507bb3 --- /dev/null +++ b/tags/initiating-vmx-operation/index.html @@ -0,0 +1 @@ + initiating-vmx-operation | Rayanfam Blog
Home Tags initiating-vmx-operation
Tag
Cancel
diff --git a/tags/inject-interrupt/index.html b/tags/inject-interrupt/index.html new file mode 100644 index 00000000..77278dd8 --- /dev/null +++ b/tags/inject-interrupt/index.html @@ -0,0 +1 @@ + inject-interrupt | Rayanfam Blog
Home Tags inject-interrupt
Tag
Cancel
diff --git a/tags/insmod/index.html b/tags/insmod/index.html new file mode 100644 index 00000000..5625753a --- /dev/null +++ b/tags/insmod/index.html @@ -0,0 +1 @@ + insmod | Rayanfam Blog
Home Tags insmod
Tag
Cancel
diff --git a/tags/install-bochs/index.html b/tags/install-bochs/index.html new file mode 100644 index 00000000..d1a04066 --- /dev/null +++ b/tags/install-bochs/index.html @@ -0,0 +1 @@ + install-bochs | Rayanfam Blog
Home Tags install-bochs
Tag
Cancel
diff --git a/tags/install-windows-on-bochs/index.html b/tags/install-windows-on-bochs/index.html new file mode 100644 index 00000000..f4a146b0 --- /dev/null +++ b/tags/install-windows-on-bochs/index.html @@ -0,0 +1 @@ + install-windows-on-bochs | Rayanfam Blog
Home Tags install-windows-on-bochs
Tag
Cancel
diff --git a/tags/instrument-instructions-using-bochs/index.html b/tags/instrument-instructions-using-bochs/index.html new file mode 100644 index 00000000..3b6a12e3 --- /dev/null +++ b/tags/instrument-instructions-using-bochs/index.html @@ -0,0 +1 @@ + instrument-instructions-using-bochs | Rayanfam Blog
Home Tags instrument-instructions-using-bochs
Tag
Cancel
diff --git a/tags/instrument-kernel-mode/index.html b/tags/instrument-kernel-mode/index.html new file mode 100644 index 00000000..31936a8c --- /dev/null +++ b/tags/instrument-kernel-mode/index.html @@ -0,0 +1 @@ + instrument-kernel-mode | Rayanfam Blog
Home Tags instrument-kernel-mode
Tag
Cancel
diff --git a/tags/instrument-linux/index.html b/tags/instrument-linux/index.html new file mode 100644 index 00000000..cfd56eeb --- /dev/null +++ b/tags/instrument-linux/index.html @@ -0,0 +1 @@ + instrument-linux | Rayanfam Blog
Home Tags instrument-linux
Tag
Cancel
diff --git a/tags/instrument-operating-system/index.html b/tags/instrument-operating-system/index.html new file mode 100644 index 00000000..0931253a --- /dev/null +++ b/tags/instrument-operating-system/index.html @@ -0,0 +1 @@ + instrument-operating-system | Rayanfam Blog
Home Tags instrument-operating-system
Tag
Cancel
diff --git a/tags/instrument-os-x/index.html b/tags/instrument-os-x/index.html new file mode 100644 index 00000000..817feb5a --- /dev/null +++ b/tags/instrument-os-x/index.html @@ -0,0 +1 @@ + instrument-os-x | Rayanfam Blog
Home Tags instrument-os-x
Tag
Cancel
diff --git a/tags/instrument-windows/index.html b/tags/instrument-windows/index.html new file mode 100644 index 00000000..b6cab3f7 --- /dev/null +++ b/tags/instrument-windows/index.html @@ -0,0 +1 @@ + instrument-windows | Rayanfam Blog
Home Tags instrument-windows
Tag
Cancel
diff --git a/tags/intel-mon-feature/index.html b/tags/intel-mon-feature/index.html new file mode 100644 index 00000000..71cfefc8 --- /dev/null +++ b/tags/intel-mon-feature/index.html @@ -0,0 +1 @@ + intel-mon-feature | Rayanfam Blog
Home Tags intel-mon-feature
Tag
Cancel
diff --git a/tags/intel-pin/index.html b/tags/intel-pin/index.html new file mode 100644 index 00000000..f860823d --- /dev/null +++ b/tags/intel-pin/index.html @@ -0,0 +1 @@ + intel-pin | Rayanfam Blog
Home Tags intel-pin
Tag
Cancel
diff --git a/tags/intel-streaming-simd-extensions-3/index.html b/tags/intel-streaming-simd-extensions-3/index.html new file mode 100644 index 00000000..5e731eeb --- /dev/null +++ b/tags/intel-streaming-simd-extensions-3/index.html @@ -0,0 +1 @@ + intel-streaming-simd-extensions-3 | Rayanfam Blog
Home Tags intel-streaming-simd-extensions-3
Tag
Cancel
diff --git a/tags/intel-synchronization-agent/index.html b/tags/intel-synchronization-agent/index.html new file mode 100644 index 00000000..0d027b81 --- /dev/null +++ b/tags/intel-synchronization-agent/index.html @@ -0,0 +1 @@ + intel-synchronization-agent | Rayanfam Blog
Home Tags intel-synchronization-agent
Tag
Cancel
diff --git a/tags/intel-virtualization/index.html b/tags/intel-virtualization/index.html new file mode 100644 index 00000000..39732d80 --- /dev/null +++ b/tags/intel-virtualization/index.html @@ -0,0 +1 @@ + intel-virtualization | Rayanfam Blog
Home Tags intel-virtualization
Tag
Cancel
diff --git a/tags/intel-vmx/index.html b/tags/intel-vmx/index.html new file mode 100644 index 00000000..bf0556e9 --- /dev/null +++ b/tags/intel-vmx/index.html @@ -0,0 +1 @@ + intel-vmx | Rayanfam Blog
Home Tags intel-vmx
Tag
Cancel
diff --git a/tags/intel-vt-x-paging/index.html b/tags/intel-vt-x-paging/index.html new file mode 100644 index 00000000..77efcd3c --- /dev/null +++ b/tags/intel-vt-x-paging/index.html @@ -0,0 +1 @@ + intel-vt-x-paging | Rayanfam Blog
Home Tags intel-vt-x-paging
Tag
Cancel
diff --git a/tags/intel-vt-x-tutorial/index.html b/tags/intel-vt-x-tutorial/index.html new file mode 100644 index 00000000..2789fbdc --- /dev/null +++ b/tags/intel-vt-x-tutorial/index.html @@ -0,0 +1 @@ + intel-vt-x-tutorial | Rayanfam Blog
Home Tags intel-vt-x-tutorial
Tag
Cancel
diff --git a/tags/intel-vt-x/index.html b/tags/intel-vt-x/index.html new file mode 100644 index 00000000..341b59b1 --- /dev/null +++ b/tags/intel-vt-x/index.html @@ -0,0 +1 @@ + intel-vt-x | Rayanfam Blog
Home Tags intel-vt-x
Tag
Cancel
diff --git a/tags/intel-vtx-tutorial/index.html b/tags/intel-vtx-tutorial/index.html new file mode 100644 index 00000000..310a0b55 --- /dev/null +++ b/tags/intel-vtx-tutorial/index.html @@ -0,0 +1 @@ + intel-vtx-tutorial | Rayanfam Blog
Home Tags intel-vtx-tutorial
Tag
Cancel
diff --git a/tags/interrupt-descriptor-table/index.html b/tags/interrupt-descriptor-table/index.html new file mode 100644 index 00000000..266dacb1 --- /dev/null +++ b/tags/interrupt-descriptor-table/index.html @@ -0,0 +1 @@ + interrupt-descriptor-table | Rayanfam Blog
Home Tags interrupt-descriptor-table
Tag
Cancel
diff --git a/tags/invalidate-ept/index.html b/tags/invalidate-ept/index.html new file mode 100644 index 00000000..aae59f49 --- /dev/null +++ b/tags/invalidate-ept/index.html @@ -0,0 +1 @@ + invalidate-ept | Rayanfam Blog
Home Tags invalidate-ept
Tag
Cancel
diff --git a/tags/invept/index.html b/tags/invept/index.html new file mode 100644 index 00000000..47ccecae --- /dev/null +++ b/tags/invept/index.html @@ -0,0 +1 @@ + invept | Rayanfam Blog
Home Tags invept
Tag
Cancel
diff --git a/tags/invoke-function/index.html b/tags/invoke-function/index.html new file mode 100644 index 00000000..f2642127 --- /dev/null +++ b/tags/invoke-function/index.html @@ -0,0 +1 @@ + invoke-function | Rayanfam Blog
Home Tags invoke-function
Tag
Cancel
diff --git a/tags/invpcid/index.html b/tags/invpcid/index.html new file mode 100644 index 00000000..8252b499 --- /dev/null +++ b/tags/invpcid/index.html @@ -0,0 +1 @@ + invpcid | Rayanfam Blog
Home Tags invpcid
Tag
Cancel
diff --git a/tags/invvpid/index.html b/tags/invvpid/index.html new file mode 100644 index 00000000..fbb39f49 --- /dev/null +++ b/tags/invvpid/index.html @@ -0,0 +1 @@ + invvpid | Rayanfam Blog
Home Tags invvpid
Tag
Cancel
diff --git a/tags/ios/index.html b/tags/ios/index.html new file mode 100644 index 00000000..3147430e --- /dev/null +++ b/tags/ios/index.html @@ -0,0 +1 @@ + ios | Rayanfam Blog
Home Tags ios
Tag
Cancel
diff --git a/tags/ipsec/index.html b/tags/ipsec/index.html new file mode 100644 index 00000000..abb5da92 --- /dev/null +++ b/tags/ipsec/index.html @@ -0,0 +1 @@ + ipsec | Rayanfam Blog
Home Tags ipsec
Tag
Cancel
diff --git a/tags/iptables/index.html b/tags/iptables/index.html new file mode 100644 index 00000000..b373aba9 --- /dev/null +++ b/tags/iptables/index.html @@ -0,0 +1 @@ + iptables | Rayanfam Blog
Home Tags iptables
Tag
Cancel
diff --git a/tags/irp-mj-device-control/index.html b/tags/irp-mj-device-control/index.html new file mode 100644 index 00000000..fd040601 --- /dev/null +++ b/tags/irp-mj-device-control/index.html @@ -0,0 +1 @@ + irp_mj_device_control | Rayanfam Blog
Home Tags irp_mj_device_control
Tag
Cancel
diff --git a/tags/jump-without-register/index.html b/tags/jump-without-register/index.html new file mode 100644 index 00000000..c825413e --- /dev/null +++ b/tags/jump-without-register/index.html @@ -0,0 +1 @@ + jump-without-register | Rayanfam Blog
Home Tags jump-without-register
Tag
Cancel
diff --git a/tags/kernel-mode-to-user-mode/index.html b/tags/kernel-mode-to-user-mode/index.html new file mode 100644 index 00000000..79642f27 --- /dev/null +++ b/tags/kernel-mode-to-user-mode/index.html @@ -0,0 +1 @@ + kernel-mode-to-user-mode | Rayanfam Blog
Home Tags kernel-mode-to-user-mode
Tag
Cancel
diff --git a/tags/kernel-mode/index.html b/tags/kernel-mode/index.html new file mode 100644 index 00000000..7e9819e8 --- /dev/null +++ b/tags/kernel-mode/index.html @@ -0,0 +1 @@ + kernel-mode | Rayanfam Blog
Home Tags kernel-mode
Tag
Cancel
diff --git a/tags/kernel/index.html b/tags/kernel/index.html new file mode 100644 index 00000000..3d9668f0 --- /dev/null +++ b/tags/kernel/index.html @@ -0,0 +1 @@ + kernel | Rayanfam Blog
Home Tags kernel
Tag
Cancel
diff --git a/tags/kernelmode-to-usermode/index.html b/tags/kernelmode-to-usermode/index.html new file mode 100644 index 00000000..4f58d887 --- /dev/null +++ b/tags/kernelmode-to-usermode/index.html @@ -0,0 +1 @@ + kernelmode-to-usermode | Rayanfam Blog
Home Tags kernelmode-to-usermode
Tag
Cancel
diff --git a/tags/ko/index.html b/tags/ko/index.html new file mode 100644 index 00000000..3ac257c7 --- /dev/null +++ b/tags/ko/index.html @@ -0,0 +1 @@ + ko | Rayanfam Blog
Home Tags ko
Tag
Cancel
diff --git a/tags/ldap/index.html b/tags/ldap/index.html new file mode 100644 index 00000000..56dcfb12 --- /dev/null +++ b/tags/ldap/index.html @@ -0,0 +1 @@ + ldap | Rayanfam Blog
Home Tags ldap
Tag
Cancel
diff --git a/tags/ldtr-register/index.html b/tags/ldtr-register/index.html new file mode 100644 index 00000000..cb62cd83 --- /dev/null +++ b/tags/ldtr-register/index.html @@ -0,0 +1 @@ + ldtr-register | Rayanfam Blog
Home Tags ldtr-register
Tag
Cancel
diff --git a/tags/linux/index.html b/tags/linux/index.html new file mode 100644 index 00000000..2aed3a4d --- /dev/null +++ b/tags/linux/index.html @@ -0,0 +1 @@ + linux | Rayanfam Blog
Home Tags linux
Tag
Cancel
diff --git a/tags/lkm/index.html b/tags/lkm/index.html new file mode 100644 index 00000000..501e33c9 --- /dev/null +++ b/tags/lkm/index.html @@ -0,0 +1 @@ + lkm | Rayanfam Blog
Home Tags lkm
Tag
Cancel
diff --git a/tags/loadbalance/index.html b/tags/loadbalance/index.html new file mode 100644 index 00000000..ad014c36 --- /dev/null +++ b/tags/loadbalance/index.html @@ -0,0 +1 @@ + loadbalance | Rayanfam Blog
Home Tags loadbalance
Tag
Cancel
diff --git a/tags/lsmod/index.html b/tags/lsmod/index.html new file mode 100644 index 00000000..62dba906 --- /dev/null +++ b/tags/lsmod/index.html @@ -0,0 +1 @@ + lsmod | Rayanfam Blog
Home Tags lsmod
Tag
Cancel
diff --git a/tags/lua/index.html b/tags/lua/index.html new file mode 100644 index 00000000..3c373546 --- /dev/null +++ b/tags/lua/index.html @@ -0,0 +1 @@ + lua | Rayanfam Blog
Home Tags lua
Tag
Cancel
diff --git a/tags/lunatik/index.html b/tags/lunatik/index.html new file mode 100644 index 00000000..2b050359 --- /dev/null +++ b/tags/lunatik/index.html @@ -0,0 +1 @@ + lunatik | Rayanfam Blog
Home Tags lunatik
Tag
Cancel
diff --git a/tags/make/index.html b/tags/make/index.html new file mode 100644 index 00000000..a2b3e1b5 --- /dev/null +++ b/tags/make/index.html @@ -0,0 +1 @@ + make | Rayanfam Blog
Home Tags make
Tag
Cancel
diff --git a/tags/malware-analysis/index.html b/tags/malware-analysis/index.html new file mode 100644 index 00000000..71c4525d --- /dev/null +++ b/tags/malware-analysis/index.html @@ -0,0 +1 @@ + malware-analysis | Rayanfam Blog
Home Tags malware-analysis
Tag
Cancel
diff --git a/tags/management/index.html b/tags/management/index.html new file mode 100644 index 00000000..a0113d98 --- /dev/null +++ b/tags/management/index.html @@ -0,0 +1 @@ + management | Rayanfam Blog
Home Tags management
Tag
Cancel
diff --git a/tags/memory-type-range-register/index.html b/tags/memory-type-range-register/index.html new file mode 100644 index 00000000..1bab3415 --- /dev/null +++ b/tags/memory-type-range-register/index.html @@ -0,0 +1 @@ + memory-type-range-register | Rayanfam Blog
Home Tags memory-type-range-register
Tag
Cancel
diff --git a/tags/method-buffered/index.html b/tags/method-buffered/index.html new file mode 100644 index 00000000..6fc701eb --- /dev/null +++ b/tags/method-buffered/index.html @@ -0,0 +1 @@ + method_buffered | Rayanfam Blog
Home Tags method_buffered
Tag
Cancel
diff --git a/tags/method-in-direct/index.html b/tags/method-in-direct/index.html new file mode 100644 index 00000000..f38b4c02 --- /dev/null +++ b/tags/method-in-direct/index.html @@ -0,0 +1 @@ + method_in_direct | Rayanfam Blog
Home Tags method_in_direct
Tag
Cancel
diff --git a/tags/method-niether/index.html b/tags/method-niether/index.html new file mode 100644 index 00000000..46fecaf1 --- /dev/null +++ b/tags/method-niether/index.html @@ -0,0 +1 @@ + method_niether | Rayanfam Blog
Home Tags method_niether
Tag
Cancel
diff --git a/tags/method-out-direct/index.html b/tags/method-out-direct/index.html new file mode 100644 index 00000000..94929946 --- /dev/null +++ b/tags/method-out-direct/index.html @@ -0,0 +1 @@ + method_out_direct | Rayanfam Blog
Home Tags method_out_direct
Tag
Cancel
diff --git a/tags/microsoft/index.html b/tags/microsoft/index.html new file mode 100644 index 00000000..4c976de7 --- /dev/null +++ b/tags/microsoft/index.html @@ -0,0 +1 @@ + microsoft | Rayanfam Blog
Home Tags microsoft
Tag
Cancel
diff --git a/tags/migetphysicaladdress/index.html b/tags/migetphysicaladdress/index.html new file mode 100644 index 00000000..87bbd445 --- /dev/null +++ b/tags/migetphysicaladdress/index.html @@ -0,0 +1 @@ + migetphysicaladdress | Rayanfam Blog
Home Tags migetphysicaladdress
Tag
Cancel
diff --git a/tags/mivatopfn/index.html b/tags/mivatopfn/index.html new file mode 100644 index 00000000..4374c7b6 --- /dev/null +++ b/tags/mivatopfn/index.html @@ -0,0 +1 @@ + mivatopfn | Rayanfam Blog
Home Tags mivatopfn
Tag
Cancel
diff --git a/tags/mizeropagethread/index.html b/tags/mizeropagethread/index.html new file mode 100644 index 00000000..035ae285 --- /dev/null +++ b/tags/mizeropagethread/index.html @@ -0,0 +1 @@ + mizeropagethread | Rayanfam Blog
Home Tags mizeropagethread
Tag
Cancel
diff --git a/tags/mmavailablepages/index.html b/tags/mmavailablepages/index.html new file mode 100644 index 00000000..58ae28aa --- /dev/null +++ b/tags/mmavailablepages/index.html @@ -0,0 +1 @@ + mmavailablepages | Rayanfam Blog
Home Tags mmavailablepages
Tag
Cancel
diff --git a/tags/mmgetphysicaladdress/index.html b/tags/mmgetphysicaladdress/index.html new file mode 100644 index 00000000..ea7120d3 --- /dev/null +++ b/tags/mmgetphysicaladdress/index.html @@ -0,0 +1 @@ + mmgetphysicaladdress | Rayanfam Blog
Home Tags mmgetphysicaladdress
Tag
Cancel
diff --git a/tags/mmgetvirtualforphysical/index.html b/tags/mmgetvirtualforphysical/index.html new file mode 100644 index 00000000..2af23f89 --- /dev/null +++ b/tags/mmgetvirtualforphysical/index.html @@ -0,0 +1 @@ + mmgetvirtualforphysical | Rayanfam Blog
Home Tags mmgetvirtualforphysical
Tag
Cancel
diff --git a/tags/mmnumberofphysicalpages/index.html b/tags/mmnumberofphysicalpages/index.html new file mode 100644 index 00000000..48dbfab5 --- /dev/null +++ b/tags/mmnumberofphysicalpages/index.html @@ -0,0 +1 @@ + mmnumberofphysicalpages | Rayanfam Blog
Home Tags mmnumberofphysicalpages
Tag
Cancel
diff --git a/tags/mmpfndatabase/index.html b/tags/mmpfndatabase/index.html new file mode 100644 index 00000000..c45757f3 --- /dev/null +++ b/tags/mmpfndatabase/index.html @@ -0,0 +1 @@ + mmpfndatabase | Rayanfam Blog
Home Tags mmpfndatabase
Tag
Cancel
diff --git a/tags/mmresidentavailablepages/index.html b/tags/mmresidentavailablepages/index.html new file mode 100644 index 00000000..dd1572f9 --- /dev/null +++ b/tags/mmresidentavailablepages/index.html @@ -0,0 +1 @@ + mmresidentavailablepages | Rayanfam Blog
Home Tags mmresidentavailablepages
Tag
Cancel
diff --git a/tags/mmu-virtualization/index.html b/tags/mmu-virtualization/index.html new file mode 100644 index 00000000..82302dfe --- /dev/null +++ b/tags/mmu-virtualization/index.html @@ -0,0 +1 @@ + mmu-virtualization | Rayanfam Blog
Home Tags mmu-virtualization
Tag
Cancel
diff --git a/tags/modelsim/index.html b/tags/modelsim/index.html new file mode 100644 index 00000000..95211638 --- /dev/null +++ b/tags/modelsim/index.html @@ -0,0 +1 @@ + ModelSim | Rayanfam Blog
Home Tags ModelSim
Tag
Cancel
diff --git a/tags/modern/index.html b/tags/modern/index.html new file mode 100644 index 00000000..5d66f136 --- /dev/null +++ b/tags/modern/index.html @@ -0,0 +1 @@ + modern | Rayanfam Blog
Home Tags modern
Tag
Cancel
diff --git a/tags/modprobe/index.html b/tags/modprobe/index.html new file mode 100644 index 00000000..f9d92f8d --- /dev/null +++ b/tags/modprobe/index.html @@ -0,0 +1 @@ + modprobe | Rayanfam Blog
Home Tags modprobe
Tag
Cancel
diff --git a/tags/module/index.html b/tags/module/index.html new file mode 100644 index 00000000..5eddb392 --- /dev/null +++ b/tags/module/index.html @@ -0,0 +1 @@ + module | Rayanfam Blog
Home Tags module
Tag
Cancel
diff --git a/tags/monitor-mwait-instructions/index.html b/tags/monitor-mwait-instructions/index.html new file mode 100644 index 00000000..6e930ee1 --- /dev/null +++ b/tags/monitor-mwait-instructions/index.html @@ -0,0 +1 @@ + monitor-mwait-instructions | Rayanfam Blog
Home Tags monitor-mwait-instructions
Tag
Cancel
diff --git a/tags/mount/index.html b/tags/mount/index.html new file mode 100644 index 00000000..6adc2834 --- /dev/null +++ b/tags/mount/index.html @@ -0,0 +1 @@ + mount | Rayanfam Blog
Home Tags mount
Tag
Cancel
diff --git a/tags/msr-modification-detection/index.html b/tags/msr-modification-detection/index.html new file mode 100644 index 00000000..beda66b2 --- /dev/null +++ b/tags/msr-modification-detection/index.html @@ -0,0 +1 @@ + msr-modification-detection | Rayanfam Blog
Home Tags msr-modification-detection
Tag
Cancel
diff --git a/tags/mtrr/index.html b/tags/mtrr/index.html new file mode 100644 index 00000000..69cae1f6 --- /dev/null +++ b/tags/mtrr/index.html @@ -0,0 +1 @@ + mtrr | Rayanfam Blog
Home Tags mtrr
Tag
Cancel
diff --git a/tags/native-functions-pin-tool/index.html b/tags/native-functions-pin-tool/index.html new file mode 100644 index 00000000..bd6821b2 --- /dev/null +++ b/tags/native-functions-pin-tool/index.html @@ -0,0 +1 @@ + native-functions-pin-tool | Rayanfam Blog
Home Tags native-functions-pin-tool
Tag
Cancel
diff --git a/tags/nested-page-tables/index.html b/tags/nested-page-tables/index.html new file mode 100644 index 00000000..8c261415 --- /dev/null +++ b/tags/nested-page-tables/index.html @@ -0,0 +1 @@ + nested-page-tables | Rayanfam Blog
Home Tags nested-page-tables
Tag
Cancel
diff --git a/tags/net-reflection/index.html b/tags/net-reflection/index.html new file mode 100644 index 00000000..82eedc95 --- /dev/null +++ b/tags/net-reflection/index.html @@ -0,0 +1 @@ + net-reflection | Rayanfam Blog
Home Tags net-reflection
Tag
Cancel
diff --git a/tags/netlist/index.html b/tags/netlist/index.html new file mode 100644 index 00000000..6c3d7360 --- /dev/null +++ b/tags/netlist/index.html @@ -0,0 +1 @@ + netlist | Rayanfam Blog
Home Tags netlist
Tag
Cancel
diff --git a/tags/network/index.html b/tags/network/index.html new file mode 100644 index 00000000..4f325ed5 --- /dev/null +++ b/tags/network/index.html @@ -0,0 +1 @@ + network | Rayanfam Blog
Home Tags network
Tag
Cancel
diff --git a/tags/nginx/index.html b/tags/nginx/index.html new file mode 100644 index 00000000..783db09e --- /dev/null +++ b/tags/nginx/index.html @@ -0,0 +1 @@ + nginx | Rayanfam Blog
Home Tags nginx
Tag
Cancel
diff --git a/tags/nodogsplash/index.html b/tags/nodogsplash/index.html new file mode 100644 index 00000000..2e46da8f --- /dev/null +++ b/tags/nodogsplash/index.html @@ -0,0 +1 @@ + nodogsplash | Rayanfam Blog
Home Tags nodogsplash
Tag
Cancel
diff --git a/tags/non-general-registers/index.html b/tags/non-general-registers/index.html new file mode 100644 index 00000000..f186bb0b --- /dev/null +++ b/tags/non-general-registers/index.html @@ -0,0 +1 @@ + non-general-registers | Rayanfam Blog
Home Tags non-general-registers
Tag
Cancel
diff --git a/tags/npt/index.html b/tags/npt/index.html new file mode 100644 index 00000000..355a5a8c --- /dev/null +++ b/tags/npt/index.html @@ -0,0 +1 @@ + npt | Rayanfam Blog
Home Tags npt
Tag
Cancel
diff --git a/tags/nt-mmpfn/index.html b/tags/nt-mmpfn/index.html new file mode 100644 index 00000000..a9d61db7 --- /dev/null +++ b/tags/nt-mmpfn/index.html @@ -0,0 +1 @@ + nt_mmpfn | Rayanfam Blog
Home Tags nt_mmpfn
Tag
Cancel
diff --git a/tags/ntopenprocesstokenex/index.html b/tags/ntopenprocesstokenex/index.html new file mode 100644 index 00000000..0ffff362 --- /dev/null +++ b/tags/ntopenprocesstokenex/index.html @@ -0,0 +1 @@ + ntopenprocesstokenex | Rayanfam Blog
Home Tags ntopenprocesstokenex
Tag
Cancel
diff --git a/tags/object-internals/index.html b/tags/object-internals/index.html new file mode 100644 index 00000000..8f1c4c9c --- /dev/null +++ b/tags/object-internals/index.html @@ -0,0 +1 @@ + object-internals | Rayanfam Blog
Home Tags object-internals
Tag
Cancel
diff --git a/tags/object-type-windows/index.html b/tags/object-type-windows/index.html new file mode 100644 index 00000000..3664ccfe --- /dev/null +++ b/tags/object-type-windows/index.html @@ -0,0 +1 @@ + object-type-windows | Rayanfam Blog
Home Tags object-type-windows
Tag
Cancel
diff --git a/tags/obpreferenceobjectbyhandlewithtag/index.html b/tags/obpreferenceobjectbyhandlewithtag/index.html new file mode 100644 index 00000000..2a86813b --- /dev/null +++ b/tags/obpreferenceobjectbyhandlewithtag/index.html @@ -0,0 +1 @@ + obpreferenceobjectbyhandlewithtag | Rayanfam Blog
Home Tags obpreferenceobjectbyhandlewithtag
Tag
Cancel
diff --git a/tags/obreferenceobjectbyhandle/index.html b/tags/obreferenceobjectbyhandle/index.html new file mode 100644 index 00000000..f0caf7eb --- /dev/null +++ b/tags/obreferenceobjectbyhandle/index.html @@ -0,0 +1 @@ + obreferenceobjectbyhandle | Rayanfam Blog
Home Tags obreferenceobjectbyhandle
Tag
Cancel
diff --git a/tags/openlane/index.html b/tags/openlane/index.html new file mode 100644 index 00000000..f9664bdc --- /dev/null +++ b/tags/openlane/index.html @@ -0,0 +1 @@ + OpenLane | Rayanfam Blog
Home Tags OpenLane
Tag
Cancel
diff --git a/tags/openldap/index.html b/tags/openldap/index.html new file mode 100644 index 00000000..74c5fd70 --- /dev/null +++ b/tags/openldap/index.html @@ -0,0 +1 @@ + openldap | Rayanfam Blog
Home Tags openldap
Tag
Cancel
diff --git a/tags/openram/index.html b/tags/openram/index.html new file mode 100644 index 00000000..24b633c5 --- /dev/null +++ b/tags/openram/index.html @@ -0,0 +1 @@ + OpenRAM | Rayanfam Blog
Home Tags OpenRAM
Tag
Cancel
diff --git a/tags/opensource/index.html b/tags/opensource/index.html new file mode 100644 index 00000000..f03f026a --- /dev/null +++ b/tags/opensource/index.html @@ -0,0 +1 @@ + opensource | Rayanfam Blog
Home Tags opensource
Tag
Cancel
diff --git a/tags/openswan/index.html b/tags/openswan/index.html new file mode 100644 index 00000000..9dd67f71 --- /dev/null +++ b/tags/openswan/index.html @@ -0,0 +1 @@ + openswan | Rayanfam Blog
Home Tags openswan
Tag
Cancel
diff --git a/tags/openwrt/index.html b/tags/openwrt/index.html new file mode 100644 index 00000000..c2811eb7 --- /dev/null +++ b/tags/openwrt/index.html @@ -0,0 +1 @@ + openwrt | Rayanfam Blog
Home Tags openwrt
Tag
Cancel
diff --git a/tags/orginalfirstthunk/index.html b/tags/orginalfirstthunk/index.html new file mode 100644 index 00000000..1aaafcbf --- /dev/null +++ b/tags/orginalfirstthunk/index.html @@ -0,0 +1 @@ + orginalfirstthunk | Rayanfam Blog
Home Tags orginalfirstthunk
Tag
Cancel
diff --git a/tags/os/index.html b/tags/os/index.html new file mode 100644 index 00000000..3ca967af --- /dev/null +++ b/tags/os/index.html @@ -0,0 +1 @@ + os | Rayanfam Blog
Home Tags os
Tag
Cancel
diff --git a/tags/packages-xml/index.html b/tags/packages-xml/index.html new file mode 100644 index 00000000..2cb083f1 --- /dev/null +++ b/tags/packages-xml/index.html @@ -0,0 +1 @@ + packages-xml | Rayanfam Blog
Home Tags packages-xml
Tag
Cancel
diff --git a/tags/packetscript/index.html b/tags/packetscript/index.html new file mode 100644 index 00000000..f9199bad --- /dev/null +++ b/tags/packetscript/index.html @@ -0,0 +1 @@ + packetscript | Rayanfam Blog
Home Tags packetscript
Tag
Cancel
diff --git a/tags/page-frame-number/index.html b/tags/page-frame-number/index.html new file mode 100644 index 00000000..f92b0c56 --- /dev/null +++ b/tags/page-frame-number/index.html @@ -0,0 +1 @@ + page-frame-number | Rayanfam Blog
Home Tags page-frame-number
Tag
Cancel
diff --git a/tags/page-management-in-windows/index.html b/tags/page-management-in-windows/index.html new file mode 100644 index 00000000..6a37aae6 --- /dev/null +++ b/tags/page-management-in-windows/index.html @@ -0,0 +1 @@ + page-management-in-windows | Rayanfam Blog
Home Tags page-management-in-windows
Tag
Cancel
diff --git a/tags/pfn-database/index.html b/tags/pfn-database/index.html new file mode 100644 index 00000000..72a7a931 --- /dev/null +++ b/tags/pfn-database/index.html @@ -0,0 +1 @@ + pfn-database | Rayanfam Blog
Home Tags pfn-database
Tag
Cancel
diff --git a/tags/pfn-in-windows/index.html b/tags/pfn-in-windows/index.html new file mode 100644 index 00000000..29e9c679 --- /dev/null +++ b/tags/pfn-in-windows/index.html @@ -0,0 +1 @@ + pfn-in-windows | Rayanfam Blog
Home Tags pfn-in-windows
Tag
Cancel
diff --git a/tags/pfn-list-corrupt/index.html b/tags/pfn-list-corrupt/index.html new file mode 100644 index 00000000..b428e23b --- /dev/null +++ b/tags/pfn-list-corrupt/index.html @@ -0,0 +1 @@ + pfn_list_corrupt | Rayanfam Blog
Home Tags pfn_list_corrupt
Tag
Cancel
diff --git a/tags/pfn/index.html b/tags/pfn/index.html new file mode 100644 index 00000000..fff5ea0a --- /dev/null +++ b/tags/pfn/index.html @@ -0,0 +1 @@ + pfn | Rayanfam Blog
Home Tags pfn
Tag
Cancel
diff --git a/tags/php-fpm/index.html b/tags/php-fpm/index.html new file mode 100644 index 00000000..f9d33257 --- /dev/null +++ b/tags/php-fpm/index.html @@ -0,0 +1 @@ + php-fpm | Rayanfam Blog
Home Tags php-fpm
Tag
Cancel
diff --git a/tags/phpldapadmin/index.html b/tags/phpldapadmin/index.html new file mode 100644 index 00000000..7db4c044 --- /dev/null +++ b/tags/phpldapadmin/index.html @@ -0,0 +1 @@ + phpldapadmin | Rayanfam Blog
Home Tags phpldapadmin
Tag
Cancel
diff --git a/tags/pin-tool-example/index.html b/tags/pin-tool-example/index.html new file mode 100644 index 00000000..53653e47 --- /dev/null +++ b/tags/pin-tool-example/index.html @@ -0,0 +1 @@ + pin-tool-example | Rayanfam Blog
Home Tags pin-tool-example
Tag
Cancel
diff --git a/tags/pin-tool-hello-world/index.html b/tags/pin-tool-hello-world/index.html new file mode 100644 index 00000000..f87e162b --- /dev/null +++ b/tags/pin-tool-hello-world/index.html @@ -0,0 +1 @@ + pin-tool-hello-world | Rayanfam Blog
Home Tags pin-tool-hello-world
Tag
Cancel
diff --git a/tags/pin-tool-on-windows/index.html b/tags/pin-tool-on-windows/index.html new file mode 100644 index 00000000..7d89778b --- /dev/null +++ b/tags/pin-tool-on-windows/index.html @@ -0,0 +1 @@ + pin-tool-on-windows | Rayanfam Blog
Home Tags pin-tool-on-windows
Tag
Cancel
diff --git a/tags/pki/index.html b/tags/pki/index.html new file mode 100644 index 00000000..c0d8d634 --- /dev/null +++ b/tags/pki/index.html @@ -0,0 +1 @@ + pki | Rayanfam Blog
Home Tags pki
Tag
Cancel
diff --git a/tags/port-security/index.html b/tags/port-security/index.html new file mode 100644 index 00000000..6b994526 --- /dev/null +++ b/tags/port-security/index.html @@ -0,0 +1 @@ + port-security | Rayanfam Blog
Home Tags port-security
Tag
Cancel
diff --git a/tags/port/index.html b/tags/port/index.html new file mode 100644 index 00000000..e061656b --- /dev/null +++ b/tags/port/index.html @@ -0,0 +1 @@ + port | Rayanfam Blog
Home Tags port
Tag
Cancel
diff --git a/tags/powershell/index.html b/tags/powershell/index.html new file mode 100644 index 00000000..4d09bf6a --- /dev/null +++ b/tags/powershell/index.html @@ -0,0 +1 @@ + powershell | Rayanfam Blog
Home Tags powershell
Tag
Cancel
diff --git a/tags/process-query-limited-information/index.html b/tags/process-query-limited-information/index.html new file mode 100644 index 00000000..274e7f27 --- /dev/null +++ b/tags/process-query-limited-information/index.html @@ -0,0 +1 @@ + process_query_limited_information | Rayanfam Blog
Home Tags process_query_limited_information
Tag
Cancel
diff --git a/tags/proxy/index.html b/tags/proxy/index.html new file mode 100644 index 00000000..0c34f9e2 --- /dev/null +++ b/tags/proxy/index.html @@ -0,0 +1 @@ + proxy | Rayanfam Blog
Home Tags proxy
Tag
Cancel
diff --git a/tags/pykd-example/index.html b/tags/pykd-example/index.html new file mode 100644 index 00000000..d37e88d9 --- /dev/null +++ b/tags/pykd-example/index.html @@ -0,0 +1 @@ + pykd-example | Rayanfam Blog
Home Tags pykd-example
Tag
Cancel
diff --git a/tags/pykd-sample/index.html b/tags/pykd-sample/index.html new file mode 100644 index 00000000..48c74b45 --- /dev/null +++ b/tags/pykd-sample/index.html @@ -0,0 +1 @@ + pykd-sample | Rayanfam Blog
Home Tags pykd-sample
Tag
Cancel
diff --git a/tags/pykd-scripts/index.html b/tags/pykd-scripts/index.html new file mode 100644 index 00000000..9bfd71c1 --- /dev/null +++ b/tags/pykd-scripts/index.html @@ -0,0 +1 @@ + pykd-scripts | Rayanfam Blog
Home Tags pykd-scripts
Tag
Cancel
diff --git a/tags/pykd-tutorial/index.html b/tags/pykd-tutorial/index.html new file mode 100644 index 00000000..1a397856 --- /dev/null +++ b/tags/pykd-tutorial/index.html @@ -0,0 +1 @@ + pykd-tutorial | Rayanfam Blog
Home Tags pykd-tutorial
Tag
Cancel
diff --git a/tags/pyqt/index.html b/tags/pyqt/index.html new file mode 100644 index 00000000..f6a8cdef --- /dev/null +++ b/tags/pyqt/index.html @@ -0,0 +1 @@ + pyqt | Rayanfam Blog
Home Tags pyqt
Tag
Cancel
diff --git a/tags/pyqt5/index.html b/tags/pyqt5/index.html new file mode 100644 index 00000000..a3df533c --- /dev/null +++ b/tags/pyqt5/index.html @@ -0,0 +1 @@ + pyqt5 | Rayanfam Blog
Home Tags pyqt5
Tag
Cancel
diff --git a/tags/python/index.html b/tags/python/index.html new file mode 100644 index 00000000..2f7034d6 --- /dev/null +++ b/tags/python/index.html @@ -0,0 +1 @@ + python | Rayanfam Blog
Home Tags python
Tag
Cancel
diff --git a/tags/python3/index.html b/tags/python3/index.html new file mode 100644 index 00000000..b61692bf --- /dev/null +++ b/tags/python3/index.html @@ -0,0 +1 @@ + python3 | Rayanfam Blog
Home Tags python3
Tag
Cancel
diff --git a/tags/qt/index.html b/tags/qt/index.html new file mode 100644 index 00000000..494a7851 --- /dev/null +++ b/tags/qt/index.html @@ -0,0 +1 @@ + qt | Rayanfam Blog
Home Tags qt
Tag
Cancel
diff --git a/tags/ransomware/index.html b/tags/ransomware/index.html new file mode 100644 index 00000000..016ea092 --- /dev/null +++ b/tags/ransomware/index.html @@ -0,0 +1 @@ + ransomware | Rayanfam Blog
Home Tags ransomware
Tag
Cancel
diff --git a/tags/rapid-virtualization-indexing/index.html b/tags/rapid-virtualization-indexing/index.html new file mode 100644 index 00000000..9d276e94 --- /dev/null +++ b/tags/rapid-virtualization-indexing/index.html @@ -0,0 +1 @@ + rapid-virtualization-indexing | Rayanfam Blog
Home Tags rapid-virtualization-indexing
Tag
Cancel
diff --git a/tags/real-access-rights/index.html b/tags/real-access-rights/index.html new file mode 100644 index 00000000..9f6d3e1b --- /dev/null +++ b/tags/real-access-rights/index.html @@ -0,0 +1 @@ + real-access-rights | Rayanfam Blog
Home Tags real-access-rights
Tag
Cancel
diff --git a/tags/reversing-netlist/index.html b/tags/reversing-netlist/index.html new file mode 100644 index 00000000..38839155 --- /dev/null +++ b/tags/reversing-netlist/index.html @@ -0,0 +1 @@ + reversing-netlist | Rayanfam Blog
Home Tags reversing-netlist
Tag
Cancel
diff --git a/tags/reversing-using-hyperdbg/index.html b/tags/reversing-using-hyperdbg/index.html new file mode 100644 index 00000000..cfb86d37 --- /dev/null +++ b/tags/reversing-using-hyperdbg/index.html @@ -0,0 +1 @@ + reversing-using-hyperdbg | Rayanfam Blog
Home Tags reversing-using-hyperdbg
Tag
Cancel
diff --git a/tags/reversing-windows-internals/index.html b/tags/reversing-windows-internals/index.html new file mode 100644 index 00000000..b71efde5 --- /dev/null +++ b/tags/reversing-windows-internals/index.html @@ -0,0 +1 @@ + reversing-windows-internals | Rayanfam Blog
Home Tags reversing-windows-internals
Tag
Cancel
diff --git a/tags/ring-transitioning/index.html b/tags/ring-transitioning/index.html new file mode 100644 index 00000000..755c658d --- /dev/null +++ b/tags/ring-transitioning/index.html @@ -0,0 +1 @@ + ring-transitioning | Rayanfam Blog
Home Tags ring-transitioning
Tag
Cancel
diff --git a/tags/ring0-to-ring3/index.html b/tags/ring0-to-ring3/index.html new file mode 100644 index 00000000..bb601dc1 --- /dev/null +++ b/tags/ring0-to-ring3/index.html @@ -0,0 +1 @@ + ring0-to-ring3 | Rayanfam Blog
Home Tags ring0-to-ring3
Tag
Cancel
diff --git a/tags/router/index.html b/tags/router/index.html new file mode 100644 index 00000000..b7c49c07 --- /dev/null +++ b/tags/router/index.html @@ -0,0 +1 @@ + router | Rayanfam Blog
Home Tags router
Tag
Cancel
diff --git a/tags/run-intel-pin-tool/index.html b/tags/run-intel-pin-tool/index.html new file mode 100644 index 00000000..534042a1 --- /dev/null +++ b/tags/run-intel-pin-tool/index.html @@ -0,0 +1 @@ + run-intel-pin-tool | Rayanfam Blog
Home Tags run-intel-pin-tool
Tag
Cancel
diff --git a/tags/run-pykd-command/index.html b/tags/run-pykd-command/index.html new file mode 100644 index 00000000..5aa838f6 --- /dev/null +++ b/tags/run-pykd-command/index.html @@ -0,0 +1 @@ + run-pykd-command | Rayanfam Blog
Home Tags run-pykd-command
Tag
Cancel
diff --git a/tags/rvi/index.html b/tags/rvi/index.html new file mode 100644 index 00000000..51fe5cd4 --- /dev/null +++ b/tags/rvi/index.html @@ -0,0 +1 @@ + rvi | Rayanfam Blog
Home Tags rvi
Tag
Cancel
diff --git a/tags/second-level-address-translation/index.html b/tags/second-level-address-translation/index.html new file mode 100644 index 00000000..15ab5b93 --- /dev/null +++ b/tags/second-level-address-translation/index.html @@ -0,0 +1 @@ + second-level-address-translation | Rayanfam Blog
Home Tags second-level-address-translation
Tag
Cancel
diff --git a/tags/security/index.html b/tags/security/index.html new file mode 100644 index 00000000..7c4961ea --- /dev/null +++ b/tags/security/index.html @@ -0,0 +1 @@ + security | Rayanfam Blog
Home Tags security
Tag
Cancel
diff --git a/tags/segment-registers/index.html b/tags/segment-registers/index.html new file mode 100644 index 00000000..6ad95797 --- /dev/null +++ b/tags/segment-registers/index.html @@ -0,0 +1 @@ + segment-registers | Rayanfam Blog
Home Tags segment-registers
Tag
Cancel
diff --git a/tags/server-core/index.html b/tags/server-core/index.html new file mode 100644 index 00000000..29ff47b1 --- /dev/null +++ b/tags/server-core/index.html @@ -0,0 +1 @@ + server-core | Rayanfam Blog
Home Tags server-core
Tag
Cancel
diff --git a/tags/setting-up-virtual-machine-monitor/index.html b/tags/setting-up-virtual-machine-monitor/index.html new file mode 100644 index 00000000..d9afd6dd --- /dev/null +++ b/tags/setting-up-virtual-machine-monitor/index.html @@ -0,0 +1 @@ + setting-up-virtual-machine-monitor | Rayanfam Blog
Home Tags setting-up-virtual-machine-monitor
Tag
Cancel
diff --git a/tags/single-context/index.html b/tags/single-context/index.html new file mode 100644 index 00000000..75509608 --- /dev/null +++ b/tags/single-context/index.html @@ -0,0 +1 @@ + single-context | Rayanfam Blog
Home Tags single-context
Tag
Cancel
diff --git a/tags/slat/index.html b/tags/slat/index.html new file mode 100644 index 00000000..ba171b67 --- /dev/null +++ b/tags/slat/index.html @@ -0,0 +1 @@ + slat | Rayanfam Blog
Home Tags slat
Tag
Cancel
diff --git a/tags/squid/index.html b/tags/squid/index.html new file mode 100644 index 00000000..59e57933 --- /dev/null +++ b/tags/squid/index.html @@ -0,0 +1 @@ + squid | Rayanfam Blog
Home Tags squid
Tag
Cancel
diff --git a/tags/squid3/index.html b/tags/squid3/index.html new file mode 100644 index 00000000..f5c6f3e5 --- /dev/null +++ b/tags/squid3/index.html @@ -0,0 +1 @@ + squid3 | Rayanfam Blog
Home Tags squid3
Tag
Cancel
diff --git a/tags/ssdt/index.html b/tags/ssdt/index.html new file mode 100644 index 00000000..cd908201 --- /dev/null +++ b/tags/ssdt/index.html @@ -0,0 +1 @@ + ssdt | Rayanfam Blog
Home Tags ssdt
Tag
Cancel
diff --git a/tags/stage-2-page-tables/index.html b/tags/stage-2-page-tables/index.html new file mode 100644 index 00000000..bbd7f76e --- /dev/null +++ b/tags/stage-2-page-tables/index.html @@ -0,0 +1 @@ + stage-2-page-tables | Rayanfam Blog
Home Tags stage-2-page-tables
Tag
Cancel
diff --git a/tags/standby-list/index.html b/tags/standby-list/index.html new file mode 100644 index 00000000..f7bc32d0 --- /dev/null +++ b/tags/standby-list/index.html @@ -0,0 +1 @@ + standby-list | Rayanfam Blog
Home Tags standby-list
Tag
Cancel
diff --git a/tags/start-virtual-machine/index.html b/tags/start-virtual-machine/index.html new file mode 100644 index 00000000..ed23fe1b --- /dev/null +++ b/tags/start-virtual-machine/index.html @@ -0,0 +1 @@ + start-virtual-machine | Rayanfam Blog
Home Tags start-virtual-machine
Tag
Cancel
diff --git a/tags/start-with-pin-tool/index.html b/tags/start-with-pin-tool/index.html new file mode 100644 index 00000000..9d063cf9 --- /dev/null +++ b/tags/start-with-pin-tool/index.html @@ -0,0 +1 @@ + start-with-pin-tool | Rayanfam Blog
Home Tags start-with-pin-tool
Tag
Cancel
diff --git a/tags/start/index.html b/tags/start/index.html new file mode 100644 index 00000000..da2785d1 --- /dev/null +++ b/tags/start/index.html @@ -0,0 +1 @@ + start | Rayanfam Blog
Home Tags start
Tag
Cancel
diff --git a/tags/strongswan/index.html b/tags/strongswan/index.html new file mode 100644 index 00000000..9b0a179c --- /dev/null +++ b/tags/strongswan/index.html @@ -0,0 +1 @@ + strongswan | Rayanfam Blog
Home Tags strongswan
Tag
Cancel
diff --git a/tags/swishdbgext/index.html b/tags/swishdbgext/index.html new file mode 100644 index 00000000..1a83d253 --- /dev/null +++ b/tags/swishdbgext/index.html @@ -0,0 +1 @@ + swishdbgext | Rayanfam Blog
Home Tags swishdbgext
Tag
Cancel
diff --git a/tags/switch/index.html b/tags/switch/index.html new file mode 100644 index 00000000..5cd369c7 --- /dev/null +++ b/tags/switch/index.html @@ -0,0 +1 @@ + switch | Rayanfam Blog
Home Tags switch
Tag
Cancel
diff --git a/tags/switchport/index.html b/tags/switchport/index.html new file mode 100644 index 00000000..1df17705 --- /dev/null +++ b/tags/switchport/index.html @@ -0,0 +1 @@ + switchport | Rayanfam Blog
Home Tags switchport
Tag
Cancel
diff --git a/tags/sysadmin/index.html b/tags/sysadmin/index.html new file mode 100644 index 00000000..c2ef41d8 --- /dev/null +++ b/tags/sysadmin/index.html @@ -0,0 +1 @@ + sysadmin | Rayanfam Blog
Home Tags sysadmin
Tag
Cancel
diff --git a/tags/syscall-hook/index.html b/tags/syscall-hook/index.html new file mode 100644 index 00000000..8a148040 --- /dev/null +++ b/tags/syscall-hook/index.html @@ -0,0 +1 @@ + syscall-hook | Rayanfam Blog
Home Tags syscall-hook
Tag
Cancel
diff --git a/tags/system-call-hook/index.html b/tags/system-call-hook/index.html new file mode 100644 index 00000000..bb9242d6 --- /dev/null +++ b/tags/system-call-hook/index.html @@ -0,0 +1 @@ + system-call-hook | Rayanfam Blog
Home Tags system-call-hook
Tag
Cancel
diff --git a/tags/system-service-descriptor-table/index.html b/tags/system-service-descriptor-table/index.html new file mode 100644 index 00000000..59afc3f0 --- /dev/null +++ b/tags/system-service-descriptor-table/index.html @@ -0,0 +1 @@ + system-service-descriptor-table | Rayanfam Blog
Home Tags system-service-descriptor-table
Tag
Cancel
diff --git a/tags/systemd/index.html b/tags/systemd/index.html new file mode 100644 index 00000000..e23e7cc1 --- /dev/null +++ b/tags/systemd/index.html @@ -0,0 +1 @@ + systemd | Rayanfam Blog
Home Tags systemd
Tag
Cancel
diff --git a/tags/sysvinit/index.html b/tags/sysvinit/index.html new file mode 100644 index 00000000..8a365bd3 --- /dev/null +++ b/tags/sysvinit/index.html @@ -0,0 +1 @@ + sysvinit | Rayanfam Blog
Home Tags sysvinit
Tag
Cancel
diff --git a/tags/timedatestamp/index.html b/tags/timedatestamp/index.html new file mode 100644 index 00000000..35e87907 --- /dev/null +++ b/tags/timedatestamp/index.html @@ -0,0 +1 @@ + timedatestamp | Rayanfam Blog
Home Tags timedatestamp
Tag
Cancel
diff --git a/tags/tr-register/index.html b/tags/tr-register/index.html new file mode 100644 index 00000000..59af4512 --- /dev/null +++ b/tags/tr-register/index.html @@ -0,0 +1 @@ + tr-register | Rayanfam Blog
Home Tags tr-register
Tag
Cancel
diff --git a/tags/trace-functions-to-kernel/index.html b/tags/trace-functions-to-kernel/index.html new file mode 100644 index 00000000..dce51066 --- /dev/null +++ b/tags/trace-functions-to-kernel/index.html @@ -0,0 +1 @@ + trace-functions-to-kernel | Rayanfam Blog
Home Tags trace-functions-to-kernel
Tag
Cancel
diff --git a/tags/tunnel/index.html b/tags/tunnel/index.html new file mode 100644 index 00000000..5fa6e6cc --- /dev/null +++ b/tags/tunnel/index.html @@ -0,0 +1 @@ + tunnel | Rayanfam Blog
Home Tags tunnel
Tag
Cancel
diff --git a/tags/tutorial/index.html b/tags/tutorial/index.html new file mode 100644 index 00000000..913cab7d --- /dev/null +++ b/tags/tutorial/index.html @@ -0,0 +1 @@ + tutorial | Rayanfam Blog
Home Tags tutorial
Tag
Cancel
diff --git a/tags/udev/index.html b/tags/udev/index.html new file mode 100644 index 00000000..75d9099f --- /dev/null +++ b/tags/udev/index.html @@ -0,0 +1 @@ + udev | Rayanfam Blog
Home Tags udev
Tag
Cancel
diff --git a/tags/uhttpd/index.html b/tags/uhttpd/index.html new file mode 100644 index 00000000..017b4f32 --- /dev/null +++ b/tags/uhttpd/index.html @@ -0,0 +1 @@ + uhttpd | Rayanfam Blog
Home Tags uhttpd
Tag
Cancel
diff --git a/tags/use-intel-pin/index.html b/tags/use-intel-pin/index.html new file mode 100644 index 00000000..a131ea10 --- /dev/null +++ b/tags/use-intel-pin/index.html @@ -0,0 +1 @@ + use-intel-pin | Rayanfam Blog
Home Tags use-intel-pin
Tag
Cancel
diff --git a/tags/using-cpu-virtualization/index.html b/tags/using-cpu-virtualization/index.html new file mode 100644 index 00000000..4c058894 --- /dev/null +++ b/tags/using-cpu-virtualization/index.html @@ -0,0 +1 @@ + using-cpu-virtualization | Rayanfam Blog
Home Tags using-cpu-virtualization
Tag
Cancel
diff --git a/tags/using-hyperdbg-debugger/index.html b/tags/using-hyperdbg-debugger/index.html new file mode 100644 index 00000000..35afa6e5 --- /dev/null +++ b/tags/using-hyperdbg-debugger/index.html @@ -0,0 +1 @@ + using-hyperdbg-debugger | Rayanfam Blog
Home Tags using-hyperdbg-debugger
Tag
Cancel
diff --git a/tags/verilator/index.html b/tags/verilator/index.html new file mode 100644 index 00000000..4faa7b64 --- /dev/null +++ b/tags/verilator/index.html @@ -0,0 +1 @@ + Verilator | Rayanfam Blog
Home Tags Verilator
Tag
Cancel
diff --git a/tags/verilog/index.html b/tags/verilog/index.html new file mode 100644 index 00000000..67c88f84 --- /dev/null +++ b/tags/verilog/index.html @@ -0,0 +1 @@ + Verilog | Rayanfam Blog
Home Tags Verilog
Tag
Cancel
diff --git a/tags/vfs/index.html b/tags/vfs/index.html new file mode 100644 index 00000000..0935306c --- /dev/null +++ b/tags/vfs/index.html @@ -0,0 +1 @@ + vfs | Rayanfam Blog
Home Tags vfs
Tag
Cancel
diff --git a/tags/vhdl/index.html b/tags/vhdl/index.html new file mode 100644 index 00000000..e751431d --- /dev/null +++ b/tags/vhdl/index.html @@ -0,0 +1 @@ + VHDL | Rayanfam Blog
Home Tags VHDL
Tag
Cancel
diff --git a/tags/virtual-machine-control-structure/index.html b/tags/virtual-machine-control-structure/index.html new file mode 100644 index 00000000..5ae04acc --- /dev/null +++ b/tags/virtual-machine-control-structure/index.html @@ -0,0 +1 @@ + virtual-machine-control-structure | Rayanfam Blog
Home Tags virtual-machine-control-structure
Tag
Cancel
diff --git a/tags/virtualize-all-logical-cores/index.html b/tags/virtualize-all-logical-cores/index.html new file mode 100644 index 00000000..62b80463 --- /dev/null +++ b/tags/virtualize-all-logical-cores/index.html @@ -0,0 +1 @@ + virtualize-all-logical-cores | Rayanfam Blog
Home Tags virtualize-all-logical-cores
Tag
Cancel
diff --git a/tags/virtualize-current-system/index.html b/tags/virtualize-current-system/index.html new file mode 100644 index 00000000..8034fd17 --- /dev/null +++ b/tags/virtualize-current-system/index.html @@ -0,0 +1 @@ + virtualize-current-system | Rayanfam Blog
Home Tags virtualize-current-system
Tag
Cancel
diff --git a/tags/vitis/index.html b/tags/vitis/index.html new file mode 100644 index 00000000..34bdf5ba --- /dev/null +++ b/tags/vitis/index.html @@ -0,0 +1 @@ + vitis | Rayanfam Blog
Home Tags vitis
Tag
Cancel
diff --git a/tags/vivado/index.html b/tags/vivado/index.html new file mode 100644 index 00000000..edd74eb4 --- /dev/null +++ b/tags/vivado/index.html @@ -0,0 +1 @@ + vivado | Rayanfam Blog
Home Tags vivado
Tag
Cancel
diff --git a/tags/vmcall/index.html b/tags/vmcall/index.html new file mode 100644 index 00000000..dc9a9323 --- /dev/null +++ b/tags/vmcall/index.html @@ -0,0 +1 @@ + vmcall | Rayanfam Blog
Home Tags vmcall
Tag
Cancel
diff --git a/tags/vmcs-auditor/index.html b/tags/vmcs-auditor/index.html new file mode 100644 index 00000000..cda5e909 --- /dev/null +++ b/tags/vmcs-auditor/index.html @@ -0,0 +1 @@ + vmcs-auditor | Rayanfam Blog
Home Tags vmcs-auditor
Tag
Cancel
diff --git a/tags/vmcs-configuration/index.html b/tags/vmcs-configuration/index.html new file mode 100644 index 00000000..70b7f3fb --- /dev/null +++ b/tags/vmcs-configuration/index.html @@ -0,0 +1 @@ + vmcs-configuration | Rayanfam Blog
Home Tags vmcs-configuration
Tag
Cancel
diff --git a/tags/vmcs-layout-checking/index.html b/tags/vmcs-layout-checking/index.html new file mode 100644 index 00000000..6fde5e17 --- /dev/null +++ b/tags/vmcs-layout-checking/index.html @@ -0,0 +1 @@ + vmcs-layout-checking | Rayanfam Blog
Home Tags vmcs-layout-checking
Tag
Cancel
diff --git a/tags/vmcs-region/index.html b/tags/vmcs-region/index.html new file mode 100644 index 00000000..e7fc68ab --- /dev/null +++ b/tags/vmcs-region/index.html @@ -0,0 +1 @@ + vmcs-region | Rayanfam Blog
Home Tags vmcs-region
Tag
Cancel
diff --git a/tags/vmcs/index.html b/tags/vmcs/index.html new file mode 100644 index 00000000..d94138e9 --- /dev/null +++ b/tags/vmcs/index.html @@ -0,0 +1 @@ + vmcs | Rayanfam Blog
Home Tags vmcs
Tag
Cancel
diff --git a/tags/vmfunc/index.html b/tags/vmfunc/index.html new file mode 100644 index 00000000..27888e91 --- /dev/null +++ b/tags/vmfunc/index.html @@ -0,0 +1 @@ + vmfunc | Rayanfam Blog
Home Tags vmfunc
Tag
Cancel
diff --git a/tags/vmlaunch-0x7/index.html b/tags/vmlaunch-0x7/index.html new file mode 100644 index 00000000..aeb686ba --- /dev/null +++ b/tags/vmlaunch-0x7/index.html @@ -0,0 +1 @@ + vmlaunch-0x7 | Rayanfam Blog
Home Tags vmlaunch-0x7
Tag
Cancel
diff --git a/tags/vmlaunch-0x8/index.html b/tags/vmlaunch-0x8/index.html new file mode 100644 index 00000000..ea897ac5 --- /dev/null +++ b/tags/vmlaunch-0x8/index.html @@ -0,0 +1 @@ + vmlaunch-0x8 | Rayanfam Blog
Home Tags vmlaunch-0x8
Tag
Cancel
diff --git a/tags/vmlaunch-error/index.html b/tags/vmlaunch-error/index.html new file mode 100644 index 00000000..82fde496 --- /dev/null +++ b/tags/vmlaunch-error/index.html @@ -0,0 +1 @@ + vmlaunch-error | Rayanfam Blog
Home Tags vmlaunch-error
Tag
Cancel
diff --git a/tags/vmlaunch/index.html b/tags/vmlaunch/index.html new file mode 100644 index 00000000..d9bcad8b --- /dev/null +++ b/tags/vmlaunch/index.html @@ -0,0 +1 @@ + vmlaunch | Rayanfam Blog
Home Tags vmlaunch
Tag
Cancel
diff --git a/tags/vmm-implementation/index.html b/tags/vmm-implementation/index.html new file mode 100644 index 00000000..e0655299 --- /dev/null +++ b/tags/vmm-implementation/index.html @@ -0,0 +1 @@ + vmm-implementation | Rayanfam Blog
Home Tags vmm-implementation
Tag
Cancel
diff --git a/tags/vmm-tutorials/index.html b/tags/vmm-tutorials/index.html new file mode 100644 index 00000000..4b3ca142 --- /dev/null +++ b/tags/vmm-tutorials/index.html @@ -0,0 +1 @@ + vmm-tutorials | Rayanfam Blog
Home Tags vmm-tutorials
Tag
Cancel
diff --git a/tags/vmm/index.html b/tags/vmm/index.html new file mode 100644 index 00000000..8ec16420 --- /dev/null +++ b/tags/vmm/index.html @@ -0,0 +1 @@ + vmm | Rayanfam Blog
Home Tags vmm
Tag
Cancel
diff --git a/tags/vmresume-error/index.html b/tags/vmresume-error/index.html new file mode 100644 index 00000000..665bd3e1 --- /dev/null +++ b/tags/vmresume-error/index.html @@ -0,0 +1 @@ + vmresume-error | Rayanfam Blog
Home Tags vmresume-error
Tag
Cancel
diff --git a/tags/vmware-and-windbg/index.html b/tags/vmware-and-windbg/index.html new file mode 100644 index 00000000..38934aaf --- /dev/null +++ b/tags/vmware-and-windbg/index.html @@ -0,0 +1 @@ + vmware-and-windbg | Rayanfam Blog
Home Tags vmware-and-windbg
Tag
Cancel
diff --git a/tags/vmware-debugging/index.html b/tags/vmware-debugging/index.html new file mode 100644 index 00000000..b498c3f0 --- /dev/null +++ b/tags/vmware-debugging/index.html @@ -0,0 +1 @@ + vmware-debugging | Rayanfam Blog
Home Tags vmware-debugging
Tag
Cancel
diff --git a/tags/vmx-0-settings-and-1-settings/index.html b/tags/vmx-0-settings-and-1-settings/index.html new file mode 100644 index 00000000..0e061ed9 --- /dev/null +++ b/tags/vmx-0-settings-and-1-settings/index.html @@ -0,0 +1 @@ + vmx-0-settings-and-1-settings | Rayanfam Blog
Home Tags vmx-0-settings-and-1-settings
Tag
Cancel
diff --git a/tags/vmx-error-checking/index.html b/tags/vmx-error-checking/index.html new file mode 100644 index 00000000..a981141d --- /dev/null +++ b/tags/vmx-error-checking/index.html @@ -0,0 +1 @@ + vmx-error-checking | Rayanfam Blog
Home Tags vmx-error-checking
Tag
Cancel
diff --git a/tags/vmx-implementation/index.html b/tags/vmx-implementation/index.html new file mode 100644 index 00000000..5476cebc --- /dev/null +++ b/tags/vmx-implementation/index.html @@ -0,0 +1 @@ + vmx-implementation | Rayanfam Blog
Home Tags vmx-implementation
Tag
Cancel
diff --git a/tags/vmx-operation/index.html b/tags/vmx-operation/index.html new file mode 100644 index 00000000..5e1480b9 --- /dev/null +++ b/tags/vmx-operation/index.html @@ -0,0 +1 @@ + vmx-operation | Rayanfam Blog
Home Tags vmx-operation
Tag
Cancel
diff --git a/tags/vmx-tutorial/index.html b/tags/vmx-tutorial/index.html new file mode 100644 index 00000000..c4231633 --- /dev/null +++ b/tags/vmx-tutorial/index.html @@ -0,0 +1 @@ + VMX-tutorial | Rayanfam Blog
Home Tags VMX-tutorial
Tag
Cancel
diff --git a/tags/vmx-tutorials/index.html b/tags/vmx-tutorials/index.html new file mode 100644 index 00000000..8dd5661d --- /dev/null +++ b/tags/vmx-tutorials/index.html @@ -0,0 +1 @@ + vmx-tutorials | Rayanfam Blog
Home Tags vmx-tutorials
Tag
Cancel
diff --git a/tags/vmxon-region/index.html b/tags/vmxon-region/index.html new file mode 100644 index 00000000..e7558210 --- /dev/null +++ b/tags/vmxon-region/index.html @@ -0,0 +1 @@ + vmxon-region | Rayanfam Blog
Home Tags vmxon-region
Tag
Cancel
diff --git a/tags/vmxon/index.html b/tags/vmxon/index.html new file mode 100644 index 00000000..7ab7fbd1 --- /dev/null +++ b/tags/vmxon/index.html @@ -0,0 +1 @@ + vmxon | Rayanfam Blog
Home Tags vmxon
Tag
Cancel
diff --git a/tags/vpid-in-hypervisor/index.html b/tags/vpid-in-hypervisor/index.html new file mode 100644 index 00000000..68673258 --- /dev/null +++ b/tags/vpid-in-hypervisor/index.html @@ -0,0 +1 @@ + vpid-in-hypervisor | Rayanfam Blog
Home Tags vpid-in-hypervisor
Tag
Cancel
diff --git a/tags/vpn/index.html b/tags/vpn/index.html new file mode 100644 index 00000000..b6a781db --- /dev/null +++ b/tags/vpn/index.html @@ -0,0 +1 @@ + vpn | Rayanfam Blog
Home Tags vpn
Tag
Cancel
diff --git a/tags/wdk-assembly/index.html b/tags/wdk-assembly/index.html new file mode 100644 index 00000000..b2a1b67f --- /dev/null +++ b/tags/wdk-assembly/index.html @@ -0,0 +1 @@ + wdk-assembly | Rayanfam Blog
Home Tags wdk-assembly
Tag
Cancel
diff --git a/tags/webserver/index.html b/tags/webserver/index.html new file mode 100644 index 00000000..bcc9d223 --- /dev/null +++ b/tags/webserver/index.html @@ -0,0 +1 @@ + webserver | Rayanfam Blog
Home Tags webserver
Tag
Cancel
diff --git a/tags/wifidog/index.html b/tags/wifidog/index.html new file mode 100644 index 00000000..6ce9773b --- /dev/null +++ b/tags/wifidog/index.html @@ -0,0 +1 @@ + wifidog | Rayanfam Blog
Home Tags wifidog
Tag
Cancel
diff --git a/tags/windbg-extension/index.html b/tags/windbg-extension/index.html new file mode 100644 index 00000000..f7bc80fb --- /dev/null +++ b/tags/windbg-extension/index.html @@ -0,0 +1 @@ + windbg-extension | Rayanfam Blog
Home Tags windbg-extension
Tag
Cancel
diff --git a/tags/windbg/index.html b/tags/windbg/index.html new file mode 100644 index 00000000..72907a26 --- /dev/null +++ b/tags/windbg/index.html @@ -0,0 +1 @@ + windbg | Rayanfam Blog
Home Tags windbg
Tag
Cancel
diff --git a/tags/windows-api-detection/index.html b/tags/windows-api-detection/index.html new file mode 100644 index 00000000..1c474c79 --- /dev/null +++ b/tags/windows-api-detection/index.html @@ -0,0 +1 @@ + windows-api-detection | Rayanfam Blog
Home Tags windows-api-detection
Tag
Cancel
diff --git a/tags/windows-callbacks/index.html b/tags/windows-callbacks/index.html new file mode 100644 index 00000000..cbaf5929 --- /dev/null +++ b/tags/windows-callbacks/index.html @@ -0,0 +1 @@ + windows-callbacks | Rayanfam Blog
Home Tags windows-callbacks
Tag
Cancel
diff --git a/tags/windows-driver-kit-inline-assembly/index.html b/tags/windows-driver-kit-inline-assembly/index.html new file mode 100644 index 00000000..596316ec --- /dev/null +++ b/tags/windows-driver-kit-inline-assembly/index.html @@ -0,0 +1 @@ + windows-driver-kit-inline-assembly | Rayanfam Blog
Home Tags windows-driver-kit-inline-assembly
Tag
Cancel
diff --git a/tags/windows-internals-tutorial/index.html b/tags/windows-internals-tutorial/index.html new file mode 100644 index 00000000..654a3240 --- /dev/null +++ b/tags/windows-internals-tutorial/index.html @@ -0,0 +1 @@ + windows-internals-tutorial | Rayanfam Blog
Home Tags windows-internals-tutorial
Tag
Cancel
diff --git a/tags/windows-mmpfn-structure/index.html b/tags/windows-mmpfn-structure/index.html new file mode 100644 index 00000000..ecaf6682 --- /dev/null +++ b/tags/windows-mmpfn-structure/index.html @@ -0,0 +1 @@ + windows-mmpfn-structure | Rayanfam Blog
Home Tags windows-mmpfn-structure
Tag
Cancel
diff --git a/tags/windows-objects/index.html b/tags/windows-objects/index.html new file mode 100644 index 00000000..ea9f0ec6 --- /dev/null +++ b/tags/windows-objects/index.html @@ -0,0 +1 @@ + windows-objects | Rayanfam Blog
Home Tags windows-objects
Tag
Cancel
diff --git a/tags/windows-paging/index.html b/tags/windows-paging/index.html new file mode 100644 index 00000000..59f2073f --- /dev/null +++ b/tags/windows-paging/index.html @@ -0,0 +1 @@ + windows-paging | Rayanfam Blog
Home Tags windows-paging
Tag
Cancel
diff --git a/tags/windows-server-2012-r2/index.html b/tags/windows-server-2012-r2/index.html new file mode 100644 index 00000000..d6bcd66f --- /dev/null +++ b/tags/windows-server-2012-r2/index.html @@ -0,0 +1 @@ + windows-server-2012-r2 | Rayanfam Blog
Home Tags windows-server-2012-r2
Tag
Cancel
diff --git a/tags/windows-server-core/index.html b/tags/windows-server-core/index.html new file mode 100644 index 00000000..6ad9e0a2 --- /dev/null +++ b/tags/windows-server-core/index.html @@ -0,0 +1 @@ + windows-server-core | Rayanfam Blog
Home Tags windows-server-core
Tag
Cancel
diff --git a/tags/windows-server/index.html b/tags/windows-server/index.html new file mode 100644 index 00000000..f0ae0520 --- /dev/null +++ b/tags/windows-server/index.html @@ -0,0 +1 @@ + windows-server | Rayanfam Blog
Home Tags windows-server
Tag
Cancel
diff --git a/tags/windows/index.html b/tags/windows/index.html new file mode 100644 index 00000000..d1820786 --- /dev/null +++ b/tags/windows/index.html @@ -0,0 +1 @@ + windows | Rayanfam Blog
Home Tags windows
Tag
Cancel
diff --git a/tags/wpp-tracing/index.html b/tags/wpp-tracing/index.html new file mode 100644 index 00000000..4799ea73 --- /dev/null +++ b/tags/wpp-tracing/index.html @@ -0,0 +1 @@ + wpp-tracing | Rayanfam Blog
Home Tags wpp-tracing
Tag
Cancel
diff --git a/tags/x64-assembly-in-driver/index.html b/tags/x64-assembly-in-driver/index.html new file mode 100644 index 00000000..be0fb0e6 --- /dev/null +++ b/tags/x64-assembly-in-driver/index.html @@ -0,0 +1 @@ + x64-assembly-in-driver | Rayanfam Blog
Home Tags x64-assembly-in-driver
Tag
Cancel
diff --git a/tags/x64-inline-assembly/index.html b/tags/x64-inline-assembly/index.html new file mode 100644 index 00000000..d8457917 --- /dev/null +++ b/tags/x64-inline-assembly/index.html @@ -0,0 +1 @@ + x64-inline-assembly | Rayanfam Blog
Home Tags x64-inline-assembly
Tag
Cancel
diff --git a/tags/x86-and-x64-c/index.html b/tags/x86-and-x64-c/index.html new file mode 100644 index 00000000..6a2a1178 --- /dev/null +++ b/tags/x86-and-x64-c/index.html @@ -0,0 +1 @@ + x86-and-x64-c | Rayanfam Blog
Home Tags x86-and-x64-c
Tag
Cancel
diff --git a/tags/xmm-register/index.html b/tags/xmm-register/index.html new file mode 100644 index 00000000..1493495d --- /dev/null +++ b/tags/xmm-register/index.html @@ -0,0 +1 @@ + xmm-register | Rayanfam Blog
Home Tags xmm-register
Tag
Cancel
diff --git a/tags/xrefs-ida-python/index.html b/tags/xrefs-ida-python/index.html new file mode 100644 index 00000000..a02464a0 --- /dev/null +++ b/tags/xrefs-ida-python/index.html @@ -0,0 +1 @@ + xrefs-ida-python | Rayanfam Blog
Home Tags xrefs-ida-python
Tag
Cancel
diff --git a/tags/xtables-addons/index.html b/tags/xtables-addons/index.html new file mode 100644 index 00000000..ad3a7c07 --- /dev/null +++ b/tags/xtables-addons/index.html @@ -0,0 +1 @@ + xtables-addons | Rayanfam Blog
Home Tags xtables-addons
Tag
Cancel
diff --git a/tags/xtables/index.html b/tags/xtables/index.html new file mode 100644 index 00000000..388c7a1e --- /dev/null +++ b/tags/xtables/index.html @@ -0,0 +1 @@ + xtables | Rayanfam Blog
Home Tags xtables
Tag
Cancel
diff --git a/tags/ymm-registers/index.html b/tags/ymm-registers/index.html new file mode 100644 index 00000000..c4fcb3c0 --- /dev/null +++ b/tags/ymm-registers/index.html @@ -0,0 +1 @@ + ymm-registers | Rayanfam Blog
Home Tags ymm-registers
Tag
Cancel
diff --git a/tags/zero-thread/index.html b/tags/zero-thread/index.html new file mode 100644 index 00000000..03be5690 --- /dev/null +++ b/tags/zero-thread/index.html @@ -0,0 +1 @@ + zero-thread | Rayanfam Blog
Home Tags zero-thread
Tag
Cancel
diff --git a/tools/index.html b/tools/index.html new file mode 100644 index 00000000..3bef5186 --- /dev/null +++ b/tools/index.html @@ -0,0 +1 @@ + Tools & Scripts | Rayanfam Blog
Home Tools & Scripts
Tools & Scripts
Cancel

Tools & Scripts

HyperDbg Debugger

HyperDbg debugger is an open-source, hypervisor-assisted user-mode, and kernel-mode Windows debugger with a focus on using hardware technologies.

https://hyperdbg.org

Windbg2IDA Plugin

https://windbg2ida.ntdebug.com

Windbg2ida lets you dump each step (instruction) in Windbg then give you a dump file and you can use it later in your IDA to put color on each line of the instructions that you’ve run to show code coverage.

https://rayanfam.com/topics/vmcsauditor-a-bochs-based-hypervisor-layout-checker/

https://rayanfam.com/topics/pinitor/

diff --git a/topics/a-new-anti-ransomware-idea/index.html b/topics/a-new-anti-ransomware-idea/index.html new file mode 100644 index 00000000..4d5ede84 --- /dev/null +++ b/topics/a-new-anti-ransomware-idea/index.html @@ -0,0 +1 @@ + A New Anti Ransomware Idea | Rayanfam Blog
Home A New Anti Ransomware Idea
Post
Cancel

A New Anti Ransomware Idea

In the last few days, I was asked to give a new idea for creating an anti ransomware and now I wanna share my idea and source codes.

The Full Source Code Is Available at : https://github.com/SinaKarvandi/Redemption-Anti-Ransomware/

Introduction

In the raise of computers in this century and as they largely used in transferring and storing sensitive data, Ransomware is a big danger which can compromise everything in a blink of an eye and causes huge loss of data or money,

according to Wikipedia:

Ransomware is computer malware that installs covertly on a victim’s device (e.g., computer, smartphone, wearable device) and that either mounts the cryptoviral extortion attack from cryptovirology that holds the victim’s data hostage, or mounts a cryptovirology leakware attack that threatens to publish the victim’s data, until a ransom is paid. Simple ransomware may lock the system in a way which is not difficult for a knowledgeable person to reverse, and display a message requesting payment to unlock it. More advanced malware encrypts the victim’s files, making them inaccessible, and demands a ransom payment to decrypt them. The ransomware may also encrypt the computer’s Master File Table (MFT) or the entire hard drive. Thus, ransomware is a denial-of-access attack that prevents computer users from accessing files since it is intractable to decrypt the files without the decryption key. Ransomware attacks are typically carried out using a Trojan that has a payload disguised as a legitimate file.

This kind of malware is dependent on Cryptography functions and I/O activities as a part of their natures and this dependency could give us a really good resource for control and audit to prevent such this activities.

The bad thing is that there is no permanent method to avoid such a malware (and it seems never be) because of billions of I/O activities that take place asynchronously in a system but being innovative can give security researchers an opportunity to evacuate a vast amount of ransomware and make it really hard and sometimes nearly impossible for bad guys to reach to their target, but as mentioned before there are many sophisticated way that could circumvent and bypass this protections. In the following article , we will show you our method of controlling ransomware which is a combination of previously invented methods and new methods to improve the detection and dismissing ransomware.

Assumption

We build our new method based on this assumptions:

1. Ransomware encrypts data to be decrypt in future.

Even our implementation prevents Denial of Service Attack (e.g. ransomware which encrypts data and delete the key without any plan to recover and decrypt in future.) but the nature of ransomware is that it reads data and it encrypt then deleteS or changeS the original data to cause a Denial of Access.

This assumption is the base idea to prevent ransomware in the case of our implementation, we don’t see any bad behavior in just reading the data. (as it itself could bring some bypasses for instance Signed Drivers which encrypt and modify data from base cannot be prevented in our method but it is somehow impossible for ransomware to introduce themselves as a Signed Driver in Amd64 structured systems even there are some bypasses for this prevention too !).

2. We assume that the users give us the highest available privilege and it is clear that Windows definitely prevents us from access the higher privilege ransomware.

3. Ransomware is not a Driver Process.

If the user system infected by Driver Ransomware it can’t prevented by no one as it goes to zero rop of kernel and it can even bypass Windows PatchGuard.

Fortunately there is no easy way to access this rop and there are many complicated mechanism to prevent this behavior and there is no major ransomware which reaches to zero rop of kernel by now.

4. We assume that the only way to access files is ReadFile and WriteFile as described in the resource section.

There are tons of other ways to write to files (e.g. Native API’s which must of them implemented in ntdll.dll likeNtOpenFile(…) and NtReadFile(…) which can also be used to access files.) but the audit is just on the two major above functions and you can understand that this method could be implemented to all other functions if you have a complete list of file accessing functions but it wants time to test and improve to make a reliable anti ransomware.

5. Ransomware are only interested in special kinds of file.

We make more than 450 formats of extensions (that could be developed by users easily) which ransomware are more interested to encrypt, the experience shows that ransomware only encrypts special kinds of extensions because if they encrypt computer completely, it could cause an early Denial of Service which could apprise user before the encryption cycle complete and prevent ransomware from any further encryption which causes it’s mission to be unaccomplished.

The extensions that we audit by default is listed below.

Audit Extension

.yuv, .ycbcra, .xis, .x3f, .x11, .wpd, .tex, .sxg, .stx, .st8, .st5, .srw, .srf, .sr2, .sqlitedb, .sqlite3, .sqlite, .sdf, .sda, .sd0, .s3db, .rwz, .rwl, .rdb, .rat, .raf, .qby, .qbx, .qbw, .qbr, .qba, .py, .psafe3, .plc, .plus_muhd, .pdd, .p7c, .p7b, .oth, .orf, .odm, .odf, .nyf, .nxl, .nx2, .nwb, .ns4, .ns3, .ns2, .nrw, .nop, .nk2, .nef, .ndd, .myd, .mrw, .moneywell, .mny, .mmw, .mfw, .mef, .mdc, .lua, .kpdx, .kdc, .kdbx, .kc2, .jpe, .incpas, .iiq, .ibz, .ibank, .hbk, .gry, .grey, .gray, .fhd, .fh, .ffd, .exf, .erf, .erbsql, .eml, .dxg, .drf, .dng, .dgc, .des, .der, .ddrw, .ddoc, .dcs, .dc2, .db_journal, .csl, .csh, .crw, .craw, .cib, .ce2, .ce1, .cdrw, .cdr6, .cdr5, .cdr4, .cdr3, .bpw, .bgt, .bdb, .bay, .bank, .backupdb, .backup, .back, .awg, .apj, .ait, .agdl, .ads, .adb, .acr, .ach, .accdt, .accdr, .accde, .ab4, .3pr, .3fr, .vmxf, .vmsd, .vhdx, .vhd, .vbox, .stm, .st7, .rvt, .qcow, .qed, .pif, .pdb, .pab, .ost, .ogg, .nvram, .ndf, .m4p, .m2ts, .log, .hpp, .hdd, .groups, .flvv, .edb, .dit, .dat, .cmt, .bin, .aiff, .xlk, .wad, .tlg, .st6, .st4, .say, .sas7bdat, .qbm, .qbb, .ptx, .pfx, .pef, .pat, .oil, .odc, .nsh, .nsg, .nsf, .nsd, .nd, .mos, .indd, .iif, .fpx, .fff, .fdb, .dtd, .design, .ddd, .dcr, .dac, .cr2, .cdx, .cdf, .blend, .bkp, .al, .adp, .act, .xlr, .xlam, .xla, .wps, .tga, .rw2, .r3d, .pspimage, .ps, .pct, .pcd, .m4v, .fxg, .flac, .eps, .dxb, .drw, .dot, .db3, .cpi, .cls, .cdr, .arw, .ai, .aac, .thm, .srt, .save, .safe, .rm, .pwm, .pages, .obj, .mlb, .md, .mbx, .lit, .laccdb, .kwm, .idx, .html, .flf, .dxf, .dwg, .dds, .csv, .css, .config, .cfg, .cer, .asx, .aspx, .aoi, .accdb, .7zip, .1cd, .xls, .wab, .rtf, .prf, .ppt, .oab, .msg, .mapimail, .jnt, .doc, .dbx, .contact, .n64, .m4a, .m4u, .m3u, .mid, .wma, .flv, .3g2, .mkv, .3gp, .mp4, .mov, .avi, .asf, .mpeg, .vob, .mpg, .wmv, .fla, .swf, .wav, .mp3, .qcow2, .vdi, .vmdk, .vmx, .wallet, .upk, .sav, .re4, .ltx, .litesql, .litemod, .lbf, .iwi, .forge, .das, .d3dbsp, .bsa, .bik, .asset, .apk, .gpg, .aes, .ARC, .PAQ, .tar.bz2, .tbk, .bak, .tar, .tgz, .gz, .7z, .rar, .zip, .djv, .djvu, .svg, .bmp, .png, .gif, .raw, .cgm, .jpeg, .jpg, .tif, .tiff, .NEF, .psd, .cmd, .bat, .sh, .class, .jar, .java, .rb, .asp, .cs, .brd, .sch, .dch, .dip, .pl, .vbs, .vb, .js, .asm, .pas, .cpp, .php, .ldf, .mdf, .ibd, .MYI, .MYD, .frm, .odb, .dbf, .db, .mdb, .sql, .SQLITEDB, .SQLITE3, .011, .010, .009, .008, .007, .006, .005, .004, .003, .002, .001, .pst, .onetoc2, .asc, .lay6, .lay, .ms11 (Security copy), .ms11, .sldm, .sldx, .ppsm, .ppsx, .ppam, .docb, .mml, .sxm, .otg, .odg, .uop, .potx, .potm, .pptx, .pptm, .std, .sxd, .pot, .pps, .sti, .sxi, .otp, .odp, .wb2, .123, .wks, .wk1, .xltx, .xltm, .xlsx, .xlsm, .xlsb, .slk, .xlw, .xlt, .xlm, .xlc, .dif, .stc, .sxc, .ots, .ods, .hwp, .602, .dotm, .dotx, .docm, .docx, .DOT, .3dm, .max, .3ds, .xml, .txt, .CSV, .uot, .RTF, .pdf, .XLS, .PPT, .stw, .sxw, .ott, .odt, .DOC, .pem, .p12, .csr, .crt, .key

Please note that the users can add their own extensions on the above list.

Implementation

In a non-technical point of view, we create honeypots all over the computer and scan for interested files in directories to make a Critical Zone Table (CZT).

The content of CZT should be safe from massive changes, if an application edited or deleted a content in CZT the Watchdog process immediately put that directory in Critical Mode (CM) and then searches for all the processes in Windows to find all of the available File Handles from the files which is in Critical Mode Folder (CMF), now if any process (which probably is the last one that access to directory) has any File Handle to files in CMF is attacked by Watchdog, even this a good way for prevent ransomware but it could bring us a vast amount of false positive result.

So the Watchdog doesn’t suspend or Kill the process but inject itself into that process and replace all the CreateFile , OpenFile , DeleteFile and all other stuffs which could cause the loss of data in Hard Disk.

So now the Watchdog is responsible for all changes in Hard Disk and Watchdog’s overrided functions is called before any changes and it do a Back up for all the deleted files and modified files that has over 50% of changes in their body in a safe place and then performs the application’s modification to avoid any error in application cycle or any false positive in result.

This backup files are saved for 10 days then deleted automatically to prevent extra charges in hard disk.

If any ransomware encrypts the data in CZT then the data is available by Anti Ransomware and could be backed.

This method gives a lot of interesting result because the ransomware still thinks that it successfully encrypted the data and send attacker a successful message but the encrypted files are backed up in real time and that attacker might think that ransomware’s mission is accomplished but the owner could have their raw file immediately without any concern.

The second methodology is Honeypot.

Honeypots are a good way for detecting ransomware cuz there is no way for general ransomware to understand which file is a truly document and which is in fact a honeypot. (This can be bypass when a ransomware has a special target and just attempt to encrypt that target and nothing more which in this scenario it could be prevented by first methodology.).

The approach of Honeypot methodology is to Suspend and Kill then delete every process which wants to modify Honeypots.

Even this method can be bypassed but it’s still a good and precise way for detecting ransomware, because there is no reason for any process to modify the files that doesn’t interfere with them and you can be more than one hundred percent sure that it wants to do something evil.

Technical Preview

Even the best method for implementing this approach is to write a customized file system driver but the lack of signing causes us to bring another solution.

We use FileSystemWatcher which is .Net Framework library to detect any modification in a CZT.

When anything detected no one can give us any hint that what process was responsible for that change then we use NtSystemInfo which use Windows API NTQuerySystemInformation for searching and enumerating handles form all available Windows Processes then if we find anything, inject our customized function to that process but if not detected anything then continue listening for a while and if it continues without detection the CMF flag is disappeared and shows that no process wants to change any other file in CMF.

Modification Kinds

In this methodology we just enumerate delete and modify as a harmful alert for Watchdog.

Delete is always an alert but in the case of modification we simply compare the result before and after the modification and backup from files which has a large amount of modified content, this achieved by using SimMetric Libraries by implementing Byte similarities by Jaro–Winkler distance and Smith-Waterman string similarity algorithms and the average of this two algorithms gives us a number which 50% or more modification rings an alert for backup but less than this number is ignored.

Benefits

The best benefit for this method is it can make all process continue their cycles without any error.

False Positive result will cause users to disable their anti ransomware but the main idea behind this anti ransomware is to allow system work properly even it is infected with ransomware or with applications which are for good purpose but act like ransomware or have ransomware behavior which is common.

Conclusion

The method described above is a good way to prevent the majority of ransomware attacks but even there are lots of circumstances but it could prevent all massive changes in hard drive which is main idea of ransomware and it could defuse all general and massive encrypt purpose ransomware and make a real-time backup and let process continue without any false positive result that can be a reason to Denial of Service by some anti ransomware.

Resources

ReadFile: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365467(v=vs.85).aspx WriteFile: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365747(v=vs.85).aspx Import Address Table: https://en.wikipedia.org/wiki/Portable_Executable#Import_Table Similarity Test: https://asecuritysite.com/forensics/simstring Jaro-Winkler: https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance Waternan: https://en.wikipedia.org/wiki/Smith%E2%80%93Waterman_algorithm

 

Some Pictures from the UI

 

 

(Please note that it is just an idea and it doesn’t implemented to be a final heal for ransomwares, if you want to use this idea you’d better to hook native windows api by a driver.)

To download source code please visit : https://github.com/SinaKarvandi/Redemption-Anti-Ransomware/

This post is licensed under CC BY 4.0 by the author.

Kernel Mode Debugging by Windbg

Useful Configs for Squid3 Cache

Comments powered by Disqus.

diff --git a/topics/active-directory-certificate-services-migration/index.html b/topics/active-directory-certificate-services-migration/index.html new file mode 100644 index 00000000..d542112b --- /dev/null +++ b/topics/active-directory-certificate-services-migration/index.html @@ -0,0 +1,7 @@ + Active Directory Certificate Services Overview and Migration | Rayanfam Blog
Home Active Directory Certificate Services Overview and Migration
Post
Cancel

Active Directory Certificate Services Overview and Migration

Active Directory Certificate Services

Active Directory Certificate Services (AD CS) is a role in Windows Server which allows you to fully implement a PKI infrastructure. AD CS also provides templates and web services for issuance and management of certificates. In this guide we will cover basic concepts of AD CS and provide a method for migration of this service to another windows server .

This won’t be an in depth guide because you can find the information you need in official Microsoft books, Microsoft TechNet website or other blogs. I will start with an introduction and then explain the whole migration process, because a complete working walkthrough was missing when I originally wanted to perform the migration.


According to official docs, AD CS is capable of:

  • Certification authorities (CAs). Root and subordinate CAs are used to issue certificates to users, computers, and services, and to manage certificate validity.
  • Web enrollment. Web enrollment allows users to connect to a CA by means of a Web browser in order to request certificates and retrieve certificate revocation lists (CRLs).
  • Online Responder. The Online Responder service decodes revocation status requests for specific certificates, evaluates the status of these certificates, and sends back a signed response containing the requested certificate status information.
  • Network Device Enrollment Service. The Network Device Enrollment Service allows routers and other network devices that do not have domain accounts to obtain certificates.

It’s noteworthy that the true power of AD CS lies in the automation made possible by extensive powershell cmdlets, “Certificate Templates” and deep integration with AD DS which lets you build complex PKI infrastructure scenarios easily and securely.

AD CS can be installed in two mode:

  • Enterprise CA
  • Standalone CA

Enterprise CA needs a domain to be present. the server on which AD CS is installed will be registered to the domain as  PKI provider (that’s why AD CS deployment in Enterprise mode needs Enterprise Admin rights on the domain). This mode enables all the fancy automation stuff and deep integration into windows and Active Directory by means of GPOs or AD CS RPC.

Standalone CA is exactly what the name implies. It does not need AD DS and works on an standalone windows server perfectly. This mode is best used to issue certificates for websites or VPNs…

Final Notes

  1. IIS is installed on the server if you want to publish CRLs (sure you want that in production :) ). However you can specify a network path and have another webserver on the target share (even something like nginx).
  2. Network Device Enrollment Services (NDES) can work with Cisco Simple Certificate Enrollment Protocol (SCEP) and issue certificates to network devices (routers, firewalls, etc). These certificates are usually used for VPN. the CA is imported into trusted CA Certificate store in windows computers on the domain automatically, enabling users to connect to corporate VPNs easily and securely. Even client certificate can be issued automatically and installed on workstations so there will be no need of user/pass for VPN, increasing security and user-friendliness.
  3. Smartcards can also be leveraged in an AD CS environment. User logon information, as well as certificates installed from a CA server, can be placed on a smart card. When a user needs to log on to a system, he places the smart card in a smart card reader or simply swipes it across the reader itself. The certificate is read, and the user is prompted only for a PIN, which is uniquely assigned to each user. After the PIN and the certificate are verified, the user is logged on to the domain.
  4. Encrypted File System (EFS) keys can also be integrated in CA and deployed domain-wide, making encryption and decryption secure and painless.

  • For a guide about basic installation of AD CS and creating a template you can refer to this guide [Virtually boring] , I won’t be explaining it here again because you can easily find information about the basics of AD CS on the internet.

Active Directory Certificate Services Migration

This method was used to migrate a Windows server 2003 AD CS installation to Windows Server 2012 R2. Note that migration from Windows server 2003 to 2012 is not officially supported by Microsoft and therefore is the trickiest and the most problematic. So this guide will likely work on other versions too.

  1. Open AD CS management panel (Certificate Authority) and Backup CA by right-clicking the main entry on the tree-view.

Backup CA on ADCS

1
+
  2. Export the following registry key: 
+
1
+
HKLM\System\CurrentControlSet\Services\CertSvc\Configuration
+
  1. you can use this command :
1
+
reg export "HKLM\System\CurrentControlSet\Services\CertSvc\Configuration" c:\cabackupregistry.reg /y
+
  1. Remove AD CS Role from the original server (Windows Server 2003 here)
  2. Install AD CS Role on the destination server (Windows Server 2012 R2 here)
    • note that it should have already be joined to the domain.
    • During installation be careful to choose the option Existing Key and supply the backup files from step 1.
  3. Open Certificate Authority on destination server and choose Restore CA. (It’s right next to where you chose Backup CA in step 1) this will restore already issued or revoked certificate database and logs.
  4. Import the registry key you exported on original server to destination server. (move with USB flash drive or network share, then double click to install)
  5. Restart AD CS (reboot the server or Stop Service / Start Service on the right click menu on Certificate Authority tree-view).

Now you should have successfully migrated AD CS and all your older certificates should be valid too. I advice you to test the CA by issuing a certificate from a template now that migration is finished.

This post is licensed under CC BY 4.0 by the author.

Bochs Emulator - Debug & Instrument

PyKD Tutorial - part 1

Comments powered by Disqus.

diff --git a/topics/assembly-challenge-jump-to-a-non-relative-address-without-using-registers/index.html b/topics/assembly-challenge-jump-to-a-non-relative-address-without-using-registers/index.html new file mode 100644 index 00000000..19d4912c --- /dev/null +++ b/topics/assembly-challenge-jump-to-a-non-relative-address-without-using-registers/index.html @@ -0,0 +1,73 @@ + Assembly Challenge : Jump to a non-relative address without using registers | Rayanfam Blog
Home Assembly Challenge : Jump to a non-relative address without using registers
Post
Cancel

Assembly Challenge : Jump to a non-relative address without using registers

During developing a dispatch table for some instructions in binaries, one of the challenging problem which I faced, was changing the registers state in a way that doesn’t affect the program flow!

So it might seem simple at first glance but what makes it complex is that I can’t use relative jumps or relative calls because, in some situation, I might be far away from .text segment of my binary.

It causes me to explore the solutions about far jumps and far calls but actually, I realized that it can’t help me too.

One of the solutions was to put the final address in the stack, then use ret in order to change xip (rip or eip).

1
+2
+
push 0xdeadbeef
+ret
+

It is somehow a good solution, its fast and is really recommended, but the thing is I don’t want to change the stack state either! Even one of my friends told me that changing the above addresses of the stack doesn’t affect a regular compiler’s flow but I think it might be better, not to modify stack because I want to publish its source and it might cause the problem in abnormal programs in future.

And of course another solution was using registers like :

1
+2
+
mov %eax,0xdeadbeef
+jmp %eax
+

It’s clear that it causes nasty problems because the rest of program flow don’t know about %eax changes so it uses a wrong value and we can’t do any further modification.

Solution

I solved the above problem by using the following code, in at&t syntax (in x86):

1
+2
+
jmp *0f(%eip)
+0: .int 0x12345678
+

The above instruction, jumps to 0x12345678 in x86 and you can see the result of compiling and disassembling it :

1
+
Sinas-MBP:Desktop sina$ clang -c aa.asm -m32
+

And to Dissemble it using objdump use the following format :

1
+2
+3
+4
+5
+6
+7
+8
+9
+
Sinas-MBP:Desktop sina$ objdump -d aa.o
+
+aa.o: file format Mach-O 32-bit i386
+
+Disassembly of section __TEXT,__text:
+__text:
+0: ff 25 00 00 00 00 jmpl *0
+6: 78 56 js 86 <__text+0x5E>
+8: 34 12 xorb $18, %al
+

In the case of x64 version of above code you can use the rip instead of eip and change the int to quad because you need more space for x64 addressing.

1
+2
+
jmp *0f(%rip)
+0: .quad 0x1234567890
+
1
+
Sinas-MBP:Desktop sina$ clang -c aa.asm
+

To Dissemble it using objdump use the following format :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
Sinas-MBP:Desktop sina$ objdump -d aa.o
+
+aa.o: file format Mach-O 64-bit x86-64
+
+Disassembly of section __TEXT,__text:
+__text:
+0: ff 25 00 00 00 00 jmpq *(%rip)
+6: 90 nop
+7: 78 56 js 86 <__text+0x5F>
+9: 34 12 xorb $18, %al
+b: 00 00 addb %al, (%rax)
+d: 00 
+

The above code jumps to 0x1234567890.

The Problem Of Relative Conditional Jumps

By now, you might think of implementing the above jmp for j* instruction like je or jnb , but the thing is you can’t because we don’t have a conditional instruction to perform the above command.

So how can we solve this ?

Simply, use relative conditional jumps with a combination of above jmp, so that the conditional jump can do what it expects to base on the flags and then it can decide to jump over an instruction (in our case jump to a far address) or can perform the above jmp.

1
+2
+3
+4
+5
+
# for jz addr
+jnz 1f
+jmp *0f(%rip)
+0: .quad addr
+1:
+

Final Thoughts

The above post derived from my question in stack overflow here, which finally answered by fuz. Even if this solution solves my problem but it is not really a good and fast way to perform this jump, and Intel advice not to use such instructions because they are really slow in CPU clock execution cycles and because I should use these instructions billions of time in a simple binary, then the normal execution becomes much slower than what I expect. So If you have any better solution, then let me know about it.

This post is licensed under CC BY 4.0 by the author.

A first look at some aspects of Intel's "Vanderpool" initiative

Useful Configs for NGINX

Comments powered by Disqus.

diff --git a/topics/bind9-chroot-on-debian-8/index.html b/topics/bind9-chroot-on-debian-8/index.html new file mode 100644 index 00000000..beee870e --- /dev/null +++ b/topics/bind9-chroot-on-debian-8/index.html @@ -0,0 +1 @@ + Bind9 chroot on debian 8 | Rayanfam Blog
Home Bind9 chroot on debian 8
Post
Cancel

Bind9 chroot on debian 8

From Wikipedia:

BIND, or named, is the most widely used Domain Name System (DNS) software on the Internet. On Unix-like operating systems it is the de facto standard.

As you know chrooting a process is very beneficial for security as any compromise cannot have effect on the whole system. But be aware escaping from chroot is not impossible. and therefore should not be used as your only security measure on a production DNS resolver.

Chrooting Bind is simple, however there are not good HOWTOs, the good ones are all outdated.

So I made this Asciinema for “chrooting bind 9 in debian 8” (systemd)

[click on it]

Let me know of any inaccuracies or suggestions as usual :)

Shahriar

  • UPDATE : Thanks to Behrad Eslamifar for letting me know, This debian 8 package will also do the job if you don’t want to do it manually:   https://github.com/cvak/bind-chroot
This post is licensed under CC BY 4.0 by the author.

Change User-Mode application's virtual address through Kernel Debugging

Import Address Table (IAT) in action

Comments powered by Disqus.

diff --git a/topics/bochs-config-and-build-on-windows-and-os-x/index.html b/topics/bochs-config-and-build-on-windows-and-os-x/index.html new file mode 100644 index 00000000..1f0b4fbb --- /dev/null +++ b/topics/bochs-config-and-build-on-windows-and-os-x/index.html @@ -0,0 +1,493 @@ + Bochs Emulator - Config & Build on Windows and OS X | Rayanfam Blog
Home Bochs Emulator - Config & Build on Windows and OS X
Post
Cancel

Bochs Emulator - Config & Build on Windows and OS X

Introduction

Bochs is an x86 - x86-64 multiplatform emulator that provides emulation of CPU Instruction Fetching without using Hypervisor technologies like Intel’s VT-x and AMD-V.

One of the benefits of emulation rather than virtualization is that you can execute instructions of old discontinued CPU structures in modern CPU or run Intel’s instructions on an AMD CPU or vice versa, more than that Bochs developers try to make Bochs devices like virtualization software devices.They provide keyboard, speaker, cdrom, disk, floppy, network device and etc.

Emulation is somehow like high-level programming languages like Java when using its bytecode or .Net Framework when using MSIL.

From IBM :

Java technology implements just-in-time (JIT) compilation to work around some of the inefficiencies, similar to Transmeta and numerous other emulation solutions (including QEMU, another platform emulation solution similar to Bochs). But Bochs also achieves efficient emulation (up to 100 million instructions per second [MIPS] on a modern processor) while also being portable. Bochs was developed purely in the C++ language for interpreted x86 instruction execution and platform emulation.

Bochs

 

Because of instructions doesn’t directly execute on a Bochs machine, It is too much slower than CPU on virtualization so Bochs comes at the cost of performance.

Even though Bochs is really slow but it gives us other benefits that worths using the emulator like Instrumenting an operating system kernel which I described in Bochs Emulator - Debug & Instrument.

Bochs is an open-source program and in order to use some features like instrumentation and other optimization you need to compile it by your self, more than that you can edit the source code for researching because Bochs have lots of cool features that can be manipulated in order to better optimize your needs, so I prefer to compile it rather than downloading its binaries.

ok, that’s enough for introduction, let’s see how to “make” it!

In the rest of topic, I’m trying to build bochs-2.9.

Configure & Make in OS X

First, you need to download the project source code, you can find it here.

The documentation uses the following syntax in order to build the Bochs on OS X machines

1
+2
+
./configure
+make
+

But it gives me the following error after running “make”:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
Sinas-MacBook-Pro:bochs-2.6.9 sina$ make
+cd iodev && \
+	/Library/Developer/CommandLineTools/usr/bin/make  libiodev.a
+g++ -c  -I.. -I./.. -I../instrument/stubs -I./../instrument/stubs  -fpascal-strings -fno-common -Wno-four-char-constants -Wno-unknown-pragmas -Dmacintosh -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES    devices.cc -o devices.o
+
+...
+
+cdrom_osx.cc:194:18: error: assigning to 'char *' from incompatible type
+      'const char *'
+  if ((devname = strrchr(devpath, '/')) != NULL) {
+                 ^~~~~~~~~~~~~~~~~~~~~
+cdrom_osx.cc:231:15: warning: comparison between NULL and non-pointer
+      ('io_registry_entry_t' (aka 'unsigned int') and NULL) [-Wnull-arithmetic]
+  if (service == NULL) {
+      ~~~~~~~ ^  ~~~~
+cdrom_osx.cc:291:30: warning: implicit conversion of NULL constant to
+      'mach_port_t' (aka 'unsigned int') [-Wnull-conversion]
+    mach_port_t masterPort = NULL;
+                ~~~~~~~~~~   ^~~~
+                             0
+4 warnings and 1 error generated.
+make[1]: *** [cdrom_osx.o] Error 1
+make: *** [iodev/hdimage/libhdimage.a] Error 2
+

that’s a weird error! I don’t know why this happens but I simply go to “cdrom_osx.cc” file and change the following line:

1
+
 if ((devname = strrchr(devpath, '/')) != NULL) {
+

to:

1
+
  if ((devname = (char*)strrchr(devpath, '/')) != NULL) {
+

And simply ignore this problem.

Let’s “make” it again.

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
+
Sinas-MacBook-Pro:bochs-2.6.9 sina$ 
+
+...
+
+Sinas-MacBook-Pro:bochs-2.6.9 sina$ 
+
+...
+
+carbon.cc:155:1: error: unknown type name 'CIconHandle'
+CIconHandle     bx_cicn[BX_MAX_PIXMAPS];
+^
+carbon.cc:269:3: error: use of undeclared identifier 'GetWindowPortBounds'
+  GetWindowPortBounds(myWindow, &box);
+  ^
+carbon.cc:270:13: error: use of undeclared identifier 'blackColor'
+  BackColor(blackColor);
+            ^
+carbon.cc:271:3: error: use of undeclared identifier 'EraseRect'
+  EraseRect(&box);
+  ^
+carbon.cc:310:3: error: use of undeclared identifier 'GetWindowPortBounds'
+  GetWindowPortBounds(myWindow, &box);
+  ^
+carbon.cc:401:10: error: use of undeclared identifier 'FindWindow'
+  part = FindWindow(wheresMyMouse, &whichWindow);
+         ^
+carbon.cc:407:5: error: use of undeclared identifier 'MenuSelect'
+    MenuSelect(wheresMyMouse);
+    ^
+carbon.cc:443:7: error: use of undeclared identifier 'CreateStandardAlert'
+      CreateStandardAlert(
+      ^
+carbon.cc:450:7: error: use of undeclared identifier 'RunStandardAlert'
+      RunStandardAlert(
+      ^
+carbon.cc:474:11: error: use of undeclared identifier 'IsWindowVisible'
+      if (IsWindowVisible(toolwin))
+          ^
+carbon.cc:488:11: error: use of undeclared identifier 'IsWindowVisible'
+      if (IsWindowVisible(toolwin) || menubarVisible)
+          ^
+carbon.cc:492:13: error: use of undeclared identifier 'IsWindowVisible'
+        if (IsWindowVisible(toolwin))
+            ^
+carbon.cc:499:14: error: use of undeclared identifier 'IsWindowVisible'
+        if (!IsWindowVisible(toolwin))
+             ^
+carbon.cc:535:3: error: use of undeclared identifier 'StopAlert'
+  StopAlert(200, NULL);
+  ^
+carbon.cc:543:3: error: use of undeclared identifier 'InitCursor'
+  InitCursor();
+  ^
+carbon.cc:553:5: error: no matching function for call to 'NewAEEventHandlerUPP'
+    NewAEEventHandlerUPP(QuitAppleEventHandler), 0, false);
+    ^~~~~~~~~~~~~~~~~~~~
+/System/Library/Frameworks/CoreServices.framework/Frameworks/AE.framework/Headers/AEDataModel.h:2730:65: note: 
+      candidate function not viable: no known conversion from 'OSErr (const
+      AppleEvent *, AppleEvent *, SInt32)' (aka 'short (const AEDesc *, AEDesc
+      *, int)') to 'AEEventHandlerProcPtr' (aka 'short (*)(const AEDesc *,
+      AEDesc *, void *)') for 1st argument
+    inline AEEventHandlerUPP                                    NewAEEve...
+                                                                ^
+carbon.cc:555:5: warning: 'ExitToShell' is deprecated: first deprecated in macOS
+      10.9 [-Wdeprecated-declarations]
+    ExitToShell();
+    ^
+/System/Library/Frameworks/ApplicationServices.framework/Frameworks/HIServices.framework/Headers/Processes.h:725:6: note: 
+      'ExitToShell' has been explicitly marked deprecated here
+void ExitToShell( void ) __attribute__ (( __noreturn__ ))   AVAILABLE_MA...
+     ^
+carbon.cc:575:5: error: use of undeclared identifier 'GetGWorld'
+    GetGWorld(&savePort, &saveDevice);
+    ^
+carbon.cc:605:56: error: use of undeclared identifier 'keepLocal'
+      &srcTileRect, disp_bpp>8 ? NULL : gCTable, NULL, keepLocal, gMyBuf...
+                                                       ^
+carbon.cc:609:5: error: use of undeclared identifier 'SetGWorld'
+    SetGWorld(gOffWorld, NULL);
+    ^
+fatal error: too many errors emitted, stopping now [-ferror-limit=]
+1 warning and 20 errors generated.
+make[1]: *** [carbon.o] Error 1
+make: *** [gui/libgui.a] Error 2
+

That’s too bad! 20 errors.

I googled it and understand that this problem happens because the libraries that it used for it’s GUI is no longer available for OS X, so the problem can be solved using SDL instead.

Now you need to download and install SDL.

1
+
brew install sdl
+

Then compile the Bochs using the following syntax.

1
+2
+
./configure --with-sdl
+make
+

Even though this problem is reported to Bochs developers but it seems none of them uses OS X, I’m sure this problem will be solved in the future versions of Bochs.

Finally, it compiles without error, but actually, I want to use more features of Bochs like disasm and debug features and instrumentation and etc.

I use the following syntax to compile the Bochs:

1
+2
+
./configure --enable-cpu-level=6 --enable-x86-64 --enable-vmx=2 --enable-pci --enable-usb --enable-usb-ohci --enable-debugger --enable-disasm --with-sdl --enable-instrumentation="instrument/stubs" --enable-cdrom
+make
+

If there isn’t any problem, you can now run the Bochs.

This time, when I run Bochs it gives me the following error:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
Sinas-MacBook-Pro:bochs-2.6.9 sina$ bochs 
+========================================================================
+                       Bochs x86 Emulator 2.6.9
+               Built from SVN snapshot on April 9, 2017
+                  Compiled on Apr 15 2018 at 14:58:06
+========================================================================
+00000000000i[      ] BXSHARE not set. using compile time default '/usr/local/share/bochs'
+00000000000i[      ] reading configuration from .bochsrc
+00000000000e[      ] .bochsrc:187: wrong value for parameter 'model'
+00000000000p[      ] >>PANIC<< .bochsrc:187: cpu directive malformed.
+00000000000e[SIM   ] notify called, but no bxevent_callback function is registered
+========================================================================
+Bochs is exiting with the following message:
+[      ] .bochsrc:187: cpu directive malformed.
+========================================================================
+00000000000i[SIM   ] quit_sim called with exit code 1
+

Actually, this problem happens because the CPU architecture that selected in “.bochsrc” file is not available in the current built.

You should run the following command in order to get the supported CPUs:

1
+
bochs --help cpu
+

Then I modified the following line:

1
+
cpu: model=core2__penryn__t9600, count=1, ips=50000000, reset__on__triple__fault=1, ignore__bad__msrs=1, msrs="msrs.def"
+

change the “core2_penryn_t9600” to e.g “core_duo_t2400_yonah”.

Running bochs again produces such error :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
Sinas-MacBook-Pro:bochs-2.6.9 sina$ bochs 
+========================================================================
+                       Bochs x86 Emulator 2.6.9
+               Built from SVN snapshot on April 9, 2017
+                  Compiled on Apr 15 2018 at 14:58:06
+========================================================================
+00000000000i[      ] BXSHARE not set. using compile time default '/usr/local/share/bochs'
+00000000000i[      ] reading configuration from .bochsrc
+00000000000e[      ] .bochsrc:718: ataX-master/slave CHS set to 0/0/0 - autodetection enabled
+00000000000p[      ] >>PANIC<< .bochsrc:914: Bochs is not compiled with lowlevel sound support
+00000000000e[SIM   ] notify called, but no bxevent_callback function is registered
+========================================================================
+Bochs is exiting with the following message:
+[      ] .bochsrc:914: Bochs is not compiled with lowlevel sound support
+========================================================================
+00000000000i[SIM   ] quit_sim called with exit code 1
+

It is because we didn’t configure Bochs for preparing sound device and we use “sound” option in “.bochsrc”, in order to build with sound support you can use one of the “–enable-sb16” or “–enable-es1370” in configuring and “make” again but I rather remove the following line from “.bochsrc” to build it again.

1
+
sound: driver=default, waveout=/dev/dsp. wavein=, midiout=
+

Now it runs without error.

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
+
Sinas-MacBook-Pro:bochs-2.6.9 sina$ bochs 
+========================================================================
+                       Bochs x86 Emulator 2.6.9
+               Built from SVN snapshot on April 9, 2017
+                  Compiled on Apr 15 2018 at 14:58:06
+========================================================================
+00000000000i[      ] BXSHARE not set. using compile time default '/usr/local/share/bochs'
+00000000000i[      ] reading configuration from .bochsrc
+00000000000e[      ] .bochsrc:718: ataX-master/slave CHS set to 0/0/0 - autodetection enabled
+00000000000e[      ] .bochsrc:925: wrong value for parameter 'mode'
+00000000000e[PCSPK ] .bochsrc:925: unknown parameter for speaker ignored.
+------------------------------
+Bochs Configuration: Main Menu
+------------------------------
+
+This is the Bochs Configuration Interface, where you can describe the
+machine that you want to simulate.  Bochs has already searched for a
+configuration file (typically called bochsrc.txt) and loaded it if it
+could be found.  When you are satisfied with the configuration, go
+ahead and start the simulation.
+
+You can also start bochs with the -q option to skip these menus.
+
+1. Restore factory default configuration
+2. Read options from...
+3. Edit options
+4. Save options to...
+5. Restore the Bochs state from...
+6. Begin simulation
+7. Quit now
+
+Please choose one: [6]
+

Building Bochs on Windows

To build Bochs for Windows you have multiple options, like building with Cygwin or MinGW but I’d rather build Bochs with Visual Studio instead.

Download (bochs-x.x.x-msvc-src.zip) latest version from here.

Extract the bochs-x.x.x-msvc-src.zip go to vs2013 and open “bochs.sln”.

You need to enable some of the features before building the solution. (It’s like ./configure –enable-x in OS X and Linux)

Go to “config.h” and modify it like this:

In order to enable debugging make sure your config file is :

1
+2
+3
+4
+
#define BX__GDBSTUB 1
+#define BX__DEBUGGER 1
+#define BX__DISASM 1
+#define BX__DEBUGGER__GUI 1
+

I also need Instrumentation so changed the following line:

1
+
#define BX__INSTRUMENTATION 1
+

make sure disable BX_SUPPORT_HANDLERS_CHAINING_SPEEDUPS because of some incompatibility issues.

1
+
#define BX__SUPPORT__HANDLERS__CHAINING__SPEEDUPS 0
+

Then trying to build and this time it gives me the following error:

1
+
unresolved external symbol ____imp____WSAStartup@8 referenced in function __main	
+

And other such errors which are linking problems.

I add the following line to bxhub.cc :

1
+
#pragma comment(lib,"Ws2__32.lib")
+

That line solves the linking problems. If there isn’t any error then just build the solution.

Installing Windows on Bochs

Installing an operating system in Bochs is a little tricky, open bochs.exe (from what you built before.) then make sure that it starts without any problem (some systems have problem opening Bochs !).

Bochs uses an image file for the purpose of its hard disk, there is a tool called bximage.exe which creates such file for you.

Open bximage.exe :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+
========================================================================
+                                bximage
+  Disk Image Creation / Conversion / Resize and Commit Tool for Bochs
+         $Id: bximage.cc 13069 2017-02-12 16:51:52Z vruppert $
+========================================================================
+
+1. Create new floppy or hard disk image
+2. Convert hard disk image to other format (mode)
+3. Resize hard disk image
+4. Commit 'undoable' redolog to base image
+5. Disk image info
+
+0. Quit
+
+Please choose one [0]
+

Then choose 1 (Create new floppy or hard disk image). In the second step choose hd for device type, growing for the next question and then choose how much space you need to allocate to your emulated machine.

In the last step specify a name and you’re good to go!

A complete result of bximage is like :

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
+
========================================================================
+                                bximage
+  Disk Image Creation / Conversion / Resize and Commit Tool for Bochs
+         $Id: bximage.cc 13069 2017-02-12 16:51:52Z vruppert $
+========================================================================
+
+1. Create new floppy or hard disk image
+2. Convert hard disk image to other format (mode)
+3. Resize hard disk image
+4. Commit 'undoable' redolog to base image
+5. Disk image info
+
+0. Quit
+
+Please choose one [0] 1
+
+Create image
+
+Do you want to create a floppy disk image or a hard disk image?
+Please type hd or fd. [hd] hd
+
+What kind of image should I create?
+Please type flat, sparse, growing, vpc or vmware4. [flat] growing
+
+Enter the hard disk size in megabytes, between 10 and 8257535
+[10] 10000
+
+What should be the name of the image?
+[c.img] c.img
+
+Creating hard disk image 'c.img' with CHS=20317/16/63
+redolog : creating redolog c.img
+redolog : Standard Header : magic='Bochs Virtual HD Image', type='Redolog', subtype='Growing', version = 2.0
+redolog : Specific Header : #entries=32768, bitmap size=128, exent size = 524288 disk size = 10485522432
+
+The following line should appear in your bochsrc:
+  ata0-master: type=disk, path="c.img", mode=growing
+(The line is stored in your windows clipboard, use CTRL-V to paste)
+
+Press any key to continue
+

After creating the image file, now you need to configure your Bochs machine.

Open the Bochs.exe and edit the memory option.

Set the memory size (megabytes) and host allocated size (megabytes) to a value greater than 32!  I choose 1000.

Then edit CPU option > CPU Configuration to turion64_tyler or whatever supports x86-64, make sure to set a proper value for Emulated instructions per second (IPS). Press OK and edit Disk & Boot > ATA Channel > First HD/CD on channel 0. In this window set Type of ATA device to disk, set path or physical device name to the c.img (the file that is created previously by bximage), Type of disk image to growing, Cylinders to 20317, heads to 16, Sectors per track to 63.

Now go to another tab (Second HD/CD on channel 0) and set the type of disk to cdrom, Path or physical device name to the Windows ISO file.

For the last step go to Boot Options, set Boot drive #1 to disk and Boot drive #2 to cdrom.

That’s it, make sure to save your configuration to avoid doing these steps again and then press start.

Select continue and don’t ask again if a prompt comes to the screen then restart the machine and you should see the Windows installation.

If you see the following picture, then you’re done.

final-bochs

By now, you should’ve configured Bochs, Let’s use more features of Bochs in the next post. (Bochs Emulator - Debug & Instrument).

References

This post is licensed under CC BY 4.0 by the author.

A partial survey among non-general purpose registers

Bochs Emulator - Debug & Instrument

Comments powered by Disqus.

diff --git a/topics/bochs-emulator-debug-and-instrument/index.html b/topics/bochs-emulator-debug-and-instrument/index.html new file mode 100644 index 00000000..0f23b28e --- /dev/null +++ b/topics/bochs-emulator-debug-and-instrument/index.html @@ -0,0 +1,217 @@ + Bochs Emulator - Debug & Instrument | Rayanfam Blog
Home Bochs Emulator - Debug & Instrument
Post
Cancel

Bochs Emulator - Debug & Instrument

There is also another post about configuring and building Bochs on Windows & OS X if you have a problem compiling Bochs, take a look at here!

In my opinion, Bochs is an amazing thing because it provides instrumentation in the lowest level of the Operating System. One of the advantages of the Boch is being able to instrument in kernel-mode, which is not available in other instrumenting tools like Intel’s pin tool.

You can see how to interact with Bochs debugger here. It’s somehow like Windbg in its syntax, if you enabled the debugger feature during the compilation then after running the OS, you can press ctrl+c and it gives you a command-line interface.

In the rest of the post, I’m gonna explain about instrumentation.

Instrumenting in Bochs is depending on the following functions that exist in /stubs/ instrument.cc.

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
+
#include "bochs.h"
+
+#if BX_INSTRUMENTATION
+
+void bx_instr_init_env(void) {}
+void bx_instr_exit_env(void) {}
+
+void bx_instr_initialize(unsigned cpu) {}
+void bx_instr_exit(unsigned cpu) {}
+void bx_instr_reset(unsigned cpu, unsigned type) {}
+void bx_instr_hlt(unsigned cpu) {}
+void bx_instr_mwait(unsigned cpu, bx_phy_address addr, unsigned len, Bit32u flags) {}
+
+void bx_instr_debug_promt() {}
+void bx_instr_debug_cmd(const char *cmd) {}
+
+void bx_instr_cnear_branch_taken(unsigned cpu, bx_address branch_eip, bx_address new_eip) {}
+void bx_instr_cnear_branch_not_taken(unsigned cpu, bx_address branch_eip) {}
+void bx_instr_ucnear_branch(unsigned cpu, unsigned what, bx_address branch_eip, bx_address new_eip) {}
+void bx_instr_far_branch(unsigned cpu, unsigned what, Bit16u prev_cs, bx_address prev_eip, Bit16u new_cs, bx_address new_eip) {}
+
+void bx_instr_opcode(unsigned cpu, bxInstruction_c *i, const Bit8u *opcode, unsigned len, bx_bool is32, bx_bool is64) {}
+
+void bx_instr_interrupt(unsigned cpu, unsigned vector) {}
+void bx_instr_exception(unsigned cpu, unsigned vector, unsigned error_code) {}
+void bx_instr_hwinterrupt(unsigned cpu, unsigned vector, Bit16u cs, bx_address eip) {}
+
+void bx_instr_tlb_cntrl(unsigned cpu, unsigned what, bx_phy_address new_cr3) {}
+void bx_instr_clflush(unsigned cpu, bx_address laddr, bx_phy_address paddr) {}
+void bx_instr_cache_cntrl(unsigned cpu, unsigned what) {}
+void bx_instr_prefetch_hint(unsigned cpu, unsigned what, unsigned seg, bx_address offset) {}
+
+void bx_instr_before_execution(unsigned cpu, bxInstruction_c *i) {}
+void bx_instr_after_execution(unsigned cpu, bxInstruction_c *i) {}
+void bx_instr_repeat_iteration(unsigned cpu, bxInstruction_c *i) {}
+
+void bx_instr_inp(Bit16u addr, unsigned len) {}
+void bx_instr_inp2(Bit16u addr, unsigned len, unsigned val) {}
+void bx_instr_outp(Bit16u addr, unsigned len, unsigned val) {}
+
+void bx_instr_lin_access(unsigned cpu, bx_address lin, bx_address phy, unsigned len, unsigned memtype, unsigned rw) {}
+void bx_instr_phy_access(unsigned cpu,                 bx_address phy, unsigned len, unsigned memtype, unsigned rw) {}
+
+void bx_instr_wrmsr(unsigned cpu, unsigned addr, Bit64u value) {}
+
+void bx_instr_vmexit(unsigned cpu, Bit32u reason, Bit64u qualification) {}
+
+#endif
+

For using Bochs instrumentation, first, you need to configure Bochs with the following argument:

1
+
./configure --enable-instrumentation="instrument/stubs"
+

Then you can change the above file and compile your project again and run Bochs with its debugger feature then Bochs automatically sets your function as a callback to its main CPU emulation functions and every time, one of the above functions performed in the target machine, then you’ll be aware.

The best reference for describing the above functions is Instrument.txt which exists under /instrument/Instrument.txt, I copied the newest version of Instrument.txt (at the time of writing this post), you can see the below file :

Instrumentation

To use instrumentation features in bochs, you must compile in support for it. You should build a custom instrumentation library in a separate directory in the “instrument/” directory. To tell configure which instrumentation library you want to use, use the “–enable-instrumentation” option. The default library consists of a set of stubs, and the following are equivalent:

1
+2
+
 ./configure [...] --enable-instrumentation
+ ./configure [...] --enable-instrumentation="instrument/stubs"
+

You could make a separate directory with your custom library, for example “instrument/myinstrument”, copy the contents of the “instrument/stubs” directory to it, then customize it. Use:

1
+
./configure [...] --enable-instrumentation="instrument/myinstrument"
+

BOCHS instrumentation callbacks

1
+
 void bx_instr_init_env();
+

The callback is called when Bochs is initialized, before of reading .bochsrc. It can be used for registration of parameters in siminterface. Then when bx_instr_init() is called it can access configuration parameters defined by bx_instr_init_env(), so instrumentalization module can use additional options in .bochsrc.

1
+
 void bx_instr_exit_env();
+

The callback is called each time Bochs exits.

1
+
 void bx_instr_initialize(unsigned cpu);
+

The callback is called each time, when Bochs initializes the CPU object. It can be used for initialization of user’s data, dynamic memory allocation and etc.

1
+
 void bx_instr_exit(unsigned cpu);
+

The callback is called each time, when Bochs destructs the CPU object. It can be used for destruction of user’s data, allocated by bx_instr_init callback.

1
+
 void bx_instr_reset(unsigned cpu, unsigned type);
+

The callback is called each time, when Bochs resets the CPU object. It would be executed once at the start of simulation and each time that user presses RESET BUTTON on the simulator’s control panel.

1
+
 void bx_instr_hlt(unsigned cpu);
+

The callback is called each time, when Bochs’ emulated CPU enters HALT or SHUTDOWN state.

1
+
 void bx_instr_mwait(unsigned cpu, bx_phy_address addr, unsigned len, Bit32u flags);
+

The callback is called each time, when Bochs’ emulated CPU enters to the MWAIT state. The callback receives monitored memory range and MWAIT flags as a parameters.

1
+
 void bx_instr_cnear_branch_taken(unsigned cpu, bx_address branch_rip, bx_address new_rip);
+

The callback is called each time, when currently executed instruction is a conditional near branch and it is taken.

1
+
 void bx_instr_cnear_branch_not_taken(unsigned cpu, bx_address branch_rip);
+

The callback is called each time, when currently executed instruction is a conditional near branch and it is not taken.

1
+
 void bx_instr_ucnear_branch(unsigned cpu, unsigned what, bx_address branch_rip, bx_address new_rip);
+

The callback is called each time, when currently executed instruction is an unconditional near branch (always taken).

1
+
 void bx_instr_far_branch(unsigned cpu, unsigned what, Bit16u prev_cs, bx_address prev_rip, Bit16u new_cs, bx_address new_rip);
+

The callback is called each time, when currently executed instruction is an unconditional far branch (always taken).

Possible operation types, passed through bx_instr_ucnear_branch and bx_instr_far_branch are:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
 #define BX_INSTR_IS_JMP 10
+ #define BX_INSTR_IS_JMP_INDIRECT 11
+ #define BX_INSTR_IS_CALL 12
+ #define BX_INSTR_IS_CALL_INDIRECT 13
+ #define BX_INSTR_IS_RET 14
+ #define BX_INSTR_IS_IRET 15
+ #define BX_INSTR_IS_INT 16
+ #define BX_INSTR_IS_SYSCALL 17
+ #define BX_INSTR_IS_SYSRET 18
+ #define BX_INSTR_IS_SYSENTER 19
+ #define BX_INSTR_IS_SYSEXIT 20
+
1
+
 void bx_instr_vmexit(unsigned cpu, Bit32u reason, Bit64u qualification);
+

This callback is called right before Bochs executes a VMEXIT.

1
+
 void bx_instr_opcode(unsigned cpu, bxInstruction_c *i, const Bit8u *opcode, unsigned len, bx_bool is32, bx_bool is64);
+

The callback is called each time, when Bochs completes to decode a new instruction. Through this callback function Bochs could provide an opcode of the instruction, opcode length and an execution mode (16/32/64).

Note, that Bochs uses translation caches so each simulated instruction might be executed multiple times but decoded only once.

1
+
 void bx_instr_interrupt(unsigned cpu, unsigned vector);
+

The callback is called each time, when Bochs simulator executes an interrupt (software interrupt, hardware interrupt or an exception).

1
+
 void bx_instr_exception(unsigned cpu, unsigned vector, unsigned error_code);
+

The callback is called each time, when Bochs simulator executes an exception.

1
+
 void bx_instr_hwinterrupt(unsigned cpu, unsigned vector, Bit16u cs, bx_address rip);
+

The callback is called each time, when Bochs simulator executes a hardware interrupt.

1
+
 void bx_instr_clflush(unsigned cpu, bx_address laddr, bx_phy_address paddr);
+

The callback is called each time the CLFLUSH instruction is executed.

1
+2
+
 void bx_instr_tlb_cntrl(unsigned cpu, unsigned what, bx_phy_address new_cr_value);
+ void bx_instr_cache_cntrl(unsigned cpu, unsigned what);
+

The callback is called each time, when Bochs simulator executes a cache/tlb control instruction.

Possible instruction types, passed through bx_instr_tlb_cntrl are:

1
+2
+3
+4
+5
+6
+7
+8
+9
+
 #define BX_INSTR_MOV_CR0 10
+ #define BX_INSTR_MOV_CR3 11
+ #define BX_INSTR_MOV_CR4 12
+ #define BX_INSTR_TASK_SWITCH 13
+ #define BX_INSTR_CONTEXT_SWITCH 14 /* VMM and SMM enter/exit */
+ #define BX_INSTR_INVLPG 15
+ #define BX_INSTR_INVEPT 16
+ #define BX_INSTR_INVVPID 17
+ #define BX_INSTR_INVPCID 18
+

The new_cr_value is provided for first 4 instruction types only and undefined for all others.

Possible instruction types, passed through bx_instr_cache_cntrl are:

1
+2
+
 #define BX_INSTR_INVD 10
+ #define BX_INSTR_WBINVD 11
+
1
+
 void bx_instr_prefetch_hint(unsigned cpu, unsigned what, unsigned seg, bx_address offset);
+

The callback is called each time, when Bochs simulator executes a PREFETCH instruction.

Possible PREFETCH types:

1
+2
+3
+4
+
 #define BX_INSTR_PREFETCH_NTA 00
+ #define BX_INSTR_PREFETCH_T0 01
+ #define BX_INSTR_PREFETCH_T1 02
+ #define BX_INSTR_PREFETCH_T2 03
+

The seg/offset arguments indicate the address of the requested prefetch.

1
+
 void bx_instr_wrmsr(unsigned cpu, unsigned msr, Bit64u value);
+

This callback is called each time when WRMSR instruction is executed. MSR number and written value passed as parameters to the callback function.

1
+
 void bx_instr_repeat_iteration(unsigned cpu, bxInstruction_c *i);
+

The callback is called each time, when Bochs simulator starts a new repeat iteration.

1
+
 void bx_instr_before_execution(unsigned cpu, bxInstruction_c *i);
+

The callback is called each time, when Bochs simulator starts a new instruction execution. In case of repeat instruction the callback will be called only once before the first iteration will be started.

1
+
 void bx_instr_after_execution(unsigned cpu, bxInstruction_c *i);
+

The callback is called each time, when Bochs simulator finishes any instruction execution. In case of repeat instruction the callback will be called only once after all repeat iterations.

1
+
 void bx_instr_lin_access(unsigned cpu, bx_address lin, bx_address phy, unsigned len, unsigned memtype, unsigned rw);
+

The callback is called each time, when Bochs simulator executes a linear memory access. Note that no page split accesses will be generated because Bochs splits page split accesses to two different memory accesses during its execution flow. The callback also will not be generated in case of direct physical memory access like page walks, SMM, VMM or SVM operations.

Possible access types are: BX_READ, BX_WRITE and BX_RW.

Currently the callback is not supported when repeat-speedups optimization is enabled.

1
+
 void bx_instr_phy_access(unsigned cpu, bx_address lin, bx_address phy, unsigned len, unsigned memtype, unsigned rw);
+

The callback is called each time, when Bochs simulator executes a physical memory access. Physical accesses include memory accesses generated by the CPU during page walks, SMM, VMM or SVM operations. Note that no page split accesses will be generated because Bochs splits page split accesses to two different memory accesses during its execution flow.

Possible access types are: BX_READ, BX_WRITE and BX_RW.

1
+2
+3
+
 void bx_instr_inp(Bit16u addr, unsigned len);
+ void bx_instr_inp2(Bit16u addr, unsigned len, unsigned val);
+ void bx_instr_outp(Bit16u addr, unsigned len, unsigned val);
+

These callback functions are a feedback from various system devices.

Known problems

1. BX_INSTR_LIN_ACCESS doesn’t work when repeat-speedups feature is enabled.

Feature requests:

1. BX_INSTR_CNEAR_BRANCH_NOT_TAKEN callback should have an additional ‘not taken’ new_rip parameter.

2. BX_INSTR_SMI, BX_INSTR_NMI, BX_INSTR_SIPI and other external events callbacks

 

If you read the above description about instrument functions, then let’s have a look at some of the important ones!

For debugging VMX you should use bx_instr_vmexit, but you should be sure to compile your Bochs with this feature enabled. By default it is enabled in the current version of Bochs :

1
+
#define BX_SUPPORT_VMX 2
+

bx_instr_phy_access can also help you debugging EPT (Extended Page Table) by checking physical addresses.

There are also other functions like bx_instr_wrmsr which is used for detecting what kind of MSR indexes an operating system or system drivers try to use.

References

This post is licensed under CC BY 4.0 by the author.

Bochs Emulator - Config & Build on Windows and OS X

Active Directory Certificate Services Overview and Migration

Comments powered by Disqus.

diff --git a/topics/build-a-simple-pin-tool/index.html b/topics/build-a-simple-pin-tool/index.html new file mode 100644 index 00000000..08553a98 --- /dev/null +++ b/topics/build-a-simple-pin-tool/index.html @@ -0,0 +1,21 @@ + Build a Simple Pin Tool | Rayanfam Blog
Home Build a Simple Pin Tool
Post
Cancel

Build a Simple Pin Tool

As I promised previously in Pinitor’s topic, I prepare a post about how to build your first pin tool.

Building a pin tool is somehow difficult when they didn’t update their sample tools so that it doesn’t work in Visual Studio 2015 or newer versions. By the way, it took me more than 2 days to resolve all the errors and compile a Hello World!

After compiling the first program, there where lots of new problems, so, believe me, it annoys you a lot but keep in mind if you discover a new problem in compiling, please comment about that and I add your problem and solving solution to the post.

After opening the Visual Studio, I see a huge number of errors that didn’t seem to be solved without violence ;) so that I decided to use Gnu Win32 make.exe in order to compile my pin tool.

Let start.

Preparing Environment

First of all download pin from Intel’s official site and then download Gnu Win32 make.exe and its dependencies. In the following list, I mentioned the dependencies that I downloaded and put beside of make.exe (in the same directory) but it might be something different in your case then you should download them manually based on the error that “make” shows you when you run it.

Libicon2.dll

Libintl3.dll

Libcharset.dll

After solving all the errors about dependency then I realized that make.exe uses cut.exe in its compiling process so I also download cut.exe and as you might know, cut.exe has some dependencies too!

Cggiconv-2.dll

Cygintkl-8.dll

Cygwin1.dll

I found this dll files from the path where Cygwin GCC is installed (it is also available at CoreUtils) so you can first download Cygwin from here then you can find and copy cut.exe and its dependencies from where you installed it.

Again running make causes new errors :(

This time make.exe needs iconv.exe and after downloading this file make.exe seems to run correctly.

You should also have c++ compiler installed previously and as I already install Visual Studio then these files are ready and I just need to provide an environment variable that points to this path and I’m good to go.

In the case of providing an environment variable, I pressed Windows Key+ R (to open run.exe) then type sysdm.cpl.

Open sysdm.cpl > Advanced tab > Environment Variable

Then in User Variables for Sina (Sina is my account name.) click on path to edit it. By adding a new variable to this section you can always access to the content of the folder without changing the working directory to this path, and make.exe uses cl.exe to compile a pin tool source code into the binary module.

In my visual studio, the path is as follows but it might be different in your case.

1
+
C:\Program Files(x86)\Microsoft VisualStudio 14.0\vc\bin
+

and in the case when you wanna compile a 64bit pin tool then you should change this variable to :

1
+
C:\Program Files(x86)\Microsoft VisualStudio 14.0\vc\bin\Amd64
+

Important Note: Keep in mind to restart your computer after adding a new variable or modifying one.

In my case when I wanna change my context to build x64 binaries then I add \amd64 to the path and restart the computer again.

You should now be able to build your first pin tool but there are some errors that sometimes might happen because of linker problem or syntax error or header mismatching that I discuss some of them in the rest of this topic

Please note that as I said above, if you encountered new errors please comment on this post to add your problem to the following list.

Problem with Linking

The very first problem that I encountered during compiling my first application was that linker can’t find libraries of some APIs.

In my case it needs kernek32.lib.

I found kernel32.lib from the following path (And you know it might be different in your case.)

1
+
C:\Program Files(x86)\Windows Kits\10\lib\10.0.15063.0\um\x86\kernel32.lib
+

Or in the case of 64-bit pin tool :

1
+
C:\Program Files(x86)\Windows Kits\10\lib\10.0.15063.0\um\x64\kernel32.lib
+

I copied this file to the make.exe path but you can also export this path to path in environment variables as I described previously.

After copying this file I added the following line to my tool’s source code after all #include(s) :

1
+
#pragma comment(lib, "kernel32.lib")
+

You by now should understand that your tool might need another .lib file to compile so you can provide more libraries for linker, same as what you did for kernel32.lib.

Building A Pin Tool

To build a new pin tool you should use make as follows :

In the case of ia32 or x86 architecture :

1
+
make.exe obj-ia32/inscount0.dll TARGET=ia32
+

In the case of Amd64 or x64 architecture :

1
+
make.exe obj-intel64/inscount0.dll
+

Note: If your tool source file is like “inscount0.cpp” then you should use be “inscount0.dll”, I mean its name should be same as source code except its extension which changes from .cpp to .dll.

Please note as I mentioned above you should change Environment Variable in the case of x64 and x86 version.

Run Pin with your Tool

To run your pin tool you should run the following command.

1
+
Pin t \source\tools\ManualExamples\inscount0.dll  cmd.exe /c dir
+

I just instrument cmd.exe using pin by inscount0.dll tool. I also pass the dir as an argument to cmd.exe.

The last thing

As I tested, pin tool has a problem with passing x64 tools to the x64 version of the pin so please don’t use the following path for running x64 tools :

1
+
C:\Users\Sina\Desktop\pin.exe
+

Instead, you should just use the following path (For x64 tools) :

1
+
C:\Users\Sina\Desktop\pin\intel64\bin\pin.exe
+

That’s it, guys,

I should also give my special thanks to Sobhan Sekhavatian one of my best friends for helping me to write this post.

I hope by now you built your first pin tool ;)

This post is licensed under CC BY 4.0 by the author.

Pinitor - An API Monitor Based on Pin

A first look at some aspects of Intel's "Vanderpool" initiative

Comments powered by Disqus.

diff --git a/topics/call-gates-ring-transitioning-in-ia-32-mode/index.html b/topics/call-gates-ring-transitioning-in-ia-32-mode/index.html new file mode 100644 index 00000000..a83b0c13 --- /dev/null +++ b/topics/call-gates-ring-transitioning-in-ia-32-mode/index.html @@ -0,0 +1,209 @@ + Call Gates' Ring Transitioning in IA-32 Mode | Rayanfam Blog
Home Call Gates' Ring Transitioning in IA-32 Mode
Post
Cancel

Call Gates' Ring Transitioning in IA-32 Mode

Have you ever thought how transitions between different rings performed?

Well, SYSENTER & SYSCALL used in modern OSs for transitioning between ring 3 to ring 0 but if there are other rings, then what’s Intel solution for entering them? The answer is call gates.

The rest of this topic described how to use call gates in modern processors.

80386 and its successors have 4 levels protections which isolate and protect user programs from each other and the operating system.

It offers an additional type of protection on a page basis, when paging is enabled(using U/S and R/W fields) .

There are 3 different types of privilege level checks when using call-gates:

  • Current Privilege Level (CPL)

  • Descriptor Privilege Level (DPL)

  • Requestor Privilege Level (RPL)

Current Privilege Level (CPL)

CPL is stored in the selector of currently executing CS register. It represents the privilege level (PL) of the currently executing task. It is also PL in the descriptor of the code segment and designated as Task Privilege Level (TPL).

Descriptor Privilege Level (DPL)

It is the PL of the object which is being attempted to be accessed by the current task or in the other words, the least privilege level for the caller to use this gate.

Requestor Privilege Level (RPL)

It is the lowest two bits of any selector. It can be used to weaken the CPL if desired.

The Effective Privilege Level (EPL) is EPL = max (CPL, RPL) (here numbers) Thus the task becomes less privileged.

Note that PL refers to Privilege Level.

Keep in mind that the above concepts also applies in other Intel structures

GDT Entries

GDT Stands for Global Descriptor Table. It contains some Segmentation Descriptors used in the age of memory segmentation and now it can be used to implement some segment change techniques like heavens gate.

In our case we use GDT as a table for our call gate descriptor. I didn’t see any use of call gates in Windows or Linux, by the way it’s a feature that exsits in processors. (Even in AMD64 and IA32e).

The GDT Entry Descriptor defined like this :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
typedef struct _GDT_ENTRY {
+	UINT16 LIMIT15_0;
+	UINT16 BASE15_0;
+	UINT8 BASE23_16;
+
+	UINT8 TYPE : 1;
+	UINT8 SUBTYPE : 1;
+	UINT8 Accessibility : 1;
+	UINT8 Access : 1;
+
+	UINT8 S : 1;
+	UINT8 DPL : 2;
+	UINT8 PRESENT : 1;
+
+	UINT8 LIMIT19_16 : 4;
+	UINT8 AVL : 1;
+	UINT8 L : 1;
+	UINT8 D : 1;
+	UINT8 GRANULARITY : 1;
+	UINT8 BASE31_24;
+}GDT_ENTRY, *PGDT_ENTRY;
+

For more information about GDT, you can read this article and also this osdev topic.

When the S flag is set to 0, the meaning of a GDT entry is quite different so it’s no longer a segment descriptor and now we can use this entry as a call-gate descriptor.

More information about call gates implementation, later in this topic.

Restricting Access to Data

Assume that a task needs data from the data segment. The privilege levels are checked at the time a selector for the target segment is loaded into the data segment register. Three privilege levels enter into privilege checking mechanism.

  • CPL
  • RPL of the selector of the target segment
  • DPL of the descriptor of the target segment

Note that access is allowed only if DPL ≥ Max(CPL, RPL) and A procedure can only access the data that is at the same or less privilege level.

CALL & JMPs (Near vs Far)

The near forms of JMP and CALL transfer within the current code segment and requires only limit checking. The far forms of JMP and CALL refer to other segments and require privilege checking.

The far JMP and CALL can be done in 2 ways:

  • Without Call Gate Descriptor

The processor permits a JMP or CALL directly to another segment only if :

1. DPL of the target segment = CPL of the calling segment

2. Confirming bit of the target code is set and DPL of the target segment ≤ CPL

Confirming Segment: These segments may be called from various privilege levels but execute at the privilege level of the calling procedure. (e.g. math library)

  • With Call Gate Descriptor

The far pointer of the control transfer instruction uses the selector part of the pointer and selects a gate. The selector and offset fields of a gate form a pointer to the entry of a procedure.

Ring Transitioning

When CPU fetches a far-call instruction, it will use that instruction’s ‘selector’ value to look up a descriptor in the GDT (or in the current LDT).

If it’s a ‘call-gate’ descriptor, and if access is allowed (i.e. if CPL ≤ DPL), then the CPU will perform a complex sequence of actions which will accomplish the requested ‘ring-transition’. CPL (Current Privilege Level) is based on least significant 2-bits in register CS (also in SS)

  • pushes the current SS:SP register
  • values onto a new stack-segment-
  • copies the specified number of parameters from the old stack onto the new stack
  • pushes the updated CS:IP register-values onto the new stack
  • loads new values into registers CS:IP (from the call gate descriptor) and into SS:SP

Where do the new values for SS:SP come from? (They’re not found in the call-gate)

They’re from a special system-segment, known as the TSS (Task State Segment). The CPU locates its TSS by referring to the value in register TR (Task Register).

Return to an Outer Ring

  • Use the far-return instruction: ‘lret’
    • Restores CS:IP from the current stack
    • Restores SS:SP from the current stack
  • Use the far-return instruction: ‘lret $n’
    • Restores CS:IP from the current stack
    • Discards n parameter-bytes from that stack
    • Restores SS:SP from that current stack

Call-Gate Descriptor

Intel defines a call-gate descriptor like this (in x86 environment):

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
typedef struct CALL_GATE32 {
+	int offset0_15 : 16;
+	int selector : 16;
+	union {
+		struct {
+			int argCount : 5;
+			int zeros : 3;
+			int type : 4;
+			int sFlag : 1;
+			int dpl : 2;
+			int pFlag : 1;
+		} part;
+		unsigned __int16 all;
+	} dummy;
+	int offset16_31 : 16;
+}CALL_GATE32, *PCALL_GATE32;
+
  • P = present (1 = yes, 0 = no)
  • DPL = Descriptor Privilege Level (0, 1 ,2 ,3)
  • Code-selector (specifies memory-segment containing procedure code)
  • Offset (specifies the procedure’s entry-point within its code-segment)
  • Parameter count (specifies how many parameter-values will be copied)
  • Gate-Type (‘0x4’ means a 16-bit call-gate, ‘0xC’ means a 32-bit call-gate)

The above descriptor is also available in AMD64 and it defines like the following picture.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
typedef struct CALL_GATE
+{
+	unsigned __int32 offset0_15 : 16;
+	unsigned __int32 selector : 16;
+	union {
+		struct {
+			unsigned __int16 ist : 3;
+			unsigned __int16 ignored : 5;
+			unsigned __int16 type : 5;
+			unsigned __int16 dpl : 2;
+			unsigned __int16 p : 1;
+		} part;
+		unsigned __int16 all;
+	} dummy;
+	unsigned __int64 offset16_63 : 48;
+	unsigned __int32 reserved : 32;
+}CALL_GATE, *PCALL_GATE;
+

Call gates facilitate controlled transfers of program control between different privilege levels. They are typically used only in operating systems or executives that use the privilege-level protection mechanism. Call gates are also useful for transferring program control between 16-bit and 32-bit code segments

Now, let’s see different types of entries that can be used in GDT Entries (the “type” field of above descriptors).

  • Bits 3 2 1 0 : Type of the entry
    • 0000 - Reserved
    • 0001 - Available 16-bit TSS
    • 0010 - Local Descriptor Table (LDT)
    • 0011 - Busy 16-bit TSS
    • 0100 - 16-bit Call Gate
    • 0101 - Task Gate
    • 0110 - 16-bit Interrupt Gate
    • 0111 - 16-bit Trap Gate
    • 1000 - Reserved
    • 1001 - Available 32-bit TSS
    • 1010 - Reserved
    • 1011 - Busy 32-bit TSS
    • 1100 - 32-bit Call Gate
    • 1101 - Reserved
    • 1110 - 32-bit Interrupt Gate
    • 1111 - 32-bit Trap Gate

Implementation

Enough for theory, let’s see how we can use them in a real environment.

The full source code is available on Github:

[https://github.com/SinaKarvandi/IA32-CALL-GATES]

After defining the above structures, now we need to declare a structure with the following assumptions.

Selector field should be 0x8 (pointing to KGDT_R0_CODE entry of GDT). As I told you about the type it should be 0xc and the minimum ring that can invoke this call-gate is (dpl = 0x3 (user-mode)) and of course, it’s present in memory.

The address should be divided into two 16 bit parts.

The following function is responsible for making a Windbg command that can be executed in order to defined a call-gate. Of course you can do the same thing using a kernel mode driver.

Note that address is the kernel handler function, gdtr is the result of “r gdtr” and GDTIndex is the GDT index that we want to modify.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
void CreateCallGateStruct(int address , int gdtr , int GDTIndex) {
+
+	CALL_GATE32 callgate32 = { 0 };
+	callgate32.dummy.part.zeros = 0x0;
+	callgate32.selector = 0x8;
+	callgate32.dummy.part.type = 0xc;
+	callgate32.dummy.part.sFlag = 0x0;
+	callgate32.dummy.part.dpl = 0x3;
+	callgate32.dummy.part.pFlag = 0x1;
+	callgate32.offset0_15 = 0x0000ffff & address;
+	address = address >> 16;
+	callgate32.offset16_31 = 0x0000ffff & address;
+
+
+	printf("eq %x+(0x8*0x%x) ", gdtr , GDTIndex);
+
+	printf("%04x", callgate32.offset16_31);
+	printf("%04x", callgate32.dummy.all);
+	printf("%04x", callgate32.selector);
+	printf("%04x", callgate32.offset0_15);
+
+}
+

Let’s execute the above code and see the results.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
	// Making windbg structure
+
+	int DispatcherAddress= 0x8176fcd4;
+	int gdtr = 0x80b5b000;
+	int GdtIndex = 0x64;
+
+	CreateCallGateStruct(DispatcherAddress, gdtr, GdtIndex);
+
+	unsigned short callGate[3];
+
+	callGate[0] = 0x0;
+	callGate[1] = 0x0;
+	callGate[2] = 0x320;
+
+	// Invoke the far call
+
+	__asm { int 3 }
+	__asm
+	{
+		call fword ptr[callGate];
+	}
+

The final result is :

1
+
eq 80b5b000+(0x8*0x64) ffff8176ec000008fffffcd4
+

If you look more precisely at the above code, you can see that, the last part is the definition of callGate array. This array chooses GDT index which your call-gate descriptor is located.

I choose 320 because the segment selector defines like this :

1
+2
+3
+4
+5
+6
+
15                                                 3    2        0
++--------------------------------------------------+----+--------+
+|          Index                                   | TI |   RPL  |
++--------------------------------------------------+----+--------+
+
+TI = Table Indicator: 0 = GDT, 1 = LDT
+

We select 320 (10100’0’000), RPL (000) TI (0 because our descriptor is on GDT, not LDT), the index is 100 (0x64 or 10100 in binary).

The last part is executing our FAR CALL. After the execution of this call we will be in our dispatcher address with Kernel Privilege Level. By now you should be aware of how to go to other rings (e.g ring 1,2) by modifying the above structures and the selectors.

The last thing is you should handle this situation and finally return to the previous rings using lret.

That’s it.

Conclusion

In this topic, we see how transitions between different rings are performed in our processor, even if almost all of the modern OSs don’t use this mechanism. For more i.formation you can review the following references.

References

[1] Intel 64 and IA-32 Architectures Software Developer’s Manual Vol. 3A – Chapter 5 – (5.8.3 Call Gates) (https://software.intel.com/en-us/articles/intel-sdm)

[2] Intel 64 and IA-32 Architectures Software Developer’s Manual Vol. 3A – Chapter 5 – (5.8.4 Accessing a Code Segment Through a Call Gate) (https://software.intel.com/en-us/articles/intel-sdm)

[4] 80386DX - (https://slideplayer.com/slide/9375765)

[5] Processor Privilege Levels - (https://slideplayer.com/slide/1652112)

[6] The Real, Protected, Long mode assembly tutorial for PCs - (https://www.codeproject.com/Articles/45788/The-Real-Protected-Long-mode-assembly-tutorial-for)

[7] Hooking the GDT - Installing Call gates - (https://github.com/David-Reguera-Garcia-Dreg/cgaty)

[8] The rootkit arsenal book – (https://www.amazon.com/Rootkit-Arsenal-Escape-Evasion-Corners/dp/1598220616)

[9] Bringing Call Gates Back – (http://www.alex-ionescu.com/?p=340)

[10] Descriptor tables in kernel exploitation – (https://j00ru.vexillium.org/2010/01/descriptor-tables-in-kernel-exploitation-a-new-article/)

This post is licensed under CC BY 4.0 by the author.

Hypervisor From Scratch – Part 5: Setting up VMCS & Running Guest Code

PacketScript overview: A Lua scripting engine for in-kernel packet processing

Comments powered by Disqus.

diff --git a/topics/captive-portal-detection-sample/index.html b/topics/captive-portal-detection-sample/index.html new file mode 100644 index 00000000..930181fe --- /dev/null +++ b/topics/captive-portal-detection-sample/index.html @@ -0,0 +1,39 @@ + Captive portal detection with a working sample in all major OSs! | Rayanfam Blog
Home Captive portal detection with a working sample in all major OSs!
Post
Cancel

Captive portal detection with a working sample in all major OSs!

Hi everyone
I’ve been working on a project which involves a developing a captive portal system from scratch. and I’m going to gradually post more of challenges we faced and the way we solved them too.

But for now I’m going to talk about captive portal detection in different OSs and how we’ve implemented it. It is not a really difficult or lengthy concept but the sad thing is that it’s not very well documented or not even documented at all!

Before beginning, I should note that there indeed are standard for captive portals detection and login, such as WISPr but they are not widely used as I know and they are mostly for embedded devices which do not have a browser to open login page and should be logged in automatically. We won’t have anything to do with WISPr protocol here.

This setup is tested with Fedora and Ubuntu, Windows 10, Windows 8.1 and android.

In order to find out whether there is a captive portal at work, the OS (or browser) should go through several steps; Firstly it should find out if internet is indeed connected or not (not disconnected completely), then it will find out that although connected to the internet, this device has limited connectivity (only DNS packets are transmitted and received). These two steps are enough for the OS to determine that it has some connection that is limited by the administrator, in some OSs this two steps are enough to detect captive portal. So making sure that only DNS packets are allowed would be enough. Firefox browser is like this and will show a bar indicating that you need to login to this network.

The third step is necessary for other OSs especially Windows; all HTTP requests going out of the network should be responded with a 302 redirect.

So it boils down to this:

  1. Only allowing DNS packets out of the network.
  2. Redirecting all HTTP requests with 302 (to the login page, but the page is not important in detection)

Implementation on OpenWRT

I will explain the steps necessary for detection in OpenWRT, using iptables and uhttpd, the default webserver on OpenWRT.

IPTables

This is really easy. Here are the rules:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
#start iptables for captive portal
+
+
+/usr/sbin/iptables -t filter -I FORWARD 1 --protocol tcp --sport 53 --jump ACCEPT
+/usr/sbin/iptables -t filter -I FORWARD 1 --protocol udp --sport 53 --jump ACCEPT
+/usr/sbin/iptables -t filter -I FORWARD 1 --protocol tcp --dport 53 --jump ACCEPT
+/usr/sbin/iptables -t filter -I FORWARD 1 --protocol udp --dport 53 --jump ACCEPT
+/usr/sbin/iptables -t filter -I FORWARD 5 -j DROP
+/usr/sbin/iptables -t nat -A prerouting_lan_rule --protocol tcp --dport 80 --jump DNAT --to-destination $(uci get network.lan.ipaddr):80
+
+#the line above does this, but you don't need to specify the ip manually
+#/usr/sbin/iptables -t nat -A prerouting_lan_rule --protocol tcp --dport 80 --jump DNAT --to-destination 192.168.100.1:80
+
  1. The first four rules accept all packets with source or destination port of 53 (domain name system)
  2. The 5th rule denies all other connections
  3. the last rule redirects all web requests to our http server running on router on port 80.
  • These rules work on OpenWRT, in order for them to work on other linuxes, you probably need to replace prerouting_lan_rule with PREROUTING.
  • It is a very good idea to rate limit DNS packets per ip using hashlimit iptables module, As this setup has no defense against DNS tunnels and covert channels, effectively bypassing all restrictions.
  • There sure are better ways to do this (such as only allowing outbound dport 53 and permitting established connections back, etc), but this guide focuses only on captive portal detection.

HTTP Redirect

The webserver is only needed for redirect, so it can be a separate light webserver running on another port too (like 8000), you change DNAT port in iptables only. So this can be a very basic webserver.

Configuring a redirect is pretty straightforward for every webserver you are using, But there wasn’t any documentation for uhttpd :/ , so I will explain redirect in uhttpd based on my experiments now.

You should change /e_tc__/uhttpd/redir.json_ like this:

1
+2
+3
+4
+5
+6
+7
+
{
+	"fallback": [
+		[ "if", [ "regex", "REQUEST_URI", [ "/*" ] ],
+			[ "rewrite", "/" ]
+		]
+	]
+}
+

It is a fallback redirect, so it will only kick in if the url does not exist. So it will not interfere with other things you will be doing on the webserver.


If you have done these steps and the three requirements are satisfied, a browser will automatically open when you connect to the network with a windows system. If you happen to accidentally close that page, just open a HTTP website (or the official one http://msftconnecttest.com/redirect ) to reach the login again.


Here are some links for further reading:

https://en.wikipedia.org/wiki/Captive_portal

https://www.secplicity.org/2016/08/26/lessons-defcon-2016-bypassing-captive-portals/

Have fun (!) with captive portals :)

This post is licensed under CC BY 4.0 by the author.

Defeating malware's Anti-VM techniques (CPUID-Based Instructions)

Inside Windows Page Frame Number (PFN) - Part 1

Comments powered by Disqus.

diff --git a/topics/change-user-mode-applications-virtual-address-through-kernel-debugging/index.html b/topics/change-user-mode-applications-virtual-address-through-kernel-debugging/index.html new file mode 100644 index 00000000..7cd2e75d --- /dev/null +++ b/topics/change-user-mode-applications-virtual-address-through-kernel-debugging/index.html @@ -0,0 +1,49 @@ + Change User-Mode application's virtual address through Kernel Debugging | Rayanfam Blog
Home Change User-Mode application's virtual address through Kernel Debugging
Post
Cancel

Change User-Mode application's virtual address through Kernel Debugging

Well, it’s somehow an odd topic but sometimes it could be really helpful in some situations.

So what are the situations?

Imagine sometimes you need to access windows stuffs that aren’t available from user-mode debuggers like ollydbg or through user-mode debugging (e.g memory after 0x7fffffff). In my experience I see some conditions that protectors make a sophisticated check for finding any debugger in memory and then change their approach to stop reverser from reversing the rest of the code. In such a situation you can make a virtual environment then break the machine completely and change your context to process and continue analyzing image. In this case you can overcome protection levels completely or at least overcome some protection levels. (some protectors never allow to run from a Virtual Machine or call some windows APIs to see if a kernel debugger is present or not and you should check for this stuffs first then continue debugging.)

So let’s get down to business,

In the following tutorial I use a VMware Virtual Machine that is ready for kernel debugging (if you don’t know how to make one pls see this link ,it describes how to do it). Then a kernel debugger (in my case Windbg) and a user-mode debugger (ollydbg).

First run myfile.exe in guest machine and attach to it from guest machine by ollydbg to see any editing that made in kernel debugging takes place in myfile.exe then break the Windbg to edit memory from host machine.

So I use the following command to get all the processes to see where you can find myfile.exe :

1
+
kd> !process 0 0
+

And it gives you a long list of processes where you can finally find myfile.exe.

1
+2
+3
+4
+
PROCESS ffffe001f9652080
+SessionId: 1 Cid: 0da4 Peb: 7ffdf000 ParentCid: 0588
+DirBase: 11d6d000 ObjectTable: ffffc0013e905680 HandleCount: <Data Not Accessible>
+Image: myfile.exe
+

So for more details about this process you can run :

1
+
kd> !process ffffe001f9652080 7
+

It should give you something like :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
1: kd> !process ffffe001f9652080 7
+PROCESS ffffe001f9652080
+SessionId: 1 Cid: 0da4 Peb: 7ffdf000 ParentCid: 0588
+DirBase: 11d6d000 ObjectTable: ffffc0013e905680 HandleCount: <Data Not Accessible>
+Image: myfile.exe
+VadRoot ffffe001f64dda10 Vads 129 Clone 0 Private 5676. Modified 520. Locked 0.
+DeviceMap ffffc0013dff8c30
+Token ffffc0014336a8e0
+ElapsedTime 00:08:14.197
+UserTime 00:00:00.046
+KernelTime 00:00:00.125
+...
+

then for switch to myfile.exe you should run :

1
+2
+3
+4
+5
+
kd> .process /p /r ffffe001f9652080
+Implicit process is now ffffe001f9652080
+.cache forcedecodeuser done
+Loading User Symbols
+.....................
+

Now you’re almost done ! you are in a 32 bit enviroment for myfile.exe which you can run all Windbg commands like what you run in Virtual Address (Instead of physical address.)

For a sample run :

1
+
kd> dc 400000
+

It gives you all the memory in myfile.exe’s base address (0x400000) which you can edit memory by something like ea command in windbg and see what’s going on after pressing g and then go to Guest Machine where you can find myfile.exe’s base address from ollydbg and see how it changed form kernel debugger.

Thanks for reading

This post is licensed under CC BY 4.0 by the author.

How to get every detail about SSDT , GDT , IDT in a blink of an eye

Bind9 chroot on debian 8

Comments powered by Disqus.

diff --git a/topics/cisco-ios-and-strongswan-ipsec-vpn/index.html b/topics/cisco-ios-and-strongswan-ipsec-vpn/index.html new file mode 100644 index 00000000..512439e4 --- /dev/null +++ b/topics/cisco-ios-and-strongswan-ipsec-vpn/index.html @@ -0,0 +1,85 @@ + Cisco IOS and StrongSWAN IPSEC VPN | Rayanfam Blog
Home Cisco IOS and StrongSWAN IPSEC VPN
Post
Cancel

Cisco IOS and StrongSWAN IPSEC VPN

In this blog post we will cover IPSEC tunnel between Linux StrongSWAN and Cisco IOS.

The strongSWAN config file can copied exactly as is to another server with the IP of Cisco Router and the tunnel will be connected between two linux routers. That is you do not need to change right and left in config files. It will be automatically detected from interface IP address (if available of course)

Cisco IOS configuration is very similar to previous post.

Here are the configuration files:

IOS Configuration

1
+2
+3
+4
+5
+6
+7
+8
+
 crypto isakmp policy 1
+     encr aes
+     hash sha256
+     authentication pre-share
+     group 14
+     lifetime 14400
+     crypto isakmp key cisco address 20.0.0.2
+     crypto ipsec transform-set ts1 esp-aes esp-sha256-hmac
+
1
+2
+
mode tunnel
+crypto map cm1 10 ipsec-isakmp
+
1
+2
+3
+
set peer 20.0.0.2
+set transform-set ts1
+match address 105
+
1
+2
+3
+4
+
interface FastEthernet0/0
+      ip address 20.0.0.1 255.0.0.0
+      duplex full
+      crypto map cm1
+
1
+2
+3
+
interface FastEthernet2/0
+      ip address 192.168.5.1 255.255.255.0
+      duplex full
+
1
+
ip route 192.168.6.0 255.255.255.0 20.0.0.2
+
1
+
route outside 192.168.6.0 255.255.255.0 20.0.0.2
+
1
+
access-list 105 permit ip 192.168.5.0 0.0.0.255 192.168.6.0 0.0.0.255
+

StrongSWAN configuration

/etc/ipsec.conf

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+
conn site2site
+           authby=secret
+           esp=aes128-sha256
+           ike = aes128-sha256-modp2048
+           ikelifetime = 4h
+           leftid=20.0.0.2
+           rightid=20.0.0.1
+           left=20.0.0.2
+           leftsubnet=192.168.6.0/24
+           right=20.0.0.1
+           rightsubnet=192.168.5.0/24
+           keyexchange=ikev1
+           pfs=no
+           auto=start
+           remote_peer_type=cisco
+

/etc/ipsec.secret

1
+
 20.0.0.2 20.0.0.1 : PSK cisco
+

/etc/sysctl.conf

1
+2
+3
+
ip_forward=1 (uncomment)
+
+# sysctl -p (run command)
+
This post is licensed under CC BY 4.0 by the author.

Exploring from User-Mode to Kernel-Mode

GRE over IPSec in Cisco IOS

Comments powered by Disqus.

diff --git a/topics/cisco-switch-sec-cheatsheet/index.html b/topics/cisco-switch-sec-cheatsheet/index.html new file mode 100644 index 00000000..c5f3a622 --- /dev/null +++ b/topics/cisco-switch-sec-cheatsheet/index.html @@ -0,0 +1,97 @@ + Cisco switch security features cheatsheet | Rayanfam Blog
Home Cisco switch security features cheatsheet
Post
Cancel

Cisco switch security features cheatsheet

Cisco switches (running IOS) have plenty of features that are critical to modern networks. Some are Cisco security features that eliminate several important attack vectors on layer 2. This is arguably the most important defense mechanism because ACLs and security mechanisms on software (layer 7) will sometimes fall short protecting the network because of the extreme complexity of communication up in this layer. So the earlier you close the holes the better!

As an example security features like protected ports can effectively harden lateral movement in windows networks (Active Directory domains), also while being so dead simple compared to more advanced methods implemented on top of active directory itself.

In this post I will give you the commands needed to implement some security features in a Cisco switch in a cheetsheet like manner.

It is important to fully understand what each feature will do, as failing to do so and running the commands blindly may cause disruption in your network. Just look up each one and read about it. :)

Reading official Cisco CCNP books is super recommended!

Port Security

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
int INTERFACE
+	switchport mode access
+	switchport access vlan 123 
+
+	#port security configuration starts here:
+	switchport port-security maximum # 
+	switchport port-security aging type inactive 
+	switchport port-security aging time 5 
+	switchport port-security violation restrict 
+	switchport port-security mac-address MAC 
+	switchport port-security mac-address sticky
+

These two commands show you port-security stats and make troubleshooting easier:

1
+2
+
show port-sec address
+show port-sec interface INTERFACE
+

DHCP Snooping

1
+2
+3
+4
+5
+6
+7
+8
+9
+
#(conf)
+	ip dhcp snooping
+	ip dhcp snooping vlan #
+
+interface INTERFACE
+	ip dhcp snooping trust 
+
+int USER-INTERFACE 
+	ip dhcp snooping limit rate #(pps)
+

Related show command:

1
+
show ip dhcp snooping
+

Dynamic ARP Inspection

1
+2
+3
+4
+5
+6
+7
+8
+
ip arp inspection 
+ip arp inspection vlan 123
+
+interface INTERFACE 
+	ip arp inspection trust
+	
+interface USER-INTERFACE 
+	ip arp inspection limit rate #(pps)
+

Related show command:

1
+
show ip arp inspection vlan 123
+

IP Source Guard

  • It requires DHCP snooping (or static ip/mac bindings)

Port based:

1
+2
+
interface INTERFACE
+ip verify source(ip) port-security(mac)
+

Creating manual entries:

1
+
ip source binding MAC vlan # IP_ADDRESS interface INTERFACE
+

Related show command:

1
+
show ip source binding
+

Protected ports

Ports that cannot communicate with each other directly.

1
+2
+3
+4
+
##private vlan edge aka protected ports : no direct traffic between those ports##
+
+interface INTERFACE
+switchport protected
+

Spanning Tress root guard

1
+2
+
int INTERFACE
+	spanning-tree guard root   superior bpdu
+

STP BPDU Guard

  • with Spanning tree port-fast
1
+
spanning-tree bpduguard enable
+

Storm Control

1
+2
+3
+4
+5
+
interface INTERFACE
+	#(do not clip anymore – all specified traffic is dropped until end of duration [1s])
+	storm-conftrol broadcast level (bbp | pps | %) # #
+	show storm-control b|m|u
+	storm-control action ACTION
+

I hope you like this post.

I am looking forward to improving this post using your contributions in a wiki-like manner. so if you think of any other feature which would be nice to be included in this post, please comment or email me and I will add it here. Thanks :)

This post is licensed under CC BY 4.0 by the author.

Inside Windows Page Frame Number (PFN) - Part 1

Inside Windows Page Frame Number (PFN) – Part 2

Comments powered by Disqus.

diff --git a/topics/defeat-malware-dynamic-api-loading/index.html b/topics/defeat-malware-dynamic-api-loading/index.html new file mode 100644 index 00000000..7e5414c1 --- /dev/null +++ b/topics/defeat-malware-dynamic-api-loading/index.html @@ -0,0 +1,5 @@ + Defeat Malware's Dynamic API Loading | Rayanfam Blog
Home Defeat Malware's Dynamic API Loading
Post
Cancel

Defeat Malware's Dynamic API Loading

There are thousands of ways which makes malwares resist against static dissambling and static analysing. One of the known ways to circumvent against suspicious API blocking or analysing statically by AV’s, is using LoadLibrary API to dynamically load a library then use its functions and it makes a CPU Intensive task for AV’s to defeat this kind of malwares.

This technique consists of using functions like LoadLibraryA or VirtualAlloc then use GetProcAdress. As msdn says, GetProcAddress : Retrieves the address of an exported function or variable from the specified dynamic-link library (DLL).

So the viral routine just maps the dll in the process virtual space then finds the function which it needs from exported function the simply call the returned address from GetProcAddress. You can see a good example here.

But how can we analyse such a malware ? As you can imagine by now, there is no way to breakpoint on a function which was not loaded previously, in this case the windbg’s sxe comes to play. sxe triggers a breakpoint when a special dll loaded you can use it in the following way :

1
+
sxe ld:user32.dll
+

And another good option is execute command instead of just a simple breakpoint.

1
+
sxe -c "bp shell32!MessageBoxW" ld:user32.dll
+

This technique helps us to debug such viruses better and easier.

This post is licensed under CC BY 4.0 by the author.

Lack of rechecking permissions in Android

Exploring from User-Mode to Kernel-Mode

Comments powered by Disqus.

diff --git a/topics/defeating-malware-anti-vm-techniques-cpuid-based-instructions/index.html b/topics/defeating-malware-anti-vm-techniques-cpuid-based-instructions/index.html new file mode 100644 index 00000000..0939939d --- /dev/null +++ b/topics/defeating-malware-anti-vm-techniques-cpuid-based-instructions/index.html @@ -0,0 +1,105 @@ + Defeating malware's Anti-VM techniques (CPUID-Based Instructions) | Rayanfam Blog
Home Defeating malware's Anti-VM techniques (CPUID-Based Instructions)
Post
Cancel

Defeating malware's Anti-VM techniques (CPUID-Based Instructions)

[The picture of this post is taken by one of my best friends, Ahmad Ghazi in Chitgar Lake !]

Introduction

You should by now be aware of everything, cause the topic’s title clearly describes the contents of this post.

As you know, almost all of the modern malware programs use some bunch of packers or protectors and using such tools cause malware to be weaponized with Anti-VM techniques which makes it impossible for reverse-engineers and analyzer to detect what’s happening inside the malware (or sometimes they implement their own methods to detect the VM presence).

Reverse-engineers always prefer to run the malware inside a Virtual Machine environment to avoid their computer to be affected by malware programs and this gives them lots of features like creating a snapshot from malware’s (VM) previous state.

In the rest of the post, I’m gonna show you some of the popular methods that use CPUID instruction in order to detect whether they’re running on a virtual machine or not.

Why CPUID?

CPUID is an instruction-level detection method and these kinds of methods are really hard to detect, as you know to trap on every execution of CPUID, you should either execute the instructions step by step(which is really slow and almost impossible) or instrument the target program. If you use instrumentation, then you might also defeat anti-instrument techniques.

Let see some examples.

Examples

As this great article in cyberbit describes:

The first method:

  • CPUID__: This instruction is executed with EAX=1 as input, the return value describes the processors features. The 31st bit of ECX on a physical machine will be equal to 0. On a guest VM it will equal to 1.

The second method:

  • “Hypervisor brand”: by calling CPUID with EAX=40000000 as input,1 the malware will get, as the return value, the virtualization vendor string in EAX, ECX, EDX.
    For example:

    • Microsoft: “Microsoft HV”
    • VMware : “VMwareVMware”

The Practical Implementation

For the first example I write the following code :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
#include "stdafx.h"
+#include <iostream>
+
+using namespace std;
+
+int main()
+{
+	bool IsUnderVM = false;
+	__asm {
+		xor    eax, eax
+		inc    eax
+		cpuid
+		bt     ecx, 0x1f
+		jc     UnderVM
+		NotUnderVM:
+		jmp     NopInstr
+		UnderVM:
+		mov    IsUnderVM, 0x1
+		NopInstr:
+		nop
+	}
+	cout << IsUnderVM;
+    return 0;
+}
+

The above code set eax=1 and executes the CPUID instruction, after that the result of ecx is checked to determine whether the 31st bit is set or not, the result is finally saved on “IsUnderVM” variable.

Now the second example:

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
+
#include "stdafx.h"
+#include <iostream>
+#include <string>
+
+using namespace std;
+
+int main()
+{
+	bool IsUnderVM = false;
+
+	__asm {
+		xor    eax, eax
+		mov    eax,0x40000000
+		cpuid
+		cmp ecx,0x4D566572
+		jne NopInstr
+		cmp edx,0x65726177
+		jne NopInstr
+		mov IsUnderVM, 0x1
+		NopInstr:
+		nop
+	}
+	cout << IsUnderVM;
+	return 0;
+}
+

The above code set eax=0x40000000 and then compares the ecx and edx against 0x4D566572 and 0x65726177 which are known and static values for VMWare products, like the first example, the result will finally save into “IsUnderVM”.

Note that, this implementation of method only works on VMWare, but if you want it to work in other vendors like VirtualBox or Hyper-V, then you have to detect what is their vendor string, it’s simple but as long I currently don’t have VBox or Hyper-V VM then you should do it by yourselves if you want to test it on other vendors.

Let see how it works!

The following picture is the first example when running on a host machine.

When it runs in a virtual environment, then the result is.

The following picture is the second example when running on a host machine.

When it runs in a virtual environment, then the result is.

You can clearly see, the difference in execution under a virtual environment and non-virtual environment.

How to defeat?

Now you might ask yourselves, Is it possible to change the CPUID results of the target virtual machine from host perspective?

Yep!

Fortunately, all the popular virtual machine vendors give the host machine an opportunity to modify CPUID and CPU features. This is because everytime your virtual machine fetches a CPUID instruction and wants to execute it, a VM-Exit happens and now hypervisor passes the execution to VMM, so this the best time of modifying CPUID result to bypass the above Anti-VM techniques.

In VMWare, you can find where your virtual machine is located, then find .vmx (config file) of your target VM then add the following line at the end of the file:

1
+
cpuid.1.ecx="0---:----:----:----:----:----:----:----"
+

Make sure to restart your VM and now the result of the first example is reversed!

The bypass for the second method is really dependent on the implementation. This might not work in your case but in my case, I can easily add the following lines to the .vmx config file.

1
+2
+
cpuid.40000000.ecx="0000:0000:0000:0000:0000:0000:0000:0000"
+cpuid.40000000.edx="0000:0000:0000:0000:0000:0000:0000:0000"
+

Now the second method gives the wrong results to the malware.

There is a good question here, which you can read in order to perform the same thing in VirtualBox but as I don’t have VBox in my machine right now, then you should do it by yourself.

The other vendors also give the same features to you so you can search for changing CPUID in your vendor and get the same results.

Summary

In this post, I described an Anti-Anti-VM technique in which you can use them to circumvent an Anti-VM malware. In the future posts, I’ll describe more about other Anti-VM techniques and the way you can defeat them, so make sure to stay connected!

References

This post is licensed under CC BY 4.0 by the author.

PyKD Tutorial - part 2

Captive portal detection with a working sample in all major OSs!

Comments powered by Disqus.

diff --git a/topics/detecting-cpu-structure-in-net-framework/index.html b/topics/detecting-cpu-structure-in-net-framework/index.html new file mode 100644 index 00000000..532c19be --- /dev/null +++ b/topics/detecting-cpu-structure-in-net-framework/index.html @@ -0,0 +1,83 @@ + Detecting CPU Structure in .Net Framework | Rayanfam Blog
Home Detecting CPU Structure in .Net Framework
Post
Cancel

Detecting CPU Structure in .Net Framework

The title seems simple, just getting CPU structure to detect if your program running on an AMD64 or an Intel x86 machine.

Remember when you install a 32-bit operation system on a 64-bit machine then all features are 32 bit so your program supports only 32-bit processor and can’t be a 64-bit one even if your CPU is x64.

By the way I search a lot across the Internet for getting the answer but almost all of them gives me , wrong information and that’s because whenever you try to get information about CPU in current PE then the result is same as current running PE structure and in almost all the situations developers set their program to support both x86 and x64 so the final EXE file will be an x86 one and you can’t correctly get CPU Type.

The following code gives you the desired result :

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
+
    static bool is64BitProcess = (IntPtr.Size == 8);
+    static bool is64BitOperatingSystem = is64BitProcess || InternalCheckIsWow64();
+
+    [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
+    [return: MarshalAs(UnmanagedType.Bool)]
+    private static extern bool IsWow64Process(
+        [In] IntPtr hProcess,
+        [Out] out bool wow64Process
+    );
+
+    public static bool InternalCheckIsWow64()
+    {
+        if ((Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor >= 1) ||
+            Environment.OSVersion.Version.Major >= 6)
+        {
+            using (Process p = Process.GetCurrentProcess())
+            {
+                bool retVal;
+                if (!IsWow64Process(p.Handle, out retVal))
+                {
+                    return false;
+                }
+                return retVal;
+            }
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+    public static string GetOSStructure() {
+        if (is64BitOperatingSystem)
+            return "x64";
+        return "x86";
+    }
+    static void Main(string[] args)
+    {
+        Console.WriteLine(GetOSStructure());
+        Console.ReadKey();
+    }
+

Remember if you build your application just for x64 machines then your CPU is always x64.

That’s obvious. 😀

This post is licensed under CC BY 4.0 by the author.

Getting started with Windows Server command line

Getting started with Python and Qt for cross-platform GUI apps

Comments powered by Disqus.

diff --git a/topics/exploring-from-usermode-to-kernelmode/index.html b/topics/exploring-from-usermode-to-kernelmode/index.html new file mode 100644 index 00000000..94306369 --- /dev/null +++ b/topics/exploring-from-usermode-to-kernelmode/index.html @@ -0,0 +1,11 @@ + Exploring from User-Mode to Kernel-Mode | Rayanfam Blog
Home Exploring from User-Mode to Kernel-Mode
Post
Cancel

Exploring from User-Mode to Kernel-Mode

There were times when I want to trace instructions from User Mode and continue tracing it into Kernel mode to reverse Windows’s internal implementation with my own supplied parameters from User Mode but there were a big problem and that was, How to access User Mode when you are in a Kernel Debugger or vice versa.

Even if I knew about changing debugger context to specific process but there were other problems which cause reversing kernel in this case, impossible.

Yesterday one of my friends explains me about how to debug it, then we improve this method and now I wanna share my experience on my blog.

Let’s get down to business…

First of all, a Kernel Debugger Windbg is needed in order to continue tracing in Kernel.

The first essential thing is we need to change the current process context (fs or gs registers) to be able to access user-mode space of the process which we need to trace its functions.

1
+
!process 0 0 notepad.exe
+

To get process location then :

1
+
.process /i AboveAddress
+

Which will notify you when the processor reaches to this process.

Remember you can also view current process by simply calling !thread as shown below :

Next step is to let windbg continue and it will notify you whenever it reaches to the desired process, so press g.

1
+
g
+

It’s getting a little tricky here.

Remember you just loaded the symbols of Kernel-mode modules but now we need user-mode modules’ symbol to find the functions.

1
+
.reload
+

And wait till all current modules symbol become loaded.

After that you can see that user-mode modules loaded successfully and you get something like :

1
+
lm nt
+

So far so good ;)

In this post, I’m gonna break on NtQuerySystemInformation which is a native function of ntdll but you can also break on everything you need, depend on address and symbol files(You can also break on your modules by loading their .pdb files.).

Let’s continue until the process calls the NtQuerySystemInformation …

The following picture shows how it performs :

Ok, everything is ready to step in or step out in windbg by the way you can’t debug like that :(

Because if you continue debugging, it is possible that kernel switches its context and your breakpoint hits for the second time and you will lose everything!

This problem can be solved by disabling the breakpoint so every time the kernel switches its context then windbg continues until you reach the last point where you were debugging that instruction.

I always use bl in order to view and disable my breakpoints.

That is it!

Hope you guys have a sweet kernel exploring!

This post is licensed under CC BY 4.0 by the author.

Defeat Malware's Dynamic API Loading

Cisco IOS and StrongSWAN IPSEC VPN

Comments powered by Disqus.

diff --git a/topics/finding-the-real-access-rights-needed-by-handles/index.html b/topics/finding-the-real-access-rights-needed-by-handles/index.html new file mode 100644 index 00000000..2832e7c8 --- /dev/null +++ b/topics/finding-the-real-access-rights-needed-by-handles/index.html @@ -0,0 +1,203 @@ + Why you should not always trust MSDN: Finding Real Access Rights Needed By Handles | Rayanfam Blog
Home Why you should not always trust MSDN: Finding Real Access Rights Needed By Handles
Post
Cancel

Why you should not always trust MSDN: Finding Real Access Rights Needed By Handles

Introduction

Hi guys,

The title of this topic is somehow weird, if you think everything in MSDN is 100% match with what Microsoft implemented in Windows (like what I used to think), you’re definitely wrong, this post shows some proofs and in the last part, I’ll give you a solution to ACCESS_RIGHTS problem.

Before starting let’s talk about some backgrounds about “ACCESS_MASK”.

Most of the explanations derived from here.

Backgrounds

The ACCESS_MASK data type is a DWORD value that defines standard, specific, and generic rights. These rights are used in access control entries (ACEs) and are the primary means of specifying the requested or granted access to an object.

A securable object is an object that can have a security descriptor. All named Windows objects are securable. Some unnamed objects, such as process and thread objects, can have security descriptors too. For most securable objects, you can specify an object’s security descriptor in the function call that creates the object. For example, you can specify a security descriptor in the CreateFile and CreateProcess functions.

Each type of securable object defines its own set of specific access rights and its own mapping of generic access rights. For information about the specific and generic access rights for each type of securable object, see the overview for that type of object.

Here is the list of some objects in Windows (From Windows Internals) that have security descriptor and of course ACCESS_MASK.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
1. Files, directories and volumes(NTFS file system)
+2. Devices
+3. Mailslots
+4. Named and anonymous pipes
+5. Jobs
+6. Processes
+7. Threads
+8. Events, keyed events and event pairs
+9. Mutexes, semaphores
+10. Shared memory sections
+11. I / O completion ports
+12. LPC ports
+13. Waitable timers
+14. Access tokens
+15. Windows stations
+16. Desktops
+17. Network shares
+18. Services
+

The structure of ACCESS_MASK is like this :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
     typedef struct _ACCESS_MASK {
+       WORD SpecificRights;
+       BYTE StandardRights;
+       BYTE AccessSystemAcl: 1;
+       BYTE Reserved: 3;
+       BYTE GenericAll: 1;
+       BYTE GenericExecute: 1;
+       BYTE GenericWrite: 1;
+       BYTE GenericRead: 1;
+     }
+     ACCESS_MASK;
+

For more information you can read these two pdfs.

Motivation

That’s enough for theory, the reason for creating this post is during the past few days, I was reading “Reading Your Way Around UAC” which is sophisticated research about Windows Access Tokens.

The most interesting part for me was where James Forshaw wrote :

What’s going on? Basically, the documentation is wrong, you don’t need QueryInformation to open the process token only QueryLimitedInformation. You can disassemble NtOpenProcessTokenEx in the kernel if you don’t believe me:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
NTSTATUS NtOpenProcessTokenEx(HANDLE ProcessHandle,
+  ACCESS_MASK DesiredAccess,
+  DWORD HandleAttributes,
+  PHANDLE TokenHandle) {
+  EPROCESS * ProcessObject;
+  NTSTATUS status = ObReferenceObjectByHandle(
+    ProcessHandle,
+    PROCESS_QUERY_LIMITED_INFORMATION,
+    PsProcessType, &
+    ProcessObject,
+    NULL);
+  ...
+}
+

Going back to Vista it’s always been the case that only QueryLimitedInformation was needed, contrary to the documentation. 

For the first time, it was so weird for me, then I ask some of my friends about the things that are implemented in contrary with documentation in MSDN, so yeah, it seems there are things in contrary with MSDN.

That’s enough for me to lose my trust to Microsoft (at least for checking handles’ ACCESS_MASK ) thus I have to somehow check everything by my self but hey, it’s such a boring, isn’t it?

So, I decided to write an IDA Python Plugin for this purpose but let’s see what we really need to look for.

The first thing we need to know is how Windows checks for the desired access. The ObReferenceObjectByHandle function is designed for this purpose and as you can see from the MSDN:

The ObReferenceObjectByHandle routine provides access validation on the object handle, and, if access can be granted, returns the corresponding pointer to the object’s body.

If you search for this function in Ntoskrnl.exe using IDA Pro, you’ll see something like this :

Functions to check for access rights of a handle

Functions to check for access rights of a handle

It seems that there are other functions for this purpose, moreover, if you look at the decompiled source of NtOpenProcessTokenEx you see that ObReferenceObjectByHandle is no longer there instead it replaced by ObpReferenceObjectByHandleWithTag so we should also add this function into our list.

NtOpenProcessToken Decompiled Source

NtOpenProcessToken Decompiled Source

From the ObpReferenceObjectByHandleWithTag you can see that the contarary still remains in MSDN. If you look at the second argument to ObpReferenceObjectByHandleWithTag you can see 0x1000 and it’s, of course, PROCESS_QUERY_LIMITED_INFORMATION while the MSDN have mentioned something else.

PROCESS_QUERY_LIMITED_INFORMATION

PROCESS_QUERY_LIMITED_INFORMATION

The documentation for OpenProcessToken:

OpenProcessToken

OpenProcessToken

Functions to Test

Actually, we just need to test the following function (from the functions available in IDA search results):

CmObReferenceObjectByHandle

 ObReferenceObjectByHandleObp

ReferenceObjectByHandleWithTag

ObReferenceObjectByHandleWithTag

Other functions are not really important to us, as I saw their XREFs, for example :

VerifierObReferenceObjectByHandle

As you can see the above function doesn’t seems to valuable in our case from the XREFs results.

IDA Python Script

The following code is the IDA Python script for finding XREFs of our target function then find the second argument from the decompiled source:

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
+
#!/usr/bin/python
+# -*- coding: utf-8 -*-
+from idautils import *
+from idaapi import *
+from idc import *
+
+FunctionName = 'ObReferenceObjectByHandle'
+
+
+def PrintDecompiledLine(line):
+    print 'Decompiled Line : ' + line
+    print 'ACCESS_MASK     : ' + line.split(FunctionName)[1].split(','
+            )[1]
+    print ''
+
+
+# Finding Function
+
+ea = BeginEA()
+
+# for funcAddr in Functions(SegStart(ea), SegEnd(ea)):
+
+for funcAddr in Functions(0x140001000, 0x140909410):
+    funcName = GetFunctionName(funcAddr)
+    if funcName == FunctionName:
+        print 'Function %s is located at 0x%x' % (funcName, funcAddr)
+        print '====================================================='
+        ea = funcAddr
+
+for ref in CodeRefsTo(ea, 1):
+    print 'Function        : ' + GetFunctionName(ref)
+    print ''
+    try:
+        cfunc = idaapi.decompile(ref)
+    except:
+        print 'Failed to decompile : ' + GetFunctionName(ref)
+
+    multiline = False
+    multilinestring = ''
+
+    # print cfunc
+
+    for item in str(cfunc).split('\n'):
+        if multiline:
+            if ')' in item and not '(' in item:
+                multiline = False
+                multilinestring += item.strip()
+                PrintDecompiledLine(multilinestring)
+            else:
+                multilinestring += item.strip()
+        elif FunctionName in item:
+            if ')' in item:
+                PrintDecompiledLine(item.strip())
+            else:
+                multiline = True
+                multilinestring = item.strip()
+
+    print '-------------------------------------------------'
+
+

Results

Here is the results, you can use them if you don’t have IDA Pro:

Note: If you want to use the results, you can find functions which start with (NT*) that’s because these functions have a pair in user-mode ntdll, so if your user-mode function ends to a ntdll native function then you can search for the same function and see the reall access rights.

Conclusion

In this post, you saw how it might be different, the real implementation over documentation so as a conclusion if you are a security researcher, it’s always better to check the real source code (decompiled) instead of just trusting the documentation.

That’s it guys, hope you enjoy it.

Aniiiiiiiiiime :)

References

[1] Reading Your Way Around UAC (Part 2) - (https://tyranidslair.blogspot.com/2017/05/reading-your-way-around-uac-part-2.html)

[2] ObReferenceObjectByHandle function - (https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-obreferenceobjectbyhandle)

[3] Process Security and Access Rights - (https://docs.microsoft.com/en-us/windows/desktop/procthread/process-security-and-access-rights)

[4] Enumerate all XefsTo a Segment in IDAPython - (https://reverseengineering.stackexchange.com/questions/3669/enumerate-all-xefsto-a-segment-in-idapython)

[5] OpenProcessToken function - (https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-openprocesstoken)

[6] About the ACCESS_MASK Structure - (https://blogs.msdn.microsoft.com/openspecification/2010/04/01/about-the-access_mask-structure/)

[7] ACCESS_MASK - (https://docs.microsoft.com/en-us/windows/desktop/SecAuthZ/access-mask)

[8] ObpReferenceProcessObjectByHandle - (http://www.codewarrior.cn/ntdoc/wrk/ob/ObpReferenceProcessObjectByHandle.htm)

This post is licensed under CC BY 4.0 by the author.

Hypervisor From Scratch – Part 6: Virtualizing An Already Running System

Reversing Windows Internals (Part 1) - Digging Into Handles, Callbacks & ObjectTypes

Comments powered by Disqus.

diff --git a/topics/fooling-windows-about-cpu/index.html b/topics/fooling-windows-about-cpu/index.html new file mode 100644 index 00000000..e2271d31 --- /dev/null +++ b/topics/fooling-windows-about-cpu/index.html @@ -0,0 +1,591 @@ + Fooling Windows about its internal CPU | Rayanfam Blog
Home Fooling Windows about its internal CPU
Post
Cancel

Fooling Windows about its internal CPU

In this post, I’m gonna show you how you can fool windows about its internal structure and sometimes give it wrong information about its internal capabilities or internal information which can bring you a lot of fun. (At least for me !)

But don’t do that it can hurt your system actually but this post is about how to change CPU Capacity measurement of Windows and see its result in user-mode programs.

There is a good article here which gives you lots of information. I recommend seeing all its structures before start reading rest of this post.

Ok, As you know windows contains lots of structures which stores its internal information about the system which is running on and almost all of this information can be found by using NtQuerySystemInformation and you can see all about it on MSDN (In the future post I will give you more details about this function.)

I’m gonna use nt!_KPCR and Code Machine describes it well :

nt!_KPCR

KPCR represents the Kernel Processor Control Region. The KPCR contains per-CPU information which is shared by the kernel and the HAL. There are as many KPCR in the system as there are CPUs.

The KPCR of the current CPU is always accessible at fs:[0] on the x86 systems and gs:[0] on x64 systems. Commonly used kernel functions like PsGetCurrentProcess() and KeGetCurrentThread() retrieve information from the KPCR using the FS/GS relative accesses.

The Prcb field contains an embedded KPRCB structure that represents the Kernel Processor Control Block.

The debugger’s “!pcr” command displays partial contents of the PCR.

You can see all Windbg gives to you from Windows symbols :

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
+
lkd> dt nt!_KPCR
++0x000 NtTib : _NT_TIB
++0x000 GdtBase : Ptr64 _KGDTENTRY64
++0x008 TssBase : Ptr64 _KTSS64
++0x010 UserRsp : Uint8B
++0x018 Self : Ptr64 _KPCR
++0x020 CurrentPrcb : Ptr64 _KPRCB
++0x028 LockArray : Ptr64 _KSPIN_LOCK_QUEUE
++0x030 Used_Self : Ptr64 Void
++0x038 IdtBase : Ptr64 _KIDTENTRY64
++0x040 Unused : [2] Uint8B
++0x050 Irql : UChar
++0x051 SecondLevelCacheAssociativity : UChar
++0x052 ObsoleteNumber : UChar
++0x053 Fill0 : UChar
++0x054 Unused0 : [3] Uint4B
++0x060 MajorVersion : Uint2B
++0x062 MinorVersion : Uint2B
++0x064 StallScaleFactor : Uint4B
++0x068 Unused1 : [3] Ptr64 Void
++0x080 KernelReserved : [15] Uint4B
++0x0bc SecondLevelCacheSize : Uint4B
++0x0c0 HalReserved : [16] Uint4B
++0x100 Unused2 : Uint4B
++0x108 KdVersionBlock : Ptr64 Void
++0x110 Unused3 : Ptr64 Void
++0x118 PcrAlign1 : [24] Uint4B
++0x180 Prcb : _KPRCB
+

There are lots of great information about Idt , IRQL in the above structure so it must be an important one!

Look at the last one, which is Prcb!

Mark Russinovich in Windows Internals wrote “The PCR and PRCB contain information about the state of each processor in the system such as current IRQL, a pointer to the hardware Idt, the currently running thread, and the next thread selected to run. The kernel and the HAL use this information to perform architecture-specific and machine-specific actions.”.

So all we need is seeing what we can do with such a structure. Let me show you the result of windbg symbols.

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
+262
+263
+264
+265
+
kd> dt nt!_KPRCB
++0x000 MxCsr : Uint4B
++0x004 LegacyNumber : UChar
++0x005 ReservedMustBeZero : UChar
++0x006 InterruptRequest : UChar
++0x007 IdleHalt : UChar
++0x008 CurrentThread : Ptr64 _KTHREAD
++0x010 NextThread : Ptr64 _KTHREAD
++0x018 IdleThread : Ptr64 _KTHREAD
++0x020 NestingLevel : UChar
++0x021 ClockOwner : UChar
++0x022 PendingTickFlags : UChar
++0x022 PendingTick : Pos 0, 1 Bit
++0x022 PendingBackupTick : Pos 1, 1 Bit
++0x023 IdleState : UChar
++0x024 Number : Uint4B
++0x028 RspBase : Uint8B
++0x030 PrcbLock : Uint8B
++0x038 PriorityState : Ptr64 Char
++0x040 CpuType : Char
++0x041 CpuID : Char
++0x042 CpuStep : Uint2B
++0x042 CpuStepping : UChar
++0x043 CpuModel : UChar
++0x044 MHz : Uint4B
++0x048 HalReserved : [8] Uint8B
++0x088 MinorVersion : Uint2B
++0x08a MajorVersion : Uint2B
++0x08c BuildType : UChar
++0x08d CpuVendor : UChar
++0x08e CoresPerPhysicalProcessor : UChar
++0x08f LogicalProcessorsPerCore : UChar
++0x090 PrcbPad04 : [6] Uint8B
++0x0c0 ParentNode : Ptr64 _KNODE
++0x0c8 GroupSetMember : Uint8B
++0x0d0 Group : UChar
++0x0d1 GroupIndex : UChar
++0x0d2 PrcbPad05 : [2] UChar
++0x0d4 InitialApicId : Uint4B
++0x0d8 ScbOffset : Uint4B
++0x0dc ApicMask : Uint4B
++0x0e0 AcpiReserved : Ptr64 Void
++0x0e8 CFlushSize : Uint4B
++0x0ec PrcbPad10 : Uint4B
++0x0f0 PrcbPad11 : [2] Uint8B
++0x100 ProcessorState : _KPROCESSOR_STATE
++0x6c0 PrcbPad12 : [6] Uint8B
++0x6f0 LockQueue : [17] _KSPIN_LOCK_QUEUE
++0x800 PPLookasideList : [16] _PP_LOOKASIDE_LIST
++0x900 PPNxPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL
++0x1500 PPNPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL
++0x2100 PPPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL
++0x2d00 PrcbPad20 : Uint8B
++0x2d08 DeferredReadyListHead : _SINGLE_LIST_ENTRY
++0x2d10 MmPageFaultCount : Int4B
++0x2d14 MmCopyOnWriteCount : Int4B
++0x2d18 MmTransitionCount : Int4B
++0x2d1c MmDemandZeroCount : Int4B
++0x2d20 MmPageReadCount : Int4B
++0x2d24 MmPageReadIoCount : Int4B
++0x2d28 MmDirtyPagesWriteCount : Int4B
++0x2d2c MmDirtyWriteIoCount : Int4B
++0x2d30 MmMappedPagesWriteCount : Int4B
++0x2d34 MmMappedWriteIoCount : Int4B
++0x2d38 KeSystemCalls : Uint4B
++0x2d3c KeContextSwitches : Uint4B
++0x2d40 PrcbPad40 : Uint4B
++0x2d44 CcFastReadNoWait : Uint4B
++0x2d48 CcFastReadWait : Uint4B
++0x2d4c CcFastReadNotPossible : Uint4B
++0x2d50 CcCopyReadNoWait : Uint4B
++0x2d54 CcCopyReadWait : Uint4B
++0x2d58 CcCopyReadNoWaitMiss : Uint4B
++0x2d5c IoReadOperationCount : Int4B
++0x2d60 IoWriteOperationCount : Int4B
++0x2d64 IoOtherOperationCount : Int4B
++0x2d68 IoReadTransferCount : _LARGE_INTEGER
++0x2d70 IoWriteTransferCount : _LARGE_INTEGER
++0x2d78 IoOtherTransferCount : _LARGE_INTEGER
++0x2d80 PacketBarrier : Int4B
++0x2d84 TargetCount : Int4B
++0x2d88 IpiFrozen : Uint4B
++0x2d90 IsrDpcStats : Ptr64 Void
++0x2d98 DeviceInterrupts : Uint4B
++0x2d9c LookasideIrpFloat : Int4B
++0x2da0 InterruptLastCount : Uint4B
++0x2da4 InterruptRate : Uint4B
++0x2da8 LastNonHrTimerExpiration : Uint8B
++0x2db0 PrcbPad41 : [20] Uint4B
++0x2e00 DpcData : [2] _KDPC_DATA
++0x2e50 DpcStack : Ptr64 Void
++0x2e58 MaximumDpcQueueDepth : Int4B
++0x2e5c DpcRequestRate : Uint4B
++0x2e60 MinimumDpcRate : Uint4B
++0x2e64 DpcLastCount : Uint4B
++0x2e68 ThreadDpcEnable : UChar
++0x2e69 QuantumEnd : UChar
++0x2e6a DpcRoutineActive : UChar
++0x2e6b IdleSchedule : UChar
++0x2e6c DpcRequestSummary : Int4B
++0x2e6c DpcRequestSlot : [2] Int2B
++0x2e6c NormalDpcState : Int2B
++0x2e6e ThreadDpcState : Int2B
++0x2e6c DpcNormalProcessingActive : Pos 0, 1 Bit
++0x2e6c DpcNormalProcessingRequested : Pos 1, 1 Bit
++0x2e6c DpcNormalThreadSignal : Pos 2, 1 Bit
++0x2e6c DpcNormalTimerExpiration : Pos 3, 1 Bit
++0x2e6c DpcNormalDpcPresent : Pos 4, 1 Bit
++0x2e6c DpcNormalLocalInterrupt : Pos 5, 1 Bit
++0x2e6c DpcNormalSpare : Pos 6, 10 Bits
++0x2e6c DpcThreadActive : Pos 16, 1 Bit
++0x2e6c DpcThreadRequested : Pos 17, 1 Bit
++0x2e6c DpcThreadSpare : Pos 18, 14 Bits
++0x2e70 LastTimerHand : Uint4B
++0x2e74 LastTick : Uint4B
++0x2e78 ClockInterrupts : Uint4B
++0x2e7c ReadyScanTick : Uint4B
++0x2e80 InterruptObject : [256] Ptr64 Void
++0x3680 TimerTable : _KTIMER_TABLE
++0x5880 DpcGate : _KGATE
++0x5898 PrcbPad52 : Ptr64 Void
++0x58a0 CallDpc : _KDPC
++0x58e0 ClockKeepAlive : Int4B
++0x58e4 PrcbPad60 : [2] UChar
++0x58e6 NmiActive : Uint2B
++0x58e8 DpcWatchdogPeriod : Int4B
++0x58ec DpcWatchdogCount : Int4B
++0x58f0 KeSpinLockOrdering : Int4B
++0x58f4 DpcWatchdogProfileCumulativeDpcThreshold : Uint4B
++0x58f8 CachedPtes : Ptr64 Void
++0x5900 WaitListHead : _LIST_ENTRY
++0x5910 WaitLock : Uint8B
++0x5918 ReadySummary : Uint4B
++0x591c AffinitizedSelectionMask : Int4B
++0x5920 QueueIndex : Uint4B
++0x5924 PrcbPad75 : [3] Uint4B
++0x5930 TimerExpirationDpc : _KDPC
++0x5970 ScbQueue : _RTL_RB_TREE
++0x5980 DispatcherReadyListHead : [32] _LIST_ENTRY
++0x5b80 InterruptCount : Uint4B
++0x5b84 KernelTime : Uint4B
++0x5b88 UserTime : Uint4B
++0x5b8c DpcTime : Uint4B
++0x5b90 InterruptTime : Uint4B
++0x5b94 AdjustDpcThreshold : Uint4B
++0x5b98 DebuggerSavedIRQL : UChar
++0x5b99 GroupSchedulingOverQuota : UChar
++0x5b9a DeepSleep : UChar
++0x5b9b PrcbPad80 : [5] UChar
++0x5ba0 DpcTimeCount : Uint4B
++0x5ba4 DpcTimeLimit : Uint4B
++0x5ba8 PeriodicCount : Uint4B
++0x5bac PeriodicBias : Uint4B
++0x5bb0 AvailableTime : Uint4B
++0x5bb4 KeExceptionDispatchCount : Uint4B
++0x5bb8 ReadyThreadCount : Uint4B
++0x5bc0 StartCycles : Uint8B
++0x5bc8 TaggedCyclesStart : Uint8B
++0x5bd0 TaggedCycles : [2] Uint8B
++0x5be0 GenerationTarget : Uint8B
++0x5be8 AffinitizedCycles : Uint8B
++0x5bf0 ImportantCycles : Uint8B
++0x5bf8 UnimportantCycles : Uint8B
++0x5c00 ReadyQueueExpectedRunTime : Uint8B
++0x5c08 PrcbPad81 : [21] Uint4B
++0x5c5c DpcWatchdogProfileSingleDpcThreshold : Uint4B
++0x5c60 MmSpinLockOrdering : Int4B
++0x5c64 PageColor : Uint4B
++0x5c68 NodeColor : Uint4B
++0x5c6c NodeShiftedColor : Uint4B
++0x5c70 SecondaryColorMask : Uint4B
++0x5c74 PrcbPad83 : Uint4B
++0x5c78 CycleTime : Uint8B
++0x5c80 Cycles : [4] [2] Uint8B
++0x5cc0 PrcbPad84 : [16] Uint4B
++0x5d00 CcFastMdlReadNoWait : Uint4B
++0x5d04 CcFastMdlReadWait : Uint4B
++0x5d08 CcFastMdlReadNotPossible : Uint4B
++0x5d0c CcMapDataNoWait : Uint4B
++0x5d10 CcMapDataWait : Uint4B
++0x5d14 CcPinMappedDataCount : Uint4B
++0x5d18 CcPinReadNoWait : Uint4B
++0x5d1c CcPinReadWait : Uint4B
++0x5d20 CcMdlReadNoWait : Uint4B
++0x5d24 CcMdlReadWait : Uint4B
++0x5d28 CcLazyWriteHotSpots : Uint4B
++0x5d2c CcLazyWriteIos : Uint4B
++0x5d30 CcLazyWritePages : Uint4B
++0x5d34 CcDataFlushes : Uint4B
++0x5d38 CcDataPages : Uint4B
++0x5d3c CcLostDelayedWrites : Uint4B
++0x5d40 CcFastReadResourceMiss : Uint4B
++0x5d44 CcCopyReadWaitMiss : Uint4B
++0x5d48 CcFastMdlReadResourceMiss : Uint4B
++0x5d4c CcMapDataNoWaitMiss : Uint4B
++0x5d50 CcMapDataWaitMiss : Uint4B
++0x5d54 CcPinReadNoWaitMiss : Uint4B
++0x5d58 CcPinReadWaitMiss : Uint4B
++0x5d5c CcMdlReadNoWaitMiss : Uint4B
++0x5d60 CcMdlReadWaitMiss : Uint4B
++0x5d64 CcReadAheadIos : Uint4B
++0x5d68 MmCacheTransitionCount : Int4B
++0x5d6c MmCacheReadCount : Int4B
++0x5d70 MmCacheIoCount : Int4B
++0x5d74 PrcbPad91 : [3] Uint4B
++0x5d80 PowerState : _PROCESSOR_POWER_STATE
++0x5f58 ScbList : _LIST_ENTRY
++0x5f68 ForceIdleDpc : _KDPC
++0x5fa8 PrcbPad92 : [18] Uint4B
++0x5ff0 KeAlignmentFixupCount : Uint4B
++0x5ff8 DpcWatchdogDpc : _KDPC
++0x6038 DpcWatchdogTimer : _KTIMER
++0x6078 Cache : [5] _CACHE_DESCRIPTOR
++0x60b4 CacheCount : Uint4B
++0x60b8 CachedCommit : Uint4B
++0x60bc CachedResidentAvailable : Uint4B
++0x60c0 HyperPte : Ptr64 Void
++0x60c8 WheaInfo : Ptr64 Void
++0x60d0 EtwSupport : Ptr64 Void
++0x60e0 InterruptObjectPool : _SLIST_HEADER
++0x60f0 HypercallPageList : _SLIST_HEADER
++0x6100 HypercallCachedPages : Ptr64 Void
++0x6108 VirtualApicAssist : Ptr64 Void
++0x6110 StatisticsPage : Ptr64 Uint8B
++0x6118 PackageProcessorSet : _KAFFINITY_EX
++0x61c0 SharedReadyQueueMask : Uint8B
++0x61c8 SharedReadyQueue : Ptr64 _KSHARED_READY_QUEUE
++0x61d0 SharedQueueScanOwner : Uint4B
++0x61d4 ScanSiblingIndex : Uint4B
++0x61d8 CoreProcessorSet : Uint8B
++0x61e0 ScanSiblingMask : Uint8B
++0x61e8 LLCMask : Uint8B
++0x61f0 CacheProcessorMask : [5] Uint8B
++0x6218 ProcessorProfileControlArea : Ptr64 _PROCESSOR_PROFILE_CONTROL_AREA
++0x6220 ProfileEventIndexAddress : Ptr64 Void
++0x6228 DpcWatchdogProfile : Ptr64 Ptr64 Void
++0x6230 DpcWatchdogProfileCurrentEmptyCapture : Ptr64 Ptr64 Void
++0x6238 PrcbPad94 : [9] Uint8B
++0x6280 SynchCounters : _SYNCH_COUNTERS
++0x6338 PteBitCache : Uint8B
++0x6340 PteBitOffset : Uint4B
++0x6348 FsCounters : _FILESYSTEM_DISK_COUNTERS
++0x6358 VendorString : [13] UChar
++0x6365 PrcbPad100 : [2] UChar
++0x6367 PendingVirtualLittle : UChar
++0x6368 FeatureBits : Uint8B
++0x6370 PrcbPad110 : Uint4B
++0x6378 UpdateSignature : _LARGE_INTEGER
++0x6380 Context : Ptr64 _CONTEXT
++0x6388 ContextFlagsInit : Uint4B
++0x6390 ExtendedState : Ptr64 _XSAVE_AREA
++0x6398 IsrStack : Ptr64 Void
++0x63a0 EntropyTimingState : _KENTROPY_TIMING_STATE
++0x64f0 AbSelfIoBoostsList : _SINGLE_LIST_ENTRY
++0x64f8 AbPropagateBoostsList : _SINGLE_LIST_ENTRY
++0x6500 AbDpc : _KDPC
++0x6540 IoIrpStackProfilerCurrent : _IOP_IRP_STACK_PROFILER
++0x6594 IoIrpStackProfilerPrevious : _IOP_IRP_STACK_PROFILER
++0x6600 LocalSharedReadyQueue : _KSHARED_READY_QUEUE
++0x6870 TimerExpirationTrace : [16] _KTIMER_EXPIRATION_TRACE
++0x6970 TimerExpirationTraceCount : Uint4B
++0x6978 ExSaPageArray : Ptr64 Void
++0x6980 SecureFault : _KSECURE_FAULT_INFORMATION
++0x69c0 Mailbox : Ptr64 _REQUEST_MAILBOX
++0x6a00 RequestMailbox : [1] _REQUEST_MAILBOX
+

Looking at the above structure shows a lot of great information about the system processor.You can find something like Current and Next Thread _KTHREAD, all information about CPU like type and capacity and manufacture company, DPC, Cycles, User-mode and Kernel-mode times and etc.

By the way, you now know that windows stores some of its internal information about CPU in nt!_PRCB and you should know find where this structure is located, I’m running an AMD64 version of Windows so it’s time to use gs:[0] in order to get the location of nt!_KPCR structure.

By using the following command you can get gs:[0] values :

1
+
dq gs:[0]
+

you can also use !pcr which gives you almost the same result :)

So, now we have where the _KPCR is located and now we should simply use :

1
+
dt <Address> nt!_KPCR
+

And after finding the pcrb now we should use dt in order to map the result into the _kpcrb structure.

 

If you want to be sure that you are in a right place you can search for Vendor string as follows :

 

It gives you kinds of hex which is equal to “GenuineIntel” and this is, of course, my CPU vendor.

 

 

After this, we know that we are in a right place so let see other fields in order to find something interesting!

After studying this structure now I see something that is so familiar to me and that is “Mhz”.

 

Can you see my CPU’s Mhz value ?!  Let me see my windows properties.

 

 

Ok, this is what we see previously, Doesn’t it ?!

 

This value (2.71 GHz) exactly equals to 0xA98 so changing this value must be interesting.

I find this value and changing it to 0x10 which is equal to 16 in decimal.

 

So we modified the value from 0x98 to 0x10 and now we want to see if it takes effect in system information or not, so let’s view system properties again.

 

That’s it, guys!

You can see we changed windows properties and now it says that you have a 16 Mhz CPU.Of course, this was an example of what we can do with prcb and it doesn’t affect on windows but you can do many other things by modifying this structure like changing threads or block a thread but keep in mind that editing this structure in a wrong way almost always causes the Blue Screen Of Dead.

Thanks for reading.

This post is licensed under CC BY 4.0 by the author.

Simple OpenLDAP + phpLDAPadmin setup

Get everything from .Net Reflection

Comments powered by Disqus.

diff --git a/topics/get-everything-from-net-reflection-by-two-method/index.html b/topics/get-everything-from-net-reflection-by-two-method/index.html new file mode 100644 index 00000000..0bf8dc01 --- /dev/null +++ b/topics/get-everything-from-net-reflection-by-two-method/index.html @@ -0,0 +1,145 @@ + Get everything from .Net Reflection | Rayanfam Blog
Home Get everything from .Net Reflection
Post
Cancel

Get everything from .Net Reflection

In the previous posts, I’d explained about low-level languages and play around kernel-mode reversing but now let’s dig into the high-level languages. In this post, I’m gonna share a comprehensive method that took more than two weeks for me to build such small and perfect function to play with .Net framework reflection. I also share this functions in my GitHub which can be accessed through this link.

.Net reflection gives .net framework a high flexibility by dynamically invoking functions from .net IL files and it has good features which can be used in order to build a plugin-based application in both desktop applications and web applications.

But for now, let me show you what these functions are and how we can use them.

I create two methods to cover all the possibilities in which a target function can be defined, the first one is for situations where you have a function that doesn’t need any argument.

It implemented as below :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
        static object InvokeAssemblyWithoutArgumant(string Path, string MethodName, object[] ArgumantsToContructor = null)
+        {
+            object ret = null;
+            System.Reflection.Assembly myDllAssembly =
+            System.Reflection.Assembly.LoadFile(Path);
+            if (ArgumantsToContructor == null)
+            {
+                foreach (Type item in myDllAssembly.GetTypes())
+                {
+                    ret = item.GetMethod(MethodName).Invoke(Activator.CreateInstance(item), null);
+                }
+            }
+            else
+            {
+                foreach (Type item in myDllAssembly.GetTypes())
+                {
+                    ret = item.GetMethod(MethodName).Invoke(Activator.CreateInstance(item, ArgumantsToContructor), null);
+                }
+            }
+            return ret;
+        }
+

and the second one :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
       static object InvokeAssemblyWithArgumant(string Path, string MethodName, object[] argumantToMethod, object[] ArgumantsToContructor = null)
+        {
+            object ret = null;
+            System.Reflection.Assembly myDllAssembly =
+            System.Reflection.Assembly.LoadFile(Path);
+            if (ArgumantsToContructor == null)
+            {
+                foreach (Type item in myDllAssembly.GetTypes())
+                {
+                    ret = item.GetMethod(MethodName).Invoke(Activator.CreateInstance(item), argumantToMethod);
+                }
+            }
+            else
+            {
+                foreach (Type item in myDllAssembly.GetTypes())
+                {
+                     ret = item.GetMethod(MethodName).Invoke(Activator.CreateInstance(item, ArgumantsToContructor), argumantToMethod);
+                }
+            }
+            return ret;
+        }
+

The second one is used for situations where you wanna pass the parameter(s) to the function.

For instance …

Consider you built a class library (.dll plugin) from the Visual Studio with the following syntax :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
    public class Class1
+    {
+        public Class1(string Arg1)
+        {
+            //Make program ready for first usage
+            System.Windows.Forms.MessageBox.Show("Constructor Invoked !" + Arg1);
+
+        }
+        public static string MyPluginFunction1(int a, int b)
+        {
+            System.Windows.Forms.MessageBox.Show("I'm here in MyPluginFunction.(Args:" + a + "-" + b + ")");
+            // Do what to want to do as a plugin
+            return "Successful";
+        }
+
+        public static string MyPluginFunction2()
+        {
+            System.Windows.Forms.MessageBox.Show("I'm here in MyPluginFunction (without arg).");
+            // Do what to want to do as a plugin
+            return "Successful";
+        }
+    }
+

If you want to call MyPluginFunction1 then you should use the second method which gives an array of object to pass to the method.

1
+2
+3
+4
+
            //dll path (Plugin Path)
+            string PathToPlugin = Environment.CurrentDirectory + "\\Plugin.dll"; // it's extension can be anything !
+            // you can also load assembly from a base64 string.
+            InvokeAssemblyWithArgumant(PathToPlugin, "MyPluginFunction1", new object[] { 1, 2 }, new object[] { "Sample Arg to constructor" });
+

If you want to call MyPluginFunction2 then you should use the first method which invokes the method directly without any arguments.

1
+2
+3
+4
+
            //dll path (Plugin Path)
+            string PathToPlugin = Environment.CurrentDirectory + "\\Plugin.dll"; // it's extension can be anything !
+            // you can also load assembly from a base64 string.
+            InvokeAssemblyWithoutArgumant(PathToPlugin, "MyPluginFunction2", new object[] { "Sample Arg to constructor" });
+

Important Note: If you have an exception like, object reference not set to an instance it is because the method name is incorrect or you invoke a non-static function within a static function or invoke a static function within a non-static function so please keep in mind the invoker function and target function should have the same state.

 

This post is also available on Github :

https://github.com/SinaKarvandi/UseAllOfDotNetReflection

This post is licensed under CC BY 4.0 by the author.

Fooling Windows about its internal CPU

Getting started with Windows Server command line

Comments powered by Disqus.

diff --git a/topics/getting-started-with-windows-server-command-line/index.html b/topics/getting-started-with-windows-server-command-line/index.html new file mode 100644 index 00000000..4ff6ee22 --- /dev/null +++ b/topics/getting-started-with-windows-server-command-line/index.html @@ -0,0 +1,55 @@ + Getting started with Windows Server command line | Rayanfam Blog
Home Getting started with Windows Server command line
Post
Cancel

Getting started with Windows Server command line

Hello everyone, In this post I am going to introduce some basic commands that are used to configure Windows Server. Of course if you are using Windows Server with GUI, you may not need these command at all (except for automation maybe). However if you work in an environment in which you configure windows network on a regular basis, you need to know some basic stuff B)

OK, Imagine you have installed Windows Server Core 2012 R2 on a server and now want to set basic configuration of your server (It’s highly possible that other versions of windows are configured exactly the same (like even windows 7 etc), but It is not tested by me):

Network:

1
+2
+3
+4
+5
+
# ip address
+netsh interface ipv4 address name="Local Area Network" source=static address=192.168.1.10 mask=255.255.255.0 gateway=192.168.1.1
+
+# dns server
+netsh interface ipv4 add dnsserver name="Local Area Connection" address=8.8.8.8
+

Hostname:

1
+2
+3
+4
+5
+
# view hostname (output of this command shows hostname)
+hostname
+
+# change hostname
+netdom renamecomputer WINSRV1 /NewName:WINSRV2
+

(you should reboot after setting hostname)

reboot:

1
+
shutdown /r /t 0
+

okay, now we want to join this computer to a domain:

Joining to a domain:

1
+
netdom join WINSRV2 /domain:lab.local /Userd:Administrator /password:Ab123456@
+

(you should reboot after joining to domain)

Installing a Role:

well, windows server is of no use without any roles installed. So as an example we will install Internet Information Services (IIS):

1
+2
+3
+4
+5
+6
+7
+8
+
# the first method uses pkgmgr and is kinda obsolete. but if it works and is supported on your platform then no need to change... :)
+
+
+start /w pkgmgr /l:log.etw /u:IIS-WebServerRole;WAS-WindowsActivationService;WAS-ProcessModel;WAS-NetFxEnvironment;WAS-ConfigurationAPI
+
+# the second command uses powershell cmdlets and is newer and will be supported in future versions of windows (probably even as the main method of interacting with windows server)
+import-module servermanager
+add-windowsfeature web-server -includeallsubfeature
+

Enable PowerShell Remoting:

PowerShell remoting enables you to run powershell command over the network on other computer. I know many powershell or cmd command for management have -ComputerName or similar argument for specifying the destination computer, but most of them work over non-standard ports and each of them may use a different microsoft remote managemnt facility. Furthermore you may run into strange permission or other problems. So it won’t result in a smooth remote exprience.

To enable PS Remoting run this command

(it can be enabled with Group Policy if you have a lot of systems to manage remotely):

1
+
Enable-PSRemoting Force
+

then connect to them like this:

1
+2
+3
+4
+5
+
Enter-PSSession Computername
+
+# specifying credentials be like:
+
+Enter-PSSession Computername "WINSRV2" Credential "LAB\administrator"
+

or if you want to run a command of multiple machines in parallel:

1
+
Invoke-Command name WINSRV1, WINSRV2, PC3 ScriptBlock {Get-Process}
+

That is the end of this post! Please feel free to comment below.

Thanks

This post is licensed under CC BY 4.0 by the author.

Get everything from .Net Reflection

Detecting CPU Structure in .Net Framework

Comments powered by Disqus.

diff --git a/topics/gre-over-ipsec-in-cisco-ios/index.html b/topics/gre-over-ipsec-in-cisco-ios/index.html new file mode 100644 index 00000000..67a0429f --- /dev/null +++ b/topics/gre-over-ipsec-in-cisco-ios/index.html @@ -0,0 +1,141 @@ + GRE over IPSec in Cisco IOS | Rayanfam Blog
Home GRE over IPSec in Cisco IOS
Post
Cancel

GRE over IPSec in Cisco IOS

Cisco GRE over IPSEC

gre-ipsec-diagram

In the following is a sample configuration for GRE/IPSEC in Cisco IOS devices. you can tailor this to your needs (changing encryption algorithms, IP addresses etc), just remember to make sure they are the same on both sides (except for IP of course).

In later posts we will discover how to do this scenario in other platforms like Cisco ASA and StrongSWAN on Linux.

The best guide for learning how IPSec works: http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-77.pdf

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
+
R1(config)# interface Tunnel0
+R1(config-if)# ip address 172.16.0.1 255.255.255.0
+R1(config-if)# ip mtu 1400
+R1(config-if)# ip tcp adjust-mss 1360
+R1(config-if)# tunnel source 1.1.1.10
+R1(config-if)# tunnel destination 2.2.2.10
+R1(config)# ip route 192.168.2.0 255.255.255.0 172.16.0.2
+R1(config)# crypto isakmp policy 1
+
+R1(config-isakmp)# encr 3des
+
+R1(config-isakmp)# hash md5
+
+R1(config-isakmp)# authentication pre-share
+
+R1(config-isakmp)# group 2
+
+R1(config-isakmp)# lifetime 86400
+
+R1(config)# crypto isakmp key CISCO address 2.2.2.10
+
+R1(config)# crypto ipsec transform-set TS esp-3des esp-md5-hmac
+
+R1(cfg-crypto-trans)# mode transport
+
+R1(config)# crypto ipsec profile protect-gre
+
+R1(ipsec-profile)# set security-association lifetime seconds 86400
+
+R1(ipsec-profile)# set transform-set TS
+
+R1(config)# interface Tunnel 0
+
+R1(config-if)# tunnel protection ipsec profile protect-gre
+
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
+
R2(config)# interface Tunnel0
+R2(config-if)# ip address 172.16.0.2 255.255.255.0
+R2(config-if)# ip mtu 1400
+R2(config-if)# ip tcp adjust-mss 1360
+R2(config-if)# tunnel source 2.2.2.10
+R2(config-if)# tunnel destination 1.1.1.10
+
+R2(config)# ip route 192.168.1.0 255.255.255.0 172.16.0.1
+
+R2(config)# crypto isakmp policy 1
+
+R2(config-isakmp)# encr 3des
+
+R2(config-isakmp)# hash md5
+
+R2(config-isakmp)# authentication pre-share
+
+R2(config-isakmp)# group 2
+
+R2(config-isakmp)# lifetime 86400
+
+R2(config)# crypto isakmp key CISCO address 1.1.1.10
+
+R2(config)# crypto ipsec transform-set TS esp-3des esp-md5-hmac
+
+R2(cfg-crypto-trans)# mode transport
+
+R2(config)# crypto ipsec profile protect-gre
+
+R2(ipsec-profile)# set security-association lifetime seconds 86400
+
+R2(ipsec-profile)# set transform-set TS
+
+R2(config)# interface Tunnel 0
+
+R2(config-if)# tunnel protection ipsec profile protect-gre
+
This post is licensed under CC BY 4.0 by the author.

Cisco IOS and StrongSWAN IPSEC VPN

Simple OpenLDAP + phpLDAPadmin setup

Comments powered by Disqus.

diff --git a/topics/gui-python-qt/index.html b/topics/gui-python-qt/index.html new file mode 100644 index 00000000..6796c686 --- /dev/null +++ b/topics/gui-python-qt/index.html @@ -0,0 +1,45 @@ + Getting started with Python and Qt for cross-platform GUI apps | Rayanfam Blog
Home Getting started with Python and Qt for cross-platform GUI apps
Post
Cancel

Getting started with Python and Qt for cross-platform GUI apps

Hi everyone, in this blog post I want to explain how to start developing desktop applications using Qt and Python. We will use PyQt5 module in order to connect Qt with Python. Qt was originally designed for use with C++ but it has many benefits which helped its growth and compability on all major desktop platforms (Windows, Linux, Mac OS). A link to a sample program I created is also given at the end of the post in order to help you understand the basics and maybe use it as a skeleton for your project.

First we start “Qt Designer” which helps us create a GUI for our application. This step is the same regardless of which programming language you want to use for the backend and logic of your program.

We drag and drop two buttons and a text label into our main window. We will also give them meaningful names: “prev_button” and “next_button”. “TextLabel” name seems fine for now.

Qt Designer screenshot

We can change item properties using the “property editor” on the right:

Qt Designer Property Editor screenshot

In the panel under “Property Editor” you can also see the name “Signal/Slot Editor” which is an important functionality in Qt. but for now we won’t cover this and only stick to the very basics.

Okay, you can fiddle with Qt Designer if you like, finally you should save it somewhere on your computer with the format “.ui (Designer UI Files)”.

*** It’s import to know that we are using Python3 and PyQt5 in this tutorial ***

Now we should open some IDE or text Editor and start working on the backend python application.

we start by importing the required modules and specifying ui file for the application to use:

1
+2
+3
+
from PyQt5 import QtCore, uic, QtWidgets
+qtCreatorFile = "btc.ui" # Enter file here.
+Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
+

You should also tell python to start a GUI application!:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
class MyApp(QtWidgets.QMainWindow, Ui_MainWindow):
+
+def __init__(self):
+    QtWidgets.QMainWindow.__init__(self)
+    Ui_MainWindow.__init__(self)
+    self.setupUi(self)
+    #.... you application continues here ....
+
+if __name__ == "__main__":
+    app = QtWidgets.QApplication(sys.argv)
+    window = MyApp()
+    window.show()
+    sys.exit(app.exec_())
+

It is not important of you do not understand what these lines mean at first. Gradually you will learn everything.

I recommend you open the template file and continue your work from that. It is mych easier than typing all of these or copying them from here. the next important thing is connecting the buttons on the screen to some functions in our application, or else this buttons won’t do anything when pressed. so you add this line in __init__ in your main qt app class:

1
+
self.prev_button.clicked.connect(self.GoPrev)
+

It says that when user clicked “prev_button” run the function “GoPrev” the same way for this:

1
+
self.next_button.clicked.connect(self.GoNext)
+

then we define our click functions which should only take one argument which is self:

1
+2
+
def GoPrev(self):
+    ....
+

and do you stuff there or call other functions from there. There may also be other ways of doing this which I’m not aware of. I’m not a professional Qt developer anyway! One more thing, this is how you set the text for a label:

1
+
self.label.setText("This is a useless text box")
+

Now you know the basics and can google your way for interacting with more controls and using advanced functions of Qt. The python code and UI file should be together for the program to start.

For now we won’t cover how to package it into a single executable file or handle Qt dependencies. It’s good to know this program can be run without any modification on Windows, Linux or Mac OS or any other operating system which supports Qt! How great is that?!

The sample given below is an application which retrieves bitcoin price from CoinDesk API and shows it to you. In addition to the current price shown on app start, you can use Next and Previous buttons to view historical data.

run it like this:_

1
+
$ python3 btc.py
+

_I hope it will be both educational and useful!

The template file can also be found in the same github project folder with the name “template.py”

Link:

[ https://github.com/Xcess/python_qt_start ]

 

Have fun developing everyone!

Shahriar

This post is licensed under CC BY 4.0 by the author.

Detecting CPU Structure in .Net Framework

Pinitor - An API Monitor Based on Pin

Comments powered by Disqus.

diff --git a/topics/hardware-design-stack/index.html b/topics/hardware-design-stack/index.html new file mode 100644 index 00000000..485a56fc --- /dev/null +++ b/topics/hardware-design-stack/index.html @@ -0,0 +1,749 @@ + Building Silicon Dreams: An Adventure in Hardware Design | Rayanfam Blog
Home Building Silicon Dreams: An Adventure in Hardware Design
Post
Cancel

Building Silicon Dreams: An Adventure in Hardware Design

Story Time

Exploring the internals of processors has long been a fascination of mine. After spending a lot of time experiencing different processor features like hypervisor and configuring different x86 MSRs, I was seeking to find a way of knowing how exactly these concepts and configurations are made and how they work on the silicon.

Initially, my knowledge of hardware design was limited to a basic understanding of VHDL and Verilog gained from a microarchitecture design class at university. However, I decided to investigate and go deeper into this field and acquire more knowledge, which I am now eager to share with you.

This article is neither a deep dive into the hardware design nor does it assume any prior knowledge of hardware design. Instead, it provides an overview of fundamental hardware design concepts, introduces the tools and devices used in the process, and aims to give you a foundational understanding of the hardware design industry.

My journey in hardware design started when I was looking to find the actual implementation of different processors. After that, I came around two major open-source RISC-V processors.

These projects are one of the best implementations of RISC-V architecture in which you can use the source code to modify the microarchitecture, add/modify/remove CPU instructions, and finally synthesize the generated Verilog codes into a Xilinx FPGA or if you’re a millionaire you can give your design to TSMC to fabricate it for you :)

For those who don’t know about RISC-V, it’s an architecture (just like x86 and AMD64) that mainly comes with RISC architecture. There are two fundamental architectures in computer design: 1) CISC and 2) RISC.

The primary distinction between Complex Instruction Set Computer (CISC) and Reduced Instruction Set Computer (RISC) architectures lies in their approach to instruction execution. CISC, exemplified by the x86 architecture, typically employs a large and varied instruction set, often with complex addressing modes, allowing a single instruction to perform multiple operations. In contrast, RISC architectures like ARM and RISC-V utilize a simplified instruction set with fixed-length instructions, focusing on streamlined, single-cycle instruction execution.

Based on these explanations, I think we can start our journey from the designing steps of a chip and finally synthesizing into an FPGA our making GDSII (more about it later) layouts.

Please bear in mind, as you read the rest of this article, it is written by someone with a primary background in OS security and low-level programming, rather than being an expert hardware designer. Furthermore, this article is not an in-depth exploration but rather a high-level overview that provides technical examples and explanations to elaborate on the concepts and distinctions. I would be happy to hear if you have any feedback, corrections, and discussion points. You can use the comments section below.

Table of Contents

  • Story Time
  • Table of Contents
  • Introduction
    • Analog Signals/Protocols
    • Digital Signals/Protocols
  • Source Code
  • Moore’s Law & Amdahl’s Law
  • Terms
  • Hardware Description Languages (SystemVerilog, Verilog, VHDL)
  • Generating Hardware Using Chisel
  • C/C++ in Hardware Design
    • Vitis HLS
  • Simulating Codes
    • Simulating HDL Code Using GTKWave or ModelSim
    • Testing Codes in Chisel
    • Testing HDL Codes Using Verilator
  • Synthesising HDL Codes
    • Programming Xilinx FPGAs Using Vivado
    • Vivado Netlists
  • The Memory
    • Distributed RAMs in FPGAs
    • Block RAM (BRAM) in FPGAs
      • Static RAM (SRAM) in ASIC
  • Elaborating ASIC Designs
  • Building Hardware Layout (GDSII)
  • Reverse Engineering Netlists
  • Conclusion
  • References

Introduction

Over the past decades, the silicon industry changed drastically reaching from 10 µm (1973) to 3 nm (2022). Shrinking chips offer advantages including increased performance, higher integration density, reduced power consumption, improved heat dissipation along economic advancements. These benefits will drive the technological companies to contribute to smaller, more powerful, and energy-efficient chips. However, it also brings challenges related to manufacturing complexity and fundamental physical limits as transistors approach atomic scales.

Wavelength (µm)10631.510.80.60.350.250.180.130.090.0650.0450.0320.0220.0140.010.0070.0050.0030.002
Year1971197419771981198419871990199319961999200120032005200720092012201420162018202020222024

However, there are technological limitations that prohibit the shrinking of the transistors (later will be discussed as Moore’s law).

Now, let’s see the hardware design from a broader view. Have you ever asked yourself why computers are 0’s and 1’s? There are plenty of scientific and engineering theories and concepts that describe why computers are using 0’s and 1’s (and using boolean algebra), but a very simple explanation is that the current technology of semiconductors works best with zero and one (high and low voltage) signals rather than analog signals. It’s always more efficient to use analog devices as long as we’re not concerned with errors! If it was not because of errors, then probably the current devices that rely on deterministic and correct results are all made of analog devices. If I don’t want to oversimplify things, I can say, that while it’s true that some functions can be efficiently performed using analog signals (e.g., audio and video processing), digital computing offers several advantages, including accuracy, precision, ease of replication, and error correction.

The same is true for digital/analog protocols. Usually, it’s more efficient to send data over analog signals as you can send data at higher bitrate and longer distances. But, why there are digital signals while analog is naturally better? Simple answer, because analog has errors and you cannot guarantee the data will be correct at both sender and receiver.

Here is a list of digital and analog protocols.

Analog Signals/Protocols:

  • VGA (Video Graphics Array)
  • RCA (Radio Corporation of America)
  • XLR (External Line Return)
  • 3.5mm Audio Jack
  • Component Video
  • Composite Video
  • S-Video (Separate Video)
  • HDMI (High-Definition Multimedia Interface)
  • DVI (Digital Visual Interface) - Some variants support analog signals.
  • VGA-to-HDMI Converters (Analog to Digital conversion)

Digital Signals/Protocols:

  • USB (Universal Serial Bus) [Usually]
  • SPI (Serial Peripheral Interface)
  • I2C (Inter-Integrated Circuit)
  • UART (Universal Asynchronous Receiver-Transmitter)
  • Ethernet (IEEE 802.3)
  • HDMI (High-Definition Multimedia Interface) - Also supports digital audio.
  • DisplayPort
  • PCIe (Peripheral Component Interconnect Express)
  • SATA (Serial ATA)
  • CAN (Controller Area Network)

Having the above knowledge, let’s explore each step more comprehensively. The rest of this article covers how digital chips are designed at different levels and discusses different tools that are used at each step.

Source Code

The full source code of the codes used in this blog post is available on GitHub : [https://github.com/SinaKarvandi/hardware-design-stack]

Moore’s Law & Amdahl’s Law

There are two important laws in computer architecture. One is Moore’s law and the other is Amdahl’s law.

Moore’s law is defined as, an observation (prediction) that the number of transistors on a microchip roughly doubles every two years, whereas its cost is halved over that same timeframe.

If you have studied computer science at the university, you probably saw that professors always use a slide about Moore’s Law, seeking to justify their lessons, and declare that Moore’s Law is no longer true! Thanks to ivs for mentioning that what they really mean is that the combination of Dennard scaling and Moore’s law isn’t true anymore. Moore’s law by itself is just fine.

Amdahl’s law on the other hand is a formula that gives the theoretical speedup in latency of the execution of a task at a fixed workload that can be expected of a system whose resources are improved. It states that “the overall performance improvement gained by optimizing a single part of a system is limited by the fraction of time that the improved part is actually used”.

But why that is important? The fact that there are physical limitations and barriers to further expanding/shrinking transistors (both because of the power consumption and speed), makes it important to design effective and efficient hardware that is capable of performing the computation at a higher speed with less transistor consumption. As a simple example, there are multiple types of adders to perform the addition of two operands, and each of them is designed for particular purposes and is good to be used in specific operations like Ripple Carry Adder (RCA), Carry Look-Ahead Adder (CLA), Carry Save Adder (CSA), etc.

Okay, but how do the programmers describe these designs? The answer is, that they use HDL languages to describe the hardware at different levels.

Terms

Before exploring more hardware design concepts, let’s explain a few terms that we use in this article.

FPGA (Field-Programmable Gate Array): FPGAs are versatile hardware devices that can be customized by users after manufacturing, allowing them to reconfigure the chip’s functionality to suit specific tasks or applications.

ASIC (Application-Specific Integrated Circuit): ASICs are specialized integrated circuits designed for a particular application, often offering optimized performance and power efficiency, but they are not reprogrammable like FPGAs.

RTL (Register-Transfer Level): RTL is a hardware description language abstraction that represents the behavior of digital circuits at a level where data movement between registers and the associated logic operations are defined, making it a crucial step in designing hardware.

Netlist: A netlist is a textual or graphical representation of a circuit’s components (gates, flip-flops, etc.) and their interconnections at a lower level than RTL but higher than physical implementation.

Synthesis: Synthesis is the automated process of converting a high-level hardware description into a lower-level netlist for hardware implementation, optimizing for efficiency. A code that can make hardware is called a synthesizable code.

AXI: The AXI (Advanced eXtensible Interface) protocol is a widely used high-performance, on-chip communication protocol designed for efficient data transfer and interconnection of IP (Intellectual Property) blocks in System-on-Chip (SoC) and FPGA designs.

Hardware Description Languages (SystemVerilog, Verilog, VHDL)

Almost all of the hardware designs (like CPUs, RAMs, GPUs, etc.) are (can be) programmed using HDL or Hardware Description Languages. The most amazing thing about HDL languages is that these languages allow you to write programs that do a simple task (AND, OR, XOR, etc.) in a very short and precise slice of time and you can achieve precise control over a multitude of tasks within a fraction of a second!

For example, assume that you want to write a program that adds two operands. Generally in assembly, we use an instruction like ADD and add a value to a register like ADD RAX, 0x55. There are plenty of stages that a processor will follow from fetch and decoding to executing. But in the final step, the values will be added in a very short fraction of time using an adder in the arithmetic logic unit (ALU). Imagine you have a processor/FPGA with a 200 MHz clock cycle speed and each addition of an adder took one cycle to complete. Let’s look at a simple formula:

Number of Clock Cycles = Clock Frequency (Hz) * Time (seconds) Number of Clock Cycles = 200,000,000 Hz * 1 second = 200,000,000 cycles

From the above example, you can see that you would able to perform 200,000,000 additions in one second and you’re sure that it took only one second to complete! You can gain control of this very small and precise fraction of time (1/200,000,000 second) by using HDL programming languages.

There are three famous HDL programming languages: VHDL, Verilog, and SystemVerilog.

Let’s see a very simple example of a VHDL code that toggles a port to zero and one by each clock.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+
+entity Switch_Control is
+    Port (
+        clock   : in  STD_LOGIC;       -- clock input
+        switch  : out STD_LOGIC := '0' -- Initialize switch to OFF (logic low)
+    );
+end Switch_Control;
+
+architecture Behavioral of Switch_Control is
+    signal toggle : STD_LOGIC := '0'; -- Signal for toggling the switch
+begin
+    process(clock)
+    begin
+        if rising_edge(clock) then
+            toggle <= not toggle; -- Toggle the switch state on rising edge of the clock
+        end if;
+    end process;
+
+    switch <= toggle; -- Assign the switch state to the output port
+end Behavioral;
+
+

The above VHDL code defines an input port “clock” and an output port “switch” and inside the architecture block, there’s a process sensitive to the rising edge of the “clock” signal. When the clock signal rises, the “toggle” signal is toggled (inverted).

The above code is also written in Verilog:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
module Switch_Control (
+    input  wire clock,        // Clock input
+    output reg  switch = 0   // Initialize switch to OFF (logic low)
+);
+
+reg toggle = 0; // Signal for toggling the switch
+
+always @(posedge clock) begin
+    toggle <= ~toggle; // Toggle the switch state on rising edge of the clock
+end
+
+always @(toggle) begin
+    switch = toggle; // Assign the switch state to the output port
+end
+
+endmodule
+

This code defines a module named Switch_Control with an input wire clock and an output register switch which is initialized to 0 (logic low). Inside the module, there’s a register named toggle initialized to 0. Effectively, this Verilog module also creates a digital switch that toggles its state on each rising edge of the clock input, and the switch output reflects this state.

The SystemVerilog’s syntax is also (more or less like Verilog) with a couple of differences. Actually, SystemVerilog extends Verilog by adding several new features and enhancements.

As you can see the above codes have one input and one output. The above code can be fabricated into a chip. The imaginary chip contains two pins, one for input and one for output. These pins can be made with different IC package types like DIP, SIP, etc. with different technologies.

IC packages

The problem with these HDL languages is that it’s so hard and frustrating to develop hardware with these languages. Even though these languages give the best control over every single part of the hardware, it can be really time-consuming to design simple hardware. Because of these reasons, hardware designers made other languages like Chisel, SystemC, and HLS that deal with higher-levels of abstractions which makes it possible to write less code; thus, speeding up and easing the development.

For now, we have a very basic understanding of HDL languages and how chips are digital chips are designed, now we can follow our journey to the next steps.

Generating Hardware Using Chisel

Personally, I have to admit that the Chisel language is one of the most exciting languages that I’ve ever dealt with. I really love the way they engineered a functional programming language like Scala and made a hardware generator language. The Constructing Hardware in a Scala Embedded Language (Chisel) is an open-source hardware description language (HDL) used to describe digital electronics and circuits at the register-transfer level (RTL). Think of it as a language that creates Verilog (or SystemVerilog) codes from the Scala codes. Later you can use the generated Verilog files to synthesize and program FPGAs or fabricate them in ASIC designs.

Chisel adds hardware construction primitives to the Scala programming language, providing designers with the power of a modern programming language to write complex, parameterizable circuit generators that produce synthesizable Verilog.

There are plenty of resources to start learning Chisel and it’s a large-scale project that is actively maintained by CHIPS Alliance and it’s used for huge projects like Rocket Chip Generator.

Let’s see a very simple example of a Chisel code:

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
+
import chisel3._
+import chisel3.util.Counter
+import circt.stage.ChiselStage
+
+class Blinky(freq: Int, startOn: Boolean = false) extends Module {
+  val io = IO(new Bundle {
+    val led0 = Output(Bool())
+  })
+  // Blink LED every second using Chisel built-in util.Counter
+  val led = RegInit(startOn.B)
+  val (_, counterWrap) = Counter(true.B, freq / 2)
+  when(counterWrap) {
+    led := ~led
+  }
+  io.led0 := led
+}
+
+object Main extends App {
+  // These lines generate the Verilog output
+  println(
+    ChiselStage.emitSystemVerilog(
+      new Blinky(1000),
+      firtoolOpts = Array("-disable-all-randomization", "-strip-debug-info")
+    )
+  )
+}
+

This Chisel code snippet is for a digital circuit that blinks an LED at a specified frequency. Defines a Chisel module named Blinky with a parameter for the frequency and an optional parameter for the initial LED state. Inside the module, it declares an output port led0 of type Bool to drive an LED. The LED state is stored in a register named led and initialized with the value provided in the startOn parameter. When the counter wraps around, it toggles the led state.

In digital designs, the clock signal is the perception of time and it’s used for synchronizing different parts of the chips. As you can see in the above code, it’s a simple LED blinker and as you can see, there is an output signal named led0. But did you notice that it didn’t specify any inputs? How can we make a blinker without knowing or being notified about the time (or more precisely without any perception of the clock signal)?

The answer to this question lies in the fact that in languages like Chisel, the clock signal is masked and handled by the Chisel and it’s not exported (revealed) to the hardware designer. In other words, the generated Verilog files from the above code actually contain the clock signal (and a reset signal). But it’s actually handled by the Chisel instead of the programmer. Let’s see the generated code from the above Chisel code.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
module Blinky(
+  input  clock,
+         reset,
+  output io_led0
+);
+
+  reg       led;
+  reg [8:0] counterWrap_c_value;
+  always @(posedge clock) begin
+    if (reset) begin
+      led <= 1'h0;
+      counterWrap_c_value <= 9'h0;
+    end
+    else begin
+      automatic logic counterWrap = counterWrap_c_value == 9'h1F3;
+      led <= counterWrap ^ led;
+      if (counterWrap)
+        counterWrap_c_value <= 9'h0;
+      else
+        counterWrap_c_value <= counterWrap_c_value + 9'h1;
+    end
+  end // always @(posedge)
+  assign io_led0 = led;
+endmodule
+

As you can see, there is actually a clock and a reset signal in addition to the io_led0 output. Using Chisel, you have less control over the hardware compared to writing codes in Verilog or VHDL, but still, you have the benefit of using a high-level language like Scala (Java). Moreover, you have the ability to integrate Verilog codes into the Chisel designs and write some parts of the codes in Chisel while using Verilog for remaining parts.

C/C++ in Hardware Design

When we talk about C/C++ in hardware design, we have to consider two different paradigms.

  1. The first approach is using C/C++ codes to program FPGA. Modern FPGAs like the Xilinx ZYNQ series come with two different units, the processing system (PS), and the FPGA programmable logic (PL). The PS part is usually an ARM processor that is connected directly to the PL parts (e.g., through AXI interface). So, you can write specific C/C++ codes compile them for a regular ARM processor, and use it in the FPGA. Usually, it’s super hard if someone wants to implement all protocols (e.g., network protocols, etc.) using the PL part. In these cases, hardware programmers make PL designs that are mainly responsible for running the computational part of a program and finally send the results back to the ARM processor and this ARM processor is responsible for synchronizing or sending/receiving data over the network. You can think of a dozen of examples how this PS part might be useful once it’s connected to the PL part. If you’re interested in this topic, you can see this tutorial as it covers a lot of exciting applications of using PL and PS together.

  2. The second approach is using C/C++ codes to design hardware or generate RTL codes. As an example, we can use Vitis HLS to generate RTL codes. High-level synthesis (HLS) is an automated design method. It starts with a high-level description of how a digital system should behave and then automatically generates the detailed hardware structure that makes that behavior possible at the register-transfer level.

ModelSim

Vitis HLS

Let’s run a simple HLS example using Xilinx Vitis. Before that, here’s a top view of how HLS works:

Vitis HLS

For example, we run a simple AXI lite connection interface using HLS (from the Vitis example codes).

Vitis HLS AXI lite

There are three main phases, C Synthesis, C/RTL Cosimulation, and Implementation. In the first phase, the Vitis synthesizer tries to interpret the C codes, after that, we can write codes for testing our design and finally, the RTL generated code (both in Verilog and VHDL format) is ready!

Here are the generated/exported AXI lite codes by Vitis HLS, and this is VHDL files and Verilog files of our design.

Simulating Codes

Once you finished writing your HDL codes or generating HDL codes from the Chisel (Scala) generator, it’s time to test the code using a simulator. The simulator works like a debugger and it’s an environment to simulate the hardware behavior to test whether the designed logic works properly or contains error. First, let’s simulate HDL codes.

Simulating HDL Code Using GTKWave or ModelSim

For simulating and testing VHDL and Verilog codes, we need to write a testbench. A testbench in Verilog, or VHDL is a file that is used exclusively for simulation, it’s a piece of non-synthesizable code that tries to simulate signals (input/output) to the designed logic, so you can see how your design behaves on different signals and inputs and what would be the final output.

For example, for the above toggle VHDL code, we write the following testbench:

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
+
library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.STD_LOGIC_ARITH.ALL;
+use IEEE.STD_LOGIC_UNSIGNED.ALL;
+
+entity Switch_Control_tb is
+end Switch_Control_tb;
+
+architecture testbench of Switch_Control_tb is
+    -- Signals for testbench
+    signal clock_tb  : STD_LOGIC := '0'; -- Testbench clock signal
+    signal switch_tb : STD_LOGIC;        -- Testbench switch signal
+
+begin
+    -- Instantiate the Switch_Control DUT
+    DUT: entity work.Switch_Control
+        port map (
+            clock   => clock_tb,
+            switch  => switch_tb
+        );
+
+    -- Clock generation process (generates a clock signal)
+    process
+    begin
+        while true loop
+            clock_tb <= not clock_tb; -- Toggle the clock
+            wait for 5 ns;            -- Clock period (adjust as needed)
+        end loop;
+    end process;
+
+end testbench;
+

This VHDL testbench creates a clock signal and connects it to the Switch_Control while monitoring the output signal switch_tb to observe the behavior of the chip during simulation.

The Verilog version of the testbench is available here.

Now it’s time to test our testbench.

Probably the most famous tool for testing HDL codes is ModelSim. There are also other tools like internal Vivado simulator (previously named isim) or GTKWave but in my opinion, ModelSim is better as it offers more functionalities along with a powerful scripting capability. Moreover, you don’t have to necessarily install Vivado to use ModelSim. You can just install ModelSim and test (simulate) your code.

The following picture is the simulation signal of the above testbench using ModelSim:

ModelSim

And this is the simulation result of the Vivado’s internal simulator:

Vivado simulator

Keep in mind that, what you will see in the simulator environment might not necessarily behave the same once it is fabricated as a chip or is programmed into an FPGA, so there are different pre- and post-synthesis simulations plus plenty of other physical and electrical considerations that might affect the behavior of the circuit, but it usually reflects the correct behavior of what you can expect from a circuit and you can find possible design errors. By the way, it’s a program that simulates the circuit, not a physical circuit!

Testing Codes in Chisel

Chisel is based on Scala and as it’s a high-level programming language, it gives you unlimited power of testing and writing testbenches. For example, imagine you write a code that is designing an arithmetic unit for a processor. You can generate dozens of random numbers and statements, write some Scala (or even Java) codes to compute a formula or an expression, and then provide these random values to your designed circuit, and compare the output of your circuit with the results of the formula.

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
+
import chisel3._
+import chisel3.util._
+import chiseltest._
+import org.scalatest.flatspec.AnyFlatSpec
+import scala.util.control.Breaks._
+
+class ToggleTest extends AnyFlatSpec with ChiselScalatestTester {
+  "DUT" should "pass" in {
+
+    test(
+      new Toggle(
+		//
+		// Arugments to parameterize the chisel designs
+		//
+      )
+    ) { dut =>
+	
+      // First, set an input called "start" to 0
+      dut.io.start.poke(0.U)
+	  
+	  // Run the circuit for one-clock
+      dut.clock.step(1)
+
+      // Perform the first test
+      val test1 = Integer.parseInt(TestingSample.SampleFormulaFunctions(1, 2).U
+
+      // Set the start to 1
+      dut.io.start.poke(1.U)
+
+	  // Set an input called "in1" to the test1 value
+      dut.io.in1.poke(test1)
+
+	  // Run the circuit for one-clock
+      dut.clock.step(1)
+
+      // Check the output from "out1" with a value like 0 
+      if (dut.io.out1.peek().litValue.toInt == 0) {
+        print("[*] Successful\n");
+      } else {
+        print("[x] NOT successful!\n");
+      }
+
+      // Remove the start bit again (disable chip)
+      dut.io.start.poke(0.U)
+    }
+  }
+}
+

This is a Chisel testbench to test a Chisel circuit with specific input conditions and check the output to determine if the circuit behaves as expected.

Initially, the start input is set to 0 using poke. The circuit is advanced by one clock cycle using dut.clock.step(1). Then, a test value is calculated using a function named TestingSample.SampleFormulaFunctions(1, 2) and converted to an integer. The start input is set to 1 to enable the circuit. The in1 input is set to the previously calculated test value using poke. The circuit is advanced by one clock cycle again. Finally, the output from out1 is checked. If it matches the expected value (0), it prints [*] Successful, otherwise [x] NOT successful!.

After that, you can run your test by using sbt, and it will make a .vcd file. The VCD file is passed as an input to the GTKWave and you can see different signals and what happens in each clock cycle and the final outputs. Of course, the results can be automatically tested with Scala but if you want to debug and find possible errors, you can see the signal waves.

If you want to use ModelSim with Chisel, I made an automated Python script to make necessary conversions between different formats and make Chisel results compatible with ModelSim, you can find it here, it also describes how you can use it.

Testing HDL Codes Using Verilator

Up until now, we have seen how to make HDL designs and we used Chisel to generate Verilog codes. We also see how we can test the HDL and Chisel codes. Now we encounter another problem and it’s speed. Testing complicated and big projects is time-consuming, we already wrote a very simple testbench but in reality, when we encounter a big project, it would take a lot of time to simulate a circuit. In order to solve this issue, hardware designers came across the idea of converting the simulation Verilog codes into C++ and compiling the simulation codes. Now, instead of interpreting codes and generating waves, everything was running directly in an executable binary file, you can assume it is like a Verilog code that is converted to C++ code but the generated C++ code is designed based on how integrated circuits work.

Verilator

The tool that is responsible for this conversion is Verilator and this conversion is called “verilate”. It verilates the specified Verilog or SystemVerilog code by reading it, performing lint checks, and optionally inserting assertion checks and coverage-analysis points. It outputs single- or multithreaded .cpp and .h files, the “Verilated” code.

For the demonstration, I verilated the toggle code that we wrote previously, you can see the generated CPP files here.

There is also a very good tutorial if you want to start learning verilator. You can also use Verilator directly with Chisel codes.

Now that we test our designed logic, it’s time to either implement the codes in FPGAs or fabricate them into chips.

Synthesising HDL Codes

Once we successfully pass the tests and simulate logic, it’s time to synthesize codes. There are two major FPGA vendors that design and fabricate FPGAs Xilinx (acquired by AMD) and Intel FPGAs (previously Altera). These two giant companies provide different tools for programming their FPGAs. Altera made Quartus while Xilinx offers Vivado. In this blog post, we use Vivado as the main tool.

Usually, the designs that are made for FPGAs are different from the ASICs. For example, there are plenty of considerations that make a Verilog design FPGA-efficient or ASIC-efficient. Most of the time, those codes that are generated for FPGAs use FPGA components like Block RAM (more about it later) or use FPGA IPs that make the code only synthesizable for FPGAs. The same is also true about ASIC design. Some designs might not be suitable for FPGAs and need to be fabricated in ASIC chips.

Programming Xilinx FPGAs Using Vivado

We can use the Vivado Design Suite to synthesize the Verilog code that we previously wrote and finally generate a bitstream file that can program an FPGA.

Vivado toggle Verilog code

Another essential step for Vivado is I/O pin planning and we can connect the clock source to our design ports or connect I/O pins (e.g., a key, LED, AXI, SPI pins, etc.). It finally produces a design constraints file (XDC file) that will guide Vivado through routing the FPGA pins.

Vivado I/O planning

For example, this is an XDC file that is used for connecting clock signals as well as some VGA I/O ports to FPGA pins.

1
+2
+3
+4
+5
+6
+7
+8
+
set_property PACKAGE_PIN F22 [get_ports reset]
+set_property IOSTANDARD LVCMOS33 [get_ports sys_clock]
+set_property PACKAGE_PIN AB19 [get_ports {VGA_B[3]}]
+set_property PACKAGE_PIN AB20 [get_ports {VGA_B[2]}]
+set_property PACKAGE_PIN Y20 [get_ports {VGA_B[1]}]
+set_property PACKAGE_PIN Y21 [get_ports {VGA_B[0]}]
+set_property PACKAGE_PIN AA21 [get_ports {VGA_G[3]}]
+...
+

Finally, it elaborates the code, makes schematic files, and generates bitstreams. Here is the schematic view generated for our toggle example.

Vivado schematic

The schematic file of the above design is available here.

Vivado Netlists

Before making the bitstream file needed for programming FPGA, the codes are converted to a lower abstraction level called netlist which is basically specific to the target FPGA components and contains the necessary components along with their interconnection to implement the actual designed logic and satisfy the design requirements.

For example, assume the very first examples (Verilog code) that we made for toggling switches. We could use the following TCL commands (Vivado command-line) to generate Verilog and VHDL functional simulation netlist:

For generating Verilog netlists:

1
+
write_verilog -mode funcsim Verilog_Netlist_Toggle.v
+

For generating VHDL netlists:

1
+
write_vhdl -mode funcsim VHDL_Netlist_Toggle.vhd
+

The final netlist is a new Verilog file (netlist file):

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
+
// Copyright 1986-2022 Xilinx, Inc. All Rights Reserved.
+// --------------------------------------------------------------------------------
+// Tool Version: Vivado v.2022.2 (win64) Build 3671981 Fri Oct 14 05:00:03 MDT 2022
+// Date        : Thu Oct  5 21:13:49 2023
+// Host        : DESKTOP-J3QO9T8 running 64-bit major release  (build 9200)
+// Command     : write_verilog -mode funcsim Verilog_Netlist_Name.v
+// Design      : Switch_Control
+// Purpose     : This verilog netlist is a functional simulation representation of the design and should not be modified
+//               or synthesized. This netlist cannot be used for SDF annotated simulation.
+// Device      : xc7z020clg484-1
+// --------------------------------------------------------------------------------
+`timescale 1 ps / 1 ps
+
+(* NotValidForBitStream *)
+module Switch_Control
+   (clock,
+    switch);
+  input clock;
+  output switch;
+
+  wire clock;
+  wire clock_IBUF;
+  wire clock_IBUF_BUFG;
+  wire p_0_in;
+  wire switch;
+  wire switch_OBUF;
+
+  BUFG clock_IBUF_BUFG_inst
+       (.I(clock_IBUF),
+        .O(clock_IBUF_BUFG));
+  IBUF #(
+    .CCIO_EN("TRUE")) 
+    clock_IBUF_inst
+       (.I(clock),
+        .O(clock_IBUF));
+  OBUF switch_OBUF_inst
+       (.I(switch_OBUF),
+        .O(switch));
+  LUT1 #(
+    .INIT(2'h1)) 
+    toggle_i_1
+       (.I0(switch_OBUF),
+        .O(p_0_in));
+  FDRE #(
+    .INIT(1'b0)) 
+    toggle_reg
+       (.C(clock_IBUF_BUFG),
+        .CE(1'b1),
+        .D(p_0_in),
+        .Q(switch_OBUF),
+        .R(1'b0));
+endmodule
+`ifndef GLBL
+`define GLBL
+`timescale  1 ps / 1 ps
+
+module glbl ();
+
+    parameter ROC_WIDTH = 100000;
+    parameter TOC_WIDTH = 0;
+    parameter GRES_WIDTH = 10000;
+    parameter GRES_START = 10000;
+
+//--------   STARTUP Globals --------------
+    wire GSR;
+    wire GTS;
+    wire GWE;
+    wire PRLD;
+    wire GRESTORE;
+    tri1 p_up_tmp;
+    tri (weak1, strong0) PLL_LOCKG = p_up_tmp;
+
+    wire PROGB_GLBL;
+    wire CCLKO_GLBL;
+    wire FCSBO_GLBL;
+    wire [3:0] DO_GLBL;
+    wire [3:0] DI_GLBL;
+   
+    reg GSR_int;
+    reg GTS_int;
+    reg PRLD_int;
+    reg GRESTORE_int;
+
+//--------   JTAG Globals --------------
+    wire JTAG_TDO_GLBL;
+    wire JTAG_TCK_GLBL;
+    wire JTAG_TDI_GLBL;
+    wire JTAG_TMS_GLBL;
+    wire JTAG_TRST_GLBL;
+
+    reg JTAG_CAPTURE_GLBL;
+    reg JTAG_RESET_GLBL;
+    reg JTAG_SHIFT_GLBL;
+    reg JTAG_UPDATE_GLBL;
+    reg JTAG_RUNTEST_GLBL;
+
+    reg JTAG_SEL1_GLBL = 0;
+    reg JTAG_SEL2_GLBL = 0 ;
+    reg JTAG_SEL3_GLBL = 0;
+    reg JTAG_SEL4_GLBL = 0;
+
+    reg JTAG_USER_TDO1_GLBL = 1'bz;
+    reg JTAG_USER_TDO2_GLBL = 1'bz;
+    reg JTAG_USER_TDO3_GLBL = 1'bz;
+    reg JTAG_USER_TDO4_GLBL = 1'bz;
+
+    assign (strong1, weak0) GSR = GSR_int;
+    assign (strong1, weak0) GTS = GTS_int;
+    assign (weak1, weak0) PRLD = PRLD_int;
+    assign (strong1, weak0) GRESTORE = GRESTORE_int;
+
+    initial begin
+	GSR_int = 1'b1;
+	PRLD_int = 1'b1;
+	#(ROC_WIDTH)
+	GSR_int = 1'b0;
+	PRLD_int = 1'b0;
+    end
+
+    initial begin
+	GTS_int = 1'b1;
+	#(TOC_WIDTH)
+	GTS_int = 1'b0;
+    end
+
+    initial begin 
+	GRESTORE_int = 1'b0;
+	#(GRES_START);
+	GRESTORE_int = 1'b1;
+	#(GRES_WIDTH);
+	GRESTORE_int = 1'b0;
+    end
+
+endmodule
+`endif
+
+

The generated netlist in VHDL format is also available here. As you can see, these generated HDL netlists are elaborated in different abstraction levels and it’s quite challenging to understand the intent of this code. Even though they’re basically the same as the codes that we wrote for toggling output and do the exact same operation, but this is the eventual code that will be programmed in the FPGA.

Fortunately, there are tools that can be used for reverse engineering netlists (more about it later).

The Memory

One of the fundamental components of chips is Static Random Access Memory (SRAM). You probably saw that Intel processors with more cache are offered at higher prices. The reason for that is that they need to chain the different numbers of flip-flops to form the cache. These chains of flip-flops are SRAMs and can be accessed immediately without any waiting clocks.

Static RAM (SRAM) consists of flip-flops, a bistable circuit composed of four to six transistors. Once a flip-flop stores a bit, it keeps that value until the opposite value is stored in it. SRAM gives fast access to data (immediate), but it is physically relatively large. Even though SRAMs are perfect for designing memory, these components are relatively expensive. That’s why other types of RAMs (like DRAMs) are used in computers.

Now let’s talk about different types of memories used in FPGAs and ASIC design.

Distributed RAMs in FPGAs

In FPGAs, there are Block RAMs (BRAMs) and distributed RAMs. Distributed RAM is made up of LUTs and is used when the size of memory is small. For instance, in your code, you have a memory with a depth of 64 and a word size of 32. This one will be inferred into Distributed RAM, not BRAM.

Let’s define a memory in VHDL:

1
+2
+
type ram_type is array (0 to 3) of STD_LOGIC_VECTOR(7 downto 0);
+signal ram: ram_type := (others => "00000000");
+

The above code defines an array type with four elements, indexed from 0 to 3. Each element of this array is a STD_LOGIC_VECTOR type, which represents an array of bits. In this case, each element is an 8-bit wide STD_LOGIC_VECTOR (ranging from bit 7 to bit 0), effectively creating a 4x8-bit memory structure. The FPGA will infer it as a distributed RAM, not Block RAM (BRAM) because the size is relatively small.

Block RAM (BRAM) in FPGAs

Block RAM (or BRAM) stands for Block Random Access Memory and is used for storing large amounts of data inside of your FPGA. BRAMs are usually external or embedded chips in the FPGA. For example, the following chip is the external design (port map) of a 36-KB BRAM used in Xilinx FPGAs.

36KB BRAM Xilinx

The delay for these BRAM components are 1-clock cycle means that if you want to access data bits in BRAMs, you have to request from the BRAM chip and it will be available at the rising edge of the next clock cycle.

Usually, FPGAs have different formats of codes for inferring Block RAMs. For example, the following Verilog code is used for inferring BRAMs in Xilinx FPGAs where you can initialize it with particular data bits specified in the file.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
module rams_init_file (clk, we, addr, din, dout);
+input clk;
+input we;
+input [5:0] addr;
+input [31:0] din;
+output [31:0] dout;
+
+reg [31:0] ram [0:63];
+reg [31:0] dout;
+
+initial begin
+    $readmemb("rams_init_file.data",ram);
+end
+
+always @(posedge clk)
+    begin
+        if (we)
+            ram[addr] <= din;
+            dout <= ram[addr];
+end endmodule
+

This Verilog module, rams_init_file, represents a simple RAM with a read initialization from a memory file. It responds to clock edges and writes to enable signals to update and read data from its memory array. Once you use the above code in Vivado, it automatically infers Block RAM (BRAM).

You can even initialize it with custom values.

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
+
library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+entity rams_sp_rom is
+port(
+    clk : in std_logic;
+    we : in std_logic;
+    addr : in std_logic_vector(5 downto 0);
+    di : in std_logic_vector(19 downto 0);
+    do : out std_logic_vector(19 downto 0)
+);
+end rams_sp_rom;
+
+architecture syn of rams_sp_rom is
+    type ram_type is array (63 downto 0) of std_logic_vector(19 downto 0);
+    signal RAM : ram_type := (X"0200A", X"00300", X"08101", X"04000", X"08601", X"0233A",
+                            X"00300", X"08602", X"02310", X"0203B", X"08300", X"04002",
+                            X"08201", X"00500", X"04001", X"02500", X"00340", X"00241",
+                            X"04002", X"08300", X"08201", X"00500", X"08101", X"00602",
+                            X"04003", X"0241E", X"00301", X"00102", X"02122", X"02021",
+                            X"00301", X"00102", X"02222", X"04001", X"00342", X"0232B",
+                            X"00900", X"00302", X"00102", X"04002", X"00900", X"08201",
+                            X"02023", X"00303", X"02433", X"00301", X"04004", X"00301",
+                            X"00102", X"02137", X"02036", X"00301", X"00102", X"02237",
+                            X"04004", X"00304", X"04040", X"02500", X"02500", X"02500",
+                            X"0030D", X"02341", X"08201", X"0400D");
+    begin
+        process(clk)
+        begin
+            if rising_edge(clk) then
+                if we = '1' then
+                    RAM(to_integer(unsigned(addr))) <= di;
+                end if;
+                do <= RAM(to_integer(unsigned(addr)));
+            end if;
+        end process;
+
+end syn;
+

This Block RAM (BRAM) is a Verilog module called rams_sp_rom representing a simple synchronous read-only memory (ROM) with a 64-entry memory array. It has a clock input clk, write enable input we, address input addr, data input di, and data output do. On each rising clock edge, it either writes data to the ROM if we is high or reads data from the ROM based on the provided address, updating the do output accordingly. The memory content is initialized with preset values in the RAM signal. Same as above, Vivado infers a BRAM for the above code.

As you can see, you can write HDL codes to READ/WRITE into the Block RAM memory. You can even create Read-Only Memory (ROM) by removing the modification (write) logic. Keep in mind that the logic of the Block RAM itself is also written with HDL codes, so it’s like using another HDL module with this difference that the Block RAMs are previously fabricated and physically available into the chips.

Static RAM (SRAM) in ASIC

There is no Block RAM (BRAM) in ASIC designs. If you want to implement a block RAM (BRAM) into an ASIC chip, you have to implement it or use custom tools for creating and simulating RAMs. One of these tools is OpenRAM. OpenRAM is an open-source framework to create the layout, netlists, timing and power models, placement and routing models, and other views necessary to use SRAMs in ASIC design.

You can configure the OpenRAM and it will create different outputs from Verilog files to GDS files that can be used for fabricating SRAMs. Finally, it gives you the necessary files that are suitable for your custom technology (45 nm, 40 nm, etc.).

Elaborating ASIC Designs

Once the implementation of hardware (usually in HDL languages) is done, it’s time to use special tools to implement them into actual transistors. This task can be done with (usually commercial, expensive, and complicated) tools like Synopsys Design Compiler. These tools try to optimize designs for timing, area, power, and test and finally generate designs that can be fabricated and implemented into chips.

Other than the commercial tools, there are open-source equivalent tools like Yosys that are able to synthesize and design Verilog codes.

Building Hardware Layout (GDSII)

Once you’ve done designing your hardware, it’s time to make it ready for fabrication. This time, the GDSII format is used to create a layout available to be used and fabricated in the industry. GDSII is an industry-standard database file format that is used to exchange data on IC layout artwork. It is a binary file format representing planar geometric shapes, text labels, and other information about the layout in hierarchical form.

Once you created your final Verilog files, you can use different tools to create a standard GDSII format. As an example, the open-source OpenROAD-Project’s OpenLane can be used that perform all ASIC implementation steps from RTL all the way down to GDSII.

As an example, I used the Blinky Verilog code that we generated by Chisel as an input for OpenLange and this is the picture of a chip that I made by using this project:

OpenLane generated image

If you want to zoom into the picture, the SVG version of this file is available here.

This was the last step and we explored our journey from HDL codes to creating hardware using GDSII. Of course, there are plenty of other steps and industry considerations at this point that should be done before making the actual chip, but I think you’ve got a very high-level idea of how chips are made.

Before wrapping up, let’s explore one more step, reverse engineering of netlists!

Reverse Engineering Netlists

If you remember from the previous sections where we explored the netlists of Vivado, you probably noticed that the netlists are mostly hard to understand. Fortunately, there are tools to help us with analyzing netlists and one of them is called HAL (or The Hardware Analyzer). As they mentioned in the git repo, their vision is that HAL becomes the hardware reverse-engineering equivalent of tools like IDA or Ghidra.

From the main descriptions: virtually all available research on netlist analysis operates on a graph-based representation of the netlist under inspection. At its core, HAL provides exactly that: A framework to parse netlists of arbitrary sources, e.g., FPGAs or ASICs, into a graph-based netlist representation and to provide the necessary built-in tools for traversal and analysis of the included gates and nets.

HAL

We can use HAL to analyze the netlist codes generated by Vivado from the previous step. First, we need to build HAL and then import the netlist file. As you can see, it visualizes the netlist view of the chips along with their input and output ports and shows the arithmetic logic behind each element (flip-flops, LUTs, Boolean Function) in the netlist. We could also use Python codes to further elaborate the netlist.

HAL

One other interesting feature of HAL is its Netlist Simulator and Waveform Viewer. This tool is also able to create automated waves for the target netlist and enables the reverse engineer to simulate selected parts of the netlist in a cycle-accurate fashion.

HAL waveviewer

Now, what if we don’t have access to the netlist files? Or if the netlist is too complex to be interpreted? Can we physically perform the reverse engineering tasks? The answer to this question is yes!

Sometimes even getting your hands on a netlist file is a hard task. In such cases, prior to reverse engineering the circuit’s netlist, an adversary will be needed to reproduce the internal netlist of the target IC. This has been a great challenge in terms of IP/IC cloning and piracy. Once the attacker gets his hand on an optical microscope, he can prepare depackaging the IC using corrosive chemicals, then delayering individual layers (metal, poly, or diffusion) using corrosive chemicals. Finally Imaging the top-view of each layer using an optical microscope or single electron microscope (SEM). The image may contain metal routing, contacts, vias, and pins in that layer.

optical probing1 optical probing2

Furthermore, annotation aligns and stitches the images of different layers could be carried out to extract gate-level netlist from the annotated images.

To mitigate such reverse engineering there have been a number of methods including gate camouflages, dummy contacts, netlist obfuscation, logic locking and so on. Some methods are shown below:

dummy contact

Nevertheless, similar invasive methods like this and this could be used to get information from the hardware imaging and eventually complete reverse engineering.

RE

Conclusion

This was a very basic introduction to the digital and hardware design world and most of the introduced tools are either open-source or publicly available, however, you should keep in mind that most of the big computer maker companies probably have their own approaches to generating hardware along with their proprietary tools, so it might not reflect how the actual hardware is made in a big tech company but sure it will give you an idea of the capabilities of the hardware design and how these hardware devices can be made.

In wrapping up our exploration of hardware design, we’ve taken a journey into the world of silicon. From the incredible advancements driven by Moore’s Law to the high-level and low-level description of hardware chips using different languages like VHDL, Verilog, and Chisel, testing hardware descriptions using tools like ModelSim, Verilator, and making design layouts ready to be fabricated using OpenLane or programming FPGA’s using Vivado. Whether you’re a seasoned expert or a newcomer to the field, hardware design continues to be an awesome adventure, where innovation and curiosity work hand in hand to shape the future of computing.

I hope you enjoyed this blog post and I will try to make more content about chip design in the future. Make sure to use the comment section below to discuss this article and see you at the next blog posts!

References

[1] Jensen Huang Tells the Media That Moore’s Law is Dead (The featured image) - (https://www.techpowerup.com/299159/jensen-huang-tells-the-media-that-moores-law-is-dead)

[2] 3 nm process - (https://en.wikipedia.org/wiki/3_nm_process)

[3] Moore’s law - (https://en.wikipedia.org/wiki/Moore%27s_law)

[4] Amdahl’s law - (https://en.wikipedia.org/wiki/Amdahl%27s_law)

[5] Comparison among Different Adders - (https://www.iosrjournals.org/iosr-jvlsi/papers/vol5-issue6/Version-1/A05610106.pdf)

[6] Difference between Verilog and SystemVerilog - (https://www.geeksforgeeks.org/difference-between-verilog-and-systemverilog/)

[7] Verilog Example Codes - (https://verificationguide.com/verilog-examples/)

[8] “Light, Thin, Short and Small”, The Development of Semiconductor Packages - (https://news.skhynix.com/light-thin-short-and-small-the-development-of-semiconductor-packages/)

[9] IC packages - (https://colegiogradual.com.br/pt/plastic-leaded-chip-carrier-plcc-pllc-dummy-component-rr-z1Qd8ysk)

[10] OpenROAD-Project’s OpenLane - (https://github.com/The-OpenROAD-Project/OpenLane)

[11] HAL – The Hardware Analyzer - (https://github.com/emsec/hal)

[12] An open-source static random access memory (SRAM) compiler - (https://github.com/VLSIDA/OpenRAM/)

[13] flip-flop - (https://www.britannica.com/technology/flip-flop)

[14] What is a Block RAM (BRAM) in an FPGA? Tutorial for beginners - (https://nandland.com/lesson-15-what-is-a-block-ram-bram/)

[15] Block RAM - (https://docs.xilinx.com/r/en-US/am007-versal-memory/Data-Flow)

[16] Initializing Block RAM From an External Data File (VHDL) - (https://docs.xilinx.com/r/en-US/ug901-vivado-synthesis/Initializing-Block-RAM-From-an-External-Data-File-Verilog)

[17] OpenRAM results - (https://github.com/VLSIDA/OpenRAM/blob/stable/docs/source/results.md)

[18] Chisel: A Modern Hardware Design Language - (https://github.com/chipsalliance/chisel)

[19] 6.11. Incorporating Verilog Blocks - (https://chipyard.readthedocs.io/en/stable/Customization/Incorporating-Verilog-Blocks.html)

[20] Design Compiler - (https://www.synopsys.com/implementation-and-signoff/rtl-synthesis-test/dc-ultra.html)

[21] Generating a Netlist - (https://docs.xilinx.com/r/en-US/ug900-vivado-logic-simulation/Working-with-Simulation-Sets)

[22] ModelSim-Intel FPGAs Standard Edition Software Version 18.1 - (https://www.intel.com/content/www/us/en/software-kit/750368/modelsim-intel-fpgas-standard-edition-software-version-18-1.html)

[23] Netlist Simulator and Waveform Viewer - (https://github.com/emsec/hal/wiki/Netlist-Simulator-and-Waveform-Viewer)

[24] Writing Test Benches - (https://alchitry.com/writing-test-benches-verilog)

[25] chiseltest - (https://github.com/ucb-bar/chiseltest/blob/main/README.md)

[26] Chisel3 Testing Cheat Sheet - (https://inst.eecs.berkeley.edu/~cs250/sp17/handouts/chisel-testercheatsheet.pdf)

[27] Chisel Testing Intensive (Beginner Track) - Chisel Community Conference 2018 - (https://www.youtube.com/watch?v=yYosPQ9RI4I&ab_channel=Chisel)

[28] Verilator, the fastest Verilog/SystemVerilog simulator - (https://www.veripool.org/verilator/)

[29] High-level synthesis - (https://en.wikipedia.org/wiki/High-level_synthesis)

[30] Vitis HLS - (https://www.xilinx.com/products/design-tools/vitis/vitis-hls.html)

[31] Krachenfels, Thilo, et al. “Automatic Extraction of Secrets from the Transistor Jungle using {Laser-Assisted}{Side-Channel} Attacks.” 30th USENIX security symposium (USENIX security 21). 2021.

[32] Puschner, Endres, et al. “Red Team vs. Blue Team: A Real-World Hardware Trojan Detection Case Study Across Four Modern CMOS Technology Generations.” 2023 IEEE Symposium on Security and Privacy (SP). IEEE, 2023.

[33] Rajendran, J., Sam, M., Sinanoglu, O. and Karri, R., 2013, November. Security analysis of integrated circuit camouflaging. In Proceedings of the 2013 ACM SIGSAC conference on Computer & communications security (pp. 709-720)

[34] Rajendran, J., Pino, Y., Sinanoglu, O., & Karri, R. (2012, June). Security analysis of logic obfuscation. In Proceedings of the 49th annual design automation conference (pp. 83-89)

[35] Han, Zhaokun, Muhammad Yasin, and Jeyavijayan JV Rajendran. “Does logic locking work with {EDA} tools?.” 30th USENIX Security Symposium (USENIX Security 21). 2021.

This post is licensed under CC BY 4.0 by the author.

HyperDbg’s One Thousand and One Nights

-

Comments powered by Disqus.

diff --git a/topics/hello-world/index.html b/topics/hello-world/index.html new file mode 100644 index 00000000..4b02ce16 --- /dev/null +++ b/topics/hello-world/index.html @@ -0,0 +1 @@ + Hello World ! | Rayanfam Blog
Home Hello World !
Post
Cancel

Hello World !

This is the first post of our blog !

After 5+ years of experience working with various technologies and developing skills in different computer fields including security and network, we want to share our little knowledge of this huge science to all people who love computers and computer security.

As you know this year is a leap year and today is the last day of the year and tomorrow is the new year. (In Persian Calendar ;) ).so that’s like the anniversary of this blog will be every four years but I think it’s so cute <3

I’m MohammadSina Karvandi, a 19 years old boy who loves to learn and teach, I’m not only interested in Computer Security and Low Level Programming but also love high level programming as well.The other writer here would be one of my best friends, Shahriar Eftekhari who is into networks and network security.

We will try to create innovative and new content about computer security, software and network and all the other things we find interesting!

I hope that everyone who dedicates his/her time reading our blog would be glad in the end, and I should say sorry in advance if we make any mistakes writing English because It’s not our first language but believe me you can bear it :P

So we’ll be glad for every comment that tells us our mistakes in writing or technical information and we’ll give you our word to update this blog every time we can.

Enjoy :)

This post is licensed under CC BY 4.0 by the author.

-

Kernel Mode Debugging by Windbg

Comments powered by Disqus.

diff --git a/topics/how-to-get-every-details-about-ssdt-gdt-idt-in-a-blink-of-an-eye/index.html b/topics/how-to-get-every-details-about-ssdt-gdt-idt-in-a-blink-of-an-eye/index.html new file mode 100644 index 00000000..58689680 --- /dev/null +++ b/topics/how-to-get-every-details-about-ssdt-gdt-idt-in-a-blink-of-an-eye/index.html @@ -0,0 +1,15 @@ + How to get every detail about SSDT , GDT , IDT in a blink of an eye | Rayanfam Blog
Home How to get every detail about SSDT , GDT , IDT in a blink of an eye
Post
Cancel

How to get every detail about SSDT , GDT , IDT in a blink of an eye

In a few days ago I was looking for something to show me the SSDT and GDT (Which is really important in malware analyzing because most of rootkits are interested in hooking and changing this stuffs.)

• SSDT (System Service Descriptor Table) • GDT (Global Descriptor Table) • IDT (Interrupt Descriptor Table)

They’re really important table in OSes for example SSDT is something like IAT (Import Address Table) in user-mode applications which holds pointer to exported functions of all .dll assemblies and in this case SSDT holds pointer to native windows APIs.

You can imagine how an attacker can just change or hook them and start and filter your arguments every time you go through this functions. then I found something like :

1
+2
+3
+4
+5
+6
+
lkd> u dwo(nt!KiServiceTable)+nt!KiServiceTable L1
+nt!NtMapUserPhysicalPagesScatter:
+fffff800013728b0 488bc4 mov rax,rsp
+lkd> u dwo(nt!KiServiceTable+4)+nt!KiServiceTable L1
+nt!NtWaitForSingleObject:
+fffff800012b83a0 4c89442418 mov [rsp+0x18],r8
+

Which wasn’t what I really wants to, but can somehow help cause as I read in one of the articles about Patchguard bypassing,

It said :

” On Windows x64 kernels, nt!KeServiceDescriptorTable conveys the address of the actual dispatch table and the number of entries in the dispatch table for the native system call interface. In this case, the actual dispatch table is stored as an array of relative offsets in nt!KiServiceTable. “

If you don’t know about SSDT and GDT and IDT first google about it then I want to show an amazing tools which called SwishDbgExt and it’s open source and free !

available at : https://github.com/comaeio/SwishDbgExt

it helps me a lot ! After compiling it from source you need to load dll like :

1
+
!load C:\\users\\sina\\desktop\\SwishDbgExt.dll
+

and then just use !ms_ssdt , and !ms_gdt and !ms_idt in Windbg to get a complete list of information about following tables.

Note : Windows kernel also uses Patchguard to protect its kernel from such changes but its just security over obscurity because Kernel Drivers run in a privilege same as kernel and there are tons of article about how to bypass Patchguard and there is nothing like this protection in x86 systems because they don’t have such thing like patchguard (But I don’t know precisely if windows has any special mechanism to protect these tables in x86 machines.).

So that’s it guys. There are lots of good things you can find in this Windbg extension that you can find in readme or in github page and it really worths to test.

This post is licensed under CC BY 4.0 by the author.

Useful Configs for Squid3 Cache

Change User-Mode application's virtual address through Kernel Debugging

Comments powered by Disqus.

diff --git a/topics/hyperdbg-one-thousand-and-one-nights/index.html b/topics/hyperdbg-one-thousand-and-one-nights/index.html new file mode 100644 index 00000000..11c71c79 --- /dev/null +++ b/topics/hyperdbg-one-thousand-and-one-nights/index.html @@ -0,0 +1 @@ + HyperDbg’s One Thousand and One Nights | Rayanfam Blog
Home HyperDbg’s One Thousand and One Nights
Post
Cancel

HyperDbg’s One Thousand and One Nights

This post is a different one, in that, it is more of an overview, rather than a technical post. Here, we provide a high-level summary of HyperDbg Debugger, its principles, and perspective.

Introduction

HyperDbg is an open-source, hypervisor-assisted debugger that can be used to debug both user-mode and kernel-mode applications. The closest similar product available to HyperDbg is WinDbg. HyperDbg provides unique abilities, enabled by exploitation of the latest features of modern processors, to assist you in your reversing journey.

The design principles employed in HyperDbg make for an OS-independent debugger with a unique architecture, offering exclusive, brand-new features.

A Bit of History

Almost three years ago (precisely, on 18 December 2019), we finished implementing EPT hooks and VPIDs, thus setting the cornerstones for designing a new debugger. Ever since, many of our friends have joined the project to develop an advanced, fully-practical, and usable debugger.

During our Windows analysis journey, we always felt the lack of two elements that we thought would most likely make our tasks much faster and more efficient, and that was our main motive, to tackle the following setbacks:

  1. None of the current classic debuggers, (such as WinDbg) have the ability to trace read/write/executes to a large structure. Sure they have to be able to use hadware debug registers, but that’s limited. They can’t specify more than four addresses, and the size is also limited to 1, 2, and 4 Bytes.
  2. There was no support for tracing instructions from user mode to kernel mode and from kernel mode to user mode. A feature like this would allow us to trace parameters to the system calls and find the exact routines (basic blocks) executed due to our produced parameters in static analysis tools like IDA Pro, Ghidra, and Radare2.

The solution to the first issue was implemented in HyperDbg and exported as a command named “!monitor”, and the secondly discussed issue was addressed with the Instrumentation Step-in or the “i”.

After that, dozens of features were added to the debugger to get HyperDbg to where it is today.

Motivation

Let’s talk about the motivations behind HyperDbg.

Classic Debuggers are Still Stuck At at their 90’s features.

Debuggers are one of the essential tools used in computer sciences for a variety of purposes.

Although there have been some upgrades and improvements over the course of years to the available debugging toolset, such as the Time Travel Debugging (TTD) feature that was introduced by Microsoft into their main debugger, WinDbg, people are essentially still debugging the same way they used to do back in the ’90s, with the same simple elements such as step-in and step-over. This puts a fundamental setback in the way of software programmers, reversers, and security researchers.

We believe debugging, analyzing, and software profiling are inseparable elements that should all be coherently integrated into debuggers to make an enhanced experience of software development and bug finding.

The intention was not to reinvent the wheel.

Our motive was not to reintroduce yet another debugger with the same set of already available features, but it was fueled by the vision of how better a debugging experience can get, and the gap between that vision and the status quo. More specifically, we recognized the powerhouse, that is, the modern hardware features of newer generation processors, and their immense capacity for utilization towards providing an enriched software analysis experience, and the lack of meaningful and practical support for such features in commodity debuggers. And it was this state of mind that gave rise to the emergence of HyperDbg. We see where HyperDbg is today, only as a starting point for future milestones and improvements to provide even easier and more convenient debugging experiences.

Kernel Debugger

The central part of the HyperDbg debugger is its kernel-mode debugger called kHyperDbg.

It has been almost two decades since kernel debuggers (such as WinDbg and GDB) have had any significant changes. We wanted to change that and advance the debugging experience to new horizons. Furthermore, HyperDbg delivers these improvements on rudimentary levels, compared to commonly used debuggers, in that, it enormously expands the range of privileges available to the user by shifting the debugging process from kernel-level (ring 0) to hypervisor-level (ring -1), a much-needed enhancement in our opinion, especially when it comes to kernel-debugging.

As kernel level debuggers are prohibited the access to manipulate the operating system’s structures to make facilities for the debugging, HyperDbg uses an entirely separate layer to monitor and change these structures without interfering with the operating system, the hypervisor level, that resides below the kernel of the operating system in the hardware privilege rings. This makes HyperDbg blazingly fast and a highly powerful tool in terms of the flexibility, privileges, and the transparency it can provide for debugging and analysis. Owing to this fundamental transition, HyperDbg is able to deliver state-of-the-art features that make it particularly more convenient to analyze complex modern binaries that run on kernel mode of operating systems and are crucial to the security and reliability of the system. Furthermore, the highly efficient and low-level implementation, coupled with the potent script engine, allows for some tremendously powerful abilities, such as changing the flow of the applications and even the operating system using simple scripts.

A few of its unique features

In this section, we will try and summarize some of the unique features of HyperDbg.

Tremendously Faster

As described earlier, HyperDbg is incredibly fast, thanks to its low-level and efficient implementation. This brings forward new opportunities for many innovative debugging scenarios. For example, let’s imagine you want to analyze every system-calls, or get a log from a function called at a very high rate. HyperDbg allows you to do all of them with ease and high performance.

Better Transparency

One of the future goals of HyperDbg is to keep enhancing the stealth and transparency of its transparent mode. Of course, it is not possible to achieve 100% transparency, but we keep trying to make it more challenging for the anti-debugging methods to detect HyperDbg.

Exporting Processor Events In Debugger

HyperDbg tends to export all system events of interest as debugger events. So many events happen in the CPU at all times. Fortunately, the majority of them are accessible via hypervisors. In HyperDbg, we export events into HyperDbg event format. Each HyperDbg event then can be used as a trigger for executing a desired action, such as breaking the debugger, executing custom assembly codes, or running a custom script engine. This standard pipeline will apply to all the current events and possible future events.

HyperDbg vs. WinDbg

As one of the closest counterparts of HyperDbg, in this section, we draw a comparison between WinDbg and HyperDbg in a detailed manner.

How different is HyperDbg from WinDbg? HyperDbg has an entirely different and standalone architecture. WinDbg operates on ring 0 (kernel) while HyperDbg is running on ring -1 (hypervisor); thus, HyperDbg is capable of providing unique features that are not available on WinDbg (OS-Level).

Additionally, HyperDbg is not just a simple debugger. It comes with modern reverse engineering methods, powered by vt-x and other similar capabilities of modern processors to facilitate reverse engineering, analyzing, and fuzzing.

WinDbg is built for development HyperDbg is built for reversing. We are not the same!

Microsoft made WinDbg to build Windows and perform driver development tasks. Contrarily, HyperDbg is mainly geared towards use in reverse engineering, where one has no idea about their target debuggee. Of course, WinDbg is better at debugging drivers with the source code. However, if you wanted to understand a mechanism in which you have no access to the source code or if symbols are only partially available, then HyperDbg would give you way more features to explore your debuggee.

HyperDbg is not a classic debugger. One of the main differentiating factors between HyperDbg and commodity debuggers stems from HyperDbg’s deliberation on the use of sophisticated methods and techniques by modern binaries to obfuscate their internal structures.

For instance, most malware uses various anti-debugging techniques to avoid showing its malicious behavior when a debugger is around. On the other hand, classic debuggers are not suitable for analyzing the internal mechanisms that are buried deep into the complicated modules of the operating systems.

HyperDbg is a hypervisor-based debugger. HyperDbg uses Intel VT-x (Ring -1) technology, while WinDbg is a kernel-based (Ring 0) debugger. Thus, HyperDbg is more privileged in terms of hardware terminology.

Being a hypervisor-based debugger brings new magical features to life. Dozens of wonderful features already exist in HyperDbg that no other debuggers have. These features drastically enhance your reverse engineering journey, and we continue to add more and more features to further improve your debugging experience.

HyperDbg is more transparent by nature. Generally, basing the debugger on the hypervisor layer makes HyperDbg more transparent than WinDbg. HyperDbg doesn’t use any debugging-related API, so even the operating system doesn’t have any idea that it’s being debugged! And that’s not all! HyperDbg can also hide from microarchitectural timing attacks that reveal the presence of the hypervisors. Transparency is a priority and is under active development.

HyperDbg is open-source, WinDbg is not. HyperDbg is a community-driven debugger, and everyone can contribute to the project. In contrast, WinDbg is not open-source, although the source codes of some older versions have been leaked several times by now.

WinDbg works in almost any architecture; HyperDbg is not. WinDbg works on multiple architectures, but so far, HyperDbg only works on x64-based systems. You can use WinDbg to debug many architectures like ARM, ARM64 (AARCH64), and AMD64 (x86_64), while HyperDbg currently only works on Intel x64-bit processors. However, it is still possible to debug x86 applications running in a system with an x64-bit processor.

HyperDbg is faster, tremendously faster. HyperDbg is shipped with a vmx-root mode compatible script engine. Every check is performed on the kernel side (vmx-root mode), and in contrast to WinDbg, nothing is passed to the debugger. This makes for a substantially faster debugging process. Based on our evaluations as part of an academic publication on HyperDbg, WinDbg was able to check 6,941 conditions, while HyperDbg could check 23,214,792 in the same time period, making HyperDbg about ~3300 folds faster than WinDbg in this benchmark.

For more information about the differences, please take a look at the following tweet.

HyperDbg’s logo is the Schrödinger’s cat, which is both dead and alive. It serves as a reminder that analyzing and reversing is not always a deterministic route for getting the correct answer. One will try, and one might succeed or fail, and that’s the nature of the digital world, which boils down to one of the two fundamental states: ones and zeros.

Contribution

HyperDbg is a large-scale project that requires a lot of time and effort from the community. Given the current number of developers and their limited time and resources, we cannot develop every part simultaneously. Therefore, new developers are warmly welcomed to join and contribute to the project. Please open discussions to discuss the HyperDbg and possible future assistance.

The future works

In the future, we want to add UEFI support to start HyperDbg before Windows. Another significant contribution would be adding Intel Processor Trace (PT) in an event and action style to the debugger and finally joining and integrating many cool projects to the HyperDbg to bring a unique debugging experience, like no one has seen before.

More importantly, HyperDbg is (for the most part) operating system-independent. We want to port HyperDbg to other operating systems like Linux and OS X.

Conclusion

Reaching the goal of a transparent, fast, and innovative debugger is not possible without the help of the community of the developers. We believe those who use HyperDbg are professional computer programmers/reversers, and almost all of them can help in this project. So, what are you waiting for? Go ahead and add your own contributions to the project!

This post is licensed under CC BY 4.0 by the author.

Hypervisor From Scratch – Part 8: How To Do Magic With Hypervisor!

Building Silicon Dreams: An Adventure in Hardware Design

Comments powered by Disqus.

diff --git a/topics/hypervisor-from-scratch-part-1/index.html b/topics/hypervisor-from-scratch-part-1/index.html new file mode 100644 index 00000000..75066d88 --- /dev/null +++ b/topics/hypervisor-from-scratch-part-1/index.html @@ -0,0 +1,125 @@ + Hypervisor From Scratch - Part 1: Basic Concepts & Configure Testing Environment | Rayanfam Blog
Home Hypervisor From Scratch - Part 1: Basic Concepts & Configure Testing Environment
Post
Cancel

Hypervisor From Scratch - Part 1: Basic Concepts & Configure Testing Environment

If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous practical examples on how to utilize hypervisors for reverse engineering.

Notice: The Hypervisor From Scratch tutorial is completely revised in August 2022. Codes from all parts are updated, unnecessary details are removed, and new explanations and materials are added to the tutorial.

Introduction

Welcome to the first part of a multi-part series of tutorials called “Hypervisor From Scratch”. As the name implies, this course contains technical details to create a basic Virtual Machine based on hardware virtualization. If you follow this tutorial, you’ll be able to create your own virtual environment and understand how VMWare, VirtualBox, KVM, and other virtualization software use processors’ facilities to create a virtual environment. Moreover, you can learn how the “VMM” module of the HyperDbg Debugger works internally.

The full source code of this tutorial is available on GitHub :

[https://github.com/SinaKarvandi/Hypervisor-From-Scratch]

Table of Contents

  • Introduction
  • Table of Contents
  • Overview
  • Hypervisors and Platforms
  • Installing Tools
  • Configuring A Testing Environment
  • Creating A Driver
    • Disabling The Driver Signature Enforcement (DSE)
  • Nested-Virtualization
    • Hyper-V’s Nested-Virtualization
    • VMware Workstation’s Nested-Virtualization
  • Concepts
  • VMX Instructions
  • Related Work
  • Conclusion
  • References

Overview

Both Intel and AMD support virtualization in their modern CPUs. Intel introduced (VT-x technology) that was previously codenamed “Vanderpool” on November 13, 2005, in Pentium 4 series. The CPU flag for VT-x capability is “VMX” which stands for Virtual Machine eXtension.

AMD, on the other hand, developed its first generation of virtualization extensions under the codename “Pacifica” and initially published them as AMD Secure Virtual Machine (SVM), but later marketed them under the trademark AMD Virtualization, abbreviated AMD-V.

There are two types of hypervisors. The “type 1” hypervisor is called a “bare-metal hypervisor” or “native” because it runs directly on a bare-metal physical server. A type 1 hypervisor has direct access to the hardware. With a type 1 hypervisor, there is no operating system to load as the hypervisor itself has the necessary functions to manage the system boot and startup.

Contrary to a type 1 hypervisor, a type 2 hypervisor loads inside an operating system, just like any other application. Because the type 2 hypervisor has to go through the operating system and is managed by the OS, the type 2 hypervisor (and its virtual machines) will run less efficiently (slower) than type 1 hypervisors.

Even though most of the concepts about the virtualization in Intel and AMD processors are the same, but there are some differences between Intel VT-x and AMD-V. For example, Intel and AMD use different instruction sets for virtualization.

The rest of these tutorials mainly focus on VT-x because Intel CPUs are more popular and widely used.

Hypervisors and Platforms

These concepts are platform independent, which means you can easily run the same code routine in both Linux or Windows and expect the same behavior from the CPU, but I prefer to use Windows as its more easily debuggable (at least for me.), but I try to give some examples for Linux systems whenever needed.

Linux kernel manages faults like #GP and other exceptions and tries to avoid the kernel panic and keep the system up; thus, it’s better to test something like a hypervisor or any CPU-related programs. In contrast, Windows never tries to manage any unexpected exception and shows a Blue Screen Of Death whenever an unexpected exception occurs; therefore, you might get lots of BSODs while testing your hypervisor.

Considering the fact that I might (and will) make mistakes like misinformation, wrong implementation, or forget about mentioning some essential explanations in these series, I should say sorry in advance, and I’ll be glad and open to every comment that tells me the mistakes in the technical details. You can use the comments below to notify me about these possible errors.

That’s enough. Let’s get started!

Installing Tools

First, we need to install Visual Studio with WDK (Windows Driver Kit).

You can download the community (free) version of the Visual Studio:

[https://visualstudio.microsoft.com/vs/community].

After installing Visual Studio, you can get the WDK from the below link:

[https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk].

We’ll use WinDbg to debug our hypervisor. We can use WinDbg and WinDbg Preview to debug our hypervisor.

WinDbg is available in Windows SDK:

[https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/]

Afterward, we should connect to debug the Windows kernel using WinDbg. You can read the following links to connect WinDbg to debug the kernel using KDNET.

The next step is downloading OSR Driver Loader. We use this tool to load our driver. You can download it using the below link:

[https://www.osronline.com/article.cfm?article=157].

Finally, we need to download SysInternals DebugView for printing the DbgPrint() results.

Chameleon

Configuring A Testing Environment

Most of the codes in this tutorial have to run at the kernel-level, and we must set up a Linux Kernel Module or a Windows Driver for this purpose.

As configuring a Virtual Machine Monitor (VMM) involves lots of assembly codes, we should be able to embed inline assemblies within our kernel project.

WDK (and also user-mode applications) no longer support inline assembly in an x64 environment, so we should be able to create a simple x64 project with the support for inline assembly codes.

I explained it step by step in one of my posts, so I highly recommend reading this topic to create a project with inline assembly support before continuing the rest of this part.

Now it’s time to create a driver!

Creating A Driver

There is a good article here if you want to start with Windows Driver Kit (WDK).

For the first example, we’ll create an elementary WDK driver. In WDK, we need two essential functions, first is a Driver Entry function that works as a starting point where the driver starts its execution whenever it is loaded. The second is for Driver Unload, responsible for removing the objects used in the driver.

In the driver entry, our driver needs to register a device so we can communicate with our virtual environment from the user-mode. On the other hand, I defined DrvUnload, which uses the PnP Windows driver feature, and we can easily unload our driver and remove the device, then reload and create a new one.

The following code is responsible for creating a new device :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
    RtlInitUnicodeString(&DriverName, L"\\Device\\MyHypervisor");
+    RtlInitUnicodeString(&DosDeviceName, L"\\DosDevices\\MyHypervisor");
+
+    NtStatus = IoCreateDevice(DriverObject, 0, &DriverName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject);
+
+    if (NtStatus == STATUS_SUCCESS)
+    {
+        DriverObject->DriverUnload = DrvUnload;
+        DeviceObject->Flags |= IO_TYPE_DEVICE;
+        DeviceObject->Flags &= (~DO_DEVICE_INITIALIZING);
+        IoCreateSymbolicLink(&DosDeviceName, &DriverName);
+    }
+

All in all, you can compile the following file to create our first WDK driver in Visual Studio (with WDK installed).

It contains a Driver Entry function which creates a device and registers the unloading routines. Whenever the driver is loaded, the DriverEntry function is called, and when we unload it, DrvUnload will be called. This function will remove the device that we registered before.

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
+
#include <ntddk.h>
+#include <wdf.h>
+#include <wdm.h>
+
+NTSTATUS
+DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath);
+
+VOID
+DrvUnload(PDRIVER_OBJECT DriverObject);
+
+#pragma alloc_text(INIT, DriverEntry)
+#pragma alloc_text(PAGE, DrvUnload)
+
+NTSTATUS
+DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
+{
+    NTSTATUS       NtStatus     = STATUS_SUCCESS;
+    PDEVICE_OBJECT DeviceObject = NULL;
+    UNICODE_STRING DriverName, DosDeviceName;
+
+    DbgPrint("DriverEntry Called.");
+
+    RtlInitUnicodeString(&DriverName, L"\\Device\\MyHypervisor");
+    RtlInitUnicodeString(&DosDeviceName, L"\\DosDevices\\MyHypervisor");
+
+    NtStatus = IoCreateDevice(DriverObject, 0, &DriverName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject);
+
+    if (NtStatus == STATUS_SUCCESS)
+    {
+        DriverObject->DriverUnload = DrvUnload;
+        DeviceObject->Flags |= IO_TYPE_DEVICE;
+        DeviceObject->Flags &= (~DO_DEVICE_INITIALIZING);
+        IoCreateSymbolicLink(&DosDeviceName, &DriverName);
+    }
+    return NtStatus;
+}
+
+VOID
+DrvUnload(PDRIVER_OBJECT DriverObject)
+{
+    UNICODE_STRING DosDeviceName;
+
+    DbgPrint("DrvUnload Called !");
+
+    RtlInitUnicodeString(&DosDeviceName, L"\\DosDevices\\MyHypervisor");
+
+    IoDeleteSymbolicLink(&DosDeviceName);
+    IoDeleteDevice(DriverObject->DeviceObject);
+}
+

Starting from the next version, the source code of each driver is available at GitHub, and we’ll talk about different features in WDK drivers. Don’t worry if you still don’t have any idea about a Windows Driver. We’ll work on it later in the next part. Just make sure to set up the environment for now.

Disabling The Driver Signature Enforcement (DSE)

If you use Windows, you should disable Driver Signature Enforcement to load our driver. That’s because Microsoft prevents any not verified code from running in Windows Kernel (Ring 0).

To do this, press and hold the shift key and restart your computer. You should see a new window.

  1. Click Advanced options.
  2. On the new Window, Click Startup Settings.
  3. Click on Restart.
  4. On the Startup Settings screen, press 7 or F7 to disable driver signature enforcement.

Disable DSE

The latest thing I remember is enabling Windows Debugging messages through the registry. This way we can get DbgPrint() results through SysInternals DebugView.

Just perform the following steps:

In the Regedit, add a key:

1
+
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Debug Print Filter
+

Under that, add a DWORD value named IHVDRIVER with a value of 0xFFFF.

Reboot the machine, and it’s good to go.

Nested-virtualization

What if you don’t have access to a separate physical machine?

You can use VMware’s (or any other virtualization product) nested-virtualization.

VMware Workstation’s Nested-Virtualization

In order to set up a nested virtualization environment, make sure to enable the following features in your VM.

VMWare Nested Virtualization

All the drivers are tested on both physical machines and VMware’s nested virtualization.

Hyper-V’s Nested-Virtualization

Hyper-V differs from VMWare in many aspects. Therefore you can’t test your hypervisor on Hyper-V’s nested virtualization. In part 8, I’ll describe how to modify your hypervisor in a way that can be used in Hyper-V, so after part 8, you’ll be able to test your hypervisor on Hyper-V’s nested virtualization.

Concepts

We will frequently use some keywords in the rest of these series, and you should know about them (Most of the definitions are derived from Intel software developer’s manual, volume 3C).

Virtual Machine Monitor (VMM): VMM acts as a host and has full control of the processor(s) and other platform hardware. A VMM is able to retain selective control of processor resources, physical memory, interrupt management, and I/O.

Guest Software: Each virtual machine (VM) is a guest software environment.

VMX Root Operation and VMX Non-root Operation: A VMM will run in VMX root operation, and guest software will run in VMX non-root operation.

VMX transitions: Transitions between VMX root operation and VMX non-root operation.

VM entries: Transitions into VMX non-root operation.

Extended Page Table (EPT): A mechanism that uses a second layer for converting the guest’s physical address to the host’s physical address.

VM exits: Transitions from VMX non-root operation to VMX root operation.

Virtual machine control structure (VMCS): is a data structure in memory that exists exactly once per VM (or more precisely, one per virtual CPU) while the VMM manages it. With every change in the execution context between different VMs, the VMCS is restored for the current VM, defining the state of the VM’s virtual processor and VMM control Guest software using VMCS.

The VMCS consists of six logical groups:

  • Guest-state area: Processor state saved into the guest state area on VM exits and loaded on VM entries.
  • Host-state area: Processor state loaded from the host state area on VM exits.
  • VM-execution control fields: Fields controlling processor operation in VMX non-root operation.
  • VM-exit control fields: Fields that control VM exits.
  • VM-entry control fields: Fields that control VM entries.
  • VM-exit information fields: Read-only fields to receive information on VM exits describing the cause and the nature of the VM exit.

I found a great work illustrating the VMCS. The PDF version is also available here.

VMCS

VMCS

Don’t worry about the fields. I’ll explain most of them clearly in the latter parts. Just remember, VMCS Structure varies between different versions of a processor.

VMX Instructions

VMX introduces the following new instructions.

Intel MnemonicDescription
INVEPTInvalidate Translations Derived from EPT
INVVPIDInvalidate Translations Based on VPID
VMCALLCall to VM Monitor
VMCLEARClear Virtual-Machine Control Structure
VMFUNCInvoke VM function
VMLAUNCHLaunch Virtual Machine
VMRESUMEResume Virtual Machine
VMPTRLDLoad Pointer to Virtual-Machine Control Structure
VMPTRSTStore Pointer to Virtual-Machine Control Structure
VMREADRead Field from Virtual-Machine Control Structure
VMWRITEWrite Field to Virtual-Machine Control Structure
VMXOFFLeave VMX Operation
VMXONEnter VMX Operation

VMM Life Cycle

VM Cycle

  • The following items summarize the life cycle of a VMM and its guest software, as well as the interactions between them:
    • Software enters VMX operation by executing a VMXON instruction.
    • Using VM entries, a VMM can turn guests into VMs (one at a time). The VMM affects a VM entry using instructions VMLAUNCH and VMRESUME; it regains control using VM exits.
    • VM exits transfer control to an entry point specified by the VMM. The VMM can take action appropriate to the cause of the VM exit and then return to the VM using a VM entry.
    • Eventually, the VMM may decide to shut itself down and leave VMX operation. It does so by executing the VMXOFF instruction.

That’s enough for now!

Other hypervisor-related works and materials.

Awesome virtualization (Introducing books, papers, projects, courses, CVEs, and other hypervisor hypervisor-related works) - https://github.com/Wenzel/awesome-virtualization

7 Days to Virtualization: A Series on Hypervisor Development - (https://revers.engineering/7-days-to-virtualization-a-series-on-hypervisor-development/)

At last, if you want to use hypervisors for debugging, researching, or reverse-engineering, you can use HyperDbg Debugger, as many innovative methods based on hypervisors are implemented in this debugger that will help you in your reversing journey.

Conclusion

In this part, we study general keywords we should be aware of and create a simple testing environment for our future tests. In the next part, I will explain how to enable VMX on your machine using the driver we made above. Then we survey the rest of the virtualization, so see you in the next part.

The second part is also available here.

References

[1] Intel® 64 and IA-32 architectures software developer’s manual combined volumes 3 (https://software.intel.com/en-us/articles/intel-sdm)

[2] Hardware-assisted Virtualization (http://www.cs.cmu.edu/~412/lectures/L04_VTx.pdf)

[3] Writing Windows Kernel Driver (https://resources.infosecinstitute.com/writing-a-windows-kernel-driver/)

[4] What Is a Type 1 Hypervisor? – (http://www.virtualizationsoftware.com/type-1-hypervisors/)

[5] Intel / AMD CPU Internals – (https://github.com/LordNoteworthy/cpu-internals)

[6] Windows 10: Disable Signed Driver Enforcement – (https://ph.answers.acer.com/app/answers/detail/a_id/38288/~/windows-10%3A-disable-signed-driver-enforcement)

[7] Instruction Set Mapping » VMX Instructions – (https://docs.oracle.com/cd/E36784_01/html/E36859/gntbx.html)

[8] HyperDbg Documentation – (https://docs.hyperdbg.org)

This post is licensed under CC BY 4.0 by the author.

x64 Inline Assembly in Windows Driver Kit

A Tour of Mount in Linux

Comments powered by Disqus.

diff --git a/topics/hypervisor-from-scratch-part-2/index.html b/topics/hypervisor-from-scratch-part-2/index.html new file mode 100644 index 00000000..c117d5c9 --- /dev/null +++ b/topics/hypervisor-from-scratch-part-2/index.html @@ -0,0 +1,489 @@ + Hypervisor From Scratch – Part 2: Entering VMX Operation | Rayanfam Blog
Home Hypervisor From Scratch – Part 2: Entering VMX Operation
Post
Cancel

Hypervisor From Scratch – Part 2: Entering VMX Operation

If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous practical examples on how to utilize hypervisors for reverse engineering.

Introduction

It’s the second part of a multiple series of a tutorial called “Hypervisor From Scratch”. First, please consider reading the first part (Basic Concepts & Configure Testing Environment) before reading this part, as it contains the essential knowledge you need to know in order to understand the rest of this tutorial. In this part, we’ll talk about WDK drivers and finally start enabling VT-x.

Table of Contents

  • Introduction
  • Table of Contents
  • Overview
  • IRP Major Functions
    • What is an IRP?
    • Configuring IRP Major Functions
    • IRP Major Functions List
  • Loading Driver and Checking Device
  • Viewing Debugging Messages In DbgView
  • Detecting Hypervisor Support
    • Setting CR4 VMXE Bit
  • Conclusion
  • References

Overview

In this section, we will learn about Detecting Hypervisor Support for our processor, then we simply config the basic operations to Enable VMX, Entering VMX Operation, and we will learn more about Window Driver Kit (WDK).

The source code of this tutorial is available at :

[https://github.com/SinaKarvandi/Hypervisor-From-Scratch/]

IRP Major Functions

Besides our kernel-mode driver (“MyHypervisorDriver”), we’ll create a user-mode application called “MyHypervisorApp”. First of all, I should encourage you to write most of the codes (whenever it’s possible) in user-mode rather than the kernel-mode, and that’s because you might not have handled exceptions properly. Hence, it leads to BSODs, or on the other hand, running less code in kernel-mode reduces the possibility of putting some nasty kernel-mode bugs.

If you remember from the previous part, we created a Windows driver. Now we want to extend our project to support more IRP Major functions.

IRP Major Functions are located in a conventional Windows table created for every device. Once we register a device in Windows, we have to introduce a handler for these IRP Major Functions.

That’s like every device has a table of Major Functions. Whenever a user-mode application calls any of these functions, Windows finds the corresponding function (if the device driver supports that MJ Function), then passes an IRP to the kernel driver.

What is an IRP?

So, what is an IRP? IRP is a structure that represents an I/O Request Packet. This packet contains many details about its caller, parameters, state of the packet, etc. We extract the caller parameters from the IRP packet.

Now, we can handle the user-mode request in the kernel based on the details provided by IRP.

Remember, when our functions in the kernel driver receive the IRP packet, it’s the responsibility of our code to investigate the caller and check its privileges, etc.

Configuring IRP Major Functions

After registering a device (explained in the previous part), we need to introduce the Major Functions of our device.

The following code is responsible for configuring different IRP MJ Functions and introducing custom kernel-mode functions as the IRP handlers.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
    if (NtStatus == STATUS_SUCCESS)
+    {
+        for (Index = 0; Index < IRP_MJ_MAXIMUM_FUNCTION; Index++)
+        {
+            DriverObject->MajorFunction[Index] = DrvUnsupported;
+        }
+
+        DbgPrint("[*] Setting Devices major functions.");
+        DriverObject->MajorFunction[IRP_MJ_CLOSE]          = DrvClose;
+        DriverObject->MajorFunction[IRP_MJ_CREATE]         = DrvCreate;
+        DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DrvIoctlDispatcher;
+
+        DriverObject->MajorFunction[IRP_MJ_READ]  = DrvRead;
+        DriverObject->MajorFunction[IRP_MJ_WRITE] = DrvWrite;
+
+        DriverObject->DriverUnload = DrvUnload;
+
+        IoCreateSymbolicLink(&DosDeviceName, &DriverName);
+    }
+    else
+    {
+        DbgPrint("[*] There were some errors in creating device.");
+    }
+

You can see that we used “DrvUnsupported” for all functions. This function handles all MJ Functions and tells the user that it’s not supported.

The main body of “DrvUnsupported” is like this:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
NTSTATUS
+DrvUnsupported(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+{
+    DbgPrint("[*] This function is not supported :( !");
+
+    Irp->IoStatus.Status      = STATUS_SUCCESS;
+    Irp->IoStatus.Information = 0;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_SUCCESS;
+}
+

We also introduce other Major Functions that are essential for our device. We’ll complete the implementation of some of these MJ Functions in the future parts.

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
+
NTSTATUS
+DrvRead(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+{
+    DbgPrint("[*] Not implemented yet :( !");
+
+    Irp->IoStatus.Status      = STATUS_SUCCESS;
+    Irp->IoStatus.Information = 0;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+DrvWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+{
+    DbgPrint("[*] Not implemented yet :( !");
+
+    Irp->IoStatus.Status      = STATUS_SUCCESS;
+    Irp->IoStatus.Information = 0;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+DrvClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+{
+    DbgPrint("[*] Not implemented yet :( !");
+
+    Irp->IoStatus.Status      = STATUS_SUCCESS;
+    Irp->IoStatus.Information = 0;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_SUCCESS;
+}
+

Now let’s see the IRP MJ Functions list and other types of Windows Driver Kit handlers routine.

IRP Major Functions List

We can use this list of IRP Major Functions to perform different operations in a WDK driver.

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
+
#define IRP_MJ_CREATE                   0x00
+#define IRP_MJ_CREATE_NAMED_PIPE        0x01
+#define IRP_MJ_CLOSE                    0x02
+#define IRP_MJ_READ                     0x03
+#define IRP_MJ_WRITE                    0x04
+#define IRP_MJ_QUERY_INFORMATION        0x05
+#define IRP_MJ_SET_INFORMATION          0x06
+#define IRP_MJ_QUERY_EA                 0x07
+#define IRP_MJ_SET_EA                   0x08
+#define IRP_MJ_FLUSH_BUFFERS            0x09
+#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a
+#define IRP_MJ_SET_VOLUME_INFORMATION   0x0b
+#define IRP_MJ_DIRECTORY_CONTROL        0x0c
+#define IRP_MJ_FILE_SYSTEM_CONTROL      0x0d
+#define IRP_MJ_DEVICE_CONTROL           0x0e
+#define IRP_MJ_INTERNAL_DEVICE_CONTROL  0x0f
+#define IRP_MJ_SHUTDOWN                 0x10
+#define IRP_MJ_LOCK_CONTROL             0x11
+#define IRP_MJ_CLEANUP                  0x12
+#define IRP_MJ_CREATE_MAILSLOT          0x13
+#define IRP_MJ_QUERY_SECURITY           0x14
+#define IRP_MJ_SET_SECURITY             0x15
+#define IRP_MJ_POWER                    0x16
+#define IRP_MJ_SYSTEM_CONTROL           0x17
+#define IRP_MJ_DEVICE_CHANGE            0x18
+#define IRP_MJ_QUERY_QUOTA              0x19
+#define IRP_MJ_SET_QUOTA                0x1a
+#define IRP_MJ_PNP                      0x1b
+#define IRP_MJ_PNP_POWER                IRP_MJ_PNP // Obsolete....
+#define IRP_MJ_MAXIMUM_FUNCTION         0x1b
+

Every major function will only trigger if we call its corresponding function from the user-mode. For instance, there is a function (in user-mode) called CreateFile (And all its variants like CreateFileA and CreateFileW for ASCII and Unicode), so every time we call CreateFile, the function that registered as IRP_MJ_CREATE will be called, if we call ReadFile then IRP_MJ_READ, or WriteFile then IRP_MJ_WRITE  will be triggered.

You can see that Windows treats its devices like files, and everything we need to pass from user-mode to kernel-mode is available in an argument with the IRP * type and available as a buffer to the kernel IRP MJ Function handlers. Windows is responsible for copying the user-mode buffer to the kernel mode stack.

Don’t worry; we use it frequently in the rest of the project, but we only support IRP_MJ_CREATE in this part and left others unimplemented for future parts.

There are other terms called “IRP Minor Functions”. We left these functionalities as they’re not used in this series.

Loading Driver and Checking Device

In order to load our driver (MyHypervisorDriver), first, download OSR Driver Loader, then run Sysinternals DbgView as administrator. Ensure that your DbgView captures the kernel (you can check by going to Capture -> Capture Kernel).

Enable Capturing Event

After that open the OSR Driver Loader (go to OsrLoader -> kit-> WNET -> AMD64 -> FRE) and open OSRLOADER.exe. Now, if you build your driver, find the .sys file (in MyHypervisorDriver\x64\Debug\ should be a file named: “MyHypervisorDriver.sys”), in OSR Driver Loader, click to browse and select (MyHypervisorDriver.sys) and then click to “Register Service” after that, you see a message box that shows your driver registered successfully, you should click on “Start Service”.

Please note that you should have WDK installed for your Visual Studio in order to be able to build your project.

Load Driver in OSR Driver Loader

Now come back to the DbgView, you should see that your driver loaded successfully, and a message “[*] DriverEntry Called.” should appear.

If there is no problem, then you’re good to go. Otherwise, you can check the next step if you have a problem with DbgView.

Keep in mind that now we have registered our driver, so we can use SysInternals WinObj to see whether “MyHypervisorDevice” is available or not.

WinObj

Viewing Debugging Messages In DbgView

Unfortunately, for some unknown reason, I’m unable to view the result of DbgPrint(). If you can see the result, then you can skip this step but if you have a problem, perform the following steps:

As I mentioned in part 1:

In “regedit.exe”, add a key:

1
+
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Debug Print Filter
+

Under that, add a DWORD value named IHVDRIVER with a value of 0xFFFF.

This method should solve the problem, but if the problem still persists, we have another option. For this purpose, we can use WinDbg to find a Windows Kernel global variable called nt!Kd\_DEFAULT\_Mask. This variable is responsible for showing the results in DbgView. It has a mask that I’m not aware of, so I just put a 0xffffffff into it to simply make it show everything!

To do this, you need a Windows Kernel Debugging using WinDbg. In WinDbg, you can run the following command:

1
+
kd> eb nt!kd_Default_Mask ff ff ff ff
+

kd_DEFAULT_Mask

After that, we should see the results and everything we’ll be ready for the next step.

Remember, this is an essential step for the rest of the topic because if we can’t see any kernel messages, sure, we can’t debug it too.

DbgView

Detecting Hypervisor Support

Discovering support for VMX is the first thing we should consider before enabling VT-x. This is covered in Intel Software Developer’s Manual volume 3C section 23.6 DISCOVERING SUPPORT FOR VMX.

You could know the presence of VMX using CPUID if CPUID.1:ECX.VMX[bit 5] = 1, then VMX operation is supported.

First, we need to know whether or not we’re running on an Intel-based processor. We can understand this using the CPUID instruction and finding the vendor string “GenuineIntel”.

The following function returns the vendor string by using the CPUID instruction.

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
+
std::string
+GetCpuID()
+{
+    // Initialize used variables
+    char   SysType[13]; // Array consisting of 13 single bytes/characters
+    string CpuID;       // The string that will be used to add all the characters to
+                        // Starting coding in assembly language
+    _asm
+        {
+            // Execute CPUID with EAX = 0 to get the CPU producer
+		XOR EAX, EAX
+		CPUID
+                    // MOV EBX to EAX and get the characters one by one by using shift out right bitwise operation.
+		MOV EAX, EBX
+		MOV SysType[0], AL
+		MOV SysType[1], AH
+		SHR EAX, 16
+		MOV SysType[2], AL
+		MOV SysType[3], AH
+                // Get the second part the same way but these values are stored in EDX
+		MOV EAX, EDX
+		MOV SysType[4], AL
+		MOV SysType[5], AH
+		SHR EAX, 16
+		MOV SysType[6], AL
+		MOV SysType[7], AH
+                // Get the third part
+		MOV EAX, ECX
+		MOV SysType[8], AL
+		MOV SysType[9], AH
+		SHR EAX, 16
+		MOV SysType[10], AL
+		MOV SysType[11], AH
+		MOV SysType[12], 00
+        }
+    CpuID.assign(SysType, 12);
+    return CpuID;
+}
+

The last step is checking for the presence of VMX. We can check it using the following code :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
bool
+DetectVmxSupport()
+{
+    bool VMX = false;
+    __asm {
+		XOR    EAX, EAX
+		INC    EAX
+		CPUID
+		BT     ECX, 0x5
+		JC     VMXSupport
+		VMXNotSupport :
+		JMP     NopInstr
+		VMXSupport :
+		MOV    VMX, 0x1
+		NopInstr :
+		NOP
+    }
+
+    return VMX;
+}
+

As you can see, it checks CPUID with EAX=1, and if the 5th (6th) bit is one, then the VMX Operation is supported. We can also perform the same thing in Kernel Driver.

All in all, our main code to detect the support for VMX should be something like this:

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
+
int
+main()
+{
+    std::string CpuId;
+
+    PrintAppearance();
+
+    CpuId = GetCpuID();
+
+    printf("[*] The CPU Vendor is : %s \n", CpuID.c_str());
+
+    if (CpuId == "GenuineIntel")
+    {
+        printf("[*] The Processor virtualization technology is VT-x. \n");
+    }
+    else
+    {
+        printf("[*] This program is not designed to run in a non-VT-x environment !\n");
+        return 1;
+    }
+
+    if (DetectVmxSupport())
+    {
+        printf("[*] VMX Operation is supported by your processor .\n");
+    }
+    else
+    {
+        printf("[*] VMX Operation is not supported by your processor .\n");
+        return 1;
+    }
+
+    HANDLE hWnd = CreateFile(L"\\\\.\\MyHypervisorDevice",
+                             GENERIC_READ | GENERIC_WRITE,
+                             FILE_SHARE_READ |
+                                 FILE_SHARE_WRITE,
+                             NULL, /// lpSecurityAttirbutes
+                             OPEN_EXISTING,
+                             FILE_ATTRIBUTE_NORMAL |
+                                 FILE_FLAG_OVERLAPPED,
+                             NULL); /// lpTemplateFile
+
+    _getch();
+
+    return 0;
+}
+

The final result:

User-mode app

Enabling VMX Operation

If the processor supports the VMX Operation, it’s time to enable it. As I told you above, IRP_MJ_CREATE is the first function that should be used to start the operation.

Before entering the VMX operation, we should enable VMX by setting CR4.VMXE[bit 13] = 1. VMX operation is then entered by executing the VMXON instruction. VMXON causes an invalid-opcode exception (#UD) if executed with CR4.VMXE = 0. Once in VMX operation, it is not possible to clear CR4.VMXE.

After that, we can leave the VMX operation by executing the VMXOFF instruction and, this time, CR4.VMXE can be cleared.

VMXON is also controlled by the IA32_FEATURE_CONTROL MSR (MSR address 3AH). This MSR is cleared to zero when a logical processor is reset.

Let’s look at the first bit of this MSR:

  •  Bit 0 is the lock bit. If this bit is clear, VMXON causes a general-protection (#GP) exception. If the lock bit is set, WRMSR to this MSR causes a general-protection exception; the MSR cannot be modified until a power-up reset.

What does it mean? It means that we can disable the VMX feature without the ability to be enabled again. Only after a system reset, we can enable the VMX.

System BIOS can use this bit to provide a setup option for BIOS to disable support for VMX. To enable VMX support in a platform, BIOS must set bit 1, bit 2, or both, as well as the lock bit.

Setting CR4 VMXE Bit

Do you remember the previous part where I told you how to create an inline assembly in Windows Driver Kit (x64)

Now we should create some function to perform this operation in assembly.

Just in Header File (in my case Source.h) declare your function:

1
+
extern void inline AsmEnableVmxOperation(void);
+

Then in the assembly file (in my case, “SourceAsm.asm”), add this function (Which sets the 13th (14th) bit of CR4).

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
AsmEnableVmxOperation PROC PUBLIC
+
+	PUSH RAX			    ; Save the state
+	
+	XOR RAX, RAX			; Clear the RAX
+	MOV RAX, CR4
+
+	OR RAX,02000h	    	; Set the 14th bit
+	MOV CR4, RAX
+	
+	POP RAX			     	; Restore the state
+	RET
+
+AsmEnableVmxOperation ENDP
+

Also, declare your function in the above of SourceAsm.asm.

1
+
PUBLIC AsmEnableVmxOperation
+

This assembly function should be called in DrvCreate:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+
NTSTATUS
+DrvCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+{
+    //
+    // Enabling VMX Operation
+    //
+    AsmEnableVmxOperation();
+    DbgPrint("[*] VMX Operation Enabled Successfully !");
+
+    Irp->IoStatus.Status      = STATUS_SUCCESS;
+    Irp->IoStatus.Information = 0;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_SUCCESS;
+}
+

At last, we should call the following function from the user-mode:

1
+2
+3
+4
+5
+6
+7
+8
+9
+
    HANDLE hWnd = CreateFile(L"\\\\.\\MyHypervisorDevice",
+                             GENERIC_READ | GENERIC_WRITE,
+                             FILE_SHARE_READ |
+                                 FILE_SHARE_WRITE,
+                             NULL, /// lpSecurityAttirbutes
+                             OPEN_EXISTING,
+                             FILE_ATTRIBUTE_NORMAL |
+                                 FILE_FLAG_OVERLAPPED,
+                             NULL); /// lpTemplateFile
+

If you see the following result, you successfully completed the second part.

Final Show

Important Note: Please consider that your .asm file should have a different name from your main driver file (.c file). For example, if your driver file is “Source.c”, then using the name “Source.asm” causes weird linking errors in Visual Studio. You should change the name of your .asm file to something like “SourceAsm.asm” to avoid these linker errors.

Conclusion

In this part, we learned about the basic stuff we need to know to create a Windows Driver Kit program, and then we entered our virtual environment to build a cornerstone for the rest of the parts.

In the third part, we’re getting deeper with Intel VT-x and making our driver even more advanced.

Note: Remember that hypervisors change over time because new features are added to the operating systems or new technologies are used. For example, updates to Meltdown & Spectre have made a lot of changes to the hypervisors. So, if you want to use Hypervisor From Scratch in your projects, research, or whatever, you should use the HyperDbg drivers. HyperDbg is actively maintained, stable, and reliable, ensuring you avoid the errors and instability problems that can arise from using older parts of the tutorial series.

The third part is also available [here].(https://rayanfam.com/topics/hypervisor-from-scratch-part-3/).

References

[1] Intel® 64 and IA-32 architectures software developer’s manual combined volumes 3 (https://software.intel.com/en-us/articles/intel-sdm

[2] IRP_MJ_DEVICE_CONTROL (https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/irp-mj-device-control)

[3]  Windows Driver Kit Samples (https://github.com/Microsoft/Windows-driver-samples/blob/master/general/ioctl/wdm/sys/sioctl.c)

[4] Setting Up Local Kernel Debugging of a Single Computer Manually (https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-local-kernel-debugging-of-a-single-computer-manually)

[5] Obtain processor manufacturer using CPUID (https://www.daniweb.com/programming/software-development/threads/112968/obtain-processor-manufacturer-using-cpuid)

[6] Plug and Play Minor IRPs (https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/plug-and-play-minor-irps)

[7] _FAST_IO_DISPATCH structure (https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_fast_io_dispatch)

[8] Filtering IRPs and Fast I/O (https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/filtering-irps-and-fast-i-o)

[9] Windows File System Filter Driver Development (https://www.apriorit.com/dev-blog/167-file-system-filter-driver)

This post is licensed under CC BY 4.0 by the author.

A Tour of Mount in Linux

Using Intel's Streaming SIMD Extensions 3 (MONITOR MWAIT) As A Kernel Debugging Trick

Comments powered by Disqus.

diff --git a/topics/hypervisor-from-scratch-part-3/index.html b/topics/hypervisor-from-scratch-part-3/index.html new file mode 100644 index 00000000..2eb4658c --- /dev/null +++ b/topics/hypervisor-from-scratch-part-3/index.html @@ -0,0 +1,927 @@ + Hypervisor From Scratch – Part 3: Setting up Our First Virtual Machine | Rayanfam Blog
Home Hypervisor From Scratch – Part 3: Setting up Our First Virtual Machine
Post
Cancel

Hypervisor From Scratch – Part 3: Setting up Our First Virtual Machine

If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous practical examples on how to utilize hypervisors for reverse engineering.

Introduction

This is the third part of the tutorial “Hypervisor From Scratch”. In this part, we’ll continue our journey toward learning hypervisors and how to start creating our custom VMM. In the previous part, we learned how to make WDK drivers that handle user-mode requests and enable the VMX bit in our processor. In this part, we extend our driver and add VMX functionalities to our VMM. At last, we use different VT-x instructions in the VMM.

Table of Contents

  • Introduction
  • Table of Contents
  • Overview
  • Interacting with the driver from user-mode
    • Buffer Descriptions for I/O Control Codes
    • METHOD_BUFFERED
    • METHOD_IN_DIRECT and METHOD_OUT_DIRECT
    • METHOD_NIETHER
    • IOCTL Structure
    • IOCTL Dispatcher
  • Per Processor Configuration
    • Setting Affinity
  • Converting physical and virtual addresses
  • Check VMX support in the kernel
  • VMXON Region
    • Allocating VMXON Region
  • Virtual-Machine Control Data Structures (VMCS)
    • Initializing VMCS Region
  • VMXOFF Instruction
  • Testing VMM
  • Conclusion
  • References

Overview

In this part, we demonstrate how to interact with VMM from Windows user-mode (IOCTL Dispatcher), then we solve the problems with the affinity and running code in a particular core. Finally, we get familiar with initializing VMXON Regions and VMCS Regions, then we load our hypervisor into each core and implement our custom functions to work with hypervisor instructions and many more things related to Virtual-Machine Control Data Structures (VMCS).

Some of the implementations are derived from HyperBone (Minimalistic VT-X hypervisor with hooks), HyperPlatform by Satoshi Tanda and hvpp which is amazing work by my friend Petr Beneš.

The full source code of this tutorial is available on :

[https://github.com/SinaKarvandi/Hypervisor-From-Scratch]

Interacting with the driver from user-mode

The most important function for us in IRP MJ functions is DrvIoctlDispatcher or (IRP_MJ_DEVICE_CONTROL) Major Function, and that’s because this function can be called from user-mode with a particular IOCTL number, which means we can have a special code in our driver and implement a unique functionality corresponding this code, then by knowing the code (from user-mode) we can ask our driver to perform the request, so this way we can request a certain functionality from the kernel.

Buffer Descriptions for I/O Control Codes

As explained above, IOCTL codes request a certain functionality from the kernel-mode. It’s clear that in most cases, we need to transfer a buffer (structure) to the kernel, which shows different details about our request. Thus, we need to copy the buffer from the user-mode and pass it to the kernel-mode routines.

There are several methods in which Windows copies the buffer of the user-mode codes to the kernel for dispatching IOCTs.

  • METHOD_BUFFERED
  • METHOD_IN_DIRECT
  • METHOD_OUT_DIRECT
  • METHOD_NIETHER

The difference is where buffers transfer between user-mode and kernel-mode. Let’s see each of them in detail.

METHOD_BUFFERED

For METHOD_BUFFERED, the pointer to the user-mode buffer is available at Irp->AssociatedIrp.SystemBuffer, and we can put the output buffer to the same address (Irp->AssociatedIrp.SystemBuffer).

This method is typically used for transferring small amounts of data per request. Most I/O control codes for device and intermediate drivers use this type as Windows copies the user-mode buffer to the kernel-mode and the kernel-mode buffer to the user-mode.

METHOD_IN_DIRECT and METHOD_OUT_DIRECT

For these methods, the pointer to the user-mode buffer is available at Irp->AssociatedIrp.SystemBuffer.

This type is generally used for reading or writing large amounts of data that must be transferred fast as it won’t copy the data and instead shares the pages.

The METHOD_IN_DIRECT is specified if the caller pass data to the driver, and the METHOD_OUT_DIRECT is selected if the caller will receive data from the driver.

METHOD_NIETHER

The input buffer address is specified by Parameters.DeviceIoControl.Type3InputBuffer in the driver’s IO_STACK_LOCATION structure, and the output buffer(to the user-mode) is specified by Irp->UserBuffer.

This method is neither buffered nor direct I/O. The I/O manager does not provide any system buffers, and the IRP provides the user-mode virtual addresses of the input and output buffers without validating or mapping them.

IOCTL Structure

We should specify all of the above transferring types into the following structure.

There is a convention for defining IOCTLs as mentioned here. The IOCTL is a 32-bit number. The first two low bits represent the “transfer type”, which can be METHOD_OUT_DIRECT, METHOD_IN_DIRECT, METHOD_BUFFERED, or METHOD_NEITHER.

The next set of bits from 2 to 13 define the “Function Code”. The high bit is referred to as the “custom bit”. This is used to determine user-defined IOCTLs versus system defined. This means that function codes 0x800 and greater are customs defined for Windows Messages.

The next two bits define the access required to issue the IOCTL. This is how the I/O Manager can reject IOCTL requests if the handle has not been opened with the correct access. The access types are such as FILE_READ_DATA, FILE_WRITE_DATA, etc.

The last bits represent the device type the IOCTLs are written for. The high bit again represents user-defined values.

We can use the following defined macro to create our IOCTL code.

1
+
#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)
+

For example, the following IOCTL code can be defined.

1
+2
+
#define IOCTL_TEST \
+    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
+

IOCTL Dispatcher

Now let’s implement our functions for dispatching IOCTL codes.

Note that the PAGED_CODE() macro ensures that the calling thread runs at an IRQL low enough to permit paging, this macro is used to ensure that paging is enabled, for example, the current execution is not at DISPATCH_LEVEL. Don’t worry; we’ll discuss IRQL in detail in the future parts.

The next step is to check the input buffer and the output buffer’s length. We’ll check it because we need to ensure that the user provides a buffer for the kernel and expects a buffer to be received. The following code gets the input and output buffer length from the IO_STACK_LOCATION.

1
+2
+3
+4
+5
+6
+7
+8
+9
+
    IrpStack     = IoGetCurrentIrpStackLocation(Irp);
+    InBufLength  = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
+    OutBufLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
+
+    if (!InBufLength || OutBufLength < DataLen)
+    {
+        NtStatus = STATUS_INVALID_PARAMETER;
+        goto End;
+    }
+

Then we have to use the switch-case through the IOCTL code.

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
+
  switch (IrpStack->Parameters.DeviceIoControl.IoControlCode)
+    {
+    case IOCTL_SIOCTL_METHOD_BUFFERED:
+
+        ...
+        
+        break;
+
+    case IOCTL_SIOCTL_METHOD_NEITHER:
+
+        ...
+        
+        break;
+
+    case IOCTL_SIOCTL_METHOD_IN_DIRECT:
+
+        ...
+        
+        break;
+
+    case IOCTL_SIOCTL_METHOD_OUT_DIRECT:
+    
+        ...
+        
+        break;
+
+    default:
+
+        //
+        // The specified I/O control code is unrecognized by this driver.
+        //
+        NtStatus = STATUS_INVALID_DEVICE_REQUEST;
+        DbgPrint("ERROR: unrecognized IOCTL %x\n",
+                 IrpStack->Parameters.DeviceIoControl.IoControlCode);
+        break;
+    }
+

The PrintIrpInfo is like this :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
VOID PrintIrpInfo(PIRP Irp)
+{
+    PIO_STACK_LOCATION  IrpStack;
+    IrpStack = IoGetCurrentIrpStackLocation(Irp);
+
+    PAGED_CODE();
+
+    DbgPrint("\tIrp->AssociatedIrp.SystemBuffer = 0x%p\n",
+        Irp->AssociatedIrp.SystemBuffer);
+    DbgPrint("\tIrp->UserBuffer = 0x%p\n", Irp->UserBuffer);
+    DbgPrint("\tIrpStack->Parameters.DeviceIoControl.Type3InputBuffer = 0x%p\n",
+        IrpStack->Parameters.DeviceIoControl.Type3InputBuffer);
+    DbgPrint("\tIrpStack->Parameters.DeviceIoControl.InputBufferLength = %d\n",
+        IrpStack->Parameters.DeviceIoControl.InputBufferLength);
+    DbgPrint("\tIrpStack->Parameters.DeviceIoControl.OutputBufferLength = %d\n",
+        IrpStack->Parameters.DeviceIoControl.OutputBufferLength);
+    return;
+}
+

If you remember from the previous part where we created a handle (HANDLE) using CreateFile, now we can use the DeviceIoControl with the previous handle and call DrvIoctlDispatcher or (IRP_MJ_DEVICE_CONTROL) along with our provided buffer in the kernel.

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
+
    char  OutputBuffer[1000];
+    char  InputBuffer[1000];
+    ULONG BytesReturned;
+    BOOL  Result;
+
+    //
+    // Performing METHOD_BUFFERED
+    //
+    StringCbCopy(InputBuffer, sizeof(InputBuffer), "This String is from User Application; using METHOD_BUFFERED");
+
+    printf("\nCalling DeviceIoControl METHOD_BUFFERED:\n");
+
+    memset(OutputBuffer, 0, sizeof(OutputBuffer));
+
+    Result = DeviceIoControl(Handle,
+                             (DWORD)IOCTL_SIOCTL_METHOD_BUFFERED,
+                             &InputBuffer,
+                             (DWORD)strlen(InputBuffer) + 1,
+                             &OutputBuffer,
+                             sizeof(OutputBuffer),
+                             &BytesReturned,
+                             NULL);
+
+    if (!Result)
+    {
+        printf("Error in DeviceIoControl : %d", GetLastError());
+        return false;
+    }
+    printf("    OutBuffer (%d): %s\n", BytesReturned, OutputBuffer);
+

For further reading, there is an old, yet great topic here which describes the different types of IOCTL dispatching.

We’re done with WDK basics! It’s time to see how we can use Windows to build our VMM.


Per Processor Configuration

Affinity to a special logical processor is one of the main considerations when working with the hypervisor.

In my Intel Core i7 6820HQ, I have four physical cores, and each core can run two threads simultaneously (due to the hyper-threading); thus, we have eight logical processors and, of course, eight sets of all the registers (including general purpose registers and MSR registers) and more importantly, eight sets of VMCSs and VMXON Regions, etc. so we should configure our VMM to work on eight logical processors.

Setting Affinity

To get the count of logical processors, we can use KeQueryActiveProcessorCount(0). Then we should pass a KAFFINITY mask to the KeSetSystemAffinityThread, which sets the system affinity of the current thread.

KAFFINITY mask can be configured using a simple power function:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
int
+MathPower(int Base, int Exponent)
+{
+    int Result = 1;
+    for (;;)
+    {
+        if (Exponent & 1)
+        {
+            Result *= Base;
+        }
+
+        Exponent >>= 1;
+        if (!Exponent)
+        {
+            break;
+        }
+        Base *= Base;
+    }
+    return Result;
+}
+

After that, we should use the following code to change the affinity of the processor and run our code in all the logical cores separately:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
    KAFFINITY AffinityMask;
+    for (size_t i = 0; i < KeQueryActiveProcessors(); i++)
+    {
+        AffinityMask = MathPower(2, i);
+        KeSetSystemAffinityThread(AffinityMask);
+
+        DbgPrint("=====================================================");
+        DbgPrint("Current thread is executing in %d th logical processor.", i);
+
+        // run code here
+    }
+

This way, we can run our codes in the different logical cores. Now, let’s see other essential functionalities we need for our hypervisor.

Converting physical and virtual addresses

VMXON Regions and VMCS Regions (see below) use the physical address as the operand to VMXON and VMPTRLD instructions, so we should create functions to convert Virtual Address to Physical address:

1
+2
+3
+4
+5
+
UINT64
+VirtualToPhysicalAddress(void * Va)
+{
+    return MmGetPhysicalAddress(Va).QuadPart;
+}
+

And as long as we can’t directly use physical addresses for our modifications in protected-mode, we have to convert Physical addresses to Virtual addresses too.

1
+2
+3
+4
+5
+6
+7
+8
+
UINT64
+PhysicalToVirtualAddress(UINT64 Pa)
+{
+    PHYSICAL_ADDRESS PhysicalAddr;
+    PhysicalAddr.QuadPart = Pa;
+
+    return MmGetVirtualForPhysical(PhysicalAddr);
+}
+

Check VMX support in the kernel

In the previous part, we query about the presence of hypervisor from user-mode, but we should also consider checking about hypervisor from kernel-mode too. This reduces the possibility of getting kernel errors in the future, or there might be something that disables the hypervisor using the lock bit. By the way, the following code checks IA32_FEATURE_CONTROL MSR (MSR address 3AH) to see if the lock bit is set or not.

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
+
BOOLEAN
+IsVmxSupported()
+{
+    CPUID Data = {0};
+
+    //
+    // Check for the VMX bit
+    //
+    __cpuid((int *)&Data, 1);
+    if ((Data.ecx & (1 << 5)) == 0)
+        return FALSE;
+
+    IA32_FEATURE_CONTROL_MSR Control = {0};
+    Control.All                      = __readmsr(MSR_IA32_FEATURE_CONTROL);
+
+    //
+    // BIOS lock check
+    //
+    if (Control.Fields.Lock == 0)
+    {
+        Control.Fields.Lock        = TRUE;
+        Control.Fields.EnableVmxon = TRUE;
+        __writemsr(MSR_IA32_FEATURE_CONTROL, Control.All);
+    }
+    else if (Control.Fields.EnableVmxon == FALSE)
+    {
+        DbgPrint("[*] VMX locked off in BIOS");
+        return FALSE;
+    }
+
+    return TRUE;
+}
+

The structures used in the above function are declared like this:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
typedef union _IA32_FEATURE_CONTROL_MSR
+{
+    ULONG64 All;
+    struct
+    {
+        ULONG64 Lock : 1;                // [0]
+        ULONG64 EnableSMX : 1;           // [1]
+        ULONG64 EnableVmxon : 1;         // [2]
+        ULONG64 Reserved2 : 5;           // [3-7]
+        ULONG64 EnableLocalSENTER : 7;   // [8-14]
+        ULONG64 EnableGlobalSENTER : 1;  // [15]
+        ULONG64 Reserved3a : 16;         //
+        ULONG64 Reserved3b : 32;         // [16-63]
+    } Fields;
+} IA32_FEATURE_CONTROL_MSR, *PIA32_FEATURE_CONTROL_MSR;
+
+typedef struct _CPUID
+{
+    int eax;
+    int ebx;
+    int ecx;
+    int edx;
+} CPUID, *PCPUID;
+

VMXON Region

Several regions are used in the VMX to handle the virtual machine state. In this part, we will walk through the VMXON Region and the VMCS Region.

Before executing VMXON, we should allocate a naturally aligned 4-KByte region of memory that our logical processor will use it to support VMX operation. This region is called the VMXON Region. The address of the VMXON Region (the VMXON pointer) is provided in an operand to VMXON instruction.

A VMM should use different VMXON Regions for each logical processor; otherwise, the behavior is “undefined”.

Please note that VMX operation requires that the following bits be 1 in VMX operation: CR0.PE, CR0.NE, CR0.PG, and CR4.VMXE. The restrictions on CR0.PE and CR0.PG implies that VMX operation is supported only in paged protected-mode. Therefore, the guest software cannot be run in unpaged protected-mode or in real-address mode.

Now that we are configuring the hypervisor, we should have a global variable that describes the state of our virtual machine. The following structure is created for this purpose. We currently have two fields called (VMXON_REGION and VMCS_REGION), but we will add new fields and enhance this structure in the future.

1
+2
+3
+4
+5
+
typedef struct _VIRTUAL_MACHINE_STATE
+{
+    UINT64 VmxonRegion; // VMXON region
+    UINT64 VmcsRegion;  // VMCS region
+} VIRTUAL_MACHINE_STATE, *PVIRTUAL_MACHINE_STATE;
+

And, of course, a global variable:

1
+
extern VIRTUAL_MACHINE_STATE* g_GuestState;
+

Allocating VMXON Region

The following function (in “Memory.c”) to allocate VMXON Region and execute VMXON instruction using the allocated region’s pointer.

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
+
BOOLEAN
+AllocateVmxonRegion(IN VIRTUAL_MACHINE_STATE * GuestState)
+{
+    // at IRQL > DISPATCH_LEVEL memory allocation routines don't work
+    if (KeGetCurrentIrql() > DISPATCH_LEVEL)
+        KeRaiseIrqlToDpcLevel();
+
+    PHYSICAL_ADDRESS PhysicalMax = {0};
+    PhysicalMax.QuadPart         = MAXULONG64;
+
+    int    VMXONSize = 2 * VMXON_SIZE;
+    BYTE * Buffer    = MmAllocateContiguousMemory(VMXONSize + ALIGNMENT_PAGE_SIZE, PhysicalMax); // Allocating a 4-KByte Contigous Memory region
+
+    PHYSICAL_ADDRESS Highest = {0}, Lowest = {0};
+    Highest.QuadPart = ~0;
+
+    // BYTE* Buffer = MmAllocateContiguousMemorySpecifyCache(VMXONSize + ALIGNMENT_PAGE_SIZE, Lowest, Highest, Lowest, MmNonCached);
+
+    if (Buffer == NULL)
+    {
+        DbgPrint("[*] Error : Couldn't Allocate Buffer for VMXON Region.");
+        return FALSE; // ntStatus = STATUS_INSUFFICIENT_RESOURCES;
+    }
+    UINT64 PhysicalBuffer = VirtualToPhysicalAddress(Buffer);
+
+    // zero-out memory
+    RtlSecureZeroMemory(Buffer, VMXONSize + ALIGNMENT_PAGE_SIZE);
+    UINT64 AlignedPhysicalBuffer = (BYTE *)((ULONG_PTR)(PhysicalBuffer + ALIGNMENT_PAGE_SIZE - 1) & ~(ALIGNMENT_PAGE_SIZE - 1));
+
+    UINT64 AlignedVirtualBuffer = (BYTE *)((ULONG_PTR)(Buffer + ALIGNMENT_PAGE_SIZE - 1) & ~(ALIGNMENT_PAGE_SIZE - 1));
+
+    DbgPrint("[*] Virtual allocated buffer for VMXON at %llx", Buffer);
+    DbgPrint("[*] Virtual aligned allocated buffer for VMXON at %llx", AlignedVirtualBuffer);
+    DbgPrint("[*] Aligned physical buffer allocated for VMXON at %llx", AlignedPhysicalBuffer);
+
+    // get IA32_VMX_BASIC_MSR RevisionId
+
+    IA32_VMX_BASIC_MSR basic = {0};
+
+    basic.All = __readmsr(MSR_IA32_VMX_BASIC);
+
+    DbgPrint("[*] MSR_IA32_VMX_BASIC (MSR 0x480) Revision Identifier %llx", basic.Fields.RevisionIdentifier);
+
+    // Changing Revision Identifier
+    *(UINT64 *)AlignedVirtualBuffer = basic.Fields.RevisionIdentifier;
+
+    int Status = __vmx_on(&AlignedPhysicalBuffer);
+    if (Status)
+    {
+        DbgPrint("[*] VMXON failed with status %d\n", Status);
+        return FALSE;
+    }
+
+    g_GuestState->VmxonRegion = AlignedPhysicalBuffer;
+
+    return TRUE;
+}
+

Let’s explain the above function. In the above function, we used MmAllocateContiguousMemory to allocate a contiguous and aligned page. We can also use MmAllocateContiguousMemorySpecifyCache to specify the cache type for the allocated memory.

You can read this link to learn about different types of memory caches.

To ensure proper behavior in VMX operation, we should maintain the VMCS region and related structures in writeback cacheable memory. Alternatively, we may map any of these regions or structures with the UC (uncached) memory type. Doing so is strongly discouraged unless necessary as it will cause the performance of transitions using those structures to suffer significantly.

Writeback is a storage method in which data is written into the cache every time a change occurs but is written into the corresponding location in the main memory only at specified intervals or under certain conditions. Being cachable or not cachable can be determined from the cache disable bit in paging structures (PTE) and in the Memory type range register (MTRR), which is described thoroughly in the 7th part of this series.

By the way, we allocated 8192 bytes because there is no guarantee that Windows allocates the aligned memory so that we can find a piece of 4096 bytes aligned in 8196 bytes. (by aligning, I mean the physical address should be divisible by 4096 without any reminder).

In my experience, the MmAllocateContiguousMemory allocation is always aligned. Maybe it is because every page in PFN is allocated by 4096 bytes, and as long as we need 4096 bytes, thus it’s aligned.

If you are interested in Page Frame Number (PFN), you can read Inside Windows Page Frame Number (PFN) – Part 1 and Inside Windows Page Frame Number (PFN) – Part 2.

Now we should convert the allocated memory address to its physical address and make sure it’s aligned.

1
+2
+3
+4
+5
+
    PHYSICAL_ADDRESS PhysicalMax = {0};
+    PhysicalMax.QuadPart         = MAXULONG64;
+
+    int    VMXONSize = 2 * VMXON_SIZE;
+    BYTE * Buffer    = MmAllocateContiguousMemory(VMXONSize + ALIGNMENT_PAGE_SIZE, PhysicalMax); // Allocating a 4-KByte Contigous Memory region
+

Memory that MmAllocateContiguousMemory allocates is uninitialized. The kernel-mode driver must first set this memory to zero, and we use RtlSecureZeroMemory for this purpose.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
    UINT64 PhysicalBuffer = VirtualToPhysicalAddress(Buffer);
+
+    // zero-out memory
+    RtlSecureZeroMemory(Buffer, VMXONSize + ALIGNMENT_PAGE_SIZE);
+    UINT64 AlignedPhysicalBuffer = (BYTE *)((ULONG_PTR)(PhysicalBuffer + ALIGNMENT_PAGE_SIZE - 1) & ~(ALIGNMENT_PAGE_SIZE - 1));
+
+    UINT64 AlignedVirtualBuffer = (BYTE *)((ULONG_PTR)(Buffer + ALIGNMENT_PAGE_SIZE - 1) & ~(ALIGNMENT_PAGE_SIZE - 1));
+
+    DbgPrint("[*] Virtual allocated buffer for VMXON at %llx", Buffer);
+    DbgPrint("[*] Virtual aligned allocated buffer for VMXON at %llx", AlignedVirtualBuffer);
+    DbgPrint("[*] Aligned physical buffer allocated for VMXON at %llx", AlignedPhysicalBuffer);
+

From Intel’s manual (24.11.5 VMXON Region ):

Before executing VMXON, software should write the VMCS revision identifier to the VMXON region. (Specifically, it should write the 31-bit VMCS revision identifier to bits 30:0 of the first 4 bytes of the VMXON region; bit 31 should be cleared to 0.)

It need not initialize the VMXON region in any other way. Software should use a separate region for each logical processor and should not access or modify the VMXON region of a logical processor between the execution of VMXON and VMXOFF on that logical processor. Doing otherwise may lead to unpredictable behavior.

So let’s get the Revision Identifier from IA32_VMX_BASIC_MSR and write it to the VMXON Region.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
    // get IA32_VMX_BASIC_MSR RevisionId
+
+    IA32_VMX_BASIC_MSR basic = {0};
+
+    basic.All = __readmsr(MSR_IA32_VMX_BASIC);
+
+    DbgPrint("[*] MSR_IA32_VMX_BASIC (MSR 0x480) Revision Identifier %llx", basic.Fields.RevisionIdentifier);
+
+    // Changing Revision Identifier
+    *(UINT64 *)AlignedVirtualBuffer = basic.Fields.RevisionIdentifier;
+

The last part is used for executing VMXON instruction.

1
+2
+3
+4
+5
+6
+7
+8
+
    int Status = __vmx_on(&AlignedPhysicalBuffer);
+    if (Status)
+    {
+        DbgPrint("[*] VMXON failed with status %d\n", Status);
+        return FALSE;
+    }
+
+    g_GuestState->VmxonRegion = AlignedPhysicalBuffer;
+

__vmx_on is the intrinsic function for executing VMXON. The status code shows different meanings.

ValueMeaning
0The operation succeeded.
1The operation failed with extended status available in the VM-instruction error field of the current VMCS.
2The operation failed without status available.

If we set the VMXON Region using VMXON and it fails, then the status is equal to 1. If there isn’t any VMCS, the status is equal to 2, and if the operation was successful, the status is zero. We get errors if we execute the above code twice without executing VMXOFF.

Now, the VMXON Region is ready, and we’re good to go.


Virtual-Machine Control Data Structures (VMCS)

A logical processor uses virtual-machine control data structure (VMCS) while it’s in VMX operation. These manage transitions into and out of VMX non-root operation (VM entries and VM exits) as well as processor behavior in VMX non-root operation. This structure is manipulated by VMCLEAR, VMPTRLD, VMREAD, and VMWRITE instructions.

VMX Life cycle

The above picture illustrates the lifecycle of VMX operation in the VMCS Region.

Initializing VMCS Region

A VMM should use different VMCS Regions, so we need to set logical processor affinity and run our initialization routine multiple times.

The location where the VMCS located is called the “VMCS Region”.

VMCS Region is a

  • 4 Kbyte (bits 11:0 must be zero)
  • Must be aligned to the 4KB boundary

This pointer must not set bits beyond the processor’s physical-address width (we can determine a processor’s physical-address width by executing CPUID with 80000008H in EAX. The physical-address width is returned in bits 7:0 of EAX.)

There might be several VMCSs simultaneously in a processor, but just one of them is currently active, and the VMLAUNCH, VMREAD, VMRESUME, and VMWRITE instructions operate only on the current VMCS.

Using VMPTRLD sets the current VMCS on a logical processor.

The memory operand of the VMCLEAR instruction is also the address of a VMCS. After executing the instruction, VMCS is neither active nor current on the logical processor. If the VMCS had been current on the logical processor, the logical processor no longer has a current VMCS.

VMPTRST is responsible to give the current VMCS pointer it stores the value FFFFFFFFFFFFFFFFH if there is no current VMCS.

The launching state of a VMCS determines which VM-entry instruction should be used with that VMCS. The VMLAUNCH instruction requires a VMCS whose launch state is “clear”; the VMRESUME instruction requires a VMCS whose launch state is “launched”. A logical processor maintains a VMCS’s launch state in the corresponding VMCS region.

If the launch state of the current VMCS is “clear”, successful execution of the VMLAUNCH instruction changes the launch state to “launched”.

The memory operand of the VMCLEAR instruction is the address of a VMCS. After execution of the instruction, the launch state of that VMCS is “clear”.

There are no other ways to modify the launch state of a VMCS (it cannot be modified using VMWRITE), and there is no direct way to discover it (it cannot be read using VMREAD).

The following picture illustrates the contents of a VMCS Region.

VMCS Region

The following code is responsible for allocating VMCS Region :

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
+
BOOLEAN
+AllocateVmcsRegion(IN VIRTUAL_MACHINE_STATE * GuestState)
+{
+    //
+    // at IRQL > DISPATCH_LEVEL memory allocation routines don't work
+    //
+    if (KeGetCurrentIrql() > DISPATCH_LEVEL)
+        KeRaiseIrqlToDpcLevel();
+
+    PHYSICAL_ADDRESS PhysicalMax = {0};
+    PhysicalMax.QuadPart         = MAXULONG64;
+
+    int    VMCSSize = 2 * VMCS_SIZE;
+    BYTE * Buffer   = MmAllocateContiguousMemory(VMCSSize + ALIGNMENT_PAGE_SIZE, PhysicalMax); // Allocating a 4-KByte Contigous Memory region
+
+    PHYSICAL_ADDRESS Highest = {0}, Lowest = {0};
+    Highest.QuadPart = ~0;
+
+    // BYTE* Buffer = MmAllocateContiguousMemorySpecifyCache(VMXONSize + ALIGNMENT_PAGE_SIZE, Lowest, Highest, Lowest, MmNonCached);
+
+    UINT64 PhysicalBuffer = VirtualToPhysicalAddress(Buffer);
+    if (Buffer == NULL)
+    {
+        DbgPrint("[*] Error : Couldn't Allocate Buffer for VMCS Region.");
+        return FALSE; // ntStatus = STATUS_INSUFFICIENT_RESOURCES;
+    }
+    // zero-out memory
+    RtlSecureZeroMemory(Buffer, VMCSSize + ALIGNMENT_PAGE_SIZE);
+    UINT64 AlignedPhysicalBuffer = (BYTE *)((ULONG_PTR)(PhysicalBuffer + ALIGNMENT_PAGE_SIZE - 1) & ~(ALIGNMENT_PAGE_SIZE - 1));
+
+    UINT64 AlignedVirtualBuffer = (BYTE *)((ULONG_PTR)(Buffer + ALIGNMENT_PAGE_SIZE - 1) & ~(ALIGNMENT_PAGE_SIZE - 1));
+
+    DbgPrint("[*] Virtual allocated buffer for VMCS at %llx", Buffer);
+    DbgPrint("[*] Virtual aligned allocated buffer for VMCS at %llx", AlignedVirtualBuffer);
+    DbgPrint("[*] Aligned physical buffer allocated for VMCS at %llx", AlignedPhysicalBuffer);
+
+    // get IA32_VMX_BASIC_MSR RevisionId
+
+    IA32_VMX_BASIC_MSR basic = {0};
+
+    basic.All = __readmsr(MSR_IA32_VMX_BASIC);
+
+    DbgPrint("[*] MSR_IA32_VMX_BASIC (MSR 0x480) Revision Identifier %llx", basic.Fields.RevisionIdentifier);
+
+    // Changing Revision Identifier
+    *(UINT64 *)AlignedVirtualBuffer = basic.Fields.RevisionIdentifier;
+
+    int Status = __vmx_vmptrld(&AlignedPhysicalBuffer);
+    if (Status)
+    {
+        DbgPrint("[*] VMCS failed with status %d\n", Status);
+        return FALSE;
+    }
+
+    g_GuestState->VmcsRegion = AlignedPhysicalBuffer;
+
+    return TRUE;
+}
+

The above code is exactly the same as VMXON Region except for __vmx_vmptrld instead of __vmx_on, __vmx_vmptrld is the intrinsic function for VMPTRLD instruction.

In VMCS, we should find the Revision Identifier from MSR_IA32_VMX_BASIC and write it in VMCS Region before executing VMPTRLD.

The MSR_IA32_VMX_BASIC is defined as below.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
typedef union _IA32_VMX_BASIC_MSR
+{
+    ULONG64 All;
+    struct
+    {
+        ULONG32 RevisionIdentifier : 31;   // [0-30]
+        ULONG32 Reserved1 : 1;             // [31]
+        ULONG32 RegionSize : 12;           // [32-43]
+        ULONG32 RegionClear : 1;           // [44]
+        ULONG32 Reserved2 : 3;             // [45-47]
+        ULONG32 SupportedIA64 : 1;         // [48]
+        ULONG32 SupportedDualMoniter : 1;  // [49]
+        ULONG32 MemoryType : 4;            // [50-53]
+        ULONG32 VmExitReport : 1;          // [54]
+        ULONG32 VmxCapabilityHint : 1;     // [55]
+        ULONG32 Reserved3 : 8;             // [56-63]
+    } Fields;
+} IA32_VMX_BASIC_MSR, *PIA32_VMX_BASIC_MSR;
+

VMXOFF Instruction

After configuring the above regions, now it’s time to think about DrvClose when the user-mode application no longer maintains the handle to the driver. At this time, we should terminate VMX and free every memory that we allocated before.

The following function is responsible for executing VMXOFF and then calling MmFreeContiguousMemory to free the allocated memory :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+
VOID
+TerminateVmx()
+{
+    DbgPrint("\n[*] Terminating VMX...\n");
+
+    KAFFINITY AffinityMask;
+    for (size_t i = 0; i < ProcessorCounts; i++)
+    {
+        AffinityMask = MathPower(2, i);
+        KeSetSystemAffinityThread(AffinityMask);
+        DbgPrint("\t\tCurrent thread is executing in %d th logical processor.", i);
+
+        __vmx_off();
+        MmFreeContiguousMemory(PhysicalToVirtualAddress(g_GuestState[i].VmxonRegion));
+        MmFreeContiguousMemory(PhysicalToVirtualAddress(g_GuestState[i].VmcsRegion));
+    }
+
+    DbgPrint("[*] VMX Operation turned off successfully. \n");
+}
+

Remember to convert VMXON and VMCS Regions to virtual addresses because MmFreeContiguousMemory accepts virtual addresses; otherwise, it leads to a BSOD.

Ok, It’s almost done!

Testing VMM

Let’s create a test case for our code, first a function for initiating VMXON and VMCS Regions through all logical processors.

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
+
VIRTUAL_MACHINE_STATE * g_GuestState;
+int                     ProcessorCounts;
+
+BOOLEAN
+InitializeVmx()
+{
+    if (!IsVmxSupported())
+    {
+        DbgPrint("[*] VMX is not supported in this machine !");
+        return FALSE;
+    }
+
+    ProcessorCounts = KeQueryActiveProcessorCount(0);
+    g_GuestState    = ExAllocatePoolWithTag(NonPagedPool,
+                                         sizeof(VIRTUAL_MACHINE_STATE) * ProcessorCounts,
+                                         POOLTAG);
+
+    DbgPrint("\n=====================================================\n");
+
+    KAFFINITY AffinityMask;
+    for (size_t i = 0; i < ProcessorCounts; i++)
+    {
+        AffinityMask = MathPower(2, i);
+
+        KeSetSystemAffinityThread(AffinityMask);
+
+        DbgPrint("\t\tCurrent thread is executing in %d th logical processor.", i);
+
+        //
+        // Enabling VMX Operation
+        //
+        AsmEnableVmxOperation();
+
+        DbgPrint("[*] VMX Operation Enabled Successfully !");
+
+        AllocateVmxonRegion(&g_GuestState[i]);
+        AllocateVmcsRegion(&g_GuestState[i]);
+
+        DbgPrint("[*] VMCS Region is allocated at  ===============> %llx", g_GuestState[i].VmcsRegion);
+        DbgPrint("[*] VMXON Region is allocated at ===============> %llx", g_GuestState[i].VmxonRegion);
+
+        DbgPrint("\n=====================================================\n");
+    }
+
+    return TRUE;
+}
+

The above function should be called from IRP MJ CREATE, so let’s modify our DrvCreate to :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+
NTSTATUS DrvCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+{
+
+    DbgPrint("[*] DrvCreate Called !");
+
+    if (InitializeVmx()) {
+        DbgPrint("[*] VMX Initiated Successfully.");
+    }
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+    Irp->IoStatus.Information = 0;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_SUCCESS;
+}
+

And modify DrvClose to :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
NTSTATUS
+DrvClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+{
+    DbgPrint("[*] DrvClose Called !");
+
+    //
+    // executing VMXOFF on every logical processor
+    //
+    TerminateVmx();
+
+    Irp->IoStatus.Status      = STATUS_SUCCESS;
+    Irp->IoStatus.Information = 0;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_SUCCESS;
+}
+

Now, run the code. In the case of creating the handle (You can see that our regions were allocated successfully).

VMX Regions

And when we call CloseHandle from user mode:

VMXOFF

Conclusion

In this part, we learned about different types of IOCTL Dispatching. We saw different functions in Windows to manage our VMM, and we initialized the VMXON Regions and VMCS Regions, then terminated them.

In the future, we’ll focus on Exteneded Page Table (EPT), VMCS, and different actions that can be performed in VMCS Regions to control our guest software.

The fourth part is also available [here].(https://rayanfam.com/topics/hypervisor-from-scratch-part-4/).

Note: Remember that hypervisors change over time because new features are added to the operating systems or new technologies are used. For example, updates to Meltdown & Spectre have made a lot of changes to the hypervisors. So, if you want to use Hypervisor From Scratch in your projects, research, or whatever, you should use the HyperDbg drivers. HyperDbg is actively maintained, stable, and reliable, ensuring you avoid the errors and instability problems that can arise from using older parts of the tutorial series.

References

[1] Intel® 64 and IA-32 architectures software developer’s manual combined volumes 3 (https://software.intel.com/en-us/articles/intel-sdm)

[2] Windows Driver Samples (https://github.com/Microsoft/Windows-driver-samples)

[3] Driver Development Part 2: Introduction to Implementing IOCTLs (https://www.codeproject.com/Articles/9575/Driver-Development-Part-2-Introduction-to-Implemen)

[3] Hyperplatform (https://github.com/tandasat/HyperPlatform)

[4] PAGED_CODE macro (https://technet.microsoft.com/en-us/ff558773(v=vs.96))

[5] HVPP (https://github.com/wbenny/hvpp)

[6] HyperBone Project (https://github.com/DarthTon/HyperBone)

[7] Memory Caching Types (https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ne-wdm-_memory_caching_type)

[8] What is writeback cache? (https://whatis.techtarget.com/definition/write-back)

[9] Buffer Descriptions for I/O Control Codes (https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/buffer-descriptions-for-i-o-control-codes)

[10] Defining I/O Control Codes (https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/defining-i-o-control-codes)

This post is licensed under CC BY 4.0 by the author.

Using Intel's Streaming SIMD Extensions 3 (MONITOR MWAIT) As A Kernel Debugging Trick

Hypervisor From Scratch – Part 4: Address Translation Using Extended Page Table (EPT)

Comments powered by Disqus.

diff --git a/topics/hypervisor-from-scratch-part-4/index.html b/topics/hypervisor-from-scratch-part-4/index.html new file mode 100644 index 00000000..4556601a --- /dev/null +++ b/topics/hypervisor-from-scratch-part-4/index.html @@ -0,0 +1,441 @@ + Hypervisor From Scratch – Part 4: Address Translation Using Extended Page Table (EPT) | Rayanfam Blog
Home Hypervisor From Scratch – Part 4: Address Translation Using Extended Page Table (EPT)
Post
Cancel

Hypervisor From Scratch – Part 4: Address Translation Using Extended Page Table (EPT)

If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous practical examples on how to utilize hypervisors for reverse engineering.

Introduction

Welcome to the 4th part of the “Hypervisor From Scratch”. This part primarily involves translating guest addresses through Extended Page Table (EPT) and its implementation. We also see how shadow tables work and basic concepts about EPT.

Table of Contents

  • Introduction
  • Table of Contents
  • Overview
  • Second Level Address Translation (SLAT)
  • Software-assisted paging (Shadow Page Tables)
  • Hardware-assisted paging (Extended Page Table)
  • Extended Page Table vs. Shadow Page Table
  • Detecting Support for EPT, NPT
  • EPT Translation
    • Implementing Extended Page Table (EPT)
    • Accessed and Dirty Flags in EPTP
  • 5-Level EPT Translation
  • Conclusion
  • References

Overview

First of all, make sure to read the earlier parts before reading this topic, as these parts depend on each other. It would help if you also had a basic understanding of the paging mechanism and how page tables work. A good article is here for paging tables.

Most of this topic is derived from Chapter 28 - (VMX SUPPORT FOR ADDRESS TRANSLATION) available at Intel 64 and IA-32 architectures software developer’s manual combined volumes 3.

The full source code of this tutorial is available on GitHub :

[https://github.com/SinaKarvandi/Hypervisor-From-Scratch]

Before starting, I should give my thanks to Petr Beneš, as this part would never have been completed without his help.

Note: This part tends to give you basic information about EPT. The main implementation of EPT for our hypervisor is explained in part 7. In part 7, we used the concept we learned here to implement EPT on an already virtualized system.

Note: Remember that hypervisors change over time because new features are added to the operating systems or new technologies are used. For example, updates to Meltdown & Spectre have made a lot of changes to the hypervisors. So, if you want to use Hypervisor From Scratch in your projects, research, or whatever, you should use the HyperDbg drivers. HyperDbg is actively maintained, stable, and reliable, ensuring you avoid the errors and instability problems that can arise from using older parts of the tutorial series.

Second Level Address Translation (SLAT)

Second Level Address Translation (SLAT) or nested paging is an extended layer in the paging mechanism used to map hardware-based virtualization virtual addresses into the physical memory.

AMD implemented SLAT through the Rapid Virtualization Indexing (RVI) technology known as Nested Page Tables (NPT) since the introduction of its third-generation Opteron processors and microarchitecture code name Barcelona. Intel also implemented SLAT in Intel VT-x technologies since the introduction of microarchitecture code name Nehalem and it’s known as Extended Page Table (EPT) and is used in Core i9, Core i7, Core i5, and Core i3 processors.

ARM processors also have an implementation known as Stage-2 page-tables.

There are two methods for implementing SLAT. The first one is Shadow Page Tables, and the second one is Extended Page Tables.

Software-assisted paging (Shadow Page Tables)

The hypervisor uses Shadow Page Tables to keep track of the state of physical memory in which the guest thinks that it has access to physical memory, but in the real world, the hardware prevents it from accessing hardware memory.

Without this prevention, the guest might control the host, which is not what is intended.

In this case, VMM maintains Shadow Page Tables that map guest virtual pages directly to machine pages.

By the way, using Shadow Page Table is not recommended today as it always leads to VMM traps (which result in a vast amount of VM-exits) and losses the performance due to the TLB flush on every switch. Another caveat is that there is a memory overhead due to shadow copying of guest page tables.

Hardware-assisted paging (Extended Page Table)

To reduce the complexity of Shadow Page Tables, avoid the excessive VM-exits, and reduce the number of TLB flushes, EPT implemented a hardware-assisted paging strategy to increase performance.

According to a VMware evaluation paper: “EPT provides performance gains of up to 48% for MMU-intensive benchmarks and up to 600% for MMU-intensive microbenchmarks”.

EPT implemented one more page table hierarchy to map guest virtual address to guest physical address, which is valid in the main memory.

In EPT,

  • One page table is maintained by guest OS, which is used to generate the guest’s physical address.
  • The other page table is maintained by VMM, which maps the guest’s physical address to the host’s physical address.

So for each memory access operation, EPT MMU directly gets the guest’s physical address from the guest page table and then automatically gets the host’s physical address from the VMM mapping table.

Extended Page Table vs. Shadow Page Table

EPT:

  • Walk any requested address
    • Appropriate to programs that have a large amount of page table miss when executing
    • Less chance to exit VM (less context switch)
  • Two-layer EPT
    • Means each access needs to walk two tables
  • Easier to develop
    • Many particular registers
    • Hardware helps guest OS to notify the VMM

SPT:

  • Only walk when SPT entry miss
    • Appropriate to programs that would access only some addresses frequently
    • Every access might be intercepted by VMM (many traps)
  • One reference
    • Fast and convenient when page hit
  • Hard to develop
    • Two-layer structure
    • Complicated reverse map
    • Permission emulation

Detecting Support for EPT, NPT

If you want to see whether your system supports EPT on Intel processor or NPT on AMD processor without using assembly (CPUID), you can download coreinfo.exe from SysInternals, then run it. The last line will show you if your processor supports EPT or NPT.

EPT Translation

EPT defines a layer of address translation that augments the translation of linear addresses.

The extended page-table mechanism (EPT) is a feature that can be used to support the virtualization of physical memory. When EPT is in use, certain addresses that would normally be treated as physical addresses (and used to access memory) are instead treated as guest physical addresses. Guest physical addresses are translated by traversing a set of EPT paging structures to produce physical addresses that are used to access memory.

EPT is used when the “enable EPT” VM-execution control is 1. It translates the guest physical addresses used in VMX non-root operation and those used by VM entry for event injection.

EPT translation is exactly like regular paging translation but with some minor differences. In paging, the processor translates a virtual address to a physical address, while in EPT translation, we want to translate a guest’s physical address to a host’s physical address.

If you’re familiar with paging, the 3rd control register (CR3) is the base address of the PML4 table (in an x64 processor or, more generally, it points to the root paging directory). In EPT guest is not aware of EPT translation, so it has CR3 too, but this CR3 is used to convert the guest’s virtual address to the guest’s physical address. Whenever we find our target (the guest’s physical address), the EPT mechanism treats our guest’s physical address like a virtual address and converts it to the host’s physical address. In this mechanism, EPTP is like CR3 but for EPT.

Just think about the above sentence one more time!

So your target physical address should be divided into four parts. The first 9 bits point to EPT PML4E (note that the PML4 base address is in EPTP). The second 9 bits indicate the EPT PDPT Entry (the base address of PDPT comes from EPT PML4E), the third 9 bits point to EPT PD Entry (the base address of PD comes from EPT PDPTE), and the last 9 bits of the guest physical address point to an entry in EPT PT table (the base address of PT comes from EPT PDE) and now the EPT PT Entry points to the host physical address of the corresponding page.

EPT Translation

You might ask, as a simple Virtual to Physical Address translation involves accessing four physical addresses, so what happens?

The answer is the processor internally translates all tables’ physical addresses one by one; that’s why paging and accessing memory in a guest software is slower than hardware address translation. The following picture illustrates the operations for a guest’s virtual address to the host’s physical address.

If you want to think about x86 EPT virtualization, assume, for example, that CR4.PAE = CR4.PSE = 0. The translation of a 32-bit linear address then operates as follows:

  • Bits 31:22 of the linear address select an entry in the guest page directory located at the guest physical address in CR3. The guest’s physical address of the guest page-directory entry (PDE) is translated through EPT to determine the guest PDE’s physical address.
  • Bits 21:12 of the linear address select an entry in the guest page table located at the guest’s physical address in the guest PDE. The guest physical address of the guest page-table entry (PTE) is translated through EPT to determine the guest PTE’s physical address.
  • Bits 11:0 of the linear address is the offset in the page frame located at the guest’s physical address in the guest PTE. The guest’s physical address determined by this offset is translated through EPT to select the physical address to which the original linear address translates.

Note that PAE stands for Physical Address Extension, which is a memory management feature for the x86 architecture that extends the address space, and PSE stands for Page Size Extension that refers to a feature of x86 processors that allows for pages larger than the standard 4 KiB size.

In addition to translating a guest’s physical address to a host’s physical address, EPT specifies the privileges that software is allowed when accessing the address. Attempts at disallowed accesses are called EPT violations and cause VM-exits.

Remember that an address will not translate through EPT when there is no read/write access.

Implementing Extended Page Table (EPT)

Now that we know some basics, let’s implement what we’ve learned before. Based on the Intel manual, we should write (VMWRITE) EPTP or Extended-Page-Table Pointer to the VMCS. The EPTP structure is described below.

Extended-Page-Table Pointer

The above tables can be described using the following structure:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
// See Table 24-8. Format of Extended-Page-Table Pointer
+typedef union _EPTP {
+    ULONG64 All;
+    struct {
+        UINT64 MemoryType : 3; // bit 2:0 (0 = Uncacheable (UC) - 6 = Write - back(WB))
+        UINT64 PageWalkLength : 3; // bit 5:3 (This value is 1 less than the EPT page-walk length) 
+        UINT64 DirtyAndAceessEnabled : 1; // bit 6  (Setting this control to 1 enables accessed and dirty flags for EPT)
+        UINT64 Reserved1 : 5; // bit 11:7 
+        UINT64 PML4Address : 36;
+        UINT64 Reserved2 : 16;
+    }Fields;
+}EPTP, *PEPTP;
+

Like the regular paging mechanism, each entry in all EPT tables is 64-bit long. EPT PML4E, EPT PDPTE, and EPT PD are the same, but EPT PTE has some minor differences.

An EPT entry is something like this:

EPT Entries

Ok, Now we should implement tables; the first table is PML4. The following table shows the format of an EPT PML4 Entry (PML4E).

EPT PML4E

PML4E is a structure like this :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
// See Table 28-1. 
+typedef union _EPT_PML4E {
+    ULONG64 All;
+    struct {
+        UINT64 Read : 1; // bit 0
+        UINT64 Write : 1; // bit 1
+        UINT64 Execute : 1; // bit 2
+        UINT64 Reserved1 : 5; // bit 7:3 (Must be Zero)
+        UINT64 Accessed : 1; // bit 8
+        UINT64 Ignored1 : 1; // bit 9
+        UINT64 ExecuteForUserMode : 1; // bit 10
+        UINT64 Ignored2 : 1; // bit 11
+        UINT64 PhysicalAddress : 36; // bit (N-1):12 or Page-Frame-Number
+        UINT64 Reserved2 : 4; // bit 51:N
+        UINT64 Ignored3 : 12; // bit 63:52
+    }Fields;
+}EPT_PML4E, *PEPT_PML4E;
+

As long as we use 4-level paging, the second table is EPT Page-Directory-Pointer-Table (PDTP). The following picture illustrates the format of PDPTE:

EPT PDPTE

PDPTE’s structure is like this:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
// See Table 28-3
+typedef union _EPT_PDPTE {
+    ULONG64 All;
+    struct {
+        UINT64 Read : 1; // bit 0
+        UINT64 Write : 1; // bit 1
+        UINT64 Execute : 1; // bit 2
+        UINT64 Reserved1 : 5; // bit 7:3 (Must be Zero)
+        UINT64 Accessed : 1; // bit 8
+        UINT64 Ignored1 : 1; // bit 9
+        UINT64 ExecuteForUserMode : 1; // bit 10
+        UINT64 Ignored2 : 1; // bit 11
+        UINT64 PhysicalAddress : 36; // bit (N-1):12 or Page-Frame-Number
+        UINT64 Reserved2 : 4; // bit 51:N
+        UINT64 Ignored3 : 12; // bit 63:52
+    }Fields;
+}EPT_PDPTE, *PEPT_PDPTE;
+

For the third table of paging, we should implement an EPT Page-Directory Entry (PDE) as described below:

EPT PDE

PDE’s structure:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
// See Table 28-5
+typedef union _EPT_PDE {
+    ULONG64 All;
+    struct {
+        UINT64 Read : 1; // bit 0
+        UINT64 Write : 1; // bit 1
+        UINT64 Execute : 1; // bit 2
+        UINT64 Reserved1 : 5; // bit 7:3 (Must be Zero)
+        UINT64 Accessed : 1; // bit 8
+        UINT64 Ignored1 : 1; // bit 9
+        UINT64 ExecuteForUserMode : 1; // bit 10
+        UINT64 Ignored2 : 1; // bit 11
+        UINT64 PhysicalAddress : 36; // bit (N-1):12 or Page-Frame-Number
+        UINT64 Reserved2 : 4; // bit 51:N
+        UINT64 Ignored3 : 12; // bit 63:52
+    }Fields;
+}EPT_PDE, *PEPT_PDE;
+

The last page is EPT which is described below.

EPT PTE

PTE will be :

Note that we have EPTMemoryType, IgnorePAT, DirtyFlag, and SuppressVE in addition to the above pages.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
// See Table 28-6
+typedef union _EPT_PTE {
+    ULONG64 All;
+    struct {
+        UINT64 Read : 1; // bit 0
+        UINT64 Write : 1; // bit 1
+        UINT64 Execute : 1; // bit 2
+        UINT64 EPTMemoryType : 3; // bit 5:3 (EPT Memory type)
+        UINT64 IgnorePAT : 1; // bit 6
+        UINT64 Ignored1 : 1; // bit 7
+        UINT64 AccessedFlag : 1; // bit 8   
+        UINT64 DirtyFlag : 1; // bit 9
+        UINT64 ExecuteForUserMode : 1; // bit 10
+        UINT64 Ignored2 : 1; // bit 11
+        UINT64 PhysicalAddress : 36; // bit (N-1):12 or Page-Frame-Number
+        UINT64 Reserved : 4; // bit 51:N
+        UINT64 Ignored3 : 11; // bit 62:52
+        UINT64 SuppressVE : 1; // bit 63
+    }Fields;
+}EPT_PTE, *PEPT_PTE;
+

There are other types of implementing page walks (2 or 3 level paging), and if you set the 7th bit of PDPTE (Maps 1 GB) or the 7th bit of PDE (Maps 2 MB) so instead of implementing 4-level paging (like what we want to do for the rest of the topic) we set those bits but keep in mind that the corresponding tables are different. These tables are described in (Table 28-4. Format of an EPT Page-Directory Entry (PDE) that Maps a 2-MByte Page) and (Table 28-2. Format of an EPT Page-Directory-Pointer-Table Entry (PDPTE) that Maps a 1-GByte Page). SimpleVisor is an example of this implementation.

An important note is almost all the above structures have a 36-bit Physical Address which means our hypervisor supports only 4-level paging. It is because every page table (and every EPT Page Table) consists of 512 entries which means you need 9 bits to select an entry, and as long as we have 4 level tables, we can’t use more than 36 (4 * 9) bits. Another method with a wider address range is not implemented in all major OS like Windows or Linux. I’ll describe EPT PML5E briefly later in this topic, but we don’t implement it in our hypervisor as it’s not widespread yet!

By the way, N is the physical address width supported by the processor. CPUID with 80000008H in EAX gives you the supported width in EAX bits 7:0.

Let’s see the rest of the code. The following code is the InitializeEptp function which is responsible for allocating and mapping EPTP.

Note that the PAGED_CODE() macro ensures that the calling thread runs at an IRQL low enough to permit paging.

1
+2
+3
+4
+5
+
UINT64
+InitializeEptp()
+{
+    PAGED_CODE();
+        ...
+

First of all, allocate EPTP and put zeros on it.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
    //
+    // Allocate EPTP
+    //
+    PEPTP EPTPointer = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, POOLTAG);
+
+    if (!EPTPointer)
+    {
+        return NULL;
+    }
+    RtlZeroMemory(EPTPointer, PAGE_SIZE);
+

Now, we need a blank page for our EPT PML4 Table.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
    //
+    //  Allocate EPT PML4
+    //
+    PEPT_PML4E EptPml4 = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, POOLTAG);
+    if (!EptPml4)
+    {
+        ExFreePoolWithTag(EPTPointer, POOLTAG);
+        return NULL;
+    }
+    RtlZeroMemory(EptPml4, PAGE_SIZE);
+

And another empty page for PDPT.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
    //
+    //  Allocate EPT Page-Directory-Pointer-Table
+    //
+    PEPT_PDPTE EptPdpt = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, POOLTAG);
+    if (!EptPdpt)
+    {
+        ExFreePoolWithTag(EptPml4, POOLTAG);
+        ExFreePoolWithTag(EPTPointer, POOLTAG);
+        return NULL;
+    }
+    RtlZeroMemory(EptPdpt, PAGE_SIZE);
+

Of course, it’s true about Page Directory Table.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
    //
+    //  Allocate EPT Page-Directory
+    //
+    PEPT_PDE EptPd = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, POOLTAG);
+
+    if (!EptPd)
+    {
+        ExFreePoolWithTag(EptPdpt, POOLTAG);
+        ExFreePoolWithTag(EptPml4, POOLTAG);
+        ExFreePoolWithTag(EPTPointer, POOLTAG);
+        return NULL;
+    }
+    RtlZeroMemory(EptPd, PAGE_SIZE);
+

The last table is a blank page for EPT Page Table.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
    //
+    //  Allocate EPT Page-Table
+    //
+    PEPT_PTE EptPt = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, POOLTAG);
+
+    if (!EptPt)
+    {
+        ExFreePoolWithTag(EptPd, POOLTAG);
+        ExFreePoolWithTag(EptPdpt, POOLTAG);
+        ExFreePoolWithTag(EptPml4, POOLTAG);
+        ExFreePoolWithTag(EPTPointer, POOLTAG);
+        return NULL;
+    }
+    RtlZeroMemory(EptPt, PAGE_SIZE);
+

Now that we have all of our pages available, let’s allocate two pages (2*4096) continuously because we need one of the pages for our RIP register to start and one page for our stack (RSP register). After that, we need two EPT Page Table Entries (PTEs) with permission to execute, read, and write. The physical address should be divided by 4096 (PAGE_SIZE) because if we dived a hex number by 4096 (0x1000), 12 digits from the right (which are zeros) would disappear, and these 12 digits are for choosing between 4096 bytes.

By the way, we let stack be executable. That’s because, in a regular VM, we should put RWX on all pages. After all, it’s the responsibility of internal page tables to set or clear NX bit. We need to change them from EPT tables for special purposes (e.g., intercepting instruction fetch for a special page). Changing from EPT tables will lead to EPT-Violation; this way, we can intercept these events.

The actual need is two pages, but we need to build page tables inside our guest software; thus, we allocate up to 10 pages.

I’ll explain about intercepting pages from EPT later in this series.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
    //
+    // Setup PT by allocating two pages Continuously
+    // We allocate two pages because we need 1 page for our RIP to start and 1 page for RSP 1 + 1 = 2
+    //
+    const int PagesToAllocate = 10;
+    UINT64    GuestMemory     = ExAllocatePoolWithTag(NonPagedPool, PagesToAllocate * PAGE_SIZE, POOLTAG);
+    RtlZeroMemory(GuestMemory, PagesToAllocate * PAGE_SIZE);
+
+    for (size_t i = 0; i < PagesToAllocate; i++)
+    {
+        EptPt[i].Fields.AccessedFlag       = 0;
+        EptPt[i].Fields.DirtyFlag          = 0;
+        EptPt[i].Fields.EPTMemoryType      = 6;
+        EptPt[i].Fields.Execute            = 1;
+        EptPt[i].Fields.ExecuteForUserMode = 0;
+        EptPt[i].Fields.IgnorePAT          = 0;
+        EptPt[i].Fields.PhysicalAddress    = (VirtualToPhysicalAddress(GuestMemory + (i * PAGE_SIZE)) / PAGE_SIZE);
+        EptPt[i].Fields.Read               = 1;
+        EptPt[i].Fields.SuppressVE         = 0;
+        EptPt[i].Fields.Write              = 1;
+    }
+

Note: EPTMemoryType can be either 0 (for uncached memory) or 6 (writeback) memory, and as we want our memory to be cacheable, so put 6 on it.

The next table is PDE. PDE should point to the PTE base address, so we just put the address of the first entry from the EPT PTE as the physical address for Page Directory Entry.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
    //
+    // Setting up PDE
+    //
+    EptPd->Fields.Accessed           = 0;
+    EptPd->Fields.Execute            = 1;
+    EptPd->Fields.ExecuteForUserMode = 0;
+    EptPd->Fields.Ignored1           = 0;
+    EptPd->Fields.Ignored2           = 0;
+    EptPd->Fields.Ignored3           = 0;
+    EptPd->Fields.PhysicalAddress    = (VirtualToPhysicalAddress(EptPt) / PAGE_SIZE);
+    EptPd->Fields.Read               = 1;
+    EptPd->Fields.Reserved1          = 0;
+    EptPd->Fields.Reserved2          = 0;
+    EptPd->Fields.Write              = 1;
+

The next step is mapping PDPT. PDPT Entry should point to the first entry of Page-Directory.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
    //
+    // Setting up PDPTE
+    //
+    EptPdpt->Fields.Accessed           = 0;
+    EptPdpt->Fields.Execute            = 1;
+    EptPdpt->Fields.ExecuteForUserMode = 0;
+    EptPdpt->Fields.Ignored1           = 0;
+    EptPdpt->Fields.Ignored2           = 0;
+    EptPdpt->Fields.Ignored3           = 0;
+    EptPdpt->Fields.PhysicalAddress    = (VirtualToPhysicalAddress(EptPd) / PAGE_SIZE);
+    EptPdpt->Fields.Read               = 1;
+    EptPdpt->Fields.Reserved1          = 0;
+    EptPdpt->Fields.Reserved2          = 0;
+    EptPdpt->Fields.Write              = 1;
+

The last step is configuring PML4E, which points to the first entry of the PTPT.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
    //
+    // Setting up PML4E
+    //
+    EptPml4->Fields.Accessed           = 0;
+    EptPml4->Fields.Execute            = 1;
+    EptPml4->Fields.ExecuteForUserMode = 0;
+    EptPml4->Fields.Ignored1           = 0;
+    EptPml4->Fields.Ignored2           = 0;
+    EptPml4->Fields.Ignored3           = 0;
+    EptPml4->Fields.PhysicalAddress    = (VirtualToPhysicalAddress(EptPdpt) / PAGE_SIZE);
+    EptPml4->Fields.Read               = 1;
+    EptPml4->Fields.Reserved1          = 0;
+    EptPml4->Fields.Reserved2          = 0;
+    EptPml4->Fields.Write              = 1;
+

We’ve almost done! Just set up the EPTP for our VMCS by putting 0x6 as the memory type (which is writeback), and we walk four times, so the page walk length is 4-1=3, and the PML4 address is the physical address of the first entry in the PML4 table.

I’ll explain the DirtyAndAcessEnabled field later in this topic.

1
+2
+3
+4
+5
+6
+7
+8
+9
+
    //
+    // Setting up EPTP
+    //
+    EPTPointer->Fields.DirtyAndAceessEnabled = 1;
+    EPTPointer->Fields.MemoryType            = 6; // 6 = Write-back (WB)
+    EPTPointer->Fields.PageWalkLength        = 3; // 4 (tables walked) - 1 = 3
+    EPTPointer->Fields.PML4Address           = (VirtualToPhysicalAddress(EptPml4) / PAGE_SIZE);
+    EPTPointer->Fields.Reserved1             = 0;
+    EPTPointer->Fields.Reserved2             = 0;
+

And the last step.

1
+2
+
    DbgPrint("[*] Extended Page Table Pointer allocated at %llx", EPTPointer);
+    return EPTPointer;
+

All the above page tables should be aligned to 4KByte boundaries, but as long as we allocate >= PAGE_SIZE (One PFN record) so it’s automatically 4kb-aligned.

Our implementation consists of 4 tables; therefore, the full layout is like this:

EPT Layout

Accessed and Dirty Flags in EPTP

In EPTP, we’ll decide whether enable accessed and dirty flags for EPT or not using the 6th bit of the extended-page-table pointer (EPTP). Setting this flag causes processor accesses to guest paging structure entries to be treated as writes.

For any EPT paging-structure entry that is used during guest-physical-address translation, bit 8 is the accessed flag. For an EPT paging-structure entry that maps a page (as opposed to referencing another EPT paging structure), bit 9 is the dirty flag.

Whenever the processor uses an EPT paging-structure entry as part of the guest-physical-address translation, it sets the accessed flag in that entry (if it is not already set).

Whenever there is a write to a guest physical address, the processor sets the dirty flag (if it is not already set) in the EPT paging-structure entry that identifies the final physical address for the guest’s physical address (either an EPT PTE or an EPT paging-structure entry in which bit 7 is 1).

These flags are “sticky”, meaning that, once set, the processor does not clear them; only software can clear them.

5-Level EPT Translation

Intel suggests a new table in translation hierarchy, called PML5 which extends the EPT into a 5-layer table, and guest operating systems can use up to 57 bits for the virtual addresses, while the classic 4-level EPT is limited to translating 48-bit guest physical addresses. None of the modern OSs use this feature yet.

PML5 is also applied to both EPT and regular paging mechanisms.

Translation begins by identifying a 4-KByte naturally aligned EPT PML5 table. It is located at the physical address specified in bits 51:12 of EPTP. An EPT PML5 table comprises 512 64-bit entries (EPT PML5Es). An EPT PML5E is selected using the physical address defined as follows.

  • Bits 63:52 are all 0.
  • Bits 51:12 are from EPTP.
  • Bits 11:3 are bits 56:48 of the guest physical address.
  • Bits 2:0 are all 0.
  • Because an EPT PML5E is identified using bits 56:48 of the guest’s physical address, it controls access to a 256-TByte region of the linear address space.

The only difference is we should put PML5 physical address instead of the PML4 address in EPTP.

For more information about 5-layer paging, take a look at this Intel documentation.

Conclusion

In this part, we see how to initialize the Extended Page Table (EPT) and map the guest’s physical address to the host’s physical address; then, we build the EPTP based on the allocated addresses.

The future part would be about building the VMCS and implementing other VMX instructions and functionalities.

The fifth part is also available here.

Have a good time!

References

[1] Vol 3C - 28.2 THE EXTENDED PAGE TABLE MECHANISM (EPT) (https://software.intel.com/en-us/articles/intel-sdm)

[2] Performance Evaluation of Intel EPT Hardware Assist (https://www.vmware.com/pdf/Perf_ESX_Intel-EPT-eval.pdf)

[3] Second Level Address Translation (https://en.wikipedia.org/wiki/Second_Level_Address_Translation)

[4] Memory Virtualization (http://www.cs.nthu.edu.tw/~ychung/slides/Virtualization/VM-Lecture-2-2-SystemVirtualizationMemory.pptx)

[5] Best Practices for Paravirtualization Enhancements from Intel® Virtualization Technology: EPT and VT-d (https://software.intel.com/en-us/articles/best-practices-for-paravirtualization-enhancements-from-intel-virtualization-technology-ept-and-vt-d)

[6] 5-Level Paging and 5-Level EPT (https://software.intel.com/sites/default/files/managed/2b/80/5-level_paging_white_paper.pdf)

[7] Xen Summit November 2007 - Jun Nakajima (http://www-archive.xenproject.org/files/xensummit_fall07/12_JunNakajima.pdf)

[8] gipervizor against rutkitov: as it works (http://developers-club.com/posts/133906/)

[9] Intel SGX Explained (https://www.semanticscholar.org/paper/Intel-SGX-Explained-Costan-Devadas/2d7f3f4ca3fbb15ae04533456e5031e0d0dc845a)

[10] Intel VT-x (https://github.com/tnballo/notebook/wiki/Intel-VTx)

[11] Introduction to IA-32e hardware paging (https://www.triplefault.io/2017/07/introduction-to-ia-32e-hardware-paging.html)

This post is licensed under CC BY 4.0 by the author.

Hypervisor From Scratch – Part 3: Setting up Our First Virtual Machine

Start linux kernel module development!

Comments powered by Disqus.

diff --git a/topics/hypervisor-from-scratch-part-5/index.html b/topics/hypervisor-from-scratch-part-5/index.html new file mode 100644 index 00000000..2b5644b0 --- /dev/null +++ b/topics/hypervisor-from-scratch-part-5/index.html @@ -0,0 +1,1611 @@ + Hypervisor From Scratch – Part 5: Setting up VMCS & Running Guest Code | Rayanfam Blog
Home Hypervisor From Scratch – Part 5: Setting up VMCS & Running Guest Code
Post
Cancel

Hypervisor From Scratch – Part 5: Setting up VMCS & Running Guest Code

If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous practical examples on how to utilize hypervisors for reverse engineering.

Introduction

Hello and welcome to the fifth part of the “Hypervisor From Scratch” tutorial series. Today we will spend our time studying different parts of Virtual Machine Control Structure (VMCS), implementing additional VMX instructions, creating a restore point, setting different VMCS control structures, and at last, we execute VMLAUNCH and enter the hardware virtualization world!

Table of contents

  • Introduction
  • Table of contents
  • Overview
  • VMX Instructions
    • VMPTRST
    • VMCLEAR
    • VMPTRLD
  • Enhancing VM State Structure
  • Preparing to launch VM
  • Saving a return point
  • Returning to the previous state
  • VMLAUNCH Instruction
  • VMX Controls
    • VM-Execution Controls
    • VM-entry Control Bits
    • VM-exit Control Bits
    • PIN-Based Execution Control
  • Configuring VMCS
    • Gathering machine state for VMCS
    • Setting up VMCS
    • Checking VMCS layout
  • VM-Exit Handler
    • Resume to next instruction
  • VMRESUME Instruction
  • Let’s Test it!
  • Conclusion
  • References

Overview

Most of this topic is derived from Chapter 24 – (VIRTUAL MACHINE CONTROL STRUCTURES) & Chapter 26 – (VM ENTRIES) available at Intel 64 and IA-32 architectures software developer’s manual (Intel SDM) combined volumes 3.

This part is highly inspired by Hypervisor For Beginner.

Before reading the rest of this part, make sure to read the previous parts as it gives you the necessary knowledge to understand the rest of this topic thoroughly.

The full source code of this tutorial is available on GitHub :

[https://github.com/SinaKarvandi/Hypervisor-From-Scratch]

Note: Remember that hypervisors change over time because new features are added to the operating systems or new technologies are used. For example, updates to Meltdown & Spectre have made a lot of changes to the hypervisors. So, if you want to use Hypervisor From Scratch in your projects, research, or whatever, you should use the HyperDbg drivers. HyperDbg is actively maintained, stable, and reliable, ensuring you avoid the errors and instability problems that can arise from using older parts of the tutorial series.

VMX Instructions

In part 3, we implemented VMXOFF function now let’s implement other VMX instructions function. I also make some changes in calling VMXON and VMPTRLD functions to make it more modular.

VMPTRST

VMPTRST instruction stores the current-VMCS pointer into a specified memory address. The operand of this instruction is always 64 bits, and it’s always a location in memory.

The following function is the implementation of VMPTRST:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
UINT64
+VmptrstInstruction()
+{
+    PHYSICAL_ADDRESS vmcspa;
+    vmcspa.QuadPart = 0;
+    __vmx_vmptrst((unsigned __int64 *)&vmcspa);
+
+    DbgPrint("[*] VMPTRST %llx\n", vmcspa);
+
+    return 0;
+}
+

VMCLEAR

This instruction applies to the VMCS, where the VMCS region resides at the physical address contained in the instruction operand. The instruction ensures that VMCS data for that VMCS (some of these data may be currently maintained on the processor) are copied to the VMCS region in memory. It also initializes some parts of the VMCS region (for example, it sets the launch state of that VMCS to clear).

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
BOOLEAN
+ClearVmcsState(VIRTUAL_MACHINE_STATE * GuestState)
+{
+    // Clear the state of the VMCS to inactive
+    int status = __vmx_vmclear(&GuestState->VmcsRegion);
+
+    DbgPrint("[*] VMCS VMCLAEAR Status is : %d\n", status);
+    if (status)
+    {
+        // Otherwise, terminate the VMX
+        DbgPrint("[*] VMCS failed to clear with status %d\n", status);
+        __vmx_off();
+        return FALSE;
+    }
+    return TRUE;
+}
+

VMPTRLD

This instruction marks the current-VMCS pointer valid and loads it with the physical address in the instruction operand. The instruction fails if its operand is not properly aligned, sets unsupported physical-address bits, or is equal to the VMXON pointer. In addition, this instruction fails if the 32 bits in memory referenced by the operand do not match the VMCS revision identifier supported by the processor.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
BOOLEAN
+LoadVmcs(VIRTUAL_MACHINE_STATE * GuestState)
+{
+    int status = __vmx_vmptrld(&GuestState->VmcsRegion);
+    if (status)
+    {
+        DbgPrint("[*] VMCS failed with status %d\n", status);
+        return FALSE;
+    }
+    return TRUE;
+}
+

In order to implement VMRESUME, you need to know about some VMCS fields, so the explanation of the VMRESUME instruction is left after we implement VMLAUNCH. (Later in this topic)

Enhancing VM State Structure

As I told you earlier, we need a structure to save the state of our virtual machine in each core separately. The following structure is used in the newest version of our hypervisor. We will describe each field in the rest of this topic.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
typedef struct _VIRTUAL_MACHINE_STATE
+{
+    UINT64 VmxoRegion;        // VMXON region
+    UINT64 VmcsRegion;        // VMCS region
+    UINT64 Eptp;              // Extended-Page-Table Pointer
+    UINT64 VmmStack;          // Stack for VMM in VM-Exit State
+    UINT64 MsrBitmap;         // MSR Bitmap Virtual Address
+    UINT64 MsrBitmapPhysical; // MSR Bitmap Physical Address
+
+} VIRTUAL_MACHINE_STATE, *PVIRTUAL_MACHINE_STATE;
+

Note that it’s not the final VIRTUAL_MACHINE_STATE structure; we’ll enhance it in the future.

Preparing to launch VM

In this part, we’re just trying to enhance our hypervisor driver. In the future parts, we will add some user-mode interactions with our driver but for now, let’s start with modifying our DriverEntry as it’s the first function that executes when our driver is loaded.

Besides all the preparation from part 2, we added the following lines to use our part 4 (EPT) structures:

1
+2
+3
+4
+5
+6
+
        //
+        // Initiating EPTP and VMX
+        //
+        PEPTP EPTP = InitializeEptp();
+
+        InitiateVmx();
+

We also added an export to a global variable called “g_VirtualGuestMemoryAddress” that holds the address of where our guest code starts.

Now let’s fill our allocated pages with \xf4, which is the hex representation of the HLT instruction. I choose HLT because, with some special configuration (described below), it’ll cause VM-exit and return the code to the host handler; so, it would be an excellent example for this part.

After that, we start creating a function called LaunchVm, which is responsible for running our virtual machine on a specific core. We will only test our hypervisor in the 0th logical processor in this part. In the future part, we’ll extend our hypervisor to virtualize the entire system.

Keep in mind that every logical core has its own VMCS, and if we want our guest code to run in other logical processors, we should configure each of them separately.

To run our codes in a certain logical core, we should set the affinity by using the Windows KeSetSystemAffinityThread function and choose the specific core’s VIRTUAL_MACHINE_STATE as each core has its own separate VMXON and VMCS regions.

The following code describes how we can run our code in different logical cores.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
VOID
+LaunchVm(int ProcessorID, PEPTP EPTP)
+{
+    DbgPrint("\n======================== Launching VM =============================\n");
+
+    KAFFINITY AffinityMask;
+    AffinityMask = MathPower(2, ProcessorID);
+    KeSetSystemAffinityThread(AffinityMask);
+
+    DbgPrint("[*]\t\tCurrent thread is executing in %d th logical processor.\n", ProcessorID);
+
+    PAGED_CODE();
+
+...
+

Now that we can specify a core number and execute codes in the target core, it’s time should allocate a specific stack so that whenever a VM-exit occurs, we can save the registers and call other host functions in vmx-root mode.

A quick reminder, whenever a vm-exit occurs, the host handler is called in vmx-root mode. When we run the VMRESUME instruction, the processor switches to the VMX non-root; thus, every kernel-mode driver and user-mode application are running in VMX non-root mode. Only the portion of our driver responsible for handling the host is executed in the VMX root-mode.

Here we need a stack for host routines. We have two options, the first option is using the current RSP, and the second one is using a separated stack. We used a separate location for the stack instead of using the current RSP of the driver, but you can use the current stack (RSP) too.

The following lines are written for allocating and zeroing the stack of our VM-exit handler.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
    //
+    // Allocate stack for the VM Exit Handler
+    //
+    UINT64 VMM_STACK_VA                = ExAllocatePoolWithTag(NonPagedPool, VMM_STACK_SIZE, POOLTAG);
+    g_GuestState[ProcessorID].VmmStack = VMM_STACK_VA;
+
+    if (g_GuestState[ProcessorID].VmmStack == NULL)
+    {
+        DbgPrint("[*] Error in allocating VMM Stack.\n");
+        return;
+    }
+    RtlZeroMemory(g_GuestState[ProcessorID].VmmStack, VMM_STACK_SIZE);
+

Same as above, we’ll allocate a page for the MSR Bitmap and add it to GuestState. I’ll describe them later in this topic.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
    //
+    // Allocate memory for MSRBitMap
+    //
+    g_GuestState[ProcessorID].MsrBitmap = MmAllocateNonCachedMemory(PAGE_SIZE); // should be aligned
+    if (g_GuestState[ProcessorID].MsrBitmap == NULL)
+    {
+        DbgPrint("[*] Error in allocating MSRBitMap.\n");
+        return;
+    }
+    RtlZeroMemory(g_GuestState[ProcessorID].MsrBitmap, PAGE_SIZE);
+    g_GuestState[ProcessorID].MsrBitmapPhysical = VirtualToPhysicalAddress(g_GuestState[ProcessorID].MsrBitmap);
+

The next step is clearing the VMCS state and loading it as the current VMCS in the specific processor (in our case, the 0th logical processor).

The ClearVmcsState and LoadVmcs functions are used as described above:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+
    //
+    // Clear the VMCS State
+    //
+    if (!ClearVmcsState(&g_GuestState[ProcessorID]))
+    {
+        goto ErrorReturn;
+    }
+
+    //
+    // Load VMCS (Set the Current VMCS)
+    //
+    if (!LoadVmcs(&g_GuestState[ProcessorID]))
+    {
+        goto ErrorReturn;
+    }
+

Now it’s time to set up VMCS. We will thoroughly discuss how to configure the VMCS later in this topic, but for now, assume that there is a function called SetupVmcs, which configures the VMCS structure.

1
+2
+
    DbgPrint("[*] Setting up VMCS.\n");
+    SetupVmcs(&g_GuestState[ProcessorID], EPTP);
+

The last step is to execute the VMLAUNCH instruction. Yet we shouldn’t forget to save the current state of the stack (RSP & RBP registers). It’s because after executing the VMLAUNCH instruction, the RIP register is changed to the GUEST_RIP; thus, we need to save the previous system state so we can return to the normal system routines after returning from VM functions. If we leave the driver with the wrong RSP & RBP registers, we’ll see a BSOD. For this purpose, the AsmSaveStateForVmxoff function is used.

Saving a return point

For AsmSaveStateForVmxoff, we declare two global variables called g_StackPointerForReturning, and g_BasePointerForReturning. There is no need to save the RIP register as the stack’s return address is always available. Just EXTERN it in the assembly file :

1
+2
+
EXTERN g_StackPointerForReturning:QWORD
+EXTERN g_BasePointerForReturning:QWORD
+

The implementation of AsmSaveStateForVmxoff :

1
+2
+3
+4
+5
+6
+7
+8
+
AsmSaveStateForVmxoff PROC PUBLIC
+
+	MOV g_StackPointerForReturning, RSP
+	MOV g_BasePointerForReturning, RBP
+
+	RET
+
+AsmSaveStateForVmxoff ENDP 
+

Returning to the previous state

That last step in our hypervisor is returning to the previous system state and turning off the hypervisor.

We previously saved the system state. Now, we can restore it (RSPand RBP registers) and clear the stack position.

Before that, the VMXOFF instruction is executed to turn off the hypervisor.

Take a look at the following code.

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
+
AsmVmxoffAndRestoreState PROC PUBLIC
+
+	VMXOFF  ; turn it off before existing
+	
+	MOV RSP, g_StackPointerForReturning
+	MOV RBP, g_BasePointerForReturning
+	
+	; make rsp point to a correct return point
+	ADD RSP, 8
+	
+	; return True
+
+	XOR RAX, RAX
+	MOV RAX, 1
+	
+	; return section
+	
+	MOV     RBX, [RSP+28h+8h]
+	MOV     RSI, [RSP+28h+10h]
+	ADD     RSP, 020h
+	POP     RDI
+	
+	RET
+	
+AsmVmxoffAndRestoreState ENDP 
+

At last, we need to precisely clear the stack. Previously we called the LaunchVm function and ended up in a new RIP. To continue the execution normally, we need to clear the stack and return to the location where we called the LaunchVm function. Therefore, in the last part of the above assembly code, which is the “return section”, I used IDA Pro to see the disassembly of the LaunchVm, so we can see how this function clears the stack, and we perform the same so we can return the previous system state gracefully. Hence, the “return section” is copied from the disassemblies of the LaunchVm in IDA Pro.

LaunchVm Return Frame

VMLAUNCH Instruction

It’s time to talk about the VMLAUNCH instruction.

Take a look at the following code.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
    __vmx_vmlaunch();
+
+    //
+    // if VMLAUNCH succeeds will never be here!
+    //
+    ULONG64 ErrorCode = 0;
+    __vmx_vmread(VM_INSTRUCTION_ERROR, &ErrorCode);
+    __vmx_off();
+    DbgPrint("[*] VMLAUNCH Error : 0x%llx\n", ErrorCode);
+    DbgBreakPoint();
+

The __vmx_vmlaunch() is the intrinsic function for the VMLAUNCH instruction and __vmx_vmread is for the VMREAD instruction.

As the comment describes, if VMLAUNCH succeeds, we’ll never execute the other lines. If there is an error in the state of VMCS (which is a common problem), we have to run VMREAD ** and read the error code from the **VM_INSTRUCTION_ERROR field of VMCS. It’s also necessary to run VMXOFF to turn off the hypervisor in the case of an error, and finally, we can print the error code.

DbgBreakPoint is just a debug breakpoint (int 3), and it can be helpful only if we’re working on a remote kernel WinDbg Debugger. It’s clear that you can’t test it in your local debugging system because executing an int 3 in the kernel will freeze your system as long as there is no debugger to catch it, so it’s highly recommended to create a remote Kernel Debugging machine and test your codes for possible errors.

You can also use VMware Workstation’s nested-virtualization to create a remote kernel debugging connection. Intel doesn’t have such a thing as “nested-virtualization” but provides some hardware facilities so vendors can support and implement nested virtualization on their own. For example, you can test your driver on VMware Workstation with nested-virtualization support (I also explained how to debug your hypervisor driver on VMware in the first part.) However, supporting Hyper-V nested virtualization needs extra things to be considered in implementing a hypervisor, so we can’t test our driver on Hyper-V nested virtualization, at least for this part. I’ll explain Hyper-V support in the 8th part.

The drivers are tested on both physical machines and VMware Workstation’s nested-virtualization.

Now it’s time to read some theories before digging into the configuration of the VMCS.

VMX Controls

Let’s talk about different controls in VMCS that govern the guest’s behavior. We will use some of these bits in this part, and some will be used in future parts. So, don’t worry about it. Just take a look at the descriptions of these bits and be aware of them.

VM-Execution Controls

In order to control our guest features, we have to set some fields in our VMCS. The following tables represent the Primary Processor-Based VM-Execution Controls and the Secondary Processor-Based VM-Execution Controls.

Primary-Processor-Based-VM-Execution-Controls

We define the above table like this:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
#define CPU_BASED_VIRTUAL_INTR_PENDING        0x00000004
+#define CPU_BASED_USE_TSC_OFFSETING           0x00000008
+#define CPU_BASED_HLT_EXITING                 0x00000080
+#define CPU_BASED_INVLPG_EXITING              0x00000200
+#define CPU_BASED_MWAIT_EXITING               0x00000400
+#define CPU_BASED_RDPMC_EXITING               0x00000800
+#define CPU_BASED_RDTSC_EXITING               0x00001000
+#define CPU_BASED_CR3_LOAD_EXITING            0x00008000
+#define CPU_BASED_CR3_STORE_EXITING           0x00010000
+#define CPU_BASED_CR8_LOAD_EXITING            0x00080000
+#define CPU_BASED_CR8_STORE_EXITING           0x00100000
+#define CPU_BASED_TPR_SHADOW                  0x00200000
+#define CPU_BASED_VIRTUAL_NMI_PENDING         0x00400000
+#define CPU_BASED_MOV_DR_EXITING              0x00800000
+#define CPU_BASED_UNCOND_IO_EXITING           0x01000000
+#define CPU_BASED_ACTIVATE_IO_BITMAP          0x02000000
+#define CPU_BASED_MONITOR_TRAP_FLAG           0x08000000
+#define CPU_BASED_ACTIVATE_MSR_BITMAP         0x10000000
+#define CPU_BASED_MONITOR_EXITING             0x20000000
+#define CPU_BASED_PAUSE_EXITING               0x40000000
+#define CPU_BASED_ACTIVATE_SECONDARY_CONTROLS 0x80000000
+

In the earlier versions of VMX, there was nothing like Secondary Processor-Based VM-Execution Controls. Now, if we want to use the secondary table, we have to set the 31st bit of the first table; otherwise, it’s like the secondary table field with zeros.

Secondary-Processor-Based-VM-Execution-Controls

The definition of the above table is this (we ignore some bits, you can define them if you want to use them in your hypervisor):

1
+2
+3
+4
+5
+
#define CPU_BASED_CTL2_ENABLE_EPT            0x2
+#define CPU_BASED_CTL2_RDTSCP                0x8
+#define CPU_BASED_CTL2_ENABLE_VPID            0x20
+#define CPU_BASED_CTL2_UNRESTRICTED_GUEST    0x80
+#define CPU_BASED_CTL2_ENABLE_VMFUNC        0x2000
+

VM-entry Control Bits

The VM-entry controls constitute a 32-bit vector that governs the basic operation of VM entries.

VM-Entry-Controls

1
+2
+3
+4
+5
+
// VM-entry Control Bits 
+#define VM_ENTRY_IA32E_MODE             0x00000200
+#define VM_ENTRY_SMM                    0x00000400
+#define VM_ENTRY_DEACT_DUAL_MONITOR     0x00000800
+#define VM_ENTRY_LOAD_GUEST_PAT         0x00004000
+

VM-exit Control Bits

The VM-exit controls constitute a 32-bit vector that governs the essential operation of VM-exits.

VM-Exit-Controls

1
+2
+3
+4
+5
+
// VM-exit Control Bits 
+#define VM_EXIT_IA32E_MODE              0x00000200
+#define VM_EXIT_ACK_INTR_ON_EXIT        0x00008000
+#define VM_EXIT_SAVE_GUEST_PAT          0x00040000
+#define VM_EXIT_LOAD_HOST_PAT           0x00080000
+

PIN-Based Execution Control

The pin-based VM-execution controls constitute a 32-bit vector that governs the handling of asynchronous events (for example, interrupts). We’ll use it in the future parts, but for now, let’s define it in our hypervisor.

Pin-Based-VM-Execution-Controls

1
+2
+3
+4
+5
+
#define PIN_BASED_VM_EXECUTION_CONTROLS_EXTERNAL_INTERRUPT        0x00000001
+#define PIN_BASED_VM_EXECUTION_CONTROLS_NMI_EXITING               0x00000008
+#define PIN_BASED_VM_EXECUTION_CONTROLS_VIRTUAL_NMI               0x00000020
+#define PIN_BASED_VM_EXECUTION_CONTROLS_ACTIVE_VMX_TIMER          0x00000040
+#define PIN_BASED_VM_EXECUTION_CONTROLS_PROCESS_POSTED_INTERRUPTS 0x00000080
+

Configuring VMCS

Now that we have a basic idea about some of the VMCS fields and controls, it’s time to configure the VMCS structure fully to make our virtualized guest ready.

Gathering machine state for VMCS

In order to configure our Guest-State and Host-State, we need to have details about the current system state, e.g., Global Descriptor Table Address (GDT), Interrupt Descriptor Table (IDT) Address and read all the Segment Registers.

These functions describe how all of these registers and segments can be gathered.

GDT Base :

1
+2
+3
+4
+5
+6
+7
+8
+9
+
GetGdtBase PROC
+
+	LOCAL	GDTR[10]:BYTE
+	SGDT	GDTR
+	MOV		RAX, QWORD PTR GDTR[2]
+
+	RET
+
+GetGdtBase ENDP
+

CS segment register:

1
+2
+3
+4
+5
+6
+
GetCs PROC
+
+	MOV		RAX, CS
+	RET
+
+GetCs ENDP
+

DS segment register:

1
+2
+3
+4
+5
+6
+
GetDs PROC
+
+	MOV		RAX, DS
+	RET
+
+GetDs ENDP
+

ES segment register:

1
+2
+3
+4
+5
+6
+
GetEs PROC
+
+	MOV		RAX, ES
+	RET
+
+GetEs ENDP
+

SS segment register:

1
+2
+3
+4
+5
+6
+
GetSs PROC
+
+	MOV		RAX, SS
+	RET
+
+GetSs ENDP
+

FS segment register:

1
+2
+3
+4
+5
+6
+
GetFs PROC
+
+	MOV		RAX, FS
+	RET
+
+GetFs ENDP
+

GS segment register:

1
+2
+3
+4
+5
+6
+
GetGs PROC
+
+	MOV		RAX, GS
+	RET
+
+GetGs ENDP
+

LDT:

1
+2
+3
+4
+5
+6
+
GetLdtr PROC
+
+	SLDT	RAX
+	RET
+
+GetLdtr ENDP
+

TR (task register):

1
+2
+3
+4
+5
+6
+
GetTr PROC
+
+	STR		RAX
+	RET
+
+GetTr ENDP
+

Interrupt Descriptor Table:

1
+2
+3
+4
+5
+6
+7
+8
+9
+
GetIdtBase PROC
+
+	LOCAL	IDTR[10]:BYTE
+	
+	SIDT	IDTR
+	MOV		RAX, QWORD PTR IDTR[2]
+	RET
+
+GetIdtBase ENDP
+

GDT Limit:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
GetGdtLimit PROC
+
+	LOCAL	GDTR[10]:BYTE
+
+	SGDT	GDTR
+	MOV		AX, WORD PTR GDTR[0]
+
+	RET
+
+GetGdtLimit ENDP
+

IDT Limit:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
GetIdtLimit PROC
+
+	LOCAL	IDTR[10]:BYTE
+	
+	SIDT	IDTR
+	MOV		AX, WORD PTR IDTR[0]
+
+	RET
+
+GetIdtLimit ENDP
+

RFLAGS:

1
+2
+3
+4
+5
+6
+7
+
GetRflags PROC
+
+	PUSHFQ
+	POP		RAX
+	RET
+
+GetRflags ENDP
+

Setting up VMCS

Let’s get down to business (we have a long way to go).

This section starts with defining a function called SetupVmcs.

1
+2
+
BOOLEAN
+SetupVmcs(VIRTUAL_MACHINE_STATE * GuestState, PEPTP EPTP);
+

This function is responsible for configuring all of the options related to VMCS and, of course, the Guest & Host state.

Configuring and modifying VMCS is done by using a special instruction called “VMWRITE”.

VMWRITE writes the contents of a primary source operand (register or memory) to a specified field in a VMCS. In VMX-root operation, the instruction writes to the current VMCS. If executed in VMX non-root operation, the instruction writes to the VMCS referenced by the VMCS link pointer field in the current VMCS.

The VMCS field is specified by the VMCS-field encoding contained in the register secondary source operand.

The following enum contains most of the VMCS fields needed for VMWRITE & VMREAD instructions. (newer processors add newer fields.)

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
+
enum VMCS_FIELDS {
+    GUEST_ES_SELECTOR = 0x00000800,
+    GUEST_CS_SELECTOR = 0x00000802,
+    GUEST_SS_SELECTOR = 0x00000804,
+    GUEST_DS_SELECTOR = 0x00000806,
+    GUEST_FS_SELECTOR = 0x00000808,
+    GUEST_GS_SELECTOR = 0x0000080a,
+    GUEST_LDTR_SELECTOR = 0x0000080c,
+    GUEST_TR_SELECTOR = 0x0000080e,
+    HOST_ES_SELECTOR = 0x00000c00,
+    HOST_CS_SELECTOR = 0x00000c02,
+    HOST_SS_SELECTOR = 0x00000c04,
+    HOST_DS_SELECTOR = 0x00000c06,
+    HOST_FS_SELECTOR = 0x00000c08,
+    HOST_GS_SELECTOR = 0x00000c0a,
+    HOST_TR_SELECTOR = 0x00000c0c,
+    IO_BITMAP_A = 0x00002000,
+    IO_BITMAP_A_HIGH = 0x00002001,
+    IO_BITMAP_B = 0x00002002,
+    IO_BITMAP_B_HIGH = 0x00002003,
+    MSR_BITMAP = 0x00002004,
+    MSR_BITMAP_HIGH = 0x00002005,
+    VM_EXIT_MSR_STORE_ADDR = 0x00002006,
+    VM_EXIT_MSR_STORE_ADDR_HIGH = 0x00002007,
+    VM_EXIT_MSR_LOAD_ADDR = 0x00002008,
+    VM_EXIT_MSR_LOAD_ADDR_HIGH = 0x00002009,
+    VM_ENTRY_MSR_LOAD_ADDR = 0x0000200a,
+    VM_ENTRY_MSR_LOAD_ADDR_HIGH = 0x0000200b,
+    TSC_OFFSET = 0x00002010,
+    TSC_OFFSET_HIGH = 0x00002011,
+    VIRTUAL_APIC_PAGE_ADDR = 0x00002012,
+    VIRTUAL_APIC_PAGE_ADDR_HIGH = 0x00002013,
+    VMFUNC_CONTROLS = 0x00002018,
+    VMFUNC_CONTROLS_HIGH = 0x00002019,
+    EPT_POINTER = 0x0000201A,
+    EPT_POINTER_HIGH = 0x0000201B,
+    EPTP_LIST = 0x00002024,
+    EPTP_LIST_HIGH = 0x00002025,
+    GUEST_PHYSICAL_ADDRESS = 0x2400,
+    GUEST_PHYSICAL_ADDRESS_HIGH = 0x2401,
+    VMCS_LINK_POINTER = 0x00002800,
+    VMCS_LINK_POINTER_HIGH = 0x00002801,
+    GUEST_IA32_DEBUGCTL = 0x00002802,
+    GUEST_IA32_DEBUGCTL_HIGH = 0x00002803,
+    PIN_BASED_VM_EXEC_CONTROL = 0x00004000,
+    CPU_BASED_VM_EXEC_CONTROL = 0x00004002,
+    EXCEPTION_BITMAP = 0x00004004,
+    PAGE_FAULT_ERROR_CODE_MASK = 0x00004006,
+    PAGE_FAULT_ERROR_CODE_MATCH = 0x00004008,
+    CR3_TARGET_COUNT = 0x0000400a,
+    VM_EXIT_CONTROLS = 0x0000400c,
+    VM_EXIT_MSR_STORE_COUNT = 0x0000400e,
+    VM_EXIT_MSR_LOAD_COUNT = 0x00004010,
+    VM_ENTRY_CONTROLS = 0x00004012,
+    VM_ENTRY_MSR_LOAD_COUNT = 0x00004014,
+    VM_ENTRY_INTR_INFO_FIELD = 0x00004016,
+    VM_ENTRY_EXCEPTION_ERROR_CODE = 0x00004018,
+    VM_ENTRY_INSTRUCTION_LEN = 0x0000401a,
+    TPR_THRESHOLD = 0x0000401c,
+    SECONDARY_VM_EXEC_CONTROL = 0x0000401e,
+    VM_INSTRUCTION_ERROR = 0x00004400,
+    VM_EXIT_REASON = 0x00004402,
+    VM_EXIT_INTR_INFO = 0x00004404,
+    VM_EXIT_INTR_ERROR_CODE = 0x00004406,
+    IDT_VECTORING_INFO_FIELD = 0x00004408,
+    IDT_VECTORING_ERROR_CODE = 0x0000440a,
+    VM_EXIT_INSTRUCTION_LEN = 0x0000440c,
+    VMX_INSTRUCTION_INFO = 0x0000440e,
+    GUEST_ES_LIMIT = 0x00004800,
+    GUEST_CS_LIMIT = 0x00004802,
+    GUEST_SS_LIMIT = 0x00004804,
+    GUEST_DS_LIMIT = 0x00004806,
+    GUEST_FS_LIMIT = 0x00004808,
+    GUEST_GS_LIMIT = 0x0000480a,
+    GUEST_LDTR_LIMIT = 0x0000480c,
+    GUEST_TR_LIMIT = 0x0000480e,
+    GUEST_GDTR_LIMIT = 0x00004810,
+    GUEST_IDTR_LIMIT = 0x00004812,
+    GUEST_ES_AR_BYTES = 0x00004814,
+    GUEST_CS_AR_BYTES = 0x00004816,
+    GUEST_SS_AR_BYTES = 0x00004818,
+    GUEST_DS_AR_BYTES = 0x0000481a,
+    GUEST_FS_AR_BYTES = 0x0000481c,
+    GUEST_GS_AR_BYTES = 0x0000481e,
+    GUEST_LDTR_AR_BYTES = 0x00004820,
+    GUEST_TR_AR_BYTES = 0x00004822,
+    GUEST_INTERRUPTIBILITY_INFO = 0x00004824,
+    GUEST_ACTIVITY_STATE = 0x00004826,
+    GUEST_SM_BASE = 0x00004828,
+    GUEST_SYSENTER_CS = 0x0000482A,
+    HOST_IA32_SYSENTER_CS = 0x00004c00,
+    CR0_GUEST_HOST_MASK = 0x00006000,
+    CR4_GUEST_HOST_MASK = 0x00006002,
+    CR0_READ_SHADOW = 0x00006004,
+    CR4_READ_SHADOW = 0x00006006,
+    CR3_TARGET_VALUE0 = 0x00006008,
+    CR3_TARGET_VALUE1 = 0x0000600a,
+    CR3_TARGET_VALUE2 = 0x0000600c,
+    CR3_TARGET_VALUE3 = 0x0000600e,
+    EXIT_QUALIFICATION = 0x00006400,
+    GUEST_LINEAR_ADDRESS = 0x0000640a,
+    GUEST_CR0 = 0x00006800,
+    GUEST_CR3 = 0x00006802,
+    GUEST_CR4 = 0x00006804,
+    GUEST_ES_BASE = 0x00006806,
+    GUEST_CS_BASE = 0x00006808,
+    GUEST_SS_BASE = 0x0000680a,
+    GUEST_DS_BASE = 0x0000680c,
+    GUEST_FS_BASE = 0x0000680e,
+    GUEST_GS_BASE = 0x00006810,
+    GUEST_LDTR_BASE = 0x00006812,
+    GUEST_TR_BASE = 0x00006814,
+    GUEST_GDTR_BASE = 0x00006816,
+    GUEST_IDTR_BASE = 0x00006818,
+    GUEST_DR7 = 0x0000681a,
+    GUEST_RSP = 0x0000681c,
+    GUEST_RIP = 0x0000681e,
+    GUEST_RFLAGS = 0x00006820,
+    GUEST_PENDING_DBG_EXCEPTIONS = 0x00006822,
+    GUEST_SYSENTER_ESP = 0x00006824,
+    GUEST_SYSENTER_EIP = 0x00006826,
+    HOST_CR0 = 0x00006c00,
+    HOST_CR3 = 0x00006c02,
+    HOST_CR4 = 0x00006c04,
+    HOST_FS_BASE = 0x00006c06,
+    HOST_GS_BASE = 0x00006c08,
+    HOST_TR_BASE = 0x00006c0a,
+    HOST_GDTR_BASE = 0x00006c0c,
+    HOST_IDTR_BASE = 0x00006c0e,
+    HOST_IA32_SYSENTER_ESP = 0x00006c10,
+    HOST_IA32_SYSENTER_EIP = 0x00006c12,
+    HOST_RSP = 0x00006c14,
+    HOST_RIP = 0x00006c16,
+};
+

OK, let’s continue with our configuration.

The next step is configuring host Segment Registers.

1
+2
+3
+4
+5
+6
+7
+
    __vmx_vmwrite(HOST_ES_SELECTOR, GetEs() & 0xF8);
+    __vmx_vmwrite(HOST_CS_SELECTOR, GetCs() & 0xF8);
+    __vmx_vmwrite(HOST_SS_SELECTOR, GetSs() & 0xF8);
+    __vmx_vmwrite(HOST_DS_SELECTOR, GetDs() & 0xF8);
+    __vmx_vmwrite(HOST_FS_SELECTOR, GetFs() & 0xF8);
+    __vmx_vmwrite(HOST_GS_SELECTOR, GetGs() & 0xF8);
+    __vmx_vmwrite(HOST_TR_SELECTOR, GetTr() & 0xF8);
+

Keep in mind that those fields that start with “HOST_” are related to the state in which the hypervisor sets whenever a VM-exit occurs, and those which begin with “GUEST_” are related to the state in which the hypervisor sets for guest when a VMLAUNCH executed.

The purpose of & 0xF8 is that Intel mentioned that the three less significant bits must be cleared; otherwise, it leads to an error as the VMLAUNCH is executed with an Invalid Host State error.

Next, we set the VMCS_LINK_POINTER, which should be ‘0xffffffffffffffff’. As we don’t have an additional VMCS. This field is mainly used for hypervisors that want to implement a nested-virtualization behavior (like VMware Nested Virtualization or KVM’s nVMX).

1
+2
+3
+4
+
    //
+    // Setting the link pointer to the required value for 4KB VMCS
+    //
+    __vmx_vmwrite(VMCS_LINK_POINTER, ~0ULL);
+

The rest of this topic intends to virtualize the machine’s current state, so the guest and host configurations must be the same.

Let’s configure GUEST_IA32_DEBUGCTL. This field works the same as the IA32_DEBUGCTL MSR in a physical machine, and we can use it if we want to use separate IA32_DEBUGCTL for each guest. It provides bit field controls to enable debug trace interrupts, debug trace stores, trace messages enable, single stepping on branches, last branch record recording, and control freezing of LBR stack.

We don’t use it in our hypervisor, but we should configure it to the current machine’s MSR_IA32_DEBUGCTL. We use __readmsr() intrinsic to read this MSR (RDMSR) and put the value of the physical machine to the guest’s GUEST_IA32_DEBUGCTL.

1
+2
+
    __vmx_vmwrite(GUEST_IA32_DEBUGCTL, __readmsr(MSR_IA32_DEBUGCTL) & 0xFFFFFFFF);
+    __vmx_vmwrite(GUEST_IA32_DEBUGCTL_HIGH, __readmsr(MSR_IA32_DEBUGCTL) >> 32);
+

Note that values we put zero on them can be ignored; if you don’t modify them, it’s like you put zero on them.

For example, configuring TSC is not important for our hypervisor in the current state, so we put zero on it.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
    /* Time-stamp counter offset */
+    __vmx_vmwrite(TSC_OFFSET, 0);
+    __vmx_vmwrite(TSC_OFFSET_HIGH, 0);
+
+    __vmx_vmwrite(PAGE_FAULT_ERROR_CODE_MASK, 0);
+    __vmx_vmwrite(PAGE_FAULT_ERROR_CODE_MATCH, 0);
+
+    __vmx_vmwrite(VM_EXIT_MSR_STORE_COUNT, 0);
+    __vmx_vmwrite(VM_EXIT_MSR_LOAD_COUNT, 0);
+
+    __vmx_vmwrite(VM_ENTRY_MSR_LOAD_COUNT, 0);
+    __vmx_vmwrite(VM_ENTRY_INTR_INFO_FIELD, 0);
+

This time, we’ll configure Segment Registers based on the GDT base address for our Host (When VM-Exit occurs).

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
    GdtBase = GetGdtBase();
+
+    FillGuestSelectorData((PVOID)GdtBase, ES, GetEs());
+    FillGuestSelectorData((PVOID)GdtBase, CS, GetCs());
+    FillGuestSelectorData((PVOID)GdtBase, SS, GetSs());
+    FillGuestSelectorData((PVOID)GdtBase, DS, GetDs());
+    FillGuestSelectorData((PVOID)GdtBase, FS, GetFs());
+    FillGuestSelectorData((PVOID)GdtBase, GS, GetGs());
+    FillGuestSelectorData((PVOID)GdtBase, LDTR, GetLdtr());
+    FillGuestSelectorData((PVOID)GdtBase, TR, GetTr());
+

GetGdtBase is defined above in the process of gathering information for our VMCS.

FillGuestSelectorData is responsible for setting the GUEST selector, attributes, limit, and base for VMCS. It is implemented as below:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
VOID
+FillGuestSelectorData(
+    PVOID  GdtBase,
+    ULONG  Segreg,
+    USHORT Selector)
+{
+    SEGMENT_SELECTOR SegmentSelector = {0};
+    ULONG            AccessRights;
+
+    GetSegmentDescriptor(&SegmentSelector, Selector, GdtBase);
+    AccessRights = ((PUCHAR)&SegmentSelector.ATTRIBUTES)[0] + (((PUCHAR)&SegmentSelector.ATTRIBUTES)[1] << 12);
+
+    if (!Selector)
+        AccessRights |= 0x10000;
+
+    __vmx_vmwrite(GUEST_ES_SELECTOR + Segreg * 2, Selector);
+    __vmx_vmwrite(GUEST_ES_LIMIT + Segreg * 2, SegmentSelector.LIMIT);
+    __vmx_vmwrite(GUEST_ES_AR_BYTES + Segreg * 2, AccessRights);
+    __vmx_vmwrite(GUEST_ES_BASE + Segreg * 2, SegmentSelector.BASE);
+}
+

The function body for GetSegmentDescriptor :

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
+
BOOLEAN
+GetSegmentDescriptor(PSEGMENT_SELECTOR SegmentSelector,
+                     USHORT            Selector,
+                     PUCHAR            GdtBase)
+{
+    PSEGMENT_DESCRIPTOR SegDesc;
+
+    if (!SegmentSelector)
+        return FALSE;
+
+    if (Selector & 0x4)
+    {
+        return FALSE;
+    }
+
+    SegDesc = (PSEGMENT_DESCRIPTOR)((PUCHAR)GdtBase + (Selector & ~0x7));
+
+    SegmentSelector->SEL               = Selector;
+    SegmentSelector->BASE              = SegDesc->BASE0 | SegDesc->BASE1 << 16 | SegDesc->BASE2 << 24;
+    SegmentSelector->LIMIT             = SegDesc->LIMIT0 | (SegDesc->LIMIT1ATTR1 & 0xf) << 16;
+    SegmentSelector->ATTRIBUTES.UCHARs = SegDesc->ATTR0 | (SegDesc->LIMIT1ATTR1 & 0xf0) << 4;
+
+    if (!(SegDesc->ATTR0 & 0x10))
+    { // LA_ACCESSED
+        ULONG64 Tmp;
+        // this is a TSS or callgate etc, save the base high part
+        Tmp                   = (*(PULONG64)((PUCHAR)SegDesc + 8));
+        SegmentSelector->BASE = (SegmentSelector->BASE & 0xffffffff) | (Tmp << 32);
+    }
+
+    if (SegmentSelector->ATTRIBUTES.Fields.G)
+    {
+        // 4096-bit granularity is enabled for this segment, scale the limit
+        SegmentSelector->LIMIT = (SegmentSelector->LIMIT << 12) + 0xfff;
+    }
+
+    return TRUE;
+}
+

Another MSR called IA32_KERNEL_GS_BASE is used to set the kernel GS base. Whenever instructions like SYSCALL are executed, and the processor enters ring 0, we need to change the current GS register, which can be done using SWAPGS instruction. This instruction copies the content of IA32_KERNEL_GS_BASE into the IA32_GS_BASE, and now it’s used in the kernel when it wants to re-enter the user-mode.

MSR_FS_BASE on the other hand, doesn’t have a kernel base because it is used in 32-Bit mode while we have a 64-bit (long mode) kernel.

Like the above MSR, we’ll configure the IA32_GS_BASE and IA32_FS_BASE MSRs based on the current system’s MSRs.

1
+2
+
    __vmx_vmwrite(GUEST_FS_BASE, __readmsr(MSR_FS_BASE));
+    __vmx_vmwrite(GUEST_GS_BASE, __readmsr(MSR_GS_BASE));
+

The GUEST_INTERRUPTIBILITY_INFO and GUEST_ACTIVITY_STATE are set to zero (we’ll describe them in the future parts).

1
+2
+
    __vmx_vmwrite(GUEST_INTERRUPTIBILITY_INFO, 0);
+    __vmx_vmwrite(GUEST_ACTIVITY_STATE, 0);   //Active state 
+

Now we reach an essential part of the VMCS, and it’s the configuration of CPU_BASED_VM_EXEC_CONTROL and SECONDARY_VM_EXEC_CONTROL controls.

These fields enable and disable some essential features of the guest, e.g., we can configure VMCS to cause a VM-Exit whenever execution of HLT instruction is detected (in guest). You can read the description of each bit in the VM-Execution Controls section on this topic.

1
+2
+
    __vmx_vmwrite(CPU_BASED_VM_EXEC_CONTROL, AdjustControls(CPU_BASED_HLT_EXITING | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, MSR_IA32_VMX_PROCBASED_CTLS));
+    __vmx_vmwrite(SECONDARY_VM_EXEC_CONTROL, AdjustControls(CPU_BASED_CTL2_RDTSCP /* | CPU_BASED_CTL2_ENABLE_EPT*/, MSR_IA32_VMX_PROCBASED_CTLS2));
+

As you can see, we set CPU_BASED_HLT_EXITING that will cause the VM-Exit on HLT and activate secondary controls using the CPU_BASED_ACTIVATE_SECONDARY_CONTROLS bit.

In the secondary controls, we used CPU_BASED_CTL2_RDTSCP, and for now, comment CPU_BASED_CTL2_ENABLE_EPT because we don’t need to deal with EPT in this part. In the 7th part, I thoroughly describe about EPT.

The description of PIN_BASED_VM_EXEC_CONTROL, VM_EXIT_CONTROLS*, and VM_ENTRY_CONTROLS is available above. We don’t have any special configuration for these controls in this part; hence, let us put zero on them.

1
+2
+3
+
    __vmx_vmwrite(PIN_BASED_VM_EXEC_CONTROL, AdjustControls(0, MSR_IA32_VMX_PINBASED_CTLS));
+    __vmx_vmwrite(VM_EXIT_CONTROLS, AdjustControls(VM_EXIT_IA32E_MODE | VM_EXIT_ACK_INTR_ON_EXIT, MSR_IA32_VMX_EXIT_CTLS));
+    __vmx_vmwrite(VM_ENTRY_CONTROLS, AdjustControls(VM_ENTRY_IA32E_MODE, MSR_IA32_VMX_ENTRY_CTLS));
+

Also, the AdjustControls is a function for configuring the 0-settings and 1-settings of these fields (we will describe them in the future parts) but for now; it’s defined like this:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
ULONG
+AdjustControls(ULONG Ctl, ULONG Msr)
+{
+    MSR MsrValue = {0};
+
+    MsrValue.Content = __readmsr(Msr);
+    Ctl &= MsrValue.High; /* bit == 0 in high word ==> must be zero */
+    Ctl |= MsrValue.Low;  /* bit == 1 in low word  ==> must be one  */
+    return Ctl;
+}
+

The next step is setting Control Registers and Debug Registers (DR7) for the guest and the host. We set them to the same values as the current machine’s state using intrinsic functions.

1
+2
+3
+4
+5
+6
+7
+
    __vmx_vmwrite(GUEST_CR0, __readcr0());
+    __vmx_vmwrite(GUEST_CR3, __readcr3());
+    __vmx_vmwrite(GUEST_CR4, __readcr4());
+
+    __vmx_vmwrite(HOST_CR0, __readcr0());
+    __vmx_vmwrite(HOST_CR3, __readcr3());
+    __vmx_vmwrite(HOST_CR4, __readcr4());
+

The next part is setting up IDT and GDT’s Base and Limit for our guest. Generally, it’s not a good idea to use the same IDT (and GDT) for the guest and host, but in order to keep our hypervisor simple, we’ll configure them to the same value.

1
+2
+3
+4
+
    __vmx_vmwrite(GUEST_GDTR_BASE, GetGdtBase());
+    __vmx_vmwrite(GUEST_IDTR_BASE, GetIdtBase());
+    __vmx_vmwrite(GUEST_GDTR_LIMIT, GetGdtLimit());
+    __vmx_vmwrite(GUEST_IDTR_LIMIT, GetIdtLimit());
+

Next, set the RFLAGS.

1
+
    __vmx_vmwrite(GUEST_RFLAGS, GetRflags());
+

If you want to use SYSENTER in your guest, you should configure the following MSRs. It’s not important to set these values in x64 Windows because Windows doesn’t support SYSENTER in x64 versions of Windows; instead, it uses SYSCALL.

The same instruction works for 32-bit processes too. In 32-bit processes, Windows first changes the execution mode to long-mode (using Heaven’s Gate technique), and then executes the SYSCALL instruction.

1
+2
+3
+4
+5
+6
+
    __vmx_vmwrite(GUEST_SYSENTER_CS, __readmsr(MSR_IA32_SYSENTER_CS));
+    __vmx_vmwrite(GUEST_SYSENTER_EIP, __readmsr(MSR_IA32_SYSENTER_EIP));
+    __vmx_vmwrite(GUEST_SYSENTER_ESP, __readmsr(MSR_IA32_SYSENTER_ESP));
+    __vmx_vmwrite(HOST_IA32_SYSENTER_CS, __readmsr(MSR_IA32_SYSENTER_CS));
+    __vmx_vmwrite(HOST_IA32_SYSENTER_EIP, __readmsr(MSR_IA32_SYSENTER_EIP));
+    __vmx_vmwrite(HOST_IA32_SYSENTER_ESP, __readmsr(MSR_IA32_SYSENTER_ESP));
+

Don’t forget to configure HOST_FS_BASE, HOST_GS_BASE, HOST_GDTR_BASE, HOST_IDTR_BASE, HOST_TR_BASE for the host in the VMCS.

1
+2
+3
+4
+5
+6
+7
+8
+
    GetSegmentDescriptor(&SegmentSelector, GetTr(), (PUCHAR)GetGdtBase());
+    __vmx_vmwrite(HOST_TR_BASE, SegmentSelector.BASE);
+
+    __vmx_vmwrite(HOST_FS_BASE, __readmsr(MSR_FS_BASE));
+    __vmx_vmwrite(HOST_GS_BASE, __readmsr(MSR_GS_BASE));
+
+    __vmx_vmwrite(HOST_GDTR_BASE, GetGdtBase());
+    __vmx_vmwrite(HOST_IDTR_BASE, GetIdtBase());
+

The next important part is to set the RIP and RSP registers of the guest when a VMLAUNCH is executed. It starts with the RIP you configured in this part and RIP and RSP of the host when a VM-exit occurs. It’s pretty clear that host RIP should point to a function responsible for managing VMX events based on the VM-exit code and whether decide to execute a VMRESUME or turn off the hypervisor using VMXOFF.

1
+2
+3
+4
+5
+
    __vmx_vmwrite(GUEST_RSP, (ULONG64)g_VirtualGuestMemoryAddress); // setup guest sp
+    __vmx_vmwrite(GUEST_RIP, (ULONG64)g_VirtualGuestMemoryAddress); // setup guest ip
+
+    __vmx_vmwrite(HOST_RSP, ((ULONG64)GuestState->VmmStack + VMM_STACK_SIZE - 1));
+    __vmx_vmwrite(HOST_RIP, (ULONG64)AsmVmexitHandler);
+

HOST_RSP points to VmmStack that we allocated before, and HOST_RIP points to AsmVmexitHandler (an assembly written function described below). GUEST_RIP points to g_VirtualGuestMemoryAddress (the global variable we configured during EPT initialization) and GUEST_RSP to the same address (g_VirtualGuestMemoryAddress) because we don’t put any instruction that uses the stack, so for a real-world example, it should point to a different writeable address.

Done! Our VMCS is almost ready.

Checking VMCS Layout

Unfortunately, checking VMCS Layout is not as straight as the other parts. We have to control all the checklists described in [CHAPTER 26] VM ENTRIES from Intel’s 64 and IA-32 Architectures Software Developer’s Manual, including the following sections:

  • 26.2 CHECKS ON VMX CONTROLS AND HOST-STATE AREA
  • 26.3 CHECKING AND LOADING GUEST STATE
  • 26.4 LOADING MSRS
  • 26.5 EVENT INJECTION
  • 26.6 SPECIAL FEATURES OF VM ENTRY
  • 26.7 VM-ENTRY FAILURES DURING OR AFTER LOADING GUEST STATE
  • 26.8 MACHINE-CHECK EVENTS DURING VM ENTRY

The hardest part of this process is when we have no idea about the incorrect part of your VMCS layout or, on the other hand, when you miss something that eventually causes the failure.

This is because Intel just gives an error number without any further details about what’s exactly wrong in n our VMCS Layout.

The errors are shown below.

VM Errors

To solve this problem, I created a user-mode application called VmcsAuditor. As its name describes, it can be a choice if you have any error and don’t have any idea about solving the problem.

Remember that VmcsAuditor is a tool based on Bochs emulator support for VMX, so all the checks come from Bochs, and it’s not a 100% reliable tool that solves all the problem as we don’t know what exactly happens inside the processor. Still, it can be handy and a time saver.

The source code and executable files are available on GitHub :

[https://github.com/SinaKarvandi/VMCS-Auditor]

Further description available here.

As a better alternative, you can use Satoshi Tanda’s code for checking the guest state.

VM-Exit Handler

When our guest software exits and gives the handle back to the host, the following VM-exit reasons might happen.

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
+
#define EXIT_REASON_EXCEPTION_NMI       0
+#define EXIT_REASON_EXTERNAL_INTERRUPT  1
+#define EXIT_REASON_TRIPLE_FAULT        2
+#define EXIT_REASON_INIT                3
+#define EXIT_REASON_SIPI                4
+#define EXIT_REASON_IO_SMI              5
+#define EXIT_REASON_OTHER_SMI           6
+#define EXIT_REASON_PENDING_VIRT_INTR   7
+#define EXIT_REASON_PENDING_VIRT_NMI    8
+#define EXIT_REASON_TASK_SWITCH         9
+#define EXIT_REASON_CPUID               10
+#define EXIT_REASON_GETSEC              11
+#define EXIT_REASON_HLT                 12
+#define EXIT_REASON_INVD                13
+#define EXIT_REASON_INVLPG              14
+#define EXIT_REASON_RDPMC               15
+#define EXIT_REASON_RDTSC               16
+#define EXIT_REASON_RSM                 17
+#define EXIT_REASON_VMCALL              18
+#define EXIT_REASON_VMCLEAR             19
+#define EXIT_REASON_VMLAUNCH            20
+#define EXIT_REASON_VMPTRLD             21
+#define EXIT_REASON_VMPTRST             22
+#define EXIT_REASON_VMREAD              23
+#define EXIT_REASON_VMRESUME            24
+#define EXIT_REASON_VMWRITE             25
+#define EXIT_REASON_VMXOFF              26
+#define EXIT_REASON_VMXON               27
+#define EXIT_REASON_CR_ACCESS           28
+#define EXIT_REASON_DR_ACCESS           29
+#define EXIT_REASON_IO_INSTRUCTION      30
+#define EXIT_REASON_MSR_READ            31
+#define EXIT_REASON_MSR_WRITE           32
+#define EXIT_REASON_INVALID_GUEST_STATE 33
+#define EXIT_REASON_MSR_LOADING         34
+#define EXIT_REASON_MWAIT_INSTRUCTION   36
+#define EXIT_REASON_MONITOR_TRAP_FLAG   37
+#define EXIT_REASON_MONITOR_INSTRUCTION 39
+#define EXIT_REASON_PAUSE_INSTRUCTION   40
+#define EXIT_REASON_MCE_DURING_VMENTRY  41
+#define EXIT_REASON_TPR_BELOW_THRESHOLD 43
+#define EXIT_REASON_APIC_ACCESS         44
+#define EXIT_REASON_ACCESS_GDTR_OR_IDTR 46
+#define EXIT_REASON_ACCESS_LDTR_OR_TR   47
+#define EXIT_REASON_EPT_VIOLATION       48
+#define EXIT_REASON_EPT_MISCONFIG       49
+#define EXIT_REASON_INVEPT              50
+#define EXIT_REASON_RDTSCP              51
+#define EXIT_REASON_VMX_PREEMPTION_TIMER_EXPIRED     52
+#define EXIT_REASON_INVVPID             53
+#define EXIT_REASON_WBINVD              54
+#define EXIT_REASON_XSETBV              55
+#define EXIT_REASON_APIC_WRITE          56
+#define EXIT_REASON_RDRAND              57
+#define EXIT_REASON_INVPCID             58
+#define EXIT_REASON_RDSEED              61
+#define EXIT_REASON_PML_FULL            62
+#define EXIT_REASON_XSAVES              63
+#define EXIT_REASON_XRSTORS             64
+#define EXIT_REASON_PCOMMIT             65
+

VMX-exit handler should be an assembly function because calling a compiled function needs some preparation and some register modification. The necessary thing in the VM-exit handler is saving the registers’ state so we can continue the guest later.

I create a sample function for saving and restoring registers. In this function, we call another C function to extend the vm-exit handler.

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
+
PUBLIC AsmVmexitHandler
+
+EXTERN MainVmexitHandler:PROC
+EXTERN VmResumeInstruction:PROC
+
+.code _text
+
+AsmVmexitHandler PROC
+
+    PUSH R15
+    PUSH R14
+    PUSH R13
+    PUSH R12
+    PUSH R11
+    PUSH R10
+    PUSH R9
+    PUSH R8        
+    PUSH RDI
+    PUSH RSI
+    PUSH RBP
+    PUSH RBP	; RSP
+    PUSH RBX
+    PUSH RDX
+    PUSH RCX
+    PUSH RAX	
+
+	MOV RCX, RSP		; GuestRegs
+	SUB	RSP, 28h
+
+	CALL	MainVmexitHandler
+	ADD	RSP, 28h	
+
+	POP RAX
+    POP RCX
+    POP RDX
+    POP RBX
+    POP RBP		; RSP
+    POP RBP
+    POP RSI
+    POP RDI 
+    POP R8
+    POP R9
+    POP R10
+    POP R11
+    POP R12
+    POP R13
+    POP R14
+    POP R15
+
+	SUB RSP, 0100h ; to avoid error in future functions
+	
+    JMP VmResumeInstruction
+	
+AsmVmexitHandler ENDP
+
+END
+

The main VM-exit handler is a switch-case function with different decisions over the VMCS VM_EXIT_REASON and EXIT_QUALIFICATION.

In this part, we’re just performing an action over EXIT_REASON_HLT and just print the result and restore the guest state normally.

From the following code, you can see what event cause the VM-exit. Just keep in mind that some reasons only lead to Vm-exit if the VMCS’s control execution fields (described above) configure it. For instance, the execution of HLT instruction in guest will cause VM-exit if the 7th bit of the Primary Processor-Based VM-Execution Controls allows it.

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
+
VOID
+MainVmexitHandler(PGUEST_REGS GuestRegs)
+{
+    ULONG ExitReason = 0;
+    __vmx_vmread(VM_EXIT_REASON, &ExitReason);
+
+    ULONG ExitQualification = 0;
+    __vmx_vmread(EXIT_QUALIFICATION, &ExitQualification);
+
+    DbgPrint("\nVM_EXIT_REASION 0x%x\n", ExitReason & 0xffff);
+    DbgPrint("\EXIT_QUALIFICATION 0x%x\n", ExitQualification);
+
+    switch (ExitReason)
+    {
+        //
+        // 25.1.2  Instructions That Cause VM Exits Unconditionally
+        // The following instructions cause VM exits when they are executed in VMX non-root operation: CPUID, GETSEC,
+        // INVD, and XSETBV. This is also true of instructions introduced with VMX, which include: INVEPT, INVVPID,
+        // VMCALL, VMCLEAR, VMLAUNCH, VMPTRLD, VMPTRST, VMRESUME, VMXOFF, and VMXON.
+        //
+
+    case EXIT_REASON_VMCLEAR:
+    case EXIT_REASON_VMPTRLD:
+    case EXIT_REASON_VMPTRST:
+    case EXIT_REASON_VMREAD:
+    case EXIT_REASON_VMRESUME:
+    case EXIT_REASON_VMWRITE:
+    case EXIT_REASON_VMXOFF:
+    case EXIT_REASON_VMXON:
+    case EXIT_REASON_VMLAUNCH:
+    {
+        break;
+    }
+    case EXIT_REASON_HLT:
+    {
+        DbgPrint("[*] Execution of HLT detected... \n");
+
+        //
+        // that's enough for now ;)
+        //
+        AsmVmxoffAndRestoreState();
+
+        break;
+    }
+    case EXIT_REASON_EXCEPTION_NMI:
+    {
+        break;
+    }
+
+    case EXIT_REASON_CPUID:
+    {
+        break;
+    }
+
+    case EXIT_REASON_INVD:
+    {
+        break;
+    }
+
+    case EXIT_REASON_VMCALL:
+    {
+        break;
+    }
+
+    case EXIT_REASON_CR_ACCESS:
+    {
+        break;
+    }
+
+    case EXIT_REASON_MSR_READ:
+    {
+        break;
+    }
+
+    case EXIT_REASON_MSR_WRITE:
+    {
+        break;
+    }
+
+    case EXIT_REASON_EPT_VIOLATION:
+    {
+        break;
+    }
+
+    default:
+    {
+        // DbgBreakPoint();
+        break;
+    }
+    }
+}
+

Resume to next instruction

If a VM-exit occurs (e.g., the guest executed a CPUID instruction), the guest RIP remains constant, and it’s up to VMM to change the Guest’s RIP or not, so if we don’t have a certain function for managing this situation, then the processor executes an infinite loop of CPUID instructions because we didn’t increment the RIP.

In order to solve this problem, we have to read a VMCS field called VM_EXIT_INSTRUCTION_LEN that stores the length of the instruction that caused the VM-exit.

First, we have to read the guest’s current RIP from the GUEST_RIP. Second, read the VM_EXIT_INSTRUCTION_LEN using VMREAD, and third the length of the instruction to the guest’s RIP. Now the guest will continue its execution from the next instruction, and we’re good to go.

The following function is for this purpose.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
VOID
+ResumeToNextInstruction()
+{
+    PVOID ResumeRIP             = NULL;
+    PVOID CurrentRIP            = NULL;
+    ULONG ExitInstructionLength = 0;
+
+    __vmx_vmread(GUEST_RIP, &CurrentRIP);
+    __vmx_vmread(VM_EXIT_INSTRUCTION_LEN, &ExitInstructionLength);
+
+    ResumeRIP = (PCHAR)CurrentRIP + ExitInstructionLength;
+
+    __vmx_vmwrite(GUEST_RIP, (ULONG64)ResumeRIP);
+}
+

VMRESUME Instruction

Now that we handled the VM-exit, it’s time to continue the guest. We could continue the execution by using the VMRESUME instruction.

VMRESUME is like VMLAUNCH, but it’s used in order to resume the guest.

To compare these instructions,

  • VMLAUNCH fails if the launch state of the current VMCS is not “clear”. If the instruction is successful, it sets the launch state to “launched.”

  • VMRESUME fails if the launch state of the current VMCS is not “launched.”

So it’s clear that if we executed the VMLAUNCH instruction before, we can’t use it anymore to resume the guest code, and in this condition, VMRESUME is used.

The following code is the implementation of VMRESUME.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
VOID
+VmResumeInstruction()
+{
+    __vmx_vmresume();
+
+    // if VMRESUME succeeds will never be here !
+
+    ULONG64 ErrorCode = 0;
+    __vmx_vmread(VM_INSTRUCTION_ERROR, &ErrorCode);
+    __vmx_off();
+    DbgPrint("[*] VMRESUME Error : 0x%llx\n", ErrorCode);
+
+    //
+    // It's such a bad error because we don't where to go!
+    // prefer to break
+    //
+    DbgBreakPoint();
+}
+

Let’s Test it!

Well, we have done with the configuration, and now it’s time to run our driver using OSR Driver Loader. As always, we should disable the driver signature enforcement and run our driver.

As you can see from the above picture (in the launching VM area), first, we set the current logical processor to 0. Next, we clear our VMCS status using the VMCLEAR instruction, set up our VMCS layout and execute the VMLAUNCH instruction.

Now, our guest code is executed, and as we configured our VMCS to cause a VM-exit in the case of the execution of the HLT (CPU_BASED_HLT_EXITING) instruction.

After running the guest, the VM-exit handler is called, then it calls the main VM-exit handler, and as the VMCS exit reason is 0xc (EXIT_REASON_HLT), we successfully detected the execution of HLT in the guest.

After that, our machine state saving mechanism is executed, and we successfully turn off the hypervisor using the VMXOFF instruction and return to the first caller with a successful **(RAX = 1) status.

That’s it! Wasn’t it easy?

:)

Conclusion

In this part, we got familiar with configuring the Virtual Machine Control Structure and finally ran our guest code. The future parts would be an enhancement to this configuration like entering protected-mode, interrupt injection, page modification logging, virtualizing the current machine, and so on. You can use the comments section below if you have any questions or problems.

See you in the next part.

The sixth part is also available here.

References

[1] Vol 3C - Chapter 24 – (VIRTUAL MACHINE CONTROL STRUCTURES (https://software.intel.com/en-us/articles/intel-sdm)

[2] Vol 3C - Chapter 26 – (VM ENTRIES) (https://software.intel.com/en-us/articles/intel-sdm)

[3] Segmentation (https://wiki.osdev.org/Segmentation)

[4] x86 memory segmentation (https://en.wikipedia.org/wiki/X86_memory_segmentation)

[5] VmcsAuditor – A Bochs-Based Hypervisor Layout Checker (https://rayanfam.com/topics/vmcsauditor-a-bochs-based-hypervisor-layout-checker/)

[6] Rohaaan/Hypervisor For Beginners (https://github.com/rohaaan/hypervisor-for-beginners)

[7] SWAPGS — Swap GS Base Register (https://www.felixcloutier.com/x86/SWAPGS.html)

[8] Knockin’ on Heaven’s Gate - Dynamic Processor Mode Switching (http://rce.co/knockin-on-heavens-gate-dynamic-processor-mode-switching/)

This post is licensed under CC BY 4.0 by the author.

VmcsAuditor - A Bochs-Based Hypervisor Layout Checker

Call Gates' Ring Transitioning in IA-32 Mode

Comments powered by Disqus.

diff --git a/topics/hypervisor-from-scratch-part-6/index.html b/topics/hypervisor-from-scratch-part-6/index.html new file mode 100644 index 00000000..d04086cc --- /dev/null +++ b/topics/hypervisor-from-scratch-part-6/index.html @@ -0,0 +1,1819 @@ + Hypervisor From Scratch – Part 6: Virtualizing An Already Running System | Rayanfam Blog
Home Hypervisor From Scratch – Part 6: Virtualizing An Already Running System
Post
Cancel

Hypervisor From Scratch – Part 6: Virtualizing An Already Running System

If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous practical examples on how to utilize hypervisors for reverse engineering.

Introduction

Hello and welcome to the 6th part of the tutorial Hypervisor From Scratch. In this part, we’ll learn how to virtualize an already running system using our custom-made hypervisor. Like other parts, this part depends on the previous parts, so make sure to read them first.

Table of contents

  • Introduction
  • Table of contents
  • Overview
  • VMX 0-settings and 1-settings
  • VMX-Fixed Bits in CR0 and CR4
  • Capturing the State of the Current Machine
    • Configuring VMCS Fields
    • Changing IRQL on all Cores
  • Changing the User-Mode App
    • Getting a handle using CreateFile
  • Using VMX Monitoring Features
    • CR3-Target Controls
    • Handling guest CPUID execution
    • Prevent CPUID Timing Leakages
    • Instructions That Cause VM-exits Conditionally
    • Control Registers Modification Detection
    • MSR Bitmaps
      • Handling MSRs Read
      • Handling MSRs Write
  • Turning off VMX and Exit from Hypervisor
  • VM-Exit Handler
  • Let’s Test it!
    • Virtualizing all the cores
    • Changing CPUID using Hypervisor
    • Detecting MSR Read & Write (MSR Bitmap)
  • Conclusion
  • References

Overview

In the 6th part, we’ll see how we can virtualize our currently running system by configuring VMCS. We use monitoring features of VMX to detect the execution of important instructions like CPUID (and change the result of CPUID from user-mode and kernel-mode), detect modifications on different control registers, and describe VMX capabilities on different microarchitectures, talking about MSR Bitmaps and lots of other cool things.

Before starting, I should give my special thanks to my friend Petr Benes as he always solves my problems, explains to me patiently, and gives me ideas to implement a hypervisor from scratch.

The full source code of this tutorial is available on GitHub :

[https://github.com/SinaKarvandi/Hypervisor-From-Scratch]

Note: Remember that hypervisors change over time because new features are added to the operating systems or new technologies are used. For example, updates to Meltdown & Spectre have made a lot of changes to the hypervisors. So, if you want to use Hypervisor From Scratch in your projects, research, or whatever, you should use the HyperDbg drivers. HyperDbg is actively maintained, stable, and reliable, ensuring you avoid the errors and instability problems that can arise from using older parts of the tutorial series.

Please make sure to have your own lab to test your hypervisor. I tested my hypervisor on the 7th generation of Intel processors, so if you use an older processor, it might not support some features on your processor, and without a remote kernel debugger (not the local kernel debugger), you might see your system halting or BSODs without understanding the actual error.

VMX 0-settings and 1-settings

In the previous parts, we implemented a function called AdjustControl. This is an essential part of each hypervisor as you might want to run your hypervisor on many different processors with different microarchitectures. We should be aware of our processor capabilities to avoid undefined behaviors and VM-Entry errors.

This works like this; first, an MSR is sent the below function, which indicates the VMCS control that needs to be modified. Then we check the corresponding MSR to understand the 1-settings and 0-settings of the control. At last, we remove the not supported bits, set those that are mandatory to be 1, and configure the control.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
ULONG
+AdjustControls(ULONG Ctl, ULONG Msr)
+{
+    MSR MsrValue = {0};
+
+    MsrValue.Content = __readmsr(Msr);
+    Ctl &= MsrValue.High; /* bit == 0 in high word ==> must be zero */
+    Ctl |= MsrValue.Low;  /* bit == 1 in low word  ==> must be one  */
+    return Ctl;
+}
+

If you remember from the previous part, we used the above function in 4 situations.

1
+2
+3
+4
+5
+6
+
    __vmx_vmwrite(CPU_BASED_VM_EXEC_CONTROL, AdjustControls(CPU_BASED_ACTIVATE_MSR_BITMAP | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, MSR_IA32_VMX_PROCBASED_CTLS));
+    __vmx_vmwrite(SECONDARY_VM_EXEC_CONTROL, AdjustControls(CPU_BASED_CTL2_RDTSCP | CPU_BASED_CTL2_ENABLE_INVPCID | CPU_BASED_CTL2_ENABLE_XSAVE_XRSTORS, MSR_IA32_VMX_PROCBASED_CTLS2));
+
+    __vmx_vmwrite(PIN_BASED_VM_EXEC_CONTROL, AdjustControls(0, MSR_IA32_VMX_PINBASED_CTLS));
+    __vmx_vmwrite(VM_EXIT_CONTROLS, AdjustControls(VM_EXIT_IA32E_MODE /* | VM_EXIT_ACK_INTR_ON_EXIT */, MSR_IA32_VMX_EXIT_CTLS));
+    __vmx_vmwrite(VM_ENTRY_CONTROLS, AdjustControls(VM_ENTRY_IA32E_MODE, MSR_IA32_VMX_ENTRY_CTLS));
+

A brief look at APPENDIX A -VMX CAPABILITY REPORTING FACILITY shows the explanation about RESERVED CONTROLS AND DEFAULT SETTINGS. In Intel VMX, certain controls are reserved and must be set to a specific value (0 or 1) determined by the processor. The specific value to which a reserved control must be set is its default setting. These kinds of settings vary for each processor and microarchitecture, but in general, there are three types of classes :

  • Always-flexible: These have never been reserved.
  • Default0: These are (or have been) reserved with a default setting of 0.
  • Default1: They are (or have been) reserved with a default setting of 1.

Now, There are separate capability MSRs for pin-based VM-execution controls, primary processor-based VM-execution controls, VM-Entry Controls, VM-Exit Controls and secondary processor-based VM-execution controls.

These MSRs are used to check the above controls:

  • MSR_IA32_VMX_PROCBASED_CTLS
  • MSR_IA32_VMX_PROCBASED_CTLS2
  • MSR_IA32_VMX_EXIT_CTLS
  • MSR_IA32_VMX_ENTRY_CTLS
  • MSR_IA32_VMX_PINBASED_CTLS

In all of the above MSRs, bits 31:0 indicate the allowed 0-settings of these controls. VM entry allows control X (bit X) to be 0 if bit X in the MSR is cleared to 0; if bit X in the MSR is set to 1, VM entry fails if control X is 0. Meanwhile, bits 63:32 indicate the allowed 1-settings of these controls. VM entry allows control X to be 1 if bit 32+X in the MSR is set to 1; if bit 32+X in the MSR is cleared to 0, VM entry fails if control X is 1.

Although there are some exceptions, now, you should understand the purpose of AdjustControls as it first reads the MSR corresponding to the VM-execution control, then adjusts the 0-settings and 1-settings, and return the final result.

I recommend seeing the result of AdjustControls specifically for MSR_IA32_VMX_PROCBASED_CTLS and MSR_IA32_VMX_PROCBASED_CTLS2 as you might unintentionally set some of the bits to 1 so, you should have a plan for handling some VM-Exits based on your specific processor.

VMX-Fixed Bits in CR0 and CR4

For CR0, IA32_VMX_CR0_FIXED0 MSR (index 486H) and IA32_VMX_CR0_FIXED1 MSR (index 487H) and for CR4 IA32_VMX_CR4_FIXED0 MSR (index 488H) and IA32_VMX_CR4_FIXED1 MSR (index 489H) indicate how bits in CR0 and CR4 may be set in VMX operation. If bit X is 1 in IA32_VMX_CRx_FIXED0, then that bit of CRx is fixed to 1 in VMX operation. Similarly, if bit X is 0 in IA32_VMX_CRx_FIXED1, then that bit of CRx is fixed to 0 in VMX operation. It is always the case that if bit X is 1 in IA32_VMX_CRx_FIXEDx, then that bit is also 1 in IA32_VMX_CRx_FIXED1.

Capturing the State of the Current Machine

In the 5th part, we saw how to configure different VMCS fields and finally execute our instruction (HLT) under the guest context. This part is similar to the last part, with some minor changes in some VMCS attributes. Let’s review and see the differences.

The first thing you need to know is that you have to create different stacks for each core as we’re going to virtualize all the cores simultaneously. These stacks will be used whenever a VM-Exit occurs.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
    //
+    // Allocate stack for the VM Exit Handler
+    //
+    UINT64 VmmStackVa                  = ExAllocatePoolWithTag(NonPagedPool, VMM_STACK_SIZE, POOLTAG);
+    g_GuestState[ProcessorID].VmmStack = VmmStackVa;
+
+    if (g_GuestState[ProcessorID].VmmStack == NULL)
+    {
+        DbgPrint("[*] Error in allocating VMM Stack\n");
+        return FALSE;
+    }
+    RtlZeroMemory(g_GuestState[ProcessorID].VmmStack, VMM_STACK_SIZE);
+
+    DbgPrint("[*] VMM Stack for logical processor %d : %llx\n", ProcessorID, g_GuestState[ProcessorID].VmmStack);
+

As you can see from the above code, we use VmmStack for each core separately (defined in the VIRTUAL_MACHINE_STATE structure).

All the other things like clearing the VMCS state, loading VMCS, and executing VMLAUNCH are exactly the same as the previous part, so I don’t want to describe them again but see the function responsible for preparing our current core to be virtualized.

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
+
VOID
+VirtualizeCurrentSystem(int ProcessorID, PEPTP EPTP, PVOID GuestStack)
+{
+    DbgPrint("\n======================== Virtualizing Current System (Logical Core 0x%x) =============================\n", ProcessorID);
+
+    //
+    // Clear the VMCS State
+    //
+    if (!ClearVmcsState(&g_GuestState[ProcessorID]))
+    {
+        goto ErrorReturn;
+    }
+
+    //
+    // Load VMCS (Set the Current VMCS)
+    //
+    if (!LoadVmcs(&g_GuestState[ProcessorID]))
+    {
+        goto ErrorReturn;
+    }
+
+    DbgPrint("[*] Setting up VMCS for current system.\n");
+    SetupVmcsAndVirtualizeMachine(&g_GuestState[ProcessorID], EPTP, GuestStack);
+
+    //
+    // Change this hook (detect modification of MSRs using RDMSR & WRMSR)
+    //
+    // DbgPrint("[*] Setting up MSR bitmaps.\n");
+
+    DbgPrint("[*] Executing VMLAUNCH.\n");
+    __vmx_vmlaunch();
+
+    //
+    // if VMLAUNCH succeeds will never be here!
+    //
+    ULONG64 ErrorCode = 0;
+    __vmx_vmread(VM_INSTRUCTION_ERROR, &ErrorCode);
+    __vmx_off();
+    DbgPrint("[*] VMLAUNCH Error : 0x%llx\n", ErrorCode);
+    DbgBreakPoint();
+
+    DbgPrint("\n===================================================================\n");
+
+ReturnWithoutError:
+
+    __vmx_off();
+    DbgPrint("[*] VMXOFF Executed Successfully!\n");
+
+    return TRUE;
+
+    //
+    // Return With Error
+    //
+ErrorReturn:
+    DbgPrint("[*] Fail to setup VMCS!\n");
+
+    return FALSE;
+}
+

From the above code, SetupVmcsAndVirtualizeMachine is new, so let’s see what’s inside this function.

Configuring VMCS Fields

VMCS Fields are nothing new. We should configure these fields to manage the state of the virtualized core.

All the VMCS fields are the same as the last part, except for the configuration of VMCS control bits:

1
+2
+3
+4
+5
+
    DbgPrint("[*] MSR_IA32_VMX_PROCBASED_CTLS : 0x%llx\n", AdjustControls(CPU_BASED_ACTIVATE_MSR_BITMAP | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, MSR_IA32_VMX_PROCBASED_CTLS));
+    DbgPrint("[*] MSR_IA32_VMX_PROCBASED_CTLS2 : 0x%llx\n", AdjustControls(CPU_BASED_CTL2_RDTSCP | CPU_BASED_CTL2_ENABLE_INVPCID | CPU_BASED_CTL2_ENABLE_XSAVE_XRSTORS, MSR_IA32_VMX_PROCBASED_CTLS2));
+
+    __vmx_vmwrite(CPU_BASED_VM_EXEC_CONTROL, AdjustControls(CPU_BASED_ACTIVATE_MSR_BITMAP | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS , MSR_IA32_VMX_PROCBASED_CTLS));
+    __vmx_vmwrite(SECONDARY_VM_EXEC_CONTROL, AdjustControls(CPU_BASED_CTL2_RDTSCP | CPU_BASED_CTL2_ENABLE_INVPCID | CPU_BASED_CTL2_ENABLE_XSAVE_XRSTORS, MSR_IA32_VMX_PROCBASED_CTLS2));
+

As you can see, for the CPU_BASED_VM_EXEC_CONTROL, we set CPU_BASED_ACTIVATE_MSR_BITMAP; this way, we can enable the MSR BITMAP filter (described later in this part). Setting this field is somehow mandatory. As you might guess, Windows accesses lots of MSRs during a simple kernel execution, so if we don’t set this bit, then we’ll exit on each MSR access, and of course, our VMX Exit-Handler is called, hence clearing this bit to zero makes the system substantially slower.

For the SECONDARY_VM_EXEC_CONTROL, we use CPU_BASED_CTL2_RDTSCP to enable RDTSCP, CPU_BASED_CTL2_ENABLE_INVPCID to enable INVPCID and the CPU_BASED_CTL2_ENABLE_XSAVE_XRSTORS to enable XSAVE and XRSTORS.

It’s because I run the above code in my Windows 10 1809 and see that Windows uses INVPCID and XSAVE for its internal use (in the processors that support these features), so if you didn’t enable them before virtualizing the core, then it probably leads to error.

Note that RDTSCP reads the current value of the processor’s time-stamp counter into the EDX:EAX registers and also reads the value of the IA32_TSC_AUX MSR (address C0000103H) into the ECX register. This instruction adds ordering to RDTSC and makes performance measures more accurate than RDTSC.

INVPCID invalidates mappings in the translation lookaside buffers (TLBs) and paging-structure caches based on the process-context identifier (PCID), and XSAVE Performs a full or partial save of processor state components to the XSAVE area located at the memory address specified by the destination operand.

Please ensure to review the final value that you put on these fields as your processor might not support all these features, so you have to implement some additional functions or ignore some of them.

Nothing is left in this function except GuestStack, which is used as the GUEST_RSP. I’ll tell you what to put in this argument later.

1
+
__vmx_vmwrite(GUEST_RSP, (ULONG64)GuestStack);     //setup guest sp
+

OK, now the problem is from where we can start our hypervisor. I mean, how to save the state of a particular core, then execute the VMLAUNCH instruction on it, and then continue the rest of the execution.

For this purpose, I’ve changed the DrvCreate routine, so you must change CreateFile from the user-mode application (I will discuss it later). In fact, DrvCreate is the function responsible for putting all the cores in the VMX state. First, it queries the core’s count and then performs the necessary initialization for each core.

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
+
NTSTATUS
+DrvCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+{
+    DbgPrint("[*] DrvCreate Called !\n");
+
+    //
+    // *** Start Virtualizing Current System ***
+    //
+
+    //
+    // Initiating EPTP and VMX
+    //
+    PEPTP EPTP = InitializeEptp();
+    InitializeVmx();
+
+    int LogicalProcessorsCount = KeQueryActiveProcessorCount(0);
+
+    for (size_t i = 0; i < LogicalProcessorsCount; i++)
+    {
+        // Launching VM for Test (in the all logical processor)
+        int ProcessorID = i;
+
+        // Allocating VMM Stack
+        AllocateVmmStack(ProcessorID);
+
+        // Allocating MSR Bit
+        AllocateMsrBitmap(ProcessorID);
+
+        RunOnProcessor(i, EPTP, VmxSaveState);
+        DbgPrint("\n======================================================================================================\n", ProcessorID);
+    }
+
+    Irp->IoStatus.Status      = STATUS_SUCCESS;
+    Irp->IoStatus.Information = 0;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_SUCCESS;
+}
+

Our tiny driver is designed to be used in uni-core, two, three, and even all the cores. As you can see from the code below, it queries the logical processor count.

1
+
    int LogicalProcessorsCount = KeQueryActiveProcessorCount(0);
+

You can edit this line to virtualize a certain number of cores or just a specific core, but the above code virtualizes all the cores by default.

Changing IRQL on all Cores

There is a function called RunOnProcessor. This function takes processor ID as its first parameter, the EPTP pointer (explained in the 4th part) as the second parameter, and a particular routine called VmxSaveState as the third.

RunOnProcessor sets the processor affinity to a special core, then it raises the IRQL to Dispatch-Level so the Windows Scheduler can’t kick in to change the context; thus, it runs our routine, and when it returns from VmxSaveState, the currently running core is virtualized so it can lower the IRQL to what it was before. Now Windows can continue its normal execution while it is under the hypervisor’s governance. IRQL stands for Interrupt Request Level, a Windows-specific mechanism to manage interrupts or give priority by their level, so raising IRQL means your routine will execute with higher priority than normal Windows codes (PASSIVE\_LEVEL & APC_LEVEL). For more information, you can visit here.

The RunOnProcessor code is shown below.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
BOOLEAN
+RunOnProcessor(ULONG ProcessorNumber, PEPTP EPTP, PFUNC Routine)
+{
+    KIRQL OldIrql;
+
+    KeSetSystemAffinityThread((KAFFINITY)(1 << ProcessorNumber));
+
+    OldIrql = KeRaiseIrqlToDpcLevel();
+
+    Routine(ProcessorNumber, EPTP);
+
+    KeLowerIrql(OldIrql);
+
+    KeRevertToUserAffinityThread();
+
+    return TRUE;
+}
+

VmxSaveState has to save the state and call another function, VirtualizeCurrentSystem.

We have to use this function in the assembly file (VMXState.asm) as all VmxSaveState is implemented in assembly. For using a C function, in the assembly, we can write the function name and use the EXTERN keyword.

The following example shows how we can use the VirtualizeCurrentSystem in the assembly file.

1
+
EXTERN VirtualizeCurrentSystem:PROC
+

The VMXSaveState is implemented like this (in assembly):

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
+
VmxSaveState PROC
+
+	PUSH RAX
+	PUSH RCX
+	PUSH RDX
+	PUSH RBX
+	PUSH RBP
+	PUSH RSI
+	PUSH RDI
+	PUSH R8
+	PUSH R9
+	PUSH R10
+	PUSH R11
+	PUSH R12
+	PUSH R13
+	PUSH R14
+	PUSH R15
+
+	SUB RSP, 28h
+
+	; It a x64 FastCall function but as long as the definition of SaveState is the same
+	; as VirtualizeCurrentSystem, so we RCX & RDX both have a correct value
+	; But VirtualizeCurrentSystem also has a stack, so it's the third argument
+	; and according to FastCall, it should be in R8
+
+	MOV R8, RSP
+
+	CALL VirtualizeCurrentSystem
+
+	RET
+
+VmxSaveState ENDP
+

It first saves the state of all registers, subtracts the stack because of Shadow Space for fast call functions, and then puts RSP to **R8 and calls the VirtualizeCurrentSystem. RSP should be moved into the R8 (as I told you for GuestStack ) because the x64 fastcall parameter should be passed in this order: RCX, RDX, R8, R9 + Stack. This means that our third argument to this function is current RSP, and this value will be used as GUEST_RSP in the VMCS.

If the above function runs without error, we should never reach to ret instruction as the state will later continue in another function called VmxRestoreState.

As we can see in the VirtualizeCurrentSystem, which eventually calls SetupVmcsAndVirtualizeMachine, the GUEST_RIP is pointing to VmxRestoreState, so the first routine that executes in the current core is VmxRestoreState.

This function is defined like this :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
VmxRestoreState PROC
+
+	ADD RSP, 28h
+	POP R15
+	POP R14
+	POP R13
+	POP R12
+	POP R11
+	POP R10
+	POP R9
+	POP R8
+	POP RDI
+	POP RSI
+	POP RBP
+	POP RBX
+	POP RDX
+	POP RCX
+	POP RAX
+	
+	RET
+	
+VmxRestoreState ENDP
+

In the above function, first, we remove the Shadow Space and restore the registers state.

When we return to RunOnProcessor, it’s time to lower the IRQL.

This function will be called many times (based on our logical cores count), and eventually, all of our cores are under VMX operation, and now we are in the VMX non-root operation.

Changing the User-Mode App

Based on the above assumptions, we have to make some trivial changes in our user-mode application so after loading the driver, it can be used to notify kernel-mode code to start and finally end of loading the hypervisor.

Getting a handle using CreateFile

After some checks for the vendor and presence of hypervisor, now we have to call DrvCreate from the kernel-mode, and it’s through the CreateFile user-mode function.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
    HANDLE Handle = CreateFile("\\\\.\\MyHypervisorDevice",
+                               GENERIC_READ | GENERIC_WRITE,
+                               FILE_SHARE_READ |
+                                   FILE_SHARE_WRITE,
+                               NULL, /// lpSecurityAttirbutes
+                               OPEN_EXISTING,
+                               FILE_ATTRIBUTE_NORMAL |
+                                   FILE_FLAG_OVERLAPPED,
+                               NULL); /// lpTemplateFile
+
+    if (Handle == INVALID_HANDLE_VALUE)
+    {
+        DWORD ErrNum = GetLastError();
+        printf("[*] CreateFile failed : %d\n", ErrNum);
+        return 1;
+    }
+

CreateFile gives us a handle that can be used in our future functions to interact with our driver. Still, whenever we close the application or call CloseHandle in the user-mode, the DrvClose is automatically called in the kernel. DrvClose turns off the hypervisor and restores the state to what it was before (not virtualized).

Using VMX Monitoring Features

After configuring all the above fields, it’s time to use the monitoring features of the VMX. We’ll see how these features are unique in the case of security applications or reverse engineering tasks. As an extra resource, you can use HyperDbg Debugger. HyperDbg is a hypervisor-based debugger that allows us to use most of these VT-x features in our debugging journey.

CR3-Target Controls

The VM-execution control fields include a set of 4 CR3-target values and a CR3-target count. If you remember the VMCS fields that I presented before in the SetupVmcsAndVirtualizeMachine, you can see the following lines :

1
+2
+3
+4
+5
+
    __vmx_vmwrite(CR3_TARGET_COUNT, 0);
+    __vmx_vmwrite(CR3_TARGET_VALUE0, 0);
+    __vmx_vmwrite(CR3_TARGET_VALUE1, 0);
+    __vmx_vmwrite(CR3_TARGET_VALUE2, 0);
+    __vmx_vmwrite(CR3_TARGET_VALUE3, 0);
+

Intel defines CR3-Target controls like this :

An execution of MOV to CR3 in VMX non-root operation does not cause a VM exit if its source operand matches one of these values. If the CR3-target count is n, only the first n CR3-target values are considered.

The implementation of using this feature is like this :

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
+
BOOLEAN
+SetTargetControls(UINT64 CR3, UINT64 Index)
+{
+    //
+    // Index starts from 0 , not 1
+    //
+    if (Index >= 4)
+    {
+        //
+        // Not supported for more than 4 , at least for now :(
+        //
+        return FALSE;
+    }
+
+    UINT64 temp = 0;
+
+    if (CR3 == 0)
+    {
+        if (g_Cr3TargetCount <= 0)
+        {
+            //
+            // Invalid command as g_Cr3TargetCount cannot be less than zero
+            // s
+            return FALSE;
+        }
+        else
+        {
+            g_Cr3TargetCount -= 1;
+            if (Index == 0)
+            {
+                __vmx_vmwrite(CR3_TARGET_VALUE0, 0);
+            }
+            if (Index == 1)
+            {
+                __vmx_vmwrite(CR3_TARGET_VALUE1, 0);
+            }
+            if (Index == 2)
+            {
+                __vmx_vmwrite(CR3_TARGET_VALUE2, 0);
+            }
+            if (Index == 3)
+            {
+                __vmx_vmwrite(CR3_TARGET_VALUE3, 0);
+            }
+        }
+    }
+    else
+    {
+        if (Index == 0)
+        {
+            __vmx_vmwrite(CR3_TARGET_VALUE0, CR3);
+        }
+        if (Index == 1)
+        {
+            __vmx_vmwrite(CR3_TARGET_VALUE1, CR3);
+        }
+        if (Index == 2)
+        {
+            __vmx_vmwrite(CR3_TARGET_VALUE2, CR3);
+        }
+        if (Index == 3)
+        {
+            __vmx_vmwrite(CR3_TARGET_VALUE3, CR3);
+        }
+        g_Cr3TargetCount += 1;
+    }
+
+    __vmx_vmwrite(CR3_TARGET_COUNT, g_Cr3TargetCount);
+    return TRUE;
+}
+

I don’t have any good example of how this control might be helpful in a regular Windows as there are thousands of CR3 changes for each process. Still, one of my friends told me that it’s used in some special cases in scientific projects to improve the overall performance.

Handling guest CPUID execution

CPUID is an instruction that unconditionally causes VM-exit. As you know, CPUID is used because it allows the software to discover details of the processor. It is also used for flushing the pipeline for processors that don’t support instructions like RDTSCP, so they can use CPUID + RDTSC and use CPUID as a barrier.

Whenever any software in any privilege level executes a CPUID instruction, our vm-exit handler is called, and now we can decide whatever we want to show to the software. For example, previously, I published an article “Defeating malware’s Anti-VM techniques (CPUID-Based Instructions)”. This article describes how to configure VMware Workstation in a way that changes the CPUID instruction results so that the malware with an anti-VM technique can’t understand that they’re executing in a virtualized environment. VMware Workstation (and other virtual environments) perform the same mechanism for handling CPUID. In the following example, I just passed the state of registers (state of registers before the VM-exits) to the HandleCPUID. This function decides whether the requested CPUID should have a modified result or just execute passthrough the original results.

The default behavior for handling every VM-Exit (caused by execution of CPUID in VMX non-root) is to get the original result by using _cpuidex, which is the intrinsic function for CPUID.

1
+
    __cpuidex(CpuInfo, (INT32)state->rax, (INT32)state->rcx);
+

As you can see, VMX non-root by itself isn’t able to execute a CPUID, and we can execute CPUID in VMX root-mode and give back the results to the VMX non-root mode.

We need to check if RAX (CPUID Index) was 1 or not. It’s because there is an indicator bit that shows whether the current machine is running under a hypervisor or not. Like many other virtual machines, we set the present hypervisor bit (the constant used in this example is like hyper-v’s bit HYPERV_HYPERVISOR_PRESENT_BIT) to show that we’re running under a hypervisor.

There is a second check about the hypervisor provider. We set it to ‘HVFS’ to show that our hypervisor is [H]yper[V]isor [F]rom [S]cratch.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+
    //
+    // Check if this was CPUID 1h, which is the features request
+    //
+    if (state->rax == 1)
+    {
+        //
+        // Set the Hypervisor Present-bit in RCX, which Intel and AMD have both
+        // reserved for this indication
+        //
+        CpuInfo[2] |= HYPERV_HYPERVISOR_PRESENT_BIT;
+    }
+
+    else if (state->rax == HYPERV_CPUID_INTERFACE)
+    {
+        //
+        // Return our interface identifier
+        //
+        CpuInfo[0] = 'HVFS'; // [H]yper[V]isor [F]rom [S]cratch
+    }
+

We can easily add more checks to the above code and customize our CPUID filter, for instance, changing our computer vendor string, etc.

Finally, we put them into registers so that the guest has a proper result every time our routine is executed.

1
+2
+3
+4
+5
+6
+7
+
    //
+    // Copy the values from the logical processor registers into the VP GPRs
+    //
+    state->rax = CpuInfo[0];
+    state->rbx = CpuInfo[1];
+    state->rcx = CpuInfo[2];
+    state->rdx = CpuInfo[3];
+

Putting all the above codes together, we have the following function:

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
+
BOOLEAN
+HandleCPUID(PGUEST_REGS state)
+{
+    INT32 CpuInfo[4];
+    ULONG Mode = 0;
+
+    //
+    // Check for the magic CPUID sequence, and check that it is coming from
+    // Ring 0. Technically we could also check the RIP and see if this falls
+    // in the expected function, but we may want to allow a separate "unload"
+    // driver or code at some point
+    //
+
+    __vmx_vmread(GUEST_CS_SELECTOR, &Mode);
+    Mode = Mode & RPL_MASK;
+
+    if ((state->rax == 0x41414141) && (state->rcx == 0x42424242) && Mode == DPL_SYSTEM)
+    {
+        return TRUE; // Indicates we have to turn off VMX
+    }
+
+    //
+    // Otherwise, issue the CPUID to the logical processor based on the indexes
+    // on the VP's GPRs
+    //
+    __cpuidex(CpuInfo, (INT32)state->rax, (INT32)state->rcx);
+
+    //
+    // Check if this was CPUID 1h, which is the features request
+    //
+    if (state->rax == 1)
+    {
+        //
+        // Set the Hypervisor Present-bit in RCX, which Intel and AMD have both
+        // reserved for this indication
+        //
+        CpuInfo[2] |= HYPERV_HYPERVISOR_PRESENT_BIT;
+    }
+
+    else if (state->rax == HYPERV_CPUID_INTERFACE)
+    {
+        //
+        // Return our interface identifier
+        //
+        CpuInfo[0] = 'HVFS'; // [H]yper[V]isor [F]rom [S]cratch
+    }
+
+    //
+    // Copy the values from the logical processor registers into the VP GPRs
+    //
+    state->rax = CpuInfo[0];
+    state->rbx = CpuInfo[1];
+    state->rcx = CpuInfo[2];
+    state->rdx = CpuInfo[3];
+
+    return FALSE; // Indicates we don't have to turn off VMX
+}
+

It’s somehow like instruction level hooking for CPUID. Also, you can have the same handling functions for many other important instructions by configuring the primary and secondary processor-based controls. Later we describe some of these instructions.

Prevent CPUID Timing Leakages

As an extra explanation about the hypervisor, CPUID is one of the ways that cause user-mode or kernel-mode software to detect the presence of hypervisor by using delta timing side-channel attacks. It originates from the fact that this instruction leads to an unconditional VM-exit which, in the case of a hypervisor, it takes much longer to execute in contrast with a non-virtualized machine.

The description of these attacks is out of this article’s scope, but in case you’re interested, you can read a detailed explanation about these attacks in this paper.

Instructions That Cause VM-exits Conditionally

Here is a list of instructions that cause VM-exits in VMX non-root operation depending on the setting of the VM-execution controls. - CLTS - ENCLS - HLT - IN, INS/INSB/INSW/INSD, OUT, OUTS/OUTSB/OUTSW/OUTSD. - INVLPG - INVPCID - LGDT, LIDT, LLDT, LTR, SGDT, SIDT, SLDT, STR - LMSW - MONITOR - MOV from CR3/CR8, MOV to CR0/1/3/4/8 - MOV DR - MWAIT - PAUSE - RDMSR, WRMSR - RDPMC - RDRAND, RDSEED - RDTSC, RDTSCP - RSM - VMREAD, VMWRITE - WBINVD - XRSTORS, XSAVES

Control Registers Modification Detection

Detecting and handling Control Registers’ (CR) modifications is one of the great monitoring features provided by hypervisors.

Imagine if someone exploits the Windows Kernel (or any other OSs) and wants to unset one of the control register bits (let’s say Write Protected orSMEP); then the hypervisor detects this modification and prevents further execution.

Note that SMEP stands for Supervisor Mode Execution Protection. CR4.SMEP allows pages to be protected from supervisor-mode instruction fetches. If CR4.SMEP = 1, software operating in supervisor mode cannot fetch instructions from linear addresses that are accessible in user mode, and WP stands for Write Protect. CR0.WP allows pages to be protected from supervisor-mode writes. If CR0.WP = 0, supervisor-mode write accesses are allowed to linear addresses with read-only access rights; if CR0.WP = 1, they are not (User-mode write accesses are never allowed to linear addresses with read-only access rights, regardless of the value of CR0.WP).

Now it’s time to implement our functions.

First, let’s read the GUEST_CRs and EXIT_QUALIFICATION of the VMCS.

1
+2
+3
+4
+
    __vmx_vmread(EXIT_QUALIFICATION , &ExitQualification);
+    __vmx_vmread(GUEST_CR0 , &GuestCR0);
+    __vmx_vmread(GUEST_CR3 , &GuestCR3);
+    __vmx_vmread(GUEST_CR4,  &GuestCR4);
+

As you can see, the following picture shows how we can interpret Exit Qualifications.

Note that EXIT_QUALIFICATION is somehow a general VMCS field that, in some situations like VM-exits caused by Invalid VMCS Layout, Control Register Modifications, I/O Bitmaps, and other events, gives additional information about the reason for the VM-exit.

As you can see from the above picture, let’s make some variables to describe the situation based on EXIT_QUALIFICATION.

Whenever a VM-Exit occurs caused by instructions like MOV CRx, REG, we have to manually modify the CRx of guest VMCS from VMX-root mode. The following code shows how to change the GUEST_CRx field of VMCS using VMWRITE.

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
+
     case TYPE_MOV_TO_CR:
+    {
+        switch (data->Fields.ControlRegister)
+        {
+        case 0:
+            __vmx_vmwrite(GUEST_CR0, *RegPtr);
+            __vmx_vmwrite(CR0_READ_SHADOW, *RegPtr);
+            break;
+        case 3:
+
+            __vmx_vmwrite(GUEST_CR3, (*RegPtr & ~(1ULL << 63)));
+
+            //
+            // In the case of using EPT, the context of EPT/VPID should be
+            // invalidated
+            //
+            break;
+        case 4:
+            __vmx_vmwrite(GUEST_CR4, *RegPtr);
+            __vmx_vmwrite(CR4_READ_SHADOW, *RegPtr);
+            break;
+        default:
+            DbgPrint("[*] Unsupported register %d\n", data->Fields.ControlRegister);
+            break;
+        }
+    }
+    break;
+

Otherwise, we have to read the CRx from our guest VMCS (not host Control Register as it might be different), then put it into the corresponding registers (in registers that we saved when the VM-exit handler called), then continue with VMRESUME. This way, the guest thinks as if it executed the MOV reg, CRx successfully.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
    case TYPE_MOV_FROM_CR:
+    {
+        switch (data->Fields.ControlRegister)
+        {
+        case 0:
+            __vmx_vmread(GUEST_CR0, RegPtr);
+            break;
+        case 3:
+            __vmx_vmread(GUEST_CR3, RegPtr);
+            break;
+        case 4:
+            __vmx_vmread(GUEST_CR4, RegPtr);
+            break;
+        default:
+            DbgPrint("[*] Unsupported register %d\n", data->Fields.ControlRegister);
+            break;
+        }
+    }
+

Putting it all together, we have a function like this :

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
+
VOID
+HandleControlRegisterAccess(PGUEST_REGS GuestState)
+{
+    ULONG ExitQualification = 0;
+
+    __vmx_vmread(EXIT_QUALIFICATION, &ExitQualification);
+
+    PMOV_CR_QUALIFICATION data = (PMOV_CR_QUALIFICATION)&ExitQualification;
+
+    PULONG64 RegPtr = (PULONG64)&GuestState->rax + data->Fields.Register;
+
+    //
+    // Because its RSP and as we didn't save RSP correctly (because of pushes)
+    // so we have to make it points to the GUEST_RSP
+    //
+    if (data->Fields.Register == 4)
+    {
+        INT64 RSP = 0;
+        __vmx_vmread(GUEST_RSP, &RSP);
+        *RegPtr = RSP;
+    }
+
+    switch (data->Fields.AccessType)
+    {
+    case TYPE_MOV_TO_CR:
+    {
+        switch (data->Fields.ControlRegister)
+        {
+        case 0:
+            __vmx_vmwrite(GUEST_CR0, *RegPtr);
+            __vmx_vmwrite(CR0_READ_SHADOW, *RegPtr);
+            break;
+        case 3:
+
+            __vmx_vmwrite(GUEST_CR3, (*RegPtr & ~(1ULL << 63)));
+
+            //
+            // In the case of using EPT, the context of EPT/VPID should be
+            // invalidated
+            //
+            break;
+        case 4:
+            __vmx_vmwrite(GUEST_CR4, *RegPtr);
+            __vmx_vmwrite(CR4_READ_SHADOW, *RegPtr);
+            break;
+        default:
+            DbgPrint("[*] Unsupported register %d\n", data->Fields.ControlRegister);
+            break;
+        }
+    }
+    break;
+
+    case TYPE_MOV_FROM_CR:
+    {
+        switch (data->Fields.ControlRegister)
+        {
+        case 0:
+            __vmx_vmread(GUEST_CR0, RegPtr);
+            break;
+        case 3:
+            __vmx_vmread(GUEST_CR3, RegPtr);
+            break;
+        case 4:
+            __vmx_vmread(GUEST_CR4, RegPtr);
+            break;
+        default:
+            DbgPrint("[*] Unsupported register %d\n", data->Fields.ControlRegister);
+            break;
+        }
+    }
+    break;
+
+    default:
+        DbgPrint("[*] Unsupported operation %d\n", data->Fields.AccessType);
+        break;
+    }
+}
+

The reason why implementing functions like HandleControlRegisterAccess is necessary is because some processors have 1-settings of some processor-based VM-execution controls like CR3-Load Exiting & CR3-Store Existing, so we have to manage these kinds of VM-exits by ourselves, but if our processor can continue without these settings, it’s strongly recommended to reduce the amounts of VM-exits and avoid configuring the settings that lead to these kinds of VM-exits because modern OSs access control registers a lot; thus, it has a significant performance penalty.

MSR Bitmaps

Everything here is based on whether you set the 28th bit of Primary Processor Based controls or not.

On processors that support the 1-setting of the “use MSR bitmaps” VM-execution control, the VM-execution control fields include the 64-bit physical address of four contiguous MSR bitmaps, which are each 1-KByte in size.

The definition of MSR bitmap is pretty clear in Intel SDM, so I just copied them from the original manual. After reading them, we’ll start to implement them and put them into our hypervisor.

  • Read bitmap for low MSRs (located at the MSR-bitmap address). This contains one bit for each MSR address in the range 00000000H to 00001FFFH. The bit determines whether the execution of RDMSR applied to that MSR causes a VM-exit.

  • Read bitmap for high MSRs (located at the MSR-bitmap address plus 1024). This contains one bit for each MSR address in the range C0000000H toC0001FFFH. The bit determines whether the execution of RDMSR applied to that MSR causes a VM-exit.

  • Write bitmap for low MSRs (located at the MSR-bitmap address plus 2048). This contains one bit for each MSR address in the range 00000000H to 00001FFFH. The bit determines whether the execution of WRMSR applied to that MSR causes a VM-exit.

  • Write bitmap for high MSRs (located at the MSR-bitmap address plus 3072). This contains one bit for each MSR address in the range C0000000H toC0001FFFH. The bit determines whether the execution of WRMSR applied to that MSR causes a VM-exit.

OK, let’s bring the above sentences into the codes. First, we’ll write our handler for MSR VM-exits.

Handling MSRs Read**

In our passthrough hypervisor, if any of the RDMSR or WRMSR caused a VM-exit, we have to manually execute RDMSR or WRMSR and set the results into the registers. Because of this, we have a function to manage our RDMSRs like :

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
+
VOID
+HandleMSRRead(PGUEST_REGS GuestRegs)
+{
+    MSR msr = {0};
+
+    //
+    // RDMSR. The RDMSR instruction causes a VM exit if any of the following are true:
+    //
+    // The "use MSR bitmaps" VM-execution control is 0.
+    // The value of ECX is not in the ranges 00000000H - 00001FFFH and C0000000H - C0001FFFH
+    // The value of ECX is in the range 00000000H - 00001FFFH and bit n in read bitmap for low MSRs is 1,
+    //   where n is the value of ECX.
+    // The value of ECX is in the range C0000000H - C0001FFFH and bit n in read bitmap for high MSRs is 1,
+    //   where n is the value of ECX & 00001FFFH.
+    //
+
+    if (((GuestRegs->rcx <= 0x00001FFF)) || ((0xC0000000 <= GuestRegs->rcx) && (GuestRegs->rcx <= 0xC0001FFF)))
+    {
+        msr.Content = MSRRead((ULONG)GuestRegs->rcx);
+    }
+    else
+    {
+        msr.Content = 0;
+    }
+
+    GuestRegs->rax = msr.Low;
+    GuestRegs->rdx = msr.High;
+}
+

You can see that it just checks for the sanity of MSR and then executes the RDMSR and ultimately put the results into RAX and RDX (because a non-virtualized RDMSR does the same thing).

Handling MSRs Writes

There is another function for handling WRMSR VM-exits :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+
VOID
+HandleMSRWrite(PGUEST_REGS GuestRegs)
+{
+    MSR msr = {0};
+
+    //
+    // Check for the sanity of MSR
+    //
+    if ((GuestRegs->rcx <= 0x00001FFF) || ((0xC0000000 <= GuestRegs->rcx) && (GuestRegs->rcx <= 0xC0001FFF)))
+    {
+        msr.Low  = (ULONG)GuestRegs->rax;
+        msr.High = (ULONG)GuestRegs->rdx;
+        MSRWrite((ULONG)GuestRegs->rcx, msr.Content);
+    }
+}
+

The functionality of the function is simple. Still, one thing that is worth experimenting by yourself is to avoid setting CPU_BASED_ACTIVATE_MSR_BITMAP in CPU_BASED_VM_EXEC_CONTROL, you’ll see that all of the MSR reads and modifications will cause a VM-exit with these reasons :

  • EXIT_REASON_MSR_READ
  • EXIT_REASON_MSR_WRITE

This time, we have to pass everything to the above functions and log these VM-exits, so you can see what are MSRs that Windows use while running in the hypervisor. As I told you above, Windows executes a vast amount of MSR instructions, so it can make your system much slower than you can bear it.

OK, let’s get back to our MSR Bitmap. We need two functions to Set bits of our MSR Bitmap.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+
VOID
+SetBit(PVOID Addr, UINT64 Bit, BOOLEAN Set)
+{
+    PAGED_CODE();
+
+    UINT64 Byte = Bit / 8;
+    UINT64 Temp = Bit % 8;
+    UINT64 N    = 7 - Temp;
+
+    BYTE * Addr2 = Addr;
+    if (Set)
+    {
+        Addr2[Byte] |= (1 << N);
+    }
+    else
+    {
+        Addr2[Byte] &= ~(1 << N);
+    }
+}
+

The other function is for retrieving a particular bit.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
VOID
+GetBit(PVOID Addr, UINT64 Bit)
+{
+    UINT64 Byte = 0, K = 0;
+    Byte         = Bit / 8;
+    K            = 7 - Bit % 8;
+    BYTE * Addr2 = Addr;
+
+    return Addr2[Byte] & (1 << K);
+}
+
+

Now it’s time to gather everything in one function based on the above descriptions about MSR Bitmaps. The following function first checks for the sanity of MSR; then, it changes the MSR Bitmap of the target logical core (this is why we hold both the Physical Address and the Virtual Address of MSR Bitmap, the physical address for VMCS fields, and the virtual address to ease the modification and future deallocation). If it’s a read (RDMSR) for low MSRs, then set the corresponding bit in MSR Bitmap Virtual Address, if it’s a write (WRMSR) for the low MSRs, then modify the MSR Bitmap + 2048 (as noted in Intel manual) and exact the same thing for high MSRs (between 0xC0000000 and 0xC0001FFF) but don’t forget the subtraction (0xC0000000) because 0xC000nnnn is not a valid bit.

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
+
BOOLEAN
+SetMsrBitmap(ULONG64 Msr, int ProcessID, BOOLEAN ReadDetection, BOOLEAN WriteDetection)
+{
+    if (!ReadDetection && !WriteDetection)
+    {
+        //
+        // Invalid Command
+        //
+        return FALSE;
+    }
+
+    if (Msr <= 0x00001FFF)
+    {
+        if (ReadDetection)
+        {
+            SetBit(g_GuestState[ProcessID].MsrBitmap, Msr, TRUE);
+        }
+        if (WriteDetection)
+        {
+            SetBit(g_GuestState[ProcessID].MsrBitmap + 2048, Msr, TRUE);
+        }
+    }
+    else if ((0xC0000000 <= Msr) && (Msr <= 0xC0001FFF))
+    {
+        if (ReadDetection)
+        {
+            SetBit(g_GuestState[ProcessID].MsrBitmap + 1024, Msr - 0xC0000000, TRUE);
+        }
+        if (WriteDetection)
+        {
+            SetBit(g_GuestState[ProcessID].MsrBitmap + 3072, Msr - 0xC0000000, TRUE);
+        }
+    }
+    else
+    {
+        return FALSE;
+    }
+    return TRUE;
+}
+

Just one more thing to remember, only the above MSR ranges are currently valid in Intel processors, so even any other RDMSRs and WRMSRs cause a VM-exit. Still, the sanity check here is mandatory as the guest might send invalid MSRs and cause the whole system to crash (in VMX root-mode). In the future parts, when we learn about the event injection, we’ll simulate the physical machine’s behavior by injecting events to the guest in the case when the guest attempted to access an invalid MSR.

Turning off VMX and Exit from Hypervisor

It’s time to turn off our hypervisor and restore the processor state to what it was before running the hypervisor.

Like how we enter hypervisor (VMLAUNCH), we have to combine our C functions with Assembly routines to save the state, execute VMXOFF, free all of our previously allocated pools, and finally restore the state.

The VMXOFF part of this routine should be executed in the VMX root-mode. You can’t just execute __vmx_vmxoff in one of your driver functions and expect it turns off the hypervisor as Windows and all its drivers are currently running in VMX non-root, so executing any of the VMX instructions is like a VM-exit with one of the following reasons.

  • EXIT_REASON_VMCLEAR
  • EXIT_REASON_VMPTRLD
  • EXIT_REASON_VMPTRST
  • EXIT_REASON_VMREAD
  • EXIT_REASON_VMRESUME
  • EXIT_REASON_VMWRITE
  • EXIT_REASON_VMXOFF
  • EXIT_REASON_VMXON
  • EXIT_REASON_VMLAUNCH

For turning off the hypervisor, it’s better to use one of our IRP Major functions. In our case, we used DrvClose as it always gets notified whenever a handle to our device is closed. If you remember from the above, we create a handle from our device using CreateFile (DrvCreate), and now it’s time to close our handle using DrvClose.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
NTSTATUS
+DrvClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+{
+    DbgPrint("[*] DrvClose Called !\n");
+
+    // executing VMXOFF (From CPUID) on every logical processor
+    TerminateVmx();
+
+    Irp->IoStatus.Status      = STATUS_SUCCESS;
+    Irp->IoStatus.Information = 0;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_SUCCESS;
+}
+

Nothing special about the above function; only the TerminateVmx is added.

This function is similar to the routine of executing VMLAUNCH, except it runs VMXOFF instead.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
VOID
+TerminateVmx()
+{
+    DbgPrint("\n[*] Terminating VMX...\n");
+
+    int LogicalProcessorsCount = KeQueryActiveProcessorCount(0);
+
+    for (size_t i = 0; i < LogicalProcessorsCount; i++)
+    {
+        DbgPrint("\t\t + Terminating VMX on processor %d\n", i);
+        RunOnProcessorForTerminateVMX(i);
+
+        //
+        // Free the destination memory
+        //
+        MmFreeContiguousMemory(PhysicalToVirtualAddress(g_GuestState[i].VmxonRegion));
+        MmFreeContiguousMemory(PhysicalToVirtualAddress(g_GuestState[i].VmcsRegion));
+        ExFreePoolWithTag(g_GuestState[i].VmmStack, POOLTAG);
+        ExFreePoolWithTag(g_GuestState[i].MsrBitmap, POOLTAG);
+    }
+
+    DbgPrint("[*] VMX Operation turned off successfully. \n");
+}
+

As you can see, it executes RunOnProcessorForTerminateVMX on all the running logical cores. It then frees the allocated buffers for VmxonRegion, VmcsRegion, VmmStack, and MsrBitmap using MmFreeContiguousMemory and, of course, converts physicals to virtuals whenever needed.

Note that you have to modify this function if you virtualized a portion of cores (not all).

In RunOnProcessorForTerminateVMX, we must tell theVMX Root Operation about turning off the hypervisor. As I told you, it’s because we can’t execute any VMX instructions in the regular driver routines, and it’s pretty clear that VMX Root Operation can prevent us from this operation if there isn’t any mechanism for handling this situation or we’re not privileged enough to unload the hypervisor.

There are several ways to tell our VMX Root Operation about VMXOFF, but in our case, we’ll use CPUID.

By now, you definitely know that executing CPUID will cause VM-exit. Now in our CPUID exit handler routine, we manage that whenever a CPUID with RAX = 0x41414141 and RCX = 0x42424242 is executed, then we have to return true, and it shows the caller that the hypervisor needs to be off.

1
+2
+3
+4
+
    if ((state->rax == 0x41414141) && (state->rcx == 0x42424242) && Mode == DPL_SYSTEM)
+    {
+        return TRUE; // Indicates we have to turn off VMX
+    }
+

There is also another check for DPL to make sure that CPUID with RAX = 0x41414141 and RCX = 0x42424242 is only executed in the system privilege level (kernel-mode). Hence, none of the user-mode applications are able to unload our hypervisor.

1
+2
+3
+
    ULONG Mode = 0;
+    __vmx_vmread(GUEST_CS_SELECTOR, &Mode);
+    Mode = Mode & RPL_MASK;
+

Now our RunOnProcessorForTerminateVMX executes CPUID with adjusted values into registers on all cores separately.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
BOOLEAN
+RunOnProcessorForTerminateVMX(ULONG ProcessorNumber)
+{
+    KIRQL OldIrql;
+    INT32 CpuInfo[4];
+
+    KeSetSystemAffinityThread((KAFFINITY)(1 << ProcessorNumber));
+
+    OldIrql = KeRaiseIrqlToDpcLevel();
+
+    //
+    // Our routine is VMXOFF
+    //
+    __cpuidex(CpuInfo, 0x41414141, 0x42424242);
+
+    KeLowerIrql(OldIrql);
+
+    KeRevertToUserAffinityThread();
+
+    return TRUE;
+}
+

In the EXIT_REASON_CPUID handler, we know that if the handler returns true, then we have to turn it off, so we should think about some other things. For example, Windows expects to later continue from GUEST_RIP and needs its previous GUEST_RSP whenever the VM-exit handler returns; thus, we have to save them in some locations and use them later to restore the Windows state.

Also, we have to increase GUEST_RIP because we want to restore the state after the CPUID.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
    case EXIT_REASON_CPUID:
+    {
+        Status = HandleCPUID(GuestRegs); // Detect whether we have to turn off VMX or Not
+        if (Status)
+        {
+            // We have to save GUEST_RIP & GUEST_RSP somewhere to restore them directly
+
+            ULONG ExitInstructionLength = 0;
+            g_GuestRIP                  = 0;
+            g_GuestRSP                  = 0;
+            __vmx_vmread(GUEST_RIP, &g_GuestRIP);
+            __vmx_vmread(GUEST_RSP, &g_GuestRSP);
+            __vmx_vmread(VM_EXIT_INSTRUCTION_LEN, &ExitInstructionLength);
+
+            g_GuestRIP += ExitInstructionLength;
+        }
+        break;
+    }
+

From the 5th part, you probably know MainVmexitHandler is called from VmexitHandler (Assembly function from VMExitHandler.asm)

Let’s see it in detail.

First, we have to extern some previously defined variables.

1
+2
+
EXTERN g_GuestRIP:QWORD
+EXTERN g_GuestRSP:QWORD
+

Now our VmexitHandler works like this, whenever a VM-exit occurs, the target logical core executes VmexitHandler as it’s defined in HOST_RIP, and our RSP is set to HOST_RSP, then we have to save all the registers. It means we must create a structure that allows us to read and modify registers in a C-like structure.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+
typedef struct _GUEST_REGS
+{
+    ULONG64 rax; // 0x00         // NOT VALID FOR SVM
+    ULONG64 rcx;
+    ULONG64 rdx; // 0x10
+    ULONG64 rbx;
+    ULONG64 rsp; // 0x20         // rsp is not stored here on SVM
+    ULONG64 rbp;
+    ULONG64 rsi; // 0x30
+    ULONG64 rdi;
+    ULONG64 r8; // 0x40
+    ULONG64 r9;
+    ULONG64 r10; // 0x50
+    ULONG64 r11;
+    ULONG64 r12; // 0x60
+    ULONG64 r13;
+    ULONG64 r14; // 0x70
+    ULONG64 r15;
+} GUEST_REGS, *PGUEST_REGS;
+

Just push all the registers in the GUEST_REGS structure order and push the RSP as the first argument to MainVmexitHandler (Fastcall RCX), then some subtraction for Shadow Space.

You can see the VmexitHandler here:

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
+
VmexitHandler PROC
+
+    PUSH R15
+    PUSH R14
+    PUSH R13
+    PUSH R12
+    PUSH R11
+    PUSH R10
+    PUSH R9
+    PUSH R8        
+    PUSH RDI
+    PUSH RSI
+    PUSH RBP
+    PUSH RBP	; RSP
+    PUSH RBX
+    PUSH RDX
+    PUSH RCX
+    PUSH RAX	
+
+
+	MOV RCX, RSP		; Fast CALL argument to PGUEST_REGS
+	SUB	RSP, 28h		; Free some space for Shadow Section
+
+	CALL	MainVmexitHandler
+
+	ADD	RSP, 28h		; Restore the state
+
+	; Check whether we have to turn off VMX or Not (the result is in RAX)
+
+	CMP	AL, 1
+	JE		VmxoffHandler
+
+	; Restore the state
+	POP RAX
+    POP RCX
+    POP RDX
+    POP RBX
+    POP RBP		; RSP
+    POP RBP
+    POP RSI
+    POP RDI 
+    POP R8
+    POP R9
+    POP R10
+    POP R11
+    POP R12
+    POP R13
+    POP R14
+    POP R15
+
+	SUB RSP, 0100h ; to avoid error in future functions
+
+	JMP VmResumeInstruction
+	
+
+VmexitHandler ENDP
+

From the above code, when we return from the MainVmexitHandler, we have to check whether the return result of MainVmexitHandler (in RAX) tells us to turn off the hypervisor or just continue.

If it needs to be continued, restore the registers state and jump to our VmResumeInstruction function.

VmResumeInstruction executes __vmx_vmresume and the processor sets the RIP register to GUEST_RIP.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
VOID
+VmResumeInstruction()
+{
+    ULONG64 ErrorCode = 0;
+
+    __vmx_vmresume();
+
+    //
+    // if VMRESUME succeeds will never be here!
+    //
+    __vmx_vmread(VM_INSTRUCTION_ERROR, &ErrorCode);
+    __vmx_off();
+    DbgPrint("[*] VMRESUME Error : 0x%llx\n", ErrorCode);
+
+    //
+    // It's such a bad error because we don't where to go
+    // prefer to break
+    //
+    DbgBreakPoint();
+}
+

But what if it needs to be turned off?

Then based on the AL register, it jumps to another function called VmxoffHandler. This function executes the VMXOFF instruction, turns off the hypervisor (in the current logical core), and then restores the registers to their previous state as we saved them in VmexitHandler.

The only thing we have to do here is changing the stack pointer to GUEST_RSP (We saved them in g_GuestRSP) and jump to the GUEST_RIP (saved in g_GuestRIP).

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
+
VmxoffHandler PROC
+
+	; Turn VMXOFF
+	VMXOFF
+
+	; Restore the state
+
+	POP RAX
+    POP RCX
+    POP RDX
+    POP RBX
+    POP RBP		; RSP
+    POP RBP
+    POP RSI
+    POP RDI 
+    POP R8
+    POP R9
+    POP R10
+    POP R11
+    POP R12
+    POP R13
+    POP R14
+    POP R15
+
+	; Set guest RIP and RSP
+
+	MOV		RSP, g_GuestRSP
+
+	JMP		g_GuestRIP
+
+VmxoffHandler ENDP
+

Now everything is done, we executed our normal Windows (driver) routine; I mean, start the execution after the last CPUID that was executed from RunOnProcessorForTerminateVMX but now we’re not in VMX operation.

VM-Exit Handler

Putting all the above codes together, now we have to manage different kinds of VM-exits, so we need to modify our previously explained (in the 5th part) VM-exit handler; if you forget about it, please review the 5th part (VM-Exit Handler), it’s exactly the same but with different actions for various exit reasons.

The first thing we need to manage is to detect every VMX instructions that are executed in VMX non-root operation; it can be done using the following code :

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
+
    //
+    // 25.1.2  Instructions That Cause VM Exits Unconditionally
+    // The following instructions cause VM exits when they are executed in VMX non-root operation: CPUID, GETSEC,
+    // INVD, and XSETBV. This is also true of instructions introduced with VMX, which include: INVEPT, INVVPID,
+    // VMCALL, VMCLEAR, VMLAUNCH, VMPTRLD, VMPTRST, VMRESUME, VMXOFF, and VMXON.
+    //
+    case EXIT_REASON_VMCLEAR:
+    case EXIT_REASON_VMPTRLD:
+    case EXIT_REASON_VMPTRST:
+    case EXIT_REASON_VMREAD:
+    case EXIT_REASON_VMRESUME:
+    case EXIT_REASON_VMWRITE:
+    case EXIT_REASON_VMXOFF:
+    case EXIT_REASON_VMXON:
+    case EXIT_REASON_VMLAUNCH:
+    {
+        // DbgBreakPoint();
+
+        /*	DbgPrint("\n [*] Target guest tries to execute VM Instruction ,"
+                "it probably causes a fatal error or system halt as the system might"
+                " think it has VMX feature enabled while it's not available due to our use of hypervisor.\n");
+                */
+
+        ULONG RFLAGS = 0;
+        __vmx_vmread(GUEST_RFLAGS, &RFLAGS);
+        __vmx_vmwrite(GUEST_RFLAGS, RFLAGS | 0x1); // cf=1 indicate vm instructions fail
+        break;
+    }
+

As I told you in DbgPrint, executing these kinds of VMX instructions will eventually cause BSOD because there might be some checks for the presence of a hypervisor before our hypervisor comes. Hence, the routine that executes these instructions (of course, it’s from the kernel) probably thinks it can execute these instructions. If it didn’t manage them well (which is common), you’ll see BSOD. Thus, you have to discover the cause of invoking these kinds of instructions and manually disable them.

If you configured any CPU-based controls or your processor support 1-settings of any of the CR Access Exit controls, you could manage them using the following VM-exit.

1
+2
+3
+4
+5
+
    case EXIT_REASON_CR_ACCESS:
+    {
+        HandleControlRegisterAccess(GuestRegs);
+        break;
+    }
+

The same thing is true for MSRs accesses. If we didn’t set any MSR Bit, every RDMSR and WRMSR cause to exit, or if we set any bits in MsrBitmap, then we have to manage them using the following function for RDMSR:

1
+2
+3
+4
+5
+6
+7
+8
+9
+
    case EXIT_REASON_MSR_READ:
+    {
+        ULONG ECX = GuestRegs->rcx & 0xffffffff;
+
+        // DbgPrint("[*] RDMSR (based on bitmap) : 0x%llx\n", ECX);
+        HandleMSRRead(GuestRegs);
+
+        break;
+    }
+

And this code for managing WRMSR:

1
+2
+3
+4
+5
+6
+7
+8
+9
+
    case EXIT_REASON_MSR_WRITE:
+    {
+        ULONG ECX = GuestRegs->rcx & 0xffffffff;
+
+        DbgPrint("[*] WRMSR (based on bitmap) : 0x%llx\n", ECX);
+        HandleMSRWrite(GuestRegs);
+
+        break;
+    }
+

And if you want to detect I/O instruction execution, then:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
    case EXIT_REASON_IO_INSTRUCTION:
+    {
+        UINT64 RIP = 0;
+        __vmx_vmread(GUEST_RIP, &RIP);
+
+        DbgPrint("[*] RIP executed IO instruction : 0x%llx\n", RIP);
+        DbgBreakPoint();
+
+        break;
+    }
+

Don’t forget to set adequate CPU-based control fields if you want to use the above functionalities.

The last thing that is important for us is the CPUID Handler. It calls HandleCPUID (described above), and if the result is true, then it saves the GUEST_RSP and GUEST_RIP so that these values can be used to restore the state after VMXOFF is executed in the target core.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
    case EXIT_REASON_CPUID:
+    {
+        Status = HandleCPUID(GuestRegs); // Detect whether we have to turn off VMX or Not
+        if (Status)
+        {
+            // We have to save GUEST_RIP & GUEST_RSP somewhere to restore them directly
+
+            ULONG ExitInstructionLength = 0;
+            g_GuestRIP                  = 0;
+            g_GuestRSP                  = 0;
+            __vmx_vmread(GUEST_RIP, &g_GuestRIP);
+            __vmx_vmread(GUEST_RSP, &g_GuestRSP);
+            __vmx_vmread(VM_EXIT_INSTRUCTION_LEN, &ExitInstructionLength);
+
+            g_GuestRIP += ExitInstructionLength;
+        }
+        break;
+    }
+

Let’s Test it!

Now it’s time to test our hypervisor.

Virtualizing all the cores

First, we have to load our driver.

Then our DriverEntry is called, so we have to run our user-mode application to virtualize all the cores.

You can see that if you press any key or close this window, it’ll call DrvClose and restores the state (VMXOFF).

The above picture shows the driver logs. At this point, all the cores are now under the hypervisor.

Changing CPUID using Hypervisor

Now let’s test the presence of the hypervisor. For this case, I used Immunity Debugger to execute CPUID with custom EAX. You can use any other debugger or any custom application.

We have to manually set the EAX to 0x40000001 or the HYPERV_CPUID_INTERFACE and then execute CPUID.

As you can see, HVFS (0x48564653) is on EAX, so we successfully hooked the CPUID execution using our hypervisor.

The above picture shows the HYPERV_CPUID_INTERFACE without the hypervisor.

At last, we have to close the user-mode app window, so it executes VMXOFF on all cores. Let’s test the above example again.

You can see that the actual results have appeared as we’re no longer under the hypervisor.

Detecting MSR Read & Write (MSR Bitmap)

In order to test MSR Bitmaps, I create a local kernel debugger (using WinDbg). In WinDbg, we can execute rdmsr and wrmsr commands to read and write into MSRs. It’s exactly like executing RDMSR and WRMSR using a system driver.

In the VirtualizeCurrentSystem function, the following line is added.

1
+
    SetMSRBitmap(0xc0000082, ProcessorID, TRUE, TRUE);
+

In WinDbg Local Debugger, we executed the above commands, and in the remote debugger, we can see the result as follows,

As you can see, the execution of RDMSR is detected. Our hypervisor is working perfectly!

That’s it all, folks.

Conclusion

In this part, we saw how we could virtualize an already running system by configuring the VMCS fields separately for each logical core. Then we used our hypervisor to change the result of the CPUID instruction and monitor every access to control registers or MSRs. After this part, our hypervisor is almost ready to be used for a practical project. The future part is about using the Extended Page Table (as described previously in the 4th part). I believe most of the exciting works in hypervisor can be performed using EPT because it has a special logging mechanism, e.g., page read/write access detection and many other cool things you’ll see in the next parts.

See you in the next part.

The seventh part is also available here.

References

[1] Vol 3C – Chapter 24 – (VIRTUAL MACHINE CONTROL STRUCTURES (https://software.intel.com/en-us/articles/intel-sdm)

[2] cpu-internals (https://github.com/LordNoteworthy/cpu-internals)

[3] RDTSCP — Read Time-Stamp Counter and Processor ID (https://www.felixcloutier.com/x86/rdtscp)

[4] INVPCID — Invalidate Process-Context Identifier (https://www.felixcloutier.com/x86/invpcid)

[5] XSAVE — Save Processor Extended States (https://www.felixcloutier.com/x86/xsave)

[6] XRSTORS — Restore Processor Extended States Supervisor (https://www.felixcloutier.com/x86/xrstors)

[7] What is IRQL ? (https://blogs.msdn.microsoft.com/doronh/2010/02/02/what-is-irql/)

This post is licensed under CC BY 4.0 by the author.

PacketScript overview: A Lua scripting engine for in-kernel packet processing

Why you should not always trust MSDN: Finding Real Access Rights Needed By Handles

Comments powered by Disqus.

diff --git a/topics/hypervisor-from-scratch-part-7/index.html b/topics/hypervisor-from-scratch-part-7/index.html new file mode 100644 index 00000000..9e818640 --- /dev/null +++ b/topics/hypervisor-from-scratch-part-7/index.html @@ -0,0 +1,2341 @@ + Hypervisor From Scratch – Part 7: Using EPT & Page-Level Monitoring Features | Rayanfam Blog
Home Hypervisor From Scratch – Part 7: Using EPT & Page-Level Monitoring Features
Post
Cancel

Hypervisor From Scratch – Part 7: Using EPT & Page-Level Monitoring Features

If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous practical examples on how to utilize hypervisors for reverse engineering.

Introduction

This is the 7th part of the tutorial Hypervisor From Scratch, and it’s about using the Extended Page Table (EPT) in an already running system. As you might know, paging is an essential part of managing memory on modern operating systems. Hypervisors use an additional paging table; this gives us an excellent opportunity to monitor different aspects of memory (Read-Write-Execute) without modifying the operating systems page-tables. EPT is a hardware mechanism, so it’s fast, but on the other hand, we have to deal with different caching and synchronization problems.

This part is highly dependent on the 4th part of the tutorial - Part 4: Address Translation Using Extended Page Table (EPT), so please read this part one more time; thus, I avoid redescribing about the basic concept relating to EPT Tables.

In the 7th part, we’ll see how we can virtualize our currently running system by configuring VMCS and creating identity tables based on Memory Type Range Register (MTRR) then we use monitoring features to detect the execution of some of the Windows functions.

This part is highly inspired by Simplevisor and Gbhv.

The picture of this post was taken by one of my best friends Ahmad, from Khānābād Village, Aligudarz.

Before starting, I should give special thanks to my friend Petr Benes for his contributions to Hypervisor From Scratch, of course, Hypervisor From Scratch could never exist without his help. I also give my regards to Alex Ionescu as he always answers my question patiently.

Overview

This part is divided into seven main sections :

  1. Implementing mechanisms to manage Vmcalls
  2. Starting with MMU Virtualization (EPT)
  3. Explaining Memory Type Range Register (MTRR) concepts
  4. Describing Page-Level Monitoring features using EPT
  5. Invalidating Translations Derived from EPT (INVEPT)
  6. Fixing some previous design caveat regarding deadlocks and synchronization problems
  7. Discussion (In this section we discuss the different question(s) and approaches about EPT)

At last, I talk about some important notes you need to know in order to debug hypervisor and EPT.

Guys, it’s ok if you didn’t understand some of the parts, by reading this article, you’ll get an idea, you could use EPT and over the time you’ll understand things better.

The source code of this part changed drastically compared to the previous part; naming conventions are improved, so you see a much cleaner and readable code; also lots of new routines added to the code, for examples routines starting with Hv are hypervisor routines, you have to call them from IRP Major functions and avoid calling methods with Vmx prefix directly as these functions manage the operations relating to VMX Operations, functions with Asm prefix are inline-assembly functions and functions starting with Ept are those that relate to Extended Page Table (EPT). Also, functions with Vmcall prefix are for VMCALL services, and functions with Invept are related to Invalidate EPT caches.

The full source code of this tutorial is available on GitHub :

[https://github.com/SinaKarvandi/Hypervisor-From-Scratch]

Note: Remember that hypervisors change over time because new features are added to the operating systems or new technologies are used. For example, updates to Meltdown & Spectre have made a lot of changes to the hypervisors. So, if you want to use Hypervisor From Scratch in your projects, research, or whatever, you should use the HyperDbg drivers. HyperDbg is actively maintained, stable, and reliable, ensuring you avoid the errors and instability problems that can arise from using older parts of the tutorial series.

Table of Contents

  • Introduction
  • Overview
  • Table of Contents
  • Implementing Functions to Manage Vmcalls
  • Starting with MMU virtualization (EPT)
  • Memory Type Range Register (MTRR)
    • Building MTRR Map
    • Fixed-Range MTRRs and PAT
  • Virtualizing Current System’s Memory using EPT

    • EPT Identity Mapping
    • Setting up PML4 and PML3 entries
    • Setting up PML2 entries

    • EPT Violation
    • EPT Misconfiguration
    • Adding EPT to VMCS
  • Monitoring Page’s RWX Activity

    • Pre-allocating Buffers for VMX Root Mode
    • Setting hook before Vmlaunch
    • Setting hook after Vmlaunch
    • Finding a Page’s entry in EPT Tables
      1. Finding PML4, PML3, PML2 entries
      2. Finding PML1 entry
    • Splitting 2 MB Pages to 4 KB Pages
    • Applying the Hook

    • Handling hooked pages’ vm-exits
  • Invalidating Translations Derived from EPT (INVEPT)
    • Invalidating All Contexts
    • Invalidating Single Context
    • Broadcasting Invept to all logical cores simultaneously
  • Fixing Previous Design Issues
    • Support to more than 64 logical cores
    • Synchronization problem in exiting VMX
    • The issues relating to the Meltdown mitigation
  • Some tips for debugging hypervisors
  • Let’s Test it!
    • How to test?
    • Demo
  • Discussion
  • Conclusion
  • References

Aniiiiime :)

Implementing Functions to Manage Vmcalls

We start this article by implementing functions relating to VMCALL. Intel describes Vmcall by “Call to VM monitor by causing VM exit.”.

Vmcall allows guest software to call for service into an underlying VM monitor. The details of the programming interface for such calls are VMM-specific. This instruction does nothing more than cause a VM exit.

In other words, whenever you execute a Vmcall instruction in Vmx non-root mode (whenever a vm-exit occurs, we are in vmx root-mode, and we stay in vmx root mode until we execute VMRESUME or VMXOFF so any other contexts is vmx non-root mode means that other drivers can use Vmcall in their contexts to request a service from our hypervisor in vmx root mode).

Execution of VMCALL causes a Vm-exit (EXIT_REASON_VMCALL). As we can set registers and stack before execution of VMCALL so we can send parameters to the Vmcall handler, I mean all we need to do is designing a calling-convention so that both vmcall handler and driver which requests a service can work together perfectly.

The first thing we need to implement is a function in assembly, which executes VMCALL and returns.

1
+2
+3
+4
+
AsmVmxVmcall PROC
+    vmcall                  ; VmxVmcallHandler(UINT64 VmcallNumber, UINT64 OptionalParam1, UINT64 OptionalParam2, UINT64 OptionalParam3)
+    ret                     ; Return type is NTSTATUS and it's on RAX from the previous function, no need to change anything
+AsmVmxVmcall ENDP
+

It defines like this,

1
+
extern NTSTATUS inline AsmVmxVmcall(unsigned long long VmcallNumber, unsigned long long OptionalParam1, unsigned long long OptionalParam2, unsigned long long OptionalParam3);
+

What distinguished from the above code is that we’re not modifying anything in AsmVmxVmcall, means that if someone passes the parameters to the AsmVmxVmcall, then the parameters are in RCX, RDX, R8, R9 and rest of them into the stack, that’s because of x64 FAST CALL calling convention.

Keep in mind that if you’re designing hypervisor for Linux, fast call in Linux is different from the fast-call in Windows.

As we saved all the registers on vm-exit so in vm-exit handler we pass the GuestRegs->rcx, GuestRegs->rdx, GuestRegs->r8, GuestRegs->r9 to the VmxVmcallHandler, the RCX is the Vmcall Number which specifies the service we want our hypervisor to perform and RDX and R8 and R9 are optional parameters.

1
+2
+3
+4
+5
+
	case EXIT_REASON_VMCALL:
+	{
+		GuestRegs->rax = VmxVmcallHandler(GuestRegs->rcx, GuestRegs->rdx, GuestRegs->r8, GuestRegs->r9);
+		break;
+	}
+

For example, we have the following services (Vmcall Numbers) for our hypervisor in this part.

1
+2
+3
+4
+5
+6
+
+#define VMCALL_TEST						0x1			// Test VMCALL
+#define VMCALL_VMXOFF					0x2			// Call VMXOFF to turn off the hypervisor
+#define VMCALL_EXEC_HOOK_PAGE			0x3			// VMCALL to Hook ExecuteAccess bit of the EPT Table
+#define VMCALL_INVEPT_ALL_CONTEXT		0x4			// VMCALL to invalidate EPT (All Contexts)
+#define VMCALL_INVEPT_SINGLE_CONTEXT	0x5			// VMCALL to invalidate EPT (A Single Context)
+

There is nothing special for VmxVmcallHandler, it’s just a simple switch case.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
/* Main Vmcall Handler */
+NTSTATUS VmxVmcallHandler(UINT64 VmcallNumber, UINT64 OptionalParam1, UINT64 OptionalParam2, UINT64 OptionalParam3)
+{
+	NTSTATUS VmcallStatus;
+	BOOLEAN HookResult;
+
+	VmcallStatus = STATUS_UNSUCCESSFUL;
+	switch (VmcallNumber)
+	{
+	case VMCALL_TEST:
+	{
+		VmcallStatus = VmcallTest(OptionalParam1, OptionalParam2, OptionalParam3);
+		break;
+	}
+	default:
+	{
+		LogWarning("Unsupported VMCALL");
+		VmcallStatus = STATUS_UNSUCCESSFUL;
+		break;
+	}
+	}
+	return VmcallStatus;
+}
+

For testing it, I created a function called VmcallTest, it simply shows the parameters passed to Vmcall.

1
+2
+3
+4
+5
+6
+
/* Test Vmcall (VMCALL_TEST) */
+NTSTATUS VmcallTest(UINT64 Param1, UINT64 Param2, UINT64 Param3) {
+
+	LogInfo("VmcallTest called with @Param1 = 0x%llx , @Param2 = 0x%llx , @Param3 = 0x%llx", Param1, Param2, Param3);
+	return STATUS_SUCCESS;
+}
+

Finally, we can use the following piece of code and pass VMCALL_TEST as the Vmcall Number along with other optional parameters.

1
+2
+
//  Check if everything is ok then return true otherwise false
+AsmVmxVmcall(VMCALL_TEST, 0x22, 0x333, 0x4444);
+

Don’t forget that the above code should bee only executed in vmx non-root mode.

There is nothing more I can say about VMCALL, but for further reading (not related to our hypervisor), if you want to know what happens if you execute VMCALL in vmx root-mode, it invokes an SMM monitor. This invocation will activate the dual-monitor treatment of system-management interrupts (SMIs) and system-management mode (SMM) if it is not already active. In other words, executing Vmcall in vmx root mode causes an SMM VM exit!

Read Section 34.15.2 and Section 34.15.6 in Intel SDM for more information.

Starting with MMU virtualization (EPT)

Let me start with differences between physical and virtual address,

Physical addressing means that your program knows the real layout of RAM. When you access a variable at address 0x8746b3, that’s where it stored in the physical RAM chips.

With virtual addressing, all application memory accesses go to a page table, which then maps from the virtual to the physical address. So every application has its own “private” address space, and no program can read or write to another program’s memory. 

EPT is a page table with a page-walk length of 4 (or in the newer versions 5). It translates guest-physical addresses to host-physical addresses.

First, you have to understand that EPT maps guest physical pages to host physical pages, mapping physical addresses make hypervisors much easier to understand because you can forget about all the concepts relating to virtual memory and operating system’s memory manager. Why? That’s because you cannot allocate more physical memory. Sure, you can hot-plug RAM right into the motherboard, but let’s forget about that for now 😉 , so the RAM usually starts at 0 and usually ends at AMOUNT OF RAM + SOME MORE, where SOME MORE is some MMIO/device space.

Look at the following picture (from hvpp), Memory Ranges from VMWare VM with 2 GB of RAM.

Memory Ranges

Note the holes between ranges (e.g., A0000 - 100000); the ranges in the screenshot are backed by actual physical RAM, and the holes are the MMIO space.

By now, you know that if you allocate or free memory, the RAM ranges are always present and what changes are the content of data in the RAM.

Keep in mind, there are certainly no holes in the RAM as an electronic circuit, but it’s how BIOS maps certain physical memory ranges to the actual hardware RAM, in other words, RAM usually isn’t one contiguous address space, if you have 1 GB of RAM it’s often not one single piece of 0 … 1GB physical address space, but some parts of that space belongs to, e.g. network card, audio card, USB hub, etc.

Let’s see what hypervisors like VMWare, Hyper-V, VirtualBox do with physical memory. We don’t have the same approach, but it helps you understand MMU virtualization better.

In VMWare (Hyper-v, VirtualBox, etc), the VM has its own physical memory, and our PC (host) also has some physical address space. EPT exists so that you can translate the guest physical memory to host physical memory. For example, if a guest wants to read from Physical Address 0x1000, it looks into EPT, and EPT tells it that the content of the memory is on the host’s physical address 0x5000. You certainly do not want to let some guests in VMWare read physical memory on the host, so it’s VMWare’s job to setup EPTs correctly and have some chunk of physical memory dedicated to a guest.

Memory Type Range Register (MTRR)

By now, you have some idea about how memory (RAM) is divided into regions; these regions can be found using MTRR registers, that’s all!

Now let’s explain them more precisely.

Wikipedia defines MTRRs like this :

Memory type range registers (MTRRs) are a set of processor supplementary capabilities control registers that provide system software with control of how accesses to memory ranges by the CPU are cached. It uses a set of programmable model-specific registers (MSRs), which are special registers provided by most modern CPUs. Possible access modes to memory ranges can be uncached, write-through, write-combining, write-protect, and write-back. In write-back mode, writes are written to the CPU’s cache, and the cache is marked dirty so that its contents are written to memory later.

In old x86 architecture systems, mainly where separate chips provided the cache outside of the CPU package, this function was controlled by the chipset itself and configured through BIOS settings, when the CPU cache was moved inside the CPU, the CPUs implemented fixed-range MTRRs.

Typically, the BIOS software configures the MTRRs. The operating system or executive is then free to modify the memory map using the typical page-level cacheability attributes.

If you confused by reading the above sentences, let me explain it more clearly. RAM is divided into different regions, We want to read the details (Base Address, End Address, and Cache Policy) of these chunks using MTRR Registers. Cache policy is something that BIOS or Operating System sets for a particular region. For example, the operating system decides to put UC (uncached) to a region that starts from 0x1000 to 0x2000 (Physical Address) of RAM then it chooses to put WB (Writeback) to a region starting from 0x5000 to 0x7000 (Physical Address), it’s based on OS policy. If you don’t know about the different memory type caches (e.g., UC, WB), you can read here.

OK, let’s see how to read these MTRRs.

The availability of the MTRR feature is model-specific means that we can determine if MTRRs are supported on a processor by executing the CPUID instruction and reading the state of the MTRR flag (bit 12) in the feature information register (EDX). Still, This check is not essential as our process probably supports as it’s an old feature.

What is essential for us, is an MSR called “IA32_MTRR_DEF_TYPE”. The following structure represents the IA32_MTRR_DEF_TYPE :

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
+
// MSR_IA32_MTRR_DEF_TYPE 
+typedef union _IA32_MTRR_DEF_TYPE_REGISTER
+{
+	struct
+	{
+		/**
+		 * [Bits 2:0] Default Memory Type.
+		 */
+		UINT64 DefaultMemoryType : 3;
+		UINT64 Reserved1 : 7;
+
+		/**
+		 * [Bit 10] Fixed Range MTRR Enable.
+		 */
+		UINT64 FixedRangeMtrrEnable : 1;
+
+		/**
+		 * [Bit 11] MTRR Enable.
+		 */
+		UINT64 MtrrEnable : 1;
+		UINT64 Reserved2 : 52;
+	};
+
+	UINT64 Flags;
+} IA32_MTRR_DEF_TYPE_REGISTER, * PIA32_MTRR_DEF_TYPE_REGISTER;
+

We implement a function called “EptCheckFeatures,” this function checks to see whether our processor supports basic EPT features or not; for MTRRs, we’ll check whether MTRRs are enabled or not. Having an enabled MTRR is necessary for our hypervisor. (we’ll complete this function later when we’re describing EPT.)

1
+2
+3
+4
+5
+6
+7
+8
+9
+
	IA32_MTRR_DEF_TYPE_REGISTER MTRRDefType;
+
+	MTRRDefType.Flags = __readmsr(MSR_IA32_MTRR_DEF_TYPE);
+
+	if (!MTRRDefType.MtrrEnable)
+	{
+		LogError("Mtrr Dynamic Ranges not supported");
+		return FALSE;
+	}
+

Building MTRR Map

Before creating a map from memory regions, It’s good to see how Windbg shows the MTRR regions and their caching policies using the “!mtrr” command.

!mtrr windbg

As you can see in the above picture, Windows prefers to use Fixed Range Registers (Fixed-support enabled) and variable range registers.

I’ll talk about fixed range registers later in this article.

In order to read MTRRs, we start by reading the VCNT value of IA32_MTRRCAP MSR (0xFE), which determines the number of variable MTRRs (Number of regions).

The next step is to iterate through each MTRR variable; we read MSR_IA32_MTRR_PHYSBASE0 and MSR_IA32_MTRR_PHYSMASK0 for each range and check if the range is valid or not (based on IA32_MTRR_PHYSMASK_REGISTER.Valid bit).

1
+2
+
		CurrentPhysBase.Flags = __readmsr(MSR_IA32_MTRR_PHYSBASE0 + (CurrentRegister * 2));
+		CurrentPhysMask.Flags = __readmsr(MSR_IA32_MTRR_PHYSMASK0 + (CurrentRegister * 2));
+

Now we need to calculate the start address and the end address (physical) based on MSRs.

The start address:

1
+2
+
			// Calculate the base address in bytes
+			Descriptor->PhysicalBaseAddress = CurrentPhysBase.PageFrameNumber * PAGE_SIZE;
+

The end address:

1
+2
+3
+4
+5
+6
+7
+
			// Calculate the total size of the range
+			// The lowest bit of the mask that is set to 1 specifies the size of the range
+			_BitScanForward64(&NumberOfBitsInMask, CurrentPhysMask.PageFrameNumber * PAGE_SIZE);
+
+			// Size of the range in bytes + Base Address
+			Descriptor->PhysicalEndAddress = Descriptor->PhysicalBaseAddress + ((1ULL << NumberOfBitsInMask) - 1ULL);
+
+

For further information about the calculation of MTRRs, you can read Intel SDM Vol 3A (11.11.3 Example Base and Mask Calculations).

And finally, read the cache policy which is set by whether BIOS or operating system.

1
+2
+
			// Memory Type (cacheability attributes)
+			Descriptor->MemoryType = (UCHAR)CurrentPhysBase.Type;
+

Putting it all together, we have the following function :

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
+
+/* Build MTRR Map of current physical addresses */
+BOOLEAN EptBuildMtrrMap()
+{
+	IA32_MTRR_CAPABILITIES_REGISTER MTRRCap;
+	IA32_MTRR_PHYSBASE_REGISTER CurrentPhysBase;
+	IA32_MTRR_PHYSMASK_REGISTER CurrentPhysMask;
+	PMTRR_RANGE_DESCRIPTOR Descriptor;
+	ULONG CurrentRegister;
+	ULONG NumberOfBitsInMask;
+
+
+	MTRRCap.Flags = __readmsr(MSR_IA32_MTRR_CAPABILITIES);
+
+	for (CurrentRegister = 0; CurrentRegister < MTRRCap.VariableRangeCount; CurrentRegister++)
+	{
+		// For each dynamic register pair
+		CurrentPhysBase.Flags = __readmsr(MSR_IA32_MTRR_PHYSBASE0 + (CurrentRegister * 2));
+		CurrentPhysMask.Flags = __readmsr(MSR_IA32_MTRR_PHYSMASK0 + (CurrentRegister * 2));
+
+		// Is the range enabled?
+		if (CurrentPhysMask.Valid)
+		{
+			// We only need to read these once because the ISA dictates that MTRRs are to be synchronized between all processors
+			// during BIOS initialization.
+			Descriptor = &EptState->MemoryRanges[EptState->NumberOfEnabledMemoryRanges++];
+
+			// Calculate the base address in bytes
+			Descriptor->PhysicalBaseAddress = CurrentPhysBase.PageFrameNumber * PAGE_SIZE;
+
+			// Calculate the total size of the range
+			// The lowest bit of the mask that is set to 1 specifies the size of the range
+			_BitScanForward64(&NumberOfBitsInMask, CurrentPhysMask.PageFrameNumber * PAGE_SIZE);
+
+			// Size of the range in bytes + Base Address
+			Descriptor->PhysicalEndAddress = Descriptor->PhysicalBaseAddress + ((1ULL << NumberOfBitsInMask) - 1ULL);
+
+			// Memory Type (cacheability attributes)
+			Descriptor->MemoryType = (UCHAR)CurrentPhysBase.Type;
+
+			if (Descriptor->MemoryType == MEMORY_TYPE_WRITE_BACK)
+			{
+				/* This is already our default, so no need to store this range.
+				 * Simply 'free' the range we just wrote. */
+				EptState->NumberOfEnabledMemoryRanges--;
+			}
+			LogInfo("MTRR Range: Base=0x%llx End=0x%llx Type=0x%x", Descriptor->PhysicalBaseAddress, Descriptor->PhysicalEndAddress, Descriptor->MemoryType);
+		}
+	}
+
+	LogInfo("Total MTRR Ranges Committed: %d", EptState->NumberOfEnabledMemoryRanges);
+
+	return TRUE;
+}
+

Fixed-Range MTRRs and PAT

The above section is enough for understanding the MTRRs for EPT. Still, I want to talk a little more about physical and virtual memory layout and caching policy (you can skip this section as it does not relate to our hypervisor).

There are other MTRR registers called Fixed Range Registers as its name implies, these registers are some predefined ranges defined by the processor (you can see them in the first lines of !mtrr command in Windbg).

These ranges are showed in the following table:

As you can see, the start of physical RAM is defined by these fixed range registers, which are for performance and legacy reasons.

Note that MTRRs should be defined contiguously; if your MTRRs are not contiguous, then the rest of the RAM is typically assumed as a hole.

Keep in mind that caching policy for each region of RAM is defined by MTRRs for PHYSICAL regions and PAGE ATTRIBUTE TABLE (PAT) for virtual areas so that each page can use its own caching policy by configuring IA32_PAT MSR. This means that sometimes the caching policy specified in MTRR registers is ignored, and instead, a page-level cache policy is used. There is a table in Intel SDM that shows the precedence rules between PAT and MTRRs (Table 11-7. Effective Page-Level Memory Types for Pentium III and More Recent Processor Families).

For further reading, you can read Intel SDM (Chapter 11 volume 3 A - 11.11 MEMORY TYPE RANGE REGISTERS (MTRRS) and 11.12 PAGE ATTRIBUTE TABLE (PAT)).

Anime :)

Virtualizing Current System’s Memory using EPT

As you have some previous information from EPT (part 4), we create an EPT table for our VM. In the case of fully virtualizing memory of the current machine, there are different approaches in implementing EPT; we can either have a separate EPT table for each of the cores or an EPT table for all the cores, our approach is using one EPT for all the cores as it’s simpler to implement and manage (more details about the benefits and caveat are discussed in Discussion section).

What we are trying to do is creating an EPT table that maps all of the available physical memory (we have the details of physical memory from MTRRs) to the physical address. It’s something like adding a table that maps the previous addresses to the previous address with some additional fields to control them. It’s ok if you’re confused, just read the rest of the article and things become more clear.

EPT Identity Mapping

In our hypervisor or all of the hypervisors that virtualize an already running system (not VMWare, VirtualBox, etc), we have a term called “Identity Mapping or 1:1 mapping”. It means that if you access guest PA (Physical Address) 0x4000, it will access host PA at 0x4000, thus, you have to map RAM’s hole as well as memory ranges to the guest.

It is the same as regular page tables (you can set page tables that way as well so that virtual address 0x1234 corresponds to the physical address 0x1234);

If you don’t map some physical memory and the guest access it, then you’ll get “EPT Violation”, which can be understood as the hypervisor’s page fault.

In order to map everything one by one, we’ll create PML4Es, then PDPTEs, then PDEs, and finally, PEs. In cases with 2 MB of granularity, we’ll skip PEs. Of course, it’s preferred to have 4 KB granularity but keep in mind that 4GB of RAM results in one million of 4 KB pages thus having a 4 KB granularity will eat a lot of memory, besides this, setting 4 KB granularity will take quite some time which will drive you crazy if you test your hypervisor frequently.

What hvpp, gbhv, and most of the other hypervisors do is initially set up 2 MB for the whole system (including RAM Ranges and MMIO holes) and then break some 2 MB pages into 4 KB pages as needed.

After splitting to 4 KB pages, you can merge them back to 2 MB pages again. We do the same for our hypervisor driver, first initial with 2 MB of granularity, then split them to 4 KB whenever needed.

Why we shouldn’t care about new memory allocations of Windows?

Well, that’s because we mapped all of the physical memory (every possible addresses in physical RAM) using 2 MB chunks, including those which are allocated and those which are not allocated yet, so no matter if Windows allocates a new memory chunk, we already have it in our EPT table.

What we want to do is creating a PML4E; then PDPTE, we’ll add that PDPTE into PML4E, then create PDE and add it to the PDPTE and finally create PE, which will point to physical address 0. Then we create another PE, that will point to address 0x1000 (if the granularity is 4 KB) or 0x200000 ( if the granularity is 2 MB ) and add it again 512 times (maximum entries in all paging tables including EPT Page tables and regular page tables are 512) then we’ll create another PDE and repeat!

All in all, our hypervisor should not care about any virtual address, it’s all about physical memory.

That’s enough for theory, let’s implement it!

Setting up PML4 and PML3 entries

First of all, we have to allocate a large memory for our EPT page table and then zero it.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
	PageTable = MmAllocateContiguousMemory((sizeof(VMM_EPT_PAGE_TABLE) / PAGE_SIZE) * PAGE_SIZE, MaxSize);
+
+	if (PageTable == NULL)
+	{
+		LogError("Failed to allocate memory for PageTable");
+		return NULL;
+	}
+
+	// Zero out all entries to ensure all unused entries are marked Not Present 
+	RtlZeroMemory(PageTable, sizeof(VMM_EPT_PAGE_TABLE));
+

We have a linked list that holds the trace of every allocated memory; we have to initialize it first so we can de-allocate our allocated pages whenever we want to turn off our hypervisor.

1
+2
+
	// Initialize the dynamic split list which holds all dynamic page splits 
+	InitializeListHead(&PageTable->DynamicSplitList);
+

It’s time to initialize the first table (EPT PML4). For the initialization phase, we set all the accesses to 1 (including Read Access, Write Access, Execute Access) on all of the EPT tables.

The physical address (Page Frame Number - PFN) for the PML4E is PML3’s address, and as it’s aligned and whenever the processor wants to translate it (it performs multiplication by PAGE_SIZE) so we divide it by PAGE_SIZE (4096).

1
+2
+3
+4
+5
+
	// Mark the first 512GB PML4 entry as present, which allows us to manage up to 512GB of discrete paging structures. 
+	PageTable->PML4[0].PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&PageTable->PML3[0]) / PAGE_SIZE;
+	PageTable->PML4[0].ReadAccess = 1;
+	PageTable->PML4[0].WriteAccess = 1;
+	PageTable->PML4[0].ExecuteAccess = 1;
+

Each PML4 entry covers 512 GB of memory, so one entry is more than enough. Each table has 512 entries, so we have to fill PML3 with 512 of 1 GB entries. We’re done this by creating a template with RWX enabled and use __stosq to fill the table with this template continuously. __stosq generates a store string instruction (rep stosq) means that continuously (in our case VMM_EPT_PML3E_COUNT=512) copy something on a special location.

The next step is to convert our previously allocated PML2 entries to physical addresses and fill the PML3 with those addresses.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
	// Set up one 'template' RWX PML3 entry and copy it into each of the 512 PML3 entries 
+	// Using the same method as SimpleVisor for copying each entry using intrinsics. 
+	RWXTemplate.ReadAccess = 1;
+	RWXTemplate.WriteAccess = 1;
+	RWXTemplate.ExecuteAccess = 1;
+
+	// Copy the template into each of the 512 PML3 entry slots 
+	__stosq((SIZE_T*)&PageTable->PML3[0], RWXTemplate.Flags, VMM_EPT_PML3E_COUNT);
+
+	// For each of the 512 PML3 entries 
+	for (EntryIndex = 0; EntryIndex < VMM_EPT_PML3E_COUNT; EntryIndex++)
+	{
+		// Map the 1GB PML3 entry to 512 PML2 (2MB) entries to describe each large page.
+		// NOTE: We do *not* manage any PML1 (4096 byte) entries and do not allocate them.
+		PageTable->PML3[EntryIndex].PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&PageTable->PML2[EntryIndex][0]) / PAGE_SIZE;
+	}
+

For PML2, we have the same approach, fill it with an RWX template, but this time we set LargePage to 1 (for the reason I told you above about initialization with 2 MB granularity). Exactly same as above, we use __stosq to fill these entries, this time with 512*512 entries as we have 512 entries, each of which describes 512 entries.

The next step is to set up each entry’s PFN addresses. I’ll describe EptSetupPML2Entry in the next section.

Note that we’re are filling entries for a 512*512 table, so we have to perform a multiplication by 512 for each EntryGroupIndex and then add it to the current PML2’s address (EntryIndex).

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
	// All PML2 entries will be RWX and 'present' 
+	PML2EntryTemplate.WriteAccess = 1;
+	PML2EntryTemplate.ReadAccess = 1;
+	PML2EntryTemplate.ExecuteAccess = 1;
+
+	// We are using 2MB large pages, so we must mark this 1 here. 
+	PML2EntryTemplate.LargePage = 1;
+
+	/* For each collection of 512 PML2 entries (512 collections * 512 entries per collection), mark it RWX using the same template above.
+	   This marks the entries as "Present" regardless of if the actual system has memory at this region or not. We will cause a fault in our
+	   EPT handler if the guest access a page outside a usable range, despite the EPT frame being present here.
+	 */
+	__stosq((SIZE_T*)&PageTable->PML2[0], PML2EntryTemplate.Flags, VMM_EPT_PML3E_COUNT * VMM_EPT_PML2E_COUNT);
+
+	// For each of the 512 collections of 512 2MB PML2 entries 
+	for (EntryGroupIndex = 0; EntryGroupIndex < VMM_EPT_PML3E_COUNT; EntryGroupIndex++)
+	{
+		// For each 2MB PML2 entry in the collection 
+		for (EntryIndex = 0; EntryIndex < VMM_EPT_PML2E_COUNT; EntryIndex++)
+		{
+			// Setup the memory type and frame number of the PML2 entry. 
+			EptSetupPML2Entry(&PageTable->PML2[EntryGroupIndex][EntryIndex], (EntryGroupIndex * VMM_EPT_PML2E_COUNT) + EntryIndex);
+		}
+	}
+

Putting it all together we have the following code:

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
+
/* Allocates page maps and create identity page table */
+PVMM_EPT_PAGE_TABLE EptAllocateAndCreateIdentityPageTable()
+{
+	PVMM_EPT_PAGE_TABLE PageTable;
+	EPT_PML3_POINTER RWXTemplate;
+	EPT_PML2_ENTRY PML2EntryTemplate;
+	SIZE_T EntryGroupIndex;
+	SIZE_T EntryIndex;
+
+	// Allocate all paging structures as 4KB aligned pages 
+	PHYSICAL_ADDRESS MaxSize;
+	PVOID Output;
+
+	// Allocate address anywhere in the OS's memory space
+	MaxSize.QuadPart = MAXULONG64;
+
+	PageTable = MmAllocateContiguousMemory((sizeof(VMM_EPT_PAGE_TABLE) / PAGE_SIZE) * PAGE_SIZE, MaxSize);
+
+	if (PageTable == NULL)
+	{
+		LogError("Failed to allocate memory for PageTable");
+		return NULL;
+	}
+
+	// Zero out all entries to ensure all unused entries are marked Not Present 
+	RtlZeroMemory(PageTable, sizeof(VMM_EPT_PAGE_TABLE));
+
+	// Initialize the dynamic split list which holds all dynamic page splits 
+	InitializeListHead(&PageTable->DynamicSplitList);
+
+	// Mark the first 512GB PML4 entry as present, which allows us to manage up to 512GB of discrete paging structures. 
+	PageTable->PML4[0].PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&PageTable->PML3[0]) / PAGE_SIZE;
+	PageTable->PML4[0].ReadAccess = 1;
+	PageTable->PML4[0].WriteAccess = 1;
+	PageTable->PML4[0].ExecuteAccess = 1;
+
+	/* Now mark each 1GB PML3 entry as RWX and map each to their PML2 entry */
+
+	// Ensure stack memory is cleared
+	RWXTemplate.Flags = 0;
+
+	// Set up one 'template' RWX PML3 entry and copy it into each of the 512 PML3 entries 
+	// Using the same method as SimpleVisor for copying each entry using intrinsics. 
+	RWXTemplate.ReadAccess = 1;
+	RWXTemplate.WriteAccess = 1;
+	RWXTemplate.ExecuteAccess = 1;
+
+	// Copy the template into each of the 512 PML3 entry slots 
+	__stosq((SIZE_T*)&PageTable->PML3[0], RWXTemplate.Flags, VMM_EPT_PML3E_COUNT);
+
+	// For each of the 512 PML3 entries 
+	for (EntryIndex = 0; EntryIndex < VMM_EPT_PML3E_COUNT; EntryIndex++)
+	{
+		// Map the 1GB PML3 entry to 512 PML2 (2MB) entries to describe each large page.
+		// NOTE: We do *not* manage any PML1 (4096 byte) entries and do not allocate them.
+		PageTable->PML3[EntryIndex].PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&PageTable->PML2[EntryIndex][0]) / PAGE_SIZE;
+	}
+
+	PML2EntryTemplate.Flags = 0;
+
+	// All PML2 entries will be RWX and 'present' 
+	PML2EntryTemplate.WriteAccess = 1;
+	PML2EntryTemplate.ReadAccess = 1;
+	PML2EntryTemplate.ExecuteAccess = 1;
+
+	// We are using 2MB large pages, so we must mark this 1 here. 
+	PML2EntryTemplate.LargePage = 1;
+
+	/* For each collection of 512 PML2 entries (512 collections * 512 entries per collection), mark it RWX using the same template above.
+	   This marks the entries as "Present" regardless of if the actual system has memory at this region or not. We will cause a fault in our
+	   EPT handler if the guest access a page outside a usable range, despite the EPT frame being present here.
+	 */
+	__stosq((SIZE_T*)&PageTable->PML2[0], PML2EntryTemplate.Flags, VMM_EPT_PML3E_COUNT * VMM_EPT_PML2E_COUNT);
+
+	// For each of the 512 collections of 512 2MB PML2 entries 
+	for (EntryGroupIndex = 0; EntryGroupIndex < VMM_EPT_PML3E_COUNT; EntryGroupIndex++)
+	{
+		// For each 2MB PML2 entry in the collection 
+		for (EntryIndex = 0; EntryIndex < VMM_EPT_PML2E_COUNT; EntryIndex++)
+		{
+			// Setup the memory type and frame number of the PML2 entry. 
+			EptSetupPML2Entry(&PageTable->PML2[EntryGroupIndex][EntryIndex], (EntryGroupIndex * VMM_EPT_PML2E_COUNT) + EntryIndex);
+		}
+	}
+
+	return PageTable;
+}
+

Setting up PML2 entries

PML2 is different from the other tables; this is because, in our 2 MB design, it’s the last table, so it has to deal with MTRRs’ caching policy.

First, we have to set the PageFrameNumber of our PML2 entry. This is because we’re mapping all 512 GB without any hole, I mean, we’re not trying to see just what are MTRR’s base and end address and map based on them, but we map every possible physical address within 512 GB. Think about it one more time.

If you want to know more about PFNs in Windows, then you can read my blog posts Inside Windows Page Frame Number (PFN) – Part 1 and Part 2.

1
+2
+3
+4
+5
+6
+
	  Each of the 512 collections of 512 PML2 entries is setup here.
+	  This will, in total, identity map every physical address from 0x0 to physical address 0x8000000000 (512GB of memory)
+
+	  ((EntryGroupIndex * VMM_EPT_PML2E_COUNT) + EntryIndex) * 2MB is the actual physical address we're mapping
+	 */
+	NewEntry->PageFrameNumber = PageFrameNumber;
+

Now it’s time to see the actual caching policy based on MTRRs. Ranges in MTRRs are not divided by 4 KB or 2 MB, and these are exact physical addresses. What we are going to do is iterating over each MTRR and see whether a special MTRR describes our current physical address or not.

If none of them describe it, then we choose Write-Back (MEMORY_TYPE_WRITE_BACK) as the default caching policy; otherwise, we have to select the caching policy that is used in MTRRs.

This approach will make our EPT PML2 as it’s like a real system.

If we don’t choose the system-specific caching policy, then it will cause catastrophic errors. For example, some of the devices that use physical memory as the command and control mechanism go through the cache and won’t immediately respond to our requests or for APIC devices will not work in the case of real-time interrupts.

The following code is responsible for finding the desired caching policy based on MTRRs.

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
+
	// Default memory type is always WB for performance. 
+	TargetMemoryType = MEMORY_TYPE_WRITE_BACK;
+
+	// For each MTRR range 
+	for (CurrentMtrrRange = 0; CurrentMtrrRange < EptState->NumberOfEnabledMemoryRanges; CurrentMtrrRange++)
+	{
+		// If this page's address is below or equal to the max physical address of the range 
+		if (AddressOfPage <= EptState->MemoryRanges[CurrentMtrrRange].PhysicalEndAddress)
+		{
+			// And this page's last address is above or equal to the base physical address of the range 
+			if ((AddressOfPage + SIZE_2_MB - 1) >= EptState->MemoryRanges[CurrentMtrrRange].PhysicalBaseAddress)
+			{
+				/* If we're here, this page fell within one of the ranges specified by the variable MTRRs
+				   Therefore, we must mark this page as the same cache type exposed by the MTRR
+				 */
+				TargetMemoryType = EptState->MemoryRanges[CurrentMtrrRange].MemoryType;
+				// LogInfo("0x%X> Range=%llX -> %llX | Begin=%llX End=%llX", PageFrameNumber, AddressOfPage, AddressOfPage + SIZE_2_MB - 1, EptState->MemoryRanges[CurrentMtrrRange].PhysicalBaseAddress, EptState->MemoryRanges[CurrentMtrrRange].PhysicalEndAddress);
+
+				// 11.11.4.1 MTRR Precedences 
+				if (TargetMemoryType == MEMORY_TYPE_UNCACHEABLE)
+				{
+					// If this is going to be marked uncacheable, then we stop the search as UC always takes precedent. 
+					break;
+				}
+			}
+		}
+	}
+
+	// Finally, commit the memory type to the entry. 
+	NewEntry->MemoryType = TargetMemoryType;
+

EPT Violation

Intel describes EPT Violation like this:

An EPT violation occurs when there is no EPT misconfiguration, but the EPT paging structure entries disallow access using the guest-physical address.

But that’s hard to understand, in short, every time one instruction tries to read a page (Read Access), or an instruction tries to write on a page (Write Access), or an instruction causes instruction fetch from a page and EPT attributes (the one we configured in the above sections) of that page doesn’t allow this, then an EPT Violation occurs.

Let me explain a little bit more, imagine we have an entry in our EPT Table which is responsible for mapping physical address 0x1000. In this entry, we set Write Access to 0 (Read Access = 1 and Execute Access = 1). If any instruction tries to write on that page, for example by using (Mov [0x1000], RAX) then as the paging attributes doesn’t allow writing, so an EPT Violation occurs and now our callback is called so that we can decide to what we want to do with that page.

By 0x1000, I mean a physical address. Of course, if you have the virtual address, then it gets translated to a physical.

Another example, let’s assume an NT function (for example NtCreateFile) is located fffff801`80230540.

1
+2
+3
+4
+
nt!NtCreateFile:
+fffff801`80230540 4881ec88000000  sub     rsp,88h
+fffff801`80230547 33c0            xor     eax,eax
+fffff801`80230549 4889442478      mov     qword ptr [rsp+78h],rax
+

If we convert it to a physical address, then the address of NtCreateFile in physical memory is 0x3B8000, now we try to find this physical address in our EPT PTE Table. Then we set Execute Access of that entry to 0. Now, each time someone tries to call, jmp, ret, etc. to this particular page, then an EPT Violation occurs.

This is the basic idea of using EPT function hooks, we talk about it in detail in Part 8.

For now, first, we have to read the physical address, which caused this EPT Violation. It’s done by reading GUEST_PHYSICAL_ADDRESS using Vmread instruction.

1
+2
+3
+4
+
		// Reading guest physical address
+		GuestPhysicalAddr = 0;
+		__vmx_vmread(GUEST_PHYSICAL_ADDRESS, &GuestPhysicalAddr);
+		LogInfo("Guest Physical Address : 0x%llx", GuestPhysicalAddr);
+

The second thing that we have to read is Exit Qualification. If you remember from the previous part, Exit Qualification gives additional details about Exit Reasons.

I mean, each Exit Reason might have a special Exit Qualification that has a special meaning for that special Exit Reason. (how many “special” I used in the previous sentence ?)

Exit Reason can be read from VM_EXIT_REASON using Vmread instruction.

1
+2
+
	ULONG ExitReason = 0;
+	__vmx_vmread(VM_EXIT_REASON, &ExitReason);
+

In the case of EPT Violation, Exit Qualification shows that the reason why this violation occurs. For example, it indicates that EPT Violation occurs because of a data read to a physical page that its Read Access is 0 or instruction fetches (a function tries to execute instruction) from a physical page that its Execute Access is 0.

The following table shows the structure of Exit Qualification and each bit’s meaning for EPT Violation.

Now that we have all the details, we need to pass them to EptHandlePageHookExit, and we deal with it in the next sections.

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
+
/*
+   Handle VM exits for EPT violations. Violations are thrown whenever an operation is performed
+   on an EPT entry that does not provide permissions to access that page.
+*/
+BOOLEAN EptHandleEptViolation(ULONG ExitQualification, UINT64 GuestPhysicalAddr)
+{
+
+	VMX_EXIT_QUALIFICATION_EPT_VIOLATION ViolationQualification;
+
+	DbgBreakPoint();
+
+	ViolationQualification.Flags = ExitQualification;
+
+	if (EptHandlePageHookExit(ViolationQualification, GuestPhysicalAddr))
+	{
+		// Handled by page hook code.
+		return TRUE;
+	}
+
+	LogError("Unexpected EPT violation");
+	DbgBreakPoint();
+
+	// Redo the instruction that caused the exception. 
+	return FALSE;
+}
+

EPT Misconfiguration

Another EPT derived vm-exit is EPT Misconfiguration (EXIT_REASON_EPT_MISCONFIG).

An EPT Misconfiguration occurs when, in the course of translating a physical guest address, the logical processor encounters an EPT paging-structure entry that contains an unsupported value.

If you want to know more about all the reasons why EPT Misconfiguration occurs, you can see Intel SDM - Vol 3C Section 28.2.3.1.

Based on my experience, I encountered EPT Misconfiguration most of the time because I clear the bit 0 of the entry (indicating that data reads are not allowed), and bit 1 is set (reporting that data writes are permitted).

Also, EPT misconfigurations occur when an EPT paging-structure entry is configured with settings reserved for future functionality.

It’s fatal error, let’s just break and see what we’ve done wrong !

1
+2
+3
+4
+5
+6
+7
+8
+9
+
VOID EptHandleMisconfiguration(UINT64 GuestAddress)
+{
+	LogInfo("EPT Misconfiguration!");
+	LogError("A field in the EPT paging structure was invalid, Faulting guest address : 0x%llx", GuestAddress);
+
+	DbgBreakPoint();
+	// We can't continue now. 
+	// EPT misconfiguration is a fatal exception that will probably crash the OS if we don't get out now.
+}
+

Adding EPT to VMCS

Our hypervisor starts virtualizing MMU by calling EptLogicalProcessorInitialize, which sets a 64-bit value called EPTP. The following table shows the structure of EPTP. If you look at part 4, we have this table in that part too, but there is a change here, bit 7 was reserved at the time I wrote part 4, and now it has something to do with shadow stacks.

EptLogicalProcessorInitialize calls EptAllocateAndCreateIdentityPageTable to allocate identity table (as described above).

For performance, we let the processor know it can cache the EPT (MemoryType to MEMORY_TYPE_WRITE_BACK).

We are not utilizing the ‘access’ and ‘dirty’ flag features (EnableAccessAndDirtyFlags to FALSE).

As Intel mentioned, Page Walk should be the count of the tables we use (4) minus 1, so PageWalkLength = 3 indicates an EPT page-walk length of 4. It is because we’re not using just three tables with 2 MB granularity, we’ll split 2 MB pages to 4 KB granularity.

The last step is to save EPTP somewhere into a global variable so we can use it later.

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
+
/*
+  Initialize EPT for an individual logical processor.
+  Creates an identity mapped page table and sets up an EPTP to be applied to the VMCS later.
+*/
+BOOLEAN EptLogicalProcessorInitialize()
+{
+	PVMM_EPT_PAGE_TABLE PageTable;
+	EPTP EPTP;
+
+	/* Allocate the identity mapped page table*/
+	PageTable = EptAllocateAndCreateIdentityPageTable();
+	if (!PageTable)
+	{
+		LogError("Unable to allocate memory for EPT");
+		return FALSE;
+	}
+
+	// Virtual address to the page table to keep track of it for later freeing 
+	EptState->EptPageTable = PageTable;
+
+	EPTP.Flags = 0;
+
+	// For performance, we let the processor know it can cache the EPT.
+	EPTP.MemoryType = MEMORY_TYPE_WRITE_BACK;
+
+	// We are not utilizing the 'access' and 'dirty' flag features. 
+	EPTP.EnableAccessAndDirtyFlags = FALSE;
+
+	/*
+	  Bits 5:3 (1 less than the EPT page-walk length) must be 3, indicating an EPT page-walk length of 4;
+	  see Section 28.2.2
+	 */
+	EPTP.PageWalkLength = 3;
+
+	// The physical page number of the page table we will be using 
+	EPTP.PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&PageTable->PML4) / PAGE_SIZE;
+
+	// We will write the EPTP to the VMCS later 
+	EptState->EptPointer = EPTP;
+
+	return TRUE;
+}
+

Finally, we need to configure Vmcs with our EPTP Table, so we use vmwrite with EPT_POINTER and set it to our EPTP.

1
+2
+
	// Set up EPT 
+	__vmx_vmwrite(EPT_POINTER, EptState->EptPointer.Flags);
+

Also, don’t forget to enable EPT feature in Secondary Processor-Based VM-Execution Controls using CPU_BASED_CTL2_ENABLE_EPT; otherwise, it won’t work.

1
+2
+3
+4
+5
+6
+
	SecondaryProcBasedVmExecControls = HvAdjustControls(CPU_BASED_CTL2_RDTSCP |
+		CPU_BASED_CTL2_ENABLE_EPT | CPU_BASED_CTL2_ENABLE_INVPCID |
+		CPU_BASED_CTL2_ENABLE_XSAVE_XRSTORS, MSR_IA32_VMX_PROCBASED_CTLS2);
+
+	__vmx_vmwrite(SECONDARY_VM_EXEC_CONTROL, SecondaryProcBasedVmExecControls);
+	LogInfo("Secondary Proc Based VM Exec Controls (MSR_IA32_VMX_PROCBASED_CTLS2) : 0x%x", SecondaryProcBasedVmExecControls);
+

Now we have a perfect EPT Table which virtualizes MMU and now all of the translations go through the EPT.

Monitoring Page’s RWX Activity

The next important topic is the monitoring of the page’s RWX. From the above section, you saw that we put each of the Read Access, Write Access and Execute Access to 1, but to use EPT’s monitoring features, we have to set some of them to 0 so that we get EPT Violation on each of the accesses mentioned above.

Using these features (setting access to 0) has its difficulties by its nature, problems relating to IRQL, splitting, absence of the ability to use NT functions, synchronization, and deadlock are some of these problems and limitations.

In this section we’re trying to solve these problem.

Pre-allocating Buffers for VMX Root Mode

After executing VMLAUNCH, we shouldn’t modify EPT Tables from Vmx non-root mode; that is because if we do it, then it might (and will) causes system inconsistency.

This limitation and the fact that we couldn’t use any NT function in VMX Root Mode bring us new challenges.

One of these challenges is that we might need to split a 2 MB Page into 4 KB pages, of course, another Page Table (PML1) is necessary to store the details of new 4 KB pages. I mean, we have to create another Page Table (PML1), and it needs a new memory.

We can’t use ExAllocatePoolTag in Vmx root-mode as it’s an NT API. (you can use it in Vmx root-mode, and you’ll see that it sometimes work and sometimes halts the system - the reason is described in the Discussion section).

The solution to this problem is using a previously allocated buffer from Vmx non-root mode and use it in Vmx root mode, so this brings us the first limitation to our hypervisor which is we have to start setting hooks from vmx non-root mode because we want to pre-allocate a buffer then we pass the buffer and hook settings to Vmx root-mode using a special Vmcalls.

By the way, this is not an unsolvable limitation, for example, you can allocate 100 pages from Vmx non-root mode and use them whenever you want in Vmx root-mode, and it’s not necessarily a limitation anymore but for now, let’s assume that the caller should start setting hooks from Vmx non-root mode.

Honestly, I wanted to make a mechanism for running code from Vmx root mode to Vmx non-root mode using NMI events; using this approach will resolve the problem of pre-allocating buffers, but for this part, let’s use pre-allocated buffers.

Hyperplatform and Hvpp use the pre-allocated buffers.

In this section and next sections we’re trying to complete a function called “EptPageHook”.

There is a per-core global variable called “PreAllocatedMemoryDetails” in GuestState that is defined like this:

1
+2
+3
+4
+5
+
typedef struct _VMX_NON_ROOT_MODE_MEMORY_ALLOCATOR
+{
+	PVOID PreAllocatedBuffer;		// As we can't use ExAllocatePoolWithTag in VMX Root mode, this holds a pre-allocated buffer address
+									// PreAllocatedBuffer == 0 indicates that it's not previously allocated
+} VMX_NON_ROOT_MODE_MEMORY_ALLOCATOR, * PVMX_NON_ROOT_MODE_MEMORY_ALLOCATOR;
+

Now that we’re trying to hook, we’ll see whether the current core has a previously pre-allocated buffer or not. If it doesn’t have a buffer, then we allocate it using ExAllocatePoolWithTag.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
	if (GuestState[LogicalCoreIndex].PreAllocatedMemoryDetails.PreAllocatedBuffer == NULL)
+	{
+		PreAllocBuff = ExAllocatePoolWithTag(NonPagedPool, sizeof(VMM_EPT_DYNAMIC_SPLIT), POOLTAG);
+
+		if (!PreAllocBuff)
+		{
+			LogError("Insufficient memory for pre-allocated buffer");
+			return FALSE;
+		}
+
+		// Zero out the memory
+		RtlZeroMemory(PreAllocBuff, sizeof(VMM_EPT_DYNAMIC_SPLIT));
+
+		// Save the pre-allocated buffer
+		GuestState[LogicalCoreIndex].PreAllocatedMemoryDetails.PreAllocatedBuffer = PreAllocBuff;
+	}
+

Now we have two different states if we previously configured the VMCS with EPT and we’re already in a hypervisor then we have to ask, Vmx root-mode to set the hook for us (Setting hook after Vmlaunch); otherwise, we can modify it in a regular function as we don’t execute VMLAUNCH (with EPT) yet (Setting hook before Vmlaunch).

By “with EPT,” I mean if we used this EPT in our hypervisor. For example, you might configure VMCS without EPTP, then you execute VMLAUNCH, and now you decide to create an EPT Table, this way doesn’t need Vmx root-mode to modify EPT Table, we can change it from Vmx non-root mode as we didn’t use this EPT Table yet.

Setting hook before Vmlaunch

I prefer to do everything in a function so that EptVmxRootModePageHook can be used for both Vmx root-mode and non-root mode. Still, you shouldn’t directly call this function as it needs a preparing phase (instead, you can call EptPageHook).

What we have to do is calling EptVmxRootModePageHook and a HasLaunched flag that determines whether we used our EPT in our Vmx operation our not.

1
+2
+3
+4
+
		if (EptVmxRootModePageHook(TargetFunc, HasLaunched) == TRUE) {
+			LogInfo("[*] Hook applied (VM has not launched)");
+			return TRUE;
+		}
+

I’ll describe EptVmxRootModePageHook in the section, Applying the Hook later.

Setting hook after Vmlaunch

If we’re already used this EPT in our Vmx operation, then we need to ask Vmx root-mode to modify the EPT Table for us; in other words, we have to call EptVmxRootModePageHook from Vmx root-mode, so it needs Vmcall.

We have some additional things to do here, as I told you, each logical core has its own set of caches relating to EPT, so we have to invalidate all the cores’ EPT Tables immediately and of course it has to be done in Vmx non-root mode as we want to use NT APIs.

To call EptVmxRootModePageHook from Vmx root-mode, we’ll use Vmcall with VMCALL_EXEC_HOOK_PAGE and also sent the functions virtual address (TargetFunc) as the first parameter.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
	if (HasLaunched)
+	{
+		if (AsmVmxVmcall(VMCALL_EXEC_HOOK_PAGE, TargetFunc, NULL, NULL, NULL) == STATUS_SUCCESS)
+		{
+			LogInfo("Hook applied from VMX Root Mode");
+
+			// Now we have to notify all the core to invalidate their EPT
+			HvNotifyAllToInvalidateEpt();
+
+			return TRUE;
+		}
+	}
+

In Vmcall handler, we just call EptVmxRootModePageHook.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
	case VMCALL_EXEC_HOOK_PAGE:
+	{
+		HookResult = EptVmxRootModePageHook(OptionalParam1, TRUE);
+
+		if (HookResult)
+		{
+			VmcallStatus = STATUS_SUCCESS;
+		}
+		else
+		{
+			VmcallStatus = STATUS_UNSUCCESSFUL;
+		}
+		break;
+	}
+

Let’s get down to invalidation part,

HvNotifyAllToInvalidateEpt uses KeIpiGenericCall which broadcasts HvInvalidateEptByVmcall on all the core.

1
+2
+3
+4
+5
+6
+
/* Notify all core to invalidate their EPT */
+VOID HvNotifyAllToInvalidateEpt()
+{
+	// Let's notify them all
+	KeIpiGenericCall(HvInvalidateEptByVmcall, EptState->EptPointer.Flags);
+}
+

As the invalidation should be within vmx root-mode (INVEPT instruction is only valid in vmx root-mode) thus HvInvalidateEptByVmcall uses Vmcall with VMCALL_INVEPT_ALL_CONTEXT and VMCALL_INVEPT_SINGLE_CONTEXT to notify vmx root-mode about invalidation.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
/* Invalidate EPT using Vmcall (should be called from Vmx non root mode) */
+VOID HvInvalidateEptByVmcall(UINT64 Context)
+{
+	if (Context == NULL)
+	{
+		// We have to invalidate all contexts
+		AsmVmxVmcall(VMCALL_INVEPT_ALL_CONTEXT, NULL, NULL, NULL);
+	}
+	else
+	{
+		// We have to invalidate all contexts
+		AsmVmxVmcall(VMCALL_INVEPT_SINGLE_CONTEXT, Context, NULL, NULL);
+	}
+}
+

The Vmcall handler uses InveptSingleContext and InveptAllContexts to invalidate the contexts; we’ll talk about invalidation in details later in this part (Invalidating Translations Derived from EPT (INVEPT)).

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
	case VMCALL_INVEPT_SINGLE_CONTEXT:
+	{
+		InveptSingleContext(OptionalParam1);
+		VmcallStatus = STATUS_SUCCESS;
+		break;
+	}
+	case VMCALL_INVEPT_ALL_CONTEXT:
+	{
+		InveptAllContexts();
+		VmcallStatus = STATUS_SUCCESS;
+		break;
+	}
+

Finding a Page’s entry in EPT Tables

Let’s see how we can find addresses in PML1, PML2, PML3 and PML4.

Finding PML4, PML3, PML2 entries

We want to find PML2 entry, for finding PML2, first, we have to find PML4 and PML3.

We used an ordinal approach to map the physical addresses so all the physical addresses are stored in the same way so we need some definitions to find the index of the entries from tables.

Here’s the definitions.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
// Index of the 1st paging structure (4096 byte)
+#define ADDRMASK_EPT_PML1_INDEX(_VAR_) ((_VAR_ & 0x1FF000ULL) >> 12)
+
+// Index of the 2nd paging structure (2MB)
+#define ADDRMASK_EPT_PML2_INDEX(_VAR_) ((_VAR_ & 0x3FE00000ULL) >> 21)
+
+// Index of the 3rd paging structure (1GB)
+#define ADDRMASK_EPT_PML3_INDEX(_VAR_) ((_VAR_ & 0x7FC0000000ULL) >> 30)
+
+// Index of the 4th paging structure (512GB)
+#define ADDRMASK_EPT_PML4_INDEX(_VAR_) ((_VAR_ & 0xFF8000000000ULL) >> 39)
+

After finding the indexes, we have to find the virtual address to that index so we can modify the page table. It’s because in protected mode we can’t access physical addresses.

The following code, first finds the indexes then return the virtual address from the EPT Page Table to that indexes.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+
/* Get the PML2 entry for this physical address. */
+PEPT_PML2_ENTRY EptGetPml2Entry(PVMM_EPT_PAGE_TABLE EptPageTable, SIZE_T PhysicalAddress)
+{
+	SIZE_T Directory, DirectoryPointer, PML4Entry;
+	PEPT_PML2_ENTRY PML2;
+
+	Directory = ADDRMASK_EPT_PML2_INDEX(PhysicalAddress);
+	DirectoryPointer = ADDRMASK_EPT_PML3_INDEX(PhysicalAddress);
+	PML4Entry = ADDRMASK_EPT_PML4_INDEX(PhysicalAddress);
+
+	// Addresses above 512GB are invalid because it is > physical address bus width 
+	if (PML4Entry > 0)
+	{
+		return NULL;
+	}
+
+	PML2 = &EptPageTable->PML2[DirectoryPointer][Directory];
+	return PML2;
+}
+

Finding PML1 entry

For PML1, we have the same approach. First, we find the PML2 the same as above. Then we check to see if the PML2 is split or not. It’s because if it’s not split before then we don’t have PML1 and it’s 3-level paging.

Finally, as we saved physical addresses contiguously, so we can find the index using ADDRMASK_EPT_PML1_INDEX (as defined above) and then return the virtual address to that page entry.

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
+
/* Get the PML1 entry for this physical address if the page is split. Return NULL if the address is invalid or the page wasn't already split. */
+PEPT_PML1_ENTRY EptGetPml1Entry(PVMM_EPT_PAGE_TABLE EptPageTable, SIZE_T PhysicalAddress)
+{
+	SIZE_T Directory, DirectoryPointer, PML4Entry;
+	PEPT_PML2_ENTRY PML2;
+	PEPT_PML1_ENTRY PML1;
+	PEPT_PML2_POINTER PML2Pointer;
+
+	Directory = ADDRMASK_EPT_PML2_INDEX(PhysicalAddress);
+	DirectoryPointer = ADDRMASK_EPT_PML3_INDEX(PhysicalAddress);
+	PML4Entry = ADDRMASK_EPT_PML4_INDEX(PhysicalAddress);
+
+	// Addresses above 512GB are invalid because it is > physical address bus width 
+	if (PML4Entry > 0)
+	{
+		return NULL;
+	}
+
+	PML2 = &EptPageTable->PML2[DirectoryPointer][Directory];
+
+	// Check to ensure the page is split 
+	if (PML2->LargePage)
+	{
+		return NULL;
+	}
+
+	// Conversion to get the right PageFrameNumber.
+	// These pointers occupy the same place in the table and are directly convertable.
+	PML2Pointer = (PEPT_PML2_POINTER)PML2;
+
+	// If it is, translate to the PML1 pointer 
+	PML1 = (PEPT_PML1_ENTRY)PhysicalAddressToVirtualAddress((PVOID)(PML2Pointer->PageFrameNumber * PAGE_SIZE));
+
+	if (!PML1)
+	{
+		return NULL;
+	}
+
+	// Index into PML1 for that address 
+	PML1 = &PML1[ADDRMASK_EPT_PML1_INDEX(PhysicalAddress)];
+
+	return PML1;
+}
+

Splitting 2 MB Pages to 4 KB Pages

As you know, in all of our hypervisor parts we used 3 LEVEL paging (PML4, PML3, PML2) and our granularity is 2 MB. Having pages with 2 MB granularity is not adequate for monitoring purposes because we might get lots of unrelated violations caused by non-relevant areas.

To fix these kind of problems, we use PML1 and 4 KB granularity.

This is where we might need an additional buffer and as we’re in vmx root-mode, then we’ll use our previously allocated buffers.

First, we get the actual entry from PML2 and check if it’s already a 4 KB defined table, if it previously split then nothing to do, we can use it.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
	// Find the PML2 entry that's currently used
+	TargetEntry = EptGetPml2Entry(EptPageTable, PhysicalAddress);
+	if (!TargetEntry)
+	{
+		LogError("An invalid physical address passed");
+		return FALSE;
+	}
+
+	// If this large page is not marked a large page, that means it's a pointer already.
+	// That page is therefore already split.
+	if (!TargetEntry->LargePage)
+	{
+		return TRUE;
+	}
+

If not, we set PreAllocatedMemoryDetails’s PreAllocatedBuffer to null so that next time the pre-allocator allocates a new buffer for this purpose.

1
+2
+
	// Free previous buffer 
+	GuestState[CoreIndex].PreAllocatedMemoryDetails.PreAllocatedBuffer = NULL;
+

Then, we should fill the PML1 with an RWX template and then split our 2 MB page into 4 KB chunks (compute 4 KB physical addresses and fill the PageFrameNumber).

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+
	// Point back to the entry in the dynamic split for easy reference for which entry that dynamic split is for.
+	NewSplit->Entry = TargetEntry;
+
+	// Make a template for RWX 
+	EntryTemplate.Flags = 0;
+	EntryTemplate.ReadAccess = 1;
+	EntryTemplate.WriteAccess = 1;
+	EntryTemplate.ExecuteAccess = 1;
+
+	// Copy the template into all the PML1 entries 
+	__stosq((SIZE_T*)&NewSplit->PML1[0], EntryTemplate.Flags, VMM_EPT_PML1E_COUNT);
+
+
+	// Set the page frame numbers for identity mapping.
+	for (EntryIndex = 0; EntryIndex < VMM_EPT_PML1E_COUNT; EntryIndex++)
+	{
+		// Convert the 2MB page frame number to the 4096 page entry number plus the offset into the frame. 
+		NewSplit->PML1[EntryIndex].PageFrameNumber = ((TargetEntry->PageFrameNumber * SIZE_2_MB) / PAGE_SIZE) + EntryIndex;
+	}
+

Finally, create a new PML2 entry (with LargePage = 0) and replace it with the previous PML2 entry.

Also keep the track of allocated memory to de-allocate it when we want to run vmxoff.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
	// Allocate a new pointer which will replace the 2MB entry with a pointer to 512 4096 byte entries. 
+	NewPointer.Flags = 0;
+	NewPointer.WriteAccess = 1;
+	NewPointer.ReadAccess = 1;
+	NewPointer.ExecuteAccess = 1;
+	NewPointer.PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&NewSplit->PML1[0]) / PAGE_SIZE;
+
+	// Add our allocation to the linked list of dynamic splits for later deallocation 
+	InsertHeadList(&EptPageTable->DynamicSplitList, &NewSplit->DynamicSplitList);
+
+	// Now, replace the entry in the page table with our new split pointer.
+	RtlCopyMemory(TargetEntry, &NewPointer, sizeof(NewPointer));
+

The following function represent the full code for splitting 2 MB pages to 4 KB pages.

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
+
/* Split 2MB (LargePage) into 4kb pages */
+BOOLEAN EptSplitLargePage(PVMM_EPT_PAGE_TABLE EptPageTable, PVOID PreAllocatedBuffer, SIZE_T PhysicalAddress, ULONG CoreIndex)
+{
+
+	PVMM_EPT_DYNAMIC_SPLIT NewSplit;
+	EPT_PML1_ENTRY EntryTemplate;
+	SIZE_T EntryIndex;
+	PEPT_PML2_ENTRY TargetEntry;
+	EPT_PML2_POINTER NewPointer;
+
+	// Find the PML2 entry that's currently used
+	TargetEntry = EptGetPml2Entry(EptPageTable, PhysicalAddress);
+	if (!TargetEntry)
+	{
+		LogError("An invalid physical address passed");
+		return FALSE;
+	}
+
+	// If this large page is not marked a large page, that means it's a pointer already.
+	// That page is therefore already split.
+	if (!TargetEntry->LargePage)
+	{
+		return TRUE;
+	}
+
+	// Free previous buffer 
+	GuestState[CoreIndex].PreAllocatedMemoryDetails.PreAllocatedBuffer = NULL;
+
+	// Allocate the PML1 entries 
+	NewSplit = (PVMM_EPT_DYNAMIC_SPLIT)PreAllocatedBuffer;
+	if (!NewSplit)
+	{
+		LogError("Failed to allocate dynamic split memory");
+		return FALSE;
+	}
+	RtlZeroMemory(NewSplit, sizeof(VMM_EPT_DYNAMIC_SPLIT));
+
+
+	// Point back to the entry in the dynamic split for easy reference for which entry that dynamic split is for.
+	NewSplit->Entry = TargetEntry;
+
+	// Make a template for RWX 
+	EntryTemplate.Flags = 0;
+	EntryTemplate.ReadAccess = 1;
+	EntryTemplate.WriteAccess = 1;
+	EntryTemplate.ExecuteAccess = 1;
+
+	// Copy the template into all the PML1 entries 
+	__stosq((SIZE_T*)&NewSplit->PML1[0], EntryTemplate.Flags, VMM_EPT_PML1E_COUNT);
+
+
+	// Set the page frame numbers for identity mapping.
+	for (EntryIndex = 0; EntryIndex < VMM_EPT_PML1E_COUNT; EntryIndex++)
+	{
+		// Convert the 2MB page frame number to the 4096 page entry number plus the offset into the frame. 
+		NewSplit->PML1[EntryIndex].PageFrameNumber = ((TargetEntry->PageFrameNumber * SIZE_2_MB) / PAGE_SIZE) + EntryIndex;
+	}
+
+	// Allocate a new pointer which will replace the 2MB entry with a pointer to 512 4096 byte entries. 
+	NewPointer.Flags = 0;
+	NewPointer.WriteAccess = 1;
+	NewPointer.ReadAccess = 1;
+	NewPointer.ExecuteAccess = 1;
+	NewPointer.PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&NewSplit->PML1[0]) / PAGE_SIZE;
+
+	// Add our allocation to the linked list of dynamic splits for later deallocation 
+	InsertHeadList(&EptPageTable->DynamicSplitList, &NewSplit->DynamicSplitList);
+
+	// Now, replace the entry in the page table with our new split pointer.
+	RtlCopyMemory(TargetEntry, &NewPointer, sizeof(NewPointer));
+
+	return TRUE;
+}
+

Applying the Hook

EptVmxRootModePageHook is one of the important parts of the EPT.

First, we check to prohibit calling this function from vmx root-mode when the pre-allocated buffer isn’t available.

1
+2
+3
+4
+5
+6
+7
+
	// Check whether we are in VMX Root Mode or Not 
+	LogicalCoreIndex = KeGetCurrentProcessorIndex();
+
+	if (GuestState[LogicalCoreIndex].IsOnVmxRootMode && GuestState[LogicalCoreIndex].PreAllocatedMemoryDetails.PreAllocatedBuffer == NULL && HasLaunched)
+	{
+		return FALSE;
+	}
+

Then we align the address as the addresses in page tables are aligned.

1
+2
+3
+
	VirtualTarget = PAGE_ALIGN(TargetFunc);
+
+	PhysicalAddress = (SIZE_T)VirtualAddressToPhysicalAddress(VirtualTarget);
+

We’ll check about the granularity and split it if it’s a LargePage (more details at the next section - Splitting 2 MB Pages to 4 KB Pages ).

1
+2
+3
+4
+5
+6
+7
+8
+9
+
	// Set target buffer
+	TargetBuffer = GuestState[LogicalCoreIndex].PreAllocatedMemoryDetails.PreAllocatedBuffer;
+
+
+	if (!EptSplitLargePage(EptState->EptPageTable, TargetBuffer, PhysicalAddress, LogicalCoreIndex))
+	{
+		LogError("Could not split page for the address : 0x%llx", PhysicalAddress);
+		return FALSE;
+	}
+

Then find the PML1 entry of the requested page and as it’s already divided into 4 KB pages so PML1 is available.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
	// Pointer to the page entry in the page table. 
+	TargetPage = EptGetPml1Entry(EptState->EptPageTable, PhysicalAddress);
+
+	// Ensure the target is valid. 
+	if (!TargetPage)
+	{
+		LogError("Failed to get PML1 entry of the target address");
+		return FALSE;
+	}
+
+	// Save the original permissions of the page 
+	OriginalEntry = *TargetPage;
+

Now, we change the attributes related to the PML1 entry, this the most interesting part of this function, for example, you can disable Write access to a 4 KB page, in our case, I disabled instruction execution (fetch) from the target page.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
	/*
+	 * Lastly, mark the entry in the table as no execute. This will cause the next time that an instruction is
+	 * fetched from this page to cause an EPT violation exit. This will allow us to swap in the fake page with our
+	 * hook.
+	 */
+	OriginalEntry.ReadAccess = 1;
+	OriginalEntry.WriteAccess = 1;
+	OriginalEntry.ExecuteAccess = 0;
+
+
+	// Apply the hook to EPT 
+	TargetPage->Flags = OriginalEntry.Flags;
+

If we are in vmx root-mode then the TLB caches have to be invalidated.

1
+2
+3
+4
+5
+6
+7
+8
+9
+
	// Invalidate the entry in the TLB caches so it will not conflict with the actual paging structure.
+	if (HasLaunched)
+	{
+		// Uncomment in order to invalidate all the contexts
+		// LogInfo("INVEPT Results : 0x%x\n", InveptAllContexts());
+		Descriptor.EptPointer = EptState->EptPointer.Flags;
+		Descriptor.Reserved = 0;
+		AsmInvept(1, &Descriptor);
+	}
+

Done ! The hook is applied.

Handling hooked pages’ vm-exits

First, we’re trying to align the Guest Physical Address (remember from the Ept Violation that we read the GUEST_PHYSICAL_ADDRESS from Vmcs). This because we’re only able to find aligned physical addresses from our EPT Table (we don’t want to iterate over them !).

1
+
	PhysicalAddress = PAGE_ALIGN(GuestPhysicalAddr);
+

Now, as I described above, we find the PML1 entry relating to this physical address. We’re not looking for PML2 that’s because, if we reached here then we probably split 2 MB pages to 4 KB pages and we have PML1 instead of PML2.

1
+2
+3
+4
+5
+6
+7
+8
+
	TargetPage = EptGetPml1Entry(EptState->EptPageTable, PhysicalAddress);
+
+	// Ensure the target is valid. 
+	if (!TargetPage)
+	{
+		LogError("Failed to get PML1 entry for target address");
+		return FALSE;
+	}
+

Finally, we check if the violation is caused by an Execute Access (based on Exit Qualification) and the violated page has Execute Access to 0, if so, then just make the page’s entry in PML1 executable and invalidate the cache so that this modification takes effect.

Don’t forget to tell our vm-exit handler to avoid skipping the current instruction (avoid adding Instruction Length to Guest RIP) and execute it one more time as the instruction didn’t execute.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
	// If the violation was due to trying to execute a non-executable page, that means that the currently
+	// swapped in page is our original RW page. We need to swap in the hooked executable page (fake page)
+	if (!ViolationQualification.EptExecutable && ViolationQualification.ExecuteAccess)
+	{
+
+		TargetPage->ExecuteAccess = 1;
+
+		// InveptAllContexts();
+		INVEPT_DESCRIPTOR Descriptor;
+
+		Descriptor.EptPointer = EptState->EptPointer.Flags;
+		Descriptor.Reserved = 0;
+		AsmInvept(1, &Descriptor);
+
+		// Redo the instruction 
+		GuestState[KeGetCurrentProcessorNumber()].IncrementRip = FALSE;
+
+		LogInfo("Set the Execute Access of a page (PFN = 0x%llx) to 1", TargetPage->PageFrameNumber);
+
+		return TRUE;
+	}
+

All in all, we have the following handler.

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
+
/* Check if this exit is due to a violation caused by a currently hooked page. Returns FALSE
+ * if the violation was not due to a page hook.
+ *
+ * If the memory access attempt was RW and the page was marked executable, the page is swapped with
+ * the original page.
+ *
+ * If the memory access attempt was execute and the page was marked not executable, the page is swapped with
+ * the hooked page.
+ */
+BOOLEAN EptHandlePageHookExit(VMX_EXIT_QUALIFICATION_EPT_VIOLATION ViolationQualification, UINT64 GuestPhysicalAddr)
+{
+	SIZE_T PhysicalAddress;
+	PVOID VirtualTarget;
+
+	PEPT_PML1_ENTRY TargetPage;
+
+
+	/* Translate the page from a physical address to virtual so we can read its memory.
+	   This function will return NULL if the physical address was not already mapped in
+	   virtual memory.
+	*/
+	PhysicalAddress = PAGE_ALIGN(GuestPhysicalAddr);
+
+	if (!PhysicalAddress)
+	{
+		LogError("Target address could not be mapped to physical memory");
+		return FALSE;
+	}
+
+	TargetPage = EptGetPml1Entry(EptState->EptPageTable, PhysicalAddress);
+
+	// Ensure the target is valid. 
+	if (!TargetPage)
+	{
+		LogError("Failed to get PML1 entry for target address");
+		return FALSE;
+	}
+
+	// If the violation was due to trying to execute a non-executable page, that means that the currently
+	// swapped in page is our original RW page. We need to swap in the hooked executable page (fake page)
+	if (!ViolationQualification.EptExecutable && ViolationQualification.ExecuteAccess)
+	{
+
+		TargetPage->ExecuteAccess = 1;
+
+		// InveptAllContexts();
+		INVEPT_DESCRIPTOR Descriptor;
+
+		Descriptor.EptPointer = EptState->EptPointer.Flags;
+		Descriptor.Reserved = 0;
+		AsmInvept(1, &Descriptor);
+
+		// Redo the instruction 
+		GuestState[KeGetCurrentProcessorNumber()].IncrementRip = FALSE;
+
+		LogInfo("Set the Execute Access of a page (PFN = 0x%llx) to 1", TargetPage->PageFrameNumber);
+
+		return TRUE;
+	}
+
+	LogError("Invalid page swapping logic in hooked page");
+
+	return FALSE;
+}
+

Invalidating Translations Derived from EPT (INVEPT)

Now that we implemented EPT, there is another problem here. It’s the software’s responsibility to invalidate the caches. For example, we changed the Execute access attribute of a particular page, now we have to tell the CPU that we changed something and it has to invalidate its cache, or in another way, we get EPT Violation for Execute access of a special page and now we no longer need these EPT Violations for this page. Hence, we set the Execute Access of this page to 1; thus, we have to tell our processor that we changed something in our page table. Are you confused? Let me explain it one more time.

Imagine we access the physical 0x1000, and it’ll get translated to host physical address 0x1000 (based on 1:1 mapping). Next time, if we access 0x1000, the CPU won’t send the request to the memory bus but uses cached memory instead. It’s faster. Now let’s say we changed the EPT Physical Address of a page to point to different EPT PD or change the attributes (Read, Write, Execute) of one of the EPT tables, now we have to tell the processor that your cache is invalid and that’s what exactly INVEPT performs.

There is a problem here; we have to separately tell each logical core that it needs to invalidate its EPT cache. In other words, each core has to execute INVEPT on its vmx root-mode. We’ll solve these problems later in this part.

There are two types of TLB Invalidation for hypervisors.

  • VMX-specific TLB-management instructions:

    • INVEPT - Invalidate cached Extended Page Table (EPT) mappings in the processor to synchronize address translation in virtual machines with memory-resident EPT pages.

    • INVVPID - Invalidate cached mappings of address translation based on the Virtual Processor ID (VPID).

We’ll talk about INVVPID in detail in part 8.

So in case if you wouldn’t perform INVEPT after changing EPT’s structures, you would be risking that the CPU would reuse old translations.

Any change to EPT structure needs INVEPT, but switching EPT (or VMCS) doesn’t require INVEPT because that translation will be “tagged” with the changed EPTP in the cache.

Now we have two terms here, Single-Context and All-Context.

1
+2
+3
+4
+5
+
typedef enum _INVEPT_TYPE
+{
+	SINGLE_CONTEXT = 0x00000001,
+	ALL_CONTEXTS = 0x00000002
+};
+

And we have a assembly function which generally executes the INVEPT.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
; Error codes :
+    VMX_ERROR_CODE_SUCCESS              = 0
+    VMX_ERROR_CODE_FAILED_WITH_STATUS   = 1
+    VMX_ERROR_CODE_FAILED               = 2
+
+AsmInvept PROC PUBLIC
+
+    invept  rcx, oword ptr [rdx]
+    jz @jz
+    jc @jc
+    xor     rax, rax
+    ret
+
+    @jz: 
+    mov     rax, VMX_ERROR_CODE_FAILED_WITH_STATUS
+    ret
+
+    @jc:
+    mov     rax, VMX_ERROR_CODE_FAILED
+    ret
+
+AsmInvept ENDP
+

From the above code, RCX describes the Type (which can be one of the all-context and single-context), and RDX is the descriptor for INVEPT.

The following structure is the descriptor for INVEPT as described in Intel SDM.

1
+2
+3
+4
+5
+
typedef struct _INVEPT_DESC
+{
+	EPTP EptPointer;
+	UINT64  Reserveds;
+}INVEPT_DESC, * PINVEPT_DESC;
+

We’ll use our assembly function in another function called Invept.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
/* Invoke the Invept instruction */
+unsigned char Invept(UINT32 Type, INVEPT_DESC* Descriptor)
+{
+	if (!Descriptor)
+	{
+		INVEPT_DESC ZeroDescriptor = { 0 };
+		Descriptor = &ZeroDescriptor;
+	}
+
+	return AsmInvept(Type, Descriptor);
+}
+

It’s time to see what are so called “All-Context“and “Single-Context”.

Invalidating All Contexts

All-Context means that you invalidate all EPT-derived translations. (for every-VM).

1
+2
+3
+4
+5
+
/* Invalidates all contexts in ept cache table */
+unsigned char InveptAllContexts()
+{
+	return Invept(ALL_CONTEXTS, NULL);
+}
+

Note: For every-VM, I mean every VM for a particular logical core; each core can have multiple VMCSs and EPT tables and switches between them. It doesn’t relate to the EPT table on other cores.

Invalidating Single Context

Single-Context means that you invalidate all EPT-derived translations based on a single EPTP (in short: for a single VM in a logical core).

1
+2
+3
+4
+5
+6
+
/* Invalidates a single context in ept cache table */
+unsigned char InveptSingleContext(UINT64 EptPointer)
+{
+	INVEPT_DESC Descriptor = { EptPointer, 0 };
+	return Invept(SINGLE_CONTEXT, &Descriptor);
+}
+

Broadcasting Invept to all logical cores simultaneously

Let say you have two cores and 1 EPTP. At some point you change EPT on core one; thus you have to invalidate EPT on all cores at that point. If you remember from the previous section, we have to notify all cores to invalidate their EPT caches using something like KeIpiGenericCall, and the problem is you can’t call KeIpiGenericCall from VM-exit for apparent reasons - you shouldn’t call any NT APIs in Vm-exit. Calling this API from Vm-exit likely causes deadlock.

We can get around this by modifying APIC and creating our custom IPI call routine. We’ll come across APIC virtualization in the future parts. Still, for now, if we want to change EPT for all cores, then we can call KeIpiGenericCall from regular kernel-mode (not vmx root-mode) and in that callback we perform Vmcall to tell our processor to invalidate its cache in vmx root-mode.

It’s because if we don’t immediately invalidate EPT, then we might lose some EPT Violations. This is because each logical core will have a different memory view.

If you remember from the above sections (EptPageHook), we’d checked whether the core is already on vmx operation (vmlaunch is executed). If it launched, then we used Vmcall to tell the processor about modifying EPT Table from the vmx root-mode. Right after returning from Vmcall, we called HvNotifyAllToInvalidateEpt to tell all the cores about new invalidation in their EPT caches (remember, we’re not on vmx root-mode anymore, we’re in vmx non-root mode so we can use NT APIs as it’s a regular kernel function).

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
	if (HasLaunched)
+	{
+		if (AsmVmxVmcall(VMCALL_EXEC_HOOK_PAGE, TargetFunc, NULL, NULL, NULL) == STATUS_SUCCESS)
+		{
+			LogInfo("Hook applied from VMX Root Mode");
+
+			// Now we have to notify all the core to invalidate their EPT
+			HvNotifyAllToInvalidateEpt();
+
+			return TRUE;
+		}
+	}
+

HvNotifyAllToInvalidateEpt, on the other hand, uses KeIpiGenericCall, and this function broadcasts HvInvalidateEptByVmcall on all the logical cores and also pass our current EPTP to this function.

1
+2
+3
+4
+5
+6
+
/* Notify all core to invalidate their EPT */
+VOID HvNotifyAllToInvalidateEpt()
+{
+	// Let's notify them all
+	KeIpiGenericCall(HvInvalidateEptByVmcall, EptState->EptPointer.Flags);
+}
+

HvInvalidateEptByVmcall decides whether the caller needs an all-contexts invalidation or a single-context invalidation, and based on that, it calls the Vmcall with adequate Vmcall number. Note that our hypervisor doesn’t have multiple EPTPs, so it’s always a single-context Vmcall.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
/* Invalidate EPT using Vmcall (should be called from Vmx non root mode) */
+VOID HvInvalidateEptByVmcall(UINT64 Context)
+{
+	if (Context == NULL)
+	{
+		// We have to invalidate all contexts
+		AsmVmxVmcall(VMCALL_INVEPT_ALL_CONTEXT, NULL, NULL, NULL);
+	}
+	else
+	{
+		// We have to invalidate all contexts
+		AsmVmxVmcall(VMCALL_INVEPT_SINGLE_CONTEXT, Context, NULL, NULL);
+	}
+}
+

Finally, Vmcall handler calls InveptAllContexts or HvInvalidateEptByVmcall based on Vmcall number in vmx root-mode.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
	case VMCALL_INVEPT_SINGLE_CONTEXT:
+	{
+		c(OptionalParam1);
+		VmcallStatus = STATUS_SUCCESS;
+		break;
+	}
+	case VMCALL_INVEPT_ALL_CONTEXT:
+	{
+		InveptAllContexts();
+		VmcallStatus = STATUS_SUCCESS;
+		break;
+	}
+

The last thing is you can’t execute INVEPT in vmx non-root mode as it causes a Vm-exit with EXIT_REASON_INVEPT (0x32) and it doesn’t have any effect.

That’s it all for INVEPT.

Anime :)

Fixing Previous Design Issues

The rest of the topic is nothing new. We want to improve our hypervisor and fix some issues from the previous parts and also support some new features and defeat some deadlocks and synchronization problems that exist in our previous parts.

Support to more than 64 logical cores

Previous versions of Hypervisor From Scratch has the problem of not supporting more than 32 cores (32*2 logical cores). This is because we used KeSetSystemAffinityThread, and it gives a KAFFINITY as its argument, and it’s a 64 Bit long variable mask.

We used KeSetSystemAffinityThread when we broadcast Vmptrld, Vmclear, VMCS Setup (Vmwrite), Vmlaunch, and Vmxoff to all cores.

The best approach to run on all logical cores is letting Windows (API) execute them on each core simultaneously. This involves raising IRQL on each core.

We have different options here; first, we can use KeGenericCallDpc. It’s an undocumented function which schedules CPU-specific DPCs on all CPUs.

The definition of KeGenericCallDpc is as bellow.

1
+2
+3
+4
+
KeGenericCallDpc(
+	_In_ PKDEFERRED_ROUTINE Routine,
+	_In_opt_ PVOID Context
+);
+

The first argument is the address of the target function, which we want to execute on each core, and context is an optional parameter to this function.

In the target function, we call KeSignalCallDpcSynchronize and KeSignalCallDpcDone to avoid synchronization problems so that all the cores finish at the same time.

KeSignalCallDpcSynchronize waits for all DPCs to synchronize at that point (where we call KeSignalCallDpcSynchronize).

1
+2
+3
+4
+
LOGICAL
+KeSignalCallDpcSynchronize(
+	_In_ PVOID SystemArgument2
+);
+

Finally, KeSignalCallDpcDone marks the DPC as being complete.

1
+2
+3
+4
+
VOID
+KeSignalCallDpcDone(
+	_In_ PVOID SystemArgument1
+);
+

The above two functions have to be executed as the last step (when everything completes) in the target function.

Another option is using KeIpiGenericCall, this routine causes the specified function to run on all processors simultaneously, and it’s documented. I used the first approach in Hypervisor From Scratch, and these updates are applied to both the initialization phase and the Vmxoff phase.

Synchronization problem in exiting VMX

As we now support more than 64 logical cores using DPCs, and as most of the functions are executed simultaneously, we have some problems with our previously designed routines. For example, in the previous parts, I used gGuestRSP and gGuestRIP to return to the former state. Using one global variable on all cores causes errors as one core might save its RIP and RSP (core 1), then other core (core 2) keeps the same data in these variables, When the first core (core 1) tries to restore the state, it’s the state of second core (core 2), and you’ll see a BSOD :D .

In order to solve this problem, we have to store a per-core structure which saves the Guest RIP and Guest RSP. The following structure is used for this purpose.

1
+2
+3
+4
+5
+6
+7
+
typedef struct _VMX_VMXOFF_STATE
+{
+	BOOLEAN IsVmxoffExecuted;					// Shows whether the VMXOFF executed or not
+	UINT64  GuestRip;							// Rip address of guest to return
+	UINT64  GuestRsp;							// Rsp address of guest to return
+
+} VMX_VMXOFF_STATE, * PVMX_VMXOFF_STATE;
+

We add the above structure to VIRTUAL_MACHINE_STATE as it’s a per-core structure.

1
+2
+3
+4
+5
+6
+
typedef struct _VIRTUAL_MACHINE_STATE
+{
+...
+	VMX_VMXOFF_STATE VmxoffState;									// Shows the vmxoff state of the guest
+...
+} VIRTUAL_MACHINE_STATE, * PVIRTUAL_MACHINE_STATE;
+

We need to broadcast Vmxoff to all of the logical cores. This is done by using the HvTerminateVmx; this function is called once and broadcast HvDpcBroadcastTerminateGuest to all logical cores and de-allocates (free) all the EPT related tables and pre-allocated buffers.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
/* Terminate Vmx on all logical cores. */
+VOID HvTerminateVmx()
+{
+	// Broadcast to terminate Vmx
+	KeGenericCallDpc(HvDpcBroadcastTerminateGuest, 0x0);
+
+	/* De-allocatee global variables */
+
+	// Free each split 
+	FOR_EACH_LIST_ENTRY(EptState->EptPageTable, DynamicSplitList, VMM_EPT_DYNAMIC_SPLIT, Split)
+		ExFreePoolWithTag(Split, POOLTAG);
+	FOR_EACH_LIST_ENTRY_END();
+
+	// Free Identity Page Table
+	MmFreeContiguousMemory(EptState->EptPageTable);
+
+	// Free GuestState
+	ExFreePoolWithTag(GuestState, POOLTAG);
+
+	// Free EptState
+	ExFreePoolWithTag(EptState, POOLTAG);
+
+}
+

HvDpcBroadcastTerminateGuest is responsible for synchronizing DPCs and calling a VMX function call VmxTerminate.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+
/* The broadcast function which terminate the guest. */
+VOID HvDpcBroadcastTerminateGuest(struct _KDPC* Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
+{
+	// Terminate Vmx using Vmcall
+	if (!VmxTerminate())
+	{
+		LogError("There were an error terminating Vmx");
+	}
+
+	// Wait for all DPCs to synchronize at this point
+	KeSignalCallDpcSynchronize(SystemArgument2);
+
+	// Mark the DPC as being complete
+	KeSignalCallDpcDone(SystemArgument1);
+}
+

VmxTerminate de-allocates per-core allocated regions like the Vmxon region, Vmcs region, Vmm Stack, and Msr Bitmap. As we implemented our Vmcall mechanism, we can use Vmcall to request a vmxoff from the vmx root mode (instead of what we’ve done in the previous version with CPUID Handler). So it executes AsmVmxVmcall with VMCALL_VMXOFF on each core, and each core will run vmxoff separately.

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
+
/* Broadcast to terminate VMX on all logical cores */
+BOOLEAN VmxTerminate()
+{
+	int CurrentCoreIndex;
+	NTSTATUS Status;
+
+	// Get the current core index
+	CurrentCoreIndex = KeGetCurrentProcessorNumber();
+
+	LogInfo("\tTerminating VMX on logical core %d", CurrentCoreIndex);
+
+	// Execute Vmcall to to turn off vmx from Vmx root mode
+	Status = AsmVmxVmcall(VMCALL_VMXOFF, NULL, NULL, NULL);
+
+	// Free the destination memory
+	MmFreeContiguousMemory(GuestState[CurrentCoreIndex].VmxonRegionVirtualAddress);
+	MmFreeContiguousMemory(GuestState[CurrentCoreIndex].VmcsRegionVirtualAddress);
+	ExFreePoolWithTag(GuestState[CurrentCoreIndex].VmmStack, POOLTAG);
+	ExFreePoolWithTag(GuestState[CurrentCoreIndex].MsrBitmapVirtualAddress, POOLTAG);
+
+	if (Status == STATUS_SUCCESS)
+	{
+		return TRUE;
+	}
+
+	return FALSE;
+}
+

Our Vmcall handler calls VmxVmxoff, and as this function is executed under vmx root-mode, so it’s allowed to run VMXOFF. This function also saves the GuestRip and GuestRsp into the per-core VMX_VMXOFF_STATE structure. This is where we solved the problem as we’re not using a shared global variable anymore. It also sets IsVmxoffExecuted, which indicates whether the logical core is on VMX operation or it left the VMX operation by executing VMXOFF.

The VmxVmxoff is implemented like this :

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
+
/* Prepare and execute Vmxoff instruction */
+VOID VmxVmxoff()
+{
+	int CurrentProcessorIndex;
+	UINT64 GuestRSP; 	// Save a pointer to guest rsp for times that we want to return to previous guest stateS
+	UINT64 GuestRIP; 	// Save a pointer to guest rip for times that we want to return to previous guest state
+	UINT64 GuestCr3;
+	UINT64 ExitInstructionLength;
+
+
+	// Initialize the variables
+	ExitInstructionLength = 0;
+	GuestRIP = 0;
+	GuestRSP = 0;
+
+	CurrentProcessorIndex = KeGetCurrentProcessorNumber();
+
+	/*
+	According to SimpleVisor :
+		Our callback routine may have interrupted an arbitrary user process,
+		and therefore not a thread running with a system-wide page directory.
+		Therefore if we return back to the original caller after turning off
+		VMX, it will keep our current "host" CR3 value which we set on entry
+		to the PML4 of the SYSTEM process. We want to return back with the
+		correct value of the "guest" CR3, so that the currently executing
+		process continues to run with its expected address space mappings.
+	*/
+
+	__vmx_vmread(GUEST_CR3, &GuestCr3);
+	__writecr3(GuestCr3);
+
+	// Read guest rsp and rip
+	__vmx_vmread(GUEST_RIP, &GuestRIP);
+	__vmx_vmread(GUEST_RSP, &GuestRSP);
+
+	// Read instruction length
+	__vmx_vmread(VM_EXIT_INSTRUCTION_LEN, &ExitInstructionLength);
+	GuestRIP += ExitInstructionLength;
+
+	// Set the previous registe states
+	GuestState[CurrentProcessorIndex].VmxoffState.GuestRip = GuestRIP;
+	GuestState[CurrentProcessorIndex].VmxoffState.GuestRsp = GuestRSP;
+
+	// Notify the Vmexit handler that VMX already turned off
+	GuestState[CurrentProcessorIndex].VmxoffState.IsVmxoffExecuted = TRUE;
+
+	// Execute Vmxoff
+	__vmx_off();
+
+}
+

As we return to vm-exit handler, we check whether we left the VMX opeation or not.

1
+2
+3
+4
+
	if (GuestState[CurrentProcessorIndex].VmxoffState.IsVmxoffExecuted)
+	{
+		return TRUE;
+	}
+

We also define two other functions called “HvReturnStackPointerForVmxoff” and “HvReturnInstructionPointerForVmxoff”, which find the logical core index and returns the corresponding stack pointer and RIP to return.

HvReturnStackPointerForVmxoff is :

1
+2
+3
+4
+5
+
/* Returns the stack pointer, to change in the case of Vmxoff */
+UINT64 HvReturnStackPointerForVmxoff()
+{
+	return GuestState[KeGetCurrentProcessorNumber()].VmxoffState.GuestRsp;
+}
+

And HvReturnInstructionPointerForVmxoff is:

1
+2
+3
+4
+5
+
/* Returns the instruction pointer, to change in the case of Vmxoff */
+UINT64 HvReturnInstructionPointerForVmxoff()
+{
+	return GuestState[KeGetCurrentProcessorNumber()].VmxoffState.GuestRip;
+}
+

Eventually, when we detect that we left the vmx operation, instead of executing VMRESUME we’ll run AsmVmxoffHandler, this function calls the HvReturnStackPointerForVmxoff and HvReturnInstructionPointerForVmxoff and puts the value of RSP and RIP after the general-purpose registers thus when we restore the general-purpose registers, we can pop the RSP from the stack and return to the previous address (ret) and continue normal execution.

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
+
+AsmVmxoffHandler PROC
+    
+    sub rsp, 020h       ; shadow space
+    call HvReturnStackPointerForVmxoff
+    add rsp, 020h       ; remove for shadow space
+
+    mov [rsp+088h], rax  ; now, rax contains rsp
+
+    sub rsp, 020h       ; shadow space
+    call HvReturnInstructionPointerForVmxoff
+    add rsp, 020h       ; remove for shadow space
+
+    mov rdx, rsp        ; save current rsp
+
+    mov rbx, [rsp+088h] ; read rsp again
+
+    mov rsp, rbx
+
+    push rax            ; push the return address as we changed the stack, we push
+                        ; it to the new stack
+
+    mov rsp, rdx        ; restore previous rsp
+                        
+    sub rbx,08h         ; we push sth, so we have to add (sub) +8 from previous stack
+                        ; also rbx already contains the rsp
+    mov [rsp+088h], rbx ; move the new pointer to the current stack
+
+	RestoreState:
+
+	pop rax
+    pop rcx
+    pop rdx
+    pop rbx
+    pop rbp		         ; rsp
+    pop rbp
+    pop rsi
+    pop rdi 
+    pop r8
+    pop r9
+    pop r10
+    pop r11
+    pop r12
+    pop r13
+    pop r14
+    pop r15
+
+    popfq
+
+	pop		rsp     ; restore rsp
+	ret             ; jump back to where we called Vmcall
+
+AsmVmxoffHandler ENDP
+

As you can see, we no longer have the problem of using a global variable among all the cores.

The issues relating to the Meltdown mitigation

As you know, EXIT_REASON_CR_ACCESS is one of the reasons that might cause VM-Exit (Especially if you’re subject to 1-setting of CRs in your VMCS). Hypervisors used to save all the general-purpose registers every time a VM-Exit occurs and then restore it at the next VMRESUME.

In the previous versions of our driver, we ignored RSP and save some trash instead of it, that’s because RSP of guest is already saved in GUEST_RSP in VMCS. After VMRESUME, it’s loaded automatically, and you know, our current RSP is invalid (it’s host RSP).

After meltdown mitigation, Windows uses MOV CR3, RSP, and as we saved trash instead of RSP, then you change CR3 to an invalid value, and it silently crashes with TRIPLE FAULT VM-Exit. It won’t give you the exact error.

For fixing this issue, we add the following code to HvHandleControlRegisterAccess, so each time when a vm-exit occurs, we change the RSP to the correct value.

1
+2
+3
+4
+5
+6
+
	/* Because its RSP and as we didn't save RSP correctly (because of pushes) so we have make it points to the GUEST_RSP */
+	if (CrExitQualification->Fields.Register == 4)
+	{
+		__vmx_vmread(GUEST_RSP, &GuestRsp);
+		*RegPtr = GuestRsp;
+	}
+

Previously, this was mentioned by Alex, for more information, you can read this article.

Some tips for debugging hypervisors

Always try to test your hypervisor in a uni-core system. If it works then, you can check it on a multi-core, so when something doesn’t work on multi-core and works on uni-core, then know that it’s a synchronization problem.

Don’t try to call Nt functions in Vmx root mode. Most of NT functions are not suitable to run in a high IRQL, so if you use it, it leads to weird behavior and crashes the whole or system is halted.

For more information, I really recommend reading Hyperplatform’s User Document (4.4. Coding Tips).

Let’s Test it!

Let’s see how we can test our hypervisor,

How to test?

In order to test our new hypervisor, we have two scenarios, and the following codes show how we test our hypervisor, the codes for tests are available at (Ept.c and HypervisorRoutines.c).

In the first scenario, we want to test page hook (EptPageHook) before executing vmlaunch, which means that Ept is initialized, and then we want to put the hook before entering VMX. (the test code is on Ept.c)

1
+2
+3
+
	///////////////////////// Example Test /////////////////////////
+	 EptPageHook(ExAllocatePoolWithTag, FALSE);
+	///////////////////////////////////////////////////////////////
+

The above function puts hook on the execution of a page containing a function (in this case ExAllocatePoolWithTag).

The second scenario is we want to test both VMCALL and EptPageHook after our hypervisor is loaded, and we’re in Vmx non-root mode (the test code is on HypervisorRoutines.c).

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
	//  Check if everything is ok then return true otherwise false
+	if (AsmVmxVmcall(VMCALL_TEST, 0x22, 0x333, 0x4444) == STATUS_SUCCESS)
+	{
+		///////////////// Test Hook after Vmx is launched /////////////////
+		EptPageHook(ExAllocatePoolWithTag, TRUE);
+		///////////////////////////////////////////////////////////////////
+		return TRUE;
+	}
+	else
+	{
+		return FALSE;
+	}
+

As you can see, it first tests the Vmcall using VMCALL_TEST and then puts the hook to a function (in this case ExAllocatePoolWithTag).

Demo

First, we load our hypervisor driver,

Driver is loaded an Hv started

For the first scenario, you can see that we successfully notified about the execution of ExAllocatePoolWith tag after vmlaunch executed, and Guest Rip is equal to the address of ExAllocatePoolWithTag and EptHandleEptViolation is responsible for handling Ept violations.

Ept Hook Before Vmlaunch

In the second testing scenario, you can see that our VMCALL is successfully executed (green line), and we notified about the execution of a page, but wait, we put our Execute Access hook on ExAllocatePoolWithTag, but the Guest Rip is equal to ExFreePool, Why?

It turns out that ExAllocatePoolWithTag and ExFreePool are both on the same page, and ExFreePool is executed earlier than ExAllocatePoolWithTag, so we get the execution of this function.

Ept Hook after Vmlaunch

The above test results show the importance of checking Guest Rip in the EPT violation handler. We’ll talk about it in the next part.

Finally, you can see the following picture which shows whether our hook successfully applied or not.

Ept hook applied from Vmx Root Mode

Discussion

This part is added to answer to questions about EPT, we’ll discuss different approaches and pros and cons of them. Thanks to Petr for answering these questions.

1. Why there are limitations on calling NT functions in VMX Root Mode?

It is because of paging and high IRQL. The reason is what explained here for the high IRQL and as we’re in a high IRQL in Vmx root mode then some pages(paged pools) might be paged-out.

The hypervisor can use a completely different address space than the NT kernel, I believe this is what regular hypervisors like Hyper-V/XEN do. They don’t use “identity EPT mapping”, therefore VA 0x10000 in VMX-root mode does not point to the same physical memory as 0x10000 in VMX non-root mode.

For example, let’s pick an NT function that can be called at HIGH_IRQL (MmGetPhysicalAddress). Let’s imagine this function is on virtual address 0x1234, but this virtual address points to that function in VMX non-root, in ntoskrnl address space.

The real question should be: “Why can I call some NT functions in VMX-root mode” the answer is that you set HOST_CR3 in VMCS to be the same as CR3 of the NT main System process, therefore hypervisor in vmx root-mode share the same memory view as VMX non-root mode.

It is important to know this, in practice for self-virtualizing hypervisors (like hyperplatform/hvpp), you don’t care, because as I said your HOST_CR3 is the same as NT’s CR3, therefore you can touch whatever memory you want.

If you happened to work on HyperV or XEN, you don’t have the same luxury. the hypervisor memory address space is not mapped at all in the virtualized OS (that’s quite the point of virtualization).

2. Why we shouldn’t modify EPT in VMX Non-Root?

In the ideal world, no memory of the hypervisor should be visible from the virtualized OS (you cant see XEN internals from the virtualized OS for example).

in hyperplatform/hvpp, you can see the memory of the hypervisor. Why? This time it’s not because of HOST_CR3 but because of identity EPT mapping - you set EPT tables in such a way, that the virtualized OS can see even the memory of the hypervisor itself.

My point is - in the ideal world you shouldn’t even see the EPT structures from within the VMX non-root mode, imagine it this way, can you modify regular page-tables from user-mode?

The answer is it depends. In reality? No. Why? because the page-tables are in kernel memory that is inaccessible from the user-mode. That’s the whole point of memory protection. Could you set page tables in such a way that it would be possible to modify them from user-mode? Yes, but it doesn’t mean you should though. This is sort of a security thing.

There’s one even more important reason: caches

Now you might have tried it and it worked most of the time in your case but that doesn’t mean it’s the correct approach.

3. What are the advantages of having EPT table for each processor separately?

When you change EPT structures and you want that change to be synced across CPUs, you have to perform IPI (KeIpiGenericCall) from within VMX root mode to flush caches on all CPUs.

In an ideal world, you would call KeIpiGenericCall from VMX-root mode. but you can’t - you’ll fastly end up in a deadlock. You’d need to implement your own IPI mechanism and set correctly APIC for VMX-root mode.

Now this can be done - but it would be non-trivial to implement.

When you have multiple EPTs for each CPU, you don’t have to do IPIs, each core manages its own EPT.

Now they won’t be 100% synced all the time, but if the EPT handler logic is the same for each core and doesn’t change over time, it doesn’t matter.

Conclusion

We come to the end of this part. I believe EPT is the most important feature that can be used by researchers, security programs and game hackers as it gives a unique ability to monitor the operating system and user-mode applications. In the next part, we’ll be using EPT and implement hidden hook mechanisms, which commonly used among hypervisors. Also, we’ll improve our hypervisor by using WPP Tracing instead of using DbgPrint, event injection, and a mechanism to talk from Vmx root-mode to Vmx non-root mode and finally we’ll see how to use Virtual Processor Identifier (VPID). Feel free to use the comments below to ask questions or ask for clarification.

See you guys in the next part.

The 8th part is available here.

Animmmmeee :)

References

[1] Memory type range register - (https://en.wikipedia.org/wiki/Memory_type_range_register)
[2] KVA Shadow: Mitigating Meltdown on Windows - (https://msrc-blog.microsoft.com/2018/03/23/kva-shadow-mitigating-meltdown-on-windows/)
[3] How to Implement a software-based SMEP(Supervisor Mode Execution Protection) with Virtualization/Hypervisor Technology - (http://hypervsir.blogspot.com/2014/11/how-to-implement-software-based.html)
[4] Vol 3A – Chapter 11 – (11.11.3 Example Base and Mask Calculations) - (https://software.intel.com/en-us/articles/intel-sdm)
[5] x86 Paging Tutorial - (https://cirosantilli.com/x86-paging)
[6] OSDev notes 2: Memory management - (http://ethv.net/workshops/osdev/notes/notes-2)
[7] Vol 3A – Chapter 11 – (11.11 MEMORY TYPE RANGE REGISTERS (MTRRS)) - (https://software.intel.com/en-us/articles/intel-sdm)
[8] Vol 3A – Chapter 11 – (11.12 PAGE ATTRIBUTE TABLE (PAT)) - (https://software.intel.com/en-us/articles/intel-sdm)
[9] HyperPlatform User Document - (https://tandasat.github.io/HyperPlatform/userdocument/)
[10] Vol 3C – Chapter 34– (34.15.2 SMM VM Exits) - (https://software.intel.com/en-us/articles/intel-sdm)
[11] Vol 3C – Chapter 34– (34.15.6 Activating the Dual-Monitor Treatment) - (https://software.intel.com/en-us/articles/intel-sdm)
[12] Windows Hotpatching: A Walkthrough - (https://jpassing.com/2011/05/03/windows-hotpatching-a-walkthrough/)
[13] Vol 3C – Chapter 28– (28.2.3.1 EPT Misconfigurations) - (https://software.intel.com/en-us/articles/intel-sdm)
[14] Vol 3C – Chapter 28– (28.2.3.2 EPT Violations) - (https://software.intel.com/en-us/articles/intel-sdm)
[15] R.I.P ROP: CET Internals in Windows 20H1 - (http://windows-internals.com/cet-on-windows)
[16] Inside Windows Page Frame Number (PFN) Part 1 - (https://rayanfam.com/topics/inside-windows-page-frame-number-part1)
[17] Inside Windows Page Frame Number (PFN) Part 2 - (https://rayanfam.com/topics/inside-windows-page-frame-number-part2)
[18] why we can access memory from non paged pool at or above DISPATCH LEVEL - (https://stackoverflow.com/questions/18764211/why-we-can-access-memory-from-non-paged-pool-at-or-above-dispatch-level)

This post is licensed under CC BY 4.0 by the author.

Reversing Windows Internals (Part 1) - Digging Into Handles, Callbacks & ObjectTypes

Hypervisor From Scratch – Part 8: How To Do Magic With Hypervisor!

Comments powered by Disqus.

diff --git a/topics/hypervisor-from-scratch-part-8/index.html b/topics/hypervisor-from-scratch-part-8/index.html new file mode 100644 index 00000000..74f1bdb8 --- /dev/null +++ b/topics/hypervisor-from-scratch-part-8/index.html @@ -0,0 +1,4169 @@ + Hypervisor From Scratch – Part 8: How To Do Magic With Hypervisor! | Rayanfam Blog
Home Hypervisor From Scratch – Part 8: How To Do Magic With Hypervisor!
Post
Cancel

Hypervisor From Scratch – Part 8: How To Do Magic With Hypervisor!

If you’re looking to use a hypervisor for analysis and reverse engineering tasks, check out HyperDbg Debugger. It’s a hypervisor-based debugger designed specifically for analyzing, fuzzing, and reversing applications. A free and comprehensive tutorial on hypervisor-based reverse engineering is available at OpenSecurityTraining2’s website (preferred) and YouTube, which demonstrates numerous practical examples on how to utilize hypervisors for reverse engineering.

Introduction

Hi guys,

Welcome to the 8th part of the Hypervisor From Scratch. If you reach here, then you probably finished reading the 7th part, and personally, I believe the 7th part was the most challenging part to understand so hats off, you did a great job.

The 8th part would be an exciting part as we’ll see lots of real-world and practical examples of solving reverse-engineering related problems with hypervisors. For example, we’ll see how hidden hooks work in the presence of hypervisor or how to create a syscall hook, and we’re eventually able to transfer messages from vmx root to OS (vmx non-root) and then into user-mode thus it gives us a valuable bunch of information about how the system works.

Besides some OS-related concepts, we’ll also see some CPU related topics like VPIDs and some general information about how patches for Meltdown and Spectre works.

Event injection, Exception Bitmap, and also adding support to virtualize a hyper-v machine are other titles that will be discussed.

Before starting, I should give special thanks to my friend Petr Benes for his contributions to Hypervisor From Scratch, of course, Hypervisor From Scratch could never have existed without his help and to Liran Alon for great helps on fixing VPID problem, and to Gerhart for his in-depth knowledge about Hyper-V internals that makes Hypervisor From Scratch available for Hyper-V.

Overview

This part is divided into eight main sections :

  1. How to inject interrupts (Event) into the guest and Exception Bitmap
  2. Implementing hidden hooks using EPT
  3. Syscall hook
  4. Invalidating EPT caches using VPID
  5. Demonstrating a custom VMX Root-mode compatible message tracing mechanism and adding WPP Tracing to our Hypervisor
  6. We’ll add support to Hyper-V
  7. Fixing some previous design caveats
  8. Discussion (In this section we discuss the different question and approaches about various topics in this part)

The full source code of this tutorial is available on GitHub :

[https://github.com/SinaKarvandi/Hypervisor-From-Scratch]

Table of Contents

  • Introduction
  • Overview
  • Table of Contents
  • Event Injection

    • Vectored Events
      1. Interrupts
      2. Exceptions
    • Exception Classifications
    • Event Injection Fields
    • Vectored Event Injection
    • Exception Error Codes
  • Exception Bitmap
  • Monitor Trap Flag (MTF)
  • Hidden Hooks (Simulating Hardware Debug Registers Without Any Limitation)

    • Hidden Hooks Scenarios for Read/Write and Execute
    • Implementing Hidden Hooks

    • Removing Hooks From Pages

    • An Important Note When Modifying EPT Entries
  • System-Call Hook
    • Finding Kernel Base
    • Finding SSDT and Shadow SSDT Tables
    • Get Routine Address by Syscall Number
  • Virtual Processor ID (VPID) & TLB
    • INVVPID - Invalidate Translations Based on VPID
      1. Individual-address invalidation
      2. Single-context invalidation
      3. All-contexts invalidation
      4. Single-context invalidation, retaining global translations
    • Important Notes For Using VPIDs
    • INVVPID vs. INVPCID
  • Designing A VMX Root-mode Compatible Message Tracing
    • Concepts
      1. What’s a spinlock?
      2. Test-and-Set
      3. What do we mean by “Safe”?
      4. What is DPC?
    • Challenges
    • Designing A Spinlock
    • Message Tracer Design
      1. Initialization Phase
      2. Sending Phase (Saving Buffer and adding them to pools)
      3. Reading Phase (Read buffers and send them to user-mode)
      4. Checking for new messages
      5. Sending messages to pools
      6. Receiving buffers and messages in user-mode
      7. IOCTL and managing user-mode requests
      8. User-mode notify callback
      9. Uninitialization Phase
  • WPP Tracing
  • Supporting to Hyper-V

    • Enable Nested Virtualization

    • Hyper-V’s visible behavior in nested virtualization

    • Hyper-V Hypervisor Top-Level Functional Specification (TLFS)

    • Out of Range MSRs

    • Hyper-V Hypercalls (VMCALLs)

    • Hyper-V Interface CPUID Leaves

  • Fixing Previous Design Issues
    • Fixing the problem with pre-allocated buffers
    • Avoid Intercepting Accesses to CR3
    • Restoring IDTR, GDTR, GS Base and FS Base
  • Let’s Test it!
    • View WPP Tracing Messages
    • How to test?
      1. Event Injection & Exception Bitmap Demo
      2. Hidden Hooks Demo
        • Read/Write Hooks or Hardware Debug Registers Simulation
        • Hidden Execution Hook
      3. Syscall Hook Demo
  • Discussion
  • Conclusion
  • References

Animmmmeee :0

Event Injection

One of the essential parts of the hypervisors is the ability to inject events (events are Interrupts, Exceptions, NMIs, and SMIs) as if they’ve arrived normally, and the capability to monitor received interrupts and exceptions.

This gives us a great ability to manage the guest operating system and unique ability to build applications, For example, if you are developing anti-cheat application, you can easily disable breakpoint and trap interrupts, and it completely disables all the features of Windbg or any other debugger as you’re the first one that is being notified about the breakpoint thus you can decide to abort the breakpoint or give it to the debugger.

This is just a simple example that the attacker needs to find a way around it. You can also use event injections for reverse-engineering purposes, e.g., directly inject a breakpoint into an application that uses different anti-debugging techniques to make its code hidden.

We can also implement some important features of our hypervisor like hidden hooks based on relying on event injection.

Before going deep into the Event Injection, we need to know some basic processor concepts and terms used by Intel. Most of them derived from this post and this answer.

Intel x86 defines two overlapping categories, vectored events (interrupts vs exceptions), and exception classes (faults vs traps vs aborts).

Vectored Events

Vectored Events (interrupts and exceptions) cause the processor to jump into an interrupt handler after saving much of the processor’s state (enough such that execution can continue from that point later).

Exceptions and interrupts have an ID, called a vector, that determines which interrupt handler the processor jumps to. Interrupt handlers are described within the Interrupt Descriptor Table (IDT).

Interrupts

Interrupts occur at random times during the execution of a program, in response to signals from the hardware. System hardware uses interrupts to handle events external to the processor, such as requests to service peripheral devices. The software can also generate interrupts by executing the INT n instruction.

Exceptions

Exceptions occur when the processor detects an error condition while executing an instruction, such as division by zero. The processor identifies a variety of error conditions, including protection violations, page faults, and internal machine faults.

Exception Classifications

Exceptions classified as faultstraps, or aborts depending on the way they reported and whether the instruction that caused the exception could be restarted without loss of program or task continuity.

In summary: traps increment the instruction pointer (RIP), faults do not, and aborts ’explode’.

We’ll start with the fault classification. You’ve probably heard of things called page faults (or segmentation faults if you’re from the past).

A fault is just an exception type that can be corrected and allows the processor the ability to execute some fault handler to rectify an offending operation without terminating the entire operation. When a fault occurs, the system state is reverted to an earlier state before the faulting operation occurred, and the fault handler is called. After executing the fault handler, the processor returns to the faulting instruction to execute it again. That last sentence is important because that means it redoes an instruction execution to make sure the proper results are used in the following operations. This is different from how a trap is handled.

A trap is an exception that is delivered immediately following the execution of a trapping instruction. In our hypervisor, we trap on various instructions, meaning that after the execution of an instruction – say rdtsc or rdtscp – a trap exception is reported to the processor. Once a trap exception is reported, control is passed to a trap handler, which will perform some operation(s). Following the execution of the trap handler, the processor returns to the instruction following the trapping instruction.

An abort, however, is an exception that occurs and doesn’t always yield the location of the error. Aborts are commonly used for reporting hardware errors, or otherwise. You won’t see these very often, and if you do… Well, you’re doing something wrong. It’s important to know that all exceptions are reported on an instruction boundary – excluding aborts. An instruction boundary is quite simple: if you have the bytes 0F 31 48 C1 E2 20 which translates to the instructions,

1
+2
+
rdtsc
+shl rdx, 20h
+

Then the instruction boundary would be between the bytes 31 and 48. That’s because 0F 31 is the instruction opcodes for rdtsc. This way, two instructions separated by a boundary.

Event Injection Fields

Event injection is done with using interruption-information field of VMCS.

The interruption-information is written into the VM-entry fields of the VMCS during VM-entry; after all the guest context has been loaded, including MSRs and Registers, it delivers the exception through the Interrupt Descriptor Table (IDT) using the vector specified in this field. 

The first field to configure event injection is VM-entry interruption-information field (32 bits) or VM_ENTRY_INTR_INFO in the VMCS, this field provides details about the event to be injected.

The following picture shows the detail of each bit.

VM-Entry Interruption-Information

  • The vector (bits 7:0) determines which entry in the IDT is used or which other event is injected or, in other words, it defines the index of Interrupt to be injected in IDT, for example, the following command (!idt) in windbg shows the IDT indexes. (note that the index is the numbers at the left).
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
+
lkd> !idt
+
+Dumping IDT: fffff8012c05b000
+
+00:	fffff80126551100 nt!KiDivideErrorFaultShadow
+01:	fffff80126551180 nt!KiDebugTrapOrFaultShadow	Stack = 0xFFFFF8012C05F9D0
+02:	fffff80126551200 nt!KiNmiInterruptShadow	Stack = 0xFFFFF8012C05F7D0
+03:	fffff80126551280 nt!KiBreakpointTrapShadow
+04:	fffff80126551300 nt!KiOverflowTrapShadow
+05:	fffff80126551380 nt!KiBoundFaultShadow
+06:	fffff80126551400 nt!KiInvalidOpcodeFaultShadow
+07:	fffff80126551480 nt!KiNpxNotAvailableFaultShadow
+08:	fffff80126551500 nt!KiDoubleFaultAbortShadow	Stack = 0xFFFFF8012C05F3D0
+09:	fffff80126551580 nt!KiNpxSegmentOverrunAbortShadow
+0a:	fffff80126551600 nt!KiInvalidTssFaultShadow
+0b:	fffff80126551680 nt!KiSegmentNotPresentFaultShadow
+0c:	fffff80126551700 nt!KiStackFaultShadow
+0d:	fffff80126551780 nt!KiGeneralProtectionFaultShadow
+0e:	fffff80126551800 nt!KiPageFaultShadow
+10:	fffff80126551880 nt!KiFloatingErrorFaultShadow
+11:	fffff80126551900 nt!KiAlignmentFaultShadow
+12:	fffff80126551980 nt!KiMcheckAbortShadow	Stack = 0xFFFFF8012C05F5D0
+13:	fffff80126551a80 nt!KiXmmExceptionShadow
+14:	fffff80126551b00 nt!KiVirtualizationExceptionShadow
+15:	fffff80126551b80 nt!KiControlProtectionFaultShadow
+1f:	fffff80126551c00 nt!KiApcInterruptShadow
+20:	fffff80126551c80 nt!KiSwInterruptShadow
+29:	fffff80126551d00 nt!KiRaiseSecurityCheckFailureShadow
+2c:	fffff80126551d80 nt!KiRaiseAssertionShadow
+2d:	fffff80126551e00 nt!KiDebugServiceTrapShadow
+2f:	fffff80126551f00 nt!KiDpcInterruptShadow
+30:	fffff80126551f80 nt!KiHvInterruptShadow
+31:	fffff80126552000 nt!KiVmbusInterrupt0Shadow
+32:	fffff80126552080 nt!KiVmbusInterrupt1Shadow
+33:	fffff80126552100 nt!KiVmbusInterrupt2Shadow
+34:	fffff80126552180 nt!KiVmbusInterrupt3Shadow
+...
+

The interruption type (bits 10:8) determines details of how the injection is performed.

In general, a VMM should use the type hardware exception for all exceptions other than the following:

  • Breakpoint exceptions (#BP): a VMM should use the type software exception.
  • Overflow exceptions (#OF): a VMM should use the use type software exception.
  • Those debug exceptions (#DB) that are generated by INT1 (a VMM should use the use type privileged software exception).

For exceptions, the deliver-error-code bit (bit 11) determines whether delivery pushes an error code on
the guest stack. (we’ll talk about error-code later)

The last bit is that VM entry injects an event if and only if the valid bit (bit 31) is 1. The valid bit in this field is cleared on every VM exit means that when you want to inject an event, you set this bit to inject your interrupt and the processor will automatically clear it at the next VM-Exit.

The second field that controls the event injection is VM-entry exception error code.

VM-entry exception error code (32 bits) or VM_ENTRY_EXCEPTION_ERROR_CODE in the VMCS: This field is used if and only if the valid bit (bit 31) and the deliver error-code bit (bit 11) are both set in the VM-entry interruption-information field.

The third field that controls the event injection is VM-entry instruction length.

VM-entry instruction length (32 bits) or VM_ENTRY_INSTRUCTION_LEN in the VMCS: For injection of events whose type is a software interrupt, software exception, or privileged software exception, this field is used to determine the value of RIP that is pushed on the stack.

All in all, these things in VMCS control the Event Injection process: VM_ENTRY_INTR_INFO, VM_ENTRY_EXCEPTION_ERROR_CODE, VM_ENTRY_INSTRUCTION_LEN.

Vectored Event Injection

If the valid bit in the VM-entry interruption-information field is 1, VM entry causes an event to be delivered (or made pending) after all components of the guest state have been loaded (including MSRs) and after the VM-execution control fields have been established.

The interruption type (which is described above) can be one of the following values.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
enum _INTERRUPT_TYPE
+{
+	INTERRUPT_TYPE_EXTERNAL_INTERRUPT = 0,
+	INTERRUPT_TYPE_RESERVED = 1,
+	INTERRUPT_TYPE_NMI = 2,
+	INTERRUPT_TYPE_HARDWARE_EXCEPTION = 3,
+	INTERRUPT_TYPE_SOFTWARE_INTERRUPT = 4,
+	INTERRUPT_TYPE_PRIVILEGED_SOFTWARE_INTERRUPT = 5,
+	INTERRUPT_TYPE_SOFTWARE_EXCEPTION = 6,
+	INTERRUPT_TYPE_OTHER_EVENT = 7
+};
+

Now it’s time to set the vector bit. The following enum is the representation of the indexes in IDT. (Look at the indexes of !idt command above).

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
+
+typedef enum _EXCEPTION_VECTORS
+{
+	EXCEPTION_VECTOR_DIVIDE_ERROR,
+	EXCEPTION_VECTOR_DEBUG_BREAKPOINT,
+	EXCEPTION_VECTOR_NMI,
+	EXCEPTION_VECTOR_BREAKPOINT,
+	EXCEPTION_VECTOR_OVERFLOW,
+	EXCEPTION_VECTOR_BOUND_RANGE_EXCEEDED,
+	EXCEPTION_VECTOR_UNDEFINED_OPCODE,
+	EXCEPTION_VECTOR_NO_MATH_COPROCESSOR,
+	EXCEPTION_VECTOR_DOUBLE_FAULT,
+	EXCEPTION_VECTOR_RESERVED0,
+	EXCEPTION_VECTOR_INVALID_TASK_SEGMENT_SELECTOR,
+	EXCEPTION_VECTOR_SEGMENT_NOT_PRESENT,
+	EXCEPTION_VECTOR_STACK_SEGMENT_FAULT,
+	EXCEPTION_VECTOR_GENERAL_PROTECTION_FAULT,
+	EXCEPTION_VECTOR_PAGE_FAULT,
+	EXCEPTION_VECTOR_RESERVED1,
+	EXCEPTION_VECTOR_MATH_FAULT,
+	EXCEPTION_VECTOR_ALIGNMENT_CHECK,
+	EXCEPTION_VECTOR_MACHINE_CHECK,
+	EXCEPTION_VECTOR_SIMD_FLOATING_POINT_NUMERIC_ERROR,
+	EXCEPTION_VECTOR_VIRTUAL_EXCEPTION,
+	EXCEPTION_VECTOR_RESERVED2,
+	EXCEPTION_VECTOR_RESERVED3,
+	EXCEPTION_VECTOR_RESERVED4,
+	EXCEPTION_VECTOR_RESERVED5,
+	EXCEPTION_VECTOR_RESERVED6,
+	EXCEPTION_VECTOR_RESERVED7,
+	EXCEPTION_VECTOR_RESERVED8,
+	EXCEPTION_VECTOR_RESERVED9,
+	EXCEPTION_VECTOR_RESERVED10,
+	EXCEPTION_VECTOR_RESERVED11,
+	EXCEPTION_VECTOR_RESERVED12
+};
+

In general, the event is delivered as if it had been generated normally, and the event is delivered using the vector in that field to select a descriptor in the IDT. Since event injection occurs after loading IDTR (IDT Register) from the guest-state area, this is the guest IDT, or in other words, the event is delivered to GUEST_IDTR_BASE and GUEST_IDTR_LIMIT.

Putting the above descriptions into the implementation, we have the following function :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
// Injects interruption to a guest
+VOID EventInjectInterruption(INTERRUPT_TYPE InterruptionType, EXCEPTION_VECTORS Vector, BOOLEAN DeliverErrorCode, ULONG32 ErrorCode)
+{
+	INTERRUPT_INFO Inject = { 0 };
+	Inject.Valid = TRUE;
+	Inject.InterruptType = InterruptionType;
+	Inject.Vector = Vector;
+	Inject.DeliverCode = DeliverErrorCode;
+	__vmx_vmwrite(VM_ENTRY_INTR_INFO, Inject.Flags);
+
+	if (DeliverErrorCode) {
+		__vmx_vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, ErrorCode);
+	}
+}
+

As an example we want to inject a #BP (breakpoint) into the guest, we can use the following code :

1
+2
+3
+4
+5
+6
+7
+8
+
/* Inject #BP to the guest (Event Injection) */
+VOID EventInjectBreakpoint()
+{
+EventInjectInterruption(INTERRUPT_TYPE_SOFTWARE_EXCEPTION, EXCEPTION_VECTOR_BREAKPOINT, FALSE, 0);
+UINT32 ExitInstrLength;
+__vmx_vmread(VM_EXIT_INSTRUCTION_LEN, &ExitInstrLength);
+__vmx_vmwrite(VM_ENTRY_INSTRUCTION_LEN, ExitInstrLength);
+}
+

Or if we want to inject a #GP(0) or general protection fault with error code 0 then we use the following code:

1
+2
+3
+4
+5
+6
+7
+8
+
/* Inject #GP to the guest (Event Injection) */
+VOID EventInjectGeneralProtection()
+{
+	EventInjectInterruption(INTERRUPT_TYPE_HARDWARE_EXCEPTION, EXCEPTION_VECTOR_GENERAL_PROTECTION_FAULT, TRUE, 0);
+	UINT32 ExitInstrLength;
+	__vmx_vmread(VM_EXIT_INSTRUCTION_LEN, &ExitInstrLength);
+	__vmx_vmwrite(VM_ENTRY_INSTRUCTION_LEN, ExitInstrLength);
+}
+

You can write functions for other types of interrupts and exceptions. The only thing that you should consider is the InterruptionType, which is always hardware exception except for #DP, #BP, #OF, which is discussed above.

Exception Error Codes

You might notice that we used VM_ENTRY_EXCEPTION_ERROR_CODE in the VMCS and 11th bit of the interruption-information field, and for some exceptions, we disabled them while for some others we set them to a specific value, so what’s that error codes?

Some exceptions will push a 32-bit “error code” on to the top of the stack, which provides additional information about the error. This value must be pulled from the stack before returning control back to the currently running program. (i.e., before calling IRET for returning from interrupt).

The fact that the error code must be pulled from the stack makes event injection more complicated as we have to make sure whether the Windows tries to pull error code from the stack or not, as it turns to error if we put something onto the stack that Windows doesn’t expect to pull it later or we didn’t push anything but Windows thoughts there is something in the stack that needs to be pulled.

The following table shows some of these exceptions with the presence or absence of Error code, this table is derived from Intel SDM, Volume 1, CHAPTER 6 (Table 6-1. Exceptions and Interrupts).

NameVector nr.TypeMnemonicError code?
Divide-by-zero Error0 (0x0)Fault#DENo
Debug1 (0x1)Fault/Trap#DBNo
Non-maskable Interrupt2 (0x2)Interrupt-No
Breakpoint3 (0x3)Trap#BPNo
Overflow4 (0x4)Trap#OFNo
Bound Range Exceeded5 (0x5)Fault#BRNo
Invalid Opcode6 (0x6)Fault#UDNo
Device Not Available7 (0x7)Fault#NMNo
Double Fault8 (0x8)Abort#DFYes (Zero)
Coprocessor Segment Overrun9 (0x9)Fault-No
Invalid TSS10 (0xA)Fault#TSYes
Segment Not Present11 (0xB)Fault#NPYes
Stack-Segment Fault12 (0xC)Fault#SSYes
General Protection Fault13 (0xD)Fault#GPYes
Page Fault14 (0xE)Fault#PFYes
Reserved15 (0xF)--No
x87 Floating-Point Exception16 (0x10)Fault#MFNo
Alignment Check17 (0x11)Fault#ACYes
Machine Check18 (0x12)Abort#MCNo
SIMD Floating-Point Exception19 (0x13)Fault#XM/#XFNo
Virtualization Exception20 (0x14)Fault#VENo
Reserved21-29 (0x15-0x1D)--No
Security Exception30 (0x1E)-#SXYes
Reserved31 (0x1F)--No
Triple Fault---No
FPU Error InterruptIRQ 13Interrupt#FERRNo

Now that we learn how to create a new events, it’s time to see how to monitor system interrupts.

Exception Bitmap

If you remember from MSR Bitmaps, we have a mask for each MSR that shows whether the read or write on that MSR should cause a vm-exit or not.

The monitoring of exceptions uses the same method, which means that a simple mask governs it. This mask is EXCEPTION_BITMAP in VMCS.

The exception bitmap is a 32-bit field that contains one bit for each exception. When an exception occurs, its vector is used to select a bit in this field. If the bit is 1, the exception causes a VM exit. If the bit is 0, the exception is delivered normally through the IDT.

Now it’s up to you to decide whether you want to inject that exception back to the guest or change the state or whatever you want to do.

For example, if you set the 3rd bit of the EXCEPTION_BITMAP, then whenever a breakpoint occurs somewhere (both user-mode and kernel-mode), a vm-exit with EXIT_REASON_EXCEPTION_NMI (exit reason == 0) occurs.

1
+2
+
	// Set exception bitmap to hook division by zero (bit 1 of EXCEPTION_BITMAP)
+	__vmx_vmwrite(EXCEPTION_BITMAP, 0x8); // breakpoint 3nd bit
+

Now we can change the state of the program, then resume the guest, remember resuming the guest doesn’t cause the exception to be delivered to the guest, we have to inject an event manually if we want that the guest process the event normally. For example, we can use the function “EventInjectBreakpoint,” as mentioned earlier, to inject the exception back to the guest.

The last question is how we can find the index of exception that occurred, you know we might set exception bitmap for multiple exceptions, so we have to know the exact reason why this vm-exit happened or more clearly, what exception causes this vm-exit.

The following VMCS fields report us about the event,

  • VM_EXIT_INTR_INFO
  • VM_EXIT_INTR_ERROR_CODE

The following table shows how we can use VM_EXIT_INTR_INFO.

Which is the following structure:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
typedef union _VMEXIT_INTERRUPT_INFO {
+	struct {
+		UINT32 Vector : 8;
+		UINT32 InterruptionType : 3;
+		UINT32 ErrorCodeValid : 1;
+		UINT32 NmiUnblocking : 1;
+		UINT32 Reserved : 18;
+		UINT32 Valid : 1;
+	};
+	UINT32 Flags;
+}VMEXIT_INTERRUPT_INFO, * PVMEXIT_INTERRUPT_INFO;
+

And we can read the details using vmread instruction, for example, the following command shows how we can detect if breakpoint (0xcc) occurred.

1
+2
+3
+4
+5
+6
+7
+
		// read the exit reason
+		__vmx_vmread(VM_EXIT_INTR_INFO, &InterruptExit);
+
+		if (InterruptExit.InterruptionType == INTERRUPT_TYPE_SOFTWARE_EXCEPTION && InterruptExit.Vector == EXCEPTION_VECTOR_BREAKPOINT)
+		{
+		// Do whatever , e.g re-inject the breakpoint
+		}
+

If we want to re-inject an exception that comes with an error code (see the above table), then the error code can be read using VM_EXIT_INTR_ERROR_CODE in VMCS. After that, write the error code to VM_ENTRY_EXCEPTION_ERROR_CODE and enable the deliver-error-code of VM_ENTRY_INTR_INFO to make sure that re-injection is without any flaw.

Also, keep in mind that page-fault is treated differently you can read Intel SDM for more information.

But wait! Have you notice that exception bitmap are just a 32-bit field in VMCS while we have up to 256 interrupts in IDT ?!

If you’re curious about this question you can read its answer in Discussion section.

Monitor Trap Flag (MTF)

Monitor Trap Flag or MTF is a feature that works exactly like Trap Flag in r/eflags except it’s invisible to the guest.

Whenever you set this flag on CPU_BASED_VM_EXEC_CONTROL, after VMRESUME, the processor executes one instruction then a vm-exit occurs.

We have to clear this flag otherwise each instruction cause a vm-exit.

The following function is responsible for setting and unsetting MTF.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
/* Set the monitor trap flag */
+VOID HvSetMonitorTrapFlag(BOOLEAN Set)
+{
+	ULONG CpuBasedVmExecControls = 0;
+
+	// Read the previous flag
+	__vmx_vmread(CPU_BASED_VM_EXEC_CONTROL, &CpuBasedVmExecControls);
+
+	if (Set) {
+		CpuBasedVmExecControls |= CPU_BASED_MONITOR_TRAP_FLAG;
+	}
+	else {
+		CpuBasedVmExecControls &= ~CPU_BASED_MONITOR_TRAP_FLAG;
+	}
+
+	// Set the new value 
+	__vmx_vmwrite(CPU_BASED_VM_EXEC_CONTROL, CpuBasedVmExecControls);
+}
+

Setting MTF leads to a vm-exit with exit reason (EXIT_REASON_MONITOR_TRAP_FLAG), we unset the MTF in the vm-exit handler.

MTF is essential in implementing hidden hooks, more details about MtfEptHookRestorePoint later in the hidden hooks section.

Here’s the MTF vm-exit handler.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
	case EXIT_REASON_MONITOR_TRAP_FLAG:
+	{
+		/* Monitor Trap Flag */
+		if (GuestState[CurrentProcessorIndex].MtfEptHookRestorePoint)
+		{
+			// Restore the previous state
+			EptHandleMonitorTrapFlag(GuestState[CurrentProcessorIndex].MtfEptHookRestorePoint);
+			// Set it to NULL
+			GuestState[CurrentProcessorIndex].MtfEptHookRestorePoint = NULL;
+		}
+		else
+		{
+			LogError("Why MTF occured ?!");
+		}
+
+		// Redo the instruction 
+		GuestState[CurrentProcessorIndex].IncrementRip = FALSE;
+
+		// We don't need MTF anymore
+		HvSetMonitorTrapFlag(FALSE);
+
+		break;
+	}
+

Hidden Hooks

(Simulating Hardware Debug Registers Without Any Limitation)

Have you ever used hardware debugger registers ?!

The debug registers allow researchers and programmers to selectively enable various debug conditions (read, write, execute) associated with a set of four debug addresses without any change in program instructions.

As you know, we can set up to 4 locations to these hardware registers, and it’s the worst limitation for these registers.

so what if we have a structure (let say _EPROCESS) and we want to see what function in Windows Read or Write in this structure?

It’s not possible with current debug registers but we use EPT to rescue !

Hidden Hooks Scenarios for Read/Write and Execute

We have two strategies for hidden hooks, one for Read/Write and one for Execute.

For Read/Write,

we unset read or write or both (based on how user wants) in the entry corresponding to the address.

This means before read or write a vm-exit occurs, and an EPT Violation will notify us. In the EPT Violation handler, we log the address that tries to read or write, then we find the entry in EPT table and set both read and write (means that any read or write to the page is allowed) and also set an MTF flag.

VMM resumes, and one instruction executes, or in other words, read or write is performed, then an MTF vm-exit occurs. In MTF vm-exit handler, we unset the read and write access again so any future access to that page will cause an EPT Violation.

Note that all of the above scenarios happen to one core. Each core has a separate TLB and separate Monitor Trap Flag.

For Execute,

For execution, we use a capability in Intel processors called execute-only.

Execute-only means that we can have a page with execute access enabled while read and write access is disabled.

If the user wants an execution hook, then we find the entry in EPT Table and unset read and write access and set the execute access. Then we create a copy from the original page (Page A) to somewhere else (Page B) and modify the copied page (Page B) with an absolute jump to the hook function.

Now, each time that any instruction attempted to execute our function, the absolute jump is performed, and our hook function is called. Each time any instruction tries to read or write to that location, an EPT Violation occurs as we unset read and write access to that page, so we can swap the original page (Page A) and also set the monitor trap flag to restore the hook after executing one instruction.

Wasn’t it easy ? Review it one more time if you didn’t understand.

You can also think about the different methods; for example, DdiMon creates a copy from that page and modifies the hook location by replacing one bytes (0xcc) breakpoint there. Now it intercepts each breakpoint (using Exception Bitmap) and swaps the original page. This method is much simpler to implement and more reliable, but it causes vm-exit for each hook, so it’s slower, but the first method for EPT Hooks never causes a vm-exit for execution.

Vm-exits for Read and Write hooks are unavoidable.

The execution hook for this part is derived from Gbps hv.

Let’s dig into implementation.

Implementing Hidden Hooks

For hooking functions, first, we split the page into 4KB entries, as described in the previous part. Then find the entry and read that entry. We want to save the details of a hooked page so we can use it later. For read/write hooks, we unset read or write or both, while for executing hooks, we unset read/write access and set execute access and also copy the page contents into a new page and swap the entry’s physical address with the second page’s physical address (fake page’s physical address).

Then we build a trampoline (explained later) and finally decide how to invalidate the TLB based on vmx-state (vmx-root or vmx non-root) and finally add the hook details to the HookedPagesList.

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
+
/* This function returns false in VMX Non-Root Mode if the VM is already initialized
+   This function have to be called through a VMCALL in VMX Root Mode */
+BOOLEAN EptPerformPageHook(PVOID TargetAddress, PVOID HookFunction, PVOID* OrigFunction, BOOLEAN UnsetRead, BOOLEAN UnsetWrite, BOOLEAN UnsetExecute) {
+
+	EPT_PML1_ENTRY ChangedEntry;
+	INVEPT_DESCRIPTOR Descriptor;
+	SIZE_T PhysicalAddress;
+	PVOID VirtualTarget;
+	PVOID TargetBuffer;
+	PEPT_PML1_ENTRY TargetPage;
+	PEPT_HOOKED_PAGE_DETAIL HookedPage;
+	ULONG LogicalCoreIndex;
+
+	// Check whether we are in VMX Root Mode or Not 
+	LogicalCoreIndex = KeGetCurrentProcessorIndex();
+
+	if (GuestState[LogicalCoreIndex].IsOnVmxRootMode && !GuestState[LogicalCoreIndex].HasLaunched)
+	{
+		return FALSE;
+	}
+
+	/* Translate the page from a physical address to virtual so we can read its memory.
+	 * This function will return NULL if the physical address was not already mapped in
+	 * virtual memory.
+	 */
+	VirtualTarget = PAGE_ALIGN(TargetAddress);
+
+	PhysicalAddress = (SIZE_T)VirtualAddressToPhysicalAddress(VirtualTarget);
+
+	if (!PhysicalAddress)
+	{
+		LogError("Target address could not be mapped to physical memory");
+		return FALSE;
+	}
+
+	// Set target buffer, request buffer from pool manager , we also need to allocate new page to replace the current page ASAP
+	TargetBuffer = PoolManagerRequestPool(SPLIT_2MB_PAGING_TO_4KB_PAGE, TRUE, sizeof(VMM_EPT_DYNAMIC_SPLIT));
+
+	if (!TargetBuffer)
+	{
+		LogError("There is no pre-allocated buffer available");
+		return FALSE;
+	}
+
+	if (!EptSplitLargePage(EptState->EptPageTable, TargetBuffer, PhysicalAddress, LogicalCoreIndex))
+	{
+		LogError("Could not split page for the address : 0x%llx", PhysicalAddress);
+		return FALSE;
+	}
+
+	// Pointer to the page entry in the page table. 
+	TargetPage = EptGetPml1Entry(EptState->EptPageTable, PhysicalAddress);
+
+	// Ensure the target is valid. 
+	if (!TargetPage)
+	{
+		LogError("Failed to get PML1 entry of the target address");
+		return FALSE;
+	}
+
+	// Save the original permissions of the page 
+	ChangedEntry = *TargetPage;
+
+	/* Execution is treated differently */
+
+	if (UnsetRead)
+		ChangedEntry.ReadAccess = 0;
+	else
+		ChangedEntry.ReadAccess = 1;
+
+	if (UnsetWrite)
+		ChangedEntry.WriteAccess = 0;
+	else
+		ChangedEntry.WriteAccess = 1;
+
+
+	/* Save the detail of hooked page to keep track of it */
+	HookedPage = PoolManagerRequestPool(TRACKING_HOOKED_PAGES, TRUE, sizeof(EPT_HOOKED_PAGE_DETAIL));
+
+	if (!HookedPage)
+	{
+		LogError("There is no pre-allocated pool for saving hooked page details");
+		return FALSE;
+	}
+
+	// Save the virtual address
+	HookedPage->VirtualAddress = TargetAddress;
+
+	// Save the physical address
+	HookedPage->PhysicalBaseAddress = PhysicalAddress;
+
+	// Fake page content physical address
+	HookedPage->PhysicalBaseAddressOfFakePageContents = (SIZE_T)VirtualAddressToPhysicalAddress(&HookedPage->FakePageContents[0]) / PAGE_SIZE;
+
+	// Save the entry address
+	HookedPage->EntryAddress = TargetPage;
+
+	// Save the orginal entry
+	HookedPage->OriginalEntry = *TargetPage;
+
+
+	// If it's Execution hook then we have to set extra fields
+	if (UnsetExecute)
+	{
+		// Show that entry has hidden hooks for execution
+		HookedPage->IsExecutionHook = TRUE;
+
+		// In execution hook, we have to make sure to unset read, write because
+		// an EPT violation should occur for these cases and we can swap the original page
+		ChangedEntry.ReadAccess = 0;
+		ChangedEntry.WriteAccess = 0;
+		ChangedEntry.ExecuteAccess = 1;
+
+		// Also set the current pfn to fake page
+		ChangedEntry.PageFrameNumber = HookedPage->PhysicalBaseAddressOfFakePageContents;
+
+		// Copy the content to the fake page
+		RtlCopyBytes(&HookedPage->FakePageContents, VirtualTarget, PAGE_SIZE);
+
+		// Create Hook
+		if (!EptHookInstructionMemory(HookedPage, TargetAddress, HookFunction, OrigFunction))
+		{
+			LogError("Could not build the hook.");
+			return FALSE;
+		}
+	}
+
+	// Save the modified entry
+	HookedPage->ChangedEntry = ChangedEntry;
+
+	// Add it to the list 
+	InsertHeadList(&EptState->HookedPagesList, &(HookedPage->PageHookList));
+
+	/***********************************************************/
+	// if not launched, there is no need to modify it on a safe environment
+	if (!GuestState[LogicalCoreIndex].HasLaunched)
+	{
+		// Apply the hook to EPT 
+		TargetPage->Flags = ChangedEntry.Flags;
+	}
+	else
+	{
+		// Apply the hook to EPT 
+		EptSetPML1AndInvalidateTLB(TargetPage, ChangedEntry, INVEPT_SINGLE_CONTEXT);
+	}
+
+	return TRUE;
+}
+

Now we need a function that creates another page and patches the original page (Page A) with an absolute jump (trampoline) that jumps another page (Page B).

In (Page B) we will jump to the hooked function also this function copies the bytes that are patched to the (Page B) and save the original function for the caller to return back to the original page on (Page B).

This is a simple inline hook that we use LDE (LDE64x64) as the detour function.

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
+
BOOLEAN EptHookInstructionMemory(PEPT_HOOKED_PAGE_DETAIL Hook, PVOID TargetFunction, PVOID HookFunction, PVOID* OrigFunction)
+{
+	SIZE_T SizeOfHookedInstructions;
+	SIZE_T OffsetIntoPage;
+
+	OffsetIntoPage = ADDRMASK_EPT_PML1_OFFSET((SIZE_T)TargetFunction);
+	LogInfo("OffsetIntoPage: 0x%llx", OffsetIntoPage);
+
+	if ((OffsetIntoPage + 13) > PAGE_SIZE - 1)
+	{
+		LogError("Function extends past a page boundary. We just don't have the technology to solve this.....");
+		return FALSE;
+	}
+
+	/* Determine the number of instructions necessary to overwrite using Length Disassembler Engine */
+	for (SizeOfHookedInstructions = 0;
+		SizeOfHookedInstructions < 13;
+		SizeOfHookedInstructions += LDE(TargetFunction, 64))
+	{
+		// Get the full size of instructions necessary to copy
+	}
+
+	LogInfo("Number of bytes of instruction mem: %d", SizeOfHookedInstructions);
+
+	/* Build a trampoline */
+
+	/* Allocate some executable memory for the trampoline */
+	Hook->Trampoline = PoolManagerRequestPool(EXEC_TRAMPOLINE, TRUE, MAX_EXEC_TRAMPOLINE_SIZE);
+
+	if (!Hook->Trampoline)
+	{
+		LogError("Could not allocate trampoline function buffer.");
+		return FALSE;
+	}
+
+	/* Copy the trampoline instructions in. */
+	RtlCopyMemory(Hook->Trampoline, TargetFunction, SizeOfHookedInstructions);
+
+	/* Add the absolute jump back to the original function. */
+	EptHookWriteAbsoluteJump(&Hook->Trampoline[SizeOfHookedInstructions], (SIZE_T)TargetFunction + SizeOfHookedInstructions);
+
+	LogInfo("Trampoline: 0x%llx", Hook->Trampoline);
+	LogInfo("HookFunction: 0x%llx", HookFunction);
+
+	/* Let the hook function call the original function */
+	*OrigFunction = Hook->Trampoline;
+
+	/* Write the absolute jump to our shadow page memory to jump to our hook. */
+	EptHookWriteAbsoluteJump(&Hook->FakePageContents[OffsetIntoPage], (SIZE_T)HookFunction);
+
+	return TRUE;
+}
+

For creating a simple absolute jump we use the following function.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
/* Write an absolute x64 jump to an arbitrary address to a buffer. */
+VOID EptHookWriteAbsoluteJump(PCHAR TargetBuffer, SIZE_T TargetAddress)
+{
+	/* mov r15, Target */
+	TargetBuffer[0] = 0x49;
+	TargetBuffer[1] = 0xBB;
+
+	/* Target */
+	*((PSIZE_T)&TargetBuffer[2]) = TargetAddress;
+
+	/* push r15 */
+	TargetBuffer[10] = 0x41;
+	TargetBuffer[11] = 0x53;
+
+	/* ret */
+	TargetBuffer[12] = 0xC3;
+}
+

In the case of EPT Violations, first, we find the details of the physical address that caused this vm-exit. Then we call EptHandleHookedPage to create a log about the details then we set an MTF to restore to the hooked state after executing one instruction.

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
+
/* Check if this exit is due to a violation caused by a currently hooked page. Returns FALSE
+ * if the violation was not due to a page hook.
+ *
+ * If the memory access attempt was RW and the page was marked executable, the page is swapped with
+ * the original page.
+ *
+ * If the memory access attempt was execute and the page was marked not executable, the page is swapped with
+ * the hooked page.
+ */
+BOOLEAN EptHandlePageHookExit(VMX_EXIT_QUALIFICATION_EPT_VIOLATION ViolationQualification, UINT64 GuestPhysicalAddr)
+{
+	BOOLEAN IsHandled = FALSE;
+	PLIST_ENTRY TempList = 0;
+
+	TempList = &EptState->HookedPagesList;
+	while (&EptState->HookedPagesList != TempList->Flink)
+	{
+		TempList = TempList->Flink;
+		PEPT_HOOKED_PAGE_DETAIL HookedEntry = CONTAINING_RECORD(TempList, EPT_HOOKED_PAGE_DETAIL, PageHookList);
+		if (HookedEntry->PhysicalBaseAddress == PAGE_ALIGN(GuestPhysicalAddr))
+		{
+			/* We found an address that match the details */
+
+			/*
+			   Returning true means that the caller should return to the ept state to the previous state when this instruction is executed
+			   by setting the Monitor Trap Flag. Return false means that nothing special for the caller to do
+			*/
+			if (EptHandleHookedPage(HookedEntry, ViolationQualification, GuestPhysicalAddr))
+			{
+				// Next we have to save the current hooked entry to restore on the next instruction's vm-exit
+				GuestState[KeGetCurrentProcessorNumber()].MtfEptHookRestorePoint = HookedEntry;
+
+				// We have to set Monitor trap flag and give it the HookedEntry to work with
+				HvSetMonitorTrapFlag(TRUE);
+
+
+			}
+
+			// Indicate that we handled the ept violation
+			IsHandled = TRUE;
+
+			// Get out of the loop
+			break;
+		}
+	}
+	// Redo the instruction 
+	GuestState[KeGetCurrentProcessorNumber()].IncrementRip = FALSE;
+	return IsHandled;
+
+}
+

Each time an EPT Violation occurs, we check whether it was because Read Access or Write Access or Execute Access violation and log GUEST_RIP, then we restore the initial flags (All read, write, and exec is allowed).

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
+
BOOLEAN EptHandleHookedPage(EPT_HOOKED_PAGE_DETAIL* HookedEntryDetails, VMX_EXIT_QUALIFICATION_EPT_VIOLATION ViolationQualification, SIZE_T PhysicalAddress) {
+
+	ULONG64 GuestRip;
+	ULONG64 ExactAccessedAddress;
+	ULONG64 AlignedVirtualAddress;
+	ULONG64 AlignedPhysicalAddress;
+
+
+	// Get alignment
+	AlignedVirtualAddress = PAGE_ALIGN(HookedEntryDetails->VirtualAddress);
+	AlignedPhysicalAddress = PAGE_ALIGN(PhysicalAddress);
+
+	// Let's read the exact address that was accesses
+	ExactAccessedAddress = AlignedVirtualAddress + PhysicalAddress - AlignedPhysicalAddress;
+
+	// Reading guest's RIP 
+	__vmx_vmread(GUEST_RIP, &GuestRip);
+
+	if (!ViolationQualification.EptExecutable && ViolationQualification.ExecuteAccess)
+	{
+		LogInfo("Guest RIP : 0x%llx tries to execute the page at : 0x%llx", GuestRip, ExactAccessedAddress);
+
+	}
+	else if (!ViolationQualification.EptWriteable && ViolationQualification.WriteAccess)
+	{
+		LogInfo("Guest RIP : 0x%llx tries to write on the page at :0x%llx", GuestRip, ExactAccessedAddress);
+	}
+	else if (!ViolationQualification.EptReadable && ViolationQualification.ReadAccess)
+	{
+		LogInfo("Guest RIP : 0x%llx tries to read the page at :0x%llx", GuestRip, ExactAccessedAddress);
+	}
+	else
+	{
+		// there was an unexpected ept violation
+		return FALSE;
+	}
+
+	EptSetPML1AndInvalidateTLB(HookedEntryDetails->EntryAddress, HookedEntryDetails->OriginalEntry, INVEPT_SINGLE_CONTEXT);
+
+	// Means that restore the Entry to the previous state after current instruction executed in the guest
+	return TRUE;
+}
+

That’s it! We have a working hidden hooks.

Removing Hooks From Pages

Removing hooks from pages are essential to us because of two reasons; first, sometimes we need to disable the hooks, and second, when we want to turn off hypervisor, we have to remove all the hooks. Otherwise, we might encounter strange behavior.

Removing hooks is simple as we saved details, including original entries in PageHookList; we have to find entries in this list and broadcast to all processors to update their TLBs and also remove that entry.

The following function is for this purpose.

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
+
+/* Remove single hook from the hooked pages list and invalidate TLB */
+BOOLEAN HvPerformPageUnHookSinglePage(UINT64 VirtualAddress) {
+	PLIST_ENTRY TempList = 0;
+	SIZE_T PhysicalAddress;
+
+	PhysicalAddress = PAGE_ALIGN(VirtualAddressToPhysicalAddress(VirtualAddress));
+
+	// Should be called from vmx non-root
+	if (GuestState[KeGetCurrentProcessorNumber()].IsOnVmxRootMode)
+	{
+		return FALSE;
+	}
+
+	TempList = &EptState->HookedPagesList;
+	while (&EptState->HookedPagesList != TempList->Flink)
+	{
+		TempList = TempList->Flink;
+		PEPT_HOOKED_PAGE_DETAIL HookedEntry = CONTAINING_RECORD(TempList, EPT_HOOKED_PAGE_DETAIL, PageHookList);
+
+		if (HookedEntry->PhysicalBaseAddress == PhysicalAddress)
+		{
+			// Remove it in all the cores
+			KeGenericCallDpc(HvDpcBroadcastRemoveHookAndInvalidateSingleEntry, HookedEntry->PhysicalBaseAddress);
+
+			// remove the entry from the list
+			RemoveEntryList(HookedEntry->PageHookList.Flink);
+
+			return TRUE;
+		}
+	}
+	// Nothing found , probably the list is not found
+	return FALSE;
+}
+

In vmx-root, we also search for the specific hook and use EptSetPML1AndInvalidateTLB to return that entry to the initial state, which is previously saved in OriginalEntry.

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
+
/* Remove and Invalidate Hook in TLB */
+// Caution : This function won't remove entries from LIST_ENTRY, just invalidate the paging, use HvPerformPageUnHookSinglePage instead
+BOOLEAN EptPageUnHookSinglePage(SIZE_T PhysicalAddress) {
+	PLIST_ENTRY TempList = 0;
+
+	// Should be called from vmx-root, for calling from vmx non-root use the corresponding VMCALL
+	if (!GuestState[KeGetCurrentProcessorNumber()].IsOnVmxRootMode)
+	{
+		return FALSE;
+	}
+
+	TempList = &EptState->HookedPagesList;
+	while (&EptState->HookedPagesList != TempList->Flink)
+	{
+		TempList = TempList->Flink;
+		PEPT_HOOKED_PAGE_DETAIL HookedEntry = CONTAINING_RECORD(TempList, EPT_HOOKED_PAGE_DETAIL, PageHookList);
+		if (HookedEntry->PhysicalBaseAddress == PAGE_ALIGN(PhysicalAddress))
+		{
+			// Undo the hook on the EPT table
+			EptSetPML1AndInvalidateTLB(HookedEntry->EntryAddress, HookedEntry->OriginalEntry, INVEPT_SINGLE_CONTEXT);
+			return TRUE;
+		}
+	}
+	// Nothing found , probably the list is not found
+	return FALSE;
+}
+

If we want to unhook all the pages, then we use another VMCALL, there is no need to iterate through the list here as all of the hooks must be removed. Just broadcast it through all the cores.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+
/* Remove all hooks from the hooked pages list and invalidate TLB */
+// Should be called from Vmx Non-root
+VOID HvPerformPageUnHookAllPages() {
+
+	// Should be called from vmx non-root
+	if (GuestState[KeGetCurrentProcessorNumber()].IsOnVmxRootMode)
+	{
+		return;
+	}
+
+	// Remove it in all the cores
+	KeGenericCallDpc(HvDpcBroadcastRemoveHookAndInvalidateAllEntries, 0x0);
+
+	// No need to remove the list as it will automatically remove by the pool uninitializer
+}
+

In vmx-root we just iterate through the list and restore them to the initial state.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
/* Remove and Invalidate Hook in TLB */
+// Caution : This function won't remove entries from LIST_ENTRY, just invalidate the paging, use HvPerformPageUnHookAllPages instead
+VOID EptPageUnHookAllPages() {
+	PLIST_ENTRY TempList = 0;
+
+	// Should be called from vmx-root, for calling from vmx non-root use the corresponding VMCALL
+	if (!GuestState[KeGetCurrentProcessorNumber()].IsOnVmxRootMode)
+	{
+		return FALSE;
+	}
+
+	TempList = &EptState->HookedPagesList;
+	while (&EptState->HookedPagesList != TempList->Flink)
+	{
+		TempList = TempList->Flink;
+		PEPT_HOOKED_PAGE_DETAIL HookedEntry = CONTAINING_RECORD(TempList, EPT_HOOKED_PAGE_DETAIL, PageHookList);
+
+		// Undo the hook on the EPT table
+		EptSetPML1AndInvalidateTLB(HookedEntry->EntryAddress, HookedEntry->OriginalEntry, INVEPT_SINGLE_CONTEXT);
+	}
+}
+

An Important Note When Modifying EPT Entries

One interesting thing that I encountered during the test of my driver on the multi-core system was the fact that EPT entries should be modified in one instruction.

For example, if you change the access bits of an EPT entry, bit by bit, then you probably get the error (EPT Misconfiguration) that one access bits changed and before the next access bit applies another core tries to access page table and it sometimes leads to an EPT Misconfiguration and sometimes you might not get the desired behavior.

For example the following method for modifying EPT entries is wrong!

1
+2
+3
+
HookedEntryDetails->EntryAddress->ExecuteAccess = 1;
+HookedEntryDetails->EntryAddress->WriteAccess = 1;
+HookedEntryDetails->EntryAddress->ReadAccess = 1;
+

But the following code is correct. (Applying changes in one instruction instantly).

1
+2
+
		// Apply the hook to EPT 
+		TargetPage->Flags = OriginalEntry.Flags;
+

This is why we have the following function that acquires a spinlock that makes sure that only one entry is modified once and then invalidate that core’s TLB.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
/*  This function set the specific PML1 entry in a spinlock protected area then invalidate the TLB ,
+	this function should be called from vmx root-mode
+*/
+VOID EptSetPML1AndInvalidateTLB(PEPT_PML1_ENTRY EntryAddress, EPT_PML1_ENTRY EntryValue, INVEPT_TYPE InvalidationType)
+{
+	// acquire the lock
+	SpinlockLock(&Pml1ModificationAndInvalidationLock);
+	// set the value
+	EntryAddress->Flags = EntryValue.Flags;
+
+	// invalidate the cache
+	if (InvalidationType == INVEPT_SINGLE_CONTEXT)
+	{
+		InveptSingleContext(EptState->EptPointer.Flags);
+	}
+	else
+	{
+		InveptAllContexts();
+	}
+	// release the lock
+	SpinlockUnlock(&Pml1ModificationAndInvalidationLock);
+}
+

The above function solves the problems of simultaneously modifying the EPT Table as we have one EPT Table for all cores.

System-Call Hook

When it comes to hypervisors, we have different options for hooking system-calls. Each of these methods has its own advantages and disadvantages.

Let’s review some of the methods, that we can use to hook system-calls.

The first method is hooking MSR 0xc0000082 (LSTAR). This MSR is the kernel-entry for dispatching system-calls. Each time an instruction like Syscall is executed in user-mode, the processor automatically switches to kernel-mode and runs the address stored in this MSR. In Windows address of KiSystemCall64 is stored in this MSR.

This means that each time an application needs to call a system-call, it executes a syscall, and now this function is responsible for finding the entries in SSDT and call. In short, SSDT is a table in Windows that stores pointer to Windows function based on a system-call number. All SSDT entries and LSTAR MSR is under the control of PatchGuard.

This brings us three possibilities!

First, we can change the MSR LSTAR to point to our custom function, and to make it PatchGuard compatible, we can set MSR Bitmap that if any kernel routine wants to read this MSR, then a vm-exit occurs so we can change the result. Instead of showing our custom handler, we can show the KiSystemCall64, and PatchGuard will never know that this is a fake MSR.

Hooking MSR LSTAR is complicated, and updates to Meltdown make it even more complicated. In a post-meltdown system, LSTAR points to KiSystemCall64Shadow, which involves changing CR3 and execute KPTI-related instruction and Meltdown mitigation. It’s not a good idea to hook LSTAR as we have difficulties with pre-Meltdown and post-Meltdown mitigations and also as the system-state changes in this MSR so we can’t hook anything in the kernel as the kernel is not mapped on CR3.

Hyperbone uses this method (even it not updated for post-meltdown systems in the time of writing this article).

The second option is finding SSDT tables and change their entry to point to our custom functions, each time the PatchGuard tries to audit these entries, we can show it the not-patched listings. The only thing that we should keep in mind is to find where KiSystemCall64 tries to read that location and save that location somewhere so we can know that if the function that tries to read is syscall dispatcher our other functions (and probably PatchGuard).

Implementing this method is not super-fast as we need to unset EPT Read for SSDT entry, and each time a read happens, a vm-exit occurs, so we have one vm-exit for each syscall thus it makes our computer slow!

The third option is finding functions in SSDT entries and put a hidden hook on the functions that we need to hook. This way, we can catch a custom list of functions because I think hooking all system-calls is stupid!

We implement the third option in this part.

Another possible way is Syscall Hooking Via Extended Feature Enable Register (EFER), as described here. This method is based on disabling Syscall Enable (or SCE bit) of the EFER MSR; hence each time a Syscall is executed, a #UD exception is generated by the processor, and we can intercept #UD by using Exception Bitmap (described above) to handle these syscalls.

Again it’s not a good idea because it leads to a vm-exit for each syscall; thus, it’s substantially slow but usable for experimental purposes.

Also, they might be other options. Don’t hesitate to send a comment to this post and describe if you know one!

Finding Kernel Base

To find SSDT, we need to find nt!KeServiceDescriptorTable and nt!KeServiceDescriptorTableShadow, these tables are exported in x86 systems but not in x64. This makes the things much complicated as the routines to find these tables might change in future versions of Windows; thus, our Syscall hooker might have problems in future versions.

First of all, we need to find the base address of ntoskrnl, and it’s the image size, this is done by using ZwQuerySystemInformation, first, we find this function by using MmGetSystemRoutineAddress.

Then we allocate a memory to get the details from Windows and find the base address and module size.

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
+
/* Get the kernel base and Image size */
+PVOID SyscallHookGetKernelBase(PULONG pImageSize)
+{
+	NTSTATUS status;
+	ZWQUERYSYSTEMINFORMATION ZwQSI = 0;
+	UNICODE_STRING routineName;
+	PVOID pModuleBase = NULL;
+	PSYSTEM_MODULE_INFORMATION pSystemInfoBuffer = NULL;
+	ULONG SystemInfoBufferSize = 0;
+
+
+	RtlInitUnicodeString(&routineName, L"ZwQuerySystemInformation");
+	ZwQSI = (ZWQUERYSYSTEMINFORMATION)MmGetSystemRoutineAddress(&routineName);
+	if (!ZwQSI)
+		return NULL;
+
+
+	status = ZwQSI(SystemModuleInformation,
+		&SystemInfoBufferSize,
+		0,
+		&SystemInfoBufferSize);
+
+	if (!SystemInfoBufferSize)
+	{
+		LogError("ZwQuerySystemInformation (1) failed");
+		return NULL;
+	}
+
+	pSystemInfoBuffer = (PSYSTEM_MODULE_INFORMATION)ExAllocatePool(NonPagedPool, SystemInfoBufferSize * 2);
+
+	if (!pSystemInfoBuffer)
+	{
+		LogError("ExAllocatePool failed");
+		return NULL;
+	}
+
+	memset(pSystemInfoBuffer, 0, SystemInfoBufferSize * 2);
+
+	status = ZwQSI(SystemModuleInformation,
+		pSystemInfoBuffer,
+		SystemInfoBufferSize * 2,
+		&SystemInfoBufferSize);
+
+	if (NT_SUCCESS(status))
+	{
+		pModuleBase = pSystemInfoBuffer->Module[0].ImageBase;
+		if (pImageSize)
+			*pImageSize = pSystemInfoBuffer->Module[0].ImageSize;
+	}
+	else {
+		LogError("ZwQuerySystemInformation (2) failed");
+		return NULL;
+	}
+
+	ExFreePool(pSystemInfoBuffer);
+	return pModuleBase;
+}
+

Update 2: You can also use RtlPcToFileHeader instead of above method:

1
+
RtlPcToFileHeader(&RtlPcToFileHeader, &NtoskrnlBase);
+

Finding SSDT and Shadow SSDT Tables

Now that we have the base address ntoskrnl we can search for this pattern to find nt!KeServiceDescriptorTableShadow.

1
+
	const unsigned char KiSystemServiceStartPattern[] = { 0x8B, 0xF8, 0xC1, 0xEF, 0x07, 0x83, 0xE7, 0x20, 0x25, 0xFF, 0x0F, 0x00, 0x00 };
+

nt!KeServiceDescriptorTableShadow contains the nt!KiServiceTable and win32k!W32pServiceTable, which is the SSDT of Syscall function for both NT Syscalls and Win32K Syscalls.

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
+
+/* Find SSDT address of Nt fucntions and W32Table */
+BOOLEAN SyscallHookFindSsdt(PUINT64 NtTable, PUINT64 Win32kTable)
+{
+	ULONG kernelSize = 0;
+	ULONG_PTR kernelBase;
+	const unsigned char KiSystemServiceStartPattern[] = { 0x8B, 0xF8, 0xC1, 0xEF, 0x07, 0x83, 0xE7, 0x20, 0x25, 0xFF, 0x0F, 0x00, 0x00 };
+	const ULONG signatureSize = sizeof(KiSystemServiceStartPattern);
+	BOOLEAN found = FALSE;
+	LONG relativeOffset = 0;
+	ULONG_PTR addressAfterPattern;
+	ULONG_PTR address;
+	SSDTStruct* shadow;
+	PVOID ntTable;
+	PVOID win32kTable;
+
+	//x64 code
+	kernelBase = (ULONG_PTR)SyscallHookGetKernelBase(&kernelSize);
+
+	if (kernelBase == 0 || kernelSize == 0)
+		return FALSE;
+
+	// Find KiSystemServiceStart
+
+	ULONG KiSSSOffset;
+	for (KiSSSOffset = 0; KiSSSOffset < kernelSize - signatureSize; KiSSSOffset++)
+	{
+		if (RtlCompareMemory(((unsigned char*)kernelBase + KiSSSOffset), KiSystemServiceStartPattern, signatureSize) == signatureSize)
+		{
+			found = TRUE;
+			break;
+		}
+	}
+
+	if (!found)
+		return FALSE;
+
+	addressAfterPattern = kernelBase + KiSSSOffset + signatureSize;
+	address = addressAfterPattern + 7; // Skip lea r10,[nt!KeServiceDescriptorTable]
+	// lea r11, KeServiceDescriptorTableShadow
+	if ((*(unsigned char*)address == 0x4c) &&
+		(*(unsigned char*)(address + 1) == 0x8d) &&
+		(*(unsigned char*)(address + 2) == 0x1d))
+	{
+		relativeOffset = *(LONG*)(address + 3);
+	}
+
+	if (relativeOffset == 0)
+		return FALSE;
+
+	shadow = (SSDTStruct*)(address + relativeOffset + 7);
+
+	ntTable = (PVOID)shadow;
+	win32kTable = (PVOID)((ULONG_PTR)shadow + 0x20);    // Offset showed in Windbg
+
+	*NtTable = ntTable;
+	*Win32kTable = win32kTable;
+
+	return TRUE;
+}
+

Note that nt!KeServiceDescriptorTable only contains the nt!KiServiceTable, and it doesn’t provide win32k!W32pServiceTable.

Get Routine Address by Syscall Number

After finding the NT Syscall Table and Win32k Syscall Table, now it’s time to translate Syscall Numbers to its corresponding address.

The following formula converts API Number to function address.

1
+
((SSDT->pServiceTable[ApiNumber] >> 4) + SSDTbase);
+

Keep in mind that NT Syscalls start from 0x0, but Win32k Syscalls start from 0x1000, so as we computer indexes based on the start of the table, we should minus the Win32k Syscalls with 0x1000.

All in all, we have the following function.

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
+
/* Find entry from SSDT table of Nt fucntions and W32Table syscalls */
+PVOID SyscallHookGetFunctionAddress(INT32 ApiNumber, BOOLEAN GetFromWin32k)
+{
+	SSDTStruct* SSDT;
+	BOOLEAN Result;
+	ULONG_PTR SSDTbase;
+	ULONG ReadOffset;
+	UINT64 NtTable, Win32kTable;
+
+	// Read the address og SSDT
+	Result = SyscallHookFindSsdt(&NtTable, &Win32kTable);
+
+	if (!Result)
+	{
+		LogError("SSDT not found");
+		return 0;
+	}
+
+	if (!GetFromWin32k)
+	{
+		SSDT = NtTable;
+	}
+	else
+	{
+		// Win32k APIs start from 0x1000
+		ApiNumber = ApiNumber - 0x1000;
+		SSDT = Win32kTable;
+	}
+
+	SSDTbase = (ULONG_PTR)SSDT->pServiceTable;
+
+	if (!SSDTbase)
+	{
+		LogError("ServiceTable not found");
+		return 0;
+	}
+	return (PVOID)((SSDT->pServiceTable[ApiNumber] >> 4) + SSDTbase);
+
+}
+

Now that we have the address of the routine that we want, now it’s time to put a hidden hook on that function, we also need their functions prototypes so we can read their arguments appropriately.

The syscall hook example is demonstrated later in the (How to test?) section.

Kizuna ai :)

Virtual Processor ID (VPID) & TLB

In Intel, its explanation about VPIDs is vague, so I found a great link that explains is so much more straightforward; hence it’s better to read the details below instead of starting with SDM.

The translation lookaside buffer (TLB) is a high-speed memory page cache for virtual to physical address translation. It follows the local principle to avoid time-consuming lookups for recently used pages.

Host mappings are not coherent to the guest and vice versa. Each guest has it’s own address space, the mapping table cannot be re-used in another guest (or host). Therefore first-generation VMs like Intel Core 2 (VMX) flush the TLB on each VM-enter (resume) and VM-exit. But flushing the TLB is a show-stopper, it is one of the most critical components in a modern CPU.

Intel engineers started to think about that. Intel Nehalem TLB entries have changed by introducing a Virtual Processor ID. So each TLB entry is tagged with this ID. The CPU does not specify VPIDs, the hypervisor allocates them, whereas the host VPID is 0. Starting with Intel Nehalem, the TLB must not be flushed. When a process tries to access a mapping where the actual VPID does not match with the TLB entry VPID a standard TLB miss occurs. Some Intel numbers show that the latency performance gain is 40% for a VM round trip transition compared to Meron, an Intel Core 2.

Imagine you have two or more VMs:

  • If you enable VPIDs, you don’t have to worry that VM1 accidentally, fetches cached memory of VM2 (or even hypervisor itself)
  • If you don’t enable VPIDs, CPU assigns VPID=0 to all operations (VMX root & VMX non-root) and flushes TLB on each transition for you

A logical processor may tag some cached information with a 16-bit VPID.

The VPID is 0000H in the following situations:

  • Outside VMX operation. (e.g System Management Mode (SMM)).
  • VMX root operation
  • VMX non-root operation when the “enable VPID” VM-execution control is 0

INVVPID - Invalidate Translations Based on VPID

In order to support VPIDs, we have to add CPU_BASED_CTL2_ENABLE_VPID to Secondary Processor-Based VM-Execution Controls.

The next step is to set a 16-bit value to VMCS’s VIRTUAL_PROCESSOR_ID field using VMWRITE instruction. This value is used as an index for the current VMCS on this core so our current VMCS’s VPID is 1.

Also, as described above, 0 has special meaning and should not be used.

1
+2
+3
+4
+5
+6
+
	// Set up VPID
+
+	/* For all processors, we will use a VPID = 1. This allows the processor to separate caching
+	   of EPT structures away from the regular OS page translation tables in the TLB.	*/
+
+	__vmx_vmwrite(VIRTUAL_PROCESSOR_ID, 1);
+

INVVPID (instruction) invalidates mappings in the translation lookaside buffers (TLBs) and paging-structure caches based on the virtual processor identifier (VPID).

For the INVVPID there are 4 types that currently supported by the processors which are reported in the IA32_VMX_EPT_VPID_CAP MSR.

The enumeration for these types are :

1
+2
+3
+4
+5
+6
+7
+
typedef enum _INVVPID_ENUM
+{
+	INDIVIDUAL_ADDRESS = 0x00000000,
+	SINGLE_CONTEXT = 0x00000001,
+	ALL_CONTEXT = 0x00000002,
+	SINGLE_CONTEXT_RETAINING_GLOBALS = 0x00000003
+}INVVPID_ENUM, *PINVVPID_ENUM;
+

I’ll describe these types in detail later.

For the implementation of INVVPID we use an assembly function like this (which executes invvpid from the RCX and RDX for x64 fast calling convention) :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
AsmInvept PROC PUBLIC
+
+    invept  rcx, oword ptr [rdx]
+    jz @jz
+    jc @jc
+    xor     rax, rax
+    ret
+
+    @jz: 
+    mov     rax, VMX_ERROR_CODE_FAILED_WITH_STATUS
+    ret
+
+    @jc:
+    mov     rax, VMX_ERROR_CODE_FAILED
+    ret
+
+AsmInvept ENDP
+

and then, a general purpose function for calling this assembly function :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
inline Invvpid(INVVPID_ENUM Type, INVVPID_DESCRIPTOR* Descriptor)
+{
+	if (!Descriptor)
+	{
+		static INVVPID_DESCRIPTOR ZeroDescriptor = { 0 };
+		Descriptor = &ZeroDescriptor;
+	}
+
+	return AsmInvvpid(Type, Descriptor);
+}
+

For INVVPID, there is a descriptor defined below.

INVVPID Descriptor

This structure defined like this :

1
+2
+3
+4
+5
+6
+7
+
typedef struct _INVVPID_DESCRIPTOR
+{
+	UINT64 VPID : 16;
+	UINT64 RESERVED : 48;
+	UINT64 LINEAR_ADDRESS;
+
+} INVVPID_DESCRIPTOR, *PINVVPID_DESCRIPTOR;
+

The types of INVVPID is defined as below :

  • Individual-address invalidation: If the INVVPID type is 0, the logical processor invalidates mappings for the linear address, and VPID specified in the INVVPID descriptor. In some cases, it may invalidate mappings for other linear addresses (or other VPIDs) as well.
1
+2
+3
+4
+5
+
inline InvvpidIndividualAddress(UINT16 Vpid, UINT64 LinearAddress)
+{
+	INVVPID_DESCRIPTOR Descriptor = { Vpid, 0, LinearAddress };
+	return Invvpid(INDIVIDUAL_ADDRESS, &Descriptor);
+}
+
  • Single-context invalidation: If the INVVPID type is 1, the logical processor invalidates all mappings tagged with the VPID specified in the INVVPID descriptor. In some cases, it may invalidate mappings for other VPIDs as well.
1
+2
+3
+4
+5
+
inline InvvpidSingleContext(UINT16 Vpid)
+{
+	INVVPID_DESCRIPTOR Descriptor = { Vpid, 0, 0 };
+	return Invvpid(SINGLE_CONTEXT, &Descriptor);
+}
+
  • All-contexts invalidation: If the INVVPID type is 2, the logical processor invalidates all mappings tagged with all VPIDs except VPID 0000H. In some cases, it may invalidate translations with VPID 0000H as well.
1
+2
+3
+4
+
inline InvvpidAllContexts()
+{
+	return Invvpid(ALL_CONTEXT, NULL);
+}
+
  • Single-context invalidation, retaining global translations: If the INVVPID type is 3, the logical processor invalidates all mappings tagged with the VPID specified in the INVVPID descriptor except global translations. In some cases, it may invalidate global translations (and mappings with other VPIDs) as well. See the “Caching Translation Information” section in Chapter 4 of the IA-32 Intel Architecture Software Developer’s Manual, Volumes 3A for information about global translations.
1
+2
+3
+4
+5
+
inline InvvpidSingleContextRetainingGlobals(UINT16 Vpid)
+{
+	INVVPID_DESCRIPTOR Descriptor = { Vpid, 0, 0 };
+	return Invvpid(SINGLE_CONTEXT_RETAINING_GLOBALS, &Descriptor);
+}
+

You probably think about how VPIDs can be used in the hypervisor. We can use it instead of INVEPT, but generally, it doesn’t have any particular usage for us. I described it more in the Discussion Section. By the way, VPIDs will be used in implementing special features as it’s more flexible than INVEPT and also when we have multiple VMCS (EPTP). (Can you think about some of them?).

Important Notes For Using VPIDs

There are some important things that you should know when using VPIDs.

Enabling VPIDs have a side-effect of not flushing TLB on VMEntry/VMExit. You should manually flush guest TLB entries if required (By using INVEPT/INVVPID). These issues might be hidden when VPID is disabled.

When VPID is disabled, VMEntry flushes the entire TLB. Thus, the hypervisor doesn’t need to explicitly invalidate TLB entries populated by the guest when performing an operation that should invalidate them (e.g., Modifying an EPT entry). When VPID is enabled, INVEPT/INVVPID should be used.

An easy way for you to find these kinds of issues is indeed the issue you have, is to execute INVEPT global-context before every VMEntry to flush entire TLB while still keeping VPID enabled. If it now works, you should check where you are missing an INVEPT execution.

In my experience, if you just enable VPIDs without any extra assumption, all processes start to crash one by one, and eventually, kernel crashes, and this is because we didn’t invalidate the TLB.

In order to solve the problem of crashing every process, we have to invalidate TLB in the case of Mov to Cr3 thus whenever a vm-exit occurs with reason == EXIT_REASON_CR_ACCESS (28) then if it’s a Mov to Cr3 we have to invalidate TLB (INVEPT or INVVPID [Look at the Update 1 for more details]).

So we edit the code like this:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
	case TYPE_MOV_TO_CR:
+	{
+		switch (CrExitQualification->Fields.ControlRegister)
+		{
+		case 0:
+			__vmx_vmwrite(GUEST_CR0, *RegPtr);
+			__vmx_vmwrite(CR0_READ_SHADOW, *RegPtr);
+			break;
+		case 3:
+			__vmx_vmwrite(GUEST_CR3, (*RegPtr & ~(1ULL << 63)));
+			// InveptSingleContext(EptState->EptPointer.Flags); (changed, look for "Update 1" at the 8th part for more detail)
+			InvvpidSingleContext(VPID_TAG);
+			break;
+		case 4:
+			__vmx_vmwrite(GUEST_CR4, *RegPtr);
+			__vmx_vmwrite(CR4_READ_SHADOW, *RegPtr);
+
+			break;
+		default:
+			LogWarning("Unsupported register %d in handling control registers access", CrExitQualification->Fields.ControlRegister);
+			break;
+		}
+	}
+

Also, note that as we have a single EPTP for all cores then it’s enough to invalidate single-context otherwise we have to invalidate all-contexts.

Update 1 : As Satoshi Tanda mentioned,

The CR3 handler should use INVVPID instead of INVEPT because INVEPT invalidates more than needed. We want to invalid caches of GVA -> HPA (combined mappings), and both instructions do this. This is why INVEPT works too, but INVEPT also invalidates caches of GPA -> HPA (guest-physical mappings), which are not impacted by the guest CR3 change and can be kept without invalidation.

The general guideline is, INVVPID when TLB flush emulation is needed, and INVEPT when EPT entries are changed. You can find more info on those instructions and cache types in :

  • 28.3.1 Information That May Be Cached
  • 28.3.3.3 Guidelines for Use of the INVVPID Instruction.

so instead of InveptSingleContext we used InvvpidSingleContext.

Honestly, we have some misunderstanding about handling Cr3 vm-exits, even though the above code works fine, but generally, it has some performance penalties. I’ll explain these performance problems in the “Fixing Previous Design Issues” section.

You might also ask why we avoid writing the 63rd bit of the CR3.

1
+
			__vmx_vmwrite(GUEST_CR3, (*RegPtr & ~(1ULL << 63)));
+

Bit 63 of CR3 is a new bit that is part of the PCID feature. It allows OS to change CR3 value without invalidating all TLB entries (tagged with the same EP4TA and VPID) besides those marked with global-bit.

EP4TA is the value of bits 51:12 of EPTP.

E.g. Windows KVA Shadowing and Linux KPTI signal this bit on CR3 mov that changes PCID between userspace PCID and kernel space PCID on user and kernel transitions.

We should not write on bit 63 of CR3 on mov reg, cr3 emulation because the processor does not write and attempt to write this will cause a crash on modern Win10. 

INVVPID vs. INVPCID

INVPCID is not really relevant to hypervisor but in the case, if you want to know, INVPCID invalidates mappings in the translation lookaside buffers (TLBs) and paging-structure caches based on the process-context identifier (PCID).

So it’s like INVVPID with the difference that it’s not specific to the hypervisor. It also has its particular contexts (currently 3), you can read more here but generally keep in mind that to reduce that overhead, a feature called Process Context ID (PCID) was introduced by Intel’s Westmere architecture and related instruction, INVPCID (invalidate PCID) with Haswell. With PCID enabled, the way the TLB is used and flushed changes. First, the TLB tags each entry with the PCID of the process that owns the entry. This allows two different mappings from the same virtual address to be stored in the TLB as long as they have a different PCID. Second, with PCID enabled, switching from one set of page tables to another doesn’t flush the TLB any more. Since each process can only use TLB entries that have the right PCID, there’s no need to flush the TLB each time.

This behavior is used in Meltdown mitigation to avoid wiping out the entire TLB for the processors that support PCID.

Designing A VMX Root-mode Compatible Message Tracing

Without any doubt, one of the hardest parts of designing a hypervisor is sending a message from Vmx root-mode to Vmx non-root mode. This is because you have lots of limitations like you can’t access non-paged buffer, and of course, most of the NT functions are not (ANY IRQL) compatible as they might access the buffers that reside in paged pool.

The things are ending here, there are plenty of other limitation to deal with.

This section is inspired by Chapter 6: Kernel Mechanisms (High IRQL Synchronization) from the Windows Kernel Programming book by Pavel Yosifovich which is a really amazing book if you want to start with kernel programming.

Concepts

This section describes some of the Operating System concepts, you should know before starting.

What’s a spinlock?

The Spin Lock is a bit in memory that provides atomic test and modify operations. When a CPU tries to acquire a spinlock, and it’s not currently free, the CPU keeps spinning on the spinlock, busy waiting for it to be released by another CPU means that it keeps checking until another thread which acquired it first release it.

Test-and-Set

You probably read about Test and Set in university. Still, in case you didn’t, in computer science, the test-and-set instruction is an instruction used to write 1 (set) to a memory location and return its old value as a single atomic (i.e., non-interruptible) operation. If multiple processes may access the same memory location, and if a process is currently performing a test-and-set, no other process may begin another test-and-set until the first process’s test-and-set is finished.

What do we mean by “Safe”?

The “safe” is used a lot in hypervisors. By “safe,” we mean something that works all the time and won’t cause system crash or system halt. It’s because it’s so tricky to manage codes in vmx root-mode. After all, interrupts are masked (disabled), or transfer buffer from vmx root-mode to vmx non-root mode needs extra effort, and we should be cautious and avoid executing some APIs to be safe.

What is DPC?

Deferred Procedure Call (DPC) is a Windows mechanism that allows high-priority tasks (e.g., an interrupt handler) to defer required but lower-priority tasks for later execution. This permits device drivers and other low-level event consumers to perform the high-priority part of their processing quickly and schedule non-critical additional processing for execution at a lower priority.

DPCs are implemented by DPC objects which are created and initialized by the kernel when a device driver or some other kernel-mode program issues requests for DPC. The DPC request is then added to the end of a DPC queue. Each processor has a separate DPC queue. DPCs have three priority levels: low, medium, and high. By default, all DPCs are set to medium priority. When Windows drops to an IRQL of Dispatch/DPC level, it checks the DPC queue for any pending DPCs and executes them until the queue is empty or some other interrupt with a higher IRQL occurs.

This is the description of DPCs from MSDN:

Because ISRs must execute as quickly as possible, drivers must usually postpone the completion of servicing an interrupt until after the ISR returns. Therefore, the system provides support for deferred procedure calls (DPCs), which can be queued from ISRs and which are executed at a later time and at a lower IRQL than the ISR.

There are two posts about DPCs here and here, you can read them for more information.

Challenges

For example, Vmx-root mode is not a HIGH_IRQL interrupt (with discussing it in Discussion Section), but as it disables all of the interrupts, we can think like it’s a HIGH_IRQL state. The problem is that must of synchronization functions are designed to be worked on IRQL less than DISPATCH_LEVEL.

Why is it problematic? Imagine you have a one-core processor, and your function requires a spinlock (let say it’s merely a buffer that needs to be accessed). The function raises the IRQL to DISPATCH_LEVEL. Now the Windows Scheduler can’t interrupt the function until it releases the spinlock and lowers the IRQL to PASSIVE_LEVEL or APC_LEVEL. During the execution of the function, a vm-exit occurs; thus, we’re in vmx root-mode now. It’s because, as I told you, vm-exit happens as if it’s a HIGH_IRQL interrupt.

Now, what if we want to access that buffer in vmx root mode? Two scenarios might occur.

  • We wait on a spinlock that was previously acquired by a thread in vmx non-root mode and this we have to wait forever. A deadlock occurs.
  • We enter the function without looking at the lock (while there is another thread that enters the function at the same time.) so it results in a corrupted buffer and invalid data.

The other limitation is in Windows design putting the thread into a waiting state cannot be done at IRQL DISPATCH_LEVEL or higher. It’s because in Windows when you acquire a spinlock it raises the IRQL to 2 – DISPATCH_LEVEL (if not already there), acquire the spinlock, perform the work and finally release the spinlock and lower IRQL back.

If you look at a function like KeAcquireSpinLock and KeReleaseSpinLock, they get an IRQL in their arguments. First, KeAcquireSpinLock saves current IRQL to the parameter supplied by the user then raises the IRQL to DISPATCH_LEVEL and sets a bit. When the function finished its works with shared data, then it calls KeReleaseSpinLock and passes that old IRQL parameter so this function unsets the bit and restore the old IRQL (lowers the IRQL).

Windows has 4 kinds of Spinlocks,

  1. KeAcquireSpinLock – KeReleaseSpinLock : This pair can be called at IRQL <= DISPATCH_LEVEL.
  2. KeAcquireSpinLockAtDpcLevel – KeReleaseSpinLockFromDpcLevel : This pair can be call at IRQL = DISPATCH_LEVEL only, it’s more optimized if you are already in IRQL 2 as it doesn’t saves the old IRQL and it’s specially designed to work on DPC routine.
  3. KeAcquireInterruptSpinLock – KeReleaseInterruptSpinLock: Hardware based use this pair e.g in Interrupt Service Routine (ISR) or it used by drivers with an interrupt source.
  4. ExInterlockedXxx : This function raises the IRQL to HIGH_LEVEL and perform it’s task, it doesn’t need a release function as no one interrupt us on HIGH_IRQL.

But unfortunately, things are more complicated when it comes to vmx root-mode. We don’t have IRQL in the vmx root-mode. It’s an operating system thing, so we can’t use any of the above functions, and things are getting worst if we want to use our message tracing mechanism between multiple cores!

For these reasons, we have to design our custom spinlock.

Designing A Spinlock

Designing spinlock in a multi-core system by its nature needs the hardware support for atomic operation means that hardware (most of the time processor) should guarantee that an operation is performed just by logical (hyper-threaded) core and it’s non-interruptible.

There is an article here that describes different kinds of spinlock with different optimizations, also it’s implemented here.

The design of this mechanism in the processor is beyond the scope of this article. We simply use an intrinsic function provided by Windows called “_interlockedbittestandset”.

This makes our implementation super simple. We just need to use the following function, and it’s the responsibility of the processor to take care of everything.

Update 2: We should use volatile keyword in parameters too, otherwise it’s like un-volatiling.

1
+2
+3
+4
+
inline BOOLEAN SpinlockTryLock(volatile LONG* Lock)
+{
+	return (!(*Lock) && !_interlockedbittestandset(Lock, 0));
+}
+

Now we need to spin! If the above function was not successful, then we have to keep CPU checking to see when another processor releases the lock.

Update 2: We should use volatile keyword in parameters too, otherwise it’s like un-volatiling.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
void SpinlockLock(volatile LONG* Lock)
+{
+	unsigned wait = 1;
+
+	while (!SpinlockTryLock(Lock))
+	{
+		for (unsigned i = 0; i < wait; ++i)
+		{
+			_mm_pause();
+		}
+
+		// Don't call "pause" too many times. If the wait becomes too big,
+		// clamp it to the max_wait.
+
+		if (wait * 2 > max_wait)
+		{
+			wait = max_wait;
+		}
+		else
+		{
+			wait = wait * 2;
+		}
+	}
+}
+

If you wonder what is the _mm_pause() then it’s equal to PAUSE instruction in x86.

Pause instruction is commonly used in the loop of testing spinlock, when some other thread owns the spinlock, to mitigate the tight loop.

PAUSE notifies the CPU that this is a spinlock wait loop, so memory and cache accesses may be optimized. See also pause instruction in x86 for some more details about avoiding the memory-order mis-speculation when leaving the spin-loop. PAUSE may stop CPU for some time to save power. Older CPUs decode it as REP NOP, so you don’t have to check if it’s supported. Older CPUs will simply do nothing (NOP) as fast as possible.

For releasing the lock, there is nothing special to do, so simply unset it without caring for any other processor as there is no other processor that wants to unset it.

Update 2: We should use volatile keyword in parameters too, otherwise it’s like un-volatiling.

1
+2
+3
+4
+
void SpinlockUnlock(volatile LONG* Lock)
+{
+	*Lock = 0;
+}
+

The last step is to use a volatile variable as the lock.

1
+2
+
// Vmx-root lock for logging
+volatile LONG VmxRootLoggingLock;
+

The “volatile” keyword tells the compiler that the value of the variable may change at any time without any action being taken by the code the compiler finds nearby. The implications of this are quite serious. There are lots of examples here if you have a problem with understanding “volatile”.

Message Tracer Design

For solving the above the challenge about deadlock, I create two message pools for saving messages. The first pool is designed to be used as storage for vmx non-root messages (buffers) and the second pool is used for vmx-root messages.

We have the following structure that describes the state of each of these two pools.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
// Core-specific buffers
+typedef struct _LOG_BUFFER_INFORMATION {
+
+	UINT64 BufferStartAddress;						// Start address of the buffer
+	UINT64 BufferEndAddress;						// End address of the buffer
+
+	UINT64 BufferForMultipleNonImmediateMessage;	// Start address of the buffer for accumulating non-immadiate messages
+	UINT32 CurrentLengthOfNonImmBuffer;				// the current size of the buffer for accumulating non-immadiate messages
+
+
+	KSPIN_LOCK BufferLock;							// SpinLock to protect access to the queue
+	KSPIN_LOCK BufferLockForNonImmMessage;			// SpinLock to protect access to the queue of non-imm messages
+
+	UINT32 CurrentIndexToSend;						// Current buffer index to send to user-mode
+	UINT32 CurrentIndexToWrite;						// Current buffer index to write new messages
+
+} LOG_BUFFER_INFORMATION, * PLOG_BUFFER_INFORMATION;
+

Generally, we’ll save the buffer as illustrated below, each chunk of the message came with BUFFER_HEADER that describes that chunk.

Other information for the buffer like Current Index to Write and Current to Send is saved in the above structure.

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
+
A core buffer is like this , it's divided into MaximumPacketsCapacity chucks,
+each chunk has PacketChunkSize + sizeof(BUFFER_HEADER) size
+
+			 __________________________
+			|      BUFFER_HEADER      |
+			|_________________________|
+			|                         |
+			|           BODY          |
+			|         (Buffer)        |
+			| size = PacketChunkSize  |
+			|                         |
+			|_________________________|
+			|      BUFFER_HEADER      |
+			|_________________________|
+			|                         |
+			|           BODY          |
+			|         (Buffer)        |
+			| size = PacketChunkSize  |
+			|                         |
+			|_________________________|
+			|                         |
+			|                         |
+			|                         |
+			|                         |
+			|           .             |
+			|           .             |
+			|           .             |
+			|                         |
+			|                         |
+			|                         |
+			|                         |
+			|_________________________|
+			|      BUFFER_HEADER      |
+			|_________________________|
+			|                         |
+			|           BODY          |
+			|         (Buffer)        |
+			| size = PacketChunkSize  |
+			|                         |
+			|_________________________|
+

The BUFFER_HEADER is defined like this,

1
+2
+3
+4
+5
+6
+
// Message buffer structure
+typedef struct _BUFFER_HEADER {
+	UINT32 OpeationNumber;	// Operation ID to user-mode
+	UINT32 BufferLength;	// The actual length
+	BOOLEAN Valid;			// Determine whether the buffer was valid to send or not
+} BUFFER_HEADER, * PBUFFER_HEADER;
+

We save the length of used length of the chunk and a bit which determine whether we sent it before or not.

Operation Number is number, which will be sent to the user-mode to show the type of the buffer that came from the kernel. In other words, it’s a number that indicates the intention (and structure) of the buffer, so the user-mode application will know what to do with this buffer.

The following Operation Numbers are currently defined :

1
+2
+3
+4
+5
+
// Message area >= 0x4
+#define OPERATION_LOG_INFO_MESSAGE							0x1
+#define OPERATION_LOG_WARNING_MESSAGE						0x2
+#define OPERATION_LOG_ERROR_MESSAGE							0x3
+#define OPERATION_LOG_NON_IMMEDIATE_MESSAGE					0x4
+

Each of them shows a different type of message, and the last one shows that a bunch buffer is accumulated in this buffer. This message tracing is designed to send any kind of the buffer from both vmx root and OS to the user-mode, so it’s not limited just to sending messages, we can send buffers with custom structures and different Operation Numbers.

The last thing about our message tracing is, it can be configured with the following constants, you can change them in order to have a better performance for your exclusive use.

1
+2
+3
+4
+5
+
// Default buffer size
+#define MaximumPacketsCapacity 1000 // number of packets
+#define PacketChunkSize		1000 // NOTE : REMEMBER TO CHANGE IT IN USER-MODE APP TOO
+#define UsermodeBufferSize  sizeof(UINT32) + PacketChunkSize + 1 /* Becausee of Opeation code at the start of the buffer + 1 for null-termminating */
+#define LogBufferSize MaximumPacketsCapacity * (PacketChunkSize + sizeof(BUFFER_HEADER))
+

You can configure things like the maximum number of chunks in a buffer and also the size of each chunk. Setting the above variables is necessary in some cases if there is no thread to consume (read) these chunks and pools are full; it replaces the previous unread buffer. Hence, if you can’t frequently consume the pools, then it’s better to specify a higher number for MaximumPacketsCapacity so that you won’t lose anything.

Initialization Phase

In the initialization phase, we allocate space for the above structure (2 times, one for vmx non-root and one for vmx-root) and then allocate the buffers to be used as the storage for saving our messages.

We have to zero them all and also KeInitializeSpinLock to initialize the spinlock. We use this spinlock only for vmx non-root, and this function makes sure that the value for the lock is unset. We do the same for our custom spinlock (VmxRootLoggingLock), just unset it.

You might ask, what is the “BufferLockForNonImmMessage”, it’s another lock that will use it as optimization (see later).

All in all, we have the following code.

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
+
/* Initialize the buffer relating to log message tracing */
+BOOLEAN LogInitialize() {
+
+
+	// Initialize buffers for trace message and data messages (wee have two buffers one for vmx root and one for vmx non-root)
+	MessageBufferInformation = ExAllocatePoolWithTag(NonPagedPool, sizeof(LOG_BUFFER_INFORMATION) * 2, POOLTAG);
+
+	if (!MessageBufferInformation)
+	{
+		return FALSE; //STATUS_INSUFFICIENT_RESOURCES
+	}
+
+	// Zeroing the memory
+	RtlZeroMemory(MessageBufferInformation, sizeof(LOG_BUFFER_INFORMATION) * 2);
+
+	// Initialize the lock for Vmx-root mode (HIGH_IRQL Spinlock)
+	VmxRootLoggingLock = 0;
+
+	// Allocate buffer for messages and initialize the core buffer information 
+	for (int i = 0; i < 2; i++)
+	{
+
+		// initialize the lock
+		// Actually, only the 0th buffer use this spinlock but let initialize it for both but the second buffer spinlock is useless 
+		// as we use our custom spinlock.
+		KeInitializeSpinLock(&MessageBufferInformation[i].BufferLock);
+		KeInitializeSpinLock(&MessageBufferInformation[i].BufferLockForNonImmMessage);
+
+		// allocate the buffer
+		MessageBufferInformation[i].BufferStartAddress = ExAllocatePoolWithTag(NonPagedPool, LogBufferSize, POOLTAG);
+		MessageBufferInformation[i].BufferForMultipleNonImmediateMessage = ExAllocatePoolWithTag(NonPagedPool, PacketChunkSize, POOLTAG);
+
+		if (!MessageBufferInformation[i].BufferStartAddress)
+		{
+			return FALSE; // STATUS_INSUFFICIENT_RESOURCES
+		}
+
+		// Zeroing the buffer
+		RtlZeroMemory(MessageBufferInformation[i].BufferStartAddress, LogBufferSize);
+
+		// Set the end address
+		MessageBufferInformation[i].BufferEndAddress = (UINT64)MessageBufferInformation[i].BufferStartAddress + LogBufferSize;
+	}
+}
+

Sending Phase (Saving Buffer and adding them to pools)

In a regular Windows routine generally, we shouldn’t be on IRQL more than Dispatch Level. There is no case that our log manager needs to be used in higher IRQLs, so we don’t care about them; thus, we have two different approaches here. First, we acquire the lock (spinlock) using KeAcquireSpinLock in vmx non-root as it’s a Windows optimized way to acquire a lock and for vmx-root mode, we acquire the lock using our previously designed spinlock.

As I told you above, we want to fix this problem that might a vmx-exit occurs when we acquired a lock, so it’s not possible to use the same spinlock as deadlock might happen.

Now we have to see whether we are operating from vmx non-root or vmx root, based on this condition, we select our lock and the index of the buffer that we want to put our message into it.

I’m not gonna explain each step, as it’s easy, it’s just managing buffer and copying data from a buffer to another buffer and also the code is well commented so you can read the code, instead, I explain tricky parts of our message tracing.

After creating a header for our new message buffer, we will copy the bytes and change the information about buffer’s indexes. The last step here is to see whether any thread is waiting to receive our message or not.

If there is no thread waiting for our message then nothing more to do here but if there is a thread which is IRP Pending state (I explain about it later), then we use KeInsertQueueDpc so that it will be added to our DPC Queue which will be subsequently executed by Windows in IRQL == DISPATCH_LEVEL.

It means that our callback function will execute by Windows later and of course, Windows execute our function in vmx non-root so it’s safe. I’ll describe this callback and how we create a DPC later.

Finally, we have to release the locks so that other threads can enter.

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
+
/* Save buffer to the pool */
+BOOLEAN LogSendBuffer(UINT32 OperationCode, PVOID Buffer, UINT32 BufferLength)
+{
+	KIRQL OldIRQL;
+	UINT32 Index;
+	BOOLEAN IsVmxRoot;
+
+	if (BufferLength > PacketChunkSize - 1 || BufferLength == 0)
+	{
+		// We can't save this huge buffer
+		return FALSE;
+	}
+
+	// Check that if we're in vmx root-mode
+	IsVmxRoot = GuestState[KeGetCurrentProcessorNumber()].IsOnVmxRootMode;
+
+	// Check if we're in Vmx-root, if it is then we use our customized HIGH_IRQL Spinlock, if not we use the windows spinlock
+	if (IsVmxRoot)
+	{
+		// Set the index
+		Index = 1;
+		SpinlockLock(&VmxRootLoggingLock);
+	}
+	else
+	{
+		// Set the index
+		Index = 0;
+		// Acquire the lock 
+		KeAcquireSpinLock(&MessageBufferInformation[Index].BufferLock, &OldIRQL);
+	}
+
+	// check if the buffer is filled to it's maximum index or not
+	if (MessageBufferInformation[Index].CurrentIndexToWrite > MaximumPacketsCapacity - 1)
+	{
+		// start from the begining
+		MessageBufferInformation[Index].CurrentIndexToWrite = 0;
+	}
+
+	// Compute the start of the buffer header
+	BUFFER_HEADER* Header = (BUFFER_HEADER*)((UINT64)MessageBufferInformation[Index].BufferStartAddress + (MessageBufferInformation[Index].CurrentIndexToWrite * (PacketChunkSize + sizeof(BUFFER_HEADER))));
+
+	// Set the header
+	Header->OpeationNumber = OperationCode;
+	Header->BufferLength = BufferLength;
+	Header->Valid = TRUE;
+
+	/* Now it's time to fill the buffer */
+
+	// compute the saving index
+	PVOID SavingBuffer = ((UINT64)MessageBufferInformation[Index].BufferStartAddress + (MessageBufferInformation[Index].CurrentIndexToWrite * (PacketChunkSize + sizeof(BUFFER_HEADER))) + sizeof(BUFFER_HEADER));
+
+	// Copy the buffer
+	RtlCopyBytes(SavingBuffer, Buffer, BufferLength);
+
+	// Increment the next index to write
+	MessageBufferInformation[Index].CurrentIndexToWrite = MessageBufferInformation[Index].CurrentIndexToWrite + 1;
+
+	// check if there is any thread in IRP Pending state, so we can complete their request
+	if (GlobalNotifyRecord != NULL)
+	{
+		/* there is some threads that needs to be completed */
+		// set the target pool
+		GlobalNotifyRecord->CheckVmxRootMessagePool = IsVmxRoot;
+		// Insert dpc to queue
+		KeInsertQueueDpc(&GlobalNotifyRecord->Dpc, GlobalNotifyRecord, NULL);
+
+		// set notify routine to null
+		GlobalNotifyRecord = NULL;
+	}
+
+	// Check if we're in Vmx-root, if it is then we use our customized HIGH_IRQL Spinlock, if not we use the windows spinlock
+	if (IsVmxRoot)
+	{
+		SpinlockUnlock(&VmxRootLoggingLock);
+	}
+	else
+	{
+		// Release the lock
+		KeReleaseSpinLock(&MessageBufferInformation[Index].BufferLock, OldIRQL);
+	}
+}
+

Reading Phase (Read buffers and send them to user-mode)

It’s time to read the previously filled buffer! The fact that we add a DPC in the previous function “LogSendBuffer” shows that the “LogReadBuffer” is executed in vmx non-root mode so we can freely use most of the APIs (not all of them).

Theoretically, we have a problem here, if we want to read a buffer from the vmx root-mode pool, then it might cause a deadlock as we acquired a vmx root-mode lock and might a vm-exit occur. Hence, we spin on this lock in vmx root mode forever, but practically there is no deadlock here. Can you guess why?

It’s because our LogReadBuffer executes in DISPATCH_LEVEL so the Windows scheduler won’t interrupt us, and our function is executed without any interruption and the fact that we’re not doing anything fancy here. I mean, we’re not performing anything (like CPUID) that causes a vm-exit in our code, so practically there is nothing to cause deadlock here, but we should keep in mind that we’re not allowed to run codes that cause vmx-exit.

We compute the header address based on previous information and also set the valid bit to zero so that it shows that this buffer is previously used.

Then we copy the buffer to the buffer that specified in arguments also put the Operation Number on the top of the target buffer so that the future functions will know about the intention of this buffer. We can also use DbgPrint to show the messages to the kernel debugger. Using DbgPrint in DISPATCH_LEVEL (vmx non-root mode) is safe. We might need to use DbgPrint multiple times as this function has a maximum of 512 bytes by default. Even though you can change the limit number but we assume the default size is selected.

Finally, we have to reset some of the information regarding buffer, clear the buffer messages (it’s not necessary to zero the buffer, but for making debug process easier, I prefer to zero the buffer), and release the locks.

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
+
/* return of this function shows whether the read was successfull or not (e.g FALSE shows there's no new buffer available.)*/
+BOOLEAN LogReadBuffer(BOOLEAN IsVmxRoot, PVOID BufferToSaveMessage, UINT32* ReturnedLength) {
+
+	KIRQL OldIRQL;
+	UINT32 Index;
+
+	// Check if we're in Vmx-root, if it is then we use our customized HIGH_IRQL Spinlock, if not we use the windows spinlock
+	if (IsVmxRoot)
+	{
+		// Set the index
+		Index = 1;
+
+		// Acquire the lock 
+		SpinlockLock(&VmxRootLoggingLock);
+	}
+	else
+	{
+		// Set the index
+		Index = 0;
+
+		// Acquire the lock 
+		KeAcquireSpinLock(&MessageBufferInformation[Index].BufferLock, &OldIRQL);
+	}
+
+	// Compute the current buffer to read
+	BUFFER_HEADER* Header = (BUFFER_HEADER*)((UINT64)MessageBufferInformation[Index].BufferStartAddress + (MessageBufferInformation[Index].CurrentIndexToSend * (PacketChunkSize + sizeof(BUFFER_HEADER))));
+
+	if (!Header->Valid)
+	{
+		// there is nothing to send
+		return FALSE;
+	}
+
+	/* If we reached here, means that there is sth to send  */
+	// First copy the header 
+	RtlCopyBytes(BufferToSaveMessage, &Header->OpeationNumber, sizeof(UINT32));
+
+
+	// Second, save the buffer contents
+	PVOID SendingBuffer = ((UINT64)MessageBufferInformation[Index].BufferStartAddress + (MessageBufferInformation[Index].CurrentIndexToSend * (PacketChunkSize + sizeof(BUFFER_HEADER))) + sizeof(BUFFER_HEADER));
+	PVOID SavingAddress = ((UINT64)BufferToSaveMessage + sizeof(UINT32)); // Because we want to pass the header of usermode header
+	RtlCopyBytes(SavingAddress, SendingBuffer, Header->BufferLength);
+
+
+#if ShowMessagesOnDebugger
+
+	// Means that show just messages
+	if (Header->OpeationNumber <= OPERATION_LOG_NON_IMMEDIATE_MESSAGE)
+	{
+		/* We're in Dpc level here so it's safe to use DbgPrint*/
+		// DbgPrint limitation is 512 Byte
+		if (Header->BufferLength > DbgPrintLimitation)
+		{
+			for (size_t i = 0; i <= Header->BufferLength / DbgPrintLimitation; i++)
+			{
+				if (i != 0)
+				{
+					DbgPrint("%s", (char*)((UINT64)SendingBuffer + (DbgPrintLimitation * i) - 2));
+				}
+				else
+				{
+					DbgPrint("%s", (char*)((UINT64)SendingBuffer + (DbgPrintLimitation * i)));
+				}
+			}
+		}
+		else
+		{
+			DbgPrint("%s", (char*)SendingBuffer);
+		}
+
+	}
+#endif
+
+	// Finally, set the current index to invalid as we sent it
+	Header->Valid = FALSE;
+
+	// Set the length to show as the ReturnedByted in usermode ioctl funtion + size of header
+	*ReturnedLength = Header->BufferLength + sizeof(UINT32);
+
+
+	// Last step is to clear the current buffer (we can't do it once when CurrentIndexToSend is zero because
+	// there might be multiple messages on the start of the queue that didn't read yet)
+	// we don't free the header
+	RtlZeroMemory(SendingBuffer, Header->BufferLength);
+
+	// Check to see whether we passed the index or not
+	if (MessageBufferInformation[Index].CurrentIndexToSend > MaximumPacketsCapacity - 2)
+	{
+		MessageBufferInformation[Index].CurrentIndexToSend = 0;
+	}
+	else
+	{
+		// Increment the next index to read
+		MessageBufferInformation[Index].CurrentIndexToSend = MessageBufferInformation[Index].CurrentIndexToSend + 1;
+	}
+
+	// Check if we're in Vmx-root, if it is then we use our customized HIGH_IRQL Spinlock, if not we use the windows spinlock
+	if (IsVmxRoot)
+	{
+		SpinlockUnlock(&VmxRootLoggingLock);
+	}
+	else
+	{
+		// Release the lock
+		KeReleaseSpinLock(&MessageBufferInformation[Index].BufferLock, OldIRQL);
+	}
+}
+

Checking for new messages

Checking for the new message is simple; we just need to check the current message index based on previous information and see if its header is valid or not. If it’s valid then it shows that we have a new message, but if it’s not valid, then some function reads the message previously, and there is no new message.

For checking the new message, we even don’t need to acquire a lock because basically we don’t write anything and in our case reading doesn’t need a lock.

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
+
/* return of this function shows whether the read was successfull or not (e.g FALSE shows there's no new buffer available.)*/
+BOOLEAN LogCheckForNewMessage(BOOLEAN IsVmxRoot) {
+
+	KIRQL OldIRQL;
+	UINT32 Index;
+
+	if (IsVmxRoot)
+	{
+		Index = 1;
+	}
+	else
+	{
+		Index = 0;
+	}
+	// Compute the current buffer to read
+	BUFFER_HEADER* Header = (BUFFER_HEADER*)((UINT64)MessageBufferInformation[Index].BufferStartAddress + (MessageBufferInformation[Index].CurrentIndexToSend * (PacketChunkSize + sizeof(BUFFER_HEADER))));
+
+	if (!Header->Valid)
+	{
+		// there is nothing to send
+		return FALSE;
+	}
+
+	/* If we reached here, means that there is sth to send  */
+	return TRUE;
+}
+

Sending messages to pools

Previously, we see how to save (send) buffers and read them. Each message is a buffer of strings, so finally, we have to use “LogSendBuffer” to send our buffer, but we need to consider extra effort to send a well-formed message.

va_start and va_end are used to support multiple arguments to one function, e.g like DbgPrint or printf.

You can use a combination of KeQuerySystemTime, ExSystemTimeToLocalTime, and RtlTimeToTimeFields to get the current system time (see the example) then putting them together with sprintf_s.

There is a particular reason why we use the sprintf-like function instead of RtlString* functions; the reason is described in the Discussion section. The next step is computing length using strnlen_s.

Finally, we have a vital optimization here; logically we create two kinds of messages, one called “Immediate Message” which we will directly send it into the pool and another type is “Non-Immediate Message” which we gather the messages in another buffer and append new messages in that buffer until its capacity is full (we shouldn’t pass the PacketChunkSize limit).

Using this way, we don’t send each message to the user-mode separately but instead, we send multiple messages in one buffer to the user-mode. We will gain visible performance improvement. For example with a configuration with PacketChunkSize == 1000 bytes we send 6 messages on a buffer (it’s average basically it depends on each message size) because you probably know that CPU has to do a lot to change its state from kernel-mode to user-mode and also creating new IRP Packet is a heavy task.

You can also change the configuration, e.g., increase the PacketChunkSize so that more messages will hold on the temporary buffer, but generally, it delays the time you see the message.

Also, we work on a buffer so we need another spinlock here.

Putting it all together we have the following code :

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
+
// Send string messages and tracing for logging and monitoring
+BOOLEAN LogSendMessageToQueue(UINT32 OperationCode, BOOLEAN IsImmediateMessage, BOOLEAN ShowCurrentSystemTime, const char* Fmt, ...)
+{
+	BOOLEAN Result;
+	va_list ArgList;
+	size_t WrittenSize;
+	UINT32 Index;
+	KIRQL OldIRQL;
+	BOOLEAN IsVmxRootMode;
+	int SprintfResult;
+	char LogMessage[PacketChunkSize];
+	char TempMessage[PacketChunkSize];
+	char TimeBuffer[20] = { 0 };
+
+	// Set Vmx State
+	IsVmxRootMode = GuestState[KeGetCurrentProcessorNumber()].IsOnVmxRootMode;
+
+	if (ShowCurrentSystemTime)
+	{
+		// It's actually not necessary to use -1 but because user-mode code might assume a null-terminated buffer so
+		// it's better to use - 1
+		va_start(ArgList, Fmt);
+		// We won't use this because we can't use in any IRQL
+		/*Status = RtlStringCchVPrintfA(TempMessage, PacketChunkSize - 1, Fmt, ArgList);*/
+		SprintfResult = vsprintf_s(TempMessage, PacketChunkSize - 1, Fmt, ArgList);
+		va_end(ArgList);
+
+		// Check if the buffer passed the limit
+		if (SprintfResult == -1)
+		{
+			// Probably the buffer is large that we can't store it
+			return FALSE;
+		}
+
+		// Fill the above with timer
+		TIME_FIELDS TimeFields;
+		LARGE_INTEGER SystemTime, LocalTime;
+		KeQuerySystemTime(&SystemTime);
+		ExSystemTimeToLocalTime(&SystemTime, &LocalTime);
+		RtlTimeToTimeFields(&LocalTime, &TimeFields);
+
+		// We won't use this because we can't use in any IRQL
+		/*Status = RtlStringCchPrintfA(TimeBuffer, RTL_NUMBER_OF(TimeBuffer),
+			"%02hd:%02hd:%02hd.%03hd", TimeFields.Hour,
+			TimeFields.Minute, TimeFields.Second,
+			TimeFields.Milliseconds);
+
+		// Append time with previous message
+		Status = RtlStringCchPrintfA(LogMessage, PacketChunkSize - 1, "(%s)\t %s", TimeBuffer, TempMessage);*/
+
+		// this function probably run without error, so there is no need to check the return value
+		sprintf_s(TimeBuffer, RTL_NUMBER_OF(TimeBuffer), "%02hd:%02hd:%02hd.%03hd", TimeFields.Hour,
+			TimeFields.Minute, TimeFields.Second,
+			TimeFields.Milliseconds);
+
+		// Append time with previous message
+		SprintfResult = sprintf_s(LogMessage, PacketChunkSize - 1, "(%s - core : %d - vmx-root? %s)\t %s", TimeBuffer, KeGetCurrentProcessorNumberEx(0), IsVmxRootMode ? "yes" : "no", TempMessage);
+
+		// Check if the buffer passed the limit
+		if (SprintfResult == -1)
+		{
+			// Probably the buffer is large that we can't store it
+			return FALSE;
+		}
+
+
+	}
+	else
+	{
+		// It's actually not necessary to use -1 but because user-mode code might assume a null-terminated buffer so
+		// it's better to use - 1
+		va_start(ArgList, Fmt);
+		// We won't use this because we can't use in any IRQL
+		/* Status = RtlStringCchVPrintfA(LogMessage, PacketChunkSize - 1, Fmt, ArgList); */
+		SprintfResult = vsprintf_s(LogMessage, PacketChunkSize - 1, Fmt, ArgList);
+		va_end(ArgList);
+
+		// Check if the buffer passed the limit
+		if (SprintfResult == -1)
+		{
+			// Probably the buffer is large that we can't store it
+			return FALSE;
+		}
+
+	}
+	// Use std function because they can be run in any IRQL
+	// RtlStringCchLengthA(LogMessage, PacketChunkSize - 1, &WrittenSize);
+	WrittenSize = strnlen_s(LogMessage, PacketChunkSize - 1);
+
+	if (LogMessage[0] == '\0') {
+
+		// nothing to write
+		DbgBreakPoint();
+		return FALSE;
+	}
+
+	if (IsImmediateMessage)
+	{
+		return LogSendBuffer(OperationCode, LogMessage, WrittenSize);
+	}
+	else
+	{
+		// Check if we're in Vmx-root, if it is then we use our customized HIGH_IRQL Spinlock, if not we use the windows spinlock
+		if (IsVmxRootMode)
+		{
+			// Set the index
+			Index = 1;
+			SpinlockLock(&VmxRootLoggingLockForNonImmBuffers);
+		}
+		else
+		{
+			// Set the index
+			Index = 0;
+			// Acquire the lock 
+			KeAcquireSpinLock(&MessageBufferInformation[Index].BufferLockForNonImmMessage, &OldIRQL);
+		}
+		//Set the result to True
+		Result = TRUE;
+
+		// If log message WrittenSize is above the buffer then we have to send the previous buffer
+		if ((MessageBufferInformation[Index].CurrentLengthOfNonImmBuffer + WrittenSize) > PacketChunkSize - 1 && MessageBufferInformation[Index].CurrentLengthOfNonImmBuffer != 0)
+		{
+
+			// Send the previous buffer (non-immediate message)
+			Result = LogSendBuffer(OPERATION_LOG_NON_IMMEDIATE_MESSAGE,
+				MessageBufferInformation[Index].BufferForMultipleNonImmediateMessage,
+				MessageBufferInformation[Index].CurrentLengthOfNonImmBuffer);
+
+			// Free the immediate buffer
+			MessageBufferInformation[Index].CurrentLengthOfNonImmBuffer = 0;
+			RtlZeroMemory(MessageBufferInformation[Index].BufferForMultipleNonImmediateMessage, PacketChunkSize);
+		}
+
+		// We have to save the message
+		RtlCopyBytes(MessageBufferInformation[Index].BufferForMultipleNonImmediateMessage +
+			MessageBufferInformation[Index].CurrentLengthOfNonImmBuffer, LogMessage, WrittenSize);
+
+		// add the length 
+		MessageBufferInformation[Index].CurrentLengthOfNonImmBuffer += WrittenSize;
+
+
+		// Check if we're in Vmx-root, if it is then we use our customized HIGH_IRQL Spinlock, if not we use the windows spinlock
+		if (IsVmxRootMode)
+		{
+			SpinlockUnlock(&VmxRootLoggingLockForNonImmBuffers);
+		}
+		else
+		{
+			// Release the lock
+			KeReleaseSpinLock(&MessageBufferInformation[Index].BufferLockForNonImmMessage, OldIRQL);
+		}
+
+		return Result;
+	}
+}
+

Receiving buffers and messages in user-mode

Receiving buffers from the user-mode is done by using an IOCTL. First, we create another thread in our user-mode application. This thread is responsible for bringing the kernel-mode buffers to the user-mode and then operate based on Operation Number.

1
+2
+3
+4
+
		HANDLE Thread = CreateThread(NULL, 0, ThreadFunc, Handle, 0, NULL);
+		if (Thread) {
+			printf("[*] Thread Created successfully !!!");
+		}
+

This thread executes the following function. We use IRP Pending for transferring data from kernel-mode to user-mode. IRP Pending is primarily used for transferring a packet. For example, you send an IRP packet to the kernel, and kernel marks this packet as Pending. Whenever the user-mode buffer is available to send to the user-mode, the kernel completes the IRP request, and the IOCTL function returns to the user-mode and continues the execution.

It’s somehow like when you use Wait for an object. We can also use events in Windows and whenever the buffer is available the event is triggered but IRP Pending is better as it designed for the purpose of sending messages to user-mode.

What we have to do is allocating a buffer for kernel-mode code and using DeviceIoControl to request the packet. When the packet from the kernel received, we process the packet and switch through the Operation Number.

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
+
void ReadIrpBasedBuffer(HANDLE  Device) {
+
+	BOOL    Status;
+	ULONG   ReturnedLength;
+	REGISTER_EVENT RegisterEvent;
+	UINT32 OperationCode;
+
+	printf(" =============================== Kernel-Mode Logs (Driver) ===============================\n");
+	RegisterEvent.hEvent = NULL;
+	RegisterEvent.Type = IRP_BASED;
+	char OutputBuffer[UsermodeBufferSize + 100] = { 0 };
+
+	try
+	{
+
+		while (TRUE) {
+
+			ZeroMemory(OutputBuffer, UsermodeBufferSize);
+
+			Sleep(200);							// we're not trying to eat all of the CPU ;)
+
+			Status = DeviceIoControl(
+				Device,							// Handle to device
+				IOCTL_REGISTER_EVENT,			// IO Control code
+				&RegisterEvent,					// Input Buffer to driver.
+				SIZEOF_REGISTER_EVENT * 2,		// Length of input buffer in bytes. (x 2 is bcuz as the driver is x64 and has 64 bit values)
+				OutputBuffer,					// Output Buffer from driver.
+				sizeof(OutputBuffer),			// Length of output buffer in bytes.
+				&ReturnedLength,				// Bytes placed in buffer.
+				NULL							// synchronous call
+			);
+
+			if (!Status) {
+				printf("Ioctl failed with code %d\n", GetLastError());
+				break;
+			}
+			printf("\n========================= Kernel Mode (Buffer) =========================\n");
+
+			OperationCode = 0;
+			memcpy(&OperationCode, OutputBuffer, sizeof(UINT32));
+
+			printf("Returned Length : 0x%x \n", ReturnedLength);
+			printf("Operation Code : 0x%x \n", OperationCode);
+
+			switch (OperationCode)
+			{
+			case OPERATION_LOG_NON_IMMEDIATE_MESSAGE:
+				printf("A buffer of messages (OPERATION_LOG_NON_IMMEDIATE_MESSAGE) :\n");
+				printf("%s", OutputBuffer + sizeof(UINT32));
+				break;
+			case OPERATION_LOG_INFO_MESSAGE:
+				printf("Information log (OPERATION_LOG_INFO_MESSAGE) :\n");
+				printf("%s", OutputBuffer + sizeof(UINT32));
+				break;
+			case OPERATION_LOG_ERROR_MESSAGE:
+				printf("Error log (OPERATION_LOG_ERROR_MESSAGE) :\n");
+				printf("%s", OutputBuffer + sizeof(UINT32));
+				break;
+			case OPERATION_LOG_WARNING_MESSAGE:
+				printf("Warning log (OPERATION_LOG_WARNING_MESSAGE) :\n");
+				printf("%s", OutputBuffer + sizeof(UINT32));
+				break;
+
+			default:
+				break;
+			}
+
+
+			printf("\n========================================================================\n");
+		}
+	}
+	catch (const std::exception&)
+	{
+		printf("\n Exception !\n");
+	}
+}
+

IOCTL and managing user-mode requests

When the IOCTL arrived on the kernel side, DrvDispatchIoControl from major functions is called. This function returns a pointer to the caller’s I/O stack location in the specified IRP.

From the IRP Stack we can read the IOCTL code and buffers address, this time we perform necessary checks and pass the arguments to LogRegisterIrpBasedNotification.

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
+
+/* Driver IOCTL Dispatcher*/
+NTSTATUS DrvDispatchIoControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
+{
+	PIO_STACK_LOCATION  IrpStack;
+	PREGISTER_EVENT RegisterEvent;
+	NTSTATUS    Status;
+
+	IrpStack = IoGetCurrentIrpStackLocation(Irp);
+
+	switch (IrpStack->Parameters.DeviceIoControl.IoControlCode)
+	{
+	case IOCTL_REGISTER_EVENT:
+
+		// First validate the parameters.
+		if (IrpStack->Parameters.DeviceIoControl.InputBufferLength < SIZEOF_REGISTER_EVENT || Irp->AssociatedIrp.SystemBuffer == NULL) {
+			Status = STATUS_INVALID_PARAMETER;
+			DbgBreakPoint();
+			break;
+		}
+		
+		RegisterEvent = (PREGISTER_EVENT)Irp->AssociatedIrp.SystemBuffer;
+
+		switch (RegisterEvent->Type) {
+		case IRP_BASED:
+			Status = LogRegisterIrpBasedNotification(DeviceObject, Irp);
+			break;
+		case EVENT_BASED:
+			Status = LogRegisterEventBasedNotification(DeviceObject, Irp);
+			break;
+		default:
+			ASSERTMSG("\tUnknow notification type from user-mode\n", FALSE);
+			Status = STATUS_INVALID_PARAMETER;
+			break;
+		}
+		break;
+
+	default:
+		ASSERT(FALSE);  // should never hit this
+		Status = STATUS_NOT_IMPLEMENTED;
+		break;
+	}
+
+	if (Status != STATUS_PENDING) {
+		Irp->IoStatus.Status = Status;
+		Irp->IoStatus.Information = 0;
+		IoCompleteRequest(Irp, IO_NO_INCREMENT);
+	}
+
+	return Status;
+}
+

To register an IRP notification, first, we check whether any other thread is pending by checking GlobalNotifyRecord if there is any thread we complete the IRP and return to the user-mode because in our design we ignore multiple threads that request the buffers means that only one thread can read the kernel-mode buffer.

Second, we initialize a custom structure that describes the state. The following structure is responsible for saving Type, DPC Object, and target buffer.

1
+2
+3
+4
+5
+6
+7
+8
+9
+
typedef struct _NOTIFY_RECORD {
+	NOTIFY_TYPE     Type;
+	union {
+		PKEVENT     Event;
+		PIRP        PendingIrp;
+	} Message;
+	KDPC            Dpc;
+	BOOLEAN			CheckVmxRootMessagePool; // Set so that notify callback can understand where to check (Vmx root or Vmx non-root)
+} NOTIFY_RECORD, * PNOTIFY_RECORD;
+

In order to fill this structure, we initialize a DPC object by calling KeInitializeDpc, this function gets the function callback that should be called later (LogNotifyUsermodeCallback) and the parameter(s) to this function (NotifyRecord).

We first check the vmx non-root pools to see if anything new is available. Otherwise, we check the vmx-root mode buffer. This precedence is because vmx non-root buffers are more important. After all, we spent must of the time in VMX Root-mode, so we might see thousands of messages from vmx-root while we have fewer messages from vmx non-root. If we check the vmx root message buffer first, then we might lose some messages from vmx non-root or never find a time to process them.

If any new message is available then we directly add a DPC to the queue (KeInsertQueueDpc).

If there isn’t any new message available, then we simply save our Notify Record for future use, and also we mark IRP to pending state using IoMarkIrpPending and return STATUS_PENDING.

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
+
/* Register a new IRP Pending thread which listens for new buffers */
+NTSTATUS LogRegisterIrpBasedNotification(PDEVICE_OBJECT DeviceObject, PIRP Irp)
+{
+	PNOTIFY_RECORD NotifyRecord;
+	PIO_STACK_LOCATION IrpStack;
+	KIRQL   OOldIrql;
+	PREGISTER_EVENT RegisterEvent;
+
+	// check if current core has another thread with pending IRP, if no then put the current thread to pending
+	// otherwise return and complete thread with STATUS_SUCCESS as there is another thread waiting for message
+
+	if (GlobalNotifyRecord == NULL)
+	{
+		IrpStack = IoGetCurrentIrpStackLocation(Irp);
+		RegisterEvent = (PREGISTER_EVENT)Irp->AssociatedIrp.SystemBuffer;
+
+		// Allocate a record and save all the event context.
+		NotifyRecord = ExAllocatePoolWithQuotaTag(NonPagedPool, sizeof(NOTIFY_RECORD), POOLTAG);
+
+		if (NULL == NotifyRecord) {
+			return  STATUS_INSUFFICIENT_RESOURCES;
+		}
+
+		NotifyRecord->Type = IRP_BASED;
+		NotifyRecord->Message.PendingIrp = Irp;
+
+		KeInitializeDpc(&NotifyRecord->Dpc, // Dpc
+			LogNotifyUsermodeCallback,     // DeferredRoutine
+			NotifyRecord        // DeferredContext
+		);
+
+		IoMarkIrpPending(Irp);
+
+		// check for new message (for both Vmx-root mode or Vmx non root-mode)
+		if (LogCheckForNewMessage(FALSE))
+		{
+			// check vmx root
+			NotifyRecord->CheckVmxRootMessagePool = FALSE;
+
+			// Insert dpc to queue
+			KeInsertQueueDpc(&NotifyRecord->Dpc, NotifyRecord, NULL);
+		}
+		else if (LogCheckForNewMessage(TRUE))
+		{
+			// check vmx non-root
+			NotifyRecord->CheckVmxRootMessagePool = TRUE;
+
+			// Insert dpc to queue
+			KeInsertQueueDpc(&NotifyRecord->Dpc, NotifyRecord, NULL);
+		}
+		else
+		{
+			// Set the notify routine to the global structure
+			GlobalNotifyRecord = NotifyRecord;
+		}
+
+		// We will return pending as we have marked the IRP pending.
+		return STATUS_PENDING;
+	}
+	else
+	{
+		return STATUS_SUCCESS;
+	}
+}
+

Usermode notify callback

As you see in the above codes, we add DPCs to queue in two functions (LogRegisterIrpBasedNotification and LogSendBuffer). This way, we won’t miss anything, and everything is processed as a message is generated. For example, if there is any thread waiting for the message then LogSendBuffer notifies it about the new message, if there isn’t any thread waiting for the message then LogSendBuffer can’t do anything, as long as a new thread comes to the kernel then it checks for the new message. Think about it one more time. It’s beautiful.

Now it’s time to read the packets from kernel pools and send them to the user-mode.

When LogNotifyUsermodeCallback is called then we sure that we’re in DISPATCH_LEVEL and vmx non-root mode.

In this function, we check if the parameters sent to the kernel are valid or not. It’s because the user-mode provides them. For example, we check the IRP stack’s Parameters. DeviceIoControl. InputBufferLength and Parameters. DeviceIoControl. OutputBufferLength to make sure they are not null or check whether the SystemBuffer is null or not.

Then we call LogReadBuffer with user-mode buffers, so this function will fill the user-mode buffer and adds the Operation Number in a suitable place. Also, Irp->IoStatus.Information provides the buffer length to the user-mode.

The last step here is to complete the IRP, so I/O Manager sends the results to the user-mode, and the thread can continue to its normal life.

The reason why we access the user-mode buffer in all processes (because DPCs might run on the random user-mode process) and why we use DPCs and don’t use other things like APCs is discussed in the Discussion section.

The following code demonstrates what we talked about it above.

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
+
/* Complete the IRP in IRP Pending state and fill the usermode buffers with pool data */
+VOID LogNotifyUsermodeCallback(PKDPC Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
+{
+
+	PNOTIFY_RECORD NotifyRecord;
+	PIRP Irp;
+	UINT32 Length;
+
+	UNREFERENCED_PARAMETER(Dpc);
+	UNREFERENCED_PARAMETER(SystemArgument1);
+	UNREFERENCED_PARAMETER(SystemArgument2);
+
+	NotifyRecord = DeferredContext;
+
+	ASSERT(NotifyRecord != NULL); // can't be NULL
+	_Analysis_assume_(NotifyRecord != NULL);
+
+	switch (NotifyRecord->Type)
+	{
+
+	case IRP_BASED:
+		Irp = NotifyRecord->Message.PendingIrp;
+
+		if (Irp != NULL) {
+
+			PCHAR OutBuff; // pointer to output buffer
+			ULONG InBuffLength; // Input buffer length
+			ULONG OutBuffLength; // Output buffer length
+			PIO_STACK_LOCATION IrpSp;
+
+			// Make suree that concurrent calls to notify function never occurs
+			if (!(Irp->CurrentLocation <= Irp->StackCount + 1))
+			{
+				DbgBreakPoint();
+				return;
+			}
+
+			IrpSp = IoGetCurrentIrpStackLocation(Irp);
+			InBuffLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+			OutBuffLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+			if (!InBuffLength || !OutBuffLength)
+			{
+				Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+				IoCompleteRequest(Irp, IO_NO_INCREMENT);
+				break;
+			}
+
+			// Check again that SystemBuffer is not null
+			if (!Irp->AssociatedIrp.SystemBuffer)
+			{
+				// Buffer is invalid
+				return;
+			}
+
+			OutBuff = Irp->AssociatedIrp.SystemBuffer;
+			Length = 0;
+
+			// Read Buffer might be empty (nothing to send)
+			if (!LogReadBuffer(NotifyRecord->CheckVmxRootMessagePool, OutBuff, &Length))
+			{
+				// we have to return here as there is nothing to send here
+				return;
+			}
+
+			Irp->IoStatus.Information = Length;
+
+
+			Irp->IoStatus.Status = STATUS_SUCCESS;
+			IoCompleteRequest(Irp, IO_NO_INCREMENT);
+		}
+		break;
+
+	case EVENT_BASED:
+
+		// Signal the Event created in user-mode.
+		KeSetEvent(NotifyRecord->Message.Event, 0, FALSE);
+
+		// Dereference the object as we are done with it.
+		ObDereferenceObject(NotifyRecord->Message.Event);
+
+		break;
+
+	default:
+		ASSERT(FALSE);
+		break;
+	}
+
+	if (NotifyRecord != NULL) {
+		ExFreePoolWithTag(NotifyRecord, POOLTAG);
+	}
+}
+

Uninitialization Phase

Nothing special, we just de-allocate the previously allocated buffers. Keep in mind that we should initialize the message tracer at the very first function of our driver so we can use it and, of course, uninitialize it at the end when we don’t have any message anymore.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+
/* Uninitialize the buffer relating to log message tracing */
+VOID LogUnInitialize()
+{
+
+	// de-allocate buffer for messages and initialize the core buffer information (for vmx-root core)
+	for (int i = 0; i < 2; i++)
+	{
+		// Free each buffers
+		ExFreePoolWithTag(MessageBufferInformation[i].BufferStartAddress, POOLTAG);
+		ExFreePoolWithTag(MessageBufferInformation[i].BufferForMultipleNonImmediateMessage, POOLTAG);
+	}
+
+	// de-allocate buffers for trace message and data messages
+	ExFreePoolWithTag(MessageBufferInformation, POOLTAG);
+}
+

Aniiiimmmmeee :)

WPP Tracing

WPP Tracing is another mechanism provided by Windows, which can be used to trace messages from both vmx non-root and vmx root-mode and in any IRQL. It is primarily intended for debugging code during development, and it’s capable of publishing events that can be consumed by applications in structured ETW events.

Logging messages with WPP software tracing is similar to using Windows event logging services. The driver logs a message ID and unformatted binary data in a log file. Subsequently, a postprocessor converts the information in the log file to a human-readable form.

In order to use WPP Tracing, first, we should configure our driver to use WPP Tracing as the message tracing by setting UseWPPTracing to TRUE. By default it’s FALSE.

1
+2
+
// Use WPP Tracing instead of all logging functions
+#define UseWPPTracing		TRUE
+

Then we go to our project’s properties and set Run Wpp Tracing to Yes and also add a custom function for sending messages by setting Function To Generate Trace Messages to HypervisorTraceLevelMessage (LEVEL,FLAGS,MSG,…).

WPP Tracing Configuration

Then we need to generate a unique GUID for our driver by using Visual Studio’s Tools -> Create GUID and generate one and put it into the following format.

1
+2
+3
+4
+5
+6
+7
+8
+
#define WPP_CONTROL_GUIDS                                              \
+    WPP_DEFINE_CONTROL_GUID(                                           \
+        HypervisorFromScratchLogger, (2AE39766,AE4B,46AB,AFC4,002DB8109721), \
+        WPP_DEFINE_BIT(HVFS_LOG)             /* bit  0 = 0x00000001 */ \
+        WPP_DEFINE_BIT(HVFS_LOG_INFO)        /* bit  1 = 0x00000002 */ \
+        WPP_DEFINE_BIT(HVFS_LOG_WARNING)     /* bit  2 = 0x00000004 */ \
+        WPP_DEFINE_BIT(HVFS_LOG_ERROR)       /* bit  3 = 0x00000008 */ \
+        )    
+

WPP_DEFINE_BIT creates some specific events for our messages that can be used in the future for masking specific events.

After all the above code, we initialize the WPP Tracing by adding the following code at the very first line of the code, e.g., DriverEntry.

1
+2
+
	// Initialize WPP Tracing
+	WPP_INIT_TRACING(DriverObject, RegistryPath);
+

At last we clean up and set WPP Tracing to off by using the following code to Driver Unload function.

1
+2
+
	// Stop the tracing
+	WPP_CLEANUP(DriverObject);
+

For making things easy, I add the following codes to our previous message tracing code, which means that instead of sending the buffers into our custom message tracing buffer, we’ll send it to WPP Tracing buffer.

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
+
+	if (OperationCode == OPERATION_LOG_INFO_MESSAGE)
+	{
+		HypervisorTraceLevelMessage(
+			TRACE_LEVEL_INFORMATION,  // ETW Level defined in evntrace.h
+			HVFS_LOG_INFO,
+			"%s",// Flag defined in WPP_CONTROL_GUIDS
+			LogMessage);
+	}
+	else if (OperationCode == OPERATION_LOG_WARNING_MESSAGE)
+	{
+		HypervisorTraceLevelMessage(
+			TRACE_LEVEL_WARNING,  // ETW Level defined in evntrace.h
+			HVFS_LOG_WARNING,
+			"%s",// Flag defined in WPP_CONTROL_GUIDS
+			LogMessage);
+	}
+	else if (OperationCode == OPERATION_LOG_ERROR_MESSAGE)
+	{
+		HypervisorTraceLevelMessage(
+			TRACE_LEVEL_ERROR,  // ETW Level defined in evntrace.h
+			HVFS_LOG_ERROR,
+			"%s",// Flag defined in WPP_CONTROL_GUIDS
+			LogMessage);
+	}
+	else
+	{
+		HypervisorTraceLevelMessage(
+			TRACE_LEVEL_NONE,  // ETW Level defined in evntrace.h
+			HVFS_LOG,
+			"%s",// Flag defined in WPP_CONTROL_GUIDS
+			LogMessage);
+	}
+

Also, we have to .tmh files. These files are auto-generated by the WPP framework, which contains the required code for trace messages. TMH file name should be the same as the C file, for example, if we are adding the trace message in “Driver.c” then we are supposed to include “Driver.tmh”. We used WPP Tracing APIs in two files, first Driver.c and Logging.c, so we have to include Driver.tmh and Logging.tmh and no need for these files in other project files as long as we gathered everything in one file.

The WPP Tracing is complete! In order to see the messages in user-mode, we have to use another application, e.g traceview.

Personally, I prefer to use my custom message tracing as WPP Tracing needs to some other application to parse the .pdb file or other files to show the messages, and I didn’t find any good example of parsing messages in an application without using another app.

You can see the results of WPP Tracing later in Let’s Test it! section.

Supporting to Hyper-V

As I told you in the previous parts, testing and building hypervisor for Hyper-V needs extra consideration and adding a few more lines of code to support Hyper-V nested virtualization.

At the time of writing this part, Hyper-V and VMware Workstation are incompatible with each other, which means that if you run Hyper-V you can’t run VMware and a message like this will appear.

VMware Workstation and Hyper-V are not compatible. Remove the Hyper-V role from the system before running VMware Workstation.

The same is true for VMware, if you run VMware you can’t run Hyper-V and you need to execute a command then restart your computer to use another VMM.

In order to use Hyper-V, you should run the following command (as administrator) and then restart your computer.

1
+
bcdedit /set hypervisorlaunchtype auto 
+

And if you want to run VMware, you can run the following command (as administrator) and restart your computer.

1
+
bcdedit /set hypervisorlaunchtype off
+

Enable Nested Virtualization

In part 1, there is a section that describes how to enable VMware’s nested virtualization and test your driver. For Hyper-V we have an exact same scenario, first, turn off the target VM then enable nested virtualization for the target virtual machine by running the following command on Powershell:

Note that instead of PutYourVmNameHere, put the name of your virtual machine that you want to enable nested virtualization for it.

1
+
Set-VMProcessor -VMName PutYourVmNameHere -ExposeVirtualizationExtensions $true
+

And if you need to disable it, you can run:

1
+
Set-VMProcessor -VMName PutYourVmNameHere -ExposeVirtualizationExtensions $false
+

Now you need to attach your Hyper-V machine to a windbg debugger. There are many ways to do it. You can read here and here (I prefer using kdnet.exe).

Now we have the testing environment, it’s time to modify our hypervisor so we can support Hyper-V.

Hyper-V’s visible behavior in nested virtualization

Hyper-V has some visible behavior for our hypervisor, which means that you should manage some of them that relate to us and give some of them to the Hyper-V as a top-level hypervisor to manage them, you’re confused? Let me explain it one more time.

In a nested virtualization environment, you’re not directly getting the vm-exits and all other hypervisor events, instead it’s the top-level hypervisor that gets the vm-exit (in our case Hyper-V is the top-level). Top-level hypervisor calls the vm-exit handler of lower-level hypervisors (our hypervisor is a low-level hypervisor in this case.) now the lower level hypervisor manages the vm-exit (for example it injects an event (interrupt) to be delivered to the guest) after vm-exit finishes it executes VMRESUME, but this instruction won’t directly go to the guest vmx non-root. Instead, it goes to the vm-exit handler of the top-level hypervisor, and now it’s the top-level hypervisor that performs the tasks (In our example, insert event to the guest).

So, even our hypervisor is not the first hypervisor that gets the event, but our hypervisor is the first to manage them.

On the other hand, Windows kernel is highly integrated to Hyper-V, which means that it uses lots of Hypercalls (Vmcalls) and MSRs to contact with Hyper-V and if the Windows kernel doesn’t get the valid response from Hyper-V then it crashes or halts.

As the first hypervisor to manage the vm-exits, we have to inspect vm-exit details to see if the vm-exit relates to us our refers to Hyper-V. In other words, it’s a general vm-exit, or it’s because Windows wants to talk with Hyper-V.

OK, let see what we should manage and what we should not.

Hyper-V Hypervisor Top-Level Functional Specification (TLFS)

The Hyper-V Hypervisor Top-Level Functional Specification (TLFS) describes the hypervisor’s externally visible behavior to other operating system components. This specification is meant to be useful for guest operating system developers.

If you want to research Hyper-V, you have to read the documentation about Hyper-V’s TLFS here, but we just want to support Hyper-V. Hence, there is documentation (Requirements for Implementing the Microsoft Hypervisor Interface) that describes the things we should do in order to support Hyper-V. Of course, we’re not going to implement all of them to make our hypervisor work on Hyper-V.

Out of Range MSRs

In part 6, I described MSR Bitmaps, if you remember MSR bitmap support MSR index (RCX) between 0x00000000 to 0x00001FFF and 0xC0000000 to 0xC0001FFF. Windows uses other MSRs from 0x40000000 to 0x400000F0 for requesting something or reporting something to vmx-root.

You might ask why they don’t use VMCALLs. Of course, they can use VMCALL, but most hypervisors do this. It’s cheaper and predates VMCALLs, and also this range is specifically designed to be used by hypervisors.

The reason why it’s cheaper is the same discussion about why use int 2e and not sysenter as the cost of sending data over vmcall and allowing it from ring 0 or ring 3 and deciding things (rdmsr doesn’t need that ring check) and sending data back is greater than a simple MSR interface and can work with legacy compilers and systems too.

You can find the definitions of these MSRs here.

All in all, I modified our previous MSR handler (both MSR Read - RDMSR and MSR Write - WRMSR to support MSRs between 0x40000000 to 0x400000F0). All we have to do is execute RDMSR or WRMSR in vmx-root mode.

You might ask, is it ok to run WRMSR or RDMSR with hardware invalid MSRs?

The answer is no! but the reason why we execute it is because we’re are in a nested virtualization environment and it’s not a real vmx-root, physically we’re in vmx non-root mode if that makes sense.

In other words, VMware or Hyper-V or any nested virtualization environment calls our vm-exit handler in vmx non-root and pretend that it’s in vmx-root mode, so executing WRMSR or RDMSR causes a real vm-exit to Hyper-V, and that’s how they can handle the actual vm-exit.

For example RDMSR handles like this :

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
+
/* Handles in the cases when RDMSR causes a Vmexit*/
+VOID HvHandleMsrRead(PGUEST_REGS GuestRegs)
+{
+
+	MSR msr = { 0 };
+
+
+	// RDMSR. The RDMSR instruction causes a VM exit if any of the following are true:
+	// 
+	// The "use MSR bitmaps" VM-execution control is 0.
+	// The value of ECX is not in the ranges 00000000H - 00001FFFH and C0000000H - C0001FFFH
+	// The value of ECX is in the range 00000000H - 00001FFFH and bit n in read bitmap for low MSRs is 1,
+	//   where n is the value of ECX.
+	// The value of ECX is in the range C0000000H - C0001FFFH and bit n in read bitmap for high MSRs is 1,
+	//   where n is the value of ECX & 00001FFFH.
+
+	/*
+	   Execute WRMSR or RDMSR on behalf of the guest. Important that this
+	   can cause bug check when the guest tries to access unimplemented MSR
+	   even within the SEH block* because the below WRMSR or RDMSR raises
+	   #GP and are not protected by the SEH block (or cannot be protected
+	   either as this code run outside the thread stack region Windows
+	   requires to proceed SEH). Hypervisors typically handle this by noop-ing
+	   WRMSR and returning zero for RDMSR with non-architecturally defined
+	   MSRs. Alternatively, one can probe which MSRs should cause #GP prior
+	   to installation of a hypervisor and the hypervisor can emulate the
+	   results.
+	   */
+
+	   // Check for sanity of MSR if they're valid or they're for reserved range for WRMSR and RDMSR
+	if ((GuestRegs->rcx <= 0x00001FFF) || ((0xC0000000 <= GuestRegs->rcx) && (GuestRegs->rcx <= 0xC0001FFF))
+		|| (GuestRegs->rcx >= RESERVED_MSR_RANGE_LOW && (GuestRegs->rcx <= RESERVED_MSR_RANGE_HI)))
+	{
+		msr.Content = __readmsr(GuestRegs->rcx);
+	}
+
+	GuestRegs->rax = msr.Low;
+	GuestRegs->rdx = msr.High;
+}
+

Same checks apply to WRMSR too.

Hyper-V Hypercalls (VMCALLs)

VMCALL is exactly like RDMSR and WRMSR, even though running VMCALL on vmx-root mode has a known behavior (invokes an SMM monitor). Still, in our case, in a nested virtualization environment, it causes a vm-exit to Hyper-V so Hyper-V can manage the hypercall.

Hyper-V has the following convention for its VMCALLs (hypercall).

Hyper-V hypercall convention

As we want to use our hypervisor VMCALLs, a quick and dirty fix for this problem is somehow show the vm-exit handler that our hypervisor routines should manage this VMCALL; thus we put some random hex values to r10, r11, r12 (as these registers are not used in fastcall calling convention, you can choose other registers too) thus we can check for these registers on the vm-exit handler to make sure that this VMCALL relates to our hypervisor.

As some of the registers should not be changed due to the Windows x64 fastcall calling convention, we save them to restore them later.

Generally, The registers RAX, RCX, RDX, R8, R9, R10, R11 are considered volatile (caller-saved) and registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 are considered nonvolatile (callee-saved).

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
     ; We change r10 to HVFS Hex ASCII and r11 to VMCALL Hex ASCII and r12 to NOHYPERV Hex ASCII so we can make sure that the calling Vmcall comes
+    ; from our hypervisor and we're resposible for managing it, otherwise it has to be managed by Hyper-V
+    push    r10
+    push    r11
+    push    r12
+    mov     r10, 48564653H          ; [HVFS]
+    mov     r11, 564d43414c4cH      ; [VMCALL]
+    mov     r12, 4e4f485950455256H   ; [NOHYPERV]
+    vmcall                          ; VmxVmcallHandler(UINT64 VmcallNumber, UINT64 OptionalParam1, UINT64 OptionalParam2, UINT64 OptionalParam3)
+    pop     r12
+    pop     r11
+    pop     r10
+    ret                             ; Return type is NTSTATUS and it's on RAX from the previous function, no need to change anything
+

For Hyper-V VMCALLs we need to adjust RCX, RDX, R8 as demonstrated in the above picture.

1
+2
+3
+4
+5
+
AsmHypervVmcall PROC
+    vmcall                       ; __fastcall Vmcall(rcx = HypercallInputValue, rdx = InputParamGPA, r8 = OutputParamGPA)
+    ret
+
+AsmHypervVmcall ENDP
+

Finally, in the vm-exit handler, we check for the VMCALL to see if our random values are store in the registers or not. If it’s on those registers, then we call our hypervisor VMCALL handler. Otherwise, we let Hyper-V do whatever it wants to its VMCALLs.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+
	case EXIT_REASON_VMCALL:
+	{
+		// Check if it's our routines that request the VMCALL our it relates to Hyper-V
+		if (GuestRegs->r10 == 0x48564653 && GuestRegs->r11 == 0x564d43414c4c && GuestRegs->r12 == 0x4e4f485950455256)
+		{
+			// Then we have to manage it as it relates to us
+			GuestRegs->rax = VmxVmcallHandler(GuestRegs->rcx, GuestRegs->rdx, GuestRegs->r8, GuestRegs->r9);
+		}
+		else
+		{
+			// Otherwise let the top-level hypervisor to manage it
+			GuestRegs->rax = AsmHypervVmcall(GuestRegs->rcx, GuestRegs->rdx, GuestRegs->r8);
+		}
+		break;
+	}
+

Hyper-V Interface CPUID Leaves

The last step on supporting Hyper-V is managing CPUID leaves, here are some of the CPUID leaves that we have to manage them.

Note that based on the document I mentioned, we have to return non “Hv#1” value. This indicates that our hypervisor does NOT conform to the Microsoft hypervisor interface.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
	else if (RegistersState->rax == CPUID_HV_VENDOR_AND_MAX_FUNCTIONS)
+	{
+
+		// Return a maximum supported hypervisor CPUID leaf range and a vendor
+		// ID signature as required by the spec.
+
+		cpu_info[0] = HYPERV_CPUID_INTERFACE;
+		cpu_info[1] = 'rFvH';  // "[H]yper[v]isor [Fr]o[m] [Scratch] = HvFrmScratch"
+		cpu_info[2] = 'rcSm';
+		cpu_info[3] = 'hcta';
+	}
+	else if (RegistersState->rax == HYPERV_CPUID_INTERFACE)
+	{
+		// Return our interface identifier
+		//cpu_info[0] = 'HVFS'; // [H]yper[V]isor [F]rom [S]cratch 
+
+		// Return non Hv#1 value. This indicate that our hypervisor does NOT
+		// conform to the Microsoft hypervisor interface.
+
+		cpu_info[0] = '0#vH';  // Hv#0
+		cpu_info[1] = cpu_info[2] = cpu_info[3] = 0;
+
+	}
+

By the way, it works without the above modification about CPUID leaves, but it’s better to manage them based on TLFS.

One other thing that I noticed during the development on Hyper-V was the fact that we have vm-exits because the guest executes HLT (Halt) instruction, of course, we don’t want to halt the processor so in the case of EXIT_REASON_HLT we simply ignore it.

Finished! From now you can test your hypervisor on Hyper-V too : )

Fixing Previous Design Issues

In this part, we want to improve our hypervisor and fix some issues from the previous parts regarding problems and misunderstandings.

Fixing the problem with pre-allocated buffers

Our previous buffer pre-allocation has 2 problems,

  • It doesn’t allow us to hook page from VMX Root mode, which means that every pool allocation should start from vmx non-root mode.
  • In the process of allocation, we didn’t acquire spinlock so that the processor might interrupt us. Next time we want to continue our execution, there is no allocation as we allocate pools per core.

To fix them, we need to design a global pool manager. You can see the pool manager code in “PoolManager.c” and “PoolManager.h”. I’m not gonna describe how it works as it’s pretty clear if you see the source code, but I’ll explain the functionality of this pool manager and how you can use its functions.

In this pool manager, instead of allocating core-core specific pre-allocated buffers, we’ll use global pre-allocated buffers with ten pre-allocated buffers ready, each time one of these buffers is used we add a request to pool manager to replace another pool as soon as possible, this way we’ll never run out of pre-allocated pools.

Of course, we might run out of the pre-allocated pool if ten requests arrive at the pool manager, but we don’t need such a request and, of course, between them, pool manager gets a chance to re-allocate new pools.

Here the functions explanation :

1
+
BOOLEAN PoolManagerInitialize();
+

Initializes the Pool Manager and pre-allocate some pools.

1
+
VOID PoolManagerUninitialize();
+

De-allocate all the allocated pools

1
+
BOOLEAN PoolManagerCheckAndPerformAllocation();
+

The above function tries to see whether a new pool request is available, if available, then allocates it. It should be called in PASSIVE_LEVEL (vmx non-root mode) because we want paging allocation, and also, the best place to check for it is on IOCTL handler as we call it frequently and it’s PASSIVE_LEVEL and safe.

1
+
BOOLEAN PoolManagerRequestAllocation(SIZE_T Size, UINT32 Count, POOL_ALLOCATION_INTENTION Intention);
+

If we have requested to allocate a new pool, we can call this function. It stores the requests somewhere in the memory to be allocated when it’s safe (IRQL == PASSIVE_LEVEL).

POOL_ALLOCATION_INTENTION is an enum that describes why we need this pool. It’s used because we might need pools for other purposes with different sizes, so we use our pool manager without any problem.

1
+
UINT64 PoolManagerRequestPool(POOL_ALLOCATION_INTENTION Intention, BOOLEAN RequestNewPool, UINT32 Size);
+

In the vmx-root mode, if we need a safe pool address immediately we call it, it also requests a new pool if we set RequestNewPool to TRUE; thus, next time that it’s safe, the pool will be allocated.

Also, you can look at the code for other explanations.

Avoid Intercepting Accesses to CR3

One of misunderstanding that we have from part 5 until this part was that we intercept CR3 accesses because we set CR3 load-exiting and CR3 store-exiting on the Cpu Based VM Exec Controls.

In general, it’s quite unusual to intercept guest accesses to CR3 when you run it under EPT. It’s a behavior mostly done when you implementing shadow MMU (Because lack of EPT support in CPU) so not intercepting CR3 accesses is the standard behavior for any hypervisor running with EPT enabled.

Intercepting CR3 access is always configurable, we have to clear bits CPU_BASED_CR3_STORE_EXITING, CPU_BASED_CR3_LOAD_EXITING, and CPU_BASED_INVLPG_EXITING in VMCS’s CPU_BASED_VM_EXEC_CONTROL.

But wait, why we should clear them, we never set them !

As noted in previous parts, certain VMX controls are reserved and must be set to a specific value (0 or 1), which is determined by the processor. That’s why we used the function “HvAdjustControls” and pass them an MSR (MSR_IA32_VMX_PROCBASED_CTLS, MSR_IA32_VMX_PINBASED_CTLS, MSR_IA32_VMX_EXIT_CTLS, MSR_IA32_VMX_ENTRY_CTLS) which represents these settings.

Actually, there are 3 types of settings for VMCS controls.

  • Always-flexible. These have never been reserved.
  • Default0. These are (or have been) reserved with a default setting of 0.
  • Default1. They are (or have been) reserved with a default setting of 1.

On newer processors, if Bit 55 (IA32_VMX_BASIC) is read as 1 if any VMX controls that are default1 may be cleared to 0. This bit also reports support for the VMX capability MSRs A32_VMX_TRUE_PINBASED_CTLS, IA32_VMX_TRUE_PROCBASED_CTLS, IA32_VMX_TRUE_EXIT_CTLS, and IA32_VMX_TRUE_ENTRY_CTLS.

So we have to check if our CPU supports this bit, if it supports then we have to use new A32_VMX_TRUE_PINBASED_CTLS, IA32_VMX_TRUE_PROCBASED_CTLS, IA32_VMX_TRUE_EXIT_CTLS, and IA32_VMX_TRUE_ENTRY_CTLS instead of MSR_IA32_VMX_PROCBASED_CTLS, MSR_IA32_VMX_PINBASED_CTLS, MSR_IA32_VMX_EXIT_CTLS, MSR_IA32_VMX_ENTRY_CTLS.

Note that MSR_IA32_VMX_PROCBASED_CTLS2 doesn’t have another version.

For this purpose, first we read the MSR_IA32_VMX_BASIC.

1
+2
+3
+4
+
	IA32_VMX_BASIC_MSR VmxBasicMsr = { 0 };
+
+	// Reading IA32_VMX_BASIC_MSR 
+	VmxBasicMsr.All = __readmsr(MSR_IA32_VMX_BASIC);
+

Then we check whether the 55th bit of the MSR_IA32_VMX_BASIC is set or not. If it’s set, then we use different MSR to our HvAdjustControls.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
	CpuBasedVmExecControls = HvAdjustControls(CPU_BASED_ACTIVATE_MSR_BITMAP	| CPU_BASED_ACTIVATE_SECONDARY_CONTROLS,
+		VmxBasicMsr.Fields.VmxCapabilityHint ? MSR_IA32_VMX_TRUE_PROCBASED_CTLS : MSR_IA32_VMX_PROCBASED_CTLS);
+
+	__vmx_vmwrite(CPU_BASED_VM_EXEC_CONTROL, CpuBasedVmExecControls);
+
+	LogInfo("Cpu Based VM Exec Controls (Based on %s) : 0x%x", 
+		VmxBasicMsr.Fields.VmxCapabilityHint ? "MSR_IA32_VMX_TRUE_PROCBASED_CTLS" : "MSR_IA32_VMX_PROCBASED_CTLS", CpuBasedVmExecControls);
+
+	SecondaryProcBasedVmExecControls = HvAdjustControls(CPU_BASED_CTL2_RDTSCP |
+		CPU_BASED_CTL2_ENABLE_EPT | CPU_BASED_CTL2_ENABLE_INVPCID |
+		CPU_BASED_CTL2_ENABLE_XSAVE_XRSTORS  | CPU_BASED_CTL2_ENABLE_VPID, MSR_IA32_VMX_PROCBASED_CTLS2);
+
+	__vmx_vmwrite(SECONDARY_VM_EXEC_CONTROL, SecondaryProcBasedVmExecControls);
+	LogInfo("Secondary Proc Based VM Exec Controls (MSR_IA32_VMX_PROCBASED_CTLS2) : 0x%x", SecondaryProcBasedVmExecControls);
+	
+	__vmx_vmwrite(PIN_BASED_VM_EXEC_CONTROL, HvAdjustControls(0,
+		VmxBasicMsr.Fields.VmxCapabilityHint ? MSR_IA32_VMX_TRUE_PINBASED_CTLS : MSR_IA32_VMX_PINBASED_CTLS));
+
+	__vmx_vmwrite(VM_EXIT_CONTROLS, HvAdjustControls(VM_EXIT_IA32E_MODE, 
+		VmxBasicMsr.Fields.VmxCapabilityHint ? MSR_IA32_VMX_TRUE_EXIT_CTLS : MSR_IA32_VMX_EXIT_CTLS));
+
+	__vmx_vmwrite(VM_ENTRY_CONTROLS, HvAdjustControls(VM_ENTRY_IA32E_MODE,
+		VmxBasicMsr.Fields.VmxCapabilityHint ? MSR_IA32_VMX_TRUE_ENTRY_CTLS : MSR_IA32_VMX_ENTRY_CTLS));
+

This way, we can gain better performance by disabling unnecessary vm-exits as there are countless CR3 changes for each process in Windows, and also meltdown patch brings twice cr3 changes. We no longer need to intercept them.

Restoring IDTR, GDTR, GS Base and FS Base

One of the things that we didn’t have in the previous parts was that we didn’t restore the IDTR, GDTR, GS Base, and FS Base when we want to turn off the hypervisor. We should reset GDTR/IDTR when you do vmxoff, or PatchGuard will detect them left modified.

In order to restore them, before executing vmxoff in each core, the following function is called and it takes care of everything that should be restored to avoid PatchGuard errors.

It read GUEST_GS_BASE and GUEST_FS_BASE from VMCS and write to restore them with WRMSR and also restore the GUEST_GDTR_BASE, GUEST_GDTR_LIMIT, and GUEST_IDTR_BASE, GUEST_IDTR_LIMIT using lgdt and lidt instructions.

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
+
VOID HvRestoreRegisters()
+{
+	ULONG64 FsBase;
+	ULONG64 GsBase;
+	ULONG64 GdtrBase;
+	ULONG64 GdtrLimit;
+	ULONG64 IdtrBase;
+	ULONG64 IdtrLimit;
+
+	// Restore FS Base 
+	__vmx_vmread(GUEST_FS_BASE, &FsBase);
+	__writemsr(MSR_FS_BASE, FsBase);
+
+	// Restore Gs Base
+	__vmx_vmread(GUEST_GS_BASE, &GsBase);
+	__writemsr(MSR_GS_BASE, GsBase);
+
+	// Restore GDTR
+	__vmx_vmread(GUEST_GDTR_BASE, &GdtrBase);
+	__vmx_vmread(GUEST_GDTR_LIMIT, &GdtrLimit);
+
+	AsmReloadGdtr(GdtrBase, GdtrLimit);
+
+	// Restore IDTR
+	__vmx_vmread(GUEST_IDTR_BASE, &IdtrBase);
+	__vmx_vmread(GUEST_IDTR_LIMIT, &IdtrLimit);
+
+	AsmReloadIdtr(IdtrBase, IdtrLimit);
+}
+

This is the assembly part to restore IDTR and GDTR.

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
+
;------------------------------------------------------------------------
+
+; AsmReloadGdtr (PVOID GdtBase (rcx), ULONG GdtLimit (rdx) );
+
+AsmReloadGdtr PROC
+	push	rcx
+	shl		rdx, 48
+	push	rdx
+	lgdt	fword ptr [rsp+6]	; do not try to modify stack selector with this ;)
+	pop		rax
+	pop		rax
+	ret
+AsmReloadGdtr ENDP
+
+;------------------------------------------------------------------------
+
+; AsmReloadIdtr (PVOID IdtBase (rcx), ULONG IdtLimit (rdx) );
+
+AsmReloadIdtr PROC
+	push	rcx
+	shl		rdx, 48
+	push	rdx
+	lidt	fword ptr [rsp+6]
+	pop		rax
+	pop		rax
+	ret
+AsmReloadIdtr ENDP
+
+;------------------------------------------------------------------------
+

Also, it’s better to unset vmx-enable bit of cr4 after executing vmxoff on each core separately.

1
+2
+
	// Now that VMX is OFF, we have to unset vmx-enable bit on cr4
+	__writecr4(__readcr4() & (~X86_CR4_VMXE));
+

Let’s Test it!

The code for our hypervisor is tested on bare-metal (physical machine), VMware’s nested virtualization and Hyper-V’s nested virtualization.

View WPP Tracing Messages

To test WPP Tracing you need an application for parsing messages, I use TraceView.

TraceView is located in the tools\<Platform> subdirectory of the Windows Driver Kit (WDK), where <Platform> represents the platform you are running the trace session on, for example, x86, x64, or arm64.

There are also other applications both GUI and Command-line for this purpose, you can see a list of some of these apps here.

First, open the traceview (run as administrator), go to File-> Create New Log Session, and use the .pdb file generated by visual studio. PDB file contains debugging information, and for WPP Tracing, they contain GUID and format of messages.

WPP Tracing Traceview

When you select your provider, then click Next.

WPP Tracing Selected Provider

Here you can configure what kind of messages you want to see, e.g you only want to see error messages.

The default configuration is to see all the messages.

WPP Tracing Log Session

Finally, you’ll see the following results.

WPP Traceview result

How to test?

Now it’s time to see what we’ve done in this part !

Note: None of the below tests are active by default, you have to uncomment specific lines to see results in your hypervisor!

Event Injection & Exception Bitmap Demo

In order to test event injection and exception bitmap we have a scenario where we want to monitor each debug breakpoint that is triggered in a user-mode application.

For this, I debugged an application with Immunity Debugger and put a breakpoint on multiple addresses. We want to intercept each breakpoint from any applications.

First, uncomment the following line in Vmx.c .

1
+2
+
	// Set exception bitmap to hook division by zero (bit 1 of EXCEPTION_BITMAP)
+	 __vmx_vmwrite(EXCEPTION_BITMAP, 0x8); // breakpoint 3nd bit
+

This will cause a vm-exit on each execution of breakpoint exception using Exception Bitmap.

The following codes are responsible to handle the vm-exits for Exception Bitmap. We check to see what was the interrupt/exception that causes this vm-exit by VM_EXIT_INTR_INFO from VMCS. If it’s a SOFTWARE EXCEPTION and its a vector is BREAKPOINT then we’re sure that execution of an (int 3 or 0xcc) was the cause for this vm-exit.

Now, we create a log that shows a breakpoint that happened in GUEST_RIP then re-inject the breakpoint back to the guest (Event Injection). We have to re-inject it back to the guest because the event is canceled after this vm-exit, you can check it, just remove the EventInjectBreakpoint(), and your user-mode debugger will no longer work.

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
+
	case EXIT_REASON_EXCEPTION_NMI:
+	{
+		/*
+
+		Exception or non-maskable interrupt (NMI). Either:
+			1: Guest software caused an exception and the bit in the exception bitmap associated with exception’s vector was set to 1
+			2: An NMI was delivered to the logical processor and the “NMI exiting” VM-execution control was 1.
+
+		VM_EXIT_INTR_INFO shows the exit infromation about event that occured and causes this exit
+		Don't forget to read VM_EXIT_INTR_ERROR_CODE in the case of re-injectiong event
+
+		*/
+
+		// read the exit reason
+		__vmx_vmread(VM_EXIT_INTR_INFO, &InterruptExit);
+
+		if (InterruptExit.InterruptionType == INTERRUPT_TYPE_SOFTWARE_EXCEPTION && InterruptExit.Vector == EXCEPTION_VECTOR_BREAKPOINT)
+		{
+
+			ULONG64 GuestRip;
+			// Reading guest's RIP 
+			__vmx_vmread(GUEST_RIP, &GuestRip);
+
+			// Send the user
+			LogInfo("Breakpoint Hit (Process Id : 0x%x) at : %llx ", PsGetCurrentProcessId(), GuestRip);
+
+			GuestState[CurrentProcessorIndex].IncrementRip = FALSE;
+
+			// re-inject #BP back to the guest
+			EventInjectBreakpoint();
+
+		}
+		else
+		{
+			LogError("Not expected event occured");
+		}
+		break;
+	}
+

To see the result as a gif, click the link below.

View Example as a .gif (event-inject-and-exception-bitmap.gif)

Event Injection & Exception Bitmap Example

Hidden Hooks Demo

Hidden hooks are divided into two parts, The first part is for hidden hooks of Read/Write (It’s like simulating hardware debug registers without any limitation), and the second part is hidden hooks for execution which is an equivalent of invisible in-line hooks.

In order to activate the hidden hooks test, uncomment HiddenHooksTest() from Driver.c .

Note that you can simultaneously use Hidden Hooks for Read/Write, Execute or syscall hook, there is no limitation.

1
+2
+3
+4
+
	//////////// test //////////// 
+	HiddenHooksTest();
+	// SyscallHookTest();
+	////////////////////////////// 
+

Read/Write Hooks or Hardware Debug Registers Simulation

For testing read and write, uncomment the first line, now you’ll be notified in the case of any Read/Write from any locations to the current thread’s _ETHREAD structure (KeGetCurrentThread()).

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
/* Make examples for testing hidden hooks */
+VOID HiddenHooksTest()
+{
+	// Hook Test
+		EptPageHook(KeGetCurrentThread(), NULL, NULL, TRUE, TRUE, FALSE);
+	//	EptPageHook(ExAllocatePoolWithTag, ExAllocatePoolWithTagHook, (PVOID*)&ExAllocatePoolWithTagOrig, FALSE, FALSE, TRUE);
+
+	// Unhook Tests
+	//HvPerformPageUnHookSinglePage(ExAllocatePoolWithTag);
+	//HvPerformPageUnHookAllPages();
+	
+}
+

To see the result as a gif, click the link below.

View Example as a .gif (hidden-hook-example-read-write.gif)

Hidden Hooks (Read/Write)

Also, you can see the results in Windbg !

Hidden Hooks (Read/Write)

Hidden Execution Hook

The second scenario for hidden hooks is to inline hook the ExAllocatePoolWithTag function.

This is done by uncommenting the following line.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
/* Make examples for testing hidden hooks */
+VOID HiddenHooksTest()
+{
+	// Hook Test
+	//	EptPageHook(KeGetCurrentThread(), NULL, NULL, TRUE, TRUE, FALSE);
+		EptPageHook(ExAllocatePoolWithTag, ExAllocatePoolWithTagHook, (PVOID*)&ExAllocatePoolWithTagOrig, FALSE, FALSE, TRUE);
+
+	// Unhook Tests
+	//HvPerformPageUnHookSinglePage(ExAllocatePoolWithTag);
+	//HvPerformPageUnHookAllPages();
+	
+}
+

And also a simple fucntion that logs each ExAllocatePoolWithTag.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
/* Hook function that HooksExAllocatePoolWithTag */
+PVOID ExAllocatePoolWithTagHook(
+	POOL_TYPE	PoolType,
+	SIZE_T      NumberOfBytes,
+	ULONG       Tag
+)
+{
+	LogInfo("ExAllocatePoolWithTag Called with : Tag = 0x%x , Number Of Bytes = %d , Pool Type = %d ", Tag, NumberOfBytes, PoolType);
+	return ExAllocatePoolWithTagOrig(PoolType, NumberOfBytes, Tag);
+}
+

The hook is applied ! you can also try to use (u nt!ExAllocatePoolWithTag) and see there is no in-line hook there, so it’s completely hidden and of course PatchGuard compatible!

To see the result as a gif, click the link below.

View Example as a .gif (hidden-hook-example-exec.gif)

Hidden Hooks (Exec)

Syscall Hook Demo

Our scenario for testing system-call hooks is first uncommenting the following line in Driver.c .

1
+2
+3
+4
+
	//////////// test //////////// 
+	// HiddenHooksTest();
+	SyscallHookTest();
+	////////////////////////////// 
+

The following function first searches for API Number 0x55 (on Windows 10 1909, 0x55 represents to NtCreateFile this is not true for all versions of Windows you have to find the correct API Number for NtCreateFile based on your Windows version, a full list of system-call numbers for Nt Table is here and for Win32k Table is here).

After finding the address of NtCreateFile (Syscall number 0x55) we set a hidden hook on this address.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
/* Make examples for testing hidden hooks */
+VOID SyscallHookTest() {
+
+	// Note that this syscall number is only valid for Windows 10 1909, you have to find the syscall number of NtCreateFile based on
+	// Your Windows version, please visit https://j00ru.vexillium.org/syscalls/nt/64/ for finding NtCreateFile's Syscall number for your Windows.
+	
+	INT32 ApiNumberOfNtCreateFile = 0x0055;
+	PVOID ApiLocationFromSSDTOfNtCreateFile = SyscallHookGetFunctionAddress(ApiNumberOfNtCreateFile, FALSE);
+
+	if (!ApiLocationFromSSDTOfNtCreateFile)
+	{
+		LogError("Error in finding base address.");
+		return FALSE;
+	}
+
+	if (EptPageHook(ApiLocationFromSSDTOfNtCreateFile, NtCreateFileHook, (PVOID*)&NtCreateFileOrig, FALSE, FALSE, TRUE))
+	{
+		LogInfo("Hook appkied to address of API Number : 0x%x at %llx\n", ApiNumberOfNtCreateFile, ApiLocationFromSSDTOfNtCreateFile);
+	}
+}
+

For handling in-line hook, the following function is used which creates a log based on the file name and finally calls the original NtCreateFile.

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
+
/* Hook function that hooks NtCreateFile */
+NTSTATUS NtCreateFileHook(
+	PHANDLE            FileHandle,
+	ACCESS_MASK        DesiredAccess,
+	POBJECT_ATTRIBUTES ObjectAttributes,
+	PIO_STATUS_BLOCK   IoStatusBlock,
+	PLARGE_INTEGER     AllocationSize,
+	ULONG              FileAttributes,
+	ULONG              ShareAccess,
+	ULONG              CreateDisposition,
+	ULONG              CreateOptions,
+	PVOID              EaBuffer,
+	ULONG              EaLength
+)
+{
+	HANDLE kFileHandle;
+	NTSTATUS ConvertStatus;
+	UNICODE_STRING kObjectName;
+	ANSI_STRING FileNameA;
+
+	kObjectName.Buffer = NULL;
+
+	__try
+	{
+
+		ProbeForRead(FileHandle, sizeof(HANDLE), 1);
+		ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), 1);
+		ProbeForRead(ObjectAttributes->ObjectName, sizeof(UNICODE_STRING), 1);
+		ProbeForRead(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length, 1);
+
+		kFileHandle = *FileHandle;
+		kObjectName.Length = ObjectAttributes->ObjectName->Length;
+		kObjectName.MaximumLength = ObjectAttributes->ObjectName->MaximumLength;
+		kObjectName.Buffer = ExAllocatePoolWithTag(NonPagedPool, kObjectName.MaximumLength, 0xA);
+		RtlCopyUnicodeString(&kObjectName, ObjectAttributes->ObjectName);
+
+		ConvertStatus = RtlUnicodeStringToAnsiString(&FileNameA, ObjectAttributes->ObjectName, TRUE);
+		LogInfo("NtCreateFile called for : %s", FileNameA.Buffer);
+
+	}
+	__except (EXCEPTION_EXECUTE_HANDLER)
+	{
+	}
+
+	if (kObjectName.Buffer)
+	{
+		ExFreePoolWithTag(kObjectName.Buffer, 0xA);
+	}
+
+
+	return NtCreateFileOrig(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize, FileAttributes,
+		ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);
+}
+

To see the result as a gif, click the link below.

View Example as a .gif (syscall-hook-example-1.gif)

Syscall Hook

Also, you can see the results in Windbg !

Syscall Hook

Discussion

It’s time to see the questions and discussions about this part, the discussion is usually about questions and experience about developing hypervisors. Thanks to Petr for making this part ready.

1. What is the IRQL in VMX root-mode? Have you ever tried to use KeGetCurrentIrql() in the VMX root-mode and see the result? It returns the below results in the picture, different IRQLs.

IRQL in VMX Root-Mode

- IRQL is nothing more than Cr8 register, Cr8 register doesn’t change when VM-exit occurs, therefore, your KeGetCurrentIrql() returns the IRQL before the VM-exit happened.

- In VM-root mode, there is “no IRQL”, because VMX doesn’t know such terms as IRQL (it’s Microsoft thingy) but practically speaking, HIGH_IRQL is what’s closest to the state in VMX-root mode because interrupts are disabled

- Actually, IRQL requirements don’t mean much when running in the VMM context. For example, even if you enter at PASSIVE_LEVEL, you are technically at HIGH_LEVEL for all intents and purposes, as interrupts are disabled. 

- You can use KeGetEffectiveIrql() in VMX-root mode, and it always returns HIGH_LEVEL (that function checks if IF (Interrupt Flag) bit in EFLAGS is set and if not, it returns HIGH_LEVEL, if yes, then it returns the same value as KeGetCurrentIrql(). The EFLAGS.IF is cleared when VM-exit happened but the IF only affects hardware interrupt, and exceptions can still occur.

- If you still have a problem with understanding IRQL in VMM then there are some interesting questions answered by Alex in Hyperplatform: https://github.com/tandasat/HyperPlatform/issues/3#issuecomment-231804839 that tries to explain why vmx root-mode is like HIGH_IRQL. I try to add some explanation to them.

2. Is it safe for you to be context switched by the OS while in the middle of VMM mode?

- Of course not. So you are at least at DISPATCH_LEVEL (As Windows schedules all threads to run at IRQLs below DISPATCH_LEVEL ).

3. Is it safe for you to “wait” on an object while at VMM mode?

- Of course not, you would be context switched to another thread/idle thread which would now be running as VMM Host. (Means that you wait on some objects and when another vm-exit occurs, you’re no longer in the previous thread.)

4. Is it safe/OK for you to receive DPCs while in the middle of VMM mode?

- Again, of course not. Another reason why you are at least at DISPATCH_LEVEL.

5. Could you receive a DPC, even if you wanted to?

- Nope. Receiving a DPC requires an interrupt, and IF in r/eflags is off, so Local APIC will never deliver it.

6. Will you receive any Device Interrupts?

- Nope, because EFLAGS.IF is off.

7. Would you want to be interrupted in the middle of VMM mode?

- Also nope. So you are at least at MAX_DIRQL.

8. Will you receive the clock interrupt?

- Nope (also why you hit a CLOCK WATCHDOG BSOD sometimes)… So you are at least at CLOCK_LEVEL.

9. Will you receive IPIs?

- Nope, because IF is off, so Local APIC will never send them. You also probably don’t want to be running IPI while inside the VMM host… So you are at least at IPI_LEVEL. Technically because you are not in the middle of handling an IPI, but rather you’ve disabled interrupts completely, you are at IPI_LEVEL + 1, aka HIGH_LEVEL.

10. Why ExAllocatePoolWithTag doesn’t work on Vmx root-mode?

- In other words, if you call, for example, ExAllocatePoolWithTag, and this is PAGED POOL, you can get unlucky and this will require page-in which requires blocking your thread, and now, some other thread will run in VMM host mode… Sure, you can get lucky and control will come back to you, but this is insane… If you request NON-PAGED POOL, it will “appear to work”… And then in one situation, a TLB flush will be required, which sends an IPI… Which can’t be delivered… And so it will hang. etc.

11. Is it ok that I used Insert DPC in VMX root-mode? I used KeInsertQueueDpc (because according to MSDN this function can be called at Any Level).

- Yes and no. it’s okay when you have GUARANTEED that you won’t get conflicting VM-exit that would somehow result in a recursion/deadlock, but that very depends on the use case.

- For demonstration purposes, I wouldn’t mind using KeInsertQueueDpc in “real/production” environment, I would probably inject NMI from the hypervisor, and in NMI handler I would queue DPC.

- It’s one more indirection, therefore it’s going to be slightly slower, but I think it’s a generally safer way… (I use it this way) however, I must note that it’s not bulletproof, as I already ran into recursive NMI injection and deadlocks in NMI handler too.

- As I said, there’s no silver bullet, there always will be some dark corners when you try to communicate with the underlying OS.

12. Using functions like RtlStringCchLengthA and RtlStringCchLengthA is not allowed because according to MSDN its IRQL is PASSIVE_LEVEL, so we can’t use them in VMX-Root mode? What should we do instead?

- We can use sprintf (and sprintf like functions) from the C std library. it’s safe to use since it doesn’t allocate any memory. AFAIK RtlString* functions are in the PAGE section, therefore they can be paged out and if you call them from VMX-root mode when they’re paged out…. you know what happens ;)

13. I was reading about VPID (INVVPID) and this seems to be unusable for hypervisors like hvpp and hyperplatform and ours? Am I right? I mean is there any special case in hypervisors that virtualize an already running system that INVVPID is preferred instead of INVEPT?

- You are right, invvpid is generally useless in our cases. the only case I can think of where invvpid might be beneficial is in emulation of “invlpg” instruction, see here.

- Simply said, invept will invalidate ALL EPT mappings. with invvpid, you can invalidate SPECIFIC addresses in the guest (i.e. underlying OS). I think you know how caches generally work, but I’ll try to explain anyway: with invept, you lose all cache for the guest, therefore it will take time to fill that cache again (each first memory access after INVEPT will be slow).

- with invvpid, the cache is retained, but the only single address is invalidated, therefore loading of only THAT address will be slow with that said, I really can’t think of any other practical example where you’d need that, except the invlpg emulation mentioned above.

14. What happens if we’re in vmx root and access an address that will cause an EPT violation?

It’s like asking “what happens if we have paging disabled and access an address that will cause a page fault” EPTs are for guests, vmx-root is essentially host. EPT translation doesn’t happen when you’re in vmx root. Only regular paging. Therefore - it doesn’t matter if you access an address that will cause an EPT violation or not, what matters is whether is that address valid in vmx-root’s regular CR3 page tables.

15. What if we want to cause vm-exit on exception/interrupts with IDT Index > 32? Exception Bitmap is just a 32-bit field in VMCS!

There are only 32 exceptions in x86 architecture. The rest are external-interrupts, which are intercepted by the pin-based control “external-interrupt exiting”. This means that you can’t select a special interrupt to cause a vm-exit, but you can configure pin-based control to cause vm-exit in the case of each interrupt.

16. If several CPUs try to acquire the same spinlock at the same time, which CPU gets the spinlock first?

- Normally, there is no order - the CPU with the fastest electrons wins :). The kernel does provide an alternative, called queued spinlocks that serve CPUs on a FIFO basis. These only work with IRQL DISPATCH_LEVEL. The relevant APIs are KeAcquireInStackQueuedSpinLock and KeReleaseInStackQueuedSpinLock. Check the WDK documentation for more details.

17. We use DPCs to transfer messages, and because we may be executing in an arbitrary user-mode process as part DPCs, then why is our message tracing works without problem?

- It works because we use METHOD_BUFFERED in our IOCTL. Generally, you have to specify that you need a buffered method in driver entry.

1
+2
+
	// Establish user-buffer access method.
+	DeviceObject->Flags |= DO_BUFFERED_IO;
+

- But in the case of IOCTLs, you have specified this flag in IOCTL code, if you’re not familiar with METHOD_BUFFERED, it’s a way that Windows gives you a system-wide address which is valid in any process (kernel-mode) that’s why we can fill the buffer from any arbitrary process and address in Irp->AssociatedIrp.SystemBuffer in any process.

- Using METHOD_BUFFERED is, of course, slower, but it solves these kinds of problems and is it’s generally safer.

18. Why we didn’t use APCs instead of DPCs in message tracing?

- We can use APCs instead of DPCs in our case, but using DPCs gives us a better priority as the callback is executed in DISPATCH_LEVEL as soon as possible. APCs are thread-specific means that whenever a thread runs, we have the chance that our callback is executed while DPCs are processor-specific so we can interrupt any random process, so it’s faster.

- Another reason is APCs are undocumented kernel object while DPCs are documented so that’s the reason why programmers prefer to use DPCs.

Conclusion

We come to the end of this part, in this part we saw some important things that can be implemented with virtualizing an already running system like hidden hooks, syscall hook, event injection, exception bitmap, and our custom VMX Root compatible message tracing, by now you should be able to use your hypervisor driver in many kinds of researches and solve your reverse-engineering problems.

In the next part, we’ll look at some advanced virtualization topics like APIC Virtualization and lots of other things to make a stable and useful hypervisor.

Hope you guys enjoyed it, see you in the next part.

Aniiiime :)

References

[1] Virtual Processor IDs and TLB - (http://www.jauu.net/2011/11/13/virtual-processor-ids-and-tlb/)
[2] INVVPID — Invalidate Translations Based on VPID - (https://www.felixcloutier.com/x86/invvpid)
[3] INVPCID — Invalidate Process-Context Identifier - (https://www.felixcloutier.com/x86/invpcid)
[4] Here’s how, and why, the Spectre and Meltdown patches will hurt performance - (https://arstechnica.com/gadgets/2018/01/heres-how-and-why-the-spectre-and-meltdown-patches-will-hurt-performance/)
[5] Is vmxoff path really safe/correct? - (https://github.com/tandasat/HyperPlatform/issues/3)
[6] Day 5: The VM-Exit Handler, Event Injection, Context Modifications, And CPUID Emulation - (https://revers.engineering/day-5-vmexits-interrupts-cpuid-emulation/)
[7] Test-and-set - (https://en.wikipedia.org/wiki/Test-and-set)
[8] _interlockedbittestandset intrinsic functions - (https://docs.microsoft.com/en-us/cpp/intrinsics/interlockedbittestandset-intrinsic-functions?view=vs-2019)
[9] Spinlocks and Read-Write Locks - (https://locklessinc.com/articles/locks/)
[10] PAUSE - Spin Loop Hint - (https://c9x.me/x86/html/file_module_x86_id_232.html)
[11] What is the purpose of the “PAUSE” instruction in x86? - (https://stackoverflow.com/questions/12894078/what-is-the-purpose-of-the-pause-instruction-in-x86)
[12] How does x86 pause instruction work in spinlock and can it be used in other scenarios? - (https://stackoverflow.com/questions/4725676/how-does-x86-pause-instruction-work-in-spinlock-and-can-it-be-used-in-other-sc)
[13] Introduction to the volatile keyword - (https://www.embedded.com/introduction-to-the-volatile-keyword/)
[14] Deferred Procedure Call - (https://en.wikipedia.org/wiki/Deferred_Procedure_Call)
[15] Reversing DPC: KeInsertQueueDpc - (https://repnz.github.io/posts/practical-reverse-engineering/reversing-dpc-keinsertqueuedpc/)
[16] Dumping DPC Queues: Adventures in HIGH_LEVEL IRQL - (https://repnz.github.io/posts/practical-reverse-engineering/dumping-dpc-queues/)
[17] Vol 3C – Chapter 31 – (31.5.1 Algorithms for Determining VMX Capabilities) – (https://software.intel.com/en-us/articles/intel-sdm)
[18] Vol 3D – Appendix A.2 – (RESERVED CONTROLS AND DEFAULT SETTINGS) – (https://software.intel.com/en-us/articles/intel-sdm)
[19] Add WPP tracing to the Kernel Mode (Windows driver) – (http://kernelpool.blogspot.com/2018/05/add-wpp-tracing-to-kernel-mode-windows.html)
[20] WPP Software Tracing – (https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/wpp-software-tracing)
[21] TraceView – (https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/traceview)
[22] What is the difference between Trap and Interrupt? – (https://stackoverflow.com/questions/3149175/what-is-the-difference-between-trap-and-interrupt)
[23] How to disable Hyper-V in command line? – (https://stackoverflow.com/questions/30496116/how-to-disable-hyper-v-in-command-line)
[24] Run Hyper-V in a Virtual Machine with Nested Virtualization – (https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/nested-virtualization)
[25] Hypervisor Top-Level Functional Specification – (https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs)
[26] Requirements for Implementing the Microsoft Hypervisor Interface – (https://github.com/Microsoft/Virtualization-Documentation/raw/master/tlfs/Requirements%20for%20Implementing%20the%20Microsoft%20Hypervisor%20Interface.pdf)
[27] Simple Svm Hook Specification – (https://github.com/tandasat/SimpleSvmHook)
[28] x86 calling conventions – (https://en.wikipedia.org/wiki/X86_calling_conventions)
[29] Exceptions – (https://wiki.osdev.org/Exceptions)
[30] Nt Syscall Table – (https://j00ru.vexillium.org/syscalls/nt/64/)
[31] Win32k Syscall Table – (https://j00ru.vexillium.org/syscalls/win32k/64/)
[32] KVA Shadow: Mitigating Meltdown on Windows – (https://msrc-blog.microsoft.com/2018/03/23/kva-shadow-mitigating-meltdown-on-windows/)
[33] HyperBone - Minimalistic VT-X hypervisor with hooks – (https://github.com/DarthTon/HyperBone)
[34] Syscall Hooking Via Extended Feature Enable Register (EFER) – (https://revers.engineering/syscall-hooking-via-extended-feature-enable-register-efer/)
[35] xdbg64’s TitanHide – (https://github.com/dotfornet/TitanHide/)
[36] System Service Descriptor Table - SSDT – (https://ired.team/miscellaneous-reversing-forensics/windows-kernel/glimpse-into-ssdt-in-windows-x64-kernel)
[37] DdiMon – (https://github.com/tandasat/DdiMon)
[38] Gbhv - Simple x64 Hypervisor Framework – (https://github.com/Gbps/gbhv)
[39] Hook SSDT(Shadow) – (https://m0uk4.gitbook.io/notebooks/mouka/windowsinternal/ssdt-hook)
[40] DetourXS – (https://github.com/DominicTobias/detourxs)
[41] What is the difference between Trap and Interrupt? – (https://stackoverflow.com/questions/3149175/what-is-the-difference-between-trap-and-interrupt)

This post is licensed under CC BY 4.0 by the author.

Hypervisor From Scratch – Part 7: Using EPT & Page-Level Monitoring Features

HyperDbg’s One Thousand and One Nights

Comments powered by Disqus.

diff --git a/topics/import-address-table-in-action/index.html b/topics/import-address-table-in-action/index.html new file mode 100644 index 00000000..71ba6675 --- /dev/null +++ b/topics/import-address-table-in-action/index.html @@ -0,0 +1,3 @@ + Import Address Table (IAT) in action | Rayanfam Blog
Home Import Address Table (IAT) in action
Post
Cancel

Import Address Table (IAT) in action

Did you ever think about how different dll files with different versions and obviously with different addresses of functions work perfectly together ? The answer is Import Address Table (IAT).

In the previous post I describe about how to get SSDT. IAT is somehow a User-Mode version of SSDT and in this post I’m gonna write about what I read and experience about IAT in action.

Why IAT is important ? It is important because it gives PE executer a list of Functions pointer which normally used to jump to Windows API’s functions. That’s okay but the thing is most of packers and protectors that I see just destroy this table ! So it is important for a reverser to know about this table because if you don’t know about it, simply can’t dump a packed exe .

All normal (Not packed) application make this table at first so in the future calls they can use calls or jumps to this addresses to reach to the functions and put function pointer to eip register.

For example, I see this kind of calling a lot in ollydbg :

1
+
Call jmp&.Kernel32.ExitProcessA
+

Which points to IAT version of ExitProcessA in Kernel32.dll image.

One of the ways to easily hook functions is to change IAT’s addresses so the new invoked functions come to your code instead of original function.

Where is IAT ?!

For creating IAT, process first create a import table by going to “ImageBase (Most of times 0x400000) + 0x3C” in this address you can find a pointer that you can find import table address in (the pointer + 0x80). This table consists of multiple variables in a structure which is called Image_Import_Descriptor and ends with a null Image_Import_Descriptor. The first variable of this structure is OrginalFirstThunk. This field is actually a backup variable (I explain it more in later paragraph in this post.)

The second parameter is TimeDateStamp (Which probably points to time or version of building function but can be null) and the third is ForwardChain (Can be null too) and last is Name.

Then process just follows the FirstThunk address and goes to dll to find the address of function which FirstThunk points to. If with any reason it can’t find the function then try it again by using OrginalFisrtThunk instead of FirstThunk and if it can’t find it again then simply the program crashes !

IAT is actually in .data but if you are interfering with some kinds of packers or proctors it can be different or completely destroyed because if you dump an exe with a destroyed IAT it doesn’t work. If you find IAT in your debugger then keep in mind that all addresses separate with two null dword.

I write this post because I can’t find any complete description about how IAT works in action but I simply describe how it works so if you have any problem with finding IAT then you can comment in this post.

In future posts I will explain about how to build new IAT when this table is destroyed by packers or protectors.

This post is licensed under CC BY 4.0 by the author.

Bind9 chroot on debian 8

Lack of rechecking permissions in Android

Comments powered by Disqus.

diff --git a/topics/inline-assembly-in-x64/index.html b/topics/inline-assembly-in-x64/index.html new file mode 100644 index 00000000..32d5405a --- /dev/null +++ b/topics/inline-assembly-in-x64/index.html @@ -0,0 +1,95 @@ + x64 Inline Assembly in Windows Driver Kit | Rayanfam Blog
Home x64 Inline Assembly in Windows Driver Kit
Post
Cancel

x64 Inline Assembly in Windows Driver Kit

As my testing always interferes with running assembly directly in kernel-level and unfortunately Microsoft no longer supports x64 inline assembly through their compilers and as I always have struggle creating a simple inline assembly project so I decided to create a post to describe how to create a Windows Driver Kit project with Inline assembly to run kernel code directly in a kernel driver.

This terms only applies to x64 Windows Kernel Driver, you can use _asm and asm directly in your code in x86 drivers and user-mode codes, Linux also supports x64 Inline assembly in their kernel modules.

Let’s get down to business.

First of all, you should have Windows WDK support in your visual studio, just follow the steps here, then after downloading Windows Driver Kit and Windows SDK then you can create a WDK Project in your visual studio like this :

Create Project

So I created a project named MyDriver after that you should add some files to your project. As you might know, you should create a .asm file beside your .c (code) and .h (header) files thus I created two files named (source.cpp)

add cpp

and the other file is source.asm.

Add Assembly file

I made a simple assembly function (Masm) along with a simple driver entry function, you can add these files to your project. For example the following lines for source.asm.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
PUBLIC MainAsm
+.code _text
+
+MainAsm PROC PUBLIC
+push rax
+
+; do stuffs here
+
+pop rax
+ret
+MainAsm ENDP
+
+MainAsm2 PROC PUBLIC
+int 3
+ret
+MainAsm2 ENDP 
+
+END
+

There are some important notes here, 

  • Your function should ends with ret, if not it never comes back from your function and as long as you are in the kernel, every error lead to a BSOD.
  • The other important note is you are responsible for the calling convention used in your driver (for passing arguments or clearing the stack).
  • The default calling convention in Windows x64 driver is fastcall so in most situations, all the parameters placed in the registers and you are not forced to clear anything before ret.

The other file is driver entry (Source.cpp), you can use something like :

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
+
#include <ntddk.h>
+#include <wdf.h>
+
+extern void inline MainAsm(void);
+extern void inline MainAsm2(void);
+
+VOID Unload(IN WDFDRIVER Driver)
+{}
+
+NTSTATUS MyDriverEntry(_In_ PDRIVER_OBJECT     DriverObject,
+	_In_ PUNICODE_STRING    RegistryPath)
+{
+
+	// NTSTATUS variable to record success or failure
+	NTSTATUS status = STATUS_SUCCESS;
+
+	// Allocate the driver configuration object
+	WDF_DRIVER_CONFIG config;
+
+	WDF_DRIVER_CONFIG_INIT(
+		&config,
+		WDF_NO_EVENT_CALLBACK // This is a non-pnp driver.
+	);
+
+	config.EvtDriverUnload = Unload;
+	MainAsm();
+
+	return status;
+}
+

Please note that I defined MainAsm and MainAsm2 from the previous assembly file as function name so that I can use it directly in my DriverEntry or etc.

Make sure to change the architecture to x64.

Change arch to x64

That’s it, build your project and you can use tools like OSR Driver Loader from osronline.com to load your driver.

Almost in all current Windows versions (<=Window 7) you should disable Driver Signature Enforcement, a protection for Windows to prevent non-verified drivers or rootkits to go in ring 0.

Update :

If you see some errors like this in your building process,

Error

Then make sure to add your driver entry in properties -> Linker -> All Options -> search for the entry point.

 Add entry

That should solve the problem.

I also added the source of this project on my GitHub, take a look at :

[https://github.com/SinaKarvandi/x64-Driver-Inline-Assembly/]

This post is licensed under CC BY 4.0 by the author.

Inside Windows Page Frame Number (PFN) – Part 2

Hypervisor From Scratch - Part 1: Basic Concepts & Configure Testing Environment

Comments powered by Disqus.

diff --git a/topics/inside-windows-page-frame-number-part1/index.html b/topics/inside-windows-page-frame-number-part1/index.html new file mode 100644 index 00000000..b9306b8a --- /dev/null +++ b/topics/inside-windows-page-frame-number-part1/index.html @@ -0,0 +1,199 @@ + Inside Windows Page Frame Number (PFN) - Part 1 | Rayanfam Blog
Home Inside Windows Page Frame Number (PFN) - Part 1
Post
Cancel

Inside Windows Page Frame Number (PFN) - Part 1

Introduction (Page Frame Number)

Windows and almost all the OSs use Page Frame Number Database in order to have a track of virtually allocated pages to know which page must be freed or evicted or if a page needs to be cached and etc.

All of these kinds of stuff manages through a list, called Page Frame Number (PFN). A long list of explanation about the states of every physically and virtually allocated pages and its corresponding attributes.

In the rest of this post, I’ll explain about Windows implementation of Page Frame Number with lots of practical examples, the following part describes basic concepts implementations, you should also read the next part in order to see how can you use or change these attributes.

If you’re familiar with non-PAE mode and PAE mode systems then I should note that in a non-PAE mode every PFN structure takes 24 bytes while in a PAE mode system this size increases to 28 so if your pages are 4096 bytes then allocates about 24 bytes more to keep tracks of every page.

As you can see here:

In non-PAE mode 24 bytes in the PFN database represents each 4 KB page of physical memory – this is a ratio of 170:1. In PAE mode 28 bytes represents each 4 KB page of physical memory – this is a ratio of 146:1. This means that roughly 6 MB or 7 MB is needed in the PFN database to describe each 1 GB of physical memory. This might not sound like much, but if you have a 32-bit system with 16 GB of physical memory, then it requires about 112 MB of the 2 GB of kernel virtual address space just to address the RAM. This is another reason why systems with 16 GB of physical memory or more will not allow the 3GB mode (also known as IncreaseUserVA) which increases the user virtual address space to 3 GB and decreases the kernel virtual address space to 1 GB on 32-bit systems.

One of the benefits of having extended pages (e.g 2MB for every page) is that it needs lower amounts of MMPFN.

Before start getting deep into the PFN, please remember the term “Page” is mostly used in the operating system level concepts whereas “Frame” is used in CPU Level concepts, therefore “Page” means virtual page and “Page Frame” means physical page.

PFN Lists

The Page Frame Number consists of lists that describe the state of some pages, there are Active Lists which shows an active page (e.g in working sets or etc), Standby List which means a list that previously backed in the disk and the page itself can be emptied and reused without incurring a disk IO, Modified List which shows that the page is previously modified and somehow must be written to the disk, Freed List, as the name describes, it shows a page that is no longer needed to be maintained and can be freed and finally Zero List that describes a page that is free and has all zeroes (0) in it.

A great picture derived from here which shows how the PFN database lists are related to each other :

PFN

These lists are used to manage memory in “page faults” state in the way that everytime a “page fault” occurs, Windows tries to find an available page form, Zero List, if the list is empty then it gets one from Freed List and zeroes the page and use it, on the other hand, if the Freed List is also empty then it goes to the Standby List and zeroes that page.

The Zero Page Thread

In Windows, there is a thread with the priority of 0 which is responsible for zeroing memory when system is idle and is the only thread in the entire system that runs at priority 0. (which is the lowest available priority because the user threads are at least 1). This thread clears the Freed List whenever is possible. Also, there is a function in Windows called RtlSecureZeroMemory() which frees a location securely but in the kernel perspective nt!KeZeroPages is responsible for freeing the pages.

The following picture shows the behavior of zero-thread:

Zero-thread

Let’s find the Zero Thread!

We know that it comes from system process, its priority is 0, this should be enough and nothing more is needed. First try to find System’s nt!_eprocess:

1
+
!process 0 System
+

Now we can see the System’s threads, the details of our target thread (zero-thread) are:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
        THREAD ffffd4056ed00040  Cid 0004.0040  Teb: 0000000000000000 Win32Thread: 0000000000000000 WAIT: (WrFreePage) KernelMode Non-Alertable
+            fffff8034637f148  NotificationEvent
+            fffff80346380480  NotificationEvent
+        Not impersonating
+        DeviceMap                 ffff99832ae1b010
+        Owning Process            ffffd4056ec56040       Image:         System
+        Attached Process          N/A            Image:         N/A
+        Wait Start TickCount      4910           Ticks: 4 (0:00:00:00.062)
+        Context Switch Count      21023          IdealProcessor: 3             
+        UserTime                  00:00:00.000
+        KernelTime                00:00:01.109
+        Win32 Start Address nt!MiZeroPageThread (0xfffff80346144ed0)
+        Stack Init ffffe700b7c14c90 Current ffffe700b7c14570
+        Base ffffe700b7c15000 Limit ffffe700b7c0f000 Call 0000000000000000
+        Priority 0 BasePriority 0 PriorityDecrement 0 IoPriority 2 PagePriority 5
+        Child-SP          RetAddr           Call Site
+        ffffe700`b7c145b0 fffff803`46016f8a nt!KiSwapContext+0x76
+        ffffe700`b7c146f0 fffff803`46016951 nt!KiSwapThread+0x16a
+        ffffe700`b7c147a0 fffff803`46014ba7 nt!KiCommitThreadWait+0x101
+        ffffe700`b7c14840 fffff803`461450b7 nt!KeWaitForMultipleObjects+0x217
+        ffffe700`b7c14920 fffff803`460bba37 nt!MiZeroPageThread+0x1e7
+        ffffe700`b7c14c10 fffff803`46173456 nt!PspSystemThreadStartup+0x47
+        ffffe700`b7c14c60 00000000`00000000 nt!KiStartSystemThread+0x16
+

As you can see, its start address is at nt!MiZeroPageThread and its priority-level is 0 and if we see the call-stack then we can see nt!MiZeroPageThread was called previously.

For more information please visit Hidden Costs of Memory Allocation.

nt!_MMPFN

The Windows structure for PFN is nt!_MMPFN which you can see below :

MMPFN

As you see, _MMPFN takes 28 bytes.

PFN records are stored in the memory based on their physical address order which means you can always calculate the physical address with the help of PFN.

1
+
Physical Address = PFN * page size(e.g 4096 Byte) + offset
+

The address of the PFN database is located at nt!MmPfnDatabase, you can use the following example to get your PFN database address in Windbg.

1
+2
+
2: kd> x nt!MmPfnDatabase
+fffff800`a2a76048 nt!MmPfnDatabase = <no type information>
+

!memusage

Another very useful command in windbg is !memusage, this command gives almost everything about PFN and pages in your memory layout and its corresponding details (e.g files, fonts, system drivers, DLL modules, executable files including their names and their paging bits modifications).

A brief sample of this command is shown below :

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
+
2: kd> !memusage
+ loading PFN database
+loading (100% complete)
+Compiling memory usage data (99% Complete).
+             Zeroed:     9841 (   39364 kb)
+               Free:   113298 (  453192 kb)
+            Standby:   105520 (  422080 kb)
+           Modified:     7923 (   31692 kb)
+    ModifiedNoWrite:        0 (       0 kb)
+       Active/Valid:   286963 ( 1147852 kb)
+         Transition:       45 (     180 kb)
+          SLIST/Bad:      567 (    2268 kb)
+            Unknown:        0 (       0 kb)
+              TOTAL:   524157 ( 2096628 kb)
+
+Dangling Yes Commit:      140 (     560 kb)
+ Dangling No Commit:    37589 (  150356 kb)
+  Building kernel map
+  Finished building kernel map
+
+ (Master1 0 for 80)
+
+ (Master1 0 for 580)
+
+ (Master1 0 for 800)
+
+ (Master1 0 for 980)
+Scanning PFN database - (97% complete) 
+ (Master1 0 for 7d100)
+Scanning PFN database - (100% complete) 
+
+  Usage Summary (in Kb):
+Control       Valid Standby Dirty Shared Locked PageTables  name
+ffffffffd 11288      0     0     0 11288     0    AWE
+ffffd4056ec4c460     0    112     0     0     0     0  mapped_file( LeelUIsl.ttf )
+ffffd4056ec4c8f0     0    160     0     0     0     0  mapped_file( malgun.ttf )
+ffffd4056ec4d6b0     0    108     0     0     0     0  mapped_file( framd.ttf )
+
+.....
+
+ffffd4057034ecd0   328    148     0     0     0     0  mapped_file( usbport.sys )
+ffffd4057034f0e0    48     28     0     0     0     0  mapped_file( mouclass.sys )
+ffffd4057034f7d0    32     28     0     0     0     0  mapped_file( serenum.sys )
+ffffd405703521a0     0     20     0     0     0     0  mapped_file( swenum.sys )
+
+.....
+
+-------- 0     20     0 ----- ----- 0  session 0 0
+-------- 4      0     0 ----- ----- 0  session 0 ffffe700b8b45000
+-------- 4      0     0 ----- ----- 0  session 1 ffffe700b8ead000
+-------- 32520      0    84 ----- ----- 1324  process ( System ) ffffd4056ec56040
+-------- 2676      0     0 ----- ----- 304  process ( msdtc.exe ) ffffd405717567c0
+-------- 4444      0     0 ----- ----- 368  process ( WmiPrvSE.exe ) ffffd405718057c0
+-------- 37756      0    60 ----- ----- 1028  process ( SearchUI.exe ) ffffd405718e87c0
+
+.....
+
+-------- 8      0     0 ----- 0 ----- driver ( condrv.sys )
+-------- 8      0     0 ----- 0 ----- driver ( WdNisDrv.sys )
+-------- 52      0     0 ----- 0 ----- driver ( peauth.sys )
+-------- 24744      0     0 ----- 0 ----- ( PFN Database )
+Summary   1147852  422260 31692 129996 204428 25156  Total
+
+.....
+
+    b45b    64      0     0    60     0     0   Page File Section
+    b56b     4      0     0     4     0     0   Page File Section
+    b7ec    84      0     0    64     0     0   Page File Section
+    b905    12      0     0     0     0     0   Page File Section
+    bf5c     4      0     0     0     0     0   Page File Section
+	
+.....
+

Note that !memusage takes a long time to finish its probes.

What if you want to know more about these pages? The Windbg help document mentioned :

Remarks

You can use the !vm extension command to analyze virtual memory use. This extension is typically more useful than !memusage. For more information about memory management, see Microsoft Windows Internals, by Mark Russinovich and David Solomon. (This book may not be available in some languages and countries.)

The !pfn extension command can be used to display a particular page frame entry in the PFN database.

Now we want survey among these command in a more detailed perspective.

That’s enough for now… I try to make another part that describes the PFN more practically, so make sure to check the blog more frequently.

The second part is also published at Inside Windows Page Frame Number (PFN) – Part 2.

References

This post is licensed under CC BY 4.0 by the author.

Captive portal detection with a working sample in all major OSs!

Cisco switch security features cheatsheet

Comments powered by Disqus.

diff --git a/topics/inside-windows-page-frame-number-part2/index.html b/topics/inside-windows-page-frame-number-part2/index.html new file mode 100644 index 00000000..468cf960 --- /dev/null +++ b/topics/inside-windows-page-frame-number-part2/index.html @@ -0,0 +1,241 @@ + Inside Windows Page Frame Number (PFN) – Part 2 | Rayanfam Blog
Home Inside Windows Page Frame Number (PFN) – Part 2
Post
Cancel

Inside Windows Page Frame Number (PFN) – Part 2

Hey there,

In the previous part, I’d explained about Page Frame Number and its importance in the OSs architecture. In this part, I’ll trace PFN more practically. I strongly recommend to read the first part, to make sure you didn’t miss anything about basic concepts.

As I described in the previous part, the PFN database is located at nt!MmPFNDatabase, in the previous versions of Windows (<Windows 10) it was statically located at 0xFFFFFA8000000000 but in Windows 10, it’s subject to ASLR.

Converting Physical Address to Virtual Address and 

Virtual Address to Physical Address

  • MmGetVirtualForPhysical (PA -> VA)

One of the purposes of using PFN database is for converting physical address to virtual address but in Windows, you can simply call nt!MmGetVirtualForPhysical and convert your physical address to virtual address, you can see a complete list of Memory Manager Routines in MSDN.

MmGetVirtualForPhysical

The MmGetVirtualForPhysical is the opposite of MmGetPhysicalAddress as it converts virtual address to physical address and MmXXX before every function in Windows kernel means it’s memory-management routine.

If we decompile this function using IDA Pro:

MmGetVirtualForPhysical-IDA

So the source is simply like this:

1
+2
+3
+4
+
__int64 __fastcall MmGetVirtualForPhysical(unsigned __int64 a1)
+{
+  return (a1 & 0xFFF) + (*(_QWORD *)(48 * (a1 >> 12) - 6047313952760i64) << 25 >> 16);
+}
+

MmGetPhysicalAddress (VA -> PA)

This function is responsible for converting Virtual Address to Physical address and will eventually call MiGetPhysicalAddress so we keep investigating and if you decompile MiGetPhysicalAddress you’ll see some interesting function: nt!MiVaToPfn.

MiGetPhysicalAddress

MiVaToPfn, as it seems, is used for converting the Virtual address to PFN.

1
+
unsigned __int64 __fastcall MiVaToPfn(__int64 a1);
+

Let’s track a special virtual address and find its corresponding PFN Record in PFN Database.

Tracking PFN Records

As I described in the first part, poi(nt!MmPfnDatabase)  can be used to get PFN Database Address:

1
+2
+
0: kd> ? poi(nt!MmPfnDatabase)
+Evaluate expression: -13194139533312 = fffff400`00000000
+

Now, let choose a kernel location to see PFN Record e.g fffff80231612000 (the location where nt module is loaded). (If you want to find user-mode addresses then you should change your context (CR3) and perform the same thing based on your virtual address.)

1
+2
+3
+4
+
0: kd> lm m nt
+Browse full module list
+start             end                 module name
+fffff802`31612000 fffff802`31e9b000   nt         (pdb symbols)
+

I convert the fffff80231612000 to its corresponding physical address using !vtop 0 Address.

1
+2
+3
+4
+5
+6
+7
+8
+
1: kd> !vtop 0 fffff80231612000
+Amd64VtoP: Virt fffff802`31612000, pagedir 1aa000
+Amd64VtoP: PML4E 1aaf80
+Amd64VtoP: PDPE 909040
+Amd64VtoP: PDE 90ac58
+Amd64VtoP: PTE 914090
+Amd64VtoP: Mapped phys 2012000
+Virtual address fffff80231612000 translates to physical address 2012000.
+

As you can see it maps to 2012000 and both results of physical and virtual addresses are the same.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
1: kd> !dc 2012000
+# 2012000 00905a4d 00000003 00000004 0000ffff MZ..............
+# 2012010 000000b8 00000000 00000040 00000000 ........@.......
+# 2012020 00000000 00000000 00000000 00000000 ................
+# 2012030 00000000 00000000 00000000 00000108 ................
+# 2012040 0eba1f0e cd09b400 4c01b821 685421cd ........!..L.!Th
+# 2012050 70207369 72676f72 63206d61 6f6e6e61 is program canno
+# 2012060 65622074 6e757220 206e6920 20534f44 t be run in DOS 
+# 2012070 65646f6d 0a0d0d2e 00000024 00000000 mode....$.......
+1: kd> dc fffff802`31612000
+fffff802`31612000  00905a4d 00000003 00000004 0000ffff  MZ..............
+fffff802`31612010  000000b8 00000000 00000040 00000000  ........@.......
+fffff802`31612020  00000000 00000000 00000000 00000000  ................
+fffff802`31612030  00000000 00000000 00000000 00000108  ................
+fffff802`31612040  0eba1f0e cd09b400 4c01b821 685421cd  ......	.!..L.!Th
+fffff802`31612050  70207369 72676f72 63206d61 6f6e6e61  is program canno
+fffff802`31612060  65622074 6e757220 206e6920 20534f44  t be run in DOS 
+fffff802`31612070  65646f6d 0a0d0d2e 00000024 00000000  mode....$.......
+

We know that the default size of every page in Windows is 4086 Bytes so if we divide our physical address by 4096 (1000h) we can get our PFN Record Number.

In our case, (2012000h/1000h) is equal to 2012, so our PFN Record Number is 2012.

There is also another command called !pte which gives your PFN Number too. Let’s verify if we found the correct value.

PFN Record Number

Ok, !pte and our result are the same. Now let’s find its record. First, you should get the nt!_MmPfn’s size.

1
+2
+
1: kd> ?? sizeof(nt!_MmPfn)
+unsigned int64 0x30
+

From the above, we have the address of nt!MmPfnDatabase (fffff400`00000000) and every record is exactly 0x30 Bytes so the final command is something like this:

dt nt!_MmPfn (Address of MmPfnDatabase) + (PFN Record Number * _MmPfn size).

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
1: kd> dt nt!_MmPfn (fffff400`00000000 + (0x2012 * 0x30))
+   +0x000 ListEntry        : _LIST_ENTRY [ 0x00000000`00000000 - 0xfffffafc`0118b090 ]
+   +0x000 TreeNode         : _RTL_BALANCED_NODE
+   +0x000 u1               : <unnamed-tag>
+   +0x008 PteAddress       : 0xfffffafc`0118b090 _MMPTE
+   +0x008 VolatilePteAddress : 0xfffffafc`0118b090 Void
+   +0x008 PteLong          : 0xfffffafc`0118b090
+   +0x010 OriginalPte      : _MMPTE
+   +0x018 u2               : _MIPFNBLINK
+   +0x020 u3               : <unnamed-tag>
+   +0x024 NodeBlinkLow     : 0
+   +0x026 Unused           : 0y0000
+   +0x026 Unused2          : 0y0000
+   +0x027 ViewCount        : 0 ''
+   +0x027 NodeFlinkLow     : 0 ''
+   +0x028 u4               : <unnamed-tag>
+

We can also use !pfn to get the details of our PFN Record directly.

1
+2
+3
+4
+5
+6
+
1: kd> !pfn 2012
+    PFN 00002012 at address FFFFF40000060360
+    flink       00000000  blink / share count 00000001  pteaddress FFFFFAFC0118B090
+    reference count 0001    used entry count  0000      Cached    color 0   Priority 4
+    restore pte 00000080  containing page 000914  Active     M       
+    Modified                
+

You can see, we computed the correct value previously.

1
+2
+
1: kd> ?(fffff400`00000000 + (0x2012 * 0x30))
+Evaluate expression: -13194139139232 = fffff400`00060360
+

You can also traverse through all the PFN Entries, just remember to get the maximum allocated pages (Physical or Virtual).

These variables can help you get precise statistics about memory allocations.

  • MmAvailablePages: Total number of available pages on the system the sum of the pages on the zeroed, free, and standby lists
  • MmResidentAvailablePages: Total number of physical pages that would be available if every process were at its minimum working set size
  • MmNumberOfPhysicalPagesTotal number of physical pages available on the system

PFN Data Structures

This section is derived from here, which worth reading:

Free, Zero and Bad lists

We start off by discussing these states - they are the simplest to understand. Pages which can take on any of these states (A flag in the _MMPTE.u3.e1.PageLocation) are kept in their own lists of Free pages (ready to be used), Zero pages (already cleared) or Bad pages (will never be used).

Active Pages: PteAddress points at a hardware PTE.

If the PFN Type is set to Active, then the physical page is used by something. The most important thing to realize is that a valid physical page (frame) must be managed by a PTE.  Since that PTE record must also be accessible to the kernel, it must be mapped in the kernel’s virtual address space.

When the PFN is Active, it contains 3 important pieces of information:

  1. The virtual address of the PTE that is managing this physical page (in _MMPFN.PteAddress).

  2. The Page Frame (Physical page number) of the PTE that is managing this physical page (in _MMPFN.u4.PteFrame). Note these two values provide the virtual and physical address of the PTE.

  3. The OriginalPte value (usually the prototype PTE which controls this page). When Windows installs a hardware PTE from a prototype PTE, it will copy the original prototype PTE into this field.

If you want to know about prototype PTE, then there is a good article here.

From the first line, you should understand how to change page attributes for your physical memory.

Let’s see…

Convert page pfn to pte

The Page Table Entry should be converted to binary to see its attributes. First I get the PTE Adress values and then convert it to binary format using .formats.

Note that I choose the first one because its Physical-Page Base Address is equal to 2012 (our PFN Number), the lowest 12 bits are used for attributes while bits 12 to 52 are used to show Physical Address.

0x02012963 —–>  10000000010010(Used for Physical Address),100101100011(used for attributes.)

10000000010010 —–> 0x2012

The following image shows its bits position and meaning. (The last one is PTE.)

Paging Tables

Windbg !vm extension

As I mentioned in the previous part, there is also another extension, called !vm.

The Windbg documentation says:

!vm
The !vm extension displays summary information about virtual memory use statistics on the target system.

!vm [Flags]
Parameters

Flags
Specifies what information will be displayed in the output from this command. This can be any sum of the following bits. The default is 0, which causes the display to include system-wide virtual memory statistics as well as memory statistics for each process.

Bit 0 (0x1)
Causes the display to omit process-specific statistics.

Bit 1 (0x2)
Causes the display to include memory management thread stacks.

Bit 2 (0x4)
(Windows XP and later) Causes the display to include terminal server memory usage.

Bit 3 (0x8)
(Windows XP and later) Causes the display to include the page file write log.

Bit 4 (0x10)
(Windows XP and later) Causes the display to include working set owner thread stacks.

Bit 5 (0x20)
(Windows Vista and later) Causes the display to include kernel virtual address usage.

The above extension can be used to get some statistics about memory allocation and the most important field for us is PFN Array Commit.

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
+
1: kd> !vm
+Page File: \??\C:\pagefile.sys
+  Current:   1179648 Kb  Free Space:   1173572 Kb
+  Minimum:   1179648 Kb  Maximum:      6291456 Kb
+Page File: \??\C:\swapfile.sys
+  Current:     16384 Kb  Free Space:     16376 Kb
+  Minimum:     16384 Kb  Maximum:      3144940 Kb
+No Name for Paging File
+  Current:   8388084 Kb  Free Space:   8032544 Kb
+  Minimum:   8388084 Kb  Maximum:      8388084 Kb
+
+Physical Memory:           524157 (    2096628 Kb)
+Available Pages:           313062 (    1252248 Kb)
+ResAvail Pages:            451681 (    1806724 Kb)
+Locked IO Pages:                0 (          0 Kb)
+Free System PTEs:      4294977640 (17179910560 Kb)
+Modified Pages:             11735 (      46940 Kb)
+Modified PF Pages:          11693 (      46772 Kb)
+Modified No Write Pages:        0 (          0 Kb)
+NonPagedPool Usage:             0 (          0 Kb)
+NonPagedPoolNx Usage:       14108 (      56432 Kb)
+NonPagedPool Max:      4294967296 (17179869184 Kb)
+PagedPool  0:               27632 (     110528 Kb)
+PagedPool  1:                2344 (       9376 Kb)
+PagedPool  2:                2402 (       9608 Kb)
+PagedPool  3:                2393 (       9572 Kb)
+PagedPool  4:                2308 (       9232 Kb)
+PagedPool Usage:            37079 (     148316 Kb)
+PagedPool Maximum:     4294967296 (17179869184 Kb)
+Processor Commit:             728 (       2912 Kb)
+Session Commit:              2947 (      11788 Kb)
+Shared Commit:              24440 (      97760 Kb)
+Special Pool:                   0 (          0 Kb)
+Kernel Stacks:               6825 (      27300 Kb)
+Pages For MDLs:              1378 (       5512 Kb)
+Pages For AWE:                  0 (          0 Kb)
+NonPagedPool Commit:        12147 (      48588 Kb)
+PagedPool Commit:           37079 (     148316 Kb)
+Driver Commit:              10370 (      41480 Kb)
+Boot Commit:                 2167 (       8668 Kb)
+PFN Array Commit:            6687 (      26748 Kb)
+System PageTables:            446 (       1784 Kb)
+ProcessLockedFilePages:        15 (         60 Kb)
+Pagefile Hash Pages:            3 (         12 Kb)
+Sum System Commit:         105232 (     420928 Kb)
+Total Private:             161822 (     647288 Kb)
+Misc/Transient Commit:       2124 (       8496 Kb)
+Committed pages:           269178 (    1076712 Kb)
+Commit limit:              819069 (    3276276 Kb)
+
+  Pid ImageName                        Commit   SharedCommit        Debt
+
+  81c MsMpEng.exe                   112764 Kb        7400 Kb        0 Kb
+...
+  f10 userinit.exe                      72 Kb           0 Kb        0 Kb
+  830 coredpussvr.exe                   72 Kb           0 Kb        0 Kb
+  1f0 smss.exe                          72 Kb           0 Kb        0 Kb
+

That’s it guys, hope you enjoy reading this topic. If you have any question, then you can use the comments.

Have fun surveying Windows!

References

[Inside Windows Page Frame Number (PFN) – Part 1] (https://rayanfam.com/topics/inside-windows-page-frame-number-part1/)  

[Memory Manager Routines] (https://msdn.microsoft.com/en-us/library/windows/hardware/ff554435(v=vs.85).aspx)  

[Page Frame Number Database] (https://flylib.com/books/en/4.491.1.69/1/)

[Rekall and the windows PFN database] (http://blog.rekall-forensic.com/2016/05/rekall-and-windows-pfn-database.html)

[Prototype PTEs] (https://www.codemachine.com/article_protopte.html)

This post is licensed under CC BY 4.0 by the author.

Cisco switch security features cheatsheet

x64 Inline Assembly in Windows Driver Kit

Comments powered by Disqus.

diff --git a/topics/intel-vanderpool-initiative-slides/index.html b/topics/intel-vanderpool-initiative-slides/index.html new file mode 100644 index 00000000..61cbce61 --- /dev/null +++ b/topics/intel-vanderpool-initiative-slides/index.html @@ -0,0 +1 @@ + A first look at some aspects of Intel's "Vanderpool" initiative | Rayanfam Blog
Home A first look at some aspects of Intel's "Vanderpool" initiative
Post
Cancel

A first look at some aspects of Intel's "Vanderpool" initiative

A few hours ago, I was working on Intel VT-x which enables a hardware support for virtualization then I saw the following slides which gives me lots of information about Hypervisor instructions,VMM, Virtual machine control structure (VMCS) and other practical information.

I don’t know about its author actually but I should give my thanks to him/her for gathering this slides.

I think it worth a lot to read ,it can be downloaded here .

This post is licensed under CC BY 4.0 by the author.

Build a Simple Pin Tool

Assembly Challenge : Jump to a non-relative address without using registers

Comments powered by Disqus.

diff --git a/topics/introduction-to-systemd-basic-usage-and-concepts/index.html b/topics/introduction-to-systemd-basic-usage-and-concepts/index.html new file mode 100644 index 00000000..e9eda4ea --- /dev/null +++ b/topics/introduction-to-systemd-basic-usage-and-concepts/index.html @@ -0,0 +1,81 @@ + Introduction to systemd : Basic Usage and Concepts | Rayanfam Blog
Home Introduction to systemd : Basic Usage and Concepts
Post
Cancel

Introduction to systemd : Basic Usage and Concepts

Hi everyone

In this post I am going to explain some essential systemd commands and concepts. As systemd popularity grew much more and changed the linux ecosystem drastically, every sysadmin, DevOps or every casual linux user should know the basics of this init system (It’s really a load of other things too!) and be able to use systemctl, journalctl, etc in order to leverage its power.

For example, by using systemd unit files you can completely get rid of using crontab as systemd replaces this functionality, you can also run the service as a temporary user or even activate a service when a connection is made to a specific port on server. as you can see, the possibilities are endless. Some of them will hopefully be described in further posts and links provided at the end of this post.

 

systemd logo


Concepts

Unit Files

Most systemd configuration take place in unit files. unit files are short configuration snippets that control behavior of systmed. Unit files are of different types which will be described below:

Service File

A service unit file is configuration file that shows how you like to run a specific process, like a web server application, a docker container, etc. It can be anything, even your own application.

Target File

A target unit file is mechanism used for grouping different services and starting them at the same time or in another desired fashion.

Timer units

Timer units are used to run services at specific times, like crontab.

Path units

Watches a path and responds based on defined actions.

Slice Units

Slice units are used for resource management of other units. Units are assigned to a slice which controls their use of resources. By default units are assigned to a default slice. which is “system.slice” for services and scope units, “user.slice” for user sessions and “machine.slice” for containers registered with systemd-machined.

Socket Units

Socket units are used to activate another service on request to a socket. It facilitates on-demand service activation. You can setup a server which listen for ssh connection and creates a container for each user which connects to the server and connects the user to the container. only using systemd! your power is limited by your imagination :D

Device Unit

systemd wrapper for udev devices.

Mount Units

Mount units are simply used to mount a filesystem automatically (or manually).


A sample service unit file (OpenSSH on debian jessie):

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
[Unit]
+Description=OpenBSD Secure Shell server
+After=network.target auditd.service
+ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
+
+[Service]
+EnvironmentFile=-/etc/default/ssh
+ExecStartPre=/usr/sbin/sshd -t
+ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
+ExecReload=/usr/sbin/sshd -t
+ExecReload=/bin/kill -HUP $MAINPID
+KillMode=process
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
+Alias=sshd.service
+

It is rather self-explanatory so after another example (a unit file for a docker container from CoreOS):

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+
[Unit]
+Description=MyApp
+After=docker.service
+Requires=docker.service
+
+[Service]
+TimeoutStartSec=0
+ExecStartPre=-/usr/bin/docker kill busybox1
+ExecStartPre=-/usr/bin/docker rm busybox1
+ExecStartPre=/usr/bin/docker pull busybox
+ExecStart=/usr/bin/docker run --name busybox1 busybox /bin/sh -c "trap 'exit 0' INT TERM; while true; do echo Hello World; sleep 1; done"
+
+[Install]
+WantedBy=multi-user.target
+
1
+2
+
$ sudo systemctl enable /etc/systemd/system/hello.service
+$ sudo systemctl start hello.service
+

 


systemd commands

systemctl

The most import command is probably systemctl. It is used for starting/stopping services. Here’s a good table from HighOnCoffee:

systemctl stop service-namesystemd stop running service
systemctl start service-namesystemctl start service
systemctl restart service-namesystemd restart running service
systemctl reload service-namereloads all config files for service
systemctl status service-namesystemctl show if service is running
systemctl enable service-namesystemctl start service at boot
systemctrl disable service-namesystemctl - disable service at boot
systemctl show service-nameshow systemctl service info

List service dependencies with this command:

1
+
# systemctl list-dependencies sshd.service
+

 

Change ad-hoc runlevel with systemctl isolate command. Boot targets are somehow equivalent to SysV init runlevels:

  • Switch to another target (in this case multi-user/runlevel 3 in old SysV):
1
+
systemctl isolate multi-user.target
+
  • Switch to graphical target (in this case graphical/runlevel 5 in old SysV):
1
+
systemctl isolate graphical.target
+

journalctl

View all log entries starting from this boot:

1
+
$ journalctl -b
+

view only kernel messages from this boot:

1
+
$ journalctl -k -b
+

using -x flag attaches some additional data to the logs, -n can get an integer and show this much lines (default 10):

1
+
$ journalctl -xn
+

view all logs from a specific service:

1
+
$ journalctl -b -e -u nginx.service
+

That’s All folks!

systemd has sooo many features (and of course so much criticism and debate!). I will try to cover more advanced features of systemd in following posts on this blog. Here are some links that will be useful for you:

https://access.redhat.com/articles/systemd-cheat-sheet

https://gist.github.com/mbodo/8f87c96ce11e91f80fbf6175412a2206

https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units

https://wiki.archlinux.org/index.php/Systemd

and this series of articles by Lennart Poettering (core systemd developer!):

This post is licensed under CC BY 4.0 by the author.

Useful Configs for NGINX

A partial survey among non-general purpose registers

Comments powered by Disqus.

diff --git a/topics/kernel-mode-debugging-by-windbg/index.html b/topics/kernel-mode-debugging-by-windbg/index.html new file mode 100644 index 00000000..084a3cbf --- /dev/null +++ b/topics/kernel-mode-debugging-by-windbg/index.html @@ -0,0 +1 @@ + Kernel Mode Debugging by Windbg | Rayanfam Blog
Home Kernel Mode Debugging by Windbg
Post
Cancel

Kernel Mode Debugging by Windbg

Hey there,

Today I’m gonna show you how to make a kernel mode debugging using VMWare and Windbg and Windows.

So why should you do this ?!

It’s clear , everything such as Kernel Mode Driver Debugging , searching for zero days and understanding windows mechanism.

There are other types of kernel debugging as described in Windows Internals by Mark Russinovich that I describe in future posts.

So let’s start.

First you need a Windbg and as I’m working in a x64 version of Windows so I use AMD64 version of Windbg. If you don’t know how to download and install windbg just google it :)

let’s get down to business. First Start VMWare and open the OS that you want to debug.

(Please note : in kernel mode debugging all U need is host machine that debugs a target machine)

Then go to edit virtual machine and click to add new hardware.In this time click next and in the new window choose the “Output to named pipe” and click next.

In the new window choose a name for your new serial port.This name will be used in Windbg.

Now start the virtual machine and after starting you should make windows into debugging mode.

There are several ways to do this and have same affects , In this case I started Run (Win Key + R) and go to msconfig.

In the msconfig choose Boot from tabs and then click on Advanced Option and check Debug checkbox and Debug port and set debug port to your serial port number (In this case COM2).

Note : It might be different in your computer so please check VMWare hardwares before set it.

After applying the above settings, Windows asks if you want to restart computer for your actions to take place.In this time leave Windows alone and Start Windbg based on your operating system CPU.

In Windbg go to File > Kernel Debug…

Now go to COM tab and set your serial port name (From what you choose in VMWare.)

then click OK and restart the VMWare.

After restarting Windows you should see something like this :

Then your’re done …

Now you can stop Windbg and see how it works like a charm.

Please remember if you stop Windbg and make breakpoint, the target windows will stop.

After doing this you can debug your Windows in Kernel Mode.

This post is licensed under CC BY 4.0 by the author.

Hello World !

A New Anti Ransomware Idea

Comments powered by Disqus.

diff --git a/topics/lack-of-rechecking-permissions-in-android/index.html b/topics/lack-of-rechecking-permissions-in-android/index.html new file mode 100644 index 00000000..1a3e85a4 --- /dev/null +++ b/topics/lack-of-rechecking-permissions-in-android/index.html @@ -0,0 +1,5 @@ + Lack of rechecking permissions in Android | Rayanfam Blog
Home Lack of rechecking permissions in Android
Post
Cancel

Lack of rechecking permissions in Android

Yesterday me and one of my friends were working on an Android Penetration testing project. After testing some kinds of exploit then we somehow get root privilege with some kinds of limitations. In the case of this exploit we can just write to any file we want and we cannot do anything more because ASLR was preventing us to do.

So we just think how we could do something to violate privacy of this Android device then as we know previously, Android devices has some kinds of files that save applications signatures and package names and permissions and this file is placed in :

1
+
/data/system/packages.xml
+

Another juicy file which holds what kinds of groups has access to what kinds of devices is also available in :

1
+
/etc/permissions/platform.xml
+

Then we start to edit the first file to see if it is possible to change the permissions of an special package or not, so we do like this :

And then find something like this :

But in the real case we just edit our application’s permissions and add another permissions to it, then restart the phone and then opened our previous application but this time with new permissions ! Unfortunately it works and has access to this new permissions and I wonder how google didn’t check permissions again ?! They store lots of signatures and use a huge number of cryptography algorithms to check integrity of files but doesn’t recheck this important stuffs. It also works on /etc/permissions/platform.xml, this file has the same affect somehow but it is not as important as previous file because this is some kind of OS settings file but packages.xml holds settings for every applications.

Note : we test this commands in Android 5.1.0 but I think google has no plan or doesn’t have any plan to add a new approach for checking permissions validation on newer versions of Android so it should work on newer versions too but not tested.

The need to check permissions again is theoretically needless because no one can access to edit this files but I think it should be done by google because in the case of exploits it can have bad affects in people’s privacy.

That’s it guys.

This post is licensed under CC BY 4.0 by the author.

Import Address Table (IAT) in action

Defeat Malware's Dynamic API Loading

Comments powered by Disqus.

diff --git a/topics/mount-in-linux/index.html b/topics/mount-in-linux/index.html new file mode 100644 index 00000000..84368682 --- /dev/null +++ b/topics/mount-in-linux/index.html @@ -0,0 +1,409 @@ + A Tour of Mount in Linux | Rayanfam Blog
Home A Tour of Mount in Linux
Post
Cancel

A Tour of Mount in Linux

I had windows 10 installed on my laptop because of serious incompatibility of ROG laptops with Linux and my desire to play some games after years of living in bash! This continued for a year and I really like windows 10 cool features like WSL and Hyper-V (with Docker!), however being in love with rolling-release distros and being fed up with windows in the past year. And the fact that I was yearning for terminal, I installed Solus OS earlier this week on a separate partition without removing windows.

And now we get to the point which I wanted to listen to some music which resided on another ntfs volume. And trying to automatically mount the drive with Solus, It occurred to me that how much has really changed in hardware representation in Linux in general. Then it became to write this post about mounting in Linux and new tools for it.

A little background

when you have a storage device (like a hard disk) attached to your computer, Linux kernel, being in charge of talking to hardware, detects the device and creates a file representing the device in /dev like /dev/sda. For each partition that is detected from the device’s partition table, another file is created like /dev/sda1

Some files are also created in sysfs (/sys) in newer kernels and expose more advanced control and info to the user. (which are not talked about in this post)

For actually being able to read/write into a device, you should mount it first. The mount command mounts a storage device or filesystem, making it accessible and attaching it to an existing directory structure.

in order to mount a volume you should invoke the mount command. First, you will create a directory (should be empty)

1
+
# mkdir /mnt/mydata
+

Then you mount the volume to that:

1
+
# mount /dev/sda1 /mnt/mydata
+

To see all mounted volumes and their options you can use mount command or findmnt (both are provided by util-linux and are available in all distros):

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
+
$ findmnt
+
+TARGET                                SOURCE                 FSTYPE          OPTIONS
+
+/                                     /dev/sda4              ext4            rw,relatime,errors=remount-ro
+├─/sys                                sysfs                  sysfs           rw,nosuid,nodev,noexec,relatime
+ ├─/sys/kernel/security              securityfs             securityfs      rw,nosuid,nodev,noexec,relatime
+ ├─/sys/fs/cgroup                    tmpfs                  tmpfs           ro,nosuid,nodev,noexec,mode=755
+  ├─/sys/fs/cgroup/unified          cgroup2                cgroup2         rw,nosuid,nodev,noexec,relatime,nsdelegate
+  ├─/sys/fs/cgroup/systemd          cgroup                 cgroup          rw,nosuid,nodev,noexec,relatime,xattr,name=systemd
+  ├─/sys/fs/cgroup/net_cls,net_prio cgroup                 cgroup          rw,nosuid,nodev,noexec,relatime,net_cls,net_prio
+  ├─/sys/fs/cgroup/perf_event       cgroup                 cgroup          rw,nosuid,nodev,noexec,relatime,perf_event
+  ├─/sys/fs/cgroup/pids             cgroup                 cgroup          rw,nosuid,nodev,noexec,relatime,pids
+  ├─/sys/fs/cgroup/cpu,cpuacct      cgroup                 cgroup          rw,nosuid,nodev,noexec,relatime,cpu,cpuacct
+  ├─/sys/fs/cgroup/blkio            cgroup                 cgroup          rw,nosuid,nodev,noexec,relatime,blkio
+  ├─/sys/fs/cgroup/cpuset           cgroup                 cgroup          rw,nosuid,nodev,noexec,relatime,cpuset
+  ├─/sys/fs/cgroup/freezer          cgroup                 cgroup          rw,nosuid,nodev,noexec,relatime,freezer
+  ├─/sys/fs/cgroup/devices          cgroup                 cgroup          rw,nosuid,nodev,noexec,relatime,devices
+  └─/sys/fs/cgroup/memory           cgroup                 cgroup          rw,nosuid,nodev,noexec,relatime,memory
+ ├─/sys/firmware/efi/efivars         efivarfs               efivarfs        rw,nosuid,nodev,noexec,relatime
+ ├─/sys/fs/bpf                       bpf                    bpf             rw,nosuid,nodev,noexec,relatime,mode=700
+ ├─/sys/kernel/debug                 debugfs                debugfs         rw,relatime
+ └─/sys/fs/fuse/connections          fusectl                fusectl         rw,relatime
+├─/proc                               proc                   proc            rw,nosuid,noexec,relatime
+ └─/proc/sys/fs/binfmt_misc          systemd-1              autofs          rw,relatime,fd=36,pgrp=1,timeout=0,minproto=5,maxproto=5,direct
+├─/dev                                devtmpfs               devtmpfs        rw,nosuid,size=8144172k,nr_inodes=2036043,mode=755
+ ├─/dev/shm                          tmpfs                  tmpfs           rw
+  └─/dev/shm                        none                   tmpfs           rw,relatime
+ ├─/dev/pts                          devpts                 devpts          rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000
+ ├─/dev/mqueue                       mqueue                 mqueue          rw,relatime
+ └─/dev/hugepages                    hugetlbfs              hugetlbfs       rw,relatime,pagesize=2M
+├─/run                                tmpfs                  tmpfs           rw,nosuid,nodev,mode=755
+ ├─/run/media/shahriar/Data              /dev/sda2              fuseblk         rw,nosuid,nodev,relatime,user_id=0,group_id=0,default_permissions,allow_other,blksize=4096
+ ├─/run/user/1000                    tmpfs                  tmpfs           rw,nosuid,nodev,relatime,size=1638552k,mode=700,uid=1000,gid=1000
+  └─/run/user/1000/gvfs             gvfsd-fuse             fuse.gvfsd-fuse rw,nosuid,nodev,relatime,user_id=1000,group_id=1000
+ └─/run/snapd/ns                     tmpfs[/snapd/ns]       tmpfs           rw,nosuid,nodev,mode=755
+   └─/run/snapd/ns/chromium.mnt      nsfs[mnt:[4026532495]] nsfs            rw
+├─/tmp                                tmpfs                  tmpfs           rw,nosuid,nodev
+├─/snap/slack/7                       /dev/loop0             squashfs        ro,nodev,relatime
+├─/snap/sublime-text/26               /dev/loop1             squashfs        ro,nodev,relatime
+├─/snap/core/5145                     /dev/loop2             squashfs        ro,nodev,relatime
+├─/snap/chromium/412                  /dev/loop4             squashfs        ro,nodev,relatime
+├─/snap/core/5328                     /dev/loop3             squashfs        ro,nodev,relatime
+└─/snap/telegram-desktop/270          /dev/loop5             squashfs        ro,nodev,relatime
+
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
+
$ mount
+sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
+proc on /proc type proc (rw,nosuid,noexec,relatime)
+devtmpfs on /dev type devtmpfs (rw,nosuid,size=8144172k,nr_inodes=2036043,mode=755)
+securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)
+tmpfs on /dev/shm type tmpfs (rw)
+devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)
+tmpfs on /run type tmpfs (rw,nosuid,nodev,mode=755)
+tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
+cgroup2 on /sys/fs/cgroup/unified type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate)
+cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)
+efivarfs on /sys/firmware/efi/efivars type efivarfs (rw,nosuid,nodev,noexec,relatime)
+bpf on /sys/fs/bpf type bpf (rw,nosuid,nodev,noexec,relatime,mode=700)
+cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
+cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
+cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
+cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
+cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
+cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
+cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
+cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
+cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
+/dev/sda4 on / type ext4 (rw,relatime,errors=remount-ro)
+mqueue on /dev/mqueue type mqueue (rw,relatime)
+hugetlbfs on /dev/hugepages type hugetlbfs (rw,relatime,pagesize=2M)
+systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=36,pgrp=1,timeout=0,minproto=5,maxproto=5,direct)
+debugfs on /sys/kernel/debug type debugfs (rw,relatime)
+tmpfs on /tmp type tmpfs (rw,nosuid,nodev)
+/var/lib/snapd/snaps/sublime-text_26.snap on /snap/sublime-text/26 type squashfs (ro,nodev,relatime,x-gdu.hide)
+/var/lib/snapd/snaps/slack_7.snap on /snap/slack/7 type squashfs (ro,nodev,relatime,x-gdu.hide)
+/var/lib/snapd/snaps/core_5145.snap on /snap/core/5145 type squashfs (ro,nodev,relatime,x-gdu.hide)
+/var/lib/snapd/snaps/chromium_412.snap on /snap/chromium/412 type squashfs (ro,nodev,relatime,x-gdu.hide)
+/var/lib/snapd/snaps/core_5328.snap on /snap/core/5328 type squashfs (ro,nodev,relatime,x-gdu.hide)
+/var/lib/snapd/snaps/telegram-desktop_270.snap on /snap/telegram-desktop/270 type squashfs (ro,nodev,relatime,x-gdu.hide)
+fusectl on /sys/fs/fuse/connections type fusectl (rw,relatime)
+tmpfs on /run/user/1000 type tmpfs (rw,nosuid,nodev,relatime,size=1638552k,mode=700,uid=1000,gid=1000)
+gvfsd-fuse on /run/user/1000/gvfs type fuse.gvfsd-fuse (rw,nosuid,nodev,relatime,user_id=1000,group_id=1000)
+tmpfs on /run/snapd/ns type tmpfs (rw,nosuid,nodev,mode=755)
+nsfs on /run/snapd/ns/chromium.mnt type nsfs (rw)
+none on /dev/shm type tmpfs (rw,relatime)
+/dev/sda2 on /run/media/shahriar/Data type fuseblk (rw,nosuid,nodev,relatime,user_id=0,group_id=0,default_permissions,allow_other,blksize=4096,uhelper=udisks2)
+

For unmounting the device you should use umount:

either the device containing the file system or the mount point can be specified.

1
+2
+3
+
# umount /dev/sda1
+
+# umount /mnt/mydata
+

A word about permissions

All device files created in /dev are only accessible to root. Therefore in a general sense, only root can mount stuff. This is very important and you need to keep that in mind. If a filesystem has Linux permission support (linux native filesystems like ext2, ext3, ext4, btrfs, xfs, etc), they are leveraged and files on the drive are treated like other files in your linux system. But some filesystems like FAT derivatives (FAT32, exFAT, etc) are not capable of storing permission information. So by default they become only available too root unless you specify some options at mount time to allow other users. consider this example:

1
+2
+3
+
replace xxx with uid/gid of user
+
+# mount -t vfat /dev/sda6 /media/FAT32 -o rw,uid=xxx,gid=xxx
+

also this neat one-liner which mounts them accessible to your user:

1
+
$ sudo mount -t vfat /dev/sda6 /media/FAT32 -o rw,uid=$(id -u),gid=$(id -g)
+

or all users…

1
+
# mount -t vfat  /dev/sda6 /media/FAT32 -o rw,umask=0000
+

Filesystems

A filesystem controls how data is stored and retrieved. usually necessary options and filesystem are detected by the mount command, but you can specify them manually like this:

1
+
# mount -t ntfs /dev/sda1 /mnt/mydata
+

You can view all filesystems supported by your kernel by reading this file:

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
+
$ cat /proc/filesystems
+
+nodev  sysfs
+nodev  rootfs
+nodev  ramfs
+nodev  bdev
+nodev  proc
+nodev  cpuset
+nodev  cgroup
+nodev  cgroup2
+nodev  tmpfs
+nodev  devtmpfs
+nodev  debugfs
+nodev  tracefs
+nodev  securityfs
+nodev  sockfs
+nodev  dax
+nodev  bpf
+nodev  pipefs
+nodev  hugetlbfs
+nodev  devpts
+            ext3
+            ext4
+            iso9660
+nodev  autofs
+            xfs
+nodev  efivarfs
+nodev  mqueue
+            btrfs
+            squashfs
+            fuseblk
+nodev  fuse
+nodev  fusectl
+nodev  overlay
+

The first column signifies whether the file system is mounted on a block device. Those beginning with nodev are not mounted on a device. The second column lists the names of the file systems supported.

The mount command cycles through the file systems listed here when one is not specified as an argument.

To see existing filesystems on drives use lsblk command like this:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
$ lsblk -f
+
+NAME   FSTYPE   LABEL    UUID                                 MOUNTPOINT
+loop0  squashfs                                               /snap/slack/7
+loop1  squashfs                                               /snap/sublime-text/26
+loop2  squashfs                                               /snap/core/5145
+loop3  squashfs                                               /snap/core/5328
+loop4  squashfs                                               /snap/chromium/412
+loop5  squashfs                                               /snap/telegram-desktop/270
+sda                                                                
+├─sda1                                                       
+├─sda2 ntfs     Data     DCDE47AADE477C30                     /run/media/shahriar/Data
+├─sda3 swap              9269aa88-3a31-4299-a1ef-f1472750717f [SWAP]
+└─sda4 ext4              7fe5feba-8c3d-4fe9-ab0a-20776aabf441 /
+sdb                                                          
+├─sdb1 ntfs     Recovery 5436D56136D544A0                     
+├─sdb2 vfat              30D6-1226                           
+├─sdb3                                                       
+└─sdb4 ntfs              560A18780A1856F9                    
+sr0                                                           
+

(loop devices are apparently being used by snapd. I will write about it in another article, it’s very cool!)

As I said earlier these are filesystem types supported by the kernel, you might ask so where else can a filesystem be implemented? And the answer is FUSE.

Filesystem in Userspace (FUSE) is a mechanism for Unix-like operating systems that lets non-privileged users create their own file systems without editing kernel code. This is achieved by running file system code in user space, while the FUSE kernel module provides only a “bridge” to the actual kernel interfaces.

However you should know that reading/writing on a FUSE filesystem involves a lot more overhead because of code providing filesystem functionality being in userland and each call to a function for that matter will traverse kernel and userland a couple of times more than a regular file operation involving a driver in kernel. Nonetheless it is a very useful feature in a lot of scenarios. (like mounting writable ntfs or a filesystem over the network)

There are also a myriad of other filesystems in the Linux world, such as overlay filesystems , so I suggest you to read references at the end of the post for further reading.

Once we know the basics of mounting in linux, we are going to learn how to automate that and what happens when Linux is booted regarding mounts.

fstab

The fstab file can be used to define how disk partitions, various other block devices, or remote filesystems should be mounted into the filesystem.

Each filesystem is described on a separate line. Fields on each line are separated by tabs or spaces. Lines starting with ‘#’ are comments. Blank lines are ignored.

The mount command will use fstab, if just one of either directory or device is given, to fill in the value for the other parameter. When doing so, mount options which are listed in fstab will also be used. (user option can be set in fstab to allow non-root users to mount)

All specified devices within /etc/fstab will be automatically mounted on startup and when the -a flag is used with mount, unless the noauto option is specified. Devices that are listed and not present will result in an error unless the nofail option is used.

1
+2
+3
+4
+5
+6
+
$ cat /etc/fstab
+
+# <device>             <dir>         <type>    <options>             <dump> <fsck>
+/dev/sda1              /             ext4      noatime               0      1
+/dev/sda2              none          swap      defaults              0      0
+/dev/sda3              /home         ext4      noatime               0      2
+

It is very recommended to use UUID or other unique identifiers instead of relying on kernel name descriptors (sda1, sdb2, …) as they may change after reboot!

UUID is the preferred method. You can find out the UUID with lsblk -f

1
+2
+3
+4
+5
+6
+7
+
$ cat /etc/fstab
+
+# <device>                                <dir> <type> <options>                                                                                            <dump> <fsck>
+UUID=CBB6-24F2                            /boot vfat   defaults                                                                                             0      2
+UUID=0a3407de-014b-458b-b5c1-848e92a327a3 /     ext4   defaults                                                                                             0      1
+UUID=b411dc99-f0a0-4c87-9e05-184977be8539 /home ext4   defaults                                                                                             0      2
+UUID=f9fe0b69-a280-415d-a03a-a32752370dee none  swap   defaults                                                                                             0      0
+

Modernizing mounting

In the early 2000s, the first attempt to cope with this was called HAL - Hardware Abstraction Layer, which did what it said on the tin, provided a layer between device nodes and the user of the nodes, so storage devices (and other hardware) can be added and removed without rebooting this system and without rewriting the /etc/fstab file.

After deprecation of HAL, everything gets replaced a dizzying number of times (DeviceKit, devfs, etc) as better approaches are discovered in a fast moving period of hardware development, udev eventually won and was the main thing for the rest of the decade.

When a device is added or removed from the system, the Linux kernel notices and sends out an event. udev is a daemon that waits around listening for these events and then responding accordingly. udev runs in user space not kernel space.

Udisks

Building on top of new technologies (D-Bus, udev), udisks was created. It has modernized the mounting on userland.

udisks provides:

  • a daemon, udisksd, that implements well-defined D-Bus interfaces that can be used to query and manipulate storage devices.
  • a command-line tool, udisksctl, that can be used to query and use the daemon The actions that a user can perform using udisks are restricted using polkit.

It is the tool that handles all the work and permission checking when you simply click on the fancy usb flash drive icon that has appeared on your desktop after inserting it into your PC.

We will cover the use of udisksctl for basic mounting/unmounting in this post. But it is important to know that all functionality provided here is just a wrapper around D-Bus calls which is easily programmable will be very useful in storage management automation. (like the clicking mentioned, etc)

To see a list of disks attached to the system (serial numbers are replaced):

1
+2
+3
+4
+5
+6
+7
+
$ udisksctl status
+
+MODEL                     REVISION  SERIAL               DEVICE
+--------------------------------------------------------------------------
+Samsung SSD 860 EVO 500GB XXXXXXX  XXXXXXXXXXX      sda    
+SanDisk SD8S   XXXXXXX  XXXXXXXXXXX         sdb    
+SlimtypeDVD A    XXXXXXX      XXXXXXXXXXX sr0    
+

To see detailed info about disk:

1
+2
+
$ udiskctl dump
+<output not shown due to length>
+

To actually mount a filesystem using this new tool:

1
+
udisksctl mount -b /dev/sdb1
+

And then to unmount:

1
+
udisksctl unmount -b /dev/sdb1
+

systemd mount units

Considering the merger of udev and systemd, and the ubiquity of systemd in modern Linux distros. It is recommended to ditch old-school habits and start having fun with cool new functionality made available to use.

Adding entries in fstab is no longer the main way to mount a device at startup. In fact all entries in /etc/fstab are converted to systemd mount units at boot.

Example systemd .mount file:

1
+2
+3
+4
+5
+
[Mount]
+What=/dev/disk/by-uuid/9269aa88-3a31-4299-bbb1-4e528a89d222
+Where=/mnt/mydata
+Type=ext4
+Options=defaults
+

Important: Mount units must be named after the mount point directories they control.

Example: the mount point /home/lennart must be configured in a unit file home-lennart.mount.

So we create a file like this:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
# vim /etc/systemd/system/mnt-mydata.mount
+
+[Unit]
+Description=Mount Some of my files to empty mydata dir
+
+[Mount]
+What=/dev/disk/by-uuid/9269aa88-3a31-4299-bbb1-4e528a89d222
+Where=/mnt/mydata
+Type=ext4
+Options=defaults
+

Of course you should signal systemd to load the unit file after you’re done editing:

1
+2
+
# systemctl daemon-reload
+# systemctl start mnt-mydata.mount
+

You can view state of the mount like other units:

1
+
# systemctl status mnt-mydata.mount
+

Important: If you want it to be mounted on each boot, you should also include an [Install] section in the unit file__:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
[Unit]
+Description=Mount Some of my files to empty mydata dir
+
+[Mount]
+What=/dev/disk/by-uuid/9269aa88-3a31-4299-bbb1-4e528a89d222
+Where=/mnt/mydata
+Type=ext4
+Options=defaults
+ 
+[Install]
+WantedBy=multi-user.target
+

And the enable the unit to be started at boot:

1
+
# systemctl enable mnt-backups.mount
+

I hope you liked this post. There are still things that were left out of this post about mounting in Linux. But I leave them up to you to explore if you are interested. Overlay filesystems, FUSE and AutoFS are useful and are recommended for reading.

References

This post is licensed under CC BY 4.0 by the author.

Hypervisor From Scratch - Part 1: Basic Concepts & Configure Testing Environment

Hypervisor From Scratch – Part 2: Entering VMX Operation

Comments powered by Disqus.

diff --git a/topics/packetscript-lua-kernel/index.html b/topics/packetscript-lua-kernel/index.html new file mode 100644 index 00000000..002ff439 --- /dev/null +++ b/topics/packetscript-lua-kernel/index.html @@ -0,0 +1,87 @@ + PacketScript overview: A Lua scripting engine for in-kernel packet processing | Rayanfam Blog
Home PacketScript overview: A Lua scripting engine for in-kernel packet processing
Post
Cancel

PacketScript overview: A Lua scripting engine for in-kernel packet processing

As I was surfing the net, trying to find a way to prototype network protocols or features in Linux. I stumbled upon PacketScript.

PacketScript is the an implementation of the Lua VM inside Linux kernel. Such implementations aren’t new ,luak and lunatik have been existed for some time. However what makes PacketScript different is the ability to mangle network packets with Lua. Not just running Lua code in kernel. as a matter of fact PacketScript uses lunatik underneath as its Lua in kernel engine.

PacketScript is built on existing technologies, ensuring more future maintainability. It is built on iptables infrastructure and on existing xtable-addons platform. Using xtables-addons makes PacketScript needless of kernel patching and compiling. You simply need to install the kernel module. xtables-addons also provides help for adding features into iptables command line interface.

PacketScript was work of André Graf as his master thesis in University of Basel. Since its original publication of thesis and source code, It has gone unmaintained since. It is currently working and I’m not aware of any bugs but the lack of maintenance may make this project unsuitable for production usage (unless forked and maintained by yourself).

Note that PacketScript compiles on linux kernel 2.x. (unless you apply the patch)

Being very disappointed from the kernel version supported by PacketScript, I found a patch by OpenWrt team who has ported PacketScript to 4.x kernels! The patch is for OpenWrt but you can easily apply it to the source and build on any other distro. (I am using PacketScript on OpenWrt myself).

If you want to use PacketScript on OpenWrt you just need to select it in menuconfig (Network->IPTables->ipt-mod-lua).

We will use Debian stable (jessie) in this guide to learn the patching process. The same process can be used for other distros too. Let’s Start :

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
+
1. Installing dependencies
+# apt install libxtables-dev linux-headers-$(uname -r) build-essential pkg-config
+
+2. Download xtables-addons https://sourceforge.net/projects/xtables-addons and extract files.
+
+3. Download patches
+
+$ cd xtables-addons-3.2/
+$ wget "https://github.com/openwrt-mirror/openwrt/raw/master/package/network/utils/xtables-addons/patches/201-fix-lua-packetscript.patch"
+$ wget "https://raw.githubusercontent.com/openwrt-mirror/openwrt/master/package/network/utils/xtables-addons/patches/200-add-lua-packetscript.patch"
+
+4. Apply patches
+
+$ patch -p1 < 200-add-lua-packetscript.patch
+$ patch -p1 < 201-fix-lua-packetscript.patch
+
+5. change config files (only necessary if patch fails at config files)
+At this stage due to difference between xtables-addons version the first patch may fail at config file, You need to change them manually and remove all patch rejects and backups.
+
+5.1. Change configs
+add 'build_LUA=m' to the 'xtables-addons-1.xy/mconfig' file
+add 'obj-${build_LUA} += LUA/' to the 'xtables-addons-1.xy/extensions/Mbuild' file
+add 'obj-${build_LUA} += LUA/' to the 'xtables-addons-1.xy/extensions/Kbuild' file
+
+5.2. Remove rejects and backups
+$ find -name "*.rej" -delete
+$ find -name "*.orig" -delete
+
+6. Build & Install
+
+$ make
+# make install
+

Note that despite installing, the module may not be loaded, to do so:

1
+
# insmod /lib/modules/$(uname -r)/extra/xt_LUA.ko
+

Note: Module may not be installed in a valid location in order to be detected by modprobe. use symlinks or change the Makefile accordingly. See this or this.

Now everything should be ready:

1
+2
+3
+
root@debian:~# iptables -I INPUT -j LUA
+iptables v1.6.0: LUA: --script parameter required
+Try `iptables -h' or 'iptables --help' for more information.
+


Destination NAT (DNAT)

Create a lua script like this (dnat.lua) :

1
+2
+3
+4
+5
+6
+
function process_packet(p)
+	local ip = p:data(packet_eth):data(packet_ip)
+	if not ip then return NF_DROP end
+	ip:daddr():set("2.1.1.1") 
+	return XT_CONTINUE
+end
+

Then push it into kernel like this:

1
+
# iptables -t mangle -A POSTROUTING -d 8.8.8.8 -j LUA --script dnat.lua
+

It is recommended to utilize Netfilter matches and extensions and use PacketScript when they cannot do what you want for performance reasons.

I am prototyping some new features in PacketScript. If my employer agreed upon open sourcing those I will create a GitHub repo and update this post with this links.

No guide is available on the internet for PacketScript. I hope this post would be useful. I am glad to answer questions in comments. Let me know of your network prototyping tools.

This post is licensed under CC BY 4.0 by the author.

Call Gates' Ring Transitioning in IA-32 Mode

Hypervisor From Scratch – Part 6: Virtualizing An Already Running System

Comments powered by Disqus.

diff --git a/topics/pinitor/index.html b/topics/pinitor/index.html new file mode 100644 index 00000000..43f3b348 --- /dev/null +++ b/topics/pinitor/index.html @@ -0,0 +1,489 @@ + Pinitor - An API Monitor Based on Pin | Rayanfam Blog
Home Pinitor - An API Monitor Based on Pin
Post
Cancel

Pinitor - An API Monitor Based on Pin

In the last few days, I was thinking about finding a way to discover the Native Windows APIs which malware programmers are more interested in. What I did was downloading a sample of 48k malware from VxHeaven collection and using Rohitab’s API Monitor which is a sophisticated tool in which you can find what is happening in your PE. There are also other tools that do the same thing but I almost everytime use Rohitab’s tool.

After struggling with such tools, I figured out that this tool is not good enough in analyzing these samples because they actually hook everything or somehow they are known applications which some packers or protectors search for them among all the processes or if you wanna debug them, there are many nasty anti-debugging techniques that are hard to bypass so let’s get rid of all of them and use instrumentation in order to defeat these techniques.

I create a pin tool which is able to have the functionality of API Monitor but based on dynamic instrumentation and Intel’s pin and it “Pinitor” which stands for Pin Monitor and actually this is a tool which detects every API calling by instrumenting the target executable.

Pinitor create a call to one of its events, so that every time a new module (e.g .dll) loaded, it notifies pin about it and then search for every exported function in that module by using the EnumExportedFunctions, After that it tries to put a call in every function or in Windows Native API so everytime any of these functions called then Pinitor captures 12 arguments from the stack by default and save it in a file.

The problem here is, I don’t know a way of knowing how many inputs does a special function have! As I know all API Monitor applications contain a second file (data storage) which tells them about all the functions and its arguments, so if you know any other way, please tell me about it in comments!

Pinitor in its first version only works in Intel x86 systems, I’ve also built an x64 version of this tool whenever possible but for now, I think it should be a good tool for researching about binaries.

In the bottom of the post, I’ve added the source code so you can change and use it for your researching but compiling a pin tool in Windows is really tricky! It takes me, more than three days to build my first pin tool, but for the future, I prepare a blog post about how to build a pin tool and the errors that might happen during the pin tool compiling process.

Installing

First of all download pin from Intel’s website, then you can download Pinitor compiled binaries here, then you are good to go.

How to use

After downloading Pin and Pinitor then you should run pin with this tool, in the following example I demonstrate a simple example of how to use.

1
+
C:\pin\ia32\bin\pin.exe -t C:\Pintools\Pinitor.dll -o MyOutput.txt -- c:\Examples\Helloworld.exe
+

This will create a file (“MyOutput.txt”) then add the results to this file.

Examples

Imagine we built a native PE by using the following code :

Example.cpp

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
#include <windows.h>
+#include <iostream>
+
+using namespace std;
+int main()
+{
+	MessageBoxA(0,"Hello world !","Welcome",0);
+
+	 void * ptr1 = VirtualAlloc((void*)0x70080000, 1000, MEM_RESERVE, PAGE_READWRITE);
+	 cout << ptr1;
+	 system("pause");
+}
+

After that, you should run Pinitor based on how to use.

And now you are able to see the result, below is a small part of the output from Pinitor :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
NtUserSetCursor(0x10007,0x6dfdb0,0x1,0,0,0x6dfad0,0x6dfab8,0x3aa0080,0x77559cf0,0x22c63f2e,0x5,0x23)
+  returns 0x10003
+RtlFreeHeap(0x2a80000,0,0x2a92fb0,0x1,0x10007,0x6dfc34,0x6dfb6c,0x3aa0080,0x748173f0,0x567d1e5c,0x4101d,0x23)
+  returns 0x1
+  returns 0x1
+  returns 0x1
+  returns 0x1
+  returns 0x1
+  returns 0x1
+RtlFreeHeap(0x2a80000,0,0x2a90ae8,0x1,0x10007,0x6dfc34,0x6dfb6c,0x3aa0080,0x748173f0,0x567d1e5c,0x4101d,0x23)
+  returns 0x1
+RtlFreeHeap(0x2a80000,0,0x2a8eb88,0x1,0x10007,0x6dfc34,0x6dfb6c,0x3aa0080,0x748173f0,0x567d1e5c,0x4101d,0x23)
+RtlGetCurrentServiceSessionId(0x2a90ae8,0x2a8eb88,0x1,0x1,0x10007,0x6dfc34,0x6dfb6c,0x3aa0080,0x748173f0,0x567d1e5c,0x4101d,0x23)
+  returns 0
+  returns 0x1
+  returns 0x1
+  returns 0x1
+VirtualAlloc(0x70080000,0x3e8,0x2000,0x1,0x10007,0x6dfc34,0x6dfb6c,0x3aa0080,0x748173f0,0x567d1e5c,0x4101d,0x23)
+NtAllocateVirtualMemory(0xffffffff,0x6dfe74,0,0x1,0x10007,0x6dfc34,0x6dfb6c,0x3aa0080,0x748173f0,0x567d1e5c,0x4101d,0x23)
+ZwAllocateVirtualMemory(0xffffffff,0x6dfe74,0,0x1,0x10007,0x6dfc34,0x6dfb6c,0x3aa0080,0x748173f0,0x567d1e5c,0x4101d,0x23)
+  returns 0
+  returns 0
+  returns 0x70080000
+

In this way, you can use Pinitor for your own binaries.

Note

You might have encountered that some of the results are somehow wrong, I analyze the “example.exe” by a debugger and understand that this results are because of application’s internal calls which are not defined in any exported functions in dll(s) but as long as they are calls and have return then pin automatically prints them in the results. I can filter this kind of calls but I think this calls should exist in the results so you can also filter this kind of calls and returns in AddInvokeFunctionToFile by simply checking the name content and if the name is null then you can ignore the rest of operation.

Contributing

We are hardly working to create a binary analyzing and reverse engineering framework which is called Binvoke and this tool and many other tools will be added to the Binvoke Framework so any contribution and innovative idea will be appreciated.

Source Code

The source code is also available on GitHub :

[ https://github.com/SinaKarvandi/Pinitor ]

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
+
/* Imported Headers */
+/* ===================================================================== */
+
+#include "pin.H"
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <list>
+/* ===================================================================== */
+/* Global Variables */
+/* ===================================================================== */
+
+std::ofstream TraceFile;
+std::list lst;
+
+/* ===================================================================== */
+/* Methods to enumerate exported functions from images (Module) */
+/* ===================================================================== */
+
+void EnumExportedFunctions(const char *, void(*callback)(char*));
+
+int Rva2Offset(unsigned int);
+
+typedef struct {
+unsigned char Name[8];
+unsigned int VirtualSize;
+unsigned int VirtualAddress;
+unsigned int SizeOfRawData;
+unsigned int PointerToRawData;
+unsigned int PointerToRelocations;
+unsigned int PointerToLineNumbers;
+unsigned short NumberOfRelocations;
+unsigned short NumberOfLineNumbers;
+unsigned int Characteristics;
+} sectionHeader;
+
+sectionHeader *sections;
+unsigned int NumberOfSections = 0;
+
+int Rva2Offset(unsigned int rva) {
+int i = 0;
+
+for (i = 0; i < NumberOfSections; i++) { unsigned int x = sections[i].VirtualAddress + sections[i].SizeOfRawData; if (x >= rva) {
+return sections[i].PointerToRawData + (rva + sections[i].SizeOfRawData) - x;
+}
+}
+
+return -1;
+}
+
+void EnumExportedFunctions(const char *szFilename, void(*callback)(char*)) {
+FILE *hFile = fopen(szFilename, "rb");
+
+if (hFile != NULL) {
+if (fgetc(hFile) == 'M' && fgetc(hFile) == 'Z') {
+unsigned int e_lfanew = 0;
+unsigned int NumberOfRvaAndSizes = 0;
+unsigned int ExportVirtualAddress = 0;
+unsigned int ExportSize = 0;
+int i = 0;
+
+fseek(hFile, 0x3C, SEEK_SET);
+fread(&e_lfanew, 4, 1, hFile);
+fseek(hFile, e_lfanew + 6, SEEK_SET);
+fread(&NumberOfSections, 2, 1, hFile);
+fseek(hFile, 108, SEEK_CUR);
+fread(&amp;NumberOfRvaAndSizes, 4, 1, hFile);
+
+if (NumberOfRvaAndSizes == 16) {
+fread(&ExportVirtualAddress, 4, 1, hFile);
+fread(&ExportSize, 4, 1, hFile);
+
+if (ExportVirtualAddress > 0 && ExportSize > 0) {
+fseek(hFile, 120, SEEK_CUR);
+
+if (NumberOfSections > 0) {
+sections = (sectionHeader *)malloc(NumberOfSections * sizeof(sectionHeader));
+
+for (i = 0; i < NumberOfSections; i++) {
+fread(sections[i].Name, 8, 1, hFile);
+fread(&sections[i].VirtualSize, 4, 1, hFile);
+fread(&sections[i].VirtualAddress, 4, 1, hFile);
+fread(&sections[i].SizeOfRawData, 4, 1, hFile);
+fread(&sections[i].PointerToRawData, 4, 1, hFile);
+fread(&sections[i].PointerToRelocations, 4, 1, hFile);
+fread(&sections[i].PointerToLineNumbers, 4, 1, hFile);
+fread(&sections[i].NumberOfRelocations, 2, 1, hFile);
+fread(&sections[i].NumberOfLineNumbers, 2, 1, hFile);
+fread(&sections[i].Characteristics, 4, 1, hFile);
+}
+
+unsigned int NumberOfNames = 0;
+unsigned int AddressOfNames = 0;
+
+int offset = Rva2Offset(ExportVirtualAddress);
+fseek(hFile, offset + 24, SEEK_SET);
+fread(&NumberOfNames, 4, 1, hFile);
+
+fseek(hFile, 4, SEEK_CUR);
+fread(&AddressOfNames, 4, 1, hFile);
+
+unsigned int namesOffset = Rva2Offset(AddressOfNames), pos = 0;
+fseek(hFile, namesOffset, SEEK_SET);
+
+for (i = 0; i < NumberOfNames; i++) {
+unsigned int y = 0;
+fread(&y, 4, 1, hFile);
+pos = ftell(hFile);
+fseek(hFile, Rva2Offset(y), SEEK_SET);
+
+char c = fgetc(hFile);
+int szNameLen = 0;
+
+while (c != '\0') {
+c = fgetc(hFile);
+szNameLen++;
+}
+
+fseek(hFile, (-szNameLen) - 1, SEEK_CUR);
+char* szName = (char*)calloc(szNameLen + 1, 1);
+fread(szName, szNameLen, 1, hFile);
+
+callback(szName);
+
+fseek(hFile, pos, SEEK_SET);
+}
+}
+}
+}
+}
+
+fclose(hFile);
+}
+}
+
+/* ===================================================================== */
+/* It'll add exported functions into the lst */
+/* ===================================================================== */
+
+void mycallback(char* szName) {
+lst.push_back(szName);
+}
+
+/* ===================================================================== */
+/* Commandline Switches */
+/* ===================================================================== */
+
+// If you don't specify the the -o in command line, then the default output will be saved in pinitor.txt
+KNOB KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool",
+"o", "pinitor.out", "specify trace file name");
+
+/* ===================================================================== */
+
+/* ===================================================================== */
+/* Showing function names and their aruments */
+/* ===================================================================== */
+
+// As I mentioned in the my blog at http://rayanfam.com/topics/pinitor/ I have know idea about
+// how to get the exact number of Arguments which is passed to the specific function except using
+// a data file which previously stored every functions entrance argument, so if you know any other
+// way then please tell me about it in my blog.
+
+VOID AddInvokeFunctionToFile(CHAR * name, ADDRINT Arg1,ADDRINT Arg2 , ADDRINT Arg3, ADDRINT Arg4, ADDRINT Arg5, ADDRINT Arg6, ADDRINT Arg7, ADDRINT Arg8, ADDRINT Arg9, ADDRINT Arg10, ADDRINT Arg11, ADDRINT Arg12)
+{
+TraceFile << name << "(" << Arg1 << ","<< Arg2 << "," << Arg3 << "," <" << endl << endl;
+cerr << "--------------------------------------------------------------------------------------------" << endl;
+cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
+return -1;
+}
+
+/* ===================================================================== */
+/* Main */
+/* ===================================================================== */
+
+int main(int argc, char *argv[])
+{
+Usage();
+// Initialize pin & symbol manager
+PIN_InitSymbols();
+
+if(PIN_Init(argc,argv))
+{
+//Prints the usage
+return Usage();
+}
+
+// Write to a file since cout and cerr maybe closed by the application
+TraceFile.open(KnobOutputFile.Value().c_str());
+TraceFile << hex;
+TraceFile.setf(ios::showbase);
+
+// Register Image to be called to instrument functions.
+IMG_AddInstrumentFunction(Image, 0);
+PIN_AddFiniFunction(Fini, 0);
+
+// Never returns
+PIN_StartProgram();
+
+return 0;
+}
+
+/* ===================================================================== */
+/* eof */
+/* ===================================================================== */
+

This project is done by the contribution of my best friend Sima.

Todo :

  • Show the exact number of Arguments to functions ( I have no idea except using a list of functions with their input parameters count. If you know the other ways please tell me in comments below.)
  • Test And Publish the Result of most used functions in virus samples
  • Build Such tool for ELF Binaries
  • Build Intel64 version of Pinitor

Special thanks to Mahdi Golshani, one of my friends who makes the logo.

This post is licensed under CC BY 4.0 by the author.

Getting started with Python and Qt for cross-platform GUI apps

Build a Simple Pin Tool

Comments powered by Disqus.

diff --git a/topics/pykd-tutorial-part1/index.html b/topics/pykd-tutorial-part1/index.html new file mode 100644 index 00000000..5a20145a --- /dev/null +++ b/topics/pykd-tutorial-part1/index.html @@ -0,0 +1,357 @@ + PyKD Tutorial - part 1 | Rayanfam Blog
Home PyKD Tutorial - part 1
Post
Cancel

PyKD Tutorial - part 1

Using windbg script syntax is such annoying thing that almost all reverse engineers have problems dealing with it but automating debugging gives such a power that can’t be easily ignored. A good solution to solve this problem is using the power and simplicity of Python and Windbg together. If you aware, Windbg also supports c-like binaries as extensions so there is a praiseworthy tool called PyKD which does the hard thing and connects Python and Windbg together in a straight and usable way. The purpose of PyKD, as they mentioned, is :

This project can help to automate debugging and crash dump analysis using Python. It allows one to take the best from both worlds: the expressiveness and convenience of Python with the power of WinDbg!

You can download PyKD at this link.

Setup PyKD

To find the main extension binary files, you should find the latest version of the Bootstrapper and download its x86 and x64 versions depending to your needs, after extracting the binary files (pykd.dll) you should load it in Windbg with the following command :

1
+
.load C:\Users\Sina\Desktop\pykd\x64\pykd.dll
+

In order to see if it successfully loaded or not, you should execute the following command, if you see something like this, then you’re good to go.

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
+
0:000> !help
+
+usage:
+
+!help
+	print this text
+
+!info
+	list installed python interpreters
+
+!select version
+	change default version of a python interpreter
+
+!py [version] [options] [file]
+	run python script or REPL
+
+	Version:
+	-2           : use Python2
+	-2.x         : use Python2.x
+	-3           : use Python3
+	-3.x         : use Python3.x
+
+	Options:
+	-g --global  : run code in the common namespace
+	-l --local   : run code in the isolated namespace
+	-m --module  : run module as the __main__ module ( see the python command line option -m )
+
+	command samples:
+	"!py"                          : run REPL
+	"!py --local"                  : run REPL in the isolated namespace
+	"!py -g script.py 10 "string"" : run a script file with an argument in the commom namespace
+	"!py -m module_name" : run a named module as the __main__
+
+!pip [version] [args]
+	run pip package manager
+
+	Version:
+	-2           : use Python2
+	-2.x         : use Python2.x
+	-3           : use Python3
+	-3.x         : use Python3.x
+
+	pip command samples:
+	"pip list"                   : show all installed packagies
+	"pip install pykd"           : install pykd
+	"pip install --upgrade pykd" : upgrade pykd to the latest version
+	"pip show pykd"              : show info about pykd package
+

If you saw the above command suggestions, one of the interesting commands which can be used to update the PyKD is :

1
+
!pip install --upgrade pykd
+

But actually, I prefer to compile the latest version from its source code rather than updating or using the PyKD.dll directly. That’s enough for the setting up and starting, in the rest of the post, we’re getting started with some useful samples of using PyKD. But the right way to get PyKD is to download its latest release and then find a file named “pykd.pyd” among other DLL files then load .pyd file.

Using PyKD Features

This section describes the general functions of PyKD.

Executing Command

The simplest thing is using the PyKD to execute and get the command result, it can be done using the following script in which r is our command and we simply print the result. You can also assign the results to a variable and separate them using Python’s regular string function.

1
+2
+3
+
import pykd
+
+print pykd.dbgCommand("r")
+

You should save the above script into a file (e.g pykd-script.py) then load it in Windbg using the following command :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
0:000> !py c:\users\Sina\desktop\pykd-script.py
+rax=0000000000000000 rbx=0000000000000010 rcx=00007fffd3d5a434
+rdx=0000000000000000 rsi=00007fffd3de4090 rdi=00007fffd3de4740
+rip=00007fffd3d8d02c rsp=0000000b419ef3b0 rbp=0000000000000000
+ r8=0000000b419ef3a8  r9=0000000000000000 r10=0000000000000000
+r11=0000000000000246 r12=0000000000000040 r13=0000000000000000
+r14=0000000b41a63000 r15=000001d8da130000
+iopl=0         nv up ei pl zr na po nc
+cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
+ntdll!LdrInitShimEngineDynamic+0x34c:
+00007fff`d3d8d02c cc              int     3
+

As you see the registers’ value is shown above, I usually use these kinds of scripts with t (step in) and p (step) to simulate an instrumenting environment and check what is going on (e.g a combination of instructions and registers’ value and its corresponding memory values.) even though this operation is too slow but still usable for special cases.

Getting Registers value

A better way of getting registers is using the  following sample :

1
+2
+3
+4
+5
+
import pykd
+
+addr = hex(pykd.reg("rsp"))
+
+print(addr)
+

Continue to run

The following command is the equivalent of go in PyKD :

1
+2
+
pykd.go()
+
+

Read the content of the memory

To read the contents of a special virtual address you should use something like this :

1
+2
+3
+4
+5
+6
+
import pykd
+
+addr = pykd.reg("rip")
+value = pykd.loadBytes(addr,16)
+
+print(value)
+

The result is :

1
+2
+
0:010> !py c:\users\Sina\desktop\pykd-script.py
+[204, 195, 204, 204, 204, 204, 204, 204, 15, 31, 132, 0, 0, 0, 0, 0]
+

The other variants of Load functions are loadAnsiString,loadBytes,loadCStr,loadChars,loadDWords,loadDoubles and etc.

Comparing Memory

The following script returns true if the contents of memory in two virtual addresses are equal otherwise the result is false.

1
+2
+3
+4
+5
+6
+
import pykd
+addr1 = 0x00007fffd3d31596
+addr2 = 0x00007fffd3d31597
+result = pykd.compareMemory(addr1,addr2,100)
+
+print(result)
+

Detach

As the documentation suggests,

1
+
pykd.detachAllProcesses() ===>     Detach from all process and resume all their threads
+

&

1
+
pykd.detachProcess() ===>     Stop process debugging
+

Find Nearest Valid Memory Location

The following script gives the nearest valid memory location, near to 0x0.

1
+2
+3
+4
+5
+
import pykd
+
+result = pykd.findMemoryRegion(0x0)
+
+print(hex(result[0]))
+

The result is :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
0:003> !py c:\users\Sina\desktop\pykd-script.py
+0x5d670000
+0:003> dc 0x5d670000
+00000000`5d670000  00905a4d 00000003 00000004 0000ffff  MZ..............
+00000000`5d670010  000000b8 00000000 00000040 00000000  ........@.......
+00000000`5d670020  00000000 00000000 00000000 00000000  ................
+00000000`5d670030  00000000 00000000 00000000 00000128  ............(...
+00000000`5d670040  0eba1f0e cd09b400 4c01b821 685421cd  ........!..L.!Th
+00000000`5d670050  70207369 72676f72 63206d61 6f6e6e61  is program canno
+00000000`5d670060  65622074 6e757220 206e6920 20534f44  t be run in DOS 
+00000000`5d670070  65646f6d 0a0d0d2e 00000024 00000000  mode....$.......
+

Finding Function Name

If you want to find the what function is located at a special address based on symbols, you should use findSymbol.

1
+2
+3
+4
+5
+
import pykd
+
+result = pykd.findSymbol(0x00007fffd3d5d960)
+
+print(result)
+

The result is :

1
+2
+
0:003> !py c:\users\Sina\desktop\pykd-script.py
+ntdll!DbgBreakPoint
+

Get Current Stack Frame

1
+2
+3
+4
+5
+
import pykd
+
+result = pykd.getFrame()
+
+print(result)
+

The result is :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
0:003> !py c:\users\Sina\desktop\pykd-script.py
+Frame: IP=7fffd3d5d960  Return=7fffd3d89bbb  Frame Offset=b41effa70  Stack Offset=b41effa78
+0:003> dc rsp
+0000000b`41effa78  d3d89bbb 00007fff 00000000 00000000  ................
+0000000b`41effa88  00000000 00000000 00000000 00000000  ................
+0000000b`41effa98  00000000 00000000 00000000 00000000  ................
+0000000b`41effaa8  d18e3034 00007fff 00000000 00000000  40..............
+0000000b`41effab8  00000000 00000000 00000000 00000000  ................
+0000000b`41effac8  00000000 00000000 00000000 00000000  ................
+0000000b`41effad8  d3d31551 00007fff 00000000 00000000  Q...............
+0000000b`41effae8  00000000 00000000 00000000 00000000  ................
+

pykd.getStack() also gives a list of stack frame objects.

Last Exception

1
+2
+3
+4
+5
+
import pykd
+
+result = pykd.getLastException()
+
+print(result)
+

The result is :

1
+2
+3
+4
+5
+6
+7
+
0:003> !py c:\users\Sina\desktop\pykd-script.py
+FirstChance= True
+ExceptionCode= 0x80000003
+ExceptionFlags= 0x0
+ExceptionRecord= 0x0
+ExceptionAddress= 0x7fffd3d5d960
+Param[0]= 0x0
+

Finding Function Location

To get where a special function located you can use the following code : It’s like executing x KERNEL32!CreateFileW in Windbg command-line.

1
+2
+3
+4
+5
+
import pykd
+
+result = pykd.getOffset("KERNEL32!CreateFileW")
+
+print(result)
+

The result is :

1
+2
+3
+4
+5
+
0:003> !py c:\users\Sina\desktop\pykd-script.py
+0x7fffd18f0940L
+
+0:003> x kernel32!CreateFileW
+00007fff`d18f0940 KERNEL32!CreateFileW (<no parameter info>)
+

Get System Version

1
+2
+3
+4
+5
+
import pykd
+
+result = pykd.getSystemVersion()
+
+print(result)
+

example result :

1
+2
+3
+4
+5
+
0:003> !py c:\users\Sina\desktop\pykd-script.py
+Major Version: 10
+Minor Version: 0
+Build: 17134
+Description: 17134.1.amd64fre.rs4_release.180410-1804
+

Getting Page Attributes

One of the important functions of PyKD is getting the page attributes.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
import pykd
+
+addr1 = pykd.reg("rip")
+
+result = pykd.getVaProtect(addr1)
+
+print("RIP Attributes  : " + str(result))
+
+addr2 = pykd.reg("rsp")
+
+result = pykd.getVaProtect(addr2)
+
+print("RSP Attributes  : " + str(result))
+

The result is :

1
+2
+3
+
0:003> !py c:\users\Sina\desktop\pykd-script.py
+RIP Attributes  : PageExecuteRead
+RSP Attributes  : PageReadWrite
+

There is also an important function called isValid which can be used to detect whether a virtual address is valid or not.

Reading and writing MSR Register

If you are in a kernel debugging, you could read MSR registers using pykd.rdmsr(value).

1
+2
+3
+4
+5
+
import pykd
+
+result = pykd.rdmsr(0x80000082)
+
+print(result)
+

To write on a specific MSR you can use pykd.wrmsr(Address, Value). That’s enough for now, I’ll write the rest of this post another time in part 2, so make sure check blog more frequently. :) The second part is also published here!

This post is licensed under CC BY 4.0 by the author.

Active Directory Certificate Services Overview and Migration

PyKD Tutorial - part 2

Comments powered by Disqus.

diff --git a/topics/pykd-tutorial-part2/index.html b/topics/pykd-tutorial-part2/index.html new file mode 100644 index 00000000..8f3db41e --- /dev/null +++ b/topics/pykd-tutorial-part2/index.html @@ -0,0 +1,95 @@ + PyKD Tutorial - part 2 | Rayanfam Blog
Home PyKD Tutorial - part 2
Post
Cancel

PyKD Tutorial - part 2

The content of this post is the second part of PyKD Tutorials, so make sure to read this topic first, then continue reading this topic.

Breakpoints

Breakpoints are such useful things and can give you the power of analyzing programs in a better and easier way by using PyKD. In the API Reference they introduce setBp function in the following way :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
setBp( (long)offset [, (object)callback]) -> breakpoint :
+    Set software breakpoint on executiont
+
+    C++ signature :
+        class pykd::Breakpoint * __ptr64 setBp(unsigned __int64 [,class boost::python::api::object {lvalue}])
+
+setBp( (long)offset, (long)size, (int)accsessType [, (object)callback]) -> breakpoint :
+    Set hardware breakpoint
+
+    C++ signature :
+        class pykd::Breakpoint * __ptr64 setBp(unsigned __int64,unsigned __int64,unsigned long [,class boost::python::api::object {lvalue}])
+

As you can see, setBp can give a pointer as its first argument and a python function as the second argument. Every time the pointer executes, your python function will be invoked. The second usage of setBp is for setting hardware breakpoints. Removing the all the breakpoints using pykd.removeAllBp(). Remove a breakpoint by its index removeBp(int).

Searching through the memory

Even though searching for a string or a special byte is really straight in Windbg but you can also use PyKD in order to search through the memory.

1
+2
+3
+4
+5
+
import pykd
+
+result = pykd.searchMemory(0x0,0x7fffffff,"A")
+
+print(hex(result))
+

And the result is :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
0:010> !py c:\users\Sina\desktop\pykd-script.py
+0x5d6700ba
+0:010> db 0x5d6700ba
+00000000`5d6700ba  41 e4 d4 aa 8f e4 0b 55-5f e4 d7 aa 8f e4 0b 55  A......U_......U
+00000000`5d6700ca  44 e4 d9 aa 8f e4 d6 aa-8e e4 f9 ab 8f e4 ab d3  D...............
+00000000`5d6700da  6f e4 f0 aa 8f e4 ab d3-6e e4 d0 ab 8f e4 ab d3  o.......n.......
+00000000`5d6700ea  53 e4 d7 aa 8f e4 db f8-54 e4 d7 aa 8f e4 d6 aa  S.......T.......
+00000000`5d6700fa  18 e4 d7 aa 8f e4 ab d3-51 e4 d7 aa 8f e4 52 69  ........Q.....Ri
+00000000`5d67010a  63 68 d6 aa 8f e4 00 00-00 00 00 00 00 00 00 00  ch..............
+00000000`5d67011a  00 00 00 00 00 00 00 00-00 00 00 00 00 00 50 45  ..............PE
+00000000`5d67012a  00 00 64 86 06 00 01 76-e7 57 00 00 00 00 00 00  ..d....v.W......
+

Edit a Byte

The equivalent of eb is setByte as follows:

1
+2
+3
+
import pykd
+
+pykd.setByte(0x7fffd3d5d965,0x41)
+

The above script is like :

1
+
eb 0x7fffd3d5d965 0x41
+

Other variants are setDWord,setDouble,setFloat.

Changing XIP

You can use setIP in order to change the current RIP or EIP which is very useful in defeating with packers and protectors.

1
+2
+3
+
import pykd
+
+pykd.setIP(0x7fffd3d5d94f)
+

Set Symbol Path

For debugging purpose you can also set symbol path like this:

1
+
pykd.setSymbolPath("srv*c:\symbols*https://msdl.microsoft.com/download/symbols")
+
1
+2
+3
+4
+5
+
0:003> !py c:\users\Sina\desktop\pykd-script.py
+
+************* Symbol Path validation summary **************
+Response                         Time (ms)     Location
+Deferred                                       srv*c:\symbols*https://msdl.microsoft.com/download/symbols
+

Step and Step-out and Trace

Instead of using p and t you can use its equivalent pykd.step() and pykd.stepout() and pykd.trace().

Disassemble Memory

The following example describes how to disassemble the memory at a specific address.

1
+2
+3
+4
+5
+
import pykd
+
+result = pykd.disasm(0x7fffd3d5d956)
+
+print(result)
+

The result depends on the location. e.g :

1
+2
+
0:003> !py c:\users\Sina\desktop\pykd-script.py
+00007fff`d3d5d956 66660f1f840000000000 nop word ptr [rax+rax]
+

Further Reading

In the above post, I tried to describe the main features of PyKD and how can it be used to ease the reverse engineering process, if you want to read more about PyKD API References take a look at this link, there is also a good article here worth to read.

This post is licensed under CC BY 4.0 by the author.

PyKD Tutorial - part 1

Defeating malware's Anti-VM techniques (CPUID-Based Instructions)

Comments powered by Disqus.

diff --git a/topics/reversing-windows-internals-part1/index.html b/topics/reversing-windows-internals-part1/index.html new file mode 100644 index 00000000..c6a52a46 --- /dev/null +++ b/topics/reversing-windows-internals-part1/index.html @@ -0,0 +1,1667 @@ + Reversing Windows Internals (Part 1) - Digging Into Handles, Callbacks & ObjectTypes | Rayanfam Blog
Home Reversing Windows Internals (Part 1) - Digging Into Handles, Callbacks & ObjectTypes
Post
Cancel

Reversing Windows Internals (Part 1) - Digging Into Handles, Callbacks & ObjectTypes

Introduction

Welcome to the first part of a series of posts about Exploring & Reversing Windows Concepts and Internals. If you reach here then you’re probably a security researcher or a programmer and this post and similar posts can help you understand what’s going on in some parts of Windows when you use objects with different users and credentials and what you can expect from Windows and how it internally works.

If you want to follow other parts of this tutorial or other tutorials, please visit here.

Overview

In this part, I’m gonna describe some internal structures and functions relating to the “Handles” and we’ll see how Windows saves these handles in its internal structures then we go to see how callbacks work. For example, when you create a handle what kind of mechanisms exists in Windows to notify you about the handle creation. After that, we’ll see how Windows internally calls these callbacks by analyzing the process of creating a handle when a user-mode application requests a handle to the kernel. At last, we’ll see how Windows saves these callbacks and check other (somehow unknown) callbacks by studying different Object Types in Windows and we’ll practically use them in our drivers.

Table of Contents

  • Introduction
  • Overview
  • Table of Contents
  • Handles
    • What is a Handle?
    • Handles in Windows Kernel
    • Changing Handles Access
  • Callbacks
    • Callbacks for processes
  • Handle Creation Process
    • PsOpenProcess
    • ObOpenObjectByPointer
    • SECURITY_SUBJECT_CONTEXT
    • ACCESS_STATE
    • ObpCreateHandle
  • Object Types
    • ObjectTypes in Windows
    • Finding Process Object Type
    • TypeIndex in Object Types
    • Finding All Windows _OBJECT_TYPE(s)
    • Finding Types which support callback
  • Analyzing Callbacks in ObjectTypes
    • DumpProcedure
    • OpenProcedure
    • CloseProcedure
    • DeleteProcedure
    • ParseProcedure
    • ParseProcedureEx
    • SecurityProcedure
    • QueryNameProcedure
    • OkayToCloseProcedure
  • Using Callbacks in ObjectTypes
  • Conclusion
  • References

Aniiiiiime :)

What is a Handle?

If you’re familiar with the way Windows shares its resources, then you probably know about the handles. In short, the handle is a value that the Windows kernel returns to the user-mode application (if you have needed privileges or have an account which is not denied by DACL) and this handle can be used for further action on the object.

There is a tool called “handle” from the SysInternals which can be downloaded from here.

The official site describes :

Ever wondered which program has a particular file or directory open? Now you can find out. Handle is a utility that displays information about open handles for any process in the system. You can use it to see the programs that have a file open, or to see the object types and names of all the handles of a program.

Let’s see what are the handles of processes.

For example,

1
+
handle windows\system
+

This command shows every handle for each process in which their handle name contains “windows\system”. The name match is case-insensitive and the fragment specified can be anywhere in the paths you are interested in. You can imagine how this way can be used to find what process(es) are opening a specific file when you try to remove them.

Handles

Finding all the handles in User-mode

One interesting thing in Windows is if you are in Integrity Level >= Medium, then you can access handles to all processes, even kernel addresses of objects. Alex mentioned that KASLR is not designed to protect against processes with Medium Integrity or above. By the way, its one of the known methods to bypass KASLR (e.g when you have a write-what-where bug and don’t know where to modify then you can use one of the objects in the kernel as you have the addresses).

The PoC for this way is available on GitHub (https://github.com/SinaKarvandi/Process-Magics/tree/master/EnumAllHandles) and you can see the results from the following images.

Dumping all handles

Now you can see all the handles even from other processes (like system process) with an unprivileged (non-elevated UAC) user.

All kernel handles and addresses

Handles In Windows Kernel

Let’s see how Windows saves and manages the handles in its kernel.

In process structure (nt!_EPROCESS) there is a field called “ObjectTable”.

1
+2
+
kd> dt nt!_EPROCESS -y object
+   +0x418 ObjectTable : Ptr64 _HANDLE_TABLE
+

The nt!_HANDLE_TABLE is like this :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+
kd> dt nt!_HANDLE_TABLE
+   +0x000 NextHandleNeedingPool : Uint4B
+   +0x004 ExtraInfoPages   : Int4B
+   +0x008 TableCode        : Uint8B
+   +0x010 QuotaProcess     : Ptr64 _EPROCESS
+   +0x018 HandleTableList  : _LIST_ENTRY
+   +0x028 UniqueProcessId  : Uint4B
+   +0x02c Flags            : Uint4B
+   +0x02c StrictFIFO       : Pos 0, 1 Bit
+   +0x02c EnableHandleExceptions : Pos 1, 1 Bit
+   +0x02c Rundown          : Pos 2, 1 Bit
+   +0x02c Duplicated       : Pos 3, 1 Bit
+   +0x02c RaiseUMExceptionOnInvalidHandleClose : Pos 4, 1 Bit
+   +0x030 HandleContentionEvent : _EX_PUSH_LOCK
+   +0x038 HandleTableLock  : _EX_PUSH_LOCK
+   +0x040 FreeLists        : [1] _HANDLE_TABLE_FREE_LIST
+   +0x040 ActualEntry      : [32] UChar
+   +0x060 DebugInfo        : Ptr64 _HANDLE_TRACE_DEBUG_INFO
+

Using HandleTableList, you can traverse through each handle of your target process.

Each handle is defined in a structure called “nt!_HANDLE_TABLE_ENTRY “ and among these fields the most interesting one is GrantedAccessBits.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
kd> dt nt!_HANDLE_TABLE_ENTRY
+   +0x000 VolatileLowValue : Int8B
+   +0x000 LowValue         : Int8B
+   +0x000 InfoTable        : Ptr64 _HANDLE_TABLE_ENTRY_INFO
+   +0x008 HighValue        : Int8B
+   +0x008 NextFreeHandleEntry : Ptr64 _HANDLE_TABLE_ENTRY
+   +0x008 LeafHandleValue  : _EXHANDLE
+   +0x000 RefCountField    : Int8B
+   +0x000 Unlocked         : Pos 0, 1 Bit
+   +0x000 RefCnt           : Pos 1, 16 Bits
+   +0x000 Attributes       : Pos 17, 3 Bits
+   +0x000 ObjectPointerBits : Pos 20, 44 Bits
+   +0x008 GrantedAccessBits : Pos 0, 25 Bits
+   +0x008 NoRightsUpgrade  : Pos 25, 1 Bit
+   +0x008 Spare1           : Pos 26, 6 Bits
+   +0x00c Spare2           : Uint4B
+

There is a command (!handle) in windbg which used to show the details about the handle.

The following picture describes the details of each field in !handle.

!handle windbg

Changing Handles GrantedAccess

If you map (Entry) field to the _HANDLE_TABLE_ENTRY then you can see the following results.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
kd> dt nt!_HANDLE_TABLE_ENTRY ffffa30c9aa26010
+   +0x000 VolatileLowValue : 0n-1981244744615788709
+   +0x000 LowValue         : 0n-1981244744615788709
+   +0x000 InfoTable        : 0xe4813466`e010ff5b _HANDLE_TABLE_ENTRY_INFO
+   +0x008 HighValue        : 0n2097151
+   +0x008 NextFreeHandleEntry : 0x00000000`001fffff _HANDLE_TABLE_ENTRY
+   +0x008 LeafHandleValue  : _EXHANDLE
+   +0x000 RefCountField    : 0n-1981244744615788709
+   +0x000 Unlocked         : 0y1
+   +0x000 RefCnt           : 0y0111111110101101 (0x7fad)
+   +0x000 Attributes       : 0y000
+   +0x000 ObjectPointerBits : 0y11100100100000010011010001100110111000000001 (0xe4813466e01)
+   +0x008 GrantedAccessBits : 0y0000111111111111111111111 (0x1fffff)
+   +0x008 NoRightsUpgrade  : 0y0
+   +0x008 Spare1           : 0y000000 (0)
+   +0x00c Spare2           : 0
+

Note that, 0x1fffff means FULL CONTROL. You easily change the access bit, e.g using “eb”. For example when you use a command like “eb ffffa30c9aa26010+8 ee” then if you see the handle again (!handle 0x04) you can see that GrantedAccess is changed.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+
PROCESS ffffe4813466e040
+    SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
+    DirBase: 001aa002  ObjectTable: ffffa30c9aa06580  HandleCount: 2986.
+    Image: System
+
+Kernel handle table at ffffa30c9aa06580 with 2986 entries in use
+
+0004: Object: ffffe4813466e040  GrantedAccess: 001fffee (Protected) Entry: ffffa30c9aa26010
+Object: ffffe4813466e040  Type: (ffffe4813467b0c0) Process
+    ObjectHeader: ffffe4813466e010 (new version)
+        HandleCount: 4  PointerCount: 131188
+

There is a good post here which describes how the above method can be used in order to bypass the restrictions that a driver can put on a special process or each process that tries to access memory of a protected-process, for example, a game with anit-cheat protection or a security software which protects its memory from being accessed by a remote process and APIs like WriteProcessMemory or ReadProcessMemory).

Let’s see some other functions relating to the handles.

There is a function ExEnumHandleTable which enumerates all the handle from a process by passing a pointer to process’s ObjectTable.

1
+2
+3
+4
+5
+6
+7
+8
+
NTKERNELAPI
+BOOLEAN
+ExEnumHandleTable (
+    __in PHANDLE_TABLE HandleTable,
+    __in EX_ENUMERATE_HANDLE_ROUTINE EnumHandleProcedure,
+    __in PVOID EnumParameter,
+    __out_opt PHANDLE Handle
+    )
+

Also, there is a function ExpLookupHandleTableEntry which gets the handle table as its first argument (RCX) and the handle value as the second argument (RDX) and returns the _HANDLE_TABLE_ENTRY corresponding to that handle. You can use them in your driver or shellcode.

1
+2
+3
+4
+5
+
PHANDLE_TABLE_ENTRY
+ExpLookupHandleTableEntry (
+    IN PHANDLE_TABLE HandleTable,
+    IN EXHANDLE tHandle
+    )
+

Callbacks for processes

In order to set callback whenever a handle to a process is requested or whatever relating to the handles of Threads or Processes, you can ObRegisterCallbacks function.

The ObRegisterCallbacks routine registers a list of callback routines for thread, process, and desktop handle operations.

The sample for using ObRegisterCallbacks is available on GitHub :

[https://github.com/SinaKarvandi/misc/tree/master/ObRegisterCallbacks]

If you load the above driver, you’ll get the following NTSTATUS error :

“{Access Denied} A process has requested access to an object, but has not been granted those access rights.”

If we look at the decompiled code from IDA and look for the error code (0xC0000022), we’ll reach to the following pseudo-code.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
      if ( v14 )
+      {
+        if ( !(unsigned int)MmVerifyCallbackFunctionCheckFlags(v14, 32i64) )
+          goto LABEL_23;
+      }
+      else if ( !v13[3] )
+      {
+        break;
+      }
+      v15 = v13[3];
+      if ( v15 && !(unsigned int)MmVerifyCallbackFunctionCheckFlags(v15, 32i64) )
+      {
+LABEL_23:
+        v5 = 0xC0000022;
+        goto LABEL_24;
+      }
+

It’s clear that MmVerifyCallbackFunctionCheckFlags is the guilty function, I don’t find a way to register my unsigned driver but for this let’s just patch it.

1
+2
+
0:  48 c7 c0 01 00 00 00    mov    rax,0x1
+7:  c3                      ret
+

The above assembly code is enough to always return true so we need to execute the following windbg command:

1
+
eb nt!MmVerifyCallbackFunctionCheckFlags 48 c7 c0 01 00 00 00 c3
+

Update 1: As Yarden mentioned, linking in with /INTEGRITYCHECK will save you the need to patch the kernel from the debugger so instead of using the above command you can add /INTEGRITYCHECK to your linker when you’re compiling your driver.

Now load the driver again. If you encounter errors like: “An instance already exists at this altitude on the volume specified”, then you have to change the following line :

1
+
	RtlInitUnicodeString(&Altitude, L"1001");
+

Make sure to run the following windbg command if you previously didn’t enable the debugging outputs.

1
+
 eb nt!kd_default_mask ff ff ff ff
+

After running driver, you have to see each handle request to processes or threads with its desired access masks and the corresponding operation (e.g creating a handle or duplicate handle or whatever).

Finally, we’ll get the following results :

Callback results

Handle Creation Process

Now that we know some of the basic concepts from callbacks and handle tables, let’s have a comprehensive survey from user-mode OpenProcess until we come back to user-mode again so we can see how Windows creates handle and saves it on its handle table.

The following functions have to be called in order to make a handle available to the user-mode in the case of opening a handle to a process.

OpenProcess (user-mode) -> NtOpenProcess (user-mode) -> NtOpenProcess (kernel-mode) -> PsOpenProcess - > ObOpenObjectByPointer -> ObpCreateHandle

If you remeber from the ObRegisterCallbacks, our callbacks called from “ObpCallPreOperationCallbacks” and this function is called by ObpCreateHandle. Also “ObpCallPostOperationCallbacks” is responsible for calling our post operation callbacks (we’ll see them).

Let’s start with OpenProcess, here is an ordered decompiled version of OpenProcess.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+
HANDLE __stdcall OpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)
+{
+  int result; // eax
+  CLIENT_ID ClientId; // [rsp+20h] [rbp-48h]
+  OBJECT_ATTRIBUTES ObjectAttributes; // [rsp+30h] [rbp-38h]
+  HANDLE ProcessHandle; // [rsp+88h] [rbp+20h]
+
+  ObjectAttributes.Length = 0x30;
+  ObjectAttributes.RootDirectory = 0i64;
+  ClientId = (CLIENT_ID)(unsigned __int64)(signed int)dwProcessId;
+  ObjectAttributes.Attributes = bInheritHandle != 0 ? 2 : 0;
+  ObjectAttributes.ObjectName = 0i64;
+  _mm_storeu_si128((__m128i *)&ObjectAttributes.SecurityDescriptor, (__m128i)0i64);
+  result = NtOpenProcess(&ProcessHandle, dwDesiredAccess, &ObjectAttributes, &ClientId);
+  if ( result >= 0 )
+    return ProcessHandle;
+  BaseSetLastNTError(result);
+  return 0i64;
+}
+

Among the above parameters, dwProcessId and dwDesiredAccess is not in our interest as they’re more and less clear but the most interesting field here is ObjectAttributes, Microsoft explains about Object Attributes here. The structure is below, you can read about each field in MSDN.

1
+2
+3
+4
+5
+6
+7
+8
+
typedef struct _OBJECT_ATTRIBUTES64 {
+    ULONG Length;
+    ULONG64 RootDirectory;
+    ULONG64 ObjectName;
+    ULONG Attributes;
+    ULONG64 SecurityDescriptor;
+    ULONG64 SecurityQualityOfService;
+} OBJECT_ATTRIBUTES64;
+

The above field has some Attributes that is interesting to us, their definition as defined in SDK :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
#define OBJ_INHERIT             			0x00000002L
+#define OBJ_PERMANENT           			0x00000010L
+#define OBJ_EXCLUSIVE           			0x00000020L
+#define OBJ_CASE_INSENSITIVE    			0x00000040L
+#define OBJ_OPENIF              			0x00000080L
+#define OBJ_OPENLINK            			0x00000100L
+#define OBJ_KERNEL_HANDLE       			0x00000200L
+#define OBJ_FORCE_ACCESS_CHECK  			0x00000400L
+#define OBJ_IGNORE_IMPERSONATED_DEVICEMAP		0x00000800L
+#define OBJ_VALID_ATTRIBUTES    			0x00000FF2L
+

We’ll see how they affect process handle creation, later.

NtOpenProcess

NtOpenProcess, first checks for its previous mode (user-mode or kernel-mode) then it calls PsOpenProcess.

1
+2
+3
+4
+5
+6
+
__kernel_entry NTSYSCALLAPI NTSTATUS NtOpenProcess(
+  PHANDLE            ProcessHandle,
+  ACCESS_MASK        DesiredAccess,
+  POBJECT_ATTRIBUTES ObjectAttributes,
+  PCLIENT_ID         ClientId
+);
+

PsOpenProcess

The PsOpenProcess is something like this, it’s not documented so it’s based on IDA’s decompile results:

1
+2
+3
+4
+5
+6
+7
+8
+9
+
NTSTATUS
+FASTCALL
+__int64 __fastcall PsOpenProcess(
+PHANDLE ProcessHandle, 
+ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes,
+ PCLIENT_ID ClientId, 
+char PreviousMode, 
+char PreviousMode2);
+

In PsOpenProcess, it first checks whether the handle pointer resides to valid user-mode address then it limits the user-mode handles to 0x1df2. From the following picture, you can see this limitation on handles and their meanings.

IDA Decompiled Source

In the case of kernel attributes, it limits the handle to the following values.

IDA Decompiled Source

As you can see, you don’t have access to OBJ_KERNEL_HANDLE and OBJ_VALID_ATTRIBUTES in user-mode and also some undocumented values 0x11800 which is not revealed by Microsoft.

In PsOpenProcess, the next check is for SeDebugPrivilege. As you might know, this is one of the powerful privileges in Windows that causes to bypass any Privilege checks and give the needed accesses directly. You might see it in tools like Mimikatz. It then passes it to the SePrivilegedServiceAuditAlarm. SePrivilegedServiceAuditAlarm is to be called whenever a privileged system service is attempted.

IDA Decompiled Source

Finally, PsOpenProcess calls ObOpenObjectByPointer.

ObOpenObjectByPointer

In order to explain about ObOpenObjectByPointer, First, we have to know about two structures “ACCESS_STATE” and “SECURITY_SUBJECT_CONTEXT”.

SECURITY_SUBJECT_CONTEXT

The SECURITY_SUBJECT_CONTEXT is used to capture the subject security context for access validation and auditing. It’s like dumping a special context’s token, then lock it in order to avoid any modification and finally do some privilege checks.

Functions like SeCaptureSubjectContext or SeCaptureSubjectContextEx return a pointer to this structure.

For example, the following code shows how this structure can be used to do some privilege checks.

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
+
BOOLEAN HasPrivilege(IN PPRIVILEGE_SET Privilege)
+{
+	BOOLEAN Result;
+	SECURITY_SUBJECT_CONTEXT SubjectContext;
+
+	/* Capture and lock the security subject context */
+	SeCaptureSubjectContext(&SubjectContext);
+	SeLockSubjectContext(&SubjectContext);
+
+	/* Do privilege check */
+	Result = SePrivilegeCheck(Privilege, &SubjectContext, UserMode);
+
+	/* Audit the privilege */
+	SePrivilegeObjectAuditAlarm(NULL,
+		&SubjectContext,
+		0,
+		Privilege,
+		Result,
+		UserMode);
+
+
+	/* Unlock and release the security subject context and return */
+	SeUnlockSubjectContext(&SubjectContext);
+	SeReleaseSubjectContext(&SubjectContext);
+	return Result;
+}
+

This structure is important because as James Forshaw mentioned in 3rd part of his post about “AppLocker internals” :

A Windows access check takes 4 main parameters:

  • SECURITY_SUBJECT_CONTEXT which identifies the caller’s access tokens.
  • A desired access mask.
  • GENERIC_MAPPING structure which allows the access check to convert generic access to object-specific access rights.
  • And most importantly, the Security Descriptor which describes the security of the resource being checked.

SECURITY_SUBJECT_CONTEXT is coming from two above mentioned functions, DesiredAccess is also a parameter to ObOpenObjectByPointer and GenericMapping comes from _OBJECT_TYPE’s _OBJECT_TYPE_INITIALIZER+0x4c (I’ll describe about Object Types later in this post.) while security descriptor can be derived from the _OBJECT_HEADER’s +0x28 and object header is also the first parameter to ObOpenObjectByPointer.

1
+
   +0x028 SecurityDescriptor : Ptr64 Void
+

ACCESS_STATE

As the MSDN describes, The ACCESS_STATE structure describes the state of an access in progress. It contains an object’s subject context, remaining desired access types, granted access types, and, optionally, a privilege set to indicate which privileges were used to permit the access.

Now, let’s return to ObOpenObjectByPointer, This function checks whether the caller passes an access state if not then it creates a new one based on the desired access and the object type’s generic mapping by calling SepCreateAccessStateFromSubjectContext , as the name implies it receives an ACCESS_STATE from the SECURITY_SUBJECT_CONTEX.

Eventually, it checks whether the the function itself (ObOpenObjectByPointer) creates ACCESS_STATE or not. If it creates, then it deletes the ACCESS_STATE and SECURITY_SUBJECT_CONTEXT using SepDeleteAccessState and SeReleaseSubjectContext.

Finally, this function calls the popular ObpCreateHandle which creates the handle.

IDA Decompiled Source

ObpCreateHandle

For creating a handle, an undocumented function “ObpCreateHandle” is responsible for creating a new handle to an existing object. Generally, ObpCreateHandle creates an entry in the process’ handle table that becomes associated with the object.

ObpCreateHandle defines like this: (there are some differences between current definition and WRK’s definition).

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
__int64 __fastcall ObpCreateHandle(
+ _OB_OPEN_REASON OpenReason,
+ void *Object,
+ unsigned int DesiredAccess,
+ _ACCESS_STATE *AccessState,
+ unsigned int ObjectPointer,
+ unsigned int Attribute,
+ char AccessMode,
+ struct _OBJECT_CREATE_INFO *CreateInfo,
+ int AccessMask2,
+ PVOID *NewObject,
+ PVOID *Handle);
+

The first argument to this function is _OB_OPEN_REASON which defines like this :

1
+2
+3
+4
+5
+6
+7
+8
+
typedef enum _OB_OPEN_REASON
+{
+         ObCreateHandle = 0,
+         ObOpenHandle = 1,
+         ObDuplicateHandle = 2,
+         ObInheritHandle = 3,
+         ObMaxOpenReason = 4
+} OB_OPEN_REASON;
+

From the above structure, you can see the cases where ObpCreateHandle might be used for.

If the handle is requested from kernel-mode then “ObpKernelHandleTable” is used as the handle table and if it’s a user-mode application then it calls ObReferenceProcessHandleTable.

IDA Decompiled Source

The above function (ObReferenceProcessHandleTable), first checks whether it can acquire RundownProtect or not. When run-down protection is in effect, the driver can safely access the object without the risk that the object will be deleted before the access completes. You can imagine that if you use ExAcquireRundownProtection on this field (RundownProtect) then each attempt to create a handle by the special process will cause a 0xC000010A error (An attempt was made to access an exiting process.).

This function finally returns Process->ObjectTable.

In the end, ObpCreateHandle calls ExReleaseRundownProtection which releases the RundownProtect of our process.

After that, ObpCreateHandle calls some undocumented Callbacks (SecurityProcedure). I’ll give a detailed explanation about these kinds of callbacks later on this topic but for now, it first checks whether SecurityProcedure is SeDefaultObjectMethod or not. SeDefaultObjectMethod is the default security method for objects. It is responsible for either retrieving, setting, and deleting the security descriptor of an object. It is not used to assign the original security descriptor to an object and as you can see if our callback fails with {buffer too small} error then it tries to call it once more so by now, you know that this callback is responsible for changing SecurityDescriptor of any object type (each object type separately using ObjectType’s SecurityProcedure).

Don’t worry if things are not clear, after reading the last part (about ObjectTypes) you can return here and read it once again and sure you’ll understand it.

IDA Decompiled Source

As you can see, these object type callbacks called for each object separately and it’s not specific to a special object (e.g Process, Thread, or Desktop objects).

After making SecurityDescriptor ready, it’s time to perform security checks, ObpCreateHandle calls SeAccessCheck. The SeAccessCheck routine determines whether the requested access rights can be granted to an object protected by a security descriptor and an object owner so ObpCreateHandle passes the SecurityDescriptor of object and AccessState that we have from the previous function to see if the access is granted or not.

As I told you above, ObpCreateHandle calls ObpCallPreOperationCallbacks and this function is responsible for calling callbacks that are registered by ObRegisterCallbacks but in contrast with above callbacks, these callbacks are limited to some object types (e.g Process, Thread, or Desktop).

This limitation is done by the following check, which checks whether object type supports callbacks and if there is any callback registered.

Later, we have a section called “Finding Types which support callback”, it describes how to find these object types but keep in mind, if you set Support Callback bit of an object manually, then PatchGuard comes in and leads to a BSOD.

IDA Decompiled Source

In order to assign a handle, it first acquires a lock to HANDLE_TABLE.HandleTableLock and then search through HANDLE_TABLE.FreeLists. If there isn’t any empty place in our handle table (based on NextHandleNeedingPool index) then it tries to allocate a new handle table entry for the specified handle table using ExpAllocateHandleTableEntrySlow.

Now, it’s time to compute the handle’s value.

IDA Decompiled Source

When the handle is computed, it calls ExpSetHandleExtraInfo which gets the HandleTable and Handle and sets the _HANDLE_TABLE_ENTRY_INFO to the handle entry.

1
+2
+3
+4
+5
+
struct _HANDLE_TABLE_ENTRY_INFO
+{
+  unsigned int AuditMask;
+  unsigned int MaxRelativeAccessMask;
+};
+

ExpSetHandleExtraInfo is used to set AuditMask and AccessMask to the handle and you can see that it removes the handle if the above function failed (using ExpFreeHandleTableEntry).

And finally, it sets the handle value :

1
+2
+3
+4
+
  HandleAddress = ___MainHandle | 0xFFFFFFFF80000000ui64;
+  if ( !IsKernelHandle )
+    HandleAddress = ___MainHandle;
+  *_Handle = HandleAddress;
+

As you can see, if the handle is user-mode handle then it strips the kernel address bit otherwise it’s a kernel handle and address itself is the handle.

This behavior will be changed in the future if Windows starts supporting Supervisor Mode Access Prevention (SMAP) because as long as they use this protection, they won’t be able to directly write on user-mode addresses and they have to execute extra instruction for this purpose.

The last step is calling ObpPostInterceptHandleCreate this function calls ObpCallPostOperationCallbacks and its responsible for calling Post Operation Callbacks.

Animmmeeeee :)

ObjectTypes in Windows

If you’d ever used functions like ObReferenceObjectByHandle, ObReferenceObjectByPointer, ObOpenObjectByPointer or other functions then you’ve probably heard of POBJECT_TYPE.

Also, creation routines for the various objects, like IoCreateDriver or PspCreateProcess, call the generic ObCreateObject and pass it a pointer to an appropriate _OBJECT_TYPE structure.

_OBJECT_TYPE is one of the important structures in Windows that stores the definition of different object like Process, Thread, Mutex, etc. If you want to know the difference between NT objects and non-objects you can read the article “What is POBJECT_TYPE?”.

Let’s see the definition:

(I dumped the structures using pdbex written by one of my best friends Petr Benes)

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+
+typedef struct _OBJECT_TYPE
+{
+  /* 0x0000 */ struct _LIST_ENTRY TypeList;
+  /* 0x0010 */ struct _UNICODE_STRING Name;
+  /* 0x0020 */ void* DefaultObject;
+  /* 0x0028 */ unsigned char Index;
+  /* 0x0029 */ char Padding_1[3];
+  /* 0x002c */ unsigned long TotalNumberOfObjects;
+  /* 0x0030 */ unsigned long TotalNumberOfHandles;
+  /* 0x0034 */ unsigned long HighWaterNumberOfObjects;
+  /* 0x0038 */ unsigned long HighWaterNumberOfHandles;
+  /* 0x003c */ long Padding_2;
+  /* 0x0040 */ struct _OBJECT_TYPE_INITIALIZER TypeInfo;
+  /* 0x00b8 */ struct _EX_PUSH_LOCK TypeLock;
+  /* 0x00c0 */ unsigned long Key;
+  /* 0x00c4 */ long Padding_3;
+  /* 0x00c8 */ struct _LIST_ENTRY CallbackList;
+} OBJECT_TYPE, *POBJECT_TYPE; /* size: 0x00d8 */
+

Finding Process Object Type

I’ll talk about some important fields later in this post but for now, let’s see how we can find the _OBJECT_TYPE of a specific object, let say process, first find all the processes EPROCESS.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
kd> !dml_proc
+Address           PID  Image file name
+ffffd689`fd06b380 4    System         
+ffffd689`fd0ad080 44   Registry       
+ffffd68a`01875040 24c  smss.exe       
+ffffd68a`07bf74c0 2a4  csrss.exe      
+ffffd68a`0779c080 304  wininit.exe    
+ffffd68a`077ae140 30c  csrss.exe      
+ffffd68a`07b21080 33c  winlogon.exe   
+ffffd68a`07b5c100 378  services.exe   
+ffffd68a`07b5d080 380  lsass.exe      
+ffffd68a`017a02c0 3dc  svchost.exe  
+	....
+

I choose lsass.exe (ffffd68a07b5d080) as the target. As you might know, Windows saves each object (e.g _EPROCESS) like this :

  1. _POOL_HEADER
  2. _OBJECT_QUOTA_CHARGES (optional)
  3. _OBJECT_HANDLE_DB (optional)
  4. _OBJECT_NAME (optional)
  5. _OBJECT_CREATOR_INFO (optional)
  6. _OBJECT_HEADER
  7. object body (e.g _EPROCESS)

So if we subtract sizeof(_OBJECT_HEADER) from the EPROCESS we’ll reach to the _OBJECT_HEADER of this object. (you can perform the same thing for _POOL_HEADER too.)

The object header is like this :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
kd> dt nt!_OBJECT_HEADER
+   +0x000 PointerCount     : Int8B
+   +0x008 HandleCount      : Int8B
+   +0x008 NextToFree       : Ptr64 Void
+   +0x010 Lock             : _EX_PUSH_LOCK
+   +0x018 TypeIndex        : UChar
+   +0x019 TraceFlags       : UChar
+   +0x019 DbgRefTrace      : Pos 0, 1 Bit
+   +0x019 DbgTracePermanent : Pos 1, 1 Bit
+   +0x01a InfoMask         : UChar
+   +0x01b Flags            : UChar
+   +0x01b NewObject        : Pos 0, 1 Bit
+   +0x01b KernelObject     : Pos 1, 1 Bit
+   +0x01b KernelOnlyAccess : Pos 2, 1 Bit
+   +0x01b ExclusiveObject  : Pos 3, 1 Bit
+   +0x01b PermanentObject  : Pos 4, 1 Bit
+   +0x01b DefaultSecurityQuota : Pos 5, 1 Bit
+   +0x01b SingleHandleEntry : Pos 6, 1 Bit
+   +0x01b DeletedInline    : Pos 7, 1 Bit
+   +0x01c Reserved         : Uint4B
+   +0x020 ObjectCreateInfo : Ptr64 _OBJECT_CREATE_INFORMATION
+   +0x020 QuotaBlockCharged : Ptr64 Void
+   +0x028 SecurityDescriptor : Ptr64 Void
+   +0x030 Body             : _QUAD
+

And the sizeof is :

1
+2
+
kd> ?? sizeof(nt!_OBJECT_HEADER)
+unsigned int64 0x38
+

but wait, we’re in top of the object’s Body (means that EPROCESS or whatever starts at _OBJECT_HEADER+0x30) so we have to subtract -0x30 from the EPROCESS (ffffd68a`07b5d080-0x30 = ffffd68a07b5d050) .

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+
kd> dt ffffd68a07b5d050 nt!_OBJECT_HEADER
+   +0x000 PointerCount     : 0n458365
+   +0x008 HandleCount      : 0n14
+   +0x008 NextToFree       : 0x00000000`0000000e Void
+   +0x010 Lock             : _EX_PUSH_LOCK
+   +0x018 TypeIndex        : 0x7a 'z'
+   +0x019 TraceFlags       : 0 ''
+   +0x019 DbgRefTrace      : 0y0
+   +0x019 DbgTracePermanent : 0y0
+   +0x01a InfoMask         : 0x88 ''
+   +0x01b Flags            : 0 ''
+   +0x01b NewObject        : 0y0
+   +0x01b KernelObject     : 0y0
+   +0x01b KernelOnlyAccess : 0y0
+   +0x01b ExclusiveObject  : 0y0
+   +0x01b PermanentObject  : 0y0
+   +0x01b DefaultSecurityQuota : 0y0
+   +0x01b SingleHandleEntry : 0y0
+   +0x01b DeletedInline    : 0y0
+   +0x01c Reserved         : 0
+   +0x020 ObjectCreateInfo : 0xfffff803`80468240 _OBJECT_CREATE_INFORMATION
+   +0x020 QuotaBlockCharged : 0xfffff803`80468240 Void
+   +0x028 SecurityDescriptor : 0xffffb082`5308716c Void
+   +0x030 Body             : _QUAD
+

You can also confirm this by looking at the “ObjectHeader” field of “!object” command.

1
+2
+3
+4
+
kd> !object ffffd68a`07b5d080
+Object: ffffd68a07b5d080  Type: (ffffd689fd09a4e0) Process
+    ObjectHeader: ffffd68a07b5d050 (new version)
+    HandleCount: 14  PointerCount: 458365
+

In Windows, each object is derived from a special type and even each type (like process, thread, token) derived from another type called “type”.

Let’s see.

In the above code you can see there is a “Type: (ffffd689fd09a4e0) Process” if we map this to nt!_OBJECT_TYPE, we can it’s “Name” which is “Process”.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
kd> dt nt!_OBJECT_TYPE ffffd689`fd09a4e0
+   +0x000 TypeList         : _LIST_ENTRY [ 0xffffd689`fd09a4e0 - 0xffffd689`fd09a4e0 ]
+   +0x010 Name             : _UNICODE_STRING "Process"
+   +0x020 DefaultObject    : (null) 
+   +0x028 Index            : 0x7 ''
+   +0x02c TotalNumberOfObjects : 0x85
+   +0x030 TotalNumberOfHandles : 0x45b
+   +0x034 HighWaterNumberOfObjects : 0x97
+   +0x038 HighWaterNumberOfHandles : 0x518
+   +0x040 TypeInfo         : _OBJECT_TYPE_INITIALIZER
+   +0x0b8 TypeLock         : _EX_PUSH_LOCK
+   +0x0c0 Key              : 0x636f7250
+   +0x0c8 CallbackList     : _LIST_ENTRY [ 0xffffb082`4f982140 - 0xffffb082`4f8f5fb0 ]
+

This _OBJECT_TYPE (process) is also mapped to a global variable called “nt!PsProcessType” (Do you remember we filled OB_OPERATION_REGISTRATION.ObjectType with PsProcessType and PsThreadType ?) and These global variables are OBJECT_TYPE**, not just single indirection pointers.

1
+2
+
kd> dq nt!PsProcessType L1
+fffff803`8056f390  ffffd689`fd09a4e0
+

TypeIndex in Object Types

Another important field from _OBJECT_HEADER is TypeIndex.

Let’s review this line again :

1
+
   +0x018 TypeIndex        : 0x7a 'z'
+

This field didn’t exist in until Windows Seven, means that before Windows Seven each _OBJECT_HEADER has a field called “Type” which was a pointer to its “_OBJECT_TYPE”, that’s the reason for (old version) and (new version) in !object’s results. You can read more about it in the article in CodeMachine.

But in the newer versions of Windows (> Windows 7) you can see TypeIndex. Instead of pointing directly to the OBJECT_TYPE data structure, the object header now contains an index into a new global data structure nt!ObTypeIndexTable, which is an array of pointers to the different OBJECT_TYPE structures.

The following command is used to get the _OBJECT_TYPE of target index, note that in this example 0x7 is index and @$ptrsize is defined by Windbg which shows either your pointer are 8 Bytes (x64) or 4 Bytes (x86).

1
+
kd> dt nt!_OBJECT_TYPE poi(nt!ObTypeIndexTable+(0x7*@$ptrsize)) 
+

You can see the result here :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
kd> dt nt!_OBJECT_TYPE poi(nt!ObTypeIndexTable+(0x7*@$ptrsize)) 
+   +0x000 TypeList         : _LIST_ENTRY [ 0xffffd689`fd09a4e0 - 0xffffd689`fd09a4e0 ]
+   +0x010 Name             : _UNICODE_STRING "Process"
+   +0x020 DefaultObject    : (null) 
+   +0x028 Index            : 0x7 ''
+   +0x02c TotalNumberOfObjects : 0x85
+   +0x030 TotalNumberOfHandles : 0x45b
+   +0x034 HighWaterNumberOfObjects : 0x97
+   +0x038 HighWaterNumberOfHandles : 0x518
+   +0x040 TypeInfo         : _OBJECT_TYPE_INITIALIZER
+   +0x0b8 TypeLock         : _EX_PUSH_LOCK
+   +0x0c0 Key              : 0x636f7250
+   +0x0c8 CallbackList     : _LIST_ENTRY [ 0xffffb082`4f982140 - 0xffffb082`4f8f5fb0 ]
+

But wait, in or example we see that (TypeIndex : 0x7a) and its index is not 0x7a! It turns out that in Windows 10 they decided to not directly point to the index (why?) of nt!ObTypeIndexTable instead you have to do some XORs in order to find the right index.

Update 2 :

Take a look at the following slides from NTarakanov :

[http://www.powerofcommunity.net/poc2018/nikita.pdf]

The reason why they XORed TypeIndex with nt!ObHeaderCookie is the fact that it’s possible to modify the nt!_OBJECT_HEADER.TypeIndex of each object (for example in the case of a pool overflow), And then it would be possible to point to other _OBJECT_TYPEs like ALPC_OBJECT and trigger this vulnerability (pool overflow) as it was possible to control the behavior of callbacks in these objects.

Pool over flow (DKOHM)

The following picture is copied from this post which describes how he understands it by reversing nt!ObGetObjectType , you can do the same thing and it works.

Find TypeIndex in Win 10

Keep in mind that the second member of nt!ObTypeIndexTable is “Type”. If you get the _OBJECT_HEADER of Process’s _OBJECT_TYPE itself, then you’ll reach to the following _OBJECT_TYPE .

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
kd> dt nt!_OBJECT_TYPE poi(nt!ObTypeIndexTable+((0x2)*@$ptrsize)) 
+   +0x000 TypeList         : _LIST_ENTRY [ 0xffffd689`fd09acd0 - 0xffffd68a`01413750 ]
+   +0x010 Name             : _UNICODE_STRING "Type"
+   +0x020 DefaultObject    : 0xfffff803`80442780 Void
+   +0x028 Index            : 0x2 ''
+   +0x02c TotalNumberOfObjects : 0x43
+   +0x030 TotalNumberOfHandles : 0
+   +0x034 HighWaterNumberOfObjects : 0x43
+   +0x038 HighWaterNumberOfHandles : 0
+   +0x040 TypeInfo         : _OBJECT_TYPE_INITIALIZER
+   +0x0b8 TypeLock         : _EX_PUSH_LOCK
+   +0x0c0 Key              : 0x546a624f
+   +0x0c8 CallbackList     : _LIST_ENTRY [ 0xffffd689`fd09ade8 - 0xffffd689`fd09ade8 ]
+

Now that we know what the object type is, it’s time to dig deeper into Windows _OBJECT_TYPEs and find all of them.

Finding All Windows _OBJECT_TYPE(s)

The first and easiest way is using Windbg’s “!object \ObjectTypes” or using tools like SysInternals’ WinObj.

WinObj

Using windbg , you’ll get the following results but the result of my tests shows that this command’s results is not complete, that’s why we need a third way to explore the kernel types. WinObj also won’t show a complete result.

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
+
kd> !object \ObjectTypes
+Object: ffffb0824ea08700  Type: (ffffd689fd09a900) Directory
+    ObjectHeader: ffffb0824ea086d0 (new version)
+    HandleCount: 0  PointerCount: 69
+    Directory Object: ffffb0824ea07cb0  Name: ObjectTypes
+
+    Hash Address          Type                      Name
+    ---- ------- ---- ----
+     00  ffffd689fd0f5640 Type                      TmTm
+     01  ffffd689fd094560 Type                      Desktop
+         ffffd689fd09a4e0 Type                      Process
+     02  ffffd689fd0f6400 Type                      EnergyTracker
+         ffffd689fd0f6980 Type                      RegistryTransaction
+     03  ffffd689fd093640 Type                      DebugObject
+     04  ffffd68a014137a0 Type                      VRegConfigurationContext
+         ffffd689fd0f50c0 Type                      TpWorkerFactory
+     05  ffffd689fd0f6ae0 Type                      Adapter
+         ffffd689fd09a220 Type                      Token
+     06  ffffd689fd0f9900 Type                      DxgkSharedResource
+     07  ffffd689fd0934e0 Type                      PsSiloContextPaged
+     08  ffffd689fd0fa400 Type                      NdisCmState
+         ffffd689fd0937a0 Type                      ActivityReference
+     09  ffffd689fd0faf00 Type                      PcwObject
+         ffffd689fd0f5bc0 Type                      WmiGuid
+     11  ffffd689fd0f90c0 Type                      DmaAdapter
+         ffffd689fd0f5e80 Type                      EtwRegistration
+     12  ffffd689fd0f9640 Type                      DxgkSharedBundleObject
+         ffffd689fd0f5900 Type                      Session
+         ffffd689fd093d20 Type                      RawInputManager
+         ffffd689fd093900 Type                      Timer
+     13  ffffd689fd094820 Type                      Mutant
+     14  ffffd689fd093a60 Type                      IRTimer
+     16  ffffd689fd0fa980 Type                      DxgkCurrentDxgProcessObject
+         ffffd689fd0f6c40 Type                      IoCompletion
+     17  ffffd689fd0f94e0 Type                      DxgkSharedProtectedSessionObject
+         ffffd689fd0fa560 Type                      DxgkSharedSyncObject
+         ffffd689fd093220 Type                      WindowStation
+         ffffd689fd094ae0 Type                      Profile
+     18  ffffd689fd0f6560 Type                      File
+     20  ffffd689fd0930c0 Type                      Partition
+     21  ffffd689fd0f9e80 Type                      DxgkSharedKeyedMutexObject
+         ffffd689fd0946c0 Type                      ActivationObject
+         ffffd689fd093380 Type                      Semaphore
+	
+	...
+         ffffd689fd09ad20 Type                      Type
+	...
+

The third method for getting objects is interpreting Windows structures manually using Windbg. Before further investigation we have to find the “Type“‘s _OBJECT_TYPE (e.g a process type itself is a “Type”), for this purpose first we have to find Process’s _OBJECT_TYPE :

1
+2
+3
+4
+5
+
kd> x nt!PsProcessType
+fffff803`8056f390 nt!PsProcessType = <no type information>
+
+kd> dq fffff803`8056f390 l1
+fffff803`8056f390  ffffd689`fd09a4e0
+

Using !object we can find its type :

1
+2
+3
+4
+5
+
kd> !object ffffd689`fd09a4e0
+Object: ffffd689fd09a4e0  Type: (ffffd689fd09ad20) Type
+    ObjectHeader: ffffd689fd09a4b0 (new version)
+    HandleCount: 0  PointerCount: 2
+    Directory Object: ffffb0824ea08700  Name: Process
+

you can also find the “Type“‘s location using “!object \ObjectTypes”.

Now that we have a pointer to “Type“‘s _OBJECT_TYPE ( = ffffd689fd09ad20), it’s time to find other types, _OBJECT_TYPE itself has a “TypeList” but doesn’t seem to be a list to Object Types, you can traverse it, it won’t give a valid result. If you remember from the first part of this post, I told you the order when Windows allocates the objects (search for “ Windows saves each object” in this post and see it again.). It turns out that above the Type’s _OBJECT_HEADER, there is another optional structure, called “_OBJECT_HEADER_CREATOR_INFO”, let’s see what’s the definition of this structure.

1
+2
+3
+4
+5
+6
+7
+8
+9
+
kd> dt nt!_OBJECT_HEADER_CREATOR_INFO
+   +0x000 TypeList         : _LIST_ENTRY
+   +0x010 CreatorUniqueProcess : Ptr64 Void
+   +0x018 CreatorBackTraceIndex : Uint2B
+   +0x01a Reserved1        : Uint2B
+   +0x01c Reserved2        : Uint4B
+
+kd> ?? sizeof(nt!_OBJECT_HEADER_CREATOR_INFO)
+unsigned int64 0x20
+

As I told you, _OBJECT_HEADER_CREATOR_INFO is above the _OBJECT_HEADER so we need a little calculation here, sizeof( _OBJECT_HEADER_CREATOR_INFO) = 0x20 and for reaching to the _OBJECT_HEADER we have to subtract the pointer (ffffd689fd09ad20) by 0x30.

ffffd689fd09ad20 - 0x30 - 0x20 = FFFFD689FD09ACD0‬ => Pointer to _OBJECT_HEADER_CREATOR_INFO

The TypeList in _OBJECT_HEADER_CREATOR_INFO is _LIST_ENTRY which shows us all the Types. We’ll use Windbg to traverse through the list.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
kd> dt FFFFD689FD09ACD0 nt!_OBJECT_HEADER_CREATOR_INFO -l TypeList.Flink -y TypeList
+TypeList.Flink at 0xffffd689`fd09acd0
+---------------------------------------------
+   +0x000 TypeList : _LIST_ENTRY [ 0xffffd689`fd09a8b0 - 0xffffd689`fd09ad20 ]
+
+TypeList.Flink at 0xffffd689`fd09a8b0
+---------------------------------------------
+   +0x000 TypeList : _LIST_ENTRY [ 0xffffd689`fd09a070 - 0xffffd689`fd09acd0 ]
+
+TypeList.Flink at 0xffffd689`fd09a070
+---------------------------------------------
+   +0x000 TypeList : _LIST_ENTRY [ 0xffffd689`fd09a1d0 - 0xffffd689`fd09a8b0 ]
+
+TypeList.Flink at 0xffffd689`fd09a1d0
+---------------------------------------------
+   +0x000 TypeList : _LIST_ENTRY [ 0xffffd689`fd09a330 - 0xffffd689`fd09a070 ]
+...
+

You can see a pointer to all of the Types (of course from _OBJECT_HEADER_CREATOR_INFO). Let’s verify we found them correctly by looking at their names (Don’t forget we have to add 0x20 and ox30 to reach the start of the _OBJECT_TYPE).

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
+
kd> dt nt!_OBJECT_TYPE  0xffffd689`fd09acd0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Type"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd09a8b0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Directory"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd09a070+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "SymbolicLink"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd09a1d0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Token"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd09a330+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Job"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd09a490+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Process"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0943b0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Thread"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd093070+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Partition"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0940f0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "UserApcReserve"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd094930+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "IoCompletionReserve"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd093750+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "ActivityReference"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd093490+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "PsSiloContextPaged"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd094250+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "PsSiloContextNonPaged"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0935f0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "DebugObject"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd094eb0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Event"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0947d0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Mutant"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd094bf0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Callback"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd093330+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Semaphore"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0938b0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Timer"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd093a10+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "IRTimer"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd094a90+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Profile"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd094d50+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "KeyedEvent"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0931d0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "WindowStation"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd094510+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Desktop"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd093b70+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Composition"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd093cd0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "RawInputManager"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd093e30+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "CoreMessaging"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd094670+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "ActivationObject"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f5070+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "TpWorkerFactory"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f6a90+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Adapter"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f5490+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Controller"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f5330+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Device"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f5750+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Driver"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f6bf0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "IoCompletion"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f51d0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "WaitCompletionPacket"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f6510+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "File"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f55f0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "TmTm"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f60f0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "TmTx"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f6670+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "TmRm"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f6eb0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "TmEn"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f6d50+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Section"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f58b0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Session"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f5a10+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Key"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f6930+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "RegistryTransaction"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f6250+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "ALPC Port"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f63b0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "EnergyTracker"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f67d0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "PowerRequest"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f5b70+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "WmiGuid"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f5e30+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "EtwRegistration"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0fa0f0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "EtwSessionDemuxEntry"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0fabf0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "EtwConsumer"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f9cd0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "CoverageSampler"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f9070+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "DmaAdapter"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0faeb0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "PcwObject"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0fad50+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "FilterCommunicationPort"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0fa250+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "FilterConnectionPort"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0fa3b0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "NdisCmState"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0fad50+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "FilterCommunicationPort"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0fa3b0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "NdisCmState"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f98b0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "DxgkSharedResource"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f9e30+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "DxgkSharedKeyedMutexObject"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0fa510+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "DxgkSharedSyncObject"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0fa670+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "DxgkSharedSwapChainObject"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0fa7d0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "DxgkDisplayManagerObject"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0fa930+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "DxgkCurrentDxgProcessObject"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f9490+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "DxgkSharedProtectedSessionObject"
+kd> dt nt!_OBJECT_TYPE  0xffffd68a`01413750+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "VRegConfigurationContext"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f95f0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "DxgkSharedBundleObject"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd09ad20+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "--- memory read error at address 0x00000130`00000000 ---"
+kd> dt nt!_OBJECT_TYPE  0xffffd689`fd0f9750+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "DxgkCompositionObject"
+

There is also another way that you can enumerate the above list by using the following command (thanks to Bruce Dang for mentioning it):

1
+
dx Debugger.Utility.Collections.FromListEntry( (*(nt!_OBJECT_TYPE **)&nt!ObpTypeObjectType)->TypeList, "nt!_OBJECT_TYPE", "TypeList").Select(o => (nt!_OBJECT_HEADER*)((unsigned char *)&o + 0x20)).Select( o => o->ObjectName)
+

The above result contains more types than using “!object \ObjectTypes” (why?) in my case, it finds 70 types while “!object \ObjectTypes” gives only 67 types !!!

Update 1: As Alex mentioned, I got more objects because I copy-pasted FilterCommunicationPort and NdisCmState twice.

Update 1: Also, “!object 0 Type” is a built-in way of enumerating the creator info list.

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
+
kd> !object 0 Type
+Scanning 67 objects of type 'Type'
+Object: ffffbc05a907b4e0  Type: (ffffbc05a907b4e0) Type
+    ObjectHeader: ffffbc05a907b4b0 (new version)
+    HandleCount: 0  PointerCount: 2
+    Directory Object: ffff9f02dca02d90  Name: Type
+Optional Headers: 
+    CreatorInfo(ffffbc05a907b4b0): Process: 0 BackTraceIndex: 58
+    NameInfo(ffffbc05a907b4b0)
+Object: ffffbc05a907b640  Type: (ffffbc05a907b4e0) Type
+    ObjectHeader: ffffbc05a907b610 (new version)
+    HandleCount: 0  PointerCount: 2
+    Directory Object: ffff9f02dca02d90  Name: Directory
+Optional Headers: 
+    CreatorInfo(ffffbc05a907b610): Process: 0 BackTraceIndex: 5a
+    NameInfo(ffffbc05a907b610)
+Object: ffffbc05a907b380  Type: (ffffbc05a907b4e0) Type
+    ObjectHeader: ffffbc05a907b350 (new version)
+    HandleCount: 0  PointerCount: 2
+    Directory Object: ffff9f02dca02d90  Name: SymbolicLink
+
+...
+
+Object: ffffbc05a907b0c0  Type: (ffffbc05a907b4e0) Type
+    ObjectHeader: ffffbc05a907b090 (new version)
+    HandleCount: 0  PointerCount: 2
+    Directory Object: ffff9f02dca02d90  Name: Job
+Optional Headers: 
+

Now that we have the pointers to all of the types, it’s time to investigate through each _OBJECT_TYPE(s).

Finding Types which support callback

In _OBJECT_TYPE there is a structure called “_OBJECT_TYPE_INITIALIZER”. It defines like this :

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
+
kd> dt nt!_OBJECT_TYPE_INITIALIZER
+   +0x000 Length           : Uint2B
+   +0x002 ObjectTypeFlags  : Uint2B
+   +0x002 CaseInsensitive  : Pos 0, 1 Bit
+   +0x002 UnnamedObjectsOnly : Pos 1, 1 Bit
+   +0x002 UseDefaultObject : Pos 2, 1 Bit
+   +0x002 SecurityRequired : Pos 3, 1 Bit
+   +0x002 MaintainHandleCount : Pos 4, 1 Bit
+   +0x002 MaintainTypeList : Pos 5, 1 Bit
+   +0x002 SupportsObjectCallbacks : Pos 6, 1 Bit
+   +0x002 CacheAligned     : Pos 7, 1 Bit
+   +0x003 UseExtendedParameters : Pos 0, 1 Bit
+   +0x003 Reserved         : Pos 1, 7 Bits
+   +0x004 ObjectTypeCode   : Uint4B
+   +0x008 InvalidAttributes : Uint4B
+   +0x00c GenericMapping   : _GENERIC_MAPPING
+   +0x01c ValidAccessMask  : Uint4B
+   +0x020 RetainAccess     : Uint4B
+   +0x024 PoolType         : _POOL_TYPE
+   +0x028 DefaultPagedPoolCharge : Uint4B
+   +0x02c DefaultNonPagedPoolCharge : Uint4B
+   +0x030 DumpProcedure    : Ptr64     void 
+   +0x038 OpenProcedure    : Ptr64     long 
+   +0x040 CloseProcedure   : Ptr64     void 
+   +0x048 DeleteProcedure  : Ptr64     void 
+   +0x050 ParseProcedure   : Ptr64     long 
+   +0x050 ParseProcedureEx : Ptr64     long 
+   +0x058 SecurityProcedure : Ptr64     long 
+   +0x060 QueryNameProcedure : Ptr64     long 
+   +0x068 OkayToCloseProcedure : Ptr64     unsigned char 
+   +0x070 WaitObjectFlagMask : Uint4B
+   +0x074 WaitObjectFlagOffset : Uint2B
+   +0x076 WaitObjectPointerOffset : Uint2B
+

It contains lots of important fields, for example, “SupportsObjectCallbacks” shows whether the object supports callbacks or not. _OBJECT_TYPE_INITIALIZER starts after 0x40 from the _OBJECT_TYPE so let’s find the objects that support callbacks from the _OBJECT_TYPES that we previously gathered. (Do you remember ObpCallPreOperationCallbacks when we reversed ObpCreateHandle? :) )

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
+
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd09acd0+20+30+40 -b -y SupportsObjectCallbacks
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd09a8b0+20+30+40 -b -y SupportsObjectCallbacks
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd09a070+20+30+40 -b -y SupportsObjectCallbacks
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd09a1d0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd09a330+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd09a490+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y1
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0943b0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y1
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd093070+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0940f0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094930+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd093750+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd093490+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094250+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0935f0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094eb0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0947d0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094bf0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd093330+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0938b0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd093a10+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094a90+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094d50+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0931d0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094510+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y1
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd093b70+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd093cd0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd093e30+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094670+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f5070+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f6a90+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f5490+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f5330+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f5750+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f6bf0+20+30+40 -b -y SupportsObjectCallbacks
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f51d0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f6510+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f55f0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f60f0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f6670+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f6eb0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f6d50+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f58b0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f5a10+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f6930+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f6250+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f63b0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f67d0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f5b70+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f5e30+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fa0f0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fabf0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f9cd0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f9070+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0faeb0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fad50+20+30+40 -b -y SupportsObjectCallbacks
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fa250+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fa3b0+20+30+40 -b -y SupportsObjectCallbacks
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fad50+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fa3b0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f98b0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f9e30+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fa510+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fa670+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fa7d0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0fa930+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f9490+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd68a`01413750+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f95f0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd09ad20+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0f9750+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y0
+

You can see that only the following three _OBJECT_TYPEs support callbacks,

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd094510+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y1
+
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd09a490+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y1
+
+kd> dt nt!_OBJECT_TYPE_INITIALIZER 0xffffd689`fd0943b0+20+30+40 -b -y SupportsObjectCallbacks 
+   +0x002 SupportsObjectCallbacks : 0y1
+
+kd> dt nt!_OBJECT_TYPE 0xffffd689`fd094510+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Desktop"
+kd> dt nt!_OBJECT_TYPE 0xffffd689`fd09a490+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Process"
+kd> dt nt!_OBJECT_TYPE 0xffffd689`fd0943b0+20+30 -y Name
+   +0x010 Name : _UNICODE_STRING "Thread"
+

The result is not really surprising, previously Alex and other friends told me that Windows 10 TH2 starts supporting “ExDesktopOObjectType”.

OperationRegisteration

There are also another important fields in _OBJECT_TYPE_INITIALIZER for example you can find the ValidAccessMask for that object or find the related functions from DumpProcedure, OpenProcedure, CloseProcedure, DeleteProcedure, ParseProcedure, ParseProcedureEx, SecurityProcedure, QueryNameProcedure, and OkayToCloseProcedure. For example in process type we have the following functions (These are the callbacks that internally used by Microsoft and it’s not revealed to drivers) :

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
    [+0x030] DumpProcedure    : 0x0 [Type: void (__cdecl*)(void *,_OBJECT_DUMP_CONTROL *)]
+    [+0x038] OpenProcedure    : 0xfffff803806b0170 [Type: long (__cdecl*)(_OB_OPEN_REASON,char,_EPROCESS *,void *,unsigned long *,unsigned long)]
+    [+0x040] CloseProcedure   : 0xfffff803806bdd50 [Type: void (__cdecl*)(_EPROCESS *,void *,unsigned __int64,unsigned __int64)]
+    [+0x048] DeleteProcedure  : 0xfffff80380664c00 [Type: void (__cdecl*)(void *)]
+    [+0x050] ParseProcedure   : 0x0 [Type: long (__cdecl*)(void *,void *,_ACCESS_STATE *,char,unsigned long,_UNICODE_STRING *,_UNICODE_STRING *,void *,_SECURITY_QUALITY_OF_SERVICE *,void * *)]
+    [+0x050] ParseProcedureEx : 0x0 [Type: long (__cdecl*)(void *,void *,_ACCESS_STATE *,char,unsigned long,_UNICODE_STRING *,_UNICODE_STRING *,void *,_SECURITY_QUALITY_OF_SERVICE *,_OB_EXTENDED_PARSE_PARAMETERS *,void * *)]
+    [+0x058] SecurityProcedure : 0xfffff803805cd360 [Type: long (__cdecl*)(void *,_SECURITY_OPERATION_CODE,unsigned long *,void *,unsigned long *,void * *,_POOL_TYPE,_GENERIC_MAPPING *,char)]
+    [+0x060] QueryNameProcedure : 0x0 [Type: long (__cdecl*)(void *,unsigned char,_OBJECT_NAME_INFORMATION *,unsigned long,unsigned long *,char)]
+    [+0x068] OkayToCloseProcedure : 0x0 [Type: unsigned char (__cdecl*)(_EPROCESS *,void *,void *,char)]
+
+kd>  u 0xfffff803806b0170 L1
+nt!PspProcessOpen:
+fffff803`806b0170 48895c2408      mov     qword ptr [rsp+8],rbx
+kd>  u 0xfffff803806bdd50 L1
+nt!PspProcessClose:
+fffff803`806bdd50 488bc4          mov     rax,rsp
+kd>  u 0xfffff80380664c00 L1
+nt!PspProcessDelete:
+fffff803`80664c00 4c8bdc          mov     r11,rsp
+kd>  u 0xfffff803805cd360 L1
+nt!SeDefaultObjectMethod:
+fffff803`805cd360 4053            push    rbx
+

Analyzing Callbacks in ObjectTypes

By now, you’re familiar with these callbacks: DumpProcedure, OpenProcedure, CloseProcedure, DeleteProcedure, ParseProcedure, ParseProcedureEx, SecurityProcedure, QueryNameProcedure, and OkayToCloseProcedure. Now let’s see which functions attempt to call them and what is the purpose of calling them.

The following definitions are the undocumented part of these callbacks.

  • DumpProcedure: Calls from nt!ObpRemoveObjectRoutine.

[Type: void (__cdecl*)(void *,_OBJECT_DUMP_CONTROL *)]

int DumpProcedure_Hook( PVOID Object, OBJECT_DUMP_CONTROL* DumpControl);

  • OpenProcedure: Calls from nt!ObpIncrementHandleCountEx and the callback target function for process is nt!PspProcessOpen.

[Type: long (__cdecl*)(_OB_OPEN_REASON,char,_EPROCESS *,void *,unsigned long *,unsigned long)]

int OpenProcedure_Hook( OB_OPEN_REASON OpenReason, CHAR AccessMode, PEPROCESS TargetProcess, PVOID Object, PULONG GrantedAccess, ULONG HandleCount);

  • CloseProcedure: Calls from nt!ObCloseHandleTableEntry and the callback target function for process is nt!PspProcessClose and for file is nt!IopCloseFile.

[Type: void (__cdecl*)(_EPROCESS *,void *,unsigned __int64,unsigned __int64)]

int CloseProcedure_Hook( PEPROCESS Process, PVOID Object, ULONG ProcessHandleCount, ULONG SystemHandleCount);

  • DeleteProcedure: Calls from nt!ObpRemoveObjectRoutine and the callback target function for process is nt!PspProcessDelete and for file is nt!IopDeleteFile.

[Type: void (__cdecl*)(void *)]

int DeleteProcedure_Hook( PVOID Object);

  • ParseProcedure & ParseProcedureEx: Calls from nt!ObpLookupObjectName and the callback target function for file is nt!IopParseFile.

[Type: long (__cdecl*)(void *,void *,_ACCESS_STATE *,char,unsigned long,_UNICODE_STRING *,_UNICODE_STRING *,void *,_SECURITY_QUALITY_OF_SERVICE *,void * )]

[Type: long (__cdecl)(void *,void *,_ACCESS_STATE *,char,unsigned long,_UNICODE_STRING *,_UNICODE_STRING *,void *,_SECURITY_QUALITY_OF_SERVICE *,_OB_EXTENDED_PARSE_PARAMETERS *,void * *)]

int ParseProcedure_Hook( PVOID ParseObject, PVOID ObjectType, PACCESS_STATE AccessState, CHAR AccessMode, ULONG Attributes, UNICODE_STRING* CompleteName, UNICODE_STRING* RemainingName, PVOID Context, SECURITY_QUALITY_OF_SERVICE* SecurityQos, OB_EXTENDED_PARSE_PARAMETERS* ExtendedParameters, PVOID* Object);

  • SecurityProcedure: Calls from nt!NtQuerySecurityObject and nt!ObpCreateHandle (Do you remeber? we see it on ObpCreateHandle and explained about its purpose) and the callback target function for file is nt!IopGetSetSecurityObject and for process is nt!SeDefaultObjectMethod.

[Type: long (__cdecl*)(void *,_SECURITY_OPERATION_CODE,unsigned long *,void *,unsigned long *,void * *,_POOL_TYPE,_GENERIC_MAPPING *,char)]

int SecurityProcedure_Hook( PVOID Object, SECURITY_OPERATION_CODE OperationCode, PULONG SecurityInformation, PVOID SecurityDescriptor, PULONG CapturedLength, PVOID* ObjectsSecurityDescriptor, POOL_TYPE PoolType, PGENERIC_MAPPING GenericMapping, CHAR Mode);

  • QueryNameProcedure: Calls from nt!ObQueryNameStringMode and target function for file is nt!IopQueryName.

[Type: long (__cdecl*)(void *,unsigned char,_OBJECT_NAME_INFORMATION *,unsigned long,unsigned long *,char)]

int QueryNameProcedure_Hook( PVOID Object, UCHAR HasObjectName, POBJECT_NAME_INFORMATION ObjectNameInfo, ULONG Length, PULONG* ReturnLength, CHAR Mode);

  • OkayToCloseProcedure: Calls from nt!ObCloseHandleTableEntry.

[Type: unsigned char (__cdecl*)(_EPROCESS *,void *,void *,char)]

int OkayToCloseProcedure_Hook( PEPROCESS Process, DWORD DW, HANDLE Handle, KPROCESSOR_MODE PreviousMode);

Using Callbacks in ObjectTypes

It’s time to use the above information to build a driver that hooks these callbacks, there is an article here from Souhail Hammou that describes the behavior of OkayToCloseProcedure.

As he describes,

The function (ObpCloseHandleTableEntry) will access the OkayToCloseProcedure field and check if it’s NULL, if that’s true the function will proceed to other checks (check if the handle is protected from being closed).

If the OkayToCloseProcedre field isn’t NULL, the function will proceed to call the callback function. If the callback function returns 0 the handle cannot be closed and ObpCloseHandleTableEntry will return STATUS_HANDLE_NOT_CLOSABLE. If it returns a value other than 0 we will proceed to the other checks as it happens when the OkayToCloseProcedure is NULL.

Now we have to create a driver which hooks all of the non-null callback procedure and also hook OkayToCloseProcedure.

Full source code of object callbacks hook is available on GitHub :

[ https://github.com/SinaKarvandi/misc/tree/master/TypeInfoCallbacksHooker ]

For this, first, you have to find a pointer the _OBJECT_TYPE structure of the object that you need to hook (e.g Process, File and etc.). For example, as I described above PsProcessType contains a pointer to the start of Process OBJECT_TYPE so in the case of hooking the processes callbacks you can use the following code.

1
+2
+3
+4
+5
+
	/* Get the Process Object Type (OBJECT_TYPE) structure */
+
+	// ProcessObjectType = ObGetObjectType(PsGetCurrentProcess());
+	ProcessObjectType = (PUCHAR)*PsProcessType;
+	DbgPrint("[*] Process Object Type Structure at : %p\n", ProcessObjectType);
+

Also, there is another option which can be used to do the same task, You can use ObGetObjectType and pass you object as its argument. For example, you can use PsGetCurrentProcess() which is a Process object or any other object. This way is more general.

For each of the callbacks, we use the following code. First, we check whether the callback is NULL or not, if it’s not null then we save the callback pointer (For future calls) then we change the address of the callback so that Windows calls our callback method first and we call the original function.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+
	/* CloseProcedure_Hook */
+	if (*(INT64*)(ProcessObjectType + 0x80) != NULL) {
+
+		// Store the previous pointer
+		*(INT64*)(CallbacksList + 0x10) = *(INT64*)(ProcessObjectType + 0x80);
+		_CloseProcedure = *(INT64*)(ProcessObjectType + 0x80);
+
+
+		// Save to pointer to new hook address
+		*(INT64*)(ProcessObjectType + 0x80) = (INT64)CloseProcedure_Hook;
+
+		DbgPrint("[*] CloseProcedure Hook Done !!\n");
+	}
+	else
+	{
+		DbgPrint("[*] CloseProcedure Hook Failed");
+	}
+

Whenever Windows calls our callback method, we have to first show the details about the object and process that causes this callback to be invoked. Then we return the result of previous callback (which Windows sets).

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
typedef int(*CloseProcedure)(PEPROCESS Process, PVOID Object, ULONG ProcessHandleCount, ULONG SystemHandleCount);
+CloseProcedure _CloseProcedure;
+
+int CloseProcedure_Hook(
+	PEPROCESS Process,
+	PVOID Object,
+	ULONG ProcessHandleCount,
+	ULONG SystemHandleCount)
+{
+	DbgPrint("[*] CloseProcedure called for object : 0x%llx, Process : %s \n", Object, (PUCHAR)Process + 0x450);
+	return _CloseProcedure(Process, Object, ProcessHandleCount, SystemHandleCount);
+}
+

Keep in mind that these modifications on callbacks are prohibited due to the presence of PatchGuard but PatchGuard won’t start in a debugged environment.

Finally, the results are:

TypeInfoCallbacksHooker Driver

Conclusion

In this post, we saw some important parts of Windows about handles, callbacks, object types and lots of other cool examples about how to use them.

The details provided in this post might be changed in the future versions of Windows, these details checked on the latest Windows 10 1903 so please don’t hesitate to correct me if you are sure something is wrong or you want to add some additional details for readers or something that changes in the future.

I’m not actively working on these series but I’ll try to post new parts as soon as possible.

That’s it guys, hope you enjoy reading this post.

Aniiiime :D

References

[1] Detecting Sysmon on the Victim Host - (https://ired.team/offensive-security/enumeration-and-discovery/detecting-sysmon-on-the-victim-host)
[2] Sysmon - (https://docs.microsoft.com/en-us/sysinternals/downloads/sysmon)
[3] Sysmon Event ID 16 - (https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=90016)
[4] Handle - (https://docs.microsoft.com/en-us/sysinternals/downloads/handle)
[5] libelevate - Bypass ObRegisterCallbacks via elevation- (https://github.com/notscimmy/libelevate)
[6] Microsoft Windows Security - (https://www.microsoftpressstore.com/articles/article.aspx?p=2228450&seqNum=3)
[7] OBJECT_ATTRIBUTES structure - (https://docs.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_object_attributes)
[8] Windows 7 Object Headers - (https://codemachine.com/article_objectheader.html)
[9] A Light on Windows 10’s “OBJECT_HEADER->TypeIndex” - (https://medium.com/@ashabdalhalim/a-light-on-windows-10s-object-header-typeindex-value-e8f907e7073a)
[10] OB_OPERATION_REGISTRATION structure - (https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_ob_operation_registration)
[11] Kernel Objects - (https://computer.forensikblog.de/en/2009/04/kernel-objects.html)
[12] Operating Offensively Against Sysmon - (https://www.darkoperator.com/blog/2018/10/5/operating-offensively-against-sysmon)
[13] DACLs and ACEs - (https://docs.microsoft.com/en-us/windows/win32/secauthz/dacls-and-aces)
[14] OkayToCloseProcedure callback kernel hook - (http://rce4fun.blogspot.com/2014/07/okaytocloseprocedure-callback-kernel_9.html)
[15] Part 1: Digging deep into LoadLibrary - (https://n4r1b.netlify.com/en/posts/2019/03/part-1-digging-deep-into-loadlibrary/)
[16] The Internals of AppLocker - Part 3 - Access Tokens and Access Checking - (https://tyranidslair.blogspot.com/2019/11/the-internals-of-applocker-part-3.html)

This post is licensed under CC BY 4.0 by the author.

Why you should not always trust MSDN: Finding Real Access Rights Needed By Handles

Hypervisor From Scratch – Part 7: Using EPT & Page-Level Monitoring Features

Comments powered by Disqus.

diff --git a/topics/simple-openldap-phpldapadmin-setup/index.html b/topics/simple-openldap-phpldapadmin-setup/index.html new file mode 100644 index 00000000..14c0ba18 --- /dev/null +++ b/topics/simple-openldap-phpldapadmin-setup/index.html @@ -0,0 +1 @@ + Simple OpenLDAP + phpLDAPadmin setup | Rayanfam Blog
Home Simple OpenLDAP + phpLDAPadmin setup
Post
Cancel

Simple OpenLDAP + phpLDAPadmin setup

Hello everyone,

In this blog post I’m going to show you how to setup a simple OpenLDAP server  with phpldapadmin on apache. I’m posting this because I didn’t find any up to date content on how to do this.

Installing OpenLDAP and exploring it with phpLDAPadmin helps you learn LDAP structure and use cases. Also you can redirect authentication of other devices on your network to this server.

These steps are run in a fresh debian wheezy (7) installation:

[click on it]

LDAP authentication can be used with a lot of services like Apache web authentication, FreeRADIUS, Cisco network devices, etc. which we will go over one by one in future posts.

Any comments are appreciated as always.

This post is licensed under CC BY 4.0 by the author.

GRE over IPSec in Cisco IOS

Fooling Windows about its internal CPU

Comments powered by Disqus.

diff --git a/topics/start-linux-kernel-module-development/index.html b/topics/start-linux-kernel-module-development/index.html new file mode 100644 index 00000000..a70cf896 --- /dev/null +++ b/topics/start-linux-kernel-module-development/index.html @@ -0,0 +1,65 @@ + Start linux kernel module development! | Rayanfam Blog
Home Start linux kernel module development!
Post
Cancel

Start linux kernel module development!

Hi everyone!

In this post I’m going to introduce you to the world of linux kernel module development. I am a newcomer in this field myself but I decided to document everything in this blog as I gradually learn them.

To start you need some sort of virtual machine. Of course you can test kernel modules on your own system but it is very risky and you can’t really debug them effectively and must use printk and debug messages to find problems in your code.

The first option is to use VirtualBox and install a linux distro in it. This way your system won’t crash and you will be safe regarding any data loss. But debugging is still not very trivial.

Second option would be using QEMU and buildroot, you can easily step-debug linux kernel code or your own code. This is the recommended method.

I have found a really nice linux+buildroot setup here. Plus some really nice tricks in linux kernel programming as the name also suggests.

Using cirosantilli buildroot

Just follow his guide on GitHub. What lies here is only some glitches I ran into while setting up the buildroot.

  • Clone the whole repository. You are better off this way.
1
+
git clone --recurse-submodules -j16 "https://github.com/cirosantilli/linux-kernel-module-cheat"
+
  • Pay attention to dependencies. strangely the configure script does not check for all of them. pay attention to error messages if you encounter any and find the appropriate package to install for your distro. (most likely you will need development version of the package too)

If you are using the buildroot mentioned then there is no need for this step. This is the minimal setup to get you started with kernel module development.

Install dependencies for compiling kernel module (if you are on Ubuntu run this):

1
+
apt-get install build-essential linux-headers-$(uname -r)
+

Your first module

Create a directory and  put this piece of code into a file (like ko_example.c):

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Shahriar EV");
+MODULE_DESCRIPTION("sample linux kernel module.");
+MODULE_VERSION("1.00");
+
+static int __init mylkm_init(void) {
+ printk(KERN_INFO "HI!\n");
+ return 0;
+}
+
+static void __exit mylkm_exit(void) {
+ printk(KERN_INFO "BYE!\n");
+}
+
+module_init(mylkm_init);
+module_exit(mylkm_exit);
+
  • The includes at the top are pretty obvious. they are required for linux kernel programming and provide us with all the functions we are using here.
  • The next block is module specs. self explanatory…
  • mylkm_init is the function which is called when module is loaded.
  • mylkm_exit is the function which is called when module is unloaded.
  • printk is used to print stuff from kernel which then can be read via dmesg.
  • KERN_INFO is severity level of the log message which comes after that.

That is enough code for now, however we need a Makefile to compile the kernel module.

Save this in a file name Makefile in the same directory as the source code:

1
+2
+3
+4
+5
+6
+7
+
obj-m += lkm_example.o
+
+all:
+ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
+
+clean:
+ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
+

Now run make and it should build successfully. To load it run (as root):

1
+
insmod ko_example.ko
+

If everything is okay, you can see “Hello World” in dmesg buffer:

1
+
dmesg
+

The module should now be visible on lsmod output.

On unloading of the kernel module, printk is called again:

1
+
rmmod ko_example.ko
+

It is very important to know that LKM developement is different from userland applications. I like to quote directly from derekmolloy.ie :

A kernel module is not an application!

do not execute sequentially— a kernel module registers itself to handle requests using its initialization function, which runs and then terminates. The type of requests that it can handle are defined within the module code. This is quite similar to the event-driven programming model that is commonly utilized in graphical-user interface (GUI) applications.

do not have automatic cleanup — any resources that are allocated to the module must be manually released when the module is unloaded, or they may be unavailable until a system reboots.

do not have printf() functions — kernel code cannot access libraries of code that is written for the Linux user space. The kernel module lives and runs in kernel space, which has its own memory address space. The interface between kernel space and user space is clearly defined and controlled. We do however have a printk() function that can output information, which can be viewed from within user space.

can be interrupted — one conceptually difficult aspect of kernel modules is that they can be used by several different programs/processes at the same time. We have to carefully construct our modules so that they have a consistent and valid behavior when they are interrupted. we have to consider the impact of multiple processes accessing the module simultaneously.

have a higher level of execution privilege — typically, more CPU cycles are allocated to kernel modules than to user-space programs. This sounds like an advantage, however, you have to be very careful that your module does not adversely affect the overall performance of your system.

do not have floating-point support — it is kernel code that uses traps to transition from integer to floating-point mode for your user space applications. However, it is very difficult to perform these traps in kernel space. The alternative is to manually save and restore floating point operations — a task that is best avoided and left to your user-space code.

http://derekmolloy.ie/writing-a-linux-kernel-module-part-1-introduction/

I think that would suffice for the first try! Further topics could be actually doing some practical tasks for a kernel module; like showing some data to userland or providing some device for other programs to use.

References

This post is licensed under CC BY 4.0 by the author.

Hypervisor From Scratch – Part 4: Address Translation Using Extended Page Table (EPT)

VmcsAuditor - A Bochs-Based Hypervisor Layout Checker

Comments powered by Disqus.

diff --git a/topics/survey-non-general-registers/index.html b/topics/survey-non-general-registers/index.html new file mode 100644 index 00000000..85e88c46 --- /dev/null +++ b/topics/survey-non-general-registers/index.html @@ -0,0 +1 @@ + A partial survey among non-general purpose registers | Rayanfam Blog
Home A partial survey among non-general purpose registers
Post
Cancel

A partial survey among non-general purpose registers

Hi guys,

In the past few days, I was searching about non-general purpose register then I saw the following pictures that give me a new idea of posting about the non-general purpose registers.

These pieces of information might have some fault because of my misunderstandings about some of them, if you see any fault then please tell me in the comments!

x86-64's registers

You can also download the PDF version of the above picture here!

I don’t know if there are other registers but in the rest of the post, I’ll explain about the register that I know, which is shown in the above pictures. By the way, you all should know about general purpose registers like eax , ecx, ebx,edx,esi,edi,esp,eip (or their equivalent rax,rbx,rdx,rcx,rip,rsp and etc). But non-genral purpose registers are also exists

First of all, let me explain you about segments.

Actually, Segmentation is the division of memory into segments or sections, to address a segment you need to have a two-dimensional variable to point to a valid address and those variables are Base Address and Limit.

  • Base Address contains the starting physical address where the segments reside in memory.
  • Limit specifies the length of the segment.

Segment Registers

Segment register fs, gs, cs, ss, ds, es is used for segmentation, in x86 in the real mode they used for addressing a pointer to a special region in memory like ss for stack segment or ds for the data segment.

Segmentation is no longer available in Long-Mode but the modern Operating Systems still use fs and gs in x86 and x64.

Model-Specific Register (MSR)

In the introduction of 80386, Intel started to introduce “experimental” features which would not necessarily be present in future.

From Wikipedia :

The first of these were two “test registers” (T6 and T7) that allowed the 80386 to speed up virtual-to-physical address conversions. Three additional test registers followed in the 80486 (TR3–TR5) that enabled testing of the processor’s caches for code and data. None of these five registers were implemented in the subsequent Pentium processor.

With the introduction of the Pentium processor, Intel provided a pair of instructions (rdmsr and wrmsr) to access current and future “model-specific registers”, as well as the CPUID instruction to determine which features are present on a particular model. Many of these registers have proven useful enough to be retained. Intel has classified these as architectural model-specific registers and has committed to their inclusion in future product lines.

GDTR, LDTR, IDTR, TR and MSW Registers

GDTR: Determines the current location of Global Descriptor Table

LDTR: Determines the current location of Local Descriptor Table

IDTR: The location of Interrupt Descriptor Table which always point to start of nt!KiDivideErrorFault.

TR: TR stands for Task Register which is a 16-bit register that holds a segment selector for TSS. This register is mainly used for task switching in hardware-level but modern Operating-Systems like Windows and Linux don’t use register because they implement their software-level task switching. LTR and STR instructions are used to read or set the value of TR Register.

Control Register

Somehow like MSR Registers, there is some older way to control CPU features.

You can use these registers (cr0-cr7) to make a feature enable or disable them.

There is also a cr8 register which is used by a REX prefix in long mode. CR8 is used to prioritize external interrupts and is referred to as the task-priority register (TPR).

You can also visit Wikipedia which described every feature related to each bit of the Control Registers.

Debug Registers

Intel and AMD also provide debug registers in order to facilitate the debugging process by providing 8 debug register.

During a debugging process, we can set up to 4 pointers to cause a hardware breakpoint with different conditions (like when you want to set a breakpoint when some instruction tries to read a special location or trying to write on that pointer and etc). all these conditions are set in DR4 to DR7 and DR0 to DR3 is used to store the pointers.

Extended Feature Enable Register (EFER)

AMD uses EFER to enable or disable some of its features (like cr8 in Intel). EFER is a model-specific register.

Floating point unit (Registers)

Because of the problem of saving floating values into binary registers, there are FPU Registers (or floating point unit registers) which helps in storing a computing on float numbers.

XMM Registers

XMM Registers are mainly used by Streaming SIMD Extensions. SSE originally added eight new 128-bit registers known as XMM0 through XMM7. The AMD64 added a further eight registers XMM8 through XMM15.

SSE instructions are used typically in digital signal processing and graphics processing.

YMM Registers

By the rise of Advanced Vector Extensions, Intel introduced the 16 new register and new sets of instructions. AVE is an upgrade to SSE which can speed up FP operations so its main purpose is to make multimedia calculation faster.

AVX uses sixteen YMM registers. Each YMM register contains:

  • eight 32-bit single-precision floating point numbers or
  • four 64-bit double-precision floating point numbers.

I’m sure that this post will be updated ! so keep checking our blog.

Thanks for reading,

This post is licensed under CC BY 4.0 by the author.

Introduction to systemd : Basic Usage and Concepts

Bochs Emulator - Config & Build on Windows and OS X

Comments powered by Disqus.

diff --git a/topics/useful-config-squid3/index.html b/topics/useful-config-squid3/index.html new file mode 100644 index 00000000..6a0db0ae --- /dev/null +++ b/topics/useful-config-squid3/index.html @@ -0,0 +1,95 @@ + Useful Configs for Squid3 Cache | Rayanfam Blog
Home Useful Configs for Squid3 Cache
Post
Cancel

Useful Configs for Squid3 Cache

Hi everyone!

After searching the web so many times and testing different configurations of Squid, I have found these minimal working configs which you can use to achieve the features you want from Squid3 Cache (which is really robust and powerful btw)

Read more for config…

“shutdown_lifetime 3” added for quicker restart of squid service, It’s not really important.

Basic caching forward proxy:

1
+2
+3
+4
+5
+
http_port 3128
+cache_dir ufs /var/spool/squid3 100 16 256
+acl MYNET src 192.168.200.0/24
+http_access allow MYNET
+shutdown_lifetime 3
+

 

Transparent caching forward proxy:

1
+2
+3
+4
+5
+
http_port 3128 transparent
+cache_dir ufs /var/spool/squid3 100 16 256
+acl MYNET src 192.168.200.0/24
+http_access allow MYNET
+shutdown_lifetime 3
+

*You will also need to forward port to squid!* iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3128

Note that using forward proxy compared to transparent proxying has better performance and is a better solution in general for reasons which are outside the scope of this How-To. So try to use it if you can and then use a proxy config script or Active Directory (if in a domain environment) to make users’ browsers use your proxy.

 

Caching forward proxy with basic file authentication:

1
+2
+3
+4
+5
+6
+7
+
http_port 3128
+cache_dir ufs /var/spool/squid3 100 16 256
+auth_param basic program /usr/lib/squid3/basic_ncsa_auth
+/etc/squid3/passwords
+auth_param basic realm proxy
+acl authenticated proxy_auth REQUIRED
+http_access allow authenticated
+

Authentication is not available with transparent proxy (obviously…duh)

 

Caching forward proxy with LDAP authentication:

1
+2
+3
+4
+5
+6
+7
+
http_port 3128
+cache_dir ufs /var/spool/squid3 100 16 256
+auth_param basic program /usr/lib/squid3/basic_ldap_auth -v 3 -b
+"dc=rio,dc=local" -f uid=%s 192.168.100.10
+auth_param basic realm proxy
+acl authenticated proxy_auth REQUIRED
+http_access allow authenticated
+

This LDAP config can’t be used with Active Directory. That’s because unlike openldap (default config), AD DS doesn’t allow a user to bind to its ldap database without presenting a valid user (Binding DN). If you want to use this config with AD DS or a securely and properly configured OpenLDAP, you should specify the binding DN in the auth_param line using “-D” for more info visit Official Squid Guide on ActiveDirectory (which has way more than necessary info and may be a little confusing, just use the syntax from the last part of the guide, I hope it works!)

 

Caching forward proxy with PAM authentication:

1
+2
+3
+4
+5
+6
+7
+
http_port 3128
+cache_dir ufs /var/spool/squid3 100 16 256
+auth_param basic program /usr/lib/squid3/basic_pam_auth
+auth_param basic realm proxy
+acl authenticated proxy_auth REQUIRED
+http_access allow authenticated
+shutdown_lifetime 3
+

Basically for authentication with local linux users…

 

Caching forward proxy with RADIUS authentication:

1
+2
+3
+4
+5
+6
+7
+
http_port 3128
+cache_dir ufs /var/spool/squid3 100 16 256
+auth_param basic program /usr/lib/squid3/basic_radius_auth -f
+/etc/squid3/radius-cred
+auth_param basic realm proxy
+acl authenticated proxy_auth REQUIRED
+http_access allow authenticated
+

Caching forward proxy with domain filtering and PAM authentication:

1
+2
+3
+4
+5
+6
+7
+8
+9
+
http_port 3128
+acl toblock dstdomain .block.rio.local
+cache_dir ufs /var/spool/squid3 100 16 256
+auth_param basic program /usr/lib/squid3/basic_pam_auth
+auth_param basic realm proxy
+acl authenticated proxy_auth REQUIRED
+http_access deny toblock
+http_access allow authenticated
+shutdown_lifetime 3
+

You can use all types of access-list in Squid. Which allows for really flexible and powerful access control for your users.

 

Squid3 not only has lots of features but also very good documentation. The main config file is around 6000 lines which only like 15 are not comments! LOL. so you get the idea…

  • Further reading: WCCP, Delay Pools

Thanks for reading this post. Any advice? please tell me in the comments!

This post is licensed under CC BY 4.0 by the author.

A New Anti Ransomware Idea

How to get every detail about SSDT , GDT , IDT in a blink of an eye

Comments powered by Disqus.

diff --git a/topics/useful-configs-for-nginx/index.html b/topics/useful-configs-for-nginx/index.html new file mode 100644 index 00000000..87a8e941 --- /dev/null +++ b/topics/useful-configs-for-nginx/index.html @@ -0,0 +1,147 @@ + Useful Configs for NGINX | Rayanfam Blog
Home Useful Configs for NGINX
Post
Cancel

Useful Configs for NGINX

After posting the first of my linux SysAdmin quick config sample series titled “Useful Configs for squid” (which you can read here). I decided to write another post, this time about the powerful and popular web/cache server NGINX!

I spent quite some time reading through nginx official docs and other blogs/websites while testing each configuration directive in different scenarios. Some of the options presented in this post do not have good or any documentation. I hope you find them useful!

*** snippets are tested on nginx on Debian 8 (jessie) but they will work on other distros/OSs with minimal or no modification.

Disclaimer: These configuration files are meant to be small and simple and designed to help you get an idea of what is possible with NGINX or quickly test some of its capabilities in a lab environment. although they probably work but they may be far from complete at times. So It’s up to you to research further if you want to leverage nginx in production.


Connecting to PHP

Probably the first thing you want to do after installing nginx is to connect it to some php interpreter to be able to run your web application.

  • Install PHP (on debian : apt install php5 php5-fpm)
  • change NGINX config file like this (essentially only uncomment the relevant section):
1
+2
+3
+4
+5
+6
+7
+8
+
location ~ \.php$ {
+     include snippets/fastcgi-php.conf;
+
+     # With php5-cgi alone:
+     #fastcgi_pass 127.0.0.1:9000;
+     # With php5-fpm:
+     fastcgi_pass unix:/var/run/php5-fpm.sock;
+}
+
  • add index.php to index line
  • verify socket properties in /etc/php5/fpm/pools.d/www.conf
    • socket permissions and user must be correct (they are correct in a default Debian Jessie install)

Redirect HTTP to HTTPS

There any many ways to accomplish this. Some websites advocate the use of if__($scheme … but THIS IS WRONG. it causes performance issues and also if in nginx behaves differently and you might get unexpected results. The correct way to do this is presented below, no rewrite, if , etc are needed this(see ):

1
+2
+3
+
location / {
+    return 301 https://$server_name$request_uri;
+}
+

*** Note that since we are doing a permanent redirect (301), it will be cached by browsers so it will be a one time thing and they will connect to https port by default in subsequent visits.

 

Nginx Reverse Proxy

Reverse proxy is a very popular and useful feature of nginx. It’s important that you completely understand how it works and how to use it effectively. a large number of websites and services are based on nginx reverse proxy like Netflix, CloudFlare CDN and many more!

basic reverse proxy:

1
+2
+3
+4
+5
+6
+7
+
server {
+    listen 80;
+    server_name rayanfam.com;
+    location / {
+        proxy_pass http://<IP of other web server>/[path of real website if not hosted on root];
+    }
+}
+

This feature is usually utilized minimally like this:

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+
server {
+    listen 80;
+    server_name www.rayanfam.com devel.rayanfam.com rayanfam.com;
+
+    location / {
+        proxy_pass http://222.222.222.222:8080;
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+    }
+}
+

I suggest you read official docs on this feature at least, there are many good articles on reverse proxying with nginx on other websites too.

Forward Proxy

This is not a very used feature but for the sake of completeness and also because it is not available on other websites I will show you how to configure nginx as a forward proxy for your organization. It will do the job very well!

1
+2
+3
+4
+5
+6
+7
+8
+
server {
+	listen 80;
+	server_name _;
+	location / {
+		resolver 8.8.8.8;
+		proxy_pass http://$http_host$uri$is_args$args;
+	}
+}
+

*** Do not host this on a public facing IP!

IP-based Block

You may want to deny or allow access only from a specific ip range. you can achieve this with iptables, but this is an acceptable way too:

1
+2
+3
+4
+5
+
location / {
+    allow 192.168.20.0/24;
+    deny all;
+    #... other directives
+}
+

Custom Error Pages

You can easily customize your error page using nginx and setup fancy error pages for all types of error (GitHub is my favorite ^_^ ):

1
+2
+3
+4
+5
+6
+7
+
# Both are mandatory. error paged should be marked as internal
+
+error_page 403 /forbidden.html;
+
+location /forbidden.html {
+    internal;
+}
+

Log format and Destination

Changing the log format and log destination is trivial in nginx. I create a new access log format and then use it to log to syslog facility.

1
+2
+3
+4
+5
+6
+7
+8
+
# creating log format
+log_format mylogformat ‘$remote_addr $request
+
+# log to a file using mylogformat
+access_log /var/log/nginx/custom_access.log mylogformat;
+
+# log to syslog server using mylogformat
+access_log syslog:server=192.168.10.10 mylogformat;
+

you can view official nginx docs regarding field names for logs and support for syslog, etc.

Basic Caching with Nginx

This snippet is intended to give you a very rough idea of caching with nginx and the minimal configuration required to activate that. In a real server more sophisticated caching will probably be required but this will get you started on this topic.

*** Caching is one of the most advanced  features of nginx, make sure to study and understand it.

1
+2
+3
+4
+5
+6
+7
+8
+
# First create the directory and set the required permissions#
+
+proxy_cache_path /var/cache/nginx keys_zone=CACHE:10m;
+
+server {
+    proxy_cache CACHE;
+    # ... other directives
+}
+

HTTP Basic Authentication

It’s the simplest form of authentication you can have for your website or a single page. yet it is effective and secure (if your password is only known by you of course). BE CAREFUL not to put your password file in your web directory! (yes I’ve seen people do that)

1
+2
+3
+4
+5
+6
+7
+
run this command in your shell. It's not part of nginx config:
+$ htpasswd -c /etc/nginx/.htpasswd shahriar
+
+# nginx config → add in desired location block
+
+    auth_basic "Private Content"; 
+    auth_basic_user_file /etc/nginx/.htpasswd;
+

some links:

Official docs

Common config pitfalls (official docs)


I hope you found this blog post useful… spread the word and tell your friends! also do not hesitate to comment. Have fun sysadmin-ing!

This post is licensed under CC BY 4.0 by the author.

Assembly Challenge : Jump to a non-relative address without using registers

Introduction to systemd : Basic Usage and Concepts

Comments powered by Disqus.

diff --git a/topics/using-intels-streaming-simd-extensions-3-monitormwait-as-a-kernel-debugging-trick/index.html b/topics/using-intels-streaming-simd-extensions-3-monitormwait-as-a-kernel-debugging-trick/index.html new file mode 100644 index 00000000..1405c11f --- /dev/null +++ b/topics/using-intels-streaming-simd-extensions-3-monitormwait-as-a-kernel-debugging-trick/index.html @@ -0,0 +1,73 @@ + Using Intel's Streaming SIMD Extensions 3 (MONITOR MWAIT) As A Kernel Debugging Trick | Rayanfam Blog
Home Using Intel's Streaming SIMD Extensions 3 (MONITOR MWAIT) As A Kernel Debugging Trick
Post
Cancel

Using Intel's Streaming SIMD Extensions 3 (MONITOR MWAIT) As A Kernel Debugging Trick

Introduction

MONITOR and MWAIT are using when the CPU needs to be stopped executing the instruction and enter an implementation-dependent optimized state until some special event happens.

MONITOR sets up an address range used to monitor write-back stores while MWAIT enables a logical processor to enter into an optimized state while waiting for a write-back store to the address range set up by MONITOR instruction.

MWAIT and MONITOR may be executed only at privilege level 0, if you use these instructions in any other privilege level, then an invalid opcode exception is thrown.

If the preceding MONITOR instruction did not successfully arm an address range or if the MONITOR instruction has not been executed prior to executing MWAIT, then the processor will not enter the implementation-dependent-optimized state. Execution will resume at the instruction following the MWAIT.

The opcode and the instructions are shown below :

1
+
0:  0f 01 c9                mwait
+
1
+
0:  0f 01 c8                monitor
+

Check Availability

The BIOS or any kernel-level driver or operating system can disable these instructions by using the IA32_MISC_ENABLE MSR;

CPUID.01H:ECX.MONITOR[bit 3] indicates the availability of MONITOR and MWAIT in the processor.

Query About Details

To query about the smallest and the largest line size that MONITOR supports you can use CPUID.05H:EAX[bits 15:0] and CPUID.05H:EBX.Largest[bits 15:0]. Values are returned in bytes.

Implementation

For MONITOR address should be in RAX/EAX, ECX and EDX are hints to processor about Monitor state. (We make them zeros.). MWAIT, on the other hand, should be executed after MONITOR and you can use ECX in order to config MWAIT about interrupts.

If ECX[0th Bit] = 0, then MWAIT will wake on every interrupts (that’s exactly like HLT instruction) but if ECX[0th Bit] = 1 then it doesn’t wake on interrupts.

As you can see in the following code, we don’t need MWAIT to wake by system interrupts so just increment the ECX.

MWAIT/MONITOR in Linux Kernel Module (AT&T Syntax):

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
+
int init_module(void)
+{
+long unsigned int address = 0xffffffff12345678; 
+
+__asm__ volatile(
+
+"push %%rax\n\t"
+"push %%rcx\n\t"
+"push %%rdx\n\t"
+
+"xor %%rax,%%rax\n\t"
+"xor %%rcx,%%rax\n\t"
+"xor %%rdx,%%rax\n\t"
+
+"movq %0,%%rax\n\t"
+"MONITOR\n\t"
+
+"xor %%rax,%%rax\n\t"
+"xor %%rcx,%%rax\n\t"
+
+"inc %%rax\n\t"
+
+"MWAIT\n\t"
+
+"pop %%rdx\n\t"
+"pop %%rcx\n\t"
+"pop %%rax\n\t"
+
+:: "g" (address));
+
+printk("The requested location has been accessed !");
+
+return 0;
+}
+

The address to monitor is stored in “address” and in our example it is 0xffffffff12345678.

Using MONITOR/MWAIT To Detect Modifications

As I told you, MONITOR/MWAIT can be used as a debugging trick, whenever we used all of our 4 debug registers then we can use our cores instead! I always use these instructions to detect whether a special range of memory (in the kernel) is modified by other processors or not. On the other hand, you have more flexibility in size rather than the debug registers but the worst thing about it is that you can notify in the case of modification but you never know what was the code that leads to this modification.

As Intel describes MONITOR/MWAIT are agent synchronization instructions so they might be used in order to trigger an event to notify a kernel program.

Limitation

One of the limitations for MONITOR/MWAIT is that it only wakes on modification on the write-back cache and not write-through cache so it seems Intel implemented these instructions just in L1 write-back.

A good answer in StackOverflow describes the differences between write-back and write-through caches:

Write-back is used for the up-to-date data is in a processor cache, and sometimes it is in main memory. If the data is in a processor cache, then that processor must stop main memory from replying to the read request, because the main memory might have a stale copy of the data. This is more complicated than write-through.

Write-through can simplify the cache coherency protocol because it doesn’t need the Modify state. The Modify state records that the cache must write back the cache line before it invalidates or evicts the line. In write-through, a cache line can always be invalidated without writing back since memory already has an up-to-date copy of the line. The

The equivalent of MWAIT and Monitor in other processors is MIPS’LL/Pause.

References

[1] How to Use the MONITOR and MWAIT Streaming SIMD Extensions 3 Instructions (https://software.intel.com/en-us/articles/how-to-use-the-monitor-and-mwait-streaming-simd-extensions-3-instructions)

[2] MWAIT — Monitor Wait (https://www.felixcloutier.com/x86/MWAIT.html)

[3] Write-back vs Write-Through (https://stackoverflow.com/questions/27087912/write-back-vs-write-through)

This post is licensed under CC BY 4.0 by the author.

Hypervisor From Scratch – Part 2: Entering VMX Operation

Hypervisor From Scratch – Part 3: Setting up Our First Virtual Machine

Comments powered by Disqus.

diff --git a/topics/vmcsauditor-a-bochs-based-hypervisor-layout-checker/index.html b/topics/vmcsauditor-a-bochs-based-hypervisor-layout-checker/index.html new file mode 100644 index 00000000..2f5a7c7e --- /dev/null +++ b/topics/vmcsauditor-a-bochs-based-hypervisor-layout-checker/index.html @@ -0,0 +1,47 @@ + VmcsAuditor - A Bochs-Based Hypervisor Layout Checker | Rayanfam Blog
Home VmcsAuditor - A Bochs-Based Hypervisor Layout Checker
Post
Cancel

VmcsAuditor - A Bochs-Based Hypervisor Layout Checker

Introduction

If you’ve ever had experience with developing your own hypervisor from scratch then you definitely faced with the problems of layout checking. What makes it hard to develop a hypervisor is the fact that Intel has a small number of errors for a huge checklist described in [CHAPTER 26] VM ENTRIES from Intel’s 64 and IA-32 Architectures Software Developer’s Manual including:

  • 26.2 CHECKS ON VMX CONTROLS AND HOST-STATE AREA
  • 26.3 CHECKING AND LOADING GUEST STATE
  • 26.4 LOADING MSRS
  • 26.5 EVENT INJECTION
  • 26.6 SPECIAL FEATURES OF VM ENTRY
  • 26.7 VM-ENTRY FAILURES DURING OR AFTER LOADING GUEST STATE
  • 26.8 MACHINE-CHECK EVENTS DURING VM ENTRY

You can check these lists by your own and if you can’t understand what is the exact problem then you can use some tools like Boch Emulator which supports the Intel VMX.

Unfortunately, Bochs emulator is really slow (as the nature of emulating) and sometimes have lots of problems with stability but there are lots of cool features that implemented in this emulator and one of them is VMX support. VMCS Auditor is a copy of Bochs emulator in a user-mode application. It asks you for each field of VMCS Layout and tells you what was your mistake.

VMCS-Auditor

Keep in mind, Bochs is just an implementation of Intel’s VMX behavior and by no mean 100% precise so there might be some problems which Bochs is not able to detect.

Note that even VMCS Auditor is a Windows executable file that runs in Windows, but its checks are related to CPU (not the OS) so it’s clear that you can give your VMCS Layout in other OSs like Linux or OS X an expect the same result.

Source code & Executables

The full source code and executable file are available on Github:

[https://github.com/SinaKarvandi/VMCS-Auditor]

Error Detection

The following table describes what error can occur for every vmx instructions.

VM Errors

VMCS Auditor focuses on Error Number #7 and #8 to perform the checking lists described in Intel’s SDM as implemented in Bochs emulator.

How to use?

In order to use VMCS Auditor, first, you have to enter some MSRs related to Intel’s VMX (e.g IA32_VMX_PINBASED_CTL). Second VMCS Auditor asks you for other options based on your settings, newer settings are appearing and if you have any problem configuring these options then the error(s) are shown to you. 

If you have a problem with configuring MSRs, you have to open your Windbg in Local Kernel Debugging Mode (remember MSRs in a VM based kernel debugging might not be true for  VMX related options) then you can use rdmsr which gives you the corresponding value.

For instance, we need to get MSR (0x481) then execute the following command in Windbg.

1
+
rdmsr 0x481
+

The above command’s result is illustrated below.

VMCS-Auditor-RDMSR

The following MSRs representing my own system’s MSRs for VMX.

1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
lkd> rdmsr 0x481
+msr[481] = 0000007f`00000016
+lkd> rdmsr 0x482
+msr[482] = fff9fffe`0401e172
+lkd> rdmsr 0x48b
+msr[48b] = 001ffcff`00000000
+lkd> rdmsr 0x483
+msr[483] = 01ffffff`00036dff
+lkd> rdmsr 0x484
+msr[484] = 0003ffff`000011ff
+lkd> rdmsr 0x48c
+msr[48c] = 00000f01`06334141
+lkd> rdmsr 0x491
+msr[491] = 00000000`00000001
+lkd> rdmsr 0x486
+msr[486] = 00000000`80000021
+lkd> rdmsr 0x487
+msr[487] = 00000000`ffffffff
+lkd> rdmsr 0x488
+msr[488] = 00000000`00002000
+lkd> rdmsr 0x489
+msr[489] = 00000000`003767ff
+

Future Works

  • Adding support for config file that works with VMCS Auditor.

Contribution

If you want to contribute to this project (e.g adding more checks or make VMCS Auditor more precise) then you can use the GitHub or suggest edits below in the comments.

Isn't it cute ? :)

Conclusion

This tool is published in order to ease the hypervisor development, in the future part of the tutorial Hypervisor From Scratch, we’ll use this tool to test our VMCS Layout before any real-world testing.

References

[1] Vol 3C – 26 VM ENTRIES  (https://software.intel.com/en-us/articles/intel-sdm)

[2] Bochs Emulator  (http://bochs.sourceforge.net)

This post is licensed under CC BY 4.0 by the author.

Start linux kernel module development!

Hypervisor From Scratch – Part 5: Setting up VMCS & Running Guest Code

Comments powered by Disqus.

diff --git a/tutorials/index.html b/tutorials/index.html new file mode 100644 index 00000000..71e1408a --- /dev/null +++ b/tutorials/index.html @@ -0,0 +1 @@ + Tutorials | Rayanfam Blog
Home Tutorials
Tutorials
Cancel